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
diff options
context:
space:
mode:
authorStefan Giehl <stefan@matomo.org>2020-02-11 11:38:48 +0300
committerGitHub <noreply@github.com>2020-02-11 11:38:47 +0300
commit5d2dab75e3654126ed95ea72b58b4c225ac59492 (patch)
tree7886dcc7c8593906fbebbca49c7946405bcfa2cc
parentab1e70016dde3ef93498bca99d0aa8b38e6d7f38 (diff)
Merge branch 3.x-dev into 4.x-dev (#15543)
* Updates search engine and social definitions (#15384) * updates device detector to latest release (#15388) * updates device detector to latest release * updates tests * translation update (#15389) * Fix Could not get the lock for ID, when creating a site (#15401) * Lock key start * do not empty key lock Co-authored-by: Thomas Steur <tsteur@users.noreply.github.com> * 3.13.1 * submodule updates * Use correct name in update available message (#15423) * Fix removing user capabilities (#15422) * Order of implode() args, avoid E_NOTICE in PHP7.4 (#15428) * Fixes possible php warning in visitor log (#15442) * silence is_executable call (#15446) * Make sure geolocation admin experience is consistent if user is not using GeoIp2 plugin. (#15447) * Fix referrers test. (#15448) * Ensure to close visitor popover correctly (#15443) * Fixes possible warning (#15453) * Forward instance_id from local config when reseting config during tests. (#15445) * Add event that allows plugins to disable archiving for certain periods/sites if they want. (#15457) * Add event that allows plugins to disable archiving for certain periods/sites if they want. * apply review feedback * Fix possible warning for columns without index (#15467) * Day range archiving issue (#15462) * Improve lock ID check for max length (#15407) Better patch for https://github.com/matomo-org/matomo/pull/15401 which was merged last minute... This way it always works even when someone calls `acquireLock` directly instead of `execute` Pushing this for now into 3.x-dev but can also put it into 4.x-dev directly but then there might be merge conflicts when merging 3.x-dev into 4.x-dev * Use SameSite none for session token when embedded into iframe (#15439) * Make sure tracking works in IE9 and lower (#15480) * Mention Joomla install FAQ (#15481) * Make sparklines work when mbstring extension is not installed (#15489) 1) Too few arguments to function mb_strtolower(), 1 passed in matomo/vendor/davaxi/sparkline/src/Sparkline/StyleTrait.php on line 129 and exactly 2 expected 2) mb_strlen is not defined * update screenshots (#15488) * 3.13.2-rc1 * Use safemode when running CLI commands (#15472) * update icons submodule (#15490) * update icons submodule * update UI tests * Fix possible undefined index notice (#15502) * Use latest davaxi/sparkline release (#15464) * translation update * submodule updates * Fix deprecation notice (#15530) see https://github.com/matomo-org/matomo/pull/15467#issuecomment-583283444 * 3.13.2-rc2 * update cache component (#15536) * fixes copy dashboard to user for more than 100 users (#15538) cherry picking #15424 to fix #15420 in 3.x-dev * Add missing return statement. (#15539) * 3.13.2 * update tests * update tests Co-authored-by: Matthieu Aubry <mattab@users.noreply.github.com> Co-authored-by: Thomas Steur <tsteur@users.noreply.github.com> Co-authored-by: Peter Upfold <pgithub@upfold.org.uk> Co-authored-by: diosmosis <diosmosis@users.noreply.github.com> Co-authored-by: Lukas Winkler <github@lw1.at>
-rw-r--r--composer.json4
-rw-r--r--composer.lock22
-rwxr-xr-xconsole5
-rw-r--r--core/Archive.php5
-rw-r--r--core/ArchiveProcessor/PluginsArchiver.php6
-rw-r--r--core/ArchiveProcessor/Rules.php20
-rw-r--r--core/Archiver/Request.php29
-rw-r--r--core/CliMulti/CliPhp.php2
-rw-r--r--core/Concurrency/Lock.php14
-rw-r--r--core/Console.php15
-rw-r--r--core/CronArchive.php21
-rw-r--r--core/DataAccess/ArchiveSelector.php2
-rw-r--r--core/FrontController.php2
-rw-r--r--core/Plugin/Report.php8
-rw-r--r--core/Session.php11
-rw-r--r--core/Session/SessionAuth.php2
-rw-r--r--core/Tracker/Model.php2
-rw-r--r--core/Tracker/Settings.php2
-rw-r--r--js/piwik.js4
-rw-r--r--js/piwik.min.js8
-rw-r--r--libs/Zend/Session.php4
-rw-r--r--libs/upgradephp/upgrade.php19
-rw-r--r--matomo.js8
-rw-r--r--piwik.js8
-rw-r--r--plugins/API/tests/System/expected/test_AutoSuggestAPITest_browserName__API.getSuggestedValuesForSegment.xml7
-rw-r--r--plugins/API/tests/System/expected/test_AutoSuggestAPITest_operatingSystemName__API.getSuggestedValuesForSegment.xml1
-rw-r--r--plugins/CoreAdminHome/javascripts/optOut.js20
-rw-r--r--plugins/CoreHome/templates/_headerMessage.twig2
-rw-r--r--plugins/CoreVisualizations/Visualizations/HtmlTable.php5
-rw-r--r--plugins/Dashboard/tests/UI/expected-screenshots/Dashboard_loaded_token_auth.png4
-rw-r--r--plugins/Dashboard/tests/UI/expected-screenshots/Dashboard_removed.png4
-rw-r--r--plugins/Live/Visualizations/VisitorLog.php7
-rw-r--r--plugins/Live/javascripts/visitorProfile.js29
m---------plugins/Morpheus/icons0
-rw-r--r--plugins/PrivacyManager/tests/UI/expected-screenshots/PrivacyManager_gdpr_tools_delete_visit_cancelled.png4
-rw-r--r--plugins/PrivacyManager/tests/UI/expected-screenshots/PrivacyManager_gdpr_tools_delete_visit_unconfirmed.png4
-rw-r--r--plugins/PrivacyManager/tests/UI/expected-screenshots/PrivacyManager_gdpr_tools_enrich_segment_by_ip.png4
-rw-r--r--plugins/PrivacyManager/tests/UI/expected-screenshots/PrivacyManager_gdpr_tools_uncheck_one_visit.png4
-rw-r--r--plugins/Referrers/tests/System/ApiTest.php14
-rw-r--r--plugins/SitesManager/templates/_trackingCodeEmail.twig1
-rw-r--r--plugins/SitesManager/templates/siteWithoutData.twig1
-rw-r--r--plugins/UserCountry/Controller.php6
-rw-r--r--plugins/UserCountry/lang/el.json3
-rw-r--r--plugins/UserCountry/lang/en.json3
-rw-r--r--plugins/UserCountry/lang/es-ar.json3
-rw-r--r--plugins/UserCountry/lang/pt-br.json3
-rw-r--r--plugins/UserCountry/lang/sq.json3
-rw-r--r--plugins/UserCountry/lang/tr.json3
-rw-r--r--plugins/UserCountry/templates/adminIndex.twig13
-rw-r--r--plugins/UsersManager/angularjs/capabilities-edit/capabilities-edit.component.html2
-rw-r--r--tests/PHPUnit/Framework/Fixture.php15
-rw-r--r--tests/PHPUnit/Framework/Mock/TestConfig.php11
-rw-r--r--tests/PHPUnit/System/ConsoleTest.php132
-rw-r--r--tests/PHPUnit/System/expected/test_apiGetReportMetadata__API.getSegmentsMetadata.xml2
-rw-r--r--tests/PHPUnit/Unit/Archiver/RequestTest.php28
-rw-r--r--tests/UI/expected-screenshots/Comparison_segmented_visitorlog.png4
-rw-r--r--tests/UI/expected-screenshots/EmptySite_emptySiteDashboard.png4
-rw-r--r--tests/UI/expected-screenshots/OneClickUpdate_login.png4
-rw-r--r--tests/UI/expected-screenshots/Theme_home.png4
-rw-r--r--tests/UI/expected-screenshots/UIIntegrationTest_dashboard1.png4
-rw-r--r--tests/UI/expected-screenshots/UIIntegrationTest_ecommerce_log.png4
-rw-r--r--tests/UI/expected-screenshots/UIIntegrationTest_ecommerce_log_segmented.png4
-rw-r--r--tests/UI/expected-screenshots/UIIntegrationTest_segmented_visitorlog.png4
-rw-r--r--tests/UI/expected-screenshots/UIIntegrationTest_visitor_profile_popup.png4
-rw-r--r--tests/UI/expected-screenshots/UIIntegrationTest_visitors_realtime_map.png4
-rw-r--r--tests/UI/expected-screenshots/UIIntegrationTest_visitors_software.png4
-rw-r--r--tests/UI/expected-screenshots/UIIntegrationTest_widgetize_ecommercelog.png4
m---------tests/travis0
68 files changed, 478 insertions, 131 deletions
diff --git a/composer.json b/composer.json
index 77a6204cb3..d6ca89fa3e 100644
--- a/composer.json
+++ b/composer.json
@@ -37,8 +37,8 @@
"matomo/ini": "~2.0",
"matomo/matomo-php-tracker": "~2.0",
"matomo/network": "~2.0",
- "matomo/referrer-spam-blacklist": "~1.0",
- "matomo/searchengine-and-social-list": "~1.0",
+ "matomo/referrer-spam-blacklist": "~3.0",
+ "matomo/searchengine-and-social-list": "~3.0",
"monolog/monolog": "~1.11",
"mustangostang/spyc": "~0.6.0",
"pear/pear_exception": "~1.0.0",
diff --git a/composer.lock b/composer.lock
index 41cefe7cda..8e53fec2d2 100644
--- a/composer.lock
+++ b/composer.lock
@@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
- "content-hash": "6436d84b7a1cbf44a996b4795723ce68",
+ "content-hash": "ef319f0bec0c7988dd83727e05ac2b39",
"packages": [
{
"name": "composer/ca-bundle",
@@ -626,16 +626,16 @@
},
{
"name": "matomo/searchengine-and-social-list",
- "version": "1.4.4",
+ "version": "3.11.0",
"source": {
"type": "git",
"url": "https://github.com/matomo-org/searchengine-and-social-list.git",
- "reference": "5c29a58c6cab8a78c3e625ffc615e759ef8466d3"
+ "reference": "80ab98ed17ea5b72730f4f4d96acc03847440103"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/matomo-org/searchengine-and-social-list/zipball/5c29a58c6cab8a78c3e625ffc615e759ef8466d3",
- "reference": "5c29a58c6cab8a78c3e625ffc615e759ef8466d3",
+ "url": "https://api.github.com/repos/matomo-org/searchengine-and-social-list/zipball/80ab98ed17ea5b72730f4f4d96acc03847440103",
+ "reference": "80ab98ed17ea5b72730f4f4d96acc03847440103",
"shasum": ""
},
"replace": {
@@ -647,7 +647,7 @@
"CC0-1.0"
],
"description": "Search engine and social network definitions used by Matomo (formerly Piwik)",
- "time": "2018-05-25T11:09:26+00:00"
+ "time": "2020-01-13T09:01:25+00:00"
},
{
"name": "maxmind-db/reader",
@@ -1236,16 +1236,16 @@
},
{
"name": "piwik/device-detector",
- "version": "3.12.2",
+ "version": "3.12.3",
"source": {
"type": "git",
"url": "https://github.com/matomo-org/device-detector.git",
- "reference": "c352144e2ab1aff81e659aa38224be987b87855b"
+ "reference": "22257883bd2c7c21d68112a907cdad7de4a2ba31"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/matomo-org/device-detector/zipball/c352144e2ab1aff81e659aa38224be987b87855b",
- "reference": "c352144e2ab1aff81e659aa38224be987b87855b",
+ "url": "https://api.github.com/repos/matomo-org/device-detector/zipball/22257883bd2c7c21d68112a907cdad7de4a2ba31",
+ "reference": "22257883bd2c7c21d68112a907cdad7de4a2ba31",
"shasum": ""
},
"require": {
@@ -1287,7 +1287,7 @@
"parser",
"useragent"
],
- "time": "2019-12-22T21:23:31+00:00"
+ "time": "2020-01-13T16:15:20+00:00"
},
{
"name": "psr/container",
diff --git a/console b/console
index 47b8ba59f9..069c992dd7 100755
--- a/console
+++ b/console
@@ -1,5 +1,8 @@
#!/usr/bin/env php
<?php
+
+use Piwik\FrontController;
+
if (!defined('PIWIK_DOCUMENT_ROOT')) {
define('PIWIK_DOCUMENT_ROOT', dirname(__FILE__) == '/' ? '' : dirname(__FILE__));
}
@@ -23,5 +26,7 @@ if (!defined('PIWIK_ENABLE_ERROR_HANDLER') || PIWIK_ENABLE_ERROR_HANDLER) {
Piwik\ExceptionHandler::setUp();
}
+FrontController::setUpSafeMode();
+
$console = new Piwik\Console();
$console->run();
diff --git a/core/Archive.php b/core/Archive.php
index 19b06db4a5..ad85d6242e 100644
--- a/core/Archive.php
+++ b/core/Archive.php
@@ -649,10 +649,11 @@ class Archive implements ArchiveQuery
foreach ($this->params->getIdSites() as $idSite) {
$site = new Site($idSite);
- if ($period->getLabel() === 'day'
+ if ($period->getLabel() === 'day'
&& !$this->params->getSegment()->isEmpty()
&& Common::getRequestVar('skipArchiveSegmentToday', 0, 'int')
- && $period->getDateStart()->toString() == Date::factory('now', $site->getTimezone())->toString()) {
+ && $period->getDateStart()->toString() == Date::factory('now', $site->getTimezone())->toString()
+ ) {
Log::debug("Skipping archive %s for %s as segment today is disabled", $period->getLabel(), $period->getPrettyString());
continue;
diff --git a/core/ArchiveProcessor/PluginsArchiver.php b/core/ArchiveProcessor/PluginsArchiver.php
index a5f828ee6e..2ada786bd4 100644
--- a/core/ArchiveProcessor/PluginsArchiver.php
+++ b/core/ArchiveProcessor/PluginsArchiver.php
@@ -160,13 +160,15 @@ class PluginsArchiver
try {
self::$currentPluginBeingArchived = $pluginName;
+ $period = $this->params->getPeriod()->getLabel();
+
$timer = new Timer();
if ($this->shouldAggregateFromRawData) {
- Log::debug("PluginsArchiver::%s: Archiving day reports for plugin '%s'.", __FUNCTION__, $pluginName);
+ Log::debug("PluginsArchiver::%s: Archiving $period reports for plugin '%s' from raw data.", __FUNCTION__, $pluginName);
$archiver->callAggregateDayReport();
} else {
- Log::debug("PluginsArchiver::%s: Archiving period reports for plugin '%s'.", __FUNCTION__, $pluginName);
+ Log::debug("PluginsArchiver::%s: Archiving $period reports for plugin '%s' using reports for smaller periods.", __FUNCTION__, $pluginName);
$archiver->callAggregateMultipleReports();
}
diff --git a/core/ArchiveProcessor/Rules.php b/core/ArchiveProcessor/Rules.php
index bf0f73b836..24f9cc2fe2 100644
--- a/core/ArchiveProcessor/Rules.php
+++ b/core/ArchiveProcessor/Rules.php
@@ -226,9 +226,21 @@ class Rules
return !$isArchivingEnabled;
}
- public static function isRequestAuthorizedToArchive()
+ public static function isRequestAuthorizedToArchive(Parameters $params = null)
{
- return Rules::isBrowserTriggerEnabled() || SettingsServer::isArchivePhpTriggered();
+ $isRequestAuthorizedToArchive = Rules::isBrowserTriggerEnabled() || SettingsServer::isArchivePhpTriggered();
+
+ if (!empty($params)) {
+ /**
+ * @ignore
+ *
+ * @params bool &$isRequestAuthorizedToArchive
+ * @params Parameters $params
+ */
+ Piwik::postEvent('Archiving.isRequestAuthorizedToArchive', [&$isRequestAuthorizedToArchive, $params]);
+ }
+
+ return $isRequestAuthorizedToArchive;
}
public static function isBrowserTriggerEnabled()
@@ -293,11 +305,11 @@ class Rules
*
* @return string[]
*/
- public static function getSelectableDoneFlagValues($includeInvalidated = true)
+ public static function getSelectableDoneFlagValues($includeInvalidated = true, Parameters $params = null)
{
$possibleValues = array(ArchiveWriter::DONE_OK, ArchiveWriter::DONE_OK_TEMPORARY);
- if (!Rules::isRequestAuthorizedToArchive()
+ if (!Rules::isRequestAuthorizedToArchive($params)
&& $includeInvalidated
) {
//If request is not authorized to archive then fetch also invalidated archives
diff --git a/core/Archiver/Request.php b/core/Archiver/Request.php
index e08b23ac54..58389384a6 100644
--- a/core/Archiver/Request.php
+++ b/core/Archiver/Request.php
@@ -31,7 +31,7 @@ class Request
*/
public function __construct($url)
{
- $this->url = $url;
+ $this->setUrl($url);
}
public function before($callable)
@@ -69,8 +69,33 @@ class Request
public function changeDate($newDate)
{
+ $this->changeParam('date', $newDate);
+ }
+
+ public function makeSureDateIsNotSingleDayRange()
+ {
+ // TODO: revisit in matomo 4
+ // period=range&date=last1/period=range&date=previous1 can cause problems during archiving due to Parameters::isDayArchive()
+ if (preg_match('/[&?]period=range/', $this->url)) {
+ if (preg_match('/[&?]date=last1/', $this->url)) {
+ $this->changeParam('period', 'day');
+ $this->changeParam('date', 'today');
+ } else if (preg_match('/[&?]date=previous1/', $this->url)) {
+ $this->changeParam('period', 'day');
+ $this->changeParam('date', 'yesterday');
+ } else if (preg_match('/[&?]date=([^,]+),([^,&]+)/', $this->url, $matches)
+ && $matches[1] == $matches[2]
+ ) {
+ $this->changeParam('period', 'day');
+ $this->changeParam('date', $matches[1]);
+ }
+ }
+ }
+
+ public function changeParam($name, $newValue)
+ {
$url = $this->getUrl();
- $url = preg_replace('/([&?])date=[^&]*/', '$1date=' . $newDate, $url);
+ $url = preg_replace('/([&?])' . preg_quote($name) . '=[^&]*/', '$1' . $name . '=' . $newValue, $url);
$this->setUrl($url);
}
}
diff --git a/core/CliMulti/CliPhp.php b/core/CliMulti/CliPhp.php
index 0d254e2339..3b1661510e 100644
--- a/core/CliMulti/CliPhp.php
+++ b/core/CliMulti/CliPhp.php
@@ -86,7 +86,7 @@ class CliPhp
private function getPhpCommandIfValid($path)
{
- if (!empty($path) && is_executable($path)) {
+ if (!empty($path) && @is_executable($path)) {
if (0 === strpos($path, PHP_BINDIR) && $this->isValidPhpType($path)) {
return $path;
}
diff --git a/core/Concurrency/Lock.php b/core/Concurrency/Lock.php
index f63bec640a..f26f589699 100644
--- a/core/Concurrency/Lock.php
+++ b/core/Concurrency/Lock.php
@@ -50,13 +50,6 @@ class Lock
public function execute($id, $callback)
{
- if (Common::mb_strlen($id) > self::MAX_KEY_LEN) {
- // Lock key might be too long for DB column, so we hash it but leave the start of the original as well
- // to make it more readable
- $md5Len = 32;
- $id = Common::mb_substr($id, 0, self::MAX_KEY_LEN - $md5Len - 1) . md5($id);
- }
-
$i = 0;
while (!$this->acquireLock($id)) {
$i++;
@@ -76,6 +69,13 @@ class Lock
{
$this->lockKey = $this->lockKeyStart . $id;
+ if (Common::mb_strlen($this->lockKey) > self::MAX_KEY_LEN) {
+ // Lock key might be too long for DB column, so we hash it but leave the start of the original as well
+ // to make it more readable
+ $md5Len = 32;
+ $this->lockKey = Common::mb_substr($id, 0, self::MAX_KEY_LEN - $md5Len - 1) . md5($id);
+ }
+
$lockValue = substr(Common::generateUniqId(), 0, 12);
$locked = $this->backend->setIfNotExists($this->lockKey, $lockValue, $ttlInSeconds);
diff --git a/core/Console.php b/core/Console.php
index 70cec3c740..1bf61c0fd2 100644
--- a/core/Console.php
+++ b/core/Console.php
@@ -78,6 +78,21 @@ class Console extends Application
public function doRun(InputInterface $input, OutputInterface $output)
{
+ try {
+ return $this->doRunImpl($input, $output);
+ } catch (\Exception $ex) {
+ try {
+ FrontController::generateSafeModeOutputFromException($ex);
+ } catch (\Exception $ex) {
+ // ignore, we re-throw the original exception, not a wrapped one
+ }
+
+ throw $ex;
+ }
+ }
+
+ private function doRunImpl(InputInterface $input, OutputInterface $output)
+ {
if ($input->hasParameterOption('--xhprof')) {
Profiler::setupProfilerXHProf(true, true);
}
diff --git a/core/CronArchive.php b/core/CronArchive.php
index fb17b960b6..6e7d186db2 100644
--- a/core/CronArchive.php
+++ b/core/CronArchive.php
@@ -1104,7 +1104,16 @@ class CronArchive
return Request::ABORT;
}
+ $urlBefore = $request->getUrl();
$request->changeDate($newDate);
+ $request->makeSureDateIsNotSingleDayRange();
+
+ // check again if we are already archiving the URL since we just changed it
+ if ($request->getUrl() !== $urlBefore
+ && $self->isAlreadyArchivingSegment($request->getUrl(), $idSite, $period, $segment)
+ ) {
+ return Request::ABORT;
+ }
$this->logArchiveWebsite($idSite, $period, $newDate);
});
@@ -1972,9 +1981,17 @@ class CronArchive
return Request::ABORT;
}
- $url = $request->getUrl();
- $url = preg_replace('/([&?])date=[^&]*/', '$1date=' . $newDate, $url);
+ $urlBefore = $request->getUrl();
+ $url = preg_replace('/([&?])date=[^&]*/', '$1date=' . $newDate, $urlBefore);
$request->setUrl($url);
+ $request->makeSureDateIsNotSingleDayRange();
+
+ // check again if we are already archiving the URL since we just changed it
+ if ($request->getUrl() !== $urlBefore
+ && $self->isAlreadyArchivingSegment($request->getUrl(), $idSite, $period, $segment)
+ ) {
+ return Request::ABORT;
+ }
$processedSegmentCount++;
$logger->info(sprintf(
diff --git a/core/DataAccess/ArchiveSelector.php b/core/DataAccess/ArchiveSelector.php
index 21d7a9f626..4232404141 100644
--- a/core/DataAccess/ArchiveSelector.php
+++ b/core/DataAccess/ArchiveSelector.php
@@ -71,7 +71,7 @@ class ArchiveSelector
$plugins = array("VisitsSummary", $requestedPlugin);
$doneFlags = Rules::getDoneFlags($plugins, $segment);
- $doneFlagValues = Rules::getSelectableDoneFlagValues($includeInvalidated);
+ $doneFlagValues = Rules::getSelectableDoneFlagValues($includeInvalidated, $params);
$results = self::getModel()->getArchiveIdAndVisits($numericTable, $idSite, $period, $dateStartIso, $dateEndIso, $minDatetimeIsoArchiveProcessedUTC, $doneFlags, $doneFlagValues);
diff --git a/core/FrontController.php b/core/FrontController.php
index 8a81006e9a..84ce6a1927 100644
--- a/core/FrontController.php
+++ b/core/FrontController.php
@@ -106,7 +106,7 @@ class FrontController extends Singleton
* @param Exception $e
* @return string
*/
- private static function generateSafeModeOutputFromException($e)
+ public static function generateSafeModeOutputFromException($e)
{
StaticContainer::get(LoggerInterface::class)->error('Uncaught exception: {exception}', [
'exception' => $e,
diff --git a/core/Plugin/Report.php b/core/Plugin/Report.php
index 391ae994d5..740eed1853 100644
--- a/core/Plugin/Report.php
+++ b/core/Plugin/Report.php
@@ -496,8 +496,8 @@ class Report
} elseif ($metric instanceof Metric) {
$name = $metric->getName();
$metricDocs = $metric->getDocumentation();
- if (empty($metricDocs)) {
- $metricDocs = @$translations[$name];
+ if (empty($metricDocs) && !empty($translations[$name])) {
+ $metricDocs = $translations[$name];
}
if (!empty($metricDocs)) {
@@ -513,8 +513,8 @@ class Report
} elseif ($processedMetric instanceof Metric) {
$name = $processedMetric->getName();
$metricDocs = $processedMetric->getDocumentation();
- if (empty($metricDocs)) {
- $metricDocs = @$translations[$name];
+ if (empty($metricDocs) && !empty($translations[$name])) {
+ $metricDocs = $translations[$name];
}
if (!empty($metricDocs)) {
diff --git a/core/Session.php b/core/Session.php
index 92857e9d3c..d7ee14beb4 100644
--- a/core/Session.php
+++ b/core/Session.php
@@ -171,6 +171,17 @@ class Session extends Zend_Session
return self::$sessionStarted;
}
+ public static function getSameSiteCookieValue()
+ {
+ $config = Config::getInstance();
+ $general = $config->General;
+ if (!empty($general['enable_framed_pages']) && ProxyHttp::isHttps()) {
+ return 'None';
+ }
+
+ return 'Lax';
+ }
+
/**
* Write cookie header. Similar to the native setcookie() function but also supports
* the SameSite cookie property.
diff --git a/core/Session/SessionAuth.php b/core/Session/SessionAuth.php
index 2b803b686f..001b3d2bb0 100644
--- a/core/Session/SessionAuth.php
+++ b/core/Session/SessionAuth.php
@@ -195,7 +195,7 @@ class SessionAuth implements Auth
$sessionParams['domain'],
$sessionParams['secure'],
$sessionParams['httponly'],
- 'lax'
+ Session::getSameSiteCookieValue()
);
// ...and we also update the expiration time stored server side so we can prevent expired sessions from being reused
diff --git a/core/Tracker/Model.php b/core/Tracker/Model.php
index 8e8b7cac9e..c7bdc72a49 100644
--- a/core/Tracker/Model.php
+++ b/core/Tracker/Model.php
@@ -346,7 +346,7 @@ class Model
list($updateParts, $sqlBind) = $this->fieldsToQuery($valuesToUpdate);
- $parts = implode($updateParts, ', ');
+ $parts = implode(', ', $updateParts);
$table = Common::prefixTable('log_link_visit_action');
$sqlQuery = "UPDATE $table SET $parts WHERE idlink_va = ?";
diff --git a/core/Tracker/Settings.php b/core/Tracker/Settings.php
index 78c889c40e..0fd61612f9 100644
--- a/core/Tracker/Settings.php
+++ b/core/Tracker/Settings.php
@@ -40,7 +40,7 @@ class Settings // TODO: merge w/ visitor recognizer or make it it's own service.
$deviceDetector = StaticContainer::get(DeviceDetectorFactory::class)->makeInstance($userAgent);
$aBrowserInfo = $deviceDetector->getClient();
- if ($aBrowserInfo['type'] != 'browser') {
+ if (empty($aBrowserInfo['type']) || 'browser' !== $aBrowserInfo['type']) {
// for now only track browsers
unset($aBrowserInfo);
}
diff --git a/js/piwik.js b/js/piwik.js
index bee045d487..3031f4d01c 100644
--- a/js/piwik.js
+++ b/js/piwik.js
@@ -6690,7 +6690,7 @@ if (typeof window.Piwik !== 'object') {
// initialize the Piwik singleton
addEventListener(windowAlias, 'beforeunload', beforeUnloadHandler, false);
- window.addEventListener('message', function(e) {
+ addEventListener(windowAlias,'message', function(e) {
if (!e || !e.origin) {
return;
}
@@ -6773,7 +6773,7 @@ if (typeof window.Piwik !== 'object') {
maq_optout_by_default: tracker.isConsentRequired()
});
}
- });
+ }, false);
Date.prototype.getTimeAlias = Date.prototype.getTime;
diff --git a/js/piwik.min.js b/js/piwik.min.js
index 353c215066..f4fda77e08 100644
--- a/js/piwik.min.js
+++ b/js/piwik.min.js
@@ -59,10 +59,10 @@ var dm=di+":"+dh;if(dj){dm+=":"+dj}at(dl,dm,dk)});if(df){return df(dk,di,dh,dj,d
dg=a(dg);if(!dh||!di){return}df=df||"Unknown";ce(function(){var dj=aO(dh,di,df,dg);if(dj){bC(dj,bG)}})};this.trackContentInteractionNode=function(dh,dg){if(N(b3)||!dh){return}var df=null;ce(function(){df=c7(dh,dg);if(df){bC(df,bG)}});return df};this.logAllContentBlocksOnPage=function(){var dh=v.findContentNodes();var df=v.collectContent(dh);var dg=typeof console;if(dg!=="undefined"&&console&&console.log){console.log(df)}};this.trackEvent=function(dg,di,df,dh,dk,dj){ce(function(){at(dg,di,df,dh,dk,dj)})};this.trackSiteSearch=function(df,dh,dg,di){b7=[];ce(function(){b5(df,dh,dg,di)})};this.setEcommerceView=function(di,df,dh,dg){if(Y(dh)){dh=String(dh)}if(!J(dh)||dh===null||dh===false||!dh.length){dh=""}else{if(dh instanceof Array){dh=T.JSON.stringify(dh)}}bT[5]=["_pkc",dh];if(J(dg)&&dg!==null&&dg!==false&&String(dg).length){bT[2]=["_pkp",dg]}if(!Y(di)&&!Y(df)){return}if(Y(di)){bT[3]=["_pks",di]}if(!Y(df)){df=""}bT[4]=["_pkn",df]};this.getEcommerceItems=function(){return JSON.parse(JSON.stringify(cW))
};this.addEcommerceItem=function(dj,df,dh,dg,di){if(Y(dj)){cW[dj]=[String(dj),df,dh,dg,di]}};this.removeEcommerceItem=function(df){if(Y(df)){df=String(df);delete cW[df]}};this.clearEcommerceCart=function(){cW={}};this.trackEcommerceOrder=function(df,dj,di,dh,dg,dk){bW(df,dj,di,dh,dg,dk)};this.trackEcommerceCartUpdate=function(df){bq(df)};this.trackRequest=function(dg,di,dh,df){ce(function(){var dj=cq(dg,di,df);bC(dj,bG,dh)})};this.ping=function(){this.trackRequest("ping=1",null,null,"ping")};this.disableQueueRequest=function(){bA.enabled=false};this.setRequestQueueInterval=function(df){if(df<1000){throw new Error("Request queue interval needs to be at least 1000ms")}bA.interval=df};this.queueRequest=function(df){ce(function(){var dg=cq(df);bA.push(dg)})};this.isConsentRequired=function(){return cx};this.getRememberedConsent=function(){var df=aC(bc);if(aC(cH)){if(df){bV(bc,bn,cT)}return null}if(!df||df===0){return null}return df};this.hasRememberedConsent=function(){return !!this.getRememberedConsent()
};this.requireConsent=function(){cx=true;bz=this.hasRememberedConsent();x++;b["CoreConsent"+x]={unload:function(){if(!bz){aE()}}}};this.setConsentGiven=function(){bz=true;bV(cH,bn,cT);var dg,df;for(dg=0;dg<cG.length;dg++){df=typeof cG[dg];if(df==="string"){bC(cG[dg],bG)}else{if(df==="object"){db(cG[dg],bG)}}}cG=[]};this.rememberConsentGiven=function(dg){if(dg){dg=dg*60*60*1000}else{dg=30*365*24*60*60*1000}this.setConsentGiven();var df=new Date().getTime();da(bc,df,dg,bn,cT,bP)};this.forgetConsentGiven=function(){var df=30*365*24*60*60*1000;bV(bc,bn,cT);da(cH,new Date().getTime(),df,bn,cT,bP);this.requireConsent()};this.isUserOptedOut=function(){return !bz};this.optUserOut=this.forgetConsentGiven;this.forgetUserOptOut=this.rememberConsentGiven;e.trigger("TrackerSetup",[this])}function H(){return{push:af}}function c(au,at){var av={};var aq,ar;for(aq=0;aq<at.length;aq++){var ao=at[aq];av[ao]=1;for(ar=0;ar<au.length;ar++){if(au[ar]&&au[ar][0]){var ap=au[ar][0];if(ao===ap){af(au[ar]);delete au[ar];
-if(av[ap]>1&&ap!=="addTracker"){ak("The method "+ap+' is registered more than once in "_paq" variable. Only the last call has an effect. Please have a look at the multiple Piwik trackers documentation: https://developer.piwik.org/guides/tracking-javascript-guide#multiple-piwik-trackers')}av[ap]++}}}}return au}var C=["addTracker","disableCookies","setTrackerUrl","setAPIUrl","enableCrossDomainLinking","setCrossDomainLinkingTimeout","setSessionCookieTimeout","setVisitorCookieTimeout","setSecureCookie","setCookiePath","setCookieDomain","setDomains","setUserId","setSiteId","alwaysUseSendBeacon","enableLinkTracking","requireConsent","setConsentGiven"];function ad(ao,aq){var ap=new Q(ao,aq);I.push(ap);_paq=c(_paq,C);for(E=0;E<_paq.length;E++){if(_paq[E]){af(_paq[E])}}_paq=new H();e.trigger("TrackerAdded",[ap]);return ap}an(T,"beforeunload",ai,false);window.addEventListener("message",function(au){if(!au||!au.origin){return}var aw,ar,ap;var ax=d(au.origin);var at=e.getAsyncTrackers();for(ar=0;ar<at.length;
-ar++){ap=d(at[ar].getPiwikUrl());if(ap===ax){aw=at[ar];break}}if(!aw){return}var aq=null;try{aq=JSON.parse(au.data)}catch(av){return}if(!aq){return}function ao(aA){var aC=G.getElementsByTagName("iframe");for(ar=0;ar<aC.length;ar++){var aB=aC[ar];var ay=d(aB.src);if(aB.contentWindow&&J(aB.contentWindow.postMessage)&&ay===ax){var az=JSON.stringify(aA);aB.contentWindow.postMessage(az,"*")}}}if(J(aq.maq_initial_value)){ao({maq_opted_in:aq.maq_initial_value&&aw.hasConsent(),maq_url:aw.getPiwikUrl(),maq_optout_by_default:aw.isConsentRequired()})}else{if(J(aq.maq_opted_in)){at=e.getAsyncTrackers();for(ar=0;ar<at.length;ar++){aw=at[ar];if(aq.maq_opted_in){aw.rememberConsentGiven()}else{aw.forgetConsentGiven()}}ao({maq_confirm_opted_in:aw.hasConsent(),maq_url:aw.getPiwikUrl(),maq_optout_by_default:aw.isConsentRequired()})}}});Date.prototype.getTimeAlias=Date.prototype.getTime;e={initialized:false,JSON:T.JSON,DOM:{addEventListener:function(ar,aq,ap,ao){var at=typeof ao;if(at==="undefined"){ao=false
-}an(ar,aq,ap,ao)},onLoad:n,onReady:q,isNodeVisible:j,isOrWasNodeVisible:v.isNodeVisible},on:function(ap,ao){if(!y[ap]){y[ap]=[]}y[ap].push(ao)},off:function(aq,ap){if(!y[aq]){return}var ao=0;for(ao;ao<y[aq].length;ao++){if(y[aq][ao]===ap){y[aq].splice(ao,1)}}},trigger:function(aq,ar,ap){if(!y[aq]){return}var ao=0;for(ao;ao<y[aq].length;ao++){y[aq][ao].apply(ap||T,ar)}},addPlugin:function(ao,ap){b[ao]=ap},getTracker:function(ao,ap){if(!J(ap)){ap=this.getAsyncTracker().getSiteId()}if(!J(ao)){ao=this.getAsyncTracker().getTrackerUrl()}return new Q(ao,ap)},getAsyncTrackers:function(){return I},addTracker:function(ao,aq){var ap;if(!I.length){ap=ad(ao,aq)}else{ap=I[0].addTracker(ao,aq)}return ap},getAsyncTracker:function(ap,at){var ar;if(I&&I.length&&I[0]){ar=I[0]}else{return ad(ap,at)}if(!at&&!ap){return ar}if((!J(at)||null===at)&&ar){at=ar.getSiteId()}if((!J(ap)||null===ap)&&ar){ap=ar.getTrackerUrl()}var aq,ao=0;for(ao;ao<I.length;ao++){aq=I[ao];if(aq&&String(aq.getSiteId())===String(at)&&aq.getTrackerUrl()===ap){return aq
-}}},retryMissedPluginCalls:function(){var ap=ah;ah=[];var ao=0;for(ao;ao<ap.length;ao++){af(ap[ao])}}};if(typeof define==="function"&&define.amd){define("piwik",[],function(){return e});define("matomo",[],function(){return e})}return e}())}
+if(av[ap]>1&&ap!=="addTracker"){ak("The method "+ap+' is registered more than once in "_paq" variable. Only the last call has an effect. Please have a look at the multiple Piwik trackers documentation: https://developer.piwik.org/guides/tracking-javascript-guide#multiple-piwik-trackers')}av[ap]++}}}}return au}var C=["addTracker","disableCookies","setTrackerUrl","setAPIUrl","enableCrossDomainLinking","setCrossDomainLinkingTimeout","setSessionCookieTimeout","setVisitorCookieTimeout","setSecureCookie","setCookiePath","setCookieDomain","setDomains","setUserId","setSiteId","alwaysUseSendBeacon","enableLinkTracking","requireConsent","setConsentGiven"];function ad(ao,aq){var ap=new Q(ao,aq);I.push(ap);_paq=c(_paq,C);for(E=0;E<_paq.length;E++){if(_paq[E]){af(_paq[E])}}_paq=new H();e.trigger("TrackerAdded",[ap]);return ap}an(T,"beforeunload",ai,false);an(T,"message",function(au){if(!au||!au.origin){return}var aw,ar,ap;var ax=d(au.origin);var at=e.getAsyncTrackers();for(ar=0;ar<at.length;ar++){ap=d(at[ar].getPiwikUrl());
+if(ap===ax){aw=at[ar];break}}if(!aw){return}var aq=null;try{aq=JSON.parse(au.data)}catch(av){return}if(!aq){return}function ao(aA){var aC=G.getElementsByTagName("iframe");for(ar=0;ar<aC.length;ar++){var aB=aC[ar];var ay=d(aB.src);if(aB.contentWindow&&J(aB.contentWindow.postMessage)&&ay===ax){var az=JSON.stringify(aA);aB.contentWindow.postMessage(az,"*")}}}if(J(aq.maq_initial_value)){ao({maq_opted_in:aq.maq_initial_value&&aw.hasConsent(),maq_url:aw.getPiwikUrl(),maq_optout_by_default:aw.isConsentRequired()})}else{if(J(aq.maq_opted_in)){at=e.getAsyncTrackers();for(ar=0;ar<at.length;ar++){aw=at[ar];if(aq.maq_opted_in){aw.rememberConsentGiven()}else{aw.forgetConsentGiven()}}ao({maq_confirm_opted_in:aw.hasConsent(),maq_url:aw.getPiwikUrl(),maq_optout_by_default:aw.isConsentRequired()})}}},false);Date.prototype.getTimeAlias=Date.prototype.getTime;e={initialized:false,JSON:T.JSON,DOM:{addEventListener:function(ar,aq,ap,ao){var at=typeof ao;if(at==="undefined"){ao=false}an(ar,aq,ap,ao)},onLoad:n,onReady:q,isNodeVisible:j,isOrWasNodeVisible:v.isNodeVisible},on:function(ap,ao){if(!y[ap]){y[ap]=[]
+}y[ap].push(ao)},off:function(aq,ap){if(!y[aq]){return}var ao=0;for(ao;ao<y[aq].length;ao++){if(y[aq][ao]===ap){y[aq].splice(ao,1)}}},trigger:function(aq,ar,ap){if(!y[aq]){return}var ao=0;for(ao;ao<y[aq].length;ao++){y[aq][ao].apply(ap||T,ar)}},addPlugin:function(ao,ap){b[ao]=ap},getTracker:function(ao,ap){if(!J(ap)){ap=this.getAsyncTracker().getSiteId()}if(!J(ao)){ao=this.getAsyncTracker().getTrackerUrl()}return new Q(ao,ap)},getAsyncTrackers:function(){return I},addTracker:function(ao,aq){var ap;if(!I.length){ap=ad(ao,aq)}else{ap=I[0].addTracker(ao,aq)}return ap},getAsyncTracker:function(ap,at){var ar;if(I&&I.length&&I[0]){ar=I[0]}else{return ad(ap,at)}if(!at&&!ap){return ar}if((!J(at)||null===at)&&ar){at=ar.getSiteId()}if((!J(ap)||null===ap)&&ar){ap=ar.getTrackerUrl()}var aq,ao=0;for(ao;ao<I.length;ao++){aq=I[ao];if(aq&&String(aq.getSiteId())===String(at)&&aq.getTrackerUrl()===ap){return aq}}},retryMissedPluginCalls:function(){var ap=ah;ah=[];var ao=0;for(ao;ao<ap.length;ao++){af(ap[ao])
+}}};if(typeof define==="function"&&define.amd){define("piwik",[],function(){return e});define("matomo",[],function(){return e})}return e}())}
/*!!! pluginTrackerHook */
(function(){function b(){if("object"!==typeof _paq){return false}var c=typeof _paq.length;if("undefined"===c){return false}return !!_paq.length}if(window&&"object"===typeof window.piwikPluginAsyncInit&&window.piwikPluginAsyncInit.length){var a=0;for(a;a<window.piwikPluginAsyncInit.length;a++){if(typeof window.piwikPluginAsyncInit[a]==="function"){window.piwikPluginAsyncInit[a]()}}}if(window&&window.piwikAsyncInit){window.piwikAsyncInit()}if(!window.Piwik.getAsyncTrackers().length){if(b()){window.Piwik.addTracker()}else{_paq={push:function(c){var d=typeof console;if(d!=="undefined"&&console&&console.error){console.error("_paq.push() was used but Matomo tracker was not initialized before the matomo.js file was loaded. Make sure to configure the tracker via _paq.push before loading matomo.js. Alternatively, you can create a tracker via Matomo.addTracker() manually and then use _paq.push but it may not fully work as tracker methods may not be executed in the correct order.",c)
}}}}}window.Piwik.trigger("PiwikInitialized",[]);window.Piwik.initialized=true}());(function(){var a=(typeof AnalyticsTracker);if(a==="undefined"){AnalyticsTracker=window.Piwik}}());if(typeof piwik_log!=="function"){piwik_log=function(b,f,d,g){function a(h){try{if(window["piwik_"+h]){return window["piwik_"+h]}}catch(i){}return}var c,e=window.Piwik.getTracker(d,f);e.setDocumentTitle(b);e.setCustomData(g);c=a("tracker_pause");if(c){e.setLinkTrackingTimer(c)}c=a("download_extensions");if(c){e.setDownloadExtensions(c)}c=a("hosts_alias");if(c){e.setDomains(c)}c=a("ignore_classes");if(c){e.setIgnoreClasses(c)}e.trackPageView();if(a("install_tracker")){piwik_track=function(i,k,j,h){e.setSiteId(k);e.setTrackerUrl(j);e.trackLink(i,h)};e.enableLinkTracking()}}}
diff --git a/libs/Zend/Session.php b/libs/Zend/Session.php
index 793f76b8ed..3f21dcd9e6 100644
--- a/libs/Zend/Session.php
+++ b/libs/Zend/Session.php
@@ -341,7 +341,7 @@ class Zend_Session extends Zend_Session_Abstract
}
if (stripos($cookieHeader, 'SameSite') === false) {
- $cookieHeader .= '; SameSite=Lax';
+ $cookieHeader .= '; SameSite=' . \Piwik\Session::getSameSiteCookieValue();
header($cookieHeader);
}
}
@@ -798,7 +798,7 @@ class Zend_Session extends Zend_Session_Abstract
$cookie_params['domain'],
$cookie_params['secure'],
false,
- 'lax'
+ \Piwik\Session::getSameSiteCookieValue()
);
}
}
diff --git a/libs/upgradephp/upgrade.php b/libs/upgradephp/upgrade.php
index 29fa9752d9..3ac2b8ae8c 100644
--- a/libs/upgradephp/upgrade.php
+++ b/libs/upgradephp/upgrade.php
@@ -681,9 +681,22 @@ if (!function_exists('utf8_decode')) {
* @param string $charset
*/
if(!function_exists('mb_strtolower')) {
- function mb_strtolower($input, $charset) {
- return strtolower($input);
- }
+ function mb_strtolower($input, $charset = '') {
+ return strtolower($input);
+ }
+}
+
+/**
+ * Use strlen if mb_strlen doesn't exist (i.e., php not compiled with --enable-mbstring)
+ * This is not a functional replacement for mb_strlen.
+ *
+ * @param string $input
+ * @param string $charset
+ */
+if(!function_exists('mb_strlen')) {
+ function mb_strlen($input, $charset = '') {
+ return strlen($input);
+ }
}
/**
diff --git a/matomo.js b/matomo.js
index 353c215066..f4fda77e08 100644
--- a/matomo.js
+++ b/matomo.js
@@ -59,10 +59,10 @@ var dm=di+":"+dh;if(dj){dm+=":"+dj}at(dl,dm,dk)});if(df){return df(dk,di,dh,dj,d
dg=a(dg);if(!dh||!di){return}df=df||"Unknown";ce(function(){var dj=aO(dh,di,df,dg);if(dj){bC(dj,bG)}})};this.trackContentInteractionNode=function(dh,dg){if(N(b3)||!dh){return}var df=null;ce(function(){df=c7(dh,dg);if(df){bC(df,bG)}});return df};this.logAllContentBlocksOnPage=function(){var dh=v.findContentNodes();var df=v.collectContent(dh);var dg=typeof console;if(dg!=="undefined"&&console&&console.log){console.log(df)}};this.trackEvent=function(dg,di,df,dh,dk,dj){ce(function(){at(dg,di,df,dh,dk,dj)})};this.trackSiteSearch=function(df,dh,dg,di){b7=[];ce(function(){b5(df,dh,dg,di)})};this.setEcommerceView=function(di,df,dh,dg){if(Y(dh)){dh=String(dh)}if(!J(dh)||dh===null||dh===false||!dh.length){dh=""}else{if(dh instanceof Array){dh=T.JSON.stringify(dh)}}bT[5]=["_pkc",dh];if(J(dg)&&dg!==null&&dg!==false&&String(dg).length){bT[2]=["_pkp",dg]}if(!Y(di)&&!Y(df)){return}if(Y(di)){bT[3]=["_pks",di]}if(!Y(df)){df=""}bT[4]=["_pkn",df]};this.getEcommerceItems=function(){return JSON.parse(JSON.stringify(cW))
};this.addEcommerceItem=function(dj,df,dh,dg,di){if(Y(dj)){cW[dj]=[String(dj),df,dh,dg,di]}};this.removeEcommerceItem=function(df){if(Y(df)){df=String(df);delete cW[df]}};this.clearEcommerceCart=function(){cW={}};this.trackEcommerceOrder=function(df,dj,di,dh,dg,dk){bW(df,dj,di,dh,dg,dk)};this.trackEcommerceCartUpdate=function(df){bq(df)};this.trackRequest=function(dg,di,dh,df){ce(function(){var dj=cq(dg,di,df);bC(dj,bG,dh)})};this.ping=function(){this.trackRequest("ping=1",null,null,"ping")};this.disableQueueRequest=function(){bA.enabled=false};this.setRequestQueueInterval=function(df){if(df<1000){throw new Error("Request queue interval needs to be at least 1000ms")}bA.interval=df};this.queueRequest=function(df){ce(function(){var dg=cq(df);bA.push(dg)})};this.isConsentRequired=function(){return cx};this.getRememberedConsent=function(){var df=aC(bc);if(aC(cH)){if(df){bV(bc,bn,cT)}return null}if(!df||df===0){return null}return df};this.hasRememberedConsent=function(){return !!this.getRememberedConsent()
};this.requireConsent=function(){cx=true;bz=this.hasRememberedConsent();x++;b["CoreConsent"+x]={unload:function(){if(!bz){aE()}}}};this.setConsentGiven=function(){bz=true;bV(cH,bn,cT);var dg,df;for(dg=0;dg<cG.length;dg++){df=typeof cG[dg];if(df==="string"){bC(cG[dg],bG)}else{if(df==="object"){db(cG[dg],bG)}}}cG=[]};this.rememberConsentGiven=function(dg){if(dg){dg=dg*60*60*1000}else{dg=30*365*24*60*60*1000}this.setConsentGiven();var df=new Date().getTime();da(bc,df,dg,bn,cT,bP)};this.forgetConsentGiven=function(){var df=30*365*24*60*60*1000;bV(bc,bn,cT);da(cH,new Date().getTime(),df,bn,cT,bP);this.requireConsent()};this.isUserOptedOut=function(){return !bz};this.optUserOut=this.forgetConsentGiven;this.forgetUserOptOut=this.rememberConsentGiven;e.trigger("TrackerSetup",[this])}function H(){return{push:af}}function c(au,at){var av={};var aq,ar;for(aq=0;aq<at.length;aq++){var ao=at[aq];av[ao]=1;for(ar=0;ar<au.length;ar++){if(au[ar]&&au[ar][0]){var ap=au[ar][0];if(ao===ap){af(au[ar]);delete au[ar];
-if(av[ap]>1&&ap!=="addTracker"){ak("The method "+ap+' is registered more than once in "_paq" variable. Only the last call has an effect. Please have a look at the multiple Piwik trackers documentation: https://developer.piwik.org/guides/tracking-javascript-guide#multiple-piwik-trackers')}av[ap]++}}}}return au}var C=["addTracker","disableCookies","setTrackerUrl","setAPIUrl","enableCrossDomainLinking","setCrossDomainLinkingTimeout","setSessionCookieTimeout","setVisitorCookieTimeout","setSecureCookie","setCookiePath","setCookieDomain","setDomains","setUserId","setSiteId","alwaysUseSendBeacon","enableLinkTracking","requireConsent","setConsentGiven"];function ad(ao,aq){var ap=new Q(ao,aq);I.push(ap);_paq=c(_paq,C);for(E=0;E<_paq.length;E++){if(_paq[E]){af(_paq[E])}}_paq=new H();e.trigger("TrackerAdded",[ap]);return ap}an(T,"beforeunload",ai,false);window.addEventListener("message",function(au){if(!au||!au.origin){return}var aw,ar,ap;var ax=d(au.origin);var at=e.getAsyncTrackers();for(ar=0;ar<at.length;
-ar++){ap=d(at[ar].getPiwikUrl());if(ap===ax){aw=at[ar];break}}if(!aw){return}var aq=null;try{aq=JSON.parse(au.data)}catch(av){return}if(!aq){return}function ao(aA){var aC=G.getElementsByTagName("iframe");for(ar=0;ar<aC.length;ar++){var aB=aC[ar];var ay=d(aB.src);if(aB.contentWindow&&J(aB.contentWindow.postMessage)&&ay===ax){var az=JSON.stringify(aA);aB.contentWindow.postMessage(az,"*")}}}if(J(aq.maq_initial_value)){ao({maq_opted_in:aq.maq_initial_value&&aw.hasConsent(),maq_url:aw.getPiwikUrl(),maq_optout_by_default:aw.isConsentRequired()})}else{if(J(aq.maq_opted_in)){at=e.getAsyncTrackers();for(ar=0;ar<at.length;ar++){aw=at[ar];if(aq.maq_opted_in){aw.rememberConsentGiven()}else{aw.forgetConsentGiven()}}ao({maq_confirm_opted_in:aw.hasConsent(),maq_url:aw.getPiwikUrl(),maq_optout_by_default:aw.isConsentRequired()})}}});Date.prototype.getTimeAlias=Date.prototype.getTime;e={initialized:false,JSON:T.JSON,DOM:{addEventListener:function(ar,aq,ap,ao){var at=typeof ao;if(at==="undefined"){ao=false
-}an(ar,aq,ap,ao)},onLoad:n,onReady:q,isNodeVisible:j,isOrWasNodeVisible:v.isNodeVisible},on:function(ap,ao){if(!y[ap]){y[ap]=[]}y[ap].push(ao)},off:function(aq,ap){if(!y[aq]){return}var ao=0;for(ao;ao<y[aq].length;ao++){if(y[aq][ao]===ap){y[aq].splice(ao,1)}}},trigger:function(aq,ar,ap){if(!y[aq]){return}var ao=0;for(ao;ao<y[aq].length;ao++){y[aq][ao].apply(ap||T,ar)}},addPlugin:function(ao,ap){b[ao]=ap},getTracker:function(ao,ap){if(!J(ap)){ap=this.getAsyncTracker().getSiteId()}if(!J(ao)){ao=this.getAsyncTracker().getTrackerUrl()}return new Q(ao,ap)},getAsyncTrackers:function(){return I},addTracker:function(ao,aq){var ap;if(!I.length){ap=ad(ao,aq)}else{ap=I[0].addTracker(ao,aq)}return ap},getAsyncTracker:function(ap,at){var ar;if(I&&I.length&&I[0]){ar=I[0]}else{return ad(ap,at)}if(!at&&!ap){return ar}if((!J(at)||null===at)&&ar){at=ar.getSiteId()}if((!J(ap)||null===ap)&&ar){ap=ar.getTrackerUrl()}var aq,ao=0;for(ao;ao<I.length;ao++){aq=I[ao];if(aq&&String(aq.getSiteId())===String(at)&&aq.getTrackerUrl()===ap){return aq
-}}},retryMissedPluginCalls:function(){var ap=ah;ah=[];var ao=0;for(ao;ao<ap.length;ao++){af(ap[ao])}}};if(typeof define==="function"&&define.amd){define("piwik",[],function(){return e});define("matomo",[],function(){return e})}return e}())}
+if(av[ap]>1&&ap!=="addTracker"){ak("The method "+ap+' is registered more than once in "_paq" variable. Only the last call has an effect. Please have a look at the multiple Piwik trackers documentation: https://developer.piwik.org/guides/tracking-javascript-guide#multiple-piwik-trackers')}av[ap]++}}}}return au}var C=["addTracker","disableCookies","setTrackerUrl","setAPIUrl","enableCrossDomainLinking","setCrossDomainLinkingTimeout","setSessionCookieTimeout","setVisitorCookieTimeout","setSecureCookie","setCookiePath","setCookieDomain","setDomains","setUserId","setSiteId","alwaysUseSendBeacon","enableLinkTracking","requireConsent","setConsentGiven"];function ad(ao,aq){var ap=new Q(ao,aq);I.push(ap);_paq=c(_paq,C);for(E=0;E<_paq.length;E++){if(_paq[E]){af(_paq[E])}}_paq=new H();e.trigger("TrackerAdded",[ap]);return ap}an(T,"beforeunload",ai,false);an(T,"message",function(au){if(!au||!au.origin){return}var aw,ar,ap;var ax=d(au.origin);var at=e.getAsyncTrackers();for(ar=0;ar<at.length;ar++){ap=d(at[ar].getPiwikUrl());
+if(ap===ax){aw=at[ar];break}}if(!aw){return}var aq=null;try{aq=JSON.parse(au.data)}catch(av){return}if(!aq){return}function ao(aA){var aC=G.getElementsByTagName("iframe");for(ar=0;ar<aC.length;ar++){var aB=aC[ar];var ay=d(aB.src);if(aB.contentWindow&&J(aB.contentWindow.postMessage)&&ay===ax){var az=JSON.stringify(aA);aB.contentWindow.postMessage(az,"*")}}}if(J(aq.maq_initial_value)){ao({maq_opted_in:aq.maq_initial_value&&aw.hasConsent(),maq_url:aw.getPiwikUrl(),maq_optout_by_default:aw.isConsentRequired()})}else{if(J(aq.maq_opted_in)){at=e.getAsyncTrackers();for(ar=0;ar<at.length;ar++){aw=at[ar];if(aq.maq_opted_in){aw.rememberConsentGiven()}else{aw.forgetConsentGiven()}}ao({maq_confirm_opted_in:aw.hasConsent(),maq_url:aw.getPiwikUrl(),maq_optout_by_default:aw.isConsentRequired()})}}},false);Date.prototype.getTimeAlias=Date.prototype.getTime;e={initialized:false,JSON:T.JSON,DOM:{addEventListener:function(ar,aq,ap,ao){var at=typeof ao;if(at==="undefined"){ao=false}an(ar,aq,ap,ao)},onLoad:n,onReady:q,isNodeVisible:j,isOrWasNodeVisible:v.isNodeVisible},on:function(ap,ao){if(!y[ap]){y[ap]=[]
+}y[ap].push(ao)},off:function(aq,ap){if(!y[aq]){return}var ao=0;for(ao;ao<y[aq].length;ao++){if(y[aq][ao]===ap){y[aq].splice(ao,1)}}},trigger:function(aq,ar,ap){if(!y[aq]){return}var ao=0;for(ao;ao<y[aq].length;ao++){y[aq][ao].apply(ap||T,ar)}},addPlugin:function(ao,ap){b[ao]=ap},getTracker:function(ao,ap){if(!J(ap)){ap=this.getAsyncTracker().getSiteId()}if(!J(ao)){ao=this.getAsyncTracker().getTrackerUrl()}return new Q(ao,ap)},getAsyncTrackers:function(){return I},addTracker:function(ao,aq){var ap;if(!I.length){ap=ad(ao,aq)}else{ap=I[0].addTracker(ao,aq)}return ap},getAsyncTracker:function(ap,at){var ar;if(I&&I.length&&I[0]){ar=I[0]}else{return ad(ap,at)}if(!at&&!ap){return ar}if((!J(at)||null===at)&&ar){at=ar.getSiteId()}if((!J(ap)||null===ap)&&ar){ap=ar.getTrackerUrl()}var aq,ao=0;for(ao;ao<I.length;ao++){aq=I[ao];if(aq&&String(aq.getSiteId())===String(at)&&aq.getTrackerUrl()===ap){return aq}}},retryMissedPluginCalls:function(){var ap=ah;ah=[];var ao=0;for(ao;ao<ap.length;ao++){af(ap[ao])
+}}};if(typeof define==="function"&&define.amd){define("piwik",[],function(){return e});define("matomo",[],function(){return e})}return e}())}
/*!!! pluginTrackerHook */
(function(){function b(){if("object"!==typeof _paq){return false}var c=typeof _paq.length;if("undefined"===c){return false}return !!_paq.length}if(window&&"object"===typeof window.piwikPluginAsyncInit&&window.piwikPluginAsyncInit.length){var a=0;for(a;a<window.piwikPluginAsyncInit.length;a++){if(typeof window.piwikPluginAsyncInit[a]==="function"){window.piwikPluginAsyncInit[a]()}}}if(window&&window.piwikAsyncInit){window.piwikAsyncInit()}if(!window.Piwik.getAsyncTrackers().length){if(b()){window.Piwik.addTracker()}else{_paq={push:function(c){var d=typeof console;if(d!=="undefined"&&console&&console.error){console.error("_paq.push() was used but Matomo tracker was not initialized before the matomo.js file was loaded. Make sure to configure the tracker via _paq.push before loading matomo.js. Alternatively, you can create a tracker via Matomo.addTracker() manually and then use _paq.push but it may not fully work as tracker methods may not be executed in the correct order.",c)
}}}}}window.Piwik.trigger("PiwikInitialized",[]);window.Piwik.initialized=true}());(function(){var a=(typeof AnalyticsTracker);if(a==="undefined"){AnalyticsTracker=window.Piwik}}());if(typeof piwik_log!=="function"){piwik_log=function(b,f,d,g){function a(h){try{if(window["piwik_"+h]){return window["piwik_"+h]}}catch(i){}return}var c,e=window.Piwik.getTracker(d,f);e.setDocumentTitle(b);e.setCustomData(g);c=a("tracker_pause");if(c){e.setLinkTrackingTimer(c)}c=a("download_extensions");if(c){e.setDownloadExtensions(c)}c=a("hosts_alias");if(c){e.setDomains(c)}c=a("ignore_classes");if(c){e.setIgnoreClasses(c)}e.trackPageView();if(a("install_tracker")){piwik_track=function(i,k,j,h){e.setSiteId(k);e.setTrackerUrl(j);e.trackLink(i,h)};e.enableLinkTracking()}}}
diff --git a/piwik.js b/piwik.js
index 353c215066..f4fda77e08 100644
--- a/piwik.js
+++ b/piwik.js
@@ -59,10 +59,10 @@ var dm=di+":"+dh;if(dj){dm+=":"+dj}at(dl,dm,dk)});if(df){return df(dk,di,dh,dj,d
dg=a(dg);if(!dh||!di){return}df=df||"Unknown";ce(function(){var dj=aO(dh,di,df,dg);if(dj){bC(dj,bG)}})};this.trackContentInteractionNode=function(dh,dg){if(N(b3)||!dh){return}var df=null;ce(function(){df=c7(dh,dg);if(df){bC(df,bG)}});return df};this.logAllContentBlocksOnPage=function(){var dh=v.findContentNodes();var df=v.collectContent(dh);var dg=typeof console;if(dg!=="undefined"&&console&&console.log){console.log(df)}};this.trackEvent=function(dg,di,df,dh,dk,dj){ce(function(){at(dg,di,df,dh,dk,dj)})};this.trackSiteSearch=function(df,dh,dg,di){b7=[];ce(function(){b5(df,dh,dg,di)})};this.setEcommerceView=function(di,df,dh,dg){if(Y(dh)){dh=String(dh)}if(!J(dh)||dh===null||dh===false||!dh.length){dh=""}else{if(dh instanceof Array){dh=T.JSON.stringify(dh)}}bT[5]=["_pkc",dh];if(J(dg)&&dg!==null&&dg!==false&&String(dg).length){bT[2]=["_pkp",dg]}if(!Y(di)&&!Y(df)){return}if(Y(di)){bT[3]=["_pks",di]}if(!Y(df)){df=""}bT[4]=["_pkn",df]};this.getEcommerceItems=function(){return JSON.parse(JSON.stringify(cW))
};this.addEcommerceItem=function(dj,df,dh,dg,di){if(Y(dj)){cW[dj]=[String(dj),df,dh,dg,di]}};this.removeEcommerceItem=function(df){if(Y(df)){df=String(df);delete cW[df]}};this.clearEcommerceCart=function(){cW={}};this.trackEcommerceOrder=function(df,dj,di,dh,dg,dk){bW(df,dj,di,dh,dg,dk)};this.trackEcommerceCartUpdate=function(df){bq(df)};this.trackRequest=function(dg,di,dh,df){ce(function(){var dj=cq(dg,di,df);bC(dj,bG,dh)})};this.ping=function(){this.trackRequest("ping=1",null,null,"ping")};this.disableQueueRequest=function(){bA.enabled=false};this.setRequestQueueInterval=function(df){if(df<1000){throw new Error("Request queue interval needs to be at least 1000ms")}bA.interval=df};this.queueRequest=function(df){ce(function(){var dg=cq(df);bA.push(dg)})};this.isConsentRequired=function(){return cx};this.getRememberedConsent=function(){var df=aC(bc);if(aC(cH)){if(df){bV(bc,bn,cT)}return null}if(!df||df===0){return null}return df};this.hasRememberedConsent=function(){return !!this.getRememberedConsent()
};this.requireConsent=function(){cx=true;bz=this.hasRememberedConsent();x++;b["CoreConsent"+x]={unload:function(){if(!bz){aE()}}}};this.setConsentGiven=function(){bz=true;bV(cH,bn,cT);var dg,df;for(dg=0;dg<cG.length;dg++){df=typeof cG[dg];if(df==="string"){bC(cG[dg],bG)}else{if(df==="object"){db(cG[dg],bG)}}}cG=[]};this.rememberConsentGiven=function(dg){if(dg){dg=dg*60*60*1000}else{dg=30*365*24*60*60*1000}this.setConsentGiven();var df=new Date().getTime();da(bc,df,dg,bn,cT,bP)};this.forgetConsentGiven=function(){var df=30*365*24*60*60*1000;bV(bc,bn,cT);da(cH,new Date().getTime(),df,bn,cT,bP);this.requireConsent()};this.isUserOptedOut=function(){return !bz};this.optUserOut=this.forgetConsentGiven;this.forgetUserOptOut=this.rememberConsentGiven;e.trigger("TrackerSetup",[this])}function H(){return{push:af}}function c(au,at){var av={};var aq,ar;for(aq=0;aq<at.length;aq++){var ao=at[aq];av[ao]=1;for(ar=0;ar<au.length;ar++){if(au[ar]&&au[ar][0]){var ap=au[ar][0];if(ao===ap){af(au[ar]);delete au[ar];
-if(av[ap]>1&&ap!=="addTracker"){ak("The method "+ap+' is registered more than once in "_paq" variable. Only the last call has an effect. Please have a look at the multiple Piwik trackers documentation: https://developer.piwik.org/guides/tracking-javascript-guide#multiple-piwik-trackers')}av[ap]++}}}}return au}var C=["addTracker","disableCookies","setTrackerUrl","setAPIUrl","enableCrossDomainLinking","setCrossDomainLinkingTimeout","setSessionCookieTimeout","setVisitorCookieTimeout","setSecureCookie","setCookiePath","setCookieDomain","setDomains","setUserId","setSiteId","alwaysUseSendBeacon","enableLinkTracking","requireConsent","setConsentGiven"];function ad(ao,aq){var ap=new Q(ao,aq);I.push(ap);_paq=c(_paq,C);for(E=0;E<_paq.length;E++){if(_paq[E]){af(_paq[E])}}_paq=new H();e.trigger("TrackerAdded",[ap]);return ap}an(T,"beforeunload",ai,false);window.addEventListener("message",function(au){if(!au||!au.origin){return}var aw,ar,ap;var ax=d(au.origin);var at=e.getAsyncTrackers();for(ar=0;ar<at.length;
-ar++){ap=d(at[ar].getPiwikUrl());if(ap===ax){aw=at[ar];break}}if(!aw){return}var aq=null;try{aq=JSON.parse(au.data)}catch(av){return}if(!aq){return}function ao(aA){var aC=G.getElementsByTagName("iframe");for(ar=0;ar<aC.length;ar++){var aB=aC[ar];var ay=d(aB.src);if(aB.contentWindow&&J(aB.contentWindow.postMessage)&&ay===ax){var az=JSON.stringify(aA);aB.contentWindow.postMessage(az,"*")}}}if(J(aq.maq_initial_value)){ao({maq_opted_in:aq.maq_initial_value&&aw.hasConsent(),maq_url:aw.getPiwikUrl(),maq_optout_by_default:aw.isConsentRequired()})}else{if(J(aq.maq_opted_in)){at=e.getAsyncTrackers();for(ar=0;ar<at.length;ar++){aw=at[ar];if(aq.maq_opted_in){aw.rememberConsentGiven()}else{aw.forgetConsentGiven()}}ao({maq_confirm_opted_in:aw.hasConsent(),maq_url:aw.getPiwikUrl(),maq_optout_by_default:aw.isConsentRequired()})}}});Date.prototype.getTimeAlias=Date.prototype.getTime;e={initialized:false,JSON:T.JSON,DOM:{addEventListener:function(ar,aq,ap,ao){var at=typeof ao;if(at==="undefined"){ao=false
-}an(ar,aq,ap,ao)},onLoad:n,onReady:q,isNodeVisible:j,isOrWasNodeVisible:v.isNodeVisible},on:function(ap,ao){if(!y[ap]){y[ap]=[]}y[ap].push(ao)},off:function(aq,ap){if(!y[aq]){return}var ao=0;for(ao;ao<y[aq].length;ao++){if(y[aq][ao]===ap){y[aq].splice(ao,1)}}},trigger:function(aq,ar,ap){if(!y[aq]){return}var ao=0;for(ao;ao<y[aq].length;ao++){y[aq][ao].apply(ap||T,ar)}},addPlugin:function(ao,ap){b[ao]=ap},getTracker:function(ao,ap){if(!J(ap)){ap=this.getAsyncTracker().getSiteId()}if(!J(ao)){ao=this.getAsyncTracker().getTrackerUrl()}return new Q(ao,ap)},getAsyncTrackers:function(){return I},addTracker:function(ao,aq){var ap;if(!I.length){ap=ad(ao,aq)}else{ap=I[0].addTracker(ao,aq)}return ap},getAsyncTracker:function(ap,at){var ar;if(I&&I.length&&I[0]){ar=I[0]}else{return ad(ap,at)}if(!at&&!ap){return ar}if((!J(at)||null===at)&&ar){at=ar.getSiteId()}if((!J(ap)||null===ap)&&ar){ap=ar.getTrackerUrl()}var aq,ao=0;for(ao;ao<I.length;ao++){aq=I[ao];if(aq&&String(aq.getSiteId())===String(at)&&aq.getTrackerUrl()===ap){return aq
-}}},retryMissedPluginCalls:function(){var ap=ah;ah=[];var ao=0;for(ao;ao<ap.length;ao++){af(ap[ao])}}};if(typeof define==="function"&&define.amd){define("piwik",[],function(){return e});define("matomo",[],function(){return e})}return e}())}
+if(av[ap]>1&&ap!=="addTracker"){ak("The method "+ap+' is registered more than once in "_paq" variable. Only the last call has an effect. Please have a look at the multiple Piwik trackers documentation: https://developer.piwik.org/guides/tracking-javascript-guide#multiple-piwik-trackers')}av[ap]++}}}}return au}var C=["addTracker","disableCookies","setTrackerUrl","setAPIUrl","enableCrossDomainLinking","setCrossDomainLinkingTimeout","setSessionCookieTimeout","setVisitorCookieTimeout","setSecureCookie","setCookiePath","setCookieDomain","setDomains","setUserId","setSiteId","alwaysUseSendBeacon","enableLinkTracking","requireConsent","setConsentGiven"];function ad(ao,aq){var ap=new Q(ao,aq);I.push(ap);_paq=c(_paq,C);for(E=0;E<_paq.length;E++){if(_paq[E]){af(_paq[E])}}_paq=new H();e.trigger("TrackerAdded",[ap]);return ap}an(T,"beforeunload",ai,false);an(T,"message",function(au){if(!au||!au.origin){return}var aw,ar,ap;var ax=d(au.origin);var at=e.getAsyncTrackers();for(ar=0;ar<at.length;ar++){ap=d(at[ar].getPiwikUrl());
+if(ap===ax){aw=at[ar];break}}if(!aw){return}var aq=null;try{aq=JSON.parse(au.data)}catch(av){return}if(!aq){return}function ao(aA){var aC=G.getElementsByTagName("iframe");for(ar=0;ar<aC.length;ar++){var aB=aC[ar];var ay=d(aB.src);if(aB.contentWindow&&J(aB.contentWindow.postMessage)&&ay===ax){var az=JSON.stringify(aA);aB.contentWindow.postMessage(az,"*")}}}if(J(aq.maq_initial_value)){ao({maq_opted_in:aq.maq_initial_value&&aw.hasConsent(),maq_url:aw.getPiwikUrl(),maq_optout_by_default:aw.isConsentRequired()})}else{if(J(aq.maq_opted_in)){at=e.getAsyncTrackers();for(ar=0;ar<at.length;ar++){aw=at[ar];if(aq.maq_opted_in){aw.rememberConsentGiven()}else{aw.forgetConsentGiven()}}ao({maq_confirm_opted_in:aw.hasConsent(),maq_url:aw.getPiwikUrl(),maq_optout_by_default:aw.isConsentRequired()})}}},false);Date.prototype.getTimeAlias=Date.prototype.getTime;e={initialized:false,JSON:T.JSON,DOM:{addEventListener:function(ar,aq,ap,ao){var at=typeof ao;if(at==="undefined"){ao=false}an(ar,aq,ap,ao)},onLoad:n,onReady:q,isNodeVisible:j,isOrWasNodeVisible:v.isNodeVisible},on:function(ap,ao){if(!y[ap]){y[ap]=[]
+}y[ap].push(ao)},off:function(aq,ap){if(!y[aq]){return}var ao=0;for(ao;ao<y[aq].length;ao++){if(y[aq][ao]===ap){y[aq].splice(ao,1)}}},trigger:function(aq,ar,ap){if(!y[aq]){return}var ao=0;for(ao;ao<y[aq].length;ao++){y[aq][ao].apply(ap||T,ar)}},addPlugin:function(ao,ap){b[ao]=ap},getTracker:function(ao,ap){if(!J(ap)){ap=this.getAsyncTracker().getSiteId()}if(!J(ao)){ao=this.getAsyncTracker().getTrackerUrl()}return new Q(ao,ap)},getAsyncTrackers:function(){return I},addTracker:function(ao,aq){var ap;if(!I.length){ap=ad(ao,aq)}else{ap=I[0].addTracker(ao,aq)}return ap},getAsyncTracker:function(ap,at){var ar;if(I&&I.length&&I[0]){ar=I[0]}else{return ad(ap,at)}if(!at&&!ap){return ar}if((!J(at)||null===at)&&ar){at=ar.getSiteId()}if((!J(ap)||null===ap)&&ar){ap=ar.getTrackerUrl()}var aq,ao=0;for(ao;ao<I.length;ao++){aq=I[ao];if(aq&&String(aq.getSiteId())===String(at)&&aq.getTrackerUrl()===ap){return aq}}},retryMissedPluginCalls:function(){var ap=ah;ah=[];var ao=0;for(ao;ao<ap.length;ao++){af(ap[ao])
+}}};if(typeof define==="function"&&define.amd){define("piwik",[],function(){return e});define("matomo",[],function(){return e})}return e}())}
/*!!! pluginTrackerHook */
(function(){function b(){if("object"!==typeof _paq){return false}var c=typeof _paq.length;if("undefined"===c){return false}return !!_paq.length}if(window&&"object"===typeof window.piwikPluginAsyncInit&&window.piwikPluginAsyncInit.length){var a=0;for(a;a<window.piwikPluginAsyncInit.length;a++){if(typeof window.piwikPluginAsyncInit[a]==="function"){window.piwikPluginAsyncInit[a]()}}}if(window&&window.piwikAsyncInit){window.piwikAsyncInit()}if(!window.Piwik.getAsyncTrackers().length){if(b()){window.Piwik.addTracker()}else{_paq={push:function(c){var d=typeof console;if(d!=="undefined"&&console&&console.error){console.error("_paq.push() was used but Matomo tracker was not initialized before the matomo.js file was loaded. Make sure to configure the tracker via _paq.push before loading matomo.js. Alternatively, you can create a tracker via Matomo.addTracker() manually and then use _paq.push but it may not fully work as tracker methods may not be executed in the correct order.",c)
}}}}}window.Piwik.trigger("PiwikInitialized",[]);window.Piwik.initialized=true}());(function(){var a=(typeof AnalyticsTracker);if(a==="undefined"){AnalyticsTracker=window.Piwik}}());if(typeof piwik_log!=="function"){piwik_log=function(b,f,d,g){function a(h){try{if(window["piwik_"+h]){return window["piwik_"+h]}}catch(i){}return}var c,e=window.Piwik.getTracker(d,f);e.setDocumentTitle(b);e.setCustomData(g);c=a("tracker_pause");if(c){e.setLinkTrackingTimer(c)}c=a("download_extensions");if(c){e.setDownloadExtensions(c)}c=a("hosts_alias");if(c){e.setDomains(c)}c=a("ignore_classes");if(c){e.setIgnoreClasses(c)}e.trackPageView();if(a("install_tracker")){piwik_track=function(i,k,j,h){e.setSiteId(k);e.setTrackerUrl(j);e.trackLink(i,h)};e.enableLinkTracking()}}}
diff --git a/plugins/API/tests/System/expected/test_AutoSuggestAPITest_browserName__API.getSuggestedValuesForSegment.xml b/plugins/API/tests/System/expected/test_AutoSuggestAPITest_browserName__API.getSuggestedValuesForSegment.xml
index f2c3a13147..deede069b3 100644
--- a/plugins/API/tests/System/expected/test_AutoSuggestAPITest_browserName__API.getSuggestedValuesForSegment.xml
+++ b/plugins/API/tests/System/expected/test_AutoSuggestAPITest_browserName__API.getSuggestedValuesForSegment.xml
@@ -18,6 +18,7 @@
<row>Amiga Aweb</row>
<row>Atomic Web Browser</row>
<row>Avast Secure Browser</row>
+ <row>AVG Secure Browser</row>
<row>Beaker Browser</row>
<row>Beamrise</row>
<row>BlackBerry Browser</row>
@@ -56,6 +57,7 @@
<row>Chrome Webview</row>
<row>dbrowser</row>
<row>Deepnet Explorer</row>
+ <row>Delta Browser</row>
<row>Dolphin</row>
<row>Dorado</row>
<row>Dooble</row>
@@ -77,6 +79,7 @@
<row>Fennec</row>
<row>Firefox</row>
<row>Firefox Focus</row>
+ <row>Firefox Reality</row>
<row>Firefox Rocket</row>
<row>Flock</row>
<row>Firefox Mobile</row>
@@ -117,6 +120,7 @@
<row>LieBaoFast</row>
<row>LG Browser</row>
<row>Links</row>
+ <row>Lovense Browser</row>
<row>LuaKit</row>
<row>Lunascape</row>
<row>Lynx</row>
@@ -149,6 +153,7 @@
<row>Odyssey Web Browser</row>
<row>Off By One</row>
<row>ONE Browser</row>
+ <row>Opera GX</row>
<row>Opera Neon</row>
<row>Opera Devices</row>
<row>Opera Mini</row>
@@ -156,6 +161,7 @@
<row>Opera</row>
<row>Opera Next</row>
<row>Opera Touch</row>
+ <row>Ordissimo</row>
<row>Oregano</row>
<row>Origyn Web Browser</row>
<row>Openwave Mobile Browser</row>
@@ -199,6 +205,7 @@
<row>Sunrise</row>
<row>SuperBird</row>
<row>Super Fast Browser</row>
+ <row>START Internet Browser</row>
<row>Streamy</row>
<row>Swiftfox</row>
<row>Seznam Browser</row>
diff --git a/plugins/API/tests/System/expected/test_AutoSuggestAPITest_operatingSystemName__API.getSuggestedValuesForSegment.xml b/plugins/API/tests/System/expected/test_AutoSuggestAPITest_operatingSystemName__API.getSuggestedValuesForSegment.xml
index cda5b1843b..ca3ca7f06b 100644
--- a/plugins/API/tests/System/expected/test_AutoSuggestAPITest_operatingSystemName__API.getSuggestedValuesForSegment.xml
+++ b/plugins/API/tests/System/expected/test_AutoSuggestAPITest_operatingSystemName__API.getSuggestedValuesForSegment.xml
@@ -47,6 +47,7 @@
<row>OS/2</row>
<row>OSF1</row>
<row>OpenBSD</row>
+ <row>Ordissimo</row>
<row>PlayStation Portable</row>
<row>PlayStation</row>
<row>Red Hat</row>
diff --git a/plugins/CoreAdminHome/javascripts/optOut.js b/plugins/CoreAdminHome/javascripts/optOut.js
index cbca7a8181..03ba11d927 100644
--- a/plugins/CoreAdminHome/javascripts/optOut.js
+++ b/plugins/CoreAdminHome/javascripts/optOut.js
@@ -4,6 +4,20 @@ function getDomain(url)
return url.replace(/^http[s]?:\/\//, '').replace(/\/.*/, '');
}
+function addEventListener(element, eventType, eventHandler) {
+ if (element.addEventListener) {
+ element.addEventListener(eventType, eventHandler, false);
+
+ return true;
+ }
+
+ if (element.attachEvent) {
+ return element.attachEvent('on' + eventType, eventHandler);
+ }
+
+ element['on' + eventType] = eventHandler;
+}
+
// Strips off protocol and trailing path and URL params
function getHostName(url)
{
@@ -72,7 +86,7 @@ function submitForm(e, form) {
// Send a message to the parent window so that it can set a first-party cookie (a fallback in case
// third-party cookies are not permitted by the browser).
if (typeof parent === 'object' && typeof parent.postMessage !== 'undefined') {
- window.addEventListener('message', function(e) {
+ addEventListener(window, 'message', function(e) {
var data = getDataIfMessageIsForThisFrame(e);
if (!data || typeof data.maq_confirm_opted_in === 'undefined') {
return;
@@ -172,7 +186,7 @@ function showWarningIfCookiesDisabled() {
var initializationTimer = null;
-document.addEventListener('DOMContentLoaded', function() {
+addEventListener(document, 'DOMContentLoaded', function() {
showWarningIfCookiesDisabled();
var trackVisitsCheckbox = document.getElementById('trackVisits');
@@ -200,7 +214,7 @@ document.addEventListener('DOMContentLoaded', function() {
// Listener for initialization message from parent window
// This will tell us the initial state the form should be in
// based on the first-party cookie value (which we can't access directly)
-window.addEventListener('message', function(e) {
+addEventListener(window, 'message', function(e) {
var data = getDataIfMessageIsForThisFrame(e);
if (!data) {
return;
diff --git a/plugins/CoreHome/templates/_headerMessage.twig b/plugins/CoreHome/templates/_headerMessage.twig
index 5633fca3cb..46ecd55c18 100644
--- a/plugins/CoreHome/templates/_headerMessage.twig
+++ b/plugins/CoreHome/templates/_headerMessage.twig
@@ -43,7 +43,7 @@
<br />
{% elseif latest_version_available and not isPiwikDemo and hasSomeViewAccess and not isUserIsAnonymous %}
{% set updateSubject = 'General_NewUpdatePiwikX'|translate(latest_version_available)|e('url') %}
- {{ 'General_PiwikXIsAvailablePleaseNotifyPiwikAdmin'|translate("<a target='_blank' rel='noreferrer noopener' href='https://matomo.org/'>Piwik</a> <a target='_blank' rel='noreferrer noopener' href='https://matomo.org/changelog/'>" ~ latest_version_available ~ "</a>", "<a href='mailto:" ~ superUserEmails ~ "?subject=" ~ updateSubject ~ "'>", "</a>")|raw }}
+ {{ 'General_PiwikXIsAvailablePleaseNotifyPiwikAdmin'|translate("<a target='_blank' rel='noreferrer noopener' href='https://matomo.org/'>Matomo</a> <a target='_blank' rel='noreferrer noopener' href='https://matomo.org/changelog/'>" ~ latest_version_available ~ "</a>", "<a href='mailto:" ~ superUserEmails ~ "?subject=" ~ updateSubject ~ "'>", "</a>")|raw }}
<br />
{% endif %}
diff --git a/plugins/CoreVisualizations/Visualizations/HtmlTable.php b/plugins/CoreVisualizations/Visualizations/HtmlTable.php
index 21116fefb9..42c0798fbf 100644
--- a/plugins/CoreVisualizations/Visualizations/HtmlTable.php
+++ b/plugins/CoreVisualizations/Visualizations/HtmlTable.php
@@ -225,9 +225,10 @@ class HtmlTable extends Visualization
foreach ($this->dataTable->getRows() as $row) {
foreach ($this->report->getMetrics() as $column => $translation) {
- $indexColumn = $columnNamesToIndices[$column]; // TODO: This might fail for goal specific columns...
+ // Try to check the column by it's index (not possible for all metrics, like custom columns)
+ $indexColumn = !empty($columnNamesToIndices[$column]) ? $columnNamesToIndices[$column] : null;
- $value = $row->getColumn($indexColumn) ?: $row->getColumn($column) ?: 0;
+ $value = (($indexColumn && $row->getColumn($indexColumn)) ? $row->getColumn($indexColumn) : $row->getColumn($column)) ?: 0;
if ($column == 'label') {
continue;
}
diff --git a/plugins/Dashboard/tests/UI/expected-screenshots/Dashboard_loaded_token_auth.png b/plugins/Dashboard/tests/UI/expected-screenshots/Dashboard_loaded_token_auth.png
index 53eccc51ba..75623864d3 100644
--- a/plugins/Dashboard/tests/UI/expected-screenshots/Dashboard_loaded_token_auth.png
+++ b/plugins/Dashboard/tests/UI/expected-screenshots/Dashboard_loaded_token_auth.png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:ea8954c9b69f59fe06edb66b9959f3b826786f58f84367bc6a0bab15f67715d2
-size 714817
+oid sha256:8135a587491cf93c53374854053cb54776d8666611b3e6869578edcbd29a4bb7
+size 714834
diff --git a/plugins/Dashboard/tests/UI/expected-screenshots/Dashboard_removed.png b/plugins/Dashboard/tests/UI/expected-screenshots/Dashboard_removed.png
index 53eccc51ba..75623864d3 100644
--- a/plugins/Dashboard/tests/UI/expected-screenshots/Dashboard_removed.png
+++ b/plugins/Dashboard/tests/UI/expected-screenshots/Dashboard_removed.png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:ea8954c9b69f59fe06edb66b9959f3b826786f58f84367bc6a0bab15f67715d2
-size 714817
+oid sha256:8135a587491cf93c53374854053cb54776d8666611b3e6869578edcbd29a4bb7
+size 714834
diff --git a/plugins/Live/Visualizations/VisitorLog.php b/plugins/Live/Visualizations/VisitorLog.php
index d6c3cd08a2..937bfec893 100644
--- a/plugins/Live/Visualizations/VisitorLog.php
+++ b/plugins/Live/Visualizations/VisitorLog.php
@@ -260,11 +260,14 @@ class VisitorLog extends Visualization
}
$action = $group['pageviewAction'];
+ $actionUrl = !empty($action['url']) ? $action['url'] : '';
+ $actionTitle = !empty($action['pageTitle']) ? $action['pageTitle'] : '';
$lastActionGroup = $actionGroups[$previousId];
+ $lastGroupUrl = !empty($lastActionGroup['pageviewAction']['url']) ? $lastActionGroup['pageviewAction']['url'] : '';
+ $lastGroupTitle = !empty($lastActionGroup['pageviewAction']['pageTitle']) ? $lastActionGroup['pageviewAction']['pageTitle'] : '';
$isLastGroupEmpty = empty($actionGroups[$previousId]['actionsOnPage']);
- $isPageviewActionSame = $lastActionGroup['pageviewAction']['url'] == $action['url']
- && $lastActionGroup['pageviewAction']['pageTitle'] == $action['pageTitle'];
+ $isPageviewActionSame = $lastGroupUrl == $actionUrl && $lastGroupTitle == $actionTitle;
// if the current action has the same url/action name as the last, merge w/ the last action group
if ($isLastGroupEmpty
diff --git a/plugins/Live/javascripts/visitorProfile.js b/plugins/Live/javascripts/visitorProfile.js
index 021908f2e8..c533561887 100644
--- a/plugins/Live/javascripts/visitorProfile.js
+++ b/plugins/Live/javascripts/visitorProfile.js
@@ -71,14 +71,27 @@
var self = this,
$element = this.$element;
- $element.on('click', '.visitor-profile-close', function (e) {
- e.preventDefault();
- try {
- $element.tooltip('destroy');
- } catch (e) {}
- broadcast.propagateNewPopoverParameter(false);
- return false;
- });
+ // if there are no popover params in stack, simply close the popover
+ if (!broadcast.popoverParamStack.length) {
+ $element.on('click', '.visitor-profile-close', function (e) {
+ e.preventDefault();
+ try {
+ $element.tooltip('destroy');
+ } catch (e) {
+ }
+ Piwik_Popover.close();
+ });
+ } else {
+ $element.on('click', '.visitor-profile-close', function (e) {
+ e.preventDefault();
+ try {
+ $element.tooltip('destroy');
+ } catch (e) {
+ }
+ broadcast.propagateNewPopoverParameter(false);
+ return false;
+ });
+ }
$element.on('click', '.visitor-profile-toggle-actions', function (e) {
e.preventDefault();
diff --git a/plugins/Morpheus/icons b/plugins/Morpheus/icons
-Subproject d22c22848b3b8f19a031f396745fcaeaefb1abc
+Subproject 44add4357b4394777e53ecea987205cb70eee28
diff --git a/plugins/PrivacyManager/tests/UI/expected-screenshots/PrivacyManager_gdpr_tools_delete_visit_cancelled.png b/plugins/PrivacyManager/tests/UI/expected-screenshots/PrivacyManager_gdpr_tools_delete_visit_cancelled.png
index 2042ab5b23..d47260c242 100644
--- a/plugins/PrivacyManager/tests/UI/expected-screenshots/PrivacyManager_gdpr_tools_delete_visit_cancelled.png
+++ b/plugins/PrivacyManager/tests/UI/expected-screenshots/PrivacyManager_gdpr_tools_delete_visit_cancelled.png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:b65fb3d081c59a5d3925d2cf02e4ba233731e4bac89f77567362be0f31fb5603
-size 544457
+oid sha256:d84b46c2698d65715ea9030081117abc91fbbe8e82ebf8ae6fddf59812861574
+size 578891
diff --git a/plugins/PrivacyManager/tests/UI/expected-screenshots/PrivacyManager_gdpr_tools_delete_visit_unconfirmed.png b/plugins/PrivacyManager/tests/UI/expected-screenshots/PrivacyManager_gdpr_tools_delete_visit_unconfirmed.png
index f86a2bb290..4aff2f54ba 100644
--- a/plugins/PrivacyManager/tests/UI/expected-screenshots/PrivacyManager_gdpr_tools_delete_visit_unconfirmed.png
+++ b/plugins/PrivacyManager/tests/UI/expected-screenshots/PrivacyManager_gdpr_tools_delete_visit_unconfirmed.png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:85f5a6e7f0bf43cad861da1fd9a1ae7ceccda110091b25b023ddc0408f065151
-size 9192
+oid sha256:e41e6690c96286c93a7c76fc81d2dedeb49e14391c7022701412bbd23a162087
+size 9193
diff --git a/plugins/PrivacyManager/tests/UI/expected-screenshots/PrivacyManager_gdpr_tools_enrich_segment_by_ip.png b/plugins/PrivacyManager/tests/UI/expected-screenshots/PrivacyManager_gdpr_tools_enrich_segment_by_ip.png
index 2042ab5b23..d226aeba91 100644
--- a/plugins/PrivacyManager/tests/UI/expected-screenshots/PrivacyManager_gdpr_tools_enrich_segment_by_ip.png
+++ b/plugins/PrivacyManager/tests/UI/expected-screenshots/PrivacyManager_gdpr_tools_enrich_segment_by_ip.png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:b65fb3d081c59a5d3925d2cf02e4ba233731e4bac89f77567362be0f31fb5603
-size 544457
+oid sha256:339c7870fa7f06734f81ecffd150be3d6f65ddfe9f9e81121c5ebecb24102c62
+size 579155
diff --git a/plugins/PrivacyManager/tests/UI/expected-screenshots/PrivacyManager_gdpr_tools_uncheck_one_visit.png b/plugins/PrivacyManager/tests/UI/expected-screenshots/PrivacyManager_gdpr_tools_uncheck_one_visit.png
index a04da828f7..d47260c242 100644
--- a/plugins/PrivacyManager/tests/UI/expected-screenshots/PrivacyManager_gdpr_tools_uncheck_one_visit.png
+++ b/plugins/PrivacyManager/tests/UI/expected-screenshots/PrivacyManager_gdpr_tools_uncheck_one_visit.png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:5bcb002145fd55f0987253acd06ca8d494174c3820ef91812c2d04fb760ba1db
-size 544528
+oid sha256:d84b46c2698d65715ea9030081117abc91fbbe8e82ebf8ae6fddf59812861574
+size 578891
diff --git a/plugins/Referrers/tests/System/ApiTest.php b/plugins/Referrers/tests/System/ApiTest.php
index be8ad2c2e6..ffcd06a46a 100644
--- a/plugins/Referrers/tests/System/ApiTest.php
+++ b/plugins/Referrers/tests/System/ApiTest.php
@@ -220,24 +220,24 @@ class ApiTest extends SystemTestCase
$t = Fixture::getTracker($idSite, $dateTime . ' 00:01:02', $defaultInit = true);
// track an HTTPS request
- $t->setUrlReferrer('https://t.umblr.com/');
+ $t->setUrlReferrer('https://somewebsite.com/');
$t->setUrl('http://piwik.net/');
$t->doTrackPageView('My Title');
// track an HTTP request
$t->setForceNewVisit(true);
- $t->setUrlReferrer('http://t.umblr.com/');
+ $t->setUrlReferrer('http://somewebsite.com/');
$t->setUrl('http://piwik.net/');
$t->doTrackPageView('My Title');
/** @var DataTable $visits */
$visits = Request::processRequest(
- 'Referrers.getWebsites',
+ 'Referrers.getWebsites',
array('idSite' => $idSite, 'period' => 'day', 'date' => $dateTime, 'flat' => 1)
);
$firstRow = $visits->getFirstRow();
- $this->assertEquals('t.umblr.com/index', $firstRow->getColumn('label'));
+ $this->assertEquals('somewebsite.com/index', $firstRow->getColumn('label'));
$this->assertEquals(2, $firstRow->getColumn('nb_visits'));
}
@@ -248,19 +248,19 @@ class ApiTest extends SystemTestCase
$t = Fixture::getTracker($idSite, $dateTime . ' 00:01:02', $defaultInit = true);
// track an HTTPS request
- $t->setUrlReferrer('https://t.umblr.com/');
+ $t->setUrlReferrer('https://somewebsite.com/');
$t->setUrl('http://piwik.net/');
$t->doTrackPageView('My Title');
// track an HTTP request
$t->setForceNewVisit(true);
- $t->setUrlReferrer('http://t.umblr.com/');
+ $t->setUrlReferrer('http://somewebsite.com/');
$t->setUrl('http://piwik.net/');
$t->doTrackPageView('My Title');
/** @var DataTable $visits */
$visits = Request::processRequest(
- 'Referrers.getWebsites',
+ 'Referrers.getWebsites',
array('idSite' => $idSite, 'period' => 'day', 'date' => $dateTime)
);
diff --git a/plugins/SitesManager/templates/_trackingCodeEmail.twig b/plugins/SitesManager/templates/_trackingCodeEmail.twig
index 156eef5530..be2a0e2362 100644
--- a/plugins/SitesManager/templates/_trackingCodeEmail.twig
+++ b/plugins/SitesManager/templates/_trackingCodeEmail.twig
@@ -14,6 +14,7 @@ WordPress: https://matomo.org/faq/new-to-piwik/how-do-i-install-the-matomo-track
Squarespace: https://matomo.org/faq/new-to-piwik/how-do-i-integrate-matomo-with-squarespace-website/
Wix: https://matomo.org/faq/new-to-piwik/how-do-i-install-the-matomo-analytics-tracking-code-on-wix/
SharePoint: https://matomo.org/faq/how-to-install/faq_19424/
+Joomla: https://matomo.org/faq/new-to-piwik/how-do-i-install-the-matomo-analytics-tracking-code-on-joomla/
** {{ 'SitesManager_Integrations'|translate }}
{{ 'CoreAdminHome_JSTrackingIntro3a'|translate('', '') }}
diff --git a/plugins/SitesManager/templates/siteWithoutData.twig b/plugins/SitesManager/templates/siteWithoutData.twig
index f4e13772e4..19e831c00e 100644
--- a/plugins/SitesManager/templates/siteWithoutData.twig
+++ b/plugins/SitesManager/templates/siteWithoutData.twig
@@ -48,6 +48,7 @@
| <a target="_blank" rel="noreferrer noopener" href='https://matomo.org/faq/new-to-piwik/how-do-i-integrate-matomo-with-squarespace-website/'>Squarespace</a>
| <a target="_blank" rel="noreferrer noopener" href='https://matomo.org/faq/new-to-piwik/how-do-i-install-the-matomo-analytics-tracking-code-on-wix/'>Wix</a>
| <a target="_blank" rel="noreferrer noopener" href='https://matomo.org/faq/how-to-install/faq_19424/'>SharePoint</a>
+ | <a target="_blank" rel="noreferrer noopener" href='https://matomo.org/faq/new-to-piwik/how-do-i-install-the-matomo-analytics-tracking-code-on-joomla/'>Joomla</a>
<br >
<br >
{{ 'SitesManager_ExtraInformationNeeded'|translate }}
diff --git a/plugins/UserCountry/Controller.php b/plugins/UserCountry/Controller.php
index 84e7fd2376..97b5eadf92 100644
--- a/plugins/UserCountry/Controller.php
+++ b/plugins/UserCountry/Controller.php
@@ -70,6 +70,9 @@ class Controller extends \Piwik\Plugin\ControllerAdmin
}
$view->isThereWorkingProvider = $isThereWorkingProvider;
+ $view->notUsingGeoIpPlugin = !Manager::getInstance()->isPluginActivated('GeoIp2')
+ && !Manager::getInstance()->isPluginActivated('GeoIp');
+
// if using either the Apache, Nginx or PECL module, they are working and there are no databases
// in misc, then the databases are located outside of Piwik, so we cannot update them
$view->showGeoIPUpdateSection = true;
@@ -80,6 +83,9 @@ class Controller extends \Piwik\Plugin\ControllerAdmin
) {
$view->showGeoIPUpdateSection = false;
}
+ if ($view->notUsingGeoIpPlugin) {
+ $view->showGeoIPUpdateSection = false;
+ }
$view->isInternetEnabled = SettingsPiwik::isInternetEnabled();
diff --git a/plugins/UserCountry/lang/el.json b/plugins/UserCountry/lang/el.json
index b282ebbdd7..e23f0f9a9d 100644
--- a/plugins/UserCountry/lang/el.json
+++ b/plugins/UserCountry/lang/el.json
@@ -98,6 +98,7 @@
"UpdaterScheduledForNextRun": "Είναι προγραμματισμένο για εκτέλεση κατά την εκτέλεση του επόμενου archive.php από το cron.",
"UpdaterWasLastRun": "Το πρόγραμμα ενημέρωσης εκτελέστηκε τελευταία στις %s.",
"UpdaterWillRunNext": "Είναι προγραμματισμένο για εκτέλεση στις %s.",
- "WidgetLocation": "Τοποθεσία Επισκέπτη"
+ "WidgetLocation": "Τοποθεσία Επισκέπτη",
+ "InstallGeoIp2": "Το Matomo προτείνει τη χρήση των βάσεων %1$sdbip%2$s, αλλά αυτό απαιτεί τη χρήση του πρόσθετου GeoIp2. Αν το εγκαταστήσετε και το ενεργοποιήσετε (που ενδεχομένως να χρειαστεί μια μακρόχρονη διαδικασία), θα μπορέσετε να ξεκινήσετε τη χρήση των βάσεων %1$sdbip%2$s. (Αγνοήστε το παρόν αν χρησιμοποιείτε ήδη τρίτο πρόσθετο που παρέχει τη δική του λειτουργικότητα για γεωτοποθεσία.)"
}
} \ No newline at end of file
diff --git a/plugins/UserCountry/lang/en.json b/plugins/UserCountry/lang/en.json
index a707f363e8..852293b84d 100644
--- a/plugins/UserCountry/lang/en.json
+++ b/plugins/UserCountry/lang/en.json
@@ -98,6 +98,7 @@
"UpdaterScheduledForNextRun": "It is scheduled to run during the next cron core:archive command execution.",
"UpdaterWasLastRun": "The updater was last run on %s.",
"UpdaterWillRunNext": "It is next scheduled to run on %s.",
- "WidgetLocation": "Visitor Location"
+ "WidgetLocation": "Visitor Location",
+ "InstallGeoIp2": "Matomo recommends using the %1$sdbip%2$s databases, but this requires using the GeoIp2 plugin. If you install and activate it (which will require a possibly long running update), you will be able to get started using %1$sdbip%2$s databases. (Ignore this if you are using a third party plugin that provides it's own geolocation functionality.)"
}
} \ No newline at end of file
diff --git a/plugins/UserCountry/lang/es-ar.json b/plugins/UserCountry/lang/es-ar.json
index 40cc3a2f78..d2f268ea3b 100644
--- a/plugins/UserCountry/lang/es-ar.json
+++ b/plugins/UserCountry/lang/es-ar.json
@@ -98,6 +98,7 @@
"UpdaterScheduledForNextRun": "Está programado para ejecutarse durante la próxima ejecución de comando cron \"core:archive\".",
"UpdaterWasLastRun": "El actualizador se ejecutó por última vez: %s.",
"UpdaterWillRunNext": "Está programado para ejecutarse: %s.",
- "WidgetLocation": "Ubicación del visitante"
+ "WidgetLocation": "Ubicación del visitante",
+ "InstallGeoIp2": "Matomo recomienda usar las bases de datos de %1$sdbip%2$s, pero requiere usar el plugin GeoIP2. Si lo instalás y activás (el cual probablemente requiera una larga actualización), vas a poder usar las bases de datos de %1$sdbip%2$s (ignorá esto si ya estás usando un plugin de terceros que ofrece su propia función de geolocalización)."
}
} \ No newline at end of file
diff --git a/plugins/UserCountry/lang/pt-br.json b/plugins/UserCountry/lang/pt-br.json
index 5f44beee1b..3927a68629 100644
--- a/plugins/UserCountry/lang/pt-br.json
+++ b/plugins/UserCountry/lang/pt-br.json
@@ -98,6 +98,7 @@
"UpdaterScheduledForNextRun": "Está programado para ser executado durante a próxima execução do cron job archive.php",
"UpdaterWasLastRun": "O atualizador foi executado pela última vez em %s.",
"UpdaterWillRunNext": "A próxima execução está programada para ser executada em %s.",
- "WidgetLocation": "Localização dos visitantes"
+ "WidgetLocation": "Localização dos visitantes",
+ "InstallGeoIp2": "O Matomo recomenda usar as bases de dados %1$sdbip%2$s, mas isto requer usar o plugin GeoIp2. Se você instalá-lo e ativá-lo (o que deve possivelmente exigir uma atualização de longa duração), você poderá começar a usar bases de dados %1$sdbip%2$s. (Ignore isto se você estiver usando um plugin de terceiro que fornece sua própria funcionalidade de geolocalização.)"
}
} \ No newline at end of file
diff --git a/plugins/UserCountry/lang/sq.json b/plugins/UserCountry/lang/sq.json
index 779a1d6bd9..80ea3839f4 100644
--- a/plugins/UserCountry/lang/sq.json
+++ b/plugins/UserCountry/lang/sq.json
@@ -98,6 +98,7 @@
"UpdaterScheduledForNextRun": "Është vënë në plan të xhirojë gjatë përmbushjes së ardhshme të urdhrit cron core:archive.",
"UpdaterWasLastRun": "Përditësuesi u xhirua së fundi më %s.",
"UpdaterWillRunNext": "Është planifikuar të xhirojë sërish më %s.",
- "WidgetLocation": "Vend Vizitori"
+ "WidgetLocation": "Vend Vizitori",
+ "InstallGeoIp2": "Matomo rekomandon përdorimin e bazave të të dhënave %1$sdbip%2$s, por kjo lyp përdorimin e shtojcës GeoIp2. Nëse e instaloni dhe e aktivizoni (çka ka shumë mundësi se do të kërkojë një përditësim që zgjat ca), do të jeni në gjendje të filloni të përdorni baza të dhënash %1$sdbip%2$s. (Shpërfilleni këtë , nëse përdorni një shtojcë pale të tretë që për gjeovendëzim sjell funksione të vetat.)"
}
} \ No newline at end of file
diff --git a/plugins/UserCountry/lang/tr.json b/plugins/UserCountry/lang/tr.json
index 5c5e6fb0cf..ef27d981dc 100644
--- a/plugins/UserCountry/lang/tr.json
+++ b/plugins/UserCountry/lang/tr.json
@@ -98,6 +98,7 @@
"UpdaterScheduledForNextRun": "core:archive komutunun yürütüleceği zamanlanmış görevin bir sonraki çalışmasında güncellenecek.",
"UpdaterWasLastRun": "Güncelleyici en son %s zamanında çalıştı.",
"UpdaterWillRunNext": "Zamanlanmış görev %s zamanında çalışacak.",
- "WidgetLocation": "Ziyaretçi Konumu"
+ "WidgetLocation": "Ziyaretçi Konumu",
+ "InstallGeoIp2": "Matomo %1$sdbip%2$s veritabanlarının kullanılmasını önerir. Ancak bunun için GeoIp2 uygulama eki gereklidir. Bu uygulama ekini kurup etkinleştirdiyseniz (büyük olasılıkla uzun sürecek bir güncelleme yapılır), %1$sdbip%2$s veritabanlarını kullanmaya başlayabilirsiniz (kendi coğrafi konum özelliğini sunan bir üçüncü taraf uygulama eki kullanıyorsanız bunu yok sayabilirsiniz)."
}
} \ No newline at end of file
diff --git a/plugins/UserCountry/templates/adminIndex.twig b/plugins/UserCountry/templates/adminIndex.twig
index ac9e2c1f59..f0a32da588 100644
--- a/plugins/UserCountry/templates/adminIndex.twig
+++ b/plugins/UserCountry/templates/adminIndex.twig
@@ -16,6 +16,7 @@
{% if not isThereWorkingProvider %}
<h3 style="margin-top:0;">{{ 'UserCountry_HowToSetupGeoIP'|translate }}</h3>
+ {% if dbipLiteUrl|default is not empty %}
<p>{{ 'UserCountry_HowToSetupGeoIPIntro'|translate }}</p>
<ul style="list-style:disc !important;margin-left:2em;">
<li style="list-style-type: disc !important;">{{ 'UserCountry_HowToSetupGeoIP_Step1'|translate('<a rel="noreferrer noopener" href="'~dbipLiteUrl~'">','</a>','<a rel="noreferrer noopener" target="_blank" href="http://db-ip.com/?refid=mtm">','</a>')|raw }}</li>
@@ -23,6 +24,9 @@
<li style="list-style-type: disc !important;">{{ 'UserCountry_HowToSetupGeoIP_Step3'|translate('<strong>','</strong>','<span style="color:green"><strong>','</strong></span>')|raw }}</li>
<li style="list-style-type: disc !important;">{{ 'UserCountry_HowToSetupGeoIP_Step4'|translate }}</li>
</ul>
+ {% else %}
+ <p>{{ 'UserCountry_InstallGeoIp2'|translate('<a rel="noreferrer noopener" target="_blank" href="http://db-ip.com/?refid=mtm">','</a>')|raw }}</p>
+ {% endif %}
<p>&nbsp;</p>
{% endif %}
@@ -127,7 +131,7 @@
</div>
{% endif %}
-{% if isInternetEnabled %}
+{% if isInternetEnabled and not notUsingGeoIpPlugin %}
<div piwik-content-block
content-title="{% if not geoIPDatabasesInstalled %}{{ 'UserCountry_GeoIPDatabases'|translate|e('html_attr') }}{% else %}{{ 'UserCountry_SetupAutomaticUpdatesOfGeoIP'|translate|e('html_attr') }}{% endif %}"
id="geoip-db-mangement">
@@ -161,9 +165,14 @@
</div>
</div>
</div>
+ {% if dbipLiteUrl|default is not empty %}
+ {% set downloadingDbLink %}<a href="{{ dbipLiteUrl|e('html_attr') }}">{{ dbipLiteFilename }}</a>{% endset %}
+ {% else %}
+ {% set downloadingDbLink = '' %}
+ {% endif %}
<div id="geoipdb-screen2-download" ng-show="locationUpdater.showFreeDownload">
<div piwik-progressbar
- label="{{ ('UserCountry_DownloadingDb'|translate('<a href="'~dbipLiteUrl~'">'~dbipLiteFilename~'</a>') ~ '...')|json_encode }}"
+ label="{{ ('UserCountry_DownloadingDb'|translate(downloadingDbLink) ~ '...')|json_encode }}"
progress="locationUpdater.progressFreeDownload">
</div>
</div>
diff --git a/plugins/UsersManager/angularjs/capabilities-edit/capabilities-edit.component.html b/plugins/UsersManager/angularjs/capabilities-edit/capabilities-edit.component.html
index acb0b5cf79..4eee7fe20e 100644
--- a/plugins/UsersManager/angularjs/capabilities-edit/capabilities-edit.component.html
+++ b/plugins/UsersManager/angularjs/capabilities-edit/capabilities-edit.component.html
@@ -13,7 +13,7 @@
<span
class="icon-close"
ng-if="!$ctrl.isIncludedInRole(capability)"
- ng-click="$ctrl.capabilityToAddOrRemove = capability; $ctrl.onToggleCapability(fakse)"
+ ng-click="$ctrl.capabilityToAddOrRemoveId = capability.id; $ctrl.onToggleCapability(false)"
></span>
</div>
diff --git a/tests/PHPUnit/Framework/Fixture.php b/tests/PHPUnit/Framework/Fixture.php
index 88b057f4e1..1150444db3 100644
--- a/tests/PHPUnit/Framework/Fixture.php
+++ b/tests/PHPUnit/Framework/Fixture.php
@@ -15,6 +15,7 @@ use Piwik\Auth;
use Piwik\Auth\Password;
use Matomo\Cache\Backend\File;
use Piwik\Cache as PiwikCache;
+use Piwik\CliMulti\CliPhp;
use Piwik\Common;
use Piwik\Config;
use Piwik\Container\StaticContainer;
@@ -145,6 +146,20 @@ class Fixture extends \PHPUnit_Framework_Assert
return 'python';
}
+ public static function getCliCommandBase()
+ {
+ $cliPhp = new CliPhp();
+ $php = $cliPhp->findPhpBinary();
+
+ $command = $php . ' ' . PIWIK_INCLUDE_PATH .'/tests/PHPUnit/proxy/console ';
+
+ if (!empty($_SERVER['HTTP_HOST'])) {
+ $command .= '--matomo-domain=' . $_SERVER['HTTP_HOST'];
+ }
+
+ return $command;
+ }
+
public static function getTestRootUrl()
{
return self::getRootUrl() . 'tests/PHPUnit/proxy/';
diff --git a/tests/PHPUnit/Framework/Mock/TestConfig.php b/tests/PHPUnit/Framework/Mock/TestConfig.php
index 3676163e0d..7fe0302e11 100644
--- a/tests/PHPUnit/Framework/Mock/TestConfig.php
+++ b/tests/PHPUnit/Framework/Mock/TestConfig.php
@@ -58,9 +58,18 @@ class TestConfig extends Config
// Ensure local mods do not affect tests
if (empty($pathGlobal)) {
+ $general = $chain->getFrom($this->getLocalPath(), 'General');
+ $instanceId = isset($general['instance_id']) ? $general['instance_id'] : null;
+
$chain->set('Debug', $chain->getFrom($this->getGlobalPath(), 'Debug'));
$chain->set('mail', $chain->getFrom($this->getGlobalPath(), 'mail'));
- $chain->set('General', $chain->getFrom($this->getGlobalPath(), 'General'));
+
+ $globalGeneral = $chain->getFrom($this->getGlobalPath(), 'General');
+ if ($instanceId) {
+ $globalGeneral['instance_id'] = $instanceId;
+ }
+ $chain->set('General', $globalGeneral);
+
$chain->set('Segments', $chain->getFrom($this->getGlobalPath(), 'Segments'));
$chain->set('Tracker', $chain->getFrom($this->getGlobalPath(), 'Tracker'));
$chain->set('Deletelogs', $chain->getFrom($this->getGlobalPath(), 'Deletelogs'));
diff --git a/tests/PHPUnit/System/ConsoleTest.php b/tests/PHPUnit/System/ConsoleTest.php
index 939cfee6ac..9221fa8530 100644
--- a/tests/PHPUnit/System/ConsoleTest.php
+++ b/tests/PHPUnit/System/ConsoleTest.php
@@ -8,9 +8,13 @@
namespace Piwik\Tests\System;
+use Piwik\CliMulti\CliPhp;
+use Piwik\Config;
use Piwik\Container\StaticContainer;
+use Piwik\Development;
use Piwik\Plugin\ConsoleCommand;
use Piwik\Plugins\Monolog\Handler\FailureLogMessageDetector;
+use Piwik\Tests\Framework\Fixture;
use Psr\Log\LoggerInterface;
use Monolog\Logger;
use Symfony\Component\Console\Input\InputInterface;
@@ -51,6 +55,56 @@ class TestCommandWithError extends ConsoleCommand
}
}
+class TestCommandWithFatalError extends ConsoleCommand
+{
+ public function configure()
+ {
+ parent::configure();
+
+ $this->setName('test-command-with-fatal-error');
+ }
+
+ public function execute(InputInterface $input, OutputInterface $output)
+ {
+ try {
+ \Piwik\ErrorHandler::pushFatalErrorBreadcrumb(static::class);
+
+ $this->executeImpl($input, $output);
+ } finally {
+ \Piwik\ErrorHandler::popFatalErrorBreadcrumb();
+ }
+ }
+
+ public function executeImpl(InputInterface $input, OutputInterface $output)
+ {
+ try {
+ \Piwik\ErrorHandler::pushFatalErrorBreadcrumb(static::class, []);
+
+ $val = "";
+ while (true) {
+ $val .= str_repeat("*", 1024 * 1024 * 1024);
+ }
+ } finally {
+ \Piwik\ErrorHandler::popFatalErrorBreadcrumb();
+ }
+ }
+}
+
+class TestCommandWithException extends ConsoleCommand
+{
+ public function configure()
+ {
+ parent::configure();
+
+ $this->setName('test-command-with-exception');
+ }
+
+ public function execute(InputInterface $input, OutputInterface $output)
+ {
+ throw new \Exception('test error');
+ }
+}
+
class ConsoleTest extends ConsoleCommandTestCase
{
public function setUp()
@@ -89,12 +143,90 @@ class ConsoleTest extends ConsoleCommandTestCase
$this->assertEquals(0, $exitCode);
}
+ public function test_Console_handlesFatalErrorsCorrectly()
+ {
+ $command = Fixture::getCliCommandBase();
+ $command .= ' test-command-with-fatal-error';
+ $command .= ' 2>&1';
+
+ $output = shell_exec($command);
+ $output = $this->normalizeOutput($output);
+
+ $expected = <<<END
+#!/usr/bin/env php
+PHP Fatal error: Allowed memory size of X bytes exhausted (tried to allocate X bytes) in /tests/PHPUnit/System/ConsoleTest.php on line 85
+
+Fatal error: Allowed memory size of X bytes exhausted (tried to allocate X bytes) in /tests/PHPUnit/System/ConsoleTest.php on line 85
+*** IN SAFEMODE ***
+Matomo encountered an error: Allowed memory size of X bytes exhausted (tried to allocate X bytes) (which lead to: Error: array (
+ 'type' => 1,
+ 'message' => 'Allowed memory size of X bytes exhausted (tried to allocate X bytes)',
+ 'file' => '/tests/PHPUnit/System/ConsoleTest.php',
+ 'line' => 85,
+ 'backtrace' => ' on /tests/PHPUnit/System/ConsoleTest.php(85)
+#0 /tests/PHPUnit/System/ConsoleTest.php(72): Piwik\\\\Tests\\\\System\\\\TestCommandWithFatalError->executeImpl()
+#1 /vendor/symfony/console/Symfony/Component/Console/Command/Command.php(257): Piwik\\\\Tests\\\\System\\\\TestCommandWithFatalError->execute()
+',
+))
+END;
+
+ $this->assertEquals($expected, $output);
+ }
+
+ public function test_Console_handlesExceptionsCorrectly()
+ {
+ $command = Fixture::getCliCommandBase();
+ $command .= ' test-command-with-exception';
+ $command .= ' 2>&1';
+
+ $output = shell_exec($command);
+ $output = $this->normalizeOutput($output);
+
+ $expected = <<<END
+#!/usr/bin/env php
+*** IN SAFEMODE ***
+
+
+
+ [Exception]
+ test error
+
+
+
+test-command-with-exception
+
+
+
+END;
+ $this->assertEquals($expected, $output);
+ }
+
public static function provideContainerConfigBeforeClass()
{
return [
'log.handlers' => [\DI\get(FailureLogMessageDetector::class)],
LoggerInterface::class => \DI\object(Logger::class)
->constructor('piwik', \DI\get('log.handlers'), \DI\get('log.processors')),
+
+ 'observers.global' => \DI\add([
+ ['Console.filterCommands', function (&$commands) {
+ $commands[] = TestCommandWithFatalError::class;
+ $commands[] = TestCommandWithException::class;
+ }],
+
+ ['Request.dispatch', function ($module, $action) {
+ if ($module === 'CorePluginsAdmin' && $action === 'safemode') {
+ print "*** IN SAFEMODE ***\n"; // will appear in output
+ }
+ }],
+ ]),
];
}
+
+ private function normalizeOutput($output)
+ {
+ $output = str_replace(PIWIK_INCLUDE_PATH, '', $output);
+ $output = preg_replace('/[0-9]+ bytes/', 'X bytes', $output);
+ return $output;
+ }
} \ No newline at end of file
diff --git a/tests/PHPUnit/System/expected/test_apiGetReportMetadata__API.getSegmentsMetadata.xml b/tests/PHPUnit/System/expected/test_apiGetReportMetadata__API.getSegmentsMetadata.xml
index effff1c382..d3d5264cab 100644
--- a/tests/PHPUnit/System/expected/test_apiGetReportMetadata__API.getSegmentsMetadata.xml
+++ b/tests/PHPUnit/System/expected/test_apiGetReportMetadata__API.getSegmentsMetadata.xml
@@ -106,7 +106,7 @@
<category>Visitors</category>
<name>Device brand</name>
<segment>deviceBrand</segment>
- <acceptedValues>3Q, 4Good, Acer, Advan, Advance, AGM, Ainol, Airness, Airties, AIS, Aiwa, Akai, Alba, Alcatel, AllCall, Allview, Allwinner, Altech UEC, altron, Amazon, AMGOO, Amoi, ANS, Apple, Archos, Arian Space, Ark, Arnova, ARRIS, Ask, Assistant, Asus, Audiovox, AVH, Avvio, Axxion, Azumi Mobile, BangOlufsen, Barnes &amp; Noble, BBK, BDF, Becker, Beeline, Beetel, BenQ, BenQ-Siemens, BGH, Bird, Bitel, Black Fox, Blackview, Blaupunkt, Blu, Bluboo, Bluegood, Bmobile, bogo, Boway, bq, Bravis, Brondi, Bush, CAGI, Capitel, Captiva, Carrefour, Casio, Casper, Cat, Celkon, Changhong, Cherry Mobile, China Mobile, Clarmin, CnM, Coby Kyros, Comio, Compal, Compaq, ComTrade Tesla, Concord, ConCorde, Condor, Coolpad, Cowon, CreNova, Crescent, Cricket, Crius Mea, Crosscall, Cube, CUBOT, CVTE, Cyrus, Danew, Datang, Datsun, Dbtel, Dell, Denver, Desay, DEXP, Dialog, Dicam, Digi, Digicel, Digiland, Digma, DMM, DNS, DoCoMo, Doogee, Doov, Dopod, Doro, Dune HD, E-Boda, E-tel, Easypix, EBEST, Echo Mobiles, ECS, EE, EKO, Eks Mobility, Elenberg, Elephone, Energizer, Energy Sistem, Ergo, Ericsson, Ericy, Essential, Essentielb, Eton, eTouch, Etuline, Eurostar, Evercoss, Evertek, Evolio, Evolveo, EvroMedia, Explay, Extrem, Ezio, Ezze, Fairphone, Famoco, Fengxiang, FiGO, FinePower, Fly, FNB, Fondi, FORME, Forstar, Foxconn, Freetel, Fujitsu, G-TiDE, Garmin-Asus, Gateway, Gemini, General Mobile, Geotel, Ghia, Gigabyte, Gigaset, Ginzzu, Gionee, GOCLEVER, Goly, GoMobile, Google, Gradiente, Grape, Grundig, Hafury, Haier, HannSpree, Hasee, Hi-Level, Highscreen, Hisense, Hoffmann, Homtom, Hoozo, Hosin, HP, HTC, Huawei, Humax, Hyrican, Hyundai, i-Joy, i-mate, i-mobile, iBall, iBerry, IconBIT, iHunt, Ikea, iKoMo, iLA, IMO Mobile, Impression, iNew, Infinix, InFocus, Inkti, InnJoo, Innostream, Inoi, INQ, Insignia, Intek, Intex, Inverto, iOcean, iPro, Irbis, iRola, iTel, iView, iZotron, JAY-Tech, Jiayu, Jolla, Just5, K-Touch, Kaan, Kalley, Karbonn, Kazam, KDDI, Kempler &amp; Strauss, Keneksi, Kiano, Kingsun, Kocaso, Kodak, Kogan, Komu, Konka, Konrow, Koobee, KOPO, Koridy, KRONO, Krüger&amp;Matz, KT-Tech, Kumai, Kyocera, LAIQ, Land Rover, Landvo, Lanix, Lark, Lava, LCT, Leagoo, Ledstar, LeEco, Lemhoov, Lenco, Lenovo, Leotec, Le Pan, Lephone, Lexand, Lexibook, LG, Lingwin, Loewe, Logicom, Lumus, LYF, M.T.T., M4tel, Majestic, Manta Multimedia, Masstel, Maxwest, Maze, Mecer, Mecool, Mediacom, MediaTek, Medion, MEEG, MegaFon, Meitu, Meizu, Memup, Metz, MEU, MicroMax, Microsoft, Mio, Miray, Mitsubishi, MIXC, MLLED, Mobicel, Mobiistar, Mobiola, Mobistel, Modecom, Mofut, Motorola, Movic, Mpman, MSI, MTC, MTN, MYFON, MyPhone, Myria, MyWigo, Navon, NEC, Neffos, Netgear, NeuImage, Newgen, NEXBOX, Nexian, Nextbit, NextBook, NGM, Nikon, Nintendo, NOA, Noain, Nobby, Noblex, Nokia, Nomi, Nous, NUU Mobile, Nvidia, NYX Mobile, O+, O2, Obi, Odys, Onda, OnePlus, OPPO, Opsson, Orange, Ouki, OUYA, Overmax, Oysters, Palm, Panacom, Panasonic, Pantech, PCBOX, PCD, PCD Argentina, PEAQ, Pentagram, Philips, phoneOne, Pioneer, Pixus, Ployer, Plum, Point of View, Polaroid, PolyPad, Polytron, Pomp, Positivo, PPTV, Prestigio, Primepad, ProScan, PULID, Q-Touch, Qilive, QMobile, Qtek, Quantum, Quechua, R-TV, Ramos, RCA Tablets, Readboy, Rikomagic, RIM, Rinno, Ritmix, Riviera, Roadrover, Rokit, Roku, Rombica, Ross&amp;Moor, Rover, RoverPad, RT Project, Safaricom, Sagem, Samsung, Sanei, Santin BiTBiZ, Sanyo, Savio, Sega, Selevision, Selfix, Sencor, Sendo, Senseit, Senwa, SFR, Sharp, Shuttle, Siemens, Sigma, Silent Circle, Simbans, Sky, Skyworth, Smart, Smartfren, Smartisan, Softbank, Sonim, Sony, Sony Ericsson, Spice, Star, Starway, STF Mobile, STK, Stonex, Storex, Sumvision, SunVan, SuperSonic, Supra, SWISSMOBILITY, Symphony, Syrox, T-Mobile, TB Touch, TCL, TechniSat, TechnoTrend, TechPad, Teclast, Tecno Mobile, Telefunken, Telego, Telenor, Telit, Tesco, Tesla, teXet, ThL, Thomson, TIANYU, Timovi, TiPhone, Tolino, Tooky, Top House, Toplux, Toshiba, Touchmate, TrekStor, Trevi, True, Tunisie Telecom, Turbo, Turbo-X, TVC, U.S. Cellular, Uhappy, Ulefone, UMIDIGI, Unimax, Uniscope, Unknown, Unnecto, Unonu, Unowhy, UTOK, UTStarcom, Vastking, Venso, Verizon, Vernee, Vertex, Vertu, Verykool, Vestel, VGO TEL, Videocon, Videoweb, ViewSonic, Vinsoc, Vitelcom, Vivax, Vivo, Vizio, VK Mobile, Vodafone, Vonino, Vorago, Voto, Voxtel, Vulcan, Walton, Web TV, Weimei, WellcoM, Wexler, Wiko, Wileyfox, Wink, Wolder, Wolfgang, Wonu, Woo, Woxter, X-TIGI, X-View, Xiaolajiao, Xiaomi, Xion, Xolo, Yandex, Yarvik, Yes, Yezz, Ytone, Yu, Yuandao, Yusun, Zeemi, Zen, Zenek, Zonda, Zopo, ZTE, Zuum, Zync, ZYQ, öwn</acceptedValues>
+ <acceptedValues>3Q, 4Good, Acer, Advan, Advance, AGM, Ainol, Airness, Airties, AIS, Aiwa, Akai, Alba, Alcatel, AllCall, Allview, Allwinner, Altech UEC, altron, Amazon, AMGOO, Amoi, ANS, Apple, Archos, Arian Space, Ark, Arnova, ARRIS, Ask, Assistant, Asus, Audiovox, AVH, Avvio, Axxion, Azumi Mobile, BangOlufsen, Barnes &amp; Noble, BBK, BDF, Becker, Beeline, Beetel, BenQ, BenQ-Siemens, BGH, Bird, Bitel, Black Fox, Blackview, Blaupunkt, Blu, Bluboo, Bluegood, Bmobile, bogo, Boway, bq, Bravis, Brondi, Bush, CAGI, Capitel, Captiva, Carrefour, Casio, Casper, Cat, Celkon, Changhong, Cherry Mobile, China Mobile, Clarmin, CnM, Coby Kyros, Comio, Compal, Compaq, ComTrade Tesla, Concord, ConCorde, Condor, Coolpad, Cowon, CreNova, Crescent, Cricket, Crius Mea, Crosscall, Cube, CUBOT, CVTE, Cyrus, Danew, Datang, Datsun, Dbtel, Dell, Denver, Desay, DEXP, Dialog, Dicam, Digi, Digicel, Digiland, Digma, DMM, DNS, DoCoMo, Doogee, Doov, Dopod, Doro, Dune HD, E-Boda, E-tel, Easypix, EBEST, Echo Mobiles, ECS, EE, EKO, Eks Mobility, Elenberg, Elephone, Energizer, Energy Sistem, Ergo, Ericsson, Ericy, Essential, Essentielb, Eton, eTouch, Etuline, Eurostar, Evercoss, Evertek, Evolio, Evolveo, EvroMedia, Explay, Extrem, Ezio, Ezze, Fairphone, Famoco, Fengxiang, FiGO, FinePower, Fly, FNB, Fondi, FORME, Forstar, Foxconn, Freetel, Fujitsu, G-TiDE, Garmin-Asus, Gateway, Gemini, General Mobile, Geotel, Ghia, Gigabyte, Gigaset, Ginzzu, Gionee, GOCLEVER, Goly, GoMobile, Google, Gradiente, Grape, Grundig, Hafury, Haier, HannSpree, Hasee, Hi-Level, Highscreen, Hisense, Hoffmann, Homtom, Hoozo, Hosin, HP, HTC, Huawei, Humax, Hyrican, Hyundai, i-Joy, i-mate, i-mobile, iBall, iBerry, IconBIT, iHunt, Ikea, iKoMo, iLA, IMO Mobile, Impression, iNew, Infinix, InFocus, Inkti, InnJoo, Innostream, Inoi, INQ, Insignia, Intek, Intex, Inverto, iOcean, iPro, Irbis, iRola, iTel, iView, iZotron, JAY-Tech, Jiayu, Jolla, Just5, K-Touch, Kaan, Kalley, Karbonn, Kazam, KDDI, Kempler &amp; Strauss, Keneksi, Kiano, Kingsun, Kocaso, Kodak, Kogan, Komu, Konka, Konrow, Koobee, KOPO, Koridy, KRONO, Krüger&amp;Matz, KT-Tech, Kumai, Kyocera, LAIQ, Land Rover, Landvo, Lanix, Lark, Lava, LCT, Leagoo, Ledstar, LeEco, Lemhoov, Lenco, Lenovo, Leotec, Le Pan, Lephone, Lexand, Lexibook, LG, Lingwin, Loewe, Logicom, Lumus, LYF, M.T.T., M4tel, Majestic, Manta Multimedia, Masstel, Maxwest, Maze, Mecer, Mecool, Mediacom, MediaTek, Medion, MEEG, MegaFon, Meitu, Meizu, Memup, Metz, MEU, MicroMax, Microsoft, Mio, Miray, Mitsubishi, MIXC, MLLED, Mobicel, Mobiistar, Mobiola, Mobistel, Modecom, Mofut, Motorola, Movic, Mpman, MSI, MTC, MTN, MYFON, MyPhone, Myria, MyWigo, Navon, NEC, Neffos, Netgear, NeuImage, Newgen, NEXBOX, Nexian, Nextbit, NextBook, NGM, Nikon, Nintendo, NOA, Noain, Nobby, Noblex, Nokia, Nomi, Nous, NUU Mobile, Nvidia, NYX Mobile, O+, O2, Obi, Odys, Onda, OnePlus, OPPO, Opsson, Orange, Ordissimo, Ouki, OUYA, Overmax, Oysters, Palm, Panacom, Panasonic, Pantech, PCBOX, PCD, PCD Argentina, PEAQ, Pentagram, Philips, phoneOne, Pioneer, Pixus, Ployer, Plum, Point of View, Polaroid, PolyPad, Polytron, Pomp, Positivo, PPTV, Prestigio, Primepad, ProScan, PULID, Q-Touch, Qilive, QMobile, Qtek, Quantum, Quechua, R-TV, Ramos, RCA Tablets, Readboy, Rikomagic, RIM, Rinno, Ritmix, Riviera, Roadrover, Rokit, Roku, Rombica, Ross&amp;Moor, Rover, RoverPad, RT Project, Safaricom, Sagem, Samsung, Sanei, Santin BiTBiZ, Sanyo, Savio, Sega, Selevision, Selfix, Sencor, Sendo, Senseit, Senwa, SFR, Sharp, Shuttle, Siemens, Sigma, Silent Circle, Simbans, Sky, Skyworth, Smart, Smartfren, Smartisan, Softbank, Sonim, Sony, Sony Ericsson, Spice, Star, Starway, STF Mobile, STK, Stonex, Storex, Sumvision, SunVan, SuperSonic, Supra, SWISSMOBILITY, Symphony, Syrox, T-Mobile, TB Touch, TCL, TechniSat, TechnoTrend, TechPad, Teclast, Tecno Mobile, Telefunken, Telego, Telenor, Telit, Tesco, Tesla, teXet, ThL, Thomson, TIANYU, Timovi, TiPhone, Tolino, Tooky, Top House, Toplux, Toshiba, Touchmate, TrekStor, Trevi, True, Tunisie Telecom, Turbo, Turbo-X, TVC, U.S. Cellular, Uhappy, Ulefone, UMIDIGI, Unimax, Uniscope, Unknown, Unnecto, Unonu, Unowhy, UTOK, UTStarcom, Vastking, Venso, Verizon, Vernee, Vertex, Vertu, Verykool, Vestel, VGO TEL, Videocon, Videoweb, ViewSonic, Vinsoc, Vitelcom, Vivax, Vivo, Vizio, VK Mobile, Vodafone, Vonino, Vorago, Voto, Voxtel, Vulcan, Walton, Web TV, Weimei, WellcoM, Wexler, Wiko, Wileyfox, Wink, Wolder, Wolfgang, Wonu, Woo, Woxter, X-TIGI, X-View, Xiaolajiao, Xiaomi, Xion, Xolo, Yandex, Yarvik, Yes, Yezz, Ytone, Yu, Yuandao, Yusun, Zeemi, Zen, Zenek, Zonda, Zopo, ZTE, Zuum, Zync, ZYQ, öwn</acceptedValues>
</row>
<row>
<type>dimension</type>
diff --git a/tests/PHPUnit/Unit/Archiver/RequestTest.php b/tests/PHPUnit/Unit/Archiver/RequestTest.php
index ae168023c2..6ae658e10b 100644
--- a/tests/PHPUnit/Unit/Archiver/RequestTest.php
+++ b/tests/PHPUnit/Unit/Archiver/RequestTest.php
@@ -11,9 +11,16 @@ namespace Piwik\Tests\Unit\Archiver;
use Piwik\Archiver\Request;
+use Piwik\Date;
class RequestTest extends \PHPUnit_Framework_TestCase
{
+ protected function tearDown()
+ {
+ parent::tearDown();
+ Date::$now = null;
+ }
+
/**
* @dataProvider getTestDataForChangeDate
*/
@@ -44,4 +51,25 @@ class RequestTest extends \PHPUnit_Framework_TestCase
],
];
}
+
+ /**
+ * @dataProvider getTestDataForMakeSureDateIsNotSingleDayRange
+ */
+ public function test_makeSureDateIsNotSingleDayRange($url, $expectedResult)
+ {
+ $request = new Request($url);
+ $request->makeSureDateIsNotSingleDayRange();
+ $this->assertEquals($expectedResult, $request->getUrl());
+ }
+
+ public function getTestDataForMakeSureDateIsNotSingleDayRange()
+ {
+ return [
+ ['?idSite=1&date=2012-03-04&period=day', '?idSite=1&date=2012-03-04&period=day'],
+ ['?idSite=1&date=2012-03-04,2012-03-06&period=range', '?idSite=1&date=2012-03-04,2012-03-06&period=range'],
+ ['?idSite=1&date=2012-03-04,2012-03-04&period=range', '?idSite=1&date=2012-03-04&period=day'],
+ ['?idSite=1&date=last1&period=range', '?idSite=1&date=today&period=day'],
+ ['?idSite=1&date=previous1&period=range', '?idSite=1&date=yesterday&period=day'],
+ ];
+ }
} \ No newline at end of file
diff --git a/tests/UI/expected-screenshots/Comparison_segmented_visitorlog.png b/tests/UI/expected-screenshots/Comparison_segmented_visitorlog.png
index 70a7be1660..0143bbd3e6 100644
--- a/tests/UI/expected-screenshots/Comparison_segmented_visitorlog.png
+++ b/tests/UI/expected-screenshots/Comparison_segmented_visitorlog.png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:9b42b527f2c040b7d8a799f2911d4106ea7d45c4445492ba01c5612e96e1d1a5
-size 402405
+oid sha256:a2c444a6346d473478d736bf795daa423f63c8b4b117d24143b295bb5b7433e6
+size 402422
diff --git a/tests/UI/expected-screenshots/EmptySite_emptySiteDashboard.png b/tests/UI/expected-screenshots/EmptySite_emptySiteDashboard.png
index 398f1992d8..364c635978 100644
--- a/tests/UI/expected-screenshots/EmptySite_emptySiteDashboard.png
+++ b/tests/UI/expected-screenshots/EmptySite_emptySiteDashboard.png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:5659a10d7a765d14695f644d0f261ec48e3837b4e2d76cf4adc35cf201a47b48
-size 299416
+oid sha256:955f729afa61f6602b09e45da4535de8e64608cc7a4f14229a1fd4992f88e88f
+size 300129
diff --git a/tests/UI/expected-screenshots/OneClickUpdate_login.png b/tests/UI/expected-screenshots/OneClickUpdate_login.png
index 8ffded54d4..c0f41534c3 100644
--- a/tests/UI/expected-screenshots/OneClickUpdate_login.png
+++ b/tests/UI/expected-screenshots/OneClickUpdate_login.png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:63cfae7c2afcafca1d96340384b829f817219e36dbd6d89fca81a0079f71610f
-size 244652
+oid sha256:1053964b638aff9107e69cd0d465a0a72799f3ea9db8884df3117c6d42efc830
+size 268010
diff --git a/tests/UI/expected-screenshots/Theme_home.png b/tests/UI/expected-screenshots/Theme_home.png
index cbb3613d0c..548efd6092 100644
--- a/tests/UI/expected-screenshots/Theme_home.png
+++ b/tests/UI/expected-screenshots/Theme_home.png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:78d41258eee99c5fe3afeecada66a2bd6c6b386684aaefa87ed6dc03e21cf643
-size 708602
+oid sha256:07a3d8e06179049407d8089499fce1e6c45c7622755bf411dba01680774486ef
+size 708620
diff --git a/tests/UI/expected-screenshots/UIIntegrationTest_dashboard1.png b/tests/UI/expected-screenshots/UIIntegrationTest_dashboard1.png
index ccfa9ab4e7..5e2f22458d 100644
--- a/tests/UI/expected-screenshots/UIIntegrationTest_dashboard1.png
+++ b/tests/UI/expected-screenshots/UIIntegrationTest_dashboard1.png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:4060bac4f42231b58dce665c64998e9886cd6d5175ee9c4692080b8cdd23ddae
-size 632336
+oid sha256:c2490d9349b38b65962458e6344b767ae32c3adbf3e7f040a367035d82c845fd
+size 632381
diff --git a/tests/UI/expected-screenshots/UIIntegrationTest_ecommerce_log.png b/tests/UI/expected-screenshots/UIIntegrationTest_ecommerce_log.png
index f565793aaf..4329757954 100644
--- a/tests/UI/expected-screenshots/UIIntegrationTest_ecommerce_log.png
+++ b/tests/UI/expected-screenshots/UIIntegrationTest_ecommerce_log.png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:7bed358956b83d163cd6b6a50add25d343479caecfc63f1752f45bce31af0a2f
-size 499249
+oid sha256:4bb7d263038ade7d53af8cda07d482efef3dc741626380832f112de561bab741
+size 499294
diff --git a/tests/UI/expected-screenshots/UIIntegrationTest_ecommerce_log_segmented.png b/tests/UI/expected-screenshots/UIIntegrationTest_ecommerce_log_segmented.png
index 779c8f836f..e2b9cf6263 100644
--- a/tests/UI/expected-screenshots/UIIntegrationTest_ecommerce_log_segmented.png
+++ b/tests/UI/expected-screenshots/UIIntegrationTest_ecommerce_log_segmented.png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:c1f826eac0237f8447bbea06cb8b0fb092adaf6007eddcb4c39bd477de4ee581
-size 61880
+oid sha256:84a67bdeb17c8b0fdad09f9170bdebb5f3325c2d459ebfcd35fcc648fc5e5552
+size 61896
diff --git a/tests/UI/expected-screenshots/UIIntegrationTest_segmented_visitorlog.png b/tests/UI/expected-screenshots/UIIntegrationTest_segmented_visitorlog.png
index b13791984c..f3d5270ff6 100644
--- a/tests/UI/expected-screenshots/UIIntegrationTest_segmented_visitorlog.png
+++ b/tests/UI/expected-screenshots/UIIntegrationTest_segmented_visitorlog.png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:292a885028edf409c1e20af41e3e54bed24c21a6cbf0118885e083a8fb63a7b7
-size 379446
+oid sha256:1b7ca48581626674bef127e465760e2fe5d22a85801ff7d96bdaeddaf3d33ee1
+size 379471
diff --git a/tests/UI/expected-screenshots/UIIntegrationTest_visitor_profile_popup.png b/tests/UI/expected-screenshots/UIIntegrationTest_visitor_profile_popup.png
index 2672fb78e1..41a8af6fb6 100644
--- a/tests/UI/expected-screenshots/UIIntegrationTest_visitor_profile_popup.png
+++ b/tests/UI/expected-screenshots/UIIntegrationTest_visitor_profile_popup.png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:b8d15df1c59ece6e63a11886d934de599a6625fca9a0edc3c0ba55e6675e65e8
-size 184786
+oid sha256:bd66aa14cc29d4917ac5bbcda1ae99aaeac188d485b22d944c8eca55666cb0c5
+size 184802
diff --git a/tests/UI/expected-screenshots/UIIntegrationTest_visitors_realtime_map.png b/tests/UI/expected-screenshots/UIIntegrationTest_visitors_realtime_map.png
index 8c1a283cf5..d79dd14d58 100644
--- a/tests/UI/expected-screenshots/UIIntegrationTest_visitors_realtime_map.png
+++ b/tests/UI/expected-screenshots/UIIntegrationTest_visitors_realtime_map.png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:e152f24cce664e5919939e0e1433a32ac7a54274c66d51d6f448f31af91007cb
-size 117081
+oid sha256:03ee5b90fbcda06971f51a13115882df782af7a5dda0a74ab0824f3d897bb75d
+size 117095
diff --git a/tests/UI/expected-screenshots/UIIntegrationTest_visitors_software.png b/tests/UI/expected-screenshots/UIIntegrationTest_visitors_software.png
index ec35942247..595620a5a6 100644
--- a/tests/UI/expected-screenshots/UIIntegrationTest_visitors_software.png
+++ b/tests/UI/expected-screenshots/UIIntegrationTest_visitors_software.png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:d48a483dec59934bbc464b46e6e9d32a7bfed6ecfdefe9e5a85c868b331211a8
-size 152916
+oid sha256:44eaebc5b4514b58019679140201ec2f1ea34295ae0a994164de98096a94723b
+size 152925
diff --git a/tests/UI/expected-screenshots/UIIntegrationTest_widgetize_ecommercelog.png b/tests/UI/expected-screenshots/UIIntegrationTest_widgetize_ecommercelog.png
index 8c0c0cd66f..d17add0934 100644
--- a/tests/UI/expected-screenshots/UIIntegrationTest_widgetize_ecommercelog.png
+++ b/tests/UI/expected-screenshots/UIIntegrationTest_widgetize_ecommercelog.png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:96dcb392d7faef30bad0c5a48f13b4eea7529e887af3d9fe136a83c1acd5f0c0
-size 485504
+oid sha256:64cd903c8e7c86e364bc239022d2cf0a669ba8d69f87ebf92d38849b4b7745be
+size 485536
diff --git a/tests/travis b/tests/travis
-Subproject 74e0a7b6902f6799f3647b0f29638158d3b1116
+Subproject f215e0066c4ea0c45cceaee3ed6a06fc623ebaf