REST API: Support non-Latin characters in template route regex.

Non-Latin characters are URL-encoded (e.g. `%cf%84%ce%b5%cf%83%cf%84`). Matching `%` in the route ensures templates with non-Latin titles can be properly saved.

Props antonyagrios, mburridge.
Fixes #57329.


git-svn-id: https://develop.svn.wordpress.org/trunk@55294 602fd350-edb4-49c9-b593-d223f7449a82
This commit is contained in:
Daniel Bachhuber 2023-02-07 23:45:18 +00:00
parent 9c4ddc2e9e
commit b46b855070
13 changed files with 157 additions and 13 deletions

View File

@ -105,7 +105,7 @@ class WP_REST_Templates_Controller extends WP_REST_Controller {
// Excludes invalid directory name characters: `/:<>*?"|`.
'([^\/:<>\*\?"\|]+(?:\/[^\/:<>\*\?"\|]+)?)',
// Matches the template name.
'[\/\w-]+'
'[\/\w%-]+'
),
array(
'args' => array(
@ -834,7 +834,7 @@ class WP_REST_Templates_Controller extends WP_REST_Controller {
'context' => array( 'embed', 'view', 'edit' ),
'required' => true,
'minLength' => 1,
'pattern' => '[a-zA-Z0-9_\-]+',
'pattern' => '[a-zA-Z0-9_\%-]+',
),
'theme' => array(
'description' => __( 'Theme identifier for the template.' ),

View File

@ -0,0 +1,4 @@
<?php
/**
* Block theme.
*/

View File

@ -0,0 +1,3 @@
<?php
echo 'PHP template for page with ID 1';

View File

@ -0,0 +1,3 @@
<!-- wp:paragraph -->
<p>Small Header Template Part</p>
<!-- /wp:paragraph -->

View File

@ -0,0 +1,7 @@
/*
Theme Name: Block Theme Non Latin
Theme URI: https://wordpress.org/
Description: Has different characters in theme directory name for testing purposes.
Version: 0.0.1
Text Domain: block-theme
*/

View File

@ -0,0 +1,3 @@
<!-- wp:paragraph -->
<p>(τεστ) Page Template</p>
<!-- /wp:paragraph -->

View File

@ -0,0 +1,3 @@
<!-- wp:paragraph -->
<p>(测试) Page Template</p>
<!-- /wp:paragraph -->

View File

@ -0,0 +1,80 @@
{
"version": 1,
"settings": {
"color": {
"palette": [
{
"slug": "light",
"name": "Light",
"color": "#f5f7f9"
},
{
"slug": "dark",
"name": "Dark",
"color": "#000"
}
],
"gradients": [
{
"name": "Custom gradient",
"gradient": "linear-gradient(135deg,rgba(0,0,0) 0%,rgb(0,0,0) 100%)",
"slug": "custom-gradient"
}
],
"custom": false,
"customGradient": false
},
"typography": {
"fontSizes": [
{
"name": "Custom",
"slug": "custom",
"size": "100px"
}
],
"customFontSize": false,
"customLineHeight": true
},
"spacing": {
"units": [
"rem"
],
"customPadding": true
},
"blocks": {
"core/paragraph": {
"color": {
"palette": [
{
"slug": "light",
"name": "Light",
"color": "#f5f7f9"
}
]
}
}
}
},
"customTemplates": [
{
"name": "page-τεστ",
"title": "Homepage template"
},
{
"name": "page-测试",
"title": "Homepage template"
}
],
"templateParts": [
{
"name": "small-header-τεστ",
"title": "Small Header",
"area": "header"
},
{
"name": "small-header-测试",
"title": "Small Header",
"area": "header"
},
]
}

View File

@ -146,14 +146,14 @@ class WP_Test_REST_Schema_Initialization extends WP_Test_REST_TestCase {
'/wp/v2/settings',
'/wp/v2/template-parts',
'/wp/v2/template-parts/(?P<id>[\d]+)/autosaves',
'/wp/v2/template-parts/(?P<id>([^\/:<>\*\?"\|]+(?:\/[^\/:<>\*\?"\|]+)?)[\/\w-]+)',
'/wp/v2/template-parts/(?P<id>([^\/:<>\*\?"\|]+(?:\/[^\/:<>\*\?"\|]+)?)[\/\w%-]+)',
'/wp/v2/template-parts/(?P<parent>[\d]+)/autosaves/(?P<id>[\d]+)',
'/wp/v2/template-parts/(?P<parent>[\d]+)/revisions',
'/wp/v2/template-parts/(?P<parent>[\d]+)/revisions/(?P<id>[\d]+)',
'/wp/v2/template-parts/lookup',
'/wp/v2/templates',
'/wp/v2/templates/(?P<id>[\d]+)/autosaves',
'/wp/v2/templates/(?P<id>([^\/:<>\*\?"\|]+(?:\/[^\/:<>\*\?"\|]+)?)[\/\w-]+)',
'/wp/v2/templates/(?P<id>([^\/:<>\*\?"\|]+(?:\/[^\/:<>\*\?"\|]+)?)[\/\w%-]+)',
'/wp/v2/templates/(?P<parent>[\d]+)/autosaves/(?P<id>[\d]+)',
'/wp/v2/templates/(?P<parent>[\d]+)/revisions',
'/wp/v2/templates/(?P<parent>[\d]+)/revisions/(?P<id>[\d]+)',

View File

@ -66,7 +66,7 @@ class Tests_REST_WpRestTemplatesController extends WP_Test_REST_Controller_Testc
'Templates route does not exist'
);
$this->assertArrayHasKey(
'/wp/v2/templates/(?P<id>([^\/:<>\*\?"\|]+(?:\/[^\/:<>\*\?"\|]+)?)[\/\w-]+)',
'/wp/v2/templates/(?P<id>([^\/:<>\*\?"\|]+(?:\/[^\/:<>\*\?"\|]+)?)[\/\w%-]+)',
$routes,
'Single template based on the given ID route does not exist'
);
@ -294,6 +294,46 @@ class Tests_REST_WpRestTemplatesController extends WP_Test_REST_Controller_Testc
'post_excerpt' => 'Description of page home template.',
),
),
'template parts: parent theme with non latin characters' => array(
'theme_dir' => 'themedir1/block-theme-non-latin',
'template' => 'small-header-%cf%84%ce%b5%cf%83%cf%84',
'args' => array(
'post_name' => 'small-header-τεστ',
'post_title' => 'Small Header τεστ Template',
'post_content' => file_get_contents( $theme_root_dir . '/block-theme-non-latin/parts/small-header-τεστ.html' ),
'post_excerpt' => 'Description of small header τεστ template.',
),
),
'template: parent theme with non latin name' => array(
'theme_dir' => 'themedir1/block-theme-non-latin',
'template' => 'page-%cf%84%ce%b5%cf%83%cf%84',
'args' => array(
'post_name' => 'page-τεστ',
'post_title' => 'τεστ Page Template',
'post_content' => file_get_contents( $theme_root_dir . 'block-theme-non-latin/templates/page-τεστ.html' ),
'post_excerpt' => 'Description of page τεστ template.',
),
),
'template parts: parent theme with chinese characters' => array(
'theme_dir' => 'themedir1/block-theme-non-latin',
'template' => 'small-header-%e6%b5%8b%e8%af%95',
'args' => array(
'post_name' => 'small-header-测试',
'post_title' => 'Small Header 测试 Template',
'post_content' => file_get_contents( $theme_root_dir . '/block-theme-non-latin/parts/small-header-测试.html' ),
'post_excerpt' => 'Description of small header 测试 template.',
),
),
'template: parent theme with non latin name using chinese characters' => array(
'theme_dir' => 'themedir1/block-theme-non-latin',
'template' => 'page-%e6%b5%8b%e8%af%95',
'args' => array(
'post_name' => 'page-测试',
'post_title' => '测试 Page Template',
'post_content' => file_get_contents( $theme_root_dir . 'block-theme-non-latin/templates/page-测试.html' ),
'post_excerpt' => 'Description of page 测试 template.',
),
),
'template: parent theme deprecated path' => array(
'theme_dir' => 'themedir1/block-theme-deprecated-path',
'template' => 'page-home',

View File

@ -181,6 +181,7 @@ class Tests_Theme_ThemeDir extends WP_UnitTestCase {
'Block Theme Child with no theme.json',
'Block Theme Child Theme With Fluid Typography',
'Block Theme Child Theme With Fluid Typography Config',
'Block Theme Non Latin',
'Block Theme [0.4.0]',
'Block Theme [1.0.0] in subdirectory',
'Block Theme Deprecated Path',

View File

@ -5105,7 +5105,7 @@ mockedApiResponse.Schema = {
"description": "Unique slug identifying the template.",
"type": "string",
"minLength": 1,
"pattern": "[a-zA-Z0-9_\\-]+",
"pattern": "[a-zA-Z0-9_\\%-]+",
"required": true
},
"theme": {
@ -5247,7 +5247,7 @@ mockedApiResponse.Schema = {
]
}
},
"/wp/v2/templates/(?P<id>([^\\/:<>\\*\\?\"\\|]+(?:\\/[^\\/:<>\\*\\?\"\\|]+)?)[\\/\\w-]+)": {
"/wp/v2/templates/(?P<id>([^\\/:<>\\*\\?\"\\|]+(?:\\/[^\\/:<>\\*\\?\"\\|]+)?)[\\/\\w%-]+)": {
"namespace": "wp/v2",
"methods": [
"GET",
@ -5296,7 +5296,7 @@ mockedApiResponse.Schema = {
"description": "Unique slug identifying the template.",
"type": "string",
"minLength": 1,
"pattern": "[a-zA-Z0-9_\\-]+",
"pattern": "[a-zA-Z0-9_\\%-]+",
"required": false
},
"theme": {
@ -5610,7 +5610,7 @@ mockedApiResponse.Schema = {
"description": "Unique slug identifying the template.",
"type": "string",
"minLength": 1,
"pattern": "[a-zA-Z0-9_\\-]+",
"pattern": "[a-zA-Z0-9_\\%-]+",
"required": false
},
"theme": {
@ -5789,7 +5789,7 @@ mockedApiResponse.Schema = {
"description": "Unique slug identifying the template.",
"type": "string",
"minLength": 1,
"pattern": "[a-zA-Z0-9_\\-]+",
"pattern": "[a-zA-Z0-9_\\%-]+",
"required": true
},
"theme": {
@ -5936,7 +5936,7 @@ mockedApiResponse.Schema = {
]
}
},
"/wp/v2/template-parts/(?P<id>([^\\/:<>\\*\\?\"\\|]+(?:\\/[^\\/:<>\\*\\?\"\\|]+)?)[\\/\\w-]+)": {
"/wp/v2/template-parts/(?P<id>([^\\/:<>\\*\\?\"\\|]+(?:\\/[^\\/:<>\\*\\?\"\\|]+)?)[\\/\\w%-]+)": {
"namespace": "wp/v2",
"methods": [
"GET",
@ -5985,7 +5985,7 @@ mockedApiResponse.Schema = {
"description": "Unique slug identifying the template.",
"type": "string",
"minLength": 1,
"pattern": "[a-zA-Z0-9_\\-]+",
"pattern": "[a-zA-Z0-9_\\%-]+",
"required": false
},
"theme": {
@ -6304,7 +6304,7 @@ mockedApiResponse.Schema = {
"description": "Unique slug identifying the template.",
"type": "string",
"minLength": 1,
"pattern": "[a-zA-Z0-9_\\-]+",
"pattern": "[a-zA-Z0-9_\\%-]+",
"required": false
},
"theme": {