Пример #1
0
 /** Render the area left of the iframe */
 public function renderSidebar()
 {
     $idSite = Common::getRequestVar('idSite');
     $period = Common::getRequestVar('period');
     $date = Common::getRequestVar('date');
     $currentUrl = Common::getRequestVar('currentUrl');
     $currentUrl = Common::unsanitizeInputValue($currentUrl);
     $normalizedCurrentUrl = PageUrl::excludeQueryParametersFromUrl($currentUrl, $idSite);
     $normalizedCurrentUrl = Common::unsanitizeInputValue($normalizedCurrentUrl);
     // load the appropriate row of the page urls report using the label filter
     ArchivingHelper::reloadConfig();
     $path = ArchivingHelper::getActionExplodedNames($normalizedCurrentUrl, Action::TYPE_PAGE_URL);
     $path = array_map('urlencode', $path);
     $label = implode('>', $path);
     $request = new Request('method=Actions.getPageUrls' . '&idSite=' . urlencode($idSite) . '&date=' . urlencode($date) . '&period=' . urlencode($period) . '&label=' . urlencode($label) . '&format=original');
     $dataTable = $request->process();
     $data = array();
     if ($dataTable->getRowsCount() > 0) {
         $row = $dataTable->getFirstRow();
         $translations = Metrics::getDefaultMetricTranslations();
         $showMetrics = array('nb_hits', 'nb_visits', 'nb_users', 'nb_uniq_visitors', 'bounce_rate', 'exit_rate', 'avg_time_on_page');
         foreach ($showMetrics as $metric) {
             $value = $row->getColumn($metric);
             if ($value === false) {
                 // skip unique visitors for period != day
                 continue;
             }
             if ($metric == 'avg_time_on_page') {
                 $value = MetricsFormatter::getPrettyTimeFromSeconds($value);
             }
             $data[] = array('name' => $translations[$metric], 'value' => $value);
         }
     }
     // generate page url string
     foreach ($path as &$part) {
         $part = preg_replace(';^/;', '', urldecode($part));
     }
     $page = '/' . implode('/', $path);
     $page = preg_replace(';/index$;', '/', $page);
     if ($page == '/') {
         $page = '/index';
     }
     // render template
     $view = new View('@Overlay/renderSidebar');
     $view->data = $data;
     $view->location = $page;
     $view->normalizedUrl = $normalizedCurrentUrl;
     $view->label = $label;
     $view->idSite = $idSite;
     $view->period = $period;
     $view->date = $date;
     $this->outputCORSHeaders();
     return $view->render();
 }
Пример #2
0
 public function getSimpleLastVisitCount()
 {
     $lastMinutes = Config::getInstance()->General[self::SIMPLE_VISIT_COUNT_WIDGET_LAST_MINUTES_CONFIG_KEY];
     $lastNData = Request::processRequest('Live.getCounters', array('lastMinutes' => $lastMinutes));
     $view = new View('@Live/getSimpleLastVisitCount');
     $view->lastMinutes = $lastMinutes;
     $view->visitors = MetricsFormatter::getPrettyNumber($lastNData[0]['visitors']);
     $view->visits = MetricsFormatter::getPrettyNumber($lastNData[0]['visits']);
     $view->actions = MetricsFormatter::getPrettyNumber($lastNData[0]['actions']);
     $view->refreshAfterXSecs = Config::getInstance()->General['live_widget_refresh_after_seconds'];
     $view->translations = array('one_visitor' => Piwik::translate('Live_NbVisitor'), 'visitors' => Piwik::translate('Live_NbVisitors'), 'one_visit' => Piwik::translate('General_OneVisit'), 'visits' => Piwik::translate('General_NVisits'), 'one_action' => Piwik::translate('General_OneAction'), 'actions' => Piwik::translate('VisitsSummary_NbActionsDescription'), 'one_minute' => Piwik::translate('General_OneMinute'), 'minutes' => Piwik::translate('General_NMinutes'));
     return $this->render($view);
 }
Пример #3
0
 /**
  * Returns the index for this plugin. Shows every other report defined by this plugin,
  * except the '...ByYear' reports. These can be loaded as related reports.
  *
  * Also, the 'getIndividual...Summary' reports are loaded by AJAX, as they can take
  * a significant amount of time to load on setups w/ lots of websites.
  */
 public function index()
 {
     Piwik::checkUserIsSuperUser();
     $view = new View('@DBStats/index');
     $this->setBasicVariablesView($view);
     $view->databaseUsageSummary = $this->getDatabaseUsageSummary(true);
     $view->trackerDataSummary = $this->getTrackerDataSummary(true);
     $view->metricDataSummary = $this->getMetricDataSummary(true);
     $view->reportDataSummary = $this->getReportDataSummary(true);
     $view->adminDataSummary = $this->getAdminDataSummary(true);
     list($siteCount, $userCount, $totalSpaceUsed) = API::getInstance()->getGeneralInformation();
     $view->siteCount = MetricsFormatter::getPrettyNumber($siteCount);
     $view->userCount = MetricsFormatter::getPrettyNumber($userCount);
     $view->totalSpaceUsed = MetricsFormatter::getPrettySizeFromBytes($totalSpaceUsed);
     return $view->render();
 }
Пример #4
0
 private function setShowGoalsColumnsProperties()
 {
     // set view properties based on goal requested
     $idSite = Common::getRequestVar('idSite', null, 'int');
     $idGoal = Common::getRequestVar('idGoal', AddColumnsProcessedMetricsGoal::GOALS_OVERVIEW, 'string');
     if (Piwik::LABEL_ID_GOAL_IS_ECOMMERCE_ORDER == $idGoal) {
         $this->setPropertiesForEcommerceView();
     } else {
         if (AddColumnsProcessedMetricsGoal::GOALS_FULL_TABLE == $idGoal) {
             $this->setPropertiesForGoals($idSite, 'all');
         } else {
             if (AddColumnsProcessedMetricsGoal::GOALS_OVERVIEW == $idGoal) {
                 $this->setPropertiesForGoalsOverview($idSite);
             } else {
                 $this->setPropertiesForGoals($idSite, array($idGoal));
             }
         }
     }
     // add goals columns
     $this->config->filters[] = array('AddColumnsProcessedMetricsGoal', array($ignore = true, $idGoal), $priority = true);
     // prettify columns
     $setRatePercent = function ($rate, $thang = false) {
         return $rate == 0 ? "0%" : $rate;
     };
     foreach ($this->config->columns_to_display as $columnName) {
         if (false !== strpos($columnName, 'conversion_rate')) {
             $this->config->filters[] = array('ColumnCallbackReplace', array($columnName, $setRatePercent));
         }
     }
     $formatPercent = function ($value) use($idSite) {
         return MetricsFormatter::getPrettyMoney(sprintf("%.1f", $value), $idSite);
     };
     foreach ($this->config->columns_to_display as $columnName) {
         if ($this->isRevenueColumn($columnName)) {
             $this->config->filters[] = array('ColumnCallbackReplace', array($columnName, $formatPercent));
         }
     }
     // this ensures that the value is set to zero for all rows where the value was not set (no conversion)
     $identityFunction = function ($value) {
         return $value;
     };
     foreach ($this->config->columns_to_display as $columnName) {
         if (!$this->isRevenueColumn($columnName)) {
             $this->config->filters[] = array('ColumnCallbackReplace', array($columnName, $identityFunction));
         }
     }
 }
