[IMP] mass_mailing_custom_unsubscribe: black, isort, prettier

This commit is contained in:
Carlos Roca 2020-08-24 10:36:19 +02:00 committed by Ernesto Tejeda
parent 9cdbc02cfc
commit 844c382fe7
9 changed files with 258 additions and 225 deletions

View File

@ -3,32 +3,25 @@
# Copyright 2020 Tecnativa - Pedro M. Baeza # Copyright 2020 Tecnativa - Pedro M. Baeza
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
{ {
'name': 'Customizable unsubscription process on mass mailing emails', "name": "Customizable unsubscription process on mass mailing emails",
'summary': 'Know and track (un)subscription reasons, GDPR compliant', "summary": "Know and track (un)subscription reasons, GDPR compliant",
'category': 'Marketing', "category": "Marketing",
'version': '12.0.1.0.2', "version": "12.0.1.0.2",
'depends': [ "depends": ["mass_mailing"],
'mass_mailing', "data": [
"security/ir.model.access.csv",
"data/mail_unsubscription_reason.xml",
"templates/general_reason_form.xml",
"templates/mass_mailing_contact_reason.xml",
"views/assets.xml",
"views/mail_unsubscription_reason_view.xml",
"views/mail_mass_mailing_list_view.xml",
"views/mail_unsubscription_view.xml",
], ],
'data': [ "demo": ["demo/assets.xml"],
'security/ir.model.access.csv', "images": ["images/form.png"],
'data/mail_unsubscription_reason.xml', "author": "Tecnativa," "Odoo Community Association (OCA)",
'templates/general_reason_form.xml', "website": "https://github.com/OCA/social",
'templates/mass_mailing_contact_reason.xml', "license": "AGPL-3",
'views/assets.xml', "installable": True,
'views/mail_unsubscription_reason_view.xml',
'views/mail_mass_mailing_list_view.xml',
'views/mail_unsubscription_view.xml',
],
'demo': [
'demo/assets.xml',
],
'images': [
'images/form.png',
],
'author': 'Tecnativa,'
'Odoo Community Association (OCA)',
'website': 'https://github.com/OCA/social',
'license': 'AGPL-3',
'installable': True,
} }

View File

