Make WordPress Core

Changeset 52399


Ignore:
Timestamp:
12/21/2021 04:12:06 AM (3 years ago)
Author:
hellofromTonya
Message:

REST API: Support . in theme directory names in WP_REST_Global_Styles_Controller, WP_REST_Templates_Controller, and WP_REST_Themes_Controller.

Regex changes from [52376] are reverted to restore the original regex patterns. Why? [52376] used an include characters pattern, which was too limiting. It did not account for localized characters, such as é, or other valid directory name characters.

The original theme directory regex pattern, i.e. [^.\/]+(?:\/[^.\/]+)? excluded the period . character. Removing the . character resolves the reported issue by allowing matching for themes/theme-dirname-1.0/ or themes/<subdirname>/theme-dirname-1.0/.

As the pattern used an exclude approach, all characters are valid for matching except for /. However, not all characters are cross-platform valid for directory names. For example, the characters /:<>*?"| are not valid on Windows OS. The pattern now excludes those characters.

The theme's directory (or subdirectory) name pattern matching is now used in WP_REST_Global_Styles_Controller, WP_REST_Templates_Controller, and WP_REST_Themes_Controller.

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

Props costdev, hellofromTonya, spacedmonkey, TimothyBlynJacobs, bijayyadav, kafleg.
Fixes #54596.

Location:
trunk
Files:
13 added
9 edited