Пример #5
0
 protected function addPageDisplayProperties(ViewDataTable $view)
 {
     $view->config->addTranslations(array('nb_hits' => Piwik::translate('General_ColumnPageviews'), 'nb_visits' => Piwik::translate('General_ColumnUniquePageviews'), 'avg_time_on_page' => Piwik::translate('General_ColumnAverageTimeOnPage'), 'bounce_rate' => Piwik::translate('General_ColumnBounceRate'), 'exit_rate' => Piwik::translate('General_ColumnExitRate'), 'avg_time_generation' => Piwik::translate('General_ColumnAverageGenerationTime')));
     // prettify avg_time_on_page column
     $getPrettyTimeFromSeconds = '\\Piwik\\MetricsFormatter::getPrettyTimeFromSeconds';
     $view->config->filters[] = array('ColumnCallbackReplace', array('avg_time_on_page', $getPrettyTimeFromSeconds));
     // prettify avg_time_generation column
     $avgTimeCallback = function ($time) {
         return $time ? MetricsFormatter::getPrettyTimeFromSeconds($time, true, true, false) : "-";
     };
     $view->config->filters[] = array('ColumnCallbackReplace', array('avg_time_generation', $avgTimeCallback));
     // add avg_generation_time tooltip
     $tooltipCallback = function ($hits, $min, $max) {
         if (!$hits) {
             return false;
         }
         return Piwik::translate("Actions_AvgGenerationTimeTooltip", array($hits, "<br />", MetricsFormatter::getPrettyTimeFromSeconds($min), MetricsFormatter::getPrettyTimeFromSeconds($max)));
     };
     $view->config->filters[] = array('ColumnCallbackAddMetadata', array(array('nb_hits_with_time_generation', 'min_time_generation', 'max_time_generation'), 'avg_time_generation_tooltip', $tooltipCallback));
     $this->addExcludeLowPopDisplayProperties($view);
 }
