PluginProbe
NotificationX – FOMO, Live Sales Notification, WooCommerce Sales Popup, GDPR, Social Proof, Announcement Banner & Floating Notification Bar / 3.3.3
NotificationX – FOMO, Live Sales Notification, WooCommerce Sales Popup, GDPR, Social Proof, Announcement Banner & Floating Notification Bar v3.3.3
3.3.3 3.3.2 3.3.1 3.3.0 3.2.14 3.2.13 3.2.12 3.2.11 3.2.10 3.2.9 3.2.8 3.2.7 trunk 0.2.5.5 0.2.5.6 0.2.5.7 1.0.0 1.0.1 1.0.2 1.0.3 1.1.0 1.1.1 1.1.2 1.1.3 1.1.4 All 158 releases
notificationx / includes / Admin / Settings.php

Settings.php in NotificationX – FOMO, Live Sales Notification, WooCommerce Sales Popup, GDPR, Social Proof, Announcement Banner & Floating Notification Bar 3.3.3, at includes/Admin/Settings.php

1,012 lines 50.4 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 /**
4 * Extension Factory
5 *
6 * @package NotificationX\Extensions
7 */
8
9 namespace NotificationX\Admin;
10
11 use NotificationX\Core\Database;
12 use NotificationX\Core\Modules;
13 use NotificationX\Core\REST;
14 use NotificationX\Core\Rules;
15 use NotificationX\Core\Upgrader;
16 use NotificationX\Extensions\GlobalFields;
17 use NotificationX\GetInstance;
18 use NotificationX\NotificationX;
19 use UsabilityDynamics\Settings as UsabilityDynamicsSettings;
20 use NotificationX\Core\Helper;
21
22 /**
23 * Settings Class
24 * @method static Settings get_instance($args = null)
25 */
26 class Settings extends UsabilityDynamicsSettings {
27 /**
28 * Instance of Settings
29 *
30 * @var Settings
31 */
32 use GetInstance;
33
34 protected $wpdb;
35 protected $defaults = [];
36
37 /**
38 * Initially Invoked when initialized.
39 *
40 * @hook init
41 */
42 public function __construct( $args ) {
43 global $wpdb;
44 $this->wpdb = $wpdb;
45 parent::__construct( $args );
46 add_action( 'init', [ $this, 'init' ] );
47 // add_filter('user_has_cap', array($this, 'allow_admin'), 10, 4);
48 }
49
50 /**
51 * Called from Admin::init();
52 * NotificationX
53 *
54 * @return void
55 */
56 public function init() {
57 // add_action('admin_enqueue_scripts', [$this, 'admin_enqueue_scripts']);
58 add_action( 'admin_menu', [ $this, 'menu' ], 25 );
59 add_filter( 'nx_branding_url', array( $this, 'nx_branding_url' ), 12 );
60 add_filter( 'nx_rest_miscellaneous', array( $this, 'miscellaneous' ), 10, 2 );
61 }
62
63 /**
64 * This method is responsible for Admin Menu of
65 * NotificationX
66 *
67 * @return void
68 */
69 public function menu() {
70 add_submenu_page( 'nx-admin', 'Settings', 'Settings', 'edit_notificationx_settings', 'nx-settings', [ Admin::get_instance(), 'views' ], 3 );
71 }
72
73 /**
74 * Register scripts and styles.
75 *
76 * @param string $hook
77 * @return void
78 */
79 function get_form_data() {
80 $data = NotificationX::get_instance()->normalize( $this->settings_form() );
81 // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedHooknameFound -- Reviewed for the NotificationX codebase: acceptable in this context.
82 $settings = apply_filters('nx_settings_page_settings', $this->get( 'settings', [] ));
83
84 /*
85 * `/builder` resolves `read_notificationx` -- the lowest capability the
86 * product defines -- and this blob is handed straight to it. The
87 * `settingsRedirect` flag below only tells the admin app not to render
88 * the settings screen; it is a client-side routing hint, not a boundary.
89 * Without this, a view-only delegate received every API key, client
90 * secret and OAuth token stored on the site.
91 */
92 if ( ! current_user_can( 'edit_notificationx_settings' ) ) {
93 $settings = self::redact_secret_settings( $settings );
94 }
95
96 $data['current_page'] = 'settings';
97 $data['rest'] = REST::get_instance()->rest_data();
98 $data['savedValues'] = $settings;
99 $data['values'] = $settings;
100 $data['assets'] = [
101 'admin' => NOTIFICATIONX_ADMIN_URL,
102 'public' => NOTIFICATIONX_PUBLIC_URL,
103 ];
104 return $data;
105 }
106
107 /**
108 * Admin Views
109 *
110 * @return void
111 */
112 public function settings_page() {
113 include_once NOTIFICATIONX_INCLUDES . 'Admin/views/settings.views.php';
114 }
115
116
117 public function settings_form( $nx_id = 0 ) {
118 // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedHooknameFound -- Reviewed for the NotificationX codebase: acceptable in this context.
119 do_action( 'nx_before_settings_fields' );
120 $wp_roles = GlobalFields::get_instance()->normalize_fields( $this->get_roles() );
121 $site_name = get_bloginfo( 'name' );
122 $settings = [
123 'id' => 'notificationx_metabox_wrapper',
124 'title' => __( 'NotificationX', 'notificationx' ),
125 'object_types' => array( 'notificationx' ),
126 'context' => 'normal',
127 'priority' => 'high',
128 'show_header' => false,
129 'tabnumber' => true,
130 'layout' => 'horizontal',
131 'is_pro_active' => NotificationX::get_instance()->is_pro(),
132 'cus_imp_limit' => Settings::get_instance()->get('settings.custom_notification_import_limit', 100),
133 'is_wpml_active'=> Helper::is_wpml_setup(),
134 'config' => [
135 'active' => 'tab-general',
136 'sidebar' => false,
137 'title' => false,
138 ],
139 'submit' => [
140 'show' => true,
141 'label' => __( 'Save Settings', 'notificationx' ),
142 'class' => 'save-settings',
143 ],
144 // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedHooknameFound -- Reviewed for the NotificationX codebase: acceptable in this context.
145 'tabs' => apply_filters('nx_settings_tab', [
146 // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedHooknameFound -- Reviewed for the NotificationX codebase: acceptable in this context.
147 'tab-general' => apply_filters('nx_settings_tab_general', [
148 'id' => 'tab-general',
149 'label' => __( 'General', 'notificationx' ),
150 'classes' => 'tab-general',
151 'priority' => 10,
152 'fields' => [
153 'section-modules' => [
154 'label' => __( 'Modules', 'notificationx' ),
155 'name' => 'section-modules',
156 'type' => 'section',
157 'classes' => NotificationX::get_instance()->is_pro() ? 'section-modules pro-activated' : 'section-modules',
158 'fields' => [
159 'modules' => [
160 // 'label' => "Modules",
161 'name' => 'modules',
162 'type' => 'toggle',
163 'multiple' => true,
164 'default' => true,
165 'style' => [
166 'type' => 'card',
167 'column' => 3,
168 ],
169 'options' => array_values( Modules::get_instance()->get_all() ),
170 ],
171 ],
172 ],
173 ],
174 ]
175 ),
176 // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedHooknameFound -- Reviewed for the NotificationX codebase: acceptable in this context.
177 'advanced-settings-tab' => apply_filters('nx_settings_tab_advanced', [
178 'id' => 'tab-advanced-settings',
179 'label' => __( 'Advanced Settings', 'notificationx' ),
180 'classes' => 'tab-advanced-settings',
181 'priority' => 20,
182 'fields' => [
183 'advanced-general' => [
184 'name' => 'advanced-general',
185 'type' => 'section',
186 'label' => __( 'General', 'notificationx' ),
187 'priority' => 5,
188 'fields' => array(
189 'enable_rest_api' => [
190 'name' => "enable_rest_api",
191 'type' => 'checkbox',
192 'label' => __('Enable REST API', 'notificationx'),
193 'default' => false,
194 'priority' => 10,
195 'description' => __( 'Forcefully enable anonymous REST API for NotificationX.', 'notificationx' ),
196 ],
197 'omit_credentials' => [
198 'name' => "omit_credentials",
199 'type' => 'checkbox',
200 'label' => __('Exclude Credentials', 'notificationx'),
201 'default' => true,
202 'priority' => 15,
203 'description' => __( 'Enabling it will remove cookies, HTTP authentication entries, and TLS client certificates from API calls on the frontend.', 'notificationx' ),
204 'help' => __( 'Note: Recommended if you use any caching plugins.', 'notificationx' ),
205 ],
206 'notification_image_size' => [
207 'name' => "notification_image_size",
208 'type' => 'select',
209 'label' => __('Image Size', 'notificationx'),
210 'default' => '100_100',
211 'options' => GlobalFields::get_instance()->normalize_fields([
212 '100_100' => __('100 x 100 px', 'notificationx'),
213 '200_200' => __('200 x 200 px', 'notificationx'),
214 '300_300' => __('300 x 300 px', 'notificationx'),
215 '400_400' => __('400 x 400 px', 'notificationx'),
216 '500_500' => __('500 x 500 px', 'notificationx'),
217 ]),
218 'priority' => 20,
219 'help' => __('Select the size for your notification image.', 'notificationx'),
220 ],
221 'custom_notification_import_limit' => [
222 'name' => "custom_notification_import_limit",
223 'type' => 'number',
224 'label' => __('Custom Notification Import Limit', 'notificationx'),
225 'default' => 100,
226 'priority' => 25,
227 ],
228 ),
229 ],
230 'powered_by' => [
231 'name' => 'powered_by',
232 'label' => __( 'Powered By', 'notificationx' ),
233 'type' => 'section',
234 'priority' => 15,
235 'fields' => [
236 'disable_powered_by' => [
237 'type' => 'checkbox',
238 'label' => __( 'Disable Powered By', 'notificationx' ),
239 'name' => 'disable_powered_by',
240 'default' => 0,
241 'priority' => 10,
242 'description' => __( 'Click, if you want to disable powered by text from notification', 'notificationx' ),
243 ],
244 ],
245 ],
246 'role_management' => array(
247 'name' => 'role_management',
248 'type' => 'section',
249 'label' => __( 'Role Management', 'notificationx' ),
250 'priority' => 30,
251 'fields' => array(
252 'notification_view_roles' => array(
253 'name' => 'notification_view_roles',
254 'type' => 'select',
255 'label' => __( 'Who Can View Notification?', 'notificationx' ),
256 'priority' => 1,
257 'multiple' => true,
258 'is_pro' => true,
259 'disable' => true,
260 'default' => [ 'administrator' ],
261 'options' => $wp_roles,
262 ),
263 'notification_roles' => array(
264 'name' => 'notification_roles',
265 'type' => 'select',
266 'label' => __( 'Who Can Create Notification?', 'notificationx' ),
267 'priority' => 1,
268 'multiple' => true,
269 'is_pro' => true,
270 'disable' => true,
271 'default' => [ 'administrator' ],
272 'options' => $wp_roles,
273 ),
274 'settings_roles' => array(
275 'name' => 'settings_roles',
276 'type' => 'select',
277 'label' => __( 'Who Can Edit Settings?', 'notificationx' ),
278 'priority' => 2,
279 'multiple' => true,
280 'is_pro' => true,
281 'disable' => true,
282 'default' => [ 'administrator' ],
283 'options' => $wp_roles,
284 ),
285 'analytics_roles' => array(
286 'name' => 'analytics_roles',
287 'type' => 'select',
288 'label' => __( 'Who Can Check Analytics?', 'notificationx' ),
289 'priority' => 3,
290 'multiple' => true,
291 'is_pro' => true,
292 'disable' => true,
293 'default' => [ 'administrator' ],
294 'options' => $wp_roles,
295 ),
296 ),
297 ),
298 ],
299 ]
300 ),
301 // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedHooknameFound -- Reviewed for the NotificationX codebase: acceptable in this context.
302 'email-analytics-reporting' => apply_filters('nx_settings_tab_email_analytics', [
303 'label' => __( 'Analytics & Reporting', 'notificationx' ),
304 'id' => 'email-analytics-reporting',
305 'classes' => 'tab-advanced-settings',
306 'priority' => 30,
307 'fields' => [
308 'analytics' => array(
309 'name' => 'analytics',
310 'priority' => 10,
311 'type' => 'section',
312 'label' => __( 'Analytics', 'notificationx' ),
313 'fields' => array(
314 'enable_analytics' => array(
315 'name' => 'enable_analytics',
316 'type' => 'checkbox',
317 'label' => __( 'Enable Analytics', 'notificationx' ),
318 'default' => true,
319 'priority' => 0,
320 ),
321 'disable_dashboard_widget' => array(
322 'name' => 'disable_dashboard_widget',
323 'type' => 'checkbox',
324 'label' => __( 'Disable Dashboard Widget', 'notificationx' ),
325 'default' => false,
326 'priority' => 5,
327 'description' => __( 'Click, if you want to disable dashboard widget of analytics only.', 'notificationx' ),
328 'rules' => Rules::is( 'enable_analytics', true ),
329 ),
330 'analytics_from' => array(
331 'name' => 'analytics_from',
332 'type' => 'select',
333 'label' => __( 'Analytics From', 'notificationx' ),
334 'options' => GlobalFields::get_instance()->normalize_fields(array(
335 'everyone' => __( 'Everyone', 'notificationx' ),
336 'guests' => __( 'Guests Only', 'notificationx' ),
337 'registered_users' => __( 'Registered Users Only', 'notificationx' ),
338 )
339 ),
340 'default' => 'everyone',
341 'priority' => 10,
342 'rules' => Rules::is( 'enable_analytics', true ),
343 ),
344 'exclude_bot_analytics' => array(
345 'name' => 'exclude_bot_analytics',
346 'type' => 'checkbox',
347 'label' => __( 'Exclude Bot Analytics', 'notificationx' ),
348 'default' => true,
349 'priority' => 15,
350 'description' => __( 'Select if you want to exclude bot analytics.', 'notificationx' ),
351 'rules' => Rules::is( 'enable_analytics', true ),
352 ),
353 ),
354 ),
355 // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedHooknameFound -- Reviewed for the NotificationX codebase: acceptable in this context.
356 'entries_mail_receiver' => apply_filters( 'nx_settings_entries_mail_receiver', array(
357 'name' => 'entries_mail_receiver',
358 'priority' => 15,
359 'type' => 'section',
360 'label' => __( 'Entries Mail Receiver', 'notificationx' ),
361 'fields' => array(
362 'enable_entries_mail' => array(
363 'name' => 'enable_entries_mail',
364 'type' => 'checkbox',
365 'label' => __( 'Enable Entries Mail Receiver', 'notificationx' ),
366 'default' => false,
367 'priority' => 0,
368 'description' => __( 'Send an email notification whenever a new entry is received.', 'notificationx' ),
369 ),
370 'entries_mail_email' => array(
371 'name' => 'entries_mail_email',
372 'type' => 'text',
373 'label' => __( 'Entry Recipient Email', 'notificationx' ),
374 'default' => get_option( 'admin_email' ),
375 'priority' => 1,
376 'rules' => Rules::is( 'enable_entries_mail', true ),
377 ),
378 'entries_mail_subject' => array(
379 'name' => 'entries_mail_subject',
380 'type' => 'text',
381 'label' => __( 'Email Subject', 'notificationx' ),
382 /* translators: %s: site name */
383 'default' => sprintf( __( 'New Entry Received on "%s"', 'notificationx' ), $site_name ),
384 'priority' => 2,
385 'rules' => Rules::is( 'enable_entries_mail', true ),
386 ),
387 'test_entries_mail' => array(
388 'name' => 'test_entries_mail',
389 'label' => __( 'Test Mail', 'notificationx' ),
390 'text' => __( 'Send Test Email', 'notificationx' ),
391 'type' => 'button',
392 'priority' => 3,
393 'rules' => Rules::is( 'enable_entries_mail', true ),
394 'ajax' => [
395 'on' => 'click',
396 'api' => '/notificationx/v1/entries-mail-test',
397 'data' => [
398 'entries_mail_email' => '@entries_mail_email',
399 'entries_mail_subject' => '@entries_mail_subject',
400 ],
401 'swal' => [
402 'text' => __( 'Test email sent successfully.', 'notificationx' ),
403 'icon' => 'success',
404 'autoClose' => 2000,
405 ],
406 ],
407 ),
408 ),
409 ) ),
410 'email_reporting' => array(
411 'name' => 'email_reporting',
412 'priority' => 20,
413 'type' => 'section',
414 'label' => __( 'Reporting', 'notificationx' ),
415 'rules' => Rules::is( 'enable_analytics', true ),
416 'fields' => array(
417 'disable_reporting' => array(
418 'name' => 'disable_reporting',
419 'label' => __( 'Disable Reporting', 'notificationx' ),
420 'type' => 'checkbox',
421 'priority' => 0,
422 'default' => 0,
423 ),
424 'reporting_frequency' => array(
425 'name' => 'reporting_frequency',
426 'type' => 'select',
427 'label' => __( 'Reporting Frequency', 'notificationx' ),
428 'default' => 'nx_weekly',
429 'is_pro' => true,
430 'priority' => 1,
431 'disable' => true,
432 'options' => GlobalFields::get_instance()->normalize_fields(array(
433 'nx_daily' => __( 'Once Daily', 'notificationx' ),
434 'nx_weekly' => __( 'Once Weekly', 'notificationx' ),
435 'nx_monthly' => __( 'Once Monthly', 'notificationx' ),
436 )
437 ),
438 'rules' => Rules::is( 'disable_reporting', false ),
439 'info' => InfoTooltipManager::get_instance()->render('reporting_frequency'),
440 ),
441 'reporting_monthly_help_text' => array(
442 'name' => 'reporting_monthly_help_text',
443 'type' => 'message',
444 'class' => 'nx-warning',
445 'priority' => 1.5,
446 'message' => __( 'It will be triggered on the first day of next month.', 'notificationx' ),
447 'rules' => Rules::is( 'reporting_frequency', 'nx_monthly' ),
448 ),
449 'reporting_day' => array(
450 'name' => 'reporting_day',
451 'type' => 'select',
452 'label' => __( 'Select Reporting Day', 'notificationx' ),
453 'default' => 'monday',
454 'priority' => 2,
455 'options' => GlobalFields::get_instance()->normalize_fields(array(
456 'sunday' => __( 'Sunday', 'notificationx' ),
457 'monday' => __( 'Monday', 'notificationx' ),
458 'tuesday' => __( 'Tuesday', 'notificationx' ),
459 'wednesday' => __( 'Wednesday', 'notificationx' ),
460 'thursday' => __( 'Thursday', 'notificationx' ),
461 'friday' => __( 'Friday', 'notificationx' ),
462 )
463 ),
464 'description' => __( 'Select a day for email report.', 'notificationx' ),
465 'rules' => Rules::logicalRule([
466 Rules::is( 'reporting_frequency', 'nx_weekly' ),
467 Rules::is( 'disable_reporting', false ),
468 ]
469 ),
470 ),
471 'reporting_email' => array(
472 'name' => 'reporting_email',
473 'type' => 'text',
474 'label' => __( 'Reporting Email', 'notificationx' ),
475 'default' => get_option( 'admin_email' ),
476 'priority' => 3,
477 'rules' => Rules::is( 'disable_reporting', false ),
478 ),
479 'reporting_subject' => array(
480 'name' => 'reporting_subject',
481 'type' => 'text',
482 'label' => __( 'Reporting Email Subject', 'notificationx' ),
483 /* translators: %s: site name */
484 'default' => sprintf( __( 'Weekly Engagement Summary of “%s”', 'notificationx' ), $site_name ),
485 'priority' => 4,
486 'disable' => true,
487 'rules' => Rules::is( 'disable_reporting', false ),
488 ),
489 'test_report' => array(
490 'name' => 'test_report',
491 'label' => __( 'Reporting Test', 'notificationx' ),
492 'text' => __( 'Test Report', 'notificationx' ),
493 'type' => 'button',
494 'priority' => 5,
495 'rules' => Rules::is( 'disable_reporting', false ),
496 'ajax' => [
497 'on' => 'click',
498 'api' => '/notificationx/v1/reporting-test',
499 'data' => [
500 'disable_reporting' => '@disable_reporting',
501 'reporting_subject' => '@reporting_subject',
502 'reporting_email' => '@reporting_email',
503 'reporting_day' => '@reporting_day',
504 'reporting_frequency' => '@reporting_frequency',
505 ],
506 'swal' => [
507 'text' => __( 'Successfully Sent a Test Report in Your Email.', 'notificationx' ),
508 'icon' => 'success',
509 'autoClose' => 2000,
510 ],
511 ],
512 ),
513 ),
514 ),
515 ],
516 ]
517 ),
518 // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedHooknameFound -- Reviewed for the NotificationX codebase: acceptable in this context.
519 'entries' => apply_filters('nx_settings_tab_entries', [
520 'label' => __( 'Entries', 'notificationx' ),
521 'id' => 'entries',
522 'classes' => 'tab-advanced-settings',
523 'priority' => 35,
524 'fields' => [
525 'feedback_entries' => array(
526 'name' => 'feedback_entries',
527 'priority' => 10,
528 'type' => 'feedback-entries',
529 'label' => __( 'Analytics', 'notificationx' ),
530 ),
531 ],
532 ]
533 ),
534 // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedHooknameFound -- Reviewed for the NotificationX codebase: acceptable in this context.
535 'cache_settings_tab' => apply_filters('nx_settings_tab_cache', [
536 'id' => 'tab-cache-settings',
537 'label' => __( 'Cache Settings', 'notificationx' ),
538 'priority' => 40,
539 'fields' => [
540 'cache_settings' => array(
541 'name' => 'cache_settings',
542 'type' => 'section',
543 'label' => __( 'Cache Settings', 'notificationx' ),
544 'priority' => 5,
545 'fields' => array(
546 'cache_limit' => array(
547 'name' => 'cache_limit',
548 'type' => 'text',
549 'label' => __( 'Cache Limit', 'notificationx' ),
550 'description' => __( 'Number of Notification Data to be saved in Database.', 'notificationx' ),
551 'default' => '100',
552 'priority' => 1,
553 ),
554 'download_stats_cache_duration' => array(
555 'name' => 'download_stats_cache_duration',
556 'type' => 'text',
557 'label' => __( 'Download Stats Cache Duration', 'notificationx' ),
558 'description' => __( 'Minutes (Schedule Duration to fetch new data).', 'notificationx' ),
559 'default' => 3,
560 'priority' => 2,
561 ),
562 'reviews_cache_duration' => array(
563 'name' => 'reviews_cache_duration',
564 'type' => 'text',
565 'label' => __( 'Reviews Cache Duration', 'notificationx' ),
566 'description' => __( 'Minutes (Schedule Duration to fetch new data).', 'notificationx' ),
567 'default' => '3',
568 'priority' => 3,
569 ),
570 ),
571 ),
572 ],
573 ]
574 ),
575 // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedHooknameFound -- Reviewed for the NotificationX codebase: acceptable in this context.
576 'tab-miscellaneous-settings' => apply_filters('nx_settings_tab_miscellaneous', [
577 'id' => 'tab-miscellaneous-settings',
578 'label' => __( 'Miscellaneous', 'notificationx' ),
579 'priority' => 50,
580 'fields' => [
581 ],
582 ]
583 ),
584 ]
585 ),
586 ];
587
588 if ( defined( 'NX_DEBUG' ) && NX_DEBUG ) {
589 $settings['tabs']['tab-miscellaneous-settings']['fields'][] = array(
590 'name' => 'danger-zone',
591 'type' => 'section',
592 'label' => __( 'Danger Zone', 'notificationx' ),
593 'priority' => 200,
594 'fields' => array(
595 'delete-settings' => array(
596 'name' => 'delete-settings',
597 'label' => __( 'Delete Settings', 'notificationx' ),
598 'text' => __( 'Delete Settings', 'notificationx' ),
599 'type' => 'button',
600 'priority' => 15,
601 'ajax' => [
602 'on' => 'click',
603 'reload' => true,
604 'api' => '/notificationx/v1/settings',
605 'data' => [
606 'delete_settings' => true,
607 ],
608 'swal' => [
609 'text' => __( 'Successfully deleted Settings.', 'notificationx' ),
610 'icon' => 'deleted',
611 'autoClose' => 2000,
612 ],
613 ],
614 ),
615 'delete-transient' => array(
616 'name' => 'delete-transient',
617 'label' => __( 'Delete Transient', 'notificationx' ),
618 'text' => __( 'Delete Transient', 'notificationx' ),
619 'type' => 'button',
620 'priority' => 25,
621 'ajax' => [
622 'on' => 'click',
623 'reload' => true,
624 'api' => '/notificationx/v1/miscellaneous',
625 'data' => [
626 'delete_transient' => true,
627 ],
628 'swal' => [
629 'text' => __( 'Successfully deleted Transient.', 'notificationx' ),
630 'icon' => 'deleted',
631 'autoClose' => 2000,
632 ],
633 ],
634 ),
635 ),
636 );
637 }
638
639 // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedHooknameFound -- Reviewed for the NotificationX codebase: acceptable in this context.
640 $settings = apply_filters( 'nx_settings_configs', $settings );
641 return $settings;
642 }
643
644 /**
645 * Get All Roles
646 * dynamically
647 *
648 * @return array
649 */
650 public function get_roles() {
651 $roles = wp_roles()->role_names;
652 unset( $roles['subscriber'] );
653 return $roles;
654 }
655 /**
656 * Get All Roles
657 * dynamically
658 * user_has_cap
659 *
660 * @return array
661 */
662 public function allow_admin( $allcaps, $caps, $args, $user ) {
663 $nx_roles = [
664 'read_notificationx',
665 'edit_notificationx',
666 'edit_notificationx_settings',
667 'read_notificationx_analytics',
668 ];
669 if ( ! empty( $caps[0] ) && array_key_exists( 'administrator', $allcaps ) && ! array_key_exists( $caps[0], $allcaps ) && in_array( $caps[0], $nx_roles ) ) {
670 $role = get_role( 'administrator' );
671 $role->add_cap( $caps[0] );
672 $allcaps[ $caps[0] ] = true;
673 }
674 return $allcaps;
675 }
676
677 public function save_settings( $settings ) {
678 if ( ! current_user_can( 'edit_notificationx_settings' ) ) {
679 return false;
680 }
681 $remove_before_save = [
682 'empty',
683 ];
684
685 // Must run before the role map is derived below, or the posted values
686 // would still reach `nx_settings_saved` and be written to the roles.
687 $settings = $this->preserve_role_settings( $settings );
688 $settings = $this->preserve_secret_settings( $settings );
689
690 $roles = $this->get_selected_roles( $settings );
691 $settings = array_merge( $settings, $roles );
692
693 // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedHooknameFound -- Reviewed for the NotificationX codebase: acceptable in this context.
694 $settings = apply_filters( 'nx_settings', $settings );
695
696 foreach ( $remove_before_save as $key ) {
697 if ( isset( $settings[ $key ] ) ) {
698 unset( $settings[ $key ] );
699 }
700 }
701 $settings = $this->preserve_protected_settings( $settings );
702
703 // need this to ensure saved value don't return empty array instead of object.
704 if ( ! empty( $settings['delete_settings'] ) ) {
705 $settings = [ 'empty' => true ];
706 // The reset discards everything preserved above, so re-apply the
707 // role gate: a settings administrator must not be able to rewrite
708 // role assignments by routing through a settings reset.
709 $settings = $this->preserve_role_settings( $settings );
710 }
711
712 $this->set( 'settings', $settings );
713 // Module toggles change which builder fields exist, and
714 // NotificationX::normalize_post() coerces field types from this cache.
715 delete_transient( 'nx_builder_fields' );
716 // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedHooknameFound -- Reviewed for the NotificationX codebase: acceptable in this context.
717 do_action( 'nx_settings_saved', $settings );
718 return true;
719 }
720
721 /**
722 * Force server-owned settings back to their stored values.
723 *
724 * `save_settings()` replaces the whole blob with the posted one, so every key
725 * the admin app is holding wins -- right for fields a merchant edits, wrong
726 * for state only the server writes. OAuth tokens are the case that bites:
727 * they live inside `settings`, so they are handed to the admin app on page
728 * load and posted straight back. Disconnecting an account and then saving
729 * from a tab opened *before* the disconnect wrote the revoked token back and
730 * the integration silently kept working -- with Google Analytics that showed
731 * up as live viewer counts on a supposedly disconnected site.
732 *
733 * Keys listed here can only be changed by the code that owns them.
734 *
735 * @param array $settings Incoming settings.
736 * @return array
737 */
738 protected function preserve_protected_settings( $settings ) {
739 // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedHooknameFound -- Reviewed for the NotificationX codebase: acceptable in this context.
740 $protected = apply_filters( 'nx_protected_settings', [
741 'nx_pa_settings', // Google Analytics OAuth token.
742 ] );
743
744 // `get()` returns false for a missing key, and false is also what a
745 // disconnect stores -- so only a sentinel separates "stored as false"
746 // from "never stored", and the two need different handling.
747 $missing = new \stdClass();
748
749 foreach ( (array) $protected as $key ) {
750 if ( ! is_string( $key ) || '' === $key ) {
751 continue;
752 }
753 $stored = $this->get( "settings.{$key}", $missing );
754 if ( $missing === $stored ) {
755 unset( $settings[ $key ] );
756 continue;
757 }
758 $settings[ $key ] = $stored;
759 }
760
761 return $settings;
762 }
763
764 /**
765 * Settings keys holding credentials rather than configuration.
766 *
767 * These are values a site owner pastes in once and which grant access to a
768 * third-party account. They must never reach a client that is not entitled
769 * to edit settings, and must never be written into an export file.
770 *
771 * Pro registers its own integrations, so the list is filterable; keep the
772 * free baseline broad rather than minimal, because a key omitted here is a
773 * key that leaks.
774 *
775 * @return array
776 */
777 public static function get_secret_settings_keys() {
778 // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedHooknameFound -- Reviewed for the NotificationX codebase: acceptable in this context.
779 return (array) apply_filters( 'nx_secret_settings', [
780 'nx_pa_settings', // Google Analytics OAuth access + refresh token.
781 'token_info',
782 'ga_client_secret',
783 'yt_client_secret',
784 'openai_access_token',
785 'mailchimp_api_key',
786 'activecampaign_api_key',
787 'brevo_api_key',
788 'convertkit_api_key',
789 'convertkit_api_secret',
790 'google_review_api_key',
791 'google_youtube_api_key',
792 'zapier_api_key',
793 'ifttt_api_key',
794 'envato_token',
795 'gmap_token',
796 ] );
797 }
798
799 /**
800 * Strip credentials out of a settings blob.
801 *
802 * Keys are removed outright rather than blanked. A blanked value is
803 * indistinguishable from "the user cleared this field" once it is posted
804 * back, which would silently destroy working integrations; an absent key is
805 * unambiguous and is restored by `preserve_secret_settings()` on save.
806 *
807 * @param array $settings
808 * @return array
809 */
810 public static function redact_secret_settings( $settings ) {
811 if ( ! is_array( $settings ) ) {
812 return $settings;
813 }
814 foreach ( self::get_secret_settings_keys() as $key ) {
815 unset( $settings[ $key ] );
816 }
817 return $settings;
818 }
819
820 /**
821 * Carry stored credentials across a save that does not carry them.
822 *
823 * Counterpart to `redact_secret_settings()`. An export has its credentials
824 * stripped, so importing one would otherwise blank every integration on the
825 * target site. Absent key means "unchanged"; a key that *is* present wins,
826 * so a settings administrator editing an API key in the UI still works, and
827 * deliberately clearing a field still clears it.
828 *
829 * Unlike `preserve_protected_settings()` these values are user-owned, so
830 * they must stay editable -- that is why this cannot simply force the
831 * stored value back.
832 *
833 * @param array $settings Incoming settings.
834 * @return array
835 */
836 protected function preserve_secret_settings( $settings ) {
837 if ( ! is_array( $settings ) ) {
838 return $settings;
839 }
840
841 $missing = new \stdClass();
842
843 foreach ( self::get_secret_settings_keys() as $key ) {
844 if ( array_key_exists( $key, $settings ) ) {
845 continue;
846 }
847 $stored = $this->get( "settings.{$key}", $missing );
848 if ( $missing !== $stored ) {
849 $settings[ $key ] = $stored;
850 }
851 }
852
853 return $settings;
854 }
855
856 /**
857 * Keep role assignments out of reach of a plain settings administrator.
858 *
859 * The role selectors grant and revoke capabilities across every role on the
860 * site, which is authority well beyond "may edit NotificationX settings".
861 * Pro hides these fields from the settings form unless the user can
862 * `delete_users` (RoleManagement::tab_advanced), but that is a filter on the
863 * form schema -- it never runs on save, so posting the keys directly to
864 * `/settings` bypassed it entirely.
865 *
866 * @param array $settings Incoming settings.
867 * @return array
868 */
869 protected function preserve_role_settings( $settings ) {
870 if ( ! is_array( $settings ) || current_user_can( 'delete_users' ) ) {
871 return $settings;
872 }
873
874 $role_keys = [
875 'notification_view_roles',
876 'notification_roles',
877 'settings_roles',
878 'analytics_roles',
879 ];
880
881 $missing = new \stdClass();
882
883 foreach ( $role_keys as $key ) {
884 $stored = $this->get( "settings.{$key}", $missing );
885 if ( $missing === $stored ) {
886 unset( $settings[ $key ] );
887 continue;
888 }
889 $settings[ $key ] = $stored;
890 }
891
892 return $settings;
893 }
894
895 public function get_role_map( $settings = [] ) {
896 if ( empty( $settings ) || count( $settings ) == 1 ) {
897 $settings = $this->get_selected_roles();
898 }
899 return [
900 'read_notificationx' => [
901 'roles' => $settings['notification_view_roles'],
902 'map' => [],
903 ],
904 'edit_notificationx' => [
905 'roles' => $settings['notification_roles'],
906 'map' => [ 'read_notificationx' ],
907 ],
908 'edit_notificationx_settings' => [
909 'roles' => $settings['settings_roles'],
910 'map' => [ 'read_notificationx' ],
911 ],
912 'read_notificationx_analytics' => [
913 'roles' => $settings['analytics_roles'],
914 'map' => [ 'read_notificationx' ],
915 ],
916 ];
917 }
918
919 public function get_selected_roles( $settings = [] ) {
920 $notification_view_roles = isset( $settings['notification_view_roles'] ) ? $settings['notification_view_roles'] : self::get_instance()->get( 'settings.notification_view_roles', [] );
921 $notification_roles = isset( $settings['notification_roles'] ) ? $settings['notification_roles'] : self::get_instance()->get( 'settings.notification_roles', [] );
922 $settings_roles = isset( $settings['settings_roles'] ) ? $settings['settings_roles'] : self::get_instance()->get( 'settings.settings_roles', [] );
923 $analytics_roles = isset( $settings['analytics_roles'] ) ? $settings['analytics_roles'] : self::get_instance()->get( 'settings.analytics_roles', [] );
924
925 if ( ! is_array( $notification_view_roles ) ) {
926 $notification_view_roles = [ $notification_view_roles ];
927 }
928 if ( ! is_array( $notification_roles ) ) {
929 $notification_roles = [ $notification_roles ];
930 }
931 if ( ! is_array( $settings_roles ) ) {
932 $settings_roles = [ $settings_roles ];
933 }
934 if ( ! is_array( $analytics_roles ) ) {
935 $analytics_roles = [ $analytics_roles ];
936 }
937
938 // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedHooknameFound -- Reviewed for the NotificationX codebase: acceptable in this context.
939 return apply_filters('nx_role_management', [
940 'notification_view_roles' => array_values( array_unique( array_merge( [ 'administrator' ], $notification_view_roles ) ) ),
941 'notification_roles' => array_values( array_unique( array_merge( [ 'administrator' ], $notification_roles ) ) ),
942 'settings_roles' => array_values( array_unique( array_merge( [ 'administrator' ], $settings_roles ) ) ),
943 'analytics_roles' => array_values( array_unique( array_merge( [ 'administrator' ], $analytics_roles ) ) ),
944 ]
945 );
946 }
947
948 /**
949 * Getter for options
950 *
951 * @param bool|\UsabilityDynamics\type $key
952 *
953 * @param bool $default
954 *
955 * @return type
956 */
957 public function Xget( $key = false, $default = false ) {
958 if ( empty( $default ) && strpos( $key, 'settings.' ) === 0 ) {
959 // getting default value from settings form.
960 $_key = str_replace( 'settings.', '', $key );
961 if ( empty( $default ) ) {
962 $defaults = $this->get_defaults();
963 $default = ! empty( $defaults[ $_key ] ) ? $defaults[ $_key ]['default'] : $default;
964 }
965 }
966 return parent::get( $key, $default );
967 }
968
969 /**
970 * Setter for options
971 *
972 * @param string|\UsabilityDynamics\type $key
973 * @param bool|\UsabilityDynamics\type $value
974 * @param bool $bypass_validation
975 *
976 * @internal param bool|\UsabilityDynamics\type $force_save
977 *
978 * @return \UsabilityDynamics\Settings
979 */
980 public function set( $key = '', $value = false, $bypass_validation = false ) {
981 if ( strpos( $key, '.' ) === false ) {
982 parent::flush( false, $key );
983 }
984 return parent::set( $key, $value, $bypass_validation );
985 }
986
987 public function nx_branding_url( $link ) {
988 $affiliate_link = $this->get( 'settings.affiliate_link' );
989 if ( ! empty( $affiliate_link ) ) {
990 $link = $affiliate_link;
991 }
992 return $link;
993 }
994
995 // @todo maybe remove
996 public function get_defaults( $key = null ) {
997 if ( empty( $this->defaults ) ) {
998 $tabs = $this->settings_form();
999 $this->defaults = NotificationX::get_instance()->get_field_names( $tabs['tabs'] );
1000 }
1001 return $this->defaults;
1002 }
1003
1004 public function miscellaneous( $return, $params ) {
1005 if ( !empty( $params['delete_transient'] ) && $params['delete_transient'] ) {
1006 Upgrader::get_instance()->clear_transient();
1007 $return = true;
1008 }
1009 return $return;
1010 }
1011 }
1012