@ -6,6 +6,7 @@
import logging import logging
from odoo.http import request, route from odoo.http import request, route
from odoo.addons.mass_mailing.controllers.main import MassMailController from odoo.addons.mass_mailing.controllers.main import MassMailController
_logger = logging.getLogger(__name__) _logger = logging.getLogger(__name__)
@ -35,14 +36,15 @@ class CustomUnsubscribe(MassMailController):
"reasons": reasons, "reasons": reasons,
"res_id": res_id, "res_id": res_id,
"token": token, "token": token,
}) },
)
@route() @route()
def mailing(self, mailing_id, email=None, res_id=None, token="", **post): def mailing(self, mailing_id, email=None, res_id=None, token="", **post):
"""Ask/save unsubscription reason.""" """Ask/save unsubscription reason."""
_logger.debug( _logger.debug(
"Called `mailing()` with: %r", "Called `mailing()` with: %r", (mailing_id, email, res_id, token, post)
(mailing_id, email, res_id, token, post)) )
reasons = request.env["mail.unsubscription.reason"].search([]) reasons = request.env["mail.unsubscription.reason"].search([])
res_id = res_id and int(res_id) res_id = res_id and int(res_id)
try: try:
@ -55,67 +57,89 @@ class CustomUnsubscribe(MassMailController):
# Unsubscribe, saving reason and details by context # Unsubscribe, saving reason and details by context
details = post.get("details", False) details = post.get("details", False)
self._add_extra_context(mailing_id, res_id, reason_id, details) self._add_extra_context(mailing_id, res_id, reason_id, details)
mailing_obj = request.env['mail.mass_mailing'] mailing_obj = request.env["mail.mass_mailing"]
mass_mailing = mailing_obj.sudo().browse(mailing_id) mass_mailing = mailing_obj.sudo().browse(mailing_id)
model = mass_mailing.mailing_model_real model = mass_mailing.mailing_model_real
if ("opt_out" in request.env[model]._fields and if (
model != "mail.mass_mailing.contact"): "opt_out" in request.env[model]._fields
and model != "mail.mass_mailing.contact"
):
mass_mailing.update_opt_out_other(email, [res_id], True) mass_mailing.update_opt_out_other(email, [res_id], True)
result = request.render("mass_mailing.page_unsubscribed", { result = request.render(
"email": email, "mass_mailing.page_unsubscribed",
"mailing_id": mailing_id, {
"res_id": res_id, "email": email,
"show_blacklist_button": request.env[ "mailing_id": mailing_id,
"ir.config_parameter" "res_id": res_id,
].sudo().get_param( "show_blacklist_button": request.env["ir.config_parameter"]
"mass_mailing.show_blacklist_buttons" .sudo()
), .get_param("mass_mailing.show_blacklist_buttons"),
}) },
)
result.qcontext.update({"reasons": reasons}) result.qcontext.update({"reasons": reasons})
else: else:
# 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
result = super().mailing( result = super().mailing(mailing_id, email, res_id, token=token, **post)
mailing_id, email, res_id, token=token, **post)
if model == "mail.mass_mailing.contact": if model == "mail.mass_mailing.contact":
# update list_ids taking into account # update list_ids taking into account
# not_cross_unsubscriptable field # not_cross_unsubscriptable field
result.qcontext.update({ result.qcontext.update(
"reasons": reasons, {
"list_ids": result.qcontext["list_ids"].filtered( "reasons": reasons,
lambda mailing_list: "list_ids": result.qcontext["list_ids"].filtered(
not mailing_list.not_cross_unsubscriptable or lambda m_list: not m_list.not_cross_unsubscriptable
mailing_list in mass_mailing.contact_list_ids or m_list in mass_mailing.contact_list_ids
) ),
}) }
)
return result return result
@route() @route()
def unsubscribe(self, mailing_id, opt_in_ids, opt_out_ids, email, res_id, def unsubscribe(
token, reason_id=None, details=None): 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.""" """Store unsubscription reasons when unsubscribing from RPC."""
# Update request context # Update request context
self._add_extra_context(mailing_id, res_id, reason_id, details) self._add_extra_context(mailing_id, res_id, reason_id, details)
_logger.debug( _logger.debug(
"Called `unsubscribe()` with: %r", "Called `unsubscribe()` with: %r",
(mailing_id, opt_in_ids, opt_out_ids, email, res_id, token, (
reason_id, details)) mailing_id,
opt_in_ids,
opt_out_ids,
email,
res_id,
token,
reason_id,
details,
),
)
return super().unsubscribe( return super().unsubscribe(
mailing_id, opt_in_ids, opt_out_ids, email, res_id, token) mailing_id, opt_in_ids, opt_out_ids, email, res_id, token
)
@route() @route()
def blacklist_add(self, mailing_id, res_id, email, token, reason_id=None, def blacklist_add(
details=None): self, mailing_id, res_id, email, token, reason_id=None, details=None
):
self._add_extra_context(mailing_id, res_id, reason_id, details) self._add_extra_context(mailing_id, res_id, reason_id, details)
return super().blacklist_add( return super().blacklist_add(mailing_id, res_id, email, token)
mailing_id, res_id, email, token)
@route() @route()
def blacklist_remove(self, mailing_id, res_id, email, token, def blacklist_remove(
reason_id=None, details=None): self, mailing_id, res_id, email, token, reason_id=None, details=None
):
self._add_extra_context(mailing_id, res_id, reason_id, details) self._add_extra_context(mailing_id, res_id, reason_id, details)
return super().blacklist_remove( return super().blacklist_remove(mailing_id, res_id, email, token)
mailing_id, res_id, email, token)
def _add_extra_context(self, mailing_id, res_id, reason_id, details): def _add_extra_context(self, mailing_id, res_id, reason_id, details):
environ = request.httprequest.headers.environ environ = request.httprequest.headers.environ
@ -123,11 +147,8 @@ class CustomUnsubscribe(MassMailController):
# redefinition of _add and _remove methods of the mail.blacklist class # redefinition of _add and _remove methods of the mail.blacklist class
extra_context = { extra_context = {
"default_metadata": "\n".join( "default_metadata": "\n".join(
"%s: %s" % (val, environ.get(val)) for val in ( "{}: {}".format(val, environ.get(val))
"REMOTE_ADDR", for val in ("REMOTE_ADDR", "HTTP_USER_AGENT", "HTTP_ACCEPT_LANGUAGE")
"HTTP_USER_AGENT",
"HTTP_ACCEPT_LANGUAGE",
)
), ),
"mailing_id": mailing_id, "mailing_id": mailing_id,
"unsubscription_res_id": int(res_id), "unsubscription_res_id": int(res_id),

View File

@ -5,34 +5,36 @@ from odoo import models
class MailBlackList(models.Model): class MailBlackList(models.Model):
_inherit = 'mail.blacklist' _inherit = "mail.blacklist"
def _add(self, email): def _add(self, email):
mailing_id = self.env.context.get('mailing_id') mailing_id = self.env.context.get("mailing_id")
res_id = self.env.context.get('unsubscription_res_id') res_id = self.env.context.get("unsubscription_res_id")
if mailing_id and res_id: if mailing_id and res_id:
mailing = self.env['mail.mass_mailing'].browse(mailing_id, mailing = self.env["mail.mass_mailing"].browse(mailing_id, self._prefetch)
self._prefetch)
model_name = mailing.mailing_model_real model_name = mailing.mailing_model_real
self.env["mail.unsubscription"].create({ self.env["mail.unsubscription"].create(
"email": email, {
"mass_mailing_id": mailing_id, "email": email,
"unsubscriber_id": "%s,%d" % (model_name, res_id), "mass_mailing_id": mailing_id,
"action": "blacklist_add", "unsubscriber_id": "%s,%d" % (model_name, res_id),
}) "action": "blacklist_add",
}
)
return super()._add(email) return super()._add(email)
def _remove(self, email): def _remove(self, email):
mailing_id = self.env.context.get('mailing_id') mailing_id = self.env.context.get("mailing_id")
res_id = self.env.context.get('unsubscription_res_id') res_id = self.env.context.get("unsubscription_res_id")
if mailing_id and res_id: if mailing_id and res_id:
mailing = self.env['mail.mass_mailing'].browse(mailing_id, mailing = self.env["mail.mass_mailing"].browse(mailing_id, self._prefetch)
self._prefetch)
model_name = mailing.mailing_model_real model_name = mailing.mailing_model_real
self.env["mail.unsubscription"].create({ self.env["mail.unsubscription"].create(
"email": email, {
"mass_mailing_id": mailing_id, "email": email,
"unsubscriber_id": "%s,%d" % (model_name, res_id), "mass_mailing_id": mailing_id,
"action": "blacklist_rm", "unsubscriber_id": "%s,%d" % (model_name, res_id),
}) "action": "blacklist_rm",
}
)
return super()._remove(email) return super()._remove(email)

View File

@ -2,9 +2,10 @@
# Copyright 2020 Tecnativa - Pedro M. Baeza # Copyright 2020 Tecnativa - Pedro M. Baeza
# 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 itertools import groupby
from odoo import models, tools from odoo import models, tools
from odoo.tools.safe_eval import safe_eval from odoo.tools.safe_eval import safe_eval
from itertools import groupby
class MailMassMailing(models.Model): class MailMassMailing(models.Model):
@ -14,61 +15,65 @@ class MailMassMailing(models.Model):
"""Save unsubscription reason when opting out from mailing.""" """Save unsubscription reason when opting out from mailing."""
self.ensure_one() self.ensure_one()
action = "unsubscription" if value else "subscription" action = "unsubscription" if value else "subscription"
subscription_model = self.env['mail.mass_mailing.list_contact_rel'] subscription_model = self.env["mail.mass_mailing.list_contact_rel"]
opt_out_records = subscription_model.search([ opt_out_records = subscription_model.search(
('contact_id.email', '=ilike', email), [
('list_id', 'in', list_ids), ("contact_id.email", "=ilike", email),
('opt_out', '!=', value), ("list_id", "in", list_ids),
]) ("opt_out", "!=", value),
model_name = 'mail.mass_mailing.contact' ]
for contact, subscriptions in groupby(opt_out_records, )
lambda r: r.contact_id): model_name = "mail.mass_mailing.contact"
for contact, subscriptions in groupby(opt_out_records, lambda r: r.contact_id):
mailing_list_ids = [r.list_id.id for r in subscriptions] mailing_list_ids = [r.list_id.id for r in subscriptions]
# 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, {
"mass_mailing_id": self.id, "email": email,
"unsubscriber_id": "%s,%d" % (model_name, contact.id), "mass_mailing_id": self.id,
'mailing_list_ids': [(6, False, mailing_list_ids)], "unsubscriber_id": "%s,%d" % (model_name, contact.id),
"action": action, "mailing_list_ids": [(6, False, mailing_list_ids)],
}) "action": action,
}
)
return super().update_opt_out(email, list_ids, value) return super().update_opt_out(email, list_ids, value)
def update_opt_out_other(self, email, res_ids, value): def update_opt_out_other(self, email, res_ids, value):
"""Method for changing unsubscription for models with opt_out field.""" """Method for changing unsubscription for models with opt_out field."""
model = self.env[self.mailing_model_real].with_context( model = self.env[self.mailing_model_real].with_context(active_test=False)
active_test=False)
action = "unsubscription" if value else "subscription" action = "unsubscription" if value else "subscription"
if 'opt_out' in model._fields: if "opt_out" in model._fields:
email_fname = 'email_from' email_fname = "email_from"
if 'email' in model._fields: if "email" in model._fields:
email_fname = 'email' email_fname = "email"
records = model.search([ records = model.search(
('id', 'in', res_ids), (email_fname, 'ilike', email)]) [("id", "in", res_ids), (email_fname, "ilike", email)]
records.write({'opt_out': value}) )
records.write({"opt_out": value})
for res_id in res_ids: for res_id in res_ids:
self.env["mail.unsubscription"].create({ self.env["mail.unsubscription"].create(
"email": email, {
"mass_mailing_id": self.id, "email": email,
"unsubscriber_id": "%s,%d" % ( "mass_mailing_id": self.id,
self.mailing_model_real, res_id), "unsubscriber_id": "%s,%d" % (self.mailing_model_real, res_id),
"action": action, "action": action,
}) }
)
def _get_opt_out_list(self): def _get_opt_out_list(self):
"""Handle models with opt_out field for excluding them.""" """Handle models with opt_out field for excluding them."""
self.ensure_one() self.ensure_one()
model = self.env[self.mailing_model_real].with_context( model = self.env[self.mailing_model_real].with_context(active_test=False)
active_test=False) if (
if (self.mailing_model_real != "mail.mass_mailing.contact" and self.mailing_model_real != "mail.mass_mailing.contact"
'opt_out' in model._fields): and "opt_out" in model._fields
email_fname = 'email_from' ):
if 'email' in model._fields: email_fname = "email_from"
email_fname = 'email' if "email" in model._fields:
email_fname = "email"
domain = safe_eval(self.mailing_domain) domain = safe_eval(self.mailing_domain)
domain = [('opt_out', '=', True)] + domain domain = [("opt_out", "=", True)] + domain
recs = self.env[self.mailing_model_real].search(domain) recs = self.env[self.mailing_model_real].search(domain)
normalized_email = ( normalized_email = (tools.email_split(c[email_fname]) for c in recs)
tools.email_split(c[email_fname]) for c in recs) return {e[0].lower() for e in normalized_email if e}
return set(e[0].lower() for e in normalized_email if e)
return super()._get_opt_out_list() return super()._get_opt_out_list()

View File

@ -10,5 +10,6 @@ class MailMassMailing(models.Model):
not_cross_unsubscriptable = fields.Boolean( not_cross_unsubscriptable = fields.Boolean(
string="Not cross unsubscriptable", string="Not cross unsubscriptable",
help="If you mark this field, this list won't be shown when " help="If you mark this field, this list won't be shown when "
"unsubscribing from other mailing list, in the section: " "unsubscribing from other mailing list, in the section: "
"'Is there any other mailing list you want to leave?'") "'Is there any other mailing list you want to leave?'",
)

View File

@ -2,8 +2,9 @@
# 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 odoo import _, api, fields, models from odoo import _, api, fields, models
from odoo.addons.mass_mailing.models.mass_mailing import \
MASS_MAILING_BUSINESS_MODELS from odoo.addons.mass_mailing.models.mass_mailing import MASS_MAILING_BUSINESS_MODELS
from .. import exceptions from .. import exceptions
@ -14,11 +15,8 @@ class MailUnsubscription(models.Model):
_rec_name = "date" _rec_name = "date"
_order = "date DESC" _order = "date DESC"
date = fields.Datetime( date = fields.Datetime(default=lambda self: self._default_date(), required=True)
default=lambda self: self._default_date(), email = fields.Char(required=True)
required=True)
email = fields.Char(
required=True)
action = fields.Selection( action = fields.Selection(
selection=[ selection=[
("subscription", "Subscription"), ("subscription", "Subscription"),
@ -34,11 +32,13 @@ class MailUnsubscription(models.Model):
"mail.mass_mailing", "mail.mass_mailing",
"Mass mailing", "Mass mailing",
required=True, required=True,
help="Mass mailing from which he was unsubscribed.") help="Mass mailing from which he was unsubscribed.",
)
unsubscriber_id = fields.Reference( unsubscriber_id = fields.Reference(
lambda self: self._selection_unsubscriber_id(), lambda self: self._selection_unsubscriber_id(),
"(Un)subscriber", "(Un)subscriber",
help="Who was subscribed or unsubscribed.") help="Who was subscribed or unsubscribed.",
)
mailing_list_ids = fields.Many2many( mailing_list_ids = fields.Many2many(
comodel_name="mail.mass_mailing.list", comodel_name="mail.mass_mailing.list",
string="Mailing lists", string="Mailing lists",
@ -48,21 +48,19 @@ class MailUnsubscription(models.Model):
"mail.unsubscription.reason", "mail.unsubscription.reason",
"Reason", "Reason",
ondelete="restrict", ondelete="restrict",
help="Why the unsubscription was made.") help="Why the unsubscription was made.",
details = fields.Text( )
help="More details on why the unsubscription was made.") details = fields.Text(help="More details on why the unsubscription was made.")
details_required = fields.Boolean( details_required = fields.Boolean(related="reason_id.details_required")
related="reason_id.details_required")
metadata = fields.Text( metadata = fields.Text(
readonly=True, readonly=True, help="HTTP request metadata used when creating this record."
help="HTTP request metadata used when creating this record.",
) )
def map_mailing_list_models(self, models): def map_mailing_list_models(self, models):
model_mapped = [] model_mapped = []
for model in models: for model in models:
if model == 'mail.mass_mailing.list': if model == "mail.mass_mailing.list":
model_mapped.append(('mail.mass_mailing.contact', model)) model_mapped.append(("mail.mass_mailing.contact", model))
else: else:
model_mapped.append((model, model)) model_mapped.append((model, model))
return model_mapped return model_mapped
@ -74,8 +72,11 @@ class MailUnsubscription(models.Model):
@api.model @api.model
def _selection_unsubscriber_id(self): def _selection_unsubscriber_id(self):
"""Models that can be linked to a ``mail.mass_mailing``.""" """Models that can be linked to a ``mail.mass_mailing``."""
model = self.env['ir.model'].search( model = (
[('model', 'in', MASS_MAILING_BUSINESS_MODELS)]).mapped('model') self.env["ir.model"]
.search([("model", "in", MASS_MAILING_BUSINESS_MODELS)])
.mapped("model")
)
return self.map_mailing_list_models(model) return self.map_mailing_list_models(model)
@api.multi @api.multi
@ -86,7 +87,8 @@ class MailUnsubscription(models.Model):
unsubscription_states = {"unsubscription", "blacklist_add"} unsubscription_states = {"unsubscription", "blacklist_add"}
if one.action in unsubscription_states and not one.reason_id: if one.action in unsubscription_states and not one.reason_id:
raise exceptions.ReasonRequiredError( raise exceptions.ReasonRequiredError(
_("Please indicate why are you unsubscribing.")) _("Please indicate why are you unsubscribing.")
)
@api.multi @api.multi
@api.constrains("details", "reason_id") @api.constrains("details", "reason_id")
@ -95,7 +97,8 @@ class MailUnsubscription(models.Model):
for one in self: for one in self:
if not one.details and one.details_required: if not one.details and one.details_required:
raise exceptions.DetailsRequiredError( raise exceptions.DetailsRequiredError(
_("Please provide details on why you are unsubscribing.")) _("Please provide details on why you are unsubscribing.")
)
@api.model @api.model
def create(self, vals): def create(self, vals):
@ -110,12 +113,8 @@ class MailUnsubscriptionReason(models.Model):
_description = "Mail unsubscription reason" _description = "Mail unsubscription reason"
_order = "sequence, name" _order = "sequence, name"
name = fields.Char( name = fields.Char(index=True, translate=True, required=True)
index=True,
translate=True,
required=True)
details_required = fields.Boolean( details_required = fields.Boolean(
help="Check to ask for more details when this reason is selected.") help="Check to ask for more details when this reason is selected."
sequence = fields.Integer( )
index=True, sequence = fields.Integer(index=True, help="Position of the reason in the list.")
help="Position of the reason in the list.")

View File

@ -2,4 +2,5 @@
# 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 . import test_ui from . import test_ui
# from . import test_unsubscription # from . import test_unsubscription

View File

@ -1,9 +1,10 @@
# 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).
import mock import mock
from odoo.tests.common import HttpCase
from werkzeug import urls from werkzeug import urls
from odoo.tests.common import HttpCase
class UICase(HttpCase): class UICase(HttpCase):
_tour_run = "odoo.__DEBUG__.services['web_tour.tour'].run('%s')" _tour_run = "odoo.__DEBUG__.services['web_tour.tour'].run('%s')"
@ -11,7 +12,7 @@ class UICase(HttpCase):
def extract_url(self, mail, *args, **kwargs): def extract_url(self, mail, *args, **kwargs):
url = mail._get_unsubscribe_url(self.email) url = mail._get_unsubscribe_url(self.email)
self.assertTrue(urls.url_parse(url).decode_query().get('token')) self.assertTrue(urls.url_parse(url).decode_query().get("token"))
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)
return True return True
@ -26,24 +27,27 @@ class UICase(HttpCase):
side_effect=self.extract_url, side_effect=self.extract_url,
) )
self.domain = self.env["ir.config_parameter"].get_param('web.base.url') self.domain = self.env["ir.config_parameter"].get_param("web.base.url")
List = self.lists = self.env["mail.mass_mailing.list"] List = self.lists = self.env["mail.mass_mailing.list"]
for n in range(4): for n in range(4):
self.lists += List.create({"name": "test list %d" % n}) self.lists += List.create({"name": "test list %d" % n})
self.contact = self.env["mail.mass_mailing.contact"].create({ self.contact = self.env["mail.mass_mailing.contact"].create(
"name": "test contact", {
"email": self.email, "name": "test contact",
"list_ids": [(6, False, self.lists.ids)], "email": self.email,
}) "list_ids": [(6, False, self.lists.ids)],
self.mailing = self.env["mail.mass_mailing"].create({ }
"name": "test mailing %d" % n, )
"mailing_model_id": self.env.ref( self.mailing = self.env["mail.mass_mailing"].create(
'mass_mailing.model_mail_mass_mailing_list').id, {
"contact_list_ids": [ "name": "test mailing %d" % n,
(6, 0, [self.lists[0].id, self.lists[3].id]) "mailing_model_id": self.env.ref(
], "mass_mailing.model_mail_mass_mailing_list"
"reply_to_mode": "thread", ).id,
}) "contact_list_ids": [(6, 0, [self.lists[0].id, self.lists[3].id])],
"reply_to_mode": "thread",
}
)
self.mailing._onchange_model_and_list() self.mailing._onchange_model_and_list()
# HACK https://github.com/odoo/odoo/pull/14429 # HACK https://github.com/odoo/odoo/pull/14429
self.mailing.body_html = """ self.mailing.body_html = """
@ -76,7 +80,8 @@ class UICase(HttpCase):
url_path=self.url, url_path=self.url,
code=self._tour_run % tour, code=self._tour_run % tour,
ready=self._tour_ready % tour, ready=self._tour_ready % tour,
login='demo') login="demo",
)
# Check results from running tour # Check results from running tour
self.assertFalse(self.lists[0].subscription_contact_ids.opt_out) self.assertFalse(self.lists[0].subscription_contact_ids.opt_out)
@ -92,32 +97,31 @@ class UICase(HttpCase):
# first unsubscription # first unsubscription
reason = "mass_mailing_custom_unsubscribe.reason_other" reason = "mass_mailing_custom_unsubscribe.reason_other"
unsubscription_1 = self.env["mail.unsubscription"].search( unsubscription_1 = self.env["mail.unsubscription"].search(
common_domain + [ common_domain
+ [
("action", "=", "unsubscription"), ("action", "=", "unsubscription"),
("details", "=", "I want to unsubscribe because I want. " ("details", "=", "I want to unsubscribe because I want. " "Period."),
"Period."),
("reason_id", "=", self.env.ref(reason).id), ("reason_id", "=", self.env.ref(reason).id),
] ]
) )
# second unsubscription # second unsubscription
reason = "mass_mailing_custom_unsubscribe.reason_not_interested" reason = "mass_mailing_custom_unsubscribe.reason_not_interested"
unsubscription_2 = self.env["mail.unsubscription"].search( unsubscription_2 = self.env["mail.unsubscription"].search(
common_domain + [ common_domain
+ [
("action", "=", "unsubscription"), ("action", "=", "unsubscription"),
("reason_id", "=", self.env.ref(reason).id), ("reason_id", "=", self.env.ref(reason).id),
] ]
) )
# re-subscription from self.lists[3] # re-subscription from self.lists[3]
unsubscription_3 = self.env["mail.unsubscription"].search( unsubscription_3 = self.env["mail.unsubscription"].search(
common_domain + [ common_domain + [("action", "=", "subscription")]
("action", "=", "subscription"),
]
) )
# unsubscriptions above are all unsubscriptions saved during the # unsubscriptions above are all unsubscriptions saved during the
# tour and they are all the existing unsubscriptions # tour and they are all the existing unsubscriptions
self.assertEqual( self.assertEqual(
unsubscription_1 | unsubscription_2 | unsubscription_3, unsubscription_1 | unsubscription_2 | unsubscription_3,
self.env["mail.unsubscription"].search([]) self.env["mail.unsubscription"].search([]),
) )
self.assertEqual(3, len(self.env["mail.unsubscription"].search([]))) self.assertEqual(3, len(self.env["mail.unsubscription"].search([])))
@ -125,13 +129,12 @@ class UICase(HttpCase):
"""Test a partner that wants to unsubscribe.""" """Test a partner that wants to unsubscribe."""
# Change mailing to be sent to partner # Change mailing to be sent to partner
partner_id = self.env["res.partner"].name_create( partner_id = self.env["res.partner"].name_create(
"Demo Partner <%s>" % self.email)[0] "Demo Partner <%s>" % self.email
self.mailing.mailing_model_id = self.env.ref( )[0]
"base.model_res_partner") self.mailing.mailing_model_id = self.env.ref("base.model_res_partner")
self.mailing.mailing_domain = repr([ self.mailing.mailing_domain = repr(
('is_blacklisted', '=', False), [("is_blacklisted", "=", False), ("id", "=", partner_id)]
('id', '=', partner_id), )
])
# Extract the unsubscription link from the message body # Extract the unsubscription link from the message body
with self.mail_postprocess_patch: with self.mail_postprocess_patch:
self.mailing.send_mail() self.mailing.send_mail()
@ -141,18 +144,21 @@ class UICase(HttpCase):
url_path=self.url, url_path=self.url,
code=self._tour_run % tour, code=self._tour_run % tour,
ready=self._tour_ready % tour, ready=self._tour_ready % tour,
login='demo') login="demo",
)
# Check results from running tour # Check results from running tour
partner = self.env["res.partner"].browse(partner_id) partner = self.env["res.partner"].browse(partner_id)
self.assertTrue(partner.is_blacklisted) self.assertTrue(partner.is_blacklisted)
reason_xid = "mass_mailing_custom_unsubscribe.reason_not_interested" reason_xid = "mass_mailing_custom_unsubscribe.reason_not_interested"
unsubscriptions = self.env["mail.unsubscription"].search([ unsubscriptions = self.env["mail.unsubscription"].search(
("action", "=", 'blacklist_add'), [
("mass_mailing_id", "=", self.mailing.id), ("action", "=", "blacklist_add"),
("email", "=", self.email), ("mass_mailing_id", "=", self.mailing.id),
("unsubscriber_id", "=", "res.partner,%d" % partner_id), ("email", "=", self.email),
("details", "=", False), ("unsubscriber_id", "=", "res.partner,%d" % partner_id),
("reason_id", "=", self.env.ref(reason_xid).id), ("details", "=", False),
]) ("reason_id", "=", self.env.ref(reason_xid).id),
]
)
self.assertEqual(1, len(unsubscriptions)) self.assertEqual(1, len(unsubscriptions))

View File

@ -2,6 +2,7 @@
# 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 odoo.tests.common import SavepointCase from odoo.tests.common import SavepointCase
from .. import exceptions from .. import exceptions
@ -9,22 +10,26 @@ class UnsubscriptionCase(SavepointCase):
def test_details_required(self): def test_details_required(self):
"""Cannot create unsubscription without details when required.""" """Cannot create unsubscription without details when required."""
with self.assertRaises(exceptions.DetailsRequiredError): with self.assertRaises(exceptions.DetailsRequiredError):
self.env["mail.unsubscription"].create({ self.env["mail.unsubscription"].create(
"email": "axelor@yourcompany.example.com", {
"mass_mailing_id": self.env.ref("mass_mailing.mass_mail_1").id, "email": "axelor@yourcompany.example.com",
"unsubscriber_id": "mass_mailing_id": self.env.ref("mass_mailing.mass_mail_1").id,
"res.partner,%d" % self.env.ref("base.res_partner_2").id, "unsubscriber_id": "res.partner,%d"
"reason_id": % self.env.ref("base.res_partner_2").id,
self.env.ref( "reason_id": self.env.ref(
"mass_mailing_custom_unsubscribe.reason_other").id, "mass_mailing_custom_unsubscribe.reason_other"
}) ).id,
}
)
def test_reason_required(self): def test_reason_required(self):
"""Cannot create unsubscription without reason when required.""" """Cannot create unsubscription without reason when required."""
with self.assertRaises(exceptions.ReasonRequiredError): with self.assertRaises(exceptions.ReasonRequiredError):
self.env["mail.unsubscription"].create({ self.env["mail.unsubscription"].create(
"email": "axelor@yourcompany.example.com", {
"mass_mailing_id": self.env.ref("mass_mailing.mass_mail_1").id, "email": "axelor@yourcompany.example.com",
"unsubscriber_id": "mass_mailing_id": self.env.ref("mass_mailing.mass_mail_1").id,
"res.partner,%d" % self.env.ref("base.res_partner_2").id, "unsubscriber_id": "res.partner,%d"
}) % self.env.ref("base.res_partner_2").id,
}
)