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-schema-settings.php

class-update-schema-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-schema-settings.php

266 lines 6.8 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Update schema 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\Schema_Management_System;
14
15 if ( ! defined( 'ABSPATH' ) ) {
16 exit; // Exit if accessed directly.
17 }
18
19 /**
20 * Updates ThinkRank structured-data (schema) settings.
21 *
22 * Schema settings are persisted through the Schema_Management_System SEO
23 * manager. When auto_deploy is enabled, saving these settings automatically
24 * regenerates and redeploys the site-wide schema markup.
25 */
26 class Update_Schema_Settings extends Ability_Base {
27 /**
28 * Boolean-typed schema keys.
29 */
30 private const BOOL_KEYS = [
31 'enabled',
32 'auto_generate_schema',
33 'rich_snippets_optimization',
34 'auto_deploy',
35 'organization_schema',
36 'knowledge_graph',
37 'website_enable_search',
38 'enable_breadcrumbs_schema',
39 'enable_local_business',
40 ];
41
42 /**
43 * Integer-typed schema keys.
44 */
45 private const INT_KEYS = [
46 'cache_duration',
47 ];
48
49 /**
50 * Array-typed schema keys.
51 */
52 private const ARRAY_KEYS = [
53 'enabled_schema_types',
54 'business_opening_hours',
55 'person_same_as',
56 ];
57
58 /**
59 * String-typed schema keys.
60 */
61 private const STRING_KEYS = [
62 'validation_level',
63 'organization_name',
64 'organization_type',
65 'organization_logo',
66 'organization_url',
67 'organization_description',
68 'organization_social_facebook',
69 'organization_social_twitter',
70 'organization_social_linkedin',
71 'organization_social_instagram',
72 'organization_social_youtube',
73 'organization_social_pinterest',
74 'organization_social_whatsapp',
75 'organization_social_telegram',
76 'organization_contact_type',
77 'organization_contact_phone',
78 'organization_contact_email',
79 'organization_contact_hours',
80 'website_name',
81 'website_url',
82 'website_description',
83 'website_author',
84 'website_search_url',
85 'business_price_range',
86 'business_geo_latitude',
87 'business_geo_longitude',
88 'person_name',
89 'person_job_title',
90 'person_description',
91 'person_image',
92 'person_url',
93 'person_email',
94 'person_telephone',
95 'person_address',
96 'person_birth_date',
97 'person_nationality',
98 'person_works_for',
99 ];
100
101 /**
102 * Constructor.
103 */
104 public function __construct() {
105 $this->id = 'thinkrank/update-schema-settings';
106 $this->label = __( 'Update ThinkRank Schema Settings', 'thinkrank' );
107 $this->description = __( 'Update ThinkRank structured-data (schema) settings. When auto_deploy is enabled, saving automatically regenerates and redeploys the site-wide schema markup. Read the current values with get-schema-settings first; only the keys you pass are changed.', 'thinkrank' );
108 }
109
110 /**
111 * {@inheritDoc}
112 *
113 * @return array<string, bool|float|string>
114 */
115 public function get_annotations() {
116 return [
117 'readonly' => false,
118 // Settings writes are recoverable: the matching get-* ability reads
119 // the previous value, so nothing is lost that cannot be put back.
120 // `destructive` is reserved for calls that lose data or reach
121 // outside the site, and marking routine configuration with it made
122 // MCP clients demand a human approval for every save — which users
123 // reported as a permission bug, because the client's refusal reads
124 // as "No approval received" (#675).
125 'destructive' => false,
126 'idempotent' => true,
127 'priority' => 2.0,
128 'openWorldHint' => false,
129 ];
130 }
131
132 /**
133 * Build the JSON schema properties for schema settings.
134 *
135 * @return array<string, mixed>
136 */
137 private static function schema_properties() {
138 $props = [];
139
140 foreach ( self::BOOL_KEYS as $key ) {
141 $props[ $key ] = [ 'type' => 'boolean' ];
142 }
143 foreach ( self::INT_KEYS as $key ) {
144 $props[ $key ] = [ 'type' => 'integer' ];
145 }
146 foreach ( self::ARRAY_KEYS as $key ) {
147 $props[ $key ] = [ 'type' => 'array' ];
148 }
149 foreach ( self::STRING_KEYS as $key ) {
150 $props[ $key ] = [ 'type' => 'string' ];
151 }
152
153 return $props;
154 }
155
156 /**
157 * {@inheritDoc}
158 *
159 * @return array<string, mixed>
160 */
161 public function get_input_schema() {
162 return [
163 'type' => 'object',
164 'additionalProperties' => false,
165 'properties' => [
166 'settings' => [
167 'type' => 'object',
168 'description' => __( 'Schema settings to update.', 'thinkrank' ),
169 'properties' => self::schema_properties(),
170 ],
171 ],
172 'required' => [ 'settings' ],
173 ];
174 }
175
176 /**
177 * {@inheritDoc}
178 *
179 * @return array<string, mixed>
180 */
181 public function get_output_schema() {
182 return [
183 'type' => 'object',
184 'properties' => [
185 'success' => [ 'type' => 'boolean' ],
186 'message' => [ 'type' => 'string' ],
187 ],
188 ];
189 }
190
191 /**
192 * Execute ability.
193 *
194 * @param array<string, mixed> $input Ability input payload.
195 * @return array<string, mixed>|\WP_Error
196 */
197 public function execute( $input ) {
198 $settings = isset( $input['settings'] ) && is_array( $input['settings'] ) ? $input['settings'] : [];
199
200 if ( empty( $settings ) ) {
201 return new \WP_Error(
202 'thinkrank_missing_schema_settings_payload',
203 __( 'A schema settings payload is required.', 'thinkrank' ),
204 [ 'status' => 400 ]
205 );
206 }
207
208 $mgr = new Schema_Management_System();
209 $merged = $mgr->get_settings( 'site', null );
210 $found = false;
211
212 foreach ( self::BOOL_KEYS as $key ) {
213 if ( array_key_exists( $key, $settings ) ) {
214 $merged[ $key ] = (bool) $settings[ $key ];
215 $found = true;
216 }
217 }
218 foreach ( self::INT_KEYS as $key ) {
219 if ( array_key_exists( $key, $settings ) ) {
220 $merged[ $key ] = (int) $settings[ $key ];
221 $found = true;
222 }
223 }
224 foreach ( self::ARRAY_KEYS as $key ) {
225 if ( array_key_exists( $key, $settings ) && is_array( $settings[ $key ] ) ) {
226 $merged[ $key ] = array_values( $settings[ $key ] );
227 $found = true;
228 }
229 }
230 foreach ( self::STRING_KEYS as $key ) {
231 if ( array_key_exists( $key, $settings ) ) {
232 $value = sanitize_text_field( (string) $settings[ $key ] );
233
234 // validation_level is an enum. An out-of-range value would make
235 // the downstream all-or-nothing validator reject the entire
236 // save, silently dropping the other valid fields — so skip an
237 // invalid value here and let the rest of the payload save.
238 if ( 'validation_level' === $key
239 && ! in_array( $value, [ 'strict', 'moderate', 'lenient' ], true ) ) {
240 continue;
241 }
242
243 $merged[ $key ] = $value;
244 $found = true;
245 }
246 }
247
248 if ( ! $found ) {
249 return new \WP_Error(
250 'thinkrank_no_valid_schema_setting_keys',
251 __( 'No valid schema setting keys were provided.', 'thinkrank' ),
252 [ 'status' => 400 ]
253 );
254 }
255
256 $result = $mgr->save_settings( 'site', null, $merged );
257
258 return [
259 'success' => (bool) $result,
260 'message' => (bool) $result
261 ? __( 'Schema settings updated.', 'thinkrank' )
262 : __( 'Failed to update schema settings.', 'thinkrank' ),
263 ];
264 }
265 }
266