nonce = $this->nonce( basename( __FILE__ ), 'reports' ); } /** * Create hooks. * * @return void */ public function hooks() { add_action( 'admin_init', array( $this, 'admin_init' ) ); foreach ( $this->get_action_keys() as $key ) { $key_underscore = self::_key( $key ); add_action( 'wp_ajax_' . $this->action_string( $key ), array( $this, 'action_' . $key_underscore ) ); } } /** * Fires on admin_init. Parse input if available. * * @return void */ public function admin_init() { $this->parse_input(); } /** * Enqueue scripts for the reports page. * * @return void */ public function enqueue() { $this->build_report_data(); wp_enqueue_script( 'jquery' ); wp_enqueue_script( 'jquery-ui-core' ); wp_enqueue_script( 'jquery-ui-datepicker' ); wp_enqueue_script( 'jquery-ui-autocomplete' ); wp_enqueue_script( 'jquery-ui-tabs' ); $this->enqueue_jquery_ui_styles(); $handle = Util::ns( 'reports' ); wp_register_script( $handle, AdCommander::assets_url() . 'js/reports.js', array( 'jquery', 'jquery-ui-core', 'jquery-ui-datepicker', 'jquery-ui-autocomplete', 'jquery-ui-tabs', ), AdCommander::version(), array( 'in_footer' => true ) ); wp_enqueue_script( $handle ); Util::enqueue_script_data( $handle, array( 'ajaxurl' => admin_url( 'admin-ajax.php' ), 'actions' => $this->get_ajax_actions(), 'reportData' => $this->current_data_bydate, 'adData' => $this->current_data_byad, 'groupData' => $this->current_data_bygroup, 'campaignData' => $this->current_data_bycampaign, 'advertiserData' => $this->current_data_byadvertiser, 'tableByDateCols' => $this->table_by_date_columns(), 'tableByAdCols' => $this->table_by_ad_columns(), 'tableByGroupCols' => $this->table_by_group_columns(), 'tableByCampaignCols' => $this->table_by_campaign_columns(), 'tableByAdvertiserCols' => $this->table_by_advertiser_columns(), 'chartType' => $this->get_chart_type(), ) ); } /** * Get necessary action keys, which will be used to create wp_ajax hooks. * * @return array */ private function get_action_keys() { return array( 'get-ads-for-filter', 'export-csv', ); } /** * Creates an array of all of the necessary actions. * * @return array */ private function get_ajax_actions() { $actions = array(); foreach ( $this->get_action_keys() as $key ) { $actions[ self::_key( $key ) ] = array( 'action' => $this->action_string( $key ), 'security' => wp_create_nonce( $this->nonce_string( $key ) ), ); } return $actions; } /** * Create HTML for the title of the page. * * @param string $title The title of the page. * * @return void */ private function title( $title ) { ?>
sf()->title( $title ); ?>
sf()->wo_forms->label( 'chart_type', __( 'Chart:', 'ad-commander' ) ); $this->sf()->wo_forms->inputgroup( 'chart_type', $this->allowed_chart_types(), $this->get_chart_type(), 'radio', array( 'id' => 'adcmdr_chart_type' ) ); ?>
form_start(); $this->form(); $this->start_wrap(); $this->title( __( 'Reports', 'ad-commander' ) ); $this->chart(); $this->form_end(); $this->end_wrap(); $this->start_wrap(); $this->tabs_start(); $this->tab_nav(); $this->table_by_date(); $this->table_by_ad(); $this->table_by_group(); $this->table_by_campaign(); $this->table_by_advertiser(); $this->tabs_end(); $this->end_wrap(); } private function tabs_start() { ?>

is_pro_loaded() ? ProBridge::instance()->pro_label( true ) : ''; ?>
get_group_by() === 'week' ) : ?>
', esc_html( Util::first_day_of_the_week() ), '' ); ?>

is_pro_loaded() ? ProBridge::instance()->pro_label( true ) : ''; ?>

