diff --git a/addons/mass_mailing/__init__.py b/addons/mass_mailing/__init__.py index 88710707227..4e9b019bd7a 100644 --- a/addons/mass_mailing/__init__.py +++ b/addons/mass_mailing/__init__.py @@ -3,4 +3,5 @@ from . import controllers from . import models +from . import report from . import wizard diff --git a/addons/mass_mailing/__manifest__.py b/addons/mass_mailing/__manifest__.py index 1e1986cc211..fd1e0c8b631 100644 --- a/addons/mass_mailing/__manifest__.py +++ b/addons/mass_mailing/__manifest__.py @@ -20,26 +20,25 @@ ], 'data': [ 'security/mass_mailing_security.xml', + 'security/ir.model.access.csv', + 'data/mail_data.xml', 'data/mass_mailing_data.xml', 'wizard/mail_compose_message_views.xml', 'wizard/mass_mailing_list_merge_views.xml', 'wizard/test_mailing_views.xml', 'wizard/mass_mailing_schedule_date_views.xml', 'views/mass_mailing_views_menus.xml', - 'views/mass_mailing_report_views.xml', 'views/mass_mailing_stats_views.xml', 'views/link_tracker_views.xml', - 'views/mass_mailing_template.xml', + 'views/mass_mailing_contact_views.xml', + 'views/mass_mailing_list_views.xml', 'views/mass_mailing_views.xml', 'views/res_config_settings_views.xml', - 'security/ir.model.access.csv', - 'views/mass_mailing_layout.xml', - 'views/editor_field_html.xml', + 'report/mass_mailing_stats_report_views.xml', + 'views/assets.xml', + 'views/mass_mailing_templates_portal.xml', 'views/themes_templates.xml', 'views/snippets_themes.xml', - 'views/snippets_themes_options.xml', - 'views/mass_mailing_blacklist_views.xml', - 'views/unsubscribe_templates.xml', ], 'demo': [ 'data/mass_mailing_demo.xml', diff --git a/addons/mass_mailing/views/mass_mailing_layout.xml b/addons/mass_mailing/data/mail_data.xml similarity index 61% rename from addons/mass_mailing/views/mass_mailing_layout.xml rename to addons/mass_mailing/data/mail_data.xml index eae060d21ea..6c9bd7b591d 100644 --- a/addons/mass_mailing/views/mass_mailing_layout.xml +++ b/addons/mass_mailing/data/mail_data.xml @@ -1,22 +1,5 @@ - - - + - + diff --git a/addons/mass_mailing/models/__init__.py b/addons/mass_mailing/models/__init__.py index c542abccfba..afeb0313925 100644 --- a/addons/mass_mailing/models/__init__.py +++ b/addons/mass_mailing/models/__init__.py @@ -1,11 +1,12 @@ # -*- coding: utf-8 -*- # Part of Odoo. See LICENSE file for full copyright and licensing details. +from . import link_tracker +from . import mass_mailing_contact +from . import mass_mailing_list from . import mass_mailing from . import mass_mailing_stats from . import mail_mail from . import mail_template from . import mail_thread from . import res_config_settings -from . import mass_mailing_report -from . import link_tracker diff --git a/addons/mass_mailing/models/mass_mailing.py b/addons/mass_mailing/models/mass_mailing.py index f4f598cf3c9..2a45147ec0d 100644 --- a/addons/mass_mailing/models/mass_mailing.py +++ b/addons/mass_mailing/models/mass_mailing.py @@ -43,251 +43,9 @@ class MassMailingTag(models.Model): color = fields.Integer(string='Color Index') _sql_constraints = [ - ('name_uniq', 'unique (name)', "Tag name already exists !"), + ('name_uniq', 'unique (name)', "Tag name already exists !"), ] -class MassMailingContactListRel(models.Model): - """ Intermediate model between mass mailing list and mass mailing contact - Indicates if a contact is opted out for a particular list - """ - _name = 'mail.mass_mailing.list_contact_rel' - _description = 'Mass Mailing Subscription Information' - _table = 'mail_mass_mailing_contact_list_rel' - _rec_name = 'contact_id' - - contact_id = fields.Many2one('mail.mass_mailing.contact', string='Contact', ondelete='cascade', required=True) - list_id = fields.Many2one('mail.mass_mailing.list', string='Mailing List', ondelete='cascade', required=True) - opt_out = fields.Boolean(string='Opt Out', - help='The contact has chosen not to receive mails anymore from this list', default=False) - unsubscription_date = fields.Datetime(string='Unsubscription Date') - contact_count = fields.Integer(related='list_id.contact_nbr', store=False, readonly=False) - message_bounce = fields.Integer(related='contact_id.message_bounce', store=False, readonly=False) - is_blacklisted = fields.Boolean(related='contact_id.is_blacklisted', store=False, readonly=False) - - _sql_constraints = [ - ('unique_contact_list', 'unique (contact_id, list_id)', - 'A contact cannot be subscribed multiple times to the same list!') - ] - - @api.model - def create(self, vals): - if 'opt_out' in vals: - vals['unsubscription_date'] = vals['opt_out'] and fields.Datetime.now() - return super(MassMailingContactListRel, self).create(vals) - - def write(self, vals): - if 'opt_out' in vals: - vals['unsubscription_date'] = vals['opt_out'] and fields.Datetime.now() - return super(MassMailingContactListRel, self).write(vals) - - def action_open_mailing_list_contact(self): - """TODO DBE : To remove - Deprecated""" - contact_id = self.contact_id - action = { - 'name': _(contact_id.name), - 'type': 'ir.actions.act_window', - 'res_model': 'mail.mass_mailing.contact', - 'view_mode': 'form', - 'target': 'current', - 'res_id': contact_id.id - } - return action - - -class MassMailingList(models.Model): - """Model of a contact list. """ - _name = 'mail.mass_mailing.list' - _order = 'name' - _description = 'Mailing List' - - name = fields.Char(string='Mailing List', required=True) - active = fields.Boolean(default=True) - contact_nbr = fields.Integer(compute="_compute_contact_nbr", string='Number of Contacts') - contact_ids = fields.Many2many( - 'mail.mass_mailing.contact', 'mail_mass_mailing_contact_list_rel', 'list_id', 'contact_id', - string='Mailing Lists') - subscription_contact_ids = fields.One2many('mail.mass_mailing.list_contact_rel', 'list_id', - string='Subscription Information') - is_public = fields.Boolean(default=True, help="The mailing list can be accessible by recipient in the unsubscription" - " page to allows him to update his subscription preferences.") - - # Compute number of contacts non opt-out, non blacklisted and valid email recipient for a mailing list - def _compute_contact_nbr(self): - self.env.cr.execute(''' - select - list_id, count(*) - from - mail_mass_mailing_contact_list_rel r - left join mail_mass_mailing_contact c on (r.contact_id=c.id) - left join mail_blacklist bl on c.email_normalized = bl.email and bl.active - where - list_id in %s - AND COALESCE(r.opt_out,FALSE) = FALSE - AND c.email_normalized IS NOT NULL - AND bl.id IS NULL - group by - list_id - ''', (tuple(self.ids), )) - data = dict(self.env.cr.fetchall()) - for mailing_list in self: - mailing_list.contact_nbr = data.get(mailing_list.id, 0) - - def name_get(self): - return [(list.id, "%s (%s)" % (list.name, list.contact_nbr)) for list in self] - - def action_merge(self, src_lists, archive): - """ - Insert all the contact from the mailing lists 'src_lists' to the - mailing list in 'self'. Possibility to archive the mailing lists - 'src_lists' after the merge except the destination mailing list 'self'. - """ - # Explation of the SQL query with an example. There are the following lists - # A (id=4): yti@odoo.com; yti@example.com - # B (id=5): yti@odoo.com; yti@openerp.com - # C (id=6): nothing - # To merge the mailing lists A and B into C, we build the view st that looks - # like this with our example: - # - # contact_id | email | row_number | list_id | - # ------------+---------------------------+------------------------ - # 4 | yti@odoo.com | 1 | 4 | - # 6 | yti@odoo.com | 2 | 5 | - # 5 | yti@example.com | 1 | 4 | - # 7 | yti@openerp.com | 1 | 5 | - # - # The row_column is kind of an occurence counter for the email address. - # Then we create the Many2many relation between the destination list and the contacts - # while avoiding to insert an existing email address (if the destination is in the source - # for example) - self.ensure_one() - # Put destination is sources lists if not already the case - src_lists |= self - self.env.cr.execute(""" - INSERT INTO mail_mass_mailing_contact_list_rel (contact_id, list_id) - SELECT st.contact_id AS contact_id, %s AS list_id - FROM - ( - SELECT - contact.id AS contact_id, - contact.email AS email, - mailing_list.id AS list_id, - row_number() OVER (PARTITION BY email ORDER BY email) AS rn - FROM - mail_mass_mailing_contact contact, - mail_mass_mailing_contact_list_rel contact_list_rel, - mail_mass_mailing_list mailing_list - WHERE contact.id=contact_list_rel.contact_id - AND COALESCE(contact_list_rel.opt_out,FALSE) = FALSE - AND contact.email_normalized NOT IN (select email from mail_blacklist where active = TRUE) - AND mailing_list.id=contact_list_rel.list_id - AND mailing_list.id IN %s - AND NOT EXISTS - ( - SELECT 1 - FROM - mail_mass_mailing_contact contact2, - mail_mass_mailing_contact_list_rel contact_list_rel2 - WHERE contact2.email = contact.email - AND contact_list_rel2.contact_id = contact2.id - AND contact_list_rel2.list_id = %s - ) - ) st - WHERE st.rn = 1;""", (self.id, tuple(src_lists.ids), self.id)) - self.invalidate_cache() - if archive: - (src_lists - self).write({'active': False}) - - def close_dialog(self): - return {'type': 'ir.actions.act_window_close'} - - -class MassMailingContact(models.Model): - """Model of a contact. This model is different from the partner model - because it holds only some basic information: name, email. The purpose is to - be able to deal with large contact list to email without bloating the partner - base.""" - _name = 'mail.mass_mailing.contact' - _inherit = ['mail.thread.blacklist'] - _description = 'Mass Mailing Contact' - _order = 'email' - _rec_name = 'email' - - name = fields.Char() - company_name = fields.Char(string='Company Name') - title_id = fields.Many2one('res.partner.title', string='Title') - email = fields.Char(required=True) - is_email_valid = fields.Boolean(compute='_compute_is_email_valid', store=True) - list_ids = fields.Many2many( - 'mail.mass_mailing.list', 'mail_mass_mailing_contact_list_rel', - 'contact_id', 'list_id', string='Mailing Lists') - subscription_list_ids = fields.One2many('mail.mass_mailing.list_contact_rel', - 'contact_id', string='Subscription Information') - country_id = fields.Many2one('res.country', string='Country') - tag_ids = fields.Many2many('res.partner.category', string='Tags') - opt_out = fields.Boolean('Opt Out', compute='_compute_opt_out', search='_search_opt_out', - help='Opt out flag for a specific mailing list.' - 'This field should not be used in a view without a unique and active mailing list context.') - - @api.depends('email') - def _compute_is_email_valid(self): - for record in self: - normalized = tools.email_normalize(record.email) - record.is_email_valid = normalized if not normalized else True - - @api.model - def _search_opt_out(self, operator, value): - # Assumes operator is '=' or '!=' and value is True or False - if operator != '=': - if operator == '!=' and isinstance(value, bool): - value = not value - else: - raise NotImplementedError() - - if 'default_list_ids' in self._context and isinstance(self._context['default_list_ids'], (list, tuple)) and len(self._context['default_list_ids']) == 1: - [active_list_id] = self._context['default_list_ids'] - contacts = self.env['mail.mass_mailing.list_contact_rel'].search([('list_id', '=', active_list_id)]) - return [('id', 'in', [record.contact_id.id for record in contacts if record.opt_out == value])] - else: - raise UserError('Search opt out cannot be executed without a unique and valid active mailing list context.') - - @api.depends('subscription_list_ids') - def _compute_opt_out(self): - if 'default_list_ids' in self._context and isinstance(self._context['default_list_ids'], (list, tuple)) and len(self._context['default_list_ids']) == 1: - [active_list_id] = self._context['default_list_ids'] - for record in self: - active_subscription_list = record.subscription_list_ids.filtered(lambda l: l.list_id.id == active_list_id) - record.opt_out = active_subscription_list.opt_out - else: - for record in self: - record.opt_out = False - - def get_name_email(self, name): - name, email = self.env['res.partner']._parse_partner_name(name) - if name and not email: - email = name - if email and not name: - name = email - return name, email - - @api.model - def name_create(self, name): - name, email = self.get_name_email(name) - contact = self.create({'name': name, 'email': email}) - return contact.name_get()[0] - - @api.model - def add_to_list(self, name, list_id): - name, email = self.get_name_email(name) - contact = self.create({'name': name, 'email': email, 'list_ids': [(4, list_id)]}) - return contact.name_get()[0] - - def _message_get_default_recipients(self): - return {r.id: { - 'partner_ids': [], - 'email_to': r.email_normalized, - 'email_cc': False} - for r in self} - class MassMailingStage(models.Model): diff --git a/addons/mass_mailing/models/mass_mailing_contact.py b/addons/mass_mailing/models/mass_mailing_contact.py new file mode 100644 index 00000000000..817c30830ac --- /dev/null +++ b/addons/mass_mailing/models/mass_mailing_contact.py @@ -0,0 +1,142 @@ +# -*- coding: utf-8 -*- +# Part of Odoo. See LICENSE file for full copyright and licensing details. + +from odoo import api, fields, models, tools, _ +from odoo.exceptions import UserError + + +class MassMailingContactListRel(models.Model): + """ Intermediate model between mass mailing list and mass mailing contact + Indicates if a contact is opted out for a particular list + """ + _name = 'mail.mass_mailing.list_contact_rel' + _description = 'Mass Mailing Subscription Information' + _table = 'mail_mass_mailing_contact_list_rel' + _rec_name = 'contact_id' + + contact_id = fields.Many2one('mail.mass_mailing.contact', string='Contact', ondelete='cascade', required=True) + list_id = fields.Many2one('mail.mass_mailing.list', string='Mailing List', ondelete='cascade', required=True) + opt_out = fields.Boolean(string='Opt Out', + help='The contact has chosen not to receive mails anymore from this list', default=False) + unsubscription_date = fields.Datetime(string='Unsubscription Date') + contact_count = fields.Integer(related='list_id.contact_nbr', store=False, readonly=False) + message_bounce = fields.Integer(related='contact_id.message_bounce', store=False, readonly=False) + is_blacklisted = fields.Boolean(related='contact_id.is_blacklisted', store=False, readonly=False) + + _sql_constraints = [ + ('unique_contact_list', 'unique (contact_id, list_id)', + 'A contact cannot be subscribed multiple times to the same list!') + ] + + @api.model + def create(self, vals): + if 'opt_out' in vals: + vals['unsubscription_date'] = vals['opt_out'] and fields.Datetime.now() + return super(MassMailingContactListRel, self).create(vals) + + def write(self, vals): + if 'opt_out' in vals: + vals['unsubscription_date'] = vals['opt_out'] and fields.Datetime.now() + return super(MassMailingContactListRel, self).write(vals) + + def action_open_mailing_list_contact(self): + """TODO DBE : To remove - Deprecated""" + contact_id = self.contact_id + action = { + 'name': _(contact_id.name), + 'type': 'ir.actions.act_window', + 'res_model': 'mail.mass_mailing.contact', + 'view_mode': 'form', + 'target': 'current', + 'res_id': contact_id.id + } + return action + + +class MassMailingContact(models.Model): + """Model of a contact. This model is different from the partner model + because it holds only some basic information: name, email. The purpose is to + be able to deal with large contact list to email without bloating the partner + base.""" + _name = 'mail.mass_mailing.contact' + _inherit = ['mail.thread.blacklist'] + _description = 'Mass Mailing Contact' + _order = 'email' + _rec_name = 'email' + + name = fields.Char() + company_name = fields.Char(string='Company Name') + title_id = fields.Many2one('res.partner.title', string='Title') + email = fields.Char(required=True) + is_email_valid = fields.Boolean(compute='_compute_is_email_valid', store=True) + list_ids = fields.Many2many( + 'mail.mass_mailing.list', 'mail_mass_mailing_contact_list_rel', + 'contact_id', 'list_id', string='Mailing Lists') + subscription_list_ids = fields.One2many('mail.mass_mailing.list_contact_rel', + 'contact_id', string='Subscription Information') + country_id = fields.Many2one('res.country', string='Country') + tag_ids = fields.Many2many('res.partner.category', string='Tags') + opt_out = fields.Boolean('Opt Out', compute='_compute_opt_out', search='_search_opt_out', + help='Opt out flag for a specific mailing list.' + 'This field should not be used in a view without a unique and active mailing list context.') + + @api.depends('email') + def _compute_is_email_valid(self): + for record in self: + normalized = tools.email_normalize(record.email) + record.is_email_valid = normalized if not normalized else True + + @api.model + def _search_opt_out(self, operator, value): + # Assumes operator is '=' or '!=' and value is True or False + if operator != '=': + if operator == '!=' and isinstance(value, bool): + value = not value + else: + raise NotImplementedError() + + if 'default_list_ids' in self._context and isinstance(self._context['default_list_ids'], (list, tuple)) and len(self._context['default_list_ids']) == 1: + [active_list_id] = self._context['default_list_ids'] + contacts = self.env['mail.mass_mailing.list_contact_rel'].search([('list_id', '=', active_list_id)]) + return [('id', 'in', [record.contact_id.id for record in contacts if record.opt_out == value])] + else: + raise UserError('Search opt out cannot be executed without a unique and valid active mailing list context.') + + @api.depends('subscription_list_ids') + def _compute_opt_out(self): + if 'default_list_ids' in self._context and isinstance(self._context['default_list_ids'], (list, tuple)) and len(self._context['default_list_ids']) == 1: + [active_list_id] = self._context['default_list_ids'] + for record in self: + active_subscription_list = record.subscription_list_ids.filtered(lambda l: l.list_id.id == active_list_id) + record.opt_out = active_subscription_list.opt_out + else: + for record in self: + record.opt_out = False + + def get_name_email(self, name): + name, email = self.env['res.partner']._parse_partner_name(name) + if name and not email: + email = name + if email and not name: + name = email + return name, email + + @api.model + def name_create(self, name): + name, email = self.get_name_email(name) + contact = self.create({'name': name, 'email': email}) + return contact.name_get()[0] + + @api.model + def add_to_list(self, name, list_id): + name, email = self.get_name_email(name) + contact = self.create({'name': name, 'email': email, 'list_ids': [(4, list_id)]}) + return contact.name_get()[0] + + def _message_get_default_recipients(self): + return {r.id: { + 'partner_ids': [], + 'email_to': r.email_normalized, + 'email_cc': False} + for r in self + } diff --git a/addons/mass_mailing/models/mass_mailing_list.py b/addons/mass_mailing/models/mass_mailing_list.py new file mode 100644 index 00000000000..48cde5784c3 --- /dev/null +++ b/addons/mass_mailing/models/mass_mailing_list.py @@ -0,0 +1,111 @@ +# -*- coding: utf-8 -*- +# Part of Odoo. See LICENSE file for full copyright and licensing details. + +from odoo import api, fields, models + + +class MassMailingList(models.Model): + """Model of a contact list. """ + _name = 'mail.mass_mailing.list' + _order = 'name' + _description = 'Mailing List' + + name = fields.Char(string='Mailing List', required=True) + active = fields.Boolean(default=True) + contact_nbr = fields.Integer(compute="_compute_contact_nbr", string='Number of Contacts') + contact_ids = fields.Many2many( + 'mail.mass_mailing.contact', 'mail_mass_mailing_contact_list_rel', 'list_id', 'contact_id', + string='Mailing Lists') + subscription_contact_ids = fields.One2many('mail.mass_mailing.list_contact_rel', 'list_id', + string='Subscription Information') + is_public = fields.Boolean(default=True, help="The mailing list can be accessible by recipient in the unsubscription" + " page to allows him to update his subscription preferences.") + + # Compute number of contacts non opt-out, non blacklisted and valid email recipient for a mailing list + def _compute_contact_nbr(self): + self.env.cr.execute(''' + select + list_id, count(*) + from + mail_mass_mailing_contact_list_rel r + left join mail_mass_mailing_contact c on (r.contact_id=c.id) + left join mail_blacklist bl on c.email_normalized = bl.email and bl.active + where + list_id in %s + AND COALESCE(r.opt_out,FALSE) = FALSE + AND c.email_normalized IS NOT NULL + AND bl.id IS NULL + group by + list_id + ''', (tuple(self.ids), )) + data = dict(self.env.cr.fetchall()) + for mailing_list in self: + mailing_list.contact_nbr = data.get(mailing_list.id, 0) + + def name_get(self): + return [(list.id, "%s (%s)" % (list.name, list.contact_nbr)) for list in self] + + def action_merge(self, src_lists, archive): + """ + Insert all the contact from the mailing lists 'src_lists' to the + mailing list in 'self'. Possibility to archive the mailing lists + 'src_lists' after the merge except the destination mailing list 'self'. + """ + # Explation of the SQL query with an example. There are the following lists + # A (id=4): yti@odoo.com; yti@example.com + # B (id=5): yti@odoo.com; yti@openerp.com + # C (id=6): nothing + # To merge the mailing lists A and B into C, we build the view st that looks + # like this with our example: + # + # contact_id | email | row_number | list_id | + # ------------+---------------------------+------------------------ + # 4 | yti@odoo.com | 1 | 4 | + # 6 | yti@odoo.com | 2 | 5 | + # 5 | yti@example.com | 1 | 4 | + # 7 | yti@openerp.com | 1 | 5 | + # + # The row_column is kind of an occurence counter for the email address. + # Then we create the Many2many relation between the destination list and the contacts + # while avoiding to insert an existing email address (if the destination is in the source + # for example) + self.ensure_one() + # Put destination is sources lists if not already the case + src_lists |= self + self.env.cr.execute(""" + INSERT INTO mail_mass_mailing_contact_list_rel (contact_id, list_id) + SELECT st.contact_id AS contact_id, %s AS list_id + FROM + ( + SELECT + contact.id AS contact_id, + contact.email AS email, + mailing_list.id AS list_id, + row_number() OVER (PARTITION BY email ORDER BY email) AS rn + FROM + mail_mass_mailing_contact contact, + mail_mass_mailing_contact_list_rel contact_list_rel, + mail_mass_mailing_list mailing_list + WHERE contact.id=contact_list_rel.contact_id + AND COALESCE(contact_list_rel.opt_out,FALSE) = FALSE + AND contact.email_normalized NOT IN (select email from mail_blacklist where active = TRUE) + AND mailing_list.id=contact_list_rel.list_id + AND mailing_list.id IN %s + AND NOT EXISTS + ( + SELECT 1 + FROM + mail_mass_mailing_contact contact2, + mail_mass_mailing_contact_list_rel contact_list_rel2 + WHERE contact2.email = contact.email + AND contact_list_rel2.contact_id = contact2.id + AND contact_list_rel2.list_id = %s + ) + ) st + WHERE st.rn = 1;""", (self.id, tuple(src_lists.ids), self.id)) + self.invalidate_cache() + if archive: + (src_lists - self).write({'active': False}) + + def close_dialog(self): + return {'type': 'ir.actions.act_window_close'} diff --git a/addons/mass_mailing/report/__init__.py b/addons/mass_mailing/report/__init__.py new file mode 100644 index 00000000000..2796202ba19 --- /dev/null +++ b/addons/mass_mailing/report/__init__.py @@ -0,0 +1,4 @@ +# -*- coding: utf-8 -*- +# Part of Odoo. See LICENSE file for full copyright and licensing details. + +from . import mass_mailing_stats_report diff --git a/addons/mass_mailing/models/mass_mailing_report.py b/addons/mass_mailing/report/mass_mailing_stats_report.py similarity index 100% rename from addons/mass_mailing/models/mass_mailing_report.py rename to addons/mass_mailing/report/mass_mailing_stats_report.py diff --git a/addons/mass_mailing/views/mass_mailing_report_views.xml b/addons/mass_mailing/report/mass_mailing_stats_report_views.xml similarity index 100% rename from addons/mass_mailing/views/mass_mailing_report_views.xml rename to addons/mass_mailing/report/mass_mailing_stats_report_views.xml diff --git a/addons/mass_mailing/views/assets.xml b/addons/mass_mailing/views/assets.xml new file mode 100644 index 00000000000..03b1a1415bc --- /dev/null +++ b/addons/mass_mailing/views/assets.xml @@ -0,0 +1,78 @@ + + + + + + + + + + + + + - diff --git a/addons/mass_mailing/views/unsubscribe_templates.xml b/addons/mass_mailing/views/mass_mailing_templates_portal.xml similarity index 100% rename from addons/mass_mailing/views/unsubscribe_templates.xml rename to addons/mass_mailing/views/mass_mailing_templates_portal.xml diff --git a/addons/mass_mailing/views/mass_mailing_views.xml b/addons/mass_mailing/views/mass_mailing_views.xml index 77a8c28c35a..7bbf205497f 100644 --- a/addons/mass_mailing/views/mass_mailing_views.xml +++ b/addons/mass_mailing/views/mass_mailing_views.xml @@ -1,435 +1,5 @@ - - - - mail.mass_mailing.list_contact_rel.view.form - mail.mass_mailing.list_contact_rel - 10 - -
- - - - - - -
-
-
- - - - mail.mass_mailing.list_contact_rel.list_contact.view.tree - mail.mass_mailing.list_contact_rel - - - - - - - - - -
-
- - - - - - -
-
- - - mail.mass_mailing.list.form.simplified - mail.mass_mailing.list - -
- - -
-
-
-
- - - -
-
-
-
-
- - - Create a Mass Mailing List - mail.mass_mailing.list - form - - new - - - - mail.mass_mailing.list.kanban - mail.mass_mailing.list - - - - - - - -
-
-
-
-
- -
-
-
- -
- -
-
-
-
-
-
- - - Mailing Lists - mail.mass_mailing.list - kanban,tree,form - -

- Create a new mailing list -

- You don't need to import your mailing lists, you can easily - send emails
to any contact saved in other Odoo apps. -

-
-
- - - mail.mass_mailing.search diff --git a/addons/mass_mailing/views/mass_mailing_views_menus.xml b/addons/mass_mailing/views/mass_mailing_views_menus.xml index 57832e6850a..4d83cae5370 100644 --- a/addons/mass_mailing/views/mass_mailing_views_menus.xml +++ b/addons/mass_mailing/views/mass_mailing_views_menus.xml @@ -4,9 +4,15 @@ + + + +
\ No newline at end of file diff --git a/addons/mass_mailing/views/snippets_themes.xml b/addons/mass_mailing/views/snippets_themes.xml index 7e908e12033..ac6c75ba128 100644 --- a/addons/mass_mailing/views/snippets_themes.xml +++ b/addons/mass_mailing/views/snippets_themes.xml @@ -769,4 +769,52 @@ + + + diff --git a/addons/mass_mailing/views/snippets_themes_options.xml b/addons/mass_mailing/views/snippets_themes_options.xml deleted file mode 100644 index 7700dd00d14..00000000000 --- a/addons/mass_mailing/views/snippets_themes_options.xml +++ /dev/null @@ -1,51 +0,0 @@ - - - - - -