← All changes
|
includes/abilities/settings/class-update-site-identity-settings.php
+44
-55
2.0.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 ] |
| @@ -169,12 +130,40 @@ | ||
| 169 | 130 | } |
| 170 | 131 | |
| 171 | 132 | $result = $mgr->save_settings( 'site', null, $merged ); |
| 172 | 133 | |
| 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 | + | |
| 151 | + return [ | |
| 152 | + 'success' => true, | |
| 153 | + 'message' => __( 'Site identity settings updated.', 'thinkrank' ), | |
| 154 | + ]; | |
| 155 | + } | |
| 156 | + | |
| 157 | + // The manager recorded why the save failed; pass it on rather than | |
| 158 | + // leaving the caller with a fixed string it cannot act on. | |
| 159 | + $reason = $mgr->get_last_save_error(); | |
| 160 | + | |
| 173 | 161 | return [ |
| 174 | - 'success' => (bool) $result, | |
| 175 | - 'message' => (bool) $result | |
| 176 | - ? __( 'Site identity settings updated.', 'thinkrank' ) | |
| 162 | + 'success' => false, | |
| 163 | + 'message' => '' !== $reason | |
| 164 | + /* translators: %s: reason the save failed. */ | |
| 165 | + ? sprintf( __( 'Failed to update site identity settings: %s', 'thinkrank' ), $reason ) | |
| 177 | 166 | : __( 'Failed to update site identity settings.', 'thinkrank' ), |
| 178 | 167 | ]; |
| 179 | 168 | } |
| 180 | 169 | } |