PluginProbe
Customify / 2.5.2
Customify v2.5.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 2.5.2, at includes/class-customify-style-manager.php

889 lines 30.6 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * This is the class that handles the overall logic for the Style Manager.
4 *
5 * @see https://pixelgrade.com
6 * @author Pixelgrade
7 * @since 1.7.0
8 */
9
10 if ( ! defined( 'ABSPATH' ) ) {
11 exit; // Exit if accessed directly
12 }
13
14 if ( ! class_exists( 'Customify_Style_Manager' ) ) {
15
16 class Customify_Style_Manager {
17
18 /**
19 * Holds the only instance of this class.
20 * @var null|Customify_Style_Manager
21 * @access protected
22 * @since 1.7.0
23 */
24 protected static $_instance = null;
25
26 /**
27 * The main plugin object (the parent).
28 * @var null|PixCustomifyPlugin
29 * @access public
30 * @since 1.7.0
31 */
32 public $parent = null;
33
34 /**
35 * The external theme configs object.
36 * @var null|Customify_Theme_Configs
37 * @access public
38 * @since 1.7.4
39 */
40 protected $theme_configs = null;
41
42 /**
43 * The color palettes object.
44 * @var null|Customify_Color_Palettes
45 * @access public
46 * @since 1.7.4
47 */
48 protected $color_palettes = null;
49
50 /**
51 * The font palettes object.
52 * @var null|Customify_Font_Palettes
53 * @access public
54 * @since 1.7.4
55 */
56 protected $font_palettes = null;
57
58 /**
59 * The Cloud API object.
60 * @var null|Customify_Cloud_Api
61 * @access public
62 * @since 1.7.4
63 */
64 protected $cloud_api = null;
65
66 /**
67 * Cache for the wupdates identification data to avoid firing the filter multiple times.
68 * @var array
69 * @access protected
70 */
71 protected static $wupdates_ids = array();
72
73 /**
74 * Constructor.
75 *
76 * @since 1.7.0
77 */
78 protected function __construct() {
79 $this->init();
80 }
81
82 /**
83 * Initialize this module.
84 *
85 * @since 1.7.4
86 */
87 public function init() {
88 /**
89 * Initialize the Themes Config logic.
90 */
91 require_once 'class-customify-theme-configs.php';
92 $this->theme_configs = Customify_Theme_Configs::instance();
93
94 /**
95 * Initialize the Color Palettes logic.
96 */
97 require_once 'class-customify-color-palettes.php';
98 $this->color_palettes = Customify_Color_Palettes::instance();
99
100 /**
101 * Initialize the Font Palettes logic.
102 */
103 require_once 'class-customify-font-palettes.php';
104 $this->font_palettes = Customify_Font_Palettes::instance();
105
106 /**
107 * Initialize the Cloud API logic.
108 */
109 require_once 'lib/class-customify-cloud-api.php';
110 $this->cloud_api = new Customify_Cloud_Api();
111
112 // Hook up.
113 $this->add_hooks();
114 }
115
116 /**
117 * Initiate our hooks
118 *
119 * @since 1.7.0
120 */
121 public function add_hooks() {
122 /*
123 * Handle the Customizer Style Manager base config.
124 */
125 add_filter( 'customify_filter_fields', array( $this, 'style_manager_section_base_config' ), 12, 1 );
126
127 /*
128 * Handle the grouping and reorganization of the Customizer theme sections when the Style Manager is active.
129 */
130 add_filter( 'customify_final_config', array( $this, 'reorganize_customify_sections' ), 10, 1 );
131 add_action( 'customize_register', array( $this, 'general_reorganization_of_customize_sections' ), 999, 1 );
132
133 /*
134 * Handle the customization of controls based on theme type.
135 */
136 add_filter( 'customify_filter_fields', array( $this, 'pre_filter_based_on_theme_type' ), 20, 1 );
137 add_filter( 'customify_final_config', array( $this, 'filter_based_on_theme_type' ), 20, 1 );
138 add_action( 'customify_after_preset_control', array( $this, 'maybe_add_text_after_color_palettes' ), 10, 1 );
139 add_action( 'customify_after_sm_palette_filter_control', array( $this, 'maybe_add_text_after_color_palette_filters' ), 10, 1 );
140 add_action( 'customify_after_sm_radio_control', array( $this, 'maybe_add_text_after_color_palettes_customize_controls' ), 10, 1 );
141
142 /*
143 * Handle the logic for user feedback.
144 */
145 add_action( 'customize_controls_print_footer_scripts', array( $this, 'output_user_feedback_modal' ) );
146 add_action( 'wp_ajax_customify_style_manager_user_feedback', array( $this, 'user_feedback_callback' ) );
147
148 /*
149 * Scripts enqueued in the Customizer.
150 */
151 add_action( 'customize_controls_init', array( $this, 'register_admin_customizer_scripts' ), 10 );
152 add_action( 'customize_controls_enqueue_scripts', array( $this, 'enqueue_admin_customizer_scripts' ), 10 );
153 }
154
155 /**
156 * Register Customizer admin scripts.
157 */
158 function register_admin_customizer_scripts() {
159 wp_register_script( PixCustomifyPlugin()->get_slug() . '-style-manager', plugins_url( 'js/customizer/style-manager.js', PixCustomifyPlugin()->get_file() ), array( 'jquery' ), PixCustomifyPlugin()->get_version() );
160 }
161
162 /**
163 * Enqueue Customizer admin scripts
164 */
165 function enqueue_admin_customizer_scripts() {
166 // If there is no style manager support, bail early.
167 if ( ! $this->is_supported() ) {
168 return;
169 }
170
171 // Enqueue the needed scripts, already registered.
172 wp_enqueue_script( PixCustomifyPlugin()->get_slug() . '-style-manager' );
173 }
174
175 /**
176 * Determine if Style Manager is supported.
177 *
178 * @return bool
179 * @since 1.7.0
180 *
181 */
182 public function is_supported() {
183 $has_support = (bool) current_theme_supports( 'customizer_style_manager' );
184
185 return apply_filters( 'customify_style_manager_is_supported', $has_support );
186 }
187
188 /**
189 * Setup the Style Manager Customizer section base config.
190 *
191 * This handles the base configuration for the controls in the Style Manager section. We expect other parties (e.g. the theme),
192 * to come and fill up the missing details (e.g. connected fields).
193 *
194 * @param array $config This holds required keys for the plugin config like 'opt-name', 'panels', 'settings'
195 *
196 * @return array
197 * @since 1.7.0
198 *
199 */
200 public function style_manager_section_base_config( $config ) {
201 // If there is no style manager support, bail early.
202 if ( ! $this->is_supported() ) {
203 return $config;
204 }
205
206 if ( ! isset( $config['sections']['style_manager_section'] ) ) {
207 $config['sections']['style_manager_section'] = array();
208 }
209
210 // The section might be already defined, thus we merge, not replace the entire section config.
211 $config['sections']['style_manager_section'] = Customify_Array::array_merge_recursive_distinct( $config['sections']['style_manager_section'], array(
212 'title' => esc_html__( 'Style Manager', 'customify' ),
213 'section_id' => 'style_manager_section',
214 // We will force this section id preventing prefixing and other regular processing.
215 'priority' => 1,
216 'options' => array(),
217 ) );
218
219 return $config;
220 }
221
222 /**
223 * Reorganize the Customizer sections.
224 *
225 * @param array $config This holds required keys for the plugin config like 'opt-name', 'panels', 'settings'.
226 *
227 * @return array
228 * @since 1.7.4
229 *
230 */
231 public function reorganize_customify_sections( $config ) {
232 // If there is no style manager support, bail early.
233 if ( ! $this->is_supported() ) {
234 return $config;
235 }
236
237 // If there is no Style Manager section or panel, bail.
238 if ( ! isset( $config['sections']['style_manager_section'] ) &&
239 ! isset( $config['panels']['style_manager_panel'] ) ) {
240
241 return $config;
242 }
243
244 $style_manager_section_config = false;
245 if ( isset( $config['sections']['style_manager_section'] ) ) {
246 $style_manager_section_config = $config['sections']['style_manager_section'];
247 unset( $config['sections']['style_manager_section'] );
248 }
249
250 // All the other sections.
251 $other_theme_sections_config = $config['sections'];
252 unset( $config['sections'] );
253
254 // The Style Manager panel.
255 if ( empty( $config['panels']['style_manager_panel'] ) ) {
256 $style_manager_panel_config = array(
257 'priority' => 22, // after the Site Identity panel
258 'capability' => 'edit_theme_options',
259 'panel_id' => 'style_manager_panel',
260 'title' => esc_html__( 'Style Manager', 'customify' ),
261 'description' => wp_kses_post( __( '<strong>Style Manager</strong> is an intuitive system to help you change the look of your website and make an excellent impression.', 'customify' ) ),
262 'sections' => array(),
263 'auto_expand_sole_section' => true, // If there is only one section in the panel, auto-expand it.
264 );
265 } else {
266 $style_manager_panel_config = $config['panels']['style_manager_panel'];
267 unset( $config['panels']['style_manager_panel'] );
268 }
269
270 $other_panels_config = false;
271 if ( ! empty( $config['panels'] ) ) {
272 $other_panels_config = $config['panels'];
273 unset( $config['panels'] );
274 }
275
276 // Maybe handle the color palettes.
277 if ( is_array( $style_manager_section_config ) && class_exists( 'Customify_Color_Palettes' ) && Customify_Color_Palettes::instance()->is_supported() ) {
278
279 // We need to split the fields in the Style Manager section into two: color palettes and fonts.
280 $color_palettes_fields = array(
281 'sm_current_color_palette',
282 'sm_palettes_description',
283 'sm_filters_description',
284 'sm_customize_description',
285 'sm_color_matrix',
286 'sm_palette_filter',
287 'sm_coloration_level',
288 'sm_color_diversity',
289 'sm_shuffle_colors',
290 'sm_dark_mode',
291 'sm_dark_color_master_slider',
292 'sm_dark_color_primary_slider',
293 'sm_dark_color_secondary_slider',
294 'sm_dark_color_tertiary_slider',
295 'sm_colors_dispersion',
296 'sm_colors_focus_point',
297 'sm_color_palette',
298 'sm_color_palette_variation',
299 'sm_color_primary',
300 'sm_color_primary_final',
301 'sm_color_secondary',
302 'sm_color_secondary_final',
303 'sm_color_tertiary',
304 'sm_color_tertiary_final',
305 'sm_dark_primary',
306 'sm_dark_primary_final',
307 'sm_dark_secondary',
308 'sm_dark_secondary_final',
309 'sm_dark_tertiary',
310 'sm_dark_tertiary_final',
311 'sm_light_primary',
312 'sm_light_primary_final',
313 'sm_light_secondary',
314 'sm_light_secondary_final',
315 'sm_light_tertiary',
316 'sm_light_tertiary_final',
317 'sm_swap_colors',
318 'sm_swap_dark_light',
319 'sm_swap_colors_dark',
320 'sm_swap_secondary_colors_dark',
321 'sm_advanced_toggle',
322 'sm_spacing_bottom',
323 );
324
325 $color_palettes_section_config = array(
326 'title' => esc_html__( 'Colors', 'customify' ),
327 'section_id' => 'sm_color_palettes_section',
328 'priority' => 10,
329 'options' => array(),
330 );
331 foreach ( $color_palettes_fields as $field_id ) {
332 if ( ! isset( $style_manager_section_config['options'][ $field_id ] ) ) {
333 continue;
334 }
335
336 if ( empty( $color_palettes_section_config['options'] ) ) {
337 $color_palettes_section_config['options'] = array( $field_id => $style_manager_section_config['options'][ $field_id ] );
338 } else {
339 $color_palettes_section_config['options'] = array_merge( $color_palettes_section_config['options'], array( $field_id => $style_manager_section_config['options'][ $field_id ] ) );
340 }
341 }
342
343 $style_manager_panel_config['sections']['sm_color_palettes_section'] = $color_palettes_section_config;
344 }
345
346 // Maybe handle the font palettes.
347 if ( is_array( $style_manager_section_config ) && class_exists( 'Customify_Font_Palettes' ) && Customify_Font_Palettes::instance()->is_supported() ) {
348
349 $font_palettes_fields = array(
350 'sm_font_palette',
351 'sm_font_palette_variation',
352 'sm_font_primary',
353 'sm_font_secondary',
354 'sm_font_body',
355 'sm_swap_fonts',
356 'sm_swap_primary_secondary_fonts',
357 );
358
359 $font_palettes_section_config = array(
360 'title' => esc_html__( 'Fonts', 'customify' ),
361 'section_id' => 'sm_font_palettes_section',
362 'priority' => 20,
363 'options' => array(),
364 );
365 foreach ( $font_palettes_fields as $field_id ) {
366 if ( ! isset( $style_manager_section_config['options'][ $field_id ] ) ) {
367 continue;
368 }
369
370 if ( empty( $font_palettes_section_config['options'] ) ) {
371 $font_palettes_section_config['options'] = array( $field_id => $style_manager_section_config['options'][ $field_id ] );
372 } else {
373 $font_palettes_section_config['options'] = array_merge( $font_palettes_section_config['options'], array( $field_id => $style_manager_section_config['options'][ $field_id ] ) );
374 }
375 }
376
377 $style_manager_panel_config['sections']['sm_font_palettes_section'] = $font_palettes_section_config;
378 }
379
380 // Start fresh and add the Style Manager panel config
381 if ( empty( $config['panels'] ) ) {
382 $config['panels'] = array();
383 }
384 $config['panels']['style_manager_panel'] = $style_manager_panel_config;
385
386 // The Theme Options panel.
387 $theme_options_panel_config = array(
388 'priority' => 24, // after the Style Manager panel.
389 'capability' => 'edit_theme_options',
390 'panel_id' => 'theme_options_panel',
391 'title' => esc_html__( 'Theme Options', 'customify' ),
392 'description' => esc_html__( 'Advanced options to change your site look-and-feel on a detailed level.', 'customify' ),
393 'sections' => array(),
394 );
395
396 // If we have other panels we will make their sections parts of the Theme Options panel.
397 if ( ! empty( $other_panels_config ) ) {
398 // If we have another panel that is called Theme Options we will extract it's sections and put them directly in the Theme Options panel.
399 $second_theme_options_sections = array();
400 foreach ( $other_panels_config as $panel_id => $panel_config ) {
401 $found = false;
402 // First try the panel ID.
403 if ( false !== strpos( strtolower( str_replace( '-', '_', $panel_id ) ), 'theme_options' ) ) {
404 $found = true;
405 }
406
407 // Second, try the panel title.
408 if ( ! $found && ! empty( $panel_config['title'] ) && false !== strpos( strtolower( str_replace( array(
409 '-',
410 '_'
411 ), ' ', $panel_config['title'] ) ), ' theme options' ) ) {
412 $found = true;
413 }
414
415 if ( $found && ! empty( $panel_config['sections'] ) ) {
416 $second_theme_options_sections = array_merge( $second_theme_options_sections, $panel_config['sections'] );
417 unset( $other_panels_config[ $panel_id ] );
418 }
419 }
420 if ( ! empty( $second_theme_options_sections ) ) {
421 $theme_options_panel_config['sections'] = array_merge( $theme_options_panel_config['sections'], $second_theme_options_sections );
422 }
423
424 // For the remaining panels, we will put their section into the Theme Options panel, but prefix their title with their respective panel title.
425 $prefixed_sections = array();
426 foreach ( $other_panels_config as $panel_id => $panel_config ) {
427 if ( ! empty( $panel_config['sections'] ) ) {
428 foreach ( $panel_config['sections'] as $section_id => $section_config ) {
429 if ( ! empty( $section_config['title'] ) && ! empty( $panel_config['title'] ) ) {
430 $section_config['title'] = $panel_config['title'] . ' - ' . $section_config['title'];
431 }
432 $prefixed_sections[ $panel_id . '_' . $section_id ] = $section_config;
433 }
434 }
435 }
436 $theme_options_panel_config['sections'] = array_merge( $theme_options_panel_config['sections'], $prefixed_sections );
437 }
438
439 // If we have other sections we will add them to the Theme Options panel.
440 if ( ! empty( $other_theme_sections_config ) ) {
441 $theme_options_panel_config['sections'] = array_merge( $theme_options_panel_config['sections'], $other_theme_sections_config );
442 }
443
444 if ( empty( $config['panels']['theme_options_panel'] ) ) {
445 $config['panels']['theme_options_panel'] = $theme_options_panel_config;
446 } else {
447 $config['panels']['theme_options_panel'] = array_merge( $config['panels']['theme_options_panel'], $theme_options_panel_config );
448 }
449
450 // Add the logic that handles sections and controls added directly to WP_Customizer, not through the config.
451 add_action( 'customize_register', array( $this, 'reorganize_direct_sections_and_controls' ), 100 );
452
453 // Remove the switch theme panel from the Customizer.
454 add_action( 'customize_register', array( $this, 'remove_switch_theme_panel' ), 12 );
455
456 return $config;
457 }
458
459 /**
460 * Reorganize the Customizer sections and panels, without accounting for Customify's configured ones.
461 *
462 * @param WP_Customize_Manager $wp_customize WP_Customize_Manager instance.
463 */
464 public function general_reorganization_of_customize_sections( $wp_customize ) {
465 $sections = $wp_customize->sections();
466 if ( ! empty( $sections['pro__section'] ) ) {
467 $sections['pro__section']->priority = 24; // After the Style Manager panel.
468 }
469
470 // Add a pretty icon to Site Identity
471 $wp_customize->get_section( 'title_tagline' )->title = '&#x1f465; ' . esc_html__( 'Site Identity', 'customify' );
472 }
473
474 /**
475 * Filter the config during the build up.
476 *
477 * @param array $config
478 *
479 * @return array
480 */
481 public function pre_filter_based_on_theme_type( $config ) {
482 if ( in_array( self::get_theme_type(), array( 'theme_wporg', 'theme_modular_wporg' ) ) ) {
483
484 add_filter( 'customify_style_manager_color_palettes_colors_classes', function ( $classes ) {
485 $classes[] = 'js-no-picker';
486
487 return $classes;
488 } );
489 }
490
491 return $config;
492 }
493
494 public function maybe_add_text_after_color_palettes( $control ) {
495 if ( 'sm_color_palette' === $control->setting->id && in_array( self::get_theme_type(), array( 'theme_wporg', 'theme_modular_wporg' ) ) ) { ?>
496 <li id="customize-control-sm_palettes_description_after_control" class="pix_customizer_setting customize-control customize-control-html" style="display: list-item;">
497 <span class="description customize-control-description"><strong>Many more color palettes</strong> are available with the PRO version of your theme.</span>
498 </li>
499 <?php }
500 }
501
502 public function maybe_add_text_after_color_palette_filters( $control ) {
503 if ( in_array( self::get_theme_type(), array( 'theme_wporg', 'theme_modular_wporg' ) ) ) { ?>
504 <li id="customize-control-sm_filters_description_after_control" class="pix_customizer_setting customize-control customize-control-html" style="display: list-item;">
505 <span class="description customize-control-description"><strong>More filters</strong> are available with the PRO version of your theme.</span>
506 </li>
507 <?php }
508 }
509
510 public function maybe_add_text_after_color_palettes_customize_controls( $control ) {
511 if ( 'sm_coloration_level' === $control->setting->id && in_array( self::get_theme_type(), array( 'theme_wporg', 'theme_modular_wporg' ) ) ) { ?>
512 <li id="customize-control-sm_customize_description_after_control" class="pix_customizer_setting customize-control customize-control-html" style="display: list-item;">
513 <span class="description customize-control-description"><strong>More options</strong> are available with the PRO version of your theme.</span>
514 </li>
515 <?php }
516 }
517
518 /**
519 * Filter the final config.
520 *
521 * @param array $config
522 *
523 * @return array
524 */
525 public function filter_based_on_theme_type( $config ) {
526 if ( ! empty( $config['panels']['style_manager_panel']['sections']['sm_color_palettes_section']['options'] ) && in_array( self::get_theme_type(), array(
527 'theme_wporg',
528 'theme_modular_wporg'
529 ) ) ) {
530 $color_palettes_options = $config['panels']['style_manager_panel']['sections']['sm_color_palettes_section']['options'];
531
532 $options_to_remove = array(
533 'sm_color_diversity',
534 'sm_shuffle_colors',
535 'sm_dark_mode',
536 );
537 foreach ( $options_to_remove as $option_key ) {
538 if ( isset( $color_palettes_options[ $option_key ] ) ) {
539 $color_palettes_options[ $option_key ]['type'] = 'hidden_control';
540 }
541 }
542
543 if ( ! empty( $color_palettes_options['sm_palette_filter']['choices'] ) ) {
544 unset( $color_palettes_options['sm_palette_filter']['choices']['clarendon'] );
545 unset( $color_palettes_options['sm_palette_filter']['choices']['pastel'] );
546 unset( $color_palettes_options['sm_palette_filter']['choices']['greyish'] );
547 }
548
549 $config['panels']['style_manager_panel']['sections']['sm_color_palettes_section']['options'] = $color_palettes_options;
550 }
551
552 return $config;
553 }
554
555 /**
556 * Get the current theme type from the WUpdates code.
557 *
558 * Generally, this is a 'theme', but it could also be 'plugin', 'theme_modular', 'theme_wporg' or other markers we wish to use.
559 *
560 * @return string
561 */
562 public static function get_theme_type() {
563 $wupdates_identification = self::get_wupdates_identification_data();
564 if ( empty( $wupdates_identification['type'] ) ) {
565 return 'theme_wporg';
566 }
567
568 return sanitize_title( $wupdates_identification['type'] );
569 }
570
571 public static function get_wupdates_identification_data( $slug = '' ) {
572 if ( empty( $slug ) ) {
573 $slug = basename( get_template_directory() );
574 }
575
576 $wupdates_ids = self::get_all_wupdates_identification_data();
577
578 // We really want an id (hash_id) and a type.
579 if ( empty( $slug ) || empty( $wupdates_ids[ $slug ] ) || ! isset( $wupdates_ids[ $slug ]['id'] ) || ! isset( $wupdates_ids[ $slug ]['type'] ) ) {
580 return false;
581 }
582
583 return $wupdates_ids[ $slug ];
584 }
585
586 public static function get_all_wupdates_identification_data() {
587 if ( empty( self::$wupdates_ids ) ) {
588 self::$wupdates_ids = apply_filters( 'wupdates_gather_ids', array() );
589 }
590
591 return self::$wupdates_ids;
592 }
593
594 /**
595 * Reorganizes sections and controls added directly to WP_Customizer, not through the config.
596 *
597 * @param WP_Customize_Manager $wp_customize
598 *
599 * @since 1.9.0
600 *
601 * @todo Please note that this is house cleaning and it is only necessary due to the lack of complete standardization on the theme side. We should not need this forever!
602 *
603 */
604 public function reorganize_direct_sections_and_controls( $wp_customize ) {
605 // We will do out best to identify direct sections and move their controls to the appropriate place.
606 /** @var WP_Customize_Section $section */
607 foreach ( $wp_customize->sections() as $section ) {
608 // These are general theme options sections that need to have their controls moved to the Theme Options > General section.
609 if ( false !== strpos( $section->id, 'theme_options' ) ) {
610 $theme_options_panel = $wp_customize->get_panel( 'theme_options_panel' );
611 $general_section = false;
612 foreach ( $theme_options_panel->sections as $theme_options_section ) {
613 if ( false !== strpos( $theme_options_section->id, 'general' ) ) {
614 $general_section = $section;
615 }
616 }
617
618 if ( false === $general_section ) {
619 // We need to add a general section in the Theme Options panel.
620 $general_section = $wp_customize->add_section( 'theme_options[general]', array(
621 'title' => esc_html__( 'General', 'customify' ),
622 'panel' => $theme_options_panel->id,
623 'priority' => 2,
624 ) );
625 }
626
627 // Move all the controls in the identified theme options section to the general one.
628 /** @var WP_Customize_Control $control */
629 foreach ( $wp_customize->controls() as $control ) {
630 if ( $control->section !== $section->id ) {
631 continue;
632 }
633
634 $control->section = $general_section->id;
635 }
636
637 // Finally remove the now empty section.
638 $wp_customize->remove_section( $section->id );
639
640 break;
641 }
642 }
643 }
644
645 /**
646 * Remove the switch/preview theme panel.
647 *
648 * @param WP_Customize_Manager $wp_customize
649 *
650 * @since 1.7.4
651 *
652 */
653 public function remove_switch_theme_panel( $wp_customize ) {
654 $wp_customize->remove_panel( 'themes' );
655 }
656
657 /**
658 * Output the user feedback modal markup, if we need to.
659 *
660 * @since 1.7.0
661 */
662 public function output_user_feedback_modal() {
663 // If there is no style manager support, bail early.
664 if ( ! $this->is_supported() ) {
665 return;
666 }
667
668 // We want to ask for feedback once a month.
669 $a_month_back = time() - MONTH_IN_SECONDS;
670
671 // Only output if we should ask for feedback.
672 if ( $this->should_ask_for_feedback( $a_month_back ) ) { ?>
673 <div id="style-manager-user-feedback-modal">
674 <div class="modal">
675 <div class="modal-dialog" role="document">
676 <div class="modal-content">
677 <form id="style-manager-user-feedback" action="#" method="post">
678 <input type="hidden" name="type" value="1_to_5"/>
679 <div class="modal-header">
680 <button type="button" class="close icon media-modal-close" data-dismiss="modal"
681 aria-label="Close"><span class="media-modal-icon"><span
682 class="screen-reader-text">Close media panel</span></span></button>
683 <!-- <a href="#" class="close button button--naked gray" data-dismiss="modal" aria-label="Close">Close</a> -->
684 </div>
685 <div class="modal-body full">
686 <div class="box box--large">
687 <div class="first-step">
688 <h2 class="modal-title">How would you rate your experience in finding
689 the right colors for your site?</h2>
690 <div class="scorecard">
691 <span>Poor</span>
692 <label>
693 <input type="radio" name="rating" value="1" required/>
694 <span>1</span>
695 </label>
696 <label>
697 <input type="radio" name="rating" value="2" required/>
698 <span>2</span>
699 </label>
700 <label>
701 <input type="radio" name="rating" value="3" required/>
702 <span>3</span>
703 </label>
704 <label>
705 <input type="radio" name="rating" value="4" required/>
706 <span>4</span>
707 </label>
708 <label>
709 <input type="radio" name="rating" value="5" required/>
710 <span>5</span>
711 </label>
712 <span>Great</span>
713 </div>
714 </div>
715 <div class="second-step hidden">
716 <p><strong>What points along the way made this a <span
717 class="rating-placeholder">5</span>* experience for
718 you?</strong><br>We are counting on your insights to guide us in
719 doing better 🙏</p>
720 <div class="not-floating-labels">
721 <div class="form-row field">
722 <textarea name="message"
723 placeholder="Describe your experience in customizing your site colors.."
724 id="style-manager-user-feedback-message" rows="6"
725 oninvalid="this.setCustomValidity('May we have a little more info about your experience?')"
726 oninput="setCustomValidity('')" required></textarea>
727 </div>
728 </div>
729 <button id="style-manager-user-feedback_btn" class="button"
730 type="submit"><?php _e( 'Send us your insights', 'customify' ); ?></button>
731 </div>
732 <div class="thanks-step hidden">
733 <h3 class="modal-title">Thank you so much for your feedback!</h3>
734 <p>It means the world to us as we strive to constantly push the limits
735 and aim higher. Stay awesome! 🤗</p>
736 <p><em>The Pixelgrade Team</em></p>
737 </div>
738 <div class="error-step hidden">
739 <h3 class="modal-title">We've hit a snag!</h3>
740 <p>We couldn't record your feedback and we would truly appreciate it if
741 you would try it again at a latter time. Stay awesome! 🤗</p>
742 </div>
743 </div>
744 </div>
745 <div class="modal-footer full">
746
747 </div>
748 </form>
749 </div>
750 </div>
751 </div>
752 <!-- End Modal -->
753 <!-- Modal Backdrop (Shadow) -->
754 <div class="modal-backdrop"></div>
755 </div>
756
757 <?php }
758 }
759
760 /**
761 * Return whether user provided feedback, and if so, return the timestamp.
762 *
763 * @return bool|int
764 */
765 public function user_provided_feedback() {
766 $user_provided_feedback = get_option( 'style_manager_user_feedback_provided' );
767 if ( empty( $user_provided_feedback ) ) {
768 return false;
769 }
770
771 return $user_provided_feedback;
772 }
773
774 /**
775 * Determine if we should ask for user feedback.
776 *
777 * @param bool|int $timestamp_limit Optional. Timestamp to compare the time the user provided feedback.
778 * If the provided timestamp is earlier than the time the user provided feedback, should ask again.
779 *
780 * @return bool
781 */
782 public function should_ask_for_feedback( $timestamp_limit = false ) {
783 if ( defined( 'CUSTOMIFY_SM_ALWAYS_ASK_FOR_FEEDBACK' ) && true === CUSTOMIFY_SM_ALWAYS_ASK_FOR_FEEDBACK ) {
784 return true;
785 }
786
787 $feedback_timestamp = $this->user_provided_feedback();
788 if ( empty( $feedback_timestamp ) ) {
789 return true;
790 }
791
792 if ( ! empty( $timestamp_limit ) && intval( $timestamp_limit ) > intval( $feedback_timestamp ) ) {
793 return true;
794 }
795
796 return false;
797 }
798
799 /**
800 * Callback for the user feedback AJAX call.
801 *
802 * @since 1.7.0
803 */
804 public function user_feedback_callback() {
805 check_ajax_referer( 'customify_style_manager_user_feedback', 'nonce' );
806
807 if ( empty( $_POST['type'] ) ) {
808 wp_send_json_error( esc_html__( 'No type provided', 'customify' ) );
809 }
810
811 if ( empty( $_POST['rating'] ) ) {
812 wp_send_json_error( esc_html__( 'No rating provided', 'customify' ) );
813 }
814
815 $type = sanitize_text_field( $_POST['type'] );
816 $rating = intval( $_POST['rating'] );
817 $message = '';
818 if ( ! empty( $_POST['message'] ) ) {
819 $message = wp_kses_post( $_POST['message'] );
820 }
821
822 $request_data = array(
823 'site_url' => home_url( '/' ),
824 'satisfaction_data' => array(
825 'type' => $type,
826 'rating' => $rating,
827 'message' => $message,
828 ),
829 );
830
831 // Send the feedback.
832 $response = $this->cloud_api->send_stats( $request_data, true );
833 if ( is_wp_error( $response ) ) {
834 wp_send_json_error( esc_html__( 'Sorry, something went wrong and we couldn\'t save your feedback.', 'customify' ) );
835 }
836 $response_data = json_decode( wp_remote_retrieve_body( $response ), true );
837 // Bail in case of decode error or failure to retrieve data
838 if ( null === $response_data || empty( $response_data['code'] ) || 'success' !== $response_data['code'] ) {
839 wp_send_json_error( esc_html__( 'Sorry, something went wrong and we couldn\'t save your feedback.', 'customify' ) );
840 }
841
842 // We need to remember that the user provided feedback (and at what timestamp).
843 update_option( 'style_manager_user_feedback_provided', time(), true );
844
845 wp_send_json_success( esc_html__( 'Thank you for your feedback.', 'customify' ) );
846 }
847
848 /**
849 * Main Customify_Style_Manager Instance
850 *
851 * Ensures only one instance of Customify_Style_Manager is loaded or can be loaded.
852 *
853 * @return Customify_Style_Manager Main Customify_Style_Manager instance
854 * @since 1.7.0
855 * @static
856 *
857 */
858 public static function instance() {
859
860 if ( is_null( self::$_instance ) ) {
861 self::$_instance = new self();
862 }
863
864 return self::$_instance;
865 }
866
867 /**
868 * Cloning is forbidden.
869 *
870 * @since 1.7.0
871 */
872 public function __clone() {
873
874 _doing_it_wrong( __FUNCTION__, esc_html__( 'You should not do that!', 'customify' ), null );
875 }
876
877 /**
878 * Unserializing instances of this class is forbidden.
879 *
880 * @since 1.7.0
881 */
882 public function __wakeup() {
883
884 _doing_it_wrong( __FUNCTION__, esc_html__( 'You should not do that!', 'customify' ), null );
885 }
886 }
887
888 }
889