PluginProbe
WPFunnels – Funnel Builder for WooCommerce with Checkout & One Click Upsell / 3.11.0
WPFunnels – Funnel Builder for WooCommerce with Checkout & One Click Upsell v3.11.0
3.13.3 3.13.2 3.13.1 3.13.0 3.12.13 3.12.12 3.12.11 3.12.10 3.12.9 3.12.8 3.12.7 3.12.6 3.12.5 3.12.4 3.12.3 3.12.1 3.12.2 3.12.0 3.11.1 3.11.0 3.10.9 3.10.8 3.10.7 3.10.6 2.8.16 All 261 releases
wpfunnels / admin / modules / settings / class-wpfnl-settings.php

class-wpfnl-settings.php in WPFunnels – Funnel Builder for WooCommerce with Checkout & One Click Upsell 3.11.0, at admin/modules/settings/class-wpfnl-settings.php

1,123 lines 41.9 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Settings module
4 *
5 * @package
6 */
7
8 namespace WPFunnels\Modules\Admin\Settings;
9
10 use WPFunnels\Admin\Module\Wpfnl_Admin_Module;
11 use WPFunnels\Rollback;
12 use WPFunnels\Traits\SingletonTrait;
13 use WPFunnels\Wpfnl;
14 use WPFunnels\Wpfnl_functions;
15
16 class Module extends Wpfnl_Admin_Module
17 {
18 use SingletonTrait;
19
20 protected $validations;
21
22 protected $prefix = '_wpfunnels_';
23
24 protected $general_settings;
25
26 protected $permalink_settings;
27
28 protected $optin_settings;
29
30 protected $offer_settings;
31
32 protected $user_roles;
33
34 protected $gtm_events;
35
36 protected $gtm_settings;
37
38 protected $facebook_pixel_events;
39
40 protected $facebook_pixel_settings;
41
42 protected $recaptcha_settings;
43
44 protected $utm_params;
45
46 protected $utm_settings;
47
48 protected $is_allow_sales;
49
50 /**
51 * User Roles Settings.
52 *
53 * @var array $user_roles_settings An associative array containing user roles settings.
54 */
55 protected $user_roles_settings;
56
57 /**
58 * Notification Settings.
59 *
60 * @var array $notification_settings An associative array containing notification settings.
61 */
62 protected $notification_settings;
63
64
65 /**
66 * Google Maps API Key.
67 *
68 * This property stores the API key used for accessing Google Maps services.
69 *
70 * @var string $google_map_api_key The Google Maps API key.
71 */
72 protected $google_map_api_key;
73
74 /**
75 * Page hooks
76 *
77 * @var array
78 */
79 private $page_hooks = [
80 'toplevel_page_wp_funnels',
81 'wp-funnels_page_wp_funnel_settings',
82 'wpfunnels_page_wpfnl_settings',
83 'wp-funnels_page_edit_funnel',
84 'wp-funnels_page_create_funnel',
85 'wp-funnels_page_wpfnl_settings',
86 'wp-funnels_page_wpf-license',
87 'wpfunnels_page_email-builder',
88 ];
89
90 protected $settings_meta_keys = [
91 '_wpfunnels_funnel_type' => 'sales',
92 '_wpfunnels_builder' => 'elementor',
93 '_wpfunnels_paypal_reference' => '',
94 '_wpfunnels_order_bump' => '',
95 '_wpfunnels_ab_testing' => '',
96 '_wpfunnels_allow_funnels' => [
97 'administrator' => true,
98 ],
99 '_wpfunnels_permalink_settings' => '',
100 '_wpfunnels_optin_settings' => '',
101 '_wpfunnels_permalink_step_base' => 'wpfunnels',
102 '_wpfunnels_permalink_flow_base' => 'step',
103 '_wpfunnels_set_permalink' => 'step',
104 ];
105
106 public function __construct()
107 {
108 add_action('admin_enqueue_scripts', [$this, 'enqueue_scripts']);
109 add_action('admin_init', [$this, 'after_permalink_settings_saved']);
110 add_action( 'admin_post_wpfunnels_rollback', [ $this, 'post_wpfunnels_rollback' ] );
111 add_action( 'wpfunnels_send_revenue_report_email', [ $this, 'send_revenue_report_email' ] );
112 $this->init_ajax();
113 }
114
115 public function is_wc_installed()
116 {
117 $path = 'woocommerce/woocommerce.php';
118 $plugins = get_plugins();
119
120 return isset($plugins[ $path ]);
121 }
122
123 public function is_ff_installed()
124 {
125 $path = 'fluentform/fluentform.php';
126 $plugins = get_plugins();
127
128 return isset($plugins[ $path ]);
129 }
130
131 public function is_elementor_installed()
132 {
133 $path = 'elementor/elementor.php';
134 $plugins = get_plugins();
135
136 return isset($plugins[ $path ]);
137 }
138
139
140 public function enqueue_scripts( $hook )
141 {
142 if ( ! in_array( $hook, $this->page_hooks, true ) ) {
143 return;
144 }
145 wp_enqueue_script('settings', plugin_dir_url(__FILE__) . 'js/settings.js', ['jquery'], WPFNL_VERSION, true);
146 }
147
148
149 public function get_view()
150 {
151 $this->init_settings();
152 $is_pro_activated = Wpfnl_functions::is_wpfnl_pro_activated();
153 $global_funnel_type = Wpfnl_functions::get_global_funnel_type();
154 require_once WPFNL_DIR . '/admin/modules/settings/views/view.php';
155 }
156
157 /**
158 * Init ajax hooks for
159 * saving metas
160 *
161 * @since 1.0.0
162 */
163 public function init_ajax()
164 {
165 $this->validations = [
166 'logged_in' => true,
167 'user_can' => 'wpf_manage_funnels',
168 ];
169 wp_ajax_helper()->handle('update-general-settings')
170 ->with_callback([ $this, 'update_general_settings' ])
171 ->with_validation($this->validations);
172
173 wp_ajax_helper()->handle('clear-templates')
174 ->with_callback([ $this, 'clear_templates_data' ])
175 ->with_validation($this->validations);
176
177 wp_ajax_helper()->handle('clear-transient')
178 ->with_callback([ $this, 'clear_transient_cache_data' ])
179 ->with_validation($this->validations);
180
181 wp_ajax_helper()->handle('wpfnl-show-log')
182 ->with_callback([ $this, 'wpfnl_show_log' ])
183 ->with_validation($this->validations);
184
185 wp_ajax_helper()->handle('wpfnl-delete-log')
186 ->with_callback([ $this, 'wpfnl_delete_log' ])
187 ->with_validation($this->validations);
188
189 wp_ajax_helper()->handle('wpfnl-send-test-notification')
190 ->with_callback([ $this, 'send_test_notification' ])
191 ->with_validation($this->validations);
192 }
193
194
195 /**
196 * Update handler for settings
197 * page
198 *
199 * @param $payload
200 *
201 * @return array
202 * @since 1.0.0
203 */
204 public function update_general_settings($payload)
205 {
206 do_action('wpfunnels/before_settings_saved', $payload);
207 $general_settings = [
208 'funnel_type' => sanitize_text_field($payload['funnel_type']),
209 'builder' => sanitize_text_field($payload['builder']),
210 'uninstall_cleanup' => isset($payload['uninstall_cleanup']) ? sanitize_text_field($payload['uninstall_cleanup']) : 'off',
211 'disable_analytics' => isset($payload['analytics_roles']) ? $payload['analytics_roles'] : '',
212 'allow_funnels' => isset($payload['permission_role']) ? $payload['permission_role'] : [],
213 'paypal_reference' => isset($payload['paypal_reference']) ? sanitize_text_field($payload['paypal_reference']) : 'off',
214 'order_bump' => isset($payload['order_bump']) ? sanitize_text_field($payload['order_bump']) : 'off',
215 'ab_testing' => isset($payload['ab_testing']) ? sanitize_text_field($payload['ab_testing']) : 'off',
216 'disable_theme_style' => isset($payload['disable_theme_style']) ? sanitize_text_field($payload['disable_theme_style']) : 'off',
217 'enable_log_status' => isset($payload['enable_log_status']) ? sanitize_text_field($payload['enable_log_status']) : 'off',
218 'enable_skip_cart' => isset($payload['enable_skip_cart']) ? $payload['enable_skip_cart'] : 'no',
219 'skip_cart_for' => isset($payload['skip_cart_for']) ? $payload['skip_cart_for'] : 'whole',
220 ];
221
222 $permalink_settings = [
223 'structure' => isset($payload['permalink_settings']) ? sanitize_text_field($payload['permalink_settings']) : 'default',
224 'step_base' => isset($payload['permalink_step_base']) ? sanitize_text_field($payload['permalink_step_base']) : WPFNL_STEPS_POST_TYPE,
225 'funnel_base' => isset($payload['permalink_funnel_base']) ? sanitize_text_field($payload['permalink_funnel_base']) : WPFNL_FUNNELS_POST_TYPE,
226 ];
227
228 if( isset($payload['sender_name']) && isset($payload['sender_email']) ){
229 $optin_settings = [
230 'sender_name' => sanitize_text_field($payload['sender_name']),
231 'sender_email' => sanitize_text_field($payload['sender_email']),
232 'email_subject' => isset($payload['email_subject']) ? sanitize_text_field($payload['email_subject']) : '',
233 ];
234 }else{
235 $optin_settings = Wpfnl_functions::get_optin_settings();
236 }
237
238
239 foreach ($payload as $key => $value) {
240 switch ($key) {
241 case 'funnel_type':
242 case 'builder':
243 $cache_key = 'wpfunnels_remote_template_data_' . WPFNL_VERSION;
244 delete_transient($cache_key);
245 delete_option(WPFNL_TEMPLATES_OPTION_KEY);
246 break;
247 case 'permalink_settings':
248 Wpfnl_functions::update_admin_settings($this->prefix.'permalink_saved', true);
249 break;
250 case 'advanced_settings':
251 Wpfnl_functions::update_admin_settings($this->prefix.'advanced_settings', $value);
252 break;
253 default:
254 break;
255 }
256 }
257
258 Wpfnl_functions::update_admin_settings($this->prefix.'general_settings', $general_settings);
259 Wpfnl_functions::update_admin_settings($this->prefix.'permalink_settings', $permalink_settings);
260 Wpfnl_functions::update_admin_settings($this->prefix.'optin_settings', $optin_settings);
261 $this->save_recaptcha_settings($payload);
262 $this->save_user_role_management_data($payload);
263 $this->save_google_map_key($payload);
264 $this->save_lms_settings($payload);
265 $this->save_notification_settings($payload);
266
267 delete_option(WPFNL_TEMPLATES_OPTION_KEY.'_74');
268 delete_option(WPFNL_TEMPLATES_OPTION_KEY.'_73');
269 delete_option(WPFNL_TEMPLATES_OPTION_KEY.'_52');
270 delete_option(WPFNL_TEMPLATES_OPTION_KEY.'_53');
271
272 delete_transient('wpfunnels_remote_template_data_74_' . WPFNL_VERSION);
273 delete_transient('wpfunnels_remote_template_data_73_' . WPFNL_VERSION);
274 delete_transient('wpfunnels_remote_template_data_52_' . WPFNL_VERSION);
275 delete_transient('wpfunnels_remote_template_data_53_' . WPFNL_VERSION);
276
277 do_action('wpfunnels/after_settings_saved', $payload );
278 return [
279 'success' => true
280 ];
281 }
282
283 /**
284 * Save Facebook pixel settings
285 *
286 * @param $settings
287 *
288 * @since 1.0.0
289 */
290 public function save_recaptcha_settings( $settings ) {
291 $enable_recapcha = isset($settings['enable_recaptcha']) ? $settings['enable_recaptcha'] : 'no';
292 $recapcha_site_key = isset($settings['recaptcha_site_key']) ? $settings['recaptcha_site_key'] : '';
293 $recapcha_site_secret = isset($settings['recaptcha_site_secret']) ? $settings['recaptcha_site_secret'] : '';
294 $recaptcha_settings = array(
295 'enable_recaptcha' => $enable_recapcha,
296 'recaptcha_site_key' => $recapcha_site_key,
297 'recaptcha_site_secret' => $recapcha_site_secret
298 );
299 Wpfnl_functions::update_admin_settings('_wpfunnels_recaptcha_setting', $recaptcha_settings );
300 }
301
302
303 /**
304 * Save user role management data
305 *
306 * @param $payload
307 *
308 * @since 3.1.4
309 */
310 public function save_user_role_management_data($payload) {
311 if ( !isset($payload['user_role_management']) ) {
312 return;
313 }
314
315 $roles = wp_unslash( $payload['user_role_management']['roles'] );
316 $sanitized_settings = array();
317 foreach ( $roles as $key => $value ) {
318 $sanitized_settings[ $key ] = ( isset( $roles[ $key ] ) ) ? sanitize_text_field( $value ) : '';
319 }
320 Wpfnl_functions::update_admin_settings('_wpfunnels_user_roles', $sanitized_settings );
321
322 $this->update_user_role_management( $sanitized_settings );
323 }
324
325
326 /**
327 * Update user role management data
328 *
329 * @param $sanitized_settings
330 *
331 * @since 3.1.4
332 */
333 private function update_user_role_management( $sanitized_settings ) {
334
335 foreach ( $sanitized_settings as $user_role => $value ) {
336 $user_role_obj = get_role( $user_role );
337 if ( $user_role_obj ) {
338 if ( 'yes' === $value ) {
339 $user_role_obj->add_cap('wpf_manage_funnels' );
340 } elseif ( 'no' === $value ) {
341 $user_role_obj->remove_cap('wpf_manage_funnels' );
342 }
343 }
344 }
345 }
346
347
348
349 /**
350 * Save google map api key settings key
351 *
352 * @param $payload
353 *
354 * @since 3.1.3
355 */
356 public function save_google_map_key( $payload ) {
357 if ( isset($payload['google_map_api_key']) ) {
358 Wpfnl_functions::update_admin_settings('_wpfunnels_google_map_api_key', $payload['google_map_api_key'] );
359 }
360 }
361
362
363 /**
364 * Save LMS provider settings
365 *
366 * @param $payload
367 *
368 * @since 2.0.0
369 */
370 public function save_lms_settings( $payload ) {
371 if ( isset($payload['lms_provider']) ) {
372 $lms_settings = array(
373 'lms_provider' => sanitize_text_field( $payload['lms_provider'] )
374 );
375 update_option( '_wpfunnels_lms_settings', $lms_settings );
376 }
377 }
378
379 /**
380 * Save notification settings
381 *
382 * @param $payload
383 *
384 * @since 3.2.0
385 */
386 public function save_notification_settings( $payload ) {
387 $old_settings = Wpfnl_functions::get_notification_settings();
388 $old_enabled = isset($old_settings['enable_revenue_report']) ? $old_settings['enable_revenue_report'] : 'no';
389
390 $notification_settings = array(
391 'enable_revenue_report' => isset($payload['enable_revenue_report']) ? sanitize_text_field($payload['enable_revenue_report']) : 'no',
392 'revenue_report_frequency' => isset($payload['revenue_report_frequency']) ? sanitize_text_field($payload['revenue_report_frequency']) : 'weekly',
393 'revenue_report_recipient' => isset($payload['revenue_report_recipient']) ? sanitize_text_field($payload['revenue_report_recipient']) : get_option('admin_email'),
394 'revenue_report_subject' => isset($payload['revenue_report_subject']) ? sanitize_text_field($payload['revenue_report_subject']) : 'Store Revenue Report - {period}',
395 'send_time' => isset($payload['send_time']) ? sanitize_text_field($payload['send_time']) : '10:00',
396 );
397
398 Wpfnl_functions::update_admin_settings('_wpfunnels_notification_settings', $notification_settings );
399
400 $new_enabled = $notification_settings['enable_revenue_report'];
401
402 // Handle action scheduler based on enable/disable
403 if ( $new_enabled === 'yes' && $old_enabled !== 'yes' ) {
404 // Enabled: Schedule the action
405 $this->schedule_revenue_report_email( $notification_settings );
406 } elseif ( $new_enabled !== 'yes' && $old_enabled === 'yes' ) {
407 // Disabled: Remove the scheduled action
408 $this->unschedule_revenue_report_email();
409 } elseif ( $new_enabled === 'yes' ) {
410 // Already enabled but settings changed: Reschedule
411 $this->unschedule_revenue_report_email();
412 $this->schedule_revenue_report_email( $notification_settings );
413 }
414 }
415
416
417 /**
418 * Schedule revenue report email using Action Scheduler
419 *
420 * @param array $settings
421 *
422 * @since 3.2.0
423 */
424 private function schedule_revenue_report_email( $settings ) {
425 if ( ! function_exists( 'as_schedule_recurring_action' ) ) {
426 return;
427 }
428
429 // Calculate next run time based on send_time
430 $send_time = isset( $settings['send_time'] ) ? $settings['send_time'] : '10:00';
431 $frequency = isset( $settings['revenue_report_frequency'] ) ? $settings['revenue_report_frequency'] : 'weekly';
432
433 // Parse time (format: HH:MM)
434 list( $hour, $minute ) = explode( ':', $send_time );
435
436 // Get current time
437 $now = current_time( 'timestamp' );
438 $today = strtotime( date( 'Y-m-d', $now ) );
439
440 // Calculate first run time (today at specified time)
441 $first_run = $today + ( $hour * HOUR_IN_SECONDS ) + ( $minute * MINUTE_IN_SECONDS );
442
443 // If the time has already passed today, schedule for tomorrow
444 if ( $first_run <= $now ) {
445 $first_run += DAY_IN_SECONDS;
446 }
447
448 // Calculate interval based on frequency
449 $interval = ( $frequency === 'monthly' ) ? MONTH_IN_SECONDS : WEEK_IN_SECONDS;
450
451 // Schedule recurring action
452 as_schedule_recurring_action(
453 $first_run,
454 $interval,
455 'wpfunnels_send_revenue_report_email',
456 array(),
457 'wpfunnels'
458 );
459 }
460
461
462 /**
463 * Unschedule revenue report email
464 *
465 * @since 3.2.0
466 */
467 private function unschedule_revenue_report_email() {
468 if ( ! function_exists( 'as_unschedule_all_actions' ) ) {
469 return;
470 }
471
472 as_unschedule_all_actions( 'wpfunnels_send_revenue_report_email', array(), 'wpfunnels' );
473 }
474
475
476 /**
477 * Initialize all the settings value
478 *
479 * @since 1.0.0
480 */
481 public function init_settings()
482 {
483 $this->general_settings = Wpfnl_functions::get_general_settings();
484 $this->is_allow_sales = Wpfnl_functions::maybe_allow_sales_funnel();
485 $this->permalink_settings = Wpfnl_functions::get_permalink_settings();
486 $this->optin_settings = Wpfnl_functions::get_optin_settings();
487 $this->offer_settings = Wpfnl_functions::get_offer_settings();
488 $this->user_roles = Wpfnl_functions::get_user_roles();
489 $this->gtm_events = Wpfnl_functions::get_gtm_events();
490 $this->gtm_settings = Wpfnl_functions::get_gtm_settings();
491 $this->facebook_pixel_events = Wpfnl_functions::get_facebook_events();
492 $this->facebook_pixel_settings = Wpfnl_functions::get_facebook_pixel_settings();
493 $this->utm_params = Wpfnl_functions::get_utm_params();
494 $this->utm_settings = Wpfnl_functions::get_utm_settings();
495 $this->recaptcha_settings = Wpfnl_functions::get_recaptcha_settings();
496 $this->user_roles_settings = Wpfnl_functions::get_user_role_settings();
497 $this->google_map_api_key = Wpfnl_functions::get_google_map_api_key();
498 $this->notification_settings = Wpfnl_functions::get_notification_settings();
499 }
500
501
502
503
504 /**
505 * Clear saved templates data
506 *
507 * @param $payload
508 *
509 * @return array
510 * @since 1.0.0
511 */
512 public function clear_templates_data($payload) {
513
514 delete_option(WPFNL_TEMPLATES_OPTION_KEY.'_wc');
515 delete_option(WPFNL_TEMPLATES_OPTION_KEY.'_lms');
516
517 $builder = Wpfnl_functions::get_builder_type();
518
519 delete_transient('wpfunnels_remote_template_data_wc_' . WPFNL_VERSION);
520 delete_transient('wpfunnels_remote_template_data_lms_' . WPFNL_VERSION);
521 delete_transient('wpfunnels_remote_template_data_lead_' . WPFNL_VERSION);
522 delete_transient('wpfunnels_remote_template_data_store_checkout_' . WPFNL_VERSION);
523
524 delete_transient('wpfunnels_remote_template_data_wc_'. $builder . '_' . WPFNL_VERSION);
525 delete_transient('wpfunnels_remote_template_data_lms_'. $builder . '_' . WPFNL_VERSION);
526 delete_transient('wpfunnels_remote_template_data_lead_'. $builder . '_' . WPFNL_VERSION);
527 delete_transient('wpfunnels_remote_template_data_store_checkout_'. $builder . '_' . WPFNL_VERSION);
528
529 return [
530 'success' => true
531 ];
532 }
533
534
535 /**
536 * Clear transient
537 *
538 * @param $payload
539 *
540 * @return array
541 */
542 public function clear_transient_cache_data($payload) {
543
544 delete_transient('wpfunnels_remote_template_data_' . WPFNL_VERSION);
545 delete_transient('wpfunnels_remote_template_data_wc_' . WPFNL_VERSION);
546 delete_transient('wpfunnels_remote_template_data_lms_' . WPFNL_VERSION);
547 delete_transient('wpfunnels_remote_template_data_lead_' . WPFNL_VERSION);
548 delete_transient('wpfunnels_remote_template_data_store_checkout_' . WPFNL_VERSION);
549 delete_transient('wpfunnels_rollback_versions_' . WPFNL_VERSION);
550 do_action('wpfunnels/after_clear_transient');
551 return array(
552 'success' => true
553 );
554 }
555
556
557 /**
558 * After settings saved hooks
559 *
560 * @since 1.0.0
561 */
562 public function after_permalink_settings_saved()
563 {
564
565 if( Wpfnl_functions::maybe_funnel_page() ){
566 $is_permalink_saved = get_option('_wpfunnels_permalink_saved');
567 if ($is_permalink_saved) {
568 flush_rewrite_rules();
569 delete_option('_wpfunnels_permalink_saved');
570 }
571 }
572 }
573
574
575 /**
576 * Get settings by meta key
577 *
578 * @param $key
579 *
580 * @return mixed|string
581 * @since 1.0.0
582 */
583 public function get_settings_by_key($key)
584 {
585 return isset($this->settings_meta_keys[$key]) ? $this->settings_meta_keys[$key]: '';
586 }
587
588 public function get_name()
589 {
590 return __('settings','wpfnl');
591 }
592
593
594 /**
595 * Get rollback version of WPF
596 *
597 * @return array|mixed
598 * @since 2.3.0
599 */
600 public function get_roll_back_versions() {
601 $rollback_versions = get_transient( 'wpfunnels_rollback_versions_' . WPFNL_VERSION );
602 if ( false === $rollback_versions ) {
603 $max_versions = 10;
604 require_once ABSPATH . 'wp-admin/includes/plugin-install.php';
605 $plugin_information = plugins_api(
606 'plugin_information', [
607 'slug' => 'wpfunnels',
608 ]
609 );
610 if ( empty( $plugin_information->versions ) || ! is_array( $plugin_information->versions ) ) {
611 return [];
612 }
613
614 natsort( $plugin_information->versions );
615 $plugin_information->versions = array_reverse($plugin_information->versions);
616 $rollback_versions = [];
617
618 $current_index = 0;
619 foreach ( $plugin_information->versions as $version => $download_link ) {
620 if ( $max_versions <= $current_index ) {
621 break;
622 }
623
624 $lowercase_version = strtolower( $version );
625 $is_valid_rollback_version = ! preg_match( '/(trunk|beta|rc|dev)/i', $lowercase_version );
626
627 /**
628 * Is rollback version is valid.
629 *
630 * Filters the check whether the rollback version is valid.
631 *
632 * @param bool $is_valid_rollback_version Whether the rollback version is valid.
633 */
634 $is_valid_rollback_version = apply_filters(
635 'wpfunnels/is_valid_rollback_version',
636 $is_valid_rollback_version,
637 $lowercase_version
638 );
639
640 if ( ! $is_valid_rollback_version ) {
641 continue;
642 }
643
644 if ( version_compare( $version, WPFNL_VERSION, '>=' ) ) {
645 continue;
646 }
647
648 $current_index++;
649 $rollback_versions[] = $version;
650 }
651
652 set_transient( 'wpfunnels_rollback_versions_' . WPFNL_VERSION, $rollback_versions, DAY_IN_SECONDS );
653 }
654
655 return $rollback_versions;
656 }
657
658
659 public function post_wpfunnels_rollback() {
660 check_admin_referer( 'wpfunnels_rollback' );
661
662 $rollback_versions = $this->get_roll_back_versions();
663 if ( empty( $_GET['version'] ) || ! in_array( $_GET['version'], $rollback_versions ) ) {
664 wp_die( esc_html__( 'Error occurred, The version selected is invalid. Try selecting different version.', 'wpfnl' ) );
665 }
666
667 $plugin_slug = basename( 'wpfunnels', '.php' );
668
669 $rollback = new Rollback(
670 [
671 'version' => $_GET['version'],
672 'plugin_name' => WPFNL_BASE,
673 'plugin_slug' => $plugin_slug,
674 'package_url' => sprintf( 'https://downloads.wordpress.org/plugin/%1s.%2s.zip', $plugin_slug, $_GET['version'] ),
675 ]
676 );
677
678 $rollback->run();
679
680 wp_die(
681 '', esc_html__( 'Rollback to Previous Version', 'wpfnl' ), [
682 'response' => 200,
683 ]
684 );
685 }
686
687
688 /**
689 * WPFunnels log
690 *
691 * @param $payload
692 *
693 * @return array
694 */
695 public static function wpfnl_show_log( $payload ) {
696 if( isset($payload['logKey']) ){
697 $key = $payload['logKey'];
698 $upload_dir = wp_upload_dir( null, false );
699 $log_url = $upload_dir['basedir'].'/wpfunnels/wpfunnels-logs/';
700 $file_url = $log_url . $key;
701
702 ob_start();
703 include_once $file_url;
704 $out = ob_get_clean();
705 ob_end_clean();
706
707 return array(
708 'success' => true,
709 'content' => $out,
710 'file_url' => $log_url. $key
711 );
712 }
713
714 return array(
715 'success' => false,
716 'content' => '',
717 'file_url' => ''
718 );
719
720
721 }
722
723
724 /**
725 * Send test notification email
726 *
727 * @param $payload
728 *
729 * @return array
730 * @since 3.2.0
731 */
732 public function send_test_notification( $payload ) {
733 if ( ! isset( $payload['email'] ) || ! is_email( $payload['email'] ) ) {
734 return array(
735 'success' => false,
736 'message' => __( 'Please provide a valid email address.', 'wpfnl' )
737 );
738 }
739
740 $settings = Wpfnl_functions::get_notification_settings();
741 $subject_template = isset( $settings['revenue_report_subject'] ) ? $settings['revenue_report_subject'] : 'Store Revenue Report - {period}';
742
743 // Calculate date range for test email (last week Monday to Sunday)
744 $start_date = date( 'Y-m-d', strtotime( 'last monday -1 week' ) );
745 $end_date = date( 'Y-m-d', strtotime( 'last sunday' ) );
746 $date_range = date( 'F j, Y', strtotime( $start_date ) ) . ' - ' . date( 'F j, Y', strtotime( $end_date ) );
747
748 $subject = '[Test] ' . str_replace( '{period}', $date_range, $subject_template );
749
750 $to = sanitize_email( $payload['email'] );
751 $message = $this->get_revenue_report_email_content( 'weekly' );
752 $headers = array( 'Content-Type: text/html; charset=UTF-8' );
753
754 $sent = wp_mail( $to, $subject, $message, $headers );
755
756 if ( $sent ) {
757 return array(
758 'success' => true,
759 'message' => __( 'Test email sent successfully! Please check your inbox.', 'wpfnl' )
760 );
761 } else {
762 return array(
763 'success' => false,
764 'message' => __( 'Failed to send test email. Please check your email configuration.', 'wpfnl' )
765 );
766 }
767 }
768
769
770 /**
771 * Send revenue report email (triggered by Action Scheduler)
772 *
773 * @since 3.2.0
774 */
775 public function send_revenue_report_email() {
776 $settings = Wpfnl_functions::get_notification_settings();
777
778 // Check if still enabled
779 if ( ! isset( $settings['enable_revenue_report'] ) || $settings['enable_revenue_report'] !== 'yes' ) {
780 return;
781 }
782
783 $recipients = isset( $settings['revenue_report_recipient'] ) ? $settings['revenue_report_recipient'] : get_option('admin_email');
784 $subject = isset( $settings['revenue_report_subject'] ) ? $settings['revenue_report_subject'] : 'Store Revenue Report - {period}';
785 $frequency = isset( $settings['revenue_report_frequency'] ) ? $settings['revenue_report_frequency'] : 'weekly';
786
787 // Calculate date range based on frequency
788 if ( $frequency === 'monthly' ) {
789 $start_date = date( 'Y-m-d', strtotime( 'first day of last month' ) );
790 $end_date = date( 'Y-m-d', strtotime( 'last day of last month' ) );
791 } else {
792 // Weekly: Last week Monday to Sunday
793 $start_date = date( 'Y-m-d', strtotime( 'last monday -1 week' ) );
794 $end_date = date( 'Y-m-d', strtotime( 'last sunday' ) );
795 }
796
797 $date_range = date( 'F j, Y', strtotime( $start_date ) ) . ' - ' . date( 'F j, Y', strtotime( $end_date ) );
798
799 // Replace {period} placeholder with date range
800 $subject = str_replace( '{period}', $date_range, $subject );
801
802 // Generate report content
803 $message = $this->get_revenue_report_email_content( $frequency );
804
805 // Prepare headers
806 $headers = array( 'Content-Type: text/html; charset=UTF-8' );
807
808 // Send email to recipients (can be multiple, comma-separated)
809 $recipient_array = array_map( 'trim', explode( ',', $recipients ) );
810
811 foreach ( $recipient_array as $recipient ) {
812 if ( is_email( $recipient ) ) {
813 wp_mail( sanitize_email( $recipient ), $subject, $message, $headers );
814 }
815 }
816 }
817
818
819 /**
820 * Get revenue report email content with actual data
821 *
822 * @param string $frequency
823 * @return string
824 * @since 3.2.0
825 */
826 private function get_revenue_report_email_content( $frequency = 'weekly' ) {
827 $site_name = get_bloginfo( 'name' );
828 $site_url = home_url();
829
830 // Calculate date range
831 if ( $frequency === 'monthly' ) {
832 $start_date = date( 'Y-m-d', strtotime( 'first day of last month' ) );
833 $end_date = date( 'Y-m-d', strtotime( 'last day of last month' ) );
834 $period_text = __( 'Monthly', 'wpfnl' );
835 } else {
836 // Weekly: Last week Monday to Sunday
837 $start_date = date( 'Y-m-d', strtotime( 'last monday -1 week' ) );
838 $end_date = date( 'Y-m-d', strtotime( 'last sunday' ) );
839 $period_text = __( 'Weekly', 'wpfnl' );
840 }
841
842 $date_range = date( 'F j, Y', strtotime( $start_date ) ) . ' - ' . date( 'F j, Y', strtotime( $end_date ) );
843
844 // Calculate previous period for comparison
845 if ( $frequency === 'monthly' ) {
846 $prev_start_date = date( 'Y-m-d', strtotime( 'first day of -2 months' ) );
847 $prev_end_date = date( 'Y-m-d', strtotime( 'last day of -2 months' ) );
848 } else {
849 $prev_start_date = date( 'Y-m-d', strtotime( 'last monday -2 weeks' ) );
850 $prev_end_date = date( 'Y-m-d', strtotime( 'last sunday -1 week' ) );
851 }
852
853 // Get revenue data for current and previous periods
854 $revenue_data = $this->get_revenue_data( $start_date, $end_date );
855 $previous_data = $this->get_revenue_data( $prev_start_date, $prev_end_date );
856
857 // Calculate percentage changes
858 $revenue_data['total_orders_change'] = $this->calculate_percentage_change( $previous_data['total_orders'], $revenue_data['total_orders'] );
859 $revenue_data['total_customers_change'] = $this->calculate_percentage_change( $previous_data['total_customers'], $revenue_data['total_customers'] );
860 $revenue_data['total_revenue_change'] = $this->calculate_percentage_change( $previous_data['total_revenue_raw'], $revenue_data['total_revenue_raw'] );
861 $revenue_data['average_order_value_change'] = $this->calculate_percentage_change( $previous_data['average_order_value_raw'], $revenue_data['average_order_value_raw'] );
862
863 // Build email with FunnelKit-style design
864 $message = '<!DOCTYPE html>';
865 $message .= '<html lang="en">';
866 $message .= '<head>';
867 $message .= '<meta charset="UTF-8">';
868 $message .= '<meta name="viewport" content="width=device-width, initial-scale=1.0">';
869 $message .= '<title>Performance Report</title>';
870 $message .= '</head>';
871 $message .= '<body style="margin: 0; padding: 0; font-family: -apple-system, BlinkMacSystemFont, \'Segoe UI\', Roboto, \'Helvetica Neue\', Arial, sans-serif; background-color: #f5f5f5;">';
872
873 // Main container
874 $message .= '<table width="100%" border="0" cellspacing="0" cellpadding="0" style="background-color: #f5f5f5; padding: 40px 20px;">';
875 $message .= '<tr><td align="center">';
876 $message .= '<table width="600" border="0" cellspacing="0" cellpadding="0" style="background-color: #ffffff; border-radius: 8px; overflow: hidden; box-shadow: 0 2px 8px rgba(0,0,0,0.1);">';
877
878 // Top orange banner with text
879 $message .= '<tr><td style="background-color: #6e42d3; padding: 12px 40px; text-align: center;">';
880 $message .= '<div style="font-size: 16px; font-weight: 600; color: #ffffff;">Your ' . esc_html( strtolower( $frequency ) ) . ' summary from WPFunnels.</div>';
881 $message .= '</td></tr>';
882
883 // WPFunnels logo section
884 $message .= '<tr><td style="background-color: #ffffff; padding: 20px 40px 20px; text-align: center;">';
885 $message .= '<svg width="38" height="28" viewBox="0 0 38 28" fill="none" xmlns="http://www.w3.org/2000/svg" style="display: inline-block; margin-right: 10px; vertical-align: middle;">';
886 $message .= '<path d="M7.01532 18H31.9847L34 11H5L7.01532 18Z" fill="#EE8134"/>';
887 $message .= '<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"/>';
888 $message .= '<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"/>';
889 $message .= '</svg>';
890 $message .= '<span style="font-size: 32px; font-weight: bold; color: #231748; vertical-align: middle;">WPFunnels</span>';
891 $message .= '</td></tr>';
892
893 // Date range, Performance Report title, and View Button - all in same background
894 $message .= '<tr><td style="padding: 20px 40px 30px; text-align: center; background-color: #f8f9fa;">';
895 // Date range
896 $message .= '<div style="font-size: 14px; color: #6c757d; font-weight: 500; margin-bottom: 20px;">' . esc_html( $date_range ) . '</div>';
897 // Performance Report title
898 $message .= '<h1 style="margin: 0 0 10px; font-size: 32px; font-weight: bold; color: #212529;">Store Revenue Summary</h1>';
899 $message .= '<p style="margin: 0 0 20px; font-size: 14px; color: #6c757d;">Track your store performance and revenue metrics for this period</p>';
900 // View Dashboard Button
901 $message .= '<a href="' . esc_url( admin_url( 'admin.php?page=wpfunnels' ) ) . '" style="display: inline-block; background-color: #6e42d3; color: #ffffff; text-decoration: none; padding: 12px 30px; border-radius: 5px; font-size: 14px; font-weight: 600;">View Dashboard</a>';
902 $message .= '</td></tr>';
903
904 // Key Performance Metrics section
905 $message .= '<tr><td style="padding: 20px 40px;">';
906 $message .= '<h2 style="margin: 0 0 10px; font-size: 24px; font-weight: bold; color: #212529; text-align: center;">Revenue Overview</h2>';
907 $message .= '<p style="margin: 0 0 30px; font-size: 14px; color: #6c757d; text-align: center;">Your ' . esc_html( strtolower( $frequency ) ) . ' performance snapshot</p>';
908
909 // Metrics grid
910 $message .= '<table width="100%" border="0" cellspacing="0" cellpadding="0">';
911
912 // Row 1: Total orders and Total contacts
913 $message .= '<tr>';
914 $message .= '<td width="50%" style="padding: 20px; vertical-align: top;">';
915 $message .= '<div style="text-align: left;">';
916 $message .= '<div style="font-size: 14px; color: #6c757d; margin-bottom: 10px; font-weight: 500;">Total orders</div>';
917 $message .= '<div style="font-size: 36px; font-weight: bold; color: #212529; margin-bottom: 5px;">' . esc_html( $revenue_data['total_orders'] ) . '</div>';
918 $message .= $revenue_data['total_orders_change'];
919 $message .= '</div>';
920 $message .= '</td>';
921 $message .= '<td width="50%" style="padding: 20px; vertical-align: top;">';
922 $message .= '<div style="text-align: left;">';
923 $message .= '<div style="font-size: 14px; color: #6c757d; margin-bottom: 10px; font-weight: 500;">Total customers</div>';
924 $message .= '<div style="font-size: 36px; font-weight: bold; color: #212529; margin-bottom: 5px;">' . esc_html( $revenue_data['total_customers'] ) . '</div>';
925 $message .= $revenue_data['total_customers_change'];
926 $message .= '</div>';
927 $message .= '</td>';
928 $message .= '</tr>';
929
930 // Row 2: Revenue and Bump revenue
931 $message .= '<tr>';
932 $message .= '<td width="50%" style="padding: 20px; vertical-align: top;">';
933 $message .= '<div style="text-align: left;">';
934 $message .= '<div style="font-size: 14px; color: #6c757d; margin-bottom: 10px; font-weight: 500;">Revenue</div>';
935 $message .= '<div style="font-size: 36px; font-weight: bold; color: #212529; margin-bottom: 5px;">' . wp_strip_all_tags( $revenue_data['total_revenue'] ) . '</div>';
936 $message .= $revenue_data['total_revenue_change'];
937 $message .= '</div>';
938 $message .= '</td>';
939 $message .= '<td width="50%" style="padding: 20px; vertical-align: top;">';
940 $message .= '<div style="text-align: left;">';
941 $message .= '<div style="font-size: 14px; color: #6c757d; margin-bottom: 10px; font-weight: 500;">Bump revenue</div>';
942 if ( Wpfnl_functions::is_wpfnl_pro_activated() ) {
943 $message .= '<div style="font-size: 36px; font-weight: bold; color: #212529; margin-bottom: 5px;">0 <span style="font-size: 18px; font-weight: normal; color: #6c757d;">USD</span></div>';
944 $message .= '<div style="font-size: 12px; color: #6c757d;">0% - Previous period</div>';
945 } else {
946 $message .= '<div style="font-size: 36px; margin-bottom: 5px;"><svg width="32" height="32" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M17 11V7C17 4.79086 15.2091 3 13 3H11C8.79086 3 7 4.79086 7 7V11M12 14V16M8 11H16C17.1046 11 18 11.8954 18 13V19C18 20.1046 17.1046 21 16 21H8C6.89543 21 6 20.1046 6 19V13C6 11.8954 6.89543 11 8 11Z" stroke="#6c757d" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg></div>';
947 $message .= '<div style="font-size: 12px; color: #6c757d;">WPFunnels Pro</div>';
948 }
949 $message .= '</div>';
950 $message .= '</td>';
951 $message .= '</tr>';
952
953 // Row 3: Upsell revenue and Average order value
954 $message .= '<tr>';
955 $message .= '<td width="50%" style="padding: 20px; vertical-align: top;">';
956 $message .= '<div style="text-align: left;">';
957 $message .= '<div style="font-size: 14px; color: #6c757d; margin-bottom: 10px; font-weight: 500;">Upsell revenue</div>';
958 if ( Wpfnl_functions::is_wpfnl_pro_activated() ) {
959 $message .= '<div style="font-size: 36px; font-weight: bold; color: #212529; margin-bottom: 5px;">0 <span style="font-size: 18px; font-weight: normal; color: #6c757d;">USD</span></div>';
960 $message .= '<div style="font-size: 12px; color: #6c757d;">0% - Previous period</div>';
961 } else {
962 $message .= '<div style="font-size: 36px; margin-bottom: 5px;"><svg width="32" height="32" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M17 11V7C17 4.79086 15.2091 3 13 3H11C8.79086 3 7 4.79086 7 7V11M12 14V16M8 11H16C17.1046 11 18 11.8954 18 13V19C18 20.1046 17.1046 21 16 21H8C6.89543 21 6 20.1046 6 19V13C6 11.8954 6.89543 11 8 11Z" stroke="#6c757d" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg></div>';
963 $message .= '<div style="font-size: 12px; color: #6c757d;">WPFunnels Pro</div>';
964 }
965 $message .= '</div>';
966 $message .= '</td>';
967 $message .= '<td width="50%" style="padding: 20px; vertical-align: top;">';
968 $message .= '<div style="text-align: left;">';
969 $message .= '<div style="font-size: 14px; color: #6c757d; margin-bottom: 10px; font-weight: 500;">Average order value</div>';
970 $message .= '<div style="font-size: 36px; font-weight: bold; color: #212529; margin-bottom: 5px;">' . wp_strip_all_tags( $revenue_data['average_order_value'] ) . '</div>';
971 $message .= $revenue_data['average_order_value_change'];
972 $message .= '</div>';
973 $message .= '</td>';
974 $message .= '</tr>';
975
976 $message .= '</table>';
977 $message .= '</td></tr>';
978
979 // Additional revenue info
980 if ( isset( $revenue_data['total_revenue_raw'] ) && $revenue_data['total_revenue_raw'] > 0 ) {
981 $message .= '<tr><td style="padding: 20px 40px;">';
982 $message .= '<div style="background-color: #fff3cd; border-left: 4px solid #ffc107; padding: 15px; border-radius: 4px;">';
983 $message .= '<p style="margin: 0; font-size: 14px; color: #856404;">Since installing WPFunnels you have captured additional revenue of <strong>' . wp_kses_post( wc_price( $revenue_data['total_revenue_raw'] ) ) . '</strong></p>';
984 $message .= '</div>';
985 $message .= '</td></tr>';
986 }
987
988 // Footer
989 $message .= '<tr><td style="padding: 30px 40px; text-align: center; background-color: #ffffff; border-top: 1px solid #e9ecef;">';
990 $message .= '<p style="margin: 0 0 10px; font-size: 12px; color: #6c757d;">This performance report email was sent from the <a href="' . esc_url( $site_url ) . '" style="color: #007bff; text-decoration: none;">' . esc_html( $site_name ) . '</a> by admin ' . esc_html( date( 'F j, Y' ) ) . '</p>';
991 $message .= '<p style="margin: 0; font-size: 12px; color: #6c757d;"><a href="' . esc_url( admin_url( 'admin.php?page=wpfnl_settings#notifications-settings' ) ) . '" style="color: #007bff; text-decoration: none;">Click here</a> to change the frequency and recipients.</p>';
992 $message .= '</td></tr>';
993
994 $message .= '</table>';
995 $message .= '</td></tr>';
996 $message .= '</table>';
997
998 $message .= '</body>';
999 $message .= '</html>';
1000
1001 return $message;
1002 }
1003
1004
1005 /**
1006 * Get revenue data from WPFunnels stats
1007 *
1008 * @param string $start_date
1009 * @param string $end_date
1010 * @return array
1011 * @since 3.2.0
1012 */
1013 private function get_revenue_data( $start_date, $end_date ) {
1014 global $wpdb;
1015 $table = $wpdb->prefix . 'wpfnl_stats';
1016
1017 // Get total orders count
1018 $total_orders = $wpdb->get_var( $wpdb->prepare(
1019 "SELECT COUNT(id) FROM $table WHERE date_created >= %s AND date_created <= %s AND status = 'completed'",
1020 $start_date,
1021 $end_date . ' 23:59:59'
1022 ) );
1023
1024 // Get total revenue (total_sales includes everything: checkout + order bump + upsell + downsell)
1025 $total_revenue_raw = $wpdb->get_var( $wpdb->prepare(
1026 "SELECT SUM(total_sales) FROM $table WHERE date_created >= %s AND date_created <= %s AND status = 'completed'",
1027 $start_date,
1028 $end_date . ' 23:59:59'
1029 ) );
1030
1031 // Get total customers (unique customer_id)
1032 $total_customers = $wpdb->get_var( $wpdb->prepare(
1033 "SELECT COUNT(DISTINCT customer_id) FROM $table WHERE date_created >= %s AND date_created <= %s AND status = 'completed'",
1034 $start_date,
1035 $end_date . ' 23:59:59'
1036 ) );
1037
1038 // Calculate average order value
1039 $total_orders_int = intval( $total_orders );
1040 $total_revenue_float = floatval( $total_revenue_raw );
1041 $average = $total_orders_int > 0 ? $total_revenue_float / $total_orders_int : 0;
1042
1043 // Format data
1044 $data = array(
1045 'total_revenue' => function_exists( 'wc_price' ) ? wc_price( $total_revenue_float ) : '$' . number_format( $total_revenue_float, 2 ),
1046 'total_revenue_raw' => $total_revenue_float,
1047 'total_orders' => $total_orders_int,
1048 'total_customers' => intval( $total_customers ),
1049 'average_order_value' => function_exists( 'wc_price' ) ? wc_price( $average ) : '$' . number_format( $average, 2 ),
1050 'average_order_value_raw' => $average,
1051 );
1052
1053 return $data;
1054 }
1055
1056
1057 /**
1058 * Calculate percentage change between two values
1059 *
1060 * @param float $old_value
1061 * @param float $new_value
1062 * @return string HTML formatted percentage change
1063 * @since 3.2.0
1064 */
1065 private function calculate_percentage_change( $old_value, $new_value ) {
1066 $old_value = floatval( $old_value );
1067 $new_value = floatval( $new_value );
1068
1069 if ( $old_value == 0 ) {
1070 if ( $new_value > 0 ) {
1071 return '<div style="font-size: 12px; color: #28a745;">+100% - Previous period</div>';
1072 } else {
1073 return '<div style="font-size: 12px; color: #6c757d;">0% - Previous period</div>';
1074 }
1075 }
1076
1077 $change = ( ( $new_value - $old_value ) / $old_value ) * 100;
1078 $change_formatted = number_format( abs( $change ), 1 );
1079
1080 if ( $change > 0 ) {
1081 return '<div style="font-size: 12px; color: #28a745;">+' . $change_formatted . '% - Previous period</div>';
1082 } elseif ( $change < 0 ) {
1083 return '<div style="font-size: 12px; color: #dc3545;">-' . $change_formatted . '% - Previous period</div>';
1084 } else {
1085 return '<div style="font-size: 12px; color: #6c757d;">0% - Previous period</div>';
1086 }
1087 }
1088
1089
1090 /**
1091 * WPFunnels log
1092 *
1093 * @param $payload
1094 *
1095 * @return array
1096 */
1097 public static function wpfnl_delete_log( $payload ) {
1098 if( isset($payload['logKey']) ){
1099 $key = sanitize_file_name( basename( $payload['logKey'] ) );
1100 $upload_dir = wp_upload_dir( null, false );
1101 $log_dir = $upload_dir['basedir'].'/wpfunnels/wpfunnels-logs/';
1102 $file_name = $log_dir . $key;
1103
1104 // Verify the resolved path is within the log directory
1105 $real_file = realpath($file_name);
1106 $real_log_dir = realpath($log_dir);
1107
1108 if ( $real_file && $real_log_dir && strpos( $real_file, $real_log_dir ) === 0 ) {
1109 $response = \Wpfnl_Logger::delete_log_file( $file_name );
1110 if( $response ){
1111 return array('success' => true);
1112 }
1113 }
1114 }
1115
1116 return array(
1117 'success' => false,
1118 'content' => '',
1119 'file_url' => ''
1120 );
1121 }
1122 }
1123