Make WordPress Core

Changeset 48171


Ignore:
Timestamp:
06/25/2020 10:11:09 PM (6 years ago)
Author:
TimothyBlynJacobs
Message:

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.

Location:
trunk
Files:
6 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/wp-includes/default-filters.php

    r48121 r48171  
    471471add_action( 'delete_attachment', '_delete_attachment_theme_mod' );
    472472add_action( 'transition_post_status', '_wp_keep_alive_customize_changeset_dependent_auto_drafts', 20, 3 );
     473add_action( 'setup_theme', 'create_initial_theme_features', 0 );
    473474
    474475// Calendar widget cache.
  • trunk/src/wp-includes/rest-api.php

    r48150 r48171  
    17801780        return $data;
    17811781}
     1782
     1783/**
     1784 * Sets the "additionalProperties" to false by default for all object definitions in the schema.
     1785 *
     1786 * @since 5.5.0
     1787 *
     1788 * @param array $schema The schema to modify.
     1789 * @return array The modified schema.
     1790 */
     1791function rest_default_additional_properties_to_false( $schema ) {
     1792        $type = (array) $schema['type'];
     1793
     1794        if ( in_array( 'object', $type, true ) ) {
     1795                if ( isset( $schema['properties'] ) ) {
     1796                        foreach ( $schema['properties'] as $key => $child_schema ) {
     1797                                $schema['properties'][ $key ] = rest_default_additional_properties_to_false( $child_schema );
     1798                        }
     1799                }
     1800
     1801                if ( ! isset( $schema['additionalProperties'] ) ) {
     1802                        $schema['additionalProperties'] = false;
     1803                }
     1804        }
     1805
     1806        if ( in_array( 'array', $type, true ) ) {
     1807                if ( isset( $schema['items'] ) ) {
     1808                        $schema['items'] = rest_default_additional_properties_to_false( $schema['items'] );
     1809                }
     1810        }
     1811
     1812        return $schema;
     1813}
  • trunk/src/wp-includes/rest-api/endpoints/class-wp-rest-themes-controller.php

    r47929 r48171  
    168168
    169169                if ( rest_is_field_included( 'theme_supports', $fields ) ) {
    170                         $item_schemas   = $this->get_item_schema();
    171                         $theme_supports = $item_schemas['properties']['theme_supports']['properties'];
    172                         foreach ( $theme_supports as $name => $schema ) {
     170                        foreach ( get_registered_theme_features() as $feature => $config ) {
     171                                if ( ! is_array( $config['show_in_rest'] ) ) {
     172                                        continue;
     173                                }
     174
     175                                $name = $config['show_in_rest']['name'];
     176
    173177                                if ( ! rest_is_field_included( "theme_supports.{$name}", $fields ) ) {
    174178                                        continue;
    175179                                }
    176180
    177                                 if ( 'formats' === $name ) {
     181                                if ( ! current_theme_supports( $feature ) ) {
     182                                        $data['theme_supports'][ $name ] = $config['show_in_rest']['schema']['default'];
    178183                                        continue;
    179184                                }
    180185
    181                                 if ( ! current_theme_supports( $name ) ) {
    182                                         $data['theme_supports'][ $name ] = false;
     186                                $support = get_theme_support( $feature );
     187
     188                                if ( isset( $config['show_in_rest']['prepare_callback'] ) ) {
     189                                        $prepare = $config['show_in_rest']['prepare_callback'];
     190                                } else {
     191                                        $prepare = array( $this, 'prepare_theme_support' );
     192                                }
     193
     194                                $prepared = $prepare( $support, $config, $feature, $request );
     195
     196                                if ( is_wp_error( $prepared ) ) {
    183197                                        continue;
    184198                                }
    185199
    186                                 if ( 'boolean' === $schema['type'] ) {
    187                                         $data['theme_supports'][ $name ] = true;
    188                                         continue;
    189                                 }
    190 
    191                                 $support = get_theme_support( $name );
    192 
    193                                 if ( is_array( $support ) ) {
    194                                         // None of the Core theme supports have variadic args.
    195                                         $support = $support[0];
    196 
    197                                         // Core multi-type theme-support schema definitions always list boolean first.
    198                                         if ( is_array( $schema['type'] ) && 'boolean' === $schema['type'][0] ) {
    199                                                 // Pass the non-boolean type through to the sanitizer, which cannot itself
    200                                                 // determine the intended type if the value is invalid (for example if an
    201                                                 // object includes non-safelisted properties).
    202                                                 $schema['type'] = $schema['type'][1];
    203                                         }
    204                                 }
    205 
    206                                 $data['theme_supports'][ $name ] = rest_sanitize_value_from_schema( $support, $schema );
    207                         }
    208 
    209                         $formats = get_theme_support( 'post-formats' );
    210                         $formats = is_array( $formats ) ? array_values( $formats[0] ) : array();
    211                         $formats = array_merge( array( 'standard' ), $formats );
    212 
    213                         $data['theme_supports']['formats'] = $formats;
     200                                $data['theme_supports'][ $name ] = $prepared;
     201                        }
    214202                }
    215203
    … …  
    232220
    233221        /**
     222         * Prepares the theme support value for inclusion in the REST API response.
     223         *
     224         * @since 5.5.0
     225         *
     226         * @param mixed           $support The raw value from {@see get_theme_support()}
     227         * @param array           $args    The feature's registration args.
     228         * @param string          $feature The feature name.
     229         * @param WP_REST_Request $request The request object.
     230         * @return mixed The prepared support value.
     231         */
     232        protected function prepare_theme_support( $support, $args, $feature, $request ) {
     233                $schema = $args['show_in_rest']['schema'];
     234
     235                if ( 'boolean' === $schema['type'] ) {
     236                        return true;
     237                }
     238
     239                if ( is_array( $support ) ) {
     240                        if ( ! $args['variadic'] ) {
     241                                $support = $support[0];
     242                        }
     243
     244                        // Multi-type theme-support schema definitions always list boolean first.
     245                        if ( is_array( $schema['type'] ) && 'boolean' === $schema['type'][0] ) {
     246                                // Pass the non-boolean type through to the sanitizer, which cannot itself
     247                                // determine the intended type if the value is invalid (for example if an
     248                                // object includes non-safelisted properties). See #50300.
     249                                $schema['type'] = $schema['type'][1];
     250                        }
     251                }
     252
     253                return rest_sanitize_value_from_schema( $support, $schema );
     254        }
     255
     256        /**
    234257         * Retrieves the theme's schema, conforming to JSON Schema.
    235258         *
    … …  
    363386                                        'type'        => 'object',
    364387                                        'readonly'    => true,
    365                                         'properties'  => array(
    366                                                 'align-wide'                => array(
    367                                                         'description' => __( 'Whether theme opts in to wide alignment CSS class.' ),
    368                                                         'type'        => 'boolean',
    369                                                 ),
    370                                                 'automatic-feed-links'      => array(
    371                                                         'description' => __( 'Whether posts and comments RSS feed links are added to head.' ),
    372                                                         'type'        => 'boolean',
    373                                                 ),
    374                                                 'custom-header'             => array(
    375                                                         'description'          => __( 'Custom header if defined by the theme.' ),
    376                                                         'type'                 => array( 'boolean', 'object' ),
    377                                                         'properties'           => array(
    378                                                                 'default-image'      => array(
    379                                                                         'type'   => 'string',
    380                                                                         'format' => 'uri',
    381                                                                 ),
    382                                                                 'random-default'     => array(
    383                                                                         'type' => 'boolean',
    384                                                                 ),
    385                                                                 'width'              => array(
    386                                                                         'type' => 'integer',
    387                                                                 ),
    388                                                                 'height'             => array(
    389                                                                         'type' => 'integer',
    390                                                                 ),
    391                                                                 'flex-height'        => array(
    392                                                                         'type' => 'boolean',
    393                                                                 ),
    394                                                                 'flex-width'         => array(
    395                                                                         'type' => 'boolean',
    396                                                                 ),
    397                                                                 'default-text-color' => array(
    398                                                                         'type' => 'string',
    399                                                                 ),
    400                                                                 'header-text'        => array(
    401                                                                         'type' => 'boolean',
    402                                                                 ),
    403                                                                 'uploads'            => array(
    404                                                                         'type' => 'boolean',
    405                                                                 ),
    406                                                                 'video'              => array(
    407                                                                         'type' => 'boolean',
    408                                                                 ),
    409                                                         ),
    410                                                         'additionalProperties' => false,
    411                                                 ),
    412                                                 'custom-background'         => array(
    413                                                         'description'          => __( 'Custom background if defined by the theme.' ),
    414                                                         'type'                 => array( 'boolean', 'object' ),
    415                                                         'properties'           => array(
    416                                                                 'default-image'      => array(
    417                                                                         'type'   => 'string',
    418                                                                         'format' => 'uri',
    419                                                                 ),
    420                                                                 'default-preset'     => array(
    421                                                                         'type' => 'string',
    422                                                                         'enum' => array(
    423                                                                                 'default',
    424                                                                                 'fill',
    425                                                                                 'fit',
    426                                                                                 'repeat',
    427                                                                                 'custom',
    428                                                                         ),
    429                                                                 ),
    430                                                                 'default-position-x' => array(
    431                                                                         'type' => 'string',
    432                                                                         'enum' => array(
    433                                                                                 'left',
    434                                                                                 'center',
    435                                                                                 'right',
    436                                                                         ),
    437                                                                 ),
    438                                                                 'default-position-y' => array(
    439                                                                         'type' => 'string',
    440                                                                         'enum' => array(
    441                                                                                 'left',
    442                                                                                 'center',
    443                                                                                 'right',
    444                                                                         ),
    445                                                                 ),
    446                                                                 'default-size'       => array(
    447                                                                         'type' => 'string',
    448                                                                         'enum' => array(
    449                                                                                 'auto',
    450                                                                                 'contain',
    451                                                                                 'cover',
    452                                                                         ),
    453                                                                 ),
    454                                                                 'default-repeat'     => array(
    455                                                                         'type' => 'string',
    456                                                                         'enum' => array(
    457                                                                                 'repeat-x',
    458                                                                                 'repeat-y',
    459                                                                                 'repeat',
    460                                                                                 'no-repeat',
    461                                                                         ),
    462                                                                 ),
    463                                                                 'default-attachment' => array(
    464                                                                         'type' => 'string',
    465                                                                         'enum' => array(
    466                                                                                 'scroll',
    467                                                                                 'fixed',
    468                                                                         ),
    469                                                                 ),
    470                                                                 'default-color'      => array(
    471                                                                         'type' => 'string',
    472                                                                 ),
    473                                                         ),
    474                                                         'additionalProperties' => false,
    475                                                 ),
    476                                                 'custom-logo'               => array(
    477                                                         'description'          => __( 'Custom logo if defined by the theme.' ),
    478                                                         'type'                 => array( 'boolean', 'object' ),
    479                                                         'properties'           => array(
    480                                                                 'width'       => array(
    481                                                                         'type' => 'integer',
    482                                                                 ),
    483                                                                 'height'      => array(
    484                                                                         'type' => 'integer',
    485                                                                 ),
    486                                                                 'flex-width'  => array(
    487                                                                         'type' => 'boolean',
    488                                                                 ),
    489                                                                 'flex-height' => array(
    490                                                                         'type' => 'boolean',
    491                                                                 ),
    492                                                                 'header-text' => array(
    493                                                                         'type'  => 'array',
    494                                                                         'items' => array(
    495                                                                                 'type' => 'string',
    496                                                                         ),
    497                                                                 ),
    498                                                         ),
    499                                                         'additionalProperties' => false,
    500                                                 ),
    501                                                 'customize-selective-refresh-widgets' => array(
    502                                                         'description' => __( 'Whether the theme enables Selective Refresh for Widgets being managed with the Customizer.' ),
    503                                                         'type'        => 'boolean',
    504                                                 ),
    505                                                 'dark-editor-style'         => array(
    506                                                         'description' => __( 'Whether theme opts in to the dark editor style UI.' ),
    507                                                         'type'        => 'boolean',
    508                                                 ),
    509                                                 'disable-custom-colors'     => array(
    510                                                         'description' => __( 'Whether the theme disables custom colors.' ),
    511                                                         'type'        => 'boolean',
    512                                                 ),
    513                                                 'disable-custom-font-sizes' => array(
    514                                                         'description' => __( 'Whether the theme disables custom font sizes.' ),
    515                                                         'type'        => 'boolean',
    516                                                 ),
    517                                                 'disable-custom-gradients'  => array(
    518                                                         'description' => __( 'Whether the theme disables custom gradients.' ),
    519                                                         'type'        => 'boolean',
    520                                                 ),
    521                                                 'editor-color-palette'      => array(
    522                                                         'description' => __( 'Custom color palette if defined by the theme.' ),
    523                                                         'type'        => array( 'boolean', 'array' ),
    524                                                         'items'       => array(
    525                                                                 'type'                 => 'object',
    526                                                                 'properties'           => array(
    527                                                                         'name'  => array(
    528                                                                                 'type' => 'string',
    529                                                                         ),
    530                                                                         'slug'  => array(
    531                                                                                 'type' => 'string',
    532                                                                         ),
    533                                                                         'color' => array(
    534                                                                                 'type' => 'string',
    535                                                                         ),
    536                                                                 ),
    537                                                                 'additionalProperties' => false,
    538                                                         ),
    539                                                 ),
    540                                                 'editor-font-sizes'         => array(
    541                                                         'description' => __( 'Custom font sizes if defined by the theme.' ),
    542                                                         'type'        => array( 'boolean', 'array' ),
    543                                                         'items'       => array(
    544                                                                 'type'                 => 'object',
    545                                                                 'properties'           => array(
    546                                                                         'name' => array(
    547                                                                                 'type' => 'string',
    548                                                                         ),
    549                                                                         'size' => array(
    550                                                                                 'type' => 'number',
    551                                                                         ),
    552                                                                         'slug' => array(
    553                                                                                 'type' => 'string',
    554                                                                         ),
    555                                                                 ),
    556                                                                 'additionalProperties' => false,
    557                                                         ),
    558                                                 ),
    559                                                 'editor-gradient-presets'   => array(
    560                                                         'description' => __( 'Custom gradient presets if defined by the theme.' ),
    561                                                         'type'        => array( 'boolean', 'array' ),
    562                                                         'items'       => array(
    563                                                                 'type'                 => 'object',
    564                                                                 'properties'           => array(
    565                                                                         'name'     => array(
    566                                                                                 'type' => 'string',
    567                                                                         ),
    568                                                                         'gradient' => array(
    569                                                                                 'type' => 'string',
    570                                                                         ),
    571                                                                         'slug'     => array(
    572                                                                                 'type' => 'string',
    573                                                                         ),
    574                                                                 ),
    575                                                                 'additionalProperties' => false,
    576                                                         ),
    577                                                 ),
    578                                                 'editor-styles'             => array(
    579                                                         'description' => __( 'Whether theme opts in to the editor styles CSS wrapper.' ),
    580                                                         'type'        => 'boolean',
    581                                                 ),
    582                                                 'formats'                   => array(
    583                                                         'description' => __( 'Post formats supported.' ),
    584                                                         'type'        => 'array',
    585                                                         'items'       => array(
    586                                                                 'type' => 'string',
    587                                                                 'enum' => get_post_format_slugs(),
    588                                                         ),
    589                                                 ),
    590                                                 'html5'                     => array(
    591                                                         'description' => __( 'Allows use of html5 markup for search forms, comment forms, comment lists, gallery, and caption.' ),
    592                                                         'type'        => array( 'boolean', 'array' ),
    593                                                         'items'       => array(
    594                                                                 'type' => 'string',
    595                                                                 'enum' => array(
    596                                                                         'search-form',
    597                                                                         'comment-form',
    598                                                                         'comment-list',
    599                                                                         'gallery',
    600                                                                         'caption',
    601                                                                         'script',
    602                                                                         'style',
    603                                                                 ),
    604                                                         ),
    605                                                 ),
    606                                                 'post-thumbnails'           => array(
    607                                                         'description' => __( 'Whether the theme supports post thumbnails.' ),
    608                                                         'type'        => array( 'boolean', 'array' ),
    609                                                         'items'       => array(
    610                                                                 'type' => 'string',
    611                                                         ),
    612                                                 ),
    613                                                 'responsive-embeds'         => array(
    614                                                         'description' => __( 'Whether the theme supports responsive embedded content.' ),
    615                                                         'type'        => 'boolean',
    616                                                 ),
    617                                                 'title-tag'                 => array(
    618                                                         'description' => __( 'Whether the theme can manage the document title tag.' ),
    619                                                         'type'        => 'boolean',
    620                                                 ),
    621                                                 'wp-block-styles'           => array(
    622                                                         'description' => __( 'Whether theme opts in to default WordPress block styles for viewing.' ),
    623                                                         'type'        => 'boolean',
    624                                                 ),
    625                                         ),
     388                                        'properties'  => array(),
    626389                                ),
    627390                                'theme_uri'      => array(
    … …  
    649412                        ),
    650413                );
     414
     415                foreach ( get_registered_theme_features() as $feature => $config ) {
     416                        if ( ! is_array( $config['show_in_rest'] ) ) {
     417                                continue;
     418                        }
     419
     420                        $name = $config['show_in_rest']['name'];
     421
     422                        $schema['properties']['theme_supports']['properties'][ $name ] = $config['show_in_rest']['schema'];
     423                }
    651424
    652425                $this->schema = $schema;
  • trunk/src/wp-includes/theme.php

    r48169 r48171  
    29952995
    29962996/**
     2997 * Registers a theme feature for use in {@see add_theme_support}.
     2998 *
     2999 * This does not indicate that the current theme supports the feature, it only describes the feature's supported options.
     3000 *
     3001 * @since 5.5.0
     3002 *
     3003 * @global $_wp_registered_theme_features
     3004 *
     3005 * @param string $feature The name uniquely identifying the feature.
     3006 * @param array $args {
     3007 *      Data used to describe the theme
     3008 *
     3009 *      @type string     $type         The type of data associated with this feature. Defaults to 'boolean'.
     3010 *                                     Valid values are 'string', 'boolean', 'integer', 'number', 'array', and 'object'.
     3011 *      @type boolean    $variadic     Does this feature utilize the variadic support of {@see add_theme_support()},
     3012 *                                     or are all arguments specified as the second parameter. Must be used with the "array" type.
     3013 *      @type string     $description  A short description of the feature. Included in the Themes REST API schema. Intended for developers.
     3014 *      @type bool|array $show_in_rest {
     3015 *          Whether this feature should be included in the Themes REST API endpoint. Defaults to not being included.
     3016 *          When registering an 'array' or 'object' type, this argument must be an array with the 'schema' key.
     3017 *
     3018 *          @type array    $schema           Specifies the JSON Schema definition describing the feature. If any objects in the schema
     3019 *                                           do not include the 'additionalProperties' keyword, it is set to false.
     3020 *          @type string   $name             An alternate name to be use as the property name in the REST API.
     3021 *          @type callable $prepare_callback A function used to format the theme support in the REST API. Receives the raw theme support value.
     3022 *      }
     3023 * }
     3024 * @return true|WP_Error True if the theme feature was successfully registered, a WP_Error object if not.
     3025 */
     3026function register_theme_feature( $feature, $args = array() ) {
     3027        global $_wp_registered_theme_features;
     3028
     3029        if ( ! is_array( $_wp_registered_theme_features ) ) {
     3030                $_wp_registered_theme_features = array();
     3031        }
     3032
     3033        $defaults = array(
     3034                'type'         => 'boolean',
     3035                'variadic'     => false,
     3036                'description'  => '',
     3037                'show_in_rest' => false,
     3038        );
     3039
     3040        $args = wp_parse_args( $args, $defaults );
     3041
     3042        if ( true === $args['show_in_rest'] ) {
     3043                $args['show_in_rest'] = array();
     3044        }
     3045
     3046        if ( is_array( $args['show_in_rest'] ) ) {
     3047                $args['show_in_rest'] = wp_parse_args(
     3048                        $args['show_in_rest'],
     3049                        array(
     3050                                'schema'           => array(),
     3051                                'name'             => $feature,
     3052                                'prepare_callback' => null,
     3053                        )
     3054                );
     3055        }
     3056
     3057        if ( ! in_array( $args['type'], array( 'string', 'boolean', 'integer', 'number', 'array', 'object' ), true ) ) {
     3058                return new WP_Error( 'invalid_type', __( 'The feature "type" is not valid JSON Schema type.' ) );
     3059        }
     3060
     3061        if ( true === $args['variadic'] && 'array' !== $args['type'] ) {
     3062                return new WP_Error( 'variadic_must_be_array', __( 'When registering a "variadic" theme feature, the "type" must be an "array".' ) );
     3063        }
     3064
     3065        if ( false !== $args['show_in_rest'] && in_array( $args['type'], array( 'array', 'object' ), true ) ) {
     3066                if ( ! is_array( $args['show_in_rest'] ) || empty( $args['show_in_rest']['schema'] ) ) {
     3067                        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.' ) );
     3068                }
     3069
     3070                if ( 'array' === $args['type'] && ! isset( $args['show_in_rest']['schema']['items'] ) ) {
     3071                        return new WP_Error( 'missing_schema_items', __( 'When registering an "array" feature, the feature\'s schema must include the "items" keyword.' ) );
     3072                }
     3073
     3074                if ( 'object' === $args['type'] && ! isset( $args['show_in_rest']['schema']['properties'] ) ) {
     3075                        return new WP_Error( 'missing_schema_properties', __( 'When registering an "object" feature, the feature\'s schema must include the "properties" keyword.' ) );
     3076                }
     3077        }
     3078
     3079        if ( is_array( $args['show_in_rest'] ) ) {
     3080                if ( isset( $args['show_in_rest']['prepare_callback'] ) && ! is_callable( $args['show_in_rest']['prepare_callback'] ) ) {
     3081                        return new WP_Error( 'invalid_rest_prepare_callback', __( 'The prepare_callback must be a callable function.' ) );
     3082                }
     3083
     3084                $args['show_in_rest']['schema'] = wp_parse_args(
     3085                        $args['show_in_rest']['schema'],
     3086                        array(
     3087                                'description' => $args['description'],
     3088                                'type'        => $args['type'],
     3089                                'default'     => false,
     3090                        )
     3091                );
     3092
     3093                if ( is_bool( $args['show_in_rest']['schema']['default'] ) && ! in_array( 'boolean', (array) $args['show_in_rest']['schema']['type'], true ) ) {
     3094                        // Automatically include the "boolean" type when the default value is a boolean.
     3095                        $args['show_in_rest']['schema']['type'] = (array) $args['show_in_rest']['schema']['type'];
     3096                        array_unshift( $args['show_in_rest']['schema']['type'], 'boolean' );
     3097                }
     3098
     3099                $args['show_in_rest']['schema'] = rest_default_additional_properties_to_false( $args['show_in_rest']['schema'] );
     3100        }
     3101
     3102        $_wp_registered_theme_features[ $feature ] = $args;
     3103
     3104        return true;
     3105}
     3106
     3107/**
     3108 * Gets the list of registered theme features.
     3109 *
     3110 * @since 5.5.0
     3111 *
     3112 * @global $_wp_registered_theme_features
     3113 *
     3114 * @return array[] List of theme features, keyed by their name.
     3115 */
     3116function get_registered_theme_features() {
     3117        global $_wp_registered_theme_features;
     3118
     3119        if ( ! is_array( $_wp_registered_theme_features ) ) {
     3120                return array();
     3121        }
     3122
     3123        return $_wp_registered_theme_features;
     3124}
     3125
     3126/**
     3127 * Gets the registration config for a theme feature.
     3128 *
     3129 * @since 5.5.0
     3130 *
     3131 * @global $_wp_registered_theme_features
     3132 *
     3133 * @param string $feature The feature name.
     3134 * @return array|null The registration args, or null if the feature was not registered.
     3135 */
     3136function get_registered_theme_feature( $feature ) {
     3137        global $_wp_registered_theme_features;
     3138
     3139        if ( ! is_array( $_wp_registered_theme_features ) ) {
     3140                return null;
     3141        }
     3142
     3143        return isset( $_wp_registered_theme_features[ $feature ] ) ? $_wp_registered_theme_features[ $feature ] : null;
     3144}
     3145
     3146/**
    29973147 * Checks an attachment being deleted to see if it's a header or background image.
    29983148 *
    … …  
    34633613        }
    34643614}
     3615
     3616/**
     3617 * Creates the initial theme features when the 'setup_theme' action is fired.
     3618 *
     3619 * See {@see 'setup_theme'}.
     3620 *
     3621 * @since 5.5.0
     3622 */
     3623function create_initial_theme_features() {
     3624        register_theme_feature(
     3625                'align-wide',
     3626                array(
     3627                        'description'  => __( 'Whether theme opts in to wide alignment CSS class.' ),
     3628                        'show_in_rest' => true,
     3629                )
     3630        );
     3631        register_theme_feature(
     3632                'automatic-feed-links',
     3633                array(
     3634                        'description'  => __( 'Whether posts and comments RSS feed links are added to head.' ),
     3635                        'show_in_rest' => true,
     3636                )
     3637        );
     3638        register_theme_feature(
     3639                'custom-background',
     3640                array(
     3641                        'description'  => __( 'Custom background if defined by the theme.' ),
     3642                        'type'         => 'object',
     3643                        'show_in_rest' => array(
     3644                                'schema' => array(
     3645                                        'properties' => array(
     3646                                                'default-image'      => array(
     3647                                                        'type'   => 'string',
     3648                                                        'format' => 'uri',
     3649                                                ),
     3650                                                'default-preset'     => array(
     3651                                                        'type' => 'string',
     3652                                                        'enum' => array(
     3653                                                                'default',
     3654                                                                'fill',
     3655                                                                'fit',
     3656                                                                'repeat',
     3657                                                                'custom',
     3658                                                        ),
     3659                                                ),
     3660                                                'default-position-x' => array(
     3661                                                        'type' => 'string',
     3662                                                        'enum' => array(
     3663                                                                'left',
     3664                                                                'center',
     3665                                                                'right',
     3666                                                        ),
     3667                                                ),
     3668                                                'default-position-y' => array(
     3669                                                        'type' => 'string',
     3670                                                        'enum' => array(
     3671                                                                'left',
     3672                                                                'center',
     3673                                                                'right',
     3674                                                        ),
     3675                                                ),
     3676                                                'default-size'       => array(
     3677                                                        'type' => 'string',
     3678                                                        'enum' => array(
     3679                                                                'auto',
     3680                                                                'contain',
     3681                                                                'cover',
     3682                                                        ),
     3683                                                ),
     3684                                                'default-repeat'     => array(
     3685                                                        'type' => 'string',
     3686                                                        'enum' => array(
     3687                                                                'repeat-x',
     3688                                                                'repeat-y',
     3689                                                                'repeat',
     3690                                                                'no-repeat',
     3691                                                        ),
     3692                                                ),
     3693                                                'default-attachment' => array(
     3694                                                        'type' => 'string',
     3695                                                        'enum' => array(
     3696                                                                'scroll',
     3697                                                                'fixed',
     3698                                                        ),
     3699                                                ),
     3700                                                'default-color'      => array(
     3701                                                        'type' => 'string',
     3702                                                ),
     3703                                        ),
     3704                                ),
     3705                        ),
     3706                )
     3707        );
     3708        register_theme_feature(
     3709                'custom-header',
     3710                array(
     3711                        'description'  => __( 'Custom header if defined by the theme.' ),
     3712                        'type'         => 'object',
     3713                        'show_in_rest' => array(
     3714                                'schema' => array(
     3715                                        'properties' => array(
     3716                                                'default-image'      => array(
     3717                                                        'type'   => 'string',
     3718                                                        'format' => 'uri',
     3719                                                ),
     3720                                                'random-default'     => array(
     3721                                                        'type' => 'boolean',
     3722                                                ),
     3723                                                'width'              => array(
     3724                                                        'type' => 'integer',
     3725                                                ),
     3726                                                'height'             => array(
     3727                                                        'type' => 'integer',
     3728                                                ),
     3729                                                'flex-height'        => array(
     3730                                                        'type' => 'boolean',
     3731                                                ),
     3732                                                'flex-width'         => array(
     3733                                                        'type' => 'boolean',
     3734                                                ),
     3735                                                'default-text-color' => array(
     3736                                                        'type' => 'string',
     3737                                                ),
     3738                                                'header-text'        => array(
     3739                                                        'type' => 'boolean',
     3740                                                ),
     3741                                                'uploads'            => array(
     3742                                                        'type' => 'boolean',
     3743                                                ),
     3744                                                'video'              => array(
     3745                                                        'type' => 'boolean',
     3746                                                ),
     3747                                        ),
     3748                                ),
     3749                        ),
     3750                )
     3751        );
     3752        register_theme_feature(
     3753                'custom-logo',
     3754                array(
     3755                        'type'         => 'object',
     3756                        'description'  => __( 'Custom logo if defined by the theme.' ),
     3757                        'show_in_rest' => array(
     3758                                'schema' => array(
     3759                                        'properties' => array(
     3760                                                'width'       => array(
     3761                                                        'type' => 'integer',
     3762                                                ),
     3763                                                'height'      => array(
     3764                                                        'type' => 'integer',
     3765                                                ),
     3766                                                'flex-width'  => array(
     3767                                                        'type' => 'boolean',
     3768                                                ),
     3769                                                'flex-height' => array(
     3770                                                        'type' => 'boolean',
     3771                                                ),
     3772                                                'header-text' => array(
     3773                                                        'type'  => 'array',
     3774                                                        'items' => array(
     3775                                                                'type' => 'string',
     3776                                                        ),
     3777                                                ),
     3778                                        ),
     3779                                ),
     3780                        ),
     3781                )
     3782        );
     3783        register_theme_feature(
     3784                'customize-selective-refresh-widgets',
     3785                array(
     3786                        'description'  => __( 'Whether the theme enables Selective Refresh for Widgets being managed with the Customizer.' ),
     3787                        'show_in_rest' => true,
     3788                )
     3789        );
     3790        register_theme_feature(
     3791                'dark-editor-style',
     3792                array(
     3793                        'description'  => __( 'Whether theme opts in to the dark editor style UI.' ),
     3794                        'show_in_rest' => true,
     3795                )
     3796        );
     3797        register_theme_feature(
     3798                'disable-custom-colors',
     3799                array(
     3800                        'description'  => __( 'Whether the theme disables custom colors.' ),
     3801                        'show_in_rest' => true,
     3802                )
     3803        );
     3804        register_theme_feature(
     3805                'disable-custom-font-sizes',
     3806                array(
     3807                        'description'  => __( 'Whether the theme disables custom font sizes.' ),
     3808                        'show_in_rest' => true,
     3809                )
     3810        );
     3811        register_theme_feature(
     3812                'disable-custom-gradients',
     3813                array(
     3814                        'description'  => __( 'Whether the theme disables custom gradients.' ),
     3815                        'show_in_rest' => true,
     3816                )
     3817        );
     3818        register_theme_feature(
     3819                'editor-color-palette',
     3820                array(
     3821                        'type'         => 'array',
     3822                        'description'  => __( 'Custom color palette if defined by the theme.' ),
     3823                        'show_in_rest' => array(
     3824                                'schema' => array(
     3825                                        'items' => array(
     3826                                                'type'       => 'object',
     3827                                                'properties' => array(
     3828                                                        'name'  => array(
     3829                                                                'type' => 'string',
     3830                                                        ),
     3831                                                        'slug'  => array(
     3832                                                                'type' => 'string',
     3833                                                        ),
     3834                                                        'color' => array(
     3835                                                                'type' => 'string',
     3836                                                        ),
     3837                                                ),
     3838                                        ),
     3839                                ),
     3840                        ),
     3841                )
     3842        );
     3843        register_theme_feature(
     3844                'editor-font-sizes',
     3845                array(
     3846                        'type'         => 'array',
     3847                        'description'  => __( 'Custom font sizes if defined by the theme.' ),
     3848                        'show_in_rest' => array(
     3849                                'schema' => array(
     3850                                        'items' => array(
     3851                                                'type'       => 'object',
     3852                                                'properties' => array(
     3853                                                        'name' => array(
     3854                                                                'type' => 'string',
     3855                                                        ),
     3856                                                        'size' => array(
     3857                                                                'type' => 'number',
     3858                                                        ),
     3859                                                        'slug' => array(
     3860                                                                'type' => 'string',
     3861                                                        ),
     3862                                                ),
     3863                                        ),
     3864                                ),
     3865                        ),
     3866                )
     3867        );
     3868        register_theme_feature(
     3869                'editor-gradient-presets',
     3870                array(
     3871                        'type'         => 'array',
     3872                        'description'  => __( 'Custom gradient presets if defined by the theme.' ),
     3873                        'show_in_rest' => array(
     3874                                'schema' => array(
     3875                                        'items' => array(
     3876                                                'type'       => 'object',
     3877                                                'properties' => array(
     3878                                                        'name'     => array(
     3879                                                                'type' => 'string',
     3880                                                        ),
     3881                                                        'gradient' => array(
     3882                                                                'type' => 'string',
     3883                                                        ),
     3884                                                        'slug'     => array(
     3885                                                                'type' => 'string',
     3886                                                        ),
     3887                                                ),
     3888                                        ),
     3889                                ),
     3890                        ),
     3891                )
     3892        );
     3893        register_theme_feature(
     3894                'editor-styles',
     3895                array(
     3896                        'description'  => __( 'Whether theme opts in to the editor styles CSS wrapper.' ),
     3897                        'show_in_rest' => true,
     3898                )
     3899        );
     3900        register_theme_feature(
     3901                'html5',
     3902                array(
     3903                        'type'         => 'array',
     3904                        'description'  => __( 'Allows use of HTML5 markup for search forms, comment forms, comment lists, gallery, and caption.' ),
     3905                        'show_in_rest' => array(
     3906                                'schema' => array(
     3907                                        'items' => array(
     3908                                                'type' => 'string',
     3909                                                'enum' => array(
     3910                                                        'search-form',
     3911                                                        'comment-form',
     3912                                                        'comment-list',
     3913                                                        'gallery',
     3914                                                        'caption',
     3915                                                        'script',
     3916                                                        'style',
     3917                                                ),
     3918                                        ),
     3919                                ),
     3920                        ),
     3921                )
     3922        );
     3923        register_theme_feature(
     3924                'post-formats',
     3925                array(
     3926                        'type'         => 'array',
     3927                        'description'  => __( 'Post formats supported.' ),
     3928                        'show_in_rest' => array(
     3929                                'name'             => 'formats',
     3930                                'schema'           => array(
     3931                                        'items'   => array(
     3932                                                'type' => 'string',
     3933                                                'enum' => get_post_format_slugs(),
     3934                                        ),
     3935                                        'default' => array( 'standard' ),
     3936                                ),
     3937                                'prepare_callback' => static function ( $formats ) {
     3938                                        $formats = is_array( $formats ) ? array_values( $formats[0] ) : array();
     3939                                        $formats = array_merge( array( 'standard' ), $formats );
     3940
     3941                                        return $formats;
     3942                                },
     3943                        ),
     3944                )
     3945        );
     3946        register_theme_feature(
     3947                'post-thumbnails',
     3948                array(
     3949                        'type'         => 'array',
     3950                        'description'  => __( 'The post types that support thumbnails or true if all post types are supported.' ),
     3951                        'show_in_rest' => array(
     3952                                'type'   => array( 'boolean', 'array' ),
     3953                                'schema' => array(
     3954                                        'items' => array(
     3955                                                'type' => 'string',
     3956                                        ),
     3957                                ),
     3958                        ),
     3959                )
     3960        );
     3961        register_theme_feature(
     3962                'responsive-embeds',
     3963                array(
     3964                        'description'  => __( 'Whether the theme supports responsive embedded content.' ),
     3965                        'show_in_rest' => true,
     3966                )
     3967        );
     3968        register_theme_feature(
     3969                'title-tag',
     3970                array(
     3971                        'description'  => __( 'Whether the theme can manage the document title tag.' ),
     3972                        'show_in_rest' => true,
     3973                )
     3974        );
     3975        register_theme_feature(
     3976                'wp-block-styles',
     3977                array(
     3978                        'description'  => __( 'Whether theme opts in to default WordPress block styles for viewing.' ),
     3979                        'show_in_rest' => true,
     3980                )
     3981        );
     3982}
  • trunk/tests/phpunit/tests/rest-api/rest-themes-controller.php

    r47921 r48171  
    261261
    262262                $theme_supports = $properties['theme_supports']['properties'];
    263                 $this->assertEquals( 20, count( $theme_supports ) );
    264263                $this->assertArrayHasKey( 'align-wide', $theme_supports );
    265264                $this->assertArrayHasKey( 'automatic-feed-links', $theme_supports );
    … …  
    282281                $this->assertArrayHasKey( 'title-tag', $theme_supports );
    283282                $this->assertArrayHasKey( 'wp-block-styles', $theme_supports );
     283                $this->assertCount( 20, $theme_supports );
    284284        }
    285285
    … …  
    999999
    10001000        /**
     1001         * @ticket 49406
     1002         */
     1003        public function test_variadic_theme_support() {
     1004                register_theme_feature(
     1005                        'test-feature',
     1006                        array(
     1007                                'type'         => 'array',
     1008                                'variadic'     => true,
     1009                                'show_in_rest' => array(
     1010                                        'schema' => array(
     1011                                                'items' => array(
     1012                                                        'type' => 'string',
     1013                                                ),
     1014                                        ),
     1015                                ),
     1016                        )
     1017                );
     1018                add_theme_support( 'test-feature', 'a', 'b', 'c' );
     1019
     1020                $response = self::perform_active_theme_request();
     1021                $result   = $response->get_data();
     1022                $this->assertTrue( isset( $result[0]['theme_supports'] ) );
     1023                $this->assertEquals( array( 'a', 'b', 'c' ), $result[0]['theme_supports']['test-feature'] );
     1024        }
     1025
     1026        /**
    10011027         * It should be possible to register custom fields to the endpoint.
    10021028         *
  • trunk/tests/phpunit/tests/theme.php

    r47122 r48171  
    416416                $this->assertEquals( 'private', get_post_status( $nav_created_post_ids[1] ) );
    417417        }
     418
     419        /**
     420         * @ticket 49406
     421         */
     422        public function test_register_theme_support_defaults() {
     423                $registered = register_theme_feature( 'test-feature' );
     424                $this->assertTrue( $registered );
     425
     426                $expected = array(
     427                        'type'         => 'boolean',
     428                        'variadic'     => false,
     429                        'description'  => '',
     430                        'show_in_rest' => false,
     431                );
     432                $this->assertEqualSets( $expected, get_registered_theme_feature( 'test-feature' ) );
     433        }
     434
     435        /**
     436         * @ticket 49406
     437         */
     438        public function test_register_theme_support_explicit() {
     439                $args = array(
     440                        'type'         => 'array',
     441                        'variadic'     => true,
     442                        'description'  => 'My Feature',
     443                        'show_in_rest' => array(
     444                                'schema' => array(
     445                                        'items' => array(
     446                                                'type' => 'string',
     447                                        ),
     448                                ),
     449                        ),
     450                );
     451
     452                register_theme_feature( 'test-feature', $args );
     453                $actual = get_registered_theme_feature( 'test-feature' );
     454
     455                $this->assertEquals( 'array', $actual['type'] );
     456                $this->assertTrue( $actual['variadic'] );
     457                $this->assertEquals( 'My Feature', $actual['description'] );
     458                $this->assertEquals( array( 'type' => 'string' ), $actual['show_in_rest']['schema']['items'] );
     459        }
     460
     461        /**
     462         * @ticket 49406
     463         */
     464        public function test_register_theme_support_upgrades_show_in_rest() {
     465                register_theme_feature( 'test-feature', array( 'show_in_rest' => true ) );
     466
     467                $expected = array(
     468                        'schema'           => array(
     469                                'type'        => 'boolean',
     470                                'description' => '',
     471                                'default'     => false,
     472                        ),
     473                        'name'             => 'test-feature',
     474                        'prepare_callback' => null,
     475                );
     476                $actual   = get_registered_theme_feature( 'test-feature' )['show_in_rest'];
     477
     478                $this->assertEqualSets( $expected, $actual );
     479        }
     480
     481        /**
     482         * @ticket 49406
     483         */
     484        public function test_register_theme_support_fills_schema() {
     485                register_theme_feature(
     486                        'test-feature',
     487                        array(
     488                                'type'         => 'array',
     489                                'description'  => 'Cool Feature',
     490                                'show_in_rest' => array(
     491                                        'schema' => array(
     492                                                'items'    => array(
     493                                                        'type' => 'string',
     494                                                ),
     495                                                'minItems' => 1,
     496                                        ),
     497                                ),
     498                        )
     499                );
     500
     501                $expected = array(
     502                        'description' => 'Cool Feature',
     503                        'type'        => array( 'boolean', 'array' ),
     504                        'items'       => array(
     505                                'type' => 'string',
     506                        ),
     507                        'minItems'    => 1,
     508                        'default'     => false,
     509                );
     510                $actual   = get_registered_theme_feature( 'test-feature' )['show_in_rest']['schema'];
     511
     512                $this->assertEqualSets( $expected, $actual );
     513        }
     514
     515        /**
     516         * @ticket 49406
     517         */
     518        public function test_register_theme_support_does_not_add_boolean_type_if_non_bool_default() {
     519                register_theme_feature(
     520                        'test-feature',
     521                        array(
     522                                'type'         => 'array',
     523                                'show_in_rest' => array(
     524                                        'schema' => array(
     525                                                'items'   => array(
     526                                                        'type' => 'string',
     527                                                ),
     528                                                'default' => array( 'standard' ),
     529                                        ),
     530                                ),
     531                        )
     532                );
     533
     534                $actual = get_registered_theme_feature( 'test-feature' )['show_in_rest']['schema']['type'];
     535                $this->assertEquals( 'array', $actual );
     536        }
     537
     538        /**
     539         * @ticket 49406
     540         */
     541        public function test_register_theme_support_defaults_additional_properties_to_false() {
     542                register_theme_feature(
     543                        'test-feature',
     544                        array(
     545                                'type'         => 'object',
     546                                'description'  => 'Cool Feature',
     547                                'show_in_rest' => array(
     548                                        'schema' => array(
     549                                                'properties' => array(
     550                                                        'a' => array(
     551                                                                'type' => 'string',
     552                                                        ),
     553                                                ),
     554                                        ),
     555                                ),
     556                        )
     557                );
     558
     559                $actual = get_registered_theme_feature( 'test-feature' )['show_in_rest']['schema'];
     560
     561                $this->assertArrayHasKey( 'additionalProperties', $actual );
     562                $this->assertFalse( $actual['additionalProperties'] );
     563        }
     564
     565        /**
     566         * @ticket 49406
     567         */
     568        public function test_register_theme_support_with_additional_properties() {
     569                register_theme_feature(
     570                        'test-feature',
     571                        array(
     572                                'type'         => 'object',
     573                                'description'  => 'Cool Feature',
     574                                'show_in_rest' => array(
     575                                        'schema' => array(
     576                                                'properties'           => array(),
     577                                                'additionalProperties' => array(
     578                                                        'type' => 'string',
     579                                                ),
     580                                        ),
     581                                ),
     582                        )
     583                );
     584
     585                $expected = array(
     586                        'type' => 'string',
     587                );
     588                $actual   = get_registered_theme_feature( 'test-feature' )['show_in_rest']['schema']['additionalProperties'];
     589
     590                $this->assertEqualSets( $expected, $actual );
     591        }
     592
     593        /**
     594         * @ticket 49406
     595         */
     596        public function test_register_theme_support_defaults_additional_properties_to_false_in_array() {
     597                register_theme_feature(
     598                        'test-feature',
     599                        array(
     600                                'type'         => 'array',
     601                                'description'  => 'Cool Feature',
     602                                'show_in_rest' => array(
     603                                        'schema' => array(
     604                                                'items' => array(
     605                                                        'type'       => 'object',
     606                                                        'properties' => array(
     607                                                                'a' => array(
     608                                                                        'type' => 'string',
     609                                                                ),
     610                                                        ),
     611                                                ),
     612                                        ),
     613                                ),
     614                        )
     615                );
     616
     617                $actual = get_registered_theme_feature( 'test-feature' )['show_in_rest']['schema']['items'];
     618
     619                $this->assertArrayHasKey( 'additionalProperties', $actual );
     620                $this->assertFalse( $actual['additionalProperties'] );
     621        }
     622
     623        /**
     624         * @ticket       49406
     625         * @dataProvider _dp_register_theme_support_validation
     626         * @param string $error_code The error code expected.
     627         * @param array  $args       The args to register.
     628         */
     629        public function test_register_theme_support_validation( $error_code, $args ) {
     630                $registered = register_theme_feature( 'test-feature', $args );
     631
     632                $this->assertWPError( $registered );
     633                $this->assertEquals( $error_code, $registered->get_error_code() );
     634        }
     635
     636        public function _dp_register_theme_support_validation() {
     637                return array(
     638                        array(
     639                                'invalid_type',
     640                                array(
     641                                        'type' => 'float',
     642                                ),
     643                        ),
     644                        array(
     645                                'invalid_type',
     646                                array(
     647                                        'type' => array( 'string' ),
     648                                ),
     649                        ),
     650                        array(
     651                                'variadic_must_be_array',
     652                                array(
     653                                        'variadic' => true,
     654                                ),
     655                        ),
     656                        array(
     657                                'missing_schema',
     658                                array(
     659                                        'type'         => 'object',
     660                                        'show_in_rest' => true,
     661                                ),
     662                        ),
     663                        array(
     664                                'missing_schema',
     665                                array(
     666                                        'type'         => 'array',
     667                                        'show_in_rest' => true,
     668                                ),
     669                        ),
     670                        array(
     671                                'missing_schema_items',
     672                                array(
     673                                        'type'         => 'array',
     674                                        'show_in_rest' => array(
     675                                                'schema' => array(
     676                                                        'type' => 'array',
     677                                                ),
     678                                        ),
     679                                ),
     680                        ),
     681                        array(
     682                                'missing_schema_properties',
     683                                array(
     684                                        'type'         => 'object',
     685                                        'show_in_rest' => array(
     686                                                'schema' => array(
     687                                                        'type' => 'object',
     688                                                ),
     689                                        ),
     690                                ),
     691                        ),
     692                        array(
     693                                'invalid_rest_prepare_callback',
     694                                array(
     695                                        'show_in_rest' => array(
     696                                                'prepare_callback' => 'this is not a valid function',
     697                                        ),
     698                                ),
     699                        ),
     700                );
     701        }
    418702}
Note: See TracChangeset for help on using the changeset viewer.