Archived
472 lines
13 KiB
PHP
472 lines
13 KiB
PHP
<?php
|
|
/**
|
|
* @package GoogleAnalytics\Admin
|
|
*/
|
|
|
|
/**
|
|
* Dashboards collector.
|
|
*/
|
|
class Yoast_GA_Dashboards_Collector {
|
|
|
|
/**
|
|
* @var boolean $api API storage
|
|
*/
|
|
public $api;
|
|
|
|
/**
|
|
* @var array $active_metrics Store the active metrics
|
|
*/
|
|
public $active_metrics;
|
|
|
|
/**
|
|
* Store the dimensions
|
|
*
|
|
* @var array
|
|
*/
|
|
private $dimensions = array();
|
|
|
|
/**
|
|
* Store the valid metrics, which should be
|
|
*
|
|
* @var array
|
|
*/
|
|
private $valid_metrics = array();
|
|
|
|
/**
|
|
* @var integer $ga_profile_id Store the GA Profile ID
|
|
*/
|
|
public $ga_profile_id;
|
|
|
|
/**
|
|
* Construct on the dashboards class for GA
|
|
*
|
|
* @param int $ga_profile_id
|
|
* @param array $active_metrics
|
|
* @param array $valid_metrics
|
|
*/
|
|
public function __construct( $ga_profile_id, $active_metrics, $valid_metrics ) {
|
|
$this->ga_profile_id = $ga_profile_id;
|
|
|
|
$active_metrics = $this->filter_metrics_to_dimensions( $active_metrics );
|
|
$this->active_metrics = $active_metrics;
|
|
|
|
add_filter( 'ga_dashboards_dimensions', array( $this, 'filter_dimensions' ), 10, 1 );
|
|
|
|
$this->options = Yoast_GA_Dashboards_Api_Options::get_instance();
|
|
|
|
$this->init_shutdown_hook();
|
|
}
|
|
|
|
/**
|
|
* Fetch the data from Google Analytics and store it
|
|
*/
|
|
public function aggregate_data() {
|
|
if ( is_numeric( $this->ga_profile_id ) ) {
|
|
// ProfileID is set
|
|
|
|
/**
|
|
* Implement the metric data first
|
|
*/
|
|
if ( is_array( $this->active_metrics ) && count( $this->active_metrics ) >= 1 ) {
|
|
$this->aggregate_metrics( $this->active_metrics );
|
|
}
|
|
|
|
/**
|
|
* Now implement the dimensions that are set
|
|
*/
|
|
if ( is_array( $this->dimensions ) && count( $this->dimensions ) >= 1 ) {
|
|
$this->aggregate_dimensions( $this->dimensions );
|
|
}
|
|
}
|
|
else {
|
|
// Failure on authenticating, please reauthenticate
|
|
}
|
|
}
|
|
|
|
/**
|
|
* This hook runs on the shutdown to fetch data from GA
|
|
*/
|
|
private function init_shutdown_hook() {
|
|
$this->api = Yoast_Api_Libs::load_api_libraries( array( 'oauth', 'googleanalytics' ) );
|
|
|
|
// Hook the WP cron event
|
|
add_action( 'wp', array( $this, 'setup_wp_cron_aggregate' ) );
|
|
|
|
// Hook our function to the WP cron event the fetch data daily
|
|
add_action( 'yst_ga_aggregate_data', array( $this, 'aggregate_data' ) );
|
|
|
|
// Check if the WP cron did run on time
|
|
if ( isset( $_GET['page'] ) && ( $_GET['page'] === 'yst_ga_dashboard' || $_GET['page'] === 'yst_ga_settings' ) ) {
|
|
add_action( 'shutdown', array( $this, 'check_api_call_hook' ) );
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Check if we scheduled the WP cron event, if not, do so.
|
|
*/
|
|
public function setup_wp_cron_aggregate() {
|
|
if ( ! wp_next_scheduled( 'yst_ga_aggregate_data' ) ) {
|
|
// Set the next event of fetching data
|
|
wp_schedule_event( strtotime( date( 'Y-m-d', strtotime( 'tomorrow' ) ) . ' 00:05:00 ' ), 'daily', 'yst_ga_aggregate_data' );
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Check if the WP cron did run yesterday. If not, we need to run it form here
|
|
*/
|
|
public function check_api_call_hook() {
|
|
$last_run = $this->get_last_aggregate_run();
|
|
|
|
|
|
/**
|
|
* Transient doesn't exists, so we need to run the
|
|
* hook (This function runs already on Shutdown so
|
|
* we can call it directly from now on) or the last run has ben more than 24 hours
|
|
*/
|
|
if ( $last_run === false || Yoast_GA_Utils::hours_between( strtotime( $last_run ), time() ) >= 24 ) {
|
|
$this->aggregate_data();
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
* Get the datetime when the aggregate data function was succesful
|
|
*
|
|
* @return mixed
|
|
*/
|
|
private function get_last_aggregate_run() {
|
|
return get_option( 'yst_ga_last_wp_run' );
|
|
}
|
|
|
|
/**
|
|
* Remove metrics and set them as a dimension if needed
|
|
*
|
|
* @param array $metrics
|
|
*
|
|
* @return mixed
|
|
*/
|
|
private function filter_metrics_to_dimensions( $metrics ) {
|
|
$filter_metrics = $this->get_filter_metrics();
|
|
|
|
foreach ( $metrics as $key => $metric_name ) {
|
|
if ( isset( $filter_metrics[ $metric_name ] ) ) {
|
|
// Add and set the dimension
|
|
$dimension = array( $filter_metrics[ $metric_name ] );
|
|
$this->dimensions = array_merge( $this->dimensions, $dimension );
|
|
|
|
// Remove it from the metrics after we've added it into dimensions
|
|
unset( $metrics[ $key ] );
|
|
}
|
|
}
|
|
|
|
return $metrics;
|
|
}
|
|
|
|
/**
|
|
* Get array with metrics which we need to filter as a dimension
|
|
*
|
|
* @return array
|
|
*/
|
|
private function get_filter_metrics() {
|
|
return array(
|
|
'source' => array(
|
|
'metric' => 'sessions',
|
|
'dimension' => 'source',
|
|
'storage_name' => 'source',
|
|
),
|
|
'top_pageviews' => array(
|
|
'metric' => 'pageViews',
|
|
'dimension' => 'pagePath',
|
|
'storage_name' => 'top_pageviews',
|
|
),
|
|
'top_countries' => array(
|
|
'metric' => 'sessions',
|
|
'dimension' => 'country',
|
|
'storage_name' => 'top_countries',
|
|
),
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Filter function for adding dimensions
|
|
*
|
|
* @filter ga_dashboards_dimensions
|
|
*
|
|
* @param array $dimensions
|
|
*
|
|
* @return array
|
|
*/
|
|
public function filter_dimensions( $dimensions = array() ) {
|
|
if ( is_array( $dimensions ) && count( $dimensions ) >= 1 ) {
|
|
$dimensions = array_merge( $this->dimensions, $dimensions );
|
|
$this->dimensions = $dimensions;
|
|
}
|
|
|
|
return $this->dimensions;
|
|
}
|
|
|
|
/**
|
|
* Get the start and and date for aggregation functionality
|
|
*
|
|
* @return array
|
|
*/
|
|
private function get_date_range() {
|
|
/**
|
|
* Filter: 'yst-ga-filter-api-end-date' - Allow people to change the end date for the dashboard
|
|
* data. Default: yesterday.
|
|
*
|
|
* @api string Date (Y-m-d)
|
|
*/
|
|
return array(
|
|
'start' => date( 'Y-m-d', strtotime( '-1 month' ) ),
|
|
'end' => apply_filters( 'yst-ga-filter-api-end-date', date( 'Y-m-d', strtotime( 'yesterday' ) ) ),
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Aggregate metrics from GA. This function should be called in the shutdown function.
|
|
*
|
|
* @param array $metrics
|
|
*/
|
|
private function aggregate_metrics( $metrics ) {
|
|
$dates = $this->get_date_range();
|
|
|
|
foreach ( $metrics as $metric ) {
|
|
$this->execute_call( $metric, $dates['start'], $dates['end'] );
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Aggregate dimensions from GA. This function should be called in the shutdown function.
|
|
*
|
|
* @param array $dimensions
|
|
*/
|
|
private function aggregate_dimensions( $dimensions ) {
|
|
$dates = $this->get_date_range();
|
|
|
|
foreach ( $dimensions as $dimension ) {
|
|
if ( isset( $dimension['metric'] ) ) {
|
|
if ( isset( $dimension['id'] ) ) {
|
|
$this->execute_call( $dimension['metric'], $dates['start'], $dates['end'], 'ga:dimension' . $dimension['id'] );
|
|
}
|
|
elseif ( isset( $dimension['dimension'] ) ) {
|
|
if ( isset( $dimension['storage_name'] ) ) {
|
|
$this->execute_call( $dimension['metric'], $dates['start'], $dates['end'], 'ga:' . $dimension['dimension'], $dimension['storage_name'] );
|
|
}
|
|
else {
|
|
$this->execute_call( $dimension['metric'], $dates['start'], $dates['end'], 'ga:' . $dimension['dimension'] );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Execute an API call to Google Analytics and store the data in the dashboards data class
|
|
*
|
|
* @param string $metric
|
|
* @param string $start_date 2014-10-16
|
|
* @param string $end_date 2014-11-20
|
|
* @param string $dimensions ga:date
|
|
* @param string $storage_name auto
|
|
*
|
|
* @return bool
|
|
*/
|
|
private function execute_call( $metric, $start_date, $end_date, $dimensions = 'ga:date', $storage_name = 'auto' ) {
|
|
$dimensions = $this->prepare_dimensions( $dimensions, $metric );
|
|
$params = $this->build_params_for_call( $start_date, $end_date, $dimensions, $metric );
|
|
$storage_type = $this->get_storage_type( $dimensions );
|
|
|
|
$response = Yoast_Google_Analytics::get_instance()->do_request( 'https://www.googleapis.com/analytics/v3/data/ga?' . $params );
|
|
|
|
if ( isset( $response['response']['code'] ) && $response['response']['code'] == 200 ) {
|
|
|
|
// Delete option api_fail because there it's successful now
|
|
delete_option( 'yst_ga_api_call_fail' );
|
|
|
|
// Success, set a transient which stores the latest runtime
|
|
update_option( 'yst_ga_last_wp_run', date( 'Y-m-d' ) );
|
|
|
|
$response = Yoast_Googleanalytics_Reporting::get_instance()->parse_response( $response, $storage_type, $start_date, $end_date );
|
|
}
|
|
else {
|
|
// When response is failing, we should count the number of
|
|
$this->save_api_failure();
|
|
|
|
return false;
|
|
}
|
|
|
|
if ( strpos( 'ga:date', $dimensions ) !== false ) {
|
|
return $this->handle_response( $response, $metric, $dimensions, $start_date, $end_date, 'datelist', $storage_name );
|
|
}
|
|
else {
|
|
return $this->handle_response( $response, $metric, $dimensions, $start_date, $end_date, 'table', $storage_name );
|
|
}
|
|
}
|
|
|
|
/**
|
|
* When the API isn't able to get a successful response (code 200), we have to save that the call has failed
|
|
*/
|
|
private function save_api_failure() {
|
|
update_option( 'yst_ga_api_call_fail', true );
|
|
}
|
|
|
|
/**
|
|
* Get the storage type from dimensions
|
|
*
|
|
* @param string $dimensions
|
|
*
|
|
* @return string
|
|
*/
|
|
private function get_storage_type( $dimensions ) {
|
|
if ( strpos( 'ga:date', $dimensions ) !== false ) {
|
|
return 'datelist';
|
|
}
|
|
else {
|
|
return 'table';
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Prepare dimensions before adding them as a parameter in a call
|
|
*
|
|
* @param array $dimensions
|
|
* @param array $metric
|
|
*
|
|
* @return array
|
|
*/
|
|
private function prepare_dimensions( $dimensions, $metric ) {
|
|
$filter_metrics = $this->get_filter_metrics();
|
|
|
|
// Check if the dimensions param is an array, if so, glue it with implode to a comma separated string.
|
|
if ( is_array( $dimensions ) ) {
|
|
$dimensions = implode( ',', $dimensions );
|
|
}
|
|
|
|
if ( in_array( $metric, $this->valid_metrics ) ) {
|
|
$dimensions = 'ga:date,' . $dimensions;
|
|
}
|
|
elseif ( isset( $filter_metrics[ str_replace( 'ga:', '', $dimensions ) ] ) ) {
|
|
// Make sure we don't have a ga:date property here
|
|
$dimensions = str_replace( 'ga:date', '', $dimensions );
|
|
}
|
|
|
|
return $dimensions;
|
|
}
|
|
|
|
/**
|
|
* Build the params for a call to Google Analytics, return them prepared for a http query
|
|
*
|
|
* @param string $start_date
|
|
* @param string $end_date
|
|
* @param string $dimensions
|
|
* @param string $metric
|
|
*
|
|
* @return string
|
|
*/
|
|
private function build_params_for_call( $start_date, $end_date, $dimensions, $metric ) {
|
|
/**
|
|
* Filter: 'yst-ga-filter-api-limit' - Allow people to change the max results value in the API
|
|
* calls. Default value is 1000 results per call.
|
|
*
|
|
* @api int 1000
|
|
*/
|
|
$api_call_limit = apply_filters( 'yst-ga-filter-api-limit', 1000 );
|
|
|
|
$params = array(
|
|
'ids' => 'ga:' . $this->ga_profile_id,
|
|
'start-date' => $start_date,
|
|
'end-date' => $end_date,
|
|
'dimensions' => $dimensions,
|
|
'metrics' => 'ga:' . $metric,
|
|
'max-results' => $api_call_limit,
|
|
);
|
|
|
|
$params = $this->add_sort_direction( $params, $dimensions, $metric );
|
|
$params = http_build_query( $params );
|
|
|
|
return $params;
|
|
}
|
|
|
|
/**
|
|
* Add a sort direction if we need to (Especially on dimensions which are
|
|
* listed in $this->get_filter_metrics())
|
|
*
|
|
* @param array $params
|
|
* @param string $dimensions
|
|
* @param string $metric
|
|
*
|
|
* @return array
|
|
*/
|
|
private function add_sort_direction( $params, $dimensions, $metric ) {
|
|
$filter_dimensions = $this->get_filter_metrics();
|
|
|
|
foreach ( $filter_dimensions as $dimension ) {
|
|
if ( str_replace( 'ga:', '', $dimensions ) == $dimension['dimension'] && str_replace( 'ga:', '', $metric ) == $dimension['metric'] ) {
|
|
$params['sort'] = '-ga:' . $dimension['metric'];
|
|
}
|
|
}
|
|
|
|
return $params;
|
|
}
|
|
|
|
/**
|
|
* Handle the response from the Google Analytics api.
|
|
*
|
|
* @param array|boolean $response
|
|
* @param string $metric
|
|
* @param array $dimensions
|
|
* @param string $start_date
|
|
* @param string $end_date
|
|
* @param string $store_as
|
|
* @param string $storage_name
|
|
*
|
|
* @return bool
|
|
*/
|
|
private function handle_response( $response, $metric, $dimensions, $start_date, $end_date, $store_as = 'table', $storage_name = 'auto' ) {
|
|
if ( is_array( $response ) ) {
|
|
// Success, store this data
|
|
$filter_metrics = $this->get_filter_metrics();
|
|
$extracted = str_replace( 'ga:', '', str_replace( 'ga:date,', '', $dimensions ) );
|
|
|
|
if ( isset( $filter_metrics[ $extracted ] ) ) {
|
|
$name = $extracted;
|
|
}
|
|
else {
|
|
$name = $metric;
|
|
}
|
|
|
|
if ( $dimensions !== 'ga:date' && ! isset( $filter_metrics[ $extracted ] ) ) {
|
|
$name = str_replace( 'ga:date,', '', $dimensions );
|
|
}
|
|
|
|
// Overwrite the name if we have a defined one
|
|
if ( $storage_name != 'auto' ) {
|
|
$name = $storage_name;
|
|
}
|
|
|
|
return Yoast_GA_Dashboards_Data::set( $name, $response, strtotime( $start_date ), strtotime( $end_date ), $store_as );
|
|
}
|
|
else {
|
|
// Failure on API call try to log it
|
|
$this->log_error( print_r( $response, true ) );
|
|
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Log an error while calling the Google Analytics API
|
|
*
|
|
* @param string $error
|
|
*/
|
|
private function log_error( $error ) {
|
|
if ( true == WP_DEBUG ) {
|
|
if ( function_exists( 'error_log' ) ) {
|
|
error_log( 'Google Analytics by Yoast (Dashboard API): ' . $error );
|
|
}
|
|
}
|
|
}
|
|
|
|
}
|