File "class-hustle-wp-dashboard-page.php"
Full Path: /home/londdqdw/public_html/06/wp-content/plugins/wordpress-popup/inc/class-hustle-wp-dashboard-page.php
File size: 16.58 KB
MIME-type: text/x-php
Charset: utf-8
<?php
/**
* File for Hustle_Wp_Dashboard_Page class.
*
* @package Hustle
* @since 4.2.1
*/
/**
* Class Hustle_Wp_Dashboard_Page.
* Handles scripts and actions for the WP Dashboard Page.
*
* @since 4.2.1
*/
class Hustle_Wp_Dashboard_Page {
/**
* Hustle's settings for the WP Dashboard analytics widget.
*
* @since 4.2.1
* @var array
*/
private $settings;
/**
* Class constructor.
*
* @since 4.2.1
* @param array $analytics_settings Hustle's settings for the WP Dashboard analytics widget.
*/
public function __construct( $analytics_settings ) {
$this->settings = $analytics_settings;
// Load styles for Dashboard.
add_action( 'admin_print_styles', array( $this, 'register_dashboard_styles' ) );
add_filter( 'admin_body_class', array( $this, 'admin_dashboard_body_class' ), 99 );
// Load scripts for Dashboard.
add_action( 'admin_enqueue_scripts', array( $this, 'register_scripts' ) );
add_action( 'wp_dashboard_setup', array( $this, 'analytics_widget_setup' ) );
add_action( 'wp_ajax_hustle_get_wp_dashboard_widget_data', array( $this, 'get_wp_dashboard_widget_data' ) );
}
/**
* Registers styles for admin dashboard.
*
* @since 4.2.1
*/
public function register_dashboard_styles() {
wp_register_style(
'hstl-roboto',
'https://fonts.bunny.net/css?family=Roboto+Condensed:300,300i,400,400i,700,700i|Roboto:300,300i,400,400i,500,500i,700,700i',
array(),
Opt_In::VERSION
);
wp_register_style(
'hstl-opensans',
'https://fonts.bunny.net/css?family=Open+Sans:400,400i,700,700i',
array(),
Opt_In::VERSION
);
wp_enqueue_style( 'hstl-roboto' );
wp_enqueue_style( 'hstl-opensans' );
wp_enqueue_style(
'hustle_dashboard_styles',
Opt_In::$plugin_url . 'assets/css/dashboard.min.css',
array(),
Opt_In::VERSION
);
}
/**
* Registers scripts.
*
* @since 4.2.1
*/
public function register_scripts() {
wp_enqueue_script(
'chartjs',
Opt_In::$plugin_url . 'assets/js/vendor/chartjs/chart.min.js',
array(),
'2.7.2',
true
);
wp_register_script(
'hustle_wp_dashboard_script',
Opt_In::$plugin_url . 'assets/js/wp-dashboard.min.js',
array( 'jquery', 'chartjs' ),
Opt_In::VERSION,
true
);
// Days labels for the chart.
for ( $h = 89; $h >= 0; $h-- ) {
$time = strtotime( '-' . $h . ' days' );
$days_array[] = gmdate( 'F d', $time );
}
// These are the labels for the different tracking types.
$tracking_actions = array(
'view' => esc_html__( 'Views', 'hustle' ),
'conversion' => esc_html__( 'All Conversions', 'hustle' ),
'cta_conversion' => esc_html__( 'CTA Conversions', 'hustle' ),
'optin_conversion' => esc_html__( 'Optin Conversions', 'hustle' ),
'rate' => esc_html__( 'Conversion Rate', 'hustle' ),
);
$data = array(
'days_labels' => $days_array,
'tracking_actions' => $tracking_actions,
'active_module_types' => array_map( 'esc_html', (array) $this->settings['modules'] ),
'loading' => esc_html__( 'Loading...', 'hustle' ),
'last_updated_ago' => esc_html__( 'Last Updated - {time} ago', 'hustle' ),
);
wp_localize_script( 'hustle_wp_dashboard_script', 'hustleVars', $data );
wp_enqueue_script( 'hustle_wp_dashboard_script' );
}
/**
* Modify admin dashboard body class to our own advantage.
*
* @since 4.2.1
*
* @param string $classes Current classes.
* @return mixed
*/
public function admin_dashboard_body_class( $classes ) {
$classes .= ' sui-hustle-dashboard';
return $classes;
}
/**
* Setup the Hustle analytics dashboard widgets.
*
* @since 4.1.0
*/
public function analytics_widget_setup() {
if ( ! Hustle_Settings_Admin::global_tracking() ) {
return;
}
$title = ! empty( $this->settings['title'] ) ? $this->settings['title'] : ' ';
wp_add_dashboard_widget(
'hustle_analytics',
esc_html( $title ),
array( $this, 'render_analytics_widget' )
);
}
/**
* Outputs the Analytics dashboard widget.
*
* @since 4.1.0
*
* @param string $post Widget Control ID.
*/
public function render_analytics_widget( $post ) {
$renderer = new Hustle_Layout_Helper( $this );
$renderer->render(
'admin/widget-analytics',
array(
'settings' => $this->settings,
)
);
}
/**
* Get analytics ranges for dashboard widget
*
* @since 4.2.1
* @return array
*/
public function get_analytic_ranges() {
$ranges = array(
7 => __( 'Last 7 days', 'hustle' ),
30 => __( 'Last 30 days', 'hustle' ),
90 => __( 'Last 90 days', 'hustle' ),
);
return $ranges;
}
/**
* Retrieves the data for the widget via AJAX.
*
* @since 4.2.1
*/
public function get_wp_dashboard_widget_data() {
Opt_In_Utils::validate_ajax_call( 'hustle_update_wp_dashboard_chart' );
$days_range = filter_input( INPUT_POST, 'days', FILTER_VALIDATE_INT );
$tracking_type = filter_input( INPUT_POST, 'trackingType', FILTER_SANITIZE_SPECIAL_CHARS );
$delete_cache = filter_input( INPUT_POST, 'delete_cache', FILTER_VALIDATE_BOOLEAN, FILTER_NULL_ON_FAILURE );
$module_types_to_display = $this->settings['modules'];
// The required fields are missing. Abort.
if ( ! $days_range || ! $tracking_type || empty( $module_types_to_display ) ) {
wp_send_json_error();
}
$allowed_ranges = array_keys( $this->get_analytic_ranges() );
$days_range = in_array( $days_range, $allowed_ranges, true ) ? $days_range : 7;
$cache_group = 'hustle_dashboard_tracking_analytics';
if ( $delete_cache ) {
$widget_data = false;
$this->delete_tracking_analytics_cache( $allowed_ranges, $cache_group );
} else {
// Try to retrieve the cached data.
$widget_data = wp_cache_get( $days_range, $cache_group );
}
// Retrieve the data if not cached.
if ( false === $widget_data ) {
$widget_data = $this->get_formatted_tracking_data( $days_range );
// Set last updated time.
$widget_data['last_updated'] = time();
$cache_expire = $this->get_tracking_analytics_cache_expire();
wp_cache_set( $days_range, $widget_data, $cache_group, $cache_expire );
}
// Set last updated time human difference.
$widget_data['last_updated'] = human_time_diff( $widget_data['last_updated'] );
wp_send_json_success( $widget_data );
}
/**
* Gets the data formatted as it's expected for the chart.
*
* @since 4.2.1
*
* @param integer $days_range Days before today.
* @return array
*/
private function get_formatted_tracking_data( $days_range ) {
$tracking = Hustle_Tracking_Model::get_instance();
// Daily stats for the period.
$daily_stats = $this->get_and_format_daily_stats( $days_range, $tracking );
// Totals from the previous range.
$previous_range_totals = $this->get_and_format_prev_range_totals( $days_range, $tracking );
// Overall totals to show, including the growth rate.
$totals = $this->get_analytics_totals( $daily_stats, $previous_range_totals );
$widget_data = array(
'data' => $daily_stats,
'totals' => $totals,
);
return $widget_data;
}
/**
* Gets the raw values for the daily stats and formats it.
*
* @since 4.2.1
*
* @param integer $days_range Number of days before today.
* @param Hustle_Tracking_Model $tracking Instance of the tracking model.
* @return array
*/
private function get_and_format_daily_stats( $days_range, $tracking ) {
$raw_result = $tracking->get_wp_dash_daily_stats_data( $days_range );
$final_data = $this->get_default_analytics_stats( $days_range );
foreach ( $raw_result as $data ) {
$module_type = $data['module_type'];
$action = $data['action'];
$day = explode( ' ', $data['date_created'] )[0];
// Use the default days as the base. Skip the current day if it's not defined.
if ( ! isset( $final_data[ $module_type ][ $action ][ $day ] ) ) {
continue;
}
$final_data[ $module_type ][ $action ][ $day ] += $data['counter'];
$final_data['overall'][ $action ][ $day ] += $data['counter'];
// We have 2 conversion types: CTA and Optin. We also have an overall
// count for conversions under simply 'conversion'. Sum these for that metric.
if ( 'optin_conversion' === $action || 'cta_conversion' === $action ) {
$final_data[ $module_type ]['conversion'][ $day ] += $data['counter'];
$final_data['overall']['conversion'][ $day ] += $data['counter'];
}
// Module types for embeds and ssharing module have the display type suffixed.
// Map these to their main module type.
if ( 0 === strpos( $module_type, 'embedded_' ) ) {
$final_data['embedded'][ $action ][ $day ] += $data['counter'];
// Same check as before: We merge CTA and Optin conversions into the 'conversion' key.
if ( 'optin_conversion' === $action || 'cta_conversion' === $action ) {
$final_data['embedded']['conversion'][ $day ] += $data['counter'];
}
}
if ( 0 === strpos( $module_type, 'social_sharing_' ) ) {
$final_data['social_sharing'][ $action ][ $day ] += $data['counter'];
}
}
// Count rates.
foreach ( $final_data as $block => $types ) {
// Calculate the rates.
foreach ( $types as $type => $days ) {
if ( 'rate' !== $type ) {
continue;
}
foreach ( $days as $day => $val ) {
if ( ! empty( $final_data[ $block ]['view'][ $day ] ) && ! empty( $final_data[ $block ]['conversion'][ $day ] ) ) {
$final_data[ $block ]['rate'][ $day ] = round( 100 * $final_data[ $block ]['conversion'][ $day ] / $final_data[ $block ]['view'][ $day ], 2 );
}
}
}
}
return $final_data;
}
/**
* Gets default analytics stats with zero values.
*
* @since 4.1.0
*
* @param int $days_ago Number of days ago when to begin the stats.
* @return array
*/
private function get_default_analytics_stats( $days_ago ) {
$days = array();
$days_ago--;
for ( $i = $days_ago; 0 <= $i; $i-- ) {
$days[] = date( 'Y-m-d', time() - $i * DAY_IN_SECONDS ); // phpcs:ignore WordPress.DateTime.RestrictedFunctions.date_date
}
$all_blocks = array(
'overall',
'popup',
'slidein',
'embedded',
'embedded_inline',
'embedded_widget',
'embedded_shortcode',
'social_sharing',
'social_sharing_floating',
'social_sharing_inline',
'social_sharing_widget',
'social_sharing_shortcode',
);
$all_types = array(
'view',
'conversion',
'cta_conversion',
'optin_conversion',
'rate',
);
$final_data = array();
foreach ( $all_blocks as $block ) {
foreach ( $all_types as $type ) {
foreach ( $days as $day ) {
$final_data[ $block ][ $type ][ $day ] = 0;
}
}
}
return $final_data;
}
/**
* Gets the raw values for the previous range totals and formats it.
* This is used to define the growth rate.
*
* @since 4.2.1
*
* @param integer $days_range Number of days before today.
* @param Hustle_Tracking_Model $tracking Instance of the tracking model.
* @return array
*/
private function get_and_format_prev_range_totals( $days_range, $tracking ) {
$module_types_map = array(
'popup' => Hustle_Module_Model::POPUP_MODULE,
'slidein' => Hustle_Module_Model::SLIDEIN_MODULE,
'embedded_inline' => Hustle_Module_Model::EMBEDDED_MODULE,
'embedded_shortcode' => Hustle_Module_Model::EMBEDDED_MODULE,
'embedded_widget' => Hustle_Module_Model::EMBEDDED_MODULE,
'social_sharing_inline' => Hustle_Module_Model::SOCIAL_SHARING_MODULE,
'social_sharing_shortcode' => Hustle_Module_Model::SOCIAL_SHARING_MODULE,
'social_sharing_widget' => Hustle_Module_Model::SOCIAL_SHARING_MODULE,
'social_sharing_floating' => Hustle_Module_Model::SOCIAL_SHARING_MODULE,
);
$default_action_counters = array(
'view' => 0,
'conversion' => 0,
'cta_conversion' => 0,
'optin_conversion' => 0,
'rate' => 0,
);
$main_counter = array(
'overall' => $default_action_counters,
Hustle_Module_Model::POPUP_MODULE => $default_action_counters,
Hustle_Module_Model::SLIDEIN_MODULE => $default_action_counters,
Hustle_Module_Model::EMBEDDED_MODULE => $default_action_counters,
Hustle_Module_Model::SOCIAL_SHARING_MODULE => $default_action_counters,
);
// Get the raw data from the tracking model.
$raw_result = $tracking->get_per_module_type_totals_prev_range( $days_range );
// Loop through the results, which are grouped by module type.
// The possible module types are the keys from the array $module_types_map.
foreach ( $raw_result as $data ) {
// Not a valid module type, abort.
if ( empty( $module_types_map[ $data->module_type ] ) ) {
continue;
}
// Not a valid action, abort.
if ( ! isset( $default_action_counters[ $data->action ] ) ) {
continue;
}
$module_type = $module_types_map[ $data->module_type ];
$action = $data->action;
$count = $data->tracked_count;
$main_counter[ $module_type ][ $action ] += $count;
$main_counter['overall'][ $action ] += $count;
}
// Let's now calculate the total conversions (CTA + Optin), the conversion rate, and overalls.
foreach ( $main_counter as $module_type => $action_counter ) {
$conversion_count = $action_counter['optin_conversion'] + $action_counter['cta_conversion'];
$rate = 0;
$total_views = $action_counter['view'];
if ( $total_views && $conversion_count ) {
$rate = round( ( $conversion_count / $total_views ) * 100, 2 );
}
$main_counter[ $module_type ]['rate'] = $rate;
$main_counter[ $module_type ]['conversion'] = $conversion_count;
}
return $main_counter;
}
/**
* Calculate the totals from the numbers we already got.
* Here you get the per module total for each tracking
* action (cta conversion, optin conversion, view),
* as well as the growth rate compared to the previous
* period of the same current length.
*
* @since 4.2.1
*
* @param array $stats The per day tracking data already retrieved for the chart.
* @param array $previous_range_totals The totals for the previous range.
* @return array
*/
private function get_analytics_totals( $stats, $previous_range_totals ) {
$total_count = array();
foreach ( $stats as $module_type => $action_data ) {
// Skip the module types with their display type attached. We won't be using these.
if ( 0 === strpos( $module_type, 'embedded_' ) || 0 === strpos( $module_type, 'social_sharing_' ) ) {
continue;
}
foreach ( $action_data as $tracking_action => $data ) {
// We'll handle the conversion rate in a sec.
if ( 'rate' === $tracking_action ) {
continue;
}
$prev_period_total = $previous_range_totals[ $module_type ][ $tracking_action ];
$current_total = array_sum( $data );
$growth = $this->calculate_growth( $current_total, $prev_period_total );
$total_count[ $module_type ][ $tracking_action ] = array(
'total' => $current_total,
'trend' => $growth,
);
}
// Let's calculate the conversion rate.
$rate = 0;
$total_views = $total_count[ $module_type ]['view']['total'];
$total_conversions = $total_count[ $module_type ]['conversion']['total'];
if ( $total_views && $total_conversions ) {
$rate = round( ( $total_conversions / $total_views ) * 100, 2 );
}
$prev_period_rate = $previous_range_totals[ $module_type ]['rate'];
$rate_growth = $this->calculate_growth( $rate, $prev_period_rate );
$total_count[ $module_type ]['rate'] = array(
'total' => $rate,
'trend' => $rate_growth,
);
}
return $total_count;
}
/**
* Calculates the growth between two values.
*
* @since 4.2.1
*
* @param int $current_total Value of the current range.
* @param int $prev_period_total Value of the previous range.
* @return int
*/
private function calculate_growth( $current_total, $prev_period_total ) {
$growth = 0;
if ( $current_total && $prev_period_total ) {
$growth = ( $current_total - $prev_period_total ) / $prev_period_total * 100;
$growth = round( $growth, 2 );
}
return $growth;
}
/**
* The cache expires at midnight, following WP's timezone.
* Return seconds to keep the cache.
*
* @since 4.4.6
* @return int
*/
private function get_tracking_analytics_cache_expire() {
$timezone = wp_timezone();
$date = new DateTime( 'tomorrow', $timezone );
$cache_expire = $date->format( 'U' ) - time();
return $cache_expire;
}
/**
* Delete tracking cache data
*
* @param array $allowed_ranges array of ranges.
* @param string $cache_group cache key group.
*
* @since 4.4.6
*/
private function delete_tracking_analytics_cache( $allowed_ranges, $cache_group ) {
foreach ( $allowed_ranges as $range ) {
wp_cache_delete( $range, $cache_group );
}
}
}