Файловый менеджер - Редактировать - /home/beautybuzzbeyond/public_html/private/widgets.tar
Назад
class-wp-widget-media.php 0000644 00000036007 14717676577 0011405 0 ustar 00 <?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 */ abstract class WP_Widget_Media extends WP_Widget { /** * Translation labels. * * @since 4.8.0 * @var array */ public $l10n = array( 'add_to_widget' => '', 'replace_media' => '', 'edit_media' => '', 'media_library_state_multi' => '', 'media_library_state_single' => '', 'missing_attachment' => '', 'no_media_selected' => '', 'add_media' => '', ); /** * Whether or not the widget has been registered yet. * * @since 4.8.1 * @var bool */ protected $registered = false; /** * The default widget description. * * @since 6.0.0 * @var string */ protected static $default_description = ''; /** * The default localized strings used by the widget. * * @since 6.0.0 * @var string[] */ protected static $l10n_defaults = array(); /** * Constructor. * * @since 4.8.0 * * @param string $id_base Base ID for the widget, lowercase and unique. * @param string $name Name for the widget displayed on the configuration page. * @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( 'description' => self::get_default_description(), 'customize_selective_refresh' => true, 'show_instance_in_rest' => true, 'mime_type' => '', ) ); $control_opts = wp_parse_args( $control_options, array() ); $this->l10n = array_merge( self::get_l10n_defaults(), array_filter( $this->l10n ) ); parent::__construct( $id_base, $name, $widget_opts, $control_opts ); } /** * Add hooks while registering all widget instances of this widget class. * * @since 4.8.0 * * @param int $number Optional. The unique order number of this widget instance * compared to other instances of the same class. Default -1. */ public function _register_one( $number = -1 ) { parent::_register_one( $number ); if ( $this->registered ) { return; } $this->registered = true; /* * Note that the widgets component in the customizer will also do * the 'admin_print_scripts-widgets.php' action in WP_Customize_Widgets::print_scripts(). */ add_action( 'admin_print_scripts-widgets.php', array( $this, 'enqueue_admin_scripts' ) ); if ( $this->is_preview() ) { add_action( 'wp_enqueue_scripts', array( $this, 'enqueue_preview_scripts' ) ); } /* * Note that the widgets component in the customizer will also do * the 'admin_footer-widgets.php' action in WP_Customize_Widgets::print_footer_scripts(). */ add_action( 'admin_footer-widgets.php', array( $this, 'render_control_template_scripts' ) ); add_filter( 'display_media_states', array( $this, 'display_media_state' ), 10, 2 ); } /** * Get schema for properties of a widget instance (item). * * @since 4.8.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() { $schema = array( 'attachment_id' => array( 'type' => 'integer', 'default' => 0, 'minimum' => 0, 'description' => __( 'Attachment post ID' ), 'media_prop' => 'id', ), 'url' => array( 'type' => 'string', 'default' => '', 'format' => 'uri', 'description' => __( 'URL to the media file' ), ), 'title' => array( 'type' => 'string', 'default' => '', 'sanitize_callback' => 'sanitize_text_field', 'description' => __( 'Title for the widget' ), 'should_preview_update' => false, ), ); /** * Filters the media widget instance schema to add additional properties. * * @since 4.9.0 * * @param array $schema Instance schema. * @param WP_Widget_Media $widget Widget object. */ $schema = apply_filters( "widget_{$this->id_base}_instance_schema", $schema, $this ); return $schema; } /** * Determine if the supplied attachment is for a valid attachment post with the specified MIME type. * * @since 4.8.0 * * @param int|WP_Post $attachment Attachment post ID or object. * @param string $mime_type MIME type. * @return bool Is matching MIME type. */ public function is_attachment_with_mime_type( $attachment, $mime_type ) { if ( empty( $attachment ) ) { return false; } $attachment = get_post( $attachment ); if ( ! $attachment ) { return false; } if ( 'attachment' !== $attachment->post_type ) { return false; } return wp_attachment_is( $mime_type, $attachment ); } /** * Sanitize a token list string, such as used in HTML rel and class attributes. * * @since 4.8.0 * * @link http://w3c.github.io/html/infrastructure.html#space-separated-tokens * @link https://developer.mozilla.org/en-US/docs/Web/API/DOMTokenList * @param string|array $tokens List of tokens separated by spaces, or an array of tokens. * @return string Sanitized token string list. */ public function sanitize_token_list( $tokens ) { if ( is_string( $tokens ) ) { $tokens = preg_split( '/\s+/', trim( $tokens ) ); } $tokens = array_map( 'sanitize_html_class', $tokens ); $tokens = array_filter( $tokens ); return implode( ' ', $tokens ); } /** * Displays the widget on the front-end. * * @since 4.8.0 * * @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 ) { $instance = wp_parse_args( $instance, wp_list_pluck( $this->get_instance_schema(), 'default' ) ); // Short-circuit if no media is selected. if ( ! $this->has_content( $instance ) ) { return; } echo $args['before_widget']; /** This filter is documented in wp-includes/widgets/class-wp-widget-pages.php */ $title = apply_filters( 'widget_title', $instance['title'], $instance, $this->id_base ); if ( $title ) { echo $args['before_title'] . $title . $args['after_title']; } /** * Filters the media widget instance prior to rendering the media. * * @since 4.8.0 * * @param array $instance Instance data. * @param array $args Widget args. * @param WP_Widget_Media $widget Widget object. */ $instance = apply_filters( "widget_{$this->id_base}_instance", $instance, $args, $this ); $this->render_media( $instance ); echo $args['after_widget']; } /** * Sanitizes the widget form values as they are saved. * * @since 4.8.0 * @since 5.9.0 Renamed `$instance` to `$old_instance` to match parent class * for PHP 8 named parameter support. * * @see WP_Widget::update() * @see WP_REST_Request::has_valid_params() * @see WP_REST_Request::sanitize_params() * * @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. */ public function update( $new_instance, $old_instance ) { $schema = $this->get_instance_schema(); foreach ( $schema as $field => $field_schema ) { if ( ! array_key_exists( $field, $new_instance ) ) { continue; } $value = $new_instance[ $field ]; /* * Workaround for rest_validate_value_from_schema() due to the fact that * rest_is_boolean( '' ) === false, while rest_is_boolean( '1' ) is true. */ if ( 'boolean' === $field_schema['type'] && '' === $value ) { $value = false; } if ( true !== rest_validate_value_from_schema( $value, $field_schema, $field ) ) { continue; } $value = rest_sanitize_value_from_schema( $value, $field_schema ); // @codeCoverageIgnoreStart if ( is_wp_error( $value ) ) { continue; // Handle case when rest_sanitize_value_from_schema() ever returns WP_Error as its phpdoc @return tag indicates. } // @codeCoverageIgnoreEnd if ( isset( $field_schema['sanitize_callback'] ) ) { $value = call_user_func( $field_schema['sanitize_callback'], $value ); } if ( is_wp_error( $value ) ) { continue; } $old_instance[ $field ] = $value; } return $old_instance; } /** * Render the media on the frontend. * * @since 4.8.0 * * @param array $instance Widget instance props. */ abstract public function render_media( $instance ); /** * Outputs the settings update form. * * Note that the widget UI itself is rendered with JavaScript via `MediaWidgetControl#render()`. * * @since 4.8.0 * * @see \WP_Widget_Media::render_control_template_scripts() Where the JS template is located. * * @param array $instance Current settings. */ final public function form( $instance ) { $instance_schema = $this->get_instance_schema(); $instance = wp_array_slice_assoc( wp_parse_args( (array) $instance, wp_list_pluck( $instance_schema, 'default' ) ), array_keys( $instance_schema ) ); foreach ( $instance as $name => $value ) : ?> <input type="hidden" data-property="<?php echo esc_attr( $name ); ?>" class="media-widget-instance-property" name="<?php echo esc_attr( $this->get_field_name( $name ) ); ?>" id="<?php echo esc_attr( $this->get_field_id( $name ) ); // Needed specifically by wpWidgets.appendTitle(). ?>" value="<?php echo esc_attr( is_array( $value ) ? implode( ',', $value ) : (string) $value ); ?>" /> <?php endforeach; } /** * Filters the default media display states for items in the Media list table. * * @since 4.8.0 * * @param array $states An array of media states. * @param WP_Post $post The current attachment object. * @return array */ public function display_media_state( $states, $post = null ) { if ( ! $post ) { $post = get_post(); } // Count how many times this attachment is used in widgets. $use_count = 0; foreach ( $this->get_settings() as $instance ) { if ( isset( $instance['attachment_id'] ) && $instance['attachment_id'] === $post->ID ) { ++$use_count; } } if ( 1 === $use_count ) { $states[] = $this->l10n['media_library_state_single']; } elseif ( $use_count > 0 ) { $states[] = sprintf( translate_nooped_plural( $this->l10n['media_library_state_multi'], $use_count ), number_format_i18n( $use_count ) ); } return $states; } /** * Enqueue preview scripts. * * These scripts normally are enqueued just-in-time when a widget is rendered. * In the customizer, however, widgets can be dynamically added and rendered via * selective refresh, and so it is important to unconditionally enqueue them in * case a widget does get added. * * @since 4.8.0 */ public function enqueue_preview_scripts() {} /** * Loads the required scripts and styles for the widget control. * * @since 4.8.0 */ public function enqueue_admin_scripts() { wp_enqueue_media(); wp_enqueue_script( 'media-widgets' ); } /** * Render form template scripts. * * @since 4.8.0 */ public function render_control_template_scripts() { ?> <script type="text/html" id="tmpl-widget-media-<?php echo esc_attr( $this->id_base ); ?>-control"> <# var elementIdPrefix = 'el' + String( Math.random() ) + '_' #> <p> <label for="{{ elementIdPrefix }}title"><?php esc_html_e( 'Title:' ); ?></label> <input id="{{ elementIdPrefix }}title" type="text" class="widefat title"> </p> <div class="media-widget-preview <?php echo esc_attr( $this->id_base ); ?>"> <div class="attachment-media-view"> <button type="button" class="select-media button-add-media not-selected"> <?php echo esc_html( $this->l10n['add_media'] ); ?> </button> </div> </div> <p class="media-widget-buttons"> <button type="button" class="button edit-media selected"> <?php echo esc_html( $this->l10n['edit_media'] ); ?> </button> <?php if ( ! empty( $this->l10n['replace_media'] ) ) : ?> <button type="button" class="button change-media select-media selected"> <?php echo esc_html( $this->l10n['replace_media'] ); ?> </button> <?php endif; ?> </p> <div class="media-widget-fields"> </div> </script> <?php } /** * Resets the cache for the default labels. * * @since 6.0.0 */ public static function reset_default_labels() { self::$default_description = ''; self::$l10n_defaults = array(); } /** * Whether the widget has content to show. * * @since 4.8.0 * * @param array $instance Widget instance props. * @return bool Whether widget has content. */ protected function has_content( $instance ) { return ( $instance['attachment_id'] && 'attachment' === get_post_type( $instance['attachment_id'] ) ) || $instance['url']; } /** * Returns the default description of the widget. * * @since 6.0.0 * * @return string */ protected static function get_default_description() { if ( self::$default_description ) { return self::$default_description; } self::$default_description = __( 'A media item.' ); return self::$default_description; } /** * Returns the default localized strings used by the widget. * * @since 6.0.0 * * @return (string|array)[] */ protected static function get_l10n_defaults() { if ( ! empty( self::$l10n_defaults ) ) { return self::$l10n_defaults; } self::$l10n_defaults = array( 'no_media_selected' => __( 'No media selected' ), 'add_media' => _x( 'Add Media', 'label for button in the media widget' ), 'replace_media' => _x( 'Replace Media', 'label for button in the media widget; should preferably not be longer than ~13 characters long' ), 'edit_media' => _x( 'Edit Media', 'label for button in the media widget; should preferably not be longer than ~13 characters long' ), 'add_to_widget' => __( 'Add to Widget' ), 'missing_attachment' => sprintf( /* translators: %s: URL to media library. */ __( 'That file cannot be found. Check your <a href="%s">media library</a> and make sure it was not deleted.' ), esc_url( admin_url( 'upload.php' ) ) ), /* translators: %d: Widget count. */ 'media_library_state_multi' => _n_noop( 'Media Widget (%d)', 'Media Widget (%d)' ), 'media_library_state_single' => __( 'Media Widget' ), 'unsupported_file_type' => __( 'Looks like this is not the correct kind of file. Please link to an appropriate file instead.' ), ); return self::$l10n_defaults; } } class-wp-widget-rss.php 0000644 00000012174 14717676577 0011134 0 ustar 00 <?php /** * Widget API: WP_Widget_RSS class * * @package WordPress * @subpackage Widgets * @since 4.4.0 */ /** * Core class used to implement a RSS widget. * * @since 2.8.0 * * @see WP_Widget */ class WP_Widget_RSS extends WP_Widget { /** * Sets up a new RSS widget instance. * * @since 2.8.0 */ public function __construct() { $widget_ops = array( 'description' => __( 'Entries from any RSS or Atom feed.' ), 'customize_selective_refresh' => true, 'show_instance_in_rest' => true, ); $control_ops = array( 'width' => 400, 'height' => 200, ); parent::__construct( 'rss', __( 'RSS' ), $widget_ops, $control_ops ); } /** * Outputs the content for the current RSS widget instance. * * @since 2.8.0 * * @param array $args Display arguments including 'before_title', 'after_title', * 'before_widget', and 'after_widget'. * @param array $instance Settings for the current RSS widget instance. */ public function widget( $args, $instance ) { if ( isset( $instance['error'] ) && $instance['error'] ) { return; } $url = ! empty( $instance['url'] ) ? $instance['url'] : ''; while ( ! empty( $url ) && stristr( $url, 'http' ) !== $url ) { $url = substr( $url, 1 ); } if ( empty( $url ) ) { return; } // Self-URL destruction sequence. if ( in_array( untrailingslashit( $url ), array( site_url(), home_url() ), true ) ) { return; } $rss = fetch_feed( $url ); $title = $instance['title']; $desc = ''; $link = ''; if ( ! is_wp_error( $rss ) ) { $desc = esc_attr( strip_tags( html_entity_decode( $rss->get_description(), ENT_QUOTES, get_option( 'blog_charset' ) ) ) ); if ( empty( $title ) ) { $title = strip_tags( $rss->get_title() ); } $link = strip_tags( $rss->get_permalink() ); while ( ! empty( $link ) && stristr( $link, 'http' ) !== $link ) { $link = substr( $link, 1 ); } } if ( empty( $title ) ) { $title = ! empty( $desc ) ? $desc : __( 'Unknown Feed' ); } /** This filter is documented in wp-includes/widgets/class-wp-widget-pages.php */ $title = apply_filters( 'widget_title', $title, $instance, $this->id_base ); if ( $title ) { $feed_link = ''; $feed_url = strip_tags( $url ); $feed_icon = includes_url( 'images/rss.png' ); $feed_link = sprintf( '<a class="rsswidget rss-widget-feed" href="%1$s"><img class="rss-widget-icon" style="border:0" width="14" height="14" src="%2$s" alt="%3$s"%4$s /></a> ', esc_url( $feed_url ), esc_url( $feed_icon ), esc_attr__( 'RSS' ), ( wp_lazy_loading_enabled( 'img', 'rss_widget_feed_icon' ) ? ' loading="lazy"' : '' ) ); /** * Filters the classic RSS widget's feed icon link. * * Themes can remove the icon link by using `add_filter( 'rss_widget_feed_link', '__return_empty_string' );`. * * @since 5.9.0 * * @param string|false $feed_link HTML for link to RSS feed. * @param array $instance Array of settings for the current widget. */ $feed_link = apply_filters( 'rss_widget_feed_link', $feed_link, $instance ); $title = $feed_link . '<a class="rsswidget rss-widget-title" href="' . esc_url( $link ) . '">' . esc_html( $title ) . '</a>'; } echo $args['before_widget']; if ( $title ) { echo $args['before_title'] . $title . $args['after_title']; } $format = current_theme_supports( 'html5', 'navigation-widgets' ) ? 'html5' : 'xhtml'; /** This filter is documented in wp-includes/widgets/class-wp-nav-menu-widget.php */ $format = apply_filters( 'navigation_widgets_format', $format ); if ( 'html5' === $format ) { // The title may be filtered: Strip out HTML and make sure the aria-label is never empty. $title = trim( strip_tags( $title ) ); $aria_label = $title ? $title : __( 'RSS Feed' ); echo '<nav aria-label="' . esc_attr( $aria_label ) . '">'; } wp_widget_rss_output( $rss, $instance ); if ( 'html5' === $format ) { echo '</nav>'; } echo $args['after_widget']; if ( ! is_wp_error( $rss ) ) { $rss->__destruct(); } unset( $rss ); } /** * Handles updating settings for the current RSS widget instance. * * @since 2.8.0 * * @param array $new_instance New settings for this instance as input by the user via * WP_Widget::form(). * @param array $old_instance Old settings for this instance. * @return array Updated settings to save. */ public function update( $new_instance, $old_instance ) { $testurl = ( isset( $new_instance['url'] ) && ( ! isset( $old_instance['url'] ) || ( $new_instance['url'] !== $old_instance['url'] ) ) ); return wp_widget_rss_process( $new_instance, $testurl ); } /** * Outputs the settings form for the RSS widget. * * @since 2.8.0 * * @param array $instance Current settings. */ public function form( $instance ) { if ( empty( $instance ) ) { $instance = array( 'title' => '', 'url' => '', 'items' => 10, 'error' => false, 'show_summary' => 0, 'show_author' => 0, 'show_date' => 0, ); } $instance['number'] = $this->number; wp_widget_rss_form( $instance ); } } class-wp-widget-search.php 0000644 00000005244 14717676577 0011572 0 ustar 00 <?php /** * Widget API: WP_Widget_Search class * * @package WordPress * @subpackage Widgets * @since 4.4.0 */ /** * Core class used to implement a Search widget. * * @since 2.8.0 * * @see WP_Widget */ class WP_Widget_Search extends WP_Widget { /** * Sets up a new Search widget instance. * * @since 2.8.0 */ public function __construct() { $widget_ops = array( 'classname' => 'widget_search', 'description' => __( 'A search form for your site.' ), 'customize_selective_refresh' => true, 'show_instance_in_rest' => true, ); parent::__construct( 'search', _x( 'Search', 'Search widget' ), $widget_ops ); } /** * Outputs the content for the current Search widget instance. * * @since 2.8.0 * * @param array $args Display arguments including 'before_title', 'after_title', * 'before_widget', and 'after_widget'. * @param array $instance Settings for the current Search widget instance. */ public function widget( $args, $instance ) { $title = ! empty( $instance['title'] ) ? $instance['title'] : ''; /** This filter is documented in wp-includes/widgets/class-wp-widget-pages.php */ $title = apply_filters( 'widget_title', $title, $instance, $this->id_base ); echo $args['before_widget']; if ( $title ) { echo $args['before_title'] . $title . $args['after_title']; } // Use active theme search form if it exists. get_search_form(); echo $args['after_widget']; } /** * Outputs the settings form for the Search widget. * * @since 2.8.0 * * @param array $instance Current settings. */ public function form( $instance ) { $instance = wp_parse_args( (array) $instance, array( 'title' => '' ) ); $title = $instance['title']; ?> <p> <label for="<?php echo $this->get_field_id( 'title' ); ?>"><?php _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( $title ); ?>" /> </p> <?php } /** * Handles updating settings for the current Search widget instance. * * @since 2.8.0 * * @param array $new_instance New settings for this instance as input by the user via * WP_Widget::form(). * @param array $old_instance Old settings for this instance. * @return array Updated settings. */ public function update( $new_instance, $old_instance ) { $instance = $old_instance; $new_instance = wp_parse_args( (array) $new_instance, array( 'title' => '' ) ); $instance['title'] = sanitize_text_field( $new_instance['title'] ); return $instance; } } class-wp-widget-recent-posts.php 0000644 00000013462 14717676577 0012754 0 ustar 00 <?php /** * Widget API: WP_Widget_Recent_Posts class * * @package WordPress * @subpackage Widgets * @since 4.4.0 */ /** * Core class used to implement a Recent Posts widget. * * @since 2.8.0 * * @see WP_Widget */ class WP_Widget_Recent_Posts extends WP_Widget { /** * Sets up a new Recent Posts widget instance. * * @since 2.8.0 */ public function __construct() { $widget_ops = array( 'classname' => 'widget_recent_entries', 'description' => __( 'Your site’s most recent Posts.' ), 'customize_selective_refresh' => true, 'show_instance_in_rest' => true, ); parent::__construct( 'recent-posts', __( 'Recent Posts' ), $widget_ops ); $this->alt_option_name = 'widget_recent_entries'; } /** * Outputs the content for the current Recent Posts widget instance. * * @since 2.8.0 * * @param array $args Display arguments including 'before_title', 'after_title', * 'before_widget', and 'after_widget'. * @param array $instance Settings for the current Recent Posts widget instance. */ public function widget( $args, $instance ) { if ( ! isset( $args['widget_id'] ) ) { $args['widget_id'] = $this->id; } $default_title = __( 'Recent Posts' ); $title = ( ! empty( $instance['title'] ) ) ? $instance['title'] : $default_title; /** This filter is documented in wp-includes/widgets/class-wp-widget-pages.php */ $title = apply_filters( 'widget_title', $title, $instance, $this->id_base ); $number = ( ! empty( $instance['number'] ) ) ? absint( $instance['number'] ) : 5; if ( ! $number ) { $number = 5; } $show_date = isset( $instance['show_date'] ) ? $instance['show_date'] : false; $r = new WP_Query( /** * Filters the arguments for the Recent Posts widget. * * @since 3.4.0 * @since 4.9.0 Added the `$instance` parameter. * * @see WP_Query::get_posts() * * @param array $args An array of arguments used to retrieve the recent posts. * @param array $instance Array of settings for the current widget. */ apply_filters( 'widget_posts_args', array( 'posts_per_page' => $number, 'no_found_rows' => true, 'post_status' => 'publish', 'ignore_sticky_posts' => true, ), $instance ) ); if ( ! $r->have_posts() ) { return; } ?> <?php echo $args['before_widget']; ?> <?php if ( $title ) { echo $args['before_title'] . $title . $args['after_title']; } $format = current_theme_supports( 'html5', 'navigation-widgets' ) ? 'html5' : 'xhtml'; /** This filter is documented in wp-includes/widgets/class-wp-nav-menu-widget.php */ $format = apply_filters( 'navigation_widgets_format', $format ); if ( 'html5' === $format ) { // The title may be filtered: Strip out HTML and make sure the aria-label is never empty. $title = trim( strip_tags( $title ) ); $aria_label = $title ? $title : $default_title; echo '<nav aria-label="' . esc_attr( $aria_label ) . '">'; } ?> <ul> <?php foreach ( $r->posts as $recent_post ) : ?> <?php $post_title = get_the_title( $recent_post->ID ); $title = ( ! empty( $post_title ) ) ? $post_title : __( '(no title)' ); $aria_current = ''; if ( get_queried_object_id() === $recent_post->ID ) { $aria_current = ' aria-current="page"'; } ?> <li> <a href="<?php the_permalink( $recent_post->ID ); ?>"<?php echo $aria_current; ?>><?php echo $title; ?></a> <?php if ( $show_date ) : ?> <span class="post-date"><?php echo get_the_date( '', $recent_post->ID ); ?></span> <?php endif; ?> </li> <?php endforeach; ?> </ul> <?php if ( 'html5' === $format ) { echo '</nav>'; } echo $args['after_widget']; } /** * Handles updating the settings for the current Recent Posts widget instance. * * @since 2.8.0 * * @param array $new_instance New settings for this instance as input by the user via * WP_Widget::form(). * @param array $old_instance Old settings for this instance. * @return array Updated settings to save. */ public function update( $new_instance, $old_instance ) { $instance = $old_instance; $instance['title'] = sanitize_text_field( $new_instance['title'] ); $instance['number'] = (int) $new_instance['number']; $instance['show_date'] = isset( $new_instance['show_date'] ) ? (bool) $new_instance['show_date'] : false; return $instance; } /** * Outputs the settings form for the Recent Posts widget. * * @since 2.8.0 * * @param array $instance Current settings. */ public function form( $instance ) { $title = isset( $instance['title'] ) ? esc_attr( $instance['title'] ) : ''; $number = isset( $instance['number'] ) ? absint( $instance['number'] ) : 5; $show_date = isset( $instance['show_date'] ) ? (bool) $instance['show_date'] : false; ?> <p> <label for="<?php echo $this->get_field_id( 'title' ); ?>"><?php _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 $title; ?>" /> </p> <p> <label for="<?php echo $this->get_field_id( 'number' ); ?>"><?php _e( 'Number of posts to show:' ); ?></label> <input class="tiny-text" id="<?php echo $this->get_field_id( 'number' ); ?>" name="<?php echo $this->get_field_name( 'number' ); ?>" type="number" step="1" min="1" value="<?php echo $number; ?>" size="3" /> </p> <p> <input class="checkbox" type="checkbox"<?php checked( $show_date ); ?> id="<?php echo $this->get_field_id( 'show_date' ); ?>" name="<?php echo $this->get_field_name( 'show_date' ); ?>" /> <label for="<?php echo $this->get_field_id( 'show_date' ); ?>"><?php _e( 'Display post date?' ); ?></label> </p> <?php } } class-wp-nav-menu-widget.php 0000644 00000015061 14717676577 0012051 0 ustar 00 <?php /** * Widget API: WP_Nav_Menu_Widget class * * @package WordPress * @subpackage Widgets * @since 4.4.0 */ /** * Core class used to implement the Navigation Menu widget. * * @since 3.0.0 * * @see WP_Widget */ class WP_Nav_Menu_Widget extends WP_Widget { /** * Sets up a new Navigation Menu widget instance. * * @since 3.0.0 */ public function __construct() { $widget_ops = array( 'description' => __( 'Add a navigation menu to your sidebar.' ), 'customize_selective_refresh' => true, 'show_instance_in_rest' => true, ); parent::__construct( 'nav_menu', __( 'Navigation Menu' ), $widget_ops ); } /** * Outputs the content for the current Navigation Menu widget instance. * * @since 3.0.0 * * @param array $args Display arguments including 'before_title', 'after_title', * 'before_widget', and 'after_widget'. * @param array $instance Settings for the current Navigation Menu widget instance. */ public function widget( $args, $instance ) { // Get menu. $nav_menu = ! empty( $instance['nav_menu'] ) ? wp_get_nav_menu_object( $instance['nav_menu'] ) : false; if ( ! $nav_menu ) { return; } $default_title = __( 'Menu' ); $title = ! empty( $instance['title'] ) ? $instance['title'] : ''; /** This filter is documented in wp-includes/widgets/class-wp-widget-pages.php */ $title = apply_filters( 'widget_title', $title, $instance, $this->id_base ); echo $args['before_widget']; if ( $title ) { echo $args['before_title'] . $title . $args['after_title']; } $format = current_theme_supports( 'html5', 'navigation-widgets' ) ? 'html5' : 'xhtml'; /** * Filters the HTML format of widgets with navigation links. * * @since 5.5.0 * * @param string $format The type of markup to use in widgets with navigation links. * Accepts 'html5', 'xhtml'. */ $format = apply_filters( 'navigation_widgets_format', $format ); if ( 'html5' === $format ) { // The title may be filtered: Strip out HTML and make sure the aria-label is never empty. $title = trim( strip_tags( $title ) ); $aria_label = $title ? $title : $default_title; $nav_menu_args = array( 'fallback_cb' => '', 'menu' => $nav_menu, 'container' => 'nav', 'container_aria_label' => $aria_label, 'items_wrap' => '<ul id="%1$s" class="%2$s">%3$s</ul>', ); } else { $nav_menu_args = array( 'fallback_cb' => '', 'menu' => $nav_menu, ); } /** * Filters the arguments for the Navigation Menu widget. * * @since 4.2.0 * @since 4.4.0 Added the `$instance` parameter. * * @param array $nav_menu_args { * An array of arguments passed to wp_nav_menu() to retrieve a navigation menu. * * @type callable|bool $fallback_cb Callback to fire if the menu doesn't exist. Default empty. * @type mixed $menu Menu ID, slug, or name. * } * @param WP_Term $nav_menu Nav menu object for the current menu. * @param array $args Display arguments for the current widget. * @param array $instance Array of settings for the current widget. */ wp_nav_menu( apply_filters( 'widget_nav_menu_args', $nav_menu_args, $nav_menu, $args, $instance ) ); echo $args['after_widget']; } /** * Handles updating settings for the current Navigation Menu widget instance. * * @since 3.0.0 * * @param array $new_instance New settings for this instance as input by the user via * WP_Widget::form(). * @param array $old_instance Old settings for this instance. * @return array Updated settings to save. */ public function update( $new_instance, $old_instance ) { $instance = array(); if ( ! empty( $new_instance['title'] ) ) { $instance['title'] = sanitize_text_field( $new_instance['title'] ); } if ( ! empty( $new_instance['nav_menu'] ) ) { $instance['nav_menu'] = (int) $new_instance['nav_menu']; } return $instance; } /** * Outputs the settings form for the Navigation Menu widget. * * @since 3.0.0 * * @global WP_Customize_Manager $wp_customize * * @param array $instance Current settings. */ public function form( $instance ) { global $wp_customize; $title = isset( $instance['title'] ) ? $instance['title'] : ''; $nav_menu = isset( $instance['nav_menu'] ) ? $instance['nav_menu'] : ''; // Get menus. $menus = wp_get_nav_menus(); $empty_menus_style = ''; $not_empty_menus_style = ''; if ( empty( $menus ) ) { $empty_menus_style = ' style="display:none" '; } else { $not_empty_menus_style = ' style="display:none" '; } $nav_menu_style = ''; if ( ! $nav_menu ) { $nav_menu_style = 'display: none;'; } // If no menus exists, direct the user to go and create some. ?> <p class="nav-menu-widget-no-menus-message" <?php echo $not_empty_menus_style; ?>> <?php if ( $wp_customize instanceof WP_Customize_Manager ) { $url = 'javascript: wp.customize.panel( "nav_menus" ).focus();'; } else { $url = admin_url( 'nav-menus.php' ); } printf( /* translators: %s: URL to create a new menu. */ __( 'No menus have been created yet. <a href="%s">Create some</a>.' ), // The URL can be a `javascript:` link, so esc_attr() is used here instead of esc_url(). esc_attr( $url ) ); ?> </p> <div class="nav-menu-widget-form-controls" <?php echo $empty_menus_style; ?>> <p> <label for="<?php echo $this->get_field_id( 'title' ); ?>"><?php _e( 'Title:' ); ?></label> <input type="text" class="widefat" id="<?php echo $this->get_field_id( 'title' ); ?>" name="<?php echo $this->get_field_name( 'title' ); ?>" value="<?php echo esc_attr( $title ); ?>" /> </p> <p> <label for="<?php echo $this->get_field_id( 'nav_menu' ); ?>"><?php _e( 'Select Menu:' ); ?></label> <select id="<?php echo $this->get_field_id( 'nav_menu' ); ?>" name="<?php echo $this->get_field_name( 'nav_menu' ); ?>"> <option value="0"><?php _e( '— Select —' ); ?></option> <?php foreach ( $menus as $menu ) : ?> <option value="<?php echo esc_attr( $menu->term_id ); ?>" <?php selected( $nav_menu, $menu->term_id ); ?>> <?php echo esc_html( $menu->name ); ?> </option> <?php endforeach; ?> </select> </p> <?php if ( $wp_customize instanceof WP_Customize_Manager ) : ?> <p class="edit-selected-nav-menu" style="<?php echo $nav_menu_style; ?>"> <button type="button" class="button"><?php _e( 'Edit Menu' ); ?></button> </p> <?php endif; ?> </div> <?php } } class-wp-widget-tag-cloud.php 0000644 00000015172 14717676577 0012205 0 ustar 00 <?php /** * Widget API: WP_Widget_Tag_Cloud class * * @package WordPress * @subpackage Widgets * @since 4.4.0 */ /** * Core class used to implement a Tag cloud widget. * * @since 2.8.0 * * @see WP_Widget */ class WP_Widget_Tag_Cloud extends WP_Widget { /** * Sets up a new Tag Cloud widget instance. * * @since 2.8.0 */ public function __construct() { $widget_ops = array( 'description' => __( 'A cloud of your most used tags.' ), 'customize_selective_refresh' => true, 'show_instance_in_rest' => true, ); parent::__construct( 'tag_cloud', __( 'Tag Cloud' ), $widget_ops ); } /** * Outputs the content for the current Tag Cloud widget instance. * * @since 2.8.0 * * @param array $args Display arguments including 'before_title', 'after_title', * 'before_widget', and 'after_widget'. * @param array $instance Settings for the current Tag Cloud widget instance. */ public function widget( $args, $instance ) { $current_taxonomy = $this->_get_current_taxonomy( $instance ); if ( ! empty( $instance['title'] ) ) { $title = $instance['title']; } else { if ( 'post_tag' === $current_taxonomy ) { $title = __( 'Tags' ); } else { $tax = get_taxonomy( $current_taxonomy ); $title = $tax->labels->name; } } $default_title = $title; $show_count = ! empty( $instance['count'] ); $tag_cloud = wp_tag_cloud( /** * Filters the taxonomy used in the Tag Cloud widget. * * @since 2.8.0 * @since 3.0.0 Added taxonomy drop-down. * @since 4.9.0 Added the `$instance` parameter. * * @see wp_tag_cloud() * * @param array $args Args used for the tag cloud widget. * @param array $instance Array of settings for the current widget. */ apply_filters( 'widget_tag_cloud_args', array( 'taxonomy' => $current_taxonomy, 'echo' => false, 'show_count' => $show_count, ), $instance ) ); if ( empty( $tag_cloud ) ) { return; } /** This filter is documented in wp-includes/widgets/class-wp-widget-pages.php */ $title = apply_filters( 'widget_title', $title, $instance, $this->id_base ); echo $args['before_widget']; if ( $title ) { echo $args['before_title'] . $title . $args['after_title']; } $format = current_theme_supports( 'html5', 'navigation-widgets' ) ? 'html5' : 'xhtml'; /** This filter is documented in wp-includes/widgets/class-wp-nav-menu-widget.php */ $format = apply_filters( 'navigation_widgets_format', $format ); if ( 'html5' === $format ) { // The title may be filtered: Strip out HTML and make sure the aria-label is never empty. $title = trim( strip_tags( $title ) ); $aria_label = $title ? $title : $default_title; echo '<nav aria-label="' . esc_attr( $aria_label ) . '">'; } echo '<div class="tagcloud">'; echo $tag_cloud; echo "</div>\n"; if ( 'html5' === $format ) { echo '</nav>'; } echo $args['after_widget']; } /** * Handles updating settings for the current Tag Cloud widget instance. * * @since 2.8.0 * * @param array $new_instance New settings for this instance as input by the user via * WP_Widget::form(). * @param array $old_instance Old settings for this instance. * @return array Settings to save or bool false to cancel saving. */ public function update( $new_instance, $old_instance ) { $instance = array(); $instance['title'] = sanitize_text_field( $new_instance['title'] ); $instance['count'] = ! empty( $new_instance['count'] ) ? 1 : 0; $instance['taxonomy'] = stripslashes( $new_instance['taxonomy'] ); return $instance; } /** * Outputs the Tag Cloud widget settings form. * * @since 2.8.0 * * @param array $instance Current settings. */ public function form( $instance ) { $title = ! empty( $instance['title'] ) ? $instance['title'] : ''; $count = isset( $instance['count'] ) ? (bool) $instance['count'] : false; ?> <p> <label for="<?php echo $this->get_field_id( 'title' ); ?>"><?php _e( 'Title:' ); ?></label> <input type="text" class="widefat" id="<?php echo $this->get_field_id( 'title' ); ?>" name="<?php echo $this->get_field_name( 'title' ); ?>" value="<?php echo esc_attr( $title ); ?>" /> </p> <?php $taxonomies = get_taxonomies( array( 'show_tagcloud' => true ), 'object' ); $current_taxonomy = $this->_get_current_taxonomy( $instance ); switch ( count( $taxonomies ) ) { // No tag cloud supporting taxonomies found, display error message. case 0: ?> <input type="hidden" id="<?php echo $this->get_field_id( 'taxonomy' ); ?>" name="<?php echo $this->get_field_name( 'taxonomy' ); ?>" value="" /> <p> <?php _e( 'The tag cloud will not be displayed since there are no taxonomies that support the tag cloud widget.' ); ?> </p> <?php break; // Just a single tag cloud supporting taxonomy found, no need to display a select. case 1: $keys = array_keys( $taxonomies ); $taxonomy = reset( $keys ); ?> <input type="hidden" id="<?php echo $this->get_field_id( 'taxonomy' ); ?>" name="<?php echo $this->get_field_name( 'taxonomy' ); ?>" value="<?php echo esc_attr( $taxonomy ); ?>" /> <?php break; // More than one tag cloud supporting taxonomy found, display a select. default: ?> <p> <label for="<?php echo $this->get_field_id( 'taxonomy' ); ?>"><?php _e( 'Taxonomy:' ); ?></label> <select class="widefat" id="<?php echo $this->get_field_id( 'taxonomy' ); ?>" name="<?php echo $this->get_field_name( 'taxonomy' ); ?>"> <?php foreach ( $taxonomies as $taxonomy => $tax ) : ?> <option value="<?php echo esc_attr( $taxonomy ); ?>" <?php selected( $taxonomy, $current_taxonomy ); ?>> <?php echo esc_html( $tax->labels->name ); ?> </option> <?php endforeach; ?> </select> </p> <?php } if ( count( $taxonomies ) > 0 ) { ?> <p> <input type="checkbox" class="checkbox" id="<?php echo $this->get_field_id( 'count' ); ?>" name="<?php echo $this->get_field_name( 'count' ); ?>" <?php checked( $count, true ); ?> /> <label for="<?php echo $this->get_field_id( 'count' ); ?>"><?php _e( 'Show tag counts' ); ?></label> </p> <?php } } /** * Retrieves the taxonomy for the current Tag cloud widget instance. * * @since 4.4.0 * * @param array $instance Current settings. * @return string Name of the current taxonomy if set, otherwise 'post_tag'. */ public function _get_current_taxonomy( $instance ) { if ( ! empty( $instance['taxonomy'] ) && taxonomy_exists( $instance['taxonomy'] ) ) { return $instance['taxonomy']; } return 'post_tag'; } } class-wp-widget-recent-comments.php 0000644 00000015624 14717676577 0013433 0 ustar 00 <?php /** * Widget API: WP_Widget_Recent_Comments class * * @package WordPress * @subpackage Widgets * @since 4.4.0 */ /** * Core class used to implement a Recent Comments widget. * * @since 2.8.0 * * @see WP_Widget */ class WP_Widget_Recent_Comments extends WP_Widget { /** * Sets up a new Recent Comments widget instance. * * @since 2.8.0 */ public function __construct() { $widget_ops = array( 'classname' => 'widget_recent_comments', 'description' => __( 'Your site’s most recent comments.' ), 'customize_selective_refresh' => true, 'show_instance_in_rest' => true, ); parent::__construct( 'recent-comments', __( 'Recent Comments' ), $widget_ops ); $this->alt_option_name = 'widget_recent_comments'; if ( is_active_widget( false, false, $this->id_base ) || is_customize_preview() ) { add_action( 'wp_head', array( $this, 'recent_comments_style' ) ); } } /** * Outputs the default styles for the Recent Comments widget. * * @since 2.8.0 */ public function recent_comments_style() { /** * Filters the Recent Comments default widget styles. * * @since 3.1.0 * * @param bool $active Whether the widget is active. Default true. * @param string $id_base The widget ID. */ if ( ! current_theme_supports( 'widgets' ) // Temp hack #14876. || ! apply_filters( 'show_recent_comments_widget_style', true, $this->id_base ) ) { return; } $type_attr = current_theme_supports( 'html5', 'style' ) ? '' : ' type="text/css"'; printf( '<style%s>.recentcomments a{display:inline !important;padding:0 !important;margin:0 !important;}</style>', $type_attr ); } /** * Outputs the content for the current Recent Comments widget instance. * * @since 2.8.0 * @since 5.4.0 Creates a unique HTML ID for the `<ul>` element * if more than one instance is displayed on the page. * * @param array $args Display arguments including 'before_title', 'after_title', * 'before_widget', and 'after_widget'. * @param array $instance Settings for the current Recent Comments widget instance. */ public function widget( $args, $instance ) { static $first_instance = true; if ( ! isset( $args['widget_id'] ) ) { $args['widget_id'] = $this->id; } $output = ''; $default_title = __( 'Recent Comments' ); $title = ( ! empty( $instance['title'] ) ) ? $instance['title'] : $default_title; /** This filter is documented in wp-includes/widgets/class-wp-widget-pages.php */ $title = apply_filters( 'widget_title', $title, $instance, $this->id_base ); $number = ( ! empty( $instance['number'] ) ) ? absint( $instance['number'] ) : 5; if ( ! $number ) { $number = 5; } $comments = get_comments( /** * Filters the arguments for the Recent Comments widget. * * @since 3.4.0 * @since 4.9.0 Added the `$instance` parameter. * * @see WP_Comment_Query::query() for information on accepted arguments. * * @param array $comment_args An array of arguments used to retrieve the recent comments. * @param array $instance Array of settings for the current widget. */ apply_filters( 'widget_comments_args', array( 'number' => $number, 'status' => 'approve', 'post_status' => 'publish', ), $instance ) ); $output .= $args['before_widget']; if ( $title ) { $output .= $args['before_title'] . $title . $args['after_title']; } $recent_comments_id = ( $first_instance ) ? 'recentcomments' : "recentcomments-{$this->number}"; $first_instance = false; $format = current_theme_supports( 'html5', 'navigation-widgets' ) ? 'html5' : 'xhtml'; /** This filter is documented in wp-includes/widgets/class-wp-nav-menu-widget.php */ $format = apply_filters( 'navigation_widgets_format', $format ); if ( 'html5' === $format ) { // The title may be filtered: Strip out HTML and make sure the aria-label is never empty. $title = trim( strip_tags( $title ) ); $aria_label = $title ? $title : $default_title; $output .= '<nav aria-label="' . esc_attr( $aria_label ) . '">'; } $output .= '<ul id="' . esc_attr( $recent_comments_id ) . '">'; if ( is_array( $comments ) && $comments ) { // Prime cache for associated posts. (Prime post term cache if we need it for permalinks.) $post_ids = array_unique( wp_list_pluck( $comments, 'comment_post_ID' ) ); _prime_post_caches( $post_ids, strpos( get_option( 'permalink_structure' ), '%category%' ), false ); foreach ( (array) $comments as $comment ) { $output .= '<li class="recentcomments">'; $output .= sprintf( /* translators: Comments widget. 1: Comment author, 2: Post link. */ _x( '%1$s on %2$s', 'widgets' ), '<span class="comment-author-link">' . get_comment_author_link( $comment ) . '</span>', '<a href="' . esc_url( get_comment_link( $comment ) ) . '">' . get_the_title( $comment->comment_post_ID ) . '</a>' ); $output .= '</li>'; } } $output .= '</ul>'; if ( 'html5' === $format ) { $output .= '</nav>'; } $output .= $args['after_widget']; echo $output; } /** * Handles updating settings for the current Recent Comments widget instance. * * @since 2.8.0 * * @param array $new_instance New settings for this instance as input by the user via * WP_Widget::form(). * @param array $old_instance Old settings for this instance. * @return array Updated settings to save. */ public function update( $new_instance, $old_instance ) { $instance = $old_instance; $instance['title'] = sanitize_text_field( $new_instance['title'] ); $instance['number'] = absint( $new_instance['number'] ); return $instance; } /** * Outputs the settings form for the Recent Comments widget. * * @since 2.8.0 * * @param array $instance Current settings. */ public function form( $instance ) { $title = isset( $instance['title'] ) ? $instance['title'] : ''; $number = isset( $instance['number'] ) ? absint( $instance['number'] ) : 5; ?> <p> <label for="<?php echo $this->get_field_id( 'title' ); ?>"><?php _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( $title ); ?>" /> </p> <p> <label for="<?php echo $this->get_field_id( 'number' ); ?>"><?php _e( 'Number of comments to show:' ); ?></label> <input class="tiny-text" id="<?php echo $this->get_field_id( 'number' ); ?>" name="<?php echo $this->get_field_name( 'number' ); ?>" type="number" step="1" min="1" value="<?php echo $number; ?>" size="3" /> </p> <?php } /** * Flushes the Recent Comments widget cache. * * @since 2.8.0 * * @deprecated 4.4.0 Fragment caching was removed in favor of split queries. */ public function flush_widget_cache() { _deprecated_function( __METHOD__, '4.4.0' ); } } class-wp-widget-pages.php 0000644 00000013130 14717676577 0011415 0 ustar 00 <?php /** * Widget API: WP_Widget_Pages class * * @package WordPress * @subpackage Widgets * @since 4.4.0 */ /** * Core class used to implement a Pages widget. * * @since 2.8.0 * * @see WP_Widget */ class WP_Widget_Pages extends WP_Widget { /** * Sets up a new Pages widget instance. * * @since 2.8.0 */ public function __construct() { $widget_ops = array( 'classname' => 'widget_pages', 'description' => __( 'A list of your site’s Pages.' ), 'customize_selective_refresh' => true, 'show_instance_in_rest' => true, ); parent::__construct( 'pages', __( 'Pages' ), $widget_ops ); } /** * Outputs the content for the current Pages widget instance. * * @since 2.8.0 * * @param array $args Display arguments including 'before_title', 'after_title', * 'before_widget', and 'after_widget'. * @param array $instance Settings for the current Pages widget instance. */ public function widget( $args, $instance ) { $default_title = __( 'Pages' ); $title = ! empty( $instance['title'] ) ? $instance['title'] : $default_title; /** * Filters the widget title. * * @since 2.6.0 * * @param string $title The widget title. Default 'Pages'. * @param array $instance Array of settings for the current widget. * @param mixed $id_base The widget ID. */ $title = apply_filters( 'widget_title', $title, $instance, $this->id_base ); $sortby = empty( $instance['sortby'] ) ? 'menu_order' : $instance['sortby']; $exclude = empty( $instance['exclude'] ) ? '' : $instance['exclude']; if ( 'menu_order' === $sortby ) { $sortby = 'menu_order, post_title'; } $output = wp_list_pages( /** * Filters the arguments for the Pages widget. * * @since 2.8.0 * @since 4.9.0 Added the `$instance` parameter. * * @see wp_list_pages() * * @param array $args An array of arguments to retrieve the pages list. * @param array $instance Array of settings for the current widget. */ apply_filters( 'widget_pages_args', array( 'title_li' => '', 'echo' => 0, 'sort_column' => $sortby, 'exclude' => $exclude, ), $instance ) ); if ( ! empty( $output ) ) { echo $args['before_widget']; if ( $title ) { echo $args['before_title'] . $title . $args['after_title']; } $format = current_theme_supports( 'html5', 'navigation-widgets' ) ? 'html5' : 'xhtml'; /** This filter is documented in wp-includes/widgets/class-wp-nav-menu-widget.php */ $format = apply_filters( 'navigation_widgets_format', $format ); if ( 'html5' === $format ) { // The title may be filtered: Strip out HTML and make sure the aria-label is never empty. $title = trim( strip_tags( $title ) ); $aria_label = $title ? $title : $default_title; echo '<nav aria-label="' . esc_attr( $aria_label ) . '">'; } ?> <ul> <?php echo $output; ?> </ul> <?php if ( 'html5' === $format ) { echo '</nav>'; } echo $args['after_widget']; } } /** * Handles updating settings for the current Pages widget instance. * * @since 2.8.0 * * @param array $new_instance New settings for this instance as input by the user via * WP_Widget::form(). * @param array $old_instance Old settings for this instance. * @return array Updated settings to save. */ public function update( $new_instance, $old_instance ) { $instance = $old_instance; $instance['title'] = sanitize_text_field( $new_instance['title'] ); if ( in_array( $new_instance['sortby'], array( 'post_title', 'menu_order', 'ID' ), true ) ) { $instance['sortby'] = $new_instance['sortby']; } else { $instance['sortby'] = 'menu_order'; } $instance['exclude'] = sanitize_text_field( $new_instance['exclude'] ); return $instance; } /** * Outputs the settings form for the Pages widget. * * @since 2.8.0 * * @param array $instance Current settings. */ public function form( $instance ) { // Defaults. $instance = wp_parse_args( (array) $instance, array( 'sortby' => 'post_title', 'title' => '', 'exclude' => '', ) ); ?> <p> <label for="<?php echo esc_attr( $this->get_field_id( 'title' ) ); ?>"><?php _e( 'Title:' ); ?></label> <input class="widefat" id="<?php echo esc_attr( $this->get_field_id( 'title' ) ); ?>" name="<?php echo esc_attr( $this->get_field_name( 'title' ) ); ?>" type="text" value="<?php echo esc_attr( $instance['title'] ); ?>" /> </p> <p> <label for="<?php echo esc_attr( $this->get_field_id( 'sortby' ) ); ?>"><?php _e( 'Sort by:' ); ?></label> <select name="<?php echo esc_attr( $this->get_field_name( 'sortby' ) ); ?>" id="<?php echo esc_attr( $this->get_field_id( 'sortby' ) ); ?>" class="widefat"> <option value="post_title"<?php selected( $instance['sortby'], 'post_title' ); ?>><?php _e( 'Page title' ); ?></option> <option value="menu_order"<?php selected( $instance['sortby'], 'menu_order' ); ?>><?php _e( 'Page order' ); ?></option> <option value="ID"<?php selected( $instance['sortby'], 'ID' ); ?>><?php _e( 'Page ID' ); ?></option> </select> </p> <p> <label for="<?php echo esc_attr( $this->get_field_id( 'exclude' ) ); ?>"><?php _e( 'Exclude:' ); ?></label> <input type="text" value="<?php echo esc_attr( $instance['exclude'] ); ?>" name="<?php echo esc_attr( $this->get_field_name( 'exclude' ) ); ?>" id="<?php echo esc_attr( $this->get_field_id( 'exclude' ) ); ?>" class="widefat" /> <br /> <small><?php _e( 'Page IDs, separated by commas.' ); ?></small> </p> <?php } } class-wp-widget-media-gallery.php 0000644 00000016317 14717676577 0013044 0 ustar 00 <?php /** * Widget API: WP_Widget_Media_Gallery class * * @package WordPress * @subpackage Widgets * @since 4.9.0 */ /** * Core class that implements a gallery widget. * * @since 4.9.0 * * @see WP_Widget_Media * @see WP_Widget */ class WP_Widget_Media_Gallery extends WP_Widget_Media { /** * Constructor. * * @since 4.9.0 */ public function __construct() { parent::__construct( 'media_gallery', __( 'Gallery' ), array( 'description' => __( 'Displays an image gallery.' ), 'mime_type' => 'image', ) ); $this->l10n = array_merge( $this->l10n, array( 'no_media_selected' => __( 'No images selected' ), 'add_media' => _x( 'Add Images', 'label for button in the gallery widget; should not be longer than ~13 characters long' ), 'replace_media' => '', '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() { $schema = 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( 'post', 'file', 'none' ), 'default' => 'post', 'media_prop' => 'link', 'should_preview_update' => false, ), 'orderby_random' => array( 'type' => 'boolean', 'default' => false, 'media_prop' => '_orderbyRandom', 'should_preview_update' => false, ), ); /** This filter is documented in wp-includes/widgets/class-wp-widget-media.php */ $schema = apply_filters( "widget_{$this->id_base}_instance_schema", $schema, $this ); return $schema; } /** * Render the media on the frontend. * * @since 4.9.0 * * @param array $instance Widget instance props. */ public function render_media( $instance ) { $instance = array_merge( wp_list_pluck( $this->get_instance_schema(), 'default' ), $instance ); $shortcode_atts = array_merge( $instance, array( 'link' => $instance['link_type'], ) ); // @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(); ?> <script type="text/html" id="tmpl-wp-media-widget-gallery-preview"> <# var ids = _.filter( data.ids, function( id ) { return ( id in data.attachments ); } ); #> <# if ( ids.length ) { #> <ul class="gallery media-widget-gallery-preview" role="list"> <# _.each( ids, function( id, index ) { #> <# var attachment = data.attachments[ id ]; #> <# if ( index < 6 ) { #> <li class="gallery-item"> <div class="gallery-icon"> <img alt="{{ attachment.alt }}" <# if ( index === 5 && data.ids.length > 6 ) { #> aria-hidden="true" <# } #> <# if ( attachment.sizes.thumbnail ) { #> src="{{ attachment.sizes.thumbnail.url }}" width="{{ attachment.sizes.thumbnail.width }}" height="{{ attachment.sizes.thumbnail.height }}" <# } else { #> src="{{ attachment.url }}" <# } #> <# if ( ! attachment.alt && attachment.filename ) { #> aria-label=" <?php echo esc_attr( sprintf( /* translators: %s: The image file name. */ __( 'The current image has no alternative text. The file name is: %s' ), '{{ attachment.filename }}' ) ); ?> " <# } #> /> <# if ( index === 5 && data.ids.length > 6 ) { #> <div class="gallery-icon-placeholder"> <p class="gallery-icon-placeholder-text" aria-label=" <?php printf( /* translators: %s: The amount of additional, not visible images in the gallery widget preview. */ __( 'Additional images added to this gallery: %s' ), '{{ data.ids.length - 5 }}' ); ?> ">+{{ data.ids.length - 5 }}</p> </div> <# } #> </div> </li> <# } #> <# } ); #> </ul> <# } else { #> <div class="attachment-media-view"> <button type="button" class="placeholder button-add-media"><?php echo esc_html( $this->l10n['add_media'] ); ?></button> </div> <# } #> </script> <?php } /** * Whether the widget has content to show. * * @since 4.9.0 * @access protected * * @param array $instance Widget instance props. * @return bool Whether widget has content. */ protected function has_content( $instance ) { if ( ! empty( $instance['ids'] ) ) { $attachments = wp_parse_id_list( $instance['ids'] ); // Prime attachment post caches. _prime_post_caches( $attachments, false, false ); foreach ( $attachments as $attachment ) { if ( 'attachment' !== get_post_type( $attachment ) ) { return false; } } return true; } return false; } } class-wp-widget-meta.php 0000644 00000007773 14717676577 0011264 0 ustar 00 <?php /** * Widget API: WP_Widget_Meta class * * @package WordPress * @subpackage Widgets * @since 4.4.0 */ /** * Core class used to implement a Meta widget. * * Displays log in/out, RSS feed links, etc. * * @since 2.8.0 * * @see WP_Widget */ class WP_Widget_Meta extends WP_Widget { /** * Sets up a new Meta widget instance. * * @since 2.8.0 */ public function __construct() { $widget_ops = array( 'classname' => 'widget_meta', 'description' => __( 'Login, RSS, & WordPress.org links.' ), 'customize_selective_refresh' => true, 'show_instance_in_rest' => true, ); parent::__construct( 'meta', __( 'Meta' ), $widget_ops ); } /** * Outputs the content for the current Meta widget instance. * * @since 2.8.0 * * @param array $args Display arguments including 'before_title', 'after_title', * 'before_widget', and 'after_widget'. * @param array $instance Settings for the current Meta widget instance. */ public function widget( $args, $instance ) { $default_title = __( 'Meta' ); $title = ! empty( $instance['title'] ) ? $instance['title'] : $default_title; /** This filter is documented in wp-includes/widgets/class-wp-widget-pages.php */ $title = apply_filters( 'widget_title', $title, $instance, $this->id_base ); echo $args['before_widget']; if ( $title ) { echo $args['before_title'] . $title . $args['after_title']; } $format = current_theme_supports( 'html5', 'navigation-widgets' ) ? 'html5' : 'xhtml'; /** This filter is documented in wp-includes/widgets/class-wp-nav-menu-widget.php */ $format = apply_filters( 'navigation_widgets_format', $format ); if ( 'html5' === $format ) { // The title may be filtered: Strip out HTML and make sure the aria-label is never empty. $title = trim( strip_tags( $title ) ); $aria_label = $title ? $title : $default_title; echo '<nav aria-label="' . esc_attr( $aria_label ) . '">'; } ?> <ul> <?php wp_register(); ?> <li><?php wp_loginout(); ?></li> <li><a href="<?php echo esc_url( get_bloginfo( 'rss2_url' ) ); ?>"><?php _e( 'Entries feed' ); ?></a></li> <li><a href="<?php echo esc_url( get_bloginfo( 'comments_rss2_url' ) ); ?>"><?php _e( 'Comments feed' ); ?></a></li> <?php /** * Filters the "WordPress.org" list item HTML in the Meta widget. * * @since 3.6.0 * @since 4.9.0 Added the `$instance` parameter. * * @param string $html Default HTML for the WordPress.org list item. * @param array $instance Array of settings for the current widget. */ echo apply_filters( 'widget_meta_poweredby', sprintf( '<li><a href="%1$s">%2$s</a></li>', esc_url( __( 'https://wordpress.org/' ) ), __( 'WordPress.org' ) ), $instance ); wp_meta(); ?> </ul> <?php if ( 'html5' === $format ) { echo '</nav>'; } echo $args['after_widget']; } /** * Handles updating settings for the current Meta widget instance. * * @since 2.8.0 * * @param array $new_instance New settings for this instance as input by the user via * WP_Widget::form(). * @param array $old_instance Old settings for this instance. * @return array Updated settings to save. */ public function update( $new_instance, $old_instance ) { $instance = $old_instance; $instance['title'] = sanitize_text_field( $new_instance['title'] ); return $instance; } /** * Outputs the settings form for the Meta widget. * * @since 2.8.0 * * @param array $instance Current settings. */ public function form( $instance ) { $instance = wp_parse_args( (array) $instance, array( 'title' => '' ) ); ?> <p> <label for="<?php echo $this->get_field_id( 'title' ); ?>"><?php _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> <?php } } class-wp-widget-custom-html.php 0000644 00000027644 14717676577 0012611 0 ustar 00 <?php /** * Widget API: WP_Widget_Custom_HTML class * * @package WordPress * @subpackage Widgets * @since 4.8.1 */ /** * Core class used to implement a Custom HTML widget. * * @since 4.8.1 * * @see WP_Widget */ class WP_Widget_Custom_HTML extends WP_Widget { /** * Whether or not the widget has been registered yet. * * @since 4.9.0 * @var bool */ protected $registered = false; /** * Default instance. * * @since 4.8.1 * @var array */ protected $default_instance = array( 'title' => '', 'content' => '', ); /** * Sets up a new Custom HTML widget instance. * * @since 4.8.1 */ public function __construct() { $widget_ops = array( 'classname' => 'widget_custom_html', 'description' => __( 'Arbitrary HTML code.' ), 'customize_selective_refresh' => true, 'show_instance_in_rest' => true, ); $control_ops = array( 'width' => 400, 'height' => 350, ); parent::__construct( 'custom_html', __( 'Custom HTML' ), $widget_ops, $control_ops ); } /** * Add hooks for enqueueing assets when registering all widget instances of this widget class. * * @since 4.9.0 * * @param int $number Optional. The unique order number of this widget instance * compared to other instances of the same class. Default -1. */ public function _register_one( $number = -1 ) { parent::_register_one( $number ); if ( $this->registered ) { return; } $this->registered = true; /* * Note that the widgets component in the customizer will also do * the 'admin_print_scripts-widgets.php' action in WP_Customize_Widgets::print_scripts(). */ add_action( 'admin_print_scripts-widgets.php', array( $this, 'enqueue_admin_scripts' ) ); /* * Note that the widgets component in the customizer will also do * the 'admin_footer-widgets.php' action in WP_Customize_Widgets::print_footer_scripts(). */ add_action( 'admin_footer-widgets.php', array( 'WP_Widget_Custom_HTML', 'render_control_template_scripts' ) ); // Note this action is used to ensure the help text is added to the end. add_action( 'admin_head-widgets.php', array( 'WP_Widget_Custom_HTML', 'add_help_text' ) ); } /** * Filters gallery shortcode attributes. * * Prevents all of a site's attachments from being shown in a gallery displayed on a * non-singular template where a $post context is not available. * * @since 4.9.0 * * @param array $attrs Attributes. * @return array Attributes. */ public function _filter_gallery_shortcode_attrs( $attrs ) { if ( ! is_singular() && empty( $attrs['id'] ) && empty( $attrs['include'] ) ) { $attrs['id'] = -1; } return $attrs; } /** * Outputs the content for the current Custom HTML widget instance. * * @since 4.8.1 * * @global WP_Post $post Global post object. * * @param array $args Display arguments including 'before_title', 'after_title', * 'before_widget', and 'after_widget'. * @param array $instance Settings for the current Custom HTML widget instance. */ public function widget( $args, $instance ) { global $post; // Override global $post so filters (and shortcodes) apply in a consistent context. $original_post = $post; if ( is_singular() ) { // Make sure post is always the queried object on singular queries (not from another sub-query that failed to clean up the global $post). $post = get_queried_object(); } else { // Nullify the $post global during widget rendering to prevent shortcodes from running with the unexpected context on archive queries. $post = null; } // Prevent dumping out all attachments from the media library. add_filter( 'shortcode_atts_gallery', array( $this, '_filter_gallery_shortcode_attrs' ) ); $instance = array_merge( $this->default_instance, $instance ); /** This filter is documented in wp-includes/widgets/class-wp-widget-pages.php */ $title = apply_filters( 'widget_title', $instance['title'], $instance, $this->id_base ); // Prepare instance data that looks like a normal Text widget. $simulated_text_widget_instance = array_merge( $instance, array( 'text' => isset( $instance['content'] ) ? $instance['content'] : '', 'filter' => false, // Because wpautop is not applied. 'visual' => false, // Because it wasn't created in TinyMCE. ) ); unset( $simulated_text_widget_instance['content'] ); // Was moved to 'text' prop. /** This filter is documented in wp-includes/widgets/class-wp-widget-text.php */ $content = apply_filters( 'widget_text', $instance['content'], $simulated_text_widget_instance, $this ); // Adds 'noopener' relationship, without duplicating values, to all HTML A elements that have a target. $content = wp_targeted_link_rel( $content ); /** * Filters the content of the Custom HTML widget. * * @since 4.8.1 * * @param string $content The widget content. * @param array $instance Array of settings for the current widget. * @param WP_Widget_Custom_HTML $widget Current Custom HTML widget instance. */ $content = apply_filters( 'widget_custom_html_content', $content, $instance, $this ); // Restore post global. $post = $original_post; remove_filter( 'shortcode_atts_gallery', array( $this, '_filter_gallery_shortcode_attrs' ) ); // Inject the Text widget's container class name alongside this widget's class name for theme styling compatibility. $args['before_widget'] = preg_replace( '/(?<=\sclass=["\'])/', 'widget_text ', $args['before_widget'] ); echo $args['before_widget']; if ( ! empty( $title ) ) { echo $args['before_title'] . $title . $args['after_title']; } echo '<div class="textwidget custom-html-widget">'; // The textwidget class is for theme styling compatibility. echo $content; echo '</div>'; echo $args['after_widget']; } /** * Handles updating settings for the current Custom HTML widget instance. * * @since 4.8.1 * * @param array $new_instance New settings for this instance as input by the user via * WP_Widget::form(). * @param array $old_instance Old settings for this instance. * @return array Settings to save or bool false to cancel saving. */ public function update( $new_instance, $old_instance ) { $instance = array_merge( $this->default_instance, $old_instance ); $instance['title'] = sanitize_text_field( $new_instance['title'] ); if ( current_user_can( 'unfiltered_html' ) ) { $instance['content'] = $new_instance['content']; } else { $instance['content'] = wp_kses_post( $new_instance['content'] ); } return $instance; } /** * Loads the required scripts and styles for the widget control. * * @since 4.9.0 */ public function enqueue_admin_scripts() { $settings = wp_enqueue_code_editor( array( 'type' => 'text/html', 'codemirror' => array( 'indentUnit' => 2, 'tabSize' => 2, ), ) ); wp_enqueue_script( 'custom-html-widgets' ); wp_add_inline_script( 'custom-html-widgets', sprintf( 'wp.customHtmlWidgets.idBases.push( %s );', wp_json_encode( $this->id_base ) ) ); if ( empty( $settings ) ) { $settings = array( 'disabled' => true, ); } wp_add_inline_script( 'custom-html-widgets', sprintf( 'wp.customHtmlWidgets.init( %s );', wp_json_encode( $settings ) ), 'after' ); $l10n = array( 'errorNotice' => array( /* translators: %d: Error count. */ 'singular' => _n( 'There is %d error which must be fixed before you can save.', 'There are %d errors which must be fixed before you can save.', 1 ), /* translators: %d: Error count. */ 'plural' => _n( 'There is %d error which must be fixed before you can save.', 'There are %d errors which must be fixed before you can save.', 2 ), // @todo This is lacking, as some languages have a dedicated dual form. For proper handling of plurals in JS, see #20491. ), ); wp_add_inline_script( 'custom-html-widgets', sprintf( 'jQuery.extend( wp.customHtmlWidgets.l10n, %s );', wp_json_encode( $l10n ) ), 'after' ); } /** * Outputs the Custom HTML widget settings form. * * @since 4.8.1 * @since 4.9.0 The form contains only hidden sync inputs. For the control UI, see `WP_Widget_Custom_HTML::render_control_template_scripts()`. * * @see WP_Widget_Custom_HTML::render_control_template_scripts() * * @param array $instance Current instance. */ public function form( $instance ) { $instance = wp_parse_args( (array) $instance, $this->default_instance ); ?> <input id="<?php echo $this->get_field_id( 'title' ); ?>" name="<?php echo $this->get_field_name( 'title' ); ?>" class="title sync-input" type="hidden" value="<?php echo esc_attr( $instance['title'] ); ?>" /> <textarea id="<?php echo $this->get_field_id( 'content' ); ?>" name="<?php echo $this->get_field_name( 'content' ); ?>" class="content sync-input" hidden><?php echo esc_textarea( $instance['content'] ); ?></textarea> <?php } /** * Render form template scripts. * * @since 4.9.0 */ public static function render_control_template_scripts() { ?> <script type="text/html" id="tmpl-widget-custom-html-control-fields"> <# var elementIdPrefix = 'el' + String( Math.random() ).replace( /\D/g, '' ) + '_' #> <p> <label for="{{ elementIdPrefix }}title"><?php esc_html_e( 'Title:' ); ?></label> <input id="{{ elementIdPrefix }}title" type="text" class="widefat title"> </p> <p> <label for="{{ elementIdPrefix }}content" id="{{ elementIdPrefix }}content-label"><?php esc_html_e( 'Content:' ); ?></label> <textarea id="{{ elementIdPrefix }}content" class="widefat code content" rows="16" cols="20"></textarea> </p> <?php if ( ! current_user_can( 'unfiltered_html' ) ) : ?> <?php $probably_unsafe_html = array( 'script', 'iframe', 'form', 'input', 'style' ); $allowed_html = wp_kses_allowed_html( 'post' ); $disallowed_html = array_diff( $probably_unsafe_html, array_keys( $allowed_html ) ); ?> <?php if ( ! empty( $disallowed_html ) ) : ?> <# if ( data.codeEditorDisabled ) { #> <p> <?php _e( 'Some HTML tags are not permitted, including:' ); ?> <code><?php echo implode( '</code>, <code>', $disallowed_html ); ?></code> </p> <# } #> <?php endif; ?> <?php endif; ?> <div class="code-editor-error-container"></div> </script> <?php } /** * Add help text to widgets admin screen. * * @since 4.9.0 */ public static function add_help_text() { $screen = get_current_screen(); $content = '<p>'; $content .= __( 'Use the Custom HTML widget to add arbitrary HTML code to your widget areas.' ); $content .= '</p>'; if ( 'false' !== wp_get_current_user()->syntax_highlighting ) { $content .= '<p>'; $content .= sprintf( /* translators: 1: Link to user profile, 2: Additional link attributes, 3: Accessibility text. */ __( 'The edit field automatically highlights code syntax. You can disable this in your <a href="%1$s" %2$s>user profile%3$s</a> to work in plain text mode.' ), esc_url( get_edit_profile_url() ), 'class="external-link" target="_blank"', sprintf( '<span class="screen-reader-text"> %s</span>', /* translators: Hidden accessibility text. */ __( '(opens in a new tab)' ) ) ); $content .= '</p>'; $content .= '<p id="editor-keyboard-trap-help-1">' . __( 'When using a keyboard to navigate:' ) . '</p>'; $content .= '<ul>'; $content .= '<li id="editor-keyboard-trap-help-2">' . __( 'In the editing area, the Tab key enters a tab character.' ) . '</li>'; $content .= '<li id="editor-keyboard-trap-help-3">' . __( 'To move away from this area, press the Esc key followed by the Tab key.' ) . '</li>'; $content .= '<li id="editor-keyboard-trap-help-4">' . __( 'Screen reader users: when in forms mode, you may need to press the Esc key twice.' ) . '</li>'; $content .= '</ul>'; } $screen->add_help_tab( array( 'id' => 'custom_html_widget', 'title' => __( 'Custom HTML Widget' ), 'content' => $content, ) ); } } class-wp-widget-categories.php 0000644 00000015537 14717676577 0012460 0 ustar 00 <?php /** * Widget API: WP_Widget_Categories class * * @package WordPress * @subpackage Widgets * @since 4.4.0 */ /** * Core class used to implement a Categories widget. * * @since 2.8.0 * * @see WP_Widget */ class WP_Widget_Categories extends WP_Widget { /** * Sets up a new Categories widget instance. * * @since 2.8.0 */ public function __construct() { $widget_ops = array( 'classname' => 'widget_categories', 'description' => __( 'A list or dropdown of categories.' ), 'customize_selective_refresh' => true, 'show_instance_in_rest' => true, ); parent::__construct( 'categories', __( 'Categories' ), $widget_ops ); } /** * Outputs the content for the current Categories widget instance. * * @since 2.8.0 * @since 4.2.0 Creates a unique HTML ID for the `<select>` element * if more than one instance is displayed on the page. * * @param array $args Display arguments including 'before_title', 'after_title', * 'before_widget', and 'after_widget'. * @param array $instance Settings for the current Categories widget instance. */ public function widget( $args, $instance ) { static $first_dropdown = true; $default_title = __( 'Categories' ); $title = ! empty( $instance['title'] ) ? $instance['title'] : $default_title; /** This filter is documented in wp-includes/widgets/class-wp-widget-pages.php */ $title = apply_filters( 'widget_title', $title, $instance, $this->id_base ); $count = ! empty( $instance['count'] ) ? '1' : '0'; $hierarchical = ! empty( $instance['hierarchical'] ) ? '1' : '0'; $dropdown = ! empty( $instance['dropdown'] ) ? '1' : '0'; echo $args['before_widget']; if ( $title ) { echo $args['before_title'] . $title . $args['after_title']; } $cat_args = array( 'orderby' => 'name', 'show_count' => $count, 'hierarchical' => $hierarchical, ); if ( $dropdown ) { printf( '<form action="%s" method="get">', esc_url( home_url() ) ); $dropdown_id = ( $first_dropdown ) ? 'cat' : "{$this->id_base}-dropdown-{$this->number}"; $first_dropdown = false; echo '<label class="screen-reader-text" for="' . esc_attr( $dropdown_id ) . '">' . $title . '</label>'; $cat_args['show_option_none'] = __( 'Select Category' ); $cat_args['id'] = $dropdown_id; /** * Filters the arguments for the Categories widget drop-down. * * @since 2.8.0 * @since 4.9.0 Added the `$instance` parameter. * * @see wp_dropdown_categories() * * @param array $cat_args An array of Categories widget drop-down arguments. * @param array $instance Array of settings for the current widget. */ wp_dropdown_categories( apply_filters( 'widget_categories_dropdown_args', $cat_args, $instance ) ); echo '</form>'; ob_start(); ?> <script> (function() { var dropdown = document.getElementById( "<?php echo esc_js( $dropdown_id ); ?>" ); function onCatChange() { if ( dropdown.options[ dropdown.selectedIndex ].value > 0 ) { dropdown.parentNode.submit(); } } dropdown.onchange = onCatChange; })(); </script> <?php wp_print_inline_script_tag( wp_remove_surrounding_empty_script_tags( ob_get_clean() ) ); } else { $format = current_theme_supports( 'html5', 'navigation-widgets' ) ? 'html5' : 'xhtml'; /** This filter is documented in wp-includes/widgets/class-wp-nav-menu-widget.php */ $format = apply_filters( 'navigation_widgets_format', $format ); if ( 'html5' === $format ) { // The title may be filtered: Strip out HTML and make sure the aria-label is never empty. $title = trim( strip_tags( $title ) ); $aria_label = $title ? $title : $default_title; echo '<nav aria-label="' . esc_attr( $aria_label ) . '">'; } ?> <ul> <?php $cat_args['title_li'] = ''; /** * Filters the arguments for the Categories widget. * * @since 2.8.0 * @since 4.9.0 Added the `$instance` parameter. * * @param array $cat_args An array of Categories widget options. * @param array $instance Array of settings for the current widget. */ wp_list_categories( apply_filters( 'widget_categories_args', $cat_args, $instance ) ); ?> </ul> <?php if ( 'html5' === $format ) { echo '</nav>'; } } echo $args['after_widget']; } /** * Handles updating settings for the current Categories widget instance. * * @since 2.8.0 * * @param array $new_instance New settings for this instance as input by the user via * WP_Widget::form(). * @param array $old_instance Old settings for this instance. * @return array Updated settings to save. */ public function update( $new_instance, $old_instance ) { $instance = $old_instance; $instance['title'] = sanitize_text_field( $new_instance['title'] ); $instance['count'] = ! empty( $new_instance['count'] ) ? 1 : 0; $instance['hierarchical'] = ! empty( $new_instance['hierarchical'] ) ? 1 : 0; $instance['dropdown'] = ! empty( $new_instance['dropdown'] ) ? 1 : 0; return $instance; } /** * Outputs the settings form for the Categories widget. * * @since 2.8.0 * * @param array $instance Current settings. */ public function form( $instance ) { // Defaults. $instance = wp_parse_args( (array) $instance, array( 'title' => '' ) ); $count = isset( $instance['count'] ) ? (bool) $instance['count'] : false; $hierarchical = isset( $instance['hierarchical'] ) ? (bool) $instance['hierarchical'] : false; $dropdown = isset( $instance['dropdown'] ) ? (bool) $instance['dropdown'] : false; ?> <p> <label for="<?php echo $this->get_field_id( 'title' ); ?>"><?php _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> <input type="checkbox" class="checkbox" id="<?php echo $this->get_field_id( 'dropdown' ); ?>" name="<?php echo $this->get_field_name( 'dropdown' ); ?>"<?php checked( $dropdown ); ?> /> <label for="<?php echo $this->get_field_id( 'dropdown' ); ?>"><?php _e( 'Display as dropdown' ); ?></label> <br /> <input type="checkbox" class="checkbox" id="<?php echo $this->get_field_id( 'count' ); ?>" name="<?php echo $this->get_field_name( 'count' ); ?>"<?php checked( $count ); ?> /> <label for="<?php echo $this->get_field_id( 'count' ); ?>"><?php _e( 'Show post counts' ); ?></label> <br /> <input type="checkbox" class="checkbox" id="<?php echo $this->get_field_id( 'hierarchical' ); ?>" name="<?php echo $this->get_field_name( 'hierarchical' ); ?>"<?php checked( $hierarchical ); ?> /> <label for="<?php echo $this->get_field_id( 'hierarchical' ); ?>"><?php _e( 'Show hierarchy' ); ?></label> </p> <?php } } class-wp-widget-block.php 0000644 00000014635 14717676577 0011423 0 ustar 00 <?php /** * Widget API: WP_Widget_Block class * * @package WordPress * @subpackage Widgets * @since 5.8.0 */ /** * Core class used to implement a Block widget. * * @since 5.8.0 * * @see WP_Widget */ class WP_Widget_Block extends WP_Widget { /** * Default instance. * * @since 5.8.0 * @var array */ protected $default_instance = array( 'content' => '', ); /** * Sets up a new Block widget instance. * * @since 5.8.0 */ public function __construct() { $widget_ops = array( 'classname' => 'widget_block', 'description' => __( 'A widget containing a block.' ), 'customize_selective_refresh' => true, 'show_instance_in_rest' => true, ); $control_ops = array( 'width' => 400, 'height' => 350, ); parent::__construct( 'block', __( 'Block' ), $widget_ops, $control_ops ); add_filter( 'is_wide_widget_in_customizer', array( $this, 'set_is_wide_widget_in_customizer' ), 10, 2 ); } /** * Outputs the content for the current Block widget instance. * * @since 5.8.0 * * @param array $args Display arguments including 'before_title', 'after_title', * 'before_widget', and 'after_widget'. * @param array $instance Settings for the current Block widget instance. */ public function widget( $args, $instance ) { $instance = wp_parse_args( $instance, $this->default_instance ); echo str_replace( 'widget_block', $this->get_dynamic_classname( $instance['content'] ), $args['before_widget'] ); /** * Filters the content of the Block widget before output. * * @since 5.8.0 * * @param string $content The widget content. * @param array $instance Array of settings for the current widget. * @param WP_Widget_Block $widget Current Block widget instance. */ echo apply_filters( 'widget_block_content', $instance['content'], $instance, $this ); echo $args['after_widget']; } /** * Calculates the classname to use in the block widget's container HTML. * * Usually this is set to `$this->widget_options['classname']` by * dynamic_sidebar(). In this case, however, we want to set the classname * dynamically depending on the block contained by this block widget. * * If a block widget contains a block that has an equivalent legacy widget, * we display that legacy widget's class name. This helps with theme * backwards compatibility. * * @since 5.8.0 * * @param string $content The HTML content of the current block widget. * @return string The classname to use in the block widget's container HTML. */ private function get_dynamic_classname( $content ) { $blocks = parse_blocks( $content ); $block_name = isset( $blocks[0] ) ? $blocks[0]['blockName'] : null; switch ( $block_name ) { case 'core/paragraph': $classname = 'widget_block widget_text'; break; case 'core/calendar': $classname = 'widget_block widget_calendar'; break; case 'core/search': $classname = 'widget_block widget_search'; break; case 'core/html': $classname = 'widget_block widget_custom_html'; break; case 'core/archives': $classname = 'widget_block widget_archive'; break; case 'core/latest-posts': $classname = 'widget_block widget_recent_entries'; break; case 'core/latest-comments': $classname = 'widget_block widget_recent_comments'; break; case 'core/tag-cloud': $classname = 'widget_block widget_tag_cloud'; break; case 'core/categories': $classname = 'widget_block widget_categories'; break; case 'core/audio': $classname = 'widget_block widget_media_audio'; break; case 'core/video': $classname = 'widget_block widget_media_video'; break; case 'core/image': $classname = 'widget_block widget_media_image'; break; case 'core/gallery': $classname = 'widget_block widget_media_gallery'; break; case 'core/rss': $classname = 'widget_block widget_rss'; break; default: $classname = 'widget_block'; } /** * The classname used in the block widget's container HTML. * * This can be set according to the name of the block contained by the block widget. * * @since 5.8.0 * * @param string $classname The classname to be used in the block widget's container HTML, * e.g. 'widget_block widget_text'. * @param string $block_name The name of the block contained by the block widget, * e.g. 'core/paragraph'. */ return apply_filters( 'widget_block_dynamic_classname', $classname, $block_name ); } /** * Handles updating settings for the current Block widget instance. * * @since 5.8.0 * @param array $new_instance New settings for this instance as input by the user via * WP_Widget::form(). * @param array $old_instance Old settings for this instance. * @return array Settings to save or bool false to cancel saving. */ public function update( $new_instance, $old_instance ) { $instance = array_merge( $this->default_instance, $old_instance ); if ( current_user_can( 'unfiltered_html' ) ) { $instance['content'] = $new_instance['content']; } else { $instance['content'] = wp_kses_post( $new_instance['content'] ); } return $instance; } /** * Outputs the Block widget settings form. * * @since 5.8.0 * * @see WP_Widget_Custom_HTML::render_control_template_scripts() * * @param array $instance Current instance. */ public function form( $instance ) { $instance = wp_parse_args( (array) $instance, $this->default_instance ); ?> <p> <label for="<?php echo $this->get_field_id( 'content' ); ?>"> <?php /* translators: HTML code of the block, not an option that blocks HTML. */ _e( 'Block HTML:' ); ?> </label> <textarea id="<?php echo $this->get_field_id( 'content' ); ?>" name="<?php echo $this->get_field_name( 'content' ); ?>" rows="6" cols="50" class="widefat code"><?php echo esc_textarea( $instance['content'] ); ?></textarea> </p> <?php } /** * Makes sure no block widget is considered to be wide. * * @since 5.8.0 * * @param bool $is_wide Whether the widget is considered wide. * @param string $widget_id Widget ID. * @return bool Updated `is_wide` value. */ public function set_is_wide_widget_in_customizer( $is_wide, $widget_id ) { if ( str_starts_with( $widget_id, 'block-' ) ) { return false; } return $is_wide; } } class-wp-widget-links.php 0000644 00000016175 14717676577 0011452 0 ustar 00 <?php /** * Widget API: WP_Widget_Links class * * @package WordPress * @subpackage Widgets * @since 4.4.0 */ /** * Core class used to implement a Links widget. * * @since 2.8.0 * * @see WP_Widget */ class WP_Widget_Links extends WP_Widget { /** * Sets up a new Links widget instance. * * @since 2.8.0 */ public function __construct() { $widget_ops = array( 'description' => __( 'Your blogroll' ), 'customize_selective_refresh' => true, ); parent::__construct( 'links', __( 'Links' ), $widget_ops ); } /** * Outputs the content for the current Links widget instance. * * @since 2.8.0 * * @param array $args Display arguments including 'before_title', 'after_title', * 'before_widget', and 'after_widget'. * @param array $instance Settings for the current Links widget instance. */ public function widget( $args, $instance ) { $show_description = isset( $instance['description'] ) ? $instance['description'] : false; $show_name = isset( $instance['name'] ) ? $instance['name'] : false; $show_rating = isset( $instance['rating'] ) ? $instance['rating'] : false; $show_images = isset( $instance['images'] ) ? $instance['images'] : true; $category = isset( $instance['category'] ) ? $instance['category'] : false; $orderby = isset( $instance['orderby'] ) ? $instance['orderby'] : 'name'; $order = 'rating' === $orderby ? 'DESC' : 'ASC'; $limit = isset( $instance['limit'] ) ? $instance['limit'] : -1; $before_widget = preg_replace( '/ id="[^"]*"/', ' id="%id"', $args['before_widget'] ); $widget_links_args = array( 'title_before' => $args['before_title'], 'title_after' => $args['after_title'], 'category_before' => $before_widget, 'category_after' => $args['after_widget'], 'show_images' => $show_images, 'show_description' => $show_description, 'show_name' => $show_name, 'show_rating' => $show_rating, 'category' => $category, 'class' => 'linkcat widget', 'orderby' => $orderby, 'order' => $order, 'limit' => $limit, ); /** * Filters the arguments for the Links widget. * * @since 2.6.0 * @since 4.4.0 Added the `$instance` parameter. * * @see wp_list_bookmarks() * * @param array $widget_links_args An array of arguments to retrieve the links list. * @param array $instance The settings for the particular instance of the widget. */ wp_list_bookmarks( apply_filters( 'widget_links_args', $widget_links_args, $instance ) ); } /** * Handles updating settings for the current Links widget instance. * * @since 2.8.0 * * @param array $new_instance New settings for this instance as input by the user via * WP_Widget::form(). * @param array $old_instance Old settings for this instance. * @return array Updated settings to save. */ public function update( $new_instance, $old_instance ) { $new_instance = (array) $new_instance; $instance = array( 'images' => 0, 'name' => 0, 'description' => 0, 'rating' => 0, ); foreach ( $instance as $field => $val ) { if ( isset( $new_instance[ $field ] ) ) { $instance[ $field ] = 1; } } $instance['orderby'] = 'name'; if ( in_array( $new_instance['orderby'], array( 'name', 'rating', 'id', 'rand' ), true ) ) { $instance['orderby'] = $new_instance['orderby']; } $instance['category'] = (int) $new_instance['category']; $instance['limit'] = ! empty( $new_instance['limit'] ) ? (int) $new_instance['limit'] : -1; return $instance; } /** * Outputs the settings form for the Links widget. * * @since 2.8.0 * * @param array $instance Current settings. */ public function form( $instance ) { // Defaults. $instance = wp_parse_args( (array) $instance, array( 'images' => true, 'name' => true, 'description' => false, 'rating' => false, 'category' => false, 'orderby' => 'name', 'limit' => -1, ) ); $link_cats = get_terms( array( 'taxonomy' => 'link_category' ) ); $limit = (int) $instance['limit']; if ( ! $limit ) { $limit = -1; } ?> <p> <label for="<?php echo $this->get_field_id( 'category' ); ?>"><?php _e( 'Select Link Category:' ); ?></label> <select class="widefat" id="<?php echo $this->get_field_id( 'category' ); ?>" name="<?php echo $this->get_field_name( 'category' ); ?>"> <option value=""><?php _ex( 'All Links', 'links widget' ); ?></option> <?php foreach ( $link_cats as $link_cat ) : ?> <option value="<?php echo (int) $link_cat->term_id; ?>" <?php selected( $instance['category'], $link_cat->term_id ); ?>> <?php echo esc_html( $link_cat->name ); ?> </option> <?php endforeach; ?> </select> <label for="<?php echo $this->get_field_id( 'orderby' ); ?>"><?php _e( 'Sort by:' ); ?></label> <select name="<?php echo $this->get_field_name( 'orderby' ); ?>" id="<?php echo $this->get_field_id( 'orderby' ); ?>" class="widefat"> <option value="name"<?php selected( $instance['orderby'], 'name' ); ?>><?php _e( 'Link title' ); ?></option> <option value="rating"<?php selected( $instance['orderby'], 'rating' ); ?>><?php _e( 'Link rating' ); ?></option> <option value="id"<?php selected( $instance['orderby'], 'id' ); ?>><?php _e( 'Link ID' ); ?></option> <option value="rand"<?php selected( $instance['orderby'], 'rand' ); ?>><?php _ex( 'Random', 'Links widget' ); ?></option> </select> </p> <p> <input class="checkbox" type="checkbox"<?php checked( $instance['images'], true ); ?> id="<?php echo $this->get_field_id( 'images' ); ?>" name="<?php echo $this->get_field_name( 'images' ); ?>" /> <label for="<?php echo $this->get_field_id( 'images' ); ?>"><?php _e( 'Show Link Image' ); ?></label> <br /> <input class="checkbox" type="checkbox"<?php checked( $instance['name'], true ); ?> id="<?php echo $this->get_field_id( 'name' ); ?>" name="<?php echo $this->get_field_name( 'name' ); ?>" /> <label for="<?php echo $this->get_field_id( 'name' ); ?>"><?php _e( 'Show Link Name' ); ?></label> <br /> <input class="checkbox" type="checkbox"<?php checked( $instance['description'], true ); ?> id="<?php echo $this->get_field_id( 'description' ); ?>" name="<?php echo $this->get_field_name( 'description' ); ?>" /> <label for="<?php echo $this->get_field_id( 'description' ); ?>"><?php _e( 'Show Link Description' ); ?></label> <br /> <input class="checkbox" type="checkbox"<?php checked( $instance['rating'], true ); ?> id="<?php echo $this->get_field_id( 'rating' ); ?>" name="<?php echo $this->get_field_name( 'rating' ); ?>" /> <label for="<?php echo $this->get_field_id( 'rating' ); ?>"><?php _e( 'Show Link Rating' ); ?></label> </p> <p> <label for="<?php echo $this->get_field_id( 'limit' ); ?>"><?php _e( 'Number of links to show:' ); ?></label> <input id="<?php echo $this->get_field_id( 'limit' ); ?>" name="<?php echo $this->get_field_name( 'limit' ); ?>" type="text" value="<?php echo ( -1 !== $limit ) ? (int) $limit : ''; ?>" size="3" /> </p> <?php } } class-wp-widget-media-audio.php 0000644 00000014124 14717676577 0012500 0 ustar 00 <?php /** * Widget API: WP_Widget_Media_Audio class * * @package WordPress * @subpackage Widgets * @since 4.8.0 */ /** * Core class that implements an audio widget. * * @since 4.8.0 * * @see WP_Widget_Media * @see WP_Widget */ class WP_Widget_Media_Audio extends WP_Widget_Media { /** * Constructor. * * @since 4.8.0 */ public function __construct() { parent::__construct( 'media_audio', __( 'Audio' ), array( 'description' => __( 'Displays an audio player.' ), 'mime_type' => 'audio', ) ); $this->l10n = array_merge( $this->l10n, array( 'no_media_selected' => __( 'No audio selected' ), 'add_media' => _x( 'Add Audio', 'label for button in the audio widget' ), 'replace_media' => _x( 'Replace Audio', 'label for button in the audio widget; should preferably not be longer than ~13 characters long' ), 'edit_media' => _x( 'Edit Audio', 'label for button in the audio widget; should preferably not be longer than ~13 characters long' ), 'missing_attachment' => sprintf( /* translators: %s: URL to media library. */ __( 'That audio file cannot be found. Check your <a href="%s">media library</a> and make sure it was not deleted.' ), esc_url( admin_url( 'upload.php' ) ) ), /* translators: %d: Widget count. */ 'media_library_state_multi' => _n_noop( 'Audio Widget (%d)', 'Audio Widget (%d)' ), 'media_library_state_single' => __( 'Audio Widget' ), 'unsupported_file_type' => __( 'Looks like this is not the correct kind of file. Please link to an audio file instead.' ), ) ); } /** * Get schema for properties of a widget instance (item). * * @since 4.8.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() { $schema = array( 'preload' => array( 'type' => 'string', 'enum' => array( 'none', 'auto', 'metadata' ), 'default' => 'none', 'description' => __( 'Preload' ), ), 'loop' => array( 'type' => 'boolean', 'default' => false, 'description' => __( 'Loop' ), ), ); foreach ( wp_get_audio_extensions() as $audio_extension ) { $schema[ $audio_extension ] = array( 'type' => 'string', 'default' => '', 'format' => 'uri', /* translators: %s: Audio extension. */ 'description' => sprintf( __( 'URL to the %s audio source file' ), $audio_extension ), ); } return array_merge( $schema, parent::get_instance_schema() ); } /** * Render the media on the frontend. * * @since 4.8.0 * * @param array $instance Widget instance props. */ public function render_media( $instance ) { $instance = array_merge( wp_list_pluck( $this->get_instance_schema(), 'default' ), $instance ); $attachment = null; if ( $this->is_attachment_with_mime_type( $instance['attachment_id'], $this->widget_options['mime_type'] ) ) { $attachment = get_post( $instance['attachment_id'] ); } if ( $attachment ) { $src = wp_get_attachment_url( $attachment->ID ); } else { $src = $instance['url']; } echo wp_audio_shortcode( array_merge( $instance, compact( 'src' ) ) ); } /** * Enqueue preview scripts. * * These scripts normally are enqueued just-in-time when an audio shortcode is used. * In the customizer, however, widgets can be dynamically added and rendered via * selective refresh, and so it is important to unconditionally enqueue them in * case a widget does get added. * * @since 4.8.0 */ public function enqueue_preview_scripts() { /** This filter is documented in wp-includes/media.php */ if ( 'mediaelement' === apply_filters( 'wp_audio_shortcode_library', 'mediaelement' ) ) { wp_enqueue_style( 'wp-mediaelement' ); wp_enqueue_script( 'wp-mediaelement' ); } } /** * Loads the required media files for the media manager and scripts for media widgets. * * @since 4.8.0 */ public function enqueue_admin_scripts() { parent::enqueue_admin_scripts(); wp_enqueue_style( 'wp-mediaelement' ); wp_enqueue_script( 'wp-mediaelement' ); $handle = 'media-audio-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' ) ); } 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; wp.mediaWidgets.controlConstructors[ %1$s ].prototype.l10n = _.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.8.0 */ public function render_control_template_scripts() { parent::render_control_template_scripts() ?> <script type="text/html" id="tmpl-wp-media-widget-audio-preview"> <# if ( data.error && 'missing_attachment' === data.error ) { #> <?php wp_admin_notice( $this->l10n['missing_attachment'], array( 'type' => 'error', 'additional_classes' => array( 'notice-alt', 'notice-missing-attachment' ), ) ); ?> <# } else if ( data.error ) { #> <?php wp_admin_notice( __( 'Unable to preview media due to an unknown error.' ), array( 'type' => 'error', 'additional_classes' => array( 'notice-alt' ), ) ); ?> <# } else if ( data.model && data.model.src ) { #> <?php wp_underscore_audio_template(); ?> <# } #> </script> <?php } } class-wp-widget-archives.php 0000644 00000015147 14717676577 0012134 0 ustar 00 <?php /** * Widget API: WP_Widget_Archives class * * @package WordPress * @subpackage Widgets * @since 4.4.0 */ /** * Core class used to implement the Archives widget. * * @since 2.8.0 * * @see WP_Widget */ class WP_Widget_Archives extends WP_Widget { /** * Sets up a new Archives widget instance. * * @since 2.8.0 */ public function __construct() { $widget_ops = array( 'classname' => 'widget_archive', 'description' => __( 'A monthly archive of your site’s Posts.' ), 'customize_selective_refresh' => true, 'show_instance_in_rest' => true, ); parent::__construct( 'archives', __( 'Archives' ), $widget_ops ); } /** * Outputs the content for the current Archives widget instance. * * @since 2.8.0 * * @param array $args Display arguments including 'before_title', 'after_title', * 'before_widget', and 'after_widget'. * @param array $instance Settings for the current Archives widget instance. */ public function widget( $args, $instance ) { $default_title = __( 'Archives' ); $title = ! empty( $instance['title'] ) ? $instance['title'] : $default_title; /** This filter is documented in wp-includes/widgets/class-wp-widget-pages.php */ $title = apply_filters( 'widget_title', $title, $instance, $this->id_base ); $count = ! empty( $instance['count'] ) ? '1' : '0'; $dropdown = ! empty( $instance['dropdown'] ) ? '1' : '0'; echo $args['before_widget']; if ( $title ) { echo $args['before_title'] . $title . $args['after_title']; } if ( $dropdown ) { $dropdown_id = "{$this->id_base}-dropdown-{$this->number}"; ?> <label class="screen-reader-text" for="<?php echo esc_attr( $dropdown_id ); ?>"><?php echo $title; ?></label> <select id="<?php echo esc_attr( $dropdown_id ); ?>" name="archive-dropdown"> <?php /** * Filters the arguments for the Archives widget drop-down. * * @since 2.8.0 * @since 4.9.0 Added the `$instance` parameter. * * @see wp_get_archives() * * @param array $args An array of Archives widget drop-down arguments. * @param array $instance Settings for the current Archives widget instance. */ $dropdown_args = apply_filters( 'widget_archives_dropdown_args', array( 'type' => 'monthly', 'format' => 'option', 'show_post_count' => $count, ), $instance ); switch ( $dropdown_args['type'] ) { case 'yearly': $label = __( 'Select Year' ); break; case 'monthly': $label = __( 'Select Month' ); break; case 'daily': $label = __( 'Select Day' ); break; case 'weekly': $label = __( 'Select Week' ); break; default: $label = __( 'Select Post' ); break; } ?> <option value=""><?php echo esc_html( $label ); ?></option> <?php wp_get_archives( $dropdown_args ); ?> </select> <?php ob_start(); ?> <script> (function() { var dropdown = document.getElementById( "<?php echo esc_js( $dropdown_id ); ?>" ); function onSelectChange() { if ( dropdown.options[ dropdown.selectedIndex ].value !== '' ) { document.location.href = this.options[ this.selectedIndex ].value; } } dropdown.onchange = onSelectChange; })(); </script> <?php wp_print_inline_script_tag( wp_remove_surrounding_empty_script_tags( ob_get_clean() ) ); } else { $format = current_theme_supports( 'html5', 'navigation-widgets' ) ? 'html5' : 'xhtml'; /** This filter is documented in wp-includes/widgets/class-wp-nav-menu-widget.php */ $format = apply_filters( 'navigation_widgets_format', $format ); if ( 'html5' === $format ) { // The title may be filtered: Strip out HTML and make sure the aria-label is never empty. $title = trim( strip_tags( $title ) ); $aria_label = $title ? $title : $default_title; echo '<nav aria-label="' . esc_attr( $aria_label ) . '">'; } ?> <ul> <?php wp_get_archives( /** * Filters the arguments for the Archives widget. * * @since 2.8.0 * @since 4.9.0 Added the `$instance` parameter. * * @see wp_get_archives() * * @param array $args An array of Archives option arguments. * @param array $instance Array of settings for the current widget. */ apply_filters( 'widget_archives_args', array( 'type' => 'monthly', 'show_post_count' => $count, ), $instance ) ); ?> </ul> <?php if ( 'html5' === $format ) { echo '</nav>'; } } echo $args['after_widget']; } /** * Handles updating settings for the current Archives widget instance. * * @since 2.8.0 * * @param array $new_instance New settings for this instance as input by the user via * WP_Widget_Archives::form(). * @param array $old_instance Old settings for this instance. * @return array Updated settings to save. */ public function update( $new_instance, $old_instance ) { $instance = $old_instance; $new_instance = wp_parse_args( (array) $new_instance, array( 'title' => '', 'count' => 0, 'dropdown' => '', ) ); $instance['title'] = sanitize_text_field( $new_instance['title'] ); $instance['count'] = $new_instance['count'] ? 1 : 0; $instance['dropdown'] = $new_instance['dropdown'] ? 1 : 0; return $instance; } /** * Outputs the settings form for the Archives widget. * * @since 2.8.0 * * @param array $instance Current settings. */ public function form( $instance ) { $instance = wp_parse_args( (array) $instance, array( 'title' => '', 'count' => 0, 'dropdown' => '', ) ); ?> <p> <label for="<?php echo $this->get_field_id( 'title' ); ?>"><?php _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> <input class="checkbox" type="checkbox"<?php checked( $instance['dropdown'] ); ?> id="<?php echo $this->get_field_id( 'dropdown' ); ?>" name="<?php echo $this->get_field_name( 'dropdown' ); ?>" /> <label for="<?php echo $this->get_field_id( 'dropdown' ); ?>"><?php _e( 'Display as dropdown' ); ?></label> <br /> <input class="checkbox" type="checkbox"<?php checked( $instance['count'] ); ?> id="<?php echo $this->get_field_id( 'count' ); ?>" name="<?php echo $this->get_field_name( 'count' ); ?>" /> <label for="<?php echo $this->get_field_id( 'count' ); ?>"><?php _e( 'Show post counts' ); ?></label> </p> <?php } } class-wp-widget-media-image.php 0000644 00000030277 14717676577 0012470 0 ustar 00 <?php /** * Widget API: WP_Widget_Media_Image class * * @package WordPress * @subpackage Widgets * @since 4.8.0 */ /** * Core class that implements an image widget. * * @since 4.8.0 * * @see WP_Widget_Media * @see WP_Widget */ class WP_Widget_Media_Image extends WP_Widget_Media { /** * Constructor. * * @since 4.8.0 */ public function __construct() { parent::__construct( 'media_image', __( 'Image' ), array( 'description' => __( 'Displays an image.' ), 'mime_type' => 'image', ) ); $this->l10n = array_merge( $this->l10n, array( 'no_media_selected' => __( 'No image selected' ), 'add_media' => _x( 'Add Image', 'label for button in the image widget' ), 'replace_media' => _x( 'Replace Image', 'label for button in the image widget; should preferably not be longer than ~13 characters long' ), 'edit_media' => _x( 'Edit Image', 'label for button in the image widget; should preferably not be longer than ~13 characters long' ), 'missing_attachment' => sprintf( /* translators: %s: URL to media library. */ __( 'That image cannot be found. Check your <a href="%s">media library</a> and make sure it was not deleted.' ), esc_url( admin_url( 'upload.php' ) ) ), /* translators: %d: Widget count. */ 'media_library_state_multi' => _n_noop( 'Image Widget (%d)', 'Image Widget (%d)' ), 'media_library_state_single' => __( 'Image Widget' ), ) ); } /** * Get schema for properties of a widget instance (item). * * @since 4.8.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_merge( array( 'size' => array( 'type' => 'string', 'enum' => array_merge( get_intermediate_image_sizes(), array( 'full', 'custom' ) ), 'default' => 'medium', 'description' => __( 'Size' ), ), 'width' => array( // Via 'customWidth', only when size=custom; otherwise via 'width'. 'type' => 'integer', 'minimum' => 0, 'default' => 0, 'description' => __( 'Width' ), ), 'height' => array( // Via 'customHeight', only when size=custom; otherwise via 'height'. 'type' => 'integer', 'minimum' => 0, 'default' => 0, 'description' => __( 'Height' ), ), 'caption' => array( 'type' => 'string', 'default' => '', 'sanitize_callback' => 'wp_kses_post', 'description' => __( 'Caption' ), 'should_preview_update' => false, ), 'alt' => array( 'type' => 'string', 'default' => '', 'sanitize_callback' => 'sanitize_text_field', 'description' => __( 'Alternative Text' ), ), 'link_type' => array( 'type' => 'string', 'enum' => array( 'none', 'file', 'post', 'custom' ), 'default' => 'custom', 'media_prop' => 'link', 'description' => __( 'Link To' ), 'should_preview_update' => true, ), 'link_url' => array( 'type' => 'string', 'default' => '', 'format' => 'uri', 'media_prop' => 'linkUrl', 'description' => __( 'URL' ), 'should_preview_update' => true, ), 'image_classes' => array( 'type' => 'string', 'default' => '', 'sanitize_callback' => array( $this, 'sanitize_token_list' ), 'media_prop' => 'extraClasses', 'description' => __( 'Image CSS Class' ), 'should_preview_update' => false, ), 'link_classes' => array( 'type' => 'string', 'default' => '', 'sanitize_callback' => array( $this, 'sanitize_token_list' ), 'media_prop' => 'linkClassName', 'should_preview_update' => false, 'description' => __( 'Link CSS Class' ), ), 'link_rel' => array( 'type' => 'string', 'default' => '', 'sanitize_callback' => array( $this, 'sanitize_token_list' ), 'media_prop' => 'linkRel', 'description' => __( 'Link Rel' ), 'should_preview_update' => false, ), 'link_target_blank' => array( 'type' => 'boolean', 'default' => false, 'media_prop' => 'linkTargetBlank', 'description' => __( 'Open link in a new tab' ), 'should_preview_update' => false, ), 'image_title' => array( 'type' => 'string', 'default' => '', 'sanitize_callback' => 'sanitize_text_field', 'media_prop' => 'title', 'description' => __( 'Image Title Attribute' ), 'should_preview_update' => false, ), /* * There are two additional properties exposed by the PostImage modal * that don't seem to be relevant, as they may only be derived read-only * values: * - originalUrl * - aspectRatio * - height (redundant when size is not custom) * - width (redundant when size is not custom) */ ), parent::get_instance_schema() ); } /** * Render the media on the frontend. * * @since 4.8.0 * * @param array $instance Widget instance props. */ public function render_media( $instance ) { $instance = array_merge( wp_list_pluck( $this->get_instance_schema(), 'default' ), $instance ); $instance = wp_parse_args( $instance, array( 'size' => 'thumbnail', ) ); $attachment = null; if ( $this->is_attachment_with_mime_type( $instance['attachment_id'], $this->widget_options['mime_type'] ) ) { $attachment = get_post( $instance['attachment_id'] ); } if ( $attachment ) { $caption = ''; if ( ! isset( $instance['caption'] ) ) { $caption = $attachment->post_excerpt; } elseif ( trim( $instance['caption'] ) ) { $caption = $instance['caption']; } $image_attributes = array( 'class' => sprintf( 'image wp-image-%d %s', $attachment->ID, $instance['image_classes'] ), 'style' => 'max-width: 100%; height: auto;', ); if ( ! empty( $instance['image_title'] ) ) { $image_attributes['title'] = $instance['image_title']; } if ( $instance['alt'] ) { $image_attributes['alt'] = $instance['alt']; } $size = $instance['size']; if ( 'custom' === $size || ! in_array( $size, array_merge( get_intermediate_image_sizes(), array( 'full' ) ), true ) ) { $size = array( $instance['width'], $instance['height'] ); $width = $instance['width']; } else { $caption_size = _wp_get_image_size_from_meta( $instance['size'], wp_get_attachment_metadata( $attachment->ID ) ); $width = empty( $caption_size[0] ) ? 0 : $caption_size[0]; } $image_attributes['class'] .= sprintf( ' attachment-%1$s size-%1$s', is_array( $size ) ? implode( 'x', $size ) : $size ); $image = wp_get_attachment_image( $attachment->ID, $size, false, $image_attributes ); } else { if ( empty( $instance['url'] ) ) { return; } $instance['size'] = 'custom'; $caption = $instance['caption']; $width = $instance['width']; $classes = 'image ' . $instance['image_classes']; if ( 0 === $instance['width'] ) { $instance['width'] = ''; } if ( 0 === $instance['height'] ) { $instance['height'] = ''; } $attr = array( 'class' => $classes, 'src' => $instance['url'], 'alt' => $instance['alt'], 'width' => $instance['width'], 'height' => $instance['height'], ); $loading_optimization_attr = wp_get_loading_optimization_attributes( 'img', $attr, 'widget_media_image' ); $attr = array_merge( $attr, $loading_optimization_attr ); $attr = array_map( 'esc_attr', $attr ); $image = '<img'; foreach ( $attr as $name => $value ) { $image .= ' ' . $name . '="' . $value . '"'; } $image .= ' />'; } // End if(). $url = ''; if ( 'file' === $instance['link_type'] ) { $url = $attachment ? wp_get_attachment_url( $attachment->ID ) : $instance['url']; } elseif ( $attachment && 'post' === $instance['link_type'] ) { $url = get_attachment_link( $attachment->ID ); } elseif ( 'custom' === $instance['link_type'] && ! empty( $instance['link_url'] ) ) { $url = $instance['link_url']; } if ( $url ) { $link = sprintf( '<a href="%s"', esc_url( $url ) ); if ( ! empty( $instance['link_classes'] ) ) { $link .= sprintf( ' class="%s"', esc_attr( $instance['link_classes'] ) ); } if ( ! empty( $instance['link_rel'] ) ) { $link .= sprintf( ' rel="%s"', esc_attr( $instance['link_rel'] ) ); } if ( ! empty( $instance['link_target_blank'] ) ) { $link .= ' target="_blank"'; } $link .= '>'; $link .= $image; $link .= '</a>'; $image = wp_targeted_link_rel( $link ); } if ( $caption ) { $image = img_caption_shortcode( array( 'width' => $width, 'caption' => $caption, ), $image ); } echo $image; } /** * Loads the required media files for the media manager and scripts for media widgets. * * @since 4.8.0 */ public function enqueue_admin_scripts() { parent::enqueue_admin_scripts(); $handle = 'media-image-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' ) ); } 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; wp.mediaWidgets.controlConstructors[ %1$s ].prototype.l10n = _.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.8.0 */ public function render_control_template_scripts() { parent::render_control_template_scripts(); ?> <script type="text/html" id="tmpl-wp-media-widget-image-fields"> <# var elementIdPrefix = 'el' + String( Math.random() ) + '_'; #> <# if ( data.url ) { #> <p class="media-widget-image-link"> <label for="{{ elementIdPrefix }}linkUrl"><?php esc_html_e( 'Link to:' ); ?></label> <input id="{{ elementIdPrefix }}linkUrl" type="text" class="widefat link" value="{{ data.link_url }}" placeholder="https://" pattern="((\w+:)?\/\/\w.*|\w+:(?!\/\/$)|\/|\?|#).*"> </p> <# } #> </script> <script type="text/html" id="tmpl-wp-media-widget-image-preview"> <# if ( data.error && 'missing_attachment' === data.error ) { #> <?php wp_admin_notice( $this->l10n['missing_attachment'], array( 'type' => 'error', 'additional_classes' => array( 'notice-alt', 'notice-missing-attachment' ), ) ); ?> <# } else if ( data.error ) { #> <?php wp_admin_notice( __( 'Unable to preview media due to an unknown error.' ), array( 'type' => 'error', 'additional_classes' => array( 'notice-alt' ), ) ); ?> <# } else if ( data.url ) { #> <img class="attachment-thumb" src="{{ data.url }}" draggable="false" alt="{{ data.alt }}" <# if ( ! data.alt && data.currentFilename ) { #> aria-label=" <?php echo esc_attr( sprintf( /* translators: %s: The image file name. */ __( 'The current image has no alternative text. The file name is: %s' ), '{{ data.currentFilename }}' ) ); ?> " <# } #> /> <# } #> </script> <?php } } class-wp-widget-calendar.php 0000644 00000005541 14717676577 0012076 0 ustar 00 <?php /** * Widget API: WP_Widget_Calendar class * * @package WordPress * @subpackage Widgets * @since 4.4.0 */ /** * Core class used to implement the Calendar widget. * * @since 2.8.0 * * @see WP_Widget */ class WP_Widget_Calendar extends WP_Widget { /** * Ensure that the ID attribute only appears in the markup once * * @since 4.4.0 * @var int */ private static $instance = 0; /** * Sets up a new Calendar widget instance. * * @since 2.8.0 */ public function __construct() { $widget_ops = array( 'classname' => 'widget_calendar', 'description' => __( 'A calendar of your site’s posts.' ), 'customize_selective_refresh' => true, 'show_instance_in_rest' => true, ); parent::__construct( 'calendar', __( 'Calendar' ), $widget_ops ); } /** * Outputs the content for the current Calendar widget instance. * * @since 2.8.0 * * @param array $args Display arguments including 'before_title', 'after_title', * 'before_widget', and 'after_widget'. * @param array $instance The settings for the particular instance of the widget. */ public function widget( $args, $instance ) { $title = ! empty( $instance['title'] ) ? $instance['title'] : ''; /** This filter is documented in wp-includes/widgets/class-wp-widget-pages.php */ $title = apply_filters( 'widget_title', $title, $instance, $this->id_base ); echo $args['before_widget']; if ( $title ) { echo $args['before_title'] . $title . $args['after_title']; } if ( 0 === self::$instance ) { echo '<div id="calendar_wrap" class="calendar_wrap">'; } else { echo '<div class="calendar_wrap">'; } get_calendar(); echo '</div>'; echo $args['after_widget']; ++self::$instance; } /** * Handles updating settings for the current Calendar widget instance. * * @since 2.8.0 * * @param array $new_instance New settings for this instance as input by the user via * WP_Widget::form(). * @param array $old_instance Old settings for this instance. * @return array Updated settings to save. */ public function update( $new_instance, $old_instance ) { $instance = $old_instance; $instance['title'] = sanitize_text_field( $new_instance['title'] ); return $instance; } /** * Outputs the settings form for the Calendar widget. * * @since 2.8.0 * * @param array $instance Current settings. */ public function form( $instance ) { $instance = wp_parse_args( (array) $instance, array( 'title' => '' ) ); ?> <p> <label for="<?php echo $this->get_field_id( 'title' ); ?>"><?php _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> <?php } } class-wp-widget-media-video.php 0000644 00000020667 14717676577 0012516 0 ustar 00 <?php /** * Widget API: WP_Widget_Media_Video class * * @package WordPress * @subpackage Widgets * @since 4.8.0 */ /** * Core class that implements a video widget. * * @since 4.8.0 * * @see WP_Widget_Media * @see WP_Widget */ class WP_Widget_Media_Video extends WP_Widget_Media { /** * Constructor. * * @since 4.8.0 */ public function __construct() { parent::__construct( 'media_video', __( 'Video' ), array( 'description' => __( 'Displays a video from the media library or from YouTube, Vimeo, or another provider.' ), 'mime_type' => 'video', ) ); $this->l10n = array_merge( $this->l10n, array( 'no_media_selected' => __( 'No video selected' ), 'add_media' => _x( 'Add Video', 'label for button in the video widget' ), 'replace_media' => _x( 'Replace Video', 'label for button in the video widget; should preferably not be longer than ~13 characters long' ), 'edit_media' => _x( 'Edit Video', 'label for button in the video widget; should preferably not be longer than ~13 characters long' ), 'missing_attachment' => sprintf( /* translators: %s: URL to media library. */ __( 'That video cannot be found. Check your <a href="%s">media library</a> and make sure it was not deleted.' ), esc_url( admin_url( 'upload.php' ) ) ), /* translators: %d: Widget count. */ 'media_library_state_multi' => _n_noop( 'Video Widget (%d)', 'Video Widget (%d)' ), 'media_library_state_single' => __( 'Video Widget' ), /* translators: %s: A list of valid video file extensions. */ 'unsupported_file_type' => sprintf( __( 'Sorry, the video at the supplied URL cannot be loaded. Please check that the URL is for a supported video file (%s) or stream (e.g. YouTube and Vimeo).' ), '<code>.' . implode( '</code>, <code>.', wp_get_video_extensions() ) . '</code>' ), ) ); } /** * Get schema for properties of a widget instance (item). * * @since 4.8.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() { $schema = array( 'preload' => array( 'type' => 'string', 'enum' => array( 'none', 'auto', 'metadata' ), 'default' => 'metadata', 'description' => __( 'Preload' ), 'should_preview_update' => false, ), 'loop' => array( 'type' => 'boolean', 'default' => false, 'description' => __( 'Loop' ), 'should_preview_update' => false, ), 'content' => array( 'type' => 'string', 'default' => '', 'sanitize_callback' => 'wp_kses_post', 'description' => __( 'Tracks (subtitles, captions, descriptions, chapters, or metadata)' ), 'should_preview_update' => false, ), ); foreach ( wp_get_video_extensions() as $video_extension ) { $schema[ $video_extension ] = array( 'type' => 'string', 'default' => '', 'format' => 'uri', /* translators: %s: Video extension. */ 'description' => sprintf( __( 'URL to the %s video source file' ), $video_extension ), ); } return array_merge( $schema, parent::get_instance_schema() ); } /** * Render the media on the frontend. * * @since 4.8.0 * * @param array $instance Widget instance props. */ public function render_media( $instance ) { $instance = array_merge( wp_list_pluck( $this->get_instance_schema(), 'default' ), $instance ); $attachment = null; if ( $this->is_attachment_with_mime_type( $instance['attachment_id'], $this->widget_options['mime_type'] ) ) { $attachment = get_post( $instance['attachment_id'] ); } $src = $instance['url']; if ( $attachment ) { $src = wp_get_attachment_url( $attachment->ID ); } if ( empty( $src ) ) { return; } $youtube_pattern = '#^https?://(?:www\.)?(?:youtube\.com/watch|youtu\.be/)#'; $vimeo_pattern = '#^https?://(.+\.)?vimeo\.com/.*#'; if ( $attachment || preg_match( $youtube_pattern, $src ) || preg_match( $vimeo_pattern, $src ) ) { add_filter( 'wp_video_shortcode', array( $this, 'inject_video_max_width_style' ) ); echo wp_video_shortcode( array_merge( $instance, compact( 'src' ) ), $instance['content'] ); remove_filter( 'wp_video_shortcode', array( $this, 'inject_video_max_width_style' ) ); } else { echo $this->inject_video_max_width_style( wp_oembed_get( $src ) ); } } /** * Inject max-width and remove height for videos too constrained to fit inside sidebars on frontend. * * @since 4.8.0 * * @param string $html Video shortcode HTML output. * @return string HTML Output. */ public function inject_video_max_width_style( $html ) { $html = preg_replace( '/\sheight="\d+"/', '', $html ); $html = preg_replace( '/\swidth="\d+"/', '', $html ); $html = preg_replace( '/(?<=width:)\s*\d+px(?=;?)/', '100%', $html ); return $html; } /** * Enqueue preview scripts. * * These scripts normally are enqueued just-in-time when a video shortcode is used. * In the customizer, however, widgets can be dynamically added and rendered via * selective refresh, and so it is important to unconditionally enqueue them in * case a widget does get added. * * @since 4.8.0 */ public function enqueue_preview_scripts() { /** This filter is documented in wp-includes/media.php */ if ( 'mediaelement' === apply_filters( 'wp_video_shortcode_library', 'mediaelement' ) ) { wp_enqueue_style( 'wp-mediaelement' ); wp_enqueue_script( 'mediaelement-vimeo' ); wp_enqueue_script( 'wp-mediaelement' ); } } /** * Loads the required scripts and styles for the widget control. * * @since 4.8.0 */ public function enqueue_admin_scripts() { parent::enqueue_admin_scripts(); $handle = 'media-video-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' ) ); } 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; wp.mediaWidgets.controlConstructors[ %1$s ].prototype.l10n = _.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.8.0 */ public function render_control_template_scripts() { parent::render_control_template_scripts() ?> <script type="text/html" id="tmpl-wp-media-widget-video-preview"> <# if ( data.error && 'missing_attachment' === data.error ) { #> <?php wp_admin_notice( $this->l10n['missing_attachment'], array( 'type' => 'error', 'additional_classes' => array( 'notice-alt', 'notice-missing-attachment' ), ) ); ?> <# } else if ( data.error && 'unsupported_file_type' === data.error ) { #> <?php wp_admin_notice( $this->l10n['unsupported_file_type'], array( 'type' => 'error', 'additional_classes' => array( 'notice-alt', 'notice-missing-attachment' ), ) ); ?> <# } else if ( data.error ) { #> <?php wp_admin_notice( __( 'Unable to preview media due to an unknown error.' ), array( 'type' => 'error', 'additional_classes' => array( 'notice-alt' ), ) ); ?> <# } else if ( data.is_oembed && data.model.poster ) { #> <a href="{{ data.model.src }}" target="_blank" class="media-widget-video-link"> <img src="{{ data.model.poster }}" /> </a> <# } else if ( data.is_oembed ) { #> <a href="{{ data.model.src }}" target="_blank" class="media-widget-video-link no-poster"> <span class="dashicons dashicons-format-video"></span> </a> <# } else if ( data.model.src ) { #> <?php wp_underscore_video_template(); ?> <# } #> </script> <?php } } class-wp-widget-text.php 0000644 00000051723 14717676577 0011314 0 ustar 00 <?php /** * Widget API: WP_Widget_Text class * * @package WordPress * @subpackage Widgets * @since 4.4.0 */ /** * Core class used to implement a Text widget. * * @since 2.8.0 * * @see WP_Widget */ class WP_Widget_Text extends WP_Widget { /** * Whether or not the widget has been registered yet. * * @since 4.8.1 * @var bool */ protected $registered = false; /** * Sets up a new Text widget instance. * * @since 2.8.0 */ public function __construct() { $widget_ops = array( 'classname' => 'widget_text', 'description' => __( 'Arbitrary text.' ), 'customize_selective_refresh' => true, 'show_instance_in_rest' => true, ); $control_ops = array( 'width' => 400, 'height' => 350, ); parent::__construct( 'text', __( 'Text' ), $widget_ops, $control_ops ); } /** * Adds hooks for enqueueing assets when registering all widget instances of this widget class. * * @param int $number Optional. The unique order number of this widget instance * compared to other instances of the same class. Default -1. */ public function _register_one( $number = -1 ) { parent::_register_one( $number ); if ( $this->registered ) { return; } $this->registered = true; if ( $this->is_preview() ) { add_action( 'wp_enqueue_scripts', array( $this, 'enqueue_preview_scripts' ) ); } /* * Note that the widgets component in the customizer will also do * the 'admin_print_scripts-widgets.php' action in WP_Customize_Widgets::print_scripts(). */ add_action( 'admin_print_scripts-widgets.php', array( $this, 'enqueue_admin_scripts' ) ); /* * Note that the widgets component in the customizer will also do * the 'admin_footer-widgets.php' action in WP_Customize_Widgets::print_footer_scripts(). */ add_action( 'admin_footer-widgets.php', array( 'WP_Widget_Text', 'render_control_template_scripts' ) ); } /** * Determines whether a given instance is legacy and should bypass using TinyMCE. * * @since 4.8.1 * * @param array $instance { * Instance data. * * @type string $text Content. * @type bool|string $filter Whether autop or content filters should apply. * @type bool $legacy Whether widget is in legacy mode. * } * @return bool Whether Text widget instance contains legacy data. */ public function is_legacy_instance( $instance ) { // Legacy mode when not in visual mode. if ( isset( $instance['visual'] ) ) { return ! $instance['visual']; } // Or, the widget has been added/updated in 4.8.0 then filter prop is 'content' and it is no longer legacy. if ( isset( $instance['filter'] ) && 'content' === $instance['filter'] ) { return false; } // If the text is empty, then nothing is preventing migration to TinyMCE. if ( empty( $instance['text'] ) ) { return false; } $wpautop = ! empty( $instance['filter'] ); $has_line_breaks = ( str_contains( trim( $instance['text'] ), "\n" ) ); // If auto-paragraphs are not enabled and there are line breaks, then ensure legacy mode. if ( ! $wpautop && $has_line_breaks ) { return true; } // If an HTML comment is present, assume legacy mode. if ( str_contains( $instance['text'], '<!--' ) ) { return true; } // In the rare case that DOMDocument is not available we cannot reliably sniff content and so we assume legacy. if ( ! class_exists( 'DOMDocument' ) ) { // @codeCoverageIgnoreStart return true; // @codeCoverageIgnoreEnd } $doc = new DOMDocument(); // Suppress warnings generated by loadHTML. $errors = libxml_use_internal_errors( true ); // phpcs:ignore WordPress.PHP.NoSilencedErrors.Discouraged @$doc->loadHTML( sprintf( '<!DOCTYPE html><html><head><meta charset="%s"></head><body>%s</body></html>', esc_attr( get_bloginfo( 'charset' ) ), $instance['text'] ) ); libxml_use_internal_errors( $errors ); $body = $doc->getElementsByTagName( 'body' )->item( 0 ); // See $allowedposttags. $safe_elements_attributes = array( 'strong' => array(), 'em' => array(), 'b' => array(), 'i' => array(), 'u' => array(), 's' => array(), 'ul' => array(), 'ol' => array(), 'li' => array(), 'hr' => array(), 'abbr' => array(), 'acronym' => array(), 'code' => array(), 'dfn' => array(), 'a' => array( 'href' => true, ), 'img' => array( 'src' => true, 'alt' => true, ), ); $safe_empty_elements = array( 'img', 'hr', 'iframe' ); foreach ( $body->getElementsByTagName( '*' ) as $element ) { /** @var DOMElement $element */ $tag_name = strtolower( $element->nodeName ); // If the element is not safe, then the instance is legacy. if ( ! isset( $safe_elements_attributes[ $tag_name ] ) ) { return true; } // If the element is not safely empty and it has empty contents, then legacy mode. if ( ! in_array( $tag_name, $safe_empty_elements, true ) && '' === trim( $element->textContent ) ) { return true; } // If an attribute is not recognized as safe, then the instance is legacy. foreach ( $element->attributes as $attribute ) { /** @var DOMAttr $attribute */ $attribute_name = strtolower( $attribute->nodeName ); if ( ! isset( $safe_elements_attributes[ $tag_name ][ $attribute_name ] ) ) { return true; } } } // Otherwise, the text contains no elements/attributes that TinyMCE could drop, and therefore the widget does not need legacy mode. return false; } /** * Filters gallery shortcode attributes. * * Prevents all of a site's attachments from being shown in a gallery displayed on a * non-singular template where a $post context is not available. * * @since 4.9.0 * * @param array $attrs Attributes. * @return array Attributes. */ public function _filter_gallery_shortcode_attrs( $attrs ) { if ( ! is_singular() && empty( $attrs['id'] ) && empty( $attrs['include'] ) ) { $attrs['id'] = -1; } return $attrs; } /** * Outputs the content for the current Text widget instance. * * @since 2.8.0 * * @global WP_Post $post Global post object. * * @param array $args Display arguments including 'before_title', 'after_title', * 'before_widget', and 'after_widget'. * @param array $instance Settings for the current Text widget instance. */ public function widget( $args, $instance ) { global $post; $title = ! empty( $instance['title'] ) ? $instance['title'] : ''; /** This filter is documented in wp-includes/widgets/class-wp-widget-pages.php */ $title = apply_filters( 'widget_title', $title, $instance, $this->id_base ); $text = ! empty( $instance['text'] ) ? $instance['text'] : ''; $is_visual_text_widget = ( ! empty( $instance['visual'] ) && ! empty( $instance['filter'] ) ); // In 4.8.0 only, visual Text widgets get filter=content, without visual prop; upgrade instance props just-in-time. if ( ! $is_visual_text_widget ) { $is_visual_text_widget = ( isset( $instance['filter'] ) && 'content' === $instance['filter'] ); } if ( $is_visual_text_widget ) { $instance['filter'] = true; $instance['visual'] = true; } /* * Suspend legacy plugin-supplied do_shortcode() for 'widget_text' filter for the visual Text widget to prevent * shortcodes being processed twice. Now do_shortcode() is added to the 'widget_text_content' filter in core itself * and it applies after wpautop() to prevent corrupting HTML output added by the shortcode. When do_shortcode() is * added to 'widget_text_content' then do_shortcode() will be manually called when in legacy mode as well. */ $widget_text_do_shortcode_priority = has_filter( 'widget_text', 'do_shortcode' ); $should_suspend_legacy_shortcode_support = ( $is_visual_text_widget && false !== $widget_text_do_shortcode_priority ); if ( $should_suspend_legacy_shortcode_support ) { remove_filter( 'widget_text', 'do_shortcode', $widget_text_do_shortcode_priority ); } // Override global $post so filters (and shortcodes) apply in a consistent context. $original_post = $post; if ( is_singular() ) { // Make sure post is always the queried object on singular queries (not from another sub-query that failed to clean up the global $post). $post = get_queried_object(); } else { // Nullify the $post global during widget rendering to prevent shortcodes from running with the unexpected context on archive queries. $post = null; } // Prevent dumping out all attachments from the media library. add_filter( 'shortcode_atts_gallery', array( $this, '_filter_gallery_shortcode_attrs' ) ); /** * Filters the content of the Text widget. * * @since 2.3.0 * @since 4.4.0 Added the `$widget` parameter. * @since 4.8.1 The `$widget` param may now be a `WP_Widget_Custom_HTML` object in addition to a `WP_Widget_Text` object. * * @param string $text The widget content. * @param array $instance Array of settings for the current widget. * @param WP_Widget_Text|WP_Widget_Custom_HTML $widget Current text or HTML widget instance. */ $text = apply_filters( 'widget_text', $text, $instance, $this ); if ( $is_visual_text_widget ) { /** * Filters the content of the Text widget to apply changes expected from the visual (TinyMCE) editor. * * By default a subset of the_content filters are applied, including wpautop and wptexturize. * * @since 4.8.0 * * @param string $text The widget content. * @param array $instance Array of settings for the current widget. * @param WP_Widget_Text $widget Current Text widget instance. */ $text = apply_filters( 'widget_text_content', $text, $instance, $this ); } else { // Now in legacy mode, add paragraphs and line breaks when checkbox is checked. if ( ! empty( $instance['filter'] ) ) { $text = wpautop( $text ); } /* * Manually do shortcodes on the content when the core-added filter is present. It is added by default * in core by adding do_shortcode() to the 'widget_text_content' filter to apply after wpautop(). * Since the legacy Text widget runs wpautop() after 'widget_text' filters are applied, the widget in * legacy mode here manually applies do_shortcode() on the content unless the default * core filter for 'widget_text_content' has been removed, or if do_shortcode() has already * been applied via a plugin adding do_shortcode() to 'widget_text' filters. */ if ( has_filter( 'widget_text_content', 'do_shortcode' ) && ! $widget_text_do_shortcode_priority ) { if ( ! empty( $instance['filter'] ) ) { $text = shortcode_unautop( $text ); } $text = do_shortcode( $text ); } } // Restore post global. $post = $original_post; remove_filter( 'shortcode_atts_gallery', array( $this, '_filter_gallery_shortcode_attrs' ) ); // Undo suspension of legacy plugin-supplied shortcode handling. if ( $should_suspend_legacy_shortcode_support ) { add_filter( 'widget_text', 'do_shortcode', $widget_text_do_shortcode_priority ); } echo $args['before_widget']; if ( ! empty( $title ) ) { echo $args['before_title'] . $title . $args['after_title']; } $text = preg_replace_callback( '#<(video|iframe|object|embed)\s[^>]*>#i', array( $this, 'inject_video_max_width_style' ), $text ); // Adds 'noopener' relationship, without duplicating values, to all HTML A elements that have a target. $text = wp_targeted_link_rel( $text ); ?> <div class="textwidget"><?php echo $text; ?></div> <?php echo $args['after_widget']; } /** * Injects max-width and removes height for videos too constrained to fit inside sidebars on frontend. * * @since 4.9.0 * * @see WP_Widget_Media_Video::inject_video_max_width_style() * * @param array $matches Pattern matches from preg_replace_callback. * @return string HTML Output. */ public function inject_video_max_width_style( $matches ) { $html = $matches[0]; $html = preg_replace( '/\sheight="\d+"/', '', $html ); $html = preg_replace( '/\swidth="\d+"/', '', $html ); $html = preg_replace( '/(?<=width:)\s*\d+px(?=;?)/', '100%', $html ); return $html; } /** * Handles updating settings for the current Text widget instance. * * @since 2.8.0 * * @param array $new_instance New settings for this instance as input by the user via * WP_Widget::form(). * @param array $old_instance Old settings for this instance. * @return array Settings to save or bool false to cancel saving. */ public function update( $new_instance, $old_instance ) { $new_instance = wp_parse_args( $new_instance, array( 'title' => '', 'text' => '', 'filter' => false, // For back-compat. 'visual' => null, // Must be explicitly defined. ) ); $instance = $old_instance; $instance['title'] = sanitize_text_field( $new_instance['title'] ); if ( current_user_can( 'unfiltered_html' ) ) { $instance['text'] = $new_instance['text']; } else { $instance['text'] = wp_kses_post( $new_instance['text'] ); } $instance['filter'] = ! empty( $new_instance['filter'] ); // Upgrade 4.8.0 format. if ( isset( $old_instance['filter'] ) && 'content' === $old_instance['filter'] ) { $instance['visual'] = true; } if ( 'content' === $new_instance['filter'] ) { $instance['visual'] = true; } if ( isset( $new_instance['visual'] ) ) { $instance['visual'] = ! empty( $new_instance['visual'] ); } // Filter is always true in visual mode. if ( ! empty( $instance['visual'] ) ) { $instance['filter'] = true; } return $instance; } /** * Enqueues preview scripts. * * These scripts normally are enqueued just-in-time when a playlist shortcode is used. * However, in the customizer, a playlist shortcode may be used in a text widget and * dynamically added via selective refresh, so it is important to unconditionally enqueue them. * * @since 4.9.3 */ public function enqueue_preview_scripts() { require_once dirname( __DIR__ ) . '/media.php'; wp_playlist_scripts( 'audio' ); wp_playlist_scripts( 'video' ); } /** * Loads the required scripts and styles for the widget control. * * @since 4.8.0 */ public function enqueue_admin_scripts() { wp_enqueue_editor(); wp_enqueue_media(); wp_enqueue_script( 'text-widgets' ); wp_add_inline_script( 'text-widgets', sprintf( 'wp.textWidgets.idBases.push( %s );', wp_json_encode( $this->id_base ) ) ); wp_add_inline_script( 'text-widgets', 'wp.textWidgets.init();', 'after' ); } /** * Outputs the Text widget settings form. * * @since 2.8.0 * @since 4.8.0 Form only contains hidden inputs which are synced with JS template. * @since 4.8.1 Restored original form to be displayed when in legacy mode. * * @see WP_Widget_Text::render_control_template_scripts() * @see _WP_Editors::editor() * * @param array $instance Current settings. */ public function form( $instance ) { $instance = wp_parse_args( (array) $instance, array( 'title' => '', 'text' => '', ) ); ?> <?php if ( ! $this->is_legacy_instance( $instance ) ) : ?> <?php if ( user_can_richedit() ) { add_filter( 'the_editor_content', 'format_for_editor', 10, 2 ); $default_editor = 'tinymce'; } else { $default_editor = 'html'; } /** This filter is documented in wp-includes/class-wp-editor.php */ $text = apply_filters( 'the_editor_content', $instance['text'], $default_editor ); // Reset filter addition. if ( user_can_richedit() ) { remove_filter( 'the_editor_content', 'format_for_editor' ); } // Prevent premature closing of textarea in case format_for_editor() didn't apply or the_editor_content filter did a wrong thing. $escaped_text = preg_replace( '#</textarea#i', '</textarea', $text ); ?> <input id="<?php echo $this->get_field_id( 'title' ); ?>" name="<?php echo $this->get_field_name( 'title' ); ?>" class="title sync-input" type="hidden" value="<?php echo esc_attr( $instance['title'] ); ?>"> <textarea id="<?php echo $this->get_field_id( 'text' ); ?>" name="<?php echo $this->get_field_name( 'text' ); ?>" class="text sync-input" hidden><?php echo $escaped_text; ?></textarea> <input id="<?php echo $this->get_field_id( 'filter' ); ?>" name="<?php echo $this->get_field_name( 'filter' ); ?>" class="filter sync-input" type="hidden" value="on"> <input id="<?php echo $this->get_field_id( 'visual' ); ?>" name="<?php echo $this->get_field_name( 'visual' ); ?>" class="visual sync-input" type="hidden" value="on"> <?php else : ?> <input id="<?php echo $this->get_field_id( 'visual' ); ?>" name="<?php echo $this->get_field_name( 'visual' ); ?>" class="visual" type="hidden" value=""> <p> <label for="<?php echo $this->get_field_id( 'title' ); ?>"><?php _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> <?php if ( ! isset( $instance['visual'] ) ) { $widget_info_message = __( 'This widget may contain code that may work better in the “Custom HTML” widget. How about trying that widget instead?' ); } else { $widget_info_message = __( 'This widget may have contained code that may work better in the “Custom HTML” widget. If you have not yet, how about trying that widget instead?' ); } wp_admin_notice( $widget_info_message, array( 'type' => 'info', 'additional_classes' => array( 'notice-alt', 'inline' ), ) ); ?> <p> <label for="<?php echo $this->get_field_id( 'text' ); ?>"><?php _e( 'Content:' ); ?></label> <textarea class="widefat" rows="16" cols="20" id="<?php echo $this->get_field_id( 'text' ); ?>" name="<?php echo $this->get_field_name( 'text' ); ?>"><?php echo esc_textarea( $instance['text'] ); ?></textarea> </p> <p> <input id="<?php echo $this->get_field_id( 'filter' ); ?>" name="<?php echo $this->get_field_name( 'filter' ); ?>" type="checkbox"<?php checked( ! empty( $instance['filter'] ) ); ?> /> <label for="<?php echo $this->get_field_id( 'filter' ); ?>"><?php _e( 'Automatically add paragraphs' ); ?></label> </p> <?php endif; } /** * Renders form template scripts. * * @since 4.8.0 * @since 4.9.0 The method is now static. */ public static function render_control_template_scripts() { $dismissed_pointers = explode( ',', (string) get_user_meta( get_current_user_id(), 'dismissed_wp_pointers', true ) ); ?> <script type="text/html" id="tmpl-widget-text-control-fields"> <# var elementIdPrefix = 'el' + String( Math.random() ).replace( /\D/g, '' ) + '_' #> <p> <label for="{{ elementIdPrefix }}title"><?php esc_html_e( 'Title:' ); ?></label> <input id="{{ elementIdPrefix }}title" type="text" class="widefat title"> </p> <?php if ( ! in_array( 'text_widget_custom_html', $dismissed_pointers, true ) ) : ?> <div hidden class="wp-pointer custom-html-widget-pointer wp-pointer-top"> <div class="wp-pointer-content"> <h3><?php _e( 'New Custom HTML Widget' ); ?></h3> <?php if ( is_customize_preview() ) : ?> <p><?php _e( 'Did you know there is a “Custom HTML” widget now? You can find it by pressing the “<a class="add-widget" href="#">Add a Widget</a>” button and searching for “HTML”. Check it out to add some custom code to your site!' ); ?></p> <?php else : ?> <p><?php _e( 'Did you know there is a “Custom HTML” widget now? You can find it by scanning the list of available widgets on this screen. Check it out to add some custom code to your site!' ); ?></p> <?php endif; ?> <div class="wp-pointer-buttons"> <a class="close" href="#"><?php _e( 'Dismiss' ); ?></a> </div> </div> <div class="wp-pointer-arrow"> <div class="wp-pointer-arrow-inner"></div> </div> </div> <?php endif; ?> <?php if ( ! in_array( 'text_widget_paste_html', $dismissed_pointers, true ) ) : ?> <div hidden class="wp-pointer paste-html-pointer wp-pointer-top"> <div class="wp-pointer-content"> <h3><?php _e( 'Did you just paste HTML?' ); ?></h3> <p><?php _e( 'Hey there, looks like you just pasted HTML into the “Visual” tab of the Text widget. You may want to paste your code into the “Text” tab instead. Alternately, try out the new “Custom HTML” widget!' ); ?></p> <div class="wp-pointer-buttons"> <a class="close" href="#"><?php _e( 'Dismiss' ); ?></a> </div> </div> <div class="wp-pointer-arrow"> <div class="wp-pointer-arrow-inner"></div> </div> </div> <?php endif; ?> <p> <label for="{{ elementIdPrefix }}text" class="screen-reader-text"><?php /* translators: Hidden accessibility text. */ esc_html_e( 'Content:' ); ?></label> <textarea id="{{ elementIdPrefix }}text" class="widefat text wp-editor-area" style="height: 200px" rows="16" cols="20"></textarea> </p> </script> <?php } } text-widgets.min.js 0000644 00000013335 14717746353 0010342 0 ustar 00 /*! This file is auto-generated */ wp.textWidgets=function(r){"use strict";var u={dismissedPointers:[],idBases:["text"]};return u.TextWidgetControl=Backbone.View.extend({events:{},initialize:function(e){var n=this;if(!e.el)throw new Error("Missing options.el");if(!e.syncContainer)throw new Error("Missing options.syncContainer");Backbone.View.prototype.initialize.call(n,e),n.syncContainer=e.syncContainer,n.$el.addClass("text-widget-fields"),n.$el.html(wp.template("widget-text-control-fields")),n.customHtmlWidgetPointer=n.$el.find(".wp-pointer.custom-html-widget-pointer"),n.customHtmlWidgetPointer.length&&(n.customHtmlWidgetPointer.find(".close").on("click",function(e){e.preventDefault(),n.customHtmlWidgetPointer.hide(),r("#"+n.fields.text.attr("id")+"-html").trigger("focus"),n.dismissPointers(["text_widget_custom_html"])}),n.customHtmlWidgetPointer.find(".add-widget").on("click",function(e){e.preventDefault(),n.customHtmlWidgetPointer.hide(),n.openAvailableWidgetsPanel()})),n.pasteHtmlPointer=n.$el.find(".wp-pointer.paste-html-pointer"),n.pasteHtmlPointer.length&&n.pasteHtmlPointer.find(".close").on("click",function(e){e.preventDefault(),n.pasteHtmlPointer.hide(),n.editor.focus(),n.dismissPointers(["text_widget_custom_html","text_widget_paste_html"])}),n.fields={title:n.$el.find(".title"),text:n.$el.find(".text")},_.each(n.fields,function(t,i){t.on("input change",function(){var e=n.syncContainer.find(".sync-input."+i);e.val()!==t.val()&&(e.val(t.val()),e.trigger("change"))}),t.val(n.syncContainer.find(".sync-input."+i).val())})},dismissPointers:function(e){_.each(e,function(e){wp.ajax.post("dismiss-wp-pointer",{pointer:e}),u.dismissedPointers.push(e)})},openAvailableWidgetsPanel:function(){var t;wp.customize.section.each(function(e){e.extended(wp.customize.Widgets.SidebarSection)&&e.expanded()&&(t=wp.customize.control("sidebars_widgets["+e.params.sidebarId+"]"))}),t&&setTimeout(function(){wp.customize.Widgets.availableWidgetsPanel.open(t),wp.customize.Widgets.availableWidgetsPanel.$search.val("HTML").trigger("keyup")})},updateFields:function(){var e,t=this;t.fields.title.is(document.activeElement)||(e=t.syncContainer.find(".sync-input.title"),t.fields.title.val(e.val())),e=t.syncContainer.find(".sync-input.text"),t.fields.text.is(":visible")?t.fields.text.is(document.activeElement)||t.fields.text.val(e.val()):t.editor&&!t.editorFocused&&e.val()!==t.fields.text.val()&&t.editor.setContent(wp.oldEditor.autop(e.val()))},initializeEditor:function(){var d,e,o,t,s=this,a=1e3,l=!1,c=!1;e=s.fields.text,d=e.attr("id"),t=e.val(),o=function(){s.editor.isDirty()&&(wp.customize&&wp.customize.state&&(wp.customize.state("processing").set(wp.customize.state("processing").get()+1),_.delay(function(){wp.customize.state("processing").set(wp.customize.state("processing").get()-1)},300)),s.editor.isHidden()||s.editor.save()),c&&t!==e.val()&&(e.trigger("change"),c=!1,t=e.val())},s.syncContainer.closest(".widget").find("[name=savewidget]:first").on("click",function(){o()}),function e(){var t,i,n;if(document.getElementById(d))if(void 0===window.tinymce)wp.oldEditor.initialize(d,{quicktags:!0,mediaButtons:!0});else{if(tinymce.get(d)&&(l=tinymce.get(d).isHidden(),wp.oldEditor.remove(d)),r(document).one("wp-before-tinymce-init.text-widget-init",function(e,t){t.plugins&&!/\bwpview\b/.test(t.plugins)&&(t.plugins+=",wpview")}),wp.oldEditor.initialize(d,{tinymce:{wpautop:!0},quicktags:!0,mediaButtons:!0}),n=function(e){e.show(),e.find(".close").trigger("focus"),wp.a11y.speak(e.find("h3, p").map(function(){return r(this).text()}).get().join("\n\n"))},!(t=window.tinymce.get(d)))throw new Error("Failed to initialize editor");i=function(){r(t.getWin()).on("pagehide",function(){_.defer(e)}),l&&switchEditors.go(d,"html"),r("#"+d+"-html").on("click",function(){s.pasteHtmlPointer.hide(),-1===u.dismissedPointers.indexOf("text_widget_custom_html")&&n(s.customHtmlWidgetPointer)}),r("#"+d+"-tmce").on("click",function(){s.customHtmlWidgetPointer.hide()}),t.on("pastepreprocess",function(e){e=e.content,-1===u.dismissedPointers.indexOf("text_widget_paste_html")&&e&&/<\w+.*?>/.test(e)&&_.delay(function(){n(s.pasteHtmlPointer)},250)})},t.initialized?i():t.on("init",i),s.editorFocused=!1,t.on("focus",function(){s.editorFocused=!0}),t.on("paste",function(){t.setDirty(!0),o()}),t.on("NodeChange",function(){c=!0}),t.on("NodeChange",_.debounce(o,a)),t.on("blur hide",function(){s.editorFocused=!1,o()}),s.editor=t}}()}}),u.widgetControls={},u.handleWidgetAdded=function(e,t){var i,n,d,o=t.find("> .widget-inside > .form, > .widget-inside > form"),s=o.find("> .id_base").val();-1===u.idBases.indexOf(s)||(s=o.find(".widget-id").val(),u.widgetControls[s])||o.find(".visual").val()&&(o=r("<div></div>"),(d=t.find(".widget-content:first")).before(o),i=new u.TextWidgetControl({el:o,syncContainer:d}),u.widgetControls[s]=i,(n=function(){t.hasClass("open")?i.initializeEditor():setTimeout(n,50)})())},u.setupAccessibleMode=function(){var e,t=r(".editwidget > form");0!==t.length&&(e=t.find(".id_base").val(),-1!==u.idBases.indexOf(e))&&t.find(".visual").val()&&(e=r("<div></div>"),(t=t.find("> .widget-inside")).before(e),new u.TextWidgetControl({el:e,syncContainer:t}).initializeEditor())},u.handleWidgetUpdated=function(e,t){var t=t.find("> .widget-inside > .form, > .widget-inside > form"),i=t.find("> .id_base").val();-1!==u.idBases.indexOf(i)&&(i=t.find("> .widget-id").val(),t=u.widgetControls[i])&&t.updateFields()},u.init=function(){var e=r(document);e.on("widget-added",u.handleWidgetAdded),e.on("widget-synced widget-updated",u.handleWidgetUpdated),r(function(){"widgets"===window.pagenow&&(r(".widgets-holder-wrap:not(#available-widgets)").find("div.widget").one("click.toggle-widget-expanded",function(){var e=r(this);u.handleWidgetAdded(new jQuery.Event("widget-added"),e)}),u.setupAccessibleMode())})},u}(jQuery); media-gallery-widget.min.js 0000644 00000007266 14717746353 0011715 0 ustar 00 /*! This file is auto-generated */ !function(i){"use strict";var a=wp.media.view.MediaFrame.Post.extend({createStates:function(){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:!0,library:wp.media.query(_.defaults({type:"image"},this.options.library))}),new wp.media.controller.GalleryEdit({library:this.options.selection,editing:this.options.editing,menu:"gallery"}),new wp.media.controller.GalleryAdd])}}),e=i.MediaWidgetModel.extend({}),t=i.MediaWidgetControl.extend({events:_.extend({},i.MediaWidgetControl.prototype.events,{"click .media-widget-gallery-preview":"editMedia"}),initialize:function(e){var t=this;i.MediaWidgetControl.prototype.initialize.call(t,e),_.bindAll(t,"updateSelectedAttachments","handleAttachmentDestroy"),t.selectedAttachments=new wp.media.model.Attachments,t.model.on("change:ids",t.updateSelectedAttachments),t.selectedAttachments.on("change",t.renderPreview),t.selectedAttachments.on("reset",t.renderPreview),t.updateSelectedAttachments(),wp.customize&&wp.customize.previewer&&t.selectedAttachments.on("change",function(){wp.customize.previewer.send("refresh-widget-partial",t.model.get("widget_id"))})},updateSelectedAttachments:function(){var e,t=this,i=t.model.get("ids"),d=_.pluck(t.selectedAttachments.models,"id"),a=_.difference(d,i);_.each(a,function(e){t.selectedAttachments.remove(t.selectedAttachments.get(e))}),_.difference(i,d).length&&(e=wp.media.query({order:"ASC",orderby:"post__in",perPage:-1,post__in:i,query:!0,type:"image"})).more().done(function(){t.selectedAttachments.reset(e.models)})},renderPreview:function(){var e=this,t=e.$el.find(".media-widget-preview"),i=wp.template("wp-media-widget-gallery-preview"),d=e.previewTemplateProps.toJSON();d.attachments={},e.selectedAttachments.each(function(e){d.attachments[e.id]=e.toJSON()}),t.html(i(d))},isSelected:function(){return!this.model.get("error")&&0<this.model.get("ids").length},editMedia:function(){var i,d=this,e=new wp.media.model.Selection(d.selectedAttachments.models,{multiple:!0}),t=d.mapModelToMediaFrameProps(d.model.toJSON());e.gallery=new Backbone.Model(t),t.size&&d.displaySettings.set("size",t.size),i=new a({frame:"manage",text:d.l10n.add_to_widget,selection:e,mimeType:d.mime_type,selectedDisplaySettings:d.displaySettings,showDisplaySettings:d.showDisplaySettings,metadata:t,editing:!0,multiple:!0,state:"gallery-edit"}),(wp.media.frame=i).on("update",function(e){var t=i.state(),e=e||t.get("selection");e&&(e.gallery&&d.model.set(d.mapMediaToModelProps(e.gallery.toJSON())),d.selectedAttachments.reset(e.models),d.model.set({ids:_.pluck(e.models,"id")}))}),i.$el.addClass("media-widget"),i.open(),e&&e.on("destroy",d.handleAttachmentDestroy)},selectMedia:function(){var i,d=this,e=new wp.media.model.Selection(d.selectedAttachments.models,{multiple:!0}),t=d.mapModelToMediaFrameProps(d.model.toJSON());t.size&&d.displaySettings.set("size",t.size),i=new a({frame:"select",text:d.l10n.add_to_widget,selection:e,mimeType:d.mime_type,selectedDisplaySettings:d.displaySettings,showDisplaySettings:d.showDisplaySettings,metadata:t,state:"gallery"}),(wp.media.frame=i).on("update",function(e){var t=i.state(),e=e||t.get("selection");e&&(e.gallery&&d.model.set(d.mapMediaToModelProps(e.gallery.toJSON())),d.selectedAttachments.reset(e.models),d.model.set({ids:_.pluck(e.models,"id")}))}),i.$el.addClass("media-widget"),i.open(),e&&e.on("destroy",d.handleAttachmentDestroy),i.$el.find(":focusable:first").focus()},handleAttachmentDestroy:function(e){this.model.set({ids:_.difference(this.model.get("ids"),[e.id])})}});i.controlConstructors.media_gallery=t,i.modelConstructors.media_gallery=e}(wp.mediaWidgets); media-gallery-widget.js 0000644 00000024162 14717746353 0011125 0 ustar 00 /** * @output wp-admin/js/widgets/media-gallery-widget.js */ /* eslint consistent-this: [ "error", "control" ] */ (function( component ) { 'use strict'; var GalleryWidgetModel, GalleryWidgetControl, GalleryDetailsMediaFrame; /** * Custom gallery details frame. * * @since 4.9.0 * @class wp.mediaWidgets~GalleryDetailsMediaFrame * @augments wp.media.view.MediaFrame.Post */ GalleryDetailsMediaFrame = wp.media.view.MediaFrame.Post.extend(/** @lends wp.mediaWidgets~GalleryDetailsMediaFrame.prototype */{ /** * Create the default states. * * @since 4.9.0 * @return {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 wp.mediaWidgets.modelConstructors.media_gallery * @augments wp.mediaWidgets.MediaWidgetModel */ GalleryWidgetModel = component.MediaWidgetModel.extend(/** @lends wp.mediaWidgets.modelConstructors.media_gallery.prototype */{} ); GalleryWidgetControl = component.MediaWidgetControl.extend(/** @lends wp.mediaWidgets.controlConstructors.media_gallery.prototype */{ /** * View events. * * @since 4.9.0 * @type {object} */ events: _.extend( {}, component.MediaWidgetControl.prototype.events, { 'click .media-widget-gallery-preview': 'editMedia' } ), /** * Gallery widget control. * * See WP_Widget_Gallery::enqueue_admin_scripts() for amending prototype from PHP exports. * * @constructs wp.mediaWidgets.controlConstructors.media_gallery * @augments wp.mediaWidgets.MediaWidgetControl * * @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. * @return {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(); /* * Refresh a Gallery widget partial when the user modifies one of the selected attachments. * This ensures that when an attachment's caption is updated in the media modal the Gallery * widget in the preview will then be refreshed to show the change. Normally doing this * would not be necessary because all of the state should be contained inside the changeset, * as everything done in the Customizer should not make a change to the site unless the * changeset itself is published. Attachments are a current exception to this rule. * For a proposal to include attachments in the customized state, see #37887. */ if ( wp.customize && wp.customize.previewer ) { control.selectedAttachments.on( 'change', function() { wp.customize.previewer.send( 'refresh-widget-partial', control.model.get( 'widget_id' ) ); } ); } }, /** * Update the selected attachments if necessary. * * @since 4.9.0 * @return {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 * @return {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 * @return {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 * @return {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( mediaFrameProps ); 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 * @return {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. * @return {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 ); media-video-widget.min.js 0000644 00000005216 14717746353 0011355 0 ustar 00 /*! This file is auto-generated */ !function(t){"use strict";var i=wp.media.view.MediaFrame.VideoDetails.extend({createStates:function(){this.states.add([new wp.media.controller.VideoDetails({media:this.media}),new wp.media.controller.MediaLibrary({type:"video",id:"add-video-source",title:wp.media.view.l10n.videoAddSourceTitle,toolbar:"add-video-source",media:this.media,menu:!1}),new wp.media.controller.MediaLibrary({type:"text",id:"add-track",title:wp.media.view.l10n.videoAddTrackTitle,toolbar:"add-track",media:this.media,menu:"video-details"})])}}),e=t.MediaWidgetModel.extend({}),d=t.MediaWidgetControl.extend({showDisplaySettings:!1,oembedResponses:{},mapModelToMediaFrameProps:function(e){e=t.MediaWidgetControl.prototype.mapModelToMediaFrameProps.call(this,e);return e.link="embed",e},fetchEmbed:function(){var t=this,d=t.model.get("url");t.oembedResponses[d]||(t.fetchEmbedDfd&&"pending"===t.fetchEmbedDfd.state()&&t.fetchEmbedDfd.abort(),t.fetchEmbedDfd=wp.apiRequest({url:wp.media.view.settings.oEmbedProxyUrl,data:{url:t.model.get("url"),maxwidth:t.model.get("width"),maxheight:t.model.get("height"),discover:!1},type:"GET",dataType:"json",context:t}),t.fetchEmbedDfd.done(function(e){t.oembedResponses[d]=e,t.renderPreview()}),t.fetchEmbedDfd.fail(function(){t.oembedResponses[d]=null}))},isHostedVideo:function(){return!0},renderPreview:function(){var e,t,d=this,i="",o=!1,a=d.model.get("attachment_id"),s=d.model.get("url"),m=d.model.get("error");(a||s)&&((t=d.selectedAttachment.get("mime"))&&a?_.contains(_.values(wp.media.view.settings.embedMimes),t)||(m="unsupported_file_type"):a||((t=document.createElement("a")).href=s,(t=t.pathname.toLowerCase().match(/\.(\w+)$/))?_.contains(_.keys(wp.media.view.settings.embedMimes),t[1])||(m="unsupported_file_type"):o=!0),o&&(d.fetchEmbed(),d.oembedResponses[s])&&(e=d.oembedResponses[s].thumbnail_url,i=d.oembedResponses[s].html.replace(/\swidth="\d+"/,' width="100%"').replace(/\sheight="\d+"/,"")),t=d.$el.find(".media-widget-preview"),d=wp.template("wp-media-widget-video-preview"),t.html(d({model:{attachment_id:a,html:i,src:s,poster:e},is_oembed:o,error:m})),wp.mediaelement.initialize())},editMedia:function(){var t=this,e=t.mapModelToMediaFrameProps(t.model.toJSON()),d=new i({frame:"video",state:"video-details",metadata:e});(wp.media.frame=d).$el.addClass("media-widget"),e=function(e){t.selectedAttachment.set(e),t.model.set(_.extend(_.omit(t.model.defaults(),"title"),t.mapMediaToModelProps(e),{error:!1}))},d.state("video-details").on("update",e),d.state("replace-video").on("replace",e),d.on("close",function(){d.detach()}),d.open()}});t.controlConstructors.media_video=d,t.modelConstructors.media_video=e}(wp.mediaWidgets); media-image-widget.js 0000644 00000012534 14717746353 0010550 0 ustar 00 /** * @output wp-admin/js/widgets/media-image-widget.js */ /* eslint consistent-this: [ "error", "control" ] */ (function( component, $ ) { 'use strict'; var ImageWidgetModel, ImageWidgetControl; /** * Image widget model. * * See WP_Widget_Media_Image::enqueue_admin_scripts() for amending prototype from PHP exports. * * @class wp.mediaWidgets.modelConstructors.media_image * @augments wp.mediaWidgets.MediaWidgetModel */ ImageWidgetModel = component.MediaWidgetModel.extend({}); /** * Image widget control. * * See WP_Widget_Media_Image::enqueue_admin_scripts() for amending prototype from PHP exports. * * @class wp.mediaWidgets.controlConstructors.media_audio * @augments wp.mediaWidgets.MediaWidgetControl */ ImageWidgetControl = component.MediaWidgetControl.extend(/** @lends wp.mediaWidgets.controlConstructors.media_image.prototype */{ /** * View events. * * @type {object} */ events: _.extend( {}, component.MediaWidgetControl.prototype.events, { 'click .media-widget-preview.populated': 'editMedia' } ), /** * Render preview. * * @return {void} */ renderPreview: function renderPreview() { var control = this, previewContainer, previewTemplate, fieldsContainer, fieldsTemplate, linkInput; if ( ! control.model.get( 'attachment_id' ) && ! control.model.get( 'url' ) ) { return; } 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 ) ) { fieldsContainer = control.$el.find( '.media-widget-fields' ); fieldsTemplate = wp.template( 'wp-media-widget-image-fields' ); fieldsContainer.html( fieldsTemplate( control.previewTemplateProps.toJSON() ) ); } }, /** * Open the media image-edit frame to modify the selected item. * * @return {void} */ editMedia: function editMedia() { var control = this, mediaFrame, updateCallback, defaultSync, metadata; metadata = control.mapModelToMediaFrameProps( control.model.toJSON() ); // Needed or else none will not be selected if linkUrl is not also empty. if ( 'none' === metadata.link ) { metadata.linkUrl = ''; } // Set up the media frame. mediaFrame = wp.media({ frame: 'image', state: 'image-details', metadata: metadata }); mediaFrame.$el.addClass( 'media-widget' ); updateCallback = function() { var mediaProps, linkType; // Update cached attachment object to avoid having to re-fetch. This also triggers re-rendering of preview. mediaProps = mediaFrame.state().attributes.image.toJSON(); linkType = mediaProps.link; mediaProps.link = mediaProps.linkUrl; control.selectedAttachment.set( mediaProps ); control.displaySettings.set( 'link', linkType ); control.model.set( _.extend( control.mapMediaToModelProps( mediaProps ), { error: false } ) ); }; mediaFrame.state( 'image-details' ).on( 'update', updateCallback ); mediaFrame.state( 'replace-image' ).on( 'replace', updateCallback ); // Disable syncing of attachment changes back to server. See <https://core.trac.wordpress.org/ticket/40403>. defaultSync = wp.media.model.Attachment.prototype.sync; wp.media.model.Attachment.prototype.sync = function rejectedSync() { return $.Deferred().rejectWith( this ).promise(); }; mediaFrame.on( 'close', function onClose() { mediaFrame.detach(); wp.media.model.Attachment.prototype.sync = defaultSync; }); mediaFrame.open(); }, /** * Get props which are merged on top of the model when an embed is chosen (as opposed to an attachment). * * @return {Object} Reset/override props. */ getEmbedResetProps: function getEmbedResetProps() { return _.extend( component.MediaWidgetControl.prototype.getEmbedResetProps.call( this ), { size: 'full', width: 0, height: 0 } ); }, /** * Get the instance props from the media selection frame. * * Prevent the image_title attribute from being initially set when adding an image from the media library. * * @param {wp.media.view.MediaFrame.Select} mediaFrame - Select frame. * @return {Object} Props. */ getModelPropsFromMediaFrame: function getModelPropsFromMediaFrame( mediaFrame ) { var control = this; return _.omit( component.MediaWidgetControl.prototype.getModelPropsFromMediaFrame.call( control, mediaFrame ), 'image_title' ); }, /** * Map model props to preview template props. * * @return {Object} Preview template props. */ mapModelToPreviewTemplateProps: function mapModelToPreviewTemplateProps() { var control = this, previewTemplateProps, url; url = control.model.get( 'url' ); previewTemplateProps = component.MediaWidgetControl.prototype.mapModelToPreviewTemplateProps.call( control ); previewTemplateProps.currentFilename = url ? url.replace( /\?.*$/, '' ).replace( /^.+\//, '' ) : ''; previewTemplateProps.link_url = control.model.get( 'link_url' ); return previewTemplateProps; } }); // Exports. component.controlConstructors.media_image = ImageWidgetControl; component.modelConstructors.media_image = ImageWidgetModel; })( wp.mediaWidgets, jQuery ); media-audio-widget.js 0000644 00000010274 14717746353 0010566 0 ustar 00 /** * @output wp-admin/js/widgets/media-audio-widget.js */ /* eslint consistent-this: [ "error", "control" ] */ (function( component ) { 'use strict'; var AudioWidgetModel, AudioWidgetControl, AudioDetailsMediaFrame; /** * Custom audio details frame that removes the replace-audio state. * * @class wp.mediaWidgets.controlConstructors~AudioDetailsMediaFrame * @augments wp.media.view.MediaFrame.AudioDetails */ AudioDetailsMediaFrame = wp.media.view.MediaFrame.AudioDetails.extend(/** @lends wp.mediaWidgets.controlConstructors~AudioDetailsMediaFrame.prototype */{ /** * Create the default states. * * @return {void} */ createStates: function createStates() { this.states.add([ new wp.media.controller.AudioDetails({ media: this.media }), new wp.media.controller.MediaLibrary({ type: 'audio', id: 'add-audio-source', title: wp.media.view.l10n.audioAddSourceTitle, toolbar: 'add-audio-source', media: this.media, menu: false }) ]); } }); /** * Audio widget model. * * See WP_Widget_Audio::enqueue_admin_scripts() for amending prototype from PHP exports. * * @class wp.mediaWidgets.modelConstructors.media_audio * @augments wp.mediaWidgets.MediaWidgetModel */ AudioWidgetModel = component.MediaWidgetModel.extend({}); /** * Audio widget control. * * See WP_Widget_Audio::enqueue_admin_scripts() for amending prototype from PHP exports. * * @class wp.mediaWidgets.controlConstructors.media_audio * @augments wp.mediaWidgets.MediaWidgetControl */ AudioWidgetControl = component.MediaWidgetControl.extend(/** @lends wp.mediaWidgets.controlConstructors.media_audio.prototype */{ /** * Show display settings. * * @type {boolean} */ showDisplaySettings: false, /** * Map model props to media frame props. * * @param {Object} modelProps - Model props. * @return {Object} Media frame props. */ mapModelToMediaFrameProps: function mapModelToMediaFrameProps( modelProps ) { var control = this, mediaFrameProps; mediaFrameProps = component.MediaWidgetControl.prototype.mapModelToMediaFrameProps.call( control, modelProps ); mediaFrameProps.link = 'embed'; return mediaFrameProps; }, /** * Render preview. * * @return {void} */ renderPreview: function renderPreview() { var control = this, previewContainer, previewTemplate, attachmentId, attachmentUrl; attachmentId = control.model.get( 'attachment_id' ); attachmentUrl = control.model.get( 'url' ); if ( ! attachmentId && ! attachmentUrl ) { return; } previewContainer = control.$el.find( '.media-widget-preview' ); previewTemplate = wp.template( 'wp-media-widget-audio-preview' ); previewContainer.html( previewTemplate({ model: { attachment_id: control.model.get( 'attachment_id' ), src: attachmentUrl }, error: control.model.get( 'error' ) })); wp.mediaelement.initialize(); }, /** * Open the media audio-edit frame to modify the selected item. * * @return {void} */ editMedia: function editMedia() { var control = this, mediaFrame, metadata, updateCallback; metadata = control.mapModelToMediaFrameProps( control.model.toJSON() ); // Set up the media frame. mediaFrame = new AudioDetailsMediaFrame({ frame: 'audio', state: 'audio-details', metadata: metadata }); wp.media.frame = mediaFrame; mediaFrame.$el.addClass( 'media-widget' ); updateCallback = function( mediaFrameProps ) { // Update cached attachment object to avoid having to re-fetch. This also triggers re-rendering of preview. control.selectedAttachment.set( mediaFrameProps ); control.model.set( _.extend( control.model.defaults(), control.mapMediaToModelProps( mediaFrameProps ), { error: false } ) ); }; mediaFrame.state( 'audio-details' ).on( 'update', updateCallback ); mediaFrame.state( 'replace-audio' ).on( 'replace', updateCallback ); mediaFrame.on( 'close', function() { mediaFrame.detach(); }); mediaFrame.open(); } }); // Exports. component.controlConstructors.media_audio = AudioWidgetControl; component.modelConstructors.media_audio = AudioWidgetModel; })( wp.mediaWidgets ); media-widgets.js 0000644 00000123560 14717746353 0007655 0 ustar 00 /** * @output wp-admin/js/widgets/media-widgets.js */ /* eslint consistent-this: [ "error", "control" ] */ /** * @namespace wp.mediaWidgets * @memberOf wp */ wp.mediaWidgets = ( function( $ ) { 'use strict'; var component = {}; /** * Widget control (view) constructors, mapping widget id_base to subclass of MediaWidgetControl. * * Media widgets register themselves by assigning subclasses of MediaWidgetControl onto this object by widget ID base. * * @memberOf wp.mediaWidgets * * @type {Object.<string, wp.mediaWidgets.MediaWidgetModel>} */ component.controlConstructors = {}; /** * Widget model constructors, mapping widget id_base to subclass of MediaWidgetModel. * * Media widgets register themselves by assigning subclasses of MediaWidgetControl onto this object by widget ID base. * * @memberOf wp.mediaWidgets * * @type {Object.<string, wp.mediaWidgets.MediaWidgetModel>} */ component.modelConstructors = {}; component.PersistentDisplaySettingsLibrary = wp.media.controller.Library.extend(/** @lends wp.mediaWidgets.PersistentDisplaySettingsLibrary.prototype */{ /** * Library which persists the customized display settings across selections. * * @constructs wp.mediaWidgets.PersistentDisplaySettingsLibrary * @augments wp.media.controller.Library * * @param {Object} options - Options. * * @return {void} */ initialize: function initialize( options ) { _.bindAll( this, 'handleDisplaySettingChange' ); wp.media.controller.Library.prototype.initialize.call( this, options ); }, /** * Sync changes to the current display settings back into the current customized. * * @param {Backbone.Model} displaySettings - Modified display settings. * @return {void} */ handleDisplaySettingChange: function handleDisplaySettingChange( displaySettings ) { this.get( 'selectedDisplaySettings' ).set( displaySettings.attributes ); }, /** * Get the display settings model. * * Model returned is updated with the current customized display settings, * and an event listener is added so that changes made to the settings * will sync back into the model storing the session's customized display * settings. * * @param {Backbone.Model} model - Display settings model. * @return {Backbone.Model} Display settings model. */ display: function getDisplaySettingsModel( model ) { var display, selectedDisplaySettings = this.get( 'selectedDisplaySettings' ); display = wp.media.controller.Library.prototype.display.call( this, model ); display.off( 'change', this.handleDisplaySettingChange ); // Prevent duplicated event handlers. display.set( selectedDisplaySettings.attributes ); if ( 'custom' === selectedDisplaySettings.get( 'link_type' ) ) { display.linkUrl = selectedDisplaySettings.get( 'link_url' ); } display.on( 'change', this.handleDisplaySettingChange ); return display; } }); /** * Extended view for managing the embed UI. * * @class wp.mediaWidgets.MediaEmbedView * @augments wp.media.view.Embed */ component.MediaEmbedView = wp.media.view.Embed.extend(/** @lends wp.mediaWidgets.MediaEmbedView.prototype */{ /** * Initialize. * * @since 4.9.0 * * @param {Object} options - Options. * @return {void} */ initialize: function( options ) { var view = this, embedController; // eslint-disable-line consistent-this wp.media.view.Embed.prototype.initialize.call( view, options ); if ( 'image' !== view.controller.options.mimeType ) { embedController = view.controller.states.get( 'embed' ); embedController.off( 'scan', embedController.scanImage, embedController ); } }, /** * Refresh embed view. * * Forked override of {wp.media.view.Embed#refresh()} to suppress irrelevant "link text" field. * * @return {void} */ refresh: function refresh() { /** * @class wp.mediaWidgets~Constructor */ var Constructor; if ( 'image' === this.controller.options.mimeType ) { Constructor = wp.media.view.EmbedImage; } else { // This should be eliminated once #40450 lands of when this is merged into core. Constructor = wp.media.view.EmbedLink.extend(/** @lends wp.mediaWidgets~Constructor.prototype */{ /** * Set the disabled state on the Add to Widget button. * * @param {boolean} disabled - Disabled. * @return {void} */ setAddToWidgetButtonDisabled: function setAddToWidgetButtonDisabled( disabled ) { this.views.parent.views.parent.views.get( '.media-frame-toolbar' )[0].$el.find( '.media-button-select' ).prop( 'disabled', disabled ); }, /** * Set or clear an error notice. * * @param {string} notice - Notice. * @return {void} */ setErrorNotice: function setErrorNotice( notice ) { var embedLinkView = this, noticeContainer; // eslint-disable-line consistent-this noticeContainer = embedLinkView.views.parent.$el.find( '> .notice:first-child' ); if ( ! notice ) { if ( noticeContainer.length ) { noticeContainer.slideUp( 'fast' ); } } else { if ( ! noticeContainer.length ) { noticeContainer = $( '<div class="media-widget-embed-notice notice notice-error notice-alt" role="alert"></div>' ); noticeContainer.hide(); embedLinkView.views.parent.$el.prepend( noticeContainer ); } noticeContainer.empty(); noticeContainer.append( $( '<p>', { html: notice })); noticeContainer.slideDown( 'fast' ); } }, /** * Update oEmbed. * * @since 4.9.0 * * @return {void} */ updateoEmbed: function() { var embedLinkView = this, url; // eslint-disable-line consistent-this url = embedLinkView.model.get( 'url' ); // Abort if the URL field was emptied out. if ( ! url ) { embedLinkView.setErrorNotice( '' ); embedLinkView.setAddToWidgetButtonDisabled( true ); return; } if ( ! url.match( /^(http|https):\/\/.+\// ) ) { embedLinkView.controller.$el.find( '#embed-url-field' ).addClass( 'invalid' ); embedLinkView.setAddToWidgetButtonDisabled( true ); } wp.media.view.EmbedLink.prototype.updateoEmbed.call( embedLinkView ); }, /** * Fetch media. * * @return {void} */ fetch: function() { var embedLinkView = this, fetchSuccess, matches, fileExt, urlParser, url, re, youTubeEmbedMatch; // eslint-disable-line consistent-this url = embedLinkView.model.get( 'url' ); if ( embedLinkView.dfd && 'pending' === embedLinkView.dfd.state() ) { embedLinkView.dfd.abort(); } fetchSuccess = function( response ) { embedLinkView.renderoEmbed({ data: { body: response } }); embedLinkView.controller.$el.find( '#embed-url-field' ).removeClass( 'invalid' ); embedLinkView.setErrorNotice( '' ); embedLinkView.setAddToWidgetButtonDisabled( false ); }; urlParser = document.createElement( 'a' ); urlParser.href = url; matches = urlParser.pathname.toLowerCase().match( /\.(\w+)$/ ); if ( matches ) { fileExt = matches[1]; if ( ! wp.media.view.settings.embedMimes[ fileExt ] ) { embedLinkView.renderFail(); } else if ( 0 !== wp.media.view.settings.embedMimes[ fileExt ].indexOf( embedLinkView.controller.options.mimeType ) ) { embedLinkView.renderFail(); } else { fetchSuccess( '<!--success-->' ); } return; } // Support YouTube embed links. re = /https?:\/\/www\.youtube\.com\/embed\/([^/]+)/; youTubeEmbedMatch = re.exec( url ); if ( youTubeEmbedMatch ) { url = 'https://www.youtube.com/watch?v=' + youTubeEmbedMatch[ 1 ]; // silently change url to proper oembed-able version. embedLinkView.model.attributes.url = url; } embedLinkView.dfd = wp.apiRequest({ url: wp.media.view.settings.oEmbedProxyUrl, data: { url: url, maxwidth: embedLinkView.model.get( 'width' ), maxheight: embedLinkView.model.get( 'height' ), discover: false }, type: 'GET', dataType: 'json', context: embedLinkView }); embedLinkView.dfd.done( function( response ) { if ( embedLinkView.controller.options.mimeType !== response.type ) { embedLinkView.renderFail(); return; } fetchSuccess( response.html ); }); embedLinkView.dfd.fail( _.bind( embedLinkView.renderFail, embedLinkView ) ); }, /** * Handle render failure. * * Overrides the {EmbedLink#renderFail()} method to prevent showing the "Link Text" field. * The element is getting display:none in the stylesheet, but the underlying method uses * uses {jQuery.fn.show()} which adds an inline style. This avoids the need for !important. * * @return {void} */ renderFail: function renderFail() { var embedLinkView = this; // eslint-disable-line consistent-this embedLinkView.controller.$el.find( '#embed-url-field' ).addClass( 'invalid' ); embedLinkView.setErrorNotice( embedLinkView.controller.options.invalidEmbedTypeError || 'ERROR' ); embedLinkView.setAddToWidgetButtonDisabled( true ); } }); } this.settings( new Constructor({ controller: this.controller, model: this.model.props, priority: 40 })); } }); /** * Custom media frame for selecting uploaded media or providing media by URL. * * @class wp.mediaWidgets.MediaFrameSelect * @augments wp.media.view.MediaFrame.Post */ component.MediaFrameSelect = wp.media.view.MediaFrame.Post.extend(/** @lends wp.mediaWidgets.MediaFrameSelect.prototype */{ /** * Create the default states. * * @return {void} */ createStates: function createStates() { var mime = this.options.mimeType, specificMimes = []; _.each( wp.media.view.settings.embedMimes, function( embedMime ) { if ( 0 === embedMime.indexOf( mime ) ) { specificMimes.push( embedMime ); } }); if ( specificMimes.length > 0 ) { mime = specificMimes; } this.states.add([ // Main states. new component.PersistentDisplaySettingsLibrary({ id: 'insert', title: this.options.title, selection: this.options.selection, priority: 20, toolbar: 'main-insert', filterable: 'dates', library: wp.media.query({ type: mime }), multiple: false, editable: true, selectedDisplaySettings: this.options.selectedDisplaySettings, displaySettings: _.isUndefined( this.options.showDisplaySettings ) ? true : this.options.showDisplaySettings, displayUserSettings: false // We use the display settings from the current/default widget instance props. }), new wp.media.controller.EditImage({ model: this.options.editImage }), // Embed states. new wp.media.controller.Embed({ metadata: this.options.metadata, type: 'image' === this.options.mimeType ? 'image' : 'link', invalidEmbedTypeError: this.options.invalidEmbedTypeError }) ]); }, /** * Main insert toolbar. * * Forked override of {wp.media.view.MediaFrame.Post#mainInsertToolbar()} to override text. * * @param {wp.Backbone.View} view - Toolbar view. * @this {wp.media.controller.Library} * @return {void} */ mainInsertToolbar: function mainInsertToolbar( view ) { var controller = this; // eslint-disable-line consistent-this view.set( 'insert', { style: 'primary', priority: 80, text: controller.options.text, // The whole reason for the fork. requires: { selection: true }, /** * Handle click. * * @ignore * * @fires wp.media.controller.State#insert() * @return {void} */ click: function onClick() { var state = controller.state(), selection = state.get( 'selection' ); controller.close(); state.trigger( 'insert', selection ).reset(); } }); }, /** * Main embed toolbar. * * Forked override of {wp.media.view.MediaFrame.Post#mainEmbedToolbar()} to override text. * * @param {wp.Backbone.View} toolbar - Toolbar view. * @this {wp.media.controller.Library} * @return {void} */ mainEmbedToolbar: function mainEmbedToolbar( toolbar ) { toolbar.view = new wp.media.view.Toolbar.Embed({ controller: this, text: this.options.text, event: 'insert' }); }, /** * Embed content. * * Forked override of {wp.media.view.MediaFrame.Post#embedContent()} to suppress irrelevant "link text" field. * * @return {void} */ embedContent: function embedContent() { var view = new component.MediaEmbedView({ controller: this, model: this.state() }).render(); this.content.set( view ); } }); component.MediaWidgetControl = Backbone.View.extend(/** @lends wp.mediaWidgets.MediaWidgetControl.prototype */{ /** * Translation strings. * * The mapping of translation strings is handled by media widget subclasses, * exported from PHP to JS such as is done in WP_Widget_Media_Image::enqueue_admin_scripts(). * * @type {Object} */ l10n: { add_to_widget: '{{add_to_widget}}', add_media: '{{add_media}}' }, /** * Widget ID base. * * This may be defined by the subclass. It may be exported from PHP to JS * such as is done in WP_Widget_Media_Image::enqueue_admin_scripts(). If not, * it will attempt to be discovered by looking to see if this control * instance extends each member of component.controlConstructors, and if * it does extend one, will use the key as the id_base. * * @type {string} */ id_base: '', /** * Mime type. * * This must be defined by the subclass. It may be exported from PHP to JS * such as is done in WP_Widget_Media_Image::enqueue_admin_scripts(). * * @type {string} */ mime_type: '', /** * View events. * * @type {Object} */ events: { 'click .notice-missing-attachment a': 'handleMediaLibraryLinkClick', 'click .select-media': 'selectMedia', 'click .placeholder': 'selectMedia', 'click .edit-media': 'editMedia' }, /** * Show display settings. * * @type {boolean} */ showDisplaySettings: true, /** * Media Widget Control. * * @constructs wp.mediaWidgets.MediaWidgetControl * @augments Backbone.View * @abstract * * @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. * * @return {void} */ initialize: function initialize( options ) { var control = this; Backbone.View.prototype.initialize.call( control, options ); if ( ! ( control.model instanceof component.MediaWidgetModel ) ) { throw new Error( 'Missing options.model' ); } if ( ! options.el ) { throw new Error( 'Missing options.el' ); } if ( ! options.syncContainer ) { throw new Error( 'Missing options.syncContainer' ); } control.syncContainer = options.syncContainer; control.$el.addClass( 'media-widget-control' ); // Allow methods to be passed in with control context preserved. _.bindAll( control, 'syncModelToInputs', 'render', 'updateSelectedAttachment', 'renderPreview' ); if ( ! control.id_base ) { _.find( component.controlConstructors, function( Constructor, idBase ) { if ( control instanceof Constructor ) { control.id_base = idBase; return true; } return false; }); if ( ! control.id_base ) { throw new Error( 'Missing id_base.' ); } } // Track attributes needed to renderPreview in it's own model. control.previewTemplateProps = new Backbone.Model( control.mapModelToPreviewTemplateProps() ); // Re-render the preview when the attachment changes. control.selectedAttachment = new wp.media.model.Attachment(); control.renderPreview = _.debounce( control.renderPreview ); control.listenTo( control.previewTemplateProps, 'change', control.renderPreview ); // Make sure a copy of the selected attachment is always fetched. control.model.on( 'change:attachment_id', control.updateSelectedAttachment ); control.model.on( 'change:url', control.updateSelectedAttachment ); control.updateSelectedAttachment(); /* * Sync the widget instance model attributes onto the hidden inputs that widgets currently use to store the state. * In the future, when widgets are JS-driven, the underlying widget instance data should be exposed as a model * from the start, without having to sync with hidden fields. See <https://core.trac.wordpress.org/ticket/33507>. */ control.listenTo( control.model, 'change', control.syncModelToInputs ); control.listenTo( control.model, 'change', control.syncModelToPreviewProps ); control.listenTo( control.model, 'change', control.render ); // Update the title. control.$el.on( 'input change', '.title', function updateTitle() { control.model.set({ title: $( this ).val().trim() }); }); // Update link_url attribute. control.$el.on( 'input change', '.link', function updateLinkUrl() { var linkUrl = $( this ).val().trim(), linkType = 'custom'; if ( control.selectedAttachment.get( 'linkUrl' ) === linkUrl || control.selectedAttachment.get( 'link' ) === linkUrl ) { linkType = 'post'; } else if ( control.selectedAttachment.get( 'url' ) === linkUrl ) { linkType = 'file'; } control.model.set( { link_url: linkUrl, link_type: linkType }); // Update display settings for the next time the user opens to select from the media library. control.displaySettings.set( { link: linkType, linkUrl: linkUrl }); }); /* * Copy current display settings from the widget model to serve as basis * of customized display settings for the current media frame session. * Changes to display settings will be synced into this model, and * when a new selection is made, the settings from this will be synced * into that AttachmentDisplay's model to persist the setting changes. */ control.displaySettings = new Backbone.Model( _.pick( control.mapModelToMediaFrameProps( _.extend( control.model.defaults(), control.model.toJSON() ) ), _.keys( wp.media.view.settings.defaultProps ) ) ); }, /** * Update the selected attachment if necessary. * * @return {void} */ updateSelectedAttachment: function updateSelectedAttachment() { var control = this, attachment; if ( 0 === control.model.get( 'attachment_id' ) ) { control.selectedAttachment.clear(); control.model.set( 'error', false ); } else if ( control.model.get( 'attachment_id' ) !== control.selectedAttachment.get( 'id' ) ) { attachment = new wp.media.model.Attachment({ id: control.model.get( 'attachment_id' ) }); attachment.fetch() .done( function done() { control.model.set( 'error', false ); control.selectedAttachment.set( attachment.toJSON() ); }) .fail( function fail() { control.model.set( 'error', 'missing_attachment' ); }); } }, /** * Sync the model attributes to the hidden inputs, and update previewTemplateProps. * * @return {void} */ syncModelToPreviewProps: function syncModelToPreviewProps() { var control = this; control.previewTemplateProps.set( control.mapModelToPreviewTemplateProps() ); }, /** * Sync the model attributes to the hidden inputs, and update previewTemplateProps. * * @return {void} */ syncModelToInputs: function syncModelToInputs() { var control = this; control.syncContainer.find( '.media-widget-instance-property' ).each( function() { var input = $( this ), value, propertyName; propertyName = input.data( 'property' ); value = control.model.get( propertyName ); if ( _.isUndefined( 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' ); } }); }, /** * Get template. * * @return {Function} Template. */ template: function template() { var control = this; if ( ! $( '#tmpl-widget-media-' + control.id_base + '-control' ).length ) { throw new Error( 'Missing widget control template for ' + control.id_base ); } return wp.template( 'widget-media-' + control.id_base + '-control' ); }, /** * Render template. * * @return {void} */ render: function render() { var control = this, titleInput; if ( ! control.templateRendered ) { control.$el.html( control.template()( control.model.toJSON() ) ); control.renderPreview(); // Hereafter it will re-render when control.selectedAttachment changes. control.templateRendered = true; } titleInput = control.$el.find( '.title' ); if ( ! titleInput.is( document.activeElement ) ) { titleInput.val( control.model.get( 'title' ) ); } control.$el.toggleClass( 'selected', control.isSelected() ); }, /** * Render media preview. * * @abstract * @return {void} */ renderPreview: function renderPreview() { throw new Error( 'renderPreview must be implemented' ); }, /** * Whether a media item is selected. * * @return {boolean} Whether selected and no error. */ isSelected: function isSelected() { var control = this; if ( control.model.get( 'error' ) ) { return false; } return Boolean( control.model.get( 'attachment_id' ) || control.model.get( 'url' ) ); }, /** * Handle click on link to Media Library to open modal, such as the link that appears when in the missing attachment error notice. * * @param {jQuery.Event} event - Event. * @return {void} */ handleMediaLibraryLinkClick: function handleMediaLibraryLinkClick( event ) { var control = this; event.preventDefault(); control.selectMedia(); }, /** * Open the media select frame to chose an item. * * @return {void} */ selectMedia: function selectMedia() { var control = this, selection, mediaFrame, defaultSync, mediaFrameProps, selectionModels = []; if ( control.isSelected() && 0 !== control.model.get( 'attachment_id' ) ) { selectionModels.push( control.selectedAttachment ); } selection = new wp.media.model.Selection( selectionModels, { multiple: false } ); mediaFrameProps = control.mapModelToMediaFrameProps( control.model.toJSON() ); if ( mediaFrameProps.size ) { control.displaySettings.set( 'size', mediaFrameProps.size ); } mediaFrame = new component.MediaFrameSelect({ title: control.l10n.add_media, frame: 'post', text: control.l10n.add_to_widget, selection: selection, mimeType: control.mime_type, selectedDisplaySettings: control.displaySettings, showDisplaySettings: control.showDisplaySettings, metadata: mediaFrameProps, state: control.isSelected() && 0 === control.model.get( 'attachment_id' ) ? 'embed' : 'insert', invalidEmbedTypeError: control.l10n.unsupported_file_type }); wp.media.frame = mediaFrame; // See wp.media(). // Handle selection of a media item. mediaFrame.on( 'insert', function onInsert() { var attachment = {}, state = mediaFrame.state(); // Update cached attachment object to avoid having to re-fetch. This also triggers re-rendering of preview. if ( 'embed' === state.get( 'id' ) ) { _.extend( attachment, { id: 0 }, state.props.toJSON() ); } else { _.extend( attachment, state.get( 'selection' ).first().toJSON() ); } control.selectedAttachment.set( attachment ); control.model.set( 'error', false ); // Update widget instance. control.model.set( control.getModelPropsFromMediaFrame( mediaFrame ) ); }); // Disable syncing of attachment changes back to server (except for deletions). See <https://core.trac.wordpress.org/ticket/40403>. defaultSync = wp.media.model.Attachment.prototype.sync; wp.media.model.Attachment.prototype.sync = function( method ) { if ( 'delete' === method ) { return defaultSync.apply( this, arguments ); } else { return $.Deferred().rejectWith( this ).promise(); } }; mediaFrame.on( 'close', function onClose() { wp.media.model.Attachment.prototype.sync = defaultSync; }); mediaFrame.$el.addClass( 'media-widget' ); mediaFrame.open(); // Clear the selected attachment when it is deleted in the media select frame. if ( selection ) { selection.on( 'destroy', function onDestroy( attachment ) { if ( control.model.get( 'attachment_id' ) === attachment.get( 'id' ) ) { control.model.set({ attachment_id: 0, url: '' }); } }); } /* * 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( '.media-frame-menu .media-menu-item.active' ).focus(); }, /** * Get the instance props from the media selection frame. * * @param {wp.media.view.MediaFrame.Select} mediaFrame - Select frame. * @return {Object} Props. */ getModelPropsFromMediaFrame: function getModelPropsFromMediaFrame( mediaFrame ) { var control = this, state, mediaFrameProps, modelProps; state = mediaFrame.state(); if ( 'insert' === state.get( 'id' ) ) { mediaFrameProps = state.get( 'selection' ).first().toJSON(); mediaFrameProps.postUrl = mediaFrameProps.link; if ( control.showDisplaySettings ) { _.extend( mediaFrameProps, mediaFrame.content.get( '.attachments-browser' ).sidebar.get( 'display' ).model.toJSON() ); } if ( mediaFrameProps.sizes && mediaFrameProps.size && mediaFrameProps.sizes[ mediaFrameProps.size ] ) { mediaFrameProps.url = mediaFrameProps.sizes[ mediaFrameProps.size ].url; } } else if ( 'embed' === state.get( 'id' ) ) { mediaFrameProps = _.extend( state.props.toJSON(), { attachment_id: 0 }, // Because some media frames use `attachment_id` not `id`. control.model.getEmbedResetProps() ); } else { throw new Error( 'Unexpected state: ' + state.get( 'id' ) ); } if ( mediaFrameProps.id ) { mediaFrameProps.attachment_id = mediaFrameProps.id; } modelProps = control.mapMediaToModelProps( mediaFrameProps ); // Clear the extension prop so sources will be reset for video and audio media. _.each( wp.media.view.settings.embedExts, function( ext ) { if ( ext in control.model.schema && modelProps.url !== modelProps[ ext ] ) { modelProps[ ext ] = ''; } }); return modelProps; }, /** * Map media frame props to model props. * * @param {Object} mediaFrameProps - Media frame props. * @return {Object} Model props. */ mapMediaToModelProps: function mapMediaToModelProps( mediaFrameProps ) { var control = this, mediaFramePropToModelPropMap = {}, modelProps = {}, extension; _.each( control.model.schema, function( fieldSchema, modelProp ) { // Ignore widget title attribute. if ( 'title' === modelProp ) { return; } mediaFramePropToModelPropMap[ fieldSchema.media_prop || modelProp ] = modelProp; }); _.each( mediaFrameProps, function( value, mediaProp ) { var propName = mediaFramePropToModelPropMap[ mediaProp ] || mediaProp; if ( control.model.schema[ propName ] ) { modelProps[ propName ] = value; } }); if ( 'custom' === mediaFrameProps.size ) { modelProps.width = mediaFrameProps.customWidth; modelProps.height = mediaFrameProps.customHeight; } if ( 'post' === mediaFrameProps.link ) { modelProps.link_url = mediaFrameProps.postUrl || mediaFrameProps.linkUrl; } else if ( 'file' === mediaFrameProps.link ) { modelProps.link_url = mediaFrameProps.url; } // Because some media frames use `id` instead of `attachment_id`. if ( ! mediaFrameProps.attachment_id && mediaFrameProps.id ) { modelProps.attachment_id = mediaFrameProps.id; } if ( mediaFrameProps.url ) { extension = mediaFrameProps.url.replace( /#.*$/, '' ).replace( /\?.*$/, '' ).split( '.' ).pop().toLowerCase(); if ( extension in control.model.schema ) { modelProps[ extension ] = mediaFrameProps.url; } } // Always omit the titles derived from mediaFrameProps. return _.omit( modelProps, 'title' ); }, /** * Map model props to media frame props. * * @param {Object} modelProps - Model props. * @return {Object} Media frame props. */ mapModelToMediaFrameProps: function mapModelToMediaFrameProps( modelProps ) { var control = this, mediaFrameProps = {}; _.each( modelProps, function( value, modelProp ) { var fieldSchema = control.model.schema[ modelProp ] || {}; mediaFrameProps[ fieldSchema.media_prop || modelProp ] = value; }); // Some media frames use attachment_id. mediaFrameProps.attachment_id = mediaFrameProps.id; if ( 'custom' === mediaFrameProps.size ) { mediaFrameProps.customWidth = control.model.get( 'width' ); mediaFrameProps.customHeight = control.model.get( 'height' ); } return mediaFrameProps; }, /** * Map model props to previewTemplateProps. * * @return {Object} Preview Template Props. */ mapModelToPreviewTemplateProps: function mapModelToPreviewTemplateProps() { var control = this, previewTemplateProps = {}; _.each( control.model.schema, function( value, prop ) { if ( ! value.hasOwnProperty( 'should_preview_update' ) || value.should_preview_update ) { previewTemplateProps[ prop ] = control.model.get( prop ); } }); // Templates need to be aware of the error. previewTemplateProps.error = control.model.get( 'error' ); return previewTemplateProps; }, /** * Open the media frame to modify the selected item. * * @abstract * @return {void} */ editMedia: function editMedia() { throw new Error( 'editMedia not implemented' ); } }); /** * Media widget model. * * @class wp.mediaWidgets.MediaWidgetModel * @augments Backbone.Model */ component.MediaWidgetModel = Backbone.Model.extend(/** @lends wp.mediaWidgets.MediaWidgetModel.prototype */{ /** * Id attribute. * * @type {string} */ idAttribute: 'widget_id', /** * Instance schema. * * This adheres to JSON Schema and subclasses should have their schema * exported from PHP to JS such as is done in WP_Widget_Media_Image::enqueue_admin_scripts(). * * @type {Object.<string, Object>} */ schema: { title: { type: 'string', 'default': '' }, attachment_id: { type: 'integer', 'default': 0 }, url: { type: 'string', 'default': '' } }, /** * Get default attribute values. * * @return {Object} Mapping of property names to their default values. */ defaults: function() { var defaults = {}; _.each( this.schema, function( fieldSchema, field ) { defaults[ field ] = fieldSchema['default']; }); return defaults; }, /** * Set attribute value(s). * * This is a wrapped version of Backbone.Model#set() which allows us to * cast the attribute values from the hidden inputs' string values into * the appropriate data types (integers or booleans). * * @param {string|Object} key - Attribute name or attribute pairs. * @param {mixed|Object} [val] - Attribute value or options object. * @param {Object} [options] - Options when attribute name and value are passed separately. * @return {wp.mediaWidgets.MediaWidgetModel} This model. */ set: function set( key, val, options ) { var model = this, attrs, opts, castedAttrs; // eslint-disable-line consistent-this if ( null === key ) { return model; } if ( 'object' === typeof key ) { attrs = key; opts = val; } else { attrs = {}; attrs[ key ] = val; opts = options; } castedAttrs = {}; _.each( attrs, function( value, name ) { var type; if ( ! model.schema[ name ] ) { castedAttrs[ name ] = value; return; } type = model.schema[ name ].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 ); } else { castedAttrs[ name ] = value; } }); return Backbone.Model.prototype.set.call( this, castedAttrs, opts ); }, /** * Get props which are merged on top of the model when an embed is chosen (as opposed to an attachment). * * @return {Object} Reset/override props. */ getEmbedResetProps: function getEmbedResetProps() { return { id: 0 }; } }); /** * Collection of all widget model instances. * * @memberOf wp.mediaWidgets * * @type {Backbone.Collection} */ component.modelCollection = new ( Backbone.Collection.extend( { model: component.MediaWidgetModel }) )(); /** * Mapping of widget ID to instances of MediaWidgetControl subclasses. * * @memberOf wp.mediaWidgets * * @type {Object.<string, wp.mediaWidgets.MediaWidgetControl>} */ component.widgetControls = {}; /** * Handle widget being added or initialized for the first time at the widget-added event. * * @memberOf wp.mediaWidgets * * @param {jQuery.Event} event - Event. * @param {jQuery} widgetContainer - Widget container element. * * @return {void} */ component.handleWidgetAdded = function handleWidgetAdded( event, widgetContainer ) { var fieldContainer, syncContainer, widgetForm, idBase, ControlConstructor, ModelConstructor, modelAttributes, widgetControl, widgetModel, widgetId, animatedCheckDelay = 50, renderWhenAnimationDone; widgetForm = widgetContainer.find( '> .widget-inside > .form, > .widget-inside > form' ); // Note: '.form' appears in the customizer, whereas 'form' on the widgets admin screen. idBase = widgetForm.find( '> .id_base' ).val(); widgetId = widgetForm.find( '> .widget-id' ).val(); // Prevent initializing already-added widgets. if ( component.widgetControls[ widgetId ] ) { return; } ControlConstructor = component.controlConstructors[ idBase ]; if ( ! ControlConstructor ) { return; } ModelConstructor = component.modelConstructors[ idBase ] || component.MediaWidgetModel; /* * Create a container element for the widget control (Backbone.View). * This is inserted into the DOM immediately before the .widget-content * element because the contents of this element are essentially "managed" * by PHP, where each widget update cause the entire element to be emptied * and replaced with the rendered output of WP_Widget::form() which is * sent back in Ajax request made to save/update the widget instance. * To prevent a "flash of replaced DOM elements and re-initialized JS * components", the JS template is rendered outside of the normal form * container. */ fieldContainer = $( '<div></div>' ); syncContainer = widgetContainer.find( '.widget-content:first' ); syncContainer.before( fieldContainer ); /* * Sync the widget instance model attributes onto the hidden inputs that widgets currently use to store the state. * In the future, when widgets are JS-driven, the underlying widget instance data should be exposed as a model * from the start, without having to sync with hidden fields. See <https://core.trac.wordpress.org/ticket/33507>. */ modelAttributes = {}; syncContainer.find( '.media-widget-instance-property' ).each( function() { var input = $( this ); modelAttributes[ input.data( 'property' ) ] = input.val(); }); modelAttributes.widget_id = widgetId; widgetModel = new ModelConstructor( modelAttributes ); widgetControl = new ControlConstructor({ el: fieldContainer, syncContainer: syncContainer, model: widgetModel }); /* * Render the widget once the widget parent's container finishes animating, * as the widget-added event fires with a slideDown of the container. * This ensures that the container's dimensions are fixed so that ME.js * can initialize with the proper dimensions. */ renderWhenAnimationDone = function() { if ( ! widgetContainer.hasClass( 'open' ) ) { setTimeout( renderWhenAnimationDone, animatedCheckDelay ); } else { widgetControl.render(); } }; renderWhenAnimationDone(); /* * Note that the model and control currently won't ever get garbage-collected * when a widget gets removed/deleted because there is no widget-removed event. */ component.modelCollection.add( [ widgetModel ] ); component.widgetControls[ widgetModel.get( 'widget_id' ) ] = widgetControl; }; /** * Setup widget in accessibility mode. * * @memberOf wp.mediaWidgets * * @return {void} */ component.setupAccessibleMode = function setupAccessibleMode() { var widgetForm, widgetId, idBase, widgetControl, ControlConstructor, ModelConstructor, modelAttributes, fieldContainer, syncContainer; widgetForm = $( '.editwidget > form' ); if ( 0 === widgetForm.length ) { return; } idBase = widgetForm.find( '.id_base' ).val(); ControlConstructor = component.controlConstructors[ idBase ]; if ( ! ControlConstructor ) { return; } widgetId = widgetForm.find( '> .widget-control-actions > .widget-id' ).val(); ModelConstructor = component.modelConstructors[ idBase ] || component.MediaWidgetModel; fieldContainer = $( '<div></div>' ); syncContainer = widgetForm.find( '> .widget-inside' ); syncContainer.before( fieldContainer ); modelAttributes = {}; syncContainer.find( '.media-widget-instance-property' ).each( function() { var input = $( this ); modelAttributes[ input.data( 'property' ) ] = input.val(); }); modelAttributes.widget_id = widgetId; widgetControl = new ControlConstructor({ el: fieldContainer, syncContainer: syncContainer, model: new ModelConstructor( modelAttributes ) }); component.modelCollection.add( [ widgetControl.model ] ); component.widgetControls[ widgetControl.model.get( 'widget_id' ) ] = widgetControl; widgetControl.render(); }; /** * Sync widget instance data sanitized from server back onto widget model. * * This gets called via the 'widget-updated' event when saving a widget from * the widgets admin screen and also via the 'widget-synced' event when making * a change to a widget in the customizer. * * @memberOf wp.mediaWidgets * * @param {jQuery.Event} event - Event. * @param {jQuery} widgetContainer - Widget container element. * * @return {void} */ component.handleWidgetUpdated = function handleWidgetUpdated( event, widgetContainer ) { var widgetForm, widgetContent, widgetId, widgetControl, attributes = {}; widgetForm = widgetContainer.find( '> .widget-inside > .form, > .widget-inside > form' ); widgetId = widgetForm.find( '> .widget-id' ).val(); widgetControl = component.widgetControls[ widgetId ]; if ( ! widgetControl ) { return; } // Make sure the server-sanitized values get synced back into the model. widgetContent = widgetForm.find( '> .widget-content' ); widgetContent.find( '.media-widget-instance-property' ).each( function() { var property = $( this ).data( 'property' ); attributes[ property ] = $( this ).val(); }); // Suspend syncing model back to inputs when syncing from inputs to model, preventing infinite loop. widgetControl.stopListening( widgetControl.model, 'change', widgetControl.syncModelToInputs ); widgetControl.model.set( attributes ); widgetControl.listenTo( widgetControl.model, 'change', widgetControl.syncModelToInputs ); }; /** * Initialize functionality. * * This function exists to prevent the JS file from having to boot itself. * When WordPress enqueues this script, it should have an inline script * attached which calls wp.mediaWidgets.init(). * * @memberOf wp.mediaWidgets * * @return {void} */ component.init = function init() { var $document = $( document ); $document.on( 'widget-added', component.handleWidgetAdded ); $document.on( 'widget-synced widget-updated', component.handleWidgetUpdated ); /* * Manually trigger widget-added events for media widgets on the admin * screen once they are expanded. The widget-added event is not triggered * for each pre-existing widget on the widgets admin screen like it is * on the customizer. Likewise, the customizer only triggers widget-added * when the widget is expanded to just-in-time construct the widget form * when it is actually going to be displayed. So the following implements * the same for the widgets admin screen, to invoke the widget-added * handler when a pre-existing media widget is expanded. */ $( function initializeExistingWidgetContainers() { var widgetContainers; if ( 'widgets' !== window.pagenow ) { return; } widgetContainers = $( '.widgets-holder-wrap:not(#available-widgets)' ).find( 'div.widget' ); widgetContainers.one( 'click.toggle-widget-expanded', function toggleWidgetExpanded() { var widgetContainer = $( this ); component.handleWidgetAdded( new jQuery.Event( 'widget-added' ), widgetContainer ); }); // Accessibility mode. if ( document.readyState === 'complete' ) { // Page is fully loaded. component.setupAccessibleMode(); } else { // Page is still loading. $( window ).on( 'load', function() { component.setupAccessibleMode(); }); } }); }; return component; })( jQuery ); media-widgets.min.js 0000644 00000033641 14717746353 0010437 0 ustar 00 /*! This file is auto-generated */ wp.mediaWidgets=function(c){"use strict";var m={controlConstructors:{},modelConstructors:{}};return m.PersistentDisplaySettingsLibrary=wp.media.controller.Library.extend({initialize:function(e){_.bindAll(this,"handleDisplaySettingChange"),wp.media.controller.Library.prototype.initialize.call(this,e)},handleDisplaySettingChange:function(e){this.get("selectedDisplaySettings").set(e.attributes)},display:function(e){var t=this.get("selectedDisplaySettings"),e=wp.media.controller.Library.prototype.display.call(this,e);return e.off("change",this.handleDisplaySettingChange),e.set(t.attributes),"custom"===t.get("link_type")&&(e.linkUrl=t.get("link_url")),e.on("change",this.handleDisplaySettingChange),e}}),m.MediaEmbedView=wp.media.view.Embed.extend({initialize:function(e){var t=this;wp.media.view.Embed.prototype.initialize.call(t,e),"image"!==t.controller.options.mimeType&&(e=t.controller.states.get("embed")).off("scan",e.scanImage,e)},refresh:function(){var e="image"===this.controller.options.mimeType?wp.media.view.EmbedImage:wp.media.view.EmbedLink.extend({setAddToWidgetButtonDisabled:function(e){this.views.parent.views.parent.views.get(".media-frame-toolbar")[0].$el.find(".media-button-select").prop("disabled",e)},setErrorNotice:function(e){var t=this.views.parent.$el.find("> .notice:first-child");e?(t.length||((t=c('<div class="media-widget-embed-notice notice notice-error notice-alt" role="alert"></div>')).hide(),this.views.parent.$el.prepend(t)),t.empty(),t.append(c("<p>",{html:e})),t.slideDown("fast")):t.length&&t.slideUp("fast")},updateoEmbed:function(){var e=this,t=e.model.get("url");t?(t.match(/^(http|https):\/\/.+\//)||(e.controller.$el.find("#embed-url-field").addClass("invalid"),e.setAddToWidgetButtonDisabled(!0)),wp.media.view.EmbedLink.prototype.updateoEmbed.call(e)):(e.setErrorNotice(""),e.setAddToWidgetButtonDisabled(!0))},fetch:function(){var t,e,i=this,n=i.model.get("url");i.dfd&&"pending"===i.dfd.state()&&i.dfd.abort(),t=function(e){i.renderoEmbed({data:{body:e}}),i.controller.$el.find("#embed-url-field").removeClass("invalid"),i.setErrorNotice(""),i.setAddToWidgetButtonDisabled(!1)},(e=document.createElement("a")).href=n,(e=e.pathname.toLowerCase().match(/\.(\w+)$/))?(e=e[1],!wp.media.view.settings.embedMimes[e]||0!==wp.media.view.settings.embedMimes[e].indexOf(i.controller.options.mimeType)?i.renderFail():t("\x3c!--success--\x3e")):((e=/https?:\/\/www\.youtube\.com\/embed\/([^/]+)/.exec(n))&&(n="https://www.youtube.com/watch?v="+e[1],i.model.attributes.url=n),i.dfd=wp.apiRequest({url:wp.media.view.settings.oEmbedProxyUrl,data:{url:n,maxwidth:i.model.get("width"),maxheight:i.model.get("height"),discover:!1},type:"GET",dataType:"json",context:i}),i.dfd.done(function(e){i.controller.options.mimeType!==e.type?i.renderFail():t(e.html)}),i.dfd.fail(_.bind(i.renderFail,i)))},renderFail:function(){var e=this;e.controller.$el.find("#embed-url-field").addClass("invalid"),e.setErrorNotice(e.controller.options.invalidEmbedTypeError||"ERROR"),e.setAddToWidgetButtonDisabled(!0)}});this.settings(new e({controller:this.controller,model:this.model.props,priority:40}))}}),m.MediaFrameSelect=wp.media.view.MediaFrame.Post.extend({createStates:function(){var t=this.options.mimeType,i=[];_.each(wp.media.view.settings.embedMimes,function(e){0===e.indexOf(t)&&i.push(e)}),0<i.length&&(t=i),this.states.add([new m.PersistentDisplaySettingsLibrary({id:"insert",title:this.options.title,selection:this.options.selection,priority:20,toolbar:"main-insert",filterable:"dates",library:wp.media.query({type:t}),multiple:!1,editable:!0,selectedDisplaySettings:this.options.selectedDisplaySettings,displaySettings:!!_.isUndefined(this.options.showDisplaySettings)||this.options.showDisplaySettings,displayUserSettings:!1}),new wp.media.controller.EditImage({model:this.options.editImage}),new wp.media.controller.Embed({metadata:this.options.metadata,type:"image"===this.options.mimeType?"image":"link",invalidEmbedTypeError:this.options.invalidEmbedTypeError})])},mainInsertToolbar:function(e){var i=this;e.set("insert",{style:"primary",priority:80,text:i.options.text,requires:{selection:!0},click:function(){var e=i.state(),t=e.get("selection");i.close(),e.trigger("insert",t).reset()}})},mainEmbedToolbar:function(e){e.view=new wp.media.view.Toolbar.Embed({controller:this,text:this.options.text,event:"insert"})},embedContent:function(){var e=new m.MediaEmbedView({controller:this,model:this.state()}).render();this.content.set(e)}}),m.MediaWidgetControl=Backbone.View.extend({l10n:{add_to_widget:"{{add_to_widget}}",add_media:"{{add_media}}"},id_base:"",mime_type:"",events:{"click .notice-missing-attachment a":"handleMediaLibraryLinkClick","click .select-media":"selectMedia","click .placeholder":"selectMedia","click .edit-media":"editMedia"},showDisplaySettings:!0,initialize:function(e){var i=this;if(Backbone.View.prototype.initialize.call(i,e),!(i.model instanceof m.MediaWidgetModel))throw new Error("Missing options.model");if(!e.el)throw new Error("Missing options.el");if(!e.syncContainer)throw new Error("Missing options.syncContainer");if(i.syncContainer=e.syncContainer,i.$el.addClass("media-widget-control"),_.bindAll(i,"syncModelToInputs","render","updateSelectedAttachment","renderPreview"),!i.id_base&&(_.find(m.controlConstructors,function(e,t){return i instanceof e&&(i.id_base=t,!0)}),!i.id_base))throw new Error("Missing id_base.");i.previewTemplateProps=new Backbone.Model(i.mapModelToPreviewTemplateProps()),i.selectedAttachment=new wp.media.model.Attachment,i.renderPreview=_.debounce(i.renderPreview),i.listenTo(i.previewTemplateProps,"change",i.renderPreview),i.model.on("change:attachment_id",i.updateSelectedAttachment),i.model.on("change:url",i.updateSelectedAttachment),i.updateSelectedAttachment(),i.listenTo(i.model,"change",i.syncModelToInputs),i.listenTo(i.model,"change",i.syncModelToPreviewProps),i.listenTo(i.model,"change",i.render),i.$el.on("input change",".title",function(){i.model.set({title:c(this).val().trim()})}),i.$el.on("input change",".link",function(){var e=c(this).val().trim(),t="custom";i.selectedAttachment.get("linkUrl")===e||i.selectedAttachment.get("link")===e?t="post":i.selectedAttachment.get("url")===e&&(t="file"),i.model.set({link_url:e,link_type:t}),i.displaySettings.set({link:t,linkUrl:e})}),i.displaySettings=new Backbone.Model(_.pick(i.mapModelToMediaFrameProps(_.extend(i.model.defaults(),i.model.toJSON())),_.keys(wp.media.view.settings.defaultProps)))},updateSelectedAttachment:function(){var e,t=this;0===t.model.get("attachment_id")?(t.selectedAttachment.clear(),t.model.set("error",!1)):t.model.get("attachment_id")!==t.selectedAttachment.get("id")&&(e=new wp.media.model.Attachment({id:t.model.get("attachment_id")})).fetch().done(function(){t.model.set("error",!1),t.selectedAttachment.set(e.toJSON())}).fail(function(){t.model.set("error","missing_attachment")})},syncModelToPreviewProps:function(){this.previewTemplateProps.set(this.mapModelToPreviewTemplateProps())},syncModelToInputs:function(){var n=this;n.syncContainer.find(".media-widget-instance-property").each(function(){var e=c(this),t=e.data("property"),i=n.model.get(t);_.isUndefined(i)||(i="array"===n.model.schema[t].type&&_.isArray(i)?i.join(","):"boolean"===n.model.schema[t].type?i?"1":"":String(i),e.val()!==i&&(e.val(i),e.trigger("change")))})},template:function(){if(c("#tmpl-widget-media-"+this.id_base+"-control").length)return wp.template("widget-media-"+this.id_base+"-control");throw new Error("Missing widget control template for "+this.id_base)},render:function(){var e,t=this;t.templateRendered||(t.$el.html(t.template()(t.model.toJSON())),t.renderPreview(),t.templateRendered=!0),(e=t.$el.find(".title")).is(document.activeElement)||e.val(t.model.get("title")),t.$el.toggleClass("selected",t.isSelected())},renderPreview:function(){throw new Error("renderPreview must be implemented")},isSelected:function(){return!this.model.get("error")&&Boolean(this.model.get("attachment_id")||this.model.get("url"))},handleMediaLibraryLinkClick:function(e){e.preventDefault(),this.selectMedia()},selectMedia:function(){var i,t,e,n=this,d=[];n.isSelected()&&0!==n.model.get("attachment_id")&&d.push(n.selectedAttachment),d=new wp.media.model.Selection(d,{multiple:!1}),(e=n.mapModelToMediaFrameProps(n.model.toJSON())).size&&n.displaySettings.set("size",e.size),i=new m.MediaFrameSelect({title:n.l10n.add_media,frame:"post",text:n.l10n.add_to_widget,selection:d,mimeType:n.mime_type,selectedDisplaySettings:n.displaySettings,showDisplaySettings:n.showDisplaySettings,metadata:e,state:n.isSelected()&&0===n.model.get("attachment_id")?"embed":"insert",invalidEmbedTypeError:n.l10n.unsupported_file_type}),(wp.media.frame=i).on("insert",function(){var e={},t=i.state();"embed"===t.get("id")?_.extend(e,{id:0},t.props.toJSON()):_.extend(e,t.get("selection").first().toJSON()),n.selectedAttachment.set(e),n.model.set("error",!1),n.model.set(n.getModelPropsFromMediaFrame(i))}),t=wp.media.model.Attachment.prototype.sync,wp.media.model.Attachment.prototype.sync=function(e){return"delete"===e?t.apply(this,arguments):c.Deferred().rejectWith(this).promise()},i.on("close",function(){wp.media.model.Attachment.prototype.sync=t}),i.$el.addClass("media-widget"),i.open(),d&&d.on("destroy",function(e){n.model.get("attachment_id")===e.get("id")&&n.model.set({attachment_id:0,url:""})}),i.$el.find(".media-frame-menu .media-menu-item.active").focus()},getModelPropsFromMediaFrame:function(e){var t,i,n=this,d=e.state();if("insert"===d.get("id"))(t=d.get("selection").first().toJSON()).postUrl=t.link,n.showDisplaySettings&&_.extend(t,e.content.get(".attachments-browser").sidebar.get("display").model.toJSON()),t.sizes&&t.size&&t.sizes[t.size]&&(t.url=t.sizes[t.size].url);else{if("embed"!==d.get("id"))throw new Error("Unexpected state: "+d.get("id"));t=_.extend(d.props.toJSON(),{attachment_id:0},n.model.getEmbedResetProps())}return t.id&&(t.attachment_id=t.id),i=n.mapMediaToModelProps(t),_.each(wp.media.view.settings.embedExts,function(e){e in n.model.schema&&i.url!==i[e]&&(i[e]="")}),i},mapMediaToModelProps:function(e){var t,i=this,n={},d={};return _.each(i.model.schema,function(e,t){"title"!==t&&(n[e.media_prop||t]=t)}),_.each(e,function(e,t){t=n[t]||t;i.model.schema[t]&&(d[t]=e)}),"custom"===e.size&&(d.width=e.customWidth,d.height=e.customHeight),"post"===e.link?d.link_url=e.postUrl||e.linkUrl:"file"===e.link&&(d.link_url=e.url),!e.attachment_id&&e.id&&(d.attachment_id=e.id),e.url&&(t=e.url.replace(/#.*$/,"").replace(/\?.*$/,"").split(".").pop().toLowerCase())in i.model.schema&&(d[t]=e.url),_.omit(d,"title")},mapModelToMediaFrameProps:function(e){var n=this,d={};return _.each(e,function(e,t){var i=n.model.schema[t]||{};d[i.media_prop||t]=e}),d.attachment_id=d.id,"custom"===d.size&&(d.customWidth=n.model.get("width"),d.customHeight=n.model.get("height")),d},mapModelToPreviewTemplateProps:function(){var i=this,n={};return _.each(i.model.schema,function(e,t){e.hasOwnProperty("should_preview_update")&&!e.should_preview_update||(n[t]=i.model.get(t))}),n.error=i.model.get("error"),n},editMedia:function(){throw new Error("editMedia not implemented")}}),m.MediaWidgetModel=Backbone.Model.extend({idAttribute:"widget_id",schema:{title:{type:"string",default:""},attachment_id:{type:"integer",default:0},url:{type:"string",default:""}},defaults:function(){var i={};return _.each(this.schema,function(e,t){i[t]=e.default}),i},set:function(e,t,i){var n,d,o=this;return null===e?o:(e="object"==typeof e?(n=e,t):((n={})[e]=t,i),d={},_.each(n,function(e,t){var i;o.schema[t]?"array"===(i=o.schema[t].type)?(d[t]=e,_.isArray(d[t])||(d[t]=d[t].split(/,/)),o.schema[t].items&&"integer"===o.schema[t].items.type&&(d[t]=_.filter(_.map(d[t],function(e){return parseInt(e,10)},function(e){return"number"==typeof e})))):d[t]="integer"===i?parseInt(e,10):"boolean"===i?!(!e||"0"===e||"false"===e):e:d[t]=e}),Backbone.Model.prototype.set.call(this,d,e))},getEmbedResetProps:function(){return{id:0}}}),m.modelCollection=new(Backbone.Collection.extend({model:m.MediaWidgetModel})),m.widgetControls={},m.handleWidgetAdded=function(e,t){var i,n,d,o,a,s,r=t.find("> .widget-inside > .form, > .widget-inside > form"),l=r.find("> .id_base").val(),r=r.find("> .widget-id").val();m.widgetControls[r]||(d=m.controlConstructors[l])&&(l=m.modelConstructors[l]||m.MediaWidgetModel,i=c("<div></div>"),(n=t.find(".widget-content:first")).before(i),o={},n.find(".media-widget-instance-property").each(function(){var e=c(this);o[e.data("property")]=e.val()}),o.widget_id=r,r=new l(o),a=new d({el:i,syncContainer:n,model:r}),(s=function(){t.hasClass("open")?a.render():setTimeout(s,50)})(),m.modelCollection.add([r]),m.widgetControls[r.get("widget_id")]=a)},m.setupAccessibleMode=function(){var e,t,i,n,d,o=c(".editwidget > form");0!==o.length&&(i=o.find(".id_base").val(),t=m.controlConstructors[i])&&(e=o.find("> .widget-control-actions > .widget-id").val(),i=m.modelConstructors[i]||m.MediaWidgetModel,d=c("<div></div>"),(o=o.find("> .widget-inside")).before(d),n={},o.find(".media-widget-instance-property").each(function(){var e=c(this);n[e.data("property")]=e.val()}),n.widget_id=e,e=new t({el:d,syncContainer:o,model:new i(n)}),m.modelCollection.add([e.model]),(m.widgetControls[e.model.get("widget_id")]=e).render())},m.handleWidgetUpdated=function(e,t){var i={},t=t.find("> .widget-inside > .form, > .widget-inside > form"),n=t.find("> .widget-id").val(),n=m.widgetControls[n];n&&(t.find("> .widget-content").find(".media-widget-instance-property").each(function(){var e=c(this).data("property");i[e]=c(this).val()}),n.stopListening(n.model,"change",n.syncModelToInputs),n.model.set(i),n.listenTo(n.model,"change",n.syncModelToInputs))},m.init=function(){var e=c(document);e.on("widget-added",m.handleWidgetAdded),e.on("widget-synced widget-updated",m.handleWidgetUpdated),c(function(){"widgets"===window.pagenow&&(c(".widgets-holder-wrap:not(#available-widgets)").find("div.widget").one("click.toggle-widget-expanded",function(){var e=c(this);m.handleWidgetAdded(new jQuery.Event("widget-added"),e)}),"complete"===document.readyState?m.setupAccessibleMode():c(window).on("load",function(){m.setupAccessibleMode()}))})},m}(jQuery); text-widgets.js 0000644 00000043201 14717746353 0007553 0 ustar 00 /** * @output wp-admin/js/widgets/text-widgets.js */ /* global tinymce, switchEditors */ /* eslint consistent-this: [ "error", "control" ] */ /** * @namespace wp.textWidgets */ wp.textWidgets = ( function( $ ) { 'use strict'; var component = { dismissedPointers: [], idBases: [ 'text' ] }; component.TextWidgetControl = Backbone.View.extend(/** @lends wp.textWidgets.TextWidgetControl.prototype */{ /** * View events. * * @type {Object} */ events: {}, /** * Text widget control. * * @constructs wp.textWidgets.TextWidgetControl * @augments Backbone.View * @abstract * * @param {Object} options - Options. * @param {jQuery} options.el - Control field container element. * @param {jQuery} options.syncContainer - Container element where fields are synced for the server. * * @return {void} */ initialize: function initialize( options ) { var control = this; if ( ! options.el ) { throw new Error( 'Missing options.el' ); } if ( ! options.syncContainer ) { throw new Error( 'Missing options.syncContainer' ); } Backbone.View.prototype.initialize.call( control, options ); control.syncContainer = options.syncContainer; control.$el.addClass( 'text-widget-fields' ); control.$el.html( wp.template( 'widget-text-control-fields' ) ); control.customHtmlWidgetPointer = control.$el.find( '.wp-pointer.custom-html-widget-pointer' ); if ( control.customHtmlWidgetPointer.length ) { control.customHtmlWidgetPointer.find( '.close' ).on( 'click', function( event ) { event.preventDefault(); control.customHtmlWidgetPointer.hide(); $( '#' + control.fields.text.attr( 'id' ) + '-html' ).trigger( 'focus' ); control.dismissPointers( [ 'text_widget_custom_html' ] ); }); control.customHtmlWidgetPointer.find( '.add-widget' ).on( 'click', function( event ) { event.preventDefault(); control.customHtmlWidgetPointer.hide(); control.openAvailableWidgetsPanel(); }); } control.pasteHtmlPointer = control.$el.find( '.wp-pointer.paste-html-pointer' ); if ( control.pasteHtmlPointer.length ) { control.pasteHtmlPointer.find( '.close' ).on( 'click', function( event ) { event.preventDefault(); control.pasteHtmlPointer.hide(); control.editor.focus(); control.dismissPointers( [ 'text_widget_custom_html', 'text_widget_paste_html' ] ); }); } control.fields = { title: control.$el.find( '.title' ), text: control.$el.find( '.text' ) }; // Sync input fields to hidden sync fields which actually get sent to the server. _.each( control.fields, function( fieldInput, fieldName ) { fieldInput.on( 'input change', function updateSyncField() { var syncInput = control.syncContainer.find( '.sync-input.' + fieldName ); if ( syncInput.val() !== fieldInput.val() ) { syncInput.val( fieldInput.val() ); syncInput.trigger( 'change' ); } }); // Note that syncInput cannot be re-used because it will be destroyed with each widget-updated event. fieldInput.val( control.syncContainer.find( '.sync-input.' + fieldName ).val() ); }); }, /** * Dismiss pointers for Custom HTML widget. * * @since 4.8.1 * * @param {Array} pointers Pointer IDs to dismiss. * @return {void} */ dismissPointers: function dismissPointers( pointers ) { _.each( pointers, function( pointer ) { wp.ajax.post( 'dismiss-wp-pointer', { pointer: pointer }); component.dismissedPointers.push( pointer ); }); }, /** * Open available widgets panel. * * @since 4.8.1 * @return {void} */ openAvailableWidgetsPanel: function openAvailableWidgetsPanel() { var sidebarControl; wp.customize.section.each( function( section ) { if ( section.extended( wp.customize.Widgets.SidebarSection ) && section.expanded() ) { sidebarControl = wp.customize.control( 'sidebars_widgets[' + section.params.sidebarId + ']' ); } }); if ( ! sidebarControl ) { return; } setTimeout( function() { // Timeout to prevent click event from causing panel to immediately collapse. wp.customize.Widgets.availableWidgetsPanel.open( sidebarControl ); wp.customize.Widgets.availableWidgetsPanel.$search.val( 'HTML' ).trigger( 'keyup' ); }); }, /** * Update input fields from the sync fields. * * This function is called at the widget-updated and widget-synced events. * A field will only be updated if it is not currently focused, to avoid * overwriting content that the user is entering. * * @return {void} */ updateFields: function updateFields() { var control = this, syncInput; if ( ! control.fields.title.is( document.activeElement ) ) { syncInput = control.syncContainer.find( '.sync-input.title' ); control.fields.title.val( syncInput.val() ); } syncInput = control.syncContainer.find( '.sync-input.text' ); if ( control.fields.text.is( ':visible' ) ) { if ( ! control.fields.text.is( document.activeElement ) ) { control.fields.text.val( syncInput.val() ); } } else if ( control.editor && ! control.editorFocused && syncInput.val() !== control.fields.text.val() ) { control.editor.setContent( wp.oldEditor.autop( syncInput.val() ) ); } }, /** * Initialize editor. * * @return {void} */ initializeEditor: function initializeEditor() { var control = this, changeDebounceDelay = 1000, id, textarea, triggerChangeIfDirty, restoreTextMode = false, needsTextareaChangeTrigger = false, previousValue; textarea = control.fields.text; id = textarea.attr( 'id' ); previousValue = textarea.val(); /** * Trigger change if dirty. * * @return {void} */ triggerChangeIfDirty = function() { var updateWidgetBuffer = 300; // See wp.customize.Widgets.WidgetControl._setupUpdateUI() which uses 250ms for updateWidgetDebounced. if ( control.editor.isDirty() ) { /* * Account for race condition in customizer where user clicks Save & Publish while * focus was just previously given to the editor. Since updates to the editor * are debounced at 1 second and since widget input changes are only synced to * settings after 250ms, the customizer needs to be put into the processing * state during the time between the change event is triggered and updateWidget * logic starts. Note that the debounced update-widget request should be able * to be removed with the removal of the update-widget request entirely once * widgets are able to mutate their own instance props directly in JS without * having to make server round-trips to call the respective WP_Widget::update() * callbacks. See <https://core.trac.wordpress.org/ticket/33507>. */ if ( wp.customize && wp.customize.state ) { wp.customize.state( 'processing' ).set( wp.customize.state( 'processing' ).get() + 1 ); _.delay( function() { wp.customize.state( 'processing' ).set( wp.customize.state( 'processing' ).get() - 1 ); }, updateWidgetBuffer ); } if ( ! control.editor.isHidden() ) { control.editor.save(); } } // Trigger change on textarea when it has changed so the widget can enter a dirty state. if ( needsTextareaChangeTrigger && previousValue !== textarea.val() ) { textarea.trigger( 'change' ); needsTextareaChangeTrigger = false; previousValue = textarea.val(); } }; // Just-in-time force-update the hidden input fields. control.syncContainer.closest( '.widget' ).find( '[name=savewidget]:first' ).on( 'click', function onClickSaveButton() { triggerChangeIfDirty(); }); /** * Build (or re-build) the visual editor. * * @return {void} */ function buildEditor() { var editor, onInit, showPointerElement; // Abort building if the textarea is gone, likely due to the widget having been deleted entirely. if ( ! document.getElementById( id ) ) { return; } // The user has disabled TinyMCE. if ( typeof window.tinymce === 'undefined' ) { wp.oldEditor.initialize( id, { quicktags: true, mediaButtons: true }); return; } // Destroy any existing editor so that it can be re-initialized after a widget-updated event. if ( tinymce.get( id ) ) { restoreTextMode = tinymce.get( id ).isHidden(); wp.oldEditor.remove( id ); } // Add or enable the `wpview` plugin. $( document ).one( 'wp-before-tinymce-init.text-widget-init', function( event, init ) { // If somebody has removed all plugins, they must have a good reason. // Keep it that way. if ( ! init.plugins ) { return; } else if ( ! /\bwpview\b/.test( init.plugins ) ) { init.plugins += ',wpview'; } } ); wp.oldEditor.initialize( id, { tinymce: { wpautop: true }, quicktags: true, mediaButtons: true }); /** * Show a pointer, focus on dismiss, and speak the contents for a11y. * * @param {jQuery} pointerElement Pointer element. * @return {void} */ showPointerElement = function( pointerElement ) { pointerElement.show(); pointerElement.find( '.close' ).trigger( 'focus' ); wp.a11y.speak( pointerElement.find( 'h3, p' ).map( function() { return $( this ).text(); } ).get().join( '\n\n' ) ); }; editor = window.tinymce.get( id ); if ( ! editor ) { throw new Error( 'Failed to initialize editor' ); } onInit = function() { // When a widget is moved in the DOM the dynamically-created TinyMCE iframe will be destroyed and has to be re-built. $( editor.getWin() ).on( 'pagehide', function() { _.defer( buildEditor ); }); // If a prior mce instance was replaced, and it was in text mode, toggle to text mode. if ( restoreTextMode ) { switchEditors.go( id, 'html' ); } // Show the pointer. $( '#' + id + '-html' ).on( 'click', function() { control.pasteHtmlPointer.hide(); // Hide the HTML pasting pointer. if ( -1 !== component.dismissedPointers.indexOf( 'text_widget_custom_html' ) ) { return; } showPointerElement( control.customHtmlWidgetPointer ); }); // Hide the pointer when switching tabs. $( '#' + id + '-tmce' ).on( 'click', function() { control.customHtmlWidgetPointer.hide(); }); // Show pointer when pasting HTML. editor.on( 'pastepreprocess', function( event ) { var content = event.content; if ( -1 !== component.dismissedPointers.indexOf( 'text_widget_paste_html' ) || ! content || ! /<\w+.*?>/.test( content ) ) { return; } // Show the pointer after a slight delay so the user sees what they pasted. _.delay( function() { showPointerElement( control.pasteHtmlPointer ); }, 250 ); }); }; if ( editor.initialized ) { onInit(); } else { editor.on( 'init', onInit ); } control.editorFocused = false; editor.on( 'focus', function onEditorFocus() { control.editorFocused = true; }); editor.on( 'paste', function onEditorPaste() { editor.setDirty( true ); // Because pasting doesn't currently set the dirty state. triggerChangeIfDirty(); }); editor.on( 'NodeChange', function onNodeChange() { needsTextareaChangeTrigger = true; }); editor.on( 'NodeChange', _.debounce( triggerChangeIfDirty, changeDebounceDelay ) ); editor.on( 'blur hide', function onEditorBlur() { control.editorFocused = false; triggerChangeIfDirty(); }); control.editor = editor; } buildEditor(); } }); /** * Mapping of widget ID to instances of TextWidgetControl subclasses. * * @memberOf wp.textWidgets * * @type {Object.<string, wp.textWidgets.TextWidgetControl>} */ component.widgetControls = {}; /** * Handle widget being added or initialized for the first time at the widget-added event. * * @memberOf wp.textWidgets * * @param {jQuery.Event} event - Event. * @param {jQuery} widgetContainer - Widget container element. * * @return {void} */ component.handleWidgetAdded = function handleWidgetAdded( event, widgetContainer ) { var widgetForm, idBase, widgetControl, widgetId, animatedCheckDelay = 50, renderWhenAnimationDone, fieldContainer, syncContainer; widgetForm = widgetContainer.find( '> .widget-inside > .form, > .widget-inside > form' ); // Note: '.form' appears in the customizer, whereas 'form' on the widgets admin screen. idBase = widgetForm.find( '> .id_base' ).val(); if ( -1 === component.idBases.indexOf( idBase ) ) { return; } // Prevent initializing already-added widgets. widgetId = widgetForm.find( '.widget-id' ).val(); if ( component.widgetControls[ widgetId ] ) { return; } // Bypass using TinyMCE when widget is in legacy mode. if ( ! widgetForm.find( '.visual' ).val() ) { return; } /* * Create a container element for the widget control fields. * This is inserted into the DOM immediately before the .widget-content * element because the contents of this element are essentially "managed" * by PHP, where each widget update cause the entire element to be emptied * and replaced with the rendered output of WP_Widget::form() which is * sent back in Ajax request made to save/update the widget instance. * To prevent a "flash of replaced DOM elements and re-initialized JS * components", the JS template is rendered outside of the normal form * container. */ fieldContainer = $( '<div></div>' ); syncContainer = widgetContainer.find( '.widget-content:first' ); syncContainer.before( fieldContainer ); widgetControl = new component.TextWidgetControl({ el: fieldContainer, syncContainer: syncContainer }); component.widgetControls[ widgetId ] = widgetControl; /* * Render the widget once the widget parent's container finishes animating, * as the widget-added event fires with a slideDown of the container. * This ensures that the textarea is visible and an iframe can be embedded * with TinyMCE being able to set contenteditable on it. */ renderWhenAnimationDone = function() { if ( ! widgetContainer.hasClass( 'open' ) ) { setTimeout( renderWhenAnimationDone, animatedCheckDelay ); } else { widgetControl.initializeEditor(); } }; renderWhenAnimationDone(); }; /** * Setup widget in accessibility mode. * * @memberOf wp.textWidgets * * @return {void} */ component.setupAccessibleMode = function setupAccessibleMode() { var widgetForm, idBase, widgetControl, fieldContainer, syncContainer; widgetForm = $( '.editwidget > form' ); if ( 0 === widgetForm.length ) { return; } idBase = widgetForm.find( '.id_base' ).val(); if ( -1 === component.idBases.indexOf( idBase ) ) { return; } // Bypass using TinyMCE when widget is in legacy mode. if ( ! widgetForm.find( '.visual' ).val() ) { return; } fieldContainer = $( '<div></div>' ); syncContainer = widgetForm.find( '> .widget-inside' ); syncContainer.before( fieldContainer ); widgetControl = new component.TextWidgetControl({ el: fieldContainer, syncContainer: syncContainer }); widgetControl.initializeEditor(); }; /** * Sync widget instance data sanitized from server back onto widget model. * * This gets called via the 'widget-updated' event when saving a widget from * the widgets admin screen and also via the 'widget-synced' event when making * a change to a widget in the customizer. * * @memberOf wp.textWidgets * * @param {jQuery.Event} event - Event. * @param {jQuery} widgetContainer - Widget container element. * @return {void} */ component.handleWidgetUpdated = function handleWidgetUpdated( event, widgetContainer ) { var widgetForm, widgetId, widgetControl, idBase; widgetForm = widgetContainer.find( '> .widget-inside > .form, > .widget-inside > form' ); idBase = widgetForm.find( '> .id_base' ).val(); if ( -1 === component.idBases.indexOf( idBase ) ) { return; } widgetId = widgetForm.find( '> .widget-id' ).val(); widgetControl = component.widgetControls[ widgetId ]; if ( ! widgetControl ) { return; } widgetControl.updateFields(); }; /** * Initialize functionality. * * This function exists to prevent the JS file from having to boot itself. * When WordPress enqueues this script, it should have an inline script * attached which calls wp.textWidgets.init(). * * @memberOf wp.textWidgets * * @return {void} */ component.init = function init() { var $document = $( document ); $document.on( 'widget-added', component.handleWidgetAdded ); $document.on( 'widget-synced widget-updated', component.handleWidgetUpdated ); /* * Manually trigger widget-added events for media widgets on the admin * screen once they are expanded. The widget-added event is not triggered * for each pre-existing widget on the widgets admin screen like it is * on the customizer. Likewise, the customizer only triggers widget-added * when the widget is expanded to just-in-time construct the widget form * when it is actually going to be displayed. So the following implements * the same for the widgets admin screen, to invoke the widget-added * handler when a pre-existing media widget is expanded. */ $( function initializeExistingWidgetContainers() { var widgetContainers; if ( 'widgets' !== window.pagenow ) { return; } widgetContainers = $( '.widgets-holder-wrap:not(#available-widgets)' ).find( 'div.widget' ); widgetContainers.one( 'click.toggle-widget-expanded', function toggleWidgetExpanded() { var widgetContainer = $( this ); component.handleWidgetAdded( new jQuery.Event( 'widget-added' ), widgetContainer ); }); // Accessibility mode. component.setupAccessibleMode(); }); }; return component; })( jQuery ); custom-html-widgets.js 0000644 00000036642 14717746353 0011056 0 ustar 00 /** * @output wp-admin/js/widgets/custom-html-widgets.js */ /* global wp */ /* eslint consistent-this: [ "error", "control" ] */ /* eslint no-magic-numbers: ["error", { "ignore": [0,1,-1] }] */ /** * @namespace wp.customHtmlWidget * @memberOf wp */ wp.customHtmlWidgets = ( function( $ ) { 'use strict'; var component = { idBases: [ 'custom_html' ], codeEditorSettings: {}, l10n: { errorNotice: { singular: '', plural: '' } } }; component.CustomHtmlWidgetControl = Backbone.View.extend(/** @lends wp.customHtmlWidgets.CustomHtmlWidgetControl.prototype */{ /** * View events. * * @type {Object} */ events: {}, /** * Text widget control. * * @constructs wp.customHtmlWidgets.CustomHtmlWidgetControl * @augments Backbone.View * @abstract * * @param {Object} options - Options. * @param {jQuery} options.el - Control field container element. * @param {jQuery} options.syncContainer - Container element where fields are synced for the server. * * @return {void} */ initialize: function initialize( options ) { var control = this; if ( ! options.el ) { throw new Error( 'Missing options.el' ); } if ( ! options.syncContainer ) { throw new Error( 'Missing options.syncContainer' ); } Backbone.View.prototype.initialize.call( control, options ); control.syncContainer = options.syncContainer; control.widgetIdBase = control.syncContainer.parent().find( '.id_base' ).val(); control.widgetNumber = control.syncContainer.parent().find( '.widget_number' ).val(); control.customizeSettingId = 'widget_' + control.widgetIdBase + '[' + String( control.widgetNumber ) + ']'; control.$el.addClass( 'custom-html-widget-fields' ); control.$el.html( wp.template( 'widget-custom-html-control-fields' )( { codeEditorDisabled: component.codeEditorSettings.disabled } ) ); control.errorNoticeContainer = control.$el.find( '.code-editor-error-container' ); control.currentErrorAnnotations = []; control.saveButton = control.syncContainer.add( control.syncContainer.parent().find( '.widget-control-actions' ) ).find( '.widget-control-save, #savewidget' ); control.saveButton.addClass( 'custom-html-widget-save-button' ); // To facilitate style targeting. control.fields = { title: control.$el.find( '.title' ), content: control.$el.find( '.content' ) }; // Sync input fields to hidden sync fields which actually get sent to the server. _.each( control.fields, function( fieldInput, fieldName ) { fieldInput.on( 'input change', function updateSyncField() { var syncInput = control.syncContainer.find( '.sync-input.' + fieldName ); if ( syncInput.val() !== fieldInput.val() ) { syncInput.val( fieldInput.val() ); syncInput.trigger( 'change' ); } }); // Note that syncInput cannot be re-used because it will be destroyed with each widget-updated event. fieldInput.val( control.syncContainer.find( '.sync-input.' + fieldName ).val() ); }); }, /** * Update input fields from the sync fields. * * This function is called at the widget-updated and widget-synced events. * A field will only be updated if it is not currently focused, to avoid * overwriting content that the user is entering. * * @return {void} */ updateFields: function updateFields() { var control = this, syncInput; if ( ! control.fields.title.is( document.activeElement ) ) { syncInput = control.syncContainer.find( '.sync-input.title' ); control.fields.title.val( syncInput.val() ); } /* * Prevent updating content when the editor is focused or if there are current error annotations, * to prevent the editor's contents from getting sanitized as soon as a user removes focus from * the editor. This is particularly important for users who cannot unfiltered_html. */ control.contentUpdateBypassed = control.fields.content.is( document.activeElement ) || control.editor && control.editor.codemirror.state.focused || 0 !== control.currentErrorAnnotations.length; if ( ! control.contentUpdateBypassed ) { syncInput = control.syncContainer.find( '.sync-input.content' ); control.fields.content.val( syncInput.val() ); } }, /** * Show linting error notice. * * @param {Array} errorAnnotations - Error annotations. * @return {void} */ updateErrorNotice: function( errorAnnotations ) { var control = this, errorNotice, message = '', customizeSetting; if ( 1 === errorAnnotations.length ) { message = component.l10n.errorNotice.singular.replace( '%d', '1' ); } else if ( errorAnnotations.length > 1 ) { message = component.l10n.errorNotice.plural.replace( '%d', String( errorAnnotations.length ) ); } if ( control.fields.content[0].setCustomValidity ) { control.fields.content[0].setCustomValidity( message ); } if ( wp.customize && wp.customize.has( control.customizeSettingId ) ) { customizeSetting = wp.customize( control.customizeSettingId ); customizeSetting.notifications.remove( 'htmlhint_error' ); if ( 0 !== errorAnnotations.length ) { customizeSetting.notifications.add( 'htmlhint_error', new wp.customize.Notification( 'htmlhint_error', { message: message, type: 'error' } ) ); } } else if ( 0 !== errorAnnotations.length ) { errorNotice = $( '<div class="inline notice notice-error notice-alt" role="alert"></div>' ); errorNotice.append( $( '<p></p>', { text: message } ) ); control.errorNoticeContainer.empty(); control.errorNoticeContainer.append( errorNotice ); control.errorNoticeContainer.slideDown( 'fast' ); wp.a11y.speak( message ); } else { control.errorNoticeContainer.slideUp( 'fast' ); } }, /** * Initialize editor. * * @return {void} */ initializeEditor: function initializeEditor() { var control = this, settings; if ( component.codeEditorSettings.disabled ) { return; } settings = _.extend( {}, component.codeEditorSettings, { /** * Handle tabbing to the field before the editor. * * @ignore * * @return {void} */ onTabPrevious: function onTabPrevious() { control.fields.title.focus(); }, /** * Handle tabbing to the field after the editor. * * @ignore * * @return {void} */ onTabNext: function onTabNext() { var tabbables = control.syncContainer.add( control.syncContainer.parent().find( '.widget-position, .widget-control-actions' ) ).find( ':tabbable' ); tabbables.first().focus(); }, /** * Disable save button and store linting errors for use in updateFields. * * @ignore * * @param {Array} errorAnnotations - Error notifications. * @return {void} */ onChangeLintingErrors: function onChangeLintingErrors( errorAnnotations ) { control.currentErrorAnnotations = errorAnnotations; }, /** * Update error notice. * * @ignore * * @param {Array} errorAnnotations - Error annotations. * @return {void} */ onUpdateErrorNotice: function onUpdateErrorNotice( errorAnnotations ) { control.saveButton.toggleClass( 'validation-blocked disabled', errorAnnotations.length > 0 ); control.updateErrorNotice( errorAnnotations ); } }); control.editor = wp.codeEditor.initialize( control.fields.content, settings ); // Improve the editor accessibility. $( control.editor.codemirror.display.lineDiv ) .attr({ role: 'textbox', 'aria-multiline': 'true', 'aria-labelledby': control.fields.content[0].id + '-label', 'aria-describedby': 'editor-keyboard-trap-help-1 editor-keyboard-trap-help-2 editor-keyboard-trap-help-3 editor-keyboard-trap-help-4' }); // Focus the editor when clicking on its label. $( '#' + control.fields.content[0].id + '-label' ).on( 'click', function() { control.editor.codemirror.focus(); }); control.fields.content.on( 'change', function() { if ( this.value !== control.editor.codemirror.getValue() ) { control.editor.codemirror.setValue( this.value ); } }); control.editor.codemirror.on( 'change', function() { var value = control.editor.codemirror.getValue(); if ( value !== control.fields.content.val() ) { control.fields.content.val( value ).trigger( 'change' ); } }); // Make sure the editor gets updated if the content was updated on the server (sanitization) but not updated in the editor since it was focused. control.editor.codemirror.on( 'blur', function() { if ( control.contentUpdateBypassed ) { control.syncContainer.find( '.sync-input.content' ).trigger( 'change' ); } }); // Prevent hitting Esc from collapsing the widget control. if ( wp.customize ) { control.editor.codemirror.on( 'keydown', function onKeydown( codemirror, event ) { var escKeyCode = 27; if ( escKeyCode === event.keyCode ) { event.stopPropagation(); } }); } } }); /** * Mapping of widget ID to instances of CustomHtmlWidgetControl subclasses. * * @alias wp.customHtmlWidgets.widgetControls * * @type {Object.<string, wp.textWidgets.CustomHtmlWidgetControl>} */ component.widgetControls = {}; /** * Handle widget being added or initialized for the first time at the widget-added event. * * @alias wp.customHtmlWidgets.handleWidgetAdded * * @param {jQuery.Event} event - Event. * @param {jQuery} widgetContainer - Widget container element. * * @return {void} */ component.handleWidgetAdded = function handleWidgetAdded( event, widgetContainer ) { var widgetForm, idBase, widgetControl, widgetId, animatedCheckDelay = 50, renderWhenAnimationDone, fieldContainer, syncContainer; widgetForm = widgetContainer.find( '> .widget-inside > .form, > .widget-inside > form' ); // Note: '.form' appears in the customizer, whereas 'form' on the widgets admin screen. idBase = widgetForm.find( '> .id_base' ).val(); if ( -1 === component.idBases.indexOf( idBase ) ) { return; } // Prevent initializing already-added widgets. widgetId = widgetForm.find( '.widget-id' ).val(); if ( component.widgetControls[ widgetId ] ) { return; } /* * Create a container element for the widget control fields. * This is inserted into the DOM immediately before the the .widget-content * element because the contents of this element are essentially "managed" * by PHP, where each widget update cause the entire element to be emptied * and replaced with the rendered output of WP_Widget::form() which is * sent back in Ajax request made to save/update the widget instance. * To prevent a "flash of replaced DOM elements and re-initialized JS * components", the JS template is rendered outside of the normal form * container. */ fieldContainer = $( '<div></div>' ); syncContainer = widgetContainer.find( '.widget-content:first' ); syncContainer.before( fieldContainer ); widgetControl = new component.CustomHtmlWidgetControl({ el: fieldContainer, syncContainer: syncContainer }); component.widgetControls[ widgetId ] = widgetControl; /* * Render the widget once the widget parent's container finishes animating, * as the widget-added event fires with a slideDown of the container. * This ensures that the textarea is visible and the editor can be initialized. */ renderWhenAnimationDone = function() { if ( ! ( wp.customize ? widgetContainer.parent().hasClass( 'expanded' ) : widgetContainer.hasClass( 'open' ) ) ) { // Core merge: The wp.customize condition can be eliminated with this change being in core: https://github.com/xwp/wordpress-develop/pull/247/commits/5322387d setTimeout( renderWhenAnimationDone, animatedCheckDelay ); } else { widgetControl.initializeEditor(); } }; renderWhenAnimationDone(); }; /** * Setup widget in accessibility mode. * * @alias wp.customHtmlWidgets.setupAccessibleMode * * @return {void} */ component.setupAccessibleMode = function setupAccessibleMode() { var widgetForm, idBase, widgetControl, fieldContainer, syncContainer; widgetForm = $( '.editwidget > form' ); if ( 0 === widgetForm.length ) { return; } idBase = widgetForm.find( '.id_base' ).val(); if ( -1 === component.idBases.indexOf( idBase ) ) { return; } fieldContainer = $( '<div></div>' ); syncContainer = widgetForm.find( '> .widget-inside' ); syncContainer.before( fieldContainer ); widgetControl = new component.CustomHtmlWidgetControl({ el: fieldContainer, syncContainer: syncContainer }); widgetControl.initializeEditor(); }; /** * Sync widget instance data sanitized from server back onto widget model. * * This gets called via the 'widget-updated' event when saving a widget from * the widgets admin screen and also via the 'widget-synced' event when making * a change to a widget in the customizer. * * @alias wp.customHtmlWidgets.handleWidgetUpdated * * @param {jQuery.Event} event - Event. * @param {jQuery} widgetContainer - Widget container element. * @return {void} */ component.handleWidgetUpdated = function handleWidgetUpdated( event, widgetContainer ) { var widgetForm, widgetId, widgetControl, idBase; widgetForm = widgetContainer.find( '> .widget-inside > .form, > .widget-inside > form' ); idBase = widgetForm.find( '> .id_base' ).val(); if ( -1 === component.idBases.indexOf( idBase ) ) { return; } widgetId = widgetForm.find( '> .widget-id' ).val(); widgetControl = component.widgetControls[ widgetId ]; if ( ! widgetControl ) { return; } widgetControl.updateFields(); }; /** * Initialize functionality. * * This function exists to prevent the JS file from having to boot itself. * When WordPress enqueues this script, it should have an inline script * attached which calls wp.textWidgets.init(). * * @alias wp.customHtmlWidgets.init * * @param {Object} settings - Options for code editor, exported from PHP. * * @return {void} */ component.init = function init( settings ) { var $document = $( document ); _.extend( component.codeEditorSettings, settings ); $document.on( 'widget-added', component.handleWidgetAdded ); $document.on( 'widget-synced widget-updated', component.handleWidgetUpdated ); /* * Manually trigger widget-added events for media widgets on the admin * screen once they are expanded. The widget-added event is not triggered * for each pre-existing widget on the widgets admin screen like it is * on the customizer. Likewise, the customizer only triggers widget-added * when the widget is expanded to just-in-time construct the widget form * when it is actually going to be displayed. So the following implements * the same for the widgets admin screen, to invoke the widget-added * handler when a pre-existing media widget is expanded. */ $( function initializeExistingWidgetContainers() { var widgetContainers; if ( 'widgets' !== window.pagenow ) { return; } widgetContainers = $( '.widgets-holder-wrap:not(#available-widgets)' ).find( 'div.widget' ); widgetContainers.one( 'click.toggle-widget-expanded', function toggleWidgetExpanded() { var widgetContainer = $( this ); component.handleWidgetAdded( new jQuery.Event( 'widget-added' ), widgetContainer ); }); // Accessibility mode. if ( document.readyState === 'complete' ) { // Page is fully loaded. component.setupAccessibleMode(); } else { // Page is still loading. $( window ).on( 'load', function() { component.setupAccessibleMode(); }); } }); }; return component; })( jQuery ); media-image-widget.min.js 0000644 00000003750 14717746353 0011332 0 ustar 00 /*! This file is auto-generated */ !function(a,o){"use strict";var e=a.MediaWidgetModel.extend({}),t=a.MediaWidgetControl.extend({events:_.extend({},a.MediaWidgetControl.prototype.events,{"click .media-widget-preview.populated":"editMedia"}),renderPreview:function(){var e,t,i=this;(i.model.get("attachment_id")||i.model.get("url"))&&(t=i.$el.find(".media-widget-preview"),e=wp.template("wp-media-widget-image-preview"),t.html(e(i.previewTemplateProps.toJSON())),t.addClass("populated"),i.$el.find(".link").is(document.activeElement)||(e=i.$el.find(".media-widget-fields"),t=wp.template("wp-media-widget-image-fields"),e.html(t(i.previewTemplateProps.toJSON()))))},editMedia:function(){var i,e,a=this,t=a.mapModelToMediaFrameProps(a.model.toJSON());"none"===t.link&&(t.linkUrl=""),(i=wp.media({frame:"image",state:"image-details",metadata:t})).$el.addClass("media-widget"),t=function(){var e=i.state().attributes.image.toJSON(),t=e.link;e.link=e.linkUrl,a.selectedAttachment.set(e),a.displaySettings.set("link",t),a.model.set(_.extend(a.mapMediaToModelProps(e),{error:!1}))},i.state("image-details").on("update",t),i.state("replace-image").on("replace",t),e=wp.media.model.Attachment.prototype.sync,wp.media.model.Attachment.prototype.sync=function(){return o.Deferred().rejectWith(this).promise()},i.on("close",function(){i.detach(),wp.media.model.Attachment.prototype.sync=e}),i.open()},getEmbedResetProps:function(){return _.extend(a.MediaWidgetControl.prototype.getEmbedResetProps.call(this),{size:"full",width:0,height:0})},getModelPropsFromMediaFrame:function(e){return _.omit(a.MediaWidgetControl.prototype.getModelPropsFromMediaFrame.call(this,e),"image_title")},mapModelToPreviewTemplateProps:function(){var e=this,t=e.model.get("url"),i=a.MediaWidgetControl.prototype.mapModelToPreviewTemplateProps.call(e);return i.currentFilename=t?t.replace(/\?.*$/,"").replace(/^.+\//,""):"",i.link_url=e.model.get("link_url"),i}});a.controlConstructors.media_image=t,a.modelConstructors.media_image=e}(wp.mediaWidgets,jQuery); media-video-widget.js 0000644 00000015554 14717746353 0010601 0 ustar 00 /** * @output wp-admin/js/widgets/media-video-widget.js */ /* eslint consistent-this: [ "error", "control" ] */ (function( component ) { 'use strict'; var VideoWidgetModel, VideoWidgetControl, VideoDetailsMediaFrame; /** * Custom video details frame that removes the replace-video state. * * @class wp.mediaWidgets.controlConstructors~VideoDetailsMediaFrame * @augments wp.media.view.MediaFrame.VideoDetails * * @private */ VideoDetailsMediaFrame = wp.media.view.MediaFrame.VideoDetails.extend(/** @lends wp.mediaWidgets.controlConstructors~VideoDetailsMediaFrame.prototype */{ /** * Create the default states. * * @return {void} */ createStates: function createStates() { this.states.add([ new wp.media.controller.VideoDetails({ media: this.media }), new wp.media.controller.MediaLibrary({ type: 'video', id: 'add-video-source', title: wp.media.view.l10n.videoAddSourceTitle, toolbar: 'add-video-source', media: this.media, menu: false }), new wp.media.controller.MediaLibrary({ type: 'text', id: 'add-track', title: wp.media.view.l10n.videoAddTrackTitle, toolbar: 'add-track', media: this.media, menu: 'video-details' }) ]); } }); /** * Video widget model. * * See WP_Widget_Video::enqueue_admin_scripts() for amending prototype from PHP exports. * * @class wp.mediaWidgets.modelConstructors.media_video * @augments wp.mediaWidgets.MediaWidgetModel */ VideoWidgetModel = component.MediaWidgetModel.extend({}); /** * Video widget control. * * See WP_Widget_Video::enqueue_admin_scripts() for amending prototype from PHP exports. * * @class wp.mediaWidgets.controlConstructors.media_video * @augments wp.mediaWidgets.MediaWidgetControl */ VideoWidgetControl = component.MediaWidgetControl.extend(/** @lends wp.mediaWidgets.controlConstructors.media_video.prototype */{ /** * Show display settings. * * @type {boolean} */ showDisplaySettings: false, /** * Cache of oembed responses. * * @type {Object} */ oembedResponses: {}, /** * Map model props to media frame props. * * @param {Object} modelProps - Model props. * @return {Object} Media frame props. */ mapModelToMediaFrameProps: function mapModelToMediaFrameProps( modelProps ) { var control = this, mediaFrameProps; mediaFrameProps = component.MediaWidgetControl.prototype.mapModelToMediaFrameProps.call( control, modelProps ); mediaFrameProps.link = 'embed'; return mediaFrameProps; }, /** * Fetches embed data for external videos. * * @return {void} */ fetchEmbed: function fetchEmbed() { var control = this, url; url = control.model.get( 'url' ); // If we already have a local cache of the embed response, return. if ( control.oembedResponses[ url ] ) { return; } // If there is an in-flight embed request, abort it. if ( control.fetchEmbedDfd && 'pending' === control.fetchEmbedDfd.state() ) { control.fetchEmbedDfd.abort(); } control.fetchEmbedDfd = wp.apiRequest({ url: wp.media.view.settings.oEmbedProxyUrl, data: { url: control.model.get( 'url' ), maxwidth: control.model.get( 'width' ), maxheight: control.model.get( 'height' ), discover: false }, type: 'GET', dataType: 'json', context: control }); control.fetchEmbedDfd.done( function( response ) { control.oembedResponses[ url ] = response; control.renderPreview(); }); control.fetchEmbedDfd.fail( function() { control.oembedResponses[ url ] = null; }); }, /** * Whether a url is a supported external host. * * @deprecated since 4.9. * * @return {boolean} Whether url is a supported video host. */ isHostedVideo: function isHostedVideo() { return true; }, /** * Render preview. * * @return {void} */ renderPreview: function renderPreview() { var control = this, previewContainer, previewTemplate, attachmentId, attachmentUrl, poster, html = '', isOEmbed = false, mime, error, urlParser, matches; attachmentId = control.model.get( 'attachment_id' ); attachmentUrl = control.model.get( 'url' ); error = control.model.get( 'error' ); if ( ! attachmentId && ! attachmentUrl ) { return; } // Verify the selected attachment mime is supported. mime = control.selectedAttachment.get( 'mime' ); if ( mime && attachmentId ) { if ( ! _.contains( _.values( wp.media.view.settings.embedMimes ), mime ) ) { error = 'unsupported_file_type'; } } else if ( ! attachmentId ) { urlParser = document.createElement( 'a' ); urlParser.href = attachmentUrl; matches = urlParser.pathname.toLowerCase().match( /\.(\w+)$/ ); if ( matches ) { if ( ! _.contains( _.keys( wp.media.view.settings.embedMimes ), matches[1] ) ) { error = 'unsupported_file_type'; } } else { isOEmbed = true; } } if ( isOEmbed ) { control.fetchEmbed(); if ( control.oembedResponses[ attachmentUrl ] ) { poster = control.oembedResponses[ attachmentUrl ].thumbnail_url; html = control.oembedResponses[ attachmentUrl ].html.replace( /\swidth="\d+"/, ' width="100%"' ).replace( /\sheight="\d+"/, '' ); } } previewContainer = control.$el.find( '.media-widget-preview' ); previewTemplate = wp.template( 'wp-media-widget-video-preview' ); previewContainer.html( previewTemplate({ model: { attachment_id: attachmentId, html: html, src: attachmentUrl, poster: poster }, is_oembed: isOEmbed, error: error })); wp.mediaelement.initialize(); }, /** * Open the media image-edit frame to modify the selected item. * * @return {void} */ editMedia: function editMedia() { var control = this, mediaFrame, metadata, updateCallback; metadata = control.mapModelToMediaFrameProps( control.model.toJSON() ); // Set up the media frame. mediaFrame = new VideoDetailsMediaFrame({ frame: 'video', state: 'video-details', metadata: metadata }); wp.media.frame = mediaFrame; mediaFrame.$el.addClass( 'media-widget' ); updateCallback = function( mediaFrameProps ) { // Update cached attachment object to avoid having to re-fetch. This also triggers re-rendering of preview. control.selectedAttachment.set( mediaFrameProps ); control.model.set( _.extend( _.omit( control.model.defaults(), 'title' ), control.mapMediaToModelProps( mediaFrameProps ), { error: false } ) ); }; mediaFrame.state( 'video-details' ).on( 'update', updateCallback ); mediaFrame.state( 'replace-video' ).on( 'replace', updateCallback ); mediaFrame.on( 'close', function() { mediaFrame.detach(); }); mediaFrame.open(); } }); // Exports. component.controlConstructors.media_video = VideoWidgetControl; component.modelConstructors.media_video = VideoWidgetModel; })( wp.mediaWidgets ); media-audio-widget.min.js 0000644 00000002647 14717746353 0011355 0 ustar 00 /*! This file is auto-generated */ !function(t){"use strict";var a=wp.media.view.MediaFrame.AudioDetails.extend({createStates:function(){this.states.add([new wp.media.controller.AudioDetails({media:this.media}),new wp.media.controller.MediaLibrary({type:"audio",id:"add-audio-source",title:wp.media.view.l10n.audioAddSourceTitle,toolbar:"add-audio-source",media:this.media,menu:!1})])}}),e=t.MediaWidgetModel.extend({}),d=t.MediaWidgetControl.extend({showDisplaySettings:!1,mapModelToMediaFrameProps:function(e){e=t.MediaWidgetControl.prototype.mapModelToMediaFrameProps.call(this,e);return e.link="embed",e},renderPreview:function(){var e,t=this,d=t.model.get("attachment_id"),a=t.model.get("url");(d||a)&&(d=t.$el.find(".media-widget-preview"),e=wp.template("wp-media-widget-audio-preview"),d.html(e({model:{attachment_id:t.model.get("attachment_id"),src:a},error:t.model.get("error")})),wp.mediaelement.initialize())},editMedia:function(){var t=this,e=t.mapModelToMediaFrameProps(t.model.toJSON()),d=new a({frame:"audio",state:"audio-details",metadata:e});(wp.media.frame=d).$el.addClass("media-widget"),e=function(e){t.selectedAttachment.set(e),t.model.set(_.extend(t.model.defaults(),t.mapMediaToModelProps(e),{error:!1}))},d.state("audio-details").on("update",e),d.state("replace-audio").on("replace",e),d.on("close",function(){d.detach()}),d.open()}});t.controlConstructors.media_audio=d,t.modelConstructors.media_audio=e}(wp.mediaWidgets); custom-html-widgets.min.js 0000644 00000012716 14717746353 0011634 0 ustar 00 /*! This file is auto-generated */ wp.customHtmlWidgets=function(a){"use strict";var s={idBases:["custom_html"],codeEditorSettings:{},l10n:{errorNotice:{singular:"",plural:""}}};return s.CustomHtmlWidgetControl=Backbone.View.extend({events:{},initialize:function(e){var n=this;if(!e.el)throw new Error("Missing options.el");if(!e.syncContainer)throw new Error("Missing options.syncContainer");Backbone.View.prototype.initialize.call(n,e),n.syncContainer=e.syncContainer,n.widgetIdBase=n.syncContainer.parent().find(".id_base").val(),n.widgetNumber=n.syncContainer.parent().find(".widget_number").val(),n.customizeSettingId="widget_"+n.widgetIdBase+"["+String(n.widgetNumber)+"]",n.$el.addClass("custom-html-widget-fields"),n.$el.html(wp.template("widget-custom-html-control-fields")({codeEditorDisabled:s.codeEditorSettings.disabled})),n.errorNoticeContainer=n.$el.find(".code-editor-error-container"),n.currentErrorAnnotations=[],n.saveButton=n.syncContainer.add(n.syncContainer.parent().find(".widget-control-actions")).find(".widget-control-save, #savewidget"),n.saveButton.addClass("custom-html-widget-save-button"),n.fields={title:n.$el.find(".title"),content:n.$el.find(".content")},_.each(n.fields,function(t,i){t.on("input change",function(){var e=n.syncContainer.find(".sync-input."+i);e.val()!==t.val()&&(e.val(t.val()),e.trigger("change"))}),t.val(n.syncContainer.find(".sync-input."+i).val())})},updateFields:function(){var e,t=this;t.fields.title.is(document.activeElement)||(e=t.syncContainer.find(".sync-input.title"),t.fields.title.val(e.val())),t.contentUpdateBypassed=t.fields.content.is(document.activeElement)||t.editor&&t.editor.codemirror.state.focused||0!==t.currentErrorAnnotations.length,t.contentUpdateBypassed||(e=t.syncContainer.find(".sync-input.content"),t.fields.content.val(e.val()))},updateErrorNotice:function(e){var t,i=this,n="";1===e.length?n=s.l10n.errorNotice.singular.replace("%d","1"):1<e.length&&(n=s.l10n.errorNotice.plural.replace("%d",String(e.length))),i.fields.content[0].setCustomValidity&&i.fields.content[0].setCustomValidity(n),wp.customize&&wp.customize.has(i.customizeSettingId)?((t=wp.customize(i.customizeSettingId)).notifications.remove("htmlhint_error"),0!==e.length&&t.notifications.add("htmlhint_error",new wp.customize.Notification("htmlhint_error",{message:n,type:"error"}))):0!==e.length?((t=a('<div class="inline notice notice-error notice-alt" role="alert"></div>')).append(a("<p></p>",{text:n})),i.errorNoticeContainer.empty(),i.errorNoticeContainer.append(t),i.errorNoticeContainer.slideDown("fast"),wp.a11y.speak(n)):i.errorNoticeContainer.slideUp("fast")},initializeEditor:function(){var e,t=this;s.codeEditorSettings.disabled||(e=_.extend({},s.codeEditorSettings,{onTabPrevious:function(){t.fields.title.focus()},onTabNext:function(){t.syncContainer.add(t.syncContainer.parent().find(".widget-position, .widget-control-actions")).find(":tabbable").first().focus()},onChangeLintingErrors:function(e){t.currentErrorAnnotations=e},onUpdateErrorNotice:function(e){t.saveButton.toggleClass("validation-blocked disabled",0<e.length),t.updateErrorNotice(e)}}),t.editor=wp.codeEditor.initialize(t.fields.content,e),a(t.editor.codemirror.display.lineDiv).attr({role:"textbox","aria-multiline":"true","aria-labelledby":t.fields.content[0].id+"-label","aria-describedby":"editor-keyboard-trap-help-1 editor-keyboard-trap-help-2 editor-keyboard-trap-help-3 editor-keyboard-trap-help-4"}),a("#"+t.fields.content[0].id+"-label").on("click",function(){t.editor.codemirror.focus()}),t.fields.content.on("change",function(){this.value!==t.editor.codemirror.getValue()&&t.editor.codemirror.setValue(this.value)}),t.editor.codemirror.on("change",function(){var e=t.editor.codemirror.getValue();e!==t.fields.content.val()&&t.fields.content.val(e).trigger("change")}),t.editor.codemirror.on("blur",function(){t.contentUpdateBypassed&&t.syncContainer.find(".sync-input.content").trigger("change")}),wp.customize&&t.editor.codemirror.on("keydown",function(e,t){27===t.keyCode&&t.stopPropagation()}))}}),s.widgetControls={},s.handleWidgetAdded=function(e,t){var i,n,o,d=t.find("> .widget-inside > .form, > .widget-inside > form"),r=d.find("> .id_base").val();-1===s.idBases.indexOf(r)||(r=d.find(".widget-id").val(),s.widgetControls[r])||(d=a("<div></div>"),(o=t.find(".widget-content:first")).before(d),i=new s.CustomHtmlWidgetControl({el:d,syncContainer:o}),s.widgetControls[r]=i,(n=function(){(wp.customize?t.parent().hasClass("expanded"):t.hasClass("open"))?i.initializeEditor():setTimeout(n,50)})())},s.setupAccessibleMode=function(){var e,t=a(".editwidget > form");0!==t.length&&(e=t.find(".id_base").val(),-1!==s.idBases.indexOf(e))&&(e=a("<div></div>"),(t=t.find("> .widget-inside")).before(e),new s.CustomHtmlWidgetControl({el:e,syncContainer:t}).initializeEditor())},s.handleWidgetUpdated=function(e,t){var t=t.find("> .widget-inside > .form, > .widget-inside > form"),i=t.find("> .id_base").val();-1!==s.idBases.indexOf(i)&&(i=t.find("> .widget-id").val(),t=s.widgetControls[i])&&t.updateFields()},s.init=function(e){var t=a(document);_.extend(s.codeEditorSettings,e),t.on("widget-added",s.handleWidgetAdded),t.on("widget-synced widget-updated",s.handleWidgetUpdated),a(function(){"widgets"===window.pagenow&&(a(".widgets-holder-wrap:not(#available-widgets)").find("div.widget").one("click.toggle-widget-expanded",function(){var e=a(this);s.handleWidgetAdded(new jQuery.Event("widget-added"),e)}),"complete"===document.readyState?s.setupAccessibleMode():a(window).on("load",function(){s.setupAccessibleMode()}))})},s}(jQuery);
| ver. 1.4 |
Github
|
.
| PHP 8.0.30 | Генерация страницы: 0.01 |
proxy
|
phpinfo
|
Настройка