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