From 153dd26c1feba5c332e4a794b02d8202c08c0d2f Mon Sep 17 00:00:00 2001 From: Weston Ruter Date: Mon, 9 Oct 2017 16:03:35 +0000 Subject: [PATCH] Customize: Eliminate use of customize-loader in core so Customizer is opened consistently in `top` window. * Open the door for future browser history feature in #28536, which is currently not feasible when customize-loader is used. * Remove customizer-loader from being used on admin screens for Dashboard, Themes, non-shiny theme install/update. * Keep the customize-loader functionality available for plugins, for the time being. It may become deprecated. * Ensure `return` param in customizer links in Themes screen update to reflect `search` updated by `pushState`. * Persist `return` when reloading Customizer due to theme switch, autosave restoration, or changeset trashing. * Use `location.replace()` instead of changing `location.href` when trashing. * Hide theme browser while Themes screen is loading when there is a `search` to prevent flash of unfiltered themes. * Use throttling instead of debouncing when searching themes to ensure that screen is updated immediately on page load. * Fix encoding and decoding of `search` param between URL and search field. * Add support for dismissing autosaves when closing customize-loader, when it is used by plugins. * Skip sending changeset UUID to customize-loader for population in browser location if changeset branching is not enabled. See #28536. Fixes #40254. git-svn-id: https://develop.svn.wordpress.org/trunk@41797 602fd350-edb4-49c9-b593-d223f7449a82 --- src/wp-admin/css/themes.css | 4 ++ .../includes/class-theme-installer-skin.php | 9 ++- .../includes/class-theme-upgrader-skin.php | 11 +++- src/wp-admin/index.php | 2 - src/wp-admin/js/customize-controls.js | 58 +++++++++++------- src/wp-admin/js/theme.js | 61 +++++++++++++------ src/wp-admin/themes.php | 11 +++- src/wp-admin/update.php | 10 +-- .../class-wp-customize-manager.php | 1 + src/wp-includes/js/customize-loader.js | 33 +++++----- src/wp-includes/script-loader.php | 2 +- 11 files changed, 130 insertions(+), 72 deletions(-) diff --git a/src/wp-admin/css/themes.css b/src/wp-admin/css/themes.css index 782d0cd066..e3394483fe 100644 --- a/src/wp-admin/css/themes.css +++ b/src/wp-admin/css/themes.css @@ -7,6 +7,10 @@ 16.1 - Manage Themes ------------------------------------------------------------------------------*/ +body.js .theme-browser.search-loading { + display: none; +} + .theme-browser .themes { clear: both; } diff --git a/src/wp-admin/includes/class-theme-installer-skin.php b/src/wp-admin/includes/class-theme-installer-skin.php index b9bfbea017..3d39492226 100644 --- a/src/wp-admin/includes/class-theme-installer-skin.php +++ b/src/wp-admin/includes/class-theme-installer-skin.php @@ -64,7 +64,14 @@ class Theme_Installer_Skin extends WP_Upgrader_Skin { $install_actions = array(); if ( current_user_can( 'edit_theme_options' ) && current_user_can( 'customize' ) ) { - $install_actions['preview'] = '' . sprintf( __( 'Live Preview “%s”' ), $name ) . ''; + $customize_url = add_query_arg( + array( + 'theme' => urlencode( $stylesheet ), + 'return' => urlencode( admin_url( 'web' === $this->type ? 'theme-install.php' : 'themes.php' ) ), + ), + admin_url( 'customize.php' ) + ); + $install_actions['preview'] = '' . sprintf( __( 'Live Preview “%s”' ), $name ) . ''; } $install_actions['activate'] = '' . sprintf( __( 'Activate “%s”' ), $name ) . ''; diff --git a/src/wp-admin/includes/class-theme-upgrader-skin.php b/src/wp-admin/includes/class-theme-upgrader-skin.php index a2dff9250f..f92375745f 100644 --- a/src/wp-admin/includes/class-theme-upgrader-skin.php +++ b/src/wp-admin/includes/class-theme-upgrader-skin.php @@ -49,13 +49,20 @@ class Theme_Upgrader_Skin extends WP_Upgrader_Skin { ), admin_url('themes.php') ); $activate_link = wp_nonce_url( $activate_link, 'switch-theme_' . $stylesheet ); + $customize_url = add_query_arg( + array( + 'theme' => urlencode( $stylesheet ), + 'return' => urlencode( admin_url( 'themes.php' ) ), + ), + admin_url( 'customize.php' ) + ); if ( get_stylesheet() == $stylesheet ) { if ( current_user_can( 'edit_theme_options' ) && current_user_can( 'customize' ) ) { - $update_actions['preview'] = '' . sprintf( __( 'Customize “%s”' ), $name ) . ''; + $update_actions['preview'] = '' . sprintf( __( 'Customize “%s”' ), $name ) . ''; } } elseif ( current_user_can( 'switch_themes' ) ) { if ( current_user_can( 'edit_theme_options' ) && current_user_can( 'customize' ) ) { - $update_actions['preview'] = '' . sprintf( __( 'Live Preview “%s”' ), $name ) . ''; + $update_actions['preview'] = '' . sprintf( __( 'Live Preview “%s”' ), $name ) . ''; } $update_actions['activate'] = '' . sprintf( __( 'Activate “%s”' ), $name ) . ''; } diff --git a/src/wp-admin/index.php b/src/wp-admin/index.php index 5c73f19008..783a43b23f 100644 --- a/src/wp-admin/index.php +++ b/src/wp-admin/index.php @@ -16,8 +16,6 @@ wp_dashboard_setup(); wp_enqueue_script( 'dashboard' ); -if ( current_user_can( 'edit_theme_options' ) ) - wp_enqueue_script( 'customize-loader' ); if ( current_user_can( 'install_plugins' ) ) { wp_enqueue_script( 'plugin-install' ); wp_enqueue_script( 'updates' ); diff --git a/src/wp-admin/js/customize-controls.js b/src/wp-admin/js/customize-controls.js index 3c4bf05569..212c02d638 100644 --- a/src/wp-admin/js/customize-controls.js +++ b/src/wp-admin/js/customize-controls.js @@ -106,7 +106,7 @@ * * @since 4.9.0 * - * @param {string|wp.customize.Notification} - Notification object to add. Alternatively code may be supplied, and in that case the second notificationObject argument must be supplied. + * @param {string|wp.customize.Notification} notification - Notification object to add. Alternatively code may be supplied, and in that case the second notificationObject argument must be supplied. * @param {wp.customize.Notification} [notificationObject] - Notification to add when first argument is the code string. * @returns {wp.customize.Notification} Added notification (or existing instance if it was already added). */ @@ -3014,7 +3014,8 @@ api.utils.parseQueryString( urlParser.search.substr( 1 ) ), { theme: themeId, - changeset_uuid: api.settings.changeset.uuid + changeset_uuid: api.settings.changeset.uuid, + 'return': api.settings.url['return'] } ); @@ -3044,7 +3045,7 @@ request.done( function() { deferred.resolve(); $( window ).off( 'beforeunload.customize-confirm' ); - window.location.href = urlParser.href; // @todo Use location.replace()? + location.replace( urlParser.href ); } ); request.fail( function() { @@ -6958,7 +6959,9 @@ if ( 'changeset_already_published' === response.code && response.next_changeset_uuid ) { api.settings.changeset.uuid = response.next_changeset_uuid; api.state( 'changesetStatus' ).set( '' ); - parent.send( 'changeset-uuid', api.settings.changeset.uuid ); + if ( api.settings.changeset.branching ) { + parent.send( 'changeset-uuid', api.settings.changeset.uuid ); + } api.previewer.send( 'changeset-uuid', api.settings.changeset.uuid ); } } ); @@ -6989,7 +6992,9 @@ api.state( 'changesetStatus' ).set( '' ); api.settings.changeset.uuid = response.next_changeset_uuid; - parent.send( 'changeset-uuid', api.settings.changeset.uuid ); + if ( api.settings.changeset.branching ) { + parent.send( 'changeset-uuid', api.settings.changeset.uuid ); + } } // Prevent subsequent requestChangesetUpdate() calls from including the settings that have been saved. @@ -7065,6 +7070,7 @@ urlParser.href = location.href; queryParams = api.utils.parseQueryString( urlParser.search.substr( 1 ) ); delete queryParams.changeset_uuid; + queryParams['return'] = api.settings.url['return']; urlParser.search = $.param( queryParams ); location.replace( urlParser.href ); }; @@ -7418,6 +7424,7 @@ } else { queryParams.customize_autosaved = 'on'; } + queryParams['return'] = api.settings.url['return']; urlParser.search = $.param( queryParams ); return urlParser.href; } @@ -7915,16 +7922,9 @@ } api.bind( 'change', startPromptingBeforeUnload ); - closeBtn.on( 'click.customize-controls-close', function( event ) { + function requestClose() { var clearedToClose = $.Deferred(); - event.preventDefault(); - - /* - * The isInsideIframe condition is because Customizer is not able to use a confirm() - * since customize-loader.js will also use one. So autosave restorations are disabled - * when customize-loader.js is used. - */ - if ( isInsideIframe || isCleanState() ) { + if ( isCleanState() ) { clearedToClose.resolve(); } else if ( confirm( api.l10n.saveAlert ) ) { @@ -7954,15 +7954,27 @@ } else { clearedToClose.reject(); } + return clearedToClose.promise(); + } - clearedToClose.done( function() { - $( window ).off( 'beforeunload.customize-confirm' ); - if ( isInsideIframe ) { - parent.send( 'close' ); - } else { - window.location.href = closeBtn.prop( 'href' ); - } + parent.bind( 'confirm-close', function() { + requestClose().done( function() { + parent.send( 'confirmed-close', true ); + } ).fail( function() { + parent.send( 'confirmed-close', false ); } ); + } ); + + closeBtn.on( 'click.customize-controls-close', function( event ) { + event.preventDefault(); + if ( isInsideIframe ) { + parent.send( 'close' ); // See confirm-close logic above. + } else { + requestClose().done( function() { + $( window ).off( 'beforeunload.customize-confirm' ); + window.location.href = closeBtn.prop( 'href' ); + } ); + } }); })(); @@ -7978,7 +7990,9 @@ parent.send( 'title', newTitle ); }); - parent.send( 'changeset-uuid', api.settings.changeset.uuid ); + if ( api.settings.changeset.branching ) { + parent.send( 'changeset-uuid', api.settings.changeset.uuid ); + } // Initialize the connection with the parent frame. parent.send( 'ready' ); diff --git a/src/wp-admin/js/theme.js b/src/wp-admin/js/theme.js index 0fddbae46d..0f35f9fa36 100644 --- a/src/wp-admin/js/theme.js +++ b/src/wp-admin/js/theme.js @@ -77,6 +77,8 @@ themes.view.Appearance = wp.Backbone.View.extend({ // Render search form. this.search(); + this.$el.removeClass( 'search-loading' ); + // Render and append this.view.render(); this.$el.empty().append( this.view.el ).addClass( 'rendered' ); @@ -1345,17 +1347,15 @@ themes.view.Search = wp.Backbone.View.extend({ event.target.value = ''; } - /** - * Since doSearch is debounced, it will only run when user input comes to a rest - */ + // Note that doSearch is throttled. this.doSearch( event ); }, // Runs a search on the theme collection. - doSearch: _.debounce( function( event ) { + doSearch: _.throttle( function( event ) { var options = {}; - this.collection.doSearch( event.target.value ); + this.collection.doSearch( event.target.value.replace( /\+/g, ' ' ) ); // if search is initiated and key is not return if ( this.searching && event.which !== 13 ) { @@ -1376,7 +1376,7 @@ themes.view.Search = wp.Backbone.View.extend({ var url = themes.router.baseUrl( '' ); if ( event.target.value ) { - url = themes.router.baseUrl( themes.router.searchPath + event.target.value ); + url = themes.router.baseUrl( themes.router.searchPath + encodeURIComponent( event.target.value ) ); } this.searching = false; @@ -1385,6 +1385,22 @@ themes.view.Search = wp.Backbone.View.extend({ } }); +/** + * Navigate router. + * + * @since 4.9.0 + * + * @param {string} url - URL to navigate to. + * @param {object} state - State. + * @returns {void} + */ +function navigateRouter( url, state ) { + var router = this; + if ( Backbone.history._hasPushState ) { + Backbone.Router.prototype.navigate.call( router, url, state ); + } +} + // Sets up the routes events for relevant url queries // Listens to [theme] and [search] params themes.Router = Backbone.Router.extend({ @@ -1405,18 +1421,14 @@ themes.Router = Backbone.Router.extend({ searchPath: '?search=', search: function( query ) { - $( '.wp-filter-search' ).val( query ); + $( '.wp-filter-search' ).val( query.replace( /\+/g, ' ' ) ); }, themes: function() { $( '.wp-filter-search' ).val( '' ); }, - navigate: function() { - if ( Backbone.history._hasPushState ) { - Backbone.Router.prototype.navigate.apply( this, arguments ); - } - } + navigate: navigateRouter }); @@ -1508,7 +1520,7 @@ themes.view.InstallerSearch = themes.view.Search.extend({ this.doSearch( event.target.value ); }, - doSearch: _.debounce( function( value ) { + doSearch: _.throttle( function( value ) { var request = {}; // Don't do anything if the search terms haven't changed. @@ -1551,7 +1563,7 @@ themes.view.InstallerSearch = themes.view.Search.extend({ this.collection.query( request ); // Set route - themes.router.navigate( themes.router.baseUrl( themes.router.searchPath + value ), { replace: true } ); + themes.router.navigate( themes.router.baseUrl( themes.router.searchPath + encodeURIComponent( value ) ), { replace: true } ); }, 500 ) }); @@ -1887,14 +1899,10 @@ themes.InstallerRouter = Backbone.Router.extend({ searchPath: '?search=', search: function( query ) { - $( '.wp-filter-search' ).val( query ); + $( '.wp-filter-search' ).val( query.replace( /\+/g, ' ' ) ); }, - navigate: function() { - if ( Backbone.history._hasPushState ) { - Backbone.Router.prototype.navigate.apply( this, arguments ); - } - } + navigate: navigateRouter }); @@ -2003,6 +2011,19 @@ $( document ).ready(function() { themes.Run.init(); } + // Update the return param just in time. + $( document.body ).on( 'click', '.load-customize', function() { + var link = $( this ), urlParser = document.createElement( 'a' ); + urlParser.href = link.prop( 'href' ); + urlParser.search = $.param( _.extend( + wp.customize.utils.parseQueryString( urlParser.search.substr( 1 ) ), + { + 'return': window.location.href + } + ) ); + link.prop( 'href', urlParser.href ); + }); + $( '.broken-themes .delete-theme' ).on( 'click', function() { return confirm( _wpThemeSettings.settings.confirmDelete ); }); diff --git a/src/wp-admin/themes.php b/src/wp-admin/themes.php index 8295a04fcb..026839f5bc 100644 --- a/src/wp-admin/themes.php +++ b/src/wp-admin/themes.php @@ -146,14 +146,13 @@ wp_localize_script( 'theme', '_wpThemeSettings', array( add_thickbox(); wp_enqueue_script( 'theme' ); wp_enqueue_script( 'updates' ); -wp_enqueue_script( 'customize-loader' ); require_once( ABSPATH . 'wp-admin/admin-header.php' ); ?>