Legend:

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

    r52376 r52399  
    4242        register_rest_route(
    4343            $this->namespace,
    44             '/' . $this->rest_base . '/themes/(?P<stylesheet>[\/\s%\w\.\(\)\[\]\@_\-]+)',
     44            // The route.
     45            sprintf(
     46                '/%s/themes/(?P<stylesheet>%s)',
     47                $this->rest_base,
     48                // Matches theme's directory: `/themes/<subdirectory>/<theme>/` or `/themes/<theme>/`.
     49                // Excludes invalid directory name characters: `/:<>*?"|`.
     50                '[^\/:<>\*\?"\|]+(?:\/[^\/:<>\*\?"\|]+)?'
     51            ),
    4552            array(
    4653                array(
     
    6269        register_rest_route(
    6370            $this->namespace,
    64             '/' . $this->rest_base . '/(?P<id>[\/\s%\w\.\(\)\[\]\@_\-]+)',
     71            '/' . $this->rest_base . '/(?P<id>[\/\w-]+)',
    6572            array(
    6673                array(
     
    8996    /**
    9097     * 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`.
     98     * For example, `wp/v2/global-styles/twentytwentytwo%200.4.0`
     99     * would be decoded to `twentytwentytwo 0.4.0`.
    93100     *
    94101     * @since 5.9.0
  • trunk/src/wp-includes/rest-api/endpoints/class-wp-rest-templates-controller.php

    r52376 r52399  
    6969        register_rest_route(
    7070            $this->namespace,
    71             '/' . $this->rest_base . '/(?P<id>[\/\s%\w\.\(\)\[\]\@_\-]+)',
     71            // The route.
     72            sprintf(
     73                '/%s/(?P<id>%s%s)',
     74                $this->rest_base,
     75                // Matches theme's directory: `/themes/<subdirectory>/<theme>/` or `/themes/<theme>/`.
     76                // Excludes invalid directory name characters: `/:<>*?"|`.
     77                '([^\/:<>\*\?"\|]+(?:\/[^\/:<>\*\?"\|]+)?)',
     78                // Matches the template name.
     79                '[\/\w-]+'
     80            ),
    7281            array(
    7382                'args'   => array(
     
    150159     */
    151160    public function _sanitize_template_id( $id ) {
    152         // Decode empty space.
    153161        $id = urldecode( $id );
    154162
  • trunk/src/wp-includes/rest-api/endpoints/class-wp-rest-themes-controller.php

    r52372 r52399  
    1717class WP_REST_Themes_Controller extends WP_REST_Controller {
    1818
    19     const PATTERN = '[^.\/]+(?:\/[^.\/]+)?';
     19    /**
     20     * Matches theme's directory: `/themes/<subdirectory>/<theme>/` or `/themes/<theme>/`.
     21     * Excludes invalid directory name characters: `/:<>*?"|`.
     22     */
     23    const PATTERN = '[^\/:<>\*\?"\|]+(?:\/[^\/:<>\*\?"\|]+)?';
    2024
    2125    /**
     
    5761                'args'   => array(
    5862                    'stylesheet' => array(
    59                         'description' => __( "The theme's stylesheet. This uniquely identifies the theme." ),
    60                         'type'        => 'string',
     63                        'description'       => __( "The theme's stylesheet. This uniquely identifies the theme." ),
     64                        'type'              => 'string',
     65                        'sanitize_callback' => array( $this, '_sanitize_stylesheet_callback' ),
    6166                    ),
    6267                ),
     
    6974            )
    7075        );
     76    }
     77
     78    /**
     79     * Sanitize the stylesheet to decode endpoint.
     80     *
     81     * @since 5.9.0
     82     *
     83     * @param string $stylesheet The stylesheet name.
     84     * @return string Sanitized stylesheet.
     85     */
     86    public function _sanitize_stylesheet_callback( $stylesheet ) {
     87        return urldecode( $stylesheet );
    7188    }
    7289
  • trunk/tests/phpunit/tests/rest-api/rest-global-styles-controller.php

    r52376 r52399  
    9999        $routes = rest_get_server()->get_routes();
    100100        $this->assertArrayHasKey(
    101             '/wp/v2/global-styles/(?P<id>[\/\s%\w\.\(\)\[\]\@_\-]+)',
     101            '/wp/v2/global-styles/(?P<id>[\/\w-]+)',
    102102            $routes,
    103103            'Single global style based on the given ID route does not exist'
     
    105105        $this->assertCount(
    106106            2,
    107             $routes['/wp/v2/global-styles/(?P<id>[\/\s%\w\.\(\)\[\]\@_\-]+)'],
     107            $routes['/wp/v2/global-styles/(?P<id>[\/\w-]+)'],
    108108            'Single global style based on the given ID route does not have exactly two elements'
    109109        );
    110110        $this->assertArrayHasKey(
    111             '/wp/v2/global-styles/themes/(?P<stylesheet>[\/\s%\w\.\(\)\[\]\@_\-]+)',
     111            '/wp/v2/global-styles/themes/(?P<stylesheet>[^\/:<>\*\?"\|]+(?:\/[^\/:<>\*\?"\|]+)?)',
    112112            $routes,
    113113            'Theme global styles route does not exist'
     
    115115        $this->assertCount(
    116116            1,
    117             $routes['/wp/v2/global-styles/themes/(?P<stylesheet>[\/\s%\w\.\(\)\[\]\@_\-]+)'],
     117            $routes['/wp/v2/global-styles/themes/(?P<stylesheet>[^\/:<>\*\?"\|]+(?:\/[^\/:<>\*\?"\|]+)?)'],
    118118            'Theme global styles route does not have exactly one element'
    119119        );
     
    165165     * @covers WP_REST_Global_Styles_Controller::get_theme_item
    166166     * @ticket 54596
    167      */
    168     public function test_get_theme_item_invalid_theme_dirname( $theme_dirname ) {
     167     *
     168     * @param string $theme_dirname Theme directory to test.
     169     * @param string $expected      Expected error code.
     170     */
     171    public function test_get_theme_item_invalid_theme_dirname( $theme_dirname, $expected ) {
    169172        wp_set_current_user( self::$admin_id );
    170173        switch_theme( $theme_dirname );
     
    172175        $request  = new WP_REST_Request( 'GET', '/wp/v2/global-styles/themes/' . $theme_dirname );
    173176        $response = rest_get_server()->dispatch( $request );
    174         $this->assertErrorResponse( 'rest_no_route', $response, 404 );
     177        $this->assertErrorResponse( $expected, $response, 404 );
    175178    }
    176179
     
    182185    public function data_get_theme_item_invalid_theme_dirname() {
    183186        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' ),
     187            '+'                      => array(
     188                'theme_dirname' => 'my+theme+',
     189                'expected'      => 'rest_theme_not_found',
     190            ),
     191            ':'                      => array(
     192                'theme_dirname' => 'my:theme:',
     193                'expected'      => 'rest_no_route',
     194            ),
     195            '<>'                     => array(
     196                'theme_dirname' => 'my<theme>',
     197                'expected'      => 'rest_no_route',
     198            ),
     199            '*'                      => array(
     200                'theme_dirname' => 'my*theme*',
     201                'expected'      => 'rest_no_route',
     202            ),
     203            '?'                      => array(
     204                'theme_dirname' => 'my?theme?',
     205                'expected'      => 'rest_no_route',
     206            ),
     207            '"'                      => array(
     208                'theme_dirname' => 'my"theme?"',
     209                'expected'      => 'rest_no_route',
     210            ),
     211            '| (invalid on Windows)' => array(
     212                'theme_dirname' => 'my|theme|',
     213                'expected'      => 'rest_no_route',
     214            ),
     215            // Themes deep in subdirectories.
     216            '2 subdirectories deep'  => array(
     217                'theme_dirname' => 'subdir/subsubdir/mytheme',
     218                'expected'      => 'rest_global_styles_not_found',
     219            ),
    190220        );
    191221    }
     
    195225     * @covers WP_REST_Global_Styles_Controller::get_theme_item
    196226     * @ticket 54596
     227     *
     228     * @param string $theme Theme directory to test.
    197229     */
    198230    public function test_get_theme_item( $theme ) {
     
    217249    public function data_get_theme_item() {
    218250        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' ),
     251            'alphabetic'                     => array( 'mytheme' ),
     252            'alphanumeric'                   => array( 'mythemev1' ),
     253            'àáâãäåæç'                       => array( 'àáâãäåæç' ),
     254            'space'                          => array( 'my theme' ),
     255            '-_.'                            => array( 'my_theme-0.1' ),
     256            '[]'                             => array( 'my[theme]' ),
     257            '()'                             => array( 'my(theme)' ),
     258            '{}'                             => array( 'my{theme}' ),
     259            '&=#@!$,^~%'                     => array( 'theme &=#@!$,^~%' ),
     260            'all combined'                   => array( 'thémé {}&=@!$,^~%[0.1](-_-)' ),
     261
     262            // Themes in a subdirectory.
     263            'subdir: alphabetic'             => array( 'subdir/mytheme' ),
     264            'subdir: alphanumeric in theme'  => array( 'subdir/mythemev1' ),
     265            'subdir: alphanumeric in subdir' => array( 'subdirv1/mytheme' ),
     266            'subdir: alphanumeric in both'   => array( 'subdirv1/mythemev1' ),
     267            'subdir: àáâãäåæç in theme'      => array( 'subdir/àáâãäåæç' ),
     268            'subdir: àáâãäåæç in subdir'     => array( 'àáâãäåæç/mythemev1' ),
     269            'subdir: àáâãäåæç in both'       => array( 'àáâãäåæç/àáâãäåæç' ),
     270            'subdir: space in theme'         => array( 'subdir/my theme' ),
     271            'subdir: space in subdir'        => array( 'sub dir/mytheme' ),
     272            'subdir: space in both'          => array( 'sub dir/my theme' ),
     273            'subdir: -_. in theme'           => array( 'subdir/my_theme-0.1' ),
     274            'subdir: -_. in subdir'          => array( 'sub_dir-0.1/mytheme' ),
     275            'subdir: -_. in both'            => array( 'sub_dir-0.1/my_theme-0.1' ),
     276            'subdir: all combined in theme'  => array( 'subdir/thémé {}&=@!$,^~%[0.1](-_-)' ),
     277            'subdir: all combined in subdir' => array( 'sűbdīr {}&=@!$,^~%[0.1](-_-)/mytheme' ),
     278            'subdir: all combined in both'   => array( 'sűbdīr {}&=@!$,^~%[0.1](-_-)/thémé {}&=@!$,^~%[0.1](-_-)' ),
    231279        );
    232280    }
  • trunk/tests/phpunit/tests/rest-api/rest-schema-setup.php

    r52376 r52399  
    136136            '/wp/v2/comments',
    137137            '/wp/v2/comments/(?P<id>[\\d]+)',
    138             '/wp/v2/global-styles/(?P<id>[\/\s%\w\.\(\)\[\]\@_\-]+)',
    139             '/wp/v2/global-styles/themes/(?P<stylesheet>[\/\s%\w\.\(\)\[\]\@_\-]+)',
     138            '/wp/v2/global-styles/(?P<id>[\/\w-]+)',
     139            '/wp/v2/global-styles/themes/(?P<stylesheet>[^\/:<>\*\?"\|]+(?:\/[^\/:<>\*\?"\|]+)?)',
    140140            '/wp/v2/search',
    141141            '/wp/v2/block-renderer/(?P<name>[a-z0-9-]+/[a-z0-9-]+)',
     
    145145            '/wp/v2/settings',
    146146            '/wp/v2/template-parts',
    147             '/wp/v2/template-parts/(?P<id>[\/\s%\w\.\(\)\[\]\@_\-]+)',
    148147            '/wp/v2/template-parts/(?P<id>[\d]+)/autosaves',
     148            '/wp/v2/template-parts/(?P<id>([^\/:<>\*\?"\|]+(?:\/[^\/:<>\*\?"\|]+)?)[\/\w-]+)',
    149149            '/wp/v2/template-parts/(?P<parent>[\d]+)/autosaves/(?P<id>[\d]+)',
    150150            '/wp/v2/template-parts/(?P<parent>[\d]+)/revisions',
    151151            '/wp/v2/template-parts/(?P<parent>[\d]+)/revisions/(?P<id>[\d]+)',
    152152            '/wp/v2/templates',
    153             '/wp/v2/templates/(?P<id>[\/\s%\w\.\(\)\[\]\@_\-]+)',
    154153            '/wp/v2/templates/(?P<id>[\d]+)/autosaves',
     154            '/wp/v2/templates/(?P<id>([^\/:<>\*\?"\|]+(?:\/[^\/:<>\*\?"\|]+)?)[\/\w-]+)',
    155155            '/wp/v2/templates/(?P<parent>[\d]+)/autosaves/(?P<id>[\d]+)',
    156156            '/wp/v2/templates/(?P<parent>[\d]+)/revisions',
    157157            '/wp/v2/templates/(?P<parent>[\d]+)/revisions/(?P<id>[\d]+)',
    158158            '/wp/v2/themes',
    159             '/wp/v2/themes/(?P<stylesheet>[^.\/]+(?:\/[^.\/]+)?)',
     159            '/wp/v2/themes/(?P<stylesheet>[^\/:<>\*\?"\|]+(?:\/[^\/:<>\*\?"\|]+)?)',
    160160            '/wp/v2/plugins',
    161161            '/wp/v2/plugins/(?P<plugin>[^.\/]+(?:\/[^.\/]+)?)',
  • trunk/tests/phpunit/tests/rest-api/rest-themes-controller.php

    r52017 r52399  
    12861286
    12871287    /**
     1288     * @dataProvider data_get_item_non_subdir_theme
     1289     * @ticket 54596
     1290     * @covers WP_REST_Themes_Controller::get_item
     1291     *
     1292     * @param string $theme_dir     Theme directory to test.
     1293     * @param string $expected_name Expected theme name.
     1294     */
     1295    public function test_get_item_non_subdir_theme( $theme_dir, $expected_name ) {
     1296        wp_set_current_user( self::$admin_id );
     1297        $request  = new WP_REST_Request( 'GET', self::$themes_route . $theme_dir );
     1298        $response = rest_do_request( $request );
     1299
     1300        $this->assertSame( 200, $response->get_status() );
     1301        $this->assertSame( $expected_name, $response->get_data()['name']['raw'] );
     1302    }
     1303
     1304    /**
     1305     * Data provider.
     1306     *
     1307     * @return array
     1308     */
     1309    public function data_get_item_non_subdir_theme() {
     1310        return array(
     1311            'parent theme'                => array(
     1312                'theme_dir'     => '/block-theme',
     1313                'expected_name' => 'Block Theme',
     1314            ),
     1315            'child theme'                 => array(
     1316                'theme_dir'     => '/block-theme-child',
     1317                'expected_name' => 'Block Theme Child Theme',
     1318            ),
     1319            'theme with _-[]. characters' => array(
     1320                'theme_dir'     => '/block_theme-[0.4.0]',
     1321                'expected_name' => 'Block Theme [0.4.0]',
     1322            ),
     1323        );
     1324    }
     1325
     1326    /**
     1327     * @dataProvider data_get_item_subdirectory_theme
    12881328     * @ticket 54349
    1289      */
    1290     public function test_get_item_subdirectory_theme() {
     1329     * @ticket 54596
     1330     * @covers WP_REST_Themes_Controller::get_item
     1331     *
     1332     * @param string $theme_dir     Theme directory to test.
     1333     * @param string $expected_name Expected theme name.
     1334     */
     1335    public function test_get_item_subdirectory_theme( $theme_dir, $expected_name ) {
    12911336        wp_set_current_user( self::$admin_id );
    1292         $request  = new WP_REST_Request( 'GET', self::$themes_route . '/subdir/theme2' );
     1337        $request  = new WP_REST_Request( 'GET', self::$themes_route . $theme_dir );
    12931338        $response = rest_do_request( $request );
    12941339
    1295         $this->assertSame( 200, $response->get_status() );
    1296         $this->assertSame( 'My Subdir Theme', $response->get_data()['name']['raw'] );
     1340        $this->assertSame(
     1341            200,
     1342            $response->get_status(),
     1343            'A 200 OK status was not returned.'
     1344        );
     1345        $this->assertSame(
     1346            $expected_name,
     1347            $response->get_data()['name']['raw'],
     1348            'The actual theme name was not the expected theme name.'
     1349        );
     1350    }
     1351
     1352    /**
     1353     * Data provider.
     1354     *
     1355     * @return array
     1356     */
     1357    public function data_get_item_subdirectory_theme() {
     1358        return array(
     1359            'theme2'                      => array(
     1360                'theme_dir'     => '/subdir/theme2',
     1361                'expected_name' => 'My Subdir Theme',
     1362            ),
     1363            'theme with _-[]. characters' => array(
     1364                'theme_dir'     => '/subdir/block_theme-[1.0.0]',
     1365                'expected_name' => 'Block Theme [1.0.0] in subdirectory',
     1366            ),
     1367        );
    12971368    }
    12981369
  • trunk/tests/phpunit/tests/rest-api/wpRestTemplatesController.php

    r52376 r52399  
    6666        );
    6767        $this->assertArrayHasKey(
    68             '/wp/v2/templates/(?P<id>[\/\s%\w\.\(\)\[\]\@_\-]+)',
     68            '/wp/v2/templates/(?P<id>([^\/:<>\*\?"\|]+(?:\/[^\/:<>\*\?"\|]+)?)[\/\w-]+)',
    6969            $routes,
    7070            'Single template based on the given ID route does not exist'
     
    206206            array( '/wp/v2/templates/default/my_template' ),
    207207            array( '/wp/v2/templates/default//my_template' ),
     208        );
     209    }
     210
     211    /**
     212     * @dataProvider data_get_item_with_valid_theme_dirname
     213     * @covers WP_REST_Templates_Controller::get_item
     214     * @ticket 54596
     215     *
     216     * @param string $theme_dir Theme directory to test.
     217     * @param string $template  Template to test.
     218     * @param array  $args      Arguments to create the 'wp_template" post.
     219     */
     220    public function test_get_item_with_valid_theme_dirname( $theme_dir, $template, array $args ) {
     221        wp_set_current_user( self::$admin_id );
     222        switch_theme( $theme_dir );
     223
     224        // Set up template post.
     225        $args['post_type'] = 'wp_template';
     226        $args['tax_input'] = array(
     227            'wp_theme' => array(
     228                get_stylesheet(),
     229            ),
     230        );
     231        $post              = self::factory()->post->create_and_get( $args );
     232        wp_set_post_terms( $post->ID, get_stylesheet(), 'wp_theme' );
     233
     234        $request  = new WP_REST_Request( 'GET', "/wp/v2/templates/{$theme_dir}//{$template}" );
     235        $response = rest_get_server()->dispatch( $request );
     236        $data     = $response->get_data();
     237        unset( $data['content'] );
     238        unset( $data['_links'] );
     239
     240        $this->assertSameSetsWithIndex(
     241            array(
     242                'id'             => "{$theme_dir}//{$template}",
     243                'theme'          => $theme_dir,
     244                'slug'           => $template,
     245                'source'         => 'custom',
     246                'origin'         => null,
     247                'type'           => 'wp_template',
     248                'description'    => $args['post_excerpt'],
     249                'title'          => array(
     250                    'raw'      => $args['post_title'],
     251                    'rendered' => $args['post_title'],
     252                ),
     253                'status'         => 'publish',
     254                'wp_id'          => $post->ID,
     255                'has_theme_file' => false,
     256                'is_custom'      => true,
     257                'author'         => self::$admin_id,
     258            ),
     259            $data
     260        );
     261    }
     262
     263    /**
     264     * Data provider.
     265     *
     266     * @return array
     267     */
     268    public function data_get_item_with_valid_theme_dirname() {
     269        $theme_root_dir = DIR_TESTDATA . '/themedir1/';
     270        return array(
     271            'template parts: parent theme'                => array(
     272                'theme_dir' => 'themedir1/block-theme',
     273                'template'  => 'small-header',
     274                'args'      => array(
     275                    'post_name'    => 'small-header',
     276                    'post_title'   => 'Small Header Template',
     277                    'post_content' => file_get_contents( $theme_root_dir . '/block-theme/parts/small-header.html' ),
     278                    'post_excerpt' => 'Description of small header template.',
     279                ),
     280            ),
     281            'template: parent theme'                      => array(
     282                'theme_dir' => 'themedir1/block-theme',
     283                'template'  => 'page-home',
     284                'args'      => array(
     285                    'post_name'    => 'page-home',
     286                    'post_title'   => 'Home Page Template',
     287                    'post_content' => file_get_contents( $theme_root_dir . 'block-theme/templates/page-home.html' ),
     288                    'post_excerpt' => 'Description of page home template.',
     289                ),
     290            ),
     291            'template: child theme'                       => array(
     292                'theme_dir' => 'themedir1/block-theme-child',
     293                'template'  => 'page-1',
     294                'args'      => array(
     295                    'post_name'    => 'page-1',
     296                    'post_title'   => 'Page 1 Template',
     297                    'post_content' => file_get_contents( $theme_root_dir . 'block-theme-child/templates/page-1.html' ),
     298                    'post_excerpt' => 'Description of page 1 template.',
     299                ),
     300            ),
     301            'template part: subdir with _-[]. characters' => array(
     302                'theme_dir' => 'themedir1/block_theme-[0.4.0]',
     303                'template'  => 'large-header',
     304                'args'      => array(
     305                    'post_name'    => 'large-header',
     306                    'post_title'   => 'Large Header Template Part',
     307                    'post_content' => file_get_contents( $theme_root_dir . 'block_theme-[0.4.0]/parts/large-header.html' ),
     308                    'post_excerpt' => 'Description of large header template.',
     309                ),
     310            ),
     311            'template: subdir with _-[]. characters'      => array(
     312                'theme_dir' => 'themedir1/block_theme-[0.4.0]',
     313                'template'  => 'page-large-header',
     314                'args'      => array(
     315                    'post_name'    => 'page-large-header',
     316                    'post_title'   => 'Page Large Template',
     317                    'post_content' => file_get_contents( $theme_root_dir . 'block_theme-[0.4.0]/templates/page-large-header.html' ),
     318                    'post_excerpt' => 'Description of page large template.',
     319                ),
     320            ),
    208321        );
    209322    }
  • trunk/tests/phpunit/tests/theme/themeDir.php

    r52391 r52399  
    164164            'Block Theme',
    165165            'Block Theme Child Theme',
     166            'Block Theme [0.4.0]',
     167            'Block Theme [1.0.0] in subdirectory',
    166168        );
    167169
  • trunk/tests/qunit/fixtures/wp-api-generated.js

    r52398 r52399  
    51415141            }
    51425142        },
    5143         "/wp/v2/templates/(?P<id>[\\/\\s%\\w\\.\\(\\)\\[\\]\\@_\\-]+)": {
     5143        "/wp/v2/templates/(?P<id>([^\\/:<>\\*\\?\"\\|]+(?:\\/[^\\/:<>\\*\\?\"\\|]+)?)[\\/\\w-]+)": {
    51445144            "namespace": "wp/v2",
    51455145            "methods": [
     
    57935793            }
    57945794        },
    5795         "/wp/v2/template-parts/(?P<id>[\\/\\s%\\w\\.\\(\\)\\[\\]\\@_\\-]+)": {
     5795        "/wp/v2/template-parts/(?P<id>([^\\/:<>\\*\\?\"\\|]+(?:\\/[^\\/:<>\\*\\?\"\\|]+)?)[\\/\\w-]+)": {
    57965796            "namespace": "wp/v2",
    57975797            "methods": [
     
    94259425            ]
    94269426        },
    9427         "/wp/v2/global-styles/themes/(?P<stylesheet>[\\/\\s%\\w\\.\\(\\)\\[\\]\\@_\\-]+)": {
     9427        "/wp/v2/global-styles/themes/(?P<stylesheet>[^\\/:<>\\*\\?\"\\|]+(?:\\/[^\\/:<>\\*\\?\"\\|]+)?)": {
    94289428            "namespace": "wp/v2",
    94299429            "methods": [
     
    94459445            ]
    94469446        },
    9447         "/wp/v2/global-styles/(?P<id>[\\/\\s%\\w\\.\\(\\)\\[\\]\\@_\\-]+)": {
     9447        "/wp/v2/global-styles/(?P<id>[\\/\\w-]+)": {
    94489448            "namespace": "wp/v2",
    94499449            "methods": [
     
    96699669            }
    96709670        },
    9671         "/wp/v2/themes/(?P<stylesheet>[^.\\/]+(?:\\/[^.\\/]+)?)": {
     9671        "/wp/v2/themes/(?P<stylesheet>[^\\/:<>\\*\\?\"\\|]+(?:\\/[^\\/:<>\\*\\?\"\\|]+)?)": {
    96729672            "namespace": "wp/v2",
    96739673            "methods": [
Note: See TracChangeset for help on using the changeset viewer.