WordPress.org

Make WordPress Core

Ticket #24716: 24716.11.diff

File 24716.11.diff, 22.3 KB (added by ericlewis, 3 years ago)
  • src/wp-admin/upload.php

    diff --git a/src/wp-admin/upload.php b/src/wp-admin/upload.php
    index 11175f9..de099c7 100644
    a b if ( 'grid' === $mode ) { 
    2424        wp_enqueue_media(); 
    2525        wp_enqueue_script( 'media' ); 
    2626        require_once( ABSPATH . 'wp-admin/admin-header.php' ); 
    27         ?><div class="view-switch media-grid-view-switch"> 
    28                 <a href="<?php echo esc_url( add_query_arg( 'mode', 'list', $_SERVER['REQUEST_URI'] ) ) ?>" class="view-list"> 
    29                         <img id="view-switch-list" src="<?php echo includes_url( 'images/blank.gif' ) ?>" width="20" height="20" title="List View" alt="List View"/> 
    30                 </a> 
    31                 <a href="<?php echo esc_url( add_query_arg( 'mode', 'grid', $_SERVER['REQUEST_URI'] ) ) ?>" class="view-grid current"> 
    32                         <img id="view-switch-excerpt" src="<?php echo includes_url( 'images/blank.gif' ) ?>" width="20" height="20" title="Grid View" alt="Grid View"/> 
    33                 </a> 
    34         </div><?php 
    3527        include( ABSPATH . 'wp-admin/admin-footer.php' ); 
    3628        exit; 
    3729} 
  • 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 c768479..59e1113 100644
    a b  
    909909        border-radius: 0; 
    910910} 
    911911 
     912.attachment .data-field { 
     913        white-space: nowrap; 
     914        text-overflow: ellipsis; 
     915        overflow: hidden; 
     916        display: block; 
     917        line-height: 19px; 
     918        height: 19px; 
     919        text-align: left; 
     920} 
     921 
    912922/** 
    913923 * Attachments Browser 
    914924 */ 
     
    924934        height: 50px; 
    925935} 
    926936 
     937.attachments-browser.hide-sidebar .media-toolbar { 
     938        right: 0; 
     939} 
     940 
    927941.attachments-browser .media-toolbar-primary > .media-button, 
    928942.attachments-browser .media-toolbar-primary > .media-button-group, 
    929943.attachments-browser .media-toolbar-secondary > .media-button, 
     
    942956        outline: none; 
    943957} 
    944958 
     959/** 
     960 * Copied styles from the theme browser view. 
     961 * 
     962 * This should be OOCSS'd so both use a shared selector. 
     963 */ 
     964.attachment .edit-media { 
     965        -ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=0)"; 
     966        opacity: 0; 
     967        position: absolute; 
     968        top: 25%; 
     969        right: 25%; 
     970        left: 25%; 
     971        background: #222; 
     972        background: rgba(0,0,0,0.7); 
     973        color: #fff; 
     974        font-size: 15px; 
     975        text-shadow: 0 1px 0 rgba(0,0,0,0.6); 
     976        -webkit-font-smoothing: antialiased; 
     977        font-weight: 600; 
     978        padding: 10px 0; 
     979        text-align: center; 
     980        -webkit-border-radius: 3px; 
     981        border-radius: 3px; 
     982        -webkit-transition: opacity 0.1s ease-in-out; 
     983        transition: opacity 0.1s ease-in-out; 
     984} 
     985 
     986.attachment:hover .edit-media { 
     987        -ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=100)"; 
     988        opacity: 1; 
     989} 
     990 
     991.attachments-browser.hide-sidebar .attachments { 
     992        right: 0; 
     993} 
     994 
    945995.attachments-browser .instructions { 
    946996        display: inline-block; 
    947997        margin-top: 16px; 
     
    23882438        line-height: 29px; 
    23892439} 
    23902440 
    2391 .media-grid-view-switch { 
    2392         position: fixed; 
    2393         right: 10px; 
    2394         top: 44px; 
    2395         z-index: 300; 
     2441.media-grid-view .view-switch { 
     2442        display: inline-block; 
     2443        float: none; 
     2444        margin-top: 13px; 
     2445        vertical-align: middle; 
    23962446} 
    23972447 
    23982448/** 
     
    24272477        display: none; 
    24282478} 
    24292479 
     2480/** 
     2481 * Copied styles from the Add theme toolbar. 
     2482 * 
     2483 * This should be OOCSS'd so both use a shared selector. 
     2484 */ 
     2485.media-grid-view .media-toolbar { 
     2486        background: #fff; 
     2487        -webkit-box-shadow: 0 1px 1px 0 rgba(0,0,0,.1); 
     2488        box-shadow: 0 1px 1px 0 rgba(0,0,0,.1); 
     2489        -webkit-box-sizing: border-box; 
     2490        -moz-box-sizing: border-box; 
     2491        box-sizing: border-box; 
     2492        color: #555; 
     2493        display: inline-block; 
     2494        font-size: 13px; 
     2495        padding: 0 20px; 
     2496        position: relative; 
     2497        width: 100%; 
     2498} 
     2499 
     2500.media-grid-view.hide-router .media-frame-title { 
     2501        box-shadow: none; 
     2502} 
     2503 
    24302504.media-grid-view .media-frame-content { 
     2505        background-color: transparent; 
    24312506        bottom: 40px; 
    24322507} 
    24332508@media screen and (max-width: 782px) { 
  • src/wp-includes/js/media-views.js

    diff --git a/src/wp-includes/js/media-views.js b/src/wp-includes/js/media-views.js
    index c1407d3..51d1a0a 100644
    a b  
    337337        }); 
    338338 
    339339        /** 
     340         * A more abstracted state, because media.controller.State expects 
     341         * specific regions (menu, title, etc.) to exist on the frame, which do not 
     342         * exist in media.view.Frame.EditAttachment. 
     343         */ 
     344        media.controller._State = Backbone.Model.extend({ 
     345                constructor: function() { 
     346                        this.on( 'activate', this._preActivate, this ); 
     347                        this.on( 'activate', this.activate, this ); 
     348                        this.on( 'activate', this._postActivate, this ); 
     349                        this.on( 'deactivate', this._deactivate, this ); 
     350                        this.on( 'deactivate', this.deactivate, this ); 
     351                        this.on( 'reset', this.reset, this ); 
     352                        this.on( 'ready', this.ready, this ); 
     353                        /** 
     354                         * Call parent constructor with passed arguments 
     355                         */ 
     356                        Backbone.Model.apply( this, arguments ); 
     357                }, 
     358 
     359                /** 
     360                 * @abstract 
     361                 */ 
     362                ready: function() {}, 
     363                /** 
     364                 * @abstract 
     365                 */ 
     366                activate: function() {}, 
     367                /** 
     368                 * @abstract 
     369                 */ 
     370                deactivate: function() {}, 
     371                /** 
     372                 * @abstract 
     373                 */ 
     374                reset: function() {}, 
     375                /** 
     376                 * @access private 
     377                 */ 
     378                _preActivate: function() { 
     379                        this.active = true; 
     380                }, 
     381                /** 
     382                 * @access private 
     383                 */ 
     384                _postActivate: function() {}, 
     385                /** 
     386                 * @access private 
     387                 */ 
     388                _deactivate: function() { 
     389                        this.active = false; 
     390                } 
     391        }); 
     392 
     393        /** 
    340394         * wp.media.controller.State 
    341395         * 
    342396         * A state is a step in a workflow that when set will trigger the controllers 
     
    13501404        }); 
    13511405 
    13521406        /** 
     1407         * A state for editing (cropping, etc.) an image. 
     1408         * 
     1409         * @constructor 
     1410         * @augments wp.media.controller.State 
     1411         * @augments Backbone.Model 
     1412         */ 
     1413        media.controller.EditImageNoFrame = media.controller._State.extend({ 
     1414                defaults: { 
     1415                        id:      'edit-attachment', 
     1416                        title:   l10n.editImage, 
     1417                        // Region mode defaults. 
     1418                        menu:    false, 
     1419                        router:  'edit-metadata', 
     1420                        content: 'edit-metadata', 
     1421                        toolbar: 'toolbar', 
     1422 
     1423                        url:     '' 
     1424                }, 
     1425 
     1426                initialize: function() { 
     1427                        media.controller._State.prototype.initialize.apply( this, arguments ); 
     1428                }, 
     1429 
     1430                activate: function() { 
     1431                        this.listenTo( this.frame, 'toolbar:render:edit-image', this.toolbar ); 
     1432                }, 
     1433 
     1434                _postActivate: function() { 
     1435                        this._content(); 
     1436                        this._router(); 
     1437                }, 
     1438 
     1439                deactivate: function() { 
     1440                        this.stopListening( this.frame ); 
     1441                }, 
     1442 
     1443                toolbar: function() { 
     1444                        var frame = this.frame, 
     1445                                lastState = frame.lastState(), 
     1446                                previous = lastState && lastState.id; 
     1447 
     1448                        frame.toolbar.set( new media.view.Toolbar({ 
     1449                                controller: frame, 
     1450                                items: { 
     1451                                        back: { 
     1452                                                style: 'primary', 
     1453                                                text:     l10n.back, 
     1454                                                priority: 20, 
     1455                                                click:    function() { 
     1456                                                        if ( previous ) { 
     1457                                                                frame.setState( previous ); 
     1458                                                        } else { 
     1459                                                                frame.close(); 
     1460                                                        } 
     1461                                                } 
     1462                                        } 
     1463                                } 
     1464                        }) ); 
     1465                }, 
     1466 
     1467                /** 
     1468                 * @access private 
     1469                 */ 
     1470                _router: function() { 
     1471                        var router = this.frame.router, 
     1472                                mode = this.get('router'), 
     1473                                view; 
     1474 
     1475                        this.frame.$el.toggleClass( 'hide-router', ! mode ); 
     1476                        if ( ! mode ) { 
     1477                                return; 
     1478                        } 
     1479 
     1480                        this.frame.router.render( mode ); 
     1481 
     1482                        view = router.get(); 
     1483                        if ( view && view.select ) { 
     1484                                view.select( this.frame.content.mode() ); 
     1485                        } 
     1486                }, 
     1487 
     1488                _content: function() { 
     1489                        var mode = this.get( 'content' ); 
     1490                        if ( mode ) { 
     1491                                this.frame[ 'content' ].render( mode ); 
     1492                        } 
     1493                } 
     1494        }); 
     1495 
     1496        /** 
    13531497         * wp.media.controller.MediaLibrary 
    13541498         * 
    13551499         * @constructor 
     
    17581902                        _.defaults( this.options, { 
    17591903                                title:    '', 
    17601904                                modal:    true, 
    1761                                 uploader: true 
     1905                                uploader: true, 
     1906                                mode:     ['select'] 
    17621907                        }); 
    17631908 
    17641909                        // Ensure core UI is enabled. 
     
    19812126                                library:   {}, 
    19822127                                multiple:  false, 
    19832128                                state:     'library', 
    1984                                 uploader:  true 
     2129                                uploader:  true, 
     2130                                mode:      [ 'grid', 'edit' ] 
    19852131                        }); 
    19862132 
    19872133                        // Ensure core and media grid view UI is enabled. 
     
    20562202                                        router:     false, 
    20572203                                        content:    'browse', 
    20582204                                        filterable: 'mime-types' 
    2059                                 }), 
    2060  
    2061                                 new media.controller.EditImage( { model: options.editImage } ) 
     2205                                }) 
    20622206                        ]); 
    20632207                }, 
    20642208 
    20652209                bindHandlers: function() { 
    20662210                        this.on( 'content:create:browse', this.browseContent, this ); 
    20672211                        this.on( 'content:render:edit-image', this.editImageContent, this ); 
     2212 
     2213                        // Handle a frame-level event for editing an attachment. 
     2214                        this.on( 'edit:attachment', this.editAttachment, this ); 
     2215                }, 
     2216 
     2217                /** 
     2218                 * Open the Edit Attachment modal. 
     2219                 */ 
     2220                editAttachment: function( model ) { 
     2221                        new media.view.Frame.EditAttachment({ model: model }); 
    20682222                }, 
    20692223 
    20702224                /** 
     
    20882242                                display:    state.get('displaySettings'), 
    20892243                                dragInfo:   state.get('dragInfo'), 
    20902244                                bulkEdit:   true, 
     2245                                sidebar:    false, 
    20912246 
    20922247                                suggestedWidth:  state.get('suggestedWidth'), 
    20932248                                suggestedHeight: state.get('suggestedHeight'), 
     
    47184873                                        compat:        false, 
    47194874                                        alt:           '', 
    47204875                                        description:   '' 
    4721                                 }); 
     4876                                }, this.options ); 
    47224877 
    47234878                        options.buttons  = this.buttons; 
    47244879                        options.describe = this.controller.state().get('describe'); 
     
    47684923                 */ 
    47694924                toggleSelectionHandler: function( event ) { 
    47704925                        var method; 
    4771  
    47724926                        // Catch enter and space events 
    47734927                        if ( 'keydown' === event.type && 13 !== event.keyCode && 32 !== event.keyCode ) { 
    47744928                                return; 
    47754929                        } 
     4930 
     4931                        // In the grid view, bubble up an edit:attachment event to the controller. 
     4932                        if ( _.contains( this.controller.options.mode, 'grid' ) ) { 
     4933                                this.controller.trigger( 'edit:attachment', this.model ); 
     4934                                return; 
     4935                        } 
     4936 
    47764937                        if ( event.shiftKey ) { 
    47774938                                method = 'between'; 
    47784939                        } else if ( event.ctrlKey || event.metaKey ) { 
     
    53035464                 */ 
    53045465                createAttachmentView: function( attachment ) { 
    53055466                        var view = new this.options.AttachmentView({ 
    5306                                 controller: this.controller, 
    5307                                 model:      attachment, 
    5308                                 collection: this.collection, 
    5309                                 selection:  this.options.selection 
     5467                                controller:           this.controller, 
     5468                                model:                attachment, 
     5469                                collection:           this.collection, 
     5470                                selection:            this.options.selection, 
     5471                                showAttachmentFields: this.options.showAttachmentFields 
    53105472                        }); 
    53115473 
    53125474                        return this._viewsByCid[ attachment.cid ] = view; 
     
    56035765                } 
    56045766        }); 
    56055767 
    5606  
    56075768        /** 
    56085769         * wp.media.view.AttachmentsBrowser 
    56095770         * 
     
    56215782                                filters: false, 
    56225783                                search:  true, 
    56235784                                display: false, 
    5624  
     5785                                sidebar: true, 
     5786                                showAttachmentFields: getUserSetting( 'showAttachmentFields', [ 'title', 'uploadedTo', 'dateFormatted', 'mime' ] ), 
    56255787                                AttachmentView: media.view.Attachment.Library 
    56265788                        }); 
    56275789 
    56285790                        this.createToolbar(); 
    56295791                        this.updateContent(); 
    5630                         this.createSidebar(); 
     5792                        if ( this.options.sidebar ) { 
     5793                                this.createSidebar(); 
     5794                        } else { 
     5795                                this.$el.addClass( 'hide-sidebar' ); 
     5796                        } 
    56315797 
    56325798                        this.collection.on( 'add remove reset', this.updateContent, this ); 
    56335799                }, 
     
    56525818 
    56535819                        this.views.add( this.toolbar ); 
    56545820 
     5821                        // Feels odd to bring the global media library switcher into the Attachment 
     5822                        // browser view. Is this a use case for doAction( 'add:toolbar-items:attachments-browser', this.toolbar ); 
     5823                        // which the controller can tap into and add this view? 
     5824                        if ( _.contains( this.controller.options.mode, 'grid' ) ) { 
     5825                                var libraryViewSwitcherConstructor = media.View.extend({ 
     5826                                        className: 'view-switch media-grid-view-switch', 
     5827                                        template: media.template( 'media-library-view-switcher') 
     5828                                }); 
     5829                                this.toolbar.set( 'libraryViewSwitcher', new libraryViewSwitcherConstructor({ 
     5830                                        controller: this.controller, 
     5831                                        priority: -90 
     5832                                }).render() ); 
     5833                        } 
     5834 
    56555835                        filters = this.options.filters; 
    56565836                        if ( 'uploaded' === filters ) { 
    56575837                                FiltersConstructor = media.view.AttachmentFilters.Uploaded; 
     
    57465926                        this.removeContent(); 
    57475927 
    57485928                        this.attachments = new media.view.Attachments({ 
    5749                                 controller: this.controller, 
    5750                                 collection: this.collection, 
    5751                                 selection:  this.options.selection, 
    5752                                 model:      this.model, 
    5753                                 sortable:   this.options.sortable, 
     5929                                controller:           this.controller, 
     5930                                collection:           this.collection, 
     5931                                selection:            this.options.selection, 
     5932                                model:                this.model, 
     5933                                sortable:             this.options.sortable, 
     5934                                showAttachmentFields: this.options.showAttachmentFields, 
    57545935 
    57555936                                // The single `Attachment` view to be used in the `Attachments` view. 
    57565937                                AttachmentView: this.options.AttachmentView 
     
    68146995                } 
    68156996        }); 
    68166997 
     6998        media.view.Attachment.Details.TwoColumn = media.view.Attachment.Details.extend({ 
     6999                template: wp.template( 'attachment-details-two-column' ), 
     7000 
     7001                initialize: function() { 
     7002                        var selection = this.options.selection; 
     7003 
     7004                        this.$el.attr('aria-label', this.model.attributes.title).attr('aria-checked', false); 
     7005                        this.model.on( 'change:sizes change:uploading', this.render, this ); 
     7006                        this.model.on( 'change:title', this._syncTitle, this ); 
     7007                        this.model.on( 'change:caption', this._syncCaption, this ); 
     7008                        this.model.on( 'change:percent', this.progress, this ); 
     7009 
     7010                        // Update the selection. 
     7011                        this.model.on( 'add', this.select, this ); 
     7012                        this.model.on( 'remove', this.deselect, this ); 
     7013                }, 
     7014        }); 
     7015        /** 
     7016         * A frame for editing the details of a specific media item. 
     7017         * 
     7018         * Pops open in a modal by default. 
     7019         */ 
     7020        media.view.Frame.EditAttachment = media.view.Frame.extend({ 
     7021 
     7022                className: 'edit-media-overlay', 
     7023                template: media.template( 'edit-attachment-frame' ), 
     7024                regions:   [ 'router', 'content' ], 
     7025 
     7026                events: { 
     7027                        'click':                    'collapse', 
     7028                        'click .delete-media-item': 'deleteMediaItem', 
     7029                        'click .left':              'previousMediaItem', 
     7030                        'click .right':             'nextMediaItem' 
     7031                }, 
     7032 
     7033                initialize: function( options ) { 
     7034                        var self = this; 
     7035                        media.view.Frame.prototype.initialize.apply( this, arguments ); 
     7036 
     7037                        _.defaults( this.options, { 
     7038                                modal:    true, 
     7039                                state: 'edit-attachment' 
     7040                        }); 
     7041 
     7042                        this.createStates(); 
     7043 
     7044                        this.on( 'content:render:edit-metadata', this.editMetadata, this ); 
     7045                        this.on( 'content:render:edit-image', this.editImageContentUgh, this ); 
     7046 
     7047                        // Only need a tab to Edit Image for images. 
     7048                        if ( this.model.get( 'type' ) === 'image' ) { 
     7049                                this.on( 'router:create', this.createRouter, this ); 
     7050                                this.on( 'router:render', this.browseRouter, this ); 
     7051                        } 
     7052 
     7053                        // Initialize modal container view. 
     7054                        if ( this.options.modal ) { 
     7055                                this.modal = new media.view.Modal({ 
     7056                                        controller: this, 
     7057                                        title:      this.options.title 
     7058                                }); 
     7059 
     7060                                // Completely destroy the modal DOM element when closing it. 
     7061                                this.modal.close = function() { 
     7062                                        self.modal.remove(); 
     7063                                }; 
     7064 
     7065                                this.modal.content( this ); 
     7066                                this.modal.open(); 
     7067                        } 
     7068                }, 
     7069 
     7070                // Add the default states to the frame. 
     7071                createStates: function() { 
     7072                        this.states.add([ 
     7073                                new media.controller.EditImageNoFrame( { model: this.model } ) 
     7074                        ]); 
     7075                }, 
     7076 
     7077                /** 
     7078                 * @returns {wp.media.view.MediaFrame} Returns itself to allow chaining 
     7079                 */ 
     7080                render: function() { 
     7081                        // Activate the default state if no active state exists. 
     7082                        if ( ! this.state() && this.options.state ) { 
     7083                                this.setState( this.options.state ); 
     7084                        } 
     7085                        /** 
     7086                         * call 'render' directly on the parent class 
     7087                         */ 
     7088                        return media.view.Frame.prototype.render.apply( this, arguments ); 
     7089                }, 
     7090 
     7091                editMetadata: function() { 
     7092                        var view = new media.view.Attachment.Details.TwoColumn({ 
     7093                                controller: this, 
     7094                                model:      this.model 
     7095                        }); 
     7096                        this.content.set( view ); 
     7097                }, 
     7098 
     7099                /** 
     7100                 * For some reason the view doesn't exist in the DOM yet, don't have the 
     7101                 * patience to track this down right now. 
     7102                 */ 
     7103                editImageContentUgh: function() { 
     7104                        _.defer( _.bind( this.editImageContent, this ) ); 
     7105                }, 
     7106 
     7107                /** 
     7108                 * Render the EditImage view into the frame's content region. 
     7109                 */ 
     7110                editImageContent: function() { 
     7111                        var view = new media.view.EditImage( { model: this.model, controller: this } ).render(); 
     7112 
     7113                        this.content.set( view ); 
     7114 
     7115                        // after creating the wrapper view, load the actual editor via an ajax call 
     7116                        view.loadEditor(); 
     7117                }, 
     7118 
     7119                /** 
     7120                 * @param {Object} router 
     7121                 * @this wp.media.controller.Region 
     7122                 */ 
     7123                createRouter: function( router ) { 
     7124                        router.view = new media.view.Router({ 
     7125                                controller: this 
     7126                        }); 
     7127                }, 
     7128 
     7129                browseRouter: function( view ) { 
     7130                        view.set({ 
     7131                                'edit-metadata': { 
     7132                                        text:     'Edit Metadata', 
     7133                                        priority: 20 
     7134                                }, 
     7135                                'edit-image': { 
     7136                                        text:     'Edit Image', 
     7137                                        priority: 40 
     7138                                } 
     7139                        }); 
     7140                } 
     7141 
     7142        }); 
     7143 
    68177144        media.view.EditImage = media.View.extend({ 
    68187145 
    68197146                className: 'image-editor', 
  • src/wp-includes/media-template.php

    diff --git a/src/wp-includes/media-template.php b/src/wp-includes/media-template.php
    index 2c0ff80..517e493 100644
    a b function wp_print_media_templates() { 
    220220                </div> 
    221221        </script> 
    222222 
     223        <script type="text/html" id="tmpl-media-library-view-switcher"> 
     224                <a href="<?php echo esc_url( add_query_arg( 'mode', 'list', $_SERVER['REQUEST_URI'] ) ) ?>" class="view-list"> 
     225                        <img id="view-switch-list" src="<?php echo includes_url( 'images/blank.gif' ) ?>" width="20" height="20" title="List View" alt="List View"/> 
     226                </a> 
     227                <a href="<?php echo esc_url( add_query_arg( 'mode', 'grid', $_SERVER['REQUEST_URI'] ) ) ?>" class="view-grid current"> 
     228                        <img id="view-switch-excerpt" src="<?php echo includes_url( 'images/blank.gif' ) ?>" width="20" height="20" title="Grid View" alt="Grid View"/> 
     229                </a> 
     230        </script> 
     231 
    223232        <script type="text/html" id="tmpl-uploader-status"> 
    224233                <h3><?php _e( 'Uploading' ); ?></h3> 
    225234                <a class="upload-dismiss-errors" href="#"><?php _e('Dismiss Errors'); ?></a> 
    function wp_print_media_templates() { 
    241250                <span class="upload-error-message">{{ data.message }}</span> 
    242251        </script> 
    243252 
     253        <script type="text/html" id="tmpl-edit-attachment-frame"> 
     254                <div class="edit-media-header"> 
     255                        <button class="left dashicons dashicons-no"><span class="screen-reader-text"><?php _e( 'Edit previous media item' ); ?></span></button> 
     256                        <button class="right dashicons dashicons-no"><span class="screen-reader-text"><?php _e( 'Edit next media item' ); ?></span></button> 
     257                </div> 
     258                <div class="media-frame-router"></div> 
     259                <div class="media-frame-content"></div> 
     260                <div class="media-frame-toolbar"></div> 
     261        </script> 
     262 
     263        <script type="text/html" id="tmpl-attachment-details-two-column"> 
     264                <h3> 
     265                        <?php _e('Attachment Details'); ?> 
     266 
     267                        <span class="settings-save-status"> 
     268                                <span class="spinner"></span> 
     269                                <span class="saved"><?php esc_html_e('Saved.'); ?></span> 
     270                        </span> 
     271                </h3> 
     272                <div class="attachment-info"> 
     273                        <div class="thumbnail thumbnail-{{ data.type }}"> 
     274                                <# if ( data.uploading ) { #> 
     275                                        <div class="media-progress-bar"><div></div></div> 
     276                                <# } else if ( 'image' === data.type ) { #> 
     277                                        <img src="{{ data.size.url }}" draggable="false" /> 
     278                                <# } else { #> 
     279                                        <img src="{{ data.icon }}" class="icon" draggable="false" /> 
     280                                <# } #> 
     281                        </div> 
     282                        <div class="details"> 
     283                                <div class="filename">{{ data.filename }}</div> 
     284                                <div class="uploaded">{{ data.dateFormatted }}</div> 
     285 
     286                                <div class="file-size">{{ data.filesizeHumanReadable }}</div> 
     287                                <# if ( 'image' === data.type && ! data.uploading ) { #> 
     288                                        <# if ( data.width && data.height ) { #> 
     289                                                <div class="dimensions">{{ data.width }} &times; {{ data.height }}</div> 
     290                                        <# } #> 
     291 
     292                                        <# if ( data.can.save ) { #> 
     293                                                <a class="edit-attachment" href="{{ data.editLink }}&amp;image-editor" target="_blank"><?php _e( 'Edit Image' ); ?></a> 
     294                                                <a class="refresh-attachment" href="#"><?php _e( 'Refresh' ); ?></a> 
     295                                        <# } #> 
     296                                <# } #> 
     297 
     298                                <# if ( data.fileLength ) { #> 
     299                                        <div class="file-length"><?php _e( 'Length:' ); ?> {{ data.fileLength }}</div> 
     300                                <# } #> 
     301 
     302                                <# if ( ! data.uploading && data.can.remove ) { #> 
     303                                        <?php if ( MEDIA_TRASH ): ?> 
     304                                                <a class="trash-attachment" href="#"><?php _e( 'Trash' ); ?></a> 
     305                                        <?php else: ?> 
     306                                                <a class="delete-attachment" href="#"><?php _e( 'Delete Permanently' ); ?></a> 
     307                                        <?php endif; ?> 
     308                                <# } #> 
     309 
     310                                <div class="compat-meta"> 
     311                                        <# if ( data.compat && data.compat.meta ) { #> 
     312                                                {{{ data.compat.meta }}} 
     313                                        <# } #> 
     314                                </div> 
     315                        </div> 
     316                </div> 
     317 
     318                <label class="setting" data-setting="url"> 
     319                        <span class="name"><?php _e('URL'); ?></span> 
     320                        <input type="text" value="{{ data.url }}" readonly /> 
     321                </label> 
     322                <# var maybeReadOnly = data.can.save || data.allowLocalEdits ? '' : 'readonly'; #> 
     323                <label class="setting" data-setting="title"> 
     324                        <span class="name"><?php _e('Title'); ?></span> 
     325                        <input type="text" value="{{ data.title }}" {{ maybeReadOnly }} /> 
     326                </label> 
     327                <label class="setting" data-setting="caption"> 
     328                        <span class="name"><?php _e('Caption'); ?></span> 
     329                        <textarea {{ maybeReadOnly }}>{{ data.caption }}</textarea> 
     330                </label> 
     331                <# if ( 'image' === data.type ) { #> 
     332                        <label class="setting" data-setting="alt"> 
     333                                <span class="name"><?php _e('Alt Text'); ?></span> 
     334                                <input type="text" value="{{ data.alt }}" {{ maybeReadOnly }} /> 
     335                        </label> 
     336                <# } #> 
     337                <label class="setting" data-setting="description"> 
     338                        <span class="name"><?php _e('Description'); ?></span> 
     339                        <textarea {{ maybeReadOnly }}>{{ data.description }}</textarea> 
     340                </label> 
     341                <label class="setting"> 
     342                                <span class="name"><?php _e( 'Uploaded By' ); ?></span> 
     343                                <span class="value">{{ data.authorName }}</span> 
     344                        </label> 
     345                <# if ( data.uploadedTo ) { #> 
     346                        <label class="setting"> 
     347                                <span class="name"><?php _e('Uploaded To'); ?></span> 
     348                                <span class="value"><a href="{{ data.uploadedToLink }}">{{ data.uploadedToTitle }}</a></span> 
     349                        </label> 
     350                <# } #> 
     351        </script> 
     352 
    244353        <script type="text/html" id="tmpl-attachment"> 
    245354                <div class="attachment-preview type-{{ data.type }} subtype-{{ data.subtype }} {{ data.orientation }}"> 
    246355                        <# if ( data.uploading ) { #> 
    function wp_print_media_templates() { 
    257366                                        <div>{{ data.filename }}</div> 
    258367                                </div> 
    259368                        <# } #> 
    260  
     369                        <# if ( _.contains( data.controller.options.mode, 'grid' ) ) { #> 
     370                                <span class="edit-media">Edit Media</span> 
     371                        <# } #> 
    261372                        <# if ( data.buttons.close ) { #> 
    262373                                <a class="close media-modal-icon" href="#" title="<?php esc_attr_e('Remove'); ?>"></a> 
    263374                        <# } #> 
    function wp_print_media_templates() { 
    283394                                        <# } #> {{ maybeReadOnly }} /> 
    284395                        <# } #> 
    285396                <# } #> 
     397                <# if ( _.contains( data.controller.options.mode, 'grid' ) ) { #> 
     398                        <# _.each( data.showAttachmentFields, function( field ) { #> 
     399                                <div class="data-field data-{{ field }}"> 
     400                                        <# if ( 'uploadedTo' === field ) { #> 
     401 
     402                                                <# if ( data[field] ) { #> 
     403                                                <?php _e( 'Uploaded To:' ) ?> 
     404                                                <# } else { #> 
     405                                                <?php _e( 'Unattached' ) ?> 
     406                                                <# } #> 
     407 
     408                                        <# } #> 
     409                                <# if ( data[field] ) { #> 
     410                                        &nbsp;{{ data[field] }} 
     411                                <# } #> 
     412                                </div> 
     413                        <# }); #> 
     414                <# } #> 
     415 
    286416        </script> 
    287417 
    288418        <script type="text/html" id="tmpl-attachment-details">