PluginProbe
Customify / 2.3.4
Customify v2.3.4
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 / class-pixcustomify.php

class-pixcustomify.php in Customify 2.3.4, at class-pixcustomify.php

2,743 lines 81.4 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * PixCustomify.
4 * @package PixCustomify
5 * @author Pixelgrade <contact@pixelgrade.com>
6 * @license GPL-2.0+
7 * @link https://pixelgrade.com
8 * @copyright 2014-2018 Pixelgrade
9 */
10
11 /**
12 * Plugin class.
13 * @package PixCustomify
14 * @author Pixelgrade <contact@pixelgrade.com>
15 */
16 class PixCustomifyPlugin {
17
18 /**
19 * Plugin version, used for cache-busting of style and script file references.
20 * @since 1.5.0
21 * @const string
22 */
23 protected $_version;
24 /**
25 * Unique identifier for your plugin.
26 * Use this value (not the variable name) as the text domain when internationalizing strings of text. It should
27 * match the Text Domain file header in the main plugin file.
28 * @since 1.0.0
29 * @var string
30 */
31 protected $plugin_slug = 'customify';
32
33 /**
34 * Instance of this class.
35 * @since 1.5.0
36 * @var object
37 */
38 protected static $_instance = null;
39
40 /**
41 * Slug of the plugin screen.
42 * @since 1.0.0
43 * @var string
44 */
45 protected $plugin_screen_hook_suffix = null;
46
47 public $display_admin_menu = false;
48
49 private $config;
50
51 private $customizer_config;
52
53 public $plugin_settings;
54
55 protected $localized = array();
56
57 protected $current_values = array();
58
59 protected $options_list = array();
60
61 protected $media_queries = array();
62
63 protected $opt_name;
64
65 protected $typo_settings;
66
67 protected $google_fonts = null;
68
69 protected $theme_fonts = null;
70
71 // these properties will get 'px' as a default unit
72 protected static $pixel_dependent_css_properties = array(
73 'width',
74 'max-width',
75 'min-width',
76
77 'height',
78 'max-height',
79 'min-height',
80
81 'padding',
82 'padding-left',
83 'padding-right',
84 'padding-top',
85 'padding-bottom',
86
87 'margin',
88 'margin-right',
89 'margin-left',
90 'margin-top',
91 'margin-bottom',
92
93 'right',
94 'left',
95 'top',
96 'bottom',
97
98 'font-size',
99 'letter-spacing',
100
101 'border-size',
102 'border-width',
103 'border-bottom-width',
104 'border-left-width',
105 'border-right-width',
106 'border-top-width'
107 );
108
109 protected $jetpack_default_modules = array();
110 protected $jetpack_blocked_modules = array();
111 protected $jetpack_sharing_default_options = array();
112
113 /**
114 * The main plugin file.
115 * @var string
116 * @access public
117 * @since 1.5.0
118 */
119 public $file;
120
121 /**
122 * Style Manager class object.
123 * @var Customify_Style_Manager
124 * @access public
125 * @since 1.0.0
126 */
127 public $style_manager = null;
128
129 /**
130 * Gutenberg class object.
131 * @var Customify_Gutenberg
132 * @access public
133 * @since 2.2.0
134 */
135 public $gutenberg = null;
136
137 /**
138 * Minimal Required PHP Version
139 * @var string
140 * @access private
141 * @since 1.5.0
142 */
143 private $minimalRequiredPhpVersion = 5.2;
144
145 protected function __construct( $file, $version = '1.0.0' ) {
146 //the main plugin file (the one that loads all this)
147 $this->file = $file;
148 //the current plugin version
149 $this->_version = $version;
150
151 if ( $this->php_version_check() ) {
152 // Only load and run the init function if we know PHP version can parse it.
153 $this->init();
154 }
155 }
156
157 /**
158 * Initialize plugin
159 */
160 private function init() {
161 // We don't want to put extra load on the heartbeat AJAX request.
162 if ( wp_doing_ajax() && isset( $_REQUEST['action'] ) && 'heartbeat' === $_REQUEST['action'] ) {
163 return;
164 }
165
166 // Load the config file
167 $this->config = $this->get_config();
168 // Load the plugin's settings from the DB
169 $this->plugin_settings = get_option( $this->config['settings-key'] );
170
171 /* Initialize the Style Manager logic. */
172 require_once( $this->get_base_path() . 'includes/class-customify-style-manager.php' );
173 if ( is_null( $this->style_manager ) ) {
174 $this->style_manager = Customify_Style_Manager::instance();
175 }
176
177 /* Initialize the Gutenberg logic. */
178 require_once( $this->get_base_path() . 'includes/class-customify-gutenberg.php' );
179 if ( is_null( $this->gutenberg ) ) {
180 $this->gutenberg = Customify_Gutenberg::instance();
181 }
182
183 // Register all the needed hooks
184 $this->register_hooks();
185 }
186
187 /**
188 * Register our actions and filters
189 */
190 function register_hooks() {
191
192 /*
193 * Load plugin text domain
194 */
195 add_action( 'init', array( $this, 'load_plugin_textdomain' ) );
196
197 /*
198 * Load the upgrade logic.
199 */
200 add_action( 'admin_init', array( $this, 'upgrade' ) );
201
202 /*
203 * Prepare and load the configuration
204 */
205 $this->init_plugin_configs();
206
207 // We need to load the configuration as late as possible so we allow all that want to influence it
208 // We need the init hook and not after_setup_theme because there a number of plugins that fire up on init (like certain modules from Jetpack)
209 // We need to be able to load things like components configs depending on those firing up or not
210 // DO NOT TRY to use the Customify values before this!
211 add_action( 'init', array( $this, 'load_plugin_configs' ), 15 );
212 // Also handle the force clearing of the cached config. Since we can't know wo can influence it, we need to be proactive.
213 add_action( 'activated_plugin', array( $this, 'clear_customizer_config_cache' ), 10 );
214 add_action( 'deactivated_plugin', array( $this, 'clear_customizer_config_cache' ), 10 );
215 add_action( 'switch_theme', array( $this, 'clear_customizer_config_cache' ), 10 );
216 add_action( 'upgrader_process_complete', array( $this, 'clear_customizer_config_cache' ), 10 );
217
218 /*
219 * Now setup the admin side of things
220 */
221 // Starting with the menu item for this plugin
222 add_action( 'admin_menu', array( $this, 'add_plugin_admin_menu' ) );
223
224 // Add an action link pointing to the options page.
225 $plugin_basename = plugin_basename( plugin_dir_path( $this->file ) . 'pixcustomify.php' );
226 add_filter( 'plugin_action_links_' . $plugin_basename, array( $this, 'add_action_links' ) );
227
228 // Load admin style sheet and JavaScript.
229 add_action( 'admin_enqueue_scripts', array( $this, 'enqueue_admin_styles' ) );
230 add_action( 'admin_enqueue_scripts', array( $this, 'enqueue_admin_scripts' ) );
231
232 /*
233 * Now it's time for the Customizer logic to kick in
234 */
235 // Styles for the Customizer
236 add_action( 'customize_controls_init', array( $this, 'register_admin_customizer_styles' ), 10 );
237 add_action( 'customize_controls_enqueue_scripts', array( $this, 'enqueue_admin_customizer_styles' ), 10 );
238 // Scripts enqueued in the Customizer
239 add_action( 'customize_controls_init', array( $this, 'register_admin_customizer_scripts' ), 15 );
240 add_action( 'customize_controls_enqueue_scripts', array( $this, 'enqueue_admin_customizer_scripts' ), 15 );
241
242 // Scripts enqueued only in the theme preview
243 add_action( 'customize_preview_init', array( $this, 'customizer_live_preview_register_scripts' ), 10 );
244 add_action( 'customize_preview_init', array( $this, 'customizer_live_preview_enqueue_scripts' ), 99999 );
245
246 // Add extra settings data to _wpCustomizeSettings.settings of the parent window.
247 add_action( 'customize_controls_print_footer_scripts', array( $this, 'customize_pane_settings_additional_data' ), 10000 );
248
249 // The frontend effects of the Customizer controls
250 $load_location = $this->get_plugin_setting( 'style_resources_location', 'wp_head' );
251
252 add_action( $load_location, array( $this, 'output_dynamic_style' ), 99 );
253 add_action( 'wp_head', array( $this, 'output_typography_dynamic_script' ), 10 );
254 add_action( 'wp_head', array( $this, 'output_typography_dynamic_style' ), 10 );
255
256 add_action( 'customize_register', array( $this, 'remove_default_sections' ), 11 );
257 add_action( 'customize_register', array( $this, 'register_customizer' ), 12 );
258 // Maybe the theme has instructed us to do things like removing sections or controls.
259 add_action( 'customize_register', array( $this, 'maybe_process_config_extras' ), 13 );
260
261 if ( $this->get_plugin_setting( 'enable_editor_style', true ) ) {
262 add_action( 'admin_enqueue_scripts', array( $this, 'script_to_add_customizer_settings_into_wp_editor' ), 10, 1 );
263 }
264
265 add_action( 'rest_api_init', array( $this, 'add_rest_routes_api' ) );
266
267 /*
268 * Development related
269 */
270 if ( defined( 'CUSTOMIFY_DEV_FORCE_DEFAULTS' ) && true === CUSTOMIFY_DEV_FORCE_DEFAULTS ) {
271 // If the development constant CUSTOMIFY_DEV_FORCE_DEFAULTS has been defined we will not save anything in the database
272 // Always go with the default
273 add_filter( 'customize_changeset_save_data', array( $this, 'prevent_changeset_save_in_devmode' ), 50, 2 );
274 // Add a JS to display a notification
275 add_action( 'customize_controls_print_footer_scripts', array( $this, 'prevent_changeset_save_in_devmode_notification' ), 100 );
276 }
277 }
278
279 /**
280 * Handle the logic to upgrade between versions. It will run only one per version change.
281 */
282 public function upgrade() {
283 $customify_dbversion = get_option( 'customify_dbversion', '0.0.1' );
284 if ( $this->get_version() === $customify_dbversion ) {
285 return;
286 }
287
288 // For versions, previous of version 2.0.0 (the Color Palettes v2.0 release).
289 if ( version_compare( $customify_dbversion, '2.0.0', '<' ) ) {
290 // Delete the option holding the fact that the user offered feedback.
291 delete_option( 'style_manager_user_feedback_provided' );
292 }
293
294 // Put the current version in the database.
295 update_option( 'customify_dbversion', $this->get_version(), true );
296 }
297
298 /**
299 * Initialize Configs, Options and Values methods.
300 */
301 function init_plugin_configs() {
302 $this->customizer_config = get_option( 'pixcustomify_config' );
303
304 // no option so go for default.
305 if ( empty( $this->customizer_config ) ) {
306 $this->customizer_config = $this->get_config_option( 'default_options' );
307 }
308
309 if ( empty( $this->customizer_config ) ) {
310 $this->customizer_config = array();
311 }
312 }
313
314 /**
315 * Load the plugin configuration and options.
316 */
317 function load_plugin_configs() {
318
319 $this->load_customizer_config();
320
321 $this->opt_name = $this->localized['options_name'] = $this->customizer_config['opt-name'];
322 $this->options_list = $this->get_options();
323
324 // Load the current options values.
325 $this->current_values = $this->get_current_values();
326
327 if ( $this->import_button_exists() ) {
328 $this->localized['import_rest_url'] = get_rest_url( '/customify/1.0/' );
329 $this->localized['import_rest_nonce'] = wp_create_nonce( 'wp_rest' );
330
331 $this->register_import_api();
332 }
333
334 $this->localized['theme_fonts'] = $this->theme_fonts = Customify_Font_Selector::instance()->get_theme_fonts();
335
336 $this->localized['ajax_url'] = admin_url( 'admin-ajax.php' );
337 $this->localized['style_manager_user_feedback_nonce'] = wp_create_nonce( 'customify_style_manager_user_feedback' );
338 $this->localized['style_manager_user_feedback_provided'] = get_option( 'style_manager_user_feedback_provided', false );
339 }
340
341 /**
342 * Set the customizer configuration.
343 *
344 * @since 2.2.1
345 *
346 * @param bool $skip_cache Optional. Whether to use the cached config or generate a new one.
347 */
348 protected function load_customizer_config( $skip_cache = false ) {
349 // First try and get the cached data
350 $data = get_option( $this->_get_customizer_config_cache_key() );
351
352 // We don't force skip the cache for AJAX requests for performance reasons.
353 if ( ! wp_doing_ajax() && defined('CUSTOMIFY_ALWAYS_GENERATE_CUSTOMIZER_CONFIG' ) && true === CUSTOMIFY_ALWAYS_GENERATE_CUSTOMIZER_CONFIG ) {
354 $skip_cache = true;
355 }
356
357 // For performance reasons, we will ONLY regenerate when in the WP ADMIN area or via an ADMIN AJAX call.
358 if ( false !== $data && false === $skip_cache && ! is_admin() && ! is_customize_preview() ) {
359 $this->customizer_config = $data;
360 return;
361 }
362
363 // Get the cache data expiration timestamp.
364 $expire_timestamp = get_option( $this->_get_customizer_config_cache_key() . '_timestamp' );
365
366 // The data isn't set, is expired or we were instructed to skip the cache; we need to regenerate the config.
367 if ( true === $skip_cache || false === $data || false === $expire_timestamp || $expire_timestamp < time() ) {
368 // Allow themes or other plugins to filter the config.
369 // We use $this->customizer_config so we can start from whatever default configuration it may be.
370 $data = apply_filters( 'customify_filter_fields', $this->customizer_config );
371 // We apply a second filter for those that wish to work with the final config and not rely on a a huge priority number.
372 $data = apply_filters( 'customify_final_config', $data );
373
374 // Cache the data in an option for 6 hours
375 update_option( $this->_get_customizer_config_cache_key() , $data, true );
376 update_option( $this->_get_customizer_config_cache_key() . '_timestamp' , time() + 6 * HOUR_IN_SECONDS, true );
377 }
378
379 $this->customizer_config = $data;
380 return;
381 }
382
383 /**
384 * Clear the customizer config cache.
385 *
386 * @since 2.2.1
387 */
388 public function clear_customizer_config_cache() {
389 delete_option( $this->_get_customizer_config_cache_key() );
390 delete_option( $this->_get_customizer_config_cache_key() . '_timestamp' );
391 }
392
393 /**
394 * Get the customizer config cache key.
395 *
396 * @since 2.2.1
397 *
398 * @return string
399 */
400 private function _get_customizer_config_cache_key() {
401 return 'customify_customizer_config';
402 }
403
404 public function get_version() {
405 return $this->_version;
406 }
407
408 public function get_slug() {
409 return $this->plugin_slug;
410 }
411
412 /**
413 * Load the plugin text domain for translation.
414 * @since 1.0.0
415 */
416 function load_plugin_textdomain() {
417 $domain = $this->plugin_slug;
418 load_plugin_textdomain( $domain, false, basename( dirname( $this->file ) ) . '/languages/' );
419 }
420
421 /** === RESOURCES === **/
422
423 /**
424 * Register Customizer admin styles
425 */
426 function register_admin_customizer_styles() {
427 wp_register_style( 'customify_select2', plugins_url( 'js/select2/css/select2.css', $this->file ), array(), $this->_version );
428 wp_register_style( 'customify_style', plugins_url( 'css/customizer.css', $this->file ), array( 'customify_select2' ), $this->_version );
429 }
430
431 /**
432 * Enqueue Customizer admin styles
433 */
434 function enqueue_admin_customizer_styles() {
435 wp_enqueue_style( 'customify_style' );
436 }
437
438 /**
439 * Register Customizer admin scripts
440 */
441 function register_admin_customizer_scripts() {
442
443 wp_register_script( 'customify_select2', plugins_url( 'js/select2/js/select2.js', $this->file ), array( 'jquery' ), $this->_version );
444 wp_register_script( 'jquery-react', plugins_url( 'js/jquery-react.js', $this->file ), array( 'jquery' ), $this->_version );
445
446 wp_register_script( 'customify-scale', plugins_url( 'js/customizer/scale-iframe.js', $this->file ), array( 'jquery' ), $this->_version );
447 wp_register_script( 'customify-fontselectfields', plugins_url( 'js/customizer/font-select-fields.js', $this->file ), array( 'jquery' ), $this->_version );
448
449 wp_register_script( $this->plugin_slug . '-customizer-scripts', plugins_url( 'js/customizer.js', $this->file ), array(
450 'jquery',
451 'customify_select2',
452 'underscore',
453 'customize-controls',
454 'customify-fontselectfields',
455
456 'customify-scale',
457 ), $this->_version );
458 }
459
460 /**
461 * Enqueue Customizer admin scripts
462 */
463 function enqueue_admin_customizer_scripts() {
464 wp_enqueue_script( 'jquery-react' );
465 wp_enqueue_script( $this->plugin_slug . '-customizer-scripts' );
466
467 wp_localize_script( $this->plugin_slug . '-customizer-scripts', 'customify_settings', apply_filters( 'customify_localized_js_settings', $this->localized ) );
468 }
469
470 /** Register Customizer scripts loaded only on previewer page */
471 function customizer_live_preview_register_scripts() {
472 wp_register_script( $this->plugin_slug . 'CSSOM', plugins_url( 'js/CSSOM.js', $this->file ), array( 'jquery' ), $this->_version, true );
473 wp_register_script( $this->plugin_slug . 'cssUpdate', plugins_url( 'js/jquery.cssUpdate.js', $this->file ), array( 'jquery' ), $this->_version, true );
474 wp_register_script( $this->plugin_slug . '-previewer-scripts', plugins_url( 'js/customizer_preview.js', $this->file ), array(
475 'jquery',
476 'customize-preview',
477 $this->plugin_slug . 'CSSOM',
478 $this->plugin_slug . 'cssUpdate'
479 ), $this->_version, true );
480 }
481
482 /** Enqueue Customizer scripts loaded only on previewer page */
483 function customizer_live_preview_enqueue_scripts() {
484 wp_enqueue_script( $this->plugin_slug . '-previewer-scripts' );
485
486 // when a live preview field is in action we need to know which props need 'px' as defaults
487 $this->localized['px_dependent_css_props'] = self::$pixel_dependent_css_properties;
488
489 wp_localize_script( $this->plugin_slug . '-previewer-scripts', 'customify_settings', $this->localized );
490 }
491
492 /**
493 * Add dynamic style only on the previewer page
494 */
495
496 /**
497 * Settings page styles
498 */
499 function enqueue_admin_styles() {
500
501 if ( ! isset( $this->plugin_screen_hook_suffix ) ) {
502 return;
503 }
504
505 $screen = get_current_screen();
506 if ( $screen->id == $this->plugin_screen_hook_suffix ) {
507 wp_enqueue_style( $this->plugin_slug . '-admin-styles', plugins_url( 'css/admin.css', $this->file ), array(), $this->_version );
508 }
509 }
510
511 /**
512 * Settings page scripts
513 */
514 function enqueue_admin_scripts() {
515
516 if ( ! isset( $this->plugin_screen_hook_suffix ) ) {
517 return;
518 }
519
520 $screen = get_current_screen();
521 if ( $screen->id == $this->plugin_screen_hook_suffix ) {
522 wp_enqueue_script( $this->plugin_slug . '-admin-script', plugins_url( 'js/admin.js', $this->file ), array( 'jquery' ), $this->_version );
523 wp_localize_script( $this->plugin_slug . '-admin-script', 'customify_settings', array(
524 'ajax_url' => admin_url( 'admin-ajax.php' ),
525 'wp_rest' => array(
526 'root' => esc_url_raw( rest_url() ),
527 'nonce' => wp_create_nonce( 'wp_rest' ),
528 'customify_settings_nonce' => wp_create_nonce( 'customify_settings_nonce' )
529 ),
530 ) );
531 }
532
533 wp_localize_script( $this->plugin_slug . '-customizer-scripts', 'WP_API_Settings', array(
534 'root' => esc_url_raw( rest_url() ),
535 'nonce' => wp_create_nonce( 'wp_rest' )
536 ) );
537 }
538
539 function add_rest_routes_api(){
540 register_rest_route( 'customify/v1', '/delete_theme_mod', array(
541 'methods' => 'POST',
542 'callback' => array( $this, 'delete_theme_mod' ),
543 'permission_callback' => array( $this, 'permission_nonce_callback' ),
544 ) );
545 }
546
547 function delete_theme_mod(){
548 if ( ! current_user_can( 'manage_options' ) ) {
549 wp_send_json_error('You don\'t have admin privileges.');
550 }
551
552 $config = apply_filters('customify_filter_fields', array() );
553
554 if ( empty( $config['opt-name'] ) ) {
555 wp_send_json_error('no option key');
556 }
557
558 $key = $config['opt-name'];
559
560 remove_theme_mod( $key );
561
562 wp_send_json_success('Deleted ' . $key . ' theme mod!');
563 }
564
565 function permission_nonce_callback() {
566 return wp_verify_nonce( $this->get_nonce(), 'customify_settings_nonce' );
567 }
568
569 private function get_nonce() {
570 $nonce = null;
571
572 if ( isset( $_REQUEST['customify_settings_nonce'] ) ) {
573 $nonce = wp_unslash( $_REQUEST['customify_settings_nonce'] );
574 } elseif ( isset( $_POST['customify_settings_nonce'] ) ) {
575 $nonce = wp_unslash( $_POST['customify_settings_nonce'] );
576 }
577
578 return $nonce;
579 }
580
581 /**
582 * Output CSS style generated by customizer
583 */
584 function output_dynamic_style() { ?>
585 <style id="customify_output_style">
586 <?php echo $this->get_dynamic_style(); ?>
587 </style>
588 <?php
589
590 /**
591 * from now on we output only style tags only for the preview purpose
592 * so don't cry if you see 30+ style tags for each section
593 */
594 if ( ! isset( $GLOBALS['wp_customize'] ) ) {
595 return;
596 }
597
598 foreach ( $this->options_list as $option_id => $options ) {
599
600 if ( isset( $options['type'] ) && $options['type'] === 'custom_background' ) {
601 $options['value'] = $this->get_option( $option_id );
602 $custom_background_output = $this->process_custom_background_field_output( $option_id, $options ); ?>
603
604 <style id="custom_background_output_for_<?php echo sanitize_html_class( $option_id ); ?>">
605 <?php
606 if ( ! empty( $custom_background_output )) {
607 echo $custom_background_output;
608 } ?>
609 </style>
610 <?php }
611
612 if ( ! isset( $options['live'] ) || $options['live'] !== true ) {
613 continue;
614 }
615
616 $this_value = $this->get_option( $option_id );
617 if ( ! empty( $options['css'] ) ) {
618 foreach ( $options['css'] as $key => $properties_set ) {
619 // We need to use a class because we may have multiple <style>s with the same "ID" for example
620 // when targeting the same property but with different selectors.
621 $unique_class = 'dynamic_setting_' . $option_id . '_property_' . str_replace( '-', '_', $properties_set['property'] ) . '_' . $key;
622
623 $inline_style = '<style class="' . sanitize_html_class( $unique_class ) . '" type="text/css">';
624
625 if ( isset( $properties_set['media'] ) && ! empty( $properties_set['media'] ) ) {
626 $inline_style .= '@media '. $properties_set['media'] . ' {';
627 }
628
629 if ( isset( $properties_set['selector'] ) && isset( $properties_set['property'] ) ) {
630 $css_output = $this->process_css_property($properties_set, $this_value);
631 if ( ! empty( $css_output ) ) {
632 $inline_style .= $css_output;
633 }
634 }
635
636 if ( isset( $properties_set['media'] ) && ! empty( $properties_set['media'] ) ) {
637 $inline_style .= '}';
638 }
639 $inline_style .= '</style>';
640
641 echo $inline_style;
642 }
643 }
644 }
645 }
646
647 function get_dynamic_style() {
648 $custom_css = '';
649
650 foreach ( $this->options_list as $option_id => $option ) {
651
652 if ( isset( $option['css'] ) && ! empty( $option['css'] ) ) {
653 // now process each
654 $custom_css .= $this->convert_setting_to_css( $option_id, $option['css'] );
655 }
656
657 if ( isset( $option['type'] ) && $option['type'] === 'custom_background' ) {
658 $option['value'] = $this->get_option( $option_id );
659 $custom_css .= $this->process_custom_background_field_output( $option_id, $option ) . PHP_EOL;
660 }
661 }
662
663 if ( ! empty( $this->media_queries ) ) {
664
665 foreach ( $this->media_queries as $media_query => $properties ) {
666
667 if ( empty( $properties ) ) {
668 continue;
669 }
670
671 $media_query_custom_css = '';
672
673 foreach ( $properties as $key => $property ) {
674 $property_settings = $property['property'];
675 $property_value = $property['value'];
676 $css_output = $this->process_css_property( $property_settings, $property_value );
677 if ( ! empty( $css_output ) ) {
678 $media_query_custom_css .= "\t" . $css_output . PHP_EOL;
679 }
680 }
681
682 if ( ! empty( $media_query_custom_css ) ) {
683 $media_query_custom_css = PHP_EOL . '@media ' . $media_query . " { " . PHP_EOL . PHP_EOL . $media_query_custom_css . "}" . PHP_EOL;
684 }
685
686 if ( ! empty( $media_query_custom_css ) ) {
687 $custom_css .= $media_query_custom_css;
688 }
689
690 }
691 }
692
693 return apply_filters( 'customify_dynamic_style', $custom_css );
694 }
695
696 protected function load_google_fonts() {
697 $fonts_path = plugin_dir_path( $this->file ) . 'features/customizer/controls/resources/google.fonts.php';
698
699 if ( file_exists( $fonts_path ) ) {
700 $this->google_fonts = require( $fonts_path );
701 }
702
703 if ( ! empty( $this->google_fonts ) ) {
704 return $this->google_fonts;
705 }
706
707 return false;
708 }
709
710 function output_typography_dynamic_style() {
711 $style = $this->get_typography_dynamic_style();
712
713 if ( ! empty( $style ) ) { ?>
714 <style id="customify_typography_output_style">
715 <?php echo $style; ?>
716 </style>
717 <?php }
718 }
719
720 function get_typography_dynamic_style() {
721 $output = '';
722
723 $this->get_typography_fields( $this->options_list, 'type', 'typography', $this->typo_settings );
724
725 if ( empty( $this->typo_settings ) ) {
726 return $output;
727 }
728
729 ob_start();
730 foreach ( $this->typo_settings as $font ) {
731 $selector = apply_filters( 'customify_typography_css_selector', $font['selector'], $font );
732
733 $load_all_weights = false;
734 if ( isset( $font['load_all_weights'] ) && $font['load_all_weights'] == 'true' ) {
735 $load_all_weights = true;
736 }
737
738 if ( isset( $selector ) && isset( $font['value'] ) && ! empty( $font['value'] ) ) {
739 // Make sure that the value is in the proper format
740 $value = PixCustomifyPlugin::decodeURIComponent( $font['value'] );
741 if ( is_string( $value ) ) {
742 $value = json_decode( $value, true );
743 }
744
745 // In case the value is null (most probably because the json_decode failed),
746 // try the default value (mostly for google fonts)
747 if ( $value === null ) {
748 $value = $this->get_font_defaults_value( $font['value'] );
749 }
750
751 // Shim the old case when the default was only the font name
752 if ( ! empty( $value ) && is_string( $value ) ) {
753 $value = array( 'font_family' => $value );
754 }
755
756 // Handle special logic for when the $value array is not an associative array
757 if ( ! $this->is_assoc( $value ) ) {
758 $value = $this->standardize_non_associative_font_default( $value );
759 }
760
761 // Bail if empty or we don't have an array
762 if ( empty( $value ) || ! is_array( $value ) ) {
763 continue;
764 }
765
766 $selected_variant = '';
767 if ( ! empty( $value['selected_variants'] ) ) {
768 if ( is_array( $value['selected_variants'] ) ) {
769 $selected_variant = $value['selected_variants'][0];
770 } else {
771 $selected_variant = $value['selected_variants'];
772 }
773 }
774
775 // First handle the case where we have the font-family in the selected variant (usually this means a custom font from our Fonto plugin)
776 if ( ! empty( $selected_variant ) && is_array( $selected_variant ) && ! empty( $selected_variant['font-family'] ) ) {
777 // The variant's font-family
778 echo $selector . " {\nfont-family: " . $selected_variant['font-family'] . ";\n";
779
780 if ( ! $load_all_weights ) {
781 // If this is a custom font (like from our plugin Fonto) with individual styles & weights - i.e. the font-family says it all
782 // we need to "force" the font-weight and font-style
783 if ( ! empty( $value['type'] ) && 'custom_individual' == $value['type'] ) {
784 $selected_variant['font-weight'] = '400 !important';
785 $selected_variant['font-style'] = 'normal !important';
786 }
787
788 // Output the font weight, if available
789 if ( ! empty( $selected_variant['font-weight'] ) ) {
790 echo "font-weight: " . $selected_variant['font-weight'] . ";\n";
791 }
792
793 // Output the font style, if available
794 if ( ! empty( $selected_variant['font-style'] ) ) {
795 echo "font-style: " . $selected_variant['font-style'] . ";\n";
796 }
797 }
798
799 echo "}\n";
800 } elseif ( isset( $value['font_family'] ) ) {
801 // The selected font family
802 echo $selector . " {\n font-family: " . $value['font_family'] . ";\n";
803
804 if ( ! empty( $selected_variant ) && ! $load_all_weights ) {
805 $weight_and_style = strtolower( $selected_variant );
806
807 $italic_font = false;
808
809 //determine if this is an italic font (the $weight_and_style is usually like '400' or '400italic' )
810 if ( strpos( $weight_and_style, 'italic' ) !== false ) {
811 $weight_and_style = str_replace( 'italic', '', $weight_and_style);
812 $italic_font = true;
813 }
814
815 if ( ! empty( $weight_and_style ) ) {
816 //a little bit of sanity check - in case it's not a number
817 if( $weight_and_style === 'regular' ) {
818 $weight_and_style = 'normal';
819 }
820 echo "font-weight: " . $weight_and_style . ";\n";
821 }
822
823 if ( $italic_font ) {
824 echo "font-style: italic;\n";
825 }
826 }
827
828 echo "}\n";
829 }
830 }
831 }
832
833 $output = ob_get_clean();
834
835 return $output;
836 }
837
838 function output_typography_dynamic_script() {
839
840 $script = $this->get_typography_dynamic_script();
841 if ( ! empty ( $script ) ) { ?>
842 <script type="text/javascript">
843 <?php echo $script; ?>
844 </script>
845 <?php }
846 }
847
848 function get_typography_dynamic_script() {
849 $output = '';
850
851 $this->get_typography_fields( $this->options_list, 'type', 'typography', $this->typo_settings );
852
853 if ( empty( $this->typo_settings ) ) {
854 return $output;
855 }
856
857 $families = '';
858
859 foreach ( $this->typo_settings as $id => $font ) {
860 if ( isset ( $font['value'] ) ) {
861
862 $load_all_weights = false;
863 if ( isset( $font['load_all_weights'] ) && $font['load_all_weights'] == 'true' ) {
864 $load_all_weights = true;
865 }
866
867 // shim the time when this was an array
868 // @todo Is this really needed? Or does it make sense?
869 if ( is_array( $font['value'] ) ) {
870 $font['value'] = stripslashes_deep( $font['value'] );
871 $font['value'] = json_encode( $font['value'] );
872 }
873
874 $value = wp_unslash( PixCustomifyPlugin::decodeURIComponent( $font['value'] ) );
875 if ( is_string( $value ) ) {
876 $value = json_decode( $value, true );
877 }
878
879 // In case the value is still null, try default value (mostly for google fonts)
880 if ( $value === null || ! is_array( $value ) ) {
881 $value = $this->get_font_defaults_value( str_replace( '"', '', $font['value'] ) );
882 }
883
884 // Bail if by this time we don't have a value of some sort
885 if ( empty( $value ) ) {
886 continue;
887 }
888
889 // Handle special logic for when the $value array is not an associative array
890 if ( ! $this->is_assoc( $value ) ) {
891 $value = $this->standardize_non_associative_font_default( $value );
892 }
893
894 // Bail if empty or we don't have an array
895 if ( empty( $value ) || ! is_array( $value ) ) {
896 continue;
897 }
898
899 if ( isset( $value['font_family'] ) && isset( $value['type'] ) && $value['type'] == 'google' ) {
900 $families .= "'" . $value['font_family'];
901
902 if ( $load_all_weights && is_array( $value['variants'] ) ) {
903 $families .= ":" . implode( ',', $value['variants'] );
904 } elseif ( isset( $value['selected_variants'] ) && ! empty( $value['selected_variants'] ) ) {
905 if ( is_array( $value['selected_variants'] ) ) {
906 $families .= ":" . implode( ',', $value['selected_variants'] );
907 } elseif ( is_string( $value['selected_variants'] ) || is_numeric( $value['selected_variants'] ) ) {
908 $families .= ":" . $value['selected_variants'];
909 }
910 } elseif ( isset( $value['variants'] ) && ! empty( $value['variants'] ) ) {
911 if ( is_array( $value['variants'] ) ) {
912 $families .= ":" . implode( ',', $value['variants'] );
913 } else {
914 $families .= ":" . $value['variants'];
915 }
916 }
917
918 if ( isset( $value['selected_subsets'] ) && ! empty( $value['selected_subsets'] ) ) {
919 if ( is_array( $value['selected_subsets'] ) ) {
920 $families .= ":" . implode( ',', $value['selected_subsets'] );
921 } else {
922 $families .= ":" . $value['selected_subsets'];
923 }
924 } elseif ( isset( $value['subsets'] ) && ! empty( $value['subsets'] ) ) {
925 if ( is_array( $value['subsets'] ) ) {
926 $families .= ":" . implode( ',', $value['subsets'] );
927 } else {
928 $families .= ":" . $value['subsets'];
929 }
930 }
931
932 $families .= '\',';
933 }
934 }
935 }
936
937 if ( ! empty ( $families ) && $this->get_plugin_setting( 'typography', '1' ) && $this->get_plugin_setting( 'typography_google_fonts', 1 ) ) {
938 ob_start();
939 ?>
940 if (typeof WebFont !== 'undefined') {<?php // if there is a WebFont object, use it ?>
941 WebFont.load({
942 google: {families: [<?php echo( rtrim( $families, ',' ) ); ?>]},
943 classes: false,
944 events: false
945 });
946 } else {<?php // basically when we don't have the WebFont object we create the google script dynamically ?>
947
948 var tk = document.createElement('script');
949 tk.src = '//ajax.googleapis.com/ajax/libs/webfont/1/webfont.js';
950 tk.type = 'text/javascript';
951
952 tk.onload = tk.onreadystatechange = function () {
953 WebFont.load({
954 google: {families: [<?php echo( rtrim( $families, ',' ) ); ?>]},
955 classes: false,
956 events: false
957 });
958 };
959
960 var s = document.getElementsByTagName('script')[0];
961 s.parentNode.insertBefore(tk, s);
962 }<?php
963 $output = ob_get_clean();
964 }
965
966 return $output;
967 }
968
969 /**
970 * Handle special logic for when the $value array is not an associative array
971 * Return a new associative array with proper keys
972 */
973 public function standardize_non_associative_font_default( $value ) {
974 // If the value provided is not array, simply return it
975 if ( ! is_array( $value ) ) {
976 return $value;
977 }
978
979 $new_value = array();
980
981 // Let's determine some type of font
982 if ( ! isset( $value[2] ) || 'google' == $value[2] ) {
983 $new_value = $this->get_font_defaults_value( $value[0] );
984 } else {
985 $new_value['type'] = $value[2];
986 }
987
988 if ( null == $new_value ) {
989 $new_value = array();
990 }
991
992 // The first entry is the font-family
993 if ( isset( $value[0] ) ) {
994 $new_value['font_family'] = $value[0];
995 }
996
997 // In case we don't have an associative array
998 // The second entry is the variants
999 if ( isset( $value[1] ) ) {
1000 $new_value['selected_variants'] = $value[1];
1001 }
1002
1003 return $new_value;
1004 }
1005
1006 /**
1007 *
1008 * @param $font_name
1009 *
1010 * @return null
1011 */
1012 public function get_font_defaults_value( $font_name ) {
1013
1014 if ( empty( $this->google_fonts ) ) {
1015 $this->load_google_fonts();
1016 }
1017
1018 if ( isset( $this->google_fonts[ $font_name ] ) ) {
1019 $value = $this->google_fonts[ $font_name ];
1020 $value['font_family'] = $font_name;
1021 $value['type'] = 'google';
1022
1023 return $value;
1024 } elseif ( isset( $this->theme_fonts[ $font_name ] ) ) {
1025 $value['type'] = 'theme_font';
1026 $value['src'] = $this->theme_fonts[ $font_name ]['src'];
1027 $value['variants'] = $this->theme_fonts[ $font_name ]['variants'];
1028 $value['font_family'] = $this->theme_fonts[ $font_name ]['family'];
1029
1030 return $value;
1031 }
1032
1033 return null;
1034 }
1035
1036 /**
1037 * Turn css options into a valid CSS output
1038 *
1039 * @param $option_id
1040 * @param array $css_config
1041 *
1042 * @return string
1043 */
1044 protected function convert_setting_to_css( $option_id, $css_config = array() ) {
1045 $output = '';
1046
1047 $this_value = $this->get_option( $option_id );
1048
1049 if ( empty( $css_config ) ) {
1050 return $output;
1051 }
1052
1053 foreach ( $css_config as $css_property ) {
1054
1055 if ( isset( $css_property['media'] ) && ! empty( $css_property['media'] ) ) {
1056 $this->media_queries[ $css_property['media'] ][ $option_id ] = array(
1057 'property' => $css_property,
1058 'value' => $this_value
1059 );
1060 continue;
1061 }
1062
1063 if ( isset( $css_property['selector'] ) && isset( $css_property['property'] ) ) {
1064 $output .= $this->process_css_property( $css_property, $this_value );
1065 }
1066 }
1067
1068 return $output;
1069 }
1070
1071 protected function process_css_property( $css_property, $this_value, $editor = false ) {
1072 $unit = '';
1073
1074 if ( isset( $css_property['unit'] ) ) {
1075 $unit = $css_property['unit'];
1076 }
1077
1078 // if the unit isn't specified but the property should have a unit force 'px' as it
1079 if ( empty( $unit ) && in_array( $css_property['property'], self::$pixel_dependent_css_properties ) ) {
1080 $unit = 'px';
1081 }
1082
1083 // lose the tons of tabs
1084 $css_property['selector'] = trim( preg_replace( '/\t+/', '', $css_property['selector'] ) );
1085
1086 $css_property['selector'] = apply_filters( 'customify_css_selector', $css_property['selector'], $css_property );
1087
1088 if ( empty( $css_property['selector'] ) ) {
1089 return '';
1090 }
1091 $this_property_output = $css_property['selector'] . ' { ' . $css_property['property'] . ': ' . $this_value . $unit . "; }" . PHP_EOL;
1092
1093 // Handle the value filter callback.
1094 if ( isset( $css_property['filter_value_cb'] ) ) {
1095 $this_value = $this->maybe_apply_filter( $css_property['filter_value_cb'], $this_value );
1096 }
1097
1098 // Handle output callback.
1099 if ( isset( $css_property['callback_filter'] ) && is_callable( $css_property['callback_filter'] ) ) {
1100 $this_property_output = call_user_func( $css_property['callback_filter'], $this_value, $css_property['selector'], $css_property['property'], $unit );
1101 }
1102
1103 // if ( ! empty( $editor ) && empty( $css_property['editor'] ) ) {
1104 // $this_property_output = '';
1105 // }
1106
1107 return $this_property_output;
1108 }
1109
1110 /**
1111 * Apply a filter (config) to a value.
1112 *
1113 * We currently handle filters like these:
1114 * // Elaborate filter config
1115 * array(
1116 * 'callback' => 'is_post_type_archive',
1117 * // The arguments we should pass to the check function.
1118 * // Think post types, taxonomies, or nothing if that is the case.
1119 * // It can be an array of values or a single value.
1120 * 'args' => array(
1121 * 'jetpack-portfolio',
1122 * ),
1123 * ),
1124 * // Simple filter - just the function name
1125 * 'is_404',
1126 *
1127 * @param array|string $filter
1128 * @param mixed $value The value to apply the filter to.
1129 *
1130 * @return mixed The filtered value.
1131 */
1132 public function maybe_apply_filter( $filter, $value ) {
1133 // Let's get some obvious things off the table.
1134 // On invalid data, we just return what we've received.
1135 if ( empty( $filter ) ) {
1136 return $value;
1137 }
1138
1139 // First, we handle the shorthand version: just a function name
1140 if ( is_string( $filter ) && is_callable( $filter ) ) {
1141 $value = call_user_func( $filter );
1142 } elseif ( is_array( $filter ) && ! empty( $filter['callback'] ) && is_callable( $filter['callback'] ) ) {
1143 if ( empty( $filter['args'] ) ) {
1144 $filter['args'] = array();
1145 }
1146 // The value is always the first argument.
1147 $filter['args'] = array( $value ) + $filter['args'];
1148
1149 $value = call_user_func_array( $filter['callback'], $filter['args'] );
1150 }
1151
1152 return $value;
1153 }
1154
1155 protected function process_custom_background_field_output( $option_id, $options ) {
1156 $selector = $output = '';
1157
1158 if ( ! isset( $options['value'] ) ) {
1159 return false;
1160 }
1161 $value = $options['value'];
1162
1163 if ( ! isset( $options['output'] ) ) {
1164 return $selector;
1165 } elseif ( is_string( $options['output'] ) ) {
1166 $selector = $options['output'];
1167 } elseif ( is_array( $options['output'] ) ) {
1168 $selector = implode( ' ', $options['output'] );
1169 }
1170
1171 // Loose the ton of tabs.
1172 $selector = trim( preg_replace( '/\t+/', '', $selector ) );
1173
1174 $output .= $selector . " {";
1175 if ( isset( $value['background-image'] ) && ! empty( $value['background-image'] ) ) {
1176 $output .= "background-image: url( " . $value['background-image'] . ");";
1177 } else {
1178 $output .= "background-image: none;";
1179 }
1180
1181 if ( isset( $value['background-repeat'] ) && ! empty( $value['background-repeat'] ) ) {
1182 $output .= "background-repeat:" . $value['background-repeat'] . ";";
1183 }
1184
1185 if ( isset( $value['background-position'] ) && ! empty( $value['background-position'] ) ) {
1186 $output .= "background-position:" . $value['background-position'] . ";";
1187 }
1188
1189 if ( isset( $value['background-size'] ) && ! empty( $value['background-size'] ) ) {
1190 $output .= "background-size:" . $value['background-size'] . ";";
1191 }
1192
1193 if ( isset( $value['background-attachment'] ) && ! empty( $value['background-attachment'] ) ) {
1194 $output .= "background-attachment:" . $value['background-attachment'] . ";";
1195 }
1196 $output .= "}\n";
1197
1198 return $output;
1199 }
1200
1201 /**
1202 * add our customizer styling edits into the wp_editor
1203 */
1204 function script_to_add_customizer_settings_into_wp_editor() {
1205
1206 ob_start();
1207 $this->output_typography_dynamic_script();
1208 $this->output_typography_dynamic_style();
1209 $this->output_dynamic_style();
1210
1211 $custom_css = ob_get_clean();
1212
1213 ob_start(); ?>
1214 (function ($) {
1215 $(window).load(function () {
1216 /**
1217 * @param iframe_id the id of the frame you want to append the style
1218 * @param style_element the style element you want to append - boooom
1219 */
1220 var append_script_to_iframe = function (ifrm_id, scriptEl) {
1221 var myIframe = document.getElementById(ifrm_id);
1222
1223 var script = myIframe.contentWindow.document.createElement("script");
1224 script.type = "text/javascript";
1225 script.innerHTML = scriptEl.innerHTML;
1226
1227 myIframe.contentWindow.document.head.appendChild(script);
1228 };
1229
1230 var append_style_to_iframe = function (ifrm_id, styleElment) {
1231 var ifrm = window.frames[ifrm_id];
1232 if ( typeof ifrm === "undefined" ) {
1233 return;
1234 }
1235 ifrm = ( ifrm.contentDocument || ifrm.contentDocument || ifrm.document );
1236 var head = ifrm.getElementsByTagName('head')[0];
1237
1238 if (typeof styleElment !== "undefined") {
1239 head.appendChild(styleElment);
1240 }
1241 };
1242
1243 var xmlString = <?php echo json_encode( str_replace( "\n", "", $custom_css ) ); ?>,
1244 parser = new DOMParser(),
1245 doc = parser.parseFromString(xmlString, "text/html");
1246
1247 if (typeof window.frames['content_ifr'] !== 'undefined') {
1248
1249 $.each(doc.head.childNodes, function (key, el) {
1250 if (typeof el !== "undefined" && typeof el.tagName !== "undefined") {
1251
1252 switch (el.tagName) {
1253 case 'STYLE' :
1254 append_style_to_iframe('content_ifr', el);
1255 break;
1256 case 'SCRIPT' :
1257 append_script_to_iframe('content_ifr', el);
1258 break;
1259 default:
1260 break;
1261 }
1262 }
1263 });
1264 }
1265 });
1266 })(jQuery);
1267 <?php
1268 $script = ob_get_clean();
1269 wp_add_inline_script( 'editor', $script );
1270
1271 }
1272
1273 /**
1274 * Register the administration menu for this plugin into the WordPress Dashboard menu.
1275 */
1276 function add_plugin_admin_menu() {
1277 $this->plugin_screen_hook_suffix = add_options_page( __( 'Customify', $this->plugin_slug ), __( 'Customify', $this->plugin_slug ), 'edit_plugins', $this->plugin_slug, array(
1278 $this,
1279 'display_plugin_admin_page'
1280 ) );
1281 }
1282
1283 /**
1284 * Render the settings page for this plugin.
1285 */
1286 function display_plugin_admin_page() {
1287 include_once( 'views/admin.php' );
1288 }
1289
1290 /**
1291 * Add settings action link to the plugins page.
1292 */
1293 function add_action_links( $links ) {
1294 return array_merge( array( 'settings' => '<a href="' . admin_url( 'options-general.php?page=pixcustomify' ) . '">' . __( 'Settings', $this->plugin_slug ) . '</a>' ), $links );
1295 }
1296
1297 protected function register_customizer_controls() {
1298
1299 // first get the base customizer extend class
1300 require_once( $this->get_base_path() . '/features/customizer/class-Pix_Customize_Control.php' );
1301
1302 // now get all the controls
1303 $path = $this->get_base_path() . '/features/customizer/controls/';
1304 pixcustomify::require_all( $path );
1305 }
1306
1307 /**
1308 * Maybe process certain "commands" from the config.
1309 *
1310 * Mainly things like removing sections, controls, etc.
1311 *
1312 * @since 1.9.0
1313 *
1314 * @param WP_Customize_Manager $wp_customize
1315 */
1316 public function maybe_process_config_extras( $wp_customize ) {
1317 // Bail if we have no external theme config data.
1318 if ( empty( $this->customizer_config ) || ! is_array( $this->customizer_config ) ) {
1319 return;
1320 }
1321
1322 // Maybe remove panels
1323 if ( ! empty( $this->customizer_config['remove_panels'] ) ) {
1324 // Standardize it.
1325 if ( is_string( $this->customizer_config['remove_panels'] ) ) {
1326 $this->customizer_config['remove_panels'] = array( $this->customizer_config['remove_panels'] );
1327 }
1328
1329 foreach ( $this->customizer_config['remove_panels'] as $panel_id ) {
1330 $wp_customize->remove_panel( $panel_id );
1331 }
1332 }
1333
1334 // Maybe change panel props.
1335 if ( ! empty( $this->customizer_config['change_panel_props'] ) ) {
1336 foreach ( $this->customizer_config['change_panel_props'] as $panel_id => $panel_props ) {
1337 if ( ! is_array( $panel_props ) ) {
1338 continue;
1339 }
1340
1341 $panel = $wp_customize->get_panel( $panel_id );
1342 if ( empty( $panel ) || ! $panel instanceof WP_Customize_Panel ) {
1343 continue;
1344 }
1345
1346 $public_props = get_class_vars( get_class( $panel ) );
1347 foreach ( $panel_props as $prop_name => $prop_value ) {
1348
1349 if ( ! in_array( $prop_name, array_keys( $public_props ) ) ) {
1350 continue;
1351 }
1352
1353 $panel->$prop_name = $prop_value;
1354 }
1355 }
1356 }
1357
1358 // Maybe remove sections
1359 if ( ! empty( $this->customizer_config['remove_sections'] ) ) {
1360 // Standardize it.
1361 if ( is_string( $this->customizer_config['remove_sections'] ) ) {
1362 $this->customizer_config['remove_sections'] = array( $this->customizer_config['remove_sections'] );
1363 }
1364
1365 foreach ( $this->customizer_config['remove_sections'] as $section_id ) {
1366
1367 if ( 'widgets' === $section_id ) {
1368 global $wp_registered_sidebars;
1369
1370 foreach ( $wp_registered_sidebars as $widget => $settings ) {
1371 $wp_customize->remove_section( 'sidebar-widgets-' . $widget );
1372 }
1373 continue;
1374 }
1375
1376 $wp_customize->remove_section( $section_id );
1377 }
1378 }
1379
1380 // Maybe change section props.
1381 if ( ! empty( $this->customizer_config['change_section_props'] ) ) {
1382 foreach ( $this->customizer_config['change_section_props'] as $section_id => $section_props ) {
1383 if ( ! is_array( $section_props ) ) {
1384 continue;
1385 }
1386
1387 $section = $wp_customize->get_section( $section_id );
1388 if ( empty( $section ) || ! $section instanceof WP_Customize_Section ) {
1389 continue;
1390 }
1391
1392 $public_props = get_class_vars( get_class( $section ) );
1393 foreach ( $section_props as $prop_name => $prop_value ) {
1394
1395 if ( ! in_array( $prop_name, array_keys( $public_props ) ) ) {
1396 continue;
1397 }
1398
1399 $section->$prop_name = $prop_value;
1400 }
1401 }
1402 }
1403
1404 // Maybe remove settings
1405 if ( ! empty( $this->customizer_config['remove_settings'] ) ) {
1406 // Standardize it.
1407 if ( is_string( $this->customizer_config['remove_settings'] ) ) {
1408 $this->customizer_config['remove_settings'] = array( $this->customizer_config['remove_settings'] );
1409 }
1410
1411 foreach ( $this->customizer_config['remove_settings'] as $setting_id ) {
1412 $wp_customize->remove_setting( $setting_id );
1413 }
1414 }
1415
1416 // Maybe change setting props.
1417 if ( ! empty( $this->customizer_config['change_setting_props'] ) ) {
1418 foreach ( $this->customizer_config['change_setting_props'] as $setting_id => $setting_props ) {
1419 if ( ! is_array( $setting_props ) ) {
1420 continue;
1421 }
1422
1423 $setting = $wp_customize->get_setting( $setting_id );
1424 if ( empty( $setting ) || ! $setting instanceof WP_Customize_Setting ) {
1425 continue;
1426 }
1427
1428 $public_props = get_class_vars( get_class( $setting ) );
1429 foreach ( $setting_props as $prop_name => $prop_value ) {
1430
1431 if ( ! in_array( $prop_name, array_keys( $public_props ) ) ) {
1432 continue;
1433 }
1434
1435 $setting->$prop_name = $prop_value;
1436 }
1437 }
1438 }
1439
1440 // Maybe remove controls
1441 if ( ! empty( $this->customizer_config['remove_controls'] ) ) {
1442 // Standardize it.
1443 if ( is_string( $this->customizer_config['remove_controls'] ) ) {
1444 $this->customizer_config['remove_controls'] = array( $this->customizer_config['remove_controls'] );
1445 }
1446
1447 foreach ( $this->customizer_config['remove_controls'] as $control_id ) {
1448 $wp_customize->remove_control( $control_id );
1449 }
1450 }
1451
1452 // Maybe change control props.
1453 if ( ! empty( $this->customizer_config['change_control_props'] ) ) {
1454 foreach ( $this->customizer_config['change_control_props'] as $control_id => $control_props ) {
1455 if ( ! is_array( $control_props ) ) {
1456 continue;
1457 }
1458
1459 $control = $wp_customize->get_control( $control_id );
1460 if ( empty( $control ) || ! $control instanceof WP_Customize_Control ) {
1461 continue;
1462 }
1463
1464 $public_props = get_class_vars( get_class( $control ) );
1465 foreach ( $control_props as $prop_name => $prop_value ) {
1466
1467 if ( ! in_array( $prop_name, array_keys( $public_props ) ) ) {
1468 continue;
1469 }
1470
1471 $control->$prop_name = $prop_value;
1472 }
1473 }
1474 }
1475 }
1476
1477 /**
1478 * @param WP_Customize_Manager $wp_customize
1479 */
1480 function register_customizer( $wp_customize ) {
1481
1482 $this->register_customizer_controls();
1483
1484 $customizer_settings = $this->customizer_config;
1485
1486 if ( ! empty ( $customizer_settings ) ) {
1487
1488 // first check the very needed options name
1489 if ( empty( $customizer_settings['opt-name'] ) ) {
1490 return;
1491 }
1492 $options_name = $customizer_settings['opt-name'];
1493 $wp_customize->options_key = $options_name;
1494
1495 // let's check if we have sections or panels
1496 if ( isset( $customizer_settings['panels'] ) && ! empty( $customizer_settings['panels'] ) ) {
1497
1498 foreach ( $customizer_settings['panels'] as $panel_id => $panel_config ) {
1499
1500 if ( ! empty( $panel_id ) && isset( $panel_config['sections'] ) && ! empty( $panel_config['sections'] ) ) {
1501
1502 // If we have been explicitly given a panel ID we will use that
1503 if ( ! empty( $panel_config['panel_id'] ) ) {
1504 $panel_id = $panel_config['panel_id'];
1505 } else {
1506 $panel_id = $options_name . '[' . $panel_id . ']';
1507 }
1508
1509 $panel_args = array(
1510 'priority' => 10,
1511 'capability' => 'edit_theme_options',
1512 'title' => __( 'Panel title is required', 'customify' ),
1513 'description' => __( 'Description of what this panel does.', 'customify' ),
1514 'auto_expand_sole_section' => false,
1515 );
1516
1517 if ( isset( $panel_config['priority'] ) && ! empty( $panel_config['priority'] ) ) {
1518 $panel_args['priority'] = $panel_config['priority'];
1519 }
1520
1521 if ( isset( $panel_config['title'] ) && ! empty( $panel_config['title'] ) ) {
1522 $panel_args['title'] = $panel_config['title'];
1523 }
1524
1525 if ( isset( $panel_config['description'] ) && ! empty( $panel_config['description'] ) ) {
1526 $panel_args['description'] = $panel_config['description'];
1527 }
1528
1529 if ( isset( $panel_config['auto_expand_sole_section'] ) ) {
1530 $panel_args['auto_expand_sole_section'] = $panel_config['auto_expand_sole_section'];
1531 }
1532
1533
1534 $wp_customize->add_panel( $panel_id, $panel_args );
1535
1536 foreach ( $panel_config['sections'] as $section_id => $section_config ) {
1537 if ( ! empty( $section_id ) && isset( $section_config['options'] ) && ! empty( $section_config['options'] ) ) {
1538 $this->register_section( $panel_id, $section_id, $options_name, $section_config, $wp_customize );
1539 }
1540 }
1541 }
1542 }
1543 }
1544
1545 if ( isset( $customizer_settings['sections'] ) && ! empty( $customizer_settings['sections'] ) ) {
1546
1547 foreach ( $customizer_settings['sections'] as $section_id => $section_config ) {
1548 if ( ! empty( $section_id ) && isset( $section_config['options'] ) && ! empty( $section_config['options'] ) ) {
1549 $this->register_section( $panel_id = false, $section_id, $options_name, $section_config, $wp_customize );
1550 }
1551 }
1552 }
1553
1554 if ( $this->plugin_settings['enable_reset_buttons'] ) {
1555 // create a toolbar section which will be present all the time
1556 $reset_section_settings = array(
1557 'title' => 'Customify Toolbox',
1558 'capability' => 'manage_options',
1559 'priority' => 999999999,
1560 'options' => array(
1561 'reset_all_button' => array(
1562 'type' => 'button',
1563 'label' => 'Reset Customify',
1564 'action' => 'reset_customify',
1565 'value' => 'Reset'
1566 ),
1567 )
1568 );
1569
1570 $wp_customize->add_section(
1571 'customify_toolbar',
1572 $reset_section_settings
1573 );
1574
1575 $wp_customize->add_setting(
1576 'reset_customify',
1577 array()
1578 );
1579 $wp_customize->add_control( new Pix_Customize_Button_Control(
1580 $wp_customize,
1581 'reset_customify',
1582 array(
1583 'label' => __( 'Reset All Customify Options to Default', 'customify' ),
1584 'section' => 'customify_toolbar',
1585 'settings' => 'reset_customify',
1586 'action' => 'reset_customify',
1587 )
1588 ) );
1589 }
1590
1591 // register typekit options
1592 if ( isset( $customizer_settings['typekit_options'] ) ) {
1593
1594 // create a toolbar section which will be present all the time
1595 $reset_section_settings = array(
1596 'title' => 'Customify Typekit Options',
1597 'capability' => 'manage_options',
1598 'options' => array(
1599 'typkit_user' => array(
1600 'type' => 'text',
1601 'label' => 'Typekit Username',
1602 ),
1603 'typkit_password' => array(
1604 'type' => 'text',
1605 'label' => 'Typekit Username',
1606 ),
1607 )
1608 );
1609 }
1610 }
1611
1612 do_action( 'customify_create_custom_control', $wp_customize );
1613 }
1614
1615 /**
1616 * @param string $panel_id
1617 * @param string $section_key
1618 * @param string $options_name
1619 * @param array $section_config
1620 * @param WP_Customize_Manager $wp_customize
1621 */
1622 protected function register_section( $panel_id, $section_key, $options_name, $section_config, $wp_customize ) {
1623
1624 if ( isset( $this->plugin_settings['disable_customify_sections'] ) && isset( $this->plugin_settings['disable_customify_sections'][ $section_key ] ) ) {
1625 return;
1626 }
1627
1628 // If we have been explicitly given a section ID we will use that
1629 if ( ! empty( $section_config['section_id'] ) ) {
1630 $section_id = $section_config['section_id'];
1631 } else {
1632 $section_id = $options_name . '[' . $section_key . ']';
1633 }
1634
1635 // Add the new section to the Customizer, but only if it is not already added.
1636 if ( ! $wp_customize->get_section( $section_id ) ) {
1637 // Merge the section settings with the defaults
1638 $section_args = wp_parse_args( $section_config, array(
1639 'priority' => 10,
1640 'panel' => $panel_id,
1641 'capability' => 'edit_theme_options',
1642 'theme_supports' => '',
1643 'title' => esc_html__( 'Title Section is required', 'customify' ),
1644 'description' => '',
1645 'type' => 'default',
1646 'description_hidden' => false,
1647 ) );
1648
1649 $wp_customize->add_section( $section_id, $section_args );
1650 }
1651
1652 // Now go through each section option and add the fields
1653 foreach ( $section_config['options'] as $option_id => $option_config ) {
1654
1655 if ( empty( $option_id ) || ! isset( $option_config['type'] ) ) {
1656 continue;
1657 }
1658
1659 // If we have been explicitly given a setting ID we will use that
1660 if ( ! empty( $option_config['setting_id'] ) ) {
1661 $setting_id = $option_config['setting_id'];
1662 } else {
1663 $setting_id = $options_name . '[' . $option_id . ']';
1664 }
1665
1666 // Add the option config to the localized array so we can pass the info to JS.
1667 // @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.
1668 $this->localized['settings'][ $setting_id ] = $option_config;
1669
1670 // Generate a safe option ID (not the final setting ID) to us in HTML attributes like ID or class
1671 $this->localized['settings'][ $setting_id ]['html_safe_option_id'] = sanitize_html_class( $option_id );
1672
1673 $this->register_field( $section_id, $setting_id, $option_config, $wp_customize );
1674 }
1675
1676 }
1677
1678 /**
1679 * Register a Customizer field (setting and control).
1680 *
1681 * @see WP_Customize_Setting
1682 * @see WP_Customize_Control
1683 *
1684 * @param string $section_id
1685 * @param string $setting_id
1686 * @param array $field_config
1687 * @param WP_Customize_Manager $wp_customize
1688 */
1689 protected function register_field( $section_id, $setting_id, $field_config, $wp_customize ) {
1690
1691 $add_control = true;
1692 // defaults
1693 $setting_args = array(
1694 'default' => '',
1695 'capability' => 'edit_theme_options',
1696 'transport' => 'refresh',
1697 );
1698 $control_args = array(
1699 'priority' => 10,
1700 'label' => '',
1701 'section' => $section_id,
1702 'settings' => $setting_id,
1703 );
1704
1705 // sanitize settings
1706 if ( ! empty( $field_config['live'] ) || $field_config['type'] === 'font' ) {
1707 $setting_args['transport'] = 'postMessage';
1708 }
1709
1710 if ( isset( $field_config['default'] ) ) {
1711 $setting_args['default'] = $field_config['default'];
1712 }
1713
1714 if ( ! empty( $field_config['capability'] ) ) {
1715 $setting_args['capability'] = $field_config['capability'];
1716 }
1717
1718 // If the setting defines it's own type we will respect that, otherwise we will follow the global plugin setting.
1719 if ( ! empty( $field_config['setting_type'] ) ) {
1720 if ( 'option' === $field_config['setting_type'] ) {
1721 $setting_args['type'] = 'option';
1722 }
1723 } elseif ( $this->plugin_settings['values_store_mod'] === 'option' ) {
1724 $setting_args['type'] = 'option';
1725 }
1726
1727 // if we arrive here this means we have a custom field control
1728 switch ( $field_config['type'] ) {
1729
1730 case 'checkbox':
1731
1732 $setting_args['sanitize_callback'] = array( $this, 'setting_sanitize_checkbox' );
1733 break;
1734
1735 default:
1736 break;
1737 }
1738
1739 if ( ! empty( $field_config['sanitize_callback'] ) && is_callable( $field_config['sanitize_callback'] ) ) {
1740 $setting_args['sanitize_callback'] = $field_config['sanitize_callback'];
1741 }
1742
1743 // Add the setting
1744 $wp_customize->add_setting( $setting_id, $setting_args );
1745
1746 // now sanitize the control
1747 if ( ! empty( $field_config['label'] ) ) {
1748 $control_args['label'] = $field_config['label'];
1749 }
1750
1751 if ( ! empty( $field_config['priority'] ) ) {
1752 $control_args['priority'] = $field_config['priority'];
1753 }
1754
1755 if ( ! empty( $field_config['desc'] ) ) {
1756 $control_args['description'] = $field_config['desc'];
1757 }
1758
1759 if ( ! empty( $field_config['active_callback'] ) ) {
1760 $control_args['active_callback'] = $field_config['active_callback'];
1761 }
1762
1763
1764 $control_args['type'] = $field_config['type'];
1765
1766 // select the control type
1767 // but first init a default
1768 $control_class_name = 'Pix_Customize_Text_Control';
1769
1770 // If is a standard wp field type call it here and skip the rest.
1771 if ( in_array( $field_config['type'], array(
1772 'checkbox',
1773 'dropdown-pages',
1774 'url',
1775 'date',
1776 'time',
1777 'datetime',
1778 'week',
1779 'search'
1780 ) ) ) {
1781 $wp_customize->add_control( $setting_id . '_control', $control_args );
1782
1783 return;
1784 } elseif ( in_array( $field_config['type'], array(
1785 'radio',
1786 'select'
1787 ) ) && ! empty( $field_config['choices'] )
1788 ) {
1789 $control_args['choices'] = $field_config['choices'];
1790 $wp_customize->add_control( $setting_id . '_control', $control_args );
1791
1792 return;
1793 } elseif ( in_array( $field_config['type'], array( 'range' ) ) && ! empty( $field_config['input_attrs'] ) ) {
1794
1795 $control_args['input_attrs'] = $field_config['input_attrs'];
1796
1797 $wp_customize->add_control( $setting_id . '_control', $control_args );
1798 }
1799
1800 // If we arrive here this means we have a custom field control.
1801 switch ( $field_config['type'] ) {
1802
1803 case 'text':
1804 if ( isset( $field_config['live'] ) ) {
1805 $control_args['live'] = $field_config['live'];
1806 }
1807
1808 $control_class_name = 'Pix_Customize_Text_Control';
1809 break;
1810
1811 case 'textarea':
1812 if ( isset( $field_config['live'] ) ) {
1813 $control_args['live'] = $field_config['live'];
1814 }
1815
1816 $control_class_name = 'Pix_Customize_Textarea_Control';
1817 break;
1818
1819 case 'color':
1820 $control_class_name = 'WP_Customize_Color_Control';
1821 break;
1822
1823 case 'color_drop':
1824 $control_class_name = 'Pix_Customize_Color_Drop_Control';
1825 break;
1826
1827 case 'ace_editor':
1828 if ( isset( $field_config['live'] ) ) {
1829 $control_args['live'] = $field_config['live'];
1830 }
1831
1832 if ( isset( $field_config['editor_type'] ) ) {
1833 $control_args['editor_type'] = $field_config['editor_type'];
1834 }
1835
1836 $control_class_name = 'Pix_Customize_Ace_Editor_Control';
1837 break;
1838
1839 case 'upload':
1840 $control_class_name = 'WP_Customize_Upload_Control';
1841 break;
1842
1843 case 'image':
1844 $control_class_name = 'WP_Customize_Image_Control';
1845 break;
1846
1847 case 'media':
1848 $control_class_name = 'WP_Customize_Media_Control';
1849 break;
1850
1851 case 'custom_background':
1852 if ( isset( $field_config['field'] ) ) {
1853 $control_args['field'] = $field_config['field'];
1854 }
1855
1856 $control_class_name = 'Pix_Customize_Background_Control';
1857 break;
1858
1859 case 'cropped_image':
1860 case 'cropped_media': // 'cropped_media' no longer works
1861 if ( isset( $field_config['width'] ) ) {
1862 $control_args['width'] = $field_config['width'];
1863 }
1864
1865 if ( isset( $field_config['height'] ) ) {
1866 $control_args['height'] = $field_config['height'];
1867 }
1868
1869 if ( isset( $field_config['flex_width'] ) ) {
1870 $control_args['flex_width'] = $field_config['flex_width'];
1871 }
1872
1873 if ( isset( $field_config['flex_height'] ) ) {
1874 $control_args['flex_height'] = $field_config['flex_height'];
1875 }
1876
1877 if ( isset( $field_config['button_labels'] ) ) {
1878 $control_args['button_labels'] = $field_config['button_labels'];
1879 }
1880
1881 $control_class_name = 'WP_Customize_Cropped_Image_Control';
1882 break;
1883
1884 // Custom types
1885 case 'typography' :
1886 $use_typography = $this->get_plugin_setting( 'typography', '1' );
1887
1888 if ( $use_typography === false ) {
1889 $add_control = false;
1890 continue;
1891 }
1892
1893 $control_class_name = 'Pix_Customize_Typography_Control';
1894
1895 if ( isset( $field_config['backup'] ) ) {
1896 $control_args['backup'] = $field_config['backup'];
1897 }
1898
1899 if ( isset( $field_config['font_weight'] ) ) {
1900 $control_args['font_weight'] = $field_config['font_weight'];
1901 }
1902
1903 if ( isset( $field_config['subsets'] ) ) {
1904 $control_args['subsets'] = $field_config['subsets'];
1905 }
1906
1907 if ( isset( $field_config['recommended'] ) ) {
1908 $control_args['recommended'] = array_flip( $field_config['recommended'] );
1909 }
1910
1911 if ( isset( $field_config['load_all_weights'] ) ) {
1912 $control_args['load_all_weights'] = $field_config['load_all_weights'];
1913 }
1914
1915 if ( isset( $field_config['default'] ) ) {
1916 $control_args['default'] = $field_config['default'];
1917 }
1918
1919 break;
1920
1921 case 'font' :
1922 $use_typography = $this->get_plugin_setting( 'typography', '1' );
1923
1924 if ( $use_typography === false ) {
1925 $add_control = false;
1926 continue;
1927 }
1928
1929 $control_class_name = 'Pix_Customize_Font_Control';
1930
1931 if ( isset( $field_config['backup'] ) ) {
1932 $control_args['backup'] = $field_config['backup'];
1933 }
1934
1935 if ( isset( $field_config['font_weight'] ) ) {
1936 $control_args['font_weight'] = $field_config['font_weight'];
1937 }
1938
1939 if ( isset( $field_config['subsets'] ) ) {
1940 $control_args['subsets'] = $field_config['subsets'];
1941 }
1942
1943 if ( isset( $field_config['recommended'] ) ) {
1944 $control_args['recommended'] = array_flip( $field_config['recommended'] );
1945 }
1946
1947 if ( isset( $field_config['load_all_weights'] ) ) {
1948 $control_args['load_all_weights'] = $field_config['load_all_weights'];
1949 }
1950
1951 if ( isset( $field_config['default'] ) ) {
1952 $control_args['default'] = $field_config['default'];
1953 }
1954
1955 if ( isset( $field_config['fields'] ) ) {
1956 $control_args['fields'] = $field_config['fields'];
1957 }
1958 $control_args['live'] = true;
1959
1960 break;
1961
1962 case 'select2' :
1963 if ( ! isset( $field_config['choices'] ) || empty( $field_config['choices'] ) ) {
1964 return;
1965 }
1966
1967 $control_args['choices'] = $field_config['choices'];
1968
1969 $control_class_name = 'Pix_Customize_Select2_Control';
1970 break;
1971
1972 case 'sm_radio' :
1973 if ( ! isset( $field_config['choices'] ) || empty( $field_config['choices'] ) ) {
1974 return;
1975 }
1976
1977 $control_args['choices'] = $field_config['choices'];
1978
1979 $control_class_name = 'Pix_Customize_SM_radio_Control';
1980 break;
1981
1982 case 'sm_palette_filter' :
1983 if ( ! isset( $field_config['choices'] ) || empty( $field_config['choices'] ) ) {
1984 return;
1985 }
1986
1987 $control_args['choices'] = $field_config['choices'];
1988
1989 $control_class_name = 'Pix_Customize_SM_palette_filter_Control';
1990 break;
1991
1992 case 'sm_switch' :
1993 if ( ! isset( $field_config['choices'] ) || empty( $field_config['choices'] ) ) {
1994 return;
1995 }
1996
1997 $control_args['choices'] = $field_config['choices'];
1998
1999 $control_class_name = 'Pix_Customize_SM_switch_Control';
2000 break;
2001
2002 case 'preset' :
2003 if ( ! isset( $field_config['choices'] ) || empty( $field_config['choices'] ) ) {
2004 return;
2005 }
2006
2007 $control_args['choices'] = $field_config['choices'];
2008
2009 if ( isset( $field_config['choices_type'] ) || ! empty( $field_config['choices_type'] ) ) {
2010 $control_args['choices_type'] = $field_config['choices_type'];
2011 }
2012
2013 if ( isset( $field_config['desc'] ) || ! empty( $field_config['desc'] ) ) {
2014 $control_args['description'] = $field_config['desc'];
2015 }
2016
2017
2018 $control_class_name = 'Pix_Customize_Preset_Control';
2019 break;
2020
2021 case 'radio_image' :
2022 if ( ! isset( $field_config['choices'] ) || empty( $field_config['choices'] ) ) {
2023 return;
2024 }
2025
2026 $control_args['choices'] = $field_config['choices'];
2027
2028 if ( isset( $field_config['choices_type'] ) || ! empty( $field_config['choices_type'] ) ) {
2029 $control_args['choices_type'] = $field_config['choices_type'];
2030 }
2031
2032 if ( isset( $field_config['desc'] ) || ! empty( $field_config['desc'] ) ) {
2033 $control_args['description'] = $field_config['desc'];
2034 }
2035
2036
2037 $control_class_name = 'Pix_Customize_Radio_Image_Control';
2038 break;
2039
2040 case 'button' :
2041 if ( ! isset( $field_config['action'] ) || empty( $field_config['action'] ) ) {
2042 return;
2043 }
2044
2045 $control_args['action'] = $field_config['action'];
2046
2047 $control_class_name = 'Pix_Customize_Button_Control';
2048
2049 break;
2050
2051 case 'html' :
2052 if ( isset( $field_config['html'] ) || ! empty( $field_config['html'] ) ) {
2053 $control_args['html'] = $field_config['html'];
2054 }
2055
2056 $control_class_name = 'Pix_Customize_HTML_Control';
2057 break;
2058
2059 case 'import_demo_data' :
2060 if ( isset( $field_config['html'] ) || ! empty( $field_config['html'] ) ) {
2061 $control_args['html'] = $field_config['html'];
2062 }
2063
2064 if ( ! isset( $field_config['label'] ) || empty( $field_config['label'] ) ) {
2065 $control_args['label'] = esc_html__( 'Import', 'customify' );
2066 } else {
2067 $control_args['label'] = $field_config['label'];
2068 }
2069
2070 if ( isset( $field_config['notices'] ) && ! empty( $field_config['notices'] ) ) {
2071 $control_args['notices'] = $field_config['notices'];
2072 }
2073
2074 $control_class_name = 'Pix_Customize_Import_Demo_Data_Control';
2075 break;
2076
2077 default:
2078 // if we don't have a real control just quit, it doesn't even matter
2079 return;
2080 break;
2081 }
2082
2083 $this_control = new $control_class_name(
2084 $wp_customize,
2085 $setting_id . '_control',
2086 $control_args
2087 );
2088
2089 if ( $add_control ) {
2090 $wp_customize->add_control( $this_control );
2091 }
2092 }
2093
2094 /**
2095 * Remove the sections selected by user
2096 *
2097 * @param WP_Customize_Manager $wp_customize
2098 */
2099 function remove_default_sections( $wp_customize ) {
2100 global $wp_registered_sidebars;
2101
2102 $to_remove = $this->get_plugin_setting( 'disable_default_sections' );
2103
2104 if ( ! empty( $to_remove ) ) {
2105 foreach ( $to_remove as $section => $nothing ) {
2106
2107 if ( $section === 'widgets' ) {
2108 foreach ( $wp_registered_sidebars as $widget => $settings ) {
2109 $wp_customize->remove_section( 'sidebar-widgets-' . $widget );
2110 }
2111 continue;
2112 }
2113
2114 $wp_customize->remove_section( $section );
2115 }
2116 }
2117 }
2118
2119 /**
2120 * Print JavaScript for adding additional data to _wpCustomizeSettings.settings object of the main window (not the preview window).
2121 */
2122 public function customize_pane_settings_additional_data() {
2123 /**
2124 * @global WP_Customize_Manager $wp_customize
2125 */
2126 global $wp_customize;
2127
2128 // Without an options name we can't do much.
2129 if ( empty( $this->customizer_config['opt-name'] ) ) {
2130 return;
2131 }
2132
2133 $options_name = $this->customizer_config['opt-name'];
2134 $customizer_settings = $wp_customize->settings();
2135 ?>
2136 <script type="text/javascript">
2137 if ( 'undefined' === typeof _wpCustomizeSettings.settings ) {
2138 _wpCustomizeSettings.settings = {};
2139 }
2140
2141 <?php
2142 echo "(function ( sAdditional ){\n";
2143
2144 $options = $this->get_options();
2145 foreach ( $options as $option_id => $option_config ) {
2146 // If we have been explicitly given a setting ID we will use that
2147 if ( ! empty( $option_config['setting_id'] ) ) {
2148 $setting_id = $option_config['setting_id'];
2149 } else {
2150 $setting_id = $options_name . '[' . $option_id . ']';
2151 }
2152 // @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()
2153 if ( ! empty( $customizer_settings[ $setting_id ] ) && ! empty( $option_config['connected_fields'] ) ) {
2154 // Pass through all the connected fields and make sure the id is in the final format
2155 $connected_fields = array();
2156 foreach ( $option_config['connected_fields'] as $key => $connected_field_config ) {
2157 $connected_field_data = array();
2158
2159 if ( is_string( $connected_field_config ) ) {
2160 $connected_field_id = $connected_field_config;
2161 } elseif ( is_array( $connected_field_config ) ) {
2162 // We have a full blown connected field config
2163 if ( is_string( $key ) ) {
2164 $connected_field_id = $key;
2165 } else {
2166 continue;
2167 }
2168
2169 // We will pass to JS all the configured connected field details.
2170 $connected_field_data = $connected_field_config;
2171 }
2172
2173 // Continue if we don't have a connected field ID to work with.
2174 if ( empty( $connected_field_id ) ) {
2175 continue;
2176 }
2177
2178 // If the connected setting is not one of our's, we will use it's ID as it is.
2179 if ( ! array_key_exists( $connected_field_id, $options ) ) {
2180 $connected_field_data['setting_id'] = $connected_field_id;
2181 }
2182 // If the connected setting specifies a setting ID, we will not prefix it and use it as it is.
2183 elseif ( ! empty( $options[ $connected_field_id ] ) && ! empty( $options[ $connected_field_id ]['setting_id'] ) ) {
2184 $connected_field_data['setting_id'] = $options[ $connected_field_id ]['setting_id'];
2185 } else {
2186 $connected_field_data['setting_id'] = $options_name . '[' . $connected_field_id . ']';
2187 }
2188
2189 $connected_fields[] = $connected_field_data;
2190 }
2191
2192 printf(
2193 "sAdditional[%s].%s = %s;\n",
2194 wp_json_encode( $setting_id ),
2195 'connected_fields',
2196 wp_json_encode( $connected_fields, JSON_FORCE_OBJECT )
2197 );
2198 }
2199 }
2200 echo "})( _wpCustomizeSettings.settings );\n";
2201 ?>
2202 </script>
2203 <?php
2204 }
2205
2206 public function get_file() {
2207 return $this->file;
2208 }
2209
2210 public function get_base_path() {
2211 return plugin_dir_path( $this->file );
2212 }
2213
2214 public function get_typography_fields( $array, $key, $value, &$results, $input_key = 0 ) {
2215 if ( ! is_array( $array ) ) {
2216 return;
2217 }
2218
2219 if ( isset( $array[ $key ] ) && $array[ $key ] == $value ) {
2220 $results[ $input_key ] = $array;
2221
2222 $default = null;
2223
2224 if ( isset( $array['default'] ) && is_array( $array['default'] ) ) {
2225 $default = json_encode( $array['default'] );
2226 }
2227
2228 $results[ $input_key ]['value'] = $this->get_option( $input_key, $default );
2229 }
2230
2231 foreach ( $array as $i => $subarray ) {
2232 $this->get_typography_fields( $subarray, $key, $value, $results, $i );
2233 }
2234 }
2235
2236 function get_options_key() {
2237 if ( ! empty( $this->opt_name ) ) {
2238 return $this->opt_name;
2239 }
2240
2241 return false;
2242 }
2243
2244 /**
2245 * Use this function when you need to know if an import button is used
2246 * @return bool
2247 */
2248 function import_button_exists() {
2249
2250 if ( empty( $this->options_list ) ) {
2251 $this->options_list = $this->get_options();
2252 }
2253
2254 foreach ( $this->options_list as $option ) {
2255 if ( isset( $option['type'] ) && 'import_demo_data' === $option['type'] ) {
2256 return true;
2257 break;
2258 }
2259 }
2260
2261 return false;
2262 }
2263
2264 /** == Helpers == */
2265
2266 protected function get_current_values( $opt_name = null ) {
2267 // Fallback to the global $opt_name
2268 if ( empty( $opt_name ) ) {
2269 $opt_name = $this->opt_name;
2270 }
2271
2272 // Bail as we have nothing to work with
2273 if ( empty( $opt_name ) ) {
2274 return false;
2275 }
2276
2277 $store_type = $this->get_plugin_setting( 'values_store_mod', 'option' );
2278 if ( $store_type === 'option' ) {
2279 return get_option( $opt_name );
2280 } elseif ( $store_type === 'theme_mod' ) {
2281 return get_theme_mod( $opt_name );
2282 }
2283
2284 return false;
2285 }
2286
2287 public function get_options() {
2288
2289 $settings = array();
2290
2291 if ( isset ( $this->customizer_config['panels'] ) ) {
2292
2293 foreach ( $this->customizer_config['panels'] as $pane_id => $panel_settings ) {
2294
2295 if ( isset( $panel_settings['sections'] ) ) {
2296 foreach ( $panel_settings['sections'] as $section_id => $section_settings ) {
2297 if ( isset( $section_settings['options'] ) ) {
2298 foreach ( $section_settings['options'] as $option_id => $option ) {
2299 $settings[ $option_id ] = $option;
2300 if ( isset( $this->current_values[ $option_id ] ) ) {
2301 $settings[ $option_id ]['value'] = $this->current_values[ $option_id ];
2302 }
2303 }
2304 }
2305 }
2306 }
2307 }
2308 }
2309
2310 if ( isset ( $this->customizer_config['sections'] ) ) {
2311 foreach ( $this->customizer_config['sections'] as $section_id => $section_settings ) {
2312 if ( isset( $section_settings['options'] ) ) {
2313 foreach ( $section_settings['options'] as $option_id => $option ) {
2314 $settings[ $option_id ] = $option;
2315 if ( isset( $this->current_values[ $option_id ] ) ) {
2316 $settings[ $option_id ]['value'] = $this->current_values[ $option_id ];
2317 }
2318 }
2319 }
2320 }
2321 }
2322
2323 return $settings;
2324 }
2325
2326 protected function get_value( $option_id, $alt_opt_name ) {
2327 global $wp_customize;
2328
2329 $options_name = $this->opt_name;
2330 $values = $this->current_values;
2331
2332 // In case someone asked for a DB value too early but it has given us the options_name under which to search, let's do it
2333 if ( empty( $options_name ) && ! empty( $alt_opt_name ) ) {
2334 $options_name = $alt_opt_name;
2335 $values = $this->get_current_values( $options_name );
2336 }
2337
2338 if ( ! empty( $wp_customize ) && method_exists( $wp_customize, 'get_setting' ) ) {
2339 // Get the field config.
2340 $option_config = $this->get_option_customizer_config( $option_id );
2341
2342 if ( empty( $option_id ) || ! isset( $option_config['type'] ) ) {
2343 return null;
2344 }
2345
2346 // If we have been explicitly given a setting ID we will use that
2347 if ( ! empty( $option_config['setting_id'] ) ) {
2348 $setting_id = $option_config['setting_id'];
2349 } else {
2350 $setting_id = $options_name . '[' . $option_id . ']';
2351 }
2352
2353 $setting = $wp_customize->get_setting( $setting_id );
2354 if ( ! empty( $setting ) ) {
2355 return $setting->value();
2356 }
2357 }
2358
2359 // shim
2360 if ( strpos( $option_id, $options_name . '[' ) !== false ) {
2361 // get only the setting id
2362 $option_id = explode( '[', $option_id );
2363 $option_id = rtrim( $option_id[1], ']' );
2364 }
2365
2366 if ( isset( $values[ $option_id ] ) ) {
2367 return $values[ $option_id ];
2368 }
2369
2370 return null;
2371 }
2372
2373 /**
2374 * A public function to get an option's value.
2375 * If there is a value and return it.
2376 * Otherwise try to get the default parameter or the default from config.
2377 *
2378 * @param $option_id
2379 * @param mixed $default Optional.
2380 * @param string $alt_opt_name Optional. We can use this to bypass the fact that the DB values are loaded on after_setup_theme, if we know what to look for.
2381 *
2382 * @return bool|null|string
2383 */
2384 public function get_option( $option_id, $default = null, $alt_opt_name = null ) {
2385 // If the development constant CUSTOMIFY_DEV_FORCE_DEFAULTS has been defined we will not retrieve anything from the database
2386 // Always go with the default
2387 if ( defined( 'CUSTOMIFY_DEV_FORCE_DEFAULTS' ) && true === CUSTOMIFY_DEV_FORCE_DEFAULTS && ! $this->skip_dev_mode_force_defaults( $option_id ) ) {
2388 $return = null;
2389 } else {
2390 // Get the field config.
2391 $option_config = $this->get_option_customizer_config( $option_id );
2392
2393 if ( empty( $option_id ) ) {
2394 $return = null;
2395 } elseif ( isset( $option_config['setting_type'] ) && $option_config['setting_type'] === 'option' ) {
2396 // We have a setting that is saved in the wp_options table, not in theme_mods.
2397 // We will fetch it directly.
2398
2399 // If we have been explicitly given a setting ID we will use that
2400 if ( ! empty( $option_config['setting_id'] ) ) {
2401 $setting_id = $option_config['setting_id'];
2402 } else {
2403 $setting_id = $this->opt_name . '[' . $option_id . ']';
2404 }
2405
2406 $return = get_option( $setting_id, null );
2407 } else {
2408 // Get the value stores in theme_mods.
2409 $return = $this->get_value( $option_id, $alt_opt_name );
2410 }
2411 }
2412
2413 if ( $return !== null ) {
2414 return $return;
2415 } elseif ( $default !== null ) {
2416 return $default;
2417 } elseif ( isset( $this->options_list[ $option_id ] ) && isset( $this->options_list[ $option_id ]['default'] ) ) {
2418 return $this->options_list[ $option_id ]['default'];
2419 }
2420
2421 return null;
2422 }
2423
2424 public function has_option( $option ) {
2425
2426 if ( isset( $this->options_list[ $option ] ) ) {
2427 return true;
2428 }
2429
2430 return false;
2431 }
2432
2433 public function get_customizer_config() {
2434 return $this->customizer_config;
2435 }
2436
2437 /**
2438 * Get the Customify configuration of a certain option.
2439 *
2440 * @param string $option_id
2441 *
2442 * @return array|false The option config or false on failure.
2443 */
2444 public function get_option_customizer_config( $option_id ) {
2445 // We need to search for the option configured under the given id (the array key)
2446 if ( isset ( $this->customizer_config['panels'] ) ) {
2447 foreach ( $this->customizer_config['panels'] as $panel_id => $panel_settings ) {
2448 if ( isset( $panel_settings['sections'] ) ) {
2449 foreach ( $panel_settings['sections'] as $section_id => $section_settings ) {
2450 if ( isset( $section_settings['options'] ) ) {
2451 foreach ( $section_settings['options'] as $id => $option_config ) {
2452 if ( $id === $option_id ) {
2453 return $option_config;
2454 }
2455 }
2456 }
2457 }
2458 }
2459 }
2460 }
2461
2462 if ( isset ( $this->customizer_config['sections'] ) ) {
2463 foreach ( $this->customizer_config['sections'] as $section_id => $section_settings ) {
2464 if ( isset( $section_settings['options'] ) ) {
2465 foreach ( $section_settings['options'] as $id => $option_config ) {
2466 if ( $id === $option_id ) {
2467 return $option_config;
2468 }
2469 }
2470 }
2471 }
2472 }
2473
2474 return false;
2475 }
2476
2477 protected function get_config() {
2478 if ( file_exists( $this->get_base_path() . 'plugin-config.php' ) ) {
2479 return include( $this->get_base_path() . 'plugin-config.php' );
2480 }
2481
2482 return false;
2483 }
2484
2485 /**
2486 * Get an option's value from the config file
2487 *
2488 * @param $option
2489 * @param null $default
2490 *
2491 * @return bool|null
2492 */
2493 public function get_config_option( $option, $default = null ) {
2494
2495 if ( isset( $this->config[ $option ] ) ) {
2496 return $this->config[ $option ];
2497 } elseif ( $default !== null ) {
2498 return $default;
2499 }
2500
2501 return false;
2502 }
2503
2504 public function get_plugin_setting( $option, $default = null ) {
2505
2506 if ( isset( $this->plugin_settings[ $option ] ) ) {
2507 return $this->plugin_settings[ $option ];
2508 } elseif ( $default !== null ) {
2509 return $default;
2510 }
2511
2512 return false;
2513 }
2514
2515 /**
2516 * Sanitize functions
2517 */
2518
2519 /**
2520 * Sanitize the checkbox.
2521 *
2522 * @param boolean $input .
2523 *
2524 * @return boolean true if is 1 or '1', false if anything else
2525 */
2526 function setting_sanitize_checkbox( $input ) {
2527 if ( 1 == $input ) {
2528 return true;
2529 } else {
2530 return false;
2531 }
2532 }
2533
2534 /**
2535 * Checks whether an array is associative or not
2536 *
2537 * @param array $array
2538 *
2539 * @return bool
2540 */
2541 public function is_assoc( $array ) {
2542
2543 if ( ! is_array( $array ) ) {
2544 return false;
2545 }
2546
2547 // Keys of the array
2548 $keys = array_keys( $array );
2549
2550 // If the array keys of the keys match the keys, then the array must
2551 // not be associative (e.g. the keys array looked like {0:0, 1:1...}).
2552 return array_keys( $keys ) !== $keys;
2553 }
2554
2555 function register_import_api() {
2556
2557 include_once( $this->get_base_path() . '/features/class-Customify_Importer.php' );
2558 $controller = new Customify_Importer_Controller();
2559 $controller->init();
2560 }
2561
2562 public function get_options_configs() {
2563 return $this->options_list;
2564 }
2565
2566 /**
2567 * Does the same thing the JS encodeURIComponent() does
2568 *
2569 * @param string $str
2570 *
2571 * @return string
2572 */
2573 public static function encodeURIComponent( $str ) {
2574 //if we get an array we just let it be
2575 if ( is_string( $str ) ) {
2576 $revert = array( '%21' => '!', '%2A' => '*', '%27' => "'", '%28' => '(', '%29' => ')' );
2577
2578 $str = strtr( rawurlencode( $str ), $revert );
2579 } else {
2580 var_dump( 'boooom' );
2581 die;
2582 }
2583
2584 return $str;
2585 }
2586
2587 /**
2588 * Does the same thing the JS decodeURIComponent() does
2589 *
2590 * @param string $str
2591 *
2592 * @return string
2593 */
2594 public static function decodeURIComponent( $str ) {
2595 // If we get an array we just let it be
2596 if ( is_string( $str ) ) {
2597 $revert = array( '!' => '%21', '*' => '%2A', "'" => '%27', '(' => '%28', ')' => '%29' );
2598 $str = rawurldecode( strtr( $str, $revert ) );
2599 }
2600
2601 return $str;
2602 }
2603
2604 /**
2605 * PHP version check
2606 */
2607 protected function php_version_check() {
2608
2609 if ( version_compare( phpversion(), $this->minimalRequiredPhpVersion ) < 0 ) {
2610 add_action( 'admin_notices', array( $this, 'notice_php_version_wrong' ) );
2611
2612 return false;
2613 }
2614
2615 return true;
2616 }
2617
2618 /**
2619 * Prevent saving of plugin options in the Customizer
2620 *
2621 * @param array $data The data to save
2622 * @param array $filter_context
2623 *
2624 * @return array
2625 */
2626 public function prevent_changeset_save_in_devmode( $data, $filter_context ) {
2627 // Get the options key
2628 $options_key = $this->customizer_config['opt-name'];
2629 if ( ! empty( $options_key ) ) {
2630 // Remove any Customify data thus preventing it from saving
2631 foreach ( $data as $option_id => $value ) {
2632 if ( false !== strpos( $option_id, $options_key ) && ! $this->skip_dev_mode_force_defaults( $option_id ) ) {
2633 unset( $data[ $option_id ] );
2634 }
2635 }
2636 }
2637
2638 return $data;
2639 }
2640
2641 /**
2642 * Determine if we should NOT enforce the CUSTOMIFY_DEV_FORCE_DEFAULTS behavior on a certain option.
2643 *
2644 * @param string $option_id
2645 *
2646 * @return bool
2647 */
2648 private function skip_dev_mode_force_defaults( $option_id ) {
2649 // Preprocess the $option_id.
2650 if ( false !== strpos( $option_id, '::' ) ) {
2651 $option_id = substr( $option_id, strpos( $option_id, '::' ) + 2 );
2652 }
2653 if ( false !== strpos( $option_id, '[' ) ) {
2654 $option_id = explode( '[', $option_id );
2655 $option_id = rtrim( $option_id[1], ']' );
2656 }
2657
2658 $option_config = $this->get_option_customizer_config( $option_id );
2659 if ( empty( $option_config ) ) {
2660 return false;
2661 }
2662
2663 // We will skip certain field types that generally don't have a default value.
2664 if ( ! empty( $option_config['type'] ) ) {
2665 switch ( $option_config['type'] ) {
2666 case 'cropped_image':
2667 case 'cropped_media':
2668 case 'image':
2669 case 'media':
2670 case 'custom_background':
2671 case 'upload':
2672 return true;
2673 break;
2674 default:
2675 break;
2676 }
2677 }
2678
2679 return false;
2680 }
2681
2682 public function prevent_changeset_save_in_devmode_notification() { ?>
2683 <script type="application/javascript">
2684 (function ( $, exports, wp ) {
2685 'use strict';
2686 // when the customizer is ready add our notification
2687 wp.customize.bind('ready', function () {
2688 wp.customize.notifications.add( 'customify_force_defaults', new wp.customize.Notification(
2689 'customify_force_defaults',
2690 {
2691 type: 'warning',
2692 message: '<strong style="margin-bottom: ">Customify: Development Mode</strong><p>All the options are switched to default. While they are changing in the live preview, they will not be kept when publish.</p>'
2693 }
2694 ) );
2695 });
2696 })(jQuery, window, wp);
2697 </script>
2698 <?php }
2699
2700 /**
2701 * Main PixCustomifyPlugin Instance
2702 *
2703 * Ensures only one instance of PixCustomifyPlugin is loaded or can be loaded.
2704 *
2705 * @since 1.0.0
2706 * @static
2707 *
2708 * @param string $file File.
2709 * @param string $version Version.
2710 *
2711 * @see PixCustomifyPlugin()
2712 * @return PixCustomifyPlugin Main PixCustomifyPlugin instance
2713 */
2714 public static function instance( $file = '', $version = '1.0.0' ) {
2715 // If the single instance hasn't been set, set it now.
2716 if ( is_null( self::$_instance ) ) {
2717 self::$_instance = new self( $file, $version );
2718 }
2719
2720 return self::$_instance;
2721 }
2722
2723 /**
2724 * Cloning is forbidden.
2725 *
2726 * @since 1.5.0
2727 */
2728 public function __clone() {
2729
2730 _doing_it_wrong( __FUNCTION__, esc_html( __( 'Cheatin&#8217; huh?' ) ), esc_html( $this->_version ) );
2731 } // End __clone ()
2732
2733 /**
2734 * Unserializing instances of this class is forbidden.
2735 *
2736 * @since 1.5.0
2737 */
2738 public function __wakeup() {
2739
2740 _doing_it_wrong( __FUNCTION__, esc_html( __( 'Cheatin&#8217; huh?' ) ), esc_html( $this->_version ) );
2741 } // End __wakeup ()
2742 }
2743