PluginProbe
Yoast SEO – Advanced SEO with real-time guidance and built-in AI / 18.0
Yoast SEO – Advanced SEO with real-time guidance and built-in AI v18.0
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.0, at inc/options/class-wpseo-option-wpseo.php

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