Пример #6
0
 /**
  * @param $visitorDetailsArray
  * @param $actionsLimit
  * @param $timezone
  * @return array
  */
 public static function enrichVisitorArrayWithActions($visitorDetailsArray, $actionsLimit, $timezone)
 {
     $idVisit = $visitorDetailsArray['idVisit'];
     $maxCustomVariables = CustomVariables::getMaxCustomVariables();
     $sqlCustomVariables = '';
     for ($i = 1; $i <= $maxCustomVariables; $i++) {
         $sqlCustomVariables .= ', custom_var_k' . $i . ', custom_var_v' . $i;
     }
     // The second join is a LEFT join to allow returning records that don't have a matching page title
     // eg. Downloads, Outlinks. For these, idaction_name is set to 0
     $sql = "\n\t\t\t\tSELECT\n\t\t\t\t\tCOALESCE(log_action_event_category.type, log_action.type, log_action_title.type) AS type,\n\t\t\t\t\tlog_action.name AS url,\n\t\t\t\t\tlog_action.url_prefix,\n\t\t\t\t\tlog_action_title.name AS pageTitle,\n\t\t\t\t\tlog_action.idaction AS pageIdAction,\n\t\t\t\t\tlog_link_visit_action.server_time as serverTimePretty,\n\t\t\t\t\tlog_link_visit_action.time_spent_ref_action as timeSpentRef,\n\t\t\t\t\tlog_link_visit_action.idlink_va AS pageId,\n\t\t\t\t\tlog_link_visit_action.custom_float\n\t\t\t\t\t" . $sqlCustomVariables . ",\n\t\t\t\t\tlog_action_event_category.name AS eventCategory,\n\t\t\t\t\tlog_action_event_action.name as eventAction\n\t\t\t\tFROM " . Common::prefixTable('log_link_visit_action') . " AS log_link_visit_action\n\t\t\t\t\tLEFT JOIN " . Common::prefixTable('log_action') . " AS log_action\n\t\t\t\t\tON  log_link_visit_action.idaction_url = log_action.idaction\n\t\t\t\t\tLEFT JOIN " . Common::prefixTable('log_action') . " AS log_action_title\n\t\t\t\t\tON  log_link_visit_action.idaction_name = log_action_title.idaction\n\t\t\t\t\tLEFT JOIN " . Common::prefixTable('log_action') . " AS log_action_event_category\n\t\t\t\t\tON  log_link_visit_action.idaction_event_category = log_action_event_category.idaction\n\t\t\t\t\tLEFT JOIN " . Common::prefixTable('log_action') . " AS log_action_event_action\n\t\t\t\t\tON  log_link_visit_action.idaction_event_action = log_action_event_action.idaction\n\t\t\t\tWHERE log_link_visit_action.idvisit = ?\n\t\t\t\tORDER BY server_time ASC\n\t\t\t\tLIMIT 0, {$actionsLimit}\n\t\t\t\t ";
     $actionDetails = Db::fetchAll($sql, array($idVisit));
     foreach ($actionDetails as $actionIdx => &$actionDetail) {
         $actionDetail =& $actionDetails[$actionIdx];
         $customVariablesPage = array();
         for ($i = 1; $i <= $maxCustomVariables; $i++) {
             if (!empty($actionDetail['custom_var_k' . $i])) {
                 $cvarKey = $actionDetail['custom_var_k' . $i];
                 $cvarKey = static::getCustomVariablePrettyKey($cvarKey);
                 $customVariablesPage[$i] = array('customVariablePageName' . $i => $cvarKey, 'customVariablePageValue' . $i => $actionDetail['custom_var_v' . $i]);
             }
             unset($actionDetail['custom_var_k' . $i]);
             unset($actionDetail['custom_var_v' . $i]);
         }
         if (!empty($customVariablesPage)) {
             $actionDetail['customVariables'] = $customVariablesPage;
         }
         if ($actionDetail['type'] == Action::TYPE_CONTENT) {
             unset($actionDetails[$actionIdx]);
             continue;
         } elseif ($actionDetail['type'] == Action::TYPE_EVENT_CATEGORY) {
             // Handle Event
             if (strlen($actionDetail['pageTitle']) > 0) {
                 $actionDetail['eventName'] = $actionDetail['pageTitle'];
             }
             unset($actionDetail['pageTitle']);
         } else {
             if ($actionDetail['type'] == Action::TYPE_SITE_SEARCH) {
                 // Handle Site Search
                 $actionDetail['siteSearchKeyword'] = $actionDetail['pageTitle'];
                 unset($actionDetail['pageTitle']);
             }
         }
         // Event value / Generation time
         if ($actionDetail['type'] == Action::TYPE_EVENT_CATEGORY) {
             if (strlen($actionDetail['custom_float']) > 0) {
                 $actionDetail['eventValue'] = round($actionDetail['custom_float'], self::EVENT_VALUE_PRECISION);
             }
         } elseif ($actionDetail['custom_float'] > 0) {
             $actionDetail['generationTime'] = \Piwik\MetricsFormatter::getPrettyTimeFromSeconds($actionDetail['custom_float'] / 1000);
         }
         unset($actionDetail['custom_float']);
         if ($actionDetail['type'] != Action::TYPE_EVENT_CATEGORY) {
             unset($actionDetail['eventCategory']);
             unset($actionDetail['eventAction']);
         }
         // Reconstruct url from prefix
         $actionDetail['url'] = Tracker\PageUrl::reconstructNormalizedUrl($actionDetail['url'], $actionDetail['url_prefix']);
         unset($actionDetail['url_prefix']);
         // Set the time spent for this action (which is the timeSpentRef of the next action)
         if (isset($actionDetails[$actionIdx + 1])) {
             $actionDetail['timeSpent'] = $actionDetails[$actionIdx + 1]['timeSpentRef'];
             $actionDetail['timeSpentPretty'] = \Piwik\MetricsFormatter::getPrettyTimeFromSeconds($actionDetail['timeSpent']);
         }
         unset($actionDetails[$actionIdx]['timeSpentRef']);
         // not needed after timeSpent is added
     }
     // If the visitor converted a goal, we shall select all Goals
     $sql = "\n\t\t\t\tSELECT\n\t\t\t\t\t\t'goal' as type,\n\t\t\t\t\t\tgoal.name as goalName,\n\t\t\t\t\t\tgoal.idgoal as goalId,\n\t\t\t\t\t\tgoal.revenue as revenue,\n\t\t\t\t\t\tlog_conversion.idlink_va as goalPageId,\n\t\t\t\t\t\tlog_conversion.server_time as serverTimePretty,\n\t\t\t\t\t\tlog_conversion.url as url\n\t\t\t\tFROM " . Common::prefixTable('log_conversion') . " AS log_conversion\n\t\t\t\tLEFT JOIN " . Common::prefixTable('goal') . " AS goal\n\t\t\t\t\tON (goal.idsite = log_conversion.idsite\n\t\t\t\t\t\tAND\n\t\t\t\t\t\tgoal.idgoal = log_conversion.idgoal)\n\t\t\t\t\tAND goal.deleted = 0\n\t\t\t\tWHERE log_conversion.idvisit = ?\n\t\t\t\t\tAND log_conversion.idgoal > 0\n                ORDER BY server_time ASC\n\t\t\t\tLIMIT 0, {$actionsLimit}\n\t\t\t";
     $goalDetails = Db::fetchAll($sql, array($idVisit));
     $sql = "SELECT\n\t\t\t\t\t\tcase idgoal when " . GoalManager::IDGOAL_CART . " then '" . Piwik::LABEL_ID_GOAL_IS_ECOMMERCE_CART . "' else '" . Piwik::LABEL_ID_GOAL_IS_ECOMMERCE_ORDER . "' end as type,\n\t\t\t\t\t\tidorder as orderId,\n\t\t\t\t\t\t" . LogAggregator::getSqlRevenue('revenue') . " as revenue,\n\t\t\t\t\t\t" . LogAggregator::getSqlRevenue('revenue_subtotal') . " as revenueSubTotal,\n\t\t\t\t\t\t" . LogAggregator::getSqlRevenue('revenue_tax') . " as revenueTax,\n\t\t\t\t\t\t" . LogAggregator::getSqlRevenue('revenue_shipping') . " as revenueShipping,\n\t\t\t\t\t\t" . LogAggregator::getSqlRevenue('revenue_discount') . " as revenueDiscount,\n\t\t\t\t\t\titems as items,\n\n\t\t\t\t\t\tlog_conversion.server_time as serverTimePretty\n\t\t\t\t\tFROM " . Common::prefixTable('log_conversion') . " AS log_conversion\n\t\t\t\t\tWHERE idvisit = ?\n\t\t\t\t\t\tAND idgoal <= " . GoalManager::IDGOAL_ORDER . "\n\t\t\t\t\tORDER BY server_time ASC\n\t\t\t\t\tLIMIT 0, {$actionsLimit}";
     $ecommerceDetails = Db::fetchAll($sql, array($idVisit));
     foreach ($ecommerceDetails as &$ecommerceDetail) {
         if ($ecommerceDetail['type'] == Piwik::LABEL_ID_GOAL_IS_ECOMMERCE_CART) {
             unset($ecommerceDetail['orderId']);
             unset($ecommerceDetail['revenueSubTotal']);
             unset($ecommerceDetail['revenueTax']);
             unset($ecommerceDetail['revenueShipping']);
             unset($ecommerceDetail['revenueDiscount']);
         }
         // 25.00 => 25
         foreach ($ecommerceDetail as $column => $value) {
             if (strpos($column, 'revenue') !== false) {
                 if ($value == round($value)) {
                     $ecommerceDetail[$column] = round($value);
                 }
             }
         }
     }
     // Enrich ecommerce carts/orders with the list of products
     usort($ecommerceDetails, array('static', 'sortByServerTime'));
     foreach ($ecommerceDetails as &$ecommerceConversion) {
         $sql = "SELECT\n\t\t\t\t\t\t\tlog_action_sku.name as itemSKU,\n\t\t\t\t\t\t\tlog_action_name.name as itemName,\n\t\t\t\t\t\t\tlog_action_category.name as itemCategory,\n\t\t\t\t\t\t\t" . LogAggregator::getSqlRevenue('price') . " as price,\n\t\t\t\t\t\t\tquantity as quantity\n\t\t\t\t\t\tFROM " . Common::prefixTable('log_conversion_item') . "\n\t\t\t\t\t\t\tINNER JOIN " . Common::prefixTable('log_action') . " AS log_action_sku\n\t\t\t\t\t\t\tON  idaction_sku = log_action_sku.idaction\n\t\t\t\t\t\t\tLEFT JOIN " . Common::prefixTable('log_action') . " AS log_action_name\n\t\t\t\t\t\t\tON  idaction_name = log_action_name.idaction\n\t\t\t\t\t\t\tLEFT JOIN " . Common::prefixTable('log_action') . " AS log_action_category\n\t\t\t\t\t\t\tON idaction_category = log_action_category.idaction\n\t\t\t\t\t\tWHERE idvisit = ?\n\t\t\t\t\t\t\tAND idorder = ?\n\t\t\t\t\t\t\tAND deleted = 0\n\t\t\t\t\t\tLIMIT 0, {$actionsLimit}\n\t\t\t\t";
         $bind = array($idVisit, isset($ecommerceConversion['orderId']) ? $ecommerceConversion['orderId'] : GoalManager::ITEM_IDORDER_ABANDONED_CART);
         $itemsDetails = Db::fetchAll($sql, $bind);
         foreach ($itemsDetails as &$detail) {
             if ($detail['price'] == round($detail['price'])) {
                 $detail['price'] = round($detail['price']);
             }
         }
         $ecommerceConversion['itemDetails'] = $itemsDetails;
     }
     $actions = array_merge($actionDetails, $goalDetails, $ecommerceDetails);
     usort($actions, array('static', 'sortByServerTime'));
     $visitorDetailsArray['actionDetails'] = $actions;
     foreach ($visitorDetailsArray['actionDetails'] as &$details) {
         switch ($details['type']) {
             case 'goal':
                 $details['icon'] = 'plugins/Morpheus/images/goal.png';
                 break;
             case Piwik::LABEL_ID_GOAL_IS_ECOMMERCE_ORDER:
             case Piwik::LABEL_ID_GOAL_IS_ECOMMERCE_CART:
                 $details['icon'] = 'plugins/Morpheus/images/' . $details['type'] . '.gif';
                 break;
             case Action::TYPE_DOWNLOAD:
                 $details['type'] = 'download';
                 $details['icon'] = 'plugins/Morpheus/images/download.png';
                 break;
             case Action::TYPE_OUTLINK:
                 $details['type'] = 'outlink';
                 $details['icon'] = 'plugins/Morpheus/images/link.gif';
                 break;
             case Action::TYPE_SITE_SEARCH:
                 $details['type'] = 'search';
                 $details['icon'] = 'plugins/Morpheus/images/search_ico.png';
                 break;
             case Action::TYPE_EVENT_CATEGORY:
                 $details['type'] = 'event';
                 $details['icon'] = 'plugins/Morpheus/images/event.png';
                 break;
             default:
                 $details['type'] = 'action';
                 $details['icon'] = null;
                 break;
         }
         // Convert datetimes to the site timezone
         $dateTimeVisit = Date::factory($details['serverTimePretty'], $timezone);
         $details['serverTimePretty'] = $dateTimeVisit->getLocalized(Piwik::translate('CoreHome_ShortDateFormat') . ' %time%');
     }
     $visitorDetailsArray['goalConversions'] = count($goalDetails);
     return $visitorDetailsArray;
 }
