PluginProbe
WPFunnels – Funnel Builder for WooCommerce with Checkout & One Click Upsell / 3.10.9
WPFunnels – Funnel Builder for WooCommerce with Checkout & One Click Upsell v3.10.9
3.13.3 3.13.2 3.13.1 3.13.0 3.12.13 3.12.12 3.12.11 3.12.10 3.12.9 3.12.8 3.12.7 3.12.6 3.12.5 3.12.4 3.12.3 3.12.1 3.12.2 3.12.0 3.11.1 3.11.0 3.10.9 3.10.8 3.10.7 3.10.6 2.8.16 All 261 releases
wpfunnels / includes / core / import-export / class-wpfnl-import-funnel.php

class-wpfnl-import-funnel.php in WPFunnels – Funnel Builder for WooCommerce with Checkout & One Click Upsell 3.10.9, at includes/core/import-export/class-wpfnl-import-funnel.php

649 lines 29.3 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 namespace WPFunnels\Import;
4 use WPFunnels\Wpfnl_functions;
5 use WPFunnels\Wpfnl;
6 use WPFunnels\Data_Store\Wpfnl_Steps_Store_Data;
7 use lementor\TemplateLibrary\Source_Base;
8 use WPFunnelsPro\AbTesting\Wpfnl_Ab_Testing;
9 use Elementor\Plugin;
10 use WPFunnels\Admin\Notices\Notice as Admin_Notice;
11
12 /**
13 * This class is responsible import funnel
14 *
15 * Import json file
16 * @since 1.7.5
17 */
18 class Wpfnl_Import {
19
20 /**
21 * Initialize ajax callback
22 *
23 * @since 1.7.5
24 */
25 public function __construct() {
26 add_action( 'wp_ajax_wpfnl_import_funnels', [ $this, 'schedule_import_funnels' ] );
27 add_action( 'wp_ajax_nopriv_wpfnl_import_funnels', [ $this, 'schedule_import_funnels' ] );
28 add_action( 'wp_ajax_wpfnl_hide_import_funnel_notice', [ $this, 'hide_import_funnel_notice' ] );
29 add_action( 'wp_ajax_nopriv_wpfnl_hide_import_funnel_notice', [ $this, 'hide_import_funnel_notice' ] );
30 add_action( 'wpfnl_import_funnels', [ $this, 'import_funnels' ] );
31 add_filter( 'wpfnl_notices', [ $this, 'add_funnel_notice' ] );
32 }
33
34 /**
35 * Adds a custom admin notice to the WordPress admin dashboard.
36 *
37 * This function is a callback used to add a custom notice to the admin dashboard.
38 * The notice is added to the existing array of notices.
39 *
40 * @param array $notices The array of existing admin notices.
41 *
42 * @return array The updated array of admin notices with the added custom notice.
43 * @since 1.9.7
44 */
45 public function add_funnel_notice( $notices ) {
46 $notices[ 'WPFunnels\Import\Wpfnl_Import' ]= 'funnel_import_notice';
47 return $notices;
48 }
49
50 /**
51 * Displays a notice related to funnel import status.
52 *
53 * This static method is used to display a notice related to the funnel import status in the WordPress admin area.
54 * It checks whether the import status option is not empty and whether the current screen context allows displaying
55 * the notice. If the conditions are met, a notice is generated using the provided options and displayed in the admin area.
56 * The notice can be dismissed by the user.
57 *
58 * @since 1.9.7
59 */
60 public static function funnel_import_notice() {
61 $import_status = get_option( 'wpfnl_import_notice', '' );
62 if( !empty( $import_status ) ) {
63 global $current_screen;
64 $disabled_notice_page = [ 'wp_funnels' ];
65 if( 'plugins' === $current_screen->parent_base || !isset( $_GET[ 'page' ] ) || ( isset( $_GET[ 'page' ] ) && in_array( $_GET[ 'page' ], $disabled_notice_page ) ) ) {
66 $nonce_field = "<input type='hidden' id='wpfnl-import-nonce' value='" . esc_attr( wp_create_nonce('wpfnl-admin') ) . "'>";
67 $options = array(
68 'id' => 'wpfnl-import-notice',
69 'title' => 'wpfunnels-basic',
70 'description' => "<span>{$import_status}</span>{$nonce_field}",
71 'classes' => [ 'notice', 'notice-info', 'wpfnl-import-notice' ],
72 'type' => 'update-plugin',
73 'dismissible' => true,
74 'icon' => '',
75 );
76 Admin_Notice::print_notice( $options );
77 }
78 }
79 }
80
81 /**
82 * Schedule the import of funnels as a background process.
83 *
84 * This method is responsible for scheduling the import of funnels as a background process. It checks if the 'wpfnl_import_funnels'
85 * action has not already been scheduled, and if so, it prepares the necessary file for import and schedules the background process.
86 * The process involves uploading the imported file, updating the import notice, and enqueueing the 'wpfnl_import_funnels' action
87 * to be executed asynchronously using a background processing mechanism (such as WP Async Task).
88 *
89 * @since 1.9.7
90 * @updated 2.5.14 - Added security checks
91 */
92 public function schedule_import_funnels() {
93 // Security: Check if user is logged in
94 if ( ! is_user_logged_in() ) {
95 wp_send_json_error( [ 'message' => __( 'Unauthorized access.', 'wpfnl-pro' ) ], 401 );
96 wp_die();
97 }
98
99 // Security: Check user capability
100 if ( ! current_user_can( 'manage_options' ) ) {
101 wp_send_json_error( [ 'message' => __( 'You do not have permission to import funnels.', 'wpfnl-pro' ) ], 403 );
102 wp_die();
103 }
104
105 // Security: Verify nonce
106 if ( ! isset( $_POST['nonce'] ) || ! wp_verify_nonce( sanitize_text_field( wp_unslash( $_POST['nonce'] ) ), 'wpfnl_import_funnels_nonce' ) ) {
107 wp_send_json_error( [ 'message' => __( 'Security check failed.', 'wpfnl-pro' ) ], 403 );
108 wp_die();
109 }
110
111 $is_pro = apply_filters( 'wpfunnels/is_pro_license_activated', false );
112
113 if( !as_has_scheduled_action( 'wpfnl_import_funnels' ) ) {
114 $file = $this->upload_imported_file();
115 if( is_wp_error( $file ) ) {
116 wp_send_json_error( [ 'message' => $file->get_error_message() ], 400 );
117 wp_die();
118 }
119 if( file_exists( $file ) ) {
120 update_option( 'wpfnl_import_notice', 'Funnels import in-progress...' );
121 as_enqueue_async_action( 'wpfnl_import_funnels', [ $file ], 'wpfunnels-import' );
122 wp_send_json_success( [ 'message' => __( 'Funnel import scheduled successfully.', 'wpfnl-pro' ) ] );
123 } else {
124 wp_send_json_error( [ 'message' => __( 'Failed to upload file.', 'wpfnl-pro' ) ], 400 );
125 }
126 } else {
127 wp_send_json_error( [ 'message' => __( 'Import already scheduled.', 'wpfnl-pro' ) ], 400 );
128 }
129 wp_die();
130 }
131
132 /**
133 * Uploads an imported file to the specified directory in the WordPress uploads folder.
134 *
135 * This private method is used to upload an imported file to a specific directory within the WordPress uploads folder.
136 * It ensures that the target directory exists and creates it if not present. The method reads the temporary uploaded
137 * file, extracts its content, and saves it to the target file location. The saved file's name is based on the original
138 * uploaded filename, and if no filename is provided, a default name is used.
139 *
140 * @return string|WP_Error The full path to the uploaded file on success, or WP_Error on failure.
141 * @since 1.9.7
142 * @updated 2.5.14 - Added comprehensive security validation
143 */
144 private function upload_imported_file() {
145 // Security: Validate file upload exists
146 if ( ! isset( $_FILES['uploaded_file'] ) || ! isset( $_FILES['uploaded_file']['tmp_name'] ) ) {
147 return new \WP_Error( 'no_file', __( 'No file uploaded.', 'wpfnl-pro' ) );
148 }
149
150 // Security: Check for upload errors
151 if ( $_FILES['uploaded_file']['error'] !== UPLOAD_ERR_OK ) {
152 return new \WP_Error( 'upload_error', __( 'File upload failed.', 'wpfnl-pro' ) );
153 }
154
155 $temp_file = sanitize_text_field( wp_unslash( $_FILES['uploaded_file']['tmp_name'] ) );
156 $file_name = sanitize_file_name( wp_unslash( $_FILES['uploaded_file']['name'] ) );
157 $file_size = intval( $_FILES['uploaded_file']['size'] );
158
159 // Security: Validate file is not empty
160 if ( empty( $temp_file ) || ! file_exists( $temp_file ) ) {
161 return new \WP_Error( 'invalid_file', __( 'Invalid file upload.', 'wpfnl-pro' ) );
162 }
163
164 // Security: Validate file size (max 10MB)
165 $max_size = 10 * 1024 * 1024; // 10MB
166 if ( $file_size > $max_size || $file_size === 0 ) {
167 return new \WP_Error( 'file_too_large', __( 'File size must be between 1 byte and 10MB.', 'wpfnl-pro' ) );
168 }
169
170 // Security: Validate file extension
171 $file_ext = strtolower( pathinfo( $file_name, PATHINFO_EXTENSION ) );
172 if ( $file_ext !== 'json' ) {
173 return new \WP_Error( 'invalid_extension', __( 'Only JSON files are allowed.', 'wpfnl-pro' ) );
174 }
175
176 // Security: Validate MIME type
177 $file_type = wp_check_filetype( $file_name, [ 'json' => 'application/json' ] );
178 if ( ! $file_type['ext'] ) {
179 return new \WP_Error( 'invalid_mime', __( 'Invalid file type. Only JSON files are allowed.', 'wpfnl-pro' ) );
180 }
181
182 // Security: Read and validate JSON content
183 $content = file_get_contents( $temp_file );
184 if ( $content === false ) {
185 return new \WP_Error( 'read_error', __( 'Failed to read file content.', 'wpfnl-pro' ) );
186 }
187
188 // Security: Validate JSON structure
189 json_decode( $content, true );
190 if ( json_last_error() !== JSON_ERROR_NONE ) {
191 return new \WP_Error( 'invalid_json', __( 'Invalid JSON format.', 'wpfnl-pro' ) );
192 }
193
194 // Security: Check for PHP code in content
195 if ( preg_match( '/<\?php|<\?=|<script|javascript:/i', $content ) ) {
196 return new \WP_Error( 'malicious_content', __( 'File contains potentially malicious content.', 'wpfnl-pro' ) );
197 }
198
199 $path = wp_upload_dir();
200 $path = $path['basedir'] . '/wpfunnels/import/';
201
202 // Make directory if not exist
203 if ( ! file_exists( $path ) ) {
204 wp_mkdir_p( $path );
205 }
206
207 // Security: Use unique filename to prevent overwrites
208 $unique_filename = wp_unique_filename( $path, $file_name );
209 $file_path = $path . $unique_filename;
210
211 // Security: Write file with restricted permissions
212 $bytes_written = file_put_contents( $file_path, $content );
213 if ( $bytes_written === false ) {
214 return new \WP_Error( 'write_error', __( 'Failed to save file.', 'wpfnl-pro' ) );
215 }
216
217 // Security: Set proper file permissions (read/write for owner only)
218 chmod( $file_path, 0600 );
219
220 return $file_path;
221 }
222
223
224 /**
225 * Import Funnel data and create
226 *
227 * @param $file
228 *
229 * @return true
230 * @since 1.7.5
231 */
232 public function import_funnels( $file ) {
233 $file_data = [];
234 if( file_exists( $file ) ) {
235 $file_data = json_decode( file_get_contents( $file ), true );
236 unlink( $file );
237 }
238 $message = __( 'The funnels import process has been successfully completed.', 'wpfnl-pro' );
239
240 if( is_array( $file_data ) ) {
241 $funnel = Wpfnl::$instance->funnel_store;
242 $exclude_meta = array( '_is_imported', '_steps_order', '_steps', '_funnel_data');
243 $is_pro = apply_filters( 'wpfunnels/is_pro_license_activated', false );
244 foreach( $file_data as $data ) {
245 if( empty( $data[ 'funnel_id' ] ) ) {
246 $message = __( 'Invalid funnel(s) data.', 'wpfnl-pro' );
247 break;
248 }
249
250 if( !$is_pro ) {
251 $imported_type = isset( $data['funnel_meta']['_wpfnl_funnel_type'][0] ) ? $data['funnel_meta']['_wpfnl_funnel_type'][0] : '';
252 $is_importing_sc = ( 'store_checkout' === $imported_type );
253 if ( $is_importing_sc ) {
254 $current_count = \WPFunnels\Wpfnl_functions::count_store_checkout_funnels();
255 if ( $current_count >= 3 ) {
256 $message = __( 'You have reached the maximum limit of 3 store checkouts on the free plan. Upgrade to Pro for unlimited store checkouts.', 'wpfnl' );
257 break;
258 }
259 } else {
260 $current_count = \WPFunnels\Wpfnl_functions::count_non_store_checkout_funnels();
261 if( $current_count >= 3 ) {
262 $message = __( 'You have reached the maximum limit of 3 funnels on the free plan. Upgrade to Pro for unlimited funnels.', 'wpfnl' );
263 break;
264 }
265 }
266 }
267
268 $funnel_data = [];
269 $funnel_name = $data[ 'funnel_name' ] ?? 'New Funnel';
270 $funnel_id = $funnel->create( $funnel_name );
271 $funnel->update_meta( $funnel_id, '_is_imported', 'yes' );
272
273 foreach( $data[ 'funnel_meta' ] as $meta_key => $meta ) {
274 if( !in_array( $meta_key, $exclude_meta ) ) {
275 $funnel->update_meta( $funnel_id, $meta_key, maybe_unserialize( $data[ 'funnel_meta' ][ $meta_key ][ 0 ] ) );
276 }
277 else {
278 if( 'funnel_data' === $meta_key ) {
279 $funnel_data = maybe_unserialize( $data[ 'funnel_meta' ][ $meta_key ][ 0 ] );
280 }
281
282 if( '_funnel_data' === $meta_key ) {
283 $funnel_data = maybe_unserialize( $data[ 'funnel_meta' ][ $meta_key ][ 0 ] );
284 }
285 }
286 }
287 $_new_step_ids = [];
288 $_new_step_order = [];
289
290 foreach( $data[ 'steps_data' ] as $step_data ) {
291 $response = $this->import_step( $funnel_id, $step_data );
292 if( isset( $response[ 'id' ], $response[ 'title' ], $response[ 'type' ] ) ) {
293 $_new_step_ids[ $step_data[ 'id' ] ] = $response[ 'id' ];
294 $_new_step_order[] = [
295 'id' => $response[ 'id' ],
296 'step_type' => $response[ 'type' ],
297 'name' => $response[ 'title' ],
298 ];
299 }
300 else {
301 $message = __( 'Invalid funnel(s) data.', 'wpfnl-pro' );
302 break;
303 }
304 }
305
306 $this->update_funnel_data( $_new_step_ids, $funnel_id, $funnel_data );
307 update_post_meta( $funnel_id, '_steps_order', $_new_step_order );
308 update_post_meta( $funnel_id, '_steps', $_new_step_order );
309
310 $instance = new Wpfnl_functions();
311 if( method_exists( $instance, 'generate_first_step' ) ) {
312 Wpfnl_functions::generate_first_step( $funnel_id, [] );
313 }
314 }
315 }
316
317 update_option( 'wpfnl_import_notice', $message );
318 return true;
319 }
320
321
322 /**
323 * Import step from json
324 * @param Int $funnel_id
325 * @param Array $step_data
326 * @return
327 * @since 1.7.5
328 *
329 */
330 public function import_step($funnel_id, $step_data){
331 if (!is_array($step_data)) {
332 return false;
333 }
334 $builder_type = Wpfnl_functions::get_builder_type();
335 $previous_step_id = isset($step_data['id']) ? $step_data['id'] : '';
336 $title = isset($step_data['title']) ? $step_data['title'] : '';
337 $slug = isset($step_data['slug']) ? $step_data['slug'] : '';
338 $page_template = isset($step_data['page_template']) ? $step_data['page_template'] : '';
339 $post_content = isset($step_data['post_content']) ? $step_data['post_content'] : '';
340 $step_type = isset($step_data['type']) ? $step_data['type'] : '';
341 $step = new Wpfnl_Steps_Store_Data();
342 $step_id = $step->create_step($funnel_id, $title, $step_type, $post_content, true);
343
344 if ($step_id) {
345
346 if ($slug) {
347 wp_update_post(array(
348 'ID' => $step_id,
349 'post_name' => $slug
350 ));
351 }
352
353 $step->update_meta($step_id, '_funnel_id', $funnel_id);
354 $step->update_meta($step_id, '_wp_page_template', $page_template);
355 $exclude_meta = array('_wpfnl_ab_testing_start_settings', '_funnel_id');
356 $sql_query_arr = [];
357 $is_enable_condition_meta_blank = false;
358 $is_condition_exits = false;
359 foreach ($step_data['meta'] as $meta_key => $meta) {
360
361 if (isset($meta[0])) {
362 if (!in_array($meta_key, $exclude_meta)) {
363 if ('_wpfnl_maybe_enable_condition' === $meta_key) {
364 if ('' === $meta[0]) {
365 $is_enable_condition_meta_blank = true;
366 }
367 }
368
369 if ('_wpfnl_step_conditions' === $meta_key) {
370 if ($meta[0]) {
371 $is_condition_exits = true;
372 }
373 }
374
375 $sql_data = [
376 'post_id' => $step_id,
377 'meta_key' => $meta_key,
378 'meta_value' => $meta[0],
379 ];
380 $sql_query_arr[] = $sql_data;
381 } elseif ('_wpfnl_ab_testing_start_settings' === $meta_key) {
382
383 $this->update_ab_testing_settings($funnel_id, $step_id, $meta[0], $step_data['ab_step_data']);
384 }
385 }
386 }
387 $this->update_meta_by_sql($sql_query_arr); // update meta
388
389 if ($is_enable_condition_meta_blank && $is_condition_exits) {
390 update_post_meta($step_id, '_wpfnl_maybe_enable_condition', 'yes');
391 }
392
393 if (('gutenberg' === $builder_type) && class_exists('\WPFunnels\Batch\Gutenberg\Wpfnl_Gutenberg_Source')) {
394 if (ini_get('allow_url_fopen')) {
395 $gutenberg_source = new \WPFunnels\Batch\Gutenberg\Wpfnl_Gutenberg_Source;
396 $gutenberg_source->import_single_template($step_id);
397 }
398 }
399
400 // Elementor Data.
401 if (('elementor' === $builder_type) && class_exists('\Elementor\Plugin') && class_exists('\WPFunnels\Batch\Elementor\Wpfnl_Elementor_Source')) {
402 if (ini_get('allow_url_fopen')) {
403 $elementor_source = new \WPFunnels\Batch\Elementor\Wpfnl_Elementor_Source;
404 $elementor_source->import_single_template($step_id);
405 Plugin::$instance->files_manager->clear_cache();
406 }
407 }
408
409 if (('divi-builder' === $builder_type) && class_exists('\WPFunnels\Batch\Divi\Wpfnl_Divi_Source')) {
410 if (ini_get('allow_url_fopen')) {
411 $divi_source = new \WPFunnels\Batch\Divi\Wpfnl_Divi_Source;
412 $divi_source->import_single_template($step_id);
413 }
414 }
415
416 if (('oxygen' === $builder_type) && class_exists('\WPFunnels\Batch\Oxygen\Wpfnl_Oxygen_Source')) {
417 if (ini_get('allow_url_fopen')) {
418 $oxygen_source = new \WPFunnels\Batch\Oxygen\Wpfnl_Oxygen_Source;
419 $oxygen_source->import_single_template($step_id);
420 }
421 }
422 delete_post_meta($step_id, '_funnel_id');
423 update_post_meta($step_id, '_funnel_id', $funnel_id);
424
425 return [
426 'id' => $step_id,
427 'type' => $step_type,
428 'title' => $title,
429 ];
430 }
431
432 return false;
433 }
434
435
436 /**
437 * Update post meta by raw SQL.
438 *
439 * Inserts post meta data into the WordPress database using raw SQL queries.
440 *
441 * @param array $sql_query_arr An array of SQL query values in the format (post_id, meta_key, meta_value).
442 *
443 * @since 1.8.10
444 * @return boolean
445 */
446 public function update_meta_by_sql( $sql_query_arr ){
447 if ( ! is_array( $sql_query_arr ) || empty( $sql_query_arr ) ) {
448 return false;
449 }
450 global $wpdb;
451 $insert_sql_query = "INSERT INTO $wpdb->postmeta (post_id, meta_key, meta_value) VALUES ";
452 $value_placeholders = array();
453 $query_args = array();
454 foreach( $sql_query_arr as $query ) {
455 if( isset($query['post_id'],$query['meta_key'],$query['meta_value']) ){
456 $value_placeholders[] = '(%d, %s, %s)';
457 $query_args[] = $query['post_id'];
458 $query_args[] = $query['meta_key'];
459 $query_args[] = $query['meta_value'];
460 }
461 }
462 if( empty( $value_placeholders ) || empty( $query_args )){
463 return false;
464 }
465 $insert_sql_query = rtrim( $insert_sql_query, ',' );
466 $insert_sql_query .= ' ' . implode( ',', $value_placeholders );
467 $prepared_query = $wpdb->prepare( $insert_sql_query, $query_args );
468 $result = $wpdb->query( $prepared_query );
469 return false !== $result;
470 }
471
472
473 /**
474 * Update A/B testing settings for a funnel step.
475 * This function updates the A/B testing settings for a funnel step with the provided parameters.
476
477 * @param int $funnel_id The ID of the funnel to which the step belongs.
478 * @param int $step_id The ID of the step for which the A/B testing settings are being updated.
479 * @param mixed $settings The new A/B testing settings for the step. This can be a serialized string or an array.
480 * @param mixed $prev_settings The previous A/B testing settings for the step. This can be a serialized string or an array.
481 *
482 * @since 1.8.8
483 * @return void
484 */
485 public function update_ab_testing_settings( $funnel_id, $step_id, $settings, $prev_settings ){
486 $settings = maybe_unserialize($settings);
487 $prev_settings = maybe_unserialize($prev_settings);
488
489 if( isset( $settings['variations'] ) ){
490 $new_settings = [];
491 $new_settings['isStart'] = 'no';
492 $new_settings['startDate'] = isset($settings['startDate']) ?$settings['startDate'] : date( 'Y-m-d H:i:s' );
493 $new_settings['endDate'] = isset($settings['endDate']) ?$settings['endDate'] : date( 'Y-m-d H:i:s' );
494 $new_settings['variations'] = $this->update_ab_variations( $funnel_id, $step_id, $settings['variations'], $prev_settings );
495 update_post_meta( $step_id, '_wpfnl_ab_testing_start_settings' , $new_settings );
496 }
497
498 }
499
500
501 /**
502 * Update A/B testing variations for a funnel step.
503 * This function updates the A/B testing variations for a funnel step with the provided parameters.
504 *
505 * @param int $funnel_id The ID of the funnel to which the step belongs.
506 * @param int $step_id The ID of the step for which the A/B testing variations are being updated.
507 * @param array $variations The new A/B testing variations for the step.
508 * @param mixed $prev_settings The previous A/B testing settings for the step.
509 *
510 * @return array The updated A/B testing variations.
511 * @since 1.8.8
512 */
513 public function update_ab_variations( $funnel_id, $step_id, $variations, $prev_settings ){
514 if( count( $variations ) >= 2 ){
515 $step = new Wpfnl_Steps_Store_Data();
516 foreach( $variations as $variation_key=>$variation ){
517
518 if( !isset($variation['stepId'], $variation['variationType'] ) ){
519 continue;
520 }
521
522 $key = array_search($variation['stepId'], array_column($prev_settings, 'step_id'));
523 if( false !== $key ){
524
525 if ( 'original' !== $variation['variationType'] && isset($prev_settings[$key]['meta']) ){
526 $metas = $prev_settings[$key]['meta'];
527 $exclude_meta = array( '_wpfnl_ab_testing_start_settings','_wpfnl_automation_steps', '_parent_step_id' );
528 $page_template = isset( $metas['page_template'] ) ? $metas['page_template'] : '';
529 $title = isset( $metas['title'] ) ? $metas['title'] : '';
530 $post_content = isset( $metas['post_content'] ) ? $metas['post_content'] : '';
531 $step_type = isset( $metas['type'] ) ? $metas['type'] : '';
532 if( $step ){
533 $new_step_id = $step->create_step($funnel_id, $title, $step_type, $post_content, true);
534 if( $new_step_id ){
535 $step->update_meta($new_step_id, '_funnel_id', $funnel_id);
536 $step->update_meta($new_step_id, '_wp_page_template', $page_template);
537 $sql_query_arr = [];
538
539 foreach( $metas['meta'] as $meta_key=>$meta ){
540 if( !in_array( $meta_key, $exclude_meta ) ){
541 $sql_data = [
542 'post_id' => $new_step_id,
543 'meta_key' => $meta_key,
544 'meta_value'=> $meta[0],
545 ];
546 $sql_query_arr[] = $sql_data;
547 }
548 }
549 $this->update_meta_by_sql( $sql_query_arr ); // update meta
550 }
551 }
552
553 }else{
554 $new_step_id = $step_id;
555 }
556 update_post_meta( $new_step_id, '_parent_step_id', $step_id );
557 $variations[$variation_key]['id'] = $new_step_id;
558 $variations[$variation_key]['stepType'] = get_post_meta( $step_id, '_step_type', true );
559 $step_edit_link = get_edit_post_link($new_step_id);
560 if( 'elementor' === Wpfnl_functions::get_builder_type() ){
561 $step_edit_link = str_replace('&amp;','&',$step_edit_link);
562 $step_edit_link = str_replace('edit','elementor',$step_edit_link);
563 }
564 $variations[$variation_key]['stepEditLink'] = $step_edit_link;
565 $variations[$variation_key]['stepViewLink'] = get_the_permalink($new_step_id);
566 $variations[$variation_key]['stepName'] = get_the_title($new_step_id);
567 $variations[$variation_key]['stepId'] = $new_step_id;
568 }
569 }
570 }else{
571 $default_data = Wpfnl_Ab_Testing::get_default_data( $step_id );
572 $variations = $default_data['variations'];
573 }
574 return $variations;
575 }
576
577
578
579 /**
580 * Update funnel data and steps
581 *
582 * @param Int $funnel_id
583 * @param Array $step_data
584 *
585 * @return void
586 * @since 1.7.5
587 */
588 public function update_funnel_data( $new_step_ids, $funnel_id, $funnel_data ){
589 $funnel_identifier = array();
590 if ($funnel_data) {
591 $node_data = isset($funnel_data['drawflow']['Home']['data']) ? $funnel_data['drawflow']['Home']['data'] : [];
592 foreach ($node_data as $node_key => $node_value) {
593 if(isset($node_value['data']['step_id'])) {
594 if ( isset($new_step_ids[$node_value['data']['step_id']]) ) {
595 $post_edit_link = base64_encode(get_edit_post_link($new_step_ids[$node_value['data']['step_id']]));
596 $post_view_link = base64_encode(get_post_permalink($new_step_ids[$node_value['data']['step_id']]));
597 $funnel_data['drawflow']['Home']['data'][$node_key]['data']['step_id'] = $new_step_ids[$node_value['data']['step_id']];
598 $funnel_data['drawflow']['Home']['data'][$node_key]['data']['step_edit_link'] = $post_edit_link;
599 $funnel_data['drawflow']['Home']['data'][$node_key]['data']['step_view_link'] = $post_view_link;
600 $funnel_data['drawflow']['Home']['data'][$node_key]['html'] = $node_value['data']['step_type'] . $new_step_ids[$node_value['data']['step_id']];
601 $funnel_identifier[$node_value['id']] = $new_step_ids[$node_value['data']['step_id']];
602 } else {
603 if ($node_value['data']['step_type'] != 'conditional') {
604 $funnel_identifier[$node_value['id']] = $node_value['data']['step_id'];
605 } else {
606 $funnel_identifier[$node_value['id']] = $node_value['data']['node_identifier'];
607 }
608 }
609 }
610 if ($node_value['data']['step_type'] == 'conditional') {
611 $funnel_identifier[$node_value['id']] = $node_value['data']['node_identifier'];
612 }
613 }
614 }
615 update_post_meta($funnel_id, '_funnel_data', $funnel_data);
616 if ($funnel_identifier) {
617 $funnel_identifier_json = json_encode($funnel_identifier, JSON_UNESCAPED_SLASHES);
618 update_post_meta($funnel_id, 'funnel_identifier', $funnel_identifier_json);
619 }
620 }
621
622 /**
623 * Hide the import funnel notice based on nonce verification.
624 *
625 * This method is responsible for hiding the import funnel notice displayed to users. It receives a nonce value from the
626 * submitted form, verifies the nonce using the 'wp_verify_nonce' function, and if the verification succeeds, it deletes the
627 * 'wpfnl_import_notice' option to hide the notice. It then sends a JSON success response to the client and terminates the script
628 * execution using 'wp_die'.
629 *
630 * @since 1.9.7
631 */
632 public function hide_import_funnel_notice() {
633 $nonce = !empty( $_POST[ 'security' ] ) ? htmlspecialchars( trim( $_POST[ 'security' ] ) ) : null; // phpcs:ignore
634
635 if( wp_verify_nonce( $nonce, 'wpfnl-admin' ) ) {
636 delete_option( 'wpfnl_import_notice' );
637 }
638 wp_send_json_success();
639 wp_die();
640 }
641
642 public function get_validation_data()
643 {
644 return [
645 'logged_in' => true,
646 'user_can' => 'wpf_manage_funnels',
647 ];
648 }
649 }