diff --git a/lib/compat/wordpress-5.9/block-template-utils.php b/lib/compat/wordpress-5.9/block-template-utils.php index ccbfd566b332c3..9f189d83e8b610 100644 --- a/lib/compat/wordpress-5.9/block-template-utils.php +++ b/lib/compat/wordpress-5.9/block-template-utils.php @@ -429,6 +429,40 @@ function _inject_theme_attribute_in_block_template_content( $template_content ) } } +if ( ! function_exists( '_remove_theme_attribute_in_block_template_content' ) ) { + /** + * Parses wp_template content and removes the theme attribute from + * each wp_template_part + * + * @param string $template_content serialized wp_template content. + * + * @return string Updated wp_template content. + */ + function _remove_theme_attribute_in_block_template_content( $template_content ) { + $has_updated_content = false; + $new_content = ''; + $template_blocks = parse_blocks( $template_content ); + + $blocks = _flatten_blocks( $template_blocks ); + foreach ( $blocks as $key => $block ) { + if ( 'core/template-part' === $block['blockName'] && isset( $block['attrs']['theme'] ) ) { + unset( $blocks[ $key ]['attrs']['theme'] ); + $has_updated_content = true; + } + } + + if ( $has_updated_content ) { + foreach ( $template_blocks as $block ) { + $new_content .= serialize_block( $block ); + } + + return $new_content; + } + + return $template_content; + } +} + if ( ! function_exists( '_build_block_template_result_from_file' ) ) { /** * Build a unified template object based on a theme file. diff --git a/lib/compat/wordpress-5.9/class-wp-rest-edit-site-export-controller.php b/lib/compat/wordpress-5.9/class-wp-rest-edit-site-export-controller.php new file mode 100644 index 00000000000000..be3d020060c5fe --- /dev/null +++ b/lib/compat/wordpress-5.9/class-wp-rest-edit-site-export-controller.php @@ -0,0 +1,83 @@ +namespace = 'wp-block-editor/v1'; + $this->rest_base = 'export'; + } + + /** + * Registers the necessary REST API routes. + */ + public function register_routes() { + register_rest_route( + $this->namespace, + '/' . $this->rest_base, + array( + array( + 'methods' => WP_REST_Server::READABLE, + 'callback' => array( $this, 'export' ), + 'permission_callback' => array( $this, 'permissions_check' ), + ), + ) + ); + } + + /** + * Checks whether a given request has permission to export. + * + * @return WP_Error|bool True if the request has access, or WP_Error object. + */ + public function permissions_check() { + if ( current_user_can( 'edit_theme_options' ) ) { + return true; + } + + return new WP_Error( + 'rest_cannot_view_url_details', + __( 'Sorry, you are not allowed to export templates and template parts.', 'gutenberg' ), + array( 'status' => rest_authorization_required_code() ) + ); + } + + /** + * Output a ZIP file with an export of the current templates + * and template parts from the site editor, and close the connection. + * + * @return WP_Error|void + */ + public function export() { + // Generate the export file. + $filename = wp_generate_edit_site_export_file(); + + if ( is_wp_error( $filename ) ) { + return $filename; + } + + header( 'Content-Type: application/zip' ); + header( 'Content-Disposition: attachment; filename=edit-site-export.zip' ); + header( 'Content-Length: ' . filesize( $filename ) ); + flush(); + readfile( $filename ); + unlink( $filename ); + exit; + } +} diff --git a/lib/compat/wordpress-5.9/edit-site-export.php b/lib/compat/wordpress-5.9/edit-site-export.php new file mode 100644 index 00000000000000..825766cffcab7d --- /dev/null +++ b/lib/compat/wordpress-5.9/edit-site-export.php @@ -0,0 +1,58 @@ +open( $filename, ZipArchive::CREATE ) ) { + return new WP_Error( __( 'Unable to open export file (archive) for writing.', 'gutenberg' ) ); + } + + $zip->addEmptyDir( 'theme' ); + $zip->addEmptyDir( 'theme/block-templates' ); + $zip->addEmptyDir( 'theme/block-template-parts' ); + + // Load templates into the zip file. + $templates = gutenberg_get_block_templates(); + foreach ( $templates as $template ) { + $template->content = _remove_theme_attribute_in_block_template_content( $template->content ); + + $zip->addFromString( + 'theme/block-templates/' . $template->slug . '.html', + $template->content + ); + } + + // Load template parts into the zip file. + $template_parts = gutenberg_get_block_templates( array(), 'wp_template_part' ); + foreach ( $template_parts as $template_part ) { + $zip->addFromString( + 'theme/block-template-parts/' . $template_part->slug . '.html', + $template_part->content + ); + } + + // Save changes to the zip file. + $zip->close(); + + return $filename; + } +} diff --git a/lib/full-site-editing/edit-site-export.php b/lib/full-site-editing/edit-site-export.php deleted file mode 100644 index 6a29c235434951..00000000000000 --- a/lib/full-site-editing/edit-site-export.php +++ /dev/null @@ -1,114 +0,0 @@ - $block ) { - if ( 'core/template-part' === $block['blockName'] && isset( $block['attrs']['theme'] ) ) { - unset( $blocks[ $key ]['attrs']['theme'] ); - $has_updated_content = true; - } - } - - if ( $has_updated_content ) { - foreach ( $template_blocks as $block ) { - $new_content .= serialize_block( $block ); - } - - return $new_content; - } - - return $template_content; -} - -/** - * Creates an export of the current templates and - * template parts from the site editor at the - * specified path in a ZIP file. - * - * @param string $filename path of the ZIP file. - */ -function gutenberg_edit_site_export_create_zip( $filename ) { - if ( ! class_exists( 'ZipArchive' ) ) { - return new WP_Error( 'Zip Export not supported.' ); - } - - $zip = new ZipArchive(); - $zip->open( $filename, ZipArchive::OVERWRITE ); - $zip->addEmptyDir( 'theme' ); - $zip->addEmptyDir( 'theme/block-templates' ); - $zip->addEmptyDir( 'theme/block-template-parts' ); - - // Load templates into the zip file. - $templates = gutenberg_get_block_templates(); - foreach ( $templates as $template ) { - $template->content = _remove_theme_attribute_from_content( $template->content ); - - $zip->addFromString( - 'theme/block-templates/' . $template->slug . '.html', - $template->content - ); - } - - // Load template parts into the zip file. - $template_parts = gutenberg_get_block_templates( array(), 'wp_template_part' ); - foreach ( $template_parts as $template_part ) { - $zip->addFromString( - 'theme/block-template-parts/' . $template_part->slug . '.html', - $template_part->content - ); - } - - // Save changes to the zip file. - $zip->close(); -} - -/** - * Output a ZIP file with an export of the current templates - * and template parts from the site editor, and close the connection. - */ -function gutenberg_edit_site_export() { - // Create ZIP file in the temporary directory. - $filename = tempnam( get_temp_dir(), 'edit-site-export' ); - gutenberg_edit_site_export_create_zip( $filename ); - - header( 'Content-Type: application/zip' ); - header( 'Content-Disposition: attachment; filename=edit-site-export.zip' ); - header( 'Content-Length: ' . filesize( $filename ) ); - flush(); - echo readfile( $filename ); - die(); -} - -add_action( - 'rest_api_init', - function () { - register_rest_route( - '__experimental/edit-site/v1', - '/export', - array( - 'methods' => 'GET', - 'callback' => 'gutenberg_edit_site_export', - 'permission_callback' => function () { - return current_user_can( 'edit_theme_options' ); - }, - ) - ); - } -); diff --git a/lib/load.php b/lib/load.php index cec6a89012da32..d2cb603bb3f4ce 100644 --- a/lib/load.php +++ b/lib/load.php @@ -71,6 +71,10 @@ function gutenberg_is_experiment_enabled( $name ) { require_once __DIR__ . '/class-wp-rest-url-details-controller.php'; } + if ( ! class_exists( 'WP_REST_Edit_Site_Export_Controller' ) ) { + require_once __DIR__ . '/compat/wordpress-5.9/class-wp-rest-edit-site-export-controller.php'; + } + require __DIR__ . '/rest-api.php'; } @@ -93,6 +97,7 @@ function gutenberg_is_experiment_enabled( $name ) { require __DIR__ . '/compat/wordpress-5.9/get-global-styles-and-settings.php'; require __DIR__ . '/compat/wordpress-5.9/json-file-decode.php'; require __DIR__ . '/compat/wordpress-5.9/translate-settings-using-i18n-schema.php'; +require __DIR__ . '/compat/wordpress-5.9/edit-site-export.php'; require __DIR__ . '/editor-settings.php'; if ( ! class_exists( 'WP_Block_Template' ) ) { @@ -114,7 +119,6 @@ function gutenberg_is_experiment_enabled( $name ) { require __DIR__ . '/full-site-editing/template-parts.php'; require __DIR__ . '/full-site-editing/template-loader.php'; require __DIR__ . '/full-site-editing/edit-site-page.php'; -require __DIR__ . '/full-site-editing/edit-site-export.php'; require __DIR__ . '/compat/wordpress-5.9/default-theme-supports.php'; require __DIR__ . '/compat/wordpress-5.9/class-gutenberg-rest-global-styles-controller.php'; require __DIR__ . '/compat/wordpress-5.9/rest-active-global-styles.php'; diff --git a/lib/rest-api.php b/lib/rest-api.php index 3e2e2b09fd1d02..49f82e2eb4b0e1 100644 --- a/lib/rest-api.php +++ b/lib/rest-api.php @@ -242,3 +242,14 @@ function gutenberg_register_global_styles_endpoints() { $editor_settings->register_routes(); } add_action( 'rest_api_init', 'gutenberg_register_global_styles_endpoints' ); + +/** + * Registers the Edit Site's Export REST API routes. + * + * @return void + */ +function gutenberg_register_edit_site_export_endpoint() { + $editor_settings = new WP_REST_Edit_Site_Export_Controller(); + $editor_settings->register_routes(); +} +add_action( 'rest_api_init', 'gutenberg_register_edit_site_export_endpoint' ); diff --git a/packages/edit-site/src/plugins/index.js b/packages/edit-site/src/plugins/index.js index 9c74d64010380c..2c0403a8d9470d 100644 --- a/packages/edit-site/src/plugins/index.js +++ b/packages/edit-site/src/plugins/index.js @@ -28,7 +28,7 @@ registerPlugin( 'edit-site', { icon={ download } onClick={ () => apiFetch( { - path: '/__experimental/edit-site/v1/export', + path: '/wp-block-editor/v1/export', parse: false, } ) .then( ( res ) => res.blob() ) diff --git a/phpunit/class-block-templates-test.php b/phpunit/class-block-templates-test.php index 7d0cfa3b3bc6c2..15c8f20b1f185c 100644 --- a/phpunit/class-block-templates-test.php +++ b/phpunit/class-block-templates-test.php @@ -191,6 +191,28 @@ function test_inject_theme_attribute_in_block_template_content() { $this->assertEquals( $content_with_no_template_part, $template_content ); } + function test_remove_theme_attribute_in_block_template_content() { + $content_with_existing_theme_attribute = ''; + $template_content = _remove_theme_attribute_in_block_template_content( $content_with_existing_theme_attribute ); + $expected = ''; + $this->assertEquals( $expected, $template_content ); + + $content_with_existing_theme_attribute_nested = ''; + $template_content = _remove_theme_attribute_in_block_template_content( $content_with_existing_theme_attribute_nested ); + $expected = ''; + $this->assertEquals( $expected, $template_content ); + + // Does not modify content when there is no existing theme attribute. + $content_without_theme_attribute = ''; + $template_content = _remove_theme_attribute_in_block_template_content( $content_without_theme_attribute ); + $this->assertEquals( $content_without_theme_attribute, $template_content ); + + // Does not remove theme when there is no template part. + $content_with_no_template_part = ''; + $template_content = _remove_theme_attribute_in_block_template_content( $content_with_no_template_part ); + $this->assertEquals( $content_with_no_template_part, $template_content ); + } + /** * Should retrieve the template from the theme files. */ diff --git a/phpunit/class-edit-site-export-test.php b/phpunit/class-edit-site-export-test.php index 8d83cb51e86341..ae56f11f283de9 100644 --- a/phpunit/class-edit-site-export-test.php +++ b/phpunit/class-edit-site-export-test.php @@ -1,36 +1,13 @@ '; - $template_content = _remove_theme_attribute_from_content( $content_with_existing_theme_attribute ); - $expected = ''; - $this->assertEquals( $expected, $template_content ); - - $content_with_existing_theme_attribute_nested = ''; - $template_content = _remove_theme_attribute_from_content( $content_with_existing_theme_attribute_nested ); - $expected = ''; - $this->assertEquals( $expected, $template_content ); - - // Does not modify content when there is no existing theme attribute. - $content_without_theme_attribute = ''; - $template_content = _remove_theme_attribute_from_content( $content_without_theme_attribute ); - $this->assertEquals( $content_without_theme_attribute, $template_content ); - - // Does not remove theme when there is no template part. - $content_with_no_template_part = ''; - $template_content = _remove_theme_attribute_from_content( $content_with_no_template_part ); - $this->assertEquals( $content_with_no_template_part, $template_content ); - } - - function test_gutenberg_edit_site_export() { - $filename = tempnam( get_temp_dir(), 'edit-site-export' ); - gutenberg_edit_site_export_create_zip( $filename ); + function test_wp_generate_edit_site_export_file() { + $filename = wp_generate_edit_site_export_file(); $this->assertTrue( file_exists( $filename ), 'zip file is created at the specified path' ); $this->assertTrue( filesize( $filename ) > 0, 'zip file is larger than 0 bytes' );