+ * @version 2.0
*
*/
-class gapi
-{
- const http_interface = 'auto'; //'auto': autodetect, 'curl' or 'fopen'
-
- const client_login_url = 'https://www.google.com/accounts/ClientLogin';
- const account_data_url = 'https://www.google.com/analytics/feeds/accounts/default';
- const report_data_url = 'https://www.google.com/analytics/feeds/data';
- const interface_name = 'GAPI-1.3';
+class gapi {
+ const account_data_url = 'https://www.googleapis.com/analytics/v3/management/accountSummaries';
+ const report_data_url = 'https://www.googleapis.com/analytics/v3/data/ga';
+ const interface_name = 'GAPI-2.0';
const dev_mode = false;
-
- private $auth_token = null;
+
+ private $auth_method = null;
private $account_entries = array();
- private $account_root_parameters = array();
private $report_aggregate_metrics = array();
private $report_root_parameters = array();
private $results = array();
-
+
/**
- * Constructor function for all new gapi instances
- *
- * Set up authenticate with Google and get auth_token
+ * Constructor function for new gapi instances
*
- * @param String $email
- * @param String $password
- * @param String $token
+ * @param string $client_email Email of OAuth2 service account (XXXXX@developer.gserviceaccount.com)
+ * @param string $key_file Location/filename of .p12 key file
+ * @param string $delegate_email Optional email of account to impersonate
* @return gapi
*/
- public function __construct($email, $password, $token=null)
- {
- if($token !== null)
- {
- $this->auth_token = $token;
- }
- else
- {
- $this->authenticateUser($email,$password);
+ public function __construct($client_email, $key_file, $delegate_email = null) {
+ if (version_compare(PHP_VERSION, '5.3.0') < 0) {
+ throw new Exception('GAPI: PHP version ' . PHP_VERSION . ' is below minimum required 5.3.0.');
}
+ $this->auth_method = new gapiOAuth2();
+ $this->auth_method->fetchToken($client_email, $key_file, $delegate_email);
}
-
+
/**
- * Return the auth token, used for storing the auth token in the user session
+ * Return the auth token string retrieved by Google
*
* @return String
*/
- public function getAuthToken()
- {
- return $this->auth_token;
+ public function getToken() {
+ return $this->auth_method->getToken();
}
-
+
+ /**
+ * Return the auth token information from the Google service
+ *
+ * @return Array
+ */
+ public function getTokenInfo() {
+ return $this->auth_method->getTokenInfo();
+ }
+
+ /**
+ * Revoke the current auth token, rendering it invalid for future requests
+ *
+ * @return Boolean
+ */
+ public function revokeToken() {
+ return $this->auth_method->revokeToken();
+ }
+
/**
* Request account data from Google Analytics
*
* @param Int $start_index OPTIONAL: Start index of results
* @param Int $max_results OPTIONAL: Max results returned
*/
- public function requestAccountData($start_index=1, $max_results=20)
- {
- $response = $this->httpRequest(gapi::account_data_url, array('start-index'=>$start_index,'max-results'=>$max_results), null, $this->generateAuthHeader());
-
- if(substr($response['code'],0,1) == '2')
- {
+ public function requestAccountData($start_index=1, $max_results=1000) {
+ $get_variables = array(
+ 'start-index' => $start_index,
+ 'max-results' => $max_results,
+ );
+ $url = new gapiRequest(gapi::account_data_url);
+ $response = $url->get($get_variables, $this->auth_method->generateAuthHeader());
+
+ if (substr($response['code'], 0, 1) == '2') {
return $this->accountObjectMapper($response['body']);
- }
- else
- {
+ } else {
throw new Exception('GAPI: Failed to request account data. Error: "' . strip_tags($response['body']) . '"');
}
}
-
+
/**
* Request report data from Google Analytics
*
@@ -110,111 +119,110 @@ public function requestAccountData($start_index=1, $max_results=20)
* @param Int $start_index OPTIONAL: Start index of results
* @param Int $max_results OPTIONAL: Max results returned
*/
- public function requestReportData($report_id, $dimensions, $metrics, $sort_metric=null, $filter=null, $start_date=null, $end_date=null, $start_index=1, $max_results=30)
- {
+ public function requestReportData($report_id, $dimensions=null, $metrics, $sort_metric=null, $filter=null, $start_date=null, $end_date=null, $start_index=1, $max_results=10000) {
$parameters = array('ids'=>'ga:' . $report_id);
-
- if(is_array($dimensions))
- {
+
+ if (is_array($dimensions)) {
$dimensions_string = '';
- foreach($dimensions as $dimesion)
- {
+ foreach ($dimensions as $dimesion) {
$dimensions_string .= ',ga:' . $dimesion;
}
- $parameters['dimensions'] = substr($dimensions_string,1);
- }
- else
- {
+ $parameters['dimensions'] = substr($dimensions_string, 1);
+ } elseif ($dimensions !== null) {
$parameters['dimensions'] = 'ga:'.$dimensions;
}
- if(is_array($metrics))
- {
+ if (is_array($metrics)) {
$metrics_string = '';
- foreach($metrics as $metric)
- {
+ foreach ($metrics as $metric) {
$metrics_string .= ',ga:' . $metric;
}
- $parameters['metrics'] = substr($metrics_string,1);
- }
- else
- {
+ $parameters['metrics'] = substr($metrics_string, 1);
+ } else {
$parameters['metrics'] = 'ga:'.$metrics;
}
-
- if($sort_metric==null&&isset($parameters['metrics']))
- {
+
+ if ($sort_metric==null&&isset($parameters['metrics'])) {
$parameters['sort'] = $parameters['metrics'];
- }
- elseif(is_array($sort_metric))
- {
+ } elseif (is_array($sort_metric)) {
$sort_metric_string = '';
-
- foreach($sort_metric as $sort_metric_value)
- {
+
+ foreach ($sort_metric as $sort_metric_value) {
//Reverse sort - Thanks Nick Sullivan
- if (substr($sort_metric_value, 0, 1) == "-")
- {
+ if (substr($sort_metric_value, 0, 1) == "-") {
$sort_metric_string .= ',-ga:' . substr($sort_metric_value, 1); // Descending
}
- else
- {
+ else {
$sort_metric_string .= ',ga:' . $sort_metric_value; // Ascending
}
}
-
+
$parameters['sort'] = substr($sort_metric_string, 1);
- }
- else
- {
- if (substr($sort_metric, 0, 1) == "-")
- {
+ } else {
+ if (substr($sort_metric, 0, 1) == "-") {
$parameters['sort'] = '-ga:' . substr($sort_metric, 1);
- }
- else
- {
+ } else {
$parameters['sort'] = 'ga:' . $sort_metric;
}
}
-
- if($filter!=null)
- {
+
+ if ($filter!=null) {
$filter = $this->processFilter($filter);
- if($filter!==false)
- {
+ if ($filter!==false) {
$parameters['filters'] = $filter;
}
}
-
- if($start_date==null)
- {
- $start_date=date('Y-m-d',strtotime('1 month ago'));
+
+ if ($start_date==null) {
+ // Use the day that Google Analytics was released (1 Jan 2005).
+ $start_date = '2005-01-01';
+ } elseif (is_int($start_date)) {
+ // Perhaps we are receiving a Unix timestamp.
+ $start_date = date('Y-m-d', $start_date);
}
-
+
$parameters['start-date'] = $start_date;
- if($end_date==null)
- {
- $end_date=date('Y-m-d');
+
+ if ($end_date==null) {
+ $end_date = date('Y-m-d');
+ } elseif (is_int($end_date)) {
+ // Perhaps we are receiving a Unix timestamp.
+ $end_date = date('Y-m-d', $end_date);
}
-
+
$parameters['end-date'] = $end_date;
-
-
+
+
$parameters['start-index'] = $start_index;
$parameters['max-results'] = $max_results;
-
+
$parameters['prettyprint'] = gapi::dev_mode ? 'true' : 'false';
- $response = $this->httpRequest(gapi::report_data_url, $parameters, null, $this->generateAuthHeader());
-
+ $url = new gapiRequest(gapi::report_data_url);
+ $response = $url->get($parameters, $this->auth_method->generateAuthHeader());
+
//HTTP 2xx
- if(substr($response['code'],0,1) == '2')
- {
+ if (substr($response['code'], 0, 1) == '2') {
return $this->reportObjectMapper($response['body']);
+ } else {
+ throw new Exception('GAPI: Failed to request report data. Error: "' . $this->cleanErrorResponse($response['body']) . '"');
+ }
+ }
+
+ /**
+ * Clean error message from Google API
+ *
+ * @param String $error Error message HTML or JSON from Google API
+ */
+ private function cleanErrorResponse($error) {
+ if (strpos($error, ']*>[^<]*<\/(style|title|script)>/i', '', $error);
+ return trim(preg_replace('/\s+/', ' ', strip_tags($error)));
}
- else
+ else
{
- throw new Exception('GAPI: Failed to request report data. Error: "' . strip_tags($response['body']) . '"');
+ $json = json_decode($error);
+ return isset($json->error->message) ? strval($json->error->message) : $error;
}
}
@@ -225,341 +233,180 @@ public function requestReportData($report_id, $dimensions, $metrics, $sort_metri
* @param String $filter
* @return String Compatible filter string
*/
- protected function processFilter($filter)
- {
+ protected function processFilter($filter) {
$valid_operators = '(!~|=~|==|!=|>|<|>=|<=|=@|!@)';
-
- $filter = preg_replace('/\s\s+/',' ',trim($filter)); //Clean duplicate whitespace
- $filter = str_replace(array(',',';'),array('\,','\;'),$filter); //Escape Google Analytics reserved characters
- $filter = preg_replace('/(&&\s*|\|\|\s*|^)([a-z]+)(\s*' . $valid_operators . ')/i','$1ga:$2$3',$filter); //Prefix ga: to metrics and dimensions
- $filter = preg_replace('/[\'\"]/i','',$filter); //Clear invalid quote characters
- $filter = preg_replace(array('/\s*&&\s*/','/\s*\|\|\s*/','/\s*' . $valid_operators . '\s*/'),array(';',',','$1'),$filter); //Clean up operators
-
- if(strlen($filter)>0)
- {
+
+ $filter = preg_replace('/\s\s+/', ' ', trim($filter)); //Clean duplicate whitespace
+ $filter = str_replace(array(',', ';'), array('\,', '\;'), $filter); //Escape Google Analytics reserved characters
+ $filter = preg_replace('/(&&\s*|\|\|\s*|^)([a-z0-9]+)(\s*' . $valid_operators . ')/i','$1ga:$2$3',$filter); //Prefix ga: to metrics and dimensions
+ $filter = preg_replace('/[\'\"]/i', '', $filter); //Clear invalid quote characters
+ $filter = preg_replace(array('/\s*&&\s*/','/\s*\|\|\s*/','/\s*' . $valid_operators . '\s*/'), array(';', ',', '$1'), $filter); //Clean up operators
+
+ if (strlen($filter) > 0) {
return urlencode($filter);
}
- else
- {
+ else {
return false;
}
}
-
+
/**
- * Report Account Mapper to convert the XML to array of useful PHP objects
+ * Report Account Mapper to convert the JSON to array of useful PHP objects
*
- * @param String $xml_string
+ * @param String $json_string
* @return Array of gapiAccountEntry objects
*/
- protected function accountObjectMapper($xml_string)
- {
- $xml = simplexml_load_string($xml_string);
-
- $this->results = null;
-
+ protected function accountObjectMapper($json_string) {
+ $json = json_decode($json_string, true);
$results = array();
- $account_root_parameters = array();
-
- //Load root parameters
-
- $account_root_parameters['updated'] = strval($xml->updated);
- $account_root_parameters['generator'] = strval($xml->generator);
- $account_root_parameters['generatorVersion'] = strval($xml->generator->attributes());
-
- $open_search_results = $xml->children('http://a9.com/-/spec/opensearchrss/1.0/');
-
- foreach($open_search_results as $key => $open_search_result)
- {
- $report_root_parameters[$key] = intval($open_search_result);
- }
-
- $account_root_parameters['startDate'] = strval($google_results->startDate);
- $account_root_parameters['endDate'] = strval($google_results->endDate);
-
- //Load result entries
-
- foreach($xml->entry as $entry)
- {
- $properties = array();
- foreach($entry->children('http://schemas.google.com/analytics/2009')->property as $property)
- {
- $properties[str_replace('ga:','',$property->attributes()->name)] = strval($property->attributes()->value);
+
+ foreach ($json['items'] as $item) {
+ foreach ($item['webProperties'] as $property) {
+ if (isset($property['profiles'][0]['id'])) {
+ $property['ProfileId'] = $property['profiles'][0]['id'];
+ }
+ $results[] = new gapiAccountEntry($property);
}
-
- $properties['title'] = strval($entry->title);
- $properties['updated'] = strval($entry->updated);
-
- $results[] = new gapiAccountEntry($properties);
}
-
- $this->account_root_parameters = $account_root_parameters;
- $this->results = $results;
-
+
+ $this->account_entries = $results;
+
return $results;
}
-
-
+
/**
- * Report Object Mapper to convert the XML to array of useful PHP objects
+ * Report Object Mapper to convert the JSON to array of useful PHP objects
*
- * @param String $xml_string
+ * @param String $json_string
* @return Array of gapiReportEntry objects
*/
- protected function reportObjectMapper($xml_string)
- {
- $xml = simplexml_load_string($xml_string);
-
+ protected function reportObjectMapper($json_string) {
+ $json = json_decode($json_string, true);
+
$this->results = null;
$results = array();
-
- $report_root_parameters = array();
+
$report_aggregate_metrics = array();
-
+
//Load root parameters
-
- $report_root_parameters['updated'] = strval($xml->updated);
- $report_root_parameters['generator'] = strval($xml->generator);
- $report_root_parameters['generatorVersion'] = strval($xml->generator->attributes());
-
- $open_search_results = $xml->children('http://a9.com/-/spec/opensearchrss/1.0/');
-
- foreach($open_search_results as $key => $open_search_result)
- {
- $report_root_parameters[$key] = intval($open_search_result);
- }
-
- $google_results = $xml->children('http://schemas.google.com/analytics/2009');
- foreach($google_results->dataSource->property as $property_attributes)
- {
- $report_root_parameters[str_replace('ga:','',$property_attributes->attributes()->name)] = strval($property_attributes->attributes()->value);
+ // Start with elements from the root level of the JSON that aren't themselves arrays.
+ $report_root_parameters = array_filter($json, function($var){
+ return !is_array($var);
+ });
+
+ // Get the items from the 'query' object, and rename them slightly.
+ foreach($json['query'] as $index => $value) {
+ $new_index = lcfirst(str_replace(' ', '', ucwords(str_replace('-', ' ', $index))));
+ $report_root_parameters[$new_index] = $value;
}
-
- $report_root_parameters['startDate'] = strval($google_results->startDate);
- $report_root_parameters['endDate'] = strval($google_results->endDate);
-
+
+ // Now merge in the profileInfo, as this is also mostly useful.
+ array_merge($report_root_parameters, $json['profileInfo']);
+
//Load result aggregate metrics
-
- foreach($google_results->aggregates->metric as $aggregate_metric)
- {
- $metric_value = strval($aggregate_metric->attributes()->value);
-
+
+ foreach($json['totalsForAllResults'] as $index => $metric_value) {
//Check for float, or value with scientific notation
- if(preg_match('/^(\d+\.\d+)|(\d+E\d+)|(\d+.\d+E\d+)$/',$metric_value))
- {
- $report_aggregate_metrics[str_replace('ga:','',$aggregate_metric->attributes()->name)] = floatval($metric_value);
- }
- else
- {
- $report_aggregate_metrics[str_replace('ga:','',$aggregate_metric->attributes()->name)] = intval($metric_value);
+ if (preg_match('/^(\d+\.\d+)|(\d+E\d+)|(\d+.\d+E\d+)$/', $metric_value)) {
+ $report_aggregate_metrics[str_replace('ga:', '', $index)] = floatval($metric_value);
+ } else {
+ $report_aggregate_metrics[str_replace('ga:', '', $index)] = intval($metric_value);
}
}
-
+
//Load result entries
-
- foreach($xml->entry as $entry)
- {
- $metrics = array();
- foreach($entry->children('http://schemas.google.com/analytics/2009')->metric as $metric)
- {
- $metric_value = strval($metric->attributes()->value);
-
- //Check for float, or value with scientific notation
- if(preg_match('/^(\d+\.\d+)|(\d+E\d+)|(\d+.\d+E\d+)$/',$metric_value))
- {
- $metrics[str_replace('ga:','',$metric->attributes()->name)] = floatval($metric_value);
- }
- else
- {
- $metrics[str_replace('ga:','',$metric->attributes()->name)] = intval($metric_value);
+ if(isset($json['rows'])){
+ foreach($json['rows'] as $row) {
+ $metrics = array();
+ $dimensions = array();
+ foreach($json['columnHeaders'] as $index => $header) {
+ switch($header['columnType']) {
+ case 'METRIC':
+ $metric_value = $row[$index];
+
+ //Check for float, or value with scientific notation
+ if(preg_match('/^(\d+\.\d+)|(\d+E\d+)|(\d+.\d+E\d+)$/',$metric_value)) {
+ $metrics[str_replace('ga:', '', $header['name'])] = floatval($metric_value);
+ } else {
+ $metrics[str_replace('ga:', '', $header['name'])] = intval($metric_value);
+ }
+ break;
+ case 'DIMENSION':
+ $dimensions[str_replace('ga:', '', $header['name'])] = strval($row[$index]);
+ break;
+ default:
+ throw new Exception("GAPI: Unrecognized columnType '{$header['columnType']}' for columnHeader '{$header['name']}'");
+ }
}
+ $results[] = new gapiReportEntry($metrics, $dimensions);
}
-
- $dimensions = array();
- foreach($entry->children('http://schemas.google.com/analytics/2009')->dimension as $dimension)
- {
- $dimensions[str_replace('ga:','',$dimension->attributes()->name)] = strval($dimension->attributes()->value);
- }
-
- $results[] = new gapiReportEntry($metrics,$dimensions);
}
-
+
$this->report_root_parameters = $report_root_parameters;
$this->report_aggregate_metrics = $report_aggregate_metrics;
$this->results = $results;
-
+
return $results;
}
-
+
/**
- * Authenticate Google Account with Google
+ * Get current analytics results
*
- * @param String $email
- * @param String $password
+ * @return Array
*/
- protected function authenticateUser($email, $password)
- {
- $post_variables = array(
- 'accountType' => 'GOOGLE',
- 'Email' => $email,
- 'Passwd' => $password,
- 'source' => gapi::interface_name,
- 'service' => 'analytics'
- );
-
- $response = $this->httpRequest(gapi::client_login_url,null,$post_variables);
-
- //Convert newline delimited variables into url format then import to array
- parse_str(str_replace(array("\n","\r\n"),'&',$response['body']),$auth_token);
-
- if(substr($response['code'],0,1) != '2' || !is_array($auth_token) || empty($auth_token['Auth']))
- {
- throw new Exception('GAPI: Failed to authenticate user. Error: "' . strip_tags($response['body']) . '"');
- }
-
- $this->auth_token = $auth_token['Auth'];
+ public function getResults() {
+ return is_array($this->results) ? $this->results : false;
}
-
+
/**
- * Generate authentication token header for all requests
+ * Get current account data
*
* @return Array
*/
- protected function generateAuthHeader()
- {
- return array('Authorization: GoogleLogin auth=' . $this->auth_token);
+ public function getAccounts() {
+ return is_array($this->account_entries) ? $this->account_entries : false;
}
-
+
/**
- * Perform http request
- *
+ * Get an array of the metrics and the matching
+ * aggregate values for the current result
*
- * @param Array $get_variables
- * @param Array $post_variables
- * @param Array $headers
- */
- protected function httpRequest($url, $get_variables=null, $post_variables=null, $headers=null)
- {
- $interface = gapi::http_interface;
-
- if(gapi::http_interface =='auto')
- {
- if(function_exists('curl_exec'))
- {
- $interface = 'curl';
- }
- else
- {
- $interface = 'fopen';
- }
- }
-
- if($interface == 'curl')
- {
- return $this->curlRequest($url, $get_variables, $post_variables, $headers);
- }
- elseif($interface == 'fopen')
- {
- return $this->fopenRequest($url, $get_variables, $post_variables, $headers);
- }
- else
- {
- throw new Exception('Invalid http interface defined. No such interface "' . gapi::http_interface . '"');
- }
- }
-
- /**
- * HTTP request using PHP CURL functions
- * Requires curl library installed and configured for PHP
- *
- * @param Array $get_variables
- * @param Array $post_variables
- * @param Array $headers
+ * @return Array
*/
- private function curlRequest($url, $get_variables=null, $post_variables=null, $headers=null)
- {
- $ch = curl_init();
-
- if(is_array($get_variables))
- {
- $get_variables = '?' . str_replace('&','&',urldecode(http_build_query($get_variables)));
- }
- else
- {
- $get_variables = null;
- }
-
- curl_setopt($ch, CURLOPT_URL, $url . $get_variables);
- curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
- curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); //CURL doesn't like google's cert
-
- if(is_array($post_variables))
- {
- curl_setopt($ch, CURLOPT_POST, true);
- curl_setopt($ch, CURLOPT_POSTFIELDS, $post_variables);
- }
-
- if(is_array($headers))
- {
- curl_setopt($ch, CURLOPT_HTTPHEADER,$headers);
- }
-
- $response = curl_exec($ch);
- $code = curl_getinfo($ch,CURLINFO_HTTP_CODE);
-
- curl_close($ch);
-
- return array('body'=>$response,'code'=>$code);
+ public function getMetrics() {
+ return $this->report_aggregate_metrics;
}
-
+
/**
- * HTTP request using native PHP fopen function
- * Requires PHP openSSL
+ * Call method to find a matching root parameter or
+ * aggregate metric to return
*
- * @param Array $get_variables
- * @param Array $post_variables
- * @param Array $headers
+ * @param $name String name of function called
+ * @return String
+ * @throws Exception if not a valid parameter or aggregate
+ * metric, or not a 'get' function
*/
- private function fopenRequest($url, $get_variables=null, $post_variables=null, $headers=null)
- {
- $http_options = array('method'=>'GET','timeout'=>3);
-
- if(is_array($headers))
- {
- $headers = implode("\r\n",$headers) . "\r\n";
- }
- else
- {
- $headers = '';
- }
-
- if(is_array($get_variables))
- {
- $get_variables = '?' . str_replace('&','&',urldecode(http_build_query($get_variables)));
- }
- else
- {
- $get_variables = null;
+ public function __call($name, $parameters) {
+ if (!preg_match('/^get/', $name)) {
+ throw new Exception('No such function "' . $name . '"');
}
-
- if(is_array($post_variables))
- {
- $post_variables = str_replace('&','&',urldecode(http_build_query($post_variables)));
- $http_options['method'] = 'POST';
- $headers = "Content-type: application/x-www-form-urlencoded\r\n" . "Content-Length: " . strlen($post_variables) . "\r\n" . $headers;
- $http_options['header'] = $headers;
- $http_options['content'] = $post_variables;
+
+ $name = preg_replace('/^get/', '', $name);
+
+ $parameter_key = gapi::ArrayKeyExists($name, $this->report_root_parameters);
+
+ if ($parameter_key) {
+ return $this->report_root_parameters[$parameter_key];
}
- else
- {
- $post_variables = '';
- $http_options['header'] = $headers;
+
+ $aggregate_metric_key = gapi::ArrayKeyExists($name, $this->report_aggregate_metrics);
+
+ if ($aggregate_metric_key) {
+ return $this->report_aggregate_metrics[$aggregate_metric_key];
}
-
- $context = stream_context_create(array('http'=>$http_options));
- $response = @file_get_contents($url . $get_variables, null, $context);
-
- return array('body'=>$response!==false?$response:'Request failed, fopen provides no further information','code'=>$response!==false?'200':'400');
+
+ throw new Exception('No valid root parameter or aggregate metric called "' . $name . '"');
}
/**
@@ -570,135 +417,60 @@ private function fopenRequest($url, $get_variables=null, $post_variables=null, $
* @param Array $search
* @return String Matching array key
*/
- public static function array_key_exists_nc($key, $search)
- {
- if (array_key_exists($key, $search))
- {
+ public static function ArrayKeyExists($key, $search) {
+ if (array_key_exists($key, $search)) {
return $key;
}
- if (!(is_string($key) && is_array($search)))
- {
+ if (!(is_string($key) && is_array($search))) {
return false;
}
$key = strtolower($key);
- foreach ($search as $k => $v)
- {
- if (strtolower($k) == $key)
- {
+ foreach ($search as $k => $v) {
+ if (strtolower($k) == $key) {
return $k;
}
}
return false;
}
-
- /**
- * Get Results
- *
- * @return Array
- */
- public function getResults()
- {
- if(is_array($this->results))
- {
- return $this->results;
- }
- else
- {
- return;
- }
- }
-
-
- /**
- * Get an array of the metrics and the matchning
- * aggregate values for the current result
- *
- * @return Array
- */
- public function getMetrics()
- {
- return $this->report_aggregate_metrics;
- }
-
- /**
- * Call method to find a matching root parameter or
- * aggregate metric to return
- *
- * @param $name String name of function called
- * @return String
- * @throws Exception if not a valid parameter or aggregate
- * metric, or not a 'get' function
- */
- public function __call($name,$parameters)
- {
- if(!preg_match('/^get/',$name))
- {
- throw new Exception('No such function "' . $name . '"');
- }
-
- $name = preg_replace('/^get/','',$name);
-
- $parameter_key = gapi::array_key_exists_nc($name,$this->report_root_parameters);
-
- if($parameter_key)
- {
- return $this->report_root_parameters[$parameter_key];
- }
-
- $aggregate_metric_key = gapi::array_key_exists_nc($name,$this->report_aggregate_metrics);
-
- if($aggregate_metric_key)
- {
- return $this->report_aggregate_metrics[$aggregate_metric_key];
- }
-
- throw new Exception('No valid root parameter or aggregate metric called "' . $name . '"');
- }
}
/**
- * Class gapiAccountEntry
- *
* Storage for individual gapi account entries
*
*/
-class gapiAccountEntry
-{
+class gapiAccountEntry {
private $properties = array();
-
- public function __construct($properties)
- {
+
+ /**
+ * Constructor function for all new gapiAccountEntry instances
+ *
+ * @param Array $properties
+ * @return gapiAccountEntry
+ */
+ public function __construct($properties) {
$this->properties = $properties;
}
-
+
/**
* toString function to return the name of the account
*
* @return String
*/
- public function __toString()
- {
- if(isset($this->properties['title']))
- {
- return $this->properties['title'];
- }
- else
- {
- return;
- }
+ public function __toString() {
+ return isset($this->properties['name']) ?
+ $this->properties['name']: '';
}
-
+
/**
* Get an associative array of the properties
* and the matching values for the current result
*
* @return Array
*/
- public function getProperties()
- {
+ public function getProperties() {
return $this->properties;
}
-
+
/**
* Call method to find a matching parameter to return
*
@@ -706,116 +478,376 @@ public function getProperties()
* @return String
* @throws Exception if not a valid parameter, or not a 'get' function
*/
- public function __call($name,$parameters)
- {
- if(!preg_match('/^get/',$name))
- {
+ public function __call($name, $parameters) {
+ if (!preg_match('/^get/', $name)) {
throw new Exception('No such function "' . $name . '"');
}
-
- $name = preg_replace('/^get/','',$name);
-
- $property_key = gapi::array_key_exists_nc($name,$this->properties);
-
- if($property_key)
- {
+
+ $name = preg_replace('/^get/', '', $name);
+
+ $property_key = gapi::ArrayKeyExists($name, $this->properties);
+
+ if ($property_key) {
return $this->properties[$property_key];
}
-
+
throw new Exception('No valid property called "' . $name . '"');
}
}
/**
- * Class gapiReportEntry
- *
* Storage for individual gapi report entries
*
*/
-class gapiReportEntry
-{
+class gapiReportEntry {
private $metrics = array();
private $dimensions = array();
-
- public function __construct($metrics,$dimesions)
- {
+
+ /**
+ * Constructor function for all new gapiReportEntry instances
+ *
+ * @param Array $metrics
+ * @param Array $dimensions
+ * @return gapiReportEntry
+ */
+ public function __construct($metrics, $dimensions) {
$this->metrics = $metrics;
- $this->dimensions = $dimesions;
+ $this->dimensions = $dimensions;
}
-
+
/**
* toString function to return the name of the result
- * this is a concatented string of the dimesions chosen
+ * this is a concatented string of the dimensions chosen
*
* For example:
* 'Firefox 3.0.10' from browser and browserVersion
*
* @return String
*/
- public function __toString()
- {
- if(is_array($this->dimensions))
- {
- return implode(' ',$this->dimensions);
- }
- else
- {
- return '';
- }
+ public function __toString() {
+ return is_array($this->dimensions) ?
+ implode(' ', $this->dimensions) : '';
}
-
+
/**
- * Get an associative array of the dimesions
+ * Get an associative array of the dimensions
* and the matching values for the current result
*
* @return Array
*/
- public function getDimesions()
- {
+ public function getDimensions() {
return $this->dimensions;
}
-
+
/**
* Get an array of the metrics and the matchning
* values for the current result
*
* @return Array
*/
- public function getMetrics()
- {
+ public function getMetrics() {
return $this->metrics;
}
-
+
/**
* Call method to find a matching metric or dimension to return
*
- * @param $name String name of function called
+ * @param String $name name of function called
+ * @param Array $parameters
* @return String
* @throws Exception if not a valid metric or dimensions, or not a 'get' function
*/
- public function __call($name,$parameters)
- {
- if(!preg_match('/^get/',$name))
- {
+ public function __call($name, $parameters) {
+ if (!preg_match('/^get/', $name)) {
throw new Exception('No such function "' . $name . '"');
}
-
- $name = preg_replace('/^get/','',$name);
-
- $metric_key = gapi::array_key_exists_nc($name,$this->metrics);
-
- if($metric_key)
- {
+
+ $name = preg_replace('/^get/', '', $name);
+
+ $metric_key = gapi::ArrayKeyExists($name, $this->metrics);
+
+ if ($metric_key) {
return $this->metrics[$metric_key];
}
-
- $dimension_key = gapi::array_key_exists_nc($name,$this->dimensions);
-
- if($dimension_key)
- {
+
+ $dimension_key = gapi::ArrayKeyExists($name, $this->dimensions);
+
+ if ($dimension_key) {
return $this->dimensions[$dimension_key];
}
throw new Exception('No valid metric or dimesion called "' . $name . '"');
}
+}
+
+/**
+ * OAuth2 Google API authentication
+ *
+ */
+class gapiOAuth2 {
+ const scope_url = 'https://www.googleapis.com/auth/analytics.readonly';
+ const request_url = 'https://www.googleapis.com/oauth2/v3/token';
+ const grant_type = 'urn:ietf:params:oauth:grant-type:jwt-bearer';
+ const header_alg = 'RS256';
+ const header_typ = 'JWT';
+
+ private function base64URLEncode($data) {
+ return rtrim(strtr(base64_encode($data), '+/', '-_'), '=');
+ }
+
+ private function base64URLDecode($data) {
+ return base64_decode(str_pad(strtr($data, '-_', '+/'), strlen($data) % 4, '=', STR_PAD_RIGHT));
+ }
+
+ /**
+ * Authenticate Google Account with OAuth2
+ *
+ * @param String $client_email
+ * @param String $key_file
+ * @param String $delegate_email
+ * @return String Authentication token
+ */
+ public function fetchToken($client_email, $key_file, $delegate_email = null) {
+ $header = array(
+ "alg" => self::header_alg,
+ "typ" => self::header_typ,
+ );
+
+ $claimset = array(
+ "iss" => $client_email,
+ "scope" => self::scope_url,
+ "aud" => self::request_url,
+ "exp" => time() + (60 * 60),
+ "iat" => time(),
+ );
+
+ if(!empty($delegate_email)) {
+ $claimset["sub"] = $delegate_email;
+ }
+
+ $data = $this->base64URLEncode(json_encode($header)) . '.' . $this->base64URLEncode(json_encode($claimset));
+
+ if (!file_exists($key_file)) {
+ if ( !file_exists(__DIR__ . DIRECTORY_SEPARATOR . $key_file) ) {
+ throw new Exception('GAPI: Failed load key file "' . $key_file . '". File could not be found.');
+ } else {
+ $key_file = __DIR__ . DIRECTORY_SEPARATOR . $key_file;
+ }
+ }
+
+ $key_data = file_get_contents($key_file);
+
+ if (empty($key_data)) {
+ throw new Exception('GAPI: Failed load key file "' . $key_file . '". File could not be opened or is empty.');
+ }
+
+ openssl_pkcs12_read($key_data, $certs, 'notasecret');
+
+ if (!isset($certs['pkey'])) {
+ throw new Exception('GAPI: Failed load key file "' . $key_file . '". Unable to load pkcs12 check if correct p12 format.');
+ }
+
+ openssl_sign($data, $signature, openssl_pkey_get_private($certs['pkey']), "sha256");
+
+ $post_variables = array(
+ 'grant_type' => self::grant_type,
+ 'assertion' => $data . '.' . $this->base64URLEncode($signature),
+ );
+
+ $url = new gapiRequest(self::request_url);
+ $response = $url->post(null, $post_variables);
+ $auth_token = json_decode($response['body'], true);
+
+ if (substr($response['code'], 0, 1) != '2' || !is_array($auth_token) || empty($auth_token['access_token'])) {
+ throw new Exception('GAPI: Failed to authenticate user. Error: "' . strip_tags($response['body']) . '"');
+ }
+
+ $this->auth_token = $auth_token['access_token'];
+
+ return $this->auth_token;
+ }
+
+ /**
+ * Return the auth token string retrieved from Google
+ *
+ * @return String
+ */
+ public function getToken() {
+ return $this->auth_token;
+ }
+
+ /**
+ * Generate authorization token header for all requests
+ *
+ * @param String $token
+ * @return Array
+ */
+ public function generateAuthHeader($token=null) {
+ if ($token == null)
+ $token = $this->auth_token;
+ return array('Authorization' => 'Bearer ' . $token);
+ }
+}
+
+/**
+ * Google Analytics API request
+ *
+ */
+class gapiRequest {
+ const http_interface = 'auto'; //'auto': autodetect, 'curl' or 'fopen'
+ const interface_name = gapi::interface_name;
+
+ private $url = null;
+
+ public function __construct($url) {
+ $this->url = $url;
+ }
+
+ /**
+ * Return the URL to be requested, optionally adding GET variables
+ *
+ * @param Array $get_variables
+ * @return String
+ */
+ public function getUrl($get_variables=null) {
+ if (is_array($get_variables)) {
+ $get_variables = '?' . str_replace('&', '&', urldecode(http_build_query($get_variables, '', '&')));
+ } else {
+ $get_variables = null;
+ }
+
+ return $this->url . $get_variables;
+ }
+
+ /**
+ * Perform http POST request
+ *
+ *
+ * @param Array $get_variables
+ * @param Array $post_variables
+ * @param Array $headers
+ */
+ public function post($get_variables=null, $post_variables=null, $headers=null) {
+ return $this->request($get_variables, $post_variables, $headers);
+ }
+
+ /**
+ * Perform http GET request
+ *
+ *
+ * @param Array $get_variables
+ * @param Array $headers
+ */
+ public function get($get_variables=null, $headers=null) {
+ return $this->request($get_variables, null, $headers);
+ }
+
+ /**
+ * Perform http request
+ *
+ *
+ * @param Array $get_variables
+ * @param Array $post_variables
+ * @param Array $headers
+ */
+ public function request($get_variables=null, $post_variables=null, $headers=null) {
+ $interface = self::http_interface;
+
+ if (self::http_interface == 'auto')
+ $interface = function_exists('curl_exec') ? 'curl' : 'fopen';
+
+ switch ($interface) {
+ case 'curl':
+ return $this->curlRequest($get_variables, $post_variables, $headers);
+ case 'fopen':
+ return $this->fopenRequest($get_variables, $post_variables, $headers);
+ default:
+ throw new Exception('Invalid http interface defined. No such interface "' . self::http_interface . '"');
+ }
+ }
+
+ /**
+ * HTTP request using PHP CURL functions
+ * Requires curl library installed and configured for PHP
+ *
+ * @param Array $get_variables
+ * @param Array $post_variables
+ * @param Array $headers
+ */
+ private function curlRequest($get_variables=null, $post_variables=null, $headers=null) {
+ $ch = curl_init();
+
+ if (is_array($get_variables)) {
+ $get_variables = '?' . str_replace('&', '&', urldecode(http_build_query($get_variables, '', '&')));
+ } else {
+ $get_variables = null;
+ }
+
+ curl_setopt($ch, CURLOPT_URL, $this->url . $get_variables);
+ curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
+ curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); //CURL doesn't like google's cert
+
+ if (is_array($post_variables)) {
+ curl_setopt($ch, CURLOPT_POST, true);
+ curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($post_variables, '', '&'));
+ }
+
+ if (is_array($headers)) {
+ $string_headers = array();
+ foreach ($headers as $key => $value) {
+ $string_headers[] = "$key: $value";
+ }
+ curl_setopt($ch, CURLOPT_HTTPHEADER, $string_headers);
+ }
+
+ $response = curl_exec($ch);
+ $code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
+
+ curl_close($ch);
+
+ return array('body' => $response, 'code' => $code);
+ }
+
+ /**
+ * HTTP request using native PHP fopen function
+ * Requires PHP openSSL
+ *
+ * @param Array $get_variables
+ * @param Array $post_variables
+ * @param Array $headers
+ */
+ private function fopenRequest($get_variables=null, $post_variables=null, $headers=null) {
+ $http_options = array('method'=>'GET', 'timeout'=>3);
+
+ $string_headers = '';
+ if (is_array($headers)) {
+ foreach ($headers as $key => $value) {
+ $string_headers .= "$key: $value\r\n";
+ }
+ }
+
+ if (is_array($get_variables)) {
+ $get_variables = '?' . str_replace('&', '&', urldecode(http_build_query($get_variables, '', '&')));
+ }
+ else {
+ $get_variables = null;
+ }
+
+ if (is_array($post_variables)) {
+ $post_variables = str_replace('&', '&', urldecode(http_build_query($post_variables, '', '&')));
+ $http_options['method'] = 'POST';
+ $string_headers = "Content-type: application/x-www-form-urlencoded\r\n" . "Content-Length: " . strlen($post_variables) . "\r\n" . $string_headers;
+ $http_options['header'] = $string_headers;
+ $http_options['content'] = $post_variables;
+ }
+ else {
+ $post_variables = '';
+ $http_options['header'] = $string_headers;
+ }
+
+ $context = stream_context_create(array('http'=>$http_options));
+ $response = @file_get_contents($this->url . $get_variables, null, $context);
+
+ return array('body'=>$response!==false?$response:'Request failed, consider using php5-curl module for more information.', 'code'=>$response!==false?'200':'400');
+ }
}
\ No newline at end of file
diff --git a/templates/Includes/DashboardGoogleAnalyticsPanel.ss b/templates/Includes/DashboardGoogleAnalyticsPanel.ss
index f07c1a6..0803978 100644
--- a/templates/Includes/DashboardGoogleAnalyticsPanel.ss
+++ b/templates/Includes/DashboardGoogleAnalyticsPanel.ss
@@ -1,5 +1,5 @@
<% if not IsConfigured %>
- <% _t('Dashboard.NOGOOGLEACCOUNT','You have not defined a Google Analytics account for this project. You can set this up by adding the following code to your project _config.php file:
DashboardGoogleAnalyticsPanel::set_account($email, $password, $profileID);
Where $email is your Google email, $password is your password, and $profileID is the profile ID of the account, found on the "Profile Settings" tab of the Google Analytics profile for this project.
') %>
+ <% _t('Dashboard.NOGOOGLEACCOUNT','You have not defined a Google Analytics account for this project. You can set this up by setting the config settings email, profile, and key_file_path on DashboardGoogleAnalyticsPanel.
More information on key files
') %>
<% else_if not IsConnected %>
<% _t('Dashboard.INVALIDGOOGLEACCOUNT','The account information you have entered for Google Analytics appears to be invalid. Please check the email and password combination and try again.
') %>
<% else %>
@@ -17,4 +17,8 @@
<% end_if %>
+<% end_if %>
+
+<% if $Error %>
+ $Error
<% end_if %>
\ No newline at end of file