WordPress 4.7
[autoinstalls/wordpress.git] / wp-includes / class-wp-customize-control.php
1 <?php
2 /**
3  * WordPress Customize Control classes
4  *
5  * @package WordPress
6  * @subpackage Customize
7  * @since 3.4.0
8  */
9
10 /**
11  * Customize Control class.
12  *
13  * @since 3.4.0
14  */
15 class WP_Customize_Control {
16
17         /**
18          * Incremented with each new class instantiation, then stored in $instance_number.
19          *
20          * Used when sorting two instances whose priorities are equal.
21          *
22          * @since 4.1.0
23          *
24          * @static
25          * @access protected
26          * @var int
27          */
28         protected static $instance_count = 0;
29
30         /**
31          * Order in which this instance was created in relation to other instances.
32          *
33          * @since 4.1.0
34          * @access public
35          * @var int
36          */
37         public $instance_number;
38
39         /**
40          * @access public
41          * @var WP_Customize_Manager
42          */
43         public $manager;
44
45         /**
46          * @access public
47          * @var string
48          */
49         public $id;
50
51         /**
52          * All settings tied to the control.
53          *
54          * @access public
55          * @var array
56          */
57         public $settings;
58
59         /**
60          * The primary setting for the control (if there is one).
61          *
62          * @access public
63          * @var string
64          */
65         public $setting = 'default';
66
67         /**
68          * Capability required to use this control.
69          *
70          * Normally this is empty and the capability is derived from the capabilities
71          * of the associated `$settings`.
72          *
73          * @since 4.5.0
74          * @access public
75          * @var string
76          */
77         public $capability;
78
79         /**
80          * @access public
81          * @var int
82          */
83         public $priority = 10;
84
85         /**
86          * @access public
87          * @var string
88          */
89         public $section = '';
90
91         /**
92          * @access public
93          * @var string
94          */
95         public $label = '';
96
97         /**
98          * @access public
99          * @var string
100          */
101         public $description = '';
102
103         /**
104          * @todo: Remove choices
105          *
106          * @access public
107          * @var array
108          */
109         public $choices = array();
110
111         /**
112          * @access public
113          * @var array
114          */
115         public $input_attrs = array();
116
117         /**
118          * Show UI for adding new content, currently only used for the dropdown-pages control.
119          *
120          * @since 4.7.0
121          * @access public
122          * @var bool
123          */
124         public $allow_addition = false;
125
126         /**
127          * @deprecated It is better to just call the json() method
128          * @access public
129          * @var array
130          */
131         public $json = array();
132
133         /**
134          * @access public
135          * @var string
136          */
137         public $type = 'text';
138
139         /**
140          * Callback.
141          *
142          * @since 4.0.0
143          * @access public
144          *
145          * @see WP_Customize_Control::active()
146          *
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).
151          */
152         public $active_callback = '';
153
154         /**
155          * Constructor.
156          *
157          * Supplied `$args` override class property defaults.
158          *
159          * If `$args['settings']` is not defined, use the $id as the setting ID.
160          *
161          * @since 3.4.0
162          *
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.
167          *
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
173          *                                                 be used.
174          *     @type string               $setting         The primary setting for the control (if there is one).
175          *                                                 Default 'default'.
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'.
192          * }
193          */
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 ];
199                         }
200                 }
201
202                 $this->manager = $manager;
203                 $this->id = $id;
204                 if ( empty( $this->active_callback ) ) {
205                         $this->active_callback = array( $this, 'active_callback' );
206                 }
207                 self::$instance_count += 1;
208                 $this->instance_number = self::$instance_count;
209
210                 // Process settings.
211                 if ( ! isset( $this->settings ) ) {
212                         $this->settings = $id;
213                 }
214
215                 $settings = array();
216                 if ( is_array( $this->settings ) ) {
217                         foreach ( $this->settings as $key => $setting ) {
218                                 $settings[ $key ] = $this->manager->get_setting( $setting );
219                         }
220                 } else if ( is_string( $this->settings ) ) {
221                         $this->setting = $this->manager->get_setting( $this->settings );
222                         $settings['default'] = $this->setting;
223                 }
224                 $this->settings = $settings;
225         }
226
227         /**
228          * Enqueue control related scripts/styles.
229          *
230          * @since 3.4.0
231          */
232         public function enqueue() {}
233
234         /**
235          * Check whether control is active to current Customizer preview.
236          *
237          * @since 4.0.0
238          * @access public
239          *
240          * @return bool Whether the control is active to the current preview.
241          */
242         final public function active() {
243                 $control = $this;
244                 $active = call_user_func( $this->active_callback, $this );
245
246                 /**
247                  * Filters response of WP_Customize_Control::active().
248                  *
249                  * @since 4.0.0
250                  *
251                  * @param bool                 $active  Whether the Customizer control is active.
252                  * @param WP_Customize_Control $control WP_Customize_Control instance.
253                  */
254                 $active = apply_filters( 'customize_control_active', $active, $control );
255
256                 return $active;
257         }
258
259         /**
260          * Default callback used when invoking WP_Customize_Control::active().
261          *
262          * Subclasses can override this with their specific logic, or they may
263          * provide an 'active_callback' argument to the constructor.
264          *
265          * @since 4.0.0
266          * @access public
267          *
268          * @return true Always true.
269          */
270         public function active_callback() {
271                 return true;
272         }
273
274         /**
275          * Fetch a setting's value.
276          * Grabs the main setting by default.
277          *
278          * @since 3.4.0
279          *
280          * @param string $setting_key
281          * @return mixed The requested setting's value, if the setting exists.
282          */
283         final public function value( $setting_key = 'default' ) {
284                 if ( isset( $this->settings[ $setting_key ] ) ) {
285                         return $this->settings[ $setting_key ]->value();
286                 }
287         }
288
289         /**
290          * Refresh the parameters passed to the JavaScript via JSON.
291          *
292          * @since 3.4.0
293          */
294         public function to_json() {
295                 $this->json['settings'] = array();
296                 foreach ( $this->settings as $key => $setting ) {
297                         $this->json['settings'][ $key ] = $setting->id;
298                 }
299
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;
308
309                 if ( 'dropdown-pages' === $this->type ) {
310                         $this->json['allow_addition'] = $this->allow_addition;
311                 }
312         }
313
314         /**
315          * Get the data to export to the client via JSON.
316          *
317          * @since 4.1.0
318          *
319          * @return array Array of parameters passed to the JavaScript.
320          */
321         public function json() {
322                 $this->to_json();
323                 return $this->json;
324         }
325
326         /**
327          * Checks if the user can use this control.
328          *
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
332          * false.
333          *
334          * @since 3.4.0
335          *
336          * @return bool False if theme doesn't support the control or user doesn't have the required permissions, otherwise true.
337          */
338         final public function check_capabilities() {
339                 if ( ! empty( $this->capability ) && ! current_user_can( $this->capability ) ) {
340                         return false;
341                 }
342
343                 foreach ( $this->settings as $setting ) {
344                         if ( ! $setting || ! $setting->check_capabilities() ) {
345                                 return false;
346                         }
347                 }
348
349                 $section = $this->manager->get_section( $this->section );
350                 if ( isset( $section ) && ! $section->check_capabilities() ) {
351                         return false;
352                 }
353
354                 return true;
355         }
356
357         /**
358          * Get the control's content for insertion into the Customizer pane.
359          *
360          * @since 4.1.0
361          *
362          * @return string Contents of the control.
363          */
364         final public function get_content() {
365                 ob_start();
366                 $this->maybe_render();
367                 return trim( ob_get_clean() );
368         }
369
370         /**
371          * Check capabilities and render the control.
372          *
373          * @since 3.4.0
374          * @uses WP_Customize_Control::render()
375          */
376         final public function maybe_render() {
377                 if ( ! $this->check_capabilities() )
378                         return;
379
380                 /**
381                  * Fires just before the current Customizer control is rendered.
382                  *
383                  * @since 3.4.0
384                  *
385                  * @param WP_Customize_Control $this WP_Customize_Control instance.
386                  */
387                 do_action( 'customize_render_control', $this );
388
389                 /**
390                  * Fires just before a specific Customizer control is rendered.
391                  *
392                  * The dynamic portion of the hook name, `$this->id`, refers to
393                  * the control ID.
394                  *
395                  * @since 3.4.0
396                  *
397                  * @param WP_Customize_Control $this WP_Customize_Control instance.
398                  */
399                 do_action( "customize_render_control_{$this->id}", $this );
400
401                 $this->render();
402         }
403
404         /**
405          * Renders the control wrapper and calls $this->render_content() for the internals.
406          *
407          * @since 3.4.0
408          */
409         protected function render() {
410                 $id    = 'customize-control-' . str_replace( array( '[', ']' ), array( '-', '' ), $this->id );
411                 $class = 'customize-control customize-control-' . $this->type;
412
413                 ?><li id="<?php echo esc_attr( $id ); ?>" class="<?php echo esc_attr( $class ); ?>">
414                         <?php $this->render_content(); ?>
415                 </li><?php
416         }
417
418         /**
419          * Get the data link attribute for a setting.
420          *
421          * @since 3.4.0
422          *
423          * @param string $setting_key
424          * @return string Data link parameter, if $setting_key is a valid setting, empty string otherwise.
425          */
426         public function get_link( $setting_key = 'default' ) {
427                 if ( ! isset( $this->settings[ $setting_key ] ) )
428                         return '';
429
430                 return 'data-customize-setting-link="' . esc_attr( $this->settings[ $setting_key ]->id ) . '"';
431         }
432
433         /**
434          * Render the data link attribute for the control's input element.
435          *
436          * @since 3.4.0
437          * @uses WP_Customize_Control::get_link()
438          *
439          * @param string $setting_key
440          */
441         public function link( $setting_key = 'default' ) {
442                 echo $this->get_link( $setting_key );
443         }
444
445         /**
446          * Render the custom attributes for the control's input element.
447          *
448          * @since 4.0.0
449          * @access public
450          */
451         public function input_attrs() {
452                 foreach ( $this->input_attrs as $attr => $value ) {
453                         echo $attr . '="' . esc_attr( $value ) . '" ';
454                 }
455         }
456
457         /**
458          * Render the control's content.
459          *
460          * Allows the content to be overridden without having to rewrite the wrapper in `$this::render()`.
461          *
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.
464          *
465          * Control content can alternately be rendered in JS. See WP_Customize_Control::print_template().
466          *
467          * @since 3.4.0
468          */
469         protected function render_content() {
470                 switch( $this->type ) {
471                         case 'checkbox':
472                                 ?>
473                                 <label>
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>
478                                         <?php endif; ?>
479                                 </label>
480                                 <?php
481                                 break;
482                         case 'radio':
483                                 if ( empty( $this->choices ) )
484                                         return;
485
486                                 $name = '_customize-radio-' . $this->id;
487
488                                 if ( ! empty( $this->label ) ) : ?>
489                                         <span class="customize-control-title"><?php echo esc_html( $this->label ); ?></span>
490                                 <?php endif;
491                                 if ( ! empty( $this->description ) ) : ?>
492                                         <span class="description customize-control-description"><?php echo $this->description ; ?></span>
493                                 <?php endif;
494
495                                 foreach ( $this->choices as $value => $label ) :
496                                         ?>
497                                         <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/>
500                                         </label>
501                                         <?php
502                                 endforeach;
503                                 break;
504                         case 'select':
505                                 if ( empty( $this->choices ) )
506                                         return;
507
508                                 ?>
509                                 <label>
510                                         <?php if ( ! empty( $this->label ) ) : ?>
511                                                 <span class="customize-control-title"><?php echo esc_html( $this->label ); ?></span>
512                                         <?php endif;
513                                         if ( ! empty( $this->description ) ) : ?>
514                                                 <span class="description customize-control-description"><?php echo $this->description; ?></span>
515                                         <?php endif; ?>
516
517                                         <select <?php $this->link(); ?>>
518                                                 <?php
519                                                 foreach ( $this->choices as $value => $label )
520                                                         echo '<option value="' . esc_attr( $value ) . '"' . selected( $this->value(), $value, false ) . '>' . $label . '</option>';
521                                                 ?>
522                                         </select>
523                                 </label>
524                                 <?php
525                                 break;
526                         case 'textarea':
527                                 ?>
528                                 <label>
529                                         <?php if ( ! empty( $this->label ) ) : ?>
530                                                 <span class="customize-control-title"><?php echo esc_html( $this->label ); ?></span>
531                                         <?php endif;
532                                         if ( ! empty( $this->description ) ) : ?>
533                                                 <span class="description customize-control-description"><?php echo $this->description; ?></span>
534                                         <?php endif; ?>
535                                         <textarea rows="5" <?php $this->input_attrs(); ?> <?php $this->link(); ?>><?php echo esc_textarea( $this->value() ); ?></textarea>
536                                 </label>
537                                 <?php
538                                 break;
539                         case 'dropdown-pages':
540                                 ?>
541                                 <label>
542                                 <?php if ( ! empty( $this->label ) ) : ?>
543                                         <span class="customize-control-title"><?php echo esc_html( $this->label ); ?></span>
544                                 <?php endif;
545                                 if ( ! empty( $this->description ) ) : ?>
546                                         <span class="description customize-control-description"><?php echo $this->description; ?></span>
547                                 <?php endif; ?>
548
549                                 <?php
550                                 $dropdown_name = '_customize-dropdown-pages-' . $this->id;
551                                 $show_option_none = __( '&mdash; Select &mdash;' );
552                                 $option_none_value = '0';
553                                 $dropdown = wp_dropdown_pages(
554                                         array(
555                                                 'name'              => $dropdown_name,
556                                                 'echo'              => 0,
557                                                 'show_option_none'  => $show_option_none,
558                                                 'option_none_value' => $option_none_value,
559                                                 'selected'          => $this->value(),
560                                         )
561                                 );
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>';
566                                 }
567
568                                 // Hackily add in the data link parameter.
569                                 $dropdown = str_replace( '<select', '<select ' . $this->get_link(), $dropdown );
570
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 ) );
580                                                 }
581                                         }
582                                         if ( $auto_draft_page_options ) {
583                                                 $dropdown = str_replace( '</select>', $auto_draft_page_options . '</select>', $dropdown );
584                                         }
585                                 }
586
587                                 echo $dropdown;
588                                 ?>
589                                 </label>
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 );
594                                         ?></button>
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&hellip;' ); ?>">
598                                                 <button type="button" class="button add-content"><?php _e( 'Add' ); ?></button>
599                                         </div>
600                                 <?php endif;
601                                 break;
602                         default:
603                                 ?>
604                                 <label>
605                                         <?php if ( ! empty( $this->label ) ) : ?>
606                                                 <span class="customize-control-title"><?php echo esc_html( $this->label ); ?></span>
607                                         <?php endif;
608                                         if ( ! empty( $this->description ) ) : ?>
609                                                 <span class="description customize-control-description"><?php echo $this->description; ?></span>
610                                         <?php endif; ?>
611                                         <input type="<?php echo esc_attr( $this->type ); ?>" <?php $this->input_attrs(); ?> value="<?php echo esc_attr( $this->value() ); ?>" <?php $this->link(); ?> />
612                                 </label>
613                                 <?php
614                                 break;
615                 }
616         }
617
618         /**
619          * Render the control's JS template.
620          *
621          * This function is only run for control types that have been registered with
622          * WP_Customize_Manager::register_control_type().
623          *
624          * In the future, this will also print the template for the control's container
625          * element and be override-able.
626          *
627          * @since 4.1.0
628          */
629         final public function print_template() {
630                 ?>
631                 <script type="text/html" id="tmpl-customize-control-<?php echo $this->type; ?>-content">
632                         <?php $this->content_template(); ?>
633                 </script>
634                 <?php
635         }
636
637         /**
638          * An Underscore (JS) template for this control's content (but not its container).
639          *
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().
642          *
643          * @see WP_Customize_Control::print_template()
644          *
645          * @since 4.1.0
646          */
647         protected function content_template() {}
648
649 }
650
651 /** WP_Customize_Color_Control class */
652 require_once( ABSPATH . WPINC . '/customize/class-wp-customize-color-control.php' );
653
654 /** WP_Customize_Media_Control class */
655 require_once( ABSPATH . WPINC . '/customize/class-wp-customize-media-control.php' );
656
657 /** WP_Customize_Upload_Control class */
658 require_once( ABSPATH . WPINC . '/customize/class-wp-customize-upload-control.php' );
659
660 /** WP_Customize_Image_Control class */
661 require_once( ABSPATH . WPINC . '/customize/class-wp-customize-image-control.php' );
662
663 /** WP_Customize_Background_Image_Control class */
664 require_once( ABSPATH . WPINC . '/customize/class-wp-customize-background-image-control.php' );
665
666 /** WP_Customize_Background_Position_Control class */
667 require_once( ABSPATH . WPINC . '/customize/class-wp-customize-background-position-control.php' );
668
669 /** WP_Customize_Cropped_Image_Control class */
670 require_once( ABSPATH . WPINC . '/customize/class-wp-customize-cropped-image-control.php' );
671
672 /** WP_Customize_Site_Icon_Control class */
673 require_once( ABSPATH . WPINC . '/customize/class-wp-customize-site-icon-control.php' );
674
675 /** WP_Customize_Header_Image_Control class */
676 require_once( ABSPATH . WPINC . '/customize/class-wp-customize-header-image-control.php' );
677
678 /** WP_Customize_Theme_Control class */
679 require_once( ABSPATH . WPINC . '/customize/class-wp-customize-theme-control.php' );
680
681 /** WP_Widget_Area_Customize_Control class */
682 require_once( ABSPATH . WPINC . '/customize/class-wp-widget-area-customize-control.php' );
683
684 /** WP_Widget_Form_Customize_Control class */
685 require_once( ABSPATH . WPINC . '/customize/class-wp-widget-form-customize-control.php' );
686
687 /** WP_Customize_Nav_Menu_Control class */
688 require_once( ABSPATH . WPINC . '/customize/class-wp-customize-nav-menu-control.php' );
689
690 /** WP_Customize_Nav_Menu_Item_Control class */
691 require_once( ABSPATH . WPINC . '/customize/class-wp-customize-nav-menu-item-control.php' );
692
693 /** WP_Customize_Nav_Menu_Location_Control class */
694 require_once( ABSPATH . WPINC . '/customize/class-wp-customize-nav-menu-location-control.php' );
695
696 /** WP_Customize_Nav_Menu_Name_Control class */
697 require_once( ABSPATH . WPINC . '/customize/class-wp-customize-nav-menu-name-control.php' );
698
699 /** WP_Customize_Nav_Menu_Auto_Add_Control class */
700 require_once( ABSPATH . WPINC . '/customize/class-wp-customize-nav-menu-auto-add-control.php' );
701
702 /** WP_Customize_New_Menu_Control class */
703 require_once( ABSPATH . WPINC . '/customize/class-wp-customize-new-menu-control.php' );