Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Mise en avant newsletter #469

Open
wants to merge 7 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 18 additions & 0 deletions config/settings/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -193,6 +193,7 @@
"django.contrib.messages.context_processors.messages",
"envergo.utils.context_processors.settings_context",
"envergo.utils.context_processors.multi_sites_context",
"envergo.utils.context_processors.newsletter_context",
"envergo.analytics.context_processors.analytics",
],
},
Expand Down Expand Up @@ -397,3 +398,20 @@
}

OPS_MATTERMOST_HANDLERS = env.list("DJANGO_OPS_MATTERMOST_HANDLERS", default=[])

BREVO = {
"API_URL": env("BREVO_API_URL", default="https://api.brevo.com/v3/"),
"API_KEY": env("BREVO_API_KEY", default=None),
"NEWSLETTER_LISTS": {
"instructeur": env("BREVO_NEWSLETTER_LIST_INSTRUCTEUR", default=None),
"amenageur": env("BREVO_NEWSLETTER_LIST_AMENAGEUR", default=None),
"geometre": env("BREVO_NEWSLETTER_LIST_GEOMETRE", default=None),
"bureau": env("BREVO_NEWSLETTER_LIST_BUREAU", default=None),
"architecte": env("BREVO_NEWSLETTER_LIST_ARCHITECTE", default=None),
"particulier": env("BREVO_NEWSLETTER_LIST_PARTICULIER", default=None),
"autre": env("BREVO_NEWSLETTER_LIST_AUTRE", default=None),
},
"NEWSLETTER_DOUBLE_OPT_IN_TEMPLATE_ID": env(
"BREVO_NEWSLETTER_DOUBLE_OPT_IN_TEMPLATE_ID", default=None
),
}
7 changes: 7 additions & 0 deletions config/urls_amenagement.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
from config.urls import handler500 # noqa
from envergo.evaluations.views import ShortUrlAdminRedirectView
from envergo.geodata.views import CatchmentAreaDebug
from envergo.users.views import NewsletterDoubleOptInConfirmation, NewsletterOptIn

from .urls import urlpatterns as common_urlpatterns

Expand All @@ -20,4 +21,10 @@
path(_("moulinette/"), include("envergo.moulinette.urls_amenagement")),
path(_("geo/"), include("envergo.geodata.urls")),
path("demonstrateur-bv/", CatchmentAreaDebug.as_view(), name="2150_debug"),
path("newsletter/", NewsletterOptIn.as_view(), name="newsletter_opt_in"),
path(
"newsletter/confirmation/",
NewsletterDoubleOptInConfirmation.as_view(),
name="newsletter_confirmation",
),
] + common_urlpatterns
31 changes: 31 additions & 0 deletions envergo/static/sass/project.scss
Original file line number Diff line number Diff line change
Expand Up @@ -1925,3 +1925,34 @@ main.demonstrateur_2150 {
#contacts_and_links {
background-color: var(--grey-975-100);
}

// override the dsfr styles for the newsletter form
.fr-follow {
.fr-follow__newsletter .fr-label {
position: static;
width: auto;
height: auto;
padding: initial;
margin: initial;
overflow: visible;
clip: auto;
white-space: normal;
border: initial;
display: block;
font-weight: 700;
line-height: 1.5rem;
}

.fr-grid-row > :first-child:last-child > div {
justify-content: space-around;
}

.fr-follow__newsletter form {
min-width: 350px;

button {
margin-left: auto;
display: block;
}
}
}
2 changes: 1 addition & 1 deletion envergo/templates/_messages.html
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{% if messages %}
<div class="messages fr-pt-5w">
<div class="messages fr-py-5w">
{% for message in messages %}
<div class="fr-alert fr-alert--{{ message.tags|default:'info' }} fr-alert--sm">
<p>{{ message|safe }}</p>
Expand Down
37 changes: 37 additions & 0 deletions envergo/templates/amenagement/_newsletter_opt_in.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
<div class="fr-follow fr-py-8w hide-print">
<div class="fr-container">
<div class="fr-grid-row">
<div class="fr-col-12">
<div class="fr-follow__newsletter">
<div>
<h2 class="fr-h5">Abonnez-vous à notre lettre d’information</h2>
<p class="fr-text--sm">
Tous les 3 mois, recevez nos actualités sur la réglementation environnementale et les évolutions du service EnvErgo en lien avec votre activité professionnelle.
</p>
<p id="newsletter-email-hint-text"
class="fr-hint-text fr-hidden fr-unhidden-md">
En renseignant votre adresse électronique, vous acceptez de recevoir nos actualités par courriel. Désinscription en un clic.
</p>
</div>
<div>
<form action="{% url 'newsletter_opt_in' %}" method="post">
{% csrf_token %}

