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/abilities/settings/class-update-site-identity-settings.php +29 -52 2.1.0 → 2.9.0 View file →
@@ -24,39 +24,14 @@
24 24 * touched so this ability cannot clobber the robots.txt configuration.
25 25 */
26 26 class Update_Site_Identity_Settings extends Ability_Base {
27 27 /**
28 - * Boolean-typed site identity keys.
29 - */
30 - private const BOOL_KEYS = [
31 - 'enabled',
32 - 'breadcrumbs_enabled',
33 - ];
34 -
35 - /**
36 - * String-typed site identity keys.
37 - */
38 - private const STRING_KEYS = [
39 - 'title_template',
40 - 'title_separator',
41 - 'site_name',
42 - 'site_description',
43 - 'tagline',
44 - 'breadcrumb_type',
45 - 'breadcrumb_home_text',
46 - 'breadcrumb_separator',
47 - 'logo_url',
48 - 'favicon_url',
49 - 'apple_touch_icon_url',
50 - ];
51 -
52 - /**
53 28 * Constructor.
54 29 */
55 30 public function __construct() {
56 31 $this->id = 'thinkrank/update-site-identity-settings';
57 32 $this->label = __( 'Update ThinkRank Site Identity Settings', 'thinkrank' );
58 - $this->description = __( 'Update ThinkRank site identity settings: title templates, site name/description, breadcrumb configuration, and brand imagery. Robots.txt configuration is never modified by this ability.', 'thinkrank' );
33 + $this->description = __( 'Update ThinkRank site identity settings: the homepage/category/tag/author/search/archive title templates, site name and description, breadcrumb configuration, brand imagery, homepage hero, and the business details behind LocalBusiness schema. Robots.txt contents and schema toggles are never modified by this ability. Read the current values with get-site-identity-settings first; only the keys you pass are changed.', 'thinkrank' );
59 34 }
60 35
61 36 /**
62 37 * {@inheritDoc}
@@ -65,9 +40,16 @@
65 40 */
66 41 public function get_annotations() {
67 42 return [
68 43 'readonly' => false,
69 - 'destructive' => true,
44 + // Settings writes are recoverable: the matching get-* ability reads
45 + // the previous value, so nothing is lost that cannot be put back.
46 + // `destructive` is reserved for calls that lose data or reach
47 + // outside the site, and marking routine configuration with it made
48 + // MCP clients demand a human approval for every save — which users
49 + // reported as a permission bug, because the client's refusal reads
50 + // as "No approval received" (#675).
51 + 'destructive' => false,
70 52 'idempotent' => true,
71 53 'priority' => 2.0,
72 54 'openWorldHint' => false,
73 55 ];
@@ -78,18 +60,9 @@
78 60 *
79 61 * @return array<string, mixed>
80 62 */
81 63 private static function schema_properties() {
82 - $props = [];
83 -
84 - foreach ( self::BOOL_KEYS as $key ) {
85 - $props[ $key ] = [ 'type' => 'boolean' ];
86 - }
87 - foreach ( self::STRING_KEYS as $key ) {
88 - $props[ $key ] = [ 'type' => 'string' ];
89 - }
90 -
91 - return $props;
64 + return Settings_Key_Map::site_identity();
92 65 }
93 66
94 67 /**
95 68 * {@inheritDoc}
@@ -144,24 +117,12 @@
144 117 }
145 118
146 119 $mgr = new Site_Identity_Manager();
147 120 $merged = $mgr->get_settings( 'site', null );
148 - $found = false;
121 + $patch = Settings_Key_Map::coerce( Settings_Key_Map::site_identity(), $settings );
122 + $merged = array_merge( $merged, $patch );
149 123
150 - foreach ( self::BOOL_KEYS as $key ) {
151 - if ( array_key_exists( $key, $settings ) ) {
152 - $merged[ $key ] = (bool) $settings[ $key ];
153 - $found = true;
154 - }
155 - }
156 - foreach ( self::STRING_KEYS as $key ) {
157 - if ( array_key_exists( $key, $settings ) ) {
158 - $merged[ $key ] = sanitize_text_field( (string) $settings[ $key ] );
159 - $found = true;
160 - }
161 - }
162 -
163 - if ( ! $found ) {
124 + if ( empty( $patch ) ) {
164 125 return new \WP_Error(
165 126 'thinkrank_no_valid_site_identity_setting_keys',
166 127 __( 'No valid site identity setting keys were provided.', 'thinkrank' ),
167 128 [ 'status' => 400 ]
@@ -170,8 +131,24 @@
170 131
171 132 $result = $mgr->save_settings( 'site', null, $merged );
172 133
173 134 if ( $result ) {
135 + // Mirror the REST route (class-site-identity-endpoint.php): a stored
136 + // setting is not the served file. When a physical robots.txt exists,
137 + // it keeps serving the previous body — and the previous allow/block
138 + // set — until it is rewritten, so an agent that blocked GPTBot got
139 + // `success: true` while the crawler was still allowed. Worse,
140 + // get_robots_txt_delivery() strips the AI block before comparing, on
141 + // the assumption the two are identical by construction, so the admin
142 + // screen reported "in sync" over the drift. Only the keys that
143 + // change the served output trigger the write.
144 + if ( array_key_exists( 'robots_txt_content', $patch )
145 + || array_key_exists( 'robots_txt_enabled', $patch )
146 + || array_key_exists( 'ai_crawler_rules', $patch )
147 + ) {
148 + $mgr->sync_robots_txt_file();
149 + }
150 +
174 151 return [
175 152 'success' => true,
176 153 'message' => __( 'Site identity settings updated.', 'thinkrank' ),
177 154 ];