diff --git a/admin/create-theme/theme-create.php b/admin/create-theme/theme-create.php index 2852e075..c3c79475 100644 --- a/admin/create-theme/theme-create.php +++ b/admin/create-theme/theme-create.php @@ -22,8 +22,13 @@ public static function clone_current_theme( $theme ) { wp_mkdir_p( $new_theme_path ); // Copy theme files. + $template_options = array( + 'localizeText' => false, + 'removeNavRefs' => false, + 'localizeImages' => false, + ); Theme_Utils::clone_theme_to_folder( $new_theme_path, $theme['slug'], $theme['name'] ); - Theme_Templates::add_templates_to_local( 'all', $new_theme_path, $theme['slug'] ); + Theme_Templates::add_templates_to_local( 'all', $new_theme_path, $theme['slug'], $template_options ); file_put_contents( $new_theme_path . DIRECTORY_SEPARATOR . 'theme.json', MY_Theme_JSON_Resolver::export_theme_data( 'all' ) ); if ( $theme['subfolder'] ) { diff --git a/admin/create-theme/theme-templates.php b/admin/create-theme/theme-templates.php index a2ab7727..437a12e2 100644 --- a/admin/create-theme/theme-templates.php +++ b/admin/create-theme/theme-templates.php @@ -153,11 +153,26 @@ public static function paternize_template( $template ) { * @param string $slug The slug of the theme. * @return object The prepared template. */ - public static function prepare_template_for_export( $template, $slug = null ) { + public static function prepare_template_for_export( $template, $slug = null, $options = null ) { + + if ( ! $options ) { + $options = array( + 'localizeText' => false, + 'removeNavRefs' => true, + 'localizeImages' => true, + ); + } + + $template = self::eliminate_environment_specific_content( $template, $options ); + + if ( array_key_exists( 'localizeText', $options ) && $options['localizeText'] ) { + $template = self::escape_text_in_template( $template ); + } + + if ( array_key_exists( 'localizeImages', $options ) && $options['localizeImages'] ) { + $template = Theme_Media::make_template_images_local( $template ); + } - $template = self::eliminate_environment_specific_content( $template ); - $template = self::escape_text_in_template( $template ); - $template = Theme_Media::make_template_images_local( $template ); $template = self::paternize_template( $template ); if ( $slug ) { @@ -176,7 +191,7 @@ public static function prepare_template_for_export( $template, $slug = null ) { * @param string $path The path to the theme folder. If null it is assumed to be the current theme. * @param string $slug The slug of the theme. If null it is assumed to be the current theme. */ - public static function add_templates_to_local( $export_type, $path = null, $slug = null ) { + public static function add_templates_to_local( $export_type, $path = null, $slug = null, $options = null ) { $theme_templates = self::get_theme_templates( $export_type ); $template_folders = get_block_theme_folders(); @@ -198,7 +213,7 @@ public static function add_templates_to_local( $export_type, $path = null, $slug foreach ( $theme_templates->templates as $template ) { - $template = self::prepare_template_for_export( $template, $slug ); + $template = self::prepare_template_for_export( $template, $slug, $options ); // Write the template content file_put_contents( @@ -226,7 +241,7 @@ public static function add_templates_to_local( $export_type, $path = null, $slug foreach ( $theme_templates->parts as $template ) { - $template = self::prepare_template_for_export( $template, $slug ); + $template = self::prepare_template_for_export( $template, $slug, $options ); // Write the template content file_put_contents( @@ -352,16 +367,18 @@ public static function escape_text( $text ) { return "get( 'TextDomain' ) . "');?>"; } - private static function eliminate_environment_specific_content_from_block( $block ) { + private static function eliminate_environment_specific_content_from_block( $block, $options = null ) { // remove theme attribute from template parts if ( 'core/template-part' === $block['blockName'] && isset( $block['attrs']['theme'] ) ) { unset( $block['attrs']['theme'] ); } - // remove ref attribute from blocks + // (optionally) remove ref attribute from nav blocks if ( 'core/navigation' === $block['blockName'] && isset( $block['attrs']['ref'] ) ) { - unset( $block['attrs']['ref'] ); + if ( ! $options || ( array_key_exists( 'removeNavRefs', $options ) && $options['removeNavRefs'] ) ) { + unset( $block['attrs']['ref'] ); + } } // remove id attributes and classes from image and cover blocks @@ -387,20 +404,20 @@ private static function eliminate_environment_specific_content_from_block( $bloc // process any inner blocks if ( ! empty( $block['innerBlocks'] ) ) { foreach ( $block['innerBlocks'] as $inner_block_key => $inner_block ) { - $block['innerBlocks'][ $inner_block_key ] = static::eliminate_environment_specific_content_from_block( $inner_block ); + $block['innerBlocks'][ $inner_block_key ] = static::eliminate_environment_specific_content_from_block( $inner_block, $options ); } } return $block; } - public static function eliminate_environment_specific_content( $template ) { + public static function eliminate_environment_specific_content( $template, $options = null ) { $template_blocks = parse_blocks( $template->content ); $parsed_content = ''; foreach ( $template_blocks as $block ) { - $parsed_block = static::eliminate_environment_specific_content_from_block( $block ); + $parsed_block = static::eliminate_environment_specific_content_from_block( $block, $options ); $parsed_content .= serialize_block( $parsed_block ); } diff --git a/includes/class-create-block-theme-api.php b/includes/class-create-block-theme-api.php index 464dc9c2..361281a5 100644 --- a/includes/class-create-block-theme-api.php +++ b/includes/class-create-block-theme-api.php @@ -420,17 +420,29 @@ function rest_update_theme( $request ) { */ function rest_save_theme( $request ) { - Theme_Fonts::persist_font_settings(); + $options = $request->get_params(); - if ( is_child_theme() ) { - Theme_Templates::add_templates_to_local( 'current' ); - Theme_Json::add_theme_json_to_local( 'current' ); - } else { - Theme_Templates::add_templates_to_local( 'all' ); - Theme_Json::add_theme_json_to_local( 'all' ); + if ( isset( $options['saveFonts'] ) && true === $options['saveFonts'] ) { + Theme_Fonts::persist_font_settings(); + } + + if ( isset( $options['saveTemplates'] ) && true === $options['saveTemplates'] ) { + if ( is_child_theme() ) { + Theme_Templates::add_templates_to_local( 'current', null, null, $options ); + } else { + Theme_Templates::add_templates_to_local( 'all', null, null, $options ); + } + Theme_Templates::clear_user_templates_customizations(); + } + + if ( isset( $options['saveStyle'] ) && true === $options['saveStyle'] ) { + if ( is_child_theme() ) { + Theme_Json::add_theme_json_to_local( 'current', null, null, $options ); + } else { + Theme_Json::add_theme_json_to_local( 'all', null, null, $options ); + } + Theme_Styles::clear_user_styles_customizations(); } - Theme_Styles::clear_user_styles_customizations(); - Theme_Templates::clear_user_templates_customizations(); return new WP_REST_Response( array( diff --git a/src/editor-sidebar/create-panel.js b/src/editor-sidebar/create-panel.js index 96dd2ef9..7a20470e 100644 --- a/src/editor-sidebar/create-panel.js +++ b/src/editor-sidebar/create-panel.js @@ -2,7 +2,6 @@ import { __ } from '@wordpress/i18n'; import { useState } from '@wordpress/element'; import { useDispatch, useSelect } from '@wordpress/data'; import apiFetch from '@wordpress/api-fetch'; -import { downloadFile } from '../utils'; import { store as noticesStore } from '@wordpress/notices'; import { // eslint-disable-next-line @@ -20,9 +19,9 @@ import { TextControl, TextareaControl, } from '@wordpress/components'; -import { chevronLeft, addCard, download, copy } from '@wordpress/icons'; +import { chevronLeft, addCard, copy } from '@wordpress/icons'; -export const CreateThemePanel = () => { +export const CreateThemePanel = ( { createType } ) => { const { createErrorNotice } = useDispatch( noticesStore ); const [ theme, setTheme ] = useState( { @@ -32,16 +31,13 @@ export const CreateThemePanel = () => { author: '', author_uri: '', tags_custom: '', + subfolder: '', } ); useSelect( ( select ) => { const themeData = select( 'core' ).getCurrentTheme(); setTheme( { - name: themeData.name.raw, - description: themeData.description.raw, - uri: themeData.theme_uri.raw, - author: themeData.author.raw, - author_uri: themeData.author_uri.raw, + ...theme, subfolder: themeData.stylesheet.lastIndexOf( '/' ) > 1 ? themeData.stylesheet.substring( @@ -52,64 +48,12 @@ export const CreateThemePanel = () => { } ); }, [] ); - const handleExportClick = () => { - const fetchOptions = { - path: '/create-block-theme/v1/export-clone', - method: 'POST', - data: theme, - headers: { - 'Content-Type': 'application/json', - }, - parse: false, - }; - - async function exportCloneTheme() { - try { - const response = await apiFetch( fetchOptions ); - downloadFile( response ); - } catch ( error ) { - const errorMessage = - error.message && error.code !== 'unknown_error' - ? error.message - : __( - 'An error occurred while attempting to export the theme.', - 'create-block-theme' - ); - createErrorNotice( errorMessage, { type: 'snackbar' } ); - } + const cloneTheme = () => { + if ( createType === 'createClone' ) { + handleCloneClick(); + } else if ( createType === 'createChild' ) { + handleCreateChildClick(); } - - exportCloneTheme(); - }; - - const handleExportChildClick = () => { - const fetchOptions = { - path: '/create-block-theme/v1/export-child-clone', - method: 'POST', - data: theme, - headers: { - 'Content-Type': 'application/json', - }, - parse: false, - }; - - async function exportCloneTheme() { - try { - const response = await apiFetch( fetchOptions ); - downloadFile( response ); - } catch ( error ) { - const errorMessage = - error.message && error.code !== 'unknown_error' - ? error.message - : __( - 'An error occurred while attempting to export the child theme.', - 'create-block-theme' - ); - createErrorNotice( errorMessage, { type: 'snackbar' } ); - } - } - - exportCloneTheme(); }; const handleCreateBlankClick = () => { @@ -202,36 +146,6 @@ export const CreateThemePanel = () => { } ); }; - const handleCreateVariationClick = () => { - apiFetch( { - path: '/create-block-theme/v1/create-variation', - method: 'POST', - data: theme, - headers: { - 'Content-Type': 'application/json', - }, - } ) - .then( () => { - // eslint-disable-next-line - alert( - __( - 'Theme variation created successfully. The editor will now reload.', - 'create-block-theme' - ) - ); - window.location.reload(); - } ) - .catch( ( error ) => { - const errorMessage = - error.message || - __( - 'An error occurred while attempting to create the theme variation.', - 'create-block-theme' - ); - createErrorNotice( errorMessage, { type: 'snackbar' } ); - } ); - }; - return ( @@ -241,13 +155,6 @@ export const CreateThemePanel = () => { - - { __( - 'Enter Metadata properties of the new theme.', - 'create-block-theme' - ) } - - { setTheme( { ...theme, name: value } ) } /> - - setTheme( { ...theme, description: value } ) - } - placeholder={ __( - 'A short description of the theme', - 'create-block-theme' - ) } - /> - - setTheme( { ...theme, uri: value } ) - } - placeholder={ __( - 'https://github.com/wordpress/twentytwentythree/', - 'create-block-theme' - ) } - /> - - setTheme( { ...theme, author: value } ) - } - placeholder={ __( - 'the WordPress team', - 'create-block-theme' - ) } - /> - - setTheme( { ...theme, author_uri: value } ) - } - placeholder={ __( - 'https://wordpress.org/', - 'create-block-theme' - ) } - /> - - setTheme( { ...theme, subfolder: value } ) - } - /> - - -
- - - - - { __( - 'Create a copy of this theme on the server and activate it. The user changes will be preserved in the new theme.', - 'create-block-theme' - ) } - -
- - - - - { __( - 'Create a child theme on the server and activate it. The user changes will be preserved in the new theme.', - 'create-block-theme' - ) } - - -
- - - - - { __( - 'Save the Global Styles changes as a theme variation.', - 'create-block-theme' - ) } - - -
- - - - - { __( - 'Export a copy of this theme as a .zip file. The user changes will be preserved in the new theme.', - 'create-block-theme' +
+ + { __( + 'Additional Theme MetaData', + 'create-block-theme' + ) } + + + + setTheme( { ...theme, description: value } ) + } + placeholder={ __( + 'A short description of the theme', + 'create-block-theme' + ) } + /> + + setTheme( { ...theme, uri: value } ) + } + placeholder={ __( + 'https://github.com/wordpress/twentytwentythree/', + 'create-block-theme' + ) } + /> + + setTheme( { ...theme, author: value } ) + } + placeholder={ __( + 'the WordPress team', + 'create-block-theme' + ) } + /> + + setTheme( { ...theme, author_uri: value } ) + } + placeholder={ __( + 'https://wordpress.org/', + 'create-block-theme' + ) } + /> +
+
+ { createType === 'createClone' && ( + <> + + ) } -
-
- - - - - { __( - 'Export a child of this theme as a .zip file. The user changes will be preserved in the new theme.', - 'create-block-theme' + { createType === 'createChild' && ( + <> + + ) } - -
- - - - - { __( - 'Create a blank theme with no styles or templates.', - 'create-block-theme' + { createType === 'createBlank' && ( + <> + + + + { __( + 'Create a blank theme with no styles or templates.', + 'create-block-theme' + ) } + + ) } - - +
); }; diff --git a/src/editor-sidebar/create-variation-panel.js b/src/editor-sidebar/create-variation-panel.js new file mode 100644 index 00000000..361c20de --- /dev/null +++ b/src/editor-sidebar/create-variation-panel.js @@ -0,0 +1,85 @@ +import { __ } from '@wordpress/i18n'; +import { useState } from '@wordpress/element'; +import { useDispatch } from '@wordpress/data'; +import { store as noticesStore } from '@wordpress/notices'; +import { + // eslint-disable-next-line + __experimentalVStack as VStack, + // eslint-disable-next-line + __experimentalText as Text, + // eslint-disable-next-line + __experimentalHeading as Heading, + // eslint-disable-next-line + __experimentalNavigatorToParentButton as NavigatorToParentButton, + PanelBody, + Button, + TextControl, +} from '@wordpress/components'; +import { chevronLeft, copy } from '@wordpress/icons'; +import { postCreateThemeVariation } from '../resolvers'; + +export const CreateVariationPanel = () => { + const { createErrorNotice } = useDispatch( noticesStore ); + + const [ theme, setTheme ] = useState( { + name: '', + } ); + + const handleCreateVariationClick = () => { + postCreateThemeVariation( theme.name ) + .then( () => { + // eslint-disable-next-line + alert( + __( + 'Theme variation created successfully. The editor will now reload.', + 'create-block-theme' + ) + ); + window.location.reload(); + } ) + .catch( ( error ) => { + const errorMessage = + error.message || + __( + 'An error occurred while attempting to create the theme variation.', + 'create-block-theme' + ); + createErrorNotice( errorMessage, { type: 'snackbar' } ); + } ); + }; + + return ( + + + + { __( 'Create Variation', 'create-block-theme' ) } + + + + + + { __( + 'Save the Global Styles changes as a theme variation.', + 'create-block-theme' + ) } + +
+ + setTheme( { ...theme, name: value } ) + } + /> +
+ +
+
+ ); +}; diff --git a/src/editor-sidebar/metadata-editor-modal.js b/src/editor-sidebar/metadata-editor-modal.js new file mode 100644 index 00000000..e9c4d765 --- /dev/null +++ b/src/editor-sidebar/metadata-editor-modal.js @@ -0,0 +1,198 @@ +import { __ } from '@wordpress/i18n'; +import { useState } from '@wordpress/element'; +import { useSelect } from '@wordpress/data'; +import { + // eslint-disable-next-line + __experimentalVStack as VStack, + // eslint-disable-next-line + __experimentalSpacer as Spacer, + // eslint-disable-next-line + __experimentalText as Text, + Modal, + Button, + TextControl, + TextareaControl, + ExternalLink, +} from '@wordpress/components'; +import { postUpdateThemeMetadata } from '../resolvers'; + +export const ThemeMetadataEditorModal = ( { onRequestClose } ) => { + const [ theme, setTheme ] = useState( { + name: '', + description: '', + uri: '', + version: '', + author: '', + author_uri: '', + tags_custom: '', + recommended_plugins: '', + } ); + + useSelect( async ( select ) => { + const themeData = select( 'core' ).getCurrentTheme(); + setTheme( { + name: themeData.name.raw, + description: themeData.description.raw, + uri: themeData.theme_uri.raw, + version: themeData.version, + author: themeData.author.raw, + author_uri: themeData.author_uri.raw, + tags_custom: themeData.tags.rendered, + subfolder: + themeData.stylesheet.lastIndexOf( '/' ) > 1 + ? themeData.stylesheet.substring( + 0, + themeData.stylesheet.lastIndexOf( '/' ) + ) + : '', + } ); + }, [] ); + + const handleUpdateClick = () => { + postUpdateThemeMetadata( theme ) + .then( () => { + // eslint-disable-next-line + alert( + __( + 'Theme updated successfully. The editor will now reload.', + 'create-block-theme' + ) + ); + window.location.reload(); + } ) + .catch( ( error ) => { + const errorMessage = + error.message || + __( + 'An error occurred while attempting to update the theme.', + 'create-block-theme' + ); + createErrorNotice( errorMessage, { type: 'snackbar' } ); + } ); + }; + return ( + + + + { __( + 'Edit Metadata properties of the current theme.', + 'create-block-theme' + ) } + + + + + setTheme( { ...theme, description: value } ) + } + placeholder={ __( + 'A short description of the theme', + 'create-block-theme' + ) } + /> + + setTheme( { ...theme, uri: value } ) + } + placeholder={ __( + 'https://github.com/wordpress/twentytwentythree/', + 'create-block-theme' + ) } + /> + + setTheme( { ...theme, author: value } ) + } + placeholder={ __( + 'the WordPress team', + 'create-block-theme' + ) } + /> + + setTheme( { ...theme, author_uri: value } ) + } + placeholder={ __( + 'https://wordpress.org/', + 'create-block-theme' + ) } + /> + + setTheme( { ...theme, version: value } ) + } + placeholder={ __( + 'Version of the theme', + 'create-block-theme' + ) } + /> + + setTheme( { ...theme, tags_custom: value } ) + } + placeholder={ __( + 'A comma-separated collection of tags', + 'create-block-theme' + ) } + /> + + { __( + 'List the recommended plugins for this theme. e.g. contact forms, social media. Plugins must be from the WordPress.org plugin repository.', + 'create-block-theme' + ) } +
+ + { __( 'Read more.', 'create-block-theme' ) } + + + } + // eslint-disable-next-line @wordpress/i18n-no-collapsible-whitespace + placeholder={ __( + `Plugin Name +https://wordpress.org/plugins/plugin-name/ +Plugin Description`, + 'create-block-theme' + ) } + value={ theme.recommended_plugins } + onChange={ ( value ) => + setTheme( { ...theme, recommended_plugins: value } ) + } + /> + + setTheme( { ...theme, subfolder: value } ) + } + /> +
+ + +
+ ); +}; diff --git a/src/editor-sidebar/save-panel.js b/src/editor-sidebar/save-panel.js new file mode 100644 index 00000000..804aaffd --- /dev/null +++ b/src/editor-sidebar/save-panel.js @@ -0,0 +1,151 @@ +import { __ } from '@wordpress/i18n'; +import { useState } from '@wordpress/element'; +import apiFetch from '@wordpress/api-fetch'; +import { + // eslint-disable-next-line + __experimentalVStack as VStack, + // eslint-disable-next-line + __experimentalHeading as Heading, + // eslint-disable-next-line + __experimentalNavigatorToParentButton as NavigatorToParentButton, + PanelBody, + Button, + CheckboxControl, +} from '@wordpress/components'; +import { chevronLeft, archive } from '@wordpress/icons'; + +export const SaveThemePanel = () => { + const [ saveOptions, setSaveOptions ] = useState( { + saveStyle: true, + saveTemplates: true, + saveFonts: true, + removeNavRefs: false, + localizeText: false, + localizeImages: false, + } ); + + const handleSaveClick = () => { + apiFetch( { + path: '/create-block-theme/v1/save', + method: 'POST', + data: saveOptions, + headers: { + 'Content-Type': 'application/json', + }, + } ) + .then( () => { + // eslint-disable-next-line + alert( + __( + 'Theme saved successfully. The editor will now reload.', + 'create-block-theme' + ) + ); + window.location.reload(); + } ) + .catch( ( error ) => { + const errorMessage = + error.message || + __( + 'An error occurred while attempting to save the theme.', + 'create-block-theme' + ); + createErrorNotice( errorMessage, { type: 'snackbar' } ); + } ); + }; + + return ( + + + + { __( 'Save Changes', 'create-block-theme' ) } + + + + + { + setSaveOptions( { + ...saveOptions, + saveFonts: ! saveOptions.saveFonts, + } ); + } } + /> + { + setSaveOptions( { + ...saveOptions, + saveStyle: ! saveOptions.saveStyle, + } ); + } } + /> + { + setSaveOptions( { + ...saveOptions, + saveTemplates: ! saveOptions.saveTemplates, + } ); + } } + /> + { + setSaveOptions( { + ...saveOptions, + localizeText: ! saveOptions.localizeText, + } ); + } } + /> + { + setSaveOptions( { + ...saveOptions, + localizeImages: ! saveOptions.localizeImages, + } ); + } } + /> + { + setSaveOptions( { + ...saveOptions, + removeNavRefs: ! saveOptions.removeNavRefs, + } ); + } } + /> + + + + ); +}; diff --git a/src/plugin-sidebar.js b/src/plugin-sidebar.js index 20397f15..234d7c67 100644 --- a/src/plugin-sidebar.js +++ b/src/plugin-sidebar.js @@ -2,8 +2,6 @@ import { useState } from '@wordpress/element'; import { registerPlugin } from '@wordpress/plugins'; import { PluginSidebar, PluginSidebarMoreMenuItem } from '@wordpress/edit-site'; import { __, _x } from '@wordpress/i18n'; -import apiFetch from '@wordpress/api-fetch'; -import { downloadFile } from './utils'; import { useDispatch } from '@wordpress/data'; import { store as noticesStore } from '@wordpress/notices'; import { @@ -18,88 +16,57 @@ import { // eslint-disable-next-line __experimentalNavigatorButton as NavigatorButton, // eslint-disable-next-line + __experimentalNavigatorToParentButton as NavigatorToParentButton, + // eslint-disable-next-line __experimentalHStack as HStack, // eslint-disable-next-line __experimentalText as Text, + // eslint-disable-next-line + __experimentalHeading as Heading, Button, Icon, FlexItem, PanelBody, } from '@wordpress/components'; - -import { UpdateThemePanel } from './editor-sidebar/update-panel'; -import { CreateThemePanel } from './editor-sidebar/create-panel'; -import ThemeJsonEditorModal from './editor-sidebar/json-editor-modal'; - import { tool, copy, download, edit, + code, chevronRight, - archive, + chevronLeft, + addCard, + blockMeta, } from '@wordpress/icons'; +import { CreateThemePanel } from './editor-sidebar/create-panel'; +import ThemeJsonEditorModal from './editor-sidebar/json-editor-modal'; +import { SaveThemePanel } from './editor-sidebar/save-panel'; +import { CreateVariationPanel } from './editor-sidebar/create-variation-panel'; +import { ThemeMetadataEditorModal } from './editor-sidebar/metadata-editor-modal'; +import { downloadExportedTheme } from './resolvers'; + const CreateBlockThemePlugin = () => { const [ isEditorOpen, setIsEditorOpen ] = useState( false ); - const { createErrorNotice } = useDispatch( noticesStore ); - const handleSaveClick = () => { - apiFetch( { - path: '/create-block-theme/v1/save', - method: 'POST', - headers: { - 'Content-Type': 'application/json', - }, - } ) - .then( () => { - // eslint-disable-next-line - alert( - __( - 'Theme saved successfully. The editor will now reload.', - 'create-block-theme' - ) - ); - window.location.reload(); - } ) - .catch( ( error ) => { - const errorMessage = - error.message || - __( - 'An error occurred while attempting to save the theme.', - 'create-block-theme' - ); - createErrorNotice( errorMessage, { type: 'snackbar' } ); - } ); - }; + const [ isMetadataEditorOpen, setIsMetadataEditorOpen ] = useState( false ); - const handleExportClick = () => { - const fetchOptions = { - path: '/create-block-theme/v1/export', - method: 'POST', - headers: { - 'Content-Type': 'application/json', - }, - parse: false, - }; + const [ cloneCreateType, setCloneCreateType ] = useState( '' ); - async function exportTheme() { - try { - const response = await apiFetch( fetchOptions ); - downloadFile( response ); - } catch ( error ) { - const errorMessage = - error.message && error.code !== 'unknown_error' - ? error.message - : __( - 'An error occurred while attempting to export the theme.', - 'create-block-theme' - ); - createErrorNotice( errorMessage, { type: 'snackbar' } ); - } - } + const { createErrorNotice } = useDispatch( noticesStore ); - exportTheme(); + const handleExportClick = () => { + downloadExportedTheme().catch( ( error ) => { + const errorMessage = + error.message && error.code !== 'unknown_error' + ? error.message + : __( + 'An error occurred while attempting to export the theme.', + 'create-block-theme' + ); + createErrorNotice( errorMessage, { type: 'snackbar' } ); + } ); }; return ( @@ -126,61 +93,122 @@ const CreateBlockThemePlugin = () => { - + + + + + + { __( + 'Save Changes to Theme', + 'create-block-theme' + ) } + + + + + + + + + { __( + 'Create Theme Variation', + 'create-block-theme' + ) } + + + + - + - - { __( - 'Export your theme as a zip file. Note: You may want to save your user changes to the theme first.', - 'create-block-theme' - ) } -
- + - + { __( - 'Theme Info', + 'Create Blank Theme', 'create-block-theme' ) } - + + + + + { __( + 'Create Theme', + 'create-block-theme' + ) } + + + + +
+
+
+ + + + + + { __( + 'Create Block Theme', + 'create-block-theme' + ) } + + + + { __( - 'Edit Metadata properties of your current theme.', + 'Would you like to clone this Theme or create a Child Theme?', 'create-block-theme' ) }
- + { + setCloneCreateType( 'createClone' ); + } } + > { __( - 'Create Theme', + 'Clone Theme', 'create-block-theme' ) } @@ -189,23 +217,32 @@ const CreateBlockThemePlugin = () => { { __( - 'Create a new theme based on your current theme and either save it or export it.', + 'Create a clone of this theme with a new name. The user changes will be preserved in the new theme.', 'create-block-theme' ) }
- + + + + { __( + 'Create Child Theme', + 'create-block-theme' + ) } + + + +
{ __( - 'Open the theme.json file to inspect theme data.', + 'Create a child theme that uses this theme as a parent. This theme will remain unchanged and the user changes will be preserved in the new child theme.', 'create-block-theme' ) } @@ -213,20 +250,35 @@ const CreateBlockThemePlugin = () => {
- - + + + + + + - - + + + + + +
+ { isEditorOpen && ( setIsEditorOpen( false ) } /> ) } + + { isMetadataEditorOpen && ( + setIsMetadataEditorOpen( false ) } + /> + ) } ); }; diff --git a/src/resolvers.js b/src/resolvers.js index 41308384..446669a6 100644 --- a/src/resolvers.js +++ b/src/resolvers.js @@ -1,4 +1,5 @@ import apiFetch from '@wordpress/api-fetch'; +import { downloadFile } from './utils'; export async function fetchThemeJson() { const fetchOptions = { @@ -25,3 +26,38 @@ export async function fetchThemeJson() { // @todo: handle error } } + +export async function postCreateThemeVariation( name ) { + return apiFetch( { + path: '/create-block-theme/v1/create-variation', + method: 'POST', + data: { name }, + headers: { + 'Content-Type': 'application/json', + }, + } ); +} + +export async function postUpdateThemeMetadata( theme ) { + return apiFetch( { + path: '/create-block-theme/v1/update', + method: 'POST', + data: theme, + headers: { + 'Content-Type': 'application/json', + }, + } ); +} + +export async function downloadExportedTheme() { + return apiFetch( { + path: '/create-block-theme/v1/export', + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + parse: false, + } ).then( ( response ) => { + downloadFile( response ); + } ); +} diff --git a/tests/test-theme-create-theme.php b/tests/test-theme-create-theme.php deleted file mode 100644 index 5eb1a474..00000000 --- a/tests/test-theme-create-theme.php +++ /dev/null @@ -1,55 +0,0 @@ -class_instance = new Create_Block_Theme_Admin(); - } - - /** - * Test if the class exists. - */ - public function test_theme_instance() { - $class_name = get_class( $this->class_instance->theme ); - $expected = 'WP_Theme'; - - $this->assertEquals( $expected, $class_name ); - } - - /** - * Test if the export_child_theme correctly creates a child theme. - */ - public function test_export_child_theme() { - $create_block_theme_admin = $this->getMockBuilder( 'Create_Block_Theme_Admin' ) - ->setMethods( array( 'download_file' ) ) - ->getMock(); - - // Stub download_file method to avoid sending headers. - $create_block_theme_admin->method( 'download_file' ) - ->willReturn( true ); - - $theme_name = 'twentytwentythree'; - $expected_child_theme_name = 'twentytwentythree-child'; - - switch_theme( $theme_name ); - $current_theme = wp_get_theme(); - - $filename = $create_block_theme_admin->export_child_theme( $current_theme ); - - // check that the zip file exists in the temp dir. - $this->assertFileExists( $filename ); - - // check that it contains a valid WordPress theme. - $zip = new ZipArchive(); - $zip->open( $filename ); - $zip->extractTo( get_theme_root() . '/' . $expected_child_theme_name ); - $zip->close(); - - $child_theme = wp_get_theme( $expected_child_theme_name ); - $this->assertTrue( $child_theme->exists() ); - } -} diff --git a/tests/test-theme-templates.php b/tests/test-theme-templates.php index 2e924071..f2bf99ec 100644 --- a/tests/test-theme-templates.php +++ b/tests/test-theme-templates.php @@ -4,13 +4,26 @@ */ class Test_Create_Block_Theme_Templates extends WP_UnitTestCase { + /** + * Ensure that the string in a template is replaced with the appropraite PHP code + */ public function test_paragraphs_are_localized() { $template = new stdClass(); $template->content = '

This is text to localize

'; $new_template = Theme_Templates::escape_text_in_template( $template ); - $this->assertStringContainsString( 'This is text to localize', $new_template->content ); + $this->assertStringContainsString( "

", $new_template->content ); $this->assertStringNotContainsString( '

This is text to localize

', $new_template->content ); + } + /** + * Ensure that escape_text_in_template is not called when the localizeText flag is set to false + */ + public function test_paragraphs_are_not_localized() { + $template = new stdClass(); + $template->slug = 'test-template'; + $template->content = '

This is text to not localize

'; + $new_template = Theme_Templates::prepare_template_for_export( $template, null, array( 'localizeText' => false ) ); + $this->assertStringContainsString( '

This is text to not localize

', $new_template->content ); } public function test_paragraphs_in_groups_are_localized() { @@ -76,6 +89,14 @@ public function test_eliminate_nav_block_ref_in_nested_block() { $this->assertStringContainsString( '', $new_template->content ); } + public function test_not_eliminate_nav_block_ref() { + $template = new stdClass(); + $template->slug = 'test-template'; + $template->content = ''; + $new_template = Theme_Templates::prepare_template_for_export( $template, null, array( 'removeNavRefs' => false ) ); + $this->assertStringContainsString( '', $new_template->content ); + } + public function test_eliminate_id_from_image() { $template = new stdClass(); $template->content = '