Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/matomo-org/matomo.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
path: root/core
diff options
context:
space:
mode:
authorMatthieu Aubry <matt@piwik.org>2015-03-09 05:57:12 +0300
committerMatthieu Aubry <matt@piwik.org>2015-03-09 05:57:12 +0300
commit5dfad9401e33215e4bf35bea41f233b00b048db6 (patch)
tree40141b24342e555be840619b0356421366ef5cfd /core
parentb91058db45e645ea8ec4d28085238ca88733ef6c (diff)
parentec4ded41c8df059d8f3817552e8c1738e7633771 (diff)
Merge pull request #7336 from piwik/fast_flatten_2
Faster flatten for some reports
Diffstat (limited to 'core')
-rw-r--r--core/API/DataTableManipulator/Flattener.php41
-rw-r--r--core/API/DataTableManipulator/ReportTotalsCalculator.php27
-rw-r--r--core/API/DataTablePostProcessor.php10
-rw-r--r--core/Archive.php33
-rw-r--r--core/DataTable.php69
-rwxr-xr-xcore/DataTable/Filter/ColumnCallbackAddColumn.php28
-rw-r--r--core/DataTable/Filter/Sort.php101
-rw-r--r--core/DataTable/Filter/Truncate.php2
-rw-r--r--core/DataTable/Map.php33
-rw-r--r--core/DataTable/Row.php44
-rw-r--r--core/Plugin/API.php2
-rw-r--r--core/Plugin/Metric.php45
-rw-r--r--core/Plugin/Report.php16
-rw-r--r--core/UrlHelper.php5
14 files changed, 323 insertions, 133 deletions
diff --git a/core/API/DataTableManipulator/Flattener.php b/core/API/DataTableManipulator/Flattener.php
index cfee16e6ba..a976b7a62e 100644
--- a/core/API/DataTableManipulator/Flattener.php
+++ b/core/API/DataTableManipulator/Flattener.php
@@ -38,17 +38,16 @@ class Flattener extends DataTableManipulator
* Separator for building recursive labels (or paths)
* @var string
*/
- public $recursiveLabelSeparator = ' - ';
+ public $recursiveLabelSeparator = '';
/**
* @param DataTable $dataTable
+ * @param string $recursiveLabelSeparator
* @return DataTable|DataTable\Map
*/
- public function flatten($dataTable)
+ public function flatten($dataTable, $recursiveLabelSeparator)
{
- if ($this->apiModule == 'Actions' || $this->apiMethod == 'getWebsites') {
- $this->recursiveLabelSeparator = '/';
- }
+ $this->recursiveLabelSeparator = $recursiveLabelSeparator;
return $this->manipulate($dataTable);
}
@@ -72,9 +71,10 @@ class Flattener extends DataTableManipulator
}
$newDataTable = $dataTable->getEmptyClone($keepFilters);
- foreach ($dataTable->getRows() as $row) {
- $this->flattenRow($row, $newDataTable);
+ foreach ($dataTable->getRows() as $rowId => $row) {
+ $this->flattenRow($row, $rowId, $newDataTable);
}
+
return $newDataTable;
}
@@ -84,15 +84,21 @@ class Flattener extends DataTableManipulator
* @param string $labelPrefix
* @param bool $parentLogo
*/
- private function flattenRow(Row $row, DataTable $dataTable,
+ private function flattenRow(Row $row, $rowId, DataTable $dataTable,
$labelPrefix = '', $parentLogo = false)
{
$label = $row->getColumn('label');
if ($label !== false) {
$label = trim($label);
- if (substr($label, 0, 1) == '/' && $this->recursiveLabelSeparator == '/') {
- $label = substr($label, 1);
+
+ if ($this->recursiveLabelSeparator == '/') {
+ if (substr($label, 0, 1) == '/') {
+ $label = substr($label, 1);
+ } elseif ($rowId === DataTable::ID_SUMMARY_ROW && $labelPrefix && $label != DataTable::LABEL_SUMMARY_ROW) {
+ $label = ' - ' . $label;
+ }
}
+
$label = $labelPrefix . $label;
$row->setColumn('label', $label);
}
@@ -103,7 +109,16 @@ class Flattener extends DataTableManipulator
$row->setMetadata('logo', $logo);
}
- $subTable = $this->loadSubtable($dataTable, $row);
+ /** @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) {
@@ -117,8 +132,8 @@ class Flattener extends DataTableManipulator
$dataTable->addRow($row);
}
$prefix = $label . $this->recursiveLabelSeparator;
- foreach ($subTable->getRows() as $row) {
- $this->flattenRow($row, $dataTable, $prefix, $logo);
+ foreach ($subTable->getRows() as $rowId => $row) {
+ $this->flattenRow($row, $rowId, $dataTable, $prefix, $logo);
}
}
}
diff --git a/core/API/DataTableManipulator/ReportTotalsCalculator.php b/core/API/DataTableManipulator/ReportTotalsCalculator.php
index b6b82effad..4703c36c7d 100644
--- a/core/API/DataTableManipulator/ReportTotalsCalculator.php
+++ b/core/API/DataTableManipulator/ReportTotalsCalculator.php
@@ -66,14 +66,19 @@ class ReportTotalsCalculator extends DataTableManipulator
$firstLevelTable = $this->makeSureToWorkOnFirstLevelDataTable($dataTable);
$metricsToCalculate = Metrics::getMetricIdsToProcessReportTotal();
+ $realMetricNames = array();
foreach ($metricsToCalculate as $metricId) {
- $realMetricName = $this->hasDataTableMetric($firstLevelTable, $metricId);
- if (empty($realMetricName)) {
- continue;
+ $metricName = Metrics::getReadableColumnName($metricId);
+ $realMetricName = $this->hasDataTableMetric($firstLevelTable, $metricId, $metricName);
+ if (!empty($realMetricName)) {
+ $realMetricNames[$metricName] = $realMetricName;
}
+ }
- foreach ($firstLevelTable->getRows() as $row) {
- $totalValues = $this->sumColumnValueToTotal($row, $metricId, $realMetricName, $totalValues);
+ foreach ($firstLevelTable->getRows() as $row) {
+ $columns = $row->getColumns();
+ foreach ($realMetricNames as $metricName => $realMetricName) {
+ $totalValues = $this->sumColumnValueToTotal($columns, $metricName, $realMetricName, $totalValues);
}
}
@@ -82,7 +87,7 @@ class ReportTotalsCalculator extends DataTableManipulator
return $dataTable;
}
- private function hasDataTableMetric(DataTable $dataTable, $metricId)
+ private function hasDataTableMetric(DataTable $dataTable, $metricId, $readableColumnName)
{
$firstRow = $dataTable->getFirstRow();
@@ -90,7 +95,6 @@ class ReportTotalsCalculator extends DataTableManipulator
return false;
}
- $readableColumnName = Metrics::getReadableColumnName($metricId);
$columnAlternatives = array(
$metricId,
$readableColumnName,
@@ -151,17 +155,18 @@ class ReportTotalsCalculator extends DataTableManipulator
return $table;
}
- private function sumColumnValueToTotal(Row $row, $metricId, $realMetricId, $totalValues)
+ private function sumColumnValueToTotal($columns, $metricName, $realMetricId, $totalValues)
{
- $value = $row->getColumn($realMetricId);
+ $value = false;
+ if (array_key_exists($realMetricId, $columns)) {
+ $value = $columns[$realMetricId];
+ }
if (false === $value) {
return $totalValues;
}
- $metricName = Metrics::getReadableColumnName($metricId);
-
if (array_key_exists($metricName, $totalValues)) {
$totalValues[$metricName] += $value;
} else {
diff --git a/core/API/DataTablePostProcessor.php b/core/API/DataTablePostProcessor.php
index 999b3fe339..b13fff1439 100644
--- a/core/API/DataTablePostProcessor.php
+++ b/core/API/DataTablePostProcessor.php
@@ -85,8 +85,8 @@ class DataTablePostProcessor
// this is non-trivial since it will require, eg, to make sure processed metrics aren't added
// after pivotBy is handled.
$dataTable = $this->applyPivotByFilter($dataTable);
- $dataTable = $this->applyFlattener($dataTable);
$dataTable = $this->applyTotalsCalculator($dataTable);
+ $dataTable = $this->applyFlattener($dataTable);
$dataTable = $this->applyGenericFilters($dataTable);
@@ -145,7 +145,13 @@ class DataTablePostProcessor
if (Common::getRequestVar('include_aggregate_rows', '0', 'string', $this->request) == '1') {
$flattener->includeAggregateRows();
}
- $dataTable = $flattener->flatten($dataTable);
+
+ $recursiveLabelSeparator = ' - ';
+ if ($this->report) {
+ $recursiveLabelSeparator = $this->report->getRecursiveLabelSeparator();
+ }
+
+ $dataTable = $flattener->flatten($dataTable, $recursiveLabelSeparator);
}
return $dataTable;
}
diff --git a/core/Archive.php b/core/Archive.php
index 1931b6a343..c7eb6e3106 100644
--- a/core/Archive.php
+++ b/core/Archive.php
@@ -452,6 +452,7 @@ class Archive
* @return DataTable|DataTable\Map See {@link getDataTable()} and
* {@link getDataTableExpanded()} for more
* information
+ * @deprecated Since Piwik 2.12.0 Use Archive::createDataTableFromArchive() instead
*/
public static function getDataTableFromArchive($name, $idSite, $period, $date, $segment, $expanded,
$idSubtable = null, $depth = null)
@@ -474,6 +475,38 @@ class Archive
return $dataTable;
}
+ /**
+ * Helper function that creates an Archive instance and queries for report data using
+ * query parameter data. API methods can use this method to reduce code redundancy.
+ *
+ * @param string $recordName The name of the report to return.
+ * @param int|string|array $idSite @see {@link build()}
+ * @param string $period @see {@link build()}
+ * @param string $date @see {@link build()}
+ * @param string $segment @see {@link build()}
+ * @param bool $expanded If true, loads all subtables. See {@link getDataTableExpanded()}
+ * @param bool $flat If true, loads all subtables and disabled all recursive filters.
+ * @param int|null $idSubtable See {@link getDataTableExpanded()}
+ * @param int|null $depth See {@link getDataTableExpanded()}
+ * @return DataTable|DataTable\Map
+ */
+ public static function createDataTableFromArchive($recordName, $idSite, $period, $date, $segment, $expanded = false, $flat = false, $idSubtable = null, $depth = null)
+ {
+ if ($flat && !$idSubtable) {
+ $expanded = true;
+ }
+
+ $dataTable = self::getDataTableFromArchive($recordName, $idSite, $period, $date, $segment, $expanded, $idSubtable, $depth);
+
+ $dataTable->filter('ReplaceColumnNames');
+
+ if ($flat) {
+ $dataTable->disableRecursiveFilters();
+ }
+
+ return $dataTable;
+ }
+
private function appendIdSubtable($recordName, $id)
{
return $recordName . "_" . $id;
diff --git a/core/DataTable.php b/core/DataTable.php
index b060a87245..0b0d9845f0 100644
--- a/core/DataTable.php
+++ b/core/DataTable.php
@@ -342,6 +342,17 @@ class DataTable implements DataTableInterface, \IteratorAggregate, \ArrayAccess
}
/**
+ * @ignore
+ * does not update the summary row!
+ */
+ public function setRows($rows)
+ {
+ unset($this->rows);
+ $this->rows = $rows;
+ $this->indexNotUpToDate = true;
+ }
+
+ /**
* Sorts the DataTable rows using the supplied callback function.
*
* @param string $functionCallback A comparison callback compatible with {@link usort}.
@@ -350,11 +361,11 @@ class DataTable implements DataTableInterface, \IteratorAggregate, \ArrayAccess
*/
public function sort($functionCallback, $columnSortedBy)
{
- $this->indexNotUpToDate = true;
- $this->tableSortedBy = $columnSortedBy;
+ $this->setTableSortedBy($columnSortedBy);
+
usort($this->rows, $functionCallback);
- if ($this->enableRecursiveSort === true) {
+ if ($this->isSortRecursiveEnabled()) {
foreach ($this->getRows() as $row) {
$subTable = $row->getSubtable();
@@ -388,6 +399,23 @@ class DataTable implements DataTableInterface, \IteratorAggregate, \ArrayAccess
}
/**
+ * @ignore
+ */
+ public function isSortRecursiveEnabled()
+ {
+ return $this->enableRecursiveSort === true;
+ }
+
+ /**
+ * @ignore
+ */
+ public function setTableSortedBy($column)
+ {
+ $this->indexNotUpToDate = true;
+ $this->tableSortedBy = $column;
+ }
+
+ /**
* Enables recursive filtering. If this method is called then the {@link filter()} method
* will apply filters to every subtable in addition to this instance.
*/
@@ -397,6 +425,14 @@ class DataTable implements DataTableInterface, \IteratorAggregate, \ArrayAccess
}
/**
+ * @ignore
+ */
+ public function disableRecursiveFilters()
+ {
+ $this->enableRecursiveFilters = false;
+ }
+
+ /**
* Applies a filter to this datatable.
*
* If {@link enableRecursiveFilters()} was called, the filter will be applied
@@ -434,6 +470,25 @@ class DataTable implements DataTableInterface, \IteratorAggregate, \ArrayAccess
}
/**
+ * Applies a filter to all subtables but not to this datatable.
+ *
+ * @param string|Closure $className Class name, eg. `"Sort"` or "Piwik\DataTable\Filters\Sort"`. If no
+ * namespace is supplied, `Piwik\DataTable\BaseFilter` is assumed. This parameter
+ * can also be a closure that takes a DataTable as its first parameter.
+ * @param array $parameters Array of extra parameters to pass to the filter.
+ */
+ public function filterSubtables($className, $parameters = array())
+ {
+ foreach ($this->getRows() as $row) {
+ $subtable = $row->getSubtable();
+ if ($subtable) {
+ $subtable->filter($className, $parameters);
+ $subtable->filterSubtables($className, $parameters);
+ }
+ }
+ }
+
+ /**
* Adds a filter and a list of parameters to the list of queued filters. These filters will be
* executed when {@link applyQueuedFilters()} is called.
*
@@ -730,6 +785,14 @@ class DataTable implements DataTableInterface, \IteratorAggregate, \ArrayAccess
}
/**
+ * @ignore
+ */
+ public function getRowsWithoutSummaryRow()
+ {
+ return $this->rows;
+ }
+
+ /**
* Returns an array containing all column values for the requested column.
*
* @param string $name The column name.
diff --git a/core/DataTable/Filter/ColumnCallbackAddColumn.php b/core/DataTable/Filter/ColumnCallbackAddColumn.php
index 27746c4f28..1d81f189be 100755
--- a/core/DataTable/Filter/ColumnCallbackAddColumn.php
+++ b/core/DataTable/Filter/ColumnCallbackAddColumn.php
@@ -10,6 +10,7 @@ namespace Piwik\DataTable\Filter;
use Piwik\DataTable;
use Piwik\DataTable\BaseFilter;
+use Piwik\Plugins\CoreHome\Columns\Metrics\CallableProcessedMetric;
/**
* Adds a new column to every row of a {@link DataTable} based on the result of callback.
@@ -83,20 +84,29 @@ class ColumnCallbackAddColumn extends BaseFilter
$functionParams = $this->functionParameters;
$functionToApply = $this->functionToApply;
- foreach ($table->getRows() as $row) {
+ $extraProcessedMetrics = $table->getMetadata(DataTable::EXTRA_PROCESSED_METRICS_METADATA_NAME);
+
+ if (empty($extraProcessedMetrics)) {
+ $extraProcessedMetrics = array();
+ }
- $row->setColumn($this->columnToAdd, function (DataTable\Row $row) use ($columns, $functionParams, $functionToApply) {
+ $metric = new CallableProcessedMetric($this->columnToAdd, function (DataTable\Row $row) use ($columns, $functionParams, $functionToApply) {
- $columnValues = array();
- foreach ($columns as $column) {
- $columnValues[] = $row->getColumn($column);
- }
+ $columnValues = array();
+ foreach ($columns as $column) {
+ $columnValues[] = $row->getColumn($column);
+ }
- $parameters = array_merge($columnValues, $functionParams);
+ $parameters = array_merge($columnValues, $functionParams);
- return call_user_func_array($functionToApply, $parameters);
- });
+ return call_user_func_array($functionToApply, $parameters);
+ }, $columns);
+ $extraProcessedMetrics[] = $metric;
+ $table->setMetadata(DataTable::EXTRA_PROCESSED_METRICS_METADATA_NAME, $extraProcessedMetrics);
+
+ foreach ($table->getRows() as $row) {
+ $row->setColumn($this->columnToAdd, $metric->compute($row));
$this->filterSubTable($row);
}
}
diff --git a/core/DataTable/Filter/Sort.php b/core/DataTable/Filter/Sort.php
index 42441c8e30..632da35dc8 100644
--- a/core/DataTable/Filter/Sort.php
+++ b/core/DataTable/Filter/Sort.php
@@ -21,7 +21,8 @@ use Piwik\Metrics;
*
* @api
*/
-class Sort extends BaseFilter
+class
+Sort extends BaseFilter
{
protected $columnToSort;
protected $order;
@@ -71,34 +72,21 @@ class Sort extends BaseFilter
* @param Row $b
* @return int
*/
- public function numberSort($a, $b)
+ public function numberSort($rowA, $rowB)
{
- $valA = $this->getColumnValue($a);
- $valB = $this->getColumnValue($b);
+ if (isset($rowA[0]) && isset($rowB[0])) {
+ if ($rowA[0] != $rowB[0] || !isset($rowA[1])) {
+ return $this->sign * ($rowA[0] < $rowB[0] ? -1 : 1);
+ } else {
+ return -1 * $this->sign * strnatcasecmp($rowA[1], $rowB[1]);
+ }
+ } elseif (!isset($rowB[0])) {
+ return -1;
+ } elseif (!isset($rowA[0])) {
+ return 1;
+ }
- return !isset($valA)
- && !isset($valB)
- ? 0
- : (
- !isset($valA)
- ? 1
- : (
- !isset($valB)
- ? -1
- : (($valA != $valB
- || !isset($a->c[Row::COLUMNS]['label']))
- ? ($this->sign * (
- $valA
- < $valB
- ? -1
- : 1)
- )
- : -1 * $this->sign * strnatcasecmp(
- $a->c[Row::COLUMNS]['label'],
- $b->c[Row::COLUMNS]['label'])
- )
- )
- );
+ return 0;
}
/**
@@ -108,10 +96,10 @@ class Sort extends BaseFilter
* @param mixed $b
* @return int
*/
- function naturalSort($a, $b)
+ function naturalSort($rowA, $rowB)
{
- $valA = $this->getColumnValue($a);
- $valB = $this->getColumnValue($b);
+ $valA = $rowA[0];
+ $valB = $rowB[0];
return !isset($valA)
&& !isset($valB)
@@ -135,10 +123,10 @@ class Sort extends BaseFilter
* @param mixed $b
* @return int
*/
- function sortString($a, $b)
+ function sortString($rowA, $rowB)
{
- $valA = $this->getColumnValue($a);
- $valB = $this->getColumnValue($b);
+ $valA = $rowA[0];
+ $valB = $rowB[0];
return !isset($valA)
&& !isset($valB)
@@ -243,6 +231,51 @@ class Sort extends BaseFilter
}
}
- $table->sort(array($this, $methodToUse), $this->columnToSort);
+ $this->sort($table, $methodToUse);
}
+
+ /**
+ * Sorts the DataTable rows using the supplied callback function.
+ *
+ * @param string $functionCallback A comparison callback compatible with {@link usort}.
+ * @param string $columnSortedBy The column name `$functionCallback` sorts by. This is stored
+ * so we can determine how the DataTable was sorted in the future.
+ */
+ private function sort(DataTable $table, $functionCallback)
+ {
+ $table->setTableSortedBy($this->columnToSort);
+
+ $rows = $table->getRowsWithoutSummaryRow();
+
+ // get column value and label only once for performance tweak
+ $values = array();
+ foreach ($rows as $key => $row) {
+ $values[$key] = array($this->getColumnValue($row), $row->getColumn('label'));
+ }
+
+ uasort($values, array($this, $functionCallback));
+
+ $sortedRows = array();
+ foreach ($values as $key => $value) {
+ $sortedRows[$key] = $rows[$key];
+ }
+
+ $table->setRows(array_values($sortedRows));
+
+ unset($rows);
+ unset($sortedRows);
+
+ if ($table->isSortRecursiveEnabled()) {
+ foreach ($table->getRows() as $row) {
+
+ $subTable = $row->getSubtable();
+ if ($subTable) {
+ $subTable->enableRecursiveSort();
+ $this->sort($subTable, $functionCallback);
+ }
+ }
+ }
+
+ }
+
}
diff --git a/core/DataTable/Filter/Truncate.php b/core/DataTable/Filter/Truncate.php
index 632eb2755e..a8fa0bd08f 100644
--- a/core/DataTable/Filter/Truncate.php
+++ b/core/DataTable/Filter/Truncate.php
@@ -96,7 +96,7 @@ class Truncate extends BaseFilter
return;
}
- $rows = $table->getRows();
+ $rows = array_values($table->getRows());
$count = $table->getRowsCount();
$newRow = new Row(array(Row::COLUMNS => array('label' => DataTable::LABEL_SUMMARY_ROW)));
diff --git a/core/DataTable/Map.php b/core/DataTable/Map.php
index 8795eac134..8787b06404 100644
--- a/core/DataTable/Map.php
+++ b/core/DataTable/Map.php
@@ -110,6 +110,19 @@ class Map implements DataTableInterface
}
/**
+ * Apply a filter to all subtables contained by this instance.
+ *
+ * @param string|Closure $className Name of filter class or a Closure.
+ * @param array $parameters Parameters to pass to the filter.
+ */
+ public function filterSubtables($className, $parameters = array())
+ {
+ foreach ($this->getDataTables() as $table) {
+ $table->filterSubtables($className, $parameters);
+ }
+ }
+
+ /**
* Returns the array of DataTables contained by this class.
*
* @return DataTable[]|Map[]
@@ -185,6 +198,26 @@ class Map implements DataTableInterface
}
/**
+ * @ignore
+ */
+ public function disableRecursiveFilters()
+ {
+ foreach ($this->getDataTables() as $table) {
+ $table->disableRecursiveFilters();
+ }
+ }
+
+ /**
+ * @ignore
+ */
+ public function enableRecursiveFilters()
+ {
+ foreach ($this->getDataTables() as $table) {
+ $table->enableRecursiveFilters();
+ }
+ }
+
+ /**
* Renames the given column in each contained {@link DataTable}.
*
* See {@link DataTable::renameColumn()}.
diff --git a/core/DataTable/Row.php b/core/DataTable/Row.php
index 5f21f3fdec..71dde68031 100644
--- a/core/DataTable/Row.php
+++ b/core/DataTable/Row.php
@@ -209,37 +209,9 @@ class Row implements \ArrayAccess, \IteratorAggregate
return false;
}
- if ($this->isColumnValueCallable($this->c[self::COLUMNS][$name])) {
- $value = $this->resolveCallableColumn($name);
-
- if (!isset($value)) {
- return false;
- }
-
- return $value;
- }
-
return $this->c[self::COLUMNS][$name];
}
- private function isColumnValueCallable($name)
- {
- if (! is_callable($name)) {
- return false;
- }
-
- if (is_object($name) && ($name instanceof \Closure)) {
- return true;
- }
-
- return is_array($name) && isset($name[0]) && is_object($name[0]);
- }
-
- private function resolveCallableColumn($columnName)
- {
- return call_user_func($this->c[self::COLUMNS][$columnName], $this);
- }
-
/**
* Returns the array of all metadata, or one requested metadata value.
*
@@ -287,16 +259,7 @@ class Row implements \ArrayAccess, \IteratorAggregate
*/
public function getColumns()
{
- $values = array();
- foreach ($this->c[self::COLUMNS] as $columnName => $val) {
- if ($this->isColumnValueCallable($val)) {
- $values[$columnName] = $this->resolveCallableColumn($columnName);
- } else {
- $values[$columnName] = $val;
- }
- }
-
- return $values;
+ return $this->c[self::COLUMNS];
}
/**
@@ -517,11 +480,6 @@ class Row implements \ArrayAccess, \IteratorAggregate
continue;
}
- if ($this->isColumnValueCallable($columnToSumValue)) {
- $this->setColumn($columnToSumName, $columnToSumValue);
- continue;
- }
-
$thisColumnValue = $this->getColumn($columnToSumName);
$operation = 'sum';
diff --git a/core/Plugin/API.php b/core/Plugin/API.php
index cc16cfb64c..4e5095a3aa 100644
--- a/core/Plugin/API.php
+++ b/core/Plugin/API.php
@@ -41,5 +41,5 @@ use Piwik\Singleton;
*/
abstract class API extends Singleton
{
-
+
}
diff --git a/core/Plugin/Metric.php b/core/Plugin/Metric.php
index e475eb837f..6d69350d92 100644
--- a/core/Plugin/Metric.php
+++ b/core/Plugin/Metric.php
@@ -108,26 +108,41 @@ abstract class Metric
*/
public static function getMetric($row, $columnName, $mappingNameToId = null)
{
- if (empty($mappingNameToId)) {
- $mappingNameToId = Metrics::getMappingFromNameToId();
- }
-
if ($row instanceof Row) {
$value = $row->getColumn($columnName);
- if ($value === false
- && isset($mappingNameToId[$columnName])
- ) {
- $value = $row->getColumn($mappingNameToId[$columnName]);
+
+ if ($value === false) {
+
+ if (empty($mappingNameToId)) {
+ $mappingNameToId = Metrics::getMappingFromNameToId();
+ }
+
+ if (isset($mappingNameToId[$columnName])) {
+ return $row->getColumn($mappingNameToId[$columnName]);
+ }
}
+
+ return $value;
+
} else {
- $value = @$row[$columnName];
- if ($value === null
- && isset($mappingNameToId[$columnName])
- ) {
- $columnName = $mappingNameToId[$columnName];
- $value = @$row[$columnName];
+ $value = null;
+ if (array_key_exists($columnName, $row)) {
+ $value = $row[$columnName];
+ } else {
+
+ if (empty($mappingNameToId)) {
+ $mappingNameToId = Metrics::getMappingFromNameToId();
+ }
+
+ if (isset($mappingNameToId[$columnName])) {
+ $columnName = $mappingNameToId[$columnName];
+
+ if (array_key_exists($columnName, $row)) {
+ return $row[$columnName];
+ }
+ }
}
- return $value;
+
}
return $value;
diff --git a/core/Plugin/Report.php b/core/Plugin/Report.php
index 62ed594f8c..b9c407e10e 100644
--- a/core/Plugin/Report.php
+++ b/core/Plugin/Report.php
@@ -182,6 +182,13 @@ class Report
protected $order = 1;
/**
+ * Separator for building recursive labels (or paths)
+ * @var string
+ * @api
+ */
+ protected $recursiveLabelSeparator = ' - ';
+
+ /**
* @var array
* @ignore
*/
@@ -357,6 +364,15 @@ class Report
}
/**
+ * @ignore
+ * @see $recursiveLabelSeparator
+ */
+ public function getRecursiveLabelSeparator()
+ {
+ return $this->recursiveLabelSeparator;
+ }
+
+ /**
* Returns an array of supported metrics and their corresponding translations. Eg `array('nb_visits' => 'Visits')`.
* By default the given {@link $metrics} are used and their corresponding translations are looked up automatically.
* If a metric is not translated, you should add the default metric translation for this metric using
diff --git a/core/UrlHelper.php b/core/UrlHelper.php
index a0bc340bbd..78682acbfc 100644
--- a/core/UrlHelper.php
+++ b/core/UrlHelper.php
@@ -231,7 +231,10 @@ class UrlHelper
$parsedUrl = parse_url($url);
$result = '';
if (isset($parsedUrl['path'])) {
- $result .= substr($parsedUrl['path'], 1);
+ if (substr($parsedUrl['path'], 0, 1) == '/') {
+ $parsedUrl['path'] = substr($parsedUrl['path'], 1);
+ }
+ $result .= $parsedUrl['path'];
}
if (isset($parsedUrl['query'])) {
$result .= '?' . $parsedUrl['query'];