Пример #7
0
 protected function getDeleteDataInfo()
 {
     Piwik::checkUserHasSuperUserAccess();
     $deleteDataInfos = array();
     $taskScheduler = new TaskScheduler();
     $deleteDataInfos["config"] = PrivacyManager::getPurgeDataSettings();
     $deleteDataInfos["deleteTables"] = "<br/>" . implode(", ", LogDataPurger::getDeleteTableLogTables());
     $scheduleTimetable = $taskScheduler->getScheduledTimeForMethod("PrivacyManager", "deleteLogTables");
     $optionTable = Option::get(self::OPTION_LAST_DELETE_PIWIK_LOGS);
     //If task was already rescheduled, read time from taskTimetable. Else, calculate next possible runtime.
     if (!empty($scheduleTimetable) && $scheduleTimetable - time() > 0) {
         $nextPossibleSchedule = (int) $scheduleTimetable;
     } else {
         $date = Date::factory("today");
         $nextPossibleSchedule = $date->addDay(1)->getTimestamp();
     }
     //deletion schedule did not run before
     if (empty($optionTable)) {
         $deleteDataInfos["lastRun"] = false;
         //next run ASAP (with next schedule run)
         $date = Date::factory("today");
         $deleteDataInfos["nextScheduleTime"] = $nextPossibleSchedule;
     } else {
         $deleteDataInfos["lastRun"] = $optionTable;
         $deleteDataInfos["lastRunPretty"] = Date::factory((int) $optionTable)->getLocalized('%day% %shortMonth% %longYear%');
         //Calculate next run based on last run + interval
         $nextScheduleRun = (int) ($deleteDataInfos["lastRun"] + $deleteDataInfos["config"]["delete_logs_schedule_lowest_interval"] * 24 * 60 * 60);
         //is the calculated next run in the past? (e.g. plugin was disabled in the meantime or something) -> run ASAP
         if ($nextScheduleRun - time() <= 0) {
             $deleteDataInfos["nextScheduleTime"] = $nextPossibleSchedule;
         } else {
             $deleteDataInfos["nextScheduleTime"] = $nextScheduleRun;
         }
     }
     $deleteDataInfos["nextRunPretty"] = MetricsFormatter::getPrettyTimeFromSeconds($deleteDataInfos["nextScheduleTime"] - time());
     return $deleteDataInfos;
 }
Пример #8
0
 function getVisitLengthPretty()
 {
     return \Piwik\MetricsFormatter::getPrettyTimeFromSeconds($this->details['visit_total_time']);
 }
Пример #9
0
 private function logArchiveTimeoutInfo()
 {
     $this->logSection("NOTES");
     // Recommend to disable browser archiving when using this script
     if (Rules::isBrowserTriggerEnabled()) {
         $this->log("- If you execute this script at least once per hour (or more often) in a crontab, you may disable 'Browser trigger archiving' in Piwik UI > Settings > General Settings. ");
         $this->log("  See the doc at: http://piwik.org/docs/setup-auto-archiving/");
     }
     $this->log("- Reports for today will be processed at most every " . $this->todayArchiveTimeToLive . " seconds. You can change this value in Piwik UI > Settings > General Settings.");
     $this->log("- Reports for the current week/month/year will be refreshed at most every " . $this->processPeriodsMaximumEverySeconds . " seconds.");
     // Try and not request older data we know is already archived
     if ($this->lastSuccessRunTimestamp !== false) {
         $dateLast = time() - $this->lastSuccessRunTimestamp;
         $this->log("- Archiving was last executed without error " . \Piwik\MetricsFormatter::getPrettyTimeFromSeconds($dateLast, true, $isHtml = false) . " ago");
     }
 }
