PluginProbe ʕ •ᴥ•ʔ
Hustle – Email Marketing, Lead Generation, Optins, Popups / 7.8.14.1
Hustle – Email Marketing, Lead Generation, Optins, Popups v7.8.14.1
7.8.14 7.8.14.1 7.8.13 7.8.13.1 trunk 3.0 3.1 3.1.1 3.1.2 3.1.3 3.1.4 4.3.2 4.4.4 4.4.5 4.4.5.1 4.4.5.4 4.6 4.6.1.1 4.6.1.4 4.7.0.2 4.7.0.3 4.7.0.7 4.7.0.9 4.7.1.0 4.7.1.1 4.8.0.0 5.0.0 5.0.1 5.0.1.1 5.0.1.2 5.1 5.1.1 5.1.2 5.1.3 5.1.3.1 5.1.3.2 5.1.4 5.1.5 6.0 6.0.1 6.0.2 6.0.3 6.0.4.2 6.0.5 6.0.6.1 6.0.7 6.0.8.1 6.0.9 7.0.0.1 7.0.2 7.0.3 7.0.4 7.1.0 7.1.1 7.2.0 7.2.1 7.3.0 7.3.1 7.3.3 7.3.5 7.3.6 7.3.7 7.4.0 7.4.1 7.4.11 7.4.13 7.4.13.1 7.4.2 7.4.3 7.4.4 7.4.5 7.4.5.1 7.4.5.2 7.4.6 7.4.7 7.5.0 7.6.0 7.6.1 7.6.3 7.6.4 7.6.6 7.7.0 7.7.1 7.8.0 7.8.1 7.8.10 7.8.10.1 7.8.10.2 7.8.11 7.8.12 7.8.12.1 7.8.2 7.8.3 7.8.4 7.8.5 7.8.6 7.8.7 7.8.8 7.8.9 7.8.9.1 7.8.9.2 7.8.9.3
wordpress-popup / inc / class-hustle-wp-dashboard-page.php
wordpress-popup / inc Last commit date
display-conditions 5 months ago front 2 days ago helpers 1 month ago metas 3 months ago multisite 5 months ago palettes 3 years ago provider 1 month ago providers 2 days ago templates 3 years ago update 5 months ago class-hustle-admin-page-abstract.php 1 month ago class-hustle-auth-token.php 5 months ago class-hustle-condition-factory.php 6 years ago class-hustle-cross-sell.php 10 months ago class-hustle-dashboard-admin.php 1 month ago class-hustle-data.php 1 month ago class-hustle-db.php 2 years ago class-hustle-installer.php 4 years ago class-hustle-meta.php 3 years ago class-hustle-module-admin.php 1 month ago class-hustle-module-collection.php 5 months ago class-hustle-module-fields.php 3 months ago class-hustle-module-page-abstract.php 1 month ago class-hustle-module-validator.php 3 months ago class-hustle-notifications.php 2 days ago class-hustle-settings-admin.php 1 month ago class-hustle-wp-dashboard-page.php 5 months ago class-opt-in.php 2 days ago hustle-background-conversion-log.php 3 months ago hustle-deletion.php 3 months ago hustle-embedded-admin.php 3 years ago hustle-entries-admin.php 5 months ago hustle-entry-model.php 1 month ago hustle-general-data-protection.php 5 months ago hustle-init.php 1 month ago hustle-mail.php 5 months ago hustle-migration.php 5 months ago hustle-model.php 2 days ago hustle-module-model.php 1 month ago hustle-module-widget-legacy.php 5 months ago hustle-module-widget.php 5 months ago hustle-modules-common-admin-ajax.php 1 month ago hustle-popup-admin.php 3 years ago hustle-providers-admin.php 1 month ago hustle-providers.php 5 months ago hustle-settings-admin-ajax.php 1 month ago hustle-settings-page.php 3 years ago hustle-slidein-admin.php 3 years ago hustle-sshare-admin.php 2 days ago hustle-sshare-model.php 2 days ago hustle-tracking-model.php 3 months ago interface-hustle-auth-provider.php 5 months ago opt-in-geo.php 5 months ago opt-in-utils.php 2 days ago opt-in-wpmudev-api.php 5 months ago
class-hustle-wp-dashboard-page.php
585 lines
1 <?php
2 /**
3 * File for Hustle_Wp_Dashboard_Page class.
4 *
5 * @package Hustle
6 * @since 4.2.1
7 */
8
9 /**
10 * Class Hustle_Wp_Dashboard_Page.
11 * Handles scripts and actions for the WP Dashboard Page.
12 *
13 * @since 4.2.1
14 */
15 class Hustle_Wp_Dashboard_Page {
16
17 /**
18 * Hustle's settings for the WP Dashboard analytics widget.
19 *
20 * @since 4.2.1
21 * @var array
22 */
23 private $settings;
24
25 /**
26 * Class constructor.
27 *
28 * @since 4.2.1
29 * @param array $analytics_settings Hustle's settings for the WP Dashboard analytics widget.
30 */
31 public function __construct( $analytics_settings ) {
32
33 $this->settings = $analytics_settings;
34
35 // Load styles for Dashboard.
36 add_action( 'admin_print_styles', array( $this, 'register_dashboard_styles' ) );
37 add_filter( 'admin_body_class', array( $this, 'admin_dashboard_body_class' ), 99 );
38
39 // Load scripts for Dashboard.
40 add_action( 'admin_enqueue_scripts', array( $this, 'register_scripts' ) );
41
42 add_action( 'wp_dashboard_setup', array( $this, 'analytics_widget_setup' ) );
43
44 add_action( 'wp_ajax_hustle_get_wp_dashboard_widget_data', array( $this, 'get_wp_dashboard_widget_data' ) );
45 }
46
47 /**
48 * Registers styles for admin dashboard.
49 *
50 * @since 4.2.1
51 */
52 public function register_dashboard_styles() {
53 wp_register_style(
54 'hstl-roboto',
55 'https://fonts.bunny.net/css?family=Roboto+Condensed:300,300i,400,400i,700,700i|Roboto:300,300i,400,400i,500,500i,700,700i',
56 array(),
57 Opt_In::VERSION
58 );
59
60 wp_register_style(
61 'hstl-opensans',
62 'https://fonts.bunny.net/css?family=Open+Sans:400,400i,700,700i',
63 array(),
64 Opt_In::VERSION
65 );
66
67 wp_enqueue_style( 'hstl-roboto' );
68 wp_enqueue_style( 'hstl-opensans' );
69
70 wp_enqueue_style(
71 'hustle_dashboard_styles',
72 Opt_In::$plugin_url . 'assets/css/dashboard.min.css',
73 array(),
74 Opt_In::VERSION
75 );
76 }
77
78 /**
79 * Registers scripts.
80 *
81 * @since 4.2.1
82 */
83 public function register_scripts() {
84
85 wp_enqueue_script(
86 'chartjs',
87 Opt_In::$plugin_url . 'assets/js/vendor/chartjs/chart.min.js',
88 array(),
89 '2.7.2',
90 true
91 );
92
93 wp_register_script(
94 'hustle_wp_dashboard_script',
95 Opt_In::$plugin_url . 'assets/js/wp-dashboard.min.js',
96 array( 'jquery', 'chartjs' ),
97 Opt_In::VERSION,
98 true
99 );
100
101 // Days labels for the chart.
102 for ( $h = 89; $h >= 0; $h-- ) {
103 $time = strtotime( '-' . $h . ' days' );
104 $days_array[] = gmdate( 'F d', $time );
105 }
106
107 // These are the labels for the different tracking types.
108 $tracking_actions = array(
109 'view' => esc_html__( 'Views', 'hustle' ),
110 'conversion' => esc_html__( 'All Conversions', 'hustle' ),
111 'cta_conversion' => esc_html__( 'CTA Conversions', 'hustle' ),
112 'optin_conversion' => esc_html__( 'Optin Conversions', 'hustle' ),
113 'rate' => esc_html__( 'Conversion Rate', 'hustle' ),
114 );
115
116 $data = array(
117 'days_labels' => $days_array,
118 'tracking_actions' => $tracking_actions,
119 'active_module_types' => array_map( 'esc_html', (array) $this->settings['modules'] ),
120 'loading' => esc_html__( 'Loading...', 'hustle' ),
121 'last_updated_ago' => esc_html__( 'Last Updated - {time} ago', 'hustle' ),
122 );
123
124 wp_localize_script( 'hustle_wp_dashboard_script', 'hustleVars', $data );
125 wp_enqueue_script( 'hustle_wp_dashboard_script' );
126 }
127
128 /**
129 * Modify admin dashboard body class to our own advantage.
130 *
131 * @since 4.2.1
132 *
133 * @param string $classes Current classes.
134 * @return mixed
135 */
136 public function admin_dashboard_body_class( $classes ) {
137 $classes .= ' sui-hustle-dashboard';
138
139 return $classes;
140 }
141
142 /**
143 * Setup the Hustle analytics dashboard widgets.
144 *
145 * @since 4.1.0
146 */
147 public function analytics_widget_setup() {
148
149 if ( ! Hustle_Settings_Admin::global_tracking() ) {
150 return;
151 }
152
153 $title = ! empty( $this->settings['title'] ) ? $this->settings['title'] : ' ';
154 wp_add_dashboard_widget(
155 'hustle_analytics',
156 esc_html( $title ),
157 array( $this, 'render_analytics_widget' )
158 );
159 }
160
161 /**
162 * Outputs the Analytics dashboard widget.
163 *
164 * @since 4.1.0
165 *
166 * @param string $post Widget Control ID.
167 */
168 public function render_analytics_widget( $post ) {
169
170 $renderer = new Hustle_Layout_Helper( $this );
171 $renderer->render(
172 'admin/widget-analytics',
173 array(
174 'settings' => $this->settings,
175 )
176 );
177 }
178
179 /**
180 * Get analytics ranges for dashboard widget
181 *
182 * @since 4.2.1
183 * @return array
184 */
185 public function get_analytic_ranges() {
186 $ranges = array(
187 7 => __( 'Last 7 days', 'hustle' ),
188 30 => __( 'Last 30 days', 'hustle' ),
189 90 => __( 'Last 90 days', 'hustle' ),
190 );
191
192 return $ranges;
193 }
194
195 /**
196 * Retrieves the data for the widget via AJAX.
197 *
198 * @since 4.2.1
199 */
200 public function get_wp_dashboard_widget_data() {
201
202 Opt_In_Utils::validate_ajax_call( 'hustle_update_wp_dashboard_chart' );
203
204 $days_range = filter_input( INPUT_POST, 'days', FILTER_VALIDATE_INT );
205 $tracking_type = filter_input( INPUT_POST, 'trackingType', FILTER_SANITIZE_SPECIAL_CHARS );
206 $delete_cache = filter_input( INPUT_POST, 'delete_cache', FILTER_VALIDATE_BOOLEAN, FILTER_NULL_ON_FAILURE );
207
208 $module_types_to_display = $this->settings['modules'];
209
210 // The required fields are missing. Abort.
211 if ( ! $days_range || ! $tracking_type || empty( $module_types_to_display ) ) {
212 wp_send_json_error();
213 }
214
215 $allowed_ranges = array_keys( $this->get_analytic_ranges() );
216 $days_range = in_array( $days_range, $allowed_ranges, true ) ? $days_range : 7;
217
218 $cache_group = 'hustle_dashboard_tracking_analytics';
219 if ( $delete_cache ) {
220 $widget_data = false;
221 $this->delete_tracking_analytics_cache( $allowed_ranges, $cache_group );
222 } else {
223 // Try to retrieve the cached data.
224 $widget_data = wp_cache_get( $days_range, $cache_group );
225 }
226
227 // Retrieve the data if not cached.
228 if ( false === $widget_data ) {
229 $widget_data = $this->get_formatted_tracking_data( $days_range );
230
231 // Set last updated time.
232 $widget_data['last_updated'] = time();
233 $cache_expire = $this->get_tracking_analytics_cache_expire();
234 wp_cache_set( $days_range, $widget_data, $cache_group, $cache_expire );
235 }
236
237 // Set last updated time human difference.
238 $widget_data['last_updated'] = human_time_diff( $widget_data['last_updated'] );
239
240 wp_send_json_success( $widget_data );
241 }
242
243 /**
244 * Gets the data formatted as it's expected for the chart.
245 *
246 * @since 4.2.1
247 *
248 * @param integer $days_range Days before today.
249 * @return array
250 */
251 private function get_formatted_tracking_data( $days_range ) {
252
253 $tracking = Hustle_Tracking_Model::get_instance();
254
255 // Daily stats for the period.
256 $daily_stats = $this->get_and_format_daily_stats( $days_range, $tracking );
257
258 // Totals from the previous range.
259 $previous_range_totals = $this->get_and_format_prev_range_totals( $days_range, $tracking );
260
261 // Overall totals to show, including the growth rate.
262 $totals = $this->get_analytics_totals( $daily_stats, $previous_range_totals );
263
264 $widget_data = array(
265 'data' => $daily_stats,
266 'totals' => $totals,
267 );
268
269 return $widget_data;
270 }
271
272 /**
273 * Gets the raw values for the daily stats and formats it.
274 *
275 * @since 4.2.1
276 *
277 * @param integer $days_range Number of days before today.
278 * @param Hustle_Tracking_Model $tracking Instance of the tracking model.
279 * @return array
280 */
281 private function get_and_format_daily_stats( $days_range, $tracking ) {
282
283 $raw_result = $tracking->get_wp_dash_daily_stats_data( $days_range );
284 $final_data = $this->get_default_analytics_stats( $days_range );
285
286 foreach ( $raw_result as $data ) {
287
288 $module_type = $data['module_type'];
289 $action = $data['action'];
290
291 $day = explode( ' ', $data['date_created'] )[0];
292
293 // Use the default days as the base. Skip the current day if it's not defined.
294 if ( ! isset( $final_data[ $module_type ][ $action ][ $day ] ) ) {
295 continue;
296 }
297 $final_data[ $module_type ][ $action ][ $day ] += $data['counter'];
298 $final_data['overall'][ $action ][ $day ] += $data['counter'];
299
300 // We have 2 conversion types: CTA and Optin. We also have an overall
301 // count for conversions under simply 'conversion'. Sum these for that metric.
302 if ( 'optin_conversion' === $action || 'cta_conversion' === $action ) {
303 $final_data[ $module_type ]['conversion'][ $day ] += $data['counter'];
304 $final_data['overall']['conversion'][ $day ] += $data['counter'];
305 }
306
307 // Module types for embeds and ssharing module have the display type suffixed.
308 // Map these to their main module type.
309 if ( 0 === strpos( $module_type, 'embedded_' ) ) {
310 $final_data['embedded'][ $action ][ $day ] += $data['counter'];
311
312 // Same check as before: We merge CTA and Optin conversions into the 'conversion' key.
313 if ( 'optin_conversion' === $action || 'cta_conversion' === $action ) {
314 $final_data['embedded']['conversion'][ $day ] += $data['counter'];
315 }
316 }
317 if ( 0 === strpos( $module_type, 'social_sharing_' ) ) {
318 $final_data['social_sharing'][ $action ][ $day ] += $data['counter'];
319 }
320 }
321
322 // Count rates.
323 foreach ( $final_data as $block => $types ) {
324
325 // Calculate the rates.
326 foreach ( $types as $type => $days ) {
327 if ( 'rate' !== $type ) {
328 continue;
329 }
330 foreach ( $days as $day => $val ) {
331 if ( ! empty( $final_data[ $block ]['view'][ $day ] ) && ! empty( $final_data[ $block ]['conversion'][ $day ] ) ) {
332 $final_data[ $block ]['rate'][ $day ] = round( 100 * $final_data[ $block ]['conversion'][ $day ] / $final_data[ $block ]['view'][ $day ], 2 );
333 }
334 }
335 }
336 }
337
338 return $final_data;
339 }
340
341 /**
342 * Gets default analytics stats with zero values.
343 *
344 * @since 4.1.0
345 *
346 * @param int $days_ago Number of days ago when to begin the stats.
347 * @return array
348 */
349 private function get_default_analytics_stats( $days_ago ) {
350 $days = array();
351 --$days_ago;
352
353 for ( $i = $days_ago; 0 <= $i; $i-- ) {
354 $days[] = date( 'Y-m-d', time() - $i * DAY_IN_SECONDS ); // phpcs:ignore WordPress.DateTime.RestrictedFunctions.date_date
355 }
356
357 $all_blocks = array(
358 'overall',
359 'popup',
360 'slidein',
361 'embedded',
362 'embedded_inline',
363 'embedded_widget',
364 'embedded_shortcode',
365 'social_sharing',
366 'social_sharing_floating',
367 'social_sharing_inline',
368 'social_sharing_widget',
369 'social_sharing_shortcode',
370 );
371
372 $all_types = array(
373 'view',
374 'conversion',
375 'cta_conversion',
376 'optin_conversion',
377 'rate',
378 );
379 $final_data = array();
380 foreach ( $all_blocks as $block ) {
381 foreach ( $all_types as $type ) {
382 foreach ( $days as $day ) {
383 $final_data[ $block ][ $type ][ $day ] = 0;
384 }
385 }
386 }
387
388 return $final_data;
389 }
390
391 /**
392 * Gets the raw values for the previous range totals and formats it.
393 * This is used to define the growth rate.
394 *
395 * @since 4.2.1
396 *
397 * @param integer $days_range Number of days before today.
398 * @param Hustle_Tracking_Model $tracking Instance of the tracking model.
399 * @return array
400 */
401 private function get_and_format_prev_range_totals( $days_range, $tracking ) {
402
403 $module_types_map = array(
404 'popup' => Hustle_Module_Model::POPUP_MODULE,
405 'slidein' => Hustle_Module_Model::SLIDEIN_MODULE,
406 'embedded_inline' => Hustle_Module_Model::EMBEDDED_MODULE,
407 'embedded_shortcode' => Hustle_Module_Model::EMBEDDED_MODULE,
408 'embedded_widget' => Hustle_Module_Model::EMBEDDED_MODULE,
409 'social_sharing_inline' => Hustle_Module_Model::SOCIAL_SHARING_MODULE,
410 'social_sharing_shortcode' => Hustle_Module_Model::SOCIAL_SHARING_MODULE,
411 'social_sharing_widget' => Hustle_Module_Model::SOCIAL_SHARING_MODULE,
412 'social_sharing_floating' => Hustle_Module_Model::SOCIAL_SHARING_MODULE,
413 );
414
415 $default_action_counters = array(
416 'view' => 0,
417 'conversion' => 0,
418 'cta_conversion' => 0,
419 'optin_conversion' => 0,
420 'rate' => 0,
421 );
422
423 $main_counter = array(
424 'overall' => $default_action_counters,
425 Hustle_Module_Model::POPUP_MODULE => $default_action_counters,
426 Hustle_Module_Model::SLIDEIN_MODULE => $default_action_counters,
427 Hustle_Module_Model::EMBEDDED_MODULE => $default_action_counters,
428 Hustle_Module_Model::SOCIAL_SHARING_MODULE => $default_action_counters,
429 );
430
431 // Get the raw data from the tracking model.
432 $raw_result = $tracking->get_per_module_type_totals_prev_range( $days_range );
433
434 // Loop through the results, which are grouped by module type.
435 // The possible module types are the keys from the array $module_types_map.
436 foreach ( $raw_result as $data ) {
437
438 // Not a valid module type, abort.
439 if ( empty( $module_types_map[ $data->module_type ] ) ) {
440 continue;
441 }
442
443 // Not a valid action, abort.
444 if ( ! isset( $default_action_counters[ $data->action ] ) ) {
445 continue;
446 }
447
448 $module_type = $module_types_map[ $data->module_type ];
449 $action = $data->action;
450 $count = $data->tracked_count;
451
452 $main_counter[ $module_type ][ $action ] += $count;
453
454 $main_counter['overall'][ $action ] += $count;
455 }
456
457 // Let's now calculate the total conversions (CTA + Optin), the conversion rate, and overalls.
458 foreach ( $main_counter as $module_type => $action_counter ) {
459
460 $conversion_count = $action_counter['optin_conversion'] + $action_counter['cta_conversion'];
461
462 $rate = 0;
463 $total_views = $action_counter['view'];
464 if ( $total_views && $conversion_count ) {
465 $rate = round( ( $conversion_count / $total_views ) * 100, 2 );
466 }
467
468 $main_counter[ $module_type ]['rate'] = $rate;
469
470 $main_counter[ $module_type ]['conversion'] = $conversion_count;
471 }
472
473 return $main_counter;
474 }
475
476 /**
477 * Calculate the totals from the numbers we already got.
478 * Here you get the per module total for each tracking
479 * action (cta conversion, optin conversion, view),
480 * as well as the growth rate compared to the previous
481 * period of the same current length.
482 *
483 * @since 4.2.1
484 *
485 * @param array $stats The per day tracking data already retrieved for the chart.
486 * @param array $previous_range_totals The totals for the previous range.
487 * @return array
488 */
489 private function get_analytics_totals( $stats, $previous_range_totals ) {
490
491 $total_count = array();
492 foreach ( $stats as $module_type => $action_data ) {
493
494 // Skip the module types with their display type attached. We won't be using these.
495 if ( 0 === strpos( $module_type, 'embedded_' ) || 0 === strpos( $module_type, 'social_sharing_' ) ) {
496 continue;
497 }
498
499 foreach ( $action_data as $tracking_action => $data ) {
500
501 // We'll handle the conversion rate in a sec.
502 if ( 'rate' === $tracking_action ) {
503 continue;
504 }
505
506 $prev_period_total = $previous_range_totals[ $module_type ][ $tracking_action ];
507 $current_total = array_sum( $data );
508 $growth = $this->calculate_growth( $current_total, $prev_period_total );
509
510 $total_count[ $module_type ][ $tracking_action ] = array(
511 'total' => $current_total,
512 'trend' => $growth,
513 );
514
515 }
516
517 // Let's calculate the conversion rate.
518 $rate = 0;
519 $total_views = $total_count[ $module_type ]['view']['total'];
520 $total_conversions = $total_count[ $module_type ]['conversion']['total'];
521 if ( $total_views && $total_conversions ) {
522 $rate = round( ( $total_conversions / $total_views ) * 100, 2 );
523 }
524 $prev_period_rate = $previous_range_totals[ $module_type ]['rate'];
525 $rate_growth = $this->calculate_growth( $rate, $prev_period_rate );
526
527 $total_count[ $module_type ]['rate'] = array(
528 'total' => $rate,
529 'trend' => $rate_growth,
530 );
531 }
532
533 return $total_count;
534 }
535
536 /**
537 * Calculates the growth between two values.
538 *
539 * @since 4.2.1
540 *
541 * @param int $current_total Value of the current range.
542 * @param int $prev_period_total Value of the previous range.
543 * @return int
544 */
545 private function calculate_growth( $current_total, $prev_period_total ) {
546
547 $growth = 0;
548 if ( $current_total && $prev_period_total ) {
549
550 $growth = ( $current_total - $prev_period_total ) / $prev_period_total * 100;
551 $growth = round( $growth, 2 );
552 }
553 return $growth;
554 }
555
556 /**
557 * The cache expires at midnight, following WP's timezone.
558 * Return seconds to keep the cache.
559 *
560 * @since 4.4.6
561 * @return int
562 */
563 private function get_tracking_analytics_cache_expire() {
564 $timezone = wp_timezone();
565 $date = new DateTime( 'tomorrow', $timezone );
566 $cache_expire = $date->format( 'U' ) - time();
567
568 return $cache_expire;
569 }
570
571 /**
572 * Delete tracking cache data
573 *
574 * @param array $allowed_ranges array of ranges.
575 * @param string $cache_group cache key group.
576 *
577 * @since 4.4.6
578 */
579 private function delete_tracking_analytics_cache( $allowed_ranges, $cache_group ) {
580 foreach ( $allowed_ranges as $range ) {
581 wp_cache_delete( $range, $cache_group );
582 }
583 }
584 }
585