PluginProbe
Customify / 1.7.2
Customify v1.7.2
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.2, at includes/class-customify-style-manager.php

1,333 lines 46.9 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 $current_palette .=
366 '<div class="color ' . $setting_id . '" data-setting="' . $setting_id . '">' . PHP_EOL .
367 '<div class="fill"></div>' . PHP_EOL .
368 '<div class="picker"><i></i></div>' . PHP_EOL .
369 '</div>' . PHP_EOL;
370 }
371 $current_palette .= '</div>';
372 }
373
374 // The section might be already defined, thus we merge, not replace the entire section config.
375 $config['sections']['style_manager_section']['options'] = array(
376 'sm_current_color_palette' => array(
377 'type' => 'html',
378 'html' =>
379 '<div class="palette-container">' . PHP_EOL .
380 '<span class="customize-control-title">Current Color Palette:</span>' . PHP_EOL .
381 '<span class="description customize-control-description">Choose a color palette to start with. Adjust its style using the variation buttons below.</span>' . PHP_EOL .
382 '<div class="c-palette">' . PHP_EOL .
383 $current_palette .
384 '<div class="c-palette__overlay">' . PHP_EOL .
385 '<div class="c-palette__label">' .
386 '<div class="c-palette__name">' . 'Original Style' . '</div>' .
387 '<div class="c-palette__control variation-light active" data-target="#_customize-input-sm_color_palette_variation_control-radio-light">' .
388 '<span class="dashicons dashicons-image-rotate"></span>' .
389 '<div class="c-palette__tooltip">Light</div>' .
390 '</div>' .
391 '<div class="c-palette__control variation-dark" data-target="#_customize-input-sm_color_palette_variation_control-radio-dark">' .
392 '<span class="dashicons dashicons-image-filter"></span>'.
393 '<div class="c-palette__tooltip">Dark</div>' .
394 '</div>' .
395 '<div class="c-palette__control variation-colorful" data-target="#_customize-input-sm_color_palette_variation_control-radio-colorful">' .
396 '<span class="dashicons dashicons-admin-appearance"></span>' .
397 '<div class="c-palette__tooltip">Colorful</div>' .
398 '</div>' .
399 '</div>' . PHP_EOL .
400 '</div>' . PHP_EOL .
401 '</div>' . PHP_EOL .
402 '</div>' . PHP_EOL .
403 '<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 .
404 '<defs>' . PHP_EOL .
405 '<filter id="goo">' . PHP_EOL .
406 '<feGaussianBlur in="SourceGraphic" stdDeviation="10" result="blur" />' . PHP_EOL .
407 '<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 .
408 '<feBlend in="SourceGraphic" in2="goo" />' . PHP_EOL .
409 '</filter>' . PHP_EOL .
410 '</defs>' . PHP_EOL .
411 '</svg>',
412 ),
413 ) + $config['sections']['style_manager_section']['options'];
414
415 return $config;
416 }
417
418 /**
419 * Maybe activate an external theme config.
420 *
421 * If the conditions are met, activate an external theme config by declaring support for the style manager and saving the config.
422 *
423 * @since 1.7.5
424 *
425 * @param array $config This holds required keys for the plugin config like 'opt-name', 'panels', 'settings'
426 * @return array
427 */
428 public function maybe_activate_external_theme_config( $config ) {
429 // If somebody else already declared support for the Style Manager, we stop and let them have it.
430 if ( $this->is_supported() ) {
431 return $config;
432 }
433
434 $external_theme_config = false;
435
436 // First gather details about the current (parent) theme.
437 $theme = wp_get_theme( get_template() );
438 // Bail if for some strange reason we couldn't find the theme.
439 if ( ! $theme->exists() ) {
440 return $config;
441 }
442
443 // Now determine if we have a theme config for the current theme.
444 $design_assets = $this->get_design_assets();
445 if ( empty( $design_assets['theme_configs'] ) || ! is_array( $design_assets['theme_configs'] ) ) {
446 return $config;
447 }
448
449 $theme_configs = $design_assets['theme_configs'];
450
451 // We will go through every theme config and determine it's match score
452 foreach ( $theme_configs as $hashid => $theme_config ) {
453 // Loose matching means that the theme doesn't have to match all the conditions.
454 $loose_match = false;
455 if ( ! empty( $theme_config['loose_match'] ) ) {
456 $loose_match = true;
457 }
458
459 $matches = 0;
460 $total = 0;
461 if ( ! empty( $theme_config['name'] ) && $theme_config['name'] == $theme->get('Name') ) {
462 $matches++;
463 $total++;
464 }
465 if ( ! empty( $theme_config['slug'] ) && $theme_config['slug'] == $theme->get_stylesheet() ) {
466 $matches++;
467 $total++;
468 }
469 if ( ! empty( $theme_config['txtd'] ) && $theme_config['txtd'] == $theme->get('TextDomain') ) {
470 $matches++;
471 $total++;
472 }
473
474 $theme_configs[ $hashid ]['match_score'] = 0;
475 if ( true === $loose_match ) {
476 $theme_configs[ $hashid ]['match_score'] = $matches;
477 } elseif( $matches === $total ) {
478 $theme_configs[ $hashid ]['match_score'] = $matches;
479 }
480 }
481
482 // Now we will order the theme configs by match scores, descending and get the highest matching candidate, if any.
483 $theme_configs = Customify_Array::array_orderby( $theme_configs, 'match_score', SORT_DESC );
484 $external_theme_config = array_shift( $theme_configs );
485 // If we've ended up with a theme config with a zero match score, bail.
486 if ( empty( $external_theme_config['match_score'] ) || empty( $external_theme_config['config']['sections'] ) ) {
487 return $config;
488 }
489
490 // Now we have a theme config to work with. Save it for later use.
491 $this->external_theme_config = $external_theme_config;
492
493 // Declare support for the Style Manager if there is such a section in the config
494 if ( isset( $external_theme_config['config']['sections']['style_manager_section'] ) ) {
495 add_theme_support( 'customizer_style_manager' );
496 }
497
498 return $config;
499 }
500
501 /**
502 * Maybe apply an external theme config.
503 *
504 * If the conditions are met, apply an external theme config. Right now we are only handling sections and their controls.
505 *
506 * @since 1.7.5
507 *
508 * @param array $config This holds required keys for the plugin config like 'opt-name', 'panels', 'settings'
509 * @return array
510 */
511 public function maybe_apply_external_theme_config( $config ) {
512 // Bail if we have no external theme config data.
513 if ( empty( $this->external_theme_config ) ) {
514 return $config;
515 }
516
517 // Apply the theme config.
518 // If we are dealing with the Customify default config, we need a clean slate, sort of.
519 if ( 'customify_defaults' === $config['opt-name'] ) {
520 // We will save the Style Manager config so we can merge with it. But the rest goes away.
521 $style_manager_section = array();
522 if ( isset( $config['sections']['style_manager_section'] ) ) {
523 $style_manager_section = $config['sections']['style_manager_section'];
524 }
525
526 $config['opt-name'] = get_template() . '_options';
527 if ( ! empty( $this->external_theme_config['config']['opt-name'] ) ) {
528 $config['opt-name'] = $this->external_theme_config['config']['opt-name'];
529 }
530
531 $config['sections'] = array(
532 'style_manager_section' => $style_manager_section,
533 );
534 }
535
536 // Now merge things.
537 $config['sections'] = Customify_Array::array_merge_recursive_distinct( $config['sections'],$this->external_theme_config['config']['sections'] );
538
539 return $config;
540 }
541
542 /**
543 * Maybe process certain "commands" from the external theme config.
544 *
545 * Mainly things like removing sections, controls, etc.
546 *
547 * @since 1.7.5
548 *
549 * @param WP_Customize_Manager $wp_customize
550 */
551 public function maybe_process_external_theme_config_extras( $wp_customize ) {
552 // Bail if we have no external theme config data.
553 if ( empty( $this->external_theme_config ) ) {
554 return;
555 }
556
557 // Maybe remove panels
558 if ( ! empty( $this->external_theme_config['config']['remove_panels'] ) ) {
559 // Standardize it.
560 if ( is_string( $this->external_theme_config['config']['remove_panels'] ) ) {
561 $this->external_theme_config['config']['remove_panels'] = array( $this->external_theme_config['config']['remove_panels'] );
562 }
563
564 foreach ( $this->external_theme_config['config']['remove_panels'] as $panel_id ) {
565 $wp_customize->remove_panel( $panel_id );
566 }
567 }
568
569 // Maybe remove sections
570 if ( ! empty( $this->external_theme_config['config']['remove_sections'] ) ) {
571 // Standardize it.
572 if ( is_string( $this->external_theme_config['config']['remove_sections'] ) ) {
573 $this->external_theme_config['config']['remove_sections'] = array( $this->external_theme_config['config']['remove_sections'] );
574 }
575
576 foreach ( $this->external_theme_config['config']['remove_sections'] as $section_id ) {
577
578 if ( 'widgets' === $section_id ) {
579 global $wp_registered_sidebars;
580
581 foreach ( $wp_registered_sidebars as $widget => $settings ) {
582 $wp_customize->remove_section( 'sidebar-widgets-' . $widget );
583 }
584 continue;
585 }
586
587 $wp_customize->remove_section( $section_id );
588 }
589 }
590
591 // Maybe remove settings
592 if ( ! empty( $this->external_theme_config['config']['remove_settings'] ) ) {
593 // Standardize it.
594 if ( is_string( $this->external_theme_config['config']['remove_settings'] ) ) {
595 $this->external_theme_config['config']['remove_settings'] = array( $this->external_theme_config['config']['remove_settings'] );
596 }
597
598 foreach ( $this->external_theme_config['config']['remove_settings'] as $setting_id ) {
599 $wp_customize->remove_setting( $setting_id );
600 }
601 }
602
603 // Maybe remove controls
604 if ( ! empty( $this->external_theme_config['config']['remove_controls'] ) ) {
605 // Standardize it.
606 if ( is_string( $this->external_theme_config['config']['remove_controls'] ) ) {
607 $this->external_theme_config['config']['remove_controls'] = array( $this->external_theme_config['config']['remove_controls'] );
608 }
609
610 foreach ( $this->external_theme_config['config']['remove_controls'] as $control_id ) {
611 $wp_customize->remove_control( $control_id );
612 }
613 }
614 }
615
616 /**
617 * Get the color palettes configuration.
618 *
619 * @since 1.7.0
620 *
621 * @param bool $skip_cache Optional. Whether to use the cached config or fetch a new one.
622 * @return array
623 */
624 protected function get_color_palettes( $skip_cache = false ) {
625 // Get the design assets data.
626 $design_assets = $this->get_design_assets( $skip_cache );
627 if ( false === $design_assets || empty( $design_assets['color_palettes'] ) ) {
628 $color_palettes_config = $this->get_default_color_palettes_config();
629 } else {
630 $color_palettes_config = $design_assets['color_palettes'];
631 }
632
633 return apply_filters( 'customify_get_color_palettes', $color_palettes_config );
634 }
635
636 /**
637 * Get the themes configuration.
638 *
639 * @since 1.7.5
640 *
641 * @param bool $skip_cache Optional. Whether to use the cached config or fetch a new one.
642 * @return array
643 */
644 protected function get_theme_configs( $skip_cache = false ) {
645 // Get the design assets data.
646 $design_assets = $this->get_design_assets( $skip_cache );
647 if ( false === $design_assets || empty( $design_assets['theme_configs'] ) ) {
648 $theme_configs = $this->get_default_color_palettes_config();
649 } else {
650 $theme_configs = $design_assets['theme_configs'];
651 }
652
653 return apply_filters( 'customify_get_theme_configs', $theme_configs );
654 }
655
656 /**
657 * Get the design assets configuration.
658 *
659 * @since 1.7.0
660 *
661 * @param bool $skip_cache Optional. Whether to use the cached config or fetch a new one.
662 * @return array
663 */
664 protected function get_design_assets( $skip_cache = false ) {
665 if ( ! is_null( $this->design_assets ) ) {
666 return $this->design_assets;
667 }
668
669 $this->design_assets = apply_filters( 'customify_style_manager_maybe_fetch_design_assets', $this->maybe_fetch_design_assets( $skip_cache ) );
670
671 return $this->design_assets;
672 }
673
674 /**
675 * Fetch the design assets data from the Pixelgrade Cloud.
676 *
677 * Caches the data for 12 hours. Use local defaults if not available.
678 *
679 * @since 1.7.0
680 *
681 * @param bool $skip_cache Optional. Whether to use the cached data or fetch a new one.
682 * @return array|false
683 */
684 protected function maybe_fetch_design_assets( $skip_cache = false ) {
685 // First try and get the cached data
686 $data = get_option( $this->_get_design_assets_cache_key() );
687
688 // For performance reasons, we will ONLY fetch remotely when in the WP ADMIN area or via an ADMIN AJAX call, regardless of settings.
689 if ( ! is_admin() ) {
690 return $data;
691 }
692
693 // Get the cache data expiration timestamp.
694 $expire_timestamp = get_option( $this->_get_design_assets_cache_key() . '_timestamp' );
695
696 // We don't force skip the cache for AJAX requests for performance reasons.
697 if ( ! wp_doing_ajax() && defined('CUSTOMIFY_SM_ALWAYS_FETCH_DESIGN_ASSETS' ) && true === CUSTOMIFY_SM_ALWAYS_FETCH_DESIGN_ASSETS ) {
698 $skip_cache = true;
699 }
700
701 // The data isn't set, is expired or we were instructed to skip the cache; we need to fetch fresh data.
702 if ( true === $skip_cache || false === $data || false === $expire_timestamp || $expire_timestamp < time() ) {
703 $request_data = apply_filters( 'customify_pixelgrade_cloud_request_data', array(
704 'site_url' => home_url('/'),
705 // We are only interested in data needed to identify the theme and eventually deliver only design assets suitable for it.
706 'theme_data' => $this->get_active_theme_data(),
707 // We are only interested in data needed to identify the plugin version and eventually deliver design assets suitable for it.
708 'site_data' => $this->get_site_data(),
709 ), $this );
710
711 $request_args = array(
712 'method' => self::$externalApiEndpoints['cloud']['getDesignAssets']['method'],
713 'timeout' => 4,
714 'blocking' => true,
715 'body' => $request_data,
716 'sslverify' => false,
717 );
718 // Get the design assets from the cloud.
719 $response = wp_remote_request( self::$externalApiEndpoints['cloud']['getDesignAssets']['url'], $request_args );
720 // Bail in case of decode error or failure to retrieve data.
721 // We will return the data already available.
722 if ( is_wp_error( $response ) ) {
723 return $data;
724 }
725 $response_data = json_decode( wp_remote_retrieve_body( $response ), true );
726 // Bail in case of decode error or failure to retrieve data.
727 // We will return the data already available.
728 if ( null === $response_data || empty( $response_data['data'] ) || empty( $response_data['code'] ) || 'success' !== $response_data['code'] ) {
729 return $data;
730 }
731
732 $data = apply_filters( 'customify_style_manager_fetch_design_assets', $response_data['data'] );
733
734 // Cache the data in an option for 6 hours
735 update_option( $this->_get_design_assets_cache_key() , $data, true );
736 update_option( $this->_get_design_assets_cache_key() . '_timestamp' , time() + 6 * HOUR_IN_SECONDS, true );
737 }
738
739 return $data;
740 }
741
742 /**
743 * Get the design assets cache key.
744 *
745 * @since 1.7.0
746 *
747 * @return string
748 */
749 protected function _get_design_assets_cache_key() {
750 return 'customify_style_manager_design_assets';
751 }
752
753 /**
754 * Get the default (hard-coded) color palettes configuration.
755 *
756 * @since 1.7.0
757 *
758 * @return array
759 */
760 protected function get_default_color_palettes_config() {
761 $default_color_palettes = array(
762 'vasco' => array(
763 'label' => esc_html__( 'Restful Beach', 'customify' ),
764 'preview' => array(
765 'background_image_url' => 'http://pxgcdn.com/images/style-manager/color-palettes/vasco-theme-palette.jpg',
766 ),
767 'options' => array(
768 'sm_color_primary' => '#38C3C8',
769 'sm_color_secondary' => '#F59828',
770 'sm_color_tertiary' => '#FB551C',
771 'sm_dark_primary' => '#2b2b28',
772 'sm_dark_secondary' => '#2B3D39',
773 'sm_dark_tertiary' => '#65726F',
774 'sm_light_primary' => '#F5F6F1',
775 'sm_light_secondary' => '#E6F7F7',
776 'sm_light_tertiary' => '#FAEDE8',
777 ),
778 ),
779 'felt' => array(
780 'label' => esc_html__( 'Warm Summer', 'customify' ),
781 'preview' => array(
782 'background_image_url' => 'http://pxgcdn.com/images/style-manager/color-palettes/felt-theme-palette.jpg',
783 ),
784 'options' => array(
785 'sm_color_primary' => '#ff6000',
786 'sm_color_secondary' => '#FF9200',
787 'sm_color_tertiary' => '#FF7019',
788 'sm_dark_primary' => '#1C1C1C',
789 'sm_dark_secondary' => '#161616',
790 'sm_dark_tertiary' => '#161616',
791 'sm_light_primary' => '#FFFCFC',
792 'sm_light_secondary' => '#FFF4E8',
793 'sm_light_tertiary' => '#F7F3F0',
794 ),
795 ),
796 'julia' => array(
797 'label' => esc_html__( 'Serenity', 'customify' ),
798 'preview' => array(
799 'background_image_url' => 'http://pxgcdn.com/images/style-manager/color-palettes/julia-theme-palette.jpg',
800 ),
801 'options' => array(
802 'sm_color_primary' => '#3349B8',
803 'sm_color_secondary' => '#3393B8',
804 'sm_color_tertiary' => '#C18866',
805 'sm_dark_primary' => '#161616',
806 'sm_dark_secondary' => '#383C50',
807 'sm_dark_tertiary' => '#383C50',
808 'sm_light_primary' => '#f7f6f5',
809 'sm_light_secondary' => '#E7F2F8',
810 'sm_light_tertiary' => '#F7ECE6',
811 ),
812 ),
813 'gema' => array(
814 'label' => esc_html__( 'Burning Red', 'customify' ),
815 'preview' => array(
816 'background_image_url' => 'http://pxgcdn.com/images/style-manager/color-palettes/gema-theme-palette.jpg',
817 ),
818 'options' => array(
819 'sm_color_primary' => '#E03A3A',
820 'sm_color_secondary' => '#F75034',
821 'sm_color_tertiary' => '#AD2D2D',
822 'sm_dark_primary' => '#000000',
823 'sm_dark_secondary' => '#000000',
824 'sm_dark_tertiary' => '#A3A3A1',
825 'sm_light_primary' => '#FFFFFF',
826 'sm_light_secondary' => '#F7F5F5',
827 'sm_light_tertiary' => '#F7F2F2',
828 ),
829 ),
830 'patch' => array(
831 'label' => esc_html__( 'Fresh Lemon', 'customify' ),
832 'preview' => array(
833 'background_image_url' => 'http://pxgcdn.com/images/style-manager/color-palettes/patch-theme-palette.jpg',
834 ),
835 'options' => array(
836 'sm_color_primary' => '#ffeb00',
837 'sm_color_secondary' => '#19CDFF',
838 'sm_color_tertiary' => '#0BE8DD',
839 'sm_dark_primary' => '#171617',
840 'sm_dark_secondary' => '#3d3e40',
841 'sm_dark_tertiary' => '#b5b5b5',
842 'sm_light_primary' => '#FFFFFF',
843 'sm_light_secondary' => '#E8FAFF',
844 'sm_light_tertiary' => '#F2FFFE',
845 ),
846 ),
847 'silk' => array(
848 'label' => esc_html__( 'Floral Bloom', 'customify' ),
849 'preview' => array(
850 'background_image_url' => 'http://pxgcdn.com/images/style-manager/color-palettes/silk-theme-palette.jpg',
851 ),
852 'options' => array(
853 'sm_color_primary' => '#A33B61',
854 'sm_color_secondary' => '#FCC9B0',
855 'sm_color_tertiary' => '#C9648A',
856 'sm_dark_primary' => '#000000',
857 'sm_dark_secondary' => '#000000',
858 'sm_dark_tertiary' => '#A3A3A1',
859 'sm_light_primary' => '#FFFFFF',
860 'sm_light_secondary' => '#F7F5F6',
861 'sm_light_tertiary' => '#F7F0F3',
862 ),
863 ),
864 'hive' => array(
865 'label' => esc_html__( 'Powerful', 'customify' ),
866 'preview' => array(
867 'background_image_url' => 'http://pxgcdn.com/images/style-manager/color-palettes/hive-theme-palette.jpg',
868 ),
869 'options' => array(
870 'sm_color_primary' => '#ffeb00',
871 'sm_color_secondary' => '#3200B2',
872 'sm_color_tertiary' => '#740AC9',
873 'sm_dark_primary' => '#171617',
874 'sm_dark_secondary' => '#171617',
875 'sm_dark_tertiary' => '#363636',
876 'sm_light_primary' => '#FFFFFF',
877 'sm_light_secondary' => '#F2F5F7',
878 'sm_light_tertiary' => '#F5F2F7',
879 ),
880 ),
881 'lilac' => array(
882 'label' => esc_html__( 'Soft Lilac', 'customify' ),
883 'preview' => array(
884 'background_image_url' => 'http://pxgcdn.com/images/style-manager/color-palettes/lilac-palette.jpg',
885 ),
886 'options' => array(
887 'sm_color_primary' => '#DD8CA9',
888 'sm_color_secondary' => '#8C9CDE',
889 'sm_color_tertiary' => '#E3B4A6',
890 'sm_dark_primary' => '#1A1A1A',
891 'sm_dark_secondary' => '#303030',
892 'sm_dark_tertiary' => '#A3A3A1',
893 'sm_light_primary' => '#F0F2F1',
894 'sm_light_secondary' => '#CED5F2',
895 'sm_light_tertiary' => '#F7E1DA',
896 ),
897 ),
898 );
899
900 return apply_filters( 'customify_style_manager_default_color_palettes', $default_color_palettes );
901 }
902
903 /**
904 * Include the customify "external" config file in the theme root and overwrite the existing theme configs.
905 *
906 * @param array $design_assets
907 *
908 * @return array
909 */
910 public function maybe_load_external_config_from_theme_root( $design_assets ) {
911 $file_name = 'customify_theme_root.php';
912
913 // First gather details about the current (parent) theme.
914 $theme = wp_get_theme( get_template() );
915 // Bail if for some strange reason we couldn't find the theme.
916 if ( ! $theme->exists() ) {
917 return $design_assets;
918 }
919
920 $file = trailingslashit( $theme->get_template_directory() ) . $file_name;
921 if ( ! file_exists( $file ) ) {
922 return $design_assets;
923 }
924
925 // We expect to get from the file include a $config variable with the entire Customify (partial) config.
926 include $file;
927
928 if ( ! isset( $config ) || ! is_array( $config ) || empty( $config['sections'] ) ) {
929 // Alert the developers that things are not alright.
930 _doing_it_wrong( __METHOD__, 'The Customify theme root config is not good! Please check it! We will not apply it.', null );
931
932 return $design_assets;
933 }
934
935 // Construct the pseudo-external theme config.
936 // Start with a clean slate.
937 $design_assets['theme_configs'] = array();
938
939 $design_assets['theme_configs']['theme_root'] = array(
940 'id' => 1,
941 'name' => $theme->get( 'Name' ),
942 'slug' => $theme->get_stylesheet(),
943 'txtd' => $theme->get( 'TextDomain' ),
944 'loose_match' => true,
945 'config' => $config,
946 'created' => '2018-05-16 15:13:58',
947 'last_modified' => '2018-05-16 15:13:58',
948 'hashid' => 'theme_root',
949 );
950
951 return $design_assets;
952 }
953
954 /**
955 * Output the theme root JSON in the Customizer page source.
956 */
957 public function maybe_output_json_external_config_from_theme_root() {
958 if ( ! empty( $this->external_theme_config['config'] ) ) {
959 // Also output the JSON in a special hidden div for easy copy pasting.
960 // Also remove any multiple tabs.
961 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;
962 }
963 }
964
965 /**
966 * Get the active theme data.
967 *
968 * @since 1.7.0
969 *
970 * @return array
971 */
972 public function get_active_theme_data() {
973 $theme_data = array();
974
975 $slug = basename( get_template_directory() );
976
977 $theme_data['slug'] = $slug;
978
979 // Get the current theme style.css data.
980 $current_theme = wp_get_theme( get_template() );
981 if ( ! empty( $current_theme ) && ! is_wp_error( $current_theme ) ) {
982 $theme_data['name'] = $current_theme->get('Name');
983 $theme_data['themeuri'] = $current_theme->get('ThemeURI');
984 $theme_data['version'] = $current_theme->get('Version');
985 $theme_data['textdomain'] = $current_theme->get('TextDomain');
986 }
987
988 // Maybe get the WUpdates theme info if it's a theme delivered from WUpdates.
989 $wupdates_ids = apply_filters( 'wupdates_gather_ids', array() );
990 if ( ! empty( $wupdates_ids[ $slug ] ) ) {
991 $theme_data['wupdates'] = $wupdates_ids[ $slug ];
992 }
993
994 return apply_filters( 'customify_style_manager_get_theme_data', $theme_data );
995 }
996
997 /**
998 * Get the site data.
999 *
1000 * @since 1.7.0
1001 *
1002 * @return array
1003 */
1004 public function get_site_data() {
1005 $site_data = array(
1006 'url' => home_url('/'),
1007 'is_ssl' => is_ssl(),
1008 );
1009
1010 $site_data['wp'] = array(
1011 'version' => get_bloginfo('version'),
1012 );
1013
1014 $site_data['customify'] = array(
1015 'version' => PixCustomifyPlugin()->get_version(),
1016 );
1017
1018 $site_data['color_palettes'] = array(
1019 'current' => $this->get_current_color_palette(),
1020 'variation' => $this->get_current_color_palette_variation(),
1021 'custom' => $this->is_using_custom_color_palette(),
1022 );
1023
1024 // If there isn't a color palette selected, there is not point in having variation or custom.
1025 if ( empty( $site_data['color_palettes']['current'] ) ) {
1026 $site_data['color_palettes']['variation'] = false;
1027 $site_data['color_palettes']['custom'] = false;
1028 }
1029
1030 return apply_filters( 'customify_style_manager_get_site_data', $site_data );
1031 }
1032
1033 /**
1034 * Get the current color palette ID or false if none is selected.
1035 *
1036 * @since 1.7.0
1037 *
1038 * @return string|false
1039 */
1040 protected function get_current_color_palette() {
1041 return get_option( 'sm_color_palette', false );
1042 }
1043
1044 /**
1045 * Get the current color palette variation ID or false if none is selected.
1046 *
1047 * @since 1.7.0
1048 *
1049 * @return string|false
1050 */
1051 protected function get_current_color_palette_variation() {
1052 return get_option( 'sm_color_palette_variation', 'light' );
1053 }
1054
1055 /**
1056 * Determine if the selected color palette has been customized and remember this in an option.
1057 *
1058 * @since 1.7.0
1059 *
1060 * @return bool
1061 */
1062 public function update_custom_palette_in_use() {
1063 // If there is no style manager support, bail early.
1064 if ( ! $this->is_supported() ) {
1065 return false;
1066 }
1067
1068 $current_palette = $this->get_current_color_palette();
1069 if ( empty( $current_palette ) ) {
1070 return false;
1071 }
1072
1073 $color_palettes = $this->get_color_palettes();
1074 if ( ! isset( $color_palettes[ $current_palette ] ) || empty( $color_palettes[ $current_palette ]['options'] ) ) {
1075 return false;
1076 }
1077
1078 $is_custom_palette = false;
1079 // If any of the current master colors has a different value than the one provided by the color palette,
1080 // it means a custom color palette is in use.
1081 $current_palette_options = $color_palettes[ $current_palette ]['options'];
1082 foreach ( $current_palette_options as $setting_id => $value ) {
1083 if ( $value != get_option( $setting_id ) ) {
1084 $is_custom_palette = true;
1085 break;
1086 }
1087 }
1088
1089 update_option( 'sm_is_custom_color_palette', $is_custom_palette, true );
1090
1091 do_action( 'customify_style_manager_updated_custom_palette_in_use', $is_custom_palette, $this );
1092
1093 return true;
1094 }
1095
1096 /**
1097 * Determine if a custom color palette is in use.
1098 *
1099 * @since 1.7.0
1100 *
1101 * @return bool
1102 */
1103 protected function is_using_custom_color_palette(){
1104 return (bool) get_option( 'sm_is_custom_color_palette', false );
1105 }
1106
1107 /**
1108 * Get all the defined Style Manager master color field ids.
1109 *
1110 * @since 1.7.0
1111 *
1112 * @param array $options
1113 * @return array
1114 */
1115 public function get_all_master_color_controls_ids( $options ) {
1116 $master_color_controls = array();
1117
1118 foreach ( $options as $option_id => $option_settings ) {
1119 if ( 'color' === $option_settings['type'] ) {
1120 $master_color_controls[] = $option_id;
1121 }
1122 }
1123
1124 return $master_color_controls;
1125 }
1126
1127 /**
1128 * Output the user feedback modal markup, if we need to.
1129 *
1130 * @since 1.7.0
1131 */
1132 public function output_user_feedback_modal() {
1133 // If there is no style manager support, bail early.
1134 if ( ! $this->is_supported() ) {
1135 return;
1136 }
1137
1138 // Only output if the user didn't provide feedback.
1139 if ( ! $this->user_provided_feedback() ) { ?>
1140 <div id="style-manager-user-feedback-modal">
1141 <div class="modal">
1142 <div class="modal-dialog" role="document">
1143 <div class="modal-content">
1144 <form id="style-manager-user-feedback" action="#" method="post">
1145 <input type="hidden" name="type" value="1_to_5" />
1146 <div class="modal-header">
1147 <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>
1148 <!-- <a href="#" class="close button button--naked gray" data-dismiss="modal" aria-label="Close">Close</a> -->
1149 </div>
1150 <div class="modal-body full">
1151 <div class="box box--large">
1152 <div class="first-step">
1153 <h2 class="modal-title">How would you rate your experience with using Color Palettes?</h2>
1154 <div class="scorecard">
1155 <span>Worst</span>
1156 <label>
1157 <input type="radio" name="rating" value="1" required />
1158 <span>1</span>
1159 </label>
1160 <label>
1161 <input type="radio" name="rating" value="2" required />
1162 <span>2</span>
1163 </label>
1164 <label>
1165 <input type="radio" name="rating" value="3" required />
1166 <span>3</span>
1167 </label>
1168 <label>
1169 <input type="radio" name="rating" value="4" required />
1170 <span>4</span>
1171 </label>
1172 <label>
1173 <input type="radio" name="rating" value="5" required />
1174 <span>5</span>
1175 </label>
1176 <span>Best</span>
1177 </div>
1178 </div>
1179 <div class="second-step hidden">
1180 <p><strong>What makes you give <span class="rating-placeholder">5</span>*?</strong> I hope you’ll answer and help us do better:</p>
1181 <div class="not-floating-labels">
1182 <div class="form-row field">
1183 <textarea name="message" placeholder="Your message.."
1184 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>
1185 </div>
1186 </div>
1187 <button id="style-manager-user-feedback_btn" class="button" type="submit"><?php _e( 'Submit my feedback', 'customify' ); ?></button>
1188 </div>
1189 <div class="thanks-step hidden">
1190 <h3 class="modal-title">Thanks for your feedback!</h3>
1191 <p>This will help us improve the product. Stay awesome! 🤗</p>
1192 </div>
1193 <div class="error-step hidden">
1194 <h3 class="modal-title">We've hit a snag!</h3>
1195 <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>
1196 </div>
1197 </div>
1198 </div>
1199 <div class="modal-footer full">
1200
1201 </div>
1202 </form>
1203 </div>
1204 </div>
1205 </div>
1206 <!-- End Modal -->
1207 <!-- Modal Backdrop (Shadow) -->
1208 <div class="modal-backdrop"></div>
1209 </div>
1210
1211 <?php }
1212 }
1213
1214 /**
1215 * @param bool|int $timestamp_limit Optional. Timestamp to compare the time the user provided feedback.
1216 * If the provided timestamp is earlier than the time the user provided feedback, returns false.
1217 *
1218 * @return bool
1219 */
1220 public function user_provided_feedback( $timestamp_limit = false ) {
1221 if ( defined( 'CUSTOMIFY_SM_ALWAYS_ASK_FOR_FEEDBACK' ) && true === CUSTOMIFY_SM_ALWAYS_ASK_FOR_FEEDBACK ) {
1222 return false;
1223 }
1224
1225 $user_provided_feedback = get_option( 'style_manager_user_feedback_provided' );
1226 if ( empty( $user_provided_feedback ) ) {
1227 return false;
1228 }
1229
1230 if ( ! empty( $timestamp ) && is_int( $timestamp ) && $timestamp_limit > $user_provided_feedback ) {
1231 return false;
1232 }
1233
1234 return true;
1235 }
1236
1237 /**
1238 * Callback for the user feedback AJAX call.
1239 *
1240 * @since 1.7.0
1241 */
1242 public function user_feedback_callback() {
1243 check_ajax_referer( 'customify_style_manager_user_feedback', 'nonce' );
1244
1245 if ( empty( $_POST['type'] ) ) {
1246 wp_send_json_error( esc_html__( 'No type provided', 'customify' ) );
1247 }
1248
1249 if ( empty( $_POST['rating'] ) ) {
1250 wp_send_json_error( esc_html__( 'No rating provided', 'customify' ) );
1251 }
1252
1253 $type = sanitize_text_field( $_POST['type'] );
1254 $rating = intval( $_POST['rating'] );
1255 $message = '';
1256 if ( ! empty( $_POST['message'] ) ) {
1257 $message = wp_kses_post( $_POST['message'] );
1258 }
1259
1260 $request_data = apply_filters( 'customify_pixelgrade_cloud_request_data', array(
1261 'site_url' => home_url( '/' ),
1262 'satisfaction_data' => array(
1263 'type' => $type,
1264 'rating' => $rating,
1265 'message' => $message,
1266 ),
1267 ), $this );
1268
1269 $request_args = array(
1270 'method' => self::$externalApiEndpoints['cloud']['stats']['method'],
1271 'timeout' => 5,
1272 'blocking' => true,
1273 'body' => $request_data,
1274 'sslverify' => false,
1275 );
1276
1277 // Send the feedback.
1278 $response = wp_remote_request( self::$externalApiEndpoints['cloud']['stats']['url'], $request_args );
1279 if ( is_wp_error( $response ) ) {
1280 wp_send_json_error( esc_html__( 'Sorry, something went wrong and we couldn\'t save your feedback.', 'customify' ) );
1281 }
1282 $response_data = json_decode( wp_remote_retrieve_body( $response ), true );
1283 // Bail in case of decode error or failure to retrieve data
1284 if ( null === $response_data || empty( $response_data['code'] ) || 'success' !== $response_data['code'] ) {
1285 wp_send_json_error( esc_html__( 'Sorry, something went wrong and we couldn\'t save your feedback.', 'customify' ) );
1286 }
1287
1288 // We need to remember that the user provided feedback (and at what timestamp).
1289 update_option( 'style_manager_user_feedback_provided', time(), true );
1290
1291 wp_send_json_success( esc_html__( 'Thank you for your feedback.', 'customify' ) );
1292 }
1293
1294 /**
1295 * Main Customify_Style_Manager Instance
1296 *
1297 * Ensures only one instance of Customify_Style_Manager is loaded or can be loaded.
1298 *
1299 * @since 1.7.0
1300 * @static
1301 * @param object $parent Main PixCustomifyPlugin instance.
1302 *
1303 * @return Customify_Style_Manager Main Customify_Style_Manager instance
1304 */
1305 public static function instance( $parent = null ) {
1306
1307 if ( is_null( self::$_instance ) ) {
1308 self::$_instance = new self( $parent );
1309 }
1310 return self::$_instance;
1311 } // End instance ()
1312
1313 /**
1314 * Cloning is forbidden.
1315 *
1316 * @since 1.7.0
1317 */
1318 public function __clone() {
1319
1320 _doing_it_wrong( __FUNCTION__,esc_html( __( 'Cheatin&#8217; huh?' ) ), null );
1321 } // End __clone ()
1322
1323 /**
1324 * Unserializing instances of this class is forbidden.
1325 *
1326 * @since 1.7.0
1327 */
1328 public function __wakeup() {
1329
1330 _doing_it_wrong( __FUNCTION__, esc_html( __( 'Cheatin&#8217; huh?' ) ), null );
1331 } // End __wakeup ()
1332 }
1333