Skip to content

Commit 177f636

Browse files
[18.0][MIG] website_mass_mailing_double_opt_in: Migration to v18 (#235)
* [18.0][MIG] website_mass_mailing_double_opt_in: Migration to v18 * [18.0][IMP] Imlemented the code to fixed the CI tests * [18.0][IMP] Changed the module description * [18.0][REM] Removed README folder * Simplify module summary in __manifest__.py Removed detailed description of the double opt-in process. * [18.0][IMP] website_mass_mailing_double_opt_in: Impplemented the translation PO file * update German translation * [18.0][IMP] website_mass_mailing_double_opt_in: Implemented the controller method and mail template * [18.0][IMP] website_mass_mailing_double_opt_in: Implmented the confirmation mail controller method * [18.0][IMP] website_mass_mailing_double_opt_in: Fix the CI tests * [18.0][IMP] website_mass_mailing_double_opt_in: Implemented email template * [18.0][IMP] website_mass_mailing_double_opt_in: Implemented the index.html file * [18.0][IMP] website_mass_mailing_double_opt_in: Implemented PO file and Created method for mail content * Update author information in manifest file * [18.0][IMP] website_mass_mailing_double_opt_in_nitrokey: Implemented email translated values * [18.0][IMP] website_mass_mailing_double_opt_in_nitrokey: Implemented email translated values --------- Co-authored-by: jans23 <jans23@users.noreply.github.com>
1 parent 3a587d5 commit 177f636

20 files changed

Lines changed: 1214 additions & 0 deletions
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
==================================
2+
Website Mass Mailing Double opt-in
3+
==================================
4+
5+
Configuration
6+
=============
7+
8+
To configure this module, you need to:
9+
10+
#. Go to **Website > Edit Page > Select Any Newsletter Template > Save**
11+
12+
Usage
13+
=====
14+
15+
This module extends Odoo's website mass mailing capabilities by implementing
16+
a double opt-in subscription process for newsletter signups.
17+
18+
When visitors subscribe to a newsletter through the website, they must confirm
19+
their subscription via a confirmation email before being added to the mailing
20+
list. This two-step verification process helps ensure legitimate subscriptions
21+
and compliance with email marketing regulations (like GDPR)
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
2+
3+
from . import controllers, models
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
2+
3+
{
4+
"name": "Website Mass Mailing Double opt-in",
5+
"version": "18.0.1.0.0",
6+
"category": "Website",
7+
"author": "Nitrokey GmbH",
8+
"website": "https://github.com/nitrokey/odoo-modules",
9+
"license": "AGPL-3",
10+
"summary": """
11+
This module extends Odoo's website mass mailing capabilities by implementing
12+
a double opt-in subscription process for newsletter signups.
13+
""",
14+
"depends": [
15+
"website_mass_mailing",
16+
],
17+
"data": [
18+
"security/mass_mailing_security.xml",
19+
"security/ir.model.access.csv",
20+
"data/mail_template.xml",
21+
"views/mass_mailing_view.xml",
22+
"views/invalid_confirmation.xml",
23+
"views/subscribe_template.xml",
24+
],
25+
"assets": {
26+
"web.assets_tests": [
27+
"website_mass_mailing_double_opt_in/static/tests/**/*",
28+
"website_mass_mailing_double_opt_in/static/tests/tours/**/*",
29+
],
30+
},
31+
"installable": True,
32+
}
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
2+
3+
from . import main
Lines changed: 142 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,142 @@
1+
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
2+
3+
import logging
4+
5+
from odoo import _
6+
from odoo.http import Controller, request, route
7+
8+
from odoo.addons.mass_mailing.controllers.main import MassMailController
9+
10+
_logger = logging.getLogger(__name__)
11+
12+
13+
class MassMailController(MassMailController):
14+
@route("/website_mass_mailing/subscribe", type="json", website=True, auth="public")
15+
def subscribe(self, list_id, value, subscription_type, **post):
16+
if not request.env["ir.http"]._verify_request_recaptcha_token(
17+
"website_mass_mailing_subscribe"
18+
):
19+
return {
20+
"toast_type": "danger",
21+
"toast_content": _("Suspicious activity detected by Google reCaptcha."),
22+
}
23+
24+
fname = self._get_fname(subscription_type)
25+
# Customisation Start
26+
if subscription_type == "email":
27+
subscription = request.env["mailing.subscription"].sudo()
28+
# add email to session
29+
request.session["mass_mailing_email"] = (
30+
subscription.double_opt_in_subscribe(
31+
list_id,
32+
value,
33+
language=post.get("language") or request.lang.code,
34+
)
35+
)
36+
else:
37+
self.subscribe_to_newsletter(subscription_type, value, list_id, fname)
38+
# Customisation End
39+
return {
40+
"toast_type": "success",
41+
"toast_content": _("Thanks for subscribing!"),
42+
}
43+
44+
45+
class ConsentController(Controller):
46+
def consent_success(self):
47+
"""Successful consent to redirect to different sides if required"""
48+
base_url = request.httprequest.host_url.rstrip("/")
49+
redirect_url = base_url + "/subscribed"
50+
return request.redirect(redirect_url)
51+
52+
def consent_failure(self):
53+
"""Redirect to a public invalid page"""
54+
return request.redirect("/newsletter/invalid")
55+
56+
@route("/newsletter/invalid", type="http", auth="public", website=True)
57+
def invalid_page(self, **kwargs):
58+
"""Public route for invalid page (uses website layout)"""
59+
return request.render(
60+
"website_mass_mailing_double_opt_in.invalid_subscription_confirmation_template"
61+
)
62+
63+
@route("/subscribed", type="http", auth="public", website=True)
64+
def subscribed(self, **kwargs):
65+
return request.render("website_mass_mailing_double_opt_in.subscribe_newsletter")
66+
67+
def _prepare_mail_content(self, mailing_list_contact, language):
68+
"""Newsletter Subscribed email template content"""
69+
if language == "de_DE":
70+
# Get translated strings
71+
greeting = "Hallo,"
72+
subject = "Sie haben den Newsletter abonniert"
73+
thank_you = "vielen Dank für die Anmeldung zum Newsletter."
74+
best_regards = "Viele Grüße,"
75+
team = "Ihr Team"
76+
elif language == "en_US":
77+
greeting = "Hi!"
78+
subject = "You Have Subscribed to the Newsletter"
79+
thank_you = "Thank you for subscribing the newsletter."
80+
best_regards = "Best regards,"
81+
team = "your team"
82+
else:
83+
# Get translated strings
84+
greeting = _("Hi!")
85+
subject = _("You Have Subscribed to the Newsletter")
86+
thank_you = _("Thank you for subscribing the newsletter.")
87+
best_regards = _("Best regards,")
88+
team = _("your team")
89+
mail_values = {
90+
"subject": subject,
91+
"body_html": f"""
92+
<div>
93+
<p>{greeting}</p>
94+
<p>{thank_you}</p>
95+
<br />
96+
<p>{best_regards}<br />{team}</p>
97+
</div>
98+
""",
99+
"email_from": request.env.user.partner_id.email,
100+
"email_to": mailing_list_contact.contact_id.email,
101+
"state": "outgoing",
102+
}
103+
return mail_values
104+
105+
@route(
106+
"/newsletter/confirmation/<access_token>",
107+
type="http",
108+
auth="none",
109+
website=True,
110+
)
111+
def consent(self, access_token, **kwargs):
112+
try:
113+
mailing_list_contact = (
114+
request.env["mailing.subscription"]
115+
.sudo()
116+
.search([("access_token", "=", access_token)], limit=1)
117+
)
118+
if not mailing_list_contact:
119+
_logger.warning(
120+
"No mailing subscription found for access token: %s", access_token
121+
)
122+
return self.consent_failure()
123+
124+
mailing_list_contact.write({"opt_out": False})
125+
126+
# Send email with explicit user context for template rendering
127+
language = (
128+
mailing_list_contact.mail_language or request.lang.code or "en_US"
129+
)
130+
if not request.env.user:
131+
request.env.user = mailing_list_contact.create_uid
132+
try:
133+
mail_values = self._prepare_mail_content(mailing_list_contact, language)
134+
mail = request.env["mail.mail"].sudo().create(mail_values)
135+
mail.with_context(**{"lang": language}).sudo().send()
136+
except Exception as e:
137+
_logger.warning("Send mail issue: %s", e)
138+
return self.consent_success()
139+
140+
except Exception as e:
141+
_logger.error("Error processing newsletter confirmation: %s", str(e))
142+
return self.consent_failure()
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
<?xml version="1.0" encoding="utf-8" ?>
2+
<odoo>
3+
<!-- Confirm Subscription Email Template -->
4+
<record id="newsletter_confirmation_request_template" model="mail.template">
5+
<field name="name">Newsletter Subscription</field>
6+
<field name="subject">Confirm your newsletter subscription</field>
7+
<field name="model_id" ref="mass_mailing.model_mailing_subscription" />
8+
<field name="email_to">{{ object.contact_id.email }}</field>
9+
<field name="body_html" type="html">
10+
<div style="margin: -10px -10px; padding:50px 30px 50px 30px; height:100%;">
11+
<div style="margin:0 auto; max-width:660px;">
12+
<div
13+
style="float: left; background-color: #FFFFFF; padding:10px 30px 10px 30px; border: 1px solid #DDDDDD;"
14+
>
15+
<div style="float: left; max-width:470px;">
16+
<p
17+
style="line-height: 21px; font-family: Helvetica, Verdana, Arial, sans-serif; font-size: 12px;"
18+
>
19+
<strong
20+
style="line-height: 21px; font-family: Helvetica, Verdana, Arial, sans-serif; font-size: 18px;"
21+
>Confirm your newsletter subscription
22+
</strong>
23+
</p>
24+
<div
25+
style="line-height: 21px; min-height: 100px; font-family: Helvetica, Verdana, Arial, sans-serif; font-size: 12px;"
26+
>
27+
<p
28+
style="line-height: 21px; font-family: Helvetica, Verdana, Arial, sans-serif; font-size: 12px;"
29+
>Thanks for subscribing to our email list.
30+
</p>
31+
<p
32+
style="line-height: 21px; font-family: Helvetica, Verdana, Arial, sans-serif; font-size: 12px;"
33+
>You allow to be in touch with you via email for the purpose of news, updates
34+
and product information. If you wish to withdraw your consent and stop hearing from
35+
us, simply click the unsubscribe link at the bottom of every email. By subscribing,
36+
you agree that we may process your information in accordance with our data privacy policy.
37+
</p>
38+
<p
39+
style="line-height: 21px; font-family: Helvetica, Verdana, Arial, sans-serif; font-size: 12px;"
40+
>Please confirm your subscription by clicking the button below:
41+
</p>
42+
<p
43+
style="line-height: 21px; font-family: Helvetica, Verdana, Arial, sans-serif; font-size: 12px; margin-bottom: 25px; padding: 15px; text-align: center;"
44+
>
45+
<a
46+
t-attf-href="/newsletter/confirmation/{{ object.access_token }}"
47+
style="background-color: #449d44; padding: 12px; font-weight: 12px; text-decoration: none; color: #fff; border-radius: 5px; font-size:16px;"
48+
>Confirm
49+
</a>
50+
</p>
51+
<p
52+
style="line-height: 21px; font-family: Helvetica, Verdana, Arial, sans-serif; font-size: 12px;"
53+
>Thank you,<br />
54+
</p>
55+
</div>
56+
</div>
57+
</div>
58+
</div>
59+
</div>
60+
</field>
61+
</record>
62+
</odoo>

0 commit comments

Comments
 (0)