From ad68bf6343a12f8b9ad748cdea6fe7e95a224437 Mon Sep 17 00:00:00 2001 From: Jairo Llopis Date: Wed, 5 Jul 2017 11:07:28 +0200 Subject: [PATCH] [MIG][mass_mailing_custom_unsubscribe] Migrate to v10 --- mass_mailing_custom_unsubscribe/README.rst | 10 ++- .../__manifest__.py | 5 +- .../controllers/main.py | 19 +++-- .../models/mail_mail.py | 9 +-- .../models/mail_mass_mailing.py | 11 ++- .../security/ir.model.access.csv | 2 +- .../static/src/js/contact.tour.js | 77 +++++++++---------- .../static/src/js/partner.tour.js | 49 ++++++------ .../tests/test_ui.py | 24 +++--- .../views/mail_unsubscription_reason_view.xml | 2 +- 10 files changed, 101 insertions(+), 107 deletions(-) diff --git a/mass_mailing_custom_unsubscribe/README.rst b/mass_mailing_custom_unsubscribe/README.rst index aec049925d..14efcbcd81 100644 --- a/mass_mailing_custom_unsubscribe/README.rst +++ b/mass_mailing_custom_unsubscribe/README.rst @@ -10,7 +10,7 @@ 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 as been unsubscribed from a mass mailing. +- Know why and when a contact has been unsubscribed from a mass mailing. Configuration ============= @@ -21,7 +21,7 @@ Unsubscription Reasons You can customize what reasons will be displayed to your unsubscriptors when they are going to unsubscribe. To do it: -#. Go to *Marketing > Configuration > Unsubscription Reasons*. +#. Go to *Mass Mailing > Configuration > Unsubscription Reasons*. #. Create / edit / remove / sort as usual. #. If *Details required* is enabled, they will have to fill a text area to continue. @@ -40,7 +40,7 @@ Once configured: .. image:: https://odoo-community.org/website/image/ir.attachment/5784_f2813bd/datas :alt: Try me on Runbot - :target: https://runbot.odoo-community.org/runbot/205/9.0 + :target: https://runbot.odoo-community.org/runbot/205/10.0 Known issues / Roadmap ====================== @@ -48,7 +48,9 @@ Known issues / Roadmap * This module adds a security hash for mass mailing unsubscription URLs, which disables insecure URLs from mass mailing messages sent before its installation. This can be a problem, but anyway you'd get that problem in - Odoo 11.0, so at least this addon will be forward-compatible with it. + Odoo 11.0, where https://github.com/odoo/odoo/pull/12040 was merged, so at + least this addon will be forward-compatible with it. So, **this feature must + be removed from here when migrating to v11**. * This module replaces AJAX submission core implementation from the mailing list management form, because it is impossible to extend it. When https://github.com/odoo/odoo/pull/14386 gets merged (which upstreams most diff --git a/mass_mailing_custom_unsubscribe/__manifest__.py b/mass_mailing_custom_unsubscribe/__manifest__.py index 382d38e011..c533f2c173 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 unsubscription reasons, track them", 'category': 'Marketing', - 'version': '9.0.2.0.0', + 'version': '10.0.1.0.0', 'depends': [ 'website_mass_mailing', ], @@ -25,8 +25,7 @@ 'images': [ 'images/form.png', ], - 'author': 'Antiun IngenierĂ­a S.L., ' - 'Tecnativa,' + 'author': 'Tecnativa,' 'Odoo Community Association (OCA)', 'website': 'https://www.tecnativa.com', 'license': 'AGPL-3', diff --git a/mass_mailing_custom_unsubscribe/controllers/main.py b/mass_mailing_custom_unsubscribe/controllers/main.py index 07839d9ccd..403efeb9af 100644 --- a/mass_mailing_custom_unsubscribe/controllers/main.py +++ b/mass_mailing_custom_unsubscribe/controllers/main.py @@ -29,7 +29,7 @@ def reason_form(self, mailing, email, res_id, token): Security token for unsubscriptions. """ reasons = request.env["mail.unsubscription.reason"].search([]) - return request.website.render( + return request.render( "mass_mailing_custom_unsubscribe.reason_form", { "email": email, @@ -75,11 +75,11 @@ def mailing(self, mailing_id, email=None, res_id=None, token="", **post): return self.reason_form(mailing, email, res_id, token) else: # Unsubscribe, saving reason and details by context - request.context.update({ - "default_reason_id": reason_id, - "default_details": post.get("details") or False, - }) - del request.env + request.context = dict( + request.context, + default_reason_id=reason_id, + default_details=post.get("details") or False, + ) # You could get a DetailsRequiredError here, but only if HTML5 # validation fails, which should not happen in modern browsers return super(CustomUnsubscribe, self).mailing( @@ -91,8 +91,11 @@ def unsubscribe(self, mailing_id, opt_in_ids, opt_out_ids, email, res_id, """Store unsubscription reasons when unsubscribing from RPC.""" # Update request context and reset environment if reason_id: - request.context["default_reason_id"] = int(reason_id) - request.context["default_details"] = details or False + request.context = dict( + request.context, + default_reason_id=int(reason_id), + default_details=details or False, + ) # 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/models/mail_mail.py b/mass_mailing_custom_unsubscribe/models/mail_mail.py index 2fae6bff04..fb3f1bf99e 100644 --- a/mass_mailing_custom_unsubscribe/models/mail_mail.py +++ b/mass_mailing_custom_unsubscribe/models/mail_mail.py @@ -2,14 +2,13 @@ # Copyright 2016 Jairo Llopis # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). -from openerp import api, models +from openerp import models class MailMail(models.Model): _inherit = 'mail.mail' - @api.model - def _get_unsubscribe_url(self, mail, email_to): - result = super(MailMail, self)._get_unsubscribe_url(mail, email_to) - token = mail.mailing_id._unsubscribe_token(mail.res_id) + def _get_unsubscribe_url(self, email_to): + result = super(MailMail, self)._get_unsubscribe_url(email_to) + token = self.mailing_id._unsubscribe_token(self.res_id) return "%s&token=%s" % (result, token) diff --git a/mass_mailing_custom_unsubscribe/models/mail_mass_mailing.py b/mass_mailing_custom_unsubscribe/models/mail_mass_mailing.py index b200644f17..62fcf04daf 100644 --- a/mass_mailing_custom_unsubscribe/models/mail_mass_mailing.py +++ b/mass_mailing_custom_unsubscribe/models/mail_mass_mailing.py @@ -37,18 +37,17 @@ def _unsubscribe_token(self, res_id, compare=None): raise AccessDenied() return token - @api.model - def update_opt_out(self, mailing_id, email, res_ids, value): + def update_opt_out(self, email, res_ids, value): """Save unsubscription reason when opting out from mailing.""" - mailing = self.browse(mailing_id) + self.ensure_one() if value and 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({ "email": email, - "mass_mailing_id": mailing.id, + "mass_mailing_id": self.id, "unsubscriber_id": "%s,%d" % ( - mailing.mailing_model, int(res_id)), + self.mailing_model, int(res_id)), }) return super(MailMassMailing, self).update_opt_out( - mailing_id, email, res_ids, value) + email, res_ids, value) diff --git a/mass_mailing_custom_unsubscribe/security/ir.model.access.csv b/mass_mailing_custom_unsubscribe/security/ir.model.access.csv index 73f5db8e6b..b8241abbbf 100644 --- a/mass_mailing_custom_unsubscribe/security/ir.model.access.csv +++ b/mass_mailing_custom_unsubscribe/security/ir.model.access.csv @@ -3,4 +3,4 @@ read_unsubscription_reason_public,Public users can read unsubscription reasons,m read_unsubscription_reason_employee,Employee users can read unsubscription reasons,model_mail_unsubscription_reason,base.group_user,1,0,0,0 write_unsubscription_reason,Mass mailing managers can manage unsubscription reasons,model_mail_unsubscription_reason,mass_mailing.group_mass_mailing_user,1,1,1,1 read_unsubscription,Marketing users can read unsubscriptions,model_mail_unsubscription,mass_mailing.group_mass_mailing_user,1,0,0,0 -write_unsubscription,Mass mailing managers can manage unsubscriptions,model_mail_unsubscription,mass_mailing.group_mass_mailing_user,1,1,1,1 +write_unsubscription,Admins can change unsubscriptions,model_mail_unsubscription,base.group_system,1,1,1,1 diff --git a/mass_mailing_custom_unsubscribe/static/src/js/contact.tour.js b/mass_mailing_custom_unsubscribe/static/src/js/contact.tour.js index 361a1300b3..df0f9f9ffb 100644 --- a/mass_mailing_custom_unsubscribe/static/src/js/contact.tour.js +++ b/mass_mailing_custom_unsubscribe/static/src/js/contact.tour.js @@ -3,75 +3,72 @@ odoo.define("mass_mailing_custom_unsubscribe.contact_tour", function (require) { "use strict"; - var Tour = require("web.Tour"); + var base = require("web_editor.base"); + var tour = require("web_tour.tour"); require("mass_mailing_custom_unsubscribe.require_details"); require("mass_mailing_custom_unsubscribe.unsubscribe"); // Allow to know if an element is required $.extend($.expr[':'], { - propRequired: function(element, index, matches) { + propRequired: function(element, index, matches) { return $(element).prop("required"); - }, + }, }); - Tour.register({ - id: "mass_mailing_custom_unsubscribe_tour_contact", - name: "Mass mailing contact unsubscribes", - mode: "test", - steps: [ + tour.register( + "mass_mailing_custom_unsubscribe_tour_contact", + { + tour: true, + wait_for: base.ready(), + }, + [ { - title: "Unsubscription reasons are invisible", - waitFor: "#unsubscribe_form .js_unsubscription_reason:hidden", + content: "Unsubscription reasons are invisible", + trigger: "#unsubscribe_form:has(.js_unsubscription_reason:hidden)", }, { - title: "Uncheck list 0", - element: "li:contains('test list 0') input", - waitFor: "li:contains('test list 0') input:checked", + content: "Uncheck list 0", + trigger: "li:contains('test list 0') input", // List 2 is not cross unsubscriptable - waitNot: "li:contains('test list 2')", + extra_trigger: "body:not(:has(li:contains('test list 2'))) li:contains('test list 0') input:checked", }, { - title: "Uncheck list 1", - element: "li:contains('test list 1') input:checked", - waitFor: ".js_unsubscription_reason:visible", + content: "Uncheck list 1", + trigger: "li:contains('test list 1') input:checked", + extra_trigger: ".js_unsubscription_reason:visible", }, { - title: "Choose other reason", - element: ".radio:contains('Other reason') :radio", - waitFor: ".radio:contains('Other reason') " + - ":radio:not(:checked)", + content: "Choose other reason", + trigger: ".radio:contains('Other reason') :radio", + extra_trigger: ".radio:contains('Other reason') " + + ":radio:not(:checked)", }, { - title: "Add details to reason", - element: "[name='details']:visible:propRequired", - sampleText: "I want to unsubscribe because I want. Period.", - waitFor: ".radio:contains('Other reason') :radio:checked", + content: "Add details to reason", + trigger: "[name='details']:visible:propRequired", + run: "text I want to unsubscribe because I want. Period.", + extra_trigger: ".radio:contains('Other reason') :radio:checked", }, { - title: "Update subscriptions 1st time", - element: "#unsubscribe_form :submit", + content: "Update subscriptions 1st time", + trigger: "#unsubscribe_form :submit", }, { - title: "Subscribe again to list 0", - element: "li:contains('test list 0') input:not(:checked)", - waitFor: ".alert-success", - waitNot: "#unsubscribe_form .js_unsubscription_reason:visible", - onend: function () { + content: "Subscribe again to list 0", + trigger: "body:not(:has(#unsubscribe_form .js_unsubscription_reason:visible)):has(.alert-success, li:contains('test list 0') input:not(:checked))", + run: function () { // This one will get the success again after next step $(".alert-success").removeClass("alert-success"); }, }, { - title: "Update subscriptions 2nd time", - element: "#unsubscribe_form :submit", - waitNot: "#unsubscribe_form .js_unsubscription_reason:visible", + content: "Update subscriptions 2nd time", + trigger: "#unsubscribe_form:not(:has(.js_unsubscription_reason:visible)) :submit", }, { - title: "Resuscription was OK", - waitFor: ".alert-success", + content: "Resuscription was OK", + trigger: ".alert-success", } ] - }); - - return Tour.tours.mass_mailing_custom_unsubscribe_tour_contact; + ); }); diff --git a/mass_mailing_custom_unsubscribe/static/src/js/partner.tour.js b/mass_mailing_custom_unsubscribe/static/src/js/partner.tour.js index 0a56823a4a..0d2365da87 100644 --- a/mass_mailing_custom_unsubscribe/static/src/js/partner.tour.js +++ b/mass_mailing_custom_unsubscribe/static/src/js/partner.tour.js @@ -3,47 +3,44 @@ odoo.define("mass_mailing_custom_unsubscribe.partner_tour", function (require) { "use strict"; - var Tour = require("web.Tour"); + var base = require("web_editor.base"); + var tour = require("web_tour.tour"); require("mass_mailing_custom_unsubscribe.require_details"); require("mass_mailing_custom_unsubscribe.unsubscribe"); // Allow to know if an element is required $.extend($.expr[':'], { - propRequired: function(element, index, matches) { + propRequired: function(element, index, matches) { return $(element).prop("required"); - }, + }, }); - Tour.register({ - id: "mass_mailing_custom_unsubscribe_tour_partner", - name: "Mass mailing partner unsubscribes", - mode: "test", - steps: [ + tour.register( + "mass_mailing_custom_unsubscribe_tour_partner", + { + tour: true, + wait_for: base.ready(), + }, + [ { - title: "Choose other reason", - element: ".radio:contains('Other reason') " + - ":radio:not(:checked)", - waitFor: "#reason_form .js_unsubscription_reason", + content: "Choose other reason", + trigger: ".radio:contains('Other reason') :radio:not(:checked)", + extra_trigger: "#reason_form .js_unsubscription_reason", }, { - title: "Switch to not interested reason", - element: ".radio:contains(\"I'm not interested\") " + - ":radio:not(:checked)", - waitFor: "[name='details']:propRequired", + content: "Switch to not interested reason", + trigger: ".radio:contains(\"I'm not interested\") :radio:not(:checked)", + extra_trigger: "[name='details']:propRequired", }, { - title: "Unsubscribe", - element: "#reason_form :submit", - waitNot: "[name='details']:propRequired", + content: "Unsubscribe", + trigger: "#reason_form :submit", + extra_trigger: "body:not(:has([name='details']:propRequired))", }, { - title: "Successfully unsubscribed", - waitFor: ".alert-success:contains(" + - "'Your changes have been saved.')", - waitNot: "#reason_form", + content: "Successfully unsubscribed", + trigger: "body:not(:has(#reason_form)) .alert-success:contains('Your changes have been saved.')", }, ] - }); - - return Tour.tours.mass_mailing_custom_unsubscribe_tour_partner; + ); }); diff --git a/mass_mailing_custom_unsubscribe/tests/test_ui.py b/mass_mailing_custom_unsubscribe/tests/test_ui.py index 39d43be523..0f13f5be74 100644 --- a/mass_mailing_custom_unsubscribe/tests/test_ui.py +++ b/mass_mailing_custom_unsubscribe/tests/test_ui.py @@ -7,8 +7,11 @@ class UICase(HttpCase): + _tour_run = "odoo.__DEBUG__.services['web_tour.tour'].run('%s')" + _tour_ready = "odoo.__DEBUG__.services['web_tour.tour'].tours.%s.ready" + def extract_url(self, mail, *args, **kwargs): - url = mail._get_unsubscribe_url(mail, self.email) + url = mail._get_unsubscribe_url(self.email) self.assertIn("&token=", url) self.assertTrue(url.startswith(self.domain)) self.url = url.replace(self.domain, "", 1) @@ -20,6 +23,7 @@ def setUp(self): self.mail_postprocess_patch = mock.patch( "openerp.addons.mass_mailing.models.mail_mail.MailMail." "_postprocess_sent_message", + autospec=True, side_effect=self.extract_url, ) with self.tempenv() as env: @@ -37,11 +41,7 @@ def setUp(self): "contact_list_ids": [(6, 0, self.lists.ids)], "reply_to_mode": "thread", }) - self.mailings[n].write( - self.mailings[n].on_change_model_and_list( - self.mailings[n].mailing_model, - self.mailings[n].contact_list_ids.ids, - )["value"]) + self.mailings[n]._onchange_model_and_list() # HACK https://github.com/odoo/odoo/pull/14429 self.mailings[n].body_html = """
@@ -86,13 +86,12 @@ def test_contact_unsubscription(self): tour = "mass_mailing_custom_unsubscribe_tour_contact" self.phantom_js( url_path=self.url, - code=("odoo.__DEBUG__.services['web.Tour']" - ".run('%s', 'test')") % tour, - ready="odoo.__DEBUG__.services['web.Tour'].tours.%s" % tour) + code=self._tour_run % tour, + ready=self._tour_ready % tour) # Check results from running tour with self.tempenv() as env: - self.assertFalse(self.contacts[0].opt_out) + self.assertTrue(self.contacts[0].opt_out) self.assertTrue(self.contacts[1].opt_out) self.assertFalse(self.contacts[2].opt_out) unsubscriptions = env["mail.unsubscription"].search([ @@ -130,9 +129,8 @@ def test_partner_unsubscription(self): tour = "mass_mailing_custom_unsubscribe_tour_partner" self.phantom_js( url_path=self.url, - code=("odoo.__DEBUG__.services['web.Tour']" - ".run('%s', 'test')") % tour, - ready="odoo.__DEBUG__.services['web.Tour'].tours.%s" % tour) + code=self._tour_run % tour, + ready=self._tour_ready % tour) # Check results from running tour with self.tempenv() as env: diff --git a/mass_mailing_custom_unsubscribe/views/mail_unsubscription_reason_view.xml b/mass_mailing_custom_unsubscribe/views/mail_unsubscription_reason_view.xml index 7b4c66a8ec..f1d6e2551f 100644 --- a/mass_mailing_custom_unsubscribe/views/mail_unsubscription_reason_view.xml +++ b/mass_mailing_custom_unsubscribe/views/mail_unsubscription_reason_view.xml @@ -26,9 +26,9 @@ mail.unsubscription.reason + -