PluginProbe
Customify / trunk
Customify vtrunk
2.10.9 1.2.3 1.2.4 1.2.5 1.2.6 1.2.7 1.2.7.1 1.3.0 1.3.1 1.4.0 1.4.1 1.4.2 1.5.0 1.5.1 1.5.2 1.5.3 1.5.4 1.5.5 1.5.6 1.5.7 1.6.0 1.6.0.1 1.6.5 1.7.0 1.7.1 All 77 releases
customify / includes / class-customify-customizer.php

class-customify-customizer.php in Customify trunk, at includes/class-customify-customizer.php

1,536 lines 49.7 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 // phpcs:disable WordPress.Security.EscapeOutput.OutputNotEscaped,WordPress.Security.EscapeOutput.ExceptionNotEscaped,WordPress.Security.EscapeOutput.UnsafePrintingFunction -- Legacy Customify view output contains trusted config HTML, dynamic CSS/JS, or WordPress Customizer binding attributes.
3 /**
4 * This is the class that handles the overall logic for the Customizer.
5 *
6 * @see https://pixelgrade.com
7 * @author Pixelgrade
8 * @since 2.4.0
9 */
10
11 if ( ! defined( 'ABSPATH' ) ) {
12 exit; // Exit if accessed directly
13 }
14
15 if ( ! class_exists( 'PixCustomify_Customizer' ) ) :
16
17 class PixCustomify_Customizer {
18
19 /**
20 * Holds the only instance of this class.
21 * @var null|PixCustomify_Customizer
22 * @access protected
23 * @since 2.4.0
24 */
25 protected static $_instance = null;
26
27 protected $localized = array();
28
29 protected $media_queries = array();
30
31 // these properties will get 'px' as a default unit
32 public static $pixel_dependent_css_properties = array(
33 'width',
34 'max-width',
35 'min-width',
36
37 'height',
38 'max-height',
39 'min-height',
40
41 'padding',
42 'padding-left',
43 'padding-right',
44 'padding-top',
45 'padding-bottom',
46
47 'margin',
48 'margin-right',
49 'margin-left',
50 'margin-top',
51 'margin-bottom',
52
53 'right',
54 'left',
55 'top',
56 'bottom',
57
58 'font-size',
59 'letter-spacing',
60
61 'border-size',
62 'border-width',
63 'border-bottom-width',
64 'border-left-width',
65 'border-right-width',
66 'border-top-width'
67 );
68
69 /**
70 * Constructor.
71 *
72 * @since 2.4.0
73 */
74 protected function __construct() {
75 // We will initialize the Customizer logic after the plugin has finished with it's configuration (at priority 15).
76 add_action( 'init', array( $this, 'init' ), 15 );
77 }
78
79 /**
80 * Initialize this module.
81 *
82 * @since 2.4.0
83 */
84 public function init() {
85 // Others will be able to add data here via the 'customify_localized_js_settings' filter.
86 // This is a just-in-time filter, triggered as late as possible.
87 $this->localized = array(
88 'config' => array(
89 'options_name' => PixCustomifyPlugin()->get_options_key(),
90 'ajax_url' => admin_url( 'admin-ajax.php' ),
91 'webfontloader_url' => plugins_url( 'js/vendor/webfontloader-1-6-28.min.js', PixCustomifyPlugin()->get_file() ),
92 'px_dependent_css_props' => self::$pixel_dependent_css_properties,
93 ),
94 // For localizing strings.
95 'l10n' => array(
96 'panelResetButton' => esc_html__( 'Panel\'s defaults', 'customify' ),
97 'sectionResetButton' => esc_html__( 'Reset All Options for This Section', 'customify' ),
98 'resetGlobalConfirmMessage' => wp_kses_post( __( 'Do you really want to reset to defaults all the fields? Watch out, this will reset all your Customify options and will save them!', 'customify' ) ),
99 'resetPanelConfirmMessage' => wp_kses_post( __( 'Do you really want to reset the settings in this panel?', 'customify' ) ),
100 'resetSectionConfirmMessage' => wp_kses_post( __( 'Do you really want to reset the settings in this section?', 'customify' ) ),
101 )
102 );
103
104 // Hook up.
105 $this->add_hooks();
106 }
107
108 /**
109 * Initiate our hooks
110 *
111 * @since 2.4.0
112 */
113 public function add_hooks() {
114
115 // Styles for the Customizer
116 add_action( 'customize_controls_init', array( $this, 'register_admin_customizer_styles' ), 10 );
117 add_action( 'customize_controls_enqueue_scripts', array( $this, 'enqueue_admin_customizer_styles' ), 10 );
118 // Scripts enqueued in the Customizer
119 add_action( 'customize_controls_init', array( $this, 'register_admin_customizer_scripts' ), 15 );
120 add_action( 'customize_controls_enqueue_scripts', array( $this, 'enqueue_admin_customizer_scripts' ), 15 );
121
122 // Scripts enqueued only in the theme preview
123 add_action( 'customize_preview_init', array( $this, 'customizer_live_preview_register_scripts' ), 10 );
124 add_action( 'customize_preview_init', array( $this, 'customizer_live_preview_enqueue_scripts' ), 99999 );
125
126 // Add extra settings data to _wpCustomizeSettings.settings of the parent window.
127 add_action( 'customize_controls_print_footer_scripts', array( $this, 'customize_pane_settings_additional_data' ), 10000 );
128
129 // The frontend effects of the Customizer controls
130 $load_location = PixCustomifyPlugin()->settings->get_plugin_setting( 'style_resources_location', 'wp_head' );
131
132 add_action( $load_location, array( $this, 'output_dynamic_style' ), 99 );
133
134 add_action( 'customize_register', array( $this, 'remove_default_sections' ), 11 );
135 add_action( 'customize_register', array( $this, 'process_customizer_config' ), 12 );
136 // Maybe the theme has instructed us to do things like removing sections or controls.
137 add_action( 'customize_register', array( $this, 'maybe_process_config_extras' ), 13 );
138
139 /*
140 * Development related
141 */
142 if ( defined( 'CUSTOMIFY_DEV_FORCE_DEFAULTS' ) && true === CUSTOMIFY_DEV_FORCE_DEFAULTS ) {
143 // If the development constant CUSTOMIFY_DEV_FORCE_DEFAULTS has been defined we will not save anything in the database
144 // Always go with the default
145 add_filter( 'customize_changeset_save_data', array( $this, 'prevent_changeset_save_in_devmode' ), 50, 1 );
146 // Add a JS to display a notification
147 add_action( 'customize_controls_print_footer_scripts', array( $this, 'prevent_changeset_save_in_devmode_notification' ), 100 );
148 }
149 }
150
151 /**
152 * Register Customizer admin styles
153 */
154 function register_admin_customizer_styles() {
155 $rtl_suffix = is_rtl() ? '-rtl' : '';
156 wp_register_style( 'customify_style', plugins_url( 'css/customizer' . $rtl_suffix . '.css', PixCustomifyPlugin()->get_file() ), array( 'dashicons' ), PixCustomifyPlugin()->get_version() );
157 }
158
159 /**
160 * Enqueue Customizer admin styles
161 */
162 function enqueue_admin_customizer_styles() {
163 wp_enqueue_style( 'customify_style' );
164 }
165
166 /**
167 * Register Customizer admin scripts
168 */
169 function register_admin_customizer_scripts() {
170 $suffix = ( defined( 'SCRIPT_DEBUG' ) && SCRIPT_DEBUG ) ? '' : '.min';
171
172 wp_register_script(
173 PixCustomifyPlugin()->get_slug() . '-select2',
174 plugins_url( 'js/vendor/select2-4.0.13/dist/js/select2.full' . $suffix . '.js', PixCustomifyPlugin()->get_file() ),
175 array( 'jquery' ),
176 PixCustomifyPlugin()->get_version(),
177 false
178 );
179 wp_register_script(
180 'jquery-react',
181 plugins_url( 'js/vendor/jquery-react' . $suffix . '.js', PixCustomifyPlugin()->get_file() ),
182 array( 'jquery' ),
183 PixCustomifyPlugin()->get_version(),
184 false
185 );
186 wp_register_script(
187 PixCustomifyPlugin()->get_slug() . '-fontfields',
188 plugins_url( 'js/customizer/font-fields' . $suffix . '.js', PixCustomifyPlugin()->get_file() ),
189 array( 'jquery', 'underscore' ),
190 PixCustomifyPlugin()->get_version(),
191 false
192 );
193
194 wp_register_script( PixCustomifyPlugin()->get_slug() . '-customizer-scripts',
195 plugins_url( 'js/customizer' . $suffix . '.js', PixCustomifyPlugin()->get_file() ),
196 array(
197 'jquery',
198 PixCustomifyPlugin()->get_slug() . '-select2',
199 'underscore',
200 'customize-controls',
201 PixCustomifyPlugin()->get_slug() . '-fontfields',
202 ),
203 PixCustomifyPlugin()->get_version(),
204 false );
205 }
206
207 /**
208 * Enqueue Customizer admin scripts
209 */
210 function enqueue_admin_customizer_scripts() {
211 wp_enqueue_script( 'jquery-react' );
212 wp_enqueue_script( PixCustomifyPlugin()->get_slug() . '-customizer-scripts' );
213
214 wp_add_inline_script( PixCustomifyPlugin()->get_slug() . '-customizer-scripts',
215 self::getlocalizeToWindowScript( 'customify',
216 apply_filters( 'customify_localized_js_settings', $this->localized )
217 ), 'before' );
218 }
219
220 /** Register Customizer scripts loaded only on previewer page */
221 function customizer_live_preview_register_scripts() {
222 $suffix = ( defined( 'SCRIPT_DEBUG' ) && SCRIPT_DEBUG ) ? '' : '.min';
223
224 wp_register_script( PixCustomifyPlugin()->get_slug() . '-CSSOM',
225 plugins_url( 'js/vendor/CSSOM' . $suffix . '.js', PixCustomifyPlugin()->get_file() ),
226 array(),
227 PixCustomifyPlugin()->get_version(), true );
228
229 wp_register_script( PixCustomifyPlugin()->get_slug() . '-cssUpdate',
230 plugins_url( 'js/jquery.cssUpdate' . $suffix . '.js', PixCustomifyPlugin()->get_file() ),
231 array( 'jquery' ),
232 PixCustomifyPlugin()->get_version(), true );
233
234 wp_register_script( PixCustomifyPlugin()->get_slug() . '-previewer-scripts',
235 plugins_url( 'js/customizer_preview' . $suffix . '.js', PixCustomifyPlugin()->get_file() ),
236 array(
237 'jquery',
238 'customize-preview',
239 'underscore',
240 PixCustomifyPlugin()->get_slug() . '-CSSOM',
241 PixCustomifyPlugin()->get_slug() . '-cssUpdate'
242 ),
243 PixCustomifyPlugin()->get_version(), true );
244 }
245
246 /** Enqueue Customizer scripts loaded only on previewer page */
247 function customizer_live_preview_enqueue_scripts() {
248 wp_enqueue_script( PixCustomifyPlugin()->get_slug() . '-previewer-scripts' );
249 }
250
251 /**
252 * Prevent saving of plugin options in the Customizer
253 *
254 * @param array $data The data to save
255 *
256 * @return array
257 */
258 public function prevent_changeset_save_in_devmode( $data ) {
259 // Get the options key
260 $options_key = PixCustomifyPlugin()->get_options_key();
261 if ( ! empty( $options_key ) ) {
262 // Remove any Customify data thus preventing it from saving
263 foreach ( $data as $option_id => $value ) {
264 if ( false !== strpos( $option_id, $options_key ) && ! PixCustomifyPlugin()->skip_dev_mode_force_defaults( $option_id ) ) {
265 unset( $data[ $option_id ] );
266 }
267 }
268 }
269
270 return $data;
271 }
272
273 public function prevent_changeset_save_in_devmode_notification() { ?>
274 <script type="application/javascript">
275 (function ( $, exports, wp ) {
276 'use strict';
277 // when the customizer is ready add our notification
278 wp.customize.bind('ready', function () {
279 wp.customize.notifications.add( 'customify_force_defaults', new wp.customize.Notification(
280 'customify_force_defaults',
281 {
282 type: 'warning',
283 message: '<?php echo wp_kses_post( __( '<strong>Customify: Development Mode</strong><p>All options are switched to default. While they are changing in the live preview, they will not be kept when you hit publish.</p>', 'customify' ) ); ?>'
284 }
285 ) );
286 });
287 })(jQuery, window, wp);
288 </script>
289 <?php }
290
291 /**
292 * Output CSS style generated by customizer
293 */
294 function output_dynamic_style() { ?>
295 <style id="customify_output_style">
296 <?php echo $this->get_dynamic_style(); ?>
297 </style>
298 <?php
299
300 /**
301 * from now on we output only style tags only for the preview purpose
302 * so don't cry if you see 30+ style tags for each section
303 */
304 if ( ! isset( $GLOBALS['wp_customize'] ) ) {
305 return;
306 }
307
308 foreach ( PixCustomifyPlugin()->get_options_details( false, true ) as $option_id => $option_details ) {
309
310 if ( isset( $option_details['type'] ) && $option_details['type'] === 'custom_background' ) {
311 $custom_background_output = $this->process_custom_background_field_output( $option_details ); ?>
312
313 <style id="custom_background_output_for_<?php echo sanitize_html_class( $option_id ); ?>">
314 <?php
315 if ( ! empty( $custom_background_output )) {
316 echo $custom_background_output;
317 } ?>
318 </style>
319 <?php }
320
321 if ( ! isset( $option_details['live'] ) || $option_details['live'] !== true ) {
322 continue;
323 }
324
325 if ( ! empty( $option_details['css'] ) ) {
326 foreach ( $option_details['css'] as $key => $properties_set ) {
327 // We need to use a class because we may have multiple <style>s with the same "ID" for example
328 // when targeting the same property but with different selectors.
329 $unique_class = 'dynamic_setting_' . $option_id . '_property_' . str_replace( '-', '_', $properties_set['property'] ) . '_' . $key;
330
331 $inline_style = '<style class="' . sanitize_html_class( $unique_class ) . '" type="text/css">';
332
333 if ( isset( $properties_set['media'] ) && ! empty( $properties_set['media'] ) ) {
334 $inline_style .= '@media '. $properties_set['media'] . ' {';
335 }
336
337 if ( isset( $properties_set['selector'] ) && isset( $properties_set['property'] ) ) {
338 $css_output = $this->process_css_property($properties_set, $option_details['value']);
339 if ( ! empty( $css_output ) ) {
340 $inline_style .= $css_output;
341 }
342 }
343
344 if ( isset( $properties_set['media'] ) && ! empty( $properties_set['media'] ) ) {
345 $inline_style .= '}';
346 }
347 $inline_style .= '</style>';
348
349 echo $inline_style;
350 }
351 }
352 }
353 }
354
355 function get_dynamic_style() {
356 $custom_css = '';
357
358 foreach ( PixCustomifyPlugin()->get_options_details( true ) as $option_id => $option_details ) {
359
360 if ( isset( $option_details['css'] ) && ! empty( $option_details['css'] ) ) {
361 // now process each
362 $custom_css .= $this->convert_setting_to_css( $option_id, $option_details );
363 }
364
365 if ( isset( $option_details['type'] ) && $option_details['type'] === 'custom_background' ) {
366 $custom_css .= $this->process_custom_background_field_output( $option_details ) . "\n";
367 }
368 }
369
370 if ( ! empty( $this->media_queries ) ) {
371
372 foreach ( $this->media_queries as $media_query => $properties ) {
373
374 if ( empty( $properties ) ) {
375 continue;
376 }
377
378 $media_query_custom_css = '';
379
380 foreach ( $properties as $key => $property ) {
381 $property_settings = $property['property'];
382 $property_value = $property['value'];
383 $css_output = $this->process_css_property( $property_settings, $property_value );
384 if ( ! empty( $css_output ) ) {
385 $media_query_custom_css .= "\t" . $css_output . "\n";
386 }
387 }
388
389 if ( ! empty( $media_query_custom_css ) ) {
390 $media_query_custom_css = "\n" . '@media ' . $media_query . " { " . "\n" . "\n" . $media_query_custom_css . "}" . "\n";
391 }
392
393 if ( ! empty( $media_query_custom_css ) ) {
394 $custom_css .= $media_query_custom_css;
395 }
396
397 }
398 }
399
400 return apply_filters( 'customify_dynamic_style', $custom_css );
401 }
402
403 /**
404 * Turn css options into a valid CSS output
405 *
406 * @param $option_id
407 * @param array $option_details
408 *
409 * @return string
410 */
411 protected function convert_setting_to_css( $option_id, $option_details ) {
412 $output = '';
413
414 if ( empty( $option_details['css'] ) ) {
415 return $output;
416 }
417
418 foreach ( $option_details['css'] as $css_property ) {
419
420 if ( isset( $css_property['media'] ) && ! empty( $css_property['media'] ) ) {
421 $this->media_queries[ $css_property['media'] ][ $option_id ] = array(
422 'property' => $css_property,
423 'value' => $option_details['value']
424 );
425 continue;
426 }
427
428 if ( isset( $css_property['selector'] ) && isset( $css_property['property'] ) ) {
429 $output .= $this->process_css_property( $css_property, $option_details['value'] );
430 }
431 }
432
433 return $output;
434 }
435
436 protected function process_css_property( $css_property, $value ) {
437 $unit = isset( $css_property['unit'] ) ? $css_property['unit'] : '';
438 // If the unit is empty (string, not boolean false) but the property should have a unit force 'px' as it
439 if ( '' === $unit && in_array( $css_property['property'], self::$pixel_dependent_css_properties ) ) {
440 $unit = 'px';
441 }
442
443 $css_property['selector'] = apply_filters( 'customify_css_selector', $this->cleanup_whitespace_css( $css_property['selector'] ), $css_property );
444 if ( empty( $css_property['selector'] ) ) {
445 return '';
446 }
447
448 $property_output = $css_property['selector'] . ' { ' . $css_property['property'] . ': ' . $value . $unit . "; }" . "\n";
449
450 // Handle the value filter callback.
451 if ( isset( $css_property['filter_value_cb'] ) ) {
452 $value = $this->maybe_apply_filter( $css_property['filter_value_cb'], $value );
453 }
454
455 // Handle output callback.
456 if ( isset( $css_property['callback_filter'] ) && is_callable( $css_property['callback_filter'] ) ) {
457 $property_output = call_user_func( $css_property['callback_filter'], $value, $css_property['selector'], $css_property['property'], $unit );
458 }
459
460 return $property_output;
461 }
462
463 /**
464 * Apply a filter (config) to a value.
465 *
466 * We currently handle filters like these:
467 * // Elaborate filter config
468 * array(
469 * 'callback' => 'is_post_type_archive',
470 * // The arguments we should pass to the check function.
471 * // Think post types, taxonomies, or nothing if that is the case.
472 * // It can be an array of values or a single value.
473 * 'args' => array(
474 * 'jetpack-portfolio',
475 * ),
476 * ),
477 * // Simple filter - just the function name
478 * 'is_404',
479 *
480 * @param array|string $filter
481 * @param mixed $value The value to apply the filter to.
482 *
483 * @return mixed The filtered value.
484 */
485 public function maybe_apply_filter( $filter, $value ) {
486 // Let's get some obvious things off the table.
487 // On invalid data, we just return what we've received.
488 if ( empty( $filter ) ) {
489 return $value;
490 }
491
492 // First, we handle the shorthand version: just a function name
493 if ( is_string( $filter ) && is_callable( $filter ) ) {
494 $value = call_user_func( $filter );
495 } elseif ( is_array( $filter ) && ! empty( $filter['callback'] ) && is_callable( $filter['callback'] ) ) {
496 if ( empty( $filter['args'] ) ) {
497 $filter['args'] = array();
498 }
499 // The value is always the first argument.
500 $filter['args'] = array( $value ) + $filter['args'];
501
502 $value = call_user_func_array( $filter['callback'], $filter['args'] );
503 }
504
505 return $value;
506 }
507
508 protected function process_custom_background_field_output( $option_details ) {
509 $selector = $output = '';
510
511 if ( ! isset( $option_details['value'] ) ) {
512 return false;
513 }
514 $value = $option_details['value'];
515
516 if ( ! isset( $option_details['output'] ) ) {
517 return $selector;
518 } elseif ( is_string( $option_details['output'] ) ) {
519 $selector = $option_details['output'];
520 } elseif ( is_array( $option_details['output'] ) ) {
521 $selector = implode( ' ', $option_details['output'] );
522 }
523
524 // Loose the ton of tabs.
525 $selector = trim( preg_replace( '/\t+/', '', $selector ) );
526
527 $output .= $selector . " {";
528 if ( isset( $value['background-image'] ) && ! empty( $value['background-image'] ) ) {
529 $output .= "background-image: url( " . $value['background-image'] . ");";
530 } else {
531 $output .= "background-image: none;";
532 }
533
534 if ( isset( $value['background-repeat'] ) && ! empty( $value['background-repeat'] ) ) {
535 $output .= "background-repeat:" . $value['background-repeat'] . ";";
536 }
537
538 if ( isset( $value['background-position'] ) && ! empty( $value['background-position'] ) ) {
539 $output .= "background-position:" . $value['background-position'] . ";";
540 }
541
542 if ( isset( $value['background-size'] ) && ! empty( $value['background-size'] ) ) {
543 $output .= "background-size:" . $value['background-size'] . ";";
544 }
545
546 if ( isset( $value['background-attachment'] ) && ! empty( $value['background-attachment'] ) ) {
547 $output .= "background-attachment:" . $value['background-attachment'] . ";";
548 }
549 $output .= "}\n";
550
551 return $output;
552 }
553
554 protected function load_customizer_controls() {
555
556 // First require the base customizer extend class.
557 require_once( trailingslashit( PixCustomifyPlugin()->get_base_path() ) . 'includes/customizer-controls/class-Pix_Customize_Control.php' );
558
559 // Now load all the controls' files.
560 $path = apply_filters( 'customify_customizer_controls_path', trailingslashit( PixCustomifyPlugin()->get_base_path() ) . 'includes/customizer-controls/' );
561 pixcustomify::require_all( $path );
562
563 do_action( 'customify_loaded_customizer_controls' );
564 }
565
566 /**
567 * Register all the panels, sections and controls we receive through the plugin's config.
568 *
569 * @param WP_Customize_Manager $wp_customize
570 */
571 function process_customizer_config( $wp_customize ) {
572
573 do_action( 'customify_before_process_customizer_config', $wp_customize );
574
575 // Require all the control classes available.
576 $this->load_customizer_controls();
577
578 // Load the customizer config.
579 $customizer_config = apply_filters( 'customify_customizer_config_pre_processing', PixCustomifyPlugin()->get_customizer_config(), $wp_customize );
580
581 // Bail if we don't have a config, or we are missing the 'opt-name' entry.
582 if ( empty( $customizer_config ) || empty( $customizer_config['opt-name'] ) ) {
583 do_action( 'customify_skip_process_customizer_config', $customizer_config, $wp_customize );
584 return;
585 }
586
587 $options_name = $customizer_config['opt-name'];
588 $wp_customize->options_key = $options_name;
589
590 // Handle panels.
591 if ( isset( $customizer_config['panels'] ) && ! empty( $customizer_config['panels'] ) ) {
592
593 foreach ( $customizer_config['panels'] as $panel_id => $panel_config ) {
594
595 if ( ! empty( $panel_id ) && isset( $panel_config['sections'] ) && ! empty( $panel_config['sections'] ) ) {
596
597 // If we have been explicitly given a panel ID we will use that
598 if ( ! empty( $panel_config['panel_id'] ) ) {
599 $panel_id = $panel_config['panel_id'];
600 } else {
601 $panel_id = $options_name . '[' . $panel_id . ']';
602 }
603
604 $panel_args = array(
605 'priority' => 10,
606 'capability' => 'edit_theme_options',
607 'title' => esc_html__( 'Panel title is required', 'customify' ),
608 'description' => esc_html__( 'Description of what this panel does.', 'customify' ),
609 'auto_expand_sole_section' => false,
610 );
611
612 if ( isset( $panel_config['priority'] ) && ! empty( $panel_config['priority'] ) ) {
613 $panel_args['priority'] = $panel_config['priority'];
614 }
615
616 if ( isset( $panel_config['title'] ) && ! empty( $panel_config['title'] ) ) {
617 $panel_args['title'] = $panel_config['title'];
618 }
619
620 if ( isset( $panel_config['description'] ) && ! empty( $panel_config['description'] ) ) {
621 $panel_args['description'] = $panel_config['description'];
622 }
623
624 if ( isset( $panel_config['auto_expand_sole_section'] ) ) {
625 $panel_args['auto_expand_sole_section'] = $panel_config['auto_expand_sole_section'];
626 }
627
628
629 $panel = $wp_customize->add_panel( $panel_id, $panel_args );
630 // Fire a general added panel hook
631 do_action( 'customify_process_customizer_config_added_panel', $panel, $panel_args, $wp_customize );
632
633 foreach ( $panel_config['sections'] as $section_id => $section_config ) {
634 if ( ! empty( $section_id ) && isset( $section_config['options'] ) && ! empty( $section_config['options'] ) ) {
635 $this->register_section( $panel_id, $section_id, $options_name, $section_config, $wp_customize );
636 }
637 }
638
639 // Fire a general finished panel hook
640 do_action( 'customify_process_customizer_config_finished_panel', $panel, $panel_args, $wp_customize );
641 }
642 }
643 }
644
645 // Handle sections.
646 if ( isset( $customizer_config['sections'] ) && ! empty( $customizer_config['sections'] ) ) {
647
648 foreach ( $customizer_config['sections'] as $section_id => $section_config ) {
649 if ( ! empty( $section_id ) && isset( $section_config['options'] ) && ! empty( $section_config['options'] ) ) {
650 $this->register_section( $panel_id = false, $section_id, $options_name, $section_config, $wp_customize );
651 }
652 }
653 }
654
655 // Handle development helper buttons.
656 if ( PixCustomifyPlugin()->settings->get_plugin_setting('enable_reset_buttons') ) {
657 // create a toolbar section which will be present all the time
658 $reset_section_settings = array(
659 'title' => esc_html__( 'Customify Toolbox', 'customify' ),
660 'capability' => 'manage_options',
661 'priority' => 999999999,
662 'options' => array(
663 'reset_all_button' => array(
664 'type' => 'button',
665 'label' => esc_html__( 'Reset Customify', 'customify' ),
666 'action' => 'reset_customify',
667 'value' => 'Reset',
668 ),
669 )
670 );
671
672 $wp_customize->add_section(
673 'customify_toolbar',
674 $reset_section_settings
675 );
676
677 $wp_customize->add_setting(
678 'reset_customify',
679 array()
680 );
681 $wp_customize->add_control( new Pix_Customize_Button_Control(
682 $wp_customize,
683 'reset_customify',
684 array(
685 'label' => esc_html__( 'Reset All Customify Options to Default', 'customify' ),
686 'section' => 'customify_toolbar',
687 'settings' => 'reset_customify',
688 'action' => 'reset_customify',
689 )
690 ) );
691 }
692
693 do_action( 'customify_after_process_customizer_config', $wp_customize );
694 }
695
696 /**
697 * @param string $panel_id
698 * @param string $section_key
699 * @param string $options_name
700 * @param array $section_config
701 * @param WP_Customize_Manager $wp_customize
702 */
703 protected function register_section( $panel_id, $section_key, $options_name, $section_config, $wp_customize ) {
704
705 // If we have been explicitly given a section ID we will use that
706 if ( ! empty( $section_config['section_id'] ) ) {
707 $section_id = $section_config['section_id'];
708 } else {
709 $section_id = $options_name . '[' . $section_key . ']';
710 }
711
712 // Add the new section to the Customizer, but only if it is not already added.
713 if ( ! $wp_customize->get_section( $section_id ) ) {
714 // Merge the section settings with the defaults
715 $section_args = wp_parse_args( $section_config, array(
716 'priority' => 10,
717 'panel' => $panel_id,
718 'capability' => 'edit_theme_options',
719 'theme_supports' => '',
720 'title' => esc_html__( 'Title Section is required', 'customify' ),
721 'description' => '',
722 'type' => 'default',
723 'description_hidden' => false,
724 ) );
725
726 // Only add the section if it is not of type `hidden`
727 if ( 'hidden' !== $section_args['type'] ) {
728 $section = $wp_customize->add_section( $section_id, $section_args );
729 // Fire a general added section hook
730 do_action( 'customify_process_customizer_config_added_section', $section, $section_args, $wp_customize );
731 }
732 }
733
734 // Now go through each section option and add the fields
735 foreach ( $section_config['options'] as $option_id => $option_config ) {
736
737 if ( empty( $option_id ) || ! isset( $option_config['type'] ) ) {
738 continue;
739 }
740
741 // If we have been explicitly given a setting ID we will use that
742 if ( ! empty( $option_config['setting_id'] ) ) {
743 $setting_id = $option_config['setting_id'];
744 } else {
745 $setting_id = $options_name . '[' . $option_id . ']';
746 }
747
748 // Add the option config to the localized array so we can pass the info to JS.
749 // @todo Maybe we should ensure that the connected_fields configs passed here follow the same format and logic as the ones in ::customize_pane_settings_additional_data() thus maybe having the data in the same place.
750
751 // Filter some settings that have purely visual purpose.
752 if ( ! empty( $option_config['type'] ) && ! in_array( $option_config['type'], array( 'html', 'button' ) ) ) {
753 $this->localized['config']['settings'][ $setting_id ] = $option_config;
754 }
755
756 // Generate a safe option ID (not the final setting ID) to us in HTML attributes like ID or class
757 $this->localized['config']['settings'][ $setting_id ]['html_safe_option_id'] = sanitize_html_class( $option_id );
758
759 $this->register_field( $section_id, $setting_id, $option_config, $wp_customize );
760 }
761
762 // Fire a general finished section hook
763 do_action( 'customify_process_customizer_config_finished_section', $section_id, $wp_customize );
764 }
765
766 /**
767 * Register a Customizer field (setting and control).
768 *
769 * @see WP_Customize_Setting
770 * @see WP_Customize_Control
771 *
772 * @param string $section_id
773 * @param string $setting_id
774 * @param array $field_config
775 * @param WP_Customize_Manager $wp_customize
776 */
777 protected function register_field( $section_id, $setting_id, $field_config, $wp_customize ) {
778
779 $add_control = true;
780 // defaults
781 $setting_args = array(
782 'type' => 'theme_mod',
783 'default' => '',
784 'capability' => 'edit_theme_options',
785 'transport' => 'refresh',
786 );
787 $control_args = array(
788 'priority' => 10,
789 'label' => '',
790 'section' => $section_id,
791 'settings' => $setting_id,
792 );
793
794 // sanitize settings
795 if ( ! empty( $field_config['live'] ) || $field_config['type'] === 'font' ) {
796 $setting_args['transport'] = 'postMessage';
797 }
798
799 if ( isset( $field_config['default'] ) ) {
800 $setting_args['default'] = $field_config['default'];
801 if ( is_array( $setting_args['default'] ) ) {
802 $setting_args['default'] = (object) $setting_args['default'];
803 }
804 }
805
806 if ( ! empty( $field_config['capability'] ) ) {
807 $setting_args['capability'] = $field_config['capability'];
808 }
809
810 // If the setting defines it's own type we will respect that, otherwise we will follow the global plugin setting.
811 if ( ! empty( $field_config['setting_type'] ) && 'option' === $field_config['setting_type'] ) {
812 $setting_args['type'] = 'option';
813 } elseif ( PixCustomifyPlugin()->settings->get_plugin_setting('values_store_mod') === 'option' ) {
814 $setting_args['type'] = 'option';
815 }
816
817 if ( ! empty( $field_config['sanitize_callback'] ) && is_callable( $field_config['sanitize_callback'] ) ) {
818 $setting_args['sanitize_callback'] = $field_config['sanitize_callback'];
819 } elseif ( 'checkbox' === $field_config['type'] ) {
820 $setting_args['sanitize_callback'] = array( $this, 'setting_sanitize_checkbox' );
821 }
822
823 // Add the setting
824 $wp_customize->add_setting( $setting_id, $setting_args );
825
826 // Stop the control registration, if we are presented with the right type.
827 if ( 'hidden_control' === $field_config['type'] ) {
828 return;
829 }
830
831 $control_args['type'] = $field_config['type'];
832
833 // now sanitize the control
834 if ( ! empty( $field_config['label'] ) ) {
835 $control_args['label'] = $field_config['label'];
836 }
837
838 if ( ! empty( $field_config['priority'] ) ) {
839 $control_args['priority'] = $field_config['priority'];
840 }
841
842 if ( ! empty( $field_config['desc'] ) ) {
843 $control_args['description'] = $field_config['desc'];
844 }
845
846 if ( ! empty( $field_config['active_callback'] ) ) {
847 $control_args['active_callback'] = $field_config['active_callback'];
848 }
849
850 // select the control type
851 // but first init a default
852 $control_class_name = 'Pix_Customize_Text_Control';
853
854 // If is a standard wp field type call it here and skip the rest.
855 if ( in_array( $field_config['type'], array(
856 'checkbox',
857 'dropdown-pages',
858 'url',
859 'date',
860 'time',
861 'datetime',
862 'week',
863 'search'
864 ) ) ) {
865 $wp_customize->add_control( $setting_id . '_control', $control_args );
866
867 return;
868 } elseif ( in_array( $field_config['type'], array(
869 'radio',
870 'select'
871 ) ) && ! empty( $field_config['choices'] )
872 ) {
873 $control_args['choices'] = $field_config['choices'];
874 $wp_customize->add_control( $setting_id . '_control', $control_args );
875
876 return;
877 } elseif ( in_array( $field_config['type'], array( 'range' ) ) && ! empty( $field_config['input_attrs'] ) ) {
878
879 $control_args['input_attrs'] = $field_config['input_attrs'];
880
881 $wp_customize->add_control( $setting_id . '_control', $control_args );
882 }
883
884 // If we arrive here this means we have a custom field control (with a corresponding class in includes/customizer-controls).
885 switch ( $field_config['type'] ) {
886
887 case 'text':
888 if ( isset( $field_config['live'] ) ) {
889 $control_args['live'] = $field_config['live'];
890 }
891
892 $control_class_name = 'Pix_Customize_Text_Control';
893 break;
894
895 case 'textarea':
896 if ( isset( $field_config['live'] ) ) {
897 $control_args['live'] = $field_config['live'];
898 }
899
900 $control_class_name = 'Pix_Customize_Textarea_Control';
901 break;
902
903 case 'color':
904 $control_class_name = 'WP_Customize_Color_Control';
905 break;
906
907 case 'color_drop':
908 $control_class_name = 'Pix_Customize_Color_Drop_Control';
909 break;
910
911 case 'ace_editor':
912 if ( isset( $field_config['live'] ) ) {
913 $control_args['live'] = $field_config['live'];
914 }
915
916 if ( isset( $field_config['editor_type'] ) ) {
917 $control_args['editor_type'] = $field_config['editor_type'];
918 }
919
920 $control_class_name = 'Pix_Customize_Ace_Editor_Control';
921 break;
922
923 case 'upload':
924 $control_class_name = 'WP_Customize_Upload_Control';
925 break;
926
927 case 'image':
928 $control_class_name = 'WP_Customize_Image_Control';
929 break;
930
931 case 'media':
932 $control_class_name = 'WP_Customize_Media_Control';
933 break;
934
935 case 'custom_background':
936 if ( isset( $field_config['field'] ) ) {
937 $control_args['field'] = $field_config['field'];
938 }
939
940 $control_class_name = 'Pix_Customize_Background_Control';
941 break;
942
943 case 'cropped_image':
944 case 'cropped_media': // 'cropped_media' no longer works
945 if ( isset( $field_config['width'] ) ) {
946 $control_args['width'] = $field_config['width'];
947 }
948
949 if ( isset( $field_config['height'] ) ) {
950 $control_args['height'] = $field_config['height'];
951 }
952
953 if ( isset( $field_config['flex_width'] ) ) {
954 $control_args['flex_width'] = $field_config['flex_width'];
955 }
956
957 if ( isset( $field_config['flex_height'] ) ) {
958 $control_args['flex_height'] = $field_config['flex_height'];
959 }
960
961 if ( isset( $field_config['button_labels'] ) ) {
962 $control_args['button_labels'] = $field_config['button_labels'];
963 }
964
965 $control_class_name = 'WP_Customize_Cropped_Image_Control';
966 break;
967
968 case 'font' :
969
970 // Only add the control if typography is turned on in the plugin settings.
971 if ( ! PixCustomifyPlugin()->settings->get_plugin_setting( 'typography', '1' ) ) {
972 $add_control = false;
973 break;
974 }
975
976 $control_class_name = 'Pix_Customize_Font_Control';
977
978 if ( isset( $field_config['recommended'] ) ) {
979 $control_args['recommended'] = $field_config['recommended'];
980 }
981
982 if ( isset( $field_config['live'] ) ) {
983 $control_args['live'] = $field_config['live'];
984 }
985
986 // This is used only as an extreme failsafe.
987 // Normally, when there is no value, the WP Settings system will fallback on the default given for the setting.
988 // See above when registering the setting corresponding to this control.
989 if ( isset( $field_config['default'] ) ) {
990 $control_args['default'] = $field_config['default'];
991 }
992
993 // We should always receive a subfields configuration.
994 if ( isset( $field_config['fields'] ) ) {
995 $control_args['fields'] = $field_config['fields'];
996 } else {
997 $control_args['fields'] = array();
998 }
999
1000 break;
1001
1002 case 'select2' :
1003 if ( ! isset( $field_config['choices'] ) || empty( $field_config['choices'] ) ) {
1004 return;
1005 }
1006
1007 $control_args['choices'] = $field_config['choices'];
1008
1009 $control_class_name = 'Pix_Customize_Select2_Control';
1010 break;
1011
1012 case 'sm_radio' :
1013 if ( ! isset( $field_config['choices'] ) || empty( $field_config['choices'] ) ) {
1014 return;
1015 }
1016
1017 $control_args['choices'] = $field_config['choices'];
1018
1019 $control_class_name = 'Pix_Customize_SM_radio_Control';
1020 break;
1021
1022 case 'sm_palette_filter' :
1023 if ( ! isset( $field_config['choices'] ) || empty( $field_config['choices'] ) ) {
1024 return;
1025 }
1026
1027 $control_args['choices'] = $field_config['choices'];
1028
1029 $control_class_name = 'Pix_Customize_SM_palette_filter_Control';
1030 break;
1031
1032 case 'sm_switch' :
1033 if ( ! isset( $field_config['choices'] ) || empty( $field_config['choices'] ) ) {
1034 return;
1035 }
1036
1037 $control_args['choices'] = $field_config['choices'];
1038
1039 $control_class_name = 'Pix_Customize_SM_switch_Control';
1040 break;
1041
1042 case 'preset' :
1043 if ( ! isset( $field_config['choices'] ) || empty( $field_config['choices'] ) ) {
1044 return;
1045 }
1046
1047 $control_args['choices'] = $field_config['choices'];
1048
1049 if ( isset( $field_config['choices_type'] ) || ! empty( $field_config['choices_type'] ) ) {
1050 $control_args['choices_type'] = $field_config['choices_type'];
1051 }
1052
1053 if ( isset( $field_config['desc'] ) || ! empty( $field_config['desc'] ) ) {
1054 $control_args['description'] = $field_config['desc'];
1055 }
1056
1057
1058 $control_class_name = 'Pix_Customize_Preset_Control';
1059 break;
1060
1061 case 'radio_image' :
1062 if ( ! isset( $field_config['choices'] ) || empty( $field_config['choices'] ) ) {
1063 return;
1064 }
1065
1066 $control_args['choices'] = $field_config['choices'];
1067
1068 if ( isset( $field_config['choices_type'] ) || ! empty( $field_config['choices_type'] ) ) {
1069 $control_args['choices_type'] = $field_config['choices_type'];
1070 }
1071
1072 if ( isset( $field_config['desc'] ) || ! empty( $field_config['desc'] ) ) {
1073 $control_args['description'] = $field_config['desc'];
1074 }
1075
1076
1077 $control_class_name = 'Pix_Customize_Radio_Image_Control';
1078 break;
1079
1080 case 'radio_html' :
1081 if ( ! isset( $field_config['choices'] ) || empty( $field_config['choices'] ) ) {
1082 return;
1083 }
1084
1085 $control_args['choices'] = $field_config['choices'];
1086
1087 if ( isset( $field_config['desc'] ) || ! empty( $field_config['desc'] ) ) {
1088 $control_args['description'] = $field_config['desc'];
1089 }
1090
1091 $control_class_name = 'Pix_Customize_Radio_HTML_Control';
1092 break;
1093
1094 case 'button' :
1095 if ( ! isset( $field_config['action'] ) || empty( $field_config['action'] ) ) {
1096 return;
1097 }
1098
1099 $control_args['action'] = $field_config['action'];
1100
1101 $control_class_name = 'Pix_Customize_Button_Control';
1102
1103 break;
1104
1105 case 'html' :
1106 if ( isset( $field_config['html'] ) || ! empty( $field_config['html'] ) ) {
1107 $control_args['html'] = $field_config['html'];
1108 }
1109
1110 $control_class_name = 'Pix_Customize_HTML_Control';
1111 break;
1112
1113 default:
1114 // if we don't have a real control just quit, it doesn't even matter
1115 return;
1116 break;
1117 }
1118
1119 $this_control = new $control_class_name(
1120 $wp_customize,
1121 $setting_id . '_control',
1122 $control_args
1123 );
1124
1125 if ( $add_control ) {
1126 $wp_customize->add_control( $this_control );
1127 }
1128 }
1129
1130 /**
1131 * Remove the sections selected by user
1132 *
1133 * @param WP_Customize_Manager $wp_customize
1134 */
1135 function remove_default_sections( $wp_customize ) {
1136 global $wp_registered_sidebars;
1137
1138 $to_remove = PixCustomifyPlugin()->settings->get_plugin_setting( 'disable_default_sections' );
1139
1140 if ( ! empty( $to_remove ) ) {
1141 foreach ( $to_remove as $section => $nothing ) {
1142
1143 if ( $section === 'widgets' ) {
1144 foreach ( $wp_registered_sidebars as $widget => $settings ) {
1145 $wp_customize->remove_section( 'sidebar-widgets-' . $widget );
1146 }
1147 continue;
1148 }
1149
1150 $wp_customize->remove_section( $section );
1151 }
1152 }
1153 }
1154
1155 /**
1156 * Print JavaScript for adding additional data to _wpCustomizeSettings.settings object of the main window (not the preview window).
1157 */
1158 public function customize_pane_settings_additional_data() {
1159 /**
1160 * @global WP_Customize_Manager $wp_customize
1161 */
1162 global $wp_customize;
1163
1164 $options_name = PixCustomifyPlugin()->get_options_key();
1165 // Without an options name we can't do much.
1166 if ( empty( $options_name ) ) {
1167 return;
1168 }
1169
1170 $customizer_settings = $wp_customize->settings();
1171 ?>
1172 <script type="text/javascript">
1173 if ( 'undefined' === typeof _wpCustomizeSettings.settings ) {
1174 _wpCustomizeSettings.settings = {};
1175 }
1176
1177 <?php
1178 echo "(function ( sAdditional ){\n";
1179
1180 $options = PixCustomifyPlugin()->get_options_details();
1181 foreach ( $options as $option_id => $option_config ) {
1182 // If we have been explicitly given a setting ID we will use that
1183 if ( ! empty( $option_config['setting_id'] ) ) {
1184 $setting_id = $option_config['setting_id'];
1185 } else {
1186 $setting_id = $options_name . '[' . $option_id . ']';
1187 }
1188 // @todo Right now we only handle the connected_fields key - make this more dynamic by adding the keys that are not returned by WP_Customize_Setting->json()
1189 if ( ! empty( $customizer_settings[ $setting_id ] ) && ! empty( $option_config['connected_fields'] ) ) {
1190 // Pass through all the connected fields and make sure the id is in the final format
1191 $connected_fields = array();
1192 foreach ( $option_config['connected_fields'] as $key => $connected_field_config ) {
1193 $connected_field_data = array();
1194
1195 if ( is_string( $connected_field_config ) ) {
1196 $connected_field_id = $connected_field_config;
1197 } elseif ( is_array( $connected_field_config ) ) {
1198 // We have a full blown connected field config
1199 if ( is_string( $key ) ) {
1200 $connected_field_id = $key;
1201 } else {
1202 continue;
1203 }
1204
1205 // We will pass to JS all the configured connected field details.
1206 $connected_field_data = $connected_field_config;
1207 }
1208
1209 // Continue if we don't have a connected field ID to work with.
1210 if ( empty( $connected_field_id ) ) {
1211 continue;
1212 }
1213
1214 // If the connected setting is not one of our's, we will use it's ID as it is.
1215 if ( ! array_key_exists( $connected_field_id, $options ) ) {
1216 $connected_field_data['setting_id'] = $connected_field_id;
1217 }
1218 // If the connected setting specifies a setting ID, we will not prefix it and use it as it is.
1219 elseif ( ! empty( $options[ $connected_field_id ] ) && ! empty( $options[ $connected_field_id ]['setting_id'] ) ) {
1220 $connected_field_data['setting_id'] = $options[ $connected_field_id ]['setting_id'];
1221 } else {
1222 $connected_field_data['setting_id'] = $options_name . '[' . $connected_field_id . ']';
1223 }
1224
1225 $connected_fields[] = $connected_field_data;
1226 }
1227
1228 printf(
1229 "sAdditional[%s].%s = %s;\n",
1230 wp_json_encode( $setting_id ),
1231 'connected_fields',
1232 wp_json_encode( $connected_fields, JSON_FORCE_OBJECT )
1233 );
1234 }
1235 }
1236 echo "})( _wpCustomizeSettings.settings );\n";
1237 ?>
1238 </script>
1239 <?php
1240 }
1241
1242 /**
1243 * Maybe process certain "commands" from the config.
1244 *
1245 * Mainly things like removing sections, controls, etc.
1246 *
1247 * @since 1.9.0
1248 *
1249 * @param WP_Customize_Manager $wp_customize
1250 */
1251 public function maybe_process_config_extras( $wp_customize ) {
1252 $customizer_config = PixCustomifyPlugin()->get_customizer_config();
1253
1254 // Bail if we have no external theme config data.
1255 if ( empty( $customizer_config ) || ! is_array( $customizer_config ) ) {
1256 return;
1257 }
1258
1259 // Maybe remove panels
1260 if ( ! empty( $customizer_config['remove_panels'] ) ) {
1261 // Standardize it.
1262 if ( is_string( $customizer_config['remove_panels'] ) ) {
1263 $customizer_config['remove_panels'] = array( $customizer_config['remove_panels'] );
1264 }
1265
1266 foreach ( $customizer_config['remove_panels'] as $panel_id ) {
1267 $wp_customize->remove_panel( $panel_id );
1268 }
1269 }
1270
1271 // Maybe change panel props.
1272 if ( ! empty( $customizer_config['change_panel_props'] ) ) {
1273 foreach ( $customizer_config['change_panel_props'] as $panel_id => $panel_props ) {
1274 if ( ! is_array( $panel_props ) ) {
1275 continue;
1276 }
1277
1278 $panel = $wp_customize->get_panel( $panel_id );
1279 if ( empty( $panel ) || ! $panel instanceof WP_Customize_Panel ) {
1280 continue;
1281 }
1282
1283 $public_props = get_class_vars( get_class( $panel ) );
1284 foreach ( $panel_props as $prop_name => $prop_value ) {
1285
1286 if ( ! in_array( $prop_name, array_keys( $public_props ) ) ) {
1287 continue;
1288 }
1289
1290 $panel->$prop_name = $prop_value;
1291 }
1292 }
1293 }
1294
1295 // Maybe remove sections
1296 if ( ! empty( $customizer_config['remove_sections'] ) ) {
1297 // Standardize it.
1298 if ( is_string( $customizer_config['remove_sections'] ) ) {
1299 $customizer_config['remove_sections'] = array( $customizer_config['remove_sections'] );
1300 }
1301
1302 foreach ( $customizer_config['remove_sections'] as $section_id ) {
1303
1304 if ( 'widgets' === $section_id ) {
1305 global $wp_registered_sidebars;
1306
1307 foreach ( $wp_registered_sidebars as $widget => $settings ) {
1308 $wp_customize->remove_section( 'sidebar-widgets-' . $widget );
1309 }
1310 continue;
1311 }
1312
1313 $wp_customize->remove_section( $section_id );
1314 }
1315 }
1316
1317 // Maybe change section props.
1318 if ( ! empty( $customizer_config['change_section_props'] ) ) {
1319 foreach ( $customizer_config['change_section_props'] as $section_id => $section_props ) {
1320 if ( ! is_array( $section_props ) ) {
1321 continue;
1322 }
1323
1324 $section = $wp_customize->get_section( $section_id );
1325 if ( empty( $section ) || ! $section instanceof WP_Customize_Section ) {
1326 continue;
1327 }
1328
1329 $public_props = get_class_vars( get_class( $section ) );
1330 foreach ( $section_props as $prop_name => $prop_value ) {
1331
1332 if ( ! in_array( $prop_name, array_keys( $public_props ) ) ) {
1333 continue;
1334 }
1335
1336 $section->$prop_name = $prop_value;
1337 }
1338 }
1339 }
1340
1341 // Maybe remove settings
1342 if ( ! empty( $customizer_config['remove_settings'] ) ) {
1343 // Standardize it.
1344 if ( is_string( $customizer_config['remove_settings'] ) ) {
1345 $customizer_config['remove_settings'] = array( $customizer_config['remove_settings'] );
1346 }
1347
1348 foreach ( $customizer_config['remove_settings'] as $setting_id ) {
1349 $wp_customize->remove_setting( $setting_id );
1350 }
1351 }
1352
1353 // Maybe change setting props.
1354 if ( ! empty( $customizer_config['change_setting_props'] ) ) {
1355 foreach ( $customizer_config['change_setting_props'] as $setting_id => $setting_props ) {
1356 if ( ! is_array( $setting_props ) ) {
1357 continue;
1358 }
1359
1360 $setting = $wp_customize->get_setting( $setting_id );
1361 if ( empty( $setting ) || ! $setting instanceof WP_Customize_Setting ) {
1362 continue;
1363 }
1364
1365 $public_props = get_class_vars( get_class( $setting ) );
1366 foreach ( $setting_props as $prop_name => $prop_value ) {
1367
1368 if ( ! in_array( $prop_name, array_keys( $public_props ) ) ) {
1369 continue;
1370 }
1371
1372 $setting->$prop_name = $prop_value;
1373 }
1374 }
1375 }
1376
1377 // Maybe remove controls
1378 if ( ! empty( $customizer_config['remove_controls'] ) ) {
1379 // Standardize it.
1380 if ( is_string( $customizer_config['remove_controls'] ) ) {
1381 $customizer_config['remove_controls'] = array( $customizer_config['remove_controls'] );
1382 }
1383
1384 foreach ( $customizer_config['remove_controls'] as $control_id ) {
1385 $wp_customize->remove_control( $control_id );
1386 }
1387 }
1388
1389 // Maybe change control props.
1390 if ( ! empty( $customizer_config['change_control_props'] ) ) {
1391 foreach ( $customizer_config['change_control_props'] as $control_id => $control_props ) {
1392 if ( ! is_array( $control_props ) ) {
1393 continue;
1394 }
1395
1396 $control = $wp_customize->get_control( $control_id );
1397 if ( empty( $control ) || ! $control instanceof WP_Customize_Control ) {
1398 continue;
1399 }
1400
1401 $public_props = get_class_vars( get_class( $control ) );
1402 foreach ( $control_props as $prop_name => $prop_value ) {
1403
1404 if ( ! in_array( $prop_name, array_keys( $public_props ) ) ) {
1405 continue;
1406 }
1407
1408 $control->$prop_name = $prop_value;
1409 }
1410 }
1411 }
1412 }
1413
1414 /* HELPERS */
1415
1416 /**
1417 * Cleanup stuff like tab characters.
1418 *
1419 * @param string $string
1420 *
1421 * @return string
1422 */
1423 function cleanup_whitespace_css( $string ) {
1424 $string = normalize_whitespace( $string );
1425
1426 return $string;
1427 }
1428
1429 public function get_fields_by_key( $fields_config, $key, $value, &$results, $input_key = 0 ) {
1430 if ( ! is_array( $fields_config ) ) {
1431 return;
1432 }
1433
1434 if ( isset( $fields_config[ $key ] ) && $fields_config[ $key ] == $value ) {
1435 $results[ $input_key ] = $fields_config;
1436
1437 $default = null;
1438 if ( isset( $fields_config['default'] ) ) {
1439 $default = $fields_config['default'];
1440 }
1441
1442 $results[ $input_key ]['value'] = PixCustomifyPlugin()->get_option( $input_key, $default );
1443 }
1444
1445 foreach ( $fields_config as $i => $subarray ) {
1446 $this->get_fields_by_key( $subarray, $key, $value, $results, $i );
1447 }
1448 }
1449
1450 /**
1451 * Return a script for flexibly localizing data to a window property.
1452 *
1453 * Unlike wp_localize_script() that simply creates a variable and assigns it the value,
1454 * thus overwriting anything that may have been in that variable, we will output a script that
1455 * will test if the variable exists and only overwrite the first level nodes, not everything.
1456 *
1457 * @since 2.7.0
1458 *
1459 * @param string $object_name Name of the variable that will contain the data.
1460 * @param array $l10n Array of data to localize.
1461 *
1462 * @return bool True on success, false on failure.
1463 */
1464 public static function getlocalizeToWindowScript( $object_name, $l10n ) {
1465 $script = "window.$object_name = window.$object_name || parent.$object_name || {};\n";
1466
1467 foreach ( (array) $l10n as $key => $value ) {
1468 if ( is_scalar( $value ) ) {
1469 $value = html_entity_decode( (string) $value, ENT_QUOTES, 'UTF-8' );
1470 }
1471
1472 $script .= "$object_name.$key = " . wp_json_encode( $value ) . ";\n";
1473 }
1474
1475 return $script;
1476 }
1477
1478 /* SANITIZATION HELPERS */
1479
1480 /**
1481 * Sanitize the checkbox.
1482 *
1483 * @param boolean $input .
1484 *
1485 * @return boolean true if is 1 or '1', false if anything else
1486 */
1487 function setting_sanitize_checkbox( $input ) {
1488 if ( 1 == $input ) {
1489 return true;
1490 } else {
1491 return false;
1492 }
1493 }
1494
1495 /**
1496 * Main PixCustomify_Customizer Instance
1497 *
1498 * Ensures only one instance of PixCustomify_Customizer is loaded or can be loaded.
1499 *
1500 * @since 2.4.0
1501 * @static
1502 *
1503 * @return PixCustomify_Customizer Main PixCustomify_Customizer instance
1504 */
1505 public static function instance() {
1506
1507 if ( is_null( self::$_instance ) ) {
1508 self::$_instance = new self();
1509 }
1510
1511 return self::$_instance;
1512 } // End instance ()
1513
1514 /**
1515 * Cloning is forbidden.
1516 *
1517 * @since 2.4.0
1518 */
1519 public function __clone() {
1520
1521 _doing_it_wrong( __FUNCTION__,esc_html__( 'You should not do that!', 'customify' ), null );
1522 }
1523
1524 /**
1525 * Unserializing instances of this class is forbidden.
1526 *
1527 * @since 2.4.0
1528 */
1529 public function __wakeup() {
1530
1531 _doing_it_wrong( __FUNCTION__, esc_html__( 'You should not do that!', 'customify' ), null );
1532 }
1533 }
1534
1535 endif;
1536