Skip to content

Instantly share code, notes, and snippets.

@westonruter
Last active April 9, 2019 14:51
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save westonruter/854aba61243ef08a2e3a2a0e721930dc to your computer and use it in GitHub Desktop.
Save westonruter/854aba61243ef08a2e3a2a0e721930dc to your computer and use it in GitHub Desktop.
<?php
/**
* Widget API: WP_Media_Widget class
*
* @package WordPress
* @subpackage Widgets
* @since 4.8.0
*/
/**
* Core class that implements a media widget.
*
* @since 4.8.0
*
* @see WP_Widget
*/
class WP_Media_Widget extends WP_Widget {
/**
* Constructor.
*
* @since 4.8.0
* @access public
*
* @param string $id_base Optional Base ID for the widget, lowercase and unique. If left empty,
* a portion of the widget's class name will be used Has to be unique.
* @param string $name Optional. Name for the widget displayed on the configuration page.
* Default empty.
* @param array $widget_options Optional. Widget options. See wp_register_sidebar_widget() for
* information on accepted arguments. Default empty array.
* @param array $control_options Optional. Widget control options. See wp_register_widget_control()
* for information on accepted arguments. Default empty array.
*/
public function __construct( $id_base = '', $name = '', $widget_options = array(), $control_options = array() ) {
$widget_opts = wp_parse_args( $widget_options, array(
'classname' => 'widget_media',
'description' => __( 'Display media such as images, video, or audio in your sidebar.' ),
'customize_selective_refresh' => true,
) );
$control_opts = wp_parse_args( $control_options, array() );
parent::__construct(
$id_base ? $id_base : 'wp-media-widget',
$name ? $name : __( 'Media' ),
$widget_opts,
$control_opts
);
add_action( 'admin_enqueue_scripts', array( $this, 'enqueue_admin_styles' ) );
add_action( 'admin_enqueue_scripts', array( $this, 'enqueue_admin_scripts' ) );
}
/**
* Displays the widget on the front-end.
*
* @since 4.8.0
* @access public
*
* @see WP_Widget::widget()
*
* @param array $args Display arguments including before_title, after_title, before_widget, and after_widget.
* @param array $instance Saved setting from the database.
*/
public function widget( $args, $instance ) {
echo $args['before_widget'];
if ( ! empty( $instance['title'] ) ) {
echo $args['before_title'] . $instance['title'] . $args['after_title'];
}
if ( ! empty( $instance['description'] ) ) {
echo '<p class="attachment-description align' . $instance['align'] . '">' . $instance['description'] . '</p>';
}
if ( ! empty( $instance['link'] ) ) {
if ( 'file' === $instance['link'] ) {
$url = wp_get_attachment_url( $instance['id'] );
$selectedLink = $url;
} else if ( 'post' === $instance['link'] ) {
$url = get_attachment_link( $instance['id'] );
$selectedLink = $url;
} else {
$selectedLink = '';
}
}
// Build the media output.
$media_output = '';
if ( ! empty( $selectedLink ) ) {
$media_output .= '<a href="' . $selectedLink . '">';
}
if ( ! empty( $instance['id'] ) ) {
if ( $attachment = get_post( $instance['id'] ) ) {
$attrs = array();
if ( ! empty( $instance['title'] ) ) {
$attrs['title'] = $instance['title'];
}
// Image.
if ( wp_attachment_is_image( $attachment ) ) {
$media_output .= $this->get_attachment_image( $instance['id'], $instance['size'], array(
'id' => $args['widget_id'],
'align' => $instance['align'],
'title' => $attachment->post_title,
'caption' => $attachment->post_excerpt,
) );
// Audio.
} elseif ( wp_attachment_is( 'audio', $attachment ) ) {
if ( ! empty( $selectedLink ) ) {
$media_output .= $attachment->post_title;
} else {
$media_output .= $this->get_attachment_audio( $attachment->ID, array() );
}
// Video.
} elseif ( wp_attachment_is( 'video', $attachment ) ) {
if ( ! empty( $selectedLink ) ) {
$media_output .= $attachment->post_title;
} else {
$media_output .= $this->get_attachment_video( $attachment->ID, array() );
}
}
}
}
if ( ! empty( $selectedLink ) ) {
$media_output .= '</a>';
}
echo $media_output;
echo $args['after_widget'];
}
/**
* Sanitizes the widget form values as they are saved.
*
* @since 4.8.0
* @access public
*
* @see WP_Widget::update()
*
* @param array $new_instance Values just sent to be saved.
* @param array $old_instance Previously saved values from database.
* @return array Updated safe values to be saved.
*/
function update( $new_instance, $old_instance ) {
$instance = $old_instance;
// ID, title
$instance['id'] = (int) $new_instance['id'];
$instance['title'] = sanitize_text_field( $new_instance['title'] );
// Everything else.
$instance['align'] = sanitize_text_field( $new_instance['align'] );
$instance['size'] = sanitize_text_field( $new_instance['size'] );
$instance['link'] = sanitize_text_field( $new_instance['link'] );
return $instance;
}
/**
* Renders an image attachment preview.
*
* @since 4.8.0
* @access public
*
* @param WP_Post $attachment Attachment object.
* @param string $widget_id Widget ID.
* @param array $instance Current widget instance arguments.
*/
public function render_image( $attachment, $widget_id, $instance ) {
$attrs = array(
'id' => $widget_id,
'align' => $instance['align'],
'title' => $attachment->post_title,
'caption' => $attachment->post_excerpt,
);
// If an image id is saved for this widget, display the image using `wp_get_attachment_image()`.
echo '<div class="media-widget-admin-preview">';
echo $this->get_attachment_image( $attachment->ID, $instance['size'], $attrs );
echo '</div>';
}
/**
* Get an elmeent reprensenting an image attachment
*
* @since 4.8.0
* @access private
*
* @param int $attachment_id Image attachment ID.
* @param string $size Image size. Default value: 'medium'
* @param array $attrs Attributes for the markup.
*/
private function get_attachment_image( $attachment_id, $size = 'medium', $attrs = array() ) {
$img_attrs = array(
'data-id' => $attrs['id'],
'title' => $attrs['title'],
'class' => 'image wp-image-' . $attachment_id,
);
if ( empty( $attrs['caption'] ) ) {
$img_attrs['class'] .= ' ' . 'align' . $attrs['align'];
}
$image = wp_get_attachment_image( $attachment_id, $size, false, $img_attrs);
if ( empty( $attrs['caption'] ) ) {
return $image;
}
$attrs['id'] .= '-caption';
$attrs['width'] = get_option( $size . '_size_w' );
return img_caption_shortcode( $attrs, $image );
}
/**
* Renders an audio attachment preview.
*
* @since 4.8.0
* @access public
*
* @param WP_Post $attachment Attachment object.
* @param string $widget_id Widget ID.
* @param array $instance Current widget instance arguments.
*/
public function render_audio( $attachment, $widget_id, $instance ) {
echo '<div class="media-widget-admin-preview">';
if ( 'embed' === $instance['link'] ) {
echo $this->get_attachment_audio( $attachment->ID );
} else {
echo '<a href="#">' . $attachment->post_title .'</a>';
}
echo '</div>';
}
/**
* Get an audio elmeent reprensenting a video attachment
*
* @since 4.8.0
* @access private
*
* @param int $attachment_id Audio attachment ID.
* @param array Attributes for the audio markup.
*/
private function get_attachment_audio( $attachment_id, $attrs = array() ) {
$output = wp_audio_shortcode( array(
'src' => wp_get_attachment_url( $attachment_id )
) );
return $output;
}
/**
* Renders a video attachment preview.
*
* @since 4.8.0
* @access public
*
* @param WP_Post $attachment Attachment object.
* @param string $widget_id Widget ID.
* @param array $instance Current widget instance arguments.
*/
public function render_video( $attachment, $widget_id, $instance ) {
echo '<div class="media-widget-admin-preview">';
if ( 'embed' === $instance['link'] ) {
echo $this->get_attachment_video( $attachment->ID );
} else {
echo '<a href="#">' . $attachment->post_title .'</a>';
}
echo '</div>';
}
/**
* Get a video elmeent reprensenting a video attachment
*
* @since 4.8.0
* @access private
*
* @param int $attachment_id Video attachment ID.
* @param array Attributes for the video markup.
*/
private function get_attachment_video( $attachment_id, $attrs = array() ) {
$output = wp_video_shortcode( array(
'src' => wp_get_attachment_url( $attachment_id )
) );
return $output;
}
/**
* Outputs the settings update form.
*
* @since 4.8.0
* @access public
*
* @param array $saved_instance Current settings.
* @return string Default return is 'noform'.
*/
public function form( $saved_instance ) {
$defaults = array(
'title' => '',
// Attachment props.
'id' => '',
'align' => '',
'size' => '',
'link' => '',
);
$instance = wp_parse_args( (array) $saved_instance, $defaults );
$attachment = empty( $instance['id'] ) ? null: get_post( $instance['id'] );
$widget_id = $this->id;
?>
<div class="<?php echo esc_attr( $widget_id ); ?> media-widget-preview">
<p>
<label for="<?php echo $this->get_field_id( 'title' ); ?>"><?php esc_html_e( 'Title:' ); ?></label>
<input class="widefat" id="<?php echo $this->get_field_id( 'title' ); ?>" name="<?php echo $this->get_field_name( 'title' ); ?>" type="text" value="<?php echo esc_attr( $instance['title'] ); ?>" />
</p>
<p>
<?php esc_html_e( 'Add an image, video, or audio to your sidebar.' ); ?>
</p>
<?php
if ( $attachment ) {
if ( wp_attachment_is_image( $attachment ) ) {
$this->render_image( $attachment, $widget_id, $instance );
} elseif ( wp_attachment_is( 'audio', $attachment ) ) {
$this->render_audio( $attachment, $widget_id, $instance );
} elseif ( wp_attachment_is( 'video', $attachment ) ) {
$this->render_video( $attachment, $widget_id, $instance );
}
}
?>
<p class="extras">
</p>
<p>
<button data-id="<?php echo esc_attr( $widget_id ); ?>" class="button select-media widefat">
<?php $attachment ? esc_html_e( 'Change Media' ) : esc_html_e( 'Select Media' ); ?>
</button>
</p>
<?php
// Use hidden form fields to capture the attachment details from the media manager.
unset( $instance['title'] );
?>
<?php foreach ( $instance as $name => $value ) : ?>
<input type="hidden" id="<?php echo esc_attr( $this->get_field_id( $name ) ); ?>" name="<?php echo esc_attr( $this->get_field_name( $name ) ); ?>" value="<?php echo esc_attr( $value ); ?>" />
<?php endforeach; ?>
</div>
<?php
}
/**
* Registers the stylesheet for handling the widget in the back-end.
*
* @since 4.8.0
* @access public
*/
public function enqueue_admin_styles() {
wp_enqueue_style( 'wp-media-widget-styles' );
}
/**
* Registers the scripts for handling the widget in the back-end.
*
* @since 4.8.0
* @access public
*/
public function enqueue_admin_scripts() {
global $pagenow;
// Bail if we are not in the widgets or customize screens.
if ( ! ( 'widgets.php' == $pagenow || 'customize.php' == $pagenow ) ) {
return;
}
// Load the required media files for the media manager.
wp_enqueue_media();
// Register, localize and enqueue custom JS.
wp_enqueue_script( 'wp-media-widget' );
wp_localize_script( 'wp-media-widget', '_mediaWidgetl10n',
array(
'title' => __( 'Select an Image' ),
'button' => __( 'Insert Image' ),
'select-media' => __( 'Select Media' ),
'change-media' => __( 'Change Media' ),
'add-to-widget' => __( 'Add to widget' ),
)
);
add_action( 'admin_print_footer_scripts', array( $this, 'admin_print_footer_scripts' ) );
}
/**
* Prints footer scripts.
*
* @since 4.8.0
* @access public
*/
public function admin_print_footer_scripts() {
?>
<script type="text/html" id="tmpl-wp-media-widget-audio">
<?php wp_underscore_audio_template() ?>
</script>
<script type="text/html" id="tmpl-wp-media-widget-video">
<?php wp_underscore_video_template() ?>
</script>
<?php
}
}
.media-widget-preview .button {
text-align: center
}
.media-widget-preview .wp-caption {
max-width: 100%;
margin: 0;
}
.media-widget-preview .image {
height: auto;
max-width: 100%;
cursor: pointer;
}
.media-widget-preview .aligncenter {
display: block;
margin: 0 auto;
text-align: center
}
/**
* @since 4.8.0
*
* @package WP_Media_Widget
*/
( function ( $ ) {
var frame, widgetFrame;
function translate( key, defaultText ) {
return ( window._mediaWidgetl10n && _mediaWidgetl10n[ key ] ) || defaultText;
}
frame = {
buttonId: '.media-widget-preview .button',
defaultProps: {
id: '',
align: '',
size: '',
link: '',
},
init: function() {
$( frame.buttonId )
.off( 'click.mediaWidget' )
.on( 'click.mediaWidget', frame.openMediaManager );
frame.bindImageClick();
wp.mediaelement.initialize();
},
bindImageClick: function() {
$( '.media-widget-preview .image' )
.off( 'click.mediaWidget' )
.on( 'click.mediaWidget', frame.openMediaManager );
},
openMediaManager: function( event ) {
event.preventDefault();
var widgetId = $( event.target ).data( 'id' );
// Create the media frame.
widgetFrame = wp.media( {
button: {
text: translate( 'add-to-widget', 'Add to widget' ), // Text of the submit button.
},
states: new wp.media.controller.Library( {
library: wp.media.query(),
title: translate( 'select-media', 'Select Media' ), // Media frame title
multiple: false,
priority: 20,
display: true, // attachment display setting
filterable: 'all',
} ),
} );
// Populate previously selected media when the media frame is opened.
widgetFrame.on( 'open', function() {
var selection = widgetFrame.state().get( 'selection' ),
ids = $( '#widget-' + widgetId + '-id' ).val().split(',');
if ( ids[0] > 0 ) {
ids.forEach( function( id ) {
var attachment = wp.media.attachment( id );
attachment.fetch();
selection.add( attachment ? [ attachment ] : [] );
} );
}
} );
// Render the attachment details.
widgetFrame.on( 'select', function() {
var props, attachment;
// Only try to render the attachment details if a selection was made.
if ( widgetFrame.state().get( 'selection' ).length > 0 ) {
props = widgetFrame.content.get( '.attachments-browser' )
.sidebar.get( 'display' ).model.toJSON();
attachment = widgetFrame.state().get( 'selection' ).first().toJSON();
frame.renderFormView( widgetId, props, attachment );
}
} );
widgetFrame.open( widgetId );
},
/**
* Renders the attachment details from the media modal into the widget.
*
* @param {String} widgetId
* @param {Object} props Attachment Display Settings (align, link, size, etc).
* @param {Object} attachment Attachment Details (title, description, caption, url, sizes, etc).
*/
renderFormView: function( widgetId, props, attachment ) {
// Start with container elements for the widgets page, customizer controls, and customizer preview.
var previewEl,
extras,
formView = $( '.' + widgetId + ', #customize-control-widget_' + widgetId + ', #' + widgetId );
// The widget title bar doesn't update automatically on the Appearance > Widgets page. This fixes that problem.
formView.closest( '.widget' ).find( '.in-widget-title' ).html( ': ' + attachment.title );
formView.find( '.attachment-description' )
[ attachment.description ? 'removeClass' : 'addClass' ]('hidden')
.html( attachment.description );
extras = formView.find( '.extras' );
// Display a preview of the image in the widgets page and customizer controls.
extras.removeClass( 'hidden' );
previewEl = formView.find( '.media-widget-admin-preview' );
if ( ! previewEl ) {
previewEl = $( '<div class="media-widget-admin-preview />' ).insertBefore( extras );
}
previewEl.html( frame.renderMediaElement( widgetId, props, attachment ) );
if ( -1 < $.inArray( attachment.type, [ 'audio', 'video' ] ) ) {
wp.mediaelement.initialize();
} else if ( 'image' === attachment.type ) {
frame.bindImageClick();
}
// Populate form fields with selection data from the media frame.
_.each( _.keys( frame.defaultProps ), function ( key ) {
formView.find( '#widget-' + widgetId + '-' + key ).val( attachment[ key ] || props[ key ] ).trigger( 'change' );
} );
// Trigger a sync to update the widget in the customizer preview.
formView.find( '#widget-' + widgetId + '-url' ).trigger( 'change' );
// Change button text
formView.find( frame.buttonId ).text( translate( 'change-media', 'Change Media' ) );
},
/**
* Renders the media attachment in HTML.
*
* @param {String} widgetId
* @param {Object} props Attachment Display Settings (align, link, size, etc).
* @param {Object} attachment Attachment Details (title, description, caption, url, sizes, etc).
*/
renderMediaElement: function( widgetId, props, attachment ) {
var image;
if ( 'image' === attachment.type ) {
image = $( '<img />' )
.addClass( 'image wp-image' + attachment.id )
.attr( {
'data-id': widgetId,
src: attachment.sizes[ props.size ].url,
title: attachment.title,
alt: attachment.alt,
width: attachment.sizes[ props.size ].width,
height: attachment.sizes[ props.size ].height
} );
if ( attachment.caption ) {
image = $( '<figure />' )
.width( attachment.sizes[ props.size ].width )
.addClass( 'wp-caption' )
.attr( 'id', widgetId + '-caption' )
.append( image );
$( '<figcaption class="wp-caption-text" />' ).text( attachment.caption ).appendTo( image );
}
return image.wrap( '<div />' ).parent().html();
}
if ( 'audio' === attachment.type ) {
if ( 'embed' === props.link ) {
return wp.media.template( 'wp-media-widget-audio' )( {
model: {
src: attachment.url
}
} );
}
return wp.html.string( {
tag: 'a',
content: attachment.title,
attrs: {
href: '#'
}
} );
}
if ( 'video' === attachment.type ) {
if ( 'embed' === props.link ) {
return wp.media.template( 'wp-media-widget-video' )( {
model: {
src: attachment.url,
width: attachment.width,
height: attachment.height
}
} );
}
return wp.html.string( {
tag: 'a',
content: attachment.title,
attrs: {
href: '#'
}
} );
}
// Unknown media type
return '';
}
};
$( document )
.ready( frame.init )
.on( 'widget-added widget-updated', frame.init );
window.wp = window.wp || {};
window.wp.MediaWidget = frame;
} )( jQuery );
<?php
/**
* Plugin Name: WP Media Widget
* Version: 0.1.0
* Description: Adding images to your widget areas is a common, yet currently incredibly tedious task -- you need to upload it in your media library, find the url, copy the url, and then manually add an image tag inside of a text widget. That's a lot to ask for a beginner user to do. We should include a default image widget in core to make this task easier.
* Author: WordPress.org
* Plugin URI: https://core.trac.wordpress.org/ticket/32417
* Domain Path: /languages
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License, version 2 or, at
* your discretion, any later version, as published by the Free
* Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
/**
* Register widget scripts.
*
* @param WP_Scripts $scripts
*/
function wp32417_default_scripts( WP_Scripts $scripts ) {
$scripts->add( 'wp-media-widget', plugin_dir_url( __FILE__ ) . 'wp-media-widget.js', array( 'jquery', 'media-models', 'media-views', 'wp-mediaelement' ) );
}
add_action( 'wp_default_scripts', 'wp32417_default_scripts' );
/**
* Register widget styles.
*
* @param WP_Styles $styles
*/
function wp32417_default_styles( WP_Styles $styles ) {
$styles->add( 'wp-media-widget-styles', plugin_dir_url( __FILE__ ) . 'wp-media-widget.css', array( 'media-views' ) );
}
add_action( 'wp_default_styles', 'wp32417_default_styles' );
/**
* Register widget.
*/
function wp32417_widgets_init() {
require_once( __DIR__ . '/class-wp-widget-media.php' );
register_widget( 'WP_Media_Widget' );
}
add_action( 'widgets_init', 'wp32417_widgets_init' );
@westonruter
Copy link
Author

I've cloned this over to its own dedicated GitHub project, so please push additional commits to https://github.com/xwp/wp-core-media-widgets

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment