
<center><h2><strong>Ubuntu</strong></h2>
­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­
<!DOCTYPE html>
<html>
<?php

/**
 * Matomo - free/libre analytics platform
 *
 * @link    https://matomo.org
 * @license https://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
 */
namespace Piwik\API\DataTableManipulator;

use Piwik\API\DataTableManipulator;
use Piwik\DataTable;
use Piwik\DataTable\Row;
use Piwik\Plugin\ReportsProvider;
/**
 * This class is responsible for flattening data tables.
 *
 * It loads subtables and combines them into a single table by concatenating the labels.
 * This manipulator is triggered by using flat=1 in the API request.
 */
class Flattener extends DataTableManipulator
{
    private $includeAggregateRows = \false;
    /**
     * If the flattener is used after calling this method, aggregate rows will
     * be included in the result. This can be useful when they contain data that
     * the leafs don't have (e.g. conversion stats in some cases).
     */
    public function includeAggregateRows()
    {
        $this->includeAggregateRows = \true;
    }
    /**
     * Separator for building recursive labels (or paths)
     * @var string
     */
    public $recursiveLabelSeparator = '';
    /**
     * Defines if dimensions should be added as additional columns if there are more than one
     * @var bool
     */
    public $showDimensions = \false;
    /**
     * @param  DataTable $dataTable
     * @param string $recursiveLabelSeparator
     * @return DataTable|DataTable\Map
     */
    public function flatten($dataTable, $recursiveLabelSeparator, bool $showDimensions)
    {
        $this->recursiveLabelSeparator = $recursiveLabelSeparator;
        $this->showDimensions = $showDimensions;
        return $this->manipulate($dataTable);
    }
    /**
     * Template method called from self::manipulate.
     * Flatten each data table.
     *
     * @param DataTable $dataTable
     * @return DataTable
     */
    protected function manipulateDataTable($dataTable)
    {
        $newDataTable = $dataTable->getEmptyClone($keepFilters = \true);
        if ($dataTable->getTotalsRow()) {
            $newDataTable->setTotalsRow($dataTable->getTotalsRow());
        }
        // this recursive filter will be applied to subtables
        $dataTable->filter('ReplaceSummaryRowLabel');
        $dataTable->filter('ReplaceColumnNames');
        $report = ReportsProvider::factory($this->apiModule, $this->apiMethod);
        if (!empty($report)) {
            $dimension = $report->getDimension();
        }
        $dimensionName = !empty($dimension) ? str_replace('.', '_', $dimension->getId()) : 'label1';
        $this->flattenDataTableInto($dataTable, $newDataTable, $level = 1, $dimensionName);
        $dimensions = $newDataTable->getMetadata('dimensions');
        $hasMultipleDimensions = is_array($dimensions) && count($dimensions) > 1;
        if ($this->showDimensions && $hasMultipleDimensions) {
            $newDataTable->filter(function ($dataTable) use($dimensions) {
                /** @var DataTable $dataTable */
                $rows = $dataTable->getRows();
                foreach ($rows as $row) {
                    foreach ($dimensions as $dimension) {
                        $row->setColumn($dimension, $row->getMetadata($dimension));
                    }
                }
            });
        }
        return $newDataTable;
    }
    /**
     * @param $dataTable DataTable
     * @param $newDataTable
     * @param $dimensionName
     */
    protected function flattenDataTableInto($dataTable, $newDataTable, $level, $dimensionName, $prefix = '', $logo = \false)
    {
        foreach ($dataTable->getRows() as $rowId => $row) {
            $this->flattenRow($row, $rowId, $newDataTable, $level, $dimensionName, $prefix, $logo);
        }
    }
    /**
     * @param string $labelPrefix
     * @param string $dimensionName
     * @param bool $parentLogo
     */
    private function flattenRow(Row $row, $rowId, DataTable $dataTable, $level, $dimensionName, $labelPrefix = '', $parentLogo = \false)
    {
        $dimensions = $dataTable->getMetadata('dimensions');
        if (empty($dimensions)) {
            $dimensions = [];
        }
        if (!in_array($dimensionName, $dimensions)) {
            $dimensions[] = $dimensionName;
        }
        $dataTable->setMetadata('dimensions', $dimensions);
        $origLabel = $label = $row->getColumn('label');
        if ($label !== \false) {
            $origLabel = $label = trim($label);
            if ($this->recursiveLabelSeparator == '/') {
                if (substr($label, 0, 1) == '/' && substr($labelPrefix, -1) == '/') {
                    $origLabel = $label = substr($label, 1);
                } elseif ($rowId === DataTable::ID_SUMMARY_ROW && $labelPrefix && $label != DataTable::LABEL_SUMMARY_ROW) {
                    $label = ' - ' . $label;
                }
            }
            if ($rowId === DataTable::ID_SUMMARY_ROW) {
                if ($row->getMetadata('url')) {
                    // remove url metadata for flattened summary rows
                    $row->deleteMetadata('url');
                }
                $row->setMetadata('is_summary', \true);
            }
            $label = $labelPrefix . $label;
            $row->setColumn('label', $label);
            if ($row->getMetadata($dimensionName)) {
                if ($rowId === DataTable::ID_SUMMARY_ROW && $this->recursiveLabelSeparator == '/') {
                    $origLabel = $row->getMetadata($dimensionName) . $this->recursiveLabelSeparator . ' - ' . $origLabel;
                } else {
                    $origLabel = $row->getMetadata($dimensionName) . $this->recursiveLabelSeparator . $origLabel;
                }
            }
            $row->setMetadata($dimensionName, $origLabel);
        }
        $logo = $row->getMetadata('logo');
        if ($logo === \false && $parentLogo !== \false) {
            $logo = $parentLogo;
            $row->setMetadata('logo', $logo);
        }
        /** @var DataTable $subTable */
        $subTable = $row->getSubtable();
        if ($subTable) {
            $subTable->applyQueuedFilters();
            $row->deleteMetadata('idsubdatatable_in_db');
        } else {
            $subTable = $this->loadSubtable($dataTable, $row);
        }
        $row->removeSubtable();
        if ($subTable === null) {
            if ($this->includeAggregateRows) {
                $row->setMetadata('is_aggregate', 0);
            }
            $dataTable->addRow($row);
        } else {
            if ($this->includeAggregateRows) {
                $row->setMetadata('is_aggregate', 1);
                $dataTable->addRow($row);
            }
            $prefix = $label . $this->recursiveLabelSeparator;
            $report = ReportsProvider::factory($this->apiModule, $this->apiMethod);
            if (!empty($report)) {
                $subDimension = $report->getSubtableDimension();
            }
            if ($level > 1) {
                $subDimension = $report->getNthLevelTableDimension($level);
            }
            if (empty($subDimension)) {
                $report = ReportsProvider::factory($this->apiModule, $this->getApiMethodForSubtable($this->request));
                $subDimension = $report->getDimension();
            }
            $subDimensionName = $subDimension ? str_replace('.', '_', $subDimension->getId()) : 'label' . (substr_count($prefix, $this->recursiveLabelSeparator) + 1);
            if ($origLabel !== \false) {
                foreach ($subTable->getRows() as $subRow) {
                    foreach ($row->getMetadata() as $name => $value) {
                        // do not set 'segment' parameter if there is a segmentValue on the row, since that will prevent the segmentValue
                        // from being used in DataTablePostProcessor
                        if ($name == 'segment' && $subRow->getMetadata('segmentValue') !== \false) {
                            continue;
                        }
                        if ($subRow->getMetadata($name) === \false) {
                            $subRow->setMetadata($name, $value);
                        }
                    }
                    $subRow->setMetadata($dimensionName, $origLabel);
                }
            }
            $this->flattenDataTableInto($subTable, $dataTable, $level + 1, $subDimensionName, $prefix, $logo);
        }
    }
    /**
     * Remove the flat parameter from the subtable request
     *
     * @param array $request
     * @return array
     */
    protected function manipulateSubtableRequest($request)
    {
        unset($request['flat']);
        return $request;
    }
}
