Skip to content

Commit

Permalink
Create email consent page
Browse files Browse the repository at this point in the history
  • Loading branch information
mathjazz committed May 15, 2024
1 parent 890be4a commit 6634366
Show file tree
Hide file tree
Showing 13 changed files with 270 additions and 4 deletions.
6 changes: 6 additions & 0 deletions docker/config/server.env.template
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,10 @@ FXA_CLIENT_ID=727f0251c388a993
FXA_SECRET_KEY=e43fd751ca5687d28288098e3e9b1294792ed9954008388e39b1cdaac0a1ebd6
FXA_OAUTH_ENDPOINT=https://oauth.stage.mozaws.net/v1
FXA_PROFILE_ENDPOINT=https://profile.stage.mozaws.net/v1

EMAIL_CONSENT_ENABLED = False
EMAIL_COMMUNICATIONS_MAIN_TEXT = Want to stay up to date and informed about all localization matters at Mozilla? Just hit the button below to get the latest updates, announcements about new Pontoon features, invitations to contributor events and more. We won’t spam you — promise!
EMAIL_COMMUNICATIONS_PRIVACY_NOTICE = By enabling email communications, I agree to Mozilla handling my personal information as explained in this <a href="https://www.mozilla.org/privacy/websites/">Privacy Notice</a>.
EMAIL_COMMUNICATIONS_HELP_TEXT = Get the latest updates about localization at Mozilla, announcements about new Pontoon features, invitations to contributor events and more.<br/><br/>By enabling email communications, I agree to Mozilla handling my personal information as explained in this <a href="https://www.mozilla.org/privacy/websites/">Privacy Notice</a>.

PROTOCOL_BUFFERS_PYTHON_IMPLEMENTATION=python
18 changes: 14 additions & 4 deletions docs/admin/deployment.rst
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,7 @@ you create:
Adds some additional django apps that can be helpful during day to day development.

``EMAIL_HOST``
SMTP host (default: ``'smtp.sendgrid.net'``)
SMTP host (default: ``'smtp.sendgrid.net'``).

``EMAIL_HOST_PASSWORD``
Password for the SMTP connection.
Expand All @@ -117,13 +117,23 @@ you create:
Username for the SMTP connection (default: ``'apikey'``).

``EMAIL_PORT``
SMTP port (default: ``587``)
SMTP port (default: ``587``).

``EMAIL_USE_TLS``
Use explicit TLS for the SMTP connection (default: ``True``)
Use explicit TLS for the SMTP connection (default: ``True``).

``EMAIL_USE_SSL``
Use implicit TLS for the SMTP connection (default: ``False``)
Use implicit TLS for the SMTP connection (default: ``False``).

``EMAIL_CONSENT_ENABLED``
Enables Email consent page (default: ``False``).

``EMAIL_COMMUNICATIONS_MAIN_TEXT``
Optional. Main text to use on the Email consent page, possibly explaining what type
of communication to expect.

``EMAIL_COMMUNICATIONS_PRIVACY_NOTICE``
Optional. Text and link to the privacy notice, used on the Email consent page.

``EMAIL_COMMUNICATIONS_HELP_TEXT``
Optional. Help text to use under the Email communications checkbox in user settings.
Expand Down
25 changes: 25 additions & 0 deletions pontoon/base/middleware.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
from django.conf import settings
from django.core.exceptions import PermissionDenied
from django.http import Http404, HttpResponseForbidden
from django.shortcuts import redirect
from django.urls import reverse
from django.utils.deprecation import MiddlewareMixin
from pontoon.base.utils import is_ajax
from raygun4py.middleware.django import Provider


Expand Down Expand Up @@ -34,3 +37,25 @@ def process_request(self, request):
return HttpResponseForbidden("<h1>Forbidden</h1>")

return None


class EmailConsentMiddleware(MiddlewareMixin):
def process_request(self, request):
if not settings.EMAIL_CONSENT_ENABLED:
return None

if not request.user.is_authenticated:
return None

if request.user.profile.email_consent_dismissed_at is not None:
return None

