Skip to content

Commit

Permalink
Site Editor: Stabilize export endpoint (#36559)
Browse files Browse the repository at this point in the history
* Site Editor: Stabilize export endpoint

* Move '_remove_theme_attribute_from_content' in block template utils

Restore 'gutenberg_generate_edit_site_export_file'

* Delete temp file after export

* Use ZipArchive::open to create export file

* Move gutenberg_generate_edit_site_export_file into 5.9 compat dir
  • Loading branch information
Mamaduka authored Nov 18, 2021
1 parent b5f267f commit 8973e2c
Show file tree
Hide file tree
Showing 9 changed files with 217 additions and 142 deletions.
34 changes: 34 additions & 0 deletions lib/compat/wordpress-5.9/block-template-utils.php
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
<?php
/**
* REST API: WP_REST_Edit_Site_Export_Controller class
*
* @package WordPress
* @subpackage REST_API
*/

/**
* Controller which provides REST endpoint for exporting current templates
* and template parts.
*
* @since 5.9.0
*
* @see WP_REST_Controller
*/
class WP_REST_Edit_Site_Export_Controller extends WP_REST_Controller {

/**
* Constructs the controller.
*/
public function __construct() {
$this->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;
}
}
58 changes: 58 additions & 0 deletions lib/compat/wordpress-5.9/edit-site-export.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
<?php
/**
* Edit Site utility methods.
*
* @package gutenberg
*/

if ( ! function_exists( 'wp_generate_edit_site_export_file' ) ) {
/**
* Creates an export of the current templates and
* template parts from the site editor at the
* specified path in a ZIP file.
*
* @return WP_Error|string Path of the ZIP file or error on failure.
*/
function wp_generate_edit_site_export_file() {
if ( ! class_exists( 'ZipArchive' ) ) {
return new WP_Error( __( 'Zip Export not supported.', 'gutenberg' ) );
}

$obscura = wp_generate_password( 12, false, false );
$filename = get_temp_dir() . 'edit-site-export-' . $obscura . '.zip';

$zip = new ZipArchive();
if ( true !== $zip->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;
}
}
114 changes: 0 additions & 114 deletions lib/full-site-editing/edit-site-export.php

This file was deleted.

6 changes: 5 additions & 1 deletion lib/load.php
Original file line number Diff line number Diff line change
Expand Up @@ -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';
}

Expand All @@ -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' ) ) {
Expand All @@ -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';
Expand Down
11 changes: 11 additions & 0 deletions lib/rest-api.php
Original file line number Diff line number Diff line change
Expand Up @@ -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' );
2 changes: 1 addition & 1 deletion packages/edit-site/src/plugins/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -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() )
Expand Down
22 changes: 22 additions & 0 deletions phpunit/class-block-templates-test.php
Original file line number Diff line number Diff line change
Expand Up @@ -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 = '<!-- wp:template-part {"slug":"header","theme":"tt1-blocks","align":"full","tagName":"header","className":"site-header"} /-->';
$template_content = _remove_theme_attribute_in_block_template_content( $content_with_existing_theme_attribute );
$expected = '<!-- wp:template-part {"slug":"header","align":"full","tagName":"header","className":"site-header"} /-->';
$this->assertEquals( $expected, $template_content );

$content_with_existing_theme_attribute_nested = '<!-- wp:group --><!-- wp:template-part {"slug":"header","theme":"tt1-blocks","align":"full","tagName":"header","className":"site-header"} /--><!-- /wp:group -->';
$template_content = _remove_theme_attribute_in_block_template_content( $content_with_existing_theme_attribute_nested );
$expected = '<!-- wp:group --><!-- wp:template-part {"slug":"header","align":"full","tagName":"header","className":"site-header"} /--><!-- /wp:group -->';
$this->assertEquals( $expected, $template_content );

// Does not modify content when there is no existing theme attribute.
$content_without_theme_attribute = '<!-- wp:template-part {"slug":"header","align":"full","tagName":"header","className":"site-header"} /-->';
$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 = '<!-- wp:post-content /-->';
$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.
*/
Expand Down
Loading

0 comments on commit 8973e2c

Please sign in to comment.