Changeset 39140
- Timestamp:
- 11/04/2016 03:53:01 PM (8 years ago)
- Location:
- trunk
- Files:
-
- 1 deleted
- 11 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/src/wp-admin/css/customize-controls.css
r38948 r39140 309 309 310 310 #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 { 312 313 -webkit-transform: none; 313 314 -ms-transform: none; … … 315 316 } 316 317 318 #customize-theme-controls .customize-themes-panel.customize-pane-child, 317 319 .section-open #customize-theme-controls .customize-pane-parent, 318 320 .in-sub-panel #customize-theme-controls .customize-pane-parent, 319 321 .section-open #customize-info, 320 322 .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 { 322 326 visibility: hidden; 323 327 height: 0; … … 330 334 .section-open #customize-theme-controls .customize-pane-parent.busy, 331 335 .in-sub-panel #customize-theme-controls .customize-pane-parent.busy, 336 .in-themes-panel #customize-theme-controls .customize-pane-parent.busy, 332 337 .section-open #customize-info.busy, 333 338 .in-sub-panel #customize-info.busy, 339 .in-themes-panel #customize-info.busy, 334 340 .busy.section-open.in-sub-panel #customize-theme-controls .customize-pane-child.current-panel, 335 341 #customize-theme-controls .customize-pane-child.open, … … 339 345 height: auto; 340 346 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%); 341 354 } 342 355 … … 1151 1164 } 1152 1165 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 { 1159 1168 cursor: default; 1160 1169 background: #fff; … … 1163 1172 border-bottom: 1px solid #ddd; 1164 1173 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; 1168 1175 } 1169 1176 … … 1173 1180 } 1174 1181 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, 1176 1197 #customize-controls .customize-section-title span.customize-action { 1177 1198 font-size: 13px; … … 1180 1201 } 1181 1202 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 { 1183 1205 position: absolute; 1184 1206 right: 10px; … … 1188 1210 } 1189 1211 1190 #customize- theme-controls .control-panel-themes > .accordion-section-title:after{1212 #customize-controls .control-section-themes .accordion-section-title:before { 1191 1213 display: none; 1192 1214 } 1193 1215 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; 1436 1219 -webkit-box-sizing: border-box; 1437 1220 -moz-box-sizing: border-box; 1438 1221 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 { 1508 1238 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; 1575 1244 } 1576 1245 … … 1582 1251 } 1583 1252 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 1684 1253 .wp-customizer .theme-browser .themes { 1685 1254 padding-bottom: 8px; 1686 1255 } 1687 1256 1257 .wp-customizer .theme-browser .theme { 1258 margin: 0; 1259 width: 100%; 1260 } 1261 1688 1262 .wp-customizer .theme-browser .theme .theme-actions { 1263 -ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=100)"; 1689 1264 opacity: 1; 1690 1265 } … … 1703 1278 line-height: 1.5; 1704 1279 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; 1705 1289 } 1706 1290 … … 1719 1303 } 1720 1304 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 1729 1305 .wp-customizer .theme-overlay .theme-backdrop { 1730 1306 background: rgba( 238, 238, 238, 0.75 ); 1731 1307 position: fixed; 1732 1308 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;1742 1309 } 1743 1310 … … 1748 1315 bottom: 45px; 1749 1316 z-index: 120; 1317 max-width: 1740px; /* To ensure that theme screenshots are not displayed larger than 880px wide. */ 1750 1318 } 1751 1319 1752 1320 .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 } 1772 1329 1773 1330 /* Small Screens */ -
trunk/src/wp-admin/css/themes.css
r38948 r39140 571 571 margin: 0 30px 0 0; 572 572 width: 55%; 573 max-width: 1200px; /* Recommended theme screenshot width, set here to avoid stretching */573 max-width: 880px; 574 574 text-align: center; 575 575 } -
trunk/src/wp-admin/customize.php
r39132 r39140 110 110 111 111 <script type="text/javascript"> 112 var ajaxurl = <?php echo wp_json_encode( admin_url( 'admin-ajax.php', 'relative' ) ); ?>, 113 pagenow = 'customize'; 112 var ajaxurl = <?php echo wp_json_encode( admin_url( 'admin-ajax.php', 'relative' ) ); ?>; 114 113 </script> 115 114 -
trunk/src/wp-admin/includes/theme.php
r38951 r39140 610 610 */ 611 611 function 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 ); 612 614 ?> 613 615 <script type="text/html" id="tmpl-customize-themes-details-view"> … … 621 623 <div class="theme-about wp-clearfix"> 622 624 <div class="theme-screenshots"> 623 <# if ( data.screenshot && data.screenshot[0] ) { #>625 <# if ( data.screenshot[0] ) { #> 624 626 <div class="screenshot"><img src="{{ data.screenshot[0] }}" alt="" /></div> 625 627 <# } else { #> … … 634 636 <h2 class="theme-name">{{{ data.name }}}<span class="theme-version"><?php printf( __( 'Version: %s' ), '{{ data.version }}' ); ?></span></h2> 635 637 <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 651 638 <p class="theme-description">{{{ data.description }}}</p> 652 639 … … 654 641 <p class="parent-theme"><?php printf( __( 'This is a child theme of %s.' ), '<strong>{{{ data.parent }}}</strong>' ); ?></p> 655 642 <# } #> 643 656 644 <# 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> 658 646 <# } #> 659 647 </div> 660 648 </div> 661 649 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 <# } #> 677 661 </div> 678 662 </script> -
trunk/src/wp-admin/includes/update-core.php
r39064 r39140 711 711 'wp-includes/locale.php', 712 712 '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. 713 714 ); 714 715 -
trunk/src/wp-admin/js/customize-controls.js
r39131 r39140 1 /* global _wpCustomizeHeader, _wpCustomizeBackground, _wpMediaViewsL10n, MediaElementPlayer , console*/1 /* global _wpCustomizeHeader, _wpCustomizeBackground, _wpMediaViewsL10n, MediaElementPlayer */ 2 2 (function( exports, $ ){ 3 3 var Container, focus, normalizedTransitionendEventName, api = wp.customize; … … 865 865 container = $( '#customize-theme-controls' ); 866 866 867 // Watch for changes to the panel state .867 // Watch for changes to the panel state 868 868 inject = function ( panelId ) { 869 869 var parentContainer; 870 870 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 872 872 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 874 874 panel.deferred.embedded.done( function () { 875 875 parentContainer = panel.contentContainer; … … 896 896 }; 897 897 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 899 899 }, 900 900 … … 1063 1063 * wp.customize.ThemesSection 1064 1064 * 1065 * Custom section for themes that loads themes by category, and also1066 * 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. 1067 1067 * 1068 1068 * @constructor … … 1076 1076 screenshotQueue: null, 1077 1077 $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 ); 1115 1085 }, 1116 1086 … … 1146 1116 }); 1147 1117 1148 _.bindAll( this, 'renderScreenshots' , 'loadMore', 'checkTerm', 'filtersChecked');1118 _.bindAll( this, 'renderScreenshots' ); 1149 1119 }, 1150 1120 … … 1153 1123 * 1154 1124 * Ignore the active states' of the contained theme controls, and just 1155 * use the section's own active state instead. This prevents empty search1156 * results for theme sections from causingthe 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. 1157 1127 * 1158 1128 * @since 4.2.0 … … 1170 1140 var section = this; 1171 1141 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 { 1178 1152 section.expand(); 1179 1153 } 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 } );1200 1154 }); 1201 1155 1202 1156 // 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 1204 1164 section.previousTheme(); 1205 1165 }); 1206 1166 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 1208 1174 section.nextTheme(); 1209 1175 }); 1210 1176 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 1212 1184 section.closeDetails(); 1213 1185 }); 1214 1186 1215 1187 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 ) { 1219 1189 var count, 1220 1190 term = event.currentTarget.value.toLowerCase().trim().replace( '-', ' ' ), … … 1228 1198 1229 1199 // Update theme count. 1230 count = section.cont entContainer.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; 1241 1211 } 1242 1212 }); 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 );1282 1213 }); 1283 1214 }, … … 1291 1222 * @param {Object} args 1292 1223 * @param {Boolean} args.unchanged 1293 * @param { Function} args.completeCallback1224 * @param {Callback} args.completeCallback 1294 1225 */ 1295 1226 onChangeExpanded: function ( expanded, args ) { … … 1304 1235 1305 1236 // 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' ) ) { 1321 1245 // Collapse any sibling sections/panels 1322 1246 api.section.each( function ( otherSection ) { 1323 if ( otherSection !== section) {1247 if ( otherSection !== panel ) { 1324 1248 otherSection.collapse( { duration: args.duration } ); 1325 1249 } 1326 1250 }); 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(); 1450 1265 } 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(); 1455 1281 } 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' ); 1578 1286 } 1579 1287 }, … … 1587 1295 var section = this; 1588 1296 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? 1598 1303 if ( ! section.screenshotQueue.length ) { 1599 1304 return; … … 1631 1336 1632 1337 /** 1633 * Update the number of themes in the section.1634 *1635 * @since 4.7.01636 */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 /**1658 1338 * Advance the modal to the next theme. 1659 1339 * … … 1675 1355 */ 1676 1356 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 ); 1679 1359 next = control.container.next( 'li.customize-control-theme' ); 1680 1360 if ( ! next.length ) { 1681 1361 return false; 1682 1362 } 1683 next = next[0].id.replace( 'customize-control- theme-' + section.params.action, section.params.action + '_theme' );1363 next = next[0].id.replace( 'customize-control-', '' ); 1684 1364 control = api.control( next ); 1685 1365 … … 1707 1387 */ 1708 1388 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 ); 1711 1391 previous = control.container.prev( 'li.customize-control-theme' ); 1712 1392 if ( ! previous.length ) { 1713 1393 return false; 1714 1394 } 1715 previous = previous[0].id.replace( 'customize-control- theme-' + section.params.action, section.params.action + '_theme' );1395 previous = previous[0].id.replace( 'customize-control-', '' ); 1716 1396 control = api.control( previous ); 1717 1397 … … 1734 1414 1735 1415 /** 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 /** 1736 1467 * Render & show the theme details for a given theme model. 1737 1468 * … … 1741 1472 */ 1742 1473 showDetails: function ( theme, callback ) { 1743 var section = this ;1474 var section = this, link; 1744 1475 callback = callback || function(){}; 1745 1476 section.currentTheme = theme.id; … … 1750 1481 section.containFocus( section.overlay ); 1751 1482 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 } ); 1753 1499 callback(); 1754 1500 }, … … 1762 1508 $( 'body' ).removeClass( 'modal-open' ); 1763 1509 this.overlay.fadeOut( 'fast' ); 1764 api.control( this.params.action + '_theme_' + this.currentTheme ).container.find( '.theme').focus();1510 api.control( 'theme_' + this.currentTheme ).focus(); 1765 1511 }, 1766 1512 … … 1842 1588 if ( ! panel.contentContainer.parent().is( panel.headContainer ) ) { 1843 1589 container.append( panel.contentContainer ); 1844 }1845 panel.renderContent();1590 panel.renderContent(); 1591 } 1846 1592 1847 1593 panel.deferred.embedded.resolve(); … … 2035 1781 } 2036 1782 }); 2037 2038 2039 /**2040 * wp.customize.ThemesPanel2041 *2042 * Custom section for themes that displays without the customize preview.2043 *2044 * @constructor2045 * @augments wp.customize.Panel2046 * @augments wp.customize.Container2047 */2048 api.ThemesPanel = api.Panel.extend({2049 installingThemes: [],2050 2051 /**2052 * @since 4.7.02053 */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 state2094 *2095 * @since 4.7.02096 *2097 * @param {Boolean} expanded2098 * @param {Object} args2099 * @param {Boolean} args.unchanged2100 * @param {Function} args.completeCallback2101 */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 changes2108 if ( args.unchanged ) {2109 if ( args.completeCallback ) {2110 args.completeCallback();2111 }2112 return;2113 }2114 2115 // Note: there is a second argument 'args' passed2116 var panel = this,2117 overlay = panel.headContainer.closest( '.wp-full-overlay' );2118 2119 if ( expanded ) {2120 overlay2121 .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 overlay2128 .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.02137 */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.previewer2181 } );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: slug2200 } );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.02213 *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.uuid2227 }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.02264 */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.02287 */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: theme2323 } );2324 2325 // Close modal and focus the section.2326 section.closeDetails();2327 section.focus();2328 }2329 2330 });2331 2332 1783 2333 1784 /** … … 2645 2096 * @param {Object} args 2646 2097 * @param {Number} args.duration 2647 * @param { Function} args.completeCallback2098 * @param {Callback} args.completeCallback 2648 2099 */ 2649 2100 onChangeActive: function ( active, args ) { … … 3816 3267 3817 3268 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 }, 3819 3294 3820 3295 /** … … 3840 3315 3841 3316 // 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' ) ) { 3843 3318 return; 3844 3319 } 3845 3320 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 3846 3329 event.preventDefault(); // Keep this AFTER the key filter above 3330 3847 3331 api.section( control.section() ).showDetails( control.params.theme ); 3848 3332 }); … … 3855 3339 $screenshot.attr( 'src', source ); 3856 3340 } 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. 3863 3346 * 3864 3347 * @since 4.2.0 … … 3876 3359 control.deactivate(); 3877 3360 } 3878 },3879 3880 /**3881 * Rerender the theme from its JS template with the installed type.3882 *3883 * @since 4.7.03884 */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 content3894 control.container.trigger( 'render-screenshot' );3895 3361 } 3896 3362 }); … … 4585 4051 theme: api.ThemeControl 4586 4052 }; 4587 api.panelConstructor = { 4588 themes: api.ThemesPanel 4589 }; 4053 api.panelConstructor = {}; 4590 4054 api.sectionConstructor = { 4591 4055 themes: api.ThemesSection … … 4705 4169 // Sort the sections within each panel 4706 4170 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 4711 4171 var sections = panel.sections(), 4712 4172 sectionHeadContainers = _.pluck( sections, 'headContainer' ); … … 5350 4810 collapsedObject = expandedControls[0] || expandedSections[0] || expandedPanels[0]; 5351 4811 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 }5362 4812 collapsedObject.collapse(); 5363 4813 event.preventDefault(); -
trunk/src/wp-admin/js/updates.js
r39099 r39140 182 182 $notice.replaceWith( $adminNotice ); 183 183 } 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 ); 189 185 } 190 186 … … 931 927 $notice = $( '[data-slug="' + args.slug + '"]' ).find( '.update-message' ).removeClass( 'notice-error' ).addClass( 'updating-message notice-warning' ).find( 'p' ); 932 928 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 944 929 } else { 945 930 $notice = $( '#update-theme' ).closest( '.notice' ).removeClass( 'notice-large' ); … … 984 969 $notice, newText; 985 970 986 if ( 'customize' === pagenow ) {987 $theme = wp.customize.control( 'installed_theme_' + response.slug ).container;988 }989 990 971 if ( 'themes-network' === pagenow ) { 991 972 $notice = $theme.find( '.update-message' ); … … 1040 1021 if ( wp.updates.maybeHandleCredentialError( response, 'update-theme' ) ) { 1041 1022 return; 1042 }1043 1044 if ( 'customize' === pagenow ) {1045 $theme = wp.customize.control( 'installed_theme_' + response.slug ).container;1046 1023 } 1047 1024 … … 1182 1159 } 1183 1160 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 ); 1193 1164 } 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' ); 1201 1167 } 1202 1168 -
trunk/src/wp-includes/class-wp-customize-manager.php
r39133 r39140 296 296 require_once( ABSPATH . WPINC . '/customize/class-wp-customize-nav-menus-panel.php' ); 297 297 298 require_once( ABSPATH . WPINC . '/customize/class-wp-customize-themes-panel.php' );299 298 require_once( ABSPATH . WPINC . '/customize/class-wp-customize-themes-section.php' ); 300 299 require_once( ABSPATH . WPINC . '/customize/class-wp-customize-sidebar-section.php' ); … … 352 351 add_action( 'wp_ajax_customize_save', array( $this, 'save' ) ); 353 352 add_action( 'wp_ajax_customize_refresh_nonces', array( $this, 'refresh_nonces' ) ); 354 add_action( 'wp_ajax_customize-load-themes', array( $this, 'load_themes_ajax' ) );355 353 356 354 add_action( 'customize_register', array( $this, 'register_controls' ) ); … … 369 367 // Export the settings to JS via the _wpCustomizeSettings variable. 370 368 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 }377 369 } 378 370 … … 2759 2751 $control->enqueue(); 2760 2752 } 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 }2764 2753 } 2765 2754 … … 2976 2965 'save' => wp_create_nonce( 'save-customize_' . $this->get_stylesheet() ), 2977 2966 'preview' => wp_create_nonce( 'preview-customize_' . $this->get_stylesheet() ), 2978 'switch-themes' => wp_create_nonce( 'switch-themes' ),2979 2967 ); 2980 2968 … … 3050 3038 'documentTitleTmpl' => $this->get_document_title_template(), 3051 3039 '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 ),3060 3040 ); 3061 3041 … … 3161 3141 /* Panel, Section, and Control Types */ 3162 3142 $this->register_panel_type( 'WP_Customize_Panel' ); 3163 $this->register_panel_type( 'WP_Customize_Themes_Panel' );3164 3143 $this->register_section_type( 'WP_Customize_Section' ); 3165 3144 $this->register_section_type( 'WP_Customize_Sidebar_Section' ); 3166 $this->register_section_type( 'WP_Customize_Themes_Section' );3167 3145 $this->register_control_type( 'WP_Customize_Color_Control' ); 3168 3146 $this->register_control_type( 'WP_Customize_Media_Control' ); … … 3175 3153 $this->register_control_type( 'WP_Customize_Theme_Control' ); 3176 3154 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, 3184 3161 ) ) ); 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…' ),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 }3245 3162 3246 3163 // Themes Setting (unused - the theme is considerably more fundamental to the Customizer experience). … … 3248 3165 'capability' => 'switch_themes', 3249 3166 ) ) ); 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 } 3250 3198 3251 3199 /* Site Identity */ … … 3702 3650 3703 3651 /** 3704 * Load themes into the theme browsing/installation UI.3705 *3706 * @since 4.7.03707 * @access public3708 */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 /**3848 3652 * Callback for validating the header_textcolor value. 3849 3653 * -
trunk/src/wp-includes/customize/class-wp-customize-theme-control.php
r38889 r39140 63 63 */ 64 64 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 ); 73 69 ?> 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"> 76 72 <# } 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"> 78 74 <# } #> 79 75 80 <# if ( data.theme.screenshot && data.theme.screenshot[0] ) { #>76 <# if ( data.theme.screenshot[0] ) { #> 81 77 <div class="theme-screenshot"> 82 78 <img data-src="{{ data.theme.screenshot[0] }}" alt="" /> … … 86 82 <# } #> 87 83 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> 93 88 <# } #> 94 89 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"> 97 94 <?php 98 95 /* translators: %s: theme name */ 99 printf( __( '<span> Current:</span> %s' ), '{{ data.theme.name}}' );96 printf( __( '<span>Active:</span> %s' ), '{{{ data.theme.name }}}' ); 100 97 ?> 101 98 </h3> 99 <# } else { #> 100 <h3 class="theme-name" id="{{ data.theme.id }}-name">{{{ data.theme.name }}}</h3> 102 101 <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> 116 103 </div> 117 104 <# } #> -
trunk/src/wp-includes/customize/class-wp-customize-themes-section.php
r38813 r39140 11 11 * Customize Themes Section class. 12 12 * 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. 14 14 * 15 15 * @since 4.2.0 … … 29 29 30 30 /** 31 * Theme section action.31 * Render the themes section, which behaves like a panel. 32 32 * 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 71 34 * @access protected 72 35 */ 73 protected function render_template() { 36 protected function render() { 37 $classes = 'accordion-section control-section control-section-' . $this->type; 74 38 ?> 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; 88 44 } 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…' ); ?></span> 98 <input type="text" id="themes-filter" placeholder="<?php esc_attr_e( 'Search installed themes…' ); ?>" /> 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"> 114 60 <?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; 128 65 } 129 66 ?> 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 133 70 <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…' ); ?></span> 76 <input type="text" id="themes-filter" placeholder="<?php esc_attr_e( 'Search installed themes…' ); ?>" /> 77 </label></p> 78 <?php endif; ?> 134 79 <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’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"> 137 81 </ul> 138 <p class="no-themes"><?php _e( 'No themes found. Try a different search.' ); ?></p>139 <p class="spinner"></p>140 82 </div> 141 83 </div> -
trunk/tests/phpunit/tests/customize/manager.php
r38928 r39140 1507 1507 $this->assertNotEmpty( $data ); 1508 1508 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 ) ); 1510 1510 $this->assertEquals( $autofocus, $data['autofocus'] ); 1511 1511 $this->assertArrayHasKey( 'save', $data['nonce'] );
Note: See TracChangeset
for help on using the changeset viewer.