if is_ajax(request):
return None

email_consent_url = "pontoon.messaging.email_consent"
if request.path == reverse(email_consent_url):
return None

request.session["next_path"] = request.get_full_path()
return redirect(email_consent_url)
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# Generated by Django 4.2.11 on 2024-05-15 16:42

from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
("base", "0061_userprofile_email_communications_enabled"),
]

operations = [
migrations.AddField(
model_name="userprofile",
name="email_consent_dismissed_at",
field=models.DateTimeField(blank=True, null=True),
),
]
1 change: 1 addition & 0 deletions pontoon/base/models/user_profile.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ class UserProfile(models.Model):
contact_email = models.EmailField("Contact email address", blank=True, null=True)
contact_email_verified = models.BooleanField(default=False)
email_communications_enabled = models.BooleanField(default=False)
email_consent_dismissed_at = models.DateTimeField(null=True, blank=True)

# Theme
class Themes(models.TextChoices):
Expand Down
Empty file added pontoon/messaging/__init__.py
Empty file.
75 changes: 75 additions & 0 deletions pontoon/messaging/static/css/email_consent.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
body > header {
background: transparent;
border-color: transparent;
position: fixed;
width: 100%;
z-index: 10;
}

body > header.menu-opened {
border-color: var(--main-border-1);
}

#main section {
display: flex;
flex-direction: column;
height: 100vh;

background-image: var(--homepage-background-image);
background-attachment: fixed;
background-size: cover;
}

#main section .container {
display: flex;
flex: 1;
flex-direction: column;
align-items: start;
justify-content: center;
overflow: hidden;
}

#main .buttons {
display: flex;
margin-bottom: 10px;
}

#main .button {
background: transparent;
color: var(--white-1);
display: flex;
font-size: 16px;
border-radius: 2px;
width: 120px;
height: 40px;
justify-content: center;
align-items: center;
font-weight: 400;
}

#main .button.enable {
background-color: var(--status-translated);
color: var(--homepage-tour-button-color);
width: 320px;
}

#main h1 {
font-size: 64px;
margin-bottom: 10px;
}

#main p {
font-size: 22px;
font-weight: 300;
line-height: 36px;
margin-bottom: 60px;
width: 900px;
}

#main p.privacy-notice {
font-size: 14px;
}

#main p.privacy-notice a {
color: var(--status-translated);
}
26 changes: 26 additions & 0 deletions pontoon/messaging/static/js/email_consent.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
$(function () {
// Toggle user profile attribute
$('.buttons .button').click(function (e) {
e.preventDefault();
const self = $(this);

$.ajax({
url: '/dismiss-email-consent/',
type: 'POST',
data: {
csrfmiddlewaretoken: $('body').data('csrf'),
value: self.is('.enable'),
},
success: function (data) {
window.location.href = data.next;
},
error: function (request) {
if (request.responseText === 'error') {
Pontoon.endLoader('Oops, something went wrong.', 'error');
} else {
Pontoon.endLoader(request.responseText, 'error');
}
},
});
});
});
29 changes: 29 additions & 0 deletions pontoon/messaging/templates/messaging/email_consent.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
{% extends 'base.html' %}

{% block title %}Email consent{% endblock %}

{% block class %}email-consent{% endblock %}

{% block middle %}
<section id="main">
<section>
<div class="container">
<h1>Let’s keep in touch</h1>
<p class="main-text">{{ settings.EMAIL_COMMUNICATIONS_MAIN_TEXT|safe }}</p>
<div class="buttons">
<a class="button enable" href="/">Enable email communications</a>
<a class="button disable" href="/">No, thanks</a>
</div>
<p class="privacy-notice">{{ settings.EMAIL_COMMUNICATIONS_PRIVACY_NOTICE|safe }}</p>
</div>
</section>
</section>
{% endblock %}

{% block extend_css %}
{% stylesheet 'email_consent' %}
{% endblock %}

