[IMP] mass_mailing_custom_unsubscribe: black, isort, prettier
This commit is contained in:
parent
9cdbc02cfc
commit
844c382fe7
@ -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,
|
|
||||||
}
|
}
|
||||||
|
@ -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),
|
||||||
|
@ -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)
|
||||||
|
@ -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()
|
||||||
|
@ -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?'",
|
||||||
|
)
|
||||||
|
@ -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.")
|
|
||||||
|
@ -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
|
||||||
|
@ -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))
|
||||||
|
@ -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,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
Loading…
Reference in New Issue
Block a user