PluginProbe
ThinkRank AI SEO – AI SEO Plugin for WordPress: Schema, XML Sitemaps, Meta Tags, Search Console & Local SEO / 2.10.0
ThinkRank AI SEO – AI SEO Plugin for WordPress: Schema, XML Sitemaps, Meta Tags, Search Console & Local SEO v2.10.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
thinkrank / includes / abilities / settings / class-update-sitemap-settings.php

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

183 lines 6.3 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Update sitemap 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\Sitemap_Generator;
14
15 if ( ! defined( 'ABSPATH' ) ) {
16 exit; // Exit if accessed directly.
17 }
18
19 /**
20 * Updates ThinkRank XML sitemap settings.
21 *
22 * Sitemap settings are persisted through the Sitemap_Generator SEO manager.
23 */
24 class Update_Sitemap_Settings extends Ability_Base {
25 /**
26 * Constructor.
27 */
28 public function __construct() {
29 $this->id = 'thinkrank/update-sitemap-settings';
30 $this->label = __( 'Update ThinkRank Sitemap Settings', 'thinkrank' );
31 $this->description = __( 'Update ThinkRank XML sitemap settings: per-type inclusion toggles, exclusion lists, and the index/splitting, styling, filename and search-engine ping options. Read the current values with get-sitemap-settings first; only the keys you pass are changed.', 'thinkrank' );
32 }
33
34 /**
35 * {@inheritDoc}
36 *
37 * @return array<string, bool|float|string>
38 */
39 public function get_annotations() {
40 return [
41 'readonly' => false,
42 // Settings writes are recoverable: the matching get-* ability reads
43 // the previous value, so nothing is lost that cannot be put back.
44 // `destructive` is reserved for calls that lose data or reach
45 // outside the site, and marking routine configuration with it made
46 // MCP clients demand a human approval for every save — which users
47 // reported as a permission bug, because the client's refusal reads
48 // as "No approval received" (#675).
49 'destructive' => false,
50 'idempotent' => true,
51 'priority' => 2.0,
52 'openWorldHint' => false,
53 ];
54 }
55
56 /**
57 * {@inheritDoc}
58 *
59 * @return array<string, mixed>
60 */
61 public function get_input_schema() {
62 return [
63 'type' => 'object',
64 'additionalProperties' => false,
65 'properties' => [
66 'settings' => [
67 'type' => 'object',
68 'description' => __( 'Sitemap settings to update.', 'thinkrank' ),
69 'properties' => Settings_Key_Map::sitemap(),
70 ],
71 'regenerate' => [
72 'type' => 'boolean',
73 'default' => true,
74 'description' => __( 'Rebuild the served sitemap before returning, so the change is live when this call reports success. Leave it on unless you are making several changes in a row and want to rebuild once at the end, in which case the last call should set it to true.', 'thinkrank' ),
75 ],
76 ],
77 'required' => [ 'settings' ],
78 ];
79 }
80
81 /**
82 * {@inheritDoc}
83 *
84 * @return array<string, mixed>
85 */
86 public function get_output_schema() {
87 return [
88 'type' => 'object',
89 'properties' => [
90 'success' => [ 'type' => 'boolean' ],
91 'message' => [ 'type' => 'string' ],
92 // Whether the SERVED sitemap reflects the change yet. Saving and
93 // serving are separate steps, and reporting only the save let an
94 // agent state the exclusion had taken effect while the static
95 // file still listed the page (#764).
96 'rebuilt' => [ 'type' => 'boolean' ],
97 'pending_since' => [
98 'type' => [ 'integer', 'null' ],
99 'description' => __( 'Unix time a rebuild has been outstanding since, when the served sitemap has not caught up.', 'thinkrank' ),
100 ],
101 ],
102 ];
103 }
104
105 /**
106 * Execute ability.
107 *
108 * @param array<string, mixed> $input Ability input payload.
109 * @return array<string, mixed>|\WP_Error
110 */
111 public function execute( $input ) {
112 $settings = isset( $input['settings'] ) && is_array( $input['settings'] ) ? $input['settings'] : [];
113
114 if ( empty( $settings ) ) {
115 return new \WP_Error(
116 'thinkrank_missing_sitemap_settings_payload',
117 __( 'A sitemap settings payload is required.', 'thinkrank' ),
118 [ 'status' => 400 ]
119 );
120 }
121
122 $gen = new Sitemap_Generator();
123 $merged = $gen->get_settings( 'site', null );
124 $patch = Settings_Key_Map::coerce( Settings_Key_Map::sitemap(), $settings );
125 $merged = array_merge( $merged, $patch );
126
127 if ( empty( $patch ) ) {
128 return new \WP_Error(
129 'thinkrank_no_valid_sitemap_setting_keys',
130 __( 'No valid sitemap setting keys were provided.', 'thinkrank' ),
131 [ 'status' => 400 ]
132 );
133 }
134
135 $result = $gen->save_settings( 'site', null, $merged );
136
137 if ( ! $result ) {
138 return [
139 'success' => false,
140 'message' => __( 'Failed to update sitemap settings.', 'thinkrank' ),
141 'rebuilt' => false,
142 ];
143 }
144
145 // Mark the rebuild outstanding either way, so a caller that opts out of
146 // the synchronous rebuild still converges via cron or the request-time
147 // takeover, and so a failed rebuild below is retried rather than lost.
148 $gen->schedule_regeneration();
149
150 $regenerate = ! isset( $input['regenerate'] ) || (bool) $input['regenerate'];
151
152 // Rebuild inline rather than leaving it to WP-Cron. The sitemap is a
153 // static file on most installs, so nothing re-runs PHP for it; where
154 // cron does not fire, the old debounced-only path left the served file
155 // stale indefinitely while this ability had already reported success
156 // (#764). A settings change is deliberate and infrequent, and the
157 // rebuild is lock-guarded, so doing it now is the honest thing.
158 $rebuilt = $regenerate ? $gen->regenerate_sitemap_from_settings() : false;
159
160 $pending_since = Sitemap_Generator::regeneration_pending_since();
161
162 if ( $rebuilt && 0 === $pending_since ) {
163 return [
164 'success' => true,
165 'message' => __( 'Sitemap settings updated and the served sitemap was rebuilt.', 'thinkrank' ),
166 'rebuilt' => true,
167 'pending_since' => null,
168 ];
169 }
170
171 // Saved, but the file a crawler fetches does not reflect it yet. Say so
172 // rather than report a success the caller cannot verify.
173 return [
174 'success' => true,
175 'message' => $regenerate
176 ? __( 'Sitemap settings updated, but the served sitemap could not be rebuilt yet and still shows the previous contents. It will be retried automatically; purge-caches with the "sitemap" scope forces another attempt.', 'thinkrank' )
177 : __( 'Sitemap settings updated. The served sitemap has not been rebuilt, as requested; run purge-caches with the "sitemap" scope when you are ready to publish the change.', 'thinkrank' ),
178 'rebuilt' => false,
179 'pending_since' => $pending_since > 0 ? $pending_since : null,
180 ];
181 }
182 }
183