PluginProbe
WPFunnels – Funnel Builder for WooCommerce with Checkout & One Click Upsell / 3.12.13
WPFunnels – Funnel Builder for WooCommerce with Checkout & One Click Upsell v3.12.13
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.12.13, at admin/modules/settings/class-wpfnl-settings.php

1,156 lines 43.7 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 'funnel_builder_mode' => isset($payload['funnel_builder_mode']) ? sanitize_text_field($payload['funnel_builder_mode']) : Wpfnl_functions::get_default_funnel_builder_mode(),
221 ];
222
223 if ( isset( $payload['funnel_builder_mode'] ) ) {
224 // Choosing the site default here is an explicit statement of intent, so
225 // drop this user's own canvas override — otherwise a view they toggled
226 // earlier would keep winning over the mode they just picked.
227 delete_user_meta( get_current_user_id(), Wpfnl_functions::CANVAS_VIEW_MODE_META_KEY );
228
229 if ( 'vertical' === $payload['funnel_builder_mode'] ) {
230 do_action( 'wpfunnels_vertical_mode_used' );
231 }
232 }
233
234 $permalink_settings = [
235 'structure' => isset($payload['permalink_settings']) ? sanitize_text_field($payload['permalink_settings']) : 'default',
236 'step_base' => isset($payload['permalink_step_base']) ? sanitize_text_field($payload['permalink_step_base']) : WPFNL_STEPS_POST_TYPE,
237 'funnel_base' => isset($payload['permalink_funnel_base']) ? sanitize_text_field($payload['permalink_funnel_base']) : WPFNL_FUNNELS_POST_TYPE,
238 ];
239
240 if( isset($payload['sender_name']) && isset($payload['sender_email']) ){
241 $optin_settings = [
242 'sender_name' => sanitize_text_field($payload['sender_name']),
243 'sender_email' => sanitize_text_field($payload['sender_email']),
244 'email_subject' => isset($payload['email_subject']) ? sanitize_text_field($payload['email_subject']) : '',
245 ];
246 }else{
247 $optin_settings = Wpfnl_functions::get_optin_settings();
248 }
249
250
251 foreach ($payload as $key => $value) {
252 switch ($key) {
253 case 'funnel_type':
254 case 'builder':
255 $cache_key = 'wpfunnels_remote_template_data_' . WPFNL_VERSION;
256 delete_transient($cache_key);
257 delete_option(WPFNL_TEMPLATES_OPTION_KEY);
258 break;
259 case 'permalink_settings':
260 Wpfnl_functions::update_admin_settings($this->prefix.'permalink_saved', true);
261 break;
262 case 'advanced_settings':
263 Wpfnl_functions::update_admin_settings($this->prefix.'advanced_settings', $value);
264 break;
265 default:
266 break;
267 }
268 }
269
270 Wpfnl_functions::update_admin_settings($this->prefix.'general_settings', $general_settings);
271 Wpfnl_functions::update_admin_settings($this->prefix.'permalink_settings', $permalink_settings);
272 Wpfnl_functions::update_admin_settings($this->prefix.'optin_settings', $optin_settings);
273 $this->save_recaptcha_settings($payload);
274 $this->save_user_role_management_data($payload);
275 $this->save_google_map_key($payload);
276 $this->save_lms_settings($payload);
277 $this->save_notification_settings($payload);
278
279 delete_option(WPFNL_TEMPLATES_OPTION_KEY.'_74');
280 delete_option(WPFNL_TEMPLATES_OPTION_KEY.'_73');
281 delete_option(WPFNL_TEMPLATES_OPTION_KEY.'_52');
282 delete_option(WPFNL_TEMPLATES_OPTION_KEY.'_53');
283
284 delete_transient('wpfunnels_remote_template_data_74_' . WPFNL_VERSION);
285 delete_transient('wpfunnels_remote_template_data_73_' . WPFNL_VERSION);
286 delete_transient('wpfunnels_remote_template_data_52_' . WPFNL_VERSION);
287 delete_transient('wpfunnels_remote_template_data_53_' . WPFNL_VERSION);
288
289 do_action('wpfunnels/after_settings_saved', $payload );
290 return [
291 'success' => true
292 ];
293 }
294
295 /**
296 * Save Facebook pixel settings
297 *
298 * @param $settings
299 *
300 * @since 1.0.0
301 */
302 public function save_recaptcha_settings( $settings ) {
303 $enable_recapcha = isset($settings['enable_recaptcha']) ? $settings['enable_recaptcha'] : 'no';
304 $recapcha_site_key = isset($settings['recaptcha_site_key']) ? $settings['recaptcha_site_key'] : '';
305 $recapcha_site_secret = isset($settings['recaptcha_site_secret']) ? $settings['recaptcha_site_secret'] : '';
306 $recaptcha_settings = array(
307 'enable_recaptcha' => $enable_recapcha,
308 'recaptcha_site_key' => $recapcha_site_key,
309 'recaptcha_site_secret' => $recapcha_site_secret
310 );
311 Wpfnl_functions::update_admin_settings('_wpfunnels_recaptcha_setting', $recaptcha_settings );
312 }
313
314
315 /**
316 * Save user role management data
317 *
318 * @param $payload
319 *
320 * @since 3.1.4
321 */
322 public function save_user_role_management_data($payload) {
323 if ( !isset($payload['user_role_management']) ) {
324 return;
325 }
326
327 $roles = wp_unslash( $payload['user_role_management']['roles'] );
328 $sanitized_settings = array();
329 foreach ( $roles as $key => $value ) {
330 $sanitized_settings[ $key ] = ( isset( $roles[ $key ] ) ) ? sanitize_text_field( $value ) : '';
331 }
332 Wpfnl_functions::update_admin_settings('_wpfunnels_user_roles', $sanitized_settings );
333
334 $this->update_user_role_management( $sanitized_settings );
335 }
336
337
338 /**
339 * Update user role management data
340 *
341 * @param $sanitized_settings
342 *
343 * @since 3.1.4
344 */
345 private function update_user_role_management( $sanitized_settings ) {
346
347 foreach ( $sanitized_settings as $user_role => $value ) {
348 $user_role_obj = get_role( $user_role );
349 if ( $user_role_obj ) {
350 if ( 'yes' === $value ) {
351 $user_role_obj->add_cap('wpf_manage_funnels' );
352 } elseif ( 'no' === $value ) {
353 $user_role_obj->remove_cap('wpf_manage_funnels' );
354 }
355 }
356 }
357 }
358
359
360
361 /**
362 * Save google map api key settings key
363 *
364 * @param $payload
365 *
366 * @since 3.1.3
367 */
368 public function save_google_map_key( $payload ) {
369 if ( isset($payload['google_map_api_key']) ) {
370 Wpfnl_functions::update_admin_settings('_wpfunnels_google_map_api_key', $payload['google_map_api_key'] );
371 }
372 }
373
374
375 /**
376 * Save LMS provider settings
377 *
378 * @param $payload
379 *
380 * @since 2.0.0
381 */
382 public function save_lms_settings( $payload ) {
383 if ( isset($payload['lms_provider']) ) {
384 $lms_settings = array(
385 'lms_provider' => sanitize_text_field( $payload['lms_provider'] )
386 );
387 update_option( '_wpfunnels_lms_settings', $lms_settings );
388 }
389 }
390
391 /**
392 * Save notification settings
393 *
394 * @param $payload
395 *
396 * @since 3.2.0
397 */
398 public function save_notification_settings( $payload ) {
399 $old_settings = Wpfnl_functions::get_notification_settings();
400 $old_enabled = isset($old_settings['enable_revenue_report']) ? $old_settings['enable_revenue_report'] : 'no';
401
402 $notification_settings = array(
403 'enable_revenue_report' => isset($payload['enable_revenue_report']) ? sanitize_text_field($payload['enable_revenue_report']) : 'no',
404 'revenue_report_frequency' => isset($payload['revenue_report_frequency']) ? sanitize_text_field($payload['revenue_report_frequency']) : 'weekly',
405 'revenue_report_recipient' => isset($payload['revenue_report_recipient']) ? sanitize_text_field($payload['revenue_report_recipient']) : get_option('admin_email'),
406 'revenue_report_subject' => isset($payload['revenue_report_subject']) ? sanitize_text_field($payload['revenue_report_subject']) : 'Store Revenue Report - {period}',
407 'send_time' => isset($payload['send_time']) ? sanitize_text_field($payload['send_time']) : '10:00',
408 );
409
410 Wpfnl_functions::update_admin_settings('_wpfunnels_notification_settings', $notification_settings );
411
412 $new_enabled = $notification_settings['enable_revenue_report'];
413
414 // Handle action scheduler based on enable/disable
415 if ( $new_enabled === 'yes' && $old_enabled !== 'yes' ) {
416 // Enabled: Schedule the action
417 $this->schedule_revenue_report_email( $notification_settings );
418 } elseif ( $new_enabled !== 'yes' && $old_enabled === 'yes' ) {
419 // Disabled: Remove the scheduled action
420 $this->unschedule_revenue_report_email();
421 } elseif ( $new_enabled === 'yes' ) {
422 // Already enabled but settings changed: Reschedule
423 $this->unschedule_revenue_report_email();
424 $this->schedule_revenue_report_email( $notification_settings );
425 }
426 }
427
428
429 /**
430 * Schedule revenue report email using Action Scheduler
431 *
432 * @param array $settings
433 *
434 * @since 3.2.0
435 */
436 private function schedule_revenue_report_email( $settings ) {
437 if ( ! function_exists( 'as_schedule_recurring_action' ) ) {
438 return;
439 }
440
441 // Calculate next run time based on send_time
442 $send_time = isset( $settings['send_time'] ) ? $settings['send_time'] : '10:00';
443 $frequency = isset( $settings['revenue_report_frequency'] ) ? $settings['revenue_report_frequency'] : 'weekly';
444
445 // Parse time (format: HH:MM)
446 list( $hour, $minute ) = explode( ':', $send_time );
447
448 // Get current time
449 $now = current_time( 'timestamp' );
450 $today = strtotime( date( 'Y-m-d', $now ) );
451
452 // Calculate first run time (today at specified time)
453 $first_run = $today + ( $hour * HOUR_IN_SECONDS ) + ( $minute * MINUTE_IN_SECONDS );
454
455 // If the time has already passed today, schedule for tomorrow
456 if ( $first_run <= $now ) {
457 $first_run += DAY_IN_SECONDS;
458 }
459
460 // Calculate interval based on frequency
461 $interval = ( $frequency === 'monthly' ) ? MONTH_IN_SECONDS : WEEK_IN_SECONDS;
462
463 // Schedule recurring action
464 as_schedule_recurring_action(
465 $first_run,
466 $interval,
467 'wpfunnels_send_revenue_report_email',
468 array(),
469 'wpfunnels'
470 );
471 }
472
473
474 /**
475 * Unschedule revenue report email
476 *
477 * @since 3.2.0
478 */
479 private function unschedule_revenue_report_email() {
480 if ( ! function_exists( 'as_unschedule_all_actions' ) ) {
481 return;
482 }
483
484 as_unschedule_all_actions( 'wpfunnels_send_revenue_report_email', array(), 'wpfunnels' );
485 }
486
487
488 /**
489 * Initialize all the settings value
490 *
491 * @since 1.0.0
492 */
493 public function init_settings()
494 {
495 $this->general_settings = Wpfnl_functions::get_general_settings();
496 $this->is_allow_sales = Wpfnl_functions::maybe_allow_sales_funnel();
497 $this->permalink_settings = Wpfnl_functions::get_permalink_settings();
498 $this->optin_settings = Wpfnl_functions::get_optin_settings();
499 $this->offer_settings = Wpfnl_functions::get_offer_settings();
500 $this->user_roles = Wpfnl_functions::get_user_roles();
501 $this->gtm_events = Wpfnl_functions::get_gtm_events();
502 $this->gtm_settings = Wpfnl_functions::get_gtm_settings();
503 $this->facebook_pixel_events = Wpfnl_functions::get_facebook_events();
504 $this->facebook_pixel_settings = Wpfnl_functions::get_facebook_pixel_settings();
505 $this->utm_params = Wpfnl_functions::get_utm_params();
506 $this->utm_settings = Wpfnl_functions::get_utm_settings();
507 $this->recaptcha_settings = Wpfnl_functions::get_recaptcha_settings();
508 $this->user_roles_settings = Wpfnl_functions::get_user_role_settings();
509 $this->google_map_api_key = Wpfnl_functions::get_google_map_api_key();
510 $this->notification_settings = Wpfnl_functions::get_notification_settings();
511 }
512
513
514
515
516 /**
517 * Clear saved templates data
518 *
519 * @param $payload
520 *
521 * @return array
522 * @since 1.0.0
523 */
524 public function clear_templates_data($payload) {
525
526 delete_option(WPFNL_TEMPLATES_OPTION_KEY.'_wc');
527 delete_option(WPFNL_TEMPLATES_OPTION_KEY.'_lms');
528
529 $builder = Wpfnl_functions::get_builder_type();
530
531 delete_transient('wpfunnels_remote_template_data_wc_' . WPFNL_VERSION);
532 delete_transient('wpfunnels_remote_template_data_lms_' . WPFNL_VERSION);
533 delete_transient('wpfunnels_remote_template_data_lead_' . WPFNL_VERSION);
534 delete_transient('wpfunnels_remote_template_data_store_checkout_' . WPFNL_VERSION);
535
536 delete_transient('wpfunnels_remote_template_data_wc_'. $builder . '_' . WPFNL_VERSION);
537 delete_transient('wpfunnels_remote_template_data_lms_'. $builder . '_' . WPFNL_VERSION);
538 delete_transient('wpfunnels_remote_template_data_lead_'. $builder . '_' . WPFNL_VERSION);
539 delete_transient('wpfunnels_remote_template_data_store_checkout_'. $builder . '_' . WPFNL_VERSION);
540
541 return [
542 'success' => true
543 ];
544 }
545
546
547 /**
548 * Clear transient
549 *
550 * @param $payload
551 *
552 * @return array
553 */
554 public function clear_transient_cache_data($payload) {
555
556 delete_transient('wpfunnels_remote_template_data_' . WPFNL_VERSION);
557 delete_transient('wpfunnels_remote_template_data_wc_' . WPFNL_VERSION);
558 delete_transient('wpfunnels_remote_template_data_lms_' . WPFNL_VERSION);
559 delete_transient('wpfunnels_remote_template_data_lead_' . WPFNL_VERSION);
560 delete_transient('wpfunnels_remote_template_data_store_checkout_' . WPFNL_VERSION);
561 delete_transient('wpfunnels_rollback_versions_' . WPFNL_VERSION);
562 do_action('wpfunnels/after_clear_transient');
563 return array(
564 'success' => true
565 );
566 }
567
568
569 /**
570 * After settings saved hooks
571 *
572 * @since 1.0.0
573 */
574 public function after_permalink_settings_saved()
575 {
576
577 if( Wpfnl_functions::maybe_funnel_page() ){
578 $is_permalink_saved = get_option('_wpfunnels_permalink_saved');
579 if ($is_permalink_saved) {
580 flush_rewrite_rules();
581 delete_option('_wpfunnels_permalink_saved');
582 }
583 }
584 }
585
586
587 /**
588 * Get settings by meta key
589 *
590 * @param $key
591 *
592 * @return mixed|string
593 * @since 1.0.0
594 */
595 public function get_settings_by_key($key)
596 {
597 return isset($this->settings_meta_keys[$key]) ? $this->settings_meta_keys[$key]: '';
598 }
599
600 public function get_name()
601 {
602 return __('settings','wpfnl');
603 }
604
605
606 /**
607 * Get rollback version of WPF
608 *
609 * @return array|mixed
610 * @since 2.3.0
611 */
612 public function get_roll_back_versions() {
613 $rollback_versions = get_transient( 'wpfunnels_rollback_versions_' . WPFNL_VERSION );
614 if ( false === $rollback_versions ) {
615 $max_versions = 10;
616 require_once ABSPATH . 'wp-admin/includes/plugin-install.php';
617 $plugin_information = plugins_api(
618 'plugin_information', [
619 'slug' => 'wpfunnels',
620 ]
621 );
622 if ( empty( $plugin_information->versions ) || ! is_array( $plugin_information->versions ) ) {
623 return [];
624 }
625
626 natsort( $plugin_information->versions );
627 $plugin_information->versions = array_reverse($plugin_information->versions);
628 $rollback_versions = [];
629
630 $current_index = 0;
631 foreach ( $plugin_information->versions as $version => $download_link ) {
632 if ( $max_versions <= $current_index ) {
633 break;
634 }
635
636 $lowercase_version = strtolower( $version );
637 $is_valid_rollback_version = ! preg_match( '/(trunk|beta|rc|dev)/i', $lowercase_version );
638
639 /**
640 * Is rollback version is valid.
641 *
642 * Filters the check whether the rollback version is valid.
643 *
644 * @param bool $is_valid_rollback_version Whether the rollback version is valid.
645 */
646 $is_valid_rollback_version = apply_filters(
647 'wpfunnels/is_valid_rollback_version',
648 $is_valid_rollback_version,
649 $lowercase_version
650 );
651
652 if ( ! $is_valid_rollback_version ) {
653 continue;
654 }
655
656 if ( version_compare( $version, WPFNL_VERSION, '>=' ) ) {
657 continue;
658 }
659
660 $current_index++;
661 $rollback_versions[] = $version;
662 }
663
664 set_transient( 'wpfunnels_rollback_versions_' . WPFNL_VERSION, $rollback_versions, DAY_IN_SECONDS );
665 }
666
667 return $rollback_versions;
668 }
669
670
671 public function post_wpfunnels_rollback() {
672 check_admin_referer( 'wpfunnels_rollback' );
673
674 $rollback_versions = $this->get_roll_back_versions();
675 if ( empty( $_GET['version'] ) || ! in_array( $_GET['version'], $rollback_versions ) ) {
676 wp_die( esc_html__( 'Error occurred, The version selected is invalid. Try selecting different version.', 'wpfnl' ) );
677 }
678
679 $plugin_slug = basename( 'wpfunnels', '.php' );
680
681 $rollback = new Rollback(
682 [
683 'version' => $_GET['version'],
684 'plugin_name' => WPFNL_BASE,
685 'plugin_slug' => $plugin_slug,
686 'package_url' => sprintf( 'https://downloads.wordpress.org/plugin/%1s.%2s.zip', $plugin_slug, $_GET['version'] ),
687 ]
688 );
689
690 $rollback->run();
691
692 wp_die(
693 '', esc_html__( 'Rollback to Previous Version', 'wpfnl' ), [
694 'response' => 200,
695 ]
696 );
697 }
698
699
700 /**
701 * WPFunnels log
702 *
703 * @param $payload
704 *
705 * @return array
706 */
707 public static function wpfnl_show_log( $payload ) {
708 if( isset($payload['logKey']) ){
709 // Reduce to a bare filename — strips any directory traversal (../),
710 // separators, and stream-wrapper prefixes (phar://, php://, etc.).
711 $key = basename( wp_unslash( $payload['logKey'] ) );
712
713 $upload_dir = wp_upload_dir( null, false );
714 $log_url = trailingslashit( $upload_dir['basedir'] ) . 'wpfunnels/wpfunnels-logs/';
715 $file_url = $log_url . $key;
716
717 // Whitelist: key must be a .log filename that actually resolves inside
718 // the logs directory. realpath() comparison defeats any residual traversal.
719 $real_dir = realpath( $log_url );
720 $real_file = file_exists( $file_url ) ? realpath( $file_url ) : false;
721
722 $is_valid = $key
723 && '.log' === strtolower( substr( $key, -4 ) )
724 && $real_dir
725 && $real_file
726 && 0 === strpos( $real_file, trailingslashit( $real_dir ) );
727
728 if ( $is_valid ) {
729 // Never include/execute the log file — read it as data and escape it.
730 $out = file_get_contents( $file_url );
731 $out = '<pre>' . esc_html( $out ) . '</pre>';
732
733 return array(
734 'success' => true,
735 'content' => $out,
736 'file_url' => $file_url,
737 );
738 }
739
740 return array(
741 'success' => false,
742 'content' => '',
743 'file_url' => '',
744 );
745 }
746
747 return array(
748 'success' => false,
749 'content' => '',
750 'file_url' => ''
751 );
752
753
754 }
755
756
757 /**
758 * Send test notification email
759 *
760 * @param $payload
761 *
762 * @return array
763 * @since 3.2.0
764 */
765 public function send_test_notification( $payload ) {
766 if ( ! isset( $payload['email'] ) || ! is_email( $payload['email'] ) ) {
767 return array(
768 'success' => false,
769 'message' => __( 'Please provide a valid email address.', 'wpfnl' )
770 );
771 }
772
773 $settings = Wpfnl_functions::get_notification_settings();
774 $subject_template = isset( $settings['revenue_report_subject'] ) ? $settings['revenue_report_subject'] : 'Store Revenue Report - {period}';
775
776 // Calculate date range for test email (last week Monday to Sunday)
777 $start_date = date( 'Y-m-d', strtotime( 'last monday -1 week' ) );
778 $end_date = date( 'Y-m-d', strtotime( 'last sunday' ) );
779 $date_range = date( 'F j, Y', strtotime( $start_date ) ) . ' - ' . date( 'F j, Y', strtotime( $end_date ) );
780
781 $subject = '[Test] ' . str_replace( '{period}', $date_range, $subject_template );
782
783 $to = sanitize_email( $payload['email'] );
784 $message = $this->get_revenue_report_email_content( 'weekly' );
785 $headers = array( 'Content-Type: text/html; charset=UTF-8' );
786
787 $sent = wp_mail( $to, $subject, $message, $headers );
788
789 if ( $sent ) {
790 return array(
791 'success' => true,
792 'message' => __( 'Test email sent successfully! Please check your inbox.', 'wpfnl' )
793 );
794 } else {
795 return array(
796 'success' => false,
797 'message' => __( 'Failed to send test email. Please check your email configuration.', 'wpfnl' )
798 );
799 }
800 }
801
802
803 /**
804 * Send revenue report email (triggered by Action Scheduler)
805 *
806 * @since 3.2.0
807 */
808 public function send_revenue_report_email() {
809 $settings = Wpfnl_functions::get_notification_settings();
810
811 // Check if still enabled
812 if ( ! isset( $settings['enable_revenue_report'] ) || $settings['enable_revenue_report'] !== 'yes' ) {
813 return;
814 }
815
816 $recipients = isset( $settings['revenue_report_recipient'] ) ? $settings['revenue_report_recipient'] : get_option('admin_email');
817 $subject = isset( $settings['revenue_report_subject'] ) ? $settings['revenue_report_subject'] : 'Store Revenue Report - {period}';
818 $frequency = isset( $settings['revenue_report_frequency'] ) ? $settings['revenue_report_frequency'] : 'weekly';
819
820 // Calculate date range based on frequency
821 if ( $frequency === 'monthly' ) {
822 $start_date = date( 'Y-m-d', strtotime( 'first day of last month' ) );
823 $end_date = date( 'Y-m-d', strtotime( 'last day of last month' ) );
824 } else {
825 // Weekly: Last week Monday to Sunday
826 $start_date = date( 'Y-m-d', strtotime( 'last monday -1 week' ) );
827 $end_date = date( 'Y-m-d', strtotime( 'last sunday' ) );
828 }
829
830 $date_range = date( 'F j, Y', strtotime( $start_date ) ) . ' - ' . date( 'F j, Y', strtotime( $end_date ) );
831
832 // Replace {period} placeholder with date range
833 $subject = str_replace( '{period}', $date_range, $subject );
834
835 // Generate report content
836 $message = $this->get_revenue_report_email_content( $frequency );
837
838 // Prepare headers
839 $headers = array( 'Content-Type: text/html; charset=UTF-8' );
840
841 // Send email to recipients (can be multiple, comma-separated)
842 $recipient_array = array_map( 'trim', explode( ',', $recipients ) );
843
844 foreach ( $recipient_array as $recipient ) {
845 if ( is_email( $recipient ) ) {
846 wp_mail( sanitize_email( $recipient ), $subject, $message, $headers );
847 }
848 }
849 }
850
851
852 /**
853 * Get revenue report email content with actual data
854 *
855 * @param string $frequency
856 * @return string
857 * @since 3.2.0
858 */
859 private function get_revenue_report_email_content( $frequency = 'weekly' ) {
860 $site_name = get_bloginfo( 'name' );
861 $site_url = home_url();
862
863 // Calculate date range
864 if ( $frequency === 'monthly' ) {
865 $start_date = date( 'Y-m-d', strtotime( 'first day of last month' ) );
866 $end_date = date( 'Y-m-d', strtotime( 'last day of last month' ) );
867 $period_text = __( 'Monthly', 'wpfnl' );
868 } else {
869 // Weekly: Last week Monday to Sunday
870 $start_date = date( 'Y-m-d', strtotime( 'last monday -1 week' ) );
871 $end_date = date( 'Y-m-d', strtotime( 'last sunday' ) );
872 $period_text = __( 'Weekly', 'wpfnl' );
873 }
874
875 $date_range = date( 'F j, Y', strtotime( $start_date ) ) . ' - ' . date( 'F j, Y', strtotime( $end_date ) );
876
877 // Calculate previous period for comparison
878 if ( $frequency === 'monthly' ) {
879 $prev_start_date = date( 'Y-m-d', strtotime( 'first day of -2 months' ) );
880 $prev_end_date = date( 'Y-m-d', strtotime( 'last day of -2 months' ) );
881 } else {
882 $prev_start_date = date( 'Y-m-d', strtotime( 'last monday -2 weeks' ) );
883 $prev_end_date = date( 'Y-m-d', strtotime( 'last sunday -1 week' ) );
884 }
885
886 // Get revenue data for current and previous periods
887 $revenue_data = $this->get_revenue_data( $start_date, $end_date );
888 $previous_data = $this->get_revenue_data( $prev_start_date, $prev_end_date );
889
890 // Calculate percentage changes
891 $revenue_data['total_orders_change'] = $this->calculate_percentage_change( $previous_data['total_orders'], $revenue_data['total_orders'] );
892 $revenue_data['total_customers_change'] = $this->calculate_percentage_change( $previous_data['total_customers'], $revenue_data['total_customers'] );
893 $revenue_data['total_revenue_change'] = $this->calculate_percentage_change( $previous_data['total_revenue_raw'], $revenue_data['total_revenue_raw'] );
894 $revenue_data['average_order_value_change'] = $this->calculate_percentage_change( $previous_data['average_order_value_raw'], $revenue_data['average_order_value_raw'] );
895
896 // Build email with FunnelKit-style design
897 $message = '<!DOCTYPE html>';
898 $message .= '<html lang="en">';
899 $message .= '<head>';
900 $message .= '<meta charset="UTF-8">';
901 $message .= '<meta name="viewport" content="width=device-width, initial-scale=1.0">';
902 $message .= '<title>Performance Report</title>';
903 $message .= '</head>';
904 $message .= '<body style="margin: 0; padding: 0; font-family: -apple-system, BlinkMacSystemFont, \'Segoe UI\', Roboto, \'Helvetica Neue\', Arial, sans-serif; background-color: #f5f5f5;">';
905
906 // Main container
907 $message .= '<table width="100%" border="0" cellspacing="0" cellpadding="0" style="background-color: #f5f5f5; padding: 40px 20px;">';
908 $message .= '<tr><td align="center">';
909 $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);">';
910
911 // Top orange banner with text
912 $message .= '<tr><td style="background-color: #6e42d3; padding: 12px 40px; text-align: center;">';
913 $message .= '<div style="font-size: 16px; font-weight: 600; color: #ffffff;">Your ' . esc_html( strtolower( $frequency ) ) . ' summary from WPFunnels.</div>';
914 $message .= '</td></tr>';
915
916 // WPFunnels logo section
917 $message .= '<tr><td style="background-color: #ffffff; padding: 20px 40px 20px; text-align: center;">';
918 $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;">';
919 $message .= '<path d="M7.01532 18H31.9847L34 11H5L7.01532 18Z" fill="#EE8134"/>';
920 $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"/>';
921 $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"/>';
922 $message .= '</svg>';
923 $message .= '<span style="font-size: 32px; font-weight: bold; color: #231748; vertical-align: middle;">WPFunnels</span>';
924 $message .= '</td></tr>';
925
926 // Date range, Performance Report title, and View Button - all in same background
927 $message .= '<tr><td style="padding: 20px 40px 30px; text-align: center; background-color: #f8f9fa;">';
928 // Date range
929 $message .= '<div style="font-size: 14px; color: #6c757d; font-weight: 500; margin-bottom: 20px;">' . esc_html( $date_range ) . '</div>';
930 // Performance Report title
931 $message .= '<h1 style="margin: 0 0 10px; font-size: 32px; font-weight: bold; color: #212529;">Store Revenue Summary</h1>';
932 $message .= '<p style="margin: 0 0 20px; font-size: 14px; color: #6c757d;">Track your store performance and revenue metrics for this period</p>';
933 // View Dashboard Button
934 $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>';
935 $message .= '</td></tr>';
936
937 // Key Performance Metrics section
938 $message .= '<tr><td style="padding: 20px 40px;">';
939 $message .= '<h2 style="margin: 0 0 10px; font-size: 24px; font-weight: bold; color: #212529; text-align: center;">Revenue Overview</h2>';
940 $message .= '<p style="margin: 0 0 30px; font-size: 14px; color: #6c757d; text-align: center;">Your ' . esc_html( strtolower( $frequency ) ) . ' performance snapshot</p>';
941
942 // Metrics grid
943 $message .= '<table width="100%" border="0" cellspacing="0" cellpadding="0">';
944
945 // Row 1: Total orders and Total contacts
946 $message .= '<tr>';
947 $message .= '<td width="50%" style="padding: 20px; vertical-align: top;">';
948 $message .= '<div style="text-align: left;">';
949 $message .= '<div style="font-size: 14px; color: #6c757d; margin-bottom: 10px; font-weight: 500;">Total orders</div>';
950 $message .= '<div style="font-size: 36px; font-weight: bold; color: #212529; margin-bottom: 5px;">' . esc_html( $revenue_data['total_orders'] ) . '</div>';
951 $message .= $revenue_data['total_orders_change'];
952 $message .= '</div>';
953 $message .= '</td>';
954 $message .= '<td width="50%" style="padding: 20px; vertical-align: top;">';
955 $message .= '<div style="text-align: left;">';
956 $message .= '<div style="font-size: 14px; color: #6c757d; margin-bottom: 10px; font-weight: 500;">Total customers</div>';
957 $message .= '<div style="font-size: 36px; font-weight: bold; color: #212529; margin-bottom: 5px;">' . esc_html( $revenue_data['total_customers'] ) . '</div>';
958 $message .= $revenue_data['total_customers_change'];
959 $message .= '</div>';
960 $message .= '</td>';
961 $message .= '</tr>';
962
963 // Row 2: Revenue and Bump revenue
964 $message .= '<tr>';
965 $message .= '<td width="50%" style="padding: 20px; vertical-align: top;">';
966 $message .= '<div style="text-align: left;">';
967 $message .= '<div style="font-size: 14px; color: #6c757d; margin-bottom: 10px; font-weight: 500;">Revenue</div>';
968 $message .= '<div style="font-size: 36px; font-weight: bold; color: #212529; margin-bottom: 5px;">' . wp_strip_all_tags( $revenue_data['total_revenue'] ) . '</div>';
969 $message .= $revenue_data['total_revenue_change'];
970 $message .= '</div>';
971 $message .= '</td>';
972 $message .= '<td width="50%" style="padding: 20px; vertical-align: top;">';
973 $message .= '<div style="text-align: left;">';
974 $message .= '<div style="font-size: 14px; color: #6c757d; margin-bottom: 10px; font-weight: 500;">Bump revenue</div>';
975 if ( Wpfnl_functions::is_wpfnl_pro_activated() ) {
976 $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>';
977 $message .= '<div style="font-size: 12px; color: #6c757d;">0% - Previous period</div>';
978 } else {
979 $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>';
980 $message .= '<div style="font-size: 12px; color: #6c757d;">WPFunnels Pro</div>';
981 }
982 $message .= '</div>';
983 $message .= '</td>';
984 $message .= '</tr>';
985
986 // Row 3: Upsell revenue and Average order value
987 $message .= '<tr>';
988 $message .= '<td width="50%" style="padding: 20px; vertical-align: top;">';
989 $message .= '<div style="text-align: left;">';
990 $message .= '<div style="font-size: 14px; color: #6c757d; margin-bottom: 10px; font-weight: 500;">Upsell revenue</div>';
991 if ( Wpfnl_functions::is_wpfnl_pro_activated() ) {
992 $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>';
993 $message .= '<div style="font-size: 12px; color: #6c757d;">0% - Previous period</div>';
994 } else {
995 $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>';
996 $message .= '<div style="font-size: 12px; color: #6c757d;">WPFunnels Pro</div>';
997 }
998 $message .= '</div>';
999 $message .= '</td>';
1000 $message .= '<td width="50%" style="padding: 20px; vertical-align: top;">';
1001 $message .= '<div style="text-align: left;">';
1002 $message .= '<div style="font-size: 14px; color: #6c757d; margin-bottom: 10px; font-weight: 500;">Average order value</div>';
1003 $message .= '<div style="font-size: 36px; font-weight: bold; color: #212529; margin-bottom: 5px;">' . wp_strip_all_tags( $revenue_data['average_order_value'] ) . '</div>';
1004 $message .= $revenue_data['average_order_value_change'];
1005 $message .= '</div>';
1006 $message .= '</td>';
1007 $message .= '</tr>';
1008
1009 $message .= '</table>';
1010 $message .= '</td></tr>';
1011
1012 // Additional revenue info
1013 if ( isset( $revenue_data['total_revenue_raw'] ) && $revenue_data['total_revenue_raw'] > 0 ) {
1014 $message .= '<tr><td style="padding: 20px 40px;">';
1015 $message .= '<div style="background-color: #fff3cd; border-left: 4px solid #ffc107; padding: 15px; border-radius: 4px;">';
1016 $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>';
1017 $message .= '</div>';
1018 $message .= '</td></tr>';
1019 }
1020
1021 // Footer
1022 $message .= '<tr><td style="padding: 30px 40px; text-align: center; background-color: #ffffff; border-top: 1px solid #e9ecef;">';
1023 $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>';
1024 $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>';
1025 $message .= '</td></tr>';
1026
1027 $message .= '</table>';
1028 $message .= '</td></tr>';
1029 $message .= '</table>';
1030
1031 $message .= '</body>';
1032 $message .= '</html>';
1033
1034 return $message;
1035 }
1036
1037
1038 /**
1039 * Get revenue data from WPFunnels stats
1040 *
1041 * @param string $start_date
1042 * @param string $end_date
1043 * @return array
1044 * @since 3.2.0
1045 */
1046 private function get_revenue_data( $start_date, $end_date ) {
1047 global $wpdb;
1048 $table = $wpdb->prefix . 'wpfnl_stats';
1049
1050 // Get total orders count
1051 $total_orders = $wpdb->get_var( $wpdb->prepare(
1052 "SELECT COUNT(id) FROM $table WHERE date_created >= %s AND date_created <= %s AND status = 'completed'",
1053 $start_date,
1054 $end_date . ' 23:59:59'
1055 ) );
1056
1057 // Get total revenue (total_sales includes everything: checkout + order bump + upsell + downsell)
1058 $total_revenue_raw = $wpdb->get_var( $wpdb->prepare(
1059 "SELECT SUM(total_sales) FROM $table WHERE date_created >= %s AND date_created <= %s AND status = 'completed'",
1060 $start_date,
1061 $end_date . ' 23:59:59'
1062 ) );
1063
1064 // Get total customers (unique customer_id)
1065 $total_customers = $wpdb->get_var( $wpdb->prepare(
1066 "SELECT COUNT(DISTINCT customer_id) FROM $table WHERE date_created >= %s AND date_created <= %s AND status = 'completed'",
1067 $start_date,
1068 $end_date . ' 23:59:59'
1069 ) );
1070
1071 // Calculate average order value
1072 $total_orders_int = intval( $total_orders );
1073 $total_revenue_float = floatval( $total_revenue_raw );
1074 $average = $total_orders_int > 0 ? $total_revenue_float / $total_orders_int : 0;
1075
1076 // Format data
1077 $data = array(
1078 'total_revenue' => function_exists( 'wc_price' ) ? wc_price( $total_revenue_float ) : '$' . number_format( $total_revenue_float, 2 ),
1079 'total_revenue_raw' => $total_revenue_float,
1080 'total_orders' => $total_orders_int,
1081 'total_customers' => intval( $total_customers ),
1082 'average_order_value' => function_exists( 'wc_price' ) ? wc_price( $average ) : '$' . number_format( $average, 2 ),
1083 'average_order_value_raw' => $average,
1084 );
1085
1086 return $data;
1087 }
1088
1089
1090 /**
1091 * Calculate percentage change between two values
1092 *
1093 * @param float $old_value
1094 * @param float $new_value
1095 * @return string HTML formatted percentage change
1096 * @since 3.2.0
1097 */
1098 private function calculate_percentage_change( $old_value, $new_value ) {
1099 $old_value = floatval( $old_value );
1100 $new_value = floatval( $new_value );
1101
1102 if ( $old_value == 0 ) {
1103 if ( $new_value > 0 ) {
1104 return '<div style="font-size: 12px; color: #28a745;">+100% - Previous period</div>';
1105 } else {
1106 return '<div style="font-size: 12px; color: #6c757d;">0% - Previous period</div>';
1107 }
1108 }
1109
1110 $change = ( ( $new_value - $old_value ) / $old_value ) * 100;
1111 $change_formatted = number_format( abs( $change ), 1 );
1112
1113 if ( $change > 0 ) {
1114 return '<div style="font-size: 12px; color: #28a745;">+' . $change_formatted . '% - Previous period</div>';
1115 } elseif ( $change < 0 ) {
1116 return '<div style="font-size: 12px; color: #dc3545;">-' . $change_formatted . '% - Previous period</div>';
1117 } else {
1118 return '<div style="font-size: 12px; color: #6c757d;">0% - Previous period</div>';
1119 }
1120 }
1121
1122
1123 /**
1124 * WPFunnels log
1125 *
1126 * @param $payload
1127 *
1128 * @return array
1129 */
1130 public static function wpfnl_delete_log( $payload ) {
1131 if( isset($payload['logKey']) ){
1132 $key = sanitize_file_name( basename( $payload['logKey'] ) );
1133 $upload_dir = wp_upload_dir( null, false );
1134 $log_dir = $upload_dir['basedir'].'/wpfunnels/wpfunnels-logs/';
1135 $file_name = $log_dir . $key;
1136
1137 // Verify the resolved path is within the log directory
1138 $real_file = realpath($file_name);
1139 $real_log_dir = realpath($log_dir);
1140
1141 if ( $real_file && $real_log_dir && strpos( $real_file, $real_log_dir ) === 0 ) {
1142 $response = \Wpfnl_Logger::delete_log_file( $file_name );
1143 if( $response ){
1144 return array('success' => true);
1145 }
1146 }
1147 }
1148
1149 return array(
1150 'success' => false,
1151 'content' => '',
1152 'file_url' => ''
1153 );
1154 }
1155 }
1156