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

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

1,676 lines 56.1 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\AdminCore\Ajax;
9
10 // Exit if accessed directly.
11 if ( ! defined( 'ABSPATH' ) ) {
12 exit;
13 }
14
15 use CartflowsAdmin\AdminCore\Inc\AdminMenu;
16 use CartflowsAdmin\AdminCore\Ajax\AjaxBase;
17 use CartflowsAdmin\AdminCore\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 // Capture creation method before insert so the meta is in place
556 // when transition_post_status fires on publish.
557 $creation_method = isset( $_POST['creation_method'] ) ? sanitize_text_field( wp_unslash( $_POST['creation_method'] ) ) : 'scratch';
558
559 // Create post object.
560 $new_flow_post = array(
561 'post_title' => isset( $_POST['flow_name'] ) ? sanitize_text_field( wp_unslash( $_POST['flow_name'] ) ) : '',
562 'post_content' => '',
563 'post_status' => 'publish',
564 'post_type' => CARTFLOWS_FLOW_POST_TYPE,
565 'meta_input' => array(
566 'wcf-flow-creation-method' => $creation_method,
567 ),
568 );
569
570 // Insert the post into the database.
571 $flow_id = wp_insert_post( $new_flow_post );
572
573 if ( is_wp_error( $flow_id ) ) {
574 wp_send_json_error( $flow_id->get_error_message() );
575 }
576
577 $store_checkout = isset( $_POST['store_checkout'] ) ? sanitize_text_field( wp_unslash( $_POST['store_checkout'] ) ) : '';
578
579 // If is store checkout update store_checkout options.
580 if ( 'true' === $store_checkout ) {
581 update_option( '_cartflows_store_checkout', $flow_id );
582
583 // reset global checkout on store checkout creation.
584 $common_settings = \Cartflows_Helper::get_common_settings();
585 $common_settings['global_checkout'] = '';
586 $common_settings['override_global_checkout'] = 'disable';
587
588 update_option( '_cartflows_common', $common_settings );
589 }
590
591 $flow_steps = array();
592
593 if ( wcf()->is_woo_active ) {
594 if ( 'true' === $store_checkout ) {
595 $steps_data = array(
596 'order-form' => array(
597 'title' => __( 'Checkout', 'cartflows' ),
598 'type' => 'checkout',
599 ),
600 'order-confirmation' => array(
601 'title' => __( 'Thank You', 'cartflows' ),
602 'type' => 'thankyou',
603 ),
604 );
605 } else {
606 $steps_data = array(
607 'sales' => array(
608 'title' => __( 'Sales Landing', 'cartflows' ),
609 'type' => 'landing',
610 ),
611 'order-form' => array(
612 'title' => __( 'Checkout', 'cartflows' ),
613 'type' => 'checkout',
614 ),
615 'order-confirmation' => array(
616 'title' => __( 'Thank You', 'cartflows' ),
617 'type' => 'thankyou',
618 ),
619 );
620 }
621 } else {
622 $steps_data = array(
623 'landing' => array(
624 'title' => __( 'Landing', 'cartflows' ),
625 'type' => 'landing',
626 ),
627 'thankyou' => array(
628 'title' => __( 'Thank You', 'cartflows' ),
629 'type' => 'landing',
630 ),
631 );
632 }
633
634 foreach ( $steps_data as $slug => $data ) {
635
636 $post_content = '';
637 $step_type = trim( $data['type'] );
638
639 // Create new step.
640 $step_id = wp_insert_post(
641 array(
642 'post_type' => CARTFLOWS_STEP_POST_TYPE,
643 'post_title' => $data['title'],
644 'post_content' => $post_content,
645 'post_status' => 'publish',
646 )
647 );
648
649 // Return the error.
650 if ( is_wp_error( $step_id ) ) {
651 wp_send_json_error( $step_id->get_error_message() );
652 }
653
654 if ( $step_id ) {
655
656 $flow_steps[] = array(
657 'id' => $step_id,
658 'title' => $data['title'],
659 'type' => $step_type,
660 );
661
662 // Insert post meta.
663 update_post_meta( $step_id, 'wcf-flow-id', $flow_id );
664 update_post_meta( $step_id, 'wcf-step-type', $step_type );
665
666 // Set taxonomies.
667 wp_set_object_terms( $step_id, $step_type, CARTFLOWS_TAXONOMY_STEP_TYPE );
668 wp_set_object_terms( $step_id, 'flow-' . $flow_id, CARTFLOWS_TAXONOMY_STEP_FLOW );
669
670 update_post_meta( $step_id, '_wp_page_template', 'cartflows-default' );
671 }
672 }
673
674 update_post_meta( $flow_id, 'wcf-steps', $flow_steps );
675
676 // Enable the Instant Layout for the flow for all page builders if the funnel is created from scratch.
677 update_post_meta( $flow_id, 'instant-layout-style', 'yes' );
678 \Cartflows_Helper::set_analytics_flag( 'first_instant_layout_enabled' );
679
680 // Track funnel creation method for analytics.
681 // $creation_method was captured and persisted as post meta before wp_insert_post above.
682 AdminHelper::track_funnel_creation_method( $creation_method );
683
684 /**
685 * Redirect to the new flow edit screen
686 */
687 $response_data = array(
688 'message' => __( 'Successfully created the Funnel!', 'cartflows' ),
689 'redirect_url' => admin_url( 'post.php?action=edit&post=' . $flow_id ),
690 'flow_id' => $flow_id,
691 );
692 wp_send_json_success( $response_data );
693 }
694
695 /**
696 * Create the Flow.
697 */
698 public function import_flow() {
699
700 wcf()->logger->import_log( 'STARTED! Importing Flow' );
701
702 $response_data = array( 'message' => $this->get_error_msg( 'permission' ) );
703
704 if ( ! current_user_can( 'cartflows_manage_flows_steps' ) ) {
705 wp_send_json_error( $response_data );
706 }
707
708 /**
709 * Nonce verification
710 */
711 if ( ! check_ajax_referer( 'cartflows_import_flow', 'security', false ) ) {
712 $response_data = array( 'message' => $this->get_error_msg( 'nonce' ) );
713 wp_send_json_error( $response_data );
714 }
715 // $_POST['flow'] is the JSON, There is nothing to sanitize JSON as it is data format not data type.
716 $flow = isset( $_POST['flow'] ) ? json_decode( stripslashes( $_POST['flow'] ), true ) : array(); //phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
717
718 // Get single step Rest API response.
719 $response = \CartFlows_API::get_instance()->get_flow( $flow['ID'] );
720
721 $is_error = AdminHelper::has_api_error( $response['data'] );
722
723 if ( $is_error['error'] ) {
724
725 wp_send_json_error(
726 array(
727 'error_code' => $is_error['error_code'],
728 'call_to_action' => $is_error['call_to_action'],
729 'message' => $is_error['error_message'],
730 'data' => $response,
731 )
732 );
733 }
734
735 $license_status = isset( $response['data']['licence_status'] ) ? $response['data']['licence_status'] : '';
736
737 // If license is invalid then.
738 if ( 'valid' !== $license_status ) {
739
740 $cf_pro_status = AdminMenu::get_instance()->get_plugin_status( 'cartflows-pro/cartflows-pro.php' );
741
742 $cta = '';
743 $btn = '';
744 if ( 'not-installed' === $cf_pro_status ) {
745 /* translators: %1$s: link html start, %2$s: link html end*/
746 $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>' );
747 /* translators: %1$s: link html start, %2$s: link html end*/
748 $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>' );
749 } elseif ( 'inactive' === $cf_pro_status ) {
750 /* translators: %1$s: link html start, %2$s: link html end*/
751 $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>' );
752 /* translators: %1$s: link html start, %2$s: link html end*/
753 $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>' );
754 } elseif ( 'active' === $cf_pro_status ) {
755 /* translators: %1$s: link html start, %2$s: link html end*/
756 $btn = sprintf( __( 'Invalid License Key! %1$sActivate CartFlows Pro%2$s', 'cartflows' ), '<a target="_blank" href="' . admin_url( 'plugins.php?cartflows-license-popup' ) . '">', '</a>' );
757 /* translators: %1$s: link html start, %2$s: link html end*/
758 $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>' );
759 }
760
761 wp_send_json_error(
762 array(
763 'message' => \ucfirst( $license_status ) . ' license key!',
764 'call_to_action' => $btn,
765 'data' => $response,
766 )
767 );
768 }
769
770 if ( empty( $flow ) ) {
771 $response_data = array( 'message' => __( 'Funnel data not found.', 'cartflows' ) );
772 wp_send_json_error( $response_data );
773 }
774
775 // Capture creation method before insert so the meta is in place
776 // when transition_post_status fires on publish.
777 $creation_method = isset( $_POST['creation_method'] ) ? sanitize_text_field( wp_unslash( $_POST['creation_method'] ) ) : 'ready_made_template';
778
779 /**
780 * Create Flow
781 */
782 $new_flow_post = array(
783 'post_title' => isset( $_POST['flow_name'] ) ? sanitize_text_field( wp_unslash( $_POST['flow_name'] ) ) : '',
784 'post_content' => '',
785 'post_status' => 'publish',
786 'post_type' => CARTFLOWS_FLOW_POST_TYPE,
787 'meta_input' => array(
788 'wcf-flow-creation-method' => $creation_method,
789 ),
790 );
791
792 // Insert the post into the database.
793 $new_flow_id = wp_insert_post( $new_flow_post );
794
795 if ( is_wp_error( $new_flow_id ) ) {
796 wp_send_json_error( $new_flow_id->get_error_message() );
797 }
798
799 $store_checkout = isset( $_POST['store_checkout'] ) ? sanitize_text_field( wp_unslash( $_POST['store_checkout'] ) ) : '';
800
801 // If is global checkout update store_checkout options.
802 if ( 'true' === $store_checkout ) {
803 update_option( '_cartflows_store_checkout', $new_flow_id );
804
805 // reset global checkout on store checkout creation.
806 $common_settings = \Cartflows_Helper::get_common_settings();
807 $common_settings['global_checkout'] = '';
808 $common_settings['override_global_checkout'] = 'disable';
809
810 update_option( '_cartflows_common', $common_settings );
811 }
812
813 // Import the Global Colors Data if exists.
814 $this->import_funnel_gcp_vars_data( $response, $new_flow_id );
815
816 wcf()->logger->import_log( '✓ Flow Created! Flow ID: ' . $new_flow_id . ' - Remote Flow ID - ' . $flow['ID'] );
817
818 /**
819 * All Import Steps
820 */
821 $steps = isset( $flow['steps'] ) ? $flow['steps'] : array();
822
823 // Return of no steps are found in the imported flow.
824 if ( empty( $steps ) ) {
825 $response_data = array( 'message' => __( 'Steps not found.', 'cartflows' ) );
826 wp_send_json_error( $response_data );
827 }
828
829 // Set the flag as true to check for the import process is started for the CartFlows. So as to import/upload the required files.
830 \CartFlows_Batch_Process::set_is_wcf_template_import( true );
831
832 foreach ( $steps as $key => $step ) {
833
834 if ( in_array( $step['type'], array( 'upsell', 'downsell' ), true ) && ( ! _is_cartflows_pro() || is_wcf_starter_plan() ) ) {
835 continue;
836 }
837
838 $this->import_single_step(
839 array(
840 'step' => array(
841 'id' => $step['ID'],
842 'title' => $step['title'],
843 'type' => $step['type'],
844 ),
845 'flow' => array(
846 'id' => $new_flow_id,
847 ),
848 'is_store_checkout' => isset( $_POST['store_checkout'] ) ? sanitize_text_field( wp_unslash( $_POST['store_checkout'] ) ) : '',
849 ),
850 'cartflows_import_flow'
851 );
852 }
853
854 /**
855 * Redirect to the new flow edit screen
856 */
857 $response_data = array(
858 'message' => __( 'Successfully imported the Flow!', 'cartflows' ),
859 'items' => $flow,
860 'redirect_url' => admin_url( 'post.php?action=edit&post=' . $new_flow_id ),
861 'new_flow_id' => $new_flow_id,
862 );
863
864 // Set the flag as false once the template import is complete.
865 \CartFlows_Batch_Process::set_is_wcf_template_import( false );
866
867 // Check if the user has imported their first flow.
868 $first_flow_imported = get_option( 'wcf_first_flow_imported', false );
869
870 if ( ! $first_flow_imported ) {
871 update_option( 'wcf_first_flow_imported', true );
872 }
873
874 // Track first template imported event.
875 \Cartflows_Helper::set_analytics_flag( 'first_template_imported' );
876
877 // Track funnel creation method for analytics.
878 // $creation_method was captured and persisted as post meta before wp_insert_post above.
879 AdminHelper::track_funnel_creation_method( $creation_method );
880
881 wcf()->logger->import_log( 'COMPLETE! Importing Flow' );
882
883 wp_send_json_success( $response_data );
884 }
885
886 /**
887 * Import Step
888 *
889 * @return void
890 */
891 public function import_step() {
892
893 wcf()->logger->import_log( 'STARTED! Importing Step' );
894
895 $response_data = array( 'message' => $this->get_error_msg( 'permission' ) );
896
897 if ( ! current_user_can( 'cartflows_manage_flows_steps' ) ) {
898 wp_send_json_error( $response_data );
899 }
900
901 /**
902 * Nonce verification
903 */
904 if ( ! check_ajax_referer( 'cartflows_import_step', 'security', false ) ) {
905 $response_data = array( 'message' => $this->get_error_msg( 'nonce' ) );
906 wp_send_json_error( $response_data );
907 }
908
909 // $_POST['step'] is the JSON, There is nothing to sanitize JSON as it is data format not data type. Hence sanitizing decoded array below.
910 $step = isset( $_POST['step'] ) ? json_decode( stripslashes( $_POST['step'] ), true ) : array(); //phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
911
912 // Return if step data not found in the import request.
913 if ( empty( $step ) ) {
914 wp_send_json_error( array( 'message' => __( 'Step data ID not found for import.', 'cartflows' ) ) );
915 }
916
917 // Sanitizing decoded array.
918 $step = array_map( 'sanitize_text_field', $step );
919
920 $flow_id = isset( $_POST['flow_id'] ) ? absint( $_POST['flow_id'] ) : 0;
921
922 $remote_flow_id = isset( $_POST['remote_flow_id'] ) ? absint( $_POST['remote_flow_id'] ) : 0;
923
924 // Return if the remote flow ID is blank or not found in the request.
925 if ( empty( $remote_flow_id ) || empty( $flow_id ) ) {
926 wp_send_json_error( array( 'message' => __( 'Funnel ID not found in the request.', 'cartflows' ) ) );
927 }
928
929 // Get single step Rest API response.
930 $response = \CartFlows_API::get_instance()->get_flow( $remote_flow_id );
931
932 if ( is_wp_error( $response['data'] ) ) {
933 /* translators: %1$s: html tag, %2$s: link html start %3$s: link html end */
934 $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>' );
935
936 wp_send_json_error(
937 array(
938 'message' => $response['data']->get_error_message(),
939 'call_to_action' => $btn,
940 'data' => $response,
941 )
942 );
943 }
944
945 $license_status = isset( $response['data']['licence_status'] ) ? $response['data']['licence_status'] : '';
946
947 // If license is invalid then.
948 if ( 'valid' !== $license_status ) {
949
950 $cf_pro_status = AdminMenu::get_instance()->get_plugin_status( 'cartflows-pro/cartflows-pro.php' );
951
952 $msg = '';
953 $cta = '';
954 if ( 'not-installed' === $cf_pro_status ) {
955 /* translators: %1$s: link html start, %2$s: link html end*/
956 $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>' );
957 $msg = __( 'To import the premium step, please upgrade to CartFlows Pro', 'cartflows' );
958 } elseif ( 'inactive' === $cf_pro_status ) {
959 /* translators: %1$s: link html start, %2$s: link html end*/
960 $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>' );
961 $msg = __( 'To import the premium step activate Cartflows Pro and validate the license key.', 'cartflows' );
962 } elseif ( 'active' === $cf_pro_status ) {
963 /* translators: %1$s: link html start, %2$s: link html end*/
964 $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>' );
965 $msg = __( 'To import the premium step activate the CartFlows Pro.', 'cartflows' );
966 }
967
968 wp_send_json_error(
969 array(
970 'message' => \ucfirst( $license_status ) . ' license key! ' . $msg,
971 'call_to_action' => $cta,
972 'data' => $response,
973 )
974 );
975 }
976
977 $step['title'] = isset( $_POST['step_name'] ) && ! empty( $_POST['step_name'] ) ? sanitize_text_field( wp_unslash( $_POST['step_name'] ) ) : $step['title'];
978
979 // Set the flag as true to check for the import process is started for the CartFlows. So as to import/upload the required files.
980 \CartFlows_Batch_Process::set_is_wcf_template_import( true );
981
982 // Create steps.
983 $this->import_single_step(
984 array(
985 'step' => array(
986 'id' => $step['ID'],
987 'title' => $step['title'],
988 'type' => $step['type'],
989 ),
990 'flow' => array(
991 'id' => $flow_id,
992 ),
993 ),
994 'cartflows_import_step'
995 );
996
997 wcf()->logger->import_log( 'COMPLETE! Importing Step' );
998
999 if ( empty( $step ) ) {
1000 $response_data = array( 'message' => __( 'Step data not found.', 'cartflows' ) );
1001 wp_send_json_error( $response_data );
1002 }
1003
1004 /**
1005 * Redirect to the new step edit screen
1006 */
1007 $response_data = array(
1008 'message' => __( 'Successfully imported the Step!', 'cartflows' ),
1009 );
1010
1011 // Set the flag as false once the template import is complete.
1012 \CartFlows_Batch_Process::set_is_wcf_template_import( false );
1013
1014 // Track first template imported event.
1015 \Cartflows_Helper::set_analytics_flag( 'first_template_imported' );
1016
1017 wcf()->logger->import_log( 'COMPLETE! Importing Step' );
1018
1019 wp_send_json_success( $response_data );
1020 }
1021
1022 /**
1023 * Updates post content of chosen template.
1024 * working only for Store Checkout.
1025 *
1026 * @return void
1027 * @since X.X.X
1028 */
1029 public function update_step() {
1030
1031 wcf()->logger->import_log( 'STARTED! Updating Step' );
1032
1033 $response_data = array( 'message' => $this->get_error_msg( 'permission' ) );
1034
1035 if ( ! current_user_can( 'cartflows_manage_flows_steps' ) ) {
1036 wp_send_json_error( $response_data );
1037 }
1038
1039 /**
1040 * Nonce verification
1041 */
1042 if ( ! check_ajax_referer( 'cartflows_update_step', 'security', false ) ) {
1043 $response_data = array( 'message' => $this->get_error_msg( 'nonce' ) );
1044 wp_send_json_error( $response_data );
1045 }
1046
1047 // $_POST['step'] is the JSON, There is nothing to sanitize JSON as it is data format not data type. Hence sanitizing decoded array below.
1048 $step = isset( $_POST['step'] ) ? json_decode( stripslashes( $_POST['step'] ), true ) : array(); //phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
1049 // Sanitizing decoded array.
1050 $step = array_map( 'sanitize_text_field', $step );
1051 $flow_id = isset( $_POST['flow_id'] ) ? absint( $_POST['flow_id'] ) : 0;
1052 $step_id = isset( $_POST['step_id'] ) ? absint( $_POST['step_id'] ) : 0;
1053
1054 $remote_flow_id = isset( $_POST['remote_flow_id'] ) ? absint( $_POST['remote_flow_id'] ) : 0;
1055
1056 // Get single step Rest API response.
1057 $response = \CartFlows_API::get_instance()->get_flow( $remote_flow_id );
1058 if ( is_wp_error( $response['data'] ) ) {
1059 /* translators: %1$s: html tag, %2$s: link html start %3$s: link html end */
1060 $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>' );
1061
1062 wp_send_json_error(
1063 array(
1064 'message' => $response['data']->get_error_message(),
1065 'call_to_action' => $btn,
1066 'data' => $response,
1067 )
1068 );
1069 }
1070
1071 $license_status = isset( $response['data']['licence_status'] ) ? $response['data']['licence_status'] : '';
1072
1073 // If license is invalid then.
1074 if ( 'valid' !== $license_status ) {
1075
1076 $cf_pro_status = AdminMenu::get_instance()->get_plugin_status( 'cartflows-pro/cartflows-pro.php' );
1077
1078 $cta = '';
1079 if ( 'not-installed' === $cf_pro_status ) {
1080 /* translators: %1$s: link html start, %2$s: link html end*/
1081 $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>' );
1082 } elseif ( 'inactive' === $cf_pro_status ) {
1083 /* translators: %1$s: link html start, %2$s: link html end*/
1084 $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>' );
1085 } elseif ( 'active' === $cf_pro_status ) {
1086 /* translators: %1$s: link html start, %2$s: link html end*/
1087 $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>' );
1088 }
1089
1090 wp_send_json_error(
1091 array(
1092 'message' => \ucfirst( $license_status ) . ' license key! ' . $cta,
1093 'data' => $response,
1094 )
1095 );
1096 }
1097
1098 if ( empty( $remote_flow_id ) ) {
1099 $response_data = array( 'message' => __( 'Funnel data not found.', 'cartflows' ) );
1100 wp_send_json_error( $response_data );
1101 }
1102 $step['title'] = isset( $_POST['step_name'] ) && ! empty( $_POST['step_name'] ) ? sanitize_text_field( wp_unslash( $_POST['step_name'] ) ) : $step['title'];
1103 // Create steps.
1104 $this->update_single_step(
1105 array(
1106 'step' => array(
1107 'id' => $step['ID'],
1108 'title' => $step['title'],
1109 'type' => $step['type'],
1110 ),
1111 'flow' => array(
1112 'id' => $flow_id,
1113 ),
1114 'current_step' => array(
1115 'id' => $step_id,
1116 ),
1117 )
1118 );
1119
1120 wcf()->logger->import_log( 'COMPLETE! Importing Step' );
1121
1122 if ( empty( $step ) ) {
1123 $response_data = array( 'message' => __( 'Step data not found.', 'cartflows' ) );
1124 wp_send_json_error( $response_data );
1125 }
1126
1127 /**
1128 * Redirect to the new step edit screen
1129 */
1130 $response_data = array(
1131 'message' => __( 'Successfully imported the Step!', 'cartflows' ),
1132 );
1133
1134 wcf()->logger->import_log( 'COMPLETE! Importing Step' );
1135
1136 wp_send_json_success( $response_data );
1137 }
1138
1139 /**
1140 * Update Sinple Step
1141 *
1142 * @param array $args Rest API Arguments.
1143 * @return void
1144 */
1145 public function update_single_step( $args = array() ) {
1146
1147 wcf()->logger->import_log( 'STARTED! Updating Step' );
1148
1149 $step_id = isset( $args['step']['id'] ) ? absint( $args['step']['id'] ) : 0;
1150 $new_step_id = isset( $args['current_step']['id'] ) ? absint( $args['current_step']['id'] ) : '';
1151
1152 if ( empty( $step_id ) || empty( $new_step_id ) ) {
1153 /* translators: %s: step ID */
1154 wp_send_json_error( sprintf( __( 'Invalid step id %1$s or post id %2$s.', 'cartflows' ), $step_id, $new_step_id ) );
1155 }
1156
1157 wcf()->logger->import_log( 'Remote Step ' . $step_id . ' for local flow "' . get_the_title( $new_step_id ) . '" [' . $new_step_id . ']' );
1158
1159 // Get single step Rest API response.
1160 $response = \CartFlows_API::get_instance()->get_template( $step_id );
1161 wcf()->logger->import_log( wp_json_encode( $response ) );
1162
1163 if ( 'divi' === \Cartflows_Helper::get_common_setting( 'default_page_builder' ) ) {
1164 if ( isset( $response['data']['divi_content'] ) && ! empty( $response['data']['divi_content'] ) ) {
1165
1166 update_post_meta( $new_step_id, 'divi_content', $response['data']['divi_content'] );
1167
1168 wp_update_post(
1169 array(
1170 'ID' => $new_step_id,
1171 'post_content' => $response['data']['divi_content'],
1172 )
1173 );
1174 }
1175 }
1176
1177 if ( 'gutenberg' === \Cartflows_Helper::get_common_setting( 'default_page_builder' ) ) {
1178 if ( isset( $response['data']['divi_content'] ) && ! empty( $response['data']['divi_content'] ) ) {
1179 wp_update_post(
1180 array(
1181 'ID' => $new_step_id,
1182 'post_content' => $response['data']['divi_content'],
1183 )
1184 );
1185 }
1186 }
1187
1188 // Import Post Meta.
1189 $this->import_post_meta( $new_step_id, $response );
1190
1191 /* Imported Step */
1192 update_post_meta( $new_step_id, 'cartflows_imported_step', 'yes' );
1193
1194 do_action( 'cartflows_import_complete' );
1195
1196 // Batch Process.
1197 do_action( 'cartflows_after_template_import', $new_step_id, $response );
1198
1199 wcf()->logger->import_log( 'COMPLETE! Importing Step' );
1200 }
1201
1202 /**
1203 * Create Simple Step
1204 *
1205 * @param array $args Rest API Arguments.
1206 * @param string $action action name to check nonce.
1207 *
1208 * @return void
1209 */
1210 public function import_single_step( $args, $action ) {
1211
1212 /**
1213 * Nonce verification
1214 */
1215 if ( ! check_ajax_referer( $action, 'security', false ) ) {
1216 $response_data = array( 'message' => __( 'Nonce verification failed.', 'cartflows' ) );
1217 wp_send_json_error( $response_data );
1218 }
1219
1220 wcf()->logger->import_log( 'STARTED! Importing Step' );
1221
1222 $step_id = isset( $args['step']['id'] ) ? absint( $args['step']['id'] ) : 0;
1223 $step_title = isset( $args['step']['title'] ) ? $args['step']['title'] : '';
1224 $step_type = isset( $args['step']['type'] ) ? $args['step']['type'] : '';
1225 $flow_id = isset( $args['flow']['id'] ) ? absint( $args['flow']['id'] ) : '';
1226 $is_store_checkout = isset( $args['is_store_checkout'] ) ? $args['is_store_checkout'] : '';
1227
1228 // create steps only for checkout and thankyou if store checkout.
1229 // This logic will be removed once we have store checkout templates on server.
1230 if ( 'true' === $is_store_checkout && ! in_array( $step_type, array( 'checkout', 'thankyou' ), true ) ) {
1231 return;
1232 }
1233
1234 // Create new step.
1235 $new_step_id = \CartFlows_Importer::get_instance()->create_step( $flow_id, $step_type, $step_title );
1236
1237 if ( empty( $step_id ) || empty( $new_step_id ) ) {
1238 /* translators: %s: step ID */
1239 wp_send_json_error( sprintf( __( 'Invalid step id %1$s or post id %2$s.', 'cartflows' ), $step_id, $new_step_id ) );
1240 }
1241
1242 wcf()->logger->import_log( 'Remote Step ' . $step_id . ' for local flow "' . get_the_title( $new_step_id ) . '" [' . $new_step_id . ']' );
1243
1244 // Get single step Rest API response.
1245 $response = \CartFlows_API::get_instance()->get_template( $step_id );
1246
1247 wcf()->logger->import_log( wp_json_encode( $response ) );
1248
1249 // Return if there is an error while importing the step template.
1250 if ( ! $response['success'] ) {
1251 $response_data = $response['data'];
1252 $error_code = wp_remote_retrieve_response_code( $response_data );
1253 $error_msge = json_decode( wp_remote_retrieve_body( $response_data ) );
1254
1255 wp_send_json_error(
1256 array(
1257 'error_code' => $error_code,
1258 'message' => ! empty( $error_msge->message ) ? $error_msge->message : '',
1259 'data' => $response['data'],
1260 'success' => $response['success'],
1261 )
1262 );
1263 }
1264
1265 if ( 'divi' === \Cartflows_Helper::get_common_setting( 'default_page_builder' ) ) {
1266 if ( isset( $response['data']['divi_content'] ) && ! empty( $response['data']['divi_content'] ) ) {
1267
1268 update_post_meta( $new_step_id, 'divi_content', $response['data']['divi_content'] );
1269
1270 wp_update_post(
1271 array(
1272 'ID' => $new_step_id,
1273 'post_content' => $response['data']['divi_content'],
1274 )
1275 );
1276 }
1277 }
1278
1279 if ( 'gutenberg' === \Cartflows_Helper::get_common_setting( 'default_page_builder' ) ) {
1280 if ( isset( $response['data']['divi_content'] ) && ! empty( $response['data']['divi_content'] ) ) {
1281 wp_update_post(
1282 array(
1283 'ID' => $new_step_id,
1284 'post_content' => $response['data']['divi_content'],
1285 )
1286 );
1287 }
1288 }
1289
1290 // Import Post Meta.
1291 $this->import_post_meta( $new_step_id, $response );
1292
1293 if ( 'checkout' === $step_type ) {
1294
1295 $posted_data = array(
1296 'primary_color' => isset( $_POST['primary_color'] ) ? sanitize_text_field( wp_unslash( $_POST['primary_color'] ) ) : '',
1297 'site_logo' => isset( $_POST['site_logo'] ) ? array_map( 'sanitize_text_field', wp_unslash( $_POST['site_logo'] ) ) : '',
1298 );
1299
1300 $this->update_store_checkout_template_data( $new_step_id, $response, $posted_data );
1301 }
1302
1303 /* Imported Step */
1304 update_post_meta( $new_step_id, 'cartflows_imported_step', 'yes' );
1305
1306 do_action( 'cartflows_import_complete' );
1307
1308 // Batch Process.
1309 do_action( 'cartflows_after_template_import', $new_step_id, $response );
1310
1311 wcf()->logger->import_log( 'COMPLETE! Importing Step' );
1312 }
1313
1314 /**
1315 * Import Post Meta
1316 *
1317 * @since 1.0.0
1318 *
1319 * @param integer $post_id Post ID.
1320 * @param array $response Post meta.
1321 * @return void
1322 */
1323 public function import_post_meta( $post_id, $response ) {
1324
1325 $metadata = apply_filters( 'cartflows_template_import_meta_data', (array) $response['post_meta'] );
1326
1327 $exclude_meta_keys = \Cartflows_Helper::get_instance()->get_meta_keys_to_exclude_from_import( $post_id );
1328
1329 foreach ( $metadata as $meta_key => $meta_value ) {
1330
1331 if ( in_array( $meta_key, $exclude_meta_keys, true ) ) {
1332 continue;
1333 }
1334
1335 // Security: Only allow meta keys matching known prefixes to prevent arbitrary DB writes.
1336 if ( ! \Cartflows_Helper::get_instance()->is_meta_key_allowed_for_import( $meta_key ) ) {
1337 continue;
1338 }
1339
1340 $meta_value = isset( $meta_value[0] ) ? $meta_value[0] : '';
1341
1342 if ( $meta_value ) {
1343
1344 if ( is_serialized( $meta_value, true ) ) {
1345 // Security: Using unserialize with allowed_classes=>false to prevent object injection.
1346 $raw_data = unserialize( stripslashes( $meta_value ), array( 'allowed_classes' => false ) ); // phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.serialize_unserialize, PHPCompatibility.FunctionUse.NewFunctionParameters.unserialize_optionsFound
1347 // Drop malicious payloads completely to prevent fatal errors.
1348 // Security: Block serialized meta that contains nested PHP objects.
1349 // Even with allowed_classes => false, unserialized payloads may contain
1350 // __PHP_Incomplete_Class instances which WordPress attempts to mutate
1351 // during wp_unslash(), causing fatal errors and violating object safety.
1352 if (
1353 false === $raw_data ||
1354 $this->has_nested_object_payload( $raw_data )
1355 ) {
1356 $raw_data = '';
1357 continue;
1358 }
1359 } elseif ( is_array( $meta_value ) ) {
1360 $raw_data = json_decode( stripslashes( $meta_value ), true );
1361 } else {
1362 $raw_data = $meta_value;
1363 }
1364
1365 if ( '_elementor_data' === $meta_key ) {
1366
1367 if ( is_array( $raw_data ) ) {
1368 $raw_data = wp_slash( wp_json_encode( $raw_data ) );
1369 } else {
1370 $raw_data = wp_slash( $raw_data );
1371 }
1372 }
1373
1374 if ( '_elementor_data' !== $meta_key && '_elementor_draft' !== $meta_key && '_fl_builder_data' !== $meta_key && '_fl_builder_draft' !== $meta_key ) {
1375 if ( is_array( $raw_data ) ) {
1376 wcf()->logger->import_log( '✓ Added post meta ' . $meta_key /* . ' | ' . wp_json_encode( $raw_data ) */ );
1377 } elseif ( ! is_object( $raw_data ) ) {
1378 wcf()->logger->import_log( '✓ Added post meta ' . $meta_key /* . ' | ' . $raw_data */ );
1379 }
1380 }
1381 update_post_meta( $post_id, $meta_key, $raw_data );
1382 }
1383 }
1384 }
1385
1386 /**
1387 * Find the Checkout block and set the Primary color and site logo provided by the user.
1388 *
1389 * @since 1.10.0
1390 *
1391 * @param int $post_id newly created steps ID.
1392 * @param array $response data received from from the imported step.
1393 * @param array $posted_data post data.
1394 *
1395 * @return void
1396 */
1397 public function update_store_checkout_template_data( $post_id, $response, $posted_data ) {
1398
1399 $store_checkout_id = get_option( '_cartflows_store_checkout', false );
1400 $current_flow_id = (int) wcf()->utils->get_flow_id_from_step_id( $post_id );
1401 $default_page_bulder = \Cartflows_Helper::get_common_setting( 'default_page_builder' );
1402
1403 if ( empty( $posted_data['primary_color'] ) && empty( $posted_data['site_logo'] ) ) {
1404 return;
1405 }
1406
1407 if ( $store_checkout_id !== $current_flow_id ) {
1408 return;
1409 }
1410
1411 if ( 'elementor' === $default_page_bulder ) {
1412
1413 $metadata = (array) $response['post_meta'];
1414
1415 foreach ( $metadata as $meta_key => $meta_value ) {
1416
1417 $meta_value = isset( $meta_value[0] ) ? $meta_value[0] : '';
1418
1419 if ( $meta_value ) {
1420
1421 if ( is_serialized( $meta_value, true ) ) {
1422 // Security: Using unserialize with allowed_classes=>false to prevent object injection.
1423 $raw_data = unserialize( stripslashes( $meta_value ), array( 'allowed_classes' => false ) ); // phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.serialize_unserialize, PHPCompatibility.FunctionUse.NewFunctionParameters.unserialize_optionsFound
1424 if ( false === $raw_data || is_object( $raw_data ) ) {
1425 continue;
1426 }
1427 } elseif ( is_array( $meta_value ) ) {
1428 $raw_data = json_decode( stripslashes( $meta_value ), true );
1429 } else {
1430 $raw_data = $meta_value;
1431 }
1432
1433 if ( '_elementor_data' === $meta_key ) {
1434
1435 $raw_data = json_decode( $raw_data, true );
1436
1437 // Find the checkout-form and update the primary color.
1438 $this->elementor_find_and_replace_template_data( $raw_data, $posted_data );
1439
1440 if ( is_array( $raw_data ) ) {
1441 $raw_data = wp_slash( wp_json_encode( $raw_data ) );
1442 } else {
1443 $raw_data = wp_slash( $raw_data );
1444 }
1445
1446 update_post_meta( $post_id, $meta_key, $raw_data );
1447
1448 }
1449 }
1450 }
1451 } elseif ( 'gutenberg' === $default_page_bulder ) {
1452
1453 $post = get_post( $post_id );
1454 $blocks = parse_blocks( $post->post_content );
1455
1456 if ( is_array( $blocks ) && ! empty( $blocks ) ) {
1457
1458 $this->gutenberg_find_and_replace_template_data( $blocks, $posted_data );
1459
1460 if ( ! empty( $blocks ) ) {
1461 $serialized_blocks = serialize_blocks( $blocks );
1462
1463 wp_update_post(
1464 array(
1465 'ID' => $post_id,
1466 'post_content' => $serialized_blocks,
1467 )
1468 );
1469 }
1470 }
1471 } elseif ( 'beaver-builder' === $default_page_bulder ) {
1472 $data = \FLBuilderModel::get_layout_data( 'published', $post_id );
1473
1474 if ( ! empty( $data ) ) {
1475
1476 $this->beaver_builder_find_and_replace_template_data( $data, $posted_data );
1477
1478 // Update page builder data.
1479 update_post_meta( $post_id, '_fl_builder_data', $data );
1480 update_post_meta( $post_id, '_fl_builder_draft', $data );
1481 }
1482 }
1483 }
1484
1485
1486 /**
1487 * Get flows list for preview
1488 *
1489 * @return void
1490 */
1491 public function get_flows_list() {
1492
1493 $response_data = array( 'message' => $this->get_error_msg( 'permission' ) );
1494
1495 if ( ! current_user_can( 'cartflows_manage_flows_steps' ) ) {
1496 wp_send_json_error( $response_data );
1497 }
1498
1499 /**
1500 * Nonce verification
1501 */
1502 if ( ! check_ajax_referer( 'cartflows_get_flows_list', 'security', false ) ) {
1503 $response_data = array( 'message' => $this->get_error_msg( 'nonce' ) );
1504 wp_send_json_error( $response_data );
1505 }
1506
1507 $flows_list = \Cartflows_Helper::get_instance()->get_flows_and_steps();
1508
1509 /**
1510 * Redirect to the new step edit screen
1511 */
1512 $response_data = array(
1513 'message' => __( 'Successful!', 'cartflows' ),
1514 'flows' => $flows_list,
1515 );
1516
1517 wp_send_json_success( $response_data );
1518 }
1519
1520 /**
1521 * Get the elementor widget data.
1522 *
1523 * @param array $elements elements data.
1524 * @param array $posted_data posted data.
1525 */
1526 public function elementor_find_and_replace_template_data( &$elements, $posted_data ) {
1527
1528 foreach ( $elements as &$element ) {
1529
1530 if ( 'widget' === $element['elType'] && 'checkout-form' === $element['widgetType'] ) {
1531 $element['settings']['global_primary_color'] = ! empty( $posted_data['primary_color'] ) ? $posted_data['primary_color'] : $element['settings']['global_primary_color'];
1532 }
1533
1534 if ( 'widget' === $element['elType'] && 'image' === $element['widgetType'] && isset( $element['settings']['_css_classes'] ) ) {
1535
1536 if ( str_contains( $element['settings']['_css_classes'], 'cartflows-store-checkout-logo-field' ) ) {
1537 $element['settings']['image']['url'] = ! empty( $posted_data['site_logo'] ) ? $posted_data['site_logo']['url'] : $element['settings']['image']['url'];
1538 $element['settings']['image']['id'] = ! empty( $posted_data['site_logo'] ) ? $posted_data['site_logo']['id'] : $element['settings']['image']['id'];
1539 }
1540 }
1541
1542 if ( ! empty( $element['elements'] ) ) {
1543 $this->elementor_find_and_replace_template_data( $element['elements'], $posted_data );
1544 }
1545 }
1546 }
1547
1548 /**
1549 * Get the block data.
1550 *
1551 * @param array $elements elements data.
1552 * @param array $posted_data posted data.
1553 */
1554 public function gutenberg_find_and_replace_template_data( &$elements, $posted_data ) {
1555
1556 foreach ( $elements as &$element ) {
1557 if ( 'wcfb/checkout-form' === $element['blockName'] ) {
1558 // Update the element with the data.
1559 $element['attrs']['globalbgColor'] = ! empty( $posted_data['primary_color'] ) ? $posted_data['primary_color'] : $element['attrs']['globalbgColor'];
1560 }
1561
1562 if ( 'uagb/info-box' === $element['blockName'] && isset( $element['attrs']['className'] ) ) {
1563
1564 if ( str_contains( $element['attrs']['className'], 'cartflows-store-checkout-logo-field' ) ) {
1565 $element['attrs']['iconImage']['id'] = ! empty( $posted_data['site_logo'] ) ? $posted_data['site_logo']['id'] : $element['attrs']['iconImage']['id'];
1566 $element['attrs']['iconImage']['url'] = ! empty( $posted_data['site_logo'] ) ? $posted_data['site_logo']['url'] : $element['attrs']['iconImage']['url'];
1567 $element['attrs']['iconImage']['link'] = ! empty( $posted_data['site_logo'] ) ? $posted_data['site_logo']['url'] : $element['attrs']['iconImage']['link'];
1568 $element['attrs']['iconImage']['sizes']['full']['url'] = ! empty( $posted_data['site_logo'] ) ? $posted_data['site_logo']['url'] : $element['attrs']['iconImage']['link'];
1569 }
1570 }
1571
1572 if ( ! empty( $element['innerBlocks'] ) ) {
1573 $this->gutenberg_find_and_replace_template_data( $element['innerBlocks'], $posted_data );
1574 }
1575 }
1576 }
1577
1578 /**
1579 * Replace the logo and color in the BB template while importing.
1580 *
1581 * @param array $elements elements data.
1582 * @param array $posted_data posted data.
1583 */
1584 public function beaver_builder_find_and_replace_template_data( &$elements, $posted_data ) {
1585
1586 foreach ( $elements as $node => &$element ) {
1587 if ( ! empty( $element->type ) && 'module' === $element->type ) {
1588
1589 if ( ! empty( $element->settings->type ) && 'cartflows-bb-checkout-form' === $element->settings->type ) {
1590 // Update the logo in the template.
1591 $element->settings->global_primary_color = ! empty( $posted_data['primary_color'] ) ? $posted_data['primary_color'] : $element->settings->global_primary_color;
1592 }
1593
1594 if ( ! empty( $element->settings->type ) && 'photo' === $element->settings->type && ! empty( $element->settings->class ) && 'cartflows-store-checkout-logo-field' === $element->settings->class ) {
1595 $module_setting = $element->settings;
1596 // Update the logo in the template.
1597 $element->settings->photo_src = ! empty( $posted_data['site_logo'] ) ? $posted_data['site_logo']['url'] : $element->settings->photo_src;
1598
1599 }
1600 }
1601 }
1602 }
1603
1604 /**
1605 * Imports the Global Colors and Patterns (GCP) variables data for a given flow.
1606 *
1607 * This function updates the post meta for a flow with the GCP variables data received in the response.
1608 * It logs the import process and updates the post meta with the GCP data.
1609 *
1610 * @param array $response The response data containing the GCP variables.
1611 * @param int $flow_id The ID of the flow for which the GCP data is being imported.
1612 *
1613 * @return void
1614 */
1615 public function import_funnel_gcp_vars_data( $response, $flow_id ) {
1616
1617 wcf()->logger->import_log( 'Start: ' . __CLASS__ . ' :: ' . __FUNCTION__ );
1618
1619 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
1620
1621 if ( isset( $response['data']['flow_gcp_meta'] ) && ! empty( $response['data']['flow_gcp_meta'] ) && is_object( $response['data']['flow_gcp_meta'] ) ) {
1622
1623 wcf()->logger->import_log( 'Before Importing:' . print_r( $response['data']['flow_gcp_meta'], true ) ); // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_print_r
1624
1625 $gcp_data = (object) array_map( 'sanitize_text_field', (array) $response['data']['flow_gcp_meta'] );
1626
1627 wcf()->logger->import_log( 'After Importing: ' . print_r( $gcp_data, true ) ); // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_print_r
1628
1629 $gcp_meta_keys = array(
1630 'wcf-enable-gcp-styling' => 'yes',
1631 'wcf-gcp-primary-color' => ! empty( $gcp_data->gcp_primary_color ) ? $gcp_data->gcp_primary_color : '',
1632 'wcf-gcp-secondary-color' => ! empty( $gcp_data->gcp_secondary_color ) ? $gcp_data->gcp_secondary_color : '',
1633 'wcf-gcp-text-color' => ! empty( $gcp_data->gcp_text_color ) ? $gcp_data->gcp_text_color : '',
1634 'wcf-gcp-accent-color' => ! empty( $gcp_data->gcp_accent_color ) ? $gcp_data->gcp_accent_color : '',
1635 );
1636
1637 foreach ( $gcp_meta_keys as $key => $value ) {
1638 update_post_meta( $flow_id, $key, $value );
1639 }
1640 }
1641
1642 wcf()->logger->import_log( 'End: ' . __CLASS__ . ' :: ' . __FUNCTION__ );
1643 }
1644
1645 /**
1646 * Detect whether the given value contains objects at any depth.
1647 *
1648 * This is used as a hard security guard to prevent storing
1649 * unserialized payloads that contain PHP objects (including
1650 * __PHP_Incomplete_Class), which can cause fatal errors and
1651 * violate object injection protections.
1652 *
1653 * @since 2.2.1
1654 *
1655 * @param mixed $value The value to inspect recursively.
1656 *
1657 * @return bool True if an object is found anywhere in the payload.
1658 */
1659 private function has_nested_object_payload( $value ) {
1660
1661 if ( is_object( $value ) ) {
1662 return true;
1663 }
1664
1665 if ( is_array( $value ) ) {
1666 foreach ( $value as $v ) {
1667 if ( $this->has_nested_object_payload( $v ) ) {
1668 return true;
1669 }
1670 }
1671 }
1672
1673 return false;
1674 }
1675 }
1676