Skip to content
Extraits de code Groupes Projets
Valider 7cc3bfc4 rédigé par diosmosis's avatar diosmosis
Parcourir les fichiers

Fixes #3135, fix opt-out form on Safari browsers by opening new window that sets the cookie.

This PR allows us to set 3rd party cookies in the opt-out form for Piwik. It works by opening a new window on form submission, reloading the new window, and setting the cookie on this reload. It is necessary to reload the window, because the session cookie isn't set, so the nonce won't be detected & so, the ignore cookie won't be set.

It works whether JavaScript is enabled or not, and other browsers still get the better UX.

The new window is closed immediately after opening, if JS is enabled.

There is also a new UI test for the opt out form, plus a small change to the UI testing framework to allow switching the user agent during tests.
parent dffd7bc7
Aucune branche associée trouvée
Aucune étiquette associée trouvée
Aucune requête de fusion associée trouvée
......@@ -321,20 +321,28 @@ class Controller extends ControllerAdmin
public function optOut()
{
$trackVisits = !IgnoreCookie::isIgnoreCookieFound();
$dntChecker = new DoNotTrackHeaderChecker();
$dntFound = $dntChecker->isDoNotTrackFound();
$nonce = Common::getRequestVar('nonce', false);
$language = Common::getRequestVar('language', '');
if ($nonce !== false && Nonce::verifyNonce('Piwik_OptOut', $nonce)) {
Nonce::discardNonce('Piwik_OptOut');
IgnoreCookie::setIgnoreCookie();
$trackVisits = !$trackVisits;
}
$setCookieInNewWindow = Common::getRequestVar('setCookieInNewWindow', false, 'int');
if ($setCookieInNewWindow) {
$reloadUrl = Url::getCurrentQueryStringWithParametersModified(array(
'showConfirmOnly' => 1,
'setCookieInNewWindow' => 0,
));
} else {
$reloadUrl = false;
$nonce = Common::getRequestVar('nonce', false);
if ($nonce !== false && Nonce::verifyNonce('Piwik_OptOut', $nonce)) {
Nonce::discardNonce('Piwik_OptOut');
IgnoreCookie::setIgnoreCookie();
$trackVisits = !$trackVisits;
}
}
$language = Common::getRequestVar('language', '');
$lang = APILanguagesManager::getInstance()->isLanguageAvailable($language)
? $language
: LanguagesManager::getLanguageCodeForCurrentUser();
......@@ -348,9 +356,18 @@ class Controller extends ControllerAdmin
$view->trackVisits = $trackVisits;
$view->nonce = Nonce::getNonce('Piwik_OptOut', 3600);
$view->language = $lang;
$view->isSafari = $this->isUserAgentSafari();
$view->showConfirmOnly = Common::getRequestVar('showConfirmOnly', false, 'int');
$view->reloadUrl = $reloadUrl;
return $view->render();
}
private function isUserAgentSafari()
{
$userAgent = @$_SERVER['HTTP_USER_AGENT'] ?: '';
return strpos($userAgent, 'Safari') !== false && strpos($userAgent, 'Chrome') === false;
}
public function uploadCustomLogo()
{
Piwik::checkUserHasSuperUserAccess();
......
......@@ -86,6 +86,7 @@
"YouAreOptedIn": "You are currently opted in.",
"YouAreOptedOut": "You are currently opted out.",
"YouMayOptOut": "You may choose not to have a unique web analytics cookie identification number assigned to your computer to avoid the aggregation and analysis of data collected on this website.",
"YouMayOptOutBis": "To make that choice, please click below to receive an opt-out cookie."
"YouMayOptOutBis": "To make that choice, please click below to receive an opt-out cookie.",
"OptingYouOut": "Opting you out, please wait..."
}
}
......@@ -2,25 +2,60 @@
<html>
<head>
<meta charset="utf-8">
{% if reloadUrl %}
<meta http-equiv="refresh" content="0; url={{ reloadUrl }}&amp;nonce={{ nonce }}" />
{% endif %}
<script>
function submitForm(event, form, loadInNewWindow) {
event.preventDefault();
if (loadInNewWindow) {
var newWindow = window.open(form.action + '&time=' + Date.now());
// when the new window loads, reload this page
newWindow.addEventListener('unload', function () {
window.location.reload();
}, false);
} else {
form.submit();
}
}
</script>
</head>
<body>
{% if dntFound %}
{{ 'CoreAdminHome_OptOutDntFound'|translate }}
{% elseif reloadUrl %}
{# empty #}
{% else %}
{# if only showing confirmation (because we're in a new window), we only display the success message if JS is disabled.
# otherwise we try to close the window immediately.
#}
{% if showConfirmOnly %}
<p>{{ 'CoreAdminHome_OptingYouOut'|translate }}</p><script>window.close();</script>
<noscript>
{% endif %}
{% if not trackVisits %}
{{ 'CoreAdminHome_OptOutComplete'|translate }}
<br/>
<br/>
{{ 'CoreAdminHome_OptOutCompleteBis'|translate }}
{% else %}
{{ 'CoreAdminHome_YouMayOptOut'|translate }}
<br/>
{{ 'CoreAdminHome_YouMayOptOutBis'|translate }}
{% endif %}
{% if showConfirmOnly %}</noscript>{% endif %}
<br/><br/>
<form method="post" action="?module=CoreAdminHome&amp;action=optOut{% if language %}&amp;language={{ language }}{% endif %}">
{% if not showConfirmOnly %}
{% set loadInNewWindow = isSafari and trackVisits %}
<form method="post" action="?module=CoreAdminHome&amp;action=optOut{% if language %}&amp;language={{ language }}{% endif %}{% if loadInNewWindow %}&amp;setCookieInNewWindow=1{% endif %}" {% if loadInNewWindow %}target="_blank"{% endif %}>
<input type="hidden" name="nonce" value="{{ nonce }}" />
<input type="hidden" name="fuzz" value="{{ "now"|date }}" />
<input onclick="this.form.submit()" type="checkbox" id="trackVisits" name="trackVisits" {% if trackVisits %}checked="checked"{% endif %} />
<input onclick="submitForm(event, this.form, {{ loadInNewWindow|default(0) }});" type="checkbox" id="trackVisits" name="trackVisits" {% if trackVisits %}checked="checked"{% endif %} />
<label for="trackVisits"><strong>
{% if trackVisits %}
{{ 'CoreAdminHome_YouAreOptedIn'|translate }} {{ 'CoreAdminHome_ClickHereToOptOut'|translate }}
......@@ -32,6 +67,7 @@
<button type="submit">{{ 'General_Save'|translate }}</button>
</noscript>
</form>
{% endif %}
{% endif %}
</body>
</html>
/*!
* Piwik - free/libre analytics platform
*
* Opt-out form tests
*
* @link http://piwik.org
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
*/
// NOTE: this test actually tests safari-specific opt out form behavior, since phantomjs' user-agent string
// is similar to Safari's
describe("OptOutForm", function () {
this.timeout(0);
var siteUrl = "/tests/resources/overlay-test-site-real/index.html",
safariUserAgent = "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_9_3) AppleWebKit/537.75.14 (KHTML, like Gecko) Version/7.0.3 Safari/7046A194A",
chromeUserAgent = "Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/41.0.2228.0 Safari/537.36";
it("should display correctly when embedded in another site", function (done) {
expect.screenshot('loaded').to.be.captureSelector('iframe#optOutIframe', function (page) {
page.userAgent = chromeUserAgent;
page.load(siteUrl);
}, done);
});
it("should reload the iframe when clicking the opt out checkbox and display an empty checkbox", function (done) {
expect.screenshot('opted-out').to.be.captureSelector('iframe#optOutIframe', function (page) {
page.evaluate(function () {
$('iframe#optOutIframe').contents().find('input#trackVisits').click();
});
page.wait(1000); // wait for iframe to reload
}, done);
});
it("should correctly show the checkbox unchecked after reloading after opting-out", function (done) {
expect.screenshot('opted-out').to.be.captureSelector('opted-out-reload', 'iframe#optOutIframe', function (page) {
page.userAgent = chromeUserAgent;
page.load(siteUrl);
}, done);
});
it("should correctly show display opted-in form when cookies are cleared", function (done) {
expect.screenshot('loaded').to.be.captureSelector('safari-loaded', 'iframe#optOutIframe', function (page) {
page.webpage.clearCookies();
page.userAgent = safariUserAgent;
page.load(siteUrl);
}, done);
});
it("should correclty set opt-out cookie on safari", function (done) {
expect.screenshot('opted-out').to.be.captureSelector('safari-opted-out', 'iframe#optOutIframe', function (page) {
page.evaluate(function () {
$('iframe#optOutIframe').contents().find('input#trackVisits').click();
});
page.load(siteUrl); // reload to check that cookie was set
}, done);
});
});
\ No newline at end of file
......@@ -12,6 +12,7 @@ var VERBOSE = false;
// TODO: should refactor, move all event queueing logic to PageAutomation class and add .frame method to change context
var PageRenderer = function (baseUrl) {
this.webpage = null;
this.userAgent = null;
this.queuedEvents = [];
this.pageLogs = [];
......@@ -34,6 +35,10 @@ PageRenderer.prototype._recreateWebPage = function () {
this.webpage = require('webpage').create();
this.webpage.viewportSize = {width:1350, height:768};
if (this.userAgent) {
this.webpage.settings.userAgent = this.userAgent;
}
this._setupWebpageEvents();
};
......
......@@ -69,6 +69,9 @@
</p>
</div>
<!-- opt out frame -->
<iframe id="optOutIframe" src="../../../index.php?module=CoreAdminHome&action=optOut&language=en"></iframe>
</div> <!-- /container -->
<!-- jQuery (necessary for Bootstrap's JavaScript plugins) -->
......
0% Chargement en cours ou .
You are about to add 0 people to the discussion. Proceed with caution.
Terminez d'abord l'édition de ce message.
Veuillez vous inscrire ou vous pour commenter