WordPress.org

Make WordPress Core

Changeset 29490


Ignore:
Timestamp:
08/14/2014 06:30:49 PM (4 years ago)
Author:
wonderboymusic
Message:

Media Grid, support MEDIA_TRASH:

  • Add a setting to _wpMediaViewsL10n.settings: mediaTrash
  • In the attachment edit modal, properly toggle between Trash/Untrash
  • In media.view.Attachment, add a method for untrashAttachment
  • When creating the grid toolbar, switch the setting order of subviews so that media.view.DeleteSelectedButton can listen to the instance of media.view.AttachmentFilters.All to update the text in its UI.
  • Add a new filter to media.view.AttachmentFilters.All, trash, when settings.mediaTrash is true
  • Allow the cached queries in Query.get() to be flushed when race conditions exist and collections need to be refreshed. This is currently only being used when MEDIA_TRASH is set, to refresh the filtered/mirrored collections related to all, trash, and any already queried filter.
  • Cleanup the bootstrapping of media.view.MediaFrame.Manage
  • Allow wp_ajax_query_attachments() to return items from the trash when MEDIA_TRASH is true
  • Allow wp_ajax_save_attachment() to set post_status when MEDIA_TRASH is true. It allows wp_delete_post() to be called, which will trash the attachment instead of deleting when the flag is set.

Props koop for the knowledge sharing and thought partnership.
See #29145.

