PluginProbe
CartFlows – Funnel Builder & Checkout Plugin for WooCommerce / 3.1.4
CartFlows – Funnel Builder & Checkout Plugin for WooCommerce v3.1.4
3.3.0 3.2.1 3.2.0 3.1.4 3.1.3 3.1.2 3.1.1 3.1.0 3.0.1 trunk 1.0.4 1.1.0 1.1.0.1 1.1.1 1.1.10 1.1.11 1.1.12 1.1.13 1.1.14 1.1.15 1.1.16 1.1.17 1.1.18 1.1.19 1.1.2 All 162 releases
cartflows / admin-legacy-core / ajax / common-settings.php

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

669 lines 20.4 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * CartFlows Flows ajax actions.
4 *
5 * @package CartFlows
6 */
7
8 namespace CartflowsAdmin\AdminLegacyCore\Ajax;
9
10 // Exit if accessed directly.
11 if ( ! defined( 'ABSPATH' ) ) {
12 exit;
13 }
14
15 use CartflowsAdmin\AdminLegacyCore\Ajax\AjaxBase;
16 use CartflowsAdmin\AdminLegacyCore\Inc\AdminHelper;
17
18 /**
19 * Class Flows.
20 */
21 class CommonSettings extends AjaxBase {
22
23 /**
24 * Instance
25 *
26 * @access private
27 * @var object Class object.
28 * @since 1.0.0
29 */
30 private static $instance;
31
32 /**
33 * URL the client should hard-navigate to after a successful save.
34 *
35 * Set by save_other_settings() when the "Switch to New UI" toggle is
36 * enabled so the dashboard reloads through the new admin loader. Read by
37 * save_global_settings() and merged into the final response payload so
38 * do_action() hooks and other side-effects still run before the redirect.
39 *
40 * @var string
41 */
42 private $pending_redirect = '';
43
44 /**
45 * Initiator
46 *
47 * @since 1.0.0
48 * @return object initialized object of class.
49 */
50 public static function get_instance() {
51 if ( ! isset( self::$instance ) ) {
52 self::$instance = new self();
53 }
54 return self::$instance;
55 }
56
57 /**
58 * Register_ajax_events.
59 *
60 * @return void
61 */
62 public function register_ajax_events() {
63
64 if ( current_user_can( 'cartflows_manage_settings' ) ) {
65
66 $ajax_events = array(
67 'save_global_settings',
68 'regenerate_css_for_steps',
69 'track_kb_search',
70 'migrate_custom_scripts',
71 'switch_to_new_ui',
72 );
73 $this->init_ajax_events( $ajax_events );
74 }
75 }
76
77 /**
78 * Opt the user into the redesigned admin UI introduced in CartFlows 3.0.
79 * Fires from the in-app notice rendered on legacy admin screens.
80 */
81 public function switch_to_new_ui() {
82
83 if ( ! current_user_can( 'cartflows_manage_settings' ) ) {
84 wp_send_json_error( array( 'message' => $this->get_error_msg( 'permission' ) ) );
85 }
86
87 if ( ! check_ajax_referer( 'cartflows_switch_to_new_ui', 'security', false ) ) {
88 wp_send_json_error( array( 'message' => $this->get_error_msg( 'nonce' ) ) );
89 }
90
91 delete_option( 'cartflows-legacy-admin' );
92
93 wp_send_json_success(
94 array(
95 'redirect_to' => admin_url( 'admin.php?page=' . CARTFLOWS_SLUG ),
96 )
97 );
98 }
99
100 /**
101 * Delete the post meta key for dynamic css to regenerate the it.
102 */
103 public function regenerate_css_for_steps() {
104
105 $response_data = array( 'messsage' => $this->get_error_msg( 'permission' ) );
106
107 if ( ! current_user_can( 'cartflows_manage_settings' ) ) {
108 wp_send_json_error( $response_data );
109 }
110
111 /**
112 * Nonce verification
113 */
114 if ( ! check_ajax_referer( 'cartflows_regenerate_css_for_steps', 'security', false ) ) {
115 $response_data = array( 'messsage' => $this->get_error_msg( 'nonce' ) );
116 wp_send_json_error( $response_data );
117 }
118
119 // Update cartflows asset version to regenerate the dynamic css. We are using the time() function to add the random number.
120 update_option( 'cartflows-assets-version', time() );
121
122 $response_data = array(
123 'messsage' => __( 'Successfully deleted the dynamic CSS keys!', 'cartflows' ),
124 );
125 wp_send_json_success( $response_data );
126 }
127
128 /**
129 * Save settings.
130 *
131 * @return void
132 */
133 public function save_global_settings() {
134
135 $response_data = array( 'messsage' => $this->get_error_msg( 'permission' ) );
136
137 if ( ! current_user_can( 'cartflows_manage_settings' ) ) {
138 wp_send_json_error( $response_data );
139 }
140
141 if ( empty( $_POST ) ) {
142 $response_data = array( 'messsage' => __( 'No post data found!', 'cartflows' ) );
143 wp_send_json_error( $response_data );
144 }
145
146 /**
147 * Nonce verification
148 */
149 if ( ! check_ajax_referer( 'cartflows_save_global_settings', 'security', false ) ) {
150 $response_data = array( 'messsage' => $this->get_error_msg( 'nonce' ) );
151 wp_send_json_error( $response_data );
152 }
153
154
155 $setting_tab = isset( $_POST['setting_tab'] ) ? sanitize_text_field( wp_unslash( $_POST['setting_tab'] ) ) : '';
156
157 switch ( $setting_tab ) {
158
159 case 'general':
160 $this->save_general_settings();
161 break;
162
163 case 'permalink':
164 $this->save_permalink_settings();
165 break;
166
167 case 'other':
168 $this->save_other_settings();
169 break;
170
171 case 'user_role_manager':
172 $this->save_user_roles_management_settings();
173 break;
174
175 case 'integrations':
176 $this->save_integration_settings();
177 break;
178
179 case 'global_scripts':
180 $this->save_global_scripts_settings();
181 break;
182
183 default:
184 $this->save_general_settings();
185
186 }
187
188 do_action( 'cartflows_admin_save_global_settings', $setting_tab, 'cartflows_save_global_settings' );
189
190
191
192 $response_data = array(
193 'messsage' => __( 'Successfully saved data!', 'cartflows' ),
194 );
195
196 // If a tab handler queued a redirect (e.g. "Switch to New UI" toggle
197 // was just enabled), pass it through so the client hard-navigates
198 // after save.
199 if ( ! empty( $this->pending_redirect ) ) {
200 $response_data['redirect_to'] = $this->pending_redirect;
201 $response_data['messsage'] = __( 'Switching to the new CartFlows UI…', 'cartflows' );
202 }
203
204 wp_send_json_success( $response_data );
205 }
206
207 /**
208 * Save integration tab settings. Such as Facebook & Google Analytics and Google AutoAddress.
209 *
210 * Note: Called from save_global_settings function.
211 *
212 * @return void
213 */
214 public function save_integration_settings() {
215
216 $new_settings = array();
217
218 if ( isset( $_POST['_cartflows_facebook'] ) ) { //phpcs:ignore
219 $new_settings = $this->sanitize_form_inputs( wp_unslash( $_POST['_cartflows_facebook'] ) ); //phpcs:ignore
220 AdminHelper::update_admin_settings_option( '_cartflows_facebook', $new_settings, false );
221 }
222
223 if ( isset( $_POST['_cartflows_google_analytics'] ) ) { //phpcs:ignore
224 $new_settings = $this->sanitize_form_inputs( wp_unslash( $_POST['_cartflows_google_analytics'] ) ); //phpcs:ignore
225 AdminHelper::update_admin_settings_option( '_cartflows_google_analytics', $new_settings, false );
226 }
227
228 if ( isset( $_POST['_cartflows_google_auto_address'] ) ) { //phpcs:ignore
229 $new_settings = $this->sanitize_form_inputs( wp_unslash( $_POST['_cartflows_google_auto_address'] ) ); //phpcs:ignore
230 AdminHelper::update_admin_settings_option( '_cartflows_google_auto_address', $new_settings, false );
231 }
232
233 if ( isset( $_POST['_cartflows_tiktok'] ) ) { //phpcs:ignore
234 $new_settings = $this->sanitize_form_inputs( wp_unslash( $_POST['_cartflows_tiktok'] ) ); //phpcs:ignore
235 AdminHelper::update_admin_settings_option( '_cartflows_tiktok', $new_settings, false );
236 }
237
238 if ( isset( $_POST['_cartflows_pinterest'] ) ) { //phpcs:ignore
239 $new_settings = $this->sanitize_form_inputs( wp_unslash( $_POST['_cartflows_pinterest'] ) ); //phpcs:ignore
240 AdminHelper::update_admin_settings_option( '_cartflows_pinterest', $new_settings, false );
241 }
242
243 if ( isset( $_POST['_cartflows_google_ads'] ) ) { //phpcs:ignore
244 $new_settings = $this->sanitize_form_inputs( wp_unslash( $_POST['_cartflows_google_ads'] ) ); //phpcs:ignore
245 AdminHelper::update_admin_settings_option( '_cartflows_google_ads', $new_settings, false );
246 }
247
248 if ( isset( $_POST['_cartflows_snapchat'] ) ) { //phpcs:ignore
249 $new_settings = $this->sanitize_form_inputs( wp_unslash( $_POST['_cartflows_snapchat'] ) ); //phpcs:ignore
250 AdminHelper::update_admin_settings_option( '_cartflows_snapchat', $new_settings, false );
251 }
252 }
253
254 /**
255 * Save global CSS & Scripts settings.
256 *
257 * Note: Called from save_global_settings function.
258 *
259 * Note: Global scripts are stored in the '_cartflows_global_scripts' option and are
260 * completely independent of the per-flow/per-step custom script migration. They always
261 * use CodeMirror code editor fields regardless of the migration status
262 * ('cartflows_script_migration_status'). No migration is needed for global scripts.
263 *
264 * @return void
265 */
266 public function save_global_scripts_settings() {
267
268 /**
269 * Nonce verification
270 */
271 if ( ! check_ajax_referer( 'cartflows_save_global_settings', 'security', false ) ) {
272 $response_data = array( 'message' => __( 'Nonce validation failed', 'cartflows' ) );
273 wp_send_json_error( $response_data );
274 }
275
276 // Global CSS/JS are output raw on every CartFlows page. Restrict authoring to users
277 // with `unfiltered_html` so per-plugin caps cannot grant script write access to lower roles.
278 if ( ! current_user_can( 'unfiltered_html' ) ) {
279 return;
280 }
281
282 if ( isset( $_POST['_cartflows_global_scripts'] ) ) { //phpcs:ignore WordPress.Security.NonceVerification.Missing
283 $raw_settings = wp_unslash( $_POST['_cartflows_global_scripts'] ); //phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
284
285 $new_settings = array();
286
287 if ( isset( $raw_settings['global_css'] ) ) {
288 // Decode first to prevent double-encoding on repeated saves, then encode once.
289 $new_settings['global_css'] = htmlentities( html_entity_decode( $raw_settings['global_css'], ENT_QUOTES, 'UTF-8' ), ENT_QUOTES, 'UTF-8' );
290 }
291
292 if ( isset( $raw_settings['global_js'] ) ) {
293 // Decode first to prevent double-encoding on repeated saves, then encode once.
294 $new_settings['global_js'] = htmlentities( html_entity_decode( $raw_settings['global_js'], ENT_QUOTES, 'UTF-8' ), ENT_QUOTES, 'UTF-8' );
295 }
296
297 AdminHelper::update_admin_settings_option( '_cartflows_global_scripts', $new_settings, false );
298 }
299 }
300
301 /**
302 * Save other tab settings.
303 *
304 * Note: Called from save_global_settings function.
305 *
306 * @return void
307 */
308 public function save_other_settings() {
309
310 /**
311 * Nonce verification
312 */
313 if ( ! check_ajax_referer( 'cartflows_save_global_settings', 'security', false ) ) {
314 $response_data = array( 'messsage' => __( 'Nonce validation failed', 'cartflows' ) );
315 wp_send_json_error( $response_data );
316 }
317
318 $delete_plugin_data = '';
319
320 if ( isset( $_POST['cartflows_delete_plugin_data'] ) ) {
321 $delete_plugin_data = sanitize_text_field( $_POST['cartflows_delete_plugin_data'] );
322 }
323
324 AdminHelper::update_admin_settings_option( 'cartflows_delete_plugin_data', $delete_plugin_data, false );
325
326 if ( _is_cartflows_pro() ) {
327 AdminHelper::update_admin_settings_option( 'cartflows_pro_delete_plugin_data', $delete_plugin_data, false );
328 }
329
330 if ( isset( $_POST['cartflows_stats_report_emails'] ) ) {
331 $enable_report_emails = sanitize_text_field( $_POST['cartflows_stats_report_emails'] );
332 AdminHelper::update_admin_settings_option( 'cartflows_stats_report_emails', $enable_report_emails, false );
333 }
334
335 if ( isset( $_POST['cartflows_stats_report_email_ids'] ) ) {
336
337 if ( ! empty( $_POST['cartflows_stats_report_email_ids'] ) ) {
338 // Ignoring the sanitization rule as we are sanitizing each email before saving it.
339 $emails = preg_split( "/[\f\r\n]+/", $_POST['cartflows_stats_report_email_ids'] ); //phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
340 $validated_emails = array();
341
342 foreach ( $emails as $email_id ) {
343
344 if ( is_email( $email_id ) ) {
345 array_push( $validated_emails, sanitize_email( $email_id ) );
346 }
347 }
348 $validated_emails = implode( "\n", $validated_emails );
349 AdminHelper::update_admin_settings_option( 'cartflows_stats_report_email_ids', $validated_emails, false );
350 } else {
351 AdminHelper::update_admin_settings_option( 'cartflows_stats_report_email_ids', '', false );
352 }
353 }
354
355 // Update the option to enable/disable the non sensitive data tracking.
356 if ( isset( $_POST['cf_usage_optin'] ) ) {
357 $enable_non_sensative_data_tracking = sanitize_text_field( $_POST['cf_usage_optin'] );
358 AdminHelper::update_admin_settings_option( 'cf_usage_optin', $enable_non_sensative_data_tracking, false );
359 }
360
361 // "Switch to New UI" — one-shot toggle. When enabled, drop the legacy-admin
362 // option so the next request boots the new admin loader, and queue a redirect
363 // that save_global_settings() will merge into the final response.
364 if ( isset( $_POST['cartflows-switch-to-new-ui'] ) ) {
365 $switch_to_new_ui = sanitize_text_field( wp_unslash( $_POST['cartflows-switch-to-new-ui'] ) );
366
367 if ( 'enable' === $switch_to_new_ui ) {
368 delete_option( 'cartflows-legacy-admin' );
369 $this->pending_redirect = admin_url( 'admin.php?page=' . CARTFLOWS_SLUG );
370 }
371 }
372 }
373
374 /**
375 * Save common settings of CartFlows. Like Default Page Builder and Store Checkout.
376 *
377 * Note: Called from save_global_settings function.
378 *
379 * @return void
380 */
381 public function save_general_settings() {
382
383 /**
384 * Nonce verification
385 */
386 if ( ! check_ajax_referer( 'cartflows_save_global_settings', 'security', false ) ) {
387 $response_data = array( 'messsage' => __( 'Nonce validation failed', 'cartflows' ) );
388 wp_send_json_error( $response_data );
389 }
390
391 $new_settings = array();
392
393 if ( isset( $_POST['_cartflows_common'] ) ) {
394 // Ignoring the sanitization rule as we are sanitizing input using our function.
395 $new_settings = $this->sanitize_form_inputs( wp_unslash( $_POST['_cartflows_common'] ) ); //phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
396 }
397
398 $common_settings = AdminHelper::get_admin_settings_option( '_cartflows_common', false, false );
399 $new_settings = wp_parse_args( $new_settings, $common_settings );
400
401 AdminHelper::update_admin_settings_option( '_cartflows_common', $new_settings, false );
402 }
403
404 /**
405 * Remove cf caps.
406 *
407 * @param object $user_role_obj user role object.
408 *
409 * @return void
410 */
411 public function remove_all_cf_cap( $user_role_obj ) {
412
413 $cf_cap = array(
414 'cartflows_manage_settings',
415 'cartflows_manage_flows_steps',
416 );
417
418 foreach ( $cf_cap as $cap ) {
419 $user_role_obj->remove_cap( $cap );
420 }
421 }
422
423 /**
424 * Add cf caps.
425 *
426 * @param object $user_role_obj user role object.
427 * @param string $access_key access key.
428 *
429 * @return void
430 */
431 public function add_selected_cf_cap( $user_role_obj, $access_key ) {
432
433 // Security: Validate access_key against allowlist to prevent privilege escalation.
434 $allowed_keys = array( 'access_to_cartflows', 'access_to_flows_and_step', 'no_access' );
435
436 if ( ! in_array( $access_key, $allowed_keys, true ) ) {
437 return;
438 }
439
440 switch ( $access_key ) {
441
442 case 'access_to_cartflows':
443 $user_role_obj->add_cap( 'cartflows_manage_settings' );
444 $user_role_obj->add_cap( 'cartflows_manage_flows_steps' );
445 break;
446
447 case 'access_to_flows_and_step':
448 $user_role_obj->add_cap( 'cartflows_manage_flows_steps' );
449 break;
450
451 default:
452 // No capabilities to add for 'no_access'.
453 break;
454
455 }
456 }
457
458 /**
459 * Add / Remove custom capability to the user role.
460 *
461 * @param array $new_settings Array of user role capability settings.
462 * @param array $old_settings Array of old user role capability settings.
463 *
464 * @return void
465 */
466 public function user_role_management( $new_settings, $old_settings ) {
467
468 // Security: Protect administrator role from modification via this endpoint.
469 $protected_roles = array( 'administrator' );
470
471 foreach ( $new_settings as $user_role => $access_key ) {
472
473 if ( in_array( $user_role, $protected_roles, true ) ) {
474 continue;
475 }
476
477 if ( ! isset( $old_settings[ $user_role ] ) || $old_settings[ $user_role ] !== $access_key ) {
478
479 $user_role_obj = get_role( $user_role );
480
481 if ( $user_role_obj ) {
482
483 $this->remove_all_cf_cap( $user_role_obj );
484
485 $this->add_selected_cf_cap( $user_role_obj, $access_key );
486
487 }
488 }
489 }
490 }
491
492 /**
493 * Save user role settings.
494 *
495 * Note: Called from save_global_settings function.
496 *
497 * @return void
498 */
499 public function save_user_roles_management_settings() {
500
501 /**
502 * Nonce verification
503 */
504 if ( ! check_ajax_referer( 'cartflows_save_global_settings', 'security', false ) ) {
505 $response_data = array( 'messsage' => __( 'Nonce validation failed', 'cartflows' ) );
506 wp_send_json_error( $response_data );
507 }
508
509 $new_settings = array();
510
511 if ( isset( $_POST['_cartflows_roles'] ) ) {
512 $new_settings = $this->sanitize_form_inputs( wp_unslash( $_POST['_cartflows_roles'] ) ); //phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
513 }
514
515 $old_settings = AdminHelper::get_admin_settings_option( '_cartflows_roles' );
516
517 $new_settings = wp_parse_args( $new_settings, $old_settings );
518
519 AdminHelper::update_admin_settings_option( '_cartflows_roles', $new_settings, false );
520
521 // Add/Remove capability.
522 $this->user_role_management( $new_settings, $old_settings );
523 }
524
525 /**
526 * Save the permalink settings.
527 *
528 * Note: Called from save_global_settings function.
529 *
530 * @return void
531 */
532 public function save_permalink_settings() {
533
534 /**
535 * Nonce verification
536 */
537 if ( ! check_ajax_referer( 'cartflows_save_global_settings', 'security', false ) ) {
538 $response_data = array( 'messsage' => __( 'Nonce validation failed', 'cartflows' ) );
539 wp_send_json_error( $response_data );
540 }
541
542 if ( isset( $_POST['reset'] ) ) {
543 $_POST['_cartflows_permalink'] = array(
544 'permalink' => CARTFLOWS_STEP_PERMALINK_SLUG,
545 'permalink_flow_base' => CARTFLOWS_FLOW_PERMALINK_SLUG,
546 'permalink_structure' => '',
547 );
548
549 }
550 $new_settings = array();
551 if ( isset( $_POST['_cartflows_permalink'] ) ) {
552 $cartflows_permalink_settings = $this->sanitize_form_inputs( wp_unslash( $_POST['_cartflows_permalink'] ) ); //phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
553
554 if ( empty( $cartflows_permalink_settings['permalink'] ) ) {
555 $new_settings['permalink'] = CARTFLOWS_STEP_PERMALINK_SLUG;
556 } else {
557 $new_settings['permalink'] = $cartflows_permalink_settings['permalink'];
558 }
559
560 if ( empty( $cartflows_permalink_settings['permalink_flow_base'] ) ) {
561 $new_settings['permalink_flow_base'] = CARTFLOWS_FLOW_PERMALINK_SLUG;
562 } else {
563 $new_settings['permalink_flow_base'] = $cartflows_permalink_settings['permalink_flow_base'];
564 }
565
566 $new_settings['permalink_structure'] = $cartflows_permalink_settings['permalink_structure'];
567
568 }
569
570 AdminHelper::update_admin_settings_option( '_cartflows_permalink', $new_settings, true );
571
572 update_option( 'cartflows_permalink_refresh', true );
573 }
574
575
576 /**
577 * Save settings.
578 *
579 * @param array $input_settings settimg data.
580 */
581 public function sanitize_form_inputs( $input_settings = array() ) {
582 $new_settings = array();
583 foreach ( $input_settings as $key => $val ) {
584
585 if ( is_array( $val ) ) {
586 foreach ( $val as $k => $v ) {
587 $new_settings[ $key ][ $k ] = ( isset( $val[ $k ] ) ) ? sanitize_text_field( $v ) : '';
588 }
589 } else {
590 $new_settings[ $key ] = ( isset( $input_settings[ $key ] ) ) ? sanitize_text_field( $val ) : '';
591 }
592 }
593 return $new_settings;
594 }
595
596 /**
597 * Track KB search terms.
598 *
599 * @return void
600 */
601 public function track_kb_search() {
602 if ( ! current_user_can( 'cartflows_manage_settings' ) ) {
603 wp_send_json_error( array( 'message' => $this->get_error_msg( 'permission' ) ) );
604 }
605
606 /**
607 * Nonce verification
608 */
609 if ( ! check_ajax_referer( 'cartflows_track_kb_search', 'security', false ) ) {
610 $response_data = array( 'messsage' => __( 'Nonce validation failed', 'cartflows' ) );
611 wp_send_json_error( $response_data );
612 }
613
614 // Get search term.
615 $search_term = isset( $_POST['search_term'] ) ? sanitize_text_field( wp_unslash( $_POST['search_term'] ) ) : '';
616
617 if ( empty( $search_term ) ) {
618 wp_send_json_error( array( 'message' => 'Search term is empty' ) );
619 }
620
621 // Store the search term for BSF Analytics.
622 $kb_searches = get_option( 'cartflows_kb_searches', array() );
623
624 // Add the current search term to the list.
625 $kb_searches[] = $search_term;
626
627 // Keep only the last 20 searches to avoid data overload.
628 $kb_searches = array_slice( $kb_searches, -20 );
629
630 // Update the option.
631 update_option( 'cartflows_kb_searches', $kb_searches );
632
633 wp_send_json_success( array( 'message' => 'Search term tracked successfully' ) );
634 }
635
636 /**
637 * Handle on-demand custom script migration triggered by user clicking "Migrate Data".
638 *
639 * Verifies nonce and capabilities, calls the migration logic from Cartflows_Update,
640 * and returns a JSON response with the migrated post count.
641 *
642 * @since 2.2.2
643 * @return void
644 */
645 public function migrate_custom_scripts() {
646
647 $response_data = array( 'message' => $this->get_error_msg( 'permission' ) );
648
649 if ( ! current_user_can( 'cartflows_manage_settings' ) ) {
650 wp_send_json_error( $response_data );
651 }
652
653 if ( ! check_ajax_referer( 'cartflows_migrate_custom_scripts', 'security', false ) ) {
654 $response_data = array( 'message' => $this->get_error_msg( 'nonce' ) );
655 wp_send_json_error( $response_data );
656 }
657
658 $migrated_count = \Cartflows_Update::get_instance()->migrate_custom_scripts_on_demand();
659
660 wp_send_json_success(
661 array(
662 /* translators: %d: number of posts migrated */
663 'message' => sprintf( __( 'Migration completed successfully. %d post(s) migrated.', 'cartflows' ), $migrated_count ),
664 'migrated_count' => $migrated_count,
665 )
666 );
667 }
668 }
669