Пример #10
0
 /**
  * Returns prettified and translated text that describes when a report was last updated.
  *
  * @return string
  */
 private function makePrettyArchivedOnText()
 {
     $dateText = $this->metadata[DataTable::ARCHIVED_DATE_METADATA_NAME];
     $date = Date::factory($dateText);
     $today = mktime(0, 0, 0);
     if ($date->getTimestamp() > $today) {
         $elapsedSeconds = time() - $date->getTimestamp();
         $timeAgo = MetricsFormatter::getPrettyTimeFromSeconds($elapsedSeconds);
         return Piwik::translate('CoreHome_ReportGeneratedXAgo', $timeAgo);
     }
     $prettyDate = $date->getLocalized("%longYear%, %longMonth% %day%") . $date->toString('S');
     return Piwik::translate('CoreHome_ReportGeneratedOn', $prettyDate);
 }
Пример #11
0
 public function getSumVisitsLengthPretty($idSite, $period, $date, $segment = false)
 {
     $table = $this->getSumVisitsLength($idSite, $period, $date, $segment);
     if (is_object($table)) {
         $table->filter('ColumnCallbackReplace', array('sum_visit_length', '\\Piwik\\MetricsFormatter::getPrettyTimeFromSeconds'));
     } else {
         $table = MetricsFormatter::getPrettyTimeFromSeconds($table);
     }
     return $table;
 }
Пример #12
0
 /**
  * Renders and echo's an admin page that lets users generate custom JavaScript
  * tracking code and custom image tracker links.
  */
 public function trackingCodeGenerator()
 {
     $view = new View('@CoreAdminHome/trackingCodeGenerator');
     $this->setBasicVariablesView($view);
     $view->topMenu = MenuTop::getInstance()->getMenu();
     $view->userMenu = MenuUser::getInstance()->getMenu();
     $viewableIdSites = APISitesManager::getInstance()->getSitesIdWithAtLeastViewAccess();
     $defaultIdSite = reset($viewableIdSites);
     $view->idSite = Common::getRequestVar('idSite', $defaultIdSite, 'int');
     $view->defaultReportSiteName = Site::getNameFor($view->idSite);
     $view->defaultSiteRevenue = \Piwik\MetricsFormatter::getCurrencySymbol($view->idSite);
     $view->maxCustomVariables = CustomVariables::getMaxCustomVariables();
     $allUrls = APISitesManager::getInstance()->getSiteUrlsFromId($view->idSite);
     if (isset($allUrls[1])) {
         $aliasUrl = $allUrls[1];
     } else {
         $aliasUrl = 'x.domain.com';
     }
     $view->defaultReportSiteAlias = $aliasUrl;
     $mainUrl = Site::getMainUrlFor($view->idSite);
     $view->defaultReportSiteDomain = @parse_url($mainUrl, PHP_URL_HOST);
     // get currencies for each viewable site
     $view->currencySymbols = APISitesManager::getInstance()->getCurrencySymbols();
     $view->serverSideDoNotTrackEnabled = \Piwik\Plugins\PrivacyManager\DoNotTrackHeaderChecker::isActive();
     return $view->render();
 }
Пример #13
0
 /**
  * Derive the unit name from a column name
  * @param $column
  * @param $idSite
  * @return string
  * @ignore
  */
 public static function getUnit($column, $idSite)
 {
     $nameToUnit = array('_rate' => '%', 'revenue' => MetricsFormatter::getCurrencySymbol($idSite), '_time_' => 's');
     foreach ($nameToUnit as $pattern => $type) {
         if (strpos($column, $pattern) !== false) {
             return $type;
         }
     }
     return '';
 }
Пример #14
0
 /**
  * Enhance $simpleDataTable using metadata :
  *
  * - remove metrics based on $reportMetadata['metrics']
  * - add 0 valued metrics if $simpleDataTable doesn't provide all $reportMetadata['metrics']
  * - format metric values to a 'human readable' format
  * - extract row metadata to a separate Simple $rowsMetadata
  *
  * @param int $idSite enables monetary value formatting based on site currency
  * @param Simple $simpleDataTable
  * @param array $metadataColumns
  * @param boolean $hasDimension
  * @param bool $returnRawMetrics If set to true, the original metrics will be returned
  *
  * @return array DataTable $enhancedDataTable filtered metrics with human readable format & Simple $rowsMetadata
  */
 private function handleSimpleDataTable($idSite, $simpleDataTable, $metadataColumns, $hasDimension, $returnRawMetrics = false)
 {
     // new DataTable to store metadata
     $rowsMetadata = new DataTable();
     // new DataTable to store 'human readable' values
     if ($hasDimension) {
         $enhancedDataTable = new DataTable();
     } else {
         $enhancedDataTable = new Simple();
     }
     foreach ($simpleDataTable->getRows() as $row) {
         $rowMetrics = $row->getColumns();
         // add missing metrics
         foreach ($metadataColumns as $id => $name) {
             if (!isset($rowMetrics[$id])) {
                 $row->setColumn($id, 0);
                 $rowMetrics[$id] = 0;
             }
         }
         $enhancedRow = new Row();
         $enhancedDataTable->addRow($enhancedRow);
         foreach ($rowMetrics as $columnName => $columnValue) {
             // filter metrics according to metadata definition
             if (isset($metadataColumns[$columnName])) {
                 // generate 'human readable' metric values
                 // if we handle MultiSites.getAll we do not always have the same idSite but different ones for
                 // each site, see https://github.com/piwik/piwik/issues/5006
                 $idSiteForRow = $idSite;
                 if ($row->getMetadata('idsite') && is_numeric($row->getMetadata('idsite'))) {
                     $idSiteForRow = (int) $row->getMetadata('idsite');
                 }
                 $prettyValue = MetricsFormatter::getPrettyValue($idSiteForRow, $columnName, $columnValue, $htmlAllowed = false);
                 $enhancedRow->addColumn($columnName, $prettyValue);
             } elseif ($returnRawMetrics) {
                 if (!isset($columnValue)) {
                     $columnValue = 0;
                 }
                 $enhancedRow->addColumn($columnName, $columnValue);
             }
         }
         // If report has a dimension, extract metadata into a distinct DataTable
         if ($hasDimension) {
             $rowMetadata = $row->getMetadata();
             $idSubDataTable = $row->getIdSubDataTable();
             // Create a row metadata only if there are metadata to insert
             if (count($rowMetadata) > 0 || !is_null($idSubDataTable)) {
                 $metadataRow = new Row();
                 $rowsMetadata->addRow($metadataRow);
                 foreach ($rowMetadata as $metadataKey => $metadataValue) {
                     $metadataRow->addColumn($metadataKey, $metadataValue);
                 }
                 if (!is_null($idSubDataTable)) {
                     $metadataRow->addColumn('idsubdatatable', $idSubDataTable);
                 }
             }
         }
     }
     return array($enhancedDataTable, $rowsMetadata);
 }
