Make WordPress Core

Changeset 39140


Ignore:
Timestamp:
11/04/2016 03:53:01 PM (8 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.