| @@ -182,8 +182,30 @@ | ||
| 182 | 182 | * @param string $category Optional. Settings category |
| 183 | 183 | * @return bool|null True on success, false on failure, null when this store |
| 184 | 184 | * does not own the category (nothing was attempted). |
| 185 | 185 | */ |
| 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 | + | |
| 186 | 208 | public function save_settings_by_category(string $context_type, ?int $context_id, array $settings, string $category = 'general'): ?bool { |
| 187 | 209 | // Not this store's category. Callers address categories by the |
| 188 | 210 | // endpoint's vocabulary (`social_media`, `site_identity`, …) while this |
| 189 | 211 | // store registers its own (`social`, `general`, …), so an unrecognised |
| @@ -907,77 +929,6 @@ | ||
| 907 | 929 | $default_settings['reset_at'] = current_time('mysql'); |
| 908 | 930 | $default_settings['reset_by'] = get_current_user_id(); |
| 909 | 931 | |
| 910 | 932 | return $this->save_settings($context_type, $context_id, $default_settings); |
| 911 | - } | |
| 912 | - | |
| 913 | - /** | |
| 914 | - * Get settings migration status | |
| 915 | - * | |
| 916 | - * @since 1.0.0 | |
| 917 | - * | |
| 918 | - * @return array Migration status information | |
| 919 | - */ | |
| 920 | - public function get_migration_status(): array { | |
| 921 | - return [ | |
| 922 | - 'current_version' => '1.0.0', | |
| 923 | - 'database_version' => get_option('thinkrank_seo_db_version', '0.0.0'), | |
| 924 | - 'migration_needed' => version_compare(get_option('thinkrank_seo_db_version', '0.0.0'), '1.0.0', '<'), | |
| 925 | - 'last_migration' => get_option('thinkrank_seo_last_migration', ''), | |
| 926 | - 'migration_log' => get_option('thinkrank_seo_migration_log', []) | |
| 927 | - ]; | |
| 928 | - } | |
| 929 | - | |
| 930 | - /** | |
| 931 | - * Perform settings migration | |
| 932 | - * | |
| 933 | - * @since 1.0.0 | |
| 934 | - * | |
| 935 | - * @param string $from_version Source version | |
| 936 | - * @param string $to_version Target version | |
| 937 | - * @return array Migration results | |
| 938 | - */ | |
| 939 | - public function migrate_settings(string $from_version, string $to_version): array { | |
| 940 | - $migration_results = [ | |
| 941 | - 'success' => false, | |
| 942 | - 'migrated_count' => 0, | |
| 943 | - 'errors' => [], | |
| 944 | - 'from_version' => $from_version, | |
| 945 | - 'to_version' => $to_version, | |
| 946 | - 'started_at' => current_time('mysql') | |
| 947 | - ]; | |
| 948 | - | |
| 949 | - try { | |
| 950 | - // Perform version-specific migrations | |
| 951 | - switch ($from_version) { | |
| 952 | - case '0.0.0': | |
| 953 | - // Initial migration - set up default settings | |
| 954 | - $contexts = ['site', 'post', 'page', 'product']; | |
| 955 | - foreach ($contexts as $context) { | |
| 956 | - $defaults = $this->get_default_settings($context); | |
| 957 | - $this->save_settings($context, null, $defaults); | |
| 958 | - $migration_results['migrated_count']++; | |
| 959 | - } | |
| 960 | - break; | |
| 961 | - default: | |
| 962 | - $migration_results['errors'][] = "No migration path defined for version {$from_version}"; | |
| 963 | - break; | |
| 964 | - } | |
| 965 | - | |
| 966 | - if (empty($migration_results['errors'])) { | |
| 967 | - $migration_results['success'] = true; | |
| 968 | - update_option('thinkrank_seo_db_version', $to_version); | |
| 969 | - update_option('thinkrank_seo_last_migration', current_time('mysql')); | |
| 970 | - | |
| 971 | - // Log migration | |
| 972 | - $migration_log = get_option('thinkrank_seo_migration_log', []); | |
| 973 | - $migration_log[] = $migration_results; | |
| 974 | - update_option('thinkrank_seo_migration_log', array_slice($migration_log, -10)); // Keep last 10 migrations | |
| 975 | - } | |
| 976 | - } catch (\Exception $e) { | |
| 977 | - $migration_results['errors'][] = 'Migration failed: ' . $e->getMessage(); | |
| 978 | - } | |
| 979 | - | |
| 980 | - $migration_results['completed_at'] = current_time('mysql'); | |
| 981 | - return $migration_results; | |
| 982 | 933 | } |
| 983 | 934 | } |