diff --git a/mass_mailing_custom_unsubscribe/README.rst b/mass_mailing_custom_unsubscribe/README.rst index 4a1129d..e585b58 100644 --- a/mass_mailing_custom_unsubscribe/README.rst +++ b/mass_mailing_custom_unsubscribe/README.rst @@ -33,6 +33,9 @@ This addon extends the unsubscription form to let you: mass mailing. - Provide proof on why you are sending mass mailings to a given contact, as required by the GDPR in Europe. +- Handle discrete unsubscriptions from other recipients that are not a mailing + list. On standard module, unsubscriptions from these recipients directly + include that mail on the general blacklist. **Table of contents** @@ -50,6 +53,15 @@ they are going to unsubscribe. To do it: #. If *Details required* is enabled, they will have to fill a text area to continue. +For having discrete unsubscriptions from other recipients than the mailing +lists, you need to add a glue module that adds 2 fields in the associated +model: + +- `opt_out`. +- Either `email` or `email_from`. + +See `mass_mailing_custom_unsubscribe_event` for an example. + Usage ===== @@ -98,6 +110,7 @@ Contributors * Jairo Llopis * David Vidal * Ernesto Tejeda + * Pedro M. Baeza Maintainers ~~~~~~~~~~~ diff --git a/mass_mailing_custom_unsubscribe/__manifest__.py b/mass_mailing_custom_unsubscribe/__manifest__.py index 15bd983..340362a 100644 --- a/mass_mailing_custom_unsubscribe/__manifest__.py +++ b/mass_mailing_custom_unsubscribe/__manifest__.py @@ -1,11 +1,12 @@ # Copyright 2016 Jairo Llopis # Copyright 2018 David Vidal +# Copyright 2020 Tecnativa - Pedro M. Baeza # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). { 'name': 'Customizable unsubscription process on mass mailing emails', 'summary': 'Know and track (un)subscription reasons, GDPR compliant', 'category': 'Marketing', - 'version': '12.0.1.0.0', + 'version': '12.0.1.0.1', 'depends': [ 'mass_mailing', ], diff --git a/mass_mailing_custom_unsubscribe/controllers/main.py b/mass_mailing_custom_unsubscribe/controllers/main.py index a4b62a1..5b5fc81 100644 --- a/mass_mailing_custom_unsubscribe/controllers/main.py +++ b/mass_mailing_custom_unsubscribe/controllers/main.py @@ -1,5 +1,6 @@ # Copyright 2015 Antiun IngenierĂ­a S.L. (http://www.antiun.com) # Copyright 2016 Jairo Llopis +# Copyright 2020 Tecnativa - Pedro M. Baeza # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). import logging @@ -43,6 +44,7 @@ class CustomUnsubscribe(MassMailController): "Called `mailing()` with: %r", (mailing_id, email, res_id, token, post)) reasons = request.env["mail.unsubscription.reason"].search([]) + res_id = res_id and int(res_id) try: # Check if we already have a reason for unsubscription reason_id = int(post["reason_id"]) @@ -53,23 +55,38 @@ class CustomUnsubscribe(MassMailController): # Unsubscribe, saving reason and details by context details = post.get("details", False) self._add_extra_context(mailing_id, res_id, reason_id, details) - # You could get a DetailsRequiredError here, but only if HTML5 - # validation fails, which should not happen in modern browsers - result = super().mailing( - mailing_id, email, res_id, token=token, **post) - result.qcontext.update({"reasons": reasons}) - # update list_ids taking into account not_cross_unsubscriptable - # field mailing_obj = request.env['mail.mass_mailing'] - mailing = mailing_obj.sudo().browse(mailing_id) - if mailing.mailing_model_real == 'mail.mass_mailing.contact': - result.qcontext.update({ - "list_ids": result.qcontext["list_ids"].filtered( - lambda mailing_list: - not mailing_list.not_cross_unsubscriptable or - mailing_list in mailing.contact_list_ids - ) + mass_mailing = mailing_obj.sudo().browse(mailing_id) + model = mass_mailing.mailing_model_real + if "opt_out" in request.env[model]._fields: + mass_mailing.update_opt_out_other(email, [res_id], True) + result = request.render("mass_mailing.page_unsubscribed", { + "email": email, + "mailing_id": mailing_id, + "res_id": res_id, + "show_blacklist_button": request.env[ + "ir.config_parameter" + ].sudo().get_param( + "mass_mailing.show_blacklist_buttons" + ), }) + result.qcontext.update({"reasons": reasons}) + else: + # You could get a DetailsRequiredError here, but only if HTML5 + # validation fails, which should not happen in modern browsers + result = super().mailing( + mailing_id, email, res_id, token=token, **post) + if model == "mail.mass_mailing.contact": + # update list_ids taking into account + # not_cross_unsubscriptable field + result.qcontext.update({ + "reasons": reasons, + "list_ids": result.qcontext["list_ids"].filtered( + lambda mailing_list: + not mailing_list.not_cross_unsubscriptable or + mailing_list in mass_mailing.contact_list_ids + ) + }) return result @route() diff --git a/mass_mailing_custom_unsubscribe/models/mail_mass_mailing.py b/mass_mailing_custom_unsubscribe/models/mail_mass_mailing.py index f992702..0fd71a2 100644 --- a/mass_mailing_custom_unsubscribe/models/mail_mass_mailing.py +++ b/mass_mailing_custom_unsubscribe/models/mail_mass_mailing.py @@ -1,7 +1,9 @@ # Copyright 2016 Jairo Llopis +# Copyright 2020 Tecnativa - Pedro M. Baeza # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). -from odoo import models +from odoo import models, tools +from odoo.tools.safe_eval import safe_eval from itertools import groupby @@ -31,3 +33,42 @@ class MailMassMailing(models.Model): "action": action, }) return super().update_opt_out(email, list_ids, value) + + def update_opt_out_other(self, email, res_ids, value): + """Method for changing unsubscription for models with opt_out field.""" + model = self.env[self.mailing_model_real].with_context( + active_test=False) + action = "unsubscription" if value else "subscription" + if 'opt_out' in model._fields: + email_fname = 'email_from' + if 'email' in model._fields: + email_fname = 'email' + records = model.search([ + ('id', 'in', res_ids), (email_fname, 'ilike', email)]) + records.write({'opt_out': value}) + for res_id in res_ids: + self.env["mail.unsubscription"].create({ + "email": email, + "mass_mailing_id": self.id, + "unsubscriber_id": "%s,%d" % ( + self.mailing_model_real, res_id), + "action": action, + }) + + def _get_opt_out_list(self): + """Handle models with opt_out field for excluding them.""" + self.ensure_one() + model = self.env[self.mailing_model_real].with_context( + active_test=False) + if (self.mailing_model_real != "mail.mass_mailing.contact" and + 'opt_out' in model._fields): + email_fname = 'email_from' + if 'email' in model._fields: + email_fname = 'email' + domain = safe_eval(self.mailing_domain) + domain = [('opt_out', '=', True)] + domain + recs = self.env[self.mailing_model_real].search(domain) + normalized_email = ( + tools.email_split(c[email_fname]) for c in recs) + return set(e[0].lower() for e in normalized_email if e) + return super()._get_opt_out_list() diff --git a/mass_mailing_custom_unsubscribe/readme/CONFIGURE.rst b/mass_mailing_custom_unsubscribe/readme/CONFIGURE.rst index e793672..f9ce968 100644 --- a/mass_mailing_custom_unsubscribe/readme/CONFIGURE.rst +++ b/mass_mailing_custom_unsubscribe/readme/CONFIGURE.rst @@ -5,3 +5,12 @@ they are going to unsubscribe. To do it: #. Create / edit / remove / sort as usual. #. If *Details required* is enabled, they will have to fill a text area to continue. + +For having discrete unsubscriptions from other recipients than the mailing +lists, you need to add a glue module that adds 2 fields in the associated +model: + +- `opt_out`. +- Either `email` or `email_from`. + +See `mass_mailing_custom_unsubscribe_event` for an example. diff --git a/mass_mailing_custom_unsubscribe/readme/CONTRIBUTORS.rst b/mass_mailing_custom_unsubscribe/readme/CONTRIBUTORS.rst index b4d6ec4..c2d0561 100644 --- a/mass_mailing_custom_unsubscribe/readme/CONTRIBUTORS.rst +++ b/mass_mailing_custom_unsubscribe/readme/CONTRIBUTORS.rst @@ -5,3 +5,4 @@ * Jairo Llopis * David Vidal * Ernesto Tejeda + * Pedro M. Baeza diff --git a/mass_mailing_custom_unsubscribe/readme/DESCRIPTION.rst b/mass_mailing_custom_unsubscribe/readme/DESCRIPTION.rst index 6806342..c289fc0 100644 --- a/mass_mailing_custom_unsubscribe/readme/DESCRIPTION.rst +++ b/mass_mailing_custom_unsubscribe/readme/DESCRIPTION.rst @@ -6,3 +6,6 @@ This addon extends the unsubscription form to let you: mass mailing. - Provide proof on why you are sending mass mailings to a given contact, as required by the GDPR in Europe. +- Handle discrete unsubscriptions from other recipients that are not a mailing + list. On standard module, unsubscriptions from these recipients directly + include that mail on the general blacklist. diff --git a/mass_mailing_custom_unsubscribe/static/description/index.html b/mass_mailing_custom_unsubscribe/static/description/index.html index d6c1658..415a40c 100644 --- a/mass_mailing_custom_unsubscribe/static/description/index.html +++ b/mass_mailing_custom_unsubscribe/static/description/index.html @@ -376,6 +376,9 @@ from a different one. mass mailing.
  • Provide proof on why you are sending mass mailings to a given contact, as required by the GDPR in Europe.
  • +
  • Handle discrete unsubscriptions from other recipients that are not a mailing +list. On standard module, unsubscriptions from these recipients directly +include that mail on the general blacklist.
  • Table of contents

    @@ -402,6 +405,14 @@ they are going to unsubscribe. To do it:

  • If Details required is enabled, they will have to fill a text area to continue.
  • +

    For having discrete unsubscriptions from other recipients than the mailing +lists, you need to add a glue module that adds 2 fields in the associated +model:

    +
      +
    • opt_out.
    • +
    • Either email or email_from.
    • +
    +

    See mass_mailing_custom_unsubscribe_event for an example.

    Usage

    @@ -449,6 +460,7 @@ If you spotted it first, help us smashing it by providing a detailed and welcome
  • Jairo Llopis
  • David Vidal
  • Ernesto Tejeda
  • +
  • Pedro M. Baeza