PluginProbe
Yoast SEO – Advanced SEO with real-time guidance and built-in AI / 18.4
Yoast SEO – Advanced SEO with real-time guidance and built-in AI v18.4
28.5 28.4 28.3 28.2 28.1 28.0 27.9 27.8 27.7 27.6 27.5 trunk 18.0 18.1 18.2 18.3 18.4 18.4.1 18.5 18.5.1 18.6 18.7 18.8 18.9 19.0 All 129 releases
wordpress-seo / inc / options / class-wpseo-option-wpseo.php

class-wpseo-option-wpseo.php in Yoast SEO – Advanced SEO with real-time guidance and built-in AI 18.4, at inc/options/class-wpseo-option-wpseo.php

510 lines 15.1 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * WPSEO plugin file.
4 *
5 * @package WPSEO\Internals\Options
6 */
7
8 /**
9 * Option: wpseo.
10 */
11 class WPSEO_Option_Wpseo extends WPSEO_Option {
12
13 /**
14 * Option name.
15 *
16 * @var string
17 */
18 public $option_name = 'wpseo';
19
20 /**
21 * Array of defaults for the option.
22 *
23 * {@internal Shouldn't be requested directly, use $this->get_defaults();}}
24 *
25 * @var array
26 */
27 protected $defaults = [
28 // Non-form fields, set via (ajax) function.
29 'tracking' => null,
30 'license_server_version' => false,
31 'ms_defaults_set' => false,
32 'ignore_search_engines_discouraged_notice' => false,
33 'indexing_first_time' => true,
34 'indexing_started' => null,
35 'indexing_reason' => '',
36 'indexables_indexing_completed' => false,
37 // Non-form field, should only be set via validation routine.
38 'version' => '', // Leave default as empty to ensure activation/upgrade works.
39 'previous_version' => '',
40 // Form fields.
41 'disableadvanced_meta' => true,
42 'enable_headless_rest_endpoints' => true,
43 'ryte_indexability' => true,
44 'baiduverify' => '', // Text field.
45 'googleverify' => '', // Text field.
46 'msverify' => '', // Text field.
47 'yandexverify' => '',
48 'site_type' => '', // List of options.
49 'has_multiple_authors' => '',
50 'environment_type' => '',
51 'content_analysis_active' => true,
52 'keyword_analysis_active' => true,
53 'enable_admin_bar_menu' => true,
54 'enable_cornerstone_content' => true,
55 'enable_xml_sitemap' => true,
56 'enable_text_link_counter' => true,
57 'show_onboarding_notice' => false,
58 'first_activated_on' => false,
59 'myyoast-oauth' => [
60 'config' => [
61 'clientId' => null,
62 'secret' => null,
63 ],
64 'access_tokens' => [],
65 ],
66 'semrush_integration_active' => true,
67 'semrush_tokens' => [],
68 'semrush_country_code' => 'us',
69 'permalink_structure' => '',
70 'home_url' => '',
71 'dynamic_permalinks' => false,
72 'category_base_url' => '',
73 'tag_base_url' => '',
74 'custom_taxonomy_slugs' => [],
75 'enable_enhanced_slack_sharing' => true,
76 'zapier_integration_active' => false,
77 'zapier_subscription' => [],
78 'zapier_api_key' => '',
79 'enable_metabox_insights' => true,
80 'enable_link_suggestions' => true,
81 'algolia_integration_active' => false,
82 'import_cursors' => [],
83 'workouts_data' => [ 'configuration' => [ 'finishedSteps' => [] ] ],
84 'dismiss_configuration_workout_notice' => false,
85 'importing_completed' => [],
86 'wincher_integration_active' => true,
87 'wincher_tokens' => [],
88 'wincher_automatically_add_keyphrases' => false,
89 'wincher_website_id' => '',
90 'wordproof_integration_active' => true,
91 'first_time_install' => false,
92 'should_redirect_after_install_free' => false,
93 'activation_redirect_timestamp_free' => 0,
94 ];
95
96 /**
97 * Sub-options which should not be overloaded with multi-site defaults.
98 *
99 * @var array
100 */
101 public $ms_exclude = [
102 'ignore_search_engines_discouraged_notice',
103 /* Privacy. */
104 'baiduverify',
105 'googleverify',
106 'msverify',
107 'yandexverify',
108 ];
109
110 /**
111 * Possible values for the site_type option.
112 *
113 * @var array
114 */
115 protected $site_types = [
116 '',
117 'blog',
118 'shop',
119 'news',
120 'smallBusiness',
121 'corporateOther',
122 'personalOther',
123 ];
124
125 /**
126 * Possible environment types.
127 *
128 * @var array
129 */
130 protected $environment_types = [
131 '',
132 'production',
133 'staging',
134 'development',
135 ];
136
137 /**
138 * Possible has_multiple_authors options.
139 *
140 * @var array
141 */
142 protected $has_multiple_authors_options = [
143 '',
144 true,
145 false,
146 ];
147
148 /**
149 * Name for an option higher in the hierarchy to override setting access.
150 *
151 * @var string
152 */
153 protected $override_option_name = 'wpseo_ms';
154
155 /**
156 * Add the actions and filters for the option.
157 *
158 * @todo [JRF => testers] Check if the extra actions below would run into problems if an option
159 * is updated early on and if so, change the call to schedule these for a later action on add/update
160 * instead of running them straight away.
161 */
162 protected function __construct() {
163 parent::__construct();
164
165 /**
166 * Filter: 'wpseo_enable_tracking' - Enables the data tracking of Yoast SEO Premium.
167 *
168 * @api string $is_enabled The enabled state. Default is false.
169 */
170 $this->defaults['tracking'] = apply_filters( 'wpseo_enable_tracking', false );
171
172 /* Clear the cache on update/add. */
173 add_action( 'add_option_' . $this->option_name, [ 'WPSEO_Utils', 'clear_cache' ] );
174 add_action( 'update_option_' . $this->option_name, [ 'WPSEO_Utils', 'clear_cache' ] );
175
176 add_filter( 'admin_title', [ 'Yoast_Input_Validation', 'add_yoast_admin_document_title_errors' ] );
177
178 /**
179 * Filter the `wpseo` option defaults.
180 *
181 * @param array $defaults Array the defaults for the `wpseo` option attributes.
182 */
183 $this->defaults = apply_filters( 'wpseo_option_wpseo_defaults', $this->defaults );
184 }
185
186 /**
187 * Get the singleton instance of this class.
188 *
189 * @return object
190 */
191 public static function get_instance() {
192 if ( ! ( self::$instance instanceof self ) ) {
193 self::$instance = new self();
194 }
195
196 return self::$instance;
197 }
198
199 /**
200 * Add filters to make sure that the option is merged with its defaults before being returned.
201 *
202 * @return void
203 */
204 public function add_option_filters() {
205 parent::add_option_filters();
206
207 list( $hookname, $callback, $priority ) = $this->get_verify_features_option_filter_hook();
208
209 if ( has_filter( $hookname, $callback ) === false ) {
210 add_filter( $hookname, $callback, $priority );
211 }
212 }
213
214 /**
215 * Remove the option filters.
216 * Called from the clean_up methods to make sure we retrieve the original old option.
217 *
218 * @return void
219 */
220 public function remove_option_filters() {
221 parent::remove_option_filters();
222
223 list( $hookname, $callback, $priority ) = $this->get_verify_features_option_filter_hook();
224
225 remove_filter( $hookname, $callback, $priority );
226 }
227
228 /**
229 * Add filters to make sure that the option default is returned if the option is not set.
230 *
231 * @return void
232 */
233 public function add_default_filters() {
234 parent::add_default_filters();
235
236 list( $hookname, $callback, $priority ) = $this->get_verify_features_default_option_filter_hook();
237
238 if ( has_filter( $hookname, $callback ) === false ) {
239 add_filter( $hookname, $callback, $priority );
240 }
241 }
242
243 /**
244 * Remove the default filters.
245 * Called from the validate() method to prevent failure to add new options.
246 *
247 * @return void
248 */
249 public function remove_default_filters() {
250 parent::remove_default_filters();
251
252 list( $hookname, $callback, $priority ) = $this->get_verify_features_default_option_filter_hook();
253
254 remove_filter( $hookname, $callback, $priority );
255 }
256
257 /**
258 * Validate the option.
259 *
260 * @param array $dirty New value for the option.
261 * @param array $clean Clean value for the option, normally the defaults.
262 * @param array $old Old value of the option.
263 *
264 * @return array Validated clean value for the option to be saved to the database.
265 */
266 protected function validate_option( $dirty, $clean, $old ) {
267
268 foreach ( $clean as $key => $value ) {
269 switch ( $key ) {
270 case 'version':
271 $clean[ $key ] = WPSEO_VERSION;
272 break;
273 case 'previous_version':
274 case 'semrush_country_code':
275 case 'license_server_version':
276 case 'home_url':
277 case 'zapier_api_key':
278 case 'wincher_website_id':
279 if ( isset( $dirty[ $key ] ) ) {
280 $clean[ $key ] = $dirty[ $key ];
281 }
282 break;
283 case 'indexing_reason':
284 if ( isset( $dirty[ $key ] ) ) {
285 $clean[ $key ] = sanitize_text_field( $dirty[ $key ] );
286 }
287 break;
288
289 /* Verification strings. */
290 case 'baiduverify':
291 case 'googleverify':
292 case 'msverify':
293 case 'yandexverify':
294 $this->validate_verification_string( $key, $dirty, $old, $clean );
295 break;
296
297 /*
298 * Boolean dismiss warnings - not fields - may not be in form
299 * (and don't need to be either as long as the default is false).
300 */
301 case 'ignore_search_engines_discouraged_notice':
302 case 'ms_defaults_set':
303 if ( isset( $dirty[ $key ] ) ) {
304 $clean[ $key ] = WPSEO_Utils::validate_bool( $dirty[ $key ] );
305 }
306 elseif ( isset( $old[ $key ] ) ) {
307 $clean[ $key ] = WPSEO_Utils::validate_bool( $old[ $key ] );
308 }
309 break;
310
311 case 'site_type':
312 $clean[ $key ] = $old[ $key ];
313 if ( isset( $dirty[ $key ] ) && in_array( $dirty[ $key ], $this->site_types, true ) ) {
314 $clean[ $key ] = $dirty[ $key ];
315 }
316 break;
317
318 case 'environment_type':
319 $clean[ $key ] = $old[ $key ];
320 if ( isset( $dirty[ $key ] ) && in_array( $dirty[ $key ], $this->environment_types, true ) ) {
321 $clean[ $key ] = $dirty[ $key ];
322 }
323 break;
324
325 case 'has_multiple_authors':
326 $clean[ $key ] = $old[ $key ];
327 if ( isset( $dirty[ $key ] ) && in_array( $dirty[ $key ], $this->has_multiple_authors_options, true ) ) {
328 $clean[ $key ] = $dirty[ $key ];
329 }
330
331 break;
332
333 case 'first_activated_on':
334 case 'indexing_started':
335 case 'activation_redirect_timestamp_free':
336 $clean[ $key ] = false;
337 if ( isset( $dirty[ $key ] ) ) {
338 if ( $dirty[ $key ] === false || WPSEO_Utils::validate_int( $dirty[ $key ] ) ) {
339 $clean[ $key ] = $dirty[ $key ];
340 }
341 }
342 break;
343
344 case 'tracking':
345 $clean[ $key ] = ( isset( $dirty[ $key ] ) ? WPSEO_Utils::validate_bool( $dirty[ $key ] ) : null );
346 break;
347
348 case 'myyoast_oauth':
349 case 'semrush_tokens':
350 case 'custom_taxonomy_slugs':
351 case 'zapier_subscription':
352 case 'wincher_tokens':
353 case 'workouts_data':
354 $clean[ $key ] = $old[ $key ];
355
356 if ( isset( $dirty[ $key ] ) ) {
357 $items = $dirty[ $key ];
358 if ( ! is_array( $items ) ) {
359 $items = json_decode( $dirty[ $key ], true );
360 }
361
362 if ( is_array( $items ) ) {
363 $clean[ $key ] = $dirty[ $key ];
364 }
365 }
366
367 break;
368
369 case 'permalink_structure':
370 case 'category_base_url':
371 case 'tag_base_url':
372 if ( isset( $dirty[ $key ] ) ) {
373 $clean[ $key ] = sanitize_option( $key, $dirty[ $key ] );
374 }
375 break;
376
377 case 'import_cursors':
378 case 'importing_completed':
379 if ( isset( $dirty[ $key ] ) && is_array( $dirty[ $key ] ) ) {
380 $clean[ $key ] = $dirty[ $key ];
381 }
382 break;
383
384 /*
385 * Boolean (checkbox) fields.
386 */
387
388 /*
389 * Covers:
390 * 'disableadvanced_meta'
391 * 'enable_headless_rest_endpoints'
392 * 'yoast_tracking'
393 * 'dynamic_permalinks'
394 * 'indexing_first_time'
395 * 'first_time_install'
396 * 'should_redirect_after_install_free'
397 * and most of the feature variables.
398 */
399 default:
400 $clean[ $key ] = ( isset( $dirty[ $key ] ) ? WPSEO_Utils::validate_bool( $dirty[ $key ] ) : false );
401 break;
402 }
403 }
404
405 return $clean;
406 }
407
408 /**
409 * Verifies that the feature variables are turned off if the network is configured so.
410 *
411 * @param mixed $options Value of the option to be returned. Typically an array.
412 *
413 * @return mixed Filtered $options value.
414 */
415 public function verify_features_against_network( $options = [] ) {
416 if ( ! is_array( $options ) || empty( $options ) ) {
417 return $options;
418 }
419
420 // For the feature variables, set their values to off in case they are disabled.
421 $feature_vars = [
422 'disableadvanced_meta' => false,
423 'ryte_indexability' => false,
424 'content_analysis_active' => false,
425 'keyword_analysis_active' => false,
426 'enable_admin_bar_menu' => false,
427 'enable_cornerstone_content' => false,
428 'enable_xml_sitemap' => false,
429 'enable_text_link_counter' => false,
430 'enable_metabox_insights' => false,
431 'enable_link_suggestions' => false,
432 'enable_headless_rest_endpoints' => false,
433 'tracking' => false,
434 'enable_enhanced_slack_sharing' => false,
435 'semrush_integration_active' => false,
436 'zapier_integration_active' => false,
437 'wincher_integration_active' => false,
438 'wordproof_integration_active' => false,
439 ];
440
441 // We can reuse this logic from the base class with the above defaults to parse with the correct feature values.
442 $options = $this->prevent_disabled_options_update( $options, $feature_vars );
443
444 return $options;
445 }
446
447 /**
448 * Gets the filter hook name and callback for adjusting the retrieved option value
449 * against the network-allowed features.
450 *
451 * @return array Array where the first item is the hook name, the second is the hook callback,
452 * and the third is the hook priority.
453 */
454 protected function get_verify_features_option_filter_hook() {
455 return [
456 "option_{$this->option_name}",
457 [ $this, 'verify_features_against_network' ],
458 11,
459 ];
460 }
461
462 /**
463 * Gets the filter hook name and callback for adjusting the default option value against the network-allowed features.
464 *
465 * @return array Array where the first item is the hook name, the second is the hook callback,
466 * and the third is the hook priority.
467 */
468 protected function get_verify_features_default_option_filter_hook() {
469 return [
470 "default_option_{$this->option_name}",
471 [ $this, 'verify_features_against_network' ],
472 11,
473 ];
474 }
475
476 /**
477 * Clean a given option value.
478 *
479 * @param array $option_value Old (not merged with defaults or filtered) option value to
480 * clean according to the rules for this option.
481 * @param string|null $current_version Optional. Version from which to upgrade, if not set,
482 * version specific upgrades will be disregarded.
483 * @param array|null $all_old_option_values Optional. Only used when importing old options to have
484 * access to the real old values, in contrast to the saved ones.
485 *
486 * @return array Cleaned option.
487 */
488 protected function clean_option( $option_value, $current_version = null, $all_old_option_values = null ) {
489 // Deal with value change from text string to boolean.
490 $value_change = [
491 'ignore_search_engines_discouraged_notice',
492 ];
493
494 $target_values = [
495 'ignore',
496 'done',
497 ];
498
499 foreach ( $value_change as $key ) {
500 if ( isset( $option_value[ $key ] )
501 && in_array( $option_value[ $key ], $target_values, true )
502 ) {
503 $option_value[ $key ] = true;
504 }
505 }
506
507 return $option_value;
508 }
509 }
510