[MIG][mass_mailing_custom_unsubscribe] Migrate to v10

pull/418/head
Jairo Llopis 2017-07-05 11:07:28 +02:00 committed by OCA-git-bot
parent d6f874d1e2
commit 1af75e7684
10 changed files with 101 additions and 107 deletions

View File

@ -10,7 +10,7 @@ This addon extends the unsubscription form to let you:
- Choose which mailing lists are not cross-unsubscriptable when unsubscribing - Choose which mailing lists are not cross-unsubscriptable when unsubscribing
from a different one. 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 Configuration
============= =============
@ -21,7 +21,7 @@ Unsubscription Reasons
You can customize what reasons will be displayed to your unsubscriptors when You can customize what reasons will be displayed to your unsubscriptors when
they are going to unsubscribe. To do it: 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. #. Create / edit / remove / sort as usual.
#. If *Details required* is enabled, they will have to fill a text area to #. If *Details required* is enabled, they will have to fill a text area to
continue. continue.
@ -40,7 +40,7 @@ Once configured:
.. image:: https://odoo-community.org/website/image/ir.attachment/5784_f2813bd/datas .. image:: https://odoo-community.org/website/image/ir.attachment/5784_f2813bd/datas
:alt: Try me on Runbot :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 Known issues / Roadmap
====================== ======================
@ -48,7 +48,9 @@ Known issues / Roadmap
* This module adds a security hash for mass mailing unsubscription URLs, which * This module adds a security hash for mass mailing unsubscription URLs, which
disables insecure URLs from mass mailing messages sent before its disables insecure URLs from mass mailing messages sent before its
installation. This can be a problem, but anyway you'd get that problem in 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 * This module replaces AJAX submission core implementation from the mailing
list management form, because it is impossible to extend it. When list management form, because it is impossible to extend it. When
https://github.com/odoo/odoo/pull/14386 gets merged (which upstreams most https://github.com/odoo/odoo/pull/14386 gets merged (which upstreams most

View File

@ -5,7 +5,7 @@
'name': "Customizable unsubscription process on mass mailing emails", 'name': "Customizable unsubscription process on mass mailing emails",
"summary": "Know unsubscription reasons, track them", "summary": "Know unsubscription reasons, track them",
'category': 'Marketing', 'category': 'Marketing',
'version': '9.0.2.0.0', 'version': '10.0.1.0.0',
'depends': [ 'depends': [
'website_mass_mailing', 'website_mass_mailing',
], ],
@ -25,8 +25,7 @@
'images': [ 'images': [
'images/form.png', 'images/form.png',
], ],
'author': 'Antiun Ingeniería S.L., ' 'author': 'Tecnativa,'
'Tecnativa,'
'Odoo Community Association (OCA)', 'Odoo Community Association (OCA)',
'website': 'https://www.tecnativa.com', 'website': 'https://www.tecnativa.com',
'license': 'AGPL-3', 'license': 'AGPL-3',

View File

