<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head><meta http-equiv="content-type" content="text/html; charset=utf-8" />
<title>[29490] trunk/src: Media Grid, support `MEDIA_TRASH`:</title>
</head>
<body>

<style type="text/css"><!--
#msg dl.meta { border: 1px #006 solid; background: #369; padding: 6px; color: #fff; }
#msg dl.meta dt { float: left; width: 6em; font-weight: bold; }
#msg dt:after { content:':';}
#msg dl, #msg dt, #msg ul, #msg li, #header, #footer, #logmsg { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt;  }
#msg dl a { font-weight: bold}
#msg dl a:link    { color:#fc3; }
#msg dl a:active  { color:#ff0; }
#msg dl a:visited { color:#cc6; }
h3 { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt; font-weight: bold; }
#msg pre { overflow: auto; background: #ffc; border: 1px #fa0 solid; padding: 6px; }
#logmsg { background: #ffc; border: 1px #fa0 solid; padding: 1em 1em 0 1em; }
#logmsg p, #logmsg pre, #logmsg blockquote { margin: 0 0 1em 0; }
#logmsg p, #logmsg li, #logmsg dt, #logmsg dd { line-height: 14pt; }
#logmsg h1, #logmsg h2, #logmsg h3, #logmsg h4, #logmsg h5, #logmsg h6 { margin: .5em 0; }
#logmsg h1:first-child, #logmsg h2:first-child, #logmsg h3:first-child, #logmsg h4:first-child, #logmsg h5:first-child, #logmsg h6:first-child { margin-top: 0; }
#logmsg ul, #logmsg ol { padding: 0; list-style-position: inside; margin: 0 0 0 1em; }
#logmsg ul { text-indent: -1em; padding-left: 1em; }#logmsg ol { text-indent: -1.5em; padding-left: 1.5em; }
#logmsg > ul, #logmsg > ol { margin: 0 0 1em 0; }
#logmsg pre { background: #eee; padding: 1em; }
#logmsg blockquote { border: 1px solid #fa0; border-left-width: 10px; padding: 1em 1em 0 1em; background: white;}
#logmsg dl { margin: 0; }
#logmsg dt { font-weight: bold; }
#logmsg dd { margin: 0; padding: 0 0 0.5em 0; }
#logmsg dd:before { content:'\00bb';}
#logmsg table { border-spacing: 0px; border-collapse: collapse; border-top: 4px solid #fa0; border-bottom: 1px solid #fa0; background: #fff; }
#logmsg table th { text-align: left; font-weight: normal; padding: 0.2em 0.5em; border-top: 1px dotted #fa0; }
#logmsg table td { text-align: right; border-top: 1px dotted #fa0; padding: 0.2em 0.5em; }
#logmsg table thead th { text-align: center; border-bottom: 1px solid #fa0; }
#logmsg table th.Corner { text-align: left; }
#logmsg hr { border: none 0; border-top: 2px dashed #fa0; height: 1px; }
#header, #footer { color: #fff; background: #636; border: 1px #300 solid; padding: 6px; }
#patch { width: 100%; }
#patch h4 {font-family: verdana,arial,helvetica,sans-serif;font-size:10pt;padding:8px;background:#369;color:#fff;margin:0;}
#patch .propset h4, #patch .binary h4 {margin:0;}
#patch pre {padding:0;line-height:1.2em;margin:0;}
#patch .diff {width:100%;background:#eee;padding: 0 0 10px 0;overflow:auto;}
#patch .propset .diff, #patch .binary .diff  {padding:10px 0;}
#patch span {display:block;padding:0 10px;}
#patch .modfile, #patch .addfile, #patch .delfile, #patch .propset, #patch .binary, #patch .copfile {border:1px solid #ccc;margin:10px 0;}
#patch ins {background:#dfd;text-decoration:none;display:block;padding:0 10px;}
#patch del {background:#fdd;text-decoration:none;display:block;padding:0 10px;}
#patch .lines, .info {color:#888;background:#fff;}
--></style>
<div id="msg">
<dl class="meta">
<dt>Revision</dt> <dd><a href="http://core.trac.wordpress.org/changeset/29490">29490</a></dd>
<dt>Author</dt> <dd>wonderboymusic</dd>
<dt>Date</dt> <dd>2014-08-14 18:30:49 +0000 (Thu, 14 Aug 2014)</dd>
</dl>

