Changeset 38813
- Timestamp:
- 10/19/2016 03:19:13 AM (9 years ago)
- Location:
- trunk
- Files:
-
- 1 added
- 10 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/src/wp-admin/css/customize-controls.css
r38709 r38813 272 272 273 273 #customize-theme-controls .customize-pane-child.open, 274 #customize-theme-controls .customize-pane-child.current-panel, 275 #customize-theme-controls .customize-themes-panel.customize-pane-child.current-panel { 274 #customize-theme-controls .customize-pane-child.current-panel { 276 275 -webkit-transform: none; 277 276 -ms-transform: none; … … 279 278 } 280 279 281 #customize-theme-controls .customize-themes-panel.customize-pane-child,282 280 .section-open #customize-theme-controls .customize-pane-parent, 283 281 .in-sub-panel #customize-theme-controls .customize-pane-parent, 284 282 .section-open #customize-info, 285 283 .in-sub-panel #customize-info, 286 .in-sub-panel.section-open #customize-theme-controls .customize-pane-child.current-panel, 287 .in-themes-panel #customize-theme-controls .customize-pane-parent, 288 .in-themes-panel #customize-info { 284 .in-sub-panel.section-open #customize-theme-controls .customize-pane-child.current-panel { 289 285 visibility: hidden; 290 286 height: 0; … … 297 293 .section-open #customize-theme-controls .customize-pane-parent.busy, 298 294 .in-sub-panel #customize-theme-controls .customize-pane-parent.busy, 299 .in-themes-panel #customize-theme-controls .customize-pane-parent.busy,300 295 .section-open #customize-info.busy, 301 296 .in-sub-panel #customize-info.busy, 302 .in-themes-panel #customize-info.busy,303 297 .busy.section-open.in-sub-panel #customize-theme-controls .customize-pane-child.current-panel, 304 298 #customize-theme-controls .customize-pane-child.open, … … 308 302 height: auto; 309 303 overflow: auto; 310 }311 312 .in-themes-panel #customize-theme-controls .customize-pane-parent,313 .in-themes-panel #customize-info {314 -webkit-transform: translateX(100%);315 -ms-transform: translateX(100%);316 transform: translateX(100%);317 304 } 318 305 … … 407 394 float: left; 408 395 width: 48px; 409 height: 7 1px;396 height: 70px; 410 397 padding: 0 24px 0 0; 411 398 margin: 0; … … 421 408 422 409 .customize-section-back { 423 height: 7 4px;410 height: 73px; 424 411 } 425 412 … … 997 984 } 998 985 999 #customize-theme-controls .control-section-themes .accordion-section-title:hover, /* Not a focusable element. */ 1000 #customize-theme-controls .control-section-themes .accordion-section-title { 986 #customize-theme-controls .control-panel-themes { 987 border-bottom: none; 988 } 989 990 #customize-theme-controls .control-panel-themes > .accordion-section-title:hover, /* Not a focusable element. */ 991 #customize-theme-controls .control-panel-themes > .accordion-section-title { 1001 992 cursor: default; 1002 993 background: #fff; … … 1005 996 border-bottom: 1px solid #ddd; 1006 997 border-left: none; 1007 margin-top: 0; 998 border-right: none; 999 margin: 0 0 15px 0; 1000 padding-right: 100px; /* Space for the button */ 1008 1001 } 1009 1002 … … 1013 1006 } 1014 1007 1015 #customize-theme-controls .control-section-themes > .accordion-section-title:hover, /* Not a focusable element. */ 1016 #customize-theme-controls .control-section-themes > .accordion-section-title { 1017 margin: 0 0 15px; 1018 } 1019 1020 #customize-controls .customize-themes-panel .accordion-section-title { 1021 margin: 15px -8px; 1022 } 1023 1024 #customize-controls .control-section-themes .accordion-section-title, 1025 #customize-controls .customize-themes-panel .accordion-section-title { 1026 padding-right: 100px; /* Space for the button */ 1027 } 1028 1029 #customize-controls .control-section-themes .accordion-section-title span.customize-action, 1008 .control-panel-themes .accordion-section-title span.customize-action, 1030 1009 #customize-controls .customize-section-title span.customize-action { 1031 1010 font-size: 13px; … … 1034 1013 } 1035 1014 1036 #customize-controls .control-section-themes .accordion-section-title .change-theme, 1037 #customize-controls .customize-themes-panel .accordion-section-title .customize-theme { 1015 .control-panel-themes .accordion-section-title .change-theme { 1038 1016 position: absolute; 1039 1017 right: 10px; … … 1043 1021 } 1044 1022 1045 #customize- controls .control-section-themes .accordion-section-title:before{1023 #customize-theme-controls .control-panel-themes > .accordion-section-title:after { 1046 1024 display: none; 1047 1025 } 1048 1026 1049 #customize-controls .customize-themes-panel { 1050 padding: 0 8px; 1051 background: #f1f1f1; 1052 -webkit-box-sizing: border-box; 1053 -moz-box-sizing: border-box; 1054 box-sizing: border-box; 1055 } 1056 1057 #customize-controls .customize-themes-panel .accordion-section-title:first-child { 1058 margin-top: 0; 1059 } 1060 1061 #customize-controls .customize-themes-panel .accordion-section-title:nth-child(2) { 1027 .control-panel-themes .customize-themes-full-container { 1028 position: fixed; 1029 top: 0; 1030 left: 0; 1031 transition: .18s left ease-in-out; 1032 margin: 0 0 0 300px; 1033 padding:25px; 1034 overflow-y: scroll; 1035 width: calc(100% - 350px); 1036 height: calc(100% - 50px); 1037 background: #eee; 1038 z-index: 20; 1039 } 1040 1041 /* Animations for opening the themes panel */ 1042 #customize-header-actions .save, 1043 #customize-header-actions .spinner, 1044 #customize-header-actions .customize-controls-preview-toggle { 1045 position: relative; 1046 top: 0; 1047 transition: .18s top ease-in-out; 1048 } 1049 1050 #customize-footer-actions, 1051 #customize-footer-actions .collapse-sidebar { 1052 bottom: 0; 1053 transition: .18s bottom ease-in-out; 1054 } 1055 1056 .in-themes-panel:not(.animating) #customize-header-actions .save, 1057 .in-themes-panel:not(.animating) #customize-header-actions .spinner, 1058 .in-themes-panel:not(.animating) #customize-header-actions .customize-controls-preview-toggle, 1059 .in-themes-panel:not(.animating) #customize-preview, 1060 .in-themes-panel:not(.animating) #customize-footer-actions { 1061 visibility: hidden; 1062 } 1063 1064 .wp-full-overlay.in-themes-panel { 1065 background: #eee; /* Prevents a black flash when fading in the panel */ 1066 } 1067 1068 .in-themes-panel #customize-header-actions .save, 1069 .in-themes-panel #customize-header-actions .spinner, 1070 .in-themes-panel #customize-header-actions .customize-controls-preview-toggle { 1071 top: -45px; 1072 } 1073 1074 .in-themes-panel #customize-footer-actions, 1075 .in-themes-panel #customize-footer-actions .collapse-sidebar { 1076 bottom: -45px; 1077 } 1078 1079 /* Don't show the theme count while the panel opens, as it's in the wrong place during the animation */ 1080 .in-themes-panel.animating .control-panel-themes .filter-themes-count { 1081 display: none; 1082 } 1083 1084 .in-themes-panel.wp-full-overlay .wp-full-overlay-sidebar-content { 1085 bottom: 0; 1086 } 1087 1088 /* Adds a delay before fading in to avoid it "jumping" */ 1089 @keyframes themes-fade-in { 1090 0% { 1091 opacity: 0; 1092 } 1093 50% { 1094 opacity: 0; 1095 } 1096 100% { 1097 opacity: 1; 1098 } 1099 } 1100 1101 .control-panel-themes .customize-themes-full-container.animate { 1102 animation: .6s themes-fade-in 1; 1103 } 1104 1105 .in-themes-panel:not(.animating) .control-panel-themes .filter-themes-count { 1106 animation: .6s themes-fade-in 1; 1107 } 1108 1109 .control-panel-themes .filter-themes-count { 1110 position: fixed; 1111 top: 0; 1112 left: 48px; 1113 width: 222px; 1114 padding: 6px 15px; 1115 margin: 0; 1116 line-height: 32px; 1117 text-align: right; 1118 z-index: 10; 1119 } 1120 1121 .control-panel-themes .filter-themes-count .themes-displayed { 1122 font-weight: 600; 1123 color: #555d66; 1124 } 1125 1126 .control-panel-themes .filter-themes-count .see-themes, 1127 .control-panel-themes .filter-themes-count .filter-themes { 1128 display: none; 1129 } 1130 1131 1132 /* Mobile - toggle between themes and filters */ 1133 @media screen and (max-width:600px) { 1134 1135 /* Show a spinner in the filters view also, reusing the main customize spinner */ 1136 .in-themes-panel.loading #customize-header-actions .spinner { 1137 position: fixed; 1138 top: 0; 1139 left: 48px; 1140 visibility: visible; 1141 } 1142 1143 .in-themes-panel.loading.showing-themes #customize-header-actions .spinner { 1144 visibility: hidden; 1145 } 1146 1147 .control-panel-themes .filter-themes-count { 1148 width: calc(100% - 93px); 1149 } 1150 1151 .control-panel-themes .filter-themes-count .themes-displayed { 1152 display: none; 1153 } 1154 1155 .wp-full-overlay:not(.showing-themes) .control-panel-themes .filter-themes-count .see-themes { 1156 display: block; 1157 float: right; 1158 } 1159 1160 .wp-full-overlay.showing-themes .control-panel-themes .filter-themes-count .filter-themes { 1161 display: block; 1162 float: right; 1163 } 1164 1165 .in-themes-panel.showing-themes .control-panel-themes .customize-panel-back { 1166 position: fixed; 1167 top: 0; 1168 left: 0; 1169 z-index: 10; 1170 height: 45px; 1171 background: #eee; 1172 } 1173 1174 .in-themes-panel.showing-themes .control-panel-themes .customize-panel-back:before { 1175 line-height: 45px; 1176 } 1177 1178 .control-panel-themes .customize-themes-full-container { 1179 width: calc(100% - 50px); 1180 margin: 0; 1181 top: 46px; 1182 height: calc(100% - 96px); 1183 z-index: 1; 1184 display: none; 1185 } 1186 1187 .showing-themes .control-panel-themes .customize-themes-full-container { 1188 display: block; 1189 } 1190 } 1191 1192 .control-panel-themes .customize-themes-notifications .notice { 1193 margin: 0 0 25px 0; 1194 } 1195 1196 .customize-themes-full-container .customize-themes-section { 1197 display: none !important; /* There is unknown JS that perpetually tries to show all theme sections when more items are added. */ 1198 overflow: hidden; 1199 } 1200 1201 .customize-themes-full-container .customize-themes-section.current-section { 1202 display: list-item !important; /* There is unknown JS that perpetually tries to show all theme sections when more items are added. */ 1203 } 1204 1205 .theme-section .customize-themes-text-before { 1206 padding: 0 0 8px 15px; 1207 margin: 15px 0 0 0; 1208 line-height: 16px; 1209 border-bottom: 1px solid #ddd; 1210 color: #555d66; 1211 } 1212 1213 .control-panel-themes .customize-themes-section-title { 1214 width: 100%; 1215 background: #fff; 1216 box-shadow: none; 1217 outline: none; 1218 border-top: none; 1219 border-bottom: 1px solid #ddd; 1220 border-left: 4px solid #fff; 1221 border-right: none; 1222 cursor: pointer; 1223 padding: 10px 15px; 1224 position: relative; 1225 text-align: left; 1062 1226 font-size: 14px; 1063 1227 font-weight: 600; 1064 } 1065 1066 #customize-controls .customize-themes-panel > h2 { 1067 padding: 15px 8px 0 8px; 1068 } 1069 1070 #customize-theme-controls .customize-themes-panel .accordion-section-content { 1228 color: #555d66; 1229 text-shadow: none; 1230 } 1231 1232 .control-panel-themes .theme-section { 1233 margin: 0; 1234 position: relative; 1235 } 1236 1237 .control-panel-themes .customize-themes-section-title:focus, 1238 .control-panel-themes .customize-themes-section-title:hover { 1239 border-left-color: #0073aa; 1240 color: #0073aa; 1241 background: #f5f5f5; 1242 } 1243 1244 .control-panel-themes .theme-section .customize-themes-section-title.selected:after { 1245 content: "\f147"; 1246 font: 16px/1 dashicons; 1247 box-sizing: border-box; 1248 width: 20px; 1249 height: 20px; 1250 padding: 3px 3px 1px 1px; /* Re-align the icon to the smaller grid */ 1251 border-radius: 100%; 1252 position: absolute; 1253 top: 9px; 1254 right: 15px; 1255 background: #0073aa; 1256 color: #fff; 1257 } 1258 1259 .control-panel-themes .customize-themes-section-title.selected { 1260 color: #0073aa; 1261 } 1262 1263 .control-panel-themes .customize-themes-section-title.themes-section-search_themes { 1264 border-left: none; 1265 padding: 5px 10px 5px 15px; 1266 width: auto; 1267 } 1268 1269 .control-panel-themes .customize-themes-section-title.themes-section-feature_filter_themes:after, 1270 .control-panel-themes .customize-themes-section-title.themes-section-favorites_themes:after { 1271 content: "\f140"; 1272 font: 20px/1 dashicons; 1273 position: absolute; 1274 right: 15px; 1275 top: 8px; 1276 } 1277 1278 .control-panel-themes .customize-themes-section-title.themes-section-search_themes .wp-filter-search { 1279 width: 100%; 1280 } 1281 1282 .control-panel-themes .customize-themes-section-title.themes-section-search_themes.selected, 1283 .control-panel-themes .customize-themes-section-title.themes-section-search_themes:hover { 1284 background: #fff; 1285 cursor: default; 1286 } 1287 1288 .control-panel-themes .customize-themes-section-title.themes-section-feature_filter_themes { 1289 margin-top: 15px; 1290 border-top: 1px solid #ddd; 1291 } 1292 1293 .control-panel-themes .filter-details { 1294 background: #f5f5f5; 1295 margin: 0; 1296 padding: 8px 15px; 1297 border-top: none; 1298 border-bottom: 1px solid #ddd; 1299 display: none; 1300 } 1301 1302 .control-panel-themes .customize-themes-section-title.selected.details-open { 1303 border-bottom-color: #f5f5f5; 1304 border-left-color: #f5f5f5; 1305 background: #f5f5f5; 1306 } 1307 1308 .control-panel-themes .favorites-form.filter-details label { 1309 padding-bottom: 6px; 1310 display: inline-block; 1311 } 1312 1313 .control-panel-themes .filter-details .filter-group { 1314 float: none; 1315 width: 100%; 1071 1316 background: transparent; 1072 display: block; 1073 } 1074 1075 .customize-control.customize-control-theme { 1076 margin-bottom: 8px; 1317 border: none; 1318 padding: 0; 1319 box-shadow: none; 1320 } 1321 1322 .control-panel-themes .filter-details .filter-group legend button { 1323 padding: 18px 15px 8px 10px; 1324 line-height: 14px; 1325 border-bottom: 1px solid #ddd; 1326 width: 100%; 1327 text-align: left; 1328 } 1329 1330 .control-panel-themes .filter-details .filter-group legend { 1331 position: relative; 1332 top: 0; 1333 width: 100%; 1334 } 1335 1336 .control-panel-themes .filter-details .filter-group legend button:after { 1337 content: "\f140"; 1338 font: 20px/1 dashicons; 1339 position: absolute; 1340 bottom: 6px; 1341 right: 5px; 1342 } 1343 1344 .control-panel-themes .filter-details .filter-group legend button:hover, 1345 .control-panel-themes .filter-details .filter-group legend button:focus { 1346 color: #0073aa; 1347 border-bottom-color: #0073aa; /* Color change for focus style should be acceptable because border-bottom is barely visible previously. */ 1348 outline: none; 1349 box-shadow: none; 1350 } 1351 1352 .control-panel-themes .filter-details .filter-group legend button.open:after { 1353 content: "\f142"; 1354 } 1355 1356 .control-panel-themes .filter-details .filter-group .filter-group-feature { 1357 display: none; 1358 margin: 0; 1359 } 1360 1361 .control-panel-themes .filter-details .filter-group-feature label { 1362 border: 1px solid #ddd; 1363 border-top: 0; 1364 background: #fff; 1365 color: #555d66; 1366 margin: 0; 1367 padding: 12px 10px 12px 34px; 1368 width: calc(100% - 46px); 1369 line-height: 16px; 1370 font-weight: 600; 1371 } 1372 1373 .control-panel-themes .filter-details .filter-group-feature input { 1374 position: absolute; 1375 margin: 12px 10px; 1376 } 1377 1378 .control-panel-themes .filter-details .filter-group-feature label:hover { 1379 color: #0073aa; 1077 1380 } 1078 1381 … … 1084 1387 } 1085 1388 1389 .loading .customize-themes-section .spinner { 1390 display: block; 1391 visibility: visible; 1392 position: relative; 1393 clear: both; 1394 width: 20px; 1395 height: 20px; 1396 left: calc(50% - 10px); 1397 float: none; 1398 margin-top: 50px; 1399 } 1400 1401 .customize-themes-section .filter-drawer { 1402 border-top: none; 1403 display: block; 1404 background: transparent; 1405 padding-top: 5px; 1406 } 1407 1408 .customize-themes-section .clear-filters { 1409 margin-left: 8px; 1410 display: none; 1411 } 1412 1413 .customize-themes-section .no-themes { 1414 display: none; 1415 } 1416 1417 .themes-section-installed_themes .theme .notice-success { 1418 display: none; /* Hide "installed" notice on installed themes tab. */ 1419 } 1420 1421 .control-panel-themes .theme-browser .theme .theme-actions .button-primary { 1422 margin: 0 0 0 8px; 1423 } 1424 1425 .customize-control-theme .theme { 1426 width: 100%; 1427 margin: 0; 1428 } 1429 1430 .customize-control.customize-control-theme { /* override most properties on .customize-control */ 1431 box-sizing: border-box; 1432 width: 18.4%; 1433 margin: 0 2% 2% 0; 1434 padding: 0; 1435 clear: none; 1436 } 1437 1438 /* 5 columns above 2100px */ 1439 @media screen and (min-width: 2101px) { 1440 .customize-control.customize-control-theme:nth-child(5n) { 1441 margin-right: 0; 1442 } 1443 } 1444 1445 /* 4 columns up to 2100px */ 1446 @media screen and (min-width: 1601px) and (max-width: 2100px) { 1447 .customize-control.customize-control-theme { 1448 width: 23.5%; 1449 } 1450 1451 .customize-control.customize-control-theme:nth-child(4n) { 1452 margin-right: 0; 1453 } 1454 } 1455 1456 /* 3 columns up to 1600px */ 1457 @media screen and (min-width: 1201px) and (max-width: 1600px) { 1458 .customize-control.customize-control-theme { 1459 width: 32%; 1460 } 1461 1462 .customize-control.customize-control-theme:nth-child(3n) { 1463 margin-right: 0; 1464 } 1465 } 1466 1467 /* 2 columns up to 1200px */ 1468 @media screen and (min-width: 851px) and (max-width: 1200px) { 1469 .customize-control.customize-control-theme { 1470 width: 49%; 1471 } 1472 1473 .customize-control.customize-control-theme:nth-child(even) { 1474 margin-right: 0; 1475 } 1476 } 1477 1478 /* 1 column up to 850 px */ 1479 @media screen and (max-width: 850px) { 1480 .customize-control.customize-control-theme { 1481 width: 100%; 1482 margin: 0 0 3% 0; 1483 } 1484 } 1485 1086 1486 .wp-customizer .theme-browser .themes { 1087 1487 padding-bottom: 8px; 1088 1488 } 1089 1489 1090 .wp-customizer .theme-browser .theme {1091 margin: 0;1092 width: 100%;1093 }1094 1095 1490 .wp-customizer .theme-browser .theme .theme-actions { 1096 -ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=100)";1097 1491 opacity: 1; 1098 1492 } … … 1111 1505 line-height: 1.5; 1112 1506 width: 100%; 1113 }1114 1115 .control-section-themes .accordion-section-title:after,1116 .customize-themes-panel .accordion-section-title:after {1117 display: none;1118 }1119 1120 .customize-themes-panel.control-panel-content {1121 border-top: 1px solid #ddd;1122 1507 } 1123 1508 … … 1136 1521 } 1137 1522 1523 /* Avoid a z-index war by resetting elements that should be under the overlay. 1524 This is likely required because of the way that sections and panels are positioned. */ 1525 .wp-customizer.modal-open #customize-header-actions, 1526 .wp-customizer.modal-open .control-panel-themes .filter-themes-count, 1527 .wp-customizer.modal-open .control-panel-themes .customize-themes-section-title.selected:after { 1528 z-index: -1; 1529 } 1530 1138 1531 .wp-customizer .theme-overlay .theme-backdrop { 1139 1532 background: rgba( 238, 238, 238, 0.75 ); 1140 1533 position: fixed; 1141 1534 z-index: 110; 1535 } 1536 1537 .wp-customizer .theme-overlay .star-rating { 1538 float: left; 1539 margin-right: 8px; 1540 } 1541 1542 .wp-customizer .theme-rating .num-ratings { 1543 line-height: 20px; 1142 1544 } 1143 1545 … … 1148 1550 bottom: 45px; 1149 1551 z-index: 120; 1150 max-width: 1740px; /* To ensure that theme screenshots are not displayed larger than 880px wide. */1151 1552 } 1152 1553 1153 1554 .wp-customizer .theme-overlay .theme-actions { 1154 text-align: right; /* Because there's only one action, match the pattern of media modals and right-align the action. */ 1155 } 1156 1157 .ie8 .wp-customizer .theme-overlay .theme-header, 1158 .ie8 .wp-customizer .theme-overlay .theme-about, 1159 .ie8 .wp-customizer .theme-overlay .theme-actions { 1160 position: static; 1161 } 1555 text-align: right; /* Because there're only one or two actions, match the UI pattern of media modals and right-align the action. */ 1556 padding: 10px 15px; 1557 } 1558 1559 .wp-customizer .theme-overlay .theme-actions .theme-install.preview { 1560 margin-left: 8px; 1561 } 1562 1563 .control-panel-themes .theme-actions .delete-theme { 1564 left: 15px; /* these override themes.css on mobile */ 1565 right: auto; 1566 bottom: auto; 1567 position: absolute; 1568 } 1569 1570 .modal-open .in-themes-panel #customize-controls .wp-full-overlay-sidebar-content { 1571 overflow: visible; /* Prevent the top-level Customizer controls from becoming visible when elements on the right of the details modal are focused. */ 1572 } 1573 1162 1574 1163 1575 /* Small Screens */ -
trunk/src/wp-admin/css/themes.css
r38795 r38813 571 571 margin: 0 30px 0 0; 572 572 width: 55%; 573 max-width: 880px;573 max-width: 1200px; /* Recommended theme screenshot width, set here to avoid stretching */ 574 574 text-align: center; 575 575 } -
trunk/src/wp-admin/customize.php
r38810 r38813 110 110 111 111 <script type="text/javascript"> 112 var ajaxurl = <?php echo wp_json_encode( admin_url( 'admin-ajax.php', 'relative' ) ); ?>; 112 var ajaxurl = <?php echo wp_json_encode( admin_url( 'admin-ajax.php', 'relative' ) ); ?>, 113 pagenow = 'customize'; 113 114 </script> 114 115 -
trunk/src/wp-admin/includes/theme.php
r38788 r38813 608 608 */ 609 609 function customize_themes_print_templates() { 610 $preview_url = esc_url( add_query_arg( 'theme', '__THEME__' ) ); // Token because esc_url() strips curly braces.611 $preview_url = str_replace( '__THEME__', '{{ data.id }}', $preview_url );612 610 ?> 613 611 <script type="text/html" id="tmpl-customize-themes-details-view"> … … 621 619 <div class="theme-about wp-clearfix"> 622 620 <div class="theme-screenshots"> 623 <# if ( data.screenshot [0] ) { #>621 <# if ( data.screenshot && data.screenshot[0] ) { #> 624 622 <div class="screenshot"><img src="{{ data.screenshot[0] }}" alt="" /></div> 625 623 <# } else { #> … … 634 632 <h2 class="theme-name">{{{ data.name }}}<span class="theme-version"><?php printf( __( 'Version: %s' ), '{{ data.version }}' ); ?></span></h2> 635 633 <h3 class="theme-author"><?php printf( __( 'By %s' ), '{{{ data.authorAndUri }}}' ); ?></h3> 634 635 <# if ( data.stars && 0 != data.num_ratings ) { #> 636 <div class="theme-rating"> 637 {{{ data.stars }}} 638 <span class="num-ratings"><?php echo sprintf( __( '(%s ratings)' ), '{{ data.num_ratings }}' ); ?></span> 639 </div> 640 <# } #> 641 642 <# if ( data.hasUpdate ) { #> 643 <div class="notice notice-warning notice-alt notice-large" data-slug="{{ data.id }}"> 644 <h3 class="notice-title"><?php _e( 'Update Available' ); ?></h3> 645 {{{ data.update }}} 646 </div> 647 <# } #> 648 636 649 <p class="theme-description">{{{ data.description }}}</p> 637 650 … … 639 652 <p class="parent-theme"><?php printf( __( 'This is a child theme of %s.' ), '<strong>{{{ data.parent }}}</strong>' ); ?></p> 640 653 <# } #> 641 642 654 <# if ( data.tags ) { #> 643 <p class="theme-tags"><span><?php _e( 'Tags:' ); ?></span> {{ data.tags}}</p>655 <p class="theme-tags"><span><?php _e( 'Tags:' ); ?></span> {{{ data.tags }}}</p> 644 656 <# } #> 645 657 </div> 646 658 </div> 647 659 648 <# if ( ! data.active ) { #> 649 <div class="theme-actions"> 650 <div class="inactive-theme"> 651 <?php 652 /* translators: %s: Theme name */ 653 $aria_label = sprintf( __( 'Preview %s' ), '{{ data.name }}' ); 654 ?> 655 <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> 656 </div> 657 </div> 658 <# } #> 660 <div class="theme-actions"> 661 <# if ( data.active ) { #> 662 <button type="button" class="button button-primary customize-theme"><?php _e( 'Customize' ); ?></a> 663 <# } else if ( 'installed' === data.type ) { #> 664 <?php if ( current_user_can( 'delete_themes' ) ) { ?> 665 <# if ( data.actions && data.actions['delete'] ) { #> 666 <a href="{{{ data.actions['delete'] }}}" data-slug="{{ data.id }}" class="button button-secondary delete-theme"><?php _e( 'Delete' ); ?></a> 667 <# } #> 668 <?php } ?> 669 <button type="button" class="button button-primary preview-theme" data-slug="{{ data.id }}"><?php _e( 'Live Preview' ); ?></span> 670 <# } else { #> 671 <button type="button" class="button theme-install" data-slug="{{ data.id }}"><?php _e( 'Install' ); ?></button> 672 <button type="button" class="button button-primary theme-install preview" data-slug="{{ data.id }}"><?php _e( 'Install & Preview' ); ?></button> 673 <# } #> 674 </div> 659 675 </div> 660 676 </script> -
trunk/src/wp-admin/js/customize-controls.js
r38810 r38813 1 /* global _wpCustomizeHeader, _wpCustomizeBackground, _wpMediaViewsL10n, MediaElementPlayer */1 /* global _wpCustomizeHeader, _wpCustomizeBackground, _wpMediaViewsL10n, MediaElementPlayer, console */ 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 … … 1040 1040 * wp.customize.ThemesSection 1041 1041 * 1042 * Custom section for themes that functions similarly to a backwards panel,1043 * and alsohandles the theme-details view rendering and navigation.1042 * Custom section for themes that loads themes by category, and also 1043 * handles the theme-details view rendering and navigation. 1044 1044 * 1045 1045 * @constructor … … 1053 1053 screenshotQueue: null, 1054 1054 $window: $( window ), 1055 1056 /** 1057 * @since 4.2.0 1058 */ 1059 initialize: function () { 1060 this.$customizeSidebar = $( '.wp-full-overlay-sidebar-content:first' ); 1061 return api.Section.prototype.initialize.apply( this, arguments ); 1055 loaded: 0, 1056 loading: false, 1057 fullyLoaded: false, 1058 term: '', 1059 filterContainer: $(), 1060 1061 /** 1062 * Embed the section in the DOM when the themes panel is ready. 1063 * 1064 * Insert the section before the themes container. Assume that a themes section is within a panel, but not necessarily the themes panel. 1065 * 1066 * @since 4.7.0 1067 */ 1068 embed: function () { 1069 var inject, 1070 section = this, 1071 container = $( '#customize-theme-controls' ); 1072 1073 // Watch for changes to the panel state 1074 inject = function ( panelId ) { 1075 var parentContainer; 1076 api.panel( panelId, function ( panel ) { 1077 // The panel has been registered, wait for it to become ready/initialized 1078 panel.deferred.embedded.done( function () { 1079 parentContainer = panel.contentContainer; 1080 if ( ! section.headContainer.parent().is( parentContainer ) ) { 1081 parentContainer.find( '.customize-themes-full-container-container' ).before( section.headContainer ); 1082 } 1083 if ( ! section.contentContainer.parent().is( section.headContainer ) ) { 1084 container.append( section.contentContainer ); 1085 } 1086 section.deferred.embedded.resolve(); 1087 }); 1088 } ); 1089 }; 1090 section.panel.bind( inject ); 1091 inject( section.panel.get() ); // Since a section may never get a panel, assume that it won't ever get one 1062 1092 }, 1063 1093 … … 1092 1122 }); 1093 1123 1094 _.bindAll( this, 'renderScreenshots' );1124 _.bindAll( this, 'renderScreenshots', 'loadMore', 'checkTerm', 'filtersChecked' ); 1095 1125 }, 1096 1126 … … 1099 1129 * 1100 1130 * Ignore the active states' of the contained theme controls, and just 1101 * use the section's own active state instead. This ensures empty search1102 * results for theme s to causethe section to become inactive.1131 * use the section's own active state instead. This prevents empty search 1132 * results for theme sections from causing the section to become inactive. 1103 1133 * 1104 1134 * @since 4.2.0 … … 1116 1146 var section = this; 1117 1147 1118 // Expand/Collapse section/panel. 1119 section.container.find( '.change-theme, .customize-theme' ).on( 'click keydown', function( event ) { 1120 if ( api.utils.isKeydownButNotEnterEvent( event ) ) { 1121 return; 1122 } 1123 event.preventDefault(); // Keep this AFTER the key filter above 1124 1125 if ( section.expanded() ) { 1126 section.collapse(); 1127 } else { 1148 section.filterContainer = $( '#accordion-section-' + section.id ); 1149 1150 // Expand section/panel. Only collapse when opening another section. 1151 section.filterContainer.on( 'click', '.customize-themes-section-title', function() { 1152 // Open the section. 1153 if ( ! section.expanded() ) { 1128 1154 section.expand(); 1129 1155 } 1156 1157 // Toggle filters. 1158 if ( section.filterContainer.find( '.filter-details' ).length ) { 1159 section.filterContainer.find( '.customize-themes-section-title' ) 1160 .toggleClass( 'details-open' ) 1161 .attr('aria-expanded', function ( i, attr ) { 1162 return attr === 'true' ? 'false' : 'true'; 1163 }); 1164 section.filterContainer.find( '.filter-details' ).slideToggle( 180 ); 1165 } 1166 }); 1167 1168 // Preview installed themes. 1169 section.container.on( 'click', '.theme-actions .preview-theme', function() { 1170 var themeId = $( this ).data( 'themeId' ); 1171 1172 $( '.wp-full-overlay' ).addClass( 'customize-loading' ); 1173 api.panel( 'themes' ).loadThemePreview( themeId ).fail( function() { 1174 $( '.wp-full-overlay' ).removeClass( 'customize-loading' ); 1175 } ); 1130 1176 }); 1131 1177 1132 1178 // Theme navigation in details view. 1133 section.container.on( 'click keydown', '.left', function( event ) { 1134 if ( api.utils.isKeydownButNotEnterEvent( event ) ) { 1135 return; 1136 } 1137 1138 event.preventDefault(); // Keep this AFTER the key filter above 1139 1179 section.container.on( 'click', '.left', function() { 1140 1180 section.previousTheme(); 1141 1181 }); 1142 1182 1143 section.container.on( 'click keydown', '.right', function( event ) { 1144 if ( api.utils.isKeydownButNotEnterEvent( event ) ) { 1145 return; 1146 } 1147 1148 event.preventDefault(); // Keep this AFTER the key filter above 1149 1183 section.container.on( 'click', '.right', function() { 1150 1184 section.nextTheme(); 1151 1185 }); 1152 1186 1153 section.container.on( 'click keydown', '.theme-backdrop, .close', function( event ) { 1154 if ( api.utils.isKeydownButNotEnterEvent( event ) ) { 1155 return; 1156 } 1157 1158 event.preventDefault(); // Keep this AFTER the key filter above 1159 1187 section.container.on( 'click', '.theme-backdrop, .close', function() { 1160 1188 section.closeDetails(); 1161 1189 }); 1162 1190 1163 1191 var renderScreenshots = _.throttle( _.bind( section.renderScreenshots, this ), 100 ); 1164 section.container.on( 'input', '#themes-filter', function( event ) { 1192 1193 // Only used when there is only one section - installed themes. 1194 $( '.control-panel-themes' ).on( 'input', '#themes-filter', function( event ) { 1165 1195 var count, 1166 1196 term = event.currentTarget.value.toLowerCase().trim().replace( '-', ' ' ), … … 1174 1204 1175 1205 // Update theme count. 1176 count = section.cont ainer.find( 'li.customize-control:visible' ).length;1177 section.container.find( '.theme-count' ).text( count );1178 }); 1179 1180 // Pre-load the first 3 theme screenshots.1181 api.bind( 'ready', function () {1182 _.each( section.controls().slice( 0, 3 ), function ( control ) {1183 var img, src = control.params.theme.screenshot[0];1184 if ( src ) {1185 img = new Image();1186 img.src = src;1206 count = section.contentContainer.find( 'li.customize-control:visible' ).length; 1207 $( '.control-panel-themes' ).find( '.theme-count' ).text( count ); 1208 }); 1209 1210 // Event listeners for queries with user-entered terms. 1211 if ( 'search' === section.params.action ) { 1212 var debounced = _.debounce( section.checkTerm, 500 ); // Wait until there is no input for 500 milliseconds to initiate a search. 1213 $( '.control-panel-themes' ).on( 'input', '#wp-filter-search-input', function() { 1214 debounced( section ); 1215 if ( ! section.expanded() ) { 1216 section.expand(); 1187 1217 } 1188 1218 }); 1219 1220 // Focus the input if the icon is clicked. 1221 section.filterContainer.find( '.search-form' ).on( 'click', function( e ) { 1222 if ( ! $( e.currentTarget ).hasClass( 'wp-filter-search' ) ) { 1223 $( e.currentTarget ).find( '.wp-filter-search' ).focus(); 1224 } 1225 }); 1226 } else if ( 'favorites' === section.params.action ) { 1227 section.container.on( 'click', '.favorites-form-submit', function() { 1228 section.checkTerm( section ); 1229 }); 1230 section.container.on( 'keydown', '#wporg-username-input', function( e ) { 1231 if ( api.utils.isKeydownButNotEnterEvent( e ) ) { 1232 return; 1233 } 1234 section.checkTerm( section ); 1235 }); 1236 } else if ( 'feature_filter' === section.params.action ) { 1237 section.container.on( 'click', '.filter-group input', function() { 1238 section.filtersChecked(); 1239 section.checkTerm( section ); 1240 }); 1241 1242 // Toggle feature filter sections. 1243 section.container.on( 'click', '.filter-group legend button', function( e ) { 1244 $( e.currentTarget ) 1245 .toggleClass( 'open' ) 1246 .attr('aria-expanded', function ( i, attr ) { 1247 return attr === 'true' ? 'false' : 'true'; 1248 }) 1249 .parent().next( '.filter-group-feature' ).slideToggle( 180 ); 1250 }); 1251 } 1252 1253 // Move section controls to the themes area. 1254 api.bind( 'ready', function () { 1255 section.contentContainer = section.container.find( '.customize-themes-section' ); 1256 section.contentContainer.appendTo( $( '.customize-themes-full-container' ) ); 1257 section.container.add( section.filterContainer ); 1189 1258 }); 1190 1259 }, … … 1198 1267 * @param {Object} args 1199 1268 * @param {Boolean} args.unchanged 1200 * @param { Callback} args.completeCallback1269 * @param {Function} args.completeCallback 1201 1270 */ 1202 1271 onChangeExpanded: function ( expanded, args ) { … … 1211 1280 1212 1281 // Note: there is a second argument 'args' passed 1213 var panel = this, 1214 section = panel.contentContainer, 1215 overlay = section.closest( '.wp-full-overlay' ), 1216 container = section.closest( '.wp-full-overlay-sidebar-content' ), 1217 customizeBtn = section.find( '.customize-theme' ), 1218 changeBtn = panel.headContainer.find( '.change-theme' ); 1219 1220 if ( expanded && ! section.hasClass( 'current-panel' ) ) { 1282 var section = this, 1283 container = section.contentContainer.closest( '.customize-themes-full-container' ); 1284 1285 if ( expanded ) { 1286 1287 if ( -1 !== $.inArray( section.params.action, [ 'search', 'favorites', 'feature_filter' ] ) && '' === section.term ) { 1288 section.collapse(); // Note that the current section hasn't been collapsed yet, so this is all we need to do to do nothing. 1289 return; // Don't expand to an empty section that can't load any themes. 1290 } 1291 1292 // Load controls if none are loaded yet. 1293 if ( 0 === section.loaded ) { 1294 section.loadControls(); 1295 } 1296 1221 1297 // Collapse any sibling sections/panels 1222 1298 api.section.each( function ( otherSection ) { 1223 if ( otherSection !== panel) {1299 if ( otherSection !== section ) { 1224 1300 otherSection.collapse( { duration: args.duration } ); 1225 1301 } 1226 1302 }); 1227 api.panel.each( function ( otherPanel ) { 1228 otherPanel.collapse( { duration: 0 } ); 1229 }); 1230 1231 panel._animateChangeExpanded( function() { 1232 changeBtn.attr( 'tabindex', '-1' ); 1233 customizeBtn.attr( 'tabindex', '0' ); 1234 1235 customizeBtn.focus(); 1236 section.css( 'top', '' ); 1237 container.scrollTop( 0 ); 1238 1239 if ( args.completeCallback ) { 1240 args.completeCallback(); 1303 1304 section.contentContainer.addClass( 'current-section' ); 1305 container.scrollTop(); 1306 section.filterContainer.find( '.customize-themes-section-title' ).addClass( 'selected' ); 1307 1308 container.on( 'scroll', _.throttle( section.renderScreenshots, 300 ) ); 1309 container.on( 'scroll', _.throttle( section.loadMore, 300 ) ); 1310 1311 if ( args.completeCallback ) { 1312 args.completeCallback(); 1313 } 1314 section.updateCount(); // Show this section's count. 1315 } else { 1316 section.contentContainer.removeClass( 'current-section' ); 1317 1318 // Always hide, even if they don't exist or are already hidden. 1319 section.filterContainer.find( '.customize-themes-section-title' ).removeClass( 'selected details-open' ).attr( 'aria-expanded', 'false' ); 1320 section.filterContainer.find( '.filter-details' ).slideUp( 180 ); 1321 1322 container.off( 'scroll' ); 1323 1324 if ( args.completeCallback ) { 1325 args.completeCallback(); 1326 } 1327 } 1328 }, 1329 1330 /** 1331 * Return the section's content element without detachng from the parent. 1332 * 1333 * @since 4.7.0 1334 */ 1335 getContent: function() { 1336 return this.container.find( '.control-section-content' ); 1337 }, 1338 1339 /** 1340 * Load theme data via ajax and add themes to the section as controls. 1341 * 1342 * @since 4.7.0 1343 */ 1344 loadControls: function() { 1345 var section = this, params, page, request; 1346 1347 if ( section.loading ) { 1348 return; // We're already loading a batch of themes. 1349 } 1350 1351 // Parameters for every API query. Additional params are set in PHP. 1352 page = Math.ceil( section.loaded / 100 ) + 1; 1353 params = { 1354 'switch-themes-nonce': api.settings.nonce['switch-themes'], 1355 'wp_customize': 'on', 1356 'theme_action': section.params.action, 1357 'customized_theme': api.settings.theme.stylesheet, 1358 'page': page 1359 }; 1360 1361 // Add fields for special request actions. 1362 if ( 'search' === section.params.action ) { 1363 if ( '' === section.term ) { 1364 return; 1365 } else { 1366 params.search = section.term; 1367 } 1368 } else if ( 'favorites' === section.params.action ) { 1369 if ( '' === section.term ) { 1370 return; 1371 } else { 1372 params.user = section.term; 1373 } 1374 } else if ( 'feature_filter' === section.params.action ) { 1375 if ( '' === section.term ) { 1376 return; 1377 } else { 1378 params.tags = section.term; 1379 } 1380 } 1381 1382 // Load themes. 1383 section.headContainer.closest( '.wp-full-overlay' ).addClass( 'loading' ); 1384 section.loading = true; 1385 section.container.find( '.no-themes' ).hide(); 1386 request = wp.ajax.post( 'customize-load-themes', params ); 1387 request.done(function( data ) { 1388 var themes = data.themes, 1389 themeControl, newThemeControls; 1390 if ( 0 !== themes.length ) { 1391 newThemeControls = []; 1392 // Add controls for each theme. 1393 _.each( themes, function ( theme ) { 1394 var customizeId = section.params.action + '_theme_' + theme.id; 1395 themeControl = new api.controlConstructor.theme( customizeId, { 1396 params: { 1397 type: 'theme', 1398 content: '<li id="customize-control-theme-' + section.params.action + '_' + theme.id + '" class="customize-control customize-control-theme"></li>', 1399 section: section.params.id, 1400 active: true, 1401 theme: theme, 1402 priority: section.loaded + 1 1403 }, 1404 previewer: api.previewer 1405 } ); 1406 1407 api.control.add( customizeId, themeControl ); 1408 newThemeControls.push( themeControl ); 1409 section.loaded = section.loaded + 1; 1410 }); 1411 1412 if ( 1 === page ) { 1413 // Pre-load the first 3 theme screenshots. 1414 _.each( section.controls().slice( 0, 3 ), function ( control ) { 1415 var img, src = control.params.theme.screenshot[0]; 1416 if ( src ) { 1417 img = new Image(); 1418 img.src = src; 1419 } 1420 }); 1421 if ( 'search' === section.params.action ) { 1422 wp.a11y.speak( api.settings.l10n.themeSearchResults.replace( '%d', data.info.results ) ); 1423 } 1424 } else { 1425 Array.prototype.push.apply( section.screenshotQueue, newThemeControls ); // Add new themes to the screenshot queue. 1241 1426 } 1242 } ); 1243 1244 overlay.addClass( 'in-themes-panel' ); 1245 section.addClass( 'current-panel' ); 1246 1247 } else if ( ! expanded && section.hasClass( 'current-panel' ) ) { 1248 panel._animateChangeExpanded( function() { 1249 changeBtn.attr( 'tabindex', '0' ); 1250 customizeBtn.attr( 'tabindex', '-1' ); 1251 1252 changeBtn.focus(); 1253 section.css( 'top', '' ); 1254 1255 if ( args.completeCallback ) { 1256 args.completeCallback(); 1427 _.delay( section.renderScreenshots, 100 ); // Wait for the controls to become visible. 1428 1429 if ( 'installed' === section.action || 100 > themes.length ) { // If we have less than the requested 100 themes, it's the end of the list. 1430 section.fullyLoaded = true; 1257 1431 } 1258 } ); 1259 1260 overlay.removeClass( 'in-themes-panel' ); 1261 section.removeClass( 'current-panel' ); 1432 } else { 1433 if ( 0 === section.loaded ) { 1434 section.container.find( '.no-themes' ).show(); 1435 wp.a11y.speak( section.container.find( '.no-themes' ).text() ); 1436 } else { 1437 section.fullyLoaded = true; 1438 } 1439 } 1440 if ( 'installed' === section.params.action ) { 1441 section.updateCount(); 1442 } else { 1443 section.updateCount( data.info.results ); 1444 } 1445 section.container.find( '.unexpected-error' ).hide(); // Hide error notice in case it was previously shown. 1446 1447 // This cannot run on request.always, as section.loading may turn false before the new controls load in the success case. 1448 section.headContainer.closest( '.wp-full-overlay' ).removeClass( 'loading' ); 1449 section.loading = false; 1450 }); 1451 request.fail(function( data ) { 1452 if ( 'undefined' === typeof data ) { 1453 section.container.find( '.unexpected-error' ).show(); 1454 wp.a11y.speak( section.container.find( '.unexpected-error' ).text() ); 1455 } else if ( typeof console !== 'undefined' && console.error ) { 1456 console.error( data ); 1457 } 1458 1459 // This cannot run on request.always, as section.loading may turn false before the new controls load in the success case. 1460 section.headContainer.closest( '.wp-full-overlay' ).removeClass( 'loading' ); 1461 section.loading = false; 1462 }); 1463 }, 1464 1465 /** 1466 * Determines whether more themes should be loaded, and loads them. 1467 * 1468 * @since 4.7.0 1469 */ 1470 loadMore: function() { 1471 var section = this, container, bottom, threshold; 1472 if ( ! section.fullyLoaded && ! section.loading ) { 1473 container = section.container.closest( '.customize-themes-full-container' ); 1474 1475 bottom = container.scrollTop() + container.height(); 1476 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. 1477 1478 if ( bottom > threshold ) { 1479 section.loadControls(); 1480 } 1481 } 1482 }, 1483 1484 /** 1485 * Event handler for search, feature filter, and favorites input that determines if the term has changed and loads new controls as needed. 1486 * 1487 * @since 4.7.0 1488 * 1489 * @param {wp.customize.ThemesSection} section The current theme section, passed through the debouncer. 1490 */ 1491 checkTerm: function( section ) { 1492 var newTerm; 1493 1494 // Find term. 1495 if ( 'search' === section.params.action ) { 1496 newTerm = $( '#wp-filter-search-input' ).val(); 1497 } else if ( 'favorites' === section.params.action ) { 1498 newTerm = $( '#wporg-username-input' ).val(); 1499 } else if ( 'feature_filter' === section.params.action ) { 1500 newTerm = section.term; // Set separately by filtersChecked(), as they're changed. 1501 if ( '' === newTerm ) { 1502 return; 1503 } 1504 } else { 1505 return; 1506 } 1507 1508 if ( section.term === newTerm && 'feature_filter' !== section.params.action ) { 1509 return; 1510 } 1511 1512 // Clear the controls in the section. 1513 _.each( section.controls(), function( control ) { 1514 control.container.remove(); 1515 api.control.remove( control.id ); 1516 }); 1517 section.loaded = 0; 1518 section.fullyLoaded = false; 1519 section.screenshotQueue = null; 1520 1521 if ( '' !== newTerm ) { // Empty term should not show any results. 1522 // Run a new query, with loadControls handling paging, etc. 1523 section.term = newTerm; 1524 section.loadControls(); 1525 if ( ! section.expanded() ) { 1526 section.expand(); // Expand the section if it isn't expanded. 1527 } 1528 } 1529 }, 1530 1531 /** 1532 * Check for filters checked in the feature filter list. 1533 * 1534 * @since 4.7.0 1535 */ 1536 filtersChecked: function() { 1537 var section = this, 1538 items = section.container.find( '.filter-group' ).find( ':checkbox' ), 1539 tags = []; 1540 1541 if ( 'feature_filter' !== section.params.action ) { 1542 return false; 1543 } 1544 1545 _.each( items.filter( ':checked' ), function( item ) { 1546 tags.push( $( item ).prop( 'value' ) ); 1547 }); 1548 1549 // When no filters are checked, restore initial state and return 1550 if ( tags.length === 0 ) { 1551 section.term = ''; 1552 } else { 1553 section.term = tags; 1262 1554 } 1263 1555 }, … … 1271 1563 var section = this; 1272 1564 1273 // Fill queue initially. 1274 if ( section.screenshotQueue === null ) { 1275 section.screenshotQueue = section.controls(); 1276 } 1277 1278 // Are all screenshots rendered? 1565 // Fill queue initially, or check for more if empty. 1566 if ( section.screenshotQueue === null || 0 === section.screenshotQueue.length ) { 1567 // Add controls that haven't had their screenshots rendered. 1568 section.screenshotQueue = _.filter( section.controls(), function( control ) { 1569 return ! control.screenshotRendered; 1570 }); 1571 } 1572 1573 // Are all screenshots rendered (for now)? 1279 1574 if ( ! section.screenshotQueue.length ) { 1280 1575 return; … … 1312 1607 1313 1608 /** 1609 * Update the number of themes in the section. 1610 * 1611 * @since 4.7.0 1612 */ 1613 updateCount: function ( count ) { 1614 if ( ! count ) { 1615 count = this.loaded; 1616 } 1617 1618 var displayed = this.container.closest( '.control-panel-content' ).find( '.themes-displayed' ), 1619 countEl = this.container.closest( '.control-panel-content' ).find( '.theme-count' ); 1620 1621 if ( 0 === count ) { 1622 countEl.text( count ); 1623 } else { 1624 // Animate the count change for emphasis. 1625 displayed.fadeOut( 180, function() { 1626 countEl.text( count ); 1627 displayed.fadeIn( 180 ); 1628 } ); 1629 wp.a11y.speak( api.settings.l10n.announceThemeCount.replace( '%d', count ) ); 1630 } 1631 }, 1632 1633 /** 1314 1634 * Advance the modal to the next theme. 1315 1635 * … … 1331 1651 */ 1332 1652 getNextTheme: function () { 1333 var control, next;1334 control = api.control( 'theme_' + this.currentTheme );1653 var section = this, control, next; 1654 control = api.control( section.params.action + '_theme_' + this.currentTheme ); 1335 1655 next = control.container.next( 'li.customize-control-theme' ); 1336 1656 if ( ! next.length ) { 1337 1657 return false; 1338 1658 } 1339 next = next[0].id.replace( 'customize-control- ', '' );1659 next = next[0].id.replace( 'customize-control-theme-' + section.params.action, section.params.action + '_theme' ); 1340 1660 control = api.control( next ); 1341 1661 … … 1363 1683 */ 1364 1684 getPreviousTheme: function () { 1365 var control, previous;1366 control = api.control( 'theme_' + this.currentTheme );1685 var section = this, control, previous; 1686 control = api.control( section.params.action + '_theme_' + this.currentTheme ); 1367 1687 previous = control.container.prev( 'li.customize-control-theme' ); 1368 1688 if ( ! previous.length ) { 1369 1689 return false; 1370 1690 } 1371 previous = previous[0].id.replace( 'customize-control- ', '' );1691 previous = previous[0].id.replace( 'customize-control-theme-' + section.params.action, section.params.action + '_theme' ); 1372 1692 control = api.control( previous ); 1373 1693 … … 1387 1707 this.overlay.find( '.left' ).addClass( 'disabled' ); 1388 1708 } 1389 },1390 1391 /**1392 * Load theme preview.1393 *1394 * @since 4.7.01395 *1396 * @param {string} themeId Theme ID.1397 * @returns {jQuery.promise} Promise.1398 */1399 loadThemePreview: function( themeId ) {1400 var deferred = $.Deferred(), onceProcessingComplete, overlay, urlParser;1401 1402 urlParser = document.createElement( 'a' );1403 urlParser.href = location.href;1404 urlParser.search = $.param( _.extend(1405 api.utils.parseQueryString( urlParser.search.substr( 1 ) ),1406 {1407 theme: themeId,1408 changeset_uuid: api.settings.changeset.uuid1409 }1410 ) );1411 1412 overlay = $( '.wp-full-overlay' );1413 overlay.addClass( 'customize-loading' );1414 1415 onceProcessingComplete = function() {1416 var request;1417 if ( api.state( 'processing' ).get() > 0 ) {1418 return;1419 }1420 1421 api.state( 'processing' ).unbind( onceProcessingComplete );1422 1423 request = api.requestChangesetUpdate();1424 request.done( function() {1425 $( window ).off( 'beforeunload.customize-confirm' );1426 window.location.href = urlParser.href;1427 } );1428 request.fail( function() {1429 overlay.removeClass( 'customize-loading' );1430 } );1431 };1432 1433 if ( 0 === api.state( 'processing' ).get() ) {1434 onceProcessingComplete();1435 } else {1436 api.state( 'processing' ).bind( onceProcessingComplete );1437 }1438 1439 return deferred.promise();1440 1709 }, 1441 1710 … … 1457 1726 section.containFocus( section.overlay ); 1458 1727 section.updateLimits(); 1728 wp.a11y.speak( api.settings.l10n.announceThemeDetails.replace( '%s', theme.name ) ); 1459 1729 1460 1730 link = section.overlay.find( '.inactive-theme > a' ); 1461 1462 1731 link.on( 'click', function( event ) { 1463 1732 event.preventDefault(); … … 1469 1738 link.addClass( 'disabled' ); 1470 1739 1471 section.loadThemePreview( theme.id ).fail( function() {1740 api.panel( 'themes' ).loadThemePreview( theme.id ).fail( function() { 1472 1741 link.removeClass( 'disabled' ); 1473 1742 } ); … … 1484 1753 $( 'body' ).removeClass( 'modal-open' ); 1485 1754 this.overlay.fadeOut( 'fast' ); 1486 api.control( 'theme_' + this.currentTheme).focus();1755 api.control( this.params.action + '_theme_' + this.currentTheme ).container.find( '.theme' ).focus(); 1487 1756 }, 1488 1757 … … 1564 1833 if ( ! panel.contentContainer.parent().is( panel.headContainer ) ) { 1565 1834 container.append( panel.contentContainer ); 1566 panel.renderContent();1567 }1835 } 1836 panel.renderContent(); 1568 1837 1569 1838 panel.deferred.embedded.resolve(); … … 1753 2022 } 1754 2023 }); 2024 2025 2026 /** 2027 * wp.customize.ThemesPanel 2028 * 2029 * Custom section for themes that displays without the customize preview. 2030 * 2031 * @constructor 2032 * @augments wp.customize.Panel 2033 * @augments wp.customize.Container 2034 */ 2035 api.ThemesPanel = api.Panel.extend({ 2036 installingThemes: [], 2037 2038 /** 2039 * @since 4.7.0 2040 */ 2041 attachEvents: function () { 2042 var panel = this; 2043 2044 // Attach regular panel events. 2045 api.Panel.prototype.attachEvents.apply( this ); 2046 2047 // Collapse panel to customize the current theme. 2048 panel.contentContainer.on( 'click', '.customize-theme', function() { 2049 panel.collapse(); 2050 }); 2051 2052 // Toggle between filtering and browsing themes on mobile. 2053 panel.contentContainer.on( 'click', '.see-themes, .filter-themes', function() { 2054 $( '.wp-full-overlay' ).toggleClass( 'showing-themes' ); 2055 }); 2056 2057 // Install (and maybe preview) a theme. 2058 panel.contentContainer.on( 'click', '.theme-install', function( event ) { 2059 panel.installTheme( event ); 2060 }); 2061 2062 // Update a theme. Theme cards have the class, the details modal has the id. 2063 panel.contentContainer.on( 'click', '.update-theme, #update-theme', function( event ) { 2064 // #update-theme is a link. 2065 event.preventDefault(); 2066 event.stopPropagation(); 2067 2068 panel.updateTheme( event ); 2069 }); 2070 2071 // Delete a theme. 2072 panel.contentContainer.on( 'click', '.delete-theme', function( event ) { 2073 panel.deleteTheme( event ); 2074 }); 2075 2076 _.bindAll( this, 'installTheme', 'updateTheme' ); 2077 }, 2078 2079 /** 2080 * Update UI to reflect expanded state 2081 * 2082 * @since 4.7.0 2083 * 2084 * @param {Boolean} expanded 2085 * @param {Object} args 2086 * @param {Boolean} args.unchanged 2087 * @param {Function} args.completeCallback 2088 */ 2089 onChangeExpanded: function ( expanded, args ) { 2090 2091 // Expand/collapse the panel normally. 2092 api.Panel.prototype.onChangeExpanded.apply( this, [ expanded, args ] ); 2093 2094 // Immediately call the complete callback if there were no changes 2095 if ( args.unchanged ) { 2096 if ( args.completeCallback ) { 2097 args.completeCallback(); 2098 } 2099 return; 2100 } 2101 2102 // Note: there is a second argument 'args' passed 2103 var panel = this, 2104 overlay = panel.headContainer.closest( '.wp-full-overlay' ); 2105 2106 if ( expanded ) { 2107 overlay 2108 .addClass( 'in-themes-panel' ).addClass( 'showing-themes' ) 2109 .delay( 200 ).find( '.customize-themes-full-container' ).addClass( 'animate' ); 2110 2111 // Automatically open the installed themes section. 2112 api.section( 'installed_themes' ).expand(); 2113 } else { 2114 overlay 2115 .removeClass( 'in-themes-panel' ) 2116 .find( '.customize-themes-full-container' ).removeClass( 'animate' ); 2117 } 2118 }, 2119 2120 /** 2121 * Install a theme via wp.updates. 2122 * 2123 * @since 4.7.0 2124 */ 2125 installTheme: function( event ) { 2126 var panel = this, preview = false, slug = $( event.target ).data( 'slug' ); 2127 2128 if ( -1 !== $.inArray( this.installingThemes, slug ) ) { 2129 return; // Theme is already being installed. 2130 } 2131 2132 wp.updates.maybeRequestFilesystemCredentials( event ); 2133 2134 $( document ).one( 'wp-theme-install-success', function( event, response ) { 2135 var theme = false, customizeId, themeControl; 2136 if ( preview ) { 2137 2138 panel.loadThemePreview( slug ).fail( function() { 2139 $( '.wp-full-overlay' ).removeClass( 'customize-loading' ); 2140 } ); 2141 2142 } else { 2143 api.control.each( function( control ) { 2144 if ( 'theme' === control.params.type && control.params.theme.id === response.slug ) { 2145 theme = control.params.theme; // Used below to add theme control. 2146 control.rerenderAsInstalled( true ); 2147 } 2148 }); 2149 2150 // Don't add the same theme more than once. 2151 if ( ! theme || 'undefined' !== typeof api.control( 'installed_theme_' + theme.id ) ) { 2152 return; 2153 } 2154 2155 // Add theme control to installed section. 2156 theme.type = 'installed'; 2157 customizeId = 'installed_theme_' + theme.id; 2158 themeControl = new api.controlConstructor.theme( customizeId, { 2159 params: { 2160 type: 'theme', 2161 content: $( '<li class="customize-control customize-control-theme"></li>' ).attr( 'id', 'customize-control-theme-installed_' + theme.id ).prop( 'outerHTML' ), 2162 section: 'installed_themes', 2163 active: true, 2164 theme: theme, 2165 priority: 0 // Add all newly-installed themes to the top. 2166 }, 2167 previewer: api.previewer 2168 } ); 2169 2170 api.control.add( customizeId, themeControl ); 2171 api.control( customizeId ).container.trigger( 'render-screenshot' ); 2172 2173 // Close the details modal if it's open to the installed theme. 2174 api.section.each( function( section ) { 2175 if ( 'themes' === section.params.type ) { 2176 if ( theme.id === section.currentTheme ) { // Don't close the modal if the user has navigated elsewhere. 2177 section.closeDetails(); 2178 } 2179 } 2180 }); 2181 } 2182 } ); 2183 2184 this.installingThemes.push( $( event.target ).data( 'slug' ) ); // Note: we don't remove elements from installingThemes, since they shouldn't be installed again. 2185 wp.updates.installTheme( { 2186 slug: slug 2187 } ); 2188 2189 // Also preview the theme as the event is triggered on Install & Preview. 2190 if ( $( event.target ).hasClass( 'preview' ) ) { 2191 preview = true; 2192 $( '.wp-full-overlay' ).addClass( 'customize-loading' ); 2193 } 2194 }, 2195 2196 /** 2197 * Load theme preview. 2198 * 2199 * @since 4.7.0 2200 * 2201 * @param {string} themeId Theme ID. 2202 * @returns {jQuery.promise} Promise. 2203 */ 2204 loadThemePreview: function( themeId ) { 2205 var deferred = $.Deferred(), onceProcessingComplete, overlay, urlParser; 2206 2207 urlParser = document.createElement( 'a' ); 2208 urlParser.href = location.href; 2209 urlParser.search = $.param( _.extend( 2210 api.utils.parseQueryString( urlParser.search.substr( 1 ) ), 2211 { 2212 theme: themeId, 2213 changeset_uuid: api.settings.changeset.uuid 2214 } 2215 ) ); 2216 2217 overlay = $( '.wp-full-overlay' ); 2218 overlay.addClass( 'customize-loading' ); 2219 2220 onceProcessingComplete = function() { 2221 var request; 2222 if ( api.state( 'processing' ).get() > 0 ) { 2223 return; 2224 } 2225 2226 api.state( 'processing' ).unbind( onceProcessingComplete ); 2227 2228 request = api.requestChangesetUpdate(); 2229 request.done( function() { 2230 $( window ).off( 'beforeunload.customize-confirm' ); 2231 window.location.href = urlParser.href; 2232 } ); 2233 request.fail( function() { 2234 overlay.removeClass( 'customize-loading' ); 2235 } ); 2236 }; 2237 2238 if ( 0 === api.state( 'processing' ).get() ) { 2239 onceProcessingComplete(); 2240 } else { 2241 api.state( 'processing' ).bind( onceProcessingComplete ); 2242 } 2243 2244 return deferred.promise(); 2245 }, 2246 2247 /** 2248 * Update a theme via wp.updates. 2249 * 2250 * @since 4.7.0 2251 */ 2252 updateTheme: function( event ) { 2253 wp.updates.maybeRequestFilesystemCredentials( event ); 2254 2255 $( document ).one( 'wp-theme-update-success', function( event, response ) { 2256 // Rerender the control to reflect the update. 2257 api.control.each( function( control ) { 2258 if ( 'theme' === control.params.type && control.params.theme.id === response.slug ) { 2259 control.params.theme.hasUpdate = false; 2260 control.rerenderAsInstalled( true ); 2261 } 2262 }); 2263 } ); 2264 2265 wp.updates.updateTheme( { 2266 slug: $( event.target ).closest( '.notice' ).data( 'slug' ) 2267 } ); 2268 }, 2269 2270 /** 2271 * Delete a theme via wp.updates. 2272 * 2273 * @since 4.7.0 2274 */ 2275 deleteTheme: function( event ) { 2276 var theme, section; 2277 theme = $( event.target ).data( 'slug' ); 2278 section = api.section( 'installed_themes' ); 2279 2280 event.preventDefault(); 2281 2282 // Confirmation dialog for deleting a theme. 2283 if ( ! window.confirm( api.settings.l10n.confirmDeleteTheme ) ) { 2284 return; 2285 } 2286 2287 wp.updates.maybeRequestFilesystemCredentials( event ); 2288 2289 $( document ).one( 'wp-theme-delete-success', function() { 2290 var control = api.control( 'installed_theme_' + theme ); 2291 2292 // Remove theme control. 2293 control.container.remove(); 2294 api.control.remove( control.id ); 2295 2296 // Update installed count. 2297 section.loaded = section.loaded - 1; 2298 section.updateCount(); 2299 2300 // Rerender any other theme controls as uninstalled. 2301 api.control.each( function( control ) { 2302 if ( 'theme' === control.params.type && control.params.theme.id === theme ) { 2303 control.rerenderAsInstalled( false ); 2304 } 2305 }); 2306 } ); 2307 2308 wp.updates.deleteTheme( { 2309 slug: theme 2310 } ); 2311 2312 // Close modal and focus the section. 2313 section.closeDetails(); 2314 section.focus(); 2315 } 2316 2317 }); 2318 1755 2319 1756 2320 /** … … 2049 2613 * @param {Object} args 2050 2614 * @param {Number} args.duration 2051 * @param { Callback} args.completeCallback2615 * @param {Function} args.completeCallback 2052 2616 */ 2053 2617 onChangeActive: function ( active, args ) { … … 3072 3636 3073 3637 touchDrag: false, 3074 isRendered: false, 3075 3076 /** 3077 * Defer rendering the theme control until the section is displayed. 3078 * 3079 * @since 4.2.0 3080 */ 3081 renderContent: function () { 3082 var control = this, 3083 renderContentArgs = arguments; 3084 3085 api.section( control.section(), function( section ) { 3086 if ( section.expanded() ) { 3087 api.Control.prototype.renderContent.apply( control, renderContentArgs ); 3088 control.isRendered = true; 3089 } else { 3090 section.expanded.bind( function( expanded ) { 3091 if ( expanded && ! control.isRendered ) { 3092 api.Control.prototype.renderContent.apply( control, renderContentArgs ); 3093 control.isRendered = true; 3094 } 3095 } ); 3096 } 3097 } ); 3098 }, 3638 screenshotRendered: false, 3099 3639 3100 3640 /** … … 3120 3660 3121 3661 // Prevent the modal from showing when the user clicks the action button. 3122 if ( $( event.target ).is( '.theme-actions .button ' ) ) {3662 if ( $( event.target ).is( '.theme-actions .button, .update-theme' ) ) { 3123 3663 return; 3124 3664 } 3125 3665 3126 api.section( control.section() ).loadThemePreview( control.params.theme.id );3127 });3128 3129 control.container.on( 'click keydown', '.theme-actions .theme-details', function( event ) {3130 if ( api.utils.isKeydownButNotEnterEvent( event ) ) {3131 return;3132 }3133 3134 3666 event.preventDefault(); // Keep this AFTER the key filter above 3135 3136 3667 api.section( control.section() ).showDetails( control.params.theme ); 3137 3668 }); … … 3144 3675 $screenshot.attr( 'src', source ); 3145 3676 } 3146 }); 3147 }, 3148 3149 /** 3150 * Show or hide the theme based on the presence of the term in the title, description, and author. 3677 control.screenshotRendered = true; 3678 }); 3679 }, 3680 3681 /** 3682 * Show or hide the theme based on the presence of the term in the title, description, tags, and author. 3151 3683 * 3152 3684 * @since 4.2.0 … … 3164 3696 control.deactivate(); 3165 3697 } 3698 }, 3699 3700 /** 3701 * Rerender the theme from its JS template with the installed type. 3702 * 3703 * @since 4.7.0 3704 */ 3705 rerenderAsInstalled: function( installed ) { 3706 var control = this, section; 3707 if ( installed ) { 3708 control.params.theme.type = 'installed'; 3709 } else { 3710 section = api.section( control.params.section ); 3711 control.params.theme.type = section.params.action; 3712 } 3713 control.renderContent(); // replaces existing content 3714 control.container.trigger( 'render-screenshot' ); 3166 3715 } 3167 3716 }); … … 3845 4394 theme: api.ThemeControl 3846 4395 }; 3847 api.panelConstructor = {}; 4396 api.panelConstructor = { 4397 themes: api.ThemesPanel 4398 }; 3848 4399 api.sectionConstructor = { 3849 4400 themes: api.ThemesSection … … 3963 4514 // Sort the sections within each panel 3964 4515 api.panel.each( function ( panel ) { 4516 if ( 'themes' === panel.id ) { 4517 return; // Don't reflow theme sections, as doing so moves them after the themes container. 4518 } 4519 3965 4520 var sections = panel.sections(), 3966 4521 sectionHeadContainers = _.pluck( sections, 'headContainer' ); … … 4568 5123 collapsedObject = expandedControls[0] || expandedSections[0] || expandedPanels[0]; 4569 5124 if ( collapsedObject ) { 5125 if ( 'themes' === collapsedObject.params.type ) { 5126 // Themes panel or section. 5127 if ( $( 'body' ).hasClass( 'modal-open' ) ) { 5128 collapsedObject.closeDetails(); 5129 } else { 5130 // If we're collapsing a section, collapse the panel also. 5131 wp.customize.panel( 'themes' ).collapse(); 5132 } 5133 return; 5134 } 4570 5135 collapsedObject.collapse(); 4571 5136 event.preventDefault(); -
trunk/src/wp-admin/js/updates.js
r38704 r38813 180 180 $notice.replaceWith( $adminNotice ); 181 181 } else { 182 $( '.wrap' ).find( '> h1' ).after( $adminNotice ); 182 if ( 'customize' === pagenow ) { 183 $( '.customize-themes-notifications' ).append( $adminNotice ); 184 } else { 185 $( '.wrap' ).find( '> h1' ).after( $adminNotice ); 186 } 183 187 } 184 188 … … 908 912 $notice = $( '[data-slug="' + args.slug + '"]' ).find( '.update-message' ).removeClass( 'notice-error' ).addClass( 'updating-message notice-warning' ).find( 'p' ); 909 913 914 } else if ( 'customize' === pagenow ) { 915 916 // Update the theme details UI. 917 $notice = $( '#update-theme' ).closest( '.notice' ).removeClass( 'notice-large' ); 918 919 $notice.find( 'h3' ).remove(); 920 921 // Add the top-level UI, and update both. 922 $notice = $notice.add( $( '#customize-control-theme-installed_' + args.slug ).find( '.update-message' ) ); 923 $notice = $notice.addClass( 'updating-message' ).find( 'p' ); 924 910 925 } else { 911 926 $notice = $( '#update-theme' ).closest( '.notice' ).removeClass( 'notice-large' ); … … 950 965 $notice, newText; 951 966 967 if ( 'customize' === pagenow ) { 968 $theme = wp.customize.control( 'installed_theme_' + response.slug ).container; 969 } 970 952 971 if ( 'themes-network' === pagenow ) { 953 972 $notice = $theme.find( '.update-message' ); … … 1002 1021 if ( wp.updates.maybeHandleCredentialError( response, 'update-theme' ) ) { 1003 1022 return; 1023 } 1024 1025 if ( 'customize' === pagenow ) { 1026 $theme = wp.customize.control( 'installed_theme_' + response.slug ).container; 1004 1027 } 1005 1028 … … 1140 1163 } 1141 1164 1142 if ( $document.find( 'body' ).hasClass( 'full-overlay-active' ) ) { 1143 $button = $( '.theme-install[data-slug="' + response.slug + '"]' ); 1144 $card = $( '.install-theme-info' ).prepend( $message ); 1165 if ( 'customize' === pagenow ) { 1166 if ( $document.find( 'body' ).hasClass( 'modal-open' ) ) { 1167 $button = $( '.theme-install[data-slug="' + response.slug + '"]' ); 1168 $card = $( '.theme-overlay .theme-info' ).prepend( $message ); 1169 } else { 1170 $button = $( '.theme-install[data-slug="' + response.slug + '"]' ); 1171 $card = $button.closest( '.theme' ).addClass( 'theme-install-failed' ).append( $message ); 1172 } 1173 $( '.wp-full-overlay' ).removeClass( 'customize-loading' ); 1145 1174 } else { 1146 $card = $( '[data-slug="' + response.slug + '"]' ).removeClass( 'focus' ).addClass( 'theme-install-failed' ).append( $message ); 1147 $button = $card.find( '.theme-install' ); 1175 if ( $document.find( 'body' ).hasClass( 'full-overlay-active' ) ) { 1176 $button = $( '.theme-install[data-slug="' + response.slug + '"]' ); 1177 $card = $( '.install-theme-info' ).prepend( $message ); 1178 } else { 1179 $card = $( '[data-slug="' + response.slug + '"]' ).removeClass( 'focus' ).addClass( 'theme-install-failed' ).append( $message ); 1180 $button = $card.find( '.theme-install' ); 1181 } 1148 1182 } 1149 1183 -
trunk/src/wp-includes/class-wp-customize-manager.php
r38810 r38813 295 295 require_once( ABSPATH . WPINC . '/customize/class-wp-customize-nav-menus-panel.php' ); 296 296 297 require_once( ABSPATH . WPINC . '/customize/class-wp-customize-themes-panel.php' ); 297 298 require_once( ABSPATH . WPINC . '/customize/class-wp-customize-themes-section.php' ); 298 299 require_once( ABSPATH . WPINC . '/customize/class-wp-customize-sidebar-section.php' ); … … 349 350 add_action( 'wp_ajax_customize_save', array( $this, 'save' ) ); 350 351 add_action( 'wp_ajax_customize_refresh_nonces', array( $this, 'refresh_nonces' ) ); 352 add_action( 'wp_ajax_customize-load-themes', array( $this, 'load_themes_ajax' ) ); 351 353 352 354 add_action( 'customize_register', array( $this, 'register_controls' ) ); … … 362 364 // Export the settings to JS via the _wpCustomizeSettings variable. 363 365 add_action( 'customize_controls_print_footer_scripts', array( $this, 'customize_pane_settings' ), 1000 ); 366 367 // Add theme update notices. 368 if ( current_user_can( 'install_themes' ) || current_user_can( 'update_themes' ) ) { 369 require_once( ABSPATH . '/wp-admin/includes/update.php' ); 370 add_action( 'customize_controls_print_footer_scripts', 'wp_print_admin_notice_templates' ); 371 } 364 372 } 365 373 … … 2585 2593 $control->enqueue(); 2586 2594 } 2595 if ( ! is_multisite() && ( current_user_can( 'install_themes' ) || current_user_can( 'update_themes' ) || current_user_can( 'delete_themes' ) ) ) { 2596 wp_enqueue_script( 'updates' ); 2597 } 2587 2598 } 2588 2599 … … 2799 2810 'save' => wp_create_nonce( 'save-customize_' . $this->get_stylesheet() ), 2800 2811 'preview' => wp_create_nonce( 'preview-customize_' . $this->get_stylesheet() ), 2812 'switch-themes' => wp_create_nonce( 'switch-themes' ), 2801 2813 ); 2802 2814 … … 2872 2884 'documentTitleTmpl' => $this->get_document_title_template(), 2873 2885 'previewableDevices' => $this->get_previewable_devices(), 2886 'l10n' => array( 2887 'confirmDeleteTheme' => __( 'Are you sure you want to delete this theme?' ), 2888 /* translators: %d is the number of theme search results, which cannot consider singular vs. plural forms */ 2889 'themeSearchResults' => __( '%d themes found' ), 2890 /* translators: %d is the number of themes being displayed, which cannot consider singular vs. plural forms */ 2891 'announceThemeCount' => __( 'Displaying %d themes' ), 2892 'announceThemeDetails' => __( 'Showing details for theme: %s' ), 2893 ), 2874 2894 ); 2875 2895 … … 2975 2995 /* Panel, Section, and Control Types */ 2976 2996 $this->register_panel_type( 'WP_Customize_Panel' ); 2997 $this->register_panel_type( 'WP_Customize_Themes_Panel' ); 2977 2998 $this->register_section_type( 'WP_Customize_Section' ); 2978 2999 $this->register_section_type( 'WP_Customize_Sidebar_Section' ); 3000 $this->register_section_type( 'WP_Customize_Themes_Section' ); 2979 3001 $this->register_control_type( 'WP_Customize_Color_Control' ); 2980 3002 $this->register_control_type( 'WP_Customize_Media_Control' ); … … 2986 3008 $this->register_control_type( 'WP_Customize_Theme_Control' ); 2987 3009 2988 /* Themes */ 2989 2990 $this->add_section( new WP_Customize_Themes_Section( $this, 'themes', array( 2991 'title' => $this->theme()->display( 'Name' ), 2992 'capability' => 'switch_themes', 2993 'priority' => 0, 3010 /* Themes (controls are loaded via ajax) */ 3011 3012 $this->add_panel( new WP_Customize_Themes_Panel( $this, 'themes', array( 3013 'title' => $this->theme()->display( 'Name' ), 3014 '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.' ), 3015 'capability' => 'switch_themes', 3016 'priority' => 0, 3017 ) ) ); 3018 3019 $this->add_section( new WP_Customize_Themes_Section( $this, 'installed_themes', array( 3020 'title' => __( 'Installed' ), 3021 'text_before' => __( 'Your local site' ), 3022 'action' => 'installed', 3023 'capability' => 'switch_themes', 3024 'panel' => 'themes', 3025 'priority' => 0, 3026 ) ) ); 3027 3028 $this->add_section( new WP_Customize_Themes_Section( $this, 'search_themes', array( 3029 'title' => __( 'Search themes…' ), 3030 'text_before' => __( 'Browse all WordPress.org themes' ), 3031 'action' => 'search', 3032 'capability' => 'install_themes', 3033 'panel' => 'themes', 3034 'priority' => 5, 3035 ) ) ); 3036 3037 $this->add_section( new WP_Customize_Themes_Section( $this, 'featured_themes', array( 3038 'title' => __( 'Featured' ), 3039 'action' => 'featured', 3040 'capability' => 'install_themes', 3041 'panel' => 'themes', 3042 'priority' => 10, 3043 ) ) ); 3044 3045 $this->add_section( new WP_Customize_Themes_Section( $this, 'popular_themes', array( 3046 'title' => __( 'Popular' ), 3047 'action' => 'popular', 3048 'capability' => 'install_themes', 3049 'panel' => 'themes', 3050 'priority' => 15, 3051 ) ) ); 3052 3053 $this->add_section( new WP_Customize_Themes_Section( $this, 'latest_themes', array( 3054 'title' => __( 'Latest' ), 3055 'action' => 'latest', 3056 'capability' => 'install_themes', 3057 'panel' => 'themes', 3058 'priority' => 20, 3059 ) ) ); 3060 3061 $this->add_section( new WP_Customize_Themes_Section( $this, 'feature_filter_themes', array( 3062 'title' => __( 'Feature Filter' ), 3063 'action' => 'feature_filter', 3064 'capability' => 'install_themes', 3065 'panel' => 'themes', 3066 'priority' => 25, 3067 ) ) ); 3068 3069 $this->add_section( new WP_Customize_Themes_Section( $this, 'favorites_themes', array( 3070 'title' => __( 'Favorites' ), 3071 'action' => 'favorites', 3072 'capability' => 'install_themes', 3073 'panel' => 'themes', 3074 'priority' => 30, 2994 3075 ) ) ); 2995 3076 … … 2998 3079 'capability' => 'switch_themes', 2999 3080 ) ) ); 3000 3001 require_once( ABSPATH . 'wp-admin/includes/theme.php' );3002 3003 // Theme Controls.3004 3005 // Add a control for the active/original theme.3006 if ( ! $this->is_theme_active() ) {3007 $themes = wp_prepare_themes_for_js( array( wp_get_theme( $this->original_stylesheet ) ) );3008 $active_theme = current( $themes );3009 $active_theme['isActiveTheme'] = true;3010 $this->add_control( new WP_Customize_Theme_Control( $this, $active_theme['id'], array(3011 'theme' => $active_theme,3012 'section' => 'themes',3013 'settings' => 'active_theme',3014 ) ) );3015 }3016 3017 $themes = wp_prepare_themes_for_js();3018 foreach ( $themes as $theme ) {3019 if ( $theme['active'] || $theme['id'] === $this->original_stylesheet ) {3020 continue;3021 }3022 3023 $theme_id = 'theme_' . $theme['id'];3024 $theme['isActiveTheme'] = false;3025 $this->add_control( new WP_Customize_Theme_Control( $this, $theme_id, array(3026 'theme' => $theme,3027 'section' => 'themes',3028 'settings' => 'active_theme',3029 ) ) );3030 }3031 3081 3032 3082 /* Site Identity */ … … 3357 3407 3358 3408 /** 3409 * Load themes into the theme browsing/installation UI. 3410 * 3411 * @since 4.7.0 3412 * @access public 3413 */ 3414 public function load_themes_ajax() { 3415 check_ajax_referer( 'switch-themes', 'switch-themes-nonce' ); 3416 3417 if ( ! current_user_can( 'switch_themes' ) ) { 3418 wp_die( -1 ); 3419 } 3420 3421 if ( empty( $_POST['theme_action'] ) ) { 3422 wp_send_json_error( 'missing_theme_action' ); 3423 } 3424 3425 if ( 'search' === $_POST['theme_action'] && ! array_key_exists( 'search', $_POST ) ) { 3426 wp_send_json_error( 'empty_search' ); 3427 } elseif ( 'favorites' === $_POST['theme_action'] && ! array_key_exists( 'user', $_POST ) ) { 3428 wp_send_json_error( 'empty_user' ); 3429 } elseif ( 'feature_filter' === $_POST['theme_action'] && ! array_key_exists( 'tags', $_POST ) ) { 3430 wp_send_json_error( 'no_features' ); 3431 } 3432 3433 require_once( ABSPATH . 'wp-admin/includes/theme.php' ); 3434 if ( 'installed' === $_POST['theme_action'] ) { 3435 $themes = array( 'themes' => wp_prepare_themes_for_js() ); 3436 foreach ( $themes['themes'] as &$theme ) { 3437 $theme['type'] = 'installed'; 3438 // Set active based on customized theme. 3439 if ( $_POST['customized_theme'] === $theme['id'] ) { 3440 $theme['active'] = true; 3441 } else { 3442 $theme['active'] = false; 3443 } 3444 } 3445 } else { 3446 if ( ! current_user_can( 'install_themes' ) ) { 3447 wp_die( -1 ); 3448 } 3449 3450 // Arguments for all queries. 3451 $args = array( 3452 'per_page' => 100, 3453 'page' => absint( $_POST['page'] ), 3454 'fields' => array( 3455 'slug' => true, 3456 'screenshot' => true, 3457 'description' => true, 3458 'requires' => true, 3459 'rating' => true, 3460 'downloaded' => true, 3461 'downloadLink' => true, 3462 'last_updated' => true, 3463 'homepage' => true, 3464 'num_ratings' => true, 3465 'tags' => true, 3466 ), 3467 ); 3468 3469 // Specialized handling for each query. 3470 switch ( $_POST['theme_action'] ) { 3471 case 'search': 3472 $args['search'] = wp_unslash( $_POST['search'] ); 3473 break; 3474 case 'favorites': 3475 $args['user'] = wp_unslash( $_POST['user'] ); 3476 case 'featured': 3477 case 'popular': 3478 $args['browse'] = wp_unslash( $_POST['theme_action'] ); 3479 break; 3480 case 'latest': 3481 $args['browse'] = 'new'; 3482 break; 3483 case 'feature_filter': 3484 $args['tag'] = wp_unslash( $_POST['tags'] ); 3485 break; 3486 } 3487 3488 // Load themes from the .org API. 3489 $themes = themes_api( 'query_themes', $args ); 3490 if ( is_wp_error( $themes ) ) { 3491 wp_send_json_error(); 3492 } 3493 3494 // This list matches the allowed tags in wp-admin/includes/theme-install.php. 3495 $themes_allowedtags = array('a' => array('href' => array(), 'title' => array(), 'target' => array()), 3496 'abbr' => array('title' => array()), 'acronym' => array('title' => array()), 3497 'code' => array(), 'pre' => array(), 'em' => array(), 'strong' => array(), 3498 'div' => array(), 'p' => array(), 'ul' => array(), 'ol' => array(), 'li' => array(), 3499 'h1' => array(), 'h2' => array(), 'h3' => array(), 'h4' => array(), 'h5' => array(), 'h6' => array(), 3500 'img' => array('src' => array(), 'class' => array(), 'alt' => array()) 3501 ); 3502 3503 // Prepare a list of installed themes to check against before the loop. 3504 $installed_themes = array(); 3505 $wp_themes = wp_get_themes(); 3506 foreach ( $wp_themes as $theme ) { 3507 $installed_themes[] = $theme->get_stylesheet(); 3508 } 3509 $update_php = network_admin_url( 'update.php?action=install-theme' ); 3510 foreach ( $themes->themes as &$theme ) { 3511 $theme->install_url = add_query_arg( array( 3512 'theme' => $theme->slug, 3513 '_wpnonce' => wp_create_nonce( 'install-theme_' . $theme->slug ), 3514 ), $update_php ); 3515 3516 $theme->name = wp_kses( $theme->name, $themes_allowedtags ); 3517 $theme->author = wp_kses( $theme->author, $themes_allowedtags ); 3518 $theme->version = wp_kses( $theme->version, $themes_allowedtags ); 3519 $theme->description = wp_kses( $theme->description, $themes_allowedtags ); 3520 $theme->tags = implode( ', ', $theme->tags ); 3521 $theme->stars = wp_star_rating( array( 'rating' => $theme->rating, 'type' => 'percent', 'number' => $theme->num_ratings, 'echo' => false ) ); 3522 $theme->num_ratings = number_format_i18n( $theme->num_ratings ); 3523 $theme->preview_url = set_url_scheme( $theme->preview_url ); 3524 3525 // Handle themes that are already installed as installed themes. 3526 if ( in_array( $theme->slug, $installed_themes, true ) ) { 3527 $theme->type = 'installed'; 3528 } else { 3529 $theme->type = $_POST['theme_action']; 3530 } 3531 3532 // Set active based on customized theme. 3533 if ( $_POST['customized_theme'] === $theme->slug ) { 3534 $theme->active = true; 3535 } else { 3536 $theme->active = false; 3537 } 3538 3539 // Map available theme properties to installed theme properties. 3540 $theme->id = $theme->slug; 3541 $theme->screenshot = array( $theme->screenshot_url ); 3542 $theme->authorAndUri = $theme->author; 3543 unset( $theme->slug ); 3544 unset( $theme->screenshot_url ); 3545 unset( $theme->author ); 3546 } // End foreach(). 3547 } // End if(). 3548 wp_send_json_success( $themes ); 3549 } 3550 3551 3552 /** 3359 3553 * Callback for validating the header_textcolor value. 3360 3554 * -
trunk/src/wp-includes/customize/class-wp-customize-theme-control.php
r38810 r38813 63 63 */ 64 64 public function content_template() { 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 ); 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 }}' ); 69 73 ?> 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">74 <# if ( data.theme.active ) { #> 75 <div class="theme active" tabindex="0" aria-describedby="{{ data.section }}-{{ data.theme.id }}-action {{ data.theme.id }}-name"> 72 76 <# } else { #> 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">77 <div class="theme" tabindex="0" aria-describedby="{{ data.section }}-{{ data.theme.id }}-action {{ data.theme.id }}-name"> 74 78 <# } #> 75 79 76 <# if ( data.theme.screenshot [0] ) { #>80 <# if ( data.theme.screenshot && data.theme.screenshot[0] ) { #> 77 81 <div class="theme-screenshot"> 78 82 <img data-src="{{ data.theme.screenshot[0] }}" alt="" /> … … 82 86 <# } #> 83 87 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> 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> 88 93 <# } #> 89 94 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"> 95 <# if ( data.theme.active ) { #> 96 <h3 class="theme-name" id="{{ data.section }}-{{ data.theme.id }}-name"> 94 97 <?php 95 98 /* translators: %s: theme name */ 96 printf( __( '<span> Active:</span> %s' ), '{{{ data.theme.name }}}' );99 printf( __( '<span>Current:</span> %s' ), '{{ data.theme.name }}' ); 97 100 ?> 98 101 </h3> 102 <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-theme-id="{{ data.theme.id }}"><?php _e( 'Live Preview' ); ?></span> 110 </div> 111 <div class="notice notice-success notice-alt"><p><?php _e( 'Installed' ); ?></p></div> 99 112 <# } else { #> 100 <h3 class="theme-name" id="{{ data. theme.id }}-name">{{{ data.theme.name }}}</h3>113 <h3 class="theme-name" id="{{ data.section }}-{{ data.theme.id }}-name">{{ data.theme.name }}</h3> 101 114 <div class="theme-actions"> 102 <button type="button" class="button theme-details"><?php _e( 'Theme Details' ); ?></button>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 }}" data-theme-id="{{ data.theme.id }}"><?php _e( 'Install & Preview' ); ?></button> 103 116 </div> 104 117 <# } #> -
trunk/src/wp-includes/customize/class-wp-customize-themes-section.php
r35943 r38813 11 11 * Customize Themes Section class. 12 12 * 13 * A UI container for theme controls, which behaves like a backwards Panel.13 * A UI container for theme controls, which are displayed in tabbed sections. 14 14 * 15 15 * @since 4.2.0 … … 29 29 30 30 /** 31 * Render the themes section, which behaves like a panel.31 * Theme section action. 32 32 * 33 * @since 4.2.0 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 34 71 * @access protected 35 72 */ 36 protected function render() { 37 $classes = 'accordion-section control-section control-section-' . $this->type; 73 protected function render_template() { 38 74 ?> 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; 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"'; 44 88 } else { 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"> 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"> 60 114 <?php 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; 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>'; 65 128 } 66 129 ?> 67 <button type="button" class="button customize-theme"><?php _e( 'Customize' ); ?></button>68 </h3>69 130 </div> 131 <# } #> 132 <div class="customize-themes-section themes-section-{{ data.id }} control-section-content themes-php"> 70 133 <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; ?>79 134 <div class="theme-browser rendered"> 80 <ul class="themes accordion-section-content"> 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"> 81 137 </ul> 138 <p class="no-themes"><?php _e( 'No themes found. Try a different search.' ); ?></p> 139 <p class="spinner"></p> 82 140 </div> 83 141 </div> -
trunk/tests/phpunit/tests/customize/manager.php
r38810 r38813 1487 1487 $this->assertNotEmpty( $data ); 1488 1488 1489 $this->assertEqualSets( array( 'theme', 'url', 'browser', 'panels', 'sections', 'nonce', 'autofocus', 'documentTitleTmpl', 'previewableDevices', 'changeset', 'timeouts' ), array_keys( $data ) );1489 $this->assertEqualSets( array( 'theme', 'url', 'browser', 'panels', 'sections', 'nonce', 'autofocus', 'documentTitleTmpl', 'previewableDevices', 'changeset', 'timeouts', 'l10n' ), array_keys( $data ) ); 1490 1490 $this->assertEquals( $autofocus, $data['autofocus'] ); 1491 1491 $this->assertArrayHasKey( 'save', $data['nonce'] );
Note: See TracChangeset
for help on using the changeset viewer.