Ticket #37974: 37974.4.diff
File 37974.4.diff, 41.0 KB (added by , 8 years ago) |
---|
-
src/wp-admin/css/customize-controls.css
687 687 688 688 /* Style for custom settings */ 689 689 690 #customize-control-front_page_sections { 691 border-top: 1px solid #ddd; 692 margin-top: 8px; 693 padding-top: 16px; 694 } 695 690 696 /** 691 697 * Dropdowns 692 698 */ … … 1205 1211 */ 1206 1212 1207 1213 /* higher specificity than .wp-core-ui .button */ 1214 #customize-theme-controls .add-new-item, 1208 1215 #customize-theme-controls .add-new-widget, 1209 1216 #customize-theme-controls .add-new-menu-item { 1210 1217 cursor: pointer; … … 1219 1226 outline: none; 1220 1227 } 1221 1228 1229 .reordering .add-new-item, 1222 1230 .reordering .add-new-widget, 1223 1231 .reordering .add-new-menu-item { 1224 1232 opacity: 0.2; … … 1226 1234 cursor: not-allowed; /* doesn't work in conjunction with pointer-events */ 1227 1235 } 1228 1236 1237 .add-new-item:before, 1229 1238 .add-new-widget:before, 1230 1239 .add-new-menu-item:before, 1231 1240 #available-menu-items .new-content-item .add-content:before { … … 1282 1291 color: #00a0d2; 1283 1292 } 1284 1293 1294 .wp-reorder-nav { 1295 display: none; 1296 background-color: #fff; 1297 position: absolute; 1298 top: 0; 1299 right: 0; 1300 } 1301 1302 .reordering .wp-reorder-nav, 1303 .wp-reorder-nav.is-active { 1304 display: block; 1305 } 1306 1307 .wp-reorder-nav button, 1285 1308 .widget-reorder-nav span, 1286 1309 .menu-item-reorder-nav button { 1287 1310 position: relative; … … 1296 1319 outline: none; 1297 1320 } 1298 1321 1322 .wp-reorder-nav button, 1299 1323 .menu-item-reorder-nav button { 1300 1324 width: 30px; 1301 1325 height: 40px; … … 1305 1329 box-shadow: none; 1306 1330 } 1307 1331 1332 .wp-reorder-nav button:before, 1308 1333 .widget-reorder-nav span:before, 1309 1334 .menu-item-reorder-nav button:before { 1310 1335 display: inline-block; … … 1320 1345 -moz-osx-font-smoothing: grayscale; 1321 1346 } 1322 1347 1348 .wp-reorder-nav button:hover, 1349 .wp-reorder-nav button:focus, 1323 1350 .widget-reorder-nav span:hover, 1324 1351 .widget-reorder-nav span:focus, 1325 1352 .menu-item-reorder-nav button:hover, … … 1328 1355 background: #eee; 1329 1356 } 1330 1357 1358 .wp-reorder-nav button { 1359 width: 33px; 1360 height: 38px; 1361 } 1362 1363 .wp-reorder-nav button:before { 1364 font: normal 20px/38px dashicons; 1365 } 1366 1367 .move-item-down:before, 1331 1368 .move-widget-down:before, 1332 1369 .menus-move-down:before { 1333 1370 content: "\f347"; 1334 1371 } 1335 1372 1373 .move-item-up:before, 1336 1374 .move-widget-up:before, 1337 1375 .menus-move-up:before { 1338 1376 content: "\f343"; … … 1343 1381 .move-up-disabled .menus-move-up, 1344 1382 .move-down-disabled .menus-move-down, 1345 1383 .move-right-disabled .menus-move-right, 1346 .move-left-disabled .menus-move-left { 1384 .move-left-disabled .menus-move-left, 1385 .wp-item:first-child .move-item-up, 1386 .wp-item:last-child .move-item-down { 1347 1387 color: #d5d5d5; 1348 1388 background-color: #fff; 1349 1389 cursor: default; … … 1351 1391 } 1352 1392 1353 1393 /** 1354 * New widget and Add-menu-itemsmodes and panels1394 * New widget, Add-menu-items, and Drawer modes and panels 1355 1395 */ 1356 1396 1357 1397 .wp-full-overlay-main { … … 1359 1399 width: 100%; 1360 1400 } 1361 1401 1402 .customize-control.is-drawer-open .add-new-item, 1403 .customize-control.is-drawer-open .add-new-item:hover, 1362 1404 body.adding-widget .add-new-widget, 1363 1405 body.adding-widget .add-new-widget:hover, 1364 1406 .adding-menu-items .add-new-menu-item, … … 1372 1414 box-shadow: inset 0 2px 5px -3px rgba(0, 0, 0, 0.5); 1373 1415 } 1374 1416 1417 .customize-control.is-drawer-open .add-new-item:before, 1375 1418 body.adding-widget .add-new-widget:before, 1376 1419 .adding-menu-items .add-new-menu-item:before, 1377 1420 #accordion-section-add_menu .add-new-menu-item.open:before { … … 1380 1423 transform: rotate(45deg); 1381 1424 } 1382 1425 1426 .customize-drawer, 1383 1427 #available-widgets, 1384 1428 #available-menu-items { 1385 1429 position: absolute; … … 1398 1442 border-right: 1px solid #ddd; 1399 1443 } 1400 1444 1445 .customize-drawer .customize-section-title, 1401 1446 #available-widgets .customize-section-title, 1402 1447 #available-menu-items .customize-section-title { 1403 1448 display: none; … … 1425 1470 } 1426 1471 1427 1472 /* search field container */ 1473 .search-group, 1428 1474 #available-widgets-filter, 1429 1475 #available-menu-items-search .accordion-section-title { 1430 1476 padding: 13px 15px; … … 1433 1479 box-sizing: border-box; 1434 1480 } 1435 1481 1482 .search-group { 1483 position: relative; 1484 } 1485 1486 .search-group input, 1436 1487 #available-widgets-filter input, 1437 1488 #available-menu-items-search input { 1438 1489 width: 100%; … … 1441 1492 padding: 6px 30px; 1442 1493 } 1443 1494 1495 .search-group input::-ms-clear, 1444 1496 #available-widgets-filter input::-ms-clear, 1445 1497 #available-menu-items-search input::-ms-clear { 1446 1498 display: none; /* remove the "x" in IE, which conflicts with the "x" icon on button.clear-results */ 1447 1499 } 1448 1500 1501 .search-group .search-icon, 1449 1502 #available-menu-items-search .search-icon, 1450 1503 #available-widgets-filter .search-icon { 1451 1504 display: block; … … 1459 1512 color: #72777c; 1460 1513 } 1461 1514 1515 .search-group .clear-results, 1462 1516 #available-widgets-filter .clear-results, 1463 1517 #available-menu-items-search .clear-results { 1464 1518 position: absolute; … … 1475 1529 outline: 0; 1476 1530 } 1477 1531 1532 .search-group .clear-results, 1478 1533 #available-widgets-filter .clear-results, 1479 1534 #available-menu-items-search .clear-results, 1480 1535 #available-menu-items-search.loading .clear-results.is-visible { … … 1481 1536 display: none; 1482 1537 } 1483 1538 1539 .search-group .clear-results.is-visible, 1484 1540 #available-widgets-filter .clear-results.is-visible, 1485 1541 #available-menu-items-search .clear-results.is-visible { 1486 1542 display: block; 1487 1543 } 1488 1544 1545 .search-group .clear-results:before, 1489 1546 #available-widgets-filter .clear-results:before, 1490 1547 #available-menu-items-search .clear-results:before { 1491 1548 content: "\f335"; … … 1495 1552 -moz-osx-font-smoothing: grayscale; 1496 1553 } 1497 1554 1555 .search-group .clear-results:hover, 1556 .search-group .clear-results:focus, 1498 1557 #available-widgets-filter .clear-results:hover, 1499 1558 #available-widgets-filter .clear-results:focus, 1500 1559 #available-menu-items-search .clear-results:hover, … … 1502 1561 color: #dc3232; 1503 1562 } 1504 1563 1564 .search-group .clear-results:focus, 1505 1565 #available-widgets-filter .clear-results:focus, 1506 1566 #available-menu-items-search .clear-results:focus { 1507 1567 -webkit-box-shadow: … … 1512 1572 0 0 2px 1px rgba(30, 140, 190, .8); 1513 1573 } 1514 1574 1575 .search-group .search-icon:after, 1515 1576 #available-menu-items-search .search-icon:after, 1516 1577 #available-widgets-filter .search-icon:after { 1517 1578 content: "\f179"; … … 1521 1582 -moz-osx-font-smoothing: grayscale; 1522 1583 } 1523 1584 1585 .search-group .spinner { 1586 margin: 0; 1587 position: absolute; 1588 top: 20px; 1589 right: 20px; 1590 } 1591 1592 .search-group.is-searching .clear-results { 1593 display: none; 1594 } 1595 1524 1596 .no-widgets-found-message { 1525 1597 display: none; 1526 1598 margin: 0; … … 1558 1630 position: static; 1559 1631 } 1560 1632 1633 .customize-drawer.is-open { 1634 left: 0; 1635 visibility: visible; 1636 } 1561 1637 1638 body.drawer-is-open .wp-full-overlay-main { 1639 left: 300px; 1640 } 1641 1642 body.drawer-is-open #customize-preview { 1643 opacity: 0.4; 1644 } 1645 1646 .ios .customize-drawer { 1647 -webkit-transition: left 0s; 1648 transition: left 0s; 1649 } 1650 1651 .customize-drawer-notice { 1652 padding: 15px; 1653 } 1654 1655 /* Sortable list items. */ 1656 1657 .wp-items-list { 1658 list-style: none; 1659 margin: 0 0 10px 0; 1660 padding: 0; 1661 position: relative; 1662 } 1663 1664 .wp-item { 1665 background: #fff; 1666 margin: -1px 0 0 0; 1667 padding: 0; 1668 } 1669 1670 .wp-item-header { 1671 border: 1px solid #dfdfdf; 1672 background: #fff; 1673 position: relative; 1674 } 1675 1676 .wp-item-delete { 1677 color: #a00; 1678 display: none; 1679 height: 38px; 1680 position: absolute; 1681 top: 0; 1682 right: 0; 1683 text-align: center; 1684 vertical-align: middle; 1685 width: 40px; 1686 } 1687 1688 .wp-item-delete:hover { 1689 color: #f00; 1690 } 1691 1692 .wp-item-delete:before { 1693 content: "\f335"; 1694 } 1695 1696 .wp-item-title { 1697 cursor: move; 1698 margin: 0; 1699 padding: 10px 20px; 1700 position: relative; 1701 word-wrap: break-word; 1702 } 1703 1704 .wp-item-title:before { 1705 content: ""; 1706 float: right; 1707 height: 28px; 1708 width: 46px; 1709 } 1710 1711 .wp-item.ui-sortable-helper { 1712 background: #f9f9f9; 1713 border: 1px solid #dfdfdf; 1714 } 1715 1716 .wp-item.ui-sortable-placeholder { 1717 background: transparent; 1718 border: 1px dashed #a0a5aa; 1719 margin-top: 0; 1720 margin-bottom: 1px; 1721 } 1722 1723 .wp-item:hover .wp-item-header { 1724 border-color: #999; 1725 z-index: 1; 1726 } 1727 1728 .customize-control.is-drawer-open .wp-item-delete { 1729 display: block; 1730 } 1731 1732 .customize-control.is-drawer-open .wp-reorder-nav { 1733 display: none; 1734 } 1735 1736 /* Search results. */ 1737 1738 .search-results { 1739 padding: 1px 0 15px; 1740 } 1741 1742 .search-results ul { 1743 margin: -1px 0 0; 1744 } 1745 1746 .search-results-item { 1747 background: #fff; 1748 border-color: #ddd; 1749 border-style: solid; 1750 border-width: 1px 0; 1751 clear: both; 1752 cursor: pointer; 1753 line-height: 10px; 1754 margin: -1px 0 0 0; 1755 padding: 10px 15px; 1756 position: relative; 1757 } 1758 1759 .search-results-item-title { 1760 display: block; 1761 font-size: 13px; 1762 font-weight: 600; 1763 line-height: 20px; 1764 padding-left: 20px; 1765 word-wrap: break-word; 1766 } 1767 1768 .search-results-item-type { 1769 color: #666; 1770 float: right; 1771 font-size: 12px; 1772 line-height: 20px; 1773 padding-left: 10px; 1774 text-align: right; 1775 } 1776 1777 .search-results-item-add { 1778 color: #82878c; 1779 height: 38px; 1780 position: absolute; 1781 top: 1px; 1782 left: 1px; 1783 width: 30px; 1784 } 1785 1786 .search-results-item-add:before { 1787 -webkit-border-radius: 50%; 1788 border-radius: 50%; 1789 content: "\f543"; 1790 height: 20px; 1791 position: relative; 1792 top: 0; 1793 left: 2px; 1794 } 1795 1796 .search-results-item:hover { 1797 border-color: #999; 1798 color: #0073aa; 1799 z-index: 1; 1800 } 1801 1802 .search-results-item:hover .search-results-item-add:before { 1803 color: #0073aa; 1804 } 1805 1806 .search-results-item.is-selected .search-results-item-add:before { 1807 content: "\f147"; 1808 } 1809 1810 .search-results-item-add, 1811 .wp-item-delete { 1812 cursor: pointer; 1813 display: inline-block; 1814 font-family: dashicons; 1815 font-size: 20px; 1816 -webkit-font-smoothing: antialiased; 1817 font-style: normal; 1818 font-weight: normal; 1819 line-height: 1; 1820 text-align: center; 1821 text-decoration: inherit; 1822 vertical-align: top; 1823 } 1824 1825 1562 1826 /* Responsive */ 1563 1827 .customize-controls-preview-toggle { 1564 1828 display: none; … … 1679 1943 margin-top: 6px; 1680 1944 } 1681 1945 1946 body.drawer-is-open .customize-drawer, 1682 1947 body.adding-widget div#available-widgets, 1683 1948 body.adding-menu-items div#available-menu-items { 1684 1949 top: 46px; … … 1687 1952 width: 100%; 1688 1953 } 1689 1954 1955 .customize-drawer .customize-section-title, 1690 1956 #available-widgets .customize-section-title, 1691 1957 #available-menu-items .customize-section-title { 1692 1958 display: block; … … 1693 1959 margin: 0; 1694 1960 } 1695 1961 1962 .customize-drawer .customize-section-back, 1696 1963 #available-widgets .customize-section-back, 1697 1964 #available-menu-items .customize-section-back { 1698 1965 height: 69px; 1699 1966 } 1700 1967 1968 .customize-drawer .customize-section-title h3, 1701 1969 #available-widgets .customize-section-title h3, 1702 1970 #available-menu-items .customize-section-title h3 { 1703 1971 font-size: 20px; … … 1712 1980 text-overflow: ellipsis; 1713 1981 } 1714 1982 1983 .customize-drawer .customize-section-title .customize-action, 1715 1984 #available-widgets .customize-section-title .customize-action, 1716 1985 #available-menu-items .customize-section-title .customize-action { 1717 1986 font-size: 13px; -
src/wp-admin/js/customize-post-collection.js
1 (function( wp, $ ) { 2 3 if ( ! wp || ! wp.customize ) { return; } 4 5 var api = wp.customize; 6 7 api.PostCollection = api.PostCollection || {}; 8 9 api.DrawerModel = Backbone.Model.extend({ 10 defaults: { 11 status: 'closed' 12 }, 13 14 close: function() { 15 this.set( 'status', 'closed' ); 16 }, 17 18 open: function() { 19 this.set( 'status', 'open' ); 20 }, 21 22 toggle: function() { 23 if ( 'open' === this.get( 'status' ) ) { 24 this.close(); 25 } else { 26 this.open(); 27 } 28 } 29 }); 30 31 api.DrawerManager = Backbone.Collection.extend({ 32 model: api.DrawerModel, 33 34 initialize: function() { 35 this.on( 'change:status', this.closeOtherDrawers ); 36 }, 37 38 closeOtherDrawers: function( model ) { 39 if ( 'open' === model.get( 'status' ) ) { 40 _.chain( this.models ).without( model ).invoke( 'close' ); 41 } 42 } 43 }); 44 45 api.DrawerView = wp.Backbone.View.extend({ 46 tagName: 'div', 47 className: 'customize-drawer', 48 49 initialize: function( options ) { 50 this.controller = options.controller; 51 this.listenTo( this.controller, 'change:status', this.updateStatusClass ); 52 }, 53 54 updateStatusClass: function() { 55 if ( 'open' === this.controller.get( 'status' ) ) { 56 this.$el.addClass( 'is-open' ); 57 } else { 58 this.$el.removeClass( 'is-open' ); 59 } 60 } 61 }); 62 63 api.PostCollection.CustomizeSectionTitleView = wp.Backbone.View.extend({ 64 className: 'customize-section-title', 65 template: wp.template( 'customize-section-title' ), 66 67 events: { 68 'click .customize-section-back': 'closeDrawer' 69 }, 70 71 initialize: function( options ) { 72 this.control = options.control; 73 }, 74 75 render: function() { 76 var data = { 77 label: this.control.params.label, 78 l10n: this.control.l10n 79 }; 80 81 this.$el.html( this.template( data ) ); 82 83 return this; 84 }, 85 86 closeDrawer: function( e ) { 87 e.preventDefault(); 88 this.control.drawer.close(); 89 } 90 }); 91 92 api.PostCollection.PostModel = Backbone.Model.extend({ 93 defaults: { 94 title: '', 95 order: 0 96 } 97 }); 98 99 api.PostCollection.PostsCollection = Backbone.Collection.extend({ 100 model: api.PostCollection.PostModel, 101 102 comparator: function( post ) { 103 return parseInt( post.get( 'order' ), 10 ); 104 } 105 }); 106 107 api.PostCollection.ControlView = wp.Backbone.View.extend({ 108 initialize: function( options ) { 109 this.control = options.control; 110 this.setting = options.setting; 111 112 this.listenTo( this.collection, 'add remove reset sort', this.updateSetting ); 113 this.listenTo( this.control.drawer, 'change:status', this.maybeTriggerSearch ); 114 this.listenTo( this.control.drawer, 'change:status', this.updateStatusClass ); 115 }, 116 117 render: function() { 118 this.views.add([ 119 new api.PostCollection.ItemListView({ 120 collection: this.collection, 121 control: this.control, 122 parent: this 123 }), 124 new api.PostCollection.AddNewItemButtonView({ 125 control: this.control 126 }) 127 ]); 128 129 return this; 130 }, 131 132 maybeTriggerSearch: function() { 133 if ( 'open' === this.control.drawer.get( 'status' ) && this.control.results.length < 1 ) { 134 this.control.search(); 135 } 136 }, 137 138 updateSetting: function() { 139 var postIds = this.collection.sort({ silent: true }).pluck( 'id' ).join( ',' ); 140 this.setting.set( postIds ); 141 }, 142 143 updateStatusClass: function() { 144 if ( 'open' === this.control.drawer.get( 'status' ) ) { 145 this.$el.addClass( 'is-drawer-open' ); 146 } else { 147 this.$el.removeClass( 'is-drawer-open' ); 148 } 149 } 150 }); 151 152 api.PostCollection.AddNewItemButtonView = wp.Backbone.View.extend({ 153 className: 'add-new-item button button-secondary alignright', 154 tagName: 'button', 155 156 events: { 157 click: 'toggleDrawer' 158 }, 159 160 initialize: function( options ) { 161 this.control = options.control; 162 }, 163 164 render: function() { 165 this.$el.text( this.control.l10n.addPosts ); 166 return this; 167 }, 168 169 toggleDrawer: function( e ) { 170 e.preventDefault(); 171 this.control.drawer.toggle(); 172 } 173 }); 174 175 api.PostCollection.ItemListView = wp.Backbone.View.extend({ 176 className: 'wp-items-list', 177 tagName: 'ol', 178 179 initialize: function( options ) { 180 this.control = options.control; 181 182 this.listenTo( this.collection, 'add', this.addItem ); 183 this.listenTo( this.collection, 'add remove', this.updateOrder ); 184 this.listenTo( this.collection, 'reset', this.render ); 185 }, 186 187 render: function() { 188 this.$el.empty(); 189 this.collection.each( this.addItem, this ); 190 this.initializeSortable(); 191 return this; 192 }, 193 194 initializeSortable: function() { 195 this.$el.sortable({ 196 axis: 'y', 197 delay: 150, 198 forceHelperSize: true, 199 forcePlaceholderSize: true, 200 opacity: 0.6, 201 start: function( e, ui ) { 202 ui.placeholder.css( 'visibility', 'visible' ); 203 }, 204 update: _.bind(function() { 205 this.updateOrder(); 206 }, this ) 207 }); 208 }, 209 210 addItem: function( item ) { 211 var itemView = new api.PostCollection.ItemView({ 212 control: this.control, 213 model: item, 214 parent: this 215 }); 216 217 this.$el.append( itemView.render().el ); 218 }, 219 220 moveDown: function( model ) { 221 var index = this.collection.indexOf( model ), 222 $items = this.$el.children(); 223 224 if ( index < this.collection.length - 1 ) { 225 $items.eq( index ).insertAfter( $items.eq( index + 1 ) ); 226 this.updateOrder(); 227 wp.a11y.speak( this.control.l10n.movedDown ); 228 } 229 }, 230 231 moveUp: function( model ) { 232 var index = this.collection.indexOf( model ), 233 $items = this.$el.children(); 234 235 if ( index > 0 ) { 236 $items.eq( index ).insertBefore( $items.eq( index - 1 ) ); 237 this.updateOrder(); 238 wp.a11y.speak( this.control.l10n.movedUp ); 239 } 240 }, 241 242 updateOrder: function() { 243 _.each( this.$el.find( 'li' ), function( item, i ) { 244 var id = $( item ).data( 'post-id' ); 245 this.collection.get( id ).set( 'order', i ); 246 }, this ); 247 248 this.collection.sort(); 249 } 250 }); 251 252 api.PostCollection.ItemView = wp.Backbone.View.extend({ 253 tagName: 'li', 254 className: 'wp-item', 255 template: wp.template( 'wp-item' ), 256 257 events: { 258 'click .js-remove': 'destroy', 259 'click .move-item-up': 'moveUp', 260 'click .move-item-down': 'moveDown' 261 }, 262 263 initialize: function( options ) { 264 this.control = options.control; 265 this.parent = options.parent; 266 this.listenTo( this.model, 'destroy', this.remove ); 267 }, 268 269 render: function() { 270 var data = _.extend( this.model.toJSON(), { 271 l10n: this.control.l10n 272 }); 273 274 this.$el.html( this.template( data ) ); 275 this.$el.data( 'post-id', this.model.get( 'id' ) ); 276 277 return this; 278 }, 279 280 moveDown: function( e ) { 281 e.preventDefault(); 282 this.parent.moveDown( this.model ); 283 }, 284 285 moveUp: function( e ) { 286 e.preventDefault(); 287 this.parent.moveUp( this.model ); 288 }, 289 290 /** 291 * Destroy the view's model. 292 * 293 * Avoid syncing to the server by triggering an event instead of 294 * calling destroy() directly on the model. 295 */ 296 destroy: function() { 297 this.model.trigger( 'destroy', this.model ); 298 }, 299 300 remove: function() { 301 this.$el.remove(); 302 } 303 }); 304 305 api.PostCollection.DrawerNoticeView = wp.Backbone.View.extend({ 306 tagName: 'div', 307 className: 'customize-drawer-notice', 308 309 initialize: function( options ) { 310 this.control = options.control; 311 this.listenTo( this.control.state, 'change:notice', this.render ); 312 }, 313 314 render: function() { 315 var notice = this.control.state.get( 'notice' ); 316 this.$el.toggle( !! notice.length ).text( notice ); 317 return this; 318 } 319 }); 320 321 api.PostCollection.SearchGroupView = wp.Backbone.View.extend({ 322 tagName: 'div', 323 className: 'search-group', 324 template: wp.template( 'search-group' ), 325 326 events: { 327 'click .clear-results' : 'clearResults', 328 'input input': 'search' 329 }, 330 331 initialize: function( options ) { 332 this.control = options.control; 333 this.listenTo( this.collection, 'add remove reset', this.updateClearResultsVisibility ); 334 }, 335 336 render: function() { 337 this.$el.html( this.template({ l10n: this.control.l10n }) ); 338 this.$clearResults = this.$( '.clear-results' ); 339 this.$field = this.$( '.search-group-field' ); 340 this.$spinner = this.$el.append( '<span class="search-group-spinner spinner" />' ).find( '.spinner' ); 341 this.updateClearResultsVisibility(); 342 return this; 343 }, 344 345 clearResults: function() { 346 this.collection.reset(); 347 this.$field.val( '' ).trigger( 'input' ).focus(); 348 }, 349 350 search: function() { 351 var view = this; 352 353 this.$el.addClass( 'is-searching' ); 354 this.$spinner.addClass( 'is-active' ); 355 356 clearTimeout( this.timeout ); 357 this.timeout = setTimeout(function() { 358 view.control.search( view.$field.val() ) 359 .always(function() { 360 view.$el.removeClass( 'is-searching' ); 361 view.$spinner.removeClass( 'is-active' ); 362 }); 363 }, 300 ); 364 }, 365 366 updateClearResultsVisibility: function() { 367 this.$clearResults.toggleClass( 'is-visible', !! this.collection.length && '' !== this.$field.val() ); 368 } 369 }); 370 371 api.PostCollection.SearchResultsView = wp.Backbone.View.extend({ 372 tagName: 'div', 373 className: 'search-results', 374 375 initialize: function( options ) { 376 this.control = options.control; 377 this.listenTo( this.collection, 'reset', this.render ); 378 }, 379 380 render: function() { 381 this.$list = this.$el.html( '<ul />' ).find( 'ul' ); 382 383 if ( this.collection.length ) { 384 this.collection.each( this.addItem, this ); 385 } else { 386 this.$el.empty(); 387 } 388 389 return this; 390 }, 391 392 addItem: function( model ) { 393 this.views.add( 'ul', new api.PostCollection.SearchResultView({ 394 control: this.control, 395 model: model 396 })); 397 } 398 }); 399 400 api.PostCollection.SearchResultView = wp.Backbone.View.extend({ 401 tagName: 'li', 402 className: 'search-results-item', 403 template: wp.template( 'search-result' ), 404 405 events: { 406 'click': 'addSection' 407 }, 408 409 initialize: function( options ) { 410 this.control = options.control; 411 this.listenTo( this.control.posts, 'add remove reset', this.updateSelectedClass ); 412 }, 413 414 render: function() { 415 var data = _.extend( this.model.toJSON(), { 416 l10n: this.control.l10n 417 }); 418 419 this.$el.html( this.template( data ) ); 420 this.updateSelectedClass(); 421 422 return this; 423 }, 424 425 addSection: function() { 426 this.control.posts.add( this.model ); 427 }, 428 429 updateSelectedClass: function() { 430 this.$el.toggleClass( 'is-selected', !! this.control.posts.findWhere({ id: this.model.get( 'id' ) }) ); 431 } 432 }); 433 434 api.PostCollection.PostCollectionControl = api.Control.extend({ 435 ready: function() { 436 var controlView, drawerView, 437 control = this, 438 section = api.section( this.section() ); 439 440 this.drawer = new api.DrawerModel(); 441 api.drawerManager.add( this.drawer ); 442 443 this.posts = new api.PostCollection.PostsCollection( this.params.posts ); 444 this.results = new api.PostCollection.PostsCollection(); 445 delete this.params.posts; 446 447 this.l10n = this.params.l10n; 448 delete this.params.l10n; 449 450 this.state = new Backbone.Model({ 451 notice: '' 452 }); 453 454 controlView = new api.PostCollection.ControlView({ 455 el: this.container, 456 collection: this.posts, 457 control: this, 458 data: this.params, 459 setting: this.setting 460 }); 461 462 controlView.render(); 463 464 drawerView = new api.DrawerView({ 465 controller: this.drawer 466 }); 467 468 drawerView.views.set([ 469 new api.PostCollection.CustomizeSectionTitleView({ 470 control: this 471 }), 472 new api.PostCollection.SearchGroupView({ 473 collection: this.results, 474 control: this 475 }), 476 new api.PostCollection.DrawerNoticeView({ 477 control: this 478 }), 479 new api.PostCollection.SearchResultsView({ 480 collection: this.results, 481 control: this 482 }) 483 ]); 484 485 $( '.wp-full-overlay' ).append( drawerView.render().$el ); 486 487 section.expanded.bind(function( isOpen ) { 488 if ( ! isOpen ) { 489 control.drawer.close(); 490 } 491 }); 492 }, 493 494 search: function( query ) { 495 var control = this; 496 497 return wp.ajax.post( 'customize_find_posts', { 498 s: query, 499 post_types: this.params.postTypes, 500 not_in: this.params.exclude, 501 wp_customize: 'on', 502 _ajax_nonce: this.params.searchNonce 503 }).done(function( response ) { 504 control.results.reset( response ); 505 control.state.set( 'notice', '' ); 506 }).fail(function( response ) { 507 control.results.reset(); 508 control.state.set( 'notice', response ); 509 }); 510 } 511 }); 512 513 /** 514 * Extends wp.customize.controlConstructor with control constructor for 515 * post_collection. 516 */ 517 $.extend( api.controlConstructor, { 518 post_collection: api.PostCollection.PostCollectionControl 519 }); 520 521 /** 522 * Create a global drawer manager. 523 */ 524 api.drawerManager = new api.DrawerManager(); 525 526 /** 527 * Toggle an HTML class on the body when drawers are opened or closed. 528 */ 529 $( document ).ready(function() { 530 var $body = $( document.body ); 531 532 api.drawerManager.on( 'change:status', function() { 533 if ( api.drawerManager.findWhere({ status: 'open' }) ) { 534 $body.addClass( 'drawer-is-open' ); 535 } else { 536 $body.removeClass( 'drawer-is-open' ); 537 } 538 }); 539 }); 540 541 /** 542 * Toggle the front_page_sections control based on the 'show_on_front' 543 * setting value. 544 */ 545 api.bind( 'ready', function() { 546 api( 'show_on_front', function( setting ) { 547 api.control( 'front_page_sections', function( control ) { 548 var toggleVisibility = function( value ) { 549 control.container.toggle( 'page' === value ); 550 }; 551 552 toggleVisibility( setting() ); 553 setting.bind( toggleVisibility ); 554 }); 555 }); 556 }); 557 558 })( window.wp, jQuery ); -
src/wp-includes/canonical.php
655 655 exit; 656 656 } 657 657 } 658 659 /** 660 * Redirect front page section permalinks to the fragment on the front page. 661 * 662 * @since 4.7.0 663 */ 664 function wp_redirect_front_page_sections() { 665 $object_id = get_queried_object_id(); 666 667 if ( 668 ! current_theme_supports( 'front-page-sections' ) 669 || is_front_page() 670 || is_home() 671 || ! is_singular() 672 || ! is_front_page_section( $object_id ) 673 ) { 674 return; 675 } 676 677 wp_redirect( get_front_page_section_url( $object_id ) ); 678 exit; 679 } -
src/wp-includes/class-wp-customize-manager.php
227 227 require_once( ABSPATH . WPINC . '/customize/class-wp-customize-nav-menu-name-control.php' ); 228 228 require_once( ABSPATH . WPINC . '/customize/class-wp-customize-nav-menu-auto-add-control.php' ); 229 229 require_once( ABSPATH . WPINC . '/customize/class-wp-customize-new-menu-control.php' ); 230 require_once( ABSPATH . WPINC . '/customize/class-wp-customize-post-collection-control.php' ); 230 231 231 232 require_once( ABSPATH . WPINC . '/customize/class-wp-customize-nav-menus-panel.php' ); 232 233 … … 289 290 290 291 add_action( 'wp_ajax_customize_save', array( $this, 'save' ) ); 291 292 add_action( 'wp_ajax_customize_refresh_nonces', array( $this, 'refresh_nonces' ) ); 293 add_action( 'wp_ajax_customize_find_posts', array( $this, 'ajax_find_posts' ) ); 292 294 293 295 add_action( 'customize_register', array( $this, 'register_controls' ) ); 294 296 add_action( 'customize_register', array( $this, 'register_dynamic_settings' ), 11 ); // allow code to create settings first … … 1970 1972 $this->register_control_type( 'WP_Customize_Cropped_Image_Control' ); 1971 1973 $this->register_control_type( 'WP_Customize_Site_Icon_Control' ); 1972 1974 $this->register_control_type( 'WP_Customize_Theme_Control' ); 1975 $this->register_control_type( 'WP_Customize_Post_Collection_Control' ); 1973 1976 1974 1977 /* Themes */ 1975 1978 … … 2302 2305 'section' => 'static_front_page', 2303 2306 'type' => 'dropdown-pages', 2304 2307 ) ); 2308 2309 if ( current_theme_supports( 'front-page-sections' ) ) { 2310 $this->add_setting( 'front_page_sections', array( 2311 'sanitize_callback' => array( $this, 'sanitize_id_list' ), 2312 ) ); 2313 2314 $this->add_control( new WP_Customize_Post_Collection_Control( $this, 'front_page_sections', array( 2315 'label' => __( 'Front page sections' ), 2316 'description' => '', 2317 'section' => 'static_front_page', 2318 'settings' => 'front_page_sections', 2319 'post_types' => get_theme_support( 'front-page-sections', 'post_types' ), 2320 'exclude' => array( 2321 get_option( 'page_on_front' ), 2322 get_option( 'page_for_posts' ), 2323 ), 2324 'l10n' => array( 2325 'addPost' => __( 'Add Section' ), 2326 'addPosts' => __( 'Add Sections' ), 2327 'movedUp' => __( 'Section moved up' ), 2328 'movedDown' => __( 'Section moved down' ), 2329 'removePost' => __( 'Remove Section' ), 2330 'searchPosts' => __( 'Search Sections' ), 2331 'searchPostsPlaceholder' => __( 'Search sections…' ), 2332 ), 2333 ) ) ); 2334 } 2305 2335 } 2306 2336 2307 2337 /** … … 2381 2411 public function _render_custom_logo_partial() { 2382 2412 return get_custom_logo(); 2383 2413 } 2414 2415 /** 2416 * Ajax handler for finding posts. 2417 * 2418 * @since 4.7.0 2419 * 2420 * @see wp_ajax_find_posts() 2421 */ 2422 public function ajax_find_posts() { 2423 check_ajax_referer( 'find-posts' ); 2424 2425 $post_types = array(); 2426 2427 if ( ! empty( $_POST['post_types'] ) ) { 2428 $post_type_names = array_map( 'sanitize_text_field', wp_unslash( $_POST['post_types'] ) ); 2429 foreach ( $post_type_names as $post_type ) { 2430 $post_types[ $post_type ] = get_post_type_object( $post_type ); 2431 } 2432 } 2433 2434 if ( empty( $post_types ) ) { 2435 $post_types['post'] = get_post_type_object( 'post' ); 2436 } 2437 2438 $args = array( 2439 'post_type' => array_keys( $post_types ), 2440 'post_status' => 'publish', 2441 'post__not_in' => isset( $_POST['not_in'] ) ? wp_parse_id_list( $_POST['not_in'] ) : array(), 2442 'posts_per_page' => 50, 2443 ); 2444 2445 if ( ! empty( $_POST['s'] ) ) { 2446 $args['s'] = sanitize_text_field( wp_unslash( $_POST['s'] ) ); 2447 } 2448 2449 $posts = get_posts( $args ); 2450 2451 if ( ! $posts ) { 2452 wp_send_json_error( __( 'No results found.' ) ); 2453 } 2454 2455 foreach ( $posts as $post ) { 2456 $data[] = array( 2457 'id' => $post->ID, 2458 'title' => $post->post_title, 2459 'type' => get_post_type_object( $post->post_type )->labels->singular_name, 2460 ); 2461 } 2462 2463 wp_send_json_success( $data ); 2464 } 2465 2466 /** 2467 * Sanitization callback for lists of IDs. 2468 * 2469 * @since 4.7.0 2470 * 2471 * @param string $value Setting value. 2472 * @return string Comma-separated list of IDs. 2473 */ 2474 public function sanitize_id_list( $value ) { 2475 return implode( ',', array_unique( array_filter( wp_parse_id_list( $value ) ) ) ); 2476 } 2384 2477 } -
src/wp-includes/class-wp-query.php
974 974 $qv['page'] = $qv['paged']; 975 975 unset($qv['paged']); 976 976 } 977 // Add section pages, if they exist. 978 $front_page_sections = array_filter( wp_parse_id_list( get_theme_mod( 'front_page_sections' ) ) ); 979 if ( $front_page_sections ) { 980 array_unshift( $front_page_sections, $qv['page_id'] ); 981 unset( $qv['page_id'] ); 982 983 $qv['post__in'] = $front_page_sections; 984 $qv['orderby'] = 'post__in'; 985 } 977 986 } 978 987 } 979 988 … … 1005 1014 } 1006 1015 } 1007 1016 1008 if ( $qv['page_id']) {1017 if ( ! empty( $qv['page_id'] ) ) { 1009 1018 if ( 'page' == get_option('show_on_front') && $qv['page_id'] == get_option('page_for_posts') ) { 1010 1019 $this->is_page = false; 1011 1020 $this->is_home = true; -
src/wp-includes/customize/class-wp-customize-post-collection-control.php
1 <?php 2 /** 3 * Customize API: WP_Customize_Post_Collection_Control class 4 * 5 * @package WordPress 6 * @subpackage Customize 7 * @since 4.7.0 8 */ 9 10 /** 11 * Customize Post Collection Control class. 12 * 13 * @since 4.7.0 14 * 15 * @see WP_Customize_Control 16 */ 17 class WP_Customize_Post_Collection_Control extends WP_Customize_Control { 18 /** 19 * Control type. 20 * 21 * @since 4.7.0 22 * @var string 23 */ 24 public $type = 'post_collection'; 25 26 /** 27 * Post types that can be added as sections.. 28 * 29 * @since 4.7.0 30 * @var array 31 */ 32 public $post_types = array( 'page', 'post' ); 33 34 /** 35 * Array of post IDs to exclude from search results. 36 * 37 * @since 4.7.0 38 * @access public 39 * @var array 40 */ 41 public $exclude = array(); 42 43 /** 44 * Localization strings. 45 * 46 * @since 4.7.0 47 * @access public 48 * @var array 49 */ 50 public $l10n = array(); 51 52 /** 53 * Constructor. 54 * 55 * @since 4.7.0 56 * 57 * @param WP_Customize_Manager $manager Customizer bootstrap instance. 58 * @param string $id Control ID. 59 * @param array $args Optional. Arguments to override class property defaults. 60 */ 61 public function __construct( $manager, $id, $args = array() ) { 62 parent::__construct( $manager, $id, $args ); 63 64 $this->l10n = wp_parse_args( $this->l10n, array( 65 'addPost' => __( 'Add Post' ), 66 'addPosts' => __( 'Add Posts' ), 67 'clearResults' => __( 'Clear Results' ), 68 'moveUp' => __( 'Move up' ), 69 'moveDown' => __( 'Move down' ), 70 'movedUp' => __( 'Post moved up' ), 71 'movedDown' => __( 'Post moved down' ), 72 'removePost' => __( 'Remove Post' ), 73 'searchPosts' => __( 'Search Posts' ), 74 'searchPostsPlaceholder' => __( 'Search posts…' ), 75 ) ); 76 } 77 78 /** 79 * Enqueue control related scripts/styles. 80 * 81 * @since 4.7.0 82 */ 83 public function enqueue() { 84 wp_enqueue_style( 'customize-post-collection' ); 85 wp_enqueue_script( 'customize-post-collection' ); 86 87 add_action( 'customize_controls_print_footer_scripts', array( 'WP_Customize_Post_Collection_Control', 'print_templates' ) ); 88 } 89 90 /** 91 * Refresh the parameters passed to the JavaScript via JSON. 92 * 93 * @since 4.7.0 94 * @uses WP_Customize_Control::to_json() 95 */ 96 public function to_json() { 97 parent::to_json(); 98 99 $this->json['exclude'] = $this->exclude; 100 $this->json['l10n'] = $this->l10n; 101 $this->json['posts'] = $this->get_posts(); 102 $this->json['postTypes'] = $this->post_types; 103 $this->json['searchNonce'] = wp_create_nonce( 'find-posts' ); 104 } 105 106 /** 107 * Don't render any content for this control from PHP. 108 * 109 * @since 4.7.0 110 * 111 * @see WP_Customize_Post_Collection_Control::content_template() 112 */ 113 public function render_content() {} 114 115 /** 116 * An Underscore (JS) template for this control's content (but not its container). 117 * 118 * @see WP_Customize_Control::print_template() 119 * 120 * @since 4.7.0 121 */ 122 protected function content_template() { 123 ?> 124 <label> 125 <# if ( data.label ) { #> 126 <span class="customize-control-title">{{ data.label }}</span> 127 <# } #> 128 <# if ( data.description ) { #> 129 <span class="description customize-control-description">{{{ data.description }}}</span> 130 <# } #> 131 </label> 132 <?php 133 } 134 135 /** 136 * Print JavaScript templates in the Customizer footer. 137 * 138 * @since 4.7.0 139 */ 140 public static function print_templates() { 141 ?> 142 <script type="text/html" id="tmpl-wp-item"> 143 <div class="wp-item-header"> 144 <h4 class="wp-item-title"><span>{{ data.title }}</span></h4> 145 146 <button type="button" class="wp-item-delete button-link js-remove"> 147 <span class="screen-reader-text">{{ data.l10n.removePost }}</span> 148 </button> 149 150 <div class="wp-reorder-nav is-active"> 151 <button class="move-item-down" tabindex="0">{{ data.l10n.moveDown }}</button> 152 <button class="move-item-up" tabindex="0">{{ data.l10n.moveUp }}</button> 153 </div> 154 </div> 155 </script> 156 157 <script type="text/html" id="tmpl-customize-section-title"> 158 <button type="button" class="customize-section-back" tabindex="-1"> 159 <span class="screen-reader-text"><?php _e( 'Back' ); ?></span> 160 </button> 161 <h3> 162 <span class="customize-action"> 163 <?php 164 /* translators: ▸ is the unicode right-pointing triangle, and %s is the control label in the Customizer */ 165 printf( __( 'Customizing ▸ %s' ), '{{ data.label }}' ); 166 ?> 167 </span> 168 {{ data.l10n.addPosts }} 169 </h3> 170 </script> 171 172 <script type="text/html" id="tmpl-search-group"> 173 <label class="screen-reader-text" for="search-group-field">{{ data.l10n.searchPosts }}</label> 174 <input type="text" id="search-group-field" placeholder="{{{ data.l10n.searchPostsPlaceholder }}}" class="search-group-field"> 175 <div class="search-icon" aria-hidden="true"></div> 176 <button type="button" class="clear-results"><span class="screen-reader-text">{{ data.l10n.clearResults }}</span></button> 177 </script> 178 179 <script type="text/html" id="tmpl-search-result"> 180 <span class="search-results-item-type">{{ data.type }}</span> 181 <span class="search-results-item-title">{{ data.title }}</span> 182 183 <button type="button" class="search-results-item-add button-link"> 184 <span class="screen-reader-text">{{ data.l10n.addPost }}</span> 185 </button> 186 </script> 187 <?php 188 } 189 190 /** 191 * Retrieve posts. 192 * 193 * @since 4.7.0 194 * 195 * @return array 196 */ 197 protected function get_posts() { 198 $data = array(); 199 $value = $this->value(); 200 201 if ( ! empty( $value ) ) { 202 $posts = get_posts( array( 203 'post_type' => $this->post_types, 204 'post_status' => 'any', 205 'post__in' => array_map( 'absint', explode( ',', $value ) ), 206 'orderby' => 'post__in', 207 'posts_per_page' => 20, 208 ) ); 209 } 210 211 if ( ! empty( $posts ) ) { 212 $i = 0; 213 foreach ( $posts as $post ) { 214 $data[] = array( 215 'id' => $post->ID, 216 'title' => $post->post_title, 217 'order' => ++$i, 218 ); 219 } 220 } 221 222 return $data; 223 } 224 } -
src/wp-includes/default-filters.php
427 427 428 428 // Canonical 429 429 add_action( 'template_redirect', 'redirect_canonical' ); 430 add_action( 'template_redirect', 'wp_redirect_front_page_sections' ); 430 431 add_action( 'template_redirect', 'wp_redirect_admin_locations', 1000 ); 431 432 432 433 // Shortcodes -
src/wp-includes/link-template.php
312 312 function get_page_link( $post = false, $leavename = false, $sample = false ) { 313 313 $post = get_post( $post ); 314 314 315 if ( 'page' == get_option( 'show_on_front' ) && $post->ID == get_option( 'page_on_front' ) ) 315 if ( 'page' == get_option( 'show_on_front' ) && $post->ID == get_option( 'page_on_front' ) ) { 316 316 $link = home_url('/'); 317 else 317 } elseif ( is_front_page_section( $post->ID ) ) { 318 $link = get_front_page_section_url( $post->ID ); 319 } else { 318 320 $link = _get_page_link( $post, $leavename, $sample ); 321 } 319 322 320 323 /** 321 324 * Filters the permalink for a page. … … 4142 4145 */ 4143 4146 return apply_filters( 'parent_theme_file_path', $path, $file ); 4144 4147 } 4148 4149 /** 4150 * Retrieve the URL for a front page section. 4151 * 4152 * @since 4.7.0 4153 * 4154 * @param int|WP_Post $post Post ID or object. 4155 * @return string 4156 */ 4157 function get_front_page_section_url( $post ) { 4158 return home_url( '#post-' . get_post( $post )->ID ); 4159 } -
src/wp-includes/post-template.php
479 479 $classes[] = 'format-standard'; 480 480 } 481 481 482 // Front page sections. 483 if ( is_front_page() && is_front_page_section( $post->ID ) ) { 484 $classes[] = 'front-page-section'; 485 } 486 482 487 $post_password_required = post_password_required( $post->ID ); 483 488 484 489 // Post requires password. … … 1794 1799 echo $rows; 1795 1800 echo "</ul>"; 1796 1801 } 1802 1803 /** 1804 * Whether a post is a front page section. 1805 * 1806 * @since 4.7.0 1807 * 1808 * @param int $post_id Post ID. 1809 * @return bool 1810 */ 1811 function is_front_page_section( $post_id ) { 1812 $section_ids = array_filter( wp_parse_id_list( get_theme_mod( 'front_page_sections' ) ) ); 1813 return in_array( intval( $post_id ), $section_ids, true ); 1814 } -
src/wp-includes/script-loader.php
476 476 $scripts->add( 'customize-nav-menus', "/wp-admin/js/customize-nav-menus$suffix.js", array( 'jquery', 'wp-backbone', 'customize-controls', 'accordion', 'nav-menu' ), false, 1 ); 477 477 $scripts->add( 'customize-preview-nav-menus', "/wp-includes/js/customize-preview-nav-menus$suffix.js", array( 'jquery', 'wp-util', 'customize-preview', 'customize-selective-refresh' ), false, 1 ); 478 478 479 $scripts->add( 'customize-post-collection', "/wp-admin/js/customize-post-collection$suffix.js", array( 'jquery', 'jquery-ui-sortable', 'jquery-ui-droppable', 'wp-backbone', 'customize-controls' ), false, 1 ); 480 479 481 $scripts->add( 'accordion', "/wp-admin/js/accordion$suffix.js", array( 'jquery' ), false, 1 ); 480 482 481 483 $scripts->add( 'shortcode', "/wp-includes/js/shortcode$suffix.js", array( 'underscore' ), false, 1 ); -
src/wp-includes/theme.php
1730 1730 1731 1731 return false; 1732 1732 } 1733 1734 case 'front-page-sections' : 1735 if ( ! is_array( $args ) ) { 1736 $args = array( 0 => array() ); 1737 } 1738 1739 $args[0] = wp_parse_args( $args[0], array( 1740 'post_types' => array( 'page' ), 1741 ) ); 1742 1743 break; 1733 1744 } 1734 1745 1735 1746 $_wp_theme_features[ $feature ] = $args; … … 1822 1833 case 'custom-logo' : 1823 1834 case 'custom-header' : 1824 1835 case 'custom-background' : 1836 case 'front-page-sections' : 1825 1837 if ( isset( $_wp_theme_features[ $feature ][0][ $args[0] ] ) ) 1826 1838 return $_wp_theme_features[ $feature ][0][ $args[0] ]; 1827 1839 return false; … … 2082 2094 return; 2083 2095 } 2084 2096 2085 require_once ABSPATH . WPINC . '/class-wp-customize-manager.php'; 2097 require_once ABSPATH . WPINC . '/class-wp-customize-manager.php'; 2086 2098 $GLOBALS['wp_customize'] = new WP_Customize_Manager(); 2087 2099 } 2088 2100