├── 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 | } --------------------------------------------------------------------------------