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:
authorThomas Steur <tsteur@users.noreply.github.com>2020-10-01 23:13:34 +0300
committerGitHub <noreply@github.com>2020-10-01 23:13:34 +0300
commit06d43857c48ada2fa7f1ad18a8309e8826c0e413 (patch)
tree7f608a20321c2e5ba2a157fb3685178e99ed8b97
parent82186597b37e255a5b13f6b9c7e53d323b2c501b (diff)
Offer feature to anonymise or shorten the referrer (#16376)
-rw-r--r--plugins/Diagnostics/tests/UI/expected-screenshots/Diagnostics_page.png4
-rw-r--r--plugins/PrivacyManager/API.php48
-rw-r--r--plugins/PrivacyManager/Config.php2
-rw-r--r--plugins/PrivacyManager/Controller.php13
-rw-r--r--plugins/PrivacyManager/Diagnostic/PrivacyInformational.php46
-rw-r--r--plugins/PrivacyManager/PrivacyManager.php21
-rw-r--r--plugins/PrivacyManager/ReferrerAnonymizer.php113
-rw-r--r--plugins/PrivacyManager/Tracker/RequestProcessor.php57
-rw-r--r--plugins/PrivacyManager/angularjs/anonymize-ip/anonymize-ip.controller.js1
-rw-r--r--plugins/PrivacyManager/config/config.php7
-rw-r--r--plugins/PrivacyManager/lang/en.json7
-rw-r--r--plugins/PrivacyManager/templates/privacySettings.twig8
-rw-r--r--plugins/PrivacyManager/tests/Fixtures/FewVisitsAnonymizedFixture.php81
-rw-r--r--plugins/PrivacyManager/tests/Integration/PrivacyManagerConfigTest.php11
-rw-r--r--plugins/PrivacyManager/tests/Integration/Tracker/RequestProcessorTest.php111
-rw-r--r--plugins/PrivacyManager/tests/System/AnonymizationTest.php2
-rw-r--r--plugins/PrivacyManager/tests/System/expected/test_userIdAnonymized__Live.getLastVisitsDetails_year.xml648
-rw-r--r--plugins/PrivacyManager/tests/System/expected/test_userIdAnonymized__Referrers.getAll_year.xml65
-rw-r--r--plugins/PrivacyManager/tests/UI/expected-screenshots/PrivacyManager_privacy_settings_default.png4
-rw-r--r--plugins/PrivacyManager/tests/Unit/AnonymizeReferrerTest.php100
20 files changed, 1331 insertions, 18 deletions
diff --git a/plugins/Diagnostics/tests/UI/expected-screenshots/Diagnostics_page.png b/plugins/Diagnostics/tests/UI/expected-screenshots/Diagnostics_page.png
index 535692f329..581764e419 100644
--- a/plugins/Diagnostics/tests/UI/expected-screenshots/Diagnostics_page.png
+++ b/plugins/Diagnostics/tests/UI/expected-screenshots/Diagnostics_page.png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:45b2e4e9624e72f440e8121ecf8ddf7827c0af1504149a97fbb88008526d8aa2
-size 373173
+oid sha256:e35c892601ee82cca7b4927d06cf0b62d695764becaa79f49ce26c993f263886
+size 378134
diff --git a/plugins/PrivacyManager/API.php b/plugins/PrivacyManager/API.php
index 94881b8b8d..fe260abcd0 100644
--- a/plugins/PrivacyManager/API.php
+++ b/plugins/PrivacyManager/API.php
@@ -8,6 +8,7 @@
namespace Piwik\Plugins\PrivacyManager;
+use Piwik\API\Request;
use Piwik\Piwik;
use Piwik\Config as PiwikConfig;
use Piwik\Plugins\PrivacyManager\Model\DataSubjects;
@@ -39,11 +40,21 @@ class API extends \Piwik\Plugin\API
*/
private $logDataAnonymizer;
- public function __construct(DataSubjects $gdpr, LogDataAnonymizations $logDataAnonymizations, LogDataAnonymizer $logDataAnonymizer)
- {
+ /**
+ * @var ReferrerAnonymizer
+ */
+ private $referrerAnonymizer;
+
+ public function __construct(
+ DataSubjects $gdpr,
+ LogDataAnonymizations $logDataAnonymizations,
+ LogDataAnonymizer $logDataAnonymizer,
+ ReferrerAnonymizer $referrerAnonymizer
+ ) {
$this->gdpr = $gdpr;
$this->logDataAnonymizations = $logDataAnonymizations;
$this->logDataAnonymizer = $logDataAnonymizer;
+ $this->referrerAnonymizer = $referrerAnonymizer;
}
private function checkDataSubjectVisits($visits)
@@ -75,8 +86,15 @@ class API extends \Piwik\Plugin\API
return $this->gdpr->exportDataSubjects($visits);
}
- public function anonymizeSomeRawData($idSites, $date, $anonymizeIp = false, $anonymizeLocation = false, $anonymizeUserId = false, $unsetVisitColumns = [], $unsetLinkVisitActionColumns = [])
- {
+ public function anonymizeSomeRawData(
+ $idSites,
+ $date,
+ $anonymizeIp = false,
+ $anonymizeLocation = false,
+ $anonymizeUserId = false,
+ $unsetVisitColumns = [],
+ $unsetLinkVisitActionColumns = []
+ ) {
Piwik::checkUserHasSuperUserAccess();
if ($idSites === 'all' || empty($idSites)) {
@@ -85,7 +103,16 @@ class API extends \Piwik\Plugin\API
$idSites = Site::getIdSitesFromIdSitesString($idSites);
}
$requester = Piwik::getCurrentUserLogin();
- $this->logDataAnonymizations->scheduleEntry($requester, $idSites, $date, $anonymizeIp, $anonymizeLocation, $anonymizeUserId, $unsetVisitColumns, $unsetLinkVisitActionColumns);
+ $this->logDataAnonymizations->scheduleEntry(
+ $requester,
+ $idSites,
+ $date,
+ $anonymizeIp,
+ $anonymizeLocation,
+ $anonymizeUserId,
+ $unsetVisitColumns,
+ $unsetLinkVisitActionColumns
+ );
}
public function getAvailableVisitColumnsToAnonymize()
@@ -93,6 +120,7 @@ class API extends \Piwik\Plugin\API
Piwik::checkUserHasSuperUserAccess();
$columns = $this->logDataAnonymizer->getAvailableVisitColumnsToAnonymize();
+
return $this->formatAvailableColumnsToAnonymize($columns);
}
@@ -101,6 +129,7 @@ class API extends \Piwik\Plugin\API
Piwik::checkUserHasSuperUserAccess();
$columns = $this->logDataAnonymizer->getAvailableLinkVisitActionColumnsToAnonymize();
+
return $this->formatAvailableColumnsToAnonymize($columns);
}
@@ -114,13 +143,14 @@ class API extends \Piwik\Plugin\API
'default_value' => $default
);
}
+
return $formatted;
}
/**
* @internal
*/
- public function setAnonymizeIpSettings($anonymizeIPEnable, $maskLength, $useAnonymizedIpForVisitEnrichment, $anonymizeUserId = false, $anonymizeOrderId = false)
+ public function setAnonymizeIpSettings($anonymizeIPEnable, $maskLength, $useAnonymizedIpForVisitEnrichment, $anonymizeUserId = false, $anonymizeOrderId = false, $anonymizeReferrer = '')
{
Piwik::checkUserHasSuperUserAccess();
@@ -132,9 +162,15 @@ class API extends \Piwik\Plugin\API
// pass
}
+ if (!empty($anonymizeReferrer)
+ && !array_key_exists($anonymizeReferrer, $this->referrerAnonymizer->getAvailableAnonymizationOptions())) {
+ $anonymizeReferrer = '';
+ }
+
$privacyConfig = new Config();
$privacyConfig->ipAddressMaskLength = (int) $maskLength;
$privacyConfig->useAnonymizedIpForVisitEnrichment = (bool) $useAnonymizedIpForVisitEnrichment;
+ $privacyConfig->anonymizeReferrer = $anonymizeReferrer;
if (false !== $anonymizeUserId) {
$privacyConfig->anonymizeUserId = (bool) $anonymizeUserId;
diff --git a/plugins/PrivacyManager/Config.php b/plugins/PrivacyManager/Config.php
index 4d0409ddaf..597d4ef3c4 100644
--- a/plugins/PrivacyManager/Config.php
+++ b/plugins/PrivacyManager/Config.php
@@ -25,6 +25,7 @@ use Piwik\Tracker\Cache;
* For IPv6 addresses 0..4 means that 0, 64, 80, 104 or all bits are masked.
* @property int $anonymizeUserId If enabled, it will pseudo anonymize the User ID
* @property int $anonymizeOrderId If enabled, it will anonymize the Order ID
+ * @property string $anonymizeReferrer Whether the referrer should be anonymized and how it much it should be anonymized
*/
class Config
{
@@ -35,6 +36,7 @@ class Config
'ipAnonymizerEnabled' => array('type' => 'boolean', 'default' => true),
'anonymizeUserId' => array('type' => 'boolean', 'default' => false),
'anonymizeOrderId' => array('type' => 'boolean', 'default' => false),
+ 'anonymizeReferrer' => array('type' => 'string', 'default' => ''),
);
public function __set($name, $value)
diff --git a/plugins/PrivacyManager/Controller.php b/plugins/PrivacyManager/Controller.php
index 130b65318c..005ee74d22 100644
--- a/plugins/PrivacyManager/Controller.php
+++ b/plugins/PrivacyManager/Controller.php
@@ -32,6 +32,17 @@ class Controller extends \Piwik\Plugin\ControllerAdmin
const ACTIVATE_DNT_NONCE = 'PrivacyManager.activateDnt';
const DEACTIVATE_DNT_NONCE = 'PrivacyManager.deactivateDnt';
+ /**
+ * @var ReferrerAnonymizer
+ */
+ private $referrerAnonymizer;
+
+ public function __construct(ReferrerAnonymizer $referrerAnonymizer)
+ {
+ parent::__construct();
+ $this->referrerAnonymizer = $referrerAnonymizer;
+ }
+
private function checkDataPurgeAdminSettingsIsEnabled()
{
if (!self::isDataPurgeSettingsEnabled()) {
@@ -208,6 +219,7 @@ class Controller extends \Piwik\Plugin\ControllerAdmin
array('key' => '30',
'value' => Piwik::translate('Intl_PeriodMonth'))
);
+ $view->referrerAnonymizationOptions = $this->referrerAnonymizer->getAvailableAnonymizationOptions();
}
$view->language = LanguagesManager::getLanguageCodeForCurrentUser();
$this->setBasicVariablesView($view);
@@ -317,6 +329,7 @@ class Controller extends \Piwik\Plugin\ControllerAdmin
$anonymizeIP["anonymizeOrderId"] = $privacyConfig->anonymizeOrderId;
$anonymizeIP["anonymizeUserId"] = $privacyConfig->anonymizeUserId;
$anonymizeIP["useAnonymizedIpForVisitEnrichment"] = $privacyConfig->useAnonymizedIpForVisitEnrichment;
+ $anonymizeIP["anonymizeReferrer"] = $privacyConfig->anonymizeReferrer;
if (!$anonymizeIP["useAnonymizedIpForVisitEnrichment"]) {
$anonymizeIP["useAnonymizedIpForVisitEnrichment"] = '0';
}
diff --git a/plugins/PrivacyManager/Diagnostic/PrivacyInformational.php b/plugins/PrivacyManager/Diagnostic/PrivacyInformational.php
new file mode 100644
index 0000000000..ee8bbed1f2
--- /dev/null
+++ b/plugins/PrivacyManager/Diagnostic/PrivacyInformational.php
@@ -0,0 +1,46 @@
+<?php
+/**
+ * Matomo - free/libre analytics platform
+ *
+ * @link https://matomo.org
+ * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
+ */
+namespace Piwik\Plugins\PrivacyManager\Diagnostic;
+
+use Piwik\Plugins\Diagnostics\Diagnostic\Diagnostic;
+use Piwik\Plugins\Diagnostics\Diagnostic\DiagnosticResult;
+use Piwik\Plugins\PrivacyManager\Config;
+use Piwik\SettingsPiwik;
+use Piwik\Translation\Translator;
+
+/**
+ * Information about Matomo itself
+ */
+class PrivacyInformational implements Diagnostic
+{
+ /**
+ * @var Translator
+ */
+ private $translator;
+
+ public function __construct(Translator $translator)
+ {
+ $this->translator = $translator;
+ }
+
+ public function execute()
+ {
+ $results = [];
+
+ if (SettingsPiwik::isMatomoInstalled()) {
+ $config = new Config();
+
+ $results[] = DiagnosticResult::informationalResult('Anonymize Referrer', $config->anonymizeReferrer);
+ $results[] = DiagnosticResult::informationalResult('Do Not Track enabled', $config->doNotTrackEnabled);
+ }
+
+ return $results;
+ }
+
+
+}
diff --git a/plugins/PrivacyManager/PrivacyManager.php b/plugins/PrivacyManager/PrivacyManager.php
index 96d659eb58..58712c9fa0 100644
--- a/plugins/PrivacyManager/PrivacyManager.php
+++ b/plugins/PrivacyManager/PrivacyManager.php
@@ -185,10 +185,29 @@ class PrivacyManager extends Plugin
'Installation.defaultSettingsForm.submit' => 'installationFormSubmit',
'Translate.getClientSideTranslationKeys' => 'getClientSideTranslationKeys',
'Template.pageFooter' => 'renderPrivacyPolicyLinks',
- 'Db.getTablesInstalled' => 'getTablesInstalled'
+ 'Db.getTablesInstalled' => 'getTablesInstalled',
+ 'Visualization.beforeRender' => 'onConfigureVisualisation'
);
}
+ public function onConfigureVisualisation(Plugin\Visualization $view)
+ {
+ if ($view->requestConfig->getApiModuleToRequest() === 'Referrers' && !$view->requestConfig->idSubtable) {
+ $config = new Config();
+ if ($config->anonymizeReferrer == ReferrerAnonymizer::EXCLUDE_NONE) {
+ return;
+ }
+ if (!$view->config->show_footer_message) {
+ $view->config->show_footer_message = '';
+ }
+ $anonymizer = StaticContainer::get(ReferrerAnonymizer::class);
+ $methods = $anonymizer->getAvailableAnonymizationOptions();
+ if (!empty($methods[$config->anonymizeReferrer])) {
+ $view->config->show_footer_message .= Piwik::translate('PrivacyManager_InfoSomeReferrerInfoMayBeAnonymized', $methods[$config->anonymizeReferrer]);
+ }
+ }
+ }
+
/**
* Register the new tables, so Matomo knows about them.
*
diff --git a/plugins/PrivacyManager/ReferrerAnonymizer.php b/plugins/PrivacyManager/ReferrerAnonymizer.php
new file mode 100644
index 0000000000..c27b6be252
--- /dev/null
+++ b/plugins/PrivacyManager/ReferrerAnonymizer.php
@@ -0,0 +1,113 @@
+<?php
+/**
+ * Matomo - free/libre analytics platform
+ *
+ * @link https://matomo.org
+ * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
+ *
+ */
+namespace Piwik\Plugins\PrivacyManager;
+
+use Piwik\Common;
+use Piwik\Piwik;
+use Piwik\Plugins\PrivacyManager\Config as PrivacyManagerConfig;
+
+class ReferrerAnonymizer
+{
+ const EXCLUDE_QUERY = 'exclude_query';
+ const EXCLUDE_PATH = 'exclude_path';
+ const EXCLUDE_ALL = 'exclude_all';
+ const EXCLUDE_NONE = '';
+
+ public function getAvailableAnonymizationOptions()
+ {
+ return array(
+ self::EXCLUDE_NONE => Piwik::translate('PrivacyManager_AnonymizeReferrerExcludeNone'),
+ self::EXCLUDE_QUERY => Piwik::translate('PrivacyManager_AnonymizeReferrerExcludeQuery'),
+ self::EXCLUDE_PATH => Piwik::translate('PrivacyManager_AnonymizeReferrerExcludePath'),
+ self::EXCLUDE_ALL => Piwik::translate('PrivacyManager_AnonymizeReferrerExcludeAll')
+ // but try to track the type still
+ );
+ }
+
+ // referer_keyword: searched keyword or campaign keyword
+ public function anonymiseReferrerKeyword($keyword, $referrerType, $anonymizeOption)
+ {
+ if ($anonymizeOption == self::EXCLUDE_NONE) {
+ return $keyword; // default, nothing to anonymise
+ }
+
+ if ($referrerType == Common::REFERRER_TYPE_CAMPAIGN) {
+ return $keyword; // we always want to keep the keyword since it is from the viewed page url, not the referrer
+ }
+
+ if ($anonymizeOption == self::EXCLUDE_ALL) {
+ return '';
+ }
+
+ if (in_array($anonymizeOption, [self::EXCLUDE_QUERY, self::EXCLUDE_PATH])) {
+ return ''; // the keyword should have not been detected
+ }
+ return $keyword;
+ }
+
+ // referer_name: eg referer host of website or campaign name or search engine name or social network name
+ public function anonymiseReferrerName($name, $referrerType, $anonymizeOption)
+ {
+ if ($anonymizeOption == self::EXCLUDE_NONE) {
+ return $name; // default, nothing to anonymise
+ }
+
+ if ($referrerType == Common::REFERRER_TYPE_CAMPAIGN) {
+ return $name; // we always want to keep the keyword since it is from the viewed page url, not the referrer
+ }
+
+ if ($referrerType == Common::REFERRER_TYPE_SOCIAL_NETWORK || $referrerType == Common::REFERRER_TYPE_SEARCH_ENGINE) {
+ return $name; // we also keep the name of the social network or search engine since it should not be personal information
+ }
+
+ if ($anonymizeOption == self::EXCLUDE_ALL) {
+ // mostly for website referrers we unset it
+ return '';
+ }
+
+ // I don't think it should be anonymised further because it would only store the hostname anyway...
+ // so if website is being used and no matter if query or path should be anonymised it should be fine to keep the value
+ return $name;
+ }
+
+ public function anonymiseReferrerUrl($url, $anonymizeOption)
+ {
+ if ($anonymizeOption === self::EXCLUDE_NONE) {
+ return $url;
+ }
+
+ if (!is_string($url) && !is_numeric($url)) {
+ return $url;
+ }
+
+ switch ($anonymizeOption) {
+ case self::EXCLUDE_QUERY:
+ $url = strtok($url, '?');
+ break;
+ case self::EXCLUDE_PATH:
+ $urlParts = @parse_url($url);
+ if (!empty($urlParts['host']) && !empty($urlParts['path'])) {
+ $scheme = $urlParts['scheme'] ?? '';
+ if ($scheme) {
+ $scheme .= '://';
+ } elseif (strpos($url, '//') === 0) {
+ $scheme = '//';
+ }
+ $url = $scheme . $urlParts['host'] . '/';
+ }
+ break;
+ case self::EXCLUDE_ALL:
+ $url = '';
+ break;
+ }
+
+ return $url;
+ }
+
+}
diff --git a/plugins/PrivacyManager/Tracker/RequestProcessor.php b/plugins/PrivacyManager/Tracker/RequestProcessor.php
index bed2d02e3a..a5c0e37c7e 100644
--- a/plugins/PrivacyManager/Tracker/RequestProcessor.php
+++ b/plugins/PrivacyManager/Tracker/RequestProcessor.php
@@ -10,18 +10,27 @@ namespace Piwik\Plugins\PrivacyManager\Tracker;
use Piwik\Common;
use Piwik\Plugins\PrivacyManager\PrivacyManager;
+use Piwik\Plugins\PrivacyManager\ReferrerAnonymizer;
use Piwik\SettingsPiwik;
use Piwik\Tracker\Request;
use Piwik\Tracker;
use Piwik\Plugins\PrivacyManager\Config as PrivacyManagerConfig;
+use Piwik\Tracker\Visit\VisitProperties;
class RequestProcessor extends Tracker\RequestProcessor
{
- public function manipulateRequest(Request $request)
+ private $config;
+ private $referrerAnonymizer;
+
+ public function __construct(PrivacyManagerConfig $config, ReferrerAnonymizer $referrerAnonymizer)
{
- $privacyConfig = new PrivacyManagerConfig();
+ $this->config = $config;
+ $this->referrerAnonymizer = $referrerAnonymizer;
+ }
- if ($privacyConfig->anonymizeUserId) {
+ public function manipulateRequest(Request $request)
+ {
+ if ($this->config->anonymizeUserId) {
$userId = $request->getParam('uid');
if ($this->isValueSet($userId)) {
$userIdAnonymized = self::anonymizeUserId($userId);
@@ -29,7 +38,7 @@ class RequestProcessor extends Tracker\RequestProcessor
}
}
- if ($privacyConfig->anonymizeOrderId) {
+ if ($this->config->anonymizeOrderId) {
$orderId = $request->getParam('ec_id');
if ($this->isValueSet($orderId)) {
$orderIdAnonymized = sha1(Common::getRandomInt() . $orderId . time() . SettingsPiwik::getSalt());
@@ -38,6 +47,46 @@ class RequestProcessor extends Tracker\RequestProcessor
}
}
+ public function onNewVisit(VisitProperties $visitProperties, Request $request)
+ {
+ $type = $visitProperties->getProperty('referer_type');
+
+ // we do not anonymise the referrer url in manipulateRequest because otherwise the referrer would not be detected
+ // correctly
+ $url = $visitProperties->getProperty('referer_url');
+ $url = $this->referrerAnonymizer->anonymiseReferrerUrl($url, $this->config->anonymizeReferrer);
+ $visitProperties->setProperty('referer_url', $url);
+
+ $name = $visitProperties->getProperty('referer_name');
+ $name = $this->referrerAnonymizer->anonymiseReferrerName($name, $type, $this->config->anonymizeReferrer);
+ $visitProperties->setProperty('referer_name', $name);
+
+ $keyword = $visitProperties->getProperty('referer_keyword');
+ $keyword = $this->referrerAnonymizer->anonymiseReferrerKeyword($keyword, $type, $this->config->anonymizeReferrer);
+ $visitProperties->setProperty('referer_keyword', $keyword);
+ }
+
+ public function onExistingVisit(&$valuesToUpdate, VisitProperties $visitProperties, Request $request)
+ {
+ if (isset($valuesToUpdate['referer_type'])){
+ $type = $valuesToUpdate['referer_type'];
+ } else {
+ $type = $visitProperties->getProperty('referer_type');
+ }
+
+ if (isset($valuesToUpdate['referer_url'])) {
+ $valuesToUpdate['referer_url'] = $this->referrerAnonymizer->anonymiseReferrerUrl($valuesToUpdate['referer_url'], $this->config->anonymizeReferrer);
+ }
+
+ if (isset($valuesToUpdate['referer_name'])) {
+ $valuesToUpdate['referer_name'] = $this->referrerAnonymizer->anonymiseReferrerName($valuesToUpdate['referer_name'], $type, $this->config->anonymizeReferrer);
+ }
+
+ if (isset($valuesToUpdate['referer_keyword'])) {
+ $valuesToUpdate['referer_keyword'] = $this->referrerAnonymizer->anonymiseReferrerKeyword($valuesToUpdate['referer_keyword'], $type, $this->config->anonymizeReferrer);
+ }
+ }
+
/**
* pseudo anonymization as we need to make sure to always generate the same UserId for the same original UserID
*
diff --git a/plugins/PrivacyManager/angularjs/anonymize-ip/anonymize-ip.controller.js b/plugins/PrivacyManager/angularjs/anonymize-ip/anonymize-ip.controller.js
index b182185d75..27739a0ccd 100644
--- a/plugins/PrivacyManager/angularjs/anonymize-ip/anonymize-ip.controller.js
+++ b/plugins/PrivacyManager/angularjs/anonymize-ip/anonymize-ip.controller.js
@@ -22,6 +22,7 @@
anonymizeIPEnable: this.enabled ? '1' : '0',
anonymizeUserId: this.anonymizeUserId ? '1' : '0',
anonymizeOrderId: this.anonymizeOrderId ? '1' : '0',
+ anonymizeReferrer: this.anonymizeReferrer ? this.anonymizeReferrer : '',
maskLength: this.maskLength,
useAnonymizedIpForVisitEnrichment: parseInt(this.useAnonymizedIpForVisitEnrichment, 10) ? '1' : '0'
}).then(function (success) {
diff --git a/plugins/PrivacyManager/config/config.php b/plugins/PrivacyManager/config/config.php
index d266508bcd..60d6392c12 100644
--- a/plugins/PrivacyManager/config/config.php
+++ b/plugins/PrivacyManager/config/config.php
@@ -1,2 +1,7 @@
<?php
-return array();
+return array(
+
+ 'diagnostics.informational' => DI\add(array(
+ DI\get('Piwik\Plugins\PrivacyManager\Diagnostic\PrivacyInformational'),
+ )),
+);
diff --git a/plugins/PrivacyManager/lang/en.json b/plugins/PrivacyManager/lang/en.json
index 0811d2f9fc..6b013147bb 100644
--- a/plugins/PrivacyManager/lang/en.json
+++ b/plugins/PrivacyManager/lang/en.json
@@ -6,6 +6,8 @@
"AnonymizeIpExtendedHelp": "When users visit your website, Matomo will not use the full IP address (such as %1$s) but instead Matomo will anonymize it first (to %2$s). IP address anonymisation is one of the requirements set by the privacy laws in some countries such as Germany.",
"AnonymizeIpMaskLengtDescription": "Select how many bytes of the visitors' IPs should be masked.",
"AnonymizeIpMaskLength": "%1$s byte(s) - e.g. %2$s",
+ "AnonymizeReferrer": "Anonymize Referrer",
+ "AnonymizeReferrerNote": "Matomo stores from which URL (referrer) a user comes to your site. In some cases such a URL may contain information that can be considered personal information. If you want to prevent such information to be tracked you can restrict the amount of referrer data that Matomo will store when a visitor enters your website. The more information is removed from the referrer information, the less likely personal information will be recorded. The more information you remove from the referrer, the less clear it will be how a visitor came to your website.",
"AskingForConsent": "Asking for consent",
"ConsentExplanation": "Consent means offering individuals choice and control about how their personal data is processed.",
"ClickHereSettings": "Click here to access the %s settings.",
@@ -17,6 +19,10 @@
"AnonymizeSites": "Anonymize the data of this site(s)",
"AnonymizeRowDataFrom": "Anonymize all raw data starting from:",
"AnonymizeRowDataTo": "Anonymize all raw data up to:",
+ "AnonymizeReferrerExcludeNone": "Don't anonymize the referrer",
+ "AnonymizeReferrerExcludeQuery": "Remove query parameters from referrer URL",
+ "AnonymizeReferrerExcludePath": "Keep only the domain of a referrer URL",
+ "AnonymizeReferrerExcludeAll": "Don't record the referrer URL but still detect the type of referrer",
"AnonymizeIp": "Anonymize IP",
"AnonymizeIpHelp": "This action cannot be undone. If enabled, for all visits during this duration the IP will be anonymized by at least 2 bytes, for example '192.168.xxx.xxx'. If you have currently configured to anonymize by 3 bytes, then this setting will be respected and all IPs will be anonymized by 3 bytes.",
"AnonymizeLocation": "Anonymize Location",
@@ -60,6 +66,7 @@
"DeleteReportsConfirm": "You are about to enable report data deletion. If old reports are removed, you will have to re-process them in order view them. Are you sure you want to do this?",
"DeleteAggregateReportsDetailedInfo": "When you enable this setting, all aggregated reports will be deleted. Aggregated reports are generated from the raw data and represent aggregated data from several individual visits. For example, the \"Country\" report lists aggregated numbers to see how many visits you have gotten from each country.",
"KeepBasicMetricsReportsDetailedInfo": "When you enable this setting, some numeric key performance indicators won't be deleted.",
+ "InfoSomeReferrerInfoMayBeAnonymized": "Some referrer information may be missing because referrer anonymisation is enabled as following: %s",
"DeleteReportsInfo2": "If you delete old reports, they may be re-processed again from your RAW data when you request them.",
"DeleteReportsInfo3": "If you have also enabled \"%s\", then the reports data you're deleting will be permanently lost.",
"DeleteReportsOlderThan": "Delete reports older than",
diff --git a/plugins/PrivacyManager/templates/privacySettings.twig b/plugins/PrivacyManager/templates/privacySettings.twig
index abeecc782e..8c5515578a 100644
--- a/plugins/PrivacyManager/templates/privacySettings.twig
+++ b/plugins/PrivacyManager/templates/privacySettings.twig
@@ -56,6 +56,14 @@
inline-help="{{ 'PrivacyManager_AnonymizeOrderIdNote'|translate|e('html_attr') }}">
</div>
+ <div piwik-field uicontrol="select" name="anonymizeReferrer"
+ ng-model="anonymizeIp.anonymizeReferrer"
+ options='{{ referrerAnonymizationOptions|json_encode|e('html_attr') }}'
+ data-title="{{ 'PrivacyManager_AnonymizeReferrer'|translate|e('html_attr') }}"
+ value="{{ anonymizeIP.anonymizeReferrer }}"
+ inline-help="{{ 'PrivacyManager_AnonymizeReferrerNote'|translate|e('html_attr') }}">
+ </div>
+
<div piwik-save-button onconfirm="anonymizeIp.save()" saving="anonymizeIp.isLoading"></div>
</div>
</div>
diff --git a/plugins/PrivacyManager/tests/Fixtures/FewVisitsAnonymizedFixture.php b/plugins/PrivacyManager/tests/Fixtures/FewVisitsAnonymizedFixture.php
index 50ea4b1264..fa58ccf360 100644
--- a/plugins/PrivacyManager/tests/Fixtures/FewVisitsAnonymizedFixture.php
+++ b/plugins/PrivacyManager/tests/Fixtures/FewVisitsAnonymizedFixture.php
@@ -11,6 +11,7 @@ use Piwik\Date;
use Piwik\Option;
use Piwik\Plugins\PrivacyManager\Config;
use Piwik\Plugins\PrivacyManager\PrivacyManager;
+use Piwik\Plugins\PrivacyManager\ReferrerAnonymizer;
use Piwik\Tests\Framework\Fixture;
use Piwik\Tracker\Cache;
@@ -28,6 +29,12 @@ class FewVisitsAnonymizedFixture extends Fixture
$this->setUpWebsite();
$this->trackAnonymizedUserId();
$this->trackAnonymizedOrderId();
+ $this->trackAnonymizedReferrerExcludeAllSearch();
+ $this->trackAnonymizedReferrerExcludeAllWebsite();
+ $this->trackAnonymizedReferrerExcludePathWebsite();
+ $this->trackAnonymizedReferrerExcludeQuerySocial();
+ $this->trackAnonymizedReferrerExcludeAllSocial();
+ $this->trackAnonymizedReferrerExcludeAllCampaign();
}
public function tearDown(): void
@@ -72,4 +79,78 @@ class FewVisitsAnonymizedFixture extends Fixture
$t->doTrackEcommerceOrder('myorderid', 10, 7, 2, 1, 0);
}
+ protected function trackAnonymizedReferrerExcludeAllWebsite()
+ {
+ $this->getPrivacyConfig()->anonymizeReferrer = ReferrerAnonymizer::EXCLUDE_ALL;
+
+ $t = self::getTracker($this->idSite, $this->dateTime, $defaultInit = true);
+ $t->setIp('56.11.55.74');
+ $t->setForceVisitDateTime(Date::factory($this->dateTime)->addHour(0.1)->getDatetime());
+ $t->setUrlReferrer('https://www.foo.com/bar/?baz=exclude_all');
+ $t->setUrl('http://example.com/exclude_all');
+ self::checkResponse($t->doTrackPageView('Exclude all referrer website'));
+
+ }
+
+ protected function trackAnonymizedReferrerExcludePathWebsite()
+ {
+ $this->getPrivacyConfig()->anonymizeReferrer = ReferrerAnonymizer::EXCLUDE_PATH;
+
+ $t = self::getTracker($this->idSite, $this->dateTime, $defaultInit = true);
+ $t->setIp('56.11.55.75');
+ $t->setForceVisitDateTime(Date::factory($this->dateTime)->addHour(0.1)->getDatetime());
+ $t->setUrlReferrer('https://www.foo.com/bar/?baz=exclude_path_website');
+ $t->setUrl('http://example.com/exclude_path_website');
+ self::checkResponse($t->doTrackPageView('Exclude path website'));
+
+ }
+
+ protected function trackAnonymizedReferrerExcludeAllSearch()
+ {
+ $this->getPrivacyConfig()->anonymizeReferrer = ReferrerAnonymizer::EXCLUDE_ALL;
+
+ $t = self::getTracker($this->idSite, $this->dateTime, $defaultInit = true);
+ $t->setIp('56.11.55.76');
+ $t->setForceVisitDateTime(Date::factory($this->dateTime)->addHour(0.1)->getDatetime());
+ $t->setUrlReferrer('http://google.com/search?q=exclude_all_search');
+ $t->setUrl('http://example.com/exclude_all_search');
+ self::checkResponse($t->doTrackPageView('Exclude all search'));
+ }
+
+ protected function trackAnonymizedReferrerExcludeQuerySocial()
+ {
+ $this->getPrivacyConfig()->anonymizeReferrer = ReferrerAnonymizer::EXCLUDE_QUERY;
+
+ $t = self::getTracker($this->idSite, $this->dateTime, $defaultInit = true);
+ $t->setIp('56.11.55.77');
+ $t->setForceVisitDateTime(Date::factory($this->dateTime)->addHour(0.1)->getDatetime());
+ $t->setUrlReferrer('https://www.facebook.com/profile?id=exclude_query_social');
+ $t->setUrl('http://example.com/exclude_query_social');
+ self::checkResponse($t->doTrackPageView('Exclude query social'));
+ }
+
+ protected function trackAnonymizedReferrerExcludeAllSocial()
+ {
+ $this->getPrivacyConfig()->anonymizeReferrer = ReferrerAnonymizer::EXCLUDE_ALL;
+
+ $t = self::getTracker($this->idSite, $this->dateTime, $defaultInit = true);
+ $t->setIp('56.11.55.78');
+ $t->setForceVisitDateTime(Date::factory($this->dateTime)->addHour(0.1)->getDatetime());
+ $t->setUrlReferrer('https://www.facebook.com/profile?id=exclude_query_social');
+ $t->setUrl('http://example.com/exclude_query_social');
+ self::checkResponse($t->doTrackPageView('Exclude query social'));
+ }
+
+ protected function trackAnonymizedReferrerExcludeAllCampaign()
+ {
+ $this->getPrivacyConfig()->anonymizeReferrer = ReferrerAnonymizer::EXCLUDE_ALL;
+
+ $t = self::getTracker($this->idSite, $this->dateTime, $defaultInit = true);
+ $t->setIp('56.11.55.78');
+ $t->setForceVisitDateTime(Date::factory($this->dateTime)->addHour(0.1)->getDatetime());
+ $t->setUrlReferrer('https://www.example.com/exclude_all_campaign');
+ $t->setUrl('http://example.com/exclude_query_social?mtm_kwd=campaignkeyword&mtm_campaign=campaign');
+ self::checkResponse($t->doTrackPageView('Exclude query social'));
+ }
+
} \ No newline at end of file
diff --git a/plugins/PrivacyManager/tests/Integration/PrivacyManagerConfigTest.php b/plugins/PrivacyManager/tests/Integration/PrivacyManagerConfigTest.php
index 183bf27c0d..43527d09aa 100644
--- a/plugins/PrivacyManager/tests/Integration/PrivacyManagerConfigTest.php
+++ b/plugins/PrivacyManager/tests/Integration/PrivacyManagerConfigTest.php
@@ -10,6 +10,7 @@ namespace Piwik\Plugins\PrivacyManager\tests;
use Piwik\Option;
use Piwik\Plugins\PrivacyManager\Config as PrivacyManagerConfig;
+use Piwik\Plugins\PrivacyManager\ReferrerAnonymizer;
use Piwik\Tests\Framework\TestCase\IntegrationTestCase;
/**
@@ -91,6 +92,15 @@ class PrivacyManagerConfigTest extends IntegrationTestCase
$this->assertTrue($this->config->anonymizeUserId);
}
+ public function test_anonymizeReferrer()
+ {
+ $this->assertSame('', $this->config->anonymizeReferrer);
+
+ $this->config->anonymizeReferrer = ReferrerAnonymizer::EXCLUDE_PATH;
+
+ $this->assertSame(ReferrerAnonymizer::EXCLUDE_PATH, $this->config->anonymizeReferrer);
+ }
+
public function test_setTrackerCacheContent()
{
$content = $this->config->setTrackerCacheGeneral(array('existingEntry' => 'test'));
@@ -102,6 +112,7 @@ class PrivacyManagerConfigTest extends IntegrationTestCase
'PrivacyManager.doNotTrackEnabled' => true,
'PrivacyManager.anonymizeUserId' => false,
'PrivacyManager.anonymizeOrderId' => false,
+ 'PrivacyManager.anonymizeReferrer' => '',
'PrivacyManager.useAnonymizedIpForVisitEnrichment' => false,
);
diff --git a/plugins/PrivacyManager/tests/Integration/Tracker/RequestProcessorTest.php b/plugins/PrivacyManager/tests/Integration/Tracker/RequestProcessorTest.php
index a820e2cdf4..72b2a09107 100644
--- a/plugins/PrivacyManager/tests/Integration/Tracker/RequestProcessorTest.php
+++ b/plugins/PrivacyManager/tests/Integration/Tracker/RequestProcessorTest.php
@@ -8,14 +8,17 @@
namespace Piwik\Plugins\PrivacyManager\tests\Integration\Tracker;
+use Piwik\Common;
use Piwik\Option;
use Piwik\Plugins\PrivacyManager\Config;
use Piwik\Plugins\PrivacyManager\PrivacyManager;
+use Piwik\Plugins\PrivacyManager\ReferrerAnonymizer;
use Piwik\Plugins\PrivacyManager\Tracker\RequestProcessor;
use Piwik\Tests\Framework\Fixture;
use Piwik\Tests\Framework\TestCase\IntegrationTestCase;
use Piwik\Tracker\Cache;
use Piwik\Tracker\Request;
+use Piwik\Tracker\Visit\VisitProperties;
/**
@@ -43,14 +46,120 @@ class RequestProcessorTest extends IntegrationTestCase
Option::set(PrivacyManager::OPTION_USERID_SALT, 'simpleuseridsalt1');
Cache::clearCacheGeneral();
- $this->requestProcessor = new RequestProcessor();
+ $anonimiser = new ReferrerAnonymizer();
+
$this->config = new Config();
+ $this->requestProcessor = new RequestProcessor($this->config, $anonimiser);
for ($i = 0; $i < 3; $i++) {
Fixture::createWebsite('2014-01-01 02:03:04');
}
}
+ public function test_onNewVisit_anonymiseReferrer_byDefaultNothingAnonymised()
+ {
+ $visit =$this->makeReferrerVisitProperties(Common::REFERRER_TYPE_WEBSITE, 'https://www.foo.com/path/?bar=baz', 'foo.com', 'barbaz');
+ $request = $this->makeRequest([]);
+ $this->requestProcessor->onNewVisit($visit, $request);
+
+ $this->assertVisitProperties($visit, Common::REFERRER_TYPE_WEBSITE, 'https://www.foo.com/path/?bar=baz', 'foo.com', 'barbaz');
+ }
+
+ public function test_onNewVisit_anonymiseReferrer_byPath()
+ {
+ $this->config->anonymizeReferrer = ReferrerAnonymizer::EXCLUDE_PATH;
+ $visit =$this->makeReferrerVisitProperties(Common::REFERRER_TYPE_WEBSITE, 'https://www.foo.com/path/?bar=baz', 'barbaz', 'foo.com');
+ $request = $this->makeRequest([]);
+ $this->requestProcessor->onNewVisit($visit, $request);
+
+ $this->assertVisitProperties($visit, Common::REFERRER_TYPE_WEBSITE, 'https://www.foo.com/', '', 'foo.com');
+ }
+
+ public function test_onNewVisit_anonymiseReferrer_website_excludeAll()
+ {
+ $this->config->anonymizeReferrer = ReferrerAnonymizer::EXCLUDE_ALL;
+ $visit =$this->makeReferrerVisitProperties(Common::REFERRER_TYPE_WEBSITE, 'https://www.foo.com/path/?bar=baz', 'foo.com', 'barbaz');
+ $request = $this->makeRequest([]);
+ $this->requestProcessor->onNewVisit($visit, $request);
+
+ $this->assertVisitProperties($visit, Common::REFERRER_TYPE_WEBSITE, '', '', '');
+ }
+
+ public function test_onNewVisit_anonymiseReferrer_search_ExcludeAll()
+ {
+ $this->config->anonymizeReferrer = ReferrerAnonymizer::EXCLUDE_ALL;
+ $visit =$this->makeReferrerVisitProperties(Common::REFERRER_TYPE_SEARCH_ENGINE, 'https://www.foo.com/path/?bar=baz', 'foo.com', 'barbaz');
+ $request = $this->makeRequest([]);
+ $this->requestProcessor->onNewVisit($visit, $request);
+
+ $this->assertVisitProperties($visit, Common::REFERRER_TYPE_SEARCH_ENGINE, '', '', 'barbaz');
+ }
+
+ public function test_onExistingVisit_anonymiseReferrer_byDefaultNothingAnonymised()
+ {
+ $this->config->anonymizeReferrer = ReferrerAnonymizer::EXCLUDE_NONE;
+ $visit = $this->makeReferrerVisitProperties(Common::REFERRER_TYPE_WEBSITE, 'https://www.foo.com/path/?bar=baz', 'foo.com', 'barbaz');
+ $request = $this->makeRequest([]);
+ $update = $visit->getProperties();
+ $this->requestProcessor->onExistingVisit($update, $visit, $request);
+
+ $this->assertVisitProperties($update, Common::REFERRER_TYPE_WEBSITE, 'https://www.foo.com/path/?bar=baz', 'foo.com', 'barbaz');
+ }
+
+ public function test_onExistingVisit_anonymiseReferrer_byPath()
+ {
+ $this->config->anonymizeReferrer = ReferrerAnonymizer::EXCLUDE_PATH;
+ $visit =$this->makeReferrerVisitProperties(Common::REFERRER_TYPE_WEBSITE, 'https://www.foo.com/path/?bar=baz', 'barbaz', 'foo.com');
+ $request = $this->makeRequest([]);
+ $update = $visit->getProperties();
+ $this->requestProcessor->onExistingVisit($update, $visit, $request);
+
+ $this->assertVisitProperties($update, Common::REFERRER_TYPE_WEBSITE, 'https://www.foo.com/', '', 'foo.com');
+ }
+
+ public function test_onExistingVisit_anonymiseReferrer_website_excludeAll()
+ {
+ $this->config->anonymizeReferrer = ReferrerAnonymizer::EXCLUDE_ALL;
+ $visit =$this->makeReferrerVisitProperties(Common::REFERRER_TYPE_WEBSITE, 'https://www.foo.com/path/?bar=baz', 'foo.com', 'barbaz');
+ $request = $this->makeRequest([]);
+ $update = $visit->getProperties();
+ $this->requestProcessor->onExistingVisit($update, $visit, $request);
+
+ $this->assertVisitProperties($update, Common::REFERRER_TYPE_WEBSITE, '', '', '');
+ }
+
+ public function test_onExistingVisit_anonymiseReferrer_search_ExcludeAll()
+ {
+ $this->config->anonymizeReferrer = ReferrerAnonymizer::EXCLUDE_ALL;
+ $visit =$this->makeReferrerVisitProperties(Common::REFERRER_TYPE_SEARCH_ENGINE, 'https://www.foo.com/path/?bar=baz', 'foo.com', 'barbaz');
+ $request = $this->makeRequest([]);
+ $update = $visit->getProperties();
+ $this->requestProcessor->onExistingVisit($update, $visit, $request);
+
+ $this->assertVisitProperties($update, Common::REFERRER_TYPE_SEARCH_ENGINE, '', '', 'barbaz');
+ }
+
+ private function assertVisitProperties($visit, $expectedType, $expectedUrl, $expectedKeyword, $exectedName)
+ {
+ if (is_array($visit)) {
+ $visit = new VisitProperties($visit);
+ }
+ $this->assertEquals($expectedType, $visit->getProperty('referer_type'));
+ $this->assertEquals($expectedUrl, $visit->getProperty('referer_url'));
+ $this->assertEquals($expectedKeyword, $visit->getProperty('referer_keyword'));
+ $this->assertEquals($exectedName, $visit->getProperty('referer_name'));
+ }
+
+ private function makeReferrerVisitProperties($type, $url, $keyword, $name)
+ {
+ $visit = new VisitProperties();
+ $visit->setProperty('referer_type', $type);
+ $visit->setProperty('referer_url', $url);
+ $visit->setProperty('referer_keyword', $keyword);
+ $visit->setProperty('referer_name', $name);
+ return $visit;
+ }
+
public function test_manipulateRequest_enabledButNoUserIdNorOrderIdSet()
{
$this->config->anonymizeUserId = true;
diff --git a/plugins/PrivacyManager/tests/System/AnonymizationTest.php b/plugins/PrivacyManager/tests/System/AnonymizationTest.php
index f52d9ab461..23ff1997d1 100644
--- a/plugins/PrivacyManager/tests/System/AnonymizationTest.php
+++ b/plugins/PrivacyManager/tests/System/AnonymizationTest.php
@@ -56,7 +56,7 @@ class AnonymizationTest extends SystemTestCase
public function getApiForTesting()
{
$apiToTest = array();
- $apiToTest[] = array(array('Live.getLastVisitsDetails'),
+ $apiToTest[] = array(array('Live.getLastVisitsDetails', 'Referrers.getAll'),
array(
'idSite' => self::$fixture->idSite,
'date' => self::$fixture->dateTime,
diff --git a/plugins/PrivacyManager/tests/System/expected/test_userIdAnonymized__Live.getLastVisitsDetails_year.xml b/plugins/PrivacyManager/tests/System/expected/test_userIdAnonymized__Live.getLastVisitsDetails_year.xml
index 279a28911c..dadf039e03 100644
--- a/plugins/PrivacyManager/tests/System/expected/test_userIdAnonymized__Live.getLastVisitsDetails_year.xml
+++ b/plugins/PrivacyManager/tests/System/expected/test_userIdAnonymized__Live.getLastVisitsDetails_year.xml
@@ -216,4 +216,652 @@
<customVariables>
</customVariables>
</row>
+ <row>
+ <idSite>1</idSite>
+ <idVisit>3</idVisit>
+ <visitIp>56.11.55.76</visitIp>
+
+ <fingerprint>d2368130580d0b14</fingerprint>
+ <actionDetails>
+ </actionDetails>
+ <goalConversions>0</goalConversions>
+ <siteCurrency>USD</siteCurrency>
+ <siteCurrencySymbol>$</siteCurrencySymbol>
+
+
+
+
+ <siteName>Piwik test</siteName>
+
+
+
+
+
+
+ <userId />
+ <visitorType>new</visitorType>
+ <visitorTypeIcon />
+ <visitConverted>0</visitConverted>
+ <visitConvertedIcon />
+ <visitCount>1</visitCount>
+ <visitEcommerceStatus>none</visitEcommerceStatus>
+ <visitEcommerceStatusIcon />
+ <daysSinceFirstVisit>0</daysSinceFirstVisit>
+ <secondsSinceFirstVisit>0</secondsSinceFirstVisit>
+ <daysSinceLastEcommerceOrder>0</daysSinceLastEcommerceOrder>
+ <secondsSinceLastEcommerceOrder />
+ <visitDuration>0</visitDuration>
+ <visitDurationPretty>0s</visitDurationPretty>
+ <searches>0</searches>
+ <actions>1</actions>
+ <interactions>1</interactions>
+ <referrerType>search</referrerType>
+ <referrerTypeName>Search Engines</referrerTypeName>
+ <referrerName>Google</referrerName>
+ <referrerKeyword>Keyword not defined</referrerKeyword>
+ <referrerKeywordPosition />
+ <referrerUrl />
+ <referrerSearchEngineUrl>http://google.com</referrerSearchEngineUrl>
+ <referrerSearchEngineIcon>plugins/Morpheus/icons/dist/searchEngines/google.com.png</referrerSearchEngineIcon>
+ <referrerSocialNetworkUrl />
+ <referrerSocialNetworkIcon />
+ <languageCode>fr</languageCode>
+ <language>French</language>
+ <deviceType>Desktop</deviceType>
+ <deviceTypeIcon>plugins/Morpheus/icons/dist/devices/desktop.png</deviceTypeIcon>
+ <deviceBrand>Unknown</deviceBrand>
+ <deviceModel>Generic Desktop</deviceModel>
+ <operatingSystem>Windows XP</operatingSystem>
+ <operatingSystemName>Windows</operatingSystemName>
+ <operatingSystemIcon>plugins/Morpheus/icons/dist/os/WIN.png</operatingSystemIcon>
+ <operatingSystemCode>WIN</operatingSystemCode>
+ <operatingSystemVersion>XP</operatingSystemVersion>
+ <browserFamily>Gecko</browserFamily>
+ <browserFamilyDescription>Gecko (Firefox)</browserFamilyDescription>
+ <browser>Firefox 3.6</browser>
+ <browserName>Firefox</browserName>
+ <browserIcon>plugins/Morpheus/icons/dist/browsers/FF.png</browserIcon>
+ <browserCode>FF</browserCode>
+ <browserVersion>3.6</browserVersion>
+ <totalEcommerceRevenue>0</totalEcommerceRevenue>
+ <totalEcommerceConversions>0</totalEcommerceConversions>
+ <totalEcommerceItems>0</totalEcommerceItems>
+ <totalAbandonedCartsRevenue>0</totalAbandonedCartsRevenue>
+ <totalAbandonedCarts>0</totalAbandonedCarts>
+ <totalAbandonedCartsItems>0</totalAbandonedCartsItems>
+ <events>0</events>
+ <continent>Europe</continent>
+ <continentCode>eur</continentCode>
+ <country>France</country>
+ <countryCode>fr</countryCode>
+ <countryFlag>plugins/Morpheus/icons/dist/flags/fr.png</countryFlag>
+ <region />
+ <regionCode />
+ <city />
+ <location>France</location>
+ <latitude />
+ <longitude />
+ <visitLocalTime>12:34:06</visitLocalTime>
+ <visitLocalHour>12</visitLocalHour>
+ <daysSinceLastVisit>0</daysSinceLastVisit>
+ <secondsSinceLastVisit>0</secondsSinceLastVisit>
+ <resolution>1024x768</resolution>
+ <plugins>cookie, flash, java</plugins>
+ <pluginsIcons>
+ <row>
+ <pluginIcon>plugins/Morpheus/icons/dist/plugins/cookie.png</pluginIcon>
+ <pluginName>cookie</pluginName>
+ </row>
+ <row>
+ <pluginIcon>plugins/Morpheus/icons/dist/plugins/flash.png</pluginIcon>
+ <pluginName>flash</pluginName>
+ </row>
+ <row>
+ <pluginIcon>plugins/Morpheus/icons/dist/plugins/java.png</pluginIcon>
+ <pluginName>java</pluginName>
+ </row>
+ </pluginsIcons>
+ <customVariables>
+ </customVariables>
+ </row>
+ <row>
+ <idSite>1</idSite>
+ <idVisit>4</idVisit>
+ <visitIp>56.11.55.74</visitIp>
+
+ <fingerprint>5b0ee9ce4097476d</fingerprint>
+ <actionDetails>
+ </actionDetails>
+ <goalConversions>0</goalConversions>
+ <siteCurrency>USD</siteCurrency>
+ <siteCurrencySymbol>$</siteCurrencySymbol>
+
+
+
+
+ <siteName>Piwik test</siteName>
+
+
+
+
+
+
+ <userId />
+ <visitorType>new</visitorType>
+ <visitorTypeIcon />
+ <visitConverted>0</visitConverted>
+ <visitConvertedIcon />
+ <visitCount>1</visitCount>
+ <visitEcommerceStatus>none</visitEcommerceStatus>
+ <visitEcommerceStatusIcon />
+ <daysSinceFirstVisit>0</daysSinceFirstVisit>
+ <secondsSinceFirstVisit>0</secondsSinceFirstVisit>
+ <daysSinceLastEcommerceOrder>0</daysSinceLastEcommerceOrder>
+ <secondsSinceLastEcommerceOrder />
+ <visitDuration>0</visitDuration>
+ <visitDurationPretty>0s</visitDurationPretty>
+ <searches>0</searches>
+ <actions>1</actions>
+ <interactions>1</interactions>
+ <referrerType>website</referrerType>
+ <referrerTypeName>Websites</referrerTypeName>
+ <referrerName />
+ <referrerKeyword />
+ <referrerKeywordPosition />
+ <referrerUrl />
+ <referrerSearchEngineUrl />
+ <referrerSearchEngineIcon />
+ <referrerSocialNetworkUrl />
+ <referrerSocialNetworkIcon />
+ <languageCode>fr</languageCode>
+ <language>French</language>
+ <deviceType>Desktop</deviceType>
+ <deviceTypeIcon>plugins/Morpheus/icons/dist/devices/desktop.png</deviceTypeIcon>
+ <deviceBrand>Unknown</deviceBrand>
+ <deviceModel>Generic Desktop</deviceModel>
+ <operatingSystem>Windows XP</operatingSystem>
+ <operatingSystemName>Windows</operatingSystemName>
+ <operatingSystemIcon>plugins/Morpheus/icons/dist/os/WIN.png</operatingSystemIcon>
+ <operatingSystemCode>WIN</operatingSystemCode>
+ <operatingSystemVersion>XP</operatingSystemVersion>
+ <browserFamily>Gecko</browserFamily>
+ <browserFamilyDescription>Gecko (Firefox)</browserFamilyDescription>
+ <browser>Firefox 3.6</browser>
+ <browserName>Firefox</browserName>
+ <browserIcon>plugins/Morpheus/icons/dist/browsers/FF.png</browserIcon>
+ <browserCode>FF</browserCode>
+ <browserVersion>3.6</browserVersion>
+ <totalEcommerceRevenue>0</totalEcommerceRevenue>
+ <totalEcommerceConversions>0</totalEcommerceConversions>
+ <totalEcommerceItems>0</totalEcommerceItems>
+ <totalAbandonedCartsRevenue>0</totalAbandonedCartsRevenue>
+ <totalAbandonedCarts>0</totalAbandonedCarts>
+ <totalAbandonedCartsItems>0</totalAbandonedCartsItems>
+ <events>0</events>
+ <continent>Europe</continent>
+ <continentCode>eur</continentCode>
+ <country>France</country>
+ <countryCode>fr</countryCode>
+ <countryFlag>plugins/Morpheus/icons/dist/flags/fr.png</countryFlag>
+ <region />
+ <regionCode />
+ <city />
+ <location>France</location>
+ <latitude />
+ <longitude />
+ <visitLocalTime>12:34:06</visitLocalTime>
+ <visitLocalHour>12</visitLocalHour>
+ <daysSinceLastVisit>0</daysSinceLastVisit>
+ <secondsSinceLastVisit>0</secondsSinceLastVisit>
+ <resolution>1024x768</resolution>
+ <plugins>cookie, flash, java</plugins>
+ <pluginsIcons>
+ <row>
+ <pluginIcon>plugins/Morpheus/icons/dist/plugins/cookie.png</pluginIcon>
+ <pluginName>cookie</pluginName>
+ </row>
+ <row>
+ <pluginIcon>plugins/Morpheus/icons/dist/plugins/flash.png</pluginIcon>
+ <pluginName>flash</pluginName>
+ </row>
+ <row>
+ <pluginIcon>plugins/Morpheus/icons/dist/plugins/java.png</pluginIcon>
+ <pluginName>java</pluginName>
+ </row>
+ </pluginsIcons>
+ <customVariables>
+ </customVariables>
+ </row>
+ <row>
+ <idSite>1</idSite>
+ <idVisit>5</idVisit>
+ <visitIp>56.11.55.75</visitIp>
+
+ <fingerprint>b4962044df535273</fingerprint>
+ <actionDetails>
+ </actionDetails>
+ <goalConversions>0</goalConversions>
+ <siteCurrency>USD</siteCurrency>
+ <siteCurrencySymbol>$</siteCurrencySymbol>
+
+
+
+
+ <siteName>Piwik test</siteName>
+
+
+
+
+
+
+ <userId />
+ <visitorType>new</visitorType>
+ <visitorTypeIcon />
+ <visitConverted>0</visitConverted>
+ <visitConvertedIcon />
+ <visitCount>1</visitCount>
+ <visitEcommerceStatus>none</visitEcommerceStatus>
+ <visitEcommerceStatusIcon />
+ <daysSinceFirstVisit>0</daysSinceFirstVisit>
+ <secondsSinceFirstVisit>0</secondsSinceFirstVisit>
+ <daysSinceLastEcommerceOrder>0</daysSinceLastEcommerceOrder>
+ <secondsSinceLastEcommerceOrder />
+ <visitDuration>0</visitDuration>
+ <visitDurationPretty>0s</visitDurationPretty>
+ <searches>0</searches>
+ <actions>1</actions>
+ <interactions>1</interactions>
+ <referrerType>website</referrerType>
+ <referrerTypeName>Websites</referrerTypeName>
+ <referrerName>www.foo.com</referrerName>
+ <referrerKeyword />
+ <referrerKeywordPosition />
+ <referrerUrl>https://www.foo.com/</referrerUrl>
+ <referrerSearchEngineUrl />
+ <referrerSearchEngineIcon />
+ <referrerSocialNetworkUrl />
+ <referrerSocialNetworkIcon />
+ <languageCode>fr</languageCode>
+ <language>French</language>
+ <deviceType>Desktop</deviceType>
+ <deviceTypeIcon>plugins/Morpheus/icons/dist/devices/desktop.png</deviceTypeIcon>
+ <deviceBrand>Unknown</deviceBrand>
+ <deviceModel>Generic Desktop</deviceModel>
+ <operatingSystem>Windows XP</operatingSystem>
+ <operatingSystemName>Windows</operatingSystemName>
+ <operatingSystemIcon>plugins/Morpheus/icons/dist/os/WIN.png</operatingSystemIcon>
+ <operatingSystemCode>WIN</operatingSystemCode>
+ <operatingSystemVersion>XP</operatingSystemVersion>
+ <browserFamily>Gecko</browserFamily>
+ <browserFamilyDescription>Gecko (Firefox)</browserFamilyDescription>
+ <browser>Firefox 3.6</browser>
+ <browserName>Firefox</browserName>
+ <browserIcon>plugins/Morpheus/icons/dist/browsers/FF.png</browserIcon>
+ <browserCode>FF</browserCode>
+ <browserVersion>3.6</browserVersion>
+ <totalEcommerceRevenue>0</totalEcommerceRevenue>
+ <totalEcommerceConversions>0</totalEcommerceConversions>
+ <totalEcommerceItems>0</totalEcommerceItems>
+ <totalAbandonedCartsRevenue>0</totalAbandonedCartsRevenue>
+ <totalAbandonedCarts>0</totalAbandonedCarts>
+ <totalAbandonedCartsItems>0</totalAbandonedCartsItems>
+ <events>0</events>
+ <continent>Europe</continent>
+ <continentCode>eur</continentCode>
+ <country>France</country>
+ <countryCode>fr</countryCode>
+ <countryFlag>plugins/Morpheus/icons/dist/flags/fr.png</countryFlag>
+ <region />
+ <regionCode />
+ <city />
+ <location>France</location>
+ <latitude />
+ <longitude />
+ <visitLocalTime>12:34:06</visitLocalTime>
+ <visitLocalHour>12</visitLocalHour>
+ <daysSinceLastVisit>0</daysSinceLastVisit>
+ <secondsSinceLastVisit>0</secondsSinceLastVisit>
+ <resolution>1024x768</resolution>
+ <plugins>cookie, flash, java</plugins>
+ <pluginsIcons>
+ <row>
+ <pluginIcon>plugins/Morpheus/icons/dist/plugins/cookie.png</pluginIcon>
+ <pluginName>cookie</pluginName>
+ </row>
+ <row>
+ <pluginIcon>plugins/Morpheus/icons/dist/plugins/flash.png</pluginIcon>
+ <pluginName>flash</pluginName>
+ </row>
+ <row>
+ <pluginIcon>plugins/Morpheus/icons/dist/plugins/java.png</pluginIcon>
+ <pluginName>java</pluginName>
+ </row>
+ </pluginsIcons>
+ <customVariables>
+ </customVariables>
+ </row>
+ <row>
+ <idSite>1</idSite>
+ <idVisit>6</idVisit>
+ <visitIp>56.11.55.77</visitIp>
+
+ <fingerprint>485359ba8a966010</fingerprint>
+ <actionDetails>
+ </actionDetails>
+ <goalConversions>0</goalConversions>
+ <siteCurrency>USD</siteCurrency>
+ <siteCurrencySymbol>$</siteCurrencySymbol>
+
+
+
+
+ <siteName>Piwik test</siteName>
+
+
+
+
+
+
+ <userId />
+ <visitorType>new</visitorType>
+ <visitorTypeIcon />
+ <visitConverted>0</visitConverted>
+ <visitConvertedIcon />
+ <visitCount>1</visitCount>
+ <visitEcommerceStatus>none</visitEcommerceStatus>
+ <visitEcommerceStatusIcon />
+ <daysSinceFirstVisit>0</daysSinceFirstVisit>
+ <secondsSinceFirstVisit>0</secondsSinceFirstVisit>
+ <daysSinceLastEcommerceOrder>0</daysSinceLastEcommerceOrder>
+ <secondsSinceLastEcommerceOrder />
+ <visitDuration>0</visitDuration>
+ <visitDurationPretty>0s</visitDurationPretty>
+ <searches>0</searches>
+ <actions>1</actions>
+ <interactions>1</interactions>
+ <referrerType>social</referrerType>
+ <referrerTypeName>Social Networks</referrerTypeName>
+ <referrerName>Facebook</referrerName>
+ <referrerKeyword />
+ <referrerKeywordPosition />
+ <referrerUrl>https://www.facebook.com/profile</referrerUrl>
+ <referrerSearchEngineUrl />
+ <referrerSearchEngineIcon />
+ <referrerSocialNetworkUrl>facebook.com</referrerSocialNetworkUrl>
+ <referrerSocialNetworkIcon>plugins/Morpheus/icons/dist/socials/facebook.com.png</referrerSocialNetworkIcon>
+ <languageCode>fr</languageCode>
+ <language>French</language>
+ <deviceType>Desktop</deviceType>
+ <deviceTypeIcon>plugins/Morpheus/icons/dist/devices/desktop.png</deviceTypeIcon>
+ <deviceBrand>Unknown</deviceBrand>
+ <deviceModel>Generic Desktop</deviceModel>
+ <operatingSystem>Windows XP</operatingSystem>
+ <operatingSystemName>Windows</operatingSystemName>
+ <operatingSystemIcon>plugins/Morpheus/icons/dist/os/WIN.png</operatingSystemIcon>
+ <operatingSystemCode>WIN</operatingSystemCode>
+ <operatingSystemVersion>XP</operatingSystemVersion>
+ <browserFamily>Gecko</browserFamily>
+ <browserFamilyDescription>Gecko (Firefox)</browserFamilyDescription>
+ <browser>Firefox 3.6</browser>
+ <browserName>Firefox</browserName>
+ <browserIcon>plugins/Morpheus/icons/dist/browsers/FF.png</browserIcon>
+ <browserCode>FF</browserCode>
+ <browserVersion>3.6</browserVersion>
+ <totalEcommerceRevenue>0</totalEcommerceRevenue>
+ <totalEcommerceConversions>0</totalEcommerceConversions>
+ <totalEcommerceItems>0</totalEcommerceItems>
+ <totalAbandonedCartsRevenue>0</totalAbandonedCartsRevenue>
+ <totalAbandonedCarts>0</totalAbandonedCarts>
+ <totalAbandonedCartsItems>0</totalAbandonedCartsItems>
+ <events>0</events>
+ <continent>Europe</continent>
+ <continentCode>eur</continentCode>
+ <country>France</country>
+ <countryCode>fr</countryCode>
+ <countryFlag>plugins/Morpheus/icons/dist/flags/fr.png</countryFlag>
+ <region />
+ <regionCode />
+ <city />
+ <location>France</location>
+ <latitude />
+ <longitude />
+ <visitLocalTime>12:34:06</visitLocalTime>
+ <visitLocalHour>12</visitLocalHour>
+ <daysSinceLastVisit>0</daysSinceLastVisit>
+ <secondsSinceLastVisit>0</secondsSinceLastVisit>
+ <resolution>1024x768</resolution>
+ <plugins>cookie, flash, java</plugins>
+ <pluginsIcons>
+ <row>
+ <pluginIcon>plugins/Morpheus/icons/dist/plugins/cookie.png</pluginIcon>
+ <pluginName>cookie</pluginName>
+ </row>
+ <row>
+ <pluginIcon>plugins/Morpheus/icons/dist/plugins/flash.png</pluginIcon>
+ <pluginName>flash</pluginName>
+ </row>
+ <row>
+ <pluginIcon>plugins/Morpheus/icons/dist/plugins/java.png</pluginIcon>
+ <pluginName>java</pluginName>
+ </row>
+ </pluginsIcons>
+ <customVariables>
+ </customVariables>
+ </row>
+ <row>
+ <idSite>1</idSite>
+ <idVisit>7</idVisit>
+ <visitIp>56.11.55.78</visitIp>
+
+ <fingerprint>15cf59ef3a6e6fd6</fingerprint>
+ <actionDetails>
+ </actionDetails>
+ <goalConversions>0</goalConversions>
+ <siteCurrency>USD</siteCurrency>
+ <siteCurrencySymbol>$</siteCurrencySymbol>
+
+
+
+
+ <siteName>Piwik test</siteName>
+
+
+
+
+
+
+ <userId />
+ <visitorType>new</visitorType>
+ <visitorTypeIcon />
+ <visitConverted>0</visitConverted>
+ <visitConvertedIcon />
+ <visitCount>1</visitCount>
+ <visitEcommerceStatus>none</visitEcommerceStatus>
+ <visitEcommerceStatusIcon />
+ <daysSinceFirstVisit>0</daysSinceFirstVisit>
+ <secondsSinceFirstVisit>0</secondsSinceFirstVisit>
+ <daysSinceLastEcommerceOrder>0</daysSinceLastEcommerceOrder>
+ <secondsSinceLastEcommerceOrder />
+ <visitDuration>0</visitDuration>
+ <visitDurationPretty>0s</visitDurationPretty>
+ <searches>0</searches>
+ <actions>1</actions>
+ <interactions>1</interactions>
+ <referrerType>social</referrerType>
+ <referrerTypeName>Social Networks</referrerTypeName>
+ <referrerName>Facebook</referrerName>
+ <referrerKeyword />
+ <referrerKeywordPosition />
+ <referrerUrl />
+ <referrerSearchEngineUrl />
+ <referrerSearchEngineIcon />
+ <referrerSocialNetworkUrl />
+ <referrerSocialNetworkIcon>plugins/Morpheus/icons/dist/socials/xx.png</referrerSocialNetworkIcon>
+ <languageCode>fr</languageCode>
+ <language>French</language>
+ <deviceType>Desktop</deviceType>
+ <deviceTypeIcon>plugins/Morpheus/icons/dist/devices/desktop.png</deviceTypeIcon>
+ <deviceBrand>Unknown</deviceBrand>
+ <deviceModel>Generic Desktop</deviceModel>
+ <operatingSystem>Windows XP</operatingSystem>
+ <operatingSystemName>Windows</operatingSystemName>
+ <operatingSystemIcon>plugins/Morpheus/icons/dist/os/WIN.png</operatingSystemIcon>
+ <operatingSystemCode>WIN</operatingSystemCode>
+ <operatingSystemVersion>XP</operatingSystemVersion>
+ <browserFamily>Gecko</browserFamily>
+ <browserFamilyDescription>Gecko (Firefox)</browserFamilyDescription>
+ <browser>Firefox 3.6</browser>
+ <browserName>Firefox</browserName>
+ <browserIcon>plugins/Morpheus/icons/dist/browsers/FF.png</browserIcon>
+ <browserCode>FF</browserCode>
+ <browserVersion>3.6</browserVersion>
+ <totalEcommerceRevenue>0</totalEcommerceRevenue>
+ <totalEcommerceConversions>0</totalEcommerceConversions>
+ <totalEcommerceItems>0</totalEcommerceItems>
+ <totalAbandonedCartsRevenue>0</totalAbandonedCartsRevenue>
+ <totalAbandonedCarts>0</totalAbandonedCarts>
+ <totalAbandonedCartsItems>0</totalAbandonedCartsItems>
+ <events>0</events>
+ <continent>Europe</continent>
+ <continentCode>eur</continentCode>
+ <country>France</country>
+ <countryCode>fr</countryCode>
+ <countryFlag>plugins/Morpheus/icons/dist/flags/fr.png</countryFlag>
+ <region />
+ <regionCode />
+ <city />
+ <location>France</location>
+ <latitude />
+ <longitude />
+ <visitLocalTime>12:34:06</visitLocalTime>
+ <visitLocalHour>12</visitLocalHour>
+ <daysSinceLastVisit>0</daysSinceLastVisit>
+ <secondsSinceLastVisit>0</secondsSinceLastVisit>
+ <resolution>1024x768</resolution>
+ <plugins>cookie, flash, java</plugins>
+ <pluginsIcons>
+ <row>
+ <pluginIcon>plugins/Morpheus/icons/dist/plugins/cookie.png</pluginIcon>
+ <pluginName>cookie</pluginName>
+ </row>
+ <row>
+ <pluginIcon>plugins/Morpheus/icons/dist/plugins/flash.png</pluginIcon>
+ <pluginName>flash</pluginName>
+ </row>
+ <row>
+ <pluginIcon>plugins/Morpheus/icons/dist/plugins/java.png</pluginIcon>
+ <pluginName>java</pluginName>
+ </row>
+ </pluginsIcons>
+ <customVariables>
+ </customVariables>
+ </row>
+ <row>
+ <idSite>1</idSite>
+ <idVisit>8</idVisit>
+ <visitIp>56.11.55.78</visitIp>
+
+ <fingerprint>15cf59ef3a6e6fd6</fingerprint>
+ <actionDetails>
+ </actionDetails>
+ <goalConversions>0</goalConversions>
+ <siteCurrency>USD</siteCurrency>
+ <siteCurrencySymbol>$</siteCurrencySymbol>
+
+
+
+
+ <siteName>Piwik test</siteName>
+
+
+
+
+
+
+ <userId />
+ <visitorType>returning</visitorType>
+ <visitorTypeIcon>plugins/Live/images/returningVisitor.png</visitorTypeIcon>
+ <visitConverted>0</visitConverted>
+ <visitConvertedIcon />
+ <visitCount>2</visitCount>
+ <visitEcommerceStatus>none</visitEcommerceStatus>
+ <visitEcommerceStatusIcon />
+ <daysSinceFirstVisit>0</daysSinceFirstVisit>
+ <secondsSinceFirstVisit>0</secondsSinceFirstVisit>
+ <daysSinceLastEcommerceOrder>0</daysSinceLastEcommerceOrder>
+ <secondsSinceLastEcommerceOrder />
+ <visitDuration>0</visitDuration>
+ <visitDurationPretty>0s</visitDurationPretty>
+ <searches>0</searches>
+ <actions>1</actions>
+ <interactions>1</interactions>
+ <referrerType>campaign</referrerType>
+ <referrerTypeName>Campaigns</referrerTypeName>
+ <referrerName>campaign</referrerName>
+ <referrerKeyword>campaignkeyword</referrerKeyword>
+ <referrerKeywordPosition />
+ <referrerUrl />
+ <referrerSearchEngineUrl />
+ <referrerSearchEngineIcon />
+ <referrerSocialNetworkUrl />
+ <referrerSocialNetworkIcon />
+ <languageCode>fr</languageCode>
+ <language>French</language>
+ <deviceType>Desktop</deviceType>
+ <deviceTypeIcon>plugins/Morpheus/icons/dist/devices/desktop.png</deviceTypeIcon>
+ <deviceBrand>Unknown</deviceBrand>
+ <deviceModel>Generic Desktop</deviceModel>
+ <operatingSystem>Windows XP</operatingSystem>
+ <operatingSystemName>Windows</operatingSystemName>
+ <operatingSystemIcon>plugins/Morpheus/icons/dist/os/WIN.png</operatingSystemIcon>
+ <operatingSystemCode>WIN</operatingSystemCode>
+ <operatingSystemVersion>XP</operatingSystemVersion>
+ <browserFamily>Gecko</browserFamily>
+ <browserFamilyDescription>Gecko (Firefox)</browserFamilyDescription>
+ <browser>Firefox 3.6</browser>
+ <browserName>Firefox</browserName>
+ <browserIcon>plugins/Morpheus/icons/dist/browsers/FF.png</browserIcon>
+ <browserCode>FF</browserCode>
+ <browserVersion>3.6</browserVersion>
+ <totalEcommerceRevenue>0</totalEcommerceRevenue>
+ <totalEcommerceConversions>0</totalEcommerceConversions>
+ <totalEcommerceItems>0</totalEcommerceItems>
+ <totalAbandonedCartsRevenue>0</totalAbandonedCartsRevenue>
+ <totalAbandonedCarts>0</totalAbandonedCarts>
+ <totalAbandonedCartsItems>0</totalAbandonedCartsItems>
+ <events>0</events>
+ <continent>Europe</continent>
+ <continentCode>eur</continentCode>
+ <country>France</country>
+ <countryCode>fr</countryCode>
+ <countryFlag>plugins/Morpheus/icons/dist/flags/fr.png</countryFlag>
+ <region />
+ <regionCode />
+ <city />
+ <location>France</location>
+ <latitude />
+ <longitude />
+ <visitLocalTime>12:34:06</visitLocalTime>
+ <visitLocalHour>12</visitLocalHour>
+ <daysSinceLastVisit>0</daysSinceLastVisit>
+ <secondsSinceLastVisit>0</secondsSinceLastVisit>
+ <resolution>1024x768</resolution>
+ <plugins>cookie, flash, java</plugins>
+ <pluginsIcons>
+ <row>
+ <pluginIcon>plugins/Morpheus/icons/dist/plugins/cookie.png</pluginIcon>
+ <pluginName>cookie</pluginName>
+ </row>
+ <row>
+ <pluginIcon>plugins/Morpheus/icons/dist/plugins/flash.png</pluginIcon>
+ <pluginName>flash</pluginName>
+ </row>
+ <row>
+ <pluginIcon>plugins/Morpheus/icons/dist/plugins/java.png</pluginIcon>
+ <pluginName>java</pluginName>
+ </row>
+ </pluginsIcons>
+ <customVariables>
+ </customVariables>
+ </row>
</result> \ No newline at end of file
diff --git a/plugins/PrivacyManager/tests/System/expected/test_userIdAnonymized__Referrers.getAll_year.xml b/plugins/PrivacyManager/tests/System/expected/test_userIdAnonymized__Referrers.getAll_year.xml
new file mode 100644
index 0000000000..dae8328bb4
--- /dev/null
+++ b/plugins/PrivacyManager/tests/System/expected/test_userIdAnonymized__Referrers.getAll_year.xml
@@ -0,0 +1,65 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<result>
+ <row>
+ <label>Facebook</label>
+ <nb_visits>2</nb_visits>
+ <nb_actions>2</nb_actions>
+ <max_actions>1</max_actions>
+ <sum_visit_length>0</sum_visit_length>
+ <bounce_count>2</bounce_count>
+ <nb_visits_converted>0</nb_visits_converted>
+ <sum_daily_nb_uniq_visitors>2</sum_daily_nb_uniq_visitors>
+ <sum_daily_nb_users>0</sum_daily_nb_users>
+ <url>facebook.com</url>
+ <logo>plugins/Morpheus/icons/dist/socials/facebook.com.png</logo>
+ <referer_type>7</referer_type>
+ </row>
+ <row>
+ <label />
+ <nb_visits>1</nb_visits>
+ <nb_actions>1</nb_actions>
+ <max_actions>1</max_actions>
+ <sum_visit_length>0</sum_visit_length>
+ <bounce_count>1</bounce_count>
+ <nb_visits_converted>0</nb_visits_converted>
+ <sum_daily_nb_uniq_visitors>1</sum_daily_nb_uniq_visitors>
+ <sum_daily_nb_users>0</sum_daily_nb_users>
+ <referer_type>3</referer_type>
+ </row>
+ <row>
+ <label>campaign</label>
+ <nb_visits>1</nb_visits>
+ <nb_actions>1</nb_actions>
+ <max_actions>1</max_actions>
+ <sum_visit_length>0</sum_visit_length>
+ <bounce_count>1</bounce_count>
+ <nb_visits_converted>0</nb_visits_converted>
+ <sum_daily_nb_uniq_visitors>1</sum_daily_nb_uniq_visitors>
+ <sum_daily_nb_users>0</sum_daily_nb_users>
+ <referer_type>6</referer_type>
+ </row>
+ <row>
+ <label>Keyword not defined</label>
+ <nb_visits>1</nb_visits>
+ <nb_actions>1</nb_actions>
+ <max_actions>1</max_actions>
+ <sum_visit_length>0</sum_visit_length>
+ <bounce_count>1</bounce_count>
+ <nb_visits_converted>0</nb_visits_converted>
+ <sum_daily_nb_uniq_visitors>1</sum_daily_nb_uniq_visitors>
+ <sum_daily_nb_users>0</sum_daily_nb_users>
+ <referer_type>2</referer_type>
+ </row>
+ <row>
+ <label>www.foo.com</label>
+ <nb_visits>1</nb_visits>
+ <nb_actions>1</nb_actions>
+ <max_actions>1</max_actions>
+ <sum_visit_length>0</sum_visit_length>
+ <bounce_count>1</bounce_count>
+ <nb_visits_converted>0</nb_visits_converted>
+ <sum_daily_nb_uniq_visitors>1</sum_daily_nb_uniq_visitors>
+ <sum_daily_nb_users>0</sum_daily_nb_users>
+ <referer_type>3</referer_type>
+ </row>
+</result> \ No newline at end of file
diff --git a/plugins/PrivacyManager/tests/UI/expected-screenshots/PrivacyManager_privacy_settings_default.png b/plugins/PrivacyManager/tests/UI/expected-screenshots/PrivacyManager_privacy_settings_default.png
index b1019001a3..6a29a57fb9 100644
--- a/plugins/PrivacyManager/tests/UI/expected-screenshots/PrivacyManager_privacy_settings_default.png
+++ b/plugins/PrivacyManager/tests/UI/expected-screenshots/PrivacyManager_privacy_settings_default.png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:e0a7292297880778ad3775085d06b2843fb543daa3f27cb6507e6a13731242c4
-size 455483
+oid sha256:964c62f6e55f8acb3fe0c95e86d843e463d6f6d8a5516b9f5d31d0e7c7784594
+size 506238
diff --git a/plugins/PrivacyManager/tests/Unit/AnonymizeReferrerTest.php b/plugins/PrivacyManager/tests/Unit/AnonymizeReferrerTest.php
new file mode 100644
index 0000000000..d600b14d3b
--- /dev/null
+++ b/plugins/PrivacyManager/tests/Unit/AnonymizeReferrerTest.php
@@ -0,0 +1,100 @@
+<?php
+/**
+ * Matomo - free/libre analytics platform
+ *
+ * @link https://matomo.org
+ * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
+ */
+
+namespace Piwik\Plugins\PrivacyManager\tests\Unit;
+
+use Piwik\Common;
+use Piwik\Plugins\PrivacyManager\ReferrerAnonymizer;
+
+class AnonymizeReferrerTest extends \PHPUnit\Framework\TestCase
+{
+ /**
+ * @var ReferrerAnonymizer
+ */
+ private $anonymizer;
+
+ public function setUp(): void
+ {
+ parent::setUp();
+ $this->anonymizer = new ReferrerAnonymizer();
+ }
+
+ /**
+ * @dataProvider getReferrerUrls
+ */
+ public function test_anonymiseReferrerUrl($expected, $url, $option)
+ {
+ $value = $this->anonymizer->anonymiseReferrerUrl($url, $option);
+ $this->assertSame($expected, $value);
+ }
+
+ /**
+ * @dataProvider getReferrerNames
+ */
+ public function test_anonymiseReferrerName($expected, $name, $type, $option)
+ {
+ $value = $this->anonymizer->anonymiseReferrerName($name, $type, $option);
+ $this->assertSame($expected, $value);
+ }
+
+ public function getReferrerNames()
+ {
+ return array(
+ ['foo', 'foo', Common::REFERRER_TYPE_WEBSITE, ReferrerAnonymizer::EXCLUDE_NONE,],
+ ['baz', 'baz', Common::REFERRER_TYPE_SEARCH_ENGINE, ReferrerAnonymizer::EXCLUDE_ALL,], // search and social is always kept
+ ['bar', 'bar', Common::REFERRER_TYPE_CAMPAIGN, ReferrerAnonymizer::EXCLUDE_ALL,], //campaign is always kept
+ ['', 'foo.com', Common::REFERRER_TYPE_WEBSITE, ReferrerAnonymizer::EXCLUDE_ALL,],
+ ['foo.com', 'foo.com', Common::REFERRER_TYPE_WEBSITE, ReferrerAnonymizer::EXCLUDE_PATH,], // host should be kept
+ );
+ }
+
+ /**
+ * @dataProvider getReferrerKeywords
+ */
+ public function test_anonymiseReferrerKeyword($expected, $keyword, $type, $option)
+ {
+ $value = $this->anonymizer->anonymiseReferrerKeyword($keyword, $type, $option);
+ $this->assertSame($expected, $value);
+ }
+
+ public function getReferrerKeywords()
+ {
+ return array(
+ ['foo', 'foo', Common::REFERRER_TYPE_WEBSITE, ReferrerAnonymizer::EXCLUDE_NONE,],
+ ['bar', 'bar', Common::REFERRER_TYPE_CAMPAIGN, ReferrerAnonymizer::EXCLUDE_ALL,], //campaign is always kept
+ ['', 'baz', Common::REFERRER_TYPE_SEARCH_ENGINE, ReferrerAnonymizer::EXCLUDE_ALL,],
+ ['', 'foo.com', Common::REFERRER_TYPE_WEBSITE, ReferrerAnonymizer::EXCLUDE_ALL,],
+ ['', 'foo.com', Common::REFERRER_TYPE_WEBSITE, ReferrerAnonymizer::EXCLUDE_PATH,],
+ ['', 'foo.com', Common::REFERRER_TYPE_WEBSITE, ReferrerAnonymizer::EXCLUDE_QUERY,],
+ ['', 'foo.com', Common::REFERRER_TYPE_SEARCH_ENGINE, ReferrerAnonymizer::EXCLUDE_QUERY,],
+ ['', 'foo.com', Common::REFERRER_TYPE_SEARCH_ENGINE, ReferrerAnonymizer::EXCLUDE_PATH,],
+ ['foo.com', 'foo.com', Common::REFERRER_TYPE_SEARCH_ENGINE, ReferrerAnonymizer::EXCLUDE_NONE,],
+ ['foo.com', 'foo.com', Common::REFERRER_TYPE_WEBSITE, ReferrerAnonymizer::EXCLUDE_NONE,],
+ );
+ }
+
+ public function getReferrerUrls()
+ {
+ return array(
+ [false, false, ReferrerAnonymizer::EXCLUDE_ALL,],
+ [false, false, ReferrerAnonymizer::EXCLUDE_NONE,],
+ ['https://foo.com/bar/baz?hello=world', 'https://foo.com/bar/baz?hello=world', ReferrerAnonymizer::EXCLUDE_NONE,],
+ ['', 'https://foo.com/bar/baz?hello=world', ReferrerAnonymizer::EXCLUDE_ALL,],
+ ['https://foo.com/bar/baz/', 'https://foo.com/bar/baz/?hello=world', ReferrerAnonymizer::EXCLUDE_QUERY,],
+ ['https://foo.com/bar/baz', 'https://foo.com/bar/baz?hello=world', ReferrerAnonymizer::EXCLUDE_QUERY,],
+ ['https://foo.com/bar/baz', 'https://foo.com/bar/baz', ReferrerAnonymizer::EXCLUDE_QUERY,],
+ ['https://foo.com/', 'https://foo.com/bar/baz/?hello=world', ReferrerAnonymizer::EXCLUDE_PATH,],
+ ['https://foo.com/', 'https://foo.com/bar/baz?hello=world', ReferrerAnonymizer::EXCLUDE_PATH,],
+ ['https://foo.com/', 'https://foo.com/bar/baz', ReferrerAnonymizer::EXCLUDE_PATH,],
+ ['https://foo.com/', 'https://foo.com/bar/baz', ReferrerAnonymizer::EXCLUDE_PATH,],
+ ['https://foo.com', 'https://foo.com', ReferrerAnonymizer::EXCLUDE_PATH,], // no path
+ ['//foo.com/', '//foo.com/path?x=1', ReferrerAnonymizer::EXCLUDE_PATH,],
+ ['foo.com/path?x=2', 'foo.com/path?x=2', ReferrerAnonymizer::EXCLUDE_PATH,],// not really a URL so isn't anonymised
+ );
+ }
+} \ No newline at end of file