From f7e58112876272e1c0e8a77df9aafaec71b4c6de Mon Sep 17 00:00:00 2001 From: Helen Hou-Sandi Date: Fri, 4 Nov 2016 15:53:01 +0000 Subject: [PATCH] Customize: Revert theme install feature. This is a great goal for core, and is close, but it is not in shape to be shipped for 4.7 and there is not enough time left in the development cycle to alter and polish sufficiently. There are bugs, but more than that, there are more fundamental questions around the use of existing UI, general UX, and how findable themes are (not) on the .org side. see #37661. git-svn-id: https://develop.svn.wordpress.org/trunk@39140 602fd350-edb4-49c9-b593-d223f7449a82 --- src/wp-admin/css/customize-controls.css | 589 ++--------- src/wp-admin/css/themes.css | 2 +- src/wp-admin/customize.php | 3 +- src/wp-admin/includes/theme.php | 48 +- src/wp-admin/includes/update-core.php | 1 + src/wp-admin/js/customize-controls.js | 970 ++++-------------- src/wp-admin/js/updates.js | 46 +- .../class-wp-customize-manager.php | 268 +---- .../class-wp-customize-theme-control.php | 51 +- .../class-wp-customize-themes-panel.php | 113 -- .../class-wp-customize-themes-section.php | 140 +-- tests/phpunit/tests/customize/manager.php | 2 +- 12 files changed, 405 insertions(+), 1828 deletions(-) delete mode 100644 src/wp-includes/customize/class-wp-customize-themes-panel.php diff --git a/src/wp-admin/css/customize-controls.css b/src/wp-admin/css/customize-controls.css index f2fbc86ef4..2921129a2b 100644 --- a/src/wp-admin/css/customize-controls.css +++ b/src/wp-admin/css/customize-controls.css @@ -308,17 +308,21 @@ body { } #customize-theme-controls .customize-pane-child.open, -#customize-theme-controls .customize-pane-child.current-panel { +#customize-theme-controls .customize-pane-child.current-panel, +#customize-theme-controls .customize-themes-panel.customize-pane-child.current-panel { -webkit-transform: none; -ms-transform: none; transform: none; } +#customize-theme-controls .customize-themes-panel.customize-pane-child, .section-open #customize-theme-controls .customize-pane-parent, .in-sub-panel #customize-theme-controls .customize-pane-parent, .section-open #customize-info, .in-sub-panel #customize-info, -.in-sub-panel.section-open #customize-theme-controls .customize-pane-child.current-panel { +.in-sub-panel.section-open #customize-theme-controls .customize-pane-child.current-panel, +.in-themes-panel #customize-theme-controls .customize-pane-parent, +.in-themes-panel #customize-info { visibility: hidden; height: 0; overflow: hidden; @@ -329,8 +333,10 @@ body { .section-open #customize-theme-controls .customize-pane-parent.busy, .in-sub-panel #customize-theme-controls .customize-pane-parent.busy, +.in-themes-panel #customize-theme-controls .customize-pane-parent.busy, .section-open #customize-info.busy, .in-sub-panel #customize-info.busy, +.in-themes-panel #customize-info.busy, .busy.section-open.in-sub-panel #customize-theme-controls .customize-pane-child.current-panel, #customize-theme-controls .customize-pane-child.open, #customize-theme-controls .customize-pane-child.current-panel, @@ -340,6 +346,13 @@ body { overflow: auto; } +.in-themes-panel #customize-theme-controls .customize-pane-parent, +.in-themes-panel #customize-info { + -webkit-transform: translateX(100%); + -ms-transform: translateX(100%); + transform: translateX(100%); +} + #customize-theme-controls .customize-pane-child.accordion-section-content, #customize-theme-controls .customize-pane-child.accordion-sub-container { display: block; @@ -1150,21 +1163,15 @@ p.customize-section-description { animation: customize-reload .75s; } -#customize-theme-controls .control-panel-themes { - border-bottom: none; -} - -#customize-theme-controls .control-panel-themes > .accordion-section-title:hover, /* Not a focusable element. */ -#customize-theme-controls .control-panel-themes > .accordion-section-title { +#customize-theme-controls .control-section-themes .accordion-section-title:hover, /* Not a focusable element. */ +#customize-theme-controls .control-section-themes .accordion-section-title { cursor: default; background: #fff; color: #555; border-top: 1px solid #ddd; border-bottom: 1px solid #ddd; border-left: none; - border-right: none; - margin: 0 0 15px 0; - padding-right: 100px; /* Space for the button */ + margin-top: 0; } #customize-theme-controls .control-section-themes .customize-themes-panel .accordion-section-title:first-child:hover, /* Not a focusable element. */ @@ -1172,14 +1179,29 @@ p.customize-section-description { border-top: 0; } -.control-panel-themes .accordion-section-title span.customize-action, +#customize-theme-controls .control-section-themes > .accordion-section-title:hover, /* Not a focusable element. */ +#customize-theme-controls .control-section-themes > .accordion-section-title { + margin: 0 0 15px; +} + +#customize-controls .customize-themes-panel .accordion-section-title { + margin: 15px -8px; +} + +#customize-controls .control-section-themes .accordion-section-title, +#customize-controls .customize-themes-panel .accordion-section-title { + padding-right: 100px; /* Space for the button */ +} + +#customize-controls .control-section-themes .accordion-section-title span.customize-action, #customize-controls .customize-section-title span.customize-action { font-size: 13px; display: block; font-weight: 400; } -.control-panel-themes .accordion-section-title .change-theme { +#customize-controls .control-section-themes .accordion-section-title .change-theme, +#customize-controls .customize-themes-panel .accordion-section-title .customize-theme { position: absolute; right: 10px; top: 50%; @@ -1187,391 +1209,38 @@ p.customize-section-description { font-weight: 400; } -#customize-theme-controls .control-panel-themes > .accordion-section-title:after { +#customize-controls .control-section-themes .accordion-section-title:before { display: none; } -.control-panel-themes .customize-themes-full-container { - position: fixed; - top: 0; - left: 0; - -webkit-transition: .18s left ease-in-out; - transition: .18s left ease-in-out; - margin: 0 0 0 300px; - padding:25px; - overflow-y: scroll; - width: -webkit-calc(100% - 350px); - width: calc(100% - 350px); - height: -webkit-calc(100% - 50px); - height: calc(100% - 50px); - background: #eee; - z-index: 20; -} - -/* Animations for opening the themes panel */ -#customize-header-actions .save, -#customize-header-actions .spinner, -#customize-header-actions .customize-controls-preview-toggle { - position: relative; - top: 0; - -webkit-transition: .18s top ease-in-out; - transition: .18s top ease-in-out; -} - -#customize-footer-actions, -#customize-footer-actions .collapse-sidebar { - bottom: 0; - -webkit-transition: .18s bottom ease-in-out; - transition: .18s bottom ease-in-out; -} - -.in-themes-panel:not(.animating) #customize-header-actions .save, -.in-themes-panel:not(.animating) #customize-header-actions .spinner, -.in-themes-panel:not(.animating) #customize-header-actions .customize-controls-preview-toggle, -.in-themes-panel:not(.animating) #customize-preview, -.in-themes-panel:not(.animating) #customize-footer-actions { - visibility: hidden; -} - -.wp-full-overlay.in-themes-panel { - background: #eee; /* Prevents a black flash when fading in the panel */ -} - -.in-themes-panel #customize-header-actions .save, -.in-themes-panel #customize-header-actions .spinner, -.in-themes-panel #customize-header-actions .customize-controls-preview-toggle { - top: -45px; -} - -.in-themes-panel #customize-footer-actions, -.in-themes-panel #customize-footer-actions .collapse-sidebar { - bottom: -45px; -} - -/* Don't show the theme count while the panel opens, as it's in the wrong place during the animation */ -.in-themes-panel.animating .control-panel-themes .filter-themes-count { - display: none; -} - -.in-themes-panel.wp-full-overlay .wp-full-overlay-sidebar-content { - bottom: 0; -} - -/* Adds a delay before fading in to avoid it "jumping" */ -@-webkit-keyframes themes-fade-in { - 0% { - opacity: 0; - } - 50% { - opacity: 0; - } - 100% { - opacity: 1; - } -} -@keyframes themes-fade-in { - 0% { - opacity: 0; - } - 50% { - opacity: 0; - } - 100% { - opacity: 1; - } -} - -.control-panel-themes .customize-themes-full-container.animate { - -webkit-animation: .6s themes-fade-in 1; - animation: .6s themes-fade-in 1; -} - -.in-themes-panel:not(.animating) .control-panel-themes .filter-themes-count { - -webkit-animation: .6s themes-fade-in 1; - animation: .6s themes-fade-in 1; -} - -.control-panel-themes .filter-themes-count { - position: fixed; - top: 0; - left: 48px; - width: 222px; - padding: 6px 15px; - margin: 0; - line-height: 32px; - text-align: right; - z-index: 10; -} - -.control-panel-themes .filter-themes-count .themes-displayed { - font-weight: 600; - color: #555d66; -} - -.control-panel-themes .filter-themes-count .see-themes, -.control-panel-themes .filter-themes-count .filter-themes { - display: none; -} - - -/* Mobile - toggle between themes and filters */ -@media screen and (max-width:600px) { - - /* Show a spinner in the filters view also, reusing the main customize spinner */ - .in-themes-panel.loading #customize-header-actions .spinner { - position: fixed; - top: 0; - left: 48px; - visibility: visible; - } - - .in-themes-panel.loading.showing-themes #customize-header-actions .spinner { - visibility: hidden; - } - - .control-panel-themes .filter-themes-count { - width: -webkit-calc(100% - 93px); - width: calc(100% - 93px); - } - - .control-panel-themes .filter-themes-count .themes-displayed { - display: none; - } - - .wp-full-overlay:not(.showing-themes) .control-panel-themes .filter-themes-count .see-themes { - display: block; - float: right; - } - - .wp-full-overlay.showing-themes .control-panel-themes .filter-themes-count .filter-themes { - display: block; - float: right; - } - - .in-themes-panel.showing-themes .control-panel-themes .customize-panel-back { - position: fixed; - top: 0; - left: 0; - z-index: 10; - height: 45px; - background: #eee; - } - - .in-themes-panel.showing-themes .control-panel-themes .customize-panel-back:before { - line-height: 45px; - } - - .control-panel-themes .customize-themes-full-container { - width: -webkit-calc(100% - 50px); - width: calc(100% - 50px); - margin: 0; - top: 46px; - height: -webkit-calc(100% - 96px); - height: calc(100% - 96px); - z-index: 1; - display: none; - } - - .showing-themes .control-panel-themes .customize-themes-full-container { - display: block; - } -} - -.control-panel-themes .customize-themes-notifications .notice { - margin: 0 0 25px 0; -} - -.customize-themes-full-container .customize-themes-section { - display: none !important; /* There is unknown JS that perpetually tries to show all theme sections when more items are added. */ - overflow: hidden; -} - -.customize-themes-full-container .customize-themes-section.current-section { - display: list-item !important; /* There is unknown JS that perpetually tries to show all theme sections when more items are added. */ -} - -.theme-section .customize-themes-text-before { - padding: 0 0 8px 15px; - margin: 15px 0 0 0; - line-height: 16px; - border-bottom: 1px solid #ddd; - color: #555d66; -} - -.control-panel-themes .customize-themes-section-title { - width: 100%; - background: #fff; - -webkit-box-shadow: none; - box-shadow: none; - outline: none; - border-top: none; - border-bottom: 1px solid #ddd; - border-left: 4px solid #fff; - border-right: none; - cursor: pointer; - padding: 10px 15px; - position: relative; - text-align: left; - font-size: 14px; - font-weight: 600; - color: #555d66; - text-shadow: none; -} - -.control-panel-themes .theme-section { - margin: 0; - position: relative; -} - -.control-panel-themes .customize-themes-section-title:focus, -.control-panel-themes .customize-themes-section-title:hover { - border-left-color: #0073aa; - color: #0073aa; - background: #f5f5f5; -} - -.control-panel-themes .theme-section .customize-themes-section-title.selected:after { - content: "\f147"; - font: 16px/1 dashicons; +#customize-controls .customize-themes-panel { + padding: 0 8px; + background: #f1f1f1; -webkit-box-sizing: border-box; -moz-box-sizing: border-box; box-sizing: border-box; - width: 20px; - height: 20px; - padding: 3px 3px 1px 1px; /* Re-align the icon to the smaller grid */ - -webkit-border-radius: 100%; - border-radius: 100%; - position: absolute; - top: 9px; - right: 15px; - background: #0073aa; - color: #fff; } -.control-panel-themes .customize-themes-section-title.selected { - color: #0073aa; +#customize-controls .customize-themes-panel .accordion-section-title:first-child { + margin-top: 0; } -.control-panel-themes .customize-themes-section-title.themes-section-search_themes { - border-left: none; - padding: 5px 10px 5px 15px; - width: auto; -} - -.control-panel-themes .customize-themes-section-title.themes-section-feature_filter_themes:after, -.control-panel-themes .customize-themes-section-title.themes-section-favorites_themes:after { - content: "\f140"; - font: 20px/1 dashicons; - position: absolute; - right: 15px; - top: 8px; -} - -.control-panel-themes .customize-themes-section-title.themes-section-search_themes .wp-filter-search { - width: 100%; -} - -.control-panel-themes .customize-themes-section-title.themes-section-search_themes.selected, -.control-panel-themes .customize-themes-section-title.themes-section-search_themes:hover { - background: #fff; - cursor: default; -} - -.control-panel-themes .customize-themes-section-title.themes-section-feature_filter_themes { - margin-top: 15px; - border-top: 1px solid #ddd; -} - -.control-panel-themes .filter-details { - background: #f5f5f5; - margin: 0; - padding: 8px 15px; - border-top: none; - border-bottom: 1px solid #ddd; - display: none; -} - -.control-panel-themes .customize-themes-section-title.selected.details-open { - border-bottom-color: #f5f5f5; - border-left-color: #f5f5f5; - background: #f5f5f5; -} - -.control-panel-themes .favorites-form.filter-details label { - padding-bottom: 6px; - display: inline-block; -} - -.control-panel-themes .filter-details .filter-group { - float: none; - width: 100%; - background: transparent; - border: none; - padding: 0; - -webkit-box-shadow: none; - box-shadow: none; -} - -.control-panel-themes .filter-details .filter-group legend button { - padding: 18px 15px 8px 10px; - line-height: 14px; - border-bottom: 1px solid #ddd; - width: 100%; - text-align: left; -} - -.control-panel-themes .filter-details .filter-group legend { - position: relative; - top: 0; - width: 100%; -} - -.control-panel-themes .filter-details .filter-group legend button:after { - content: "\f140"; - font: 20px/1 dashicons; - position: absolute; - bottom: 6px; - right: 5px; -} - -.control-panel-themes .filter-details .filter-group legend button:hover, -.control-panel-themes .filter-details .filter-group legend button:focus { - color: #0073aa; - border-bottom-color: #0073aa; /* Color change for focus style should be acceptable because border-bottom is barely visible previously. */ - outline: none; - -webkit-box-shadow: none; - box-shadow: none; -} - -.control-panel-themes .filter-details .filter-group legend button.open:after { - content: "\f142"; -} - -.control-panel-themes .filter-details .filter-group .filter-group-feature { - display: none; - margin: 0; -} - -.control-panel-themes .filter-details .filter-group-feature label { - border: 1px solid #ddd; - border-top: 0; - background: #fff; - color: #555d66; - margin: 0; - padding: 12px 10px 12px 34px; - width: -webkit-calc(100% - 46px); - width: calc(100% - 46px); - line-height: 16px; +#customize-controls .customize-themes-panel .accordion-section-title:nth-child(2) { + font-size: 14px; font-weight: 600; } -.control-panel-themes .filter-details .filter-group-feature input { - position: absolute; - margin: 12px 10px; +#customize-controls .customize-themes-panel > h2 { + padding: 15px 8px 0 8px; } -.control-panel-themes .filter-details .filter-group-feature label:hover { - color: #0073aa; +#customize-theme-controls .customize-themes-panel .accordion-section-content { + background: transparent; + display: block; +} + +.customize-control.customize-control-theme { + margin-bottom: 8px; } #customize-theme-controls .themes.accordion-section-content { @@ -1581,111 +1250,17 @@ p.customize-section-description { width: 100%; } -.loading .customize-themes-section .spinner { - display: block; - visibility: visible; - position: relative; - clear: both; - width: 20px; - height: 20px; - left: -webkit-calc(50% - 10px); - left: calc(50% - 10px); - float: none; - margin-top: 50px; -} - -.customize-themes-section .filter-drawer { - border-top: none; - display: block; - background: transparent; - padding-top: 5px; -} - -.customize-themes-section .clear-filters { - margin-left: 8px; - display: none; -} - -.customize-themes-section .no-themes { - display: none; -} - -.themes-section-installed_themes .theme .notice-success { - display: none; /* Hide "installed" notice on installed themes tab. */ -} - -.control-panel-themes .theme-browser .theme .theme-actions .button-primary { - margin: 0 0 0 8px; -} - -.customize-control-theme .theme { - width: 100%; - margin: 0; -} - -.customize-control.customize-control-theme { /* override most properties on .customize-control */ - -webkit-box-sizing: border-box; - -moz-box-sizing: border-box; - box-sizing: border-box; - width: 18.4%; - margin: 0 2% 2% 0; - padding: 0; - clear: none; -} - -/* 5 columns above 2100px */ -@media screen and (min-width: 2101px) { - .customize-control.customize-control-theme:nth-child(5n) { - margin-right: 0; - } -} - -/* 4 columns up to 2100px */ -@media screen and (min-width: 1601px) and (max-width: 2100px) { - .customize-control.customize-control-theme { - width: 23.5%; - } - - .customize-control.customize-control-theme:nth-child(4n) { - margin-right: 0; - } -} - -/* 3 columns up to 1600px */ -@media screen and (min-width: 1201px) and (max-width: 1600px) { - .customize-control.customize-control-theme { - width: 32%; - } - - .customize-control.customize-control-theme:nth-child(3n) { - margin-right: 0; - } -} - -/* 2 columns up to 1200px */ -@media screen and (min-width: 851px) and (max-width: 1200px) { - .customize-control.customize-control-theme { - width: 49%; - } - - .customize-control.customize-control-theme:nth-child(even) { - margin-right: 0; - } -} - -/* 1 column up to 850 px */ -@media screen and (max-width: 850px) { - .customize-control.customize-control-theme { - width: 100%; - margin: 0 0 3% 0; - } -} - .wp-customizer .theme-browser .themes { padding-bottom: 8px; } +.wp-customizer .theme-browser .theme { + margin: 0; + width: 100%; +} + .wp-customizer .theme-browser .theme .theme-actions { + -ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=100)"; opacity: 1; } @@ -1704,6 +1279,15 @@ p.customize-section-description { width: 100%; } +.control-section-themes .accordion-section-title:after, +.customize-themes-panel .accordion-section-title:after { + display: none; +} + +.customize-themes-panel.control-panel-content { + border-top: 1px solid #ddd; +} + /* Details View */ .wp-customizer .theme-overlay { display: none; @@ -1718,58 +1302,31 @@ p.customize-section-description { z-index: 109; } -/* Avoid a z-index war by resetting elements that should be under the overlay. - This is likely required because of the way that sections and panels are positioned. */ -.wp-customizer.modal-open #customize-header-actions, -.wp-customizer.modal-open .control-panel-themes .filter-themes-count, -.wp-customizer.modal-open .control-panel-themes .customize-themes-section-title.selected:after { - z-index: -1; -} - .wp-customizer .theme-overlay .theme-backdrop { background: rgba( 238, 238, 238, 0.75 ); position: fixed; z-index: 110; } -.wp-customizer .theme-overlay .star-rating { - float: left; - margin-right: 8px; -} - -.wp-customizer .theme-rating .num-ratings { - line-height: 20px; -} - .wp-customizer .theme-overlay .theme-wrap { left: 90px; right: 90px; top: 45px; bottom: 45px; z-index: 120; + max-width: 1740px; /* To ensure that theme screenshots are not displayed larger than 880px wide. */ } .wp-customizer .theme-overlay .theme-actions { - text-align: right; /* Because there're only one or two actions, match the UI pattern of media modals and right-align the action. */ - padding: 10px 15px; + text-align: right; /* Because there's only one action, match the pattern of media modals and right-align the action. */ } -.wp-customizer .theme-overlay .theme-actions .theme-install.preview { - margin-left: 8px; +.ie8 .wp-customizer .theme-overlay .theme-header, +.ie8 .wp-customizer .theme-overlay .theme-about, +.ie8 .wp-customizer .theme-overlay .theme-actions { + position: static; } -.control-panel-themes .theme-actions .delete-theme { - left: 15px; /* these override themes.css on mobile */ - right: auto; - bottom: auto; - position: absolute; -} - -.modal-open .in-themes-panel #customize-controls .wp-full-overlay-sidebar-content { - overflow: visible; /* Prevent the top-level Customizer controls from becoming visible when elements on the right of the details modal are focused. */ -} - - /* Small Screens */ @media (max-width:850px), (max-height:472px) { .wp-customizer .theme-overlay .theme-wrap { diff --git a/src/wp-admin/css/themes.css b/src/wp-admin/css/themes.css index 42721d2bee..5019c1cd18 100644 --- a/src/wp-admin/css/themes.css +++ b/src/wp-admin/css/themes.css @@ -570,7 +570,7 @@ body.folded .theme-browser ~ .theme-overlay .theme-wrap { float: left; margin: 0 30px 0 0; width: 55%; - max-width: 1200px; /* Recommended theme screenshot width, set here to avoid stretching */ + max-width: 880px; text-align: center; } diff --git a/src/wp-admin/customize.php b/src/wp-admin/customize.php index eea5ddcdbb..0a1f9b3efa 100644 --- a/src/wp-admin/customize.php +++ b/src/wp-admin/customize.php @@ -109,8 +109,7 @@ $admin_title = sprintf( $wp_customize->get_document_title_template(), __( 'Loadi ?><?php echo $admin_title; ?> ', - section: section.params.id, - active: true, - theme: theme, - priority: section.loaded + 1 - }, - previewer: api.previewer - } ); - - api.control.add( customizeId, themeControl ); - newThemeControls.push( themeControl ); - section.loaded = section.loaded + 1; - }); - - if ( 1 === page ) { - // Pre-load the first 3 theme screenshots. - _.each( section.controls().slice( 0, 3 ), function ( control ) { - var img, src = control.params.theme.screenshot[0]; - if ( src ) { - img = new Image(); - img.src = src; - } - }); - if ( 'search' === section.params.action ) { - wp.a11y.speak( api.settings.l10n.themeSearchResults.replace( '%d', data.info.results ) ); - } - } else { - Array.prototype.push.apply( section.screenshotQueue, newThemeControls ); // Add new themes to the screenshot queue. + if ( args.completeCallback ) { + args.completeCallback(); } - _.delay( section.renderScreenshots, 100 ); // Wait for the controls to become visible. + } ); - if ( 'installed' === section.action || 100 > themes.length ) { // If we have less than the requested 100 themes, it's the end of the list. - section.fullyLoaded = true; + overlay.addClass( 'in-themes-panel' ); + section.addClass( 'current-panel' ); + + } else if ( ! expanded && section.hasClass( 'current-panel' ) ) { + panel._animateChangeExpanded( function() { + changeBtn.attr( 'tabindex', '0' ); + customizeBtn.attr( 'tabindex', '-1' ); + + changeBtn.focus(); + section.css( 'top', '' ); + + if ( args.completeCallback ) { + args.completeCallback(); } - } else { - if ( 0 === section.loaded ) { - section.container.find( '.no-themes' ).show(); - wp.a11y.speak( section.container.find( '.no-themes' ).text() ); - } else { - section.fullyLoaded = true; - } - } - if ( 'installed' === section.params.action ) { - section.updateCount(); - } else { - section.updateCount( data.info.results ); - } - section.container.find( '.unexpected-error' ).hide(); // Hide error notice in case it was previously shown. + } ); - // This cannot run on request.always, as section.loading may turn false before the new controls load in the success case. - section.headContainer.closest( '.wp-full-overlay' ).removeClass( 'loading' ); - section.loading = false; - }); - request.fail(function( data ) { - if ( 'undefined' === typeof data ) { - section.container.find( '.unexpected-error' ).show(); - wp.a11y.speak( section.container.find( '.unexpected-error' ).text() ); - } else if ( typeof console !== 'undefined' && console.error ) { - console.error( data ); - } - - // This cannot run on request.always, as section.loading may turn false before the new controls load in the success case. - section.headContainer.closest( '.wp-full-overlay' ).removeClass( 'loading' ); - section.loading = false; - }); - }, - - /** - * Determines whether more themes should be loaded, and loads them. - * - * @since 4.7.0 - */ - loadMore: function() { - var section = this, container, bottom, threshold; - if ( ! section.fullyLoaded && ! section.loading ) { - container = section.container.closest( '.customize-themes-full-container' ); - - bottom = container.scrollTop() + container.height(); - threshold = container.prop( 'scrollHeight' ) - 3000; // Use a fixed distance to the bottom of loaded results to avoid unnecessarily loading results sooner when using a percentage of scroll distance. - - if ( bottom > threshold ) { - section.loadControls(); - } - } - }, - - /** - * Event handler for search, feature filter, and favorites input that determines if the term has changed and loads new controls as needed. - * - * @since 4.7.0 - * - * @param {wp.customize.ThemesSection} section The current theme section, passed through the debouncer. - */ - checkTerm: function( section ) { - var newTerm; - - // Find term. - if ( 'search' === section.params.action ) { - newTerm = $( '#wp-filter-search-input' ).val(); - } else if ( 'favorites' === section.params.action ) { - newTerm = $( '#wporg-username-input' ).val(); - } else if ( 'feature_filter' === section.params.action ) { - newTerm = section.term; // Set separately by filtersChecked(), as they're changed. - if ( '' === newTerm ) { - return; - } - } else { - return; - } - - if ( section.term === newTerm && 'feature_filter' !== section.params.action ) { - return; - } - - // Clear the controls in the section. - _.each( section.controls(), function( control ) { - control.container.remove(); - api.control.remove( control.id ); - }); - section.loaded = 0; - section.fullyLoaded = false; - section.screenshotQueue = null; - - if ( '' !== newTerm ) { // Empty term should not show any results. - // Run a new query, with loadControls handling paging, etc. - section.term = newTerm; - section.loadControls(); - if ( ! section.expanded() ) { - section.expand(); // Expand the section if it isn't expanded. - } - } - }, - - /** - * Check for filters checked in the feature filter list. - * - * @since 4.7.0 - */ - filtersChecked: function() { - var section = this, - items = section.container.find( '.filter-group' ).find( ':checkbox' ), - tags = []; - - if ( 'feature_filter' !== section.params.action ) { - return false; - } - - _.each( items.filter( ':checked' ), function( item ) { - tags.push( $( item ).prop( 'value' ) ); - }); - - // When no filters are checked, restore initial state and return - if ( tags.length === 0 ) { - section.term = ''; - } else { - section.term = tags; + overlay.removeClass( 'in-themes-panel' ); + section.removeClass( 'current-panel' ); } }, @@ -1586,15 +1294,12 @@ renderScreenshots: function( ) { var section = this; - // Fill queue initially, or check for more if empty. - if ( section.screenshotQueue === null || 0 === section.screenshotQueue.length ) { - // Add controls that haven't had their screenshots rendered. - section.screenshotQueue = _.filter( section.controls(), function( control ) { - return ! control.screenshotRendered; - }); + // Fill queue initially. + if ( section.screenshotQueue === null ) { + section.screenshotQueue = section.controls(); } - // Are all screenshots rendered (for now)? + // Are all screenshots rendered? if ( ! section.screenshotQueue.length ) { return; } @@ -1629,31 +1334,6 @@ } ); }, - /** - * Update the number of themes in the section. - * - * @since 4.7.0 - */ - updateCount: function ( count ) { - if ( ! count ) { - count = this.loaded; - } - - var displayed = this.container.closest( '.control-panel-content' ).find( '.themes-displayed' ), - countEl = this.container.closest( '.control-panel-content' ).find( '.theme-count' ); - - if ( 0 === count ) { - countEl.text( count ); - } else { - // Animate the count change for emphasis. - displayed.fadeOut( 180, function() { - countEl.text( count ); - displayed.fadeIn( 180 ); - } ); - wp.a11y.speak( api.settings.l10n.announceThemeCount.replace( '%d', count ) ); - } - }, - /** * Advance the modal to the next theme. * @@ -1674,13 +1354,13 @@ * @since 4.2.0 */ getNextTheme: function () { - var section = this, control, next; - control = api.control( section.params.action + '_theme_' + this.currentTheme ); + var control, next; + control = api.control( 'theme_' + this.currentTheme ); next = control.container.next( 'li.customize-control-theme' ); if ( ! next.length ) { return false; } - next = next[0].id.replace( 'customize-control-theme-' + section.params.action, section.params.action + '_theme' ); + next = next[0].id.replace( 'customize-control-', '' ); control = api.control( next ); return control.params.theme; @@ -1706,13 +1386,13 @@ * @since 4.2.0 */ getPreviousTheme: function () { - var section = this, control, previous; - control = api.control( section.params.action + '_theme_' + this.currentTheme ); + var control, previous; + control = api.control( 'theme_' + this.currentTheme ); previous = control.container.prev( 'li.customize-control-theme' ); if ( ! previous.length ) { return false; } - previous = previous[0].id.replace( 'customize-control-theme-' + section.params.action, section.params.action + '_theme' ); + previous = previous[0].id.replace( 'customize-control-', '' ); control = api.control( previous ); return control.params.theme; @@ -1732,6 +1412,57 @@ } }, + /** + * Load theme preview. + * + * @since 4.7.0 + * + * @param {string} themeId Theme ID. + * @returns {jQuery.promise} Promise. + */ + loadThemePreview: function( themeId ) { + var deferred = $.Deferred(), onceProcessingComplete, overlay, urlParser; + + urlParser = document.createElement( 'a' ); + urlParser.href = location.href; + urlParser.search = $.param( _.extend( + api.utils.parseQueryString( urlParser.search.substr( 1 ) ), + { + theme: themeId, + changeset_uuid: api.settings.changeset.uuid + } + ) ); + + overlay = $( '.wp-full-overlay' ); + overlay.addClass( 'customize-loading' ); + + onceProcessingComplete = function() { + var request; + if ( api.state( 'processing' ).get() > 0 ) { + return; + } + + api.state( 'processing' ).unbind( onceProcessingComplete ); + + request = api.requestChangesetUpdate(); + request.done( function() { + $( window ).off( 'beforeunload.customize-confirm' ); + window.location.href = urlParser.href; + } ); + request.fail( function() { + overlay.removeClass( 'customize-loading' ); + } ); + }; + + if ( 0 === api.state( 'processing' ).get() ) { + onceProcessingComplete(); + } else { + api.state( 'processing' ).bind( onceProcessingComplete ); + } + + return deferred.promise(); + }, + /** * Render & show the theme details for a given theme model. * @@ -1740,7 +1471,7 @@ * @param {Object} theme */ showDetails: function ( theme, callback ) { - var section = this; + var section = this, link; callback = callback || function(){}; section.currentTheme = theme.id; section.overlay.html( section.template( theme ) ) @@ -1749,7 +1480,22 @@ $( 'body' ).addClass( 'modal-open' ); section.containFocus( section.overlay ); section.updateLimits(); - wp.a11y.speak( api.settings.l10n.announceThemeDetails.replace( '%s', theme.name ) ); + + link = section.overlay.find( '.inactive-theme > a' ); + + link.on( 'click', function( event ) { + event.preventDefault(); + + // Short-circuit if request is currently being made. + if ( link.hasClass( 'disabled' ) ) { + return; + } + link.addClass( 'disabled' ); + + section.loadThemePreview( theme.id ).fail( function() { + link.removeClass( 'disabled' ); + } ); + } ); callback(); }, @@ -1761,7 +1507,7 @@ closeDetails: function () { $( 'body' ).removeClass( 'modal-open' ); this.overlay.fadeOut( 'fast' ); - api.control( this.params.action + '_theme_' + this.currentTheme ).container.find( '.theme' ).focus(); + api.control( 'theme_' + this.currentTheme ).focus(); }, /** @@ -1841,8 +1587,8 @@ } if ( ! panel.contentContainer.parent().is( panel.headContainer ) ) { container.append( panel.contentContainer ); + panel.renderContent(); } - panel.renderContent(); panel.deferred.embedded.resolve(); }, @@ -2035,301 +1781,6 @@ } }); - - /** - * wp.customize.ThemesPanel - * - * Custom section for themes that displays without the customize preview. - * - * @constructor - * @augments wp.customize.Panel - * @augments wp.customize.Container - */ - api.ThemesPanel = api.Panel.extend({ - installingThemes: [], - - /** - * @since 4.7.0 - */ - attachEvents: function () { - var panel = this; - - // Attach regular panel events. - api.Panel.prototype.attachEvents.apply( this ); - - // Collapse panel to customize the current theme. - panel.contentContainer.on( 'click', '.customize-theme', function() { - panel.collapse(); - }); - - // Toggle between filtering and browsing themes on mobile. - panel.contentContainer.on( 'click', '.see-themes, .filter-themes', function() { - $( '.wp-full-overlay' ).toggleClass( 'showing-themes' ); - }); - - // Install (and maybe preview) a theme. - panel.contentContainer.on( 'click', '.theme-install', function( event ) { - panel.installTheme( event ); - }); - - // Update a theme. Theme cards have the class, the details modal has the id. - panel.contentContainer.on( 'click', '.update-theme, #update-theme', function( event ) { - // #update-theme is a link. - event.preventDefault(); - event.stopPropagation(); - - panel.updateTheme( event ); - }); - - // Delete a theme. - panel.contentContainer.on( 'click', '.delete-theme', function( event ) { - panel.deleteTheme( event ); - }); - - _.bindAll( this, 'installTheme', 'updateTheme' ); - }, - - /** - * Update UI to reflect expanded state - * - * @since 4.7.0 - * - * @param {Boolean} expanded - * @param {Object} args - * @param {Boolean} args.unchanged - * @param {Function} args.completeCallback - */ - onChangeExpanded: function ( expanded, args ) { - - // Expand/collapse the panel normally. - api.Panel.prototype.onChangeExpanded.apply( this, [ expanded, args ] ); - - // Immediately call the complete callback if there were no changes - if ( args.unchanged ) { - if ( args.completeCallback ) { - args.completeCallback(); - } - return; - } - - // Note: there is a second argument 'args' passed - var panel = this, - overlay = panel.headContainer.closest( '.wp-full-overlay' ); - - if ( expanded ) { - overlay - .addClass( 'in-themes-panel' ).addClass( 'showing-themes' ) - .delay( 200 ).find( '.customize-themes-full-container' ).addClass( 'animate' ); - - // Automatically open the installed themes section. - api.section( 'installed_themes' ).expand(); - } else { - overlay - .removeClass( 'in-themes-panel' ) - .find( '.customize-themes-full-container' ).removeClass( 'animate' ); - } - }, - - /** - * Install a theme via wp.updates. - * - * @since 4.7.0 - */ - installTheme: function( event ) { - var panel = this, preview = false, slug = $( event.target ).data( 'slug' ); - - if ( -1 !== $.inArray( this.installingThemes, slug ) ) { - return; // Theme is already being installed. - } - - wp.updates.maybeRequestFilesystemCredentials( event ); - - $( document ).one( 'wp-theme-install-success', function( event, response ) { - var theme = false, customizeId, themeControl; - if ( preview ) { - - panel.loadThemePreview( slug ).fail( function() { - $( '.wp-full-overlay' ).removeClass( 'customize-loading' ); - } ); - - } else { - api.control.each( function( control ) { - if ( 'theme' === control.params.type && control.params.theme.id === response.slug ) { - theme = control.params.theme; // Used below to add theme control. - control.rerenderAsInstalled( true ); - } - }); - - // Don't add the same theme more than once. - if ( ! theme || 'undefined' !== typeof api.control( 'installed_theme_' + theme.id ) ) { - return; - } - - // Add theme control to installed section. - theme.type = 'installed'; - customizeId = 'installed_theme_' + theme.id; - themeControl = new api.controlConstructor.theme( customizeId, { - params: { - type: 'theme', - content: $( '
  • ' ).attr( 'id', 'customize-control-theme-installed_' + theme.id ).prop( 'outerHTML' ), - section: 'installed_themes', - active: true, - theme: theme, - priority: 0 // Add all newly-installed themes to the top. - }, - previewer: api.previewer - } ); - - api.control.add( customizeId, themeControl ); - api.control( customizeId ).container.trigger( 'render-screenshot' ); - - // Close the details modal if it's open to the installed theme. - api.section.each( function( section ) { - if ( 'themes' === section.params.type ) { - if ( theme.id === section.currentTheme ) { // Don't close the modal if the user has navigated elsewhere. - section.closeDetails(); - } - } - }); - } - } ); - - this.installingThemes.push( $( event.target ).data( 'slug' ) ); // Note: we don't remove elements from installingThemes, since they shouldn't be installed again. - wp.updates.installTheme( { - slug: slug - } ); - - // Also preview the theme as the event is triggered on Install & Preview. - if ( $( event.target ).hasClass( 'preview' ) ) { - preview = true; - $( '.wp-full-overlay' ).addClass( 'customize-loading' ); - } - }, - - /** - * Load theme preview. - * - * @since 4.7.0 - * - * @param {string} themeId Theme ID. - * @returns {jQuery.promise} Promise. - */ - loadThemePreview: function( themeId ) { - var deferred = $.Deferred(), onceProcessingComplete, overlay, urlParser; - - urlParser = document.createElement( 'a' ); - urlParser.href = location.href; - urlParser.search = $.param( _.extend( - api.utils.parseQueryString( urlParser.search.substr( 1 ) ), - { - theme: themeId, - changeset_uuid: api.settings.changeset.uuid - } - ) ); - - overlay = $( '.wp-full-overlay' ); - overlay.addClass( 'customize-loading' ); - - onceProcessingComplete = function() { - var request; - if ( api.state( 'processing' ).get() > 0 ) { - return; - } - - api.state( 'processing' ).unbind( onceProcessingComplete ); - - request = api.requestChangesetUpdate(); - request.done( function() { - $( window ).off( 'beforeunload.customize-confirm' ); - window.location.href = urlParser.href; - } ); - request.fail( function() { - overlay.removeClass( 'customize-loading' ); - } ); - }; - - if ( 0 === api.state( 'processing' ).get() ) { - onceProcessingComplete(); - } else { - api.state( 'processing' ).bind( onceProcessingComplete ); - } - - return deferred.promise(); - }, - - /** - * Update a theme via wp.updates. - * - * @since 4.7.0 - */ - updateTheme: function( event ) { - wp.updates.maybeRequestFilesystemCredentials( event ); - - $( document ).one( 'wp-theme-update-success', function( event, response ) { - // Rerender the control to reflect the update. - api.control.each( function( control ) { - if ( 'theme' === control.params.type && control.params.theme.id === response.slug ) { - control.params.theme.hasUpdate = false; - control.rerenderAsInstalled( true ); - } - }); - } ); - - wp.updates.updateTheme( { - slug: $( event.target ).closest( '.notice' ).data( 'slug' ) - } ); - }, - - /** - * Delete a theme via wp.updates. - * - * @since 4.7.0 - */ - deleteTheme: function( event ) { - var theme, section; - theme = $( event.target ).data( 'slug' ); - section = api.section( 'installed_themes' ); - - event.preventDefault(); - - // Confirmation dialog for deleting a theme. - if ( ! window.confirm( api.settings.l10n.confirmDeleteTheme ) ) { - return; - } - - wp.updates.maybeRequestFilesystemCredentials( event ); - - $( document ).one( 'wp-theme-delete-success', function() { - var control = api.control( 'installed_theme_' + theme ); - - // Remove theme control. - control.container.remove(); - api.control.remove( control.id ); - - // Update installed count. - section.loaded = section.loaded - 1; - section.updateCount(); - - // Rerender any other theme controls as uninstalled. - api.control.each( function( control ) { - if ( 'theme' === control.params.type && control.params.theme.id === theme ) { - control.rerenderAsInstalled( false ); - } - }); - } ); - - wp.updates.deleteTheme( { - slug: theme - } ); - - // Close modal and focus the section. - section.closeDetails(); - section.focus(); - } - - }); - - /** * A Customizer Control. * @@ -2644,7 +2095,7 @@ * @param {Boolean} active * @param {Object} args * @param {Number} args.duration - * @param {Function} args.completeCallback + * @param {Callback} args.completeCallback */ onChangeActive: function ( active, args ) { if ( args.unchanged ) { @@ -3815,7 +3266,31 @@ api.ThemeControl = api.Control.extend({ touchDrag: false, - screenshotRendered: false, + isRendered: false, + + /** + * Defer rendering the theme control until the section is displayed. + * + * @since 4.2.0 + */ + renderContent: function () { + var control = this, + renderContentArgs = arguments; + + api.section( control.section(), function( section ) { + if ( section.expanded() ) { + api.Control.prototype.renderContent.apply( control, renderContentArgs ); + control.isRendered = true; + } else { + section.expanded.bind( function( expanded ) { + if ( expanded && ! control.isRendered ) { + api.Control.prototype.renderContent.apply( control, renderContentArgs ); + control.isRendered = true; + } + } ); + } + } ); + }, /** * @since 4.2.0 @@ -3839,11 +3314,20 @@ } // Prevent the modal from showing when the user clicks the action button. - if ( $( event.target ).is( '.theme-actions .button, .update-theme' ) ) { + if ( $( event.target ).is( '.theme-actions .button' ) ) { + return; + } + + api.section( control.section() ).loadThemePreview( control.params.theme.id ); + }); + + control.container.on( 'click keydown', '.theme-actions .theme-details', function( event ) { + if ( api.utils.isKeydownButNotEnterEvent( event ) ) { return; } event.preventDefault(); // Keep this AFTER the key filter above + api.section( control.section() ).showDetails( control.params.theme ); }); @@ -3854,12 +3338,11 @@ if ( source ) { $screenshot.attr( 'src', source ); } - control.screenshotRendered = true; }); }, /** - * Show or hide the theme based on the presence of the term in the title, description, tags, and author. + * Show or hide the theme based on the presence of the term in the title, description, and author. * * @since 4.2.0 */ @@ -3875,23 +3358,6 @@ } else { control.deactivate(); } - }, - - /** - * Rerender the theme from its JS template with the installed type. - * - * @since 4.7.0 - */ - rerenderAsInstalled: function( installed ) { - var control = this, section; - if ( installed ) { - control.params.theme.type = 'installed'; - } else { - section = api.section( control.params.section ); - control.params.theme.type = section.params.action; - } - control.renderContent(); // replaces existing content - control.container.trigger( 'render-screenshot' ); } }); @@ -4584,9 +4050,7 @@ background_position: api.BackgroundPositionControl, theme: api.ThemeControl }; - api.panelConstructor = { - themes: api.ThemesPanel - }; + api.panelConstructor = {}; api.sectionConstructor = { themes: api.ThemesSection }; @@ -4704,10 +4168,6 @@ // Sort the sections within each panel api.panel.each( function ( panel ) { - if ( 'themes' === panel.id ) { - return; // Don't reflow theme sections, as doing so moves them after the themes container. - } - var sections = panel.sections(), sectionHeadContainers = _.pluck( sections, 'headContainer' ); rootNodes.push( panel ); @@ -5349,16 +4809,6 @@ // Collapse the most granular expanded object. collapsedObject = expandedControls[0] || expandedSections[0] || expandedPanels[0]; if ( collapsedObject ) { - if ( 'themes' === collapsedObject.params.type ) { - // Themes panel or section. - if ( $( 'body' ).hasClass( 'modal-open' ) ) { - collapsedObject.closeDetails(); - } else { - // If we're collapsing a section, collapse the panel also. - wp.customize.panel( 'themes' ).collapse(); - } - return; - } collapsedObject.collapse(); event.preventDefault(); } diff --git a/src/wp-admin/js/updates.js b/src/wp-admin/js/updates.js index d15a79a620..400842ebd4 100644 --- a/src/wp-admin/js/updates.js +++ b/src/wp-admin/js/updates.js @@ -181,11 +181,7 @@ if ( $notice.length ) { $notice.replaceWith( $adminNotice ); } else { - if ( 'customize' === pagenow ) { - $( '.customize-themes-notifications' ).append( $adminNotice ); - } else { - $( '.wrap' ).find( '> h1' ).after( $adminNotice ); - } + $( '.wrap' ).find( '> h1' ).after( $adminNotice ); } $document.trigger( 'wp-updates-notice-added' ); @@ -930,17 +926,6 @@ if ( 'themes-network' === pagenow ) { $notice = $( '[data-slug="' + args.slug + '"]' ).find( '.update-message' ).removeClass( 'notice-error' ).addClass( 'updating-message notice-warning' ).find( 'p' ); - } else if ( 'customize' === pagenow ) { - - // Update the theme details UI. - $notice = $( '#update-theme' ).closest( '.notice' ).removeClass( 'notice-large' ); - - $notice.find( 'h3' ).remove(); - - // Add the top-level UI, and update both. - $notice = $notice.add( $( '#customize-control-theme-installed_' + args.slug ).find( '.update-message' ) ); - $notice = $notice.addClass( 'updating-message' ).find( 'p' ); - } else { $notice = $( '#update-theme' ).closest( '.notice' ).removeClass( 'notice-large' ); @@ -983,10 +968,6 @@ }, $notice, newText; - if ( 'customize' === pagenow ) { - $theme = wp.customize.control( 'installed_theme_' + response.slug ).container; - } - if ( 'themes-network' === pagenow ) { $notice = $theme.find( '.update-message' ); @@ -1041,10 +1022,6 @@ return; } - if ( 'customize' === pagenow ) { - $theme = wp.customize.control( 'installed_theme_' + response.slug ).container; - } - if ( 'themes-network' === pagenow ) { $notice = $theme.find( '.update-message ' ); } else { @@ -1181,23 +1158,12 @@ return; } - if ( 'customize' === pagenow ) { - if ( $document.find( 'body' ).hasClass( 'modal-open' ) ) { - $button = $( '.theme-install[data-slug="' + response.slug + '"]' ); - $card = $( '.theme-overlay .theme-info' ).prepend( $message ); - } else { - $button = $( '.theme-install[data-slug="' + response.slug + '"]' ); - $card = $button.closest( '.theme' ).addClass( 'theme-install-failed' ).append( $message ); - } - $( '.wp-full-overlay' ).removeClass( 'customize-loading' ); + if ( $document.find( 'body' ).hasClass( 'full-overlay-active' ) ) { + $button = $( '.theme-install[data-slug="' + response.slug + '"]' ); + $card = $( '.install-theme-info' ).prepend( $message ); } else { - if ( $document.find( 'body' ).hasClass( 'full-overlay-active' ) ) { - $button = $( '.theme-install[data-slug="' + response.slug + '"]' ); - $card = $( '.install-theme-info' ).prepend( $message ); - } else { - $card = $( '[data-slug="' + response.slug + '"]' ).removeClass( 'focus' ).addClass( 'theme-install-failed' ).append( $message ); - $button = $card.find( '.theme-install' ); - } + $card = $( '[data-slug="' + response.slug + '"]' ).removeClass( 'focus' ).addClass( 'theme-install-failed' ).append( $message ); + $button = $card.find( '.theme-install' ); } $button diff --git a/src/wp-includes/class-wp-customize-manager.php b/src/wp-includes/class-wp-customize-manager.php index a67ccfbb90..30581388d9 100644 --- a/src/wp-includes/class-wp-customize-manager.php +++ b/src/wp-includes/class-wp-customize-manager.php @@ -295,7 +295,6 @@ final class WP_Customize_Manager { require_once( ABSPATH . WPINC . '/customize/class-wp-customize-nav-menus-panel.php' ); - require_once( ABSPATH . WPINC . '/customize/class-wp-customize-themes-panel.php' ); require_once( ABSPATH . WPINC . '/customize/class-wp-customize-themes-section.php' ); require_once( ABSPATH . WPINC . '/customize/class-wp-customize-sidebar-section.php' ); require_once( ABSPATH . WPINC . '/customize/class-wp-customize-nav-menu-section.php' ); @@ -351,7 +350,6 @@ final class WP_Customize_Manager { add_action( 'wp_ajax_customize_save', array( $this, 'save' ) ); add_action( 'wp_ajax_customize_refresh_nonces', array( $this, 'refresh_nonces' ) ); - add_action( 'wp_ajax_customize-load-themes', array( $this, 'load_themes_ajax' ) ); add_action( 'customize_register', array( $this, 'register_controls' ) ); add_action( 'customize_register', array( $this, 'register_dynamic_settings' ), 11 ); // allow code to create settings first @@ -368,12 +366,6 @@ final class WP_Customize_Manager { // Export the settings to JS via the _wpCustomizeSettings variable. add_action( 'customize_controls_print_footer_scripts', array( $this, 'customize_pane_settings' ), 1000 ); - - // Add theme update notices. - if ( current_user_can( 'install_themes' ) || current_user_can( 'update_themes' ) ) { - require_once( ABSPATH . '/wp-admin/includes/update.php' ); - add_action( 'customize_controls_print_footer_scripts', 'wp_print_admin_notice_templates' ); - } } /** @@ -2758,9 +2750,6 @@ final class WP_Customize_Manager { foreach ( $this->controls as $control ) { $control->enqueue(); } - if ( ! is_multisite() && ( current_user_can( 'install_themes' ) || current_user_can( 'update_themes' ) || current_user_can( 'delete_themes' ) ) ) { - wp_enqueue_script( 'updates' ); - } } /** @@ -2975,7 +2964,6 @@ final class WP_Customize_Manager { $nonces = array( 'save' => wp_create_nonce( 'save-customize_' . $this->get_stylesheet() ), 'preview' => wp_create_nonce( 'preview-customize_' . $this->get_stylesheet() ), - 'switch-themes' => wp_create_nonce( 'switch-themes' ), ); /** @@ -3049,14 +3037,6 @@ final class WP_Customize_Manager { 'autofocus' => $this->get_autofocus(), 'documentTitleTmpl' => $this->get_document_title_template(), 'previewableDevices' => $this->get_previewable_devices(), - 'l10n' => array( - 'confirmDeleteTheme' => __( 'Are you sure you want to delete this theme?' ), - /* translators: %d is the number of theme search results, which cannot consider singular vs. plural forms */ - 'themeSearchResults' => __( '%d themes found' ), - /* translators: %d is the number of themes being displayed, which cannot consider singular vs. plural forms */ - 'announceThemeCount' => __( 'Displaying %d themes' ), - 'announceThemeDetails' => __( 'Showing details for theme: %s' ), - ), ); // Prepare Customize Section objects to pass to JavaScript. @@ -3160,10 +3140,8 @@ final class WP_Customize_Manager { /* Panel, Section, and Control Types */ $this->register_panel_type( 'WP_Customize_Panel' ); - $this->register_panel_type( 'WP_Customize_Themes_Panel' ); $this->register_section_type( 'WP_Customize_Section' ); $this->register_section_type( 'WP_Customize_Sidebar_Section' ); - $this->register_section_type( 'WP_Customize_Themes_Section' ); $this->register_control_type( 'WP_Customize_Color_Control' ); $this->register_control_type( 'WP_Customize_Media_Control' ); $this->register_control_type( 'WP_Customize_Upload_Control' ); @@ -3174,80 +3152,50 @@ final class WP_Customize_Manager { $this->register_control_type( 'WP_Customize_Site_Icon_Control' ); $this->register_control_type( 'WP_Customize_Theme_Control' ); - /* Themes (controls are loaded via ajax) */ + /* Themes */ - $this->add_panel( new WP_Customize_Themes_Panel( $this, 'themes', array( - 'title' => $this->theme()->display( 'Name' ), - 'description' => __( 'Once themes are installed, you can live-preview them on your site, customize them, and publish your new design. Browse available themes via the filters in this menu.' ), - 'capability' => 'switch_themes', - 'priority' => 0, + $this->add_section( new WP_Customize_Themes_Section( $this, 'themes', array( + 'title' => $this->theme()->display( 'Name' ), + 'capability' => 'switch_themes', + 'priority' => 0, ) ) ); - $this->add_section( new WP_Customize_Themes_Section( $this, 'installed_themes', array( - 'title' => __( 'Installed' ), - 'text_before' => __( 'Your local site' ), - 'action' => 'installed', - 'capability' => 'switch_themes', - 'panel' => 'themes', - 'priority' => 0, - ) ) ); - - if ( ! is_multisite() ) { - $this->add_section( new WP_Customize_Themes_Section( $this, 'search_themes', array( - 'title' => __( 'Search themes…' ), - 'text_before' => __( 'Browse all WordPress.org themes' ), - 'action' => 'search', - 'capability' => 'install_themes', - 'panel' => 'themes', - 'priority' => 5, - ) ) ); - - $this->add_section( new WP_Customize_Themes_Section( $this, 'featured_themes', array( - 'title' => __( 'Featured' ), - 'action' => 'featured', - 'capability' => 'install_themes', - 'panel' => 'themes', - 'priority' => 10, - ) ) ); - - $this->add_section( new WP_Customize_Themes_Section( $this, 'popular_themes', array( - 'title' => __( 'Popular' ), - 'action' => 'popular', - 'capability' => 'install_themes', - 'panel' => 'themes', - 'priority' => 15, - ) ) ); - - $this->add_section( new WP_Customize_Themes_Section( $this, 'latest_themes', array( - 'title' => __( 'Latest' ), - 'action' => 'latest', - 'capability' => 'install_themes', - 'panel' => 'themes', - 'priority' => 20, - ) ) ); - - $this->add_section( new WP_Customize_Themes_Section( $this, 'feature_filter_themes', array( - 'title' => __( 'Feature Filter' ), - 'action' => 'feature_filter', - 'capability' => 'install_themes', - 'panel' => 'themes', - 'priority' => 25, - ) ) ); - - $this->add_section( new WP_Customize_Themes_Section( $this, 'favorites_themes', array( - 'title' => __( 'Favorites' ), - 'action' => 'favorites', - 'capability' => 'install_themes', - 'panel' => 'themes', - 'priority' => 30, - ) ) ); - } - // Themes Setting (unused - the theme is considerably more fundamental to the Customizer experience). $this->add_setting( new WP_Customize_Filter_Setting( $this, 'active_theme', array( 'capability' => 'switch_themes', ) ) ); + require_once( ABSPATH . 'wp-admin/includes/theme.php' ); + + // Theme Controls. + + // Add a control for the active/original theme. + if ( ! $this->is_theme_active() ) { + $themes = wp_prepare_themes_for_js( array( wp_get_theme( $this->original_stylesheet ) ) ); + $active_theme = current( $themes ); + $active_theme['isActiveTheme'] = true; + $this->add_control( new WP_Customize_Theme_Control( $this, $active_theme['id'], array( + 'theme' => $active_theme, + 'section' => 'themes', + 'settings' => 'active_theme', + ) ) ); + } + + $themes = wp_prepare_themes_for_js(); + foreach ( $themes as $theme ) { + if ( $theme['active'] || $theme['id'] === $this->original_stylesheet ) { + continue; + } + + $theme_id = 'theme_' . $theme['id']; + $theme['isActiveTheme'] = false; + $this->add_control( new WP_Customize_Theme_Control( $this, $theme_id, array( + 'theme' => $theme, + 'section' => 'themes', + 'settings' => 'active_theme', + ) ) ); + } + /* Site Identity */ $this->add_section( 'title_tagline', array( @@ -3700,150 +3648,6 @@ final class WP_Customize_Manager { $this->add_dynamic_settings( $setting_ids ); } - /** - * Load themes into the theme browsing/installation UI. - * - * @since 4.7.0 - * @access public - */ - public function load_themes_ajax() { - check_ajax_referer( 'switch-themes', 'switch-themes-nonce' ); - - if ( ! current_user_can( 'switch_themes' ) ) { - wp_die( -1 ); - } - - if ( empty( $_POST['theme_action'] ) ) { - wp_send_json_error( 'missing_theme_action' ); - } - - if ( 'search' === $_POST['theme_action'] && ! array_key_exists( 'search', $_POST ) ) { - wp_send_json_error( 'empty_search' ); - } elseif ( 'favorites' === $_POST['theme_action'] && ! array_key_exists( 'user', $_POST ) ) { - wp_send_json_error( 'empty_user' ); - } elseif ( 'feature_filter' === $_POST['theme_action'] && ! array_key_exists( 'tags', $_POST ) ) { - wp_send_json_error( 'no_features' ); - } - - require_once( ABSPATH . 'wp-admin/includes/theme.php' ); - if ( 'installed' === $_POST['theme_action'] ) { - $themes = array( 'themes' => wp_prepare_themes_for_js() ); - foreach ( $themes['themes'] as &$theme ) { - $theme['type'] = 'installed'; - // Set active based on customized theme. - if ( $_POST['customized_theme'] === $theme['id'] ) { - $theme['active'] = true; - } else { - $theme['active'] = false; - } - } - } else { - if ( ! current_user_can( 'install_themes' ) ) { - wp_die( -1 ); - } - - // Arguments for all queries. - $args = array( - 'per_page' => 100, - 'page' => absint( $_POST['page'] ), - 'fields' => array( - 'slug' => true, - 'screenshot' => true, - 'description' => true, - 'requires' => true, - 'rating' => true, - 'downloaded' => true, - 'downloadLink' => true, - 'last_updated' => true, - 'homepage' => true, - 'num_ratings' => true, - 'tags' => true, - ), - ); - - // Specialized handling for each query. - switch ( $_POST['theme_action'] ) { - case 'search': - $args['search'] = wp_unslash( $_POST['search'] ); - break; - case 'favorites': - $args['user'] = wp_unslash( $_POST['user'] ); - case 'featured': - case 'popular': - $args['browse'] = wp_unslash( $_POST['theme_action'] ); - break; - case 'latest': - $args['browse'] = 'new'; - break; - case 'feature_filter': - $args['tag'] = wp_unslash( $_POST['tags'] ); - break; - } - - // Load themes from the .org API. - $themes = themes_api( 'query_themes', $args ); - if ( is_wp_error( $themes ) ) { - wp_send_json_error(); - } - - // This list matches the allowed tags in wp-admin/includes/theme-install.php. - $themes_allowedtags = array('a' => array('href' => array(), 'title' => array(), 'target' => array()), - 'abbr' => array('title' => array()), 'acronym' => array('title' => array()), - 'code' => array(), 'pre' => array(), 'em' => array(), 'strong' => array(), - 'div' => array(), 'p' => array(), 'ul' => array(), 'ol' => array(), 'li' => array(), - 'h1' => array(), 'h2' => array(), 'h3' => array(), 'h4' => array(), 'h5' => array(), 'h6' => array(), - 'img' => array('src' => array(), 'class' => array(), 'alt' => array()) - ); - - // Prepare a list of installed themes to check against before the loop. - $installed_themes = array(); - $wp_themes = wp_get_themes(); - foreach ( $wp_themes as $theme ) { - $installed_themes[] = $theme->get_stylesheet(); - } - $update_php = network_admin_url( 'update.php?action=install-theme' ); - foreach ( $themes->themes as &$theme ) { - $theme->install_url = add_query_arg( array( - 'theme' => $theme->slug, - '_wpnonce' => wp_create_nonce( 'install-theme_' . $theme->slug ), - ), $update_php ); - - $theme->name = wp_kses( $theme->name, $themes_allowedtags ); - $theme->author = wp_kses( $theme->author, $themes_allowedtags ); - $theme->version = wp_kses( $theme->version, $themes_allowedtags ); - $theme->description = wp_kses( $theme->description, $themes_allowedtags ); - $theme->tags = implode( ', ', $theme->tags ); - $theme->stars = wp_star_rating( array( 'rating' => $theme->rating, 'type' => 'percent', 'number' => $theme->num_ratings, 'echo' => false ) ); - $theme->num_ratings = number_format_i18n( $theme->num_ratings ); - $theme->preview_url = set_url_scheme( $theme->preview_url ); - - // Handle themes that are already installed as installed themes. - if ( in_array( $theme->slug, $installed_themes, true ) ) { - $theme->type = 'installed'; - } else { - $theme->type = $_POST['theme_action']; - } - - // Set active based on customized theme. - if ( $_POST['customized_theme'] === $theme->slug ) { - $theme->active = true; - } else { - $theme->active = false; - } - - // Map available theme properties to installed theme properties. - $theme->id = $theme->slug; - $theme->screenshot = array( $theme->screenshot_url ); - $theme->authorAndUri = $theme->author; - unset( $theme->slug ); - unset( $theme->screenshot_url ); - unset( $theme->author ); - } // End foreach(). - } // End if(). - wp_send_json_success( $themes ); - } - - /** * Callback for validating the header_textcolor value. * diff --git a/src/wp-includes/customize/class-wp-customize-theme-control.php b/src/wp-includes/customize/class-wp-customize-theme-control.php index 1401d9fec0..fdd8f131da 100644 --- a/src/wp-includes/customize/class-wp-customize-theme-control.php +++ b/src/wp-includes/customize/class-wp-customize-theme-control.php @@ -62,22 +62,18 @@ class WP_Customize_Theme_Control extends WP_Customize_Control { * @access public */ public function content_template() { - /* translators: %s: theme name */ - $details_label = sprintf( __( 'Details for theme: %s' ), '{{ data.theme.name }}' ); - /* translators: %s: theme name */ - $customize_label = sprintf( __( 'Customize theme: %s' ), '{{ data.theme.name }}' ); - /* translators: %s: theme name */ - $preview_label = sprintf( __( 'Live preview theme: %s' ), '{{ data.theme.name }}' ); - /* translators: %s: theme name */ - $install_label = sprintf( __( 'Install and preview theme: %s' ), '{{ data.theme.name }}' ); + $current_url = set_url_scheme( 'http://' . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI'] ); + $active_url = esc_url( remove_query_arg( 'customize_theme', $current_url ) ); + $preview_url = esc_url( add_query_arg( 'customize_theme', '__THEME__', $current_url ) ); // Token because esc_url() strips curly braces. + $preview_url = str_replace( '__THEME__', '{{ data.theme.id }}', $preview_url ); ?> - <# if ( data.theme.active ) { #> -
    + <# if ( data.theme.isActiveTheme ) { #> +
    <# } else { #> -
    +
    <# } #> - <# if ( data.theme.screenshot && data.theme.screenshot[0] ) { #> + <# if ( data.theme.screenshot[0] ) { #>
    @@ -85,34 +81,25 @@ class WP_Customize_Theme_Control extends WP_Customize_Control {
    <# } #> - - - - <# if ( 'installed' === data.theme.type && data.theme.hasUpdate ) { #> -

    ' . __( 'Update now' ) . '' ); ?>

    + <# if ( data.theme.isActiveTheme ) { #> + + <# } else { #> + <# } #> - <# if ( data.theme.active ) { #> -

    +
    + + <# if ( data.theme.isActiveTheme ) { #> +

    Current: %s' ), '{{ data.theme.name }}' ); + printf( __( 'Active: %s' ), '{{{ data.theme.name }}}' ); ?>

    -
    - -
    -

    - <# } else if ( 'installed' === data.theme.type ) { #> -

    {{ data.theme.name }}

    -
    -
    -

    <# } else { #> -

    {{ data.theme.name }}

    +

    {{{ data.theme.name }}}

    - +
    <# } #>
    diff --git a/src/wp-includes/customize/class-wp-customize-themes-panel.php b/src/wp-includes/customize/class-wp-customize-themes-panel.php deleted file mode 100644 index 4d83e3078b..0000000000 --- a/src/wp-includes/customize/class-wp-customize-themes-panel.php +++ /dev/null @@ -1,113 +0,0 @@ - -
  • -

    - manager->is_theme_active() ) { - echo '' . __( 'Active theme' ) . ' {{ data.title }}'; - } else { - echo '' . __( 'Previewing theme' ) . ' {{ data.title }}'; - } - ?> - - - - -

    -
      -
    • - -
    • - 0' ); - ?> - - -
    • -
    • - -
      - ' . __( 'Themes' ) . '' ); // Separate strings for consistency with other panels. - ?> - - <# if ( data.description ) { #> - - <# } #> - -
      - - <# if ( data.description ) { #> -
      - {{{ data.description }}} -
      - <# } #> - -
    • -
    • -
    • -
        -
      • -
      -
    • - action; - $exported['text_before'] = $this->text_before; - - return $exported; - } - - /** - * Render a themes section as a JS template. - * - * The template is only rendered by PHP once, so all actions are prepared at once on the server side. - * - * @since 4.7.0 + * @since 4.2.0 * @access protected */ - protected function render_template() { + protected function render() { + $classes = 'accordion-section control-section control-section-' . $this->type; ?> -
    • - <# if ( '' !== data.text_before ) { #> -

      {{ data.text_before }}

      - <# } #> - <# if ( 'search' === data.action ) { #> -
      - - - -
      - <# } else { #> - <# if ( 'favorites' === data.action || 'feature_filter' === data.action ) { - var attr = ' aria-expanded="false"'; +
    • +

      + manager->is_theme_active() ) { + echo '' . __( 'Active theme' ) . ' ' . $this->title; } else { - var attr = ''; - } #> - - <# } #> - - <# if ( 'installed' === data.action ) { #> -

      - -

      - <# } #> - - <# if ( 'favorites' === data.action ) { #> -
      -

      -

      - - - -

      -
      - <# } else if ( 'feature_filter' === data.action ) { #> -
      + echo '' . __( 'Previewing theme' ) . ' ' . $this->title; + } + ?> + + controls ) > 0 ) : ?> + + +

      +
      +

      + + + controls ) + 1 /* Active theme */; ?> +

      +

      $features ) { - echo '
      '; - $feature_name = esc_html( $feature_name ); - echo ''; - echo '
      '; - foreach ( $features as $feature => $feature_name ) { - $feature = esc_attr( $feature ); - echo ' '; - echo '
      '; - } - echo '
      '; - echo '
      '; + if ( $this->manager->is_theme_active() ) { + echo '' . __( 'Active theme' ) . ' ' . $this->title; + } else { + echo '' . __( 'Previewing theme' ) . ' ' . $this->title; } ?> -

      - <# } #> -
      + + + + +
      + controls ) > 4 ) : ?> +

      +
      - -
        +
        -

        -

    • diff --git a/tests/phpunit/tests/customize/manager.php b/tests/phpunit/tests/customize/manager.php index f21622647d..a28d83400b 100644 --- a/tests/phpunit/tests/customize/manager.php +++ b/tests/phpunit/tests/customize/manager.php @@ -1506,7 +1506,7 @@ class Tests_WP_Customize_Manager extends WP_UnitTestCase { $data = json_decode( $json, true ); $this->assertNotEmpty( $data ); - $this->assertEqualSets( array( 'theme', 'url', 'browser', 'panels', 'sections', 'nonce', 'autofocus', 'documentTitleTmpl', 'previewableDevices', 'changeset', 'timeouts', 'l10n' ), array_keys( $data ) ); + $this->assertEqualSets( array( 'theme', 'url', 'browser', 'panels', 'sections', 'nonce', 'autofocus', 'documentTitleTmpl', 'previewableDevices', 'changeset', 'timeouts' ), array_keys( $data ) ); $this->assertEquals( $autofocus, $data['autofocus'] ); $this->assertArrayHasKey( 'save', $data['nonce'] ); $this->assertArrayHasKey( 'preview', $data['nonce'] );