@ -29,7 +29,7 @@ class CustomUnsubscribe(MassMailController):
Security token for unsubscriptions. Security token for unsubscriptions.
""" """
reasons = request.env["mail.unsubscription.reason"].search([]) reasons = request.env["mail.unsubscription.reason"].search([])
return request.website.render( return request.render(
"mass_mailing_custom_unsubscribe.reason_form", "mass_mailing_custom_unsubscribe.reason_form",
{ {
"email": email, "email": email,
@ -75,11 +75,11 @@ class CustomUnsubscribe(MassMailController):
return self.reason_form(mailing, email, res_id, token) return self.reason_form(mailing, email, res_id, token)
else: else:
# Unsubscribe, saving reason and details by context # Unsubscribe, saving reason and details by context
request.context.update({ request.context = dict(
"default_reason_id": reason_id, request.context,
"default_details": post.get("details") or False, default_reason_id=reason_id,
}) default_details=post.get("details") or False,
del request.env )
# You could get a DetailsRequiredError here, but only if HTML5 # You could get a DetailsRequiredError here, but only if HTML5
# validation fails, which should not happen in modern browsers # validation fails, which should not happen in modern browsers
return super(CustomUnsubscribe, self).mailing( return super(CustomUnsubscribe, self).mailing(
@ -91,8 +91,11 @@ class CustomUnsubscribe(MassMailController):
"""Store unsubscription reasons when unsubscribing from RPC.""" """Store unsubscription reasons when unsubscribing from RPC."""
# Update request context and reset environment # Update request context and reset environment
if reason_id: if reason_id:
request.context["default_reason_id"] = int(reason_id) request.context = dict(
request.context["default_details"] = details or False request.context,
default_reason_id=int(reason_id),
default_details=details or False,
)
# FIXME Remove token check in version where this is merged: # FIXME Remove token check in version where this is merged:
# https://github.com/odoo/odoo/pull/14385 # https://github.com/odoo/odoo/pull/14385
mailing = request.env['mail.mass_mailing'].sudo().browse(mailing_id) mailing = request.env['mail.mass_mailing'].sudo().browse(mailing_id)

View File

@ -2,14 +2,13 @@
# Copyright 2016 Jairo Llopis <jairo.llopis@tecnativa.com> # Copyright 2016 Jairo Llopis <jairo.llopis@tecnativa.com>
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). # 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): class MailMail(models.Model):
_inherit = 'mail.mail' _inherit = 'mail.mail'
@api.model def _get_unsubscribe_url(self, email_to):
def _get_unsubscribe_url(self, mail, email_to): result = super(MailMail, self)._get_unsubscribe_url(email_to)
result = super(MailMail, self)._get_unsubscribe_url(mail, email_to) token = self.mailing_id._unsubscribe_token(self.res_id)
token = mail.mailing_id._unsubscribe_token(mail.res_id)
return "%s&token=%s" % (result, token) return "%s&token=%s" % (result, token)

View File

@ -37,18 +37,17 @@ class MailMassMailing(models.Model):
raise AccessDenied() raise AccessDenied()
return token return token
@api.model def update_opt_out(self, email, res_ids, value):
def update_opt_out(self, mailing_id, email, res_ids, value):
"""Save unsubscription reason when opting out from mailing.""" """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"): if value and self.env.context.get("default_reason_id"):
for res_id in res_ids: for res_id in res_ids:
# reason_id and details are expected from the context # reason_id and details are expected from the context
self.env["mail.unsubscription"].create({ self.env["mail.unsubscription"].create({
"email": email, "email": email,
"mass_mailing_id": mailing.id, "mass_mailing_id": self.id,
"unsubscriber_id": "%s,%d" % ( "unsubscriber_id": "%s,%d" % (
mailing.mailing_model, int(res_id)), self.mailing_model, int(res_id)),
}) })
return super(MailMassMailing, self).update_opt_out( return super(MailMassMailing, self).update_opt_out(
mailing_id, email, res_ids, value) email, res_ids, value)

View File

@ -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 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 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 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

1 id name model_id:id group_id:id perm_read perm_write perm_create perm_unlink
3 read_unsubscription_reason_employee Employee users can read unsubscription reasons model_mail_unsubscription_reason base.group_user 1 0 0 0
4 write_unsubscription_reason Mass mailing managers can manage unsubscription reasons model_mail_unsubscription_reason mass_mailing.group_mass_mailing_user 1 1 1 1
5 read_unsubscription Marketing users can read unsubscriptions model_mail_unsubscription mass_mailing.group_mass_mailing_user 1 0 0 0
6 write_unsubscription Mass mailing managers can manage unsubscriptions Admins can change unsubscriptions model_mail_unsubscription mass_mailing.group_mass_mailing_user base.group_system 1 1 1 1

View File

@ -3,7 +3,8 @@
odoo.define("mass_mailing_custom_unsubscribe.contact_tour", odoo.define("mass_mailing_custom_unsubscribe.contact_tour",
function (require) { function (require) {
"use strict"; "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.require_details");
require("mass_mailing_custom_unsubscribe.unsubscribe"); require("mass_mailing_custom_unsubscribe.unsubscribe");
@ -14,64 +15,60 @@ odoo.define("mass_mailing_custom_unsubscribe.contact_tour",
}, },
}); });
Tour.register({ tour.register(
id: "mass_mailing_custom_unsubscribe_tour_contact", "mass_mailing_custom_unsubscribe_tour_contact",
name: "Mass mailing contact unsubscribes",
mode: "test",
steps: [
{ {
title: "Unsubscription reasons are invisible", tour: true,
waitFor: "#unsubscribe_form .js_unsubscription_reason:hidden", wait_for: base.ready(),
},
[
{
content: "Unsubscription reasons are invisible",
trigger: "#unsubscribe_form:has(.js_unsubscription_reason:hidden)",
}, },
{ {
title: "Uncheck list 0", content: "Uncheck list 0",
element: "li:contains('test list 0') input", trigger: "li:contains('test list 0') input",
waitFor: "li:contains('test list 0') input:checked",
// List 2 is not cross unsubscriptable // 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", content: "Uncheck list 1",
element: "li:contains('test list 1') input:checked", trigger: "li:contains('test list 1') input:checked",
waitFor: ".js_unsubscription_reason:visible", extra_trigger: ".js_unsubscription_reason:visible",
}, },
{ {
title: "Choose other reason", content: "Choose other reason",
element: ".radio:contains('Other reason') :radio", trigger: ".radio:contains('Other reason') :radio",
waitFor: ".radio:contains('Other reason') " + extra_trigger: ".radio:contains('Other reason') " +
":radio:not(:checked)", ":radio:not(:checked)",
}, },
{ {
title: "Add details to reason", content: "Add details to reason",
element: "[name='details']:visible:propRequired", trigger: "[name='details']:visible:propRequired",
sampleText: "I want to unsubscribe because I want. Period.", run: "text I want to unsubscribe because I want. Period.",
waitFor: ".radio:contains('Other reason') :radio:checked", extra_trigger: ".radio:contains('Other reason') :radio:checked",
}, },
{ {
title: "Update subscriptions 1st time", content: "Update subscriptions 1st time",
element: "#unsubscribe_form :submit", trigger: "#unsubscribe_form :submit",
}, },
{ {
title: "Subscribe again to list 0", content: "Subscribe again to list 0",
element: "li:contains('test list 0') input:not(:checked)", trigger: "body:not(:has(#unsubscribe_form .js_unsubscription_reason:visible)):has(.alert-success, li:contains('test list 0') input:not(:checked))",
waitFor: ".alert-success", run: function () {
waitNot: "#unsubscribe_form .js_unsubscription_reason:visible",
onend: function () {
// This one will get the success again after next step // This one will get the success again after next step
$(".alert-success").removeClass("alert-success"); $(".alert-success").removeClass("alert-success");
}, },
}, },
{ {
title: "Update subscriptions 2nd time", content: "Update subscriptions 2nd time",
element: "#unsubscribe_form :submit", trigger: "#unsubscribe_form:not(:has(.js_unsubscription_reason:visible)) :submit",
waitNot: "#unsubscribe_form .js_unsubscription_reason:visible",
}, },
{ {
title: "Resuscription was OK", content: "Resuscription was OK",
waitFor: ".alert-success", trigger: ".alert-success",
} }
] ]
}); );
return Tour.tours.mass_mailing_custom_unsubscribe_tour_contact;
}); });

View File

@ -3,7 +3,8 @@
odoo.define("mass_mailing_custom_unsubscribe.partner_tour", odoo.define("mass_mailing_custom_unsubscribe.partner_tour",
function (require) { function (require) {
"use strict"; "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.require_details");
require("mass_mailing_custom_unsubscribe.unsubscribe"); require("mass_mailing_custom_unsubscribe.unsubscribe");
@ -14,36 +15,32 @@ odoo.define("mass_mailing_custom_unsubscribe.partner_tour",
}, },
}); });
Tour.register({ tour.register(
id: "mass_mailing_custom_unsubscribe_tour_partner", "mass_mailing_custom_unsubscribe_tour_partner",
name: "Mass mailing partner unsubscribes",
mode: "test",
steps: [
{ {
title: "Choose other reason", tour: true,
element: ".radio:contains('Other reason') " + wait_for: base.ready(),
":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", content: "Switch to not interested reason",
element: ".radio:contains(\"I'm not interested\") " + trigger: ".radio:contains(\"I'm not interested\") :radio:not(:checked)",
":radio:not(:checked)", extra_trigger: "[name='details']:propRequired",
waitFor: "[name='details']:propRequired",
}, },
{ {
title: "Unsubscribe", content: "Unsubscribe",
element: "#reason_form :submit", trigger: "#reason_form :submit",
waitNot: "[name='details']:propRequired", extra_trigger: "body:not(:has([name='details']:propRequired))",
}, },
{ {
title: "Successfully unsubscribed", content: "Successfully unsubscribed",
waitFor: ".alert-success:contains(" + trigger: "body:not(:has(#reason_form)) .alert-success:contains('Your changes have been saved.')",
"'Your changes have been saved.')",
waitNot: "#reason_form",
}, },
] ]
}); );
return Tour.tours.mass_mailing_custom_unsubscribe_tour_partner;
}); });

View File

@ -7,8 +7,11 @@ from openerp.tests.common import HttpCase
class UICase(HttpCase): 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): 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.assertIn("&token=", url)
self.assertTrue(url.startswith(self.domain)) self.assertTrue(url.startswith(self.domain))
self.url = url.replace(self.domain, "", 1) self.url = url.replace(self.domain, "", 1)
@ -20,6 +23,7 @@ class UICase(HttpCase):
self.mail_postprocess_patch = mock.patch( self.mail_postprocess_patch = mock.patch(
"openerp.addons.mass_mailing.models.mail_mail.MailMail." "openerp.addons.mass_mailing.models.mail_mail.MailMail."
"_postprocess_sent_message", "_postprocess_sent_message",
autospec=True,
side_effect=self.extract_url, side_effect=self.extract_url,
) )
with self.tempenv() as env: with self.tempenv() as env:
@ -37,11 +41,7 @@ class UICase(HttpCase):
"contact_list_ids": [(6, 0, self.lists.ids)], "contact_list_ids": [(6, 0, self.lists.ids)],
"reply_to_mode": "thread", "reply_to_mode": "thread",
}) })
self.mailings[n].write( self.mailings[n]._onchange_model_and_list()
self.mailings[n].on_change_model_and_list(
self.mailings[n].mailing_model,
self.mailings[n].contact_list_ids.ids,
)["value"])
# HACK https://github.com/odoo/odoo/pull/14429 # HACK https://github.com/odoo/odoo/pull/14429
self.mailings[n].body_html = """ self.mailings[n].body_html = """
<div> <div>
@ -86,13 +86,12 @@ class UICase(HttpCase):
tour = "mass_mailing_custom_unsubscribe_tour_contact" tour = "mass_mailing_custom_unsubscribe_tour_contact"
self.phantom_js( self.phantom_js(
url_path=self.url, url_path=self.url,
code=("odoo.__DEBUG__.services['web.Tour']" code=self._tour_run % tour,
".run('%s', 'test')") % tour, ready=self._tour_ready % tour)
ready="odoo.__DEBUG__.services['web.Tour'].tours.%s" % tour)
# Check results from running tour # Check results from running tour
with self.tempenv() as env: 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.assertTrue(self.contacts[1].opt_out)
self.assertFalse(self.contacts[2].opt_out) self.assertFalse(self.contacts[2].opt_out)
unsubscriptions = env["mail.unsubscription"].search([ unsubscriptions = env["mail.unsubscription"].search([
@ -130,9 +129,8 @@ class UICase(HttpCase):
tour = "mass_mailing_custom_unsubscribe_tour_partner" tour = "mass_mailing_custom_unsubscribe_tour_partner"
self.phantom_js( self.phantom_js(
url_path=self.url, url_path=self.url,
code=("odoo.__DEBUG__.services['web.Tour']" code=self._tour_run % tour,
".run('%s', 'test')") % tour, ready=self._tour_ready % tour)
ready="odoo.__DEBUG__.services['web.Tour'].tours.%s" % tour)
# Check results from running tour # Check results from running tour
with self.tempenv() as env: with self.tempenv() as env:

View File

@ -26,9 +26,9 @@
<field name="model">mail.unsubscription.reason</field> <field name="model">mail.unsubscription.reason</field>
<field name="arch" type="xml"> <field name="arch" type="xml">
<tree> <tree>
<field name="sequence" widget="handle"/>
<field name="name"/> <field name="name"/>
<field name="details_required"/> <field name="details_required"/>
<field name="sequence" invisible="True"/>
</tree> </tree>
</field> </field>
</record> </record>