Make WordPress Core

Ticket #50105: 50105.diff

File 50105.diff, 16.6 KB (added by afercia, 5 years ago)
  • src/js/media/models/attachments.js

     
    355355        hasMore: function() {
    356356                return this.mirroring ? this.mirroring.hasMore() : false;
    357357        },
     358
    358359        /**
     360         * Holds the total number of attachments.
     361         *
     362         * @since 5.5.0
     363         */
     364        totalAttachments: 0,
     365
     366        /**
     367         * Gets the total number of attachments.
     368         *
     369         * @since 5.5.0
     370         *
     371         * @return {number} The total number of attachments.
     372         */
     373        getTotalAttachments: function() {
     374                return this.mirroring ? this.mirroring.totalAttachments : 0;
     375        },
     376
     377        /**
    359378         * A custom AJAX-response parser.
    360379         *
    361380         * See trac ticket #24753
    362381         *
    363          * @param {Object|Array} resp The raw response Object/Array.
     382         * Called automatically by Backbone whenever a collection's models are returned
     383         * by the server, in fetch. The default implementation is a no-op, simply
     384         * passing through the JSON response. We override this to add attributes to
     385         * the collection items.
     386         *
     387         * Since WordPress 5.5, the response returns the attachments under `response.attachments`
     388         * and `response.totalAttachments` holds the total number of attachments found.
     389         *
     390         * @param {Object|Array} response The raw response Object/Array.
    364391         * @param {Object} xhr
    365392         * @return {Array} The array of model attributes to be added to the collection
    366393         */
    367         parse: function( resp, xhr ) {
    368                 if ( ! _.isArray( resp ) ) {
    369                         resp = [resp];
     394        parse: function( response, xhr ) {
     395                if ( ! _.isArray( response.attachments ) ) {
     396                        response = [response.attachments];
    370397                }
    371398
    372                 return _.map( resp, function( attrs ) {
     399                this.totalAttachments = parseInt( response.totalAttachments, 10 );
     400
     401                return _.map( response.attachments, function( attrs ) {
    373402                        var id, attachment, newAttributes;
    374403
    375404                        if ( attrs instanceof Backbone.Model ) {
  • src/js/media/models/query.js

     
    112112                options = options || {};
    113113                options.remove = false;
    114114
    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 ) {
    117120                                query._hasMore = false;
    118121                        }
    119122                });
  • src/js/media/views/attachments/browser.js

     
    22        mediaTrash = wp.media.view.settings.mediaTrash,
    33        l10n = wp.media.view.l10n,
    44        $ = jQuery,
    5         AttachmentsBrowser;
     5        AttachmentsBrowser,
     6        infiniteScrolling = wp.media.view.settings.infiniteScrolling,
     7        __ = wp.i18n.__,
     8        sprintf = wp.i18n.sprintf;
    69
    710/**
    811 * wp.media.view.AttachmentsBrowser
     
    6871                        this.createUploader();
    6972                }
    7073
    71 
    7274                // Add a heading before the attachments list.
    7375                this.createAttachmentsHeading();
    7476
    75                 // Create the list of attachments.
    76                 this.createAttachments();
     77                // Create the attachments wrapper view.
     78                this.createAttachmentsWrapperView();
    7779
     80                if ( ! infiniteScrolling ) {
     81                        this.$el.addClass( 'has-load-more' );
     82                        this.createLoadMoreView();
     83                }
     84
    7885                // For accessibility reasons, place the normal sidebar after the attachments, see ticket #36909.
    7986                if ( this.options.sidebar && 'errors' !== this.options.sidebar ) {
    8087                        this.createSidebar();
     
    9299
    93100                this.collection.on( 'add remove reset', this.updateContent, this );
    94101
     102                if ( ! infiniteScrolling ) {
     103                        this.collection.on( 'add remove reset', this.updateLoadMoreView, this );
     104                }
     105
    95106                // The non-cached or cached attachments query has completed.
    96107                this.collection.on( 'attachments:received', this.announceSearchResults, this );
    97108        },
     
    392403                        noItemsView;
    393404
    394405                if ( this.controller.isModeActive( 'grid' ) ) {
     406                        // Usually the media library.
    395407                        noItemsView = view.attachmentsNoResults;
    396408                } else {
     409                        // Usually the media modal.
    397410                        noItemsView = view.uploader;
    398411                }
    399412
     
    433446                }
    434447        },
    435448
     449        /**
     450         * Creates the Attachments wrapper view.
     451         *
     452         * @since 5.5.0
     453         *
     454         * @return {void}
     455         */
     456        createAttachmentsWrapperView: function() {
     457                this.attachmentsWrapper = new wp.media.View( {
     458                        className: 'attachments-wrapper'
     459                } );
     460
     461                // Create the list of attachments.
     462                this.views.add( this.attachmentsWrapper );
     463                this.createAttachments();
     464        },
     465
    436466        createAttachments: function() {
    437467                this.attachments = new wp.media.view.Attachments({
    438468                        controller:           this.controller,
     
    451481                this.controller.on( 'attachment:keydown:arrow',     _.bind( this.attachments.arrowEvent, this.attachments ) );
    452482                this.controller.on( 'attachment:details:shift-tab', _.bind( this.attachments.restoreFocus, this.attachments ) );
    453483
    454                 this.views.add( this.attachments );
     484                this.views.add( '.attachments-wrapper', this.attachments );
    455485
    456 
    457486                if ( this.controller.isModeActive( 'grid' ) ) {
    458487                        this.attachmentsNoResults = new View({
    459488                                controller: this.controller,
     
    467496                }
    468497        },
    469498
     499        /**
     500         * Creates the load more button and attachments counter view.
     501         *
     502         * @since 5.5.0
     503         *
     504         * @return {void}
     505         */
     506        createLoadMoreView: function() {
     507                var view = this;
     508
     509                this.loadMoreButton = new wp.media.view.Button( {
     510                        text: l10n.loadMore,
     511                        className: 'load-more hidden',
     512                        style: 'primary',
     513                        size: 'hero',
     514                        click: function() {
     515                                view.loadMoreAttachments();
     516                        }
     517                } );
     518
     519                this.loadMoreCount = new View( {
     520                        controller: this.controller,
     521                        tagName: 'p',
     522                        className: 'load-more-count hidden'
     523                } );
     524
     525                this.loadMoreSpinner = new wp.media.view.Spinner();
     526
     527                this.views.add( '.attachments-wrapper', this.loadMoreCount );
     528                this.views.add( '.attachments-wrapper', this.loadMoreButton );
     529                this.views.add( '.attachments-wrapper', this.loadMoreSpinner );
     530        },
     531
     532        /**
     533         * Updates the Load More view. This function is debounced because the
     534         * collection updates multiple times at the add, remove, and reset events.
     535         * We need it to run only once, after all attachments are added or removed.
     536         *
     537         * @since 5.5.0
     538         *
     539         * @return {void}
     540         */
     541        updateLoadMoreView: _.debounce( function() {
     542                // Ensure the load more view elements are initially hidden at each update.
     543                this.loadMoreButton.$el.addClass( 'hidden' );
     544                this.loadMoreCount.$el.addClass( 'hidden' );
     545
     546                if ( ! this.collection.getTotalAttachments() ) {
     547                        return;
     548                }
     549
     550                if ( this.collection.length ) {
     551                        this.loadMoreCount.$el.text(
     552                                /* translators: 1: Number of displayed attachments, 2: Number of total attachments. */
     553                                sprintf(
     554                                        __( 'Media items %1$s of %2$s' ),
     555                                        this.collection.length,
     556                                        this.collection.getTotalAttachments()
     557                                )
     558                        );
     559
     560                        this.loadMoreCount.$el.removeClass( 'hidden' );
     561                }
     562
     563                /*
     564                 * Notice that while the collection updates multiple times hasMore() may
     565                 * return true when it's actually not true.
     566                 */
     567                if ( this.collection.hasMore() ) {
     568                        this.loadMoreButton.$el.removeClass( 'hidden' );
     569                }
     570        }, 10 ),
     571
     572        /**
     573         * Loads more attachments.
     574         *
     575         * @since 5.5.0
     576         *
     577         * @return {void}
     578         */
     579        loadMoreAttachments: function() {
     580                var view = this,
     581                        loadMoreSpinner = this.loadMoreSpinner,
     582                        firstAddedMediaItemIndex;
     583
     584                if ( ! this.collection.hasMore() ) {
     585                        return;
     586                }
     587
     588                // Collection index starts from 0.
     589                firstAddedMediaItemIndex = this.collection.length;
     590
     591                loadMoreSpinner.show();
     592
     593                this.collection.more().done( function() {
     594                        // Within done(), `this` is the returned collection.
     595                        loadMoreSpinner.hide();
     596                        // Set focus on first added item.
     597                        view.$el.find( 'li' ).eq( firstAddedMediaItemIndex ).focus();
     598                } );
     599        },
     600
    470601        createAttachmentsHeading: function() {
    471602                this.attachmentsHeading = new wp.media.view.Heading( {
    472603                        text: l10n.attachmentsList,
  • src/js/media/views/attachments.js

     
    11var View = wp.media.View,
    22        $ = jQuery,
    3         Attachments;
     3        Attachments,
     4        infiniteScrolling = wp.media.view.settings.infiniteScrolling;
    45
    56Attachments = View.extend(/** @lends wp.media.view.Attachments.prototype */{
    67        tagName:   'ul',
     
    3536                this.el.id = _.uniqueId('__attachments-view-');
    3637
    3738                /**
     39                 * @param infiniteScrolling  Whether to enable infinite scrolling or use
     40                 *                           the default "load more" button.
    3841                 * @param refreshSensitivity The time in milliseconds to throttle the scroll
    3942                 *                           handler.
    4043                 * @param refreshThreshold   The amount of pixels that should be scrolled before
     
    4952                 *                           calculating the total number of columns.
    5053                 */
    5154                _.defaults( this.options, {
     55                        infiniteScrolling:  infiniteScrolling || false,
    5256                        refreshSensitivity: wp.media.isTouchDevice ? 300 : 200,
    5357                        refreshThreshold:   3,
    5458                        AttachmentView:     wp.media.view.Attachment,
     
    8488
    8589                this.controller.on( 'library:selection:add', this.attachmentFocus, this );
    8690
    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();
    8994
    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                }
    9298
    9399                this.initSortable();
    94100
     
    387393                        this.views.set( this.collection.map( this.createAttachmentView, this ) );
    388394                } else {
    389395                        this.views.unset();
    390                         this.collection.more().done( this.scroll );
     396                        if ( this.options.infiniteScrolling ) {
     397                                this.collection.more().done( this.scroll );
     398                        }
    391399                }
    392400        },
    393401
     
    400408         * @return {void}
    401409         */
    402410        ready: function() {
    403                 this.scroll();
     411                if ( this.options.infiniteScrolling ) {
     412                        this.scroll();
     413                }
    404414        },
    405415
    406416        /**
  • src/wp-admin/css/media.css

     
    416416
    417417.media-frame.mode-grid,
    418418.media-frame.mode-grid .media-frame-content,
    419 .media-frame.mode-grid .attachments-browser .attachments,
     419.media-frame.mode-grid .attachments-browser:not(.has-load-more) .attachments,
     420.media-frame.mode-grid .attachments-browser.has-load-more .attachments-wrapper,
    420421.media-frame.mode-grid .uploader-inline-content {
    421422        position: static;
    422423}
     
    494495        border: 4px dashed #b4b9be;
    495496}
    496497
    497 .media-frame.mode-select .attachments-browser.fixed .attachments {
     498.media-frame.mode-select .attachments-browser.fixed:not(.has-load-more) .attachments,
     499.media-frame.mode-select .attachments-browser.has-load-more.fixed .attachments-wrapper {
    498500        position: relative;
    499501        top: 94px; /* prevent jumping up when the toolbar becomes fixed */
    500502        padding-bottom: 94px; /* offset for above so the bottom doesn't get cut off */
  • src/wp-admin/includes/ajax-actions.php

     
    29692969        $posts = array_map( 'wp_prepare_attachment_for_js', $query->posts );
    29702970        $posts = array_filter( $posts );
    29712971
    2972         wp_send_json_success( $posts );
     2972        $result = array(
     2973                'attachments'      => $posts,
     2974                'totalAttachments' => $query->found_posts,
     2975        );
     2976
     2977        wp_send_json_success( $result );
    29732978}
    29742979
    29752980/**
  • src/wp-includes/css/media-views.css

     
    11701170        padding: 2px 8px 8px;
    11711171}
    11721172
    1173 .attachments-browser .attachments,
     1173.attachments-browser:not(.has-load-more) .attachments,
     1174.attachments-browser.has-load-more .attachments-wrapper,
    11741175.attachments-browser .uploader-inline {
    11751176        position: absolute;
    11761177        top: 72px;
     
    12491250        padding: 2em 0 0 2em;
    12501251}
    12511252
     1253.attachments-browser .load-more-count {
     1254        clear: both;
     1255        text-align: center;
     1256}
     1257
     1258.attachments-browser .load-more {
     1259        display: block;
     1260        margin: 1em auto;
     1261}
     1262
     1263.media-frame .attachments-browser .load-more + .spinner {
     1264        display: block;
     1265        float: none;
     1266        margin: 8px auto 16px;
     1267}
     1268
    12521269/**
    12531270 * Progress Bar
    12541271 */
  • src/wp-includes/media.php

     
    39693969                );
    39703970        }
    39713971
     3972        /**
     3973         * Filters whether the Media Library grid has infinite scrolling. Default `false`.
     3974         *
     3975         * @since 5.5.0
     3976         *
     3977         * @param bool $value The filtered value, defaults to `false`.
     3978         */
     3979        $infinite_scrolling = apply_filters( 'media_library_infinite_scrolling', false );
     3980
    39723981        $settings = array(
    3973                 'tabs'             => $tabs,
    3974                 'tabUrl'           => add_query_arg( array( 'chromeless' => true ), admin_url( 'media-upload.php' ) ),
    3975                 'mimeTypes'        => wp_list_pluck( get_post_mime_types(), 0 ),
     3982                'tabs'              => $tabs,
     3983                'tabUrl'            => add_query_arg( array( 'chromeless' => true ), admin_url( 'media-upload.php' ) ),
     3984                'mimeTypes'         => wp_list_pluck( get_post_mime_types(), 0 ),
    39763985                /** This filter is documented in wp-admin/includes/media.php */
    3977                 'captions'         => ! apply_filters( 'disable_captions', '' ),
    3978                 'nonce'            => array(
     3986                'captions'          => ! apply_filters( 'disable_captions', '' ),
     3987                'nonce'             => array(
    39793988                        'sendToEditor' => wp_create_nonce( 'media-send-to-editor' ),
    39803989                ),
    3981                 'post'             => array(
     3990                'post'              => array(
    39823991                        'id' => 0,
    39833992                ),
    3984                 'defaultProps'     => $props,
    3985                 'attachmentCounts' => array(
     3993                'defaultProps'      => $props,
     3994                'attachmentCounts'  => array(
    39863995                        'audio' => ( $show_audio_playlist ) ? 1 : 0,
    39873996                        'video' => ( $show_video_playlist ) ? 1 : 0,
    39883997                ),
    3989                 'oEmbedProxyUrl'   => rest_url( 'oembed/1.0/proxy' ),
    3990                 'embedExts'        => $exts,
    3991                 'embedMimes'       => $ext_mimes,
    3992                 'contentWidth'     => $content_width,
    3993                 'months'           => $months,
    3994                 'mediaTrash'       => MEDIA_TRASH ? 1 : 0,
     3998                'oEmbedProxyUrl'    => rest_url( 'oembed/1.0/proxy' ),
     3999                'embedExts'         => $exts,
     4000                'embedMimes'        => $ext_mimes,
     4001                'contentWidth'      => $content_width,
     4002                'months'            => $months,
     4003                'mediaTrash'        => MEDIA_TRASH ? 1 : 0,
     4004                'infiniteScrolling' => ( $infinite_scrolling ) ? 1 : 0,
    39954005        );
    39964006
    39974007        $post = null;
     
    40234033                $post_type_object = get_post_type_object( 'post' );
    40244034        }
    40254035
     4036        /* translators: Accessibility text. %d: Number of attachments found in a search. */
     4037        $media_found_has_more_results = __( 'Number of media items displayed: %d. Click load more for more results.' );
     4038        if ( $infinite_scrolling ) {
     4039                /* translators: Accessibility text. %d: Number of attachments found in a search. */
     4040                $media_found_has_more_results = __( 'Number of media items displayed: %d. Scroll the page for more results.' );
     4041        }
     4042
    40264043        $strings = array(
    40274044                // Generic.
    40284045                'mediaFrameDefaultTitle'      => __( 'Media' ),
     
    40754092                'searchLabel'                 => __( 'Search' ),
    40764093                'searchMediaLabel'            => __( 'Search Media' ),          // Backward compatibility pre-5.3.
    40774094                'searchMediaPlaceholder'      => __( 'Search media items...' ), // Placeholder (no ellipsis), backward compatibility pre-5.3.
     4095                /* translators: %d: Number of attachments found in a search. */
    40784096                'mediaFound'                  => __( 'Number of media items found: %d' ),
    4079                 'mediaFoundHasMoreResults'    => __( 'Number of media items displayed: %d. Scroll the page for more results.' ),
     4097                'mediaFoundHasMoreResults'    => $media_found_has_more_results,
    40804098                'noMedia'                     => __( 'No media items found.' ),
    40814099                'noMediaTryNewSearch'         => __( 'No media items found. Try a different search.' ),
     4100                'loadMore'                    => __( 'Load more' ),
    40824101
    40834102                // Library Details.
    40844103                'attachmentDetails'           => __( 'Attachment Details' ),