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:
authorBen Burgess <88810029+bx80@users.noreply.github.com>2022-09-09 07:08:01 +0300
committerGitHub <noreply@github.com>2022-09-09 07:08:01 +0300
commit178c87bdba89ec714805e346d97fdda7af79498c (patch)
tree94522a17183c1f3517be02596fa12abfadce091c /plugins/CoreAdminHome
parent437dfd2e2b21d3aa875d33d4a095ff784a26a310 (diff)
Improved opt out (#19528)
* New opt out UI, self-contained code - WIP * Updated translation * built vue files * Added OptOutJS functionality, code improvements, show custom guide link on UI * Rebuild Vue * built vue files * Fix typo * Default iframe out-out to show intro, minor code tidy, updated UI test screenshots * built vue files * Replaced let with var * Improved warning when opt-out content div is missing, added info box to recommend testing * built vue files * Update UI test screenshots * Show intro parameter show as zero if not set, hide opt out complete message if not showing intro, added additional opt out url exclusions, reverted incorrect regex adjustments * built vue files * Annotated new core home embed code methods as internal * Simplify code Co-authored-by: Stefan Giehl <stefan@matomo.org> * Updated parameter name, added trailing url trimming * Added language choice selection for self-contained opt-out code * Increased test_TotalPiwikFilesSize_isWithinReasonnableSize size from 58MiB to 60MiB * Adjust layout * Update UI test screenshot * Prevent session start for optOutJS controller method * Fix conflicts * Fix for vue error * built vue files * Added content type and cache control headers when serving the opt out javascript * Added option to apply styling (disabled by default), improved reminder box visibility, updated developer log, added what's new, added title to opt-out developer guide section * built vue files * Fix typo Co-authored-by: Justin Velluppillai <justin@innocraft.com> * Update tests * Update UI test screenshot * Moved inline CSS style to less * built vue files * Update UI test screenshot * Update test reminder section style * built vue files * Update UI test screenshot * Accept cookie path and cookie domain as URL parameters, revert loading (incorrect) cookie settings from config * Bump to retry random test fail * Local vue build * built vue files * Added blog post link to the what's new notification * Update UI test screenshot * Update "What's New?" link url Co-authored-by: bx80 <bx80@users.noreply.github.com> Co-authored-by: Stefan Giehl <stefan@matomo.org> Co-authored-by: Justin Velluppillai <justin@innocraft.com>
Diffstat (limited to 'plugins/CoreAdminHome')
-rw-r--r--plugins/CoreAdminHome/API.php57
-rw-r--r--plugins/CoreAdminHome/Controller.php17
-rw-r--r--plugins/CoreAdminHome/OptOutManager.php408
-rw-r--r--plugins/CoreAdminHome/lang/en.json6
-rw-r--r--plugins/CoreAdminHome/templates/optOut.twig2
5 files changed, 474 insertions, 16 deletions
diff --git a/plugins/CoreAdminHome/API.php b/plugins/CoreAdminHome/API.php
index b922733c45..4b0e4380c2 100644
--- a/plugins/CoreAdminHome/API.php
+++ b/plugins/CoreAdminHome/API.php
@@ -48,11 +48,18 @@ class API extends \Piwik\Plugin\API
*/
private $trackingFailures;
- public function __construct(Scheduler $scheduler, ArchiveInvalidator $invalidator, Failures $trackingFailures)
+ /**
+ * @var OptOutManager
+ */
+ private $optOutManager;
+
+ public function __construct(Scheduler $scheduler, ArchiveInvalidator $invalidator, Failures $trackingFailures,
+ OptOutManager $optOutManager)
{
$this->scheduler = $scheduler;
$this->invalidator = $invalidator;
$this->trackingFailures = $trackingFailures;
+ $this->optOutManager = $optOutManager;
}
/**
@@ -335,4 +342,52 @@ class API extends \Piwik\Plugin\API
return array($toInvalidate, $invalidDates);
}
+
+ /**
+ * Show the JavaScript opt out code
+ *
+ * @param string $backgroundColor
+ * @param string $fontColor
+ * @param string $fontSize
+ * @param string $fontFamily
+ * @param bool $applyStyling
+ * @param bool $showIntro
+ * @param string $matomoUrl
+ * @param string $language
+ *
+ * @return string
+ *
+ * @internal
+ */
+ public function getOptOutJSEmbedCode(string $backgroundColor, string $fontColor,
+ string $fontSize, string $fontFamily, bool $applyStyling, bool $showIntro,
+ string $matomoUrl, string $language): string
+ {
+
+ return $this->optOutManager->getOptOutJSEmbedCode($matomoUrl, $language, $backgroundColor, $fontColor, $fontSize,
+ $fontFamily, $applyStyling, $showIntro);
+ }
+
+ /**
+ * Show the self-contained JavaScript opt out code
+ *
+ * @param string $backgroundColor
+ * @param string $fontColor
+ * @param string $fontSize
+ * @param string $fontFamily
+ * @param bool $applyStyling
+ * @param bool $showIntro
+ *
+ * @return string
+ *
+ * @internal
+ */
+ public function getOptOutSelfContainedEmbedCode(string $backgroundColor,
+ string $fontColor, string $fontSize, string $fontFamily,
+ bool $applyStyling = false, bool $showIntro = true): string
+ {
+ return $this->optOutManager->getOptOutSelfContainedEmbedCode($backgroundColor, $fontColor, $fontSize, $fontFamily, $applyStyling, $showIntro);
+ }
+
+
} \ No newline at end of file
diff --git a/plugins/CoreAdminHome/Controller.php b/plugins/CoreAdminHome/Controller.php
index 20283d6d4c..4af369a7ae 100644
--- a/plugins/CoreAdminHome/Controller.php
+++ b/plugins/CoreAdminHome/Controller.php
@@ -255,11 +255,24 @@ class Controller extends ControllerAdmin
}
/**
- * Shows the "Track Visits" checkbox.
+ * Shows the "Track Visits" checkbox - iFrame (deprecated)
*/
public function optOut()
{
- return $this->optOutManager->getOptOutView()->render();
+ return $this->optOutManager->getOptOutViewIframe()->render();
+ }
+
+ /**
+ * Shows the Javascript opt out
+ *
+ * @return string
+ * @throws Exception
+ */
+ public function optOutJS(): string
+ {
+ Common::sendHeader('Content-Type: application/javascript; charset=utf-8');
+ Common::sendHeader('Cache-Control: no-store');
+ return $this->optOutManager->getOptOutJS();
}
public function uploadCustomLogo()
diff --git a/plugins/CoreAdminHome/OptOutManager.php b/plugins/CoreAdminHome/OptOutManager.php
index 5f994e2559..927e3ad630 100644
--- a/plugins/CoreAdminHome/OptOutManager.php
+++ b/plugins/CoreAdminHome/OptOutManager.php
@@ -10,6 +10,7 @@ namespace Piwik\Plugins\CoreAdminHome;
use Piwik\Common;
use Piwik\Nonce;
+use Piwik\Piwik;
use Piwik\Plugins\LanguagesManager\API as APILanguagesManager;
use Piwik\Plugins\LanguagesManager\LanguagesManager;
use Piwik\Plugins\PrivacyManager\DoNotTrackHeaderChecker;
@@ -17,6 +18,27 @@ use Piwik\Tracker\IgnoreCookie;
use Piwik\Url;
use Piwik\View;
+/*
+ * There are three different opt-out choices:
+ *
+ * iFrame : an <iframe> tag is added to the webpage with a Matomo URL as the source, this URL serves the opt-out
+ * content and sets the opt-out cookie for the Matomo URL domain. Translation and styling is done
+ * server side, a third party cookie is set. Not well supported with modern browser third party cookie
+ * restrictions, no longer offered as an option in the UI but the content URL is supported for existing
+ * webpages still using the iFrame code.
+ *
+ * JavaScript : an empty <div> tag is added to the webpage along with a <script> reference which loads dynamic
+ * JavaScript from a Matomo URL, the JavaScript then populates the empty div with the opt-out content.
+ * Translation and styling is read server side and built into the JavaScript, the Matomo tracker is
+ * used to set a first party cookie if it is loaded, otherwise the first party cookie is set directly.
+ * Can be broken by ad blockers that prevent third party scripts.
+ *
+ * Self-Contained: an empty <div> tag is added to the webpage along with an inline <script> tag containing the entire
+ * opt-out JavaScript. Translation and styling are built into the script when it is generated by the UI
+ * and any changes require modifying the code on each webpage. A first party cookie is set. It is
+ * unlikely that the script will be blocked as it is fully self-contained and part of the webpage.
+ *
+ */
class OptOutManager
{
/** @var DoNotTrackHeaderChecker */
@@ -38,7 +60,7 @@ class OptOutManager
private $queryParameters = array();
/**
- * @param DoNotTrackHeaderChecker $doNotTrackHeaderChecker
+ * @param DoNotTrackHeaderChecker|null $doNotTrackHeaderChecker
*/
public function __construct(DoNotTrackHeaderChecker $doNotTrackHeaderChecker = null)
{
@@ -158,10 +180,349 @@ class OptOutManager
}
/**
+ * Return the HTML code to be added to pages for the JavaScript opt-out
+ *
+ * @param string $matomoUrl
+ * @param string $language
+ * @param string $backgroundColor
+ * @param string $fontColor
+ * @param string $fontSize
+ * @param string $fontFamily
+ * @param bool $applyStyling
+ * @param bool $showIntro
+ *
+ * @return string
+ */
+ public function getOptOutJSEmbedCode(string $matomoUrl, string $language, string $backgroundColor, string $fontColor,
+ string $fontSize, string $fontFamily, bool $applyStyling, bool $showIntro): string
+ {
+ return '<div id="matomo-opt-out"></div>
+<script src="'.rtrim($matomoUrl, '/').'/index.php?module=CoreAdminHome&action=optOutJS&div=matomo-opt-out&language='.$language.($applyStyling ? '&backgroundColor='.$backgroundColor.'&fontColor='.$fontColor.'&fontSize='.$fontSize.'&fontFamily='.$fontFamily : '').'&showIntro='.($showIntro ? '1' : '0').'"></script>';
+ }
+
+ /**
+ * Return the HTML code to be added to pages for the self-contained opt-out
+ *
+ * @param string $backgroundColor
+ * @param string $fontColor
+ * @param string $fontSize
+ * @param string $fontFamily
+ * @param bool $applyStyling
+ * @param bool $showIntro
+ *
+ * @return string
+ */
+ public function getOptOutSelfContainedEmbedCode(string $backgroundColor, string $fontColor, string $fontSize,
+ string $fontFamily, bool $applyStyling, bool $showIntro): string
+ {
+
+ $cookiePath = Common::getRequestVar('cookiePath', '', 'string');
+ $cookieDomain = Common::getRequestVar('cookieDomain', '', 'string');
+
+ $settings = [
+ 'showIntro' => $showIntro,
+ 'divId' => 'matomo-opt-out',
+ 'useSecureCookies' => true,
+ 'cookiePath' => ($cookiePath !== '' ? $cookiePath : null),
+ 'cookieDomain' => ($cookieDomain !== '' ? $cookieDomain : null),
+ 'cookieSameSite' => Common::getRequestVar('cookieSameSite', 'Lax', 'string'),
+ ];
+
+ // Self contained code translations are static and always use the language of the user who generated the embed code
+ $settings = array_merge($settings, $this->getTranslations());
+ $settingsString = 'var settings = '.json_encode($settings).';';
+
+ $styleSheet = $this->optOutStyling($fontSize, $fontColor, $fontFamily, $backgroundColor, true);
+
+$code = <<<HTML
+<div id="matomo-opt-out" style=""></div>
+<script>
+ var settings = {};
+ document.addEventListener('DOMContentLoaded', function() {
+ window.MatomoConsent.init(settings.useSecureCookies, settings.cookiePath, settings.cookieDomain, settings.cookieSameSite);
+ showContent(window.MatomoConsent.hasConsent());
+ });
+
+ window.MatomoConsent = { };
+</script>
+HTML;
+ return str_replace('window.MatomoConsent = { };', $this->getOptOutCommonJS(),
+ str_replace('style=""', ($applyStyling ? 'style="'.$styleSheet.'"' : ''),
+ str_replace("var settings = {};", $settingsString, $code)));
+ }
+
+ /**
+ * Generate and return JavaScript to show the opt-out option
+ *
+ * All optOutJS URL params:
+ * backgroundColor
+ * fontColor
+ * fontSize
+ * fontFamily
+ * language (default "auto") Language code for the translations or "auto" to use the browser language
+ * showIntro (default 1) Should the opt-out intro text be shown?
+ * divId (default "matomo-opt-out") The id of the div which will contain the opt-out form
+ * useCookiesIfNoTracker (default 1) Should consent cookies be read/written directly if the tracker can't be found?
+ * useCookiesTimeout (default 10) How long to wait for the tracker to be detected?
+ * useSecureCookies (default 1) Set secure cookies?
+ * cookiePath (default blank) Use this path for consent cookies
+ * cookieDomain (default blank) Use this domain for consent cookies
+ *
+ * @return string
+ */
+ public function getOptOutJS() : string
+ {
+
+ $language = Common::getRequestVar('language', 'auto', 'string');
+ $showIntro = Common::getRequestVar('showIntro', 1, 'int');
+ $divId = Common::getRequestVar('divId', 'matomo-opt-out', 'string');
+ $useCookiesIfNoTracker = Common::getRequestVar('useCookiesIfNoTracker', 1, 'int');
+ $useCookiesTimeout = Common::getRequestVar('useCookiesTimeout', 10, 'int');
+ $useSecureCookies = Common::getRequestVar('useSecureCookies', 1, 'int');
+ $cookiePath = Common::getRequestVar('cookiePath', '', 'string');
+ $cookieDomain = Common::getRequestVar('cookieDomain', '', 'string');
+
+ // If the language parameter is 'auto' then use the browser language
+ if ($language === 'auto') {
+ $language = Common::extractLanguageCodeFromBrowserLanguage(Common::getBrowserLanguage(),
+ APILanguagesManager::getInstance()->getAvailableLanguages());
+ }
+
+ $settings = [
+ 'showIntro' => $showIntro,
+ 'divId' => $divId,
+ 'useSecureCookies' => $useSecureCookies,
+ 'cookiePath' => ($cookiePath !== '' ? $cookiePath : null),
+ 'cookieDomain' => ($cookieDomain !== '' ? $cookieDomain : null),
+ 'useCookiesIfNoTracker' => $useCookiesIfNoTracker,
+ 'useCookiesTimeout' => $useCookiesTimeout,
+ ];
+
+ // Self contained code translations are static and always use the language of the user who generated the embed code
+ $translations = $this->getTranslations($language);
+ $translations['OptOutErrorNoTracker'] = Piwik::translate('CoreAdminHome_OptOutErrorNoTracker', [], $language);
+ $settings = array_merge($settings, $translations);
+ $settingsString = 'var settings = '.json_encode($settings).';';
+
+ $styleSheet = $this->optOutStyling(null, null, null, null, true);
+
+ /** @lang JavaScript */
+ $code = <<<JS
+
+ var settings = {};
+ var checkForTrackerTried = 0;
+ var checkForTrackerTries = (settings.useCookiesTimeout * 4);
+ var checkForTrackerInterval = 250;
+ var optOutDiv = null;
+
+ function optOutInit() {
+ optOutDiv = document.getElementById(settings.divId);
+ if (optOutDiv) {
+ optOutDiv.style.cssText += 'stylecss'; // Appending css to avoid overwritting existing inline div styles
+ } else {
+ showContent(false, null, true); // will show unable to find opt-out div error
+ return;
+ }
+ checkForMatomoTracker();
+ }
+
+ function checkForMatomoTracker() {
+ if (typeof _paq !== 'undefined') {
+ showOptOutTracker();
+ return;
+ }
+
+ if (checkForTrackerTried < checkForTrackerTries) {
+ setTimeout(checkForMatomoTracker, checkForTrackerInterval);
+ checkForTrackerTried++;
+ return;
+ }
+
+ if (settings.useCookiesIfNoTracker) {
+ showOptOutDirect();
+ return;
+ }
+
+ console.log('Matomo OptOutJS: failed to find Matomo tracker after '+(checkForTrackerTries*checkForTrackerInterval / 1000)+' seconds');
+ }
+
+ function showOptOutTracker() {
+ _paq.push([function () {
+ if (settings.cookieDomain) {
+ _paq.push(['setCookieDomain', settings.cookieDomain]);
+ }
+ if (settings.cookiePath) {
+ _paq.push(['setCookiePath', settings.cookiePath]);
+ }
+ if (this.isUserOptedOut()) {
+ _paq.push(['forgetUserOptOut']);
+ showContent(false, null, true);
+ } else {
+ _paq.push(['optUserOut']);
+ showContent(true, null, true);
+ }
+ }]);
+ }
+
+ function showOptOutDirect() {
+ window.MatomoConsent.init(settings.useSecureCookies, settings.cookiePath, settings.cookieDomain, settings.cookieSameSite);
+ showContent(window.MatomoConsent.hasConsent());
+ }
+
+ document.addEventListener('DOMContentLoaded', optOutInit());
+
+ window.MatomoConsent = { };
+JS;
+
+ return str_replace('window.MatomoConsent = { };', $this->getOptOutCommonJS(),
+ str_replace('stylecss', $styleSheet,
+ str_replace("var settings = {};", $settingsString, $code)));
+
+ }
+
+ /**
+ * Return the shared opt-out JavaScript (used by self-contained and tracker versions)
+ *
+ * @return string
+ */
+ private function getOptOutCommonJS() : string
+ {
+ /** @lang JavaScript */
+ return <<<JS
+
+ function showContent(consent, errorMessage = null, useTracker = false) {
+
+ var errorBlock = '<p style="color: red; font-weight: bold;">';
+
+ var div = document.getElementById(settings.divId);
+ if (!div) {
+ const warningDiv = document.createElement("div");
+ var msg = 'Unable to find opt-out content div: "'+settings.divId+'"';
+ warningDiv.id = settings.divId+'-warning';
+ warningDiv.innerHTML = errorBlock+msg+'</p>';
+ document.body.insertBefore(warningDiv, document.body.firstChild);
+ console.log(msg);
+ return;
+ }
+
+ if (!navigator || !navigator.cookieEnabled) {
+ div.innerHTML = errorBlock+settings.OptOutErrorNoCookies+'</p>';
+ return;
+ }
+ if (location.protocol !== 'https:') {
+ div.innerHTML = errorBlock+settings.OptOutErrorNotHttps+'</p>';
+ return;
+ }
+ if (errorMessage !== null) {
+ div.innerHTML = errorBlock+errorMessage+'</p>';
+ return;
+ }
+ var content = '';
+ if (consent) {
+ if (settings.showIntro) {
+ content += '<p>'+settings.YouMayOptOut2+' '+settings.YouMayOptOut3+'</p>';
+ }
+ if (useTracker) {
+ content += '<input onclick="_paq.push([\'optUserOut\']);showContent(false, null, true);" id="trackVisits" type="checkbox" checked="checked" />';
+ } else {
+ content += '<input onclick="window.MatomoConsent.consentRevoked();showContent(false);" id="trackVisits" type="checkbox" checked="checked" />';
+ }
+ content += '<label for="trackVisits"><strong><span>'+settings.YouAreNotOptedOut+' '+settings.UncheckToOptOut+'</span></strong></label>';
+ } else {
+ if (settings.showIntro) {
+ content += '<p>'+settings.OptOutComplete+' '+settings.OptOutCompleteBis+'</p>';
+ }
+ if (useTracker) {
+ content += '<input onclick="_paq.push([\'forgetUserOptOut\']);showContent(true, null, true);" id="trackVisits" type="checkbox" />';
+ } else {
+ content += '<input onclick="window.MatomoConsent.consentGiven();showContent(true);" id="trackVisits" type="checkbox" />';
+ }
+ content += '<label for="trackVisits"><strong><span>'+settings.YouAreOptedOut+' '+settings.CheckToOptIn+'</span></strong></label>';
+ }
+ div.innerHTML = content;
+ };
+
+ window.MatomoConsent = {
+ cookiesDisabled: (!navigator || !navigator.cookieEnabled),
+ CONSENT_COOKIE_NAME: 'mtm_consent', CONSENT_REMOVED_COOKIE_NAME: 'mtm_consent_removed',
+ cookieIsSecure: false, useSecureCookies: true, cookiePath: '', cookieDomain: '', cookieSameSite: 'Lax',
+ init: function(useSecureCookies, cookiePath, cookieDomain, cookieSameSite) {
+ this.useSecureCookies = useSecureCookies; this.cookiePath = cookiePath;
+ this.cookieDomain = cookieDomain; this.cookieSameSite = cookieSameSite;
+ if(useSecureCookies && location.protocol !== 'https:') {
+ console.log('Error with setting useSecureCookies: You cannot use this option on http.');
+ } else {
+ this.cookieIsSecure = useSecureCookies;
+ }
+ },
+ hasConsent: function() {
+ var value = this.getCookie(this.CONSENT_COOKIE_NAME);
+ if (this.getCookie(this.CONSENT_REMOVED_COOKIE_NAME) && value) {
+ this.setCookie(this.CONSENT_COOKIE_NAME, '', -129600000);
+ return false;
+ }
+ return (value || value !== 0);
+ },
+ consentGiven: function() {
+ this.setCookie(this.CONSENT_REMOVED_COOKIE_NAME, '', -129600000);
+ this.setCookie(this.CONSENT_COOKIE_NAME, new Date().getTime(), 946080000000);
+ },
+ consentRevoked: function() {
+ this.setCookie(this.CONSENT_COOKIE_NAME, '', -129600000);
+ this.setCookie(this.CONSENT_REMOVED_COOKIE_NAME, new Date().getTime(), 946080000000);
+ },
+ getCookie: function(cookieName) {
+ var cookiePattern = new RegExp('(^|;)[ ]*' + cookieName + '=([^;]*)'), cookieMatch = cookiePattern.exec(document.cookie);
+ return cookieMatch ? window.decodeURIComponent(cookieMatch[2]) : 0;
+ },
+ setCookie: function(cookieName, value, msToExpire) {
+ var expiryDate = new Date();
+ expiryDate.setTime((new Date().getTime()) + msToExpire);
+ document.cookie = cookieName + '=' + window.encodeURIComponent(value) +
+ (msToExpire ? ';expires=' + expiryDate.toGMTString() : '') +
+ ';path=' + (this.cookiePath || '/') +
+ (this.cookieDomain ? ';domain=' + this.cookieDomain : '') +
+ (this.cookieIsSecure ? ';secure' : '') +
+ ';SameSite=' + this.cookieSameSite;
+ if ((!msToExpire || msToExpire >= 0) && this.getCookie(cookieName) !== String(value)) {
+ console.log('There was an error setting cookie `' + cookieName + '`. Please check domain and path.');
+ }
+ }
+ };
+JS;
+
+ }
+
+ /**
+ * Get translations used by the opt-out popup
+ *
+ * @param string|null $language
+ *
+ * @return array
+ */
+ private function getTranslations(string $language = null) : array
+ {
+ return [
+ 'OptOutComplete' => Piwik::translate('CoreAdminHome_OptOutComplete', [], $language),
+ 'OptOutCompleteBis' => Piwik::translate('CoreAdminHome_OptOutCompleteBis', [], $language),
+ 'YouMayOptOut2' => Piwik::translate('CoreAdminHome_YouMayOptOut2', [], $language),
+ 'YouMayOptOut3' => Piwik::translate('CoreAdminHome_YouMayOptOut3', [], $language),
+ 'OptOutErrorNoCookies' => Piwik::translate('CoreAdminHome_OptOutErrorNoCookies', [], $language),
+ 'OptOutErrorNotHttps' => Piwik::translate('CoreAdminHome_OptOutErrorNotHttps', [], $language),
+ 'YouAreNotOptedOut' => Piwik::translate('CoreAdminHome_YouAreNotOptedOut', [], $language),
+ 'UncheckToOptOut' => Piwik::translate('CoreAdminHome_UncheckToOptOut', [], $language),
+ 'YouAreOptedOut' => Piwik::translate('CoreAdminHome_YouAreOptedOut', [], $language),
+ 'CheckToOptIn' => Piwik::translate('CoreAdminHome_CheckToOptIn', [], $language),
+ ];
+ }
+
+ /**
+ * Return the content of the iFrame opt out
+ *
* @return View
* @throws \Exception
*/
- public function getOptOutView()
+ public function getOptOutViewIFrame()
{
if ($this->view) {
return $this->view;
@@ -182,7 +543,7 @@ class OptOutManager
$reloadUrl = Url::getCurrentQueryStringWithParametersModified(array(
'showConfirmOnly' => 1,
'setCookieInNewWindow' => 0,
- 'nonce' => $nonce ? $nonce : ''
+ 'nonce' => $nonce ? : ''
));
} else {
$reloadUrl = false;
@@ -210,7 +571,9 @@ class OptOutManager
'nonce' => $nonce
), false);
- $this->addStylesheet($this->optOutStyling());
+ if (Common::getRequestVar('applyStyling', 1, 'int')) {
+ $this->addStylesheet($this->optOutStyling());
+ }
$this->view = new View("@CoreAdminHome/optOut");
@@ -221,6 +584,7 @@ class OptOutManager
$this->view->trackVisits = $trackVisits;
$this->view->nonce = $nonce;
$this->view->language = $lang;
+ $this->view->showIntro = Common::getRequestVar('showIntro', 1, 'int');
$this->view->showConfirmOnly = Common::getRequestVar('showConfirmOnly', false, 'int');
$this->view->reloadUrl = $reloadUrl;
$this->view->javascripts = $this->getJavaScripts();
@@ -230,13 +594,31 @@ class OptOutManager
return $this->view;
}
- private function optOutStyling()
+ /**
+ * Provide a CSS style sheet based on the chosen opt out style options
+ *
+ * @param string|null $fontSize
+ * @param string|null $fontColor
+ * @param string|null $fontFamily
+ * @param string|null $backgroundColor
+ * @param bool $noBody
+ *
+ * @return string
+ * @throws \Exception
+ */
+ private function optOutStyling(?string $fontSize = null, ?string $fontColor = null, ?string $fontFamily = null,
+ ?string $backgroundColor = null, bool $noBody = false): string
{
- $cssfontsize = Common::unsanitizeInputValue(Common::getRequestVar('fontSize', false, 'string'));
- $cssfontcolour = Common::unsanitizeInputValue(Common::getRequestVar('fontColor', false, 'string'));
- $cssfontfamily = Common::unsanitizeInputValue(Common::getRequestVar('fontFamily', false, 'string'));
- $cssbackgroundcolor = Common::unsanitizeInputValue(Common::getRequestVar('backgroundColor', false, 'string'));
- $cssbody = 'body { ';
+ $cssfontsize = ($fontSize ? : Common::unsanitizeInputValue(Common::getRequestVar('fontSize', false, 'string')));
+ $cssfontcolour = ($fontColor ? : Common::unsanitizeInputValue(Common::getRequestVar('fontColor', false, 'string')));
+ $cssfontfamily = ($fontFamily ? : Common::unsanitizeInputValue(Common::getRequestVar('fontFamily', false, 'string')));
+ $cssbackgroundcolor = ($backgroundColor ? : Common::unsanitizeInputValue(Common::getRequestVar('backgroundColor', false, 'string')));
+
+ if (!$noBody) {
+ $cssbody = 'body { ';
+ } else {
+ $cssbody = '';
+ }
$hexstrings = array(
'fontColor' => $cssfontcolour,
@@ -248,12 +630,14 @@ class OptOutManager
}
}
+ /** @noinspection RegExpRedundantEscape */
if ($cssfontsize && (preg_match("/^[0-9]+[\.]?[0-9]*(px|pt|em|rem|%)$/", $cssfontsize))) {
$cssbody .= 'font-size: ' . $cssfontsize . '; ';
} else if ($cssfontsize) {
throw new \Exception("The URL parameter fontSize value of '$cssfontsize' is not valid. Expected value is for example '15pt', '1.2em' or '13px'.\n");
}
+ /** @noinspection RegExpRedundantEscape */
if ($cssfontfamily && (preg_match('/^[a-zA-Z0-9-\ ,\'"]+$/', $cssfontfamily))) {
$cssbody .= 'font-family: ' . $cssfontfamily . '; ';
} else if ($cssfontfamily) {
@@ -267,7 +651,9 @@ class OptOutManager
$cssbody .= 'background-color: #' . $cssbackgroundcolor . '; ';
}
- $cssbody .= '}';
+ if (!$noBody) {
+ $cssbody .= '}';
+ }
return $cssbody;
}
/**
diff --git a/plugins/CoreAdminHome/lang/en.json b/plugins/CoreAdminHome/lang/en.json
index 92e6d1aaa8..380e0a42cb 100644
--- a/plugins/CoreAdminHome/lang/en.json
+++ b/plugins/CoreAdminHome/lang/en.json
@@ -91,9 +91,11 @@
"OptOutDntFound": "You are not being tracked since your browser is reporting that you do not want to. This is a setting of your browser so you won't be able to opt-in until you disable the 'Do Not Track' feature.",
"OptOutErrorNoCookies": "The tracking opt-out feature requires cookies to be enabled.",
"OptOutErrorNotHttps": "The tracking opt-out feature may not work because this site was not loaded over HTTPS. Please reload the page to check if your opt out status changed.",
- "OptOutErrorWindowOpen": "The tracking Opt-out feature may not work because it looks like the opt out popup was blocked. Please reload the page to check if your opt out status changed.",
+ "OptOutErrorWindowOpen": "The tracking opt-out feature may not work because it looks like the opt out popup was blocked. Please reload the page to check if your opt out status changed.",
+ "OptOutErrorNoTracker": "The tracking opt-out feature was unable to find the Matomo Tracker code on this page",
"OptOutExplanation": "Matomo is dedicated to providing privacy on the Internet. To provide your visitors with the choice of opting-out of Matomo Web Analytics, you can add the following HTML code on one of your website page, for example in a Privacy Policy page.",
- "OptOutExplanationIntro": "This code will display an iFrame containing a link for your visitors to opt-out of Matomo by setting an opt-out cookie in their web browsers. See below (or %1$sclick here%2$s) to view the content that will be displayed by the iFrame.",
+ "OptOutExplanationIntro": "This code will display a div containing a link for your visitors to opt-out of Matomo by setting an opt-out cookie in their web browsers. See below (or %1$sclick here%2$s) to view the content that will be displayed by the div.",
+ "OptOutCustomOptOutLink": "You can also create a completely custom opt-out form using HTML and JavaScript, as explained in this developer %1$sguide%2$s.",
"OptOutForYourVisitors": "Matomo opt-out for your visitors",
"OptingYouOut": "Opting you out, please wait…",
"PersonalPluginSettings": "Personal Plugin Settings",
diff --git a/plugins/CoreAdminHome/templates/optOut.twig b/plugins/CoreAdminHome/templates/optOut.twig
index 1ad00ea29b..6a81e33ec7 100644
--- a/plugins/CoreAdminHome/templates/optOut.twig
+++ b/plugins/CoreAdminHome/templates/optOut.twig
@@ -45,6 +45,7 @@
<noscript>
{% endif %}
+ {% if showIntro %}
<p id="textOptIn" {% if trackVisits %}style=" display:none"{% endif %}>
{{ 'CoreAdminHome_OptOutComplete'|translate }}
@@ -53,6 +54,7 @@
<p id="textOptOut" {% if not trackVisits %}style="display:none"{% endif %}>
{{ 'CoreAdminHome_YouMayOptOut2'|translate }} {{ 'CoreAdminHome_YouMayOptOut3'|translate }}
</p>
+ {% endif %}
<p id="textError_cookies" style="display:none; color: red; font-weight: bold;" %}>
{{ 'CoreAdminHome_OptOutErrorNoCookies'|translate }}