PluginProbe
ThinkRank AI SEO – AI SEO Plugin for WordPress: Schema, XML Sitemaps, Meta Tags, Search Console & Local SEO / 2.9.0
ThinkRank AI SEO – AI SEO Plugin for WordPress: Schema, XML Sitemaps, Meta Tags, Search Console & Local SEO v2.9.0
2.10.0 2.9.0 2.8.0 2.7.0 2.6.0 2.5.0 2.4.0 2.3.0 2.2.0 2.1.1 2.1.0 2.0.2 2.0.1 2.0.0 1.32.0 1.31.0 1.30.0 1.29.0 1.28.0 1.27.0 1.26.0 1.25.0 trunk 1.0.0 1.0.1 All 51 releases
← All changes | includes/seo/class-seo-settings-manager.php +39 -78 1.30.0 → 2.9.0 View file →
@@ -179,16 +179,45 @@
179 179 * @param string $context_type The context type
180 180 * @param int|null $context_id Optional. Context ID
181 181 * @param array $settings Settings array to save
182 182 * @param string $category Optional. Settings category
183 - * @return bool True on success, false on failure
183 + * @return bool|null True on success, false on failure, null when this store
184 + * does not own the category (nothing was attempted).
184 185 */
185 - public function save_settings_by_category(string $context_type, ?int $context_id, array $settings, string $category = 'general'): bool {
186 - // Validate category
186 + /**
187 + * This store's boolean keys, so a read hands them back as booleans and its
188 + * own validator accepts them.
189 + *
190 + * validation_rules['boolean_fields'] is the source: without this, four of
191 + * the five ('auto_generate', 'validation_enabled', 'output_enabled',
192 + * 'cache_enabled') came back from the database as the string '1', and
193 + * save_settings_by_category() — which merges the existing settings before
194 + * saving — then failed its own validation. The endpoint reported HTTP 500
195 + * over a write the dedicated manager had already committed (#395).
196 + *
197 + * @since 2.0.1
198 + *
199 + * @return string[] Keys to coerce to boolean on read.
200 + */
201 + protected function boolean_setting_keys(): array {
202 + return array_values(array_unique(array_merge(
203 + parent::boolean_setting_keys(),
204 + $this->validation_rules['boolean_fields']
205 + )));
206 + }
207 +
208 + public function save_settings_by_category(string $context_type, ?int $context_id, array $settings, string $category = 'general'): ?bool {
209 + // Not this store's category. Callers address categories by the
210 + // endpoint's vocabulary (`social_media`, `site_identity`, …) while this
211 + // store registers its own (`social`, `general`, …), so an unrecognised
212 + // name is routine and must NOT be reported as a failed write — the
213 + // caller's dedicated manager is the store of record for those (#371).
214 + // Returning false here made every such save answer 500 while the
215 + // manager's row had already committed.
187 216 if (!isset($this->settings_categories[$category])) {
188 - return false;
217 + return null;
189 218 }
190 -
219 +
191 220 // Check if context is supported for this category
192 221 if (!in_array($context_type, $this->settings_categories[$category]['contexts'], true)) {
193 222 return false;
194 223 }
@@ -249,10 +278,13 @@
249 278 // Extract category if provided
250 279 $category = $settings_data['category'] ?? 'general';
251 280 unset($settings_data['category']);
252 281
253 - $success = $this->save_settings_by_category($context_type, $context_id, $settings_data, $category);
254 -
282 + // Normalise the tri-state to a boolean: an unknown category (null)
283 + // persisted nothing here, and this bulk API has no dedicated-manager
284 + // fallback, so it is a failure from this caller's point of view.
285 + $success = true === $this->save_settings_by_category($context_type, $context_id, $settings_data, $category);
286 +
255 287 $results[$context_key] = [
256 288 'success' => $success,
257 289 'context_type' => $context_type,
258 290 'context_id' => $context_id,
@@ -897,77 +929,6 @@
897 929 $default_settings['reset_at'] = current_time('mysql');
898 930 $default_settings['reset_by'] = get_current_user_id();
899 931
900 932 return $this->save_settings($context_type, $context_id, $default_settings);
901 - }
902 -
903 - /**
904 - * Get settings migration status
905 - *
906 - * @since 1.0.0
907 - *
908 - * @return array Migration status information
909 - */
910 - public function get_migration_status(): array {
911 - return [
912 - 'current_version' => '1.0.0',
913 - 'database_version' => get_option('thinkrank_seo_db_version', '0.0.0'),
914 - 'migration_needed' => version_compare(get_option('thinkrank_seo_db_version', '0.0.0'), '1.0.0', '<'),
915 - 'last_migration' => get_option('thinkrank_seo_last_migration', ''),
916 - 'migration_log' => get_option('thinkrank_seo_migration_log', [])
917 - ];
918 - }
919 -
920 - /**
921 - * Perform settings migration
922 - *
923 - * @since 1.0.0
924 - *
925 - * @param string $from_version Source version
926 - * @param string $to_version Target version
927 - * @return array Migration results
928 - */
929 - public function migrate_settings(string $from_version, string $to_version): array {
930 - $migration_results = [
931 - 'success' => false,
932 - 'migrated_count' => 0,
933 - 'errors' => [],
934 - 'from_version' => $from_version,
935 - 'to_version' => $to_version,
936 - 'started_at' => current_time('mysql')
937 - ];
938 -
939 - try {
940 - // Perform version-specific migrations
941 - switch ($from_version) {
942 - case '0.0.0':
943 - // Initial migration - set up default settings
944 - $contexts = ['site', 'post', 'page', 'product'];
945 - foreach ($contexts as $context) {
946 - $defaults = $this->get_default_settings($context);
947 - $this->save_settings($context, null, $defaults);
948 - $migration_results['migrated_count']++;
949 - }
950 - break;
951 - default:
952 - $migration_results['errors'][] = "No migration path defined for version {$from_version}";
953 - break;
954 - }
955 -
956 - if (empty($migration_results['errors'])) {
957 - $migration_results['success'] = true;
958 - update_option('thinkrank_seo_db_version', $to_version);
959 - update_option('thinkrank_seo_last_migration', current_time('mysql'));
960 -
961 - // Log migration
962 - $migration_log = get_option('thinkrank_seo_migration_log', []);
963 - $migration_log[] = $migration_results;
964 - update_option('thinkrank_seo_migration_log', array_slice($migration_log, -10)); // Keep last 10 migrations
965 - }
966 - } catch (\Exception $e) {
967 - $migration_results['errors'][] = 'Migration failed: ' . $e->getMessage();
968 - }
969 -
970 - $migration_results['completed_at'] = current_time('mysql');
971 - return $migration_results;
972 933 }
973 934 }