├── ga_stats_filter_handler.inc
├── ga_stats.info
├── README
├── ga_stats.views.inc
├── ga_stats.install
├── inc
├── ga.inc
└── gapi.class.php
└── ga_stats.module
/ga_stats_filter_handler.inc:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/ga_stats.info:
--------------------------------------------------------------------------------
1 | core = "7.x"
2 | description = "Allows you to pull in data that Google Analytics has gathered for your site and use it in Views."
3 | name = "Google Analytics Statistics"
4 | package = "Statistics"
5 |
--------------------------------------------------------------------------------
/README:
--------------------------------------------------------------------------------
1 | Google Analytics Statistics - @author jcaldwell aka jec006
2 |
3 | A Drupal 7 module that provides views integration for the Google Analytics Data Export API.
4 |
5 | This allows you to use Google Analytics directly through views to do things like Most Popular, Most Read ect.
6 | You can combine the statistics to create interesting results.
7 |
8 | After you install the module - go to /admin/config/ga_stats to configure your Google Analytics account information and begin pulling statistics.
--------------------------------------------------------------------------------
/ga_stats.views.inc:
--------------------------------------------------------------------------------
1 | $mv){
15 | foreach($times as $tk=>$tv){
16 | $data['ga_stats_count_'.$mk.'_'.$tk]['table']['group'] = t('Statistics');
17 | $data['ga_stats_count_'.$mk.'_'.$tk]['table']['join'] = array(
18 | 'node' => array(
19 | 'table' => 'ga_stats_count',
20 | 'left_field' => 'nid',
21 | 'field' => 'nid',
22 | 'type' => 'LEFT OUTER',
23 | 'extra' => 'ga_stats_count_'.$mk.'_'.$tk.'.metric=\''.$mk.'\' AND ga_stats_count_'.$mk.'_'.$tk.'.timeframe=\''.$tk.'\''
24 | )
25 | );
26 | $data['ga_stats_count_'.$mk.'_'.$tk]['count'] = array(
27 | 'title' => t("$mv $tv"),
28 | 'help' => t('Information retrieved from Google Analytics'),
29 | 'field' => array(
30 | 'handler'=> 'views_handler_field_numeric',
31 | ),
32 | 'sort' => array(
33 | 'handler' => 'views_handler_sort',
34 | ),
35 | 'filter' => array(
36 | 'handler' => 'views_handler_filter_numeric',
37 | )
38 | );
39 | }
40 | }
41 |
42 | return $data;
43 | }
--------------------------------------------------------------------------------
/ga_stats.install:
--------------------------------------------------------------------------------
1 | 'Stores counts for different metics',
11 | 'fields' => array(
12 | 'nid' => array(
13 | 'type' => 'int',
14 | 'not null' => FALSE,
15 | 'description' => 'nid of related node',
16 | ),
17 | 'url' => array(
18 | 'type' => 'varchar',
19 | 'length' => 128,
20 | 'not null' => FALSE,
21 | 'description' => 'url of page',
22 | ),
23 | 'metric' => array(
24 | 'type' => 'varchar',
25 | 'length' => 128,
26 | 'not null' => FALSE,
27 | 'description' => 'name of metric',
28 | ),
29 | 'timeframe' => array(
30 | 'type' => 'varchar',
31 | 'length' => 128,
32 | 'not null' => FALSE,
33 | 'description' => 'timeframe of metric'
34 | ),
35 | 'count' => array(
36 | 'type' => 'int',
37 | 'not null' => TRUE,
38 | 'description' => 'number of reads',
39 | ),
40 | ),
41 | 'indexes' => array(
42 | 'ga_stats_metric'=>array('metric'),
43 | 'ga_stats_timeframe'=>array('timeframe'),
44 | 'ga_stats_nid'=>array('nid')
45 | )
46 | );
47 | return $schema;
48 | }
49 |
50 | /*
51 | * Implementation of hook_install
52 | */
53 | function ga_stats_install() {
54 |
55 | }
56 |
57 | /*
58 | * Implementation of hook_uninstall
59 | */
60 | function ga_stats_uninstall() {
61 | variable_del('ga_stats_email');
62 | variable_del('ga_stats_password');
63 | variable_del('ga_stats_profile');
64 | }
65 |
--------------------------------------------------------------------------------
/inc/ga.inc:
--------------------------------------------------------------------------------
1 | $d) {
39 | $data_array[$k]['url'] = $d[$url_dim];
40 | }
41 |
42 | return $data_array;
43 | }
44 |
45 | /*
46 | * Helper function to list metrics with plugin names and google analytics names
47 | */
48 | function ga_stats_ga_metrics($all=false) {
49 |
50 | $metrics = array(
51 | 'pageviews'=>'Page Views',
52 | 'uniquePageviews'=>'Unique Page Views',
53 | 'avgTimeOnPage'=>'Average Time on Page',
54 | 'entrances'=>'Entrance Page'
55 | );
56 |
57 | if(!$all){
58 | $enabled = variable_get('ga_stats_enabled_metrics', array('pageviews'));
59 | foreach($metrics as $k=>$v){
60 | if(!$enabled[$k]){
61 | unset($metrics[$k]);
62 | }
63 | }
64 | }
65 |
66 | return $metrics;
67 | }
68 | /**
69 | * @param $just_keys : whether we should return all the data or just the keys->values array
70 | * TODO: add forever
71 | */
72 | function ga_stats_ga_timeframes($just_keys=false, $all=false){
73 | if($just_keys){
74 | $timeframes = array(
75 | 'hour' => 'in the past hour',
76 | 'today' => 'in the past 24 hours',
77 | 'week' => 'in the past 7 days',
78 | 'month' => 'in the past 31 days',
79 | 'forever' => 'for all time'
80 | );
81 | } else {
82 | $timeframes = array(
83 | 'hour'=>array('secsToSub'=>(60*60), 'filter'=>'hour=='+date('G'), 'title'=>'in the past hour'),
84 | 'today'=>array('secsToSub'=>(60*60*24), 'filter'=>'day=='+date('j'), 'title'=>'in the past 24 hours'),
85 | 'week'=>array('secsToSub'=>(60*60*24*7), 'title'=>'in the past 7 days'),
86 | 'month'=>array('secsToSub'=>(60*60*24*31), 'title'=>'in the past 31 days'),
87 | 'forever'=>array('secsToSub'=>time() - strtotime('2005-01-01'), 'title'=>'for all time')
88 | );
89 | }
90 |
91 | if(!$all){
92 | $enabled = variable_get('ga_stats_enabled_timeframes', array('today', 'month'));
93 | foreach($timeframes as $k=>$v){
94 | if(!$enabled[$k]){
95 | unset($timeframes[$k]);
96 | }
97 | }
98 | }
99 |
100 | return $timeframes;
101 | }
102 |
103 | function ga_stats_ga_data_array($data_in) {
104 | $data_all = array();
105 | $data = array();
106 |
107 | foreach ($data_in as $d) {
108 | $metrics = $d->getMetrics();
109 | $dimensions = $d->getDimensions();
110 | $data_all[] = array_merge($metrics, $dimensions);
111 | }
112 | return $data_all;
113 | }
114 |
115 | function ga_stats_ga_get_accounts(){
116 | require_once 'gapi.class.php';
117 |
118 | $user = variable_get('ga_stats_email', '');
119 | $password = variable_get('ga_stats_password', '');
120 |
121 | if($user && $password){
122 | try{
123 | $ga = new gapi($user, $password);
124 | $accounts = $ga->requestAccountData(1, 5000);
125 | return $accounts;
126 | } catch (Exception $e) {
127 | drupal_set_message('Invalid Google Analytics login.', 'error');
128 | watchdog('ga_stats', 'Invalid Google Analytics login.');
129 | return;
130 | }
131 | } else {
132 | drupal_set_message(t('Google Analytics Email and password not set - Could not retrieve Account Information.'), 'error');
133 | watchdog('ga_stats', t('Google Analytics email and password not set.'));
134 | }
135 | }
136 |
137 | /**
138 | * @param $metric - one of the metrics values - a string value - i.e. 'pageviews'
139 | * $report_id, $dimensions, $metrics, $sort_metric=null, $filter=null, $start_date=null, $end_date=null, $start_index=1, $max_results=30
140 | */
141 |
142 | function ga_stats_query_data($request){
143 | //this is really a waste I think because this shit is preprocessed
144 | require_once 'gapi.class.php';
145 |
146 | $user = variable_get('ga_stats_email', '');
147 | $password = variable_get('ga_stats_password', '');
148 | $aid = variable_get('ga_stats_profile', '');
149 |
150 | if($user && $password){
151 | try{
152 | $ga = new gapi($user, $password);
153 | $data = $ga->requestReportData($aid, $request['dimensions'], $request['metrics'], $request['sort_metric'], null, $request['start_date'], $request['end_date'], 1, $request['max_results']);
154 | } catch (Exception $e) {
155 | drupal_set_message('Invalid Google Analytics login.', 'error');
156 | watchdog('ga_stats', 'Invalid Google Analytics login.');
157 | return array();
158 | }
159 | return $data;
160 | } else {
161 | drupal_set_message('Google Analytics Email and password not set.', 'error');
162 | watchdog('ga_stats', 'Google Analytics email and password not set.');
163 | }
164 | }
--------------------------------------------------------------------------------
/ga_stats.module:
--------------------------------------------------------------------------------
1 | url = $d['url'];
20 | $count->count = $d[$metric];
21 | $count->metric = $metric;
22 | $count->nid = false;
23 | $count->timeframe = $timeframe;
24 | $alias = preg_replace('/^\//','',$count->url);
25 | if( !preg_match('/^node\/([0-9]*)/',$alias,$matches) ){
26 | $alias = drupal_lookup_path('source',$alias);
27 | }
28 | if( preg_match('/^node\/([0-9]*)/',$alias,$matches) ){
29 | $count->nid = $matches[1];
30 | }
31 |
32 | // only log nodes
33 | if($count->nid) {
34 | $counts[] = $count;
35 | }
36 | }
37 |
38 | return $counts;
39 | }
40 |
41 | /**
42 | * Implementation of hook_view_api
43 | */
44 | function ga_stats_views_api(){
45 | return array(
46 | 'api' => 2,
47 | );
48 | }
49 |
50 |
51 | /*
52 | * Implementation of hook_cron
53 | * TODO: May want to hook this up another way (drush?)
54 | */
55 | function ga_stats_cron() {
56 |
57 | if(!cache_get('ga_stats_data')){
58 | $data = ga_stats_update_counts();
59 | //save the data for 10 or 30 minutes (based on whether hourly data is enabled
60 | $times = variable_get('ga_stats_enabled_timeframes', array('today', 'month'));
61 | if(in_array('hour', $times)){
62 | $add = 60*10;
63 | } else {
64 | $add = 60 * 30;
65 | }
66 |
67 | cache_set('ga_stats_data', $data, 'cache', time()+$add);
68 | }
69 | }
70 |
71 | /*
72 | * Goes through sources and metrics and updates databases
73 | * NOTE: as views can not tell the differenece between metrics and time frames
74 | * we are delete all counts before rebuilding
75 | */
76 | function ga_stats_update_counts() {
77 | $metrics = ga_stats_ga_metrics();
78 | $timeframes = ga_stats_ga_timeframes();
79 | $data = array();
80 | $user = variable_get('ga_stats_email', '');
81 | $password = variable_get('ga_stats_password', '');
82 | $aid = variable_get('ga_stats_profile', '');
83 |
84 | if($user && $password){
85 | foreach ($metrics as $metric=>$title) {
86 | foreach ($timeframes as $key=>$time){
87 | $filter = isset($time['filter']) ? $time['filter'] : null;
88 | $new_data = ga_stats_get_data($metric, time()-$time['secsToSub'], time(), $key, $filter);
89 | $data= array_merge($data, $new_data);
90 | }
91 | }
92 |
93 | db_query('DELETE FROM {ga_stats_count}');
94 | foreach ($data as $obj) {
95 | ga_stats_write_count($obj);
96 | }
97 | drupal_set_message('Counts Successfully Updated');
98 | return $data;
99 | } else {
100 | drupal_set_message('Google Analytics email and password not set.', 'error');
101 | }
102 | }
103 |
104 | /*
105 | * Implimentation of hook_menu
106 | *
107 | * Add admin page and update count call back
108 | */
109 |
110 | function ga_stats_menu() {
111 | $items['admin/config/system/ga_stats'] = array(
112 | 'title' => 'Google Analytics Statistics',
113 | 'description' => 'Configuration for Google Analytics Statistics',
114 | 'page callback' => 'drupal_get_form',
115 | 'page arguments' => array('ga_stats_admin_settings'),
116 | 'access arguments' => array('administer'),
117 | 'type' => MENU_NORMAL_ITEM,
118 | );
119 | return $items;
120 | }
121 |
122 | /*
123 | * build the admin form
124 | */
125 | function ga_stats_admin_settings() {
126 | $form = array();
127 |
128 | $form['ga_stats_login'] = array(
129 | '#type' => 'fieldset',
130 | '#title' => t('Google Analytics Login Information'),
131 | '#collapsible' => TRUE
132 | );
133 | $form['ga_stats_login']['ga_stats_email'] = array(
134 | '#type' => 'textfield',
135 | '#title' => t('Account Email'),
136 | '#description' => t('The email account you use to log in to Google Analytics'),
137 | '#default_value' => variable_get('ga_stats_email', '')
138 | );
139 | $form['ga_stats_login']['ga_stats_password'] = array(
140 | '#type' => 'password',
141 | '#title' => t('Account Password'),
142 | '#description' => t('The password you use to log in to Google Analytics'),
143 | '#default_value' => variable_get('ga_stats_password', '')
144 | );
145 |
146 | $form['ga_stats_accounts'] = array(
147 | '#type' => 'fieldset',
148 | '#title' => t('Google Analytics Tracking Accounts'),
149 | '#collapsible' => TRUE
150 | );
151 | if(variable_get('ga_stats_email', false) && variable_get('ga_stats_password', false)){
152 | $account = ga_stats_ga_get_accounts();
153 | } else {
154 | $account = false;
155 | }
156 | $options = array();
157 | if(is_array($account)){
158 | foreach($account as $id=>$value){
159 | $acc = $value->getProperties();
160 | $options[$acc['profileId']] = $acc['title'];
161 | }
162 | }
163 | if($options){
164 | $form['ga_stats_accounts']['ga_stats_profile'] = array(
165 | '#type' => 'select',
166 | '#title' => t('Google Analytics Profile to Use'),
167 | '#description' => t('The Google Analytics profile from which to retrieve statistics'),
168 | '#options' => $options,
169 | '#default_value' => variable_get('ga_stats_profile', '')
170 | );
171 |
172 | } else {
173 | $form['ga_stats_accounts']['ga_stats_profile'] = array(
174 | '#type' => 'markup',
175 | '#markup' => '
'.t('Email and Pasword not set or invalid. Please set the login information and save this form. You will then be able to view and configure account information.').'
'
176 | );
177 | }
178 |
179 | $form['enabled_stats'] = array(
180 | '#type' => 'fieldset',
181 | '#title' => t('Enabled Statistics'),
182 | '#description' => t('Make sure to clear the Drupal cache after changing this setting in order to inform Views of the new settings.
WARNING: Do not disable a setting which is currently in use in Views.')
183 | );
184 |
185 | $form['enabled_stats']['ga_stats_enabled_metrics'] = array(
186 | '#type'=>'checkboxes',
187 | '#default_value'=> variable_get('ga_stats_enabled_metrics', array('pageviews')),
188 | '#options' => ga_stats_ga_metrics(true),
189 | '#title' => t('Metrics'),
190 | '#description' => t('The metrics that will be available from Google Analytics in Views.')
191 | );
192 |
193 | $form['enabled_stats']['ga_stats_enabled_timeframes'] = array(
194 | '#type'=>'checkboxes',
195 | '#default_value'=> variable_get('ga_stats_enabled_timeframes', array('today', 'month')),
196 | '#options' => ga_stats_ga_timeframes(true, true),
197 | '#title' => t('Time Frames'),
198 | '#description' => t('The timeframes that will be available from Google Analytics in Views.')
199 | );
200 |
201 | $form['ga_stats_max_results'] = array(
202 | '#type' => 'textfield',
203 | '#title' => t('Max Results per Metric/Timeframe'),
204 | '#description' => t('The max results that a call (a metric/timeframe combination) can return. MUST be a number.'),
205 | '#default_value' => variable_get('ga_stats_max_results', '100')
206 | );
207 |
208 | if(variable_get('ga_stats_profile', false)){
209 | $form['actions']['ga_stats_update'] = array(
210 | '#type' => 'button',
211 | '#value' => t('Update Counts'),
212 | '#weight' => 20,
213 | '#executes_submit_callback' => TRUE,
214 | '#submit' => array('ga_stats_update_counts'),
215 | );
216 | }
217 |
218 | return system_settings_form($form);
219 | }
220 |
221 | function ga_stats_write_count($count){
222 | drupal_write_record('ga_stats_count', $count);
223 | }
--------------------------------------------------------------------------------
/inc/gapi.class.php:
--------------------------------------------------------------------------------
1 | .
21 | *
22 | * @author Stig Manning
23 | * @version 1.3
24 | *
25 | */
26 |
27 | class gapi
28 | {
29 | const http_interface = 'auto'; //'auto': autodetect, 'curl' or 'fopen'
30 |
31 | const client_login_url = 'https://www.google.com/accounts/ClientLogin';
32 | const account_data_url = 'https://www.google.com/analytics/feeds/accounts/default';
33 | const report_data_url = 'https://www.google.com/analytics/feeds/data';
34 | const interface_name = 'GAPI-1.3';
35 | const dev_mode = false;
36 |
37 | private $auth_token = null;
38 | private $account_entries = array();
39 | private $account_root_parameters = array();
40 | private $report_aggregate_metrics = array();
41 | private $report_root_parameters = array();
42 | private $results = array();
43 |
44 | /**
45 | * Constructor function for all new gapi instances
46 | *
47 | * Set up authenticate with Google and get auth_token
48 | *
49 | * @param String $email
50 | * @param String $password
51 | * @param String $token
52 | * @return gapi
53 | */
54 | public function __construct($email, $password, $token=null)
55 | {
56 | if($token !== null)
57 | {
58 | $this->auth_token = $token;
59 | }
60 | else
61 | {
62 | $this->authenticateUser($email,$password);
63 | }
64 | }
65 |
66 | /**
67 | * Return the auth token, used for storing the auth token in the user session
68 | *
69 | * @return String
70 | */
71 | public function getAuthToken()
72 | {
73 | return $this->auth_token;
74 | }
75 |
76 | /**
77 | * Request account data from Google Analytics
78 | *
79 | * @param Int $start_index OPTIONAL: Start index of results
80 | * @param Int $max_results OPTIONAL: Max results returned
81 | */
82 | public function requestAccountData($start_index=1, $max_results=20)
83 | {
84 | $response = $this->httpRequest(gapi::account_data_url, array('start-index'=>$start_index,'max-results'=>$max_results), null, $this->generateAuthHeader());
85 |
86 | if(substr($response['code'],0,1) == '2')
87 | {
88 | return $this->accountObjectMapper($response['body']);
89 | }
90 | else
91 | {
92 | throw new Exception('GAPI: Failed to request account data. Error: "' . strip_tags($response['body']) . '"');
93 | }
94 | }
95 |
96 | /**
97 | * Request report data from Google Analytics
98 | *
99 | * $report_id is the Google report ID for the selected account
100 | *
101 | * $parameters should be in key => value format
102 | *
103 | * @param String $report_id
104 | * @param Array $dimensions Google Analytics dimensions e.g. array('browser')
105 | * @param Array $metrics Google Analytics metrics e.g. array('pageviews')
106 | * @param Array $sort_metric OPTIONAL: Dimension or dimensions to sort by e.g.('-visits')
107 | * @param String $filter OPTIONAL: Filter logic for filtering results
108 | * @param String $start_date OPTIONAL: Start of reporting period
109 | * @param String $end_date OPTIONAL: End of reporting period
110 | * @param Int $start_index OPTIONAL: Start index of results
111 | * @param Int $max_results OPTIONAL: Max results returned
112 | */
113 | public function requestReportData($report_id, $dimensions, $metrics, $sort_metric=null, $filter=null, $start_date=null, $end_date=null, $start_index=1, $max_results=30)
114 | {
115 | $parameters = array('ids'=>'ga:' . $report_id);
116 |
117 | if(is_array($dimensions))
118 | {
119 | $dimensions_string = '';
120 | foreach($dimensions as $dimesion)
121 | {
122 | $dimensions_string .= ',ga:' . $dimesion;
123 | }
124 | $parameters['dimensions'] = substr($dimensions_string,1);
125 | }
126 | else
127 | {
128 | $parameters['dimensions'] = 'ga:'.$dimensions;
129 | }
130 |
131 | if(is_array($metrics))
132 | {
133 | $metrics_string = '';
134 | foreach($metrics as $metric)
135 | {
136 | $metrics_string .= ',ga:' . $metric;
137 | }
138 | $parameters['metrics'] = substr($metrics_string,1);
139 | }
140 | else
141 | {
142 | $parameters['metrics'] = 'ga:'.$metrics;
143 | }
144 |
145 | if($sort_metric==null&&isset($parameters['metrics']))
146 | {
147 | $parameters['sort'] = $parameters['metrics'];
148 | }
149 | elseif(is_array($sort_metric))
150 | {
151 | $sort_metric_string = '';
152 |
153 | foreach($sort_metric as $sort_metric_value)
154 | {
155 | //Reverse sort - Thanks Nick Sullivan
156 | if (substr($sort_metric_value, 0, 1) == "-")
157 | {
158 | $sort_metric_string .= ',-ga:' . substr($sort_metric_value, 1); // Descending
159 | }
160 | else
161 | {
162 | $sort_metric_string .= ',ga:' . $sort_metric_value; // Ascending
163 | }
164 | }
165 |
166 | $parameters['sort'] = substr($sort_metric_string, 1);
167 | }
168 | else
169 | {
170 | if (substr($sort_metric, 0, 1) == "-")
171 | {
172 | $parameters['sort'] = '-ga:' . substr($sort_metric, 1);
173 | }
174 | else
175 | {
176 | $parameters['sort'] = 'ga:' . $sort_metric;
177 | }
178 | }
179 |
180 | if($filter!=null)
181 | {
182 | $filter = $this->processFilter($filter);
183 | if($filter!==false)
184 | {
185 | $parameters['filters'] = $filter;
186 | }
187 | }
188 |
189 | if($start_date==null)
190 | {
191 | $start_date=date('Y-m-d',strtotime('1 month ago'));
192 | }
193 |
194 | $parameters['start-date'] = $start_date;
195 |
196 | if($end_date==null)
197 | {
198 | $end_date=date('Y-m-d');
199 | }
200 |
201 | $parameters['end-date'] = $end_date;
202 |
203 | $parameters['start-index'] = $start_index;
204 | $parameters['max-results'] = $max_results;
205 |
206 | $parameters['prettyprint'] = gapi::dev_mode ? 'true' : 'false';
207 |
208 | $response = $this->httpRequest(gapi::report_data_url, $parameters, null, $this->generateAuthHeader());
209 |
210 | //HTTP 2xx
211 | if(substr($response['code'],0,1) == '2')
212 | {
213 | return $this->reportObjectMapper($response['body']);
214 | }
215 | else
216 | {
217 | throw new Exception('GAPI: Failed to request report data. Error: "' . strip_tags($response['body']) . '"');
218 | }
219 | }
220 |
221 | /**
222 | * Process filter string, clean parameters and convert to Google Analytics
223 | * compatible format
224 | *
225 | * @param String $filter
226 | * @return String Compatible filter string
227 | */
228 | protected function processFilter($filter)
229 | {
230 | $valid_operators = '(!~|=~|==|!=|>|<|>=|<=|=@|!@)';
231 |
232 | $filter = preg_replace('/\s\s+/',' ',trim($filter)); //Clean duplicate whitespace
233 | $filter = str_replace(array(',',';'),array('\,','\;'),$filter); //Escape Google Analytics reserved characters
234 | $filter = preg_replace('/(&&\s*|\|\|\s*|^)([a-z]+)(\s*' . $valid_operators . ')/i','$1ga:$2$3',$filter); //Prefix ga: to metrics and dimensions
235 | $filter = preg_replace('/[\'\"]/i','',$filter); //Clear invalid quote characters
236 | $filter = preg_replace(array('/\s*&&\s*/','/\s*\|\|\s*/','/\s*' . $valid_operators . '\s*/'),array(';',',','$1'),$filter); //Clean up operators
237 |
238 | if(strlen($filter)>0)
239 | {
240 | return urlencode($filter);
241 | }
242 | else
243 | {
244 | return false;
245 | }
246 | }
247 |
248 | /**
249 | * Report Account Mapper to convert the XML to array of useful PHP objects
250 | *
251 | * @param String $xml_string
252 | * @return Array of gapiAccountEntry objects
253 | */
254 | protected function accountObjectMapper($xml_string)
255 | {
256 | $xml = simplexml_load_string($xml_string);
257 |
258 | $this->results = null;
259 |
260 | $results = array();
261 | $account_root_parameters = array();
262 |
263 | //Load root parameters
264 |
265 | $account_root_parameters['updated'] = strval($xml->updated);
266 | $account_root_parameters['generator'] = strval($xml->generator);
267 | $account_root_parameters['generatorVersion'] = strval($xml->generator->attributes());
268 |
269 | $open_search_results = $xml->children('http://a9.com/-/spec/opensearchrss/1.0/');
270 |
271 | foreach($open_search_results as $key => $open_search_result)
272 | {
273 | $report_root_parameters[$key] = intval($open_search_result);
274 | }
275 | //ADDED: If statement to avoid error messages
276 | if(isset($google_results)){
277 | $account_root_parameters['startDate'] = strval($google_results->startDate);
278 | $account_root_parameters['endDate'] = strval($google_results->endDate);
279 | }
280 | //Load result entries
281 |
282 | foreach($xml->entry as $entry)
283 | {
284 | $properties = array();
285 | foreach($entry->children('http://schemas.google.com/analytics/2009')->property as $property)
286 | {
287 | $properties[str_replace('ga:','',$property->attributes()->name)] = strval($property->attributes()->value);
288 | }
289 |
290 | $properties['title'] = strval($entry->title);
291 | $properties['updated'] = strval($entry->updated);
292 |
293 | $results[] = new gapiAccountEntry($properties);
294 | }
295 |
296 | $this->account_root_parameters = $account_root_parameters;
297 | $this->results = $results;
298 |
299 | return $results;
300 | }
301 |
302 |
303 | /**
304 | * Report Object Mapper to convert the XML to array of useful PHP objects
305 | *
306 | * @param String $xml_string
307 | * @return Array of gapiReportEntry objects
308 | */
309 | protected function reportObjectMapper($xml_string)
310 | {
311 | $xml = simplexml_load_string($xml_string);
312 |
313 | $this->results = null;
314 | $results = array();
315 |
316 | $report_root_parameters = array();
317 | $report_aggregate_metrics = array();
318 |
319 | //Load root parameters
320 |
321 | $report_root_parameters['updated'] = strval($xml->updated);
322 | $report_root_parameters['generator'] = strval($xml->generator);
323 | $report_root_parameters['generatorVersion'] = strval($xml->generator->attributes());
324 |
325 | $open_search_results = $xml->children('http://a9.com/-/spec/opensearchrss/1.0/');
326 |
327 | foreach($open_search_results as $key => $open_search_result)
328 | {
329 | $report_root_parameters[$key] = intval($open_search_result);
330 | }
331 |
332 | $google_results = $xml->children('http://schemas.google.com/analytics/2009');
333 |
334 | foreach($google_results->dataSource->property as $property_attributes)
335 | {
336 | $report_root_parameters[str_replace('ga:','',$property_attributes->attributes()->name)] = strval($property_attributes->attributes()->value);
337 | }
338 |
339 | $report_root_parameters['startDate'] = strval($google_results->startDate);
340 | $report_root_parameters['endDate'] = strval($google_results->endDate);
341 |
342 | //Load result aggregate metrics
343 |
344 | foreach($google_results->aggregates->metric as $aggregate_metric)
345 | {
346 | $metric_value = strval($aggregate_metric->attributes()->value);
347 |
348 | //Check for float, or value with scientific notation
349 | if(preg_match('/^(\d+\.\d+)|(\d+E\d+)|(\d+.\d+E\d+)$/',$metric_value))
350 | {
351 | $report_aggregate_metrics[str_replace('ga:','',$aggregate_metric->attributes()->name)] = floatval($metric_value);
352 | }
353 | else
354 | {
355 | $report_aggregate_metrics[str_replace('ga:','',$aggregate_metric->attributes()->name)] = intval($metric_value);
356 | }
357 | }
358 |
359 | //Load result entries
360 |
361 | foreach($xml->entry as $entry)
362 | {
363 | $metrics = array();
364 | foreach($entry->children('http://schemas.google.com/analytics/2009')->metric as $metric)
365 | {
366 | $metric_value = strval($metric->attributes()->value);
367 |
368 | //Check for float, or value with scientific notation
369 | if(preg_match('/^(\d+\.\d+)|(\d+E\d+)|(\d+.\d+E\d+)$/',$metric_value))
370 | {
371 | $metrics[str_replace('ga:','',$metric->attributes()->name)] = floatval($metric_value);
372 | }
373 | else
374 | {
375 | $metrics[str_replace('ga:','',$metric->attributes()->name)] = intval($metric_value);
376 | }
377 | }
378 |
379 | $dimensions = array();
380 | foreach($entry->children('http://schemas.google.com/analytics/2009')->dimension as $dimension)
381 | {
382 | $dimensions[str_replace('ga:','',$dimension->attributes()->name)] = strval($dimension->attributes()->value);
383 | }
384 |
385 | $results[] = new gapiReportEntry($metrics,$dimensions);
386 | }
387 |
388 | $this->report_root_parameters = $report_root_parameters;
389 | $this->report_aggregate_metrics = $report_aggregate_metrics;
390 | $this->results = $results;
391 |
392 | return $results;
393 | }
394 |
395 | /**
396 | * Authenticate Google Account with Google
397 | *
398 | * @param String $email
399 | * @param String $password
400 | */
401 | protected function authenticateUser($email, $password)
402 | {
403 | $post_variables = array(
404 | 'accountType' => 'GOOGLE',
405 | 'Email' => $email,
406 | 'Passwd' => $password,
407 | 'source' => gapi::interface_name,
408 | 'service' => 'analytics'
409 | );
410 |
411 | $response = $this->httpRequest(gapi::client_login_url,null,$post_variables);
412 |
413 | //Convert newline delimited variables into url format then import to array
414 | parse_str(str_replace(array("\n","\r\n"),'&',$response['body']),$auth_token);
415 |
416 | if(substr($response['code'],0,1) != '2' || !is_array($auth_token) || empty($auth_token['Auth']))
417 | {
418 | throw new Exception('GAPI: Failed to authenticate user. Error: "' . strip_tags($response['body']) . '"');
419 | }
420 |
421 | $this->auth_token = $auth_token['Auth'];
422 | }
423 |
424 | /**
425 | * Generate authentication token header for all requests
426 | *
427 | * @return Array
428 | */
429 | protected function generateAuthHeader()
430 | {
431 | return array('Authorization: GoogleLogin auth=' . $this->auth_token);
432 | }
433 |
434 | /**
435 | * Perform http request
436 | *
437 | *
438 | * @param Array $get_variables
439 | * @param Array $post_variables
440 | * @param Array $headers
441 | */
442 | protected function httpRequest($url, $get_variables=null, $post_variables=null, $headers=null)
443 | {
444 | $interface = gapi::http_interface;
445 |
446 | if(gapi::http_interface =='auto')
447 | {
448 | if(function_exists('curl_exec'))
449 | {
450 | $interface = 'curl';
451 | }
452 | else
453 | {
454 | $interface = 'fopen';
455 | }
456 | }
457 |
458 | if($interface == 'curl')
459 | {
460 | return $this->curlRequest($url, $get_variables, $post_variables, $headers);
461 | }
462 | elseif($interface == 'fopen')
463 | {
464 | return $this->fopenRequest($url, $get_variables, $post_variables, $headers);
465 | }
466 | else
467 | {
468 | throw new Exception('Invalid http interface defined. No such interface "' . gapi::http_interface . '"');
469 | }
470 | }
471 |
472 | /**
473 | * HTTP request using PHP CURL functions
474 | * Requires curl library installed and configured for PHP
475 | *
476 | * @param Array $get_variables
477 | * @param Array $post_variables
478 | * @param Array $headers
479 | */
480 | private function curlRequest($url, $get_variables=null, $post_variables=null, $headers=null)
481 | {
482 | $ch = curl_init();
483 |
484 | if(is_array($get_variables))
485 | {
486 | $get_variables = '?' . str_replace('&','&',urldecode(http_build_query($get_variables)));
487 | }
488 | else
489 | {
490 | $get_variables = null;
491 | }
492 |
493 | curl_setopt($ch, CURLOPT_URL, $url . $get_variables);
494 | curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
495 | curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); //CURL doesn't like google's cert
496 |
497 | if(is_array($post_variables))
498 | {
499 | curl_setopt($ch, CURLOPT_POST, true);
500 | curl_setopt($ch, CURLOPT_POSTFIELDS, $post_variables);
501 | }
502 |
503 | if(is_array($headers))
504 | {
505 | curl_setopt($ch, CURLOPT_HTTPHEADER,$headers);
506 | }
507 |
508 | $response = curl_exec($ch);
509 | $code = curl_getinfo($ch,CURLINFO_HTTP_CODE);
510 |
511 | curl_close($ch);
512 |
513 | return array('body'=>$response,'code'=>$code);
514 | }
515 |
516 | /**
517 | * HTTP request using native PHP fopen function
518 | * Requires PHP openSSL
519 | *
520 | * @param Array $get_variables
521 | * @param Array $post_variables
522 | * @param Array $headers
523 | */
524 | private function fopenRequest($url, $get_variables=null, $post_variables=null, $headers=null)
525 | {
526 | $http_options = array('method'=>'GET','timeout'=>3);
527 |
528 | if(is_array($headers))
529 | {
530 | $headers = implode("\r\n",$headers) . "\r\n";
531 | }
532 | else
533 | {
534 | $headers = '';
535 | }
536 |
537 | if(is_array($get_variables))
538 | {
539 | $get_variables = '?' . str_replace('&','&',urldecode(http_build_query($get_variables)));
540 | }
541 | else
542 | {
543 | $get_variables = null;
544 | }
545 |
546 | if(is_array($post_variables))
547 | {
548 | $post_variables = str_replace('&','&',urldecode(http_build_query($post_variables)));
549 | $http_options['method'] = 'POST';
550 | $headers = "Content-type: application/x-www-form-urlencoded\r\n" . "Content-Length: " . strlen($post_variables) . "\r\n" . $headers;
551 | $http_options['header'] = $headers;
552 | $http_options['content'] = $post_variables;
553 | }
554 | else
555 | {
556 | $post_variables = '';
557 | $http_options['header'] = $headers;
558 | }
559 |
560 | $context = stream_context_create(array('http'=>$http_options));
561 | $response = @file_get_contents($url . $get_variables, null, $context);
562 |
563 | return array('body'=>$response!==false?$response:'Request failed, fopen provides no further information','code'=>$response!==false?'200':'400');
564 | }
565 |
566 | /**
567 | * Case insensitive array_key_exists function, also returns
568 | * matching key.
569 | *
570 | * @param String $key
571 | * @param Array $search
572 | * @return String Matching array key
573 | */
574 | public static function array_key_exists_nc($key, $search)
575 | {
576 | if (array_key_exists($key, $search))
577 | {
578 | return $key;
579 | }
580 | if (!(is_string($key) && is_array($search)))
581 | {
582 | return false;
583 | }
584 | $key = strtolower($key);
585 | foreach ($search as $k => $v)
586 | {
587 | if (strtolower($k) == $key)
588 | {
589 | return $k;
590 | }
591 | }
592 | return false;
593 | }
594 |
595 | /**
596 | * Get Results
597 | *
598 | * @return Array
599 | */
600 | public function getResults()
601 | {
602 | if(is_array($this->results))
603 | {
604 | return $this->results;
605 | }
606 | else
607 | {
608 | return;
609 | }
610 | }
611 |
612 |
613 | /**
614 | * Get an array of the metrics and the matchning
615 | * aggregate values for the current result
616 | *
617 | * @return Array
618 | */
619 | public function getMetrics()
620 | {
621 | return $this->report_aggregate_metrics;
622 | }
623 |
624 | /**
625 | * Call method to find a matching root parameter or
626 | * aggregate metric to return
627 | *
628 | * @param $name String name of function called
629 | * @return String
630 | * @throws Exception if not a valid parameter or aggregate
631 | * metric, or not a 'get' function
632 | */
633 | public function __call($name,$parameters)
634 | {
635 | if(!preg_match('/^get/',$name))
636 | {
637 | throw new Exception('No such function "' . $name . '"');
638 | }
639 |
640 | $name = preg_replace('/^get/','',$name);
641 |
642 | $parameter_key = gapi::array_key_exists_nc($name,$this->report_root_parameters);
643 |
644 | if($parameter_key)
645 | {
646 | return $this->report_root_parameters[$parameter_key];
647 | }
648 |
649 | $aggregate_metric_key = gapi::array_key_exists_nc($name,$this->report_aggregate_metrics);
650 |
651 | if($aggregate_metric_key)
652 | {
653 | return $this->report_aggregate_metrics[$aggregate_metric_key];
654 | }
655 |
656 | throw new Exception('No valid root parameter or aggregate metric called "' . $name . '"');
657 | }
658 | }
659 |
660 | /**
661 | * Class gapiAccountEntry
662 | *
663 | * Storage for individual gapi account entries
664 | *
665 | */
666 | class gapiAccountEntry
667 | {
668 | private $properties = array();
669 |
670 | public function __construct($properties)
671 | {
672 | $this->properties = $properties;
673 | }
674 |
675 | /**
676 | * toString function to return the name of the account
677 | *
678 | * @return String
679 | */
680 | public function __toString()
681 | {
682 | if(isset($this->properties['title']))
683 | {
684 | return $this->properties['title'];
685 | }
686 | else
687 | {
688 | return;
689 | }
690 | }
691 |
692 | /**
693 | * Get an associative array of the properties
694 | * and the matching values for the current result
695 | *
696 | * @return Array
697 | */
698 | public function getProperties()
699 | {
700 | return $this->properties;
701 | }
702 |
703 | /**
704 | * Call method to find a matching parameter to return
705 | *
706 | * @param $name String name of function called
707 | * @return String
708 | * @throws Exception if not a valid parameter, or not a 'get' function
709 | */
710 | public function __call($name,$parameters)
711 | {
712 | if(!preg_match('/^get/',$name))
713 | {
714 | throw new Exception('No such function "' . $name . '"');
715 | }
716 |
717 | $name = preg_replace('/^get/','',$name);
718 |
719 | $property_key = gapi::array_key_exists_nc($name,$this->properties);
720 |
721 | if($property_key)
722 | {
723 | return $this->properties[$property_key];
724 | }
725 |
726 | throw new Exception('No valid property called "' . $name . '"');
727 | }
728 | }
729 |
730 | /**
731 | * Class gapiReportEntry
732 | *
733 | * Storage for individual gapi report entries
734 | *
735 | */
736 | class gapiReportEntry
737 | {
738 | private $metrics = array();
739 | private $dimensions = array();
740 |
741 | public function __construct($metrics,$dimesions)
742 | {
743 | $this->metrics = $metrics;
744 | $this->dimensions = $dimesions;
745 | }
746 |
747 | /**
748 | * toString function to return the name of the result
749 | * this is a concatented string of the dimesions chosen
750 | *
751 | * For example:
752 | * 'Firefox 3.0.10' from browser and browserVersion
753 | *
754 | * @return String
755 | */
756 | public function __toString()
757 | {
758 | if(is_array($this->dimensions))
759 | {
760 | return implode(' ',$this->dimensions);
761 | }
762 | else
763 | {
764 | return '';
765 | }
766 | }
767 |
768 | /**
769 | * Get an associative array of the dimesions
770 | * and the matching values for the current result
771 | *
772 | * @return Array
773 | */
774 | //ADDED : Fixed spelling error
775 | public function getDimensions()
776 | {
777 | return $this->dimensions;
778 | }
779 |
780 | /**
781 | * Get an array of the metrics and the matchning
782 | * values for the current result
783 | *
784 | * @return Array
785 | */
786 | public function getMetrics()
787 | {
788 | return $this->metrics;
789 | }
790 |
791 | /**
792 | * Call method to find a matching metric or dimension to return
793 | *
794 | * @param $name String name of function called
795 | * @return String
796 | * @throws Exception if not a valid metric or dimensions, or not a 'get' function
797 | */
798 | public function __call($name,$parameters)
799 | {
800 | if(!preg_match('/^get/',$name))
801 | {
802 | throw new Exception('No such function "' . $name . '"');
803 | }
804 |
805 | $name = preg_replace('/^get/','',$name);
806 |
807 | $metric_key = gapi::array_key_exists_nc($name,$this->metrics);
808 |
809 | if($metric_key)
810 | {
811 | return $this->metrics[$metric_key];
812 | }
813 |
814 | $dimension_key = gapi::array_key_exists_nc($name,$this->dimensions);
815 |
816 | if($dimension_key)
817 | {
818 | return $this->dimensions[$dimension_key];
819 | }
820 |
821 | throw new Exception('No valid metric or dimesion called "' . $name . '"');
822 | }
823 | }
--------------------------------------------------------------------------------