Ticket #37974: 37974.diff
File 37974.diff, 37.4 KB (added by , 8 years ago) |
---|
-
src/wp-admin/css/customize-controls.css
1282 1282 color: #00a0d2; 1283 1283 } 1284 1284 1285 .reordering .add-new-item { 1286 opacity: 0.2; 1287 pointer-events: none; 1288 cursor: not-allowed; /* doesn't work in conjunction with pointer-events */ 1289 } 1290 1291 .wp-reorder-nav { 1292 display: none; 1293 background-color: #fff; 1294 position: absolute; 1295 top: 0; 1296 right: 0; 1297 } 1298 1299 .reordering .wp-reorder-nav { 1300 display: block; 1301 } 1302 1303 .wp-reorder-nav button, 1285 1304 .widget-reorder-nav span, 1286 1305 .menu-item-reorder-nav button { 1287 1306 position: relative; … … 1296 1315 outline: none; 1297 1316 } 1298 1317 1318 .wp-reorder-nav button, 1299 1319 .menu-item-reorder-nav button { 1300 1320 width: 30px; 1301 1321 height: 40px; … … 1305 1325 box-shadow: none; 1306 1326 } 1307 1327 1328 .wp-reorder-nav button:before, 1308 1329 .widget-reorder-nav span:before, 1309 1330 .menu-item-reorder-nav button:before { 1310 1331 display: inline-block; … … 1320 1341 -moz-osx-font-smoothing: grayscale; 1321 1342 } 1322 1343 1344 .wp-reorder-nav button:hover, 1345 .wp-reorder-nav button:focus, 1323 1346 .widget-reorder-nav span:hover, 1324 1347 .widget-reorder-nav span:focus, 1325 1348 .menu-item-reorder-nav button:hover, … … 1328 1351 background: #eee; 1329 1352 } 1330 1353 1354 .wp-reorder-nav button { 1355 width: 33px; 1356 height: 38px; 1357 } 1358 1359 .wp-reorder-nav button:before { 1360 font: normal 20px/38px dashicons; 1361 } 1362 1363 .move-item-up:before, 1331 1364 .move-widget-down:before, 1332 1365 .menus-move-down:before { 1333 1366 content: "\f347"; 1334 1367 } 1335 1368 1369 .move-item-down:before, 1336 1370 .move-widget-up:before, 1337 1371 .menus-move-up:before { 1338 1372 content: "\f343"; … … 1343 1377 .move-up-disabled .menus-move-up, 1344 1378 .move-down-disabled .menus-move-down, 1345 1379 .move-right-disabled .menus-move-right, 1346 .move-left-disabled .menus-move-left { 1380 .move-left-disabled .menus-move-left, 1381 .wp-item:first-child .move-item-up, 1382 .wp-item:last-child .move-item-down { 1347 1383 color: #d5d5d5; 1348 1384 background-color: #fff; 1349 1385 cursor: default; -
src/wp-admin/css/customize-post-collection.css
1 /* General 2 ------------------------------------------------------------------------------*/ 3 4 .customize-dashicon, 5 .drawer-toggle:before { 6 cursor: pointer; 7 display: inline-block; 8 font-family: dashicons; 9 font-size: 20px; 10 -webkit-font-smoothing: antialiased; 11 font-style: normal; 12 font-weight: normal; 13 line-height: 1; 14 position: relative; 15 text-align: center; 16 text-decoration: inherit; 17 vertical-align: top; 18 } 19 20 .drawer-toggle:before { 21 content: "\f132"; 22 position: relative; 23 left: -2px; 24 top: -1px; 25 transition: all 0.2s; 26 vertical-align: middle; 27 } 28 29 .customize-button-delete { 30 color: #a00; 31 } 32 33 .customize-button-delete:hover { 34 color: #f00; 35 } 36 37 .customize-button-delete.customize-dashicon:before { 38 content: "\f335"; 39 } 40 41 .customize-control.is-open .drawer-toggle:before { 42 transform: rotate( 45deg ); 43 } 44 45 46 /* Drawer 47 ------------------------------------------------------------------------------*/ 48 49 .customize-drawer { 50 background: #eee; 51 border-right: 1px solid #ddd; 52 display: block; 53 margin: 0; 54 overflow-x: hidden; 55 overflow-y: auto; 56 position: absolute; 57 top: 0; 58 right: 0; 59 bottom: 0; 60 left: -301px; 61 transition: left 0.18s; 62 visibility: hidden; 63 width: 300px; 64 z-index: 4; 65 } 66 67 .customize-drawer-notice { 68 padding: 15px; 69 } 70 71 .customize-drawer.is-open { 72 left: 0; 73 visibility: visible; 74 } 75 76 .drawer-is-open .wp-full-overlay-main { 77 left: 300px; 78 } 79 80 81 /* Sortable Item List 82 ------------------------------------------------------------------------------*/ 83 84 .wp-items-list { 85 list-style: none; 86 margin: 0 0 10px 0; 87 padding: 0; 88 position: relative; 89 } 90 91 .wp-item { 92 background: #fff; 93 margin: -1px 0 0 0; 94 padding: 0; 95 } 96 97 .wp-item-header { 98 border: 1px solid #dfdfdf; 99 background: #fff; 100 position: relative; 101 } 102 103 .wp-item-delete { 104 display: none; 105 height: 100%; 106 position: absolute; 107 top: 0; 108 right: 0; 109 bottom: 0; 110 text-align: center; 111 vertical-align: middle; 112 width: 40px; 113 } 114 115 .wp-item-title { 116 cursor: move; 117 margin: 0; 118 padding: 10px 20px; 119 position: relative; 120 } 121 122 .wp-item-toggle { 123 display: none; 124 height: 100%; 125 position: absolute; 126 top: 0; 127 right: 0; 128 bottom: 0; 129 text-align: right; 130 vertical-align: middle; 131 width: 40px; 132 } 133 134 .wp-item-toggle:before { 135 box-sizing: border-box; 136 color: #aaa; 137 content: "\f140"; /* Down arrow. */ 138 position: absolute; 139 top: 10px; 140 right: 10px; 141 bottom: 10px; 142 left: 0; 143 vertical-align: middle; 144 } 145 146 .wp-item-toggle:before:hover { 147 color: #777; 148 } 149 150 .wp-item-body { 151 border-color: #999; 152 border-style: solid; 153 border-width: 0 1px 1px 1px; 154 display: none; 155 margin: 0; 156 padding: 10px; 157 position: relative; 158 } 159 160 .wp-item-body input.regular-text { 161 max-width: 100%; 162 } 163 164 .wp-item-actions { 165 clear: both; 166 line-height: 1; 167 } 168 169 .wp-item-actions a { 170 cursor: pointer; 171 } 172 173 .wp-item.ui-sortable-helper { 174 background: #f9f9f9; 175 border: 1px solid #dfdfdf; 176 } 177 178 .wp-item.ui-sortable-placeholder { 179 background: transparent; 180 border: 1px dashed #a0a5aa; 181 margin-top: 0; 182 margin-bottom: 1px; 183 } 184 185 .wp-item:hover .wp-item-header, 186 .wp-item.is-open .wp-item-header { 187 border-color: #999; 188 z-index: 1; 189 } 190 191 .wp-item.is-open .wp-item-body { 192 z-index: 1; 193 } 194 195 .wp-item.is-open .wp-item-header { 196 border-bottom: 1px solid #e5e5e5; 197 } 198 199 .wp-item.is-open .wp-item-toggle:before { 200 content: "\f142"; /* Up arrow.*/ 201 } 202 203 .wp-item.is-open .wp-item-body { 204 display: block; 205 } 206 207 .customize-control.is-open .wp-item-delete { 208 display: block; 209 } 210 211 .customize-control.is-open .wp-item-toggle { 212 display: none; 213 } 214 215 216 /* Search 217 ------------------------------------------------------------------------------*/ 218 219 .search-group { 220 border-bottom: 1px solid #ddd; 221 margin: 0; 222 padding: 12px 15px; 223 position: relative; 224 } 225 226 .search-group-field { 227 padding: 6px 10px; 228 width: 100%; 229 } 230 231 .search-group-spinner { 232 margin: 0; 233 position: absolute; 234 top: 19px; 235 right: 20px; 236 } 237 238 .search-group-button-clear { 239 background: transparent; 240 border-width: 0; 241 cursor: pointer; 242 height: 20px; 243 padding: 0; 244 position: absolute; 245 top: 19px; 246 right: 20px; 247 text-align: center; 248 width: 20px; 249 } 250 251 252 /* Search Results 253 ------------------------------------------------------------------------------*/ 254 255 .search-results { 256 padding: 1px 15px 15px; 257 } 258 259 .search-results-item { 260 background: #fff; 261 border: 1px solid #ddd; 262 clear: both; 263 cursor: pointer; 264 line-height: 10px; 265 margin: -1px 0 0 0; 266 padding: 10px 15px; 267 position: relative; 268 } 269 270 .search-results-item-title { 271 display: block; 272 font-size: 13px; 273 font-weight: 600; 274 line-height: 20px; 275 padding-left: 20px; 276 } 277 278 .search-results-item-type { 279 color: #666; 280 float: right; 281 font-size: 12px; 282 line-height: 20px; 283 text-align: right; 284 } 285 286 .search-results-item-add { 287 color: #82878c; 288 height: 38px; 289 position: absolute; 290 top: 1px; 291 left: 1px; 292 width: 30px; 293 } 294 295 .search-results-item-add:before { 296 -webkit-border-radius: 50%; 297 border-radius: 50%; 298 content: "\f543"; 299 height: 20px; 300 position: relative; 301 top: 0; 302 left: 2px; 303 } 304 305 .search-results-item:hover { 306 border-color: #999; 307 color: #0073aa; 308 z-index: 1; 309 } 310 311 .search-results-item:hover .search-results-item-add:before { 312 color: #0073aa; 313 } 314 315 .is-selected .search-results-item-add:before { 316 content: "\f147"; 317 } -
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 isOpen: function() { 19 return 'open' === this.get( 'status' ); 20 }, 21 22 open: function() { 23 this.set( 'status', 'open' ); 24 }, 25 26 toggle: function() { 27 if ( this.isOpen() ) { 28 this.close(); 29 } else { 30 this.open(); 31 } 32 } 33 }); 34 35 api.DrawerManager = Backbone.Collection.extend({ 36 model: api.DrawerModel, 37 38 initialize: function() { 39 this.on( 'change:status', this.closeOtherDrawers ); 40 }, 41 42 closeOtherDrawers: function( model ) { 43 if ( 'open' === model.get( 'status' ) ) { 44 _.chain( this.models ).without( model ).invoke( 'close' ); 45 } 46 } 47 }); 48 49 api.DrawerView = wp.Backbone.View.extend({ 50 tagName: 'div', 51 className: 'customize-drawer', 52 53 initialize: function( options ) { 54 this.controller = options.controller; 55 this.listenTo( this.controller, 'change:status', this.updateStatusClass ); 56 }, 57 58 updateStatusClass: function() { 59 if ( 'open' === this.controller.get( 'status' ) ) { 60 this.$el.addClass( 'is-open' ); 61 } else { 62 this.$el.removeClass( 'is-open' ); 63 } 64 } 65 }); 66 67 api.PostCollection.PostModel = Backbone.Model.extend({ 68 defaults: { 69 title: '', 70 order: 0 71 } 72 }); 73 74 api.PostCollection.PostsCollection = Backbone.Collection.extend({ 75 model: api.PostCollection.PostModel, 76 77 comparator: function( post ) { 78 return parseInt( post.get( 'order' ), 10 ); 79 } 80 }); 81 82 api.PostCollection.ControlView = wp.Backbone.View.extend({ 83 initialize: function( options ) { 84 this.control = options.control; 85 this.setting = options.setting; 86 87 this.listenTo( this.collection, 'add remove reset sort', this.updateSetting ); 88 this.listenTo( this.control.drawer, 'change:status', this.maybeTriggerSearch ); 89 this.listenTo( this.control.drawer, 'change:status', this.updateStatusClass ); 90 }, 91 92 render: function() { 93 this.views.add([ 94 new api.PostCollection.ItemListView({ 95 collection: this.collection, 96 control: this.control, 97 parent: this 98 }), 99 100 new api.PostCollection.ControlActionsView({ 101 collection: this.collection, 102 control: this.control, 103 parent: this 104 }) 105 ]); 106 107 return this; 108 }, 109 110 maybeTriggerSearch: function() { 111 if ( 'open' === this.control.drawer.get( 'status' ) && this.control.results.length < 1 ) { 112 this.control.search(); 113 } 114 }, 115 116 updateSetting: function() { 117 var postIds = this.collection.sort({ silent: true }).pluck( 'id' ).join( ',' ); 118 this.setting.set( postIds ); 119 }, 120 121 updateStatusClass: function() { 122 if ( 'open' === this.control.drawer.get( 'status' ) ) { 123 this.$el.addClass( 'is-open' ); 124 } else { 125 this.$el.removeClass( 'is-open' ); 126 } 127 } 128 }); 129 130 api.PostCollection.ControlActionsView = wp.Backbone.View.extend({ 131 className: 'actions', 132 tagName: 'div', 133 134 initialize: function( options ) { 135 this.control = options.control; 136 this.parent = options.parent; 137 }, 138 139 render: function() { 140 this.views.add([ 141 new api.PostCollection.AddNewItemButtonView({ 142 control: this.control 143 }), 144 new api.PostCollection.ReorderToggleButtonView({ 145 control: this.control 146 }) 147 ]); 148 149 return this; 150 } 151 }); 152 153 api.PostCollection.AddNewItemButtonView = wp.Backbone.View.extend({ 154 className: 'drawer-toggle add-new-item button button-secondary alignright', 155 tagName: 'button', 156 157 events: { 158 click: 'toggleDrawer' 159 }, 160 161 initialize: function( options ) { 162 this.control = options.control; 163 }, 164 165 render: function() { 166 this.$el.text( this.control.l10n.addPosts ); 167 return this; 168 }, 169 170 toggleDrawer: function( e ) { 171 e.preventDefault(); 172 this.control.drawer.toggle(); 173 } 174 }); 175 176 api.PostCollection.ReorderToggleButtonView = wp.Backbone.View.extend({ 177 className: 'reorder-toggle button-link', 178 tagName: 'button', 179 180 events: { 181 click: 'toggleReordering' 182 }, 183 184 initialize: function( options ) { 185 this.control = options.control; 186 this.listenTo( this.control.posts, 'add remove reset', this.updateVisibility ); 187 }, 188 189 render: function() { 190 this.$el.empty(); 191 192 this.$el.append( $( '<span />', { 193 'class': 'reorder', 194 text: this.control.l10n.reorder 195 }) ); 196 197 this.$el.append( $( '<span />', { 198 'class': 'reorder-done', 199 text: this.control.l10n.reorderDone 200 }) ); 201 202 this.updateVisibility(); 203 return this; 204 }, 205 206 toggleReordering: function( e ) { 207 e.preventDefault(); 208 this.control.drawer.close(); 209 this.control.container.toggleClass( 'reordering' ); 210 }, 211 212 updateVisibility: function() { 213 this.$el.toggle( !! this.control.posts.length ); 214 } 215 }); 216 217 api.PostCollection.ItemListView = wp.Backbone.View.extend({ 218 className: 'wp-items-list', 219 tagName: 'ol', 220 221 initialize: function( options ) { 222 this.control = options.control; 223 224 this.listenTo( this.collection, 'add', this.addItem ); 225 this.listenTo( this.collection, 'add remove', this.updateOrder ); 226 this.listenTo( this.collection, 'reset', this.render ); 227 }, 228 229 render: function() { 230 this.$el.empty(); 231 this.collection.each( this.addItem, this ); 232 this.initializeSortable(); 233 return this; 234 }, 235 236 initializeSortable: function() { 237 this.$el.sortable({ 238 axis: 'y', 239 delay: 150, 240 forceHelperSize: true, 241 forcePlaceholderSize: true, 242 opacity: 0.6, 243 start: function( e, ui ) { 244 ui.placeholder.css( 'visibility', 'visible' ); 245 }, 246 update: _.bind(function() { 247 this.updateOrder(); 248 }, this ) 249 }); 250 }, 251 252 addItem: function( item ) { 253 var itemView = new api.PostCollection.ItemView({ 254 control: this.control, 255 model: item, 256 parent: this 257 }); 258 259 this.$el.append( itemView.render().el ); 260 }, 261 262 moveDown: function( model ) { 263 var index = this.collection.indexOf( model ), 264 $items = this.$el.children(); 265 266 if ( index < this.collection.length - 1 ) { 267 $items.eq( index ).insertAfter( $items.eq( index + 1 ) ); 268 this.updateOrder(); 269 wp.a11y.speak( this.control.l10n.movedDown ); 270 } 271 }, 272 273 moveUp: function( model ) { 274 var index = this.collection.indexOf( model ), 275 $items = this.$el.children(); 276 277 if ( index > 0 ) { 278 $items.eq( index ).insertBefore( $items.eq( index - 1 ) ); 279 this.updateOrder(); 280 wp.a11y.speak( this.control.l10n.movedUp ); 281 } 282 }, 283 284 updateOrder: function() { 285 _.each( this.$el.find( 'li' ), function( item, i ) { 286 var id = $( item ).data( 'post-id' ); 287 this.collection.get( id ).set( 'order', i ); 288 }, this ); 289 290 this.collection.sort(); 291 } 292 }); 293 294 api.PostCollection.ItemView = wp.Backbone.View.extend({ 295 tagName: 'li', 296 className: 'wp-item', 297 template: wp.template( 'wp-item' ), 298 299 events: { 300 'click .js-toggle': 'toggleOpenStatus', 301 'click .js-close': 'minimize', 302 'click .js-remove': 'destroy', 303 'click .move-item-up': 'moveUp', 304 'click .move-item-down': 'moveDown' 305 }, 306 307 initialize: function( options ) { 308 this.control = options.control; 309 this.parent = options.parent; 310 this.listenTo( this.model, 'destroy', this.remove ); 311 }, 312 313 render: function() { 314 var data = _.extend( this.model.toJSON(), { 315 l10n: this.control.l10n 316 }); 317 318 this.$el.html( this.template( data ) ); 319 this.$el.data( 'post-id', this.model.get( 'id' ) ); 320 321 return this; 322 }, 323 324 minimize: function( e ) { 325 e.preventDefault(); 326 this.$el.removeClass( 'is-open' ); 327 }, 328 329 moveDown: function( e ) { 330 e.preventDefault(); 331 this.parent.moveDown( this.model ); 332 }, 333 334 moveUp: function( e ) { 335 e.preventDefault(); 336 this.parent.moveUp( this.model ); 337 }, 338 339 toggleOpenStatus: function( e ) { 340 e.preventDefault(); 341 this.$el.toggleClass( 'is-open' ); 342 }, 343 344 /** 345 * Destroy the view's model. 346 * 347 * Avoid syncing to the server by triggering an event instead of 348 * calling destroy() directly on the model. 349 */ 350 destroy: function() { 351 this.model.trigger( 'destroy', this.model ); 352 }, 353 354 remove: function() { 355 this.$el.remove(); 356 } 357 }); 358 359 api.PostCollection.DrawerNoticeView = wp.Backbone.View.extend({ 360 tagName: 'div', 361 className: 'customize-drawer-notice', 362 363 initialize: function( options ) { 364 this.control = options.control; 365 this.listenTo( this.control.state, 'change:notice', this.render ); 366 }, 367 368 render: function() { 369 var notice = this.control.state.get( 'notice' ); 370 this.$el.toggle( !! notice.length ).text( notice ); 371 return this; 372 } 373 }); 374 375 api.PostCollection.SearchGroupView = wp.Backbone.View.extend({ 376 tagName: 'div', 377 className: 'search-group', 378 template: wp.template( 'search-group' ), 379 380 events: { 381 'input input': 'search' 382 }, 383 384 initialize: function( options ) { 385 this.control = options.control; 386 this.parent = options.parent; 387 }, 388 389 render: function() { 390 this.$el.html( this.template({ l10n: this.control.l10n }) ); 391 this.$field = this.$el.find( '.search-group-field' ); 392 this.$spinner = this.$el.append( '<span class="search-group-spinner spinner" />' ).find( '.spinner' ); 393 394 this.views.add([ 395 new api.PostCollection.ClearResultsButtonView({ 396 collection: this.collection, 397 control: this.control, 398 parent: this 399 }) 400 ]); 401 402 return this; 403 }, 404 405 search: function() { 406 var view = this; 407 408 this.$spinner.addClass( 'is-active' ); 409 410 clearTimeout( this.timeout ); 411 this.timeout = setTimeout(function() { 412 view.control.search( view.$field.val() ) 413 .always(function() { 414 view.$spinner.removeClass( 'is-active' ); 415 }); 416 }, 300 ); 417 } 418 }); 419 420 api.PostCollection.ClearResultsButtonView = wp.Backbone.View.extend({ 421 tagName: 'button', 422 className: 'search-group-button-clear customize-button-delete customize-dashicon', 423 424 events: { 425 'click': 'clearResults' 426 }, 427 428 initialize: function( options ) { 429 this.control = options.control; 430 this.parent = options.parent; 431 this.listenTo( this.collection, 'add remove reset', this.toggleVisibility ); 432 }, 433 434 render: function() { 435 this.$el.html( $( '<span />', { 436 'class': 'screen-reader-text', 437 text: this.control.l10n.clearResults 438 }) ); 439 this.toggleVisibility(); 440 return this; 441 }, 442 443 clearResults: function() { 444 this.collection.reset(); 445 this.parent.$field.val( '' ); 446 }, 447 448 toggleVisibility: function() { 449 this.$el.toggle( !! this.collection.length ); 450 } 451 }); 452 453 api.PostCollection.SearchResultsView = wp.Backbone.View.extend({ 454 tagName: 'div', 455 className: 'search-results', 456 457 initialize: function( options ) { 458 this.control = options.control; 459 this.listenTo( this.collection, 'reset', this.render ); 460 }, 461 462 render: function() { 463 this.$list = this.$el.html( '<ul />' ).find( 'ul' ); 464 465 if ( this.collection.length ) { 466 this.collection.each( this.addItem, this ); 467 } else { 468 this.$el.empty(); 469 } 470 471 return this; 472 }, 473 474 addItem: function( model ) { 475 this.views.add( 'ul', new api.PostCollection.SearchResultView({ 476 control: this.control, 477 model: model 478 })); 479 } 480 }); 481 482 api.PostCollection.SearchResultView = wp.Backbone.View.extend({ 483 tagName: 'li', 484 className: 'search-results-item', 485 template: wp.template( 'search-result' ), 486 487 events: { 488 'click': 'addSection' 489 }, 490 491 initialize: function( options ) { 492 this.control = options.control; 493 this.listenTo( this.control.posts, 'add remove reset', this.updateSelectedClass ); 494 }, 495 496 render: function() { 497 var data = _.extend( this.model.toJSON(), { 498 l10n: this.control.l10n 499 }); 500 501 this.$el.html( this.template( data ) ); 502 this.updateSelectedClass(); 503 504 return this; 505 }, 506 507 addSection: function() { 508 this.control.posts.add( this.model ); 509 }, 510 511 updateSelectedClass: function() { 512 this.$el.toggleClass( 'is-selected', this.control.posts.contains( this.model ) ); 513 } 514 }); 515 516 api.PostCollection.PostCollectionControl = api.Control.extend({ 517 ready: function() { 518 var drawerView, 519 control = this, 520 section = api.section( this.section() ); 521 522 this.drawer = new api.DrawerModel(); 523 api.drawerManager.add( this.drawer ); 524 525 this.posts = new api.PostCollection.PostsCollection( this.params.posts ); 526 this.results = new api.PostCollection.PostsCollection(); 527 delete this.params.posts; 528 529 this.l10n = this.params.l10n; 530 delete this.params.l10n; 531 532 this.state = new Backbone.Model({ 533 notice: '' 534 }); 535 536 this.view = new api.PostCollection.ControlView({ 537 el: this.container, 538 collection: this.posts, 539 control: this, 540 data: this.params, 541 setting: this.setting 542 }); 543 544 this.view.render(); 545 546 drawerView = new api.DrawerView({ 547 controller: this.drawer 548 }); 549 550 drawerView.views.set([ 551 new api.PostCollection.SearchGroupView({ 552 collection: this.results, 553 control: this 554 }), 555 new api.PostCollection.DrawerNoticeView({ 556 control: this 557 }), 558 new api.PostCollection.SearchResultsView({ 559 collection: this.results, 560 control: this 561 }) 562 ]); 563 564 $( '.wp-full-overlay' ).append( drawerView.render().$el ); 565 566 section.expanded.bind(function( isOpen ) { 567 if ( ! isOpen ) { 568 control.drawer.close(); 569 } 570 }); 571 }, 572 573 search: function( query ) { 574 var control = this; 575 576 return wp.ajax.post( 'customize_find_posts', { 577 s: query, 578 post_types: this.params.postTypes, 579 //not_in: this.posts.pluck( 'id' ), 580 wp_customize: 'on', 581 _ajax_nonce: this.params.searchNonce 582 }).done(function( response ) { 583 control.results.reset( response ); 584 control.state.set( 'notice', '' ); 585 }).fail(function( response ) { 586 control.results.reset(); 587 control.state.set( 'notice', response ); 588 }); 589 } 590 }); 591 592 /** 593 * Extends wp.customize.controlConstructor with control constructor for 594 * post_collection. 595 */ 596 $.extend( api.controlConstructor, { 597 post_collection: api.PostCollection.PostCollectionControl 598 }); 599 600 /** 601 * Create a global drawer manager. 602 */ 603 api.drawerManager = new api.DrawerManager(); 604 605 /** 606 * Toggle an HTML class on the body when drawers are opened or closed. 607 */ 608 $( document ).ready(function() { 609 var $body = $( document.body ); 610 611 api.drawerManager.on( 'change:status', function() { 612 if ( api.drawerManager.findWhere({ status: 'open' }) ) { 613 $body.addClass( 'drawer-is-open' ); 614 } else { 615 $body.removeClass( 'drawer-is-open' ); 616 } 617 }); 618 }); 619 620 /** 621 * Toggle the front_page_sections control based on the 'show_on_front' 622 * setting value. 623 */ 624 api.bind( 'ready', function() { 625 api( 'show_on_front', function( setting ) { 626 api.control( 'front_page_sections', function( control ) { 627 var toggleVisibility = function( value ) { 628 control.container.toggle( 'page' === value ); 629 }; 630 631 toggleVisibility( setting() ); 632 setting.bind( toggleVisibility ); 633 }); 634 }); 635 }); 636 637 })( window.wp, jQuery ); -
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 … … 1963 1965 $this->register_control_type( 'WP_Customize_Cropped_Image_Control' ); 1964 1966 $this->register_control_type( 'WP_Customize_Site_Icon_Control' ); 1965 1967 $this->register_control_type( 'WP_Customize_Theme_Control' ); 1968 $this->register_control_type( 'WP_Customize_Post_Collection_Control' ); 1966 1969 1967 1970 /* Themes */ 1968 1971 … … 2295 2298 'section' => 'static_front_page', 2296 2299 'type' => 'dropdown-pages', 2297 2300 ) ); 2301 2302 if ( current_theme_supports( 'front-page-sections' ) ) { 2303 $this->add_setting( 'front_page_sections', array( 2304 'sanitize_callback' => array( $this, 'sanitize_id_list' ), 2305 ) ); 2306 2307 $this->add_control( new WP_Customize_Post_Collection_Control( $this, 'front_page_sections', array( 2308 'label' => __( 'Front page sections' ), 2309 'description' => '', 2310 'section' => 'static_front_page', 2311 'settings' => 'front_page_sections', 2312 'post_types' => get_theme_support( 'front-page-sections', 'post_types' ), 2313 'l10n' => array( 2314 'addPost' => __( 'Add Section' ), 2315 'addPosts' => __( 'Add Sections' ), 2316 'movedUp' => __( 'Section moved up' ), 2317 'movedDown' => __( 'Section moved down' ), 2318 'removePost' => __( 'Remove Section' ), 2319 'reorder' => _x( 'Reorder', 'Reorder sections in Customizer' ), 2320 'reorderDone' => _x( 'Done', 'Cancel reordering sections in Customizer' ), 2321 'searchPosts' => __( 'Search Sections' ), 2322 'searchPostsPlaceholder' => __( 'Search sections…' ), 2323 'togglePost' => __( 'Toggle Section' ), 2324 ), 2325 ) ) ); 2326 } 2298 2327 } 2299 2328 2300 2329 /** … … 2374 2403 public function _render_custom_logo_partial() { 2375 2404 return get_custom_logo(); 2376 2405 } 2406 2407 /** 2408 * Ajax handler for finding posts. 2409 * 2410 * @since 4.7.0 2411 * 2412 * @see wp_ajax_find_posts() 2413 */ 2414 public function ajax_find_posts() { 2415 check_ajax_referer( 'find-posts' ); 2416 2417 $post_types = array(); 2418 2419 if ( ! empty( $_POST['post_types'] ) ) { 2420 $post_type_names = array_map( 'sanitize_text_field', wp_unslash( $_POST['post_types'] ) ); 2421 foreach ( $post_type_names as $post_type ) { 2422 $post_types[ $post_type ] = get_post_type_object( $post_type ); 2423 } 2424 } 2425 2426 if ( empty( $post_types ) ) { 2427 $post_types['post'] = get_post_type_object( 'post' ); 2428 } 2429 2430 $args = array( 2431 'post_type' => array_keys( $post_types ), 2432 'post_status' => 'publish', 2433 'post__not_in' => isset( $_POST['not_in'] ) ? wp_parse_id_list( $_POST['not_in'] ) : array(), 2434 'posts_per_page' => 50, 2435 ); 2436 2437 if ( ! empty( $_POST['s'] ) ) { 2438 $args['s'] = sanitize_text_field( wp_unslash( $_POST['s'] ) ); 2439 } 2440 2441 $posts = get_posts( $args ); 2442 2443 if ( ! $posts ) { 2444 wp_send_json_error( __( 'No results found.' ) ); 2445 } 2446 2447 foreach ( $posts as $post ) { 2448 $data[] = array( 2449 'id' => $post->ID, 2450 'title' => $post->post_title, 2451 'type' => get_post_type_object( $post->post_type )->labels->singular_name, 2452 ); 2453 } 2454 2455 wp_send_json_success( $data ); 2456 } 2457 2458 /** 2459 * Sanitization callback for lists of IDs. 2460 * 2461 * @since 4.7.0 2462 * 2463 * @param string $value Setting value. 2464 * @return string Comma-separated list of IDs. 2465 */ 2466 public function sanitize_id_list( $value ) { 2467 return implode( ',', array_unique( array_filter( wp_parse_id_list( $value ) ) ) ); 2468 } 2377 2469 } -
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 * Localization strings. 36 * 37 * @since 4.7.0 38 * @access public 39 * @var array 40 */ 41 public $l10n = array(); 42 43 /** 44 * Constructor. 45 * 46 * @since 4.7.0 47 * 48 * @param WP_Customize_Manager $manager Customizer bootstrap instance. 49 * @param string $id Control ID. 50 * @param array $args Optional. Arguments to override class property defaults. 51 */ 52 public function __construct( $manager, $id, $args = array() ) { 53 parent::__construct( $manager, $id, $args ); 54 55 $this->l10n = wp_parse_args( $this->l10n, array( 56 'addPost' => __( 'Add Post' ), 57 'addPosts' => __( 'Add Posts' ), 58 'clearResults' => __( 'Clear Results' ), 59 'moveUp' => __( 'Move up' ), 60 'moveDown' => __( 'Move down' ), 61 'movedUp' => __( 'Post moved up' ), 62 'movedDown' => __( 'Post moved down' ), 63 'remove' => __( 'Remove' ), 64 'removePost' => __( 'Remove Post' ), 65 'reorder' => _x( 'Reorder', 'Reorder posts in Customizer' ), 66 'reorderDone' => _x( 'Done', 'Cancel reordering posts in Customizer' ), 67 'searchPosts' => __( 'Search Posts' ), 68 'searchPostsPlaceholder' => __( 'Search posts…' ), 69 'togglePost' => __( 'Toggle Post' ), 70 ) ); 71 } 72 73 /** 74 * Enqueue control related scripts/styles. 75 * 76 * @since 4.7.0 77 */ 78 public function enqueue() { 79 wp_enqueue_style( 'customize-post-collection' ); 80 wp_enqueue_script( 'customize-post-collection' ); 81 82 add_action( 'customize_controls_print_footer_scripts', array( 'WP_Customize_Post_Collection_Control', 'print_templates' ) ); 83 } 84 85 /** 86 * Refresh the parameters passed to the JavaScript via JSON. 87 * 88 * @since 4.7.0 89 * @uses WP_Customize_Control::to_json() 90 */ 91 public function to_json() { 92 parent::to_json(); 93 94 $this->json['l10n'] = $this->l10n; 95 $this->json['posts'] = $this->get_posts(); 96 $this->json['postTypes'] = $this->post_types; 97 $this->json['searchNonce'] = wp_create_nonce( 'find-posts' ); 98 } 99 100 /** 101 * Don't render any content for this control from PHP. 102 * 103 * @since 4.7.0 104 * 105 * @see WP_Customize_Post_Collection_Control::content_template() 106 */ 107 public function render_content() {} 108 109 /** 110 * An Underscore (JS) template for this control's content (but not its container). 111 * 112 * @see WP_Customize_Control::print_template() 113 * 114 * @since 4.7.0 115 */ 116 protected function content_template() { 117 ?> 118 <label> 119 <# if ( data.label ) { #> 120 <span class="customize-control-title">{{ data.label }}</span> 121 <# } #> 122 <# if ( data.description ) { #> 123 <span class="description customize-control-description">{{{ data.description }}}</span> 124 <# } #> 125 </label> 126 <?php 127 } 128 129 /** 130 * Print JavaScript templates in the Customizer footer. 131 * 132 * @since 4.7.0 133 */ 134 public static function print_templates() { 135 ?> 136 <script type="text/html" id="tmpl-wp-item"> 137 <div class="wp-item-header"> 138 <h4 class="wp-item-title"> 139 <span class="text">{{{ data.title }}}</span> 140 </h4> 141 142 <button type="button" class="wp-item-toggle customize-dashicon button-link js-toggle"> 143 <span class="screen-reader-text">{{ data.l10n.togglePost }}</span> 144 </button> 145 146 <button type="button" class="wp-item-delete customize-button-delete customize-dashicon button-link js-remove"> 147 <span class="screen-reader-text">{{ data.l10n.removePost }}</span> 148 </button> 149 150 <div class="wp-reorder-nav"> 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 156 <div class="wp-item-body"> 157 <div class="wp-item-actions"> 158 <a class="wp-item-delete customize-button-delete js-remove">{{ data.l10n.remove }}</a> 159 </div> 160 </div> 161 </script> 162 163 <script type="text/html" id="tmpl-search-group"> 164 <label class="screen-reader-text" for="search-group-field">{{ data.l10n.searchPosts }}</label> 165 <input type="text" id="search-group-field" placeholder="{{{ data.l10n.searchPostsPlaceholder }}}" class="search-group-field"> 166 </script> 167 168 <script type="text/html" id="tmpl-search-result"> 169 <span class="search-results-item-type">{{ data.type }}</span> 170 <span class="search-results-item-title">{{ data.title }}</span> 171 172 <button type="button" class="search-results-item-add customize-dashicon button-link"> 173 <span class="screen-reader-text">{{ data.l10n.addPost }}</span> 174 </button> 175 </script> 176 <?php 177 } 178 179 /** 180 * Retrieve posts. 181 * 182 * @since 4.7.0 183 * 184 * @return array 185 */ 186 protected function get_posts() { 187 $data = array(); 188 $value = $this->value(); 189 190 if ( ! empty( $value ) ) { 191 $posts = get_posts( array( 192 'post_type' => $this->post_types, 193 'post_status' => 'any', 194 'post__in' => array_map( 'absint', explode( ',', $value ) ), 195 'orderby' => 'post__in', 196 'posts_per_page' => 20, 197 ) ); 198 } 199 200 if ( ! empty( $posts ) ) { 201 $i = 0; 202 foreach ( $posts as $post ) { 203 $data[] = array( 204 'id' => $post->ID, 205 'title' => $post->post_title, 206 'order' => ++$i, 207 ); 208 } 209 } 210 211 return $data; 212 } 213 } -
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 ); … … 819 821 820 822 $styles->add( 'wp-admin', false, array( 'dashicons', 'common', 'forms', 'admin-menu', 'dashboard', 'list-tables', 'edit', 'revisions', 'media', 'themes', 'about', 'nav-menus', 'widgets', 'site-icon', 'l10n' ) ); 821 823 822 $styles->add( 'login', "/wp-admin/css/login$suffix.css", array( 'dashicons', 'buttons', 'forms', 'l10n' ) ); 823 $styles->add( 'install', "/wp-admin/css/install$suffix.css", array( 'buttons' ) ); 824 $styles->add( 'wp-color-picker', "/wp-admin/css/color-picker$suffix.css" ); 825 $styles->add( 'customize-controls', "/wp-admin/css/customize-controls$suffix.css", array( 'wp-admin', 'colors', 'ie', 'imgareaselect' ) ); 826 $styles->add( 'customize-widgets', "/wp-admin/css/customize-widgets$suffix.css", array( 'wp-admin', 'colors' ) ); 827 $styles->add( 'customize-nav-menus', "/wp-admin/css/customize-nav-menus$suffix.css", array( 'wp-admin', 'colors' ) ); 828 $styles->add( 'press-this', "/wp-admin/css/press-this$suffix.css", array( 'buttons' ) ); 824 $styles->add( 'login', "/wp-admin/css/login$suffix.css", array( 'dashicons', 'buttons', 'forms', 'l10n' ) ); 825 $styles->add( 'install', "/wp-admin/css/install$suffix.css", array( 'buttons' ) ); 826 $styles->add( 'wp-color-picker', "/wp-admin/css/color-picker$suffix.css" ); 827 $styles->add( 'customize-controls', "/wp-admin/css/customize-controls$suffix.css", array( 'wp-admin', 'colors', 'ie', 'imgareaselect' ) ); 828 $styles->add( 'customize-widgets', "/wp-admin/css/customize-widgets$suffix.css", array( 'wp-admin', 'colors' ) ); 829 $styles->add( 'customize-nav-menus', "/wp-admin/css/customize-nav-menus$suffix.css", array( 'wp-admin', 'colors' ) ); 830 $styles->add( 'customize-post-collection', "/wp-admin/css/customize-post-collection$suffix.css", array( 'wp-admin', 'colors', 'customize-controls' ) ); 831 $styles->add( 'press-this', "/wp-admin/css/press-this$suffix.css", array( 'buttons' ) ); 829 832 830 833 $styles->add( 'ie', "/wp-admin/css/ie$suffix.css" ); 831 834 $styles->add_data( 'ie', 'conditional', 'lte IE 7' ); -
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