Location:
trunk/src
Files:
7 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/wp-admin/includes/ajax-actions.php

    r29454 r29490  
    21622162
    21632163    $query['post_type'] = 'attachment';
    2164     $query['post_status'] = 'inherit';
     2164    if ( MEDIA_TRASH
     2165        && ! empty( $_REQUEST['query']['post_status'] )
     2166        && 'trash' === $_REQUEST['query']['post_status'] ) {
     2167        $query['post_status'] = 'trash';
     2168    } else {
     2169        $query['post_status'] = 'inherit';
     2170    }
     2171
    21652172    if ( current_user_can( get_post_type_object( 'attachment' )->cap->read_private_posts ) )
    21662173        $query['post_status'] .= ',private';
     
    22162223    if ( isset( $changes['description'] ) )
    22172224        $post['post_content'] = $changes['description'];
     2225
     2226    if ( MEDIA_TRASH && isset( $changes['status'] ) )
     2227        $post['post_status'] = $changes['status'];
    22182228
    22192229    if ( isset( $changes['alt'] ) ) {
     
    22442254    }
    22452255
    2246     wp_update_post( $post );
     2256    if ( MEDIA_TRASH && isset( $changes['status'] ) && 'trash' === $changes['status'] ) {
     2257        wp_delete_post( $id );
     2258    } else {
     2259        wp_update_post( $post );
     2260    }
     2261
    22472262    wp_send_json_success();
    22482263}
  • trunk/src/wp-includes/css/media-views.css

    r29489 r29490  
    16041604.attachment-info .refresh-attachment,
    16051605.attachment-info .delete-attachment,
    1606 .attachment-info .trash-attachment {
     1606.attachment-info .trash-attachment,
     1607.attachment-info .untrash-attachment {
    16071608    display: block;
    16081609    text-decoration: none;
     
    16211622
    16221623.media-modal .delete-attachment,
    1623 .media-modal .trash-attachment {
     1624.media-modal .trash-attachment,
     1625.media-modal .untrash-attachment {
    16241626    color: #bc0b0b;
    16251627}
    16261628
    16271629.media-modal .delete-attachment:hover,
    1628 .media-modal .trash-attachment:hover {
     1630.media-modal .trash-attachment:hover,
     1631.media-modal .untrash-attachment:hover {
    16291632    color: red;
    16301633}
     
    27442747}
    27452748
    2746 .edit-attachment-frame .delete-attachment {
     2749.edit-attachment-frame .delete-attachment,
     2750.edit-attachment-frame .trash-attachment,
     2751.edit-attachment-frame .untrash-attachment {
    27472752    float: right;
    27482753    margin-top: 7px;
  • trunk/src/wp-includes/js/media-grid.js

    r29489 r29490  
    184184            wp.media( {
    185185                frame:       'edit-attachments',
    186                 gridRouter:  this.gridRouter,
     186                controller:  this,
    187187                library:     this.state().get('library'),
    188188                model:       model
     
    231231
    232232        bindDeferred: function() {
     233            if ( ! this.browserView.dfd ) {
     234                return;
     235            }
    233236            this.browserView.dfd.done( _.bind( this.startHistory, this ) );
    234237        },
     
    353356
    354357        events: {
    355             'click':                    'collapse',
    356             'click .delete-media-item': 'deleteMediaItem',
    357358            'click .left':              'previousMediaItem',
    358359            'click .right':             'nextMediaItem'
     
    360361
    361362        initialize: function() {
    362             var self = this;
    363 
    364363            media.view.Frame.prototype.initialize.apply( this, arguments );
    365364
     
    369368            });
    370369
    371             this.gridRouter = this.options.gridRouter;
    372 
     370            this.controller = this.options.controller;
     371            this.gridRouter = this.controller.gridRouter;
    373372            this.library = this.options.library;
    374373
     
    376375                this.model = this.options.model;
    377376            } else {
     377                // this is a hack
    378378                this.model = this.library.at( 0 );
    379379            }
    380380
     381            this.bindHandlers();
     382            this.createStates();
     383            this.createModal();
     384
     385            this.title.mode( 'default' );
     386
     387            this.options.hasPrevious = this.hasPrevious();
     388            this.options.hasNext = this.hasNext();
     389        },
     390
     391        bindHandlers: function() {
     392            // Bind default title creation.
     393            this.on( 'title:create:default', this.createTitle, this );
     394
    381395            // Close the modal if the attachment is deleted.
    382             this.listenTo( this.model, 'destroy', this.close, this );
    383 
    384             this.createStates();
     396            this.listenTo( this.model, 'change:status destroy', this.close, this );
    385397
    386398            this.on( 'content:create:edit-metadata', this.editMetadataMode, this );
     
    388400            this.on( 'content:render:edit-image', this.editImageModeRender, this );
    389401            this.on( 'close', this.detach );
    390 
    391             // Bind default title creation.
    392             this.on( 'title:create:default', this.createTitle, this );
    393             this.title.mode( 'default' );
    394 
    395             this.options.hasPrevious = this.hasPrevious();
    396             this.options.hasNext = this.hasNext();
     402        },
     403
     404        createModal: function() {
     405            var self = this;
    397406
    398407            // Initialize modal container view.
     
    610619        initialize: function() {
    611620            media.view.Button.prototype.initialize.apply( this, arguments );
     621            if ( this.options.filters ) {
     622                this.listenTo( this.options.filters.model, 'change', this.filterChange );
     623            }
    612624            this.listenTo( this.controller, 'selection:toggle', this.toggleDisabled );
    613625        },
    614626
     627        filterChange: function( model ) {
     628            if ( 'trash' === model.get( 'status' ) ) {
     629                this.model.set( 'text', l10n.untrashSelected );
     630            } else if ( media.view.settings.mediaTrash ) {
     631                this.model.set( 'text', l10n.trashSelected );
     632            } else {
     633                this.model.set( 'text', l10n.deleteSelected );
     634            }
     635        },
     636
    615637        toggleDisabled: function() {
    616             this.$el.attr( 'disabled', ! this.controller.state().get( 'selection' ).length );
     638            this.model.set( 'disabled', ! this.controller.state().get( 'selection' ).length );
    617639        },
    618640
    619641        render: function() {
    620642            media.view.Button.prototype.render.apply( this, arguments );
    621             this.$el.addClass( 'delete-selected-button hidden' );
     643            if ( this.controller.isModeActive( 'select' ) ) {
     644                this.$el.addClass( 'delete-selected-button' );
     645            } else {
     646                this.$el.addClass( 'delete-selected-button hidden' );
     647            }
    622648            return this;
    623649        }
  • trunk/src/wp-includes/js/media-models.js

    r29076 r29490  
    825825         * @access private
    826826         */
    827         _requery: function() {
     827        _requery: function( cache ) {
     828            var props;
    828829            if ( this.props.get('query') ) {
    829                 this.mirror( Query.get( this.props.toJSON() ) );
     830                props = this.props.toJSON();
     831                props.cache = ( true !== cache );
     832                this.mirror( Query.get( props ) );
    830833            }
    831834        },
     
    948951
    949952                return uploadedTo === attachment.get('uploadedTo');
     953            },
     954            /**
     955             * @static
     956             * @param {wp.media.model.Attachment} attachment
     957             *
     958             * @this wp.media.model.Attachments
     959             *
     960             * @returns {Boolean}
     961             */
     962            status: function( attachment ) {
     963                var status = this.props.get('status');
     964                if ( _.isUndefined( status ) ) {
     965                    return true;
     966                }
     967
     968                return status === attachment.get('status');
    950969            }
    951970        }
     
    11451164            'perPage':   'posts_per_page',
    11461165            'menuOrder': 'menu_order',
    1147             'uploadedTo': 'post_parent'
     1166            'uploadedTo': 'post_parent',
     1167            'status':     'post_status'
    11481168        },
    11491169        /**
     
    11701190                    orderby  = Query.orderby,
    11711191                    defaults = Query.defaultProps,
    1172                     query;
     1192                    query,
     1193                    cache    = !! props.cache;
    11731194
    11741195                // Remove the `query` property. This isn't linked to a query,
    11751196                // this *is* the query.
    11761197                delete props.query;
     1198                delete props.cache;
    11771199
    11781200                // Fill default args.
     
    12081230
    12091231                // Search the query cache for matches.
    1210                 query = _.find( queries, function( query ) {
    1211                     return _.isEqual( query.args, args );
    1212                 });
     1232                if ( cache ) {
     1233                    query = _.find( queries, function( query ) {
     1234                        return _.isEqual( query.args, args );
     1235                    });
     1236                } else {
     1237                    queries = [];
     1238                }
    12131239
    12141240                // Otherwise, create a new query and add it to the cache.
  • trunk/src/wp-includes/js/media-views.js

    r29484 r29490  
    56725672                    text: text,
    56735673                    props: {
     5674                        status:  null,
    56745675                        type:    key,
    56755676                        uploadedTo: null,
     
    56835684                text:  l10n.allMediaItems,
    56845685                props: {
     5686                    status:  null,
    56855687                    type:    null,
    56865688                    uploadedTo: null,
     
    56955697                    text:  l10n.uploadedToThisPost,
    56965698                    props: {
     5699                        status:  null,
    56975700                        type:    null,
    56985701                        uploadedTo: media.view.settings.post.id,
     
    57075710                text:  l10n.unattached,
    57085711                props: {
     5712                    status:     null,
    57095713                    uploadedTo: 0,
    57105714                    type:       null,
     
    57145718                priority: 50
    57155719            };
     5720
     5721            if ( media.view.settings.mediaTrash ) {
     5722                filters.trash = {
     5723                    text:  l10n.trash,
     5724                    props: {
     5725                        uploadedTo: null,
     5726                        status:     'trash',
     5727                        type:       null,
     5728                        orderby:    'date',
     5729                        order:      'DESC'
     5730                    },
     5731                    priority: 50
     5732                };
     5733            }
    57165734
    57175735            this.filters = filters;
     
    57665784
    57675785        createToolbar: function() {
    5768             var filters,
    5769                 LibraryViewSwitcher,
    5770                 FiltersConstructor;
     5786            var LibraryViewSwitcher, Filters;
    57715787
    57725788            /**
     
    57785794
    57795795            this.views.add( this.toolbar );
     5796
     5797            this.toolbar.set( 'spinner', new media.view.Spinner({
     5798                priority: -60
     5799            }) );
     5800
     5801            if ( -1 !== $.inArray( this.options.filters, [ 'uploaded', 'all' ] ) ) {
     5802                // "Filters" will return a <select>, need to render
     5803                // screen reader text before
     5804                this.toolbar.set( 'filtersLabel', new media.view.Label({
     5805                    value: l10n.filterByType,
     5806                    attributes: {
     5807                        'for':  'media-attachment-filters'
     5808                    },
     5809                    priority:   -80
     5810                }).render() );
     5811
     5812                if ( 'uploaded' === this.options.filters ) {
     5813                    this.toolbar.set( 'filters', new media.view.AttachmentFilters.Uploaded({
     5814                        controller: this.controller,
     5815                        model:      this.collection.props,
     5816                        priority:   -80
     5817                    }).render() );
     5818                } else {
     5819                    Filters = new media.view.AttachmentFilters.All({
     5820                        controller: this.controller,
     5821                        model:      this.collection.props,
     5822                        priority:   -80
     5823                    });
     5824
     5825                    this.toolbar.set( 'filters', Filters.render() );
     5826                }
     5827            }
    57805828
    57815829            // Feels odd to bring the global media library switcher into the Attachment
     
    58155863
    58165864                this.toolbar.set( 'deleteSelectedButton', new media.view.DeleteSelectedButton({
     5865                    filters: Filters,
    58175866                    style: 'primary',
    58185867                    disabled: true,
    5819                     text: l10n.deleteSelected,
     5868                    text: media.view.settings.mediaTrash ? l10n.trashSelected : l10n.deleteSelected,
    58205869                    controller: this.controller,
    58215870                    priority: -60,
    58225871                    click: function() {
    5823                         while ( this.controller.state().get( 'selection' ).length > 0 ) {
    5824                             this.controller.state().get( 'selection' ).at( 0 ).destroy();
     5872                        var model, changed = [],
     5873                            selection = this.controller.state().get( 'selection' ),
     5874                            library = this.controller.state().get( 'library' );
     5875
     5876                        while ( selection.length > 0 ) {
     5877                            model = selection.at( 0 );
     5878                            if ( media.view.settings.mediaTrash && 'trash' === model.get( 'status' ) ) {
     5879                                model.set( 'status', 'inherit' );
     5880                                changed.push( model.save() );
     5881                                selection.remove( model );
     5882                            } else if ( media.view.settings.mediaTrash ) {
     5883                                model.set( 'status', 'trash' );
     5884                                changed.push( model.save() );
     5885                                selection.remove( model );
     5886                            } else {
     5887                                model.destroy();
     5888                            }
     5889                        }
     5890
     5891                        if ( changed.length ) {
     5892                            $.when( changed ).then( function() {
     5893                                library._requery( true );
     5894                            } );
    58255895                        }
    58265896                    }
    5827                 }).render() );
    5828             }
    5829 
    5830             this.toolbar.set( 'spinner', new media.view.Spinner({
    5831                 priority: -60
    5832             }) );
    5833 
    5834             filters = this.options.filters;
    5835             if ( 'uploaded' === filters ) {
    5836                 FiltersConstructor = media.view.AttachmentFilters.Uploaded;
    5837             } else if ( 'all' === filters ) {
    5838                 FiltersConstructor = media.view.AttachmentFilters.All;
    5839             }
    5840 
    5841             if ( FiltersConstructor ) {
    5842                 // "FiltersConstructor" will return a <select>, need to render
    5843                 // screen reader text before
    5844                 this.toolbar.set( 'filtersLabel', new media.view.Label({
    5845                     value: l10n.filterByType,
    5846                     attributes: {
    5847                         'for':  'media-attachment-filters'
    5848                     },
    5849                     priority:   -80
    5850                 }).render() );
    5851                 this.toolbar.set( 'filters', new FiltersConstructor({
    5852                     controller: this.controller,
    5853                     model:      this.collection.props,
    5854                     priority:   -80
    58555897                }).render() );
    58565898            }
     
    64216463            'click .delete-attachment':       'deleteAttachment',
    64226464            'click .trash-attachment':        'trashAttachment',
     6465            'click .untrash-attachment':      'untrashAttachment',
    64236466            'click .edit-attachment':         'editAttachment',
    64246467            'click .refresh-attachment':      'refreshAttachment',
     
    64546497         */
    64556498        trashAttachment: function( event ) {
     6499            var library = this.controller.library;
    64566500            event.preventDefault();
    64576501
    6458             this.model.destroy();
     6502            if ( media.view.settings.mediaTrash ) {
     6503                this.model.set( 'status', 'trash' );
     6504                this.model.save().done( function() {
     6505                    library._requery( true );
     6506                } );
     6507            }  else {
     6508                this.model.destroy();
     6509            }
     6510        },
     6511        /**
     6512         * @param {Object} event
     6513         */
     6514        untrashAttachment: function( event ) {
     6515            var library = this.controller.library;
     6516            event.preventDefault();
     6517
     6518            this.model.set( 'status', 'inherit' );
     6519            this.model.save().done( function() {
     6520                library._requery( true );
     6521            } );
    64596522        },
    64606523        /**
  • trunk/src/wp-includes/media-template.php

    r29457 r29490  
    317317                    <# if ( ! data.uploading && data.can.remove ) { #>
    318318                        <?php if ( MEDIA_TRASH ): ?>
     319                        <# if ( 'trash' === data.status ) { #>
     320                            <a class="untrash-attachment" href="#"><?php _e( 'Untrash' ); ?></a>
     321                        <# } else { #>
    319322                            <a class="trash-attachment" href="#"><?php _e( 'Trash' ); ?></a>
     323                        <# } #>
    320324                        <?php else: ?>
    321325                            <a class="delete-attachment" href="#"><?php _e( 'Delete Permanently' ); ?></a>
  • trunk/src/wp-includes/media.php

    r29486 r29490  
    28702870        'contentWidth' => $content_width,
    28712871        'months'       => $months,
     2872        'mediaTrash'   => MEDIA_TRASH ? 1 : 0
    28722873    );
    28732874
     
    29322933        'insertIntoPost'         => $hier ? __( 'Insert into page' ) : __( 'Insert into post' ),
    29332934        'unattached'             => __( 'Unattached' ),
     2935        'trash'                  => __( 'Trash' ),
    29342936        'uploadedToThisPost'     => $hier ? __( 'Uploaded to this page' ) : __( 'Uploaded to this post' ),
    29352937        'warnDelete'             => __( "You are about to permanently delete this item.\n  'Cancel' to stop, 'OK' to delete." ),
     
    29372939        'bulkSelect'             => __( 'Bulk Select' ),
    29382940        'cancelSelection'        => __( 'Cancel Selection' ),
     2941        'trashSelected'          => __( 'Trash Selected' ),
     2942        'untrashSelected'        => __( 'Untrash Selected' ),
    29392943        'deleteSelected'         => __( 'Delete Selected' ),
    29402944        'deletePermanently'      => __( 'Delete Permanently' ),
Note: See TracChangeset for help on using the changeset viewer.