Make WordPress Core

Changeset 39140


Ignore:
Timestamp:
11/04/2016 03:53:01 PM (10 years ago)
Author:
helen
Message:

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.

Location:
trunk
Files:
1 deleted
11 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/wp-admin/css/customize-controls.css

    r38948 r39140  
    309309
    310310#customize-theme-controls .customize-pane-child.open,
    311 #customize-theme-controls .customize-pane-child.current-panel {
     311#customize-theme-controls .customize-pane-child.current-panel,
     312#customize-theme-controls .customize-themes-panel.customize-pane-child.current-panel {
    312313        -webkit-transform: none;
    313314        -ms-transform: none;
    … …  
    315316}
    316317
     318#customize-theme-controls .customize-themes-panel.customize-pane-child,
    317319.section-open #customize-theme-controls .customize-pane-parent,
    318320.in-sub-panel #customize-theme-controls .customize-pane-parent,
    319321.section-open #customize-info,
    320322.in-sub-panel #customize-info,
    321 .in-sub-panel.section-open #customize-theme-controls .customize-pane-child.current-panel {
     323.in-sub-panel.section-open #customize-theme-controls .customize-pane-child.current-panel,
     324.in-themes-panel #customize-theme-controls .customize-pane-parent,
     325.in-themes-panel #customize-info {
    322326        visibility: hidden;
    323327        height: 0;
    … …  
    330334.section-open #customize-theme-controls .customize-pane-parent.busy,
    331335.in-sub-panel #customize-theme-controls .customize-pane-parent.busy,
     336.in-themes-panel #customize-theme-controls .customize-pane-parent.busy,
    332337.section-open #customize-info.busy,
    333338.in-sub-panel #customize-info.busy,
     339.in-themes-panel #customize-info.busy,
    334340.busy.section-open.in-sub-panel #customize-theme-controls .customize-pane-child.current-panel,
    335341#customize-theme-controls .customize-pane-child.open,
    … …  
    339345        height: auto;
    340346        overflow: auto;
     347}
     348
     349.in-themes-panel #customize-theme-controls .customize-pane-parent,
     350.in-themes-panel #customize-info {
     351        -webkit-transform: translateX(100%);
     352        -ms-transform: translateX(100%);
     353        transform: translateX(100%);
    341354}
    342355
    … …  
    11511164}
    11521165
    1153 #customize-theme-controls .control-panel-themes {
    1154         border-bottom: none;
    1155 }
    1156 
    1157 #customize-theme-controls .control-panel-themes > .accordion-section-title:hover, /* Not a focusable element. */
    1158 #customize-theme-controls .control-panel-themes > .accordion-section-title {
     1166#customize-theme-controls .control-section-themes .accordion-section-title:hover, /* Not a focusable element. */
     1167#customize-theme-controls .control-section-themes .accordion-section-title {
    11591168        cursor: default;
    11601169        background: #fff;
    … …  
    11631172        border-bottom: 1px solid #ddd;
    11641173        border-left: none;
    1165         border-right: none;
    1166         margin: 0 0 15px 0;
    1167         padding-right: 100px; /* Space for the button */
     1174        margin-top: 0;
    11681175}
    11691176
    … …  
    11731180}
    11741181
    1175 .control-panel-themes .accordion-section-title span.customize-action,
     1182#customize-theme-controls .control-section-themes > .accordion-section-title:hover, /* Not a focusable element. */
     1183#customize-theme-controls .control-section-themes > .accordion-section-title {
     1184        margin: 0 0 15px;
     1185}
     1186
     1187#customize-controls .customize-themes-panel .accordion-section-title {
     1188        margin: 15px -8px;
     1189}
     1190
     1191#customize-controls .control-section-themes .accordion-section-title,
     1192#customize-controls .customize-themes-panel .accordion-section-title {
     1193        padding-right: 100px; /* Space for the button */
     1194}
     1195
     1196#customize-controls .control-section-themes .accordion-section-title span.customize-action,
    11761197#customize-controls .customize-section-title span.customize-action {
    11771198        font-size: 13px;
    … …  
    11801201}
    11811202
    1182 .control-panel-themes .accordion-section-title .change-theme {
     1203#customize-controls .control-section-themes .accordion-section-title .change-theme,
     1204#customize-controls .customize-themes-panel .accordion-section-title .customize-theme {
    11831205        position: absolute;
    11841206        right: 10px;
    … …  
    11881210}
    11891211
    1190 #customize-theme-controls .control-panel-themes > .accordion-section-title:after {
     1212#customize-controls .control-section-themes .accordion-section-title:before {
    11911213        display: none;
    11921214}
    11931215
    1194 .control-panel-themes .customize-themes-full-container {
    1195         position: fixed;
    1196         top: 0;
    1197         left: 0;
    1198         -webkit-transition: .18s left ease-in-out;
    1199         transition: .18s left ease-in-out;
    1200         margin: 0 0 0 300px;
    1201         padding:25px;
    1202         overflow-y: scroll;
    1203         width: -webkit-calc(100% - 350px);
    1204         width: calc(100% - 350px);
    1205         height: -webkit-calc(100% - 50px);
    1206         height: calc(100% - 50px);
    1207         background: #eee;
    1208         z-index: 20;
    1209 }
    1210 
    1211 /* Animations for opening the themes panel */
    1212 #customize-header-actions .save,
    1213 #customize-header-actions .spinner,
    1214 #customize-header-actions .customize-controls-preview-toggle {
    1215         position: relative;
    1216         top: 0;
    1217         -webkit-transition: .18s top ease-in-out;
    1218         transition: .18s top ease-in-out;
    1219 }
    1220 
    1221 #customize-footer-actions,
    1222 #customize-footer-actions .collapse-sidebar {
    1223         bottom: 0;
    1224         -webkit-transition: .18s bottom ease-in-out;
    1225         transition: .18s bottom ease-in-out;
    1226 }
    1227 
    1228 .in-themes-panel:not(.animating) #customize-header-actions .save,
    1229 .in-themes-panel:not(.animating) #customize-header-actions .spinner,
    1230 .in-themes-panel:not(.animating) #customize-header-actions .customize-controls-preview-toggle,
    1231 .in-themes-panel:not(.animating) #customize-preview,
    1232 .in-themes-panel:not(.animating) #customize-footer-actions {
    1233         visibility: hidden;
    1234 }
    1235 
    1236 .wp-full-overlay.in-themes-panel {
    1237         background: #eee; /* Prevents a black flash when fading in the panel */
    1238 }
    1239 
    1240 .in-themes-panel #customize-header-actions .save,
    1241 .in-themes-panel #customize-header-actions .spinner,
    1242 .in-themes-panel #customize-header-actions .customize-controls-preview-toggle {
    1243         top: -45px;
    1244 }
    1245 
    1246 .in-themes-panel #customize-footer-actions,
    1247 .in-themes-panel #customize-footer-actions .collapse-sidebar {
    1248         bottom: -45px;
    1249 }
    1250 
    1251 /* Don't show the theme count while the panel opens, as it's in the wrong place during the animation */
    1252 .in-themes-panel.animating .control-panel-themes .filter-themes-count {
    1253         display: none;
    1254 }
    1255 
    1256 .in-themes-panel.wp-full-overlay .wp-full-overlay-sidebar-content {
    1257         bottom: 0;
    1258 }
    1259 
    1260 /* Adds a delay before fading in to avoid it "jumping" */
    1261 @-webkit-keyframes themes-fade-in {
    1262         0% {
    1263                 opacity: 0;
    1264         }
    1265         50% {
    1266                 opacity: 0;
    1267         }
    1268         100% {
    1269                 opacity: 1;
    1270         }
    1271 }
    1272 @keyframes themes-fade-in {
    1273         0% {
    1274                 opacity: 0;
    1275         }
    1276         50% {
    1277                 opacity: 0;
    1278         }
    1279         100% {
    1280                 opacity: 1;
    1281         }
    1282 }
    1283 
    1284 .control-panel-themes .customize-themes-full-container.animate {
    1285         -webkit-animation: .6s themes-fade-in 1;
    1286         animation: .6s themes-fade-in 1;
    1287 }
    1288 
    1289 .in-themes-panel:not(.animating) .control-panel-themes .filter-themes-count {
    1290         -webkit-animation: .6s themes-fade-in 1;
    1291         animation: .6s themes-fade-in 1;
    1292 }
    1293 
    1294 .control-panel-themes .filter-themes-count {
    1295         position: fixed;
    1296         top: 0;
    1297         left: 48px;
    1298         width: 222px;
    1299         padding: 6px 15px;
    1300         margin: 0;
    1301         line-height: 32px;
    1302         text-align: right;
    1303         z-index: 10;
    1304 }
    1305 
    1306 .control-panel-themes .filter-themes-count .themes-displayed {
    1307         font-weight: 600;
    1308         color: #555d66;
    1309 }
    1310 
    1311 .control-panel-themes .filter-themes-count .see-themes,
    1312 .control-panel-themes .filter-themes-count .filter-themes {
    1313         display: none;
    1314 }
    1315 
    1316 
    1317 /* Mobile - toggle between themes and filters */
    1318 @media screen and (max-width:600px) {
    1319 
    1320         /* Show a spinner in the filters view also, reusing the main customize spinner */
    1321         .in-themes-panel.loading #customize-header-actions .spinner {
    1322                 position: fixed;
    1323                 top: 0;
    1324                 left: 48px;
    1325                 visibility: visible;
    1326         }
    1327 
    1328         .in-themes-panel.loading.showing-themes #customize-header-actions .spinner {
    1329                 visibility: hidden;
    1330         }
    1331 
    1332         .control-panel-themes .filter-themes-count {
    1333                 width: -webkit-calc(100% - 93px);
    1334                 width: calc(100% - 93px);
    1335         }
    1336 
    1337         .control-panel-themes .filter-themes-count .themes-displayed {
    1338                 display: none;
    1339         }
    1340 
    1341         .wp-full-overlay:not(.showing-themes) .control-panel-themes .filter-themes-count .see-themes {
    1342                 display: block;
    1343                 float: right;
    1344         }
    1345 
    1346         .wp-full-overlay.showing-themes .control-panel-themes .filter-themes-count .filter-themes {
    1347                 display: block;
    1348                 float: right;
    1349         }
    1350 
    1351         .in-themes-panel.showing-themes .control-panel-themes .customize-panel-back {
    1352                 position: fixed;
    1353                 top: 0;
    1354                 left: 0;
    1355                 z-index: 10;
    1356                 height: 45px;
    1357                 background: #eee;
    1358         }
    1359 
    1360         .in-themes-panel.showing-themes .control-panel-themes .customize-panel-back:before {
    1361                 line-height: 45px;
    1362         }
    1363 
    1364         .control-panel-themes .customize-themes-full-container {
    1365                 width: -webkit-calc(100% - 50px);
    1366                 width: calc(100% - 50px);
    1367                 margin: 0;
    1368                 top: 46px;
    1369                 height: -webkit-calc(100% - 96px);
    1370                 height: calc(100% - 96px);
    1371                 z-index: 1;
    1372                 display: none;
    1373         }
    1374 
    1375         .showing-themes .control-panel-themes .customize-themes-full-container {
    1376                 display: block;
    1377         }
    1378 }
    1379 
    1380 .control-panel-themes .customize-themes-notifications .notice {
    1381         margin: 0 0 25px 0;
    1382 }
    1383 
    1384 .customize-themes-full-container .customize-themes-section {
    1385         display: none !important; /* There is unknown JS that perpetually tries to show all theme sections when more items are added. */
    1386         overflow: hidden;
    1387 }
    1388 
    1389 .customize-themes-full-container .customize-themes-section.current-section {
    1390         display: list-item !important; /* There is unknown JS that perpetually tries to show all theme sections when more items are added. */
    1391 }
    1392 
    1393 .theme-section .customize-themes-text-before {
    1394         padding: 0 0 8px 15px;
    1395         margin: 15px 0 0 0;
    1396         line-height: 16px;
    1397         border-bottom: 1px solid #ddd;
    1398         color: #555d66;
    1399 }
    1400 
    1401 .control-panel-themes .customize-themes-section-title {
    1402         width: 100%;
    1403         background: #fff;
    1404         -webkit-box-shadow: none;
    1405         box-shadow: none;
    1406         outline: none;
    1407         border-top: none;
    1408         border-bottom: 1px solid #ddd;
    1409         border-left: 4px solid #fff;
    1410         border-right: none;
    1411         cursor: pointer;
    1412         padding: 10px 15px;
    1413         position: relative;
    1414         text-align: left;
    1415         font-size: 14px;
    1416         font-weight: 600;
    1417         color: #555d66;
    1418         text-shadow: none;
    1419 }
    1420 
    1421 .control-panel-themes .theme-section {
    1422         margin: 0;
    1423         position: relative;
    1424 }
    1425 
    1426 .control-panel-themes .customize-themes-section-title:focus,
    1427 .control-panel-themes .customize-themes-section-title:hover {
    1428         border-left-color: #0073aa;
    1429         color: #0073aa;
    1430         background: #f5f5f5;
    1431 }
    1432 
    1433 .control-panel-themes .theme-section .customize-themes-section-title.selected:after {
    1434         content: "\f147";
    1435         font: 16px/1 dashicons;
     1216#customize-controls .customize-themes-panel {
     1217        padding: 0 8px;
     1218        background: #f1f1f1;
    14361219        -webkit-box-sizing: border-box;
    14371220        -moz-box-sizing: border-box;
    14381221        box-sizing: border-box;
    1439         width: 20px;
    1440         height: 20px;
    1441         padding: 3px 3px 1px 1px; /* Re-align the icon to the smaller grid */
    1442         -webkit-border-radius: 100%;
    1443         border-radius: 100%;
    1444         position: absolute;
    1445         top: 9px;
    1446         right: 15px;
    1447         background: #0073aa;
    1448         color: #fff;
    1449 }
    1450 
    1451 .control-panel-themes .customize-themes-section-title.selected {
    1452         color: #0073aa;
    1453 }
    1454 
    1455 .control-panel-themes .customize-themes-section-title.themes-section-search_themes {
    1456         border-left: none;
    1457         padding: 5px 10px 5px 15px;
    1458         width: auto;
    1459 }
    1460 
    1461 .control-panel-themes .customize-themes-section-title.themes-section-feature_filter_themes:after,
    1462 .control-panel-themes .customize-themes-section-title.themes-section-favorites_themes:after {
    1463         content: "\f140";
    1464         font: 20px/1 dashicons;
    1465         position: absolute;
    1466         right: 15px;
    1467         top: 8px;
    1468 }
    1469 
    1470 .control-panel-themes .customize-themes-section-title.themes-section-search_themes .wp-filter-search {
    1471         width: 100%;
    1472 }
    1473 
    1474 .control-panel-themes .customize-themes-section-title.themes-section-search_themes.selected,
    1475 .control-panel-themes .customize-themes-section-title.themes-section-search_themes:hover {
    1476         background: #fff;
    1477         cursor: default;
    1478 }
    1479 
    1480 .control-panel-themes .customize-themes-section-title.themes-section-feature_filter_themes {
    1481         margin-top: 15px;
    1482         border-top: 1px solid #ddd;
    1483 }
    1484 
    1485 .control-panel-themes .filter-details {
    1486         background: #f5f5f5;
    1487         margin: 0;
    1488         padding: 8px 15px;
    1489         border-top: none;
    1490         border-bottom: 1px solid #ddd;
    1491         display: none;
    1492 }
    1493 
    1494 .control-panel-themes .customize-themes-section-title.selected.details-open {
    1495         border-bottom-color: #f5f5f5;
    1496     border-left-color: #f5f5f5;
    1497         background: #f5f5f5;
    1498 }
    1499 
    1500 .control-panel-themes .favorites-form.filter-details label {
    1501         padding-bottom: 6px;
    1502         display: inline-block;
    1503 }
    1504 
    1505 .control-panel-themes .filter-details .filter-group {
    1506         float: none;
    1507         width: 100%;
     1222}
     1223
     1224#customize-controls .customize-themes-panel .accordion-section-title:first-child {
     1225        margin-top: 0;
     1226}
     1227
     1228#customize-controls .customize-themes-panel .accordion-section-title:nth-child(2) {
     1229        font-size: 14px;
     1230        font-weight: 600;
     1231}
     1232
     1233#customize-controls .customize-themes-panel > h2 {
     1234        padding: 15px 8px 0 8px;
     1235}
     1236
     1237#customize-theme-controls .customize-themes-panel .accordion-section-content {
    15081238        background: transparent;
    1509         border: none;
    1510         padding: 0;
    1511         -webkit-box-shadow: none;
    1512         box-shadow: none;
    1513 }
    1514 
    1515 .control-panel-themes .filter-details .filter-group legend button {
    1516         padding: 18px 15px 8px 10px;
    1517         line-height: 14px;
    1518         border-bottom: 1px solid #ddd;
    1519         width: 100%;
    1520         text-align: left;
    1521 }
    1522 
    1523 .control-panel-themes .filter-details .filter-group legend {
    1524         position: relative;
    1525         top: 0;
    1526         width: 100%;
    1527 }
    1528 
    1529 .control-panel-themes .filter-details .filter-group legend button:after {
    1530         content: "\f140";
    1531         font: 20px/1 dashicons;
    1532         position: absolute;
    1533         bottom: 6px;
    1534         right: 5px;
    1535 }
    1536 
    1537 .control-panel-themes .filter-details .filter-group legend button:hover,
    1538 .control-panel-themes .filter-details .filter-group legend button:focus {
    1539         color: #0073aa;
    1540         border-bottom-color: #0073aa; /* Color change for focus style should be acceptable because border-bottom is barely visible previously. */
    1541         outline: none;
    1542         -webkit-box-shadow: none;
    1543         box-shadow: none;
    1544 }
    1545 
    1546 .control-panel-themes .filter-details .filter-group legend button.open:after {
    1547         content: "\f142";
    1548 }
    1549 
    1550 .control-panel-themes .filter-details .filter-group .filter-group-feature {
    1551         display: none;
    1552         margin: 0;
    1553 }
    1554 
    1555 .control-panel-themes .filter-details .filter-group-feature label {
    1556         border: 1px solid #ddd;
    1557         border-top: 0;
    1558         background: #fff;
    1559         color: #555d66;
    1560         margin: 0;
    1561         padding: 12px 10px 12px 34px;
    1562         width: -webkit-calc(100% - 46px);
    1563         width: calc(100% - 46px);
    1564         line-height: 16px;
    1565         font-weight: 600;
    1566 }
    1567 
    1568 .control-panel-themes .filter-details .filter-group-feature input {
    1569         position: absolute;
    1570         margin: 12px 10px;
    1571 }
    1572 
    1573 .control-panel-themes .filter-details .filter-group-feature label:hover {
    1574         color: #0073aa;
     1239        display: block;
     1240}
     1241
     1242.customize-control.customize-control-theme {
     1243        margin-bottom: 8px;
    15751244}
    15761245
    … …  
    15821251}
    15831252
    1584 .loading .customize-themes-section .spinner {
    1585         display: block;
    1586         visibility: visible;
    1587         position: relative;
    1588         clear: both;
    1589         width: 20px;
    1590         height: 20px;
    1591         left: -webkit-calc(50% - 10px);
    1592         left: calc(50% - 10px);
    1593         float: none;
    1594         margin-top: 50px;
    1595 }
    1596 
    1597 .customize-themes-section .filter-drawer {
    1598         border-top: none;
    1599         display: block;
    1600         background: transparent;
    1601         padding-top: 5px;
    1602 }
    1603 
    1604 .customize-themes-section .clear-filters {
    1605         margin-left: 8px;
    1606         display: none;
    1607 }
    1608 
    1609 .customize-themes-section .no-themes {
    1610         display: none;
    1611 }
    1612 
    1613 .themes-section-installed_themes .theme .notice-success {
    1614         display: none; /* Hide "installed" notice on installed themes tab. */
    1615 }
    1616 
    1617 .control-panel-themes .theme-browser .theme .theme-actions .button-primary {
    1618         margin: 0 0 0 8px;
    1619 }
    1620 
    1621 .customize-control-theme .theme {
    1622         width: 100%;
    1623         margin: 0;
    1624 }
    1625 
    1626 .customize-control.customize-control-theme { /* override most properties on .customize-control */
    1627         -webkit-box-sizing: border-box;
    1628         -moz-box-sizing: border-box;
    1629         box-sizing: border-box;
    1630         width: 18.4%;
    1631         margin: 0 2% 2% 0;
    1632         padding: 0;
    1633         clear: none;
    1634 }
    1635 
    1636 /* 5 columns above 2100px */
    1637 @media screen and (min-width: 2101px) {
    1638         .customize-control.customize-control-theme:nth-child(5n) {
    1639                 margin-right: 0;
    1640         }
    1641 }
    1642 
    1643 /* 4 columns up to 2100px */
    1644 @media screen and (min-width: 1601px) and (max-width: 2100px) {
    1645         .customize-control.customize-control-theme {
    1646                 width: 23.5%;
    1647         }
    1648 
    1649         .customize-control.customize-control-theme:nth-child(4n) {
    1650                 margin-right: 0;
    1651         }
    1652 }
    1653 
    1654 /* 3 columns up to 1600px */
    1655 @media screen and (min-width: 1201px) and (max-width: 1600px) {
    1656         .customize-control.customize-control-theme {
    1657                 width: 32%;
    1658         }
    1659 
    1660         .customize-control.customize-control-theme:nth-child(3n) {
    1661                 margin-right: 0;
    1662         }
    1663 }
    1664 
    1665 /* 2 columns up to 1200px */
    1666 @media screen and (min-width: 851px) and (max-width: 1200px) {
    1667         .customize-control.customize-control-theme {
    1668                 width: 49%;
    1669         }
    1670 
    1671         .customize-control.customize-control-theme:nth-child(even) {
    1672                 margin-right: 0;
    1673         }
    1674 }
    1675 
    1676 /* 1 column up to 850 px */
    1677 @media screen and (max-width: 850px) {
    1678         .customize-control.customize-control-theme {
    1679                 width: 100%;
    1680                 margin: 0 0 3% 0;
    1681         }
    1682 }
    1683 
    16841253.wp-customizer .theme-browser .themes {
    16851254        padding-bottom: 8px;
    16861255}
    16871256
     1257.wp-customizer .theme-browser .theme {
     1258        margin: 0;
     1259        width: 100%;
     1260}
     1261
    16881262.wp-customizer .theme-browser .theme .theme-actions {
     1263        -ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=100)";
    16891264        opacity: 1;
    16901265}
    … …  
    17031278        line-height: 1.5;
    17041279        width: 100%;
     1280}
     1281
     1282.control-section-themes .accordion-section-title:after,
     1283.customize-themes-panel .accordion-section-title:after {
     1284        display: none;
     1285}
     1286
     1287.customize-themes-panel.control-panel-content {
     1288        border-top: 1px solid #ddd;
    17051289}
    17061290
    … …  
    17191303}
    17201304
    1721 /* Avoid a z-index war by resetting elements that should be under the overlay.
    1722    This is likely required because of the way that sections and panels are positioned. */
    1723 .wp-customizer.modal-open #customize-header-actions,
    1724 .wp-customizer.modal-open .control-panel-themes .filter-themes-count,
    1725 .wp-customizer.modal-open .control-panel-themes .customize-themes-section-title.selected:after {
    1726         z-index: -1;
    1727 }
    1728 
    17291305.wp-customizer .theme-overlay .theme-backdrop {
    17301306        background: rgba( 238, 238, 238, 0.75 );
    17311307        position: fixed;
    17321308        z-index: 110;
    1733 }
    1734 
    1735 .wp-customizer .theme-overlay .star-rating {
    1736         float: left;
    1737         margin-right: 8px;
    1738 }
    1739 
    1740 .wp-customizer .theme-rating .num-ratings {
    1741         line-height: 20px;
    17421309}
    17431310
    … …  
    17481315        bottom: 45px;
    17491316        z-index: 120;
     1317        max-width: 1740px; /* To ensure that theme screenshots are not displayed larger than 880px wide. */
    17501318}
    17511319
    17521320.wp-customizer .theme-overlay .theme-actions {
    1753         text-align: right; /* Because there're only one or two actions, match the UI pattern of media modals and right-align the action. */
    1754         padding: 10px 15px;
    1755 }
    1756 
    1757 .wp-customizer .theme-overlay .theme-actions .theme-install.preview {
    1758         margin-left: 8px;
    1759 }
    1760 
    1761 .control-panel-themes .theme-actions .delete-theme {
    1762         left: 15px; /* these override themes.css on mobile */
    1763         right: auto;
    1764         bottom: auto;
    1765         position: absolute;
    1766 }
    1767 
    1768 .modal-open .in-themes-panel #customize-controls .wp-full-overlay-sidebar-content {
    1769         overflow: visible; /* Prevent the top-level Customizer controls from becoming visible when elements on the right of the details modal are focused. */
    1770 }
    1771 
     1321        text-align: right; /* Because there's only one action, match the pattern of media modals and right-align the action. */
     1322}
     1323
     1324.ie8 .wp-customizer .theme-overlay .theme-header,
     1325.ie8 .wp-customizer .theme-overlay .theme-about,
     1326.ie8 .wp-customizer .theme-overlay .theme-actions {
     1327        position: static;
     1328}
    17721329
    17731330/* Small Screens */
  • trunk/src/wp-admin/css/themes.css

    r38948 r39140  
    571571        margin: 0 30px 0 0;
    572572        width: 55%;
    573         max-width: 1200px; /* Recommended theme screenshot width, set here to avoid stretching */
     573        max-width: 880px;
    574574        text-align: center;
    575575}
  • trunk/src/wp-admin/customize.php

    r39132 r39140  
    110110
    111111<script type="text/javascript">
    112 var ajaxurl = <?php echo wp_json_encode( admin_url( 'admin-ajax.php', 'relative' ) ); ?>,
    113     pagenow = 'customize';
     112var ajaxurl = <?php echo wp_json_encode( admin_url( 'admin-ajax.php', 'relative' ) ); ?>;
    114113</script>
    115114
  • trunk/src/wp-admin/includes/theme.php

    r38951 r39140  
    610610 */
    611611function customize_themes_print_templates() {
     612        $preview_url = esc_url( add_query_arg( 'theme', '__THEME__' ) ); // Token because esc_url() strips curly braces.
     613        $preview_url = str_replace( '__THEME__', '{{ data.id }}', $preview_url );
    612614        ?>
    613615        <script type="text/html" id="tmpl-customize-themes-details-view">
    … …  
    621623                        <div class="theme-about wp-clearfix">
    622624                                <div class="theme-screenshots">
    623                                 <# if ( data.screenshot && data.screenshot[0] ) { #>
     625                                <# if ( data.screenshot[0] ) { #>
    624626                                        <div class="screenshot"><img src="{{ data.screenshot[0] }}" alt="" /></div>
    625627                                <# } else { #>
    … …  
    634636                                        <h2 class="theme-name">{{{ data.name }}}<span class="theme-version"><?php printf( __( 'Version: %s' ), '{{ data.version }}' ); ?></span></h2>
    635637                                        <h3 class="theme-author"><?php printf( __( 'By %s' ), '{{{ data.authorAndUri }}}' ); ?></h3>
    636 
    637                                         <# if ( data.stars && 0 != data.num_ratings ) { #>
    638                                                 <div class="theme-rating">
    639                                                         {{{ data.stars }}}
    640                                                         <span class="num-ratings"><?php echo sprintf( __( '(%s ratings)' ), '{{ data.num_ratings }}' ); ?></span>
    641                                                 </div>
    642                                         <# } #>
    643 
    644                                         <# if ( data.hasUpdate ) { #>
    645                                                 <div class="notice notice-warning notice-alt notice-large" data-slug="{{ data.id }}">
    646                                                         <h3 class="notice-title"><?php _e( 'Update Available' ); ?></h3>
    647                                                         {{{ data.update }}}
    648                                                 </div>
    649                                         <# } #>
    650 
    651638                                        <p class="theme-description">{{{ data.description }}}</p>
    652639
    … …  
    654641                                                <p class="parent-theme"><?php printf( __( 'This is a child theme of %s.' ), '<strong>{{{ data.parent }}}</strong>' ); ?></p>
    655642                                        <# } #>
     643
    656644                                        <# if ( data.tags ) { #>
    657                                                 <p class="theme-tags"><span><?php _e( 'Tags:' ); ?></span> {{{ data.tags }}}</p>
     645                                                <p class="theme-tags"><span><?php _e( 'Tags:' ); ?></span> {{ data.tags }}</p>
    658646                                        <# } #>
    659647                                </div>
    660648                        </div>
    661649
    662                         <div class="theme-actions">
    663                                 <# if ( data.active ) { #>
    664                                         <button type="button" class="button button-primary customize-theme"><?php _e( 'Customize' ); ?></a>
    665                                 <# } else if ( 'installed' === data.type ) { #>
    666                                         <?php if ( current_user_can( 'delete_themes' ) ) { ?>
    667                                                 <# if ( data.actions && data.actions['delete'] ) { #>
    668                                                         <a href="{{{ data.actions['delete'] }}}" data-slug="{{ data.id }}" class="button button-secondary delete-theme"><?php _e( 'Delete' ); ?></a>
    669                                                 <# } #>
    670                                         <?php } ?>
    671                                         <button type="button" class="button button-primary preview-theme" data-slug="{{ data.id }}"><?php _e( 'Live Preview' ); ?></span>
    672                                 <# } else { #>
    673                                         <button type="button" class="button theme-install" data-slug="{{ data.id }}"><?php _e( 'Install' ); ?></button>
    674                                         <button type="button" class="button button-primary theme-install preview" data-slug="{{ data.id }}"><?php _e( 'Install & Preview' ); ?></button>
    675                                 <# } #>
    676                         </div>
     650                        <# if ( ! data.active ) { #>
     651                                <div class="theme-actions">
     652                                        <div class="inactive-theme">
     653                                                <?php
     654                                                /* translators: %s: Theme name */
     655                                                $aria_label = sprintf( __( 'Preview %s' ), '{{ data.name }}' );
     656                                                ?>
     657                                                <a href="<?php echo $preview_url; ?>" target="_top" class="button button-primary" aria-label="<?php echo esc_attr( $aria_label ); ?>"><?php _e( 'Live Preview' ); ?></a>
     658                                        </div>
     659                                </div>
     660                        <# } #>
    677661                </div>
    678662        </script>
  • trunk/src/wp-admin/includes/update-core.php

    r39064 r39140  
    711711'wp-includes/locale.php',
    712712'wp-includes/session.php',
     713'wp-includes/customize/class-wp-customize-themes-panel.php', // Removed in beta; when the feature comes back remember to remove it from this array. See #37661.
    713714);
    714715
  • trunk/src/wp-admin/js/customize-controls.js

    r39131 r39140  
    1 /* global _wpCustomizeHeader, _wpCustomizeBackground, _wpMediaViewsL10n, MediaElementPlayer, console */
     1/* global _wpCustomizeHeader, _wpCustomizeBackground, _wpMediaViewsL10n, MediaElementPlayer */
    22(function( exports, $ ){
    33        var Container, focus, normalizedTransitionendEventName, api = wp.customize;
    … …  
    865865                                container = $( '#customize-theme-controls' );
    866866
    867                         // Watch for changes to the panel state.
     867                        // Watch for changes to the panel state
    868868                        inject = function ( panelId ) {
    869869                                var parentContainer;
    870870                                if ( panelId ) {
    871                                         // The panel has been supplied, so wait until the panel object is registered.
     871                                        // The panel has been supplied, so wait until the panel object is registered
    872872                                        api.panel( panelId, function ( panel ) {
    873                                                 // The panel has been registered, wait for it to become ready/initialized.
     873                                                // The panel has been registered, wait for it to become ready/initialized
    874874                                                panel.deferred.embedded.done( function () {
    875875                                                        parentContainer = panel.contentContainer;
    … …  
    896896                        };
    897897                        section.panel.bind( inject );
    898                         inject( section.panel.get() ); // Since a section may never get a panel, assume that it won't ever get one.
     898                        inject( section.panel.get() ); // Since a section may never get a panel, assume that it won't ever get one
    899899                },
    900900
    … …  
    10631063         * wp.customize.ThemesSection
    10641064         *
    1065          * Custom section for themes that loads themes by category, and also
    1066          * handles the theme-details view rendering and navigation.
     1065         * Custom section for themes that functions similarly to a backwards panel,
     1066         * and also handles the theme-details view rendering and navigation.
    10671067         *
    10681068         * @constructor
    … …  
    10761076                screenshotQueue: null,
    10771077                $window: $( window ),
    1078                 loaded: 0,
    1079                 loading: false,
    1080                 fullyLoaded: false,
    1081                 term: '',
    1082                 filterContainer: $(),
    1083 
    1084                 /**
    1085                  * Embed the section in the DOM when the themes panel is ready.
    1086                  *
    1087                  * Insert the section before the themes container. Assume that a themes section is within a panel, but not necessarily the themes panel.
    1088                  *
    1089                  * @since 4.7.0
    1090                  */
    1091                 embed: function () {
    1092                         var inject,
    1093                                 section = this,
    1094                                 container = $( '#customize-theme-controls' );
    1095 
    1096                         // Watch for changes to the panel state
    1097                         inject = function ( panelId ) {
    1098                                 var parentContainer;
    1099                                 api.panel( panelId, function ( panel ) {
    1100                                         // The panel has been registered, wait for it to become ready/initialized
    1101                                         panel.deferred.embedded.done( function () {
    1102                                                 parentContainer = panel.contentContainer;
    1103                                                 if ( ! section.headContainer.parent().is( parentContainer ) ) {
    1104                                                         parentContainer.find( '.customize-themes-full-container-container' ).before( section.headContainer );
    1105                                                 }
    1106                                                 if ( ! section.contentContainer.parent().is( section.headContainer ) ) {
    1107                                                         container.append( section.contentContainer );
    1108                                                 }
    1109                                                 section.deferred.embedded.resolve();
    1110                                         });
    1111                                 } );
    1112                         };
    1113                         section.panel.bind( inject );
    1114                         inject( section.panel.get() ); // Since a section may never get a panel, assume that it won't ever get one
     1078
     1079                /**
     1080                 * @since 4.2.0
     1081                 */
     1082                initialize: function () {
     1083                        this.$customizeSidebar = $( '.wp-full-overlay-sidebar-content:first' );
     1084                        return api.Section.prototype.initialize.apply( this, arguments );
    11151085                },
    11161086
    … …  
    11461116                        });
    11471117
    1148                         _.bindAll( this, 'renderScreenshots', 'loadMore', 'checkTerm', 'filtersChecked' );
     1118                        _.bindAll( this, 'renderScreenshots' );
    11491119                },
    11501120
    … …  
    11531123                 *
    11541124                 * Ignore the active states' of the contained theme controls, and just
    1155                  * use the section's own active state instead. This prevents empty search
    1156                  * results for theme sections from causing the section to become inactive.
     1125                 * use the section's own active state instead. This ensures empty search
     1126                 * results for themes to cause the section to become inactive.
    11571127                 *
    11581128                 * @since 4.2.0
    … …  
    11701140                        var section = this;
    11711141
    1172                         section.filterContainer = $( '#accordion-section-' + section.id );
    1173 
    1174                         // Expand section/panel. Only collapse when opening another section.
    1175                         section.filterContainer.on( 'click', '.customize-themes-section-title', function() {
    1176                                 // Open the section.
    1177                                 if ( ! section.expanded() ) {
     1142                        // Expand/Collapse section/panel.
     1143                        section.container.find( '.change-theme, .customize-theme' ).on( 'click keydown', function( event ) {
     1144                                if ( api.utils.isKeydownButNotEnterEvent( event ) ) {
     1145                                        return;
     1146                                }
     1147                                event.preventDefault(); // Keep this AFTER the key filter above
     1148
     1149                                if ( section.expanded() ) {
     1150                                        section.collapse();
     1151                                } else {
    11781152                                        section.expand();
    11791153                                }
    1180 
    1181                                 // Toggle filters.
    1182                                 if ( section.filterContainer.find( '.filter-details' ).length ) {
    1183                                         section.filterContainer.find( '.customize-themes-section-title' )
    1184                                                 .toggleClass( 'details-open' )
    1185                                                 .attr('aria-expanded', function ( i, attr ) {
    1186                                                         return attr === 'true' ? 'false' : 'true';
    1187                                                 });
    1188                                         section.filterContainer.find( '.filter-details' ).slideToggle( 180 );
    1189                                 }
    1190                         });
    1191 
    1192                         // Preview installed themes.
    1193                         section.container.on( 'click', '.theme-actions .preview-theme', function() {
    1194                                 var themeId = $( this ).data( 'slug' );
    1195 
    1196                                 $( '.wp-full-overlay' ).addClass( 'customize-loading' );
    1197                                 api.panel( 'themes' ).loadThemePreview( themeId ).fail( function() {
    1198                                         $( '.wp-full-overlay' ).removeClass( 'customize-loading' );
    1199                                 } );
    12001154                        });
    12011155
    12021156                        // Theme navigation in details view.
    1203                         section.container.on( 'click', '.left', function() {
     1157                        section.container.on( 'click keydown', '.left', function( event ) {
     1158                                if ( api.utils.isKeydownButNotEnterEvent( event ) ) {
     1159                                        return;
     1160                                }
     1161
     1162                                event.preventDefault(); // Keep this AFTER the key filter above
     1163
    12041164                                section.previousTheme();
    12051165                        });
    12061166
    1207                         section.container.on( 'click', '.right', function() {
     1167                        section.container.on( 'click keydown', '.right', function( event ) {
     1168                                if ( api.utils.isKeydownButNotEnterEvent( event ) ) {
     1169                                        return;
     1170                                }
     1171
     1172                                event.preventDefault(); // Keep this AFTER the key filter above
     1173
    12081174                                section.nextTheme();
    12091175                        });
    12101176
    1211                         section.container.on( 'click', '.theme-backdrop, .close', function() {
     1177                        section.container.on( 'click keydown', '.theme-backdrop, .close', function( event ) {
     1178                                if ( api.utils.isKeydownButNotEnterEvent( event ) ) {
     1179                                        return;
     1180                                }
     1181
     1182                                event.preventDefault(); // Keep this AFTER the key filter above
     1183
    12121184                                section.closeDetails();
    12131185                        });
    12141186
    12151187                        var renderScreenshots = _.throttle( _.bind( section.renderScreenshots, this ), 100 );
    1216 
    1217                         // Only used when there is only one section - installed themes.
    1218                         $( '.control-panel-themes' ).on( 'input', '#themes-filter', function( event ) {
     1188                        section.container.on( 'input', '#themes-filter', function( event ) {
    12191189                                var count,
    12201190                                        term = event.currentTarget.value.toLowerCase().trim().replace( '-', ' ' ),
    … …  
    12281198
    12291199                                // Update theme count.
    1230                                 count = section.contentContainer.find( 'li.customize-control:visible' ).length;
    1231                                 $( '.control-panel-themes' ).find( '.theme-count' ).text( count );
    1232                         });
    1233 
    1234                         // Event listeners for queries with user-entered terms.
    1235                         if ( 'search' === section.params.action ) {
    1236                                 var debounced = _.debounce( section.checkTerm, 500 ); // Wait until there is no input for 500 milliseconds to initiate a search.
    1237                                 $( '.control-panel-themes' ).on( 'input', '#wp-filter-search-input', function() {
    1238                                         debounced( section );
    1239                                         if ( ! section.expanded() ) {
    1240                                                 section.expand();
     1200                                count = section.container.find( 'li.customize-control:visible' ).length;
     1201                                section.container.find( '.theme-count' ).text( count );
     1202                        });
     1203
     1204                        // Pre-load the first 3 theme screenshots.
     1205                        api.bind( 'ready', function () {
     1206                                _.each( section.controls().slice( 0, 3 ), function ( control ) {
     1207                                        var img, src = control.params.theme.screenshot[0];
     1208                                        if ( src ) {
     1209                                                img = new Image();
     1210                                                img.src = src;
    12411211                                        }
    12421212                                });
    1243 
    1244                                 // Focus the input if the icon is clicked.
    1245                                 section.filterContainer.find( '.search-form' ).on( 'click', function( e ) {
    1246                                         if ( ! $( e.currentTarget ).hasClass( 'wp-filter-search' ) ) {
    1247                                                 $( e.currentTarget ).find( '.wp-filter-search' ).focus();
    1248                                         }
    1249                                 });
    1250                         } else if ( 'favorites' === section.params.action ) {
    1251                                 section.container.on( 'click', '.favorites-form-submit', function() {
    1252                                         section.checkTerm( section );
    1253                                 });
    1254                                 section.container.on( 'keydown', '#wporg-username-input', function( e ) {
    1255                                         if ( api.utils.isKeydownButNotEnterEvent( e ) ) {
    1256                                                 return;
    1257                                         }
    1258                                         section.checkTerm( section );
    1259                                 });
    1260                         } else if ( 'feature_filter' === section.params.action ) {
    1261                                 section.container.on( 'click', '.filter-group input', function() {
    1262                                         section.filtersChecked();
    1263                                         section.checkTerm( section );
    1264                                 });
    1265 
    1266                                 // Toggle feature filter sections.
    1267                                 section.container.on( 'click', '.filter-group legend button', function( e ) {
    1268                                         $( e.currentTarget )
    1269                                                 .toggleClass( 'open' )
    1270                                                 .attr('aria-expanded', function ( i, attr ) {
    1271                                                         return attr === 'true' ? 'false' : 'true';
    1272                                                 })
    1273                                                 .parent().next( '.filter-group-feature' ).slideToggle( 180 );
    1274                                 });
    1275                         }
    1276 
    1277                         // Move section controls to the themes area.
    1278                         api.bind( 'ready', function () {
    1279                                 section.contentContainer = section.container.find( '.customize-themes-section' );
    1280                                 section.contentContainer.appendTo( $( '.customize-themes-full-container' ) );
    1281                                 section.container.add( section.filterContainer );
    12821213                        });
    12831214                },
    … …  
    12911222                 * @param {Object}   args
    12921223                 * @param {Boolean}  args.unchanged
    1293                  * @param {Function} args.completeCallback
     1224                 * @param {Callback} args.completeCallback
    12941225                 */
    12951226                onChangeExpanded: function ( expanded, args ) {
    … …  
    13041235
    13051236                        // Note: there is a second argument 'args' passed
    1306                         var section = this,
    1307                                 container = section.contentContainer.closest( '.customize-themes-full-container' );
    1308 
    1309                         if ( expanded ) {
    1310 
    1311                                 if ( -1 !== $.inArray( section.params.action, [ 'search', 'favorites', 'feature_filter' ] ) && '' === section.term ) {
    1312                                         section.collapse(); // Note that the current section hasn't been collapsed yet, so this is all we need to do to do nothing.
    1313                                         return; // Don't expand to an empty section that can't load any themes.
    1314                                 }
    1315 
    1316                                 // Load controls if none are loaded yet.
    1317                                 if ( 0 === section.loaded ) {
    1318                                         section.loadControls();
    1319                                 }
    1320 
     1237                        var panel = this,
     1238                                section = panel.contentContainer,
     1239                                overlay = section.closest( '.wp-full-overlay' ),
     1240                                container = section.closest( '.wp-full-overlay-sidebar-content' ),
     1241                                customizeBtn = section.find( '.customize-theme' ),
     1242                                changeBtn = panel.headContainer.find( '.change-theme' );
     1243
     1244                        if ( expanded && ! section.hasClass( 'current-panel' ) ) {
    13211245                                // Collapse any sibling sections/panels
    13221246                                api.section.each( function ( otherSection ) {
    1323                                         if ( otherSection !== section ) {
     1247                                        if ( otherSection !== panel ) {
    13241248                                                otherSection.collapse( { duration: args.duration } );
    13251249                                        }
    13261250                                });
    1327 
    1328                                 section.contentContainer.addClass( 'current-section' );
    1329                                 container.scrollTop();
    1330                                 section.filterContainer.find( '.customize-themes-section-title' ).addClass( 'selected' );
    1331 
    1332                                 container.on( 'scroll', _.throttle( section.renderScreenshots, 300 ) );
    1333                                 container.on( 'scroll', _.throttle( section.loadMore, 300 ) );
    1334 
    1335                                 if ( args.completeCallback ) {
    1336                                         args.completeCallback();
    1337                                 }
    1338                                 section.updateCount(); // Show this section's count.
    1339                         } else {
    1340                                 section.contentContainer.removeClass( 'current-section' );
    1341 
    1342                                 // Always hide, even if they don't exist or are already hidden.
    1343                                 section.filterContainer.find( '.customize-themes-section-title' ).removeClass( 'selected details-open' ).attr( 'aria-expanded', 'false' );
    1344                                 section.filterContainer.find( '.filter-details' ).slideUp( 180 );
    1345 
    1346                                 container.off( 'scroll' );
    1347 
    1348                                 if ( args.completeCallback ) {
    1349                                         args.completeCallback();
    1350                                 }
    1351                         }
    1352                 },
    1353 
    1354                 /**
    1355                  * Return the section's content element without detachng from the parent.
    1356                  *
    1357                  * @since 4.7.0
    1358                  */
    1359                 getContent: function() {
    1360                         return this.container.find( '.control-section-content' );
    1361                 },
    1362 
    1363                 /**
    1364                  * Load theme data via ajax and add themes to the section as controls.
    1365                  *
    1366                  * @since 4.7.0
    1367                  */
    1368                 loadControls: function() {
    1369                         var section = this, params, page, request;
    1370 
    1371                         if ( section.loading ) {
    1372                                 return; // We're already loading a batch of themes.
    1373                         }
    1374 
    1375                         // Parameters for every API query. Additional params are set in PHP.
    1376                         page = Math.ceil( section.loaded / 100 ) + 1;
    1377                         params = {
    1378                                 'switch-themes-nonce': api.settings.nonce['switch-themes'],
    1379                                 'wp_customize': 'on',
    1380                                 'theme_action': section.params.action,
    1381                                 'customized_theme': api.settings.theme.stylesheet,
    1382                                 'page': page
    1383                         };
    1384 
    1385                         // Add fields for special request actions.
    1386                         if ( 'search' === section.params.action ) {
    1387                                 if ( '' === section.term ) {
    1388                                         return;
    1389                                 } else {
    1390                                         params.search = section.term;
    1391                                 }
    1392                         } else if ( 'favorites' === section.params.action ) {
    1393                                 if ( '' === section.term ) {
    1394                                         return;
    1395                                 } else {
    1396                                         params.user = section.term;
    1397                                 }
    1398                         } else if ( 'feature_filter' === section.params.action ) {
    1399                                 if ( '' === section.term ) {
    1400                                         return;
    1401                                 } else {
    1402                                         params.tags = section.term;
    1403                                 }
    1404                         }
    1405 
    1406                         // Load themes.
    1407                         section.headContainer.closest( '.wp-full-overlay' ).addClass( 'loading' );
    1408                         section.loading = true;
    1409                         section.container.find( '.no-themes' ).hide();
    1410                         request = wp.ajax.post( 'customize-load-themes', params );
    1411                         request.done(function( data ) {
    1412                                 var themes = data.themes,
    1413                                     themeControl, newThemeControls;
    1414                                 if ( 0 !== themes.length ) {
    1415                                         newThemeControls = [];
    1416                                         // Add controls for each theme.
    1417                                         _.each( themes, function ( theme ) {
    1418                                                 var customizeId = section.params.action + '_theme_' + theme.id;
    1419                                                 themeControl = new api.controlConstructor.theme( customizeId, {
    1420                                                         params: {
    1421                                                                 type: 'theme',
    1422                                                                 content: '<li id="customize-control-theme-' + section.params.action + '_' + theme.id + '" class="customize-control customize-control-theme"></li>',
    1423                                                                 section: section.params.id,
    1424                                                                 active: true,
    1425                                                                 theme: theme,
    1426                                                                 priority: section.loaded + 1
    1427                                                         },
    1428                                                         previewer: api.previewer
    1429                                                 } );
    1430 
    1431                                                 api.control.add( customizeId, themeControl );
    1432                                                 newThemeControls.push( themeControl );
    1433                                                 section.loaded = section.loaded + 1;
    1434                                         });
    1435 
    1436                                         if ( 1 === page ) {
    1437                                                 // Pre-load the first 3 theme screenshots.
    1438                                                 _.each( section.controls().slice( 0, 3 ), function ( control ) {
    1439                                                         var img, src = control.params.theme.screenshot[0];
    1440                                                         if ( src ) {
    1441                                                                 img = new Image();
    1442                                                                 img.src = src;
    1443                                                         }
    1444                                                 });
    1445                                                 if ( 'search' === section.params.action ) {
    1446                                                         wp.a11y.speak( api.settings.l10n.themeSearchResults.replace( '%d', data.info.results ) );
    1447                                                 }
    1448                                         } else {
    1449                                                 Array.prototype.push.apply( section.screenshotQueue, newThemeControls ); // Add new themes to the screenshot queue.
     1251                                api.panel.each( function ( otherPanel ) {
     1252                                        otherPanel.collapse( { duration: 0 } );
     1253                                });
     1254
     1255                                panel._animateChangeExpanded( function() {
     1256                                        changeBtn.attr( 'tabindex', '-1' );
     1257                                        customizeBtn.attr( 'tabindex', '0' );
     1258
     1259                                        customizeBtn.focus();
     1260                                        section.css( 'top', '' );
     1261                                        container.scrollTop( 0 );
     1262
     1263                                        if ( args.completeCallback ) {
     1264                                                args.completeCallback();
    14501265                                        }
    1451                                         _.delay( section.renderScreenshots, 100 ); // Wait for the controls to become visible.
    1452 
    1453                                         if ( 'installed' === section.action || 100 > themes.length ) { // If we have less than the requested 100 themes, it's the end of the list.
    1454                                                 section.fullyLoaded = true;
     1266                                } );
     1267
     1268                                overlay.addClass( 'in-themes-panel' );
     1269                                section.addClass( 'current-panel' );
     1270
     1271                        } else if ( ! expanded && section.hasClass( 'current-panel' ) ) {
     1272                                panel._animateChangeExpanded( function() {
     1273                                        changeBtn.attr( 'tabindex', '0' );
     1274                                        customizeBtn.attr( 'tabindex', '-1' );
     1275
     1276                                        changeBtn.focus();
     1277                                        section.css( 'top', '' );
     1278
     1279                                        if ( args.completeCallback ) {
     1280                                                args.completeCallback();
    14551281                                        }
    1456                                 } else {
    1457                                         if ( 0 === section.loaded ) {
    1458                                                 section.container.find( '.no-themes' ).show();
    1459                                                 wp.a11y.speak( section.container.find( '.no-themes' ).text() );
    1460                                         } else {
    1461                                                 section.fullyLoaded = true;
    1462                                         }
    1463                                 }
    1464                                 if ( 'installed' === section.params.action ) {
    1465                                         section.updateCount();
    1466                                 } else {
    1467                                         section.updateCount( data.info.results );
    1468                                 }
    1469                                 section.container.find( '.unexpected-error' ).hide(); // Hide error notice in case it was previously shown.
    1470 
    1471                                 // This cannot run on request.always, as section.loading may turn false before the new controls load in the success case.
    1472                                 section.headContainer.closest( '.wp-full-overlay' ).removeClass( 'loading' );
    1473                                 section.loading = false;
    1474                         });
    1475                         request.fail(function( data ) {
    1476                                 if ( 'undefined' === typeof data ) {
    1477                                         section.container.find( '.unexpected-error' ).show();
    1478                                         wp.a11y.speak( section.container.find( '.unexpected-error' ).text() );
    1479                                 } else if ( typeof console !== 'undefined' && console.error ) {
    1480                                         console.error( data );
    1481                                 }
    1482 
    1483                                 // This cannot run on request.always, as section.loading may turn false before the new controls load in the success case.
    1484                                 section.headContainer.closest( '.wp-full-overlay' ).removeClass( 'loading' );
    1485                                 section.loading = false;
    1486                         });
    1487                 },
    1488 
    1489                 /**
    1490                  * Determines whether more themes should be loaded, and loads them.
    1491                  *
    1492                  * @since 4.7.0
    1493                  */
    1494                 loadMore: function() {
    1495                         var section = this, container, bottom, threshold;
    1496                         if ( ! section.fullyLoaded && ! section.loading ) {
    1497                                 container = section.container.closest( '.customize-themes-full-container' );
    1498 
    1499                                 bottom = container.scrollTop() + container.height();
    1500                                 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.
    1501 
    1502                                 if ( bottom > threshold ) {
    1503                                         section.loadControls();
    1504                                 }
    1505                         }
    1506                 },
    1507 
    1508                 /**
    1509                  * Event handler for search, feature filter, and favorites input that determines if the term has changed and loads new controls as needed.
    1510                  *
    1511                  * @since 4.7.0
    1512                  *
    1513                  * @param {wp.customize.ThemesSection} section The current theme section, passed through the debouncer.
    1514                  */
    1515                 checkTerm: function( section ) {
    1516                         var newTerm;
    1517 
    1518                         // Find term.
    1519                         if ( 'search' === section.params.action ) {
    1520                                 newTerm = $( '#wp-filter-search-input' ).val();
    1521                         } else if ( 'favorites' === section.params.action ) {
    1522                                 newTerm = $( '#wporg-username-input' ).val();
    1523                         } else if ( 'feature_filter' === section.params.action ) {
    1524                                 newTerm = section.term; // Set separately by filtersChecked(), as they're changed.
    1525                                 if ( '' === newTerm ) {
    1526                                         return;
    1527                                 }
    1528                         } else {
    1529                                 return;
    1530                         }
    1531 
    1532                         if ( section.term === newTerm && 'feature_filter' !== section.params.action ) {
    1533                                 return;
    1534                         }
    1535 
    1536                         // Clear the controls in the section.
    1537                         _.each( section.controls(), function( control ) {
    1538                                 control.container.remove();
    1539                                 api.control.remove( control.id );
    1540                         });
    1541                         section.loaded = 0;
    1542                         section.fullyLoaded = false;
    1543                         section.screenshotQueue = null;
    1544 
    1545                         if ( '' !== newTerm ) { // Empty term should not show any results.
    1546                                 // Run a new query, with loadControls handling paging, etc.
    1547                                 section.term = newTerm;
    1548                                 section.loadControls();
    1549                                 if ( ! section.expanded() ) {
    1550                                         section.expand(); // Expand the section if it isn't expanded.
    1551                                 }
    1552                         }
    1553                 },
    1554 
    1555                 /**
    1556                  * Check for filters checked in the feature filter list.
    1557                  *
    1558                  * @since 4.7.0
    1559                  */
    1560                 filtersChecked: function() {
    1561                         var section = this,
    1562                             items = section.container.find( '.filter-group' ).find( ':checkbox' ),
    1563                             tags = [];
    1564 
    1565                         if ( 'feature_filter' !== section.params.action ) {
    1566                                 return false;
    1567                         }
    1568 
    1569                         _.each( items.filter( ':checked' ), function( item ) {
    1570                                 tags.push( $( item ).prop( 'value' ) );
    1571                         });
    1572 
    1573                         // When no filters are checked, restore initial state and return
    1574                         if ( tags.length === 0 ) {
    1575                                 section.term = '';
    1576                         } else {
    1577                                 section.term = tags;
     1282                                } );
     1283
     1284                                overlay.removeClass( 'in-themes-panel' );
     1285                                section.removeClass( 'current-panel' );
    15781286                        }
    15791287                },
    … …  
    15871295                        var section = this;
    15881296
    1589                         // Fill queue initially, or check for more if empty.
    1590                         if ( section.screenshotQueue === null || 0 === section.screenshotQueue.length ) {
    1591                                 // Add controls that haven't had their screenshots rendered.
    1592                                 section.screenshotQueue = _.filter( section.controls(), function( control ) {
    1593                                         return ! control.screenshotRendered;
    1594                                 });
    1595                         }
    1596 
    1597                         // Are all screenshots rendered (for now)?
     1297                        // Fill queue initially.
     1298                        if ( section.screenshotQueue === null ) {
     1299                                section.screenshotQueue = section.controls();
     1300                        }
     1301
     1302                        // Are all screenshots rendered?
    15981303                        if ( ! section.screenshotQueue.length ) {
    15991304                                return;
    … …  
    16311336
    16321337                /**
    1633                  * Update the number of themes in the section.
    1634                  *
    1635                  * @since 4.7.0
    1636                  */
    1637                 updateCount: function ( count ) {
    1638                         if ( ! count ) {
    1639                                 count = this.loaded;
    1640                         }
    1641 
    1642                         var displayed = this.container.closest( '.control-panel-content' ).find( '.themes-displayed' ),
    1643                             countEl = this.container.closest( '.control-panel-content' ).find( '.theme-count' );
    1644 
    1645                         if ( 0 === count ) {
    1646                                 countEl.text( count );
    1647                         } else {
    1648                                 // Animate the count change for emphasis.
    1649                                 displayed.fadeOut( 180, function() {
    1650                                         countEl.text( count );
    1651                                         displayed.fadeIn( 180 );
    1652                                 } );
    1653                                 wp.a11y.speak( api.settings.l10n.announceThemeCount.replace( '%d', count ) );
    1654                         }
    1655                 },
    1656 
    1657                 /**
    16581338                 * Advance the modal to the next theme.
    16591339                 *
    … …  
    16751355                 */
    16761356                getNextTheme: function () {
    1677                         var section = this, control, next;
    1678                         control = api.control( section.params.action + '_theme_' + this.currentTheme );
     1357                        var control, next;
     1358                        control = api.control( 'theme_' + this.currentTheme );
    16791359                        next = control.container.next( 'li.customize-control-theme' );
    16801360                        if ( ! next.length ) {
    16811361                                return false;
    16821362                        }
    1683                         next = next[0].id.replace( 'customize-control-theme-' + section.params.action, section.params.action + '_theme' );
     1363                        next = next[0].id.replace( 'customize-control-', '' );
    16841364                        control = api.control( next );
    16851365
    … …  
    17071387                 */
    17081388                getPreviousTheme: function () {
    1709                         var section = this, control, previous;
    1710                         control = api.control( section.params.action + '_theme_' + this.currentTheme );
     1389                        var control, previous;
     1390                        control = api.control( 'theme_' + this.currentTheme );
    17111391                        previous = control.container.prev( 'li.customize-control-theme' );
    17121392                        if ( ! previous.length ) {
    17131393                                return false;
    17141394                        }
    1715                         previous = previous[0].id.replace( 'customize-control-theme-' + section.params.action, section.params.action + '_theme' );
     1395                        previous = previous[0].id.replace( 'customize-control-', '' );
    17161396                        control = api.control( previous );
    17171397
    … …  
    17341414
    17351415                /**
     1416                 * Load theme preview.
     1417                 *
     1418                 * @since 4.7.0
     1419                 *
     1420                 * @param {string} themeId Theme ID.
     1421                 * @returns {jQuery.promise} Promise.
     1422                 */
     1423                loadThemePreview: function( themeId ) {
     1424                        var deferred = $.Deferred(), onceProcessingComplete, overlay, urlParser;
     1425
     1426                        urlParser = document.createElement( 'a' );
     1427                        urlParser.href = location.href;
     1428                        urlParser.search = $.param( _.extend(
     1429                                api.utils.parseQueryString( urlParser.search.substr( 1 ) ),
     1430                                {
     1431                                        theme: themeId,
     1432                                        changeset_uuid: api.settings.changeset.uuid
     1433                                }
     1434                        ) );
     1435
     1436                        overlay = $( '.wp-full-overlay' );
     1437                        overlay.addClass( 'customize-loading' );
     1438
     1439                        onceProcessingComplete = function() {
     1440                                var request;
     1441                                if ( api.state( 'processing' ).get() > 0 ) {
     1442                                        return;
     1443                                }
     1444
     1445                                api.state( 'processing' ).unbind( onceProcessingComplete );
     1446
     1447                                request = api.requestChangesetUpdate();
     1448                                request.done( function() {
     1449                                        $( window ).off( 'beforeunload.customize-confirm' );
     1450                                        window.location.href = urlParser.href;
     1451                                } );
     1452                                request.fail( function() {
     1453                                        overlay.removeClass( 'customize-loading' );
     1454                                } );
     1455                        };
     1456
     1457                        if ( 0 === api.state( 'processing' ).get() ) {
     1458                                onceProcessingComplete();
     1459                        } else {
     1460                                api.state( 'processing' ).bind( onceProcessingComplete );
     1461                        }
     1462
     1463                        return deferred.promise();
     1464                },
     1465
     1466                /**
    17361467                 * Render & show the theme details for a given theme model.
    17371468                 *
    … …  
    17411472                 */
    17421473                showDetails: function ( theme, callback ) {
    1743                         var section = this;
     1474                        var section = this, link;
    17441475                        callback = callback || function(){};
    17451476                        section.currentTheme = theme.id;
    … …  
    17501481                        section.containFocus( section.overlay );
    17511482                        section.updateLimits();
    1752                         wp.a11y.speak( api.settings.l10n.announceThemeDetails.replace( '%s', theme.name ) );
     1483
     1484                        link = section.overlay.find( '.inactive-theme > a' );
     1485
     1486                        link.on( 'click', function( event ) {
     1487                                event.preventDefault();
     1488
     1489                                // Short-circuit if request is currently being made.
     1490                                if ( link.hasClass( 'disabled' ) ) {
     1491                                        return;
     1492                                }
     1493                                link.addClass( 'disabled' );
     1494
     1495                                section.loadThemePreview( theme.id ).fail( function() {
     1496                                        link.removeClass( 'disabled' );
     1497                                } );
     1498                        } );
    17531499                        callback();
    17541500                },
    … …  
    17621508                        $( 'body' ).removeClass( 'modal-open' );
    17631509                        this.overlay.fadeOut( 'fast' );
    1764                         api.control( this.params.action + '_theme_' + this.currentTheme ).container.find( '.theme' ).focus();
     1510                        api.control( 'theme_' + this.currentTheme ).focus();
    17651511                },
    17661512
    … …  
    18421588                        if ( ! panel.contentContainer.parent().is( panel.headContainer ) ) {
    18431589                                container.append( panel.contentContainer );
    1844                         }
    1845                         panel.renderContent();
     1590                                panel.renderContent();
     1591                        }
    18461592
    18471593                        panel.deferred.embedded.resolve();
    … …  
    20351781                }
    20361782        });
    2037 
    2038 
    2039         /**
    2040          * wp.customize.ThemesPanel
    2041          *
    2042          * Custom section for themes that displays without the customize preview.
    2043          *
    2044          * @constructor
    2045          * @augments wp.customize.Panel
    2046          * @augments wp.customize.Container
    2047          */
    2048         api.ThemesPanel = api.Panel.extend({
    2049                 installingThemes: [],
    2050 
    2051                 /**
    2052                  * @since 4.7.0
    2053                  */
    2054                 attachEvents: function () {
    2055                         var panel = this;
    2056 
    2057                         // Attach regular panel events.
    2058                         api.Panel.prototype.attachEvents.apply( this );
    2059 
    2060                         // Collapse panel to customize the current theme.
    2061                         panel.contentContainer.on( 'click', '.customize-theme', function() {
    2062                                 panel.collapse();
    2063                         });
    2064 
    2065                         // Toggle between filtering and browsing themes on mobile.
    2066                         panel.contentContainer.on( 'click', '.see-themes, .filter-themes', function() {
    2067                                 $( '.wp-full-overlay' ).toggleClass( 'showing-themes' );
    2068                         });
    2069 
    2070                         // Install (and maybe preview) a theme.
    2071                         panel.contentContainer.on( 'click', '.theme-install', function( event ) {
    2072                                 panel.installTheme( event );
    2073                         });
    2074 
    2075                         // Update a theme. Theme cards have the class, the details modal has the id.
    2076                         panel.contentContainer.on( 'click', '.update-theme, #update-theme', function( event ) {
    2077                                 // #update-theme is a link.
    2078                                 event.preventDefault();
    2079                                 event.stopPropagation();
    2080 
    2081                                 panel.updateTheme( event );
    2082                         });
    2083 
    2084                         // Delete a theme.
    2085                         panel.contentContainer.on( 'click', '.delete-theme', function( event ) {
    2086                                 panel.deleteTheme( event );
    2087                         });
    2088 
    2089                         _.bindAll( this, 'installTheme', 'updateTheme' );
    2090                 },
    2091 
    2092                 /**
    2093                  * Update UI to reflect expanded state
    2094                  *
    2095                  * @since 4.7.0
    2096                  *
    2097                  * @param {Boolean}  expanded
    2098                  * @param {Object}   args
    2099                  * @param {Boolean}  args.unchanged
    2100                  * @param {Function} args.completeCallback
    2101                  */
    2102                 onChangeExpanded: function ( expanded, args ) {
    2103 
    2104                         // Expand/collapse the panel normally.
    2105                         api.Panel.prototype.onChangeExpanded.apply( this, [ expanded, args ] );
    2106 
    2107                         // Immediately call the complete callback if there were no changes
    2108                         if ( args.unchanged ) {
    2109                                 if ( args.completeCallback ) {
    2110                                         args.completeCallback();
    2111                                 }
    2112                                 return;
    2113                         }
    2114 
    2115                         // Note: there is a second argument 'args' passed
    2116                         var panel = this,
    2117                                 overlay = panel.headContainer.closest( '.wp-full-overlay' );
    2118 
    2119                         if ( expanded ) {
    2120                                 overlay
    2121                                         .addClass( 'in-themes-panel' ).addClass( 'showing-themes' )
    2122                                         .delay( 200 ).find( '.customize-themes-full-container' ).addClass( 'animate' );
    2123 
    2124                                 // Automatically open the installed themes section.
    2125                                 api.section( 'installed_themes' ).expand();
    2126                         } else {
    2127                                 overlay
    2128                                         .removeClass( 'in-themes-panel' )
    2129                                         .find( '.customize-themes-full-container' ).removeClass( 'animate' );
    2130                         }
    2131                 },
    2132 
    2133                 /**
    2134                  * Install a theme via wp.updates.
    2135                  *
    2136                  * @since 4.7.0
    2137                  */
    2138                 installTheme: function( event ) {
    2139                         var panel = this, preview = false, slug = $( event.target ).data( 'slug' );
    2140 
    2141                         if ( -1 !== $.inArray( this.installingThemes, slug ) ) {
    2142                                 return; // Theme is already being installed.
    2143                         }
    2144 
    2145                         wp.updates.maybeRequestFilesystemCredentials( event );
    2146 
    2147                         $( document ).one( 'wp-theme-install-success', function( event, response ) {
    2148                                 var theme = false, customizeId, themeControl;
    2149                                 if ( preview ) {
    2150 
    2151                                         panel.loadThemePreview( slug ).fail( function() {
    2152                                                 $( '.wp-full-overlay' ).removeClass( 'customize-loading' );
    2153                                         } );
    2154 
    2155                                 } else {
    2156                                         api.control.each( function( control ) {
    2157                                                 if ( 'theme' === control.params.type && control.params.theme.id === response.slug ) {
    2158                                                         theme = control.params.theme; // Used below to add theme control.
    2159                                                         control.rerenderAsInstalled( true );
    2160                                                 }
    2161                                         });
    2162 
    2163                                         // Don't add the same theme more than once.
    2164                                         if ( ! theme || 'undefined' !== typeof api.control( 'installed_theme_' + theme.id ) ) {
    2165                                                 return;
    2166                                         }
    2167 
    2168                                         // Add theme control to installed section.
    2169                                         theme.type = 'installed';
    2170                                         customizeId = 'installed_theme_' + theme.id;
    2171                                         themeControl = new api.controlConstructor.theme( customizeId, {
    2172                                                 params: {
    2173                                                         type: 'theme',
    2174                                                         content: $( '<li class="customize-control customize-control-theme"></li>' ).attr( 'id', 'customize-control-theme-installed_' + theme.id ).prop( 'outerHTML' ),
    2175                                                         section: 'installed_themes',
    2176                                                         active: true,
    2177                                                         theme: theme,
    2178                                                         priority: 0 // Add all newly-installed themes to the top.
    2179                                                 },
    2180                                                 previewer: api.previewer
    2181                                         } );
    2182 
    2183                                         api.control.add( customizeId, themeControl );
    2184                                         api.control( customizeId ).container.trigger( 'render-screenshot' );
    2185 
    2186                                         // Close the details modal if it's open to the installed theme.
    2187                                         api.section.each( function( section ) {
    2188                                                 if ( 'themes' === section.params.type ) {
    2189                                                         if ( theme.id === section.currentTheme ) { // Don't close the modal if the user has navigated elsewhere.
    2190                                                                 section.closeDetails();
    2191                                                         }
    2192                                                 }
    2193                                         });
    2194                                 }
    2195                         } );
    2196 
    2197                         this.installingThemes.push( $( event.target ).data( 'slug' ) ); // Note: we don't remove elements from installingThemes, since they shouldn't be installed again.
    2198                         wp.updates.installTheme( {
    2199                                 slug: slug
    2200                         } );
    2201 
    2202                         // Also preview the theme as the event is triggered on Install & Preview.
    2203                         if ( $( event.target ).hasClass( 'preview' ) ) {
    2204                                 preview = true;
    2205                                 $( '.wp-full-overlay' ).addClass( 'customize-loading' );
    2206                         }
    2207                 },
    2208 
    2209                 /**
    2210                  * Load theme preview.
    2211                  *
    2212                  * @since 4.7.0
    2213                  *
    2214                  * @param {string} themeId Theme ID.
    2215                  * @returns {jQuery.promise} Promise.
    2216                  */
    2217                 loadThemePreview: function( themeId ) {
    2218                         var deferred = $.Deferred(), onceProcessingComplete, overlay, urlParser;
    2219 
    2220                         urlParser = document.createElement( 'a' );
    2221                         urlParser.href = location.href;
    2222                         urlParser.search = $.param( _.extend(
    2223                                 api.utils.parseQueryString( urlParser.search.substr( 1 ) ),
    2224                                 {
    2225                                         theme: themeId,
    2226                                         changeset_uuid: api.settings.changeset.uuid
    2227                                 }
    2228                         ) );
    2229 
    2230                         overlay = $( '.wp-full-overlay' );
    2231                         overlay.addClass( 'customize-loading' );
    2232 
    2233                         onceProcessingComplete = function() {
    2234                                 var request;
    2235                                 if ( api.state( 'processing' ).get() > 0 ) {
    2236                                         return;
    2237                                 }
    2238 
    2239                                 api.state( 'processing' ).unbind( onceProcessingComplete );
    2240 
    2241                                 request = api.requestChangesetUpdate();
    2242                                 request.done( function() {
    2243                                         $( window ).off( 'beforeunload.customize-confirm' );
    2244                                         window.location.href = urlParser.href;
    2245                                 } );
    2246                                 request.fail( function() {
    2247                                         overlay.removeClass( 'customize-loading' );
    2248                                 } );
    2249                         };
    2250 
    2251                         if ( 0 === api.state( 'processing' ).get() ) {
    2252                                 onceProcessingComplete();
    2253                         } else {
    2254                                 api.state( 'processing' ).bind( onceProcessingComplete );
    2255                         }
    2256 
    2257                         return deferred.promise();
    2258                 },
    2259 
    2260                 /**
    2261                  * Update a theme via wp.updates.
    2262                  *
    2263                  * @since 4.7.0
    2264                  */
    2265                 updateTheme: function( event ) {
    2266                         wp.updates.maybeRequestFilesystemCredentials( event );
    2267 
    2268                         $( document ).one( 'wp-theme-update-success', function( event, response ) {
    2269                                 // Rerender the control to reflect the update.
    2270                                 api.control.each( function( control ) {
    2271                                         if ( 'theme' === control.params.type && control.params.theme.id === response.slug ) {
    2272                                                 control.params.theme.hasUpdate = false;
    2273                                                 control.rerenderAsInstalled( true );
    2274                                         }
    2275                                 });
    2276                         } );
    2277 
    2278                         wp.updates.updateTheme( {
    2279                                 slug: $( event.target ).closest( '.notice' ).data( 'slug' )
    2280                         } );
    2281                 },
    2282 
    2283                 /**
    2284                  * Delete a theme via wp.updates.
    2285                  *
    2286                  * @since 4.7.0
    2287                  */
    2288                 deleteTheme: function( event ) {
    2289                         var theme, section;
    2290                         theme = $( event.target ).data( 'slug' );
    2291                         section = api.section( 'installed_themes' );
    2292 
    2293                         event.preventDefault();
    2294 
    2295                         // Confirmation dialog for deleting a theme.
    2296                         if ( ! window.confirm( api.settings.l10n.confirmDeleteTheme ) ) {
    2297                                 return;
    2298                         }
    2299 
    2300                         wp.updates.maybeRequestFilesystemCredentials( event );
    2301 
    2302                         $( document ).one( 'wp-theme-delete-success', function() {
    2303                                 var control = api.control( 'installed_theme_' + theme );
    2304 
    2305                                 // Remove theme control.
    2306                                 control.container.remove();
    2307                                 api.control.remove( control.id );
    2308 
    2309                                 // Update installed count.
    2310                                 section.loaded = section.loaded - 1;
    2311                                 section.updateCount();
    2312 
    2313                                 // Rerender any other theme controls as uninstalled.
    2314                                 api.control.each( function( control ) {
    2315                                         if ( 'theme' === control.params.type && control.params.theme.id === theme ) {
    2316                                                 control.rerenderAsInstalled( false );
    2317                                         }
    2318                                 });
    2319                         } );
    2320 
    2321                         wp.updates.deleteTheme( {
    2322                                 slug: theme
    2323                         } );
    2324 
    2325                         // Close modal and focus the section.
    2326                         section.closeDetails();
    2327                         section.focus();
    2328                 }
    2329 
    2330         });
    2331 
    23321783
    23331784        /**
    … …  
    26452096                 * @param {Object}   args
    26462097                 * @param {Number}   args.duration
    2647                  * @param {Function} args.completeCallback
     2098                 * @param {Callback} args.completeCallback
    26482099                 */
    26492100                onChangeActive: function ( active, args ) {
    … …  
    38163267
    38173268                touchDrag: false,
    3818                 screenshotRendered: false,
     3269                isRendered: false,
     3270
     3271                /**
     3272                 * Defer rendering the theme control until the section is displayed.
     3273                 *
     3274                 * @since 4.2.0
     3275                 */
     3276                renderContent: function () {
     3277                        var control = this,
     3278                                renderContentArgs = arguments;
     3279
     3280                        api.section( control.section(), function( section ) {
     3281                                if ( section.expanded() ) {
     3282                                        api.Control.prototype.renderContent.apply( control, renderContentArgs );
     3283                                        control.isRendered = true;
     3284                                } else {
     3285                                        section.expanded.bind( function( expanded ) {
     3286                                                if ( expanded && ! control.isRendered ) {
     3287                                                        api.Control.prototype.renderContent.apply( control, renderContentArgs );
     3288                                                        control.isRendered = true;
     3289                                                }
     3290                                        } );
     3291                                }
     3292                        } );
     3293                },
    38193294
    38203295                /**
    … …  
    38403315
    38413316                                // Prevent the modal from showing when the user clicks the action button.
    3842                                 if ( $( event.target ).is( '.theme-actions .button, .update-theme' ) ) {
     3317                                if ( $( event.target ).is( '.theme-actions .button' ) ) {
    38433318                                        return;
    38443319                                }
    38453320
     3321                                api.section( control.section() ).loadThemePreview( control.params.theme.id );
     3322                        });
     3323
     3324                        control.container.on( 'click keydown', '.theme-actions .theme-details', function( event ) {
     3325                                if ( api.utils.isKeydownButNotEnterEvent( event ) ) {
     3326                                        return;
     3327                                }
     3328
    38463329                                event.preventDefault(); // Keep this AFTER the key filter above
     3330
    38473331                                api.section( control.section() ).showDetails( control.params.theme );
    38483332                        });
    … …  
    38553339                                        $screenshot.attr( 'src', source );
    38563340                                }
    3857                                 control.screenshotRendered = true;
    3858                         });
    3859                 },
    3860 
    3861                 /**
    3862                  * Show or hide the theme based on the presence of the term in the title, description, tags, and author.
     3341                        });
     3342                },
     3343
     3344                /**
     3345                 * Show or hide the theme based on the presence of the term in the title, description, and author.
    38633346                 *
    38643347                 * @since 4.2.0
    … …  
    38763359                                control.deactivate();
    38773360                        }
    3878                 },
    3879 
    3880                 /**
    3881                  * Rerender the theme from its JS template with the installed type.
    3882                  *
    3883                  * @since 4.7.0
    3884                  */
    3885                 rerenderAsInstalled: function( installed ) {
    3886                         var control = this, section;
    3887                         if ( installed ) {
    3888                                 control.params.theme.type = 'installed';
    3889                         } else {
    3890                                 section = api.section( control.params.section );
    3891                                 control.params.theme.type = section.params.action;
    3892                         }
    3893                         control.renderContent(); // replaces existing content
    3894                         control.container.trigger( 'render-screenshot' );
    38953361                }
    38963362        });
    … …  
    45854051                theme:               api.ThemeControl
    45864052        };
    4587         api.panelConstructor = {
    4588                 themes: api.ThemesPanel
    4589         };
     4053        api.panelConstructor = {};
    45904054        api.sectionConstructor = {
    45914055                themes: api.ThemesSection
    … …  
    47054169                // Sort the sections within each panel
    47064170                api.panel.each( function ( panel ) {
    4707                         if ( 'themes' === panel.id ) {
    4708                                 return; // Don't reflow theme sections, as doing so moves them after the themes container.
    4709                         }
    4710 
    47114171                        var sections = panel.sections(),
    47124172                                sectionHeadContainers = _.pluck( sections, 'headContainer' );
    … …  
    53504810                        collapsedObject = expandedControls[0] || expandedSections[0] || expandedPanels[0];
    53514811                        if ( collapsedObject ) {
    5352                                 if ( 'themes' === collapsedObject.params.type ) {
    5353                                         // Themes panel or section.
    5354                                         if ( $( 'body' ).hasClass( 'modal-open' ) ) {
    5355                                                 collapsedObject.closeDetails();
    5356                                         } else {
    5357                                                 // If we're collapsing a section, collapse the panel also.
    5358                                                 wp.customize.panel( 'themes' ).collapse();
    5359                                         }
    5360                                         return;
    5361                                 }
    53624812                                collapsedObject.collapse();
    53634813                                event.preventDefault();
  • trunk/src/wp-admin/js/updates.js

    r39099 r39140  
    182182                        $notice.replaceWith( $adminNotice );
    183183                } else {
    184                         if ( 'customize' === pagenow ) {
    185                                 $( '.customize-themes-notifications' ).append( $adminNotice );
    186                         } else {
    187                                 $( '.wrap' ).find( '> h1' ).after( $adminNotice );
    188                         }
     184                        $( '.wrap' ).find( '> h1' ).after( $adminNotice );
    189185                }
    190186
    … …  
    931927                        $notice = $( '[data-slug="' + args.slug + '"]' ).find( '.update-message' ).removeClass( 'notice-error' ).addClass( 'updating-message notice-warning' ).find( 'p' );
    932928
    933                 } else if ( 'customize' === pagenow ) {
    934 
    935                         // Update the theme details UI.
    936                         $notice = $( '#update-theme' ).closest( '.notice' ).removeClass( 'notice-large' );
    937 
    938                         $notice.find( 'h3' ).remove();
    939 
    940                         // Add the top-level UI, and update both.
    941                         $notice = $notice.add( $( '#customize-control-theme-installed_' + args.slug ).find( '.update-message' ) );
    942                         $notice = $notice.addClass( 'updating-message' ).find( 'p' );
    943 
    944929                } else {
    945930                        $notice = $( '#update-theme' ).closest( '.notice' ).removeClass( 'notice-large' );
    … …  
    984969                        $notice, newText;
    985970
    986                 if ( 'customize' === pagenow ) {
    987                         $theme = wp.customize.control( 'installed_theme_' + response.slug ).container;
    988                 }
    989 
    990971                if ( 'themes-network' === pagenow ) {
    991972                        $notice = $theme.find( '.update-message' );
    … …  
    10401021                if ( wp.updates.maybeHandleCredentialError( response, 'update-theme' ) ) {
    10411022                        return;
    1042                 }
    1043 
    1044                 if ( 'customize' === pagenow ) {
    1045                         $theme = wp.customize.control( 'installed_theme_' + response.slug ).container;
    10461023                }
    10471024
    … …  
    11821159                }
    11831160
    1184                 if ( 'customize' === pagenow ) {
    1185                         if ( $document.find( 'body' ).hasClass( 'modal-open' ) ) {
    1186                                 $button = $( '.theme-install[data-slug="' + response.slug + '"]' );
    1187                                 $card   = $( '.theme-overlay .theme-info' ).prepend( $message );
    1188                         } else {
    1189                                 $button = $( '.theme-install[data-slug="' + response.slug + '"]' );
    1190                                 $card   = $button.closest( '.theme' ).addClass( 'theme-install-failed' ).append( $message );
    1191                         }
    1192                         $( '.wp-full-overlay' ).removeClass( 'customize-loading' );
     1161                if ( $document.find( 'body' ).hasClass( 'full-overlay-active' ) ) {
     1162                        $button = $( '.theme-install[data-slug="' + response.slug + '"]' );
     1163                        $card   = $( '.install-theme-info' ).prepend( $message );
    11931164                } else {
    1194                         if ( $document.find( 'body' ).hasClass( 'full-overlay-active' ) ) {
    1195                                 $button = $( '.theme-install[data-slug="' + response.slug + '"]' );
    1196                                 $card   = $( '.install-theme-info' ).prepend( $message );
    1197                         } else {
    1198                                 $card   = $( '[data-slug="' + response.slug + '"]' ).removeClass( 'focus' ).addClass( 'theme-install-failed' ).append( $message );
    1199                                 $button = $card.find( '.theme-install' );
    1200                         }
     1165                        $card   = $( '[data-slug="' + response.slug + '"]' ).removeClass( 'focus' ).addClass( 'theme-install-failed' ).append( $message );
     1166                        $button = $card.find( '.theme-install' );
    12011167                }
    12021168
  • trunk/src/wp-includes/class-wp-customize-manager.php

    r39133 r39140  
    296296                require_once( ABSPATH . WPINC . '/customize/class-wp-customize-nav-menus-panel.php' );
    297297
    298                 require_once( ABSPATH . WPINC . '/customize/class-wp-customize-themes-panel.php' );
    299298                require_once( ABSPATH . WPINC . '/customize/class-wp-customize-themes-section.php' );
    300299                require_once( ABSPATH . WPINC . '/customize/class-wp-customize-sidebar-section.php' );
    … …  
    352351                add_action( 'wp_ajax_customize_save',           array( $this, 'save' ) );
    353352                add_action( 'wp_ajax_customize_refresh_nonces', array( $this, 'refresh_nonces' ) );
    354                 add_action( 'wp_ajax_customize-load-themes',    array( $this, 'load_themes_ajax' ) );
    355353
    356354                add_action( 'customize_register',                 array( $this, 'register_controls' ) );
    … …  
    369367                // Export the settings to JS via the _wpCustomizeSettings variable.
    370368                add_action( 'customize_controls_print_footer_scripts', array( $this, 'customize_pane_settings' ), 1000 );
    371 
    372                 // Add theme update notices.
    373                 if ( current_user_can( 'install_themes' ) || current_user_can( 'update_themes' ) ) {
    374                         require_once( ABSPATH . '/wp-admin/includes/update.php' );
    375                         add_action( 'customize_controls_print_footer_scripts', 'wp_print_admin_notice_templates' );
    376                 }
    377369        }
    378370
    … …  
    27592751                        $control->enqueue();
    27602752                }
    2761                 if ( ! is_multisite() && ( current_user_can( 'install_themes' ) || current_user_can( 'update_themes' ) || current_user_can( 'delete_themes' ) ) ) {
    2762                         wp_enqueue_script( 'updates' );
    2763                 }
    27642753        }
    27652754
    … …  
    29762965                        'save' => wp_create_nonce( 'save-customize_' . $this->get_stylesheet() ),
    29772966                        'preview' => wp_create_nonce( 'preview-customize_' . $this->get_stylesheet() ),
    2978                         'switch-themes' => wp_create_nonce( 'switch-themes' ),
    29792967                );
    29802968
    … …  
    30503038                        'documentTitleTmpl' => $this->get_document_title_template(),
    30513039                        'previewableDevices' => $this->get_previewable_devices(),
    3052                         'l10n' => array(
    3053                                 'confirmDeleteTheme' => __( 'Are you sure you want to delete this theme?' ),
    3054                                 /* translators: %d is the number of theme search results, which cannot consider singular vs. plural forms */
    3055                                 'themeSearchResults' => __( '%d themes found' ),
    3056                                 /* translators: %d is the number of themes being displayed, which cannot consider singular vs. plural forms */
    3057                                 'announceThemeCount' => __( 'Displaying %d themes' ),
    3058                                 'announceThemeDetails' => __( 'Showing details for theme: %s' ),
    3059                         ),
    30603040                );
    30613041
    … …  
    31613141                /* Panel, Section, and Control Types */
    31623142                $this->register_panel_type( 'WP_Customize_Panel' );
    3163                 $this->register_panel_type( 'WP_Customize_Themes_Panel' );
    31643143                $this->register_section_type( 'WP_Customize_Section' );
    31653144                $this->register_section_type( 'WP_Customize_Sidebar_Section' );
    3166                 $this->register_section_type( 'WP_Customize_Themes_Section' );
    31673145                $this->register_control_type( 'WP_Customize_Color_Control' );
    31683146                $this->register_control_type( 'WP_Customize_Media_Control' );
    … …  
    31753153                $this->register_control_type( 'WP_Customize_Theme_Control' );
    31763154
    3177                 /* Themes (controls are loaded via ajax) */
    3178 
    3179                 $this->add_panel( new WP_Customize_Themes_Panel( $this, 'themes', array(
    3180                         'title'       => $this->theme()->display( 'Name' ),
    3181                         '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.' ),
    3182                         'capability'  => 'switch_themes',
    3183                         'priority'    => 0,
     3155                /* Themes */
     3156
     3157                $this->add_section( new WP_Customize_Themes_Section( $this, 'themes', array(
     3158                        'title'      => $this->theme()->display( 'Name' ),
     3159                        'capability' => 'switch_themes',
     3160                        'priority'   => 0,
    31843161                ) ) );
    3185 
    3186                 $this->add_section( new WP_Customize_Themes_Section( $this, 'installed_themes', array(
    3187                         'title'       => __( 'Installed' ),
    3188                         'text_before' => __( 'Your local site' ),
    3189                         'action'      => 'installed',
    3190                         'capability'  => 'switch_themes',
    3191                         'panel'       => 'themes',
    3192                         'priority'    => 0,
    3193                 ) ) );
    3194 
    3195                 if ( ! is_multisite() ) {
    3196                         $this->add_section( new WP_Customize_Themes_Section( $this, 'search_themes', array(
    3197                                 'title'       => __( 'Search themes&hellip;' ),
    3198                                 'text_before' => __( 'Browse all WordPress.org themes' ),
    3199                                 'action'      => 'search',
    3200                                 'capability'  => 'install_themes',
    3201                                 'panel'       => 'themes',
    3202                                 'priority'    => 5,
    3203                         ) ) );
    3204 
    3205                         $this->add_section( new WP_Customize_Themes_Section( $this, 'featured_themes', array(
    3206                                 'title'      => __( 'Featured' ),
    3207                                 'action'     => 'featured',
    3208                                 'capability' => 'install_themes',
    3209                                 'panel'      => 'themes',
    3210                                 'priority'   => 10,
    3211                         ) ) );
    3212 
    3213                         $this->add_section( new WP_Customize_Themes_Section( $this, 'popular_themes', array(
    3214                                 'title'      => __( 'Popular' ),
    3215                                 'action'     => 'popular',
    3216                                 'capability' => 'install_themes',
    3217                                 'panel'      => 'themes',
    3218                                 'priority'   => 15,
    3219                         ) ) );
    3220 
    3221                         $this->add_section( new WP_Customize_Themes_Section( $this, 'latest_themes', array(
    3222                                 'title'      => __( 'Latest' ),
    3223                                 'action'     => 'latest',
    3224                                 'capability' => 'install_themes',
    3225                                 'panel'      => 'themes',
    3226                                 'priority'   => 20,
    3227                         ) ) );
    3228 
    3229                         $this->add_section( new WP_Customize_Themes_Section( $this, 'feature_filter_themes', array(
    3230                                 'title'      => __( 'Feature Filter' ),
    3231                                 'action'     => 'feature_filter',
    3232                                 'capability' => 'install_themes',
    3233                                 'panel'      => 'themes',
    3234                                 'priority'   => 25,
    3235                         ) ) );
    3236 
    3237                         $this->add_section( new WP_Customize_Themes_Section( $this, 'favorites_themes', array(
    3238                                 'title'      => __( 'Favorites' ),
    3239                                 'action'     => 'favorites',
    3240                                 'capability' => 'install_themes',
    3241                                 'panel'      => 'themes',
    3242                                 'priority'   => 30,
    3243                         ) ) );
    3244                 }
    32453162
    32463163                // Themes Setting (unused - the theme is considerably more fundamental to the Customizer experience).
    … …  
    32483165                        'capability' => 'switch_themes',
    32493166                ) ) );
     3167
     3168                require_once( ABSPATH . 'wp-admin/includes/theme.php' );
     3169
     3170                // Theme Controls.
     3171
     3172                // Add a control for the active/original theme.
     3173                if ( ! $this->is_theme_active() ) {
     3174                        $themes = wp_prepare_themes_for_js( array( wp_get_theme( $this->original_stylesheet ) ) );
     3175                        $active_theme = current( $themes );
     3176                        $active_theme['isActiveTheme'] = true;
     3177                        $this->add_control( new WP_Customize_Theme_Control( $this, $active_theme['id'], array(
     3178                                'theme'    => $active_theme,
     3179                                'section'  => 'themes',
     3180                                'settings' => 'active_theme',
     3181                        ) ) );
     3182                }
     3183
     3184                $themes = wp_prepare_themes_for_js();
     3185                foreach ( $themes as $theme ) {
     3186                        if ( $theme['active'] || $theme['id'] === $this->original_stylesheet ) {
     3187                                continue;
     3188                        }
     3189
     3190                        $theme_id = 'theme_' . $theme['id'];
     3191                        $theme['isActiveTheme'] = false;
     3192                        $this->add_control( new WP_Customize_Theme_Control( $this, $theme_id, array(
     3193                                'theme'    => $theme,
     3194                                'section'  => 'themes',
     3195                                'settings' => 'active_theme',
     3196                        ) ) );
     3197                }
    32503198
    32513199                /* Site Identity */
    … …  
    37023650
    37033651        /**
    3704          * Load themes into the theme browsing/installation UI.
    3705          *
    3706          * @since 4.7.0
    3707          * @access public
    3708          */
    3709         public function load_themes_ajax() {
    3710                 check_ajax_referer( 'switch-themes', 'switch-themes-nonce' );
    3711 
    3712                 if ( ! current_user_can( 'switch_themes' ) ) {
    3713                         wp_die( -1 );
    3714                 }
    3715 
    3716                 if ( empty( $_POST['theme_action'] ) ) {
    3717                         wp_send_json_error( 'missing_theme_action' );
    3718                 }
    3719 
    3720                 if ( 'search' === $_POST['theme_action'] && ! array_key_exists( 'search', $_POST ) ) {
    3721                         wp_send_json_error( 'empty_search' );
    3722                 } elseif ( 'favorites' === $_POST['theme_action'] && ! array_key_exists( 'user', $_POST ) ) {
    3723                         wp_send_json_error( 'empty_user' );
    3724                 } elseif ( 'feature_filter' === $_POST['theme_action'] && ! array_key_exists( 'tags', $_POST ) ) {
    3725                         wp_send_json_error( 'no_features' );
    3726                 }
    3727 
    3728                 require_once( ABSPATH . 'wp-admin/includes/theme.php' );
    3729                 if ( 'installed' === $_POST['theme_action'] ) {
    3730                         $themes = array( 'themes' => wp_prepare_themes_for_js() );
    3731                         foreach ( $themes['themes'] as &$theme ) {
    3732                                 $theme['type'] = 'installed';
    3733                                 // Set active based on customized theme.
    3734                                 if ( $_POST['customized_theme'] === $theme['id'] ) {
    3735                                         $theme['active'] = true;
    3736                                 } else {
    3737                                         $theme['active'] = false;
    3738                                 }
    3739                         }
    3740                 } else {
    3741                         if ( ! current_user_can( 'install_themes' ) ) {
    3742                                 wp_die( -1 );
    3743                         }
    3744 
    3745                         // Arguments for all queries.
    3746                         $args = array(
    3747                                 'per_page' => 100,
    3748                                 'page' => absint( $_POST['page'] ),
    3749                                 'fields' => array(
    3750                                         'slug' => true,
    3751                                         'screenshot' => true,
    3752                                         'description' => true,
    3753                                         'requires' => true,
    3754                                         'rating' => true,
    3755                                         'downloaded' => true,
    3756                                         'downloadLink' => true,
    3757                                         'last_updated' => true,
    3758                                         'homepage' => true,
    3759                                         'num_ratings' => true,
    3760                                         'tags' => true,
    3761                                 ),
    3762                         );
    3763 
    3764                         // Specialized handling for each query.
    3765                         switch ( $_POST['theme_action'] ) {
    3766                                 case 'search':
    3767                                         $args['search'] = wp_unslash( $_POST['search'] );
    3768                                         break;
    3769                                 case 'favorites':
    3770                                         $args['user'] = wp_unslash( $_POST['user'] );
    3771                                 case 'featured':
    3772                                 case 'popular':
    3773                                         $args['browse'] = wp_unslash( $_POST['theme_action'] );
    3774                                         break;
    3775                                 case 'latest':
    3776                                         $args['browse'] = 'new';
    3777                                         break;
    3778                                 case 'feature_filter':
    3779                                         $args['tag'] = wp_unslash( $_POST['tags'] );
    3780                                         break;
    3781                         }
    3782 
    3783                         // Load themes from the .org API.
    3784                         $themes = themes_api( 'query_themes', $args );
    3785                         if ( is_wp_error( $themes ) ) {
    3786                                 wp_send_json_error();
    3787                         }
    3788 
    3789                         // This list matches the allowed tags in wp-admin/includes/theme-install.php.
    3790                         $themes_allowedtags = array('a' => array('href' => array(), 'title' => array(), 'target' => array()),
    3791                                 'abbr' => array('title' => array()), 'acronym' => array('title' => array()),
    3792                                 'code' => array(), 'pre' => array(), 'em' => array(), 'strong' => array(),
    3793                                 'div' => array(), 'p' => array(), 'ul' => array(), 'ol' => array(), 'li' => array(),
    3794                                 'h1' => array(), 'h2' => array(), 'h3' => array(), 'h4' => array(), 'h5' => array(), 'h6' => array(),
    3795                                 'img' => array('src' => array(), 'class' => array(), 'alt' => array())
    3796                         );
    3797 
    3798                         // Prepare a list of installed themes to check against before the loop.
    3799                         $installed_themes = array();
    3800                         $wp_themes = wp_get_themes();
    3801                         foreach ( $wp_themes as $theme ) {
    3802                                 $installed_themes[] = $theme->get_stylesheet();
    3803                         }
    3804                         $update_php = network_admin_url( 'update.php?action=install-theme' );
    3805                         foreach ( $themes->themes as &$theme ) {
    3806                                 $theme->install_url = add_query_arg( array(
    3807                                         'theme'    => $theme->slug,
    3808                                         '_wpnonce' => wp_create_nonce( 'install-theme_' . $theme->slug ),
    3809                                 ), $update_php );
    3810 
    3811                                 $theme->name        = wp_kses( $theme->name, $themes_allowedtags );
    3812                                 $theme->author      = wp_kses( $theme->author, $themes_allowedtags );
    3813                                 $theme->version     = wp_kses( $theme->version, $themes_allowedtags );
    3814                                 $theme->description = wp_kses( $theme->description, $themes_allowedtags );
    3815                                 $theme->tags        = implode( ', ', $theme->tags );
    3816                                 $theme->stars       = wp_star_rating( array( 'rating' => $theme->rating, 'type' => 'percent', 'number' => $theme->num_ratings, 'echo' => false ) );
    3817                                 $theme->num_ratings = number_format_i18n( $theme->num_ratings );
    3818                                 $theme->preview_url = set_url_scheme( $theme->preview_url );
    3819 
    3820                                 // Handle themes that are already installed as installed themes.
    3821                                 if ( in_array( $theme->slug, $installed_themes, true ) ) {
    3822                                         $theme->type = 'installed';
    3823                                 } else {
    3824                                         $theme->type = $_POST['theme_action'];
    3825                                 }
    3826 
    3827                                 // Set active based on customized theme.
    3828                                 if ( $_POST['customized_theme'] === $theme->slug ) {
    3829                                         $theme->active = true;
    3830                                 } else {
    3831                                         $theme->active = false;
    3832                                 }
    3833 
    3834                                 // Map available theme properties to installed theme properties.
    3835                                 $theme->id           = $theme->slug;
    3836                                 $theme->screenshot   = array( $theme->screenshot_url );
    3837                                 $theme->authorAndUri = $theme->author;
    3838                                 unset( $theme->slug );
    3839                                 unset( $theme->screenshot_url );
    3840                                 unset( $theme->author );
    3841                         } // End foreach().
    3842                 } // End if().
    3843                 wp_send_json_success( $themes );
    3844         }
    3845 
    3846 
    3847         /**
    38483652         * Callback for validating the header_textcolor value.
    38493653         *
  • trunk/src/wp-includes/customize/class-wp-customize-theme-control.php

    r38889 r39140  
    6363         */
    6464        public function content_template() {
    65                 /* translators: %s: theme name */
    66                 $details_label = sprintf( __( 'Details for theme: %s' ), '{{ data.theme.name }}' );
    67                 /* translators: %s: theme name */
    68                 $customize_label = sprintf( __( 'Customize theme: %s' ), '{{ data.theme.name }}' );
    69                 /* translators: %s: theme name */
    70                 $preview_label = sprintf( __( 'Live preview theme: %s' ), '{{ data.theme.name }}' );
    71                 /* translators: %s: theme name */
    72                 $install_label = sprintf( __( 'Install and preview theme: %s' ), '{{ data.theme.name }}' );
     65                $current_url = set_url_scheme( 'http://' . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI'] );
     66                $active_url  = esc_url( remove_query_arg( 'customize_theme', $current_url ) );
     67                $preview_url = esc_url( add_query_arg( 'customize_theme', '__THEME__', $current_url ) ); // Token because esc_url() strips curly braces.
     68                $preview_url = str_replace( '__THEME__', '{{ data.theme.id }}', $preview_url );
    7369                ?>
    74                 <# if ( data.theme.active ) { #>
    75                         <div class="theme active" tabindex="0" aria-describedby="{{ data.section }}-{{ data.theme.id }}-action {{ data.theme.id }}-name">
     70                <# if ( data.theme.isActiveTheme ) { #>
     71                        <div class="theme active" tabindex="0" data-preview-url="<?php echo esc_attr( $active_url ); ?>" aria-describedby="{{ data.theme.id }}-action {{ data.theme.id }}-name">
    7672                <# } else { #>
    77                         <div class="theme" tabindex="0" aria-describedby="{{ data.section }}-{{ data.theme.id }}-action {{ data.theme.id }}-name">
     73                        <div class="theme" tabindex="0" data-preview-url="<?php echo esc_attr( $preview_url ); ?>" aria-describedby="{{ data.theme.id }}-action {{ data.theme.id }}-name">
    7874                <# } #>
    7975
    80                         <# if ( data.theme.screenshot && data.theme.screenshot[0] ) { #>
     76                        <# if ( data.theme.screenshot[0] ) { #>
    8177                                <div class="theme-screenshot">
    8278                                        <img data-src="{{ data.theme.screenshot[0] }}" alt="" />
    … …  
    8682                        <# } #>
    8783
    88                         <span class="more-details theme-details" id="{{ data.section }}-{{ data.theme.id }}-action" aria-label="<?php echo esc_attr( $details_label ); ?>"><?php _e( 'Theme Details' ); ?></span>
    89 
    90 
    91                         <# if ( 'installed' === data.theme.type && data.theme.hasUpdate ) { #>
    92                                 <div class="update-message notice inline notice-warning notice-alt" data-slug="{{ data.theme.id }}"><p><?php printf( __( 'New version available. %s' ), '<button class="button-link update-theme" type="button">' . __( 'Update now' ) . '</button>' ); ?></p></div>
     84                        <# if ( data.theme.isActiveTheme ) { #>
     85                                <span class="more-details" id="{{ data.theme.id }}-action"><?php _e( 'Customize' ); ?></span>
     86                        <# } else { #>
     87                                <span class="more-details" id="{{ data.theme.id }}-action"><?php _e( 'Live Preview' ); ?></span>
    9388                        <# } #>
    9489
    95                         <# if ( data.theme.active ) { #>
    96                                 <h3 class="theme-name" id="{{ data.section }}-{{ data.theme.id }}-name">
     90                        <div class="theme-author"><?php printf( __( 'By %s' ), '{{ data.theme.author }}' ); ?></div>
     91
     92                        <# if ( data.theme.isActiveTheme ) { #>
     93                                <h3 class="theme-name" id="{{ data.theme.id }}-name">
    9794                                        <?php
    9895                                        /* translators: %s: theme name */
    99                                         printf( __( '<span>Current:</span> %s' ), '{{ data.theme.name }}' );
     96                                        printf( __( '<span>Active:</span> %s' ), '{{{ data.theme.name }}}' );
    10097                                        ?>
    10198                                </h3>
     99                        <# } else { #>
     100                                <h3 class="theme-name" id="{{ data.theme.id }}-name">{{{ data.theme.name }}}</h3>
    102101                                <div class="theme-actions">
    103                                         <button type="button" class="button button-primary customize-theme" aria-label="<?php echo esc_attr( $customize_label ); ?>"><?php _e( 'Customize' ); ?></button>
    104                                 </div>
    105                                 <div class="notice notice-success notice-alt"><p><?php _e( 'Installed' ); ?></p></div>
    106                         <# } else if ( 'installed' === data.theme.type ) { #>
    107                                 <h3 class="theme-name" id="{{ data.section }}-{{ data.theme.id }}-name">{{ data.theme.name }}</h3>
    108                                 <div class="theme-actions">
    109                                         <button type="button" class="button button-primary preview-theme" aria-label="<?php echo esc_attr( $preview_label ); ?>" data-slug="{{ data.theme.id }}"><?php _e( 'Live Preview' ); ?></span>
    110                                 </div>
    111                                 <div class="notice notice-success notice-alt"><p><?php _e( 'Installed' ); ?></p></div>
    112                         <# } else { #>
    113                                 <h3 class="theme-name" id="{{ data.section }}-{{ data.theme.id }}-name">{{ data.theme.name }}</h3>
    114                                 <div class="theme-actions">
    115                                         <button type="button" class="button button-primary theme-install preview" aria-label="<?php echo esc_attr( $install_label ); ?>" data-slug="{{ data.theme.id }}" data-name="{{ data.theme.name }}"><?php _e( 'Install & Preview' ); ?></button>
     102                                        <button type="button" class="button theme-details"><?php _e( 'Theme Details' ); ?></button>
    116103                                </div>
    117104                        <# } #>
  • trunk/src/wp-includes/customize/class-wp-customize-themes-section.php

    r38813 r39140  
    1111 * Customize Themes Section class.
    1212 *
    13  * A UI container for theme controls, which are displayed in tabbed sections.
     13 * A UI container for theme controls, which behaves like a backwards Panel.
    1414 *
    1515 * @since 4.2.0
    … …  
    2929
    3030        /**
    31          * Theme section action.
     31         * Render the themes section, which behaves like a panel.
    3232         *
    33          * Defines the type of themes to load (installed, featured, latest, etc.).
    34          *
    35          * @since 4.7.0
    36          * @access public
    37          * @var string
    38          */
    39         public $action = '';
    40 
    41         /**
    42          * Text before theme section heading.
    43          *
    44          * @since 4.7.0
    45          * @access public
    46          * @var string
    47          */
    48         public $text_before = '';
    49 
    50         /**
    51          * Get section parameters for JS.
    52          *
    53          * @since 4.7.0
    54          * @access public
    55          * @return array Exported parameters.
    56          */
    57         public function json() {
    58                 $exported = parent::json();
    59                 $exported['action'] = $this->action;
    60                 $exported['text_before'] = $this->text_before;
    61 
    62                 return $exported;
    63         }
    64 
    65         /**
    66          * Render a themes section as a JS template.
    67          *
    68          * The template is only rendered by PHP once, so all actions are prepared at once on the server side.
    69          *
    70          * @since 4.7.0
     33         * @since 4.2.0
    7134         * @access protected
    7235         */
    73         protected function render_template() {
     36        protected function render() {
     37                $classes = 'accordion-section control-section control-section-' . $this->type;
    7438                ?>
    75                 <li id="accordion-section-{{ data.id }}" class="theme-section">
    76                         <# if ( '' !== data.text_before ) { #>
    77                                 <p class="customize-themes-text-before">{{ data.text_before }}</p>
    78                         <# } #>
    79                         <# if ( 'search' === data.action ) { #>
    80                                 <div class="search-form customize-themes-section-title themes-section-search_themes">
    81                                         <label class="screen-reader-text" for="wp-filter-search-input">{{ data.title }}</label>
    82                                         <input placeholder="{{ data.title }}" type="text" aria-describedby="live-search-desc" id="wp-filter-search-input" class="wp-filter-search">
    83                                         <span id="live-search-desc" class="screen-reader-text"><?php _e( 'The search results will be updated as you type.' ); ?></span>
    84                                 </div>
    85                         <# } else { #>
    86                                 <# if ( 'favorites' === data.action || 'feature_filter' === data.action ) {
    87                                         var attr = ' aria-expanded="false"';
     39                <li id="accordion-section-<?php echo esc_attr( $this->id ); ?>" class="<?php echo esc_attr( $classes ); ?>">
     40                        <h3 class="accordion-section-title">
     41                                <?php
     42                                if ( $this->manager->is_theme_active() ) {
     43                                        echo '<span class="customize-action">' . __( 'Active theme' ) . '</span> ' . $this->title;
    8844                                } else {
    89                                         var attr = '';
    90                                 } #>
    91                                 <button type="button" class="customize-themes-section-title themes-section-{{ data.id }}"{{{ attr }}}>{{ data.title }}</button>
    92                         <# } #>
    93                         <?php if ( ! current_user_can( 'install_themes' ) || is_multisite() ) : ?>
    94                                 <# if ( 'installed' === data.action ) { #>
    95                                         <p class="themes-filter-container">
    96                                                 <label for="themes-filter">
    97                                                         <span class="screen-reader-text"><?php _e( 'Search installed themes&hellip;' ); ?></span>
    98                                                         <input type="text" id="themes-filter" placeholder="<?php esc_attr_e( 'Search installed themes&hellip;' ); ?>" />
    99                                                 </label>
    100                                         </p>
    101                                 <# } #>
    102                         <?php endif; ?>
    103                         <# if ( 'favorites' === data.action ) { #>
    104                                 <div class="favorites-form filter-details">
    105                                         <p class="install-help"><?php _e( 'If you have marked themes as favorites on WordPress.org, you can browse them here.' ); ?></p>
    106                                         <p>
    107                                                 <label for="wporg-username-input"><?php _e( 'Your WordPress.org username:' ); ?></label>
    108                                                 <input type="search" id="wporg-username-input" value="">
    109                                                 <button type="button" class="button button-secondary favorites-form-submit"><?php _e( 'Get Favorites' ); ?></button>
    110                                         </p>
    111                                 </div>
    112                         <# } else if ( 'feature_filter' === data.action ) { #>
    113                                 <div class="filter-drawer filter-details">
     45                                        echo '<span class="customize-action">' . __( 'Previewing theme' ) . '</span> ' . $this->title;
     46                                }
     47                                ?>
     48
     49                                <?php if ( count( $this->controls ) > 0 ) : ?>
     50                                        <button type="button" class="button change-theme" tabindex="0"><?php _ex( 'Change', 'theme' ); ?></button>
     51                                <?php endif; ?>
     52                        </h3>
     53                        <div class="customize-themes-panel control-panel-content themes-php">
     54                                <h3 class="accordion-section-title customize-section-title">
     55                                        <span class="customize-action"><?php _e( 'Customizing' ); ?></span>
     56                                        <?php _e( 'Themes' ); ?>
     57                                        <span class="title-count theme-count"><?php echo count( $this->controls ) + 1 /* Active theme */; ?></span>
     58                                </h3>
     59                                <h3 class="accordion-section-title customize-section-title">
    11460                                        <?php
    115                                         $feature_list = get_theme_feature_list();
    116                                         foreach ( $feature_list as $feature_name => $features ) {
    117                                                 echo '<fieldset class="filter-group">';
    118                                                 $feature_name = esc_html( $feature_name );
    119                                                 echo '<legend><button type="button" class="button-link" aria-expanded="false">' . $feature_name . '</button></legend>';
    120                                                 echo '<div class="filter-group-feature">';
    121                                                 foreach ( $features as $feature => $feature_name ) {
    122                                                         $feature = esc_attr( $feature );
    123                                                         echo '<input type="checkbox" id="filter-id-' . $feature . '" value="' . $feature . '" /> ';
    124                                                         echo '<label for="filter-id-' . $feature . '">' . $feature_name . '</label><br>';
    125                                                 }
    126                                                 echo '</div>';
    127                                                 echo '</fieldset>';
     61                                        if ( $this->manager->is_theme_active() ) {
     62                                                echo '<span class="customize-action">' . __( 'Active theme' ) . '</span> ' . $this->title;
     63                                        } else {
     64                                                echo '<span class="customize-action">' . __( 'Previewing theme' ) . '</span> ' . $this->title;
    12865                                        }
    12966                                        ?>
    130                                 </div>
    131                         <# } #>
    132                         <div class="customize-themes-section themes-section-{{ data.id }} control-section-content themes-php">
     67                                        <button type="button" class="button customize-theme"><?php _e( 'Customize' ); ?></button>
     68                                </h3>
     69
    13370                                <div class="theme-overlay" tabindex="0" role="dialog" aria-label="<?php esc_attr_e( 'Theme Details' ); ?>"></div>
     71
     72                                <div id="customize-container"></div>
     73                                <?php if ( count( $this->controls ) > 4 ) : ?>
     74                                        <p><label for="themes-filter">
     75                                                <span class="screen-reader-text"><?php _e( 'Search installed themes&hellip;' ); ?></span>
     76                                                <input type="text" id="themes-filter" placeholder="<?php esc_attr_e( 'Search installed themes&hellip;' ); ?>" />
     77                                        </label></p>
     78                                <?php endif; ?>
    13479                                <div class="theme-browser rendered">
    135                                         <div class="error unexpected-error" style="display: none; "><p><?php _e( 'An unexpected error occurred. Something may be wrong with WordPress.org or this server&#8217;s configuration. If you continue to have problems, please try the <a href="https://wordpress.org/support/">support forums</a>.' ); ?></p></div>
    136                                         <ul class="themes">
     80                                        <ul class="themes accordion-section-content">
    13781                                        </ul>
    138                                         <p class="no-themes"><?php _e( 'No themes found. Try a different search.' ); ?></p>
    139                                         <p class="spinner"></p>
    14082                                </div>
    14183                        </div>
  • trunk/tests/phpunit/tests/customize/manager.php

    r38928 r39140  
    15071507                $this->assertNotEmpty( $data );
    15081508
    1509                 $this->assertEqualSets( array( 'theme', 'url', 'browser', 'panels', 'sections', 'nonce', 'autofocus', 'documentTitleTmpl', 'previewableDevices', 'changeset', 'timeouts', 'l10n' ), array_keys( $data ) );
     1509                $this->assertEqualSets( array( 'theme', 'url', 'browser', 'panels', 'sections', 'nonce', 'autofocus', 'documentTitleTmpl', 'previewableDevices', 'changeset', 'timeouts' ), array_keys( $data ) );
    15101510                $this->assertEquals( $autofocus, $data['autofocus'] );
    15111511                $this->assertArrayHasKey( 'save', $data['nonce'] );
Note: See TracChangeset for help on using the changeset viewer.