WordPress.org

Make WordPress Core

Ticket #24716: 24716.14.diff

File 24716.14.diff, 39.2 KB (added by wonderboymusic, 4 years ago)
  • src/wp-admin/upload.php

     
    2222
    2323if ( 'grid' === $mode ) {
    2424        wp_enqueue_media();
     25        wp_enqueue_script( 'media-grid' );
    2526        wp_enqueue_script( 'media' );
    2627        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
    3528        include( ABSPATH . 'wp-admin/admin-footer.php' );
    3629        exit;
    3730}
  • src/wp-includes/css/media-views.css

     
    750750        max-height: 100%;
    751751}
    752752
     753.attachment-preview.type-audio .thumbnail,
     754.attachment-preview.type-video .thumbnail {
     755        z-index: 1;
     756        margin: 5%;
     757        max-width: 90%;
     758        max-height: 90%;
     759}
     760
     761.media-frame-content .attachment-preview.type-audio .icon,
     762.media-frame-content .attachment-preview.type-video .icon {
     763        z-index: 2;
     764        background: #f1f1f1;
     765        position: relative;
     766        padding: 0;
     767        top: 15%;
     768        left: auto;
     769        right: auto;
     770}
     771
     772.attachment-preview.type-audio .filename,
     773.attachment-preview.type-video .filename {
     774        z-index: 3;
     775}
     776
    753777.attachment-preview .thumbnail:after {
    754778        content: '';
    755779        display: block;
     
    909933        border-radius: 0;
    910934}
    911935
     936.attachment .data-fields {
     937        margin: 5px 0 0;
     938}
     939
     940.attachment .data-field {
     941        white-space: nowrap;
     942        text-overflow: ellipsis;
     943        overflow: hidden;
     944        display: block;
     945        line-height: 19px;
     946        height: 19px;
     947        text-align: left;
     948        width: 90%;
     949        margin: 0 5%;
     950}
     951
    912952/**
    913953 * Attachments Browser
    914954 */
     
    924964        height: 50px;
    925965}
    926966
     967.attachments-browser.hide-sidebar .media-toolbar {
     968        right: 0;
     969}
     970
    927971.attachments-browser .media-toolbar-primary > .media-button,
    928972.attachments-browser .media-toolbar-primary > .media-button-group,
    929973.attachments-browser .media-toolbar-secondary > .media-button,
     
    942986        outline: none;
    943987}
    944988
     989/**
     990 * Copied styles from the theme browser view.
     991 *
     992 * This should be OOCSS'd so both use a shared selector.
     993 */
     994.attachment .edit-media {
     995        -ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=0)";
     996        opacity: 0;
     997        position: absolute;
     998        z-index: 4;
     999        top: 25%;
     1000        right: 25%;
     1001        left: 25%;
     1002        background: #222;
     1003        background: rgba(0,0,0,0.7);
     1004        color: #fff;
     1005        font-size: 15px;
     1006        text-shadow: 0 1px 0 rgba(0,0,0,0.6);
     1007        -webkit-font-smoothing: antialiased;
     1008        font-weight: 600;
     1009        padding: 10px 0;
     1010        text-align: center;
     1011        -webkit-border-radius: 3px;
     1012        border-radius: 3px;
     1013        -webkit-transition: opacity 0.1s ease-in-out;
     1014        transition: opacity 0.1s ease-in-out;
     1015}
     1016
     1017.attachment:hover .edit-media {
     1018        -ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=100)";
     1019        opacity: 1;
     1020}
     1021
     1022.attachments-browser.hide-sidebar .attachments {
     1023        right: 0;
     1024}
     1025
    9451026.attachments-browser .instructions {
    9461027        display: inline-block;
    9471028        margin-top: 16px;
     
    23882469        line-height: 29px;
    23892470}
    23902471
    2391 .media-grid-view-switch {
    2392         position: fixed;
    2393         right: 10px;
    2394         top: 44px;
    2395         z-index: 300;
     2472.media-grid-view .view-switch {
     2473        display: inline-block;
     2474        float: none;
     2475        margin-top: 13px;
     2476        vertical-align: middle;
    23962477}
    23972478
    23982479/**
     
    24272508        display: none;
    24282509}
    24292510
     2511/**
     2512 * Copied styles from the Add theme toolbar.
     2513 *
     2514 * This should be OOCSS'd so both use a shared selector.
     2515 */
     2516.media-grid-view .media-toolbar {
     2517        background: #fff;
     2518        -webkit-box-shadow: 0 1px 1px 0 rgba(0,0,0,.1);
     2519        box-shadow: 0 1px 1px 0 rgba(0,0,0,.1);
     2520        -webkit-box-sizing: border-box;
     2521        -moz-box-sizing: border-box;
     2522        box-sizing: border-box;
     2523        color: #555;
     2524        display: inline-block;
     2525        font-size: 13px;
     2526        padding: 0 20px;
     2527        position: relative;
     2528        width: 100%;
     2529}
     2530
     2531/**
     2532 * The left and right buttons are copied from the expanded theme details modal.
     2533 *
     2534 * This should be OOCSS'd so both use a shared selector.
     2535 */
     2536.edit-attachment-frame .edit-media-header .left,
     2537.edit-attachment-frame .edit-media-header .right {
     2538        cursor: pointer;
     2539        color: #777;
     2540        background-color: transparent;
     2541        height: 48px;
     2542        width: 54px;
     2543        float: left;
     2544        text-align: center;
     2545        border: 0;
     2546        border-right: 1px solid #ddd;
     2547}
     2548
     2549.edit-attachment-frame .edit-media-header .right:before,
     2550.edit-attachment-frame .edit-media-header .left:before {
     2551        font: normal 20px/50px 'dashicons' !important;
     2552        display: inline;
     2553        font-weight: 300;
     2554}
     2555
     2556
     2557.edit-attachment-frame .edit-media-header .left:before {
     2558        content: '\f340';
     2559}
     2560
     2561.edit-attachment-frame .edit-media-header .right:before {
     2562        content: '\f344';
     2563}
     2564
     2565.edit-attachment-frame .edit-media-header .left.disabled,
     2566.edit-attachment-frame .edit-media-header .right.disabled,
     2567.edit-attachment-frame .edit-media-header .left.disabled:hover,
     2568.edit-attachment-frame .edit-media-header .right.disabled:hover {
     2569        color: #ccc;
     2570        background: inherit;
     2571        cursor: inherit;
     2572}
     2573
     2574.edit-attachment-frame .edit-media-header .close:hover,
     2575.edit-attachment-frame .edit-media-header .right:hover,
     2576.edit-attachment-frame .edit-media-header .left:hover,
     2577.edit-attachment-frame .edit-media-header .close:focus,
     2578.edit-attachment-frame .edit-media-header .right:focus,
     2579.edit-attachment-frame .edit-media-header .left:focus {
     2580        background: #0074a2;
     2581        color: #fff;
     2582}
     2583
     2584.edit-attachment-frame .media-frame-content,
     2585.edit-attachment-frame .media-frame-router {
     2586        left: 0;
     2587}
     2588
     2589/* Hiding this for the moment instead of removing it from the template. */
     2590.edit-attachment-frame h3 {
     2591        display: none;
     2592}
     2593
     2594.edit-attachment-frame .attachment-details {
     2595        position: absolute;
     2596        overflow: auto;
     2597        top: 0;
     2598        bottom: 0;
     2599        right: 0;
     2600        left: 0;
     2601}
     2602
     2603.edit-attachment-frame .attachment-info {
     2604        border-bottom: 0;
     2605        border-right: 1px solid #ddd;
     2606        bottom: 0;
     2607        position: absolute;
     2608        top: 0;
     2609        left: 0;
     2610        margin-bottom: 0;
     2611        padding: 2% 4%;
     2612        right: 50%;
     2613}
     2614
     2615.edit-attachment-frame .attachment-info .thumbnail {
     2616        max-width: none;
     2617        max-height: none;
     2618}
     2619
     2620.edit-attachment-frame .attachment-info .thumbnail-image img {
     2621        margin: 0;
     2622}
     2623
     2624.edit-attachment-frame .attachment-info .thumbnail-image:after {
     2625        -webkit-box-shadow: none;
     2626                box-shadow: none;
     2627}
     2628
     2629.edit-attachment-frame .attachment-info .thumbnail img {
     2630        max-width: none;
     2631        max-height: 50%;
     2632}
     2633
     2634.edit-attachment-frame .attachment-info .details {
     2635        float: none;
     2636}
     2637
     2638.edit-attachment-frame .attachment-fields {
     2639        bottom: 0;
     2640        padding: 2% 4%;
     2641        position: absolute;
     2642        top: 0;
     2643        left: 50%;
     2644        right: 0;
     2645}
     2646
     2647.edit-attachment-frame .attachment-fields .setting {
     2648        display: block;
     2649        float: left;
     2650        width: 100%;
     2651        margin: 1px 0;
     2652}
     2653
     2654.edit-attachment-frame .attachment-fields .setting label {
     2655        display: block;
     2656}
     2657
     2658.edit-attachment-frame .attachment-fields .setting .link-to-custom {
     2659        margin: 3px 0;
     2660}
     2661
     2662.edit-attachment-frame .attachment-fields .setting .name {
     2663        min-width: 30%;
     2664        margin-right: 4%;
     2665        font-size: 12px;
     2666        text-align: right;
     2667}
     2668
     2669.edit-attachment-frame .attachment-fields .setting select {
     2670        max-width: 65%;
     2671}
     2672
     2673.edit-attachment-frame .attachment-fields .setting input[type="checkbox"],
     2674.edit-attachment-frame .attachment-fields .field input[type="checkbox"] {
     2675        width: 16px;
     2676        float: none;
     2677        margin: 8px 3px 0;
     2678        padding: 0;
     2679}
     2680
     2681.edit-attachment-frame .attachment-fields .setting span {
     2682        float: left;
     2683        min-height: 22px;
     2684        padding-top: 8px;
     2685        line-height: 16px;
     2686        font-weight: normal;
     2687        color: #666;
     2688}
     2689
     2690.edit-attachment-frame .attachment-fields .setting input[type="text"],
     2691.edit-attachment-frame .attachment-fields .setting input[type="password"],
     2692.edit-attachment-frame .attachment-fields .setting input[type="number"],
     2693.edit-attachment-frame .attachment-fields .setting input[type="search"],
     2694.edit-attachment-frame .attachment-fields .setting input[type="email"],
     2695.edit-attachment-frame .attachment-fields .setting input[type="url"],
     2696.edit-attachment-frame .attachment-fields .setting textarea,
     2697.edit-attachment-frame .attachment-fields .setting .value {
     2698        margin: 1px;
     2699        width: 65%;
     2700        float: right;
     2701        padding: 6px 8px;
     2702        -webkit-box-sizing: border-box;
     2703        -moz-box-sizing: border-box;
     2704        box-sizing: border-box;
     2705}
     2706
     2707.edit-attachment-frame .attachment-fields .setting textarea {
     2708        height: 62px;
     2709        resize: vertical;
     2710}
     2711
     2712.edit-attachment-frame .attachment-fields select {
     2713        margin-top: 3px;
     2714}
     2715
     2716.media-grid-view.hide-router .media-frame-title {
     2717        box-shadow: none;
     2718}
     2719
    24302720.media-grid-view .media-frame-content {
     2721        background-color: transparent;
    24312722        bottom: 40px;
    24322723}
    24332724@media screen and (max-width: 782px) {
  • src/wp-includes/js/media-grid.js

     
     1(function($, _, Backbone, wp) {
     2        var media = wp.media, l10n;
     3
     4        // Link any localized strings.
     5        if ( media.view.l10n ) {
     6                l10n = media.view.l10n;
     7        } else {
     8                l10n = media.view.l10n = typeof _wpMediaViewsL10n === 'undefined' ? {} : _wpMediaViewsL10n;
     9                delete l10n.settings;
     10        }
     11
     12        /**
     13         * A more abstracted state, because media.controller.State expects
     14         * specific regions (menu, title, etc.) to exist on the frame, which do not
     15         * exist in media.view.Frame.EditAttachment.
     16         */
     17        media.controller._State = Backbone.Model.extend({
     18                constructor: function() {
     19                        this.on( 'activate', this._preActivate, this );
     20                        this.on( 'activate', this.activate, this );
     21                        this.on( 'activate', this._postActivate, this );
     22                        this.on( 'deactivate', this._deactivate, this );
     23                        this.on( 'deactivate', this.deactivate, this );
     24                        this.on( 'reset', this.reset, this );
     25                        this.on( 'ready', this.ready, this );
     26                        /**
     27                         * Call parent constructor with passed arguments
     28                         */
     29                        Backbone.Model.apply( this, arguments );
     30                },
     31
     32                /**
     33                 * @abstract
     34                 */
     35                ready: function() {},
     36                /**
     37                 * @abstract
     38                 */
     39                activate: function() {},
     40                /**
     41                 * @abstract
     42                 */
     43                deactivate: function() {},
     44                /**
     45                 * @abstract
     46                 */
     47                reset: function() {},
     48                /**
     49                 * @access private
     50                 */
     51                _preActivate: function() {
     52                        this.active = true;
     53                },
     54                /**
     55                 * @access private
     56                 */
     57                _postActivate: function() {},
     58                /**
     59                 * @access private
     60                 */
     61                _deactivate: function() {
     62                        this.active = false;
     63                }
     64        });
     65
     66        /**
     67         * A state for editing (cropping, etc.) an image.
     68         *
     69         * @constructor
     70         * @augments wp.media.controller.State
     71         * @augments Backbone.Model
     72         */
     73        media.controller.EditImageNoFrame = media.controller._State.extend({
     74                defaults: {
     75                        id:      'edit-attachment',
     76                        title:   l10n.editImage,
     77                        // Region mode defaults.
     78                        menu:    false,
     79                        router:  'edit-metadata',
     80                        content: 'edit-metadata',
     81                        toolbar: 'toolbar',
     82
     83                        url:     ''
     84                },
     85
     86                initialize: function() {
     87                        media.controller._State.prototype.initialize.apply( this, arguments );
     88                },
     89
     90                activate: function() {
     91                        this.listenTo( this.frame, 'toolbar:render:edit-image', this.toolbar );
     92                },
     93
     94                _postActivate: function() {
     95                        this._content();
     96                        this._router();
     97                },
     98
     99                deactivate: function() {
     100                        this.stopListening( this.frame );
     101                },
     102
     103                toolbar: function() {
     104                        var frame = this.frame,
     105                                lastState = frame.lastState(),
     106                                previous = lastState && lastState.id;
     107
     108                        frame.toolbar.set( new media.view.Toolbar({
     109                                controller: frame,
     110                                items: {
     111                                        back: {
     112                                                style: 'primary',
     113                                                text:     l10n.back,
     114                                                priority: 20,
     115                                                click:    function() {
     116                                                        if ( previous ) {
     117                                                                frame.setState( previous );
     118                                                        } else {
     119                                                                frame.close();
     120                                                        }
     121                                                }
     122                                        }
     123                                }
     124                        }) );
     125                },
     126
     127                /**
     128                 * @access private
     129                 */
     130                _router: function() {
     131                        var router = this.frame.router,
     132                                mode = this.get('router'),
     133                                view;
     134
     135                        this.frame.$el.toggleClass( 'hide-router', ! mode );
     136                        if ( ! mode ) {
     137                                return;
     138                        }
     139
     140                        this.frame.router.render( mode );
     141
     142                        view = router.get();
     143                        if ( view && view.select ) {
     144                                view.select( this.frame.content.mode() );
     145                        }
     146                },
     147
     148                _content: function() {
     149                        var mode = this.get( 'content' );
     150                        if ( mode ) {
     151                                this.frame[ 'content' ].render( mode );
     152                        }
     153                }
     154        });
     155
     156        /**
     157         * wp.media.view.MediaFrame.Manage
     158         *
     159         * A generic management frame workflow.
     160         *
     161         * Used in the media grid view.
     162         *
     163         * @constructor
     164         * @augments wp.media.view.MediaFrame
     165         * @augments wp.media.view.Frame
     166         * @augments wp.media.View
     167         * @augments wp.Backbone.View
     168         * @augments Backbone.View
     169         * @mixes wp.media.controller.StateMachine
     170         */
     171        media.view.MediaFrame.Manage = media.view.MediaFrame.extend({
     172                /**
     173                 * @global wp.Uploader
     174                 */
     175                initialize: function() {
     176                        _.defaults( this.options, {
     177                                title:     l10n.mediaLibraryTitle,
     178                                modal:     false,
     179                                selection: [],
     180                                library:   {},
     181                                multiple:  false,
     182                                state:     'library',
     183                                uploader:  true,
     184                                mode:      [ 'grid', 'edit' ]
     185                        });
     186
     187                        // Ensure core and media grid view UI is enabled.
     188                        this.$el.addClass('wp-core-ui media-grid-view');
     189
     190                        // Force the uploader off if the upload limit has been exceeded or
     191                        // if the browser isn't supported.
     192                        if ( wp.Uploader.limitExceeded || ! wp.Uploader.browser.supported ) {
     193                                this.options.uploader = false;
     194                        }
     195
     196                        // Initialize a window-wide uploader.
     197                        if ( this.options.uploader ) {
     198                                this.uploader = new media.view.UploaderWindow({
     199                                        controller: this,
     200                                        uploader: {
     201                                                dropzone:  $('body'),
     202                                                container: $('body')
     203                                        }
     204                                }).render();
     205                                this.uploader.ready();
     206                                $('body').append( this.uploader.el );
     207
     208                                this.options.uploader = false;
     209                        }
     210
     211                        /**
     212                         * call 'initialize' directly on the parent class
     213                         */
     214                        media.view.MediaFrame.prototype.initialize.apply( this, arguments );
     215
     216                        // Since we're not using the default modal built into
     217                        // a media frame, append our $element to the supplied container.
     218                        this.$el.appendTo( this.options.container );
     219
     220                        this.createSelection();
     221                        this.createStates();
     222                        this.bindHandlers();
     223                        this.render();
     224                },
     225
     226                createSelection: function() {
     227                        var selection = this.options.selection;
     228
     229                        if ( ! (selection instanceof media.model.Selection) ) {
     230                                this.options.selection = new media.model.Selection( selection, {
     231                                        multiple: this.options.multiple
     232                                });
     233                        }
     234
     235                        this._selection = {
     236                                attachments: new media.model.Attachments(),
     237                                difference: []
     238                        };
     239                },
     240
     241                createStates: function() {
     242                        var options = this.options;
     243
     244                        if ( this.options.states ) {
     245                                return;
     246                        }
     247
     248                        // Add the default states.
     249                        this.states.add([
     250                                new media.controller.Library({
     251                                        library:    media.query( options.library ),
     252                                        multiple:   options.multiple,
     253                                        title:      options.title,
     254                                        priority:   20,
     255                                        toolbar:    false,
     256                                        router:     false,
     257                                        content:    'browse',
     258                                        filterable: 'mime-types'
     259                                })
     260                        ]);
     261                },
     262
     263                bindHandlers: function() {
     264                        this.on( 'content:create:browse', this.browseContent, this );
     265                        this.on( 'content:render:edit-image', this.editImageContent, this );
     266
     267                        // Handle a frame-level event for editing an attachment.
     268                        this.on( 'edit:attachment', this.editAttachment, this );
     269                        this.on( 'edit:attachment:next', this.editNextAttachment, this );
     270                        this.on( 'edit:attachment:previous', this.editPreviousAttachment, this );
     271                },
     272
     273                editPreviousAttachment: function( currentModel ) {
     274                        var library = this.state().get('library'),
     275                            currentModelIndex = library.indexOf( currentModel );
     276                        this.trigger( 'edit:attachment', library.at( currentModelIndex - 1 ) );
     277                },
     278
     279                editNextAttachment: function( currentModel ) {
     280                        var library = this.state().get('library'),
     281                            currentModelIndex = library.indexOf( currentModel );
     282                        this.trigger( 'edit:attachment', library.at( currentModelIndex + 1 ) );
     283                },
     284
     285                /**
     286                 * Open the Edit Attachment modal.
     287                 */
     288                editAttachment: function( model ) {
     289                        var library = this.state().get('library'), hasPrevious, hasNext;
     290                        if ( library.indexOf( model ) > 0 ) {
     291                                hasPrevious = true;
     292                        }
     293                        else {
     294                                hasPrevious = false;
     295                        }
     296                        if ( library.indexOf( model ) < library.length - 1 ) {
     297                                hasNext = true;
     298                        }
     299                        else {
     300                                hasNext = false;
     301                        }
     302
     303                        new media.view.Frame.EditAttachment({
     304                                hasPrevious:    hasPrevious,
     305                                hasNext:        hasNext,
     306                                model:          model,
     307                                gridController: this
     308                        });
     309                },
     310
     311                /**
     312                 * Content
     313                 *
     314                 * @param {Object} content
     315                 * @this wp.media.controller.Region
     316                 */
     317                browseContent: function( content ) {
     318                        var state = this.state();
     319
     320                        // Browse our library of attachments.
     321                        content.view = new media.view.AttachmentsBrowser({
     322                                controller: this,
     323                                collection: state.get('library'),
     324                                selection:  state.get('selection'),
     325                                model:      state,
     326                                sortable:   state.get('sortable'),
     327                                search:     state.get('searchable'),
     328                                filters:    state.get('filterable'),
     329                                display:    state.get('displaySettings'),
     330                                dragInfo:   state.get('dragInfo'),
     331                                bulkEdit:   true,
     332                                sidebar:    false,
     333
     334                                suggestedWidth:  state.get('suggestedWidth'),
     335                                suggestedHeight: state.get('suggestedHeight'),
     336
     337                                AttachmentView: state.get('AttachmentView')
     338                        });
     339                },
     340
     341                editImageContent: function() {
     342                        var image = this.state().get('image'),
     343                                view = new media.view.EditImage( { model: image, controller: this } ).render();
     344
     345                        this.content.set( view );
     346
     347                        // after creating the wrapper view, load the actual editor via an ajax call
     348                        view.loadEditor();
     349
     350                }
     351        });
     352
     353        media.view.Attachment.Details.TwoColumn = media.view.Attachment.Details.extend({
     354                template: wp.template( 'attachment-details-two-column' ),
     355
     356                initialize: function() {
     357                        this.$el.attr('aria-label', this.model.attributes.title).attr('aria-checked', false);
     358                        this.model.on( 'change:sizes change:uploading', this.render, this );
     359                        this.model.on( 'change:title', this._syncTitle, this );
     360                        this.model.on( 'change:caption', this._syncCaption, this );
     361                        this.model.on( 'change:percent', this.progress, this );
     362
     363                        // Update the selection.
     364                        this.model.on( 'add', this.select, this );
     365                        this.model.on( 'remove', this.deselect, this );
     366                }
     367        });
     368
     369        /**
     370         * A frame for editing the details of a specific media item.
     371         *
     372         * Opens in a modal by default.
     373         *
     374         * Requires an attachment model to be passed in the options hash under `model`.
     375         */
     376        media.view.Frame.EditAttachment = media.view.Frame.extend({
     377
     378                className: 'edit-attachment-frame',
     379                template: media.template( 'edit-attachment-frame' ),
     380                regions:   [ 'router', 'content' ],
     381
     382                events: {
     383                        'click':                    'collapse',
     384                        'click .delete-media-item': 'deleteMediaItem',
     385                        'click .left':              'previousMediaItem',
     386                        'click .right':             'nextMediaItem'
     387                },
     388
     389                initialize: function( options ) {
     390                        var self = this;
     391                        media.view.Frame.prototype.initialize.apply( this, arguments );
     392
     393                        _.defaults( this.options, {
     394                                modal:    true,
     395                                state: 'edit-attachment'
     396                        });
     397
     398                        this.createStates();
     399
     400                        this.on( 'content:render:edit-metadata', this.editMetadataContent, this );
     401                        this.on( 'content:render:edit-image', this.editImageContentUgh, this );
     402
     403                        // Only need a tab to Edit Image for images.
     404                        if ( this.model.get( 'type' ) === 'image' ) {
     405                                this.on( 'router:create', this.createRouter, this );
     406                                this.on( 'router:render', this.browseRouter, this );
     407                        }
     408
     409                        // Initialize modal container view.
     410                        if ( this.options.modal ) {
     411                                this.modal = new media.view.Modal({
     412                                        controller: this,
     413                                        title:      this.options.title
     414                                });
     415
     416                                // Completely destroy the modal DOM element when closing it.
     417                                this.modal.close = function() {
     418                                        self.modal.remove();
     419                                };
     420
     421                                this.modal.content( this );
     422                                this.modal.open();
     423                        }
     424                },
     425
     426                /**
     427                 * Add the default states to the frame.
     428                 */
     429                createStates: function() {
     430                        this.states.add([
     431                                new media.controller.EditImageNoFrame( { model: this.model } )
     432                        ]);
     433                },
     434
     435                /**
     436                 * @returns {wp.media.view.MediaFrame} Returns itself to allow chaining
     437                 */
     438                render: function() {
     439                        // Activate the default state if no active state exists.
     440                        if ( ! this.state() && this.options.state ) {
     441                                this.setState( this.options.state );
     442                        }
     443                        /**
     444                         * call 'render' directly on the parent class
     445                         */
     446                        return media.view.Frame.prototype.render.apply( this, arguments );
     447                },
     448
     449                /**
     450                 * Content region rendering callback for the `edit-metadata` mode.
     451                 */
     452                editMetadataContent: function() {
     453                        var view = new media.view.Attachment.Details.TwoColumn({
     454                                controller: this,
     455                                model:      this.model
     456                        });
     457                        this.content.set( view );
     458                },
     459
     460                /**
     461                 * For some reason the view doesn't exist in the DOM yet, don't have the
     462                 * patience to track this down right now.
     463                 */
     464                editImageContentUgh: function() {
     465                        _.defer( _.bind( this.editImageContent, this ) );
     466                },
     467
     468                /**
     469                 * Render the EditImage view into the frame's content region.
     470                 */
     471                editImageContent: function() {
     472                        var view = new media.view.EditImage( { model: this.model, controller: this } ).render();
     473
     474                        this.content.set( view );
     475
     476                        // after creating the wrapper view, load the actual editor via an ajax call
     477                        view.loadEditor();
     478                },
     479
     480                /**
     481                 * Create the router view.
     482                 *
     483                 * @param {Object} router
     484                 * @this wp.media.controller.Region
     485                 */
     486                createRouter: function( router ) {
     487                        router.view = new media.view.Router({
     488                                controller: this
     489                        });
     490                },
     491
     492                /**
     493                 * Router rendering callback.
     494                 *
     495                 * @param  media.view.Router view Instantiated in this.createRouter()
     496                 */
     497                browseRouter: function( view ) {
     498                        view.set({
     499                                'edit-metadata': {
     500                                        text:     'Edit Metadata',
     501                                        priority: 20
     502                                },
     503                                'edit-image': {
     504                                        text:     'Edit Image',
     505                                        priority: 40
     506                                }
     507                        });
     508                },
     509
     510                /**
     511                 * Click handler to switch to the previous media item.
     512                 */
     513                previousMediaItem: function() {
     514                        if ( ! this.options.hasPrevious )
     515                                return;
     516                        this.modal.close();
     517                        this.options.gridController.trigger( 'edit:attachment:previous', this.model );
     518                },
     519
     520                /**
     521                 * Click handler to switch to the next media item.
     522                 */
     523                nextMediaItem: function() {
     524                        if ( ! this.options.hasNext )
     525                                return;
     526                        this.modal.close();
     527                        this.options.gridController.trigger( 'edit:attachment:next', this.model );
     528                }
     529
     530        });
     531
     532}(jQuery, _, Backbone, wp));
     533 No newline at end of file
  • src/wp-includes/js/media-views.js

     
    17581758                        _.defaults( this.options, {
    17591759                                title:    '',
    17601760                                modal:    true,
    1761                                 uploader: true
     1761                                uploader: true,
     1762                                mode:     ['select']
    17621763                        });
    17631764
    17641765                        // Ensure core UI is enabled.
     
    19551956        });
    19561957
    19571958        /**
    1958          * wp.media.view.MediaFrame.Manage
    1959          *
    1960          * A generic management frame workflow.
    1961          *
    1962          * Used in the media grid view.
    1963          *
    1964          * @constructor
    1965          * @augments wp.media.view.MediaFrame
    1966          * @augments wp.media.view.Frame
    1967          * @augments wp.media.View
    1968          * @augments wp.Backbone.View
    1969          * @augments Backbone.View
    1970          * @mixes wp.media.controller.StateMachine
    1971          */
    1972         media.view.MediaFrame.Manage = media.view.MediaFrame.extend({
    1973                 /**
    1974                  * @global wp.Uploader
    1975                  */
    1976                 initialize: function() {
    1977                         _.defaults( this.options, {
    1978                                 title:     l10n.mediaLibraryTitle,
    1979                                 modal:     false,
    1980                                 selection: [],
    1981                                 library:   {},
    1982                                 multiple:  false,
    1983                                 state:     'library',
    1984                                 uploader:  true
    1985                         });
    1986 
    1987                         // Ensure core and media grid view UI is enabled.
    1988                         this.$el.addClass('wp-core-ui media-grid-view');
    1989 
    1990                         // Force the uploader off if the upload limit has been exceeded or
    1991                         // if the browser isn't supported.
    1992                         if ( wp.Uploader.limitExceeded || ! wp.Uploader.browser.supported ) {
    1993                                 this.options.uploader = false;
    1994                         }
    1995 
    1996                         // Initialize a window-wide uploader.
    1997                         if ( this.options.uploader ) {
    1998                                 this.uploader = new media.view.UploaderWindow({
    1999                                         controller: this,
    2000                                         uploader: {
    2001                                                 dropzone:  $('body'),
    2002                                                 container: $('body')
    2003                                         }
    2004                                 }).render();
    2005                                 this.uploader.ready();
    2006                                 $('body').append( this.uploader.el );
    2007 
    2008                                 this.options.uploader = false;
    2009                         }
    2010 
    2011                         /**
    2012                          * call 'initialize' directly on the parent class
    2013                          */
    2014                         media.view.MediaFrame.prototype.initialize.apply( this, arguments );
    2015 
    2016                         // Since we're not using the default modal built into
    2017                         // a media frame, append our $element to the supplied container.
    2018                         this.$el.appendTo( this.options.container );
    2019 
    2020                         this.createSelection();
    2021                         this.createStates();
    2022                         this.bindHandlers();
    2023                         this.render();
    2024                 },
    2025 
    2026                 createSelection: function() {
    2027                         var selection = this.options.selection;
    2028 
    2029                         if ( ! (selection instanceof media.model.Selection) ) {
    2030                                 this.options.selection = new media.model.Selection( selection, {
    2031                                         multiple: this.options.multiple
    2032                                 });
    2033                         }
    2034 
    2035                         this._selection = {
    2036                                 attachments: new media.model.Attachments(),
    2037                                 difference: []
    2038                         };
    2039                 },
    2040 
    2041                 createStates: function() {
    2042                         var options = this.options;
    2043 
    2044                         if ( this.options.states ) {
    2045                                 return;
    2046                         }
    2047 
    2048                         // Add the default states.
    2049                         this.states.add([
    2050                                 new media.controller.Library({
    2051                                         library:    media.query( options.library ),
    2052                                         multiple:   options.multiple,
    2053                                         title:      options.title,
    2054                                         priority:   20,
    2055                                         toolbar:    false,
    2056                                         router:     false,
    2057                                         content:    'browse',
    2058                                         filterable: 'mime-types'
    2059                                 }),
    2060 
    2061                                 new media.controller.EditImage( { model: options.editImage } )
    2062                         ]);
    2063                 },
    2064 
    2065                 bindHandlers: function() {
    2066                         this.on( 'content:create:browse', this.browseContent, this );
    2067                         this.on( 'content:render:edit-image', this.editImageContent, this );
    2068                 },
    2069 
    2070                 /**
    2071                  * Content
    2072                  *
    2073                  * @param {Object} content
    2074                  * @this wp.media.controller.Region
    2075                  */
    2076                 browseContent: function( content ) {
    2077                         var state = this.state();
    2078 
    2079                         // Browse our library of attachments.
    2080                         content.view = new media.view.AttachmentsBrowser({
    2081                                 controller: this,
    2082                                 collection: state.get('library'),
    2083                                 selection:  state.get('selection'),
    2084                                 model:      state,
    2085                                 sortable:   state.get('sortable'),
    2086                                 search:     state.get('searchable'),
    2087                                 filters:    state.get('filterable'),
    2088                                 display:    state.get('displaySettings'),
    2089                                 dragInfo:   state.get('dragInfo'),
    2090                                 bulkEdit:   true,
    2091 
    2092                                 suggestedWidth:  state.get('suggestedWidth'),
    2093                                 suggestedHeight: state.get('suggestedHeight'),
    2094 
    2095                                 AttachmentView: state.get('AttachmentView')
    2096                         });
    2097                 },
    2098 
    2099                 editImageContent: function() {
    2100                         var image = this.state().get('image'),
    2101                                 view = new media.view.EditImage( { model: image, controller: this } ).render();
    2102 
    2103                         this.content.set( view );
    2104 
    2105                         // after creating the wrapper view, load the actual editor via an ajax call
    2106                         view.loadEditor();
    2107 
    2108                 }
    2109         });
    2110 
    2111         /**
    21121959         * wp.media.view.MediaFrame.Select
    21131960         *
    21141961         * Type of media frame that is used to select an item or items from the media library
     
    46654512                        var selection = this.options.selection;
    46664513
    46674514                        this.$el.attr('aria-label', this.model.attributes.title).attr('aria-checked', false);
    4668                         this.model.on( 'change:sizes change:uploading', this.render, this );
     4515                        this.model.on( 'change', this.render, this );
    46694516                        this.model.on( 'change:title', this._syncTitle, this );
    46704517                        this.model.on( 'change:caption', this._syncCaption, this );
    46714518                        this.model.on( 'change:percent', this.progress, this );
     
    47184565                                        compat:        false,
    47194566                                        alt:           '',
    47204567                                        description:   ''
    4721                                 });
     4568                                }, this.options );
    47224569
    47234570                        options.buttons  = this.buttons;
    47244571                        options.describe = this.controller.state().get('describe');
     
    47684615                 */
    47694616                toggleSelectionHandler: function( event ) {
    47704617                        var method;
    4771 
    47724618                        // Catch enter and space events
    47734619                        if ( 'keydown' === event.type && 13 !== event.keyCode && 32 !== event.keyCode ) {
    47744620                                return;
    47754621                        }
     4622
     4623                        // In the grid view, bubble up an edit:attachment event to the controller.
     4624                        if ( _.contains( this.controller.options.mode, 'grid' ) ) {
     4625                                this.controller.trigger( 'edit:attachment', this.model );
     4626                                return;
     4627                        }
     4628
    47764629                        if ( event.shiftKey ) {
    47774630                                method = 'between';
    47784631                        } else if ( event.ctrlKey || event.metaKey ) {
     
    53035156                 */
    53045157                createAttachmentView: function( attachment ) {
    53055158                        var view = new this.options.AttachmentView({
    5306                                 controller: this.controller,
    5307                                 model:      attachment,
    5308                                 collection: this.collection,
    5309                                 selection:  this.options.selection
     5159                                controller:           this.controller,
     5160                                model:                attachment,
     5161                                collection:           this.collection,
     5162                                selection:            this.options.selection,
     5163                                showAttachmentFields: this.options.showAttachmentFields
    53105164                        });
    53115165
    53125166                        return this._viewsByCid[ attachment.cid ] = view;
     
    56035457                }
    56045458        });
    56055459
    5606 
    56075460        /**
    56085461         * wp.media.view.AttachmentsBrowser
    56095462         *
     
    56215474                                filters: false,
    56225475                                search:  true,
    56235476                                display: false,
    5624 
     5477                                sidebar: true,
     5478                                showAttachmentFields: getUserSetting( 'showAttachmentFields', [ 'title', 'uploadedTo', 'dateFormatted', 'mime' ] ),
    56255479                                AttachmentView: media.view.Attachment.Library
    56265480                        });
    56275481
    56285482                        this.createToolbar();
    56295483                        this.updateContent();
    5630                         this.createSidebar();
     5484                        if ( this.options.sidebar ) {
     5485                                this.createSidebar();
     5486                        } else {
     5487                                this.$el.addClass( 'hide-sidebar' );
     5488                        }
    56315489
    56325490                        this.collection.on( 'add remove reset', this.updateContent, this );
    56335491                },
     
    56525510
    56535511                        this.views.add( this.toolbar );
    56545512
     5513                        // Feels odd to bring the global media library switcher into the Attachment
     5514                        // browser view. Is this a use case for doAction( 'add:toolbar-items:attachments-browser', this.toolbar );
     5515                        // which the controller can tap into and add this view?
     5516                        if ( _.contains( this.controller.options.mode, 'grid' ) ) {
     5517                                var libraryViewSwitcherConstructor = media.View.extend({
     5518                                        className: 'view-switch media-grid-view-switch',
     5519                                        template: media.template( 'media-library-view-switcher')
     5520                                });
     5521                                this.toolbar.set( 'libraryViewSwitcher', new libraryViewSwitcherConstructor({
     5522                                        controller: this.controller,
     5523                                        priority: -90
     5524                                }).render() );
     5525                        }
     5526
    56555527                        filters = this.options.filters;
    56565528                        if ( 'uploaded' === filters ) {
    56575529                                FiltersConstructor = media.view.AttachmentFilters.Uploaded;
     
    57465618                        this.removeContent();
    57475619
    57485620                        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,
     5621                                controller:           this.controller,
     5622                                collection:           this.collection,
     5623                                selection:            this.options.selection,
     5624                                model:                this.model,
     5625                                sortable:             this.options.sortable,
     5626                                showAttachmentFields: this.options.showAttachmentFields,
    57545627
    57555628                                // The single `Attachment` view to be used in the `Attachments` view.
    57565629                                AttachmentView: this.options.AttachmentView
  • src/wp-includes/media-template.php

     
    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>
     
    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<# if ( ! data.hasPrevious ) { #> disabled <# } #>"><span class="screen-reader-text"><?php _e( 'Edit previous media item' ); ?></span></button>
     256                        <button class="right dashicons dashicons-no<# if ( ! data.hasNext ) { #> disabled <# } #>"><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.sizes.full.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                <div class="attachment-fields">
     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                </div>
     352        </script>
     353
    244354        <script type="text/html" id="tmpl-attachment">
    245355                <div class="attachment-preview type-{{ data.type }} subtype-{{ data.subtype }} {{ data.orientation }}">
    246356                        <# if ( data.uploading ) { #>
     
    251361                                                <img src="{{ data.size.url }}" draggable="false" alt="" />
    252362                                        </div>
    253363                                </div>
    254                         <# } else { #>
     364                        <# } else {
     365                                if ( data.thumb && data.thumb.src && data.thumb.src !== data.icon ) {
     366                                #><img src="{{ data.thumb.src }}" class="thumbnail" draggable="false" /><#
     367                                } #>
    255368                                <img src="{{ data.icon }}" class="icon" draggable="false" />
    256369                                <div class="filename">
    257370                                        <div>{{ data.filename }}</div>
    258371                                </div>
    259372                        <# } #>
    260 
     373                        <# if ( _.contains( data.controller.options.mode, 'grid' ) ) { #>
     374                                <span class="edit-media">Edit</span>
     375                        <# } #>
    261376                        <# if ( data.buttons.close ) { #>
    262377                                <a class="close media-modal-icon" href="#" title="<?php esc_attr_e('Remove'); ?>"></a>
    263378                        <# } #>
     
    268383                </div>
    269384                <#
    270385                var maybeReadOnly = data.can.save || data.allowLocalEdits ? '' : 'readonly';
    271                 if ( data.describe ) { #>
    272                         <# if ( 'image' === data.type ) { #>
     386                if ( data.describe ) {
     387                        if ( 'image' === data.type ) { #>
    273388                                <input type="text" value="{{ data.caption }}" class="describe" data-setting="caption"
    274389                                        placeholder="<?php esc_attr_e('Caption this image&hellip;'); ?>" {{ maybeReadOnly }} />
    275390                        <# } else { #>
     
    281396                                        <# } else { #>
    282397                                                placeholder="<?php esc_attr_e('Describe this media file&hellip;'); ?>"
    283398                                        <# } #> {{ maybeReadOnly }} />
    284                         <# } #>
     399                        <# }
     400                }
     401
     402                if ( _.contains( data.controller.options.mode, 'grid' ) ) { #>
     403                <div class="data-fields">
     404                <# _.each( data.showAttachmentFields, function( field ) { #>
     405                        <div class="data-field data-{{ field }}"><#
     406                                if ( 'uploadedTo' === field ) {
     407                                        if ( data[field] ) {
     408                                        #><?php _e( 'Uploaded To:' ) ?><#
     409                                        } else {
     410                                        #><?php _e( 'Unattached' ) ?><#
     411                                        }
     412                                } else if ( 'title' === field && ! data[ field ] ) {
     413                                #><?php _e( '(No title)' ) ?><#
     414                                }
     415
     416                                if ( data[ field ] ) {
     417                                        #>{{ data[ field ] }}<#
     418                                }
     419                        #></div>
     420                <# }); #>
     421                </div>
    285422                <# } #>
     423
    286424        </script>
    287425
    288426        <script type="text/html" id="tmpl-attachment-details">
  • src/wp-includes/script-loader.php

     
    506506                $scripts->add( 'dashboard', "/wp-admin/js/dashboard$suffix.js", array( 'jquery', 'admin-comments', 'postbox' ), false, 1 );
    507507
    508508                $scripts->add( 'list-revisions', "/wp-includes/js/wp-list-revisions$suffix.js" );
    509 
     509                $scripts->add( 'media-grid', "/wp-includes/js/media-grid$suffix.js", array( 'media-editor' ), false, 1 );
    510510                $scripts->add( 'media', "/wp-admin/js/media$suffix.js", array( 'jquery' ), false, 1 );
    511511                did_action( 'init' ) && $scripts->localize( 'media', 'attachMediaBoxL10n', array(
    512512                        'error' => __( 'An error has occurred. Please reload the page and try again.' ),