mirror of https://github.com/OCA/social.git
[MIG][mass_mailing_custom_unsubscribe] Migrate to v10
parent
d6f874d1e2
commit
1af75e7684
|
@ -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
|
||||
|
|
|
@ -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',
|
||||
|
|
|
@ -29,7 +29,7 @@ class CustomUnsubscribe(MassMailController):
|
|||
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 @@ class CustomUnsubscribe(MassMailController):
|
|||
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 @@ class CustomUnsubscribe(MassMailController):
|
|||
"""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)
|
||||
|
|
|
@ -2,14 +2,13 @@
|
|||
# Copyright 2016 Jairo Llopis <jairo.llopis@tecnativa.com>
|
||||
# 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)
|
||||
|
|
|
@ -37,18 +37,17 @@ class MailMassMailing(models.Model):
|
|||
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)
|
||||
|
|
|
@ -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
|
||||
|
|
|
|
@ -3,7 +3,8 @@
|
|||
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");
|
||||
|
||||
|
@ -14,64 +15,60 @@ odoo.define("mass_mailing_custom_unsubscribe.contact_tour",
|
|||
},
|
||||
});
|
||||
|
||||
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",
|
||||
{
|
||||
title: "Unsubscription reasons are invisible",
|
||||
waitFor: "#unsubscribe_form .js_unsubscription_reason:hidden",
|
||||
tour: true,
|
||||
wait_for: base.ready(),
|
||||
},
|
||||
[
|
||||
{
|
||||
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') " +
|
||||
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;
|
||||
);
|
||||
});
|
||||
|
|
|
@ -3,7 +3,8 @@
|
|||
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");
|
||||
|
||||
|
@ -14,36 +15,32 @@ odoo.define("mass_mailing_custom_unsubscribe.partner_tour",
|
|||
},
|
||||
});
|
||||
|
||||
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",
|
||||
{
|
||||
title: "Choose other reason",
|
||||
element: ".radio:contains('Other reason') " +
|
||||
":radio:not(:checked)",
|
||||
waitFor: "#reason_form .js_unsubscription_reason",
|
||||
tour: true,
|
||||
wait_for: base.ready(),
|
||||
},
|
||||
[
|
||||
{
|
||||
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;
|
||||
);
|
||||
});
|
||||
|
|
|
@ -7,8 +7,11 @@ from openerp.tests.common import 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):
|
||||
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 @@ class UICase(HttpCase):
|
|||
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 @@ class UICase(HttpCase):
|
|||
"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 = """
|
||||
<div>
|
||||
|
@ -86,13 +86,12 @@ class UICase(HttpCase):
|
|||
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 @@ class UICase(HttpCase):
|
|||
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:
|
||||
|
|
|
@ -26,9 +26,9 @@
|
|||
<field name="model">mail.unsubscription.reason</field>
|
||||
<field name="arch" type="xml">
|
||||
<tree>
|
||||
<field name="sequence" widget="handle"/>
|
||||
<field name="name"/>
|
||||
<field name="details_required"/>
|
||||
<field name="sequence" invisible="True"/>
|
||||
</tree>
|
||||
</field>
|
||||
</record>
|
||||
|
|
Loading…
Reference in New Issue