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:
authordiosmosis <benaka@piwik.pro>2015-03-10 10:35:37 +0300
committerdiosmosis <benaka@piwik.pro>2015-03-10 10:35:37 +0300
commit79f9b8b98368539839741ae5d7bd68733de3468e (patch)
tree379c3024beb4c39f0dee8800a41fd830d7210f67 /core
parent141113add7c265f46cc7e4aec15c8e70f3088c24 (diff)
parentb3371cfee95beaee193dc1272d129fdc775e756e (diff)
Merge branch 'master' into 7276_update_command_progress
Diffstat (limited to 'core')
-rw-r--r--core/API/DataTableManipulator/Flattener.php41
-rw-r--r--core/API/DataTableManipulator/LabelFilter.php2
-rw-r--r--core/API/DataTableManipulator/ReportTotalsCalculator.php27
-rw-r--r--core/API/DataTablePostProcessor.php48
-rw-r--r--core/API/Proxy.php14
-rw-r--r--core/API/Request.php10
-rw-r--r--core/API/ResponseBuilder.php12
-rw-r--r--core/Archive.php33
-rw-r--r--core/ArchiveProcessor/Rules.php21
-rw-r--r--core/Common.php20
-rw-r--r--core/Config.php27
-rw-r--r--core/Console.php1
-rw-r--r--core/CronArchive.php6
-rw-r--r--core/DataFiles/SearchEngines.php25
-rw-r--r--core/DataTable.php69
-rwxr-xr-xcore/DataTable/Filter/ColumnCallbackAddColumn.php28
-rw-r--r--core/DataTable/Filter/PivotByDimension.php7
-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/FrontController.php90
-rw-r--r--core/Http.php8
-rw-r--r--core/Http/ControllerResolver.php142
-rw-r--r--core/IP.php237
-rw-r--r--core/Menu/MenuAdmin.php26
-rw-r--r--core/Menu/MenuTop.php31
-rw-r--r--core/Period.php12
-rw-r--r--core/Plugin/API.php2
-rw-r--r--core/Plugin/Manager.php14
-rw-r--r--core/Plugin/Metric.php49
-rw-r--r--core/Plugin/Report.php16
-rw-r--r--core/Plugin/ViewDataTable.php4
-rw-r--r--core/Plugin/Visualization.php139
-rw-r--r--core/Plugin/Widgets.php1
-rw-r--r--core/Session.php6
-rw-r--r--core/SettingsPiwik.php15
-rw-r--r--core/Tracker/VisitExcluded.php9
-rw-r--r--core/UrlHelper.php5
-rw-r--r--core/Version.php2
-rw-r--r--core/View.php9
-rw-r--r--core/ViewDataTable/Config.php16
-rw-r--r--core/ViewDataTable/Request.php14
-rw-r--r--core/ViewDataTable/RequestConfig.php34
44 files changed, 713 insertions, 739 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/LabelFilter.php b/core/API/DataTableManipulator/LabelFilter.php
index cd8300991b..0d4e11772a 100644
--- a/core/API/DataTableManipulator/LabelFilter.php
+++ b/core/API/DataTableManipulator/LabelFilter.php
@@ -147,6 +147,8 @@ class LabelFilter extends DataTableManipulator
}
$variations[] = $label;
+ $variations = array_unique($variations);
+
return $variations;
}
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..dfa0434da6 100644
--- a/core/API/DataTablePostProcessor.php
+++ b/core/API/DataTablePostProcessor.php
@@ -59,6 +59,9 @@ class DataTablePostProcessor
*/
private $formatter;
+ private $callbackBeforeGenericFilters;
+ private $callbackAfterGenericFilters;
+
/**
* Constructor.
*/
@@ -66,11 +69,31 @@ class DataTablePostProcessor
{
$this->apiModule = $apiModule;
$this->apiMethod = $apiMethod;
- $this->request = $request;
+ $this->setRequest($request);
$this->report = Report::factory($apiModule, $apiMethod);
$this->apiInconsistencies = new Inconsistencies();
- $this->formatter = new Formatter();
+ $this->setFormatter(new Formatter());
+ }
+
+ public function setFormatter(Formatter $formatter)
+ {
+ $this->formatter = $formatter;
+ }
+
+ public function setRequest($request)
+ {
+ $this->request = $request;
+ }
+
+ public function setCallbackBeforeGenericFilters($callbackBeforeGenericFilters)
+ {
+ $this->callbackBeforeGenericFilters = $callbackBeforeGenericFilters;
+ }
+
+ public function setCallbackAfterGenericFilters($callbackAfterGenericFilters)
+ {
+ $this->callbackAfterGenericFilters = $callbackAfterGenericFilters;
}
/**
@@ -85,22 +108,27 @@ 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);
+ if ($this->callbackBeforeGenericFilters) {
+ call_user_func($this->callbackBeforeGenericFilters, $dataTable);
+ }
+ $dataTable = $this->applyGenericFilters($dataTable);
$this->applyComputeProcessedMetrics($dataTable);
+ if ($this->callbackAfterGenericFilters) {
+ call_user_func($this->callbackAfterGenericFilters, $dataTable);
+ }
+
// we automatically safe decode all datatable labels (against xss)
$dataTable->queueFilter('SafeDecodeLabel');
-
$dataTable = $this->convertSegmentValueToSegment($dataTable);
$dataTable = $this->applyQueuedFilters($dataTable);
$dataTable = $this->applyRequestedColumnDeletion($dataTable);
$dataTable = $this->applyLabelFilter($dataTable);
$dataTable = $this->applyMetricsFormatting($dataTable);
-
return $dataTable;
}
@@ -145,7 +173,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/API/Proxy.php b/core/API/Proxy.php
index 0345693b62..0f8667d0d8 100644
--- a/core/API/Proxy.php
+++ b/core/API/Proxy.php
@@ -490,7 +490,7 @@ class Proxy extends Singleton
$hideLine = trim($hideLine);
$hideLine .= ' ';
- $token = trim(strtok($hideLine, " "), "\n");
+ $token = trim(strtok($hideLine, " "), "\n");
$hide = false;
@@ -529,18 +529,6 @@ class Proxy extends Singleton
}
/**
- * Returns the number of required parameters (parameters without default values).
- *
- * @param string $class The class name
- * @param string $name The method name
- * @return int The number of required parameters
- */
- private function getNumberOfRequiredParameters($class, $name)
- {
- return $this->metadataArray[$class][$name]['numberOfRequiredParameters'];
- }
-
- /**
* Returns true if the method is found in the API of the given class name.
*
* @param string $className The class name
diff --git a/core/API/Request.php b/core/API/Request.php
index b135a5f20e..2ed5dfc567 100644
--- a/core/API/Request.php
+++ b/core/API/Request.php
@@ -18,6 +18,7 @@ use Piwik\SettingsServer;
use Piwik\Url;
use Piwik\UrlHelper;
use Piwik\Log;
+use Piwik\Plugin\Manager as PluginManager;
/**
* Dispatches API requests to the appropriate API method.
@@ -218,12 +219,11 @@ class Request
list($module, $method) = $this->extractModuleAndMethod($moduleMethod);
- list($module, $method) = $this->getRenamedModuleAndAction($module, $method);
+ list($module, $method) = self::getRenamedModuleAndAction($module, $method);
+
+ PluginManager::getInstance()->checkIsPluginActivated($module);
- if (!\Piwik\Plugin\Manager::getInstance()->isPluginActivated($module)) {
- throw new PluginDeactivatedException($module);
- }
- $apiClassName = $this->getClassNameAPI($module);
+ $apiClassName = self::getClassNameAPI($module);
self::reloadAuthUsingTokenAuth($this->request);
diff --git a/core/API/ResponseBuilder.php b/core/API/ResponseBuilder.php
index 4ebe50305d..68e448b0b4 100644
--- a/core/API/ResponseBuilder.php
+++ b/core/API/ResponseBuilder.php
@@ -25,6 +25,7 @@ class ResponseBuilder
private $apiRenderer = null;
private $request = null;
private $sendHeader = true;
+ private $postProcessDataTable = true;
private $apiModule = false;
private $apiMethod = false;
@@ -45,6 +46,11 @@ class ResponseBuilder
$this->sendHeader = false;
}
+ public function disableDataTablePostProcessor()
+ {
+ $this->postProcessDataTable = false;
+ }
+
/**
* This method processes the data resulting from the API call.
*
@@ -164,8 +170,10 @@ class ResponseBuilder
private function handleDataTable(DataTableInterface $datatable)
{
- $postProcessor = new DataTablePostProcessor($this->apiModule, $this->apiMethod, $this->request);
- $datatable = $postProcessor->process($datatable);
+ if ($this->postProcessDataTable) {
+ $postProcessor = new DataTablePostProcessor($this->apiModule, $this->apiMethod, $this->request);
+ $datatable = $postProcessor->process($datatable);
+ }
return $this->apiRenderer->renderDataTable($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/ArchiveProcessor/Rules.php b/core/ArchiveProcessor/Rules.php
index a715623ca6..06b38aa27d 100644
--- a/core/ArchiveProcessor/Rules.php
+++ b/core/ArchiveProcessor/Rules.php
@@ -124,30 +124,15 @@ class Rules
*/
public static function shouldPurgeOutdatedArchives(Date $date)
{
- if (! self::isRequestAuthorizedToArchive()){
- Log::info("Purging temporary archives: skipped (no authorization)");
- return false;
- }
-
- $key = self::FLAG_TABLE_PURGED . "blob_" . $date->toString('Y_m');
- $timestamp = Option::get($key);
-
- // we shall purge temporary archives after their timeout is finished, plus an extra 6 hours
- // in case archiving is disabled or run once a day, we give it this extra time to run
- // and re-process more recent records...
- $temporaryArchivingTimeout = self::getTodayArchiveTimeToLive();
- $hoursBetweenPurge = 6;
- $purgeEveryNSeconds = max($temporaryArchivingTimeout, $hoursBetweenPurge * 3600);
-
// we only delete archives if we are able to process them, otherwise, the browser might process reports
// when &segment= is specified (or custom date range) and would below, delete temporary archives that the
// browser is not able to process until next cron run (which could be more than 1 hour away)
- if ($timestamp !== false && $timestamp >= time() - $purgeEveryNSeconds) {
- Log::info("Purging temporary archives: skipped (purging every " . $hoursBetweenPurge . "hours)");
+ if (! self::isRequestAuthorizedToArchive()){
+ Log::info("Purging temporary archives: skipped (no authorization)");
return false;
}
- Option::set($key, time());
+ $temporaryArchivingTimeout = self::getTodayArchiveTimeToLive();
if (self::isBrowserTriggerEnabled()) {
// If Browser Archiving is enabled, it is likely there are many more temporary archives
diff --git a/core/Common.php b/core/Common.php
index 7e3bcbc7af..1ed8d48935 100644
--- a/core/Common.php
+++ b/core/Common.php
@@ -638,26 +638,6 @@ class Common
}
/**
- * Convert IP address (in network address format) to presentation format.
- * This is a backward compatibility function for code that only expects
- * IPv4 addresses (i.e., doesn't support IPv6).
- *
- * @see IP::N2P()
- *
- * This function does not support the long (or its string representation)
- * returned by the built-in ip2long() function, from Piwik 1.3 and earlier.
- *
- * @deprecated 1.4
- *
- * @param string $ip IP address in network address format
- * @return string
- */
- public static function long2ip($ip)
- {
- return IP::long2ip($ip);
- }
-
- /**
* JSON encode wrapper
* - missing or broken in some php 5.x versions
*
diff --git a/core/Config.php b/core/Config.php
index 014424c119..e28f581ea8 100644
--- a/core/Config.php
+++ b/core/Config.php
@@ -455,10 +455,7 @@ class Config extends Singleton
: $this->configLocal[$name];
}
- if ($section === null && $name == 'superuser') {
- $user = $this->getConfigSuperUserForBackwardCompatibility();
- return $user;
- } else if ($section === null) {
+ if ($section === null) {
$section = array();
}
@@ -469,28 +466,6 @@ class Config extends Singleton
return $tmp;
}
- /**
- * @deprecated since version 2.0.4
- */
- public function getConfigSuperUserForBackwardCompatibility()
- {
- try {
- $db = Db::get();
- $user = $db->fetchRow("SELECT login, email, password
- FROM " . Common::prefixTable("user") . "
- WHERE superuser_access = 1
- ORDER BY date_registered ASC LIMIT 1");
-
- if (!empty($user)) {
- $user['bridge'] = 1;
- return $user;
- }
-
- } catch (Exception $e) {}
-
- return array();
- }
-
public function getFromGlobalConfig($name)
{
if (isset($this->configGlobal[$name])) {
diff --git a/core/Console.php b/core/Console.php
index de51756f39..3eb7a5104b 100644
--- a/core/Console.php
+++ b/core/Console.php
@@ -163,6 +163,7 @@ class Console extends Application
public static function initPlugins()
{
Plugin\Manager::getInstance()->loadActivatedPlugins();
+ Plugin\Manager::getInstance()->loadPluginTranslations();
}
private function getDefaultPiwikCommands()
diff --git a/core/CronArchive.php b/core/CronArchive.php
index 5c1b497649..29c897c8bf 100644
--- a/core/CronArchive.php
+++ b/core/CronArchive.php
@@ -825,11 +825,7 @@ class CronArchive
public function log($m)
{
$this->output .= $m . "\n";
- try {
- Log::info($m);
- } catch(Exception $e) {
- print($m . "\n");
- }
+ Log::info($m);
}
public function logError($m)
diff --git a/core/DataFiles/SearchEngines.php b/core/DataFiles/SearchEngines.php
index 9a40f96748..084c00e5e1 100644
--- a/core/DataFiles/SearchEngines.php
+++ b/core/DataFiles/SearchEngines.php
@@ -189,6 +189,8 @@ if (!isset($GLOBALS['Piwik_SearchEngines'])) {
'searchqu.com' => array('Ask'),
'search.tb.ask.com' => array('Ask'),
'nortonsafe.search.ask.com' => array('Ask'),
+ 'safesearch.avira.com' => array('Ask'),
+ 'avira.search.ask.com' => array('Ask'),
// Atlas
'searchatlas.centrum.cz' => array('Atlas', 'q', '?q={k}'),
@@ -283,6 +285,7 @@ if (!isset($GLOBALS['Piwik_SearchEngines'])) {
// DasOertliche
'www.dasoertliche.de' => array('DasOertliche', 'kw'),
+ 'www2.dasoertliche.de' => array('DasOertliche', array('ph', 'kw')),
// DasTelefonbuch
'www1.dastelefonbuch.de' => array('DasTelefonbuch', 'kw'),
@@ -453,6 +456,7 @@ if (!isset($GLOBALS['Piwik_SearchEngines'])) {
'suche.gmx.net' => array('Google', 'q', 'web?q={k}'),
'search.incredibar.com' => array('Google', 'q', 'search.php?q={k}'),
'www.delta-search.com' => array('Google', 'q'),
+ 'www1.delta-search.com' => array('Google', 'q'),
'search.1und1.de' => array('Google', 'q', 'web?q={k}'),
'search.zonealarm.com' => array('Google'),
'start.lenovo.com' => array('Google', 'q', 'search/index.php?q={k}'),
@@ -464,11 +468,8 @@ if (!isset($GLOBALS['Piwik_SearchEngines'])) {
'search.smt.docomo.ne.jp' => array('Google', 'MT'),
'image.search.smt.docomo.ne.jp' => array('Google', 'MT'),
'gfsoso.com' => array('Google', 'q'),
-
- // Google Earth
- // - 2010-09-13: are these redirects now?
- 'www.googleearth.de' => array('Google'),
- 'www.googleearth.fr' => array('Google'),
+ 'searches.safehomepage.com' => array('Google', 'q'),
+ 'searches.f-secure.com' => array('Google', 'query', 'search?query={k}'),
// Google Cache
'webcache.googleusercontent.com' => array('Google', '/\/search\?q=cache:[A-Za-z0-9]+:[^+]+([^&]+)/', 'search?q={k}'),
@@ -619,6 +620,8 @@ if (!isset($GLOBALS['Piwik_SearchEngines'])) {
'eu.ixquick.com' => array('Ixquick'),
's8-eu.ixquick.com' => array('Ixquick'),
's1-eu.ixquick.de' => array('Ixquick'),
+ 's2-eu4.ixquick.com' => array('Ixquick'),
+ 's5-eu4.ixquick.com' => array('Ixquick'),
// Jyxo
'jyxo.1188.cz' => array('Jyxo', 'q', 's?q={k}'),
@@ -680,6 +683,7 @@ if (!isset($GLOBALS['Piwik_SearchEngines'])) {
// Metager
'meta.rrzn.uni-hannover.de' => array('Metager', 'eingabe', 'meta/cgi-bin/meta.ger1?eingabe={k}'),
'www.metager.de' => array('Metager'),
+ 'metager.de' => array('Metager'),
// Metager2
'metager2.de' => array('Metager2', 'q', 'search/index.php?q={k}'),
@@ -756,6 +760,9 @@ if (!isset($GLOBALS['Piwik_SearchEngines'])) {
// PeoplePC
'search.peoplepc.com' => array('PeoplePC', 'q', 'search?q={k}'),
+ // PeopleCheck
+ 'extern.peoplecheck.de' => array('PeopleCheck', 'q', 'link.php?q={k}'),
+
// Picsearch
'www.picsearch.com' => array('Picsearch', 'q', 'index.cgi?q={k}'),
@@ -777,6 +784,9 @@ if (!isset($GLOBALS['Piwik_SearchEngines'])) {
'www.qualigo.de' => array('Qualigo'),
'www.qualigo.nl' => array('Qualigo'),
+ // Qwant
+ 'www.qwant.com' => array('Qwant', 'q'),
+
// Rakuten
'websearch.rakuten.co.jp' => array('Rakuten', 'qt', 'WebIS?qt={k}'),
@@ -837,6 +847,9 @@ if (!isset($GLOBALS['Piwik_SearchEngines'])) {
'm.sm.cn' => array('sm.cn', 'q', 's?q={k}'),
'm.sp.sm.cn' => array('sm.cn'),
+ // sm.de
+ 'www.sm.de' => array('sm.de', 'q', '?q={k}'),
+
// SmartAdressbar
'search.smartaddressbar.com' => array('SmartAddressbar', 's', '?s={k}'),
@@ -1033,7 +1046,7 @@ if (!isset($GLOBALS['Piwik_SearchEngines'])) {
// '{}.dir.yahoo.com' => array('Yahoo! Directory'),
// Yahoo! Images
- 'images.search.yahoo.com' => array('Yahoo! Images', 'p', 'search/images?p={k}'),
+ 'images.search.yahoo.com' => array('Yahoo! Images', array('p', 'va'), 'search/images?p={k}'),
// '*.images.search.yahoo.com'=> array('Yahoo! Images'), // see built-in helper in Common.php
'{}.images.yahoo.com' => array('Yahoo! Images'),
'cade.images.yahoo.com' => array('Yahoo! Images'),
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/PivotByDimension.php b/core/DataTable/Filter/PivotByDimension.php
index 61e68423e8..3eeff39b44 100644
--- a/core/DataTable/Filter/PivotByDimension.php
+++ b/core/DataTable/Filter/PivotByDimension.php
@@ -286,13 +286,12 @@ class PivotByDimension extends BaseFilter
return null;
}
- if ($row->isSubtableLoaded()) {
- $subtable = $row->getSubtable();
- } else {
+ $subtable = $row->getSubtable();
+ if (!$subtable) {
$subtable = $this->thisReport->fetchSubtable($idSubtable, $this->getRequestParamOverride($table));
}
- if ($subtable === null) { // sanity check
+ if (!$subtable) { // sanity check
throw new Exception("Unexpected error: could not load subtable '$idSubtable'.");
}
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/FrontController.php b/core/FrontController.php
index 8136b520bf..242ba03df6 100644
--- a/core/FrontController.php
+++ b/core/FrontController.php
@@ -14,10 +14,9 @@ use Piwik\API\Request;
use Piwik\API\ResponseBuilder;
use Piwik\Container\StaticContainer;
use Piwik\Exception\AuthenticationFailedException;
+use Piwik\Http\ControllerResolver;
use Piwik\Http\Router;
-use Piwik\Plugin\Controller;
use Piwik\Plugin\Report;
-use Piwik\Plugin\Widgets;
use Piwik\Session;
/**
@@ -110,83 +109,6 @@ class FrontController extends Singleton
}
}
- protected function makeController($module, $action, &$parameters)
- {
- $container = StaticContainer::getContainer();
-
- $controllerClassName = $this->getClassNameController($module);
-
- // TRY TO FIND ACTION IN CONTROLLER
- if (class_exists($controllerClassName)) {
-
- $class = $this->getClassNameController($module);
- /** @var $controller Controller */
- $controller = $container->make($class);
-
- $controllerAction = $action;
- if ($controllerAction === false) {
- $controllerAction = $controller->getDefaultAction();
- }
-
- if (is_callable(array($controller, $controllerAction))) {
-
- return array($controller, $controllerAction);
- }
-
- if ($action === false) {
- $this->triggerControllerActionNotFoundError($module, $controllerAction);
- }
-
- }
-
- // TRY TO FIND ACTION IN WIDGET
- $widget = Widgets::factory($module, $action);
-
- if (!empty($widget)) {
-
- $parameters['widgetModule'] = $module;
- $parameters['widgetMethod'] = $action;
-
- return array($container->make('Piwik\Plugins\CoreHome\Controller'), 'renderWidget');
- }
-
- // TRY TO FIND ACTION IN REPORT
- $report = Report::factory($module, $action);
-
- if (!empty($report)) {
-
- $parameters['reportModule'] = $module;
- $parameters['reportAction'] = $action;
-
- return array($container->make('Piwik\Plugins\CoreHome\Controller'), 'renderReportWidget');
- }
-
- if (!empty($action) && Report::PREFIX_ACTION_IN_MENU === substr($action, 0, strlen(Report
- ::PREFIX_ACTION_IN_MENU))) {
- $reportAction = lcfirst(substr($action, 4)); // menuGetPageUrls => getPageUrls
- $report = Report::factory($module, $reportAction);
-
- if (!empty($report)) {
- $parameters['reportModule'] = $module;
- $parameters['reportAction'] = $reportAction;
-
- return array($container->make('Piwik\Plugins\CoreHome\Controller'), 'renderReportMenu');
- }
- }
-
- $this->triggerControllerActionNotFoundError($module, $action);
- }
-
- protected function triggerControllerActionNotFoundError($module, $action)
- {
- throw new Exception("Action '$action' not found in the module '$module'.");
- }
-
- protected function getClassNameController($module)
- {
- return "\\Piwik\\Plugins\\$module\\Controller";
- }
-
/**
* Executes the requested plugin controller method and returns the data, capturing anything the
* method `echo`s.
@@ -195,7 +117,7 @@ class FrontController extends Singleton
* of whatever is in the output buffer._
*
* @param string $module The name of the plugin whose controller to execute, eg, `'UserCountryMap'`.
- * @param string $action The controller action name, eg, `'realtimeMap'`.
+ * @param string $actionName The controller action name, eg, `'realtimeMap'`.
* @param array $parameters Array of parameters to pass to the controller action method.
* @return string The `echo`'d data or the return value of the controller action.
* @deprecated
@@ -587,7 +509,10 @@ class FrontController extends Singleton
*/
Piwik::postEvent('Request.dispatch', array(&$module, &$action, &$parameters));
- list($controller, $actionToCall) = $this->makeController($module, $action, $parameters);
+ /** @var ControllerResolver $controllerResolver */
+ $controllerResolver = StaticContainer::get('Piwik\Http\ControllerResolver');
+
+ $controller = $controllerResolver->getController($module, $action, $parameters);
/**
* Triggered directly before controller actions are dispatched.
@@ -602,7 +527,7 @@ class FrontController extends Singleton
*/
Piwik::postEvent(sprintf('Controller.%s.%s', $module, $action), array(&$parameters));
- $result = call_user_func_array(array($controller, $actionToCall), $parameters);
+ $result = call_user_func_array($controller, $parameters);
/**
* Triggered after a controller action is successfully called.
@@ -628,6 +553,7 @@ class FrontController extends Singleton
* @param array $parameters The arguments passed to the controller action.
*/
Piwik::postEvent('Request.dispatch.end', array(&$result, $module, $action, $parameters));
+
return $result;
}
diff --git a/core/Http.php b/core/Http.php
index 25d6d59566..08bcbcef15 100644
--- a/core/Http.php
+++ b/core/Http.php
@@ -179,7 +179,7 @@ class Http
throw new Exception('Malformed URL: ' . $aUrl);
}
- if ($url['scheme'] != 'http') {
+ if ($url['scheme'] != 'http' && $url['scheme'] != 'https') {
throw new Exception('Invalid protocol/scheme: ' . $url['scheme']);
}
$host = $url['host'];
@@ -405,7 +405,11 @@ class Http
}
fclose($handle);
} else {
- $response = file_get_contents($aUrl, 0, $ctx);
+ $response = @file_get_contents($aUrl, 0, $ctx);
+ if ($response === false) {
+ $error = error_get_last();
+ throw new \Exception($error['message']);
+ }
$fileLength = strlen($response);
}
diff --git a/core/Http/ControllerResolver.php b/core/Http/ControllerResolver.php
new file mode 100644
index 0000000000..8d58f0ed73
--- /dev/null
+++ b/core/Http/ControllerResolver.php
@@ -0,0 +1,142 @@
+<?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\Http;
+
+use DI\FactoryInterface;
+use Exception;
+use Piwik\Plugin\Controller;
+use Piwik\Plugin\Report;
+use Piwik\Plugin\Widgets;
+use Piwik\Session;
+
+/**
+ * Resolves the controller that will handle the request.
+ *
+ * A controller is a PHP callable.
+ */
+class ControllerResolver
+{
+ /**
+ * @var FactoryInterface
+ */
+ private $abstractFactory;
+
+ public function __construct(FactoryInterface $abstractFactory)
+ {
+ $this->abstractFactory = $abstractFactory;
+ }
+
+ /**
+ * @param string $module
+ * @param string|null $action
+ * @param array $parameters
+ * @throws Exception Controller not found.
+ * @return callable The controller is a PHP callable.
+ */
+ public function getController($module, $action, array &$parameters)
+ {
+ $controller = $this->createPluginController($module, $action);
+ if ($controller) {
+ return $controller;
+ }
+
+ $controller = $this->createWidgetController($module, $action, $parameters);
+ if ($controller) {
+ return $controller;
+ }
+
+ $controller = $this->createReportController($module, $action, $parameters);
+ if ($controller) {
+ return $controller;
+ }
+
+ $controller = $this->createReportMenuController($module, $action, $parameters);
+ if ($controller) {
+ return $controller;
+ }
+
+ throw new Exception(sprintf("Action '%s' not found in the module '%s'", $action, $module));
+ }
+
+ private function createPluginController($module, $action)
+ {
+ $controllerClass = "Piwik\\Plugins\\$module\\Controller";
+ if (!class_exists($controllerClass)) {
+ return null;
+ }
+
+ /** @var $controller Controller */
+ $controller = $this->abstractFactory->make($controllerClass);
+
+ $action = $action ?: $controller->getDefaultAction();
+
+ if (!is_callable(array($controller, $action))) {
+ return null;
+ }
+
+ return array($controller, $action);
+ }
+
+ private function createWidgetController($module, $action, array &$parameters)
+ {
+ $widget = Widgets::factory($module, $action);
+
+ if (!$widget) {
+ return null;
+ }
+
+ $parameters['widget'] = $widget;
+ $parameters['method'] = $action;
+
+ return array($this->createCoreHomeController(), 'renderWidget');
+ }
+
+ private function createReportController($module, $action, array &$parameters)
+ {
+ $report = Report::factory($module, $action);
+
+ if (!$report) {
+ return null;
+ }
+
+ $parameters['report'] = $report;
+
+ return array($this->createCoreHomeController(), 'renderReportWidget');
+ }
+
+ private function createReportMenuController($module, $action, array &$parameters)
+ {
+ if (!$this->isReportMenuAction($action)) {
+ return null;
+ }
+
+ $action = lcfirst(substr($action, 4)); // menuGetPageUrls => getPageUrls
+ $report = Report::factory($module, $action);
+
+ if (!$report) {
+ return null;
+ }
+
+ $parameters['report'] = $report;
+
+ return array($this->createCoreHomeController(), 'renderReportMenu');
+ }
+
+ private function isReportMenuAction($action)
+ {
+ $startsWithMenu = (Report::PREFIX_ACTION_IN_MENU === substr($action, 0, strlen(Report::PREFIX_ACTION_IN_MENU)));
+
+ return !empty($action) && $startsWithMenu;
+ }
+
+ private function createCoreHomeController()
+ {
+ return $this->abstractFactory->make('Piwik\Plugins\CoreHome\Controller');
+ }
+}
diff --git a/core/IP.php b/core/IP.php
index e952ddbe27..b6ec2c2d22 100644
--- a/core/IP.php
+++ b/core/IP.php
@@ -10,8 +10,6 @@
namespace Piwik;
use Piwik\Network\IPUtils;
-use Piwik\Network\IPv4;
-use Piwik\Network\IPv6;
/**
* Contains IP address helper functions (for both IPv4 and IPv6).
@@ -40,223 +38,6 @@ use Piwik\Network\IPv6;
class IP
{
/**
- * Removes the port and the last portion of a CIDR IP address.
- *
- * @param string $ipString The IP address to sanitize.
- * @return string
- *
- * @deprecated Use IPUtils::sanitizeIp() instead
- * @see \Piwik\Network\IPUtils
- */
- public static function sanitizeIp($ipString)
- {
- return IPUtils::sanitizeIp($ipString);
- }
-
- /**
- * Sanitize human-readable (user-supplied) IP address range.
- *
- * Accepts the following formats for $ipRange:
- * - single IPv4 address, e.g., 127.0.0.1
- * - single IPv6 address, e.g., ::1/128
- * - IPv4 block using CIDR notation, e.g., 192.168.0.0/22 represents the IPv4 addresses from 192.168.0.0 to 192.168.3.255
- * - IPv6 block using CIDR notation, e.g., 2001:DB8::/48 represents the IPv6 addresses from 2001:DB8:0:0:0:0:0:0 to 2001:DB8:0:FFFF:FFFF:FFFF:FFFF:FFFF
- * - wildcards, e.g., 192.168.0.*
- *
- * @param string $ipRangeString IP address range
- * @return string|bool IP address range in CIDR notation OR false
- *
- * @deprecated Use IPUtils::sanitizeIpRange() instead
- * @see \Piwik\Network\IPUtils
- */
- public static function sanitizeIpRange($ipRangeString)
- {
- $result = IPUtils::sanitizeIpRange($ipRangeString);
-
- return $result === null ? false : $result;
- }
-
- /**
- * Converts an IP address in presentation format to network address format.
- *
- * @param string $ipString IP address, either IPv4 or IPv6, e.g., `"127.0.0.1"`.
- * @return string Binary-safe string, e.g., `"\x7F\x00\x00\x01"`.
- *
- * @deprecated Use IPUtils::stringToBinaryIP() instead
- * @see \Piwik\Network\IPUtils
- */
- public static function P2N($ipString)
- {
- return IPUtils::stringToBinaryIP($ipString);
- }
-
- /**
- * Convert network address format to presentation format.
- *
- * See also {@link prettyPrint()}.
- *
- * @param string $ip IP address in network address format.
- * @return string IP address in presentation format.
- *
- * @deprecated Use IPUtils::binaryToStringIP() instead
- */
- public static function N2P($ip)
- {
- return IPUtils::binaryToStringIP($ip);
- }
-
- /**
- * Alias for {@link N2P()}.
- *
- * @param string $ip IP address in network address format.
- * @return string IP address in presentation format.
- *
- * @deprecated Will be removed
- */
- public static function prettyPrint($ip)
- {
- return IPUtils::binaryToStringIP($ip);
- }
-
- /**
- * Returns true if `$ip` is an IPv4, IPv4-compat, or IPv4-mapped address, false
- * if otherwise.
- *
- * @param string $ip IP address in network address format.
- * @return bool True if IPv4, else false.
- *
- * @deprecated Will be removed
- * @see \Piwik\Network\IP
- */
- public static function isIPv4($ip)
- {
- $ip = Network\IP::fromBinaryIP($ip);
-
- return $ip instanceof IPv4;
- }
-
- /**
- * Converts an IP address (in network address format) to presentation format.
- * This is a backward compatibility function for code that only expects
- * IPv4 addresses (i.e., doesn't support IPv6).
- *
- * This function does not support the long (or its string representation)
- * returned by the built-in ip2long() function, from Piwik 1.3 and earlier.
- *
- * @param string $ip IPv4 address in network address format.
- * @return string IP address in presentation format.
- *
- * @deprecated This method was kept for backward compatibility and doesn't seem used
- */
- public static function long2ip($ip)
- {
- // IPv4
- if (strlen($ip) == 4) {
- return IPUtils::binaryToStringIP($ip);
- }
-
- // IPv6 - transitional address?
- if (strlen($ip) == 16) {
- if (substr_compare($ip, "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff", 0, 12) === 0
- || substr_compare($ip, "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", 0, 12) === 0
- ) {
- // remap 128-bit IPv4-mapped and IPv4-compat addresses
- return IPUtils::binaryToStringIP(substr($ip, 12));
- }
- }
-
- return '0.0.0.0';
- }
-
- /**
- * Returns true if $ip is an IPv6 address, false if otherwise. This function does
- * a naive check. It assumes that whatever format $ip is in, it is well-formed.
- *
- * @param string $ip
- * @return bool
- *
- * @deprecated Will be removed
- * @see \Piwik\Network\IP
- */
- public static function isIPv6($ip)
- {
- $ip = Network\IP::fromBinaryIP($ip);
-
- return $ip instanceof IPv6;
- }
-
- /**
- * Returns true if $ip is a IPv4 mapped address, false if otherwise.
- *
- * @param string $ip
- * @return bool
- *
- * @deprecated Will be removed
- * @see \Piwik\Network\IP
- */
- public static function isMappedIPv4($ip)
- {
- $ip = Network\IP::fromStringIP($ip);
-
- if (! $ip instanceof IPv6) {
- return false;
- }
-
- return $ip->isMappedIPv4();
- }
-
- /**
- * Returns an IPv4 address from a 'mapped' IPv6 address.
- *
- * @param string $ip eg, `'::ffff:192.0.2.128'`
- * @return string eg, `'192.0.2.128'`
- *
- * @deprecated Use Piwik\Network\IP::toIPv4String() instead
- * @see \Piwik\Network\IP
- */
- public static function getIPv4FromMappedIPv6($ip)
- {
- $ip = Network\IP::fromStringIP($ip);
-
- return $ip->toIPv4String();
- }
-
- /**
- * Get low and high IP addresses for a specified range.
- *
- * @param array $ipRange An IP address range in presentation format.
- * @return array|bool Array `array($lowIp, $highIp)` in network address format, or false on failure.
- *
- * @deprecated Use Piwik\Network\IPUtils::getIPRangeBounds() instead
- * @see \Piwik\Network\IPUtils
- */
- public static function getIpsForRange($ipRange)
- {
- $result = IPUtils::getIPRangeBounds($ipRange);
-
- return $result === null ? false : $result;
- }
-
- /**
- * Determines if an IP address is in a specified IP address range.
- *
- * An IPv4-mapped address should be range checked with an IPv4-mapped address range.
- *
- * @param string $ip IP address in network address format
- * @param array $ipRanges List of IP address ranges
- * @return bool True if in any of the specified IP address ranges; else false.
- *
- * @deprecated Use Piwik\Network\IP::isInRanges() instead
- * @see \Piwik\Network\IP
- */
- public static function isIpInRange($ip, $ipRanges)
- {
- $ip = Network\IP::fromBinaryIP($ip);
-
- return $ip->isInRanges($ipRanges);
- }
-
- /**
* Returns the most accurate IP address availble for the current user, in
* IPv4 format. This could be the proxy client's IP address.
*
@@ -333,22 +114,4 @@ class IP
}
return trim(Common::sanitizeInputValue($csv));
}
-
- /**
- * Returns the hostname for a given IP address.
- *
- * @param string $ipStr Human-readable IP address.
- * @return string The hostname or unmodified $ipStr on failure.
- *
- * @deprecated Use Piwik\Network\IP::getHostname() instead
- * @see \Piwik\Network\IP
- */
- public static function getHostByAddr($ipStr)
- {
- $ip = Network\IP::fromStringIP($ipStr);
-
- $host = $ip->getHostname();
-
- return $host === null ? $ipStr : $host;
- }
}
diff --git a/core/Menu/MenuAdmin.php b/core/Menu/MenuAdmin.php
index 5335091285..9c05734e5f 100644
--- a/core/Menu/MenuAdmin.php
+++ b/core/Menu/MenuAdmin.php
@@ -33,24 +33,6 @@ use Piwik\Piwik;
class MenuAdmin extends MenuAbstract
{
/**
- * Adds a new AdminMenu entry under the 'Settings' category.
- *
- * @param string $adminMenuName The name of the admin menu entry. Can be a translation token.
- * @param string|array $url The URL the admin menu entry should link to, or an array of query parameters
- * that can be used to build the URL.
- * @param boolean $displayedForCurrentUser Whether this menu entry should be displayed for the
- * current user. If false, the entry will not be added.
- * @param int $order The order hint.
- * @deprecated since version 2.4.0. See {@link Piwik\Plugin\Menu} for new implementation.
- */
- public static function addEntry($adminMenuName, $url, $displayedForCurrentUser = true, $order = 20)
- {
- if ($displayedForCurrentUser) {
- self::getInstance()->addItem('General_Settings', $adminMenuName, $url, $order);
- }
- }
-
- /**
* See {@link add()}. Adds a new menu item to the development section of the admin menu.
* @param string $menuName
* @param array $url
@@ -142,12 +124,4 @@ class MenuAdmin extends MenuAbstract
return parent::getMenu();
}
-
- /**
- * @deprecated since version 2.4.0. See {@link Piwik\Plugin\Menu} for new implementation.
- */
- public static function removeEntry($menuName, $subMenuName = false)
- {
- MenuAdmin::getInstance()->remove($menuName, $subMenuName);
- }
}
diff --git a/core/Menu/MenuTop.php b/core/Menu/MenuTop.php
index 55c09c57af..87ccb3c5f8 100644
--- a/core/Menu/MenuTop.php
+++ b/core/Menu/MenuTop.php
@@ -32,37 +32,6 @@ use Piwik\Piwik;
class MenuTop extends MenuAbstract
{
/**
- * Adds a new entry to the TopMenu.
- *
- * @param string $topMenuName The menu item name. Can be a translation token.
- * @param string|array $url The URL the admin menu entry should link to, or an array of query parameters
- * that can be used to build the URL. If `$isHTML` is true, this can be a string with
- * HTML that is simply embedded.
- * @param boolean $displayedForCurrentUser Whether this menu entry should be displayed for the
- * current user. If false, the entry will not be added.
- * @param int $order The order hint.
- * @param bool $isHTML Whether `$url` is an HTML string or a URL that will be rendered as a link.
- * @param bool|string $tooltip Optional tooltip to display.
- * @deprecated since version 2.4.0. See {@link Piwik\Plugin\Menu} for new implementation.
- */
- public static function addEntry($topMenuName, $url, $displayedForCurrentUser = true, $order = 10, $isHTML = false, $tooltip = false)
- {
- if ($isHTML) {
- MenuTop::getInstance()->addHtml($topMenuName, $url, $displayedForCurrentUser, $order, $tooltip);
- } else {
- MenuTop::getInstance()->add($topMenuName, null, $url, $displayedForCurrentUser, $order, $tooltip);
- }
- }
-
- /**
- * @deprecated since version 2.4.0. See {@link Piwik\Plugin\Menu} for new implementation.
- */
- public static function removeEntry($menuName, $subMenuName = false)
- {
- MenuTop::getInstance()->remove($menuName, $subMenuName);
- }
-
- /**
* Directly adds a menu entry containing html.
*
* @param string $menuName
diff --git a/core/Period.php b/core/Period.php
index d4044ed68e..b924e981c8 100644
--- a/core/Period.php
+++ b/core/Period.php
@@ -9,7 +9,6 @@
namespace Piwik;
use Piwik\Container\StaticContainer;
-use Piwik\Period\Factory as PeriodFactory;
use Piwik\Period\Range;
use Piwik\Translation\Translator;
@@ -66,17 +65,6 @@ abstract class Period
}
/**
- * @deprecated Use Factory::build instead
- * @param $period
- * @param $date
- * @return Period
- */
- public static function factory($period, $date)
- {
- return PeriodFactory::build($period, $date);
- }
-
- /**
* Returns true if `$dateString` and `$period` represent multiple periods.
*
* Will return true for date/period combinations where date references multiple
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/Manager.php b/core/Plugin/Manager.php
index 7b85c49a5a..22eb0296d1 100644
--- a/core/Plugin/Manager.php
+++ b/core/Plugin/Manager.php
@@ -21,6 +21,7 @@ use Piwik\Log;
use Piwik\Option;
use Piwik\Piwik;
use Piwik\Plugin;
+use Piwik\PluginDeactivatedException;
use Piwik\Singleton;
use Piwik\Theme;
use Piwik\Tracker;
@@ -266,6 +267,19 @@ class Manager extends Singleton
}
/**
+ * Checks whether the given plugin is activated, if not triggers an exception.
+ *
+ * @param string $pluginName
+ * @throws PluginDeactivatedException
+ */
+ public function checkIsPluginActivated($pluginName)
+ {
+ if (!$this->isPluginActivated($pluginName)) {
+ throw new PluginDeactivatedException($pluginName);
+ }
+ }
+
+ /**
* Returns `true` if plugin is loaded (in memory).
*
* @param string $name Name of plugin, eg, `'Acions'`.
diff --git a/core/Plugin/Metric.php b/core/Plugin/Metric.php
index e475eb837f..4122dfeedf 100644
--- a/core/Plugin/Metric.php
+++ b/core/Plugin/Metric.php
@@ -108,29 +108,44 @@ 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]);
- }
- } else {
- $value = @$row[$columnName];
- if ($value === null
- && isset($mappingNameToId[$columnName])
- ) {
- $columnName = $mappingNameToId[$columnName];
- $value = @$row[$columnName];
+
+ if ($value === false) {
+
+ if (empty($mappingNameToId)) {
+ $mappingNameToId = Metrics::getMappingFromNameToId();
+ }
+
+ if (isset($mappingNameToId[$columnName])) {
+ return $row->getColumn($mappingNameToId[$columnName]);
+ }
}
+
return $value;
+
+ } elseif (!empty($row)) {
+
+ if (array_key_exists($columnName, $row)) {
+ return $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 null;
}
/**
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/Plugin/ViewDataTable.php b/core/Plugin/ViewDataTable.php
index 4f3b854127..a49358e48b 100644
--- a/core/Plugin/ViewDataTable.php
+++ b/core/Plugin/ViewDataTable.php
@@ -316,7 +316,7 @@ abstract class ViewDataTable implements ViewInterface
return new VizRequest();
}
- protected function loadDataTableFromAPI($fixedRequestParams = array())
+ protected function loadDataTableFromAPI()
{
if (!is_null($this->dataTable)) {
// data table is already there
@@ -324,7 +324,7 @@ abstract class ViewDataTable implements ViewInterface
return $this->dataTable;
}
- $this->dataTable = $this->request->loadDataTableFromAPI($fixedRequestParams);
+ $this->dataTable = $this->request->loadDataTableFromAPI();
return $this->dataTable;
}
diff --git a/core/Plugin/Visualization.php b/core/Plugin/Visualization.php
index 15b468e362..3d48c24d15 100644
--- a/core/Plugin/Visualization.php
+++ b/core/Plugin/Visualization.php
@@ -10,6 +10,8 @@
namespace Piwik\Plugin;
use Piwik\API\DataTablePostProcessor;
+use Piwik\API\Proxy;
+use Piwik\API\ResponseBuilder;
use Piwik\Common;
use Piwik\DataTable;
use Piwik\Date;
@@ -23,6 +25,8 @@ use Piwik\Plugins\API\API as ApiApi;
use Piwik\Plugins\PrivacyManager\PrivacyManager;
use Piwik\View;
use Piwik\ViewDataTable\Manager as ViewDataTableManager;
+use Piwik\Plugin\Manager as PluginManager;
+use Piwik\API\Request as ApiRequest;
/**
* The base class for report visualizations that output HTML and use JavaScript.
@@ -173,8 +177,7 @@ class Visualization extends ViewDataTable
try {
$this->beforeLoadDataTable();
-
- $this->loadDataTableFromAPI(array('disable_generic_filters' => 1, 'format_metrics' => 0));
+ $this->loadDataTableFromAPI();
$this->postDataTableLoadedFromAPI();
$requestPropertiesAfterLoadDataTable = $this->requestConfig->getProperties();
@@ -233,6 +236,35 @@ class Visualization extends ViewDataTable
return $view;
}
+ /**
+ * @internal
+ */
+ protected function loadDataTableFromAPI()
+ {
+ if (!is_null($this->dataTable)) {
+ // data table is already there
+ // this happens when setDataTable has been used
+ return $this->dataTable;
+ }
+
+ // we build the request (URL) to call the API
+ $request = $this->buildApiRequestArray();
+
+ $module = $this->requestConfig->getApiModuleToRequest();
+ $method = $this->requestConfig->getApiMethodToRequest();
+
+ PluginManager::getInstance()->checkIsPluginActivated($module);
+
+ $class = ApiRequest::getClassNameAPI($module);
+ $dataTable = Proxy::getInstance()->call($class, $method, $request);
+
+ $response = new ResponseBuilder($format = 'original', $request);
+ $response->disableSendHeader();
+ $response->disableDataTablePostProcessor();
+
+ $this->dataTable = $response->getResponse($dataTable, $module, $method);
+ }
+
private function getReportMetadata()
{
$request = $this->request->getRequestArray() + $_GET + $_POST;
@@ -255,11 +287,16 @@ class Visualization extends ViewDataTable
$this->config->footer_icons = ViewDataTableManager::configureFooterIcons($this);
}
- if (!\Piwik\Plugin\Manager::getInstance()->isPluginActivated('Goals')) {
+ if (!$this->isPluginActivated('Goals')) {
$this->config->show_goals = false;
}
}
+ private function isPluginActivated($pluginName)
+ {
+ return PluginManager::getInstance()->isPluginActivated($pluginName);
+ }
+
/**
* Assigns a template variable making it available in the Twig template specified by
* {@link TEMPLATE_FILE}.
@@ -357,48 +394,39 @@ class Visualization extends ViewDataTable
private function applyFilters()
{
- list($priorityFilters, $otherFilters) = $this->config->getFiltersToRun();
+ $postProcessor = $this->makeDataTablePostProcessor(); // must be created after requestConfig is final
+ $self = $this;
- // First, filters that delete rows
- foreach ($priorityFilters as $filter) {
- $this->dataTable->filter($filter[0], $filter[1]);
- }
+ $postProcessor->setCallbackBeforeGenericFilters(function (DataTable\DataTableInterface $dataTable) use ($self, $postProcessor) {
- $this->beforeGenericFiltersAreAppliedToLoadedDataTable();
+ // First, filters that delete rows
+ foreach ($self->config->getPriorityFilters() as $filter) {
+ $dataTable->filter($filter[0], $filter[1]);
+ }
- if (!in_array($this->requestConfig->filter_sort_column, $this->config->columns_to_display)) {
- $hasNbUniqVisitors = in_array('nb_uniq_visitors', $this->config->columns_to_display);
- $this->requestConfig->setDefaultSort($this->config->columns_to_display, $hasNbUniqVisitors, $this->dataTable->getColumns());
- }
+ $self->beforeGenericFiltersAreAppliedToLoadedDataTable();
- $postProcessor = $this->makeDataTablePostProcessor(); // must be created after requestConfig is final
+ if (!in_array($self->requestConfig->filter_sort_column, $self->config->columns_to_display)) {
+ $hasNbUniqVisitors = in_array('nb_uniq_visitors', $self->config->columns_to_display);
+ $columns = $dataTable->getColumns();
+ $self->requestConfig->setDefaultSort($self->config->columns_to_display, $hasNbUniqVisitors, $columns);
+ }
- if (!$this->requestConfig->areGenericFiltersDisabled()) {
- $this->dataTable = $postProcessor->applyGenericFilters($this->dataTable);
- }
+ $postProcessor->setRequest($self->buildApiRequestArray());
+ });
- $postProcessor->applyComputeProcessedMetrics($this->dataTable);
+ $postProcessor->setCallbackAfterGenericFilters(function (DataTable\DataTableInterface $dataTable) use ($self) {
- $this->afterGenericFiltersAreAppliedToLoadedDataTable();
+ $self->afterGenericFiltersAreAppliedToLoadedDataTable();
- // queue other filters so they can be applied later if queued filters are disabled
- foreach ($otherFilters as $filter) {
- $this->dataTable->queueFilter($filter[0], $filter[1]);
- }
+ // queue other filters so they can be applied later if queued filters are disabled
+ foreach ($self->config->getPresentationFilters() as $filter) {
+ $dataTable->queueFilter($filter[0], $filter[1]);
+ }
- // Finally, apply datatable filters that were queued (should be 'presentation' filters that
- // do not affect the number of rows)
- if (!$this->requestConfig->areQueuedFiltersDisabled()) {
- $this->dataTable->applyQueuedFilters();
- }
+ });
- if ($this->requestConfig->shouldFormatMetrics()) {
- $formatter = $this->metricsFormatter;
- $report = $this->report;
- $this->dataTable->filter(function (DataTable $table) use ($formatter, $report) {
- $formatter->formatMetrics($table, $report);
- });
- }
+ $this->dataTable = $postProcessor->process($this->dataTable);
}
private function removeEmptyColumnsFromDisplay()
@@ -456,7 +484,7 @@ class Visualization extends ViewDataTable
*/
private function hasReportBeenPurged()
{
- if (!\Piwik\Plugin\Manager::getInstance()->isPluginActivated('PrivacyManager')) {
+ if (!$this->isPluginActivated('PrivacyManager')) {
return false;
}
@@ -629,15 +657,14 @@ class Visualization extends ViewDataTable
private function makeDataTablePostProcessor()
{
- $requestArray = $this->request->getRequestArray();
- $request = \Piwik\API\Request::getRequestArrayFromString($requestArray);
+ $request = $this->buildApiRequestArray();
+ $module = $this->requestConfig->getApiModuleToRequest();
+ $method = $this->requestConfig->getApiMethodToRequest();
- if (false === $this->config->enable_sort) {
- $request['filter_sort_column'] = '';
- $request['filter_sort_order'] = '';
- }
+ $processor = new DataTablePostProcessor($module, $method, $request);
+ $processor->setFormatter($this->metricsFormatter);
- return new DataTablePostProcessor($this->requestConfig->getApiModuleToRequest(), $this->requestConfig->getApiMethodToRequest(), $request);
+ return $processor;
}
private function logMessageIfRequestPropertiesHaveChanged(array $requestPropertiesBefore)
@@ -685,4 +712,30 @@ class Visualization extends ViewDataTable
return $result;
}
+
+ /**
+ * @internal
+ *
+ * @return array
+ */
+ public function buildApiRequestArray()
+ {
+ $requestArray = $this->request->getRequestArray();
+ $request = APIRequest::getRequestArrayFromString($requestArray);
+
+ if (false === $this->config->enable_sort) {
+ $request['filter_sort_column'] = '';
+ $request['filter_sort_order'] = '';
+ }
+
+ if (!array_key_exists('format_metrics', $request) || $request['format_metrics'] === 'bc') {
+ $request['format_metrics'] = '1';
+ }
+
+ if (!$this->requestConfig->disable_queued_filters && array_key_exists('disable_queued_filters', $request)) {
+ unset($request['disable_queued_filters']);
+ }
+
+ return $request;
+ }
}
diff --git a/core/Plugin/Widgets.php b/core/Plugin/Widgets.php
index 3199e7a80b..36081fe2d2 100644
--- a/core/Plugin/Widgets.php
+++ b/core/Plugin/Widgets.php
@@ -125,6 +125,7 @@ class Widgets
/**
* @ignore
+ * @return Widgets|null
*/
public static function factory($module, $action)
{
diff --git a/core/Session.php b/core/Session.php
index 9e49987072..9733593205 100644
--- a/core/Session.php
+++ b/core/Session.php
@@ -124,8 +124,7 @@ class Session extends Zend_Session
we recommend that you <a href='http://piwik.org/faq/how-to-install/#faq_133' rel='noreferrer' target='_blank'>enable database session storage</a>.";
}
- $pathToSessions = Filechecks::getErrorMessageMissingPermissions(Filesystem::getPathToPiwikRoot() . '/tmp/sessions/');
- $pathToSessions = SettingsPiwik::rewriteTmpPathWithInstanceId($pathToSessions);
+ $pathToSessions = Filechecks::getErrorMessageMissingPermissions(self::getSessionsDirectory());
$message = sprintf("Error: %s %s %s\n<pre>Debug: the original error was \n%s</pre>",
Piwik::translate('General_ExceptionUnableToStartSession'),
$pathToSessions,
@@ -147,8 +146,7 @@ class Session extends Zend_Session
*/
public static function getSessionsDirectory()
{
- $path = PIWIK_USER_PATH . '/tmp/sessions';
- return SettingsPiwik::rewriteTmpPathWithInstanceId($path);
+ return StaticContainer::get('path.tmp') . '/sessions';
}
public static function close()
diff --git a/core/SettingsPiwik.php b/core/SettingsPiwik.php
index 6a30ddfecb..7a731b9b98 100644
--- a/core/SettingsPiwik.php
+++ b/core/SettingsPiwik.php
@@ -267,21 +267,6 @@ class SettingsPiwik
}
/**
- * If Piwik uses per-domain config file, also make tmp/ folder per-domain
- * @param $path
- * @return string
- * @throws \Exception
- *
- * @deprecated Get the 'path.tmp' config from the container instead.
- */
- public static function rewriteTmpPathWithInstanceId($path)
- {
- $tmp = '/tmp/';
- $path = self::rewritePathAppendPiwikInstanceId($path, $tmp);
- return $path;
- }
-
- /**
* If Piwik uses per-domain config file, make sure CustomLogo is unique
* @param $path
* @return mixed
diff --git a/core/Tracker/VisitExcluded.php b/core/Tracker/VisitExcluded.php
index f3d8d0cab2..ae380cac42 100644
--- a/core/Tracker/VisitExcluded.php
+++ b/core/Tracker/VisitExcluded.php
@@ -11,7 +11,7 @@ namespace Piwik\Tracker;
use Piwik\Common;
use Piwik\Config;
use Piwik\DeviceDetectorFactory;
-use Piwik\IP;
+use Piwik\Network\IP;
use Piwik\Piwik;
/**
@@ -158,9 +158,10 @@ class VisitExcluded
$deviceDetector = DeviceDetectorFactory::getInstance($this->userAgent);
+ $ip = IP::fromBinaryIP($this->ip);
+
return !$allowBots
- && ($deviceDetector->isBot()
- || IP::isIpInRange($this->ip, $this->getBotIpRanges()));
+ && ($deviceDetector->isBot() || $ip->isInRanges($this->getBotIpRanges()));
}
protected function getBotIpRanges()
@@ -223,7 +224,7 @@ class VisitExcluded
$websiteAttributes = Cache::getCacheWebsiteAttributes($this->idSite);
if (!empty($websiteAttributes['excluded_ips'])) {
- $ip = \Piwik\Network\IP::fromBinaryIP($this->ip);
+ $ip = IP::fromBinaryIP($this->ip);
if ($ip->isInRanges($websiteAttributes['excluded_ips'])) {
Common::printDebug('Visitor IP ' . $ip->toString() . ' is excluded from being tracked');
return true;
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'];
diff --git a/core/Version.php b/core/Version.php
index 08d4d4d809..0d5a9e14b9 100644
--- a/core/Version.php
+++ b/core/Version.php
@@ -20,7 +20,7 @@ final class Version
* The current Piwik version.
* @var string
*/
- const VERSION = '2.11.2-b2';
+ const VERSION = '2.12.0-b1';
public function isStableVersion($version)
{
diff --git a/core/View.php b/core/View.php
index 27fe7569a5..423aaeb278 100644
--- a/core/View.php
+++ b/core/View.php
@@ -221,6 +221,7 @@ class View implements ViewInterface
$this->url = Common::sanitizeInputValue(Url::getCurrentUrl());
$this->token_auth = Piwik::getCurrentUserTokenAuth();
$this->userHasSomeAdminAccess = Piwik::isUserHasSomeAdminAccess();
+ $this->userIsAnonymous = Piwik::isUserIsAnonymous();
$this->userIsSuperUser = Piwik::hasUserSuperUserAccess();
$this->latest_version_available = UpdateCheck::isNewestVersionAvailable();
$this->disableLink = Common::getRequestVar('disableLink', 0, 'int');
@@ -368,7 +369,13 @@ class View implements ViewInterface
$twig = new Twig();
$environment = $twig->getTwigEnvironment();
$environment->clearTemplateCache();
- $environment->clearCacheFiles();
+
+ $cacheDirectory = $environment->getCache();
+ if (!empty($cacheDirectory)
+ && is_dir($cacheDirectory)
+ ) {
+ $environment->clearCacheFiles();
+ }
}
/**
diff --git a/core/ViewDataTable/Config.php b/core/ViewDataTable/Config.php
index 220a253518..f4eef3c639 100644
--- a/core/ViewDataTable/Config.php
+++ b/core/ViewDataTable/Config.php
@@ -557,7 +557,7 @@ class Config
/**
* @ignore
*/
- public function getFiltersToRun()
+ private function getFiltersToRun()
{
$priorityFilters = array();
$presentationFilters = array();
@@ -581,6 +581,20 @@ class Config
return array($priorityFilters, $presentationFilters);
}
+ public function getPriorityFilters()
+ {
+ $filters = $this->getFiltersToRun();
+
+ return $filters[0];
+ }
+
+ public function getPresentationFilters()
+ {
+ $filters = $this->getFiltersToRun();
+
+ return $filters[1];
+ }
+
/**
* Adds a related report to the {@link $related_reports} property. If the report
* references the one that is currently being displayed, it will not be added to the related
diff --git a/core/ViewDataTable/Request.php b/core/ViewDataTable/Request.php
index 352ce25899..259d4caaa1 100644
--- a/core/ViewDataTable/Request.php
+++ b/core/ViewDataTable/Request.php
@@ -32,15 +32,11 @@ class Request
* It builds the API request string and uses Request to call the API.
* The requested DataTable object is stored in $this->dataTable.
*/
- public function loadDataTableFromAPI($fixedRequestParams = array())
+ public function loadDataTableFromAPI()
{
// we build the request (URL) to call the API
$requestArray = $this->getRequestArray();
- foreach ($fixedRequestParams as $key => $value) {
- $requestArray[$key] = $value;
- }
-
// we make the request to the API
$request = new ApiRequest($requestArray);
@@ -104,6 +100,14 @@ class Request
unset($requestArray['filter_limit']);
}
+ if ($this->requestConfig->disable_generic_filters) {
+ $requestArray['disable_generic_filters'] = '0';
+ }
+
+ if ($this->requestConfig->disable_queued_filters) {
+ $requestArray['disable_queued_filters'] = 0;
+ }
+
return $requestArray;
}
diff --git a/core/ViewDataTable/RequestConfig.php b/core/ViewDataTable/RequestConfig.php
index 753ee55b98..a6c9b7bed8 100644
--- a/core/ViewDataTable/RequestConfig.php
+++ b/core/ViewDataTable/RequestConfig.php
@@ -314,35 +314,6 @@ class RequestConfig
$this->filter_sort_order = 'desc';
}
- /**
- * Returns `true` if queued filters have been disabled, `false` if otherwise.
- *
- * @return bool
- */
- public function areQueuedFiltersDisabled()
- {
- return isset($this->disable_queued_filters) && $this->disable_queued_filters;
- }
-
- /**
- * Returns `true` if generic filters have been disabled, `false` if otherwise.
- *
- * @return bool
- */
- public function areGenericFiltersDisabled()
- {
- // if disable_generic_filters query param is set to '1', generic filters are disabled
- if (Common::getRequestVar('disable_generic_filters', '0', 'string') == 1) {
- return true;
- }
-
- if (isset($this->disable_generic_filters) && true === $this->disable_generic_filters) {
- return true;
- }
-
- return false;
- }
-
public function getApiModuleToRequest()
{
list($module, $method) = explode('.', $this->apiMethodToRequestDataTable);
@@ -356,9 +327,4 @@ class RequestConfig
return $method;
}
-
- public function shouldFormatMetrics()
- {
- return Common::getRequestVar('format_metrics', '1', 'string', $this->request_parameters_to_modify) != 0;
- }
}