[MIG] mass_mailing_custom_unsubscribe: Migration to 11.0

This commit is contained in:
David 2018-05-25 18:49:55 +02:00 committed by Ernesto Tejeda
parent 0d1b3a499e
commit 2e416ed4d0
27 changed files with 181 additions and 198 deletions

View File

@ -1,96 +1 @@
.. image:: https://img.shields.io/badge/licence-AGPL--3-blue.svg **This file is going to be generated by oca-gen-addon-readme.**
:target: http://www.gnu.org/licenses/agpl-3.0-standalone.html
:alt: License: AGPL-3
==========================================================
Customizable unsubscription process on mass mailing emails
==========================================================
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 has been subscribed or unsubscribed from a
mass mailing.
- Provide proof on why you are sending mass mailings to a given contact, as
required by the GDPR in Europe.
Configuration
=============
Unsubscription Reasons
----------------------
You can customize what reasons will be displayed to your unsubscriptors when
they are going to unsubscribe. To do it:
#. 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.
Usage
=====
Once configured:
#. Go to *Mass Mailing > Mailings > Mass Mailings > Create*.
#. Edit your mass mailing at wish, but remember to add a snippet from
*Footers*, so people have an *Unsubscribe* link.
#. Send it.
#. If somebody gets unsubscribed, you will see logs about that under
*Mass Mailing > Mailings > Unsubscriptions*.
.. 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/10.0
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, 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
needed changes), this addon will need a refactoring (mostly removing
duplicated functionality and depending on it instead of replacing it). In the
mean time, there is a little chance that this introduces some
incompatibilities with other addons that depend on ``website_mass_mailing``.
Bug Tracker
===========
Bugs are tracked on `GitHub Issues
<https://github.com/OCA/social/issues>`_. In case of trouble, please
check there if your issue has already been reported. If you spotted it first,
help us smashing it by providing a detailed and welcomed feedback.
Credits
=======
Contributors
------------
* Rafael Blasco <rafael.blasco@tecnativa.com>
* Antonio Espinosa <antonio.espinosa@tecnativa.com>
* Jairo Llopis <jairo.llopis@tecnativa.com>
Maintainer
----------
.. image:: https://odoo-community.org/logo.png
:alt: Odoo Community Association
:target: https://odoo-community.org
This module is maintained by the OCA.
OCA, or the Odoo Community Association, is a nonprofit organization whose
mission is to support the collaborative development of Odoo features and
promote its widespread use.
To contribute to this module, please visit https://odoo-community.org.

View File

@ -1,5 +1,4 @@
# -*- coding: utf-8 -*-
# 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 . import controllers, models from . import controllers, models
from .hooks import post_init_hook

View File

@ -1,11 +1,11 @@
# -*- coding: utf-8 -*-
# Copyright 2016 Jairo Llopis <jairo.llopis@tecnativa.com> # Copyright 2016 Jairo Llopis <jairo.llopis@tecnativa.com>
# Copyright 2018 David Vidal <david.vidal@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).
{ {
'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': '10.0.2.0.0', 'version': '11.0.1.0.0',
'depends': [ 'depends': [
'website_mass_mailing', 'website_mass_mailing',
], ],
@ -17,9 +17,10 @@
'views/assets.xml', 'views/assets.xml',
'views/mail_unsubscription_reason_view.xml', 'views/mail_unsubscription_reason_view.xml',
'views/mail_mass_mailing_list_view.xml', 'views/mail_mass_mailing_list_view.xml',
'views/mail_mass_mailing_contact_view.xml',
'views/mail_unsubscription_view.xml', 'views/mail_unsubscription_view.xml',
], ],
"demo": [ 'demo': [
'demo/assets.xml', 'demo/assets.xml',
], ],
'images': [ 'images': [
@ -27,7 +28,8 @@
], ],
'author': 'Tecnativa,' 'author': 'Tecnativa,'
'Odoo Community Association (OCA)', 'Odoo Community Association (OCA)',
'website': 'https://www.tecnativa.com', 'website': 'https://github.com/OCA/social',
'license': 'AGPL-3', 'license': 'AGPL-3',
'installable': True, 'installable': True,
'post_init_hook': 'post_init_hook',
} }

