Archived
4060 lines
133 KiB
PHP
4060 lines
133 KiB
PHP
<?php
|
|
/**
|
|
* @package Internals
|
|
*/
|
|
|
|
// Avoid direct calls to this file
|
|
if ( ! defined( 'WPSEO_VERSION' ) ) {
|
|
header( 'Status: 403 Forbidden' );
|
|
header( 'HTTP/1.1 403 Forbidden' );
|
|
exit();
|
|
}
|
|
|
|
if ( ! class_exists( 'WPSEO_Option' ) ) {
|
|
/**
|
|
* @package WordPress\Plugins\WPSeo
|
|
* @subpackage Internals
|
|
* @since 1.5.0
|
|
* @version 1.5.0
|
|
*
|
|
* This abstract class and it's concrete classes implement defaults and value validation for
|
|
* all WPSEO options and subkeys within options.
|
|
*
|
|
* Some guidelines:
|
|
* [Retrieving options]
|
|
* - Use the normal get_option() to retrieve an option. You will receive a complete array for the option.
|
|
* Any subkeys which were not set, will have their default values in place.
|
|
* - In other words, you will normally not have to check whether a subkey isset() as they will *always* be set.
|
|
* They will also *always* be of the correct variable type.
|
|
* The only exception to this are the options with variable option names based on post_type or taxonomy
|
|
* as those will not always be available before the taxonomy/post_type is registered.
|
|
* (they will be available if a value was set, they won't be if it wasn't as the class won't know
|
|
* that a default needs to be injected).
|
|
* Oh and the very few options where the default value is null, i.e. wpseo->'theme_has_description'
|
|
*
|
|
* [Updating/Adding options]
|
|
* - For multisite site_options, please use the WPSEO_Options::update_site_option() method.
|
|
* - For normal options, use the normal add/update_option() functions. As long a the classes here
|
|
* are instantiated, validation for all options and their subkeys will be automatic.
|
|
* - On (succesfull) update of a couple of options, certain related actions will be run automatically.
|
|
* Some examples:
|
|
* - on change of wpseo[yoast_tracking], the cron schedule will be adjusted accordingly
|
|
* - on change of wpseo_permalinks and wpseo_xml, the rewrite rules will be flushed
|
|
* - on change of wpseo and wpseo_title, some caches will be cleared
|
|
*
|
|
*
|
|
* [Important information about add/updating/changing these classes]
|
|
* - Make sure that option array key names are unique across options. The WPSEO_Options::get_all()
|
|
* method merges most options together. If any of them have non-unique names, even if they
|
|
* are in a different option, they *will* overwrite each other.
|
|
* - When you add a new array key in an option: make sure you add proper defaults and add the key
|
|
* to the validation routine in the proper place or add a new validation case.
|
|
* You don't need to do any upgrading as any option returned will always be merged with the
|
|
* defaults, so new options will automatically be available.
|
|
* If the default value is a string which need translating, add this to the concrete class
|
|
* translate_defaults() method.
|
|
* - When you remove an array key from an option: if it's important that the option is really removed,
|
|
* add the WPSEO_Option::clean_up( $option_name ) method to the upgrade run.
|
|
* This will re-save the option and automatically remove the array key no longer in existance.
|
|
* - When you rename a sub-option: add it to the clean_option() routine and run that in the upgrade run.
|
|
* - When you change the default for an option sub-key, make sure you verify that the validation routine will
|
|
* still work the way it should.
|
|
* Example: changing a default from '' (empty string) to 'text' with a validation routine with tests
|
|
* for an empty string will prevent a user from saving an empty string as the real value. So the
|
|
* test for '' with the validation routine would have to be removed in that case.
|
|
* - If an option needs specific actions different from defined in this abstract class, you can just overrule
|
|
* a method by defining it in the concrete class.
|
|
*
|
|
*
|
|
* @todo - [JRF => testers] double check that validation will not cause errors when called
|
|
* from upgrade routine (some of the WP functions may not yet be available)
|
|
*/
|
|
abstract class WPSEO_Option {
|
|
|
|
/**
|
|
* @var string Option name - MUST be set in concrete class and set to public.
|
|
*/
|
|
protected $option_name;
|
|
|
|
/**
|
|
* @var string Option group name for use in settings forms
|
|
* - will be set automagically if not set in concrete class
|
|
* (i.e. if it confirm to the normal pattern 'yoast' . $option_name . 'options',
|
|
* only set in conrete class if it doesn't)
|
|
*/
|
|
public $group_name;
|
|
|
|
/**
|
|
* @var bool Whether to include the option in the return for WPSEO_Options::get_all().
|
|
* Also determines which options are copied over for ms_(re)set_blog().
|
|
*/
|
|
public $include_in_all = true;
|
|
|
|
/**
|
|
* @var bool Whether this option is only for when the install is multisite.
|
|
*/
|
|
public $multisite_only = false;
|
|
|
|
/**
|
|
* @var array Array of defaults for the option - MUST be set in concrete class.
|
|
* Shouldn't be requested directly, use $this->get_defaults();
|
|
*/
|
|
protected $defaults;
|
|
|
|
/**
|
|
* @var array Array of variable option name patterns for the option - if any -
|
|
* Set this when the option contains array keys which vary based on post_type
|
|
* or taxonomy
|
|
*/
|
|
protected $variable_array_key_patterns;
|
|
|
|
/**
|
|
* @var array Array of sub-options which should not be overloaded with multi-site defaults
|
|
*/
|
|
public $ms_exclude = array();
|
|
|
|
/**
|
|
* @var object Instance of this class
|
|
*/
|
|
protected static $instance;
|
|
|
|
/**
|
|
*
|
|
* @var bool Whether the filter extension is loaded
|
|
*/
|
|
public static $has_filters = true;
|
|
|
|
|
|
/* *********** INSTANTIATION METHODS *********** */
|
|
|
|
/**
|
|
* Add all the actions and filters for the option
|
|
*
|
|
* @return \WPSEO_Option
|
|
*/
|
|
protected function __construct() {
|
|
|
|
self::$has_filters = extension_loaded( 'filter' );
|
|
|
|
/* Add filters which get applied to the get_options() results */
|
|
$this->add_default_filters(); // return defaults if option not set
|
|
$this->add_option_filters(); // merge with defaults if option *is* set
|
|
|
|
|
|
if ( $this->multisite_only !== true ) {
|
|
/* The option validation routines remove the default filters to prevent failing
|
|
to insert an option if it's new. Let's add them back afterwards. */
|
|
add_action( 'add_option', array( $this, 'add_default_filters' ) ); // adding back after INSERT
|
|
|
|
if ( version_compare( $GLOBALS['wp_version'], '3.7', '!=' ) ) { // adding back after non-WP 3.7 UPDATE
|
|
add_action( 'update_option', array( $this, 'add_default_filters' ) );
|
|
} else { // adding back after WP 3.7 UPDATE
|
|
add_filter( 'pre_update_option_' . $this->option_name, array( $this, 'wp37_add_default_filters' ) );
|
|
}
|
|
}
|
|
else if ( is_multisite() ) {
|
|
/* The option validation routines remove the default filters to prevent failing
|
|
to insert an option if it's new. Let's add them back afterwards.
|
|
|
|
For site_options, this method is not foolproof as these actions are not fired
|
|
on an insert/update failure. Please use the WPSEO_Options::update_site_option() method
|
|
for updating site options to make sure the filters are in place. */
|
|
add_action( 'add_site_option_' . $this->option_name, array( $this, 'add_default_filters' ) );
|
|
add_action( 'update_site_option_' . $this->option_name, array( $this, 'add_default_filters' ) );
|
|
}
|
|
|
|
|
|
|
|
/* Make sure the option will always get validated, independently of register_setting()
|
|
(only available on back-end) */
|
|
add_filter( 'sanitize_option_' . $this->option_name, array( $this, 'validate' ) );
|
|
|
|
/* Register our option for the admin pages */
|
|
add_action( 'admin_init', array( $this, 'register_setting' ) );
|
|
|
|
|
|
/* Set option group name if not given */
|
|
if ( ! isset( $this->group_name ) || $this->group_name === '' ) {
|
|
$this->group_name = 'yoast_' . $this->option_name . '_options';
|
|
}
|
|
|
|
/* Translate some defaults as early as possible - textdomain is loaded in init on priority 1 */
|
|
if ( method_exists( $this, 'translate_defaults' ) ) {
|
|
add_action( 'init', array( $this, 'translate_defaults' ), 2 );
|
|
}
|
|
|
|
/**
|
|
* Enrich defaults once custom post types and taxonomies have been registered
|
|
* which is normally done on the init action.
|
|
*
|
|
* @todo - [JRF/testers] verify that none of the options which are only available after
|
|
* enrichment are used before the enriching
|
|
*/
|
|
if ( method_exists( $this, 'enrich_defaults' ) ) {
|
|
add_action( 'init', array( $this, 'enrich_defaults' ), 99 );
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
* All concrete classes *must* contain the get_instance method
|
|
* @internal Unfortunately I can't define it as an abstract as it also *has* to be static....
|
|
*/
|
|
//abstract protected static function get_instance();
|
|
|
|
|
|
/**
|
|
* Concrete classes *may* contain a translate_defaults method
|
|
*/
|
|
//abstract public function translate_defaults();
|
|
|
|
|
|
/**
|
|
* Concrete classes *may* contain a enrich_defaults method to add additional defaults once
|
|
* all post_types and taxonomies have been registered
|
|
*/
|
|
//abstract public function enrich_defaults();
|
|
|
|
|
|
/* *********** METHODS INFLUENCING get_option() *********** */
|
|
|
|
/**
|
|
* Add filters to make sure that the option default is returned if the option is not set
|
|
*
|
|
* @return void
|
|
*/
|
|
public function add_default_filters() {
|
|
// Don't change, needs to check for false as could return prio 0 which would evaluate to false
|
|
if ( has_filter( 'default_option_' . $this->option_name, array( $this, 'get_defaults' ) ) === false ) {
|
|
add_filter( 'default_option_' . $this->option_name, array( $this, 'get_defaults' ) );
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
* Abusing a filter to re-add our default filters
|
|
* WP 3.7 specific as update_option action hook was in the wrong place temporarily
|
|
* @see http://core.trac.wordpress.org/ticket/25705
|
|
*
|
|
* @param mixed $new_value
|
|
*
|
|
* @return mixed unchanged value
|
|
*/
|
|
public function wp37_add_default_filters( $new_value ) {
|
|
$this->add_default_filters();
|
|
|
|
return $new_value;
|
|
}
|
|
|
|
|
|
/**
|
|
* Remove the default filters.
|
|
* Called from the validate() method to prevent failure to add new options
|
|
*
|
|
* @return void
|
|
*/
|
|
public function remove_default_filters() {
|
|
remove_filter( 'default_option_' . $this->option_name, array( $this, 'get_defaults' ) );
|
|
}
|
|
|
|
|
|
/**
|
|
* Get the enriched default value for an option
|
|
*
|
|
* Checks if the concrete class contains an enrich_defaults() method and if so, runs it.
|
|
*
|
|
* @internal the enrich_defaults method is used to set defaults for variable array keys in an option,
|
|
* such as array keys depending on post_types and/or taxonomies
|
|
*
|
|
* @return array
|
|
*/
|
|
public function get_defaults() {
|
|
if ( method_exists( $this, 'translate_defaults' ) ) {
|
|
$this->translate_defaults();
|
|
}
|
|
|
|
if ( method_exists( $this, 'enrich_defaults' ) ) {
|
|
$this->enrich_defaults();
|
|
}
|
|
|
|
return apply_filters( 'wpseo_defaults', $this->defaults, $this->option_name );
|
|
}
|
|
|
|
|
|
/**
|
|
* Add filters to make sure that the option is merged with its defaults before being returned
|
|
*
|
|
* @return void
|
|
*/
|
|
public function add_option_filters() {
|
|
// Don't change, needs to check for false as could return prio 0 which would evaluate to false
|
|
if ( has_filter( 'option_' . $this->option_name, array( $this, 'get_option' ) ) === false ) {
|
|
add_filter( 'option_' . $this->option_name, array( $this, 'get_option' ) );
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
* Remove the option filters.
|
|
* Called from the clean_up methods to make sure we retrieve the original old option
|
|
*
|
|
* @return void
|
|
*/
|
|
public function remove_option_filters() {
|
|
remove_filter( 'option_' . $this->option_name, array( $this, 'get_option' ) );
|
|
}
|
|
|
|
|
|
/**
|
|
* Merge an option with its default values
|
|
*
|
|
* This method should *not* be called directly!!! It is only meant to filter the get_option() results
|
|
*
|
|
* @param mixed $options Option value
|
|
*
|
|
* @return mixed Option merged with the defaults for that option
|
|
*/
|
|
public function get_option( $options = null ) {
|
|
$filtered = $this->array_filter_merge( $options );
|
|
|
|
/* If the option contains variable option keys, make sure we don't remove those settings
|
|
- even if the defaults are not complete yet.
|
|
Unfortunately this means we also won't be removing the settings for post types or taxonomies
|
|
which are no longer in the WP install, but rather that than the other way around */
|
|
if ( isset( $this->variable_array_key_patterns ) ) {
|
|
$filtered = $this->retain_variable_keys( $options, $filtered );
|
|
}
|
|
|
|
return $filtered;
|
|
}
|
|
|
|
|
|
/* *********** METHODS influencing add_uption(), update_option() and saving from admin pages *********** */
|
|
|
|
/**
|
|
* Register (whitelist) the option for the configuration pages.
|
|
* The validation callback is already registered separately on the sanitize_option hook,
|
|
* so no need to double register.
|
|
*
|
|
* @return void
|
|
*/
|
|
public function register_setting() {
|
|
if ( WPSEO_Options::grant_access() ) {
|
|
register_setting( $this->group_name, $this->option_name );
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
* Validate the option
|
|
*
|
|
* @param mixed $option_value The unvalidated new value for the option
|
|
*
|
|
* @return array Validated new value for the option
|
|
*/
|
|
public function validate( $option_value ) {
|
|
$clean = $this->get_defaults();
|
|
|
|
/* Return the defaults if the new value is empty */
|
|
if ( ! is_array( $option_value ) || $option_value === array() ) {
|
|
return $clean;
|
|
}
|
|
|
|
|
|
$option_value = array_map( array( __CLASS__, 'trim_recursive' ), $option_value );
|
|
if ( $this->multisite_only !== true ) {
|
|
$old = get_option( $this->option_name );
|
|
}
|
|
else {
|
|
$old = get_site_option( $this->option_name );
|
|
}
|
|
$clean = $this->validate_option( $option_value, $clean, $old );
|
|
|
|
/* Retain the values for variable array keys even when the post type/taxonomy is not yet registered */
|
|
if ( isset( $this->variable_array_key_patterns ) ) {
|
|
$clean = $this->retain_variable_keys( $option_value, $clean );
|
|
}
|
|
|
|
$this->remove_default_filters();
|
|
|
|
return $clean;
|
|
}
|
|
|
|
|
|
/**
|
|
* All concrete classes must contain a validate_option() method which validates all
|
|
* values within the option
|
|
*/
|
|
abstract protected function validate_option( $dirty, $clean, $old );
|
|
|
|
|
|
/* *********** METHODS for ADDING/UPDATING/UPGRADING the option *********** */
|
|
|
|
/**
|
|
* Retrieve the real old value (unmerged with defaults)
|
|
*
|
|
* @return array|bool the original option value (which can be false if the option doesn't exist)
|
|
*/
|
|
protected function get_original_option() {
|
|
$this->remove_default_filters();
|
|
$this->remove_option_filters();
|
|
|
|
// Get (unvalidated) array, NOT merged with defaults
|
|
if ( $this->multisite_only !== true ) {
|
|
$option_value = get_option( $this->option_name );
|
|
}
|
|
else {
|
|
$option_value = get_site_option( $this->option_name );
|
|
}
|
|
|
|
$this->add_option_filters();
|
|
$this->add_default_filters();
|
|
|
|
return $option_value;
|
|
}
|
|
|
|
/**
|
|
* Add the option if it doesn't exist for some strange reason
|
|
*
|
|
* @uses WPSEO_Option::get_original_option()
|
|
*
|
|
* @return void
|
|
*/
|
|
public function maybe_add_option() {
|
|
if ( $this->get_original_option() === false ) {
|
|
if ( $this->multisite_only !== true ) {
|
|
update_option( $this->option_name, $this->get_defaults() );
|
|
}
|
|
else {
|
|
$this->update_site_option( $this->get_defaults() );
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
* Update a site_option
|
|
*
|
|
* @internal This special method is only needed for multisite options, but very needed indeed there.
|
|
* The order in which certain functions and hooks are run is different between get_option() and
|
|
* get_site_option() which means in practice that the removing of the default filters would be
|
|
* done too late and the re-adding of the default filters might not be done at all.
|
|
* Aka: use the WPSEO_Options::update_site_option() method (which calls this method) for
|
|
* safely adding/updating multisite options.
|
|
*
|
|
* @return bool whether the update was succesfull
|
|
*/
|
|
public function update_site_option( $value ) {
|
|
if ( $this->multisite_only === true && is_multisite() ) {
|
|
$this->remove_default_filters();
|
|
$result = update_site_option( $this->option_name, $value );
|
|
$this->add_default_filters();
|
|
return $result;
|
|
}
|
|
else {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
* Retrieve the real old value (unmerged with defaults), clean and re-save the option
|
|
*
|
|
* @uses WPSEO_Option::get_original_option()
|
|
* @uses WPSEO_Option::import()
|
|
*
|
|
* @param string $current_version (optional) Version from which to upgrade, if not set,
|
|
* version specific upgrades will be disregarded
|
|
*
|
|
* @return void
|
|
*/
|
|
public function clean( $current_version = null ) {
|
|
$option_value = $this->get_original_option();
|
|
$this->import( $option_value, $current_version );
|
|
}
|
|
|
|
|
|
/**
|
|
* Clean and re-save the option
|
|
* @uses clean_option() method from concrete class if it exists
|
|
*
|
|
* @todo [JRF/whomever] Figure out a way to show settings error during/after the upgrade - maybe
|
|
* something along the lines of:
|
|
* -> add them to a property in this class
|
|
* -> if that property isset at the end of the routine and add_settings_error function does not exist,
|
|
* save as transient (or update the transient if one already exists)
|
|
* -> next time an admin is in the WP back-end, show the errors and delete the transient or only delete it
|
|
* once the admin has dismissed the message (add ajax function)
|
|
* Important: all validation routines which add_settings_errors would need to be changed for this to work
|
|
*
|
|
* @param array $option_value Option value to be imported
|
|
* @param string $current_version (optional) Version from which to upgrade, if not set,
|
|
* version specific upgrades will be disregarded
|
|
* @param array $all_old_option_values (optional) Only used when importing old options to have
|
|
* access to the real old values, in contrast to the saved ones
|
|
*
|
|
* @return void
|
|
*/
|
|
public function import( $option_value, $current_version = null, $all_old_option_values = null ) {
|
|
if ( $option_value === false ) {
|
|
$option_value = $this->get_defaults();
|
|
} elseif ( is_array( $option_value ) && method_exists( $this, 'clean_option' ) ) {
|
|
$option_value = $this->clean_option( $option_value, $current_version, $all_old_option_values );
|
|
}
|
|
|
|
/* Save the cleaned value - validation will take care of cleaning out array keys which
|
|
should no longer be there */
|
|
if ( $this->multisite_only !== true ) {
|
|
update_option( $this->option_name, $option_value );
|
|
}
|
|
else {
|
|
$this->update_site_option( $this->option_name, $option_value );
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
* Concrete classes *may* contain a clean_option method which will clean out old/renamed
|
|
* values within the option
|
|
*/
|
|
//abstract public function clean_option( $option_value, $current_version = null, $all_old_option_values = null );
|
|
|
|
|
|
/* *********** HELPER METHODS for internal use *********** */
|
|
|
|
/**
|
|
* Helper method - Combines a fixed array of default values with an options array
|
|
* while filtering out any keys which are not in the defaults array.
|
|
*
|
|
* @todo [JRF] - shouldn't this be a straight array merge ? at the end of the day, the validation
|
|
* removes any invalid keys on save
|
|
*
|
|
* @param array $options (Optional) Current options
|
|
* - if not set, the option defaults for the $option_key will be returned.
|
|
*
|
|
* @return array Combined and filtered options array.
|
|
*/
|
|
protected function array_filter_merge( $options = null ) {
|
|
|
|
$defaults = $this->get_defaults();
|
|
|
|
if ( ! isset( $options ) || $options === false || $options === array() ) {
|
|
return $defaults;
|
|
}
|
|
|
|
$options = (array) $options;
|
|
/*$filtered = array();
|
|
|
|
if ( $defaults !== array() ) {
|
|
foreach ( $defaults as $key => $default_value ) {
|
|
// @todo should this walk through array subkeys ?
|
|
$filtered[ $key ] = ( isset( $options[ $key ] ) ? $options[ $key ] : $default_value );
|
|
}
|
|
}*/
|
|
$filtered = array_merge( $defaults, $options );
|
|
|
|
return $filtered;
|
|
}
|
|
|
|
|
|
/**
|
|
* Make sure that any set option values relating to post_types and/or taxonomies are retained,
|
|
* even when that post_type or taxonomy may not yet have been registered.
|
|
*
|
|
* @internal The wpseo_titles concrete class overrules this method. Make sure that any changes
|
|
* applied here, also get ported to that version.
|
|
*
|
|
* @param array $dirty Original option as retrieved from the database
|
|
* @param array $clean Filtered option where any options which shouldn't be in our option
|
|
* have already been removed and any options which weren't set
|
|
* have been set to their defaults
|
|
*
|
|
* @return array
|
|
*/
|
|
protected function retain_variable_keys( $dirty, $clean ) {
|
|
if ( ( is_array( $this->variable_array_key_patterns ) && $this->variable_array_key_patterns !== array() ) && ( is_array( $dirty ) && $dirty !== array() ) ) {
|
|
foreach ( $dirty as $key => $value ) {
|
|
|
|
// do nothing if already in filtered options
|
|
if ( isset( $clean[ $key ] ) ) {
|
|
continue;
|
|
}
|
|
|
|
foreach ( $this->variable_array_key_patterns as $pattern ) {
|
|
|
|
if ( strpos( $key, $pattern ) === 0 ) {
|
|
$clean[ $key ] = $value;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return $clean;
|
|
}
|
|
|
|
|
|
/**
|
|
* Check whether a given array key conforms to one of the variable array key patterns for this option
|
|
*
|
|
* @usedby validate_option() methods for options with variable array keys
|
|
*
|
|
* @param string $key Array key to check
|
|
*
|
|
* @return string Pattern if it conforms, original array key if it doesn't or if the option
|
|
* does not have variable array keys
|
|
*/
|
|
protected function get_switch_key( $key ) {
|
|
if ( ! isset( $this->variable_array_key_patterns ) || ( ! is_array( $this->variable_array_key_patterns ) || $this->variable_array_key_patterns === array() ) ) {
|
|
return $key;
|
|
}
|
|
|
|
foreach ( $this->variable_array_key_patterns as $pattern ) {
|
|
if ( strpos( $key, $pattern ) === 0 ) {
|
|
return $pattern;
|
|
}
|
|
}
|
|
|
|
return $key;
|
|
}
|
|
|
|
|
|
/* *********** GENERIC HELPER METHODS *********** */
|
|
|
|
/**
|
|
* Emulate the WP native sanitize_text_field function in a %%variable%% safe way
|
|
* @see https://core.trac.wordpress.org/browser/trunk/src/wp-includes/formatting.php for the original
|
|
*
|
|
* Sanitize a string from user input or from the db
|
|
*
|
|
* check for invalid UTF-8,
|
|
* Convert single < characters to entity,
|
|
* strip all tags,
|
|
* remove line breaks, tabs and extra white space,
|
|
* strip octets - BUT DO NOT REMOVE (part of) VARIABLES WHICH WILL BE REPLACED.
|
|
*
|
|
* @param string $value
|
|
*
|
|
* @return string
|
|
*/
|
|
public static function sanitize_text_field( $value ) {
|
|
$filtered = wp_check_invalid_utf8( $value );
|
|
|
|
if ( strpos( $filtered, '<' ) !== false ) {
|
|
$filtered = wp_pre_kses_less_than( $filtered );
|
|
// This will strip extra whitespace for us.
|
|
$filtered = wp_strip_all_tags( $filtered, true );
|
|
} else {
|
|
$filtered = trim( preg_replace( '`[\r\n\t ]+`', ' ', $filtered ) );
|
|
}
|
|
|
|
$found = false;
|
|
while ( preg_match( '`[^%](%[a-f0-9]{2})`i', $filtered, $match ) ) {
|
|
$filtered = str_replace( $match[1], '', $filtered );
|
|
$found = true;
|
|
}
|
|
|
|
if ( $found ) {
|
|
// Strip out the whitespace that may now exist after removing the octets.
|
|
$filtered = trim( preg_replace( '` +`', ' ', $filtered ) );
|
|
}
|
|
|
|
/**
|
|
* Filter a sanitized text field string.
|
|
*
|
|
* @since WP 2.9.0
|
|
*
|
|
* @param string $filtered The sanitized string.
|
|
* @param string $str The string prior to being sanitized.
|
|
*/
|
|
return apply_filters( 'sanitize_text_field', $filtered, $value );
|
|
}
|
|
|
|
|
|
/**
|
|
* Sanitize a url for saving to the database
|
|
* Not to be confused with the old native WP function
|
|
*
|
|
* @todo [JRF => whomever] check/improve url verification
|
|
*
|
|
* @todo [JRF => whomever] when someone would reorganize the classes, this should maybe
|
|
* be moved to a general WPSEO_Utils class. Obviously all calls to this method should be
|
|
* adjusted in that case.
|
|
*
|
|
* @param string $value
|
|
* @param array $allowed_protocols
|
|
*
|
|
* @return string
|
|
*/
|
|
public static function sanitize_url( $value, $allowed_protocols = array( 'http', 'https' ) ) {
|
|
return esc_url_raw( sanitize_text_field( rawurldecode( $value ) ), $allowed_protocols );
|
|
}
|
|
|
|
/**
|
|
* Validate a value as boolean
|
|
*
|
|
* @todo [JRF => whomever] when someone would reorganize the classes, this (and the emulate method
|
|
* below) should maybe be moved to a general WPSEO_Utils class. Obviously all calls to this method
|
|
* should be adjusted in that case.
|
|
*
|
|
* @static
|
|
*
|
|
* @param mixed $value
|
|
*
|
|
* @return bool
|
|
*/
|
|
public static function validate_bool( $value ) {
|
|
if ( self::$has_filters ) {
|
|
return filter_var( $value, FILTER_VALIDATE_BOOLEAN );
|
|
} else {
|
|
return self::emulate_filter_bool( $value );
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Cast a value to bool
|
|
*
|
|
* @static
|
|
*
|
|
* @param mixed $value Value to cast
|
|
*
|
|
* @return bool
|
|
*/
|
|
public static function emulate_filter_bool( $value ) {
|
|
$true = array(
|
|
'1',
|
|
'true',
|
|
'True',
|
|
'TRUE',
|
|
'y',
|
|
'Y',
|
|
'yes',
|
|
'Yes',
|
|
'YES',
|
|
'on',
|
|
'On',
|
|
'On',
|
|
|
|
);
|
|
$false = array(
|
|
'0',
|
|
'false',
|
|
'False',
|
|
'FALSE',
|
|
'n',
|
|
'N',
|
|
'no',
|
|
'No',
|
|
'NO',
|
|
'off',
|
|
'Off',
|
|
'OFF',
|
|
);
|
|
|
|
if ( is_bool( $value ) ) {
|
|
return $value;
|
|
} else if ( is_int( $value ) && ( $value === 0 || $value === 1 ) ) {
|
|
return (bool) $value;
|
|
} else if ( ( is_float( $value ) && ! is_nan( $value ) ) && ( $value === (float) 0 || $value === (float) 1 ) ) {
|
|
return (bool) $value;
|
|
} else if ( is_string( $value ) ) {
|
|
$value = trim( $value );
|
|
if ( in_array( $value, $true, true ) ) {
|
|
return true;
|
|
} else if ( in_array( $value, $false, true ) ) {
|
|
return false;
|
|
} else {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
|
|
/**
|
|
* Validate a value as integer
|
|
*
|
|
* @todo [JRF => whomever] when someone would reorganize the classes, this (and the emulate method
|
|
* below) should maybe be moved to a general WPSEO_Utils class. Obviously all calls to this method
|
|
* should be adjusted in that case.
|
|
*
|
|
* @static
|
|
*
|
|
* @param mixed $value
|
|
*
|
|
* @return mixed int or false in case of failure to convert to int
|
|
*/
|
|
public static function validate_int( $value ) {
|
|
if ( self::$has_filters ) {
|
|
return filter_var( $value, FILTER_VALIDATE_INT );
|
|
} else {
|
|
return self::emulate_filter_int( $value );
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Cast a value to integer
|
|
*
|
|
* @static
|
|
*
|
|
* @param mixed $value Value to cast
|
|
*
|
|
* @return int|bool
|
|
*/
|
|
public static function emulate_filter_int( $value ) {
|
|
if ( is_int( $value ) ) {
|
|
return $value;
|
|
} else if ( is_float( $value ) ) {
|
|
if ( (int) $value == $value && ! is_nan( $value ) ) {
|
|
return (int) $value;
|
|
} else {
|
|
return false;
|
|
}
|
|
} else if ( is_string( $value ) ) {
|
|
$value = trim( $value );
|
|
if ( $value === '' ) {
|
|
return false;
|
|
} else if ( ctype_digit( $value ) ) {
|
|
return (int) $value;
|
|
} else if ( strpos( $value, '-' ) === 0 && ctype_digit( substr( $value, 1 ) ) ) {
|
|
return (int) $value;
|
|
} else {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
|
|
/**
|
|
* Recursively trim whitespace round a string value or of string values within an array
|
|
* Only trims strings to avoid typecasting a variable (to string)
|
|
*
|
|
* @todo [JRF => whomever] when someone would reorganize the classes, this should maybe
|
|
* be moved to a general WPSEO_Utils class. Obviously all calls to this method should be
|
|
* adjusted in that case.
|
|
*
|
|
* @static
|
|
*
|
|
* @param mixed $value Value to trim or array of values to trim
|
|
*
|
|
* @return mixed Trimmed value or array of trimmed values
|
|
*/
|
|
public static function trim_recursive( $value ) {
|
|
if ( is_string( $value ) ) {
|
|
$value = trim( $value );
|
|
} elseif ( is_array( $value ) ) {
|
|
$value = array_map( array( __CLASS__, 'trim_recursive' ), $value );
|
|
}
|
|
|
|
return $value;
|
|
}
|
|
|
|
} /* End of class WPSEO_Option */
|
|
|
|
} /* End of class-exists wrapper */
|
|
|
|
|
|
/*******************************************************************
|
|
* Option: wpseo
|
|
*******************************************************************/
|
|
if ( ! class_exists( 'WPSEO_Option_Wpseo' ) ) {
|
|
|
|
class WPSEO_Option_Wpseo extends WPSEO_Option {
|
|
|
|
/**
|
|
* @var string option name
|
|
*/
|
|
public $option_name = 'wpseo';
|
|
|
|
/**
|
|
* @var array Array of defaults for the option
|
|
* Shouldn't be requested directly, use $this->get_defaults();
|
|
*/
|
|
protected $defaults = array(
|
|
// Non-form fields, set via (ajax) function
|
|
'blocking_files' => array(),
|
|
'ignore_blog_public_warning' => false,
|
|
'ignore_meta_description_warning' => null, // overwrite in __construct()
|
|
'ignore_page_comments' => false,
|
|
'ignore_permalink' => false,
|
|
'ignore_tour' => false,
|
|
'ms_defaults_set' => false,
|
|
'theme_description_found' => null, // overwrite in __construct()
|
|
'theme_has_description' => null, // overwrite in __construct()
|
|
'tracking_popup_done' => false,
|
|
// Non-form field, should only be set via validation routine
|
|
'version' => '', // leave default as empty to ensure activation/upgrade works
|
|
|
|
// Form fields:
|
|
'alexaverify' => '', // text field
|
|
'disableadvanced_meta' => true,
|
|
'googleverify' => '', // text field
|
|
'msverify' => '', // text field
|
|
'pinterestverify' => '',
|
|
'yandexverify' => '',
|
|
'yoast_tracking' => false,
|
|
);
|
|
|
|
public static $desc_defaults = array(
|
|
'ignore_meta_description_warning' => false,
|
|
'theme_description_found' => '', // text string description
|
|
'theme_has_description' => null,
|
|
);
|
|
|
|
/**
|
|
* @var array Array of sub-options which should not be overloaded with multi-site defaults
|
|
*/
|
|
public $ms_exclude = array(
|
|
'ignore_blog_public_warning',
|
|
'ignore_meta_description_warning',
|
|
'ignore_page_comments',
|
|
'ignore_permalink',
|
|
'ignore_tour',
|
|
|
|
/* theme dependent */
|
|
'theme_description_found',
|
|
'theme_has_description',
|
|
|
|
/* privacy */
|
|
'alexaverify',
|
|
'googleverify',
|
|
'msverify',
|
|
'pinterestverify',
|
|
'yandexverify',
|
|
);
|
|
|
|
|
|
/**
|
|
* Add the actions and filters for the option
|
|
*
|
|
* @todo [JRF => testers] Check if the extra actions below would run into problems if an option
|
|
* is updated early on and if so, change the call to schedule these for a later action on add/update
|
|
* instead of running them straight away
|
|
*
|
|
* @return \WPSEO_Option_Wpseo
|
|
*/
|
|
protected function __construct() {
|
|
/* Dirty fix for making certain defaults available during activation while still only
|
|
defining them once */
|
|
foreach ( self::$desc_defaults as $key => $value ) {
|
|
$this->defaults[ $key ] = $value;
|
|
}
|
|
|
|
parent::__construct();
|
|
|
|
/* Clear the cache on update/add */
|
|
add_action( 'add_option_' . $this->option_name, array( 'WPSEO_Options', 'clear_cache' ) );
|
|
add_action( 'update_option_' . $this->option_name, array( 'WPSEO_Options', 'clear_cache' ) );
|
|
|
|
|
|
/* Check if the yoast tracking cron job needs adding/removing on successfull option add/update */
|
|
add_action( 'add_option_' . $this->option_name, array(
|
|
'WPSEO_Options',
|
|
'schedule_yoast_tracking',
|
|
), 15, 2 );
|
|
add_action( 'update_option_' . $this->option_name, array(
|
|
'WPSEO_Options',
|
|
'schedule_yoast_tracking',
|
|
), 15, 2 );
|
|
}
|
|
|
|
|
|
/**
|
|
* Get the singleton instance of this class
|
|
*
|
|
* @return object
|
|
*/
|
|
public static function get_instance() {
|
|
if ( ! ( self::$instance instanceof self ) ) {
|
|
self::$instance = new self();
|
|
}
|
|
|
|
return self::$instance;
|
|
}
|
|
|
|
|
|
/**
|
|
* Validate the option
|
|
*
|
|
* @param array $dirty New value for the option
|
|
* @param array $clean Clean value for the option, normally the defaults
|
|
* @param array $old Old value of the option
|
|
*
|
|
* @return array Validated clean value for the option to be saved to the database
|
|
*/
|
|
protected function validate_option( $dirty, $clean, $old ) {
|
|
|
|
foreach ( $clean as $key => $value ) {
|
|
switch ( $key ) {
|
|
case 'version':
|
|
$clean[ $key ] = WPSEO_VERSION;
|
|
break;
|
|
|
|
|
|
case 'blocking_files':
|
|
/* @internal [JRF] to really validate this we should also do a file_exists()
|
|
* on each array entry and remove files which no longer exist, but that might be overkill */
|
|
if ( isset( $dirty[ $key ] ) && is_array( $dirty[ $key ] ) ) {
|
|
$clean[ $key ] = array_unique( $dirty[ $key ] );
|
|
} elseif ( isset( $old[ $key ] ) && is_array( $old[ $key ] ) ) {
|
|
$clean[ $key ] = array_unique( $old[ $key ] );
|
|
}
|
|
break;
|
|
|
|
|
|
case 'theme_description_found':
|
|
if ( isset( $dirty[ $key ] ) && is_string( $dirty[ $key ] ) ) {
|
|
$clean[ $key ] = $dirty[ $key ]; // @todo [JRF/whomever] maybe do wp_kses ?
|
|
} elseif ( isset( $old[ $key ] ) && is_string( $old[ $key ] ) ) {
|
|
$clean[ $key ] = $old[ $key ];
|
|
}
|
|
break;
|
|
|
|
|
|
/* text fields */
|
|
case 'alexaverify':
|
|
case 'googleverify':
|
|
case 'msverify':
|
|
case 'pinterestverify':
|
|
case 'yandexverify':
|
|
if ( isset( $dirty[ $key ] ) && $dirty[ $key ] !== '' ) {
|
|
$meta = $dirty[ $key ];
|
|
if ( strpos( $meta, 'content=' ) ) {
|
|
// Make sure we only have the real key, not a complete meta tag
|
|
preg_match( '`content=([\'"])?([^\'"> ]+)(?:\1|[ />])`', $meta, $match );
|
|
if ( isset( $match[2] ) ) {
|
|
$meta = $match[2];
|
|
}
|
|
unset( $match );
|
|
}
|
|
|
|
$meta = sanitize_text_field( $meta );
|
|
if ( $meta !== '' ) {
|
|
$regex = '`^[A-Fa-f0-9_-]+$`';
|
|
$service = '';
|
|
|
|
switch ( $key ) {
|
|
case 'googleverify':
|
|
$regex = '`^[A-Za-z0-9_-]+$`';
|
|
$service = 'Google Webmaster tools';
|
|
break;
|
|
|
|
case 'msverify':
|
|
$service = 'Bing Webmaster tools';
|
|
break;
|
|
|
|
case 'pinterestverify':
|
|
$service = 'Pinterest';
|
|
break;
|
|
|
|
case 'yandexverify':
|
|
$service = 'Yandex Webmaster tools';
|
|
break;
|
|
|
|
case 'alexaverify':
|
|
$regex = '`^[A-Za-z0-9_-]{20,}$`';
|
|
$service = 'Alexa ID';
|
|
}
|
|
|
|
if ( preg_match( $regex, $meta ) ) {
|
|
$clean[ $key ] = $meta;
|
|
} else {
|
|
if ( isset( $old[ $key ] ) && preg_match( $regex, $old[ $key ] ) ) {
|
|
$clean[ $key ] = $old[ $key ];
|
|
}
|
|
if ( function_exists( 'add_settings_error' ) ) {
|
|
add_settings_error(
|
|
$this->group_name, // slug title of the setting
|
|
'_' . $key, // suffix-id for the error message box
|
|
sprintf( __( '%s does not seem to be a valid %s verification string. Please correct.', 'wordpress-seo' ), '<strong>' . esc_html( $meta ) . '</strong>', $service ), // the error message
|
|
'error' // error type, either 'error' or 'updated'
|
|
);
|
|
}
|
|
}
|
|
}
|
|
unset( $meta, $regex, $service );
|
|
}
|
|
break;
|
|
|
|
|
|
/* boolean|null fields - if set a check was done, if null, it hasn't */
|
|
case 'theme_has_description':
|
|
if ( isset( $dirty[ $key ] ) ) {
|
|
$clean[ $key ] = self::validate_bool( $dirty[ $key ] );
|
|
} elseif ( isset( $old[ $key ] ) ) {
|
|
$clean[ $key ] = self::validate_bool( $old[ $key ] );
|
|
}
|
|
break;
|
|
|
|
|
|
/* boolean dismiss warnings - not fields - may not be in form
|
|
(and don't need to be either as long as the default is false) */
|
|
case 'ignore_blog_public_warning':
|
|
case 'ignore_meta_description_warning':
|
|
case 'ignore_page_comments':
|
|
case 'ignore_permalink':
|
|
case 'ignore_tour':
|
|
case 'ms_defaults_set':
|
|
case 'tracking_popup_done':
|
|
if ( isset( $dirty[ $key ] ) ) {
|
|
$clean[ $key ] = self::validate_bool( $dirty[ $key ] );
|
|
} elseif ( isset( $old[ $key ] ) ) {
|
|
$clean[ $key ] = self::validate_bool( $old[ $key ] );
|
|
}
|
|
break;
|
|
|
|
|
|
/* boolean (checkbox) fields */
|
|
case 'disableadvanced_meta':
|
|
case 'yoast_tracking':
|
|
default:
|
|
$clean[ $key ] = ( isset( $dirty[ $key ] ) ? self::validate_bool( $dirty[ $key ] ) : false );
|
|
break;
|
|
}
|
|
}
|
|
|
|
return $clean;
|
|
}
|
|
|
|
|
|
/**
|
|
* Clean a given option value
|
|
*
|
|
* @param array $option_value Old (not merged with defaults or filtered) option value to
|
|
* clean according to the rules for this option
|
|
* @param string $current_version (optional) Version from which to upgrade, if not set,
|
|
* version specific upgrades will be disregarded
|
|
* @param array $all_old_option_values (optional) Only used when importing old options to have
|
|
* access to the real old values, in contrast to the saved ones
|
|
*
|
|
* @return array Cleaned option
|
|
*/
|
|
protected function clean_option( $option_value, $current_version = null, $all_old_option_values = null ) {
|
|
|
|
// Rename some options *and* change their value
|
|
$rename = array(
|
|
'presstrends' => array(
|
|
'new_name' => 'yoast_tracking',
|
|
'new_value' => true,
|
|
),
|
|
'presstrends_popup' => array(
|
|
'new_name' => 'tracking_popup_done',
|
|
'new_value' => true,
|
|
),
|
|
);
|
|
foreach ( $rename as $old => $new ) {
|
|
if ( isset( $option_value[ $old ] ) && ! isset( $option_value[ $new['new_name'] ] ) ) {
|
|
$option_value[ $new['new_name'] ] = $new['new_value'];
|
|
unset( $option_value[ $old ] );
|
|
}
|
|
}
|
|
unset( $rename, $old, $new );
|
|
|
|
|
|
// Deal with renaming of some options without losing the settings
|
|
$rename = array(
|
|
'tracking_popup' => 'tracking_popup_done',
|
|
'meta_description_warning' => 'ignore_meta_description_warning',
|
|
);
|
|
foreach ( $rename as $old => $new ) {
|
|
if ( isset( $option_value[ $old ] ) && ! isset( $option_value[ $new ] ) ) {
|
|
$option_value[ $new ] = $option_value[ $old ];
|
|
unset( $option_value[ $old ] );
|
|
}
|
|
}
|
|
unset( $rename, $old, $new );
|
|
|
|
|
|
// Change a array sub-option to two straight sub-options
|
|
if ( isset( $option_value['theme_check']['description'] ) && ! isset( $option_value['theme_has_description'] ) ) {
|
|
// @internal the negate is by design!
|
|
$option_value['theme_has_description'] = ! $option_value['theme_check']['description'];
|
|
}
|
|
if ( isset( $option_values['theme_check']['description_found'] ) && ! isset( $option_value['theme_description_found'] ) ) {
|
|
$option_value['theme_description_found'] = $option_value['theme_check']['description_found'];
|
|
}
|
|
|
|
|
|
// Deal with value change from text string to boolean
|
|
$value_change = array(
|
|
'ignore_blog_public_warning',
|
|
'ignore_meta_description_warning',
|
|
'ignore_page_comments',
|
|
'ignore_permalink',
|
|
'ignore_tour',
|
|
//'disableadvanced_meta', => not needed as is 'on' which will auto-convert to true
|
|
'tracking_popup_done',
|
|
);
|
|
foreach ( $value_change as $key ) {
|
|
if ( isset( $option_value[ $key ] ) && in_array( $option_value[ $key ], array(
|
|
'ignore',
|
|
'done',
|
|
), true )
|
|
) {
|
|
$option_value[ $key ] = true;
|
|
}
|
|
}
|
|
|
|
return $option_value;
|
|
}
|
|
|
|
} /* End of class WPSEO_Option_Wpseo */
|
|
|
|
} /* End of class-exists wrapper */
|
|
|
|
|
|
/*******************************************************************
|
|
* Option: wpseo_permalinks
|
|
*******************************************************************/
|
|
if ( ! class_exists( 'WPSEO_Option_Permalinks' ) ) {
|
|
|
|
/**
|
|
* @internal Clean routine for 1.5 not needed as values used to be saved as string 'on' and those will convert
|
|
* automatically
|
|
*/
|
|
class WPSEO_Option_Permalinks extends WPSEO_Option {
|
|
|
|
/**
|
|
* @var string option name
|
|
*/
|
|
public $option_name = 'wpseo_permalinks';
|
|
|
|
/**
|
|
* @var array Array of defaults for the option
|
|
* Shouldn't be requested directly, use $this->get_defaults();
|
|
*/
|
|
protected $defaults = array(
|
|
'cleanpermalinks' => false,
|
|
'cleanpermalink-extravars' => '', // text field
|
|
'cleanpermalink-googlecampaign' => false,
|
|
'cleanpermalink-googlesitesearch' => false,
|
|
'cleanreplytocom' => false,
|
|
'cleanslugs' => true,
|
|
'force_transport' => 'default',
|
|
'redirectattachment' => false,
|
|
'stripcategorybase' => false,
|
|
'trailingslash' => false,
|
|
);
|
|
|
|
|
|
/**
|
|
* @static
|
|
* @var array $force_transport_options Available options for the force_transport setting
|
|
* Used for input validation
|
|
*
|
|
* @internal Important: Make sure the options added to the array here are in line with the keys
|
|
* for the options set for the select box in the admin/pages/permalinks.php file
|
|
*/
|
|
public static $force_transport_options = array(
|
|
'default', // = leave as-is
|
|
'http',
|
|
'https',
|
|
);
|
|
|
|
|
|
/**
|
|
* Add the actions and filters for the option
|
|
*
|
|
* @todo [JRF => testers] Check if the extra actions below would run into problems if an option
|
|
* is updated early on and if so, change the call to schedule these for a later action on add/update
|
|
* instead of running them straight away
|
|
*
|
|
* @return \WPSEO_Option_Permalinks
|
|
*/
|
|
protected function __construct() {
|
|
parent::__construct();
|
|
add_action( 'update_option_' . $this->option_name, array( 'WPSEO_Options', 'clear_rewrites' ) );
|
|
}
|
|
|
|
|
|
/**
|
|
* Get the singleton instance of this class
|
|
*
|
|
* @return object
|
|
*/
|
|
public static function get_instance() {
|
|
if ( ! ( self::$instance instanceof self ) ) {
|
|
self::$instance = new self();
|
|
}
|
|
|
|
return self::$instance;
|
|
}
|
|
|
|
|
|
/**
|
|
* Validate the option
|
|
*
|
|
* @param array $dirty New value for the option
|
|
* @param array $clean Clean value for the option, normally the defaults
|
|
* @param array $old Old value of the option (not used here as all fields will always be in the form)
|
|
*
|
|
* @return array Validated clean value for the option to be saved to the database
|
|
*/
|
|
protected function validate_option( $dirty, $clean, $old ) {
|
|
|
|
foreach ( $clean as $key => $value ) {
|
|
switch ( $key ) {
|
|
case 'force_transport':
|
|
if ( isset( $dirty[ $key ] ) && in_array( $dirty[ $key ], self::$force_transport_options, true ) ) {
|
|
$clean[ $key ] = $dirty[ $key ];
|
|
} else {
|
|
if ( isset( $old[ $key ] ) && in_array( $old[ $key ], self::$force_transport_options, true ) ) {
|
|
$clean[ $key ] = $old[ $key ];
|
|
}
|
|
if ( function_exists( 'add_settings_error' ) ) {
|
|
add_settings_error(
|
|
$this->group_name, // slug title of the setting
|
|
'_' . $key, // suffix-id for the error message box
|
|
__( 'Invalid transport mode set for the canonical settings. Value reset to default.', 'wordpress-seo' ), // the error message
|
|
'error' // error type, either 'error' or 'updated'
|
|
);
|
|
}
|
|
}
|
|
break;
|
|
|
|
/* text fields */
|
|
case 'cleanpermalink-extravars':
|
|
if ( isset( $dirty[ $key ] ) && $dirty[ $key ] !== '' ) {
|
|
$clean[ $key ] = sanitize_text_field( $dirty[ $key ] );
|
|
}
|
|
break;
|
|
|
|
/* boolean (checkbox) fields */
|
|
case 'cleanpermalinks':
|
|
case 'cleanpermalink-googlesitesearch':
|
|
case 'cleanpermalink-googlecampaign':
|
|
case 'cleanreplytocom':
|
|
case 'cleanslugs':
|
|
case 'redirectattachment':
|
|
case 'stripcategorybase':
|
|
case 'trailingslash':
|
|
default:
|
|
$clean[ $key ] = ( isset( $dirty[ $key ] ) ? self::validate_bool( $dirty[ $key ] ) : false );
|
|
break;
|
|
}
|
|
}
|
|
|
|
return $clean;
|
|
}
|
|
|
|
|
|
/**
|
|
* Clean a given option value
|
|
*
|
|
* @param array $option_value Old (not merged with defaults or filtered) option value to
|
|
* clean according to the rules for this option
|
|
* @param string $current_version (optional) Version from which to upgrade, if not set,
|
|
* version specific upgrades will be disregarded
|
|
* @param array $all_old_option_values (optional) Only used when importing old options to have
|
|
* access to the real old values, in contrast to the saved ones
|
|
*
|
|
* @return array Cleaned option
|
|
*/
|
|
/*protected function clean_option( $option_value, $current_version = null, $all_old_option_values = null ) {
|
|
|
|
return $option_value;
|
|
}*/
|
|
|
|
|
|
} /* End of class WPSEO_Option_Permalinks */
|
|
|
|
} /* End of class-exists wrapper */
|
|
|
|
|
|
/*******************************************************************
|
|
* Option: wpseo_titles
|
|
*******************************************************************/
|
|
if ( ! class_exists( 'WPSEO_Option_Titles' ) ) {
|
|
|
|
class WPSEO_Option_Titles extends WPSEO_Option {
|
|
|
|
/**
|
|
* @var string option name
|
|
*/
|
|
public $option_name = 'wpseo_titles';
|
|
|
|
/**
|
|
* @var array Array of defaults for the option
|
|
* Shouldn't be requested directly, use $this->get_defaults();
|
|
* @internal Note: Some of the default values are added via the translate_defaults() method
|
|
*/
|
|
protected $defaults = array(
|
|
// Non-form fields, set via (ajax) function
|
|
'title_test' => 0,
|
|
// Form fields
|
|
'forcerewritetitle' => false,
|
|
'separator' => 'sc-dash',
|
|
'hide-feedlinks' => false,
|
|
'hide-rsdlink' => false,
|
|
'hide-shortlink' => false,
|
|
'hide-wlwmanifest' => false,
|
|
'noodp' => false,
|
|
'noydir' => false,
|
|
'usemetakeywords' => false,
|
|
'title-home-wpseo' => '%%sitename%% %%page%% %%sep%% %%sitedesc%%', // text field
|
|
'title-author-wpseo' => '', // text field
|
|
'title-archive-wpseo' => '%%date%% %%page%% %%sep%% %%sitename%%', // text field
|
|
'title-search-wpseo' => '', // text field
|
|
'title-404-wpseo' => '', // text field
|
|
|
|
'metadesc-home-wpseo' => '', // text area
|
|
'metadesc-author-wpseo' => '', // text area
|
|
'metadesc-archive-wpseo' => '', // text area
|
|
|
|
'metakey-home-wpseo' => '', // text field
|
|
'metakey-author-wpseo' => '', // text field
|
|
|
|
'noindex-subpages-wpseo' => false,
|
|
'noindex-author-wpseo' => false,
|
|
'noindex-archive-wpseo' => true,
|
|
'disable-author' => false,
|
|
'disable-date' => false,
|
|
|
|
|
|
/**
|
|
* Uses enrich_defaults to add more along the lines of:
|
|
* - 'title-' . $pt->name => ''; // text field
|
|
* - 'metadesc-' . $pt->name => ''; // text field
|
|
* - 'metakey-' . $pt->name => ''; // text field
|
|
* - 'noindex-' . $pt->name => false;
|
|
* - 'showdate-' . $pt->name => false;
|
|
* - 'hideeditbox-' . $pt->name => false;
|
|
*
|
|
* - 'title-ptarchive-' . $pt->name => ''; // text field
|
|
* - 'metadesc-ptarchive-' . $pt->name => ''; // text field
|
|
* - 'metakey-ptarchive-' . $pt->name => ''; // text field
|
|
* - 'bctitle-ptarchive-' . $pt->name => ''; // text field
|
|
* - 'noindex-ptarchive-' . $pt->name => false;
|
|
*
|
|
* - 'title-tax-' . $tax->name => '''; // text field
|
|
* - 'metadesc-tax-' . $tax->name => ''; // text field
|
|
* - 'metakey-tax-' . $tax->name => ''; // text field
|
|
* - 'noindex-tax-' . $tax->name => false;
|
|
* - 'hideeditbox-tax-' . $tax->name => false;
|
|
*/
|
|
);
|
|
|
|
/**
|
|
* @var array Array of variable option name patterns for the option
|
|
*/
|
|
protected $variable_array_key_patterns = array(
|
|
'title-',
|
|
'metadesc-',
|
|
'metakey-',
|
|
'noindex-',
|
|
'showdate-',
|
|
'hideeditbox-',
|
|
'bctitle-ptarchive-',
|
|
);
|
|
|
|
/**
|
|
* @var array Array of sub-options which should not be overloaded with multi-site defaults
|
|
*/
|
|
public $ms_exclude = array(
|
|
/* theme dependent */
|
|
'title_test',
|
|
'forcerewritetitle',
|
|
);
|
|
|
|
/**
|
|
* @var array Array of the separator options. To get these options use WPSEO_Option_Titles::get_instance()->get_separator_options()
|
|
*/
|
|
private $separator_options = array(
|
|
'sc-dash' => '-',
|
|
'sc-ndash' => '–',
|
|
'sc-mdash' => '—',
|
|
'sc-middot' => '·',
|
|
'sc-bull' => '•',
|
|
'sc-star' => '*',
|
|
'sc-smstar' => '⋆',
|
|
'sc-pipe' => '|',
|
|
'sc-tilde' => '~',
|
|
'sc-laquo' => '«',
|
|
'sc-raquo' => '»',
|
|
'sc-lt' => '<',
|
|
'sc-gt' => '>',
|
|
);
|
|
|
|
/**
|
|
* Add the actions and filters for the option
|
|
*
|
|
* @todo [JRF => testers] Check if the extra actions below would run into problems if an option
|
|
* is updated early on and if so, change the call to schedule these for a later action on add/update
|
|
* instead of running them straight away
|
|
*
|
|
* @return \WPSEO_Option_Titles
|
|
*/
|
|
protected function __construct() {
|
|
parent::__construct();
|
|
add_action( 'update_option_' . $this->option_name, array( 'WPSEO_Options', 'clear_cache' ) );
|
|
add_action( 'init', array( $this, 'end_of_init' ), 999 );
|
|
}
|
|
|
|
|
|
/**
|
|
* Make sure we can recognize the right action for the double cleaning
|
|
*/
|
|
public function end_of_init() {
|
|
do_action( 'wpseo_double_clean_titles' );
|
|
}
|
|
|
|
|
|
/**
|
|
* Get the singleton instance of this class
|
|
*
|
|
* @return object
|
|
*/
|
|
public static function get_instance() {
|
|
if ( ! ( self::$instance instanceof self ) ) {
|
|
self::$instance = new self();
|
|
}
|
|
|
|
return self::$instance;
|
|
}
|
|
|
|
/**
|
|
* Get the available separator options
|
|
*
|
|
* @return array
|
|
*/
|
|
public function get_separator_options() {
|
|
$separators = $this->separator_options;
|
|
|
|
/**
|
|
* Allow altering the array with separator options
|
|
* @api array $separator_options Array with the separator options
|
|
*/
|
|
$filtered_separators = apply_filters( 'wpseo_separator_options', $separators );
|
|
|
|
if ( is_array( $filtered_separators ) && $filtered_separators !== array() ) {
|
|
$separators = array_merge( $separators, $filtered_separators );
|
|
}
|
|
|
|
return $separators;
|
|
}
|
|
|
|
/**
|
|
* Translate strings used in the option defaults
|
|
*
|
|
* @return void
|
|
*/
|
|
public function translate_defaults() {
|
|
$this->defaults['title-author-wpseo'] = sprintf( __( '%s, Author at %s', 'wordpress-seo' ), '%%name%%', '%%sitename%%' ) . ' %%page%% ';
|
|
$this->defaults['title-search-wpseo'] = sprintf( __( 'You searched for %s', 'wordpress-seo' ), '%%searchphrase%%' ) . ' %%page%% %%sep%% %%sitename%%';
|
|
$this->defaults['title-404-wpseo'] = __( 'Page not found', 'wordpress-seo' ) . ' %%sep%% %%sitename%%';
|
|
}
|
|
|
|
|
|
/**
|
|
* Add dynamically created default options based on available post types and taxonomies
|
|
*
|
|
* @return void
|
|
*/
|
|
public function enrich_defaults() {
|
|
|
|
// Retrieve all the relevant post type and taxonomy arrays
|
|
$post_type_names = get_post_types( array( 'public' => true ), 'names' );
|
|
|
|
$post_type_objects_custom = get_post_types( array( 'public' => true, '_builtin' => false ), 'objects' );
|
|
|
|
$taxonomy_names = get_taxonomies( array( 'public' => true ), 'names' );
|
|
|
|
|
|
if ( $post_type_names !== array() ) {
|
|
foreach ( $post_type_names as $pt ) {
|
|
$this->defaults[ 'title-' . $pt ] = '%%title%% %%page%% %%sep%% %%sitename%%'; // text field
|
|
$this->defaults[ 'metadesc-' . $pt ] = ''; // text area
|
|
$this->defaults[ 'metakey-' . $pt ] = ''; // text field
|
|
$this->defaults[ 'noindex-' . $pt ] = false;
|
|
$this->defaults[ 'showdate-' . $pt ] = false;
|
|
$this->defaults[ 'hideeditbox-' . $pt ] = false;
|
|
}
|
|
unset( $pt );
|
|
}
|
|
|
|
if ( $post_type_objects_custom !== array() ) {
|
|
foreach ( $post_type_objects_custom as $pt ) {
|
|
if ( ! $pt->has_archive ) {
|
|
continue;
|
|
}
|
|
|
|
$this->defaults[ 'title-ptarchive-' . $pt->name ] = sprintf( __( '%s Archive', 'wordpress-seo' ), '%%pt_plural%%' ) . ' %%page%% %%sep%% %%sitename%%'; // text field
|
|
$this->defaults[ 'metadesc-ptarchive-' . $pt->name ] = ''; // text area
|
|
$this->defaults[ 'metakey-ptarchive-' . $pt->name ] = ''; // text field
|
|
$this->defaults[ 'bctitle-ptarchive-' . $pt->name ] = ''; // text field
|
|
$this->defaults[ 'noindex-ptarchive-' . $pt->name ] = false;
|
|
}
|
|
unset( $pt );
|
|
}
|
|
|
|
if ( $taxonomy_names !== array() ) {
|
|
foreach ( $taxonomy_names as $tax ) {
|
|
$this->defaults[ 'title-tax-' . $tax ] = sprintf( __( '%s Archives', 'wordpress-seo' ), '%%term_title%%' ) . ' %%page%% %%sep%% %%sitename%%'; // text field
|
|
$this->defaults[ 'metadesc-tax-' . $tax ] = ''; // text area
|
|
$this->defaults[ 'metakey-tax-' . $tax ] = ''; // text field
|
|
$this->defaults[ 'hideeditbox-tax-' . $tax ] = false;
|
|
|
|
if ( $tax !== 'post_format' ) {
|
|
$this->defaults[ 'noindex-tax-' . $tax ] = false;
|
|
} else {
|
|
$this->defaults[ 'noindex-tax-' . $tax ] = true;
|
|
}
|
|
}
|
|
unset( $tax );
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
* Validate the option
|
|
*
|
|
* @param array $dirty New value for the option
|
|
* @param array $clean Clean value for the option, normally the defaults
|
|
* @param array $old Old value of the option
|
|
*
|
|
* @return array Validated clean value for the option to be saved to the database
|
|
*/
|
|
protected function validate_option( $dirty, $clean, $old ) {
|
|
|
|
foreach ( $clean as $key => $value ) {
|
|
$switch_key = $this->get_switch_key( $key );
|
|
|
|
switch ( $switch_key ) {
|
|
/* text fields */
|
|
/* Covers:
|
|
'title-home-wpseo', 'title-author-wpseo', 'title-archive-wpseo',
|
|
'title-search-wpseo', 'title-404-wpseo'
|
|
'title-' . $pt->name
|
|
'title-ptarchive-' . $pt->name
|
|
'title-tax-' . $tax->name */
|
|
case 'title-':
|
|
if ( isset( $dirty[ $key ] ) ) {
|
|
$clean[ $key ] = self::sanitize_text_field( $dirty[ $key ] );
|
|
}
|
|
break;
|
|
|
|
/* Covers:
|
|
'metadesc-home-wpseo', 'metadesc-author-wpseo', 'metadesc-archive-wpseo'
|
|
'metadesc-' . $pt->name
|
|
'metadesc-ptarchive-' . $pt->name
|
|
'metadesc-tax-' . $tax->name */
|
|
case 'metadesc-':
|
|
/* Covers:
|
|
'metakey-home-wpseo', 'metakey-author-wpseo'
|
|
'metakey-' . $pt->name
|
|
'metakey-ptarchive-' . $pt->name
|
|
'metakey-tax-' . $tax->name */
|
|
case 'metakey-':
|
|
/* Covers:
|
|
''bctitle-ptarchive-' . $pt->name */
|
|
case 'bctitle-ptarchive-':
|
|
if ( isset( $dirty[ $key ] ) && $dirty[ $key ] !== '' ) {
|
|
$clean[ $key ] = self::sanitize_text_field( $dirty[ $key ] );
|
|
}
|
|
break;
|
|
|
|
|
|
/* integer field - not in form*/
|
|
case 'title_test':
|
|
if ( isset( $dirty[ $key ] ) ) {
|
|
$int = self::validate_int( $dirty[ $key ] );
|
|
if ( $int !== false && $int >= 0 ) {
|
|
$clean[ $key ] = $int;
|
|
}
|
|
} elseif ( isset( $old[ $key ] ) ) {
|
|
$int = self::validate_int( $old[ $key ] );
|
|
if ( $int !== false && $int >= 0 ) {
|
|
$clean[ $key ] = $int;
|
|
}
|
|
}
|
|
break;
|
|
|
|
/* Separator field - Radio */
|
|
case 'separator':
|
|
if ( isset( $dirty[ $key ] ) && $dirty[ $key ] !== '' ) {
|
|
|
|
// Get separator fields
|
|
$separator_fields = $this->get_separator_options();
|
|
|
|
// Check if the given separator is exists
|
|
if ( isset( $separator_fields[ $dirty[ $key ] ] ) ) {
|
|
$clean[ $key ] = $dirty[ $key ];
|
|
}
|
|
}
|
|
break;
|
|
|
|
/* boolean fields */
|
|
case 'forcerewritetitle':
|
|
case 'usemetakeywords':
|
|
case 'noodp':
|
|
case 'noydir':
|
|
case 'hide-rsdlink':
|
|
case 'hide-wlwmanifest':
|
|
case 'hide-shortlink':
|
|
case 'hide-feedlinks':
|
|
case 'disable-author':
|
|
case 'disable-date':
|
|
/* Covers:
|
|
'noindex-subpages-wpseo', 'noindex-author-wpseo', 'noindex-archive-wpseo'
|
|
'noindex-' . $pt->name
|
|
'noindex-ptarchive-' . $pt->name
|
|
'noindex-tax-' . $tax->name */
|
|
case 'noindex-':
|
|
case 'showdate-': /* 'showdate-'. $pt->name */
|
|
/* Covers:
|
|
'hideeditbox-'. $pt->name
|
|
'hideeditbox-tax-' . $tax->name */
|
|
case 'hideeditbox-':
|
|
default:
|
|
$clean[ $key ] = ( isset( $dirty[ $key ] ) ? self::validate_bool( $dirty[ $key ] ) : false );
|
|
break;
|
|
}
|
|
}
|
|
|
|
return $clean;
|
|
}
|
|
|
|
|
|
/**
|
|
* Clean a given option value
|
|
*
|
|
* @param array $option_value Old (not merged with defaults or filtered) option value to
|
|
* clean according to the rules for this option
|
|
* @param string $current_version (optional) Version from which to upgrade, if not set,
|
|
* version specific upgrades will be disregarded
|
|
* @param array $all_old_option_values (optional) Only used when importing old options to have
|
|
* access to the real old values, in contrast to the saved ones
|
|
*
|
|
* @return array Cleaned option
|
|
*/
|
|
protected function clean_option( $option_value, $current_version = null, $all_old_option_values = null ) {
|
|
static $original = null;
|
|
|
|
// Double-run this function to ensure renaming of the taxonomy options will work
|
|
if ( ! isset( $original ) && has_action( 'wpseo_double_clean_titles', array(
|
|
$this,
|
|
'clean',
|
|
) ) === false
|
|
) {
|
|
add_action( 'wpseo_double_clean_titles', array( $this, 'clean' ) );
|
|
$original = $option_value;
|
|
}
|
|
|
|
/* Move options from very old option to this one
|
|
@internal Don't rename to the 'current' names straight away as that would prevent
|
|
the rename/unset combi below from working
|
|
@todo [JRF] maybe figure out a smarter way to deal with this */
|
|
$old_option = null;
|
|
if ( isset( $all_old_option_values ) ) {
|
|
// Ok, we have an import
|
|
if ( isset( $all_old_option_values['wpseo_indexation'] ) && is_array( $all_old_option_values['wpseo_indexation'] ) && $all_old_option_values['wpseo_indexation'] !== array() ) {
|
|
$old_option = $all_old_option_values['wpseo_indexation'];
|
|
}
|
|
} else {
|
|
$old_option = get_option( 'wpseo_indexation' );
|
|
}
|
|
if ( is_array( $old_option ) && $old_option !== array() ) {
|
|
$move = array(
|
|
'noindexauthor' => 'noindex-author',
|
|
'disableauthor' => 'disable-author',
|
|
'noindexdate' => 'noindex-archive',
|
|
'noindexcat' => 'noindex-category',
|
|
'noindextag' => 'noindex-post_tag',
|
|
'noindexpostformat' => 'noindex-post_format',
|
|
'noindexsubpages' => 'noindex-subpages',
|
|
'hidersdlink' => 'hide-rsdlink',
|
|
'hidefeedlinks' => 'hide-feedlinks',
|
|
'hidewlwmanifest' => 'hide-wlwmanifest',
|
|
'hideshortlink' => 'hide-shortlink',
|
|
);
|
|
foreach ( $move as $old => $new ) {
|
|
if ( isset( $old_option[ $old ] ) && ! isset( $option_value[ $new ] ) ) {
|
|
$option_value[ $new ] = $old_option[ $old ];
|
|
}
|
|
}
|
|
unset( $move, $old, $new );
|
|
}
|
|
unset( $old_option );
|
|
|
|
|
|
// Fix wrongness created by buggy version 1.2.2
|
|
if ( isset( $option_value['title-home'] ) && $option_value['title-home'] === '%%sitename%% - %%sitedesc%% - 12345' ) {
|
|
$option_value['title-home-wpseo'] = '%%sitename%% - %%sitedesc%%';
|
|
}
|
|
|
|
|
|
/* Renaming these options to avoid ever overwritting these if a (bloody stupid) user /
|
|
programmer would use any of the following as a custom post type or custom taxonomy:
|
|
'home', 'author', 'archive', 'search', '404', 'subpages'
|
|
|
|
Similarly, renaming the tax options to avoid a custom post type and a taxonomy
|
|
with the same name occupying the same option */
|
|
$rename = array(
|
|
'title-home' => 'title-home-wpseo',
|
|
'title-author' => 'title-author-wpseo',
|
|
'title-archive' => 'title-archive-wpseo',
|
|
'title-search' => 'title-search-wpseo',
|
|
'title-404' => 'title-404-wpseo',
|
|
'metadesc-home' => 'metadesc-home-wpseo',
|
|
'metadesc-author' => 'metadesc-author-wpseo',
|
|
'metadesc-archive' => 'metadesc-archive-wpseo',
|
|
'metakey-home' => 'metakey-home-wpseo',
|
|
'metakey-author' => 'metakey-author-wpseo',
|
|
'noindex-subpages' => 'noindex-subpages-wpseo',
|
|
'noindex-author' => 'noindex-author-wpseo',
|
|
'noindex-archive' => 'noindex-archive-wpseo',
|
|
);
|
|
foreach ( $rename as $old => $new ) {
|
|
if ( isset( $option_value[ $old ] ) && ! isset( $option_value[ $new ] ) ) {
|
|
$option_value[ $new ] = $option_value[ $old ];
|
|
unset( $option_value[ $old ] );
|
|
}
|
|
}
|
|
unset( $rename, $old, $new );
|
|
|
|
|
|
/* @internal This clean-up action can only be done effectively once the taxonomies and post_types
|
|
* have been registered, i.e. at the end of the init action. */
|
|
if ( isset( $original ) && current_filter() === 'wpseo_double_clean_titles' || did_action( 'wpseo_double_clean_titles' ) > 0 ) {
|
|
$rename = array(
|
|
'title-' => 'title-tax-',
|
|
'metadesc-' => 'metadesc-tax-',
|
|
'metakey-' => 'metakey-tax-',
|
|
'noindex-' => 'noindex-tax-',
|
|
'tax-hideeditbox-' => 'hideeditbox-tax-',
|
|
|
|
);
|
|
$taxonomy_names = get_taxonomies( array( 'public' => true ), 'names' );
|
|
$post_type_names = get_post_types( array( 'public' => true ), 'names' );
|
|
$defaults = $this->get_defaults();
|
|
if ( $taxonomy_names !== array() ) {
|
|
foreach ( $taxonomy_names as $tax ) {
|
|
foreach ( $rename as $old_prefix => $new_prefix ) {
|
|
if (
|
|
( isset( $original[ $old_prefix . $tax ] ) && ! isset( $original[ $new_prefix . $tax ] ) )
|
|
&& ( ! isset( $option_value[ $new_prefix . $tax ] )
|
|
|| ( isset( $option_value[ $new_prefix . $tax ] )
|
|
&& $option_value[ $new_prefix . $tax ] === $defaults[ $new_prefix . $tax ] ) )
|
|
) {
|
|
$option_value[ $new_prefix . $tax ] = $original[ $old_prefix . $tax ];
|
|
|
|
/* Check if there is a cpt with the same name as the tax,
|
|
if so, we should make sure that the old setting hasn't been removed */
|
|
if ( ! isset( $post_type_names[ $tax ] ) && isset( $option_value[ $old_prefix . $tax ] ) ) {
|
|
unset( $option_value[ $old_prefix . $tax ] );
|
|
} else {
|
|
if ( isset( $post_type_names[ $tax ] ) && ! isset( $option_value[ $old_prefix . $tax ] ) ) {
|
|
$option_value[ $old_prefix . $tax ] = $original[ $old_prefix . $tax ];
|
|
}
|
|
}
|
|
|
|
if ( $old_prefix === 'tax-hideeditbox-' ) {
|
|
unset( $option_value[ $old_prefix . $tax ] );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
unset( $rename, $taxonomy_names, $post_type_names, $tax, $old_prefix, $new_prefix );
|
|
}
|
|
|
|
|
|
/* Make sure the values of the variable option key options are cleaned as they
|
|
may be retained and would not be cleaned/validated then */
|
|
if ( is_array( $option_value ) && $option_value !== array() ) {
|
|
foreach ( $option_value as $key => $value ) {
|
|
$switch_key = $this->get_switch_key( $key );
|
|
|
|
// Similar to validation routine - any changes made there should be made here too
|
|
switch ( $switch_key ) {
|
|
/* text fields */
|
|
case 'title-':
|
|
case 'metadesc-':
|
|
case 'metakey-':
|
|
case 'bctitle-ptarchive-':
|
|
$option_value[ $key ] = self::sanitize_text_field( $value );
|
|
break;
|
|
|
|
|
|
/* boolean fields */
|
|
case 'noindex-':
|
|
case 'showdate-':
|
|
case 'hideeditbox-':
|
|
default:
|
|
$option_value[ $key ] = self::validate_bool( $value );
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
return $option_value;
|
|
}
|
|
|
|
|
|
/**
|
|
* Make sure that any set option values relating to post_types and/or taxonomies are retained,
|
|
* even when that post_type or taxonomy may not yet have been registered.
|
|
*
|
|
* @internal Overrule the abstract class version of this to make sure one extra renamed variable key
|
|
* does not get removed. IMPORTANT: keep this method in line with the parent on which it is based!
|
|
*
|
|
* @param array $dirty Original option as retrieved from the database
|
|
* @param array $clean Filtered option where any options which shouldn't be in our option
|
|
* have already been removed and any options which weren't set
|
|
* have been set to their defaults
|
|
*
|
|
* @return array
|
|
*/
|
|
protected function retain_variable_keys( $dirty, $clean ) {
|
|
if ( ( is_array( $this->variable_array_key_patterns ) && $this->variable_array_key_patterns !== array() ) && ( is_array( $dirty ) && $dirty !== array() ) ) {
|
|
|
|
// Add the extra pattern
|
|
$patterns = $this->variable_array_key_patterns;
|
|
$patterns[] = 'tax-hideeditbox-';
|
|
|
|
/**
|
|
* Allow altering the array with variable array key patterns
|
|
* @api array $patterns Array with the variable array key patterns
|
|
*/
|
|
$patterns = apply_filters( 'wpseo_option_titles_variable_array_key_patterns', $patterns );
|
|
|
|
foreach ( $dirty as $key => $value ) {
|
|
|
|
// do nothing if already in filtered option array
|
|
if ( isset( $clean[ $key ] ) ) {
|
|
continue;
|
|
}
|
|
|
|
foreach ( $patterns as $pattern ) {
|
|
if ( strpos( $key, $pattern ) === 0 ) {
|
|
$clean[ $key ] = $value;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return $clean;
|
|
}
|
|
|
|
|
|
} /* End of class WPSEO_Option_Titles */
|
|
|
|
} /* End of class-exists wrapper */
|
|
|
|
|
|
/*******************************************************************
|
|
* Option: wpseo_rss
|
|
*******************************************************************/
|
|
if ( ! class_exists( 'WPSEO_Option_RSS' ) ) {
|
|
|
|
class WPSEO_Option_RSS extends WPSEO_Option {
|
|
|
|
/**
|
|
* @var string option name
|
|
*/
|
|
public $option_name = 'wpseo_rss';
|
|
|
|
/**
|
|
* @var array Array of defaults for the option
|
|
* Shouldn't be requested directly, use $this->get_defaults();
|
|
* @internal Note: Some of the default values are added via the translate_defaults() method
|
|
*/
|
|
protected $defaults = array(
|
|
'rssbefore' => '', // text area
|
|
'rssafter' => '', // text area
|
|
);
|
|
|
|
|
|
/**
|
|
* Get the singleton instance of this class
|
|
*
|
|
* @return object
|
|
*/
|
|
public static function get_instance() {
|
|
if ( ! ( self::$instance instanceof self ) ) {
|
|
self::$instance = new self();
|
|
}
|
|
|
|
return self::$instance;
|
|
}
|
|
|
|
|
|
/**
|
|
* Translate strings used in the option defaults
|
|
*
|
|
* @return void
|
|
*/
|
|
public function translate_defaults() {
|
|
$this->defaults['rssafter'] = sprintf( __( 'The post %s appeared first on %s.', 'wordpress-seo' ), '%%POSTLINK%%', '%%BLOGLINK%%' );
|
|
}
|
|
|
|
|
|
/**
|
|
* Validate the option
|
|
*
|
|
* @param array $dirty New value for the option
|
|
* @param array $clean Clean value for the option, normally the defaults
|
|
* @param array $old Old value of the option
|
|
*
|
|
* @return array Validated clean value for the option to be saved to the database
|
|
*/
|
|
protected function validate_option( $dirty, $clean, $old ) {
|
|
foreach ( $clean as $key => $value ) {
|
|
if ( isset( $dirty[ $key ] ) ) {
|
|
$clean[ $key ] = wp_kses_post( $dirty[ $key ] );
|
|
}
|
|
}
|
|
|
|
return $clean;
|
|
}
|
|
|
|
} /* End of class WPSEO_Option_RSS */
|
|
|
|
} /* End of class-exists wrapper */
|
|
|
|
|
|
/*******************************************************************
|
|
* Option: wpseo_internallinks
|
|
*******************************************************************/
|
|
if ( ! class_exists( 'WPSEO_Option_InternalLinks' ) ) {
|
|
|
|
class WPSEO_Option_InternalLinks extends WPSEO_Option {
|
|
|
|
/**
|
|
* @var string option name
|
|
*/
|
|
public $option_name = 'wpseo_internallinks';
|
|
|
|
/**
|
|
* @var array Array of defaults for the option
|
|
* Shouldn't be requested directly, use $this->get_defaults();
|
|
* @internal Note: Some of the default values are added via the translate_defaults() method
|
|
*/
|
|
protected $defaults = array(
|
|
'breadcrumbs-404crumb' => '', // text field
|
|
'breadcrumbs-blog-remove' => false,
|
|
'breadcrumbs-boldlast' => false,
|
|
'breadcrumbs-archiveprefix' => '', // text field
|
|
'breadcrumbs-enable' => false,
|
|
'breadcrumbs-home' => '', // text field
|
|
'breadcrumbs-prefix' => '', // text field
|
|
'breadcrumbs-searchprefix' => '', // text field
|
|
'breadcrumbs-sep' => '»', // text field
|
|
|
|
/**
|
|
* Uses enrich_defaults() to add more along the lines of:
|
|
* - 'post_types-' . $pt->name . '-maintax' => 0 / string
|
|
* - 'taxonomy-' . $tax->name . '-ptparent' => 0 / string
|
|
*/
|
|
);
|
|
|
|
/**
|
|
* @var array Array of variable option name patterns for the option
|
|
*/
|
|
protected $variable_array_key_patterns = array(
|
|
'post_types-',
|
|
'taxonomy-',
|
|
);
|
|
|
|
|
|
/**
|
|
* Get the singleton instance of this class
|
|
*
|
|
* @return object
|
|
*/
|
|
public static function get_instance() {
|
|
if ( ! ( self::$instance instanceof self ) ) {
|
|
self::$instance = new self();
|
|
}
|
|
|
|
return self::$instance;
|
|
}
|
|
|
|
|
|
/**
|
|
* Translate strings used in the option defaults
|
|
*
|
|
* @return void
|
|
*/
|
|
public function translate_defaults() {
|
|
$this->defaults['breadcrumbs-404crumb'] = __( 'Error 404: Page not found', 'wordpress-seo' );
|
|
$this->defaults['breadcrumbs-archiveprefix'] = __( 'Archives for', 'wordpress-seo' );
|
|
$this->defaults['breadcrumbs-home'] = __( 'Home', 'wordpress-seo' );
|
|
$this->defaults['breadcrumbs-searchprefix'] = __( 'You searched for', 'wordpress-seo' );
|
|
}
|
|
|
|
|
|
/**
|
|
* Add dynamically created default options based on available post types and taxonomies
|
|
*
|
|
* @return void
|
|
*/
|
|
public function enrich_defaults() {
|
|
|
|
// Retrieve all the relevant post type and taxonomy arrays
|
|
$post_type_names = get_post_types( array( 'public' => true ), 'names' );
|
|
$taxonomy_names_custom = get_taxonomies( array( 'public' => true, '_builtin' => false ), 'names' );
|
|
|
|
if ( $post_type_names !== array() ) {
|
|
foreach ( $post_type_names as $pt ) {
|
|
$pto_taxonomies = get_object_taxonomies( $pt, 'names' );
|
|
if ( $pto_taxonomies !== array() ) {
|
|
$this->defaults[ 'post_types-' . $pt . '-maintax' ] = 0; // select box
|
|
}
|
|
unset( $pto_taxonomies );
|
|
}
|
|
unset( $pt );
|
|
}
|
|
|
|
if ( $taxonomy_names_custom !== array() ) {
|
|
foreach ( $taxonomy_names_custom as $tax ) {
|
|
$this->defaults[ 'taxonomy-' . $tax . '-ptparent' ] = 0; // select box;
|
|
}
|
|
unset( $tax );
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
* Validate the option
|
|
*
|
|
* @param array $dirty New value for the option
|
|
* @param array $clean Clean value for the option, normally the defaults
|
|
* @param array $old Old value of the option
|
|
*
|
|
* @return array Validated clean value for the option to be saved to the database
|
|
*/
|
|
protected function validate_option( $dirty, $clean, $old ) {
|
|
|
|
$allowed_post_types = $this->get_allowed_post_types();
|
|
|
|
foreach ( $clean as $key => $value ) {
|
|
|
|
$switch_key = $this->get_switch_key( $key );
|
|
|
|
switch ( $switch_key ) {
|
|
/* text fields */
|
|
case 'breadcrumbs-404crumb':
|
|
case 'breadcrumbs-archiveprefix':
|
|
case 'breadcrumbs-home':
|
|
case 'breadcrumbs-prefix':
|
|
case 'breadcrumbs-searchprefix':
|
|
case 'breadcrumbs-sep':
|
|
if ( isset( $dirty[ $key ] ) ) {
|
|
$clean[ $key ] = wp_kses_post( $dirty[ $key ] );
|
|
}
|
|
break;
|
|
|
|
|
|
/* 'post_types-' . $pt->name . '-maintax' fields */
|
|
case 'post_types-':
|
|
$post_type = str_replace( array( 'post_types-', '-maintax' ), '', $key );
|
|
$taxonomies = get_object_taxonomies( $post_type, 'names' );
|
|
|
|
if ( isset( $dirty[ $key ] ) ) {
|
|
if ( $taxonomies !== array() && in_array( $dirty[ $key ], $taxonomies, true ) ) {
|
|
$clean[ $key ] = $dirty[ $key ];
|
|
} elseif ( (string) $dirty[ $key ] === '0' || (string) $dirty[ $key ] === '' ) {
|
|
$clean[ $key ] = 0;
|
|
} elseif ( sanitize_title_with_dashes( $dirty[ $key ] ) === $dirty[ $key ] ) {
|
|
// Allow taxonomies which may not be registered yet
|
|
$clean[ $key ] = $dirty[ $key ];
|
|
} else {
|
|
if ( isset( $old[ $key ] ) ) {
|
|
$clean[ $key ] = sanitize_title_with_dashes( $old[ $key ] );
|
|
}
|
|
if ( function_exists( 'add_settings_error' ) ) {
|
|
/* @todo [JRF => whomever] maybe change the untranslated $pt name in the
|
|
* error message to the nicely translated label ? */
|
|
add_settings_error(
|
|
$this->group_name, // slug title of the setting
|
|
'_' . $key, // suffix-id for the error message box
|
|
sprintf( __( 'Please select a valid taxonomy for post type "%s"', 'wordpress-seo' ), $post_type ), // the error message
|
|
'error' // error type, either 'error' or 'updated'
|
|
);
|
|
}
|
|
}
|
|
} elseif ( isset( $old[ $key ] ) ) {
|
|
$clean[ $key ] = sanitize_title_with_dashes( $old[ $key ] );
|
|
}
|
|
unset( $taxonomies, $post_type );
|
|
break;
|
|
|
|
|
|
/* 'taxonomy-' . $tax->name . '-ptparent' fields */
|
|
case 'taxonomy-':
|
|
if ( isset( $dirty[ $key ] ) ) {
|
|
if ( $allowed_post_types !== array() && in_array( $dirty[ $key ], $allowed_post_types, true ) ) {
|
|
$clean[ $key ] = $dirty[ $key ];
|
|
} elseif ( (string) $dirty[ $key ] === '0' || (string) $dirty[ $key ] === '' ) {
|
|
$clean[ $key ] = 0;
|
|
} elseif ( sanitize_key( $dirty[ $key ] ) === $dirty[ $key ] ) {
|
|
// Allow taxonomies which may not be registered yet
|
|
$clean[ $key ] = $dirty[ $key ];
|
|
} else {
|
|
if ( isset( $old[ $key ] ) ) {
|
|
$clean[ $key ] = sanitize_key( $old[ $key ] );
|
|
}
|
|
if ( function_exists( 'add_settings_error' ) ) {
|
|
/* @todo [JRF =? whomever] maybe change the untranslated $tax name in the
|
|
* error message to the nicely translated label ? */
|
|
$tax = str_replace( array( 'taxonomy-', '-ptparent' ), '', $key );
|
|
add_settings_error(
|
|
$this->group_name, // slug title of the setting
|
|
'_' . $tax, // suffix-id for the error message box
|
|
sprintf( __( 'Please select a valid post type for taxonomy "%s"', 'wordpress-seo' ), $tax ), // the error message
|
|
'error' // error type, either 'error' or 'updated'
|
|
);
|
|
unset( $tax );
|
|
}
|
|
}
|
|
} elseif ( isset( $old[ $key ] ) ) {
|
|
$clean[ $key ] = sanitize_key( $old[ $key ] );
|
|
}
|
|
break;
|
|
|
|
|
|
/* boolean fields */
|
|
case 'breadcrumbs-blog-remove':
|
|
case 'breadcrumbs-boldlast':
|
|
case 'breadcrumbs-enable':
|
|
default:
|
|
$clean[ $key ] = ( isset( $dirty[ $key ] ) ? self::validate_bool( $dirty[ $key ] ) : false );
|
|
break;
|
|
}
|
|
}
|
|
|
|
return $clean;
|
|
}
|
|
|
|
|
|
/**
|
|
* Retrieve a list of the allowed post types as breadcrumb parent for a taxonomy
|
|
* Helper method for validation
|
|
* @internal don't make static as new types may still be registered
|
|
*/
|
|
protected function get_allowed_post_types() {
|
|
$allowed_post_types = array();
|
|
|
|
$post_types = get_post_types( array( 'public' => true ), 'objects' );
|
|
|
|
if ( get_option( 'show_on_front' ) == 'page' && get_option( 'page_for_posts' ) > 0 ) {
|
|
$allowed_post_types[] = 'post';
|
|
}
|
|
|
|
if ( is_array( $post_types ) && $post_types !== array() ) {
|
|
foreach ( $post_types as $type ) {
|
|
if ( $type->has_archive ) {
|
|
$allowed_post_types[] = $type->name;
|
|
}
|
|
}
|
|
}
|
|
|
|
return $allowed_post_types;
|
|
}
|
|
|
|
|
|
/**
|
|
* Clean a given option value
|
|
*
|
|
* @param array $option_value Old (not merged with defaults or filtered) option value to
|
|
* clean according to the rules for this option
|
|
* @param string $current_version (optional) Version from which to upgrade, if not set,
|
|
* version specific upgrades will be disregarded
|
|
* @param array $all_old_option_values (optional) Only used when importing old options to have
|
|
* access to the real old values, in contrast to the saved ones
|
|
*
|
|
* @return array Cleaned option
|
|
*/
|
|
protected function clean_option( $option_value, $current_version = null, $all_old_option_values = null ) {
|
|
|
|
/* Make sure the old fall-back defaults for empty option keys are now added to the option */
|
|
if ( isset( $current_version ) && version_compare( $current_version, '1.5.2.3', '<' ) ) {
|
|
if ( has_action( 'init', array( 'WPSEO_Options', 'bring_back_breadcrumb_defaults' ) ) === false ) {
|
|
add_action( 'init', array( 'WPSEO_Options', 'bring_back_breadcrumb_defaults' ), 3 );
|
|
}
|
|
}
|
|
|
|
/* Make sure the values of the variable option key options are cleaned as they
|
|
may be retained and would not be cleaned/validated then */
|
|
if ( is_array( $option_value ) && $option_value !== array() ) {
|
|
|
|
$allowed_post_types = $this->get_allowed_post_types();
|
|
|
|
foreach ( $option_value as $key => $value ) {
|
|
$switch_key = $this->get_switch_key( $key );
|
|
|
|
// Similar to validation routine - any changes made there should be made here too
|
|
switch ( $switch_key ) {
|
|
/* 'post_types-' . $pt->name . '-maintax' fields */
|
|
case 'post_types-':
|
|
$post_type = str_replace( array( 'post_types-', '-maintax' ), '', $key );
|
|
$taxonomies = get_object_taxonomies( $post_type, 'names' );
|
|
|
|
if ( $taxonomies !== array() && in_array( $value, $taxonomies, true ) ) {
|
|
$option_value[ $key ] = $value;
|
|
} elseif ( (string) $value === '0' || (string) $value === '' ) {
|
|
$option_value[ $key ] = 0;
|
|
} elseif ( sanitize_title_with_dashes( $value ) === $value ) {
|
|
// Allow taxonomies which may not be registered yet
|
|
$option_value[ $key ] = $value;
|
|
}
|
|
unset( $taxonomies, $post_type );
|
|
break;
|
|
|
|
|
|
/* 'taxonomy-' . $tax->name . '-ptparent' fields */
|
|
case 'taxonomy-':
|
|
if ( $allowed_post_types !== array() && in_array( $value, $allowed_post_types, true ) ) {
|
|
$option_value[ $key ] = $value;
|
|
} elseif ( (string) $value === '0' || (string) $value === '' ) {
|
|
$option_value[ $key ] = 0;
|
|
} elseif ( sanitize_key( $option_value[ $key ] ) === $option_value[ $key ] ) {
|
|
// Allow post types which may not be registered yet
|
|
$option_value[ $key ] = $value;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
return $option_value;
|
|
}
|
|
|
|
/**
|
|
* With the changes to v1.5, the defaults for some of the textual breadcrumb settings are added
|
|
* dynamically, but empty strings are allowed.
|
|
* This caused issues for people who left the fields empty on purpose relying on the defaults.
|
|
* This little routine fixes that.
|
|
* Needs to be run on 'init' hook at prio 3 to make sure the defaults are translated.
|
|
*/
|
|
public function bring_back_defaults() {
|
|
$option = get_option( $this->option_name );
|
|
|
|
$values_to_bring_back = array(
|
|
'breadcrumbs-404crumb',
|
|
'breadcrumbs-archiveprefix',
|
|
'breadcrumbs-home',
|
|
'breadcrumbs-searchprefix',
|
|
'breadcrumbs-sep',
|
|
);
|
|
foreach ( $values_to_bring_back as $key ) {
|
|
if ( $option[ $key ] === '' && $this->defaults[ $key ] !== '' ) {
|
|
$option[ $key ] = $this->defaults[ $key ];
|
|
}
|
|
}
|
|
update_option( $this->option_name, $option );
|
|
}
|
|
|
|
} /* End of class WPSEO_Option_InternalLinks */
|
|
|
|
} /* End of class-exists wrapper */
|
|
|
|
|
|
/*******************************************************************
|
|
* Option: wpseo_xml
|
|
*******************************************************************/
|
|
if ( ! class_exists( 'WPSEO_Option_XML' ) ) {
|
|
|
|
class WPSEO_Option_XML extends WPSEO_Option {
|
|
|
|
/**
|
|
* @var string option name
|
|
*/
|
|
public $option_name = 'wpseo_xml';
|
|
|
|
/**
|
|
* @var string option group name for use in settings forms
|
|
*/
|
|
public $group_name = 'yoast_wpseo_xml_sitemap_options';
|
|
|
|
/**
|
|
* @var array Array of defaults for the option
|
|
* Shouldn't be requested directly, use $this->get_defaults();
|
|
*/
|
|
protected $defaults = array(
|
|
'disable_author_sitemap' => true,
|
|
'disable_author_noposts' => true,
|
|
'enablexmlsitemap' => true,
|
|
'entries-per-page' => 1000,
|
|
'xml_ping_yahoo' => false,
|
|
'xml_ping_ask' => false,
|
|
|
|
/**
|
|
* Uses enrich_defaults to add more along the lines of:
|
|
* - 'user_role-' . $role_name . '-not_in_sitemap' => bool
|
|
* - 'post_types-' . $pt->name . '-not_in_sitemap' => bool
|
|
* - 'taxonomies-' . $tax->name . '-not_in_sitemap' => bool
|
|
*/
|
|
);
|
|
|
|
/**
|
|
* @var array Array of variable option name patterns for the option
|
|
*/
|
|
protected $variable_array_key_patterns = array(
|
|
'user_role-',
|
|
'post_types-',
|
|
'taxonomies-',
|
|
);
|
|
|
|
|
|
/**
|
|
* Add the actions and filters for the option
|
|
*
|
|
* @todo [JRF => testers] Check if the extra actions below would run into problems if an option
|
|
* is updated early on and if so, change the call to schedule these for a later action on add/update
|
|
* instead of running them straight away
|
|
*
|
|
* @return \WPSEO_Option_XML
|
|
*/
|
|
protected function __construct() {
|
|
parent::__construct();
|
|
add_action( 'update_option_' . $this->option_name, array( 'WPSEO_Options', 'clear_rewrites' ) );
|
|
}
|
|
|
|
|
|
/**
|
|
* Get the singleton instance of this class
|
|
*
|
|
* @return object
|
|
*/
|
|
public static function get_instance() {
|
|
if ( ! ( self::$instance instanceof self ) ) {
|
|
self::$instance = new self();
|
|
}
|
|
|
|
return self::$instance;
|
|
}
|
|
|
|
|
|
/**
|
|
* Add dynamically created default options based on available post types and taxonomies
|
|
*
|
|
* @return void
|
|
*/
|
|
public function enrich_defaults() {
|
|
|
|
$user_roles = wpseo_get_roles();
|
|
$filtered_user_roles = apply_filters( 'wpseo_sitemaps_supported_user_roles', $user_roles );
|
|
if ( is_array( $filtered_user_roles ) && $filtered_user_roles !== array() ) {
|
|
foreach ( $filtered_user_roles as $role_name => $role_value ) {
|
|
$this->defaults['user_role-' . $role_name . '-not_in_sitemap'] = false;
|
|
|
|
unset( $user_role );
|
|
}
|
|
}
|
|
unset( $filtered_user_roles );
|
|
|
|
$post_type_names = get_post_types( array( 'public' => true ), 'names' );
|
|
$filtered_post_types = apply_filters( 'wpseo_sitemaps_supported_post_types', $post_type_names );
|
|
|
|
if ( is_array( $filtered_post_types ) && $filtered_post_types !== array() ) {
|
|
foreach ( $filtered_post_types as $pt ) {
|
|
if ( $pt !== 'attachment' ) {
|
|
$this->defaults[ 'post_types-' . $pt . '-not_in_sitemap' ] = false;
|
|
} else {
|
|
$this->defaults[ 'post_types-' . $pt . '-not_in_sitemap' ] = true;
|
|
}
|
|
}
|
|
unset( $pt );
|
|
}
|
|
unset( $filtered_post_types );
|
|
|
|
$taxonomy_objects = get_taxonomies( array( 'public' => true ), 'objects' );
|
|
$filtered_taxonomies = apply_filters( 'wpseo_sitemaps_supported_taxonomies', $taxonomy_objects );
|
|
if ( is_array( $filtered_taxonomies ) && $filtered_taxonomies !== array() ) {
|
|
foreach ( $filtered_taxonomies as $tax ) {
|
|
if ( isset( $tax->labels->name ) && trim( $tax->labels->name ) != '' ) {
|
|
$this->defaults[ 'taxonomies-' . $tax->name . '-not_in_sitemap' ] = false;
|
|
}
|
|
}
|
|
unset( $tax );
|
|
}
|
|
unset( $filtered_taxonomies );
|
|
|
|
}
|
|
|
|
|
|
/**
|
|
* Validate the option
|
|
*
|
|
* @param array $dirty New value for the option
|
|
* @param array $clean Clean value for the option, normally the defaults
|
|
* @param array $old Old value of the option
|
|
*
|
|
* @return array Validated clean value for the option to be saved to the database
|
|
*/
|
|
protected function validate_option( $dirty, $clean, $old ) {
|
|
|
|
foreach ( $clean as $key => $value ) {
|
|
$switch_key = $this->get_switch_key( $key );
|
|
|
|
switch ( $switch_key ) {
|
|
/* integer fields */
|
|
case 'entries-per-page':
|
|
/* @todo [JRF/JRF => Yoast] add some more rules (minimum 50 or something
|
|
* - what should be the guideline?) and adjust error message */
|
|
if ( isset( $dirty[ $key ] ) && $dirty[ $key ] !== '' ) {
|
|
$int = self::validate_int( $dirty[ $key ] );
|
|
if ( $int !== false && $int > 0 ) {
|
|
$clean[ $key ] = $int;
|
|
} else {
|
|
if ( isset( $old[ $key ] ) && $old[ $key ] !== '' ) {
|
|
$int = self::validate_int( $old[ $key ] );
|
|
if ( $int !== false && $int > 0 ) {
|
|
$clean[ $key ] = $int;
|
|
}
|
|
}
|
|
if ( function_exists( 'add_settings_error' ) ) {
|
|
add_settings_error(
|
|
$this->group_name, // slug title of the setting
|
|
'_' . $key, // suffix-id for the error message box
|
|
sprintf( __( '"Max entries per sitemap page" should be a positive number, which %s is not. Please correct.', 'wordpress-seo' ), '<strong>' . esc_html( sanitize_text_field( $dirty[ $key ] ) ) . '</strong>' ), // the error message
|
|
'error' // error type, either 'error' or 'updated'
|
|
);
|
|
}
|
|
}
|
|
unset( $int );
|
|
}
|
|
break;
|
|
|
|
|
|
/* boolean fields */
|
|
case 'disable_author_sitemap':
|
|
case 'disable_author_noposts':
|
|
case 'enablexmlsitemap':
|
|
case 'user_role-': /* 'user_role' . $role_name . '-not_in_sitemap' fields */
|
|
case 'post_types-': /* 'post_types-' . $pt->name . '-not_in_sitemap' fields */
|
|
case 'taxonomies-': /* 'taxonomies-' . $tax->name . '-not_in_sitemap' fields */
|
|
case 'xml_ping_yahoo':
|
|
case 'xml_ping_ask':
|
|
default:
|
|
$clean[ $key ] = ( isset( $dirty[ $key ] ) ? self::validate_bool( $dirty[ $key ] ) : false );
|
|
break;
|
|
}
|
|
}
|
|
|
|
return $clean;
|
|
}
|
|
|
|
|
|
/**
|
|
* Clean a given option value
|
|
*
|
|
* @param array $option_value Old (not merged with defaults or filtered) option value to
|
|
* clean according to the rules for this option
|
|
* @param string $current_version (optional) Version from which to upgrade, if not set,
|
|
* version specific upgrades will be disregarded
|
|
* @param array $all_old_option_values (optional) Only used when importing old options to have
|
|
* access to the real old values, in contrast to the saved ones
|
|
*
|
|
* @return array Cleaned option
|
|
*/
|
|
protected function clean_option( $option_value, $current_version = null, $all_old_option_values = null ) {
|
|
|
|
/* Make sure the values of the variable option key options are cleaned as they
|
|
may be retained and would not be cleaned/validated then */
|
|
if ( is_array( $option_value ) && $option_value !== array() ) {
|
|
|
|
foreach ( $option_value as $key => $value ) {
|
|
$switch_key = $this->get_switch_key( $key );
|
|
|
|
// Similar to validation routine - any changes made there should be made here too
|
|
switch ( $switch_key ) {
|
|
case 'user_role-': /* 'user_role-' . $role_name. '-not_in_sitemap' fields */
|
|
case 'post_types-': /* 'post_types-' . $pt->name . '-not_in_sitemap' fields */
|
|
case 'taxonomies-': /* 'taxonomies-' . $tax->name . '-not_in_sitemap' fields */
|
|
$option_value[ $key ] = self::validate_bool( $value );
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
return $option_value;
|
|
}
|
|
|
|
|
|
} /* End of class WPSEO_Option_XML */
|
|
|
|
} /* End of class-exists wrapper */
|
|
|
|
|
|
/*******************************************************************
|
|
* Option: wpseo_social
|
|
*******************************************************************/
|
|
if ( ! class_exists( 'WPSEO_Option_Social' ) ) {
|
|
|
|
class WPSEO_Option_Social extends WPSEO_Option {
|
|
|
|
/**
|
|
* @var string option name
|
|
*/
|
|
public $option_name = 'wpseo_social';
|
|
|
|
/**
|
|
* @var array Array of defaults for the option
|
|
* Shouldn't be requested directly, use $this->get_defaults();
|
|
*/
|
|
protected $defaults = array(
|
|
// Non-form fields, set via procedural code in admin/pages/social.php
|
|
'fb_admins' => array(), // array of user id's => array( name => '', link => '' )
|
|
'fbapps' => array(), // array of linked fb apps id's => fb app display names
|
|
|
|
// Non-form field, set via translate_defaults() and validate_option() methods
|
|
'fbconnectkey' => '',
|
|
// Form fields:
|
|
'facebook_site' => '', // text field
|
|
'og_default_image' => '', // text field
|
|
'og_frontpage_title' => '', // text field
|
|
'og_frontpage_desc' => '', // text field
|
|
'og_frontpage_image' => '', // text field
|
|
'opengraph' => true,
|
|
'googleplus' => false,
|
|
'plus-publisher' => '', // text field
|
|
'twitter' => false,
|
|
'twitter_site' => '', // text field
|
|
'twitter_card_type' => 'summary',
|
|
// Form field, but not always available:
|
|
'fbadminapp' => 0, // app id from fbapps list
|
|
);
|
|
|
|
/**
|
|
* @var array Array of sub-options which should not be overloaded with multi-site defaults
|
|
*/
|
|
public $ms_exclude = array(
|
|
/* privacy */
|
|
'fb_admins',
|
|
'fbapps',
|
|
'fbconnectkey',
|
|
'fbadminapp',
|
|
);
|
|
|
|
|
|
/**
|
|
* @var array Array of allowed twitter card types
|
|
* While we only have the options summary and summary_large_image in the
|
|
* interface now, we might change that at some point.
|
|
*
|
|
* @internal Uncomment any of these to allow them in validation *and* automatically add them as a choice
|
|
* in the options page
|
|
*/
|
|
public static $twitter_card_types = array(
|
|
'summary' => '',
|
|
'summary_large_image' => '',
|
|
//'photo' => '',
|
|
//'gallery' => '',
|
|
//'app' => '',
|
|
//'player' => '',
|
|
//'product' => '',
|
|
);
|
|
|
|
|
|
/**
|
|
* Get the singleton instance of this class
|
|
*
|
|
* @return object
|
|
*/
|
|
public static function get_instance() {
|
|
if ( ! ( self::$instance instanceof self ) ) {
|
|
self::$instance = new self();
|
|
}
|
|
|
|
return self::$instance;
|
|
}
|
|
|
|
|
|
/**
|
|
* Translate/set strings used in the option defaults
|
|
*
|
|
* @return void
|
|
*/
|
|
public function translate_defaults() {
|
|
/* Auto-magically set the fb connect key */
|
|
$this->defaults['fbconnectkey'] = self::get_fbconnectkey();
|
|
|
|
self::$twitter_card_types['summary'] = __( 'Summary', 'wordpress-seo' );
|
|
self::$twitter_card_types['summary_large_image'] = __( 'Summary with large image', 'wordpress-seo' );
|
|
}
|
|
|
|
|
|
/**
|
|
* Get a Facebook connect key for the blog
|
|
*
|
|
* @static
|
|
* @return string
|
|
*/
|
|
public static function get_fbconnectkey() {
|
|
return md5( get_bloginfo( 'url' ) . rand() );
|
|
}
|
|
|
|
|
|
/**
|
|
* Validate the option
|
|
*
|
|
* @param array $dirty New value for the option
|
|
* @param array $clean Clean value for the option, normally the defaults
|
|
* @param array $old Old value of the option
|
|
*
|
|
* @return array Validated clean value for the option to be saved to the database
|
|
*/
|
|
protected function validate_option( $dirty, $clean, $old ) {
|
|
|
|
foreach ( $clean as $key => $value ) {
|
|
switch ( $key ) {
|
|
/* Automagic Facebook connect key */
|
|
case 'fbconnectkey':
|
|
if ( ( isset( $old[ $key ] ) && $old[ $key ] !== '' ) && preg_match( '`^[a-f0-9]{32}$`', $old[ $key ] ) > 0 ) {
|
|
$clean[ $key ] = $old[ $key ];
|
|
} else {
|
|
$clean[ $key ] = self::get_fbconnectkey();
|
|
}
|
|
break;
|
|
|
|
|
|
/* Will not always exist in form */
|
|
case 'fb_admins':
|
|
if ( isset( $dirty[ $key ] ) && is_array( $dirty[ $key ] ) ) {
|
|
if ( $dirty[ $key ] === array() ) {
|
|
$clean[ $key ] = array();
|
|
} else {
|
|
foreach ( $dirty[ $key ] as $user_id => $fb_array ) {
|
|
/* @todo [JRF/JRF => Yoast/whomever] add user_id validation -
|
|
* are these WP user-ids or FB user-ids ? Probably FB user-ids,
|
|
* if so, find out the rules for FB user-ids
|
|
*/
|
|
if ( is_array( $fb_array ) && $fb_array !== array() ) {
|
|
foreach ( $fb_array as $fb_key => $fb_value ) {
|
|
switch ( $fb_key ) {
|
|
case 'name':
|
|
/* @todo [JRF => whomever] add validation for name based
|
|
* on rules if there are any
|
|
* Input comes from: $_GET['userrealname'] */
|
|
$clean[ $key ][ $user_id ][ $fb_key ] = sanitize_text_field( $fb_value );
|
|
break;
|
|
|
|
case 'link':
|
|
$clean[ $key ][ $user_id ][ $fb_key ] = self::sanitize_url( $fb_value );
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
unset( $user_id, $fb_array, $fb_key, $fb_value );
|
|
}
|
|
} elseif ( isset( $old[ $key ] ) && is_array( $old[ $key ] ) ) {
|
|
$clean[ $key ] = $old[ $key ];
|
|
}
|
|
break;
|
|
|
|
|
|
/* Will not always exist in form */
|
|
case 'fbapps':
|
|
if ( isset( $dirty[ $key ] ) && is_array( $dirty[ $key ] ) ) {
|
|
if ( $dirty[ $key ] === array() ) {
|
|
$clean[ $key ] = array();
|
|
} else {
|
|
$clean[ $key ] = array();
|
|
foreach ( $dirty[ $key ] as $app_id => $display_name ) {
|
|
if ( ctype_digit( (string) $app_id ) !== false ) {
|
|
$clean[ $key ][ $app_id ] = sanitize_text_field( $display_name );
|
|
}
|
|
}
|
|
unset( $app_id, $display_name );
|
|
}
|
|
} elseif ( isset( $old[ $key ] ) && is_array( $old[ $key ] ) ) {
|
|
$clean[ $key ] = $old[ $key ];
|
|
}
|
|
break;
|
|
|
|
|
|
/* text fields */
|
|
case 'og_frontpage_desc':
|
|
case 'og_frontpage_title':
|
|
if ( isset( $dirty[ $key ] ) && $dirty[ $key ] !== '' ) {
|
|
$clean[ $key ] = self::sanitize_text_field( $dirty[ $key ] );
|
|
}
|
|
break;
|
|
|
|
|
|
/* url text fields - no ftp allowed */
|
|
case 'facebook_site':
|
|
case 'plus-publisher':
|
|
case 'og_default_image':
|
|
case 'og_frontpage_image':
|
|
if ( isset( $dirty[ $key ] ) && $dirty[ $key ] !== '' ) {
|
|
$url = self::sanitize_url( $dirty[ $key ] );
|
|
if ( $url !== '' ) {
|
|
$clean[ $key ] = $url;
|
|
} else {
|
|
if ( isset( $old[ $key ] ) && $old[ $key ] !== '' ) {
|
|
$url = self::sanitize_url( $old[ $key ] );
|
|
if ( $url !== '' ) {
|
|
$clean[ $key ] = $url;
|
|
}
|
|
}
|
|
if ( function_exists( 'add_settings_error' ) ) {
|
|
$url = self::sanitize_url( $dirty[ $key ] );
|
|
add_settings_error(
|
|
$this->group_name, // slug title of the setting
|
|
'_' . $key, // suffix-id for the error message box
|
|
sprintf( __( '%s does not seem to be a valid url. Please correct.', 'wordpress-seo' ), '<strong>' . esc_html( $url ) . '</strong>' ), // the error message
|
|
'error' // error type, either 'error' or 'updated'
|
|
);
|
|
}
|
|
}
|
|
unset( $url );
|
|
}
|
|
break;
|
|
|
|
|
|
/* twitter user name */
|
|
case 'twitter_site':
|
|
if ( isset( $dirty[ $key ] ) && $dirty[ $key ] !== '' ) {
|
|
$twitter_id = sanitize_text_field( ltrim( $dirty[ $key ], '@' ) );
|
|
/**
|
|
* From the Twitter documentation about twitter screen names:
|
|
* Typically a maximum of 15 characters long, but some historical accounts
|
|
* may exist with longer names.
|
|
* A username can only contain alphanumeric characters (letters A-Z, numbers 0-9)
|
|
* with the exception of underscores
|
|
* @link https://support.twitter.com/articles/101299-why-can-t-i-register-certain-usernames
|
|
* @link https://dev.twitter.com/docs/platform-objects/users
|
|
*/
|
|
if ( preg_match( '`^[A-Za-z0-9_]{1,25}$`', $twitter_id ) ) {
|
|
$clean[ $key ] = $twitter_id;
|
|
} else {
|
|
if ( isset( $old[ $key ] ) && $old[ $key ] !== '' ) {
|
|
$twitter_id = sanitize_text_field( ltrim( $old[ $key ], '@' ) );
|
|
if ( preg_match( '`^[A-Za-z0-9_]{1,25}$`', $twitter_id ) ) {
|
|
$clean[ $key ] = $twitter_id;
|
|
}
|
|
}
|
|
if ( function_exists( 'add_settings_error' ) ) {
|
|
add_settings_error(
|
|
$this->group_name, // slug title of the setting
|
|
'_' . $key, // suffix-id for the error message box
|
|
sprintf( __( '%s does not seem to be a valid Twitter user-id. Please correct.', 'wordpress-seo' ), '<strong>' . esc_html( sanitize_text_field( $dirty[ $key ] ) ) . '</strong>' ), // the error message
|
|
'error' // error type, either 'error' or 'updated'
|
|
);
|
|
}
|
|
}
|
|
unset( $twitter_id );
|
|
}
|
|
break;
|
|
|
|
case 'twitter_card_type':
|
|
if ( isset( $dirty[ $key ] ) && $dirty[ $key ] !== '' && isset( self::$twitter_card_types[ $dirty[ $key ] ] ) ) {
|
|
$clean[ $key ] = $dirty[ $key ];
|
|
}
|
|
break;
|
|
|
|
/* boolean fields */
|
|
case 'googleplus':
|
|
case 'opengraph':
|
|
case 'twitter':
|
|
$clean[ $key ] = ( isset( $dirty[ $key ] ) ? self::validate_bool( $dirty[ $key ] ) : false );
|
|
break;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Only validate 'fbadminapp' once we are sure that 'fbapps' has been validated already.
|
|
* Will not always exist in form - if not available it means that fbapps is empty,
|
|
* so leave the clean default.
|
|
*/
|
|
if ( ( isset( $dirty['fbadminapp'] ) && $dirty['fbadminapp'] != 0 ) && isset( $clean['fbapps'][ $dirty['fbadminapp'] ] ) ) {
|
|
$clean['fbadminapp'] = $dirty['fbadminapp'];
|
|
}
|
|
|
|
|
|
return $clean;
|
|
}
|
|
|
|
|
|
/**
|
|
* Clean a given option value
|
|
*
|
|
* @param array $option_value Old (not merged with defaults or filtered) option value to
|
|
* clean according to the rules for this option
|
|
* @param string $current_version (optional) Version from which to upgrade, if not set,
|
|
* version specific upgrades will be disregarded
|
|
* @param array $all_old_option_values (optional) Only used when importing old options to have
|
|
* access to the real old values, in contrast to the saved ones
|
|
*
|
|
* @return array Cleaned option
|
|
*/
|
|
protected function clean_option( $option_value, $current_version = null, $all_old_option_values = null ) {
|
|
|
|
/* Move options from very old option to this one */
|
|
$old_option = null;
|
|
if ( isset( $all_old_option_values ) ) {
|
|
// Ok, we have an import
|
|
if ( isset( $all_old_option_values['wpseo_indexation'] ) && is_array( $all_old_option_values['wpseo_indexation'] ) && $all_old_option_values['wpseo_indexation'] !== array() ) {
|
|
$old_option = $all_old_option_values['wpseo_indexation'];
|
|
}
|
|
} else {
|
|
$old_option = get_option( 'wpseo_indexation' );
|
|
}
|
|
|
|
if ( is_array( $old_option ) && $old_option !== array() ) {
|
|
$move = array(
|
|
'opengraph',
|
|
'fb_adminid',
|
|
'fb_appid',
|
|
);
|
|
foreach ( $move as $key ) {
|
|
if ( isset( $old_option[ $key ] ) && ! isset( $option_value[ $key ] ) ) {
|
|
$option_value[ $key ] = $old_option[ $key ];
|
|
}
|
|
}
|
|
unset( $move, $key );
|
|
}
|
|
unset( $old_option );
|
|
|
|
|
|
/* Clean some values which may not always be in form and may otherwise not be cleaned/validated */
|
|
if ( isset( $option_value['fbapps'] ) && ( is_array( $option_value['fbapps'] ) && $option_value['fbapps'] !== array() ) ) {
|
|
$fbapps = array();
|
|
foreach ( $option_value['fbapps'] as $app_id => $display_name ) {
|
|
if ( ctype_digit( (string) $app_id ) !== false ) {
|
|
$fbapps[ $app_id ] = sanitize_text_field( $display_name );
|
|
}
|
|
}
|
|
unset( $app_id, $display_name );
|
|
|
|
$option_value['fbapps'] = $fbapps;
|
|
}
|
|
|
|
return $option_value;
|
|
}
|
|
|
|
|
|
} /* End of class WPSEO_Option_Social */
|
|
|
|
} /* End of class-exists wrapper */
|
|
|
|
|
|
/*******************************************************************
|
|
* Option: wpseo_ms
|
|
*******************************************************************/
|
|
if ( is_multisite() && ! class_exists( 'WPSEO_Option_MS' ) ) {
|
|
|
|
/**
|
|
* Site option for Multisite installs only
|
|
*
|
|
* This class will not even be available/loaded if not on multisite, so none of the actions will
|
|
* register if not on multisite.
|
|
*
|
|
* Overloads a number of methods of the abstract class to ensure the use of the correct site_option
|
|
* WP functions.
|
|
*/
|
|
class WPSEO_Option_MS extends WPSEO_Option {
|
|
|
|
/**
|
|
* @var string option name
|
|
*/
|
|
public $option_name = 'wpseo_ms';
|
|
|
|
/**
|
|
* @var string option group name for use in settings forms
|
|
*/
|
|
public $group_name = 'yoast_wpseo_multisite_options';
|
|
|
|
/**
|
|
* @var bool whether to include the option in the return for WPSEO_Options::get_all()
|
|
*/
|
|
public $include_in_all = false;
|
|
|
|
/**
|
|
* @var bool whether this option is only for when the install is multisite
|
|
*/
|
|
public $multisite_only = true;
|
|
|
|
/**
|
|
* @var array Array of defaults for the option
|
|
* Shouldn't be requested directly, use $this->get_defaults();
|
|
*/
|
|
protected $defaults = array(
|
|
'access' => 'admin',
|
|
'defaultblog' => '', //numeric blog id or empty
|
|
);
|
|
|
|
/**
|
|
* @static
|
|
* @var array $allowed_access_options Available options for the 'access' setting
|
|
* Used for input validation
|
|
*
|
|
* @internal Important: Make sure the options added to the array here are in line with the keys
|
|
* for the options set for the select box in the admin/pages/network.php file
|
|
*/
|
|
public static $allowed_access_options = array(
|
|
'admin',
|
|
'superadmin',
|
|
);
|
|
|
|
|
|
/**
|
|
* Get the singleton instance of this class
|
|
*
|
|
* @return object
|
|
*/
|
|
public static function get_instance() {
|
|
if ( ! ( self::$instance instanceof self ) ) {
|
|
self::$instance = new self();
|
|
}
|
|
|
|
return self::$instance;
|
|
}
|
|
|
|
|
|
/**
|
|
* Add filters to make sure that the option default is returned if the option is not set
|
|
*
|
|
* @return void
|
|
*/
|
|
public function add_default_filters() {
|
|
// Don't change, needs to check for false as could return prio 0 which would evaluate to false
|
|
if ( has_filter( 'default_site_option_' . $this->option_name, array( $this, 'get_defaults' ) ) === false ) {
|
|
add_filter( 'default_site_option_' . $this->option_name, array( $this, 'get_defaults' ) );
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
* Remove the default filters.
|
|
* Called from the validate() method to prevent failure to add new options
|
|
*
|
|
* @return void
|
|
*/
|
|
public function remove_default_filters() {
|
|
remove_filter( 'default_site_option_' . $this->option_name, array( $this, 'get_defaults' ) );
|
|
}
|
|
|
|
|
|
/**
|
|
* Add filters to make sure that the option is merged with its defaults before being returned
|
|
*
|
|
* @return void
|
|
*/
|
|
public function add_option_filters() {
|
|
// Don't change, needs to check for false as could return prio 0 which would evaluate to false
|
|
if ( has_filter( 'site_option_' . $this->option_name, array( $this, 'get_option' ) ) === false ) {
|
|
add_filter( 'site_option_' . $this->option_name, array( $this, 'get_option' ) );
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
* Remove the option filters.
|
|
* Called from the clean_up methods to make sure we retrieve the original old option
|
|
*
|
|
* @return void
|
|
*/
|
|
public function remove_option_filters() {
|
|
remove_filter( 'site_option_' . $this->option_name, array( $this, 'get_option' ) );
|
|
}
|
|
|
|
|
|
/* *********** METHODS influencing add_uption(), update_option() and saving from admin pages *********** */
|
|
|
|
|
|
/**
|
|
* Validate the option
|
|
*
|
|
* @param array $dirty New value for the option
|
|
* @param array $clean Clean value for the option, normally the defaults
|
|
* @param array $old Old value of the option
|
|
*
|
|
* @return array Validated clean value for the option to be saved to the database
|
|
*/
|
|
protected function validate_option( $dirty, $clean, $old ) {
|
|
|
|
foreach ( $clean as $key => $value ) {
|
|
switch ( $key ) {
|
|
case 'access':
|
|
if ( isset( $dirty[ $key ] ) && in_array( $dirty[ $key ], self::$allowed_access_options, true ) ) {
|
|
$clean[ $key ] = $dirty[ $key ];
|
|
} elseif ( function_exists( 'add_settings_error' ) ) {
|
|
add_settings_error(
|
|
$this->group_name, // slug title of the setting
|
|
'_' . $key, // suffix-id for the error message box
|
|
sprintf( __( '%s is not a valid choice for who should be allowed access to the WP SEO settings. Value reset to the default.', 'wordpress-seo' ), esc_html( sanitize_text_field( $dirty[ $key ] ) ) ), // the error message
|
|
'error' // error type, either 'error' or 'updated'
|
|
);
|
|
}
|
|
break;
|
|
|
|
|
|
case 'defaultblog':
|
|
if ( isset( $dirty[ $key ] ) && ( $dirty[ $key ] !== '' && $dirty[ $key ] !== '-' ) ) {
|
|
$int = self::validate_int( $dirty[ $key ] );
|
|
if ( $int !== false && $int > 0 ) {
|
|
// Check if a valid blog number has been received
|
|
$exists = get_blog_details( $int, false );
|
|
if ( $exists && $exists->deleted == 0 ) {
|
|
$clean[ $key ] = $int;
|
|
} elseif ( function_exists( 'add_settings_error' ) ) {
|
|
add_settings_error(
|
|
$this->group_name, // slug title of the setting
|
|
'_' . $key, // suffix-id for the error message box
|
|
esc_html__( 'The default blog setting must be the numeric blog id of the blog you want to use as default.', 'wordpress-seo' ) . '<br>' . sprintf( esc_html__( 'This must be an existing blog. Blog %s does not exist or has been marked as deleted.', 'wordpress-seo' ), '<strong>' . esc_html( sanitize_text_field( $dirty[ $key ] ) ) . '</strong>' ), // the error message
|
|
'error' // error type, either 'error' or 'updated'
|
|
);
|
|
}
|
|
unset( $exists );
|
|
} elseif ( function_exists( 'add_settings_error' ) ) {
|
|
add_settings_error(
|
|
$this->group_name, // slug title of the setting
|
|
'_' . $key, // suffix-id for the error message box
|
|
esc_html__( 'The default blog setting must be the numeric blog id of the blog you want to use as default.', 'wordpress-seo' ) . '<br>' . esc_html__( 'No numeric value was received.', 'wordpress-seo' ), // the error message
|
|
'error' // error type, either 'error' or 'updated'
|
|
);
|
|
}
|
|
unset( $int );
|
|
}
|
|
break;
|
|
|
|
default:
|
|
$clean[ $key ] = ( isset( $dirty[ $key ] ) ? self::validate_bool( $dirty[ $key ] ) : false );
|
|
break;
|
|
}
|
|
}
|
|
|
|
return $clean;
|
|
}
|
|
|
|
|
|
/**
|
|
* Clean a given option value
|
|
*
|
|
* @param array $option_value Old (not merged with defaults or filtered) option value to
|
|
* clean according to the rules for this option
|
|
* @param string $current_version (optional) Version from which to upgrade, if not set,
|
|
* version specific upgrades will be disregarded
|
|
* @param array $all_old_option_values (optional) Only used when importing old options to have
|
|
* access to the real old values, in contrast to the saved ones
|
|
*
|
|
* @return array Cleaned option
|
|
*/
|
|
/*protected function clean_option( $option_value, $current_version = null, $all_old_option_values = null ) {
|
|
|
|
return $option_value;
|
|
}*/
|
|
|
|
|
|
} /* End of class WPSEO_Option_MS */
|
|
|
|
} /* End of class-exists wrapper */
|
|
|
|
|
|
/*******************************************************************
|
|
* Option: wpseo_taxonomy_meta
|
|
*******************************************************************/
|
|
if ( ! class_exists( 'WPSEO_Taxonomy_Meta' ) ) {
|
|
|
|
class WPSEO_Taxonomy_Meta extends WPSEO_Option {
|
|
|
|
/**
|
|
* @var string option name
|
|
*/
|
|
public $option_name = 'wpseo_taxonomy_meta';
|
|
|
|
/**
|
|
* @var bool whether to include the option in the return for WPSEO_Options::get_all()
|
|
*/
|
|
public $include_in_all = false;
|
|
|
|
/**
|
|
* @var array Array of defaults for the option
|
|
* Shouldn't be requested directly, use $this->get_defaults();
|
|
* @internal Important: in contrast to most defaults, the below array format is
|
|
* very bare. The real option is in the format [taxonomy_name][term_id][...]
|
|
* where [...] is any of the $defaults_per_term options shown below.
|
|
* This is of course taken into account in the below methods.
|
|
*/
|
|
protected $defaults = array();
|
|
|
|
|
|
/**
|
|
* @static
|
|
* @var string Option name - same as $option_name property, but now also available to static
|
|
* methods
|
|
*/
|
|
public static $name;
|
|
|
|
/**
|
|
* @static
|
|
* @var array Array of defaults for individual taxonomy meta entries
|
|
*/
|
|
public static $defaults_per_term = array(
|
|
'wpseo_title' => '',
|
|
'wpseo_desc' => '',
|
|
'wpseo_metakey' => '',
|
|
'wpseo_canonical' => '',
|
|
'wpseo_bctitle' => '',
|
|
'wpseo_noindex' => 'default',
|
|
'wpseo_sitemap_include' => '-',
|
|
);
|
|
|
|
/**
|
|
* @static
|
|
* @var array Available index options
|
|
* Used for form generation and input validation
|
|
* @internal Labels (translation) added on admin_init via WPSEO_Taxonomy::translate_meta_options()
|
|
*/
|
|
public static $no_index_options = array(
|
|
'default' => '',
|
|
'index' => '',
|
|
'noindex' => '',
|
|
);
|
|
|
|
/**
|
|
* @static
|
|
* @var array Available sitemap include options
|
|
* Used for form generation and input validation
|
|
* @internal Labels (translation) added on admin_init via WPSEO_Taxonomy::translate_meta_options()
|
|
*/
|
|
public static $sitemap_include_options = array(
|
|
'-' => '',
|
|
'always' => '',
|
|
'never' => '',
|
|
);
|
|
|
|
|
|
/**
|
|
* Add the actions and filters for the option
|
|
*
|
|
* @todo [JRF => testers] Check if the extra actions below would run into problems if an option
|
|
* is updated early on and if so, change the call to schedule these for a later action on add/update
|
|
* instead of running them straight away
|
|
*
|
|
* @return \WPSEO_Taxonomy_Meta
|
|
*/
|
|
protected function __construct() {
|
|
parent::__construct();
|
|
|
|
/* On succesfull update/add of the option, flush the W3TC cache */
|
|
add_action( 'add_option_' . $this->option_name, array( 'WPSEO_Options', 'flush_w3tc_cache' ) );
|
|
add_action( 'update_option_' . $this->option_name, array( 'WPSEO_Options', 'flush_w3tc_cache' ) );
|
|
}
|
|
|
|
|
|
/**
|
|
* Get the singleton instance of this class
|
|
*
|
|
* @return object
|
|
*/
|
|
public static function get_instance() {
|
|
if ( ! ( self::$instance instanceof self ) ) {
|
|
self::$instance = new self();
|
|
self::$name = self::$instance->option_name;
|
|
}
|
|
|
|
return self::$instance;
|
|
}
|
|
|
|
|
|
public function enrich_defaults() {
|
|
$extra_defaults_per_term = apply_filters( 'wpseo_add_extra_taxmeta_term_defaults', array() );
|
|
if ( is_array( $extra_defaults_per_term ) ) {
|
|
self::$defaults_per_term = array_merge( $extra_defaults_per_term, self::$defaults_per_term );
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
* Helper method - Combines a fixed array of default values with an options array
|
|
* while filtering out any keys which are not in the defaults array.
|
|
*
|
|
* @static
|
|
*
|
|
* @param string $option_key Option name of the option we're doing the merge for
|
|
* @param array $options (Optional) Current options
|
|
* - if not set, the option defaults for the $option_key will be returned.
|
|
*
|
|
* @return array Combined and filtered options array.
|
|
*/
|
|
/*public function array_filter_merge( $option_key, $options = null ) {
|
|
|
|
$defaults = $this->get_defaults( $option_key );
|
|
|
|
if ( ! isset( $options ) || $options === false ) {
|
|
return $defaults;
|
|
}
|
|
|
|
/ *
|
|
@internal Adding the defaults to all taxonomy terms each time the option is retrieved
|
|
will be quite inefficient if there are a lot of taxonomy terms
|
|
As long as taxonomy_meta is only retrieved via methods in this class, we shouldn't need this
|
|
|
|
$options = (array) $options;
|
|
$filtered = array();
|
|
|
|
if ( $options !== array() ) {
|
|
foreach ( $options as $taxonomy => $terms ) {
|
|
if ( is_array( $terms ) && $terms !== array() ) {
|
|
foreach ( $terms as $id => $term_meta ) {
|
|
foreach ( self::$defaults_per_term as $name => $default ) {
|
|
if ( isset( $options[ $taxonomy ][ $id ][ $name ] ) ) {
|
|
$filtered[ $taxonomy ][ $id ][ $name ] = $options[ $taxonomy ][ $id ][ $name ];
|
|
}
|
|
else {
|
|
$filtered[ $name ] = $default;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
unset( $taxonomy, $terms, $id, $term_meta, $name, $default );
|
|
}
|
|
// end of may be remove
|
|
|
|
return $filtered;
|
|
* /
|
|
|
|
return (array) $options;
|
|
}*/
|
|
|
|
|
|
/**
|
|
* Validate the option
|
|
*
|
|
* @param array $dirty New value for the option
|
|
* @param array $clean Clean value for the option, normally the defaults
|
|
* @param array $old Old value of the option
|
|
*
|
|
* @return array Validated clean value for the option to be saved to the database
|
|
*/
|
|
protected function validate_option( $dirty, $clean, $old ) {
|
|
|
|
/* Prevent complete validation (which can be expensive when there are lots of terms)
|
|
if only one item has changed and has already been validated */
|
|
if ( isset( $dirty['wpseo_already_validated'] ) && $dirty['wpseo_already_validated'] === true ) {
|
|
unset( $dirty['wpseo_already_validated'] );
|
|
|
|
return $dirty;
|
|
}
|
|
|
|
|
|
foreach ( $dirty as $taxonomy => $terms ) {
|
|
/* Don't validate taxonomy - may not be registered yet and we don't want to remove valid ones */
|
|
if ( is_array( $terms ) && $terms !== array() ) {
|
|
foreach ( $terms as $term_id => $meta_data ) {
|
|
/* Only validate term if the taxonomy exists */
|
|
if ( taxonomy_exists( $taxonomy ) && get_term_by( 'id', $term_id, $taxonomy ) === false ) {
|
|
/* Is this term id a special case ? */
|
|
if ( has_filter( 'wpseo_tax_meta_special_term_id_validation_' . $term_id ) !== false ) {
|
|
$clean[ $taxonomy ][ $term_id ] = apply_filters( 'wpseo_tax_meta_special_term_id_validation_' . $term_id, $meta_data, $taxonomy, $term_id );
|
|
}
|
|
continue;
|
|
}
|
|
|
|
if ( is_array( $meta_data ) && $meta_data !== array() ) {
|
|
/* Validate meta data */
|
|
$old_meta = self::get_term_meta( $term_id, $taxonomy );
|
|
$meta_data = self::validate_term_meta_data( $meta_data, $old_meta );
|
|
if ( $meta_data !== array() ) {
|
|
$clean[ $taxonomy ][ $term_id ] = $meta_data;
|
|
}
|
|
}
|
|
|
|
// Deal with special cases (for when taxonomy doesn't exist yet)
|
|
if ( ! isset( $clean[ $taxonomy ][ $term_id ] ) && has_filter( 'wpseo_tax_meta_special_term_id_validation_' . $term_id ) !== false ) {
|
|
$clean[ $taxonomy ][ $term_id ] = apply_filters( 'wpseo_tax_meta_special_term_id_validation_' . $term_id, $meta_data, $taxonomy, $term_id );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return $clean;
|
|
}
|
|
|
|
|
|
/**
|
|
* Validate the meta data for one individual term and removes default values (no need to save those)
|
|
*
|
|
* @static
|
|
*
|
|
* @param array $meta_data New values
|
|
* @param array $old_meta The original values
|
|
*
|
|
* @return array Validated and filtered value
|
|
*/
|
|
public static function validate_term_meta_data( $meta_data, $old_meta ) {
|
|
|
|
$clean = self::$defaults_per_term;
|
|
$meta_data = array_map( array( __CLASS__, 'trim_recursive' ), $meta_data );
|
|
|
|
if ( ! is_array( $meta_data ) || $meta_data === array() ) {
|
|
return $clean;
|
|
}
|
|
|
|
foreach ( $clean as $key => $value ) {
|
|
switch ( $key ) {
|
|
|
|
case 'wpseo_noindex':
|
|
if ( isset( $meta_data[ $key ] ) ) {
|
|
if ( isset( self::$no_index_options[ $meta_data[ $key ] ] ) ) {
|
|
$clean[ $key ] = $meta_data[ $key ];
|
|
}
|
|
} elseif ( isset( $old_meta[ $key ] ) ) {
|
|
// Retain old value if field currently not in use
|
|
$clean[ $key ] = $old_meta[ $key ];
|
|
}
|
|
break;
|
|
|
|
case 'wpseo_sitemap_include':
|
|
if ( isset( $meta_data[ $key ] ) && isset( self::$sitemap_include_options[ $meta_data[ $key ] ] ) ) {
|
|
$clean[ $key ] = $meta_data[ $key ];
|
|
}
|
|
break;
|
|
|
|
case 'wpseo_canonical':
|
|
if ( isset( $meta_data[ $key ] ) && $meta_data[ $key ] !== '' ) {
|
|
$url = self::sanitize_url( $meta_data[ $key ] );
|
|
if ( $url !== '' ) {
|
|
$clean[ $key ] = $url;
|
|
}
|
|
}
|
|
break;
|
|
|
|
case 'wpseo_metakey':
|
|
case 'wpseo_bctitle':
|
|
if ( isset( $meta_data[ $key ] ) ) {
|
|
$clean[ $key ] = self::sanitize_text_field( stripslashes( $meta_data[ $key ] ) );
|
|
} elseif ( isset( $old_meta[ $key ] ) ) {
|
|
// Retain old value if field currently not in use
|
|
$clean[ $key ] = $old_meta[ $key ];
|
|
}
|
|
break;
|
|
|
|
case 'wpseo_title':
|
|
case 'wpseo_desc':
|
|
default:
|
|
if ( isset( $meta_data[ $key ] ) && is_string( $meta_data[ $key ] ) ) {
|
|
$clean[ $key ] = self::sanitize_text_field( stripslashes( $meta_data[ $key ] ) );
|
|
}
|
|
break;
|
|
}
|
|
|
|
$clean[ $key ] = apply_filters( 'wpseo_sanitize_tax_meta_' . $key, $clean[ $key ], ( isset( $meta_data[ $key ] ) ? $meta_data[ $key ] : null ), ( isset( $old_meta[ $key ] ) ? $old_meta[ $key ] : null ) );
|
|
}
|
|
|
|
// Only save the non-default values
|
|
return array_diff_assoc( $clean, self::$defaults_per_term );
|
|
}
|
|
|
|
|
|
/**
|
|
* Clean a given option value
|
|
* - Convert old option values to new
|
|
* - Fixes strings which were escaped (should have been sanitized - escaping is for output)
|
|
*
|
|
* @param array $option_value Old (not merged with defaults or filtered) option value to
|
|
* clean according to the rules for this option
|
|
* @param string $current_version (optional) Version from which to upgrade, if not set,
|
|
* version specific upgrades will be disregarded
|
|
* @param array $all_old_option_values (optional) Only used when importing old options to have
|
|
* access to the real old values, in contrast to the saved ones
|
|
*
|
|
* @return array Cleaned option
|
|
*/
|
|
protected function clean_option( $option_value, $current_version = null, $all_old_option_values = null ) {
|
|
|
|
/* Clean up old values and remove empty arrays */
|
|
if ( is_array( $option_value ) && $option_value !== array() ) {
|
|
|
|
foreach ( $option_value as $taxonomy => $terms ) {
|
|
|
|
if ( is_array( $terms ) && $terms !== array() ) {
|
|
|
|
foreach ( $terms as $term_id => $meta_data ) {
|
|
if ( ! is_array( $meta_data ) || $meta_data === array() ) {
|
|
// Remove empty term arrays
|
|
unset( $option_value[ $taxonomy ][ $term_id ] );
|
|
} else {
|
|
foreach ( $meta_data as $key => $value ) {
|
|
|
|
switch ( $key ) {
|
|
case 'noindex':
|
|
if ( $value === 'on' ) {
|
|
// Convert 'on' to 'noindex'
|
|
$option_value[ $taxonomy ][ $term_id ][ $key ] = 'noindex';
|
|
}
|
|
break;
|
|
|
|
case 'canonical':
|
|
case 'wpseo_metakey':
|
|
case 'wpseo_bctitle':
|
|
case 'wpseo_title':
|
|
case 'wpseo_desc':
|
|
// @todo [JRF => whomever] needs checking, I don't have example data [JRF]
|
|
if ( $value !== '' ) {
|
|
// Fix incorrectly saved (encoded) canonical urls and texts
|
|
$option_value[ $taxonomy ][ $term_id ][ $key ] = wp_specialchars_decode( stripslashes( $value ), ENT_QUOTES );
|
|
}
|
|
break;
|
|
|
|
default:
|
|
// @todo [JRF => whomever] needs checking, I don't have example data [JRF]
|
|
if ( $value !== '' ) {
|
|
// Fix incorrectly saved (escaped) text strings
|
|
$option_value[ $taxonomy ][ $term_id ][ $key ] = wp_specialchars_decode( $value, ENT_QUOTES );
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
// Remove empty taxonomy arrays
|
|
unset( $option_value[ $taxonomy ] );
|
|
}
|
|
}
|
|
}
|
|
|
|
return $option_value;
|
|
}
|
|
|
|
|
|
/**
|
|
* Retrieve a taxonomy term's meta value(s).
|
|
*
|
|
* @static
|
|
*
|
|
* @param mixed $term Term to get the meta value for
|
|
* either (string) term name, (int) term id or (object) term
|
|
* @param string $taxonomy Name of the taxonomy to which the term is attached
|
|
* @param string $meta (optional) Meta value to get (without prefix)
|
|
*
|
|
* @return mixed|bool Value for the $meta if one is given, might be the default
|
|
* If no meta is given, an array of all the meta data for the term
|
|
* False if the term does not exist or the $meta provided is invalid
|
|
*/
|
|
public static function get_term_meta( $term, $taxonomy, $meta = null ) {
|
|
/* Figure out the term id */
|
|
if ( is_int( $term ) ) {
|
|
$term = get_term_by( 'id', $term, $taxonomy );
|
|
} elseif ( is_string( $term ) ) {
|
|
$term = get_term_by( 'slug', $term, $taxonomy );
|
|
}
|
|
|
|
if ( is_object( $term ) && isset( $term->term_id ) ) {
|
|
$term_id = $term->term_id;
|
|
} else {
|
|
return false;
|
|
}
|
|
|
|
|
|
$tax_meta = get_option( self::$name );
|
|
|
|
/* If we have data for the term, merge with defaults for complete array, otherwise set defaults */
|
|
if ( isset( $tax_meta[ $taxonomy ][ $term_id ] ) ) {
|
|
$tax_meta = array_merge( self::$defaults_per_term, $tax_meta[ $taxonomy ][ $term_id ] );
|
|
} else {
|
|
$tax_meta = self::$defaults_per_term;
|
|
}
|
|
|
|
/* Either return the complete array or a single value from it or false if the value does not exist
|
|
(shouldn't happen after merge with defaults, indicates typo in request) */
|
|
if ( ! isset( $meta ) ) {
|
|
return $tax_meta;
|
|
} else {
|
|
if ( isset( $tax_meta[ 'wpseo_' . $meta ] ) ) {
|
|
return $tax_meta[ 'wpseo_' . $meta ];
|
|
} else {
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
|
|
} /* End of class WPSEO_Taxonomy_Meta */
|
|
|
|
} /* End of class-exists wrapper */
|
|
|
|
|
|
/*******************************************************************
|
|
* Overall Option Management
|
|
*******************************************************************/
|
|
if ( ! class_exists( 'WPSEO_Options' ) ) {
|
|
/**
|
|
* Overal Option Management class
|
|
*
|
|
* Instantiates all the options and offers a number of utility methods to work with the options
|
|
*/
|
|
class WPSEO_Options {
|
|
|
|
|
|
/**
|
|
* @static
|
|
* @var array Options this class uses
|
|
* Array format: (string) option_name => (string) name of concrete class for the option
|
|
*/
|
|
public static $options = array(
|
|
'wpseo' => 'WPSEO_Option_Wpseo',
|
|
'wpseo_permalinks' => 'WPSEO_Option_Permalinks',
|
|
'wpseo_titles' => 'WPSEO_Option_Titles',
|
|
'wpseo_social' => 'WPSEO_Option_Social',
|
|
'wpseo_rss' => 'WPSEO_Option_RSS',
|
|
'wpseo_internallinks' => 'WPSEO_Option_InternalLinks',
|
|
'wpseo_xml' => 'WPSEO_Option_XML',
|
|
'wpseo_ms' => 'WPSEO_Option_MS',
|
|
'wpseo_taxonomy_meta' => 'WPSEO_Taxonomy_Meta',
|
|
);
|
|
|
|
protected static $option_instances;
|
|
|
|
/**
|
|
* @var object Instance of this class
|
|
*/
|
|
protected static $instance;
|
|
|
|
|
|
/**
|
|
* Instantiate all the WPSEO option management classes
|
|
*/
|
|
protected function __construct() {
|
|
foreach ( self::$options as $option_name => $option_class ) {
|
|
if ( class_exists( $option_class ) ) {
|
|
self::$option_instances[ $option_name ] = call_user_func( array( $option_class, 'get_instance' ) );
|
|
}
|
|
else {
|
|
unset( self::$options[ $option_name ] );
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Get the singleton instance of this class
|
|
*
|
|
* @return object
|
|
*/
|
|
public static function get_instance() {
|
|
if ( ! ( self::$instance instanceof self ) ) {
|
|
self::$instance = new self();
|
|
}
|
|
|
|
return self::$instance;
|
|
}
|
|
|
|
|
|
/**
|
|
* Check whether the current user is allowed to access the configuration.
|
|
*
|
|
* @todo [JRF => whomever] when someone would reorganize the classes, this should maybe
|
|
* be moved to a general WPSEO_Utils class. Obviously all calls to this method should be
|
|
* adjusted in that case.
|
|
*
|
|
* @return boolean
|
|
*/
|
|
public static function grant_access() {
|
|
if ( ! is_multisite() ) {
|
|
return true;
|
|
}
|
|
|
|
$options = get_site_option( 'wpseo_ms' );
|
|
if ( $options['access'] === 'admin' && current_user_can( 'manage_options' ) ) {
|
|
return true;
|
|
}
|
|
|
|
if ( $options['access'] === 'superadmin' && ! is_super_admin() ) {
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
/**
|
|
* Get the group name of an option for use in the settings form
|
|
*
|
|
* @param string $option_name the option for which you want to retrieve the option group name
|
|
*
|
|
* @return string|bool
|
|
*/
|
|
public static function get_group_name( $option_name ) {
|
|
if ( isset( self::$option_instances[ $option_name ] ) ) {
|
|
return self::$option_instances[ $option_name ]->group_name;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
|
|
/**
|
|
* Get a specific default value for an option
|
|
*
|
|
* @param string $option_name The option for which you want to retrieve a default
|
|
* @param string $key The key within the option who's default you want
|
|
*
|
|
* @return mixed
|
|
*/
|
|
public static function get_default( $option_name, $key ) {
|
|
if ( isset( self::$option_instances[ $option_name ] ) ) {
|
|
$defaults = self::$option_instances[ $option_name ]->get_defaults();
|
|
if ( isset( $defaults[ $key ] ) ) {
|
|
return $defaults[ $key ];
|
|
}
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
|
|
/**
|
|
* Update a site_option
|
|
*
|
|
* @param string $option_name The option name of the option to save
|
|
* @param mized $value The new value for the option
|
|
*
|
|
* @return bool
|
|
*/
|
|
public static function update_site_option( $option_name, $value ) {
|
|
if ( is_network_admin() && isset( self::$option_instances[ $option_name ] ) ) {
|
|
return self::$option_instances[ $option_name ]->update_site_option( $value );
|
|
}
|
|
else {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
* Get the instantiated option instance
|
|
*
|
|
* @param string $option_name The option for which you want to retrieve the instance
|
|
*
|
|
* @return object|bool
|
|
*/
|
|
public static function get_option_instance( $option_name ) {
|
|
if ( isset( self::$option_instances[ $option_name ] ) ) {
|
|
return self::$option_instances[ $option_name ];
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
|
|
/**
|
|
* Retrieve an array of the options which should be included in get_all() and reset().
|
|
*
|
|
* @static
|
|
* @return array Array of option names
|
|
*/
|
|
public static function get_option_names() {
|
|
static $option_names = array();
|
|
|
|
if ( $option_names === array() ) {
|
|
foreach ( self::$option_instances as $option_name => $option_object ) {
|
|
if ( $option_object->include_in_all === true ) {
|
|
$option_names[] = $option_name;
|
|
}
|
|
}
|
|
$option_names = apply_filters( 'wpseo_options', $option_names );
|
|
}
|
|
|
|
return $option_names;
|
|
}
|
|
|
|
|
|
/**
|
|
* Retrieve all the options for the SEO plugin in one go.
|
|
*
|
|
* @todo [JRF] see if we can get some extra efficiency for this one, though probably not as options may
|
|
* well change between calls (enriched defaults and such)
|
|
*
|
|
* @static
|
|
* @return array Array combining the values of (nearly) all the options
|
|
*/
|
|
public static function get_all() {
|
|
$all_options = array();
|
|
$option_names = self::get_option_names();
|
|
|
|
if ( is_array( $option_names ) && $option_names !== array() ) {
|
|
foreach ( $option_names as $option_name ) {
|
|
if ( self::$option_instances[ $option_name ]->multisite_only !== true ) {
|
|
$option = get_option( $option_name );
|
|
}
|
|
else {
|
|
$option = get_site_option( $option_name );
|
|
}
|
|
|
|
if ( is_array( $option ) && $option !== array() ) {
|
|
$all_options = array_merge( $all_options, $option );
|
|
}
|
|
}
|
|
}
|
|
|
|
return $all_options;
|
|
}
|
|
|
|
|
|
/**
|
|
* Run the clean up routine for one or all options
|
|
*
|
|
* @param array|string $option_name (optional) the option you want to clean or an array of
|
|
* option names for the options you want to clean.
|
|
* If not set, all options will be cleaned
|
|
* @param string $current_version (optional) Version from which to upgrade, if not set,
|
|
* version specific upgrades will be disregarded
|
|
*
|
|
* @return void
|
|
*/
|
|
public static function clean_up( $option_name = null, $current_version = null ) {
|
|
if ( isset( $option_name ) && is_string( $option_name ) && $option_name !== '' ) {
|
|
if ( isset( self::$option_instances[ $option_name ] ) ) {
|
|
self::$option_instances[ $option_name ]->clean( $current_version );
|
|
}
|
|
} elseif ( isset( $option_name ) && is_array( $option_name ) && $option_name !== array() ) {
|
|
foreach ( $option_name as $option ) {
|
|
if ( isset( self::$option_instances[ $option ] ) ) {
|
|
self::$option_instances[ $option ]->clean( $current_version );
|
|
}
|
|
}
|
|
} else {
|
|
foreach ( self::$option_instances as $instance ) {
|
|
$instance->clean( $current_version );
|
|
}
|
|
|
|
// If we've done a full clean-up, we can safely remove this really old option
|
|
delete_option( 'wpseo_indexation' );
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
* Check that all options exist in the database and add any which don't
|
|
*
|
|
* @return void
|
|
*/
|
|
public static function ensure_options_exist() {
|
|
foreach ( self::$option_instances as $instance ) {
|
|
$instance->maybe_add_option();
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
* Correct the inadvertent removal of the fallback to default values from the breadcrumbs
|
|
*
|
|
* @since 1.5.2.3
|
|
*/
|
|
public static function bring_back_breadcrumb_defaults() {
|
|
if ( isset( self::$option_instances['wpseo_internallinks'] ) ) {
|
|
self::$option_instances['wpseo_internallinks']->bring_back_defaults();
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
* Initialize some options on first install/activate/reset
|
|
*
|
|
* @static
|
|
* @return void
|
|
*/
|
|
public static function initialize() {
|
|
/* Make sure title_test and description_test function are available even when called
|
|
from the isolated activation */
|
|
require_once( WPSEO_PATH . 'inc/wpseo-non-ajax-functions.php' );
|
|
|
|
// wpseo_title_test();
|
|
wpseo_description_test();
|
|
|
|
/* Force WooThemes to use WordPress SEO data. */
|
|
if ( function_exists( 'woo_version_init' ) ) {
|
|
update_option( 'seo_woo_use_third_party_data', 'true' );
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
* Reset all options to their default values and rerun some tests
|
|
*
|
|
* @static
|
|
* @return void
|
|
*/
|
|
public static function reset() {
|
|
if ( ! is_multisite() ) {
|
|
$option_names = self::get_option_names();
|
|
if ( is_array( $option_names ) && $option_names !== array() ) {
|
|
foreach ( $option_names as $option_name ) {
|
|
delete_option( $option_name );
|
|
update_option( $option_name, get_option( $option_name ) );
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
// Reset MS blog based on network default blog setting
|
|
self::reset_ms_blog( get_current_blog_id() );
|
|
}
|
|
|
|
self::initialize();
|
|
}
|
|
|
|
|
|
/**
|
|
* Initialize default values for a new multisite blog
|
|
*
|
|
* @static
|
|
*
|
|
* @param bool $force_init Whether to always do the initialization routine (title/desc test)
|
|
* @return void
|
|
*/
|
|
public static function maybe_set_multisite_defaults( $force_init = false ) {
|
|
$option = get_option( 'wpseo' );
|
|
|
|
if ( is_multisite() ) {
|
|
if ( $option['ms_defaults_set'] === false ) {
|
|
self::reset_ms_blog( get_current_blog_id() );
|
|
self::initialize();
|
|
}
|
|
else if ( $force_init === true ) {
|
|
self::initialize();
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
* Reset all options for a specific multisite blog to their default values based upon a
|
|
* specified default blog if one was chosen on the network page or the plugin defaults if it was not
|
|
*
|
|
* @static
|
|
*
|
|
* @param int|string $blog_id Blog id of the blog for which to reset the options
|
|
*
|
|
* @return void
|
|
*/
|
|
public static function reset_ms_blog( $blog_id ) {
|
|
if ( is_multisite() ) {
|
|
$options = get_site_option( 'wpseo_ms' );
|
|
$option_names = self::get_option_names();
|
|
|
|
if ( is_array( $option_names ) && $option_names !== array() ) {
|
|
$base_blog_id = $blog_id;
|
|
if ( $options['defaultblog'] !== '' && $options['defaultblog'] != 0 ) {
|
|
$base_blog_id = $options['defaultblog'];
|
|
}
|
|
|
|
foreach ( $option_names as $option_name ) {
|
|
delete_blog_option( $blog_id, $option_name );
|
|
|
|
$new_option = get_blog_option( $base_blog_id, $option_name );
|
|
|
|
/* Remove sensitive, theme dependent and site dependent info */
|
|
if ( isset( self::$option_instances[ $option_name ] ) && self::$option_instances[ $option_name ]->ms_exclude !== array() ) {
|
|
foreach ( self::$option_instances[ $option_name ]->ms_exclude as $key ) {
|
|
unset( $new_option[ $key ] );
|
|
}
|
|
}
|
|
|
|
if ( $option_name === 'wpseo' ) {
|
|
$new_option['ms_defaults_set'] = true;
|
|
}
|
|
|
|
update_blog_option( $blog_id, $option_name, $new_option );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/* ************** METHODS FOR ACTIONS TO TAKE ON CERTAIN OPTION UPDATES ****************/
|
|
|
|
/**
|
|
* (Un-)schedule the yoast tracking cronjob if the tracking option has changed
|
|
*
|
|
* @internal Better to be done here, rather than in the Yoast_Tracking class as
|
|
* class-tracking.php may not be loaded and might not need to be (lean loading).
|
|
*
|
|
* @todo [JRF => whomever] when someone would reorganize the classes, this should maybe
|
|
* be moved to a general WPSEO_Utils class. Obviously all calls to this method should be
|
|
* adjusted in that case.
|
|
*
|
|
* @todo - [JRF => Yoast] check if this has any impact on other Yoast plugins which may
|
|
* use the same tracking schedule hook. If so, maybe get any other yoast plugin options,
|
|
* check for the tracking status and unschedule based on the combined status.
|
|
*
|
|
* @static
|
|
*
|
|
* @param mixed $disregard Not needed - passed by add/update_option action call
|
|
* Option name if option was added, old value if option was updated
|
|
* @param array $value The (new/current) value of the wpseo option
|
|
* @param bool $force_unschedule Whether to force an unschedule (i.e. on deactivate)
|
|
*
|
|
* @return void
|
|
*/
|
|
public static function schedule_yoast_tracking( $disregard, $value, $force_unschedule = false ) {
|
|
$current_schedule = wp_next_scheduled( 'yoast_tracking' );
|
|
|
|
if ( $force_unschedule !== true && ( $value['yoast_tracking'] === true && $current_schedule === false ) ) {
|
|
// The tracking checks daily, but only sends new data every 7 days.
|
|
wp_schedule_event( time(), 'daily', 'yoast_tracking' );
|
|
} elseif ( $force_unschedule === true || ( $value['yoast_tracking'] === false && $current_schedule !== false ) ) {
|
|
wp_clear_scheduled_hook( 'yoast_tracking' );
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
* Clears the WP or W3TC cache depending on which is used
|
|
*
|
|
* @todo [JRF => whomever] when someone would reorganize the classes, this should maybe
|
|
* be moved to a general WPSEO_Utils class. Obviously all calls to this method should be
|
|
* adjusted in that case.
|
|
*
|
|
* @static
|
|
* @return void
|
|
*/
|
|
public static function clear_cache() {
|
|
if ( function_exists( 'w3tc_pgcache_flush' ) ) {
|
|
w3tc_pgcache_flush();
|
|
} elseif ( function_exists( 'wp_cache_clear_cache' ) ) {
|
|
wp_cache_clear_cache();
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
* Flush W3TC cache after succesfull update/add of taxonomy meta option
|
|
*
|
|
* @todo [JRF => whomever] when someone would reorganize the classes, this should maybe
|
|
* be moved to a general WPSEO_Utils class. Obviously all calls to this method should be
|
|
* adjusted in that case.
|
|
*
|
|
* @todo [JRF => whomever] check the above and this function to see if they should be combined or really
|
|
* do something significantly different
|
|
*
|
|
* @static
|
|
* @return void
|
|
*/
|
|
public static function flush_w3tc_cache() {
|
|
if ( defined( 'W3TC_DIR' ) && function_exists( 'w3tc_objectcache_flush' ) ) {
|
|
w3tc_objectcache_flush();
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
* Clear rewrite rules
|
|
*
|
|
* @todo [JRF => whomever] when someone would reorganize the classes, this should maybe
|
|
* be moved to a general WPSEO_Utils class. Obviously all calls to this method should be
|
|
* adjusted in that case.
|
|
*
|
|
* @static
|
|
* @return void
|
|
*/
|
|
public static function clear_rewrites() {
|
|
delete_option( 'rewrite_rules' );
|
|
}
|
|
|
|
|
|
} /* End of class WPSEO_Options */
|
|
|
|
} /* End of class-exists wrapper */
|