PluginProbe
CartFlows – Funnel Builder & Checkout Plugin for WooCommerce / 3.3.0
CartFlows – Funnel Builder & Checkout Plugin for WooCommerce v3.3.0
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 / inc / admin-menu.php

admin-menu.php in CartFlows – Funnel Builder & Checkout Plugin for WooCommerce 3.3.0, at admin-legacy-core/inc/admin-menu.php

1,231 lines 43.9 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * CartFlows Admin Menu.
4 *
5 * @package CartFlows
6 */
7
8 namespace CartflowsAdmin\AdminLegacyCore\Inc;
9
10 use CartflowsAdmin\AdminLegacyCore\Inc\AdminHelper;
11 use CartflowsAdmin\AdminLegacyCore\Inc\LogStatus;
12
13
14 // Exit if accessed directly.
15 if ( ! defined( 'ABSPATH' ) ) {
16 exit;
17 }
18
19 /**
20 * Class Admin_Menu.
21 */
22 class AdminMenu {
23
24 /**
25 * Instance
26 *
27 * @access private
28 * @var object Class object.
29 * @since 1.0.0
30 */
31 private static $instance;
32
33 /**
34 * For Gutenberg
35 *
36 * @var $is_gutenberg_editor_active
37 */
38 private $is_gutenberg_editor_active = false;
39
40 /**
41 * Initiator
42 *
43 * @since 1.0.0
44 * @return object initialized object of class.
45 */
46 public static function get_instance() {
47 if ( ! isset( self::$instance ) ) {
48 self::$instance = new self();
49 }
50 return self::$instance;
51 }
52
53 /**
54 * Instance
55 *
56 * @access private
57 * @var string Class object.
58 * @since 1.0.0
59 */
60 private $menu_slug;
61
62 /**
63 * Constructor
64 *
65 * @since 1.0.0
66 */
67 public function __construct() {
68 $this->menu_slug = 'cartflows';
69
70 $this->initialize_hooks();
71 }
72
73 /**
74 * Init Hooks.
75 *
76 * @since 1.0.0
77 * @return void
78 */
79 public function initialize_hooks() {
80
81 add_action( 'admin_menu', array( $this, 'setup_menu' ) );
82 add_action( 'admin_init', array( $this, 'settings_admin_scripts' ) );
83 add_action( 'admin_init', array( $this, 'add_capabilities_to_admin' ) );
84
85 /* Flow content view */
86 add_action( 'cartflows_render_admin_page_content', array( $this, 'render_content' ), 10, 2 );
87
88 add_action( 'edit_form_after_title', array( $this, 'new_admin_flow_setting_redirection' ), 10, 1 );
89
90 /* To check the status of gutenberg */
91 add_action( 'enqueue_block_editor_assets', array( $this, 'set_block_editor_status' ) );
92 add_action( 'admin_footer', array( $this, 'back_to_new_step_ui_for_gutenberg' ), 999 );
93
94 add_action( 'admin_notices', array( $this, 'back_to_new_step_ui_for_classic_editor' ) );
95
96 add_action( 'wp_ajax_cartflows_fetch_whats_new_data', array( $this, 'fetch_whats_new_data' ) );
97 }
98
99 /**
100 * Display admin notices.
101 *
102 * @since 1.0.0
103 *
104 * @return void
105 */
106 public function back_to_new_step_ui_for_classic_editor() {
107
108 if ( CARTFLOWS_STEP_POST_TYPE !== get_post_type() ) {
109 return;
110 }
111
112 $flow_id = get_post_meta( get_the_id(), 'wcf-flow-id', true );
113 $step_id = isset( $_GET['post'] ) ? intval( $_GET['post'] ) : 0; //phpcs:ignore WordPress.Security.NonceVerification.Recommended
114
115 if ( $flow_id && $step_id ) {
116 $path = \Cartflows_Helper::get_global_setting( '_cartflows_store_checkout' ) === $flow_id ? 'store-checkout' : 'flows';
117
118 $flow_redirect_url = esc_url( admin_url() . 'admin.php?page=' . $this->menu_slug . '&path= ' . $path . '&action=wcf-edit-flow&flow_id=' . $flow_id ); // phpcs:igmore Generic.Strings.UnnecessaryStringConcat.Found.
119 ?>
120 <div class="wcf-notice-back-edit-step">
121 <p>
122 <a href="<?php echo esc_url( $flow_redirect_url ); ?>" class="button button-primary button-hero wcf-header-back-button" style="text-decoration: none;">
123 <?php esc_html_e( 'Edit Funnel', 'cartflows' ); ?>
124 </a>
125 </p>
126 </div>
127 <?php
128 }
129 }
130
131 /**
132 * Set status true for gutenberg.
133 *
134 * @return void
135 */
136 public function set_block_editor_status() {
137
138 if ( ! current_user_can( 'cartflows_manage_flows_steps' ) ) {
139 return;
140 }
141
142 // Set gutenberg status here.
143 $this->is_gutenberg_editor_active = true;
144 }
145
146 /**
147 * Back to flow button gutenberg template
148 *
149 * @return void
150 */
151 public function back_to_new_step_ui_for_gutenberg() {
152
153 // Exit if block editor is not enabled.
154 if ( ! $this->is_gutenberg_editor_active ) {
155 return;
156 }
157
158 wp_enqueue_script( 'cartflows-admin-common-script', CARTFLOWS_ADMIN_CORE_URL . 'assets/js/common.js', array( 'jquery' ), CARTFLOWS_VER, false );
159
160 if ( CARTFLOWS_STEP_POST_TYPE !== get_post_type() ) {
161 return;
162 }
163
164 $flow_id = get_post_meta( get_the_id(), 'wcf-flow-id', true );
165 $step_id = isset( $_GET['post'] ) ? intval( $_GET['post'] ) : 0; //phpcs:ignore WordPress.Security.NonceVerification.Recommended
166
167 if ( $flow_id && $step_id ) {
168 $path = \Cartflows_Helper::get_global_setting( '_cartflows_store_checkout' ) === $flow_id ? 'store-checkout' : 'flows';
169 $flow_redirect_url = esc_url( admin_url() . 'admin.php?page=' . $this->menu_slug . '&path=' . $path . '&action=wcf-edit-flow&flow_id=' . $flow_id );
170 ?>
171 <script id="wcf-gutenberg-back-step-button" type="text/html">
172 <div class="wcf-notice-back-edit-step gutenberg-button" style="display: flex; margin: 2px 5px;">
173 <a href="<?php echo esc_url( $flow_redirect_url ); ?>" class="button button-primary button-large wcf-header-back-button" style="text-decoration: none; font-size: 13px; line-height: 2.5;"><?php esc_html_e( 'Edit Funnel', 'cartflows' ); ?></a>
174 </div>
175 </script>
176 <script>
177 window.addEventListener('load', function () {
178 ( function( window, wp ){
179 var link = document.querySelector('a.components-button.edit-post-fullscreen-mode-close');
180 if (link) {
181 link.setAttribute('href', "<?php echo htmlspecialchars_decode( esc_url( $flow_redirect_url ) );//phpcs:ignore ?>")
182 }
183
184 } )( window, wp )
185 });
186 </script>
187 <?php
188 }
189 }
190
191
192 /**
193 * Back to flow button
194 *
195 * @param array $meta meta data.
196 *
197 * @return void
198 */
199 public function new_admin_flow_setting_redirection( $meta ) {
200
201 $flow_id = isset( $_GET['post'] ) ? intval( $_GET['post'] ) : 0; //phpcs:ignore WordPress.Security.NonceVerification.Recommended
202
203 $post_type = get_post_type();
204
205 if ( CARTFLOWS_FLOW_POST_TYPE === $post_type && 0 !== $flow_id ) {
206
207 $redirect_url = esc_url( admin_url() . 'admin.php?page=' . $this->menu_slug . '&path=flows&action=wcf-edit-flow&flow_id=' . $flow_id );
208 $btn_markup = '<div class="wcf-flow-editing-action" style="padding: 50px;text-align: center;">';
209 $btn_markup .= '<a class="button button-primary button-hero" href="' . $redirect_url . '">' . __( 'Go to Funnel Editing', 'cartflows' ) . '</a>';
210 $btn_markup .= '</div>';
211
212 echo wp_kses_post( $btn_markup );
213 }
214 }
215
216 /**
217 * Initialize after Cartflows pro get loaded.
218 */
219 public function settings_admin_scripts() {
220
221 // Enqueue admin scripts.
222 // Ignoring nonce verification as using SuperGlobal variables on WordPress hooks.
223 if ( isset( $_GET['page'] ) && ( 'cartflows' === sanitize_text_field( $_GET['page'] ) || false !== strpos( sanitize_text_field( $_GET['page'] ), 'cartflows_' ) ) ) { //phpcs:ignore WordPress.Security.NonceVerification.Recommended
224
225 add_action( 'admin_enqueue_scripts', array( $this, 'styles_scripts' ) );
226
227 add_filter( 'admin_footer_text', array( $this, 'add_footer_link' ), 99 );
228 }
229 }
230
231 /**
232 * Add submenu to admin menu.
233 *
234 * @since 1.0.0
235 */
236 public function setup_menu() {
237 global $submenu;
238
239 if ( current_user_can( 'cartflows_manage_flows_steps' ) ) {
240 $parent_slug = $this->menu_slug;
241 $capability = 'cartflows_manage_flows_steps';
242 $menu_priority = apply_filters( 'cartflows_menu_priority', ! defined( 'ASTRA_THEME_VERSION' ) ? 40 : 3 );
243
244 add_menu_page(
245 'CartFlows',
246 'CartFlows',
247 $capability,
248 $parent_slug,
249 array( $this, 'render' ),
250 'data:image/svg+xml;base64,' . base64_encode( file_get_contents( CARTFLOWS_DIR . 'assets/images/cartflows-icon.svg' ) ),
251 $menu_priority
252 );
253
254 // Add settings menu.
255 add_submenu_page(
256 $parent_slug,
257 __( 'Funnels', 'cartflows' ),
258 __( 'Funnels', 'cartflows' ),
259 $capability,
260 'admin.php?page=' . $this->menu_slug . '&path=flows'
261 );
262
263 $global_checkout_id = absint( \Cartflows_Helper::get_global_setting( '_cartflows_store_checkout' ) );
264 $global_checkout_param = ( empty( $global_checkout_id ) ) ? 'path=store-checkout' : 'path=store-checkout&action=wcf-edit-flow&flow_id=' . $global_checkout_id;
265
266 add_submenu_page(
267 $parent_slug,
268 __( 'Store Checkout', 'cartflows' ),
269 __( 'Store Checkout', 'cartflows' ),
270 $capability,
271 'admin.php?page=' . $this->menu_slug . '&' . $global_checkout_param
272 );
273
274 add_submenu_page(
275 $parent_slug,
276 __( 'Analytics', 'cartflows' ),
277 __( 'Analytics', 'cartflows' ),
278 $capability,
279 'admin.php?page=' . $this->menu_slug . '&path=analytics'
280 );
281
282 if ( current_user_can( 'cartflows_manage_settings' ) ) {
283 add_submenu_page(
284 $parent_slug,
285 __( 'Automations', 'cartflows' ),
286 // Here the inline CSS is added to make sure that the menu's tag css should display correctly on all pages.
287 __( 'Automations', 'cartflows' ) . '<span class="submenu-tag" style="margin-left: 4px; color: #f06434; vertical-align: super; font-size: 9px;">' . __( 'New', 'cartflows' ) . '</span>',
288 $capability,
289 'admin.php?page=' . $this->menu_slug . '&path=automations'
290 );
291
292 if ( 'active' !== $this->get_plugin_status( 'modern-cart/modern-cart.php' ) ) {
293 add_submenu_page(
294 $parent_slug,
295 __( 'Modern Cart', 'cartflows' ),
296 // Here the inline CSS is added to make sure that the menu's tag css should display correctly on all pages.
297 __( 'Modern Cart', 'cartflows' ) . '<span class="submenu-tag" style="margin-left: 4px; color: #f06434; vertical-align: super; font-size: 9px;">' . __( 'New', 'cartflows' ) . '</span>',
298 $capability,
299 'admin.php?page=' . $this->menu_slug . '&path=modern-cart'
300 );
301 }
302
303 add_submenu_page(
304 $parent_slug,
305 __( 'Add-ons', 'cartflows' ),
306 __( 'Add-ons', 'cartflows' ),
307 ! wcf_file_mod_disabled() ? $capability : 'do_not_allow',
308 'admin.php?page=' . $this->menu_slug . '&path=addons'
309 );
310
311 add_submenu_page(
312 $parent_slug,
313 __( 'Learn', 'cartflows' ),
314 __( 'Learn', 'cartflows' ),
315 $capability,
316 'admin.php?page=' . $this->menu_slug . '&path=learn'
317 );
318
319 if ( ! _is_cartflows_pro() ) {
320 add_submenu_page(
321 $parent_slug,
322 __( 'Get CartFlows Pro', 'cartflows' ),
323 __( 'Get CartFlows Pro', 'cartflows' ),
324 $capability,
325 \Cartflows_Helper::get_upgrade_to_pro_link()
326 );
327 }
328 }
329
330 // Rename to Home menu.
331 // Disable phpcs since we need to override submenu name.
332 $submenu[ $parent_slug ][0][0] = __( 'Dashboard', 'cartflows' ); //phpcs:ignore WordPress.WP.GlobalVariablesOverride.Prohibited
333 }
334 }
335
336 /**
337 * Add custom capabilities to Admin user.
338 */
339 public function add_capabilities_to_admin() {
340
341 global $wp_roles;
342 // Add custom capabilities to admin by default.
343 $capabilities = array(
344 'cartflows_manage_settings',
345 'cartflows_manage_flows_steps',
346 );
347
348 foreach ( $capabilities as $cap ) {
349 $wp_roles->add_cap( 'administrator', $cap );
350 }
351 }
352
353 /**
354 * Renders the admin settings.
355 *
356 * @since 1.0.0
357 * @return void
358 */
359 public function render() {
360 $menu_page_slug = ( isset( $_GET['page'] ) ) ? sanitize_text_field( wp_unslash( $_GET['page'] ) ) : CARTFLOWS_SETTINGS; //phpcs:ignore WordPress.Security.NonceVerification.Recommended
361 $page_action = '';
362
363 if ( isset( $_GET['action'] ) ) { //phpcs:ignore WordPress.Security.NonceVerification.Recommended
364 $page_action = sanitize_text_field( wp_unslash( $_GET['action'] ) ); //phpcs:ignore WordPress.Security.NonceVerification.Recommended
365 $page_action = str_replace( '_', '-', $page_action );
366 }
367
368 include_once CARTFLOWS_ADMIN_CORE_DIR . 'views/admin-base.php';
369 }
370
371 /**
372 * Renders the admin settings content.
373 *
374 * @since 1.0.0
375 * @param sting $menu_page_slug current page name.
376 * @param sting $page_action current page action.
377 *
378 * @return void
379 */
380 public function render_content( $menu_page_slug, $page_action ) {
381
382 if ( 'cartflows' === $menu_page_slug ) {
383 if ( $this->is_current_page( 'cartflows' ) ) {
384 include_once CARTFLOWS_ADMIN_CORE_DIR . 'views/settings-app.php';
385 } elseif ( $this->is_current_page( 'cartflows', array( 'wcf-edit-flow' ) ) ) {
386 include_once CARTFLOWS_ADMIN_CORE_DIR . 'views/editor-app.php';
387 } elseif ( $this->is_current_page( 'cartflows', array( 'wcf-license' ) ) && _is_cartflows_pro() ) {
388 do_action( 'cartflows_admin_log', 'wcf-license' );
389 } else {
390 include_once CARTFLOWS_ADMIN_CORE_DIR . 'views/404-error.php';
391 }
392 }
393 }
394
395 /**
396 * Enqueues the needed CSS/JS for the builder's admin settings page.
397 *
398 * @since 1.0.0
399 */
400 public function styles_scripts() {
401
402 $admin_slug = 'cartflows-admin';
403
404 // Load the custom font for admin area.
405 wp_enqueue_style( $admin_slug . '-font', 'https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600&display=swap', array(), CARTFLOWS_VER );
406
407 // Styles.
408 wp_enqueue_style( $admin_slug . '-common', CARTFLOWS_ADMIN_CORE_URL . 'assets/css/common.css', array(), CARTFLOWS_VER );
409 wp_style_add_data( $admin_slug . '-common', 'rtl', 'replace' );
410
411 wp_enqueue_script( $admin_slug . '-common-script', CARTFLOWS_ADMIN_CORE_URL . 'assets/js/common.js', array( 'jquery' ), CARTFLOWS_VER, false );
412
413 wp_enqueue_script( $admin_slug . '-suretriggers-integration', CARTFLOWS_SURETRIGGERS_INTEGRATION_BASE_URL . 'js/v2/embed.js', array(), CARTFLOWS_VER, true );
414
415 $current_flow_steps = array();
416 $flow_id = isset( $_GET['flow_id'] ) ? intval( $_GET['flow_id'] ) : 0; //phpcs:ignore WordPress.Security.NonceVerification.Recommended
417 if ( $flow_id ) {
418 $current_flow_steps = AdminHelper::get_flow_meta_options( $flow_id );
419 }
420
421 $product_src = esc_url_raw(
422 add_query_arg(
423 array(
424 'post_type' => 'product',
425 'wcf-woo-iframe' => 'true',
426 ),
427 admin_url( 'post-new.php' )
428 )
429 );
430
431
432 $page_builder = \Cartflows_Helper::get_common_setting( 'default_page_builder' );
433 $page_builder_name = \Cartflows_Helper::get_page_builder_name( $page_builder );
434
435 $global_checkout_id = \Cartflows_Helper::get_global_setting( '_cartflows_store_checkout' );
436
437 $flow_action = 'wcf-edit-flow';
438 $step_action = 'wcf-edit-step';
439
440 $flows_and_steps = \Cartflows_Helper::get_instance()->get_flows_and_steps();
441
442 $cf_pro_status = $this->get_cartflows_pro_plugin_status();
443 $cf_pro_type_inactive = '';
444 if ( 'inactive' === $cf_pro_status || 'not-installed' === $cf_pro_status ) {
445
446 if ( ! function_exists( 'get_plugins' ) ) {
447 require_once ABSPATH . 'wp-admin/includes/plugin.php';
448 }
449
450 $plugin = get_plugins();
451 $cf_pro_type_inactive = isset( $plugin['cartflows-pro/cartflows-pro.php']['Name'] ) ? $plugin['cartflows-pro/cartflows-pro.php']['Name'] : '';
452 }
453
454 $order_url = '#';
455
456 if ( wcf()->is_woo_active && class_exists( '\Automattic\WooCommerce\Utilities\OrderUtil' ) ) {
457 $order_url = \Automattic\WooCommerce\Utilities\OrderUtil::custom_orders_table_usage_is_enabled() ? admin_url( 'admin.php?page=wc-orders' ) : admin_url( 'edit.php?post_type=shop_order' );
458 }
459
460 $localize = apply_filters(
461 'cartflows_admin_localized_vars',
462 array(
463 'current_user' => ! empty( wp_get_current_user()->user_firstname ) ? wp_get_current_user()->user_firstname : wp_get_current_user()->display_name,
464 'cf_pro_status' => $cf_pro_status,
465 'cf_pro_type' => 'free',
466 'cf_pro_type_inactive' => $cf_pro_type_inactive,
467 'woocommerce_status' => $this->get_plugin_status( 'woocommerce/woocommerce.php' ),
468 'default_page_builder' => $page_builder,
469 'required_plugins' => \Cartflows_Helper::get_plugins_groupby_page_builders(),
470 'required_plugins_data' => $this->get_required_plugins_data(),
471 'is_any_required_plugins_missing' => $this->get_any_required_plugins_status(),
472 'admin_base_slug' => $this->menu_slug,
473 'admin_base_url' => admin_url(),
474 'title_length' => apply_filters(
475 'cartflows_flows_steps_title_length',
476 array(
477 'max' => 50,
478 'display_length' => 40,
479 )
480 ),
481 'plugin_dir' => CARTFLOWS_URL,
482 'admin_url' => admin_url( 'admin.php' ),
483 'ajax_url' => admin_url( 'admin-ajax.php' ),
484 'is_rtl' => is_rtl(),
485 'home_slug' => $this->menu_slug,
486 'is_pro' => _is_cartflows_pro(),
487 'is_file_mod_disabled' => function_exists( 'wcf_file_mod_disabled' ) ? wcf_file_mod_disabled() : false, // Keep false by default if function not exists.
488 'page_builder' => $page_builder,
489 'page_builder_name' => $page_builder_name,
490 'global_checkout' => \Cartflows_Helper::get_common_setting( 'global_checkout' ),
491 'flows_count' => 1, // Removing the flow count condition.
492 'currentFlowSteps' => $current_flow_steps,
493 // Delete this code after 3 major update. Added in 1.10.4.
494 'license_status' => \_is_cartflows_pro_license_activated(),
495 'license_popup_url' => admin_url( 'admin.php?page=cartflows&settings=1&tab=license' ),
496 'store_checkout_show_product_tab' => \Cartflows_Helper::display_product_tab_in_store_checkout(),
497 'cf_domain_url' => CARTFLOWS_DOMAIN_URL,
498 'cf_upgrade_to_pro_url' => \Cartflows_Helper::get_upgrade_to_pro_link(),
499 'logo_url' => esc_url_raw( CARTFLOWS_URL . 'assets/images/cartflows-logo.svg' ),
500 'create_product_src' => $product_src,
501 'cf_font_family' => AdminHelper::get_font_family(),
502 'flows_and_steps' => ! empty( $flows_and_steps ) ? $flows_and_steps : '',
503 'store_checkout_flows_and_steps' => \Cartflows_Helper::get_instance()->get_flows_and_steps( '', 'store-checkout' ),
504 'woo_currency' => function_exists( 'get_woocommerce_currency_symbol' ) ? get_woocommerce_currency_symbol() : '',
505 'template_library_url' => wcf()->get_site_url(),
506 'image_placeholder' => esc_url_raw( CARTFLOWS_URL . 'admin-legacy-core/assets/images/image-placeholder.png' ),
507 'google_fonts' => \CartFlows_Font_Families::get_google_fonts(),
508 'system_fonts' => \CartFlows_Font_Families::get_system_fonts(),
509 'font_weights' => array(
510 '100' => __( 'Thin 100', 'cartflows' ),
511 '200' => __( 'Extra-Light 200', 'cartflows' ),
512 '300' => __( 'Light 300', 'cartflows' ),
513 '400' => __( 'Normal 400', 'cartflows' ),
514 '500' => __( 'Medium 500', 'cartflows' ),
515 '600' => __( 'Semi-Bold 600', 'cartflows' ),
516 '700' => __( 'Bold 700', 'cartflows' ),
517 '800' => __( 'Extra-Bold 800', 'cartflows' ),
518 '900' => __( 'Ultra-Bold 900', 'cartflows' ),
519 ),
520 'global_checkout_id' => $global_checkout_id ? absint( $global_checkout_id ) : '',
521 'flow_action' => $flow_action,
522 'step_action' => $step_action,
523 'old_global_checkout' => get_option( '_cartflows_old_global_checkout', false ),
524 'woopayments_status' => $this->get_plugin_status( 'woocommerce-payments/woocommerce-payments.php' ),
525 'ca_status' => $this->get_plugin_status( 'woo-cart-abandonment-recovery/woo-cart-abandonment-recovery.php' ),
526 'suretriggers_status' => $this->get_plugin_status( 'suretriggers/suretriggers.php' ),
527 'moderncart_status' => $this->get_plugin_status( 'modern-cart/modern-cart.php' ),
528 'cpsw_connection_status' => 'success' === get_option( 'cpsw_test_con_status', false ) || 'success' === get_option( 'cpsw_con_status', false ),
529 'current_user_can_manage_cartflows' => current_user_can( 'cartflows_manage_settings' ),
530 'is_set_report_email_ids' => get_option( 'cartflows_stats_report_email_ids', false ),
531 'cf_docs_data' => get_option( 'cartflows_docs_data', false ),
532 'woo_order_url' => $order_url,
533 'integrations' => $this->get_recommendation_integrations(),
534 'plugin_installer_nonce' => wp_create_nonce( 'updates' ),
535 'instant_checkout_notice_status' => \Cartflows_Helper::get_admin_settings_option( 'wcf-instant-checkout-notice-skipped', false, false ),
536 'cartflows_admin_notices' => $this->cartflows_admin_notices(),
537 'whats_new_rss_feed' => $this->get_whats_new_rss_feeds_data(),
538 'cartflows_current_version' => CARTFLOWS_VER,
539 'cartflows_previous_versions' => \Cartflows_Helper::get_rollback_versions_options(),
540 'rollback_url' => esc_url( add_query_arg( 'version', 'VERSION', wp_nonce_url( admin_url( 'admin-post.php?action=cartflows_rollback' ), 'cartflows_rollback' ) ) ),
541 'utm_defaults' => \Cartflows_Helper::get_utm_defaults(),
542 'switch_ui_notice' => array(
543 // is_new_ui_notice_visible() already enforces the
544 // cartflows_manage_settings capability + CARTFLOWS_LEGACY_ADMIN
545 // + per-user dismissal — same gating the AJAX endpoints check.
546 'visible' => \Cartflows_Admin_Notices::get_instance()->is_new_ui_notice_visible(),
547 'switch_action' => 'cartflows_switch_to_new_ui',
548 'switch_nonce' => wp_create_nonce( 'cartflows_switch_to_new_ui' ),
549 'dismiss_action' => 'cartflows_dismiss_new_ui_notice',
550 'dismiss_nonce' => wp_create_nonce( 'cartflows_dismiss_new_ui_notice' ),
551 ),
552 )
553 );
554
555 // Load the codemirror for code editor.
556 wp_enqueue_code_editor(
557 array(
558 'type' => 'text/javascript',
559 )
560 );
561 wp_enqueue_code_editor(
562 array(
563 'type' => 'text/css',
564 )
565 );
566
567 wp_enqueue_script( 'wp-code-editor' );
568 wp_enqueue_style( 'wp-codemirror' );
569
570 if ( $this->is_current_page( $this->menu_slug ) ) {
571 $this->settings_app_scripts( $localize );
572 } elseif ( $this->is_current_page( 'cartflows', array( 'wcf-edit-flow' ) ) ) {
573 wp_enqueue_media();
574 wp_enqueue_editor(); // Require for Order Bump Desc WP Editor/ tinymce field.
575 $this->editor_app_scripts( $localize );
576 }
577 }
578
579 /**
580 * Get recommendation integrations.
581 *
582 * @since 1.11.8
583 *
584 * @return array
585 */
586 public function cartflows_admin_notices() {
587 $notices = array();
588
589 $notices = $this->page_builder_plugin_notices( $notices );
590 $notices = apply_filters( 'cartflows_admin_notices', $notices );
591
592 return $notices;
593 }
594
595 /**
596 * Get builder plugin notices.
597 *
598 * @since 1.11.8
599 * @param array $notices Notices array.
600 * @return array
601 */
602 public function page_builder_plugin_notices( $notices ) {
603 $required_plugins_missing = $this->get_any_required_plugins_status();
604 $default_page_builder = \Cartflows_Helper::get_common_setting( 'default_page_builder' );
605 $required_plugins = array();
606 if ( isset( \Cartflows_Helper::get_plugins_groupby_page_builders()[ $default_page_builder ] ) ) {
607 $required_plugins = \Cartflows_Helper::get_plugins_groupby_page_builders()[ $default_page_builder ];
608 }
609
610 $page_builder_plugins = isset( $required_plugins['plugins'] ) ? $required_plugins['plugins'] : array();
611 $any_inactive = false;
612 if ( ! empty( $page_builder_plugins ) ) {
613 foreach ( $page_builder_plugins as $plugin ) {
614 if ( 'activated' !== $plugin['status'] ) {
615 $any_inactive = true;
616 break; // Stop checking if we found an active plugin.
617 }
618 }
619 }
620
621 // Get the titles.
622 $titles = $this->generate_plugin_titles( $page_builder_plugins );
623
624 if ( 'yes' === $required_plugins_missing && $any_inactive ) {
625 $notices[] = '<div class="wcf-payment-gateway-notice--text"><p class="text-sm text-yellow-700">' . wp_kses_post(
626 sprintf(
627 // Translators: %1$s is the required page builder title, %2$s is the opening anchor tag to plugins.php, %3$s is the closing anchor tag, %4$s is the plugin title.
628 __( 'The default page builder is set to %1$s. Please %2$sinstall & activate%3$s the %4$s to start editing the steps.', 'cartflows' ), //phpcs:ignore
629 '<span class="capitalize">' . esc_html( $required_plugins['title'] ) . '</span>',
630 '<span class="font-medium"><a href="' . esc_url( admin_url() . 'plugins.php' ) . '" class="underline text-yellow-700 hover:text-yellow-600" target="_blank">',
631 '</a></span>',
632 '<span class="capitalize">' . esc_html( implode( ', ', $titles ) ) . '</span>'
633 )
634 ) . '</p></div>';
635
636 }
637 return $notices;
638 }
639
640 /**
641 * Get required plugin titles from their slugs.
642 *
643 * @since 2.1.6
644 * @param array $plugins plugins array.
645 * @return array
646 */
647 public function generate_plugin_titles( $plugins ) {
648 $titles = array();
649
650 if ( ! is_array( $plugins ) || empty( $plugins ) ) {
651 return $titles;
652 }
653
654 foreach ( $plugins as $plugin ) {
655
656 if ( 'ultimate-addons-for-gutenberg' === $plugin['slug'] ) {
657 $title = 'Spectra'; // Don't add this for translation as it is a plugin name.
658 } else {
659 $slug_parts = explode( '-', $plugin['slug'] );
660 $title = implode( ' ', array_map( 'ucfirst', $slug_parts ) ); // Convert each part to title case.
661 }
662
663 $titles[] = $title;
664 }
665
666 return $titles;
667 }
668
669
670 /**
671 * Get required plugin status.
672 */
673 public function get_required_plugins_data() {
674
675 $missing_plugins_data = array();
676
677 $page_builder_plugins = \Cartflows_Helper::get_plugins_groupby_page_builders();
678
679 foreach ( $page_builder_plugins as $slug => $data ) {
680
681 $missing_plugins_data[ $slug ] = 'no';
682 $current_page_builder_data = $page_builder_plugins[ $slug ];
683
684 foreach ( $current_page_builder_data['plugins'] as $plugin ) {
685 if ( 'activate' === $plugin['status'] || 'install' === $plugin['status'] ) {
686 $missing_plugins_data[ $slug ] = 'yes';
687 }
688 }
689
690 // Divi.
691 if ( 'divi' === $slug ) {
692 if ( 'activate' === $current_page_builder_data['theme-status'] ) {
693 $missing_plugins_data[ $slug ] = 'yes';
694 }
695 }
696 }
697
698 return $missing_plugins_data;
699 }
700 /**
701 * Get required plugin status
702 */
703 public function get_any_required_plugins_status() {
704
705 $default_page_builder = \Cartflows_Helper::get_common_setting( 'default_page_builder' );
706 $any_inactive = 'no';
707
708 $page_builder_plugins = \Cartflows_Helper::get_plugins_groupby_page_builders();
709
710 if ( isset( $page_builder_plugins[ $default_page_builder ] ) ) {
711
712 $current_page_builder_data = $page_builder_plugins[ $default_page_builder ];
713
714 foreach ( $current_page_builder_data['plugins'] as $plugin ) {
715 if ( 'activate' === $plugin['status'] || 'install' === $plugin['status'] ) {
716 $any_inactive = 'yes';
717 }
718 }
719
720 // Divi.
721 if ( 'divi' === $default_page_builder ) {
722 if ( 'activate' === $current_page_builder_data['theme-status'] ) {
723 $any_inactive = 'yes';
724 }
725 }
726 }
727
728 return $any_inactive;
729 }
730
731 /**
732 * Get CartFlows plugin status
733 *
734 * @since 1.11.8
735 *
736 * @return string
737 */
738 public function get_cartflows_pro_plugin_status() {
739
740 $status = $this->get_plugin_status( 'cartflows-pro/cartflows-pro.php' );
741
742 if ( 'active' === $status && ! _is_cartflows_pro() ) {
743
744 $status = 'inactive';
745 }
746
747 return $status;
748 }
749
750 /**
751 * Get plugin status
752 *
753 * @since 1.1.4
754 *
755 * @param string $plugin_init_file Plguin init file.
756 * @return mixed
757 */
758 public function get_plugin_status( $plugin_init_file ) {
759
760 $installed_plugins = get_plugins();
761
762 if ( ! isset( $installed_plugins[ $plugin_init_file ] ) ) {
763 return 'not-installed';
764 } elseif ( is_plugin_active( $plugin_init_file ) ) {
765 return 'active';
766 } else {
767 return 'inactive';
768 }
769 }
770
771 /**
772 * Get plugin status
773 *
774 * @since 1.1.4
775 *
776 * @param string $theme_name Theme slug file.
777 * @return string
778 */
779 public function get_themes_status( $theme_name ) {
780
781 $theme = wp_get_theme();
782
783 // Theme installed and activate.
784 if ( $theme_name === $theme->name || $theme_name === $theme->parent_theme ) {
785 return 'active';
786 }
787
788 // Theme installed but not activate.
789 foreach ( (array) wp_get_themes() as $theme_dir => $theme ) {
790 if ( $theme_name === $theme->name || $theme_name === $theme->parent_theme ) {
791 return 'inactive';
792 }
793 }
794
795 // If none of the above conditions are found then the theme is not installed.
796 return 'not-installed';
797 }
798
799 /**
800 * Settings app scripts
801 *
802 * @param array $localize Variable names.
803 */
804 public function settings_app_scripts( $localize ) {
805 $handle = 'wcf-react-settings';
806 $build_path = CARTFLOWS_ADMIN_CORE_DIR . 'assets/build/';
807 $build_url = CARTFLOWS_ADMIN_CORE_URL . 'assets/build/';
808 $script_asset_path = $build_path . 'settings-app.asset.php';
809 $script_info = file_exists( $script_asset_path )
810 ? include $script_asset_path
811 : array(
812 'dependencies' => array(),
813 'version' => CARTFLOWS_VER,
814 );
815
816 $script_dep = array_merge( $script_info['dependencies'], array( 'updates' ) );
817
818 wp_register_script(
819 $handle,
820 $build_url . 'settings-app.js',
821 $script_dep,
822 $script_info['version'],
823 true
824 );
825
826 wp_register_style(
827 $handle,
828 $build_url . 'settings-app.css',
829 array(),
830 CARTFLOWS_VER
831 );
832
833 wp_enqueue_script( $handle );
834
835 wp_set_script_translations( $handle, 'cartflows' );
836
837 wp_enqueue_style( $handle );
838 wp_style_add_data( $handle, 'rtl', 'replace' );
839
840 $localize['is_flows_limit'] = false; // Removed the flow count condition.
841
842 $localize = $this->debugger_scripts( $localize );
843
844 $localize['save_learn_completed_nonce'] = wp_create_nonce( 'cartflows_save_learn_completed' );
845 $localize['activate_plugin_nonce'] = wp_create_nonce( 'cartflows_activate_plugin' );
846
847 wp_localize_script( $handle, 'cartflows_admin', $localize );
848 }
849
850 /**
851 * Settings app scripts
852 *
853 * @param array $localize Variable names.
854 */
855 public function editor_app_scripts( $localize ) {
856 $handle = 'wcf-editor-app';
857 $build_path = CARTFLOWS_ADMIN_CORE_DIR . 'assets/build/';
858 $build_url = CARTFLOWS_ADMIN_CORE_URL . 'assets/build/';
859 $script_asset_path = $build_path . 'editor-app.asset.php';
860 $script_info = file_exists( $script_asset_path )
861 ? include $script_asset_path
862 : array(
863 'dependencies' => array(),
864 'version' => CARTFLOWS_VER,
865 );
866
867 wp_register_script(
868 $handle,
869 $build_url . 'editor-app.js',
870 $script_info['dependencies'],
871 $script_info['version'],
872 true
873 );
874
875 wp_register_style(
876 $handle,
877 $build_url . 'editor-app.css',
878 array(),
879 CARTFLOWS_VER
880 );
881
882 wp_register_style(
883 'wcf-canvas-base-style',
884 $build_url . 'style-editor-app.css',
885 array(),
886 CARTFLOWS_VER
887 );
888 // Enqueue the registered style.
889 wp_enqueue_style( 'wcf-canvas-base-style' );
890
891 wp_enqueue_script( $handle );
892
893 wp_set_script_translations( $handle, 'cartflows' );
894
895 wp_enqueue_style( $handle );
896 wp_style_add_data( $handle, 'rtl', 'replace' );
897
898 $localize['flow_id'] = isset( $_GET['flow_id'] ) ? intval( $_GET['flow_id'] ) : 0; //phpcs:ignore WordPress.Security.NonceVerification.Recommended
899
900 $step_id = isset( $_GET['step_id'] ) ? intval( $_GET['step_id'] ) : false; //phpcs:ignore WordPress.Security.NonceVerification.Recommended
901
902 if ( $step_id ) {
903
904 $meta_options = AdminHelper::get_step_meta_options( $step_id );
905 $localize['options'] = $meta_options['options'];
906 }
907
908 wp_localize_script( $handle, 'cartflows_admin', $localize );
909 }
910
911 /**
912 * Debugger scripts.
913 *
914 * @param array $localize Variable names.
915 */
916 public function debugger_scripts( $localize ) {
917
918 $logs = LogStatus::get_instance()->get_log_files();
919 $viewed_log = '';
920 $viewed_log_file = '';
921
922 // Calling this function on CartFlows action hook. Hence ignoring nonce.
923 if ( ! empty( $_REQUEST['log_file'] ) ) { //phpcs:ignore WordPress.Security.NonceVerification.Recommended
924
925 $filename = sanitize_text_field( wp_unslash( $_REQUEST['log_file'] ) ); //phpcs:ignore WordPress.Security.NonceVerification.Recommended
926
927 if ( isset( $logs[ $filename ] ) ) {
928 $viewed_log = $filename;
929 $viewed_log_file = $viewed_log . '.log';
930 }
931 } elseif ( ! empty( $logs ) ) {
932 $viewed_log = current( $logs ) ? pathinfo( current( $logs ), PATHINFO_FILENAME ) : '';
933 $viewed_log_file = $viewed_log . '.log';
934 }
935
936 if ( ! empty( $viewed_log_file ) ) {
937 $file_content = file_get_contents( CARTFLOWS_LOG_DIR . $viewed_log_file );
938 $localize['file_content'] = $file_content;
939 $localize['log_key'] = $viewed_log;
940
941 }
942 $localize['logs'] = $logs;
943 return $localize;
944 }
945
946
947 /**
948 * CHeck if it is current page by parameters
949 *
950 * @param string $page_slug Menu name.
951 * @param string $action Menu name.
952 *
953 * @return string page url
954 */
955 public function is_current_page( $page_slug = '', $action = '' ) {
956
957 $page_matched = false;
958
959 if ( empty( $page_slug ) ) {
960 return false;
961 }
962
963 $current_page_slug = isset( $_GET['page'] ) ? sanitize_text_field( $_GET['page'] ) : ''; //phpcs:ignore WordPress.Security.NonceVerification.Recommended
964 $current_action = isset( $_GET['action'] ) ? sanitize_text_field( $_GET['action'] ) : ''; //phpcs:ignore WordPress.Security.NonceVerification.Recommended
965
966 if ( ! is_array( $action ) ) {
967 $action = explode( ' ', $action );
968 }
969
970 if ( $page_slug === $current_page_slug && in_array( $current_action, $action, true ) ) {
971 $page_matched = true;
972 }
973
974 return $page_matched;
975 }
976
977 /**
978 * Add footer link.
979 */
980 public function add_footer_link() {
981
982 $logs_page_url = add_query_arg(
983 array(
984 'page' => CARTFLOWS_SLUG,
985 'path' => 'wcf-log',
986 ),
987 admin_url( '/admin.php' )
988 );
989
990 return '<span id="footer-thankyou"> Thank you for using <a href="' . esc_url( \Cartflows_Helper::get_utm_url( 'https://cartflows.com/', array( 'utm_campaign' => 'footer-link' ) ) ) . '">CartFlows</a></span> | <a href="' . $logs_page_url . '">Logs</a>';
991 }
992
993 /**
994 * Get CartFlows recommended integrations.
995 *
996 * @return array
997 */
998 public function get_recommendation_integrations() {
999
1000 return array(
1001 'plugins' => apply_filters(
1002 'cartflows_admin_integrated_plugins',
1003 array(
1004 array(
1005 'title' => __( 'WooCommerce', 'cartflows' ),
1006 'subtitle' => __( 'WooCommerce is a customizable, open-source ecommerce platform built on WordPress.', 'cartflows' ),
1007 'isPro' => false,
1008 'status' => $this->get_plugin_status( 'woocommerce/woocommerce.php' ),
1009 'slug' => 'woocommerce',
1010 'path' => 'woocommerce/woocommerce.php',
1011 'redirection' => admin_url( 'admin.php?page=wc-admin' ),
1012 'logoPath' => array(
1013 'icon_path' => CARTFLOWS_ADMIN_CORE_URL . 'assets/images/plugins/woo.svg',
1014 ),
1015 ),
1016 array(
1017 'title' => __( 'Power Coupons', 'cartflows' ),
1018 'subtitle' => __( 'Create powerful coupon rules with advanced conditions, discount types, and usage restrictions for WooCommerce.', 'cartflows' ),
1019 'isPro' => false,
1020 'status' => $this->get_plugin_status( 'power-coupons/power-coupons.php' ),
1021 'slug' => 'power-coupons',
1022 'path' => 'power-coupons/power-coupons.php',
1023 'redirection' => admin_url( 'admin.php?page=power_coupons_settings' ),
1024 'logoPath' => array(
1025 'icon_path' => CARTFLOWS_ADMIN_CORE_URL . 'assets/images/plugins/power-coupons.svg',
1026 ),
1027 ),
1028 array(
1029 'title' => __( 'Modern Cart for WooCommerce', 'cartflows' ),
1030 'subtitle' => __( 'A fast, customizable cart built to boost conversions, maximise profits, and elevate the shopping experience.', 'cartflows' ),
1031 'isPro' => false,
1032 'status' => $this->get_plugin_status( 'modern-cart/modern-cart.php' ),
1033 'slug' => 'modern-cart',
1034 'path' => 'modern-cart/modern-cart.php',
1035 'redirection' => admin_url( 'admin.php?page=moderncart_settings' ),
1036 'logoPath' => array(
1037 'icon_path' => CARTFLOWS_ADMIN_CORE_URL . 'assets/images/plugins/modern-cart-for-woocommerce.svg',
1038 ),
1039 'hot_badge' => true,
1040 ),
1041 array(
1042 'title' => __( 'Cart Abandonment Recovery', 'cartflows' ),
1043 'subtitle' => __( 'Recover abandonded carts with ease in less than 10 minutes.', 'cartflows' ),
1044 'isPro' => false,
1045 'status' => $this->get_plugin_status( 'woo-cart-abandonment-recovery/woo-cart-abandonment-recovery.php' ),
1046 'slug' => 'woo-cart-abandonment-recovery',
1047 'path' => 'woo-cart-abandonment-recovery/woo-cart-abandonment-recovery.php',
1048 'redirection' => admin_url( 'admin.php?page=woo-cart-abandonment-recovery' ),
1049 'logoPath' => array(
1050 'icon_path' => CARTFLOWS_ADMIN_CORE_URL . 'assets/images/plugins/wcar.svg',
1051 ),
1052 'hot_badge' => true,
1053 ),
1054 array(
1055 'title' => __( 'Variation Swatches for WooCommerce', 'cartflows' ),
1056 'subtitle' => __( 'Convert dropdown boxes into highly engaging variation swatches.', 'cartflows' ),
1057 'isPro' => false,
1058 'status' => $this->get_plugin_status( 'variation-swatches-woo/variation-swatches-woo.php' ),
1059 'slug' => 'variation-swatches-woo',
1060 'path' => 'variation-swatches-woo/variation-swatches-woo.php',
1061 'redirection' => admin_url( 'admin.php?page=cfvsw_settings' ),
1062 'logoPath' => array(
1063 'icon_path' => CARTFLOWS_ADMIN_CORE_URL . 'assets/images/plugins/variation-swatches-woo.svg',
1064 ),
1065 'hot_badge' => true,
1066 ),
1067 array(
1068 'title' => __( 'WooPayments: Integrated WooCommerce Payments', 'cartflows' ),
1069 'subtitle' => __( 'Payments made simple, with no monthly fees – designed exclusively for WooCommerce stores.', 'cartflows' ),
1070 'isPro' => false,
1071 'status' => $this->get_plugin_status( 'woocommerce-payments/woocommerce-payments.php' ),
1072 'redirection' => admin_url( 'admin.php?page=wc-admin&path=/payments/connect' ),
1073 'slug' => 'woocommerce-payments',
1074 'path' => 'woocommerce-payments/woocommerce-payments.php',
1075 'logoPath' => array(
1076 'icon_path' => CARTFLOWS_ADMIN_CORE_URL . 'assets/images/plugins/woocommere-payments.png',
1077 ),
1078 ),
1079 array(
1080 'title' => __( 'OttoKit', 'cartflows' ),
1081 'subtitle' => __( 'OttoKit helps people automate their work by integrating multiple apps and plugins, allowing them to share data and perform tasks automatically.', 'cartflows' ),
1082 'isPro' => true,
1083 'status' => $this->get_plugin_status( 'suretriggers/suretriggers.php' ),
1084 'slug' => 'suretriggers',
1085 'path' => 'suretriggers/suretriggers.php',
1086 'link' => 'https://ottokit.com/pricing/',
1087 'redirection' => admin_url( 'admin.php?page=suretriggers' ),
1088 'logoPath' => array(
1089 'icon_path' => CARTFLOWS_ADMIN_CORE_URL . 'assets/images/plugins/suretriggers-emblem.svg',
1090 ),
1091 ),
1092 array(
1093 'title' => __( 'Ultimate addons for Elementor', 'cartflows' ),
1094 'subtitle' => __( 'Powerful Elementor addon with InfoCard, Fancy Heading, Before/After Slider, Price Box, FAQ Schema, WooCommerce widgets & Header-Footer builder.', 'cartflows' ),
1095 'isPro' => false,
1096 'status' => $this->get_plugin_status( 'header-footer-elementor/header-footer-elementor.php' ),
1097 'slug' => 'header-footer-elementor',
1098 'path' => 'header-footer-elementor/header-footer-elementor.php',
1099 'redirection' => admin_url( 'admin.php?page=hfe#dashboard' ),
1100 'logoPath' => array(
1101 'icon_path' => CARTFLOWS_ADMIN_CORE_URL . 'assets/images/plugins/uael.png',
1102 ),
1103 ),
1104 array(
1105 'title' => __( 'Spectra', 'cartflows' ),
1106 'subtitle' => __( 'Power-up the Gutenberg editor with advanced and powerful blocks.', 'cartflows' ),
1107 'isPro' => false,
1108 'status' => $this->get_plugin_status( 'ultimate-addons-for-gutenberg/ultimate-addons-for-gutenberg.php' ),
1109 'slug' => 'ultimate-addons-for-gutenberg',
1110 'path' => 'ultimate-addons-for-gutenberg/ultimate-addons-for-gutenberg.php',
1111 'redirection' => admin_url( 'options-general.php?page=spectra' ),
1112 'logoPath' => array(
1113 'icon_path' => CARTFLOWS_ADMIN_CORE_URL . 'assets/images/plugins/spectra.svg',
1114 ),
1115 ),
1116 array(
1117 'title' => __( 'SureForms', 'cartflows' ),
1118 'subtitle' => __( 'Transform your WordPress form-building experience with stunning designs, ai integration, and no-code flexibility.', 'cartflows' ),
1119 'isPro' => false,
1120 'status' => $this->get_plugin_status( 'sureforms/sureforms.php' ),
1121 'slug' => 'sureforms',
1122 'path' => 'sureforms/sureforms.php',
1123 'redirection' => admin_url( 'admin.php?page=sureforms_menu' ),
1124 'logoPath' => array(
1125 'icon_path' => CARTFLOWS_ADMIN_CORE_URL . 'assets/images/plugins/sureforms.svg',
1126 ),
1127 ),
1128 array(
1129 'title' => __( 'SureMembers', 'cartflows' ),
1130 'subtitle' => __( 'A simple yet powerful way to add content restriction to your website.', 'cartflows' ),
1131 'isPro' => true,
1132 'status' => $this->get_plugin_status( 'suremembers/suremembers.php' ),
1133 'slug' => 'suremembers',
1134 'path' => 'suremembers/suremembers.php',
1135 'link' => 'https://suremembers.com/pricing/',
1136 'redirection' => admin_url( 'edit.php?post_type=wsm_access_group' ),
1137 'logoPath' => array(
1138 'icon_path' => CARTFLOWS_ADMIN_CORE_URL . 'assets/images/plugins/suremembers.svg',
1139 ),
1140 ),
1141 )
1142 ),
1143 'themes' => apply_filters(
1144 'cartflows_admin_integrated_themes',
1145 array(
1146 array(
1147 'title' => __( 'Astra', 'cartflows' ),
1148 'subtitle' => __( 'Astra is fast, fully customizable & beautiful WordPress theme suitable for blog, personal portfolio, business website and WooCommerce storefront.', 'cartflows' ),
1149 'isPro' => false,
1150 'status' => $this->get_themes_status( 'Astra' ),
1151 'slug' => 'astra',
1152 'redirection' => admin_url( 'admin.php?page=wc-admin' ),
1153 'logoPath' => array(
1154 'icon_path' => CARTFLOWS_ADMIN_CORE_URL . 'assets/images/themes/astra-icon.svg',
1155 ),
1156 ),
1157 array(
1158 'title' => __( 'Spectra One', 'cartflows' ),
1159 'subtitle' => __( 'Spectra One is a beautiful and modern WordPress theme built with the Full Site Editing (FSE) feature. It\'s a versatile theme that can be used for blogs, portfolios, businesses, and more.', 'cartflows' ),
1160 'isPro' => false,
1161 'status' => $this->get_themes_status( 'Spectra One' ),
1162 'slug' => 'spectra-one',
1163 'redirection' => admin_url( 'admin.php?page=wc-admin' ),
1164 'logoPath' => array(
1165 'icon_path' => CARTFLOWS_ADMIN_CORE_URL . 'assets/images/themes/spectra-one.svg',
1166 ),
1167 ),
1168 )
1169 ),
1170 );
1171 }
1172
1173 /**
1174 * Prepare the array of RSS Feeds of CartFlows for Whats New slide-our pannel.
1175 *
1176 * @since 2.1.6
1177 * @return array The prepared array of RSS feeds.
1178 */
1179 public function get_whats_new_rss_feeds_data() {
1180 return array(
1181 array(
1182 'key' => 'cartflows',
1183 'label' => 'CartFlows',
1184 'url' => add_query_arg(
1185 array(
1186 'action' => 'cartflows_fetch_whats_new_data',
1187 'nonce' => wp_create_nonce( 'cartflows_fetch_whats_new_data' ),
1188 ),
1189 admin_url( 'admin-ajax.php' )
1190 ), // 'https://cartflows.com/whats-new/feed/'
1191 ),
1192 );
1193 }
1194
1195 /**
1196 * Fetch the Whats New RSS feed from the URL.
1197 *
1198 * @since 1.0.3
1199 * @return void
1200 */
1201 public function fetch_whats_new_data() {
1202 if ( ! isset( $_GET['nonce'] ) || ! wp_verify_nonce( sanitize_text_field( $_GET['nonce'] ), 'cartflows_fetch_whats_new_data' ) ) {
1203 // Verify the nonce, if it fails, return an error.
1204 wp_send_json_error( array( 'message' => __( 'Nonce verification failed.', 'cartflows' ) ) );
1205 }
1206
1207 // Security: Require admin capability to access RSS feed proxy.
1208 if ( ! current_user_can( 'cartflows_manage_flows_steps' ) ) {
1209 wp_send_json_error( array( 'message' => __( 'Permission denied.', 'cartflows' ) ) );
1210 }
1211
1212 // Fetch the RSS feed from the URL. This saves us from the CORS issue.
1213 $feed = wp_remote_retrieve_body( wp_safe_remote_get( 'https://cartflows.com/product/cartflows/feed/' ) ); // phpcs:ignore -- This is a valid use case cannot use VIP rules here.
1214
1215 /*
1216 * This is an RSS document, parsed as XML by the What's New client. It must not be
1217 * run through wp_kses_post() — that strips the feed's own elements and leaves the
1218 * client nothing to parse. Declaring the type is what stops a browser sniffing the
1219 * response as HTML; the body itself is fetched over HTTPS from our own domain and
1220 * this endpoint is behind a nonce and a capability check.
1221 */
1222 header( 'Content-Type: application/rss+xml; charset=' . get_option( 'blog_charset' ) );
1223 header( 'X-Content-Type-Options: nosniff' );
1224
1225 echo $feed; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- RSS/XML body, see above.
1226 exit;
1227 }
1228 }
1229
1230 AdminMenu::get_instance();
1231