<input type="hidden"
name="redirect_url"
value="{{ request.build_absolute_uri }}" />
{% include '_field_snippet.html' with field=newsletter_opt_in_form.type %}
{% include '_field_snippet.html' with field=newsletter_opt_in_form.email %}
<button class="fr-btn fr-mt-2w"
title="S‘abonner à notre lettre d’information"
type="submit">M'abonner</button>
</form>
<p id="newsletter-email-hint-text" class="fr-hint-text fr-hidden-md">
En renseignant votre adresse électronique, vous acceptez de recevoir nos actualités par courriel. Désinscription en un clic.
</p>
</div>
</div>
</div>
</div>
</div>
</div>
3 changes: 2 additions & 1 deletion envergo/templates/amenagement/base.html
Original file line number Diff line number Diff line change
Expand Up @@ -23,13 +23,14 @@
{% endblock %}

{% block after-content %}
<section class="fr-py-8w alt hide-print">
<section class="fr-py-8w hide-print">
<div class="fr-container">
<div class="fr-grid-row fr-grid-row--center">
<div class="fr-col fr-col-md-8 fr-col-lg-7 fr-col-xl-6">{% include 'amenagement/_learn_more.html' %}</div>
</div>
</div>
</section>
{% include 'amenagement/_newsletter_opt_in.html' %}
{% endblock %}

{% block footer %}
Expand Down
11 changes: 11 additions & 0 deletions envergo/templates/amenagement/moulinette/result.html
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,17 @@ <h2>
</section>
{% endblock coming_soon_regulations %}

{% block after_result_content %}
<section class="fr-py-8w hide-print">
<div class="fr-container">
<div class="fr-grid-row fr-grid-row--center">
<div class="fr-col">{% include 'amenagement/_learn_more.html' %}</div>
</div>
</div>
</section>
{% include 'amenagement/_newsletter_opt_in.html' %}
{% endblock after_result_content %}

{% block after-content %}
{% include 'amenagement/moulinette/_actions_banner.html' %}
{% endblock %}
Expand Down
2 changes: 2 additions & 0 deletions envergo/templates/moulinette/base_result.html
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@
<section class="fr-py-4w fr-px-4w">
{% block result %}{% endblock %}
</section>
{% block after_result_content %}
{% endblock after_result_content %}
</div>

<div id="project-summary" class="fr-px-2w">
Expand Down
39 changes: 39 additions & 0 deletions envergo/users/forms.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
from django import forms
from django.contrib.auth.forms import UserCreationForm
from django.forms.widgets import Select
from django.utils.translation import gettext_lazy as _

from envergo.users.models import User
Expand Down Expand Up @@ -40,3 +41,41 @@ def __init__(self, *args, **kwargs):
def clean_email(self):
email = self.cleaned_data["email"]
return email.lower()


class AllowDisabledSelect(Select):
"""A select widget (drop down list) that will disable options where the value is set to an empty string"""

def create_option(
self, name, value, label, selected, index, subindex=None, attrs=None
):
option_dict = super().create_option(
name, value, label, selected, index, subindex=subindex, attrs=attrs
)
if not value:
option_dict["attrs"]["disabled"] = "disabled"
return option_dict


class NewsletterOptInForm(forms.Form):
type = forms.ChoiceField(
required=True,
label="Vous êtes",
choices=(
("", "Sélectionner une option"),
("instructeur", "Service instructeur urbanisme"),
("amenageur", "Aménageur"),
("geometre", "Géomètre"),
("bureau", "Bureau d'études"),
("architecte", "Architecte"),
("particulier", "Particulier"),
("autre", "Autre"),
),
widget=AllowDisabledSelect(attrs={"placeholder": "Sélectionnez votre type"}),
)
email = forms.EmailField(
required=True,
label="Votre adresse email",
widget=forms.EmailInput(attrs={"placeholder": "ex. : [email protected]"}),
)
redirect_url = forms.CharField(required=True, widget=forms.HiddenInput())
97 changes: 95 additions & 2 deletions envergo/users/views.py
Original file line number Diff line number Diff line change
@@ -1,15 +1,23 @@
import logging

