Customify – A Theme Customizer Booster - Version 1.9.0

Version Description

  • Added ability to modify existing Customizer panels, sections, controls
  • Added system for admin notifications
  • Overall enhancements for more performance and stability
Download this release

Release Info

Developer vlad.olaru
Plugin Icon Customify – A Theme Customizer Booster
Version 1.9.0
Comparing to
See all releases

Code changes from version 1.8.0 to 1.9.0

class-pixcustomify.php CHANGED
@@ -230,6 +230,8 @@ class PixCustomifyPlugin {
230
 
231
  add_action( 'customize_register', array( $this, 'remove_default_sections' ), 11 );
232
  add_action( 'customize_register', array( $this, 'register_customizer' ), 12 );
 
 
233
 
234
  if ( $this->get_plugin_setting( 'enable_editor_style', true ) ) {
235
  add_action( 'admin_head', array( $this, 'add_customizer_settings_into_wp_editor' ) );
@@ -1141,6 +1143,176 @@ class PixCustomifyPlugin {
1141
  pixcustomify::require_all( $path );
1142
  }
1143
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1144
  /**
1145
  * @param WP_Customize_Manager $wp_customize
1146
  */
@@ -1176,8 +1348,8 @@ class PixCustomifyPlugin {
1176
  $panel_args = array(
1177
  'priority' => 10,
1178
  'capability' => 'edit_theme_options',
1179
- 'title' => __( 'Panel title is required', 'pixcustomify' ),
1180
- 'description' => __( 'Description of what this panel does.', 'pixcustomify' ),
1181
  'auto_expand_sole_section' => false,
1182
  );
1183
 
230
 
231
  add_action( 'customize_register', array( $this, 'remove_default_sections' ), 11 );
232
  add_action( 'customize_register', array( $this, 'register_customizer' ), 12 );
233
+ // Maybe the theme has instructed us to do things like removing sections or controls.
234
+ add_action( 'customize_register', array( $this, 'maybe_process_config_extras' ), 13 );
235
 
236
  if ( $this->get_plugin_setting( 'enable_editor_style', true ) ) {
237
  add_action( 'admin_head', array( $this, 'add_customizer_settings_into_wp_editor' ) );
1143
  pixcustomify::require_all( $path );
1144
  }
1145
 
1146
+ /**
1147
+ * Maybe process certain "commands" from the config.
1148
+ *
1149
+ * Mainly things like removing sections, controls, etc.
1150
+ *
1151
+ * @since 1.9.0
1152
+ *
1153
+ * @param WP_Customize_Manager $wp_customize
1154
+ */
1155
+ public function maybe_process_config_extras( $wp_customize ) {
1156
+ // Bail if we have no external theme config data.
1157
+ if ( empty( $this->customizer_config ) || ! is_array( $this->customizer_config ) ) {
1158
+ return;
1159
+ }
1160
+
1161
+ // Maybe remove panels
1162
+ if ( ! empty( $this->customizer_config['remove_panels'] ) ) {
1163
+ // Standardize it.
1164
+ if ( is_string( $this->customizer_config['remove_panels'] ) ) {
1165
+ $this->customizer_config['remove_panels'] = array( $this->customizer_config['remove_panels'] );
1166
+ }
1167
+
1168
+ foreach ( $this->customizer_config['remove_panels'] as $panel_id ) {
1169
+ $wp_customize->remove_panel( $panel_id );
1170
+ }
1171
+ }
1172
+
1173
+ // Maybe change panel props.
1174
+ if ( ! empty( $this->customizer_config['change_panel_props'] ) ) {
1175
+ foreach ( $this->customizer_config['change_panel_props'] as $panel_id => $panel_props ) {
1176
+ if ( ! is_array( $panel_props ) ) {
1177
+ continue;
1178
+ }
1179
+
1180
+ $panel = $wp_customize->get_panel( $panel_id );
1181
+ if ( empty( $panel ) || ! $panel instanceof WP_Customize_Panel ) {
1182
+ continue;
1183
+ }
1184
+
1185
+ $public_props = get_class_vars( get_class( $panel ) );
1186
+ foreach ( $panel_props as $prop_name => $prop_value ) {
1187
+
1188
+ if ( ! in_array( $prop_name, array_keys( $public_props ) ) ) {
1189
+ continue;
1190
+ }
1191
+
1192
+ $panel->$prop_name = $prop_value;
1193
+ }
1194
+ }
1195
+ }
1196
+
1197
+ // Maybe remove sections
1198
+ if ( ! empty( $this->customizer_config['remove_sections'] ) ) {
1199
+ // Standardize it.
1200
+ if ( is_string( $this->customizer_config['remove_sections'] ) ) {
1201
+ $this->customizer_config['remove_sections'] = array( $this->customizer_config['remove_sections'] );
1202
+ }
1203
+
1204
+ foreach ( $this->customizer_config['remove_sections'] as $section_id ) {
1205
+
1206
+ if ( 'widgets' === $section_id ) {
1207
+ global $wp_registered_sidebars;
1208
+
1209
+ foreach ( $wp_registered_sidebars as $widget => $settings ) {
1210
+ $wp_customize->remove_section( 'sidebar-widgets-' . $widget );
1211
+ }
1212
+ continue;
1213
+ }
1214
+
1215
+ $wp_customize->remove_section( $section_id );
1216
+ }
1217
+ }
1218
+
1219
+ // Maybe change section props.
1220
+ if ( ! empty( $this->customizer_config['change_section_props'] ) ) {
1221
+ foreach ( $this->customizer_config['change_section_props'] as $section_id => $section_props ) {
1222
+ if ( ! is_array( $section_props ) ) {
1223
+ continue;
1224
+ }
1225
+
1226
+ $section = $wp_customize->get_section( $section_id );
1227
+ if ( empty( $section ) || ! $section instanceof WP_Customize_Section ) {
1228
+ continue;
1229
+ }
1230
+
1231
+ $public_props = get_class_vars( get_class( $section ) );
1232
+ foreach ( $section_props as $prop_name => $prop_value ) {
1233
+
1234
+ if ( ! in_array( $prop_name, array_keys( $public_props ) ) ) {
1235
+ continue;
1236
+ }
1237
+
1238
+ $section->$prop_name = $prop_value;
1239
+ }
1240
+ }
1241
+ }
1242
+
1243
+ // Maybe remove settings
1244
+ if ( ! empty( $this->customizer_config['remove_settings'] ) ) {
1245
+ // Standardize it.
1246
+ if ( is_string( $this->customizer_config['remove_settings'] ) ) {
1247
+ $this->customizer_config['remove_settings'] = array( $this->customizer_config['remove_settings'] );
1248
+ }
1249
+
1250
+ foreach ( $this->customizer_config['remove_settings'] as $setting_id ) {
1251
+ $wp_customize->remove_setting( $setting_id );
1252
+ }
1253
+ }
1254
+
1255
+ // Maybe change setting props.
1256
+ if ( ! empty( $this->customizer_config['change_setting_props'] ) ) {
1257
+ foreach ( $this->customizer_config['change_setting_props'] as $setting_id => $setting_props ) {
1258
+ if ( ! is_array( $setting_props ) ) {
1259
+ continue;
1260
+ }
1261
+
1262
+ $setting = $wp_customize->get_setting( $setting_id );
1263
+ if ( empty( $setting ) || ! $setting instanceof WP_Customize_Setting ) {
1264
+ continue;
1265
+ }
1266
+
1267
+ $public_props = get_class_vars( get_class( $setting ) );
1268
+ foreach ( $setting_props as $prop_name => $prop_value ) {
1269
+
1270
+ if ( ! in_array( $prop_name, array_keys( $public_props ) ) ) {
1271
+ continue;
1272
+ }
1273
+
1274
+ $setting->$prop_name = $prop_value;
1275
+ }
1276
+ }
1277
+ }
1278
+
1279
+ // Maybe remove controls
1280
+ if ( ! empty( $this->customizer_config['remove_controls'] ) ) {
1281
+ // Standardize it.
1282
+ if ( is_string( $this->customizer_config['remove_controls'] ) ) {
1283
+ $this->customizer_config['remove_controls'] = array( $this->customizer_config['remove_controls'] );
1284
+ }
1285
+
1286
+ foreach ( $this->customizer_config['remove_controls'] as $control_id ) {
1287
+ $wp_customize->remove_control( $control_id );
1288
+ }
1289
+ }
1290
+
1291
+ // Maybe change control props.
1292
+ if ( ! empty( $this->customizer_config['change_control_props'] ) ) {
1293
+ foreach ( $this->customizer_config['change_control_props'] as $control_id => $control_props ) {
1294
+ if ( ! is_array( $control_props ) ) {
1295
+ continue;
1296
+ }
1297
+
1298
+ $control = $wp_customize->get_control( $control_id );
1299
+ if ( empty( $control ) || ! $control instanceof WP_Customize_Control ) {
1300
+ continue;
1301
+ }
1302
+
1303
+ $public_props = get_class_vars( get_class( $control ) );
1304
+ foreach ( $control_props as $prop_name => $prop_value ) {
1305
+
1306
+ if ( ! in_array( $prop_name, array_keys( $public_props ) ) ) {
1307
+ continue;
1308
+ }
1309
+
1310
+ $control->$prop_name = $prop_value;
1311
+ }
1312
+ }
1313
+ }
1314
+ }
1315
+
1316
  /**
1317
  * @param WP_Customize_Manager $wp_customize
1318
  */
1348
  $panel_args = array(
1349
  'priority' => 10,
1350
  'capability' => 'edit_theme_options',
1351
+ 'title' => __( 'Panel title is required', 'customify' ),
1352
+ 'description' => __( 'Description of what this panel does.', 'customify' ),
1353
  'auto_expand_sole_section' => false,
1354
  );
1355
 
customify.php CHANGED
@@ -3,7 +3,7 @@
3
  Plugin Name: Customify
4
  Plugin URI: https://wordpress.org/plugins/customify/
5
  Description: A Theme Customizer Booster
6
- Version: 1.8.0
7
  Author: Pixelgrade
8
  Author URI: https://pixelgrade.com
9
  Author Email: contact@pixelgrade.com
@@ -23,12 +23,12 @@ if ( ! defined('EXT')) {
23
  define('EXT', '.php');
24
  }
25
 
26
- require 'core/bootstrap' . EXT;
27
 
28
  // Include our helper array class.
29
- require 'includes/lib/class-customify-array' . EXT;
30
 
31
- $config = include 'plugin-config' . EXT;
32
 
33
  // set textdomain
34
  pixcustomify::settextdomain( 'customify' );
@@ -36,7 +36,7 @@ pixcustomify::settextdomain( 'customify' );
36
  // Ensure Test Data
37
  // ----------------
38
 
39
- $defaults = include 'plugin-defaults'.EXT;
40
 
41
  $current_data = get_option( $config['settings-key'] );
42
 
@@ -61,7 +61,7 @@ function PixCustomifyPlugin() {
61
  */
62
  require_once plugin_dir_path( __FILE__ ) . 'class-pixcustomify.php';
63
 
64
- $instance = PixCustomifyPlugin::instance( __FILE__, '1.8.0' );
65
 
66
  return $instance;
67
  }
3
  Plugin Name: Customify
4
  Plugin URI: https://wordpress.org/plugins/customify/
5
  Description: A Theme Customizer Booster
6
+ Version: 1.9.0
7
  Author: Pixelgrade
8
  Author URI: https://pixelgrade.com
9
  Author Email: contact@pixelgrade.com
23
  define('EXT', '.php');
24
  }
25
 
26
+ require 'core/bootstrap.php';
27
 
28
  // Include our helper array class.
29
+ require 'includes/lib/class-customify-array.php';
30
 
31
+ $config = include 'plugin-config.php';
32
 
33
  // set textdomain
34
  pixcustomify::settextdomain( 'customify' );
36
  // Ensure Test Data
37
  // ----------------
38
 
39
+ $defaults = include 'plugin-defaults.php';
40
 
41
  $current_data = get_option( $config['settings-key'] );
42
 
61
  */
62
  require_once plugin_dir_path( __FILE__ ) . 'class-pixcustomify.php';
63
 
64
+ $instance = PixCustomifyPlugin::instance( __FILE__, '1.9.0' );
65
 
66
  return $instance;
67
  }
includes/admin-notifications-manager/admin-notice-manager.css ADDED
@@ -0,0 +1,29 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /* Styling for Pixcloud Admin Notice Manager */
2
+
3
+ /* Style redirect button to look like link */
4
+ .pixcloud_anm-link {
5
+ color: #0073aa;
6
+ padding: 0;
7
+ margin: 0;
8
+ border-style: none;
9
+ background: none;
10
+ outline: 0;
11
+ transition-property: border, background, color;
12
+ transition-duration: .05s;
13
+ transition-timing-function: ease-in-out;
14
+ text-decoration: underline;
15
+ }
16
+
17
+ .pixcloud_anm-link:active {
18
+ color: #00a0d2;
19
+ }
20
+
21
+ .pixcloud_anm-link:hover {
22
+ color: #00a0d2;
23
+ cursor: pointer;
24
+ }
25
+
26
+ .pixcloud_anm-link:focus {
27
+ color: #124964;
28
+ box-shadow: 0 0 0 1px #5b9dd9, 0 0 2px 1px rgba(30, 140, 190, .8);
29
+ }
includes/admin-notifications-manager/admin-notice-manager.js ADDED
@@ -0,0 +1,80 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ (function( $ ) {
2
+ 'use strict';
3
+
4
+ /**
5
+ * Javascript for Admin Notice Manager
6
+ */
7
+
8
+ // On document ready
9
+ $( function() {
10
+
11
+ var redirectUrl = '';
12
+ var dismissEvent = '';
13
+ var redirectNewTab = false;
14
+
15
+ // Send ajax
16
+ function ajaxDismiss( dismissElement ) {
17
+ var container = dismissElement.closest( '.notice' );
18
+ var noticeID = container.find('.pixcloud_anm-notice-id').val();
19
+ var data = {
20
+ action : container.find( '.pixcloud_anm-id' ).val() + '_dismiss_admin_notice',
21
+ noticeID : noticeID
22
+ };
23
+ data['nonce-pixcloud_anm-' + noticeID] = container.find( '#nonce-pixcloud_anm-' + noticeID ).val();
24
+ if ( dismissEvent.length ) {
25
+ data['pixcloud_anm-event'] = dismissEvent;
26
+ }
27
+ $.ajax({
28
+ url: ajaxurl,
29
+ type: 'post',
30
+ data: data
31
+ });
32
+ }
33
+
34
+ // Dismiss notice
35
+ function dismissNotice( dismissElement ) {
36
+ var container = dismissElement.closest( '.notice' );
37
+ container.fadeTo( 100, 0, function() {
38
+ container.slideUp( 100, function() {
39
+ container.remove();
40
+ });
41
+ });
42
+ }
43
+
44
+ // Send ajax on click of dismiss icon
45
+ $( 'body' ).on( 'click', '.notice-manager-ajax .notice-dismiss', function() {
46
+ ajaxDismiss( $(this) );
47
+ });
48
+
49
+ // On click of dismiss element, set redirect url or event and trigger ajax dismiss
50
+ $( 'body' ).on( 'click', '.pixcloud_anm-dismiss', function() {
51
+ if ( 'pixcloud_anm-redirect' == $(this).attr('name') ) {
52
+ redirectUrl = $(this).val();
53
+ if ( $(this).data( 'newtab' ) ) {
54
+ redirectNewTab = true;
55
+ }
56
+ } else if ( 'pixcloud_anm-event' == $(this).attr('name') ) {
57
+ dismissEvent = $(this).val();
58
+ }
59
+ ajaxDismiss( $(this) );
60
+ dismissNotice( $(this) );
61
+ });
62
+
63
+ // Prevent form submit and redirect if url has been set
64
+ $( 'body' ).on( 'submit', '.pixcloud_anm-form', function(evt) {
65
+ evt.preventDefault();
66
+ if ( redirectUrl.length > 0 ) {
67
+ setTimeout( function() {
68
+ if ( redirectNewTab ) {
69
+ window.open( redirectUrl, '_blank' );
70
+ } else {
71
+ window.location.href = redirectUrl;
72
+ }
73
+ }, 100 );
74
+ }
75
+ return false;
76
+ });
77
+
78
+ });
79
+
80
+ })( jQuery );
includes/admin-notifications-manager/class-admin-notifications-manager.php ADDED
@@ -0,0 +1,1300 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /**
4
+ *
5
+ * A class to handle the display and dismissal of pixcloud admin notices.
6
+ *
7
+ * This class started from this one https://github.com/jlad26/admin-notice-manager/blob/master/admin-notice-manager/class-admin-notice-manager.php
8
+ *
9
+ */
10
+ class Pixcloud_Admin_Notifications_Manager {
11
+
12
+ /**
13
+ * Holds the only instance of this class.
14
+ * @var null|Pixcloud_Admin_Notifications_Manager
15
+ * @access protected
16
+ * @since 1.9.0
17
+ */
18
+ protected static $_instance = null;
19
+
20
+ /**
21
+ * The unique identifier of this admin notice manager.
22
+ *
23
+ * @access protected
24
+ * @since 1.9.0
25
+ * @var string $manager_id The string used to uniquely identify this manager.
26
+ * Used as a prefix to keys when storing in usermeta.
27
+ */
28
+ protected $manager_id;
29
+
30
+ /**
31
+ * The version of this plugin.
32
+ *
33
+ * @access protected
34
+ * @since 1.9.0
35
+ * @var string $version The current version of the parent plugin.
36
+ */
37
+ protected $version;
38
+
39
+ /**
40
+ * The url to the assets directory.
41
+ *
42
+ * @access protected
43
+ * @since 1.9.0
44
+ * @var string $url_to_assets_dir Url to the directory where the files admin-notice-manager.js and
45
+ * admin-notice-manager.css are located. Must include trailing slash.
46
+ */
47
+ protected $url_to_assets_dir;
48
+
49
+ /**
50
+ * The text domain for translation.
51
+ *
52
+ * @access protected
53
+ * @since 1.9.0
54
+ * @var string $text_domain Text domain for translation.
55
+ */
56
+ protected $text_domain;
57
+
58
+ /**
59
+ * Notifications to be processed and maybe displayed.
60
+ *
61
+ * @access protected
62
+ * @since 1.9.0
63
+ * @var array $notifications
64
+ */
65
+ protected $notifications = array();
66
+
67
+ /**
68
+ * The supported notification types and their conversions.
69
+ *
70
+ * @access protected
71
+ * @since 1.9.0
72
+ * @var array
73
+ */
74
+ protected $types = array(
75
+ 'info' => 'info',
76
+ 'standard_info' => 'info',
77
+ 'success' => 'success',
78
+ 'standard_success' => 'success',
79
+ 'warning' => 'warning',
80
+ 'standard_warning' => 'warning',
81
+ 'error' => 'error',
82
+ 'standard_error' => 'error',
83
+ 'custom' => 'custom',
84
+ );
85
+
86
+ /**
87
+ * Constructor.
88
+ *
89
+ * @since 1.9.0
90
+ *
91
+ * @param array $args
92
+ */
93
+ protected function __construct( $args = array() ) {
94
+ $this->init( $args );
95
+ }
96
+
97
+ /**
98
+ * Initialize the notifications manager.
99
+ *
100
+ * @since 1.9.0
101
+ *
102
+ * @param array $args {
103
+ *
104
+ * @type string $manager_id Unique id for this manager. Used as a prefix for database keys.
105
+ * @type string $plugin_name Name of the parent plugin.
106
+ * @type string $url_to_assets_dir Path to directory containing admin-notice-manager.js.
107
+ * @type string $text_domain Text domain for translation.
108
+ * @type string $version Plugin version.
109
+ * @type array $notifications Array of array of notifications with keys the notification id
110
+ * }
111
+ */
112
+ public function init( array $args ) {
113
+ // We will only initialize in the WP dashboard (admin area).
114
+ if ( ! is_admin() ) {
115
+ return;
116
+ }
117
+
118
+ $defaults = array(
119
+ 'manager_id' => 'pixcloud_anm',
120
+ 'plugin_name' => 'Unnamed plugin',
121
+ 'url_to_assets_dir' => '',
122
+ 'text_domain' => 'default',
123
+ 'version' => '',
124
+ 'notifications' => array(),
125
+ );
126
+
127
+ $args = wp_parse_args( $args, $defaults );
128
+
129
+ // Set manager id, text domain and version.
130
+ $this->manager_id = empty( $args['manager_id'] ) ? 'pixcloud_anm' : $args['manager_id'];
131
+ $this->text_domain = $args['text_domain'];
132
+ $this->version = $args['version'];
133
+ $this->plugin_name = $args['plugin_name'];
134
+ $this->notifications = $args['notifications'];
135
+
136
+ // Add hooks, but only if we are not uninstalling the plugin.
137
+ if ( ! defined( 'WP_UNINSTALL_PLUGIN' ) ) {
138
+ $this->add_hooks();
139
+ }
140
+
141
+ // Set url to js directory, adding trailing slash if needed. Defaults to this file's directory.
142
+ if ( empty( $args['url_to_assets_dir'] ) ) {
143
+ $args['url_to_assets_dir'] = plugin_dir_url( __FILE__ );
144
+ }
145
+ if ( '/' != substr( $args['url_to_assets_dir'], - 1 ) ) {
146
+ $args['url_to_assets_dir'] .= '/';
147
+ }
148
+ $this->url_to_assets_dir = $args['url_to_assets_dir'];
149
+
150
+ // Make sure that the conditions processing logic is loaded.
151
+ require_once 'class-notification-conditions.php';
152
+ }
153
+
154
+ /**
155
+ * Initiate our hooks.
156
+ *
157
+ * @since 1.9.0
158
+ */
159
+ public function add_hooks() {
160
+ // Add action to load remote notifications.
161
+ add_action( 'admin_init', array( $this, 'maybe_load_remote_notifications' ) );
162
+
163
+ // Add actions to display notices as needed.
164
+ add_action( 'admin_notices', array( $this, 'display_notices' ) );
165
+
166
+ // Add JS and ajax processing to handle dismissal of persistent notices.
167
+ add_action( 'admin_enqueue_scripts', array( $this, 'enqueue_scripts' ) );
168
+ add_action( 'wp_ajax_' . $this->manager_id . '_dismiss_admin_notice', array( $this, 'dismiss_notice' ) );
169
+
170
+ // Add notifications data to requests.
171
+ add_filter( 'customify_pixelgrade_cloud_request_data', array( $this, 'add_data_to_request' ), 10, 1 );
172
+ }
173
+
174
+ public function maybe_load_remote_notifications() {
175
+ if ( empty( $this->notifications ) ) {
176
+ $this->notifications = $this->convert_remote_notifications_config( $this->get_remote_notifications_config() );
177
+ }
178
+ }
179
+
180
+ /**
181
+ * Get the remote notifications configuration.
182
+ *
183
+ * @since 1.9.0
184
+ *
185
+ * @param bool $skip_cache Optional. Whether to use the cached config or fetch a new one.
186
+ *
187
+ * @return array
188
+ */
189
+ public function get_remote_notifications_config( $skip_cache = false ) {
190
+ // Make sure that the Design Assets class is loaded.
191
+ require_once dirname(__FILE__ ) . '/../lib/class-customify-design-assets.php';
192
+
193
+ // Get the design assets data.
194
+ $design_assets = Customify_Design_Assets::instance()->get( $skip_cache );
195
+ if ( false === $design_assets || empty( $design_assets['notifications'] ) ) {
196
+ $notifications_config = array();
197
+ } else {
198
+ $notifications_config = $design_assets['notifications'];
199
+ }
200
+
201
+ return apply_filters( 'customify_get_remote_notifications', $notifications_config );
202
+ }
203
+
204
+ public function convert_remote_notifications_config( $notifications_config ) {
205
+ $notifications = array();
206
+ if ( empty( $notifications_config ) || ! is_array( $notifications_config ) ) {
207
+ return $notifications;
208
+ }
209
+
210
+ foreach ( $notifications_config as $notification_config ) {
211
+ // Make sure we have something to work with.
212
+ if ( empty( $notification_config['hashid'] ) || empty( $notification_config['options'] ) || ! is_array( $notification_config['options'] ) ) {
213
+ continue;
214
+ }
215
+
216
+ $options = $notification_config['options'];
217
+
218
+ $options['id'] = $notification_config['hashid'];
219
+
220
+ // Make sure we support the notification type.
221
+ if ( empty( $options['type']) || ! array_key_exists( $options['type'], $this->types ) ) {
222
+ continue;
223
+ }
224
+ // Use the standard (converted) type.
225
+ $options['type'] = $this->types[ $options['type'] ];
226
+
227
+ // Handle the empty message.
228
+ if ( empty( $options['message'] ) ) {
229
+ continue;
230
+ }
231
+
232
+ // Parse the details.
233
+ $notification = $this->parse_notice_args( $options );
234
+
235
+ if ( ! empty( $notification ) ) {
236
+ $notifications[ $notification_config['hashid'] ] = $notification;
237
+ }
238
+ }
239
+
240
+ return $notifications;
241
+ }
242
+
243
+ /**
244
+ * Parse notice args.
245
+ *
246
+ * @access protected
247
+ * @since 1.9.0
248
+ *
249
+ * @param array $notice Array of user-set args
250
+ *
251
+ * @return array|false Notice with defaults validated and set as necessary, or false if values not validated.
252
+ */
253
+ protected function parse_notice_args( $notice ) {
254
+ if ( empty( $notice ) ) {
255
+ return false;
256
+ }
257
+
258
+ // Set the notice arguments using defaults where necessary. (user_ids should always be set for an opt out notice.)
259
+ $defaults = array(
260
+ 'type' => 'error',
261
+ 'message' => '',
262
+ 'container_classes' => array(),
263
+ 'wrap_tag' => 'p',
264
+ 'user_ids' => array(),
265
+ 'screen_ids' => array(),
266
+ 'post_ids' => array(),
267
+ 'persistent' => false,
268
+ 'dismissable' => true,
269
+ 'no_js_dismissable' => false,
270
+ 'dismiss_for_all_users' => false,
271
+ );
272
+
273
+ $notice = wp_parse_args( $notice, $defaults );
274
+
275
+ // Set the notice id if not already set.
276
+ if ( ! isset( $notice['id'] ) ) {
277
+ $notice['id'] = md5( $notice['message'] . $notice['type'] );
278
+ }
279
+
280
+ // Set the screen ids if not already set. (Will always already be set for opt out notices.)
281
+ if ( ! isset( $notice['screen_ids'] ) ) {
282
+
283
+ // Default is generally all screens...
284
+ $notice['screen_ids'] = array();
285
+
286
+ // ...but for persistent notices we set default to current screen if we can
287
+ if ( $notice['persistent'] && $screen_id = $this->get_current_screen_id() ) {
288
+ $notice['screen_ids'] = array( $screen_id );
289
+ }
290
+ }
291
+
292
+ // If notice is not dismissable, set no-js-dismissable to false as well.
293
+ if ( ! $notice['dismissable'] ) {
294
+ $notice['no_js_dismissable'] = false;
295
+ }
296
+
297
+ // Validate all values.
298
+ foreach ( $notice as $key => $value ) {
299
+
300
+ switch ( $key ) {
301
+ case 'id' :
302
+ if ( ! is_string( $value ) && ! is_int( $value ) ) {
303
+ return false;
304
+ }
305
+ break;
306
+ case 'message' :
307
+ if ( ! is_string( $value ) && ! is_array( $value ) ) {
308
+ return false;
309
+ }
310
+ if ( empty( $value ) ) {
311
+ return false;
312
+ }
313
+ break;
314
+ case 'wrap_tag' :
315
+ if ( ! is_string( $value ) ) {
316
+ return false;
317
+ }
318
+ break;
319
+ case 'user_ids' :
320
+ if ( ! is_array( $value ) ) {
321
+ return false;
322
+ } else {
323
+ foreach ( $value as $user_info ) {
324
+ if ( ! is_int( $user_info ) && ! is_string( $user_info ) ) {
325
+ return false;
326
+ break;
327
+ }
328
+ }
329
+ }
330
+ break;
331
+ case 'screen_ids' :
332
+ if ( ! is_array( $value ) ) {
333
+ return false;
334
+ } else {
335
+ foreach ( $value as $screen_id ) {
336
+ if ( ! is_string( $screen_id ) ) {
337
+ return false;
338
+ break;
339
+ }
340
+ }
341
+ }
342
+ break;
343
+ case 'post_ids' :
344
+ if ( ! is_array( $value ) ) {
345
+ return false;
346
+ } else {
347
+ foreach ( $value as $post_id ) {
348
+ if ( ! is_int( $post_id ) ) {
349
+ return false;
350
+ break;
351
+ }
352
+ }
353
+ }
354
+ break;
355
+ case 'persistent' :
356
+ case 'dismissable' :
357
+ case 'dismiss_for_all_users' :
358
+ if ( ! is_bool( $value ) ) {
359
+ return false;
360
+ }
361
+ break;
362
+ default:
363
+ break;
364
+ }
365
+ }
366
+
367
+ return $notice;
368
+ }
369
+
370
+ /**
371
+ * Enqueue scripts and styles.
372
+ *
373
+ * @param string $hook Hook
374
+ *
375
+ * @hooked admin_enqueue_scripts
376
+ */
377
+ public function enqueue_scripts( $hook ) {
378
+ // Note that handle is not specific to the manager id so if we are using two plugins with the notice manager files are only loaded once.
379
+ wp_enqueue_script( 'pixcloud-admin-notice-manager-js', $this->url_to_assets_dir . 'admin-notice-manager.js', array( 'jquery' ), $this->version );
380
+ wp_enqueue_style( 'pixcloud-admin-notice-manager-css', $this->url_to_assets_dir . 'admin-notice-manager.css', array(), $this->version );
381
+ }
382
+
383
+ /**
384
+ * Add a notification.
385
+ *
386
+ * @since 1.9.0
387
+ *
388
+ * @param array $notice {
389
+ * @type string $id Unique id for this notice. Default is hashed value of some of the notice parameters.
390
+ * Setting an id is recommended however - otherwise non-unique ids are possible and may
391
+ * cause unexpected deletion of notices. Updating messages when they are changed by the
392
+ * developer gets fiddly too.
393
+ * @type string $message Message to be displayed.
394
+ * @type string $wrap_tag Tag to wrap message in. Default is 'p'. Set to empty string or false for no wrap.
395
+ * @type string $type One of 'success', 'error', warning', 'info'. Default is 'error'.
396
+ * @type array $user_ids Empty array means all users. Default is all users (not current user).
397
+ * For example: array( 3, 'administrator', 55, 153, 'editors' ) will set the message
398
+ * for users with ids of 3, 55 and 153, and for all users that are administrators or editors.
399
+ * @type array $screen_ids Array of screen ids on which message should be displayed.
400
+ * Set to empty array for all screens. If left unset the current screen is set if possible,
401
+ * it is recommended to explicitly specify the desired screen rather than leaving unset.
402
+ * If during testing the notice is set on a screen that is then not viewed because of a redirect
403
+ * (e.g. options), changing the screen in the notice args will have no effect because the notice
404
+ * has been stored in the db and will not be updated.
405
+ * Default is all screens (empty array).
406
+ * @type array $post_ids Array of post ids on which message should be displayed. Empty array means all posts.
407
+ * Default is all posts.
408
+ * @type string $persistent True for persistent, false for one-time. Default is false.
409
+ * @type bool $dismissable Whether notice is dismissable. Default is true.
410
+ * @type bool $no_js_dismissable Whether to give option to dismiss notice if no js. Only applies when $dismissable is true.
411
+ * Default is false. Caution should be used in setting this to true. The act of dismissing the
412
+ * notice refreshes the screen so any changed data on screen will be lost. This could be extremely
413
+ * frustrating for a user who has just entered or updated loads of data (e.g., when editing a post).
414
+ * @type bool $dismiss_for_all_users Whether to delete notice for all users or just the user that has dismissed the notice.
415
+ * Only applies when $dismissable is true. Default is false.
416
+ * }
417
+ * @return array|WP_Error|bool Notice that has been set by user, or error if notice has failed.
418
+ */
419
+ public function add_notification( array $notice ) {
420
+
421
+ // If not set, set user ids to empty array to indicate all users.
422
+ if ( ! isset( $notice['user_ids'] ) ) {
423
+ $notice['user_ids'] = array();
424
+ }
425
+
426
+ // If required, set default screen ids to all screens.
427
+ if ( ! isset( $notice['screen_ids'] ) ) {
428
+ $notice['screen_ids'] = array();
429
+ }
430
+
431
+ $notice = $this->parse_notice_args( $notice );
432
+
433
+ $notice_id = $notice['id'];
434
+ unset( $notice['id'] );
435
+
436
+ $notices = $this->notifications;
437
+
438
+ // Add new notice to existing notices. NB this over-writes a notice with the same id.
439
+ $notices[ $notice_id ] = $notice;
440
+
441
+ // Update notices.
442
+ $this->notifications = $notices;
443
+
444
+ return $notice;
445
+
446
+ }
447
+
448
+ /**
449
+ * Add notifications.
450
+ *
451
+ * @since 1.9.0
452
+ *
453
+ * @param array $notices Array of notices. If notice id is not set, key of array is used.
454
+ *
455
+ * @return array Array with keys as notice ids and values as either notice set or WP Error object.
456
+ */
457
+ public function add_notifications( array $notices ) {
458
+
459
+ $results = array();
460
+
461
+ foreach ( $notices as $key => $notice ) {
462
+ if ( ! isset( $notice['id'] ) ) {
463
+ $notice['id'] = $key;
464
+ }
465
+ $results[] = $this->add_notification( $notice );
466
+ }
467
+
468
+ return $results;
469
+
470
+ }
471
+
472
+ /**
473
+ * Parse user ids, converting roles to user ids.
474
+ *
475
+ * @access protected
476
+ * @since 1.9.0
477
+ *
478
+ * @param array $user_ids
479
+ *
480
+ * @return array $user_ids Parsed user ids
481
+ */
482
+ protected function parse_user_roles( array $user_ids ) {
483
+
484
+ // Convert user roles to user ids and add them.
485
+ if ( ! empty( $user_ids ) ) {
486
+
487
+ $user_roles = array();
488
+ foreach ( $user_ids as $key => $user_info ) {
489
+ if ( is_string( $user_info ) ) {
490
+ $user_roles[] = $user_info;
491
+ unset( $user_ids[ $key ] );
492
+ }
493
+ }
494
+ if ( ! empty( $user_roles ) ) {
495
+ $args = array(
496
+ 'count_total' => false,
497
+ 'fields' => 'ID',
498
+ 'role__in' => $user_roles,
499
+ );
500
+ $role_user_ids = get_users( $args );
501
+
502
+ if ( ! empty( $role_user_ids ) ) {
503
+ $user_ids = array_unique( array_merge( $user_ids, $role_user_ids ) );
504
+ }
505
+ }
506
+ }
507
+
508
+ return $user_ids;
509
+
510
+ }
511
+
512
+
513
+ /**
514
+ * Get current screen id.
515
+ *
516
+ * @since 1.9.0
517
+ *
518
+ * @access protected
519
+ * @return int $screen_id id of current screen, 0 if not available
520
+ */
521
+ protected function get_current_screen_id() {
522
+ $screen_id = 0;
523
+ if ( function_exists( 'get_current_screen' ) ) {
524
+ $screen = get_current_screen();
525
+ if ( ! empty( $screen ) ) {
526
+ $screen_id = $screen->id;
527
+ }
528
+ }
529
+
530
+ return $screen_id;
531
+ }
532
+
533
+ /**
534
+ * Returns html for a dismiss on redirect link.
535
+ *
536
+ * @since 1.9.0
537
+ *
538
+ * @param array $args {
539
+ *
540
+ * @type string $content Html to display as link.
541
+ * @type string $redirect Redirect url. Set as empty string for no redirect. Default is no redirect.
542
+ * @type string $new_tab If true, link is opened in a new window / tab (equivalent to target="_blank". Default is false.
543
+ * Only works on browsers with js enabled.
544
+ * @type array $classes Array of classes for the button. Default is array( pixcloud_anm-link ) which styles as a link.
545
+ * }
546
+ * @return string Button html (styled by default as link)
547
+ */
548
+ public function dismiss_on_redirect_link( array $args ) {
549
+
550
+ $defaults = array(
551
+ 'content' => 'Undefined',
552
+ 'redirect' => '',
553
+ 'new_tab' => false,
554
+ 'classes' => array( 'pixcloud_anm-link' ),
555
+ );
556
+
557
+ $args = wp_parse_args( $args, $defaults );
558
+
559
+ $classes = array( 'pixcloud_anm-dismiss' );
560
+ if ( ! empty( $args['classes'] ) && is_array( $args['classes'] ) ) {
561
+ $classes = array_merge( $args['classes'], $classes );
562
+ }
563
+ $classes = implode( ' ', $classes );
564
+
565
+ // Add button with value of redirect url.
566
+ return '<button type="submit" class="' . $classes . '" name="pixcloud_anm-redirect" data-newtab="' . intval( $args['new_tab'] ) . '" value="' . esc_attr( $args['redirect'] ) . '">' . $args['content'] . '</button>';
567
+
568
+ }
569
+
570
+ /**
571
+ * Returns html for button that triggers a specific action hook.
572
+ *
573
+ * @since 1.9.0
574
+ *
575
+ * @param array $args {
576
+ *
577
+ * @type string $content Html to display as button / link content.
578
+ * @type string $event String to identify dismiss event. The action triggered will be
579
+ * "{$manager_id}_user_notice_dismissed_{$notice_id}_{$event}" and the dismissing
580
+ * user id is passed as an argument to the action. Leave unset for no specific action to be fired.
581
+ * @type array $classes Array of classes for the button. Default is array( pixcloud_anm-link ) which styles as a link.
582
+ * }
583
+ * @return string
584
+ */
585
+ public function dismiss_event_button( array $args ) {
586
+
587
+ $defaults = array(
588
+ 'content' => 'Undefined',
589
+ 'event' => '',
590
+ 'classes' => array( 'pixcloud_anm-link' ),
591
+ );
592
+
593
+ $args = wp_parse_args( $args, $defaults );
594
+
595
+ $classes = array( 'pixcloud_anm-dismiss', 'pixcloud_anm-event' );
596
+ if ( ! empty( $args['classes'] ) && is_array( $args['classes'] ) ) {
597
+ $classes = array_merge( $args['classes'], $classes );
598
+ }
599
+ $classes = implode( ' ', $classes );
600
+
601
+ // Add button with value of event.
602
+ return '<button type="submit" class="' . $classes . '" name="pixcloud_anm-event" value="' . esc_attr( $args['event'] ) . '">' . $args['content'] . '</button>';
603
+
604
+ }
605
+
606
+ /**
607
+ * Display user notices.
608
+ *
609
+ * @since 1.9.0
610
+ *
611
+ * @hooked admin_notices
612
+ */
613
+ public function display_notices() {
614
+
615
+ $screen_id = $this->get_current_screen_id();
616
+ $user_id = get_current_user_id();
617
+
618
+ $this->display_opt_out_notices( $screen_id, $user_id );
619
+ }
620
+
621
+ /**
622
+ * Display opt out notices.
623
+ *
624
+ * @access protected
625
+ * @since 1.9.0
626
+ *
627
+ * @param int $screen_id Current screen id (0 if not known).
628
+ * @param int $user_id Current user id.
629
+ */
630
+ protected function display_opt_out_notices( $screen_id, $user_id ) {
631
+
632
+ global $post;
633
+ $post_id = is_object( $post ) ? $post->ID : 0;
634
+
635
+ $notices = $this->notifications;
636
+
637
+ if ( ! empty( $notices ) ) {
638
+ $user_notifications_details = get_user_meta( $user_id, $this->manager_id . '_notifications', true );
639
+
640
+ foreach ( $notices as $notice_id => $notice ) {
641
+
642
+ $notice['id'] = $notice_id;
643
+
644
+ // If screen ids have been specified, check whether notice should be displayed
645
+ if ( ! empty( $notice['screen_ids'] ) ) {
646
+ if ( ! in_array( $screen_id, $notice['screen_ids'] ) ) {
647
+ continue;
648
+ }
649
+ }
650
+
651
+ // If post ids have been specified, check whether notice should be displayed
652
+ if ( ! empty( $notice['post_ids'] ) ) {
653
+ if ( ! in_array( $post_id, $notice['post_ids'] ) ) {
654
+ continue;
655
+ }
656
+ }
657
+
658
+ // If user ids or roles have been specified, check whether notice should be displayed to the current user.
659
+ if ( ! empty( $notice['user_ids'] ) ) {
660
+ // Convert roles to user ids
661
+ $user_ids = $this->parse_user_roles( $notice['user_ids'] );
662
+
663
+ if ( ! in_array( $user_id, $user_ids ) ) {
664
+ continue;
665
+ }
666
+ }
667
+
668
+ // Check whether user has already dismissed this notice
669
+ if ( ! empty( $user_notifications_details ) ) {
670
+ if ( array_key_exists( $notice['id'], $user_notifications_details ) && ! empty( $user_notifications_details[ $notice['id'] ]['dismissed'] ) ) {
671
+ continue;
672
+ }
673
+ }
674
+
675
+ // Check the start and end dates.
676
+ if ( ! empty( $notice['start_date'] ) && time() < strtotime( $notice['start_date'] ) ) {
677
+ continue;
678
+ }
679
+ if ( ! empty( $notice['end_date'] ) && strtotime( $notice['end_date'] ) < time() ) {
680
+ continue;
681
+ }
682
+
683
+ $display = true;
684
+ // If the notice has local conditions, process them.
685
+ if ( ! empty( $notice['local_conditions'] ) ) {
686
+ $display = Pixcloud_Notification_Conditions::process( $notice['local_conditions'] );
687
+ }
688
+
689
+ // Display the notice with option to filter display.
690
+ if ( true === apply_filters( $this->manager_id . '_display_notice', $display, $notice ) ) {
691
+
692
+ if ( 'custom' !== $notice['type'] ) {
693
+ $this->display_standard_notice( $notice );
694
+ } else {
695
+ $this->display_custom_notice( $notice );
696
+ }
697
+
698
+ // Register the notice display
699
+ $this->register_user_notice_display( $notice['id'], $user_id );
700
+
701
+ // Remove notice once viewed if this is a one-time notice.
702
+ if ( ! $notice['persistent'] ) {
703
+ $this->dismiss_notice_for_user( $notice['id'], $user_id );
704
+ }
705
+ }
706
+ }
707
+ }
708
+ }
709
+
710
+ /**
711
+ * Display standard notice.
712
+ *
713
+ * @access protected
714
+ * @since 1.9.0
715
+ *
716
+ * @param array $notice array of notice parameters.
717
+ */
718
+ protected function display_standard_notice( $notice ) {
719
+
720
+ // Add classes to the notice container as needed.
721
+ $container_classes = array(
722
+ 'notice',
723
+ 'notice-' . esc_attr( $notice['type'] ),
724
+ );
725
+ if ( $notice['dismissable'] ) {
726
+ $container_classes[] = 'is-dismissible';
727
+ }
728
+ if ( $notice['persistent'] && $notice['dismissable'] ) {
729
+ $container_classes[] = 'notice-manager-ajax';
730
+ }
731
+
732
+ // Parse any content tags.
733
+ $notice['message'] = self::parse_content_tags( $notice['message'] );
734
+
735
+ // Convert newlines to <br>s.
736
+ $notice['message'] = nl2br( $notice['message'] );
737
+
738
+ if ( ! empty( $notice['wrap_tag'] ) ) {
739
+ $notice['message'] = '<' . $notice['wrap_tag'] . '>' . $notice['message'] . '</' . $notice['wrap_tag'] . '>';
740
+ }
741
+
742
+ // Display the notice markup.
743
+ ?>
744
+ <div id="<?php echo esc_attr( $this->manager_id . '-' . $notice['id'] ); ?>" class="<?php echo implode( ' ', $container_classes ); ?>">
745
+ <form class="pixcloud_anm-form"
746
+ action="<?php echo admin_url( 'admin-ajax.php?action=' . $this->manager_id . '_dismiss_admin_notice' ); ?>"
747
+ method="post">
748
+ <?php if ( in_array( 'notice-manager-ajax', $container_classes ) ) { ?>
749
+ <input type="hidden" class="pixcloud_anm-id" value="<?php echo esc_attr( $this->manager_id ); ?>"/>
750
+ <input type="hidden" class="pixcloud_anm-notice-id" name="noticeID"
751
+ value="<?php echo esc_attr( $this->manager_id . '-' . $notice['id'] ); ?>"/>
752
+ <noscript><input type="hidden" name="pixcloud_anm-no-js" value="1"/></noscript>
753
+ <?php wp_nonce_field( $this->manager_id . '_dismiss_admin_notice', 'nonce-pixcloud_anm-' . $this->manager_id . '-' . $notice['id'] );
754
+ }
755
+ if ( $notice['no_js_dismissable'] ) {
756
+ ?>
757
+ <noscript>
758
+ <table>
759
+ <tr>
760
+ <td style="width: 100%">
761
+ </noscript><?php
762
+ }
763
+
764
+ echo $notice['message'];
765
+
766
+ if ( $notice['no_js_dismissable'] ) {
767
+ ?>
768
+ <noscript>
769
+ </td>
770
+ <td>
771
+ <button type="submit" class="notice-dismiss"><span class="screen-reader-text"><?php esc_html_e( 'Dismiss this notice.', 'customify' ); ?></span>
772
+ </button>
773
+ </td>
774
+ </tr></table>
775
+ </noscript>
776
+ <?php } ?>
777
+ </form>
778
+ </div>
779
+ <?php
780
+ }
781
+
782
+ /**
783
+ * Display custom notice.
784
+ *
785
+ * @access protected
786
+ * @since 1.9.0
787
+ *
788
+ * @param array $notice array of notice parameters.
789
+ */
790
+ protected function display_custom_notice( $notice ) {
791
+
792
+ // Add classes to the notice container as needed.
793
+ $container_classes = array(
794
+ 'notice', // this class is needed by WordPress to properly identify notices and move/enhance them with JS.
795
+ );
796
+
797
+ // Merge with the custom classes received.
798
+ if ( ! empty( $notice['container_classes' ] ) || is_array( $notice['container_classes'] ) ) {
799
+ $container_classes = array_merge( $container_classes, $notice['container_classes'] );
800
+ }
801
+
802
+ if ( $notice['dismissable'] ) {
803
+ $container_classes[] = 'is-dismissible';
804
+ }
805
+ if ( $notice['persistent'] && $notice['dismissable'] ) {
806
+ $container_classes[] = 'notice-manager-ajax';
807
+ }
808
+
809
+ // Standardize the message since we can receive the markup directly or a complex data structure with markup, CSS, and so on.
810
+ if ( is_string( $notice['message'] ) ) {
811
+ $notice['message'] = array(
812
+ 'markup' => $notice['message'],
813
+ );
814
+ }
815
+
816
+ // Bail if we don't have any markup.
817
+ if ( empty( $notice['message']['markup'] ) ) {
818
+ return;
819
+ }
820
+
821
+ // Parse any content tags.
822
+ $notice['message']['markup'] = self::parse_content_tags( $notice['message']['markup'] );
823
+
824
+ // Output the custom CSS.
825
+ if ( ! empty( $notice['message']['css'] ) ) { ?>
826
+ <style type="text/css">
827
+ <?php echo $notice['message']['css']; ?>
828
+ </style>
829
+ <?php }
830
+
831
+ // Display the notice markup.
832
+ ?>
833
+ <div id="<?php echo esc_attr( $this->manager_id . '-' . $notice['id'] ); ?>" class="<?php echo implode( ' ', $container_classes ); ?>">
834
+ <form class="pixcloud_anm-form"
835
+ action="<?php echo admin_url( 'admin-ajax.php?action=' . $this->manager_id . '_dismiss_admin_notice' ); ?>"
836
+ method="post">
837
+ <?php if ( in_array( 'notice-manager-ajax', $container_classes ) ) { ?>
838
+ <input type="hidden" class="pixcloud_anm-id" value="<?php echo esc_attr( $this->manager_id ); ?>"/>
839
+ <input type="hidden" class="pixcloud_anm-notice-id" name="noticeID"
840
+ value="<?php echo esc_attr( $this->manager_id . '-' . $notice['id'] ); ?>"/>
841
+ <noscript><input type="hidden" name="pixcloud_anm-no-js" value="1"/></noscript>
842
+ <?php wp_nonce_field( $this->manager_id . '_dismiss_admin_notice', 'nonce-pixcloud_anm-' . $this->manager_id . '-' . $notice['id'] );
843
+ }
844
+ if ( $notice['no_js_dismissable'] ) {
845
+ ?>
846
+ <noscript>
847
+ <table>
848
+ <tr>
849
+ <td style="width: 100%">
850
+ </noscript><?php
851
+ }
852
+
853
+ echo $notice['message']['markup'];
854
+
855
+ if ( $notice['no_js_dismissable'] ) {
856
+ ?>
857
+ <noscript>
858
+ </td>
859
+ <td>
860
+ <button type="submit" class="notice-dismiss"><span class="screen-reader-text"><?php esc_html_e( 'Dismiss this notice.', 'customify' ); ?></span></button>
861
+ </td>
862
+ </tr></table>
863
+ </noscript>
864
+ <?php } ?>
865
+ </form>
866
+ </div>
867
+ <?php
868
+ }
869
+
870
+ /**
871
+ * Replace any content tags present in the content.
872
+ *
873
+ * @param string $content
874
+ *
875
+ * @return string
876
+ */
877
+ protected function parse_content_tags( $content ) {
878
+ $original_content = $content;
879
+
880
+ // Allow others to alter the content before we do our work
881
+ $content = apply_filters( 'pixcloud_notifications_before_parse_content_tags', $content );
882
+
883
+ // Now we will replace all the supported tags with their value
884
+ // %year%
885
+ $content = str_replace( '%year%', date( 'Y' ), $content );
886
+
887
+ // %site-title% or %site_title%
888
+ $content = str_replace( '%site-title%', get_bloginfo( 'name' ), $content );
889
+ $content = str_replace( '%site_title%', get_bloginfo( 'name' ), $content );
890
+
891
+ // Handle the current user tags.
892
+ if ( false !== strpos( $content, '%user_first_name%' ) ||
893
+ false !== strpos( $content, '%user_last_name%' ) ||
894
+ false !== strpos( $content, '%user_nickname%' ) ||
895
+ false !== strpos( $content, '%user_display_name%' ) ) {
896
+ $user = wp_get_current_user();
897
+
898
+ if ( ! empty( $user ) && ! is_wp_error( $user ) ) {
899
+ // %first_name%
900
+ if ( ! empty( $user->first_name ) ) {
901
+ $content = str_replace( '%user_first_name%', $user->first_name, $content );
902
+ } else {
903
+ // Fallback to display_name.
904
+ $content = str_replace( '%user_first_name%', $user->display_name, $content );
905
+ }
906
+ // %last_name%
907
+ $content = str_replace( '%user_last_name%', $user->last_name, $content );
908
+ // %display_name%
909
+ $content = str_replace( '%user_nickname%', $user->display_name, $content );
910
+ // %nickname%
911
+ $content = str_replace( '%user_display_name%', $user->nickname, $content );
912
+ }
913
+ }
914
+
915
+ // %active_theme%
916
+ $content = str_replace( '%active_theme%', Pixcloud_Notification_Conditions::get_active_theme_name(), $content );
917
+
918
+ // %customify_version%
919
+ $content = str_replace( '%customify_version%', Pixcloud_Notification_Conditions::get_customify_version(), $content );
920
+
921
+ // %style_manager_version%
922
+ $content = str_replace( '%style_manager_version%', Pixcloud_Notification_Conditions::get_style_manager_version(), $content );
923
+
924
+ // %current_color_palette%
925
+ $content = str_replace( '%current_color_palette%', Pixcloud_Notification_Conditions::get_current_color_palette_label(), $content );
926
+
927
+ /*
928
+ * URLs.
929
+ */
930
+ // %home_url%
931
+ $content = str_replace( '%home_url%', home_url(), $content );
932
+
933
+ // %customizer_url%
934
+ $content = str_replace( '%customizer_url%', wp_customize_url(), $content );
935
+ // %customizer_style_manager_url%
936
+ $section_link = add_query_arg( array( 'autofocus[panel]' => 'style_manager_panel' ), admin_url( 'customize.php' ) );
937
+ $content = str_replace( '%customizer_style_manager_url%', $section_link, $content );
938
+ // %customizer_style_manager_colors_url%
939
+ $section_link = add_query_arg( array( 'autofocus[section]' => 'sm_color_palettes_section' ), admin_url( 'customize.php' ) );
940
+ $content = str_replace( '%customizer_style_manager_colors_url%', $section_link, $content );
941
+ // %customizer_style_manager_fonts_url%
942
+ $section_link = add_query_arg( array( 'autofocus[section]' => 'sm_font_palettes_section' ), admin_url( 'customize.php' ) );
943
+ $content = str_replace( '%customizer_style_manager_fonts_url%', $section_link, $content );
944
+ // %customizer_theme_options_url%
945
+ $section_link = add_query_arg( array( 'autofocus[panel]' => 'theme_options_panel' ), admin_url( 'customize.php' ) );
946
+ $content = str_replace( '%customizer_theme_options_url%', $section_link, $content );
947
+ // %customizer_menus_url%
948
+ $section_link = add_query_arg( array( 'autofocus[panel]' => 'nav_menus' ), admin_url( 'customize.php' ) );
949
+ $content = str_replace( '%customizer_menus_url%', $section_link, $content );
950
+ // %customizer_widgets_url%
951
+ $section_link = add_query_arg( array( 'autofocus[panel]' => 'widgets' ), admin_url( 'customize.php' ) );
952
+ $content = str_replace( '%customizer_widgets_url%', $section_link, $content );
953
+ // %customizer_homepage_settings_url%
954
+ $section_link = add_query_arg( array( 'autofocus[section]' => 'static_front_page' ), admin_url( 'customize.php' ) );
955
+ $content = str_replace( '%customizer_homepage_settings_url%', $section_link, $content );
956
+ // %customizer_site_identity_url%
957
+ $section_link = add_query_arg( array( 'autofocus[section]' => 'publish_settings' ), admin_url( 'customize.php' ) );
958
+ $content = str_replace( '%customizer_site_identity_url%', $section_link, $content );
959
+
960
+ // %pixelgrade_care_dashboard_url%
961
+ $content = str_replace( '%pixelgrade_care_dashboard_url%', admin_url( 'admin.php?page=pixelgrade_care' ), $content );
962
+ // %pixelgrade_care_themes_url%
963
+ $content = str_replace( '%pixelgrade_care_themes_url%', admin_url( 'admin.php?page=pixelgrade_themes' ), $content );
964
+
965
+ // Allow others to alter the content after we did our work
966
+ return apply_filters( 'pixcloud_notifications_after_parse_content_tags', $content, $original_content );
967
+ }
968
+
969
+ /**
970
+ * Process ajax call to dismiss notice.
971
+ *
972
+ * @since 1.9.0
973
+ */
974
+ public function dismiss_notice() {
975
+
976
+ if ( isset( $_POST['noticeID'] ) ) {
977
+
978
+ $notice_id = sanitize_text_field( $_POST['noticeID'] );
979
+
980
+ // Check nonce.
981
+ check_ajax_referer( $this->manager_id . '_dismiss_admin_notice', 'nonce-pixcloud_anm-' . $notice_id );
982
+
983
+ // Sanitize message ID after stripping off the '[manager_id]-'.
984
+ $notice_id = str_replace( $this->manager_id . '-', '', sanitize_text_field( $_POST['noticeID'] ) );
985
+
986
+ // Get notice info.
987
+ if ( $user = wp_get_current_user() ) {
988
+
989
+ // Get event if there was one.
990
+ $event = isset( $_POST['pixcloud_anm-event'] ) ? sanitize_text_field( $_POST['pixcloud_anm-event'] ) : false;
991
+
992
+ if ( ! empty( $this->notifications[ $notice_id ] ) && ! empty( $this->notifications[ $notice_id ]['dismissable'] ) ) {
993
+
994
+ if ( ! empty( $this->notifications[ $notice_id ]['dismiss_for_all_users'] ) ) {
995
+ // Dismiss for all users
996
+ $this->dismiss_notice_for_all_users( $notice_id, $this->notifications[ $notice_id ], $event );
997
+ } else {
998
+ // Dismiss notice just for the current user.
999
+ $this->dismiss_notice_for_user( $notice_id, $user->ID, $event );
1000
+ }
1001
+
1002
+ // Redirect if this is not an ajax request.
1003
+ if ( isset( $_POST['pixcloud_anm-no-js'] ) ) {
1004
+
1005
+ // If a redirect has been set, use it.
1006
+ if ( isset( $_POST['pixcloud_anm-redirect'] ) ) {
1007
+ if ( ! empty( $_POST['pixcloud_anm-redirect'] ) ) {
1008
+ wp_redirect( $_POST['pixcloud_anm-redirect'] );
1009
+ exit();
1010
+ }
1011
+ }
1012
+
1013
+ // If not redirected, go back to where we came from.
1014
+ wp_safe_redirect( wp_get_referer() );
1015
+ exit();
1016
+ }
1017
+ }
1018
+ }
1019
+ }
1020
+
1021
+ wp_die();
1022
+
1023
+ }
1024
+
1025
+ /**
1026
+ * Add dismissal to all users for a notice.
1027
+ *
1028
+ * @access protected
1029
+ * @since 1.9.0
1030
+ *
1031
+ * @param string $notice_id Unique ID of message.
1032
+ * @param array $notice The notice details.
1033
+ * @param string|false $event
1034
+ *
1035
+ * @return bool $dismissed True if notice was dismissed successfully.
1036
+ */
1037
+ protected function dismiss_notice_for_all_users( $notice_id, $notice, $event = false ) {
1038
+
1039
+ $dismissed = true;
1040
+
1041
+ // If user ids have been specified, check whether notice should be displayed to this user
1042
+ if ( ! empty( $notice['user_ids'] ) ) {
1043
+ // Convert roles to user ids
1044
+ $user_ids = $this->parse_user_roles( $notice['user_ids'] );
1045
+ } else {
1046
+ $user_ids = get_users( array( 'fields' => 'id' ) );
1047
+ }
1048
+
1049
+ if ( ! empty( $user_ids ) && is_array( $user_ids ) ) {
1050
+ foreach ( $user_ids as $user_id ) {
1051
+ // We are only interested in failures. One failure is enough to return false overall.
1052
+ $dismissed = $this->dismiss_notice_for_user( $notice_id, $user_id, $event ) ? $dismissed : false;
1053
+ }
1054
+ }
1055
+
1056
+ return $dismissed;
1057
+ }
1058
+
1059
+ /**
1060
+ * Add dismissal to a user for a notice.
1061
+ *
1062
+ * @access protected
1063
+ * @since 1.9.0
1064
+ *
1065
+ * @param string $notice_id Unique ID of message.
1066
+ * @param int $user_id User id for whom message should be dismissed.
1067
+ * @param string|false $event
1068
+ *
1069
+ * @return bool $dismissed True if notice was dismissed successfully.
1070
+ */
1071
+ protected function dismiss_notice_for_user( $notice_id, $user_id, $event = false ) {
1072
+
1073
+ $dismissed = false;
1074
+ if ( ! $notices = get_user_meta( $user_id, $this->manager_id . '_notifications', true ) ) {
1075
+ $notices = array();
1076
+ }
1077
+
1078
+ if ( ! array_key_exists( $notice_id, $notices ) ) {
1079
+ $notices[ $notice_id ] = array(
1080
+ 'dismissed' => true,
1081
+ );
1082
+ $dismissed = update_user_meta( $user_id, $this->manager_id . '_notifications', $notices );
1083
+ } elseif ( empty( $notices[ $notice_id ]['dismissed'] ) ) {
1084
+ $notices[ $notice_id ]['dismissed'] = true;
1085
+ $dismissed = update_user_meta( $user_id, $this->manager_id . '_notifications', $notices );
1086
+ }
1087
+
1088
+ // Allow for other actions on dismissal of notice.
1089
+ if ( $dismissed ) {
1090
+ do_action( $this->manager_id . '_user_notice_dismissed_' . $notice_id, $user_id );
1091
+ if ( $event ) {
1092
+ do_action( $this->manager_id . '_user_notice_dismissed_' . $notice_id . '_' . $event, $user_id );
1093
+ }
1094
+ }
1095
+
1096
+ return $dismissed;
1097
+
1098
+ }
1099
+
1100
+ /**
1101
+ * Register display of a notice for a user.
1102
+ *
1103
+ * @access protected
1104
+ * @since 1.9.0
1105
+ *
1106
+ * @param string $notice_id Unique ID of notice.
1107
+ * @param int $user_id User id for whom the notice should be registered as displayed.
1108
+ *
1109
+ * @return bool $displayed True if notice was registered as displayed successfully.
1110
+ */
1111
+ protected function register_user_notice_display( $notice_id, $user_id ) {
1112
+
1113
+ $registered = false;
1114
+ if ( ! $notices = get_user_meta( $user_id, $this->manager_id . '_notifications', true ) ) {
1115
+ $notices = array();
1116
+ }
1117
+
1118
+ if ( ! array_key_exists( $notice_id, $notices ) ) {
1119
+ $notices[ $notice_id ] = array(
1120
+ 'displayed' => true,
1121
+ );
1122
+ $registered = update_user_meta( $user_id, $this->manager_id . '_notifications', $notices );
1123
+ } elseif ( empty( $notices[ $notice_id ]['displayed'] ) ) {
1124
+ $notices[ $notice_id ]['displayed'] = true;
1125
+ $registered = update_user_meta( $user_id, $this->manager_id . '_notifications', $notices );
1126
+ }
1127
+
1128
+ // Allow for other actions on register display of notice.
1129
+ if ( $registered ) {
1130
+ do_action( $this->manager_id . '_user_notice_displayed_' . $notice_id, $user_id );
1131
+ }
1132
+
1133
+ return $registered;
1134
+
1135
+ }
1136
+
1137
+ /**
1138
+ * Get all user ids with notifications data.
1139
+ *
1140
+ * @since 1.9.0
1141
+ *
1142
+ * @access protected
1143
+ * @return array Array of user ids.
1144
+ */
1145
+ protected function get_users_with_notifications_data() {
1146
+
1147
+ $args = array(
1148
+ 'meta_query' => array(
1149
+ array(
1150
+ 'key' => $this->manager_id . '_notifications',
1151
+ 'compare' => '!=',
1152
+ 'value' => '',
1153
+ ),
1154
+ ),
1155
+ 'fields' => 'ID',
1156
+ );
1157
+
1158
+ return get_users( $args );
1159
+
1160
+ }
1161
+
1162
+ /**
1163
+ * Remove notifications data from a user.
1164
+ *
1165
+ * @since 1.9.0
1166
+ *
1167
+ * @param int $user_id User ID from whom to remove dismissals. Set to 0 for all users.
1168
+ * @param array $notice_ids Array of notice ids to remove
1169
+ */
1170
+ public function remove_user_notifications_data( $user_id = 0, array $notice_ids ) {
1171
+
1172
+ if ( ! empty( $notice_ids ) ) {
1173
+
1174
+ if ( $user_notifications_details = get_user_meta( $user_id, $this->manager_id . '_notifications', true ) ) {
1175
+
1176
+ $update = false;
1177
+ foreach ( $notice_ids as $notice_id ) {
1178
+ if ( ! empty( $user_notifications_details[ $notice_id ] ) ) {
1179
+ unset( $user_notifications_details[ $notice_id ] );
1180
+ $update = true;
1181
+ }
1182
+ }
1183
+
1184
+ if ( $update ) {
1185
+ if ( empty( $user_notifications_details ) ) {
1186
+ delete_user_meta( $user_id, $this->manager_id . '_notifications' );
1187
+ } else {
1188
+ update_user_meta( $user_id, $this->manager_id . '_notifications', $user_notifications_details );
1189
+ }
1190
+ return;
1191
+ }
1192
+ }
1193
+ }
1194
+
1195
+ delete_metadata( 'user', $user_id, $this->manager_id . '_notifications', false, true );
1196
+ }
1197
+
1198
+ /**
1199
+ * Remove all notifications data from a user.
1200
+ *
1201
+ * @since 1.9.0
1202
+ *
1203
+ * @param int $user_id User ID from whom to remove dismissals. Set to 0 for all users.
1204
+ */
1205
+ public function remove_user_data( $user_id = 0 ) {
1206
+ delete_metadata( 'user', $user_id, $this->manager_id . '_notifications', false, true );
1207
+ }
1208
+
1209
+ /**
1210
+ * Remove all data from the database. (Can be called on plugin uninstall, for example.)
1211
+ *
1212
+ * @since 1.9.0
1213
+ */
1214
+ public function remove_all_data() {
1215
+ $this->remove_user_data();
1216
+ }
1217
+
1218
+ public function add_data_to_request( $data ) {
1219
+ // Do nothing if data is already there.
1220
+ if ( isset( $data['notifications_data'] ) ) {
1221
+ return $data;
1222
+ }
1223
+
1224
+ $notifications_data = array();
1225
+
1226
+ $user_ids = $this->get_users_with_notifications_data();
1227
+ if ( ! empty( $user_ids ) ) {
1228
+ foreach ( $user_ids as $user_id ) {
1229
+ $user_notifications_data = get_user_meta( $user_id, $this->manager_id . '_notifications', true );
1230
+ if ( ! empty( $user_notifications_data ) ) {
1231
+ foreach ( $user_notifications_data as $id => $user_notification_data ) {
1232
+ // We will send data user agnostic.
1233
+ // We will rely on the fact that displayed and dismissed is boolean (so they can be translated to 0 and 1),
1234
+ // and simply do the sum of them.
1235
+ if ( ! isset( $notifications_data[ $id ] ) ) {
1236
+ $notifications_data[ $id ] = array(
1237
+ 'seen_by_users' => 0,
1238
+ 'dismissed_by_users' => 0,
1239
+ );
1240
+ }
1241
+
1242
+ if ( ! empty( $user_notification_data['displayed'] ) ) {
1243
+ $notifications_data[ $id ]['seen_by_users'] ++;
1244
+ }
1245
+
1246
+ if ( ! empty( $user_notification_data['dismissed'] ) ) {
1247
+ $notifications_data[ $id ]['dismissed_by_users'] ++;
1248
+ }
1249
+ }
1250
+ }
1251
+ }
1252
+ }
1253
+
1254
+ if ( ! empty( $notifications_data ) ) {
1255
+ $data['notifications_data'] = $notifications_data;
1256
+ }
1257
+
1258
+ return $data;
1259
+ }
1260
+
1261
+ /**
1262
+ * Main Pixcloud_Admin_Notifications_Manager Instance
1263
+ *
1264
+ * Ensures only one instance of Pixcloud_Admin_Notifications_Manager is loaded or can be loaded.
1265
+ *
1266
+ * @since 1.9.0
1267
+ * @static
1268
+ *
1269
+ * @param array $args The arguments to initialize the notifications manager.
1270
+ * @return Pixcloud_Admin_Notifications_Manager Main Pixcloud_Admin_Notifications_Manager instance
1271
+ */
1272
+ public static function instance( $args = array() ) {
1273
+
1274
+ if ( is_null( self::$_instance ) ) {
1275
+ self::$_instance = new self( $args );
1276
+ }
1277
+ return self::$_instance;
1278
+ } // End instance ()
1279
+
1280
+ /**
1281
+ * Cloning is forbidden.
1282
+ *
1283
+ * @since 1.9.0
1284
+ */
1285
+ public function __clone() {
1286
+
1287
+ _doing_it_wrong( __FUNCTION__,esc_html( __( 'Cheatin&#8217; huh?' ) ), null );
1288
+ } // End __clone ()
1289
+
1290
+ /**
1291
+ * Unserializing instances of this class is forbidden.
1292
+ *
1293
+ * @since 1.9.0
1294
+ */
1295
+ public function __wakeup() {
1296
+
1297
+ _doing_it_wrong( __FUNCTION__, esc_html( __( 'Cheatin&#8217; huh?' ) ), null );
1298
+ } // End __wakeup ()
1299
+
1300
+ }
includes/admin-notifications-manager/class-notification-conditions.php ADDED
@@ -0,0 +1,732 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /**
4
+ *
5
+ * A class to handle the conditions of notifications.
6
+ *
7
+ * These conditions are in the format provided by jQuery QueryBuilder.
8
+ */
9
+ class Pixcloud_Notification_Conditions {
10
+
11
+ protected static $group_relations = array(
12
+ 'AND', 'OR',
13
+ );
14
+
15
+ protected static $active_theme_details = null;
16
+
17
+ /**
18
+ * Process a notification's conditions.
19
+ *
20
+ * @param array $conditions
21
+ *
22
+ * @return bool|mixed
23
+ */
24
+ public static function process( $conditions ) {
25
+ // First check if the conditions are valid.
26
+ // On invalid conditions we return true.
27
+ if ( empty( $conditions['valid'] ) ) {
28
+ return apply_filters( 'pixcloud_notification_conditions_result', true, $conditions );
29
+ }
30
+
31
+ // Process the group. Any top level conditions are expected to be a group, not an individual rule.
32
+ $result = self::process_group( $conditions );
33
+
34
+ return apply_filters( 'pixcloud_notification_conditions_result', $result, $conditions );
35
+ }
36
+
37
+ /**
38
+ * Process and evaluate a notification condition group.
39
+ *
40
+ * @param array $group_conditions
41
+ *
42
+ * @return bool
43
+ */
44
+ public static function process_group( $group_conditions ) {
45
+ // By default we will use the AND relation among group rules or subgroups.
46
+ $group_relation = 'AND';
47
+ if ( ! empty( $group_conditions['condition'] ) && in_array( $group_conditions['condition'], self::$group_relations ) ) {
48
+ $group_relation = $group_conditions['condition'];
49
+ }
50
+
51
+ if ( empty( $group_conditions['rules'] ) || ! is_array( $group_conditions['rules'] ) ) {
52
+ return true;
53
+ }
54
+
55
+ switch ( $group_relation ) {
56
+ case 'AND':
57
+ // By default we assure that the conditions evaluate to true.
58
+ $result = true;
59
+ break;
60
+ case 'OR':
61
+ // By default we assure that the conditions evaluate to false.
62
+ $result = false;
63
+ break;
64
+ default:
65
+ $result = false;
66
+ break;
67
+ }
68
+
69
+ $stop = false;
70
+ foreach ( $group_conditions['rules'] as $rule ) {
71
+ // Determine if it is a simple rule or a subgroup.
72
+ if ( ! empty( $rule['rules'] ) ) {
73
+ $result = self::process_group( $rule );
74
+ } else {
75
+ $result = self::process_rule( $rule );
76
+ }
77
+
78
+ // Now evaluate the rule result according to the group relation.
79
+ switch ( $group_relation ) {
80
+ case 'AND':
81
+ if ( false === $result ) {
82
+ // Stop the evaluation.
83
+ $stop = true;
84
+ }
85
+ break;
86
+ case 'OR':
87
+ if ( true === $result ) {
88
+ // Stop the evaluation.
89
+ $stop = true;
90
+ }
91
+ break;
92
+ default:
93
+ // We should not reach here but just in case.
94
+ $stop = true;
95
+ break;
96
+ }
97
+
98
+ // Stop the rules processing if this is the case.
99
+ if ( true === $stop ) {
100
+ break;
101
+ }
102
+ }
103
+
104
+ return apply_filters( 'pixcloud_notification_conditions_group_result', $result, $group_conditions['rules'], $group_relation, $group_conditions );
105
+ }
106
+
107
+ /**
108
+ * Process and evaluate a notification condition rule.
109
+ *
110
+ * @param array $rule
111
+ *
112
+ * @return bool
113
+ */
114
+ public static function process_rule( $rule ) {
115
+ $result = true;
116
+
117
+ // First validate the rule, just in case. On anything invalid we will return true.
118
+ if ( empty( $rule['id'] ) ) {
119
+ return $result;
120
+ }
121
+ if ( empty( $rule['operator'] ) ) {
122
+ return $result;
123
+ }
124
+
125
+ if ( ! isset( $rule['value'] ) ) {
126
+ $rule['value'] = null;
127
+ }
128
+
129
+ // Now determine the field value (the dynamic part of the rule).
130
+ if ( ! method_exists( __CLASS__, 'get_' . $rule['id'] ) ) {
131
+ return $result;
132
+ }
133
+ $field_value = call_user_func( array( __CLASS__, 'get_' . $rule['id'] ), $rule );
134
+ // Make sure that we work with the provided field type, regardless if it is a single value or a list.
135
+ $field_value = self::convert_value_to_type( $field_value, $rule['type'] );
136
+ $rule['value'] = self::convert_value_to_type( $rule['value'], $rule['type'] );
137
+
138
+ // Before we evaluate the expression, we need to account for the special expressions (e.g. function_exists, class_exists).
139
+ switch ( $rule['id'] ) {
140
+ case 'function_exists':
141
+ // We apply function_exists to each value.
142
+ if ( is_array( $rule['value'] ) ) {
143
+ $rule['value'] = array_map( 'function_exists', $rule['value'] );
144
+ } else {
145
+ $rule['value'] = function_exists( $rule['value'] );
146
+ }
147
+
148
+ // Make sure that the field value is true.
149
+ $field_value = true;
150
+ break;
151
+ case 'class_exists':
152
+ // We apply function_exists to each value.
153
+ if ( is_array( $rule['value'] ) ) {
154
+ $rule['value'] = array_map( 'class_exists', $rule['value'] );
155
+ } else {
156
+ $rule['value'] = class_exists( $rule['value'] );
157
+ }
158
+
159
+ // Make sure that the field value is true.
160
+ $field_value = true;
161
+ break;
162
+ default:
163
+ break;
164
+ }
165
+
166
+ // Now evaluate the expression.
167
+ require_once 'class-notification-logicalexpression.php';
168
+ $result = Pixcloud_Notification_LogicalExpression::evaluate( $field_value, $rule['operator'], $rule['value'] );
169
+
170
+ return apply_filters( 'pixcloud_notification_conditions_rule_result', $result, $field_value, $rule['operator'], $rule['value'], $rule );
171
+ }
172
+
173
+ public static function evaluate_expression( $left, $operator, $right, $rule ) {
174
+
175
+ }
176
+
177
+ /* ========================
178
+ * THE FIELD VALUES GETTERS
179
+ */
180
+
181
+ public static function get_style_manager_is_supported( $rule = null ) {
182
+ if ( class_exists( 'Customify_Style_Manager' ) && Customify_Style_Manager::instance()->is_supported() ) {
183
+ return true;
184
+ }
185
+
186
+ return false;
187
+ }
188
+
189
+ public static function get_style_manager_user_provided_feedback( $rule = null ) {
190
+ if ( class_exists( 'Customify_Style_Manager' ) && Customify_Style_Manager::instance()->user_provided_feedback() ) {
191
+ return true;
192
+ }
193
+
194
+ return false;
195
+ }
196
+
197
+ public static function get_style_manager_user_provided_feedback_days_ago( $rule = null ) {
198
+ $user_provided_feedback = get_option( 'style_manager_user_feedback_provided' );
199
+ if ( empty( $user_provided_feedback ) ) {
200
+ return false;
201
+ }
202
+
203
+ return round( ( time() - $user_provided_feedback ) / DAY_IN_SECONDS );
204
+ }
205
+
206
+ public static function get_current_color_palette_hashid( $rule = null ) {
207
+ if ( class_exists('Customify_Color_Palettes') ) {
208
+ return Customify_Color_Palettes::instance()->get_current_palette();
209
+ }
210
+
211
+ return '';
212
+ }
213
+
214
+ public static function get_current_color_palette_label( $rule = null ) {
215
+ if ( class_exists('Customify_Color_Palettes') ) {
216
+ $color_palette_hashid = self::get_current_color_palette_hashid( $rule );
217
+ $color_palettes = Customify_Color_Palettes::instance()->get_palettes();
218
+ if ( ! empty( $color_palettes[ $color_palette_hashid ] ) ) {
219
+ return $color_palettes[ $color_palette_hashid ]['label'];
220
+ }
221
+ }
222
+
223
+ return '';
224
+ }
225
+
226
+ public static function get_current_color_palette_is_custom( $rule = null ) {
227
+ if ( class_exists('Customify_Color_Palettes') ) {
228
+ return Customify_Color_Palettes::instance()->is_using_custom_palette();
229
+ }
230
+
231
+ return false;
232
+ }
233
+
234
+ public static function get_current_color_palette_is_variation_in_use( $rule = null ) {
235
+ if ( class_exists('Customify_Color_Palettes') ) {
236
+ return Customify_Color_Palettes::instance()->get_current_palette_variation();
237
+ }
238
+
239
+ return false;
240
+ }
241
+
242
+ public static function get_active_theme_slug( $rule = null ) {
243
+ $theme_details = self::get_active_theme_details();
244
+
245
+ if ( ! empty( $theme_details['slug'] ) ) {
246
+ return $theme_details['slug'];
247
+ }
248
+
249
+ return '';
250
+ }
251
+
252
+ public static function get_active_theme_hashid( $rule = null ) {
253
+ $theme_details = self::get_active_theme_details();
254
+
255
+ if ( ! empty( $theme_details['hashid'] ) ) {
256
+ return $theme_details['hashid'];
257
+ }
258
+
259
+ return '';
260
+ }
261
+
262
+ public static function get_active_theme_name( $rule = null ) {
263
+ $theme_details = self::get_active_theme_details();
264
+
265
+ if ( ! empty( $theme_details['name'] ) ) {
266
+ return $theme_details['name'];
267
+ }
268
+
269
+ return '';
270
+ }
271
+
272
+ public static function get_active_theme_author( $rule = null ) {
273
+ $theme_details = self::get_active_theme_details();
274
+
275
+ if ( ! empty( $theme_details['author'] ) ) {
276
+ return $theme_details['author'];
277
+ }
278
+
279
+ return '';
280
+ }
281
+
282
+ public static function get_active_theme_has_wupdates_valid_code( $rule = null ) {
283
+ $theme_details = self::get_active_theme_details();
284
+
285
+ if ( ! empty( $theme_details['wupdates_code_unchanged'] ) ) {
286
+ return true;
287
+ }
288
+
289
+ return false;
290
+ }
291
+
292
+ public static function get_active_theme_has_pixelgrade_license( $rule = null ) {
293
+ $theme_details = self::get_active_theme_details();
294
+
295
+ if ( ! empty( $theme_details['license_hash'] ) ) {
296
+ return true;
297
+ }
298
+
299
+ return false;
300
+ }
301
+
302
+ public static function get_active_theme_pixelgrade_license_status( $rule = null ) {
303
+ $theme_details = self::get_active_theme_details();
304
+
305
+ if ( ! empty( $theme_details['license_status'] ) ) {
306
+ return $theme_details['license_status'];
307
+ }
308
+
309
+ return '';
310
+ }
311
+
312
+ public static function get_active_theme_version( $rule = null ) {
313
+ $theme_details = self::get_active_theme_details();
314
+
315
+ if ( ! empty( $theme_details['version'] ) ) {
316
+ return $theme_details['version'];
317
+ }
318
+
319
+ return '0.0.1';
320
+ }
321
+
322
+ public static function get_customify_version( $rule = null ) {
323
+ if ( function_exists( 'PixCustomifyPlugin' ) ) {
324
+ return PixCustomifyPlugin()->get_version();
325
+ }
326
+
327
+ return false;
328
+ }
329
+
330
+ public static function get_style_manager_version( $rule = null ) {
331
+ if ( function_exists( 'StyleManager_Plugin' ) ) {
332
+ return StyleManager_Plugin()->get_version();
333
+ }
334
+
335
+ return false;
336
+ }
337
+
338
+ public static function get_wp_version( $rule = null ) {
339
+ return get_bloginfo( 'version' );
340
+ }
341
+
342
+ public static function get_php_version( $rule = null ) {
343
+ if ( function_exists( 'phpversion' ) ) {
344
+ return phpversion();
345
+ }
346
+
347
+ return false;
348
+ }
349
+
350
+ public static function get_current_user_role( $rule = null ) {
351
+ $current_user = wp_get_current_user();
352
+
353
+ if ( ! empty( $current_user ) && ! is_wp_error( $current_user ) ) {
354
+ return $current_user->roles;
355
+ }
356
+
357
+ return false;
358
+ }
359
+
360
+ public static function get_current_user_capabilities( $rule = null ) {
361
+ $current_user = wp_get_current_user();
362
+
363
+ if ( ! empty( $current_user ) && ! is_wp_error( $current_user ) ) {
364
+ return $current_user->allcaps;
365
+ }
366
+
367
+ return false;
368
+ }
369
+
370
+ public static function get_site_is_public( $rule = null ) {
371
+ // Local/development url parts to match for
372
+ $devsite_needles = array(
373
+ 'localhost',
374
+ ':8888',
375
+ '.local',
376
+ '.dev',
377
+ ':8082',
378
+ 'staging.',
379
+ '.invalid',
380
+ '.test',
381
+ '.example',
382
+ );
383
+
384
+ if ( self::string_contains_any( get_bloginfo( 'url'), $devsite_needles ) ) {
385
+ return false;
386
+ }
387
+
388
+ return true;
389
+ }
390
+
391
+ public static function get_site_url( $rule = null ) {
392
+ return get_bloginfo( 'url');
393
+ }
394
+
395
+ public static function get_site_is_multisite( $rule = null ) {
396
+ return is_multisite();
397
+ }
398
+
399
+ public static function get_site_number_of_posts( $rule = null ) {
400
+ // Make sure it is an array.
401
+ $post_count = json_decode( json_encode( wp_count_posts( 'post' ) ), true );
402
+ return ! empty( $post_count['publish'] ) ? $post_count['publish'] : 0;
403
+ }
404
+
405
+ public static function get_site_number_of_pages( $rule = null ) {
406
+ // Make sure it is an array.
407
+ $post_count = json_decode( json_encode( wp_count_posts( 'page' ) ), true );
408
+ return ! empty( $post_count['publish'] ) ? $post_count['publish'] : 0;
409
+ }
410
+
411
+ public static function get_current_date( $rule = null ) {
412
+ return date('Y/m/d');
413
+ }
414
+
415
+ // This is special.
416
+ public static function get_class_exists( $rule = null ) {
417
+ return true;
418
+ }
419
+
420
+ // This is special.
421
+ public static function get_function_exists( $rule = null ) {
422
+ return true;
423
+ }
424
+
425
+ public static function get_wp_debug_active( $rule = null ) {
426
+ return defined( 'WP_DEBUG') && true === WP_DEBUG;
427
+ }
428
+
429
+ public static function get_pixelgrade_dev_mode_active( $rule = null ) {
430
+ return defined( 'PIXELGRADE_CARE__DEV_MODE') && true === PIXELGRADE_CARE__DEV_MODE;
431
+ }
432
+
433
+ public static function get_customify_dev_force_defaults_active( $rule = null ) {
434
+ return defined( 'CUSTOMIFY_DEV_FORCE_DEFAULTS') && true === CUSTOMIFY_DEV_FORCE_DEFAULTS;
435
+ }
436
+
437
+ public static function get_sm_dev_customizer_force_defaults_active( $rule = null ) {
438
+ return defined( 'SM_DEV_CUSTOMIZER_FORCE_DEFAULTS') && true === SM_DEV_CUSTOMIZER_FORCE_DEFAULTS;
439
+ }
440
+
441
+ /* =======
442
+ * HELPERS
443
+ */
444
+
445
+ /**
446
+ * @param $value
447
+ * @param $type
448
+ *
449
+ * @return false|float|int|string
450
+ */
451
+ public static function convert_value_to_type( $value, $type ) {
452
+ if ( null === $value ) {
453
+ return $value;
454
+ }
455
+
456
+ // Make sure we are not dealing with stdClass.
457
+ if ( $value instanceof stdClass ) {
458
+ $value = json_decode( json_encode( $value ), true );
459
+ }
460
+
461
+ if ( ! empty( $type ) ) {
462
+ switch ( $type ) {
463
+ case 'integer':
464
+ if ( is_array( $value ) ) {
465
+ $value = array_map( 'intval', $value );
466
+ } else {
467
+ $value = intval( $value );
468
+ }
469
+ break;
470
+ case 'string':
471
+ if ( is_array( $value ) ) {
472
+ $value = array_map( 'strval', $value );
473
+ } else {
474
+ $value = strval( $value );
475
+ }
476
+ break;
477
+ case 'double':
478
+ if ( is_array( $value ) ) {
479
+ $value = array_map( 'doubleval', $value );
480
+ } else {
481
+ $value = doubleval( $value );
482
+ }
483
+ break;
484
+ case 'date':
485
+ if ( is_array( $value ) ) {
486
+ $value = array_map( 'strtotime', $value );
487
+ $value = array_map( array( __CLASS__, 'dateval' ), $value );
488
+ } else {
489
+ $value = self::dateval( strtotime( $value ) );
490
+ }
491
+ break;
492
+ case 'time':
493
+ if ( is_array( $value ) ) {
494
+ $value = array_map( 'strtotime', $value );
495
+ $value = array_map( array( __CLASS__, 'timeval' ), $value );
496
+ } else {
497
+ $value = self::timeval( strtotime( $value ) );
498
+ }
499
+ break;
500
+ case 'datetime':
501
+ if ( is_array( $value ) ) {
502
+ $value = array_map( 'strtotime', $value );
503
+ $value = array_map( array( __CLASS__, 'datetimeval' ), $value );
504
+ } else {
505
+ $value = self::datetimeval( strtotime( $value ) );
506
+ }
507
+ break;
508
+ case 'boolean':
509
+ if ( is_array( $value ) ) {
510
+ $value = array_map( 'boolval', $value );
511
+ } else {
512
+ $value = boolval( $value );
513
+ }
514
+ break;
515
+ default:
516
+ break;
517
+ }
518
+ }
519
+
520
+ return $value;
521
+ }
522
+
523
+ protected static function dateval( $timestamp ) {
524
+ return date('Y/m/d', $timestamp );
525
+ }
526
+
527
+ protected static function timeval( $timestamp ) {
528
+ return date('H:i:s', $timestamp );
529
+ }
530
+
531
+ protected static function datetimeval( $timestamp ) {
532
+ return date('Y/m/d H:i:s', $timestamp );
533
+ }
534
+
535
+ /**
536
+ * Grab all the details about the current active theme.
537
+ *
538
+ * @return array
539
+ */
540
+ public static function get_active_theme_details() {
541
+ if ( self::$active_theme_details !== null ) {
542
+ return self::$active_theme_details;
543
+ }
544
+
545
+ $theme_details = array();
546
+
547
+ // Gather Pixelgrade and WUpdates theme details.
548
+ $theme_details['is_pixelgrade_theme'] = self::is_pixelgrade_theme();
549
+ $theme_details['hashid'] = self::get_wupdates_theme_hashid();
550
+ $theme_details['wupdates_code_unchanged'] = self::is_wupdates_code_unchanged();
551
+ $theme_details['license_hash'] = get_theme_mod( 'pixcare_license_hash', false );
552
+ $theme_details['license_status'] = get_theme_mod( 'pixcare_license_status', false );
553
+
554
+ // Gather the rest of the theme details.
555
+ /** @var WP_Theme $theme */
556
+ $theme = wp_get_theme();
557
+ $parent = $theme->parent();
558
+ if ( is_child_theme() && ! empty( $parent ) ) {
559
+ $theme = $parent;
560
+ }
561
+
562
+ // The theme name should be the one from the wupdates array.
563
+ $wupdates_theme_name = self::get_original_theme_name();
564
+ if ( ! empty( $wupdates_theme_name ) ) {
565
+ $theme_details['name'] = $wupdates_theme_name;
566
+ }
567
+ // If for some reason we couldn't get the theme name from the WUpdates code, use the standard theme name.
568
+ if ( empty( $theme_details['name'] ) ) {
569
+ $theme_details['name'] = $theme->get( 'Name' );
570
+ }
571
+
572
+ // The theme slug should be the one from the wupdates array
573
+ $wupdates_theme_slug = self::get_original_theme_slug();
574
+ if ( ! empty( $wupdates_theme_slug ) ) {
575
+ $theme_details['slug'] = $wupdates_theme_slug;
576
+ }
577
+ // If for some reason we couldn't get the theme slug from the WUpdates code, use the standard theme slug.
578
+ if ( empty( $theme_details['slug'] ) ) {
579
+ $theme_details['slug'] = basename( get_template_directory() );
580
+ }
581
+
582
+ $theme_details['uri'] = $theme->get( 'ThemeURI' );
583
+ $theme_details['desc'] = $theme->get( 'Description' );
584
+ $theme_details['author'] = $theme->get( 'Author' );
585
+ $theme_details['version'] = $theme->get( 'Version' );
586
+
587
+ $theme_details['is_child'] = is_child_theme();
588
+ $theme_details['template'] = $theme->get_template();
589
+
590
+ self::$active_theme_details = $theme_details;
591
+
592
+ return $theme_details;
593
+ }
594
+
595
+ /**
596
+ * Determine if the current theme is one of ours.
597
+ *
598
+ * @return bool
599
+ */
600
+ public static function is_pixelgrade_theme() {
601
+ // Get the id of the current theme
602
+ $wupdates_ids = apply_filters( 'wupdates_gather_ids', array() );
603
+ $slug = basename( get_template_directory() );
604
+ // If we have the WUpdates information tied to the current theme slug, then we are good
605
+ if ( isset( $wupdates_ids[ $slug ] ) ) {
606
+ return true;
607
+ }
608
+
609
+ // Next we will test for the author in the theme header
610
+ $theme = wp_get_theme();
611
+ $theme_author = $theme->get( 'Author' );
612
+ if ( ! empty( $theme_author ) && strtolower( $theme_author ) == 'pixelgrade' ) {
613
+ return true;
614
+ }
615
+
616
+ return false;
617
+ }
618
+
619
+ /**
620
+ * Checks if the wupdates_gather_ids code has been tempered with.
621
+ *
622
+ * @return bool
623
+ */
624
+ public static function is_wupdates_code_unchanged() {
625
+ // Get the id of the current theme
626
+ $wupdates_ids = apply_filters( 'wupdates_gather_ids', array() );
627
+ $slug = basename( get_template_directory() );
628
+ // If the user hasn't got any pixelgrade themes - return true. They don't need this filter
629
+ if ( ! self::has_pixelgrade_theme() ) {
630
+ return true;
631
+ }
632
+
633
+ // Check if the wupdates_ids array is missing either of this properties
634
+ if ( ! isset( $wupdates_ids[ $slug ] ) || ! isset( $wupdates_ids[ $slug ]['name'] ) || ! isset( $wupdates_ids[ $slug ]['slug'] ) || ! isset( $wupdates_ids[ $slug ]['id'] ) || ! isset( $wupdates_ids[ $slug ]['type'] ) || ! isset( $wupdates_ids[ $slug ]['digest'] ) ) {
635
+ return false;
636
+ }
637
+ // Create the md5 hash from the properties of wupdates_ids and compare it to the digest from that array
638
+ $md5 = md5( 'name-' . $wupdates_ids[ $slug ]['name'] . ';slug-' . $wupdates_ids[ $slug ]['slug'] . ';id-' . $wupdates_ids[ $slug ]['id'] . ';type-' . $wupdates_ids[ $slug ]['type'] );
639
+ // the md5 hash should be the same one as the digest hash
640
+ if ( $md5 !== $wupdates_ids[ $slug ]['digest'] ) {
641
+ return false;
642
+ }
643
+ return true;
644
+ }
645
+
646
+ /**
647
+ * Determine if there are any Pixelgrade themes currently installed.
648
+ *
649
+ * @return bool
650
+ */
651
+ public static function has_pixelgrade_theme() {
652
+ $themes = wp_get_themes();
653
+ // Loop through the themes.
654
+ // If we find a theme from pixelgrade return true.
655
+ /** @var WP_Theme $theme */
656
+ foreach ( $themes as $theme ) {
657
+ $theme_author = $theme->get( 'Author' );
658
+
659
+ if ( ! empty( $theme_author ) && strtolower( $theme_author ) == 'pixelgrade' ) {
660
+ return true;
661
+ }
662
+ }
663
+
664
+ // No themes from pixelgrade found, return false.
665
+ return false;
666
+ }
667
+
668
+ /**
669
+ * Get the current theme original name from the WUpdates code.
670
+ *
671
+ * @return string
672
+ */
673
+ public static function get_original_theme_name() {
674
+ // Get the id of the current theme
675
+ $wupdates_ids = apply_filters( 'wupdates_gather_ids', array() );
676
+ $slug = basename( get_template_directory() );
677
+ if ( ! isset( $wupdates_ids[ $slug ] ) || ! isset( $wupdates_ids[ $slug ]['name'] ) ) {
678
+ return ucfirst( $slug );
679
+ }
680
+ return $wupdates_ids[ $slug ]['name'];
681
+ }
682
+
683
+ /**
684
+ * Get the current theme original slug from the WUpdates code.
685
+ *
686
+ * @return string
687
+ */
688
+ public static function get_original_theme_slug() {
689
+ // Get the id of the current theme
690
+ $wupdates_ids = apply_filters( 'wupdates_gather_ids', array() );
691
+ $slug = basename( get_template_directory() );
692
+ if ( ! isset( $wupdates_ids[ $slug ] ) || ! isset( $wupdates_ids[ $slug ]['slug'] ) ) {
693
+ return $slug;
694
+ }
695
+
696
+ return sanitize_title( $wupdates_ids[ $slug ]['slug'] );
697
+ }
698
+
699
+ /**
700
+ * Get the current theme hashid from the WUpdates code.
701
+ *
702
+ * @return string
703
+ */
704
+ public static function get_wupdates_theme_hashid() {
705
+ // Get the id of the current theme
706
+ $wupdates_ids = apply_filters( 'wupdates_gather_ids', array() );
707
+ $slug = basename( get_template_directory() );
708
+ if ( ! isset( $wupdates_ids[ $slug ] ) || ! isset( $wupdates_ids[ $slug ]['id'] ) ) {
709
+ return false;
710
+ }
711
+
712
+ return $wupdates_ids[ $slug ]['id'];
713
+ }
714
+
715
+ /**
716
+ * Check if the $haystack contains any of the needles.
717
+ *
718
+ * @param string $haystack
719
+ * @param array $needles
720
+ *
721
+ * @return bool
722
+ */
723
+ public static function string_contains_any( $haystack, $needles ) {
724
+ foreach ( $needles as $needle ) {
725
+ if ( false !== strpos( $haystack, $needle ) ) {
726
+ return true;
727
+ }
728
+ }
729
+
730
+ return false;
731
+ }
732
+ }
includes/admin-notifications-manager/class-notification-logicalexpression.php ADDED
@@ -0,0 +1,441 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /**
4
+ *
5
+ * A class to handle logical expression for notification conditions.
6
+ *
7
+ * Please note that we only support scalar data types and lists of these data types (only when the operator operates on lists)!
8
+ */
9
+ class Pixcloud_Notification_LogicalExpression {
10
+
11
+ /**
12
+ * All the supported operators.
13
+ *
14
+ * @var array
15
+ */
16
+ protected static $all_operators = array(
17
+ 'equal', 'not_equal', 'is_empty', 'is_not_empty',
18
+ 'less', 'less_or_equal', 'greater', 'greater_or_equal',
19
+ 'begins_with', 'not_begins_with', 'contains', 'not_contains', 'ends_with', 'not_ends_with',
20
+ 'between', 'not_between',
21
+ 'in', 'not_in', 'any', 'all',
22
+ );
23
+
24
+ /**
25
+ * All the unary operators (i.e. work with only the left operand).
26
+ *
27
+ * @var array
28
+ */
29
+ protected static $unary_operators = array(
30
+ 'is_empty', 'is_not_empty',
31
+ );
32
+
33
+ /**
34
+ * All the binary operators (i.e. work with left and a single right operand).
35
+ *
36
+ * @var array
37
+ */
38
+ protected static $binary_operators = array(
39
+ 'equal', 'not_equal',
40
+ 'less', 'less_or_equal', 'greater', 'greater_or_equal',
41
+ 'begins_with', 'not_begins_with', 'contains', 'not_contains', 'ends_with', 'not_ends_with',
42
+ );
43
+
44
+ /**
45
+ * All the ternary operators (i.e. work with a left operand and two right operands).
46
+ *
47
+ * @var array
48
+ */
49
+ protected static $ternary_operators = array(
50
+ 'between', 'not_between',
51
+ );
52
+
53
+
54
+ /**
55
+ * All the list operators (i.e. work with a (list) left operand and (list) right operand).
56
+ *
57
+ * @var array
58
+ */
59
+ protected static $list_operators = array(
60
+ 'in', 'not_in', 'any', 'all',
61
+ );
62
+
63
+ /**
64
+ * Evaluate a logical expression with one or two operands and an operator.
65
+ *
66
+ * @param mixed $left
67
+ * @param string $operator
68
+ * @param mixed $right Optional.
69
+ *
70
+ * @return bool|null The logical expression result or null on invalid data.
71
+ */
72
+ public static function evaluate( $left, $operator, $right = null ) {
73
+ // Reject unknown operators.
74
+ if ( ! in_array( $operator, self::$all_operators ) ) {
75
+ return null;
76
+ }
77
+ // Now, check the number of values a operator can handle and reject on wrong number.
78
+ if ( in_array( $operator, self::$binary_operators ) && is_array( $right ) && count( $right ) > 1 ) {
79
+ return null;
80
+ }
81
+ if ( in_array( $operator, self::$ternary_operators ) && ( ! is_array( $right ) || count( $right ) != 2 ) ) {
82
+ return null;
83
+ }
84
+ if ( in_array( $operator, self::$list_operators ) && ! is_array( $right ) ) {
85
+ return null;
86
+ }
87
+
88
+ // Reject missing operator evaluation method.
89
+ if ( ! method_exists( __CLASS__, 'evaluate_' . $operator ) ) {
90
+ return null;
91
+ }
92
+
93
+ return (bool) call_user_func( array( __CLASS__, 'evaluate_' . $operator ), $left, $right );
94
+ }
95
+
96
+ /* =============================
97
+ * EVALUATORS FOR EACH OPERATOR.
98
+ */
99
+
100
+ /**
101
+ * Determine if the left operand is equal to the right operand. The comparison is strict type.
102
+ *
103
+ * @param mixed $left
104
+ * @param mixed $right
105
+ *
106
+ * @return bool
107
+ */
108
+ public static function evaluate_equal( $left, $right ) {
109
+ // Sanity check.
110
+ if ( is_array( $right ) ) {
111
+ $right = array_shift( $right );
112
+ }
113
+
114
+ return $left === $right;
115
+ }
116
+
117
+ /**
118
+ * Determine if the left operand is not equal to the right operand. The comparison is strict type.
119
+ *
120
+ * @param mixed $left
121
+ * @param mixed $right
122
+ *
123
+ * @return bool
124
+ */
125
+ public static function evaluate_not_equal( $left, $right ) {
126
+ // Sanity check.
127
+ if ( is_array( $right ) ) {
128
+ $right = array_shift( $right );
129
+ }
130
+
131
+ return $left !== $right;
132
+ }
133
+
134
+ /**
135
+ * Determine if the left operand is empty (the php empty() function is applied).
136
+ *
137
+ * @param mixed $left
138
+ * @param mixed $right Optional. Not used.
139
+ *
140
+ * @return bool
141
+ */
142
+ public static function evaluate_is_empty( $left, $right = null ) {
143
+ return empty( $left );
144
+ }
145
+
146
+ /**
147
+ * Determine if the left operand is not empty (the php empty() function is applied).
148
+ *
149
+ * @param mixed $left
150
+ * @param mixed $right Optional. Not used.
151
+ *
152
+ * @return bool
153
+ */
154
+ public static function evaluate_is_not_empty( $left, $right = null ) {
155
+ return ! empty( $left );
156
+ }
157
+
158
+ /**
159
+ * Determine if the left (numeric) operand less than the right (numeric) operand.
160
+ *
161
+ * @param int|double|float $left
162
+ * @param int|double|float $right
163
+ *
164
+ * @return bool
165
+ */
166
+ public static function evaluate_less( $left, $right ) {
167
+ // Sanity check.
168
+ if ( is_array( $right ) ) {
169
+ $right = array_shift( $right );
170
+ }
171
+
172
+ return $left < $right;
173
+ }
174
+
175
+ /**
176
+ * Determine if the left (numeric) operand less or equal than the right (numeric) operand.
177
+ *
178
+ * @param int|double|float $left
179
+ * @param int|double|float $right
180
+ *
181
+ * @return bool
182
+ */
183
+ public static function evaluate_less_or_equal( $left, $right ) {
184
+ // Sanity check.
185
+ if ( is_array( $right ) ) {
186
+ $right = array_shift( $right );
187
+ }
188
+
189
+ return $left <= $right;
190
+ }
191
+
192
+ /**
193
+ * Determine if the left (numeric) operand greater than the right (numeric) operand.
194
+ *
195
+ * @param int|double|float $left
196
+ * @param int|double|float $right
197
+ *
198
+ * @return bool
199
+ */
200
+ public static function evaluate_greater( $left, $right ) {
201
+ // Sanity check.
202
+ if ( is_array( $right ) ) {
203
+ $right = array_shift( $right );
204
+ }
205
+
206
+ return $left > $right;
207
+ }
208
+
209
+ /**
210
+ * Determine if the left (numeric) operand greater or equal than the right (numeric) operand.
211
+ *
212
+ * @param int|double|float $left
213
+ * @param int|double|float $right
214
+ *
215
+ * @return bool
216
+ */
217
+ public static function evaluate_greater_or_equal( $left, $right ) {
218
+ // Sanity check.
219
+ if ( is_array( $right ) ) {
220
+ $right = array_shift( $right );
221
+ }
222
+
223
+ return $left >= $right;
224
+ }
225
+
226
+ /**
227
+ * Determine if the right (string) operand is at start of the left (string) operand.
228
+ *
229
+ * @param string $left
230
+ * @param string $right
231
+ *
232
+ * @return bool
233
+ */
234
+ public static function evaluate_begins_with( $left, $right ) {
235
+ // Sanity check.
236
+ if ( is_array( $right ) ) {
237
+ $right = array_shift( $right );
238
+ }
239
+
240
+ return 0 === strpos( $left, $right );
241
+ }
242
+
243
+ /**
244
+ * Determine if the right (string) operand is not at start of the left (string) operand.
245
+ *
246
+ * @param string $left
247
+ * @param string $right
248
+ *
249
+ * @return bool
250
+ */
251
+ public static function evaluate_not_begins_with( $left, $right ) {
252
+ // Sanity check.
253
+ if ( is_array( $right ) ) {
254
+ $right = array_shift( $right );
255
+ }
256
+
257
+ return 0 !== strpos( $left, $right );
258
+ }
259
+
260
+ /**
261
+ * Determine if the right (string) operand is part of the left (string) operand.
262
+ *
263
+ * @param string $left
264
+ * @param string $right
265
+ *
266
+ * @return bool
267
+ */
268
+ public static function evaluate_contains( $left, $right ) {
269
+ // Sanity check.
270
+ if ( is_array( $right ) ) {
271
+ $right = array_shift( $right );
272
+ }
273
+
274
+ return false !== strpos( $left, $right );
275
+ }
276
+
277
+ /**
278
+ * Determine if the right (string) operand is not part of the left (string) operand.
279
+ *
280
+ * @param string $left
281
+ * @param string $right
282
+ *
283
+ * @return bool
284
+ */
285
+ public static function evaluate_not_contains( $left, $right ) {
286
+ // Sanity check.
287
+ if ( is_array( $right ) ) {
288
+ $right = array_shift( $right );
289
+ }
290
+
291
+ return false === strpos( $left, $right );
292
+ }
293
+
294
+ /**
295
+ * Determine if the right (string) operand is at end of the left (string) operand.
296
+ *
297
+ * @param string $left
298
+ * @param string $right
299
+ *
300
+ * @return bool
301
+ */
302
+ public static function evaluate_ends_with( $left, $right ) {
303
+ // Sanity check.
304
+ if ( is_array( $right ) ) {
305
+ $right = array_shift( $right );
306
+ }
307
+
308
+ return ( strlen( $left ) - strlen( $right ) ) === strrpos( $left, $right );
309
+ }
310
+
311
+ /**
312
+ * Determine if the right (string) operand is not at end of the left (string) operand.
313
+ *
314
+ * @param string $left
315
+ * @param string $right
316
+ *
317
+ * @return bool
318
+ */
319
+ public static function evaluate_not_ends_with( $left, $right ) {
320
+ // Sanity check.
321
+ if ( is_array( $right ) ) {
322
+ $right = array_shift( $right );
323
+ }
324
+
325
+ return ( strlen( $left ) - strlen( $right ) ) !== strrpos( $left, $right );
326
+ }
327
+
328
+ /**
329
+ * Determine if the left operand is between in the two values in right one.
330
+ *
331
+ * @param $left
332
+ * @param array $right
333
+ *
334
+ * @return bool
335
+ */
336
+ public static function evaluate_between( $left, $right ) {
337
+ // Get the two values
338
+ $small = array_shift( $right );
339
+ $big = array_shift( $right );
340
+
341
+ return ( $small <= $left ) && ( $left <= $big );
342
+ }
343
+
344
+ /**
345
+ * Determine if the left operand is not between in the two values in right one.
346
+ *
347
+ * @param $left
348
+ * @param array $right
349
+ *
350
+ * @return bool
351
+ */
352
+ public static function evaluate_not_between( $left, $right ) {
353
+ // Get the two values
354
+ $small = array_shift( $right );
355
+ $big = array_shift( $right );
356
+
357
+ return ! ( ( $small <= $left ) && ( $left <= $big ) );
358
+ }
359
+
360
+ /**
361
+ * Determine if the left operand is present in the right (list) one.
362
+ *
363
+ * @param $left
364
+ * @param array $right
365
+ *
366
+ * @return bool
367
+ */
368
+ public static function evaluate_in( $left, $right ) {
369
+ // Sanity check.
370
+ if ( ! is_array( $right ) ) {
371
+ $right = array( $right );
372
+ }
373
+
374
+ return ( in_array( $left, $right ) );
375
+ }
376
+
377
+ /**
378
+ * Determine if the left operand is not present in the right (list) one.
379
+ *
380
+ * @param $left
381
+ * @param array $right
382
+ *
383
+ * @return bool
384
+ */
385
+ public static function evaluate_not_in( $left, $right ) {
386
+ // Sanity check.
387
+ if ( ! is_array( $right ) ) {
388
+ $right = array( $right );
389
+ }
390
+
391
+ return ! ( in_array( $left, $right ) );
392
+ }
393
+
394
+ /**
395
+ * Determine if any of the values in the left operand are present in the right one.
396
+ *
397
+ * @param array $left
398
+ * @param array $right
399
+ *
400
+ * @return bool
401
+ */
402
+ public static function evaluate_any( $left, $right ) {
403
+ // Sanity check.
404
+ if ( ! is_array( $left ) ) {
405
+ $left = array( $left );
406
+ }
407
+ if ( ! is_array( $right ) ) {
408
+ $right = array( $right );
409
+ }
410
+
411
+ $intersect = array_intersect( $left, $right );
412
+
413
+ return ! empty( $intersect );
414
+ }
415
+
416
+ /**
417
+ * Determine if all of the values in the left operand are present in the right one.
418
+ *
419
+ * @param array $left
420
+ * @param array $right
421
+ *
422
+ * @return bool
423
+ */
424
+ public static function evaluate_all( $left, $right ) {
425
+ // Sanity check.
426
+ if ( ! is_array( $left ) ) {
427
+ $left = array( $left );
428
+ }
429
+ if ( ! is_array( $right ) ) {
430
+ $right = array( $right );
431
+ }
432
+
433
+ $intersect = array_intersect( $left, $right );
434
+
435
+ return count( $intersect ) === count( $left );
436
+ }
437
+
438
+ /* =======
439
+ * HELPERS
440
+ */
441
+ }
includes/class-customify-color-palettes.php CHANGED
@@ -43,7 +43,7 @@ class Customify_Color_Palettes {
43
  }
44
 
45
  /**
46
- * Initiate our hooks
47
  *
48
  * @since 1.7.4
49
  */
@@ -415,11 +415,11 @@ class Customify_Color_Palettes {
415
  '</defs>' . PHP_EOL .
416
  '</svg>',
417
  ),
418
- 'sm_color_matrix' => array(
419
- 'type' => 'html',
420
- 'setting_id' => 'sm_color_matrix',
421
- 'html' => '<div class="sm_color_matrix"></div>'
422
- ),
423
  // 'sm_dark_color_master_slider' => array(
424
  // 'setting_id' => 'sm_dark_color_master_slider',
425
  // 'type' => 'range',
@@ -736,7 +736,7 @@ class Customify_Color_Palettes {
736
  *
737
  * @return string|false
738
  */
739
- protected function get_current_palette() {
740
  return get_option( 'sm_color_palette', false );
741
  }
742
 
@@ -747,7 +747,7 @@ class Customify_Color_Palettes {
747
  *
748
  * @return string|false
749
  */
750
- protected function get_current_palette_variation() {
751
  return get_option( 'sm_color_palette_variation', false );
752
  }
753
 
@@ -799,7 +799,7 @@ class Customify_Color_Palettes {
799
  *
800
  * @return bool
801
  */
802
- protected function is_using_custom_palette(){
803
  return (bool) get_option( 'sm_is_custom_color_palette', false );
804
  }
805
 
@@ -860,7 +860,7 @@ class Customify_Color_Palettes {
860
  * @since 1.7.4
861
  * @static
862
  *
863
- * @return Customify_Font_Palettes Main Customify_Color_Palettes instance
864
  */
865
  public static function instance() {
866
 
43
  }
44
 
45
  /**
46
+ * Initiate our hooks.
47
  *
48
  * @since 1.7.4
49
  */
415
  '</defs>' . PHP_EOL .
416
  '</svg>',
417
  ),
418
+ // 'sm_color_matrix' => array(
419
+ // 'type' => 'html',
420
+ // 'setting_id' => 'sm_color_matrix',
421
+ // 'html' => '<div class="sm_color_matrix"></div>'
422
+ // ),
423
  // 'sm_dark_color_master_slider' => array(
424
  // 'setting_id' => 'sm_dark_color_master_slider',
425
  // 'type' => 'range',
736
  *
737
  * @return string|false
738
  */
739
+ public function get_current_palette() {
740
  return get_option( 'sm_color_palette', false );
741
  }
742
 
747
  *
748
  * @return string|false
749
  */
750
+ public function get_current_palette_variation() {
751
  return get_option( 'sm_color_palette_variation', false );
752
  }
753
 
799
  *
800
  * @return bool
801
  */
802
+ public function is_using_custom_palette(){
803
  return (bool) get_option( 'sm_is_custom_color_palette', false );
804
  }
805
 
860
  * @since 1.7.4
861
  * @static
862
  *
863
+ * @return Customify_Color_Palettes Main Customify_Color_Palettes instance
864
  */
865
  public static function instance() {
866
 
includes/class-customify-font-palettes.php CHANGED
@@ -1315,7 +1315,7 @@ class Customify_Font_Palettes {
1315
  *
1316
  * @return string|false
1317
  */
1318
- protected function get_current_palette() {
1319
  return get_option( 'sm_font_palette', false );
1320
  }
1321
 
@@ -1326,7 +1326,7 @@ class Customify_Font_Palettes {
1326
  *
1327
  * @return string|false
1328
  */
1329
- protected function get_current_palette_variation() {
1330
  return get_option( 'sm_font_palette_variation', false );
1331
  }
1332
 
@@ -1378,7 +1378,7 @@ class Customify_Font_Palettes {
1378
  *
1379
  * @return bool
1380
  */
1381
- protected function is_using_custom_palette(){
1382
  return (bool) get_option( 'sm_is_custom_font_palette', false );
1383
  }
1384
 
1315
  *
1316
  * @return string|false
1317
  */
1318
+ public function get_current_palette() {
1319
  return get_option( 'sm_font_palette', false );
1320
  }
1321
 
1326
  *
1327
  * @return string|false
1328
  */
1329
+ public function get_current_palette_variation() {
1330
  return get_option( 'sm_font_palette_variation', false );
1331
  }
1332
 
1378
  *
1379
  * @return bool
1380
  */
1381
+ public function is_using_custom_palette(){
1382
  return (bool) get_option( 'sm_is_custom_font_palette', false );
1383
  }
1384
 
includes/class-customify-style-manager.php CHANGED
@@ -55,6 +55,14 @@ class Customify_Style_Manager {
55
  */
56
  protected $font_palettes = null;
57
 
 
 
 
 
 
 
 
 
58
  /**
59
  * The Cloud API object.
60
  * @var null|Customify_Cloud_Api
@@ -67,12 +75,8 @@ class Customify_Style_Manager {
67
  * Constructor.
68
  *
69
  * @since 1.7.0
70
- *
71
- * @param $parent
72
  */
73
- protected function __construct( $parent = null ) {
74
- $this->parent = $parent;
75
-
76
  $this->init();
77
  }
78
 
@@ -94,12 +98,24 @@ class Customify_Style_Manager {
94
  require_once 'class-customify-color-palettes.php';
95
  $this->color_palettes = Customify_Color_Palettes::instance();
96
 
97
- /**
98
- * Initialize the Font Palettes logic.
99
- */
100
  // require_once 'class-customify-font-palettes.php';
101
  // $this->font_palettes = Customify_Font_Palettes::instance();
102
 
 
 
 
 
 
 
 
 
 
 
 
 
103
  /**
104
  * Initialize the Cloud API logic.
105
  */
@@ -239,8 +255,8 @@ class Customify_Style_Manager {
239
  'priority' => 22,
240
  'capability' => 'edit_theme_options',
241
  'panel_id' => 'style_manager_panel',
242
- 'title' => __( 'Style Manager', 'pixcustomify' ),
243
- 'description' => __( '<strong>Style Manager</strong> is an intuitive system to help you change the look of your website and make an excellent impression.', 'pixcustomify' ),
244
  'sections' => array(),
245
  'auto_expand_sole_section' => true, // If there is only one section in the panel, auto-expand it.
246
  );
@@ -277,7 +293,7 @@ class Customify_Style_Manager {
277
  );
278
 
279
  $color_palettes_section_config = array(
280
- 'title' => __( 'Colors', 'pixcustomify' ),
281
  'section_id' => 'sm_color_palettes_section',
282
  'priority' => 10,
283
  'options' => array(),
@@ -311,7 +327,7 @@ class Customify_Style_Manager {
311
  );
312
 
313
  $font_palettes_section_config = array(
314
- 'title' => __( 'Fonts', 'pixcustomify' ),
315
  'section_id' => 'sm_font_palettes_section',
316
  'priority' => 20,
317
  'options' => array(),
@@ -336,17 +352,70 @@ class Customify_Style_Manager {
336
  'priority' => 23,
337
  'capability' => 'edit_theme_options',
338
  'panel_id' => 'theme_options_panel',
339
- 'title' => __( 'Theme Options', 'pixcustomify' ),
340
- 'description' => __( 'Advanced options to change your site look-and-feel on a detailed level.', 'pixcustomify' ),
341
  'sections' => $other_theme_sections_config,
342
  );
343
 
344
- // Finally, remove the switch theme panel from the Customizer.
345
- add_action( 'customize_register', array( $this, 'remove_switch_theme_panel' ), 10 );
 
 
 
346
 
347
  return $config;
348
  }
349
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
350
  /**
351
  * Remove the switch/preview theme panel.
352
  *
@@ -369,8 +438,11 @@ class Customify_Style_Manager {
369
  return;
370
  }
371
 
372
- // Only output if the user didn't provide feedback.
373
- if ( ! $this->user_provided_feedback() ) { ?>
 
 
 
374
  <div id="style-manager-user-feedback-modal">
375
  <div class="modal">
376
  <div class="modal-dialog" role="document">
@@ -446,26 +518,42 @@ class Customify_Style_Manager {
446
  }
447
 
448
  /**
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
449
  * @param bool|int $timestamp_limit Optional. Timestamp to compare the time the user provided feedback.
450
- * If the provided timestamp is earlier than the time the user provided feedback, returns false.
451
  *
452
  * @return bool
453
  */
454
- public function user_provided_feedback( $timestamp_limit = false ) {
455
  if ( defined( 'CUSTOMIFY_SM_ALWAYS_ASK_FOR_FEEDBACK' ) && true === CUSTOMIFY_SM_ALWAYS_ASK_FOR_FEEDBACK ) {
456
- return false;
457
  }
458
 
459
- $user_provided_feedback = get_option( 'style_manager_user_feedback_provided' );
460
- if ( empty( $user_provided_feedback ) ) {
461
- return false;
462
  }
463
 
464
- if ( ! empty( $timestamp ) && is_int( $timestamp ) && $timestamp_limit > $user_provided_feedback ) {
465
- return false;
466
  }
467
 
468
- return true;
469
  }
470
 
471
  /**
@@ -524,14 +612,13 @@ class Customify_Style_Manager {
524
  *
525
  * @since 1.7.0
526
  * @static
527
- * @param object $parent Main PixCustomifyPlugin instance.
528
  *
529
  * @return Customify_Style_Manager Main Customify_Style_Manager instance
530
  */
531
- public static function instance( $parent = null ) {
532
 
533
  if ( is_null( self::$_instance ) ) {
534
- self::$_instance = new self( $parent );
535
  }
536
 
537
  return self::$_instance;
55
  */
56
  protected $font_palettes = null;
57
 
58
+ /**
59
+ * The notifications object.
60
+ * @var null|Pixcloud_Admin_Notifications_Manager
61
+ * @access public
62
+ * @since 1.9.0
63
+ */
64
+ protected $notifications = null;
65
+
66
  /**
67
  * The Cloud API object.
68
  * @var null|Customify_Cloud_Api
75
  * Constructor.
76
  *
77
  * @since 1.7.0
 
 
78
  */
79
+ protected function __construct() {
 
 
80
  $this->init();
81
  }
82
 
98
  require_once 'class-customify-color-palettes.php';
99
  $this->color_palettes = Customify_Color_Palettes::instance();
100
 
101
+ // /**
102
+ // * Initialize the Font Palettes logic.
103
+ // */
104
  // require_once 'class-customify-font-palettes.php';
105
  // $this->font_palettes = Customify_Font_Palettes::instance();
106
 
107
+ /**
108
+ * Initialize the Notifications logic.
109
+ */
110
+ require_once 'admin-notifications-manager/class-admin-notifications-manager.php';
111
+ $this->notifications = Pixcloud_Admin_Notifications_Manager::instance(
112
+ array(
113
+ 'plugin_name' => 'Customify',
114
+ 'text_domain' => 'customify',
115
+ 'version' => '',
116
+ )
117
+ );
118
+
119
  /**
120
  * Initialize the Cloud API logic.
121
  */
255
  'priority' => 22,
256
  'capability' => 'edit_theme_options',
257
  'panel_id' => 'style_manager_panel',
258
+ 'title' => esc_html__( 'Style Manager', 'customify' ),
259
+ 'description' => __( '<strong>Style Manager</strong> is an intuitive system to help you change the look of your website and make an excellent impression.', 'customify' ),
260
  'sections' => array(),
261
  'auto_expand_sole_section' => true, // If there is only one section in the panel, auto-expand it.
262
  );
293
  );
294
 
295
  $color_palettes_section_config = array(
296
+ 'title' => esc_html__( 'Colors', 'customify' ),
297
  'section_id' => 'sm_color_palettes_section',
298
  'priority' => 10,
299
  'options' => array(),
327
  );
328
 
329
  $font_palettes_section_config = array(
330
+ 'title' => esc_html__( 'Fonts', 'customify' ),
331
  'section_id' => 'sm_font_palettes_section',
332
  'priority' => 20,
333
  'options' => array(),
352
  'priority' => 23,
353
  'capability' => 'edit_theme_options',
354
  'panel_id' => 'theme_options_panel',
355
+ 'title' => esc_html__( 'Theme Options', 'customify' ),
356
+ 'description' => esc_html__( 'Advanced options to change your site look-and-feel on a detailed level.', 'customify' ),
357
  'sections' => $other_theme_sections_config,
358
  );
359
 
360
+ // Add the logic that handles sections and controls added directly to WP_Customizer, not through the config.
361
+ add_action( 'customize_register', array( $this, 'reorganize_direct_sections_and_controls' ), 100 );
362
+
363
+ // Remove the switch theme panel from the Customizer.
364
+ add_action( 'customize_register', array( $this, 'remove_switch_theme_panel' ), 12 );
365
 
366
  return $config;
367
  }
368
 
369
+ /**
370
+ * Reorganizes sections and controls added directly to WP_Customizer, not through the config.
371
+ *
372
+ * @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!
373
+ *
374
+ * @since 1.9.0
375
+ *
376
+ * @param WP_Customize_Manager $wp_customize
377
+ */
378
+ public function reorganize_direct_sections_and_controls( $wp_customize ) {
379
+ // We will do out best to identify direct sections and move their controls to the appropriate place.
380
+ /** @var WP_Customize_Section $section */
381
+ foreach ( $wp_customize->sections() as $section ) {
382
+ // These are general theme options sections that need to have their controls moved to the Theme Options > General section.
383
+ if ( false !== strpos( $section->id, 'theme_options') ) {
384
+ $theme_options_panel = $wp_customize->get_panel( 'theme_options_panel' );
385
+ $general_section = false;
386
+ foreach ( $theme_options_panel->sections as $theme_options_section ) {
387
+ if ( false !== strpos( $theme_options_section->id, 'general' ) ) {
388
+ $general_section = $section;
389
+ }
390
+ }
391
+
392
+ if ( false === $general_section ) {
393
+ // We need to add a general section in the Theme Options panel.
394
+ $general_section = $wp_customize->add_section( 'theme_options[general]', array(
395
+ 'title' => esc_html__( 'General', 'customify' ),
396
+ 'panel' => $theme_options_panel->id,
397
+ 'priority' => 2,
398
+ ) );
399
+ }
400
+
401
+ // Move all the controls in the identified theme options section to the general one.
402
+ /** @var WP_Customize_Control $control */
403
+ foreach ( $wp_customize->controls() as $control ) {
404
+ if ( $control->section !== $section->id ) {
405
+ continue;
406
+ }
407
+
408
+ $control->section = $general_section->id;
409
+ }
410
+
411
+ // Finally remove the now empty section.
412
+ $wp_customize->remove_section( $section->id );
413
+
414
+ break;
415
+ }
416
+ }
417
+ }
418
+
419
  /**
420
  * Remove the switch/preview theme panel.
421
  *
438
  return;
439
  }
440
 
441
+ // We want to ask for feedback once a month.
442
+ $a_month_back = time() - MONTH_IN_SECONDS;
443
+
444
+ // Only output if we should ask for feedback.
445
+ if ( $this->should_ask_for_feedback( $a_month_back ) ) { ?>
446
  <div id="style-manager-user-feedback-modal">
447
  <div class="modal">
448
  <div class="modal-dialog" role="document">
518
  }
519
 
520
  /**
521
+ * Return whether user provided feedback, and if so, return the timestamp.
522
+ *
523
+ * @return bool|int
524
+ */
525
+ public function user_provided_feedback() {
526
+ $user_provided_feedback = get_option( 'style_manager_user_feedback_provided' );
527
+ if ( empty( $user_provided_feedback ) ) {
528
+ return false;
529
+ }
530
+
531
+ return $user_provided_feedback;
532
+ }
533
+
534
+ /**
535
+ * Determine if we should ask for user feedback.
536
+ *
537
  * @param bool|int $timestamp_limit Optional. Timestamp to compare the time the user provided feedback.
538
+ * If the provided timestamp is earlier than the time the user provided feedback, should ask again.
539
  *
540
  * @return bool
541
  */
542
+ public function should_ask_for_feedback( $timestamp_limit = false ) {
543
  if ( defined( 'CUSTOMIFY_SM_ALWAYS_ASK_FOR_FEEDBACK' ) && true === CUSTOMIFY_SM_ALWAYS_ASK_FOR_FEEDBACK ) {
544
+ return true;
545
  }
546
 
547
+ $feedback_timestamp = $this->user_provided_feedback();
548
+ if ( empty( $feedback_timestamp ) ) {
549
+ return true;
550
  }
551
 
552
+ if ( ! empty( $timestamp_limit ) && intval( $timestamp_limit ) > intval( $feedback_timestamp ) ) {
553
+ return true;
554
  }
555
 
556
+ return false;
557
  }
558
 
559
  /**
612
  *
613
  * @since 1.7.0
614
  * @static
 
615
  *
616
  * @return Customify_Style_Manager Main Customify_Style_Manager instance
617
  */
618
+ public static function instance() {
619
 
620
  if ( is_null( self::$_instance ) ) {
621
+ self::$_instance = new self();
622
  }
623
 
624
  return self::$_instance;
includes/lib/class-customify-cloud-api.php CHANGED
@@ -65,17 +65,33 @@ class Customify_Cloud_Api {
65
  * @return array|false
66
  */
67
  public function fetch_design_assets() {
68
- $request_data = apply_filters( 'customify_pixelgrade_cloud_request_data', array(
69
  'site_url' => home_url('/'),
70
  // We are only interested in data needed to identify the theme and eventually deliver only design assets suitable for it.
71
  'theme_data' => $this->get_active_theme_data(),
72
  // We are only interested in data needed to identify the plugin version and eventually deliver design assets suitable for it.
73
  'site_data' => $this->get_site_data(),
74
- ), $this );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
75
 
76
  $request_args = array(
77
  'method' => self::$externalApiEndpoints['cloud']['getDesignAssets']['method'],
78
- 'timeout' => 4,
79
  'blocking' => true,
80
  'body' => $request_data,
81
  'sslverify' => false,
65
  * @return array|false
66
  */
67
  public function fetch_design_assets() {
68
+ $request_data = array(
69
  'site_url' => home_url('/'),
70
  // We are only interested in data needed to identify the theme and eventually deliver only design assets suitable for it.
71
  'theme_data' => $this->get_active_theme_data(),
72
  // We are only interested in data needed to identify the plugin version and eventually deliver design assets suitable for it.
73
  'site_data' => $this->get_site_data(),
74
+ // Extra post statuses besides `publish`.
75
+ 'post_status' => array(),
76
+ );
77
+
78
+ // Handle development and testing constants.
79
+ if ( defined('SM_FETCH_DRAFT_ASSETS') && true === SM_FETCH_DRAFT_ASSETS ) {
80
+ $request_data['post_status'][] = 'draft';
81
+ }
82
+ if ( defined('SM_FETCH_PRIVATE_ASSETS') && true === SM_FETCH_PRIVATE_ASSETS ) {
83
+ $request_data['post_status'][] = 'private';
84
+ }
85
+ if ( defined('SM_FETCH_FUTURE_ASSETS') && true === SM_FETCH_FUTURE_ASSETS ) {
86
+ $request_data['post_status'][] = 'future';
87
+ }
88
+
89
+ // Allow others to filter the data we send.
90
+ $request_data = apply_filters( 'customify_pixelgrade_cloud_request_data', $request_data, $this );
91
 
92
  $request_args = array(
93
  'method' => self::$externalApiEndpoints['cloud']['getDesignAssets']['method'],
94
+ 'timeout' => 5,
95
  'blocking' => true,
96
  'body' => $request_data,
97
  'sslverify' => false,
js/customizer/color-palettes.js CHANGED
@@ -13,14 +13,14 @@ let ColorPalettes = ( function( $, exports, wp ) {
13
  "sm_light_tertiary"
14
  ];
15
 
16
- // const master_color_selector = '#_customize-input-sm_dark_color_master_slider_control';
17
- // const primary_color_selector = '#_customize-input-sm_dark_color_primary_slider_control';
18
- // const secondary_color_selector = '#_customize-input-sm_dark_color_secondary_slider_control';
19
- // const tertiary_color_selector = '#_customize-input-sm_dark_color_tertiary_slider_control';
20
- // const color_dispersion_selector = '#_customize-input-sm_colors_dispersion_control';
21
- // const color_focus_point_selector = '#_customize-input-sm_colors_focus_point_control';
22
- // const color_sliders_selector = primary_color_selector + ', ' + secondary_color_selector + ', ' + tertiary_color_selector;
23
- // const all_sliders_selector = color_sliders_selector + ', ' + color_dispersion_selector + ', ' + color_focus_point_selector;
24
 
25
  let setupGlobalsDone = false;
26
 
@@ -177,7 +177,7 @@ let ColorPalettes = ( function( $, exports, wp ) {
177
  // return an array with the hex values of a certain color palette
178
  const getPaletteColors = ( palette_id ) => {
179
 
180
- }
181
 
182
  // return an array with the hex values of the current palette
183
  const getCurrentPaletteColors = () => {
@@ -188,7 +188,7 @@ let ColorPalettes = ( function( $, exports, wp ) {
188
  colors.push( color );
189
  } );
190
  return colors;
191
- }
192
 
193
  const createCurrentPaletteControls = () => {
194
  const $palette = $( '.c-color-palette' );
@@ -238,7 +238,7 @@ let ColorPalettes = ( function( $, exports, wp ) {
238
  $( obj ).data( 'target' ).not( $input ).hide();
239
  } );
240
  $input.show().focus();
241
- }
242
 
243
  $obj.on( 'click', ( e ) => {
244
  e.stopPropagation();
@@ -309,6 +309,7 @@ let ColorPalettes = ( function( $, exports, wp ) {
309
  $label.remove();
310
 
311
  $( this ).trigger( 'customify:preset-change' );
 
312
  setCurrentPalette( label );
313
 
314
  setPalettesOnConnectedFields();
@@ -439,6 +440,31 @@ let ColorPalettes = ( function( $, exports, wp ) {
439
  $( '.customify_preset.color_palette .palette__item' ).addClass( 'hidden' ).filter( optionsSelector ).removeClass( 'hidden' );
440
  }, 30 );
441
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
442
  const refreshCurrentPaletteControl = () => {
443
  toggleAlteredClassOnMasterControls();
444
  toggleHiddenClassOnMasterControls();
@@ -594,7 +620,7 @@ let ColorPalettes = ( function( $, exports, wp ) {
594
  bindConnectedFields();
595
  resetSettings();
596
  refreshCurrentPaletteControl();
597
- }
598
 
599
  const confirmChanges = ( callback ) => {
600
  if ( typeof callback !== 'function' ) {
@@ -611,8 +637,7 @@ let ColorPalettes = ( function( $, exports, wp ) {
611
  if ( ! altered || confirmed ) {
612
  callback();
613
  }
614
- }
615
-
616
 
617
  const bindEvents = () => {
618
  const paletteControlSelector = '.c-color-palette__control';
@@ -636,17 +661,16 @@ let ColorPalettes = ( function( $, exports, wp ) {
636
  // when variation is changed reload connected fields from cached version of customizer settings config
637
  $( document ).on( 'change', '[name="_customize-radio-sm_color_palette_variation_control"]', reinitializeConnectedFields );
638
 
639
- //
640
  $( document ).on( 'click', '.customify_preset.color_palette input', function () {
641
  confirmChanges( onPaletteChange.bind( this ) );
642
  } );
643
 
644
- // $( all_sliders_selector ).on( 'input', reloadConnectedFields );
645
-
646
- // $( master_color_selector ).on( 'input', function() {
647
- // const masterValue = $( master_color_selector ).val();
648
- // $( color_sliders_selector ).val( masterValue ).trigger( 'input' );
649
- // } );
650
  };
651
 
652
  wp.customize.bind( 'ready', function() {
@@ -659,6 +683,7 @@ let ColorPalettes = ( function( $, exports, wp ) {
659
  bindConnectedFields();
660
  refreshCurrentPaletteControl();
661
  setPalettesOnConnectedFields();
 
662
  bindEvents();
663
  } );
664
 
13
  "sm_light_tertiary"
14
  ];
15
 
16
+ // const master_color_selector = '#_customize-input-sm_dark_color_master_slider_control';
17
+ // const primary_color_selector = '#_customize-input-sm_dark_color_primary_slider_control';
18
+ // const secondary_color_selector = '#_customize-input-sm_dark_color_secondary_slider_control';
19
+ // const tertiary_color_selector = '#_customize-input-sm_dark_color_tertiary_slider_control';
20
+ // const color_dispersion_selector = '#_customize-input-sm_colors_dispersion_control';
21
+ // const color_focus_point_selector = '#_customize-input-sm_colors_focus_point_control';
22
+ // const color_sliders_selector = primary_color_selector + ', ' + secondary_color_selector + ', ' + tertiary_color_selector;
23
+ // const all_sliders_selector = color_sliders_selector + ', ' + color_dispersion_selector + ', ' + color_focus_point_selector;
24
 
25
  let setupGlobalsDone = false;
26
 
177
  // return an array with the hex values of a certain color palette
178
  const getPaletteColors = ( palette_id ) => {
179
 
180
+ };
181
 
182
  // return an array with the hex values of the current palette
183
  const getCurrentPaletteColors = () => {
188
  colors.push( color );
189
  } );
190
  return colors;
191
+ };
192
 
193
  const createCurrentPaletteControls = () => {
194
  const $palette = $( '.c-color-palette' );
238
  $( obj ).data( 'target' ).not( $input ).hide();
239
  } );
240
  $input.show().focus();
241
+ };
242
 
243
  $obj.on( 'click', ( e ) => {
244
  e.stopPropagation();
309
  $label.remove();
310
 
311
  $( this ).trigger( 'customify:preset-change' );
312
+
313
  setCurrentPalette( label );
314
 
315
  setPalettesOnConnectedFields();
440
  $( '.customify_preset.color_palette .palette__item' ).addClass( 'hidden' ).filter( optionsSelector ).removeClass( 'hidden' );
441
  }, 30 );
442
 
443
+ const refreshCurrentPaletteControl = () => {
444
+ toggleAlteredClassOnMasterControls();
445
+ toggleHiddenClassOnMasterControls();
446
+ updateActiveVariationControlColor();
447
+
448
+ const swapConnectedFields = ( settings ) => {
449
+ let variation = getCurrentVariation();
450
+ let swapMap = window.colorPalettesVariations[variation];
451
+ let newSettings = JSON.parse(JSON.stringify(settings));
452
+ let oldSettings = JSON.parse(JSON.stringify(settings));
453
+
454
+ _.each( masterSettingIds, function( masterSettingId ) {
455
+ let connectedFields = wp.customize.settings.settings[masterSettingId]['connected_fields'];
456
+
457
+ if ( ! _.isUndefined( connectedFields ) && ! _.isEmpty( connectedFields ) ) {
458
+ optionsToShow.push( masterSettingId );
459
+ }
460
+ } );
461
+
462
+ optionsSelector = '.' + optionsToShow.join(', .');
463
+
464
+ $( '.c-color-palette .color' ).addClass( 'hidden' ).filter( optionsSelector ).removeClass( 'hidden' )
465
+ $( '.customify_preset.color_palette .palette__item' ).addClass( 'hidden' ).filter( optionsSelector ).removeClass( 'hidden' );
466
+ }, 30 );
467
+
468
  const refreshCurrentPaletteControl = () => {
469
  toggleAlteredClassOnMasterControls();
470
  toggleHiddenClassOnMasterControls();
620
  bindConnectedFields();
621
  resetSettings();
622
  refreshCurrentPaletteControl();
623
+ };
624
 
625
  const confirmChanges = ( callback ) => {
626
  if ( typeof callback !== 'function' ) {
637
  if ( ! altered || confirmed ) {
638
  callback();
639
  }
640
+ };
 
641
 
642
  const bindEvents = () => {
643
  const paletteControlSelector = '.c-color-palette__control';
661
  // when variation is changed reload connected fields from cached version of customizer settings config
662
  $( document ).on( 'change', '[name="_customize-radio-sm_color_palette_variation_control"]', reinitializeConnectedFields );
663
 
 
664
  $( document ).on( 'click', '.customify_preset.color_palette input', function () {
665
  confirmChanges( onPaletteChange.bind( this ) );
666
  } );
667
 
668
+ // $( all_sliders_selector ).on( 'input', reloadConnectedFields );
669
+ //
670
+ // $( master_color_selector ).on( 'input', function() {
671
+ // const masterValue = $( master_color_selector ).val();
672
+ // $( color_sliders_selector ).val( masterValue ).trigger( 'input' );
673
+ // } );
674
  };
675
 
676
  wp.customize.bind( 'ready', function() {
683
  bindConnectedFields();
684
  refreshCurrentPaletteControl();
685
  setPalettesOnConnectedFields();
686
+
687
  bindEvents();
688
  } );
689
 
palettes.md CHANGED
@@ -15,7 +15,22 @@ Next you need to organize and reduce the number of all those colors to three gro
15
  From over 7-years experience of building sites, we find out that those three categories could fill up most of the decisions that a designer take in setting up their site’s elements.
16
 
17
  ### 1.2 Define the Master Colors
18
- Our current color palettes system supports at most 9 colors (3 accent colors, 3 dark shades and 3 light shades). To make things easier define those colors as constants in your `functions.php` file like so:
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
19
 
20
  ```php
21
  // Color
15
  From over 7-years experience of building sites, we find out that those three categories could fill up most of the decisions that a designer take in setting up their site’s elements.
16
 
17
  ### 1.2 Define the Master Colors
18
+ Our current color palettes system supports at most 9 colors (3 accent colors, 3 dark shades and 3 light shades). Below are some guidelines and suggestions about the purpose of each color:
19
+
20
+ | Section | Type | Description |
21
+ |:--|:--|:--|
22
+ | **Color** | Primary | The main Accent color use it to draw the highest attention.|
23
+ | | Secondary | An alternative color for Primary |
24
+ | | Tertiary | An alternative color for Secondary |
25
+ | **Dark** | Primary | Headings Color – usually the darkest shade. |
26
+ | | Secondary | Body text color |
27
+ | | Tertiary | The lightest shade of dark. |
28
+ | **Light** | Primary | Main site background color |
29
+ | | Secondary | A complementary color for site background |
30
+ | | Tertiary | A *highlighter* background color for various elements (e.g. post-it notes, brush strokes). Should have a greater intensity than the others two light colors. |
31
+
32
+
33
+ To make things easier define those colors as constants in your `functions.php` file like so:
34
 
35
  ```php
36
  // Color
readme.txt CHANGED
@@ -2,7 +2,7 @@
2
  Contributors: pixelgrade, euthelup, babbardel, vlad.olaru, cristianfrumusanu, raduconstantin, razvanonofrei
3
  Tags: customizer, css, editor, live, preview, customizer
4
  Requires at least: 4.7.0
5
- Tested up to: 4.9.7
6
  Stable tag: 1.8.0
7
  License: GPLv2 or later
8
  License URI: http://www.gnu.org/licenses/gpl-2.0.html
@@ -45,6 +45,11 @@ With [Customify](https://github.com/pixelgrade/customify), developers can easily
45
 
46
  == Changelog ==
47
 
 
 
 
 
 
48
  = 1.8.0 =
49
  * Added altered state for colors in the current color palette when any of the controls connected to the color has been modified
50
  * Added the colors from the current palette to all the color pickers in the Theme Options section
2
  Contributors: pixelgrade, euthelup, babbardel, vlad.olaru, cristianfrumusanu, raduconstantin, razvanonofrei
3
  Tags: customizer, css, editor, live, preview, customizer
4
  Requires at least: 4.7.0
5
+ Tested up to: 4.9.8
6
  Stable tag: 1.8.0
7
  License: GPLv2 or later
8
  License URI: http://www.gnu.org/licenses/gpl-2.0.html
45
 
46
  == Changelog ==
47
 
48
+ = 1.9.0 =
49
+ * Added ability to modify existing Customizer panels, sections, controls
50
+ * Added system for admin notifications
51
+ * Overall enhancements for more performance and stability
52
+
53
  = 1.8.0 =
54
  * Added altered state for colors in the current color palette when any of the controls connected to the color has been modified
55
  * Added the colors from the current palette to all the color pickers in the Theme Options section