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 | includes/class-wpfnl.php +136 -114 2.8.16 → 3.13.3 View file →
@@ -14,8 +14,9 @@
14 14 * @author RexTheme <[email protected]>
15 15 */
16 16 namespace WPFunnels;
17 17
18 +use Wpfnl_Activator;
18 19 use Wpfnl_i18n;
19 20 use Wpfnl_Loader;
20 21 use Wpfnl_Logger;
21 22 use WPFunnels\Admin\OptinField\OptinField;
@@ -22,10 +23,12 @@
22 23 use WPFunnels\Admin\SetupWizard;
23 24 use WPFunnels\Admin\Wpfnl_Admin as Admin;
24 25 use WPFunnels\Admin\Notices\Notice;
25 26 use WPFunnels\Admin\Banner\SpecialOccasionBanner;
27 +use WPFunnels\Admin\Banner\SetupReminderBanner;
26 28 use WPFunnels\Ajax_Handler\Ajax_Handler;
27 29 use WPFunnels\Classes\OrderBumpActions\Wpfnl_Order_Bump_Action;
30 +use WPFunnels\Classes\TestimonialActions\Wpfnl_Testimonial_Action;
28 31 use WPFunnels\Compatibility\CartLift\Wpfnl_Cart_Lift_Compatibility;
29 32 use WPFunnels\Compatibility\SlimSeo\Wpfnl_Slim_Seo_Compatibility;
30 33 use WPFunnels\Frontend\Wpfnl_Public;
31 34 use WPFunnels\Frontend\Wpfnl_Public as Frontend;
@@ -33,8 +36,10 @@
33 36 use WPFunnels\Data_Store\Wpfnl_Funnel_Store_Data as Funnel_Store;
34 37 use WPFunnels\Data_Store\Wpfnl_Steps_Store_Data as Step_Store;
35 38 use WPFunnels\Menu\Wpfnl_Menus as Menu;
36 39 use WPFunnels\Modules\Wpfnl_Modules_Manager as Module_Manager;
40 +use WPFunnels\Report\OptinRecorder;
41 +use WPFunnels\Report\StatHookHandler;
37 42 use WPFunnels\Rest\Server;
38 43 // use WPFunnels\Shortcodes\Wpfnl_Shortcode;
39 44 use WPFunnels\Shortcodes\Wpfnl_Shortcodes;
40 45 use WPFunnels\Widgets\Wpfnl_Widgets_Manager as Widget_Manager;
@@ -40,11 +45,11 @@
40 45 use WPFunnels\Widgets\Wpfnl_Widgets_Manager as Widget_Manager;
41 46 use WPFunnels\Batch\Elementor\Wpfnl_Batch;
42 47 use WPFunnels\Meta\Wpfnl_Default_Meta;
43 48 use WPFunnels\Compatibility\Wpfnl_Compatibility;
44 -
45 -
46 -
49 +use WPFunnels\Export\Wpfnl_Export;
50 +use WPFunnels\Import\Wpfnl_Import;
51 +use WPFunnels\Report\Reporting;
47 52 /**
48 53 * The file that defines the core plugin class
49 54 *
50 55 * A class definition that includes attributes and functions used across both the
@@ -99,8 +104,10 @@
99 104
100 105
101 106 public $order_bump_actions;
102 107
108 + public $testimonial_actions;
109 +
103 110 public $meta;
104 111
105 112 public $frontend;
106 113
@@ -108,8 +115,13 @@
108 115
109 116 public $shortcodes;
110 117 public $shortcode;
111 118
119 +
120 + public $export_funnel;
121 +
122 + public $import_funnel;
123 +
112 124 /**
113 125 * Admin notice object
114 126 *
115 127 * @var string $admin_notice
@@ -114,10 +126,10 @@
114 126 *
115 127 * @var string $admin_notice
116 128 */
117 129 protected $admin_notice;
118 -
119 -
130 +
131 +
120 132 /**
121 133 * Admin banner object
122 134 *
123 135 * @var string $admin_banner
@@ -123,11 +135,28 @@
123 135 * @var string $admin_banner
124 136 */
125 137 protected $admin_banner;
126 138
139 + /**
140 + * Setup reminder banner object
141 + *
142 + * @var SetupReminderBanner $setup_reminder_banner
143 + * @since 3.6.0
144 + */
145 + protected $setup_reminder_banner;
146 +
127 147 protected $optin_field;
128 148
129 149 /**
150 + * Reporting object
151 + *
152 + * @var $reporting
153 + *
154 + * @since 3.2.0
155 + */
156 + protected $reporting;
157 +
158 + /**
130 159 * The loader that's responsible for maintaining and registering all hooks that power
131 160 * the plugin.
132 161 *
133 162 * @since 1.0.0
@@ -144,9 +173,9 @@
144 173 * @var string $plugin_name The string used to uniquely identify this plugin.
145 174 */
146 175 protected $plugin_name;
147 176
148 -
177 +
149 178 /**
150 179 * Object variable for Compatibility class
151 180 *
152 181 * @since
@@ -166,9 +195,9 @@
166 195
167 196
168 197 /**
169 198 * Ajax handler
170 - *
199 + *
171 200 * @var $ajax_handler
172 201 */
173 202 protected $ajax_handler;
174 203
@@ -180,10 +209,10 @@
180 209 * @access protected
181 210 * @var $cart_lift
182 211 */
183 212 protected $cart_lift;
184 -
185 213
214 +
186 215 /**
187 216 * Object variable for Slim_Seo_Compatibility class
188 217 *
189 218 * @since
@@ -191,9 +220,29 @@
191 220 * @var $slim_seo
192 221 */
193 222 protected $slim_seo;
194 223
224 +
225 + /**
226 + * StatHookHandler Object
227 + *
228 + * @var $stat_hooks_handler
229 + * @type StatHookHandler
230 + * @since 3.2.0
231 + */
232 + protected $stat_hooks_handler;
233 +
234 +
195 235 /**
236 + * OptinRecorder Object
237 + *
238 + * @var $optin_recorder
239 + * @type OptinRecorder
240 + * @since 3.2.0
241 + */
242 + protected $optin_recorder;
243 +
244 + /**
196 245 * Instance.
197 246 *
198 247 * Ensures only one instance of the plugin class is loaded or can be loaded.
199 248 *
@@ -242,35 +291,39 @@
242 291 add_action( 'admin_notices', array('WPFunnels\Admin\Notices\Notice', 'woocommerce_dependency_notice') );
243 292 return;
244 293 }*/
245 294
295 + /**
296 + * Store Checkout needs WooCommerce. While it is missing, keep the
297 + * activation endpoint registered so the Store Checkout screen can
298 + * install/activate WooCommerce without leaving the page.
299 + *
300 + * @since 3.13.1
301 + */
302 + if ( ! Wpfnl_functions::is_wc_active() ) {
303 + add_action( 'wp_ajax_plugin_install_success', array( $this, 'plugin_install_success' ) );
304 + }
305 +
246 306 // load core modules
247 307 add_action( 'admin_footer', array($this, 'admin_footer_style') );
248 308 add_action( 'wpfunnels/pro_init', array( $this, 'after_pro_plugin_init' ) );
249 - add_action( 'plugins_loaded', array($this, 'load_plugin'), 99 );
309 + add_action( 'plugins_loaded', array($this, 'load_plugin'), 9 );
250 310
251 311 $this->set_locale();
252 312 $this->init_hooks();
253 - if( !Wpfnl_functions::is_pro_license_activated() || ( Wpfnl_functions::is_pro_license_activated() && !version_compare( WPFNL_PRO_VERSION, "1.7.3", ">=" ) ) ){
254 - add_filter( 'wpfunnels/update_funnel_data_response', [$this, 'update_funnel_data_response'], 10 );
255 - }
256 -
257 - if( !Wpfnl_functions::is_pro_license_activated() || !Wpfnl_functions::is_mint_mrm_active() || ( Wpfnl_functions::is_pro_license_activated() && !version_compare( WPFNL_PRO_VERSION, "1.7.6", ">=" ) ) ){
258 - add_filter( 'wpfunnels/update_funnel_data_response', [$this, 'update_mint_funnel_data_response'], 10 );
259 - }
260 313 }
261 314
262 315
263 316 /**
264 317 * update funnel data response
265 - *
318 + *
266 319 * @param Array $response
267 - *
320 + *
268 321 * @since 1.7.8
269 322 * @return Array $response
270 323 */
271 324 public function update_mint_funnel_data_response( $response ){
272 -
325 +
273 326 $automation_steps = [];
274 327 if( is_array($response) && !empty($response['funnel_data']['drawflow']['Home']['data']) ){
275 328 $steps_order = $response['funnel_data']['drawflow']['Home']['data'];
276 329 if( is_array($steps_order) ){
@@ -288,97 +341,8 @@
288 341 return $response;
289 342 }
290 343
291 344
292 - /**
293 - * Update funnel data response
294 - *
295 - * @param Array $response
296 - *
297 - * @return Array $response
298 - * @since 2.6.2
299 - */
300 - public function update_funnel_data_response( $response ){
301 - $ab_data = [];
302 - if( is_array($response) && !empty($response['funnel_data']['drawflow']['Home']['data']) ){
303 - $steps_order = $steps_order = $response['funnel_data']['drawflow']['Home']['data'];
304 - if( is_array($steps_order) ){
305 - foreach( $steps_order as $key=>$step ){
306 - if( isset( $step['data']['step_id'] ) ){
307 - $step_id = $step['data']['step_id'];
308 - $default_settings = $this->get_default_start_setting( $step_id );
309 - $ab_data['step_'.$step_id]['data'] = $default_settings;
310 - $ab_data[]['success'] = true;
311 - }
312 - }
313 - }
314 - }
315 - $response['ab_data'] = $ab_data;
316 - return $response;
317 - }
318 -
319 -
320 - /**
321 - * Get default start settings for AB testing
322 - *
323 - * @param Int $step_id
324 - *
325 - * @return array
326 - * @since 2.6.2
327 - */
328 - public function get_default_start_setting( $step_id ){
329 - if( $step_id ){
330 - $step_edit_link = get_edit_post_link( $step_id );
331 - $funnel_id = Wpfnl_functions::get_funnel_id_from_step( $step_id );
332 -
333 - $utm_settings = Wpfnl_functions::get_funnel_utm_settings( $funnel_id );
334 - $view_link = get_post_permalink($step_id);
335 -
336 - if ( $utm_settings[ 'utm_enable' ] == 'on' ) {
337 - unset( $utm_settings[ 'utm_enable' ] );
338 - $view_link = add_query_arg( $utm_settings, $view_link );
339 - $view_link = strtolower( $view_link );
340 - }
341 -
342 - if( 'elementor' == Wpfnl_functions::get_builder_type() ){
343 - $step_edit_link = str_replace('&amp;','&',$step_edit_link);
344 - $step_edit_link = str_replace('edit','elementor',$step_edit_link);
345 - }
346 - $default_settings = [
347 - 'is_ab_enabled' => '',
348 - 'start_settings' => [
349 - 'auto_winner' => [
350 - 'is_enabled' => '',
351 - 'conditions' =>
352 - [
353 - 'index' => 'trafiic',
354 - 'value' => 70
355 - ]
356 - ],
357 - 'winner' => '',
358 - 'is_started' => '',
359 - 'start_date' => date( 'Y-m-d H:i:s' ),
360 - 'variations' => [
361 - [
362 - 'id' => $step_id,
363 - 'traffic' => 100,
364 - 'step_type'=> get_post_meta($step_id,'_step_type',true),
365 - 'variation_type' => 'original',
366 - 'step_edit_link' => $step_edit_link,
367 - 'step_view_link' => $view_link,
368 - 'step_title' => get_the_title($step_id),
369 - 'conversion' => 0,
370 - 'visit' => 0,
371 - 'shouldShowAnalytics' => false,
372 - ],
373 - ],
374 - ]
375 - ];
376 - }
377 - return $default_settings ?? [];
378 - }
379 -
380 -
381 345 /**
382 346 * Enqueue funnel admin common scripts
383 347 */
384 348 public function enqueue_scripts() {
@@ -386,8 +350,17 @@
386 350 wp_localize_script( $this->plugin_name. '-common', 'wpfnl_common_vars', array(
387 351 'ajaxurl' => admin_url( 'admin-ajax.php' ),
388 352 'wpfnl_activate_plugin_nonce' => wp_create_nonce('wpfnl_activate_plugin_nonce')
389 353 ));
354 +
355 + $post_id = get_the_ID();
356 + $custom_css = get_post_meta( $post_id, 'rex_gutenberg_css', true );
357 +
358 + if ( $custom_css ) {
359 + wp_register_style( 'rex-gutenberg-css', false );
360 + wp_enqueue_style( 'rex-gutenberg-css' );
361 + wp_add_inline_style( 'rex-gutenberg-css', $custom_css );
362 + }
390 363 }
391 364
392 365
393 366 /**
@@ -455,11 +428,10 @@
455 428
456 429 do_action( 'wpfunnels/init' );
457 430 }
458 431
459 -
460 432 public function init() {
461 - $this->admin = new Admin( $this->get_plugin_name(), $this->get_version() );
433 + $this->admin = new Admin( $this->get_plugin_name(), $this->get_version() );
462 434 $this->frontend = Wpfnl_Public::getInstance();
463 435 $this->cpt = new CPT();
464 436 $this->menu = new Menu();
465 437 $this->funnel_store = new Funnel_Store();
@@ -464,13 +436,16 @@
464 436 $this->menu = new Menu();
465 437 $this->funnel_store = new Funnel_Store();
466 438 $this->step_store = new Step_Store();
467 439 $this->template_manager = new TemplateLibrary\Manager();
440 +
468 441 $this->widget_manager = Widget_Manager::getInstance()->init();
469 442 $this->page_templates = new PageTemplates\Manager();
470 443 $this->admin_notice = new Notice();
471 - $this->admin_banner = new SpecialOccasionBanner('black-friday', '2023-11-23 19:00:00', '2023-12-05 23:59:59', 'https://getwpfunnels.com/pricing/?utm_source=notification-cta&utm_medium=wpf-plugin&utm_campaign=BFCM23');
444 + $this->admin_banner = new SpecialOccasionBanner('4thjuly', '2026-07-02 00:00:01', '2026-07-07 23:59:59', 'https://getwpfunnels.com/pricing/?utm_source=plugin&utm_medium=dashboard-banner-wpf&utm_campaign=4THJULY2026');
445 + $this->setup_reminder_banner = new SetupReminderBanner();
472 446 $this->order_bump_actions = new Wpfnl_Order_Bump_Action();
447 + $this->testimonial_actions = new Wpfnl_Testimonial_Action();
473 448 $this->meta = new Wpfnl_Default_Meta();
474 449 $this->shortcodes = Wpfnl_Shortcodes::getInstance()->init();
475 450 $this->ajax_handler = new Ajax_Handler();
476 451 $this->optin_field = new OptinField();
@@ -475,8 +450,50 @@
475 450 $this->ajax_handler = new Ajax_Handler();
476 451 $this->optin_field = new OptinField();
477 452 $this->log = Wpfnl_Logger::getInstance()->initialize_logging();
478 453 $this->compatibility = new Wpfnl_Compatibility();
454 +
455 + $this->stat_hooks_handler = new StatHookHandler();
456 + $this->optin_recorder = new OptinRecorder();
457 +
458 + // Initialize Payment Gateway Factory
459 + if ( Wpfnl_functions::is_wc_active() ) {
460 + Gateway\Payment_Gateways_Factory::getInstance();
461 +
462 + // Initialize Offer Module (upsell/downsell) only if Pro is not active
463 + // When Pro is active, it handles offer functionality
464 + if ( ! Wpfnl_functions::is_wpfnl_pro_activated() ) {
465 + new Offer\Wpfnl_Offer();
466 + }
467 +
468 + // Store Checkout: override the WooCommerce default checkout with the
469 + // WPFunnels Store Checkout step (only when a step is configured).
470 + require_once WPFNL_DIR . 'includes/core/woocommerce/class-wpfnl-store-checkout-override.php';
471 + \WPFunnels\WooCommerce\Wpfnl_Store_Checkout_Override::getInstance();
472 + }
473 +
474 + $this->export_funnel = Wpfnl_Export::getInstance()->init_ajax();
475 + $this->import_funnel = new Wpfnl_Import();
476 +
477 + $this->reporting = Reporting::get_instance();
478 + $this->reporting->init();
479 +
480 + // AI copilot module: settings migration + conversation tables.
481 + \WPFunnels\AI\AIInit::init();
482 +
483 + // Tool layer: WordPress Abilities (always, when available) + the MCP
484 + // server for external agents (gated on the `_wpfnl_mcp_enabled` option).
485 + // Ability registration must NOT depend on that option — the in-plugin
486 + // AI copilot resolves its own tools through the same registry
487 + // (ToolGateway -> wp_get_abilities()), so turning off the external
488 + // MCP endpoint would otherwise silently leave the copilot with zero
489 + // tools to call.
490 + if ( \WPFunnels\MCP\MCPInit::abilitiesApiAvailable() ) {
491 + $expose_mcp_server = 'no' !== get_option( '_wpfnl_mcp_enabled', 'yes' );
492 + ( new \WPFunnels\MCP\MCPInit() )->init( $expose_mcp_server );
493 + }
494 +
495 + Wpfnl_Activator::init();
479 496 }
480 497
481 498
482 499
@@ -498,8 +515,12 @@
498 515 */
499 516 private function load_dependencies()
500 517 {
501 518
519 + /**
520 + * Require the action scheduler
521 + */
522 + require_once plugin_dir_path(dirname(__FILE__)) . 'vendor/woocommerce/action-scheduler/action-scheduler.php';
502 523
503 524 /**
504 525 * The class responsible for auto loading all files of the
505 526 * core plugin.
@@ -586,9 +607,9 @@
586 607 private function set_locale()
587 608 {
588 609 $plugin_i18n = new Wpfnl_i18n();
589 610
590 - $this->loader->add_action('plugins_loaded', $plugin_i18n, 'load_plugin_textdomain');
611 + $this->loader->add_action('init', $plugin_i18n, 'load_plugin_textdomain');
591 612
592 613 }
593 614
594 615
@@ -711,8 +732,11 @@
711 732 */
712 733 public function init_setup_wizard() {
713 734 add_action('init', array( $this, 'register_setup_wizard_page' ));
714 735 add_action('admin_init', array($this, 'admin_redirects'));
736 +
737 + // Register AJAX handlers for setup wizard (needs to be available even when not on wizard page)
738 + \WPFunnels\Admin\SetupWizard::register_ajax_handlers();
715 739 }
716 740
717 741
718 742 /**
@@ -741,10 +765,8 @@
741 765 {
742 766 // Setup wizard redirect.
743 767 if (get_transient('_wpfunnels_activation_redirect')) {
744 768 $do_redirect = true;
745 - $current_page = isset($_GET['page']) ? wc_clean(wp_unslash($_GET['page'])) : false; // phpcs:ignore WordPress.Security.NonceVerification
746 -
747 769 // On these pages, or during these events, postpone the redirect.
748 770 if (wp_doing_ajax() || is_network_admin() || !current_user_can('manage_options')) {
749 771 $do_redirect = false;
750 772 }