diff options
author | Ben Burgess <88810029+bx80@users.noreply.github.com> | 2022-09-09 07:08:01 +0300 |
---|---|---|
committer | GitHub <noreply@github.com> | 2022-09-09 07:08:01 +0300 |
commit | 178c87bdba89ec714805e346d97fdda7af79498c (patch) | |
tree | 94522a17183c1f3517be02596fa12abfadce091c /plugins/CoreAdminHome | |
parent | 437dfd2e2b21d3aa875d33d4a095ff784a26a310 (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.php | 57 | ||||
-rw-r--r-- | plugins/CoreAdminHome/Controller.php | 17 | ||||
-rw-r--r-- | plugins/CoreAdminHome/OptOutManager.php | 408 | ||||
-rw-r--r-- | plugins/CoreAdminHome/lang/en.json | 6 | ||||
-rw-r--r-- | plugins/CoreAdminHome/templates/optOut.twig | 2 |
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 }} |