Пример #15
0
 /**
  * Returns an array describing a visitor using her last visits (uses a maximum of 100).
  *
  * @param int $idSite Site ID
  * @param bool|false|string $visitorId The ID of the visitor whose profile to retrieve.
  * @param bool|false|string $segment
  * @param bool $checkForLatLong If true, hasLatLong will appear in the output and be true if
  *                              one of the first 100 visits has a latitude/longitude.
  * @return array
  */
 public function getVisitorProfile($idSite, $visitorId = false, $segment = false, $checkForLatLong = false)
 {
     Piwik::checkUserHasViewAccess($idSite);
     if ($visitorId === false) {
         $visitorId = $this->getMostRecentVisitorId($idSite, $segment);
     }
     $newSegment = ($segment === false ? '' : $segment . ';') . 'visitorId==' . $visitorId;
     $visits = $this->loadLastVisitorDetailsFromDatabase($idSite, $period = false, $date = false, $newSegment, $numVisitorsToFetch = self::VISITOR_PROFILE_MAX_VISITS_TO_AGGREGATE, $overrideVisitorId = false, $minTimestamp = false);
     $this->addFilterToCleanVisitors($visits, $idSite, $flat = false, $doNotFetchActions = false, $filterNow = true);
     if ($visits->getRowsCount() == 0) {
         return array();
     }
     $isEcommerceEnabled = Site::isEcommerceEnabledFor($idSite);
     $result = array();
     $result['totalVisits'] = 0;
     $result['totalVisitDuration'] = 0;
     $result['totalActions'] = 0;
     $result['totalSearches'] = 0;
     $result['totalPageViews'] = 0;
     $result['totalGoalConversions'] = 0;
     $result['totalConversionsByGoal'] = array();
     if ($isEcommerceEnabled) {
         $result['totalEcommerceConversions'] = 0;
         $result['totalEcommerceRevenue'] = 0;
         $result['totalEcommerceItems'] = 0;
         $result['totalAbandonedCarts'] = 0;
         $result['totalAbandonedCartsRevenue'] = 0;
         $result['totalAbandonedCartsItems'] = 0;
     }
     $countries = array();
     $continents = array();
     $cities = array();
     $siteSearchKeywords = array();
     $pageGenerationTimeTotal = 0;
     // aggregate all requested visits info for total_* info
     foreach ($visits->getRows() as $visit) {
         ++$result['totalVisits'];
         $result['totalVisitDuration'] += $visit->getColumn('visitDuration');
         $result['totalActions'] += $visit->getColumn('actions');
         $result['totalGoalConversions'] += $visit->getColumn('goalConversions');
         // individual goal conversions are stored in action details
         foreach ($visit->getColumn('actionDetails') as $action) {
             if ($action['type'] == 'goal') {
                 // handle goal conversion
                 $idGoal = $action['goalId'];
                 $idGoalKey = 'idgoal=' . $idGoal;
                 if (!isset($result['totalConversionsByGoal'][$idGoalKey])) {
                     $result['totalConversionsByGoal'][$idGoalKey] = 0;
                 }
                 ++$result['totalConversionsByGoal'][$idGoalKey];
                 if (!empty($action['revenue'])) {
                     if (!isset($result['totalRevenueByGoal'][$idGoalKey])) {
                         $result['totalRevenueByGoal'][$idGoalKey] = 0;
                     }
                     $result['totalRevenueByGoal'][$idGoalKey] += $action['revenue'];
                 }
             } else {
                 if ($action['type'] == Piwik::LABEL_ID_GOAL_IS_ECOMMERCE_ORDER && $isEcommerceEnabled) {
                     ++$result['totalEcommerceConversions'];
                     $result['totalEcommerceRevenue'] += $action['revenue'];
                     $result['totalEcommerceItems'] += $action['items'];
                 } else {
                     if ($action['type'] == Piwik::LABEL_ID_GOAL_IS_ECOMMERCE_CART && $isEcommerceEnabled) {
                         ++$result['totalAbandonedCarts'];
                         $result['totalAbandonedCartsRevenue'] += $action['revenue'];
                         $result['totalAbandonedCartsItems'] += $action['items'];
                     }
                 }
             }
             if (isset($action['siteSearchKeyword'])) {
                 $keyword = $action['siteSearchKeyword'];
                 if (!isset($siteSearchKeywords[$keyword])) {
                     $siteSearchKeywords[$keyword] = 0;
                     ++$result['totalSearches'];
                 }
                 ++$siteSearchKeywords[$keyword];
             }
             if (isset($action['generationTime'])) {
                 $pageGenerationTimeTotal += $action['generationTime'];
                 ++$result['totalPageViews'];
             }
         }
         $countryCode = $visit->getColumn('countryCode');
         if (!isset($countries[$countryCode])) {
             $countries[$countryCode] = 0;
         }
         ++$countries[$countryCode];
         $continentCode = $visit->getColumn('continentCode');
         if (!isset($continents[$continentCode])) {
             $continents[$continentCode] = 0;
         }
         ++$continents[$continentCode];
         if ($countryCode && !array_key_exists($countryCode, $cities)) {
             $cities[$countryCode] = array();
         }
         $city = $visit->getColumn('city');
         if (!empty($city)) {
             $cities[$countryCode][] = $city;
         }
     }
     // sort countries/continents/search keywords by visit/action
     asort($countries);
     asort($continents);
     arsort($siteSearchKeywords);
     // transform country/continents/search keywords into something that will look good in XML
     $result['countries'] = $result['continents'] = $result['searches'] = array();
     foreach ($countries as $countryCode => $nbVisits) {
         $countryInfo = array('country' => $countryCode, 'nb_visits' => $nbVisits, 'flag' => \Piwik\Plugins\UserCountry\getFlagFromCode($countryCode), 'prettyName' => \Piwik\Plugins\UserCountry\countryTranslate($countryCode));
         if (!empty($cities[$countryCode])) {
             $countryInfo['cities'] = array_unique($cities[$countryCode]);
         }
         $result['countries'][] = $countryInfo;
     }
     foreach ($continents as $continentCode => $nbVisits) {
         $result['continents'][] = array('continent' => $continentCode, 'nb_visits' => $nbVisits, 'prettyName' => \Piwik\Plugins\UserCountry\continentTranslate($continentCode));
     }
     foreach ($siteSearchKeywords as $keyword => $searchCount) {
         $result['searches'][] = array('keyword' => $keyword, 'searches' => $searchCount);
     }
     if ($result['totalPageViews']) {
         $result['averagePageGenerationTime'] = round($pageGenerationTimeTotal / $result['totalPageViews'], $precision = 2);
     }
     $result['totalVisitDurationPretty'] = MetricsFormatter::getPrettyTimeFromSeconds($result['totalVisitDuration']);
     // use requested visits for first/last visit info
     $rows = $visits->getRows();
     $result['firstVisit'] = $this->getVisitorProfileVisitSummary(end($rows));
     $result['lastVisit'] = $this->getVisitorProfileVisitSummary(reset($rows));
     // check if requested visits have lat/long
     if ($checkForLatLong) {
         $result['hasLatLong'] = false;
         foreach ($rows as $visit) {
             if ($visit->getColumn('latitude') !== false) {
                 // realtime map only checks for latitude
                 $result['hasLatLong'] = true;
                 break;
             }
         }
     }
     // save count of visits we queries
     $result['visitsAggregated'] = count($rows);
     // use N most recent visits for last_visits
     $visits->deleteRowsOffset(self::VISITOR_PROFILE_MAX_VISITS_TO_SHOW);
     $result['lastVisits'] = $visits;
     // use the right date format for the pretty server date
     $timezone = Site::getTimezoneFor($idSite);
     foreach ($result['lastVisits']->getRows() as $visit) {
         $dateTimeVisitFirstAction = Date::factory($visit->getColumn('firstActionTimestamp'), $timezone);
         $datePretty = $dateTimeVisitFirstAction->getLocalized(self::VISITOR_PROFILE_DATE_FORMAT);
         $visit->setColumn('serverDatePrettyFirstAction', $datePretty);
         $dateTimePretty = $datePretty . ' ' . $visit->getColumn('serverTimePrettyFirstAction');
         $visit->setColumn('serverDateTimePrettyFirstAction', $dateTimePretty);
     }
     $result['userId'] = $visit->getColumn('userId');
     // get visitor IDs that are adjacent to this one in log_visit
     // TODO: make sure order of visitor ids is not changed if a returning visitor visits while the user is
     //       looking at the popup.
     $latestVisitTime = reset($rows)->getColumn('lastActionDateTime');
     $result['nextVisitorId'] = $this->getAdjacentVisitorId($idSite, $visitorId, $latestVisitTime, $segment, $getNext = true);
     $result['previousVisitorId'] = $this->getAdjacentVisitorId($idSite, $visitorId, $latestVisitTime, $segment, $getNext = false);
     /**
      * Triggered in the Live.getVisitorProfile API method. Plugins can use this event
      * to discover and add extra data to visitor profiles.
      *
      * For example, if an email address is found in a custom variable, a plugin could load the
      * gravatar for the email and add it to the visitor profile, causing it to display in the
      * visitor profile popup.
      *
      * The following visitor profile elements can be set to augment the visitor profile popup:
      *
      * - **visitorAvatar**: A URL to an image to display in the top left corner of the popup.
      * - **visitorDescription**: Text to be used as the tooltip of the avatar image.
      *
      * @param array &$visitorProfile The unaugmented visitor profile info.
      */
     Piwik::postEvent('Live.getExtraVisitorDetails', array(&$result));
     return $result;
 }
