PluginProbe
ThinkRank AI SEO – AI SEO Plugin for WordPress: Schema, XML Sitemaps, Meta Tags, Search Console & Local SEO / 2.7.0
ThinkRank AI SEO – AI SEO Plugin for WordPress: Schema, XML Sitemaps, Meta Tags, Search Console & Local SEO v2.7.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 1.0.2 1.1.0 1.10.0 All 48 releases
← All changes | includes/api/class-global-seo-endpoint.php +26 -5 2.2.02.7.0 View file →
@@ -239,18 +239,28 @@
239 239 // Capture the previously-stored value so a genuine write failure can be
240 240 // told apart from a no-op save (payload identical to what's stored).
241 241 $previous = $all_settings[$post_type] ?? null;
242 242
243 + // MERGE, never replace. This entity row is shared: the Content Type
244 + // Matrix stores its per-feature tri-states (meta_enabled, schema_enabled,
245 + // open_graph_enabled, twitter_card_enabled, analytics_enabled) and its
246 + // sitemap_include flag under the SAME key, and normalize_settings_patch()
247 + // whitelists only the Global SEO fields — so a wholesale replace here
248 + // silently dropped every matrix choice the moment the user saved the
249 + // Global SEO screen next door. Global SEO's own keys still win, because
250 + // sanitize_settings() emits them complete.
251 + $merged = array_merge(is_array($previous) ? $previous : [], $sanitized_settings);
252 +
243 253 // Update settings for this post type
244 - $all_settings[$post_type] = $sanitized_settings;
254 + $all_settings[$post_type] = $merged;
245 255
246 256 // Save to database
247 257 $updated = update_option(self::OPTION_NAME, $all_settings);
248 258
249 - if ($updated || $previous === $sanitized_settings) {
259 + if ($updated || $previous === $merged) {
250 260 return new WP_REST_Response([
251 261 'success' => true,
252 - 'data' => $sanitized_settings,
262 + 'data' => $merged,
253 263 'post_type' => $post_type,
254 264 'message' => sprintf('Settings saved successfully for post type: %s', $post_type)
255 265 ], 200);
256 266 }
@@ -300,10 +310,21 @@
300 310
301 311 // Get all settings
302 312 $all_settings = get_option(self::OPTION_NAME, []);
303 313
304 - // Remove settings for this post type (will fall back to defaults)
305 - unset($all_settings[$post_type]);
314 + // Reset only what this screen owns. The entity row is shared with the
315 + // Content Type Matrix, so dropping the whole row would reset the user's
316 + // per-feature tri-states and sitemap choice as a side effect of a button
317 + // that says nothing about them. DEFAULT_SETTINGS is the exact set of
318 + // Global SEO keys, so anything outside it belongs to another screen.
319 + $stored = $all_settings[$post_type] ?? [];
320 + $kept = is_array($stored) ? array_diff_key($stored, self::DEFAULT_SETTINGS) : [];
321 +
322 + if ($kept === []) {
323 + unset($all_settings[$post_type]);
324 + } else {
325 + $all_settings[$post_type] = $kept;
326 + }
306 327
307 328 // Save updated settings
308 329 update_option(self::OPTION_NAME, $all_settings);
309 330