From 666912edf9c341fc6504e59670a582bfedaa0eef Mon Sep 17 00:00:00 2001
From: Weston Ruter
Date: Mon, 25 Sep 2017 06:27:32 +0000
Subject: [PATCH] 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.
git-svn-id: https://develop.svn.wordpress.org/trunk@41590 602fd350-edb4-49c9-b593-d223f7449a82
---
src/wp-admin/css/widgets.css | 67 +++-
.../js/widgets/media-gallery-widget.js | 325 ++++++++++++++++++
src/wp-admin/js/widgets/media-image-widget.js | 10 +
src/wp-admin/js/widgets/media-widgets.js | 40 ++-
src/wp-includes/default-widgets.php | 3 +
src/wp-includes/script-loader.php | 1 +
src/wp-includes/widgets.php | 2 +
.../widgets/class-wp-widget-media-gallery.php | 224 ++++++++++++
.../widgets/class-wp-widget-media.php | 10 +-
.../tests/widgets/media-gallery-widget.php | 201 +++++++++++
tests/qunit/index.html | 64 +++-
.../js/widgets/test-media-gallery-widget.js | 30 ++
12 files changed, 965 insertions(+), 12 deletions(-)
create mode 100644 src/wp-admin/js/widgets/media-gallery-widget.js
create mode 100644 src/wp-includes/widgets/class-wp-widget-media-gallery.php
create mode 100644 tests/phpunit/tests/widgets/media-gallery-widget.php
create mode 100644 tests/qunit/wp-admin/js/widgets/test-media-gallery-widget.js
diff --git a/src/wp-admin/css/widgets.css b/src/wp-admin/css/widgets.css
index f31b63c881..e291590e70 100644
--- a/src/wp-admin/css/widgets.css
+++ b/src/wp-admin/css/widgets.css
@@ -87,7 +87,7 @@
.media-widget-control .placeholder {
border: 1px dashed #b4b9be;
box-sizing: border-box;
- cursor: default;
+ cursor: pointer;
line-height: 20px;
padding: 9px 0;
position: relative;
@@ -162,6 +162,71 @@
margin: 1em 0;
}
+.media-widget-gallery-preview {
+ display: -webkit-box;
+ display: flex;
+ -webkit-box-pack: start;
+ justify-content: flex-start;
+ flex-wrap: wrap;
+}
+
+.media-widget-preview.media_gallery,
+.media-widget-preview.media_image {
+ cursor: pointer;
+}
+
+.media-widget-gallery-preview .gallery-item {
+ box-sizing: border-box;
+ width: 50%;
+ margin: 0;
+ padding: 1.79104477%;
+}
+
+/*
+ * Use targeted nth-last-child selectors to control the size of each image
+ * based on how many gallery items are present in the grid.
+ * See: https://alistapart.com/article/quantity-queries-for-css
+ */
+.media-widget-gallery-preview .gallery-item:nth-last-child(3):first-child,
+.media-widget-gallery-preview .gallery-item:nth-last-child(3):first-child ~ .gallery-item,
+.media-widget-gallery-preview .gallery-item:nth-last-child(n+5),
+.media-widget-gallery-preview .gallery-item:nth-last-child(n+5) ~ .gallery-item,
+.media-widget-gallery-preview .gallery-item:nth-last-child(n+6),
+.media-widget-gallery-preview .gallery-item:nth-last-child(n+6) ~ .gallery-item {
+ max-width: 33.33%;
+}
+
+.media-widget-gallery-preview .gallery-item img {
+ height: auto;
+ vertical-align: bottom;
+}
+
+.media-widget-gallery-preview .gallery-icon {
+ position: relative;
+}
+
+.media-widget-gallery-preview .gallery-icon-placeholder {
+ position: absolute;
+ top: 0;
+ bottom: 0;
+ width: 100%;
+ box-sizing: border-box;
+ display: -webkit-box;
+ display: flex;
+ -webkit-box-align: center;
+ align-items: center;
+ -webkit-box-pack: center;
+ justify-content: center;
+ background-color: rgba( 0, 0, 0, .5 );
+}
+
+.media-widget-gallery-preview .gallery-icon-placeholder-text {
+ font-weight: 600;
+ font-size: 2em;
+ color: white;
+}
+
+
/* Widget Dragging Helpers */
.widget.ui-draggable-dragging {
min-width: 100%;
diff --git a/src/wp-admin/js/widgets/media-gallery-widget.js b/src/wp-admin/js/widgets/media-gallery-widget.js
new file mode 100644
index 0000000000..f569968e89
--- /dev/null
+++ b/src/wp-admin/js/widgets/media-gallery-widget.js
@@ -0,0 +1,325 @@
+/* eslint consistent-this: [ "error", "control" ] */
+(function( component ) {
+ 'use strict';
+
+ var GalleryWidgetModel, GalleryWidgetControl, GalleryDetailsMediaFrame;
+
+ /**
+ * Custom gallery details frame.
+ *
+ * @since 4.9.0
+ * @class GalleryDetailsMediaFrame
+ * @constructor
+ */
+ GalleryDetailsMediaFrame = wp.media.view.MediaFrame.Post.extend( {
+
+ /**
+ * Create the default states.
+ *
+ * @since 4.9.0
+ * @returns {void}
+ */
+ createStates: function createStates() {
+ this.states.add([
+ new wp.media.controller.Library({
+ id: 'gallery',
+ title: wp.media.view.l10n.createGalleryTitle,
+ priority: 40,
+ toolbar: 'main-gallery',
+ filterable: 'uploaded',
+ multiple: 'add',
+ editable: true,
+
+ library: wp.media.query( _.defaults({
+ type: 'image'
+ }, this.options.library ) )
+ }),
+
+ // Gallery states.
+ new wp.media.controller.GalleryEdit({
+ library: this.options.selection,
+ editing: this.options.editing,
+ menu: 'gallery'
+ }),
+
+ new wp.media.controller.GalleryAdd()
+ ]);
+ }
+ } );
+
+ /**
+ * Gallery widget model.
+ *
+ * See WP_Widget_Gallery::enqueue_admin_scripts() for amending prototype from PHP exports.
+ *
+ * @since 4.9.0
+ * @class GalleryWidgetModel
+ * @constructor
+ */
+ GalleryWidgetModel = component.MediaWidgetModel.extend( {} );
+
+ /**
+ * Gallery widget control.
+ *
+ * See WP_Widget_Gallery::enqueue_admin_scripts() for amending prototype from PHP exports.
+ *
+ * @since 4.9.0
+ * @class GalleryWidgetControl
+ * @constructor
+ */
+ GalleryWidgetControl = component.MediaWidgetControl.extend( {
+
+ /**
+ * View events.
+ *
+ * @since 4.9.0
+ * @type {object}
+ */
+ events: _.extend( {}, component.MediaWidgetControl.prototype.events, {
+ 'click .media-widget-gallery-preview': 'editMedia'
+ } ),
+
+ /**
+ * Initialize.
+ *
+ * @since 4.9.0
+ * @param {Object} options - Options.
+ * @param {Backbone.Model} options.model - Model.
+ * @param {jQuery} options.el - Control field container element.
+ * @param {jQuery} options.syncContainer - Container element where fields are synced for the server.
+ * @returns {void}
+ */
+ initialize: function initialize( options ) {
+ var control = this;
+
+ component.MediaWidgetControl.prototype.initialize.call( control, options );
+
+ _.bindAll( control, 'updateSelectedAttachments', 'handleAttachmentDestroy' );
+ control.selectedAttachments = new wp.media.model.Attachments();
+ control.model.on( 'change:ids', control.updateSelectedAttachments );
+ control.selectedAttachments.on( 'change', control.renderPreview );
+ control.selectedAttachments.on( 'reset', control.renderPreview );
+ control.updateSelectedAttachments();
+ },
+
+ /**
+ * Update the selected attachments if necessary.
+ *
+ * @since 4.9.0
+ * @returns {void}
+ */
+ updateSelectedAttachments: function updateSelectedAttachments() {
+ var control = this, newIds, oldIds, removedIds, addedIds, addedQuery;
+
+ newIds = control.model.get( 'ids' );
+ oldIds = _.pluck( control.selectedAttachments.models, 'id' );
+
+ removedIds = _.difference( oldIds, newIds );
+ _.each( removedIds, function( removedId ) {
+ control.selectedAttachments.remove( control.selectedAttachments.get( removedId ) );
+ });
+
+ addedIds = _.difference( newIds, oldIds );
+ if ( addedIds.length ) {
+ addedQuery = wp.media.query({
+ order: 'ASC',
+ orderby: 'post__in',
+ perPage: -1,
+ post__in: newIds,
+ query: true,
+ type: 'image'
+ });
+ addedQuery.more().done( function() {
+ control.selectedAttachments.reset( addedQuery.models );
+ });
+ }
+ },
+
+ /**
+ * Render preview.
+ *
+ * @since 4.9.0
+ * @returns {void}
+ */
+ renderPreview: function renderPreview() {
+ var control = this, previewContainer, previewTemplate, data;
+
+ previewContainer = control.$el.find( '.media-widget-preview' );
+ previewTemplate = wp.template( 'wp-media-widget-gallery-preview' );
+
+ data = control.previewTemplateProps.toJSON();
+ data.attachments = {};
+ control.selectedAttachments.each( function( attachment ) {
+ data.attachments[ attachment.id ] = attachment.toJSON();
+ } );
+
+ previewContainer.html( previewTemplate( data ) );
+ },
+
+ /**
+ * Determine whether there are selected attachments.
+ *
+ * @since 4.9.0
+ * @returns {boolean} Selected.
+ */
+ isSelected: function isSelected() {
+ var control = this;
+
+ if ( control.model.get( 'error' ) ) {
+ return false;
+ }
+
+ return control.model.get( 'ids' ).length > 0;
+ },
+
+ /**
+ * Open the media select frame to edit images.
+ *
+ * @since 4.9.0
+ * @returns {void}
+ */
+ editMedia: function editMedia() {
+ var control = this, selection, mediaFrame, mediaFrameProps;
+
+ selection = new wp.media.model.Selection( control.selectedAttachments.models, {
+ multiple: true
+ });
+
+ mediaFrameProps = control.mapModelToMediaFrameProps( control.model.toJSON() );
+ selection.gallery = new Backbone.Model( _.pick( mediaFrameProps, 'columns', 'link', 'size', '_orderbyRandom' ) );
+ if ( mediaFrameProps.size ) {
+ control.displaySettings.set( 'size', mediaFrameProps.size );
+ }
+ mediaFrame = new GalleryDetailsMediaFrame({
+ frame: 'manage',
+ text: control.l10n.add_to_widget,
+ selection: selection,
+ mimeType: control.mime_type,
+ selectedDisplaySettings: control.displaySettings,
+ showDisplaySettings: control.showDisplaySettings,
+ metadata: mediaFrameProps,
+ editing: true,
+ multiple: true,
+ state: 'gallery-edit'
+ });
+ wp.media.frame = mediaFrame; // See wp.media().
+
+ // Handle selection of a media item.
+ mediaFrame.on( 'update', function onUpdate( newSelection ) {
+ var state = mediaFrame.state(), resultSelection;
+
+ resultSelection = newSelection || state.get( 'selection' );
+ if ( ! resultSelection ) {
+ return;
+ }
+
+ // Copy orderby_random from gallery state.
+ if ( resultSelection.gallery ) {
+ control.model.set( control.mapMediaToModelProps( resultSelection.gallery.toJSON() ) );
+ }
+
+ // Directly update selectedAttachments to prevent needing to do additional request.
+ control.selectedAttachments.reset( resultSelection.models );
+
+ // Update models in the widget instance.
+ control.model.set( {
+ ids: _.pluck( resultSelection.models, 'id' )
+ } );
+ } );
+
+ mediaFrame.$el.addClass( 'media-widget' );
+ mediaFrame.open();
+
+ if ( selection ) {
+ selection.on( 'destroy', control.handleAttachmentDestroy );
+ }
+ },
+
+ /**
+ * Open the media select frame to chose an item.
+ *
+ * @since 4.9.0
+ * @returns {void}
+ */
+ selectMedia: function selectMedia() {
+ var control = this, selection, mediaFrame, mediaFrameProps;
+ selection = new wp.media.model.Selection( control.selectedAttachments.models, {
+ multiple: true
+ });
+
+ mediaFrameProps = control.mapModelToMediaFrameProps( control.model.toJSON() );
+ if ( mediaFrameProps.size ) {
+ control.displaySettings.set( 'size', mediaFrameProps.size );
+ }
+ mediaFrame = new GalleryDetailsMediaFrame({
+ frame: 'select',
+ text: control.l10n.add_to_widget,
+ selection: selection,
+ mimeType: control.mime_type,
+ selectedDisplaySettings: control.displaySettings,
+ showDisplaySettings: control.showDisplaySettings,
+ metadata: mediaFrameProps,
+ state: 'gallery'
+ });
+ wp.media.frame = mediaFrame; // See wp.media().
+
+ // Handle selection of a media item.
+ mediaFrame.on( 'update', function onUpdate( newSelection ) {
+ var state = mediaFrame.state(), resultSelection;
+
+ resultSelection = newSelection || state.get( 'selection' );
+ if ( ! resultSelection ) {
+ return;
+ }
+
+ // Copy orderby_random from gallery state.
+ if ( resultSelection.gallery ) {
+ control.model.set( control.mapMediaToModelProps( resultSelection.gallery.toJSON() ) );
+ }
+
+ // Directly update selectedAttachments to prevent needing to do additional request.
+ control.selectedAttachments.reset( resultSelection.models );
+
+ // Update widget instance.
+ control.model.set( {
+ ids: _.pluck( resultSelection.models, 'id' )
+ } );
+ } );
+
+ mediaFrame.$el.addClass( 'media-widget' );
+ mediaFrame.open();
+
+ if ( selection ) {
+ selection.on( 'destroy', control.handleAttachmentDestroy );
+ }
+
+ /*
+ * Make sure focus is set inside of modal so that hitting Esc will close
+ * the modal and not inadvertently cause the widget to collapse in the customizer.
+ */
+ mediaFrame.$el.find( ':focusable:first' ).focus();
+ },
+
+ /**
+ * Clear the selected attachment when it is deleted in the media select frame.
+ *
+ * @since 4.9.0
+ * @param {wp.media.models.Attachment} attachment - Attachment.
+ * @returns {void}
+ */
+ handleAttachmentDestroy: function handleAttachmentDestroy( attachment ) {
+ var control = this;
+ control.model.set( {
+ ids: _.difference(
+ control.model.get( 'ids' ),
+ [ attachment.id ]
+ )
+ } );
+ }
+ } );
+
+ // Exports.
+ component.controlConstructors.media_gallery = GalleryWidgetControl;
+ component.modelConstructors.media_gallery = GalleryWidgetModel;
+
+})( wp.mediaWidgets );
diff --git a/src/wp-admin/js/widgets/media-image-widget.js b/src/wp-admin/js/widgets/media-image-widget.js
index ddbe6b3e24..78b257feae 100644
--- a/src/wp-admin/js/widgets/media-image-widget.js
+++ b/src/wp-admin/js/widgets/media-image-widget.js
@@ -24,6 +24,15 @@
*/
ImageWidgetControl = component.MediaWidgetControl.extend({
+ /**
+ * View events.
+ *
+ * @type {object}
+ */
+ events: _.extend( {}, component.MediaWidgetControl.prototype.events, {
+ 'click .media-widget-preview.populated': 'editMedia'
+ } ),
+
/**
* Render preview.
*
@@ -38,6 +47,7 @@
previewContainer = control.$el.find( '.media-widget-preview' );
previewTemplate = wp.template( 'wp-media-widget-image-preview' );
previewContainer.html( previewTemplate( control.previewTemplateProps.toJSON() ) );
+ previewContainer.addClass( 'populated' );
linkInput = control.$el.find( '.link' );
if ( ! linkInput.is( document.activeElement ) ) {
diff --git a/src/wp-admin/js/widgets/media-widgets.js b/src/wp-admin/js/widgets/media-widgets.js
index e3bc41c480..5e7383c6fc 100644
--- a/src/wp-admin/js/widgets/media-widgets.js
+++ b/src/wp-admin/js/widgets/media-widgets.js
@@ -429,6 +429,7 @@ wp.mediaWidgets = ( function( $ ) {
events: {
'click .notice-missing-attachment a': 'handleMediaLibraryLinkClick',
'click .select-media': 'selectMedia',
+ 'click .placeholder': 'selectMedia',
'click .edit-media': 'editMedia'
},
@@ -591,17 +592,25 @@ wp.mediaWidgets = ( function( $ ) {
syncModelToInputs: function syncModelToInputs() {
var control = this;
control.syncContainer.find( '.media-widget-instance-property' ).each( function() {
- var input = $( this ), value;
- value = control.model.get( input.data( 'property' ) );
+ var input = $( this ), value, propertyName;
+ propertyName = input.data( 'property' );
+ value = control.model.get( propertyName );
if ( _.isUndefined( value ) ) {
return;
}
- value = String( value );
- if ( input.val() === value ) {
- return;
+
+ if ( 'array' === control.model.schema[ propertyName ].type && _.isArray( value ) ) {
+ value = value.join( ',' );
+ } else if ( 'boolean' === control.model.schema[ propertyName ].type ) {
+ value = value ? '1' : ''; // Because in PHP, strval( true ) === '1' && strval( false ) === ''.
+ } else {
+ value = String( value );
+ }
+
+ if ( input.val() !== value ) {
+ input.val( value );
+ input.trigger( 'change' );
}
- input.val( value );
- input.trigger( 'change' );
});
},
@@ -1002,7 +1011,22 @@ wp.mediaWidgets = ( function( $ ) {
return;
}
type = model.schema[ name ].type;
- if ( 'integer' === type ) {
+ if ( 'array' === type ) {
+ castedAttrs[ name ] = value;
+ if ( ! _.isArray( castedAttrs[ name ] ) ) {
+ castedAttrs[ name ] = castedAttrs[ name ].split( /,/ ); // Good enough for parsing an ID list.
+ }
+ if ( model.schema[ name ].items && 'integer' === model.schema[ name ].items.type ) {
+ castedAttrs[ name ] = _.filter(
+ _.map( castedAttrs[ name ], function( id ) {
+ return parseInt( id, 10 );
+ },
+ function( id ) {
+ return 'number' === typeof id;
+ }
+ ) );
+ }
+ } else if ( 'integer' === type ) {
castedAttrs[ name ] = parseInt( value, 10 );
} else if ( 'boolean' === type ) {
castedAttrs[ name ] = ! ( ! value || '0' === value || 'false' === value );
diff --git a/src/wp-includes/default-widgets.php b/src/wp-includes/default-widgets.php
index 7c8a903c56..767002b642 100644
--- a/src/wp-includes/default-widgets.php
+++ b/src/wp-includes/default-widgets.php
@@ -31,6 +31,9 @@ require_once( ABSPATH . WPINC . '/widgets/class-wp-widget-media-image.php' );
/** WP_Widget_Media_Video class */
require_once( ABSPATH . WPINC . '/widgets/class-wp-widget-media-video.php' );
+/** WP_Widget_Media_Gallery class */
+require_once( ABSPATH . WPINC . '/widgets/class-wp-widget-media-gallery.php' );
+
/** WP_Widget_Meta class */
require_once( ABSPATH . WPINC . '/widgets/class-wp-widget-meta.php' );
diff --git a/src/wp-includes/script-loader.php b/src/wp-includes/script-loader.php
index c85822c807..fa1ada70fb 100644
--- a/src/wp-includes/script-loader.php
+++ b/src/wp-includes/script-loader.php
@@ -699,6 +699,7 @@ function wp_default_scripts( &$scripts ) {
$scripts->add( 'media-audio-widget', "/wp-admin/js/widgets/media-audio-widget$suffix.js", array( 'media-widgets', 'media-audiovideo' ) );
$scripts->add( 'media-image-widget', "/wp-admin/js/widgets/media-image-widget$suffix.js", array( 'media-widgets' ) );
+ $scripts->add( 'media-gallery-widget', "/wp-admin/js/widgets/media-gallery-widget$suffix.js", array( 'media-widgets' ) );
$scripts->add( 'media-video-widget', "/wp-admin/js/widgets/media-video-widget$suffix.js", array( 'media-widgets', 'media-audiovideo', 'wp-api-request' ) );
$scripts->add( 'text-widgets', "/wp-admin/js/widgets/text-widgets$suffix.js", array( 'jquery', 'backbone', 'editor', 'wp-util', 'wp-a11y' ) );
$scripts->add( 'custom-html-widgets', "/wp-admin/js/widgets/custom-html-widgets$suffix.js", array( 'code-editor', 'jquery', 'backbone', 'wp-util', 'jquery-ui-core', 'wp-a11y' ) );
diff --git a/src/wp-includes/widgets.php b/src/wp-includes/widgets.php
index fe0e058d9a..e4fcc527b7 100644
--- a/src/wp-includes/widgets.php
+++ b/src/wp-includes/widgets.php
@@ -1609,6 +1609,8 @@ function wp_widgets_init() {
register_widget( 'WP_Widget_Media_Image' );
+ register_widget( 'WP_Widget_Media_Gallery' );
+
register_widget( 'WP_Widget_Media_Video' );
register_widget( 'WP_Widget_Meta' );
diff --git a/src/wp-includes/widgets/class-wp-widget-media-gallery.php b/src/wp-includes/widgets/class-wp-widget-media-gallery.php
new file mode 100644
index 0000000000..5c07c4fc6b
--- /dev/null
+++ b/src/wp-includes/widgets/class-wp-widget-media-gallery.php
@@ -0,0 +1,224 @@
+ __( 'Displays an image gallery.' ),
+ 'mime_type' => 'image',
+ ) );
+
+ $this->l10n = array_merge( $this->l10n, array(
+ 'no_media_selected' => __( 'No images selected' ),
+ 'add_media' => _x( 'Select Images', 'label for button in the gallery widget; should not be longer than ~13 characters long' ),
+ 'replace_media' => _x( 'Replace Gallery', 'label for button in the gallery widget; should not be longer than ~13 characters long' ),
+ 'edit_media' => _x( 'Edit Gallery', 'label for button in the gallery widget; should not be longer than ~13 characters long' ),
+ ) );
+ }
+
+ /**
+ * Get schema for properties of a widget instance (item).
+ *
+ * @since 4.9.0
+ *
+ * @see WP_REST_Controller::get_item_schema()
+ * @see WP_REST_Controller::get_additional_fields()
+ * @link https://core.trac.wordpress.org/ticket/35574
+ * @return array Schema for properties.
+ */
+ public function get_instance_schema() {
+ return array(
+ 'title' => array(
+ 'type' => 'string',
+ 'default' => '',
+ 'sanitize_callback' => 'sanitize_text_field',
+ 'description' => __( 'Title for the widget' ),
+ 'should_preview_update' => false,
+ ),
+ 'ids' => array(
+ 'type' => 'array',
+ 'items' => array(
+ 'type' => 'integer',
+ ),
+ 'default' => array(),
+ 'sanitize_callback' => 'wp_parse_id_list',
+ ),
+ 'columns' => array(
+ 'type' => 'integer',
+ 'default' => 3,
+ 'minimum' => 1,
+ 'maximum' => 9,
+ ),
+ 'size' => array(
+ 'type' => 'string',
+ 'enum' => array_merge( get_intermediate_image_sizes(), array( 'full', 'custom' ) ),
+ 'default' => 'thumbnail',
+ ),
+ 'link_type' => array(
+ 'type' => 'string',
+ 'enum' => array( 'none', 'file', 'post' ),
+ 'default' => 'none',
+ 'media_prop' => 'link',
+ 'should_preview_update' => false,
+ ),
+ 'orderby_random' => array(
+ 'type' => 'boolean',
+ 'default' => false,
+ 'media_prop' => '_orderbyRandom',
+ 'should_preview_update' => false,
+ ),
+ );
+ }
+
+ /**
+ * Render the media on the frontend.
+ *
+ * @since 4.9.0
+ *
+ * @param array $instance Widget instance props.
+ * @return void
+ */
+ public function render_media( $instance ) {
+ $instance = array_merge( wp_list_pluck( $this->get_instance_schema(), 'default' ), $instance );
+
+ $shortcode_atts = array(
+ 'ids' => $instance['ids'],
+ 'columns' => $instance['columns'],
+ 'link' => $instance['link_type'],
+ 'size' => $instance['size'],
+ );
+
+ // @codeCoverageIgnoreStart
+ if ( $instance['orderby_random'] ) {
+ $shortcode_atts['orderby'] = 'rand';
+ }
+
+ // @codeCoverageIgnoreEnd
+ echo gallery_shortcode( $shortcode_atts );
+ }
+
+ /**
+ * Loads the required media files for the media manager and scripts for media widgets.
+ *
+ * @since 4.9.0
+ */
+ public function enqueue_admin_scripts() {
+ parent::enqueue_admin_scripts();
+
+ $handle = 'media-gallery-widget';
+ wp_enqueue_script( $handle );
+
+ $exported_schema = array();
+ foreach ( $this->get_instance_schema() as $field => $field_schema ) {
+ $exported_schema[ $field ] = wp_array_slice_assoc( $field_schema, array( 'type', 'default', 'enum', 'minimum', 'format', 'media_prop', 'should_preview_update', 'items' ) );
+ }
+ wp_add_inline_script(
+ $handle,
+ sprintf(
+ 'wp.mediaWidgets.modelConstructors[ %s ].prototype.schema = %s;',
+ wp_json_encode( $this->id_base ),
+ wp_json_encode( $exported_schema )
+ )
+ );
+
+ wp_add_inline_script(
+ $handle,
+ sprintf(
+ '
+ wp.mediaWidgets.controlConstructors[ %1$s ].prototype.mime_type = %2$s;
+ _.extend( wp.mediaWidgets.controlConstructors[ %1$s ].prototype.l10n, %3$s );
+ ',
+ wp_json_encode( $this->id_base ),
+ wp_json_encode( $this->widget_options['mime_type'] ),
+ wp_json_encode( $this->l10n )
+ )
+ );
+ }
+
+ /**
+ * Render form template scripts.
+ *
+ * @since 4.9.0
+ */
+ public function render_control_template_scripts() {
+ parent::render_control_template_scripts();
+ ?>
+
+ get_field_name( $name ) ); ?>"
id="get_field_id( $name ) ); // Needed specifically by wpWidgets.appendTitle(). ?>"
- value=""
+ value=""
/>
-
-
+
+
+
+