Пример #16
0
 /**
  * Returns the currency symbol for a site.
  *
  * @param int $idSite The ID of the site to return the currency symbol for.
  * @return string eg, `'$'`.
  */
 public static function getCurrencySymbol($idSite)
 {
     $symbols = MetricsFormatter::getCurrencyList();
     $site = new Site($idSite);
     $currency = $site->getCurrency();
     if (isset($symbols[$currency])) {
         return $symbols[$currency][0];
     }
     return '';
 }
Пример #17
0
 protected function applyPrettyMoney(&$sites)
 {
     foreach ($sites as $idsite => &$site) {
         $revenue = "-";
         if (!empty($site['revenue'])) {
             $revenue = MetricsFormatter::getPrettyMoney($site['revenue'], $site['idsite'], $htmlAllowed = false);
         }
         $site['revenue'] = '"' . $revenue . '"';
     }
 }
Пример #18
0
 /**
  * Returns the list of currency symbols
  * @see getCurrencyList()
  * @return array( currencyId => currencySymbol )
  */
 public function getCurrencySymbols()
 {
     $currencies = MetricsFormatter::getCurrencyList();
     return array_map(function ($a) {
         return $a[0];
     }, $currencies);
 }
Пример #19
0
 /**
  * The "Manage Users and Permissions" Admin UI screen
  */
 function index()
 {
     Piwik::checkUserIsNotAnonymous();
     $view = new View('@UsersManager/index');
     $IdSitesAdmin = APISitesManager::getInstance()->getSitesIdWithAdminAccess();
     $idSiteSelected = 1;
     if (count($IdSitesAdmin) > 0) {
         $defaultWebsiteId = $IdSitesAdmin[0];
         $idSiteSelected = Common::getRequestVar('idSite', $defaultWebsiteId);
     }
     if ($idSiteSelected === 'all') {
         $usersAccessByWebsite = array();
         $defaultReportSiteName = Piwik::translate('UsersManager_ApplyToAllWebsites');
     } else {
         $usersAccessByWebsite = APIUsersManager::getInstance()->getUsersAccessFromSite($idSiteSelected);
         $defaultReportSiteName = Site::getNameFor($idSiteSelected);
     }
     // we dont want to display the user currently logged so that the user can't change his settings from admin to view...
     $currentlyLogged = Piwik::getCurrentUserLogin();
     $usersLogin = APIUsersManager::getInstance()->getUsersLogin();
     foreach ($usersLogin as $login) {
         if (!isset($usersAccessByWebsite[$login])) {
             $usersAccessByWebsite[$login] = 'noaccess';
         }
     }
     unset($usersAccessByWebsite[$currentlyLogged]);
     // $usersAccessByWebsite is not supposed to contain unexistant logins, but it does when upgrading from some old Piwik version
     foreach ($usersAccessByWebsite as $login => $access) {
         if (!in_array($login, $usersLogin)) {
             unset($usersAccessByWebsite[$login]);
             continue;
         }
     }
     ksort($usersAccessByWebsite);
     $users = array();
     $superUsers = array();
     $usersAliasByLogin = array();
     if (Piwik::isUserHasSomeAdminAccess()) {
         $view->showLastSeen = true;
         $users = APIUsersManager::getInstance()->getUsers();
         foreach ($users as $index => $user) {
             $usersAliasByLogin[$user['login']] = $user['alias'];
             $lastSeen = LastSeenTimeLogger::getLastSeenTimeForUser($user['login']);
             $users[$index]['last_seen'] = $lastSeen == 0 ? false : MetricsFormatter::getPrettyTimeFromSeconds(time() - $lastSeen);
         }
         if (Piwik::hasUserSuperUserAccess()) {
             foreach ($users as $user) {
                 if ($user['superuser_access']) {
                     $superUsers[] = $user['login'];
                 }
             }
         }
     }
     $view->anonymousHasViewAccess = $this->hasAnonymousUserViewAccess($usersAccessByWebsite);
     $view->idSiteSelected = $idSiteSelected;
     $view->defaultReportSiteName = $defaultReportSiteName;
     $view->users = $users;
     $view->superUserLogins = $superUsers;
     $view->usersAliasByLogin = $usersAliasByLogin;
     $view->usersCount = count($users) - 1;
     $view->usersAccessByWebsite = $usersAccessByWebsite;
     $websites = APISitesManager::getInstance()->getSitesWithAdminAccess();
     uasort($websites, array('Piwik\\Plugins\\UsersManager\\Controller', 'orderByName'));
     $view->websites = $websites;
     $this->setBasicVariablesView($view);
     return $view->render();
 }
