Themes: Introduce register_theme_feature API.
Currently themes can declare support for a given feature by using add_theme_support(). This commit adds a register_theme_feature() API that allows plugins and WordPress Core to declare a list of available features that themes can support. The REST API uses this to expose a theme's supported features if the feature has been registered with "show_in_rest" set to true. Props kadamwhite, spacedmonkey, williampatton, desrosj, TimothyBlynJacobs. Fixes #49406. git-svn-id: https://develop.svn.wordpress.org/trunk@48171 602fd350-edb4-49c9-b593-d223f7449a82
This commit is contained in:
parent
9568134d7b
commit
9a604011ee
@ -470,6 +470,7 @@ add_action( 'transition_post_status', '_wp_customize_publish_changeset', 10, 3 )
|
||||
add_action( 'admin_enqueue_scripts', '_wp_customize_loader_settings' );
|
||||
add_action( 'delete_attachment', '_delete_attachment_theme_mod' );
|
||||
add_action( 'transition_post_status', '_wp_keep_alive_customize_changeset_dependent_auto_drafts', 20, 3 );
|
||||
add_action( 'setup_theme', 'create_initial_theme_features', 0 );
|
||||
|
||||
// Calendar widget cache.
|
||||
add_action( 'save_post', 'delete_get_calendar_cache' );
|
||||
|
@ -1779,3 +1779,35 @@ function rest_filter_response_by_context( $data, $schema, $context ) {
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the "additionalProperties" to false by default for all object definitions in the schema.
|
||||
*
|
||||
* @since 5.5.0
|
||||
*
|
||||
* @param array $schema The schema to modify.
|
||||
* @return array The modified schema.
|
||||
*/
|
||||
function rest_default_additional_properties_to_false( $schema ) {
|
||||
$type = (array) $schema['type'];
|
||||
|
||||
if ( in_array( 'object', $type, true ) ) {
|
||||
if ( isset( $schema['properties'] ) ) {
|
||||
foreach ( $schema['properties'] as $key => $child_schema ) {
|
||||
$schema['properties'][ $key ] = rest_default_additional_properties_to_false( $child_schema );
|
||||
}
|
||||
}
|
||||
|
||||
if ( ! isset( $schema['additionalProperties'] ) ) {
|
||||
$schema['additionalProperties'] = false;
|
||||
}
|
||||
}
|
||||
|
||||
if ( in_array( 'array', $type, true ) ) {
|
||||
if ( isset( $schema['items'] ) ) {
|
||||
$schema['items'] = rest_default_additional_properties_to_false( $schema['items'] );
|
||||
}
|
||||
}
|
||||
|
||||
return $schema;
|
||||
}
|
||||
|
@ -167,50 +167,38 @@ class WP_REST_Themes_Controller extends WP_REST_Controller {
|
||||
}
|
||||
|
||||
if ( rest_is_field_included( 'theme_supports', $fields ) ) {
|
||||
$item_schemas = $this->get_item_schema();
|
||||
$theme_supports = $item_schemas['properties']['theme_supports']['properties'];
|
||||
foreach ( $theme_supports as $name => $schema ) {
|
||||
foreach ( get_registered_theme_features() as $feature => $config ) {
|
||||
if ( ! is_array( $config['show_in_rest'] ) ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$name = $config['show_in_rest']['name'];
|
||||
|
||||
if ( ! rest_is_field_included( "theme_supports.{$name}", $fields ) ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ( 'formats' === $name ) {
|
||||
if ( ! current_theme_supports( $feature ) ) {
|
||||
$data['theme_supports'][ $name ] = $config['show_in_rest']['schema']['default'];
|
||||
continue;
|
||||
}
|
||||
|
||||
if ( ! current_theme_supports( $name ) ) {
|
||||
$data['theme_supports'][ $name ] = false;
|
||||
$support = get_theme_support( $feature );
|
||||
|
||||
if ( isset( $config['show_in_rest']['prepare_callback'] ) ) {
|
||||
$prepare = $config['show_in_rest']['prepare_callback'];
|
||||
} else {
|
||||
$prepare = array( $this, 'prepare_theme_support' );
|
||||
}
|
||||
|
||||
$prepared = $prepare( $support, $config, $feature, $request );
|
||||
|
||||
if ( is_wp_error( $prepared ) ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ( 'boolean' === $schema['type'] ) {
|
||||
$data['theme_supports'][ $name ] = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
$support = get_theme_support( $name );
|
||||
|
||||
if ( is_array( $support ) ) {
|
||||
// None of the Core theme supports have variadic args.
|
||||
$support = $support[0];
|
||||
|
||||
// Core multi-type theme-support schema definitions always list boolean first.
|
||||
if ( is_array( $schema['type'] ) && 'boolean' === $schema['type'][0] ) {
|
||||
// Pass the non-boolean type through to the sanitizer, which cannot itself
|
||||
// determine the intended type if the value is invalid (for example if an
|
||||
// object includes non-safelisted properties).
|
||||
$schema['type'] = $schema['type'][1];
|
||||
}
|
||||
}
|
||||
|
||||
$data['theme_supports'][ $name ] = rest_sanitize_value_from_schema( $support, $schema );
|
||||
$data['theme_supports'][ $name ] = $prepared;
|
||||
}
|
||||
|
||||
$formats = get_theme_support( 'post-formats' );
|
||||
$formats = is_array( $formats ) ? array_values( $formats[0] ) : array();
|
||||
$formats = array_merge( array( 'standard' ), $formats );
|
||||
|
||||
$data['theme_supports']['formats'] = $formats;
|
||||
}
|
||||
|
||||
$data = $this->add_additional_fields_to_object( $data, $request );
|
||||
@ -230,6 +218,41 @@ class WP_REST_Themes_Controller extends WP_REST_Controller {
|
||||
return apply_filters( 'rest_prepare_theme', $response, $theme, $request );
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepares the theme support value for inclusion in the REST API response.
|
||||
*
|
||||
* @since 5.5.0
|
||||
*
|
||||
* @param mixed $support The raw value from {@see get_theme_support()}
|
||||
* @param array $args The feature's registration args.
|
||||
* @param string $feature The feature name.
|
||||
* @param WP_REST_Request $request The request object.
|
||||
* @return mixed The prepared support value.
|
||||
*/
|
||||
protected function prepare_theme_support( $support, $args, $feature, $request ) {
|
||||
$schema = $args['show_in_rest']['schema'];
|
||||
|
||||
if ( 'boolean' === $schema['type'] ) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if ( is_array( $support ) ) {
|
||||
if ( ! $args['variadic'] ) {
|
||||
$support = $support[0];
|
||||
}
|
||||
|
||||
// Multi-type theme-support schema definitions always list boolean first.
|
||||
if ( is_array( $schema['type'] ) && 'boolean' === $schema['type'][0] ) {
|
||||
// Pass the non-boolean type through to the sanitizer, which cannot itself
|
||||
// determine the intended type if the value is invalid (for example if an
|
||||
// object includes non-safelisted properties). See #50300.
|
||||
$schema['type'] = $schema['type'][1];
|
||||
}
|
||||
}
|
||||
|
||||
return rest_sanitize_value_from_schema( $support, $schema );
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the theme's schema, conforming to JSON Schema.
|
||||
*
|
||||
@ -362,267 +385,7 @@ class WP_REST_Themes_Controller extends WP_REST_Controller {
|
||||
'description' => __( 'Features supported by this theme.' ),
|
||||
'type' => 'object',
|
||||
'readonly' => true,
|
||||
'properties' => array(
|
||||
'align-wide' => array(
|
||||
'description' => __( 'Whether theme opts in to wide alignment CSS class.' ),
|
||||
'type' => 'boolean',
|
||||
),
|
||||
'automatic-feed-links' => array(
|
||||
'description' => __( 'Whether posts and comments RSS feed links are added to head.' ),
|
||||
'type' => 'boolean',
|
||||
),
|
||||
'custom-header' => array(
|
||||
'description' => __( 'Custom header if defined by the theme.' ),
|
||||
'type' => array( 'boolean', 'object' ),
|
||||
'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',
|
||||
),
|
||||
),
|
||||
'additionalProperties' => false,
|
||||
),
|
||||
'custom-background' => array(
|
||||
'description' => __( 'Custom background if defined by the theme.' ),
|
||||
'type' => array( 'boolean', 'object' ),
|
||||
'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',
|
||||
),
|
||||
),
|
||||
'additionalProperties' => false,
|
||||
),
|
||||
'custom-logo' => array(
|
||||
'description' => __( 'Custom logo if defined by the theme.' ),
|
||||
'type' => array( 'boolean', 'object' ),
|
||||
'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',
|
||||
),
|
||||
),
|
||||
),
|
||||
'additionalProperties' => false,
|
||||
),
|
||||
'customize-selective-refresh-widgets' => array(
|
||||
'description' => __( 'Whether the theme enables Selective Refresh for Widgets being managed with the Customizer.' ),
|
||||
'type' => 'boolean',
|
||||
),
|
||||
'dark-editor-style' => array(
|
||||
'description' => __( 'Whether theme opts in to the dark editor style UI.' ),
|
||||
'type' => 'boolean',
|
||||
),
|
||||
'disable-custom-colors' => array(
|
||||
'description' => __( 'Whether the theme disables custom colors.' ),
|
||||
'type' => 'boolean',
|
||||
),
|
||||
'disable-custom-font-sizes' => array(
|
||||
'description' => __( 'Whether the theme disables custom font sizes.' ),
|
||||
'type' => 'boolean',
|
||||
),
|
||||
'disable-custom-gradients' => array(
|
||||
'description' => __( 'Whether the theme disables custom gradients.' ),
|
||||
'type' => 'boolean',
|
||||
),
|
||||
'editor-color-palette' => array(
|
||||
'description' => __( 'Custom color palette if defined by the theme.' ),
|
||||
'type' => array( 'boolean', 'array' ),
|
||||
'items' => array(
|
||||
'type' => 'object',
|
||||
'properties' => array(
|
||||
'name' => array(
|
||||
'type' => 'string',
|
||||
),
|
||||
'slug' => array(
|
||||
'type' => 'string',
|
||||
),
|
||||
'color' => array(
|
||||
'type' => 'string',
|
||||
),
|
||||
),
|
||||
'additionalProperties' => false,
|
||||
),
|
||||
),
|
||||
'editor-font-sizes' => array(
|
||||
'description' => __( 'Custom font sizes if defined by the theme.' ),
|
||||
'type' => array( 'boolean', 'array' ),
|
||||
'items' => array(
|
||||
'type' => 'object',
|
||||
'properties' => array(
|
||||
'name' => array(
|
||||
'type' => 'string',
|
||||
),
|
||||
'size' => array(
|
||||
'type' => 'number',
|
||||
),
|
||||
'slug' => array(
|
||||
'type' => 'string',
|
||||
),
|
||||
),
|
||||
'additionalProperties' => false,
|
||||
),
|
||||
),
|
||||
'editor-gradient-presets' => array(
|
||||
'description' => __( 'Custom gradient presets if defined by the theme.' ),
|
||||
'type' => array( 'boolean', 'array' ),
|
||||
'items' => array(
|
||||
'type' => 'object',
|
||||
'properties' => array(
|
||||
'name' => array(
|
||||
'type' => 'string',
|
||||
),
|
||||
'gradient' => array(
|
||||
'type' => 'string',
|
||||
),
|
||||
'slug' => array(
|
||||
'type' => 'string',
|
||||
),
|
||||
),
|
||||
'additionalProperties' => false,
|
||||
),
|
||||
),
|
||||
'editor-styles' => array(
|
||||
'description' => __( 'Whether theme opts in to the editor styles CSS wrapper.' ),
|
||||
'type' => 'boolean',
|
||||
),
|
||||
'formats' => array(
|
||||
'description' => __( 'Post formats supported.' ),
|
||||
'type' => 'array',
|
||||
'items' => array(
|
||||
'type' => 'string',
|
||||
'enum' => get_post_format_slugs(),
|
||||
),
|
||||
),
|
||||
'html5' => array(
|
||||
'description' => __( 'Allows use of html5 markup for search forms, comment forms, comment lists, gallery, and caption.' ),
|
||||
'type' => array( 'boolean', 'array' ),
|
||||
'items' => array(
|
||||
'type' => 'string',
|
||||
'enum' => array(
|
||||
'search-form',
|
||||
'comment-form',
|
||||
'comment-list',
|
||||
'gallery',
|
||||
'caption',
|
||||
'script',
|
||||
'style',
|
||||
),
|
||||
),
|
||||
),
|
||||
'post-thumbnails' => array(
|
||||
'description' => __( 'Whether the theme supports post thumbnails.' ),
|
||||
'type' => array( 'boolean', 'array' ),
|
||||
'items' => array(
|
||||
'type' => 'string',
|
||||
),
|
||||
),
|
||||
'responsive-embeds' => array(
|
||||
'description' => __( 'Whether the theme supports responsive embedded content.' ),
|
||||
'type' => 'boolean',
|
||||
),
|
||||
'title-tag' => array(
|
||||
'description' => __( 'Whether the theme can manage the document title tag.' ),
|
||||
'type' => 'boolean',
|
||||
),
|
||||
'wp-block-styles' => array(
|
||||
'description' => __( 'Whether theme opts in to default WordPress block styles for viewing.' ),
|
||||
'type' => 'boolean',
|
||||
),
|
||||
),
|
||||
'properties' => array(),
|
||||
),
|
||||
'theme_uri' => array(
|
||||
'description' => __( 'The URI of the theme\'s webpage.' ),
|
||||
@ -649,6 +412,16 @@ class WP_REST_Themes_Controller extends WP_REST_Controller {
|
||||
),
|
||||
);
|
||||
|
||||
foreach ( get_registered_theme_features() as $feature => $config ) {
|
||||
if ( ! is_array( $config['show_in_rest'] ) ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$name = $config['show_in_rest']['name'];
|
||||
|
||||
$schema['properties']['theme_supports']['properties'][ $name ] = $config['show_in_rest']['schema'];
|
||||
}
|
||||
|
||||
$this->schema = $schema;
|
||||
|
||||
return $this->add_additional_fields_schema( $this->schema );
|
||||
|
@ -2993,6 +2993,156 @@ function require_if_theme_supports( $feature, $include ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers a theme feature for use in {@see 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
|
||||
*
|
||||
* @global $_wp_registered_theme_features
|
||||
*
|
||||
* @param string $feature The name uniquely identifying the feature.
|
||||
* @param array $args {
|
||||
* Data used to describe the theme
|
||||
*
|
||||
* @type string $type The type of data associated with this feature. Defaults to 'boolean'.
|
||||
* Valid values are 'string', 'boolean', 'integer', 'number', 'array', and 'object'.
|
||||
* @type boolean $variadic Does this feature utilize the variadic support of {@see 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 use 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', __( 'The prepare_callback must be a callable function.' ) );
|
||||
}
|
||||
|
||||
$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 $_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 $_wp_registered_theme_features
|
||||
*
|
||||
* @param string $feature The feature name.
|
||||
* @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.
|
||||
*
|
||||
@ -3462,3 +3612,371 @@ function _wp_keep_alive_customize_changeset_dependent_auto_drafts( $new_status,
|
||||
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',
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
)
|
||||
);
|
||||
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,
|
||||
)
|
||||
);
|
||||
}
|
||||
|
@ -260,7 +260,6 @@ class WP_Test_REST_Themes_Controller extends WP_Test_REST_Controller_Testcase {
|
||||
$this->assertArrayHasKey( 'version', $properties );
|
||||
|
||||
$theme_supports = $properties['theme_supports']['properties'];
|
||||
$this->assertEquals( 20, count( $theme_supports ) );
|
||||
$this->assertArrayHasKey( 'align-wide', $theme_supports );
|
||||
$this->assertArrayHasKey( 'automatic-feed-links', $theme_supports );
|
||||
$this->assertArrayHasKey( 'custom-header', $theme_supports );
|
||||
@ -281,6 +280,7 @@ class WP_Test_REST_Themes_Controller extends WP_Test_REST_Controller_Testcase {
|
||||
$this->assertArrayHasKey( 'responsive-embeds', $theme_supports );
|
||||
$this->assertArrayHasKey( 'title-tag', $theme_supports );
|
||||
$this->assertArrayHasKey( 'wp-block-styles', $theme_supports );
|
||||
$this->assertCount( 20, $theme_supports );
|
||||
}
|
||||
|
||||
/**
|
||||
@ -997,6 +997,32 @@ class WP_Test_REST_Themes_Controller extends WP_Test_REST_Controller_Testcase {
|
||||
$this->assertEquals( array( 'post' ), $result[0]['theme_supports']['post-thumbnails'] );
|
||||
}
|
||||
|
||||
/**
|
||||
* @ticket 49406
|
||||
*/
|
||||
public function test_variadic_theme_support() {
|
||||
register_theme_feature(
|
||||
'test-feature',
|
||||
array(
|
||||
'type' => 'array',
|
||||
'variadic' => true,
|
||||
'show_in_rest' => array(
|
||||
'schema' => array(
|
||||
'items' => array(
|
||||
'type' => 'string',
|
||||
),
|
||||
),
|
||||
),
|
||||
)
|
||||
);
|
||||
add_theme_support( 'test-feature', 'a', 'b', 'c' );
|
||||
|
||||
$response = self::perform_active_theme_request();
|
||||
$result = $response->get_data();
|
||||
$this->assertTrue( isset( $result[0]['theme_supports'] ) );
|
||||
$this->assertEquals( array( 'a', 'b', 'c' ), $result[0]['theme_supports']['test-feature'] );
|
||||
}
|
||||
|
||||
/**
|
||||
* It should be possible to register custom fields to the endpoint.
|
||||
*
|
||||
|
@ -415,4 +415,288 @@ class Tests_Theme extends WP_UnitTestCase {
|
||||
$this->assertEquals( 'trash', get_post_status( $nav_created_post_ids[0] ) );
|
||||
$this->assertEquals( 'private', get_post_status( $nav_created_post_ids[1] ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* @ticket 49406
|
||||
*/
|
||||
public function test_register_theme_support_defaults() {
|
||||
$registered = register_theme_feature( 'test-feature' );
|
||||
$this->assertTrue( $registered );
|
||||
|
||||
$expected = array(
|
||||
'type' => 'boolean',
|
||||
'variadic' => false,
|
||||
'description' => '',
|
||||
'show_in_rest' => false,
|
||||
);
|
||||
$this->assertEqualSets( $expected, get_registered_theme_feature( 'test-feature' ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* @ticket 49406
|
||||
*/
|
||||
public function test_register_theme_support_explicit() {
|
||||
$args = array(
|
||||
'type' => 'array',
|
||||
'variadic' => true,
|
||||
'description' => 'My Feature',
|
||||
'show_in_rest' => array(
|
||||
'schema' => array(
|
||||
'items' => array(
|
||||
'type' => 'string',
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
register_theme_feature( 'test-feature', $args );
|
||||
$actual = get_registered_theme_feature( 'test-feature' );
|
||||
|
||||
$this->assertEquals( 'array', $actual['type'] );
|
||||
$this->assertTrue( $actual['variadic'] );
|
||||
$this->assertEquals( 'My Feature', $actual['description'] );
|
||||
$this->assertEquals( array( 'type' => 'string' ), $actual['show_in_rest']['schema']['items'] );
|
||||
}
|
||||
|
||||
/**
|
||||
* @ticket 49406
|
||||
*/
|
||||
public function test_register_theme_support_upgrades_show_in_rest() {
|
||||
register_theme_feature( 'test-feature', array( 'show_in_rest' => true ) );
|
||||
|
||||
$expected = array(
|
||||
'schema' => array(
|
||||
'type' => 'boolean',
|
||||
'description' => '',
|
||||
'default' => false,
|
||||
),
|
||||
'name' => 'test-feature',
|
||||
'prepare_callback' => null,
|
||||
);
|
||||
$actual = get_registered_theme_feature( 'test-feature' )['show_in_rest'];
|
||||
|
||||
$this->assertEqualSets( $expected, $actual );
|
||||
}
|
||||
|
||||
/**
|
||||
* @ticket 49406
|
||||
*/
|
||||
public function test_register_theme_support_fills_schema() {
|
||||
register_theme_feature(
|
||||
'test-feature',
|
||||
array(
|
||||
'type' => 'array',
|
||||
'description' => 'Cool Feature',
|
||||
'show_in_rest' => array(
|
||||
'schema' => array(
|
||||
'items' => array(
|
||||
'type' => 'string',
|
||||
),
|
||||
'minItems' => 1,
|
||||
),
|
||||
),
|
||||
)
|
||||
);
|
||||
|
||||
$expected = array(
|
||||
'description' => 'Cool Feature',
|
||||
'type' => array( 'boolean', 'array' ),
|
||||
'items' => array(
|
||||
'type' => 'string',
|
||||
),
|
||||
'minItems' => 1,
|
||||
'default' => false,
|
||||
);
|
||||
$actual = get_registered_theme_feature( 'test-feature' )['show_in_rest']['schema'];
|
||||
|
||||
$this->assertEqualSets( $expected, $actual );
|
||||
}
|
||||
|
||||
/**
|
||||
* @ticket 49406
|
||||
*/
|
||||
public function test_register_theme_support_does_not_add_boolean_type_if_non_bool_default() {
|
||||
register_theme_feature(
|
||||
'test-feature',
|
||||
array(
|
||||
'type' => 'array',
|
||||
'show_in_rest' => array(
|
||||
'schema' => array(
|
||||
'items' => array(
|
||||
'type' => 'string',
|
||||
),
|
||||
'default' => array( 'standard' ),
|
||||
),
|
||||
),
|
||||
)
|
||||
);
|
||||
|
||||
$actual = get_registered_theme_feature( 'test-feature' )['show_in_rest']['schema']['type'];
|
||||
$this->assertEquals( 'array', $actual );
|
||||
}
|
||||
|
||||
/**
|
||||
* @ticket 49406
|
||||
*/
|
||||
public function test_register_theme_support_defaults_additional_properties_to_false() {
|
||||
register_theme_feature(
|
||||
'test-feature',
|
||||
array(
|
||||
'type' => 'object',
|
||||
'description' => 'Cool Feature',
|
||||
'show_in_rest' => array(
|
||||
'schema' => array(
|
||||
'properties' => array(
|
||||
'a' => array(
|
||||
'type' => 'string',
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
)
|
||||
);
|
||||
|
||||
$actual = get_registered_theme_feature( 'test-feature' )['show_in_rest']['schema'];
|
||||
|
||||
$this->assertArrayHasKey( 'additionalProperties', $actual );
|
||||
$this->assertFalse( $actual['additionalProperties'] );
|
||||
}
|
||||
|
||||
/**
|
||||
* @ticket 49406
|
||||
*/
|
||||
public function test_register_theme_support_with_additional_properties() {
|
||||
register_theme_feature(
|
||||
'test-feature',
|
||||
array(
|
||||
'type' => 'object',
|
||||
'description' => 'Cool Feature',
|
||||
'show_in_rest' => array(
|
||||
'schema' => array(
|
||||
'properties' => array(),
|
||||
'additionalProperties' => array(
|
||||
'type' => 'string',
|
||||
),
|
||||
),
|
||||
),
|
||||
)
|
||||
);
|
||||
|
||||
$expected = array(
|
||||
'type' => 'string',
|
||||
);
|
||||
$actual = get_registered_theme_feature( 'test-feature' )['show_in_rest']['schema']['additionalProperties'];
|
||||
|
||||
$this->assertEqualSets( $expected, $actual );
|
||||
}
|
||||
|
||||
/**
|
||||
* @ticket 49406
|
||||
*/
|
||||
public function test_register_theme_support_defaults_additional_properties_to_false_in_array() {
|
||||
register_theme_feature(
|
||||
'test-feature',
|
||||
array(
|
||||
'type' => 'array',
|
||||
'description' => 'Cool Feature',
|
||||
'show_in_rest' => array(
|
||||
'schema' => array(
|
||||
'items' => array(
|
||||
'type' => 'object',
|
||||
'properties' => array(
|
||||
'a' => array(
|
||||
'type' => 'string',
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
)
|
||||
);
|
||||
|
||||
$actual = get_registered_theme_feature( 'test-feature' )['show_in_rest']['schema']['items'];
|
||||
|
||||
$this->assertArrayHasKey( 'additionalProperties', $actual );
|
||||
$this->assertFalse( $actual['additionalProperties'] );
|
||||
}
|
||||
|
||||
/**
|
||||
* @ticket 49406
|
||||
* @dataProvider _dp_register_theme_support_validation
|
||||
* @param string $error_code The error code expected.
|
||||
* @param array $args The args to register.
|
||||
*/
|
||||
public function test_register_theme_support_validation( $error_code, $args ) {
|
||||
$registered = register_theme_feature( 'test-feature', $args );
|
||||
|
||||
$this->assertWPError( $registered );
|
||||
$this->assertEquals( $error_code, $registered->get_error_code() );
|
||||
}
|
||||
|
||||
public function _dp_register_theme_support_validation() {
|
||||
return array(
|
||||
array(
|
||||
'invalid_type',
|
||||
array(
|
||||
'type' => 'float',
|
||||
),
|
||||
),
|
||||
array(
|
||||
'invalid_type',
|
||||
array(
|
||||
'type' => array( 'string' ),
|
||||
),
|
||||
),
|
||||
array(
|
||||
'variadic_must_be_array',
|
||||
array(
|
||||
'variadic' => true,
|
||||
),
|
||||
),
|
||||
array(
|
||||
'missing_schema',
|
||||
array(
|
||||
'type' => 'object',
|
||||
'show_in_rest' => true,
|
||||
),
|
||||
),
|
||||
array(
|
||||
'missing_schema',
|
||||
array(
|
||||
'type' => 'array',
|
||||
'show_in_rest' => true,
|
||||
),
|
||||
),
|
||||
array(
|
||||
'missing_schema_items',
|
||||
array(
|
||||
'type' => 'array',
|
||||
'show_in_rest' => array(
|
||||
'schema' => array(
|
||||
'type' => 'array',
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
array(
|
||||
'missing_schema_properties',
|
||||
array(
|
||||
'type' => 'object',
|
||||
'show_in_rest' => array(
|
||||
'schema' => array(
|
||||
'type' => 'object',
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
array(
|
||||
'invalid_rest_prepare_callback',
|
||||
array(
|
||||
'show_in_rest' => array(
|
||||
'prepare_callback' => 'this is not a valid function',
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user