PluginProbe
Customify / 2.7.0
Customify v2.7.0
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-pixcustomify.php

class-pixcustomify.php in Customify 2.7.0, at includes/class-pixcustomify.php

1,013 lines 30.1 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-2020 Pixelgrade
9 */
10
11 /**
12 * Main 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 * The main plugin file.
42 * @var string
43 * @access public
44 * @since 1.5.0
45 */
46 public $file;
47
48 /**
49 * Settings class object.
50 * @var Customify_Settings
51 * @access public
52 * @since 2.4.0
53 */
54 public $settings = null;
55
56 /**
57 * Customizer class object to handle customizer controls and logic.
58 * @var PixCustomify_Customizer
59 * @access public
60 * @since 2.4.0
61 */
62 public $customizer = null;
63
64 /**
65 * Fonts class object to handle fonts global logic.
66 * @var Customify_Fonts_Global
67 * @access public
68 * @since 2.7.0
69 */
70 public $fonts_global = null;
71
72 /**
73 * Style Manager class object.
74 * @var Customify_Style_Manager
75 * @access public
76 * @since 1.0.0
77 */
78 public $style_manager = null;
79
80 /**
81 * Block Editor class object.
82 * @var Customify_Block_Editor
83 * @access public
84 * @since 2.7.0
85 */
86 public $block_editor = null;
87
88 /**
89 * Classic Editor class object.
90 * @var Customify_Classic_Editor
91 * @access public
92 * @since 2.7.0
93 */
94 public $classic_editor = null;
95
96 protected $options_minimal_details = array();
97 protected $options_details = array();
98
99 protected $opt_name;
100
101 private $customizer_config = array();
102
103 /**
104 * Minimal Required PHP Version
105 * @var string
106 * @access private
107 * @since 1.5.0
108 */
109 private $minimalRequiredPhpVersion = '5.4';
110
111 protected function __construct( $file, $version = '1.0.0' ) {
112 // The main plugin file (the one that loads all this).
113 $this->file = $file;
114 // The current plugin version.
115 $this->_version = $version;
116
117 if ( $this->php_version_check() ) {
118 // Only load and run the init function if we know PHP version can parse it.
119 $this->init();
120 }
121 }
122
123 /**
124 * Initialize plugin
125 */
126 private function init() {
127 // We don't want to put extra load on the heartbeat AJAX request.
128 if ( wp_doing_ajax() && isset( $_REQUEST['action'] ) && 'heartbeat' === $_REQUEST['action'] ) {
129 return;
130 }
131
132 // Handle the install and uninstall logic
133 register_activation_hook( $this->get_file(), array( 'PixCustomifyPlugin', 'install' ) );
134
135 /* Initialize the plugin settings logic. */
136 require_once( $this->get_base_path() . 'includes/class-customify-settings.php' );
137 if ( is_null( $this->settings ) ) {
138 $this->settings = Customify_Settings::instance( $this->get_file(), $this->get_slug(), $this->get_version() );
139 }
140
141 /* Initialize the Customizer logic. */
142 require_once( $this->get_base_path() . 'includes/class-customify-customizer.php' );
143 if ( is_null( $this->customizer ) ) {
144 $this->customizer = PixCustomify_Customizer::instance();
145 }
146
147 /* Initialize the Fonts logic. */
148 require_once( $this->get_base_path() . 'includes/class-customify-fonts-global.php' );
149 if ( is_null( $this->fonts_global ) ) {
150 $this->fonts_global = Customify_Fonts_Global::instance();
151 }
152
153 /* Initialize the Style Manager logic. */
154 require_once( $this->get_base_path() . 'includes/class-customify-style-manager.php' );
155 if ( is_null( $this->style_manager ) ) {
156 $this->style_manager = Customify_Style_Manager::instance();
157 }
158
159 /* Initialize the Block Editor integration logic. */
160 require_once( $this->get_base_path() . 'includes/class-customify-block-editor.php' );
161 if ( is_null( $this->block_editor ) ) {
162 $this->block_editor = Customify_Block_Editor::instance();
163 }
164
165 /* Initialize the Classic Editor integration logic. */
166 require_once( $this->get_base_path() . 'includes/class-customify-classic-editor.php' );
167 if ( is_null( $this->classic_editor ) ) {
168 $this->classic_editor = Customify_Classic_Editor::instance();
169 }
170
171 // Register all the needed hooks
172 $this->register_hooks();
173 }
174
175 /**
176 * Register our actions and filters
177 */
178 function register_hooks() {
179
180 /*
181 * Load plugin text domain
182 */
183 add_action( 'init', array( $this, 'load_plugin_textdomain' ) );
184
185 /*
186 * Load the upgrade logic.
187 */
188 add_action( 'admin_init', array( $this, 'upgrade' ) );
189
190 /*
191 * Handle the force clearing of the caches. We clear in a proactive manner.
192 */
193 add_action( 'activated_plugin', array( $this, 'invalidate_all_caches' ), 1 );
194 add_action( 'deactivated_plugin', array( $this, 'invalidate_all_caches' ), 1 );
195 add_action( 'after_switch_theme', array( $this, 'invalidate_all_caches' ), 1 );
196 add_action( 'upgrader_process_complete', array( $this, 'invalidate_all_caches' ), 1 );
197
198 // Whenever we update data from the Customizer, we will invalidate the options details (that include the value).
199 add_filter( 'customize_changeset_save_data', array( $this, 'filter_invalidate_options_details_cache' ), 50, 1 );
200 }
201
202 /**
203 * Handle the logic to upgrade between versions. It will run only one per version change.
204 */
205 public function upgrade() {
206 $customify_dbversion = get_option( 'customify_dbversion', '0.0.1' );
207 if ( $this->get_version() === $customify_dbversion ) {
208 return;
209 }
210
211 // For versions, previous of version 2.0.0 (the Color Palettes v2.0 release).
212 if ( version_compare( $customify_dbversion, '2.0.0', '<' ) ) {
213 // Delete the option holding the fact that the user offered feedback.
214 delete_option( 'style_manager_user_feedback_provided' );
215 }
216
217 // Put the current version in the database.
218 update_option( 'customify_dbversion', $this->get_version(), true );
219 }
220
221 /**
222 * Invalidate all caches.
223 *
224 * @since 2.6.0
225 */
226 public function invalidate_all_caches() {
227 $this->invalidate_customizer_config_cache();
228 $this->invalidate_options_details_cache();
229 $this->invalidate_customizer_opt_name_cache();
230 $this->invalidate_options_details_cache();
231
232 do_action( 'customify_invalidate_all_caches' );
233 }
234
235 /**
236 * Invalidate all caches, when hooked via a filter (just pass through the value).
237 *
238 * @since 2.6.0
239 *
240 * @param mixed $value
241 * @return mixed
242 */
243 public function filter_invalidate_all_caches( $value ) {
244 $this->invalidate_all_caches();
245
246 return $value;
247 }
248
249 /**
250 * This will clear any instance properties that are used as local cache during a request to avoid
251 * fetching the data from DB on each method call.
252 *
253 * This may be called during a request when something happens that (potentially) invalidates our data mid-request.
254 */
255 public function clear_locally_cached_data() {
256 $this->opt_name = null;
257
258 $this->customizer_config = null;
259
260 $this->options_minimal_details = null;
261 $this->options_details = null;
262 }
263
264 public function get_options_key( $skip_cache = false ) {
265 if ( ! empty( $this->opt_name ) ) {
266 return $this->opt_name;
267 }
268
269 if ( $this->should_force_skip_cache() ) {
270 $skip_cache = true;
271 }
272
273 // First try and get the cached data
274 $data = get_option( $this->get_customizer_opt_name_cache_key() );
275 $expire_timestamp = false;
276
277 // Only try to get the expire timestamp if we really need to.
278 if ( true !== $skip_cache && false !== $data ) {
279 // Get the cache data expiration timestamp.
280 $expire_timestamp = get_option( $this->get_customizer_opt_name_cache_key() . '_timestamp' );
281 }
282
283 // The data isn't set, is expired or we were instructed to skip the cache; we need to regenerate the config.
284 if ( true === $skip_cache || false === $data || false === $expire_timestamp || $expire_timestamp < time() ) {
285
286 $data = $this->get_customizer_config( 'opt-name' );
287
288 if ( true !== $skip_cache ) {
289 // Cache the data in an option for 6 hours, but only if we are not supposed to skip the cache entirely.
290 update_option( $this->get_customizer_opt_name_cache_key(), $data, true );
291 update_option( $this->get_customizer_opt_name_cache_key() . '_timestamp', time() + 6 * HOUR_IN_SECONDS, true );
292 }
293 }
294
295 $this->opt_name = $data;
296 return $data;
297 }
298
299 private function get_customizer_opt_name_cache_key() {
300 return 'customify_customizer_opt_name';
301 }
302
303 public function invalidate_customizer_opt_name_cache() {
304 update_option( $this->get_customizer_opt_name_cache_key() . '_timestamp' , time() - 24 * HOUR_IN_SECONDS, true );
305
306 $this->clear_locally_cached_data();
307 }
308
309 public function filter_invalidate_customizer_opt_name_cache( $value ) {
310 $this->invalidate_customizer_opt_name_cache();
311
312 return $value;
313 }
314
315
316 public function get_options_details( $only_minimal_details = false, $skip_cache = false ) {
317
318 // If we already have the data, do as little as possible.
319 if ( true === $only_minimal_details && ! empty( $this->options_minimal_details ) ) {
320 return $this->options_minimal_details;
321 }
322 if ( ! empty( $this->options_details ) ) {
323 return $this->options_details;
324 }
325
326 if ( $this->should_force_skip_cache() ) {
327 $skip_cache = true;
328 }
329
330 // We will first look for cached data
331
332 $data = $this->options_minimal_details = get_option( $this->get_options_minimal_details_cache_key() );
333 if ( false !== $data && false === $only_minimal_details ) {
334 $extra_details_data = get_option( $this->get_options_extra_details_cache_key() );
335 if ( is_array( $extra_details_data ) ) {
336 $data = $this->options_details = Customify_Array::array_merge_recursive_distinct( $data, $extra_details_data );
337 } else {
338 // Something is wrong with the extra details and we need to regenerate.
339 $this->invalidate_options_details_cache();
340 }
341 }
342
343 // For performance reasons, we will use the cached data (even if stale)
344 // when a user is not logged in or a user without administrative capabilities is logged in.
345 if ( false !== $data && false === $skip_cache && ! current_user_can( 'manage_options' ) ) {
346 return $data;
347 }
348
349 $expire_timestamp = false;
350
351 // Only try to get the expire timestamp if we really need to.
352 if ( true !== $skip_cache && false !== $data ) {
353 // Get the cached data expiration timestamp.
354 $expire_timestamp = get_option( $this->get_options_details_cache_timestamp_key() );
355 }
356
357 // The data isn't set, is expired or we were instructed to skip the cache; we need to regenerate the config.
358 if ( true === $skip_cache || false === $data || false === $expire_timestamp || $expire_timestamp < time() ) {
359 $options_minimal_details = array();
360 $options_extra_details = array();
361
362 $minimal_detail_keys = array(
363 'type',
364 'setting_type',
365 'setting_id',
366 'default',
367 'css',
368 'output',
369 'value',
370 'selector',
371 'callback',
372 'active_callback',
373 );
374
375 $customizer_config = $this->get_customizer_config();
376
377 if ( isset ( $customizer_config['panels'] ) ) {
378 foreach ( $customizer_config['panels'] as $pane_id => $panel_settings ) {
379 if ( isset( $panel_settings['sections'] ) ) {
380 foreach ( $panel_settings['sections'] as $section_id => $section_settings ) {
381 if ( isset( $section_settings['options'] ) ) {
382 foreach ( $section_settings['options'] as $option_id => $option_config ) {
383 if ( is_array( $option_config ) ) {
384 foreach ( $option_config as $key => $value ) {
385 if ( in_array( $key, $minimal_detail_keys ) ) {
386 $options_minimal_details[ $option_id ][ $key ] = $value;
387 } else {
388 $options_extra_details[ $option_id ][ $key ] = $value;
389 }
390 }
391
392 $options_minimal_details[ $option_id ]['value'] = $this->get_option( $option_id, null, $option_config );
393 }
394 }
395 }
396 }
397 }
398 }
399 }
400
401 if ( isset ( $customizer_config['sections'] ) ) {
402 foreach ( $customizer_config['sections'] as $section_id => $section_settings ) {
403 if ( isset( $section_settings['options'] ) ) {
404 foreach ( $section_settings['options'] as $option_id => $option_config ) {
405 if ( is_array( $option_config ) ) {
406 foreach ( $option_config as $key => $value ) {
407 if ( in_array( $key, $minimal_detail_keys ) ) {
408 $options_minimal_details[ $option_id ][ $key ] = $value;
409 } else {
410 $options_extra_details[ $option_id ][ $key ] = $value;
411 }
412 }
413
414 $options_minimal_details[ $option_id ]['value'] = $this->get_option( $option_id, null, $option_config );
415 }
416 }
417 }
418 }
419 }
420
421 if ( true !== $skip_cache ) {
422 // Cache the data for 6 hours, but only if we are not supposed to skip the cache entirely.
423 update_option( $this->get_options_minimal_details_cache_key(), $options_minimal_details, true );
424 update_option( $this->get_options_extra_details_cache_key(), $options_extra_details, false ); // we will not autoload extra details for performance reasons.
425 update_option( $this->get_options_details_cache_timestamp_key(), time() + 6 * HOUR_IN_SECONDS, true );
426 }
427
428 $data = $this->options_minimal_details = $options_minimal_details;
429 $this->options_details = Customify_Array::array_merge_recursive_distinct( $options_minimal_details, $options_extra_details );
430 if ( false === $only_minimal_details ) {
431 $data = $this->options_details;
432 }
433 }
434
435 return $data;
436 }
437
438 private function should_force_skip_cache() {
439 // If our development constant is defined and true, we will always skip the cache, except for AJAX calls.
440 // Other, more specific cases may impose skipping the cache also on AJAX calls.
441 if ( ! wp_doing_ajax()
442 && defined('CUSTOMIFY_ALWAYS_GENERATE_CUSTOMIZER_CONFIG' )
443 && true === CUSTOMIFY_ALWAYS_GENERATE_CUSTOMIZER_CONFIG ) {
444 return true;
445 }
446
447 // If we are in the Customizer and the request has a $_POST['customized'] parameter, we will skip the cache
448 // since this means that the preview is being reloaded with temporary settings values.
449 if ( ! empty( $_POST['customized'] ) ) {
450 return true;
451 }
452
453 // If we are currently previewing a theme without being actually active, we should not use cached data.
454
455 if ( ! empty( $_REQUEST['theme'] ) || ! empty( $_REQUEST['customize_theme'] ) ) {
456 return true;
457 }
458
459 /** @var WP_Customize_Manager $wp_customize */
460 global $wp_customize;
461 if ( ! empty( $wp_customize )
462 && method_exists( $wp_customize, 'is_theme_active' )
463 && ! $wp_customize->is_theme_active() ) {
464
465 return true;
466 }
467
468 return false;
469 }
470
471 private function get_options_minimal_details_cache_key() {
472 return 'customify_options_minimal_details';
473 }
474
475 private function get_options_extra_details_cache_key() {
476 return 'customify_options_extra_details';
477 }
478
479 private function get_options_details_cache_timestamp_key() {
480 return 'customify_options_details_timestamp';
481 }
482
483 public function invalidate_options_details_cache() {
484 update_option( $this->get_options_details_cache_timestamp_key(), time() - 24 * HOUR_IN_SECONDS, true );
485
486 $this->clear_locally_cached_data();
487 }
488
489 public function filter_invalidate_options_details_cache( $value ) {
490 $this->invalidate_options_details_cache();
491
492 return $value;
493 }
494
495 public function has_option( $option ) {
496
497 $options_details = $this->get_options_details(true);
498 if ( isset( $options_details[ $option ] ) ) {
499 return true;
500 }
501
502 return false;
503 }
504
505 public function get_customizer_config( $key = false ) {
506 $customizer_config = $this->load_customizer_config();
507
508 if ( false !== $key ) {
509 if ( is_array( $customizer_config ) && isset( $customizer_config[ $key ] ) ) {
510 return $customizer_config[ $key ];
511 }
512
513 return null;
514 }
515
516 return $customizer_config;
517 }
518
519 /**
520 * Set the customizer configuration.
521 *
522 * @since 2.2.1
523 *
524 * @param bool $skip_cache Optional. Whether to use the cached config or generate a new one.
525 * @return array
526 */
527 protected function load_customizer_config( $skip_cache = false ) {
528 if ( ! empty( $this->customizer_config ) ) {
529 return $this->customizer_config;
530 }
531
532 if ( $this->should_force_skip_cache() ) {
533 $skip_cache = true;
534 }
535
536 // First try and get the cached data
537 $data = get_option( $this->get_customizer_config_cache_key() );
538
539 // For performance reasons, we will use the cached data (even if stale)
540 // when a user is not logged in or a user without administrative capabilities is logged in.
541 if ( false !== $data && false === $skip_cache && ! current_user_can( 'manage_options' ) ) {
542 $this->customizer_config = $data;
543 return $data;
544 }
545
546 $expire_timestamp = false;
547
548 // Only try to get the expire timestamp if we really need to.
549 if ( true !== $skip_cache && false !== $data ) {
550 // Get the cache data expiration timestamp.
551 $expire_timestamp = get_option( $this->get_customizer_config_cache_key() . '_timestamp' );
552 }
553
554 // The data isn't set, is expired or we were instructed to skip the cache; we need to regenerate the config.
555 if ( true === $skip_cache || false === $data || false === $expire_timestamp || $expire_timestamp < time() ) {
556 // Allow themes or other plugins to filter the config.
557 $data = apply_filters( 'customify_filter_fields', array() );
558 // We apply a second filter for those that wish to work with the final config and not rely on a a huge priority number.
559 $data = apply_filters( 'customify_final_config', $data );
560
561 if ( true !== $skip_cache ) {
562 // Cache the data in an option for 6 hours, but only if we are not supposed to skip the cache entirely.
563 update_option( $this->get_customizer_config_cache_key(), $data, false );
564 update_option( $this->get_customizer_config_cache_key() . '_timestamp', time() + 6 * HOUR_IN_SECONDS, true );
565 }
566 }
567
568 $this->customizer_config = $data;
569
570 return $data;
571 }
572
573 private function get_customizer_config_cache_key() {
574 return 'customify_customizer_config';
575 }
576
577 public function invalidate_customizer_config_cache() {
578 update_option( $this->get_customizer_config_cache_key() . '_timestamp' , time() - 24 * HOUR_IN_SECONDS, true );
579
580 $this->clear_locally_cached_data();
581 }
582
583 /**
584 * Invalidate the customizer config cache, when hooked via a filter (just pass through the value).
585 *
586 * @since 2.4.0
587 *
588 * @param mixed $value
589 * @return mixed
590 */
591 public function filter_invalidate_customizer_config_cache( $value ) {
592 $this->invalidate_customizer_config_cache();
593
594 return $value;
595 }
596
597 /**
598 * Get the Customify configuration (and value, hence "details") of a certain option.
599 *
600 * @param string $option_id
601 * @param bool $minimal_details Optional. Whether to return only the minimum amount of details (mainly what is needed on the frontend).
602 * The advantage is that these details are cached, thus skipping the customizer_config!
603 * @param bool $skip_cache Optional.
604 *
605 * @return array|false The option config or false on failure.
606 */
607 public function get_option_details( $option_id, $minimal_details = false, $skip_cache = false ) {
608 if ( empty( $option_id ) ) {
609 return false;
610 }
611
612 $options_details = $this->get_options_details( $minimal_details, $skip_cache );
613 if ( ! empty( $options_details ) && is_array( $options_details ) && isset( $options_details[ $option_id ] ) ) {
614 return $options_details[ $option_id ];
615 }
616
617 return false;
618 }
619
620 /**
621 * This is just a wrapper for get_options_details() for backwards compatibility.
622 *
623 * @param bool $only_minimal_details
624 * @param bool $skip_cache
625 *
626 * @return array|mixed|void
627 */
628 public function get_options_configs( $only_minimal_details = false, $skip_cache = false ) {
629 return $this->get_options_details( $only_minimal_details, $skip_cache );
630 }
631
632 /**
633 * Get the value of a setting ID saved in a wp_options array entry.
634 *
635 * @param string $option_id This is only the option ID, that may differ from setting ID ( like in `body_font` vs `rosa_opt[body_font]`)
636 * @param string $setting_id We will use this to get the Customizer value, when in that context.
637 *
638 * @return mixed|null
639 */
640 protected function get_option_mod_value( $option_id, $setting_id ) {
641 global $wp_customize;
642
643 if ( empty( $option_id ) || empty( $setting_id ) ) {
644 return null;
645 }
646
647 if ( ! empty( $wp_customize ) && method_exists( $wp_customize, 'get_setting' ) ) {
648 $setting = $wp_customize->get_setting( $setting_id );
649 if ( ! empty( $setting ) ) {
650 return $setting->value();
651 }
652 }
653
654 $values = get_option( $this->get_options_key() );
655
656 if ( ! empty( $values ) && is_array( $values ) && isset( $values[ $option_id ] ) ) {
657 return $values[ $option_id ];
658 }
659
660 return null;
661 }
662
663 /**
664 * Get the value of a certain setting ID saved in the theme mod array.
665 *
666 * @param string $option_id This is only the option ID, that may differ from setting ID ( like in `body_font` vs `rosa_opt[body_font]`)
667 * @param string $setting_id We will use this to get the Customizer value, when in that context.
668 *
669 * @return mixed|null
670 */
671 protected function get_theme_mod_value( $option_id, $setting_id ) {
672 global $wp_customize;
673
674 if ( empty( $option_id ) || empty( $setting_id ) ) {
675 return null;
676 }
677
678 if ( ! empty( $wp_customize ) && method_exists( $wp_customize, 'get_setting' ) ) {
679 $setting = $wp_customize->get_setting( $setting_id );
680 if ( ! empty( $setting ) ) {
681 return $setting->value();
682 } elseif ( $wp_customize->is_preview() ) {
683 // If the setting is not registered (like in asking for the value before wp_loaded), we will read directly from the posted values via the changeset.
684 // Not really the best way, but ok.
685 $post_values = $wp_customize->unsanitized_post_values();
686 if ( array_key_exists( $setting_id, $post_values ) ) {
687 $value = $post_values[ $setting_id ];
688 // Skip validation and sanitization since it is too early.
689 if ( ! is_null( $value ) && ! is_wp_error( $value ) ) {
690 return $value;
691 }
692 }
693 }
694 }
695
696 $values = get_theme_mod( $this->get_options_key() );
697
698 if ( ! empty( $values ) && is_array( $values ) && isset( $values[ $option_id ] ) ) {
699 return $values[ $option_id ];
700 }
701
702 return null;
703 }
704
705 /**
706 * A public function to get an option's value.
707 * If there is a value and return it.
708 * Otherwise try to get the default parameter or the default from config.
709 *
710 * @param $option_id
711 * @param mixed $default Optional.
712 * @param array $option_details Optional.
713 *
714 * @return bool|null|string
715 */
716 public function get_option( $option_id, $default = null, $option_details = null ) {
717
718 if ( null === $option_details ) {
719 // Get the field config.
720 $option_details = $this->get_option_details( $option_id, true );
721 }
722
723 // If the development constant CUSTOMIFY_DEV_FORCE_DEFAULTS has been defined we will not retrieve anything from the database
724 // Always go with the default
725 if ( defined( 'CUSTOMIFY_DEV_FORCE_DEFAULTS' )
726 && true === CUSTOMIFY_DEV_FORCE_DEFAULTS
727 && ! $this->skip_dev_mode_force_defaults( $option_id, $option_details ) ) {
728
729 $value = null;
730 } else {
731
732 if ( empty( $option_id ) || empty( $option_details ) || ! is_array( $option_details ) ) {
733 $value = null;
734 } elseif ( isset( $option_details['value'] ) ) {
735 // If we already have the value cached in the option details, we will use that.
736 $value = $option_details['value'];
737 } else {
738 $value = null;
739
740 /*
741 * First determine the setting ID.
742 */
743 $setting_id = $this->get_options_key() . '[' . $option_id . ']';
744 // If we have been explicitly given a setting ID we will use that.
745 if ( ! empty( $option_details['setting_id'] ) ) {
746 $setting_id = $option_details['setting_id'];
747 }
748
749 /*
750 * Second, try to get the stored value of the setting.
751 */
752
753 // If we have a setting that directly declares it (not deduced like when registering fields in the Customizer)
754 // should be saved in the wp_options table, not in theme_mods, we will attempt to fetch it directly, first.
755 if ( isset( $option_details['setting_type'] ) && $option_details['setting_type'] === 'option' ) {
756 $value = get_option( $setting_id, null );
757 }
758
759 // If we don't have a value, we will grab the setting value from the array of values stored in either
760 // a wp_option entry or in the theme_mods.
761 // The "save as array" behavior happens even in the case of 'option' setting type if
762 // the setting ID is of the form 'rosa_option[some_key]' (aka a multidimensional setting ID).
763 if ( null === $value ) {
764 if ( ! empty( PixCustomifyPlugin()->settings ) && PixCustomifyPlugin()->settings->get_plugin_setting( 'values_store_mod' ) === 'option' ) {
765 // Get the value stored in a option.
766 $value = $this->get_option_mod_value( $option_id, $setting_id );
767 } else {
768 // Get the value stored in theme_mods.
769 $value = $this->get_theme_mod_value( $option_id, $setting_id );
770 }
771 }
772 }
773 }
774
775 // If we have a non-null value, return it.
776 if ( $value !== null ) {
777 return $value;
778 }
779
780 // If we have a non-null default, return it.
781 if ( $default !== null ) {
782 return $default;
783 }
784
785 // Finally, attempt to use the default value set in the config, if available.
786 if ( ! empty( $option_details ) && is_array( $option_details ) && isset( $option_details['default'] ) ) {
787 return $option_details['default'];
788 }
789
790 return null;
791 }
792
793 /**
794 * Determine if we should NOT enforce the CUSTOMIFY_DEV_FORCE_DEFAULTS behavior on a certain option.
795 *
796 * @param string $option_id
797 * @param array $option_config Optional.
798 *
799 * @return bool
800 */
801 public function skip_dev_mode_force_defaults( $option_id, $option_config = null ) {
802 // Preprocess the $option_id.
803 if ( false !== strpos( $option_id, '::' ) ) {
804 $option_id = substr( $option_id, strpos( $option_id, '::' ) + 2 );
805 }
806 if ( false !== strpos( $option_id, '[' ) ) {
807 $option_id = explode( '[', $option_id );
808 $option_id = rtrim( $option_id[1], ']' );
809 }
810
811 if ( null === $option_config ) {
812 $option_config = $this->get_option_details( $option_id, true );
813 }
814 if ( empty( $option_config ) || ! is_array( $option_config ) ) {
815 return false;
816 }
817
818 // We will skip certain field types that generally don't have a default value.
819 if ( ! empty( $option_config['type'] ) ) {
820 switch ( $option_config['type'] ) {
821 case 'cropped_image':
822 case 'cropped_media':
823 case 'image':
824 case 'media':
825 case 'custom_background':
826 case 'upload':
827 return true;
828 break;
829 default:
830 break;
831 }
832 }
833
834 return false;
835 }
836
837 public function get_version() {
838 return $this->_version;
839 }
840
841 public function get_slug() {
842 return $this->plugin_slug;
843 }
844
845 public function get_file() {
846 return $this->file;
847 }
848
849 public function get_base_path() {
850 return wp_normalize_path( plugin_dir_path( $this->file ) );
851 }
852
853 /**
854 * Load the plugin text domain for translation.
855 * @since 1.0.0
856 */
857 function load_plugin_textdomain() {
858 $domain = $this->plugin_slug;
859 load_plugin_textdomain( $domain, false, basename( dirname( $this->file ) ) . '/languages/' );
860 }
861
862 /**
863 * Checks whether an array is associative or not
864 *
865 * @param array $array
866 *
867 * @return bool
868 */
869 public function is_assoc( $array ) {
870
871 if ( ! is_array( $array ) ) {
872 return false;
873 }
874
875 // Keys of the array
876 $keys = array_keys( $array );
877
878 // If the array keys of the keys match the keys, then the array must
879 // not be associative (e.g. the keys array looked like {0:0, 1:1...}).
880 return array_keys( $keys ) !== $keys;
881 }
882
883 /**
884 * Does the same thing the JS encodeURIComponent() does
885 *
886 * @param string $str
887 *
888 * @return string
889 */
890 public static function encodeURIComponent( $str ) {
891 //if we get an array we just let it be
892 if ( is_string( $str ) ) {
893 $revert = array( '%21' => '!', '%2A' => '*', '%27' => "'", '%28' => '(', '%29' => ')' );
894 $str = strtr( rawurlencode( $str ), $revert );
895 }
896
897 return $str;
898 }
899
900 /**
901 * Does the same thing the JS decodeURIComponent() does
902 *
903 * @param mixed $str
904 *
905 * @return mixed
906 */
907 public static function decodeURIComponent( $str ) {
908 // Nothing to do if we receive an array.
909 if ( is_array( $str ) ) {
910 return $str;
911 }
912
913 if ( is_string( $str ) ) {
914 $revert = array( '!' => '%21', '*' => '%2A', "'" => '%27', '(' => '%28', ')' => '%29' );
915 $str = rawurldecode( strtr( $str, $revert ) );
916 }
917
918 return $str;
919 }
920
921 /**
922 * PHP version check
923 */
924 protected function php_version_check() {
925
926 if ( version_compare( phpversion(), $this->minimalRequiredPhpVersion ) < 0 ) {
927 add_action( 'admin_notices', array( $this, 'notice_php_version_wrong' ) );
928
929 return false;
930 }
931
932 return true;
933 }
934
935 /*
936 * Install everything needed
937 */
938 static public function install() {
939 $config = Customify_Settings::get_plugin_config();
940
941 $defaults = array(
942
943 # Hidden fields
944 'settings_saved_once' => '0',
945 # General
946 'values_store_mod' => 'theme_mod',
947
948 'typography' => true,
949 'typography_standard_fonts' => true,
950 'typography_google_fonts' => true,
951 'typography_group_google_fonts' => true,
952 'typography_cloud_fonts' => true,
953 'disable_default_sections' => array(),
954 'disable_customify_sections' => array(),
955 'enable_reset_buttons' => false,
956 'enable_editor_style' => true,
957 'style_resources_location' => 'wp_head'
958 );
959
960 $current_data = get_option( $config['settings-key'] );
961
962 if ( $current_data === false ) {
963 add_option( $config['settings-key'], $defaults );
964 } elseif ( count( array_diff_key( $defaults, $current_data ) ) != 0) {
965 $plugin_data = array_merge( $defaults, $current_data );
966 update_option( $config['settings-key'], $plugin_data );
967 }
968 }
969
970 /**
971 * Main PixCustomifyPlugin Instance
972 *
973 * Ensures only one instance of PixCustomifyPlugin is loaded or can be loaded.
974 *
975 * @since 1.0.0
976 * @static
977 *
978 * @param string $file File.
979 * @param string $version Version.
980 *
981 * @see PixCustomifyPlugin()
982 * @return PixCustomifyPlugin Main PixCustomifyPlugin instance
983 */
984 public static function instance( $file = '', $version = '1.0.0' ) {
985 // If the single instance hasn't been set, set it now.
986 if ( is_null( self::$_instance ) ) {
987 self::$_instance = new self( $file, $version );
988 }
989
990 return self::$_instance;
991 }
992
993 /**
994 * Cloning is forbidden.
995 *
996 * @since 1.5.0
997 */
998 public function __clone() {
999
1000 _doing_it_wrong( __FUNCTION__, esc_html__( 'You should not do that!', 'customify' ), null );
1001 }
1002
1003 /**
1004 * Unserializing instances of this class is forbidden.
1005 *
1006 * @since 1.5.0
1007 */
1008 public function __wakeup() {
1009
1010 _doing_it_wrong( __FUNCTION__, esc_html__( 'You should not do that!', 'customify' ), null );
1011 }
1012 }
1013