PluginProbe
CartFlows – Funnel Builder & Checkout Plugin for WooCommerce / 3.0.1
CartFlows – Funnel Builder & Checkout Plugin for WooCommerce v3.0.1
3.3.0 3.2.1 3.2.0 3.1.4 3.1.3 3.1.2 3.1.1 3.1.0 3.0.1 trunk 1.0.4 1.1.0 1.1.0.1 1.1.1 1.1.10 1.1.11 1.1.12 1.1.13 1.1.14 1.1.15 1.1.16 1.1.17 1.1.18 1.1.19 1.1.2 All 162 releases
cartflows / admin-legacy-core / ajax / common-settings.php

common-settings.php in CartFlows – Funnel Builder & Checkout Plugin for WooCommerce 3.0.1, at admin-legacy-core/ajax/common-settings.php

636 lines 19.0 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * CartFlows Flows ajax actions.
4 *
5 * @package CartFlows
6 */
7
8 namespace CartflowsAdmin\AdminLegacyCore\Ajax;
9
10 // Exit if accessed directly.
11 if ( ! defined( 'ABSPATH' ) ) {
12 exit;
13 }
14
15 use CartflowsAdmin\AdminLegacyCore\Ajax\AjaxBase;
16 use CartflowsAdmin\AdminLegacyCore\Inc\AdminHelper;
17
18 /**
19 * Class Flows.
20 */
21 class CommonSettings extends AjaxBase {
22
23 /**
24 * Instance
25 *
26 * @access private
27 * @var object Class object.
28 * @since 1.0.0
29 */
30 private static $instance;
31
32 /**
33 * Initiator
34 *
35 * @since 1.0.0
36 * @return object initialized object of class.
37 */
38 public static function get_instance() {
39 if ( ! isset( self::$instance ) ) {
40 self::$instance = new self();
41 }
42 return self::$instance;
43 }
44
45 /**
46 * Register_ajax_events.
47 *
48 * @return void
49 */
50 public function register_ajax_events() {
51
52 if ( current_user_can( 'cartflows_manage_settings' ) ) {
53
54 $ajax_events = array(
55 'save_global_settings',
56 'regenerate_css_for_steps',
57 'track_kb_search',
58 'migrate_custom_scripts',
59 'switch_to_new_ui',
60 );
61 $this->init_ajax_events( $ajax_events );
62 }
63 }
64
65 /**
66 * Opt the user into the redesigned admin UI introduced in CartFlows 3.0.
67 * Fires from the in-app notice rendered on legacy admin screens.
68 */
69 public function switch_to_new_ui() {
70
71 if ( ! current_user_can( 'cartflows_manage_settings' ) ) {
72 wp_send_json_error( array( 'message' => $this->get_error_msg( 'permission' ) ) );
73 }
74
75 if ( ! check_ajax_referer( 'cartflows_switch_to_new_ui', 'security', false ) ) {
76 wp_send_json_error( array( 'message' => $this->get_error_msg( 'nonce' ) ) );
77 }
78
79 delete_option( 'cartflows-legacy-admin' );
80
81 wp_send_json_success(
82 array(
83 'redirect_to' => admin_url( 'admin.php?page=' . CARTFLOWS_SLUG ),
84 )
85 );
86 }
87
88 /**
89 * Delete the post meta key for dynamic css to regenerate the it.
90 */
91 public function regenerate_css_for_steps() {
92
93 $response_data = array( 'messsage' => $this->get_error_msg( 'permission' ) );
94
95 if ( ! current_user_can( 'cartflows_manage_settings' ) ) {
96 wp_send_json_error( $response_data );
97 }
98
99 /**
100 * Nonce verification
101 */
102 if ( ! check_ajax_referer( 'cartflows_regenerate_css_for_steps', 'security', false ) ) {
103 $response_data = array( 'messsage' => $this->get_error_msg( 'nonce' ) );
104 wp_send_json_error( $response_data );
105 }
106
107 // Update cartflows asset version to regenerate the dynamic css. We are using the time() function to add the random number.
108 update_option( 'cartflows-assets-version', time() );
109
110 $response_data = array(
111 'messsage' => __( 'Successfully deleted the dynamic CSS keys!', 'cartflows' ),
112 );
113 wp_send_json_success( $response_data );
114 }
115
116 /**
117 * Save settings.
118 *
119 * @return void
120 */
121 public function save_global_settings() {
122
123 $response_data = array( 'messsage' => $this->get_error_msg( 'permission' ) );
124
125 if ( ! current_user_can( 'cartflows_manage_settings' ) ) {
126 wp_send_json_error( $response_data );
127 }
128
129 if ( empty( $_POST ) ) {
130 $response_data = array( 'messsage' => __( 'No post data found!', 'cartflows' ) );
131 wp_send_json_error( $response_data );
132 }
133
134 /**
135 * Nonce verification
136 */
137 if ( ! check_ajax_referer( 'cartflows_save_global_settings', 'security', false ) ) {
138 $response_data = array( 'messsage' => $this->get_error_msg( 'nonce' ) );
139 wp_send_json_error( $response_data );
140 }
141
142
143 $setting_tab = isset( $_POST['setting_tab'] ) ? sanitize_text_field( wp_unslash( $_POST['setting_tab'] ) ) : '';
144
145 switch ( $setting_tab ) {
146
147 case 'general':
148 $this->save_general_settings();
149 break;
150
151 case 'permalink':
152 $this->save_permalink_settings();
153 break;
154
155 case 'other':
156 $this->save_other_settings();
157 break;
158
159 case 'user_role_manager':
160 $this->save_user_roles_management_settings();
161 break;
162
163 case 'integrations':
164 $this->save_integration_settings();
165 break;
166
167 case 'global_scripts':
168 $this->save_global_scripts_settings();
169 break;
170
171 default:
172 $this->save_general_settings();
173
174 }
175
176 do_action( 'cartflows_admin_save_global_settings', $setting_tab, 'cartflows_save_global_settings' );
177
178
179
180 $response_data = array(
181 'messsage' => __( 'Successfully saved data!', 'cartflows' ),
182 );
183 wp_send_json_success( $response_data );
184 }
185
186 /**
187 * Save integration tab settings. Such as Facebook & Google Analytics and Google AutoAddress.
188 *
189 * Note: Called from save_global_settings function.
190 *
191 * @return void
192 */
193 public function save_integration_settings() {
194
195 $new_settings = array();
196
197 if ( isset( $_POST['_cartflows_facebook'] ) ) { //phpcs:ignore
198 $new_settings = $this->sanitize_form_inputs( wp_unslash( $_POST['_cartflows_facebook'] ) ); //phpcs:ignore
199 AdminHelper::update_admin_settings_option( '_cartflows_facebook', $new_settings, false );
200 }
201
202 if ( isset( $_POST['_cartflows_google_analytics'] ) ) { //phpcs:ignore
203 $new_settings = $this->sanitize_form_inputs( wp_unslash( $_POST['_cartflows_google_analytics'] ) ); //phpcs:ignore
204 AdminHelper::update_admin_settings_option( '_cartflows_google_analytics', $new_settings, false );
205 }
206
207 if ( isset( $_POST['_cartflows_google_auto_address'] ) ) { //phpcs:ignore
208 $new_settings = $this->sanitize_form_inputs( wp_unslash( $_POST['_cartflows_google_auto_address'] ) ); //phpcs:ignore
209 AdminHelper::update_admin_settings_option( '_cartflows_google_auto_address', $new_settings, false );
210 }
211
212 if ( isset( $_POST['_cartflows_tiktok'] ) ) { //phpcs:ignore
213 $new_settings = $this->sanitize_form_inputs( wp_unslash( $_POST['_cartflows_tiktok'] ) ); //phpcs:ignore
214 AdminHelper::update_admin_settings_option( '_cartflows_tiktok', $new_settings, false );
215 }
216
217 if ( isset( $_POST['_cartflows_pinterest'] ) ) { //phpcs:ignore
218 $new_settings = $this->sanitize_form_inputs( wp_unslash( $_POST['_cartflows_pinterest'] ) ); //phpcs:ignore
219 AdminHelper::update_admin_settings_option( '_cartflows_pinterest', $new_settings, false );
220 }
221
222 if ( isset( $_POST['_cartflows_google_ads'] ) ) { //phpcs:ignore
223 $new_settings = $this->sanitize_form_inputs( wp_unslash( $_POST['_cartflows_google_ads'] ) ); //phpcs:ignore
224 AdminHelper::update_admin_settings_option( '_cartflows_google_ads', $new_settings, false );
225 }
226
227 if ( isset( $_POST['_cartflows_snapchat'] ) ) { //phpcs:ignore
228 $new_settings = $this->sanitize_form_inputs( wp_unslash( $_POST['_cartflows_snapchat'] ) ); //phpcs:ignore
229 AdminHelper::update_admin_settings_option( '_cartflows_snapchat', $new_settings, false );
230 }
231 }
232
233 /**
234 * Save global CSS & Scripts settings.
235 *
236 * Note: Called from save_global_settings function.
237 *
238 * Note: Global scripts are stored in the '_cartflows_global_scripts' option and are
239 * completely independent of the per-flow/per-step custom script migration. They always
240 * use CodeMirror code editor fields regardless of the migration status
241 * ('cartflows_script_migration_status'). No migration is needed for global scripts.
242 *
243 * @return void
244 */
245 public function save_global_scripts_settings() {
246
247 /**
248 * Nonce verification
249 */
250 if ( ! check_ajax_referer( 'cartflows_save_global_settings', 'security', false ) ) {
251 $response_data = array( 'message' => __( 'Nonce validation failed', 'cartflows' ) );
252 wp_send_json_error( $response_data );
253 }
254
255 // Global CSS/JS are output raw on every CartFlows page. Restrict authoring to users
256 // with `unfiltered_html` so per-plugin caps cannot grant script write access to lower roles.
257 if ( ! current_user_can( 'unfiltered_html' ) ) {
258 return;
259 }
260
261 if ( isset( $_POST['_cartflows_global_scripts'] ) ) { //phpcs:ignore WordPress.Security.NonceVerification.Missing
262 $raw_settings = wp_unslash( $_POST['_cartflows_global_scripts'] ); //phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
263
264 $new_settings = array();
265
266 if ( isset( $raw_settings['global_css'] ) ) {
267 // Decode first to prevent double-encoding on repeated saves, then encode once.
268 $new_settings['global_css'] = htmlentities( html_entity_decode( $raw_settings['global_css'], ENT_QUOTES, 'UTF-8' ), ENT_QUOTES, 'UTF-8' );
269 }
270
271 if ( isset( $raw_settings['global_js'] ) ) {
272 // Decode first to prevent double-encoding on repeated saves, then encode once.
273 $new_settings['global_js'] = htmlentities( html_entity_decode( $raw_settings['global_js'], ENT_QUOTES, 'UTF-8' ), ENT_QUOTES, 'UTF-8' );
274 }
275
276 AdminHelper::update_admin_settings_option( '_cartflows_global_scripts', $new_settings, false );
277 }
278 }
279
280 /**
281 * Save other tab settings.
282 *
283 * Note: Called from save_global_settings function.
284 *
285 * @return void
286 */
287 public function save_other_settings() {
288
289 /**
290 * Nonce verification
291 */
292 if ( ! check_ajax_referer( 'cartflows_save_global_settings', 'security', false ) ) {
293 $response_data = array( 'messsage' => __( 'Nonce validation failed', 'cartflows' ) );
294 wp_send_json_error( $response_data );
295 }
296
297 $delete_plugin_data = '';
298
299 if ( isset( $_POST['cartflows_delete_plugin_data'] ) ) {
300 $delete_plugin_data = sanitize_text_field( $_POST['cartflows_delete_plugin_data'] );
301 }
302
303 AdminHelper::update_admin_settings_option( 'cartflows_delete_plugin_data', $delete_plugin_data, false );
304
305 if ( _is_cartflows_pro() ) {
306 AdminHelper::update_admin_settings_option( 'cartflows_pro_delete_plugin_data', $delete_plugin_data, false );
307 }
308
309 if ( isset( $_POST['cartflows_stats_report_emails'] ) ) {
310 $enable_report_emails = sanitize_text_field( $_POST['cartflows_stats_report_emails'] );
311 AdminHelper::update_admin_settings_option( 'cartflows_stats_report_emails', $enable_report_emails, false );
312 }
313
314 if ( isset( $_POST['cartflows_stats_report_email_ids'] ) ) {
315
316 if ( ! empty( $_POST['cartflows_stats_report_email_ids'] ) ) {
317 // Ignoring the sanitization rule as we are sanitizing each email before saving it.
318 $emails = preg_split( "/[\f\r\n]+/", $_POST['cartflows_stats_report_email_ids'] ); //phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
319 $validated_emails = array();
320
321 foreach ( $emails as $email_id ) {
322
323 if ( is_email( $email_id ) ) {
324 array_push( $validated_emails, sanitize_email( $email_id ) );
325 }
326 }
327 $validated_emails = implode( "\n", $validated_emails );
328 AdminHelper::update_admin_settings_option( 'cartflows_stats_report_email_ids', $validated_emails, false );
329 } else {
330 AdminHelper::update_admin_settings_option( 'cartflows_stats_report_email_ids', '', false );
331 }
332 }
333
334 // Update the option to enable/disable the non sensitive data tracking.
335 if ( isset( $_POST['cf_usage_optin'] ) ) {
336 $enable_non_sensative_data_tracking = sanitize_text_field( $_POST['cf_usage_optin'] );
337 AdminHelper::update_admin_settings_option( 'cf_usage_optin', $enable_non_sensative_data_tracking, false );
338 }
339 }
340
341 /**
342 * Save common settings of CartFlows. Like Default Page Builder and Store Checkout.
343 *
344 * Note: Called from save_global_settings function.
345 *
346 * @return void
347 */
348 public function save_general_settings() {
349
350 /**
351 * Nonce verification
352 */
353 if ( ! check_ajax_referer( 'cartflows_save_global_settings', 'security', false ) ) {
354 $response_data = array( 'messsage' => __( 'Nonce validation failed', 'cartflows' ) );
355 wp_send_json_error( $response_data );
356 }
357
358 $new_settings = array();
359
360 if ( isset( $_POST['_cartflows_common'] ) ) {
361 // Ignoring the sanitization rule as we are sanitizing input using our function.
362 $new_settings = $this->sanitize_form_inputs( wp_unslash( $_POST['_cartflows_common'] ) ); //phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
363 }
364
365 $common_settings = AdminHelper::get_admin_settings_option( '_cartflows_common', false, false );
366 $new_settings = wp_parse_args( $new_settings, $common_settings );
367
368 AdminHelper::update_admin_settings_option( '_cartflows_common', $new_settings, false );
369 }
370
371 /**
372 * Remove cf caps.
373 *
374 * @param object $user_role_obj user role object.
375 *
376 * @return void
377 */
378 public function remove_all_cf_cap( $user_role_obj ) {
379
380 $cf_cap = array(
381 'cartflows_manage_settings',
382 'cartflows_manage_flows_steps',
383 );
384
385 foreach ( $cf_cap as $cap ) {
386 $user_role_obj->remove_cap( $cap );
387 }
388 }
389
390 /**
391 * Add cf caps.
392 *
393 * @param object $user_role_obj user role object.
394 * @param string $access_key access key.
395 *
396 * @return void
397 */
398 public function add_selected_cf_cap( $user_role_obj, $access_key ) {
399
400 // Security: Validate access_key against allowlist to prevent privilege escalation.
401 $allowed_keys = array( 'access_to_cartflows', 'access_to_flows_and_step', 'no_access' );
402
403 if ( ! in_array( $access_key, $allowed_keys, true ) ) {
404 return;
405 }
406
407 switch ( $access_key ) {
408
409 case 'access_to_cartflows':
410 $user_role_obj->add_cap( 'cartflows_manage_settings' );
411 $user_role_obj->add_cap( 'cartflows_manage_flows_steps' );
412 break;
413
414 case 'access_to_flows_and_step':
415 $user_role_obj->add_cap( 'cartflows_manage_flows_steps' );
416 break;
417
418 default:
419 // No capabilities to add for 'no_access'.
420 break;
421
422 }
423 }
424
425 /**
426 * Add / Remove custom capability to the user role.
427 *
428 * @param array $new_settings Array of user role capability settings.
429 * @param array $old_settings Array of old user role capability settings.
430 *
431 * @return void
432 */
433 public function user_role_management( $new_settings, $old_settings ) {
434
435 // Security: Protect administrator role from modification via this endpoint.
436 $protected_roles = array( 'administrator' );
437
438 foreach ( $new_settings as $user_role => $access_key ) {
439
440 if ( in_array( $user_role, $protected_roles, true ) ) {
441 continue;
442 }
443
444 if ( ! isset( $old_settings[ $user_role ] ) || $old_settings[ $user_role ] !== $access_key ) {
445
446 $user_role_obj = get_role( $user_role );
447
448 if ( $user_role_obj ) {
449
450 $this->remove_all_cf_cap( $user_role_obj );
451
452 $this->add_selected_cf_cap( $user_role_obj, $access_key );
453
454 }
455 }
456 }
457 }
458
459 /**
460 * Save user role settings.
461 *
462 * Note: Called from save_global_settings function.
463 *
464 * @return void
465 */
466 public function save_user_roles_management_settings() {
467
468 /**
469 * Nonce verification
470 */
471 if ( ! check_ajax_referer( 'cartflows_save_global_settings', 'security', false ) ) {
472 $response_data = array( 'messsage' => __( 'Nonce validation failed', 'cartflows' ) );
473 wp_send_json_error( $response_data );
474 }
475
476 $new_settings = array();
477
478 if ( isset( $_POST['_cartflows_roles'] ) ) {
479 $new_settings = $this->sanitize_form_inputs( wp_unslash( $_POST['_cartflows_roles'] ) ); //phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
480 }
481
482 $old_settings = AdminHelper::get_admin_settings_option( '_cartflows_roles' );
483
484 $new_settings = wp_parse_args( $new_settings, $old_settings );
485
486 AdminHelper::update_admin_settings_option( '_cartflows_roles', $new_settings, false );
487
488 // Add/Remove capability.
489 $this->user_role_management( $new_settings, $old_settings );
490 }
491
492 /**
493 * Save the permalink settings.
494 *
495 * Note: Called from save_global_settings function.
496 *
497 * @return void
498 */
499 public function save_permalink_settings() {
500
501 /**
502 * Nonce verification
503 */
504 if ( ! check_ajax_referer( 'cartflows_save_global_settings', 'security', false ) ) {
505 $response_data = array( 'messsage' => __( 'Nonce validation failed', 'cartflows' ) );
506 wp_send_json_error( $response_data );
507 }
508
509 if ( isset( $_POST['reset'] ) ) {
510 $_POST['_cartflows_permalink'] = array(
511 'permalink' => CARTFLOWS_STEP_PERMALINK_SLUG,
512 'permalink_flow_base' => CARTFLOWS_FLOW_PERMALINK_SLUG,
513 'permalink_structure' => '',
514 );
515
516 }
517 $new_settings = array();
518 if ( isset( $_POST['_cartflows_permalink'] ) ) {
519 $cartflows_permalink_settings = $this->sanitize_form_inputs( wp_unslash( $_POST['_cartflows_permalink'] ) ); //phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
520
521 if ( empty( $cartflows_permalink_settings['permalink'] ) ) {
522 $new_settings['permalink'] = CARTFLOWS_STEP_PERMALINK_SLUG;
523 } else {
524 $new_settings['permalink'] = $cartflows_permalink_settings['permalink'];
525 }
526
527 if ( empty( $cartflows_permalink_settings['permalink_flow_base'] ) ) {
528 $new_settings['permalink_flow_base'] = CARTFLOWS_FLOW_PERMALINK_SLUG;
529 } else {
530 $new_settings['permalink_flow_base'] = $cartflows_permalink_settings['permalink_flow_base'];
531 }
532
533 $new_settings['permalink_structure'] = $cartflows_permalink_settings['permalink_structure'];
534
535 }
536
537 AdminHelper::update_admin_settings_option( '_cartflows_permalink', $new_settings, true );
538
539 update_option( 'cartflows_permalink_refresh', true );
540 }
541
542
543 /**
544 * Save settings.
545 *
546 * @param array $input_settings settimg data.
547 */
548 public function sanitize_form_inputs( $input_settings = array() ) {
549 $new_settings = array();
550 foreach ( $input_settings as $key => $val ) {
551
552 if ( is_array( $val ) ) {
553 foreach ( $val as $k => $v ) {
554 $new_settings[ $key ][ $k ] = ( isset( $val[ $k ] ) ) ? sanitize_text_field( $v ) : '';
555 }
556 } else {
557 $new_settings[ $key ] = ( isset( $input_settings[ $key ] ) ) ? sanitize_text_field( $val ) : '';
558 }
559 }
560 return $new_settings;
561 }
562
563 /**
564 * Track KB search terms.
565 *
566 * @return void
567 */
568 public function track_kb_search() {
569 if ( ! current_user_can( 'cartflows_manage_settings' ) ) {
570 wp_send_json_error( array( 'message' => $this->get_error_msg( 'permission' ) ) );
571 }
572
573 /**
574 * Nonce verification
575 */
576 if ( ! check_ajax_referer( 'cartflows_track_kb_search', 'security', false ) ) {
577 $response_data = array( 'messsage' => __( 'Nonce validation failed', 'cartflows' ) );
578 wp_send_json_error( $response_data );
579 }
580
581 // Get search term.
582 $search_term = isset( $_POST['search_term'] ) ? sanitize_text_field( wp_unslash( $_POST['search_term'] ) ) : '';
583
584 if ( empty( $search_term ) ) {
585 wp_send_json_error( array( 'message' => 'Search term is empty' ) );
586 }
587
588 // Store the search term for BSF Analytics.
589 $kb_searches = get_option( 'cartflows_kb_searches', array() );
590
591 // Add the current search term to the list.
592 $kb_searches[] = $search_term;
593
594 // Keep only the last 20 searches to avoid data overload.
595 $kb_searches = array_slice( $kb_searches, -20 );
596
597 // Update the option.
598 update_option( 'cartflows_kb_searches', $kb_searches );
599
600 wp_send_json_success( array( 'message' => 'Search term tracked successfully' ) );
601 }
602
603 /**
604 * Handle on-demand custom script migration triggered by user clicking "Migrate Data".
605 *
606 * Verifies nonce and capabilities, calls the migration logic from Cartflows_Update,
607 * and returns a JSON response with the migrated post count.
608 *
609 * @since 2.2.2
610 * @return void
611 */
612 public function migrate_custom_scripts() {
613
614 $response_data = array( 'message' => $this->get_error_msg( 'permission' ) );
615
616 if ( ! current_user_can( 'cartflows_manage_settings' ) ) {
617 wp_send_json_error( $response_data );
618 }
619
620 if ( ! check_ajax_referer( 'cartflows_migrate_custom_scripts', 'security', false ) ) {
621 $response_data = array( 'message' => $this->get_error_msg( 'nonce' ) );
622 wp_send_json_error( $response_data );
623 }
624
625 $migrated_count = \Cartflows_Update::get_instance()->migrate_custom_scripts_on_demand();
626
627 wp_send_json_success(
628 array(
629 /* translators: %d: number of posts migrated */
630 'message' => sprintf( __( 'Migration completed successfully. %d post(s) migrated.', 'cartflows' ), $migrated_count ),
631 'migrated_count' => $migrated_count,
632 )
633 );
634 }
635 }
636