is_pro_loaded() ? ProBridge::instance()->pro_label( true ) : ''; ?>

is_pro_loaded() ? ProBridge::instance()->pro_label( true ) : ''; ?>

is_pro_loaded() ? ProBridge::instance()->pro_label( true ) : ''; ?>

( $this->get_group_by() === 'hour' ) ? __( 'Hour', 'ad-commander' ) : __( 'Date', 'ad-commander' ), 'impressions' => __( 'Impressions', 'ad-commander' ), 'clicks' => __( 'Clicks', 'ad-commander' ), 'ctr' => __( 'CTR', 'ad-commander' ), ); } /** * Create the columns for the by-ad table. */ private function table_by_ad_columns() { return array( 'ad' => __( 'Ad', 'ad-commander' ), 'impressions' => __( 'Impressions', 'ad-commander' ), 'clicks' => __( 'Clicks', 'ad-commander' ), 'ctr' => __( 'CTR', 'ad-commander' ), ); } /** * Create the columns for the by-group table. */ private function table_by_group_columns() { return array( 'group' => __( 'Group', 'ad-commander' ), 'impressions' => __( 'Impressions', 'ad-commander' ), 'clicks' => __( 'Clicks', 'ad-commander' ), 'ctr' => __( 'CTR', 'ad-commander' ), ); } /** * Create the columns for the by-campaign table. */ private function table_by_campaign_columns() { return array( 'campaign' => __( 'Campaign', 'ad-commander' ), 'impressions' => __( 'Impressions', 'ad-commander' ), 'clicks' => __( 'Clicks', 'ad-commander' ), 'ctr' => __( 'CTR', 'ad-commander' ), ); } /** * Create the columns for the by-advertiser table. */ private function table_by_advertiser_columns() { return array( 'advertiser' => __( 'Advertiser', 'ad-commander' ), 'impressions' => __( 'Impressions', 'ad-commander' ), 'clicks' => __( 'Clicks', 'ad-commander' ), 'ctr' => __( 'CTR', 'ad-commander' ), ); } protected function sanitize_all_input( $source = array() ) { $input = array(); foreach ( array( 'period', 'group_by', 'chart_type', 'start_date', 'end_date', 'filter_by_ad_ids', ) as $key ) { if ( ! isset( $source[ $key ] ) ) { continue; } $value = ( is_array( $source[ $key ] ) ) ? array_map( 'sanitize_text_field', wp_unslash( $source[ $key ] ) ) : sanitize_text_field( wp_unslash( $source[ $key ] ) ); switch ( $key ) { case 'start_date': case 'end_date': $type = 'date'; break; case 'filter_by_ad_ids': $type = 'ints'; break; default: $type = 'str'; break; } $value = $this->sanitize_by_type( $value, $type ); $input[ $key ] = $value; } return $input; } /** * Parse the input for later use. */ protected function parse_input() { if ( ! isset( $_REQUEST[ $this->nonce['name'] ] ) ) { return; } if ( ! wp_verify_nonce( sanitize_key( $_REQUEST[ $this->nonce['name'] ] ), $this->nonce['action'] ) ) { die( esc_html( __( 'Security check', 'ad-commander' ) ) ); } $this->input = $this->sanitize_all_input( $_REQUEST ); } /** * Start of the form on the Reports page. * * @return void */ private function form_start() { ?>
sf()->wo_forms->label( 'period', __( 'Period', 'ad-commander' ) ); $this->sf()->wo_forms->select( 'period', self::allowed_periods( false ), $this->get_period(), array( 'id' => 'adcmdr_period' ) ); ?>
sf()->wo_forms->label( 'group_by', __( 'Group By', 'ad-commander' ) ); $this->sf()->wo_forms->select( 'group_by', self::allowed_group_by( false ), $this->get_group_by(), array( 'id' => 'adcmdr_group_by' ) ); ?>
sf()->wo_forms->label( 'start_date', __( 'Start Date', 'ad-commander' ) ); $this->sf()->wo_forms->input( 'start_date', $this->get_start_date(), 'text', array( 'id' => 'adcmdr_start_date', 'classes' => 'adcmdr-datepicker', ) ); ?>
sf()->wo_forms->label( 'end_date', __( 'End Date', 'ad-commander' ) ); $this->sf()->wo_forms->input( 'end_date', $this->get_end_date(), 'text', array( 'id' => 'adcmdr_end_date', 'classes' => 'adcmdr-datepicker', ) ); ?>
sf()->wo_forms->label( 'filter_by_ads', __( 'Filter By Ads', 'ad-commander' ) ); $this->sf()->wo_forms->input( 'filter_by_ads', '', 'text', array( 'id' => 'adcmdr_filter_by_ads', 'placeholder' => __( 'Search by ad title', 'ad-commander' ), ) ); $filtered_ads = $this->get_filtered_ads(); ?>
sf()->message( '' . __( 'Some stats may take a minute to update.', 'ad-commander' ) . '' ); $this->nonce_field( $this->nonce ); ?>
date_display_format ) { if ( $this->get_group_by() !== 'hour' ) { $this->date_display_format = get_option( 'date_format' ); } else { $this->date_display_format = get_option( 'time_format' ); } } return $this->date_display_format; } /** * Alternate display format for dates. * * @param string $format The date format to modify. */ private function date_display_format_alt( $format ) { return str_replace( array( 'F ', ', Y' ), array( 'M ', ', \'y' ), $format ); } /** * The allowed time periods and their related info. * * @param bool $detailed Whether to return a detailed array or not. * * @return array */ public static function allowed_periods( $detailed = true ) { $periods = array( 'today' => array( 'text' => __( 'Today', 'ad-commander' ), 'modifier' => '', ), 'yesterday' => array( 'text' => __( 'Yesterday', 'ad-commander' ), 'modifier' => '', ), 'this_week' => array( 'text' => __( 'This Week', 'ad-commander' ), 'modifier' => 'this week', ), 'this_month' => array( 'text' => __( 'This Month', 'ad-commander' ), 'modifier' => 'first day of this month', ), 'this_year' => array( 'text' => __( 'This Year', 'ad-commander' ), 'modifier' => 'first day of January', ), 'last_week' => array( 'text' => __( 'Last Week', 'ad-commander' ), 'modifier' => 'last week', ), 'last_month' => array( 'text' => __( 'Last Month', 'ad-commander' ), 'modifier' => 'last month', ), 'last_year' => array( 'text' => __( 'Last Year', 'ad-commander' ), 'modifier' => 'last year', ), 'past_7_days' => array( 'text' => __( 'Past 7 days', 'ad-commander' ), 'modifier' => '-6 days', ), 'past_14_days' => array( 'text' => __( 'Past 14 days', 'ad-commander' ), 'modifier' => '-13 days', ), 'past_30_days' => array( 'text' => __( 'Past 30 days', 'ad-commander' ), 'modifier' => '-29 days', ), 'past_365_days' => array( 'text' => __( 'Past 365 days', 'ad-commander' ), 'modifier' => '-364 days', ), 'custom' => array( 'text' => __( 'Custom', 'ad-commander' ), 'modifier' => '', ), ); if ( ! $detailed ) { $periods_simple = array(); foreach ( $periods as $key => $value ) { $periods_simple[ $key ] = $value['text']; } return $periods_simple; } return $periods; } /** * The allowed group_by keys and their related info. * * @param bool $detailed Whether to return a detailed array or not. * * @return array */ public static function allowed_group_by( $detailed = true ) { $periods = array( 'hour' => array( 'text' => __( 'Hour', 'ad-commander' ), 'modifier' => '+1 hour', ), 'day' => array( 'text' => __( 'Day', 'ad-commander' ), 'modifier' => '+1 day', ), 'week' => array( 'text' => __( 'Week', 'ad-commander' ), 'modifier' => '+1 week', ), 'month' => array( 'text' => __( 'Month', 'ad-commander' ), 'modifier' => '+1 month', ), ); if ( ! $detailed ) { $periods_simple = array(); foreach ( $periods as $key => $value ) { $periods_simple[ $key ] = $value['text']; } return $periods_simple; } return $periods; } /** * The allowed chart types. * * @return array */ private function allowed_chart_types() { return array( 'line' => 'Line', 'bar' => 'Bar', ); } /** * Returns a modifier string for a time period if one exists. * * @param string $period The period to find the modifier for. * * @return null|string */ private function datetime_modifier_string( $period ) { $periods = self::allowed_periods(); return isset( $periods[ $period ]['modifier'] ) ? $periods[ $period ]['modifier'] : null; } /** * Returns the ad IDs that we are filtering by, if any. * * @return array */ private function get_filtered_ad_ids() { if ( isset( $this->input['filter_by_ad_ids'] ) ) { return Util::arrayify( $this->input['filter_by_ad_ids'] ); } return array(); } /** * Gets an array of ads based on selected filters. * * @return array */ private function get_filtered_ads() { $ads = $this->get_ads_for_filter(); $filtered_ads = array(); foreach ( $ads as $ad ) { if ( in_array( $ad['id'], $this->get_filtered_ad_ids() ) ) { $filtered_ads[] = $ad; } } return $filtered_ads; } /** * Gets the current filtered period. * * @return string */ private function get_period() { if ( isset( $this->input['period'] ) && in_array( $this->input['period'], array_keys( self::allowed_periods() ) ) ) { return $this->input['period']; } return 'past_7_days'; } /** * Gets the current filtered group_by. * * @return string */ private function get_group_by() { if ( isset( $this->input['group_by'] ) && in_array( $this->input['group_by'], array_keys( self::allowed_group_by() ) ) ) { return $this->input['group_by']; } return 'day'; } /** * Gets the current chart type. * * @return string */ private function get_chart_type() { if ( isset( $this->input['chart_type'] ) && in_array( $this->input['chart_type'], array_keys( $this->allowed_chart_types() ) ) ) { return $this->input['chart_type']; } return 'line'; } /** * Gets the current filtered start date. * * @return string|null */ private function get_start_date() { if ( isset( $this->input['start_date'] ) && $this->get_period() === 'custom' ) { return $this->input['start_date']; } return null; } /** * Gets the current filtered end date. * * @return string|null */ private function get_end_date() { if ( isset( $this->input['end_date'] ) && $this->get_period() === 'custom' ) { return $this->input['end_date']; } return null; } /** * Gets the modifier string for a group_by string. * * @param string $group_by The period we're grouping by. * * @return string */ private function get_group_by_modifier( $group_by ) { switch ( $group_by ) { case 'month': $modifier = 'first day of next month'; break; case 'week': $modifier = 'next ' . Util::first_day_of_the_week(); break; case 'hour': $modifier = 'next hour'; break; case 'day': default: $modifier = 'next day'; break; } return $modifier; } /** * Creates an array of dates from a start and an end date. * * @param array $start_end_dates Array containing two DateTime objects. * @param string $group_by Determines the frequency of the range of dates. * @param bool $include_end_date Whether to include the end date or not. * * @return array */ private function range_of_start_dates( $start_end_dates, $group_by = 'day', $include_end_date = true ) { $start_date = clone $start_end_dates['start_date']; $end_date = clone $start_end_dates['end_date']; if ( $include_end_date ) { $end_date = $end_date->modify( '+1 day' ); // So that $end_date is included in our DatePeriod } if ( $group_by === 'hour' ) { $now = Util::datetime_wp_timezone( current_time( 'Y-m-d H:00:00' ) ); $now->modify( 'next hour' )->modify( '-1 second' ); if ( $end_date->getTimestamp() > $now->getTimestamp() ) { $end_date = clone $now; } } $modifier = $this->get_group_by_modifier( $group_by ); $periods = array(); $date = clone $start_date; while ( $date->getTimestamp() < $end_date->getTimestamp() ) { $periods[] = clone $date; $date = $date->modify( $modifier ); } $dates = array(); $format = $this->date_display_format(); $format_alt = $this->date_display_format_alt( $format ); foreach ( $periods as $key => $value ) { $timestamp = $value->getTimestamp(); $value_formatted = $value->format( $format ); $value_formatted_alt = $value->format( $format_alt ); $dates[] = array( 'display_format' => $value_formatted, 'display_format_alt' => $value_formatted_alt, 'timestamp' => $timestamp, 'datetime' => $value, ); } return $dates; } /** * Creates start and end dates for a given period. * * @param string $period The period for which we are getting start and end dates. * * @return array */ private function start_end_dates( $period ) { if ( $period === 'custom' ) { $start_date_custom = $this->get_start_date(); $end_date_custom = $this->get_end_date(); if ( ! $start_date_custom || ! $end_date_custom ) { return false; } $start_date = Util::datetime_wp_timezone( $start_date_custom )->modify( 'today' ); $end_date = Util::datetime_wp_timezone( $end_date_custom )->modify( 'today' ); } else { $now = current_time( 'Y-m-d H:i:s' ); $now = Util::datetime_wp_timezone( $now )->modify( 'today' ); $end_date = clone $now; if ( $period === 'yesterday' ) { $end_date = $end_date->modify( '-1 day' ); } $start_date = clone $end_date; } $modifier = $this->datetime_modifier_string( $period ); if ( $modifier ) { if ( $modifier === 'this week' ) { $start_week = Util::first_day_of_the_week(); $today = clone $now; /** * If the week starts today, end date and start date are the same. */ if ( $today->format( 'l' ) !== $start_week ) { $start_date->modify( 'last ' . $start_week ); } } elseif ( $modifier === 'last week' ) { $start_week = Util::first_day_of_the_week(); if ( $end_date->format( 'l' ) === $start_week ) { $end_date->modify( '-1 day' ); } else { $end_date->modify( 'last ' . $start_week )->modify( '-1 day' ); } $end_date->modify( 'today' ); $start_date = clone $end_date; $start_date->modify( '-6 days' ); } elseif ( $modifier === 'last month' ) { $end_date->modify( 'last day of last month' )->modify( 'today' ); $start_date = clone $end_date; $start_date->modify( 'first day of this month' ); } elseif ( $modifier === 'last year' ) { $end_date->modify( 'last year December 31' )->modify( 'today' ); $start_date = clone $end_date; $start_date->modify( 'this year January 1' ); } else { $start_date = $start_date->modify( $modifier ); } } $start_date->modify( 'today' ); /** * Return set of dates */ return array( 'start_date' => $start_date, 'end_date' => $end_date, ); } /** * The name of the transient that stores our filtered ads. * * @return string */ public static function filter_ads_transient() { return Util::ns( 'report_filter_ads', '_' ); } /** * Store the available filter ads in a transient so we don't have to query them every time. * * @return array */ private function get_ads_for_filter() { $ads = get_transient( self::filter_ads_transient() ); if ( $ads === false ) { $available_ads = Query::ads( 'post_title', 'asc', Util::any_post_status(), array(), array() ); $ads = array(); if ( ! empty( $available_ads ) ) { foreach ( $available_ads as $ad ) { $ads[] = array( 'id' => $ad->ID, 'title' => $ad->post_title, ); } } set_transient( self::filter_ads_transient(), $ads, MINUTE_IN_SECONDS ); } return $ads; } /** * Wp_ajax hook. Gets all of the ads for use in the filter. * * @return void */ public function action_get_ads_for_filter() { $action = 'get-ads-for-filter'; check_ajax_referer( $this->nonce_string( $action ), 'security' ); if ( ! current_user_can( AdCommander::capability() ) ) { wp_die(); } wp_send_json_success( array( 'action' => $action, 'ads' => $this->get_ads_for_filter(), ) ); wp_die(); } /** * Wp_ajax hook. Gets all of the ads for use in the filter. * * @return void */ public function action_export_csv() { $action = 'export-csv'; check_ajax_referer( $this->nonce_string( $action ), 'security' ); if ( ! current_user_can( AdCommander::capability() ) ) { wp_die(); } if ( ProBridge::instance()->pro_feature_check( 'exportreports' ) ) { AdminExport::instance()->do_export(); } wp_die(); } /** * Get the total stats for a given day or days. * * @param array $ad_ids The ad IDs to total stats for. * @param string|int $days Either 'all' or the number of days to look back when totaling. * @param string $type The type of stat to count. * * @return int */ public function ad_stats( $ad_ids, $days = 'all', $type = 'impressions' ) { if ( $days === 'all' ) { return TrackingLocal::total_stats( $ad_ids, $type, null, null ); } $end_date = Util::datetime_wp_timezone( current_time( 'Y-m-d H:00:00' ) ); $end_date->modify( 'next hour' )->modify( '-1 second' ); $days = ( intval( $days ) - 1 ); if ( $days < 0 ) { $days = 0; } $start_date = clone $end_date; $start_date->modify( '-' . $days . ' days' )->modify( 'today' ); return TrackingLocal::total_stats( $ad_ids, $type, $start_date->getTimestamp(), $end_date->getTimestamp() ); } /** * Builds the data for display on the report page. * * @return void */ protected function build_report_data( $ignore_notices = false ) { $this->current_data_bydate = array(); $this->current_data_byad = array(); $this->current_data_bygroup = array(); $this->current_data_bycampaign = array(); $this->current_data_byadvertiser = array(); $notice = null; $period = $this->get_period(); $start_end_dates = $this->start_end_dates( $period ); if ( ! isset( $start_end_dates['start_date'] ) || ! isset( $start_end_dates['end_date'] ) || ! $start_end_dates['start_date'] || ! $start_end_dates['end_date'] ) { $notice = __( 'There was an error processing your start and end dates.', 'ad-commander' ); } if ( $start_end_dates['start_date'] > $start_end_dates['end_date'] && ! $ignore_notices ) { $notice = __( 'The selected start date is before the end date.', 'ad-commander' ); } if ( $notice && ! $ignore_notices ) { add_action( 'admin_notices', function () use ( $notice ) { ?>

get_group_by(); $start_date_range = $this->range_of_start_dates( $start_end_dates, $group_by ); $i = 0; $parsed_dates = array(); foreach ( $start_date_range as $date ) { $start_timestamp = $date['timestamp']; if ( isset( $start_date_range[ $i + 1 ]['datetime'] ) ) { $end_datetime = clone $start_date_range[ $i + 1 ]['datetime']; $end_datetime->modify( '-1 second' ); } else { $end_datetime = clone $start_end_dates['end_date']; $end_datetime->modify( 'tomorrow' )->modify( '-1 second' ); } $impressions = TrackingLocal::total_impressions( $this->get_filtered_ad_ids(), $start_timestamp, $end_datetime->getTimestamp(), false ); $clicks = TrackingLocal::total_clicks( $this->get_filtered_ad_ids(), $start_timestamp, $end_datetime->getTimestamp(), false ); $display_formatted = $date['display_format']; if ( $group_by !== 'hour' && $date['datetime']->format( $this->date_display_format() ) !== $end_datetime->format( $this->date_display_format() ) ) { $display_formatted = $date['datetime']->format( $this->date_display_format() ) . ' – ' . $end_datetime->format( $this->date_display_format() ); } $parsed_dates[] = array( 'date' => $display_formatted, // . ' - ' . $start_timestamp . ' - ' . $end_datetime->getTimestamp(), // For testing 'date_alt' => $date['display_format_alt'], 'timestamp' => $date['timestamp'], 'impressions' => $impressions, 'clicks' => $clicks, 'ctr' => Util::ctr( $clicks, $impressions ), ); ++$i; } $this->current_data_bydate = $parsed_dates; $parsed_ads = array(); $ad_groups = array(); $ad_campaigns = array(); $ad_advertisers = array(); $ad_ids = TrackingLocal::ads_with_stats( $start_date_range[0]['timestamp'], $end_datetime->getTimestamp(), $this->get_filtered_ad_ids() ); if ( ! empty( $ad_ids ) ) { $ads = Query::ads( 'post_title', 'asc', Util::any_post_status(), array(), array(), $ad_ids ); foreach ( $ad_ids as $ad_id ) { $impressions = TrackingLocal::total_impressions( $ad_id, $start_date_range[0]['timestamp'], $end_datetime->getTimestamp(), false ); $clicks = TrackingLocal::total_clicks( $ad_id, $start_date_range[0]['timestamp'], $end_datetime->getTimestamp(), false ); $this_ad = new \stdClass(); foreach ( $ads as $ad ) { if ( $ad->ID === $ad_id ) { $this_ad = $ad; break; } } $parsed_ads[] = array( 'ad_id' => $ad_id, /* translators: %d: Ad id */ 'ad' => isset( $this_ad->post_title ) ? $this_ad->post_title : sprintf( __( 'Ad not found (ID: %d)', 'ad-commander' ), $ad_id ), 'edit_link' => isset( $this_ad->post_title ) ? Admin::edit_ad_post_url( $this_ad->ID, $this_ad->post_status ) : null, 'impressions' => $impressions, 'clicks' => $clicks, 'ctr' => Util::ctr( $clicks, $impressions ), ); if ( isset( $this_ad->ID ) && $this_ad->ID ) { $groups = wp_get_post_terms( $this_ad->ID, AdCommander::tax_group(), array( 'fields' => 'all' ) ); if ( $groups && ! is_wp_error( $groups ) && ! empty( $groups ) ) { $ad_groups[ $ad_id ] = $groups; } $campaigns = wp_get_post_terms( $this_ad->ID, AdCommander::tax_campaign(), array( 'fields' => 'all' ) ); if ( $campaigns && ! is_wp_error( $campaigns ) && ! empty( $campaigns ) ) { $ad_campaigns[ $ad_id ] = $campaigns; } $advertisers = wp_get_post_terms( $this_ad->ID, AdCommander::tax_advertiser(), array( 'fields' => 'all' ) ); if ( $advertisers && ! is_wp_error( $advertisers ) && ! empty( $advertisers ) ) { $ad_advertisers[ $ad_id ] = $advertisers; } } } } if ( ! empty( $parsed_ads ) ) { usort( $parsed_ads, function ( $a, $b ) { if ( $a['edit_link'] === null ) { return PHP_INT_MAX; } return $a['ad'] <=> $b['ad']; } ); } $this->current_data_byad = $parsed_ads; $parsed_groups = array(); if ( ! empty( $ad_groups ) ) { foreach ( $ad_groups as $ad_id => $groups ) { foreach ( $groups as $group ) { if ( ! isset( $parsed_groups[ $group->term_id ] ) ) { $parsed_groups[ $group->term_id ] = array( 'group_id' => $group->term_id, 'group' => $group->name, 'impressions' => 0, 'clicks' => 0, 'ctr' => 0, 'edit_link' => Admin::admin_group_term_url( $group->term_id ), ); } foreach ( $parsed_ads as $ad ) { if ( $ad['ad_id'] === $ad_id ) { $parsed_groups[ $group->term_id ]['impressions'] += $ad['impressions']; $parsed_groups[ $group->term_id ]['clicks'] += $ad['clicks']; break; } } } } foreach ( $parsed_groups as $group_id => $group_data ) { $parsed_groups[ $group_id ]['ctr'] = Util::ctr( $group_data['clicks'], $group_data['impressions'] ); } } $this->current_data_bygroup = array_values( $parsed_groups ); $parsed_campaigns = array(); if ( ! empty( $ad_campaigns ) ) { foreach ( $ad_campaigns as $ad_id => $campaigns ) { foreach ( $campaigns as $campaign ) { if ( ! isset( $parsed_campaigns[ $campaign->term_id ] ) ) { $parsed_campaigns[ $campaign->term_id ] = array( 'campaign_id' => $campaign->term_id, 'campaign' => $campaign->name, 'impressions' => 0, 'clicks' => 0, 'ctr' => 0, 'edit_link' => Admin::admin_campaign_term_url( $campaign->term_id ), ); } foreach ( $parsed_ads as $ad ) { if ( $ad['ad_id'] === $ad_id ) { $parsed_campaigns[ $campaign->term_id ]['impressions'] += $ad['impressions']; $parsed_campaigns[ $campaign->term_id ]['clicks'] += $ad['clicks']; break; } } } } foreach ( $parsed_campaigns as $campaign_id => $campaign_data ) { $parsed_campaigns[ $campaign_id ]['ctr'] = Util::ctr( $campaign_data['clicks'], $campaign_data['impressions'] ); } } $this->current_data_bycampaign = array_values( $parsed_campaigns ); $parsed_advertisers = array(); if ( ! empty( $ad_advertisers ) ) { foreach ( $ad_advertisers as $ad_id => $advertisers ) { foreach ( $advertisers as $advertiser ) { if ( ! isset( $parsed_advertisers[ $advertiser->term_id ] ) ) { $parsed_advertisers[ $advertiser->term_id ] = array( 'advertiser_id' => $advertiser->term_id, 'advertiser' => $advertiser->name, 'impressions' => 0, 'clicks' => 0, 'ctr' => 0, 'edit_link' => Admin::admin_advertiser_term_url( $advertiser->term_id ), ); } foreach ( $parsed_ads as $ad ) { if ( $ad['ad_id'] === $ad_id ) { $parsed_advertisers[ $advertiser->term_id ]['impressions'] += $ad['impressions']; $parsed_advertisers[ $advertiser->term_id ]['clicks'] += $ad['clicks']; break; } } } } foreach ( $parsed_advertisers as $advertiser_id => $advertiser_data ) { $parsed_advertisers[ $advertiser_id ]['ctr'] = Util::ctr( $advertiser_data['clicks'], $advertiser_data['impressions'] ); } } $this->current_data_byadvertiser = array_values( $parsed_advertisers ); } /** * Build report data from provided input and return the results. * Intended for programmatic use (e.g., abilities API). * * @param array $input Associative array with keys: period, group_by, start_date, end_date, filter_by_ad_ids. * * @return array|WP_Error */ public function build_report( $input = array() ) { // Convert Y-m-d dates to the F j, Y format expected by sanitize_all_input. foreach ( array( 'start_date', 'end_date' ) as $date_key ) { if ( isset( $input[ $date_key ] ) ) { $date = \DateTime::createFromFormat( 'Y-m-d', $input[ $date_key ] ); if ( $date && $date->format( 'Y-m-d' ) === $input[ $date_key ] ) { $input[ $date_key ] = $date->format( 'F j, Y' ); } } } $this->input = $this->sanitize_all_input( $input ); $this->build_report_data( true ); $cast_ctr = function ( $item ) { $item['ctr'] = (float) $item['ctr']; return $item; }; return array( 'by_date' => array_map( $cast_ctr, $this->current_data_bydate ), 'by_ad' => array_map( $cast_ctr, $this->current_data_byad ), 'by_group' => array_map( $cast_ctr, $this->current_data_bygroup ), 'by_campaign' => array_map( $cast_ctr, $this->current_data_bycampaign ), 'by_advertiser' => array_map( $cast_ctr, $this->current_data_byadvertiser ), ); } }