| <?php |
|
|
| |
| |
| |
| |
| |
| |
|
|
| namespace Piwik\Plugins\CoreVisualizations\Visualizations; |
|
|
| use Piwik\API\Request; |
| use Piwik\Common; |
| use Piwik\Container\StaticContainer; |
| use Piwik\DataTable; |
| use Piwik\Metrics; |
| use Piwik\Metrics\Formatter as MetricFormatter; |
| use Piwik\Period\Factory; |
| use Piwik\Plugin\Report; |
| use Piwik\Plugin\ReportsProvider; |
| use Piwik\Plugin\ViewDataTable; |
| use Piwik\Plugins\API\Filter\DataComparisonFilter; |
| use Piwik\Plugins\CoreVisualizations\FeatureFlags\SparklinesRedesign; |
| use Piwik\Plugins\FeatureFlags\FeatureFlagManager; |
| use Piwik\Piwik; |
| use Piwik\SettingsPiwik; |
| use Piwik\View; |
|
|
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| class Sparklines extends ViewDataTable |
| { |
| public const ID = 'sparklines'; |
|
|
| public static function getDefaultConfig() |
| { |
| return new Sparklines\Config(); |
| } |
|
|
| public function supportsComparison() |
| { |
| return true; |
| } |
|
|
| |
| |
| |
| public function render() |
| { |
| $view = new View('@CoreVisualizations/_dataTableViz_sparklines.twig'); |
|
|
| $columnsList = []; |
| if ($this->config->hasSparklineMetrics()) { |
| foreach ($this->config->getSparklineMetrics() as $cols) { |
| $columns = $cols['columns']; |
| if (!is_array($columns)) { |
| $columns = [$columns]; |
| } |
|
|
| $columnsList = array_merge($columns, $columnsList); |
| } |
| } |
|
|
| $view->allMetricsDocumentation = array_merge(Metrics::getDefaultMetricsDocumentation(), $this->config->metrics_documentation); |
|
|
| $this->requestConfig->request_parameters_to_modify['columns'] = $columnsList; |
| $this->requestConfig->request_parameters_to_modify['format_metrics'] = '1'; |
|
|
| |
| |
| |
| |
| |
| |
| |
| $this->requestConfig->request_parameters_to_modify['include_trends'] = '1'; |
|
|
| $request = $this->getRequestArray(); |
| if ( |
| $this->isComparing() |
| && !empty($request['comparePeriods']) |
| && count($request['comparePeriods']) == 1 |
| ) { |
| $this->requestConfig->request_parameters_to_modify['invert_compare_change_compute'] = 1; |
| } |
|
|
| if (!empty($this->requestConfig->apiMethodToRequestDataTable)) { |
| $this->fetchConfiguredSparklines(); |
| } |
|
|
| $view->sparklines = $this->config->getSortedSparklines(); |
| $view->isWidget = Common::getRequestVar('widget', 0, 'int'); |
| $view->titleAttributes = $this->config->title_attributes; |
| $view->footerMessage = $this->config->show_footer_message; |
| $view->areSparklinesLinkable = $this->config->areSparklinesLinkable(); |
| $view->isComparing = $this->isComparing(); |
|
|
| |
| |
| $featureFlagManager = StaticContainer::get(FeatureFlagManager::class); |
| $view->useNewSparklinesGrid = $featureFlagManager->isFeatureActive(SparklinesRedesign::class) |
| && !$this->isComparing(); |
|
|
| $view->title = ''; |
| if ($this->config->show_title) { |
| $view->title = $this->config->title; |
| } |
|
|
| return $view->render(); |
| } |
|
|
| |
| |
| |
| |
| |
| |
| |
| protected function loadDataTableFromAPI(array $forcedParams = []) |
| { |
| if (!is_null($this->dataTable)) { |
| |
| |
| return $this->dataTable; |
| } |
|
|
| if ($this->isComparing()) { |
| $forcedParams['compare'] = '1'; |
| } |
|
|
| $this->dataTable = $this->request->loadDataTableFromAPI($forcedParams); |
|
|
| return $this->dataTable; |
| } |
|
|
| private function fetchConfiguredSparklines() |
| { |
| $data = $this->loadDataTableFromAPI(['format_metrics' => '0']); |
|
|
| $this->applyFilters($data); |
|
|
| if (!$this->config->hasSparklineMetrics()) { |
| foreach ($data->getColumns() as $column) { |
| $this->config->addSparklineMetric($column); |
| } |
| } |
|
|
| $report = ReportsProvider::factory($this->requestConfig->getApiModuleToRequest(), $this->requestConfig->getApiMethodToRequest()); |
| $processedMetrics = Report::getProcessedMetricsForTable($data, $report); |
| $metricFormatter = new MetricFormatter(); |
| $idSite = $this->getRequestArray()['idSite'] ?? false; |
| $metricTranslations = Metrics::getDefaultMetricTranslations(); |
|
|
| $firstRow = $data->getFirstRow(); |
| if ($firstRow) { |
| $comparisons = $firstRow->getComparisons(); |
| } else { |
| $comparisons = null; |
| } |
|
|
| $originalDate = Common::getRequestVar('date'); |
| $originalPeriod = Common::getRequestVar('period'); |
|
|
| $comparisonRows = []; |
| $isComparing = $this->isComparing() && !empty($comparisons); |
| if ($isComparing) { |
| foreach ($comparisons->getRows() as $comparisonRow) { |
| $segment = $comparisonRow->getMetadata('compareSegment'); |
| if ($segment === false) { |
| $segment = Request::getRawSegmentFromRequest() ?: ''; |
| } |
|
|
| $date = $comparisonRow->getMetadata('compareDate'); |
| $period = $comparisonRow->getMetadata('comparePeriod'); |
|
|
| $comparisonRows[$segment][$period][$date] = $comparisonRow; |
| } |
| } |
|
|
| foreach ($this->config->getSparklineMetrics() as $sparklineMetricIndex => $sparklineMetric) { |
| $column = $sparklineMetric['columns']; |
| $order = $sparklineMetric['order']; |
| $graphParams = $sparklineMetric['graphParams']; |
|
|
| if (!isset($order)) { |
| $order = 1000; |
| } |
|
|
| if ($column === 'label') { |
| continue; |
| } |
|
|
| if (empty($column)) { |
| $this->config->addPlaceholder($order); |
| continue; |
| } |
|
|
| if (!is_array($column)) { |
| $column = [$column]; |
| } |
|
|
| $columnMetrics = []; |
| foreach ($column as $col) { |
| foreach ($processedMetrics as $metricObj) { |
| if ($metricObj->getName() === $col) { |
| $columnMetrics[$col] = $metricObj; |
| break; |
| } |
| } |
| } |
|
|
| $sparklineUrlParams = array_merge($this->config->custom_parameters, [ |
| 'columns' => $column, |
| 'module' => $this->requestConfig->getApiModuleToRequest(), |
| 'action' => $this->requestConfig->getApiMethodToRequest(), |
| ]); |
|
|
| $periodObj = Factory::build($originalPeriod, $originalDate); |
| $comparePeriods = $data->getMetadata('comparePeriods'); |
| $compareDates = $data->getMetadata('compareDates'); |
|
|
| |
| $compareDatesWithoutOriginalDate = $compareDates ? array_slice($compareDates, 1) : []; |
| $comparePeriodsWithoutOriginalPeriod = $comparePeriods ? array_slice($comparePeriods, 1) : []; |
|
|
| $periodSelector = new EvolutionPeriodSelector($this->config); |
| $comparisonPeriods = $periodSelector->getComparisonPeriodObjects($comparePeriodsWithoutOriginalPeriod, $compareDatesWithoutOriginalDate); |
| $sparklineUrlParams = $periodSelector->setDatePeriods($sparklineUrlParams, $periodObj, $comparisonPeriods, $isComparing); |
|
|
| if ($isComparing) { |
| $sparklineUrlParams['compareSegments'] = []; |
|
|
| $compareSegments = $data->getMetadata('compareSegments'); |
| foreach ($compareSegments as $segmentIndex => $segment) { |
| $metrics = []; |
| $seriesIndices = []; |
|
|
| foreach ($comparePeriods as $periodIndex => $period) { |
| $date = $compareDates[$periodIndex]; |
|
|
| $compareRow = $this->findComparisonRow($comparisonRows, $segment, $period, $date); |
| if (!$compareRow) { |
| continue; |
| } |
| $segmentPretty = $compareRow->getMetadata('compareSegmentPretty'); |
| $periodPretty = $compareRow->getMetadata('comparePeriodPretty'); |
|
|
| $columnToUse = $this->removeUniqueVisitorsIfNotEnabledForPeriod($column, $period); |
|
|
| [$compareValues, $compareDescriptions, $evolutions] = $this->getValuesAndDescriptions($compareRow, $columnToUse, '_change', '_trend'); |
|
|
| foreach ($compareValues as $i => $value) { |
| if (!isset($column[$i])) { |
| continue; |
| } |
| $formattedValue = $this->formatSparklineMetricValue( |
| $value, |
| $columnToUse[$i], |
| $columnMetrics, |
| $metricFormatter, |
| $idSite |
| ); |
|
|
| $metricInfo = [ |
| 'value' => $formattedValue, |
| 'description' => $compareDescriptions[$i], |
| 'title' => $metricTranslations[$columnToUse[$i]] ?? $compareDescriptions[$i], |
| 'group' => $periodPretty, |
| ]; |
|
|
| if (isset($evolutions[$i])) { |
| $comparisonIndex = $periodIndex === 0 ? 1 : 0; |
| $comparisonRow = $comparePeriods[$comparisonIndex] ?? false; |
| $comparisonDate = $compareDates[$comparisonIndex] ?? false; |
| $originalCompareRow = ($comparisonRow && $comparisonDate) |
| ? $this->findComparisonRow($comparisonRows, $segment, $comparisonRow, $comparisonDate) |
| : false; |
| $originalValue = $originalCompareRow ? $originalCompareRow->getColumn($columnToUse[$i]) : 0; |
| if ($originalValue === false) { |
| $originalValue = 0; |
| } |
| $originalPeriodPretty = $originalCompareRow |
| ? $originalCompareRow->getMetadata('comparePeriodPretty') |
| : ''; |
| $formattedOriginalValue = $this->formatSparklineMetricValue( |
| $originalValue, |
| $columnToUse[$i], |
| $columnMetrics, |
| $metricFormatter, |
| $idSite |
| ); |
| $evolutions[$i]['tooltip'] = Piwik::translate('General_EvolutionSummaryGeneric', [ |
| $formattedValue . ' ' . $compareDescriptions[$i], |
| $periodPretty, |
| $formattedOriginalValue . ' ' . $compareDescriptions[$i], |
| $originalPeriodPretty, |
| ltrim((string) $evolutions[$i]['percent'], '+'), |
| ]); |
| $metricInfo['evolution'] = $evolutions[$i]; |
| } |
|
|
| $metrics[] = $metricInfo; |
| } |
|
|
| $seriesIndices[] = DataComparisonFilter::getComparisonSeriesIndex($data, $periodIndex, $segmentIndex); |
| } |
|
|
| |
| $title = count($compareSegments) > 1 ? $segmentPretty : null; |
|
|
| $params = array_merge($sparklineUrlParams, [ |
| 'segment' => $segment, |
| ]); |
|
|
| $this->config->addSparkline($params, $metrics, $desc = null, null, ($order * 100) + $segmentIndex, $title, $sparklineMetricIndex, $seriesIndices, $graphParams); |
| } |
| } else { |
| [$values, $descriptions] = $this->getValuesAndDescriptions($firstRow, $column); |
|
|
| $metrics = []; |
| foreach ($values as $i => $value) { |
| if (!isset($column[$i])) { |
| continue; |
| } |
| $newMetric = [ |
| 'value' => $this->formatSparklineMetricValue( |
| $value, |
| $column[$i], |
| $columnMetrics, |
| $metricFormatter, |
| $idSite |
| ), |
| 'description' => $descriptions[$i], |
| 'title' => $metricTranslations[$column[$i]] ?? $descriptions[$i], |
| ]; |
|
|
| $metrics[] = $newMetric; |
| } |
|
|
| $evolution = null; |
|
|
| $computeEvolution = $this->config->compute_evolution; |
| if ($computeEvolution) { |
| $evolution = $computeEvolution(array_combine((is_array($column) ? $column : [$column]), $values), $processedMetrics); |
| $newMetric['evolution'] = $evolution; |
| } |
|
|
| $this->config->addSparkline($sparklineUrlParams, $metrics, $desc = null, $evolution, $order, $title = null, $group = $sparklineMetricIndex, $seriesIndices = null, $graphParams); |
| } |
| } |
| } |
|
|
| private function applyFilters(DataTable\DataTableInterface $table) |
| { |
| foreach ($this->config->getPriorityFilters() as $filter) { |
| $table->filter($filter[0], $filter[1]); |
| } |
|
|
| |
| foreach ($this->config->getPresentationFilters() as $filter) { |
| $table->queueFilter($filter[0], $filter[1]); |
| } |
|
|
| $table->applyQueuedFilters(); |
| } |
|
|
| private function getValuesAndDescriptions($firstRow, $columns, $evolutionColumnNameSuffix = null, $trendColumnNameSuffix = null) |
| { |
| if (!is_array($columns)) { |
| $columns = [$columns]; |
| } |
|
|
| $translations = $this->config->translations; |
|
|
| $values = []; |
| $descriptions = []; |
| $evolutions = []; |
|
|
| foreach ($columns as $col) { |
| $value = 0; |
| if ($firstRow) { |
| $value = $firstRow->getColumn($col); |
| } |
|
|
| if ($value === false) { |
| $value = 0; |
| } |
|
|
| if ($evolutionColumnNameSuffix !== null) { |
| $evolution = $firstRow->getColumn($col . $evolutionColumnNameSuffix); |
| $trend = $firstRow->getColumn($col . $trendColumnNameSuffix); |
| if ($evolution !== false) { |
| $evolutions[] = [ |
| 'percent' => ltrim($evolution, '+'), |
| 'trend' => $trend, |
| 'tooltip' => '', |
| 'isLowerValueBetter' => Metrics::isLowerValueBetter($col), |
| ]; |
| } |
| } |
|
|
| $values[] = $value; |
| $descriptions[] = isset($translations[$col]) ? $translations[$col] : $col; |
| } |
|
|
| return [$values, $descriptions, $evolutions]; |
| } |
|
|
| private function removeUniqueVisitorsIfNotEnabledForPeriod($columns, $period) |
| { |
| if (SettingsPiwik::isUniqueVisitorsEnabled($period)) { |
| return $columns; |
| } |
|
|
| if (!is_array($columns)) { |
| $columns = [$columns]; |
| } |
|
|
| return array_diff($columns, ['nb_users', 'nb_uniq_visitors']); |
| } |
|
|
| private function formatSparklineMetricValue($value, string $columnName, array $columnMetrics, MetricFormatter $metricFormatter, int $idSite) |
| { |
| if (strpos($columnName, 'revenue') !== false && $idSite > 0) { |
| return $metricFormatter->getPrettyMoney($value, $idSite); |
| } |
|
|
| if (isset($columnMetrics[$columnName]) && $columnMetrics[$columnName]) { |
| return $columnMetrics[$columnName]->format($value, $metricFormatter); |
| } |
|
|
| return $value; |
| } |
|
|
| private function findComparisonRow(array $comparisonRows, string $segment, string $period, string $date): ?DataTable\Row |
| { |
| if (isset($comparisonRows[$segment][$period][$date])) { |
| return $comparisonRows[$segment][$period][$date]; |
| } |
|
|
| if (strpos($date, ',') === false) { |
| $rangeDate = Factory::build($period, $date)->getRangeString(); |
| if (isset($comparisonRows[$segment][$period][$rangeDate])) { |
| return $comparisonRows[$segment][$period][$rangeDate]; |
| } |
| } |
|
|
| return null; |
| } |
| } |
|
|