3 * WordPress Customize Control classes
6 * @subpackage Customize
11 * Customize Control class.
15 class WP_Customize_Control {
18 * Incremented with each new class instantiation, then stored in $instance_number.
20 * Used when sorting two instances whose priorities are equal.
28 protected static $instance_count = 0;
31 * Order in which this instance was created in relation to other instances.
37 public $instance_number;
41 * @var WP_Customize_Manager
52 * All settings tied to the control.
60 * The primary setting for the control (if there is one).
65 public $setting = 'default';
71 public $priority = 10;
89 public $description = '';
92 * @todo: Remove choices
97 public $choices = array();
103 public $input_attrs = array();
106 * @deprecated It is better to just call the json() method
110 public $json = array();
116 public $type = 'text';
124 * @see WP_Customize_Control::active()
126 * @var callable Callback is called with one argument, the instance of
127 * WP_Customize_Control, and returns bool to indicate whether
128 * the control is active (such as it relates to the URL
129 * currently being previewed).
131 public $active_callback = '';
136 * Supplied $args override class property defaults.
138 * If $args['settings'] is not defined, use the $id as the setting ID.
142 * @param WP_Customize_Manager $manager Customizer bootstrap instance.
143 * @param string $id Control ID.
144 * @param array $args Optional. Arguments to override class property defaults.
146 public function __construct( $manager, $id, $args = array() ) {
147 $keys = array_keys( get_object_vars( $this ) );
148 foreach ( $keys as $key ) {
149 if ( isset( $args[ $key ] ) ) {
150 $this->$key = $args[ $key ];
154 $this->manager = $manager;
156 if ( empty( $this->active_callback ) ) {
157 $this->active_callback = array( $this, 'active_callback' );
159 self::$instance_count += 1;
160 $this->instance_number = self::$instance_count;
163 if ( empty( $this->settings ) ) {
164 $this->settings = $id;
168 if ( is_array( $this->settings ) ) {
169 foreach ( $this->settings as $key => $setting ) {
170 $settings[ $key ] = $this->manager->get_setting( $setting );
173 $this->setting = $this->manager->get_setting( $this->settings );
174 $settings['default'] = $this->setting;
176 $this->settings = $settings;
180 * Enqueue control related scripts/styles.
184 public function enqueue() {}
187 * Check whether control is active to current Customizer preview.
192 * @return bool Whether the control is active to the current preview.
194 final public function active() {
196 $active = call_user_func( $this->active_callback, $this );
199 * Filter response of WP_Customize_Control::active().
203 * @param bool $active Whether the Customizer control is active.
204 * @param WP_Customize_Control $control WP_Customize_Control instance.
206 $active = apply_filters( 'customize_control_active', $active, $control );
212 * Default callback used when invoking WP_Customize_Control::active().
214 * Subclasses can override this with their specific logic, or they may
215 * provide an 'active_callback' argument to the constructor.
220 * @return true Always true.
222 public function active_callback() {
227 * Fetch a setting's value.
228 * Grabs the main setting by default.
232 * @param string $setting_key
233 * @return mixed The requested setting's value, if the setting exists.
235 final public function value( $setting_key = 'default' ) {
236 if ( isset( $this->settings[ $setting_key ] ) ) {
237 return $this->settings[ $setting_key ]->value();
242 * Refresh the parameters passed to the JavaScript via JSON.
246 public function to_json() {
247 $this->json['settings'] = array();
248 foreach ( $this->settings as $key => $setting ) {
249 $this->json['settings'][ $key ] = $setting->id;
252 $this->json['type'] = $this->type;
253 $this->json['priority'] = $this->priority;
254 $this->json['active'] = $this->active();
255 $this->json['section'] = $this->section;
256 $this->json['content'] = $this->get_content();
257 $this->json['label'] = $this->label;
258 $this->json['description'] = $this->description;
259 $this->json['instanceNumber'] = $this->instance_number;
263 * Get the data to export to the client via JSON.
267 * @return array Array of parameters passed to the JavaScript.
269 public function json() {
275 * Check if the theme supports the control and check user capabilities.
279 * @return bool False if theme doesn't support the control or user doesn't have the required permissions, otherwise true.
281 final public function check_capabilities() {
282 foreach ( $this->settings as $setting ) {
283 if ( ! $setting->check_capabilities() )
287 $section = $this->manager->get_section( $this->section );
288 if ( isset( $section ) && ! $section->check_capabilities() )
295 * Get the control's content for insertion into the Customizer pane.
299 * @return string Contents of the control.
301 final public function get_content() {
303 $this->maybe_render();
304 return trim( ob_get_clean() );
308 * Check capabilities and render the control.
311 * @uses WP_Customize_Control::render()
313 final public function maybe_render() {
314 if ( ! $this->check_capabilities() )
318 * Fires just before the current Customizer control is rendered.
322 * @param WP_Customize_Control $this WP_Customize_Control instance.
324 do_action( 'customize_render_control', $this );
327 * Fires just before a specific Customizer control is rendered.
329 * The dynamic portion of the hook name, `$this->id`, refers to
334 * @param WP_Customize_Control $this {@see WP_Customize_Control} instance.
336 do_action( 'customize_render_control_' . $this->id, $this );
342 * Renders the control wrapper and calls $this->render_content() for the internals.
346 protected function render() {
347 $id = 'customize-control-' . str_replace( '[', '-', str_replace( ']', '', $this->id ) );
348 $class = 'customize-control customize-control-' . $this->type;
350 ?><li id="<?php echo esc_attr( $id ); ?>" class="<?php echo esc_attr( $class ); ?>">
351 <?php $this->render_content(); ?>
356 * Get the data link attribute for a setting.
360 * @param string $setting_key
361 * @return string Data link parameter, if $setting_key is a valid setting, empty string otherwise.
363 public function get_link( $setting_key = 'default' ) {
364 if ( ! isset( $this->settings[ $setting_key ] ) )
367 return 'data-customize-setting-link="' . esc_attr( $this->settings[ $setting_key ]->id ) . '"';
371 * Render the data link attribute for the control's input element.
374 * @uses WP_Customize_Control::get_link()
376 * @param string $setting_key
378 public function link( $setting_key = 'default' ) {
379 echo $this->get_link( $setting_key );
383 * Render the custom attributes for the control's input element.
388 public function input_attrs() {
389 foreach( $this->input_attrs as $attr => $value ) {
390 echo $attr . '="' . esc_attr( $value ) . '" ';
395 * Render the control's content.
397 * Allows the content to be overriden without having to rewrite the wrapper in $this->render().
399 * Supports basic input types `text`, `checkbox`, `textarea`, `radio`, `select` and `dropdown-pages`.
400 * Additional input types such as `email`, `url`, `number`, `hidden` and `date` are supported implicitly.
402 * Control content can alternately be rendered in JS. See {@see WP_Customize_Control::print_template()}.
406 protected function render_content() {
407 switch( $this->type ) {
411 <input type="checkbox" value="<?php echo esc_attr( $this->value() ); ?>" <?php $this->link(); checked( $this->value() ); ?> />
412 <?php echo esc_html( $this->label ); ?>
413 <?php if ( ! empty( $this->description ) ) : ?>
414 <span class="description customize-control-description"><?php echo $this->description; ?></span>
420 if ( empty( $this->choices ) )
423 $name = '_customize-radio-' . $this->id;
425 if ( ! empty( $this->label ) ) : ?>
426 <span class="customize-control-title"><?php echo esc_html( $this->label ); ?></span>
428 if ( ! empty( $this->description ) ) : ?>
429 <span class="description customize-control-description"><?php echo $this->description ; ?></span>
432 foreach ( $this->choices as $value => $label ) :
435 <input type="radio" value="<?php echo esc_attr( $value ); ?>" name="<?php echo esc_attr( $name ); ?>" <?php $this->link(); checked( $this->value(), $value ); ?> />
436 <?php echo esc_html( $label ); ?><br/>
442 if ( empty( $this->choices ) )
447 <?php if ( ! empty( $this->label ) ) : ?>
448 <span class="customize-control-title"><?php echo esc_html( $this->label ); ?></span>
450 if ( ! empty( $this->description ) ) : ?>
451 <span class="description customize-control-description"><?php echo $this->description; ?></span>
454 <select <?php $this->link(); ?>>
456 foreach ( $this->choices as $value => $label )
457 echo '<option value="' . esc_attr( $value ) . '"' . selected( $this->value(), $value, false ) . '>' . $label . '</option>';
466 <?php if ( ! empty( $this->label ) ) : ?>
467 <span class="customize-control-title"><?php echo esc_html( $this->label ); ?></span>
469 if ( ! empty( $this->description ) ) : ?>
470 <span class="description customize-control-description"><?php echo $this->description; ?></span>
472 <textarea rows="5" <?php $this->link(); ?>><?php echo esc_textarea( $this->value() ); ?></textarea>
476 case 'dropdown-pages':
477 $dropdown = wp_dropdown_pages(
479 'name' => '_customize-dropdown-pages-' . $this->id,
481 'show_option_none' => __( '— Select —' ),
482 'option_none_value' => '0',
483 'selected' => $this->value(),
487 // Hackily add in the data link parameter.
488 $dropdown = str_replace( '<select', '<select ' . $this->get_link(), $dropdown );
491 '<label class="customize-control-select"><span class="customize-control-title">%s</span> %s</label>',
499 <?php if ( ! empty( $this->label ) ) : ?>
500 <span class="customize-control-title"><?php echo esc_html( $this->label ); ?></span>
502 if ( ! empty( $this->description ) ) : ?>
503 <span class="description customize-control-description"><?php echo $this->description; ?></span>
505 <input type="<?php echo esc_attr( $this->type ); ?>" <?php $this->input_attrs(); ?> value="<?php echo esc_attr( $this->value() ); ?>" <?php $this->link(); ?> />
513 * Render the control's JS template.
515 * This function is only run for control types that have been registered with
516 * {@see WP_Customize_Manager::register_control_type()}.
518 * In the future, this will also print the template for the control's container
519 * element and be override-able.
523 final public function print_template() {
525 <script type="text/html" id="tmpl-customize-control-<?php echo $this->type; ?>-content">
526 <?php $this->content_template(); ?>
532 * An Underscore (JS) template for this control's content (but not its container).
534 * Class variables for this control class are available in the `data` JS object;
535 * export custom variables by overriding {@see WP_Customize_Control::to_json()}.
537 * @see WP_Customize_Control::print_template()
541 protected function content_template() {}
546 * Customize Color Control class.
550 * @see WP_Customize_Control
552 class WP_Customize_Color_Control extends WP_Customize_Control {
557 public $type = 'color';
569 * @uses WP_Customize_Control::__construct()
571 * @param WP_Customize_Manager $manager Customizer bootstrap instance.
572 * @param string $id Control ID.
573 * @param array $args Optional. Arguments to override class property defaults.
575 public function __construct( $manager, $id, $args = array() ) {
576 $this->statuses = array( '' => __('Default') );
577 parent::__construct( $manager, $id, $args );
581 * Enqueue scripts/styles for the color picker.
585 public function enqueue() {
586 wp_enqueue_script( 'wp-color-picker' );
587 wp_enqueue_style( 'wp-color-picker' );
591 * Refresh the parameters passed to the JavaScript via JSON.
594 * @uses WP_Customize_Control::to_json()
596 public function to_json() {
598 $this->json['statuses'] = $this->statuses;
599 $this->json['defaultValue'] = $this->setting->default;
603 * Don't render the control content from PHP, as it's rendered via JS on load.
607 public function render_content() {}
610 * Render a JS template for the content of the color picker control.
614 public function content_template() {
616 <# var defaultValue = '';
617 if ( data.defaultValue ) {
618 if ( '#' !== data.defaultValue.substring( 0, 1 ) ) {
619 defaultValue = '#' + data.defaultValue;
621 defaultValue = data.defaultValue;
623 defaultValue = ' data-default-color=' + defaultValue; // Quotes added automatically.
626 <# if ( data.label ) { #>
627 <span class="customize-control-title">{{{ data.label }}}</span>
629 <# if ( data.description ) { #>
630 <span class="description customize-control-description">{{{ data.description }}}</span>
632 <div class="customize-control-content">
633 <input class="color-picker-hex" type="text" maxlength="7" placeholder="<?php esc_attr_e( 'Hex Value' ); ?>" {{ defaultValue }} />
641 * Customize Media Control class.
645 * @see WP_Customize_Control
647 class WP_Customize_Media_Control extends WP_Customize_Control {
655 public $type = 'media';
658 * Media control mime type.
664 public $mime_type = '';
673 public $button_labels = array();
679 * @since 4.2.0 Moved from WP_Customize_Upload_Control.
681 * @param WP_Customize_Manager $manager Customizer bootstrap instance.
682 * @param string $id Control ID.
683 * @param array $args Optional. Arguments to override class property defaults.
685 public function __construct( $manager, $id, $args = array() ) {
686 parent::__construct( $manager, $id, $args );
688 $this->button_labels = array(
689 'select' => __( 'Select File' ),
690 'change' => __( 'Change File' ),
691 'default' => __( 'Default' ),
692 'remove' => __( 'Remove' ),
693 'placeholder' => __( 'No file selected' ),
694 'frame_title' => __( 'Select File' ),
695 'frame_button' => __( 'Choose File' ),
700 * Enqueue control related scripts/styles.
703 * @since 4.2.0 Moved from WP_Customize_Upload_Control.
705 public function enqueue() {
710 * Refresh the parameters passed to the JavaScript via JSON.
713 * @since 4.2.0 Moved from WP_Customize_Upload_Control.
715 * @see WP_Customize_Control::to_json()
717 public function to_json() {
719 $this->json['label'] = html_entity_decode( $this->label, ENT_QUOTES, get_bloginfo( 'charset' ) );
720 $this->json['mime_type'] = $this->mime_type;
721 $this->json['button_labels'] = $this->button_labels;
722 $this->json['canUpload'] = current_user_can( 'upload_files' );
724 $value = $this->value();
726 if ( is_object( $this->setting ) ) {
727 if ( $this->setting->default ) {
728 // Fake an attachment model - needs all fields used by template.
729 // Note that the default value must be a URL, NOT an attachment ID.
730 $type = in_array( substr( $this->setting->default, -3 ), array( 'jpg', 'png', 'gif', 'bmp' ) ) ? 'image' : 'document';
731 $default_attachment = array(
733 'url' => $this->setting->default,
735 'icon' => wp_mime_type_icon( $type ),
736 'title' => basename( $this->setting->default ),
739 if ( 'image' === $type ) {
740 $default_attachment['sizes'] = array(
741 'full' => array( 'url' => $this->setting->default ),
745 $this->json['defaultAttachment'] = $default_attachment;
748 if ( $value && $this->setting->default && $value === $this->setting->default ) {
749 // Set the default as the attachment.
750 $this->json['attachment'] = $this->json['defaultAttachment'];
751 } elseif ( $value ) {
752 $this->json['attachment'] = wp_prepare_attachment_for_js( $value );
758 * Don't render any content for this control from PHP.
761 * @since 4.2.0 Moved from WP_Customize_Upload_Control.
763 * @see WP_Customize_Media_Control::content_template()
765 public function render_content() {}
768 * Render a JS template for the content of the media control.
771 * @since 4.2.0 Moved from WP_Customize_Upload_Control.
773 public function content_template() {
775 <label for="{{ data.settings['default'] }}-button">
776 <# if ( data.label ) { #>
777 <span class="customize-control-title">{{ data.label }}</span>
779 <# if ( data.description ) { #>
780 <span class="description customize-control-description">{{{ data.description }}}</span>
784 <# if ( data.attachment && data.attachment.id ) { #>
785 <div class="current">
786 <div class="container">
787 <div class="attachment-media-view attachment-media-view-{{ data.attachment.type }} {{ data.attachment.orientation }}">
788 <div class="thumbnail thumbnail-{{ data.attachment.type }}">
789 <# if ( 'image' === data.attachment.type && data.attachment.sizes && data.attachment.sizes.medium ) { #>
790 <img class="attachment-thumb" src="{{ data.attachment.sizes.medium.url }}" draggable="false" />
791 <# } else if ( 'image' === data.attachment.type && data.attachment.sizes && data.attachment.sizes.full ) { #>
792 <img class="attachment-thumb" src="{{ data.attachment.sizes.full.url }}" draggable="false" />
793 <# } else if ( 'audio' === data.attachment.type ) { #>
794 <# if ( data.attachment.image && data.attachment.image.src && data.attachment.image.src !== data.attachment.icon ) { #>
795 <img src="{{ data.attachment.image.src }}" class="thumbnail" draggable="false" />
797 <img src="{{ data.attachment.icon }}" class="attachment-thumb type-icon" draggable="false" />
799 <p class="attachment-meta attachment-meta-title">“{{ data.attachment.title }}”</p>
800 <# if ( data.attachment.album || data.attachment.meta.album ) { #>
801 <p class="attachment-meta"><em>{{ data.attachment.album || data.attachment.meta.album }}</em></p>
803 <# if ( data.attachment.artist || data.attachment.meta.artist ) { #>
804 <p class="attachment-meta">{{ data.attachment.artist || data.attachment.meta.artist }}</p>
806 <audio style="visibility: hidden" controls class="wp-audio-shortcode" width="100%" preload="none">
807 <source type="{{ data.attachment.mime }}" src="{{ data.attachment.url }}"/>
809 <# } else if ( 'video' === data.attachment.type ) { #>
810 <div class="wp-media-wrapper wp-video">
811 <video controls="controls" class="wp-video-shortcode" preload="metadata"
812 <# if ( data.attachment.image && data.attachment.image.src !== data.attachment.icon ) { #>poster="{{ data.attachment.image.src }}"<# } #>>
813 <source type="{{ data.attachment.mime }}" src="{{ data.attachment.url }}"/>
817 <img class="attachment-thumb type-icon icon" src="{{ data.attachment.icon }}" draggable="false" />
818 <p class="attachment-title">{{ data.attachment.title }}</p>
824 <div class="actions">
825 <# if ( data.canUpload ) { #>
826 <button type="button" class="button remove-button"><?php echo $this->button_labels['remove']; ?></button>
827 <button type="button" class="button upload-button" id="{{ data.settings['default'] }}-button"><?php echo $this->button_labels['change']; ?></button>
828 <div style="clear:both"></div>
832 <div class="current">
833 <div class="container">
834 <div class="placeholder">
837 <?php echo $this->button_labels['placeholder']; ?>
843 <div class="actions">
844 <# if ( data.defaultAttachment ) { #>
845 <button type="button" class="button default-button"><?php echo $this->button_labels['default']; ?></button>
847 <# if ( data.canUpload ) { #>
848 <button type="button" class="button upload-button" id="{{ data.settings['default'] }}-button"><?php echo $this->button_labels['select']; ?></button>
850 <div style="clear:both"></div>
858 * Customize Upload Control Class.
862 * @see WP_Customize_Media_Control
864 class WP_Customize_Upload_Control extends WP_Customize_Media_Control {
865 public $type = 'upload';
866 public $mime_type = '';
867 public $button_labels = array();
868 public $removed = ''; // unused
869 public $context; // unused
870 public $extensions = array(); // unused
873 * Refresh the parameters passed to the JavaScript via JSON.
877 * @uses WP_Customize_Media_Control::to_json()
879 public function to_json() {
882 $value = $this->value();
884 // Get the attachment model for the existing file.
885 $attachment_id = attachment_url_to_postid( $value );
886 if ( $attachment_id ) {
887 $this->json['attachment'] = wp_prepare_attachment_for_js( $attachment_id );
894 * Customize Image Control class.
898 * @see WP_Customize_Upload_Control
900 class WP_Customize_Image_Control extends WP_Customize_Upload_Control {
901 public $type = 'image';
902 public $mime_type = 'image';
908 * @uses WP_Customize_Upload_Control::__construct()
910 * @param WP_Customize_Manager $manager Customizer bootstrap instance.
911 * @param string $id Control ID.
912 * @param array $args Optional. Arguments to override class property defaults.
914 public function __construct( $manager, $id, $args = array() ) {
915 parent::__construct( $manager, $id, $args );
917 $this->button_labels = array(
918 'select' => __( 'Select Image' ),
919 'change' => __( 'Change Image' ),
920 'remove' => __( 'Remove' ),
921 'default' => __( 'Default' ),
922 'placeholder' => __( 'No image selected' ),
923 'frame_title' => __( 'Select Image' ),
924 'frame_button' => __( 'Choose Image' ),
932 public function prepare_control() {}
939 * @param string $label
940 * @param mixed $callback
942 public function add_tab( $id, $label, $callback ) {}
950 public function remove_tab( $id ) {}
957 * @param string $thumbnail_url
959 public function print_tab_image( $url, $thumbnail_url = null ) {}
963 * Customize Background Image Control class.
967 * @see WP_Customize_Image_Control
969 class WP_Customize_Background_Image_Control extends WP_Customize_Image_Control {
970 public $type = 'background';
976 * @uses WP_Customize_Image_Control::__construct()
978 * @param WP_Customize_Manager $manager Customizer bootstrap instance.
980 public function __construct( $manager ) {
981 parent::__construct( $manager, 'background_image', array(
982 'label' => __( 'Background Image' ),
983 'section' => 'background_image',
988 * Enqueue control related scripts/styles.
992 public function enqueue() {
995 wp_localize_script( 'customize-controls', '_wpCustomizeBackground', array(
997 'add' => wp_create_nonce( 'background-add' ),
1004 * Customize Cropped Image Control class.
1008 * @see WP_Customize_Media_Control
1010 class WP_Customize_Cropped_Image_Control extends WP_Customize_Media_Control {
1019 public $type = 'cropped_image';
1022 * Suggested width for cropped image.
1028 public $width = 150;
1031 * Suggested height for cropped image.
1037 public $height = 150;
1040 * Whether the width is flexible.
1046 public $flex_width = false;
1049 * Whether the height is flexible.
1055 public $flex_height = false;
1058 * Enqueue control related scripts/styles.
1063 public function enqueue() {
1064 wp_enqueue_script( 'customize-views' );
1070 * Refresh the parameters passed to the JavaScript via JSON.
1075 * @see WP_Customize_Control::to_json()
1077 public function to_json() {
1080 $this->json['width'] = absint( $this->width );
1081 $this->json['height'] = absint( $this->height );
1082 $this->json['flex_width'] = absint( $this->flex_width );
1083 $this->json['flex_height'] = absint( $this->flex_height );
1089 * Customize Site Icon control class.
1091 * Used only for custom functionality in JavaScript.
1095 * @see WP_Customize_Cropped_Image_Control
1097 class WP_Customize_Site_Icon_Control extends WP_Customize_Cropped_Image_Control {
1106 public $type = 'site_icon';
1114 * @param WP_Customize_Manager $manager Customizer bootstrap instance.
1115 * @param string $id Control ID.
1116 * @param array $args Optional. Arguments to override class property defaults.
1118 public function __construct( $manager, $id, $args = array() ) {
1119 parent::__construct( $manager, $id, $args );
1120 add_action( 'customize_controls_print_styles', 'wp_site_icon', 99 );
1125 * Customize Header Image Control class.
1129 * @see WP_Customize_Image_Control
1131 class WP_Customize_Header_Image_Control extends WP_Customize_Image_Control {
1132 public $type = 'header';
1133 public $uploaded_headers;
1134 public $default_headers;
1141 * @param WP_Customize_Manager $manager Customizer bootstrap instance.
1143 public function __construct( $manager ) {
1144 parent::__construct( $manager, 'header_image', array(
1145 'label' => __( 'Header Image' ),
1146 'settings' => array(
1147 'default' => 'header_image',
1148 'data' => 'header_image_data',
1150 'section' => 'header_image',
1151 'removed' => 'remove-header',
1152 'get_url' => 'get_header_image',
1160 public function enqueue() {
1162 wp_enqueue_script( 'customize-views' );
1164 $this->prepare_control();
1166 wp_localize_script( 'customize-views', '_wpCustomizeHeader', array(
1168 'width' => absint( get_theme_support( 'custom-header', 'width' ) ),
1169 'height' => absint( get_theme_support( 'custom-header', 'height' ) ),
1170 'flex-width' => absint( get_theme_support( 'custom-header', 'flex-width' ) ),
1171 'flex-height' => absint( get_theme_support( 'custom-header', 'flex-height' ) ),
1172 'currentImgSrc' => $this->get_current_image_src(),
1175 'add' => wp_create_nonce( 'header-add' ),
1176 'remove' => wp_create_nonce( 'header-remove' ),
1178 'uploads' => $this->uploaded_headers,
1179 'defaults' => $this->default_headers
1187 * @global Custom_Image_Header $custom_image_header
1189 public function prepare_control() {
1190 global $custom_image_header;
1191 if ( empty( $custom_image_header ) ) {
1195 // Process default headers and uploaded headers.
1196 $custom_image_header->process_default_headers();
1197 $this->default_headers = $custom_image_header->get_default_header_images();
1198 $this->uploaded_headers = $custom_image_header->get_uploaded_header_images();
1204 public function print_header_image_template() {
1206 <script type="text/template" id="tmpl-header-choice">
1207 <# if (data.random) { #>
1208 <button type="button" class="button display-options random">
1209 <span class="dashicons dashicons-randomize dice"></span>
1210 <# if ( data.type === 'uploaded' ) { #>
1211 <?php _e( 'Randomize uploaded headers' ); ?>
1212 <# } else if ( data.type === 'default' ) { #>
1213 <?php _e( 'Randomize suggested headers' ); ?>
1219 <# if (data.type === 'uploaded') { #>
1220 <button type="button" class="dashicons dashicons-no close"><span class="screen-reader-text"><?php _e( 'Remove image' ); ?></span></button>
1223 <button type="button" class="choice thumbnail"
1224 data-customize-image-value="{{{data.header.url}}}"
1225 data-customize-header-image-data="{{JSON.stringify(data.header)}}">
1226 <span class="screen-reader-text"><?php _e( 'Set image' ); ?></span>
1227 <img src="{{{data.header.thumbnail_url}}}" alt="{{{data.header.alt_text || data.header.description}}}">
1233 <script type="text/template" id="tmpl-header-current">
1234 <# if (data.choice) { #>
1235 <# if (data.random) { #>
1237 <div class="placeholder">
1239 <span><span class="dashicons dashicons-randomize dice"></span>
1240 <# if ( data.type === 'uploaded' ) { #>
1241 <?php _e( 'Randomizing uploaded headers' ); ?>
1242 <# } else if ( data.type === 'default' ) { #>
1243 <?php _e( 'Randomizing suggested headers' ); ?>
1251 <img src="{{{data.header.thumbnail_url}}}" alt="{{{data.header.alt_text || data.header.description}}}" tabindex="0"/>
1256 <div class="placeholder">
1259 <?php _e( 'No image set' ); ?>
1270 * @return string|void
1272 public function get_current_image_src() {
1273 $src = $this->value();
1274 if ( isset( $this->get_url ) ) {
1275 $src = call_user_func( $this->get_url, $src );
1283 public function render_content() {
1284 $this->print_header_image_template();
1285 $visibility = $this->get_current_image_src() ? '' : ' style="display:none" ';
1286 $width = absint( get_theme_support( 'custom-header', 'width' ) );
1287 $height = absint( get_theme_support( 'custom-header', 'height' ) );
1289 <div class="customize-control-content">
1290 <p class="customizer-section-intro">
1292 if ( $width && $height ) {
1293 printf( __( 'While you can crop images to your liking after clicking <strong>Add new image</strong>, your theme recommends a header size of <strong>%s × %s</strong> pixels.' ), $width, $height );
1294 } elseif ( $width ) {
1295 printf( __( 'While you can crop images to your liking after clicking <strong>Add new image</strong>, your theme recommends a header width of <strong>%s</strong> pixels.' ), $width );
1297 printf( __( 'While you can crop images to your liking after clicking <strong>Add new image</strong>, your theme recommends a header height of <strong>%s</strong> pixels.' ), $height );
1301 <div class="current">
1302 <span class="customize-control-title">
1303 <?php _e( 'Current header' ); ?>
1305 <div class="container">
1308 <div class="actions">
1309 <?php if ( current_user_can( 'upload_files' ) ): ?>
1310 <button type="button"<?php echo $visibility; ?> class="button remove" aria-label="<?php esc_attr_e( 'Hide header image' ); ?>"><?php _e( 'Hide image' ); ?></button>
1311 <button type="button" class="button new" aria-label="<?php esc_attr_e( 'Add new header image' ); ?>"><?php _e( 'Add new image' ); ?></button>
1312 <div style="clear:both"></div>
1315 <div class="choices">
1316 <span class="customize-control-title header-previously-uploaded">
1317 <?php _ex( 'Previously uploaded', 'custom headers' ); ?>
1319 <div class="uploaded">
1323 <span class="customize-control-title header-default">
1324 <?php _ex( 'Suggested', 'custom headers' ); ?>
1326 <div class="default">
1337 * Customize Theme Control class.
1341 * @see WP_Customize_Control
1343 class WP_Customize_Theme_Control extends WP_Customize_Control {
1346 * Customize control type.
1352 public $type = 'theme';
1364 * Refresh the parameters passed to the JavaScript via JSON.
1369 * @see WP_Customize_Control::to_json()
1371 public function to_json() {
1373 $this->json['theme'] = $this->theme;
1377 * Don't render the control content from PHP, as it's rendered via JS on load.
1382 public function render_content() {}
1385 * Render a JS template for theme display.
1390 public function content_template() {
1391 $current_url = set_url_scheme( 'http://' . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI'] );
1392 $active_url = esc_url( remove_query_arg( 'theme', $current_url ) );
1393 $preview_url = esc_url( add_query_arg( 'theme', '__THEME__', $current_url ) ); // Token because esc_url() strips curly braces.
1394 $preview_url = str_replace( '__THEME__', '{{ data.theme.id }}', $preview_url );
1396 <# if ( data.theme.isActiveTheme ) { #>
1397 <div class="theme active" tabindex="0" data-preview-url="<?php echo esc_attr( $active_url ); ?>" aria-describedby="{{ data.theme.id }}-action {{ data.theme.id }}-name">
1399 <div class="theme" tabindex="0" data-preview-url="<?php echo esc_attr( $preview_url ); ?>" aria-describedby="{{ data.theme.id }}-action {{ data.theme.id }}-name">
1402 <# if ( data.theme.screenshot[0] ) { #>
1403 <div class="theme-screenshot">
1404 <img data-src="{{ data.theme.screenshot[0] }}" alt="" />
1407 <div class="theme-screenshot blank"></div>
1410 <# if ( data.theme.isActiveTheme ) { #>
1411 <span class="more-details" id="{{ data.theme.id }}-action"><?php _e( 'Customize' ); ?></span>
1413 <span class="more-details" id="{{ data.theme.id }}-action"><?php _e( 'Live Preview' ); ?></span>
1416 <div class="theme-author"><?php printf( __( 'By %s' ), '{{ data.theme.author }}' ); ?></div>
1418 <# if ( data.theme.isActiveTheme ) { #>
1419 <h3 class="theme-name" id="{{ data.theme.id }}-name">
1421 /* translators: %s: theme name */
1422 printf( __( '<span>Active:</span> %s' ), '{{{ data.theme.name }}}' );
1426 <h3 class="theme-name" id="{{ data.theme.id }}-name">{{{ data.theme.name }}}</h3>
1427 <div class="theme-actions">
1428 <button type="button" class="button theme-details"><?php _e( 'Theme Details' ); ?></button>
1437 * Widget Area Customize Control class.
1441 * @see WP_Customize_Control
1443 class WP_Widget_Area_Customize_Control extends WP_Customize_Control {
1444 public $type = 'sidebar_widgets';
1447 public function to_json() {
1449 $exported_properties = array( 'sidebar_id' );
1450 foreach ( $exported_properties as $key ) {
1451 $this->json[ $key ] = $this->$key;
1458 public function render_content() {
1460 <span class="button-secondary add-new-widget" tabindex="0">
1461 <?php _e( 'Add a Widget' ); ?>
1464 <span class="reorder-toggle" tabindex="0">
1465 <span class="reorder"><?php _ex( 'Reorder', 'Reorder widgets in Customizer' ); ?></span>
1466 <span class="reorder-done"><?php _ex( 'Done', 'Cancel reordering widgets in Customizer' ); ?></span>
1474 * Widget Form Customize Control class.
1478 * @see WP_Customize_Control
1480 class WP_Widget_Form_Customize_Control extends WP_Customize_Control {
1481 public $type = 'widget_form';
1483 public $widget_id_base;
1485 public $is_new = false;
1488 public $is_wide = false;
1490 public function to_json() {
1492 $exported_properties = array( 'widget_id', 'widget_id_base', 'sidebar_id', 'width', 'height', 'is_wide' );
1493 foreach ( $exported_properties as $key ) {
1494 $this->json[ $key ] = $this->$key;
1500 * @global array $wp_registered_widgets
1502 public function render_content() {
1503 global $wp_registered_widgets;
1504 require_once ABSPATH . '/wp-admin/includes/widgets.php';
1506 $widget = $wp_registered_widgets[ $this->widget_id ];
1507 if ( ! isset( $widget['params'][0] ) ) {
1508 $widget['params'][0] = array();
1512 'widget_id' => $widget['id'],
1513 'widget_name' => $widget['name'],
1516 $args = wp_list_widget_controls_dynamic_sidebar( array( 0 => $args, 1 => $widget['params'][0] ) );
1517 echo $this->manager->widgets->get_widget_control( $args );
1521 * Whether the current widget is rendered on the page.
1526 * @return bool Whether the widget is rendered.
1528 public function active_callback() {
1529 return $this->manager->widgets->is_widget_rendered( $this->widget_id );
1534 * Customize Nav Menu Control Class.
1538 class WP_Customize_Nav_Menu_Control extends WP_Customize_Control {
1547 public $type = 'nav_menu';
1550 * The nav menu setting.
1554 * @var WP_Customize_Nav_Menu_Setting
1559 * Don't render the control's content - it uses a JS template instead.
1564 public function render_content() {}
1567 * JS/Underscore template for the control UI.
1572 public function content_template() {
1574 <button type="button" class="button-secondary add-new-menu-item" aria-label="<?php esc_attr_e( 'Add or remove menu items' ); ?>" aria-expanded="false" aria-controls="available-menu-items">
1575 <?php _e( 'Add Items' ); ?>
1577 <button type="button" class="not-a-button reorder-toggle" aria-label="<?php esc_attr_e( 'Reorder menu items' ); ?>" aria-describedby="reorder-items-desc-{{ data.menu_id }}">
1578 <span class="reorder"><?php _ex( 'Reorder', 'Reorder menu items in Customizer' ); ?></span>
1579 <span class="reorder-done"><?php _ex( 'Done', 'Cancel reordering menu items in Customizer' ); ?></span>
1581 <p class="screen-reader-text" id="reorder-items-desc-{{ data.menu_id }}"><?php _e( 'When in reorder mode, additional controls to reorder menu items will be available in the items list above.' ); ?></p>
1582 <span class="add-menu-item-loading spinner"></span>
1583 <span class="menu-delete-item">
1584 <button type="button" class="not-a-button menu-delete">
1585 <?php _e( 'Delete menu' ); ?> <span class="screen-reader-text">{{ data.menu_name }}</span>
1588 <?php if ( current_theme_supports( 'menus' ) ) : ?>
1589 <ul class="menu-settings">
1590 <li class="customize-control">
1591 <span class="customize-control-title"><?php _e( 'Menu locations' ); ?></span>
1594 <?php foreach ( get_registered_nav_menus() as $location => $description ) : ?>
1595 <li class="customize-control customize-control-checkbox assigned-menu-location">
1597 <input type="checkbox" data-menu-id="{{ data.menu_id }}" data-location-id="<?php echo esc_attr( $location ); ?>" class="menu-location" /> <?php echo $description; ?>
1598 <span class="theme-location-set"><?php printf( _x( '(Current: %s)', 'Current menu location' ), '<span class="current-menu-location-name-' . esc_attr( $location ) . '"></span>' ); ?></span>
1601 <?php endforeach; ?>
1608 * Return parameters for this control.
1613 * @return array Exported parameters.
1615 public function json() {
1616 $exported = parent::json();
1617 $exported['menu_id'] = $this->setting->term_id;
1624 * Customize control to represent the name field for a given menu.
1628 class WP_Customize_Nav_Menu_Item_Control extends WP_Customize_Control {
1637 public $type = 'nav_menu_item';
1640 * The nav menu item setting.
1644 * @var WP_Customize_Nav_Menu_Item_Setting
1654 * @see WP_Customize_Control::__construct()
1656 * @param WP_Customize_Manager $manager Customizer bootstrap instance.
1657 * @param string $id The control ID.
1658 * @param array $args Optional. Overrides class property defaults.
1660 public function __construct( $manager, $id, $args = array() ) {
1661 parent::__construct( $manager, $id, $args );
1665 * Don't render the control's content - it's rendered with a JS template.
1670 public function render_content() {}
1673 * JS/Underscore template for the control UI.
1678 public function content_template() {
1680 <div class="menu-item-bar">
1681 <div class="menu-item-handle">
1682 <span class="item-type" aria-hidden="true">{{ data.item_type_label }}</span>
1683 <span class="item-title" aria-hidden="true">
1684 <span class="spinner"></span>
1685 <span class="menu-item-title<# if ( ! data.title ) { #> no-title<# } #>">{{ data.title || wp.customize.Menus.data.l10n.untitled }}</span>
1687 <span class="item-controls">
1688 <button type="button" class="not-a-button item-edit" aria-expanded="false"><span class="screen-reader-text"><?php
1689 /* translators: 1: Title of a menu item, 2: Type of a menu item */
1690 printf( __( 'Edit menu item: %1$s (%2$s)' ), '{{ data.title || wp.customize.Menus.data.l10n.untitled }}', '{{ data.item_type_label }}' );
1691 ?></span><span class="toggle-indicator" aria-hidden="true"></span></button>
1692 <button type="button" class="not-a-button item-delete submitdelete deletion"><span class="screen-reader-text"><?php
1693 /* translators: 1: Title of a menu item, 2: Type of a menu item */
1694 printf( __( 'Remove Menu Item: %1$s (%2$s)' ), '{{ data.title || wp.customize.Menus.data.l10n.untitled }}', '{{ data.item_type_label }}' );
1700 <div class="menu-item-settings" id="menu-item-settings-{{ data.menu_item_id }}">
1701 <# if ( 'custom' === data.item_type ) { #>
1702 <p class="field-url description description-thin">
1703 <label for="edit-menu-item-url-{{ data.menu_item_id }}">
1704 <?php _e( 'URL' ); ?><br />
1705 <input class="widefat code edit-menu-item-url" type="text" id="edit-menu-item-url-{{ data.menu_item_id }}" name="menu-item-url" />
1709 <p class="description description-thin">
1710 <label for="edit-menu-item-title-{{ data.menu_item_id }}">
1711 <?php _e( 'Navigation Label' ); ?><br />
1712 <input type="text" id="edit-menu-item-title-{{ data.menu_item_id }}" class="widefat edit-menu-item-title" name="menu-item-title" />
1715 <p class="field-link-target description description-thin">
1716 <label for="edit-menu-item-target-{{ data.menu_item_id }}">
1717 <input type="checkbox" id="edit-menu-item-target-{{ data.menu_item_id }}" class="edit-menu-item-target" value="_blank" name="menu-item-target" />
1718 <?php _e( 'Open link in a new tab' ); ?>
1721 <p class="field-attr-title description description-thin">
1722 <label for="edit-menu-item-attr-title-{{ data.menu_item_id }}">
1723 <?php _e( 'Title Attribute' ); ?><br />
1724 <input type="text" id="edit-menu-item-attr-title-{{ data.menu_item_id }}" class="widefat edit-menu-item-attr-title" name="menu-item-attr-title" />
1727 <p class="field-css-classes description description-thin">
1728 <label for="edit-menu-item-classes-{{ data.menu_item_id }}">
1729 <?php _e( 'CSS Classes' ); ?><br />
1730 <input type="text" id="edit-menu-item-classes-{{ data.menu_item_id }}" class="widefat code edit-menu-item-classes" name="menu-item-classes" />
1733 <p class="field-xfn description description-thin">
1734 <label for="edit-menu-item-xfn-{{ data.menu_item_id }}">
1735 <?php _e( 'Link Relationship (XFN)' ); ?><br />
1736 <input type="text" id="edit-menu-item-xfn-{{ data.menu_item_id }}" class="widefat code edit-menu-item-xfn" name="menu-item-xfn" />
1739 <p class="field-description description description-thin">
1740 <label for="edit-menu-item-description-{{ data.menu_item_id }}">
1741 <?php _e( 'Description' ); ?><br />
1742 <textarea id="edit-menu-item-description-{{ data.menu_item_id }}" class="widefat edit-menu-item-description" rows="3" cols="20" name="menu-item-description">{{ data.description }}</textarea>
1743 <span class="description"><?php _e( 'The description will be displayed in the menu if the current theme supports it.' ); ?></span>
1747 <div class="menu-item-actions description-thin submitbox">
1748 <# if ( ( 'post_type' === data.item_type || 'taxonomy' === data.item_type ) && '' !== data.original_title ) { #>
1749 <p class="link-to-original">
1750 <?php printf( __( 'Original: %s' ), '<a class="original-link" href="{{ data.url }}">{{ data.original_title }}</a>' ); ?>
1754 <button type="button" class="not-a-button item-delete submitdelete deletion"><?php _e( 'Remove' ); ?></button>
1755 <span class="spinner"></span>
1757 <input type="hidden" name="menu-item-db-id[{{ data.menu_item_id }}]" class="menu-item-data-db-id" value="{{ data.menu_item_id }}" />
1758 <input type="hidden" name="menu-item-parent-id[{{ data.menu_item_id }}]" class="menu-item-data-parent-id" value="{{ data.parent }}" />
1759 </div><!-- .menu-item-settings-->
1760 <ul class="menu-item-transport"></ul>
1765 * Return parameters for this control.
1770 * @return array Exported parameters.
1772 public function json() {
1773 $exported = parent::json();
1774 $exported['menu_item_id'] = $this->setting->post_id;
1781 * Customize Menu Location Control Class.
1783 * This custom control is only needed for JS.
1787 * @see WP_Customize_Control
1789 class WP_Customize_Nav_Menu_Location_Control extends WP_Customize_Control {
1798 public $type = 'nav_menu_location';
1807 public $location_id = '';
1810 * Refresh the parameters passed to JavaScript via JSON.
1815 * @see WP_Customize_Control::to_json()
1817 public function to_json() {
1819 $this->json['locationId'] = $this->location_id;
1823 * Render content just like a normal select control.
1828 public function render_content() {
1829 if ( empty( $this->choices ) ) {
1834 <?php if ( ! empty( $this->label ) ) : ?>
1835 <span class="customize-control-title"><?php echo esc_html( $this->label ); ?></span>
1838 <?php if ( ! empty( $this->description ) ) : ?>
1839 <span class="description customize-control-description"><?php echo $this->description; ?></span>
1842 <select <?php $this->link(); ?>>
1844 foreach ( $this->choices as $value => $label ) :
1845 echo '<option value="' . esc_attr( $value ) . '"' . selected( $this->value(), $value, false ) . '>' . $label . '</option>';
1855 * Customize control to represent the name field for a given menu.
1859 * @see WP_Customize_Control
1861 class WP_Customize_Nav_Menu_Name_Control extends WP_Customize_Control {
1864 * Type of control, used by JS.
1870 public $type = 'nav_menu_name';
1873 * No-op since we're using JS template.
1878 protected function render_content() {}
1881 * Render the Underscore template for this control.
1886 protected function content_template() {
1889 <# if ( data.label ) { #>
1890 <span class="customize-control-title screen-reader-text">{{ data.label }}</span>
1892 <input type="text" class="menu-name-field live-update-section-title" />
1899 * Customize control to represent the auto_add field for a given menu.
1903 * @see WP_Customize_Control
1905 class WP_Customize_Nav_Menu_Auto_Add_Control extends WP_Customize_Control {
1908 * Type of control, used by JS.
1914 public $type = 'nav_menu_auto_add';
1917 * No-op since we're using JS template.
1922 protected function render_content() {}
1925 * Render the Underscore template for this control.
1930 protected function content_template() {
1932 <span class="customize-control-title"><?php _e( 'Menu options' ); ?></span>
1934 <input type="checkbox" class="auto_add" />
1935 <?php _e( 'Automatically add new top-level pages to this menu' ); ?>
1942 * Customize control class for new menus.
1946 * @see WP_Customize_Control
1948 class WP_Customize_New_Menu_Control extends WP_Customize_Control {
1957 public $type = 'new_menu';
1960 * Render the control's content.
1965 public function render_content() {
1967 <button type="button" class="button button-primary" id="create-new-menu-submit"><?php _e( 'Create Menu' ); ?></button>
1968 <span class="spinner"></span>