Changeset 52399
- Timestamp:
- 12/21/2021 04:12:06 AM (3 years ago)
- 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 42 42 register_rest_route( 43 43 $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 ), 45 52 array( 46 53 array( … … 62 69 register_rest_route( 63 70 $this->namespace, 64 '/' . $this->rest_base . '/(?P<id>[\/\ s%\w\.\(\)\[\]\@_\-]+)',71 '/' . $this->rest_base . '/(?P<id>[\/\w-]+)', 65 72 array( 66 73 array( … … 89 96 /** 90 97 * Sanitize the global styles ID or stylesheet to decode endpoint. 91 * For example, `wp/v2/global-styles/t emplatetwentytwo%200.4.0`92 * would be decoded to `t emplatetwentytwo 0.4.0`.98 * For example, `wp/v2/global-styles/twentytwentytwo%200.4.0` 99 * would be decoded to `twentytwentytwo 0.4.0`. 93 100 * 94 101 * @since 5.9.0 -
trunk/src/wp-includes/rest-api/endpoints/class-wp-rest-templates-controller.php
r52376 r52399 69 69 register_rest_route( 70 70 $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 ), 72 81 array( 73 82 'args' => array( … … 150 159 */ 151 160 public function _sanitize_template_id( $id ) { 152 // Decode empty space.153 161 $id = urldecode( $id ); 154 162 -
trunk/src/wp-includes/rest-api/endpoints/class-wp-rest-themes-controller.php
r52372 r52399 17 17 class WP_REST_Themes_Controller extends WP_REST_Controller { 18 18 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 = '[^\/:<>\*\?"\|]+(?:\/[^\/:<>\*\?"\|]+)?'; 20 24 21 25 /** … … 57 61 'args' => array( 58 62 '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' ), 61 66 ), 62 67 ), … … 69 74 ) 70 75 ); 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 ); 71 88 } 72 89 -
trunk/tests/phpunit/tests/rest-api/rest-global-styles-controller.php
r52376 r52399 99 99 $routes = rest_get_server()->get_routes(); 100 100 $this->assertArrayHasKey( 101 '/wp/v2/global-styles/(?P<id>[\/\ s%\w\.\(\)\[\]\@_\-]+)',101 '/wp/v2/global-styles/(?P<id>[\/\w-]+)', 102 102 $routes, 103 103 'Single global style based on the given ID route does not exist' … … 105 105 $this->assertCount( 106 106 2, 107 $routes['/wp/v2/global-styles/(?P<id>[\/\ s%\w\.\(\)\[\]\@_\-]+)'],107 $routes['/wp/v2/global-styles/(?P<id>[\/\w-]+)'], 108 108 'Single global style based on the given ID route does not have exactly two elements' 109 109 ); 110 110 $this->assertArrayHasKey( 111 '/wp/v2/global-styles/themes/(?P<stylesheet>[ \/\s%\w\.\(\)\[\]\@_\-]+)',111 '/wp/v2/global-styles/themes/(?P<stylesheet>[^\/:<>\*\?"\|]+(?:\/[^\/:<>\*\?"\|]+)?)', 112 112 $routes, 113 113 'Theme global styles route does not exist' … … 115 115 $this->assertCount( 116 116 1, 117 $routes['/wp/v2/global-styles/themes/(?P<stylesheet>[ \/\s%\w\.\(\)\[\]\@_\-]+)'],117 $routes['/wp/v2/global-styles/themes/(?P<stylesheet>[^\/:<>\*\?"\|]+(?:\/[^\/:<>\*\?"\|]+)?)'], 118 118 'Theme global styles route does not have exactly one element' 119 119 ); … … 165 165 * @covers WP_REST_Global_Styles_Controller::get_theme_item 166 166 * @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 ) { 169 172 wp_set_current_user( self::$admin_id ); 170 173 switch_theme( $theme_dirname ); … … 172 175 $request = new WP_REST_Request( 'GET', '/wp/v2/global-styles/themes/' . $theme_dirname ); 173 176 $response = rest_get_server()->dispatch( $request ); 174 $this->assertErrorResponse( 'rest_no_route', $response, 404 );177 $this->assertErrorResponse( $expected, $response, 404 ); 175 178 } 176 179 … … 182 185 public function data_get_theme_item_invalid_theme_dirname() { 183 186 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 ), 190 220 ); 191 221 } … … 195 225 * @covers WP_REST_Global_Styles_Controller::get_theme_item 196 226 * @ticket 54596 227 * 228 * @param string $theme Theme directory to test. 197 229 */ 198 230 public function test_get_theme_item( $theme ) { … … 217 249 public function data_get_theme_item() { 218 250 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](-_-)' ), 231 279 ); 232 280 } -
trunk/tests/phpunit/tests/rest-api/rest-schema-setup.php
r52376 r52399 136 136 '/wp/v2/comments', 137 137 '/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>[^\/:<>\*\?"\|]+(?:\/[^\/:<>\*\?"\|]+)?)', 140 140 '/wp/v2/search', 141 141 '/wp/v2/block-renderer/(?P<name>[a-z0-9-]+/[a-z0-9-]+)', … … 145 145 '/wp/v2/settings', 146 146 '/wp/v2/template-parts', 147 '/wp/v2/template-parts/(?P<id>[\/\s%\w\.\(\)\[\]\@_\-]+)',148 147 '/wp/v2/template-parts/(?P<id>[\d]+)/autosaves', 148 '/wp/v2/template-parts/(?P<id>([^\/:<>\*\?"\|]+(?:\/[^\/:<>\*\?"\|]+)?)[\/\w-]+)', 149 149 '/wp/v2/template-parts/(?P<parent>[\d]+)/autosaves/(?P<id>[\d]+)', 150 150 '/wp/v2/template-parts/(?P<parent>[\d]+)/revisions', 151 151 '/wp/v2/template-parts/(?P<parent>[\d]+)/revisions/(?P<id>[\d]+)', 152 152 '/wp/v2/templates', 153 '/wp/v2/templates/(?P<id>[\/\s%\w\.\(\)\[\]\@_\-]+)',154 153 '/wp/v2/templates/(?P<id>[\d]+)/autosaves', 154 '/wp/v2/templates/(?P<id>([^\/:<>\*\?"\|]+(?:\/[^\/:<>\*\?"\|]+)?)[\/\w-]+)', 155 155 '/wp/v2/templates/(?P<parent>[\d]+)/autosaves/(?P<id>[\d]+)', 156 156 '/wp/v2/templates/(?P<parent>[\d]+)/revisions', 157 157 '/wp/v2/templates/(?P<parent>[\d]+)/revisions/(?P<id>[\d]+)', 158 158 '/wp/v2/themes', 159 '/wp/v2/themes/(?P<stylesheet>[^ .\/]+(?:\/[^.\/]+)?)',159 '/wp/v2/themes/(?P<stylesheet>[^\/:<>\*\?"\|]+(?:\/[^\/:<>\*\?"\|]+)?)', 160 160 '/wp/v2/plugins', 161 161 '/wp/v2/plugins/(?P<plugin>[^.\/]+(?:\/[^.\/]+)?)', -
trunk/tests/phpunit/tests/rest-api/rest-themes-controller.php
r52017 r52399 1286 1286 1287 1287 /** 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 1288 1328 * @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 ) { 1291 1336 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 ); 1293 1338 $response = rest_do_request( $request ); 1294 1339 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 ); 1297 1368 } 1298 1369 -
trunk/tests/phpunit/tests/rest-api/wpRestTemplatesController.php
r52376 r52399 66 66 ); 67 67 $this->assertArrayHasKey( 68 '/wp/v2/templates/(?P<id> [\/\s%\w\.\(\)\[\]\@_\-]+)',68 '/wp/v2/templates/(?P<id>([^\/:<>\*\?"\|]+(?:\/[^\/:<>\*\?"\|]+)?)[\/\w-]+)', 69 69 $routes, 70 70 'Single template based on the given ID route does not exist' … … 206 206 array( '/wp/v2/templates/default/my_template' ), 207 207 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 ), 208 321 ); 209 322 } -
trunk/tests/phpunit/tests/theme/themeDir.php
r52391 r52399 164 164 'Block Theme', 165 165 'Block Theme Child Theme', 166 'Block Theme [0.4.0]', 167 'Block Theme [1.0.0] in subdirectory', 166 168 ); 167 169 -
trunk/tests/qunit/fixtures/wp-api-generated.js
r52398 r52399 5141 5141 } 5142 5142 }, 5143 "/wp/v2/templates/(?P<id> [\\/\\s%\\w\\.\\(\\)\\[\\]\\@_\\-]+)": {5143 "/wp/v2/templates/(?P<id>([^\\/:<>\\*\\?\"\\|]+(?:\\/[^\\/:<>\\*\\?\"\\|]+)?)[\\/\\w-]+)": { 5144 5144 "namespace": "wp/v2", 5145 5145 "methods": [ … … 5793 5793 } 5794 5794 }, 5795 "/wp/v2/template-parts/(?P<id> [\\/\\s%\\w\\.\\(\\)\\[\\]\\@_\\-]+)": {5795 "/wp/v2/template-parts/(?P<id>([^\\/:<>\\*\\?\"\\|]+(?:\\/[^\\/:<>\\*\\?\"\\|]+)?)[\\/\\w-]+)": { 5796 5796 "namespace": "wp/v2", 5797 5797 "methods": [ … … 9425 9425 ] 9426 9426 }, 9427 "/wp/v2/global-styles/themes/(?P<stylesheet>[ \\/\\s%\\w\\.\\(\\)\\[\\]\\@_\\-]+)": {9427 "/wp/v2/global-styles/themes/(?P<stylesheet>[^\\/:<>\\*\\?\"\\|]+(?:\\/[^\\/:<>\\*\\?\"\\|]+)?)": { 9428 9428 "namespace": "wp/v2", 9429 9429 "methods": [ … … 9445 9445 ] 9446 9446 }, 9447 "/wp/v2/global-styles/(?P<id>[\\/\\ s%\\w\\.\\(\\)\\[\\]\\@_\\-]+)": {9447 "/wp/v2/global-styles/(?P<id>[\\/\\w-]+)": { 9448 9448 "namespace": "wp/v2", 9449 9449 "methods": [ … … 9669 9669 } 9670 9670 }, 9671 "/wp/v2/themes/(?P<stylesheet>[^ .\\/]+(?:\\/[^.\\/]+)?)": {9671 "/wp/v2/themes/(?P<stylesheet>[^\\/:<>\\*\\?\"\\|]+(?:\\/[^\\/:<>\\*\\?\"\\|]+)?)": { 9672 9672 "namespace": "wp/v2", 9673 9673 "methods": [
Note: See TracChangeset
for help on using the changeset viewer.