false, 'critical_email' => false, 'weekly_email_to' => '', // Delete all Keepwatch data when the plugin is deleted. CodeCanyon build keeps data unless the administrator // opts in (Envato: ask before deleting, allow keeping); the WordPress.org build cleans up by default. 'delete_data' => ! Distribution::is_codecanyon(), ); } /** * Current settings merged with defaults. * * @return array{weekly_email: bool, critical_email: bool, weekly_email_to: string, delete_data: bool} */ public static function all(): array { $stored = get_option( self::OPTION, array() ); if ( ! is_array( $stored ) ) { $stored = array(); } $merged = array_merge( self::defaults(), $stored ); $merged['weekly_email'] = (bool) $merged['weekly_email']; $merged['critical_email'] = (bool) $merged['critical_email']; $merged['weekly_email_to'] = is_string( $merged['weekly_email_to'] ) ? $merged['weekly_email_to'] : ''; $merged['delete_data'] = (bool) $merged['delete_data']; return $merged; } /** * Sanitize and persist settings from a form submission. * * @param array $input Raw input. * @return array{weekly_email: bool, critical_email: bool, weekly_email_to: string, delete_data: bool} */ public static function update( array $input ): array { $current = self::all(); $to = isset( $input['weekly_email_to'] ) ? sanitize_email( (string) $input['weekly_email_to'] ) : $current['weekly_email_to']; $settings = array( 'weekly_email' => ! empty( $input['weekly_email'] ), 'critical_email' => ! empty( $input['critical_email'] ), 'weekly_email_to' => is_email( $to ) ? $to : '', 'delete_data' => ! empty( $input['delete_data'] ), ); update_option( self::OPTION, $settings, false ); self::sync_schedules( $settings ); return $settings; } /** * Whether uninstalling should delete all Keepwatch data (read by uninstall.php through the stored option). */ public static function delete_data_on_uninstall(): bool { return self::all()['delete_data']; } /** * Store default values that an older version did not save yet, so uninstall.php (which runs without the * plugin loaded) sees this build's choice for delete_data. */ public static function persist_missing_defaults(): void { $stored = get_option( self::OPTION, array() ); if ( ! is_array( $stored ) ) { $stored = array(); } if ( array() === array_diff_key( self::defaults(), $stored ) ) { return; } update_option( self::OPTION, array_merge( self::defaults(), $stored ), false ); } /** * Whether the optional weekly digest is enabled. */ public static function weekly_email_enabled(): bool { return self::all()['weekly_email']; } /** * Whether critical-issue alerts are enabled. */ public static function critical_email_enabled(): bool { return self::all()['critical_email']; } /** * Recipient address (admin email when empty). */ public static function weekly_email_recipient(): string { $to = self::all()['weekly_email_to']; if ( '' !== $to && is_email( $to ) ) { return $to; } $admin = get_option( 'admin_email' ); return is_string( $admin ) ? $admin : ''; } /** * Callback for admin_init. WordPress passes an empty string to action callbacks, so the hook must not be * bound to sync_schedules() directly (its typed parameter would cause a fatal error). */ public static function on_admin_init(): void { self::persist_missing_defaults(); self::sync_schedules(); } /** * Cron events exist only while the matching email option is on. * * @param array{weekly_email: bool, critical_email: bool, weekly_email_to: string, delete_data: bool}|null $settings Settings to apply; null reads the stored ones. */ public static function sync_schedules( ?array $settings = null ): void { if ( null === $settings ) { $settings = self::all(); } if ( $settings['weekly_email'] ) { Mail\WeeklyEmail::schedule(); } else { Mail\WeeklyEmail::unschedule(); } if ( $settings['critical_email'] ) { Mail\CriticalAlert::schedule(); } else { Mail\CriticalAlert::unschedule(); } } }