* @copyright 2007-2013 PrestaShop SA * @license http://opensource.org/licenses/afl-3.0.php Academic Free License (AFL 3.0) * International Registered Trademark & Property of PrestaShop SA */ if (!defined('_PS_VERSION_')) exit; class Gapi extends Module { public function __construct($type = null) { $this->name = 'gapi'; $this->tab = 'administration'; $this->version = 0.9; $this->author = 'PrestaShop'; $this->need_instance = 0; parent::__construct(); $this->displayName = $this->l('Google Analytics API'); } public function getContent() { // You can switch to the 1.3 API by replacing the following function call by $this->api_1_3_getContent() return $this->api_3_0_getContent(); } public function api_3_0_authenticate() { // https://developers.google.com/accounts/docs/OAuth2WebServer $params = array( 'response_type' => 'code', 'client_id' => Configuration::get('PS_GAPI30_CLIENT_ID_TMP'), 'scope' => 'https://www.googleapis.com/auth/analytics.readonly', 'redirect_uri' => Tools::getShopDomain(true, false).__PS_BASE_URI__.'modules/'.$this->name.'/oauth2callback.php', 'state' => $this->context->employee->id.'-'.Tools::encrypt($this->context->employee->id.Configuration::get('PS_GAPI30_CLIENT_ID_TMP')), 'approval_prompt' => 'force', 'access_type' => 'offline' ); Tools::redirectLink('https://accounts.google.com/o/oauth2/auth?'.http_build_query($params)); } public function api_3_0_refreshtoken() { $params = array( 'client_id' => Configuration::get('PS_GAPI30_CLIENT_ID'), 'client_secret' => Configuration::get('PS_GAPI30_CLIENT_SECRET'), 'grant_type' => 'authorization_code' ); // https://developers.google.com/accounts/docs/OAuth2WebServer#offline if (Configuration::get('PS_GAPI30_REFRESH_TOKEN')) $params['refresh_token'] = Configuration::get('PS_GAPI30_REFRESH_TOKEN'); else { $params['code'] = Configuration::get('PS_GAPI30_AUTHORIZATION_CODE'); $params['redirect_uri'] = Tools::getShopDomain(true, false).__PS_BASE_URI__.'modules/'.$this->name.'/oauth2callback.php'; } $stream_context = stream_context_create(array( 'http' => array( 'method'=> 'POST', 'content' => http_build_query($params), 'header' => 'Content-type: application/x-www-form-urlencoded', 'timeout' => 5, ) )); if (!$response_json = Tools::file_get_contents('https://accounts.google.com/o/oauth2/token', false, $stream_context)) return false; $response = Tools::jsonDecode($response_json, true); Configuration::updateValue('PS_GAPI30_ACCESS_TOKEN', $response['access_token']); Configuration::updateValue('PS_GAPI30_TOKEN_EXPIRATION', time() + (int)$response['expires_in']); if (isset($response['refresh_token'])) Configuration::updateValue('PS_GAPI30_REFRESH_TOKEN', $response['refresh_token']); return true; } public function api_3_0_getContent() { $html = ''; if (Tools::isSubmit('PS_GAPI30_CLIENT_ID')) { Configuration::updateValue('PS_GAPI30_REQUEST_URI_TMP', dirname($_SERVER['REQUEST_URI']).'/'.AdminController::$currentIndex.'&configure='.$this->name.'&token='.Tools::getAdminTokenLite('AdminModules')); Configuration::updateValue('PS_GAPI30_CLIENT_ID_TMP', trim(Tools::getValue('PS_GAPI30_CLIENT_ID'))); Configuration::updateValue('PS_GAPI30_CLIENT_SECRET_TMP', trim(Tools::getValue('PS_GAPI30_CLIENT_SECRET'))); Configuration::updateValue('PS_GAPI30_PROFILE_TMP', trim(Tools::getValue('PS_GAPI30_PROFILE'))); // This will redirect the user to Google API authentication page $this->api_3_0_authenticate(); } elseif (Tools::getValue('oauth2callback') == 'error') $html .= $this->displayError('Google API: Access denied'); elseif (Tools::getValue('oauth2callback') == 'undefined') $html .= $this->displayError('Something wrong happened with Google API authorization'); elseif (Tools::getValue('oauth2callback') == 'success') { if ($this->api_3_0_refreshtoken()) $html .= $this->displayConfirmation('Google API Authorization granted'); else $html .= $this->displayError('Google API Authorization granted but access token cannot be retrieved'); } if (Configuration::get('PS_GAPI30_CLIENT_ID') && Configuration::get('PS_GAPI30_CLIENT_SECRET') && Configuration::get('PS_GAPI30_PROFILE')) { $result_test = $this->api_3_0_requestReportData('', 'ga:visits,ga:uniquePageviews', date('Y-m-d', strtotime('-1 day')), date('Y-m-d', strtotime('-1 day')), null, null, 1, 1); if (!$result_test) $html .= $this->displayError('Cannot retrieve test results'); else $html .= $this->displayConfirmation(sprintf($this->l('Yesterday, your store received the visit of %d people for a total of %d unique page views.'), $result_test[0]['metrics']['visits'], $result_test[0]['metrics']['uniquePageviews'])); } $helper = new HelperOptions($this); $helper->id = $this->id; $helper->currentIndex = AdminController::$currentIndex.'&configure='.$this->name; $helper->token = Tools::getAdminTokenLite('AdminModules'); $helper->module = $this; $fields_options = array( 'general' => array( 'title' => $this->l('Google Analytics API'), 'icon' => $this->_path.'logo.png', 'fields' => $fields = array( 'PS_GAPI30_CLIENT_ID' => array( 'title' => $this->l('Client ID'), 'validation' => 'isEmail', 'type' => 'text' ), 'PS_GAPI30_CLIENT_SECRET' => array( 'title' => $this->l('Client Secret'), 'validation' => 'isNothing', 'type' => 'text' ), 'PS_GAPI30_PROFILE' => array( 'title' => $this->l('Profile'), 'validation' => 'isUnsignedInt', 'type' => 'text', 'desc' => sprintf($this->l('You can find your profile ID in the address bar of your browser while accessing Analytics report: %s.'), ''.$this->l('see screenshot').'') ) ), 'submit' => array('title' => $this->l('Authenticate')), ) ); return $html.$helper->generateOptions($fields_options); } public function api_3_0_oauth2callback() { if (!Tools::getValue('state')) die ('token missing'); $state = explode('-', Tools::getValue('state')); if (count($state) != 2) die ('token malformed'); if ($state[1] != Tools::encrypt($state[0].Configuration::get('PS_GAPI30_CLIENT_ID_TMP'))) die ('token not valid'); $oauth2callback = 'undefined'; $url = Configuration::get('PS_GAPI30_REQUEST_URI_TMP'); if (Tools::getValue('error')) $oauth2callback = 'error'; elseif (Tools::getValue('code')) { Configuration::updateValue('PS_GAPI30_CLIENT_ID', Configuration::get('PS_GAPI30_CLIENT_ID_TMP')); Configuration::updateValue('PS_GAPI30_CLIENT_SECRET', Configuration::get('PS_GAPI30_CLIENT_SECRET_TMP')); Configuration::updateValue('PS_GAPI30_PROFILE', Configuration::get('PS_GAPI30_PROFILE_TMP')); Configuration::updateValue('PS_GAPI30_AUTHORIZATION_CODE', Tools::getValue('code')); $oauth2callback = 'success'; } Configuration::deleteByName('PS_GAPI30_CLIENT_ID_TMP'); Configuration::deleteByName('PS_GAPI30_CLIENT_SECRET_TMP'); Configuration::deleteByName('PS_GAPI30_PROFILE_TMP'); Configuration::deleteByName('PS_GAPI30_REQUEST_URI_TMP'); Configuration::deleteByName('PS_GAPI30_REFRESH_TOKEN'); Tools::redirectAdmin($url.'&oauth2callback='.$oauth2callback); } // https://developers.google.com/analytics/devguides/reporting/core/dimsmets // requestReportData('ga:country', 'ga:visits', '2013-08-25', '2013-08-25', null, null, 1, 1000); protected function api_3_0_requestReportData($dimensions, $metrics, $date_from, $date_to, $sort = null, $filters = null, $start = 1, $limit = 30) { if (Configuration::get('PS_GAPI30_TOKEN_EXPIRATION') < time() + 30 && !$this->api_3_0_refreshtoken()) return false; $bearer = Configuration::get('PS_GAPI30_ACCESS_TOKEN'); $params = array( 'ids' => 'ga:'.Configuration::get('PS_GAPI30_PROFILE'), 'dimensions' => $dimensions, 'metrics' => $metrics, 'sort' => $sort ? $sort : $metrics, 'start-date' => $date_from, 'end-date' => $date_to, 'start-index' => $start, 'max-results' => $limit, ); if ($filters !== null) $params['filters'] = $filters; $content = str_replace('&', '&', urldecode(http_build_query($params))); $stream_context = stream_context_create(array( 'http' => array( 'method'=> 'GET', 'header' => 'Authorization: Bearer '.$bearer."\r\n", 'timeout' => 5, ) )); if (!$response_json = Tools::file_get_contents('https://www.googleapis.com/analytics/v3/data/ga?'.$content, false, $stream_context)) return false; // https://developers.google.com/analytics/devguides/reporting/core/v3/reference $response = Tools::jsonDecode($response_json, true); $result = array(); foreach ($response['rows'] as $row) { $metrics = array(); $dimensions = array(); foreach($row as $key => $value) if ($response['columnHeaders'][$key]['columnType'] == 'DIMENSION') $dimensions[str_replace('ga:', '', $response['columnHeaders'][$key]['name'])] = $value; elseif ($response['columnHeaders'][$key]['columnType'] == 'METRIC') $metrics[str_replace('ga:', '', $response['columnHeaders'][$key]['name'])] = $value; $result[] = array('metrics' => $metrics, 'dimensions' => $dimensions); } return $result; } public function api_1_3_getContent() { $html = ''; if (Tools::isSubmit('PS_GAPI13_EMAIL')) { if ($this->api_1_3_authenticate(Tools::getValue('PS_GAPI13_EMAIL'), Tools::getValue('PS_GAPI13_PASSWORD'))) { Configuration::updateValue('PS_GAPI13_EMAIL', Tools::getValue('PS_GAPI13_EMAIL')); Configuration::updateValue('PS_GAPI13_PASSWORD', Tools::getValue('PS_GAPI13_PASSWORD')); Configuration::updateValue('PS_GAPI13_PROFILE', Tools::getValue('PS_GAPI13_PROFILE')); } else $html .= $this->displayError($this->l('Authentication failed')); } if (Configuration::get('PS_GAPI13_EMAIL') && Configuration::get('PS_GAPI13_PASSWORD') && Configuration::get('PS_GAPI13_PROFILE')) { $result_test = $this->api_3_0_requestReportData('', 'ga:visits,ga:uniquePageviews', date('Y-m-d', strtotime('-1 day')), date('Y-m-d', strtotime('-1 day')), null, null, 1, 1); if (!$result_test) $html .= $this->displayError('Cannot retrieve test results'); else $html .= $this->displayConfirmation(sprintf($this->l('Yesterday, your store received the visit of %d people for a total of %d unique page views.'), $result_test[0]['metrics']['visits'], $result_test[0]['metrics']['uniquePageviews'])); } $helper = new HelperOptions($this); $helper->id = $this->id; $helper->currentIndex = AdminController::$currentIndex.'&configure='.$this->name; $helper->token = Tools::getAdminTokenLite('AdminModules'); $helper->module = $this; $fields_options = array( 'general' => array( 'title' => $this->l('Google Analytics API'), 'icon' => $this->_path.'logo.png', 'fields' => $fields = array( 'PS_GAPI13_EMAIL' => array( 'title' => $this->l('Email'), 'validation' => 'isEmail', 'type' => 'text' ), 'PS_GAPI13_PASSWORD' => array( 'title' => $this->l('Password'), 'validation' => 'isNothing', 'type' => 'password' ), 'PS_GAPI13_PROFILE' => array( 'title' => $this->l('Profile'), 'validation' => 'isUnsignedInt', 'type' => 'text', 'desc' => sprintf($this->l('You can find your profile ID in the address bar of your browser while accessing Analytics report: %s.'), ''.$this->l('see screenshot').'') ) ), 'submit' => array('title' => $this->l('Authenticate'), 'class' => 'button'), ) ); return $html.$helper->generateOptions($fields_options); } protected function api_1_3_authenticate($email, $password) { $stream_context = stream_context_create(array( 'http' => array( 'method'=> 'POST', 'content' => 'accountType=GOOGLE&Email='.urlencode($email).'&Passwd='.urlencode($password).'&source=GAPI-1.3&service=analytics', 'header' => 'Content-type: application/x-www-form-urlencoded', 'timeout' => 5, ) )); if (!$response = Tools::file_get_contents('https://www.google.com/accounts/ClientLogin', false, $stream_context)) return false; parse_str(str_replace(array("\n", "\r\n"), '&', $response), $response_array); if (!is_array($response_array) || !isset($response_array['Auth']) || empty($response_array['Auth'])) return false; $this->auth_token = $response_array['Auth']; return true; } // requestReportData('ga:country', 'ga:visits', '2013-08-25', '2013-08-25', null, null, 1, 1000); protected function api_1_3_requestReportData($dimensions, $metrics, $date_from, $date_to, $sort = null, $filters = null, $start = 1, $limit = 30) { if (!$this->api_1_3_authenticate(Configuration::get('PS_GAPI13_EMAIL'), Configuration::get('PS_GAPI13_PASSWORD'))) return false; $params = array( 'ids' => 'ga:'.Configuration::get('PS_GAPI13_PROFILE'), 'dimensions' => $dimensions, 'metrics' => $metrics, 'sort' => $sort ? $sort : $metrics, 'start-date' => $date_from, 'end-date' => $date_to, 'start-index' => $start, 'max-results' => $limit, ); if ($filters !== null) $params['filters'] = $filters; $content = str_replace('&', '&', urldecode(http_build_query($params))); $stream_context = stream_context_create(array( 'http' => array( 'method'=> 'GET', 'header' => "Authorization: GoogleLogin auth=".$this->auth_token."\r\n", 'timeout' => 5, ) )); if (!$response = Tools::file_get_contents('https://www.google.com/analytics/feeds/data?'.$content, false, $stream_context)) return false; $xml = simplexml_load_string($response); /* Meta data not useful at this time */ /* $report_root_parameters = array(); $report_aggregate_metrics = array(); $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); foreach($google_results->aggregates->metric as $aggregate_metric) { $key = str_replace('ga:', '', $aggregate_metric->attributes()->name); $metric_value = strval($aggregate_metric->attributes()->value); if (preg_match('/^(\d+\.\d+)|(\d+E\d+)|(\d+.\d+E\d+)$/', $metric_value)) $report_aggregate_metrics[$key] = floatval($metric_value); else $report_aggregate_metrics[$key] = intval($metric_value); } */ $result = array(); foreach($xml->entry as $entry) { $metrics = array(); foreach ($entry->children('http://schemas.google.com/analytics/2009')->metric as $metric) { $key = str_replace('ga:','',$metric->attributes()->name); $metric_value = strval($metric->attributes()->value); if (preg_match('/^(\d+\.\d+)|(\d+E\d+)|(\d+.\d+E\d+)$/',$metric_value)) $metrics[$key] = floatval($metric_value); else $metrics[$key] = intval($metric_value); } $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); $result[] = array('metrics' => $metrics, 'dimensions' => $dimensions); } return $result; } }