diff --git a/src/wp-admin/includes/class-theme-upgrader.php b/src/wp-admin/includes/class-theme-upgrader.php index 0a73f4b86c..4e874cd636 100644 --- a/src/wp-admin/includes/class-theme-upgrader.php +++ b/src/wp-admin/includes/class-theme-upgrader.php @@ -566,15 +566,28 @@ class Theme_Upgrader extends WP_Upgrader { ); } - // If it's not a child theme, it must have at least an index.php to be legit. - if ( empty( $info['Template'] ) && ! file_exists( $working_directory . 'index.php' ) ) { + /* + * Parent themes must contain an index file: + * - classic themes require /index.php + * - block themes require /templates/index.html or block-templates/index.html (deprecated 5.9.0). + */ + if ( + empty( $info['Template'] ) && + ! file_exists( $working_directory . 'index.php' ) && + ! file_exists( $working_directory . 'templates/index.html' ) && + ! file_exists( $working_directory . 'block-templates/index.html' ) + ) { return new WP_Error( 'incompatible_archive_theme_no_index', $this->strings['incompatible_archive'], sprintf( - /* translators: %s: index.php */ - __( 'The theme is missing the %s file.' ), - 'index.php' + /* translators: 1: templates/index.html, 2: index.php, 3: Documentation URL, 4: Template, 5: style.css */ + __( 'Template is missing. Standalone themes need to have a %1$s or %2$s template file. Child themes need to have a %4$s header in the %5$s stylesheet.' ), + 'templates/index.html', + 'index.php', + __( 'https://developer.wordpress.org/themes/advanced-topics/child-themes/' ), + 'Template', + 'style.css' ) ); } diff --git a/src/wp-includes/class-wp-theme.php b/src/wp-includes/class-wp-theme.php index cef5b034c6..c2398fb4eb 100644 --- a/src/wp-includes/class-wp-theme.php +++ b/src/wp-includes/class-wp-theme.php @@ -341,7 +341,9 @@ final class WP_Theme implements ArrayAccess { $this->template = $this->stylesheet; $theme_path = $this->theme_root . '/' . $this->stylesheet; - if ( ! file_exists( $theme_path . '/templates/index.html' ) + if ( + ! file_exists( $theme_path . '/templates/index.html' ) + && ! file_exists( $theme_path . '/block-templates/index.html' ) // Deprecated path support since 5.9.0. && ! file_exists( $theme_path . '/index.php' ) ) { $error_message = sprintf( diff --git a/src/wp-includes/theme.php b/src/wp-includes/theme.php index d511ab1a8d..55e2d13789 100644 --- a/src/wp-includes/theme.php +++ b/src/wp-includes/theme.php @@ -861,7 +861,9 @@ function validate_current_theme() { return true; } - if ( ! file_exists( get_template_directory() . '/templates/index.html' ) + if ( + ! file_exists( get_template_directory() . '/templates/index.html' ) + && ! file_exists( get_template_directory() . '/block-templates/index.html' ) // Deprecated path support since 5.9.0. && ! file_exists( get_template_directory() . '/index.php' ) ) { // Invalid. diff --git a/tests/phpunit/data/themedir1/block-theme-deprecated-path/block-templates/index.html b/tests/phpunit/data/themedir1/block-theme-deprecated-path/block-templates/index.html new file mode 100644 index 0000000000..e76bcaaf3a --- /dev/null +++ b/tests/phpunit/data/themedir1/block-theme-deprecated-path/block-templates/index.html @@ -0,0 +1,3 @@ + +

Index Template

+ \ No newline at end of file diff --git a/tests/phpunit/data/themedir1/block-theme-deprecated-path/block-templates/page-home.html b/tests/phpunit/data/themedir1/block-theme-deprecated-path/block-templates/page-home.html new file mode 100644 index 0000000000..6be0c70fb2 --- /dev/null +++ b/tests/phpunit/data/themedir1/block-theme-deprecated-path/block-templates/page-home.html @@ -0,0 +1,3 @@ + +

Page (Home) Template

+ \ No newline at end of file diff --git a/tests/phpunit/data/themedir1/block-theme-deprecated-path/block-templates/page.html b/tests/phpunit/data/themedir1/block-theme-deprecated-path/block-templates/page.html new file mode 100644 index 0000000000..61c493e1bd --- /dev/null +++ b/tests/phpunit/data/themedir1/block-theme-deprecated-path/block-templates/page.html @@ -0,0 +1,3 @@ + +

Page Template

+ \ No newline at end of file diff --git a/tests/phpunit/data/themedir1/block-theme-deprecated-path/parts/small-header.html b/tests/phpunit/data/themedir1/block-theme-deprecated-path/parts/small-header.html new file mode 100644 index 0000000000..5c18e98125 --- /dev/null +++ b/tests/phpunit/data/themedir1/block-theme-deprecated-path/parts/small-header.html @@ -0,0 +1,3 @@ + +

Small Header Template Part