- +

@@ -234,7 +233,13 @@ if ( ! $ct->errors() || ( 1 == count( $ct->errors()->get_error_codes() ) ?> -
+ +
$theme, 'fields' => array('sections' => false, 'tags' => false) ) ); //Save on a bit of bandwidth. - if ( is_wp_error($api) ) - wp_die($api); - - wp_enqueue_script( 'customize-loader' ); + if ( is_wp_error( $api ) ) { + wp_die( $api ); + } $title = __('Install Themes'); $parent_file = 'themes.php'; @@ -253,8 +251,6 @@ if ( isset($_GET['action']) ) { $file_upload = new File_Upload_Upgrader('themezip', 'package'); - wp_enqueue_script( 'customize-loader' ); - $title = __('Upload Theme'); $parent_file = 'themes.php'; $submenu_file = 'theme-install.php'; diff --git a/src/wp-includes/class-wp-customize-manager.php b/src/wp-includes/class-wp-customize-manager.php index 0e4cf6c689..d239ad3ea6 100644 --- a/src/wp-includes/class-wp-customize-manager.php +++ b/src/wp-includes/class-wp-customize-manager.php @@ -4236,6 +4236,7 @@ final class WP_Customize_Manager { ), 'url' => array( 'preview' => esc_url_raw( $this->get_preview_url() ), + 'return' => esc_url_raw( $this->get_return_url() ), 'parent' => esc_url_raw( admin_url() ), 'activated' => esc_url_raw( home_url( '/' ) ), 'ajax' => esc_url_raw( admin_url( 'admin-ajax.php', 'relative' ) ), diff --git a/src/wp-includes/js/customize-loader.js b/src/wp-includes/js/customize-loader.js index 27228bd47b..9d0aad8497 100644 --- a/src/wp-includes/js/customize-loader.js +++ b/src/wp-includes/js/customize-loader.js @@ -1,4 +1,4 @@ -/* global _wpCustomizeLoaderSettings, confirm */ +/* global _wpCustomizeLoaderSettings */ /** * Expose a public API that allows the customizer to be * loaded on any page. @@ -208,25 +208,30 @@ window.wp = window.wp || {}; * Close the Customizer overlay. */ close: function() { - if ( ! this.active ) { + var self = this, onConfirmClose; + if ( ! self.active ) { return; } - // Display AYS dialog if Customizer is dirty - if ( ! this.saved() && ! confirm( Loader.settings.l10n.saveAlert ) ) { - // Go forward since Customizer is exited by history.back() - history.forward(); - return; - } + onConfirmClose = function( confirmed ) { + if ( confirmed ) { + self.active = false; + self.trigger( 'close' ); - this.active = false; + // Restore document title prior to opening the Live Preview + if ( self.originalDocumentTitle ) { + document.title = self.originalDocumentTitle; + } + } else { - this.trigger( 'close' ); + // Go forward since Customizer is exited by history.back() + history.forward(); + } + self.messenger.unbind( 'confirmed-close', onConfirmClose ); + }; + self.messenger.bind( 'confirmed-close', onConfirmClose ); - // Restore document title prior to opening the Live Preview - if ( this.originalDocumentTitle ) { - document.title = this.originalDocumentTitle; - } + Loader.messenger.send( 'confirm-close' ); }, /** diff --git a/src/wp-includes/script-loader.php b/src/wp-includes/script-loader.php index 7b4eb73c5c..8989cf0ce3 100644 --- a/src/wp-includes/script-loader.php +++ b/src/wp-includes/script-loader.php @@ -725,7 +725,7 @@ function wp_default_scripts( &$scripts ) { $scripts->add( 'text-widgets', "/wp-admin/js/widgets/text-widgets$suffix.js", array( 'jquery', 'backbone', 'editor', 'wp-util', 'wp-a11y' ) ); $scripts->add( 'custom-html-widgets', "/wp-admin/js/widgets/custom-html-widgets$suffix.js", array( 'code-editor', 'jquery', 'backbone', 'wp-util', 'jquery-ui-core', 'wp-a11y' ) ); - $scripts->add( 'theme', "/wp-admin/js/theme$suffix.js", array( 'wp-backbone', 'wp-a11y' ), false, 1 ); + $scripts->add( 'theme', "/wp-admin/js/theme$suffix.js", array( 'wp-backbone', 'wp-a11y', 'customize-base' ), false, 1 ); $scripts->add( 'inline-edit-post', "/wp-admin/js/inline-edit-post$suffix.js", array( 'jquery', 'tags-suggest', 'wp-a11y' ), false, 1 ); did_action( 'init' ) && $scripts->localize( 'inline-edit-post', 'inlineEditL10n', array(