import requests
from braces.views import AnonymousRequiredMixin, MessageMixin
from django.conf import settings
from django.contrib import messages
from django.contrib.auth import login
from django.contrib.auth.tokens import default_token_generator
from django.http import HttpResponseRedirect
from django.urls import reverse, reverse_lazy
from django.utils.http import urlsafe_base64_decode
from django.views.generic import CreateView, TemplateView
from django.views import View
from django.views.generic import CreateView, FormView, TemplateView

from envergo.users.forms import RegisterForm
from envergo.users.forms import NewsletterOptInForm, RegisterForm
from envergo.users.models import User
from envergo.users.tasks import send_account_activation_email

logger = logging.getLogger(__name__)


class Register(AnonymousRequiredMixin, CreateView):
"""Allow users to create new accounts."""
Expand Down Expand Up @@ -89,3 +97,88 @@ def get(self, request, *args, **kwargs):
return HttpResponseRedirect(redirect_url)

return super().get(request, *args, **kwargs)


class NewsletterOptIn(FormView):
form_class = NewsletterOptInForm

def form_valid(self, form):
"""Send the form data to Brevo API."""
if not settings.BREVO.get("API_KEY"):
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Je m'interroge sur cette gestion d'erreur. S'il y a un problème de settings manquant, c'est l'équipe EnvErgo qui devrait recevoir une erreur, pas l'utilisateur. Ici, le message affiché laisse penser à l'utilisateur qu'il y a une erreur de son côté ce qui n'est pas le cas. Au moins ajouter un log d'erreur avec logger.error ?

logger.error(
"No Brevo API key found in settings for newsletter opt-in",
extra={"form": form},
)
form.add_error(
None,
"Le service n'est pas disponible actuellement. Merci de réessayer plus tard.",
)
return self.form_invalid(form)

api_url = f"{settings.BREVO['API_URL']}contacts/doubleOptinConfirmation"
headers = {
"Content-Type": "application/json",
"api-key": settings.BREVO["API_KEY"],
}

body = {
"email": form.cleaned_data["email"],
"includeListIds": [
settings.BREVO["NEWSLETTER_LISTS"].get(
form.cleaned_data["type"],
settings.BREVO["NEWSLETTER_LISTS"]["autre"],
)
],
"attributes": {
"TYPE": form.cleaned_data["type"],
"OPT_IN_NEWSLETTER": True,
},
"redirectionUrl": self.request.build_absolute_uri(
reverse("newsletter_confirmation")
),
"templateId": settings.BREVO["NEWSLETTER_DOUBLE_OPT_IN_TEMPLATE_ID"],
}

response = requests.post(api_url, json=body, headers=headers)

if 200 <= response.status_code < 400:
messages.info(
self.request,
"Vous avez reçu un e-mail de confirmation pour valider votre inscription à la newsletter.",
)
res = HttpResponseRedirect(form.cleaned_data["redirect_url"])
else:
logger.error(
"Error while creating/updating contact via Brevo API",
extra={"response": response},
)
res = self.form_invalid(form)

return res

def form_invalid(self, form):
message = "Nous n'avons pas pu enregistrer votre inscription à la newsletter."
# Handle field-specific errors
for field, errors in form.errors.items():
if field != "__all__":
field_label = form[field].label
for error in errors:
message += f"<br/>{field_label} : {error}"

# Handle non-field errors
if "__all__" in form.errors:
for error in form.errors["__all__"]:
message += f"<br/>{error}"

messages.error(self.request, message)
return HttpResponseRedirect(
form.cleaned_data.get("redirect_url", reverse("home"))
)


class NewsletterDoubleOptInConfirmation(View):
def get(self, request, *args, **kwargs):
messages.success(
request, "Votre inscription à la newsletter EnvErgo est confirmée !"
)
return HttpResponseRedirect(reverse("home"))
8 changes: 8 additions & 0 deletions envergo/utils/context_processors.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
from django.conf import settings
from django.urls import NoReverseMatch, reverse

from envergo.users.forms import NewsletterOptInForm


def settings_context(_request):
"""Settings available by default to the templates context."""
Expand Down Expand Up @@ -45,3 +47,9 @@ def multi_sites_context(_request):
return {
"base_template": _request.base_template,
}


def newsletter_context(_request):
return {
"newsletter_opt_in_form": NewsletterOptInForm(),
}
Loading