1<?php
2/**
3 * WordPress Customize Section classes
4 *
5 * @package WordPress
6 * @subpackage Customize
7 * @since 3.4.0
8 */
9
10/**
11 * Customize Section class.
12 *
13 * A UI container for controls, managed by the WP_Customize_Manager class.
14 *
15 * @since 3.4.0
16 *
17 * @see WP_Customize_Manager
18 */
19#[AllowDynamicProperties]
20class WP_Customize_Section {
21
22 /**
23 * Incremented with each new class instantiation, then stored in $instance_number.
24 *
25 * Used when sorting two instances whose priorities are equal.
26 *
27 * @since 4.1.0
28 * @var int
29 */
30 protected static $instance_count = 0;
31
32 /**
33 * Order in which this instance was created in relation to other instances.
34 *
35 * @since 4.1.0
36 * @var int
37 */
38 public $instance_number;
39
40 /**
41 * WP_Customize_Manager instance.
42 *
43 * @since 3.4.0
44 * @var WP_Customize_Manager
45 */
46 public $manager;
47
48 /**
49 * Unique identifier.
50 *
51 * @since 3.4.0
52 * @var string
53 */
54 public $id;
55
56 /**
57 * Priority of the section which informs load order of sections.
58 *
59 * @since 3.4.0
60 * @var int
61 */
62 public $priority = 160;
63
64 /**
65 * Panel in which to show the section, making it a sub-section.
66 *
67 * @since 4.0.0
68 * @var string
69 */
70 public $panel = '';
71
72 /**
73 * Capability required for the section.
74 *
75 * @since 3.4.0
76 * @var string
77 */
78 public $capability = 'edit_theme_options';
79
80 /**
81 * Theme features required to support the section.
82 *
83 * @since 3.4.0
84 * @var string|string[]
85 */
86 public $theme_supports = '';
87
88 /**
89 * Title of the section to show in UI.
90 *
91 * @since 3.4.0
92 * @var string
93 */
94 public $title = '';
95
96 /**
97 * Description to show in the UI.
98 *
99 * @since 3.4.0
100 * @var string
101 */
102 public $description = '';
103
104 /**
105 * Customizer controls for this section.
106 *
107 * @since 3.4.0
108 * @var array
109 */
110 public $controls;
111
112 /**
113 * Type of this section.
114 *
115 * @since 4.1.0
116 * @var string
117 */
118 public $type = 'default';
119
120 /**
121 * Active callback.
122 *
123 * @since 4.1.0
124 *
125 * @see WP_Customize_Section::active()
126 *
127 * @var callable Callback is called with one argument, the instance of
128 * WP_Customize_Section, and returns bool to indicate whether
129 * the section is active (such as it relates to the URL currently
130 * being previewed).
131 */
132 public $active_callback = '';
133
134 /**
135 * Show the description or hide it behind the help icon.
136 *
137 * @since 4.7.0
138 *
139 * @var bool Indicates whether the Section's description should be
140 * hidden behind a help icon ("?") in the Section header,
141 * similar to how help icons are displayed on Panels.
142 */
143 public $description_hidden = false;
144
145 /**
146 * Constructor.
147 *
148 * Any supplied $args override class property defaults.
149 *
150 * @since 3.4.0
151 *
152 * @param WP_Customize_Manager $manager Customizer bootstrap instance.
153 * @param string $id A specific ID of the section.
154 * @param array $args {
155 * Optional. Array of properties for the new Section object. Default empty array.
156 *
157 * @type int $priority Priority of the section, defining the display order
158 * of panels and sections. Default 160.
159 * @type string $panel The panel this section belongs to (if any).
160 * Default empty.
161 * @type string $capability Capability required for the section.
162 * Default 'edit_theme_options'
163 * @type string|string[] $theme_supports Theme features required to support the section.
164 * @type string $title Title of the section to show in UI.
165 * @type string $description Description to show in the UI.
166 * @type string $type Type of the section.
167 * @type callable $active_callback Active callback.
168 * @type bool $description_hidden Hide the description behind a help icon,
169 * instead of inline above the first control.
170 * Default false.
171 * }
172 */
173 public function __construct( $manager, $id, $args = array() ) {
174 $keys = array_keys( get_object_vars( $this ) );
175 foreach ( $keys as $key ) {
176 if ( isset( $args[ $key ] ) ) {
177 $this->$key = $args[ $key ];
178 }
179 }
180
181 $this->manager = $manager;
182 $this->id = $id;
183 if ( empty( $this->active_callback ) ) {
184 $this->active_callback = array( $this, 'active_callback' );
185 }
186 self::$instance_count += 1;
187 $this->instance_number = self::$instance_count;
188
189 $this->controls = array(); // Users cannot customize the $controls array.
190 }
191
192 /**
193 * Check whether section is active to current Customizer preview.
194 *
195 * @since 4.1.0
196 *
197 * @return bool Whether the section is active to the current preview.
198 */
199 final public function active() {
200 $section = $this;
201 $active = call_user_func( $this->active_callback, $this );
202
203 /**
204 * Filters response of WP_Customize_Section::active().
205 *
206 * @since 4.1.0
207 *
208 * @param bool $active Whether the Customizer section is active.
209 * @param WP_Customize_Section $section WP_Customize_Section instance.
210 */
211 $active = apply_filters( 'customize_section_active', $active, $section );
212
213 return $active;
214 }
215
216 /**
217 * Default callback used when invoking WP_Customize_Section::active().
218 *
219 * Subclasses can override this with their specific logic, or they may provide
220 * an 'active_callback' argument to the constructor.
221 *
222 * @since 4.1.0
223 *
224 * @return true Always true.
225 */
226 public function active_callback() {
227 return true;
228 }
229
230 /**
231 * Gather the parameters passed to client JavaScript via JSON.
232 *
233 * @since 4.1.0
234 *
235 * @return array The array to be exported to the client as JSON.
236 */
237 public function json() {
238 $array = wp_array_slice_assoc( (array) $this, array( 'id', 'description', 'priority', 'panel', 'type', 'description_hidden' ) );
239 $array['title'] = html_entity_decode( $this->title, ENT_QUOTES, get_bloginfo( 'charset' ) );
240 $array['content'] = $this->get_content();
241 $array['active'] = $this->active();
242 $array['instanceNumber'] = $this->instance_number;
243
244 if ( $this->panel ) {
245 /* translators: ▸ is the unicode right-pointing triangle. %s: Section title in the Customizer. */
246 $array['customizeAction'] = sprintf( __( 'Customizing ▸ %s' ), esc_html( $this->manager->get_panel( $this->panel )->title ) );
247 } else {
248 $array['customizeAction'] = __( 'Customizing' );
249 }
250
251 return $array;
252 }
253
254 /**
255 * Checks required user capabilities and whether the theme has the
256 * feature support required by the section.
257 *
258 * @since 3.4.0
259 *
260 * @return bool False if theme doesn't support the section or user doesn't have the capability.
261 */
262 final public function check_capabilities() {
263 if ( $this->capability && ! current_user_can( $this->capability ) ) {
264 return false;
265 }
266
267 if ( $this->theme_supports && ! current_theme_supports( ...(array) $this->theme_supports ) ) {
268 return false;
269 }
270
271 return true;
272 }
273
274 /**
275 * Get the section's content for insertion into the Customizer pane.
276 *
277 * @since 4.1.0
278 *
279 * @return string Contents of the section.
280 */
281 final public function get_content() {
282 ob_start();
283 $this->maybe_render();
284 return trim( ob_get_clean() );
285 }
286
287 /**
288 * Check capabilities and render the section.
289 *
290 * @since 3.4.0
291 */
292 final public function maybe_render() {
293 if ( ! $this->check_capabilities() ) {
294 return;
295 }
296
297 /**
298 * Fires before rendering a Customizer section.
299 *
300 * @since 3.4.0
301 *
302 * @param WP_Customize_Section $section WP_Customize_Section instance.
303 */
304 do_action( 'customize_render_section', $this );
305 /**
306 * Fires before rendering a specific Customizer section.
307 *
308 * The dynamic portion of the hook name, `$this->id`, refers to the ID
309 * of the specific Customizer section to be rendered.
310 *
311 * @since 3.4.0
312 */
313 do_action( "customize_render_section_{$this->id}" );
314
315 $this->render();
316 }
317
318 /**
319 * Render the section UI in a subclass.
320 *
321 * Sections are now rendered in JS by default, see WP_Customize_Section::print_template().
322 *
323 * @since 3.4.0
324 */
325 protected function render() {}
326
327 /**
328 * Render the section's JS template.
329 *
330 * This function is only run for section types that have been registered with
331 * WP_Customize_Manager::register_section_type().
332 *
333 * @since 4.3.0
334 *
335 * @see WP_Customize_Manager::render_template()
336 */
337 public function print_template() {
338 ?>
339 <script type="text/html" id="tmpl-customize-section-<?php echo $this->type; ?>">
340 <?php $this->render_template(); ?>
341 </script>
342 <?php
343 }
344
345 /**
346 * An Underscore (JS) template for rendering this section.
347 *
348 * Class variables for this section class are available in the `data` JS object;
349 * export custom variables by overriding WP_Customize_Section::json().
350 *
351 * @since 4.3.0
352 *
353 * @see WP_Customize_Section::print_template()
354 */
355 protected function render_template() {
356 ?>
357 <li id="accordion-section-{{ data.id }}" class="accordion-section control-section control-section-{{ data.type }}">
358 <h3 class="accordion-section-title">
359 <button type="button" class="accordion-trigger" aria-expanded="false" aria-controls="{{ data.id }}-content">
360 {{ data.title }}
361 </button>
362 </h3>
363 <ul class="accordion-section-content" id="{{ data.id }}-content">
364 <li class="customize-section-description-container section-meta <# if ( data.description_hidden ) { #>customize-info<# } #>">
365 <div class="customize-section-title">
366 <button class="customize-section-back" tabindex="-1">
367 <span class="screen-reader-text">
368 <?php
369 /* translators: Hidden accessibility text. */
370 _e( 'Back' );
371 ?>
372 </span>
373 </button>
374 <h3>
375 <span class="customize-action">
376 {{{ data.customizeAction }}}
377 </span>
378 {{ data.title }}
379 </h3>
380 <# if ( data.description && data.description_hidden ) { #>
381 <button type="button" class="customize-help-toggle dashicons dashicons-editor-help" aria-expanded="false"><span class="screen-reader-text">
382 <?php
383 /* translators: Hidden accessibility text. */
384 _e( 'Help' );
385 ?>
386 </span></button>
387 <div class="description customize-section-description">
388 {{{ data.description }}}
389 </div>
390 <# } #>
391
392 <div class="customize-control-notifications-container"></div>
393 </div>
394
395 <# if ( data.description && ! data.description_hidden ) { #>
396 <div class="description customize-section-description">
397 {{{ data.description }}}
398 </div>
399 <# } #>
400 </li>
401 </ul>
402 </li>
403 <?php
404 }
405}
406
407/** WP_Customize_Themes_Section class */
408require_once ABSPATH . WPINC . '/customize/class-wp-customize-themes-section.php';
409
410/** WP_Customize_Sidebar_Section class */
411require_once ABSPATH . WPINC . '/customize/class-wp-customize-sidebar-section.php';
412
413/** WP_Customize_Nav_Menu_Section class */
414require_once ABSPATH . WPINC . '/customize/class-wp-customize-nav-menu-section.php';
415