Ticket #50105: 50105.5.diff
File 50105.5.diff, 19.8 KB (added by , 4 years ago) |
---|
-
src/js/media/models/attachments.js
diff --git a/src/js/media/models/attachments.js b/src/js/media/models/attachments.js index b0c58d93a5..51d73090b2 100644
a b var Attachments = Backbone.Collection.extend(/** @lends wp.media.model.Attachmen 347 347 hasMore: function() { 348 348 return this.mirroring ? this.mirroring.hasMore() : false; 349 349 }, 350 /** 351 * Holds the total number of attachments. 352 * 353 * @since 5.5.0 354 */ 355 totalAttachments: 0, 356 357 /** 358 * Gets the total number of attachments. 359 * 360 * @since 5.5.0 361 * 362 * @return {number} The total number of attachments. 363 */ 364 getTotalAttachments: function() { 365 return this.mirroring ? this.mirroring.totalAttachments : 0; 366 }, 367 350 368 /** 351 369 * A custom Ajax-response parser. 352 370 * 353 371 * See trac ticket #24753 354 372 * 355 * @param {Object|Array} resp The raw response Object/Array. 373 * Called automatically by Backbone whenever a collection's models are returned 374 * by the server, in fetch. The default implementation is a no-op, simply 375 * passing through the JSON response. We override this to add attributes to 376 * the collection items. 377 * 378 * Since WordPress 5.5, the response returns the attachments under `response.attachments` 379 * and `response.totalAttachments` holds the total number of attachments found. 380 * 381 * @param {Object|Array} response The raw response Object/Array. 356 382 * @param {Object} xhr 357 383 * @return {Array} The array of model attributes to be added to the collection 358 384 */ 359 parse: function( resp , xhr ) {360 if ( ! _.isArray( resp ) ) {361 resp = [resp];385 parse: function( response, xhr ) { 386 if ( ! _.isArray( response.attachments ) ) { 387 response = [response.attachments]; 362 388 } 363 389 364 return _.map( resp, function( attrs ) { 390 this.totalAttachments = parseInt( response.totalAttachments, 10 ); 391 392 return _.map( response.attachments, function( attrs ) { 365 393 var id, attachment, newAttributes; 366 394 367 395 if ( attrs instanceof Backbone.Model ) { -
src/js/media/models/query.js
diff --git a/src/js/media/models/query.js b/src/js/media/models/query.js index c29a638b10..471d828ac6 100644
a b Query = Attachments.extend(/** @lends wp.media.model.Query.prototype */{ 112 112 options = options || {}; 113 113 options.remove = false; 114 114 115 return this._more = this.fetch( options ).done( function( resp ) { 116 if ( _.isEmpty( resp ) || -1 === this.args.posts_per_page || resp.length < this.args.posts_per_page ) { 115 return this._more = this.fetch( options ).done( function( response ) { 116 // Since WordPress 5.5, the response returns the attachments under `response.attachments`. 117 var attachments = response.attachments; 118 119 if ( _.isEmpty( attachments ) || -1 === this.args.posts_per_page || attachments.length < this.args.posts_per_page ) { 117 120 query._hasMore = false; 118 121 } 119 122 }); -
src/js/media/views/attachments.js
diff --git a/src/js/media/views/attachments.js b/src/js/media/views/attachments.js index cb81ea3072..1fc9d777d6 100644
a b 1 1 var View = wp.media.View, 2 2 $ = jQuery, 3 Attachments; 3 Attachments, 4 infiniteScrolling = wp.media.view.settings.infiniteScrolling; 4 5 5 6 Attachments = View.extend(/** @lends wp.media.view.Attachments.prototype */{ 6 7 tagName: 'ul', … … Attachments = View.extend(/** @lends wp.media.view.Attachments.prototype */{ 35 36 this.el.id = _.uniqueId('__attachments-view-'); 36 37 37 38 /** 39 * @param infiniteScrolling Whether to enable infinite scrolling or use 40 * the default "load more" button. 38 41 * @param refreshSensitivity The time in milliseconds to throttle the scroll 39 42 * handler. 40 43 * @param refreshThreshold The amount of pixels that should be scrolled before … … Attachments = View.extend(/** @lends wp.media.view.Attachments.prototype */{ 49 52 * calculating the total number of columns. 50 53 */ 51 54 _.defaults( this.options, { 55 infiniteScrolling: infiniteScrolling || false, 52 56 refreshSensitivity: wp.media.isTouchDevice ? 300 : 200, 53 57 refreshThreshold: 3, 54 58 AttachmentView: wp.media.view.Attachment, … … Attachments = View.extend(/** @lends wp.media.view.Attachments.prototype */{ 84 88 85 89 this.controller.on( 'library:selection:add', this.attachmentFocus, this ); 86 90 87 // Throttle the scroll handler and bind this. 88 this.scroll = _.chain( this.scroll ).bind( this ).throttle( this.options.refreshSensitivity ).value(); 91 if ( this.options.infiniteScrolling ) { 92 // Throttle the scroll handler and bind this. 93 this.scroll = _.chain( this.scroll ).bind( this ).throttle( this.options.refreshSensitivity ).value(); 89 94 90 this.options.scrollElement = this.options.scrollElement || this.el; 91 $( this.options.scrollElement ).on( 'scroll', this.scroll ); 95 this.options.scrollElement = this.options.scrollElement || this.el; 96 $( this.options.scrollElement ).on( 'scroll', this.scroll ); 97 } 92 98 93 99 this.initSortable(); 94 100 … … Attachments = View.extend(/** @lends wp.media.view.Attachments.prototype */{ 387 393 this.views.set( this.collection.map( this.createAttachmentView, this ) ); 388 394 } else { 389 395 this.views.unset(); 390 this.collection.more().done( this.scroll ); 396 if ( this.options.infiniteScrolling ) { 397 this.collection.more().done( this.scroll ); 398 } 391 399 } 392 400 }, 393 401 … … Attachments = View.extend(/** @lends wp.media.view.Attachments.prototype */{ 400 408 * @return {void} 401 409 */ 402 410 ready: function() { 403 this.scroll(); 411 if ( this.options.infiniteScrolling ) { 412 this.scroll(); 413 } 404 414 }, 405 415 406 416 /** -
src/js/media/views/attachments/browser.js
diff --git a/src/js/media/views/attachments/browser.js b/src/js/media/views/attachments/browser.js index b29090832a..f3ac20e1ca 100644
a b var View = wp.media.View, 2 2 mediaTrash = wp.media.view.settings.mediaTrash, 3 3 l10n = wp.media.view.l10n, 4 4 $ = jQuery, 5 AttachmentsBrowser; 5 AttachmentsBrowser, 6 infiniteScrolling = wp.media.view.settings.infiniteScrolling, 7 __ = wp.i18n.__, 8 sprintf = wp.i18n.sprintf; 6 9 7 10 /** 8 11 * wp.media.view.AttachmentsBrowser … … AttachmentsBrowser = View.extend(/** @lends wp.media.view.AttachmentsBrowser.pro 68 71 this.createUploader(); 69 72 } 70 73 71 72 74 // Add a heading before the attachments list. 73 75 this.createAttachmentsHeading(); 74 76 75 // Create the list of attachments. 76 this.createAttachments(); 77 // Create the attachments wrapper view. 78 this.createAttachmentsWrapperView(); 79 80 if ( ! infiniteScrolling ) { 81 this.$el.addClass( 'has-load-more' ); 82 this.createLoadMoreView(); 83 } 77 84 78 85 // For accessibility reasons, place the normal sidebar after the attachments, see ticket #36909. 79 86 if ( this.options.sidebar && 'errors' !== this.options.sidebar ) { … … AttachmentsBrowser = View.extend(/** @lends wp.media.view.AttachmentsBrowser.pro 92 99 93 100 this.collection.on( 'add remove reset', this.updateContent, this ); 94 101 102 if ( ! infiniteScrolling ) { 103 this.collection.on( 'add remove reset', this.updateLoadMoreView, this ); 104 } 105 95 106 // The non-cached or cached attachments query has completed. 96 107 this.collection.on( 'attachments:received', this.announceSearchResults, this ); 97 108 }, … … AttachmentsBrowser = View.extend(/** @lends wp.media.view.AttachmentsBrowser.pro 106 117 * @return {void} 107 118 */ 108 119 announceSearchResults: _.debounce( function() { 109 var count; 120 var count, 121 /* translators: Accessibility text. %d: Number of attachments found in a search. */ 122 mediaFoundHasMoreResultsMessage = __( 'Number of media items displayed: %d. Click load more for more results.' ); 123 124 if ( infiniteScrolling ) { 125 /* translators: Accessibility text. %d: Number of attachments found in a search. */ 126 mediaFoundHasMoreResultsMessage = __( 'Number of media items displayed: %d. Scroll the page for more results.' ); 127 } 110 128 111 129 if ( this.collection.mirroring.args.s ) { 112 130 count = this.collection.length; … … AttachmentsBrowser = View.extend(/** @lends wp.media.view.AttachmentsBrowser.pro 117 135 } 118 136 119 137 if ( this.collection.hasMore() ) { 120 wp.a11y.speak( l10n.mediaFoundHasMoreResults.replace( '%d', count ) );138 wp.a11y.speak( mediaFoundHasMoreResultsMessage.replace( '%d', count ) ); 121 139 return; 122 140 } 123 141 … … AttachmentsBrowser = View.extend(/** @lends wp.media.view.AttachmentsBrowser.pro 392 410 noItemsView; 393 411 394 412 if ( this.controller.isModeActive( 'grid' ) ) { 413 // Usually the media library. 395 414 noItemsView = view.attachmentsNoResults; 396 415 } else { 416 // Usually the media modal. 397 417 noItemsView = view.uploader; 398 418 } 399 419 … … AttachmentsBrowser = View.extend(/** @lends wp.media.view.AttachmentsBrowser.pro 433 453 } 434 454 }, 435 455 456 /** 457 * Creates the Attachments wrapper view. 458 * 459 * @since 5.5.0 460 * 461 * @return {void} 462 */ 463 createAttachmentsWrapperView: function() { 464 this.attachmentsWrapper = new wp.media.View( { 465 className: 'attachments-wrapper' 466 } ); 467 468 // Create the list of attachments. 469 this.views.add( this.attachmentsWrapper ); 470 this.createAttachments(); 471 }, 472 436 473 createAttachments: function() { 437 474 this.attachments = new wp.media.view.Attachments({ 438 475 controller: this.controller, … … AttachmentsBrowser = View.extend(/** @lends wp.media.view.AttachmentsBrowser.pro 451 488 this.controller.on( 'attachment:keydown:arrow', _.bind( this.attachments.arrowEvent, this.attachments ) ); 452 489 this.controller.on( 'attachment:details:shift-tab', _.bind( this.attachments.restoreFocus, this.attachments ) ); 453 490 454 this.views.add( this.attachments ); 455 491 this.views.add( '.attachments-wrapper', this.attachments ); 456 492 457 493 if ( this.controller.isModeActive( 'grid' ) ) { 458 494 this.attachmentsNoResults = new View({ … … AttachmentsBrowser = View.extend(/** @lends wp.media.view.AttachmentsBrowser.pro 467 503 } 468 504 }, 469 505 506 /** 507 * Creates the load more button and attachments counter view. 508 * 509 * @since 5.5.0 510 * 511 * @return {void} 512 */ 513 createLoadMoreView: function() { 514 var view = this; 515 516 this.loadMoreWrapper = new View( { 517 controller: this.controller, 518 className: 'load-more-wrapper' 519 } ); 520 521 this.loadMoreCount = new View( { 522 controller: this.controller, 523 tagName: 'p', 524 className: 'load-more-count hidden' 525 } ); 526 527 this.loadMoreButton = new wp.media.view.Button( { 528 text: __( 'Load more' ), 529 className: 'load-more hidden', 530 style: 'primary', 531 size: 'hero', 532 click: function() { 533 view.loadMoreAttachments(); 534 } 535 } ); 536 537 this.loadMoreSpinner = new wp.media.view.Spinner(); 538 539 this.loadMoreJumpToFirst = new wp.media.view.Button( { 540 text: __( 'Jump to first new item' ), 541 className: 'load-more-jump visually-hidden', 542 size: '', 543 click: function() { 544 view.jumpToFirstAddedItem(); 545 } 546 } ); 547 548 this.views.add( '.attachments-wrapper', this.loadMoreWrapper ); 549 this.views.add( '.load-more-wrapper', this.loadMoreCount ); 550 this.views.add( '.load-more-wrapper', this.loadMoreButton ); 551 this.views.add( '.load-more-wrapper', this.loadMoreSpinner ); 552 this.views.add( '.load-more-wrapper', this.loadMoreJumpToFirst ); 553 }, 554 555 /** 556 * Updates the Load More view. This function is debounced because the 557 * collection updates multiple times at the add, remove, and reset events. 558 * We need it to run only once, after all attachments are added or removed. 559 * 560 * @since 5.5.0 561 * 562 * @return {void} 563 */ 564 updateLoadMoreView: _.debounce( function() { 565 // Ensure the load more view elements are initially hidden at each update. 566 this.loadMoreButton.$el.addClass( 'hidden' ); 567 this.loadMoreCount.$el.addClass( 'hidden' ); 568 this.loadMoreJumpToFirst.$el.addClass( 'visually-hidden' ); 569 570 if ( ! this.collection.getTotalAttachments() ) { 571 return; 572 } 573 574 if ( this.collection.length ) { 575 this.loadMoreCount.$el.text( 576 /* translators: 1: Number of displayed attachments, 2: Number of total attachments. */ 577 sprintf( 578 __( 'Media items %1$s of %2$s' ), 579 this.collection.length, 580 this.collection.getTotalAttachments() 581 ) 582 ); 583 584 this.loadMoreCount.$el.removeClass( 'hidden' ); 585 } 586 587 /* 588 * Notice that while the collection updates multiple times hasMore() may 589 * return true when it's actually not true. 590 */ 591 if ( this.collection.hasMore() ) { 592 this.loadMoreButton.$el.removeClass( 'hidden' ); 593 } 594 595 // Find the media item to move focus to. The jQuery `eq()` index is zero-based. 596 this.firstAddedMediaItem = this.$el.find( '.attachment' ).eq( this.firstAddedMediaItemIndex ); 597 598 // If there's a media item to move focus to, make the "Jump to" button available on focus. 599 if ( this.firstAddedMediaItem.length ) { 600 this.loadMoreJumpToFirst.$el.removeClass( 'visually-hidden' ); 601 } 602 }, 10 ), 603 604 /** 605 * Loads more attachments. 606 * 607 * @since 5.5.0 608 * 609 * @return {void} 610 */ 611 loadMoreAttachments: function() { 612 var view = this; 613 614 if ( ! this.collection.hasMore() ) { 615 return; 616 } 617 618 /* 619 * The collection index is zero-based while the length counts the actual 620 * amount of items. Thus the length is equivaent to the position of the 621 * first added item. 622 */ 623 this.firstAddedMediaItemIndex = this.collection.length; 624 625 view.loadMoreSpinner.show(); 626 627 this.collection.more().done( function() { 628 // Within done(), `this` is the returned collection. 629 view.loadMoreSpinner.hide(); 630 } ); 631 }, 632 633 /** 634 * Moves focus to the first new added item. . 635 * 636 * @since 5.5.0 637 * 638 * @return {void} 639 */ 640 jumpToFirstAddedItem: function() { 641 // Set focus on first added item. 642 this.firstAddedMediaItem.focus(); 643 }, 644 470 645 createAttachmentsHeading: function() { 471 646 this.attachmentsHeading = new wp.media.view.Heading( { 472 647 text: l10n.attachmentsList, -
src/wp-admin/css/media.css
diff --git a/src/wp-admin/css/media.css b/src/wp-admin/css/media.css index f13268c44b..b8d9ec6324 100644
a b border color while dragging a file over the uploader drop area */ 420 420 421 421 .media-frame.mode-grid, 422 422 .media-frame.mode-grid .media-frame-content, 423 .media-frame.mode-grid .attachments-browser .attachments, 423 .media-frame.mode-grid .attachments-browser:not(.has-load-more) .attachments, 424 .media-frame.mode-grid .attachments-browser.has-load-more .attachments-wrapper, 424 425 .media-frame.mode-grid .uploader-inline-content { 425 426 position: static; 426 427 } … … border color while dragging a file over the uploader drop area */ 498 499 border: 4px dashed #c3c4c7; 499 500 } 500 501 501 .media-frame.mode-select .attachments-browser.fixed .attachments { 502 .media-frame.mode-select .attachments-browser.fixed:not(.has-load-more) .attachments, 503 .media-frame.mode-select .attachments-browser.has-load-more.fixed .attachments-wrapper { 502 504 position: relative; 503 505 top: 94px; /* prevent jumping up when the toolbar becomes fixed */ 504 506 padding-bottom: 94px; /* offset for above so the bottom doesn't get cut off */ -
src/wp-admin/includes/ajax-actions.php
diff --git a/src/wp-admin/includes/ajax-actions.php b/src/wp-admin/includes/ajax-actions.php index 6f91c3a7ea..ae4b43daa7 100644
a b function wp_ajax_query_attachments() { 2993 2993 $posts = array_map( 'wp_prepare_attachment_for_js', $query->posts ); 2994 2994 $posts = array_filter( $posts ); 2995 2995 2996 wp_send_json_success( $posts ); 2996 $result = array( 2997 'attachments' => $posts, 2998 'totalAttachments' => $query->found_posts, 2999 ); 3000 3001 wp_send_json_success( $result ); 2997 3002 } 2998 3003 2999 3004 /** -
src/wp-includes/css/media-views.css
diff --git a/src/wp-includes/css/media-views.css b/src/wp-includes/css/media-views.css index 5fa85fda7d..fe6c0ba757 100644
a b 1190 1190 padding: 2px 8px 8px; 1191 1191 } 1192 1192 1193 .attachments-browser .attachments, 1193 .attachments-browser:not(.has-load-more) .attachments, 1194 .attachments-browser.has-load-more .attachments-wrapper, 1194 1195 .attachments-browser .uploader-inline { 1195 1196 position: absolute; 1196 1197 top: 72px; … … 1269 1270 padding: 2em 0 0 2em; 1270 1271 } 1271 1272 1273 .load-more-wrapper { 1274 clear: both; 1275 display: flex; 1276 flex-wrap: wrap; 1277 align-items: center; 1278 justify-content: center; 1279 padding: 1em 0; 1280 } 1281 1282 .load-more-wrapper .load-more-count { 1283 min-width: 100%; 1284 margin: 0 0 1em; 1285 text-align: center; 1286 } 1287 1288 .load-more-wrapper .load-more { 1289 margin: 0; 1290 } 1291 1292 /* Needs high specificity. */ 1293 .media-frame .load-more-wrapper .load-more + .spinner { 1294 float: none; 1295 margin: 0 -30px 0 10px; 1296 } 1297 1298 /* Reset spinner margin when the button is hidden to avoid horizontal scrollbar. */ 1299 .media-frame .load-more-wrapper .load-more.hidden + .spinner { 1300 margin: 0; 1301 } 1302 1303 /* Force a new row within the flex container. */ 1304 .load-more-wrapper::after { 1305 content: ""; 1306 min-width: 100%; 1307 order: 1; 1308 } 1309 1310 .load-more-wrapper .load-more-jump { 1311 /* Force a new row within the flex container. */ 1312 order: 2; 1313 /* Place the button out of screen. */ 1314 position: relative; 1315 left: -1000em; 1316 margin: 1em 0 0; 1317 } 1318 1319 .load-more-wrapper .load-more-jump.visually-hidden { 1320 visibility: hidden; 1321 } 1322 1323 .load-more-wrapper .load-more-jump:focus { 1324 /* Reveal the button on focus. */ 1325 left: auto; 1326 } 1327 1272 1328 /** 1273 1329 * Progress Bar 1274 1330 */ -
src/wp-includes/media.php
diff --git a/src/wp-includes/media.php b/src/wp-includes/media.php index 11b6fae7d1..e4e67a93b7 100644
a b function wp_enqueue_media( $args = array() ) { 4293 4293 ); 4294 4294 } 4295 4295 4296 /** 4297 * Filters whether the Media Library grid has infinite scrolling. Default `false`. 4298 * 4299 * @since 5.5.0 4300 * 4301 * @param bool $value The filtered value, defaults to `false`. 4302 */ 4303 $infinite_scrolling = apply_filters( 'media_library_infinite_scrolling', false ); 4304 4296 4305 $settings = array( 4297 'tabs' => $tabs,4298 'tabUrl' => add_query_arg( array( 'chromeless' => true ), admin_url( 'media-upload.php' ) ),4299 'mimeTypes' => wp_list_pluck( get_post_mime_types(), 0 ),4306 'tabs' => $tabs, 4307 'tabUrl' => add_query_arg( array( 'chromeless' => true ), admin_url( 'media-upload.php' ) ), 4308 'mimeTypes' => wp_list_pluck( get_post_mime_types(), 0 ), 4300 4309 /** This filter is documented in wp-admin/includes/media.php */ 4301 'captions' => ! apply_filters( 'disable_captions', '' ),4302 'nonce' => array(4310 'captions' => ! apply_filters( 'disable_captions', '' ), 4311 'nonce' => array( 4303 4312 'sendToEditor' => wp_create_nonce( 'media-send-to-editor' ), 4304 4313 ), 4305 'post' => array(4314 'post' => array( 4306 4315 'id' => 0, 4307 4316 ), 4308 'defaultProps' => $props,4309 'attachmentCounts' => array(4317 'defaultProps' => $props, 4318 'attachmentCounts' => array( 4310 4319 'audio' => ( $show_audio_playlist ) ? 1 : 0, 4311 4320 'video' => ( $show_video_playlist ) ? 1 : 0, 4312 4321 ), 4313 'oEmbedProxyUrl' => rest_url( 'oembed/1.0/proxy' ), 4314 'embedExts' => $exts, 4315 'embedMimes' => $ext_mimes, 4316 'contentWidth' => $content_width, 4317 'months' => $months, 4318 'mediaTrash' => MEDIA_TRASH ? 1 : 0, 4322 'oEmbedProxyUrl' => rest_url( 'oembed/1.0/proxy' ), 4323 'embedExts' => $exts, 4324 'embedMimes' => $ext_mimes, 4325 'contentWidth' => $content_width, 4326 'months' => $months, 4327 'mediaTrash' => MEDIA_TRASH ? 1 : 0, 4328 'infiniteScrolling' => ( $infinite_scrolling ) ? 1 : 0, 4319 4329 ); 4320 4330 4321 4331 $post = null; … … function wp_enqueue_media( $args = array() ) { 4399 4409 'searchLabel' => __( 'Search' ), 4400 4410 'searchMediaLabel' => __( 'Search media' ), // Backward compatibility pre-5.3. 4401 4411 'searchMediaPlaceholder' => __( 'Search media items...' ), // Placeholder (no ellipsis), backward compatibility pre-5.3. 4412 /* translators: %d: Number of attachments found in a search. */ 4402 4413 'mediaFound' => __( 'Number of media items found: %d' ), 4403 'mediaFoundHasMoreResults' => __( 'Number of media items displayed: %d. Scroll the page for more results.' ),4404 4414 'noMedia' => __( 'No media items found.' ), 4405 4415 'noMediaTryNewSearch' => __( 'No media items found. Try a different search.' ), 4406 4416