From a3aa4c6d3eb699c612b0d768782976b98d46c9a8 Mon Sep 17 00:00:00 2001 From: Jairo Llopis Date: Fri, 27 Apr 2018 09:57:12 +0200 Subject: [PATCH 1/8] [IMP] mass_mailing_custom_unsubscribe: GDPR compliance - Record resubscriptions too. - Record action metadata. --- mass_mailing_custom_unsubscribe/README.rst | 5 +++- .../__manifest__.py | 4 +-- .../controllers/main.py | 20 +++++++++---- .../data/mail_unsubscription_reason.xml | 2 -- mass_mailing_custom_unsubscribe/exceptions.py | 4 +++ .../models/mail_mass_mailing.py | 3 +- .../models/mail_unsubscription.py | 29 ++++++++++++++++--- .../tests/test_unsubscription.py | 14 +++++++-- .../views/mail_unsubscription_view.xml | 3 +- 9 files changed, 66 insertions(+), 18 deletions(-) diff --git a/mass_mailing_custom_unsubscribe/README.rst b/mass_mailing_custom_unsubscribe/README.rst index 14efcbcd81..568d653014 100644 --- a/mass_mailing_custom_unsubscribe/README.rst +++ b/mass_mailing_custom_unsubscribe/README.rst @@ -10,7 +10,10 @@ This addon extends the unsubscription form to let you: - Choose which mailing lists are not cross-unsubscriptable when unsubscribing from a different one. -- Know why and when a contact has been unsubscribed from a mass mailing. +- Know why and when a contact has been subscribed or unsubscribed from a + mass mailing. +- Provide proof on why you are sending mass mailings to a given contact, as + required by the GDPR in Europe. Configuration ============= diff --git a/mass_mailing_custom_unsubscribe/__manifest__.py b/mass_mailing_custom_unsubscribe/__manifest__.py index c533f2c173..956877693f 100644 --- a/mass_mailing_custom_unsubscribe/__manifest__.py +++ b/mass_mailing_custom_unsubscribe/__manifest__.py @@ -3,9 +3,9 @@ # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). { 'name': "Customizable unsubscription process on mass mailing emails", - "summary": "Know unsubscription reasons, track them", + "summary": "Know and track (un)subscription reasons, GDPR compliant", 'category': 'Marketing', - 'version': '10.0.1.0.0', + 'version': '10.0.1.1.0', 'depends': [ 'website_mass_mailing', ], diff --git a/mass_mailing_custom_unsubscribe/controllers/main.py b/mass_mailing_custom_unsubscribe/controllers/main.py index 403efeb9af..0c656aaf5e 100644 --- a/mass_mailing_custom_unsubscribe/controllers/main.py +++ b/mass_mailing_custom_unsubscribe/controllers/main.py @@ -90,12 +90,22 @@ def unsubscribe(self, mailing_id, opt_in_ids, opt_out_ids, email, res_id, token, reason_id=None, details=None): """Store unsubscription reasons when unsubscribing from RPC.""" # Update request context and reset environment + environ = request.httprequest.headers.environ + extra_context = { + "default_metadata": "\n".join( + "%s: %s" % (val, environ.get(val)) for val in ( + "REMOTE_ADDR", + "HTTP_USER_AGENT", + "HTTP_ACCEPT_LANGUAGE", + "HTTP_REFERER", + ) + ), + } if reason_id: - request.context = dict( - request.context, - default_reason_id=int(reason_id), - default_details=details or False, - ) + extra_context["default_reason_id"] = int(reason_id) + if details: + extra_context["default_details"] = details + request.context = dict(request.context, **extra_context) # FIXME Remove token check in version where this is merged: # https://github.com/odoo/odoo/pull/14385 mailing = request.env['mail.mass_mailing'].sudo().browse(mailing_id) diff --git a/mass_mailing_custom_unsubscribe/data/mail_unsubscription_reason.xml b/mass_mailing_custom_unsubscribe/data/mail_unsubscription_reason.xml index 4335f9b77f..24a6eda30e 100644 --- a/mass_mailing_custom_unsubscribe/data/mail_unsubscription_reason.xml +++ b/mass_mailing_custom_unsubscribe/data/mail_unsubscription_reason.xml @@ -2,7 +2,6 @@ - - diff --git a/mass_mailing_custom_unsubscribe/exceptions.py b/mass_mailing_custom_unsubscribe/exceptions.py index 195b619874..bc9bbcc441 100644 --- a/mass_mailing_custom_unsubscribe/exceptions.py +++ b/mass_mailing_custom_unsubscribe/exceptions.py @@ -7,3 +7,7 @@ class DetailsRequiredError(exceptions.ValidationError): pass + + +class ReasonRequiredError(exceptions.ValidationError): + pass diff --git a/mass_mailing_custom_unsubscribe/models/mail_mass_mailing.py b/mass_mailing_custom_unsubscribe/models/mail_mass_mailing.py index 62fcf04daf..e33ddadae1 100644 --- a/mass_mailing_custom_unsubscribe/models/mail_mass_mailing.py +++ b/mass_mailing_custom_unsubscribe/models/mail_mass_mailing.py @@ -40,7 +40,7 @@ def _unsubscribe_token(self, res_id, compare=None): def update_opt_out(self, email, res_ids, value): """Save unsubscription reason when opting out from mailing.""" self.ensure_one() - if value and self.env.context.get("default_reason_id"): + if not value or self.env.context.get("default_reason_id"): for res_id in res_ids: # reason_id and details are expected from the context self.env["mail.unsubscription"].create({ @@ -48,6 +48,7 @@ def update_opt_out(self, email, res_ids, value): "mass_mailing_id": self.id, "unsubscriber_id": "%s,%d" % ( self.mailing_model, int(res_id)), + "action": "unsubscription" if value else "subscription", }) return super(MailMassMailing, self).update_opt_out( email, res_ids, value) diff --git a/mass_mailing_custom_unsubscribe/models/mail_unsubscription.py b/mass_mailing_custom_unsubscribe/models/mail_unsubscription.py index 83999bcb18..112fe97127 100644 --- a/mass_mailing_custom_unsubscribe/models/mail_unsubscription.py +++ b/mass_mailing_custom_unsubscribe/models/mail_unsubscription.py @@ -2,7 +2,7 @@ # Copyright 2016 Jairo Llopis # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). -from openerp import _, api, fields, models +from odoo import _, api, fields, models from .. import exceptions @@ -16,6 +16,15 @@ class MailUnsubscription(models.Model): required=True) email = fields.Char( required=True) + action = fields.Selection( + selection=[ + ("subscription", "Subscription"), + ("unsubscription", "Unsubscription"), + ], + required=True, + default="unsubscription", + help="What did the (un)subscriber choose to do.", + ) mass_mailing_id = fields.Many2one( "mail.mass_mailing", "Mass mailing", @@ -23,19 +32,22 @@ class MailUnsubscription(models.Model): help="Mass mailing from which he was unsubscribed.") unsubscriber_id = fields.Reference( lambda self: self._selection_unsubscriber_id(), - "Unsubscriber", + "(Un)subscriber", required=True, - help="Who was unsubscribed.") + help="Who was subscribed or unsubscribed.") reason_id = fields.Many2one( "mail.unsubscription.reason", "Reason", ondelete="restrict", - required=True, help="Why the unsubscription was made.") details = fields.Char( help="More details on why the unsubscription was made.") details_required = fields.Boolean( related="reason_id.details_required") + metadata = fields.Char( + readonly=True, + help="HTTP request metadata used when creating this record.", + ) @api.model def _default_date(self): @@ -46,6 +58,15 @@ def _selection_unsubscriber_id(self): """Models that can be linked to a ``mail.mass_mailing``.""" return self.env["mail.mass_mailing"]._get_mailing_model() + @api.multi + @api.constrains("action", "reason_id") + def _check_reason_needed(self): + """Ensure reason is given for unsubscriptions.""" + for one in self: + if one.action == "unsubscription" and not one.reason_id: + raise exceptions.ReasonRequiredError( + _("Please indicate why are you unsubscribing.")) + @api.multi @api.constrains("details", "reason_id") def _check_details_needed(self): diff --git a/mass_mailing_custom_unsubscribe/tests/test_unsubscription.py b/mass_mailing_custom_unsubscribe/tests/test_unsubscription.py index 06c4750769..b6d7043ac9 100644 --- a/mass_mailing_custom_unsubscribe/tests/test_unsubscription.py +++ b/mass_mailing_custom_unsubscribe/tests/test_unsubscription.py @@ -2,11 +2,11 @@ # Copyright 2016 Jairo Llopis # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). -from openerp.tests.common import TransactionCase +from openerp.tests.common import SavepointCase from .. import exceptions -class UnsubscriptionCase(TransactionCase): +class UnsubscriptionCase(SavepointCase): def test_details_required(self): """Cannot create unsubscription without details when required.""" with self.assertRaises(exceptions.DetailsRequiredError): @@ -19,3 +19,13 @@ def test_details_required(self): self.env.ref( "mass_mailing_custom_unsubscribe.reason_other").id, }) + + def test_reason_required(self): + """Cannot create unsubscription without reason when required.""" + with self.assertRaises(exceptions.ReasonRequiredError): + self.env["mail.unsubscription"].create({ + "email": "axelor@yourcompany.example.com", + "mass_mailing_id": self.env.ref("mass_mailing.mass_mail_1").id, + "unsubscriber_id": + "res.partner,%d" % self.env.ref("base.res_partner_2").id, + }) diff --git a/mass_mailing_custom_unsubscribe/views/mail_unsubscription_view.xml b/mass_mailing_custom_unsubscribe/views/mail_unsubscription_view.xml index da8f23ca69..9b9e48654a 100644 --- a/mass_mailing_custom_unsubscribe/views/mail_unsubscription_view.xml +++ b/mass_mailing_custom_unsubscribe/views/mail_unsubscription_view.xml @@ -15,7 +15,8 @@ - + From 995a74197f4818b6b209d9d6625ee6aeabcdff61 Mon Sep 17 00:00:00 2001 From: Jairo Llopis Date: Wed, 2 May 2018 09:04:58 +0100 Subject: [PATCH 2/8] !fixup [IMP] mass_mailing_custom_unsubscribe: GDPR compliance --- mass_mailing_custom_unsubscribe/__manifest__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mass_mailing_custom_unsubscribe/__manifest__.py b/mass_mailing_custom_unsubscribe/__manifest__.py index 956877693f..d8bbe90f9a 100644 --- a/mass_mailing_custom_unsubscribe/__manifest__.py +++ b/mass_mailing_custom_unsubscribe/__manifest__.py @@ -5,7 +5,7 @@ 'name': "Customizable unsubscription process on mass mailing emails", "summary": "Know and track (un)subscription reasons, GDPR compliant", 'category': 'Marketing', - 'version': '10.0.1.1.0', + 'version': '10.0.2.0.0', 'depends': [ 'website_mass_mailing', ], From 5d07ef71d8ab4a8908fbcb4a6242f4afbd031742 Mon Sep 17 00:00:00 2001 From: Jairo Llopis Date: Mon, 7 May 2018 12:19:43 +0100 Subject: [PATCH 3/8] !squash [IMP] mass_mailing_custom_unsubscribe: GDPR compliance - Never require details in frontend if unsubscription form is hidden. - Make ESLint happy. - Quick color-based action distinction in tree view. - Add useful quick groupings. - Display (un)subscription metadata. --- .../controllers/main.py | 1 - .../models/mail_unsubscription.py | 3 +-- .../static/src/js/require_details.js | 7 ++++-- .../static/src/js/unsubscribe.js | 24 +++++++++++-------- .../views/mail_unsubscription_view.xml | 9 ++++++- 5 files changed, 28 insertions(+), 16 deletions(-) diff --git a/mass_mailing_custom_unsubscribe/controllers/main.py b/mass_mailing_custom_unsubscribe/controllers/main.py index 0c656aaf5e..281bc3a50b 100644 --- a/mass_mailing_custom_unsubscribe/controllers/main.py +++ b/mass_mailing_custom_unsubscribe/controllers/main.py @@ -97,7 +97,6 @@ def unsubscribe(self, mailing_id, opt_in_ids, opt_out_ids, email, res_id, "REMOTE_ADDR", "HTTP_USER_AGENT", "HTTP_ACCEPT_LANGUAGE", - "HTTP_REFERER", ) ), } diff --git a/mass_mailing_custom_unsubscribe/models/mail_unsubscription.py b/mass_mailing_custom_unsubscribe/models/mail_unsubscription.py index 112fe97127..e5c1cacb4f 100644 --- a/mass_mailing_custom_unsubscribe/models/mail_unsubscription.py +++ b/mass_mailing_custom_unsubscribe/models/mail_unsubscription.py @@ -33,7 +33,6 @@ class MailUnsubscription(models.Model): unsubscriber_id = fields.Reference( lambda self: self._selection_unsubscriber_id(), "(Un)subscriber", - required=True, help="Who was subscribed or unsubscribed.") reason_id = fields.Many2one( "mail.unsubscription.reason", @@ -44,7 +43,7 @@ class MailUnsubscription(models.Model): help="More details on why the unsubscription was made.") details_required = fields.Boolean( related="reason_id.details_required") - metadata = fields.Char( + metadata = fields.Text( readonly=True, help="HTTP request metadata used when creating this record.", ) diff --git a/mass_mailing_custom_unsubscribe/static/src/js/require_details.js b/mass_mailing_custom_unsubscribe/static/src/js/require_details.js index 6df78b7566..6c778c68a8 100644 --- a/mass_mailing_custom_unsubscribe/static/src/js/require_details.js +++ b/mass_mailing_custom_unsubscribe/static/src/js/require_details.js @@ -5,7 +5,7 @@ odoo.define("mass_mailing_custom_unsubscribe.require_details", "use strict"; var animation = require("web_editor.snippets.animation"); - return animation.registry.mass_mailing_custom_unsubscribe_require_details = + animation.registry.mass_mailing_custom_unsubscribe_require_details = animation.Class.extend({ selector: ".js_unsubscription_reason", @@ -19,7 +19,10 @@ odoo.define("mass_mailing_custom_unsubscribe.require_details", toggle: function (event) { this.$details.prop( "required", - $(event.target).is("[data-details-required]")); + $(event.target).is("[data-details-required]") && + $(event.target).is(":visible")); }, }); + + return animation.registry.mass_mailing_custom_unsubscribe_require_details; }); diff --git a/mass_mailing_custom_unsubscribe/static/src/js/unsubscribe.js b/mass_mailing_custom_unsubscribe/static/src/js/unsubscribe.js index 49f92b6dc1..484eb2e7b7 100644 --- a/mass_mailing_custom_unsubscribe/static/src/js/unsubscribe.js +++ b/mass_mailing_custom_unsubscribe/static/src/js/unsubscribe.js @@ -7,15 +7,15 @@ * that when it gets merged, and remove most of this file. */ odoo.define("mass_mailing_custom_unsubscribe.unsubscribe", function (require) { "use strict"; - var core = require("web.core"), - ajax = require("web.ajax"), - animation = require("web_editor.snippets.animation"), - _t = core._t; + var core = require("web.core"); + var ajax = require("web.ajax"); + var animation = require("web_editor.snippets.animation"); + var _t = core._t; - return animation.registry.mass_mailing_unsubscribe = + animation.registry.mass_mailing_unsubscribe = animation.Class.extend({ selector: "#unsubscribe_form", - start: function (editable_mode) { + start: function () { this.controller = '/mail/mailing/unsubscribe'; this.$alert = this.$(".alert"); this.$email = this.$("input[name='email']"); @@ -32,7 +32,7 @@ odoo.define("mass_mailing_custom_unsubscribe.unsubscribe", function (require) { // Helper to get list ids, to use in this.$contacts.map() int_val: function (index, element) { - return parseInt($(element).val()); + return parseInt($(element).val(), 10); }, // Get a filtered array of integer IDs of matching lists @@ -62,16 +62,18 @@ odoo.define("mass_mailing_custom_unsubscribe.unsubscribe", function (require) { values: function () { var result = { email: this.$email.val(), - mailing_id: parseInt(this.$mailing_id.val()), + mailing_id: parseInt(this.$mailing_id.val(), 10), opt_in_ids: this.contact_ids(true), opt_out_ids: this.contact_ids(false), - res_id: parseInt(this.$res_id.val()), + res_id: parseInt(this.$res_id.val(), 10), token: this.$token.val(), }; // Only send reason and details if an unsubscription was found if (this.$reasons.is(":visible")) { result.reason_id = parseInt( - this.$reasons.find("[name='reason_id']:checked").val()); + this.$reasons.find("[name='reason_id']:checked").val(), + 10 + ); result.details = this.$details.val(); } return result; @@ -108,4 +110,6 @@ odoo.define("mass_mailing_custom_unsubscribe.unsubscribe", function (require) { .addClass("alert-warning"); }, }); + + return animation.registry.mass_mailing_unsubscribe; }); diff --git a/mass_mailing_custom_unsubscribe/views/mail_unsubscription_view.xml b/mass_mailing_custom_unsubscribe/views/mail_unsubscription_view.xml index 9b9e48654a..17f501afad 100644 --- a/mass_mailing_custom_unsubscribe/views/mail_unsubscription_view.xml +++ b/mass_mailing_custom_unsubscribe/views/mail_unsubscription_view.xml @@ -15,11 +15,13 @@ + +
@@ -37,11 +39,12 @@ Mail Unsubscription Tree mail.unsubscription - + + @@ -64,6 +67,10 @@ context="{'group_by': 'date:month'}"/> + + Date: Tue, 8 May 2018 11:29:30 +0100 Subject: [PATCH 4/8] !squash [IMP] mass_mailing_custom_unsubscribe: GDPR compliance - Do not store duplicated (un)subscriptions. - Require reasons for unsubscriptions only. - Pivot & graph views. --- .../models/mail_mass_mailing.py | 19 ++++++++++---- .../models/mail_unsubscription.py | 1 + .../static/src/js/unsubscribe.js | 8 +++++- .../views/mail_unsubscription_view.xml | 25 ++++++++++++++++++- 4 files changed, 46 insertions(+), 7 deletions(-) diff --git a/mass_mailing_custom_unsubscribe/models/mail_mass_mailing.py b/mass_mailing_custom_unsubscribe/models/mail_mass_mailing.py index e33ddadae1..2eec3d4a8c 100644 --- a/mass_mailing_custom_unsubscribe/models/mail_mass_mailing.py +++ b/mass_mailing_custom_unsubscribe/models/mail_mass_mailing.py @@ -40,15 +40,24 @@ def _unsubscribe_token(self, res_id, compare=None): def update_opt_out(self, email, res_ids, value): """Save unsubscription reason when opting out from mailing.""" self.ensure_one() - if not value or self.env.context.get("default_reason_id"): - for res_id in res_ids: + action = "unsubscription" if value else "subscription" + records = self.env[self.mailing_model].browse(res_ids) + previous = self.env["mail.unsubscription"].search(limit=1, args=[ + ("mass_mailing_id", "=", self.id), + ("email", "=", email), + ("action", "=", action), + ]) + for one in records: + # Store action only when something changed, or there was no + # previous subscription record + if one.opt_out != value or (action == "subscription" and + not previous): # reason_id and details are expected from the context self.env["mail.unsubscription"].create({ "email": email, "mass_mailing_id": self.id, - "unsubscriber_id": "%s,%d" % ( - self.mailing_model, int(res_id)), - "action": "unsubscription" if value else "subscription", + "unsubscriber_id": "%s,%d" % (one._name, one.id), + "action": action, }) return super(MailMassMailing, self).update_opt_out( email, res_ids, value) diff --git a/mass_mailing_custom_unsubscribe/models/mail_unsubscription.py b/mass_mailing_custom_unsubscribe/models/mail_unsubscription.py index e5c1cacb4f..2d6c13c344 100644 --- a/mass_mailing_custom_unsubscribe/models/mail_unsubscription.py +++ b/mass_mailing_custom_unsubscribe/models/mail_unsubscription.py @@ -10,6 +10,7 @@ class MailUnsubscription(models.Model): _name = "mail.unsubscription" _inherit = "mail.thread" _rec_name = "date" + _order = "date DESC" date = fields.Datetime( default=lambda self: self._default_date(), diff --git a/mass_mailing_custom_unsubscribe/static/src/js/unsubscribe.js b/mass_mailing_custom_unsubscribe/static/src/js/unsubscribe.js index 484eb2e7b7..12ec3a05ab 100644 --- a/mass_mailing_custom_unsubscribe/static/src/js/unsubscribe.js +++ b/mass_mailing_custom_unsubscribe/static/src/js/unsubscribe.js @@ -50,11 +50,17 @@ odoo.define("mass_mailing_custom_unsubscribe.unsubscribe", function (require) { }); // Hide reasons form if you are only subscribing this.$reasons.toggleClass("hidden", !$disabled.length); + var $radios = this.$reasons.find(":radio"); if (this.$reasons.is(":hidden")) { // Uncheck chosen reason - this.$reasons.find(":radio").prop("checked", false) + $radios.prop("checked", false) + // Unrequire specifying a reason + .prop("required", false) // Remove possible constraints for details .trigger("change"); + } else { + // Require specifying a reason + $radios.prop("required", true); } }, diff --git a/mass_mailing_custom_unsubscribe/views/mail_unsubscription_view.xml b/mass_mailing_custom_unsubscribe/views/mail_unsubscription_view.xml index 17f501afad..b51de08b21 100644 --- a/mass_mailing_custom_unsubscribe/views/mail_unsubscription_view.xml +++ b/mass_mailing_custom_unsubscribe/views/mail_unsubscription_view.xml @@ -80,8 +80,31 @@ + + Mail Unsubscription Pivot + mail.unsubscription + + + + + + + + + + Mail Unsubscription Graph + mail.unsubscription + + + + + + + + Date: Tue, 8 May 2018 12:54:44 +0100 Subject: [PATCH 5/8] fixup! !squash [IMP] mass_mailing_custom_unsubscribe: GDPR compliance --- mass_mailing_custom_unsubscribe/controllers/main.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/mass_mailing_custom_unsubscribe/controllers/main.py b/mass_mailing_custom_unsubscribe/controllers/main.py index 281bc3a50b..3b2157227f 100644 --- a/mass_mailing_custom_unsubscribe/controllers/main.py +++ b/mass_mailing_custom_unsubscribe/controllers/main.py @@ -45,6 +45,8 @@ def mailing(self, mailing_id, email=None, res_id=None, token="", **post): _logger.debug( "Called `mailing()` with: %r", (mailing_id, email, res_id, token, post)) + if res_id: + res_id = int(res_id) mailing = request.env["mail.mass_mailing"].sudo().browse(mailing_id) mailing._unsubscribe_token(res_id, token) # Mass mailing list contacts are a special case because they have a From efb57e7b8d13cfa7c08e699d2dc0d2c47e5e0c2a Mon Sep 17 00:00:00 2001 From: Jairo Llopis Date: Tue, 8 May 2018 12:54:44 +0100 Subject: [PATCH 6/8] fixup! !squash [IMP] mass_mailing_custom_unsubscribe: GDPR compliance --- .../models/mail_unsubscription.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/mass_mailing_custom_unsubscribe/models/mail_unsubscription.py b/mass_mailing_custom_unsubscribe/models/mail_unsubscription.py index 2d6c13c344..2fbe664d8a 100644 --- a/mass_mailing_custom_unsubscribe/models/mail_unsubscription.py +++ b/mass_mailing_custom_unsubscribe/models/mail_unsubscription.py @@ -76,6 +76,13 @@ def _check_details_needed(self): raise exceptions.DetailsRequiredError( _("Please provide details on why you are unsubscribing.")) + @api.model + def create(self, vals): + # No reasons for subscriptions + if vals.get("action") == "subscription": + vals = dict(vals, reason_id=False, details=False) + return super(MailUnsubscription, self).create(vals) + class MailUnsubscriptionReason(models.Model): _name = "mail.unsubscription.reason" From 31634d4e40bdac1b2e87df97a6583a88c07ff11c Mon Sep 17 00:00:00 2001 From: Jairo Llopis Date: Tue, 8 May 2018 12:54:44 +0100 Subject: [PATCH 7/8] fixup! !squash [IMP] mass_mailing_custom_unsubscribe: GDPR compliance --- .../models/mail_unsubscription.py | 19 +++++++++++++++++++ .../views/mail_unsubscription_view.xml | 6 +++++- 2 files changed, 24 insertions(+), 1 deletion(-) diff --git a/mass_mailing_custom_unsubscribe/models/mail_unsubscription.py b/mass_mailing_custom_unsubscribe/models/mail_unsubscription.py index 2fbe664d8a..684207c00e 100644 --- a/mass_mailing_custom_unsubscribe/models/mail_unsubscription.py +++ b/mass_mailing_custom_unsubscribe/models/mail_unsubscription.py @@ -35,6 +35,14 @@ class MailUnsubscription(models.Model): lambda self: self._selection_unsubscriber_id(), "(Un)subscriber", help="Who was subscribed or unsubscribed.") + mailing_list_id = fields.Many2one( + "mail.mass_mailing.list", + "Mailing list", + ondelete="set null", + compute="_compute_mailing_list_id", + store=True, + help="(Un)subscribed mass mailing list, if any.", + ) reason_id = fields.Many2one( "mail.unsubscription.reason", "Reason", @@ -76,6 +84,17 @@ def _check_details_needed(self): raise exceptions.DetailsRequiredError( _("Please provide details on why you are unsubscribing.")) + @api.multi + @api.depends("unsubscriber_id") + def _compute_mailing_list_id(self): + """Get the mass mailing list, if it is possible.""" + for one in self: + try: + one.mailing_list_id = one.unsubscriber_id["list_id"] + except KeyError: + # Possibly model != mail.mass_mailing.contact; no problem + pass + @api.model def create(self, vals): # No reasons for subscriptions diff --git a/mass_mailing_custom_unsubscribe/views/mail_unsubscription_view.xml b/mass_mailing_custom_unsubscribe/views/mail_unsubscription_view.xml index b51de08b21..177b5598cd 100644 --- a/mass_mailing_custom_unsubscribe/views/mail_unsubscription_view.xml +++ b/mass_mailing_custom_unsubscribe/views/mail_unsubscription_view.xml @@ -14,6 +14,7 @@ + + @@ -58,6 +60,7 @@ + @@ -85,8 +88,9 @@ mail.unsubscription - + + From 6b887b05106c4338eae1ece12452363f970aa154 Mon Sep 17 00:00:00 2001 From: Jairo Llopis Date: Tue, 8 May 2018 12:54:44 +0100 Subject: [PATCH 8/8] fixup! !squash [IMP] mass_mailing_custom_unsubscribe: GDPR compliance --- mass_mailing_custom_unsubscribe/models/mail_unsubscription.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mass_mailing_custom_unsubscribe/models/mail_unsubscription.py b/mass_mailing_custom_unsubscribe/models/mail_unsubscription.py index 684207c00e..148ae9da8a 100644 --- a/mass_mailing_custom_unsubscribe/models/mail_unsubscription.py +++ b/mass_mailing_custom_unsubscribe/models/mail_unsubscription.py @@ -90,8 +90,8 @@ def _compute_mailing_list_id(self): """Get the mass mailing list, if it is possible.""" for one in self: try: - one.mailing_list_id = one.unsubscriber_id["list_id"] - except KeyError: + one.mailing_list_id = one.unsubscriber_id.list_id + except AttributeError: # Possibly model != mail.mass_mailing.contact; no problem pass