Make WordPress Core

Changeset 41590


Ignore:
Timestamp:
09/25/2017 06:27:32 AM (7 years ago)
Author:
westonruter
Message:

Widgets: Introduce Gallery widget for displaying image galleries.

  • Galleries are managed in the widget in the same way they are managed in the post editor, both using the media manager.
  • Gallery widget is merged from the Core Media Widgets v0.2.0 feature plugin and it extends WP_Widget_Media in the same way as is done for image, audio, and video widgets.
  • Model syncing logic is updated to support booleans and arrays (of integers).
  • Placeholder areas in media widgets are now clickable shortcuts for selecting media.
  • Image widget placeholder is updated to match gallery widget where clicking preview is shortcut for editing media.

Props westonruter, joemcgill, timmydcrawford, m1tk00, obenland, melchoyce.
See #32417.
Fixes #41914.

Location:
trunk
Files:
4 added
8 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/wp-admin/css/widgets.css

    r41376 r41590  
    8888    border: 1px dashed #b4b9be;
    8989    box-sizing: border-box;
    90     cursor: default;
     90    cursor: pointer;
    9191    line-height: 20px;
    9292    padding: 9px 0;
     
    162162    margin: 1em 0;
    163163}
     164
     165.media-widget-gallery-preview {
     166    display: -webkit-box;
     167    display: flex;
     168    -webkit-box-pack: start;
     169    justify-content: flex-start;
     170    flex-wrap: wrap;
     171}
     172
     173.media-widget-preview.media_gallery,
     174.media-widget-preview.media_image {
     175    cursor: pointer;
     176}
     177
     178.media-widget-gallery-preview .gallery-item {
     179    box-sizing: border-box;
     180    width: 50%;
     181    margin: 0;
     182    padding: 1.79104477%;
     183}
     184
     185/*
     186 * Use targeted nth-last-child selectors to control the size of each image
     187 * based on how many gallery items are present in the grid.
     188 * See: https://alistapart.com/article/quantity-queries-for-css
     189 */
     190.media-widget-gallery-preview .gallery-item:nth-last-child(3):first-child,
     191.media-widget-gallery-preview .gallery-item:nth-last-child(3):first-child ~ .gallery-item,
     192.media-widget-gallery-preview .gallery-item:nth-last-child(n+5),
     193.media-widget-gallery-preview .gallery-item:nth-last-child(n+5) ~ .gallery-item,
     194.media-widget-gallery-preview .gallery-item:nth-last-child(n+6),
     195.media-widget-gallery-preview .gallery-item:nth-last-child(n+6) ~ .gallery-item {
     196    max-width: 33.33%;
     197}
     198
     199.media-widget-gallery-preview .gallery-item img {
     200    height: auto;
     201    vertical-align: bottom;
     202}
     203
     204.media-widget-gallery-preview .gallery-icon {
     205    position: relative;
     206}
     207
     208.media-widget-gallery-preview .gallery-icon-placeholder {
     209    position: absolute;
     210    top: 0;
     211    bottom: 0;
     212    width: 100%;
     213    box-sizing: border-box;
     214    display: -webkit-box;
     215    display: flex;
     216    -webkit-box-align: center;
     217    align-items: center;
     218    -webkit-box-pack: center;
     219    justify-content: center;
     220    background-color: rgba( 0, 0, 0, .5 );
     221}
     222
     223.media-widget-gallery-preview .gallery-icon-placeholder-text {
     224    font-weight: 600;
     225    font-size: 2em;
     226    color: white;
     227}
     228
    164229
    165230/* Widget Dragging Helpers */
  • trunk/src/wp-admin/js/widgets/media-image-widget.js

    r41252 r41590  
    2626
    2727        /**
     28         * View events.
     29         *
     30         * @type {object}
     31         */
     32        events: _.extend( {}, component.MediaWidgetControl.prototype.events, {
     33            'click .media-widget-preview.populated': 'editMedia'
     34        } ),
     35
     36        /**
    2837         * Render preview.
    2938         *
     
    3948            previewTemplate = wp.template( 'wp-media-widget-image-preview' );
    4049            previewContainer.html( previewTemplate( control.previewTemplateProps.toJSON() ) );
     50            previewContainer.addClass( 'populated' );
    4151
    4252            linkInput = control.$el.find( '.link' );
  • trunk/src/wp-admin/js/widgets/media-widgets.js

    r41350 r41590  
    430430            'click .notice-missing-attachment a': 'handleMediaLibraryLinkClick',
    431431            'click .select-media': 'selectMedia',
     432            'click .placeholder': 'selectMedia',
    432433            'click .edit-media': 'editMedia'
    433434        },
     
    592593            var control = this;
    593594            control.syncContainer.find( '.media-widget-instance-property' ).each( function() {
    594                 var input = $( this ), value;
    595                 value = control.model.get( input.data( 'property' ) );
     595                var input = $( this ), value, propertyName;
     596                propertyName = input.data( 'property' );
     597                value = control.model.get( propertyName );
    596598                if ( _.isUndefined( value ) ) {
    597599                    return;
    598600                }
    599                 value = String( value );
    600                 if ( input.val() === value ) {
    601                     return;
    602                 }
    603                 input.val( value );
    604                 input.trigger( 'change' );
     601
     602                if ( 'array' === control.model.schema[ propertyName ].type && _.isArray( value ) ) {
     603                    value = value.join( ',' );
     604                } else if ( 'boolean' === control.model.schema[ propertyName ].type ) {
     605                    value = value ? '1' : ''; // Because in PHP, strval( true ) === '1' && strval( false ) === ''.
     606                } else {
     607                    value = String( value );
     608                }
     609
     610                if ( input.val() !== value ) {
     611                    input.val( value );
     612                    input.trigger( 'change' );
     613                }
    605614            });
    606615        },
     
    10031012                }
    10041013                type = model.schema[ name ].type;
    1005                 if ( 'integer' === type ) {
     1014                if ( 'array' === type ) {
     1015                    castedAttrs[ name ] = value;
     1016                    if ( ! _.isArray( castedAttrs[ name ] ) ) {
     1017                        castedAttrs[ name ] = castedAttrs[ name ].split( /,/ ); // Good enough for parsing an ID list.
     1018                    }
     1019                    if ( model.schema[ name ].items && 'integer' === model.schema[ name ].items.type ) {
     1020                        castedAttrs[ name ] = _.filter(
     1021                            _.map( castedAttrs[ name ], function( id ) {
     1022                                return parseInt( id, 10 );
     1023                            },
     1024                            function( id ) {
     1025                                return 'number' === typeof id;
     1026                            }
     1027                        ) );
     1028                    }
     1029                } else if ( 'integer' === type ) {
    10061030                    castedAttrs[ name ] = parseInt( value, 10 );
    10071031                } else if ( 'boolean' === type ) {
  • trunk/src/wp-includes/default-widgets.php

    r41312 r41590  
    3232require_once( ABSPATH . WPINC . '/widgets/class-wp-widget-media-video.php' );
    3333
     34/** WP_Widget_Media_Gallery class */
     35require_once( ABSPATH . WPINC . '/widgets/class-wp-widget-media-gallery.php' );
     36
    3437/** WP_Widget_Meta class */
    3538require_once( ABSPATH . WPINC . '/widgets/class-wp-widget-meta.php' );
  • trunk/src/wp-includes/script-loader.php

    r41584 r41590  
    700700        $scripts->add( 'media-audio-widget', "/wp-admin/js/widgets/media-audio-widget$suffix.js", array( 'media-widgets', 'media-audiovideo' ) );
    701701        $scripts->add( 'media-image-widget', "/wp-admin/js/widgets/media-image-widget$suffix.js", array( 'media-widgets' ) );
     702        $scripts->add( 'media-gallery-widget', "/wp-admin/js/widgets/media-gallery-widget$suffix.js", array( 'media-widgets' ) );
    702703        $scripts->add( 'media-video-widget', "/wp-admin/js/widgets/media-video-widget$suffix.js", array( 'media-widgets', 'media-audiovideo', 'wp-api-request' ) );
    703704        $scripts->add( 'text-widgets', "/wp-admin/js/widgets/text-widgets$suffix.js", array( 'jquery', 'backbone', 'editor', 'wp-util', 'wp-a11y' ) );
  • trunk/src/wp-includes/widgets.php

    r41555 r41590  
    16101610    register_widget( 'WP_Widget_Media_Image' );
    16111611
     1612    register_widget( 'WP_Widget_Media_Gallery' );
     1613
    16121614    register_widget( 'WP_Widget_Media_Video' );
    16131615
  • trunk/src/wp-includes/widgets/class-wp-widget-media.php

    r41252 r41590  
    258258            }
    259259            $value = $new_instance[ $field ];
     260
     261            // Workaround for rest_validate_value_from_schema() due to the fact that rest_is_boolean( '' ) === false, while rest_is_boolean( '1' ) is true.
     262            if ( 'boolean' === $field_schema['type'] && '' === $value ) {
     263                $value = false;
     264            }
     265
    260266            if ( true !== rest_validate_value_from_schema( $value, $field_schema, $field ) ) {
    261267                continue;
     
    317323                name="<?php echo esc_attr( $this->get_field_name( $name ) ); ?>"
    318324                id="<?php echo esc_attr( $this->get_field_id( $name ) ); // Needed specifically by wpWidgets.appendTitle(). ?>"
    319                 value="<?php echo esc_attr( strval( $value ) ); ?>"
     325                value="<?php echo esc_attr( is_array( $value ) ? join( ',', $value ) : strval( $value ) ); ?>"
    320326            />
    321327        <?php
     
    389395                <input id="{{ elementIdPrefix }}title" type="text" class="widefat title">
    390396            </p>
    391             <div class="media-widget-preview">
     397            <div class="media-widget-preview <?php echo esc_attr( $this->id_base ); ?>">
    392398                <div class="attachment-media-view">
    393399                    <div class="placeholder"><?php echo esc_html( $this->l10n['no_media_selected'] ); ?></div>
  • trunk/tests/qunit/index.html

    r41375 r41590  
    120120            _.extend( wp.mediaWidgets.controlConstructors[ "media_audio" ].prototype.l10n, {"no_media_selected":"No audio selected","select_media":"Select File","change_media":"Change Audio","edit_media":"Edit Audio","add_to_widget":"Add to Widget","missing_attachment":"We can&#8217;t find that audio file. Check your <a href=\"http:\/\/src.wordpress-develop.dev\/wp-admin\/upload.php\">media library<\/a> and make sure it wasn&#8217;t deleted.","media_library_state_multi":{"0":"Audio Widget (%d)","1":"Audio Widget (%d)","singular":"Audio Widget (%d)","plural":"Audio Widget (%d)","context":null,"domain":null},"media_library_state_single":"Audio Widget"} );
    121121        </script>
     122        <script type='text/javascript' src='../../src/wp-admin/js/widgets/media-gallery-widget.js'></script>
     123        <script type='text/javascript'>
     124            wp.mediaWidgets.modelConstructors[ "media_gallery" ].prototype.schema = {"title":{"type":"string","default":"","should_preview_update":false},"ids":{"type":"string","default":""},"columns":{"type":"integer","default":3},"size":{"type":"string","default":"thumbnail","enum":["thumbnail","medium","medium_large","large","post-thumbnail","full","custom"]},"link_type":{"type":"string","default":"none","enum":["none","file","post"],"media_prop":"link","should_preview_update":false},"orderby_random":{"type":"boolean","default":false,"media_prop":"_orderbyRandom","should_preview_update":false},"attachments":{"type":"string","default":""}};
     125            wp.mediaWidgets.controlConstructors[ "media_gallery" ].prototype.mime_type = "image";
     126
     127            _.extend( wp.mediaWidgets.controlConstructors[ "media_gallery" ].prototype.l10n, {"no_media_selected":"No images selected","add_media":"Add Media","replace_media":"Replace Media","edit_media":"Edit Gallery","add_to_widget":"Add to Widget","missing_attachment":"We can&#8217;t find that gallery. Check your <a href=\"http:\/\/src.wordpress-develop.dev\/wp-admin\/upload.php\">media library<\/a> and make sure it wasn&#8217;t deleted.","media_library_state_multi":"","media_library_state_single":"","unsupported_file_type":"Looks like this isn&#8217;t the correct kind of file. Please link to an appropriate file instead.","select_media":"Select Images","change_media":"Add Image"} );
     128        </script>
    122129
    123130        <!-- Unit tests -->
     
    137144        <script src="wp-admin/js/widgets/test-media-widgets.js"></script>
    138145        <script src="wp-admin/js/widgets/test-media-image-widget.js"></script>
     146        <script src="wp-admin/js/widgets/test-media-gallery-widget.js"></script>
    139147        <script src="wp-admin/js/widgets/test-media-video-widget.js"></script>
    140148
     
    570578                </div><!-- #available-widgets -->
    571579            </div><!-- #widgets-left -->
    572            
     580
    573581            <script type="text/html" id="tmpl-widget-media-media_image-control">
    574582            <# var elementIdPrefix = 'el' + String( Math.random() ) + '_' #>
     
    811819        <div class="media-frame-uploader"></div>
    812820    </script>
     821
     822    <script type="text/html" id="tmpl-widget-media-media_gallery-control">
     823        <# var elementIdPrefix = 'el' + String( Math.random() ) + '_' #>
     824        <p>
     825            <label for="{{ elementIdPrefix }}title">Title:</label>
     826            <input id="{{ elementIdPrefix }}title" type="text" class="widefat title">
     827        </p>
     828        <div class="media-widget-preview">
     829            <div class="attachment-media-view">
     830                <div class="placeholder">No images selected</div>
     831            </div>
     832            </div>
     833            <p class="media-widget-buttons">
     834                <button type="button" class="button edit-media selected">
     835                    Edit Gallery                </button>
     836                <button type="button" class="button change-media select-media selected">
     837                    Replace Media               </button>
     838                <button type="button" class="button select-media not-selected">
     839                    Add Media               </button>
     840            </p>
     841        <div class="media-widget-fields">
     842        </div>
     843    </script>
     844                <script type="text/html" id="tmpl-wp-media-widget-gallery-preview">
     845            <# var describedById = 'describedBy-' + String( Math.random() ); #>
     846            <# data.attachments = data.attachments ? JSON.parse(data.attachments) : ''; #>
     847            <# if ( Array.isArray( data.attachments ) && data.attachments.length ) { #>
     848                <div class="gallery gallery-columns-{{ data.columns }}">
     849                    <# _.each( data.attachments, function( attachment, index ) { #>
     850                        <dl class="gallery-item">
     851                            <dt class="gallery-icon">
     852                            <# if ( attachment.sizes.thumbnail ) { #>
     853                                <img src="{{ attachment.sizes.thumbnail.url }}" width="{{ attachment.sizes.thumbnail.width }}" height="{{ attachment.sizes.thumbnail.height }}" alt="" />
     854                            <# } else { #>
     855                                <img src="{{ attachment.url }}" alt="" />
     856                            <# } #>
     857                            </dt>
     858                            <# if ( attachment.caption ) { #>
     859                                <dd class="wp-caption-text gallery-caption">
     860                                    {{{ data.verifyHTML( attachment.caption ) }}}
     861                                </dd>
     862                            <# } #>
     863                        </dl>
     864                        <# if ( index % data.columns === data.columns - 1 ) { #>
     865                            <br style="clear: both;">
     866                        <# } #>
     867                    <# } ); #>
     868                </div>
     869            <# } else { #>
     870                <div class="attachment-media-view">
     871                    <p class="placeholder">No images selected</p>
     872                </div>
     873            <# } #>
     874        </script>
    813875
    814876    <script type="text/html" id="tmpl-media-modal">
Note: See TracChangeset for help on using the changeset viewer.