Make WordPress Core

Changeset 52376


Ignore:
Timestamp:
12/14/2021 06:22:07 PM (22 months ago)
Author:
hellofromTonya
Message:

REST API: Add block theme support for valid non-alphanumeric characters in theme's directory name.

Themes whose wp-content/themes/<dirname> include valid non-alphanumeric (cross-platform) characters work for non-block themes, but did not previously resolve for block themes. For example, a block theme in wp-content/themes/twentytwentytwo-0.4.0/ directory resulted a 404 "No route was found matching the URL and request method" response when attempting to customize it in the Site Editor.

This commit adds support for the following characters in a theme's root directory: _, ., @, [, ], (, and ). Subdirectory themes and - are already supported.

Follow-up to [51003], [52051], [52275].

Props mkaz, costdev, hellofromTonya, jffng, justinahinon, peterwilsoncc, spacedmonkey, TimothyBlynJacobs.
Fixes #54596.

Location:
trunk
Files:
6 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/wp-includes/rest-api/endpoints/class-wp-rest-global-styles-controller.php

    r52374 r52376  
    4242        register_rest_route(
    4343            $this->namespace,
    44             '/' . $this->rest_base . '/themes/(?P<stylesheet>[^.\/]+(?:\/[^.\/]+)?)',
     44            '/' . $this->rest_base . '/themes/(?P<stylesheet>[\/\s%\w\.\(\)\[\]\@_\-]+)',
    4545            array(
    4646                array(
     
    5050                    'args'                => array(
    5151                        'stylesheet' => array(
    52                             'description' => __( 'The theme identifier' ),
    53                             'type'        => 'string',
     52                            'description'       => __( 'The theme identifier' ),
     53                            'type'              => 'string',
     54                            'sanitize_callback' => array( $this, '_sanitize_global_styles_callback' ),
    5455                        ),
    5556                    ),
     
    5859        );
    5960
    60         // Lists/updates a single gloval style variation based on the given id.
     61        // Lists/updates a single global style variation based on the given id.
    6162        register_rest_route(
    6263            $this->namespace,
    63             '/' . $this->rest_base . '/(?P<id>[\/\w-]+)',
     64            '/' . $this->rest_base . '/(?P<id>[\/\s%\w\.\(\)\[\]\@_\-]+)',
    6465            array(
    6566                array(
     
    6970                    'args'                => array(
    7071                        'id' => array(
    71                             'description' => __( 'The id of a template' ),
    72                             'type'        => 'string',
     72                            'description'       => __( 'The id of a template' ),
     73                            'type'              => 'string',
     74                            'sanitize_callback' => array( $this, '_sanitize_global_styles_callback' ),
    7375                        ),
    7476                    ),
     
    8385            )
    8486        );
     87    }
     88
     89    /**
     90     * Sanitize the global styles ID or stylesheet to decode endpoint.
     91     * For example, `wp/v2/global-styles/templatetwentytwo%200.4.0`
     92     * would be decoded to `templatetwentytwo 0.4.0`.
     93     *
     94     * @since 5.9.0
     95     *
     96     * @param string $id_or_stylesheet Global styles ID or stylesheet.
     97     * @return string Sanitized global styles ID or stylesheet.
     98     */
     99    public function _sanitize_global_styles_callback( $id_or_stylesheet ) {
     100        return urldecode( $id_or_stylesheet );
    85101    }
    86102
     
    520536     *
    521537     * @param WP_REST_Request $request The request instance.
    522      *
    523538     * @return WP_REST_Response|WP_Error
    524539     */
  • trunk/src/wp-includes/rest-api/endpoints/class-wp-rest-templates-controller.php

    r52345 r52376  
    6969        register_rest_route(
    7070            $this->namespace,
    71             '/' . $this->rest_base . '/(?P<id>[\/\w-]+)',
     71            '/' . $this->rest_base . '/(?P<id>[\/\s%\w\.\(\)\[\]\@_\-]+)',
    7272            array(
    7373                'args'   => array(
     
    150150     */
    151151    public function _sanitize_template_id( $id ) {
     152        // Decode empty space.
     153        $id = urldecode( $id );
     154
    152155        $last_slash_pos = strrpos( $id, '/' );
    153156        if ( false === $last_slash_pos ) {
  • trunk/tests/phpunit/tests/rest-api/rest-global-styles-controller.php

    r52374 r52376  
    9494    /**
    9595     * @covers WP_REST_Global_Styles_Controller::register_routes
     96     * @ticket 54596
    9697     */
    9798    public function test_register_routes() {
    9899        $routes = rest_get_server()->get_routes();
    99         $this->assertArrayHasKey( '/wp/v2/global-styles/(?P<id>[\/\w-]+)', $routes );
    100         $this->assertCount( 2, $routes['/wp/v2/global-styles/(?P<id>[\/\w-]+)'] );
    101         $this->assertArrayHasKey( '/wp/v2/global-styles/themes/(?P<stylesheet>[^.\/]+(?:\/[^.\/]+)?)', $routes );
    102         $this->assertCount( 1, $routes['/wp/v2/global-styles/themes/(?P<stylesheet>[^.\/]+(?:\/[^.\/]+)?)'] );
     100        $this->assertArrayHasKey(
     101            '/wp/v2/global-styles/(?P<id>[\/\s%\w\.\(\)\[\]\@_\-]+)',
     102            $routes,
     103            'Single global style based on the given ID route does not exist'
     104        );
     105        $this->assertCount(
     106            2,
     107            $routes['/wp/v2/global-styles/(?P<id>[\/\s%\w\.\(\)\[\]\@_\-]+)'],
     108            'Single global style based on the given ID route does not have exactly two elements'
     109        );
     110        $this->assertArrayHasKey(
     111            '/wp/v2/global-styles/themes/(?P<stylesheet>[\/\s%\w\.\(\)\[\]\@_\-]+)',
     112            $routes,
     113            'Theme global styles route does not exist'
     114        );
     115        $this->assertCount(
     116            1,
     117            $routes['/wp/v2/global-styles/themes/(?P<stylesheet>[\/\s%\w\.\(\)\[\]\@_\-]+)'],
     118            'Theme global styles route does not have exactly one element'
     119        );
    103120    }
    104121
     
    133150    }
    134151
    135 
    136152    /**
    137153     * @covers WP_REST_Global_Styles_Controller::get_theme_item
     
    146162
    147163    /**
    148      * @covers WP_REST_Global_Styles_Controller::get_theme_item
    149      */
    150     public function test_get_theme_item() {
    151         wp_set_current_user( self::$admin_id );
    152         $request  = new WP_REST_Request( 'GET', '/wp/v2/global-styles/themes/tt1-blocks' );
     164     * @dataProvider data_get_theme_item_invalid_theme_dirname
     165     * @covers WP_REST_Global_Styles_Controller::get_theme_item
     166     * @ticket 54596
     167     */
     168    public function test_get_theme_item_invalid_theme_dirname( $theme_dirname ) {
     169        wp_set_current_user( self::$admin_id );
     170        switch_theme( $theme_dirname );
     171
     172        $request  = new WP_REST_Request( 'GET', '/wp/v2/global-styles/themes/' . $theme_dirname );
     173        $response = rest_get_server()->dispatch( $request );
     174        $this->assertErrorResponse( 'rest_no_route', $response, 404 );
     175    }
     176
     177    /**
     178     * Data provider.
     179     *
     180     * @return array
     181     */
     182    public function data_get_theme_item_invalid_theme_dirname() {
     183        return array(
     184            'with |'                 => array( 'my|theme' ),
     185            'with +'                 => array( 'my+theme' ),
     186            'with {}'                => array( 'my{theme}' ),
     187            'with #'                 => array( 'my#theme' ),
     188            'with !'                 => array( 'my!theme' ),
     189            'multiple invalid chars' => array( 'mytheme-[_(+@)]#! v4.0' ),
     190        );
     191    }
     192
     193    /**
     194     * @dataProvider data_get_theme_item
     195     * @covers WP_REST_Global_Styles_Controller::get_theme_item
     196     * @ticket 54596
     197     */
     198    public function test_get_theme_item( $theme ) {
     199        wp_set_current_user( self::$admin_id );
     200        switch_theme( $theme );
     201
     202        $request  = new WP_REST_Request( 'GET', '/wp/v2/global-styles/themes/' . $theme );
    153203        $response = rest_get_server()->dispatch( $request );
    154204        $data     = $response->get_data();
    155205        $links    = $response->get_links();
    156         $this->assertArrayHasKey( 'settings', $data );
    157         $this->assertArrayHasKey( 'styles', $data );
    158         $this->assertArrayHasKey( 'self', $links );
    159         $this->assertStringContainsString( '/wp/v2/global-styles/themes/tt1-blocks', $links['self'][0]['href'] );
     206        $this->assertArrayHasKey( 'settings', $data, 'Data does not have "settings" key' );
     207        $this->assertArrayHasKey( 'styles', $data, 'Data does not have "styles" key' );
     208        $this->assertArrayHasKey( 'self', $links, 'Links do not have a "self" key' );
     209        $this->assertStringContainsString( '/wp/v2/global-styles/themes/' . $theme, $links['self'][0]['href'] );
     210    }
     211
     212    /**
     213     * Data provider.
     214     *
     215     * @return array
     216     */
     217    public function data_get_theme_item() {
     218        return array(
     219            'alphabetic chars'   => array( 'mytheme' ),
     220            'alphanumeric chars' => array( 'mythemev1' ),
     221            'space'              => array( 'my theme' ),
     222            '-'                  => array( 'my-theme' ),
     223            '_'                  => array( 'my_theme' ),
     224            '.'                  => array( 'mytheme0.1' ),
     225            '- and .'            => array( 'my-theme-0.1' ),
     226            'space and .'        => array( 'mytheme v0.1' ),
     227            'space, -, _, .'     => array( 'my-theme-v0.1' ),
     228            '[]'                 => array( 'my[theme]' ),
     229            '()'                 => array( 'my(theme)' ),
     230            '@'                  => array( 'my@theme' ),
     231        );
    160232    }
    161233
  • trunk/tests/phpunit/tests/rest-api/rest-schema-setup.php

    r52272 r52376  
    7070    }
    7171
     72    /**
     73     * @ticket 54596
     74     */
    7275    public function test_expected_routes_in_schema() {
    7376        $routes = rest_get_server()->get_routes();
     
    133136            '/wp/v2/comments',
    134137            '/wp/v2/comments/(?P<id>[\\d]+)',
    135             '/wp/v2/global-styles/(?P<id>[\/\w-]+)',
    136             '/wp/v2/global-styles/themes/(?P<stylesheet>[^.\/]+(?:\/[^.\/]+)?)',
     138            '/wp/v2/global-styles/(?P<id>[\/\s%\w\.\(\)\[\]\@_\-]+)',
     139            '/wp/v2/global-styles/themes/(?P<stylesheet>[\/\s%\w\.\(\)\[\]\@_\-]+)',
    137140            '/wp/v2/search',
    138141            '/wp/v2/block-renderer/(?P<name>[a-z0-9-]+/[a-z0-9-]+)',
     
    142145            '/wp/v2/settings',
    143146            '/wp/v2/template-parts',
    144             '/wp/v2/template-parts/(?P<id>[\/\w-]+)',
     147            '/wp/v2/template-parts/(?P<id>[\/\s%\w\.\(\)\[\]\@_\-]+)',
    145148            '/wp/v2/template-parts/(?P<id>[\d]+)/autosaves',
    146149            '/wp/v2/template-parts/(?P<parent>[\d]+)/autosaves/(?P<id>[\d]+)',
     
    148151            '/wp/v2/template-parts/(?P<parent>[\d]+)/revisions/(?P<id>[\d]+)',
    149152            '/wp/v2/templates',
    150             '/wp/v2/templates/(?P<id>[\/\w-]+)',
     153            '/wp/v2/templates/(?P<id>[\/\s%\w\.\(\)\[\]\@_\-]+)',
    151154            '/wp/v2/templates/(?P<id>[\d]+)/autosaves',
    152155            '/wp/v2/templates/(?P<parent>[\d]+)/autosaves/(?P<id>[\d]+)',
  • trunk/tests/phpunit/tests/rest-api/wpRestTemplatesController.php

    r52275 r52376  
    5454    }
    5555
     56    /**
     57     * @covers WP_REST_Templates_Controller::register_routes
     58     * @ticket 54596
     59     */
    5660    public function test_register_routes() {
    5761        $routes = rest_get_server()->get_routes();
    58         $this->assertArrayHasKey( '/wp/v2/templates', $routes );
    59         $this->assertArrayHasKey( '/wp/v2/templates/(?P<id>[\/\w-]+)', $routes );
     62        $this->assertArrayHasKey(
     63            '/wp/v2/templates',
     64            $routes,
     65            'Templates route does not exist'
     66        );
     67        $this->assertArrayHasKey(
     68            '/wp/v2/templates/(?P<id>[\/\s%\w\.\(\)\[\]\@_\-]+)',
     69            $routes,
     70            'Single template based on the given ID route does not exist'
     71        );
    6072    }
    6173
  • trunk/tests/qunit/fixtures/wp-api-generated.js

    r52286 r52376  
    51355135            }
    51365136        },
    5137         "/wp/v2/templates/(?P<id>[\\/\\w-]+)": {
     5137        "/wp/v2/templates/(?P<id>[\\/\\s%\\w\\.\\(\\)\\[\\]\\@_\\-]+)": {
    51385138            "namespace": "wp/v2",
    51395139            "methods": [
     
    57875787            }
    57885788        },
    5789         "/wp/v2/template-parts/(?P<id>[\\/\\w-]+)": {
     5789        "/wp/v2/template-parts/(?P<id>[\\/\\s%\\w\\.\\(\\)\\[\\]\\@_\\-]+)": {
    57905790            "namespace": "wp/v2",
    57915791            "methods": [
     
    94199419            ]
    94209420        },
    9421         "/wp/v2/global-styles/themes/(?P<stylesheet>[^.\\/]+(?:\\/[^.\\/]+)?)": {
     9421        "/wp/v2/global-styles/themes/(?P<stylesheet>[\\/\\s%\\w\\.\\(\\)\\[\\]\\@_\\-]+)": {
    94229422            "namespace": "wp/v2",
    94239423            "methods": [
     
    94399439            ]
    94409440        },
    9441         "/wp/v2/global-styles/(?P<id>[\\/\\w-]+)": {
     9441        "/wp/v2/global-styles/(?P<id>[\\/\\s%\\w\\.\\(\\)\\[\\]\\@_\\-]+)": {
    94429442            "namespace": "wp/v2",
    94439443            "methods": [
Note: See TracChangeset for help on using the changeset viewer.