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 / importer.php

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

1,655 lines 55.3 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Importer
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\Inc\AdminMenu;
16 use CartflowsAdmin\AdminLegacyCore\Ajax\AjaxBase;
17 use CartflowsAdmin\AdminLegacyCore\Inc\AdminHelper;
18
19 /**
20 * Importer.
21 */
22 class Importer extends AjaxBase {
23
24 /**
25 * Instance
26 *
27 * @access private
28 * @var object Class object.
29 * @since 1.0.0
30 */
31 private static $instance;
32
33 /**
34 * Initiator
35 *
36 * @since 1.0.0
37 * @return object initialized object of class.
38 */
39 public static function get_instance() {
40 if ( ! isset( self::$instance ) ) {
41 self::$instance = new self();
42 }
43 return self::$instance;
44 }
45
46 /**
47 * Register AJAX Events.
48 *
49 * @since 1.0.0
50 * @return void
51 */
52 public function register_ajax_events() {
53
54 $ajax_events = array(
55 'create_flow',
56 'import_flow',
57
58 'create_step',
59
60 'import_step',
61
62 'activate_plugin',
63 'activate_theme',
64
65 'sync_library',
66 'request_count',
67 'import_sites',
68 'update_library_complete',
69 'export_flow',
70
71 'get_flows_list',
72
73 'import_json_flow',
74 'export_all_flows',
75 'update_step',
76 );
77
78 $this->init_ajax_events( $ajax_events );
79
80 add_action( 'admin_footer', array( $this, 'json_importer_popup_wrapper' ) );
81 add_action( 'wp_ajax_cartflows_install_plugin', 'wp_ajax_install_plugin' );
82 }
83
84 /**
85 * Export Flows.
86 *
87 * @since 1.0.0
88 * @return void
89 */
90 public function export_all_flows() {
91
92 $response_data = array( 'message' => $this->get_error_msg( 'permission' ) );
93
94 if ( ! current_user_can( 'cartflows_manage_flows_steps' ) ) {
95 wp_send_json_error( $response_data );
96 }
97
98 if ( ! check_ajax_referer( 'cartflows_export_all_flows', 'security', false ) ) {
99 $response_data = array( 'message' => $this->get_error_msg( 'nonce' ) );
100 wp_send_json_error( $response_data );
101 }
102
103 $export = \CartFlows_Importer::get_instance();
104 $flows = $export->get_all_flow_export_data();
105
106 if ( ! empty( $flows ) && is_array( $flows ) && count( $flows ) > 0 ) {
107
108 $response_data = array(
109 'message' => __( 'Funnel exported successfully', 'cartflows' ),
110 'flows' => wp_json_encode( $flows ),
111 'export' => true,
112 );
113
114 } else {
115 $response_data = array(
116 'message' => __( 'No Funnels to export', 'cartflows' ),
117 'flows' => $flows,
118 'export' => false,
119 );
120 }
121
122 wp_send_json_success( $response_data );
123 }
124
125 /**
126 * Import the Flow.
127 *
128 * @since 1.0.0
129 * @return void
130 */
131 public function import_json_flow() {
132
133 $response_data = array( 'message' => $this->get_error_msg( 'permission' ) );
134
135 if ( ! current_user_can( 'cartflows_manage_flows_steps' ) ) {
136 wp_send_json_error( $response_data );
137 }
138
139 /**
140 * Nonce verification
141 */
142 if ( ! check_ajax_referer( 'cartflows_import_json_flow', 'security', false ) ) {
143 $response_data = array( 'message' => $this->get_error_msg( 'nonce' ) );
144 wp_send_json_error( $response_data );
145 }
146
147 // $_POST['flow_data'] is the JSON, There is nothing to sanitize JSON as it is data format not data type.
148 $flow_data = ( isset( $_POST['flow_data'] ) ) ? json_decode( stripslashes( $_POST['flow_data'] ), true ) : array(); //phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
149 $check_store_checkout = isset( $_POST['check_store_checkout'] ) ? sanitize_text_field( wp_unslash( $_POST['check_store_checkout'] ) ) : 'no'; //phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
150 $force_import = isset( $_POST['force_import'] ) ? sanitize_text_field( wp_unslash( $_POST['force_import'] ) ) : 'no'; //phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
151
152 $response_data = array(
153 'message' => 'Error occured. Funnel not imported.',
154 'flow_data' => $flow_data,
155 'redirect_url' => admin_url( 'admin.php?page=' . CARTFLOWS_SLUG ),
156 );
157
158 // Check if this is a Store Checkout flow and if one already exists.
159 if ( 'yes' === $check_store_checkout && 'yes' !== $force_import ) {
160 $existing_store_checkout = \Cartflows_Helper::get_global_setting( '_cartflows_store_checkout' );
161
162 if ( $existing_store_checkout ) {
163 // Send confirmation prompt to the user.
164 wp_send_json_success(
165 array(
166 'requires_confirmation' => true,
167 'message' => __( 'A Store Checkout funnel already exists. Importing this funnel will replace the current Store Checkout funnel.', 'cartflows' ),
168 'flow_data' => $flow_data,
169 )
170 );
171 }
172 }
173
174 if ( is_array( $flow_data ) ) {
175 // Set the flag as true to check for the import process is started for the CartFlows. So as to import/upload the required files.
176 \CartFlows_Batch_Process::set_is_wcf_template_import( true );
177
178 $imported_flow = \CartFlows_Importer::get_instance()->import_from_json_data( $flow_data );
179
180 // Set the flag as false once the template import is complete.
181 \CartFlows_Batch_Process::set_is_wcf_template_import( false );
182
183 $response_data['message'] = 'Funnel Imported successfully';
184 $response_data['redirect_url'] = admin_url( 'admin.php?page=' . CARTFLOWS_SLUG . '&path=flows' );
185
186 }
187
188 wp_send_json_success( $response_data );
189 }
190
191 /**
192 * Import Wrapper.
193 *
194 * @since 1.0.0
195 * @return void
196 */
197 public function json_importer_popup_wrapper() {
198 echo '<div id="wcf-json-importer"></div>';
199 }
200
201 /**
202 * Export Step
203 */
204 public function export_flow() {
205
206 $response_data = array( 'message' => $this->get_error_msg( 'permission' ) );
207
208 if ( ! current_user_can( 'cartflows_manage_flows_steps' ) ) {
209 wp_send_json_error( $response_data );
210 }
211
212 /**
213 * Nonce verification
214 */
215 if ( ! check_ajax_referer( 'cartflows_export_flow', 'security', false ) ) {
216 $response_data = array( 'message' => $this->get_error_msg( 'nonce' ) );
217 wp_send_json_error( $response_data );
218 }
219 $flow_id = ( isset( $_POST['flow_id'] ) ) ? absint( $_POST['flow_id'] ) : '';
220
221 if ( ! $flow_id ) {
222 $response_data = array( 'message' => __( 'Invalid flow ID.', 'cartflows' ) );
223 wp_send_json_error( $response_data );
224 }
225
226 $flows[] = \CartFlows_Importer::get_instance()->get_flow_export_data( $flow_id );
227
228 $response_data = array(
229 'message' => __( 'Funnel exported successfully', 'cartflows' ),
230 'flow_name' => sanitize_title( get_the_title( $flow_id ) ),
231 'flows' => wp_json_encode( $flows ),
232 );
233 wp_send_json_success( $response_data );
234 }
235
236 /**
237 * Update library complete
238 */
239 public function update_library_complete() {
240 $response_data = array( 'message' => $this->get_error_msg( 'permission' ) );
241 if ( ! current_user_can( 'cartflows_manage_flows_steps' ) ) {
242 wp_send_json_error( $response_data );
243 }
244
245 /**
246 * Nonce verification
247 */
248 if ( ! check_ajax_referer( 'cartflows_update_library_complete', 'security', false ) ) {
249 $response_data = array( 'message' => $this->get_error_msg( 'nonce' ) );
250 wp_send_json_error( $response_data );
251 }
252
253 $templates = ! empty( $_POST['template'] ) ? sanitize_text_field( wp_unslash( $_POST['template'] ) ) : '';
254
255 \CartFlows_Batch_Process::get_instance()->update_latest_checksums( $templates );
256
257 update_site_option( 'cartflows-batch-is-complete', 'no' );
258 update_site_option( 'cartflows-manual-sync-complete', 'yes' );
259
260 $response_data = array( 'message' => 'SUCCESS: cartflows_import_sites' );
261 wp_send_json_success( $response_data );
262 }
263
264 /**
265 * Import Sites
266 */
267 public function import_sites() {
268 $response_data = array( 'message' => $this->get_error_msg( 'permission' ) );
269 if ( ! current_user_can( 'cartflows_manage_flows_steps' ) ) {
270 wp_send_json_error( $response_data );
271 }
272
273 /**
274 * Nonce verification
275 */
276 if ( ! check_ajax_referer( 'cartflows_import_sites', 'security', false ) ) {
277 $response_data = array( 'message' => $this->get_error_msg( 'nonce' ) );
278 wp_send_json_error( $response_data );
279 }
280
281 $page_no = isset( $_POST['page_no'] ) ? absint( $_POST['page_no'] ) : '';
282 $template = isset( $_POST['template'] ) ? sanitize_text_field( wp_unslash( $_POST['template'] ) ) : '';
283 if ( $page_no ) {
284 $sites_and_pages = \Cartflows_Batch_Processing_Sync_Library::get_instance()->import_sites( $page_no, $template );
285 wp_send_json_success(
286 array(
287 'message' => 'SUCCESS: cartflows_import_sites',
288 'sites_and_pages' => $sites_and_pages,
289 )
290 );
291 }
292
293 wp_send_json_error(
294 array(
295 'message' => 'SUCCESS: cartflows_import_sites',
296 )
297 );
298 }
299
300 /**
301 * Sync Library
302 */
303 public function sync_library() {
304 $response_data = array( 'message' => $this->get_error_msg( 'permission' ) );
305 if ( ! current_user_can( 'cartflows_manage_flows_steps' ) ) {
306 wp_send_json_error( $response_data );
307 }
308
309 /**
310 * Nonce verification
311 */
312 if ( ! check_ajax_referer( 'cartflows_sync_library', 'security', false ) ) {
313 $response_data = array( 'message' => $this->get_error_msg( 'nonce' ) );
314 wp_send_json_error( $response_data );
315 }
316
317 $templates = ! empty( $_POST['template'] ) ? sanitize_text_field( wp_unslash( $_POST['template'] ) ) : '';
318
319 /**
320 * LOGIC
321 */
322 if ( 'no' === \CartFlows_Batch_Process::get_instance()->get_last_export_checksums( $templates ) ) {
323 wp_send_json_success( 'updated' );
324 }
325
326 $status = \CartFlows_Batch_Process::get_instance()->test_cron();
327 if ( is_wp_error( $status ) ) {
328 $import_with = 'ajax';
329 } else {
330 $import_with = 'batch';
331 // Process import.
332 \CartFlows_Batch_Process::get_instance()->process_batch( $templates );
333 }
334
335 $response_data = array(
336 'message' => 'SUCCESS: cartflows_sync_library',
337 'status' => $import_with,
338 );
339
340 wp_send_json_success( $response_data );
341 }
342
343 /**
344 * Request Count
345 */
346 public function request_count() {
347 $response_data = array( 'message' => $this->get_error_msg( 'permission' ) );
348 if ( ! current_user_can( 'cartflows_manage_flows_steps' ) ) {
349 wp_send_json_error( $response_data );
350 }
351
352 /**
353 * Nonce verification
354 */
355 if ( ! check_ajax_referer( 'cartflows_request_count', 'security', false ) ) {
356 $response_data = array( 'message' => $this->get_error_msg( 'nonce' ) );
357 wp_send_json_error( $response_data );
358 }
359
360 $templates = ! empty( $_POST['template'] ) ? sanitize_text_field( wp_unslash( $_POST['template'] ) ) : '';
361
362 $total_requests = \CartFlows_Batch_Process::get_instance()->get_total_requests( '', $templates );
363 if ( $total_requests ) {
364 wp_send_json_success(
365 array(
366 'message' => 'SUCCESS: cartflows_request_count',
367 'count' => $total_requests,
368 )
369 );
370 }
371
372 wp_send_json_error(
373 array(
374 'message' => 'ERROR: cartflows_request_count',
375 'count' => $total_requests,
376 )
377 );
378 }
379
380 /**
381 * Create Step
382 */
383 public function create_step() {
384
385 $response_data = array( 'message' => $this->get_error_msg( 'permission' ) );
386
387 if ( ! current_user_can( 'cartflows_manage_flows_steps' ) ) {
388 wp_send_json_error( $response_data );
389 }
390
391 /**
392 * Nonce verification
393 */
394 if ( ! check_ajax_referer( 'cartflows_create_step', 'security', false ) ) {
395 $response_data = array( 'message' => $this->get_error_msg( 'nonce' ) );
396 wp_send_json_error( $response_data );
397 }
398
399 wcf()->logger->import_log( 'STARTED! Importing Step' );
400
401 $flow_id = ( isset( $_POST['flow_id'] ) ) ? absint( $_POST['flow_id'] ) : 0;
402
403 if ( CARTFLOWS_FLOW_POST_TYPE !== get_post_type( $flow_id ) ) {
404 wp_send_json_error(
405 array(
406 array(
407 'status' => false,
408 'message' => __( 'Invalid Funnel Id has been provided.', 'cartflows' ),
409 ),
410 )
411 );
412 }
413
414 $step_type = ( isset( $_POST['step_type'] ) ) ? sanitize_text_field( $_POST['step_type'] ) : '';
415 $step_title = ( isset( $_POST['step_title'] ) ) ? sanitize_text_field( $_POST['step_title'] ) : '';
416 $step_title = isset( $_POST['step_name'] ) && ! empty( $_POST['step_name'] ) ? sanitize_text_field( wp_unslash( $_POST['step_name'] ) ) : $step_title;
417
418 // Create new step.
419 $new_step_id = \CartFlows_Importer::get_instance()->create_step( $flow_id, $step_type, $step_title );
420
421 if ( empty( $new_step_id ) ) {
422 /* translators: %s: step ID */
423 wp_send_json_error( sprintf( __( 'Invalid step id %1$s.', 'cartflows' ), $new_step_id ) );
424 }
425
426 /**
427 * Redirect to the new flow edit screen
428 */
429 $response_data = array(
430 'message' => __( 'Successfully created the step!', 'cartflows' ),
431 'redirect_url' => admin_url( 'post.php?action=edit&post=' . $new_step_id ),
432 );
433 wp_send_json_success( $response_data );
434 }
435
436 /**
437 * Active Plugin
438 */
439 public function activate_plugin() {
440 $response_data = array( 'message' => $this->get_error_msg( 'permission' ) );
441
442 if ( ! current_user_can( 'cartflows_manage_flows_steps' ) || ! current_user_can( 'activate_plugins' ) ) {
443 wp_send_json_error( $response_data );
444 }
445
446 /**
447 * Nonce verification
448 */
449 if ( ! check_ajax_referer( 'cartflows_activate_plugin', 'security', false ) ) {
450 $response_data = array( 'message' => $this->get_error_msg( 'nonce' ) );
451 wp_send_json_error( $response_data );
452 }
453
454 \wp_clean_plugins_cache();
455
456 $plugin_init = ( isset( $_POST['init'] ) ) ? sanitize_text_field( $_POST['init'] ) : '';
457
458 $do_sliently = true;
459
460 $exclude_do_silently = array(
461 'woo-cart-abandonment-recovery/woo-cart-abandonment-recovery.php',
462 );
463
464 if ( in_array( $plugin_init, $exclude_do_silently, true ) ) {
465 $do_sliently = false;
466 }
467
468 $activate = \activate_plugin( $plugin_init, '', false, $do_sliently );
469
470 if ( is_wp_error( $activate ) ) {
471 wp_send_json_error(
472 array(
473 'success' => false,
474 'message' => $activate->get_error_message(),
475 )
476 );
477 }
478
479
480 if ( class_exists( '\BSF_UTM_Analytics' ) && is_callable( '\BSF_UTM_Analytics::update_referer' ) ) {
481 $plugin_slug = pathinfo( $plugin_init, PATHINFO_FILENAME ); // Retrives the plugin slug from the init.
482 \BSF_UTM_Analytics::update_referer( 'cartflows', $plugin_slug );
483 }
484
485 wp_send_json_success(
486 array(
487 'success' => true,
488 'message' => 'Plugin activated successfully.',
489 )
490 );
491 }
492
493 /**
494 * Activate theme
495 *
496 * @since 2.0.12
497 * @return void
498 */
499 public function activate_theme() {
500
501 // Verify Nonce.
502 $response_data = array( 'message' => $this->get_error_msg( 'permission' ) );
503
504 if ( ! current_user_can( 'cartflows_manage_flows_steps' ) || ! current_user_can( 'switch_themes' ) ) {
505 wp_send_json_error( $response_data );
506 }
507
508 /**
509 * Nonce verification
510 */
511 if ( ! check_ajax_referer( 'cartflows_activate_theme', 'security', false ) ) {
512 $response_data = array( 'message' => $this->get_error_msg( 'nonce' ) );
513 wp_send_json_error( $response_data );
514 }
515
516 // Check the theme slug is available or not.
517 $theme_slug = ( isset( $_POST['theme_slug'] ) ) ? sanitize_text_field( $_POST['theme_slug'] ) : '';
518
519 // If the theme slug is not available then bail.
520 if ( empty( $theme_slug ) ) {
521 $response_data = array( 'message' => $this->get_error_msg( 'parameter' ) );
522 wp_send_json_error( $response_data );
523 }
524
525 // Pass the theme slug and switch the theme and activate it.
526 switch_theme( $theme_slug );
527
528 wp_send_json_success(
529 array(
530 'success' => true,
531 'message' => __( 'Theme Activated', 'cartflows' ),
532 )
533 );
534 }
535
536 /**
537 * Create the Flow.
538 */
539 public function create_flow() {
540
541 $response_data = array( 'message' => $this->get_error_msg( 'permission' ) );
542
543 if ( ! current_user_can( 'cartflows_manage_flows_steps' ) ) {
544 wp_send_json_error( $response_data );
545 }
546
547 /**
548 * Nonce verification
549 */
550 if ( ! check_ajax_referer( 'cartflows_create_flow', 'security', false ) ) {
551 $response_data = array( 'message' => $this->get_error_msg( 'nonce' ) );
552 wp_send_json_error( $response_data );
553 }
554
555 // Create post object.
556 $new_flow_post = array(
557 'post_title' => isset( $_POST['flow_name'] ) ? sanitize_text_field( wp_unslash( $_POST['flow_name'] ) ) : '',
558 'post_content' => '',
559 'post_status' => 'publish',
560 'post_type' => CARTFLOWS_FLOW_POST_TYPE,
561 );
562
563 // Insert the post into the database.
564 $flow_id = wp_insert_post( $new_flow_post );
565
566 if ( is_wp_error( $flow_id ) ) {
567 wp_send_json_error( $flow_id->get_error_message() );
568 }
569
570 $store_checkout = isset( $_POST['store_checkout'] ) ? sanitize_text_field( wp_unslash( $_POST['store_checkout'] ) ) : '';
571
572 // If is store checkout update store_checkout options.
573 if ( 'true' === $store_checkout ) {
574 update_option( '_cartflows_store_checkout', $flow_id );
575
576 // reset global checkout on store checkout creation.
577 $common_settings = \Cartflows_Helper::get_common_settings();
578 $common_settings['global_checkout'] = '';
579 $common_settings['override_global_checkout'] = 'disable';
580
581 update_option( '_cartflows_common', $common_settings );
582 }
583
584 $flow_steps = array();
585
586 if ( wcf()->is_woo_active ) {
587 if ( 'true' === $store_checkout ) {
588 $steps_data = array(
589 'order-form' => array(
590 'title' => __( 'Checkout', 'cartflows' ),
591 'type' => 'checkout',
592 ),
593 'order-confirmation' => array(
594 'title' => __( 'Thank You', 'cartflows' ),
595 'type' => 'thankyou',
596 ),
597 );
598 } else {
599 $steps_data = array(
600 'sales' => array(
601 'title' => __( 'Sales Landing', 'cartflows' ),
602 'type' => 'landing',
603 ),
604 'order-form' => array(
605 'title' => __( 'Checkout', 'cartflows' ),
606 'type' => 'checkout',
607 ),
608 'order-confirmation' => array(
609 'title' => __( 'Thank You', 'cartflows' ),
610 'type' => 'thankyou',
611 ),
612 );
613 }
614 } else {
615 $steps_data = array(
616 'landing' => array(
617 'title' => __( 'Landing', 'cartflows' ),
618 'type' => 'landing',
619 ),
620 'thankyou' => array(
621 'title' => __( 'Thank You', 'cartflows' ),
622 'type' => 'landing',
623 ),
624 );
625 }
626
627 foreach ( $steps_data as $slug => $data ) {
628
629 $post_content = '';
630 $step_type = trim( $data['type'] );
631
632 // Create new step.
633 $step_id = wp_insert_post(
634 array(
635 'post_type' => CARTFLOWS_STEP_POST_TYPE,
636 'post_title' => $data['title'],
637 'post_content' => $post_content,
638 'post_status' => 'publish',
639 )
640 );
641
642 // Return the error.
643 if ( is_wp_error( $step_id ) ) {
644 wp_send_json_error( $step_id->get_error_message() );
645 }
646
647 if ( $step_id ) {
648
649 $flow_steps[] = array(
650 'id' => $step_id,
651 'title' => $data['title'],
652 'type' => $step_type,
653 );
654
655 // Insert post meta.
656 update_post_meta( $step_id, 'wcf-flow-id', $flow_id );
657 update_post_meta( $step_id, 'wcf-step-type', $step_type );
658
659 // Set taxonomies.
660 wp_set_object_terms( $step_id, $step_type, CARTFLOWS_TAXONOMY_STEP_TYPE );
661 wp_set_object_terms( $step_id, 'flow-' . $flow_id, CARTFLOWS_TAXONOMY_STEP_FLOW );
662
663 update_post_meta( $step_id, '_wp_page_template', 'cartflows-default' );
664 }
665 }
666
667 update_post_meta( $flow_id, 'wcf-steps', $flow_steps );
668
669 // Enable the Instant Layout for the flow for all page builders if the funnel is created from scratch.
670 update_post_meta( $flow_id, 'instant-layout-style', 'yes' );
671
672 // Track funnel creation method for analytics.
673 $creation_method = isset( $_POST['creation_method'] ) ? sanitize_text_field( wp_unslash( $_POST['creation_method'] ) ) : 'scratch';
674 AdminHelper::track_funnel_creation_method( $creation_method );
675
676 /**
677 * Redirect to the new flow edit screen
678 */
679 $response_data = array(
680 'message' => __( 'Successfully created the Funnel!', 'cartflows' ),
681 'redirect_url' => admin_url( 'post.php?action=edit&post=' . $flow_id ),
682 'flow_id' => $flow_id,
683 );
684 wp_send_json_success( $response_data );
685 }
686
687 /**
688 * Create the Flow.
689 */
690 public function import_flow() {
691
692 wcf()->logger->import_log( 'STARTED! Importing Flow' );
693
694 $response_data = array( 'message' => $this->get_error_msg( 'permission' ) );
695
696 if ( ! current_user_can( 'cartflows_manage_flows_steps' ) ) {
697 wp_send_json_error( $response_data );
698 }
699
700 /**
701 * Nonce verification
702 */
703 if ( ! check_ajax_referer( 'cartflows_import_flow', 'security', false ) ) {
704 $response_data = array( 'message' => $this->get_error_msg( 'nonce' ) );
705 wp_send_json_error( $response_data );
706 }
707 // $_POST['flow'] is the JSON, There is nothing to sanitize JSON as it is data format not data type.
708 $flow = isset( $_POST['flow'] ) ? json_decode( stripslashes( $_POST['flow'] ), true ) : array(); //phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
709
710 // Get single step Rest API response.
711 $response = \CartFlows_API::get_instance()->get_flow( $flow['ID'] );
712
713 $is_error = AdminHelper::has_api_error( $response['data'] );
714
715 if ( $is_error['error'] ) {
716
717 wp_send_json_error(
718 array(
719 'error_code' => $is_error['error_code'],
720 'call_to_action' => $is_error['call_to_action'],
721 'message' => $is_error['error_message'],
722 'data' => $response,
723 )
724 );
725 }
726
727 $license_status = isset( $response['data']['licence_status'] ) ? $response['data']['licence_status'] : '';
728
729 // If license is invalid then.
730 if ( 'valid' !== $license_status ) {
731
732 $cf_pro_status = AdminMenu::get_instance()->get_plugin_status( 'cartflows-pro/cartflows-pro.php' );
733
734 $cta = '';
735 $btn = '';
736 if ( 'not-installed' === $cf_pro_status ) {
737 /* translators: %1$s: link html start, %2$s: link html end*/
738 $btn = sprintf( __( 'CartFlows Pro Required! %1$sUpgrade to CartFlows Pro%2$s', 'cartflows' ), '<a target="_blank" href="https://cartflows.com/?utm_source=dashboard&utm_medium=free-cartflows&utm_campaign=go-pro">', '</a>' );
739 /* translators: %1$s: link html start, %2$s: link html end*/
740 $cta = sprintf( __( 'To import the premium flow %1$supgrade to CartFlows Pro%2$s.', 'cartflows' ), '<a target="_blank" href="https://cartflows.com/?utm_source=dashboard&utm_medium=free-cartflows&utm_campaign=go-pro">', '</a>' );
741 } elseif ( 'inactive' === $cf_pro_status ) {
742 /* translators: %1$s: link html start, %2$s: link html end*/
743 $btn = sprintf( __( 'Activate the CartFlows Pro to import the flow! %1$sActivate CartFlows Pro%2$s', 'cartflows' ), '<a target="_blank" href="' . admin_url( 'plugins.php?plugin_status=search&paged=1&s=CartFlows+Pro' ) . '">', '</a>' );
744 /* translators: %1$s: link html start, %2$s: link html end*/
745 $cta = sprintf( __( 'To import the premium flow %1$sactivate Cartflows Pro%2$s and validate the license key.', 'cartflows' ), '<a target="_blank" href="' . admin_url( 'plugins.php?plugin_status=search&paged=1&s=CartFlows+Pro' ) . '">', '</a>' );
746 } elseif ( 'active' === $cf_pro_status ) {
747 /* translators: %1$s: link html start, %2$s: link html end*/
748 $btn = sprintf( __( 'Invalid License Key! %1$sActivate CartFlows Pro%2$s', 'cartflows' ), '<a target="_blank" href="' . admin_url( 'plugins.php?cartflows-license-popup' ) . '">', '</a>' );
749 /* translators: %1$s: link html start, %2$s: link html end*/
750 $cta = sprintf( __( 'To import the premium flow %1$sactivate CartFlows Pro%2$s.', 'cartflows' ), '<a target="_blank" href="' . admin_url( 'plugins.php?cartflows-license-popup' ) . '">', '</a>' );
751 }
752
753 wp_send_json_error(
754 array(
755 'message' => \ucfirst( $license_status ) . ' license key!',
756 'call_to_action' => $btn,
757 'data' => $response,
758 )
759 );
760 }
761
762 if ( empty( $flow ) ) {
763 $response_data = array( 'message' => __( 'Funnel data not found.', 'cartflows' ) );
764 wp_send_json_error( $response_data );
765 }
766
767 /**
768 * Create Flow
769 */
770 $new_flow_post = array(
771 'post_title' => isset( $_POST['flow_name'] ) ? sanitize_text_field( wp_unslash( $_POST['flow_name'] ) ) : '',
772 'post_content' => '',
773 'post_status' => 'publish',
774 'post_type' => CARTFLOWS_FLOW_POST_TYPE,
775 );
776
777 // Insert the post into the database.
778 $new_flow_id = wp_insert_post( $new_flow_post );
779
780 if ( is_wp_error( $new_flow_id ) ) {
781 wp_send_json_error( $new_flow_id->get_error_message() );
782 }
783
784 $store_checkout = isset( $_POST['store_checkout'] ) ? sanitize_text_field( wp_unslash( $_POST['store_checkout'] ) ) : '';
785
786 // If is global checkout update store_checkout options.
787 if ( 'true' === $store_checkout ) {
788 update_option( '_cartflows_store_checkout', $new_flow_id );
789
790 // reset global checkout on store checkout creation.
791 $common_settings = \Cartflows_Helper::get_common_settings();
792 $common_settings['global_checkout'] = '';
793 $common_settings['override_global_checkout'] = 'disable';
794
795 update_option( '_cartflows_common', $common_settings );
796 }
797
798 // Import the Global Colors Data if exists.
799 $this->import_funnel_gcp_vars_data( $response, $new_flow_id );
800
801 wcf()->logger->import_log( '✓ Flow Created! Flow ID: ' . $new_flow_id . ' - Remote Flow ID - ' . $flow['ID'] );
802
803 /**
804 * All Import Steps
805 */
806 $steps = isset( $flow['steps'] ) ? $flow['steps'] : array();
807
808 // Return of no steps are found in the imported flow.
809 if ( empty( $steps ) ) {
810 $response_data = array( 'message' => __( 'Steps not found.', 'cartflows' ) );
811 wp_send_json_error( $response_data );
812 }
813
814 // Set the flag as true to check for the import process is started for the CartFlows. So as to import/upload the required files.
815 \CartFlows_Batch_Process::set_is_wcf_template_import( true );
816
817 foreach ( $steps as $key => $step ) {
818
819 if ( in_array( $step['type'], array( 'upsell', 'downsell' ), true ) && ( ! _is_cartflows_pro() || is_wcf_starter_plan() ) ) {
820 continue;
821 }
822
823 $this->import_single_step(
824 array(
825 'step' => array(
826 'id' => $step['ID'],
827 'title' => $step['title'],
828 'type' => $step['type'],
829 ),
830 'flow' => array(
831 'id' => $new_flow_id,
832 ),
833 'is_store_checkout' => isset( $_POST['store_checkout'] ) ? sanitize_text_field( wp_unslash( $_POST['store_checkout'] ) ) : '',
834 ),
835 'cartflows_import_flow'
836 );
837 }
838
839 /**
840 * Redirect to the new flow edit screen
841 */
842 $response_data = array(
843 'message' => __( 'Successfully imported the Flow!', 'cartflows' ),
844 'items' => $flow,
845 'redirect_url' => admin_url( 'post.php?action=edit&post=' . $new_flow_id ),
846 'new_flow_id' => $new_flow_id,
847 );
848
849 // Set the flag as false once the template import is complete.
850 \CartFlows_Batch_Process::set_is_wcf_template_import( false );
851
852 // Check if the user has imported their first flow.
853 $first_flow_imported = get_option( 'wcf_first_flow_imported', false );
854
855 if ( ! $first_flow_imported ) {
856 update_option( 'wcf_first_flow_imported', true );
857 }
858
859 // Track funnel creation method for analytics.
860 $creation_method = isset( $_POST['creation_method'] ) ? sanitize_text_field( wp_unslash( $_POST['creation_method'] ) ) : 'ready_made_template';
861 AdminHelper::track_funnel_creation_method( $creation_method );
862
863 wcf()->logger->import_log( 'COMPLETE! Importing Flow' );
864
865 wp_send_json_success( $response_data );
866 }
867
868 /**
869 * Import Step
870 *
871 * @return void
872 */
873 public function import_step() {
874
875 wcf()->logger->import_log( 'STARTED! Importing Step' );
876
877 $response_data = array( 'message' => $this->get_error_msg( 'permission' ) );
878
879 if ( ! current_user_can( 'cartflows_manage_flows_steps' ) ) {
880 wp_send_json_error( $response_data );
881 }
882
883 /**
884 * Nonce verification
885 */
886 if ( ! check_ajax_referer( 'cartflows_import_step', 'security', false ) ) {
887 $response_data = array( 'message' => $this->get_error_msg( 'nonce' ) );
888 wp_send_json_error( $response_data );
889 }
890
891 // $_POST['step'] is the JSON, There is nothing to sanitize JSON as it is data format not data type. Hence sanitizing decoded array below.
892 $step = isset( $_POST['step'] ) ? json_decode( stripslashes( $_POST['step'] ), true ) : array(); //phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
893
894 // Return if step data not found in the import request.
895 if ( empty( $step ) ) {
896 wp_send_json_error( array( 'message' => __( 'Step data ID not found for import.', 'cartflows' ) ) );
897 }
898
899 // Sanitizing decoded array.
900 $step = array_map( 'sanitize_text_field', $step );
901
902 $flow_id = isset( $_POST['flow_id'] ) ? absint( $_POST['flow_id'] ) : 0;
903
904 $remote_flow_id = isset( $_POST['remote_flow_id'] ) ? absint( $_POST['remote_flow_id'] ) : 0;
905
906 // Return if the remote flow ID is blank or not found in the request.
907 if ( empty( $remote_flow_id ) || empty( $flow_id ) ) {
908 wp_send_json_error( array( 'message' => __( 'Funnel ID not found in the request.', 'cartflows' ) ) );
909 }
910
911 // Get single step Rest API response.
912 $response = \CartFlows_API::get_instance()->get_flow( $remote_flow_id );
913
914 if ( is_wp_error( $response['data'] ) ) {
915 /* translators: %1$s: html tag, %2$s: link html start %3$s: link html end */
916 $btn = sprintf( __( 'Request timeout error. Please check if the firewall or any security plugin is blocking the outgoing HTTP/HTTPS requests to templates.cartflows.com or not. %1$sTo resolve this issue, please check this %2$sarticle%3$s.', 'cartflows' ), '<br><br>', '<a target="_blank" href="https://cartflows.com/docs/request-timeout-error-while-importing-the-flow-step-templates/?utm_source=dashboard&utm_medium=free-cartflows&utm_campaign=docs">', '</a>' );
917
918 wp_send_json_error(
919 array(
920 'message' => $response['data']->get_error_message(),
921 'call_to_action' => $btn,
922 'data' => $response,
923 )
924 );
925 }
926
927 $license_status = isset( $response['data']['licence_status'] ) ? $response['data']['licence_status'] : '';
928
929 // If license is invalid then.
930 if ( 'valid' !== $license_status ) {
931
932 $cf_pro_status = AdminMenu::get_instance()->get_plugin_status( 'cartflows-pro/cartflows-pro.php' );
933
934 $msg = '';
935 $cta = '';
936 if ( 'not-installed' === $cf_pro_status ) {
937 /* translators: %1$s: link html start, %2$s: link html end*/
938 $cta = sprintf( __( '%1$sUpgrade to CartFlows Pro.%2$s', 'cartflows' ), '<a target="_blanks" class="wcf-button wcf-primary-button" href="https://cartflows.com/?utm_source=dashboard&utm_medium=free-cartflows&utm_campaign=go-pro">', '</a>' );
939 $msg = __( 'To import the premium step, please upgrade to CartFlows Pro', 'cartflows' );
940 } elseif ( 'inactive' === $cf_pro_status ) {
941 /* translators: %1$s: link html start, %2$s: link html end*/
942 $cta = sprintf( __( '%1$sActivate CartFlows Pro%2$s', 'cartflows' ), '<a target="_blank" class="wcf-button wcf-primary-button" href="' . admin_url( 'plugins.php?plugin_status=search&paged=1&s=CartFlows+Pro' ) . '">', '</a>' );
943 $msg = __( 'To import the premium step activate Cartflows Pro and validate the license key.', 'cartflows' );
944 } elseif ( 'active' === $cf_pro_status ) {
945 /* translators: %1$s: link html start, %2$s: link html end*/
946 $cta = sprintf( __( '%1$sActivate CartFlows Pro License %2$s', 'cartflows' ), '<a target="_blank" class="wcf-button wcf-primary-button" href="' . admin_url( 'plugins.php?cartflows-license-popup' ) . '">', '</a>' );
947 $msg = __( 'To import the premium step activate the CartFlows Pro.', 'cartflows' );
948 }
949
950 wp_send_json_error(
951 array(
952 'message' => \ucfirst( $license_status ) . ' license key! ' . $msg,
953 'call_to_action' => $cta,
954 'data' => $response,
955 )
956 );
957 }
958
959 $step['title'] = isset( $_POST['step_name'] ) && ! empty( $_POST['step_name'] ) ? sanitize_text_field( wp_unslash( $_POST['step_name'] ) ) : $step['title'];
960
961 // Set the flag as true to check for the import process is started for the CartFlows. So as to import/upload the required files.
962 \CartFlows_Batch_Process::set_is_wcf_template_import( true );
963
964 // Create steps.
965 $this->import_single_step(
966 array(
967 'step' => array(
968 'id' => $step['ID'],
969 'title' => $step['title'],
970 'type' => $step['type'],
971 ),
972 'flow' => array(
973 'id' => $flow_id,
974 ),
975 ),
976 'cartflows_import_step'
977 );
978
979 wcf()->logger->import_log( 'COMPLETE! Importing Step' );
980
981 if ( empty( $step ) ) {
982 $response_data = array( 'message' => __( 'Step data not found.', 'cartflows' ) );
983 wp_send_json_error( $response_data );
984 }
985
986 /**
987 * Redirect to the new step edit screen
988 */
989 $response_data = array(
990 'message' => __( 'Successfully imported the Step!', 'cartflows' ),
991 );
992
993 // Set the flag as false once the template import is complete.
994 \CartFlows_Batch_Process::set_is_wcf_template_import( false );
995
996 wcf()->logger->import_log( 'COMPLETE! Importing Step' );
997
998 wp_send_json_success( $response_data );
999 }
1000
1001 /**
1002 * Updates post content of chosen template.
1003 * working only for Store Checkout.
1004 *
1005 * @return void
1006 * @since X.X.X
1007 */
1008 public function update_step() {
1009
1010 wcf()->logger->import_log( 'STARTED! Updating Step' );
1011
1012 $response_data = array( 'message' => $this->get_error_msg( 'permission' ) );
1013
1014 if ( ! current_user_can( 'cartflows_manage_flows_steps' ) ) {
1015 wp_send_json_error( $response_data );
1016 }
1017
1018 /**
1019 * Nonce verification
1020 */
1021 if ( ! check_ajax_referer( 'cartflows_update_step', 'security', false ) ) {
1022 $response_data = array( 'message' => $this->get_error_msg( 'nonce' ) );
1023 wp_send_json_error( $response_data );
1024 }
1025
1026 // $_POST['step'] is the JSON, There is nothing to sanitize JSON as it is data format not data type. Hence sanitizing decoded array below.
1027 $step = isset( $_POST['step'] ) ? json_decode( stripslashes( $_POST['step'] ), true ) : array(); //phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
1028 // Sanitizing decoded array.
1029 $step = array_map( 'sanitize_text_field', $step );
1030 $flow_id = isset( $_POST['flow_id'] ) ? absint( $_POST['flow_id'] ) : 0;
1031 $step_id = isset( $_POST['step_id'] ) ? absint( $_POST['step_id'] ) : 0;
1032
1033 $remote_flow_id = isset( $_POST['remote_flow_id'] ) ? absint( $_POST['remote_flow_id'] ) : 0;
1034
1035 // Get single step Rest API response.
1036 $response = \CartFlows_API::get_instance()->get_flow( $remote_flow_id );
1037 if ( is_wp_error( $response['data'] ) ) {
1038 /* translators: %1$s: html tag, %2$s: link html start %3$s: link html end */
1039 $btn = sprintf( __( 'Request timeout error. Please check if the firewall or any security plugin is blocking the outgoing HTTP/HTTPS requests to templates.cartflows.com or not. %1$sTo resolve this issue, please check this %2$s article%3$s.', 'cartflows' ), '<br><br>', '<a target="_blank" href="https://cartflows.com/docs/request-timeout-error-while-importing-the-flow-step-templates/?utm_source=dashboard&utm_medium=free-cartflows&utm_campaign=docs">', '</a>' );
1040
1041 wp_send_json_error(
1042 array(
1043 'message' => $response['data']->get_error_message(),
1044 'call_to_action' => $btn,
1045 'data' => $response,
1046 )
1047 );
1048 }
1049
1050 $license_status = isset( $response['data']['licence_status'] ) ? $response['data']['licence_status'] : '';
1051
1052 // If license is invalid then.
1053 if ( 'valid' !== $license_status ) {
1054
1055 $cf_pro_status = AdminMenu::get_instance()->get_plugin_status( 'cartflows-pro/cartflows-pro.php' );
1056
1057 $cta = '';
1058 if ( 'not-installed' === $cf_pro_status ) {
1059 /* translators: %1$s: link html start, %2$s: link html end*/
1060 $cta = sprintf( __( 'Upgrade to %1$sCartFlows Pro.%2$s', 'cartflows' ), '<a target="_blanks" href="https://cartflows.com/?utm_source=dashboard&utm_medium=free-cartflows&utm_campaign=go-pro">', '</a>' );
1061 } elseif ( 'inactive' === $cf_pro_status ) {
1062 /* translators: %1$s: link html start, %2$s: link html end*/
1063 $cta = sprintf( __( '%1$sActivate CartFlows Pro%2$s', 'cartflows' ), '<a target="_blank" href="' . admin_url( 'plugins.php?plugin_status=search&paged=1&s=CartFlows+Pro' ) . '">', '</a>' );
1064 } elseif ( 'active' === $cf_pro_status ) {
1065 /* translators: %1$s: link html start, %2$s: link html end*/
1066 $cta = sprintf( __( 'CartFlows Pro license is not active. Activate %1$sCartFlows Pro License %2$s', 'cartflows' ), '<a target="_blank" href="' . admin_url( 'plugins.php?cartflows-license-popup' ) . '">', '</a>' );
1067 }
1068
1069 wp_send_json_error(
1070 array(
1071 'message' => \ucfirst( $license_status ) . ' license key! ' . $cta,
1072 'data' => $response,
1073 )
1074 );
1075 }
1076
1077 if ( empty( $remote_flow_id ) ) {
1078 $response_data = array( 'message' => __( 'Funnel data not found.', 'cartflows' ) );
1079 wp_send_json_error( $response_data );
1080 }
1081 $step['title'] = isset( $_POST['step_name'] ) && ! empty( $_POST['step_name'] ) ? sanitize_text_field( wp_unslash( $_POST['step_name'] ) ) : $step['title'];
1082 // Create steps.
1083 $this->update_single_step(
1084 array(
1085 'step' => array(
1086 'id' => $step['ID'],
1087 'title' => $step['title'],
1088 'type' => $step['type'],
1089 ),
1090 'flow' => array(
1091 'id' => $flow_id,
1092 ),
1093 'current_step' => array(
1094 'id' => $step_id,
1095 ),
1096 )
1097 );
1098
1099 wcf()->logger->import_log( 'COMPLETE! Importing Step' );
1100
1101 if ( empty( $step ) ) {
1102 $response_data = array( 'message' => __( 'Step data not found.', 'cartflows' ) );
1103 wp_send_json_error( $response_data );
1104 }
1105
1106 /**
1107 * Redirect to the new step edit screen
1108 */
1109 $response_data = array(
1110 'message' => __( 'Successfully imported the Step!', 'cartflows' ),
1111 );
1112
1113 wcf()->logger->import_log( 'COMPLETE! Importing Step' );
1114
1115 wp_send_json_success( $response_data );
1116 }
1117
1118 /**
1119 * Update Sinple Step
1120 *
1121 * @param array $args Rest API Arguments.
1122 * @return void
1123 */
1124 public function update_single_step( $args = array() ) {
1125
1126 wcf()->logger->import_log( 'STARTED! Updating Step' );
1127
1128 $step_id = isset( $args['step']['id'] ) ? absint( $args['step']['id'] ) : 0;
1129 $new_step_id = isset( $args['current_step']['id'] ) ? absint( $args['current_step']['id'] ) : '';
1130
1131 if ( empty( $step_id ) || empty( $new_step_id ) ) {
1132 /* translators: %s: step ID */
1133 wp_send_json_error( sprintf( __( 'Invalid step id %1$s or post id %2$s.', 'cartflows' ), $step_id, $new_step_id ) );
1134 }
1135
1136 wcf()->logger->import_log( 'Remote Step ' . $step_id . ' for local flow "' . get_the_title( $new_step_id ) . '" [' . $new_step_id . ']' );
1137
1138 // Get single step Rest API response.
1139 $response = \CartFlows_API::get_instance()->get_template( $step_id );
1140 wcf()->logger->import_log( wp_json_encode( $response ) );
1141
1142 if ( 'divi' === \Cartflows_Helper::get_common_setting( 'default_page_builder' ) ) {
1143 if ( isset( $response['data']['divi_content'] ) && ! empty( $response['data']['divi_content'] ) ) {
1144
1145 update_post_meta( $new_step_id, 'divi_content', $response['data']['divi_content'] );
1146
1147 wp_update_post(
1148 array(
1149 'ID' => $new_step_id,
1150 'post_content' => $response['data']['divi_content'],
1151 )
1152 );
1153 }
1154 }
1155
1156 if ( 'gutenberg' === \Cartflows_Helper::get_common_setting( 'default_page_builder' ) ) {
1157 if ( isset( $response['data']['divi_content'] ) && ! empty( $response['data']['divi_content'] ) ) {
1158 wp_update_post(
1159 array(
1160 'ID' => $new_step_id,
1161 'post_content' => $response['data']['divi_content'],
1162 )
1163 );
1164 }
1165 }
1166
1167 // Import Post Meta.
1168 $this->import_post_meta( $new_step_id, $response );
1169
1170 /* Imported Step */
1171 update_post_meta( $new_step_id, 'cartflows_imported_step', 'yes' );
1172
1173 do_action( 'cartflows_import_complete' );
1174
1175 // Batch Process.
1176 do_action( 'cartflows_after_template_import', $new_step_id, $response );
1177
1178 wcf()->logger->import_log( 'COMPLETE! Importing Step' );
1179 }
1180
1181 /**
1182 * Create Simple Step
1183 *
1184 * @param array $args Rest API Arguments.
1185 * @param string $action action name to check nonce.
1186 *
1187 * @return void
1188 */
1189 public function import_single_step( $args, $action ) {
1190
1191 /**
1192 * Nonce verification
1193 */
1194 if ( ! check_ajax_referer( $action, 'security', false ) ) {
1195 $response_data = array( 'message' => __( 'Nonce verification failed.', 'cartflows' ) );
1196 wp_send_json_error( $response_data );
1197 }
1198
1199 wcf()->logger->import_log( 'STARTED! Importing Step' );
1200
1201 $step_id = isset( $args['step']['id'] ) ? absint( $args['step']['id'] ) : 0;
1202 $step_title = isset( $args['step']['title'] ) ? $args['step']['title'] : '';
1203 $step_type = isset( $args['step']['type'] ) ? $args['step']['type'] : '';
1204 $flow_id = isset( $args['flow']['id'] ) ? absint( $args['flow']['id'] ) : '';
1205 $is_store_checkout = isset( $args['is_store_checkout'] ) ? $args['is_store_checkout'] : '';
1206
1207 // create steps only for checkout and thankyou if store checkout.
1208 // This logic will be removed once we have store checkout templates on server.
1209 if ( 'true' === $is_store_checkout && ! in_array( $step_type, array( 'checkout', 'thankyou' ), true ) ) {
1210 return;
1211 }
1212
1213 // Create new step.
1214 $new_step_id = \CartFlows_Importer::get_instance()->create_step( $flow_id, $step_type, $step_title );
1215
1216 if ( empty( $step_id ) || empty( $new_step_id ) ) {
1217 /* translators: %s: step ID */
1218 wp_send_json_error( sprintf( __( 'Invalid step id %1$s or post id %2$s.', 'cartflows' ), $step_id, $new_step_id ) );
1219 }
1220
1221 wcf()->logger->import_log( 'Remote Step ' . $step_id . ' for local flow "' . get_the_title( $new_step_id ) . '" [' . $new_step_id . ']' );
1222
1223 // Get single step Rest API response.
1224 $response = \CartFlows_API::get_instance()->get_template( $step_id );
1225
1226 wcf()->logger->import_log( wp_json_encode( $response ) );
1227
1228 // Return if there is an error while importing the step template.
1229 if ( ! $response['success'] ) {
1230 $response_data = $response['data'];
1231 $error_code = wp_remote_retrieve_response_code( $response_data );
1232 $error_msge = json_decode( wp_remote_retrieve_body( $response_data ) );
1233
1234 wp_send_json_error(
1235 array(
1236 'error_code' => $error_code,
1237 'message' => ! empty( $error_msge->message ) ? $error_msge->message : '',
1238 'data' => $response['data'],
1239 'success' => $response['success'],
1240 )
1241 );
1242 }
1243
1244 if ( 'divi' === \Cartflows_Helper::get_common_setting( 'default_page_builder' ) ) {
1245 if ( isset( $response['data']['divi_content'] ) && ! empty( $response['data']['divi_content'] ) ) {
1246
1247 update_post_meta( $new_step_id, 'divi_content', $response['data']['divi_content'] );
1248
1249 wp_update_post(
1250 array(
1251 'ID' => $new_step_id,
1252 'post_content' => $response['data']['divi_content'],
1253 )
1254 );
1255 }
1256 }
1257
1258 if ( 'gutenberg' === \Cartflows_Helper::get_common_setting( 'default_page_builder' ) ) {
1259 if ( isset( $response['data']['divi_content'] ) && ! empty( $response['data']['divi_content'] ) ) {
1260 wp_update_post(
1261 array(
1262 'ID' => $new_step_id,
1263 'post_content' => $response['data']['divi_content'],
1264 )
1265 );
1266 }
1267 }
1268
1269 // Import Post Meta.
1270 $this->import_post_meta( $new_step_id, $response );
1271
1272 if ( 'checkout' === $step_type ) {
1273
1274 $posted_data = array(
1275 'primary_color' => isset( $_POST['primary_color'] ) ? sanitize_text_field( wp_unslash( $_POST['primary_color'] ) ) : '',
1276 'site_logo' => isset( $_POST['site_logo'] ) ? array_map( 'sanitize_text_field', wp_unslash( $_POST['site_logo'] ) ) : '',
1277 );
1278
1279 $this->update_store_checkout_template_data( $new_step_id, $response, $posted_data );
1280 }
1281
1282 /* Imported Step */
1283 update_post_meta( $new_step_id, 'cartflows_imported_step', 'yes' );
1284
1285 do_action( 'cartflows_import_complete' );
1286
1287 // Batch Process.
1288 do_action( 'cartflows_after_template_import', $new_step_id, $response );
1289
1290 wcf()->logger->import_log( 'COMPLETE! Importing Step' );
1291 }
1292
1293 /**
1294 * Import Post Meta
1295 *
1296 * @since 1.0.0
1297 *
1298 * @param integer $post_id Post ID.
1299 * @param array $response Post meta.
1300 * @return void
1301 */
1302 public function import_post_meta( $post_id, $response ) {
1303
1304 $metadata = apply_filters( 'cartflows_template_import_meta_data', (array) $response['post_meta'] );
1305
1306 $exclude_meta_keys = \Cartflows_Helper::get_instance()->get_meta_keys_to_exclude_from_import( $post_id );
1307
1308 foreach ( $metadata as $meta_key => $meta_value ) {
1309
1310 if ( in_array( $meta_key, $exclude_meta_keys, true ) ) {
1311 continue;
1312 }
1313
1314 // Security: Only allow meta keys matching known prefixes to prevent arbitrary DB writes.
1315 if ( ! \Cartflows_Helper::get_instance()->is_meta_key_allowed_for_import( $meta_key ) ) {
1316 continue;
1317 }
1318
1319 $meta_value = isset( $meta_value[0] ) ? $meta_value[0] : '';
1320
1321 if ( $meta_value ) {
1322
1323 if ( is_serialized( $meta_value, true ) ) {
1324 // Security: Using unserialize with allowed_classes=>false to prevent object injection.
1325 $raw_data = unserialize( stripslashes( $meta_value ), array( 'allowed_classes' => false ) ); // phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.serialize_unserialize, PHPCompatibility.FunctionUse.NewFunctionParameters.unserialize_optionsFound
1326 // Drop malicious payloads completely to prevent fatal errors.
1327 // Security: Block serialized meta that contains nested PHP objects.
1328 // Even with allowed_classes => false, unserialized payloads may contain
1329 // __PHP_Incomplete_Class instances which WordPress attempts to mutate
1330 // during wp_unslash(), causing fatal errors and violating object safety.
1331 if (
1332 false === $raw_data ||
1333 $this->has_nested_object_payload( $raw_data )
1334 ) {
1335 $raw_data = '';
1336 continue;
1337 }
1338 } elseif ( is_array( $meta_value ) ) {
1339 $raw_data = json_decode( stripslashes( $meta_value ), true );
1340 } else {
1341 $raw_data = $meta_value;
1342 }
1343
1344 if ( '_elementor_data' === $meta_key ) {
1345
1346 if ( is_array( $raw_data ) ) {
1347 $raw_data = wp_slash( wp_json_encode( $raw_data ) );
1348 } else {
1349 $raw_data = wp_slash( $raw_data );
1350 }
1351 }
1352
1353 if ( '_elementor_data' !== $meta_key && '_elementor_draft' !== $meta_key && '_fl_builder_data' !== $meta_key && '_fl_builder_draft' !== $meta_key ) {
1354 if ( is_array( $raw_data ) ) {
1355 wcf()->logger->import_log( '✓ Added post meta ' . $meta_key /* . ' | ' . wp_json_encode( $raw_data ) */ );
1356 } elseif ( ! is_object( $raw_data ) ) {
1357 wcf()->logger->import_log( '✓ Added post meta ' . $meta_key /* . ' | ' . $raw_data */ );
1358 }
1359 }
1360 update_post_meta( $post_id, $meta_key, $raw_data );
1361 }
1362 }
1363 }
1364
1365 /**
1366 * Find the Checkout block and set the Primary color and site logo provided by the user.
1367 *
1368 * @since 1.10.0
1369 *
1370 * @param int $post_id newly created steps ID.
1371 * @param array $response data received from from the imported step.
1372 * @param array $posted_data post data.
1373 *
1374 * @return void
1375 */
1376 public function update_store_checkout_template_data( $post_id, $response, $posted_data ) {
1377
1378 $store_checkout_id = get_option( '_cartflows_store_checkout', false );
1379 $current_flow_id = (int) wcf()->utils->get_flow_id_from_step_id( $post_id );
1380 $default_page_bulder = \Cartflows_Helper::get_common_setting( 'default_page_builder' );
1381
1382 if ( empty( $posted_data['primary_color'] ) && empty( $posted_data['site_logo'] ) ) {
1383 return;
1384 }
1385
1386 if ( $store_checkout_id !== $current_flow_id ) {
1387 return;
1388 }
1389
1390 if ( 'elementor' === $default_page_bulder ) {
1391
1392 $metadata = (array) $response['post_meta'];
1393
1394 foreach ( $metadata as $meta_key => $meta_value ) {
1395
1396 $meta_value = isset( $meta_value[0] ) ? $meta_value[0] : '';
1397
1398 if ( $meta_value ) {
1399
1400 if ( is_serialized( $meta_value, true ) ) {
1401 // Security: Using unserialize with allowed_classes=>false to prevent object injection.
1402 $raw_data = unserialize( stripslashes( $meta_value ), array( 'allowed_classes' => false ) ); // phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.serialize_unserialize, PHPCompatibility.FunctionUse.NewFunctionParameters.unserialize_optionsFound
1403 if ( false === $raw_data || is_object( $raw_data ) ) {
1404 continue;
1405 }
1406 } elseif ( is_array( $meta_value ) ) {
1407 $raw_data = json_decode( stripslashes( $meta_value ), true );
1408 } else {
1409 $raw_data = $meta_value;
1410 }
1411
1412 if ( '_elementor_data' === $meta_key ) {
1413
1414 $raw_data = json_decode( $raw_data, true );
1415
1416 // Find the checkout-form and update the primary color.
1417 $this->elementor_find_and_replace_template_data( $raw_data, $posted_data );
1418
1419 if ( is_array( $raw_data ) ) {
1420 $raw_data = wp_slash( wp_json_encode( $raw_data ) );
1421 } else {
1422 $raw_data = wp_slash( $raw_data );
1423 }
1424
1425 update_post_meta( $post_id, $meta_key, $raw_data );
1426
1427 }
1428 }
1429 }
1430 } elseif ( 'gutenberg' === $default_page_bulder ) {
1431
1432 $post = get_post( $post_id );
1433 $blocks = parse_blocks( $post->post_content );
1434
1435 if ( is_array( $blocks ) && ! empty( $blocks ) ) {
1436
1437 $this->gutenberg_find_and_replace_template_data( $blocks, $posted_data );
1438
1439 if ( ! empty( $blocks ) ) {
1440 $serialized_blocks = serialize_blocks( $blocks );
1441
1442 wp_update_post(
1443 array(
1444 'ID' => $post_id,
1445 'post_content' => $serialized_blocks,
1446 )
1447 );
1448 }
1449 }
1450 } elseif ( 'beaver-builder' === $default_page_bulder ) {
1451 $data = \FLBuilderModel::get_layout_data( 'published', $post_id );
1452
1453 if ( ! empty( $data ) ) {
1454
1455 $this->beaver_builder_find_and_replace_template_data( $data, $posted_data );
1456
1457 // Update page builder data.
1458 update_post_meta( $post_id, '_fl_builder_data', $data );
1459 update_post_meta( $post_id, '_fl_builder_draft', $data );
1460 }
1461 }
1462 }
1463
1464
1465 /**
1466 * Get flows list for preview
1467 *
1468 * @return void
1469 */
1470 public function get_flows_list() {
1471
1472 $response_data = array( 'message' => $this->get_error_msg( 'permission' ) );
1473
1474 if ( ! current_user_can( 'cartflows_manage_flows_steps' ) ) {
1475 wp_send_json_error( $response_data );
1476 }
1477
1478 /**
1479 * Nonce verification
1480 */
1481 if ( ! check_ajax_referer( 'cartflows_get_flows_list', 'security', false ) ) {
1482 $response_data = array( 'message' => $this->get_error_msg( 'nonce' ) );
1483 wp_send_json_error( $response_data );
1484 }
1485
1486 $flows_list = \Cartflows_Helper::get_instance()->get_flows_and_steps();
1487
1488 /**
1489 * Redirect to the new step edit screen
1490 */
1491 $response_data = array(
1492 'message' => __( 'Successful!', 'cartflows' ),
1493 'flows' => $flows_list,
1494 );
1495
1496 wp_send_json_success( $response_data );
1497 }
1498
1499 /**
1500 * Get the elementor widget data.
1501 *
1502 * @param array $elements elements data.
1503 * @param array $posted_data posted data.
1504 */
1505 public function elementor_find_and_replace_template_data( &$elements, $posted_data ) {
1506
1507 foreach ( $elements as &$element ) {
1508
1509 if ( 'widget' === $element['elType'] && 'checkout-form' === $element['widgetType'] ) {
1510 $element['settings']['global_primary_color'] = ! empty( $posted_data['primary_color'] ) ? $posted_data['primary_color'] : $element['settings']['global_primary_color'];
1511 }
1512
1513 if ( 'widget' === $element['elType'] && 'image' === $element['widgetType'] && isset( $element['settings']['_css_classes'] ) ) {
1514
1515 if ( str_contains( $element['settings']['_css_classes'], 'cartflows-store-checkout-logo-field' ) ) {
1516 $element['settings']['image']['url'] = ! empty( $posted_data['site_logo'] ) ? $posted_data['site_logo']['url'] : $element['settings']['image']['url'];
1517 $element['settings']['image']['id'] = ! empty( $posted_data['site_logo'] ) ? $posted_data['site_logo']['id'] : $element['settings']['image']['id'];
1518 }
1519 }
1520
1521 if ( ! empty( $element['elements'] ) ) {
1522 $this->elementor_find_and_replace_template_data( $element['elements'], $posted_data );
1523 }
1524 }
1525 }
1526
1527 /**
1528 * Get the block data.
1529 *
1530 * @param array $elements elements data.
1531 * @param array $posted_data posted data.
1532 */
1533 public function gutenberg_find_and_replace_template_data( &$elements, $posted_data ) {
1534
1535 foreach ( $elements as &$element ) {
1536 if ( 'wcfb/checkout-form' === $element['blockName'] ) {
1537 // Update the element with the data.
1538 $element['attrs']['globalbgColor'] = ! empty( $posted_data['primary_color'] ) ? $posted_data['primary_color'] : $element['attrs']['globalbgColor'];
1539 }
1540
1541 if ( 'uagb/info-box' === $element['blockName'] && isset( $element['attrs']['className'] ) ) {
1542
1543 if ( str_contains( $element['attrs']['className'], 'cartflows-store-checkout-logo-field' ) ) {
1544 $element['attrs']['iconImage']['id'] = ! empty( $posted_data['site_logo'] ) ? $posted_data['site_logo']['id'] : $element['attrs']['iconImage']['id'];
1545 $element['attrs']['iconImage']['url'] = ! empty( $posted_data['site_logo'] ) ? $posted_data['site_logo']['url'] : $element['attrs']['iconImage']['url'];
1546 $element['attrs']['iconImage']['link'] = ! empty( $posted_data['site_logo'] ) ? $posted_data['site_logo']['url'] : $element['attrs']['iconImage']['link'];
1547 $element['attrs']['iconImage']['sizes']['full']['url'] = ! empty( $posted_data['site_logo'] ) ? $posted_data['site_logo']['url'] : $element['attrs']['iconImage']['link'];
1548 }
1549 }
1550
1551 if ( ! empty( $element['innerBlocks'] ) ) {
1552 $this->gutenberg_find_and_replace_template_data( $element['innerBlocks'], $posted_data );
1553 }
1554 }
1555 }
1556
1557 /**
1558 * Replace the logo and color in the BB template while importing.
1559 *
1560 * @param array $elements elements data.
1561 * @param array $posted_data posted data.
1562 */
1563 public function beaver_builder_find_and_replace_template_data( &$elements, $posted_data ) {
1564
1565 foreach ( $elements as $node => &$element ) {
1566 if ( ! empty( $element->type ) && 'module' === $element->type ) {
1567
1568 if ( ! empty( $element->settings->type ) && 'cartflows-bb-checkout-form' === $element->settings->type ) {
1569 // Update the logo in the template.
1570 $element->settings->global_primary_color = ! empty( $posted_data['primary_color'] ) ? $posted_data['primary_color'] : $element->settings->global_primary_color;
1571 }
1572
1573 if ( ! empty( $element->settings->type ) && 'photo' === $element->settings->type && ! empty( $element->settings->class ) && 'cartflows-store-checkout-logo-field' === $element->settings->class ) {
1574 $module_setting = $element->settings;
1575 // Update the logo in the template.
1576 $element->settings->photo_src = ! empty( $posted_data['site_logo'] ) ? $posted_data['site_logo']['url'] : $element->settings->photo_src;
1577
1578 }
1579 }
1580 }
1581 }
1582
1583 /**
1584 * Imports the Global Colors and Patterns (GCP) variables data for a given flow.
1585 *
1586 * This function updates the post meta for a flow with the GCP variables data received in the response.
1587 * It logs the import process and updates the post meta with the GCP data.
1588 *
1589 * @param array $response The response data containing the GCP variables.
1590 * @param int $flow_id The ID of the flow for which the GCP data is being imported.
1591 *
1592 * @return void
1593 */
1594 public function import_funnel_gcp_vars_data( $response, $flow_id ) {
1595
1596 wcf()->logger->import_log( 'Start: ' . __CLASS__ . ' :: ' . __FUNCTION__ );
1597
1598 wcf()->logger->import_log( 'Newly Imported Flow ID: ' . $flow_id . PHP_EOL . ' Response ' . print_r( $response, true ) ); // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_print_r
1599
1600 if ( isset( $response['data']['flow_gcp_meta'] ) && ! empty( $response['data']['flow_gcp_meta'] ) && is_object( $response['data']['flow_gcp_meta'] ) ) {
1601
1602 wcf()->logger->import_log( 'Before Importing:' . print_r( $response['data']['flow_gcp_meta'], true ) ); // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_print_r
1603
1604 $gcp_data = (object) array_map( 'sanitize_text_field', (array) $response['data']['flow_gcp_meta'] );
1605
1606 wcf()->logger->import_log( 'After Importing: ' . print_r( $gcp_data, true ) ); // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_print_r
1607
1608 $gcp_meta_keys = array(
1609 'wcf-enable-gcp-styling' => 'yes',
1610 'wcf-gcp-primary-color' => ! empty( $gcp_data->gcp_primary_color ) ? $gcp_data->gcp_primary_color : '',
1611 'wcf-gcp-secondary-color' => ! empty( $gcp_data->gcp_secondary_color ) ? $gcp_data->gcp_secondary_color : '',
1612 'wcf-gcp-text-color' => ! empty( $gcp_data->gcp_text_color ) ? $gcp_data->gcp_text_color : '',
1613 'wcf-gcp-accent-color' => ! empty( $gcp_data->gcp_accent_color ) ? $gcp_data->gcp_accent_color : '',
1614 );
1615
1616 foreach ( $gcp_meta_keys as $key => $value ) {
1617 update_post_meta( $flow_id, $key, $value );
1618 }
1619 }
1620
1621 wcf()->logger->import_log( 'End: ' . __CLASS__ . ' :: ' . __FUNCTION__ );
1622 }
1623
1624 /**
1625 * Detect whether the given value contains objects at any depth.
1626 *
1627 * This is used as a hard security guard to prevent storing
1628 * unserialized payloads that contain PHP objects (including
1629 * __PHP_Incomplete_Class), which can cause fatal errors and
1630 * violate object injection protections.
1631 *
1632 * @since 2.2.1
1633 *
1634 * @param mixed $value The value to inspect recursively.
1635 *
1636 * @return bool True if an object is found anywhere in the payload.
1637 */
1638 private function has_nested_object_payload( $value ) {
1639
1640 if ( is_object( $value ) ) {
1641 return true;
1642 }
1643
1644 if ( is_array( $value ) ) {
1645 foreach ( $value as $v ) {
1646 if ( $this->has_nested_object_payload( $v ) ) {
1647 return true;
1648 }
1649 }
1650 }
1651
1652 return false;
1653 }
1654 }
1655