PluginProbe
WPFunnels – Funnel Builder for WooCommerce with Checkout & One Click Upsell / 3.13.3
WPFunnels – Funnel Builder for WooCommerce with Checkout & One Click Upsell v3.13.3
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
← All changes | admin/class-wpfnl-admin.php +1816 -545 2.8.16 → 3.13.3 View file →
@@ -1,17 +1,21 @@
1 1 <?php
2 +
2 3 /**
3 4 * Admin class
4 - *
5 + *
5 6 * @package
6 7 */
8 +
7 9 namespace WPFunnels\Admin;
8 10
11 +use WPFunnels\Admin\BatchProcessing\BatchProcessingController;
12 +use WPFunnels\Admin\Migrations\MigrationManager;
9 13 use WPFunnels\Wpfnl;
10 14 use WPFunnels\Wpfnl_functions;
11 -use Wpfnl_Pro_GB_Functions;
12 -use WPFunnels\Frontend\Wpfnl_Frontend;
15 +use MRM\Common\MrmCommon;
13 16
17 +
14 18 /**
15 19 * The admin-specific functionality of the plugin.
16 20 *
17 21 * @link https://rextheme.com
@@ -38,8 +42,23 @@
38 42 */
39 43 const WPFNL_FEED_OPTION_KEY = 'wpfnl_remote_info_feed_data';
40 44
41 45 /**
46 + * Plugin file of the free Mail Mint plugin, as wordpress.org ships it.
47 + */
48 + const MAIL_MINT_BASENAME = 'mail-mint/mail-mint.php';
49 +
50 + /**
51 + * Plugin file of Mail Mint Pro.
52 + */
53 + const MAIL_MINT_PRO_BASENAME = 'mail-mint-pro/mail-mint-pro.php';
54 +
55 + /**
56 + * Plugin file of WPFunnels Pro.
57 + */
58 + const WPFNL_PRO_BASENAME = 'wpfunnels-pro/wpfnl-pro.php';
59 +
60 + /**
42 61 * The ID of this plugin.
43 62 *
44 63 * @since 1.0.0
45 64 * @access private
@@ -60,20 +79,32 @@
60 79 private $dependency_plugins;
61 80
62 81
63 82 /**
64 - * Page hooks
83 + * Screens still rendered by the legacy PHP/Vue admin, which is what the
84 + * legacy stylesheet and bundle are written against.
65 85 *
86 + * NOTE: 'toplevel_page_wpfunnels' is deliberately absent. That screen is the
87 + * React admin, which ships its own namespaced bundle and must not inherit
88 + * the legacy admin CSS. The funnel list, its trash tab and the Templates
89 + * page are routes of that same app now, so their hooks are gone from here
90 + * too.
91 + *
66 92 * @var array
67 93 */
68 94 private $page_hooks = [
69 - 'toplevel_page_wp_funnels',
70 - 'wp-funnels_page_wp_funnel_settings',
71 - 'wp-funnels_page_edit_funnel',
72 - 'wp-funnels_page_create_funnel',
73 - 'wp-funnels_page_wpfnl_settings',
74 - 'wp-funnels_page_wpf-license',
75 - 'wp-funnels_page_email-builder',
95 + 'wpfunnels_page_wpf_feature_comparison',
96 + 'wpfunnels_page_wp_funnel_settings',
97 + 'wpfunnels_page_wpfnl_settings',
98 + 'wpfunnels_page_wp_global_store',
99 + 'wpfunnels_page_edit_funnel',
100 + 'wpfunnels_page_create_funnel',
101 + 'wpfunnels_page_wpfnl_automations',
102 + 'wpfunnels_page_wpfnl_addons',
103 + 'wpfunnels_page_store_checkout',
104 + 'wpfunnels_page_wpfunnels_integrations',
105 + 'wpfunnels_page_wpf-license',
106 + 'wpfunnels_page_email-builder',
76 107 ];
77 108
78 109 /**
79 110 * Initialize the class and set its properties.
@@ -79,9 +110,9 @@
79 110 * Initialize the class and set its properties.
80 111 *
81 112 * @param string $plugin_name The name of this plugin.
82 113 * @param string $version The version of this plugin.
83 - *
114 + *
84 115 * @since 1.0.0
85 116 */
86 117 public function __construct($plugin_name, $version)
87 118 {
@@ -86,31 +117,77 @@
86 117 public function __construct($plugin_name, $version)
87 118 {
88 119 $this->plugin_name = $plugin_name;
89 120 $this->version = $version;
90 -
91 - add_action( 'admin_enqueue_scripts', [$this, 'enqueue_styles'] );
92 - add_action( 'admin_enqueue_scripts', [$this, 'enqueue_scripts'] );
93 - add_filter( 'plugin_action_links_' . WPFNL_BASE, [$this, 'add_funnel_action_links'] );
94 - add_filter( 'locale', array($this, 'change_local_for_funnel_canvas') );
95 - if( is_admin() ) {
96 - add_filter( 'wp_dropdown_pages', array( $this, 'show_funnel_steps_on_reading' ) );
97 - add_filter( 'gettext', [$this,'wpfnl_customizing_checkout_text'], 10, 3 );
98 - add_action( 'admin_init', [$this,'wpfnl_add_type_meta'] );
99 - add_action( 'deactivated_plugin', [$this, 'wpfnl_deactivated_plugin'] );
100 - add_action( 'activated_plugin', [$this, 'wpfnl_activated_plugin'] );
101 121
122 + add_action('admin_enqueue_scripts', [$this, 'enqueue_styles']);
123 + add_action('admin_enqueue_scripts', [$this, 'enqueue_scripts']);
124 + add_filter('plugin_action_links_' . WPFNL_BASE, [$this, 'add_funnel_action_links']);
125 + add_filter('locale', array($this, 'change_local_for_funnel_canvas'));
126 + if (is_admin()) {
127 + add_filter('admin_body_class', [$this, 'add_wpfunnels_admin_body_class']);
128 + add_filter('wp_dropdown_pages', array($this, 'show_funnel_steps_on_reading'));
129 + add_filter('gettext', [$this, 'wpfnl_customizing_checkout_text'], 10, 3);
130 + add_action('admin_init', [$this, 'wpfnl_add_type_meta']);
131 + add_action('admin_init', array($this, 'init_actions'));
132 + add_action('deactivated_plugin', [$this, 'wpfnl_deactivated_plugin']);
133 + add_action('activated_plugin', [$this, 'wpfnl_activated_plugin']);
134 +
102 135 // Register Dashboard Widgets.
103 - add_action( 'wp_dashboard_setup', [ $this, 'wpfnl_register_dashboard_widgets' ] );
104 -
136 + add_action('wp_dashboard_setup', [$this, 'wpfnl_register_dashboard_widgets']);
137 +
138 + if (isset($_GET['page']) && 'edit_funnel' === $_GET['page']) {
139 + wp_cache_flush();
140 + }
105 141 }
106 142
107 - add_action( 'admin_head', function() {
108 - remove_submenu_page( WPFNL_MAIN_PAGE_SLUG, 'email-builder' );
109 - } );
110 - }
111 -
143 + add_action('admin_head', function () {
144 + remove_submenu_page(WPFNL_MAIN_PAGE_SLUG, 'email-builder');
145 + });
112 146
147 +
148 + // Remove all admin notices from WPFunnels Dashboard
149 + if ($this->is_current_page('wpfunnels')) {
150 + remove_all_actions('admin_notices');
151 + }
152 +
153 +
154 + /**
155 + * @todo This piece of code will be removed after releasing 3.2.0. We need to introduced this because we have changed the funnel template creation strategy.
156 + *
157 + */
158 + add_action('admin_init', function () {
159 + $current_version = get_option('wpfunnels_version');
160 + $is_already_synced = get_option('_is_wpfunnels_new_templates_synced', 'no');
161 + if (version_compare($current_version, '3.1.2', '>') && 'yes' != $is_already_synced) {
162 + delete_option(WPFNL_TEMPLATES_OPTION_KEY . '_wc');
163 + delete_option(WPFNL_TEMPLATES_OPTION_KEY . '_lms');
164 + delete_transient('wpfunnels_remote_template_data_wc_' . WPFNL_VERSION);
165 + delete_transient('wpfunnels_remote_template_data_lms_' . WPFNL_VERSION);
166 + delete_transient('wpfunnels_remote_template_data_lead_' . WPFNL_VERSION);
167 + delete_transient('wpfunnels_remote_template_data_store_checkout_' . WPFNL_VERSION);
168 + update_option('_is_wpfunnels_new_templates_synced', 'yes');
169 + }
170 + });
171 +
172 + add_action('admin_init', array($this, 'attach_admin_capabilities'));
173 +
174 + add_action('wpfunnels_tracker_optin', array($this, 'wpfunnels_tracker_optin'), 10);
175 + add_action('mint_before_sending_sms', array($this, 'create_mrm_contact'), 10);
176 + }
177 +
178 +
179 + public function init_actions()
180 + {
181 + $batch_processor = new BatchProcessingController();
182 + new MigrationManager($batch_processor);
183 +
184 +
185 + // Run stat status sync migration directly (independent of Action Scheduler).
186 + $stat_sync = new \WPFunnels\Admin\Migrations\StatStatusSyncMigration();
187 + $stat_sync->run();
188 + }
189 +
113 190 /**
114 191 * Register dashboard widgets.
115 192 *
116 193 * Adds a new WPFunnels widgets to WordPress dashboard.
@@ -118,10 +195,11 @@
118 195 * Fired by `wp_dashboard_setup` action.
119 196 *
120 197 * @since 2.6.2
121 198 */
122 - public function wpfnl_register_dashboard_widgets() {
123 - wp_add_dashboard_widget( 'wpfnl-dashboard-overview', esc_html__( 'WPFunnels Overview', 'wpfnl' ), [ $this, 'wpfnl_dashboard_overview_widget' ] );
199 + public function wpfnl_register_dashboard_widgets()
200 + {
201 + wp_add_dashboard_widget('wpfnl-dashboard-overview', esc_html__('WPFunnels Overview', 'wpfnl'), [$this, 'wpfnl_dashboard_overview_widget']);
124 202 global $wp_meta_boxes;
125 203
126 204 $dashboard = $wp_meta_boxes['dashboard']['normal']['core'];
127 205 $ours = [
@@ -127,25 +205,39 @@
127 205 $ours = [
128 206 'wpfnl-dashboard-overview' => $dashboard['wpfnl-dashboard-overview'],
129 207 ];
130 208
131 - $wp_meta_boxes['dashboard']['normal']['core'] = array_merge( $ours, $dashboard );
209 + $wp_meta_boxes['dashboard']['normal']['core'] = array_merge($ours, $dashboard);
132 210 }
133 211
134 212
135 213 /**
136 - * Ppfunnels dashboard widget.
214 + * WPFunnels dashboard widget.
137 215 *
138 216 * Displays the wpfunnels dashboard widget.
139 - *
217 + *
140 218 * @since 2.6.2
141 219 */
142 - public function wpfnl_dashboard_overview_widget() {
143 - $create_new_label = __( 'Create New Funnel', 'wpfnl' );
220 + public function wpfnl_dashboard_overview_widget()
221 + {
222 + $upgrade_to_pro = __('Upgrade to Pro', 'wpfnl');
223 + $install_activate = __('Install & Activate', 'wpfnl');
224 + $is_mail_mint_active = WPFnl_functions::is_mail_mint_free_active();
144 225
145 226 $response = $this->get_wpfnl_feed_data();
146 -
147 - ?>
227 +
228 + $icons = [
229 + 'Total Orders' => '<svg width="16" height="17" fill="none" viewBox="0 0 16 17" xmlns="http://www.w3.org/2000/svg"><path fill="#B7A6E9" d="M12.061 8.04a4.015 4.015 0 01-3.478-6.03h-5.74l-.029-.236A2.01 2.01 0 00.82 0H.67a.67.67 0 000 1.34h.15a.67.67 0 01.665.592l.922 7.84a3.35 3.35 0 003.328 2.96h6.997a.67.67 0 000-1.341H5.734a2.01 2.01 0 01-1.89-1.34h7.987a3.35 3.35 0 003.298-2.756l.16-.884a4.01 4.01 0 01-3.228 1.63z"/><path fill="#B7A6E9" d="M10.542 5.994a1.254 1.254 0 00.901.402h.023a1.255 1.255 0 00.894-.37l2.863-2.863a.672.672 0 10-.95-.952l-2.806 2.81-.827-.887a.67.67 0 00-.98.915l.882.945zM4.69 16.082a1.34 1.34 0 100-2.68 1.34 1.34 0 000 2.68zm6.701 0a1.34 1.34 0 100-2.68 1.34 1.34 0 000 2.68z"/></svg>',
230 + 'Total Revenue' => '<svg width="15" height="15" fill="none" viewBox="0 0 15 15" xmlns="http://www.w3.org/2000/svg"><path fill="#B7A6E9" d="M9.75 15h-4.5C1.5 15 0 13.5 0 9.75v-4.5C0 1.5 1.5 0 5.25 0h4.5C13.5 0 15 1.5 15 5.25v4.5C15 13.5 13.5 15 9.75 15z"/><path fill="#B7A6E9" d="M5.004 9.247c0 .968.742 1.748 1.665 1.748H8.55c.803 0 1.455-.683 1.455-1.523 0-.915-.397-1.237-.99-1.447l-3.022-1.05c-.593-.21-.99-.533-.99-1.448 0-.84.652-1.522 1.455-1.522H8.34c.923 0 1.665.78 1.665 1.747"/><path stroke="#fff" stroke-linecap="round" stroke-linejoin="round" d="M5.004 9.247c0 .968.742 1.748 1.665 1.748H8.55c.803 0 1.455-.683 1.455-1.523 0-.915-.397-1.237-.99-1.447l-3.022-1.05c-.593-.21-.99-.533-.99-1.448 0-.84.652-1.522 1.455-1.522H8.34c.923 0 1.665.78 1.665 1.747"/><path fill="#B7A6E9" d="M7.5 3v9-9z"/><path stroke="#fff" stroke-linecap="round" stroke-linejoin="round" d="M7.5 3v9"/></svg>',
231 + 'Order Bump Revenue' => '<svg width="15" height="15" fill="none" viewBox="0 0 15 15" xmlns="http://www.w3.org/2000/svg"><path stroke="#B7A6E9" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="10" stroke-width="1.5" d="M5.376 1L3.05 3.333M9.475 1l2.327 2.333"/><path fill="#fff" stroke="#B7A6E9" stroke-width="1.5" d="M1 4.76c0-1.19.636-1.286 1.427-1.286h10c.791 0 1.428.097 1.428 1.286 0 1.382-.637 1.285-1.427 1.285H2.427C1.637 6.045 1 6.142 1 4.76z"/><path fill="#B7A6E9" d="M3.273 13.064L1.79 5.944h10.975l-.89 6.526c0 .712-1.582 1.286-2.373 1.484H5.35c-.237.237-1.483-.495-2.076-.89z"/><path stroke="#B7A6E9" stroke-linecap="round" stroke-width="1.5" d="M1.964 6.142l.906 5.553c.206 1.247.7 2.16 2.54 2.16h3.875c1.999 0 2.294-.874 2.526-2.082l1.08-5.63"/><path stroke="#fff" stroke-linecap="round" stroke-linejoin="round" d="M8.91 9.504L7.425 8.021 5.943 9.504M7.426 8.02v3.56"/></svg>',
232 + 'Upsell/Downsell Revenue' => '<svg width="15" height="15" fill="none" viewBox="0 0 15 15" xmlns="http://www.w3.org/2000/svg"><path fill="#B7A6E9" d="M5.25 15h4.5C13.5 15 15 13.5 15 9.75v-4.5C15 1.5 13.5 0 9.75 0h-4.5C1.5 0 0 1.5 0 5.25v4.5C0 13.5 1.5 15 5.25 15z"/><path fill="#B7A6E9" d="M6.379 5.25h3.37v3.371"/><path stroke="#fff" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5" d="M6.379 5.25h3.37v3.371M9.75 5.25l-4.5 4.5"/></svg>',
233 + 'Total Leads' => '<svg width="13" height="13" fill="none" viewBox="0 0 13 13" xmlns="http://www.w3.org/2000/svg"><path fill="#B7A6E9" d="M8.45 0h-3.9C1.3 0 0 1.3 0 4.55v3.9c0 2.457.741 3.803 2.509 4.303.143-1.69 1.879-3.023 3.991-3.023 2.113 0 3.848 1.333 3.991 3.023C12.259 12.253 13 10.907 13 8.45v-3.9C13 1.3 11.7 0 8.45 0zM6.5 7.91a2.33 2.33 0 010-4.66 2.33 2.33 0 010 4.66z"/></svg>',
234 + ];
235 +
236 +
237 + $pro_icon_svg = '<div class="pro-icon"><svg width="14" height="14" fill="none" viewBox="0 0 14 14" xmlns="http://www.w3.org/2000/svg"><circle cx="7" cy="7" r="7" fill="#66CB64"/><path fill="#fff" fill-rule="evenodd" d="M7.623 4.465a.69.69 0 00-1.15 0l-1.36 2.08-1.04-.59c-.52-.294-1.146.168-1.005.743l.706 2.88c.032.13.15.222.285.222h5.977a.292.292 0 00.285-.221l.706-2.88c.141-.576-.485-1.038-1.005-.743l-1.04.588-1.36-2.08zm-.493.312a.098.098 0 00-.165 0l-1.51 2.311a.296.296 0 01-.393.094l-1.28-.725a.097.097 0 00-.144.106l.652 2.659h5.515l.652-2.66a.096.096 0 00-.143-.105l-1.281.725a.296.296 0 01-.393-.094l-1.51-2.31z" clip-rule="evenodd"/><path fill="#fff" d="M4.312 10.377a.291.291 0 00-.293.29.29.29 0 00.293.288h5.472c.161 0 .293-.13.293-.289a.291.291 0 00-.293-.289H4.312z"/><circle cx="10.739" cy="4.987" r=".514" fill="#fff"/><circle cx="3.139" cy="4.987" r=".514" fill="#fff"/><circle cx="7.042" cy="3.139" r=".514" fill="#fff"/><path fill="#fff" d="M9.918 9.404h-5.65L3.241 6.22l1.952.616 1.849-2.465L8.993 6.94l1.746-.822-.821 3.287z"/></svg></div>';
238 +
239 +?>
148 240 <div class="wpfnl-dashboard-widget">
149 241 <div class="wpfnl-overview__header">
150 242 <div class="wpfnl-overview__logo">
151 243 <div class="wpfnl-logo-wrapper">
@@ -150,88 +242,349 @@
150 242 <div class="wpfnl-overview__logo">
151 243 <div class="wpfnl-logo-wrapper">
152 244 <svg width="38" height="28" viewBox="0 0 38 28" fill="none" xmlns="http://www.w3.org/2000/svg">
153 245 <path d="M7.01532 18H31.9847L34 11H5L7.01532 18Z" fill="#EE8134"></path>
154 - <path d="M11.9621 27.2975C12.0923 27.7154 12.4792 28 12.9169 28H26.0831C26.5208 28 26.9077 27.7154 27.0379 27.2975L29 21H10L11.9621 27.2975Z" fill="#6E42D3"></path>
155 - <path d="M37.8161 0.65986C37.61 0.247888 37.2609 0 36.8867 0H1.11326C0.739128 0 0.390003 0.247888 0.183972 0.65986C-0.0220592 1.07193 -0.0573873 1.59277 0.0898627 2.04655L1.69781 7H36.3022L37.9102 2.04655C38.0574 1.59287 38.022 1.07193 37.8161 0.65986Z" fill="#6E42D3"></path>
246 + <path
247 + d="M11.9621 27.2975C12.0923 27.7154 12.4792 28 12.9169 28H26.0831C26.5208 28 26.9077 27.7154 27.0379 27.2975L29 21H10L11.9621 27.2975Z"
248 + fill="#6E42D3"></path>
249 + <path
250 + d="M37.8161 0.65986C37.61 0.247888 37.2609 0 36.8867 0H1.11326C0.739128 0 0.390003 0.247888 0.183972 0.65986C-0.0220592 1.07193 -0.0573873 1.59277 0.0898627 2.04655L1.69781 7H36.3022L37.9102 2.04655C38.0574 1.59287 38.022 1.07193 37.8161 0.65986Z"
251 + fill="#6E42D3"></path>
156 252 </svg>
157 253 </div>
158 254 </div>
159 255
160 - <div class="wpfnl-overview__versions">
161 - <span class="wpfnl-overview__version"><?php echo __( 'WPFunnels', 'wpfnl' ); ?> v<?php echo WPFNL_VERSION;?></span>
162 - <?php if( Wpfnl_functions::is_pro_license_activated() ) { ?>
163 - <span class="wpfnl-overview__version"><?php echo __( 'WPFunnels Pro', 'wpfnl' ); ?> v<?php echo WPFNL_PRO_VERSION;?></span>
164 - <?php } ?>
256 + <div class="wpfnl-overview__upgrade">
257 + <span><?php echo !Wpfnl_functions::is_pro_license_activated() ? __('Upgrade to Pro to get premium features such as one-click upsells, A/B testing and more!', 'wpfnl') : __('Start creating high converting landing pages, sales funnels, and checkout flows in minutes!', 'wpfnl'); ?></span>
165 258 </div>
166 259
167 - <div class="wpfnl-overview__create">
168 - <a href="<?php echo admin_url('admin.php?page=wp_funnels') ?>" target="_blank" class="button"><span aria-hidden="true" class="dashicons dashicons-plus"></span> <?php echo esc_html( $create_new_label ); ?></a>
169 - </div>
170 -
260 + <?php if (!Wpfnl_functions::is_pro_license_activated()) { ?>
261 + <div class="wpfnl-overview__create">
262 + <a href="https://getwpfunnels.com/pricing/" target="_blank" class="button"><span aria-hidden="true"></span>
263 + <?php echo esc_html($upgrade_to_pro); ?></a>
264 + </div>
265 + <?php
266 + }?>
267 +
171 268 </div>
172 - <?php if ( !is_wp_error( $response ) && !empty( $response ) ) : ?>
173 - <div class="wpfnl-overview__recently-edited">
174 - <h3 class="wpfnl-heading wpfnl-divider_bottom"><?php echo __( 'News & Updates', 'wpfnl' ); ?></h3>
175 - <ul class="wpfnl-overview__posts">
269 +
270 + <!-- overview report -->
271 + <div class="wpfnl-overview__report">
272 + <?php foreach ($response as $report):
273 + $icon_html = $icons[$report['title']] ?? '';
274 +?>
275 + <div class="report-item">
276 + <div class="icon"><?php echo $icon_html; ?></div>
277 + <div class="text-wrapper">
278 + <p class="title"><?php echo esc_html($report['title']); ?></p>
279 + <p class="amount"><?php echo $report['amount']; ?></p>
280 + </div>
281 +
282 + <?php if ($report['show_pro'] && !Wpfnl_functions::is_pro_license_activated()): ?>
283 + <?php echo $pro_icon_svg; ?>
176 284 <?php
177 - foreach ( $response as $data ) :
178 - ?>
179 - <li class="wpfnl-overview__post">
180 - <a href="<?php echo esc_url( $data['link'] ); ?>" class="wpfnl-overview__post-link" target="_blank">
181 - <?php echo esc_html($data['title']['rendered']); ?>
182 - </a>
183 - <p class="wpfnl-overview__post-description">
184 - <?php
185 - $text = explode(' ',strip_tags($data['content']['rendered']) );
186 - if (sizeof($text)>100) $text = array_slice($text,0,70);
187 - echo implode(' ',$text).'.....';
188 - ?>
189 - </p>
190 - </li>
191 - <?php endforeach; ?>
192 - </ul>
285 + endif; ?>
286 +
287 + </div>
288 + <?php
289 + endforeach; ?>
290 + </div>
291 +
292 + <?php if (!Wpfnl_functions::is_mint_mrm_active()): ?>
293 + <!-- install mail mint -->
294 + <div
295 + class="wpfnl-overview__mailmint <?php echo Wpfnl_functions::is_mint_mrm_active() ? 'is-active' : 'is-disabled'; ?>">
296 + <div class="icon-wrapper">
297 + <div class="icon">
298 + <svg width="34" height="34" fill="none" viewBox="0 0 34 34" xmlns="http://www.w3.org/2000/svg">
299 + <circle cx="17" cy="17" r="16.5" fill="#fff" stroke="#573BFF" />
300 + <path fill="#573BFF"
301 + d="M24.084 14.876l-.634 8.778.905-1.267.996-10.225-9.23 6.153-2.082-2.172-5.972-5.067 7.601 8.868c.272.271.634.362.996.09l7.42-5.158z" />
302 + <path fill="#02C4FB"
303 + d="M21.822 11.62c-3.891.814-7.24.904-8.416.814l.995 1.086c4.525-.272 6.697-.905 7.24-1.267.362-.272.271-.543.18-.634z" />
304 + <path fill="#573BFF"
305 + d="M20.374 13.61c-2.444.724-4.525.905-5.249.905l.724.633c2.805-.362 4.163-.905 4.434-1.176.272-.181.181-.362.09-.362z" />
306 + <path fill="#573BFF"
307 + d="M25.947 11.345c-.212-.033-.418.064-.596.184l-9.05 6.153-4.162-4.525-.749-.793-.79-.835c1.72.18 8.778-.09 12.67-.996 1.81-.452 1.447-1.267 1.266-1.176-8.415 1.176-15.745.452-15.745.452-.403-.034-.617.066-.815.362-.18.272-.09.634.09.905l.453.453 2.082 2.08 5.158 5.43c.271.272.633.272.995.09l8.054-5.52c-.453 4.073-.996 8.145-1.267 9.14-1.81.363-12.397.453-13.845.091-.453-.724-.996-4.253-1.358-7.963-.09-.543-.633-.905-1.176-.634 0 0-.362.177-.362.724.905 8.597 1.81 9.05 2.081 9.23.543.272 4.163.453 7.873.453s7.33-.181 7.873-.634c.18-.18.814-.723 1.9-11.401v-.091c.074-.736-.152-1.113-.58-1.18z" />
308 + <path fill="url(#paint0_linear_129_34)" d="M8.474 11.538h2.135l1.687 1.79H9.984l-1.51-1.79z" />
309 + <defs>
310 + <linearGradient id="paint0_linear_129_34" x1="9.613" x2="10.654" y1="11.538" y2="13.37"
311 + gradientUnits="userSpaceOnUse">
312 + <stop stop-color="#0C0632" />
313 + <stop offset="1" stop-color="#573BFF" />
314 + </linearGradient>
315 + </defs>
316 + </svg>
317 + </div>
318 + <div class="text-wrapper">
319 + <p class="description">
320 + <?php echo $is_mail_mint_active ? __('Use email marketing automation for your funnels easily with <b>Mail Mint</b>!', 'wpfnl') : __('Use email marketing automation for your funnels easily with <b>Mail Mint</b>!', 'wpfnl'); ?>
321 + </p>
322 + </div>
323 + </div>
324 + <div class="wpfnl-overview__install-mailmint">
325 + <?php if (Wpfnl_functions::is_mail_mint_free_active()): ?>
326 + <a href="https://getwpfunnels.com/pricing/#mail-mint" target="_blank" class="button">
327 + <?php echo $upgrade_to_pro; ?>
328 + </a>
329 + <?php
330 + else: ?>
331 + <button id="pro-modal-dropdown-btn" class="button">
332 + <span class="loader" style="display:none;"></span>
333 + <span class="btn-text"><?php echo $install_activate; ?></span>
334 + </button>
335 + <?php
336 + endif; ?>
337 + </div>
338 +
193 339 </div>
194 - <?php endif; ?>
195 -
196 -
340 + <?php
341 + endif; ?>
342 +
197 343 <div class="wpfnl-overview__footer wpfnl-divider_top">
198 344 <ul>
199 - <?php foreach ( $this->get_wpfunnels_dashboard_overview_widget_footer_actions() as $action_id => $action ) : ?>
200 - <li class="wpfnl-overview__<?php echo esc_attr( $action_id ); ?>"><a href="<?php echo esc_attr( $action['link'] ); ?>" target="_blank"><?php echo esc_html( $action['title'] ); ?> <span class="screen-reader-text"><?php echo esc_html__( '(opens in a new window)', 'wpfnl' ); ?></span><span aria-hidden="true" class="dashicons dashicons-external"></span></a></li>
201 - <?php endforeach; ?>
345 + <?php foreach ($this->get_wpfunnels_dashboard_overview_widget_footer_actions() as $action_id => $action): ?>
346 + <li class="wpfnl-overview__<?php echo esc_attr($action_id); ?>"><a
347 + href="<?php echo esc_attr($action['link']); ?>"
348 + target="_blank"><?php echo esc_html($action['title']); ?> <span
349 + class="screen-reader-text"><?php echo esc_html__('(opens in a new window)', 'wpfnl'); ?></span></a>
350 + </li>
351 + <?php
352 + endforeach; ?>
202 353 </ul>
203 354 </div>
204 355
356 + <script>
357 + (function ($) {
358 +
359 + const apiService = (path = "", method = "GET", data, content_type = "text/plain") => {
360 + return new Promise((resolve, reject) => {
361 + jQuery.ajax({
362 + url: wpApiSettings.root + path,
363 + type: method,
364 + data: data,
365 + beforeSend: function (xhr) {
366 + xhr.setRequestHeader("X-WP-Nonce", wpApiSettings.nonce);
367 + },
368 + dataType: "json",
369 + contentType: content_type,
370 + success: resolve,
371 + error: reject
372 + });
373 + });
374 + };
375 +
376 + $(document).ready(function () {
377 + jQuery("#pro-modal-dropdown-btn").click((e) => {
378 + e.preventDefault();
379 + var btn = $('#pro-modal-dropdown-btn');
380 + var loader = btn.find('.loader');
381 + var btnText = btn.find('.btn-text');
382 + loader.show();
383 + btn.prop('disabled', true);
384 + btnText.text('Processing...');
385 + apiService("wpfunnels/v1/activate-plugin", 'POST', JSON.stringify({
386 + basename: 'mail-mint/mail-mint.php',
387 + slug: 'mail-mint',
388 + }), 'application/json').then((res) => {
389 + var upgradeBtn = $(
390 + '<a href="https://getwpfunnels.com/pricing/#mail-mint" target="_blank" class="button" style="margin-left:0;">' +
391 + 'Upgrade to Pro' +
392 + '</a>'
393 + );
394 + btn.closest('.wpfnl-overview__install-mailmint').html(upgradeBtn);
395 + $('.text-wrapper .title').text('Get Mail Mint Pro');
396 + $('.text-wrapper .description').html('Use email marketing automation for your funnels easily with <b>Mail Mint</b>!');
397 + }).catch((e) => {
398 + btn.replaceWith(btnPrevState);
399 + addClickEvent();
400 + })
401 + });
402 + });
403 + })(jQuery);
404 + </script>
405 +
205 406 <style>
206 - .wpfnl-dashboard-widget .wpfnl-overview__header {
207 - display: table;
208 - width: 100%;
209 - -webkit-box-shadow: 0 5px 8px rgb(0 0 0 / 5%);
210 - box-shadow: 0 5px 8px rgb(0 0 0 / 5%);
211 - margin: 0 -12px 8px;
212 - padding: 0 12px 12px;
407 + .wpfnl-dashboard-widget {
408 + margin-top: -11px;
213 409 }
214 - .wpfnl-dashboard-widget .wpfnl-overview__logo {
215 - width: 30px;
410 +
411 + .wpfnl-dashboard-widget .wpfnl-overview__mailmint {
412 + display: flex;
413 + align-items: center;
414 + justify-content: space-between;
415 + padding: 16px 20px;
416 + border: 1px solid #EEEAF8;
417 + margin-bottom: 15px;
216 418 }
217 - .wpfnl-dashboard-widget .wpfnl-overview__logo,
218 - .wpfnl-dashboard-widget .wpfnl-overview__versions,
219 - .wpfnl-dashboard-widget .wpfnl-overview__create {
220 - display: table-cell;
419 +
420 + .wpfnl-dashboard-widget .wpfnl-overview__mailmint.is-active .wpfnl-overview__install-mailmint button {
421 + pointer-events: none;
422 + cursor: not-allowed;
423 + }
424 +
425 + .wpfnl-dashboard-widget .wpfnl-overview__mailmint .icon-wrapper {
426 + display: flex;
427 + gap: 12px;
428 + align-items: center;
429 + }
430 +
431 + .wpfnl-dashboard-widget .wpfnl-overview__mailmint .wpfnl-overview__install-mailmint .button {
432 + border-radius: 7px;
433 + background: #573BFF;
434 + color: #FFF;
435 + text-align: center;
436 + font-family: Inter;
437 + font-size: 12px;
438 + font-style: normal;
439 + font-weight: 500;
440 + line-height: 2.6;
441 + border: none;
442 + }
443 +
444 + .wpfnl-dashboard-widget .wpfnl-overview__mailmint .wpfnl-overview__install-mailmint .button:focus {
445 + box-shadow: unset;
446 + }
447 +
448 + .wpfnl-dashboard-widget .wpfnl-overview__mailmint .wpfnl-overview__install-mailmint .loader {
449 + border: 2px solid #f3f3f3;
450 + border-top: 2px solid #573BFF;
451 + border-radius: 50%;
452 + width: 12px;
453 + height: 12px;
454 + animation: spin 0.8s linear infinite;
455 + display: inline-block;
221 456 vertical-align: middle;
457 + margin-right: 4px;
222 458 }
459 +
460 + @keyframes spin {
461 + 0% {
462 + transform: rotate(0deg);
463 + }
464 +
465 + 100% {
466 + transform: rotate(360deg);
467 + }
468 + }
469 +
470 + .wpfnl-dashboard-widget .wpfnl-overview__mailmint .wpfnl-overview__install-mailmint .button:disabled {
471 + cursor: not-allowed;
472 + opacity: 0.7;
473 + background: #573BFF !important;
474 + color: #FFF !important;
475 + }
476 +
477 +
478 + .wpfnl-dashboard-widget .wpfnl-overview__mailmint .icon-wrapper .text-wrapper .title {
479 + color: #2E3148;
480 + font-size: 16px;
481 + font-weight: 600;
482 + line-height: 1;
483 + letter-spacing: -0.32px;
484 + margin: 0 0 8px;
485 + }
486 +
487 + .wpfnl-dashboard-widget .wpfnl-overview__mailmint .icon-wrapper .text-wrapper .description {
488 + margin: 0;
489 + line-height: 1;
490 + }
491 +
492 + .wpfnl-dashboard-widget .wpfnl-overview__report {
493 + display: grid;
494 + grid-template-columns: repeat(2, 1fr);
495 + border: 1px solid #EEEAF8;
496 + margin: 15px 0;
497 + }
498 +
499 + .wpfnl-dashboard-widget .wpfnl-overview__report .report-item {
500 + display: flex;
501 + gap: 15px;
502 + padding: 20px;
503 + position: relative;
504 +
505 + }
506 +
507 + .wpfnl-dashboard-widget .wpfnl-overview__report .report-item:nth-child(odd)::before {
508 + content: "";
509 + position: absolute;
510 + right: 15%;
511 + height: 36px;
512 + width: 1px;
513 + background: linear-gradient(180deg, rgba(238, 234, 248, 0.00) -2.78%, #EEEAF8 47.1%, rgba(238, 234, 248, 0.00) 104.17%);
514 + }
515 +
516 + .wpfnl-dashboard-widget .wpfnl-overview__report .report-item .text-wrapper p {
517 + margin: 0;
518 + line-height: 1;
519 + }
520 +
521 + .wpfnl-dashboard-widget .wpfnl-overview__report .report-item .text-wrapper p.title {
522 + color: #2E3148;
523 + font-size: 12px;
524 + font-weight: 400;
525 + margin-bottom: 8px;
526 + }
527 +
528 + .wpfnl-dashboard-widget .wpfnl-overview__report .report-item .text-wrapper p.amount {
529 + color: #2E3148;
530 + font-size: 15px;
531 + font-weight: 600;
532 + line-height: normal;
533 + }
534 +
535 + .wpfnl-dashboard-widget .wpfnl-overview__header {
536 + display: flex;
537 + align-items: center;
538 + justify-content: space-between;
539 + border-bottom: 1px solid #eee;
540 + gap: 15px;
541 + padding: 15px 15px;
542 + margin: 0 -12px;
543 +
544 + }
545 +
546 + .wpfnl-dashboard-widget .wpfnl-overview__header .wpfnl-overview__upgrade {
547 + padding: 0px 15px;
548 + }
549 +
223 550 .wpfnl-dashboard-widget .wpfnl-overview__versions {
224 - padding: 0 10px;
225 551 font-size: 0.9em;
226 552 line-height: 1.5;
227 553 }
554 +
228 555 .wpfnl-dashboard-widget .wpfnl-overview__version {
229 556 display: block;
230 557 }
231 - .wpfnl-dashboard-widget .wpfnl-overview__create {
232 - text-align: right;
558 +
559 + .wpfnl-dashboard-widget .wpfnl-overview__create a {
560 + border-radius: 7px;
561 + background: #6E42D3;
562 + border: 1px solid #6E42D3;
563 + font-style: normal;
564 + line-height: 2.28;
565 + color: #FFF;
566 + text-align: center;
567 + font-family: Inter;
568 + font-size: 14px;
569 + font-weight: 500;
233 570 }
571 +
572 + .wpfnl-dashboard-widget .wpfnl-overview__create a:hover {
573 + background: #6E42D3;
574 + color: #FFF;
575 + box-shadow: unset;
576 + border-color: transparent;
577 + }
578 +
579 + .wpfnl-dashboard-widget .wpfnl-overview__create a:focus {
580 + background: #6E42D3;
581 + color: #FFF;
582 + outline: none;
583 + box-shadow: unset;
584 + border-color: transparent;
585 + }
586 +
234 587 .wpfnl-dashboard-widget .wpfnl-overview__create .dashicons {
235 588 font-size: 15px;
236 589 line-height: 1;
237 590 position: relative;
@@ -236,22 +589,26 @@
236 589 line-height: 1;
237 590 position: relative;
238 591 top: 8px;
239 592 }
593 +
240 594 .wpfnl-dashboard-widget .wpfnl-overview__recently-edited .wpfnl-heading {
241 - font-weight: 600!important;
595 + font-weight: 600 !important;
242 596 border-bottom: 1px solid #eee;
243 - margin: 0 -12px!important;
244 - padding: 6px 12px!important;
245 - margin-bottom: 13px!important;
597 + margin: 0 -12px !important;
598 + padding: 6px 12px !important;
599 + margin-bottom: 13px !important;
246 600 }
601 +
247 602 .wpfnl-dashboard-widget .wpfnl-overview__post {
248 603 margin-top: 10px;
249 604 font-weight: 500;
250 605 }
606 +
251 607 .wpfnl-dashboard-widget .wpfnl-overview__post-link {
252 608 display: inline-block;
253 609 }
610 +
254 611 .wpfnl-dashboard-widget .wpfnl-overview__post-description {
255 612 margin: 0 0 1.5em;
256 613 }
257 614
@@ -270,30 +627,51 @@
270 627 list-style: none;
271 628 margin: 0;
272 629 padding: 0;
273 630 }
631 +
274 632 .wpfnl-dashboard-widget .wpfnl-overview__footer ul li {
275 633 padding: 0 10px;
276 634 margin: 0;
277 - border-left: 1px solid #ddd;
635 + position: relative;
278 636 }
637 +
638 + .wpfnl-dashboard-widget .wpfnl-overview__footer .wpfnl-overview__blog a,
639 + .wpfnl-dashboard-widget .wpfnl-overview__footer .wpfnl-overview__changelog a,
640 + .wpfnl-dashboard-widget .wpfnl-overview__footer .wpfnl-overview__help a {
641 + color: #667085;
642 + font-size: 12px;
643 + font-weight: 400;
644 + position: relative;
645 + }
646 +
647 + .wpfnl-dashboard-widget .wpfnl-overview__footer .wpfnl-overview__blog a:hover,
648 + .wpfnl-dashboard-widget .wpfnl-overview__footer .wpfnl-overview__changelog a:hover,
649 + .wpfnl-dashboard-widget .wpfnl-overview__footer .wpfnl-overview__help a:hover {
650 + color: #6846CE;
651 + }
652 +
653 + .wpfnl-dashboard-widget .wpfnl-overview__footer ul li:not(:last-child)::before {
654 + content: "";
655 + position: absolute;
656 + right: 0;
657 + top: 3px;
658 + height: 16px;
659 + width: 1px;
660 + background: linear-gradient(180deg, rgba(238, 234, 248, 0.00) -2.78%, #EEEAF8 47.1%, rgba(238, 234, 248, 0.00) 104.17%);
661 + }
662 +
279 663 .wpfnl-dashboard-widget .wpfnl-overview__footer ul li:first-child {
280 664 padding-left: 0;
281 665 border: none;
282 666 }
283 - .wpfnl-dashboard-widget .wpfnl-overview__footer ul li .dashicons {
284 - font-size: 17px;
285 - color: #606a73;
286 - position: relative;
287 - top: 1px;
288 - }
667 +
289 668 .wpfnl-dashboard-widget .wpfnl-overview__footer ul li.wpfnl-overview__go-pro a {
290 - color: #ee8134;
291 - font-weight: 700;
669 + color: #DF8740;
670 + font-size: 12px;
671 + font-weight: 400;
672 + line-height: normal;
292 673 }
293 - .wpfnl-dashboard-widget .wpfnl-overview__footer ul li.wpfnl-overview__go-pro a .dashicons {
294 - color: #ee8134;
295 - }
296 674 </style>
297 675 </div>
298 676 <?php
299 677 }
@@ -310,15 +688,11 @@
310 688 *
311 689 * @return array Feed data.
312 690 * @since 2.7.16
313 691 */
314 - public function get_wpfnl_feed_data ( $force_update = false ){
315 - $feed = $this->get_wpfnl_info_data( $force_update );
316 -
317 - if ( empty( $feed ) || !isset( $feed['data']) ){
318 - return [];
319 - }
320 - return $feed['data'];
692 + public function get_wpfnl_feed_data($force_update = false)
693 + {
694 + return $this->get_wpfnl_info_data($force_update);
321 695 }
322 696
323 697 /**
324 698 * Get wpfunnles info data.
@@ -332,67 +706,77 @@
332 706 *
333 707 * @return array|false Info data, or false.
334 708 * @since 2.7.16
335 709 */
336 - private function get_wpfnl_info_data ( $force_update ){
337 - $cache_key = 'wpfunnel_remote_feed_data_' . WPFNL_VERSION;
710 + private function get_wpfnl_info_data($force_update)
711 + {
712 + global $wpdb;
713 + $response = [
714 + [
715 + 'title' => 'Total Orders',
716 + 'amount' => $wpdb->get_var($wpdb->prepare("SELECT COUNT(id) FROM {$wpdb->prefix}wpfnl_stats WHERE status = %s", 'completed')),
717 + 'show_pro' => false
718 + ],
719 + [
720 + 'title' => 'Total Revenue',
721 + 'amount' => Wpfnl_functions::is_wc_active() ? wc_price((int)$wpdb->get_var($wpdb->prepare("SELECT SUM(total_sales) FROM {$wpdb->prefix}wpfnl_stats WHERE status = %s", 'completed'))) : number_format(0, 2),
722 + 'show_pro' => false
723 + ],
724 + [
725 + 'title' => 'Order Bump Revenue',
726 + 'amount' => Wpfnl_functions::is_wc_active() ? wc_price((int)$wpdb->get_var($wpdb->prepare("SELECT SUM(orderbump_sales) FROM {$wpdb->prefix}wpfnl_stats WHERE status = %s", 'completed'))) : number_format(0, 2),
727 + 'show_pro' => false
728 + ],
729 + [
730 + 'title' => 'Upsell/Downsell Revenue',
731 + 'amount' => (Wpfnl_functions::is_pro_license_activated() && Wpfnl_functions::is_wc_active()) ? wc_price((int)$wpdb->get_var($wpdb->prepare("SELECT SUM(upsell_sales + downsell_sales) FROM {$wpdb->prefix}wpfnl_stats WHERE status = %s", 'completed'))) : number_format(0, 2),
732 + 'show_pro' => true
733 + ],
734 + [
735 + 'title' => 'Total Leads',
736 + 'amount' => $wpdb->get_var("SELECT COUNT(id) FROM {$wpdb->prefix}wpfnl_optin_entries"),
737 + 'show_pro' => false
738 + ],
739 + ];
338 740
339 - $info_data = get_transient( $cache_key );
340 -
341 - if ( $force_update || false === $info_data || empty( $info_data ) ){
342 - $url = 'https://getwpfunnels.com/wp-json/wp/v2/posts';
343 - $params = [
344 - 'per_page' => 3,
345 - ];
346 - $url = add_query_arg($params, $url);
347 - $response = $this->remote_get($url);
348 -
349 - if ( is_wp_error( $response ) || empty( $response['data'] ) ){
350 - set_transient($cache_key, [], 2 * HOUR_IN_SECONDS);
351 - return false;
352 - }
353 -
354 - $info_data = $response;
355 - set_transient( $cache_key, $info_data, 12 * HOUR_IN_SECONDS );
356 -
357 - }
358 - return $info_data;
741 + return $response;
359 742 }
360 743
361 744 /**
362 745 * Get all post from url
363 - *
746 + *
364 747 * @param $url
365 748 * @param $args
366 - *
749 + *
367 750 * @return array
368 751 */
369 - private function remote_get($url, $args = [])
370 - {
371 - $response = wp_remote_get($url, $args);
752 + private function remote_get($url, $args = [])
753 + {
754 + $response = wp_remote_get($url, $args);
372 755
373 - if ( is_wp_error( $response ) || ! is_array( $response ) || ! isset( $response['body'] ) ) {
756 + if (is_wp_error($response) || !is_array($response) || !isset($response['body'])) {
374 757 return [
375 758 'success' => false,
376 759 'message' => $response->get_error_message(),
377 - 'data' => $response,
760 + 'data' => $response,
378 761 ];
379 762 }
380 763
381 764 // Decode the results.
382 - $results = json_decode( $response['body'], true );
765 + $results = json_decode($response['body'], true);
383 766
384 - if ( ! is_array( $results ) ) {
385 - return new \WP_Error( 'unexpected_data_format', 'Data was not returned in the expected format.' );
767 + if (!is_array($results)) {
768 + return new \WP_Error('unexpected_data_format', 'Data was not returned in the expected format.');
386 769 }
387 770
388 - return [
389 - 'success' => true,
390 - 'message' => 'Data successfully retrieved',
391 - 'data' => json_decode(wp_remote_retrieve_body($response), true),
392 - ];
393 - }
771 + return [
772 + 'success' => true,
773 + 'message' => 'Data successfully retrieved',
774 + 'data' => json_decode(wp_remote_retrieve_body($response), true),
775 + ];
776 + }
394 777
778 +
395 779 /**
396 780 * Get wpfunnels dashboard overview widget footer actions.
397 781 *
398 782 * Retrieves the footer action links displayed in wpfunnels dashboard widget.
@@ -399,30 +783,31 @@
399 783 *
400 784 * @since 1.9.0
401 785 * @access private
402 786 */
403 - private function get_wpfunnels_dashboard_overview_widget_footer_actions() {
787 + private function get_wpfunnels_dashboard_overview_widget_footer_actions()
788 + {
404 789 $actions = [
790 + 'go-pro' => [
791 + 'title' => esc_html__('Upgrade to Pro', 'wpfnl'),
792 + 'link' => 'https://getwpfunnels.com/pricing/',
793 + ],
405 794 'blog' => [
406 - 'title' => esc_html__( 'Blog', 'wpfnl' ),
795 + 'title' => esc_html__('Blogs', 'wpfnl'),
407 796 'link' => 'https://getwpfunnels.com/blog/',
408 797 ],
409 798 'changelog' => [
410 - 'title' => esc_html__( 'Changelog', 'wpfnl' ),
799 + 'title' => esc_html__('Changelogs', 'wpfnl'),
411 800 'link' => 'https://getwpfunnels.com/changelog/',
412 801 ],
413 - 'go-pro' => [
414 - 'title' => esc_html__( 'Upgrade', 'wpfnl' ),
415 - 'link' => 'https://getwpfunnels.com/pricing/',
416 - ],
417 802 'help' => [
418 - 'title' => esc_html__( 'Help', 'wpfnl' ),
803 + 'title' => esc_html__('Get Help', 'wpfnl'),
419 804 'link' => 'https://getwpfunnels.com/docs/getting-started-with-wpfunnels/',
420 805 ],
421 806
422 807 ];
423 808
424 - if( Wpfnl_functions::is_pro_license_activated() ){
809 + if (Wpfnl_functions::is_pro_license_activated()) {
425 810 unset($actions['go-pro']);
426 811 }
427 812
428 813 return $actions;
@@ -439,8 +824,12 @@
439 824 {
440 825 $documentation = array(
441 826 '<a href="https://getwpfunnels.com/docs/wpfunnels-wordpress-funnel-builder/" target="_blank" >Documentation</a>',
442 827 );
828 + $is_pro_installed = Wpfnl_functions::is_plugin_installed('wpfunnels-pro/wpfnl-pro.php');
829 + if (!defined('WPFNL_PRO_VERSION') && !$is_pro_installed) {
830 + $documentation[] = '<a href="https://getwpfunnels.com/pricing/" target="_blank" style="color:#6E42D3;font-weight:bold" >Upgrade to Pro</a>';
831 + }
443 832 $actions = array_merge($actions, $documentation);
444 833 return $actions;
445 834 }
446 835
@@ -462,20 +851,570 @@
462 851 * The Wpfnl_Loader will then create the relationship
463 852 * between the defined hooks and the functions defined in this
464 853 * class.
465 854 */
466 - if ( 'wp-funnels_page_email-builder' === $hook ) {
467 - wp_enqueue_style($this->plugin_name .'-email-builder' , plugin_dir_url(__FILE__) . 'assets/dist/email-builder/email-builder.css', [], $this->version, 'all');
855 + if ('wpfunnels_page_email-builder' === $hook) {
856 + wp_enqueue_style($this->plugin_name . '-email-builder', plugin_dir_url(__FILE__) . 'assets/dist/email-builder/email-builder.css', [], $this->version, 'all');
468 857 }
858 +
469 859 if (in_array($hook, $this->page_hooks)) {
470 - wp_enqueue_style( 'wp-color-picker' );
860 + wp_enqueue_style('wp-color-picker');
471 861 wp_enqueue_style($this->plugin_name . '-jquery-ui', plugin_dir_url(__FILE__) . 'assets/css/jquery-ui.min.css', [], $this->version, 'all');
862 +
472 863 wp_enqueue_style($this->plugin_name, plugin_dir_url(__FILE__) . 'assets/css/wpfnl-admin.css', [], $this->version, 'all');
864 +
473 865 do_action('wpfunnels_after_styles_loaded');
474 866 }
475 867 }
476 868
477 869 /**
870 + * Screen hooks the React single-page admin renders on — the WPFunnels
871 + * dashboard, plus the alias slug it was first built under.
872 + *
873 + * The settings page is here too. It normally redirects to `#/settings`
874 + * before this ever runs, so nothing loads on the way through; the entry
875 + * matters for the case where that redirect is filtered off, when the page
876 + * renders the app itself and needs the app's assets to do it.
877 + *
878 + * @return array Hook suffixes.
879 + * @since 3.13.2
880 + */
881 + private function get_react_admin_hooks()
882 + {
883 + return array(
884 + 'toplevel_page_' . WPFNL_MAIN_PAGE_SLUG,
885 + 'wpfunnels_page_' . WPFNL_REACT_ADMIN_SLUG,
886 + 'wpfunnels_page_' . WPFNL_GLOBAL_SETTINGS_SLUG,
887 + );
888 + }
889 +
890 +
891 + /**
892 + * Resolve what the automation screen still needs before it can be used.
893 + *
894 + * Automations run on four things: WPFunnels and WPFunnels Pro with an
895 + * activated licence, and Mail Mint and Mail Mint Pro with an activated
896 + * licence. Each of the two Pro stacks walks the same ladder — not on the
897 + * site, on the site but switched off, running but unlicensed, satisfied —
898 + * and each rung needs a different next step, so the whole thing is resolved
899 + * here rather than in the React app: only PHP can tell an uninstalled plugin
900 + * from a deactivated one, and only PHP knows the licence state.
901 + *
902 + * @return array List of dependency descriptors, in the order they should be
903 + * presented.
904 + * @since 3.13.3
905 + */
906 + private function get_automation_dependencies()
907 + {
908 + return array(
909 + $this->get_wpfnl_pro_dependency(),
910 + $this->get_mail_mint_dependency(),
911 + );
912 + }
913 +
914 +
915 + /**
916 + * Dependency descriptor for the WPFunnels Pro half of the ladder.
917 + *
918 + * WPFunnels Pro is not on wordpress.org, so the "not installed" rung is a
919 + * purchase link rather than a one-click install.
920 + *
921 + * @return array Dependency descriptor.
922 + * @since 3.13.3
923 + */
924 + private function get_wpfnl_pro_dependency()
925 + {
926 + $base = array(
927 + 'id' => 'wpfunnels-pro',
928 + 'name' => __('WPFunnels Pro', 'wpfnl'),
929 + 'basename' => self::WPFNL_PRO_BASENAME,
930 + 'slug' => 'wpfunnels-pro',
931 + 'action' => '',
932 + 'url' => '',
933 + 'newTab' => false,
934 + );
935 +
936 + if ((bool) apply_filters('wpfunnels/is_pro_license_activated', false)) {
937 + return array_merge($base, array(
938 + 'state' => 'satisfied',
939 + 'message' => '',
940 + 'label' => '',
941 + ));
942 + }
943 +
944 + if (Wpfnl_functions::is_plugin_activated(self::WPFNL_PRO_BASENAME)) {
945 + return array_merge($base, array(
946 + 'state' => 'needs_license',
947 + 'message' => __('WPFunnels Pro is active, but its license has not been activated yet.', 'wpfnl'),
948 + 'label' => __('Activate License', 'wpfnl'),
949 + 'url' => esc_url_raw(add_query_arg(array('page' => 'wpf-license'), admin_url('admin.php'))),
950 + ));
951 + }
952 +
953 + if (Wpfnl_functions::is_plugin_installed(self::WPFNL_PRO_BASENAME)) {
954 + return array_merge($base, array(
955 + 'state' => 'needs_activation',
956 + 'message' => __('WPFunnels Pro is installed but currently inactive.', 'wpfnl'),
957 + 'label' => __('Activate WPFunnels Pro', 'wpfnl'),
958 + 'action' => 'activate',
959 + ));
960 + }
961 +
962 + return array_merge($base, array(
963 + 'state' => 'not_installed',
964 + 'message' => __('Automations need WPFunnels Pro, which is not installed on this site.', 'wpfnl'),
965 + 'label' => __('Upgrade to Pro', 'wpfnl'),
966 + 'url' => 'https://getwpfunnels.com/pricing/',
967 + 'newTab' => true,
968 + ));
969 + }
970 +
971 +
972 + /**
973 + * Dependency descriptor for the Mail Mint half of the ladder.
974 + *
975 + * The free plugin comes from wordpress.org, so its first two rungs are
976 + * one-click: wpfunnels/v1/activate-plugin installs the package when it is
977 + * missing and activates it either way. Mail Mint Pro is a paid plugin, so
978 + * its "not installed" rung is a purchase link.
979 + *
980 + * @return array Dependency descriptor.
981 + * @since 3.13.3
982 + */
983 + private function get_mail_mint_dependency()
984 + {
985 + $base = array(
986 + 'id' => 'mail-mint',
987 + 'name' => __('Mail Mint', 'wpfnl'),
988 + 'basename' => self::MAIL_MINT_BASENAME,
989 + 'slug' => 'mail-mint',
990 + 'action' => '',
991 + 'url' => '',
992 + 'newTab' => false,
993 + );
994 +
995 + if (Wpfnl_functions::is_mail_mint_pro_license_active() && Wpfnl_functions::is_mail_mint_free_active()) {
996 + return array_merge($base, array(
997 + 'state' => 'satisfied',
998 + 'message' => '',
999 + 'label' => '',
1000 + ));
1001 + }
1002 +
1003 + if (!Wpfnl_functions::is_plugin_installed(self::MAIL_MINT_BASENAME)) {
1004 + return array_merge($base, array(
1005 + 'state' => 'not_installed',
1006 + 'message' => __('Automations need Mail Mint, which is not installed on this site.', 'wpfnl'),
1007 + 'label' => __('Install Mail Mint', 'wpfnl'),
1008 + 'action' => 'install',
1009 + ));
1010 + }
1011 +
1012 + if (!Wpfnl_functions::is_mail_mint_free_active()) {
1013 + return array_merge($base, array(
1014 + 'state' => 'needs_activation',
1015 + 'message' => __('Mail Mint is installed but currently inactive.', 'wpfnl'),
1016 + 'label' => __('Activate Mail Mint', 'wpfnl'),
1017 + 'action' => 'activate',
1018 + ));
1019 + }
1020 +
1021 + // Mail Mint is running; the rest of the ladder is about its Pro plugin.
1022 + if (Wpfnl_functions::is_plugin_activated(self::MAIL_MINT_PRO_BASENAME)) {
1023 + return array_merge($base, array(
1024 + 'state' => 'needs_license',
1025 + 'name' => __('Mail Mint Pro', 'wpfnl'),
1026 + 'basename' => self::MAIL_MINT_PRO_BASENAME,
1027 + 'message' => __('Mail Mint Pro is active, but its license has not been activated yet.', 'wpfnl'),
1028 + 'label' => __('Activate License', 'wpfnl'),
1029 + 'url' => esc_url_raw(admin_url('admin.php?page=mrm-admin#/settings/license')),
1030 + ));
1031 + }
1032 +
1033 + if (Wpfnl_functions::is_plugin_installed(self::MAIL_MINT_PRO_BASENAME)) {
1034 + return array_merge($base, array(
1035 + 'state' => 'needs_activation',
1036 + 'name' => __('Mail Mint Pro', 'wpfnl'),
1037 + 'basename' => self::MAIL_MINT_PRO_BASENAME,
1038 + 'slug' => 'mail-mint-pro',
1039 + 'message' => __('Mail Mint Pro is installed but currently inactive.', 'wpfnl'),
1040 + 'label' => __('Activate Mail Mint Pro', 'wpfnl'),
1041 + 'action' => 'activate',
1042 + ));
1043 + }
1044 +
1045 + return array_merge($base, array(
1046 + 'state' => 'not_installed',
1047 + 'name' => __('Mail Mint Pro', 'wpfnl'),
1048 + 'basename' => self::MAIL_MINT_PRO_BASENAME,
1049 + 'slug' => 'mail-mint-pro',
1050 + 'message' => __('Automations need Mail Mint Pro with an activated license.', 'wpfnl'),
1051 + 'label' => __('Get Mail Mint Pro', 'wpfnl'),
1052 + 'url' => 'https://getwpfunnels.com/pricing/#mail-mint',
1053 + 'newTab' => true,
1054 + ));
1055 + }
1056 +
1057 +
1058 + /**
1059 + * Work out what the header's Pro call to action should say and where it
1060 + * should go.
1061 + *
1062 + * There are three ways an install can fall short of a licensed Pro, and each
1063 + * needs a different next step — telling someone to buy Pro when the plugin is
1064 + * sitting deactivated in their plugins list is a dead end. Resolved here
1065 + * rather than in the React app because the activation link needs a nonce and
1066 + * a capability check.
1067 + *
1068 + * @return array|null CTA descriptor, or null when Pro is fully licensed and
1069 + * no call to action belongs in the header.
1070 + * @since 3.13.2
1071 + */
1072 + private function get_pro_cta()
1073 + {
1074 + if (!function_exists('get_plugins')) {
1075 + require_once ABSPATH . 'wp-admin/includes/plugin.php';
1076 + }
1077 +
1078 + $basename = 'wpfunnels-pro/wpfnl-pro.php';
1079 +
1080 + if ((bool) apply_filters('wpfunnels/is_pro_license_activated', false)) {
1081 + return null;
1082 + }
1083 +
1084 + // Installed and running, but unlicensed — the features stay locked until
1085 + // the licence is activated, so send them to the licence screen.
1086 + if (Wpfnl_functions::is_plugin_activated($basename)) {
1087 + return array(
1088 + 'state' => 'needs_license',
1089 + 'label' => __('Activate License', 'wpfnl'),
1090 + 'title' => __('Activate your WPFunnels Pro license to unlock Pro features', 'wpfnl'),
1091 + 'url' => esc_url_raw(add_query_arg(array('page' => 'wpf-license'), admin_url('admin.php'))),
1092 + 'newTab' => false,
1093 + );
1094 + }
1095 +
1096 + // Present but deactivated — one click away, no purchase needed.
1097 + if (Wpfnl_functions::is_plugin_installed($basename)) {
1098 + $plugins_url = admin_url('plugins.php');
1099 + $can_activate = current_user_can('activate_plugins');
1100 +
1101 + /*
1102 + * Built with add_query_arg rather than wp_nonce_url on purpose.
1103 + * wp_nonce_url() returns esc_html()'d output, so its separators come
1104 + * back as `&#038;`. That is correct for a URL echoed straight into
1105 + * markup — the HTML parser decodes it — but this one travels through
1106 + * wp_localize_script and is applied by React with setAttribute,
1107 + * where nothing decodes entities. The literal `&#038;` would split
1108 + * the query string into junk parameters, `plugin` and `_wpnonce`
1109 + * would arrive empty, and WordPress would answer the click with
1110 + * "The link you followed has expired."
1111 + *
1112 + * wp_localize_script's own html_entity_decode() does not save us
1113 + * either: it only walks top-level scalars, and this URL is nested
1114 + * inside the CTA array.
1115 + *
1116 + * Without the capability the one-click link would only bounce off a
1117 + * permission check, so point at the plugins list instead.
1118 + */
1119 + $url = $can_activate
1120 + ? add_query_arg(
1121 + array(
1122 + 'action' => 'activate',
1123 + 'plugin' => rawurlencode($basename),
1124 + '_wpnonce' => wp_create_nonce('activate-plugin_' . $basename),
1125 + ),
1126 + $plugins_url
1127 + )
1128 + : $plugins_url;
1129 +
1130 + return array(
1131 + 'state' => 'needs_activation',
1132 + 'label' => __('Activate Pro', 'wpfnl'),
1133 + 'title' => __('WPFunnels Pro is installed but not active — activate it to unlock Pro features', 'wpfnl'),
1134 + 'url' => esc_url_raw($url),
1135 + 'newTab' => false,
1136 + );
1137 + }
1138 +
1139 + // Not on the site at all.
1140 + return array(
1141 + 'state' => 'not_installed',
1142 + 'label' => __('Upgrade to Pro', 'wpfnl'),
1143 + 'title' => __('Upgrade to WPFunnels Pro', 'wpfnl'),
1144 + 'url' => 'https://getwpfunnels.com/pricing/',
1145 + 'newTab' => true,
1146 + );
1147 + }
1148 +
1149 +
1150 + /**
1151 + * Enqueue the React single-page admin bundle.
1152 + *
1153 + * Deliberately kept out of the `$page_hooks` group: the React admin ships
1154 + * its own namespaced stylesheet (every class prefixed `rwpfnl`) and must not
1155 + * inherit the legacy admin CSS, which is written against the Vue markup.
1156 + *
1157 + * @param string $hook Current admin screen hook suffix.
1158 + *
1159 + * @return void
1160 + * @since 3.13.2
1161 + */
1162 + /**
1163 + * Data the React template library screen needs.
1164 + *
1165 + * The same facts the Vue library read off `WPFunnelVars` and
1166 + * `template_library_object`, narrowed to what the standalone Templates page
1167 + * actually uses. The listing itself still comes from the REST API; this is
1168 + * only the context the listing is read in.
1169 + *
1170 + * @return array Template library context.
1171 + * @since 3.13.5
1172 + */
1173 + private function get_template_library_data()
1174 + {
1175 + $is_pro_license_active = (bool) apply_filters('wpfunnels/is_pro_license_activated', false);
1176 +
1177 + return array(
1178 + 'builder' => Wpfnl_functions::get_builder_type(),
1179 + 'isAnyPluginMissing' => Wpfnl_functions::is_any_plugin_missing(),
1180 + 'dependencyPlugins' => Wpfnl_functions::get_dependency_plugins_status(),
1181 + 'types' => Wpfnl_functions::get_template_types(),
1182 + // Store Checkout funnels have their own allowance, so they are not
1183 + // counted against this one.
1184 + 'activeFunnels' => Wpfnl_functions::count_non_store_checkout_funnels(),
1185 + // -1 once the licence lifts the cap.
1186 + 'allowedFunnels' => $is_pro_license_active ? -1 : 3,
1187 + // The remote-funnel add-on serves the library from elsewhere, and
1188 + // this screen then has nothing of its own to fetch.
1189 + 'isRemote' => Wpfnl_functions::maybe_remote_funnel(),
1190 + );
1191 + }
1192 +
1193 +
1194 + private function enqueue_react_admin_assets($hook)
1195 + {
1196 + if (!in_array($hook, $this->get_react_admin_hooks(), true)) {
1197 + return;
1198 + }
1199 +
1200 + /*
1201 + * A production build splits the webpack runtime into its own chunk that
1202 + * every entry depends on, so it has to load first. A development build
1203 + * (npm run start) keeps the runtime inside each entry and emits no such
1204 + * file, hence the existence check rather than a hard dependency.
1205 + */
1206 + $runtime_deps = array();
1207 + $runtime_path = WPFNL_DIR . '/admin/assets/dist/runtime/index.min.js';
1208 +
1209 + if (file_exists($runtime_path)) {
1210 + wp_enqueue_script(
1211 + 'wpfnl-webpack-runtime',
1212 + plugin_dir_url(__FILE__) . 'assets/dist/runtime/index.min.js',
1213 + array(),
1214 + $this->version,
1215 + true
1216 + );
1217 +
1218 + $runtime_deps[] = 'wpfnl-webpack-runtime';
1219 + }
1220 +
1221 + wp_enqueue_script(
1222 + 'wpfnl-react-admin',
1223 + plugin_dir_url(__FILE__) . 'assets/dist/js/wpfnl-react-admin.min.js',
1224 + $runtime_deps,
1225 + $this->version,
1226 + true
1227 + );
1228 +
1229 + Wpfnl_functions::enqueue_bundle_style('wpfnl-react-admin', 'wpfnl-react-admin-bundle');
1230 +
1231 + // The React admin renders the whole header action group itself — the
1232 + // AI Assistant drawer included (see src/admin/components/AiDrawer/) —
1233 + // so the Vue global-ai-drawer bundle is NOT loaded on this screen: it
1234 + // would find neither of its mount points (#wpfnl-header-actions,
1235 + // #wpfnl-global-ai-drawer), and loading it anyway only shipped a second
1236 + // copy of the group's markup and styles.
1237 + $_is_pro_license_active = (bool) apply_filters('wpfunnels/is_pro_license_activated', false);
1238 +
1239 + /*
1240 + * Everything after the page name in the title WordPress just printed —
1241 + * separator, site name, " — WordPress". The React admin is one WordPress
1242 + * page with its routes in the hash, so WordPress titles the tab once,
1243 + * with this page's own name ("Dashboard"), and every route below it
1244 + * inherits that. The app retitles itself per route (see
1245 + * src/admin/utils/documentTitle.js) and needs this tail, which is
1246 + * translatable and so cannot be rebuilt in JavaScript.
1247 + *
1248 + * `$admin_title` is built from `$title`, so it always starts with it;
1249 + * the prefix check is only there in case an `admin_title` filter has
1250 + * rewritten the whole thing, in which case the app keeps what WordPress
1251 + * printed for the page name it resolves. Entities such as `&lsaquo;`
1252 + * survive here: wp_localize_script() decodes them on the way out.
1253 + */
1254 + $_document_title_suffix = '';
1255 +
1256 + if (isset($GLOBALS['title'], $GLOBALS['admin_title'])) {
1257 + $_page_title = (string) $GLOBALS['title'];
1258 + $_admin_title = (string) $GLOBALS['admin_title'];
1259 +
1260 + if ('' !== $_page_title && 0 === strpos($_admin_title, $_page_title)) {
1261 + $_document_title_suffix = substr($_admin_title, strlen($_page_title));
1262 + }
1263 + }
1264 +
1265 + // Mail Mint's install state is read from get_plugins() below.
1266 + if (!function_exists('get_plugins')) {
1267 + require_once ABSPATH . 'wp-admin/includes/plugin.php';
1268 + }
1269 +
1270 + /*
1271 + * Core's plugin updater. The template library route offers to install
1272 + * the page builder its templates are authored for when that plugin is
1273 + * missing, and `wp.updates` is what does the installing.
1274 + */
1275 + wp_enqueue_script('updates');
1276 +
1277 + wp_localize_script(
1278 + 'wpfnl-react-admin',
1279 + 'RWPFNLAdmin',
1280 + array(
1281 + 'rest_api_url' => get_rest_url(),
1282 + 'nonce' => wp_create_nonce('wp_rest'),
1283 + 'security' => wp_create_nonce('wpfnl-admin'),
1284 + 'admin_url' => admin_url(),
1285 + 'image_path' => WPFNL_URL . 'admin/assets/images',
1286 + // Base URL the bundle resolves its async chunks against — the
1287 + // canvas editor is code-split (see src/admin/public-path.js).
1288 + 'dist_url' => WPFNL_URL . 'admin/assets/dist/',
1289 + 'priceConfig' => Wpfnl_functions::get_wc_price_config(),
1290 + 'isProActivated' => apply_filters('wpfunnels/is_wpfnl_pro_active', false),
1291 + 'isWPFIntegrationActive' => apply_filters('wpfunnels/is_integration_license_active', false),
1292 + /*
1293 + * Mail Mint's licence state, which decides whether the automation
1294 + * route shows the promo screen at all or hands the visitor to the
1295 + * legacy automation list.
1296 + */
1297 + 'isMailMintProLicenseActive' => Wpfnl_functions::is_mail_mint_pro_license_active(),
1298 + /*
1299 + * What the automation screen still needs, rung by rung, with the
1300 + * next step for each. Plugin-installing rungs carry a basename and
1301 + * slug the screen posts to wpfunnels/v1/activate-plugin; the rest
1302 + * carry a URL.
1303 + */
1304 + 'automationDependencies' => $this->get_automation_dependencies(),
1305 + 'canInstallPlugins' => current_user_can('install_plugins'),
1306 + 'canActivatePlugins' => current_user_can('activate_plugins'),
1307 + 'getText' => Wpfnl_functions::get_text(),
1308 + 'startOfWeek' => (int) get_option('start_of_week', 0),
1309 + /*
1310 + * Whether the onboarding checklist was on show last time the
1311 + * dashboard asked — 'yes', 'no', or absent when unknown. Only
1312 + * decides whether to hold space for it while its real status
1313 + * loads; the status itself still comes from the REST call.
1314 + */
1315 + 'onboardingHint' => get_transient(\WPFunnels\Rest\Controllers\DashboardController::ONBOARDING_VISIBILITY_KEY),
1316 + /*
1317 + * Header action group data. It has to ride on this handle, not on
1318 + * the AI-drawer handle the Vue group used: WordPress prints a
1319 + * script's localized data immediately before that script's own
1320 + * tag, and the React bundle is enqueued first — so anything
1321 + * attached to the drawer does not exist yet when React renders.
1322 + */
1323 + 'isProLicenseActive' => $_is_pro_license_active,
1324 + 'upgradeUrl' => 'https://getwpfunnels.com/pricing/',
1325 + 'proCta' => $this->get_pro_cta(),
1326 + 'documentTitleSuffix' => $_document_title_suffix,
1327 + /*
1328 + * What the template library route runs on. Every value here is
1329 + * something only PHP can answer — which page builder the site is
1330 + * set up for and whether its plugin is actually there, how much
1331 + * of an unlicensed install's funnel allowance is left, which
1332 + * template types this install has. Arrives as a real object:
1333 + * wp_localize_script only stringifies top-level scalars.
1334 + */
1335 + 'templateLibrary' => $this->get_template_library_data(),
1336 + /*
1337 + * The two settings panels that are still Vue apps of their own,
1338 + * fetched by the settings screen when one of them is opened.
1339 + */
1340 + 'settingsPanelApps' => $this->get_settings_panel_apps(),
1341 + )
1342 + );
1343 +
1344 + $this->enqueue_settings_panel_apps();
1345 + }
1346 +
1347 +
1348 + /**
1349 + * The two settings panels that are still Vue apps of their own.
1350 + *
1351 + * "AI Assistant" and "MCP for AI Agents" each own their fields and save them
1352 + * through their own REST routes, so the React settings screen renders a
1353 + * mount point and asks those bundles to mount into it rather than
1354 + * reimplementing them (see src/admin/pages/Settings/VuePanel.jsx). The
1355 + * bundles are unchanged — they still mount themselves on the legacy settings
1356 + * page, where the mount point is printed by PHP.
1357 + *
1358 + * @return void
1359 + * @since 3.13.8
1360 + */
1361 + private function enqueue_settings_panel_apps()
1362 + {
1363 + $_current_user = wp_get_current_user();
1364 + $_common = array(
1365 + 'username' => $_current_user ? $_current_user->user_login : '',
1366 + 'rest_url' => esc_url_raw(get_rest_url()),
1367 + 'nonce' => wp_create_nonce('wp_rest'),
1368 + 'admin_url' => esc_url_raw(admin_url()),
1369 + );
1370 +
1371 + /*
1372 + * The globals the two bundles read, printed before the React bundle so
1373 + * they are there whenever a bundle is pulled in later. They are a few
1374 + * hundred bytes; the bundles themselves are megabytes, which is why
1375 + * only the data is printed up front.
1376 + */
1377 + wp_add_inline_script(
1378 + 'wpfnl-react-admin',
1379 + 'window.WPFnlMcpSettings = ' . wp_json_encode(array_merge($_common, array('site_url' => esc_url_raw(home_url())))) . ';' .
1380 + 'window.WPFnlAiAssistantSettings = ' . wp_json_encode($_common) . ';',
1381 + 'before'
1382 + );
1383 + }
1384 +
1385 +
1386 + /**
1387 + * Where the settings screen fetches its two Vue panels from.
1388 + *
1389 + * Neither bundle is enqueued: only one settings panel in the app needs
1390 + * either of them, and the route is in the fragment, which the server never
1391 + * sees — so loading them on every screen of the admin would cost every
1392 + * screen megabytes for a panel most visitors never open. The panel loads
1393 + * what it needs when it is opened (see pages/Settings/panels/VuePanel.jsx).
1394 + *
1395 + * The stylesheet is empty on a development build, which injects its styles
1396 + * from JavaScript and emits none.
1397 + *
1398 + * @return array Script and stylesheet URL per panel.
1399 + * @since 3.13.8
1400 + */
1401 + private function get_settings_panel_apps()
1402 + {
1403 + return array(
1404 + 'aiAssistant' => array(
1405 + 'script' => plugin_dir_url(__FILE__) . 'assets/dist/js/ai-assistant-settings.min.js',
1406 + 'style' => Wpfnl_functions::get_bundle_style_url('ai-assistant-settings'),
1407 + ),
1408 + 'mcp' => array(
1409 + 'script' => plugin_dir_url(__FILE__) . 'assets/dist/js/mcp-agent-settings.min.js',
1410 + 'style' => Wpfnl_functions::get_bundle_style_url('mcp-agent-settings'),
1411 + ),
1412 + );
1413 + }
1414 +
1415 +
1416 + /**
478 1417 * Register the JavaScript for the admin area.
479 1418 *
480 1419 * @since 1.0.0
481 1420 */
@@ -494,34 +1433,104 @@
494 1433 */
495 1434
496 1435
497 1436 $screen = get_current_screen();
498 -
499 - if('dashboard' === $screen->id || 'plugins' === $screen->id ){
500 - wp_enqueue_script( $this->plugin_name.'-global', plugin_dir_url(__FILE__) . 'assets/js/wpfnl-global.js', ['jquery', 'jquery-ui-sortable'], $this->version, true );
1437 +
1438 +
1439 + $this->enqueue_react_admin_assets($hook);
1440 +
1441 +
1442 + if ('dashboard' === $screen->id || 'plugins' === $screen->id) {
1443 + wp_enqueue_script($this->plugin_name . '-global', plugin_dir_url(__FILE__) . 'assets/js/wpfnl-global.js', ['jquery', 'jquery-ui-sortable'], $this->version, true);
501 1444 }
502 1445
503 -
504 - if ( 'wp-funnels_page_email-builder' === $hook ) {
1446 + if ('wpfunnels_page_email-builder' === $hook) {
1447 + $is_wc_installed = 'no';
1448 + $is_wpfunnels_installed = 'no';
1449 + if (is_plugin_active('woocommerce/woocommerce.php')) {
1450 + $is_wc_installed = 'yes';
1451 + }
1452 + if (is_plugin_active('wpfunnels/wpfunnels.php')) {
1453 + $is_wpfunnels_installed = 'yes';
1454 + }
505 1455 $dependency = require_once WPFNL_PATH . '/admin/assets/dist/email-builder/main.min.asset.php';
506 1456 wp_enqueue_script(
507 - $this->plugin_name . '-email-builder',
508 - plugin_dir_url(__FILE__) . 'assets/dist/email-builder/main.min.js',
509 - $dependency['dependencies'],
510 - $this->version,
511 - true
1457 + $this->plugin_name . '-email-builder',
1458 + plugin_dir_url(__FILE__) . 'assets/dist/email-builder/main.min.js',
1459 + $dependency['dependencies'],
1460 + $this->version,
1461 + true
512 1462 );
513 - wp_localize_script(
514 - $this->plugin_name . '-email-builder',
515 - 'WPFEmailBuilderVars',
516 - array(
517 - 'editor_data_source' => $this->get_editor_source(),
518 - )
519 - );
1463 + wp_localize_script(
1464 + $this->plugin_name . '-email-builder',
1465 + 'WPFEmailBuilderVars',
1466 + array(
1467 + 'editor_data_source' => $this->get_editor_source(),
1468 + 'post_types' => MrmCommon::get_all_post_types(),
1469 + 'is_wc_installed' => $is_wc_installed,
1470 + 'is_mint_active' => Wpfnl_functions::is_mint_mrm_active(),
1471 + )
1472 + );
520 1473 }
521 - if ( in_array($hook, $this->page_hooks ) ) {
522 -
523 - $is_wc_installed = 'no';
1474 +
1475 + // Store Checkout without WooCommerce: power the "Plugin Required" modal
1476 + // rendered by admin/modules/store-checkout/views/woocommerce-required.php.
1477 + if ('wpfunnels_page_' . WPFNL_STORE_CHECKOUT_SLUG === $hook && !Wpfnl_functions::is_wc_active()) {
1478 + $wc_plugin = 'woocommerce/woocommerce.php';
1479 + $wc_status = Wpfnl_functions::is_plugin_installed($wc_plugin) ? 'inactive' : 'not-installed';
1480 + $wc_label = 'not-installed' === $wc_status
1481 + ? __('Install WooCommerce', 'wpfnl')
1482 + : __('Activate WooCommerce', 'wpfnl');
1483 +
1484 + wp_enqueue_script(
1485 + $this->plugin_name . '-store-checkout-woo',
1486 + plugin_dir_url(__FILE__) . 'assets/js/wpfnl-store-checkout-woo.js',
1487 + ['jquery', 'updates'],
1488 + $this->version,
1489 + true
1490 + );
1491 + wp_localize_script($this->plugin_name . '-store-checkout-woo', 'wpfnlStoreCheckoutWoo', array(
1492 + 'ajaxUrl' => admin_url('admin-ajax.php'),
1493 + 'nonce' => wp_create_nonce('wpfnl_activate_plugin_nonce'),
1494 + 'plugin' => $wc_plugin,
1495 + 'slug' => 'woocommerce',
1496 + 'status' => $wc_status,
1497 + 'buttonLabel' => $wc_label,
1498 + 'i18n' => array(
1499 + 'installing' => __('Installing', 'wpfnl'),
1500 + 'activating' => __('Activating', 'wpfnl'),
1501 + 'redirecting' => __('Redirecting', 'wpfnl'),
1502 + 'error' => __('Something went wrong. Please install and activate WooCommerce manually.', 'wpfnl'),
1503 + ),
1504 + ));
1505 + }
1506 +
1507 +
1508 + if (in_array($hook, $this->page_hooks)) {
1509 +
1510 + // Admin header actions — the Knowledge Base, AI Assistant, Settings
1511 + // and Upgrade group rendered into #wpfnl-header-actions from every
1512 + // legacy WPFunnels admin screen. The data goes out under the
1513 + // WPFNAIFunnel global because that is the name
1514 + // admin/src/copilot/services/ai.js reads, and the drawer shares
1515 + // that client with what is left of the copilot bundle.
1516 + $_settings_page_url = admin_url( 'admin.php?page=' . WPFNL_MAIN_PAGE_SLUG ) . '#/settings';
1517 +
1518 + wp_enqueue_script('wpfnl-global-ai-drawer', plugin_dir_url(__FILE__) . 'assets/dist/js/global-ai-drawer.min.js', [], $this->version, true);
1519 + Wpfnl_functions::enqueue_bundle_style( 'global-ai-drawer', 'wpfnl-global-ai-drawer-bundle' );
1520 + wp_localize_script('wpfnl-global-ai-drawer', 'WPFNAIFunnel', array(
1521 + 'adminUrl' => esc_url_raw( admin_url( 'admin.php' ) ),
1522 + 'settingsPageUrl' => esc_url_raw( $_settings_page_url ),
1523 + 'isPro' => (bool) apply_filters( 'wpfunnels/is_pro_license_activated', false ),
1524 + 'upgradeUrl' => 'https://getwpfunnels.com/pricing/',
1525 + 'restApiUrl' => esc_url_raw( get_rest_url() ),
1526 + 'restNonce' => wp_create_nonce( 'wp_rest' ),
1527 + 'rest_url' => esc_url_raw( get_rest_url() ),
1528 + 'nonce' => wp_create_nonce( 'wp_rest' ),
1529 + 'rest_nonce' => wp_create_nonce( 'wp_rest' ),
1530 + ));
1531 +
1532 + $is_wc_installed = 'no';
524 1533 $is_wpfunnels_installed = 'no';
525 1534 if (is_plugin_active('woocommerce/woocommerce.php')) {
526 1535 $is_wc_installed = 'yes';
527 1536 }
@@ -542,27 +1551,32 @@
542 1551 }
543 1552 }
544 1553 }
545 1554
546 - $funnel_id = '';
547 - $funnel_title = '';
548 - $step_id = '';
1555 + $funnel_id = '';
1556 + $funnel_title = '';
1557 + $step_id = '';
549 1558 if (isset($_GET['id'])) {
550 - $funnel_id = $_GET['id'];
551 - $funnel_title = html_entity_decode(get_the_title($funnel_id));
1559 + $funnel_id = $_GET['id'];
1560 + $funnel_title = html_entity_decode(get_the_title($funnel_id));
552 1561 if (isset($_GET['step_id'])) {
553 1562 $step_id = filter_input(INPUT_GET, 'step_id', FILTER_VALIDATE_INT);
554 1563 }
1564 +
1565 + // For store checkout funnels, resolve the checkout step ID when not in URL.
1566 + if ( empty( $step_id ) && 'store_checkout' === get_post_meta( $funnel_id, '_wpfnl_funnel_type', true ) ) {
1567 + $step_id = \WPFunnels\WooCommerce\Wpfnl_Store_Checkout_Conditions::get_checkout_step_id_for_funnel( $funnel_id );
1568 + }
555 1569 }
556 1570
557 1571 /**
558 - * Get funnel preview link
559 - */
560 - $steps = get_post_meta( $funnel_id, '_steps_order', true );
561 - $funnel_preview_link = '#';
1572 + * Get funnel preview link
1573 + */
1574 + $steps = get_post_meta($funnel_id, '_steps_order', true);
1575 + $funnel_preview_link = '#';
562 1576 $response['success'] = false;
563 1577 if ($steps) {
564 - if ( isset($steps[0]) && $steps[0]['id'] ) {
1578 + if (isset($steps[0]) && $steps[0]['id']) {
565 1579 $funnel_preview_link = get_post_permalink($steps[0]['id']);
566 1580 }
567 1581 }
568 1582
@@ -571,205 +1585,293 @@
571 1585 wp_enqueue_editor();
572 1586 wp_enqueue_script('wp-color-picker');
573 1587 wp_enqueue_media();
574 1588
575 - wp_enqueue_script( $this->plugin_name . '-jed', plugin_dir_url(__FILE__) . 'assets/js/jed.js', ['jquery'], $this->version, true );
576 - wp_enqueue_script( $this->plugin_name, plugin_dir_url(__FILE__) . 'assets/js/wpfnl-admin.js', ['jquery', 'jquery-ui-sortable'], $this->version, true );
577 - wp_enqueue_script( $this->plugin_name . '-runtime', plugin_dir_url(__FILE__) . 'assets/dist/runtime/index.min.js', ['jquery', 'wp-i18n', 'wp-util', 'updates', 'wp-color-picker', $this->plugin_name . '-jed'], $this->version, true );
578 - wp_enqueue_script( $this->plugin_name . '-funnel-window', plugin_dir_url(__FILE__) . 'assets/dist/js/funnel-components.min.js', ['jquery', 'wp-i18n', 'wp-util', 'updates', 'wp-color-picker', $this->plugin_name . '-jed', $this->plugin_name . '-runtime'], $this->version, true );
579 - wp_enqueue_script( $this->plugin_name . '-backbone-marionette', plugin_dir_url(__FILE__) . 'assets/lib/backbone/backbone.marionette.min.js', ['backbone'], $this->version, true );
580 - wp_enqueue_script( $this->plugin_name . '-backbone-radio', plugin_dir_url(__FILE__) . 'assets/lib/backbone/backbone.radio.min.js', ['backbone'], $this->version, true );
1589 + wp_enqueue_script($this->plugin_name . '-jed', plugin_dir_url(__FILE__) . 'assets/js/jed.js', ['jquery'], $this->version, true);
1590 + wp_enqueue_script($this->plugin_name, plugin_dir_url(__FILE__) . 'assets/js/wpfnl-admin.js', ['jquery', 'jquery-ui-sortable'], $this->version, true);
581 1591
582 - $general_settings = Wpfnl_functions::get_general_settings();
583 - $builder = $general_settings['builder'];
1592 + wp_enqueue_script($this->plugin_name . '-runtime', plugin_dir_url(__FILE__) . 'assets/dist/runtime/index.min.js', ['jquery', 'wp-i18n', 'wp-util', 'updates', 'wp-color-picker', $this->plugin_name . '-jed'], $this->version, true);
1593 + wp_enqueue_script($this->plugin_name . '-funnel-window', plugin_dir_url(__FILE__) . 'assets/dist/js/funnel-components.min.js', ['jquery', 'wp-i18n', 'wp-util', 'updates', 'wp-color-picker', $this->plugin_name . '-jed', $this->plugin_name . '-runtime'], $this->version, true);
1594 + Wpfnl_functions::enqueue_bundle_style( 'funnel-components', $this->plugin_name . '-funnel-window-bundle' );
1595 + wp_enqueue_script($this->plugin_name . '-backbone-marionette', plugin_dir_url(__FILE__) . 'assets/lib/backbone/backbone.marionette.min.js', ['backbone'], $this->version, true);
1596 + wp_enqueue_script($this->plugin_name . '-backbone-radio', plugin_dir_url(__FILE__) . 'assets/lib/backbone/backbone.radio.min.js', ['backbone'], $this->version, true);
584 1597
1598 + $general_settings = Wpfnl_functions::get_general_settings();
1599 + $builder = $general_settings['builder'];
1600 + $funnel_builder_mode = Wpfnl_functions::get_default_funnel_builder_mode();
1601 + $canvas_view_mode = Wpfnl_functions::get_canvas_view_mode();
1602 +
585 1603 /**
586 1604 * This code snippet will check if pro addons is activated or not. if not activated
587 1605 * Total number of funnels will be maximum 3, otherwise customer can add as more funnels
588 1606 * As they want
589 1607 */
590 - $is_pro_active = apply_filters( 'wpfunnels/is_pro_license_activated', false );
591 - $count_funnels = wp_count_posts('wpfunnels')->publish + wp_count_posts('wpfunnels')->draft;
1608 + $is_pro_active = apply_filters('wpfunnels/is_pro_license_activated', false);
1609 + $count_funnels = Wpfnl_functions::count_non_store_checkout_funnels( array( 'publish', 'draft', 'trash' ) );
1610 + $count_active_funnels = Wpfnl_functions::count_non_store_checkout_funnels();
592 1611 $total_allowed_funnels = 3;
593 1612 if ($is_pro_active) {
594 1613 $total_allowed_funnels = -1;
595 1614 }
1615 + $count_store_checkouts = Wpfnl_functions::count_store_checkout_funnels();
1616 + $total_allowed_store_checkouts = $is_pro_active ? -1 : 3;
596 1617
597 1618 $wc_currency_symbol = '$';
598 - if ( function_exists('get_woocommerce_currency_symbol') ) {
599 - $wc_currency_symbol = html_entity_decode( get_woocommerce_currency_symbol() );
1619 + if (function_exists('get_woocommerce_currency_symbol')) {
1620 + $wc_currency_symbol = html_entity_decode(get_woocommerce_currency_symbol());
600 1621 }
601 1622 $ld_currency_symbol = '$';
602 - if ( function_exists('learndash_get_currency_symbol') ) {
603 - $ld_currency_symbol = html_entity_decode( learndash_get_currency_symbol() );
1623 + if (function_exists('learndash_get_currency_symbol')) {
1624 + $ld_currency_symbol = html_entity_decode(learndash_get_currency_symbol());
604 1625 }
605 1626
606 - wp_localize_script( $this->plugin_name, 'WPFunnelVars', array(
607 - 'ajaxurl' => admin_url( 'admin-ajax.php' ),
608 - 'rest_api_url' => get_rest_url(),
609 - 'security' => wp_create_nonce('wpfnl-admin'),
610 - 'admin_url' => admin_url(),
611 - 'edit_funnel_url' => admin_url('admin.php?page=edit_funnel'),
612 - 'i18n' => array( 'wpfnl' => $this->get_jed_locale_data( 'wpfnl' ) ),
613 - 'is_wc_installed' => $is_wc_installed,
614 - 'is_wpfunnels_installed' => $is_wpfunnels_installed,
615 - 'products' => $products,
616 - 'funnel_id' => $funnel_id,
617 - 'step_id' => $step_id,
618 - 'funnel_title' => $funnel_title,
619 - 'funnel_preview_link' => $funnel_preview_link,
620 - 'site_url' => site_url(),
621 - 'image_path' => WPFNL_URL . 'admin/assets/images',
622 - 'placeholder_image_path' => WPFNL_URL . 'admin/assets/images/ob_placeholder.png',
623 - 'nonce' => wp_create_nonce('wp_rest'),
624 - 'isNewFunnel' => \Wpfnl_Activator::is_new_install() ? true : false,
625 - 'isProActivated' => $is_pro_active,
626 - 'totalFunnels' => $count_funnels,
627 - 'totalAllowedFunnels' => $total_allowed_funnels,
628 - 'builder' => $builder,
629 - 'dependencyPlugins' => Wpfnl_functions::get_dependency_plugins_status(),
630 - 'allCoupons' => Wpfnl_functions::get_all_coupons(),
631 - 'isAnyPluginMissing' => Wpfnl_functions::is_any_plugin_missing(),
632 - 'isGlobalFunnelActivated' => Wpfnl_functions::is_global_funnel_activated(),
633 - 'isGlobalFunnel' => Wpfnl_functions::is_global_funnel( $funnel_id ),
634 - 'GBFVersion' => defined('WPFNL_PRO_GB_VERSION') ? WPFNL_PRO_GB_VERSION : '',
635 - 'isSkipOffer' => $this->is_skip_offer(),
636 - 'paymentMethod' => $this->get_payment_method(),
637 - 'shippingMethod' => $this->get_shipping_method(),
638 - 'isGbf' => $this->maybe_gbf( $funnel_id ),
639 - 'isLms' => $this->maybe_lms( $funnel_id ),
640 - 'isLmsActivated' => Wpfnl_functions::is_lms_addon_active(),
641 - 'isLmsSettings' => Wpfnl_functions::is_enable_lms_settings(),
642 - 'isLmsDisbaled' => $this->maybe_lms_settings_disbaled(),
643 - 'lmsVersion' => defined( 'WPFNL_PRO_LMS_VERSION' ) || defined( 'WPFUNNELS_PRO_LMS_VERSION' ) ? version_compare(LEARNDASH_VERSION, '4.2.1.1', '>=') : false,
644 - 'global_funnel_type' => Wpfnl_functions::get_global_funnel_type(),
645 - 'individual_funnel_type' => get_post_meta( $funnel_id, '_wpfnl_funnel_type', true ),
646 - 'gbf_set_condition_steps' => $this->get_gbf_steps($funnel_id),
647 - 'mint_steps' => $this->mint_step_settings($funnel_id),
648 - 'mint_triggers' => Wpfnl_functions::get_mint_triggers(),
649 - 'mint_actions' => Wpfnl_functions::get_mint_actions(),
650 - 'mint_tags' => Wpfnl_functions::get_mint_contact_groups( 'tags' ),
651 - 'mint_lists' => Wpfnl_functions::get_mint_contact_groups( 'lists' ),
652 - 'mint_sequences' => Wpfnl_functions::get_sequences(),
653 - 'obList' => Wpfnl_functions::get_all_ob( $funnel_id ),
654 - 'upsellSteps' => Wpfnl_functions::get_selected_steps( 'upsell', $funnel_id ),
655 - 'downsellSteps' => Wpfnl_functions::get_selected_steps( 'downsell', $funnel_id ),
656 - 'landingAndCustomSteps' => Wpfnl_functions::get_selected_steps( 'landing', $funnel_id ),
657 - 'customSteps' => Wpfnl_functions::get_selected_steps( 'custom', $funnel_id ),
658 - 'onlySettingsSteps' => Wpfnl_functions::get_only_settings_steps(),
659 - 'cfeDefault' => Wpfnl_functions::get_cfe_default_fields(),
660 - 'adminEmail' => get_bloginfo( 'admin_email' ),
661 - 'mailMintEmail' => Wpfnl_functions::get_mailmint_email(),
662 - 'emailBuilderUrl' => admin_url().'admin.php?page=email-builder',
663 - 'currentUrl' => (isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] === 'on' ? "https" : "http") . "://$_SERVER[HTTP_HOST]$_SERVER[REQUEST_URI]",
664 - 'wc_currency' => $wc_currency_symbol,
665 - 'ld_currency' => $ld_currency_symbol,
1627 + $product_url = esc_url_raw(
1628 + add_query_arg(
1629 + array(
1630 + 'post_type' => 'product',
1631 + 'wpfunnels' => 'yes',
1632 + ),
1633 + admin_url('post-new.php')
1634 + )
1635 + );
1636 +
1637 + wp_localize_script($this->plugin_name, 'WPFunnelVars', array(
1638 + 'ajaxurl' => admin_url('admin-ajax.php'),
1639 + 'rest_api_url' => get_rest_url(),
1640 + 'security' => wp_create_nonce('wpfnl-admin'),
1641 + 'import_nonce' => wp_create_nonce('wpfnl_import_funnels_nonce'),
1642 + 'admin_url' => admin_url(),
1643 + 'edit_funnel_url' => admin_url('admin.php?page=edit_funnel'),
1644 + 'i18n' => array('wpfnl' => $this->get_jed_locale_data('wpfnl')),
1645 + 'current_user_id' => get_current_user_id(),
1646 + 'is_wc_installed' => $is_wc_installed,
1647 + 'is_wpfunnels_installed' => $is_wpfunnels_installed,
1648 + 'products' => $products,
1649 + 'funnel_id' => $funnel_id,
1650 + 'step_id' => $step_id,
1651 + 'funnel_title' => $funnel_title,
1652 + 'funnel_preview_link' => $funnel_preview_link,
1653 + 'site_url' => site_url(),
1654 + 'image_path' => WPFNL_URL . 'admin/assets/images',
1655 + 'placeholder_image_path' => WPFNL_URL . 'admin/assets/images/ob_placeholder.png',
1656 + 'nonce' => wp_create_nonce('wp_rest'),
1657 + 'isNewFunnel' => \Wpfnl_Activator::is_new_install() ? true : false,
1658 + 'isProActivated' => $is_pro_active,
1659 + 'totalFunnels' => $count_funnels,
1660 + 'count_active_funnels' => $count_active_funnels,
1661 + 'totalStoreCheckouts' => $count_store_checkouts,
1662 + 'totalAllowedStoreCheckouts' => $total_allowed_store_checkouts,
1663 + 'product_url' => $product_url,
1664 + 'totalAllowedFunnels' => $total_allowed_funnels,
1665 + 'builder' => $builder,
1666 + 'funnel_builder_mode' => $funnel_builder_mode,
1667 + 'canvas_view_mode' => $canvas_view_mode,
1668 + 'dependencyPlugins' => Wpfnl_functions::get_dependency_plugins_status(),
1669 + 'isAnyPluginMissing' => Wpfnl_functions::is_any_plugin_missing(),
1670 + 'isGlobalFunnelActivated' => Wpfnl_functions::is_global_funnel_activated(),
1671 + 'isGlobalFunnel' => Wpfnl_functions::is_global_funnel($funnel_id),
1672 + 'GBFVersion' => defined('WPFNL_PRO_GBF_VERSION') ? WPFNL_PRO_GBF_VERSION : ( defined('WPFNL_PRO_GB_VERSION') ? WPFNL_PRO_GB_VERSION : '' ),
1673 + 'isSkipOffer' => $this->is_skip_offer(),
1674 + 'paymentMethod' => $this->get_payment_method(),
1675 + 'shippingMethod' => $this->get_shipping_method(),
1676 + 'isGbf' => $this->maybe_gbf($funnel_id),
1677 + 'isLms' => $this->maybe_lms($funnel_id),
1678 + 'lmsProvider' => $this->get_lms_provider(),
1679 + 'isLmsActivated' => Wpfnl_functions::is_lms_addon_active(),
1680 + 'isAnyLmsPluginActive' => Wpfnl_functions::is_any_lms_plugin_active(),
1681 + 'isLmsSettings' => Wpfnl_functions::is_enable_lms_settings(),
1682 + 'isLmsDisbaled' => $this->maybe_lms_settings_disbaled(),
1683 + 'lmsVersion' => (defined('WPFNL_PRO_LMS_VERSION') || defined('WPFUNNELS_PRO_LMS_VERSION')) && defined('LEARNDASH_VERSION') ? version_compare(LEARNDASH_VERSION, '4.2.1.1', '>=') : false,
1684 + 'individual_funnel_type' => get_post_meta($funnel_id, '_wpfnl_funnel_type', true),
1685 + 'isStoreCheckout' => get_post_meta($funnel_id, '_wpfnl_funnel_type', true) === 'store_checkout',
1686 + 'isSaleFunnel' => get_post_meta($funnel_id, '_wpfnl_funnel_type', true) === 'wc' || get_post_meta($funnel_id, '_wpfnl_funnel_type', true) === 'store_checkout',
1687 + 'gbf_set_condition_steps' => $this->get_gbf_steps($funnel_id),
1688 + 'reconfigurable_condition_data' => $this->get_reconfigurable_condition_data($funnel_id),
1689 + 'mint_steps' => $this->mint_step_settings($funnel_id),
1690 + 'mint_tags' => Wpfnl_functions::get_mint_contact_groups('tags'),
1691 + 'mint_lists' => Wpfnl_functions::get_mint_contact_groups('lists'),
1692 + 'mint_sequences' => Wpfnl_functions::get_sequences(),
1693 + 'obList' => Wpfnl_functions::get_all_ob($funnel_id),
1694 + 'upsellSteps' => Wpfnl_functions::get_selected_steps('upsell', $funnel_id),
1695 + 'downsellSteps' => Wpfnl_functions::get_selected_steps('downsell', $funnel_id),
1696 + 'landingAndCustomSteps' => Wpfnl_functions::get_selected_steps('landing', $funnel_id),
1697 + 'customSteps' => Wpfnl_functions::get_selected_steps('custom', $funnel_id),
1698 + 'onlySettingsSteps' => Wpfnl_functions::get_only_settings_steps(),
1699 + 'isWCML' => defined('WCML_VERSION'),
1700 + 'cfeDefault' => Wpfnl_functions::get_cfe_default_fields(),
1701 + 'adminEmail' => get_bloginfo('admin_email'),
1702 + 'siteName' => get_option('blogname'),
1703 + 'mailMintEmail' => Wpfnl_functions::get_mailmint_email(),
1704 + 'email_settings' => Wpfnl_functions::get_mailmint_email_settings(),
1705 + 'mint_twillo_settings' => Wpfnl_functions::get_mailmint_twillo_settings(),
1706 + 'emailBuilderUrl' => admin_url() . 'admin.php?page=email-builder',
1707 + 'currentUrl' => (isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] === 'on' ? "https" : "http") . "://$_SERVER[HTTP_HOST]$_SERVER[REQUEST_URI]",
1708 + 'wc_currency' => $wc_currency_symbol,
1709 + 'ld_currency' => $ld_currency_symbol,
1710 + 'getText' => Wpfnl_functions::get_text(),
1711 + 'user_role_manager_data' => Wpfnl_functions::get_user_role_settings(),
1712 + 'availableGateways' => Wpfnl_functions::get_enabled_payment_gateways(),
1713 + 'guidedTour' => Wpfnl_functions::get_guided_tour(),
1714 + 'isWPFIntegrationActive' => apply_filters( 'wpfunnels/is_integration_license_active', false ),
666 1715 ));
667 - wp_localize_script( $this->plugin_name, 'template_library_object',
1716 + wp_localize_script(
1717 + $this->plugin_name,
1718 + 'template_library_object',
668 1719 array(
669 - 'ajaxurl' => esc_url_raw(admin_url('admin-ajax.php')),
670 - 'rest_api_url' => esc_url_raw(get_rest_url()),
671 - 'dashboard_url' => esc_url_raw(admin_url('admin.php?page=' . WPFNL_MAIN_PAGE_SLUG)),
672 - 'settings_url' => esc_url_raw(admin_url('admin.php?page=settings')),
673 - 'home_url' => esc_url_raw(home_url()),
674 - 'funnel_id' => $funnel_id,
675 - 'is_pro' => Wpfnl_functions::is_pro_license_activated(),
676 - 'is_ab_tesing_available'=> defined('WPFNL_PRO_VERSION') ? version_compare( WPFNL_PRO_VERSION, "1.7.3", ">=" ) : false,
677 - 'is_webhook_licensed' => Wpfnl_functions::is_webhook_license_activated(),
678 - 'pro_url' => add_query_arg('wpfnl-dashboard', '1', GETWPFUNNEL_HOME_URL),
679 - 'nonce' => wp_create_nonce('wp_rest'),
680 - 'image_path' => WPFNL_URL . 'admin/assets/images',
681 - 'template_type' => Wpfnl_functions::get_template_types(),
682 - 'countries' => Wpfnl_functions::get_countries(),
683 - 'supported_steps' => Wpfnl_functions::get_supported_step_type(),
684 - 'is_mint_active' => Wpfnl_functions::is_mint_mrm_active(),
685 - 'settingsSteps' => Wpfnl_functions::get_settings_steps(),
686 - )
1720 + 'ajaxurl' => esc_url_raw(admin_url('admin-ajax.php')),
1721 + 'rest_api_url' => esc_url_raw(get_rest_url()),
1722 + 'dashboard_url' => ( $funnel_id && get_post_meta( $funnel_id, '_wpfnl_funnel_type', true ) === 'store_checkout' )
1723 + ? esc_url_raw(admin_url('admin.php?page=store_checkout'))
1724 + : esc_url_raw(admin_url('admin.php?page=' . WPFNL_FUNNEL_PAGE_SLUG)),
1725 + 'settings_url' => esc_url_raw(admin_url('admin.php?page=settings')),
1726 + 'home_url' => esc_url_raw(home_url()),
1727 + 'funnel_id' => $funnel_id,
1728 + 'is_pro' => Wpfnl_functions::is_pro_license_activated(),
1729 + 'is_ab_tesing_available' => defined('WPFNL_PRO_VERSION') ? version_compare(WPFNL_PRO_VERSION, "1.7.3", ">=") : false,
1730 + 'is_webhook_licensed' => Wpfnl_functions::is_webhook_license_activated(),
1731 + 'pro_url' => add_query_arg('wpfnl-dashboard', '1', GETWPFUNNEL_PRICING_URL),
1732 + 'nonce' => wp_create_nonce('wp_rest'),
1733 + 'image_path' => WPFNL_URL . 'admin/assets/images',
1734 + 'template_type' => Wpfnl_functions::get_template_types(),
1735 + 'countries' => Wpfnl_functions::get_countries(),
1736 + 'supported_steps' => Wpfnl_functions::get_supported_step_type(),
1737 + 'is_mint_active' => Wpfnl_functions::is_mint_mrm_active(),
1738 + 'settingsSteps' => Wpfnl_functions::get_settings_steps(),
1739 + 'isRemote' => Wpfnl_functions::maybe_remote_funnel(),
1740 + 'isStoreCheckout' => 'wpfunnels_page_store_checkout' === $hook,
1741 + )
687 1742 );
688 -
689 - wp_localize_script( $this->plugin_name, 'CheckoutStep',
1743 +
1744 + wp_localize_script(
1745 + $this->plugin_name,
1746 + 'CheckoutStep',
690 1747 array(
691 - 'ajaxurl' => esc_url_raw(admin_url('admin-ajax.php')),
692 - 'rest_api_url' => esc_url_raw(get_rest_url()),
693 - 'wc_currency' => $wc_currency_symbol,
694 - 'nonce' => wp_create_nonce('wp_rest'),
695 - 'security' => wp_create_nonce('wpfnl-admin'),
696 - 'image_path' => WPFNL_URL . 'admin/assets/images',
697 - 'tooltipIcon' => WPFNL_URL . 'admin/partials/icons/question-tooltip-icon.php',
698 - 'imageUploadIcon' => WPFNL_URL . 'admin/partials/icons/image-upload-icon.php',
699 - 'step_id' => $step_id,
700 - 'back' => add_query_arg(
701 - array(
702 - 'page' => WPFNL_EDIT_FUNNEL_SLUG,
703 - 'id' => filter_input(INPUT_GET, 'id', FILTER_VALIDATE_INT),
704 - 'step_id' => $step_id,
705 - ),
706 - admin_url('admin.php')
707 - )
708 - )
1748 + 'ajaxurl' => esc_url_raw(admin_url('admin-ajax.php')),
1749 + 'rest_api_url' => esc_url_raw(get_rest_url()),
1750 + 'wc_currency' => $wc_currency_symbol,
1751 + 'nonce' => wp_create_nonce('wp_rest'),
1752 + 'security' => wp_create_nonce('wpfnl-admin'),
1753 + 'image_path' => WPFNL_URL . 'admin/assets/images',
1754 + 'tooltipIcon' => WPFNL_URL . 'admin/partials/icons/question-tooltip-icon.php',
1755 + 'imageUploadIcon' => WPFNL_URL . 'admin/partials/icons/image-upload-icon.php',
1756 + 'step_id' => $step_id,
1757 + 'back' => add_query_arg(
1758 + array(
1759 + 'page' => WPFNL_EDIT_FUNNEL_SLUG,
1760 + 'id' => filter_input(INPUT_GET, 'id', FILTER_VALIDATE_INT),
1761 + 'step_id' => $step_id,
1762 + ),
1763 + admin_url('admin.php')
1764 + ),
1765 + 'priceConfig' => Wpfnl_functions::get_wc_price_config(),
1766 + )
709 1767 );
710 1768
711 - wp_localize_script( $this->plugin_name, 'wpfnl_addons_vars',
1769 + wp_localize_script(
1770 + $this->plugin_name,
1771 + 'wpfnl_addons_vars',
712 1772 array(
713 - 'addons' => Wpfnl_functions::get_supported_addons(),
714 - 'gbf_conditions' => Wpfnl_functions::get_supported_gbf_offer_condition(),
715 - 'categories' => Wpfnl_functions::get_categories_gbf_offer_condition(),
716 - 'role' => Wpfnl_functions::get_all_user_roles(),
717 - 'tags' => Wpfnl_functions::get_all_tags(),
718 - 'shippingClasses'=> Wpfnl_functions::get_shipping_classes(),
719 - )
1773 + 'addons' => Wpfnl_functions::get_supported_addons(),
1774 + 'gbf_conditions' => Wpfnl_functions::get_supported_gbf_offer_condition(),
1775 + 'categories' => Wpfnl_functions::get_categories_gbf_offer_condition(),
1776 + 'role' => Wpfnl_functions::get_all_user_roles(),
1777 + 'tags' => Wpfnl_functions::get_all_tags(),
1778 + 'shippingClasses' => Wpfnl_functions::get_shipping_classes(),
1779 + )
720 1780 );
721 1781
1782 +
722 1783 /**
723 1784 * Localize scripts for funnel window
724 - *
1785 + *
725 1786 * @var $localize
726 1787 */
727 1788
728 1789 $notices = $this->get_builder_notice();
729 1790
730 - $localize = apply_filters( 'wpfunnels/funnel_window_admin_localize', array() );
731 - wp_localize_script( $this->plugin_name . '-funnel-window', 'wpfunnels_funnel_localize', array($localize));
732 - do_action( 'wpfunnels_after_scripts_loaded' );
1791 + $localize = apply_filters('wpfunnels/funnel_window_admin_localize', array());
1792 + wp_localize_script($this->plugin_name . '-funnel-window', 'wpfunnels_funnel_localize', array($localize));
1793 +
1794 +
1795 + do_action('wpfunnels_after_scripts_loaded');
733 1796 }
734 1797 }
735 1798
736 1799
1800 + public function get_lms_provider()
1801 + {
1802 + $lms_settings = get_option('_wpfunnels_lms_settings', array());
1803 + $provider_id = isset($lms_settings['lms_provider']) ? $lms_settings['lms_provider'] : 'learndash';
1804 + return $provider_id;
1805 + }
1806 +
1807 +
737 1808 /**
1809 + * Get the reconfigurable condition data for a funnel
1810 + */
1811 + public function get_reconfigurable_condition_data($funnel_id)
1812 + {
1813 + if (!$funnel_id) {
1814 + return [];
1815 + }
1816 + $data = get_post_meta($funnel_id, '_wpfnl_reconfigurable_condition_data', true);
1817 + if (!is_array($data) || empty($data)) {
1818 + return [];
1819 + }
1820 + return $data;
1821 + }
1822 +
1823 +
1824 + /**
738 1825 * Get all setps where GBF conditions is already set
739 - *
1826 + *
740 1827 * @param Integer $funnel_id
741 - *
1828 + *
742 1829 * @since 2.6.1
743 1830 */
744 - private function get_gbf_steps( $funnel_id ){
1831 + private function get_gbf_steps($funnel_id)
1832 + {
745 1833 $gbf_steps = [];
746 - if( $funnel_id ){
747 - $is_gbf = get_post_meta($funnel_id, 'is_global_funnel', true);
748 - if( 'yes' === $is_gbf ){
749 - $steps = Wpfnl_functions::get_steps( $funnel_id );
750 - if( is_array($steps) ){
751 - foreach($steps as $step ){
752 - if( isset($step['id'], $step['step_type']) ){
753 - if( 'checkout' === $step['step_type'] ){
754 - $conditions = get_post_meta( $funnel_id, 'global_funnel_start_condition', true );
755 - if( is_array($conditions) ){
756 - array_push( $gbf_steps, $step['id'] );
1834 + if ($funnel_id) {
1835 + $funnel_type = get_post_meta($funnel_id, '_wpfnl_funnel_type', true);
1836 + $is_gbf = get_post_meta($funnel_id, 'is_global_funnel', true);
1837 +
1838 + if ('yes' === $is_gbf) {
1839 + $steps = Wpfnl_functions::get_steps($funnel_id);
1840 + if (is_array($steps)) {
1841 + foreach ($steps as $step) {
1842 + if (isset($step['id'], $step['step_type'])) {
1843 + if ('checkout' === $step['step_type']) {
1844 + $conditions = get_post_meta($funnel_id, 'global_funnel_start_condition', true);
1845 + if (is_array($conditions)) {
1846 + array_push($gbf_steps, $step['id']);
757 1847 }
758 - }elseif( 'upsell' === $step['step_type'] || 'downsell' === $step['step_type'] ){
759 -
760 - $conditions = get_post_meta( $step['id'], 'global_funnel_'.$step['step_type'].'_rules', true );
761 - if( is_array($conditions) ){
762 - array_push( $gbf_steps, $step['id'] );
1848 + }
1849 + elseif ('upsell' === $step['step_type'] || 'downsell' === $step['step_type']) {
1850 + $conditions = get_post_meta($step['id'], 'global_funnel_' . $step['step_type'] . '_rules', true);
1851 + if (is_array($conditions)) {
1852 + array_push($gbf_steps, $step['id']);
763 1853 }
764 1854 }
765 1855 }
766 -
767 1856 }
768 1857 }
1858 + } elseif ('store_checkout' === $funnel_type) {
1859 + $steps = Wpfnl_functions::get_steps($funnel_id);
1860 + if (is_array($steps)) {
1861 + foreach ($steps as $step) {
1862 + if (isset($step['id'], $step['step_type']) && 'checkout' === $step['step_type']) {
1863 + $raw = get_post_meta($step['id'], '_wpfnl_store_checkout_condition', true);
1864 + $condition = is_string($raw) ? json_decode($raw, true) : $raw;
1865 + if (is_array($condition) && isset($condition['condition_type']) && 'rules' === $condition['condition_type']) {
1866 + array_push($gbf_steps, $step['id']);
1867 + }
1868 + }
1869 + }
1870 + }
769 1871 }
770 1872 }
771 -
1873 +
772 1874 return $gbf_steps;
773 1875 }
774 1876
775 1877
@@ -779,34 +1881,36 @@
779 1881 * @param string $domain Translation domain.
780 1882 *
781 1883 * @return array
782 1884 */
783 - public function get_jed_locale_data( $domain, $language_dir = null ) {
784 - $plugin_translations = $this->get_translations_for_plugin_domain( $domain, $language_dir );
785 - $translations = get_translations_for_domain( $domain );
1885 + public function get_jed_locale_data($domain, $language_dir = null)
1886 + {
1887 + $plugin_translations = $this->get_translations_for_plugin_domain($domain, $language_dir);
1888 + $translations = get_translations_for_domain($domain);
786 1889
787 1890 $locale = array(
788 - 'domain' => $domain,
1891 + 'domain' => $domain,
789 1892 'locale_data' => array(
790 1893 $domain => array(
791 1894 '' => array(
792 1895 'domain' => $domain,
793 - 'lang' => is_admin() ? get_user_locale() : get_locale(),
1896 + 'lang' => is_admin() ? get_user_locale() : get_locale(),
794 1897 ),
795 1898 ),
796 1899 ),
797 1900 );
798 1901
799 - if ( ! empty( $translations->headers['Plural-Forms'] ) ) {
800 - $locale['locale_data'][ $domain ]['']['plural_forms'] = $translations->headers['Plural-Forms'];
801 - } else if ( ! empty( $plugin_translations['header'] ) ) {
802 - $locale['locale_data'][ $domain ]['']['plural_forms'] = $plugin_translations['header']['Plural-Forms'];
1902 + if (!empty($translations->headers['Plural-Forms'])) {
1903 + $locale['locale_data'][$domain]['']['plural_forms'] = $translations->headers['Plural-Forms'];
803 1904 }
1905 + else if (!empty($plugin_translations['header'])) {
1906 + $locale['locale_data'][$domain]['']['plural_forms'] = $plugin_translations['header']['Plural-Forms'];
1907 + }
804 1908
805 - $entries = array_merge( $plugin_translations['translations'], $translations->entries );
1909 + $entries = array_merge($plugin_translations['translations'], $translations->entries);
806 1910
807 - foreach ( $entries as $msgid => $entry ) {
808 - $locale['locale_data'][ $domain ][ $msgid ] = $entry->translations;
1911 + foreach ($entries as $msgid => $entry) {
1912 + $locale['locale_data'][$domain][$msgid] = $entry->translations;
809 1913 }
810 1914
811 1915 return $locale;
812 1916 }
@@ -819,26 +1923,27 @@
819 1923 * @param string $language_dir
820 1924 *
821 1925 * @return array
822 1926 */
823 - public function get_translations_for_plugin_domain( $domain, $language_dir = null ) {
824 - if ( $language_dir == null ) {
825 - $language_dir = WPFNL_PATH . '/languages/';
1927 + public function get_translations_for_plugin_domain($domain, $language_dir = null)
1928 + {
1929 + if ($language_dir == null) {
1930 + $language_dir = WPFNL_PATH . '/languages/';
826 1931 }
827 - $languages = get_available_languages( $language_dir );
1932 + $languages = get_available_languages($language_dir);
828 1933 $get_site_lang = is_admin() ? get_user_locale() : get_locale();
829 - $mo_file_name = $domain . '-' . $get_site_lang;
830 - $translations = [];
1934 + $mo_file_name = $domain . '-' . $get_site_lang;
1935 + $translations = [];
831 1936
832 - if ( in_array( $mo_file_name, $languages ) && file_exists( $language_dir . $mo_file_name . '.mo' ) ) {
1937 + if (in_array($mo_file_name, $languages) && file_exists($language_dir . $mo_file_name . '.mo')) {
833 1938 $mo = new \MO();
834 - if ( $mo->import_from_file( $language_dir . $mo_file_name . '.mo' ) ) {
1939 + if ($mo->import_from_file($language_dir . $mo_file_name . '.mo')) {
835 1940 $translations = $mo->entries;
836 1941 }
837 1942 }
838 1943
839 1944 return [
840 - 'header' => isset( $mo ) ? $mo->headers : '',
1945 + 'header' => isset($mo) ? $mo->headers : '',
841 1946 'translations' => $translations,
842 1947 ];
843 1948 }
844 1949
@@ -845,26 +1950,32 @@
845 1950
846 1951 /**
847 1952 * Check GBF version for skip offer
848 1953 */
849 - public function is_skip_offer(){
850 - if( defined('WPFNL_PRO_GB_VERSION') ){
851 - return version_compare( WPFNL_PRO_GB_VERSION, "1.0.9", ">=" );
1954 + public function is_skip_offer()
1955 + {
1956 + // GBF is now built into WPFunnels Pro — always available when GBF is activated.
1957 + if ( Wpfnl_functions::is_global_funnel_activated() ) {
1958 + return true;
852 1959 }
1960 + // Legacy: standalone addon still active.
1961 + if ( defined('WPFNL_PRO_GB_VERSION') ) {
1962 + return version_compare( WPFNL_PRO_GB_VERSION, '1.0.9', '>=' );
1963 + }
853 1964 return false;
854 1965 }
855 1966
856 - public function mint_step_settings( $funnel_id ){
857 - $steps = get_post_meta( $funnel_id, '_steps', true );
1967 + public function mint_step_settings($funnel_id)
1968 + {
1969 + $steps = get_post_meta($funnel_id, '_steps', true);
858 1970 $mint_steps = [];
859 - if( is_array($steps) ){
860 - foreach( $steps as $step ){
1971 + if (is_array($steps)) {
1972 + foreach ($steps as $step) {
861 1973 $mint_settings['stepID'] = $step['id'];
862 - $value = get_post_meta( $step['id'], '_wpfnl_automation_steps', true );
1974 + $value = get_post_meta($step['id'], '_wpfnl_automation_steps', true);
863 1975 $mint_settings['value'] = is_array($value) ? $value : [];
864 - array_push($mint_steps,$mint_settings);
1976 + array_push($mint_steps, $mint_settings);
865 1977 }
866 -
867 1978 }
868 1979 return $mint_steps;
869 1980 }
870 1981
@@ -870,15 +1981,15 @@
870 1981
871 1982
872 1983 /**
873 1984 * Define w2cloud routes
874 - *
1985 + *
875 1986 * @return array
876 1987 */
877 1988 public function get_wpfnl_routes()
878 1989 {
879 1990 $routes = array(
880 - array(
1991 + array(
881 1992 'path' => '/',
882 1993 'name' => 'home',
883 1994 'component' => 'Home'
884 1995 )
@@ -887,19 +1998,20 @@
887 1998 }
888 1999
889 2000 /**
890 2001 * Get all active Payment Method
891 - *
2002 + *
892 2003 * @return array
893 2004 */
894 - public function get_payment_method(){
895 - if( Wpfnl_functions::is_wc_active() ) {
2005 + public function get_payment_method()
2006 + {
2007 + if (Wpfnl_functions::is_wc_active()) {
896 2008 $gateways = WC()->payment_gateways->get_available_payment_gateways();
897 2009 $enabled_gateways = [];
898 2010
899 - if( $gateways ) {
900 - foreach( $gateways as $key => $gateway ) {
901 - if( $gateway->enabled == 'yes' ) {
2011 + if ($gateways) {
2012 + foreach ($gateways as $key => $gateway) {
2013 + if ($gateway->enabled == 'yes') {
902 2014 $enabled_gateways[$key] = $gateway->method_title;
903 2015 }
904 2016 }
905 2017 }
@@ -909,17 +2021,18 @@
909 2021 }
910 2022
911 2023 /**
912 2024 * Get all shipping methods
913 - *
2025 + *
914 2026 * @return array
915 2027 */
916 - public function get_shipping_method(){
917 - if( Wpfnl_functions::is_wc_active() ) {
2028 + public function get_shipping_method()
2029 + {
2030 + if (Wpfnl_functions::is_wc_active()) {
918 2031 $available_shipping = WC()->shipping()->get_shipping_methods();
919 2032 $enabled_shipping = [];
920 - if( $available_shipping ) {
921 - foreach( $available_shipping as $key => $shipping ) {
2033 + if ($available_shipping) {
2034 + foreach ($available_shipping as $key => $shipping) {
922 2035 $enabled_shipping[$key] = $shipping->method_title;
923 2036 }
924 2037 }
925 2038 return $enabled_shipping;
@@ -929,17 +2042,18 @@
929 2042
930 2043
931 2044 /**
932 2045 * Show funnel steps on reading
933 - *
2046 + *
934 2047 * @param $output
935 - *
2048 + *
936 2049 * @return mixed
937 2050 */
938 - public function show_funnel_steps_on_reading( $output ) {
2051 + public function show_funnel_steps_on_reading($output)
2052 + {
939 2053 global $pagenow;
940 - if ( ( 'options-reading.php' === $pagenow || 'customize.php' === $pagenow ) && preg_match( '#page_on_front#', $output ) ) {
941 - $output = $this->show_steps_options( $output );
2054 + if (('options-reading.php' === $pagenow || 'customize.php' === $pagenow) && preg_match('#page_on_front#', $output)) {
2055 + $output = $this->show_steps_options($output);
942 2056 }
943 2057 return $output;
944 2058 }
945 2059
@@ -947,45 +2061,46 @@
947 2061 /**
948 2062 * Show steps pages on reading
949 2063 *
950 2064 * @param $output
951 - *
2065 + *
952 2066 * @return string
953 2067 */
954 - private function show_steps_options( $output ) {
955 - $options = '';
2068 + private function show_steps_options($output)
2069 + {
2070 + $options = '';
956 2071
957 2072 $steps = get_posts(
958 2073 array(
959 - 'post_type' => WPFNL_STEPS_POST_TYPE,
960 - 'posts_per_page' => - 1,
961 - 'numberposts' => 100,
962 - 'orderby' => 'title',
963 - 'order' => 'ASC',
964 - 'post_status' => 'publish',
965 - 'meta_query' => array(
966 - 'relation' => 'OR',
2074 + 'post_type' => WPFNL_STEPS_POST_TYPE,
2075 + 'posts_per_page' => -1,
2076 + 'numberposts' => 100,
2077 + 'orderby' => 'title',
2078 + 'order' => 'ASC',
2079 + 'post_status' => 'publish',
2080 + 'meta_query' => array(
2081 + 'relation' => 'OR',
967 2082 array(
968 - 'key' => '_step_type',
969 - 'value' => 'landing',
970 - ),
2083 + 'key' => '_step_type',
2084 + 'value' => 'landing',
2085 + ),
971 2086 array(
972 - 'key' => '_step_type',
973 - 'value' => 'checkout',
974 - )
975 - ),
976 - )
2087 + 'key' => '_step_type',
2088 + 'value' => 'checkout',
2089 + )
2090 + ),
2091 + )
977 2092 );
978 2093
979 - if( $steps && is_array( $steps ) ) {
980 - $front_page_id = get_option( 'page_on_front' );
981 - foreach ( $steps as $step ) {
982 - $selected = selected( $front_page_id, $step->ID, false );
983 - $post_type_obj = get_post_type_object( $step->post_type );
984 - $options .= "<option value=\"{$step->ID}\"{$selected}>{$step->post_title} (WPFunnels {$post_type_obj->labels->singular_name})</option>";
2094 + if ($steps && is_array($steps)) {
2095 + $front_page_id = get_option('page_on_front');
2096 + foreach ($steps as $step) {
2097 + $selected = selected($front_page_id, $step->ID, false);
2098 + $post_type_obj = get_post_type_object($step->post_type);
2099 + $options .= "<option value=\"{$step->ID}\"{$selected}>{$step->post_title} (WPFunnels {$post_type_obj->labels->singular_name})</option>";
985 2100 }
986 - $options .= '</select>';
987 - $output = str_replace( '</select>', $options, $output );
2101 + $options .= '</select>';
2102 + $output = str_replace('</select>', $options, $output);
988 2103 }
989 2104 return $output;
990 2105 }
991 2106
@@ -994,12 +2109,13 @@
994 2109 * Get notice for builder
995 2110 *
996 2111 * @return array
997 2112 */
998 - public function get_builder_notice() {
2113 + public function get_builder_notice()
2114 + {
999 2115 $builder = Wpfnl_functions::get_builder_type();
1000 2116 $notices = array();
1001 - if( 'divi-builder' === $builder ) {
2117 + if ('divi-builder' === $builder) {
1002 2118 $notices['notices'][] = array();
1003 2119 }
1004 2120 return $notices;
1005 2121 }
@@ -1007,13 +2123,14 @@
1007 2123
1008 2124 /**
1009 2125 * Change text in order details page
1010 2126 */
1011 - public function wpfnl_customizing_checkout_text( $translated_text, $untranslated_text, $domain ){
2127 + public function wpfnl_customizing_checkout_text($translated_text, $untranslated_text, $domain)
2128 + {
1012 2129
1013 - if( is_admin() ){
1014 - if ( $translated_text == 'Coupon(s):' ) {
1015 - $translated_text = __( 'Discount:', 'wpfnl' );
2130 + if (is_admin()) {
2131 + if ($translated_text == 'Coupon(s):') {
2132 + $translated_text = __('Discount:', 'wpfnl');
1016 2133 }
1017 2134 }
1018 2135
1019 2136 return $translated_text;
@@ -1023,15 +2140,16 @@
1023 2140 /**
1024 2141 * Change local for if this is funnel canvas window
1025 2142 *
1026 2143 * @param $local
1027 - *
2144 + *
1028 2145 * @return string
1029 2146 *
1030 2147 * @since 2.3.8
1031 2148 */
1032 - public function change_local_for_funnel_canvas($local) {
1033 - if( Wpfnl_functions::is_funnel_canvas_window() ) {
2149 + public function change_local_for_funnel_canvas($local)
2150 + {
2151 + if (Wpfnl_functions::is_funnel_canvas_window()) {
1034 2152 return 'en_US';
1035 2153 }
1036 2154 return $local;
1037 2155 }
@@ -1041,11 +2159,11 @@
1041 2159 * Add type meta
1042 2160 *
1043 2161 * @since 2.4.4
1044 2162 */
1045 - public function wpfnl_add_type_meta(){
2163 + public function wpfnl_add_type_meta()
2164 + {
1046 2165 Wpfnl_functions::add_type_meta();
1047 -
1048 2166 }
1049 2167
1050 2168
1051 2169 /**
@@ -1051,18 +2169,19 @@
1051 2169 /**
1052 2170 * Maybe Global funnel
1053 2171 *
1054 2172 * @param String
1055 - *
2173 + *
1056 2174 * @return Bool
1057 2175 * @since 2.4.11
1058 2176 */
1059 - public function maybe_gbf( $funnel_id ){
2177 + public function maybe_gbf($funnel_id)
2178 + {
1060 2179
1061 - if( is_plugin_active( 'wpfunnels-pro-gbf/wpfnl-pro-gb.php' )){
1062 - if( $funnel_id ){
1063 - $is_gbf = get_post_meta($funnel_id,'is_global_funnel',true);
1064 - if( 'yes' == $is_gbf ){
2180 + if ( Wpfnl_functions::is_global_funnel_activated() ) {
2181 + if ($funnel_id) {
2182 + $is_gbf = get_post_meta($funnel_id, 'is_global_funnel', true);
2183 + if ('yes' == $is_gbf) {
1065 2184 return true;
1066 2185 }
1067 2186 }
1068 2187 }
@@ -1074,12 +2193,13 @@
1074 2193 * Check funnel type is lms or not by funnel_id
1075 2194 *
1076 2195 * @return Bool
1077 2196 */
1078 - public function maybe_lms( $funnel_id ){
2197 + public function maybe_lms($funnel_id)
2198 + {
1079 2199
1080 - $type = get_post_meta($funnel_id,'_wpfnl_funnel_type',true);
1081 - if( 'lms' === $type ){
2200 + $type = get_post_meta($funnel_id, '_wpfnl_funnel_type', true);
2201 + if ('lms' === $type) {
1082 2202 return true;
1083 2203 }
1084 2204 return false;
1085 2205 }
@@ -1086,9 +2206,10 @@
1086 2206
1087 2207 /**
1088 2208 * May be lms settings is disbaled
1089 2209 */
1090 - public function maybe_lms_settings_disbaled(){
2210 + public function maybe_lms_settings_disbaled()
2211 + {
1091 2212 return Wpfnl_functions::is_wc_active() && Wpfnl_functions::is_lms_addon_active();
1092 2213 }
1093 2214
1094 2215
@@ -1093,144 +2214,294 @@
1093 2214
1094 2215
1095 2216 /**
1096 2217 * Trigger when any plugin is deactivated
1097 - *
2218 + *
1098 2219 * @param $filename
1099 - *
2220 + *
1100 2221 * @since 2.5.1
1101 2222 */
1102 - public function wpfnl_deactivated_plugin( $filename ){
1103 -
1104 - if( 'woocommerce/woocommerce.php' === $filename ){
1105 - if( !Wpfnl_functions::is_lms_addon_active() ){
1106 - Wpfnl_functions::update_funnel_type_to_lead();
1107 - }
2223 + public function wpfnl_deactivated_plugin($filename)
2224 + {
2225 + if ('woocommerce/woocommerce.php' === $filename) {
1108 2226 Wpfnl_functions::wpfnl_delete_transient();
1109 - }elseif( 'sfwd-lms/sfwd_lms.php' === $filename ){
1110 - if( !Wpfnl_functions::is_wc_active() ){
1111 - Wpfnl_functions::update_funnel_type_to_lead();
1112 - }
2227 + }
2228 + elseif ('sfwd-lms/sfwd_lms.php' === $filename) {
1113 2229 Wpfnl_functions::wpfnl_delete_transient();
1114 - }elseif( 'wpfunnels-pro-lms/wpfunnels-pro-lms.php' === $filename ){
2230 + }
2231 + elseif ('wpfunnels-pro-lms/wpfunnels-pro-lms.php' === $filename) {
1115 2232 Wpfnl_functions::wpfnl_delete_transient();
1116 2233 }
1117 -
2234 + elseif ('qubely/qubely.php' === $filename) {
2235 + Wpfnl_functions::wpfnl_delete_transient();
2236 + delete_option(WPFNL_TEMPLATES_OPTION_KEY . '_wc');
2237 + delete_option(WPFNL_TEMPLATES_OPTION_KEY . '_lms');
2238 + delete_transient('wpfunnels_remote_template_data_wc_' . WPFNL_VERSION);
2239 + delete_transient('wpfunnels_remote_template_data_lms_' . WPFNL_VERSION);
2240 + delete_transient('wpfunnels_remote_template_data_lead_' . WPFNL_VERSION);
2241 + delete_transient('wpfunnels_remote_template_data_store_checkout_' . WPFNL_VERSION);
2242 + }
1118 2243 }
1119 2244
1120 2245
1121 2246 /**
1122 2247 * Trigger when any plugin is activated
1123 - *
2248 + *
1124 2249 * @param $filename
1125 - *
2250 + *
1126 2251 * @since 2.5.1
1127 2252 */
1128 - public function wpfnl_activated_plugin( $filename ){
1129 - if( 'woocommerce/woocommerce.php' === $filename ){
2253 + public function wpfnl_activated_plugin($filename)
2254 + {
2255 + if ('woocommerce/woocommerce.php' === $filename) {
1130 2256 Wpfnl_functions::wpfnl_delete_transient();
1131 - }elseif( 'sfwd-lms/sfwd_lms.php' === $filename ){
1132 - if( Wpfnl_functions::is_lms_addon_active() ){
2257 + }
2258 + elseif ('sfwd-lms/sfwd_lms.php' === $filename) {
2259 + if (Wpfnl_functions::is_lms_addon_active()) {
1133 2260 Wpfnl_functions::wpfnl_delete_transient();
1134 2261 }
1135 - }elseif( 'wpfunnels-pro-lms/wpfunnels-pro-lms.php' === $filename ){
2262 + }
2263 + elseif ('wpfunnels-pro-lms/wpfunnels-pro-lms.php' === $filename) {
1136 2264 Wpfnl_functions::wpfnl_delete_transient();
1137 2265 }
2266 + elseif ('qubely/qubely.php' === $filename) {
2267 + Wpfnl_functions::wpfnl_delete_transient();
2268 + delete_option(WPFNL_TEMPLATES_OPTION_KEY . '_wc');
2269 + delete_option(WPFNL_TEMPLATES_OPTION_KEY . '_lms');
2270 + delete_transient('wpfunnels_remote_template_data_wc_' . WPFNL_VERSION);
2271 + delete_transient('wpfunnels_remote_template_data_lms_' . WPFNL_VERSION);
2272 + delete_transient('wpfunnels_remote_template_data_lead_' . WPFNL_VERSION);
2273 + delete_transient('wpfunnels_remote_template_data_store_checkout_' . WPFNL_VERSION);
2274 + }
1138 2275 }
1139 2276
1140 2277
1141 2278
1142 - /**
1143 - * Get editor source data
1144 - *
1145 - * @return array
1146 - * @since 1.0.0
1147 - */
1148 - private function get_editor_source() {
1149 - // get product categories for email builder.
1150 - $wc_categories = $this->get_formatted_wc_categories();
1151 - $wp_categories = $this->get_formatted_wp_post_categories();
2279 + /**
2280 + * Get editor source data
2281 + *
2282 + * @return array
2283 + * @since 1.0.0
2284 + */
2285 + private function get_editor_source()
2286 + {
2287 + // get product categories for email builder.
2288 + $wc_categories = $this->get_formatted_wc_categories();
2289 + $wp_categories = $this->get_formatted_wp_post_categories();
1152 2290
1153 - return apply_filters(
1154 - 'plugin_hook_name',
1155 - array(
1156 - 'product_categories' => $wc_categories,
1157 - 'post_categories' => $wp_categories,
1158 - 'placeholder_image' => ''
1159 - )
1160 - );
1161 - }
2291 + return apply_filters(
2292 + 'plugin_hook_name',
2293 + array(
2294 + 'product_categories' => $wc_categories,
2295 + 'post_categories' => $wp_categories,
2296 + 'placeholder_image' => ''
2297 + )
2298 + );
2299 + }
1162 2300
1163 2301
1164 2302
1165 - /**
1166 - * Get the WooCommerce product categories
1167 - *
1168 - * @return array
1169 - * @since 1.0.0
1170 - */
1171 - private function get_formatted_wc_categories() {
1172 - $taxonomy = 'product_cat';
1173 - $orderby = 'name';
1174 - $show_count = 0;
1175 - $pad_counts = 0;
1176 - $hierarchical = 1;
1177 - $title = '';
1178 - $empty = 0;
2303 + /**
2304 + * Get the WooCommerce product categories
2305 + *
2306 + * @return array
2307 + * @since 1.0.0
2308 + */
2309 + private function get_formatted_wc_categories()
2310 + {
2311 + $taxonomy = 'product_cat';
2312 + $orderby = 'name';
2313 + $show_count = 0;
2314 + $pad_counts = 0;
2315 + $hierarchical = 1;
2316 + $title = '';
2317 + $empty = 0;
1179 2318
1180 - $args = array(
1181 - 'taxonomy' => $taxonomy,
1182 - 'orderby' => $orderby,
1183 - 'show_count' => $show_count,
1184 - 'pad_counts' => $pad_counts,
1185 - 'hierarchical' => $hierarchical,
1186 - 'title_li' => $title,
1187 - 'hide_empty' => $empty,
1188 - );
1189 - $product_categories = get_categories( $args );
1190 - $wc_categories = array();
1191 - foreach ( $product_categories as $product_cat ) {
1192 - $wc_categories[] = array(
1193 - 'value' => $product_cat->term_id,
1194 - 'label' => $product_cat->name,
1195 - );
1196 - }
2319 + $args = array(
2320 + 'taxonomy' => $taxonomy,
2321 + 'orderby' => $orderby,
2322 + 'show_count' => $show_count,
2323 + 'pad_counts' => $pad_counts,
2324 + 'hierarchical' => $hierarchical,
2325 + 'title_li' => $title,
2326 + 'hide_empty' => $empty,
2327 + );
2328 + $product_categories = get_categories($args);
2329 + $wc_categories = array();
2330 + foreach ($product_categories as $product_cat) {
2331 + $wc_categories[] = array(
2332 + 'value' => $product_cat->term_id,
2333 + 'label' => $product_cat->name,
2334 + );
2335 + }
1197 2336
1198 - return $wc_categories;
1199 - }
2337 + return $wc_categories;
2338 + }
1200 2339
1201 2340
1202 - /**
1203 - * Get the WordPress post categories
1204 - *
1205 - * @return array
1206 - * @since 1.0.0
1207 - */
1208 - private function get_formatted_wp_post_categories() {
1209 - $taxonomy = 'category';
1210 - $orderby = 'name';
1211 - $show_count = 0;
1212 - $pad_counts = 0;
1213 - $hierarchical = 1;
1214 - $title = '';
1215 - $empty = 0;
2341 + /**
2342 + * Get the WordPress post categories
2343 + *
2344 + * @return array
2345 + * @since 1.0.0
2346 + */
2347 + private function get_formatted_wp_post_categories()
2348 + {
2349 + $taxonomy = 'category';
2350 + $orderby = 'name';
2351 + $show_count = 0;
2352 + $pad_counts = 0;
2353 + $hierarchical = 1;
2354 + $title = '';
2355 + $empty = 0;
1216 2356
1217 - $args = array(
1218 - 'taxonomy' => $taxonomy,
1219 - 'orderby' => $orderby,
1220 - 'show_count' => $show_count,
1221 - 'pad_counts' => $pad_counts,
1222 - 'hierarchical' => $hierarchical,
1223 - 'title_li' => $title,
1224 - 'hide_empty' => $empty,
1225 - );
1226 - $post_categories = get_categories( $args );
1227 - $categories = array();
1228 - foreach ( $post_categories as $post_cat ) {
1229 - $categories[] = array(
1230 - 'value' => $post_cat->term_id,
1231 - 'label' => $post_cat->name,
1232 - );
1233 - }
1234 - return $categories;
1235 - }
1236 -}
2357 + $args = array(
2358 + 'taxonomy' => $taxonomy,
2359 + 'orderby' => $orderby,
2360 + 'show_count' => $show_count,
2361 + 'pad_counts' => $pad_counts,
2362 + 'hierarchical' => $hierarchical,
2363 + 'title_li' => $title,
2364 + 'hide_empty' => $empty,
2365 + );
2366 + $post_categories = get_categories($args);
2367 + $categories = array();
2368 + foreach ($post_categories as $post_cat) {
2369 + $categories[] = array(
2370 + 'value' => $post_cat->term_id,
2371 + 'label' => $post_cat->name,
2372 + );
2373 + }
2374 + return $categories;
2375 + }
2376 +
2377 +
2378 + /**
2379 + * Add custom capabilities to administrator role
2380 + *
2381 + * @since 3.1.3
2382 + */
2383 + public function attach_admin_capabilities()
2384 + {
2385 + global $wp_roles;
2386 + $capabilities = array(
2387 + 'wpf_manage_funnels',
2388 + );
2389 + foreach ($capabilities as $cap) {
2390 + $wp_roles->add_cap('administrator', $cap);
2391 + }
2392 + }
2393 +
2394 + /**
2395 + * Checks if the current page slug matches the provided slug.
2396 + *
2397 + * @param $slug
2398 + * @return bool
2399 + * @since 3.5.0
2400 + */
2401 + public function is_current_page($slug)
2402 + {
2403 + if (empty($slug)) {
2404 + return false;
2405 + }
2406 + $current_page_slug = isset($_GET['page']) ? sanitize_text_field($_GET['page']) : '';
2407 + if ($slug === $current_page_slug) {
2408 + return true;
2409 + }
2410 + return false;
2411 + }
2412 +
2413 + /**
2414 + * Add body class for WPFunnels admin pages.
2415 + *
2416 + * @param string $classes Current body classes.
2417 + * @return string Modified body classes.
2418 + * @since 3.9.5
2419 + */
2420 + public function add_wpfunnels_admin_body_class($classes)
2421 + {
2422 + // Check if we're on a WPFunnels admin page.
2423 + $screen = get_current_screen();
2424 +
2425 + if ($screen && (in_array($screen->id, $this->page_hooks) || 'wpfunnels_page_' . WPFNL_AI_FUNNEL_PAGE_SLUG === $screen->id)) {
2426 + $classes .= ' wpfunnels-body';
2427 + }
2428 +
2429 + // The React admin styles the WordPress content column itself, so it gets
2430 + // its own class instead of the legacy one.
2431 + if ($screen && in_array($screen->id, $this->get_react_admin_hooks(), true)) {
2432 + $classes .= ' rwpfnl-page';
2433 + }
2434 +
2435 + return $classes;
2436 + }
2437 +
2438 +
2439 + /**
2440 + * Create contact on mailmint when user allows to track data
2441 + *
2442 + * @param array $data
2443 + *
2444 + * @since 3.3.1
2445 + */
2446 + public function wpfunnels_tracker_optin($data)
2447 + {
2448 + if (isset($data['admin_email'], $data['first_name'], $data['last_name'])) {
2449 + $json_body_data = json_encode([
2450 + 'email' => $data['admin_email'],
2451 + 'first_name' => $data['first_name'],
2452 + 'last_name' => $data['last_name'],
2453 + ]);
2454 +
2455 + $webHookUrl = [
2456 + 'https://staging-useraccount.kinsta.cloud/?mailmint=1&route=webhook&topic=contact&hash=b258136d-6759-4e91-bbab-0b7397af6dc7'
2457 + ];
2458 +
2459 + try {
2460 + if (!empty($webHookUrl)) {
2461 + foreach ($webHookUrl as $url) {
2462 + wp_remote_request($url, [
2463 + 'method' => 'POST',
2464 + 'headers' => [
2465 + 'Content-Type' => 'application/json',
2466 + ],
2467 + 'body' => $json_body_data
2468 + ]);
2469 + }
2470 + }
2471 + }
2472 + catch (\Exception $e) {
2473 + }
2474 + }
2475 + }
2476 +
2477 + /**
2478 + * Creates a new MRM contact.
2479 + *
2480 + * @param array $data The data for the contact.
2481 + * @return void
2482 + */
2483 + public function create_mrm_contact($data)
2484 + {
2485 + if (!isset($data['data']['user_email'])) {
2486 + return;
2487 + }
2488 + // Get double opt-in settings.
2489 + $default = \MRM\Common\MrmCommon::double_optin_default_configuration();
2490 + $settings = get_option('_mrm_optin_settings', $default);
2491 + $enable = isset($settings['enable']) ? $settings['enable'] : false;
2492 +
2493 + $user_data = array(
2494 + 'email' => $data['data']['user_email'],
2495 + 'first_name' => isset($data['data']['first_name']) ? $data['data']['first_name'] : '',
2496 + 'last_name' => isset($data['data']['last_name']) ? $data['data']['last_name'] : '',
2497 + 'meta_fields' => array(
2498 + 'phone_number' => isset($data['data']['phone_number']) ? $data['data']['phone_number'] : '',
2499 + ),
2500 + 'status' => $enable ? 'pending' : 'subscribed',
2501 + 'source' => 'WPFunnels',
2502 + );
2503 +
2504 + $mail_mint_object = new \WPFunnels\Integrations\MailMint($user_data);
2505 + $mail_mint_object->create_or_update_contact();
2506 + }
2507 +}