+ \ No newline at end of file diff --git a/tests/phpunit/data/themedir1/block-theme-deprecated-path/style.css b/tests/phpunit/data/themedir1/block-theme-deprecated-path/style.css new file mode 100644 index 0000000000..5b4beaa4fc --- /dev/null +++ b/tests/phpunit/data/themedir1/block-theme-deprecated-path/style.css @@ -0,0 +1,7 @@ +/* +Theme Name: Block Theme Deprecated Path +Theme URI: https://wordpress.org/ +Description: For testing purposes only. +Version: 1.0.0 +Text Domain: block-theme-deprecated-path +*/ diff --git a/tests/phpunit/data/themedir1/block-theme-deprecated-path/styles/variation.json b/tests/phpunit/data/themedir1/block-theme-deprecated-path/styles/variation.json new file mode 100644 index 0000000000..ad3affb115 --- /dev/null +++ b/tests/phpunit/data/themedir1/block-theme-deprecated-path/styles/variation.json @@ -0,0 +1,23 @@ +{ + "version": 2, + "settings": { + "color": { + "palette": [ + { + "slug": "foreground", + "color": "#3F67C6", + "name": "Foreground" + } + ] + } + }, + "styles": { + "blocks": { + "core/post-title": { + "typography": { + "fontWeight": "700" + } + } + } + } +} \ No newline at end of file diff --git a/tests/phpunit/data/themedir1/block-theme-deprecated-path/theme.json b/tests/phpunit/data/themedir1/block-theme-deprecated-path/theme.json new file mode 100644 index 0000000000..38fcb1d9dd --- /dev/null +++ b/tests/phpunit/data/themedir1/block-theme-deprecated-path/theme.json @@ -0,0 +1,71 @@ +{ + "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-home", + "title": "Homepage template" + } + ], + "templateParts": [ + { + "name": "small-header", + "title": "Small Header", + "area": "header" + } + ] +} diff --git a/tests/phpunit/tests/rest-api/wpRestTemplatesController.php b/tests/phpunit/tests/rest-api/wpRestTemplatesController.php index 5242436bd6..ef4a726e30 100644 --- a/tests/phpunit/tests/rest-api/wpRestTemplatesController.php +++ b/tests/phpunit/tests/rest-api/wpRestTemplatesController.php @@ -288,6 +288,16 @@ class Tests_REST_WpRestTemplatesController extends WP_Test_REST_Controller_Testc 'post_excerpt' => 'Description of page home template.', ), ), + 'template: parent theme deprecated path' => array( + 'theme_dir' => 'themedir1/block-theme-deprecated-path', + 'template' => 'page-home', + 'args' => array( + 'post_name' => 'page-home', + 'post_title' => 'Home Page Template', + 'post_content' => file_get_contents( $theme_root_dir . 'block-theme-deprecated-path/block-templates/page-home.html' ), + 'post_excerpt' => 'Description of page home template.', + ), + ), 'template: child theme' => array( 'theme_dir' => 'themedir1/block-theme-child', 'template' => 'page-1', diff --git a/tests/phpunit/tests/theme/themeDir.php b/tests/phpunit/tests/theme/themeDir.php index 652a2f6cfa..edf7563815 100644 --- a/tests/phpunit/tests/theme/themeDir.php +++ b/tests/phpunit/tests/theme/themeDir.php @@ -165,14 +165,12 @@ class Tests_Theme_ThemeDir extends WP_UnitTestCase { 'Block Theme Child Theme', 'Block Theme [0.4.0]', 'Block Theme [1.0.0] in subdirectory', + 'Block Theme Deprecated Path', 'Webfonts theme', 'Empty `fontFace` in theme.json - no webfonts defined', ); - sort( $theme_names ); - sort( $expected ); - - $this->assertSame( $expected, $theme_names ); + $this->assertSameSets( $expected, $theme_names ); } /** diff --git a/tests/phpunit/tests/theme/wpTheme.php b/tests/phpunit/tests/theme/wpTheme.php index 1096ca29c6..dd3855bafc 100644 --- a/tests/phpunit/tests/theme/wpTheme.php +++ b/tests/phpunit/tests/theme/wpTheme.php @@ -309,6 +309,10 @@ class Tests_Theme_wpTheme extends WP_UnitTestCase { 'theme_dir' => 'block-theme-child', 'expected' => true, ), + 'deprecated block theme' => array( + 'theme_dir' => 'block-theme-deprecated-path', + 'expected' => true, + ), ); } @@ -335,42 +339,47 @@ class Tests_Theme_wpTheme extends WP_UnitTestCase { */ public function data_get_file_path() { return array( - 'no theme: no file given' => array( + 'no theme: no file given' => array( 'theme_dir' => 'nonexistent', 'file' => '', 'expected' => '/nonexistent', ), - 'parent theme: no file given' => array( + 'parent theme: no file given' => array( 'theme_dir' => 'block-theme', 'file' => '', 'expected' => '/block-theme', ), - 'child theme: no file given' => array( + 'child theme: no file given' => array( 'theme_dir' => 'block-theme-child', 'file' => '', 'expected' => '/block-theme-child', ), - 'nonexistent theme: file given' => array( + 'nonexistent theme: file given' => array( 'theme_dir' => 'nonexistent', 'file' => '/templates/page.html', 'expected' => '/nonexistent/templates/page.html', ), - 'parent theme: file exists' => array( + 'parent theme: file exists' => array( 'theme_dir' => 'block-theme', 'file' => '/templates/page-home.html', 'expected' => '/block-theme/templates/page-home.html', ), - 'parent theme: file does not exist' => array( + 'parent theme: deprecated file exists' => array( + 'theme_dir' => 'block-theme-deprecated-path', + 'file' => '/block-templates/page-home.html', + 'expected' => '/block-theme-deprecated-path/block-templates/page-home.html', + ), + 'parent theme: file does not exist' => array( 'theme_dir' => 'block-theme', 'file' => '/templates/nonexistent.html', 'expected' => '/block-theme/templates/nonexistent.html', ), - 'child theme: file exists' => array( + 'child theme: file exists' => array( 'theme_dir' => 'block-theme-child', 'file' => '/templates/page-1.html', 'expected' => '/block-theme-child/templates/page-1.html', ), - 'child theme: file does not exist' => array( + 'child theme: file does not exist' => array( 'theme_dir' => 'block-theme-child', 'file' => '/templates/nonexistent.html', 'expected' => '/block-theme/templates/nonexistent.html',