[MOV] mass_mailing: reorganize module according to guidelines
PURPOSE Mass mailing is currently a bit messy. As SMS will be added as a way to notify people in mass through SMS let us take this opportunity to somehow clean this application: organization, light code cleaning, model renaming. SPECIFICATIONS Guidelines: re-organize model files and split them according to main models. Also reorganize views. Done in this commit * split big python file by main models; * split views according to python files; * move statistics report to /report; * merge some exploded view files (assets, snippets, application menus); LINKS Task ID 2037906 Preparing task ID 1997464 (SMS addition in mass mailing) PR #34938
This commit is contained in:
@@ -3,4 +3,5 @@
|
||||
|
||||
from . import controllers
|
||||
from . import models
|
||||
from . import report
|
||||
from . import wizard
|
||||
|
||||
@@ -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',
|
||||
|
||||
+2
-19
@@ -1,22 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo>
|
||||
<template id="mass_mailing_mail_style">
|
||||
<style>
|
||||
@media screen and (max-width: 768px) {
|
||||
.o_mail_col_mv {
|
||||
display: block !important;
|
||||
width: auto !important;
|
||||
}
|
||||
.o_mail_table_styles {
|
||||
width: 100% !important;
|
||||
}
|
||||
.o_mail_col_container {
|
||||
margin: 0px 0px 10px 0px !important;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</template>
|
||||
|
||||
<odoo><data noupdate="1">
|
||||
<!-- Reference: https://litmus.com/community/learning/24-how-to-code-a-responsive-email-from-scratch -->
|
||||
<template id="mass_mailing_mail_layout">
|
||||
<!DOCTYPE html>
|
||||
@@ -34,4 +17,4 @@
|
||||
</body>
|
||||
</html>
|
||||
</template>
|
||||
</odoo>
|
||||
</data></odoo>
|
||||
@@ -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
|
||||
|
||||
@@ -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):
|
||||
|
||||
|
||||
@@ -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
|
||||
}
|
||||
@@ -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'}
|
||||
@@ -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
|
||||
@@ -0,0 +1,78 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo>
|
||||
<template id="assets_backend" name="mass_mailing assets" inherit_id="web.assets_backend">
|
||||
<xpath expr="." position="inside">
|
||||
<link rel="stylesheet" type="text/scss" href="/mass_mailing/static/src/scss/mass_mailing.scss"/>
|
||||
<link rel="stylesheet" href="/mass_mailing/static/src/css/email_template.css"/>
|
||||
|
||||
<script type="text/javascript" src="/mass_mailing/static/src/js/mass_mailing.js"></script>
|
||||
<script type="text/javascript" src="/mass_mailing/static/src/js/mass_mailing_widget.js"></script>
|
||||
</xpath>
|
||||
<xpath expr="//script[last()]" position="after">
|
||||
<script type="text/javascript" src="/mass_mailing/static/src/js/mass_mailing_campaign_kanban_record.js"></script>
|
||||
<script type="text/javascript" src="/mass_mailing/static/src/js/mass_mailing_campaign_kanban_renderer.js"></script>
|
||||
<script type="text/javascript" src="/mass_mailing/static/src/js/mass_mailing_campaign_kanban_view.js"></script>
|
||||
<script type="text/javascript" src="/mass_mailing/static/src/js/mass_mailing_list_kanban_record.js"></script>
|
||||
<script type="text/javascript" src="/mass_mailing/static/src/js/mass_mailing_list_kanban_renderer.js"></script>
|
||||
<script type="text/javascript" src="/mass_mailing/static/src/js/mass_mailing_list_kanban_view.js"></script>
|
||||
<script type="text/javascript" src="/mass_mailing/static/src/js/unsubscribe.js"></script>
|
||||
</xpath>
|
||||
</template>
|
||||
|
||||
<template id="assets_mail_themes">
|
||||
<link rel="stylesheet" type="text/scss" href="/mass_mailing/static/src/scss/themes/theme_basic.scss"/>
|
||||
<link rel="stylesheet" type="text/scss" href="/mass_mailing/static/src/scss/themes/theme_default.scss"/>
|
||||
<t t-call="mass_mailing.mass_mailing_mail_style"/>
|
||||
</template>
|
||||
|
||||
<template id="assets_mail_themes_edition"> <!-- maybe to remove and convert into a field dumy with attr invisible if the template is not selected -->
|
||||
<t t-call="web._assets_helpers">
|
||||
<link rel="stylesheet" type="text/scss" href="/web_editor/static/src/scss/wysiwyg_variables.scss"/>
|
||||
</t>
|
||||
<link rel="stylesheet" type="text/scss" href="/mass_mailing/static/src/scss/mass_mailing.ui.scss"/>
|
||||
<link rel="stylesheet" type="text/scss" href="/web/static/src/scss/webclient.scss"/>
|
||||
</template>
|
||||
|
||||
<template id="iframe_css_assets_edit">
|
||||
<t t-call-assets="web.assets_common" t-js="false"/>
|
||||
<t t-call-assets="web.assets_frontend" t-js="false"/>
|
||||
<t t-call-assets="web_editor.assets_wysiwyg" t-js="false"/>
|
||||
<t t-call-assets="mass_mailing.assets_mail_themes" t-js="false"/>
|
||||
<t t-call-assets="mass_mailing.assets_mail_themes_edition" t-js="false"/>
|
||||
</template>
|
||||
|
||||
<template id="iframe_css_assets_readonly">
|
||||
<link rel="stylesheet" type="text/scss" href="/mass_mailing/static/src/css/basic_theme_readonly.css"/>
|
||||
</template>
|
||||
|
||||
<template id="qunit_suite" inherit_id="web.qunit_suite">
|
||||
<xpath expr="." position="inside">
|
||||
<script type="text/javascript">
|
||||
odoo.define('mass_mailing.FieldHtml.test', function (require) {
|
||||
'use strict';
|
||||
var MassMailingFieldHtml = require('mass_mailing.FieldHtml');
|
||||
MassMailingFieldHtml.include({jsLibs: []});
|
||||
});
|
||||
</script>
|
||||
<script type="text/javascript" src="/mass_mailing/static/src/js/mass_mailing_snippets.js"/>
|
||||
<script type="text/javascript" src="/mass_mailing/static/tests/mass_mailing_html_tests.js"/>
|
||||
</xpath>
|
||||
</template>
|
||||
|
||||
<template id="mass_mailing_mail_style">
|
||||
<style>
|
||||
@media screen and (max-width: 768px) {
|
||||
.o_mail_col_mv {
|
||||
display: block !important;
|
||||
width: auto !important;
|
||||
}
|
||||
.o_mail_table_styles {
|
||||
width: 100% !important;
|
||||
}
|
||||
.o_mail_col_container {
|
||||
margin: 0px 0px 10px 0px !important;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</template>
|
||||
</odoo>
|
||||
@@ -1,42 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo>
|
||||
<template id="assets_mail_themes">
|
||||
<link rel="stylesheet" type="text/scss" href="/mass_mailing/static/src/scss/themes/theme_basic.scss"/>
|
||||
<link rel="stylesheet" type="text/scss" href="/mass_mailing/static/src/scss/themes/theme_default.scss"/>
|
||||
<t t-call="mass_mailing.mass_mailing_mail_style"/>
|
||||
</template>
|
||||
|
||||
<template id="assets_mail_themes_edition"> <!-- maybe to remove and convert into a field dumy with attr invisible if the template is not selected -->
|
||||
<t t-call="web._assets_helpers">
|
||||
<link rel="stylesheet" type="text/scss" href="/web_editor/static/src/scss/wysiwyg_variables.scss"/>
|
||||
</t>
|
||||
<link rel="stylesheet" type="text/scss" href="/mass_mailing/static/src/scss/mass_mailing.ui.scss"/>
|
||||
<link rel="stylesheet" type="text/scss" href="/web/static/src/scss/webclient.scss"/>
|
||||
</template>
|
||||
|
||||
<template id="iframe_css_assets_edit">
|
||||
<t t-call-assets="web.assets_common" t-js="false"/>
|
||||
<t t-call-assets="web.assets_frontend" t-js="false"/>
|
||||
<t t-call-assets="web_editor.assets_wysiwyg" t-js="false"/>
|
||||
<t t-call-assets="mass_mailing.assets_mail_themes" t-js="false"/>
|
||||
<t t-call-assets="mass_mailing.assets_mail_themes_edition" t-js="false"/>
|
||||
</template>
|
||||
|
||||
<template id="iframe_css_assets_readonly">
|
||||
<link rel="stylesheet" type="text/scss" href="/mass_mailing/static/src/css/basic_theme_readonly.css"/>
|
||||
</template>
|
||||
|
||||
<template id="qunit_suite" inherit_id="web.qunit_suite">
|
||||
<xpath expr="." position="inside">
|
||||
<script type="text/javascript">
|
||||
odoo.define('mass_mailing.FieldHtml.test', function (require) {
|
||||
'use strict';
|
||||
var MassMailingFieldHtml = require('mass_mailing.FieldHtml');
|
||||
MassMailingFieldHtml.include({jsLibs: []});
|
||||
});
|
||||
</script>
|
||||
<script type="text/javascript" src="/mass_mailing/static/src/js/mass_mailing_snippets.js"/>
|
||||
<script type="text/javascript" src="/mass_mailing/static/tests/mass_mailing_html_tests.js"/>
|
||||
</xpath>
|
||||
</template>
|
||||
</odoo>
|
||||
@@ -1,4 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<odoo>
|
||||
<menuitem id="mail_blacklist_mm_menu" name="Blacklist" action="mail.mail_blacklist_action" parent="mass_mailing_configuration"/>
|
||||
</odoo>
|
||||
@@ -0,0 +1,267 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo>
|
||||
<!-- MAILING CONTACT -->
|
||||
<record model="ir.ui.view" id="mail_mass_mailing_list_contact_rel_view_form">
|
||||
<!--TODO DBE : To remove - Deprecated -->
|
||||
<field name="name">mail.mass_mailing.list_contact_rel.view.form</field>
|
||||
<field name="model">mail.mass_mailing.list_contact_rel</field>
|
||||
<field name="priority">10</field>
|
||||
<field name="arch" type="xml">
|
||||
<form string="Mailing List contacts">
|
||||
<sheet>
|
||||
<group>
|
||||
<field name="list_id"/>
|
||||
<field name="is_blacklisted" invisible="1"/>
|
||||
<label for="contact_id" class="oe_inline"/>
|
||||
<div class="o_row o_row_readonly">
|
||||
<i class="fa fa-ban" style="color: red;" role="img" title="This email is blacklisted for mass mailing"
|
||||
aria-label="Blacklisted" attrs="{'invisible': [('is_blacklisted', '=', False)]}" groups="base.group_user"></i>
|
||||
<field name="contact_id"/>
|
||||
</div>
|
||||
<field name="unsubscription_date" readonly="1"/>
|
||||
<field name="opt_out"/>
|
||||
<field name="message_bounce" readonly="1"/>
|
||||
</group>
|
||||
</sheet>
|
||||
</form>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record model="ir.ui.view" id="mail_mass_mailing_list_contact_rel_list_contact_view_tree">
|
||||
<!--TODO DBE : To remove - Deprecated -->
|
||||
<field name="name">mail.mass_mailing.list_contact_rel.list_contact.view.tree</field>
|
||||
<field name="model">mail.mass_mailing.list_contact_rel</field>
|
||||
<field name="arch" type="xml">
|
||||
<tree string="Mailing Lists">
|
||||
<field name="contact_id"/>
|
||||
<field name="unsubscription_date"/>
|
||||
<field name="opt_out"/>
|
||||
<field name="message_bounce"/>
|
||||
<field name="is_blacklisted"/>
|
||||
<button name="action_open_mailing_list_contact" string="Open Contact" type="object" icon="fa-user"/>
|
||||
</tree>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record model="ir.ui.view" id="mail_mass_mailing_list_contact_view_search">
|
||||
<!--TODO DBE : To remove - Deprecated -->
|
||||
<field name="name">mail.mass_mailing.list_contact_rel.view.search</field>
|
||||
<field name="model">mail.mass_mailing.list_contact_rel</field>
|
||||
<field name="arch" type="xml">
|
||||
<search string="Mailing List Contacts">
|
||||
<field name="contact_id"/>
|
||||
<field name="opt_out"/>
|
||||
<field name="list_id"/>
|
||||
<separator/>
|
||||
<filter string="Valid Recipients" name="valid_recipients" domain="[('opt_out', '=', False), ('contact_id.is_blacklisted', '=', False), ('contact_id.is_email_valid', '=', True)]"/>
|
||||
<separator/>
|
||||
<filter string="Exclude Opt Out" name="not_opt_out" domain="[('opt_out', '=', False)]"/>
|
||||
<separator/>
|
||||
<filter string="Exclude Blacklisted" name="not_blacklisted" domain="[('contact_id.is_blacklisted', '=', False)]"/>
|
||||
<separator/>
|
||||
<filter string="Exclude Invalid Emails" name="valid_email" domain="[('contact_id.is_email_valid', '=', True)]"/>
|
||||
<separator/>
|
||||
<filter string="Exclude Bounced" name="not_bounced" domain="[('message_bounce', '<', 3)]"/>
|
||||
<group expand="0" string="Group By">
|
||||
<filter string="Creation Date" name="group_create_date"
|
||||
context="{'group_by': 'create_date'}"/>
|
||||
</group>
|
||||
</search>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record model="ir.ui.view" id="view_mail_mass_mailing_contact_search">
|
||||
<field name="name">mail.mass_mailing.contact.search</field>
|
||||
<field name="model">mail.mass_mailing.contact</field>
|
||||
<field name="arch" type="xml">
|
||||
<search string="Mailing List Contacts">
|
||||
<field name="name" filter_domain="['|','|',('name','ilike',self),('company_name','ilike',self), ('email','ilike',self)]"
|
||||
string="Name / Email"/>
|
||||
<field name="tag_ids"/>
|
||||
<field name="list_ids"/>
|
||||
<separator/>
|
||||
<filter string="Valid Recipients" name="valid_recipients" domain="[('opt_out', '=', False), ('is_blacklisted', '=', False), ('is_email_valid', '=', True)]"
|
||||
invisible="'default_list_ids' not in context"/>
|
||||
<separator/>
|
||||
<filter string="Exclude Blacklisted" name="not_blacklisted" domain="[('is_blacklisted', '=', False)]"/>
|
||||
<separator/>
|
||||
<filter string="Exclude Opt Out" name="not_opt_out" domain="[('opt_out', '=', False)]"
|
||||
invisible="'default_list_ids' not in context"/>
|
||||
<separator/>
|
||||
<separator/>
|
||||
<filter string="Exclude Bounced" name="not_bounced" domain="[('message_bounce', '<', 3)]"/>
|
||||
<group expand="0" string="Group By">
|
||||
<filter string="Creation Date" name="group_create_date"
|
||||
context="{'group_by': 'create_date'}"/>
|
||||
</group>
|
||||
</search>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record model="ir.ui.view" id="view_mail_mass_mailing_contact_tree">
|
||||
<field name="name">mail.mass_mailing.contact.tree</field>
|
||||
<field name="model">mail.mass_mailing.contact</field>
|
||||
<field name="priority">10</field>
|
||||
<field name="arch" type="xml">
|
||||
<tree string="Mailing List Contacts">
|
||||
<field name="create_date"/>
|
||||
<field name="name"/>
|
||||
<field name="company_name"/>
|
||||
<field name="email"/>
|
||||
<field name="message_bounce"/>
|
||||
<field name="opt_out" invisible="'default_list_ids' not in context"/>
|
||||
<field name="is_blacklisted"/>
|
||||
</tree>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="view_mail_mass_mailing_contact_kanban" model="ir.ui.view">
|
||||
<field name="name">mail.mass_mailing.contact.kanban</field>
|
||||
<field name="model">mail.mass_mailing.contact</field>
|
||||
<field name="arch" type="xml">
|
||||
<kanban>
|
||||
<field name="name"/>
|
||||
<field name="company_name"/>
|
||||
<field name="email"/>
|
||||
<field name="message_bounce"/>
|
||||
<templates>
|
||||
<t t-name="kanban-box">
|
||||
<div t-attf-class="oe_kanban_global_click">
|
||||
<div class="o_kanban_record_top">
|
||||
<div class="o_kanban_record_headings">
|
||||
<strong class="o_kanban_record_title">
|
||||
<t t-esc="record.name.value"/>
|
||||
</strong>
|
||||
</div>
|
||||
<span class="badge badge-pill" title="Number of bounced email.">
|
||||
<i class="fa fa-exclamation-triangle" role="img" aria-label="Warning" title="Warning"/> <t t-esc="record.message_bounce.value" title=""/>
|
||||
</span>
|
||||
</div>
|
||||
<div class="o_kanban_record_body">
|
||||
<field name="tag_ids"/>
|
||||
</div>
|
||||
<div class="o_kanban_record_bottom">
|
||||
<div class="oe_kanban_bottom_left">
|
||||
<strong>
|
||||
<t t-esc="record.email.value"/>
|
||||
</strong>
|
||||
</div>
|
||||
<div class="oe_kanban_bottom_right">
|
||||
<t t-esc="record.company_name.value"/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</t>
|
||||
</templates>
|
||||
</kanban>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record model="ir.ui.view" id="view_mail_mass_mailing_contact_form">
|
||||
<field name="name">mail.mass_mailing.contact.form</field>
|
||||
<field name="model">mail.mass_mailing.contact</field>
|
||||
<field name="priority">10</field>
|
||||
<field name="arch" type="xml">
|
||||
<form string="Mailing List Contacts">
|
||||
<sheet>
|
||||
<div class="oe_title">
|
||||
<label for="name" class="oe_edit_only"/>
|
||||
<h1>
|
||||
<field name="name" placeholder="e.g. John Smith"/>
|
||||
</h1>
|
||||
<label for="tag_ids" class="oe_edit_only"/>
|
||||
<div>
|
||||
<field name="tag_ids" widget="many2many_tags" style="width: 100%%"/>
|
||||
</div>
|
||||
</div>
|
||||
<group>
|
||||
<group>
|
||||
<field name="is_blacklisted" invisible="1"/>
|
||||
<label for="email" class="oe_inline"/>
|
||||
<div class="o_row o_row_readonly">
|
||||
<i class="fa fa-ban" style="color: red;" role="img" title="This email is blacklisted for mass mailing"
|
||||
aria-label="Blacklisted" attrs="{'invisible': [('is_blacklisted', '=', False)]}" groups="base.group_user"></i>
|
||||
<field name="email" widget="email"/>
|
||||
</div>
|
||||
<field name="title_id"/>
|
||||
<field name="company_name"/>
|
||||
<field name="country_id"/>
|
||||
</group>
|
||||
<group>
|
||||
<field name="create_date" readonly="1"/>
|
||||
<label for="message_bounce"/>
|
||||
<div class="o_row">
|
||||
<field name="message_bounce"/>
|
||||
<span> times</span>
|
||||
</div>
|
||||
</group>
|
||||
</group>
|
||||
<field name="subscription_list_ids">
|
||||
<tree editable="bottom">
|
||||
<field name="list_id"/>
|
||||
<field name="unsubscription_date"/>
|
||||
<field name="opt_out"/>
|
||||
</tree>
|
||||
</field>
|
||||
</sheet>
|
||||
<div class="oe_chatter">
|
||||
<field name="message_follower_ids" widget="mail_followers"/>
|
||||
<field name="message_ids" widget="mail_thread"/>
|
||||
</div>
|
||||
</form>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record model="ir.ui.view" id="view_mail_mass_mailing_contact_pivot">
|
||||
<field name="name">mail.mass_mailing.contact.pivot</field>
|
||||
<field name="model">mail.mass_mailing.contact</field>
|
||||
<field name="priority">10</field>
|
||||
<field name="arch" type="xml">
|
||||
<pivot string="Mailing List Contacts" stacked="True">
|
||||
<field name="create_date" type="row"/>
|
||||
</pivot>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record model="ir.ui.view" id="view_mail_mass_mailing_contact_graph">
|
||||
<field name="name">mail.mass_mailing.contact.graph</field>
|
||||
<field name="model">mail.mass_mailing.contact</field>
|
||||
<field name="priority">10</field>
|
||||
<field name="arch" type="xml">
|
||||
<graph string="Mailing List Contacts" stacked="True">
|
||||
<field name="create_date" type="row"/>
|
||||
</graph>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record model="ir.actions.act_window" id="action_view_mass_mailing_contacts">
|
||||
<field name="name">Mailing List Contacts</field>
|
||||
<field name="res_model">mail.mass_mailing.contact</field>
|
||||
<field name="view_mode">tree,kanban,form,graph,pivot</field>
|
||||
<field name="context">{'search_default_valid_recipient': 1}</field>
|
||||
<field name="help" type="html">
|
||||
<p class="o_view_nocontent_smiling_face">
|
||||
Create a contact in your address book
|
||||
</p><p>
|
||||
Odoo helps you to easily track all activities related to a customer.
|
||||
</p>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record model="ir.actions.act_window" id="action_view_mass_mailing_contacts_from_list">
|
||||
<field name="name">Recipients</field>
|
||||
<field name="res_model">mail.mass_mailing.contact</field>
|
||||
<field name="view_mode">tree,form,graph</field>
|
||||
<field name="domain">[('list_ids', 'in', [active_id])]</field>
|
||||
<field name="context">{'search_default_valid_recipients': 1, 'default_list_ids': [active_id]}</field>
|
||||
<field name="help" type="html">
|
||||
<p class="o_view_nocontent_smiling_face">
|
||||
Create a new recipient
|
||||
</p>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<menuitem name="Mailing List Contacts" id="menu_email_mass_mailing_contacts"
|
||||
parent="mass_mailing_mailing_list_menu" sequence="4"
|
||||
action="action_view_mass_mailing_contacts"/>
|
||||
</odoo>
|
||||
@@ -0,0 +1,167 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo>
|
||||
<!-- MAILING LIST -->
|
||||
<record model="ir.ui.view" id="view_mail_mass_mailing_list_search">
|
||||
<field name="name">mail.mass_mailing.list.search</field>
|
||||
<field name="model">mail.mass_mailing.list</field>
|
||||
<field name="arch" type="xml">
|
||||
<search string="Mailing Lists">
|
||||
<field name="name"/>
|
||||
<field name="create_date"/>
|
||||
<field name="is_public"/>
|
||||
<filter name="inactive" string="Archived" domain="[('active','=',False)]"/>
|
||||
<group expand="0" string="Group By">
|
||||
<filter string="Creation Period" name="group_create_date"
|
||||
context="{'group_by': 'create_date'}"/>
|
||||
</group>
|
||||
</search>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record model="ir.ui.view" id="view_mail_mass_mailing_list_tree">
|
||||
<field name="name">mail.mass_mailing.list.tree</field>
|
||||
<field name="model">mail.mass_mailing.list</field>
|
||||
<field name="priority">10</field>
|
||||
<field name="arch" type="xml">
|
||||
<tree string="Mailing Lists">
|
||||
<field name="name"/>
|
||||
<field name="create_date"/>
|
||||
<field name="is_public"/>
|
||||
<field name="contact_nbr"/>
|
||||
</tree>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record model="ir.ui.view" id="view_mail_mass_mailing_list_form">
|
||||
<field name="name">mail.mass_mailing.list.form</field>
|
||||
<field name="model">mail.mass_mailing.list</field>
|
||||
<field name="arch" type="xml">
|
||||
<form string="Contact List">
|
||||
<sheet>
|
||||
<widget name="web_ribbon" text="Archived" bg_color="bg-danger" attrs="{'invisible': [('active', '=', True)]}"/>
|
||||
<div class="oe_button_box" name="button_box">
|
||||
<button name="%(mass_mailing.action_view_mass_mailing_contacts_from_list)d"
|
||||
type="action" icon="fa-user" class="oe_stat_button">
|
||||
<field name="contact_nbr" string="Recipients" widget="statinfo"/>
|
||||
</button>
|
||||
</div>
|
||||
<div class="oe_title">
|
||||
<label for="name" class="oe_edit_only"/>
|
||||
<h1>
|
||||
<field name="name" placeholder="e.g. Consumer Newsletter"/>
|
||||
</h1>
|
||||
</div>
|
||||
<group>
|
||||
<field name="active" invisible="1"/>
|
||||
<field name="is_public"/>
|
||||
</group>
|
||||
</sheet>
|
||||
</form>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="view_mail_mass_mailing_list_form_simplified" model="ir.ui.view">
|
||||
<field name="name">mail.mass_mailing.list.form.simplified</field>
|
||||
<field name="model">mail.mass_mailing.list</field>
|
||||
<field name="arch" type="xml">
|
||||
<form string="Contact List">
|
||||
<group>
|
||||
<group>
|
||||
<div class="oe_title">
|
||||
<label for="name" class="oe_edit_only"/>
|
||||
<h1>
|
||||
<field name="name" placeholder="e.g. Consumer Newsletter"/>
|
||||
</h1>
|
||||
</div>
|
||||
</group>
|
||||
</group>
|
||||
<group>
|
||||
<field name="is_public"/>
|
||||
</group>
|
||||
<footer>
|
||||
<button string="Create" name="close_dialog" type="object" class="btn-primary"/>
|
||||
<button string="Discard" class="btn-secondary" special="cancel"/>
|
||||
</footer>
|
||||
</form>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="open_create_mass_mailing_list" model="ir.actions.act_window">
|
||||
<field name="name">Create a Mass Mailing List</field>
|
||||
<field name="res_model">mail.mass_mailing.list</field>
|
||||
<field name="view_mode">form</field>
|
||||
<field name="view_id" ref="view_mail_mass_mailing_list_form_simplified"/>
|
||||
<field name="target">new</field>
|
||||
</record>
|
||||
|
||||
<record id="view_mail_mass_mailing_list_kanban" model="ir.ui.view">
|
||||
<field name="name">mail.mass_mailing.list.kanban</field>
|
||||
<field name="model">mail.mass_mailing.list</field>
|
||||
<field name="arch" type="xml">
|
||||
<kanban js_class="mass_mailing_list_kanban" class="o_kanban_mobile" on_create="mass_mailing.open_create_mass_mailing_list">
|
||||
<field name="name"/>
|
||||
<field name="contact_nbr"/>
|
||||
<field name="active"/>
|
||||
<templates>
|
||||
<t t-name="kanban-box">
|
||||
<div t-attf-class="oe_kanban_global_click">
|
||||
<div class="o_mass_mailing_kanban_main">
|
||||
<div class="o_kanban_card_content">
|
||||
<div class="o_kanban_primary_left">
|
||||
<div class="o_primary">
|
||||
<span><t t-esc="record.name.value"/></span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="o_kanban_card_manage_pane">
|
||||
<div class="o_kanban_card_manage_section o_dropdown_kanban dropdown">
|
||||
<a role="button" class="dropdown-toggle o-no-caret btn" data-toggle="dropdown" href="#" aria-label="Dropdown menu" title="Dropdown menu">
|
||||
<span class="fa fa-ellipsis-v"/>
|
||||
</a>
|
||||
<div class="dropdown-menu" role="menu">
|
||||
<a t-if="widget.editable" role="menuitem" type="edit" class="dropdown-item">Edit</a>
|
||||
<a t-if="widget.deletable" role="menuitem" type="delete" class="dropdown-item">Delete</a>
|
||||
<a role="menuitem" class="dropdown-item o_kanban_mailing_active" name="toggle_active" type="object">
|
||||
<t t-if="record.active.raw_value">Archive</t>
|
||||
<t t-if="!record.active.raw_value">Restore</t>
|
||||
</a>
|
||||
<ul class="oe_kanban_colorpicker" data-field="color"/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="o_mailing_list_kanban_boxes">
|
||||
<a class="o_project_kanban_box" name="%(action_view_mass_mailing_contacts_from_list)d" type="action">
|
||||
<div>
|
||||
<span class="badge badge-pill">
|
||||
<i class="fa fa-user" role="img" aria-label="Contacts" title="Contacts"/>
|
||||
<t t-esc="record.contact_nbr.value"/>
|
||||
</span>
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</t>
|
||||
</templates>
|
||||
</kanban>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record model="ir.actions.act_window" id="action_view_mass_mailing_lists">
|
||||
<field name="name">Mailing Lists</field>
|
||||
<field name="res_model">mail.mass_mailing.list</field>
|
||||
<field name="view_mode">kanban,tree,form</field>
|
||||
<field name="help" type="html">
|
||||
<p class="o_view_nocontent_smiling_face">
|
||||
Create a new mailing list
|
||||
</p><p>
|
||||
You don't need to import your mailing lists, you can easily
|
||||
send emails<br/> to any contact saved in other Odoo apps.
|
||||
</p>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<menuitem name="Mailing Lists" id="menu_email_mass_mailing_lists"
|
||||
parent="mass_mailing_mailing_list_menu" sequence="3"
|
||||
action="action_view_mass_mailing_lists"/>
|
||||
</odoo>
|
||||
@@ -1,21 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo>
|
||||
<template id="assets_backend" name="mass_mailing assets" inherit_id="web.assets_backend">
|
||||
<xpath expr="." position="inside">
|
||||
<link rel="stylesheet" type="text/scss" href="/mass_mailing/static/src/scss/mass_mailing.scss"/>
|
||||
<link rel="stylesheet" href="/mass_mailing/static/src/css/email_template.css"/>
|
||||
|
||||
<script type="text/javascript" src="/mass_mailing/static/src/js/mass_mailing.js"></script>
|
||||
<script type="text/javascript" src="/mass_mailing/static/src/js/mass_mailing_widget.js"></script>
|
||||
</xpath>
|
||||
<xpath expr="//script[last()]" position="after">
|
||||
<script type="text/javascript" src="/mass_mailing/static/src/js/mass_mailing_campaign_kanban_record.js"></script>
|
||||
<script type="text/javascript" src="/mass_mailing/static/src/js/mass_mailing_campaign_kanban_renderer.js"></script>
|
||||
<script type="text/javascript" src="/mass_mailing/static/src/js/mass_mailing_campaign_kanban_view.js"></script>
|
||||
<script type="text/javascript" src="/mass_mailing/static/src/js/mass_mailing_list_kanban_record.js"></script>
|
||||
<script type="text/javascript" src="/mass_mailing/static/src/js/mass_mailing_list_kanban_renderer.js"></script>
|
||||
<script type="text/javascript" src="/mass_mailing/static/src/js/mass_mailing_list_kanban_view.js"></script>
|
||||
<script type="text/javascript" src="/mass_mailing/static/src/js/unsubscribe.js"></script>
|
||||
</xpath>
|
||||
</template>
|
||||
</odoo>
|
||||
@@ -1,435 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo>
|
||||
<!-- MAILING CONTACT -->
|
||||
<record model="ir.ui.view" id="mail_mass_mailing_list_contact_rel_view_form">
|
||||
<!--TODO DBE : To remove - Deprecated -->
|
||||
<field name="name">mail.mass_mailing.list_contact_rel.view.form</field>
|
||||
<field name="model">mail.mass_mailing.list_contact_rel</field>
|
||||
<field name="priority">10</field>
|
||||
<field name="arch" type="xml">
|
||||
<form string="Mailing List contacts">
|
||||
<sheet>
|
||||
<group>
|
||||
<field name="list_id"/>
|
||||
<field name="is_blacklisted" invisible="1"/>
|
||||
<label for="contact_id" class="oe_inline"/>
|
||||
<div class="o_row o_row_readonly">
|
||||
<i class="fa fa-ban" style="color: red;" role="img" title="This email is blacklisted for mass mailing"
|
||||
aria-label="Blacklisted" attrs="{'invisible': [('is_blacklisted', '=', False)]}" groups="base.group_user"></i>
|
||||
<field name="contact_id"/>
|
||||
</div>
|
||||
<field name="unsubscription_date" readonly="1"/>
|
||||
<field name="opt_out"/>
|
||||
<field name="message_bounce" readonly="1"/>
|
||||
</group>
|
||||
</sheet>
|
||||
</form>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record model="ir.ui.view" id="mail_mass_mailing_list_contact_rel_list_contact_view_tree">
|
||||
<!--TODO DBE : To remove - Deprecated -->
|
||||
<field name="name">mail.mass_mailing.list_contact_rel.list_contact.view.tree</field>
|
||||
<field name="model">mail.mass_mailing.list_contact_rel</field>
|
||||
<field name="arch" type="xml">
|
||||
<tree string="Mailing Lists">
|
||||
<field name="contact_id"/>
|
||||
<field name="unsubscription_date"/>
|
||||
<field name="opt_out"/>
|
||||
<field name="message_bounce"/>
|
||||
<field name="is_blacklisted"/>
|
||||
<button name="action_open_mailing_list_contact" string="Open Contact" type="object" icon="fa-user"/>
|
||||
</tree>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record model="ir.ui.view" id="mail_mass_mailing_list_contact_view_search">
|
||||
<!--TODO DBE : To remove - Deprecated -->
|
||||
<field name="name">mail.mass_mailing.list_contact_rel.view.search</field>
|
||||
<field name="model">mail.mass_mailing.list_contact_rel</field>
|
||||
<field name="arch" type="xml">
|
||||
<search string="Mailing List Contacts">
|
||||
<field name="contact_id"/>
|
||||
<field name="opt_out"/>
|
||||
<field name="list_id"/>
|
||||
<separator/>
|
||||
<filter string="Valid Recipients" name="valid_recipients" domain="[('opt_out', '=', False), ('contact_id.is_blacklisted', '=', False), ('contact_id.is_email_valid', '=', True)]"/>
|
||||
<separator/>
|
||||
<filter string="Exclude Opt Out" name="not_opt_out" domain="[('opt_out', '=', False)]"/>
|
||||
<separator/>
|
||||
<filter string="Exclude Blacklisted" name="not_blacklisted" domain="[('contact_id.is_blacklisted', '=', False)]"/>
|
||||
<separator/>
|
||||
<filter string="Exclude Invalid Emails" name="valid_email" domain="[('contact_id.is_email_valid', '=', True)]"/>
|
||||
<separator/>
|
||||
<filter string="Exclude Bounced" name="not_bounced" domain="[('message_bounce', '<', 3)]"/>
|
||||
<group expand="0" string="Group By">
|
||||
<filter string="Creation Date" name="group_create_date"
|
||||
context="{'group_by': 'create_date'}"/>
|
||||
</group>
|
||||
</search>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record model="ir.ui.view" id="view_mail_mass_mailing_contact_search">
|
||||
<field name="name">mail.mass_mailing.contact.search</field>
|
||||
<field name="model">mail.mass_mailing.contact</field>
|
||||
<field name="arch" type="xml">
|
||||
<search string="Mailing List Contacts">
|
||||
<field name="name" filter_domain="['|','|',('name','ilike',self),('company_name','ilike',self), ('email','ilike',self)]"
|
||||
string="Name / Email"/>
|
||||
<field name="tag_ids"/>
|
||||
<field name="list_ids"/>
|
||||
<separator/>
|
||||
<filter string="Valid Recipients" name="valid_recipients" domain="[('opt_out', '=', False), ('is_blacklisted', '=', False), ('is_email_valid', '=', True)]"
|
||||
invisible="'default_list_ids' not in context"/>
|
||||
<separator/>
|
||||
<filter string="Exclude Blacklisted" name="not_blacklisted" domain="[('is_blacklisted', '=', False)]"/>
|
||||
<separator/>
|
||||
<filter string="Exclude Opt Out" name="not_opt_out" domain="[('opt_out', '=', False)]"
|
||||
invisible="'default_list_ids' not in context"/>
|
||||
<separator/>
|
||||
<separator/>
|
||||
<filter string="Exclude Bounced" name="not_bounced" domain="[('message_bounce', '<', 3)]"/>
|
||||
<group expand="0" string="Group By">
|
||||
<filter string="Creation Date" name="group_create_date"
|
||||
context="{'group_by': 'create_date'}"/>
|
||||
</group>
|
||||
</search>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record model="ir.ui.view" id="view_mail_mass_mailing_contact_tree">
|
||||
<field name="name">mail.mass_mailing.contact.tree</field>
|
||||
<field name="model">mail.mass_mailing.contact</field>
|
||||
<field name="priority">10</field>
|
||||
<field name="arch" type="xml">
|
||||
<tree string="Mailing List Contacts">
|
||||
<field name="create_date"/>
|
||||
<field name="name"/>
|
||||
<field name="company_name"/>
|
||||
<field name="email"/>
|
||||
<field name="message_bounce"/>
|
||||
<field name="opt_out" invisible="'default_list_ids' not in context"/>
|
||||
<field name="is_blacklisted"/>
|
||||
</tree>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="view_mail_mass_mailing_contact_kanban" model="ir.ui.view">
|
||||
<field name="name">mail.mass_mailing.contact.kanban</field>
|
||||
<field name="model">mail.mass_mailing.contact</field>
|
||||
<field name="arch" type="xml">
|
||||
<kanban>
|
||||
<field name="name"/>
|
||||
<field name="company_name"/>
|
||||
<field name="email"/>
|
||||
<field name="message_bounce"/>
|
||||
<templates>
|
||||
<t t-name="kanban-box">
|
||||
<div t-attf-class="oe_kanban_global_click">
|
||||
<div class="o_kanban_record_top">
|
||||
<div class="o_kanban_record_headings">
|
||||
<strong class="o_kanban_record_title">
|
||||
<t t-esc="record.name.value"/>
|
||||
</strong>
|
||||
</div>
|
||||
<span class="badge badge-pill" title="Number of bounced email.">
|
||||
<i class="fa fa-exclamation-triangle" role="img" aria-label="Warning" title="Warning"/> <t t-esc="record.message_bounce.value" title=""/>
|
||||
</span>
|
||||
</div>
|
||||
<div class="o_kanban_record_body">
|
||||
<field name="tag_ids"/>
|
||||
</div>
|
||||
<div class="o_kanban_record_bottom">
|
||||
<div class="oe_kanban_bottom_left">
|
||||
<strong>
|
||||
<t t-esc="record.email.value"/>
|
||||
</strong>
|
||||
</div>
|
||||
<div class="oe_kanban_bottom_right">
|
||||
<t t-esc="record.company_name.value"/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</t>
|
||||
</templates>
|
||||
</kanban>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record model="ir.ui.view" id="view_mail_mass_mailing_contact_form">
|
||||
<field name="name">mail.mass_mailing.contact.form</field>
|
||||
<field name="model">mail.mass_mailing.contact</field>
|
||||
<field name="priority">10</field>
|
||||
<field name="arch" type="xml">
|
||||
<form string="Mailing List Contacts">
|
||||
<sheet>
|
||||
<div class="oe_title">
|
||||
<label for="name" class="oe_edit_only"/>
|
||||
<h1>
|
||||
<field name="name" placeholder="e.g. John Smith"/>
|
||||
</h1>
|
||||
<label for="tag_ids" class="oe_edit_only"/>
|
||||
<div>
|
||||
<field name="tag_ids" widget="many2many_tags" style="width: 100%%"/>
|
||||
</div>
|
||||
</div>
|
||||
<group>
|
||||
<group>
|
||||
<field name="is_blacklisted" invisible="1"/>
|
||||
<label for="email" class="oe_inline"/>
|
||||
<div class="o_row o_row_readonly">
|
||||
<i class="fa fa-ban" style="color: red;" role="img" title="This email is blacklisted for mass mailing"
|
||||
aria-label="Blacklisted" attrs="{'invisible': [('is_blacklisted', '=', False)]}" groups="base.group_user"></i>
|
||||
<field name="email" widget="email"/>
|
||||
</div>
|
||||
<field name="title_id"/>
|
||||
<field name="company_name"/>
|
||||
<field name="country_id"/>
|
||||
</group>
|
||||
<group>
|
||||
<field name="create_date" readonly="1"/>
|
||||
<label for="message_bounce"/>
|
||||
<div class="o_row">
|
||||
<field name="message_bounce"/>
|
||||
<span> times</span>
|
||||
</div>
|
||||
</group>
|
||||
</group>
|
||||
<field name="subscription_list_ids">
|
||||
<tree editable="bottom">
|
||||
<field name="list_id"/>
|
||||
<field name="unsubscription_date"/>
|
||||
<field name="opt_out"/>
|
||||
</tree>
|
||||
</field>
|
||||
</sheet>
|
||||
<div class="oe_chatter">
|
||||
<field name="message_follower_ids" widget="mail_followers"/>
|
||||
<field name="message_ids" widget="mail_thread"/>
|
||||
</div>
|
||||
</form>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record model="ir.ui.view" id="view_mail_mass_mailing_contact_pivot">
|
||||
<field name="name">mail.mass_mailing.contact.pivot</field>
|
||||
<field name="model">mail.mass_mailing.contact</field>
|
||||
<field name="priority">10</field>
|
||||
<field name="arch" type="xml">
|
||||
<pivot string="Mailing List Contacts" stacked="True">
|
||||
<field name="create_date" type="row"/>
|
||||
</pivot>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record model="ir.ui.view" id="view_mail_mass_mailing_contact_graph">
|
||||
<field name="name">mail.mass_mailing.contact.graph</field>
|
||||
<field name="model">mail.mass_mailing.contact</field>
|
||||
<field name="priority">10</field>
|
||||
<field name="arch" type="xml">
|
||||
<graph string="Mailing List Contacts" stacked="True">
|
||||
<field name="create_date" type="row"/>
|
||||
</graph>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record model="ir.actions.act_window" id="action_view_mass_mailing_contacts">
|
||||
<field name="name">Mailing List Contacts</field>
|
||||
<field name="res_model">mail.mass_mailing.contact</field>
|
||||
<field name="view_mode">tree,kanban,form,graph,pivot</field>
|
||||
<field name="context">{'search_default_valid_recipient': 1}</field>
|
||||
<field name="help" type="html">
|
||||
<p class="o_view_nocontent_smiling_face">
|
||||
Create a contact in your address book
|
||||
</p><p>
|
||||
Odoo helps you to easily track all activities related to a customer.
|
||||
</p>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record model="ir.actions.act_window" id="action_view_mass_mailing_contacts_from_list">
|
||||
<field name="name">Recipients</field>
|
||||
<field name="res_model">mail.mass_mailing.contact</field>
|
||||
<field name="view_mode">tree,form,graph</field>
|
||||
<field name="domain">[('list_ids', 'in', [active_id])]</field>
|
||||
<field name="context">{'search_default_valid_recipients': 1, 'default_list_ids': [active_id]}</field>
|
||||
<field name="help" type="html">
|
||||
<p class="o_view_nocontent_smiling_face">
|
||||
Create a new recipient
|
||||
</p>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<menuitem name="Mailing List Contacts" id="menu_email_mass_mailing_contacts"
|
||||
parent="mass_mailing_mailing_list_menu" sequence="4"
|
||||
action="action_view_mass_mailing_contacts"/>
|
||||
|
||||
<!-- MAILING LIST -->
|
||||
<record model="ir.ui.view" id="view_mail_mass_mailing_list_search">
|
||||
<field name="name">mail.mass_mailing.list.search</field>
|
||||
<field name="model">mail.mass_mailing.list</field>
|
||||
<field name="arch" type="xml">
|
||||
<search string="Mailing Lists">
|
||||
<field name="name"/>
|
||||
<field name="create_date"/>
|
||||
<field name="is_public"/>
|
||||
<filter name="inactive" string="Archived" domain="[('active','=',False)]"/>
|
||||
<group expand="0" string="Group By">
|
||||
<filter string="Creation Period" name="group_create_date"
|
||||
context="{'group_by': 'create_date'}"/>
|
||||
</group>
|
||||
</search>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record model="ir.ui.view" id="view_mail_mass_mailing_list_tree">
|
||||
<field name="name">mail.mass_mailing.list.tree</field>
|
||||
<field name="model">mail.mass_mailing.list</field>
|
||||
<field name="priority">10</field>
|
||||
<field name="arch" type="xml">
|
||||
<tree string="Mailing Lists">
|
||||
<field name="name"/>
|
||||
<field name="create_date"/>
|
||||
<field name="is_public"/>
|
||||
<field name="contact_nbr"/>
|
||||
</tree>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record model="ir.ui.view" id="view_mail_mass_mailing_list_form">
|
||||
<field name="name">mail.mass_mailing.list.form</field>
|
||||
<field name="model">mail.mass_mailing.list</field>
|
||||
<field name="arch" type="xml">
|
||||
<form string="Contact List">
|
||||
<sheet>
|
||||
<widget name="web_ribbon" text="Archived" bg_color="bg-danger" attrs="{'invisible': [('active', '=', True)]}"/>
|
||||
<div class="oe_button_box" name="button_box">
|
||||
<button name="%(mass_mailing.action_view_mass_mailing_contacts_from_list)d"
|
||||
type="action" icon="fa-user" class="oe_stat_button">
|
||||
<field name="contact_nbr" string="Recipients" widget="statinfo"/>
|
||||
</button>
|
||||
</div>
|
||||
<div class="oe_title">
|
||||
<label for="name" class="oe_edit_only"/>
|
||||
<h1>
|
||||
<field name="name" placeholder="e.g. Consumer Newsletter"/>
|
||||
</h1>
|
||||
</div>
|
||||
<group>
|
||||
<field name="active" invisible="1"/>
|
||||
<field name="is_public"/>
|
||||
</group>
|
||||
</sheet>
|
||||
</form>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="view_mail_mass_mailing_list_form_simplified" model="ir.ui.view">
|
||||
<field name="name">mail.mass_mailing.list.form.simplified</field>
|
||||
<field name="model">mail.mass_mailing.list</field>
|
||||
<field name="arch" type="xml">
|
||||
<form string="Contact List">
|
||||
<group>
|
||||
<group>
|
||||
<div class="oe_title">
|
||||
<label for="name" class="oe_edit_only"/>
|
||||
<h1>
|
||||
<field name="name" placeholder="e.g. Consumer Newsletter"/>
|
||||
</h1>
|
||||
</div>
|
||||
</group>
|
||||
</group>
|
||||
<group>
|
||||
<field name="is_public"/>
|
||||
</group>
|
||||
<footer>
|
||||
<button string="Create" name="close_dialog" type="object" class="btn-primary"/>
|
||||
<button string="Discard" class="btn-secondary" special="cancel"/>
|
||||
</footer>
|
||||
</form>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="open_create_mass_mailing_list" model="ir.actions.act_window">
|
||||
<field name="name">Create a Mass Mailing List</field>
|
||||
<field name="res_model">mail.mass_mailing.list</field>
|
||||
<field name="view_mode">form</field>
|
||||
<field name="view_id" ref="view_mail_mass_mailing_list_form_simplified"/>
|
||||
<field name="target">new</field>
|
||||
</record>
|
||||
|
||||
<record id="view_mail_mass_mailing_list_kanban" model="ir.ui.view">
|
||||
<field name="name">mail.mass_mailing.list.kanban</field>
|
||||
<field name="model">mail.mass_mailing.list</field>
|
||||
<field name="arch" type="xml">
|
||||
<kanban js_class="mass_mailing_list_kanban" class="o_kanban_mobile" on_create="mass_mailing.open_create_mass_mailing_list">
|
||||
<field name="name"/>
|
||||
<field name="contact_nbr"/>
|
||||
<field name="active"/>
|
||||
<templates>
|
||||
<t t-name="kanban-box">
|
||||
<div t-attf-class="oe_kanban_global_click">
|
||||
<div class="o_mass_mailing_kanban_main">
|
||||
<div class="o_kanban_card_content">
|
||||
<div class="o_kanban_primary_left">
|
||||
<div class="o_primary">
|
||||
<span><t t-esc="record.name.value"/></span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="o_kanban_card_manage_pane">
|
||||
<div class="o_kanban_card_manage_section o_dropdown_kanban dropdown">
|
||||
<a role="button" class="dropdown-toggle o-no-caret btn" data-toggle="dropdown" href="#" aria-label="Dropdown menu" title="Dropdown menu">
|
||||
<span class="fa fa-ellipsis-v"/>
|
||||
</a>
|
||||
<div class="dropdown-menu" role="menu">
|
||||
<a t-if="widget.editable" role="menuitem" type="edit" class="dropdown-item">Edit</a>
|
||||
<a t-if="widget.deletable" role="menuitem" type="delete" class="dropdown-item">Delete</a>
|
||||
<a role="menuitem" class="dropdown-item o_kanban_mailing_active" name="toggle_active" type="object">
|
||||
<t t-if="record.active.raw_value">Archive</t>
|
||||
<t t-if="!record.active.raw_value">Restore</t>
|
||||
</a>
|
||||
<ul class="oe_kanban_colorpicker" data-field="color"/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="o_mailing_list_kanban_boxes">
|
||||
<a class="o_project_kanban_box" name="%(action_view_mass_mailing_contacts_from_list)d" type="action">
|
||||
<div>
|
||||
<span class="badge badge-pill">
|
||||
<i class="fa fa-user" role="img" aria-label="Contacts" title="Contacts"/>
|
||||
<t t-esc="record.contact_nbr.value"/>
|
||||
</span>
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</t>
|
||||
</templates>
|
||||
</kanban>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record model="ir.actions.act_window" id="action_view_mass_mailing_lists">
|
||||
<field name="name">Mailing Lists</field>
|
||||
<field name="res_model">mail.mass_mailing.list</field>
|
||||
<field name="view_mode">kanban,tree,form</field>
|
||||
<field name="help" type="html">
|
||||
<p class="o_view_nocontent_smiling_face">
|
||||
Create a new mailing list
|
||||
</p><p>
|
||||
You don't need to import your mailing lists, you can easily
|
||||
send emails<br/> to any contact saved in other Odoo apps.
|
||||
</p>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<menuitem name="Mailing Lists" id="menu_email_mass_mailing_lists"
|
||||
parent="mass_mailing_mailing_list_menu" sequence="3"
|
||||
action="action_view_mass_mailing_lists"/>
|
||||
|
||||
<!-- MAILING !-->
|
||||
<record model="ir.ui.view" id="view_mail_mass_mailing_search">
|
||||
<field name="name">mail.mass_mailing.search</field>
|
||||
|
||||
@@ -4,9 +4,15 @@
|
||||
<menuitem name="Email Marketing" id="mass_mailing_menu_root" sequence="60" web_icon="mass_mailing,static/description/icon.png"/>
|
||||
<menuitem name="Mailing Lists" id="mass_mailing_mailing_list_menu"
|
||||
parent="mass_mailing_menu_root" sequence="2" groups="mass_mailing.group_mass_mailing_user"/>
|
||||
|
||||
<!-- Marketing / Configuration -->
|
||||
<menuitem name="Configuration" id="mass_mailing_configuration"
|
||||
parent="mass_mailing_menu_root"
|
||||
sequence="100"
|
||||
groups="mass_mailing.group_mass_mailing_user"/>
|
||||
|
||||
<!-- Configuration / Blacklist -->
|
||||
<menuitem id="mail_blacklist_mm_menu" name="Blacklist"
|
||||
action="mail.mail_blacklist_action"
|
||||
parent="mass_mailing_configuration"/>
|
||||
</odoo>
|
||||
@@ -769,4 +769,52 @@
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<!-- Snippet themes Options -->
|
||||
<template id="snippet_options">
|
||||
<t t-call="web_editor.snippet_options"/>
|
||||
<t t-raw="0"/>
|
||||
|
||||
<div data-js="mass_mailing_sizing_x"
|
||||
data-selector="img, .mv, .col_mv, td, th"
|
||||
data-exclude=".o_mail_no_resize, .o_mail_no_options"/>
|
||||
|
||||
<div data-js="mass_mailing_table_item"
|
||||
data-selector="td, th"
|
||||
data-exclude=".o_mail_no_options"/>
|
||||
|
||||
<div data-js="table_row"
|
||||
data-selector="tr:has(> .row), tr:has(> .col_mv)"
|
||||
data-exclude=".o_mail_no_options"
|
||||
data-drop-near="tr:has(> .row), tr:has(> .col_mv)"/>
|
||||
|
||||
<div data-js="table_column"
|
||||
data-selector=".col>td, .col>th"
|
||||
data-exclude=".o_mail_no_options"
|
||||
data-drop-near=".col>td, .col>th"/>
|
||||
|
||||
<div data-js="table_column_mv"
|
||||
data-selector=".col_mv, td, th"
|
||||
data-exclude=".o_mail_no_options"
|
||||
data-drop-near=".col_mv, td, th"/>
|
||||
|
||||
<div data-js="content"
|
||||
data-selector=".note-editable > div:not(.o_layout), .note-editable .oe_structure > div, .oe_snippet_body"
|
||||
data-exclude=".o_mail_no_options"
|
||||
data-drop-near="[data-oe-field='body_html']:not(:has(.o_layout)) > *, .oe_structure > *"
|
||||
data-drop-in="[data-oe-field='body_html']:not(:has(.o_layout)), .oe_structure"/>
|
||||
|
||||
<div data-js="sizing_y"
|
||||
data-selector=".note-editable > div:not(.o_layout), .note-editable .oe_structure > div, td, th"
|
||||
data-exclude=".o_mail_no_resize, .o_mail_no_options"/>
|
||||
|
||||
<div data-js="colorpicker"
|
||||
data-selector=".note-editable > div:not(.o_layout), .note-editable .oe_structure > div, td, th"
|
||||
data-exclude=".o_mail_no_colorpicker, .o_mail_no_options">
|
||||
<div class="dropdown-submenu">
|
||||
<a tabindex="-1" href="#" class="dropdown-item"><i class="fa fa-eyedropper"/>Background Color</a>
|
||||
<div class="dropdown-menu" role="menu"/>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</odoo>
|
||||
|
||||
@@ -1,51 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo>
|
||||
|
||||
<template id="snippet_options">
|
||||
<t t-call="web_editor.snippet_options"/>
|
||||
<t t-raw="0"/>
|
||||
|
||||
<div data-js="mass_mailing_sizing_x"
|
||||
data-selector="img, .mv, .col_mv, td, th"
|
||||
data-exclude=".o_mail_no_resize, .o_mail_no_options"/>
|
||||
|
||||
<div data-js="mass_mailing_table_item"
|
||||
data-selector="td, th"
|
||||
data-exclude=".o_mail_no_options"/>
|
||||
|
||||
<div data-js="table_row"
|
||||
data-selector="tr:has(> .row), tr:has(> .col_mv)"
|
||||
data-exclude=".o_mail_no_options"
|
||||
data-drop-near="tr:has(> .row), tr:has(> .col_mv)"/>
|
||||
|
||||
<div data-js="table_column"
|
||||
data-selector=".col>td, .col>th"
|
||||
data-exclude=".o_mail_no_options"
|
||||
data-drop-near=".col>td, .col>th"/>
|
||||
|
||||
<div data-js="table_column_mv"
|
||||
data-selector=".col_mv, td, th"
|
||||
data-exclude=".o_mail_no_options"
|
||||
data-drop-near=".col_mv, td, th"/>
|
||||
|
||||
<div data-js="content"
|
||||
data-selector=".note-editable > div:not(.o_layout), .note-editable .oe_structure > div, .oe_snippet_body"
|
||||
data-exclude=".o_mail_no_options"
|
||||
data-drop-near="[data-oe-field='body_html']:not(:has(.o_layout)) > *, .oe_structure > *"
|
||||
data-drop-in="[data-oe-field='body_html']:not(:has(.o_layout)), .oe_structure"/>
|
||||
|
||||
<div data-js="sizing_y"
|
||||
data-selector=".note-editable > div:not(.o_layout), .note-editable .oe_structure > div, td, th"
|
||||
data-exclude=".o_mail_no_resize, .o_mail_no_options"/>
|
||||
|
||||
<div data-js="colorpicker"
|
||||
data-selector=".note-editable > div:not(.o_layout), .note-editable .oe_structure > div, td, th"
|
||||
data-exclude=".o_mail_no_colorpicker, .o_mail_no_options">
|
||||
<div class="dropdown-submenu">
|
||||
<a tabindex="-1" href="#" class="dropdown-item"><i class="fa fa-eyedropper"/>Background Color</a>
|
||||
<div class="dropdown-menu" role="menu"/>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
</odoo>
|
||||
Reference in New Issue
Block a user