Пример #20
0
 protected function addFilter_sumTime()
 {
     $sumtimeFilter = new Twig_SimpleFilter('sumtime', function ($numberOfSeconds) {
         return MetricsFormatter::getPrettyTimeFromSeconds($numberOfSeconds);
     });
     $this->twig->addFilter($sumtimeFilter);
 }
Пример #21
0
 /**
  * @dataProvider getGetPrettyValueTestCases
  */
 public function testGetPrettyValue($columnName, $value, $expected)
 {
     Translate::loadEnglishTranslation();
     $access = Access::getInstance();
     $access->setSuperUserAccess(true);
     $idsite = API::getInstance()->addSite("test", "http://test");
     $this->assertEquals($expected, MetricsFormatter::getPrettyValue($idsite, $columnName, $value, false, false));
     Translate::unloadEnglishTranslation();
 }
Пример #22
0
 /**
  * Returns the domain age for the current url
  *
  * @return int
  */
 public function getAge()
 {
     $ageArchiveOrg = $this->_getAgeArchiveOrg();
     $ageWhoIs = $this->_getAgeWhoIs();
     $ageWhoisCom = $this->_getAgeWhoisCom();
     $ages = array();
     if ($ageArchiveOrg > 0) {
         $ages[] = $ageArchiveOrg;
     }
     if ($ageWhoIs > 0) {
         $ages[] = $ageWhoIs;
     }
     if ($ageWhoisCom > 0) {
         $ages[] = $ageWhoisCom;
     }
     if (count($ages) > 1) {
         $maxAge = min($ages);
     } else {
         $maxAge = array_shift($ages);
     }
     if ($maxAge) {
         return MetricsFormatter::getPrettyTimeFromSeconds(time() - $maxAge);
     }
     return false;
 }
Пример #23
0
 /**
  * Enhance $simpleDataTable using metadata :
  *
  * - remove metrics based on $reportMetadata['metrics']
  * - add 0 valued metrics if $simpleDataTable doesn't provide all $reportMetadata['metrics']
  * - format metric values to a 'human readable' format
  * - extract row metadata to a separate Simple $rowsMetadata
  *
  * @param int $idSite enables monetary value formatting based on site currency
  * @param Simple $simpleDataTable
  * @param array $metadataColumns
  * @param boolean $hasDimension
  * @param bool $returnRawMetrics If set to true, the original metrics will be returned
  *
  * @return array DataTable $enhancedDataTable filtered metrics with human readable format & Simple $rowsMetadata
  */
 private function handleSimpleDataTable($idSite, $simpleDataTable, $metadataColumns, $hasDimension, $returnRawMetrics = false)
 {
     // new DataTable to store metadata
     $rowsMetadata = new DataTable();
     // new DataTable to store 'human readable' values
     if ($hasDimension) {
         $enhancedDataTable = new DataTable();
     } else {
         $enhancedDataTable = new Simple();
     }
     // add missing metrics
     foreach ($simpleDataTable->getRows() as $row) {
         $rowMetrics = $row->getColumns();
         foreach ($metadataColumns as $id => $name) {
             if (!isset($rowMetrics[$id])) {
                 $row->addColumn($id, 0);
             }
         }
     }
     foreach ($simpleDataTable->getRows() as $row) {
         $enhancedRow = new Row();
         $enhancedDataTable->addRow($enhancedRow);
         $rowMetrics = $row->getColumns();
         foreach ($rowMetrics as $columnName => $columnValue) {
             // filter metrics according to metadata definition
             if (isset($metadataColumns[$columnName])) {
                 // generate 'human readable' metric values
                 $prettyValue = MetricsFormatter::getPrettyValue($idSite, $columnName, $columnValue, $htmlAllowed = false);
                 $enhancedRow->addColumn($columnName, $prettyValue);
             } elseif ($returnRawMetrics) {
                 $enhancedRow->addColumn($columnName, $columnValue);
             }
         }
         // If report has a dimension, extract metadata into a distinct DataTable
         if ($hasDimension) {
             $rowMetadata = $row->getMetadata();
             $idSubDataTable = $row->getIdSubDataTable();
             // Create a row metadata only if there are metadata to insert
             if (count($rowMetadata) > 0 || !is_null($idSubDataTable)) {
                 $metadataRow = new Row();
                 $rowsMetadata->addRow($metadataRow);
                 foreach ($rowMetadata as $metadataKey => $metadataValue) {
                     $metadataRow->addColumn($metadataKey, $metadataValue);
                 }
                 if (!is_null($idSubDataTable)) {
                     $metadataRow->addColumn('idsubdatatable', $idSubDataTable);
                 }
             }
         }
     }
     return array($enhancedDataTable, $rowsMetadata);
 }