diff --git a/src/wp-includes/block-template.php b/src/wp-includes/block-template.php index 85ad05cfc91ae..af2ed79fa6c30 100644 --- a/src/wp-includes/block-template.php +++ b/src/wp-includes/block-template.php @@ -233,6 +233,50 @@ function _block_template_render_title_tag() { echo '' . wp_get_document_title() . '' . "\n"; } +/** + * Prints the opening of the document a block theme renders into. + * + * Block themes have no header.php, so the markup that opens their documents lives in the + * template canvas rather than in the theme. Printing it from here lets anything that has to + * open a document on a block theme's behalf produce exactly what the canvas produces. + * + * @since 7.1.0 + * @access private + */ +function _wp_block_theme_document_start() { + echo '' . "\n"; + echo '\n"; + echo "\n"; + echo "\t" . '' . "\n"; + echo "\t"; + wp_head(); + echo "\n"; + echo "\n"; + echo '\n"; + wp_body_open(); +} + +/** + * Prints the closing of the document a block theme renders into. + * + * The counterpart to _wp_block_theme_document_start(). + * + * @since 7.1.0 + * @access private + */ +function _wp_block_theme_document_end() { + echo "\n"; + wp_footer(); + echo "\n"; + echo "\n"; +} + /** * Returns the markup for the current template. * diff --git a/src/wp-includes/general-template.php b/src/wp-includes/general-template.php index 29c30137e173d..c7c0df5cd9fb5 100644 --- a/src/wp-includes/general-template.php +++ b/src/wp-includes/general-template.php @@ -45,6 +45,19 @@ function get_header( $name = null, $args = array() ) { $templates[] = 'header.php'; + /* + * A block theme has no header.php by design, so locate_template() falls through to + * wp-includes/theme-compat/header.php: markup deprecated since 3.0.0 that opens the + * document with the old default theme's page frame and its site title and description. + * Print the same document opening the template canvas uses instead, so a block theme + * gets its own markup whether the page is rendered by a block template or by a caller + * of this function. + */ + if ( wp_is_block_theme() && ! _wp_theme_has_template( $templates ) ) { + _wp_block_theme_document_start(); + return; + } + if ( ! locate_template( $templates, true, true, $args ) ) { return false; } @@ -89,6 +102,12 @@ function get_footer( $name = null, $args = array() ) { $templates[] = 'footer.php'; + // See get_header(): block themes close their document the way the canvas does. + if ( wp_is_block_theme() && ! _wp_theme_has_template( $templates ) ) { + _wp_block_theme_document_end(); + return; + } + if ( ! locate_template( $templates, true, true, $args ) ) { return false; } diff --git a/src/wp-includes/template-canvas.php b/src/wp-includes/template-canvas.php index 8f631e5a77dfb..1631832b91fd6 100644 --- a/src/wp-includes/template-canvas.php +++ b/src/wp-includes/template-canvas.php @@ -10,18 +10,10 @@ * This needs to run before so that blocks can add scripts and styles in wp_head(). */ $template_html = get_the_block_template_html(); -?> -> - - - - -> - +_wp_block_theme_document_start(); +?> - - - - +assertNull( get_footer() ); } + /** + * Tests that a block theme opens its own document rather than the deprecated fallback. + * + * A block theme ships no header.php, so locate_template() reaches + * wp-includes/theme-compat/header.php, whose markup opens the page with the old default + * theme's frame, site title and description. + * + * @ticket 55023 + * + * @covers ::get_header + */ + public function test_get_header_uses_block_theme_document_when_theme_has_no_header() { + switch_theme( 'block-theme' ); + + $output = get_echo( 'get_header' ); + + $this->assertStringContainsString( '', $output, 'The document should be opened.' ); + $this->assertStringContainsString( 'assertStringNotContainsString( 'id="page"', $output, 'The theme-compat page frame should not be printed.' ); + $this->assertStringNotContainsString( 'id="header"', $output, 'The theme-compat header should not be printed.' ); + } + + /** + * Tests that a block theme closes the document it opened. + * + * @ticket 55023 + * + * @covers ::get_footer + */ + public function test_get_footer_uses_block_theme_document_when_theme_has_no_footer() { + switch_theme( 'block-theme' ); + + // As a page does: wp_head() is where the back-compat skip link action is unhooked. + get_echo( 'get_header' ); + $output = get_echo( 'get_footer' ); + + $this->assertStringContainsString( '', $output, 'The body element should be closed.' ); + $this->assertStringContainsString( '', $output, 'The document should be closed.' ); + $this->assertStringNotContainsString( 'proudly powered by', $output, 'The theme-compat footer should not be printed.' ); + $this->assertStringNotContainsString( 'id="footer"', $output, 'The theme-compat footer should not be printed.' ); + } + + /** + * Tests that the deprecation notice for a missing header.php is not raised for block themes, + * which are not expected to provide one. + * + * @ticket 55023 + * + * @covers ::get_header + */ + public function test_get_header_does_not_deprecate_for_block_theme() { + switch_theme( 'block-theme' ); + + // Would trigger the deprecation notice handled by WP_UnitTestCase and fail the test. + get_echo( 'get_header' ); + + $this->assertTrue( wp_is_block_theme(), 'The test should run on a block theme.' ); + } + + /** + * Tests that a classic theme without header.php keeps the deprecated fallback, so themes + * relying on it are unaffected. camelCase is a classic fixture theme with no header.php. + * + * @ticket 55023 + * + * @expectedDeprecated Theme without header.php + * + * @covers ::get_header + */ + public function test_get_header_still_falls_back_for_classic_theme() { + switch_theme( 'camelCase' ); + + $output = get_echo( 'get_header' ); + + $this->assertStringContainsString( 'id="page"', $output, 'The theme-compat markup should still be printed for classic themes.' ); + } + /** * @ticket 40969 *