--- a/wp/wp-includes/theme.php Tue Oct 22 16:11:46 2019 +0200
+++ b/wp/wp-includes/theme.php Tue Dec 15 13:49:49 2020 +0100
@@ -15,18 +15,20 @@
* @since 3.4.0
*
* @global array $wp_theme_directories
- * @staticvar array $_themes
*
* @param array $args {
* Optional. The search arguments.
*
- * @type mixed $errors True to return themes with errors, false to return themes without errors, null to return all themes.
- * Defaults to false.
- * @type mixed $allowed (Multisite) True to return only allowed themes for a site. False to return only disallowed themes for a site.
- * 'site' to return only site-allowed themes. 'network' to return only network-allowed themes.
- * Null to return all themes. Defaults to null.
- * @type int $blog_id (Multisite) The blog ID used to calculate which themes are allowed.
- * Defaults to 0, synonymous for the current blog.
+ * @type mixed $errors True to return themes with errors, false to return
+ * themes without errors, null to return all themes.
+ * Default false.
+ * @type mixed $allowed (Multisite) True to return only allowed themes for a site.
+ * False to return only disallowed themes for a site.
+ * 'site' to return only site-allowed themes.
+ * 'network' to return only network-allowed themes.
+ * Null to return all themes. Default null.
+ * @type int $blog_id (Multisite) The blog ID used to calculate which themes
+ * are allowed. Default 0, synonymous for the current blog.
* }
* @return WP_Theme[] Array of WP_Theme objects.
*/
@@ -48,7 +50,7 @@
$current_theme = get_stylesheet();
if ( isset( $theme_directories[ $current_theme ] ) ) {
$root_of_current_theme = get_raw_theme_root( $current_theme );
- if ( ! in_array( $root_of_current_theme, $wp_theme_directories ) ) {
+ if ( ! in_array( $root_of_current_theme, $wp_theme_directories, true ) ) {
$root_of_current_theme = WP_CONTENT_DIR . $root_of_current_theme;
}
$theme_directories[ $current_theme ]['theme_root'] = $root_of_current_theme;
@@ -79,7 +81,9 @@
if ( isset( $_themes[ $theme_root['theme_root'] . '/' . $theme ] ) ) {
$themes[ $theme ] = $_themes[ $theme_root['theme_root'] . '/' . $theme ];
} else {
- $themes[ $theme ] = $_themes[ $theme_root['theme_root'] . '/' . $theme ] = new WP_Theme( $theme, $theme_root['theme_root'] );
+ $themes[ $theme ] = new WP_Theme( $theme, $theme_root['theme_root'] );
+
+ $_themes[ $theme_root['theme_root'] . '/' . $theme ] = $themes[ $theme ];
}
}
@@ -101,12 +105,14 @@
*
* @global array $wp_theme_directories
*
- * @param string $stylesheet Directory name for the theme. Optional. Defaults to current theme.
- * @param string $theme_root Absolute path of the theme root to look in. Optional. If not specified, get_raw_theme_root()
- * is used to calculate the theme root for the $stylesheet provided (or current theme).
- * @return WP_Theme Theme object. Be sure to check the object's exists() method if you need to confirm the theme's existence.
+ * @param string $stylesheet Optional. Directory name for the theme. Defaults to current theme.
+ * @param string $theme_root Optional. Absolute path of the theme root to look in.
+ * If not specified, get_raw_theme_root() is used to calculate
+ * the theme root for the $stylesheet provided (or current theme).
+ * @return WP_Theme Theme object. Be sure to check the object's exists() method
+ * if you need to confirm the theme's existence.
*/
-function wp_get_theme( $stylesheet = null, $theme_root = null ) {
+function wp_get_theme( $stylesheet = '', $theme_root = '' ) {
global $wp_theme_directories;
if ( empty( $stylesheet ) ) {
@@ -117,7 +123,7 @@
$theme_root = get_raw_theme_root( $stylesheet );
if ( false === $theme_root ) {
$theme_root = WP_CONTENT_DIR . '/themes';
- } elseif ( ! in_array( $theme_root, (array) $wp_theme_directories ) ) {
+ } elseif ( ! in_array( $theme_root, (array) $wp_theme_directories, true ) ) {
$theme_root = WP_CONTENT_DIR . $theme_root;
}
}
@@ -129,7 +135,7 @@
* Clears the cache held by get_theme_roots() and WP_Theme.
*
* @since 3.5.0
- * @param bool $clear_update_cache Whether to clear the Theme updates cache
+ * @param bool $clear_update_cache Whether to clear the theme updates cache.
*/
function wp_clean_themes_cache( $clear_update_cache = true ) {
if ( $clear_update_cache ) {
@@ -146,20 +152,19 @@
*
* @since 3.0.0
*
- * @return bool true if a child theme is in use, false otherwise.
+ * @return bool True if a child theme is in use, false otherwise.
*/
function is_child_theme() {
return ( TEMPLATEPATH !== STYLESHEETPATH );
}
/**
- * Retrieve name of the current stylesheet.
+ * Retrieves name of the current stylesheet.
*
- * The theme name that the administrator has currently set the front end theme
- * as.
+ * The theme name that is currently set as the front end theme.
*
- * For all intents and purposes, the template name and the stylesheet name are
- * going to be the same for most cases.
+ * For all intents and purposes, the template name and the stylesheet name
+ * are going to be the same for most cases.
*
* @since 1.5.0
*
@@ -177,11 +182,11 @@
}
/**
- * Retrieve stylesheet directory path for current theme.
+ * Retrieves stylesheet directory path for current theme.
*
* @since 1.5.0
*
- * @return string Path to current theme directory.
+ * @return string Path to current theme's stylesheet directory.
*/
function get_stylesheet_directory() {
$stylesheet = get_stylesheet();
@@ -201,11 +206,11 @@
}
/**
- * Retrieve stylesheet directory URI.
+ * Retrieves stylesheet directory URI for current theme.
*
* @since 1.5.0
*
- * @return string
+ * @return string URI to current theme's stylesheet directory.
*/
function get_stylesheet_directory_uri() {
$stylesheet = str_replace( '%2F', '/', rawurlencode( get_stylesheet() ) );
@@ -225,14 +230,14 @@
}
/**
- * Retrieves the URI of current theme stylesheet.
+ * Retrieves stylesheet URI for current theme.
*
* The stylesheet file name is 'style.css' which is appended to the stylesheet directory URI path.
* See get_stylesheet_directory_uri().
*
* @since 1.5.0
*
- * @return string
+ * @return string URI to current theme's stylesheet.
*/
function get_stylesheet_uri() {
$stylesheet_dir_uri = get_stylesheet_directory_uri();
@@ -265,9 +270,9 @@
*
* @since 2.1.0
*
- * @global WP_Locale $wp_locale
+ * @global WP_Locale $wp_locale WordPress date and time locale object.
*
- * @return string
+ * @return string URI to current theme's localized stylesheet.
*/
function get_locale_stylesheet_uri() {
global $wp_locale;
@@ -293,7 +298,7 @@
}
/**
- * Retrieve name of the current theme.
+ * Retrieves name of the current theme.
*
* @since 1.5.0
*
@@ -311,11 +316,11 @@
}
/**
- * Retrieve current theme directory.
+ * Retrieves template directory path for current theme.
*
* @since 1.5.0
*
- * @return string Template directory path.
+ * @return string Path to current theme's template directory.
*/
function get_template_directory() {
$template = get_template();
@@ -327,7 +332,7 @@
*
* @since 1.5.0
*
- * @param string $template_dir The URI of the current theme directory.
+ * @param string $template_dir The path of the current theme directory.
* @param string $template Directory name of the current theme.
* @param string $theme_root Absolute path to the themes directory.
*/
@@ -335,11 +340,11 @@
}
/**
- * Retrieve theme directory URI.
+ * Retrieves template directory URI for current theme.
*
* @since 1.5.0
*
- * @return string Template directory URI.
+ * @return string URI to current theme's template directory.
*/
function get_template_directory_uri() {
$template = str_replace( '%2F', '/', rawurlencode( get_template() ) );
@@ -359,13 +364,14 @@
}
/**
- * Retrieve theme roots.
+ * Retrieves theme roots.
*
* @since 2.9.0
*
* @global array $wp_theme_directories
*
- * @return array|string An array of theme roots keyed by template/stylesheet or a single theme root if all themes have the same root.
+ * @return array|string An array of theme roots keyed by template/stylesheet
+ * or a single theme root if all themes have the same root.
*/
function get_theme_roots() {
global $wp_theme_directories;
@@ -383,22 +389,24 @@
}
/**
- * Register a directory that contains themes.
+ * Registers a directory that contains themes.
*
* @since 2.9.0
*
* @global array $wp_theme_directories
*
- * @param string $directory Either the full filesystem path to a theme folder or a folder within WP_CONTENT_DIR
- * @return bool
+ * @param string $directory Either the full filesystem path to a theme folder
+ * or a folder within WP_CONTENT_DIR.
+ * @return bool True if successfully registered a directory that contains themes,
+ * false if the directory does not exist.
*/
function register_theme_directory( $directory ) {
global $wp_theme_directories;
if ( ! file_exists( $directory ) ) {
- // Try prepending as the theme directory could be relative to the content directory
+ // Try prepending as the theme directory could be relative to the content directory.
$directory = WP_CONTENT_DIR . '/' . $directory;
- // If this directory does not exist, return and do not register
+ // If this directory does not exist, return and do not register.
if ( ! file_exists( $directory ) ) {
return false;
}
@@ -409,7 +417,7 @@
}
$untrailed = untrailingslashit( $directory );
- if ( ! empty( $untrailed ) && ! in_array( $untrailed, $wp_theme_directories ) ) {
+ if ( ! empty( $untrailed ) && ! in_array( $untrailed, $wp_theme_directories, true ) ) {
$wp_theme_directories[] = $untrailed;
}
@@ -417,15 +425,14 @@
}
/**
- * Search all registered theme directories for complete and valid themes.
+ * Searches all registered theme directories for complete and valid themes.
*
* @since 2.9.0
*
* @global array $wp_theme_directories
- * @staticvar array $found_themes
*
- * @param bool $force Optional. Whether to force a new directory scan. Defaults to false.
- * @return array|false Valid themes found
+ * @param bool $force Optional. Whether to force a new directory scan. Default false.
+ * @return array|false Valid themes found on success, false on failure.
*/
function search_theme_directories( $force = false ) {
global $wp_theme_directories;
@@ -444,9 +451,11 @@
$wp_theme_directories = (array) $wp_theme_directories;
$relative_theme_roots = array();
- // Set up maybe-relative, maybe-absolute array of theme directories.
- // We always want to return absolute, but we need to cache relative
- // to use in get_theme_root().
+ /*
+ * Set up maybe-relative, maybe-absolute array of theme directories.
+ * We always want to return absolute, but we need to cache relative
+ * to use in get_theme_root().
+ */
foreach ( $wp_theme_directories as $theme_root ) {
if ( 0 === strpos( $theme_root, WP_CONTENT_DIR ) ) {
$relative_theme_roots[ str_replace( WP_CONTENT_DIR, '', $theme_root ) ] = $theme_root;
@@ -461,9 +470,11 @@
* @since 3.4.0
*
* @param bool $cache_expiration Whether to get the cache of the theme directories. Default false.
- * @param string $cache_directory Directory to be searched for the cache.
+ * @param string $context The class or function name calling the filter.
*/
- if ( $cache_expiration = apply_filters( 'wp_cache_themes_persistently', false, 'search_theme_directories' ) ) {
+ $cache_expiration = apply_filters( 'wp_cache_themes_persistently', false, 'search_theme_directories' );
+
+ if ( $cache_expiration ) {
$cached_roots = get_site_transient( 'theme_roots' );
if ( is_array( $cached_roots ) ) {
foreach ( $cached_roots as $theme_dir => $theme_root ) {
@@ -479,10 +490,10 @@
return $found_themes;
}
if ( ! is_int( $cache_expiration ) ) {
- $cache_expiration = 1800; // half hour
+ $cache_expiration = 30 * MINUTE_IN_SECONDS;
}
} else {
- $cache_expiration = 1800; // half hour
+ $cache_expiration = 30 * MINUTE_IN_SECONDS;
}
/* Loop the registered theme directories and extract all themes */
@@ -495,12 +506,12 @@
continue;
}
foreach ( $dirs as $dir ) {
- if ( ! is_dir( $theme_root . '/' . $dir ) || $dir[0] == '.' || $dir == 'CVS' ) {
+ if ( ! is_dir( $theme_root . '/' . $dir ) || '.' === $dir[0] || 'CVS' === $dir ) {
continue;
}
if ( file_exists( $theme_root . '/' . $dir . '/style.css' ) ) {
// wp-content/themes/a-single-theme
- // wp-content/themes is $theme_root, a-single-theme is $dir
+ // wp-content/themes is $theme_root, a-single-theme is $dir.
$found_themes[ $dir ] = array(
'theme_file' => $dir . '/style.css',
'theme_root' => $theme_root,
@@ -508,14 +519,14 @@
} else {
$found_theme = false;
// wp-content/themes/a-folder-of-themes/*
- // wp-content/themes is $theme_root, a-folder-of-themes is $dir, then themes are $sub_dirs
+ // wp-content/themes is $theme_root, a-folder-of-themes is $dir, then themes are $sub_dirs.
$sub_dirs = @ scandir( $theme_root . '/' . $dir );
if ( ! $sub_dirs ) {
trigger_error( "$theme_root/$dir is not readable", E_USER_NOTICE );
continue;
}
foreach ( $sub_dirs as $sub_dir ) {
- if ( ! is_dir( $theme_root . '/' . $dir . '/' . $sub_dir ) || $dir[0] == '.' || $dir == 'CVS' ) {
+ if ( ! is_dir( $theme_root . '/' . $dir . '/' . $sub_dir ) || '.' === $dir[0] || 'CVS' === $dir ) {
continue;
}
if ( ! file_exists( $theme_root . '/' . $dir . '/' . $sub_dir . '/style.css' ) ) {
@@ -548,7 +559,7 @@
$theme_roots[ $theme_dir ] = $relative_theme_roots[ $theme_data['theme_root'] ]; // Convert absolute to relative.
}
- if ( $theme_roots != get_site_transient( 'theme_roots' ) ) {
+ if ( get_site_transient( 'theme_roots' ) != $theme_roots ) {
set_site_transient( 'theme_roots', $theme_roots, $cache_expiration );
}
@@ -556,7 +567,7 @@
}
/**
- * Retrieve path to themes directory.
+ * Retrieves path to themes directory.
*
* Does not have trailing slash.
*
@@ -564,19 +575,27 @@
*
* @global array $wp_theme_directories
*
- * @param string $stylesheet_or_template The stylesheet or template name of the theme
- * @return string Theme path.
+ * @param string $stylesheet_or_template Optional. The stylesheet or template name of the theme.
+ * Default is to leverage the main theme root.
+ * @return string Themes directory path.
*/
-function get_theme_root( $stylesheet_or_template = false ) {
+function get_theme_root( $stylesheet_or_template = '' ) {
global $wp_theme_directories;
- if ( $stylesheet_or_template && $theme_root = get_raw_theme_root( $stylesheet_or_template ) ) {
- // Always prepend WP_CONTENT_DIR unless the root currently registered as a theme directory.
- // This gives relative theme roots the benefit of the doubt when things go haywire.
- if ( ! in_array( $theme_root, (array) $wp_theme_directories ) ) {
- $theme_root = WP_CONTENT_DIR . $theme_root;
+ $theme_root = '';
+
+ if ( $stylesheet_or_template ) {
+ $theme_root = get_raw_theme_root( $stylesheet_or_template );
+ if ( $theme_root ) {
+ // Always prepend WP_CONTENT_DIR unless the root currently registered as a theme directory.
+ // This gives relative theme roots the benefit of the doubt when things go haywire.
+ if ( ! in_array( $theme_root, (array) $wp_theme_directories, true ) ) {
+ $theme_root = WP_CONTENT_DIR . $theme_root;
+ }
}
- } else {
+ }
+
+ if ( ! $theme_root ) {
$theme_root = WP_CONTENT_DIR . '/themes';
}
@@ -591,7 +610,7 @@
}
/**
- * Retrieve URI for themes directory.
+ * Retrieves URI for themes directory.
*
* Does not have trailing slash.
*
@@ -601,11 +620,11 @@
*
* @param string $stylesheet_or_template Optional. The stylesheet or template name of the theme.
* Default is to leverage the main theme root.
- * @param string $theme_root Optional. The theme root for which calculations will be based, preventing
- * the need for a get_raw_theme_root() call.
- * @return string Themes URI.
+ * @param string $theme_root Optional. The theme root for which calculations will be based,
+ * preventing the need for a get_raw_theme_root() call. Default empty.
+ * @return string Themes directory URI.
*/
-function get_theme_root_uri( $stylesheet_or_template = false, $theme_root = false ) {
+function get_theme_root_uri( $stylesheet_or_template = '', $theme_root = '' ) {
global $wp_theme_directories;
if ( $stylesheet_or_template && ! $theme_root ) {
@@ -613,7 +632,7 @@
}
if ( $stylesheet_or_template && $theme_root ) {
- if ( in_array( $theme_root, (array) $wp_theme_directories ) ) {
+ if ( in_array( $theme_root, (array) $wp_theme_directories, true ) ) {
// Absolute path. Make an educated guess. YMMV -- but note the filter below.
if ( 0 === strpos( $theme_root, WP_CONTENT_DIR ) ) {
$theme_root_uri = content_url( str_replace( WP_CONTENT_DIR, '', $theme_root ) );
@@ -638,22 +657,22 @@
*
* @param string $theme_root_uri The URI for themes directory.
* @param string $siteurl WordPress web address which is set in General Options.
- * @param string $stylesheet_or_template Stylesheet or template name of the theme.
+ * @param string $stylesheet_or_template The stylesheet or template name of the theme.
*/
return apply_filters( 'theme_root_uri', $theme_root_uri, get_option( 'siteurl' ), $stylesheet_or_template );
}
/**
- * Get the raw theme root relative to the content directory with no filters applied.
+ * Gets the raw theme root relative to the content directory with no filters applied.
*
* @since 3.1.0
*
* @global array $wp_theme_directories
*
- * @param string $stylesheet_or_template The stylesheet or template name of the theme
+ * @param string $stylesheet_or_template The stylesheet or template name of the theme.
* @param bool $skip_cache Optional. Whether to skip the cache.
* Defaults to false, meaning the cache is used.
- * @return string Theme root
+ * @return string Theme root.
*/
function get_raw_theme_root( $stylesheet_or_template, $skip_cache = false ) {
global $wp_theme_directories;
@@ -664,7 +683,7 @@
$theme_root = false;
- // If requesting the root for the current theme, consult options to avoid calling get_theme_roots()
+ // If requesting the root for the current theme, consult options to avoid calling get_theme_roots().
if ( ! $skip_cache ) {
if ( get_option( 'stylesheet' ) == $stylesheet_or_template ) {
$theme_root = get_option( 'stylesheet_root' );
@@ -684,7 +703,7 @@
}
/**
- * Display localized stylesheet link element.
+ * Displays localized stylesheet link element.
*
* @since 2.1.0
*/
@@ -693,7 +712,14 @@
if ( empty( $stylesheet ) ) {
return;
}
- echo '<link rel="stylesheet" href="' . $stylesheet . '" type="text/css" media="screen" />';
+
+ $type_attr = current_theme_supports( 'html5', 'style' ) ? '' : ' type="text/css"';
+
+ printf(
+ '<link rel="stylesheet" href="%s"%s media="screen" />',
+ $stylesheet,
+ $type_attr
+ );
}
/**
@@ -708,11 +734,16 @@
* @global WP_Customize_Manager $wp_customize
* @global array $sidebars_widgets
*
- * @param string $stylesheet Stylesheet name
+ * @param string $stylesheet Stylesheet name.
*/
function switch_theme( $stylesheet ) {
global $wp_theme_directories, $wp_customize, $sidebars_widgets;
+ $requirements = validate_theme_requirements( $stylesheet );
+ if ( is_wp_error( $requirements ) ) {
+ wp_die( $requirements );
+ }
+
$_sidebars_widgets = null;
if ( 'wp_ajax_customize_save' === current_action() ) {
$old_sidebars_widgets_data_setting = $wp_customize->get_setting( 'old_sidebars_widgets_data' );
@@ -799,16 +830,17 @@
}
/**
- * Checks that current theme files 'index.php' and 'style.css' exists.
+ * Checks that the current theme has 'index.php' and 'style.css' files.
*
* Does not initially check the default theme, which is the fallback and should always exist.
* But if it doesn't exist, it'll fall back to the latest core default theme that does exist.
* Will switch theme to the fallback theme if current theme does not validate.
*
- * You can use the {@see 'validate_current_theme'} filter to return false to
- * disable this functionality.
+ * You can use the {@see 'validate_current_theme'} filter to return false to disable
+ * this functionality.
*
* @since 1.5.0
+ *
* @see WP_DEFAULT_THEME
*
* @return bool
@@ -845,6 +877,7 @@
/**
* If we're in an invalid state but WP_DEFAULT_THEME doesn't exist,
* switch to the latest core default theme that's installed.
+ *
* If it turns out that this latest core default theme is our current
* theme, then there's nothing we can do about that, so we have to bail,
* rather than going into an infinite loop. (This is why there are
@@ -861,7 +894,79 @@
}
/**
- * Retrieve all theme modifications.
+ * Validates the theme requirements for WordPress version and PHP version.
+ *
+ * Uses the information from `Requires at least` and `Requires PHP` headers
+ * defined in the theme's `style.css` file.
+ *
+ * If the headers are not present in the theme's stylesheet file,
+ * `readme.txt` is also checked as a fallback.
+ *
+ * @since 5.5.0
+ *
+ * @param string $stylesheet Directory name for the theme.
+ * @return true|WP_Error True if requirements are met, WP_Error on failure.
+ */
+function validate_theme_requirements( $stylesheet ) {
+ $theme = wp_get_theme( $stylesheet );
+
+ $requirements = array(
+ 'requires' => ! empty( $theme->get( 'RequiresWP' ) ) ? $theme->get( 'RequiresWP' ) : '',
+ 'requires_php' => ! empty( $theme->get( 'RequiresPHP' ) ) ? $theme->get( 'RequiresPHP' ) : '',
+ );
+
+ $readme_file = $theme->theme_root . '/' . $stylesheet . '/readme.txt';
+
+ if ( file_exists( $readme_file ) ) {
+ $readme_headers = get_file_data(
+ $readme_file,
+ array(
+ 'requires' => 'Requires at least',
+ 'requires_php' => 'Requires PHP',
+ ),
+ 'theme'
+ );
+
+ $requirements = array_merge( $readme_headers, $requirements );
+ }
+
+ $compatible_wp = is_wp_version_compatible( $requirements['requires'] );
+ $compatible_php = is_php_version_compatible( $requirements['requires_php'] );
+
+ if ( ! $compatible_wp && ! $compatible_php ) {
+ return new WP_Error(
+ 'theme_wp_php_incompatible',
+ sprintf(
+ /* translators: %s: Theme name. */
+ _x( '<strong>Error:</strong> Current WordPress and PHP versions do not meet minimum requirements for %s.', 'theme' ),
+ $theme->display( 'Name' )
+ )
+ );
+ } elseif ( ! $compatible_php ) {
+ return new WP_Error(
+ 'theme_php_incompatible',
+ sprintf(
+ /* translators: %s: Theme name. */
+ _x( '<strong>Error:</strong> Current PHP version does not meet minimum requirements for %s.', 'theme' ),
+ $theme->display( 'Name' )
+ )
+ );
+ } elseif ( ! $compatible_wp ) {
+ return new WP_Error(
+ 'theme_wp_incompatible',
+ sprintf(
+ /* translators: %s: Theme name. */
+ _x( '<strong>Error:</strong> Current WordPress version does not meet minimum requirements for %s.', 'theme' ),
+ $theme->display( 'Name' )
+ )
+ );
+ }
+
+ return true;
+}
+
+/**
+ * Retrieves all theme modifications.
*
* @since 3.1.0
*
@@ -885,18 +990,18 @@
}
/**
- * Retrieve theme modification value for the current theme.
+ * Retrieves theme modification value for the current theme.
*
* If the modification name does not exist, then the $default will be passed
- * through {@link https://secure.php.net/sprintf sprintf()} PHP function with the first
- * string the template directory URI and the second string the stylesheet
- * directory URI.
+ * through {@link https://www.php.net/sprintf sprintf()} PHP function with
+ * the template directory URI as the first string and the stylesheet directory URI
+ * as the second string.
*
* @since 2.1.0
*
- * @param string $name Theme modification name.
- * @param bool|string $default
- * @return mixed
+ * @param string $name Theme modification name.
+ * @param string|false $default Optional. Theme modification default value. Default false.
+ * @return mixed Theme modification value.
*/
function get_theme_mod( $name, $default = false ) {
$mods = get_theme_mods();
@@ -905,10 +1010,9 @@
/**
* Filters the theme modification, or 'theme_mod', value.
*
- * The dynamic portion of the hook name, `$name`, refers to
- * the key name of the modification array. For example,
- * 'header_textcolor', 'header_image', and so on depending
- * on the theme options.
+ * The dynamic portion of the hook name, `$name`, refers to the key name
+ * of the modification array. For example, 'header_textcolor', 'header_image',
+ * and so on depending on the theme options.
*
* @since 2.2.0
*
@@ -918,7 +1022,10 @@
}
if ( is_string( $default ) ) {
- $default = sprintf( $default, get_template_directory_uri(), get_stylesheet_directory_uri() );
+ // Only run the replacement if an sprintf() string format pattern was found.
+ if ( preg_match( '#(?<!%)%(?:\d+\$?)?s#', $default ) ) {
+ $default = sprintf( $default, get_template_directory_uri(), get_stylesheet_directory_uri() );
+ }
}
/** This filter is documented in wp-includes/theme.php */
@@ -926,7 +1033,7 @@
}
/**
- * Update theme modification value for the current theme.
+ * Updates theme modification value for the current theme.
*
* @since 2.1.0
*
@@ -938,16 +1045,16 @@
$old_value = isset( $mods[ $name ] ) ? $mods[ $name ] : false;
/**
- * Filters the theme mod value on save.
+ * Filters the theme modification, or 'theme_mod', value on save.
*
- * The dynamic portion of the hook name, `$name`, refers to the key name of
- * the modification array. For example, 'header_textcolor', 'header_image',
+ * The dynamic portion of the hook name, `$name`, refers to the key name
+ * of the modification array. For example, 'header_textcolor', 'header_image',
* and so on depending on the theme options.
*
* @since 3.9.0
*
- * @param string $value The new value of the theme mod.
- * @param string $old_value The current value of the theme mod.
+ * @param string $value The new value of the theme modification.
+ * @param string $old_value The current value of the theme modification.
*/
$mods[ $name ] = apply_filters( "pre_set_theme_mod_{$name}", $value, $old_value );
@@ -956,10 +1063,10 @@
}
/**
- * Remove theme modification name from current theme list.
+ * Removes theme modification name from current theme list.
*
- * If removing the name also removes all elements, then the entire option will
- * be removed.
+ * If removing the name also removes all elements, then the entire option
+ * will be removed.
*
* @since 2.1.0
*
@@ -983,7 +1090,7 @@
}
/**
- * Remove theme modifications option for current theme.
+ * Removes theme modifications option for current theme.
*
* @since 2.1.0
*/
@@ -1035,7 +1142,7 @@
}
/**
- * Check whether a header image is set or not.
+ * Checks whether a header image is set or not.
*
* @since 4.2.0
*
@@ -1048,7 +1155,7 @@
}
/**
- * Retrieve header image for custom header.
+ * Retrieves header image for custom header.
*
* @since 2.1.0
*
@@ -1057,7 +1164,7 @@
function get_header_image() {
$url = get_theme_mod( 'header_image', get_theme_support( 'custom-header', 'default-image' ) );
- if ( 'remove-header' == $url ) {
+ if ( 'remove-header' === $url ) {
return false;
}
@@ -1069,7 +1176,7 @@
}
/**
- * Create image tag markup for a custom header image.
+ * Creates image tag markup for a custom header image.
*
* @since 4.4.0
*
@@ -1136,7 +1243,7 @@
}
/**
- * Display the image markup for a custom header image.
+ * Displays the image markup for a custom header image.
*
* @since 4.4.0
*
@@ -1147,14 +1254,13 @@
}
/**
- * Get random header image data from registered images in theme.
+ * Gets random header image data from registered images in theme.
*
* @since 3.4.0
*
* @access private
*
- * @global array $_wp_default_headers
- * @staticvar object $_wp_random_header
+ * @global array $_wp_default_headers
*
* @return object
*/
@@ -1166,10 +1272,10 @@
$header_image_mod = get_theme_mod( 'header_image', '' );
$headers = array();
- if ( 'random-uploaded-image' == $header_image_mod ) {
+ if ( 'random-uploaded-image' === $header_image_mod ) {
$headers = get_uploaded_header_images();
} elseif ( ! empty( $_wp_default_headers ) ) {
- if ( 'random-default-image' == $header_image_mod ) {
+ if ( 'random-default-image' === $header_image_mod ) {
$headers = $_wp_default_headers;
} else {
if ( current_theme_supports( 'custom-header', 'random-default' ) ) {
@@ -1187,26 +1293,29 @@
$_wp_random_header->url = sprintf( $_wp_random_header->url, get_template_directory_uri(), get_stylesheet_directory_uri() );
$_wp_random_header->thumbnail_url = sprintf( $_wp_random_header->thumbnail_url, get_template_directory_uri(), get_stylesheet_directory_uri() );
}
+
return $_wp_random_header;
}
/**
- * Get random header image url from registered images in theme.
+ * Gets random header image URL from registered images in theme.
*
* @since 3.2.0
*
- * @return string Path to header image
+ * @return string Path to header image.
*/
function get_random_header_image() {
$random_image = _get_random_header_data();
+
if ( empty( $random_image->url ) ) {
return '';
}
+
return $random_image->url;
}
/**
- * Check if random header image is in use.
+ * Checks if random header image is in use.
*
* Always true if user expressly chooses the option in Appearance > Header.
* Also true if theme has multiple header images registered, no specific header image
@@ -1214,20 +1323,24 @@
*
* @since 3.2.0
*
- * @param string $type The random pool to use. any|default|uploaded
+ * @param string $type The random pool to use. Possible values include 'any',
+ * 'default', 'uploaded'. Default 'any'.
* @return bool
*/
function is_random_header_image( $type = 'any' ) {
$header_image_mod = get_theme_mod( 'header_image', get_theme_support( 'custom-header', 'default-image' ) );
- if ( 'any' == $type ) {
- if ( 'random-default-image' == $header_image_mod || 'random-uploaded-image' == $header_image_mod || ( '' != get_random_header_image() && empty( $header_image_mod ) ) ) {
+ if ( 'any' === $type ) {
+ if ( 'random-default-image' === $header_image_mod
+ || 'random-uploaded-image' === $header_image_mod
+ || ( '' !== get_random_header_image() && empty( $header_image_mod ) )
+ ) {
return true;
}
} else {
- if ( "random-$type-image" == $header_image_mod ) {
+ if ( "random-$type-image" === $header_image_mod ) {
return true;
- } elseif ( 'default' == $type && empty( $header_image_mod ) && '' != get_random_header_image() ) {
+ } elseif ( 'default' === $type && empty( $header_image_mod ) && '' !== get_random_header_image() ) {
return true;
}
}
@@ -1236,19 +1349,20 @@
}
/**
- * Display header image URL.
+ * Displays header image URL.
*
* @since 2.1.0
*/
function header_image() {
$image = get_header_image();
+
if ( $image ) {
echo esc_url( $image );
}
}
/**
- * Get the header images uploaded for the current theme.
+ * Gets the header images uploaded for the current theme.
*
* @since 3.2.0
*
@@ -1257,7 +1371,7 @@
function get_uploaded_header_images() {
$header_images = array();
- // @todo caching
+ // @todo Caching.
$headers = get_posts(
array(
'post_type' => 'attachment',
@@ -1296,7 +1410,7 @@
}
/**
- * Get the header image data.
+ * Gets the header image data.
*
* @since 3.4.0
*
@@ -1312,9 +1426,10 @@
} else {
$data = get_theme_mod( 'header_image_data' );
if ( ! $data && current_theme_supports( 'custom-header', 'default-image' ) ) {
- $directory_args = array( get_template_directory_uri(), get_stylesheet_directory_uri() );
- $data = array();
- $data['url'] = $data['thumbnail_url'] = vsprintf( get_theme_support( 'custom-header', 'default-image' ), $directory_args );
+ $directory_args = array( get_template_directory_uri(), get_stylesheet_directory_uri() );
+ $data = array();
+ $data['url'] = vsprintf( get_theme_support( 'custom-header', 'default-image' ), $directory_args );
+ $data['thumbnail_url'] = $data['url'];
if ( ! empty( $_wp_default_headers ) ) {
foreach ( (array) $_wp_default_headers as $default_header ) {
$url = vsprintf( $default_header['url'], $directory_args );
@@ -1340,13 +1455,14 @@
}
/**
- * Register a selection of default headers to be displayed by the custom header admin UI.
+ * Registers a selection of default headers to be displayed by the custom header admin UI.
*
* @since 3.0.0
*
* @global array $_wp_default_headers
*
- * @param array $headers Array of headers keyed by a string id. The ids point to arrays containing 'url', 'thumbnail_url', and 'description' keys.
+ * @param array $headers Array of headers keyed by a string ID. The IDs point to arrays
+ * containing 'url', 'thumbnail_url', and 'description' keys.
*/
function register_default_headers( $headers ) {
global $_wp_default_headers;
@@ -1355,7 +1471,7 @@
}
/**
- * Unregister default headers.
+ * Unregisters default headers.
*
* This function must be called after register_default_headers() has already added the
* header you want to remove.
@@ -1382,7 +1498,7 @@
}
/**
- * Check whether a header video is set or not.
+ * Checks whether a header video is set or not.
*
* @since 4.7.0
*
@@ -1395,7 +1511,7 @@
}
/**
- * Retrieve header video URL for custom header.
+ * Retrieves header video URL for custom header.
*
* Uses a local video if present, or falls back to an external video.
*
@@ -1404,12 +1520,13 @@
* @return string|false Header video URL or false if there is no video.
*/
function get_header_video_url() {
- $id = absint( get_theme_mod( 'header_video' ) );
- $url = esc_url( get_theme_mod( 'external_header_video' ) );
+ $id = absint( get_theme_mod( 'header_video' ) );
if ( $id ) {
// Get the file URL from the attachment ID.
$url = wp_get_attachment_url( $id );
+ } else {
+ $url = get_theme_mod( 'external_header_video' );
}
/**
@@ -1429,19 +1546,20 @@
}
/**
- * Display header video URL.
+ * Displays header video URL.
*
* @since 4.7.0
*/
function the_header_video_url() {
$video = get_header_video_url();
+
if ( $video ) {
echo esc_url( $video );
}
}
/**
- * Retrieve header video settings.
+ * Retrieves header video settings.
*
* @since 4.7.0
*
@@ -1485,7 +1603,7 @@
}
/**
- * Check whether a custom header is set or not.
+ * Checks whether a custom header is set or not.
*
* @since 4.7.0
*
@@ -1520,7 +1638,7 @@
}
/**
- * Modify whether the custom header video is eligible to show on the current page.
+ * Filters whether the custom header video is eligible to show on the current page.
*
* @since 4.7.0
*
@@ -1532,7 +1650,7 @@
}
/**
- * Retrieve the markup for a custom header.
+ * Retrieves the markup for a custom header.
*
* The container div will always be returned in the Customizer preview.
*
@@ -1552,7 +1670,7 @@
}
/**
- * Print the markup for a custom header.
+ * Prints the markup for a custom header.
*
* A container div will always be printed in the Customizer preview.
*
@@ -1573,7 +1691,7 @@
}
/**
- * Retrieve background image for custom background.
+ * Retrieves background image for custom background.
*
* @since 3.0.0
*
@@ -1584,7 +1702,7 @@
}
/**
- * Display background image path.
+ * Displays background image path.
*
* @since 3.0.0
*/
@@ -1593,7 +1711,7 @@
}
/**
- * Retrieve value for custom background color.
+ * Retrieves value for custom background color.
*
* @since 3.0.0
*
@@ -1604,7 +1722,7 @@
}
/**
- * Display background color value.
+ * Displays background color value.
*
* @since 3.0.0
*/
@@ -1625,13 +1743,15 @@
// A default has to be specified in style.css. It will not be printed here.
$color = get_background_color();
- if ( $color === get_theme_support( 'custom-background', 'default-color' ) ) {
+ if ( get_theme_support( 'custom-background', 'default-color' ) === $color ) {
$color = false;
}
+ $type_attr = current_theme_supports( 'html5', 'style' ) ? '' : ' type="text/css"';
+
if ( ! $background && ! $color ) {
if ( is_customize_preview() ) {
- echo '<style type="text/css" id="custom-background-css"></style>';
+ printf( '<style%s id="custom-background-css"></style>', $type_attr );
}
return;
}
@@ -1685,22 +1805,23 @@
$style .= $image . $position . $size . $repeat . $attachment;
}
?>
-<style type="text/css" id="custom-background-css">
+<style<?php echo $type_attr; ?> id="custom-background-css">
body.custom-background { <?php echo trim( $style ); ?> }
</style>
<?php
}
/**
- * Render the Custom CSS style element.
+ * Renders the Custom CSS style element.
*
* @since 4.7.0
*/
function wp_custom_css_cb() {
$styles = wp_get_custom_css();
if ( $styles || is_customize_preview() ) :
+ $type_attr = current_theme_supports( 'html5', 'style' ) ? '' : ' type="text/css"';
?>
- <style type="text/css" id="wp-custom-css">
+ <style<?php echo $type_attr; ?> id="wp-custom-css">
<?php echo strip_tags( $styles ); // Note that esc_html() cannot be used because `div > span` is not interpreted properly. ?>
</style>
<?php
@@ -1708,7 +1829,7 @@
}
/**
- * Fetch the `custom_css` post for a given theme.
+ * Fetches the `custom_css` post for a given theme.
*
* @since 4.7.0
*
@@ -1759,7 +1880,7 @@
}
/**
- * Fetch the saved Custom CSS content for rendering.
+ * Fetches the saved Custom CSS content for rendering.
*
* @since 4.7.0
*
@@ -1792,7 +1913,7 @@
}
/**
- * Update the `custom_css` post for a given theme.
+ * Updates the `custom_css` post for a given theme.
*
* Inserts a `custom_css` post when one doesn't yet exist.
*
@@ -1892,7 +2013,7 @@
}
/**
- * Add callback for custom TinyMCE editor stylesheets.
+ * Adds callback for custom TinyMCE editor stylesheets.
*
* The parameter $stylesheet is the name of the stylesheet, relative to
* the theme root. It also accepts an array of stylesheets.
@@ -1914,15 +2035,13 @@
* Defaults to 'editor-style.css'
*/
function add_editor_style( $stylesheet = 'editor-style.css' ) {
+ global $editor_styles;
+
add_theme_support( 'editor-style' );
- if ( ! is_admin() ) {
- return;
- }
-
- global $editor_styles;
$editor_styles = (array) $editor_styles;
$stylesheet = (array) $stylesheet;
+
if ( is_rtl() ) {
$rtl_stylesheet = str_replace( '.css', '-rtl.css', $stylesheet[0] );
$stylesheet[] = $rtl_stylesheet;
@@ -1952,17 +2071,17 @@
}
/**
- * Retrieve any registered editor stylesheets
+ * Retrieves any registered editor stylesheet URLs.
*
* @since 4.0.0
*
* @global array $editor_styles Registered editor stylesheets
*
- * @return array If registered, a list of editor stylesheet URLs.
+ * @return string[] If registered, a list of editor stylesheet URLs.
*/
function get_editor_stylesheets() {
$stylesheets = array();
- // load editor_style.css if the current theme supports it
+ // Load editor_style.css if the current theme supports it.
if ( ! empty( $GLOBALS['editor_styles'] ) && is_array( $GLOBALS['editor_styles'] ) ) {
$editor_styles = $GLOBALS['editor_styles'];
@@ -1998,17 +2117,17 @@
}
/**
- * Filters the array of stylesheets applied to the editor.
+ * Filters the array of URLs of stylesheets applied to the editor.
*
* @since 4.3.0
*
- * @param array $stylesheets Array of stylesheets to be applied to the editor.
+ * @param string[] $stylesheets Array of URLs of stylesheets to be applied to the editor.
*/
return apply_filters( 'editor_stylesheets', $stylesheets );
}
/**
- * Expand a theme's starter content configuration using core-provided data.
+ * Expands a theme's starter content configuration using core-provided data.
*
* @since 4.7.0
*
@@ -2034,7 +2153,7 @@
'<strong>' . _x( 'Address', 'Theme starter content' ) . "</strong>\n",
_x( '123 Main Street', 'Theme starter content' ) . "\n" . _x( 'New York, NY 10001', 'Theme starter content' ) . "\n\n",
'<strong>' . _x( 'Hours', 'Theme starter content' ) . "</strong>\n",
- _x( 'Monday—Friday: 9:00AM–5:00PM', 'Theme starter content' ) . "\n" . _x( 'Saturday & Sunday: 11:00AM–3:00PM', 'Theme starter content' ),
+ _x( 'Monday–Friday: 9:00AM–5:00PM', 'Theme starter content' ) . "\n" . _x( 'Saturday & Sunday: 11:00AM–3:00PM', 'Theme starter content' ),
)
),
'filter' => true,
@@ -2099,7 +2218,7 @@
'title' => _x( 'Home', 'Theme starter content' ),
'url' => home_url( '/' ),
),
- 'page_home' => array( // Deprecated in favor of link_home.
+ 'page_home' => array( // Deprecated in favor of 'link_home'.
'type' => 'post_type',
'object' => 'page',
'object_id' => '{{home}}',
@@ -2170,17 +2289,26 @@
'home' => array(
'post_type' => 'page',
'post_title' => _x( 'Home', 'Theme starter content' ),
- 'post_content' => _x( 'Welcome to your site! This is your homepage, which is what most visitors will see when they come to your site for the first time.', 'Theme starter content' ),
+ 'post_content' => sprintf(
+ "<!-- wp:paragraph -->\n<p>%s</p>\n<!-- /wp:paragraph -->",
+ _x( 'Welcome to your site! This is your homepage, which is what most visitors will see when they come to your site for the first time.', 'Theme starter content' )
+ ),
),
'about' => array(
'post_type' => 'page',
'post_title' => _x( 'About', 'Theme starter content' ),
- 'post_content' => _x( 'You might be an artist who would like to introduce yourself and your work here or maybe you’re a business with a mission to describe.', 'Theme starter content' ),
+ 'post_content' => sprintf(
+ "<!-- wp:paragraph -->\n<p>%s</p>\n<!-- /wp:paragraph -->",
+ _x( 'You might be an artist who would like to introduce yourself and your work here or maybe you’re a business with a mission to describe.', 'Theme starter content' )
+ ),
),
'contact' => array(
'post_type' => 'page',
'post_title' => _x( 'Contact', 'Theme starter content' ),
- 'post_content' => _x( 'This is a page with some basic contact information, such as an address and phone number. You might also try a plugin to add a contact form.', 'Theme starter content' ),
+ 'post_content' => sprintf(
+ "<!-- wp:paragraph -->\n<p>%s</p>\n<!-- /wp:paragraph -->",
+ _x( 'This is a page with some basic contact information, such as an address and phone number. You might also try a plugin to add a contact form.', 'Theme starter content' )
+ ),
),
'blog' => array(
'post_type' => 'page',
@@ -2194,7 +2322,10 @@
'homepage-section' => array(
'post_type' => 'page',
'post_title' => _x( 'A homepage section', 'Theme starter content' ),
- 'post_content' => _x( 'This is an example of a homepage section. Homepage sections can be any page other than the homepage itself, including the page that shows your latest blog posts.', 'Theme starter content' ),
+ 'post_content' => sprintf(
+ "<!-- wp:paragraph -->\n<p>%s</p>\n<!-- /wp:paragraph -->",
+ _x( 'This is an example of a homepage section. Homepage sections can be any page other than the homepage itself, including the page that shows your latest blog posts.', 'Theme starter content' )
+ ),
),
),
);
@@ -2267,7 +2398,8 @@
}
break;
- // All that's left now are posts (besides attachments). Not a default case for the sake of clarity and future work.
+ // All that's left now are posts (besides attachments).
+ // Not a default case for the sake of clarity and future work.
case 'posts':
foreach ( $config[ $type ] as $id => $item ) {
if ( is_array( $item ) ) {
@@ -2318,32 +2450,46 @@
* If attached to a hook, it must be {@see 'after_setup_theme'}.
* The {@see 'init'} hook may be too late for some features.
*
+ * Example usage:
+ *
+ * add_theme_support( 'title-tag' );
+ * add_theme_support( 'custom-logo', array(
+ * 'height' => 480,
+ * 'width' => 720,
+ * ) );
+ *
* @since 2.9.0
- * @since 3.6.0 The `html5` feature was added
- * @since 3.9.0 The `html5` feature now also accepts 'gallery' and 'caption'
- * @since 4.1.0 The `title-tag` feature was added
- * @since 4.5.0 The `customize-selective-refresh-widgets` feature was added
- * @since 4.7.0 The `starter-content` feature was added
+ * @since 3.4.0 The `custom-header-uploads` feature was deprecated.
+ * @since 3.6.0 The `html5` feature was added.
+ * @since 3.9.0 The `html5` feature now also accepts 'gallery' and 'caption'.
+ * @since 4.1.0 The `title-tag` feature was added.
+ * @since 4.5.0 The `customize-selective-refresh-widgets` feature was added.
+ * @since 4.7.0 The `starter-content` feature was added.
* @since 5.0.0 The `responsive-embeds`, `align-wide`, `dark-editor-style`, `disable-custom-colors`,
- * `disable-custom-font-sizes`, `editor-color-pallete`, `editor-font-sizes`,
+ * `disable-custom-font-sizes`, `editor-color-palette`, `editor-font-sizes`,
* `editor-styles`, and `wp-block-styles` features were added.
+ * @since 5.3.0 The `html5` feature now also accepts 'script' and 'style'.
+ * @since 5.3.0 Formalized the existing and already documented `...$args` parameter
+ * by adding it to the function signature.
+ * @since 5.5.0 The `core-block-patterns` feature was added and is enabled by default.
+ * @since 5.5.0 The `custom-logo` feature now also accepts 'unlink-homepage-logo'.
*
* @global array $_wp_theme_features
*
- * @param string $feature The feature being added. Likely core values include 'post-formats',
- * 'post-thumbnails', 'html5', 'custom-logo', 'custom-header-uploads',
- * 'custom-header', 'custom-background', 'title-tag', 'starter-content',
- * 'responsive-embeds', etc.
- * @param mixed $args,... Optional extra arguments to pass along with certain features.
+ * @param string $feature The feature being added. Likely core values include 'post-formats', 'post-thumbnails',
+ * 'custom-header', 'custom-background', 'custom-logo', 'menus', 'automatic-feed-links',
+ * 'html5', 'title-tag', 'customize-selective-refresh-widgets', 'starter-content',
+ * 'responsive-embeds', 'align-wide', 'dark-editor-style', 'disable-custom-colors',
+ * 'disable-custom-font-sizes', 'editor-color-palette', 'editor-font-sizes',
+ * 'editor-styles', 'wp-block-styles', and 'core-block-patterns'.
+ * @param mixed ...$args Optional extra arguments to pass along with certain features.
* @return void|bool False on failure, void otherwise.
*/
-function add_theme_support( $feature ) {
+function add_theme_support( $feature, ...$args ) {
global $_wp_theme_features;
- if ( func_num_args() == 1 ) {
+ if ( ! $args ) {
$args = true;
- } else {
- $args = array_slice( func_get_args(), 1 );
}
switch ( $feature ) {
@@ -2357,14 +2503,14 @@
* Merge post types with any that already declared their support
* for post thumbnails.
*/
- if ( is_array( $args[0] ) && isset( $_wp_theme_features['post-thumbnails'] ) ) {
+ if ( isset( $args[0] ) && is_array( $args[0] ) && isset( $_wp_theme_features['post-thumbnails'] ) ) {
$args[0] = array_unique( array_merge( $_wp_theme_features['post-thumbnails'][0], $args[0] ) );
}
break;
case 'post-formats':
- if ( is_array( $args[0] ) ) {
+ if ( isset( $args[0] ) && is_array( $args[0] ) ) {
$post_formats = get_post_format_slugs();
unset( $post_formats['standard'] );
@@ -2377,7 +2523,7 @@
if ( empty( $args[0] ) ) {
// Build an array of types for back-compat.
$args = array( 0 => array( 'comment-list', 'comment-form', 'search-form' ) );
- } elseif ( ! is_array( $args[0] ) ) {
+ } elseif ( ! isset( $args[0] ) || ! is_array( $args[0] ) ) {
_doing_it_wrong( "add_theme_support( 'html5' )", __( 'You need to pass an array of types.' ), '3.6.1' );
return false;
}
@@ -2389,15 +2535,16 @@
break;
case 'custom-logo':
- if ( ! is_array( $args ) ) {
+ if ( true === $args ) {
$args = array( 0 => array() );
}
$defaults = array(
- 'width' => null,
- 'height' => null,
- 'flex-width' => false,
- 'flex-height' => false,
- 'header-text' => '',
+ 'width' => null,
+ 'height' => null,
+ 'flex-width' => false,
+ 'flex-height' => false,
+ 'header-text' => '',
+ 'unlink-homepage-logo' => false,
);
$args[0] = wp_parse_args( array_intersect_key( $args[0], $defaults ), $defaults );
@@ -2412,7 +2559,7 @@
return add_theme_support( 'custom-header', array( 'uploads' => true ) );
case 'custom-header':
- if ( ! is_array( $args ) ) {
+ if ( true === $args ) {
$args = array( 0 => array() );
}
@@ -2448,11 +2595,13 @@
$args[0] = wp_parse_args( $args[0], $defaults );
}
- // If a constant was defined, use that value. Otherwise, define the constant to ensure
- // the constant is always accurate (and is not defined later, overriding our value).
- // As stated above, the first value wins.
- // Once we get to wp_loaded (just-in-time), define any constants we haven't already.
- // Constants are lame. Don't reference them. This is just for backward compatibility.
+ /*
+ * If a constant was defined, use that value. Otherwise, define the constant to ensure
+ * the constant is always accurate (and is not defined later, overriding our value).
+ * As stated above, the first value wins.
+ * Once we get to wp_loaded (just-in-time), define any constants we haven't already.
+ * Constants are lame. Don't reference them. This is just for backward compatibility.
+ */
if ( defined( 'NO_HEADER_TEXT' ) ) {
$args[0]['header-text'] = ! NO_HEADER_TEXT;
@@ -2502,7 +2651,7 @@
break;
case 'custom-background':
- if ( ! is_array( $args ) ) {
+ if ( true === $args ) {
$args = array( 0 => array() );
}
@@ -2550,10 +2699,10 @@
case 'title-tag':
// Can be called in functions.php but must happen before wp_loaded, i.e. not in header.php.
if ( did_action( 'wp_loaded' ) ) {
- /* translators: 1: title-tag, 2: wp_loaded */
_doing_it_wrong(
"add_theme_support( 'title-tag' )",
sprintf(
+ /* translators: 1: title-tag, 2: wp_loaded */
__( 'Theme support for %1$s should be registered before the %2$s hook.' ),
'<code>title-tag</code>',
'<code>wp_loaded</code>'
@@ -2590,7 +2739,7 @@
}
if ( is_admin() ) {
- require_once( ABSPATH . 'wp-admin/custom-header.php' );
+ require_once ABSPATH . 'wp-admin/includes/class-custom-image-header.php';
$custom_image_header = new Custom_Image_Header( $args[0]['admin-head-callback'], $args[0]['admin-preview-callback'] );
}
}
@@ -2603,7 +2752,7 @@
add_action( 'wp_head', $args[0]['wp-head-callback'] );
if ( is_admin() ) {
- require_once( ABSPATH . 'wp-admin/custom-background.php' );
+ require_once ABSPATH . 'wp-admin/includes/class-custom-background.php';
$custom_background = new Custom_Background( $args[0]['admin-head-callback'], $args[0]['admin-preview-callback'] );
}
}
@@ -2621,9 +2770,10 @@
$classes = array_map( 'sanitize_html_class', $classes );
$classes = '.' . implode( ', .', $classes );
+ $type_attr = current_theme_supports( 'html5', 'style' ) ? '' : ' type="text/css"';
?>
<!-- Custom Logo: hide header text -->
- <style id="custom-logo-css" type="text/css">
+ <style id="custom-logo-css"<?php echo $type_attr; ?>>
<?php echo $classes; ?> {
position: absolute;
clip: rect(1px, 1px, 1px, 1px);
@@ -2634,26 +2784,34 @@
}
/**
- * Gets the theme support arguments passed when registering that support
+ * Gets the theme support arguments passed when registering that support.
+ *
+ * Example usage:
+ *
+ * get_theme_support( 'custom-logo' );
+ * get_theme_support( 'custom-header', 'width' );
*
* @since 3.1.0
+ * @since 5.3.0 Formalized the existing and already documented `...$args` parameter
+ * by adding it to the function signature.
*
* @global array $_wp_theme_features
*
- * @param string $feature The feature to check.
+ * @param string $feature The feature to check. See add_theme_support() for the list
+ * of possible values.
+ * @param mixed ...$args Optional extra arguments to be checked against certain features.
* @return mixed The array of extra arguments or the value for the registered feature.
*/
-function get_theme_support( $feature ) {
+function get_theme_support( $feature, ...$args ) {
global $_wp_theme_features;
if ( ! isset( $_wp_theme_features[ $feature ] ) ) {
return false;
}
- if ( func_num_args() <= 1 ) {
+ if ( ! $args ) {
return $_wp_theme_features[ $feature ];
}
- $args = array_slice( func_get_args(), 1 );
switch ( $feature ) {
case 'custom-logo':
case 'custom-header':
@@ -2675,13 +2833,16 @@
* be used for child themes to override support from the parent theme.
*
* @since 3.0.0
+ *
* @see add_theme_support()
- * @param string $feature The feature being removed.
+ *
+ * @param string $feature The feature being removed. See add_theme_support() for the list
+ * of possible values.
* @return bool|void Whether feature was removed.
*/
function remove_theme_support( $feature ) {
- // Blacklist: for internal registrations not used directly by themes.
- if ( in_array( $feature, array( 'editor-style', 'widgets', 'menus' ) ) ) {
+ // Do not remove internal registrations that are not used directly by themes.
+ if ( in_array( $feature, array( 'editor-style', 'widgets', 'menus' ), true ) ) {
return false;
}
@@ -2689,16 +2850,18 @@
}
/**
- * Do not use. Removes theme support internally, ignorant of the blacklist.
+ * Do not use. Removes theme support internally without knowledge of those not used
+ * by themes directly.
*
* @access private
* @since 3.1.0
- *
* @global array $_wp_theme_features
* @global Custom_Image_Header $custom_image_header
* @global Custom_Background $custom_background
*
- * @param string $feature
+ * @param string $feature The feature being removed. See add_theme_support() for the list
+ * of possible values.
+ * @return bool True if support was removed, false if the feature was not registered.
*/
function _remove_theme_support( $feature ) {
global $_wp_theme_features;
@@ -2745,23 +2908,33 @@
}
unset( $_wp_theme_features[ $feature ] );
+
return true;
}
/**
* Checks a theme's support for a given feature.
*
+ * Example usage:
+ *
+ * current_theme_supports( 'custom-logo' );
+ * current_theme_supports( 'html5', 'comment-form' );
+ *
* @since 2.9.0
+ * @since 5.3.0 Formalized the existing and already documented `...$args` parameter
+ * by adding it to the function signature.
*
* @global array $_wp_theme_features
*
- * @param string $feature The feature being checked.
+ * @param string $feature The feature being checked. See add_theme_support() for the list
+ * of possible values.
+ * @param mixed ...$args Optional extra arguments to be checked against certain features.
* @return bool True if the current theme supports the feature, false otherwise.
*/
-function current_theme_supports( $feature ) {
+function current_theme_supports( $feature, ...$args ) {
global $_wp_theme_features;
- if ( 'custom-header-uploads' == $feature ) {
+ if ( 'custom-header-uploads' === $feature ) {
return current_theme_supports( 'custom-header', 'uploads' );
}
@@ -2769,33 +2942,34 @@
return false;
}
- // If no args passed then no extra checks need be performed
- if ( func_num_args() <= 1 ) {
+ // If no args passed then no extra checks need be performed.
+ if ( ! $args ) {
return true;
}
- $args = array_slice( func_get_args(), 1 );
-
switch ( $feature ) {
case 'post-thumbnails':
- // post-thumbnails can be registered for only certain content/post types by passing
- // an array of types to add_theme_support(). If no array was passed, then
- // any type is accepted
- if ( true === $_wp_theme_features[ $feature ] ) { // Registered for all types
+ /*
+ * post-thumbnails can be registered for only certain content/post types
+ * by passing an array of types to add_theme_support().
+ * If no array was passed, then any type is accepted.
+ */
+ if ( true === $_wp_theme_features[ $feature ] ) { // Registered for all types.
return true;
}
$content_type = $args[0];
- return in_array( $content_type, $_wp_theme_features[ $feature ][0] );
+ return in_array( $content_type, $_wp_theme_features[ $feature ][0], true );
case 'html5':
case 'post-formats':
- // specific post formats can be registered by passing an array of types to
- // add_theme_support()
-
- // Specific areas of HTML5 support *must* be passed via an array to add_theme_support()
-
+ /*
+ * Specific post formats can be registered by passing an array of types
+ * to add_theme_support().
+ *
+ * Specific areas of HTML5 support *must* be passed via an array to add_theme_support().
+ */
$type = $args[0];
- return in_array( $type, $_wp_theme_features[ $feature ][0] );
+ return in_array( $type, $_wp_theme_features[ $feature ][0], true );
case 'custom-logo':
case 'custom-header':
@@ -2807,18 +2981,16 @@
/**
* Filters whether the current theme supports a specific feature.
*
- * The dynamic portion of the hook name, `$feature`, refers to the specific theme
- * feature. Possible values include 'post-formats', 'post-thumbnails', 'custom-background',
- * 'custom-header', 'menus', 'automatic-feed-links', 'html5',
- * 'starter-content', and 'customize-selective-refresh-widgets'.
+ * The dynamic portion of the hook name, `$feature`, refers to the specific
+ * theme feature. See add_theme_support() for the list of possible values.
*
* @since 3.4.0
*
- * @param bool true Whether the current theme supports the given feature. Default true.
- * @param array $args Array of arguments for the feature.
- * @param string $feature The theme feature.
+ * @param bool $supports Whether the current theme supports the given feature. Default true.
+ * @param array $args Array of arguments for the feature.
+ * @param string $feature The theme feature.
*/
- return apply_filters( "current_theme_supports-{$feature}", true, $args, $_wp_theme_features[ $feature ] );
+ return apply_filters( "current_theme_supports-{$feature}", true, $args, $_wp_theme_features[ $feature ] ); // phpcs:ignore WordPress.NamingConventions.ValidHookName.UseUnderscores
}
/**
@@ -2826,19 +2998,206 @@
*
* @since 2.9.0
*
- * @param string $feature The feature being checked.
+ * @param string $feature The feature being checked. See add_theme_support() for the list
+ * of possible values.
* @param string $include Path to the file.
* @return bool True if the current theme supports the supplied feature, false otherwise.
*/
function require_if_theme_supports( $feature, $include ) {
if ( current_theme_supports( $feature ) ) {
- require( $include );
+ require $include;
return true;
}
return false;
}
/**
+ * Registers a theme feature for use in add_theme_support().
+ *
+ * This does not indicate that the current theme supports the feature, it only describes
+ * the feature's supported options.
+ *
+ * @since 5.5.0
+ *
+ * @see add_theme_support()
+ *
+ * @global array $_wp_registered_theme_features
+ *
+ * @param string $feature The name uniquely identifying the feature. See add_theme_support()
+ * for the list of possible values.
+ * @param array $args {
+ * Data used to describe the theme.
+ *
+ * @type string $type The type of data associated with this feature.
+ * Valid values are 'string', 'boolean', 'integer',
+ * 'number', 'array', and 'object'. Defaults to 'boolean'.
+ * @type boolean $variadic Does this feature utilize the variadic support
+ * of add_theme_support(), or are all arguments specified
+ * as the second parameter. Must be used with the "array" type.
+ * @type string $description A short description of the feature. Included in
+ * the Themes REST API schema. Intended for developers.
+ * @type bool|array $show_in_rest {
+ * Whether this feature should be included in the Themes REST API endpoint.
+ * Defaults to not being included. When registering an 'array' or 'object' type,
+ * this argument must be an array with the 'schema' key.
+ *
+ * @type array $schema Specifies the JSON Schema definition describing
+ * the feature. If any objects in the schema do not include
+ * the 'additionalProperties' keyword, it is set to false.
+ * @type string $name An alternate name to be used as the property name
+ * in the REST API.
+ * @type callable $prepare_callback A function used to format the theme support in the REST API.
+ * Receives the raw theme support value.
+ * }
+ * }
+ * @return true|WP_Error True if the theme feature was successfully registered, a WP_Error object if not.
+ */
+function register_theme_feature( $feature, $args = array() ) {
+ global $_wp_registered_theme_features;
+
+ if ( ! is_array( $_wp_registered_theme_features ) ) {
+ $_wp_registered_theme_features = array();
+ }
+
+ $defaults = array(
+ 'type' => 'boolean',
+ 'variadic' => false,
+ 'description' => '',
+ 'show_in_rest' => false,
+ );
+
+ $args = wp_parse_args( $args, $defaults );
+
+ if ( true === $args['show_in_rest'] ) {
+ $args['show_in_rest'] = array();
+ }
+
+ if ( is_array( $args['show_in_rest'] ) ) {
+ $args['show_in_rest'] = wp_parse_args(
+ $args['show_in_rest'],
+ array(
+ 'schema' => array(),
+ 'name' => $feature,
+ 'prepare_callback' => null,
+ )
+ );
+ }
+
+ if ( ! in_array( $args['type'], array( 'string', 'boolean', 'integer', 'number', 'array', 'object' ), true ) ) {
+ return new WP_Error(
+ 'invalid_type',
+ __( 'The feature "type" is not valid JSON Schema type.' )
+ );
+ }
+
+ if ( true === $args['variadic'] && 'array' !== $args['type'] ) {
+ return new WP_Error(
+ 'variadic_must_be_array',
+ __( 'When registering a "variadic" theme feature, the "type" must be an "array".' )
+ );
+ }
+
+ if ( false !== $args['show_in_rest'] && in_array( $args['type'], array( 'array', 'object' ), true ) ) {
+ if ( ! is_array( $args['show_in_rest'] ) || empty( $args['show_in_rest']['schema'] ) ) {
+ return new WP_Error(
+ 'missing_schema',
+ __( 'When registering an "array" or "object" feature to show in the REST API, the feature\'s schema must also be defined.' )
+ );
+ }
+
+ if ( 'array' === $args['type'] && ! isset( $args['show_in_rest']['schema']['items'] ) ) {
+ return new WP_Error(
+ 'missing_schema_items',
+ __( 'When registering an "array" feature, the feature\'s schema must include the "items" keyword.' )
+ );
+ }
+
+ if ( 'object' === $args['type'] && ! isset( $args['show_in_rest']['schema']['properties'] ) ) {
+ return new WP_Error(
+ 'missing_schema_properties',
+ __( 'When registering an "object" feature, the feature\'s schema must include the "properties" keyword.' )
+ );
+ }
+ }
+
+ if ( is_array( $args['show_in_rest'] ) ) {
+ if ( isset( $args['show_in_rest']['prepare_callback'] ) && ! is_callable( $args['show_in_rest']['prepare_callback'] ) ) {
+ return new WP_Error(
+ 'invalid_rest_prepare_callback',
+ sprintf(
+ /* translators: %s: prepare_callback */
+ __( 'The "%s" must be a callable function.' ),
+ 'prepare_callback'
+ )
+ );
+ }
+
+ $args['show_in_rest']['schema'] = wp_parse_args(
+ $args['show_in_rest']['schema'],
+ array(
+ 'description' => $args['description'],
+ 'type' => $args['type'],
+ 'default' => false,
+ )
+ );
+
+ if ( is_bool( $args['show_in_rest']['schema']['default'] )
+ && ! in_array( 'boolean', (array) $args['show_in_rest']['schema']['type'], true )
+ ) {
+ // Automatically include the "boolean" type when the default value is a boolean.
+ $args['show_in_rest']['schema']['type'] = (array) $args['show_in_rest']['schema']['type'];
+ array_unshift( $args['show_in_rest']['schema']['type'], 'boolean' );
+ }
+
+ $args['show_in_rest']['schema'] = rest_default_additional_properties_to_false( $args['show_in_rest']['schema'] );
+ }
+
+ $_wp_registered_theme_features[ $feature ] = $args;
+
+ return true;
+}
+
+/**
+ * Gets the list of registered theme features.
+ *
+ * @since 5.5.0
+ *
+ * @global array $_wp_registered_theme_features
+ *
+ * @return array[] List of theme features, keyed by their name.
+ */
+function get_registered_theme_features() {
+ global $_wp_registered_theme_features;
+
+ if ( ! is_array( $_wp_registered_theme_features ) ) {
+ return array();
+ }
+
+ return $_wp_registered_theme_features;
+}
+
+/**
+ * Gets the registration config for a theme feature.
+ *
+ * @since 5.5.0
+ *
+ * @global array $_wp_registered_theme_features
+ *
+ * @param string $feature The feature name. See add_theme_support() for the list
+ * of possible values.
+ * @return array|null The registration args, or null if the feature was not registered.
+ */
+function get_registered_theme_feature( $feature ) {
+ global $_wp_registered_theme_features;
+
+ if ( ! is_array( $_wp_registered_theme_features ) ) {
+ return null;
+ }
+
+ return isset( $_wp_registered_theme_features[ $feature ] ) ? $_wp_registered_theme_features[ $feature ] : null;
+}
+
+/**
* Checks an attachment being deleted to see if it's a header or background image.
*
* If true it removes the theme modification which would be pointing at the deleted
@@ -2849,7 +3208,7 @@
* @since 4.3.0 Also removes `header_image_data`.
* @since 4.5.0 Also removes custom logo theme mods.
*
- * @param int $id The attachment id.
+ * @param int $id The attachment ID.
*/
function _delete_attachment_theme_mod( $id ) {
$attachment_image = wp_get_attachment_url( $id );
@@ -2880,10 +3239,11 @@
* @since 3.3.0
*/
function check_theme_switched() {
- if ( $stylesheet = get_option( 'theme_switched' ) ) {
+ $stylesheet = get_option( 'theme_switched' );
+ if ( $stylesheet ) {
$old_theme = wp_get_theme( $stylesheet );
- // Prevent widget & menu mapping from running since Customizer already called it up front
+ // Prevent widget & menu mapping from running since Customizer already called it up front.
if ( get_option( 'theme_switched_via_customizer' ) ) {
remove_action( 'after_switch_theme', '_wp_menus_changed' );
remove_action( 'after_switch_theme', '_wp_sidebars_changed' );
@@ -2930,11 +3290,11 @@
*/
function _wp_customize_include() {
- $is_customize_admin_page = ( is_admin() && 'customize.php' == basename( $_SERVER['PHP_SELF'] ) );
+ $is_customize_admin_page = ( is_admin() && 'customize.php' === basename( $_SERVER['PHP_SELF'] ) );
$should_include = (
$is_customize_admin_page
||
- ( isset( $_REQUEST['wp_customize'] ) && 'on' == $_REQUEST['wp_customize'] )
+ ( isset( $_REQUEST['wp_customize'] ) && 'on' === $_REQUEST['wp_customize'] )
||
( ! empty( $_GET['customize_changeset_uuid'] ) || ! empty( $_POST['customize_changeset_uuid'] ) )
);
@@ -2955,10 +3315,16 @@
);
$theme = null;
- $changeset_uuid = false; // Value false indicates UUID should be determined after_setup_theme to either re-use existing saved changeset or else generate a new UUID if none exists.
+ $autosaved = null;
$messenger_channel = null;
- $autosaved = null;
- $branching = false; // Set initially fo false since defaults to true for back-compat; can be overridden via the customize_changeset_branching filter.
+
+ // Value false indicates UUID should be determined after_setup_theme
+ // to either re-use existing saved changeset or else generate a new UUID if none exists.
+ $changeset_uuid = false;
+
+ // Set initially fo false since defaults to true for back-compat;
+ // can be overridden via the customize_changeset_branching filter.
+ $branching = false;
if ( $is_customize_admin_page && isset( $input_vars['changeset_uuid'] ) ) {
$changeset_uuid = sanitize_key( $input_vars['changeset_uuid'] );
@@ -3082,11 +3448,12 @@
* transitioned into pending status by a contributor.
*
* @since 4.7.0
+ *
* @see wp_insert_post()
*
* @param array $post_data An array of slashed post data.
* @param array $supplied_post_data An array of sanitized, but otherwise unmodified post data.
- * @returns array Filtered data.
+ * @return array Filtered data.
*/
function _wp_customize_changeset_filter_insert_post_data( $post_data, $supplied_post_data ) {
if ( isset( $post_data['post_type'] ) && 'customize_changeset' === $post_data['post_type'] ) {
@@ -3144,7 +3511,7 @@
* The theme's stylesheet will be urlencoded if necessary.
* @return string
*/
-function wp_customize_url( $stylesheet = null ) {
+function wp_customize_url( $stylesheet = '' ) {
$url = admin_url( 'customize.php' );
if ( $stylesheet ) {
$url .= '?theme=' . urlencode( $stylesheet );
@@ -3167,35 +3534,29 @@
*
* @since 3.4.0
* @since 4.7.0 Support for IE8 and below is explicitly removed via conditional comments.
+ * @since 5.5.0 IE8 and older are no longer supported.
*/
function wp_customize_support_script() {
$admin_origin = parse_url( admin_url() );
$home_origin = parse_url( home_url() );
$cross_domain = ( strtolower( $admin_origin['host'] ) != strtolower( $home_origin['host'] ) );
-
+ $type_attr = current_theme_supports( 'html5', 'script' ) ? '' : ' type="text/javascript"';
?>
- <!--[if lte IE 8]>
- <script type="text/javascript">
- document.body.className = document.body.className.replace( /(^|\s)(no-)?customize-support(?=\s|$)/, '' ) + ' no-customize-support';
- </script>
- <![endif]-->
- <!--[if gte IE 9]><!-->
- <script type="text/javascript">
- (function() {
- var request, b = document.body, c = 'className', cs = 'customize-support', rcs = new RegExp('(^|\\s+)(no-)?'+cs+'(\\s+|$)');
-
- <?php if ( $cross_domain ) : ?>
- request = (function(){ var xhr = new XMLHttpRequest(); return ('withCredentials' in xhr); })();
- <?php else : ?>
- request = true;
- <?php endif; ?>
-
- b[c] = b[c].replace( rcs, ' ' );
- // The customizer requires postMessage and CORS (if the site is cross domain)
- b[c] += ( window.postMessage && request ? ' ' : ' no-' ) + cs;
- }());
- </script>
- <!--<![endif]-->
+ <script<?php echo $type_attr; ?>>
+ (function() {
+ var request, b = document.body, c = 'className', cs = 'customize-support', rcs = new RegExp('(^|\\s+)(no-)?'+cs+'(\\s+|$)');
+
+ <?php if ( $cross_domain ) : ?>
+ request = (function(){ var xhr = new XMLHttpRequest(); return ('withCredentials' in xhr); })();
+ <?php else : ?>
+ request = true;
+ <?php endif; ?>
+
+ b[c] = b[c].replace( rcs, ' ' );
+ // The customizer requires postMessage and CORS (if the site is cross domain).
+ b[c] += ( window.postMessage && request ? ' ' : ' no-' ) + cs;
+ }());
+ </script>
<?php
}
@@ -3215,7 +3576,7 @@
}
/**
- * Make sure that auto-draft posts get their post_date bumped or status changed to draft to prevent premature garbage-collection.
+ * Makes sure that auto-draft posts get their post_date bumped or status changed to draft to prevent premature garbage-collection.
*
* When a changeset is updated but remains an auto-draft, ensure the post_date
* for the auto-draft posts remains the same so that it will be
@@ -3236,10 +3597,11 @@
* @access private
* @see wp_delete_auto_drafts()
*
+ * @global wpdb $wpdb WordPress database abstraction object.
+ *
* @param string $new_status Transition to this post status.
* @param string $old_status Previous post status.
* @param \WP_Post $post Post data.
- * @global wpdb $wpdb
*/
function _wp_keep_alive_customize_changeset_dependent_auto_drafts( $new_status, $old_status, $post ) {
global $wpdb;
@@ -3304,3 +3666,374 @@
clean_post_cache( $post_id );
}
}
+
+/**
+ * Creates the initial theme features when the 'setup_theme' action is fired.
+ *
+ * See {@see 'setup_theme'}.
+ *
+ * @since 5.5.0
+ */
+function create_initial_theme_features() {
+ register_theme_feature(
+ 'align-wide',
+ array(
+ 'description' => __( 'Whether theme opts in to wide alignment CSS class.' ),
+ 'show_in_rest' => true,
+ )
+ );
+ register_theme_feature(
+ 'automatic-feed-links',
+ array(
+ 'description' => __( 'Whether posts and comments RSS feed links are added to head.' ),
+ 'show_in_rest' => true,
+ )
+ );
+ register_theme_feature(
+ 'custom-background',
+ array(
+ 'description' => __( 'Custom background if defined by the theme.' ),
+ 'type' => 'object',
+ 'show_in_rest' => array(
+ 'schema' => array(
+ 'properties' => array(
+ 'default-image' => array(
+ 'type' => 'string',
+ 'format' => 'uri',
+ ),
+ 'default-preset' => array(
+ 'type' => 'string',
+ 'enum' => array(
+ 'default',
+ 'fill',
+ 'fit',
+ 'repeat',
+ 'custom',
+ ),
+ ),
+ 'default-position-x' => array(
+ 'type' => 'string',
+ 'enum' => array(
+ 'left',
+ 'center',
+ 'right',
+ ),
+ ),
+ 'default-position-y' => array(
+ 'type' => 'string',
+ 'enum' => array(
+ 'left',
+ 'center',
+ 'right',
+ ),
+ ),
+ 'default-size' => array(
+ 'type' => 'string',
+ 'enum' => array(
+ 'auto',
+ 'contain',
+ 'cover',
+ ),
+ ),
+ 'default-repeat' => array(
+ 'type' => 'string',
+ 'enum' => array(
+ 'repeat-x',
+ 'repeat-y',
+ 'repeat',
+ 'no-repeat',
+ ),
+ ),
+ 'default-attachment' => array(
+ 'type' => 'string',
+ 'enum' => array(
+ 'scroll',
+ 'fixed',
+ ),
+ ),
+ 'default-color' => array(
+ 'type' => 'string',
+ ),
+ ),
+ ),
+ ),
+ )
+ );
+ register_theme_feature(
+ 'custom-header',
+ array(
+ 'description' => __( 'Custom header if defined by the theme.' ),
+ 'type' => 'object',
+ 'show_in_rest' => array(
+ 'schema' => array(
+ 'properties' => array(
+ 'default-image' => array(
+ 'type' => 'string',
+ 'format' => 'uri',
+ ),
+ 'random-default' => array(
+ 'type' => 'boolean',
+ ),
+ 'width' => array(
+ 'type' => 'integer',
+ ),
+ 'height' => array(
+ 'type' => 'integer',
+ ),
+ 'flex-height' => array(
+ 'type' => 'boolean',
+ ),
+ 'flex-width' => array(
+ 'type' => 'boolean',
+ ),
+ 'default-text-color' => array(
+ 'type' => 'string',
+ ),
+ 'header-text' => array(
+ 'type' => 'boolean',
+ ),
+ 'uploads' => array(
+ 'type' => 'boolean',
+ ),
+ 'video' => array(
+ 'type' => 'boolean',
+ ),
+ ),
+ ),
+ ),
+ )
+ );
+ register_theme_feature(
+ 'custom-logo',
+ array(
+ 'type' => 'object',
+ 'description' => __( 'Custom logo if defined by the theme.' ),
+ 'show_in_rest' => array(
+ 'schema' => array(
+ 'properties' => array(
+ 'width' => array(
+ 'type' => 'integer',
+ ),
+ 'height' => array(
+ 'type' => 'integer',
+ ),
+ 'flex-width' => array(
+ 'type' => 'boolean',
+ ),
+ 'flex-height' => array(
+ 'type' => 'boolean',
+ ),
+ 'header-text' => array(
+ 'type' => 'array',
+ 'items' => array(
+ 'type' => 'string',
+ ),
+ ),
+ 'unlink-homepage-logo' => array(
+ 'type' => 'boolean',
+ ),
+ ),
+ ),
+ ),
+ )
+ );
+ register_theme_feature(
+ 'customize-selective-refresh-widgets',
+ array(
+ 'description' => __( 'Whether the theme enables Selective Refresh for Widgets being managed with the Customizer.' ),
+ 'show_in_rest' => true,
+ )
+ );
+ register_theme_feature(
+ 'dark-editor-style',
+ array(
+ 'description' => __( 'Whether theme opts in to the dark editor style UI.' ),
+ 'show_in_rest' => true,
+ )
+ );
+ register_theme_feature(
+ 'disable-custom-colors',
+ array(
+ 'description' => __( 'Whether the theme disables custom colors.' ),
+ 'show_in_rest' => true,
+ )
+ );
+ register_theme_feature(
+ 'disable-custom-font-sizes',
+ array(
+ 'description' => __( 'Whether the theme disables custom font sizes.' ),
+ 'show_in_rest' => true,
+ )
+ );
+ register_theme_feature(
+ 'disable-custom-gradients',
+ array(
+ 'description' => __( 'Whether the theme disables custom gradients.' ),
+ 'show_in_rest' => true,
+ )
+ );
+ register_theme_feature(
+ 'editor-color-palette',
+ array(
+ 'type' => 'array',
+ 'description' => __( 'Custom color palette if defined by the theme.' ),
+ 'show_in_rest' => array(
+ 'schema' => array(
+ 'items' => array(
+ 'type' => 'object',
+ 'properties' => array(
+ 'name' => array(
+ 'type' => 'string',
+ ),
+ 'slug' => array(
+ 'type' => 'string',
+ ),
+ 'color' => array(
+ 'type' => 'string',
+ ),
+ ),
+ ),
+ ),
+ ),
+ )
+ );
+ register_theme_feature(
+ 'editor-font-sizes',
+ array(
+ 'type' => 'array',
+ 'description' => __( 'Custom font sizes if defined by the theme.' ),
+ 'show_in_rest' => array(
+ 'schema' => array(
+ 'items' => array(
+ 'type' => 'object',
+ 'properties' => array(
+ 'name' => array(
+ 'type' => 'string',
+ ),
+ 'size' => array(
+ 'type' => 'number',
+ ),
+ 'slug' => array(
+ 'type' => 'string',
+ ),
+ ),
+ ),
+ ),
+ ),
+ )
+ );
+ register_theme_feature(
+ 'editor-gradient-presets',
+ array(
+ 'type' => 'array',
+ 'description' => __( 'Custom gradient presets if defined by the theme.' ),
+ 'show_in_rest' => array(
+ 'schema' => array(
+ 'items' => array(
+ 'type' => 'object',
+ 'properties' => array(
+ 'name' => array(
+ 'type' => 'string',
+ ),
+ 'gradient' => array(
+ 'type' => 'string',
+ ),
+ 'slug' => array(
+ 'type' => 'string',
+ ),
+ ),
+ ),
+ ),
+ ),
+ )
+ );
+ register_theme_feature(
+ 'editor-styles',
+ array(
+ 'description' => __( 'Whether theme opts in to the editor styles CSS wrapper.' ),
+ 'show_in_rest' => true,
+ )
+ );
+ register_theme_feature(
+ 'html5',
+ array(
+ 'type' => 'array',
+ 'description' => __( 'Allows use of HTML5 markup for search forms, comment forms, comment lists, gallery, and caption.' ),
+ 'show_in_rest' => array(
+ 'schema' => array(
+ 'items' => array(
+ 'type' => 'string',
+ 'enum' => array(
+ 'search-form',
+ 'comment-form',
+ 'comment-list',
+ 'gallery',
+ 'caption',
+ 'script',
+ 'style',
+ ),
+ ),
+ ),
+ ),
+ )
+ );
+ register_theme_feature(
+ 'post-formats',
+ array(
+ 'type' => 'array',
+ 'description' => __( 'Post formats supported.' ),
+ 'show_in_rest' => array(
+ 'name' => 'formats',
+ 'schema' => array(
+ 'items' => array(
+ 'type' => 'string',
+ 'enum' => get_post_format_slugs(),
+ ),
+ 'default' => array( 'standard' ),
+ ),
+ 'prepare_callback' => static function ( $formats ) {
+ $formats = is_array( $formats ) ? array_values( $formats[0] ) : array();
+ $formats = array_merge( array( 'standard' ), $formats );
+
+ return $formats;
+ },
+ ),
+ )
+ );
+ register_theme_feature(
+ 'post-thumbnails',
+ array(
+ 'type' => 'array',
+ 'description' => __( 'The post types that support thumbnails or true if all post types are supported.' ),
+ 'show_in_rest' => array(
+ 'type' => array( 'boolean', 'array' ),
+ 'schema' => array(
+ 'items' => array(
+ 'type' => 'string',
+ ),
+ ),
+ ),
+ )
+ );
+ register_theme_feature(
+ 'responsive-embeds',
+ array(
+ 'description' => __( 'Whether the theme supports responsive embedded content.' ),
+ 'show_in_rest' => true,
+ )
+ );
+ register_theme_feature(
+ 'title-tag',
+ array(
+ 'description' => __( 'Whether the theme can manage the document title tag.' ),
+ 'show_in_rest' => true,
+ )
+ );
+ register_theme_feature(
+ 'wp-block-styles',
+ array(
+ 'description' => __( 'Whether theme opts in to default WordPress block styles for viewing.' ),
+ 'show_in_rest' => true,
+ )
+ );
+}