File Manager

Current Directory: /home/astoriaah/www/administrator/components/com_sh404sef/adapters
Viewing File: /home/astoriaah/www/administrator/components/com_sh404sef/adapters/analyticsgareportdashboard.php
<?php /** * sh404SEF - SEO extension for Joomla! * * @author Yannick Gaultier * @copyright (c) Yannick Gaultier - Weeblr llc - 2018 * @package sh404SEF * @license http://www.gnu.org/copyleft/gpl.html GNU/GPL * @version 4.14.0.3812 * @date 2018-05-16 */ // Security check to ensure this file is being included by a parent file. if (!defined('_JEXEC')) { die('Direct Access to this location is not allowed.'); } /** * Implement Google analytics handling * * @author shumisha * */ class Sh404sefAdapterAnalyticsgareportdashboard { // general SEF config, includes analytics details protected $_config = null; // option set, describes reports to be built protected $_options = null; // a http client, already setup protected $_client = null; // base url for queries protected $_endPoint = ''; // raw response obtained from supplier protected $_rawData = null; // formated responsed object, ready to be used // to display report. This is not HTML or similar, // but simply PHP object/array data, ready // to be displayed, vs xml obtained from supplier protected $_formatedResponse = null; // path to charting library root dir private $_baseChartPath = ''; // temp image files storage private $_tmpImagesPath = 'media/com_sh404sef/analytics'; /** * Constructor, simple init */ public function __construct() { $this->_baseChartPath = sh404SEF_ADMIN_ABS_PATH . 'lib/pChart'; } /** * * @param object $config * @param array $options * @param $Auth * @param $endPoint * * @return \stdClass * @internal param object $client */ public function fetchData($config, $options, $endPoint) { // store parameters $this->_config = $config; $this->_options = $options; $this->_endPoint = $endPoint; // get a http client $this->_client = Sh404sefHelperAnalytics::getHttpClient(); // response object $this->_formatedResponse = new stdClass(); // read data from Google $this->_getData(); // processing of global data always happens $this->_addFiguresVisits(); // call methods to create each of the report sub parts switch ($this->_options['subrequest']) { case 'visits': // create a graph of visits $this->_formatedResponse->images['visits'] = $this->_createVisitsGraph(); break; case 'sources': // create a graph of traffic sources $this->_formatedResponse->images['sources'] = $this->_createSourcesGraph(); break; case 'perf': $this->_addCustomVars(); break; case 'top5referrers': $this->_addFiguresReferrers(); break; case 'top5urls': $this->_addFiguresUrls(); break; case 'topsocialfb': $this->_addFiguresSocialFB(); break; case 'topsocialtweeter': $this->_addFiguresSocialTweeter(); break; case 'topsocialpinterest': $this->_addFiguresSocialPinterest(); break; case 'topsocialplusone': $this->_addFiguresSocialPlusOne(); break; case 'topsocialplusonepage': $this->_addFiguresSocialPlusOnePage(); break; } // send back to caller return $this->_formatedResponse; } /** * Make queries to Google API as needed * to collect data required to build report * * The final report page is built using several ajax calls. The main dashboard template * is first displayed, with only place holders for the various parts * Then some javascript fired with the main dashboard display * performs several requests, or "subrequests", each asking for a specific set * of data or graphic * Reason for this is to speed things up, as the whole request-google-respond-prepare graph * can last 2 or 3 seconds. As there may be 6 or 7 requests, this adds up to more than 15 seconds * Using several ajax calls allow doing all the requests in parallel, and the total time is * not much longer than the time for the longest individual subrequest * On the contrary, some delay between subrequests has been introduced * as Google has/may refuse serving them if they exceed allowances, published as * 4 concurrent requests max * 10 req. per sec max * */ protected function _getData() { // need config, to know which data user wants to display : visits, unique visitors, pageviews $sefConfig = Sh404sefFactory::getConfig(); // set headers required by Google Analytics //$headers = array( // 'GData-Version' => 2 // , 'Authorization' => 'GoogleLogin auth=' . $this->_Auth //); $headers = array('GData-Version' => 2, 'Authorization' => 'Bearer ' . Sh404sefHelperAnalytics_auth::getAccessToken()); $this->_client->setHeaders($headers); ShlSystem_Log::debug('sh404sef', '%s::%d: %s', __METHOD__, __LINE__, 'Analytics: preparing to fetch data with headers: ' . print_r($headers, true)); // 0 . Global data, needed in all queries to calculate % // build uri as needed to retrieve required data $query = array('dimensions' => '' , 'metrics' => 'ga:pageviews,ga:sessions,ga:users,ga:bounces,ga:entrances,ga:sessionDuration,ga:newUsers' , 'start-date' => $this->_options['startDate'] , 'end-date' => $this->_options['endDate'] ); // use method to build the correct url $uri = $this->_buildQueryUri($query); ShlSystem_Log::debug('sh404sef', '%s::%d: %s', __METHOD__, __LINE__, 'Analytics: preparing to fetch data with query: ' . print_r($query, true)); // set target API url $this->_client->setUri($uri); // perform query $this->_query('global'); // 0bis . Global social data, needed in all queries to calculate % // build uri as needed to retrieve required data $query = array('dimensions' => 'ga:eventCategory,ga:eventAction' , 'metrics' => 'ga:totalEvents,ga:sessions' , 'start-date' => $this->_options['startDate'] , 'end-date' => $this->_options['endDate'] ); // use method to build the correct url $uri = $this->_buildQueryUri($query); // set target API url $this->_client->setUri($uri); // perform query $this->_query('globalSocial'); // specific queries for each subrequest switch ($this->_options['subrequest']) { case 'visits': // 1 . query pageviews and visits // build uri as needed to retrieve required data $query = array('dimensions' => $this->_options['groupBy'] , 'metrics' => $sefConfig->analyticsDashboardDataType , 'start-date' => $this->_options['startDate'] , 'end-date' => $this->_options['endDate'] ); // use method to build the correct url $uri = $this->_buildQueryUri($query); // set target API url $this->_client->setUri($uri); // perform query $this->_query('visits'); break; case 'sources': // 2 . traffic sources // Google does not allow combining dimension=ga:medium with metric = unique visitors $metric = $sefConfig->analyticsDashboardDataType == 'ga:users' ? 'ga:sessions' : $sefConfig->analyticsDashboardDataType; // build uri as needed to retrieve required data $query = array('dimensions' => 'ga:medium' , 'metrics' => $metric , 'start-date' => $this->_options['startDate'] , 'end-date' => $this->_options['endDate'] ); // use method to build the correct url $uri = $this->_buildQueryUri($query); // set target API url $this->_client->setUri($uri); // perform query $this->_query('sources'); break; case 'perf': // 4 . Custom var : logged in users // build uri as needed to retrieve required data $query = array('dimensions' => 'ga:customVarValue' . SH404SEF_ANALYTICS_USER_CUSTOM_VAR , 'metrics' => 'ga:pageviews' , 'sort' => '-ga:pageviews' , 'max-results' => '100' , 'start-date' => $this->_options['startDate'] , 'end-date' => $this->_options['endDate'] ); // use method to build the correct url $uri = $this->_buildQueryUri($query); // set target API url $this->_client->setUri($uri); // perform query $this->_query('logged-in-users'); // 5 . Custom var : page creation time // build uri as needed to retrieve required data $query = array('dimensions' => 'ga:customVarValue' . SH404SEF_ANALYTICS_TIME_CUSTOM_VAR , 'metrics' => 'ga:pageviews' , 'sort' => '-ga:pageviews' , 'max-results' => '512' , 'start-date' => $this->_options['startDate'] , 'end-date' => $this->_options['endDate'] ); // use method to build the correct url $uri = $this->_buildQueryUri($query); // set target API url $this->_client->setUri($uri); // perform query $this->_query('page-creation-time'); break; case 'top5urls': // 6 . Top 5 urls // build uri as needed to retrieve required data $query = array('dimensions' => 'ga:pagePath' , 'metrics' => 'ga:pageviews,ga:timeOnPage' , 'sort' => '-ga:pageviews' , 'max-results' => $this->_options['max-top-urls'] , 'start-date' => $this->_options['startDate'] , 'end-date' => $this->_options['endDate'] ); // use method to build the correct url $uri = $this->_buildQueryUri($query); // set target API url $this->_client->setUri($uri); // perform query $this->_query('top5urls'); break; case 'top5referrers': // 7 . Top 5 referrer // Google does not allow combining dimension=ga:medium with metric = unique visitors $metric = $sefConfig->analyticsDashboardDataType == 'ga:users' ? 'ga:sessions' : $sefConfig->analyticsDashboardDataType; // build uri as needed to retrieve required data $query = array('dimensions' => 'ga:source,ga:referralPath' , 'metrics' => $metric . ',ga:sessionDuration,ga:bounces,ga:newUsers' , 'sort' => '-' . $metric , 'max-results' => $this->_options['max-top-referrers'] , 'start-date' => $this->_options['startDate'] , 'end-date' => $this->_options['endDate'] ); // use method to build the correct url $uri = $this->_buildQueryUri($query); // set target API url $this->_client->setUri($uri); // perform query $this->_query('top5referrers'); break; case 'topsocialfb': // 8 . topSocial // build uri as needed to retrieve required data $query = array('dimensions' => 'ga:eventCategory,ga:eventLabel,ga:eventAction' , 'metrics' => 'ga:totalEvents,ga:pageviews,ga:timeOnPage' , 'sort' => '-' . 'ga:totalEvents' , 'filters' => 'ga:eventCategory==sh404SEF_social_tracker_facebook' , 'max-results' => $this->_options['max-top-referrers'] , 'start-date' => $this->_options['startDate'] , 'end-date' => $this->_options['endDate'] ); // use method to build the correct url $uri = $this->_buildQueryUri($query); // set target API url $this->_client->setUri($uri); // perform query $this->_query('topSocialFB'); break; case 'topsocialtweeter': // 8 . topSocial // build uri as needed to retrieve required data $query = array('dimensions' => 'ga:eventCategory,ga:eventLabel,ga:eventAction' , 'metrics' => 'ga:totalEvents,ga:pageviews,ga:timeOnPage' , 'sort' => '-' . 'ga:totalEvents' , 'filters' => 'ga:eventCategory==sh404SEF_social_tracker_tweeter' , 'max-results' => $this->_options['max-top-referrers'] , 'start-date' => $this->_options['startDate'] , 'end-date' => $this->_options['endDate'] ); // use method to build the correct url $uri = $this->_buildQueryUri($query); // set target API url $this->_client->setUri($uri); // perform query $this->_query('topSocialTweeter'); break; case 'topsocialpinterest': // 8 . topSocial // build uri as needed to retrieve required data $query = array('dimensions' => 'ga:eventCategory,ga:eventLabel,ga:eventAction' , 'metrics' => 'ga:totalEvents,ga:pageviews,ga:timeOnPage' , 'sort' => '-' . 'ga:totalEvents' , 'filters' => 'ga:eventCategory==sh404SEF_social_tracker_pinterest' , 'max-results' => $this->_options['max-top-referrers'] , 'start-date' => $this->_options['startDate'] , 'end-date' => $this->_options['endDate'] ); // use method to build the correct url $uri = $this->_buildQueryUri($query); // set target API url $this->_client->setUri($uri); // perform query $this->_query('topSocialPinterest'); break; case 'topsocialplusone': // 8 . topSocial // build uri as needed to retrieve required data $query = array('dimensions' => 'ga:eventCategory,ga:eventLabel,ga:eventAction' , 'metrics' => 'ga:totalEvents,ga:pageviews,ga:timeOnPage' , 'sort' => '-' . 'ga:totalEvents' , 'filters' => 'ga:eventCategory==sh404SEF_social_tracker_gplus' , 'max-results' => $this->_options['max-top-referrers'] , 'start-date' => $this->_options['startDate'] , 'end-date' => $this->_options['endDate'] ); // use method to build the correct url $uri = $this->_buildQueryUri($query); // set target API url $this->_client->setUri($uri); // perform query $this->_query('topSocialPlusOne'); break; case 'topsocialplusonepage': // 8 . topSocial // build uri as needed to retrieve required data $query = array('dimensions' => 'ga:eventCategory,ga:eventLabel,ga:eventAction' , 'metrics' => 'ga:totalEvents,ga:pageviews,ga:timeOnPage' , 'sort' => '-' . 'ga:totalEvents' , 'filters' => 'ga:eventCategory==sh404SEF_social_tracker_gplus_page' , 'max-results' => $this->_options['max-top-referrers'] , 'start-date' => $this->_options['startDate'] , 'end-date' => $this->_options['endDate'] ); // use method to build the correct url $uri = $this->_buildQueryUri($query); // set target API url $this->_client->setUri($uri); // perform query $this->_query('topSocialPlusOnePage'); break; } } protected function _addFiguresVisits() { if (!empty($this->_rawData['global'][0])) { $this->_formatedResponse->global = $this->_rawData['global'][0]; $this->_formatedResponse->global->bounceRate = empty($this->_formatedResponse->global->entrances) ? 0 : $this->_formatedResponse->global->bounces / $this->_formatedResponse->global->entrances; $this->_formatedResponse->global->avgTimeOnSite = empty($this->_formatedResponse->global->sessions) ? 0 : $this->_formatedResponse->global->sessionDuration / $this->_formatedResponse->global->sessions; $this->_formatedResponse->global->newVisitsPerCent = empty($this->_formatedResponse->global->sessions) ? 0 : $this->_formatedResponse->global->newUsers / $this->_formatedResponse->global->sessions; $this->_formatedResponse->global->pagesPerVisit = empty($this->_formatedResponse->global->sessions) ? 0 : $this->_formatedResponse->global->pageviews / $this->_formatedResponse->global->sessions; } else { $this->_formatedResponse->global = new stdClass(); $this->_formatedResponse->global->sessions = 0; $this->_formatedResponse->global->users = 0; $this->_formatedResponse->global->pageviews = 0; $this->_formatedResponse->global->bounceRate = 0; $this->_formatedResponse->global->avgTimeOnSite = 0; $this->_formatedResponse->global->newVisitsPerCent = 0; $this->_formatedResponse->global->pagesPerVisit = 0; } // add social engagement global values $this->_formatedResponse->global->totalSocialEvents = 0; $this->_formatedResponse->global->sh404SEF_social_tracker_facebook = 0; $this->_formatedResponse->global->sh404SEF_social_tracker_gplus = 0; $this->_formatedResponse->global->sh404SEF_social_tracker_tweeter = 0; $this->_formatedResponse->global->sh404SEF_social_tracker_pinterest = 0; $this->_formatedResponse->global->sh404SEF_social_tracker_gplus_page = 0; $this->_formatedResponse->global->visitsWithSocialEngagement = 0; if (!empty($this->_rawData['globalSocial'])) { foreach ($this->_rawData['globalSocial'] as $entry) { $cat = $entry->dimension['eventCategory']; $action = $entry->dimension['eventAction']; switch ($entry->dimension['eventCategory']) { case 'sh404SEF_social_tracker_facebook': $this->_formatedResponse->global->visitsWithSocialEngagement += $entry->sessions; if ($action == 'like') { $this->_formatedResponse->global->totalSocialEvents += $entry->totalEvents; $this->_formatedResponse->global->$cat += $entry->totalEvents; } else { $this->_formatedResponse->global->totalSocialEvents -= $entry->totalEvents; $this->_formatedResponse->global->$cat -= $entry->totalEvents; } break; case 'sh404SEF_social_tracker_gplus': $this->_formatedResponse->global->visitsWithSocialEngagement += $entry->sessions; if ($action == 'on') { $this->_formatedResponse->global->totalSocialEvents += $entry->totalEvents; $this->_formatedResponse->global->$cat += $entry->totalEvents; } else { $this->_formatedResponse->global->totalSocialEvents -= $entry->totalEvents; $this->_formatedResponse->global->$cat -= $entry->totalEvents; } break; case 'sh404SEF_social_tracker_tweeter': case 'sh404SEF_social_tracker_pinterest': case 'sh404SEF_social_tracker_gplus_page': $this->_formatedResponse->global->visitsWithSocialEngagement += $entry->sessions; $this->_formatedResponse->global->totalSocialEvents += $entry->totalEvents; $this->_formatedResponse->global->$cat += $entry->totalEvents; break; } } $this->_formatedResponse->global->visitsWithSocialEngagement = empty($this->_formatedResponse->global->sessions) ? 0 : $this->_formatedResponse->global->visitsWithSocialEngagement / $this->_formatedResponse->global->sessions; } } protected function _addFiguresUrls() { $this->_formatedResponse->top5urls = array(); foreach ($this->_rawData['top5urls'] as $entry) { $entry->avgTimeOnPage = empty($entry->pageviews) ? 0 : $entry->timeOnPage / $entry->pageviews; $entry->pageviewsPerCent = empty($this->_formatedResponse->global->pageviews) ? 0 : $entry->pageviews / $this->_formatedResponse->global->pageviews; $this->_formatedResponse->top5urls[] = $entry; } } protected function _addFiguresSocialFB() { $this->_formatedResponse->topSocialFB = array(); foreach ($this->_rawData['topSocialFB'] as $entry) { $entry->avgTimeOnPage = empty($entry->pageviews) ? 0 : $entry->timeOnPage / $entry->pageviews; $entry->eventsPerCent = empty($this->_formatedResponse->global->totalSocialEvents) ? 0 : $entry->totalEvents / $this->_formatedResponse->global->totalSocialEvents; $this->_formatedResponse->topSocialFB[] = $entry; } } protected function _addFiguresSocialTweeter() { $this->_formatedResponse->topSocialTweeter = array(); foreach ($this->_rawData['topSocialTweeter'] as $entry) { $entry->avgTimeOnPage = empty($entry->pageviews) ? 0 : $entry->timeOnPage / $entry->pageviews; $entry->eventsPerCent = empty($this->_formatedResponse->global->totalSocialEvents) ? 0 : $entry->totalEvents / $this->_formatedResponse->global->totalSocialEvents; $this->_formatedResponse->topSocialTweeter[] = $entry; } } protected function _addFiguresSocialPinterest() { $this->_formatedResponse->topSocialPinterest = array(); foreach ($this->_rawData['topSocialPinterest'] as $entry) { $entry->avgTimeOnPage = empty($entry->pageviews) ? 0 : $entry->timeOnPage / $entry->pageviews; $entry->eventsPerCent = empty($this->_formatedResponse->global->totalSocialEvents) ? 0 : $entry->totalEvents / $this->_formatedResponse->global->totalSocialEvents; $this->_formatedResponse->topSocialPinterest[] = $entry; } } protected function _addFiguresSocialPlusOne() { $this->_formatedResponse->topSocialPlusOne = array(); foreach ($this->_rawData['topSocialPlusOne'] as $entry) { $entry->avgTimeOnPage = empty($entry->pageviews) ? 0 : $entry->timeOnPage / $entry->pageviews; $entry->eventsPerCent = empty($this->_formatedResponse->global->totalSocialEvents) ? 0 : $entry->totalEvents / $this->_formatedResponse->global->totalSocialEvents; $this->_formatedResponse->topSocialPlusOne[] = $entry; } } protected function _addFiguresSocialPlusOnePage() { $this->_formatedResponse->topSocialPlusOnePage = array(); foreach ($this->_rawData['topSocialPlusOnePage'] as $entry) { $entry->avgTimeOnPage = empty($entry->pageviews) ? 0 : $entry->timeOnPage / $entry->pageviews; $entry->eventsPerCent = empty($this->_formatedResponse->global->totalSocialEvents) ? 0 : $entry->totalEvents / $this->_formatedResponse->global->totalSocialEvents; $this->_formatedResponse->topSocialPlusOnePage[] = $entry; } } protected function _addFiguresReferrers() { // need config, to know which data user wants to display : visits, unique visitors, pageviews $sefConfig = Sh404sefFactory::getConfig(); // Google does not allow combining dimension=ga:medium with metric = unique visitors $metric = $sefConfig->analyticsDashboardDataType == 'ga:users' ? 'ga:sessions' : $sefConfig->analyticsDashboardDataType; $dataTypeString = str_replace('ga:', '', $metric); $this->_formatedResponse->top5referrers = array(); foreach ($this->_rawData['top5referrers'] as $entry) { $entry->views = $entry->$dataTypeString; $entry->avgTimeOnSite = empty($entry->$dataTypeString) ? 0 : $entry->sessionDuration / $entry->$dataTypeString; $entry->bounceRate = empty($entry->$dataTypeString) ? 0 : $entry->bounces / $entry->$dataTypeString; $entry->viewsPerCent = empty($this->_formatedResponse->global->$dataTypeString) ? 0 : $entry->$dataTypeString / $this->_formatedResponse->global->$dataTypeString; $this->_formatedResponse->top5referrers[] = $entry; } } protected function _addCustomVars() { $this->_formatedResponse->perf = new stdClass(); // prepare time and memory calculation $field = 'customVarValue' . SH404SEF_ANALYTICS_TIME_CUSTOM_VAR; $totalPages = 0; $totalTime = 0; $totalMemory = 0; // now iterate over time and memory records foreach ($this->_rawData['page-creation-time'] as $entry) { $record = intval($entry->dimension[$field]); // make it an int, this will remove dev data that had decimals // sanitize $record = $record < 3 ? 0 : $record; // we only calculate averaged on pages we have time and ram data for if (!empty($record)) { // separate time and memory $time = $record >> 4; // bits 5 and up $memory = $record & 15; // bits 1 to 4 // aggregate $totalPages += $entry->pageviews; $totalTime += Sh404sefHelperAnalytics::declassifyTime($time) * $entry->pageviews; $totalMemory += Sh404sefHelperAnalytics::declassifyMemory($memory) * $entry->pageviews; } } // calculate averages time and ram now $this->_formatedResponse->perf->avgPageCreationTime = empty($totalPages) ? 0 : $totalTime / $totalPages; $this->_formatedResponse->perf->avgMemoryUsed = empty($totalPages) ? 0 : $totalMemory / $totalPages; // logged in users handling $field = 'customVarValue' . SH404SEF_ANALYTICS_USER_CUSTOM_VAR; $groups = Sh404sefHelperGeneral::getUserGroups('title'); $loggedInPages = 0; foreach ($this->_rawData['logged-in-users'] as $entry) { $userType = urldecode($entry->dimension[$field]); // if user was logged in, any group, including "Public Frontend" if (in_array($userType, $groups)) { // include # of pages $loggedInPages += $entry->pageviews; } } $this->_formatedResponse->perf->loggedInUserRate = empty($this->_formatedResponse->global->pageviews) ? 0 : $loggedInPages / $this->_formatedResponse->global->pageviews; } protected function _createVisitsGraph() { // load pChart graphic library $this->_loadLibs(); // définition des données à afficher $dataSet = new pData(); // need config, to know which data user wants to display : visits, unique visitors, pageviews $sefConfig = Sh404sefFactory::getConfig(); $dataTypeString = str_replace('ga:', '', $sefConfig->analyticsDashboardDataType); // get data from response $data = array(); $maxY = 0; foreach ($this->_rawData['visits'] as $entry) { $data[] = $entry->$dataTypeString; if ($entry->$dataTypeString > $maxY) { $maxY = $entry->$dataTypeString; } } // format dates $dates = Sh404sefHelperAnalytics::formatAbciseDates($this->_rawData['visits'], $this->_options); $dataSet->AddPoint($data, $dataTypeString); $dataSet->AddPoint($dates, "dates"); $dataSet->addSerie($dataTypeString); $dataSet->SetAbsciseLabelSerie("dates"); $label = Sh404sefHelperAnalytics::getDataTypeTitleLabel($dataTypeString); $dataSet->SetSerieName($label, $dataTypeString); // Initialise the graph $w = $this->_options['cpWidth']; $w = empty($w) ? 400 : intval($w - 40); $h = 225; $chart = new pChart($w, $h); $fontSize = 8; // calculate left margin based on max value to display $leftMargin = 20 + $fontSize + 20 + $fontSize * strlen($maxY); $bottomMargin = 5 + $fontSize * 6; switch ($this->_options['groupBy']) { case 'ga:year,ga:month,ga:week,ga:day': $YAxisName = JText::_('Day'); break; // date string represents a week number case 'ga:year,ga:month,ga:week': $YAxisName = JText::_('Week'); break; case 'ga:year,ga:month': $YAxisName = JText::_('Month'); break; } $dataSet->SetYAxisName($label . JText::_('COM_SH404SEF_ANALYTICS_REPORT_PER_LABEL') . $YAxisName); $chart->setFontProperties($this->_baseChartPath . '/Fonts/arial.ttf', $fontSize); $chart->setGraphArea($leftMargin, 30, $w - 20, $h - $bottomMargin); $chart->drawFilledRoundedRectangle(7, 7, $w - 7, $h - 7, 5, 240, 240, 240); $chart->drawRoundedRectangle(5, 5, $w - 5, $h - 5, 5, 230, 230, 230); $chart->drawGraphArea(255, 255, 255, TRUE); $d = $dataSet->GetData(); if (!empty($d)) { $chart->drawScale($d, $dataSet->GetDataDescription(), SCALE_NORMAL, 150, 150, 150, TRUE, 60, 0, false); $chart->drawGrid(4, TRUE, 230, 230, 230, 50); // Draw the 0 line $chart->setFontProperties($this->_baseChartPath . '/Fonts/arial.ttf', $fontSize); $chart->drawTreshold(0, 143, 55, 72, TRUE, TRUE); // Draw the line graph $chart->drawLineGraph($d, $dataSet->GetDataDescription()); $chart->drawPlotGraph($d, $dataSet->GetDataDescription(), 3, 2, 255, 255, 255); } // create a temporary file for $user = JFactory::getUser(); // make sure the root tmp dir exists Sh404sefHelperFiles::createDirAndIndex(JPATH_ROOT . '/' . $this->_tmpImagesPath); // file name is per user $basePath = $this->_tmpImagesPath . '/' . md5('useless_' . $user->id . '_hashing') . '/'; // create path and make sure there's an index.html file in it Sh404sefHelperFiles::createDirAndIndex(JPATH_ROOT . '/' . $basePath); $imageFileName = Sh404sefHelperAnalytics::createTempFile($basePath, $this->_options['report'] . '.' . $this->_options['accountId'] . '.visits.' . $dataTypeString); $chart->Render(JPATH_ROOT . '/' . $imageFileName); // need cleaning up, so let's remove all report files for that user older than an hour or so Sh404sefHelperAnalytics::cleanReportsImageFiles(JPATH_ROOT . '/' . $basePath, $age = 4000); return JURI::root() . $imageFileName; } protected function _createSourcesGraph() { // load pChart graphic library $this->_loadLibs(); // définition des données à afficher $dataSet = new pData(); // need config, to know which data user wants to display : visits, unique visitors, pageviews $sefConfig = Sh404sefFactory::getConfig(); // Google does not allow combining dimension=ga:medium with metric = unique visitors $dataType = $sefConfig->analyticsDashboardDataType == 'ga:users' ? 'ga:sessions' : $sefConfig->analyticsDashboardDataType; $dataTypeString = str_replace('ga:', '', $dataType); // sort data for proper display usort($this->_rawData['sources'], array($this, '_sortSourcesDataCompareFunction')); // we walk the array, pulling out alternatively // the first and last items // making the new array having the largest item // followed by the smallest, then second largest // then second smallest, ... // which makes drawing labels much easier $tmpArray = array(); $even = false; $max = count($this->_rawData['sources']); for ($i = 0; $i < $max; $i++) { if ($even) { // pull last item in sorted array $tmpArray[] = array_pop($this->_rawData['sources']); } else { // pull array first item $tmpArray[] = array_shift($this->_rawData['sources']); } // flag inversion $even = !$even; }; // get data from response $data = array(); $types = array(); foreach ($tmpArray as $entry) { $value = $entry->$dataTypeString; // do not add empty values, as pChart would choke on that and display a warning if (!empty($value)) { $data[] = $value; $types[] = Sh404sefHelperAnalytics::getReferralLabel($entry->dimension['medium']); } } $dataSet->AddPoint($data, "visits"); $dataSet->AddPoint($types, "types"); $dataSet->addSerie('visits'); $dataSet->SetAbsciseLabelSerie("types"); $label = JText::_('COM_SH404SEF_ANALYTICS_REPORT_SOURCES') . JText::_('COM_SH404SEF_ANALYTICS_REPORT_BY_LABEL') . Sh404sefHelperAnalytics::getDataTypeTitle(); $dataSet->SetSerieName($label, "visits"); // Initialise the graph $w = intval(0.45 * $this->_options['cpWidth']); $w = empty($w) ? 160 : $w; $radius = intval($w * 0.22); $margin = 5; $h = intval($w * 0.8); $centreX = intval(0.50 * $w); $centreY = intval(0.50 * $h); $chart = new pChart($w, $h); $fontSize = 8; // prepare graph $chart->setFontProperties($this->_baseChartPath . '/' . 'Fonts/arial.ttf', $fontSize); $chart->loadColorPalette($this->_baseChartPath . '/' . 'palettes' . '/' . 'tones-2-green-soft.php'); $chart->setGraphArea($margin, $margin, $w - $margin, $h - $margin); // This will draw a shadow under the pie chart $chart->drawFilledCircle($centreX + 4, $centreY + 4, $radius, 200, 200, 200); // Draw the pie chart $d = $dataSet->GetData(); if (!empty($d)) { $chart->drawBasicPieGraph($d, $dataSet->GetDataDescription(), $centreX, $centreY, $radius, PIE_PERCENTAGE_LABEL_VALUE, 255, 255, 218); } // create a temporary file for $user = JFactory::getUser(); // make sure the root tmp dir exists Sh404sefHelperFiles::createDirAndIndex(JPATH_ROOT . '/' . $this->_tmpImagesPath); // file name is variable to avoid browser cache $basePath = $this->_tmpImagesPath . '/' . md5('useless_' . $user->id . '_hashing') . '/'; // create path and make sure there's an index.html file in it Sh404sefHelperFiles::createDirAndIndex(JPATH_ROOT . '/' . $basePath); $imageFileName = Sh404sefHelperAnalytics::createTempFile($basePath, $this->_options['report'] . '.' . $this->_options['accountId'] . '.sources.' . $dataTypeString); $chart->Render(JPATH_ROOT . '/' . $imageFileName); // need cleaning up, so let's remove all report files for that user older than an hour or so Sh404sefHelperAnalytics::cleanReportsImageFiles(JPATH_ROOT . '/' . $basePath, $age = 4000); return JURI::root() . $imageFileName; } /** * Builds an URI suitable to query Google Analytics API * * @param array $query a set of query constraints (ie: dimensions, metrics, etc) */ protected function _buildQueryUri($query) { // default values $defaultQuery = array( 'ids' => 'ga:' . $this->_options['accountId'] , 'dimensions' => array() , 'metrics' => array() , 'sort' => null , 'filters' => null , 'segment' => null , 'start-date' => $this->_options['startDate'] , 'end-date' => $this->_options['endDate'] , 'start-index' => null , 'max-results' => $this->_options['max-results'] ); // combine with request $query = array_merge($defaultQuery, $query); // combine them into a uri $uri = ''; foreach ($query as $key => $value) { if (!is_null($value)) { $uri .= '&' . $key . '=' . $value; } } // prepare dimensions $uri = $this->_endPoint . 'data?' . ltrim($uri, '&'); return $uri; } protected function _query($type) { //perform request $response = $this->_client->request(); ShlSystem_Log::debug('sh404sef', '%s::%d: %s', __METHOD__, __LINE__, 'Analytics: raw query response for data: ' . print_r($response, true)); $body = $response->getBody(); ShlSystem_Log::debug('sh404sef', '%s::%d: %s', __METHOD__, __LINE__, 'Analytics: raw query response for data: body = ' . print_r($body, true)); // check if authentified Sh404sefHelperAnalytics::verifyAuthResponse($response); // analyze response $parser = new DOMDocument(); $parser->loadXML($response->getBody()); $this->_rawData[$type] = $parser->getElementsByTagName('entry'); $this->_decodeXml($type); } protected function _decodeXml($type) { $data = array(); foreach ($this->_rawData[$type] as $entry) { $r = new StdClass(); $dimensions = $entry->getElementsByTagName('dimension'); foreach ($dimensions as $dimension) { $name = $dimension->getAttribute('name'); $name = str_replace('ga:', '', $name); $r->dimension[$name] = $dimension->getAttribute('value'); } $metrics = $entry->getElementsByTagName('metric'); foreach ($metrics as $metric) { $name = $metric->getAttribute('name'); $name = str_replace('ga:', '', $name); $r->$name = $metric->getAttribute('value'); } $data[] = clone($r); } $this->_rawData[$type] = $data; } private function _loadLibs() { // include pChart pChart require_once($this->_baseChartPath . '/' . 'pChart' . '/' . 'pData.class.php'); require_once($this->_baseChartPath . '/' . 'pChart' . '/' . 'pChart.class.php'); } /** * NUmerical sort function * * @param $first * @param $second * * @return unknown_type */ protected function _sortSourcesDataCompareFunction($first, $second) { // need config, to know which data user wants to display : visits, unique visitors, pageviews $sefConfig = Sh404sefFactory::getConfig(); // Google does not allow combining dimension=ga:medium with metric = unique visitors $metric = $sefConfig->analyticsDashboardDataType == 'ga:users' ? 'ga:sessions' : $sefConfig->analyticsDashboardDataType; $dataTypeString = str_replace('ga:', '', $metric); if ($first->$dataTypeString == $second->$dataTypeString) { return 0; } return $first->$dataTypeString > $second->$dataTypeString ? +1 : -1; } }