WordPress.org

Make WordPress Core


Ignore:
Timestamp:
06/25/2020 10:11:09 PM (15 months 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.

File:
1 edited

Legend:

Unmodified
Added
Removed
  • 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}
Note: See TracChangeset for help on using the changeset viewer.