Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Theme Export Class #57524

Open
wants to merge 3 commits into
base: trunk
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
<?php
/**
* REST API: Gutenberg_REST_Edit_Site_Export_Controller class
*
* @package Gutenberg
* @subpackage REST_API
*/

/**
* Core class used to access block patterns via the REST API.
*
* @since 6.4.0
*
* @see WP_REST_Controller
*/
class Gutenberg_REST_Edit_Site_Export_Controller_6_5 extends WP_REST_Edit_Site_Export_Controller {
/**
* Output a ZIP file with an export of the current templates
* and template parts from the site editor, and close the connection.
*
* @since 5.9.0
* @since 6.5.0 Use WP_Theme_Export class to generate theme zip file.
*
* @return WP_Error|void
*/
public function export() {
// Generate the export file.
$filename = WP_Theme_Export::generate_theme_export();

if ( is_wp_error( $filename ) ) {
$filename->add_data( array( 'status' => 500 ) );

return $filename;
}

$theme_name = basename( get_stylesheet() );
header( 'Content-Type: application/zip' );
header( 'Content-Disposition: attachment; filename=' . $theme_name . '.zip' );
header( 'Content-Length: ' . filesize( $filename ) );
flush();
readfile( $filename );
unlink( $filename );
exit;
}
}
10 changes: 10 additions & 0 deletions lib/compat/wordpress-6.5/rest-api.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,16 @@
die( 'Silence is golden.' );
}

/**
* Registers the Edit Site Export REST API routes.
*/
function gutenberg_register_edit_site_export_endpoints() {
$edit_site_export_controller = new Gutenberg_REST_Edit_Site_Export_Controller_6_5();
$edit_site_export_controller->register_routes();
}

add_action( 'rest_api_init', 'gutenberg_register_edit_site_export_endpoints' );

/**
* Registers the Global Styles Revisions REST API routes.
*/
Expand Down
123 changes: 123 additions & 0 deletions lib/experimental/theme-export/class-wp-theme-export.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
<?php
/**
* WP_Theme_Export class
*
* @package WordPress
* @since 6.5.0
*/

if ( class_exists( 'WP_Theme_Export' ) ) {
return;
}

/**
* Core class used to generate a theme export file.
*
* @since 6.5.0
*/
class WP_Theme_Export {
public function __construct() {
// empty.
}

/**
* Generate the theme export file.
*
* @since 6.5.0
*/
public function generate_theme_export() {
global $wp_version;

if ( ! class_exists( 'ZipArchive' ) ) {
return new WP_Error( 'missing_zip_package', __( 'Zip Export not supported.', 'gutenberg' ) );
}

$obscura = wp_generate_password( 12, false, false );
$theme_name = basename( get_stylesheet() );
$filename = get_temp_dir() . $theme_name . $obscura . '.zip';

$zip = new ZipArchive();
if ( true !== $zip->open( $filename, ZipArchive::CREATE | ZipArchive::OVERWRITE ) ) {
return new WP_Error( 'unable_to_create_zip', __( 'Unable to open export file (archive) for writing.', 'gutenberg' ) );
}

$zip->addEmptyDir( 'templates' );
$zip->addEmptyDir( 'parts' );

// Get path of the theme.
$theme_path = wp_normalize_path( get_stylesheet_directory() );

// Create recursive directory iterator.
$theme_files = new RecursiveIteratorIterator(
new RecursiveDirectoryIterator( $theme_path ),
RecursiveIteratorIterator::LEAVES_ONLY
);

// Make a copy of the current theme.
foreach ( $theme_files as $file ) {
// Skip directories as they are added automatically.
if ( ! $file->isDir() ) {
// Get real and relative path for current file.
$file_path = wp_normalize_path( $file );
$relative_path = substr( $file_path, strlen( $theme_path ) + 1 );

if ( ! wp_is_theme_directory_ignored( $relative_path ) ) {
$zip->addFile( $file_path, $relative_path );
}
}
}

// Load templates into the zip file.
$templates = get_block_templates();
foreach ( $templates as $template ) {
$template->content = traverse_and_serialize_blocks(
parse_blocks( $template->content ),
'_remove_theme_attribute_from_template_part_block'
);

$zip->addFromString(
'templates/' . $template->slug . '.html',
$template->content
);
}

// Load template parts into the zip file.
$template_parts = get_block_templates( array(), 'wp_template_part' );
foreach ( $template_parts as $template_part ) {
$zip->addFromString(
'parts/' . $template_part->slug . '.html',
$template_part->content
);
}

// Load theme.json into the zip file.
$tree = WP_Theme_JSON_Resolver::get_theme_data( array(), array( 'with_supports' => false ) );
// Merge with user data.
$tree->merge( WP_Theme_JSON_Resolver::get_user_data() );

$theme_json_raw = $tree->get_data();
// If a version is defined, add a schema.
if ( $theme_json_raw['version'] ) {
$theme_json_version = 'wp/' . substr( $wp_version, 0, 3 );
$schema = array( '$schema' => 'https://schemas.wp.org/' . $theme_json_version . '/theme.json' );
$theme_json_raw = array_merge( $schema, $theme_json_raw );
}

// Convert to a string.
$theme_json_encoded = wp_json_encode( $theme_json_raw, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE );

// Replace 4 spaces with a tab.
$theme_json_tabbed = preg_replace( '~(?:^|\G)\h{4}~m', "\t", $theme_json_encoded );

// Add the theme.json file to the zip.
$zip->addFromString(
'theme.json',
$theme_json_tabbed
);

// Save changes to the zip file.
$zip->close();

return $filename;
}
}
4 changes: 4 additions & 0 deletions lib/load.php
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ function gutenberg_is_experiment_enabled( $name ) {
require_once __DIR__ . '/compat/wordpress-6.4/theme-previews.php';

// WordPress 6.5 compat.
require_once __DIR__ . '/compat/wordpress-6.5/class-gutenberg-rest-edit-site-export-controller-6-5.php';
require_once __DIR__ . '/compat/wordpress-6.5/class-gutenberg-rest-global-styles-revisions-controller-6-5.php';
require_once __DIR__ . '/compat/wordpress-6.5/rest-api.php';

Expand Down Expand Up @@ -113,6 +114,9 @@ function gutenberg_is_experiment_enabled( $name ) {
require __DIR__ . '/experimental/l10n.php';
require __DIR__ . '/experimental/synchronization.php';

// Theme export class.
require __DIR__ . '/experimental/theme-export/class-wp-theme-export.php';

if ( gutenberg_is_experiment_enabled( 'gutenberg-no-tinymce' ) ) {
require __DIR__ . '/experimental/disable-tinymce.php';
}
Expand Down
Loading