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

class-customify-style-manager.php in Customify 1.7.1, at includes/class-customify-style-manager.php

1,329 lines 46.7 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 class Customify_Style_Manager {
4
5 /**
6 * Holds the only instance of this class.
7 * @var null|Customify_Style_Manager
8 * @access protected
9 * @since 1.7.0
10 */
11 protected static $_instance = null;
12
13 /**
14 * The main plugin object (the parent).
15 * @var PixCustomifyPlugin
16 * @access public
17 * @since 1.7.0
18 */
19 public $parent = null;
20
21 /**
22 * External REST API endpoints used for communicating with the Pixelgrade Cloud.
23 * @var array
24 * @access public
25 * @since 1.7.0
26 */
27 public static $externalApiEndpoints;
28
29 /**
30 * The current design assets config.
31 * @var array
32 * @access public
33 * @since 1.7.0
34 */
35 public $design_assets = null;
36
37 /**
38 * The external theme config.
39 * @var array
40 * @access public
41 * @since 1.7.5
42 */
43 public $external_theme_config = null;
44
45 /**
46 * Constructor.
47 *
48 * @since 1.7.0
49 *
50 * @param $parent
51 */
52 protected function __construct( $parent = null ) {
53 $this->parent = $parent;
54
55 // Make sure our constants are in place, if not already defined.
56 defined( 'PIXELGRADE_CLOUD__API_BASE' ) || define( 'PIXELGRADE_CLOUD__API_BASE', 'https://cloud.pixelgrade.com/' );
57
58 // Save the external API endpoints in a easy to get property.
59 self::$externalApiEndpoints = apply_filters( 'customify_style_manager_external_api_endpoints', array(
60 'cloud' => array(
61 'getDesignAssets' => array(
62 'method' => 'GET',
63 'url' => PIXELGRADE_CLOUD__API_BASE . 'wp-json/pixcloud/v1/front/design_assets',
64 ),
65 'stats' => array(
66 'method' => 'POST',
67 'url' => PIXELGRADE_CLOUD__API_BASE . 'wp-json/pixcloud/v1/front/stats',
68 ),
69 ),
70 ) );
71
72 // Hook up.
73 $this->add_hooks();
74 }
75
76 /**
77 * Initiate our hooks
78 *
79 * @since 1.7.0
80 */
81 public function add_hooks() {
82 /*
83 * Handle the Customizer Style Manager base config.
84 */
85 add_filter( 'customify_filter_fields', array( $this, 'style_manager_section_base_config' ), 12, 1 );
86 // This needs to come after the external theme config has been applied
87 add_filter( 'customify_filter_fields', array( $this, 'add_current_color_palette_control' ), 110, 1 );
88
89 /*
90 * Handle the external theme configuration logic. We use a late priority to be able to overwrite if we have to.
91 */
92 add_filter( 'customify_filter_fields', array( $this, 'maybe_activate_external_theme_config' ), 10, 1 );
93 add_filter( 'customify_filter_fields', array( $this, 'maybe_apply_external_theme_config' ), 100, 1 );
94 // Maybe the theme has instructed us to do things like removing sections or controls.
95 add_action( 'customize_register', array( $this, 'maybe_process_external_theme_config_extras' ), 11 );
96
97 // Determine if we should use the config in the theme root and skip the external config.
98 if ( defined('CUSTOMIFY_SM_LOAD_THEME_ROOT_CONFIG') && true === CUSTOMIFY_SM_LOAD_THEME_ROOT_CONFIG ) {
99 add_filter( 'customify_style_manager_maybe_fetch_design_assets', array( $this, 'maybe_load_external_config_from_theme_root' ), 10, 1 );
100 add_filter( 'customize_controls_print_styles', array( $this, 'maybe_output_json_external_config_from_theme_root' ), 0 );
101 }
102
103 /*
104 * Handle the logic on settings update/save.
105 */
106 add_action( 'customize_save_after', array( $this, 'update_custom_palette_in_use' ), 10, 1 );
107
108 /*
109 * Handle the logic for user feedback.
110 */
111 add_action( 'customize_controls_print_footer_scripts', array( $this, 'output_user_feedback_modal' ) );
112 add_action( 'wp_ajax_customify_style_manager_user_feedback', array( $this, 'user_feedback_callback' ) );
113
114 /*
115 * Scripts enqueued in the Customizer.
116 */
117 add_action( 'customize_controls_init', array( $this, 'register_admin_customizer_scripts' ), 10 );
118 add_action( 'customize_controls_enqueue_scripts', array( $this, 'enqueue_admin_customizer_scripts' ), 10 );
119 }
120
121 /**
122 * Register Customizer admin scripts
123 */
124 function register_admin_customizer_scripts() {
125 wp_register_script( $this->parent->get_slug() . '-swap-values', plugins_url( 'js/customizer/customify-swap-values.js', $this->parent->file ), array( 'jquery' ), $this->parent->get_version() );
126 wp_register_script( $this->parent->get_slug() . '-palette-variations', plugins_url( 'js/customizer/customify-palette-variations.js', $this->parent->file ), array( 'jquery' ), $this->parent->get_version() );
127 wp_register_script( $this->parent->get_slug() . '-palettes', plugins_url( 'js/customizer/customify-palettes.js', $this->parent->file ), array( 'jquery', $this->parent->get_slug() . '-palette-variations', $this->parent->get_slug() . '-swap-values' ), $this->parent->get_version() );
128 }
129
130 /**
131 * Enqueue Customizer admin scripts
132 */
133 function enqueue_admin_customizer_scripts() {
134 // If there is no style manager support, bail early.
135 if ( ! $this->is_supported() ) {
136 return;
137 }
138
139 wp_enqueue_script( $this->parent->get_slug() . '-palettes' );
140 }
141
142 /**
143 * Determine if Style Manager is supported.
144 *
145 * @since 1.7.0
146 *
147 * @return bool
148 */
149 public function is_supported() {
150 $has_support = (bool) current_theme_supports( 'customizer_style_manager' );
151
152 return apply_filters( 'customify_style_manager_is_supported', $has_support );
153 }
154
155 /**
156 * Setup the Style Manager Customizer section base config.
157 *
158 * This handles the base configuration for the controls in the Style Manager section. We expect other parties (e.g. the theme),
159 * to come and fill up the missing details (e.g. connected fields).
160 *
161 * @since 1.7.0
162 *
163 * @param array $config This holds required keys for the plugin config like 'opt-name', 'panels', 'settings'
164 * @return array
165 */
166 public function style_manager_section_base_config( $config ) {
167 // If there is no style manager support, bail early.
168 if ( ! $this->is_supported() ) {
169 return $config;
170 }
171
172 if ( ! isset( $config['sections']['style_manager_section'] ) ) {
173 $config['sections']['style_manager_section'] = array();
174 }
175
176 // The section might be already defined, thus we merge, not replace the entire section config.
177 $config['sections']['style_manager_section'] = array_replace_recursive( $config['sections']['style_manager_section'], array(
178 'title' => esc_html__( 'Style Manager', 'customify' ),
179 'section_id' => 'style_manager_section', // We will force this section id preventing prefixing and other regular processing.
180 'priority' => 1,
181 'options' => array(
182 'sm_color_palette' => array(
183 'type' => 'preset',
184 // We will bypass the plugin setting regarding where to store - we will store it cross-theme in wp_options
185 'setting_type' => 'option',
186 // We will force this setting id preventing prefixing and other regular processing.
187 'setting_id' => 'sm_color_palette',
188 // We don't want to refresh the preview window, even though we have no direct effect on it through this field.
189 'live' => true,
190 'label' => esc_html__( 'Select a color palette:', 'customify' ),
191 'desc' => esc_html__( 'Conveniently change the design of your site with color palettes. Easy as pie.', 'customify' ),
192 'default' => 'lilac',
193 'choices_type' => 'color_palette',
194 'choices' => $this->get_color_palettes(),
195 ),
196 'sm_color_palette_variation' => array(
197 'type' => 'radio',
198 'setting_type' => 'option',
199 'setting_id' => 'sm_color_palette_variation',
200 'label' => esc_html__( 'Palette Variation', 'customify' ),
201 'default' => 'light',
202 'live' => true,
203 'choices' => array(
204 'light' => esc_html__( 'light', 'customify' ),
205 'light_alt' => esc_html__( 'light_alt', 'customify' ),
206
207 'dark' => esc_html__( 'dark', 'customify' ),
208 'dark_alt' => esc_html__( 'dark_alt', 'customify' ),
209
210 'colorful' => esc_html__( 'colorful', 'customify' ),
211 'colorful_alt' => esc_html__( 'colorful_alt', 'customify' ),
212 ),
213 ),
214 'sm_color_primary' => array(
215 'type' => 'color',
216 // We will bypass the plugin setting regarding where to store - we will store it cross-theme in wp_options
217 'setting_type' => 'option',
218 // We will force this setting id preventing prefixing and other regular processing.
219 'setting_id' => 'sm_color_primary',
220 // We don't want to refresh the preview window, even though we have no direct effect on it through this field.
221 'live' => true,
222 'label' => esc_html__( 'Color Primary', 'customify' ),
223 'default' => '#ffeb00',
224 'connected_fields' => array(),
225 ),
226 'sm_color_secondary' => array(
227 'type' => 'color',
228 'setting_type' => 'option',
229 'setting_id' => 'sm_color_secondary',
230 'live' => true,
231 'label' => esc_html__( 'Color Secondary', 'customify' ),
232 'default' => '#00ecff',
233 'connected_fields' => array(),
234 ),
235 'sm_color_tertiary' => array(
236 'type' => 'color',
237 'setting_type' => 'option',
238 'setting_id' => 'sm_color_tertiary',
239 'live' => true,
240 'label' => esc_html__( 'Color Tertiary', 'customify' ),
241 'default' => '#00ecff',
242 'connected_fields' => array(),
243 ),
244 'sm_dark_primary' => array(
245 'type' => 'color',
246 'setting_type' => 'option',
247 'setting_id' => 'sm_dark_primary',
248 'live' => true,
249 'label' => esc_html__( 'Dark Primary', 'customify' ),
250 'default' => '#171617',
251 'connected_fields' => array(),
252 ),
253 'sm_dark_secondary' => array(
254 'type' => 'color',
255 'setting_type' => 'option',
256 'setting_id' => 'sm_dark_secondary',
257 'live' => true,
258 'label' => esc_html__( 'Dark Secondary', 'customify' ),
259 'default' => '#383c50',
260 'connected_fields' => array(),
261 ),
262 'sm_dark_tertiary' => array(
263 'type' => 'color',
264 'setting_type' => 'option',
265 'setting_id' => 'sm_dark_tertiary',
266 'live' => true,
267 'label' => esc_html__( 'Dark Tertiary', 'customify' ),
268 'default' => '#65726F',
269 'connected_fields' => array(),
270 ),
271 'sm_light_primary' => array(
272 'type' => 'color',
273 'setting_type' => 'option',
274 'setting_id' => 'sm_light_primary',
275 'live' => true,
276 'label' => esc_html__( 'Light Primary', 'customify' ),
277 'default' => '#ffffff',
278 'connected_fields' => array(),
279 ),
280 'sm_light_secondary' => array(
281 'type' => 'color',
282 'setting_type' => 'option',
283 'setting_id' => 'sm_light_secondary',
284 'live' => true,
285 'label' => esc_html__( 'Light Secondary', 'customify' ),
286 'default' => '#ffffff',
287 'connected_fields' => array(),
288 ),
289 'sm_light_tertiary' => array(
290 'type' => 'color',
291 'setting_type' => 'option',
292 'setting_id' => 'sm_light_tertiary',
293 'live' => true,
294 'label' => esc_html__( 'Light Tertiary', 'customify' ),
295 'default' => '#ffffff',
296 'connected_fields' => array(),
297 ),
298 'sm_swap_colors' => array(
299 'type' => 'button',
300 'setting_type' => 'option',
301 'setting_id' => 'sm_swap_colors',
302 'label' => esc_html__( 'Swap Colors', 'customify' ),
303 'action' => 'sm_swap_colors',
304 ),
305 'sm_swap_dark_light' => array(
306 'type' => 'button',
307 'setting_type' => 'option',
308 'setting_id' => 'sm_swap_dark_light',
309 'label' => esc_html__( 'Swap Dark ⇆ Light', 'customify' ),
310 'action' => 'sm_swap_dark_light',
311 ),
312 'sm_swap_colors_dark' => array(
313 'type' => 'button',
314 'setting_type' => 'option',
315 'setting_id' => 'sm_swap_colors_dark',
316 'label' => esc_html__( 'Swap Colors ⇆ Dark', 'customify' ),
317 'action' => 'sm_swap_colors_dark',
318 ),
319 'sm_swap_secondary_colors_dark' => array(
320 'type' => 'button',
321 'setting_type' => 'option',
322 'setting_id' => 'sm_swap_secondary_colors_dark',
323 'label' => esc_html__( 'Swap Secondary Color ⇆ Secondary Dark', 'customify' ),
324 'action' => 'sm_swap_secondary_colors_dark',
325 ),
326 'sm_advanced_toggle' => array(
327 'type' => 'button',
328 'setting_type' => 'option',
329 'setting_id' => 'sm_toggle_advanced_settings',
330 'label' => esc_html__( 'Toggle Advanced Settings', 'customify' ),
331 'action' => 'sm_toggle_advanced_settings',
332 ),
333 ),
334 ) );
335
336 return $config;
337 }
338
339 /**
340 * Add the current color palette control to the Style Manager section.
341 *
342 * @since 1.7.0
343 *
344 * @param array $config
345 * @return array
346 */
347 public function add_current_color_palette_control( $config ) {
348 // If there is no style manager support, bail early.
349 if ( ! $this->is_supported() ) {
350 return $config;
351 }
352
353 if ( ! isset( $config['sections']['style_manager_section'] ) ) {
354 $config['sections']['style_manager_section'] = array();
355 }
356
357 $current_palette = '';
358 $current_palette_sets = array( 'current', 'next' );
359
360 $master_color_controls_ids = $this->get_all_master_color_controls_ids( $config['sections']['style_manager_section']['options'] );
361
362 foreach ( $current_palette_sets as $set ) {
363 $current_palette .= '<div class="colors ' . $set . '">';
364 foreach ( $master_color_controls_ids as $setting_id ) {
365 if ( ! empty( $config['sections']['style_manager_section']['options'][ $setting_id ]['connected_fields'] ) ) {
366 $current_palette .=
367 '<div class="color ' . $setting_id . '" data-setting="' . $setting_id . '">' . PHP_EOL .
368 '<div class="fill"></div>' . PHP_EOL .
369 '<div class="picker"><i></i></div>' . PHP_EOL .
370 '</div>' . PHP_EOL;
371 }
372 }
373 $current_palette .= '</div>';
374 }
375
376 // The section might be already defined, thus we merge, not replace the entire section config.
377 $config['sections']['style_manager_section']['options'] = array(
378 'sm_current_color_palette' => array(
379 'type' => 'html',
380 'html' =>
381 '<div class="palette-container">' . PHP_EOL .
382 '<span class="customize-control-title">Current Color Palette:</span>' . PHP_EOL .
383 '<span class="description customize-control-description">Choose a color palette to start with. Adjust its style using the variation buttons below.</span>' . PHP_EOL .
384 '<div class="c-palette">' . PHP_EOL .
385 $current_palette .
386 '<div class="c-palette__overlay">' . PHP_EOL .
387 '<div class="c-palette__label">' .
388 '<div class="c-palette__name">' . 'Original Style' . '</div>' .
389 '<div class="c-palette__control variation-light active" data-target="#_customize-input-sm_color_palette_variation_control-radio-light">' .
390 '<span class="dashicons dashicons-image-rotate"></span>' .
391 '<div class="c-palette__tooltip">Light</div>' .
392 '</div>' .
393 '<div class="c-palette__control variation-dark" data-target="#_customize-input-sm_color_palette_variation_control-radio-dark">' .
394 '<span class="dashicons dashicons-image-filter"></span>'.
395 '<div class="c-palette__tooltip">Dark</div>' .
396 '</div>' .
397 '<div class="c-palette__control variation-colorful" data-target="#_customize-input-sm_color_palette_variation_control-radio-colorful">' .
398 '<span class="dashicons dashicons-admin-appearance"></span>' .
399 '<div class="c-palette__tooltip">Colorful</div>' .
400 '</div>' .
401 '</div>' . PHP_EOL .
402 '</div>' . PHP_EOL .
403 '</div>' . PHP_EOL .
404 '</div>' . PHP_EOL .
405 '<svg class="c-palette__blur" width="15em" height="15em" viewBox="0 0 15 15" xmlns="http://www.w3.org/2000/svg" version="1.1">' . PHP_EOL .
406 '<defs>' . PHP_EOL .
407 '<filter id="goo">' . PHP_EOL .
408 '<feGaussianBlur in="SourceGraphic" stdDeviation="10" result="blur" />' . PHP_EOL .
409 '<feColorMatrix in="blur" mode="matrix" values="1 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 0 50 -20" result="goo" />' . PHP_EOL .
410 '<feBlend in="SourceGraphic" in2="goo" />' . PHP_EOL .
411 '</filter>' . PHP_EOL .
412 '</defs>' . PHP_EOL .
413 '</svg>',
414 ),
415 ) + $config['sections']['style_manager_section']['options'];
416
417 return $config;
418 }
419
420 /**
421 * Maybe activate an external theme config.
422 *
423 * If the conditions are met, activate an external theme config by declaring support for the style manager and saving the config.
424 *
425 * @since 1.7.5
426 *
427 * @param array $config This holds required keys for the plugin config like 'opt-name', 'panels', 'settings'
428 * @return array
429 */
430 public function maybe_activate_external_theme_config( $config ) {
431 // If somebody else already declared support for the Style Manager, we stop and let them have it.
432 if ( $this->is_supported() ) {
433 return $config;
434 }
435
436 $external_theme_config = false;
437
438 // First gather details about the current (parent) theme.
439 $theme = wp_get_theme( get_template() );
440 // Bail if for some strange reason we couldn't find the theme.
441 if ( ! $theme->exists() ) {
442 return $config;
443 }
444
445 // Now determine if we have a theme config for the current theme.
446 $design_assets = $this->get_design_assets();
447 if ( empty( $design_assets['theme_configs'] ) || ! is_array( $design_assets['theme_configs'] ) ) {
448 return $config;
449 }
450
451 $theme_configs = $design_assets['theme_configs'];
452
453 // We will go through every theme config and determine it's match score
454 foreach ( $theme_configs as $hashid => $theme_config ) {
455 // Loose matching means that the theme doesn't have to match all the conditions.
456 $loose_match = false;
457 if ( ! empty( $theme_config['loose_match'] ) ) {
458 $loose_match = true;
459 }
460
461 $matches = 0;
462 $total = 0;
463 if ( ! empty( $theme_config['name'] ) && $theme_config['name'] == $theme->get('Name') ) {
464 $matches++;
465 $total++;
466 }
467 if ( ! empty( $theme_config['slug'] ) && $theme_config['slug'] == $theme->get_stylesheet() ) {
468 $matches++;
469 $total++;
470 }
471 if ( ! empty( $theme_config['txtd'] ) && $theme_config['txtd'] == $theme->get('TextDomain') ) {
472 $matches++;
473 $total++;
474 }
475
476 $theme_configs[ $hashid ]['match_score'] = 0;
477 if ( true === $loose_match ) {
478 $theme_configs[ $hashid ]['match_score'] = $matches;
479 } elseif( $matches === $total ) {
480 $theme_configs[ $hashid ]['match_score'] = $matches;
481 }
482 }
483
484 // Now we will order the theme configs by match scores, descending and get the highest matching candidate, if any.
485 $theme_configs = Customify_Array::array_orderby( $theme_configs, 'match_score', SORT_DESC );
486 $external_theme_config = array_shift( $theme_configs );
487 // If we've ended up with a theme config with a zero match score, bail.
488 if ( empty( $external_theme_config['match_score'] ) || empty( $external_theme_config['config']['sections'] ) ) {
489 return $config;
490 }
491
492 // Now we have a theme config to work with. Save it for later use.
493 $this->external_theme_config = $external_theme_config;
494
495 // Declare support for the Style Manager if there is such a section in the config
496 if ( isset( $external_theme_config['config']['sections']['style_manager_section'] ) ) {
497 add_theme_support( 'customizer_style_manager' );
498 }
499
500 return $config;
501 }
502
503 /**
504 * Maybe apply an external theme config.
505 *
506 * If the conditions are met, apply an external theme config. Right now we are only handling sections and their controls.
507 *
508 * @since 1.7.5
509 *
510 * @param array $config This holds required keys for the plugin config like 'opt-name', 'panels', 'settings'
511 * @return array
512 */
513 public function maybe_apply_external_theme_config( $config ) {
514 // Bail if we have no external theme config data.
515 if ( empty( $this->external_theme_config ) ) {
516 return $config;
517 }
518
519 // Apply the theme config.
520 // If we are dealing with the Customify default config, we need a clean slate, sort of.
521 if ( 'customify_defaults' === $config['opt-name'] ) {
522 // We will save the Style Manager config so we can merge with it. But the rest goes away.
523 $style_manager_section = array();
524 if ( isset( $config['sections']['style_manager_section'] ) ) {
525 $style_manager_section = $config['sections']['style_manager_section'];
526 }
527
528 $config['opt-name'] = get_template() . '_options';
529 if ( ! empty( $this->external_theme_config['config']['opt-name'] ) ) {
530 $config['opt-name'] = $this->external_theme_config['config']['opt-name'];
531 }
532
533 $config['sections'] = array(
534 'style_manager_section' => $style_manager_section,
535 );
536 }
537
538 // Now merge things.
539 $config['sections'] = Customify_Array::array_merge_recursive_distinct( $config['sections'],$this->external_theme_config['config']['sections'] );
540
541 return $config;
542 }
543
544 /**
545 * Maybe process certain "commands" from the external theme config.
546 *
547 * Mainly things like removing sections, controls, etc.
548 *
549 * @since 1.7.5
550 *
551 * @param WP_Customize_Manager $wp_customize
552 */
553 public function maybe_process_external_theme_config_extras( $wp_customize ) {
554 // Bail if we have no external theme config data.
555 if ( empty( $this->external_theme_config ) ) {
556 return;
557 }
558
559 // Maybe remove panels
560 if ( ! empty( $this->external_theme_config['config']['remove_panels'] ) ) {
561 // Standardize it.
562 if ( is_string( $this->external_theme_config['config']['remove_panels'] ) ) {
563 $this->external_theme_config['config']['remove_panels'] = array( $this->external_theme_config['config']['remove_panels'] );
564 }
565
566 foreach ( $this->external_theme_config['config']['remove_panels'] as $panel_id ) {
567 $wp_customize->remove_panel( $panel_id );
568 }
569 }
570
571 // Maybe remove sections
572 if ( ! empty( $this->external_theme_config['config']['remove_sections'] ) ) {
573 // Standardize it.
574 if ( is_string( $this->external_theme_config['config']['remove_sections'] ) ) {
575 $this->external_theme_config['config']['remove_sections'] = array( $this->external_theme_config['config']['remove_sections'] );
576 }
577
578 foreach ( $this->external_theme_config['config']['remove_sections'] as $section_id ) {
579
580 if ( 'widgets' === $section_id ) {
581 global $wp_registered_sidebars;
582
583 foreach ( $wp_registered_sidebars as $widget => $settings ) {
584 $wp_customize->remove_section( 'sidebar-widgets-' . $widget );
585 }
586 continue;
587 }
588
589 $wp_customize->remove_section( $section_id );
590 }
591 }
592
593 // Maybe remove settings
594 if ( ! empty( $this->external_theme_config['config']['remove_settings'] ) ) {
595 // Standardize it.
596 if ( is_string( $this->external_theme_config['config']['remove_settings'] ) ) {
597 $this->external_theme_config['config']['remove_settings'] = array( $this->external_theme_config['config']['remove_settings'] );
598 }
599
600 foreach ( $this->external_theme_config['config']['remove_settings'] as $setting_id ) {
601 $wp_customize->remove_setting( $setting_id );
602 }
603 }
604
605 // Maybe remove controls
606 if ( ! empty( $this->external_theme_config['config']['remove_controls'] ) ) {
607 // Standardize it.
608 if ( is_string( $this->external_theme_config['config']['remove_controls'] ) ) {
609 $this->external_theme_config['config']['remove_controls'] = array( $this->external_theme_config['config']['remove_controls'] );
610 }
611
612 foreach ( $this->external_theme_config['config']['remove_controls'] as $control_id ) {
613 $wp_customize->remove_control( $control_id );
614 }
615 }
616 }
617
618 /**
619 * Get the color palettes configuration.
620 *
621 * @since 1.7.0
622 *
623 * @param bool $skip_cache Optional. Whether to use the cached config or fetch a new one.
624 * @return array
625 */
626 protected function get_color_palettes( $skip_cache = false ) {
627 // Get the design assets data.
628 $design_assets = $this->get_design_assets( $skip_cache );
629 if ( false === $design_assets || empty( $design_assets['color_palettes'] ) ) {
630 $color_palettes_config = $this->get_default_color_palettes_config();
631 } else {
632 $color_palettes_config = $design_assets['color_palettes'];
633 }
634
635 return apply_filters( 'customify_get_color_palettes', $color_palettes_config );
636 }
637
638 /**
639 * Get the themes configuration.
640 *
641 * @since 1.7.5
642 *
643 * @param bool $skip_cache Optional. Whether to use the cached config or fetch a new one.
644 * @return array
645 */
646 protected function get_theme_configs( $skip_cache = false ) {
647 // Get the design assets data.
648 $design_assets = $this->get_design_assets( $skip_cache );
649 if ( false === $design_assets || empty( $design_assets['theme_configs'] ) ) {
650 $theme_configs = $this->get_default_color_palettes_config();
651 } else {
652 $theme_configs = $design_assets['theme_configs'];
653 }
654
655 return apply_filters( 'customify_get_theme_configs', $theme_configs );
656 }
657
658 /**
659 * Get the design assets configuration.
660 *
661 * @since 1.7.0
662 *
663 * @param bool $skip_cache Optional. Whether to use the cached config or fetch a new one.
664 * @return array
665 */
666 protected function get_design_assets( $skip_cache = false ) {
667 if ( ! is_null( $this->design_assets ) ) {
668 return $this->design_assets;
669 }
670
671 $this->design_assets = apply_filters( 'customify_style_manager_maybe_fetch_design_assets', $this->maybe_fetch_design_assets( $skip_cache ) );
672
673 return $this->design_assets;
674 }
675
676 /**
677 * Fetch the design assets data from the Pixelgrade Cloud.
678 *
679 * Caches the data for 12 hours. Use local defaults if not available.
680 *
681 * @since 1.7.0
682 *
683 * @param bool $skip_cache Optional. Whether to use the cached data or fetch a new one.
684 * @return array|false
685 */
686 protected function maybe_fetch_design_assets( $skip_cache = false ) {
687 // First try and get the cached data
688 $data = get_option( $this->_get_design_assets_cache_key() );
689
690 // For performance reasons, we will ONLY fetch remotely when in the WP ADMIN area or via an ADMIN AJAX call, regardless of settings.
691 if ( ! is_admin() ) {
692 return $data;
693 }
694
695 // Get the cache data expiration timestamp.
696 $expire_timestamp = get_option( $this->_get_design_assets_cache_key() . '_timestamp' );
697
698 // We don't force skip the cache for AJAX requests for performance reasons.
699 if ( ! wp_doing_ajax() && defined('CUSTOMIFY_SM_ALWAYS_FETCH_DESIGN_ASSETS' ) && true === CUSTOMIFY_SM_ALWAYS_FETCH_DESIGN_ASSETS ) {
700 $skip_cache = true;
701 }
702
703 // The data isn't set, is expired or we were instructed to skip the cache; we need to fetch fresh data.
704 if ( true === $skip_cache || false === $data || false === $expire_timestamp || $expire_timestamp < time() ) {
705 $request_data = apply_filters( 'customify_pixelgrade_cloud_request_data', array(
706 'site_url' => home_url('/'),
707 // We are only interested in data needed to identify the theme and eventually deliver only design assets suitable for it.
708 'theme_data' => $this->get_active_theme_data(),
709 // We are only interested in data needed to identify the plugin version and eventually deliver design assets suitable for it.
710 'site_data' => $this->get_site_data(),
711 ), $this );
712
713 $request_args = array(
714 'method' => self::$externalApiEndpoints['cloud']['getDesignAssets']['method'],
715 'timeout' => 4,
716 'blocking' => true,
717 'body' => $request_data,
718 'sslverify' => false,
719 );
720 // Get the design assets from the cloud.
721 $response = wp_remote_request( self::$externalApiEndpoints['cloud']['getDesignAssets']['url'], $request_args );
722 // Bail in case of decode error or failure to retrieve data.
723 // We will return the data already available.
724 if ( is_wp_error( $response ) ) {
725 return $data;
726 }
727 $response_data = json_decode( wp_remote_retrieve_body( $response ), true );
728 // Bail in case of decode error or failure to retrieve data.
729 // We will return the data already available.
730 if ( null === $response_data || empty( $response_data['data'] ) || empty( $response_data['code'] ) || 'success' !== $response_data['code'] ) {
731 return $data;
732 }
733
734 $data = apply_filters( 'customify_style_manager_fetch_design_assets', $response_data['data'] );
735
736 // Cache the data in an option for 6 hours
737 update_option( $this->_get_design_assets_cache_key() , $data, true );
738 update_option( $this->_get_design_assets_cache_key() . '_timestamp' , time() + 6 * HOUR_IN_SECONDS, true );
739 }
740
741 return $data;
742 }
743
744 /**
745 * Get the design assets cache key.
746 *
747 * @since 1.7.0
748 *
749 * @return string
750 */
751 protected function _get_design_assets_cache_key() {
752 return 'customify_style_manager_design_assets';
753 }
754
755 /**
756 * Get the default (hard-coded) color palettes configuration.
757 *
758 * @since 1.7.0
759 *
760 * @return array
761 */
762 protected function get_default_color_palettes_config() {
763 $default_color_palettes = array(
764 'vasco' => array(
765 'label' => esc_html__( 'Restful Beach', 'customify' ),
766 'preview' => array(
767 'background_image_url' => 'http://pxgcdn.com/images/style-manager/color-palettes/vasco-theme-palette.jpg',
768 ),
769 'options' => array(
770 'sm_color_primary' => '#38C3C8',
771 'sm_color_secondary' => '#F59828',
772 'sm_color_tertiary' => '#FB551C',
773 'sm_dark_primary' => '#2b2b28',
774 'sm_dark_secondary' => '#2B3D39',
775 'sm_dark_tertiary' => '#65726F',
776 'sm_light_primary' => '#F5F6F1',
777 'sm_light_secondary' => '#E6F7F7',
778 'sm_light_tertiary' => '#FAEDE8',
779 ),
780 ),
781 'felt' => array(
782 'label' => esc_html__( 'Warm Summer', 'customify' ),
783 'preview' => array(
784 'background_image_url' => 'http://pxgcdn.com/images/style-manager/color-palettes/felt-theme-palette.jpg',
785 ),
786 'options' => array(
787 'sm_color_primary' => '#ff6000',
788 'sm_color_secondary' => '#FF9200',
789 'sm_color_tertiary' => '#FF7019',
790 'sm_dark_primary' => '#1C1C1C',
791 'sm_dark_secondary' => '#161616',
792 'sm_dark_tertiary' => '#161616',
793 'sm_light_primary' => '#FFFCFC',
794 'sm_light_secondary' => '#FFF4E8',
795 'sm_light_tertiary' => '#F7F3F0',
796 ),
797 ),
798 'julia' => array(
799 'label' => esc_html__( 'Serenity', 'customify' ),
800 'preview' => array(
801 'background_image_url' => 'http://pxgcdn.com/images/style-manager/color-palettes/julia-theme-palette.jpg',
802 ),
803 'options' => array(
804 'sm_color_primary' => '#3349B8',
805 'sm_color_secondary' => '#3393B8',
806 'sm_color_tertiary' => '#C18866',
807 'sm_dark_primary' => '#161616',
808 'sm_dark_secondary' => '#383C50',
809 'sm_dark_tertiary' => '#383C50',
810 'sm_light_primary' => '#f7f6f5',
811 'sm_light_secondary' => '#E7F2F8',
812 'sm_light_tertiary' => '#F7ECE6',
813 ),
814 ),
815 'gema' => array(
816 'label' => esc_html__( 'Burning Red', 'customify' ),
817 'preview' => array(
818 'background_image_url' => 'http://pxgcdn.com/images/style-manager/color-palettes/gema-theme-palette.jpg',
819 ),
820 'options' => array(
821 'sm_color_primary' => '#E03A3A',
822 'sm_color_secondary' => '#F75034',
823 'sm_color_tertiary' => '#AD2D2D',
824 'sm_dark_primary' => '#000000',
825 'sm_dark_secondary' => '#000000',
826 'sm_dark_tertiary' => '#A3A3A1',
827 'sm_light_primary' => '#FFFFFF',
828 'sm_light_secondary' => '#F7F5F5',
829 'sm_light_tertiary' => '#F7F2F2',
830 ),
831 ),
832 'patch' => array(
833 'label' => esc_html__( 'Fresh Lemon', 'customify' ),
834 'preview' => array(
835 'background_image_url' => 'http://pxgcdn.com/images/style-manager/color-palettes/patch-theme-palette.jpg',
836 ),
837 'options' => array(
838 'sm_color_primary' => '#ffeb00',
839 'sm_color_secondary' => '#19CDFF',
840 'sm_color_tertiary' => '#0BE8DD',
841 'sm_dark_primary' => '#171617',
842 'sm_dark_secondary' => '#3d3e40',
843 'sm_dark_tertiary' => '#b5b5b5',
844 'sm_light_primary' => '#FFFFFF',
845 'sm_light_secondary' => '#E8FAFF',
846 'sm_light_tertiary' => '#F2FFFE',
847 ),
848 ),
849 'silk' => array(
850 'label' => esc_html__( 'Floral Bloom', 'customify' ),
851 'preview' => array(
852 'background_image_url' => 'http://pxgcdn.com/images/style-manager/color-palettes/silk-theme-palette.jpg',
853 ),
854 'options' => array(
855 'sm_color_primary' => '#A33B61',
856 'sm_color_secondary' => '#FCC9B0',
857 'sm_color_tertiary' => '#C9648A',
858 'sm_dark_primary' => '#000000',
859 'sm_dark_secondary' => '#000000',
860 'sm_dark_tertiary' => '#A3A3A1',
861 'sm_light_primary' => '#FFFFFF',
862 'sm_light_secondary' => '#F7F5F6',
863 'sm_light_tertiary' => '#F7F0F3',
864 ),
865 ),
866 'hive' => array(
867 'label' => esc_html__( 'Powerful', 'customify' ),
868 'preview' => array(
869 'background_image_url' => 'http://pxgcdn.com/images/style-manager/color-palettes/hive-theme-palette.jpg',
870 ),
871 'options' => array(
872 'sm_color_primary' => '#ffeb00',
873 'sm_color_secondary' => '#3200B2',
874 'sm_color_tertiary' => '#740AC9',
875 'sm_dark_primary' => '#171617',
876 'sm_dark_secondary' => '#171617',
877 'sm_dark_tertiary' => '#363636',
878 'sm_light_primary' => '#FFFFFF',
879 'sm_light_secondary' => '#F2F5F7',
880 'sm_light_tertiary' => '#F5F2F7',
881 ),
882 ),
883 'lilac' => array(
884 'label' => esc_html__( 'Soft Lilac', 'customify' ),
885 'preview' => array(
886 'background_image_url' => 'http://pxgcdn.com/images/style-manager/color-palettes/lilac-palette.jpg',
887 ),
888 'options' => array(
889 'sm_color_primary' => '#DD8CA9',
890 'sm_color_secondary' => '#8C9CDE',
891 'sm_color_tertiary' => '#E3B4A6',
892 'sm_dark_primary' => '#1A1A1A',
893 'sm_dark_secondary' => '#303030',
894 'sm_dark_tertiary' => '#A3A3A1',
895 'sm_light_primary' => '#F0F2F1',
896 'sm_light_secondary' => '#CED5F2',
897 'sm_light_tertiary' => '#F7E1DA',
898 ),
899 ),
900 );
901
902 return apply_filters( 'customify_style_manager_default_color_palettes', $default_color_palettes );
903 }
904
905 /**
906 * Include the customify "external" config file in the theme root and overwrite the existing theme configs.
907 *
908 * @param array $design_assets
909 *
910 * @return array
911 */
912 public function maybe_load_external_config_from_theme_root( $design_assets ) {
913 $file_name = 'customify_theme_root.php';
914
915 // First gather details about the current (parent) theme.
916 $theme = wp_get_theme( get_template() );
917 // Bail if for some strange reason we couldn't find the theme.
918 if ( ! $theme->exists() ) {
919 return $design_assets;
920 }
921
922 $file = trailingslashit( $theme->get_template_directory() ) . $file_name;
923 if ( ! file_exists( $file ) ) {
924 return $design_assets;
925 }
926
927 // We expect to get from the file include a $config variable with the entire Customify (partial) config.
928 include $file;
929
930 if ( ! isset( $config ) || ! is_array( $config ) || empty( $config['sections'] ) ) {
931 // Alert the developers that things are not alright.
932 _doing_it_wrong( __METHOD__, 'The Customify theme root config is not good! Please check it! We will not apply it.', null );
933
934 return $design_assets;
935 }
936
937 // Construct the pseudo-external theme config.
938 // Start with a clean slate.
939 $design_assets['theme_configs'] = array();
940
941 $design_assets['theme_configs']['theme_root'] = array(
942 'id' => 1,
943 'name' => $theme->get( 'Name' ),
944 'slug' => $theme->get_stylesheet(),
945 'txtd' => $theme->get( 'TextDomain' ),
946 'loose_match' => true,
947 'config' => $config,
948 'created' => '2018-05-16 15:13:58',
949 'last_modified' => '2018-05-16 15:13:58',
950 'hashid' => 'theme_root',
951 );
952
953 return $design_assets;
954 }
955
956 /**
957 * Output the theme root JSON in the Customizer page source.
958 */
959 public function maybe_output_json_external_config_from_theme_root() {
960 if ( ! empty( $this->external_theme_config['config'] ) ) {
961 // Also output the JSON in a special hidden div for easy copy pasting.
962 // Also remove any multiple tabs.
963 echo PHP_EOL . '<!--' . PHP_EOL . 'Just copy&paste this:' . PHP_EOL . PHP_EOL . trim( str_replace( '\t\t', '', json_encode( $this->external_theme_config['config'] ) ) ) . PHP_EOL . PHP_EOL . '-->' . PHP_EOL;
964 }
965 }
966
967 /**
968 * Get the active theme data.
969 *
970 * @since 1.7.0
971 *
972 * @return array
973 */
974 public function get_active_theme_data() {
975 $theme_data = array();
976
977 $slug = basename( get_template_directory() );
978
979 $theme_data['slug'] = $slug;
980
981 // Get the current theme style.css data.
982 $current_theme = wp_get_theme( get_template() );
983 if ( ! empty( $current_theme ) && ! is_wp_error( $current_theme ) ) {
984 $theme_data['name'] = $current_theme->get('Name');
985 $theme_data['themeuri'] = $current_theme->get('ThemeURI');
986 $theme_data['version'] = $current_theme->get('Version');
987 $theme_data['textdomain'] = $current_theme->get('TextDomain');
988 }
989
990 // Maybe get the WUpdates theme info if it's a theme delivered from WUpdates.
991 $wupdates_ids = apply_filters( 'wupdates_gather_ids', array() );
992 if ( ! empty( $wupdates_ids[ $slug ] ) ) {
993 $theme_data['wupdates'] = $wupdates_ids[ $slug ];
994 }
995
996 return apply_filters( 'customify_style_manager_get_theme_data', $theme_data );
997 }
998
999 /**
1000 * Get the site data.
1001 *
1002 * @since 1.7.0
1003 *
1004 * @return array
1005 */
1006 public function get_site_data() {
1007 $site_data = array(
1008 'url' => home_url('/'),
1009 'is_ssl' => is_ssl(),
1010 );
1011
1012 $site_data['wp'] = array(
1013 'version' => get_bloginfo('version'),
1014 );
1015
1016 $site_data['customify'] = array(
1017 'version' => PixCustomifyPlugin()->get_version(),
1018 );
1019
1020 $site_data['color_palettes'] = array(
1021 'current' => $this->get_current_color_palette(),
1022 'variation' => $this->get_current_color_palette_variation(),
1023 'custom' => $this->is_using_custom_color_palette(),
1024 );
1025
1026 return apply_filters( 'customify_style_manager_get_site_data', $site_data );
1027 }
1028
1029 /**
1030 * Get the current color palette ID or false if none is selected.
1031 *
1032 * @since 1.7.0
1033 *
1034 * @return string|false
1035 */
1036 protected function get_current_color_palette() {
1037 return get_option( 'sm_color_palette', false );
1038 }
1039
1040 /**
1041 * Get the current color palette variation ID or false if none is selected.
1042 *
1043 * @since 1.7.0
1044 *
1045 * @return string|false
1046 */
1047 protected function get_current_color_palette_variation() {
1048 return get_option( 'sm_color_palette_variation', false );
1049 }
1050
1051 /**
1052 * Determine if the selected color palette has been customized and remember this in an option.
1053 *
1054 * @since 1.7.0
1055 *
1056 * @return bool
1057 */
1058 public function update_custom_palette_in_use() {
1059 // If there is no style manager support, bail early.
1060 if ( ! $this->is_supported() ) {
1061 return false;
1062 }
1063
1064 $current_palette = $this->get_current_color_palette();
1065 if ( empty( $current_palette ) ) {
1066 return false;
1067 }
1068
1069 $color_palettes = $this->get_color_palettes();
1070 if ( ! isset( $color_palettes[ $current_palette ] ) || empty( $color_palettes[ $current_palette ]['options'] ) ) {
1071 return false;
1072 }
1073
1074 $is_custom_palette = false;
1075 // If any of the current master colors has a different value than the one provided by the color palette,
1076 // it means a custom color palette is in use.
1077 $current_palette_options = $color_palettes[ $current_palette ]['options'];
1078 foreach ( $current_palette_options as $setting_id => $value ) {
1079 if ( $value != get_option( $setting_id ) ) {
1080 $is_custom_palette = true;
1081 break;
1082 }
1083 }
1084
1085 update_option( 'sm_is_custom_color_palette', $is_custom_palette, true );
1086
1087 do_action( 'customify_style_manager_updated_custom_palette_in_use', $is_custom_palette, $this );
1088
1089 return true;
1090 }
1091
1092 /**
1093 * Determine if a custom color palette is in use.
1094 *
1095 * @since 1.7.0
1096 *
1097 * @return bool
1098 */
1099 protected function is_using_custom_color_palette(){
1100 return (bool) get_option( 'sm_is_custom_color_palette', false );
1101 }
1102
1103 /**
1104 * Get all the defined Style Manager master color field ids.
1105 *
1106 * @since 1.7.0
1107 *
1108 * @param array $options
1109 * @return array
1110 */
1111 public function get_all_master_color_controls_ids( $options ) {
1112 $master_color_controls = array();
1113
1114 foreach ( $options as $option_id => $option_settings ) {
1115 if ( 'color' === $option_settings['type'] ) {
1116 $master_color_controls[] = $option_id;
1117 }
1118 }
1119
1120 return $master_color_controls;
1121 }
1122
1123 /**
1124 * Output the user feedback modal markup, if we need to.
1125 *
1126 * @since 1.7.0
1127 */
1128 public function output_user_feedback_modal() {
1129 // If there is no style manager support, bail early.
1130 if ( ! $this->is_supported() ) {
1131 return;
1132 }
1133
1134 // Only output if the user didn't provide feedback.
1135 if ( ! $this->user_provided_feedback() ) { ?>
1136 <div id="style-manager-user-feedback-modal">
1137 <div class="modal">
1138 <div class="modal-dialog" role="document">
1139 <div class="modal-content">
1140 <form id="style-manager-user-feedback" action="#" method="post">
1141 <input type="hidden" name="type" value="1_to_5" />
1142 <div class="modal-header">
1143 <button type="button" class="close icon media-modal-close" data-dismiss="modal" aria-label="Close"><span class="media-modal-icon"><span class="screen-reader-text">Close media panel</span></span></button>
1144 <!-- <a href="#" class="close button button--naked gray" data-dismiss="modal" aria-label="Close">Close</a> -->
1145 </div>
1146 <div class="modal-body full">
1147 <div class="box box--large">
1148 <div class="first-step">
1149 <h2 class="modal-title">How would you rate your experience with using Color Palettes?</h2>
1150 <div class="scorecard">
1151 <span>Worst</span>
1152 <label>
1153 <input type="radio" name="rating" value="1" required />
1154 <span>1</span>
1155 </label>
1156 <label>
1157 <input type="radio" name="rating" value="2" required />
1158 <span>2</span>
1159 </label>
1160 <label>
1161 <input type="radio" name="rating" value="3" required />
1162 <span>3</span>
1163 </label>
1164 <label>
1165 <input type="radio" name="rating" value="4" required />
1166 <span>4</span>
1167 </label>
1168 <label>
1169 <input type="radio" name="rating" value="5" required />
1170 <span>5</span>
1171 </label>
1172 <span>Best</span>
1173 </div>
1174 </div>
1175 <div class="second-step hidden">
1176 <p><strong>What makes you give <span class="rating-placeholder">5</span>*?</strong> I hope you’ll answer and help us do better:</p>
1177 <div class="not-floating-labels">
1178 <div class="form-row field">
1179 <textarea name="message" placeholder="Your message.."
1180 id="style-manager-user-feedback-message" rows="4" oninvalid="this.setCustomValidity('May we have a little more info about your experience?')" oninput="setCustomValidity('')" required></textarea>
1181 </div>
1182 </div>
1183 <button id="style-manager-user-feedback_btn" class="button" type="submit"><?php _e( 'Submit my feedback', 'customify' ); ?></button>
1184 </div>
1185 <div class="thanks-step hidden">
1186 <h3 class="modal-title">Thanks for your feedback!</h3>
1187 <p>This will help us improve the product. Stay awesome! 🤗</p>
1188 </div>
1189 <div class="error-step hidden">
1190 <h3 class="modal-title">We've hit a snag!</h3>
1191 <p>We couldn't record your feedback and we would truly appreciate it if you would try it again at a latter time. Stay awesome! 🤗</p>
1192 </div>
1193 </div>
1194 </div>
1195 <div class="modal-footer full">
1196
1197 </div>
1198 </form>
1199 </div>
1200 </div>
1201 </div>
1202 <!-- End Modal -->
1203 <!-- Modal Backdrop (Shadow) -->
1204 <div class="modal-backdrop"></div>
1205 </div>
1206
1207 <?php }
1208 }
1209
1210 /**
1211 * @param bool|int $timestamp_limit Optional. Timestamp to compare the time the user provided feedback.
1212 * If the provided timestamp is earlier than the time the user provided feedback, returns false.
1213 *
1214 * @return bool
1215 */
1216 public function user_provided_feedback( $timestamp_limit = false ) {
1217 if ( defined( 'CUSTOMIFY_SM_ALWAYS_ASK_FOR_FEEDBACK' ) && true === CUSTOMIFY_SM_ALWAYS_ASK_FOR_FEEDBACK ) {
1218 return false;
1219 }
1220
1221 $user_provided_feedback = get_option( 'style_manager_user_feedback_provided' );
1222 if ( empty( $user_provided_feedback ) ) {
1223 return false;
1224 }
1225
1226 if ( ! empty( $timestamp ) && is_int( $timestamp ) && $timestamp_limit > $user_provided_feedback ) {
1227 return false;
1228 }
1229
1230 return true;
1231 }
1232
1233 /**
1234 * Callback for the user feedback AJAX call.
1235 *
1236 * @since 1.7.0
1237 */
1238 public function user_feedback_callback() {
1239 check_ajax_referer( 'customify_style_manager_user_feedback', 'nonce' );
1240
1241 if ( empty( $_POST['type'] ) ) {
1242 wp_send_json_error( esc_html__( 'No type provided', 'customify' ) );
1243 }
1244
1245 if ( empty( $_POST['rating'] ) ) {
1246 wp_send_json_error( esc_html__( 'No rating provided', 'customify' ) );
1247 }
1248
1249 $type = sanitize_text_field( $_POST['type'] );
1250 $rating = intval( $_POST['rating'] );
1251 $message = '';
1252 if ( ! empty( $_POST['message'] ) ) {
1253 $message = wp_kses_post( $_POST['message'] );
1254 }
1255
1256 $request_data = apply_filters( 'customify_pixelgrade_cloud_request_data', array(
1257 'site_url' => home_url( '/' ),
1258 'satisfaction_data' => array(
1259 'type' => $type,
1260 'rating' => $rating,
1261 'message' => $message,
1262 ),
1263 ), $this );
1264
1265 $request_args = array(
1266 'method' => self::$externalApiEndpoints['cloud']['stats']['method'],
1267 'timeout' => 5,
1268 'blocking' => true,
1269 'body' => $request_data,
1270 'sslverify' => false,
1271 );
1272
1273 // Send the feedback.
1274 $response = wp_remote_request( self::$externalApiEndpoints['cloud']['stats']['url'], $request_args );
1275 if ( is_wp_error( $response ) ) {
1276 wp_send_json_error( esc_html__( 'Sorry, something went wrong and we couldn\'t save your feedback.', 'customify' ) );
1277 }
1278 $response_data = json_decode( wp_remote_retrieve_body( $response ), true );
1279 // Bail in case of decode error or failure to retrieve data
1280 if ( null === $response_data || empty( $response_data['code'] ) || 'success' !== $response_data['code'] ) {
1281 wp_send_json_error( esc_html__( 'Sorry, something went wrong and we couldn\'t save your feedback.', 'customify' ) );
1282 }
1283
1284 // We need to remember that the user provided feedback (and at what timestamp).
1285 update_option( 'style_manager_user_feedback_provided', time(), true );
1286
1287 wp_send_json_success( esc_html__( 'Thank you for your feedback.', 'customify' ) );
1288 }
1289
1290 /**
1291 * Main Customify_Style_Manager Instance
1292 *
1293 * Ensures only one instance of Customify_Style_Manager is loaded or can be loaded.
1294 *
1295 * @since 1.7.0
1296 * @static
1297 * @param object $parent Main PixCustomifyPlugin instance.
1298 *
1299 * @return Customify_Style_Manager Main Customify_Style_Manager instance
1300 */
1301 public static function instance( $parent = null ) {
1302
1303 if ( is_null( self::$_instance ) ) {
1304 self::$_instance = new self( $parent );
1305 }
1306 return self::$_instance;
1307 } // End instance ()
1308
1309 /**
1310 * Cloning is forbidden.
1311 *
1312 * @since 1.7.0
1313 */
1314 public function __clone() {
1315
1316 _doing_it_wrong( __FUNCTION__,esc_html( __( 'Cheatin&#8217; huh?' ) ), null );
1317 } // End __clone ()
1318
1319 /**
1320 * Unserializing instances of this class is forbidden.
1321 *
1322 * @since 1.7.0
1323 */
1324 public function __wakeup() {
1325
1326 _doing_it_wrong( __FUNCTION__, esc_html( __( 'Cheatin&#8217; huh?' ) ), null );
1327 } // End __wakeup ()
1328 }
1329