PageRenderTime 85ms CodeModel.GetById 28ms app.highlight 49ms RepoModel.GetById 1ms app.codeStats 0ms

/wp-content/plugins/wordpress-seo/inc/options/class-wpseo-option-titles.php

https://bitbucket.org/carloskikea/helpet
PHP | 816 lines | 462 code | 89 blank | 265 comment | 86 complexity | a308c3081f206570c84fd48db71a0745 MD5 | raw file
  1<?php
  2/**
  3 * WPSEO plugin file.
  4 *
  5 * @package WPSEO\Internals\Options
  6 */
  7
  8/**
  9 * Option: wpseo_titles.
 10 */
 11class WPSEO_Option_Titles extends WPSEO_Option {
 12
 13	/**
 14	 * @var  string  Option name.
 15	 */
 16	public $option_name = 'wpseo_titles';
 17
 18	/**
 19	 * @var  array  Array of defaults for the option.
 20	 *        Shouldn't be requested directly, use $this->get_defaults();
 21	 *
 22	 * {@internal Note: Some of the default values are added via the translate_defaults() method.}}
 23	 */
 24	protected $defaults = array(
 25		// Non-form fields, set via (ajax) function.
 26		'title_test'                    => 0,
 27		// Form fields.
 28		'forcerewritetitle'             => false,
 29		'separator'                     => 'sc-dash',
 30		'title-home-wpseo'              => '%%sitename%% %%page%% %%sep%% %%sitedesc%%', // Text field.
 31		'title-author-wpseo'            => '', // Text field.
 32		'title-archive-wpseo'           => '%%date%% %%page%% %%sep%% %%sitename%%', // Text field.
 33		'title-search-wpseo'            => '', // Text field.
 34		'title-404-wpseo'               => '', // Text field.
 35
 36		'metadesc-home-wpseo'           => '', // Text area.
 37		'metadesc-author-wpseo'         => '', // Text area.
 38		'metadesc-archive-wpseo'        => '', // Text area.
 39		'rssbefore'                     => '', // Text area.
 40		'rssafter'                      => '', // Text area.
 41
 42		'noindex-author-wpseo'          => false,
 43		'noindex-author-noposts-wpseo'  => true,
 44		'noindex-archive-wpseo'         => true,
 45
 46		'disable-author'                => false,
 47		'disable-date'                  => false,
 48		'disable-post_format'           => false,
 49		'disable-attachment'            => true,
 50
 51		'breadcrumbs-404crumb'          => '', // Text field.
 52		'breadcrumbs-display-blog-page' => true,
 53		'breadcrumbs-boldlast'          => false,
 54		'breadcrumbs-archiveprefix'     => '', // Text field.
 55		'breadcrumbs-enable'            => false,
 56		'breadcrumbs-home'              => '', // Text field.
 57		'breadcrumbs-prefix'            => '', // Text field.
 58		'breadcrumbs-searchprefix'      => '', // Text field.
 59		'breadcrumbs-sep'               => '&raquo;', // Text field.
 60
 61		'website_name'                  => '',
 62		'person_name'                   => '',
 63		'alternate_website_name'        => '',
 64		'company_logo'                  => '',
 65		'company_name'                  => '',
 66		'company_or_person'             => '',
 67
 68		'stripcategorybase'             => false,
 69
 70		/**
 71		 * Uses enrich_defaults to add more along the lines of:
 72		 * - 'title-' . $pt->name                => ''; // Text field.
 73		 * - 'metadesc-' . $pt->name             => ''; // Text field.
 74		 * - 'noindex-' . $pt->name              => false;
 75		 * - 'showdate-' . $pt->name             => false;
 76		 * - 'display-metabox-pt-' . $pt->name   => false;
 77		 *
 78		 * - 'title-ptarchive-' . $pt->name      => ''; // Text field.
 79		 * - 'metadesc-ptarchive-' . $pt->name   => ''; // Text field.
 80		 * - 'bctitle-ptarchive-' . $pt->name    => ''; // Text field.
 81		 * - 'noindex-ptarchive-' . $pt->name    => false;
 82		 *
 83		 * - 'title-tax-' . $tax->name           => '''; // Text field.
 84		 * - 'metadesc-tax-' . $tax->name        => ''; // Text field.
 85		 * - 'noindex-tax-' . $tax->name         => false;
 86		 * - 'display-metabox-tax-' . $tax->name => false;
 87		 */
 88	);
 89
 90	/**
 91	 * @var  array  Array of variable option name patterns for the option.
 92	 */
 93	protected $variable_array_key_patterns = array(
 94		'title-',
 95		'metadesc-',
 96		'noindex-',
 97		'showdate-',
 98		'display-metabox-pt-',
 99		'bctitle-ptarchive-',
100		'post_types-',
101		'taxonomy-',
102	);
103
104	/**
105	 * @var array  Array of sub-options which should not be overloaded with multi-site defaults.
106	 */
107	public $ms_exclude = array(
108		/* theme dependent */
109		'title_test',
110		'forcerewritetitle',
111	);
112
113	/**
114	 * @var array Array of the separator options. To get these options use WPSEO_Option_Titles::get_instance()->get_separator_options().
115	 */
116	private $separator_options = array(
117		'sc-dash'   => '-',
118		'sc-ndash'  => '&ndash;',
119		'sc-mdash'  => '&mdash;',
120		'sc-middot' => '&middot;',
121		'sc-bull'   => '&bull;',
122		'sc-star'   => '*',
123		'sc-smstar' => '&#8902;',
124		'sc-pipe'   => '|',
125		'sc-tilde'  => '~',
126		'sc-laquo'  => '&laquo;',
127		'sc-raquo'  => '&raquo;',
128		'sc-lt'     => '&lt;',
129		'sc-gt'     => '&gt;',
130	);
131
132	/**
133	 * Add the actions and filters for the option.
134	 *
135	 * @todo [JRF => testers] Check if the extra actions below would run into problems if an option
136	 * is updated early on and if so, change the call to schedule these for a later action on add/update
137	 * instead of running them straight away.
138	 *
139	 * @return \WPSEO_Option_Titles
140	 */
141	protected function __construct() {
142		parent::__construct();
143		add_action( 'update_option_' . $this->option_name, array( 'WPSEO_Utils', 'clear_cache' ) );
144		add_action( 'init', array( $this, 'end_of_init' ), 999 );
145	}
146
147
148	/**
149	 * Make sure we can recognize the right action for the double cleaning.
150	 */
151	public function end_of_init() {
152		do_action( 'wpseo_double_clean_titles' );
153	}
154
155	/**
156	 * Get the singleton instance of this class.
157	 *
158	 * @return object
159	 */
160	public static function get_instance() {
161		if ( ! ( self::$instance instanceof self ) ) {
162			self::$instance = new self();
163		}
164
165		return self::$instance;
166	}
167
168	/**
169	 * Get the available separator options.
170	 *
171	 * @return array
172	 */
173	public function get_separator_options() {
174		$separators = $this->separator_options;
175
176		/**
177		 * Allow altering the array with separator options.
178		 *
179		 * @api  array  $separator_options  Array with the separator options.
180		 */
181		$filtered_separators = apply_filters( 'wpseo_separator_options', $separators );
182
183		if ( is_array( $filtered_separators ) && $filtered_separators !== array() ) {
184			$separators = array_merge( $separators, $filtered_separators );
185		}
186
187		return $separators;
188	}
189
190	/**
191	 * Translate strings used in the option defaults.
192	 *
193	 * @return void
194	 */
195	public function translate_defaults() {
196		/* translators: 1: Author name; 2: Site name. */
197		$this->defaults['title-author-wpseo'] = sprintf( __( '%1$s, Author at %2$s', 'wordpress-seo' ), '%%name%%', '%%sitename%%' ) . ' %%page%% ';
198		/* translators: %s expands to the search phrase. */
199		$this->defaults['title-search-wpseo'] = sprintf( __( 'You searched for %s', 'wordpress-seo' ), '%%searchphrase%%' ) . ' %%page%% %%sep%% %%sitename%%';
200		$this->defaults['title-404-wpseo']    = __( 'Page not found', 'wordpress-seo' ) . ' %%sep%% %%sitename%%';
201		/* translators: 1: link to post; 2: link to blog. */
202		$this->defaults['rssafter'] = sprintf( __( 'The post %1$s appeared first on %2$s.', 'wordpress-seo' ), '%%POSTLINK%%', '%%BLOGLINK%%' );
203
204		$this->defaults['breadcrumbs-404crumb']      = __( 'Error 404: Page not found', 'wordpress-seo' );
205		$this->defaults['breadcrumbs-archiveprefix'] = __( 'Archives for', 'wordpress-seo' );
206		$this->defaults['breadcrumbs-home']          = __( 'Home', 'wordpress-seo' );
207		$this->defaults['breadcrumbs-searchprefix']  = __( 'You searched for', 'wordpress-seo' );
208	}
209
210
211	/**
212	 * Add dynamically created default options based on available post types and taxonomies.
213	 *
214	 * @return  void
215	 */
216	public function enrich_defaults() {
217		/*
218		 * Retrieve all the relevant post type and taxonomy arrays.
219		 *
220		 * WPSEO_Post_Type::get_accessible_post_types() should *not* be used here.
221		 * These are the defaults and can be prepared for any public post type.
222		 */
223		$post_type_names = get_post_types( array( 'public' => true ), 'names' );
224
225		$post_type_objects_custom = get_post_types(
226			array(
227				'public'   => true,
228				'_builtin' => false,
229			),
230			'objects'
231		);
232
233		$taxonomy_names = get_taxonomies( array( 'public' => true ), 'names' );
234
235
236		if ( $post_type_names !== array() ) {
237			foreach ( $post_type_names as $pt ) {
238				$this->defaults[ 'title-' . $pt ]              = '%%title%% %%page%% %%sep%% %%sitename%%'; // Text field.
239				$this->defaults[ 'metadesc-' . $pt ]           = ''; // Text area.
240				$this->defaults[ 'noindex-' . $pt ]            = false;
241				$this->defaults[ 'showdate-' . $pt ]           = false;
242				$this->defaults[ 'display-metabox-pt-' . $pt ] = true;
243			}
244			unset( $pt );
245		}
246
247		if ( $post_type_objects_custom !== array() ) {
248			/* translators: %s expands to the name of a post type (plural). */
249			$archive = sprintf( __( '%s Archive', 'wordpress-seo' ), '%%pt_plural%%' );
250			foreach ( $post_type_objects_custom as $pt ) {
251				if ( ! $pt->has_archive ) {
252					continue;
253				}
254
255				$this->defaults[ 'title-ptarchive-' . $pt->name ]    = $archive . ' %%page%% %%sep%% %%sitename%%'; // Text field.
256				$this->defaults[ 'metadesc-ptarchive-' . $pt->name ] = ''; // Text area.
257				$this->defaults[ 'bctitle-ptarchive-' . $pt->name ]  = ''; // Text field.
258				$this->defaults[ 'noindex-ptarchive-' . $pt->name ]  = false;
259			}
260			unset( $pt );
261		}
262
263		if ( $taxonomy_names !== array() ) {
264			/* translators: %s expands to the variable used for term title. */
265			$archives = sprintf( __( '%s Archives', 'wordpress-seo' ), '%%term_title%%' );
266			foreach ( $taxonomy_names as $tax ) {
267				$this->defaults[ 'title-tax-' . $tax ]           = $archives . ' %%page%% %%sep%% %%sitename%%'; // Text field.
268				$this->defaults[ 'metadesc-tax-' . $tax ]        = ''; // Text area.
269				$this->defaults[ 'display-metabox-tax-' . $tax ] = true;
270
271				if ( $tax !== 'post_format' ) {
272					$this->defaults[ 'noindex-tax-' . $tax ] = false;
273				}
274				else {
275					$this->defaults[ 'noindex-tax-' . $tax ] = true;
276				}
277			}
278			unset( $tax );
279		}
280
281		/*
282		 * Retrieve all the relevant post type and taxonomy arrays.
283		 *
284		 * WPSEO_Post_Type::get_accessible_post_types() should *not* be used here.
285		 */
286		$post_type_names       = get_post_types( array( 'public' => true ), 'names' );
287		$taxonomy_names_custom = get_taxonomies(
288			array(
289				'public'   => true,
290				'_builtin' => false,
291			),
292			'names'
293		);
294
295		if ( $post_type_names !== array() ) {
296			foreach ( $post_type_names as $pt ) {
297				$pto_taxonomies = get_object_taxonomies( $pt, 'names' );
298				if ( $pto_taxonomies !== array() ) {
299					$this->defaults[ 'post_types-' . $pt . '-maintax' ] = 0; // Select box.
300				}
301				unset( $pto_taxonomies );
302			}
303			unset( $pt );
304		}
305
306		if ( $taxonomy_names_custom !== array() ) {
307			foreach ( $taxonomy_names_custom as $tax ) {
308				$this->defaults[ 'taxonomy-' . $tax . '-ptparent' ] = 0; // Select box;.
309			}
310			unset( $tax );
311		}
312	}
313
314	/**
315	 * Validate the option.
316	 *
317	 * @param  array $dirty New value for the option.
318	 * @param  array $clean Clean value for the option, normally the defaults.
319	 * @param  array $old   Old value of the option.
320	 *
321	 * @return  array      Validated clean value for the option to be saved to the database.
322	 */
323	protected function validate_option( $dirty, $clean, $old ) {
324		$allowed_post_types = $this->get_allowed_post_types();
325
326		foreach ( $clean as $key => $value ) {
327			$switch_key = $this->get_switch_key( $key );
328
329			switch ( $switch_key ) {
330				/* Breadcrumbs text fields. */
331				case 'breadcrumbs-404crumb':
332				case 'breadcrumbs-archiveprefix':
333				case 'breadcrumbs-home':
334				case 'breadcrumbs-prefix':
335				case 'breadcrumbs-searchprefix':
336				case 'breadcrumbs-sep':
337					if ( isset( $dirty[ $key ] ) ) {
338						$clean[ $key ] = wp_kses_post( $dirty[ $key ] );
339					}
340					break;
341
342				/*
343				 * Text fields.
344				 */
345
346				/*
347				 * Covers:
348				 *  'title-home-wpseo', 'title-author-wpseo', 'title-archive-wpseo',
349				 *  'title-search-wpseo', 'title-404-wpseo'
350				 *  'title-' . $pt->name
351				 *  'title-ptarchive-' . $pt->name
352				 *  'title-tax-' . $tax->name
353				 */
354				case 'website_name':
355				case 'alternate_website_name':
356				case 'title-':
357					if ( isset( $dirty[ $key ] ) ) {
358						$clean[ $key ] = WPSEO_Utils::sanitize_text_field( $dirty[ $key ] );
359					}
360					break;
361
362				case 'company_or_person':
363					if ( isset( $dirty[ $key ] ) && $dirty[ $key ] !== '' ) {
364						if ( in_array( $dirty[ $key ], array( 'company', 'person' ), true ) ) {
365							$clean[ $key ] = $dirty[ $key ];
366						}
367					}
368					break;
369
370				case 'company_logo':
371					$this->validate_url( $key, $dirty, $old, $clean );
372					break;
373
374				/*
375				 * Covers:
376				 *  'metadesc-home-wpseo', 'metadesc-author-wpseo', 'metadesc-archive-wpseo'
377				 *  'metadesc-' . $pt->name
378				 *  'metadesc-ptarchive-' . $pt->name
379				 *  'metadesc-tax-' . $tax->name
380				 *  and also:
381				 *  'bctitle-ptarchive-' . $pt->name
382				 */
383				case 'metadesc-':
384				case 'bctitle-ptarchive-':
385				case 'company_name':
386				case 'person_name':
387					if ( isset( $dirty[ $key ] ) && $dirty[ $key ] !== '' ) {
388						$clean[ $key ] = WPSEO_Utils::sanitize_text_field( $dirty[ $key ] );
389					}
390					break;
391
392				/*
393				 * Covers: 'rssbefore', 'rssafter'
394				 */
395				case 'rssbefore':
396				case 'rssafter':
397					if ( isset( $dirty[ $key ] ) ) {
398						$clean[ $key ] = wp_kses_post( $dirty[ $key ] );
399					}
400					break;
401
402				/* 'post_types-' . $pt->name . '-maintax' fields. */
403				case 'post_types-':
404					$post_type  = str_replace( array( 'post_types-', '-maintax' ), '', $key );
405					$taxonomies = get_object_taxonomies( $post_type, 'names' );
406
407					if ( isset( $dirty[ $key ] ) ) {
408						if ( $taxonomies !== array() && in_array( $dirty[ $key ], $taxonomies, true ) ) {
409							$clean[ $key ] = $dirty[ $key ];
410						}
411						elseif ( (string) $dirty[ $key ] === '0' || (string) $dirty[ $key ] === '' ) {
412							$clean[ $key ] = 0;
413						}
414						elseif ( sanitize_title_with_dashes( $dirty[ $key ] ) === $dirty[ $key ] ) {
415							// Allow taxonomies which may not be registered yet.
416							$clean[ $key ] = $dirty[ $key ];
417						}
418						else {
419							if ( isset( $old[ $key ] ) ) {
420								$clean[ $key ] = sanitize_title_with_dashes( $old[ $key ] );
421							}
422							/**
423							 * @todo [JRF => whomever] maybe change the untranslated $pt name in the
424							 * error message to the nicely translated label ?
425							 */
426							add_settings_error(
427								$this->group_name, // Slug title of the setting.
428								'_' . $key, // Suffix-id for the error message box.
429								/* translators: %s expands to a post type. */
430								sprintf( __( 'Please select a valid taxonomy for post type "%s"', 'wordpress-seo' ), $post_type ), // The error message.
431								'error' // Error type, either 'error' or 'updated'.
432							);
433						}
434					}
435					elseif ( isset( $old[ $key ] ) ) {
436						$clean[ $key ] = sanitize_title_with_dashes( $old[ $key ] );
437					}
438					unset( $taxonomies, $post_type );
439					break;
440
441				/* 'taxonomy-' . $tax->name . '-ptparent' fields. */
442				case 'taxonomy-':
443					if ( isset( $dirty[ $key ] ) ) {
444						if ( $allowed_post_types !== array() && in_array( $dirty[ $key ], $allowed_post_types, true ) ) {
445							$clean[ $key ] = $dirty[ $key ];
446						}
447						elseif ( (string) $dirty[ $key ] === '0' || (string) $dirty[ $key ] === '' ) {
448							$clean[ $key ] = 0;
449						}
450						elseif ( sanitize_key( $dirty[ $key ] ) === $dirty[ $key ] ) {
451							// Allow taxonomies which may not be registered yet.
452							$clean[ $key ] = $dirty[ $key ];
453						}
454						else {
455							if ( isset( $old[ $key ] ) ) {
456								$clean[ $key ] = sanitize_key( $old[ $key ] );
457							}
458							/**
459							 * @todo [JRF =? whomever] maybe change the untranslated $tax name in the
460							 * error message to the nicely translated label ?
461							 */
462							$tax = str_replace( array( 'taxonomy-', '-ptparent' ), '', $key );
463							add_settings_error(
464								$this->group_name, // Slug title of the setting.
465								'_' . $tax, // Suffix-id for the error message box.
466								/* translators: %s expands to a taxonomy slug. */
467								sprintf( __( 'Please select a valid post type for taxonomy "%s"', 'wordpress-seo' ), $tax ), // The error message.
468								'error' // Error type, either 'error' or 'updated'.
469							);
470							unset( $tax );
471						}
472					}
473					elseif ( isset( $old[ $key ] ) ) {
474						$clean[ $key ] = sanitize_key( $old[ $key ] );
475					}
476					break;
477
478				/* Integer field - not in form. */
479				case 'title_test':
480					if ( isset( $dirty[ $key ] ) ) {
481						$int = WPSEO_Utils::validate_int( $dirty[ $key ] );
482						if ( $int !== false && $int >= 0 ) {
483							$clean[ $key ] = $int;
484						}
485					}
486					elseif ( isset( $old[ $key ] ) ) {
487						$int = WPSEO_Utils::validate_int( $old[ $key ] );
488						if ( $int !== false && $int >= 0 ) {
489							$clean[ $key ] = $int;
490						}
491					}
492					break;
493
494				/* Separator field - Radio */
495				case 'separator':
496					if ( isset( $dirty[ $key ] ) && $dirty[ $key ] !== '' ) {
497
498						// Get separator fields.
499						$separator_fields = $this->get_separator_options();
500
501						// Check if the given separator is exists.
502						if ( isset( $separator_fields[ $dirty[ $key ] ] ) ) {
503							$clean[ $key ] = $dirty[ $key ];
504						}
505					}
506					break;
507
508				/*
509				 * Boolean fields.
510				 */
511
512				/*
513				 * Covers:
514				 *  'noindex-author-wpseo', 'noindex-author-noposts-wpseo', 'noindex-archive-wpseo'
515				 *  'noindex-' . $pt->name
516				 *  'noindex-ptarchive-' . $pt->name
517				 *  'noindex-tax-' . $tax->name
518				 *  'forcerewritetitle':
519				 *  'noodp':
520				 *  'noydir':
521				 *  'disable-author':
522				 *  'disable-date':
523				 *  'disable-post_format';
524				 *  'noindex-'
525				 *  'showdate-'
526				 *  'showdate-'. $pt->name
527				 *  'display-metabox-pt-'
528				 *  'display-metabox-pt-'. $pt->name
529				 *  'display-metabox-tax-'
530				 *  'display-metabox-tax-' . $tax->name
531				 *  'breadcrumbs-display-blog-page'
532				 *  'breadcrumbs-boldlast'
533				 *  'breadcrumbs-enable'
534				 *  'stripcategorybase'
535				 */
536				default:
537					$clean[ $key ] = ( isset( $dirty[ $key ] ) ? WPSEO_Utils::validate_bool( $dirty[ $key ] ) : false );
538					break;
539			}
540		}
541
542		return $clean;
543	}
544
545	/**
546	 * Retrieve a list of the allowed post types as breadcrumb parent for a taxonomy.
547	 * Helper method for validation.
548	 *
549	 * {@internal Don't make static as new types may still be registered.}}
550	 *
551	 * @return array
552	 */
553	protected function get_allowed_post_types() {
554		$allowed_post_types = array();
555
556		/*
557		 * WPSEO_Post_Type::get_accessible_post_types() should *not* be used here.
558		 */
559		$post_types = get_post_types( array( 'public' => true ), 'objects' );
560
561		if ( get_option( 'show_on_front' ) === 'page' && get_option( 'page_for_posts' ) > 0 ) {
562			$allowed_post_types[] = 'post';
563		}
564
565		if ( is_array( $post_types ) && $post_types !== array() ) {
566			foreach ( $post_types as $type ) {
567				if ( $type->has_archive ) {
568					$allowed_post_types[] = $type->name;
569				}
570			}
571		}
572
573		return $allowed_post_types;
574	}
575
576	/**
577	 * Clean a given option value.
578	 *
579	 * @param  array  $option_value          Old (not merged with defaults or filtered) option value to
580	 *                                       clean according to the rules for this option.
581	 * @param  string $current_version       Optional. Version from which to upgrade, if not set,
582	 *                                       version specific upgrades will be disregarded.
583	 * @param  array  $all_old_option_values Optional. Only used when importing old options to have
584	 *                                       access to the real old values, in contrast to the saved ones.
585	 *
586	 * @return  array            Cleaned option.
587	 */
588	protected function clean_option( $option_value, $current_version = null, $all_old_option_values = null ) {
589		static $original = null;
590
591		// Double-run this function to ensure renaming of the taxonomy options will work.
592		if ( ! isset( $original ) && has_action( 'wpseo_double_clean_titles', array(
593				$this,
594				'clean',
595			) ) === false
596		) {
597			add_action( 'wpseo_double_clean_titles', array( $this, 'clean' ) );
598			$original = $option_value;
599		}
600
601		/*
602		 * Move options from very old option to this one.
603		 *
604		 * {@internal Don't rename to the 'current' names straight away as that would prevent
605		 *            the rename/unset combi below from working.}}
606		 *
607		 * @todo [JRF] maybe figure out a smarter way to deal with this.
608		 */
609		$old_option = null;
610		if ( isset( $all_old_option_values ) ) {
611			// Ok, we have an import.
612			if ( isset( $all_old_option_values['wpseo_indexation'] ) && is_array( $all_old_option_values['wpseo_indexation'] ) && $all_old_option_values['wpseo_indexation'] !== array() ) {
613				$old_option = $all_old_option_values['wpseo_indexation'];
614			}
615		}
616		else {
617			$old_option = get_option( 'wpseo_indexation' );
618		}
619		if ( is_array( $old_option ) && $old_option !== array() ) {
620			$move = array(
621				'noindexauthor'     => 'noindex-author',
622				'disableauthor'     => 'disable-author',
623				'noindexdate'       => 'noindex-archive',
624				'noindexcat'        => 'noindex-category',
625				'noindextag'        => 'noindex-post_tag',
626				'noindexpostformat' => 'noindex-post_format',
627			);
628			foreach ( $move as $old => $new ) {
629				if ( isset( $old_option[ $old ] ) && ! isset( $option_value[ $new ] ) ) {
630					$option_value[ $new ] = $old_option[ $old ];
631				}
632			}
633			unset( $move, $old, $new );
634		}
635		unset( $old_option );
636
637
638		// Fix wrongness created by buggy version 1.2.2.
639		if ( isset( $option_value['title-home'] ) && $option_value['title-home'] === '%%sitename%% - %%sitedesc%% - 12345' ) {
640			$option_value['title-home-wpseo'] = '%%sitename%% - %%sitedesc%%';
641		}
642
643
644		/*
645		 * Renaming these options to avoid ever overwritting these if a (bloody stupid) user /
646		 * programmer would use any of the following as a custom post type or custom taxonomy:
647		 * 'home', 'author', 'archive', 'search', '404', 'subpages'.
648		 *
649		 * Similarly, renaming the tax options to avoid a custom post type and a taxonomy
650		 * with the same name occupying the same option.
651		 */
652		$rename = array(
653			'title-home'       => 'title-home-wpseo',
654			'title-author'     => 'title-author-wpseo',
655			'title-archive'    => 'title-archive-wpseo',
656			'title-search'     => 'title-search-wpseo',
657			'title-404'        => 'title-404-wpseo',
658			'metadesc-home'    => 'metadesc-home-wpseo',
659			'metadesc-author'  => 'metadesc-author-wpseo',
660			'metadesc-archive' => 'metadesc-archive-wpseo',
661			'noindex-author'   => 'noindex-author-wpseo',
662			'noindex-archive'  => 'noindex-archive-wpseo',
663		);
664		foreach ( $rename as $old => $new ) {
665			if ( isset( $option_value[ $old ] ) && ! isset( $option_value[ $new ] ) ) {
666				$option_value[ $new ] = $option_value[ $old ];
667				unset( $option_value[ $old ] );
668			}
669		}
670		unset( $rename, $old, $new );
671
672
673		/**
674		 * {@internal This clean-up action can only be done effectively once the taxonomies
675		 *            and post_types have been registered, i.e. at the end of the init action.}}
676		 */
677		if ( isset( $original ) && current_filter() === 'wpseo_double_clean_titles' || did_action( 'wpseo_double_clean_titles' ) > 0 ) {
678			$rename = array(
679				'title-'           => 'title-tax-',
680				'metadesc-'        => 'metadesc-tax-',
681				'noindex-'         => 'noindex-tax-',
682				'tax-hideeditbox-' => 'hideeditbox-tax-',
683
684			);
685
686			$taxonomy_names  = get_taxonomies( array( 'public' => true ), 'names' );
687			$post_type_names = get_post_types( array( 'public' => true ), 'names' );
688			$defaults        = $this->get_defaults();
689			if ( $taxonomy_names !== array() ) {
690				foreach ( $taxonomy_names as $tax ) {
691					foreach ( $rename as $old_prefix => $new_prefix ) {
692						if (
693							( isset( $original[ $old_prefix . $tax ] ) && ! isset( $original[ $new_prefix . $tax ] ) )
694							&& ( ! isset( $option_value[ $new_prefix . $tax ] )
695								|| ( isset( $option_value[ $new_prefix . $tax ] )
696									&& $option_value[ $new_prefix . $tax ] === $defaults[ $new_prefix . $tax ] ) )
697						) {
698							$option_value[ $new_prefix . $tax ] = $original[ $old_prefix . $tax ];
699
700							/*
701							 * Check if there is a cpt with the same name as the tax,
702							 * if so, we should make sure that the old setting hasn't been removed.
703							 */
704							if ( ! isset( $post_type_names[ $tax ] ) && isset( $option_value[ $old_prefix . $tax ] ) ) {
705								unset( $option_value[ $old_prefix . $tax ] );
706							}
707							else {
708								if ( isset( $post_type_names[ $tax ] ) && ! isset( $option_value[ $old_prefix . $tax ] ) ) {
709									$option_value[ $old_prefix . $tax ] = $original[ $old_prefix . $tax ];
710								}
711							}
712
713							if ( $old_prefix === 'tax-hideeditbox-' ) {
714								unset( $option_value[ $old_prefix . $tax ] );
715							}
716						}
717					}
718				}
719			}
720			unset( $rename, $taxonomy_names, $post_type_names, $defaults, $tax, $old_prefix, $new_prefix );
721		}
722
723
724		/*
725		 * Make sure the values of the variable option key options are cleaned as they
726		 * may be retained and would not be cleaned/validated then.
727		 */
728		if ( is_array( $option_value ) && $option_value !== array() ) {
729			foreach ( $option_value as $key => $value ) {
730				$switch_key = $this->get_switch_key( $key );
731
732				// Similar to validation routine - any changes made there should be made here too.
733				switch ( $switch_key ) {
734					/* text fields */
735					case 'title-':
736					case 'metadesc-':
737					case 'bctitle-ptarchive-':
738						$option_value[ $key ] = WPSEO_Utils::sanitize_text_field( $value );
739						break;
740
741					case 'separator':
742						if ( ! array_key_exists( $value, $this->get_separator_options() ) ) {
743							$option_value[ $key ] = false;
744						}
745						break;
746
747					/*
748					 * Boolean fields.
749					 */
750
751					/*
752					 * Covers:
753					 *  'noindex-'
754					 *  'showdate-'
755					 *  'hideeditbox-'
756					 */
757					default:
758						$option_value[ $key ] = WPSEO_Utils::validate_bool( $value );
759						break;
760				}
761			}
762			unset( $key, $value, $switch_key );
763		}
764
765		return $option_value;
766	}
767
768
769	/**
770	 * Make sure that any set option values relating to post_types and/or taxonomies are retained,
771	 * even when that post_type or taxonomy may not yet have been registered.
772	 *
773	 * {@internal Overrule the abstract class version of this to make sure one extra renamed
774	 *            variable key does not get removed. IMPORTANT: keep this method in line with
775	 *            the parent on which it is based!}}
776	 *
777	 * @param  array $dirty Original option as retrieved from the database.
778	 * @param  array $clean Filtered option where any options which shouldn't be in our option
779	 *                      have already been removed and any options which weren't set
780	 *                      have been set to their defaults.
781	 *
782	 * @return  array
783	 */
784	protected function retain_variable_keys( $dirty, $clean ) {
785		if ( ( is_array( $this->variable_array_key_patterns ) && $this->variable_array_key_patterns !== array() ) && ( is_array( $dirty ) && $dirty !== array() ) ) {
786
787			// Add the extra pattern.
788			$patterns   = $this->variable_array_key_patterns;
789			$patterns[] = 'tax-hideeditbox-';
790
791			/**
792			 * Allow altering the array with variable array key patterns.
793			 *
794			 * @api  array  $patterns  Array with the variable array key patterns.
795			 */
796			$patterns = apply_filters( 'wpseo_option_titles_variable_array_key_patterns', $patterns );
797
798			foreach ( $dirty as $key => $value ) {
799
800				// Do nothing if already in filtered option array.
801				if ( isset( $clean[ $key ] ) ) {
802					continue;
803				}
804
805				foreach ( $patterns as $pattern ) {
806					if ( strpos( $key, $pattern ) === 0 ) {
807						$clean[ $key ] = $value;
808						break;
809					}
810				}
811			}
812		}
813
814		return $clean;
815	}
816}