update Piwik to version 2.16 (fixes #91)

This commit is contained in:
oliver 2016-04-10 18:55:57 +02:00
commit 47263617c5
52 changed files with 418860 additions and 226988 deletions

View file

@ -1,6 +1,6 @@
<?php
/**
* Piwik - Open source web analytics
* Piwik - free/libre analytics platform
*
* @link http://piwik.org
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
@ -9,12 +9,11 @@
namespace Piwik\DataTable;
use Piwik\DataTable;
use Piwik\DataTable\Row;
/**
* A filter is set of logic that manipulates a DataTable. Existing filters do things like,
*
* - remove rows
*
* - add/remove rows
* - change column values (change string to lowercase, truncate, etc.)
* - add/remove columns or metadata (compute percentage values, add an 'icon' metadata based on the label, etc.)
* - add/remove/edit subtable associated with rows
@ -22,10 +21,10 @@ use Piwik\DataTable\Row;
*
* Filters are called with a DataTable instance and extra parameters that are specified
* in {@link Piwik\DataTable::filter()} and {@link Piwik\DataTable::queueFilter()}.
*
*
* To see examples of Filters look at the existing ones in the Piwik\DataTable\BaseFilter
* namespace.
*
*
* @api
*/
abstract class BaseFilter
@ -37,7 +36,7 @@ abstract class BaseFilter
/**
* Constructor.
*
*
* @param DataTable $table
*/
public function __construct(DataTable $table)
@ -73,8 +72,8 @@ abstract class BaseFilter
if (!$this->enableRecursive) {
return;
}
if ($row->isSubtableLoaded()) {
$subTable = Manager::getInstance()->getTable($row->getIdSubDataTable());
$subTable = $row->getSubtable();
if ($subTable) {
$this->filter($subTable);
}
}

View file

@ -1,6 +1,6 @@
<?php
/**
* Piwik - Open source web analytics
* Piwik - free/libre analytics platform
*
* @link http://piwik.org
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
@ -13,8 +13,8 @@
*/
namespace {
use Piwik\DataTable\Row\DataTableSummaryRow;
use Piwik\DataTable\Row;
use Piwik\DataTable\Row\DataTableSummaryRow;
class Piwik_DataTable_Row_DataTableSummary extends DataTableSummaryRow
{
@ -24,5 +24,11 @@ namespace {
{
}
}
// only used for BC to unserialize old archived Row instances. We cannot unserialize Row directly as it implements
// the Serializable interface and it would fail on PHP5.6+ when userializing the Row instance directly.
class Piwik_DataTable_SerializedRow
{
public $c;
}
}

View file

@ -1,6 +1,6 @@
<?php
/**
* Piwik - Open source web analytics
* Piwik - free/libre analytics platform
*
* @link http://piwik.org
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later

View file

@ -1,6 +1,6 @@
<?php
/**
* Piwik - Open source web analytics
* Piwik - free/libre analytics platform
*
* @link http://piwik.org
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
@ -9,30 +9,33 @@
namespace Piwik\DataTable\Filter;
use Piwik\DataTable\BaseFilter;
use Piwik\DataTable\Row;
use Piwik\DataTable;
use Piwik\Metrics;
use Piwik\Plugin\Metric;
use Piwik\Plugins\CoreHome\Columns\Metrics\ActionsPerVisit;
use Piwik\Plugins\CoreHome\Columns\Metrics\AverageTimeOnSite;
use Piwik\Plugins\CoreHome\Columns\Metrics\BounceRate;
use Piwik\Plugins\CoreHome\Columns\Metrics\ConversionRate;
/**
* Adds processed metrics columns to a {@link DataTable} using metrics that already exist.
*
* Columns added are:
*
*
* - **conversion_rate**: percent value of `nb_visits_converted / nb_visits
* - **nb_actions_per_visit**: `nb_actions / nb_visits`
* - **avg_time_on_site**: in number of seconds, `round(visit_length / nb_visits)`. Not
* pretty formatted.
* - **bounce_rate**: percent value of `bounce_count / nb_visits`
*
*
* Adding the **filter_add_columns_when_show_all_columns** query parameter to
* an API request will trigger the execution of this Filter.
*
*
* _Note: This filter must be called before {@link ReplaceColumnNames} is called._
*
*
* **Basic usage example**
*
*
* $dataTable->filter('AddColumnsProcessedMetrics');
*
*
* @api
*/
class AddColumnsProcessedMetrics extends BaseFilter
@ -43,7 +46,7 @@ class AddColumnsProcessedMetrics extends BaseFilter
/**
* Constructor.
*
*
* @param DataTable $table The table to eventually filter.
* @param bool $deleteRowsWithNoVisit Whether to delete rows with no visits or not.
*/
@ -61,89 +64,32 @@ class AddColumnsProcessedMetrics extends BaseFilter
*/
public function filter($table)
{
$rowsIdToDelete = array();
if ($this->deleteRowsWithNoVisit) {
$this->deleteRowsWithNoVisit($table);
}
$extraProcessedMetrics = $table->getMetadata(DataTable::EXTRA_PROCESSED_METRICS_METADATA_NAME);
$extraProcessedMetrics[] = new ConversionRate();
$extraProcessedMetrics[] = new ActionsPerVisit();
$extraProcessedMetrics[] = new AverageTimeOnSite();
$extraProcessedMetrics[] = new BounceRate();
$table->setMetadata(DataTable::EXTRA_PROCESSED_METRICS_METADATA_NAME, $extraProcessedMetrics);
}
private function deleteRowsWithNoVisit(DataTable $table)
{
foreach ($table->getRows() as $key => $row) {
$nbVisits = $this->getColumn($row, Metrics::INDEX_NB_VISITS);
$nbActions = $this->getColumn($row, Metrics::INDEX_NB_ACTIONS);
$nbVisits = Metric::getMetric($row, 'nb_visits');
$nbActions = Metric::getMetric($row, 'nb_actions');
if ($nbVisits == 0
&& $nbActions == 0
&& $this->deleteRowsWithNoVisit
) {
// case of keyword/website/campaign with a conversion for this day,
// but no visit, we don't show it
$rowsIdToDelete[] = $key;
continue;
// case of keyword/website/campaign with a conversion for this day, but no visit, we don't show it
$table->deleteRow($key);
}
$nbVisitsConverted = (int)$this->getColumn($row, Metrics::INDEX_NB_VISITS_CONVERTED);
if ($nbVisitsConverted > 0) {
$conversionRate = round(100 * $nbVisitsConverted / $nbVisits, $this->roundPrecision);
try {
$row->addColumn('conversion_rate', $conversionRate . "%");
} catch (\Exception $e) {
// conversion_rate can be defined upstream apparently? FIXME
}
}
if ($nbVisits == 0) {
$actionsPerVisit = $averageTimeOnSite = $bounceRate = $this->invalidDivision;
} else {
// nb_actions / nb_visits => Actions/visit
// sum_visit_length / nb_visits => Avg. Time on Site
// bounce_count / nb_visits => Bounce Rate
$actionsPerVisit = round($nbActions / $nbVisits, $this->roundPrecision);
$visitLength = $this->getColumn($row, Metrics::INDEX_SUM_VISIT_LENGTH);
$averageTimeOnSite = round($visitLength / $nbVisits, $rounding = 0);
$bounceRate = round(100 * $this->getColumn($row, Metrics::INDEX_BOUNCE_COUNT) / $nbVisits, $this->roundPrecision);
}
try {
$row->addColumn('nb_actions_per_visit', $actionsPerVisit);
$row->addColumn('avg_time_on_site', $averageTimeOnSite);
// It could be useful for API users to have raw sum length value.
//$row->addMetadata('sum_visit_length', $visitLength);
} catch (\Exception $e) {
}
try {
$row->addColumn('bounce_rate', $bounceRate . "%");
} catch (\Exception $e) {
}
$this->filterSubTable($row);
}
$table->deleteRows($rowsIdToDelete);
}
/**
* Returns column from a given row.
* Will work with 2 types of datatable
* - raw datatables coming from the archive DB, which columns are int indexed
* - datatables processed resulting of API calls, which columns have human readable english names
*
* @param Row|array $row
* @param int $columnIdRaw see consts in Archive::
* @param bool|array $mappingIdToName
* @return mixed Value of column, false if not found
*/
protected function getColumn($row, $columnIdRaw, $mappingIdToName = false)
{
if (empty($mappingIdToName)) {
$mappingIdToName = Metrics::$mappingFromIdToName;
}
$columnIdReadable = $mappingIdToName[$columnIdRaw];
if ($row instanceof Row) {
$raw = $row->getColumn($columnIdRaw);
if ($raw !== false) {
return $raw;
}
return $row->getColumn($columnIdReadable);
}
if (isset($row[$columnIdRaw])) {
return $row[$columnIdRaw];
}
if (isset($row[$columnIdReadable])) {
return $row[$columnIdReadable];
}
return false;
}
}

View file

@ -1,6 +1,6 @@
<?php
/**
* Piwik - Open source web analytics
* Piwik - free/libre analytics platform
*
* @link http://piwik.org
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
@ -8,16 +8,23 @@
*/
namespace Piwik\DataTable\Filter;
use Exception;
use Piwik\Archive\DataTableFactory;
use Piwik\DataTable;
use Piwik\Metrics;
use Piwik\Piwik;
use Piwik\Tracker\GoalManager;
use Piwik\Plugin\Metric;
use Piwik\Plugins\Goals\Columns\Metrics\GoalSpecific\AverageOrderRevenue;
use Piwik\Plugins\Goals\Columns\Metrics\GoalSpecific\ConversionRate;
use Piwik\Plugins\Goals\Columns\Metrics\GoalSpecific\Conversions;
use Piwik\Plugins\Goals\Columns\Metrics\GoalSpecific\ItemsCount;
use Piwik\Plugins\Goals\Columns\Metrics\GoalSpecific\Revenue;
use Piwik\Plugins\Goals\Columns\Metrics\GoalSpecific\RevenuePerVisit as GoalSpecificRevenuePerVisit;
use Piwik\Plugins\Goals\Columns\Metrics\RevenuePerVisit;
/**
* Adds goal related metrics to a {@link DataTable} using metrics that already exist.
*
* Metrics added are:
*
* - **revenue_per_visit**: total goal and ecommerce revenue / nb_visits
* - **goal_%idGoal%_conversion_rate**: the conversion rate. There will be one of
* these columns for each goal that exists
@ -36,17 +43,17 @@ use Piwik\Tracker\GoalManager;
* reports.
* - **goal_%idGoal%_items**: number of items. Only for ecommerce order and abandoned cart
* reports.
*
*
* Adding the **filter_update_columns_when_show_all_goals** query parameter to
* an API request will trigger the execution of this Filter.
*
*
* _Note: This filter must be called before {@link ReplaceColumnNames} is called._
*
*
* **Basic usage example**
*
*
* $dataTable->filter('AddColumnsProcessedMetricsGoal',
* array($enable = true, $idGoal = Piwik::LABEL_ID_GOAL_IS_ECOMMERCE_ORDER));
*
*
* @api
*/
class AddColumnsProcessedMetricsGoal extends AddColumnsProcessedMetrics
@ -68,7 +75,7 @@ class AddColumnsProcessedMetricsGoal extends AddColumnsProcessedMetrics
/**
* Constructor.
*
*
* @param DataTable $table The table that will eventually filtered.
* @param bool $enable Always set to true.
* @param string $processOnlyIdGoal Defines what metrics to add (don't process metrics when you don't display them).
@ -76,13 +83,14 @@ class AddColumnsProcessedMetricsGoal extends AddColumnsProcessedMetrics
* If self::GOALS_OVERVIEW, only the main goal metrics will be added.
* If an int > 0, then will process only metrics for this specific Goal.
*/
public function __construct($table, $enable = true, $processOnlyIdGoal)
public function __construct($table, $enable = true, $processOnlyIdGoal, $goalsToProcess = null)
{
$this->processOnlyIdGoal = $processOnlyIdGoal;
$this->isEcommerce = $this->processOnlyIdGoal == Piwik::LABEL_ID_GOAL_IS_ECOMMERCE_ORDER || $this->processOnlyIdGoal == Piwik::LABEL_ID_GOAL_IS_ECOMMERCE_CART;
parent::__construct($table);
// Ensure that all rows with no visit but conversions will be displayed
$this->deleteRowsWithNoVisit = false;
$this->goalsToProcess = $goalsToProcess;
}
/**
@ -95,132 +103,63 @@ class AddColumnsProcessedMetricsGoal extends AddColumnsProcessedMetrics
{
// Add standard processed metrics
parent::filter($table);
$roundingPrecision = GoalManager::REVENUE_PRECISION;
$expectedColumns = array();
foreach ($table->getRows() as $key => $row) {
$currentColumns = $row->getColumns();
$newColumns = array();
// visits could be undefined when there is a conversion but no visit
$nbVisits = (int)$this->getColumn($row, Metrics::INDEX_NB_VISITS);
$conversions = (int)$this->getColumn($row, Metrics::INDEX_NB_CONVERSIONS);
$goals = $this->getColumn($currentColumns, Metrics::INDEX_GOALS);
if ($goals) {
$revenue = 0;
foreach ($goals as $goalId => $goalMetrics) {
if ($goalId == Piwik::LABEL_ID_GOAL_IS_ECOMMERCE_CART) {
continue;
}
if ($goalId >= GoalManager::IDGOAL_ORDER
|| $goalId == Piwik::LABEL_ID_GOAL_IS_ECOMMERCE_ORDER
) {
$revenue += (int)$this->getColumn($goalMetrics, Metrics::INDEX_GOAL_REVENUE, Metrics::$mappingFromIdToNameGoal);
}
}
$goals = $this->getGoalsInTable($table);
if (!empty($this->goalsToProcess)) {
$goals = array_unique(array_merge($goals, $this->goalsToProcess));
sort($goals);
}
if ($revenue == 0) {
$revenue = (int)$this->getColumn($currentColumns, Metrics::INDEX_REVENUE);
}
if (!isset($currentColumns['revenue_per_visit'])) {
// If no visit for this metric, but some conversions, we still want to display some kind of "revenue per visit"
// even though it will actually be in this edge case "Revenue per conversion"
$revenuePerVisit = $this->invalidDivision;
if ($nbVisits > 0
|| $conversions > 0
) {
$revenuePerVisit = round($revenue / ($nbVisits == 0 ? $conversions : $nbVisits), $roundingPrecision);
}
$newColumns['revenue_per_visit'] = $revenuePerVisit;
}
if ($this->processOnlyIdGoal == self::GOALS_MINIMAL_REPORT) {
$row->addColumns($newColumns);
$idSite = DataTableFactory::getSiteIdFromMetadata($table);
$extraProcessedMetrics = $table->getMetadata(DataTable::EXTRA_PROCESSED_METRICS_METADATA_NAME);
$extraProcessedMetrics[] = new RevenuePerVisit();
if ($this->processOnlyIdGoal != self::GOALS_MINIMAL_REPORT) {
foreach ($goals as $idGoal) {
if (($this->processOnlyIdGoal > self::GOALS_FULL_TABLE
|| $this->isEcommerce)
&& $this->processOnlyIdGoal != $idGoal
) {
continue;
}
// Display per goal metrics
// - conversion rate
// - conversions
// - revenue per visit
foreach ($goals as $goalId => $goalMetrics) {
$goalId = str_replace("idgoal=", "", $goalId);
if (($this->processOnlyIdGoal > self::GOALS_FULL_TABLE
|| $this->isEcommerce)
&& $this->processOnlyIdGoal != $goalId
) {
continue;
}
$conversions = (int)$this->getColumn($goalMetrics, Metrics::INDEX_GOAL_NB_CONVERSIONS, Metrics::$mappingFromIdToNameGoal);
// Goal Conversion rate
$name = 'goal_' . $goalId . '_conversion_rate';
if ($nbVisits == 0) {
$value = $this->invalidDivision;
} else {
$value = round(100 * $conversions / $nbVisits, $roundingPrecision);
}
$newColumns[$name] = $value . "%";
$expectedColumns[$name] = true;
$extraProcessedMetrics[] = new ConversionRate($idSite, $idGoal); // PerGoal\ConversionRate
// When the table is displayed by clicking on the flag icon, we only display the columns
// Visits, Conversions, Per goal conversion rate, Revenue
if ($this->processOnlyIdGoal == self::GOALS_OVERVIEW) {
continue;
}
// Goal Conversions
$name = 'goal_' . $goalId . '_nb_conversions';
$newColumns[$name] = $conversions;
$expectedColumns[$name] = true;
// Goal Revenue per visit
$name = 'goal_' . $goalId . '_revenue_per_visit';
// See comment above for $revenuePerVisit
$goalRevenue = (float)$this->getColumn($goalMetrics, Metrics::INDEX_GOAL_REVENUE, Metrics::$mappingFromIdToNameGoal);
$revenuePerVisit = round($goalRevenue / ($nbVisits == 0 ? $conversions : $nbVisits), $roundingPrecision);
$newColumns[$name] = $revenuePerVisit;
$expectedColumns[$name] = true;
// Total revenue
$name = 'goal_' . $goalId . '_revenue';
$newColumns[$name] = $goalRevenue;
$expectedColumns[$name] = true;
if ($this->isEcommerce) {
// AOV Average Order Value
$name = 'goal_' . $goalId . '_avg_order_revenue';
$newColumns[$name] = $goalRevenue / $conversions;
$expectedColumns[$name] = true;
// Items qty
$name = 'goal_' . $goalId . '_items';
$newColumns[$name] = $this->getColumn($goalMetrics, Metrics::INDEX_GOAL_ECOMMERCE_ITEMS, Metrics::$mappingFromIdToNameGoal);
$expectedColumns[$name] = true;
}
// When the table is displayed by clicking on the flag icon, we only display the columns
// Visits, Conversions, Per goal conversion rate, Revenue
if ($this->processOnlyIdGoal == self::GOALS_OVERVIEW) {
continue;
}
}
// conversion_rate can be defined upstream apparently? FIXME
try {
$row->addColumns($newColumns);
} catch (Exception $e) {
}
}
$expectedColumns['revenue_per_visit'] = true;
$extraProcessedMetrics[] = new Conversions($idSite, $idGoal); // PerGoal\Conversions or GoalSpecific\
$extraProcessedMetrics[] = new GoalSpecificRevenuePerVisit($idSite, $idGoal); // PerGoal\Revenue
$extraProcessedMetrics[] = new Revenue($idSite, $idGoal); // PerGoal\Revenue
// make sure all goals values are set, 0 by default
// if no value then sorting would put at the end
$expectedColumns = array_keys($expectedColumns);
$rows = $table->getRows();
foreach ($rows as &$row) {
foreach ($expectedColumns as $name) {
if (false === $row->getColumn($name)) {
$value = 0;
if (strpos($name, 'conversion_rate') !== false) {
$value = '0%';
}
$row->addColumn($name, $value);
if ($this->isEcommerce) {
$extraProcessedMetrics[] = new AverageOrderRevenue($idSite, $idGoal);
$extraProcessedMetrics[] = new ItemsCount($idSite, $idGoal);
}
}
}
$table->setMetadata(DataTable::EXTRA_PROCESSED_METRICS_METADATA_NAME, $extraProcessedMetrics);
}
private function getGoalsInTable(DataTable $table)
{
$result = array();
foreach ($table->getRows() as $row) {
$goals = Metric::getMetric($row, 'goals');
if (!$goals) {
continue;
}
foreach ($goals as $goalId => $goalMetrics) {
$goalId = str_replace("idgoal=", "", $goalId);
$result[] = $goalId;
}
}
return array_unique($result);
}
}

View file

@ -0,0 +1,99 @@
<?php
/**
* Piwik - free/libre analytics platform
*
* @link http://piwik.org
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
*
*/
namespace Piwik\DataTable\Filter;
use Piwik\DataTable;
use Piwik\DataTable\BaseFilter;
use Piwik\Development;
/**
* Executes a filter for each row of a {@link DataTable} and generates a segment filter for each row.
*
* **Basic usage example**
*
* $dataTable->filter('AddSegmentByLabel', array('segmentName'));
* $dataTable->filter('AddSegmentByLabel', array(array('segmentName1', 'segment2'), ';');
*
* @api
*/
class AddSegmentByLabel extends BaseFilter
{
private $segments;
private $delimiter;
/**
* Generates a segment filter based on the label column and the given segment names
*
* @param DataTable $table
* @param string|array $segmentOrSegments Either one segment or an array of segments.
* If more than one segment is given a delimter has to be defined.
* @param string $delimiter The delimiter by which the label should be splitted.
*/
public function __construct($table, $segmentOrSegments, $delimiter = '')
{
parent::__construct($table);
if (!is_array($segmentOrSegments)) {
$segmentOrSegments = array($segmentOrSegments);
}
$this->segments = $segmentOrSegments;
$this->delimiter = $delimiter;
}
/**
* See {@link AddSegmentByLabel}.
*
* @param DataTable $table
*/
public function filter($table)
{
if (empty($this->segments)) {
$msg = 'AddSegmentByLabel is called without having any segments defined';
Development::error($msg);
return;
}
if (count($this->segments) === 1) {
$segment = reset($this->segments);
foreach ($table->getRowsWithoutSummaryRow() as $key => $row) {
$label = $row->getColumn('label');
if (!empty($label)) {
$row->setMetadata('segment', $segment . '==' . urlencode($label));
}
}
} elseif (!empty($this->delimiter)) {
$numSegments = count($this->segments);
$conditionAnd = ';';
foreach ($table->getRowsWithoutSummaryRow() as $key => $row) {
$label = $row->getColumn('label');
if (!empty($label)) {
$parts = explode($this->delimiter, $label);
if (count($parts) === $numSegments) {
$filter = array();
foreach ($this->segments as $index => $segment) {
if (!empty($segment)) {
$filter[] = $segment . '==' . urlencode($parts[$index]);
}
}
$row->setMetadata('segment', implode($conditionAnd, $filter));
}
}
}
} else {
$names = implode(', ', $this->segments);
$msg = 'Multiple segments are given but no delimiter defined. Segments: ' . $names;
Development::error($msg);
}
}
}

View file

@ -0,0 +1,63 @@
<?php
/**
* Piwik - free/libre analytics platform
*
* @link http://piwik.org
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
*
*/
namespace Piwik\DataTable\Filter;
use Piwik\DataTable;
use Piwik\DataTable\BaseFilter;
/**
* Executes a filter for each row of a {@link DataTable} and generates a segment filter for each row.
* It will map the label column to a segmentValue by searching for the label in the index of the given
* mapping array.
*
* **Basic usage example**
*
* $dataTable->filter('AddSegmentByLabelMapping', array('segmentName', array('1' => 'smartphone, '2' => 'desktop')));
*
* @api
*/
class AddSegmentByLabelMapping extends BaseFilter
{
private $segment;
private $mapping;
/**
* @param DataTable $table
* @param string $segment
* @param array $mapping
*/
public function __construct($table, $segment, $mapping)
{
parent::__construct($table);
$this->segment = $segment;
$this->mapping = $mapping;
}
/**
* See {@link AddSegmentByLabelMapping}.
*
* @param DataTable $table
*/
public function filter($table)
{
if (empty($this->segment) || empty($this->mapping)) {
return;
}
foreach ($table->getRows() as $row) {
$label = $row->getColumn('label');
if (!empty($this->mapping[$label])) {
$label = $this->mapping[$label];
$row->setMetadata('segment', $this->segment . '==' . urlencode($label));
}
}
}
}

View file

@ -0,0 +1,78 @@
<?php
/**
* Piwik - free/libre analytics platform
*
* @link http://piwik.org
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
*
*/
namespace Piwik\DataTable\Filter;
use Piwik\DataTable\BaseFilter;
use Piwik\DataTable;
/**
* Converts for each row of a {@link DataTable} a segmentValue to a segment (expression). The name of the segment
* is automatically detected based on the given report.
*
* **Basic usage example**
*
* $dataTable->filter('AddSegmentBySegmentValue', array($reportInstance));
*
* @api
*/
class AddSegmentBySegmentValue extends BaseFilter
{
/**
* @var \Piwik\Plugin\Report
*/
private $report;
/**
* @param DataTable $table
* @param $report
*/
public function __construct($table, $report)
{
parent::__construct($table);
$this->report = $report;
}
/**
* See {@link AddSegmentBySegmentValue}.
*
* @param DataTable $table
* @return int The number of deleted rows.
*/
public function filter($table)
{
if (empty($this->report) || !$table->getRowsCount()) {
return;
}
$dimension = $this->report->getDimension();
if (empty($dimension)) {
return;
}
$segments = $dimension->getSegments();
if (empty($segments)) {
return;
}
/** @var \Piwik\Plugin\Segment $segment */
$segment = reset($segments);
$segmentName = $segment->getSegment();
foreach ($table->getRows() as $row) {
$value = $row->getMetadata('segmentValue');
$filter = $row->getMetadata('segment');
if ($value !== false && $filter === false) {
$row->setMetadata('segment', sprintf('%s==%s', $segmentName, urlencode($value)));
}
}
}
}

View file

@ -0,0 +1,32 @@
<?php
/**
* Piwik - free/libre analytics platform
*
* @link http://piwik.org
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
*
*/
namespace Piwik\DataTable\Filter;
use Piwik\DataTable;
/**
* Executes a filter for each row of a {@link DataTable} and generates a segment filter for each row.
*
* **Basic usage example**
*
* $dataTable->filter('AddSegmentValue', array());
* $dataTable->filter('AddSegmentValue', array(function ($label) {
* $transformedValue = urldecode($transformedValue);
* return $transformedValue;
* });
*
* @api
*/
class AddSegmentValue extends ColumnCallbackAddMetadata
{
public function __construct($table, $callback = null)
{
parent::__construct($table, 'label', 'segmentValue', $callback, null, false);
}
}

View file

@ -1,6 +1,6 @@
<?php
/**
* Piwik - Open source web analytics
* Piwik - free/libre analytics platform
*
* @link http://piwik.org
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
@ -16,12 +16,12 @@ use Piwik\DataTable\Row\DataTableSummaryRow;
* Adds a summary row to {@link DataTable}s that contains the sum of all other table rows.
*
* **Basic usage example**
*
*
* $dataTable->filter('AddSummaryRow');
*
*
* // use a human readable label for the summary row (instead of '-1')
* $dataTable->filter('AddSummaryRow', array($labelSummaryRow = Piwik::translate('General_Total')));
*
*
* @api
*/
class AddSummaryRow extends BaseFilter

View file

@ -1,6 +1,6 @@
<?php
/**
* Piwik - Open source web analytics
* Piwik - free/libre analytics platform
*
* @link http://piwik.org
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
@ -28,11 +28,11 @@ use Piwik\Piwik;
* This filter can be extended to vary exactly how ranges are prettified based
* on the range values found in the DataTable. To see an example of this,
* take a look at the {@link BeautifyTimeRangeLabels} filter.
*
*
* **Basic usage example**
*
*
* $dataTable->queueFilter('BeautifyRangeLabels', array("1 visit", "%s visits"));
*
*
* @api
*/
class BeautifyRangeLabels extends ColumnCallbackReplace
@ -65,7 +65,7 @@ class BeautifyRangeLabels extends ColumnCallbackReplace
parent::__construct($table, 'label', array($this, 'beautify'), array());
$this->labelSingular = $labelSingular;
$this->labelPlural = $labelPlural;
$this->labelPlural = $labelPlural;
}
/**

View file

@ -1,6 +1,6 @@
<?php
/**
* Piwik - Open source web analytics
* Piwik - free/libre analytics platform
*
* @link http://piwik.org
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
@ -17,11 +17,11 @@ use Piwik\DataTable;
* This filter customizes the behavior of the {@link BeautifyRangeLabels} filter
* so range values that are less than one minute are displayed in seconds but
* other ranges are displayed in minutes.
*
*
* **Basic usage**
*
*
* $dataTable->filter('BeautifyTimeRangeLabels', array("%1$s-%2$s min", "1 min", "%s min"));
*
*
* @api
*/
class BeautifyTimeRangeLabels extends BeautifyRangeLabels
@ -70,7 +70,7 @@ class BeautifyTimeRangeLabels extends BeautifyRangeLabels
{
if ($lowerBound < 60) {
return sprintf($this->labelSecondsPlural, $lowerBound, $lowerBound);
} else if ($lowerBound == 60) {
} elseif ($lowerBound == 60) {
return $this->labelSingular;
} else {
return sprintf($this->labelPlural, ceil($lowerBound / 60));

View file

@ -1,6 +1,6 @@
<?php
/**
* Piwik - Open source web analytics
* Piwik - free/libre analytics platform
*
* @link http://piwik.org
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
@ -8,8 +8,10 @@
*/
namespace Piwik\DataTable\Filter;
use Piwik\Common;
use Piwik\DataTable;
use Piwik\DataTable\Row;
use Piwik\NumberFormatter;
use Piwik\Site;
/**
@ -17,15 +19,16 @@ use Piwik\Site;
* it to each row as a percentage.
*
* **This filter cannot be used as an argument to {@link Piwik\DataTable::filter()}** since
* it requires corresponding data from another DataTable. Instead,
* it requires corresponding data from another DataTable. Instead,
* you must manually perform a binary filter (see the **MultiSites** API for an
* example).
*
* The evolution metric is calculated as:
*
*
* ((currentValue - pastValue) / pastValue) * 100
*
* @api
* @deprecated since v2.10.0
*/
class CalculateEvolutionFilter extends ColumnCallbackAddColumnPercentage
{
@ -100,7 +103,9 @@ class CalculateEvolutionFilter extends ColumnCallbackAddColumnPercentage
protected function getDivisor($row)
{
$pastRow = $this->getPastRowFromCurrent($row);
if (!$pastRow) return 0;
if (!$pastRow) {
return 0;
}
return $pastRow->getColumn($this->columnNameUsedAsDivisor);
}
@ -121,6 +126,9 @@ class CalculateEvolutionFilter extends ColumnCallbackAddColumnPercentage
{
$value = self::getPercentageValue($value, $divisor, $this->quotientPrecision);
$value = self::appendPercentSign($value);
$value = Common::forceDotAsSeparatorForDecimalPoint($value);
return $value;
}
@ -150,9 +158,10 @@ class CalculateEvolutionFilter extends ColumnCallbackAddColumnPercentage
{
$number = self::getPercentageValue($currentValue - $pastValue, $pastValue, $quotientPrecision);
if ($appendPercentSign) {
$number = self::appendPercentSign($number);
return NumberFormatter::getInstance()->formatPercent($number, $quotientPrecision);
}
return $number;
return NumberFormatter::getInstance()->format($number, $quotientPrecision);
}
public static function appendPercentSign($number)
@ -165,6 +174,7 @@ class CalculateEvolutionFilter extends ColumnCallbackAddColumnPercentage
if ($number > 0) {
$number = '+' . $number;
}
return $number;
}

View file

@ -1,6 +1,6 @@
<?php
/**
* Piwik - Open source web analytics
* Piwik - free/libre analytics platform
*
* @link http://piwik.org
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
@ -10,16 +10,17 @@ 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.
*
*
* **Basic usage example**
*
*
* $callback = function ($visits, $timeSpent) {
* return round($timeSpent / $visits, 2);
* };
*
*
* $dataTable->filter('ColumnCallbackAddColumn', array(array('nb_visits', 'sum_time_spent'), 'avg_time_on_site', $callback));
*
* @api
@ -79,17 +80,33 @@ class ColumnCallbackAddColumn extends BaseFilter
*/
public function filter($table)
{
foreach ($table->getRows() as $row) {
$columns = $this->columns;
$functionParams = $this->functionParameters;
$functionToApply = $this->functionToApply;
$extraProcessedMetrics = $table->getMetadata(DataTable::EXTRA_PROCESSED_METRICS_METADATA_NAME);
if (empty($extraProcessedMetrics)) {
$extraProcessedMetrics = array();
}
$metric = new CallableProcessedMetric($this->columnToAdd, function (DataTable\Row $row) use ($columns, $functionParams, $functionToApply) {
$columnValues = array();
foreach ($this->columns as $column) {
foreach ($columns as $column) {
$columnValues[] = $row->getColumn($column);
}
$parameters = array_merge($columnValues, $this->functionParameters);
$value = call_user_func_array($this->functionToApply, $parameters);
$parameters = array_merge($columnValues, $functionParams);
$row->setColumn($this->columnToAdd, $value);
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);
}
}

View file

@ -1,6 +1,6 @@
<?php
/**
* Piwik - Open source web analytics
* Piwik - free/libre analytics platform
*
* @link http://piwik.org
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
@ -13,11 +13,11 @@ use Piwik\Piwik;
/**
* Calculates a percentage value for each row of a {@link DataTable} and adds the result
* to each row.
*
*
* See {@link ColumnCallbackAddColumnQuotient} for more information.
*
* **Basic usage example**
*
*
* $nbVisits = // ... get the visits for a period ...
* $dataTable->queueFilter('ColumnCallbackAddColumnPercentage', array('nb_visits', 'nb_visits_percentage', $nbVisits, 1));
*

View file

@ -1,6 +1,6 @@
<?php
/**
* Piwik - Open source web analytics
* Piwik - free/libre analytics platform
*
* @link http://piwik.org
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
@ -15,14 +15,14 @@ use Piwik\DataTable\Row;
/**
* Calculates the quotient of two columns and adds the result as a new column
* for each row of a DataTable.
*
*
* This filter is used to calculate rate values (eg, `'bounce_rate'`), averages
* (eg, `'avg_time_on_page'`) and other types of values.
*
* **Basic usage example**
*
*
* $dataTable->queueFilter('ColumnCallbackAddColumnQuotient', array('bounce_rate', 'bounce_count', 'nb_visits', $precision = 2));
*
*
* @api
*/
class ColumnCallbackAddColumnQuotient extends BaseFilter
@ -38,7 +38,7 @@ class ColumnCallbackAddColumnQuotient extends BaseFilter
/**
* Constructor.
*
*
* @param DataTable $table The DataTable that will eventually be filtered.
* @param string $columnNameToAdd The name of the column to add the quotient value to.
* @param string $columnValueToRead The name of the column that holds the dividend.
@ -75,7 +75,7 @@ class ColumnCallbackAddColumnQuotient extends BaseFilter
*/
public function filter($table)
{
foreach ($table->getRows() as $key => $row) {
foreach ($table->getRows() as $row) {
$value = $this->getDividend($row);
if ($value === false && $this->shouldSkipRows) {
continue;
@ -109,6 +109,7 @@ class ColumnCallbackAddColumnQuotient extends BaseFilter
if ($divisor > 0 && $value > 0) {
$quotient = round($value / $divisor, $this->quotientPrecision);
}
return $quotient;
}
@ -135,7 +136,7 @@ class ColumnCallbackAddColumnQuotient extends BaseFilter
{
if (!is_null($this->totalValueUsedAsDivisor)) {
return $this->totalValueUsedAsDivisor;
} else if ($this->getDivisorFromSummaryRow) {
} elseif ($this->getDivisorFromSummaryRow) {
$summaryRow = $this->table->getRowFromId(DataTable::ID_SUMMARY_ROW);
return $summaryRow->getColumn($this->columnNameUsedAsDivisor);
} else {

View file

@ -1,6 +1,6 @@
<?php
/**
* Piwik - Open source web analytics
* Piwik - free/libre analytics platform
*
* @link http://piwik.org
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
@ -14,11 +14,11 @@ use Piwik\DataTable\BaseFilter;
/**
* Executes a callback for each row of a {@link DataTable} and adds the result as a new
* row metadata value.
*
*
* **Basic usage example**
*
*
* $dataTable->filter('ColumnCallbackAddMetadata', array('label', 'logo', 'Piwik\Plugins\MyPlugin\getLogoFromLabel'));
*
*
* @api
*/
class ColumnCallbackAddMetadata extends BaseFilter
@ -31,7 +31,7 @@ class ColumnCallbackAddMetadata extends BaseFilter
/**
* Constructor.
*
*
* @param DataTable $table The DataTable instance that will be filtered.
* @param string|array $columnsToRead The columns to read from each row and pass on to the callback.
* @param string $metadataToAdd The name of the metadata field that will be added to each row.
@ -48,12 +48,12 @@ class ColumnCallbackAddMetadata extends BaseFilter
if (!is_array($columnsToRead)) {
$columnsToRead = array($columnsToRead);
}
$this->columnsToRead = $columnsToRead;
$this->functionToApply = $functionToApply;
$this->columnsToRead = $columnsToRead;
$this->functionToApply = $functionToApply;
$this->functionParameters = $functionParameters;
$this->metadataToAdd = $metadataToAdd;
$this->applyToSummaryRow = $applyToSummaryRow;
$this->metadataToAdd = $metadataToAdd;
$this->applyToSummaryRow = $applyToSummaryRow;
}
/**
@ -63,11 +63,13 @@ class ColumnCallbackAddMetadata extends BaseFilter
*/
public function filter($table)
{
foreach ($table->getRows() as $key => $row) {
if (!$this->applyToSummaryRow && $key == DataTable::ID_SUMMARY_ROW) {
continue;
}
if ($this->applyToSummaryRow) {
$rows = $table->getRows();
} else {
$rows = $table->getRowsWithoutSummaryRow();
}
foreach ($rows as $key => $row) {
$parameters = array();
foreach ($this->columnsToRead as $columnsToRead) {
$parameters[] = $row->getColumn($columnsToRead);

View file

@ -0,0 +1,55 @@
<?php
/**
* Piwik - free/libre analytics platform
*
* @link http://piwik.org
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
*
*/
namespace Piwik\DataTable\Filter;
use Piwik\DataTable;
use Piwik\DataTable\BaseFilter;
/**
* Executes a callback for each row of a {@link DataTable} and removes the defined metadata column from each row.
*
* **Basic usage example**
*
* $dataTable->filter('ColumnCallbackDeleteMetadata', array('segmentValue'));
*
* @api
*/
class ColumnCallbackDeleteMetadata extends BaseFilter
{
private $metadataToRemove;
/**
* Constructor.
*
* @param DataTable $table The DataTable instance that will be filtered.
* @param string $metadataToRemove The name of the metadata field that will be removed from each row.
*/
public function __construct($table, $metadataToRemove)
{
parent::__construct($table);
$this->metadataToRemove = $metadataToRemove;
}
/**
* See {@link ColumnCallbackDeleteMetadata}.
*
* @param DataTable $table
*/
public function filter($table)
{
$this->enableRecursive(true);
foreach ($table->getRows() as $row) {
$row->deleteMetadata($this->metadataToRemove);
$this->filterSubTable($row);
}
}
}

View file

@ -1,6 +1,6 @@
<?php
/**
* Piwik - Open source web analytics
* Piwik - free/libre analytics platform
*
* @link http://piwik.org
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
@ -15,23 +15,22 @@ use Piwik\DataTable\BaseFilter;
* Deletes all rows for which a callback returns true.
*
* **Basic usage example**
*
*
* $labelsToRemove = array('label1', 'label2', 'label2');
* $dataTable->filter('ColumnCallbackDeleteRow', array('label', function ($label) use ($labelsToRemove) {
* return in_array($label, $labelsToRemove);
* }));
*
*
* @api
*/
class ColumnCallbackDeleteRow extends BaseFilter
{
private $columnToFilter;
private $function;
private $functionParams;
/**
* Constructor.
*
*
* @param DataTable $table The DataTable that will be filtered eventually.
* @param array|string $columnsToFilter The column or array of columns that should be
* passed to the callback.

View file

@ -1,6 +1,6 @@
<?php
/**
* Piwik - Open source web analytics
* Piwik - free/libre analytics platform
*
* @link http://piwik.org
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
@ -15,9 +15,9 @@ use Piwik\DataTable\Row;
/**
* Replaces one or more column values in each row of a DataTable with the results
* of a callback.
*
*
* **Basic usage example**
*
*
* $truncateString = function ($value, $truncateLength) {
* if (strlen($value) > $truncateLength) {
* return substr(0, $truncateLength);
@ -25,10 +25,11 @@ use Piwik\DataTable\Row;
* return $value;
* }
* };
*
*
* // label, url and truncate_length are columns in $dataTable
* $dataTable->filter('ColumnCallbackReplace', array('label', 'url'), $truncateString, null, array('truncate_length'));
*
*
* @api
*/
class ColumnCallbackReplace extends BaseFilter
{
@ -39,7 +40,7 @@ class ColumnCallbackReplace extends BaseFilter
/**
* Constructor.
*
*
* @param DataTable $table The DataTable to filter.
* @param array|string $columnsToFilter The columns whose values should be passed to the callback
* and then replaced with the callback's result.
@ -54,14 +55,14 @@ class ColumnCallbackReplace extends BaseFilter
$extraColumnParameters = array())
{
parent::__construct($table);
$this->functionToApply = $functionToApply;
$this->functionToApply = $functionToApply;
$this->functionParameters = $functionParameters;
if (!is_array($columnsToFilter)) {
$columnsToFilter = array($columnsToFilter);
}
$this->columnsToFilter = $columnsToFilter;
$this->columnsToFilter = $columnsToFilter;
$this->extraColumnParameters = $extraColumnParameters;
}
@ -72,13 +73,14 @@ class ColumnCallbackReplace extends BaseFilter
*/
public function filter($table)
{
foreach ($table->getRows() as $key => $row) {
foreach ($table->getRows() as $row) {
$extraColumnParameters = array();
foreach ($this->extraColumnParameters as $columnName) {
$extraColumnParameters[] = $row->getColumn($columnName);
}
foreach ($this->columnsToFilter as $column) {
// when a value is not defined, we set it to zero by default (rather than displaying '-')
$value = $this->getElementToReplace($row, $column);
if ($value === false) {
@ -86,14 +88,21 @@ class ColumnCallbackReplace extends BaseFilter
}
$parameters = array_merge(array($value), $extraColumnParameters);
if (!is_null($this->functionParameters)) {
$parameters = array_merge($parameters, $this->functionParameters);
}
$newValue = call_user_func_array($this->functionToApply, $parameters);
$this->setElementToReplace($row, $column, $newValue);
$this->filterSubTable($row);
}
}
if (in_array('label', $this->columnsToFilter)) {
// we need to force rebuilding the index
$table->setLabelsHaveChanged();
}
}
/**

Some files were not shown because too many files have changed in this diff Show more