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';
68 * Capability required to use this control.
70 * Normally this is empty and the capability is derived from the capabilities
71 * of the associated `$settings`.
83 public $priority = 10;
101 public $description = '';
104 * @todo: Remove choices
109 public $choices = array();
115 public $input_attrs = array();
118 * Show UI for adding new content, currently only used for the dropdown-pages control.
124 public $allow_addition = false;
127 * @deprecated It is better to just call the json() method
131 public $json = array();
137 public $type = 'text';
145 * @see WP_Customize_Control::active()
147 * @var callable Callback is called with one argument, the instance of
148 * WP_Customize_Control, and returns bool to indicate whether
149 * the control is active (such as it relates to the URL
150 * currently being previewed).
152 public $active_callback = '';
157 * Supplied `$args` override class property defaults.
159 * If `$args['settings']` is not defined, use the $id as the setting ID.
163 * @param WP_Customize_Manager $manager Customizer bootstrap instance.
164 * @param string $id Control ID.
165 * @param array $args {
166 * Optional. Arguments to override class property defaults.
168 * @type int $instance_number Order in which this instance was created in relation
169 * to other instances.
170 * @type WP_Customize_Manager $manager Customizer bootstrap instance.
171 * @type string $id Control ID.
172 * @type array $settings All settings tied to the control. If undefined, `$id` will
174 * @type string $setting The primary setting for the control (if there is one).
176 * @type int $priority Order priority to load the control. Default 10.
177 * @type string $section Section the control belongs to. Default empty.
178 * @type string $label Label for the control. Default empty.
179 * @type string $description Description for the control. Default empty.
180 * @type array $choices List of choices for 'radio' or 'select' type controls, where
181 * values are the keys, and labels are the values.
182 * Default empty array.
183 * @type array $input_attrs List of custom input attributes for control output, where
184 * attribute names are the keys and values are the values. Not
185 * used for 'checkbox', 'radio', 'select', 'textarea', or
186 * 'dropdown-pages' control types. Default empty array.
187 * @type array $json Deprecated. Use WP_Customize_Control::json() instead.
188 * @type string $type Control type. Core controls include 'text', 'checkbox',
189 * 'textarea', 'radio', 'select', and 'dropdown-pages'. Additional
190 * input types such as 'email', 'url', 'number', 'hidden', and
191 * 'date' are supported implicitly. Default 'text'.
194 public function __construct( $manager, $id, $args = array() ) {
195 $keys = array_keys( get_object_vars( $this ) );
196 foreach ( $keys as $key ) {
197 if ( isset( $args[ $key ] ) ) {
198 $this->$key = $args[ $key ];
202 $this->manager = $manager;
204 if ( empty( $this->active_callback ) ) {
205 $this->active_callback = array( $this, 'active_callback' );
207 self::$instance_count += 1;
208 $this->instance_number = self::$instance_count;
211 if ( ! isset( $this->settings ) ) {
212 $this->settings = $id;
216 if ( is_array( $this->settings ) ) {
217 foreach ( $this->settings as $key => $setting ) {
218 $settings[ $key ] = $this->manager->get_setting( $setting );
220 } else if ( is_string( $this->settings ) ) {
221 $this->setting = $this->manager->get_setting( $this->settings );
222 $settings['default'] = $this->setting;
224 $this->settings = $settings;
228 * Enqueue control related scripts/styles.
232 public function enqueue() {}
235 * Check whether control is active to current Customizer preview.
240 * @return bool Whether the control is active to the current preview.
242 final public function active() {
244 $active = call_user_func( $this->active_callback, $this );
247 * Filters response of WP_Customize_Control::active().
251 * @param bool $active Whether the Customizer control is active.
252 * @param WP_Customize_Control $control WP_Customize_Control instance.
254 $active = apply_filters( 'customize_control_active', $active, $control );
260 * Default callback used when invoking WP_Customize_Control::active().
262 * Subclasses can override this with their specific logic, or they may
263 * provide an 'active_callback' argument to the constructor.
268 * @return true Always true.
270 public function active_callback() {
275 * Fetch a setting's value.
276 * Grabs the main setting by default.
280 * @param string $setting_key
281 * @return mixed The requested setting's value, if the setting exists.
283 final public function value( $setting_key = 'default' ) {
284 if ( isset( $this->settings[ $setting_key ] ) ) {
285 return $this->settings[ $setting_key ]->value();
290 * Refresh the parameters passed to the JavaScript via JSON.
294 public function to_json() {
295 $this->json['settings'] = array();
296 foreach ( $this->settings as $key => $setting ) {
297 $this->json['settings'][ $key ] = $setting->id;
300 $this->json['type'] = $this->type;
301 $this->json['priority'] = $this->priority;
302 $this->json['active'] = $this->active();
303 $this->json['section'] = $this->section;
304 $this->json['content'] = $this->get_content();
305 $this->json['label'] = $this->label;
306 $this->json['description'] = $this->description;
307 $this->json['instanceNumber'] = $this->instance_number;
309 if ( 'dropdown-pages' === $this->type ) {
310 $this->json['allow_addition'] = $this->allow_addition;
315 * Get the data to export to the client via JSON.
319 * @return array Array of parameters passed to the JavaScript.
321 public function json() {
327 * Checks if the user can use this control.
329 * Returns false if the user cannot manipulate one of the associated settings,
330 * or if one of the associated settings does not exist. Also returns false if
331 * the associated section does not exist or if its capability check returns
336 * @return bool False if theme doesn't support the control or user doesn't have the required permissions, otherwise true.
338 final public function check_capabilities() {
339 if ( ! empty( $this->capability ) && ! current_user_can( $this->capability ) ) {
343 foreach ( $this->settings as $setting ) {
344 if ( ! $setting || ! $setting->check_capabilities() ) {
349 $section = $this->manager->get_section( $this->section );
350 if ( isset( $section ) && ! $section->check_capabilities() ) {
358 * Get the control's content for insertion into the Customizer pane.
362 * @return string Contents of the control.
364 final public function get_content() {
366 $this->maybe_render();
367 return trim( ob_get_clean() );
371 * Check capabilities and render the control.
374 * @uses WP_Customize_Control::render()
376 final public function maybe_render() {
377 if ( ! $this->check_capabilities() )
381 * Fires just before the current Customizer control is rendered.
385 * @param WP_Customize_Control $this WP_Customize_Control instance.
387 do_action( 'customize_render_control', $this );
390 * Fires just before a specific Customizer control is rendered.
392 * The dynamic portion of the hook name, `$this->id`, refers to
397 * @param WP_Customize_Control $this WP_Customize_Control instance.
399 do_action( "customize_render_control_{$this->id}", $this );
405 * Renders the control wrapper and calls $this->render_content() for the internals.
409 protected function render() {
410 $id = 'customize-control-' . str_replace( array( '[', ']' ), array( '-', '' ), $this->id );
411 $class = 'customize-control customize-control-' . $this->type;
413 ?><li id="<?php echo esc_attr( $id ); ?>" class="<?php echo esc_attr( $class ); ?>">
414 <?php $this->render_content(); ?>
419 * Get the data link attribute for a setting.
423 * @param string $setting_key
424 * @return string Data link parameter, if $setting_key is a valid setting, empty string otherwise.
426 public function get_link( $setting_key = 'default' ) {
427 if ( ! isset( $this->settings[ $setting_key ] ) )
430 return 'data-customize-setting-link="' . esc_attr( $this->settings[ $setting_key ]->id ) . '"';
434 * Render the data link attribute for the control's input element.
437 * @uses WP_Customize_Control::get_link()
439 * @param string $setting_key
441 public function link( $setting_key = 'default' ) {
442 echo $this->get_link( $setting_key );
446 * Render the custom attributes for the control's input element.
451 public function input_attrs() {
452 foreach ( $this->input_attrs as $attr => $value ) {
453 echo $attr . '="' . esc_attr( $value ) . '" ';
458 * Render the control's content.
460 * Allows the content to be overridden without having to rewrite the wrapper in `$this::render()`.
462 * Supports basic input types `text`, `checkbox`, `textarea`, `radio`, `select` and `dropdown-pages`.
463 * Additional input types such as `email`, `url`, `number`, `hidden` and `date` are supported implicitly.
465 * Control content can alternately be rendered in JS. See WP_Customize_Control::print_template().
469 protected function render_content() {
470 switch( $this->type ) {
474 <input type="checkbox" value="<?php echo esc_attr( $this->value() ); ?>" <?php $this->link(); checked( $this->value() ); ?> />
475 <?php echo esc_html( $this->label ); ?>
476 <?php if ( ! empty( $this->description ) ) : ?>
477 <span class="description customize-control-description"><?php echo $this->description; ?></span>
483 if ( empty( $this->choices ) )
486 $name = '_customize-radio-' . $this->id;
488 if ( ! empty( $this->label ) ) : ?>
489 <span class="customize-control-title"><?php echo esc_html( $this->label ); ?></span>
491 if ( ! empty( $this->description ) ) : ?>
492 <span class="description customize-control-description"><?php echo $this->description ; ?></span>
495 foreach ( $this->choices as $value => $label ) :
498 <input type="radio" value="<?php echo esc_attr( $value ); ?>" name="<?php echo esc_attr( $name ); ?>" <?php $this->link(); checked( $this->value(), $value ); ?> />
499 <?php echo esc_html( $label ); ?><br/>
505 if ( empty( $this->choices ) )
510 <?php if ( ! empty( $this->label ) ) : ?>
511 <span class="customize-control-title"><?php echo esc_html( $this->label ); ?></span>
513 if ( ! empty( $this->description ) ) : ?>
514 <span class="description customize-control-description"><?php echo $this->description; ?></span>
517 <select <?php $this->link(); ?>>
519 foreach ( $this->choices as $value => $label )
520 echo '<option value="' . esc_attr( $value ) . '"' . selected( $this->value(), $value, false ) . '>' . $label . '</option>';
529 <?php if ( ! empty( $this->label ) ) : ?>
530 <span class="customize-control-title"><?php echo esc_html( $this->label ); ?></span>
532 if ( ! empty( $this->description ) ) : ?>
533 <span class="description customize-control-description"><?php echo $this->description; ?></span>
535 <textarea rows="5" <?php $this->input_attrs(); ?> <?php $this->link(); ?>><?php echo esc_textarea( $this->value() ); ?></textarea>
539 case 'dropdown-pages':
542 <?php if ( ! empty( $this->label ) ) : ?>
543 <span class="customize-control-title"><?php echo esc_html( $this->label ); ?></span>
545 if ( ! empty( $this->description ) ) : ?>
546 <span class="description customize-control-description"><?php echo $this->description; ?></span>
550 $dropdown_name = '_customize-dropdown-pages-' . $this->id;
551 $show_option_none = __( '— Select —' );
552 $option_none_value = '0';
553 $dropdown = wp_dropdown_pages(
555 'name' => $dropdown_name,
557 'show_option_none' => $show_option_none,
558 'option_none_value' => $option_none_value,
559 'selected' => $this->value(),
562 if ( empty( $dropdown ) ) {
563 $dropdown = sprintf( '<select id="%1$s" name="%1$s">', esc_attr( $dropdown_name ) );
564 $dropdown .= sprintf( '<option value="%1$s">%2$s</option>', esc_attr( $option_none_value ), esc_html( $show_option_none ) );
565 $dropdown .= '</select>';
568 // Hackily add in the data link parameter.
569 $dropdown = str_replace( '<select', '<select ' . $this->get_link(), $dropdown );
571 // Even more hacikly add auto-draft page stubs.
572 // @todo Eventually this should be removed in favor of the pages being injected into the underlying get_pages() call. See <https://github.com/xwp/wp-customize-posts/pull/250>.
573 $nav_menus_created_posts_setting = $this->manager->get_setting( 'nav_menus_created_posts' );
574 if ( $nav_menus_created_posts_setting && current_user_can( 'publish_pages' ) ) {
575 $auto_draft_page_options = '';
576 foreach ( $nav_menus_created_posts_setting->value() as $auto_draft_page_id ) {
577 $post = get_post( $auto_draft_page_id );
578 if ( $post && 'page' === $post->post_type ) {
579 $auto_draft_page_options .= sprintf( '<option value="%1$s">%2$s</option>', esc_attr( $post->ID ), esc_html( $post->post_title ) );
582 if ( $auto_draft_page_options ) {
583 $dropdown = str_replace( '</select>', $auto_draft_page_options . '</select>', $dropdown );
590 <?php if ( $this->allow_addition && current_user_can( 'publish_pages' ) && current_user_can( 'edit_theme_options' ) ) : // Currently tied to menus functionality. ?>
591 <button type="button" class="button-link add-new-toggle"><?php
592 /* translators: %s: add new page label */
593 printf( __( '+ %s' ), get_post_type_object( 'page' )->labels->add_new_item );
595 <div class="new-content-item">
596 <label for="create-input-<?php echo $this->id; ?>"><span class="screen-reader-text"><?php _e( 'New page title' ); ?></span></label>
597 <input type="text" id="create-input-<?php echo $this->id; ?>" class="create-item-input" placeholder="<?php esc_attr_e( 'New page title…' ); ?>">
598 <button type="button" class="button add-content"><?php _e( 'Add' ); ?></button>
605 <?php if ( ! empty( $this->label ) ) : ?>
606 <span class="customize-control-title"><?php echo esc_html( $this->label ); ?></span>
608 if ( ! empty( $this->description ) ) : ?>
609 <span class="description customize-control-description"><?php echo $this->description; ?></span>
611 <input type="<?php echo esc_attr( $this->type ); ?>" <?php $this->input_attrs(); ?> value="<?php echo esc_attr( $this->value() ); ?>" <?php $this->link(); ?> />
619 * Render the control's JS template.
621 * This function is only run for control types that have been registered with
622 * WP_Customize_Manager::register_control_type().
624 * In the future, this will also print the template for the control's container
625 * element and be override-able.
629 final public function print_template() {
631 <script type="text/html" id="tmpl-customize-control-<?php echo $this->type; ?>-content">
632 <?php $this->content_template(); ?>
638 * An Underscore (JS) template for this control's content (but not its container).
640 * Class variables for this control class are available in the `data` JS object;
641 * export custom variables by overriding WP_Customize_Control::to_json().
643 * @see WP_Customize_Control::print_template()
647 protected function content_template() {}
651 /** WP_Customize_Color_Control class */
652 require_once( ABSPATH . WPINC . '/customize/class-wp-customize-color-control.php' );
654 /** WP_Customize_Media_Control class */
655 require_once( ABSPATH . WPINC . '/customize/class-wp-customize-media-control.php' );
657 /** WP_Customize_Upload_Control class */
658 require_once( ABSPATH . WPINC . '/customize/class-wp-customize-upload-control.php' );
660 /** WP_Customize_Image_Control class */
661 require_once( ABSPATH . WPINC . '/customize/class-wp-customize-image-control.php' );
663 /** WP_Customize_Background_Image_Control class */
664 require_once( ABSPATH . WPINC . '/customize/class-wp-customize-background-image-control.php' );
666 /** WP_Customize_Background_Position_Control class */
667 require_once( ABSPATH . WPINC . '/customize/class-wp-customize-background-position-control.php' );
669 /** WP_Customize_Cropped_Image_Control class */
670 require_once( ABSPATH . WPINC . '/customize/class-wp-customize-cropped-image-control.php' );
672 /** WP_Customize_Site_Icon_Control class */
673 require_once( ABSPATH . WPINC . '/customize/class-wp-customize-site-icon-control.php' );
675 /** WP_Customize_Header_Image_Control class */
676 require_once( ABSPATH . WPINC . '/customize/class-wp-customize-header-image-control.php' );
678 /** WP_Customize_Theme_Control class */
679 require_once( ABSPATH . WPINC . '/customize/class-wp-customize-theme-control.php' );
681 /** WP_Widget_Area_Customize_Control class */
682 require_once( ABSPATH . WPINC . '/customize/class-wp-widget-area-customize-control.php' );
684 /** WP_Widget_Form_Customize_Control class */
685 require_once( ABSPATH . WPINC . '/customize/class-wp-widget-form-customize-control.php' );
687 /** WP_Customize_Nav_Menu_Control class */
688 require_once( ABSPATH . WPINC . '/customize/class-wp-customize-nav-menu-control.php' );
690 /** WP_Customize_Nav_Menu_Item_Control class */
691 require_once( ABSPATH . WPINC . '/customize/class-wp-customize-nav-menu-item-control.php' );
693 /** WP_Customize_Nav_Menu_Location_Control class */
694 require_once( ABSPATH . WPINC . '/customize/class-wp-customize-nav-menu-location-control.php' );
696 /** WP_Customize_Nav_Menu_Name_Control class */
697 require_once( ABSPATH . WPINC . '/customize/class-wp-customize-nav-menu-name-control.php' );
699 /** WP_Customize_Nav_Menu_Auto_Add_Control class */
700 require_once( ABSPATH . WPINC . '/customize/class-wp-customize-nav-menu-auto-add-control.php' );
702 /** WP_Customize_New_Menu_Control class */
703 require_once( ABSPATH . WPINC . '/customize/class-wp-customize-new-menu-control.php' );