{% block extend_js %}
{% javascript 'email_consent' %}
{% endblock %}
17 changes: 17 additions & 0 deletions pontoon/messaging/urls.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
from django.urls import path

from . import views

urlpatterns = [
# Email consent
path(
"email-consent/",
views.email_consent,
name="pontoon.messaging.email_consent",
),
path(
"dismiss-email-consent/",
views.dismiss_email_consent,
name="pontoon.messaging.dismiss_email_consent",
),
]
43 changes: 43 additions & 0 deletions pontoon/messaging/views.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import json

from django.contrib.auth.decorators import login_required
from django.db import transaction
from django.http import JsonResponse
from django.shortcuts import render
from django.utils import timezone
from django.views.decorators.http import require_POST


def email_consent(request):
return render(
request,
"messaging/email_consent.html",
)


@login_required(redirect_field_name="", login_url="/403")
@require_POST
@transaction.atomic
def dismiss_email_consent(request):
value = request.POST.get("value", None)

if not value:
return JsonResponse(
{
"status": False,
"message": "Bad Request: Value not set",
},
status=400,
)

profile = request.user.profile
profile.email_communications_enabled = json.loads(value)
profile.email_consent_dismissed_at = timezone.now()
profile.save()

return JsonResponse(
{
"status": True,
"next": request.session.get("next_path", "/"),
}
)
15 changes: 15 additions & 0 deletions pontoon/settings/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -212,6 +212,11 @@ def _default_from_email():
EMAIL_HOST_PASSWORD = os.environ.get(
"EMAIL_HOST_PASSWORD", os.environ.get("SENDGRID_PASSWORD", "")
)
EMAIL_CONSENT_ENABLED = os.environ.get("EMAIL_CONSENT_ENABLED", "False") != "False"
EMAIL_COMMUNICATIONS_MAIN_TEXT = os.environ.get("EMAIL_COMMUNICATIONS_MAIN_TEXT", "")
EMAIL_COMMUNICATIONS_PRIVACY_NOTICE = os.environ.get(
"EMAIL_COMMUNICATIONS_PRIVACY_NOTICE", ""
)
EMAIL_COMMUNICATIONS_HELP_TEXT = os.environ.get("EMAIL_COMMUNICATIONS_HELP_TEXT", "")

# Log emails to console if the SendGrid credentials are missing.
Expand All @@ -233,6 +238,7 @@ def _default_from_email():
"pontoon.insights",
"pontoon.localizations",
"pontoon.machinery",
"pontoon.messaging",
"pontoon.projects",
"pontoon.sync",
"pontoon.tags",
Expand Down Expand Up @@ -287,6 +293,7 @@ def _default_from_email():
"django.contrib.messages.middleware.MessageMiddleware",
"django.middleware.clickjacking.XFrameOptionsMiddleware",
"csp.middleware.CSPMiddleware",
"pontoon.base.middleware.EmailConsentMiddleware",
)

CONTEXT_PROCESSORS = (
Expand Down Expand Up @@ -511,6 +518,10 @@ def _default_from_email():
"source_filenames": ("css/homepage.css",),
"output_filename": "css/homepage.min.css",
},
"email_consent": {
"source_filenames": ("css/email_consent.css",),
"output_filename": "css/email_consent.min.css",
},
}

PIPELINE_JS = {
Expand Down Expand Up @@ -645,6 +656,10 @@ def _default_from_email():
"source_filenames": ("js/homepage.js",),
"output_filename": "js/homepage.min.js",
},
"email_consent": {
"source_filenames": ("js/email_consent.js",),
"output_filename": "js/email_consent.min.js",
},
}

PIPELINE = {
Expand Down
1 change: 1 addition & 0 deletions pontoon/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ class LocaleConverter(StringConverter):
path("", include("pontoon.api.urls")),
path("", include("pontoon.homepage.urls")),
path("", include("pontoon.uxactionlog.urls")),
path("", include("pontoon.messaging.urls")),
# Team page: Must be at the end
path("<locale:locale>/", team, name="pontoon.teams.team"),
]

0 comments on commit 6634366

Please sign in to comment.