<h3>Log Message</h3>
<pre>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 <a href="http://core.trac.wordpress.org/ticket/29145">#29145</a>.</pre>

<h3>Modified Paths</h3>
<ul>
<li><a href="#trunksrcwpadminincludesajaxactionsphp">trunk/src/wp-admin/includes/ajax-actions.php</a></li>
<li><a href="#trunksrcwpincludescssmediaviewscss">trunk/src/wp-includes/css/media-views.css</a></li>
<li><a href="#trunksrcwpincludesjsmediagridjs">trunk/src/wp-includes/js/media-grid.js</a></li>
<li><a href="#trunksrcwpincludesjsmediamodelsjs">trunk/src/wp-includes/js/media-models.js</a></li>
<li><a href="#trunksrcwpincludesjsmediaviewsjs">trunk/src/wp-includes/js/media-views.js</a></li>
<li><a href="#trunksrcwpincludesmediatemplatephp">trunk/src/wp-includes/media-template.php</a></li>
<li><a href="#trunksrcwpincludesmediaphp">trunk/src/wp-includes/media.php</a></li>
</ul>

</div>
<div id="patch">
<h3>Diff</h3>
<a id="trunksrcwpadminincludesajaxactionsphp"></a>
<div class="modfile"><h4>Modified: trunk/src/wp-admin/includes/ajax-actions.php (29489 => 29490)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/src/wp-admin/includes/ajax-actions.php     2014-08-14 07:17:27 UTC (rev 29489)
+++ trunk/src/wp-admin/includes/ajax-actions.php        2014-08-14 18:30:49 UTC (rev 29490)
</span><span class="lines">@@ -2161,7 +2161,14 @@
</span><span class="cx">  ) ) );
</span><span class="cx"> 
</span><span class="cx">  $query['post_type'] = 'attachment';
</span><del>-       $query['post_status'] = 'inherit';
</del><ins>+        if ( MEDIA_TRASH
+               && ! empty( $_REQUEST['query']['post_status'] )
+               && 'trash' === $_REQUEST['query']['post_status'] ) {
+               $query['post_status'] = 'trash';
+       } else {
+               $query['post_status'] = 'inherit';
+       }
+
</ins><span class="cx">   if ( current_user_can( get_post_type_object( 'attachment' )->cap->read_private_posts ) )
</span><span class="cx">          $query['post_status'] .= ',private';
</span><span class="cx"> 
</span><span class="lines">@@ -2216,6 +2223,9 @@
</span><span class="cx">  if ( isset( $changes['description'] ) )
</span><span class="cx">          $post['post_content'] = $changes['description'];
</span><span class="cx"> 
</span><ins>+       if ( MEDIA_TRASH && isset( $changes['status'] ) )
+               $post['post_status'] = $changes['status'];
+
</ins><span class="cx">   if ( isset( $changes['alt'] ) ) {
</span><span class="cx">          $alt = wp_unslash( $changes['alt'] );
</span><span class="cx">          if ( $alt != get_post_meta( $id, '_wp_attachment_image_alt', true ) ) {
</span><span class="lines">@@ -2243,7 +2253,12 @@
</span><span class="cx">          }
</span><span class="cx">  }
</span><span class="cx"> 
</span><del>-       wp_update_post( $post );
</del><ins>+        if ( MEDIA_TRASH && isset( $changes['status'] ) && 'trash' === $changes['status'] ) {
+               wp_delete_post( $id );
+       } else {
+               wp_update_post( $post );
+       }
+
</ins><span class="cx">   wp_send_json_success();
</span><span class="cx"> }
</span><span class="cx"> 
</span></span></pre></div>
<a id="trunksrcwpincludescssmediaviewscss"></a>
<div class="modfile"><h4>Modified: trunk/src/wp-includes/css/media-views.css (29489 => 29490)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/src/wp-includes/css/media-views.css        2014-08-14 07:17:27 UTC (rev 29489)
+++ trunk/src/wp-includes/css/media-views.css   2014-08-14 18:30:49 UTC (rev 29490)
</span><span class="lines">@@ -1603,7 +1603,8 @@
</span><span class="cx"> .attachment-info .edit-attachment,
</span><span class="cx"> .attachment-info .refresh-attachment,
</span><span class="cx"> .attachment-info .delete-attachment,
</span><del>-.attachment-info .trash-attachment {
</del><ins>+.attachment-info .trash-attachment,
+.attachment-info .untrash-attachment {
</ins><span class="cx">   display: block;
</span><span class="cx">  text-decoration: none;
</span><span class="cx">  white-space: nowrap;
</span><span class="lines">@@ -1620,12 +1621,14 @@
</span><span class="cx"> }
</span><span class="cx"> 
</span><span class="cx"> .media-modal .delete-attachment,
</span><del>-.media-modal .trash-attachment {
</del><ins>+.media-modal .trash-attachment,
+.media-modal .untrash-attachment {
</ins><span class="cx">   color: #bc0b0b;
</span><span class="cx"> }
</span><span class="cx"> 
</span><span class="cx"> .media-modal .delete-attachment:hover,
</span><del>-.media-modal .trash-attachment:hover {
</del><ins>+.media-modal .trash-attachment:hover,
+.media-modal .untrash-attachment:hover {
</ins><span class="cx">   color: red;
</span><span class="cx"> }
</span><span class="cx"> 
</span><span class="lines">@@ -2743,7 +2746,9 @@
</span><span class="cx">  max-height: calc( 100% - 42px ); /* leave space for actions underneath */
</span><span class="cx"> }
</span><span class="cx"> 
</span><del>-.edit-attachment-frame .delete-attachment {
</del><ins>+.edit-attachment-frame .delete-attachment,
+.edit-attachment-frame .trash-attachment,
+.edit-attachment-frame .untrash-attachment {
</ins><span class="cx">   float: right;
</span><span class="cx">  margin-top: 7px;
</span><span class="cx"> }
</span></span></pre></div>
<a id="trunksrcwpincludesjsmediagridjs"></a>
<div class="modfile"><h4>Modified: trunk/src/wp-includes/js/media-grid.js (29489 => 29490)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/src/wp-includes/js/media-grid.js   2014-08-14 07:17:27 UTC (rev 29489)
+++ trunk/src/wp-includes/js/media-grid.js      2014-08-14 18:30:49 UTC (rev 29490)
</span><span class="lines">@@ -183,7 +183,7 @@
</span><span class="cx">                  // Create a new EditAttachment frame, passing along the library and the attachment model.
</span><span class="cx">                  wp.media( {
</span><span class="cx">                          frame:       'edit-attachments',
</span><del>-                               gridRouter:  this.gridRouter,
</del><ins>+                                controller:  this,
</ins><span class="cx">                           library:     this.state().get('library'),
</span><span class="cx">                          model:       model
</span><span class="cx">                  } );
</span><span class="lines">@@ -230,6 +230,9 @@
</span><span class="cx">          },
</span><span class="cx"> 
</span><span class="cx">          bindDeferred: function() {
</span><ins>+                       if ( ! this.browserView.dfd ) {
+                               return;
+                       }
</ins><span class="cx">                   this.browserView.dfd.done( _.bind( this.startHistory, this ) );
</span><span class="cx">          },
</span><span class="cx"> 
</span><span class="lines">@@ -352,15 +355,11 @@
</span><span class="cx">          regions:   [ 'title', 'content' ],
</span><span class="cx"> 
</span><span class="cx">          events: {
</span><del>-                       'click':                    'collapse',
-                       'click .delete-media-item': 'deleteMediaItem',
</del><span class="cx">                   'click .left':              'previousMediaItem',
</span><span class="cx">                  'click .right':             'nextMediaItem'
</span><span class="cx">          },
</span><span class="cx"> 
</span><span class="cx">          initialize: function() {
</span><del>-                       var self = this;
-
</del><span class="cx">                   media.view.Frame.prototype.initialize.apply( this, arguments );
</span><span class="cx"> 
</span><span class="cx">                  _.defaults( this.options, {
</span><span class="lines">@@ -368,33 +367,43 @@
</span><span class="cx">                          state: 'edit-attachment'
</span><span class="cx">                  });
</span><span class="cx"> 
</span><del>-                       this.gridRouter = this.options.gridRouter;
-
</del><ins>+                        this.controller = this.options.controller;
+                       this.gridRouter = this.controller.gridRouter;
</ins><span class="cx">                   this.library = this.options.library;
</span><span class="cx"> 
</span><span class="cx">                  if ( this.options.model ) {
</span><span class="cx">                          this.model = this.options.model;
</span><span class="cx">                  } else {
</span><ins>+                               // this is a hack
</ins><span class="cx">                           this.model = this.library.at( 0 );
</span><span class="cx">                  }
</span><span class="cx"> 
</span><ins>+                       this.bindHandlers();
+                       this.createStates();
+                       this.createModal();
+
+                       this.title.mode( 'default' );
+
+                       this.options.hasPrevious = this.hasPrevious();
+                       this.options.hasNext = this.hasNext();
+               },
+
+               bindHandlers: function() {
+                       // Bind default title creation.
+                       this.on( 'title:create:default', this.createTitle, this );
+
</ins><span class="cx">                   // Close the modal if the attachment is deleted.
</span><del>-                       this.listenTo( this.model, 'destroy', this.close, this );
</del><ins>+                        this.listenTo( this.model, 'change:status destroy', this.close, this );
</ins><span class="cx"> 
</span><del>-                       this.createStates();
-
</del><span class="cx">                   this.on( 'content:create:edit-metadata', this.editMetadataMode, this );
</span><span class="cx">                  this.on( 'content:create:edit-image', this.editImageMode, this );
</span><span class="cx">                  this.on( 'content:render:edit-image', this.editImageModeRender, this );
</span><span class="cx">                  this.on( 'close', this.detach );
</span><ins>+               },
</ins><span class="cx"> 
</span><del>-                       // Bind default title creation.
-                       this.on( 'title:create:default', this.createTitle, this );
-                       this.title.mode( 'default' );
</del><ins>+                createModal: function() {
+                       var self = this;
</ins><span class="cx"> 
</span><del>-                       this.options.hasPrevious = this.hasPrevious();
-                       this.options.hasNext = this.hasNext();
-
</del><span class="cx">                   // Initialize modal container view.
</span><span class="cx">                  if ( this.options.modal ) {
</span><span class="cx">                          this.modal = new media.view.Modal({
</span><span class="lines">@@ -609,16 +618,33 @@
</span><span class="cx">  media.view.DeleteSelectedButton = media.view.Button.extend({
</span><span class="cx">          initialize: function() {
</span><span class="cx">                  media.view.Button.prototype.initialize.apply( this, arguments );
</span><ins>+                       if ( this.options.filters ) {
+                               this.listenTo( this.options.filters.model, 'change', this.filterChange );
+                       }
</ins><span class="cx">                   this.listenTo( this.controller, 'selection:toggle', this.toggleDisabled );
</span><span class="cx">          },
</span><span class="cx"> 
</span><ins>+               filterChange: function( model ) {
+                       if ( 'trash' === model.get( 'status' ) ) {
+                               this.model.set( 'text', l10n.untrashSelected );
+                       } else if ( media.view.settings.mediaTrash ) {
+                               this.model.set( 'text', l10n.trashSelected );
+                       } else {
+                               this.model.set( 'text', l10n.deleteSelected );
+                       }
+               },
+
</ins><span class="cx">           toggleDisabled: function() {
</span><del>-                       this.$el.attr( 'disabled', ! this.controller.state().get( 'selection' ).length );
</del><ins>+                        this.model.set( 'disabled', ! this.controller.state().get( 'selection' ).length );
</ins><span class="cx">           },
</span><span class="cx"> 
</span><span class="cx">          render: function() {
</span><span class="cx">                  media.view.Button.prototype.render.apply( this, arguments );
</span><del>-                       this.$el.addClass( 'delete-selected-button hidden' );
</del><ins>+                        if ( this.controller.isModeActive( 'select' ) ) {
+                               this.$el.addClass( 'delete-selected-button' );
+                       } else {
+                               this.$el.addClass( 'delete-selected-button hidden' );
+                       }
</ins><span class="cx">                   return this;
</span><span class="cx">          }
</span><span class="cx">  });
</span></span></pre></div>
<a id="trunksrcwpincludesjsmediamodelsjs"></a>
<div class="modfile"><h4>Modified: trunk/src/wp-includes/js/media-models.js (29489 => 29490)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/src/wp-includes/js/media-models.js 2014-08-14 07:17:27 UTC (rev 29489)
+++ trunk/src/wp-includes/js/media-models.js    2014-08-14 18:30:49 UTC (rev 29490)
</span><span class="lines">@@ -824,9 +824,12 @@
</span><span class="cx">          /**
</span><span class="cx">           * @access private
</span><span class="cx">           */
</span><del>-               _requery: function() {
</del><ins>+                _requery: function( cache ) {
+                       var props;
</ins><span class="cx">                   if ( this.props.get('query') ) {
</span><del>-                               this.mirror( Query.get( this.props.toJSON() ) );
</del><ins>+                                props = this.props.toJSON();
+                               props.cache = ( true !== cache );
+                               this.mirror( Query.get( props ) );
</ins><span class="cx">                   }
</span><span class="cx">          },
</span><span class="cx">          /**
</span><span class="lines">@@ -947,6 +950,22 @@
</span><span class="cx">                          }
</span><span class="cx"> 
</span><span class="cx">                          return uploadedTo === attachment.get('uploadedTo');
</span><ins>+                       },
+                       /**
+                        * @static
+                        * @param {wp.media.model.Attachment} attachment
+                        *
+                        * @this wp.media.model.Attachments
+                        *
+                        * @returns {Boolean}
+                        */
+                       status: function( attachment ) {
+                               var status = this.props.get('status');
+                               if ( _.isUndefined( status ) ) {
+                                       return true;
+                               }
+
+                               return status === attachment.get('status');
</ins><span class="cx">                   }
</span><span class="cx">          }
</span><span class="cx">  });
</span><span class="lines">@@ -1144,7 +1163,8 @@
</span><span class="cx">                  'type':      'post_mime_type',
</span><span class="cx">                  'perPage':   'posts_per_page',
</span><span class="cx">                  'menuOrder': 'menu_order',
</span><del>-                       'uploadedTo': 'post_parent'
</del><ins>+                        'uploadedTo': 'post_parent',
+                       'status':     'post_status'
</ins><span class="cx">           },
</span><span class="cx">          /**
</span><span class="cx">           * @static
</span><span class="lines">@@ -1169,11 +1189,13 @@
</span><span class="cx">                          var args     = {},
</span><span class="cx">                                  orderby  = Query.orderby,
</span><span class="cx">                                  defaults = Query.defaultProps,
</span><del>-                                       query;
</del><ins>+                                        query,
+                                       cache    = !! props.cache;
</ins><span class="cx"> 
</span><span class="cx">                          // Remove the `query` property. This isn't linked to a query,
</span><span class="cx">                          // this *is* the query.
</span><span class="cx">                          delete props.query;
</span><ins>+                               delete props.cache;
</ins><span class="cx"> 
</span><span class="cx">                          // Fill default args.
</span><span class="cx">                          _.defaults( props, defaults );
</span><span class="lines">@@ -1207,9 +1229,13 @@
</span><span class="cx">                          args.orderby = orderby.valuemap[ props.orderby ] || props.orderby;
</span><span class="cx"> 
</span><span class="cx">                          // Search the query cache for matches.
</span><del>-                               query = _.find( queries, function( query ) {
-                                       return _.isEqual( query.args, args );
-                               });
</del><ins>+                                if ( cache ) {
+                                       query = _.find( queries, function( query ) {
+                                               return _.isEqual( query.args, args );
+                                       });
+                               } else {
+                                       queries = [];
+                               }
</ins><span class="cx"> 
</span><span class="cx">                          // Otherwise, create a new query and add it to the cache.
</span><span class="cx">                          if ( ! query ) {
</span></span></pre></div>
<a id="trunksrcwpincludesjsmediaviewsjs"></a>
<div class="modfile"><h4>Modified: trunk/src/wp-includes/js/media-views.js (29489 => 29490)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/src/wp-includes/js/media-views.js  2014-08-14 07:17:27 UTC (rev 29489)
+++ trunk/src/wp-includes/js/media-views.js     2014-08-14 18:30:49 UTC (rev 29490)
</span><span class="lines">@@ -5671,6 +5671,7 @@
</span><span class="cx">                          filters[ key ] = {
</span><span class="cx">                                  text: text,
</span><span class="cx">                                  props: {
</span><ins>+                                               status:  null,
</ins><span class="cx">                                           type:    key,
</span><span class="cx">                                          uploadedTo: null,
</span><span class="cx">                                          orderby: 'date',
</span><span class="lines">@@ -5682,6 +5683,7 @@
</span><span class="cx">                  filters.all = {
</span><span class="cx">                          text:  l10n.allMediaItems,
</span><span class="cx">                          props: {
</span><ins>+                                       status:  null,
</ins><span class="cx">                                   type:    null,
</span><span class="cx">                                  uploadedTo: null,
</span><span class="cx">                                  orderby: 'date',
</span><span class="lines">@@ -5694,6 +5696,7 @@
</span><span class="cx">                          filters.uploaded = {
</span><span class="cx">                                  text:  l10n.uploadedToThisPost,
</span><span class="cx">                                  props: {
</span><ins>+                                               status:  null,
</ins><span class="cx">                                           type:    null,
</span><span class="cx">                                          uploadedTo: media.view.settings.post.id,
</span><span class="cx">                                          orderby: 'menuOrder',
</span><span class="lines">@@ -5706,6 +5709,7 @@
</span><span class="cx">                  filters.unattached = {
</span><span class="cx">                          text:  l10n.unattached,
</span><span class="cx">                          props: {
</span><ins>+                                       status:     null,
</ins><span class="cx">                                   uploadedTo: 0,
</span><span class="cx">                                  type:       null,
</span><span class="cx">                                  orderby:    'menuOrder',
</span><span class="lines">@@ -5714,6 +5718,20 @@
</span><span class="cx">                          priority: 50
</span><span class="cx">                  };
</span><span class="cx"> 
</span><ins>+                       if ( media.view.settings.mediaTrash ) {
+                               filters.trash = {
+                                       text:  l10n.trash,
+                                       props: {
+                                               uploadedTo: null,
+                                               status:     'trash',
+                                               type:       null,
+                                               orderby:    'date',
+                                               order:      'DESC'
+                                       },
+                                       priority: 50
+                               };
+                       }
+
</ins><span class="cx">                   this.filters = filters;
</span><span class="cx">          }
</span><span class="cx">  });
</span><span class="lines">@@ -5765,9 +5783,7 @@
</span><span class="cx">          },
</span><span class="cx"> 
</span><span class="cx">          createToolbar: function() {
</span><del>-                       var filters,
-                               LibraryViewSwitcher,
-                               FiltersConstructor;
</del><ins>+                        var LibraryViewSwitcher, Filters;
</ins><span class="cx"> 
</span><span class="cx">                  /**
</span><span class="cx">                   * @member {wp.media.view.Toolbar}
</span><span class="lines">@@ -5778,6 +5794,38 @@
</span><span class="cx"> 
</span><span class="cx">                  this.views.add( this.toolbar );
</span><span class="cx"> 
</span><ins>+                       this.toolbar.set( 'spinner', new media.view.Spinner({
+                               priority: -60
+                       }) );
+
+                       if ( -1 !== $.inArray( this.options.filters, [ 'uploaded', 'all' ] ) ) {
+                               // "Filters" will return a <select>, need to render
+                               // screen reader text before
+                               this.toolbar.set( 'filtersLabel', new media.view.Label({
+                                       value: l10n.filterByType,
+                                       attributes: {
+                                               'for':  'media-attachment-filters'
+                                       },
+                                       priority:   -80
+                               }).render() );
+
+                               if ( 'uploaded' === this.options.filters ) {
+                                       this.toolbar.set( 'filters', new media.view.AttachmentFilters.Uploaded({
+                                               controller: this.controller,
+                                               model:      this.collection.props,
+                                               priority:   -80
+                                       }).render() );
+                               } else {
+                                       Filters = new media.view.AttachmentFilters.All({
+                                               controller: this.controller,
+                                               model:      this.collection.props,
+                                               priority:   -80
+                                       });
+
+                                       this.toolbar.set( 'filters', Filters.render() );
+                               }
+                       }
+
</ins><span class="cx">                   // Feels odd to bring the global media library switcher into the Attachment
</span><span class="cx">                  // browser view. Is this a use case for doAction( 'add:toolbar-items:attachments-browser', this.toolbar );
</span><span class="cx">                  // which the controller can tap into and add this view?
</span><span class="lines">@@ -5814,47 +5862,41 @@
</span><span class="cx">                          }).render() );
</span><span class="cx"> 
</span><span class="cx">                          this.toolbar.set( 'deleteSelectedButton', new media.view.DeleteSelectedButton({
</span><ins>+                                       filters: Filters,
</ins><span class="cx">                                   style: 'primary',
</span><span class="cx">                                  disabled: true,
</span><del>-                                       text:  l10n.deleteSelected,
</del><ins>+                                        text: media.view.settings.mediaTrash ? l10n.trashSelected : l10n.deleteSelected,
</ins><span class="cx">                                   controller: this.controller,
</span><span class="cx">                                  priority: -60,
</span><span class="cx">                                  click: function() {
</span><del>-                                               while ( this.controller.state().get( 'selection' ).length > 0 ) {
-                                                       this.controller.state().get( 'selection' ).at( 0 ).destroy();
</del><ins>+                                                var model, changed = [],
+                                                       selection = this.controller.state().get( 'selection' ),
+                                                       library = this.controller.state().get( 'library' );
+
+                                               while ( selection.length > 0 ) {
+                                                       model = selection.at( 0 );
+                                                       if ( media.view.settings.mediaTrash && 'trash' === model.get( 'status' ) ) {
+                                                               model.set( 'status', 'inherit' );
+                                                               changed.push( model.save() );
+                                                               selection.remove( model );
+                                                       } else if ( media.view.settings.mediaTrash ) {
+                                                               model.set( 'status', 'trash' );
+                                                               changed.push( model.save() );
+                                                               selection.remove( model );
+                                                       } else {
+                                                               model.destroy();
+                                                       }
</ins><span class="cx">                                           }
</span><ins>+
+                                               if ( changed.length ) {
+                                                       $.when( changed ).then( function() {
+                                                               library._requery( true );
+                                                       } );
+                                               }
</ins><span class="cx">                                   }
</span><span class="cx">                          }).render() );
</span><span class="cx">                  }
</span><span class="cx"> 
</span><del>-                       this.toolbar.set( 'spinner', new media.view.Spinner({
-                               priority: -60
-                       }) );
-
-                       filters = this.options.filters;
-                       if ( 'uploaded' === filters ) {
-                               FiltersConstructor = media.view.AttachmentFilters.Uploaded;
-                       } else if ( 'all' === filters ) {
-                               FiltersConstructor = media.view.AttachmentFilters.All;
-                       }
-
-                       if ( FiltersConstructor ) {
-                               // "FiltersConstructor" will return a <select>, need to render
-                               // screen reader text before
-                               this.toolbar.set( 'filtersLabel', new media.view.Label({
-                                       value: l10n.filterByType,
-                                       attributes: {
-                                               'for':  'media-attachment-filters'
-                                       },
-                                       priority:   -80
-                               }).render() );
-                               this.toolbar.set( 'filters', new FiltersConstructor({
-                                       controller: this.controller,
-                                       model:      this.collection.props,
-                                       priority:   -80
-                               }).render() );
-                       }
-
</del><span class="cx">                   if ( this.options.search ) {
</span><span class="cx">                          // Search is an input, screen reader text needs to be rendered before
</span><span class="cx">                          this.toolbar.set( 'searchLabel', new media.view.Label({
</span><span class="lines">@@ -6420,6 +6462,7 @@
</span><span class="cx">                  'change [data-setting] textarea': 'updateSetting',
</span><span class="cx">                  'click .delete-attachment':       'deleteAttachment',
</span><span class="cx">                  'click .trash-attachment':        'trashAttachment',
</span><ins>+                       'click .untrash-attachment':      'untrashAttachment',
</ins><span class="cx">                   'click .edit-attachment':         'editAttachment',
</span><span class="cx">                  'click .refresh-attachment':      'refreshAttachment',
</span><span class="cx">                  'keydown':                        'toggleSelectionHandler'
</span><span class="lines">@@ -6453,13 +6496,33 @@
</span><span class="cx">           * @param {Object} event
</span><span class="cx">           */
</span><span class="cx">          trashAttachment: function( event ) {
</span><ins>+                       var library = this.controller.library;
</ins><span class="cx">                   event.preventDefault();
</span><span class="cx"> 
</span><del>-                       this.model.destroy();
</del><ins>+                        if ( media.view.settings.mediaTrash ) {
+                               this.model.set( 'status', 'trash' );
+                               this.model.save().done( function() {
+                                       library._requery( true );
+                               } );
+                       }  else {
+                               this.model.destroy();
+                       }
</ins><span class="cx">           },
</span><span class="cx">          /**
</span><span class="cx">           * @param {Object} event
</span><span class="cx">           */
</span><ins>+               untrashAttachment: function( event ) {
+                       var library = this.controller.library;
+                       event.preventDefault();
+
+                       this.model.set( 'status', 'inherit' );
+                       this.model.save().done( function() {
+                               library._requery( true );
+                       } );
+               },
+               /**
+                * @param {Object} event
+                */
</ins><span class="cx">           editAttachment: function( event ) {
</span><span class="cx">                  var editState = this.controller.states.get( 'edit-image' );
</span><span class="cx">                  if ( window.imageEdit && editState ) {
</span></span></pre></div>
<a id="trunksrcwpincludesmediatemplatephp"></a>
<div class="modfile"><h4>Modified: trunk/src/wp-includes/media-template.php (29489 => 29490)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/src/wp-includes/media-template.php 2014-08-14 07:17:27 UTC (rev 29489)
+++ trunk/src/wp-includes/media-template.php    2014-08-14 18:30:49 UTC (rev 29490)
</span><span class="lines">@@ -316,7 +316,11 @@
</span><span class="cx"> 
</span><span class="cx">                                  <# if ( ! data.uploading && data.can.remove ) { #>
</span><span class="cx">                                          <?php if ( MEDIA_TRASH ): ?>
</span><ins>+                                               <# if ( 'trash' === data.status ) { #>
+                                                       <a class="untrash-attachment" href="#"><?php _e( 'Untrash' ); ?></a>
+                                               <# } else { #>
</ins><span class="cx">                                                   <a class="trash-attachment" href="#"><?php _e( 'Trash' ); ?></a>
</span><ins>+                                               <# } #>
</ins><span class="cx">                                           <?php else: ?>
</span><span class="cx">                                                  <a class="delete-attachment" href="#"><?php _e( 'Delete Permanently' ); ?></a>
</span><span class="cx">                                          <?php endif; ?>
</span></span></pre></div>
<a id="trunksrcwpincludesmediaphp"></a>
<div class="modfile"><h4>Modified: trunk/src/wp-includes/media.php (29489 => 29490)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/src/wp-includes/media.php  2014-08-14 07:17:27 UTC (rev 29489)
+++ trunk/src/wp-includes/media.php     2014-08-14 18:30:49 UTC (rev 29490)
</span><span class="lines">@@ -2869,6 +2869,7 @@
</span><span class="cx">          'embedMimes'   => $ext_mimes,
</span><span class="cx">          'contentWidth' => $content_width,
</span><span class="cx">          'months'       => $months,
</span><ins>+               'mediaTrash'   => MEDIA_TRASH ? 1 : 0
</ins><span class="cx">   );
</span><span class="cx"> 
</span><span class="cx">  $post = null;
</span><span class="lines">@@ -2931,11 +2932,14 @@
</span><span class="cx">          'noItemsFound'           => __( 'No items found.' ),
</span><span class="cx">          'insertIntoPost'         => $hier ? __( 'Insert into page' ) : __( 'Insert into post' ),
</span><span class="cx">          'unattached'             => __( 'Unattached' ),
</span><ins>+               'trash'                  => __( 'Trash' ),
</ins><span class="cx">           'uploadedToThisPost'     => $hier ? __( 'Uploaded to this page' ) : __( 'Uploaded to this post' ),
</span><span class="cx">          'warnDelete'             => __( "You are about to permanently delete this item.\n  'Cancel' to stop, 'OK' to delete." ),
</span><span class="cx">          'warnBulkDelete'         => __( "You are about to permanently delete these items.\n  'Cancel' to stop, 'OK' to delete." ),
</span><span class="cx">          'bulkSelect'             => __( 'Bulk Select' ),
</span><span class="cx">          'cancelSelection'        => __( 'Cancel Selection' ),
</span><ins>+               'trashSelected'          => __( 'Trash Selected' ),
+               'untrashSelected'        => __( 'Untrash Selected' ),
</ins><span class="cx">           'deleteSelected'         => __( 'Delete Selected' ),
</span><span class="cx">          'deletePermanently'      => __( 'Delete Permanently' ),
</span><span class="cx">          'apply'                  => __( 'Apply' ),
</span></span></pre>
</div>
</div>

</body>
</html>