'%%title%% %%sep%% %%sitename%%', 'metaTitleSeparator' => '-', 'metaDescriptionTemplate' => '%%excerpt%%', 'xmlSitemap' => 'on', 'xmlSitemapPostTypes' => [ 'post' => 'on', 'page' => 'on', ], 'xmlSitemapTaxonomies' => [ 'category' => 'on', ], 'xmlSitemapExcludeNoindex' => 'on', 'xmlSitemapImages' => 'on', 'robotsTxt' => 'on', 'robotsTxtContent' => '', 'llmsTxtPostTypes' => ['page', 'post'], 'llmsTxtExcludeNoindex' => 'on', 'schemaMarkup' => 'on', 'schemaSiteType' => 'organization', 'schemaSiteName' => get_bloginfo( 'name' ), 'schemaSiteLogo' => '', 'schemaSitelinksSearch' => 'on', 'openGraph' => 'on', 'twitterCards' => 'on', 'socialFallbackImage' => '', 'socialTwitterSite' => '', 'socialFacebook' => '', 'socialInstagram' => '', 'socialLinkedin' => '', 'socialYoutube' => '', 'socialPinterest' => '', 'verifyGoogle' => '', 'verifyBing' => '', 'verifyYandex' => '', 'verifyPinterest' => '', 'verifyBaidu' => '', 'mediaPages' => 'off', 'disableAuthorArchivesSingleAuthor' => 'off', 'breadcrumbs' => 'off', 'breadcrumbsSeparator' => '/', 'breadcrumbsHomeLabel' => '', 'llmsTxt' => 'on', 'aiCrawlersAllow' => 'on', 'aiCrawlers' => [], 'redirects' => 'on', 'redirects404Monitor' => 'on', 'redirects404Retention' => '30', 'redirects404Ignore' => '', 'reuseExistingSeo' => 'on', 'aeoCitationTracking' => 'on', 'emailNotificationsRecipient' => '', 'notifyDigest' => 'off', 'notifyDigestFrequency' => 'weekly', 'notifyCritical' => 'off', 'notify404' => 'off', 'notify404Threshold' => 10, ]; // Get existing settings $existingSettings = get_option( 'rankify_settings', [] ); // Add default settings (only adds if option doesn't exist) add_option( 'rankify_settings', $defaultSettings ); // Backfill defaults a site installed before this version has never seen. // The update handler below only runs when WordPress's own updater fired, // so without this a newly added setting stays missing (and reads as false) // until the user happens to save the settings form. Re-read the option // rather than reusing $existingSettings — the premium merge above may // already have written to it. $currentSettings = get_option( 'rankify_settings', [] ); $missingSettings = array_diff_key( $defaultSettings, $currentSettings ); if ( !empty( $currentSettings ) && !empty( $missingSettings ) ) { update_option( 'rankify_settings', array_merge( $currentSettings, $missingSettings ) ); } // Handle plugin update - add any new default settings that don't exist yet if ( get_transient( 'rankify_updated' ) ) { $currentSettings = get_option( 'rankify_settings', [] ); $allDefaults = ( \DaftPlug\Rankify\rankify_pro()->is__premium_only() ? array_merge( $defaultSettings, $premiumSettings ?? [] ) : $defaultSettings ); // Only add settings that don't exist, preserving user's existing values $newSettings = array_diff_key( $allDefaults, $currentSettings ); if ( !empty( $newSettings ) ) { update_option( 'rankify_settings', array_merge( $currentSettings, $newSettings ) ); } delete_transient( 'rankify_updated' ); } // The overview's figures are derived from post content and from these // settings, so both have to invalidate the cached snapshot. add_action( 'save_post', [SiteAudit::class, 'flush'] ); add_action( 'deleted_post', [SiteAudit::class, 'flush'] ); add_action( 'rankify_settings_update:after', [SiteAudit::class, 'flush'] ); add_action( 'upgrader_process_complete', [$this, 'onUpdate'], 10, 2 ); \DaftPlug\Rankify\rankify_pro()->add_action( 'after_uninstall', [$this, 'onUninstall'] ); add_action( 'rest_api_init', [$this, 'registerRoutes'] ); } public function onUpdate( $upgraderObject, $options ) { if ( $options['action'] == 'update' && $options['type'] == 'plugin' && isset( $options['plugins'] ) ) { foreach ( $options['plugins'] as $plugin ) { if ( $plugin == RANKIFY_BASENAME ) { set_transient( 'rankify_updated', 'yes', 3600 ); } } } } public function onUninstall() { delete_option( 'rankify_settings' ); delete_option( 'rankify_gsc_connection' ); delete_option( 'rankify_crawler_beacon_seen' ); delete_option( 'rankify_crawler_tokens' ); } public function registerRoutes() { \DaftPlug\Rankify\Helpers\Utils::registerRestRoute( '/settings', [ 'methods' => 'PUT', 'callback' => [$this, 'updateSettings'], 'permission_callback' => function () { return current_user_can( 'manage_options' ); }, ] ); } public function updateSettings( \WP_REST_Request $request ) { if ( !wp_verify_nonce( $request->get_header( 'X-WP-Nonce' ), 'wp_rest' ) ) { return new \WP_Error('invalid_nonce', 'Invalid nonce', [ 'status' => 403, ]); } $newSettings = $request->get_param( 'settings' ); if ( !is_array( $newSettings ) ) { return new \WP_Error('invalid_settings', 'Invalid settings format', [ 'status' => 400, ]); } $currentSettings = get_option( 'rankify_settings', [] ); $settings = wp_parse_args( $newSettings, $currentSettings ); do_action( 'rankify_settings_update:before', $settings ); // update_option() returns false both on failure AND when the value is unchanged. // Always fire the after action and return success since settings are in the desired state. update_option( 'rankify_settings', $settings ); do_action( 'rankify_settings_update:after', $settings ); return new \WP_REST_Response([ 'status' => 'success', ], 200); } }