From 165110b322f9f0e32772e28dcd261f69e235c75d Mon Sep 17 00:00:00 2001 From: scruffian Date: Mon, 24 Apr 2023 13:12:17 +0200 Subject: [PATCH 1/6] Add Theme Previews make it work for child themes sanitize the theme_preview param use the url package move to middleware make a serverside middleware add comment remove comment change url add comments add the theme previews file rename param remove unsed params fix PHPCS Add live preview buttons to block themes partly update the editor to reflect that theme preview is on keep the preview flag on when navigating through pages activate theme from the preview add an experiment add default stylesheet hide behind and experiment Update lib/experiments-page.php Co-authored-by: Dave Smith Update lib/experiments-page.php Co-authored-by: Dave Smith Update lib/experiments-page.php Co-authored-by: Dave Smith linting and docs add a snackbar notice Translate script strings Render theme name instead of path Update packages/edit-site/src/components/save-hub/index.js Use @wordpress/url utils show button on all block themes even after scrolling Update site editor save button to activate theme Fix activate buttons to remove url param Replace history instead of pushing only activate themes when saving dirty changes move functions to utils move activate to a hook add a return url use getQueryArgs Refactor save hub activate and save button add warning about theme activation Co-authored-by: Dave Smith <444434+getdave@users.noreply.github.com> rename function fix rebase fix PHPCS fix the experiment name wrap changes in an experiment --- lib/compat/wordpress-6.3/theme-previews.php | 126 ++++++++++++++++++ lib/experimental/editor-settings.php | 4 + lib/experiments-page.php | 12 ++ lib/load.php | 1 + packages/api-fetch/src/index.js | 2 + .../src/middlewares/theme-preview.js | 35 +++++ .../edit-site/src/components/editor/index.js | 7 +- .../edit-site/src/components/routes/link.js | 12 ++ .../src/components/save-button/index.js | 13 +- .../src/components/save-hub/index.js | 3 +- .../src/components/save-panel/index.js | 16 ++- .../index.js | 10 ++ .../sidebar-navigation-screen/index.js | 29 +++- .../src/utils/is-previewing-theme.js | 18 +++ .../edit-site/src/utils/use-activate-theme.js | 38 ++++++ .../components/entities-saved-states/index.js | 72 +++++++--- 16 files changed, 373 insertions(+), 25 deletions(-) create mode 100644 lib/compat/wordpress-6.3/theme-previews.php create mode 100644 packages/api-fetch/src/middlewares/theme-preview.js create mode 100644 packages/edit-site/src/utils/is-previewing-theme.js create mode 100644 packages/edit-site/src/utils/use-activate-theme.js diff --git a/lib/compat/wordpress-6.3/theme-previews.php b/lib/compat/wordpress-6.3/theme-previews.php new file mode 100644 index 0000000000000..400c5e28c0500 --- /dev/null +++ b/lib/compat/wordpress-6.3/theme-previews.php @@ -0,0 +1,126 @@ +errors() ) ) { + return sanitize_text_field( $preview_stylesheet ); + } + + return $current_stylesheet; +} + +/** + * Filters the blog option to return the parent theme directory for the previewed theme. + * + * @param string $current_stylesheet The current theme directory. + * @return string The previewed theme directory. + */ +function gutenberg_theme_preview_template( $current_stylesheet = null ) { + $preview_stylesheet = ! empty( $_GET['theme_preview'] ) ? $_GET['theme_preview'] : null; + $wp_theme = wp_get_theme( $preview_stylesheet ); + if ( ! is_wp_error( $wp_theme->errors() ) ) { + return sanitize_text_field( $wp_theme->get_template() ); + } + + return $current_stylesheet; +} + +/** + * Adds a middleware to the REST API to set the theme for the preview. + */ +function gutenberg_attach_theme_preview_middleware() { + wp_add_inline_script( + 'wp-api-fetch', + sprintf( + 'wp.apiFetch.use( wp.apiFetch.createThemePreviewMiddleware( %s ) );', + wp_json_encode( sanitize_text_field( $_GET['theme_preview'] ) ) + ), + 'after' + ); +} + +/** + * Temporary function to add a live preview button to block themes. + * Remove when https://core.trac.wordpress.org/ticket/58190 lands. + */ +function add_live_preview_button() { + global $pagenow; + if ( 'themes.php' === $pagenow ) { + ?> + + + + __( 'Enable Block Theme Previews', 'gutenberg' ), + 'id' => 'gutenberg-theme-previews', + ) + ); + register_setting( 'gutenberg-experiments', 'gutenberg-experiments' diff --git a/lib/load.php b/lib/load.php index 8cceb21906293..a322e8af03a9d 100644 --- a/lib/load.php +++ b/lib/load.php @@ -48,6 +48,7 @@ function gutenberg_is_experiment_enabled( $name ) { require_once __DIR__ . '/compat/wordpress-6.3/class-gutenberg-rest-templates-controller-6-3.php'; require_once __DIR__ . '/compat/wordpress-6.3/class-gutenberg-rest-global-styles-controller-6-3.php'; require_once __DIR__ . '/compat/wordpress-6.3/rest-api.php'; + require_once __DIR__ . '/compat/wordpress-6.3/theme-previews.php'; // Experimental. if ( ! class_exists( 'WP_Rest_Customizer_Nonces' ) ) { diff --git a/packages/api-fetch/src/index.js b/packages/api-fetch/src/index.js index a0cda678f8d3e..408f2af0901f1 100644 --- a/packages/api-fetch/src/index.js +++ b/packages/api-fetch/src/index.js @@ -14,6 +14,7 @@ import namespaceEndpointMiddleware from './middlewares/namespace-endpoint'; import httpV1Middleware from './middlewares/http-v1'; import userLocaleMiddleware from './middlewares/user-locale'; import mediaUploadMiddleware from './middlewares/media-upload'; +import createThemePreviewMiddleware from './middlewares/theme-preview'; import { parseResponseAndNormalizeError, parseAndThrowError, @@ -193,5 +194,6 @@ apiFetch.createPreloadingMiddleware = createPreloadingMiddleware; apiFetch.createRootURLMiddleware = createRootURLMiddleware; apiFetch.fetchAllMiddleware = fetchAllMiddleware; apiFetch.mediaUploadMiddleware = mediaUploadMiddleware; +apiFetch.createThemePreviewMiddleware = createThemePreviewMiddleware; export default apiFetch; diff --git a/packages/api-fetch/src/middlewares/theme-preview.js b/packages/api-fetch/src/middlewares/theme-preview.js new file mode 100644 index 0000000000000..2f41293230eae --- /dev/null +++ b/packages/api-fetch/src/middlewares/theme-preview.js @@ -0,0 +1,35 @@ +/** + * WordPress dependencies + */ +import { addQueryArgs, hasQueryArg } from '@wordpress/url'; + +/** + * This appends a `theme_preview` parameter to the REST API request URL if + * the admin URL contains a `theme` GET parameter. + * + * @param {Record} themePath + * @return {import('../types').APIFetchMiddleware} Preloading middleware. + */ +const createThemePreviewMiddleware = ( themePath ) => ( options, next ) => { + if ( + typeof options.url === 'string' && + ! hasQueryArg( options.url, 'theme_preview' ) + ) { + options.url = addQueryArgs( options.url, { + theme_preview: themePath, + } ); + } + + if ( + typeof options.path === 'string' && + ! hasQueryArg( options.path, 'theme_preview' ) + ) { + options.path = addQueryArgs( options.path, { + theme_preview: themePath, + } ); + } + + return next( options ); +}; + +export default createThemePreviewMiddleware; diff --git a/packages/edit-site/src/components/editor/index.js b/packages/edit-site/src/components/editor/index.js index 434554bbda7e8..62b62082e4b92 100644 --- a/packages/edit-site/src/components/editor/index.js +++ b/packages/edit-site/src/components/editor/index.js @@ -171,7 +171,12 @@ export default function Editor() { } + notices={ + ( isEditMode || + window?.__experimentalEnableThemePreviews ) && ( + + ) + } content={ <> diff --git a/packages/edit-site/src/components/routes/link.js b/packages/edit-site/src/components/routes/link.js index a77f83944dd9d..71313176a1c60 100644 --- a/packages/edit-site/src/components/routes/link.js +++ b/packages/edit-site/src/components/routes/link.js @@ -8,6 +8,10 @@ import { privateApis as routerPrivateApis } from '@wordpress/router'; * Internal dependencies */ import { unlock } from '../../private-apis'; +import { + isPreviewingTheme, + currentlyPreviewingTheme, +} from '../../utils/is-previewing-theme'; const { useHistory } = unlock( routerPrivateApis ); @@ -29,6 +33,14 @@ export function useLink( params = {}, state, shouldReplace = false ) { window.location.href, ...Object.keys( currentArgs ) ); + + if ( isPreviewingTheme() ) { + params = { + ...params, + theme_preview: currentlyPreviewingTheme(), + }; + } + const newUrl = addQueryArgs( currentUrlWithoutArgs, params ); return { diff --git a/packages/edit-site/src/components/save-button/index.js b/packages/edit-site/src/components/save-button/index.js index 0e4bbf6296abc..4e4c6dfd35f65 100644 --- a/packages/edit-site/src/components/save-button/index.js +++ b/packages/edit-site/src/components/save-button/index.js @@ -11,6 +11,7 @@ import { displayShortcut } from '@wordpress/keycodes'; * Internal dependencies */ import { store as editSiteStore } from '../../store'; +import { isPreviewingTheme } from '../../utils/is-previewing-theme'; export default function SaveButton( { className = 'edit-site-save-button__button', @@ -33,9 +34,17 @@ export default function SaveButton( { }, [] ); const { setIsSaveViewOpened } = useDispatch( editSiteStore ); - const disabled = ! isDirty || isSaving; + const activateSaveEnabled = isPreviewingTheme() || isDirty; + const disabled = isSaving || ! activateSaveEnabled; - const label = __( 'Save' ); + let label; + if ( isPreviewingTheme() && isDirty ) { + label = __( 'Activate & Save' ); + } else if ( isPreviewingTheme() ) { + label = __( 'Activate' ); + } else { + label = __( 'Save' ); + } return (