From bb6dd6c44b04357fd6869bf104bb8967c43c5287 Mon Sep 17 00:00:00 2001 From: Alex Gibson Date: Fri, 17 Jan 2025 12:59:41 +0000 Subject: [PATCH] Firefox privacy notice and legal terms for ToS (Fixes #15872) --- .../templates/legal/terms/firefox-tos.html | 33 ++++ bedrock/legal/tests/test_views.py | 32 +++ bedrock/legal/urls.py | 2 +- bedrock/legal/views.py | 12 ++ .../templates/privacy/base-protocol.html | 10 +- .../notices/firefox-old-style-notice.html | 10 - .../privacy/notices/firefox-tos.html | 40 ++++ .../templates/privacy/notices/firefox.html | 1 + bedrock/privacy/tests/__init__.py | 3 + bedrock/privacy/tests/test_views.py | 31 +++ bedrock/privacy/views.py | 9 +- media/css/legal/legal-terms-firefox-tos.scss | 48 +++++ media/css/privacy/privacy-firefox-tos.scss | 186 ++++++++++++++++++ media/static-bundles.json | 12 ++ 14 files changed, 411 insertions(+), 18 deletions(-) create mode 100644 bedrock/legal/templates/legal/terms/firefox-tos.html create mode 100644 bedrock/legal/tests/test_views.py delete mode 100644 bedrock/privacy/templates/privacy/notices/firefox-old-style-notice.html create mode 100644 bedrock/privacy/templates/privacy/notices/firefox-tos.html create mode 100644 bedrock/privacy/tests/__init__.py create mode 100644 bedrock/privacy/tests/test_views.py create mode 100644 media/css/legal/legal-terms-firefox-tos.scss create mode 100644 media/css/privacy/privacy-firefox-tos.scss diff --git a/bedrock/legal/templates/legal/terms/firefox-tos.html b/bedrock/legal/templates/legal/terms/firefox-tos.html new file mode 100644 index 0000000000..368b67b435 --- /dev/null +++ b/bedrock/legal/templates/legal/terms/firefox-tos.html @@ -0,0 +1,33 @@ +{# + This Source Code Form is subject to the terms of the Mozilla Public + License, v. 2.0. If a copy of the MPL was not distributed with this + file, You can obtain one at https://mozilla.org/MPL/2.0/. +#} + +{% extends "legal/docs-base.html" %} + +{% block page_title %}{{ ftl('legal-firefox-rights') }}{% endblock %} + +{% block site_css %} + {% if switch('m24-website-refresh') %} + {{ css_bundle('m24-root') }} + {% endif %} + {{ css_bundle('protocol-firefox') }} +{% endblock %} + +{% block page_css %} + {{ css_bundle('legal-terms-firefox-tos') }} +{% endblock %} + +{% block site_header %}{% endblock %} + +{% block side_nav %}{% endblock %} + +{# disable GA on Fx Privacy Notice. Bug 1576673 #} +{% block google_analytics %}{% endblock %} +{% block glean %}{% endblock %} + +{# Exclude stub attribution for in-product pages: issus 9620 #} +{% block stub_attribution %}{% endblock %} + +{% block site_footer %}{% endblock %} diff --git a/bedrock/legal/tests/test_views.py b/bedrock/legal/tests/test_views.py new file mode 100644 index 0000000000..08c11e859b --- /dev/null +++ b/bedrock/legal/tests/test_views.py @@ -0,0 +1,32 @@ +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at https://mozilla.org/MPL/2.0/. + +from unittest.mock import patch + +from django.http import HttpResponse +from django.test.client import RequestFactory + +from bedrock.legal import views +from bedrock.legal_docs import views as legal_docs_views +from bedrock.mozorg.tests import TestCase + + +@patch("bedrock.firefox.views.l10n_utils.render", return_value=HttpResponse()) +@patch.object(legal_docs_views, "load_legal_doc") +class TestFirefoxTermsOfServiceDocView(TestCase): + def test_default_template(self, render_mock, lld_mock): + req = RequestFactory().get("/about/legal/terms/firefox/") + req.locale = "en-US" + view = views.FirefoxTermsOfServiceDocView.as_view() + view(req) + template = lld_mock.call_args[0][1] + assert template == "legal/terms/firefox.html" + + def test_tos_template(self, render_mock, lld_mock): + req = RequestFactory().get("/about/legal/terms/firefox/?v=product") + req.locale = "en-US" + view = views.FirefoxTermsOfServiceDocView.as_view() + view(req) + template = lld_mock.call_args[0][1] + assert template == "legal/terms/firefox-tos.html" diff --git a/bedrock/legal/urls.py b/bedrock/legal/urls.py index 4d1d4d8ad4..e736bc074e 100644 --- a/bedrock/legal/urls.py +++ b/bedrock/legal/urls.py @@ -28,7 +28,7 @@ urlpatterns = ( page("terms/builders-challenge/", "legal/terms/builders-challenge.html"), path( "terms/firefox/", - LegalDocView.as_view(template_name="legal/terms/firefox.html", legal_doc_name="firefox_about_rights"), + views.FirefoxTermsOfServiceDocView.as_view(legal_doc_name="firefox_about_rights"), name="legal.terms.firefox", ), path( diff --git a/bedrock/legal/views.py b/bedrock/legal/views.py index 9ddfa5d2a9..06b356a1cd 100644 --- a/bedrock/legal/views.py +++ b/bedrock/legal/views.py @@ -10,6 +10,7 @@ from django.views.decorators.csrf import csrf_protect from bedrock.base.urlresolvers import reverse from bedrock.legal.forms import FraudReportForm +from bedrock.legal_docs.views import LegalDocView from lib import l10n_utils FRAUD_REPORT_EMAIL_FROM = settings.DEFAULT_FROM_EMAIL @@ -17,6 +18,17 @@ FRAUD_REPORT_EMAIL_SUBJECT = "New trademark infringement report: %s; %s" FRAUD_REPORT_EMAIL_TO = ["trademarks@mozilla.com"] +class FirefoxTermsOfServiceDocView(LegalDocView): + def get_template_names(self): + variant = self.request.GET.get("v", None) + template_name = "legal/terms/firefox.html" + + if variant == "product": + template_name = "legal/terms/firefox-tos.html" + + return [template_name] + + def submit_form(request, form): form_submitted = True diff --git a/bedrock/privacy/templates/privacy/base-protocol.html b/bedrock/privacy/templates/privacy/base-protocol.html index 2b9d10df04..8e5559e35a 100644 --- a/bedrock/privacy/templates/privacy/base-protocol.html +++ b/bedrock/privacy/templates/privacy/base-protocol.html @@ -71,10 +71,12 @@ {% endif %} {% endblock %} - + {% block sidemenu %} + + {% endblock %} {% endblock %} diff --git a/bedrock/privacy/templates/privacy/notices/firefox-old-style-notice.html b/bedrock/privacy/templates/privacy/notices/firefox-old-style-notice.html deleted file mode 100644 index 2de73748c9..0000000000 --- a/bedrock/privacy/templates/privacy/notices/firefox-old-style-notice.html +++ /dev/null @@ -1,10 +0,0 @@ -{# - This Source Code Form is subject to the terms of the Mozilla Public - License, v. 2.0. If a copy of the MPL was not distributed with this - file, You can obtain one at https://mozilla.org/MPL/2.0/. -#} - -{% extends "privacy/notices/base-notice-paragraphs.html" %} - -{# disable GA on Fx Privacy Notice. Bug 1576673 #} -{% block google_analytics %}{% endblock %} diff --git a/bedrock/privacy/templates/privacy/notices/firefox-tos.html b/bedrock/privacy/templates/privacy/notices/firefox-tos.html new file mode 100644 index 0000000000..dcf4104c77 --- /dev/null +++ b/bedrock/privacy/templates/privacy/notices/firefox-tos.html @@ -0,0 +1,40 @@ +{# + This Source Code Form is subject to the terms of the Mozilla Public + License, v. 2.0. If a copy of the MPL was not distributed with this + file, You can obtain one at https://mozilla.org/MPL/2.0/. +#} + +{% extends "privacy/notices/base-notice-headings.html" %} + +{% block site_css %} + {% if switch('m24-website-refresh') %} + {{ css_bundle('m24-root') }} + {% endif %} + {{ css_bundle('protocol-firefox') }} +{% endblock %} + +{% block page_css %} + {{ css_bundle('privacy_firefox_tos') }} +{% endblock %} + +{% set body_id = "privacy_tos" %} + +{% block site_header %}{% endblock %} + +{% block sidemenu %}{% endblock %} + +{% block article_header_logo %}{% endblock %} + +{% block js %} + {{ super() }} + {{ js_bundle('privacy_firefox') }} +{% endblock %} + +{# disable GA on Fx Privacy Notice. Bug 1576673 #} +{% block google_analytics %}{% endblock %} +{% block glean %}{% endblock %} + +{# Exclude stub attribution for in-product pages: issus 9620 #} +{% block stub_attribution %}{% endblock %} + +{% block site_footer %}{% endblock %} diff --git a/bedrock/privacy/templates/privacy/notices/firefox.html b/bedrock/privacy/templates/privacy/notices/firefox.html index 782b7a8fe0..8ab93aeea4 100644 --- a/bedrock/privacy/templates/privacy/notices/firefox.html +++ b/bedrock/privacy/templates/privacy/notices/firefox.html @@ -17,6 +17,7 @@ {# disable GA on Fx Privacy Notice. Bug 1576673 #} {% block google_analytics %}{% endblock %} +{% block glean %}{% endblock %} {# Exclude stub attribution for in-product pages: issus 9620 #} {% block stub_attribution %}{% endblock %} diff --git a/bedrock/privacy/tests/__init__.py b/bedrock/privacy/tests/__init__.py new file mode 100644 index 0000000000..448bb8652d --- /dev/null +++ b/bedrock/privacy/tests/__init__.py @@ -0,0 +1,3 @@ +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at https://mozilla.org/MPL/2.0/. diff --git a/bedrock/privacy/tests/test_views.py b/bedrock/privacy/tests/test_views.py new file mode 100644 index 0000000000..3b6afc4210 --- /dev/null +++ b/bedrock/privacy/tests/test_views.py @@ -0,0 +1,31 @@ +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at https://mozilla.org/MPL/2.0/. + +from unittest.mock import patch + +from django.http import HttpResponse +from django.test.client import RequestFactory + +from bedrock.mozorg.tests import TestCase +from bedrock.privacy import views + + +@patch("bedrock.firefox.views.l10n_utils.render", return_value=HttpResponse()) +@patch.object(views.PrivacyDocView, "get_legal_doc") +class TestFirefoxTermsOfServiceDocView(TestCase): + def test_default_template(self, render_mock, lld_mock): + req = RequestFactory().get("/privacy/notices/firefox/") + req.locale = "en-US" + view = views.firefox_notices + view(req) + template = lld_mock.call_args[0][1] + assert template == "privacy/notices/firefox.html" + + def test_tos_template(self, render_mock, lld_mock): + req = RequestFactory().get("/privacy/notices/firefox/?v=product") + req.locale = "en-US" + view = views.firefox_notices + view(req) + template = lld_mock.call_args[0][1] + assert template == "privacy/notices/firefox-tos.html" diff --git a/bedrock/privacy/views.py b/bedrock/privacy/views.py index 6c2c97d094..19edffcfb5 100644 --- a/bedrock/privacy/views.py +++ b/bedrock/privacy/views.py @@ -40,10 +40,13 @@ class PrivacyDocView(LegalDocView): class FirefoxPrivacyDocView(PrivacyDocView): def get_legal_doc(self): doc = super().get_legal_doc() - if len(doc["content"].select(".privacy-header-firefox")) > 0: - self.template_name = "privacy/notices/firefox.html" + variant = self.request.GET.get("v", None) + + if variant == "product": + self.template_name = "privacy/notices/firefox-tos.html" else: - self.template_name = "privacy/notices/firefox-old-style-notice.html" + self.template_name = "privacy/notices/firefox.html" + return doc diff --git a/media/css/legal/legal-terms-firefox-tos.scss b/media/css/legal/legal-terms-firefox-tos.scss new file mode 100644 index 0000000000..a7cad87468 --- /dev/null +++ b/media/css/legal/legal-terms-firefox-tos.scss @@ -0,0 +1,48 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. + +@use '~@mozilla-protocol/core/protocol/css/includes/lib' as *; + +// narrow top / bottom padding for modal display +#outer-wrapper > .mzp-l-content { + padding-bottom: $spacing-xl; + padding-top: $spacing-xl; +} + +// narrow max-width for readability +@media #{$mq-lg} { + .mzp-l-main { + max-width: 640px; + margin: 0 auto; + } +} + +// manual styling for markdown content. +.article-body { + h1 { + @include text-title-sm; + } + + h2, + h3, + h4 { + @include text-title-xs; + } + + blockquote { + @include text-body-lg; + } + + ul { + list-style: disc; + } + + ol { + list-style: decimal; + } + + li { + margin: $spacing-sm $spacing-lg; + } +} diff --git a/media/css/privacy/privacy-firefox-tos.scss b/media/css/privacy/privacy-firefox-tos.scss new file mode 100644 index 0000000000..4fbcb1bfbd --- /dev/null +++ b/media/css/privacy/privacy-firefox-tos.scss @@ -0,0 +1,186 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. + +@use '~@mozilla-protocol/core/protocol/css/includes/lib' as *; +@use '~@mozilla-protocol/core/protocol/css/includes/mixins/details'; + +$border: 2px solid $color-marketing-gray-20; + +// narrow top / bottom padding for modal display +#outer-wrapper > .mzp-l-content { + padding-bottom: $spacing-xl; + padding-top: $spacing-xl; +} + +// narrow max-width for readability +@media #{$mq-lg} { + .mzp-l-main { + max-width: 640px; + margin: 0 auto; + } +} + +// * -------------------------------------------------------------------------- */ +// Policy header + +.privacy-title { + margin-bottom: $spacing-lg; + + h1 { + @include text-title-xs; + font-weight: bold; + } + + a { + color: inherit; + text-decoration: none; + } +} + +// * -------------------------------------------------------------------------- */ +// Policy header intro + +.privacy-lead-in { + border-bottom: $border; + clear: both; + margin-bottom: $layout-sm; + padding-bottom: $layout-xs; + + h2 { + @include text-title-xs; + margin-top: $spacing-2xl; + } +} + +// * -------------------------------------------------------------------------- */ +// Main policy content + +.privacy-body { + > div > section > section { + border-bottom: $border; + } + + h2 { + @include text-title-xs; + margin-bottom: $layout-sm; + } + + h3 { + @include text-body-lg; + margin: $layout-sm 0; + } + + h4 { + @include text-body-md; + margin: $layout-sm 0; + } + + hr { + display: none; + } +} + +// * -------------------------------------------------------------------------- */ +// Privacy choices widget + +.data-choices { + @include clearfix; + background-color: $color-marketing-gray-20; + border-radius: $border-radius-md; + margin-top: $spacing-xl; + padding: $spacing-sm; + position: relative; + text-align: center; + + p { + @include text-body-md; + display: inline-block; + margin: 0; + padding: $spacing-sm 0; + } + + .mzp-c-button { + display: block; + margin: $spacing-md auto 0; + } + + @media #{$mq-md} { + @include grid-column-gap($spacing-lg); + display: grid; + grid-template-columns: 4fr 1fr; + + @include bidi(((text-align, left, right),)); + + .mzp-c-button { + margin: 0; + } + } +} + +// * -------------------------------------------------------------------------- */ +// Policy footer + +.privacy-footnote { + @include text-body-md; + border-top: $border; + margin-top: $spacing-2xl; + padding: $spacing-2xl 0; + + h2 { + @include text-title-xs; + } + + h3 { + @include visually-hidden; + } +} + +// * -------------------------------------------------------------------------- */ +// Summary and details widget + +.format-headings .privacy-body { + @include details; + + .is-summary { + button { + @include summary; + @include bidi(((padding-left, 0, padding-right, 0),)); + } + + button[aria-expanded='true']::before { + @include summary-open; + } + + + div { + margin-top: $spacing-sm * -1; + margin-bottom: $spacing-xl; + } + } +} + +.format-paragraphs .privacy-body { + @include details; + + .is-summary { + button { + padding: 0; + + &::after { + color: $color-link; + content: attr(data-open); + margin-left: 0.5ex; + text-decoration: underline; + } + } + + button[aria-expanded='true']::after { + content: attr(data-close); + } + + + div { + margin-top: $spacing-sm * -1; + margin-bottom: $spacing-xl; + } + } +} diff --git a/media/static-bundles.json b/media/static-bundles.json index 863e848894..b269fab2b0 100644 --- a/media/static-bundles.json +++ b/media/static-bundles.json @@ -267,6 +267,12 @@ ], "name": "legal" }, + { + "files": [ + "css/legal/legal-terms-firefox-tos.scss" + ], + "name": "legal-terms-firefox-tos" + }, { "files": [ "css/legal/builders-challenge.scss" @@ -675,6 +681,12 @@ ], "name": "privacy_protocol" }, + { + "files": [ + "css/privacy/privacy-firefox-tos.scss" + ], + "name": "privacy_firefox_tos" + }, { "files": [ "css/firefox/facebook-container.scss"