View File

@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
# 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).

View File

@ -1,12 +1,11 @@
# -*- coding: utf-8 -*-
# Copyright 2015 Antiun Ingeniería S.L. (http://www.antiun.com) # Copyright 2015 Antiun Ingeniería S.L. (http://www.antiun.com)
# 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.html). # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
import logging import logging
from openerp.http import request, route from odoo.http import request, route
from openerp.addons.website_mass_mailing.controllers.main \ from odoo.addons.website_mass_mailing.controllers.main \
import MassMailController import MassMailController
_logger = logging.getLogger(__name__) _logger = logging.getLogger(__name__)
@ -45,24 +44,18 @@ class CustomUnsubscribe(MassMailController):
_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))
if res_id:
res_id = int(res_id)
mailing = request.env["mail.mass_mailing"].sudo().browse(mailing_id) mailing = request.env["mail.mass_mailing"].sudo().browse(mailing_id)
mailing._unsubscribe_token(res_id, token)
# Mass mailing list contacts are a special case because they have a # Mass mailing list contacts are a special case because they have a
# subscription management form # subscription management form
if mailing.mailing_model == 'mail.mass_mailing.contact': if mailing.mailing_model_real == 'mail.mass_mailing.contact':
result = super(CustomUnsubscribe, self).mailing( result = super(CustomUnsubscribe, self).mailing(
mailing_id, email, res_id, **post) mailing_id, email, res_id, token=token, **post)
# FIXME Remove res_id and token in version where this is merged:
# https://github.com/odoo/odoo/pull/14385
result.qcontext.update({ result.qcontext.update({
"token": token,
"res_id": res_id,
"contacts": result.qcontext["contacts"].filtered( "contacts": result.qcontext["contacts"].filtered(
lambda contact: lambda contact:
not contact.list_id.not_cross_unsubscriptable or not any(contact.list_ids.mapped(
contact.list_id <= mailing.contact_list_ids 'not_cross_unsubscriptable')) or
contact.list_ids <= mailing.contact_list_ids
), ),
"reasons": "reasons":
request.env["mail.unsubscription.reason"].search([]), request.env["mail.unsubscription.reason"].search([]),
@ -85,7 +78,7 @@ class CustomUnsubscribe(MassMailController):
# 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(
mailing_id, email, res_id, **post) mailing_id, email, res_id, token=token, **post)
@route() @route()
def unsubscribe(self, mailing_id, opt_in_ids, opt_out_ids, email, res_id, def unsubscribe(self, mailing_id, opt_in_ids, opt_out_ids, email, res_id,
@ -107,10 +100,6 @@ class CustomUnsubscribe(MassMailController):
if details: if details:
extra_context["default_details"] = details extra_context["default_details"] = details
request.context = dict(request.context, **extra_context) request.context = dict(request.context, **extra_context)
# 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)
mailing._unsubscribe_token(res_id, token)
_logger.debug( _logger.debug(
"Called `unsubscribe()` with: %r", "Called `unsubscribe()` with: %r",
(mailing_id, opt_in_ids, opt_out_ids, email, res_id, token, (mailing_id, opt_in_ids, opt_out_ids, email, res_id, token,

View File

@ -1,8 +1,7 @@
# -*- coding: utf-8 -*-
# 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 exceptions from odoo import exceptions
class DetailsRequiredError(exceptions.ValidationError): class DetailsRequiredError(exceptions.ValidationError):

View File

@ -0,0 +1,17 @@
# Copyright 2018 David Vidal <david.vidal@tecnativa.com>
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
from odoo import api, SUPERUSER_ID
def post_init_hook(cr, registry):
"""Ensure all existing contacts are going to work as v10"""
env = api.Environment(cr, SUPERUSER_ID, {})
contacts = env['mail.mass_mailing.contact'].search([])
for contact in contacts:
if len(contact.list_ids) <= 1:
continue
list_1 = contact.list_ids[0]
for list_ in contact.list_ids - list_1:
contact.copy({"list_ids": [(6, 0, list_.ids)]})
contact.list_ids = list_1

View File

@ -1,7 +1,6 @@
# -*- coding: utf-8 -*-
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
from . import mail_mail
from . import mail_mass_mailing from . import mail_mass_mailing
from . import mail_mass_mailing_contact
from . import mail_mass_mailing_list from . import mail_mass_mailing_list
from . import mail_unsubscription from . import mail_unsubscription

View File

@ -1,14 +0,0 @@
# -*- coding: utf-8 -*-
# Copyright 2016 Jairo Llopis <jairo.llopis@tecnativa.com>
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
from openerp import models
class MailMail(models.Model):
_inherit = 'mail.mail'
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)

View File

@ -1,52 +1,27 @@
# -*- coding: utf-8 -*-
# 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 hmac from odoo import models
import hashlib
from openerp import api, models
from openerp.exceptions import AccessDenied
from openerp.tools import consteq
class MailMassMailing(models.Model): class MailMassMailing(models.Model):
_inherit = "mail.mass_mailing" _inherit = "mail.mass_mailing"
@api.multi
def _unsubscribe_token(self, res_id, compare=None):
"""Generate a secure hash for this mailing list and parameters.
This is appended to the unsubscription URL and then checked at
unsubscription time to ensure no malicious unsubscriptions are
performed.
:param int res_id:
ID of the resource that will be unsubscribed.
:param str compare:
Received token to be compared with the good one.
:raise AccessDenied:
Will happen if you provide :param:`compare` and it does not match
the good token.
"""
secret = self.env["ir.config_parameter"].sudo().get_param(
"database.secret")
key = (self.env.cr.dbname, self.id, int(res_id))
token = hmac.new(str(secret), repr(key), hashlib.sha512).hexdigest()
if compare is not None and not consteq(token, str(compare)):
raise AccessDenied()
return token
def update_opt_out(self, email, res_ids, value): def update_opt_out(self, email, res_ids, value):
"""Save unsubscription reason when opting out from mailing.""" """Save unsubscription reason when opting out from mailing."""
self.ensure_one() self.ensure_one()
model = self.env[self.mailing_model_real].with_context(
active_test=False)
action = "unsubscription" if value else "subscription" action = "unsubscription" if value else "subscription"
records = self.env[self.mailing_model].browse(res_ids) records = self.env[model._name].browse(res_ids)
previous = self.env["mail.unsubscription"].search(limit=1, args=[ previous = self.env["mail.unsubscription"].search(limit=1, args=[
("mass_mailing_id", "=", self.id), ("mass_mailing_id", "=", self.id),
("email", "=", email), ("email", "=", email),
("action", "=", action), ("action", "=", action),
]) ])
if 'opt_out' not in model._fields:
return super(MailMassMailing, self).update_opt_out(
email, res_ids, value)
for one in records: for one in records:
# Store action only when something changed, or there was no # Store action only when something changed, or there was no
# previous subscription record # previous subscription record
@ -59,5 +34,7 @@ class MailMassMailing(models.Model):
"unsubscriber_id": "%s,%d" % (one._name, one.id), "unsubscriber_id": "%s,%d" % (one._name, one.id),
"action": action, "action": action,
}) })
if model._name == 'mail.mass_mailing.contact':
pass
return super(MailMassMailing, self).update_opt_out( return super(MailMassMailing, self).update_opt_out(
email, res_ids, value) email, res_ids, value)

View File

@ -0,0 +1,30 @@
# Copyright 2018 David Vidal <david.vidal@tecnativa.com>
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
from odoo import api, fields, models
class MailMassMailing(models.Model):
_inherit = "mail.mass_mailing.contact"
# Recover the old Many2one field so we can set a contact by list
mailing_list_id = fields.Many2one(
'mail.mass_mailing.list',
string='Mailing List',
ondelete='cascade',
compute="_compute_mailing_list_id",
inverse="_inverse_mailing_list_id",
search="_search_mailing_list_id",
)
@api.depends('list_ids')
def _compute_mailing_list_id(self):
for contact in self:
contact.mailing_list_id = contact.list_ids[:1]
def _inverse_mailing_list_id(self):
for contact in self:
contact.list_ids = contact.mailing_list_id
def _search_mailing_list_id(self, operator, value):
return [('list_ids', operator, value)]

View File

@ -1,8 +1,7 @@
# -*- coding: utf-8 -*-
# Copyright 2016 Pedro M. Baeza <pedro.baeza@tecnativa.com> # Copyright 2016 Pedro M. Baeza <pedro.baeza@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 fields, models from odoo import fields, models
class MailMassMailing(models.Model): class MailMassMailing(models.Model):

View File

@ -1,8 +1,9 @@
# -*- coding: utf-8 -*-
# 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 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 .. import exceptions from .. import exceptions
@ -35,13 +36,14 @@ class MailUnsubscription(models.Model):
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_id = fields.Many2one( mailing_list_id = fields.Many2many(
"mail.mass_mailing.list", comodel_name="mail.mass_mailing.list",
"Mailing list", string="Mailing list",
ondelete="set null", ondelete="set null",
compute="_compute_mailing_list_id", compute="_compute_mailing_list_id",
store=True, store=True,
help="(Un)subscribed mass mailing list, if any.", help="(Un)subscribed mass mailing list, if any.",
readonly=False,
) )
reason_id = fields.Many2one( reason_id = fields.Many2one(
"mail.unsubscription.reason", "mail.unsubscription.reason",
@ -57,6 +59,15 @@ class MailUnsubscription(models.Model):
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):
model_mapped = []
for model in models:
if model == 'mail.mass_mailing.list':
model_mapped.append(('mail.mass_mailing.contact', model))
else:
model_mapped.append((model, model))
return model_mapped
@api.model @api.model
def _default_date(self): def _default_date(self):
return fields.Datetime.now() return fields.Datetime.now()
@ -64,7 +75,9 @@ 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``."""
return self.env["mail.mass_mailing"]._get_mailing_model() model = self.env['ir.model'].search(
[('model', 'in', MASS_MAILING_BUSINESS_MODELS)]).mapped('model')
return self.map_mailing_list_models(model)
@api.multi @api.multi
@api.constrains("action", "reason_id") @api.constrains("action", "reason_id")
@ -90,7 +103,7 @@ class MailUnsubscription(models.Model):
"""Get the mass mailing list, if it is possible.""" """Get the mass mailing list, if it is possible."""
for one in self: for one in self:
try: try:
one.mailing_list_id = one.unsubscriber_id.list_id one.mailing_list_id |= one.unsubscriber_id.mailing_list_id
except AttributeError: except AttributeError:
# Possibly model != mail.mass_mailing.contact; no problem # Possibly model != mail.mass_mailing.contact; no problem
pass pass

View File

@ -0,0 +1,10 @@
Unsubscription Reasons
----------------------
You can customize what reasons will be displayed to your unsubscriptors when
they are going to unsubscribe. To do it:
#. 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.

View File

@ -0,0 +1,4 @@
* Rafael Blasco <rafael.blasco@tecnativa.com>
* Antonio Espinosa <antonio.espinosa@tecnativa.com>
* Jairo Llopis <jairo.llopis@tecnativa.com>
* David Vidal <david.vidal@tecnativa.com>

View File

@ -0,0 +1,8 @@
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 has been subscribed or unsubscribed from a
mass mailing.
- Provide proof on why you are sending mass mailings to a given contact, as
required by the GDPR in Europe.

View File

@ -0,0 +1,11 @@
* As version 11 has introduced a new relation type between mailing lists and
contacts that has multiple usability issues that are being reworked by Odoo
to land in version 12, this module falls back to the version 10 behaviour in
which one contact belonged to just one list.
* 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
needed changes), this addon will need a refactoring (mostly removing
duplicated functionality and depending on it instead of replacing it). In the
mean time, there is a little chance that this introduces some
incompatibilities with other addons that depend on ``website_mass_mailing``.

View File

@ -0,0 +1,8 @@
Once configured:
#. Go to *Mass Mailing > Mailings > Mass Mailings > Create*.
#. Edit your mass mailing at wish, but remember to add a snippet from
*Footers*, so people have an *Unsubscribe* link.
#. Send it.
#. If somebody gets unsubscribed, you will see logs about that under
*Mass Mailing > Mailings > Unsubscriptions*.

View File

@ -39,7 +39,7 @@ odoo.define("mass_mailing_custom_unsubscribe.partner_tour",
}, },
{ {
content: "Successfully unsubscribed", content: "Successfully unsubscribed",
trigger: "body:not(:has(#reason_form)) .alert-success:contains('Your changes have been saved.')", trigger: "body:not(:has(#reason_form)) .alert-success:contains('You have been successfully unsubscribed!')",
}, },
] ]
); );

View File

@ -3,7 +3,7 @@
odoo.define("mass_mailing_custom_unsubscribe.require_details", odoo.define("mass_mailing_custom_unsubscribe.require_details",
function (require) { function (require) {
"use strict"; "use strict";
var animation = require("web_editor.snippets.animation"); var animation = require("website.content.snippets.animation");
animation.registry.mass_mailing_custom_unsubscribe_require_details = animation.registry.mass_mailing_custom_unsubscribe_require_details =
animation.Class.extend({ animation.Class.extend({

View File

@ -9,7 +9,7 @@ odoo.define("mass_mailing_custom_unsubscribe.unsubscribe", function (require) {
"use strict"; "use strict";
var core = require("web.core"); var core = require("web.core");
var ajax = require("web.ajax"); var ajax = require("web.ajax");
var animation = require("web_editor.snippets.animation"); var animation = require("website.content.snippets.animation");
var _t = core._t; var _t = core._t;
animation.registry.mass_mailing_unsubscribe = animation.registry.mass_mailing_unsubscribe =
@ -24,7 +24,7 @@ odoo.define("mass_mailing_custom_unsubscribe.unsubscribe", function (require) {
this.$token = this.$("input[name='token']"); this.$token = this.$("input[name='token']");
this.$res_id = this.$("input[name='res_id']"); this.$res_id = this.$("input[name='res_id']");
this.$reasons = this.$(".js_unsubscription_reason"); this.$reasons = this.$(".js_unsubscription_reason");
this.$details = this.$reasons.find("[name='details']") this.$details = this.$reasons.find("[name='details']");
this.$el.on("submit", $.proxy(this.submit, this)); this.$el.on("submit", $.proxy(this.submit, this));
this.$contacts.on("change", $.proxy(this.toggle_reasons, this)); this.$contacts.on("change", $.proxy(this.toggle_reasons, this));
this.toggle_reasons(); this.toggle_reasons();

View File

@ -10,7 +10,7 @@
<!-- Disable core AJAX submission of form, because it is impossible to <!-- Disable core AJAX submission of form, because it is impossible to
extend it as it is designed right now. It is refactored in this addon. extend it as it is designed right now. It is refactored in this addon.
TODO Remove when merged https://github.com/odoo/odoo/pull/14386. --> TODO Remove when merged https://github.com/odoo/odoo/pull/14386. -->
<xpath expr="//div[@class='container o_unsubscribe_form']" <xpath expr="//div[hasclass('container', 'o_unsubscribe_form')]"
position="attributes"> position="attributes">
<attribute name="class" value="container o_unsubscribe_form_custom"/> <attribute name="class" value="container o_unsubscribe_form_custom"/>
</xpath> </xpath>

View File

@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
# 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).

View File

@ -1,9 +1,8 @@
# -*- coding: utf-8 -*-
# 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 contextlib import contextmanager from contextlib import contextmanager
from openerp.tests.common import HttpCase from odoo.tests.common import HttpCase
class UICase(HttpCase): class UICase(HttpCase):
@ -37,7 +36,7 @@ class UICase(HttpCase):
}) })
self.mailings += Mailing.create({ self.mailings += Mailing.create({
"name": "test mailing %d" % n, "name": "test mailing %d" % n,
"mailing_model": "mail.mass_mailing.contact", "mailing_model_id": self.env["mail.mass_mailing.contact"],
"contact_list_ids": [(6, 0, self.lists.ids)], "contact_list_ids": [(6, 0, self.lists.ids)],
"reply_to_mode": "thread", "reply_to_mode": "thread",
}) })
@ -53,7 +52,7 @@ class UICase(HttpCase):
self.contacts += Contact.create({ self.contacts += Contact.create({
"name": "test contact %d" % n, "name": "test contact %d" % n,
"email": self.email, "email": self.email,
"list_id": self.lists[n].id, "mailing_list_id": self.lists[n].id,
}) })
def tearDown(self): def tearDown(self):
@ -117,7 +116,8 @@ class UICase(HttpCase):
# Change mailing to be sent to partner # Change mailing to be sent to partner
partner_id = env["res.partner"].name_create( partner_id = env["res.partner"].name_create(
"Demo Partner <%s>" % self.email)[0] "Demo Partner <%s>" % self.email)[0]
self.mailings[0].mailing_model = "res.partner" self.mailings[0].mailing_model_id = self.env.ref(
"base.model_res_partner")
self.mailings[0].mailing_domain = repr([ self.mailings[0].mailing_domain = repr([
('opt_out', '=', False), ('opt_out', '=', False),
('id', '=', partner_id), ('id', '=', partner_id),

View File

@ -1,8 +1,7 @@
# -*- coding: utf-8 -*-
# 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.tests.common import SavepointCase from odoo.tests.common import SavepointCase
from .. import exceptions from .. import exceptions

View File

@ -0,0 +1,30 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright 2018 David Vidal <david.vidal@tecnativa.com>
License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). -->
<odoo>
<record id="view_mail_mass_mailing_contact_form" model="ir.ui.view">
<field name="model">mail.mass_mailing.contact</field>
<field name="inherit_id" ref="mass_mailing.view_mail_mass_mailing_contact_form"/>
<field name="arch" type="xml">
<field name="list_ids" position="attributes">
<attribute name="invisible">1</attribute>
</field>
<field name="email" position="after">
<field name="mailing_list_id"/>
</field>
</field>
</record>
<record id="view_mail_mass_mailing_contact_tree" model="ir.ui.view">
<field name="model">mail.mass_mailing.contact</field>
<field name="inherit_id" ref="mass_mailing.view_mail_mass_mailing_contact_tree"/>
<field name="arch" type="xml">
<field name="email" position="before">
<field name="mailing_list_id"/>
</field>
</field>
</record>
</odoo>

View File

@ -14,7 +14,7 @@
<field name="date"/> <field name="date"/>
<field name="mass_mailing_id"/> <field name="mass_mailing_id"/>
<field name="unsubscriber_id"/> <field name="unsubscriber_id"/>
<field name="mailing_list_id"/> <field name="mailing_list_id" widget="many2many_tags"/>
<field name="email"/> <field name="email"/>
<field name="action"/> <field name="action"/>
<field name="reason_id" <field name="reason_id"
@ -44,7 +44,7 @@
<field name="date"/> <field name="date"/>
<field name="mass_mailing_id"/> <field name="mass_mailing_id"/>
<field name="unsubscriber_id"/> <field name="unsubscriber_id"/>
<field name="mailing_list_id"/> <field name="mailing_list_id" widget="many2many_tags"/>
<field name="email" invisible="True"/> <field name="email" invisible="True"/>
<field name="action"/> <field name="action"/>
<field name="reason_id"/> <field name="reason_id"/>