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
thinkrank / includes / abilities / settings / class-update-site-identity-settings.php

class-update-site-identity-settings.php in ThinkRank AI SEO – AI SEO Plugin for WordPress: Schema, XML Sitemaps, Meta Tags, Search Console & Local SEO 2.7.0, at includes/abilities/settings/class-update-site-identity-settings.php

170 lines 5.3 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Update site identity settings ability.
4 *
5 * @package ThinkRank\Abilities\Settings
6 */
7
8 declare(strict_types=1);
9
10 namespace ThinkRank\Abilities\Settings;
11
12 use ThinkRank\Abilities\Ability_Base;
13 use ThinkRank\SEO\Site_Identity_Manager;
14
15 if ( ! defined( 'ABSPATH' ) ) {
16 exit; // Exit if accessed directly.
17 }
18
19 /**
20 * Updates ThinkRank site identity settings.
21 *
22 * Only the non-robots identity keys are writable here. The robots.txt keys
23 * (robots_txt_enabled, allow_search_engines, robots_txt_content) are never
24 * touched so this ability cannot clobber the robots.txt configuration.
25 */
26 class Update_Site_Identity_Settings extends Ability_Base {
27 /**
28 * Constructor.
29 */
30 public function __construct() {
31 $this->id = 'thinkrank/update-site-identity-settings';
32 $this->label = __( 'Update ThinkRank Site Identity Settings', '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' );
34 }
35
36 /**
37 * {@inheritDoc}
38 *
39 * @return array<string, bool|float|string>
40 */
41 public function get_annotations() {
42 return [
43 'readonly' => false,
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,
52 'idempotent' => true,
53 'priority' => 2.0,
54 'openWorldHint' => false,
55 ];
56 }
57
58 /**
59 * Build the JSON schema properties for site identity settings.
60 *
61 * @return array<string, mixed>
62 */
63 private static function schema_properties() {
64 return Settings_Key_Map::site_identity();
65 }
66
67 /**
68 * {@inheritDoc}
69 *
70 * @return array<string, mixed>
71 */
72 public function get_input_schema() {
73 return [
74 'type' => 'object',
75 'additionalProperties' => false,
76 'properties' => [
77 'settings' => [
78 'type' => 'object',
79 'description' => __( 'Site identity settings to update.', 'thinkrank' ),
80 'properties' => self::schema_properties(),
81 ],
82 ],
83 'required' => [ 'settings' ],
84 ];
85 }
86
87 /**
88 * {@inheritDoc}
89 *
90 * @return array<string, mixed>
91 */
92 public function get_output_schema() {
93 return [
94 'type' => 'object',
95 'properties' => [
96 'success' => [ 'type' => 'boolean' ],
97 'message' => [ 'type' => 'string' ],
98 ],
99 ];
100 }
101
102 /**
103 * Execute ability.
104 *
105 * @param array<string, mixed> $input Ability input payload.
106 * @return array<string, mixed>|\WP_Error
107 */
108 public function execute( $input ) {
109 $settings = isset( $input['settings'] ) && is_array( $input['settings'] ) ? $input['settings'] : [];
110
111 if ( empty( $settings ) ) {
112 return new \WP_Error(
113 'thinkrank_missing_site_identity_settings_payload',
114 __( 'A site identity settings payload is required.', 'thinkrank' ),
115 [ 'status' => 400 ]
116 );
117 }
118
119 $mgr = new Site_Identity_Manager();
120 $merged = $mgr->get_settings( 'site', null );
121 $patch = Settings_Key_Map::coerce( Settings_Key_Map::site_identity(), $settings );
122 $merged = array_merge( $merged, $patch );
123
124 if ( empty( $patch ) ) {
125 return new \WP_Error(
126 'thinkrank_no_valid_site_identity_setting_keys',
127 __( 'No valid site identity setting keys were provided.', 'thinkrank' ),
128 [ 'status' => 400 ]
129 );
130 }
131
132 $result = $mgr->save_settings( 'site', null, $merged );
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
161 return [
162 'success' => false,
163 'message' => '' !== $reason
164 /* translators: %s: reason the save failed. */
165 ? sprintf( __( 'Failed to update site identity settings: %s', 'thinkrank' ), $reason )
166 : __( 'Failed to update site identity settings.', 'thinkrank' ),
167 ];
168 }
169 }
170