PluginProbe
WPFunnels – Funnel Builder for WooCommerce with Checkout & One Click Upsell / 3.12.1
WPFunnels – Funnel Builder for WooCommerce with Checkout & One Click Upsell v3.12.1
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.12.1, at includes/core/import-export/class-wpfnl-import-funnel.php

656 lines 29.6 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 if ( ! Wpfnl_functions::is_wpfnl_pro_activated() ) {
357 $exclude_meta = array_merge( $exclude_meta, array(
358 '_wpfnl_maybe_enable_condition',
359 '_wpfnl_step_conditions',
360 '_wpfnl_next_step_after_condition',
361 ) );
362 }
363 $sql_query_arr = [];
364 $is_enable_condition_meta_blank = false;
365 $is_condition_exits = false;
366 foreach ($step_data['meta'] as $meta_key => $meta) {
367
368 if (isset($meta[0])) {
369 if (!in_array($meta_key, $exclude_meta)) {
370 if ('_wpfnl_maybe_enable_condition' === $meta_key) {
371 if ('' === $meta[0]) {
372 $is_enable_condition_meta_blank = true;
373 }
374 }
375
376 if ('_wpfnl_step_conditions' === $meta_key) {
377 if ($meta[0]) {
378 $is_condition_exits = true;
379 }
380 }
381
382 $sql_data = [
383 'post_id' => $step_id,
384 'meta_key' => $meta_key,
385 'meta_value' => $meta[0],
386 ];
387 $sql_query_arr[] = $sql_data;
388 } elseif ('_wpfnl_ab_testing_start_settings' === $meta_key) {
389
390 $this->update_ab_testing_settings($funnel_id, $step_id, $meta[0], $step_data['ab_step_data']);
391 }
392 }
393 }
394 $this->update_meta_by_sql($sql_query_arr); // update meta
395
396 if ($is_enable_condition_meta_blank && $is_condition_exits) {
397 update_post_meta($step_id, '_wpfnl_maybe_enable_condition', 'yes');
398 }
399
400 if (('gutenberg' === $builder_type) && class_exists('\WPFunnels\Batch\Gutenberg\Wpfnl_Gutenberg_Source')) {
401 if (ini_get('allow_url_fopen')) {
402 $gutenberg_source = new \WPFunnels\Batch\Gutenberg\Wpfnl_Gutenberg_Source;
403 $gutenberg_source->import_single_template($step_id);
404 }
405 }
406
407 // Elementor Data.
408 if (('elementor' === $builder_type) && class_exists('\Elementor\Plugin') && class_exists('\WPFunnels\Batch\Elementor\Wpfnl_Elementor_Source')) {
409 if (ini_get('allow_url_fopen')) {
410 $elementor_source = new \WPFunnels\Batch\Elementor\Wpfnl_Elementor_Source;
411 $elementor_source->import_single_template($step_id);
412 Plugin::$instance->files_manager->clear_cache();
413 }
414 }
415
416 if (('divi-builder' === $builder_type) && class_exists('\WPFunnels\Batch\Divi\Wpfnl_Divi_Source')) {
417 if (ini_get('allow_url_fopen')) {
418 $divi_source = new \WPFunnels\Batch\Divi\Wpfnl_Divi_Source;
419 $divi_source->import_single_template($step_id);
420 }
421 }
422
423 if (('oxygen' === $builder_type) && class_exists('\WPFunnels\Batch\Oxygen\Wpfnl_Oxygen_Source')) {
424 if (ini_get('allow_url_fopen')) {
425 $oxygen_source = new \WPFunnels\Batch\Oxygen\Wpfnl_Oxygen_Source;
426 $oxygen_source->import_single_template($step_id);
427 }
428 }
429 delete_post_meta($step_id, '_funnel_id');
430 update_post_meta($step_id, '_funnel_id', $funnel_id);
431
432 return [
433 'id' => $step_id,
434 'type' => $step_type,
435 'title' => $title,
436 ];
437 }
438
439 return false;
440 }
441
442
443 /**
444 * Update post meta by raw SQL.
445 *
446 * Inserts post meta data into the WordPress database using raw SQL queries.
447 *
448 * @param array $sql_query_arr An array of SQL query values in the format (post_id, meta_key, meta_value).
449 *
450 * @since 1.8.10
451 * @return boolean
452 */
453 public function update_meta_by_sql( $sql_query_arr ){
454 if ( ! is_array( $sql_query_arr ) || empty( $sql_query_arr ) ) {
455 return false;
456 }
457 global $wpdb;
458 $insert_sql_query = "INSERT INTO $wpdb->postmeta (post_id, meta_key, meta_value) VALUES ";
459 $value_placeholders = array();
460 $query_args = array();
461 foreach( $sql_query_arr as $query ) {
462 if( isset($query['post_id'],$query['meta_key'],$query['meta_value']) ){
463 $value_placeholders[] = '(%d, %s, %s)';
464 $query_args[] = $query['post_id'];
465 $query_args[] = $query['meta_key'];
466 $query_args[] = $query['meta_value'];
467 }
468 }
469 if( empty( $value_placeholders ) || empty( $query_args )){
470 return false;
471 }
472 $insert_sql_query = rtrim( $insert_sql_query, ',' );
473 $insert_sql_query .= ' ' . implode( ',', $value_placeholders );
474 $prepared_query = $wpdb->prepare( $insert_sql_query, $query_args );
475 $result = $wpdb->query( $prepared_query );
476 return false !== $result;
477 }
478
479
480 /**
481 * Update A/B testing settings for a funnel step.
482 * This function updates the A/B testing settings for a funnel step with the provided parameters.
483
484 * @param int $funnel_id The ID of the funnel to which the step belongs.
485 * @param int $step_id The ID of the step for which the A/B testing settings are being updated.
486 * @param mixed $settings The new A/B testing settings for the step. This can be a serialized string or an array.
487 * @param mixed $prev_settings The previous A/B testing settings for the step. This can be a serialized string or an array.
488 *
489 * @since 1.8.8
490 * @return void
491 */
492 public function update_ab_testing_settings( $funnel_id, $step_id, $settings, $prev_settings ){
493 $settings = maybe_unserialize($settings);
494 $prev_settings = maybe_unserialize($prev_settings);
495
496 if( isset( $settings['variations'] ) ){
497 $new_settings = [];
498 $new_settings['isStart'] = 'no';
499 $new_settings['startDate'] = isset($settings['startDate']) ?$settings['startDate'] : date( 'Y-m-d H:i:s' );
500 $new_settings['endDate'] = isset($settings['endDate']) ?$settings['endDate'] : date( 'Y-m-d H:i:s' );
501 $new_settings['variations'] = $this->update_ab_variations( $funnel_id, $step_id, $settings['variations'], $prev_settings );
502 update_post_meta( $step_id, '_wpfnl_ab_testing_start_settings' , $new_settings );
503 }
504
505 }
506
507
508 /**
509 * Update A/B testing variations for a funnel step.
510 * This function updates the A/B testing variations for a funnel step with the provided parameters.
511 *
512 * @param int $funnel_id The ID of the funnel to which the step belongs.
513 * @param int $step_id The ID of the step for which the A/B testing variations are being updated.
514 * @param array $variations The new A/B testing variations for the step.
515 * @param mixed $prev_settings The previous A/B testing settings for the step.
516 *
517 * @return array The updated A/B testing variations.
518 * @since 1.8.8
519 */
520 public function update_ab_variations( $funnel_id, $step_id, $variations, $prev_settings ){
521 if( count( $variations ) >= 2 ){
522 $step = new Wpfnl_Steps_Store_Data();
523 foreach( $variations as $variation_key=>$variation ){
524
525 if( !isset($variation['stepId'], $variation['variationType'] ) ){
526 continue;
527 }
528
529 $key = array_search($variation['stepId'], array_column($prev_settings, 'step_id'));
530 if( false !== $key ){
531
532 if ( 'original' !== $variation['variationType'] && isset($prev_settings[$key]['meta']) ){
533 $metas = $prev_settings[$key]['meta'];
534 $exclude_meta = array( '_wpfnl_ab_testing_start_settings','_wpfnl_automation_steps', '_parent_step_id' );
535 $page_template = isset( $metas['page_template'] ) ? $metas['page_template'] : '';
536 $title = isset( $metas['title'] ) ? $metas['title'] : '';
537 $post_content = isset( $metas['post_content'] ) ? $metas['post_content'] : '';
538 $step_type = isset( $metas['type'] ) ? $metas['type'] : '';
539 if( $step ){
540 $new_step_id = $step->create_step($funnel_id, $title, $step_type, $post_content, true);
541 if( $new_step_id ){
542 $step->update_meta($new_step_id, '_funnel_id', $funnel_id);
543 $step->update_meta($new_step_id, '_wp_page_template', $page_template);
544 $sql_query_arr = [];
545
546 foreach( $metas['meta'] as $meta_key=>$meta ){
547 if( !in_array( $meta_key, $exclude_meta ) ){
548 $sql_data = [
549 'post_id' => $new_step_id,
550 'meta_key' => $meta_key,
551 'meta_value'=> $meta[0],
552 ];
553 $sql_query_arr[] = $sql_data;
554 }
555 }
556 $this->update_meta_by_sql( $sql_query_arr ); // update meta
557 }
558 }
559
560 }else{
561 $new_step_id = $step_id;
562 }
563 update_post_meta( $new_step_id, '_parent_step_id', $step_id );
564 $variations[$variation_key]['id'] = $new_step_id;
565 $variations[$variation_key]['stepType'] = get_post_meta( $step_id, '_step_type', true );
566 $step_edit_link = get_edit_post_link($new_step_id);
567 if( 'elementor' === Wpfnl_functions::get_builder_type() ){
568 $step_edit_link = str_replace('&amp;','&',$step_edit_link);
569 $step_edit_link = str_replace('edit','elementor',$step_edit_link);
570 }
571 $variations[$variation_key]['stepEditLink'] = $step_edit_link;
572 $variations[$variation_key]['stepViewLink'] = get_the_permalink($new_step_id);
573 $variations[$variation_key]['stepName'] = get_the_title($new_step_id);
574 $variations[$variation_key]['stepId'] = $new_step_id;
575 }
576 }
577 }else{
578 $default_data = Wpfnl_Ab_Testing::get_default_data( $step_id );
579 $variations = $default_data['variations'];
580 }
581 return $variations;
582 }
583
584
585
586 /**
587 * Update funnel data and steps
588 *
589 * @param Int $funnel_id
590 * @param Array $step_data
591 *
592 * @return void
593 * @since 1.7.5
594 */
595 public function update_funnel_data( $new_step_ids, $funnel_id, $funnel_data ){
596 $funnel_identifier = array();
597 if ($funnel_data) {
598 $node_data = isset($funnel_data['drawflow']['Home']['data']) ? $funnel_data['drawflow']['Home']['data'] : [];
599 foreach ($node_data as $node_key => $node_value) {
600 if(isset($node_value['data']['step_id'])) {
601 if ( isset($new_step_ids[$node_value['data']['step_id']]) ) {
602 $post_edit_link = base64_encode(get_edit_post_link($new_step_ids[$node_value['data']['step_id']]));
603 $post_view_link = base64_encode(get_post_permalink($new_step_ids[$node_value['data']['step_id']]));
604 $funnel_data['drawflow']['Home']['data'][$node_key]['data']['step_id'] = $new_step_ids[$node_value['data']['step_id']];
605 $funnel_data['drawflow']['Home']['data'][$node_key]['data']['step_edit_link'] = $post_edit_link;
606 $funnel_data['drawflow']['Home']['data'][$node_key]['data']['step_view_link'] = $post_view_link;
607 $funnel_data['drawflow']['Home']['data'][$node_key]['html'] = $node_value['data']['step_type'] . $new_step_ids[$node_value['data']['step_id']];
608 $funnel_identifier[$node_value['id']] = $new_step_ids[$node_value['data']['step_id']];
609 } else {
610 if ($node_value['data']['step_type'] != 'conditional') {
611 $funnel_identifier[$node_value['id']] = $node_value['data']['step_id'];
612 } else {
613 $funnel_identifier[$node_value['id']] = $node_value['data']['node_identifier'];
614 }
615 }
616 }
617 if ($node_value['data']['step_type'] == 'conditional') {
618 $funnel_identifier[$node_value['id']] = $node_value['data']['node_identifier'];
619 }
620 }
621 }
622 update_post_meta($funnel_id, '_funnel_data', $funnel_data);
623 if ($funnel_identifier) {
624 $funnel_identifier_json = json_encode($funnel_identifier, JSON_UNESCAPED_SLASHES);
625 update_post_meta($funnel_id, 'funnel_identifier', $funnel_identifier_json);
626 }
627 }
628
629 /**
630 * Hide the import funnel notice based on nonce verification.
631 *
632 * This method is responsible for hiding the import funnel notice displayed to users. It receives a nonce value from the
633 * submitted form, verifies the nonce using the 'wp_verify_nonce' function, and if the verification succeeds, it deletes the
634 * 'wpfnl_import_notice' option to hide the notice. It then sends a JSON success response to the client and terminates the script
635 * execution using 'wp_die'.
636 *
637 * @since 1.9.7
638 */
639 public function hide_import_funnel_notice() {
640 $nonce = !empty( $_POST[ 'security' ] ) ? htmlspecialchars( trim( $_POST[ 'security' ] ) ) : null; // phpcs:ignore
641
642 if( wp_verify_nonce( $nonce, 'wpfnl-admin' ) ) {
643 delete_option( 'wpfnl_import_notice' );
644 }
645 wp_send_json_success();
646 wp_die();
647 }
648
649 public function get_validation_data()
650 {
651 return [
652 'logged_in' => true,
653 'user_can' => 'wpf_manage_funnels',
654 ];
655 }
656 }