PluginProbe
CartFlows – Funnel Builder & Checkout Plugin for WooCommerce / 3.2.1
CartFlows – Funnel Builder & Checkout Plugin for WooCommerce v3.2.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 / classes / importer / class-cartflows-importer.php

class-cartflows-importer.php in CartFlows – Funnel Builder & Checkout Plugin for WooCommerce 3.2.1, at classes/importer/class-cartflows-importer.php

569 lines 16.2 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * CartFlows Admin
4 *
5 * @package CartFlows
6 * @since 1.0.0
7 */
8
9 // Exit if accessed directly.
10 if ( ! defined( 'ABSPATH' ) ) {
11 exit;
12 }
13 use CartflowsAdmin\AdminCore\Inc\AdminHelper;
14
15 if ( ! class_exists( 'CartFlows_Importer' ) ) :
16
17 /**
18 * CartFlows Import
19 *
20 * @since 1.0.0
21 */
22 class CartFlows_Importer {
23
24 /**
25 * Instance
26 *
27 * @since 1.0.0
28 * @access private
29 * @var object Class object.
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
44 return self::$instance;
45 }
46
47 /**
48 * Constructor
49 *
50 * @since 1.0.0
51 */
52 public function __construct() {
53
54 add_action( 'cartflows_import_complete', array( $this, 'clear_cache' ) );
55 }
56
57 /**
58 * Get flow export data
59 *
60 * @since 1.1.4
61 *
62 * @param integer $flow_id Flow ID.
63 * @return array
64 */
65 public function get_flow_export_data( $flow_id ) {
66
67 $export_all = apply_filters( 'cartflows_export_all', true );
68
69 $new_steps = array();
70 $steps = get_post_meta( $flow_id, 'wcf-steps', true );
71 if ( is_array( $steps ) ) {
72 foreach ( $steps as $key => $step ) {
73
74 // Add step post meta.
75 $all_meta = get_post_meta( $step['id'] );
76
77 $exclude_step_meta_keys = Cartflows_Helper::get_instance()->get_meta_keys_to_exclude_from_import( $step['id'] );
78
79 if ( is_array( $all_meta ) ) {
80
81 foreach ( $exclude_step_meta_keys as $meta_key ) {
82
83 if ( in_array( $meta_key, array_keys( $all_meta ), true ) ) {
84 unset( $all_meta[ $meta_key ] );
85 }
86 }
87 }
88
89 // Add single step.
90 $step_data_arr = array(
91 'title' => get_the_title( $step['id'] ),
92 'type' => $step['type'],
93 'meta' => $all_meta,
94 'post_content' => '',
95 );
96
97 if ( $export_all ) {
98 $step_post_obj = get_post( $step['id'] );
99 $step_data_arr['post_content'] = $step_post_obj->post_content;
100 }
101
102 $new_steps[] = $step_data_arr;
103 }
104 }
105
106 $store_checkout_id = (string) Cartflows_Helper::get_global_setting( '_cartflows_store_checkout' );
107 $flow_type = ( $store_checkout_id === (string) $flow_id ) ? 'store-checkout' : 'flows';
108
109 // Get all flow settings using AdminHelper.
110 $flow_settings = AdminHelper::get_flow_meta_options( $flow_id );
111
112 // Remove steps data as we're already handling it separately.
113 if ( isset( $flow_settings['wcf-steps'] ) ) {
114 unset( $flow_settings['wcf-steps'] );
115 }
116
117 if ( 'store-checkout' === $flow_type ) {
118 $common = Cartflows_Helper::get_common_settings();
119 $override_global_checkout = $common['override_global_checkout'];
120 $flow_settings['override_global_checkout'] = $override_global_checkout;
121 }
122
123 $flow_data = array(
124 'title' => get_the_title( $flow_id ),
125 'flow_type' => $flow_type,
126 'flow_meta' => $flow_settings,
127 'steps' => $new_steps,
128 );
129
130 return $flow_data;
131 }
132
133 /**
134 * Get all flow export data
135 *
136 * @since 1.1.4
137 */
138 public function get_all_flow_export_data() {
139
140 $query_args = array(
141 'post_type' => CARTFLOWS_FLOW_POST_TYPE,
142
143 // Query performance optimization.
144 'fields' => 'ids',
145 'no_found_rows' => true,
146 'posts_per_page' => -1,
147 );
148
149 $query = new WP_Query( $query_args );
150 $flows = array();
151 if ( $query->posts ) {
152 foreach ( $query->posts as $key => $post_id ) {
153 $flows[] = $this->get_flow_export_data( $post_id );
154 }
155 }
156
157 return $flows;
158 }
159
160
161 /**
162 * Import flow from the JSON data
163 *
164 * @since 1.6.15
165 * @param array $flows JSON array.
166 * @return void
167 */
168 public function import_from_json_data( $flows ) {
169 if ( $flows ) {
170
171 foreach ( $flows as $key => $flow ) {
172 $this->import_single_flow_from_json( $flow );
173 }
174 }
175 }
176
177 /**
178 * Import Single Flow from JSON
179 *
180 * @param array $flow flow_data.
181 * @param bool $return return value.
182 */
183 public function import_single_flow_from_json( $flow = array(), $return = false ) {
184
185 $default_page_builder = Cartflows_Helper::get_common_setting( 'default_page_builder' );
186
187 // Check if this is a store checkout flow.
188 if ( isset( $flow['flow_type'] ) && 'store-checkout' === $flow['flow_type'] ) {
189 // Get the current store checkout ID.
190 $store_checkout_id = Cartflows_Helper::get_global_setting( '_cartflows_store_checkout' );
191
192 if ( ! empty( $store_checkout_id ) ) {
193 wp_delete_post( absint( $store_checkout_id ), true );
194 }
195 }
196
197 $flow_title = $flow['title'];
198 if ( post_exists( $flow['title'] ) ) {
199 $flow_title = $flow['title'] . ' Copy';
200 }
201
202 // Create post object.
203 $new_flow_args = apply_filters(
204 'cartflows_flow_importer_args',
205 array(
206 'post_type' => CARTFLOWS_FLOW_POST_TYPE,
207 'post_title' => $flow_title,
208 'post_status' => 'publish',
209 )
210 );
211
212 // Insert the post into the database.
213 $flow_id = wp_insert_post( $new_flow_args );
214
215 // Import flow_meta settings from the imported flow.
216 if ( isset( $flow['flow_meta'] ) && is_array( $flow['flow_meta'] ) ) {
217 foreach ( $flow['flow_meta'] as $meta_key => $meta_value ) {
218 update_post_meta( $flow_id, $meta_key, $meta_value );
219 wcf()->logger->import_log( '(✓) Imported flow meta: ' . $meta_key );
220 }
221 }
222
223 // If this is a store checkout flow, update the global setting.
224 if ( isset( $flow['flow_type'] ) && 'store-checkout' === $flow['flow_type'] ) {
225 update_option( '_cartflows_store_checkout', $flow_id );
226 $old_global_checkout = get_option( '_cartflows_old_global_checkout', false );
227 $checkout_id = $old_global_checkout ? absint( $old_global_checkout ) : $flow_id;
228
229 // Reset global checkout on store checkout creation.
230 $common_settings = \Cartflows_Helper::get_common_settings();
231 $common_settings['global_checkout'] = $checkout_id;
232
233 if ( isset( $flow['flow_meta']['override_global_checkout'] ) ) {
234 $common_settings['override_global_checkout'] = $flow['flow_meta']['override_global_checkout'];
235 }
236
237 update_option( '_cartflows_common', $common_settings );
238 }
239
240 /**
241 * Fire after flow import
242 *
243 * @since 1.6.15
244 * @param int $flow_id Flow ID.
245 * @param array $new_flow_args Flow post args.
246 * @param array $flows Flow JSON data.
247 */
248 do_action( 'cartflows_flow_imported', $flow_id, $new_flow_args, $flow );
249
250 if ( $flow['steps'] ) {
251
252 $exclude_meta_keys = Cartflows_Helper::get_instance()->get_meta_keys_to_exclude_from_import();
253
254 foreach ( $flow['steps'] as $key => $step ) {
255
256 $new_all_meta = array();
257 if ( is_array( $step['meta'] ) ) {
258
259 foreach ( $step['meta'] as $meta_key => $mvalue ) {
260
261 if ( in_array( $meta_key, $exclude_meta_keys, true ) ) {
262 continue;
263 }
264
265 // Security: Only allow meta keys matching known prefixes to prevent arbitrary DB writes.
266 if ( ! Cartflows_Helper::get_instance()->is_meta_key_allowed_for_import( $meta_key ) ) {
267 continue;
268 }
269
270 if ( is_serialized( $mvalue[0], true ) ) {
271 // Security: Using unserialize with allowed_classes=>false to prevent object injection.
272 $meta_value = unserialize( stripslashes( $mvalue[0] ), array( 'allowed_classes' => false ) ); // phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.serialize_unserialize, PHPCompatibility.FunctionUse.NewFunctionParameters.unserialize_optionsFound
273 // Drop malicious payloads completely to prevent fatal errors.
274 if ( false === $meta_value || is_object( $meta_value ) ) {
275 $meta_value = '';
276 }
277 } else {
278 $meta_value = $mvalue[0];
279 }
280
281 if ( '_elementor_data' === $meta_key ) {
282
283 if ( is_array( $meta_value ) ) {
284 $meta_value = wp_slash( wp_json_encode( $meta_value ) );
285 } else {
286 $meta_value = wp_slash( $meta_value );
287 }
288 }
289
290 /**
291 * Commented the below code and added the above code check and convert the elementor data into the proper format.
292 * Kept this code for future code reference. Remove it after two updates.
293 * $meta_value = maybe_unserialize( $mvalue[0] );
294 * */
295
296 $new_all_meta[ $meta_key ] = $meta_value;
297
298 }
299 }
300 $post_content = isset( $step['post_content'] ) ? ( 'gutenberg' === $default_page_builder ? $step['post_content'] : wp_slash( wp_json_encode( $step['post_content'] ) ) ) : '';
301
302
303 $new_step_args = apply_filters(
304 'cartflows_step_importer_args',
305 array(
306 'post_type' => CARTFLOWS_STEP_POST_TYPE,
307 'post_title' => $step['title'],
308 'post_status' => 'publish',
309 'meta_input' => $new_all_meta,
310 'post_content' => $post_content,
311 )
312 );
313 $new_step_id = wp_insert_post( $new_step_args );
314
315 /**
316 * Fire after step import
317 *
318 * @since 1.6.15
319 * @param int $new_step_id step ID.
320 * @param int $flow_id flow ID.
321 * @param array $new_step_args Step post args.
322 * @param array $flow_steps Flow steps.
323 * @param array $flows All flows JSON data.
324 */
325 do_action( 'cartflows_step_imported', $new_step_id, $flow_id, $new_step_args, $flow['steps'] );
326
327 // Insert post meta.
328 update_post_meta( $new_step_id, 'wcf-flow-id', $flow_id );
329
330 $step_taxonomy = CARTFLOWS_TAXONOMY_STEP_TYPE;
331 $current_term = term_exists( $step['type'], $step_taxonomy );
332
333 $step_slug = $step['type'];
334
335 if ( isset( $current_term['term_id'] ) ) {
336 // Set type object.
337 $data = get_term( $current_term['term_id'], $step_taxonomy );
338 $step_slug = $data->slug;
339 }
340
341 // Set term step type.
342 wp_set_object_terms( $new_step_id, $step_slug, $step_taxonomy );
343
344 // Set type.
345 update_post_meta( $new_step_id, 'wcf-step-type', $step_slug );
346
347 // Set flow.
348 wp_set_object_terms( $new_step_id, 'flow-' . $flow_id, CARTFLOWS_TAXONOMY_STEP_FLOW );
349
350 self::get_instance()->set_step_to_flow( $flow_id, $new_step_id, $step['title'], $step_slug );
351
352 if ( apply_filters( 'cartflows_enable_imported_content_processing', true ) ) {
353 if ( isset( $step['post_content'] ) && ! empty( $step['post_content'] ) ) {
354
355 // Download and replace images.
356 $content = $this->get_content( $step['post_content'] );
357
358 $is_divi = ( 'other' === $default_page_builder ) && ( class_exists( 'ET_Builder_Plugin' ) || Cartflows_Compatibility::get_instance()->is_divi_enabled() );
359
360 // Encode content if the page builder is not DIVI and Gutenberg.
361 $encode_content = ! $is_divi && 'gutenberg' !== $default_page_builder;
362 $post_content = $encode_content ? wp_slash( wp_json_encode( $content ) ) : $content;
363
364 // Update post content.
365 wp_update_post(
366 array(
367 'ID' => intval( $new_step_id ),
368 // If the page builder is DIVI then pass the content as it is but for rest of the page builders, Encrypt it.
369 'post_content' => $post_content,
370 )
371 );
372 }
373
374 // Elementor Data.
375 if ( ( 'elementor' === $default_page_builder ) && class_exists( '\Elementor\Plugin' ) ) {
376 // Add "elementor" in import [queue].
377 // @todo Remove required `allow_url_fopen` support.
378 if ( ini_get( 'allow_url_fopen' ) && isset( $step['meta']['_elementor_data'] ) ) {
379 $obj = new \Elementor\TemplateLibrary\CartFlows_Importer_Elementor();
380 $obj->import_single_template( $new_step_id );
381 }
382 }
383
384 // Beaver Builder.
385 if ( ( 'beaver-builder' === $default_page_builder ) && class_exists( 'FLBuilder' ) ) {
386 if ( isset( $step['meta']['_fl_builder_data'] ) ) {
387 CartFlows_Importer_Beaver_Builder::get_instance()->import_single_post( $new_step_id );
388 }
389 }
390 }
391 }
392 }
393
394 if ( $return ) {
395 return array(
396 'flow_id' => $flow_id,
397 'edit_link' => admin_url( 'post.php?action=edit&post=' . $flow_id ),
398 );
399 }
400 }
401
402 /**
403 * Download and Replace hotlink images
404 *
405 * @since 1.6.15
406 *
407 * @param string $content Mixed post content.
408 * @return array Hotlink image array.
409 */
410 public function get_content( $content = '' ) {
411
412 $content = stripslashes( $content );
413
414 // Extract all links.
415 $all_links = wp_extract_urls( $content );
416
417 // Not have any link.
418 if ( empty( $all_links ) ) {
419 return $content;
420 }
421
422 $link_mapping = array();
423 $image_links = array();
424 $other_links = array();
425
426 // Extract normal and image links.
427 foreach ( $all_links as $key => $link ) {
428 if ( preg_match( '/^((https?:\/\/)|(www\.))([a-z0-9-].?)+(:[0-9]+)?\/[\w\-]+\.(jpg|png|gif|jpeg)\/?$/i', $link ) ) {
429
430 // Get all image links.
431 // Avoid *-150x, *-300x and *-1024x images.
432 if (
433 false === strpos( $link, '-150x' ) &&
434 false === strpos( $link, '-300x' ) &&
435 false === strpos( $link, '-1024x' )
436 ) {
437 $image_links[] = $link;
438 }
439 } else {
440
441 // Collect other links.
442 $other_links[] = $link;
443 }
444 }
445
446 // Step 1: Download images.
447 if ( ! empty( $image_links ) ) {
448 foreach ( $image_links as $key => $image_url ) {
449 // Download remote image.
450 $image = array(
451 'url' => $image_url,
452 'id' => 0,
453 );
454 $downloaded_image = CartFlows_Import_Image::get_instance()->import( $image );
455
456 // Old and New image mapping links.
457 $link_mapping[ $image_url ] = $downloaded_image['url'];
458 }
459 }
460
461 // Step 3: Replace mapping links.
462 foreach ( $link_mapping as $old_url => $new_url ) {
463 $content = str_replace( $old_url, $new_url, $content );
464
465 // Replace the slashed URLs if any exist.
466 $old_url = str_replace( '/', '/\\', $old_url );
467 $new_url = str_replace( '/', '/\\', $new_url );
468 $content = str_replace( $old_url, $new_url, $content );
469 }
470
471 return $content;
472 }
473
474 /**
475 * Clear Cache.
476 *
477 * @since 1.0.0
478 */
479 public function clear_cache() {
480 // Clear 'Elementor' file cache.
481 if ( class_exists( '\Elementor\Plugin' ) ) {
482 \Elementor\Plugin::$instance->files_manager->clear_cache();
483 }
484 }
485
486 /**
487 * Set steps to the flow
488 *
489 * @param integer $flow_id Flow ID.
490 * @param integer $new_step_id New step ID.
491 * @param string $step_title Flow Type.
492 * @param string $step_slug Flow Type.
493 */
494 public function set_step_to_flow( $flow_id, $new_step_id, $step_title, $step_slug ) {
495 // Update steps for the current flow.
496 $flow_steps = get_post_meta( $flow_id, 'wcf-steps', true );
497
498 if ( ! is_array( $flow_steps ) ) {
499 $flow_steps = array();
500 }
501
502 $flow_steps[] = array(
503 'id' => $new_step_id,
504 'title' => $step_title,
505 'type' => $step_slug,
506 );
507 update_post_meta( $flow_id, 'wcf-steps', $flow_steps );
508 wcf()->logger->import_log( '(✓) Updated flow steps post meta key \'wcf-steps\' ' . wp_json_encode( $flow_steps ) );
509 }
510
511 /**
512 * Create step for given flow.
513 *
514 * @param int $flow_id flow ID.
515 * @param int $step_type step type.
516 * @param int $step_title step title.
517 * @since 1.0.0
518 *
519 * @return mixed
520 */
521 public function create_step( $flow_id, $step_type, $step_title ) {
522
523 $args = array(
524 'post_type' => CARTFLOWS_STEP_POST_TYPE,
525 'post_title' => $step_title,
526 'post_status' => 'publish',
527 );
528
529 $new_step_id = wp_insert_post( $args );
530
531 if ( $new_step_id ) {
532
533 $flow_steps = get_post_meta( $flow_id, 'wcf-steps', true );
534
535 if ( ! is_array( $flow_steps ) ) {
536 $flow_steps = array();
537 }
538
539 $flow_steps[] = array(
540 'id' => $new_step_id,
541 'title' => $step_title,
542 'type' => $step_type,
543 );
544
545 $flow_steps = Cartflows_Helper::get_instance()->maybe_update_flow_steps( $flow_id, $flow_steps );
546
547 // insert post meta.
548 update_post_meta( $new_step_id, 'wcf-flow-id', $flow_id );
549 update_post_meta( $new_step_id, 'wcf-step-type', $step_type );
550 update_post_meta( $new_step_id, '_wp_page_template', 'cartflows-default' );
551
552 wp_set_object_terms( $new_step_id, $step_type, CARTFLOWS_TAXONOMY_STEP_TYPE );
553 wp_set_object_terms( $new_step_id, 'flow-' . $flow_id, CARTFLOWS_TAXONOMY_STEP_FLOW );
554 }
555
556 update_post_meta( $flow_id, 'wcf-steps', $flow_steps );
557 do_action( 'cartflows_after_create_step', $new_step_id, $step_type );
558
559 return $new_step_id;
560 }
561 }
562
563 /**
564 * Initialize class object with 'get_instance()' method
565 */
566 CartFlows_Importer::get_instance();
567
568 endif;
569