| <?php |
|
|
| |
| |
| |
| |
| |
| |
|
|
| namespace Piwik\Plugins\CoreHome\DataTableRowAction; |
|
|
| use Exception; |
| use Piwik\API\DataTablePostProcessor; |
| use Piwik\API\Request; |
| use Piwik\Common; |
| use Piwik\DataTable\DataTableInterface; |
| use Piwik\DataTable\Map; |
| use Piwik\Date; |
| use Piwik\Metrics; |
| use Piwik\NumberFormatter; |
| use Piwik\Period\Factory as PeriodFactory; |
| use Piwik\Piwik; |
| use Piwik\Plugin\ViewDataTable; |
| use Piwik\Plugins\API\Filter\DataComparisonFilter; |
| use Piwik\Plugins\CoreVisualizations\Visualizations\Graph\Config as GraphConfig; |
| use Piwik\Plugins\CoreVisualizations\Visualizations\JqplotGraph\Config as JqplotGraphConfig; |
| use Piwik\Plugins\CoreVisualizations\Visualizations\JqplotGraph\Evolution as EvolutionViz; |
| use Piwik\ViewDataTable\Factory; |
| use Piwik\ViewDataTable\Manager as ViewDataTableManager; |
|
|
| |
| |
| |
| |
| class RowEvolution |
| { |
| |
| |
| |
| |
| protected $idSite; |
|
|
| |
| |
| |
| |
| protected $apiMethod; |
|
|
| |
| |
| |
| |
| protected $label; |
|
|
| |
| |
| |
| |
| protected $period; |
|
|
| |
| |
| |
| |
| protected $date; |
|
|
| |
| |
| |
| |
| protected $segment; |
|
|
| |
| |
| |
| |
| protected $availableMetrics; |
|
|
| |
| |
| |
| |
| protected $dimension; |
|
|
| |
| |
| |
| |
| protected $dataTable; |
|
|
| |
| |
| |
| |
| protected $rowLabel; |
|
|
| |
| |
| |
| |
| protected $rowIcon; |
|
|
| |
| |
| |
| |
| protected $graphType; |
|
|
| |
| |
| |
| |
| protected $graphMetrics; |
|
|
| |
| |
| |
| |
| protected $initiallyShowAllMetrics = false; |
|
|
| |
| |
| |
| |
| |
| |
| |
| |
| public function __construct($idSite, $date, $graphType = 'graphEvolution') |
| { |
| $this->apiMethod = Common::getRequestVar('apiMethod', '', 'string'); |
| if (empty($this->apiMethod)) { |
| throw new Exception("Parameter apiMethod not set."); |
| } |
|
|
| $label = DataTablePostProcessor::getLabelFromRequest($_GET); |
| if (!is_array($label)) { |
| throw new Exception("Expected label to be an array, got instead: " . $label); |
| } |
| $this->label = Common::unsanitizeInputValue($label[0]); |
|
|
| if ($this->label === '') { |
| throw new Exception("Parameter label not set."); |
| } |
|
|
| $this->period = Common::getRequestVar('period', '', 'string'); |
| PeriodFactory::checkPeriodIsEnabled($this->period); |
|
|
| if ($this->period != 'range' && !($date instanceof Date)) { |
| throw new Exception("Expected date to be an instance of \\Piwik\\Date"); |
| } |
|
|
| $this->idSite = $idSite; |
| $this->graphType = $graphType; |
|
|
| if ($this->period != 'range') { |
| |
| |
| $cache = ViewDataTableManager::getViewDataTableParameters( |
| Piwik::getCurrentUserLogin(), |
| 'CoreHome.getRowEvolutionGraph' |
| ); |
| $lastDay = (isset($cache['evolution_' . $this->period . '_last_n']) ? $cache['evolution_' . $this->period . '_last_n'] : null); |
| $end = $date->toString(); |
| [$this->date, $lastN] = EvolutionViz::getDateRangeAndLastN($this->period, $end, $lastDay); |
| } |
| $this->segment = \Piwik\API\Request::getRawSegmentFromRequest(); |
|
|
| $this->loadEvolutionReport(); |
| } |
|
|
| |
| |
| |
| |
| |
| |
| public function renderPopover($controller, $view) |
| { |
| |
| $this->graphType = 'graphEvolution'; |
| $this->graphMetrics = $this->availableMetrics; |
| $view->graph = $this->getRowEvolutionGraphFromController($controller); |
|
|
| |
| $view->metrics = $this->getMetricsToggles(); |
|
|
| |
| $metricsText = Piwik::translate('RowEvolution_AvailableMetrics'); |
| $popoverTitle = ''; |
| if ($this->rowLabel) { |
| $icon = $this->rowIcon ? '<img height="16px" src="' . $this->rowIcon . '" alt="">' : ''; |
| $rowLabel = str_replace('/', '<wbr>/', str_replace('&', '<wbr>&', $this->rowLabel)); |
| $metricsText = sprintf(Piwik::translate('RowEvolution_MetricsFor'), $this->dimension . ': ' . $icon . ' ' . $rowLabel); |
| $popoverTitle = $icon . ' ' . $this->rowLabel; |
| } |
|
|
| $view->availableMetricsText = $metricsText; |
| $view->popoverTitle = $popoverTitle; |
|
|
| return $view->render(); |
| } |
|
|
| |
| |
| |
| |
| protected function loadEvolutionReport($column = false) |
| { |
| [$apiModule, $apiAction] = explode('.', $this->apiMethod); |
|
|
| |
| $parameters = array( |
| 'method' => 'API.getRowEvolution', |
| 'label' => $this->label, |
| 'apiModule' => $apiModule, |
| 'apiAction' => $apiAction, |
| 'idSite' => $this->idSite, |
| 'period' => $this->period, |
| 'date' => $this->date, |
| 'format' => 'original', |
| 'serialize' => '0', |
| ); |
|
|
| if (!empty($this->segment)) { |
| $parameters['segment'] = $this->segment; |
| } |
|
|
| if ($column !== false) { |
| $parameters['column'] = $column; |
| } |
|
|
| $unmodifiedSeriesLabels = []; |
| $isComparing = DataComparisonFilter::isCompareParamsPresent(); |
| if ($isComparing) { |
| $compareDates = Common::getRequestVar('compareDates', [], 'array'); |
| $comparePeriods = Common::getRequestVar('comparePeriods', [], 'array'); |
| $compareSegments = Common::getRequestVar('compareSegments', [], 'array'); |
|
|
| $totalSeriesCount = (count($compareSegments) + 1) * (count($comparePeriods) + 1); |
|
|
| for ($i = 0; $i < $totalSeriesCount; ++$i) { |
| $unmodifiedSeriesLabels[] = DataComparisonFilter::getPrettyComparisonLabelFromSeriesIndex($i); |
| } |
|
|
| $parameters['compare'] = 1; |
|
|
| foreach ($comparePeriods as $index => $period) { |
| $date = $compareDates[$index]; |
|
|
| if ($period == 'range') { |
| $comparePeriods[$index] = 'day'; |
| } else { |
| [$newDate, $lastN] = EvolutionViz::getDateRangeAndLastN($period, $date); |
| $compareDates[$index] = $newDate; |
| } |
| } |
|
|
| $parameters['compareDates'] = $compareDates; |
| $parameters['comparePeriods'] = $comparePeriods; |
| } |
|
|
| $parameters = array_filter($parameters, function ($value) { |
| return $value !== null && $value !== false; |
| }); |
|
|
| |
| $report = Request::processRequest('API.getRowEvolution', $parameters); |
|
|
| |
| |
| if ($isComparing) { |
| $dataTables = $report['reportData']->getDataTables(); |
| $modifiedSeriesLabels = reset($dataTables)->getMetadata('comparisonSeries'); |
| $seriesMap = array_combine($modifiedSeriesLabels, $unmodifiedSeriesLabels); |
|
|
| foreach ($report['metadata']['metrics'] as $key => $metricInfo) { |
| foreach ($seriesMap as $modified => $unmodified) { |
| $report['metadata']['metrics'][$key]['name'] = str_replace($modified, $unmodified, $report['metadata']['metrics'][$key]['name']); |
| } |
| } |
| } |
|
|
| $this->extractEvolutionReport($report); |
| } |
|
|
| |
| |
| |
| |
| protected function extractEvolutionReport($report) |
| { |
| $this->dataTable = $report['reportData']; |
| $this->rowLabel = $this->extractPrettyLabel($report); |
| $this->rowIcon = !empty($report['logo']) ? $report['logo'] : false; |
| $this->availableMetrics = $report['metadata']['metrics']; |
| $this->dimension = $report['metadata']['dimension']; |
| } |
|
|
| |
| |
| |
| |
| |
| |
| |
| public function getRowEvolutionGraph($graphType = false, $metrics = false) |
| { |
| |
| $view = Factory::build( |
| $graphType ? : $this->graphType, |
| $this->apiMethod, |
| $controllerAction = 'CoreHome.getRowEvolutionGraph', |
| $forceDefault = true |
| ); |
| $view->setDataTable($this->dataTable); |
|
|
| if (!empty($this->graphMetrics)) { |
| $view->config->columns_to_display = array_keys($metrics ? : $this->graphMetrics); |
| } |
|
|
| $view->requestConfig->request_parameters_to_modify['label'] = ''; |
| $view->config->export_parameters_to_modify['label'] = $this->label; |
| $view->config->show_flatten_table_export = false; |
| $view->config->show_goals = false; |
| $view->config->show_search = false; |
| $view->config->show_all_views_icons = false; |
| $view->config->show_related_reports = false; |
| $view->config->show_footer_message = false; |
|
|
| foreach ($this->availableMetrics as $metric => $metadata) { |
| $view->config->translations[$metric] = $metadata['name']; |
| } |
|
|
| if ($view->config instanceof GraphConfig) { |
| $view->config->show_series_picker = false; |
| } |
|
|
| if ($view->config instanceof JqplotGraphConfig) { |
| $view->config->external_series_toggle = 'RowEvolutionSeriesToggle'; |
| $view->config->external_series_toggle_show_all = $this->initiallyShowAllMetrics; |
| } |
|
|
| return $view; |
| } |
|
|
| |
| |
| |
| |
| protected function getMetricsToggles() |
| { |
| $i = 0; |
| $metrics = array(); |
| foreach ($this->availableMetrics as $metric => $metricData) { |
| $unit = Metrics::getUnit($metric, $this->idSite); |
| $change = isset($metricData['change']) ? $metricData['change'] : false; |
|
|
| [$first, $last] = $this->getFirstAndLastDataPointsForMetric($metric); |
| $fractionDigits = max($this->getFractionDigits($first), $this->getFractionDigits($last)); |
|
|
| $details = Piwik::translate('RowEvolution_MetricBetweenText', array( |
| NumberFormatter::getInstance()->format($first, $fractionDigits) . $unit, |
| NumberFormatter::getInstance()->format($last, $fractionDigits) . $unit, |
| )); |
|
|
| if ($change !== false) { |
| $lowerIsBetter = Metrics::isLowerValueBetter($metric); |
| if (substr($change, 0, 1) == '+') { |
| $changeClass = $lowerIsBetter ? 'bad' : 'good'; |
| $changeImage = $lowerIsBetter ? 'arrow_up_red' : 'arrow_up'; |
| } elseif (substr($change, 0, 1) == '-') { |
| $changeClass = $lowerIsBetter ? 'good' : 'bad'; |
| $changeImage = $lowerIsBetter ? 'arrow_down_green' : 'arrow_down'; |
| } else { |
| $changeClass = 'neutral'; |
| $changeImage = false; |
| } |
|
|
| $change = '<span class="' . $changeClass . '">' |
| . ($changeImage ? '<img src="plugins/MultiSites/images/' . $changeImage . '.png" /> ' : '') |
| . $change . '</span>'; |
|
|
| $details .= ', ' . Piwik::translate('RowEvolution_MetricChangeText', $change); |
| } |
|
|
| |
| $max = isset($metricData['max']) ? $metricData['max'] : 0; |
| $min = isset($metricData['min']) ? $metricData['min'] : 0; |
| $minmax = Piwik::translate('RowEvolution_MetricMinMax', array( |
| $metricData['name'], |
| NumberFormatter::getInstance()->formatNumber($min, $fractionDigits, $fractionDigits) . $unit, |
| NumberFormatter::getInstance()->formatNumber($max, $fractionDigits, $fractionDigits) . $unit, |
| )); |
|
|
| $newMetric = array( |
| 'label' => $metricData['name'], |
| 'details' => $details, |
| 'minmax' => $minmax, |
| 'sparkline' => $this->getSparkline($metric), |
| ); |
| |
| if (!empty($metricData['logo'])) { |
| $newMetric['logo'] = $metricData['logo']; |
| } |
|
|
| |
| if ( |
| $metric == 'nb_users' |
| && $first == 0 |
| && $last == 0 |
| ) { |
| $newMetric['hide'] = true; |
| } |
|
|
| $metrics[] = $newMetric; |
| $i++; |
| } |
|
|
| return $metrics; |
| } |
|
|
| |
| |
| |
| |
| |
| |
| protected function getSparkline($metric) |
| { |
| |
| $view = $this->getRowEvolutionGraph($graphType = 'sparkline', $metrics = array($metric => $metric)); |
|
|
| ob_start(); |
| $view->render(); |
| $spark = ob_get_contents(); |
| ob_end_clean(); |
|
|
| |
| Common::sendHeader('Content-type: text/html'); |
|
|
| |
| $spark = base64_encode($spark); |
| return '<img width="100" height="25" src="data:image/png;base64,' . $spark . '" />'; |
| } |
|
|
| |
| |
| |
| |
| public function useAvailableMetrics() |
| { |
| $this->graphMetrics = $this->availableMetrics; |
| } |
|
|
| |
| |
| |
| |
| private function getFirstAndLastDataPointsForMetric($metric) |
| { |
| $first = 0; |
| $firstTable = $this->dataTable->getFirstRow(); |
| if (!empty($firstTable)) { |
| $row = $firstTable->getFirstRow(); |
| if (!empty($row)) { |
| $first = floatval($row->getColumn($metric)); |
| } |
| } |
|
|
| $last = 0; |
| $lastTable = $this->dataTable->getLastRow(); |
| if (!empty($lastTable)) { |
| $row = $lastTable->getFirstRow(); |
| if (!empty($row)) { |
| $last = floatval($row->getColumn($metric)); |
| } |
| } |
|
|
| return array($first, $last); |
| } |
|
|
| |
| |
| |
| |
| protected function extractPrettyLabel($report) |
| { |
| |
| $rowLabel = Common::sanitizeInputValue($report['label']); |
|
|
| |
| $dataTableMap = $report['reportData']; |
|
|
| |
| if ($dataTableMap instanceof DataTableInterface) { |
| $labelPretty = $dataTableMap->getColumn('label_html'); |
| $labelPretty = array_filter($labelPretty, 'strlen'); |
| $labelPretty = current($labelPretty); |
| if (!empty($labelPretty)) { |
| return $labelPretty; |
| } |
| } |
| return $rowLabel; |
| } |
|
|
| |
| |
| |
| private function getFractionDigits($value): int |
| { |
| $value = (string) $value; |
| $fraction = substr(strrchr($value, ".") ?: '', 1); |
| return strlen($fraction); |
| } |
|
|
| |
| |
| |
| protected function getRowEvolutionGraphFromController(\Piwik\Plugins\CoreHome\Controller $controller) |
| { |
| return $controller->getRowEvolutionGraph($fetch = true, $rowEvolution = $this); |
| } |
| } |
|
|