diff --git a/src/wp-admin/css/list-tables.css b/src/wp-admin/css/list-tables.css index 0b4115d1de..62fc5b1331 100644 --- a/src/wp-admin/css/list-tables.css +++ b/src/wp-admin/css/list-tables.css @@ -634,7 +634,6 @@ classes exist in paginate_links() but not seen in list table output. */ .view-switch > a:before { color: #bbb; - content: '\f163'; display: inline-block; float: left; font: normal 20px/1 'dashicons'; @@ -645,6 +644,10 @@ classes exist in paginate_links() but not seen in list table output. */ -moz-osx-font-smoothing: grayscale; } +.view-switch > .view-list:before { + content: '\f163'; +} + .view-switch a:hover:before { color: #727272; } @@ -653,11 +656,18 @@ classes exist in paginate_links() but not seen in list table output. */ color: #0074a2; } -.view-switch > a + a:before { +.view-switch > a + a { margin-left: 5px; +} + +.view-switch > .view-excerpt:before { content: '\f164'; } +.view-switch > .view-grid:before { + content: '\f180'; +} + .filter { float: left; margin: -5px 0 0 10px; diff --git a/src/wp-admin/includes/class-wp-list-table.php b/src/wp-admin/includes/class-wp-list-table.php index b2ca8aa985..76f25e0931 100644 --- a/src/wp-admin/includes/class-wp-list-table.php +++ b/src/wp-admin/includes/class-wp-list-table.php @@ -493,8 +493,17 @@ class WP_List_Table {
$title ) { - $class = ( $current_mode == $mode ) ? 'class="current"' : ''; - echo "$title\n"; + $classes = array( 'view-' . $mode ); + if ( $current_mode == $mode ) + $classes[] = 'current'; + printf( + "%s\n", + esc_url( add_query_arg( 'mode', $mode ) ), + implode( ' ', $classes ), + esc_url( includes_url( 'images/blank.gif' ) ), + $title, + $title + ); } ?>
diff --git a/src/wp-admin/includes/class-wp-media-list-table.php b/src/wp-admin/includes/class-wp-media-list-table.php index 4de086ab22..fc008b1c6e 100644 --- a/src/wp-admin/includes/class-wp-media-list-table.php +++ b/src/wp-admin/includes/class-wp-media-list-table.php @@ -23,7 +23,7 @@ class WP_Media_List_Table extends WP_List_Table { } public function prepare_items() { - global $lost, $wp_query, $post_mime_types, $avail_post_mime_types; + global $lost, $wp_query, $post_mime_types, $avail_post_mime_types, $mode; $q = $_REQUEST; @@ -34,6 +34,8 @@ class WP_Media_List_Table extends WP_List_Table { $this->is_trash = isset( $_REQUEST['status'] ) && 'trash' == $_REQUEST['status']; + $mode = empty( $_REQUEST['mode'] ) ? 'list' : $_REQUEST['mode']; + $this->set_pagination_args( array( 'total_items' => $wp_query->found_posts, 'total_pages' => $wp_query->max_num_pages, @@ -125,6 +127,49 @@ class WP_Media_List_Table extends WP_List_Table { _e( 'No media attachments found.' ); } + protected function pagination( $which ) { + global $mode; + + parent::pagination( $which ); + + $this->view_switcher( $mode ); + } + + /** + * Display a view switcher + * + * @since 3.1.0 + * @access protected + */ + protected function view_switcher( $current_mode ) { + $modes = array( + 'list' => __( 'List View' ), + 'grid' => __( 'Grid View' ) + ); + +?> + +
+ $title ) { + $classes = array( 'view-' . $mode ); + if ( $current_mode == $mode ) + $classes[] = 'current'; + printf( + "%s\n", + esc_url( add_query_arg( 'mode', $mode ) ), + implode( ' ', $classes ), + esc_url( includes_url( 'images/blank.gif' ) ), + $title, + $title + + ); + } + ?> +
+'; diff --git a/src/wp-admin/js/media.js b/src/wp-admin/js/media.js index f9fc6508af..e767b093c6 100644 --- a/src/wp-admin/js/media.js +++ b/src/wp-admin/js/media.js @@ -72,6 +72,12 @@ var findPosts; }; $( document ).ready( function() { + // Open up a manage media frame into the grid. + wp.media && wp.media({ + frame: 'manage', + container: $('#wpbody-content') + }).open(); + $( '#find-posts-submit' ).click( function( event ) { if ( ! $( '#find-posts-response input[type="radio"]:checked' ).length ) event.preventDefault(); diff --git a/src/wp-admin/upload.php b/src/wp-admin/upload.php index c38a0b6356..11175f9c5e 100644 --- a/src/wp-admin/upload.php +++ b/src/wp-admin/upload.php @@ -12,6 +12,30 @@ require_once( dirname( __FILE__ ) . '/admin.php' ); if ( !current_user_can('upload_files') ) wp_die( __( 'You do not have permission to upload files.' ) ); +$mode = get_user_option( 'media_library_mode', get_current_user_id() ) ? get_user_option( 'media_library_mode', get_current_user_id() ) : 'grid'; +$modes = array( 'grid', 'list' ); + +if ( isset( $_GET['mode'] ) && in_array( $_GET['mode'], $modes ) ) { + $mode = $_GET['mode']; + update_user_option( get_current_user_id(), 'media_library_mode', $mode ); +} + +if ( 'grid' === $mode ) { + wp_enqueue_media(); + wp_enqueue_script( 'media' ); + require_once( ABSPATH . 'wp-admin/admin-header.php' ); + ?>
+ + List View + + + Grid View + +
get_pagenum(); @@ -237,7 +261,7 @@ if ( !empty($message) ) { ?> display(); ?>
- + diff --git a/src/wp-includes/css/media-views.css b/src/wp-includes/css/media-views.css index 7d16c309ea..779f0849a8 100644 --- a/src/wp-includes/css/media-views.css +++ b/src/wp-includes/css/media-views.css @@ -295,10 +295,11 @@ margin: 3px 0; } -.media-sidebar .setting span { +.media-sidebar .setting .name { min-width: 30%; margin-right: 4%; font-size: 12px; + text-align: right; } .media-sidebar .setting select { @@ -319,13 +320,17 @@ min-height: 22px; padding-top: 8px; line-height: 16px; - text-align: right; font-weight: normal; color: #666; } +.compat-item label span { + text-align: right; +} + .media-sidebar .setting input, -.media-sidebar .setting textarea { +.media-sidebar .setting textarea, +.media-sidebar .setting .value { margin: 1px; width: 65%; float: right; @@ -1812,7 +1817,7 @@ color: #666; } -.image-details .embed-media-settings .setting span { +.image-details .embed-media-settings .setting .name { float: left; width: 25%; text-align: right; @@ -2368,3 +2373,77 @@ background-image: url(../images/spinner-2x.gif); } } + + +/** + * Media Grid + */ + +.media-grid-view h1 { + color: #222; + font-size: 23px; + font-weight: 400; + margin: 10px 0 0; + padding: 9px 15px 4px 22px; + line-height: 29px; +} + +.media-grid-view-switch { + position: fixed; + right: 10px; + top: 44px; + z-index: 300; +} + +/** + * Position both the frame and the uploader window into the content + * area of the screen. + */ +.media-grid-view { + position: fixed; + bottom: 0; + left: 160px; + right: 0; + top: 32px; +} +@media screen and (max-width: 900px) { + .auto-fold .media-grid-view { + left: 36px; + } +} +@media screen and (max-width: 782px) { + .media-grid-view { + top: 46px; + } + .auto-fold .media-grid-view { + left: 0px; + bottom: 0px; + } +} + +/* Regions we don't use at all */ +.media-grid-view .media-frame-toolbar, +.media-grid-view .media-frame-menu { + display: none; +} + +.media-grid-view .media-frame-content { + bottom: 40px; +} +@media screen and (max-width: 782px) { + .media-grid-view .media-frame-content { + border-bottom: none; + bottom: 0; + } +} + +@media only screen and (max-width: 640px), screen and (max-height: 400px) { + .media-grid-view .media-frame-title { + display: block; + width: auto; + bottom: auto; + right: 0; + top: 0; + height: 60px; + } +} \ No newline at end of file diff --git a/src/wp-includes/js/media-models.js b/src/wp-includes/js/media-models.js index 1dd719688e..176b424037 100644 --- a/src/wp-includes/js/media-models.js +++ b/src/wp-includes/js/media-models.js @@ -30,6 +30,8 @@ window.wp = window.wp || {}; frame = new MediaFrame.Select( attributes ); } else if ( 'post' === attributes.frame && MediaFrame.Post ) { frame = new MediaFrame.Post( attributes ); + } else if ( 'manage' === attributes.frame && MediaFrame.Manage ) { + frame = new MediaFrame.Manage( attributes ); } else if ( 'image' === attributes.frame && MediaFrame.ImageDetails ) { frame = new MediaFrame.ImageDetails( attributes ); } else if ( 'audio' === attributes.frame && MediaFrame.AudioDetails ) { diff --git a/src/wp-includes/js/media-views.js b/src/wp-includes/js/media-views.js index b665cb5b7b..677574609e 100644 --- a/src/wp-includes/js/media-views.js +++ b/src/wp-includes/js/media-views.js @@ -615,7 +615,7 @@ this.get('selection').on( 'add remove reset', this.refreshContent, this ); - if ( this.get('contentUserSetting') ) { + if ( this.get( 'router' ) && this.get('contentUserSetting') ) { this.frame.on( 'content:activate', this.saveContentMode, this ); this.set( 'content', getUserSetting( 'libraryContent', this.get('content') ) ); } @@ -1920,6 +1920,160 @@ }; }); + /** + * wp.media.view.MediaFrame.Manage + * + * A generic management frame workflow. + * + * Used in the media grid view. + * + * @constructor + * @augments wp.media.view.MediaFrame + * @augments wp.media.view.Frame + * @augments wp.media.View + * @augments wp.Backbone.View + * @augments Backbone.View + * @mixes wp.media.controller.StateMachine + */ + media.view.MediaFrame.Manage = media.view.MediaFrame.extend({ + /** + * @global wp.Uploader + */ + initialize: function() { + _.defaults( this.options, { + title: l10n.mediaLibraryTitle, + modal: false, + selection: [], + library: {}, + multiple: false, + state: 'library', + uploader: true + }); + + // Ensure core and media grid view UI is enabled. + this.$el.addClass('wp-core-ui media-grid-view'); + + // Force the uploader off if the upload limit has been exceeded or + // if the browser isn't supported. + if ( wp.Uploader.limitExceeded || ! wp.Uploader.browser.supported ) { + this.options.uploader = false; + } + + // Initialize a window-wide uploader. + if ( this.options.uploader ) { + this.uploader = new media.view.UploaderWindow({ + controller: this, + uploader: { + dropzone: $('body'), + container: $('body') + } + }).render(); + this.uploader.ready(); + $('body').append( this.uploader.el ); + + this.options.uploader = false; + } + + /** + * call 'initialize' directly on the parent class + */ + media.view.MediaFrame.prototype.initialize.apply( this, arguments ); + + // Since we're not using the default modal built into + // a media frame, append our $element to the supplied container. + this.$el.appendTo( this.options.container ); + + this.createSelection(); + this.createStates(); + this.bindHandlers(); + this.render(); + }, + + createSelection: function() { + var selection = this.options.selection; + + if ( ! (selection instanceof media.model.Selection) ) { + this.options.selection = new media.model.Selection( selection, { + multiple: this.options.multiple + }); + } + + this._selection = { + attachments: new media.model.Attachments(), + difference: [] + }; + }, + + createStates: function() { + var options = this.options; + + if ( this.options.states ) { + return; + } + + // Add the default states. + this.states.add([ + new media.controller.Library({ + library: media.query( options.library ), + multiple: options.multiple, + title: options.title, + priority: 20, + toolbar: false, + router: false, + content: 'browse', + filterable: 'mime-types' + }), + + new media.controller.EditImage( { model: options.editImage } ) + ]); + }, + + bindHandlers: function() { + this.on( 'content:create:browse', this.browseContent, this ); + this.on( 'content:render:edit-image', this.editImageContent, this ); + }, + + /** + * Content + * + * @param {Object} content + * @this wp.media.controller.Region + */ + browseContent: function( content ) { + var state = this.state(); + + // Browse our library of attachments. + content.view = new media.view.AttachmentsBrowser({ + controller: this, + collection: state.get('library'), + selection: state.get('selection'), + model: state, + sortable: state.get('sortable'), + search: state.get('searchable'), + filters: state.get('filterable'), + display: state.get('displaySettings'), + dragInfo: state.get('dragInfo'), + bulkEdit: true, + + suggestedWidth: state.get('suggestedWidth'), + suggestedHeight: state.get('suggestedHeight'), + + AttachmentView: state.get('AttachmentView') + }); + }, + + editImageContent: function() { + var image = this.state().get('image'), + view = new media.view.EditImage( { model: image, controller: this } ).render(); + + this.content.set( view ); + + // after creating the wrapper view, load the actual editor via an ajax call + view.loadEditor(); + + }, + }); + /** * wp.media.view.MediaFrame.Select * @@ -5363,6 +5517,45 @@ } }); + /** + * wp.media.view.AttachmentFilters.FileTypes + * + * @constructor + * @augments wp.media.view.AttachmentFilters + * @augments wp.media.View + * @augments wp.Backbone.View + * @augments Backbone.View + */ + media.view.AttachmentFilters.mimeTypes = media.view.AttachmentFilters.extend({ + createFilters: function() { + var filters = {}; + + _.each( media.view.settings.mimeTypes || {}, function( text, key ) { + filters[ key ] = { + text: text, + props: { + type: key, + uploadedTo: null, + orderby: 'date', + order: 'DESC' + } + }; + }); + filters.all = { + text: l10n.allMediaTypes, + props: { + type: null, + uploadedTo: null, + orderby: 'date', + order: 'DESC' + }, + priority: 10 + }; + + this.filters = filters; + } + }); + /** * wp.media.view.AttachmentsBrowser @@ -5417,6 +5610,8 @@ FiltersConstructor = media.view.AttachmentFilters.Uploaded; } else if ( 'all' === filters ) { FiltersConstructor = media.view.AttachmentFilters.All; + } else if ( 'mime-types' === filters ) { + FiltersConstructor = media.view.AttachmentFilters.mimeTypes; } if ( FiltersConstructor ) { diff --git a/src/wp-includes/media-template.php b/src/wp-includes/media-template.php index 59c018ca9a..d0869c0af4 100644 --- a/src/wp-includes/media-template.php +++ b/src/wp-includes/media-template.php @@ -308,6 +308,7 @@ function wp_print_media_templates() {
{{ data.filename }}
{{ data.dateFormatted }}
+
{{ data.filesizeHumanReadable }}
<# if ( 'image' === data.type && ! data.uploading ) { #> <# if ( data.width && data.height ) { #>
{{ data.width }} × {{ data.height }}
@@ -339,25 +340,39 @@ function wp_print_media_templates() { + <# var maybeReadOnly = data.can.save || data.allowLocalEdits ? '' : 'readonly'; #> - - + + <# if ( 'image' === data.type ) { #> <# } #> -