[MERGE] mail: reorganize channel model and views
PURPOSE
Reorganize and lint mail.channel code. Purpose is to ease future modifications
linked to groups and channels in mail / Discuss.
SPECIFICATIONS
Move mail.channel.partner and mail.moderation models into their own file.
This makes some noise in diff but as channel is quite an heavy model let us
do this once for all.
Move mail.channel.partner views in their own file, as for python models.
Also containing
* separate and reorganize fields by main definition;
* separate and reorganize methods by categories, notably CRUD + orm, members
management, moderation, mailing, IM, commands;
* reorganize compute section: compute (by field order), constraints then
onchanges;
Reorganize and lint mail.channel code. Purpose is to ease future modifications
linked to groups and channels in mail / Discuss.
Some tools methods do not necessarily require to be public.
Cleanup a bit mail addon by moving menu definitions in their own file
according to guidelines.
No functional change should be involved in this PR as only some code move
and light renaming has been performed.
LINKS
Prepares Task ID-2070632 (Discuss channel task)
Prepares Task ID-2419762 (SM channel task)
COM PR #64862
Signed-off-by: Thibault Delavallee (tde) <tde@openerp.com>
This commit is contained in:
@@ -7,8 +7,8 @@ from odoo import fields, models
|
||||
class Channel(models.Model):
|
||||
_inherit = 'mail.channel'
|
||||
|
||||
def partner_info(self, all_partners, direct_partners):
|
||||
partner_infos = super(Channel, self).partner_info(all_partners, direct_partners)
|
||||
def _get_channel_partner_info(self, all_partners, direct_partners):
|
||||
partner_infos = super(Channel, self)._get_channel_partner_info(all_partners, direct_partners)
|
||||
# only search for leave out_of_office_date_end if im_status is on leave
|
||||
partners_on_leave = [partner_id for partner_id in direct_partners.ids if 'leave' in partner_infos[partner_id]['im_status']]
|
||||
if partners_on_leave:
|
||||
|
||||
@@ -11,7 +11,6 @@
|
||||
'depends': ['base', 'base_setup', 'bus', 'web_tour'],
|
||||
'data': [
|
||||
'views/assets.xml',
|
||||
'views/mail_menus.xml',
|
||||
'wizard/invite_view.xml',
|
||||
'wizard/mail_blacklist_remove_view.xml',
|
||||
'wizard/mail_compose_message_view.xml',
|
||||
@@ -25,6 +24,7 @@
|
||||
'views/mail_mail_views.xml',
|
||||
'views/mail_followers_views.xml',
|
||||
'views/mail_moderation_views.xml',
|
||||
'views/mail_channel_partner_views.xml',
|
||||
'views/mail_channel_views.xml',
|
||||
'views/mail_shortcode_views.xml',
|
||||
'views/mail_activity_views.xml',
|
||||
@@ -45,6 +45,7 @@
|
||||
'views/ir_model_views.xml',
|
||||
'views/res_partner_views.xml',
|
||||
'views/mail_blacklist_views.xml',
|
||||
'views/mail_menus.xml',
|
||||
],
|
||||
'demo': [
|
||||
'data/mail_channel_demo.xml',
|
||||
|
||||
@@ -15,6 +15,8 @@ from . import mail_thread
|
||||
from . import mail_thread_blacklist
|
||||
from . import mail_thread_cc
|
||||
from . import mail_blacklist
|
||||
from . import mail_moderation
|
||||
from . import mail_channel_partner
|
||||
from . import mail_channel
|
||||
from . import mail_template
|
||||
from . import mail_shortcode
|
||||
|
||||
+191
-223
@@ -10,68 +10,11 @@ from odoo import _, api, fields, models, modules, tools, Command
|
||||
from odoo.exceptions import UserError, ValidationError
|
||||
from odoo.osv import expression
|
||||
from odoo.tools import ormcache, formataddr
|
||||
from odoo.exceptions import AccessError
|
||||
from odoo.addons.base.models.ir_model import MODULE_UNINSTALL_FLAG
|
||||
|
||||
MODERATION_FIELDS = ['moderation', 'moderator_ids', 'moderation_ids', 'moderation_notify', 'moderation_notify_msg', 'moderation_guidelines', 'moderation_guidelines_msg']
|
||||
_logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class ChannelPartner(models.Model):
|
||||
_name = 'mail.channel.partner'
|
||||
_description = 'Listeners of a Channel'
|
||||
_table = 'mail_channel_partner'
|
||||
_rec_name = 'partner_id'
|
||||
|
||||
custom_channel_name = fields.Char('Custom channel name')
|
||||
partner_id = fields.Many2one('res.partner', string='Recipient', ondelete='cascade')
|
||||
partner_email = fields.Char('Email', related='partner_id.email', readonly=False)
|
||||
channel_id = fields.Many2one('mail.channel', string='Channel', ondelete='cascade')
|
||||
fetched_message_id = fields.Many2one('mail.message', string='Last Fetched')
|
||||
seen_message_id = fields.Many2one('mail.message', string='Last Seen')
|
||||
fold_state = fields.Selection([('open', 'Open'), ('folded', 'Folded'), ('closed', 'Closed')], string='Conversation Fold State', default='open')
|
||||
is_minimized = fields.Boolean("Conversation is minimized")
|
||||
is_pinned = fields.Boolean("Is pinned on the interface", default=True)
|
||||
|
||||
@api.model_create_multi
|
||||
def create(self, vals_list):
|
||||
"""Similar access rule as the access rule of the mail channel.
|
||||
|
||||
It can not be implemented in XML, because when the record will be created, the
|
||||
partner will be added in the channel and the security rule will always authorize
|
||||
the creation.
|
||||
"""
|
||||
if not self.env.is_admin():
|
||||
for vals in vals_list:
|
||||
if 'channel_id' in vals and not self.env.is_admin():
|
||||
channel_id = self.env['mail.channel'].browse(vals['channel_id'])
|
||||
if not channel_id._can_invite(vals.get('partner_id')):
|
||||
raise AccessError(_('This user can not be added in this channel'))
|
||||
return super(ChannelPartner, self).create(vals_list)
|
||||
|
||||
def write(self, vals):
|
||||
if not self.env.is_admin():
|
||||
if {'channel_id', 'partner_id', 'partner_email'} & set(vals):
|
||||
raise AccessError(_('You can not write on this field'))
|
||||
return super(ChannelPartner, self).write(vals)
|
||||
|
||||
|
||||
class Moderation(models.Model):
|
||||
_name = 'mail.moderation'
|
||||
_description = 'Channel black/white list'
|
||||
|
||||
email = fields.Char(string="Email", index=True, required=True)
|
||||
status = fields.Selection([
|
||||
('allow', 'Always Allow'),
|
||||
('ban', 'Permanent Ban')],
|
||||
string="Status", required=True)
|
||||
channel_id = fields.Many2one('mail.channel', string="Channel", index=True, required=True)
|
||||
|
||||
_sql_constraints = [
|
||||
('channel_email_uniq', 'unique (email,channel_id)', 'The email address must be unique per channel !')
|
||||
]
|
||||
|
||||
|
||||
class Channel(models.Model):
|
||||
""" A mail.channel is a discussion group that may behave like a listener
|
||||
on documents. """
|
||||
@@ -94,39 +37,39 @@ class Channel(models.Model):
|
||||
image_path = modules.get_module_resource('mail', 'static/src/img', 'groupdefault.png')
|
||||
return base64.b64encode(open(image_path, 'rb').read())
|
||||
|
||||
# description
|
||||
name = fields.Char('Name', required=True, translate=True)
|
||||
active = fields.Boolean(default=True, help="Set active to false to hide the channel without removing it.")
|
||||
channel_type = fields.Selection([
|
||||
('chat', 'Chat Discussion'),
|
||||
('channel', 'Channel')],
|
||||
'Channel Type', default='channel')
|
||||
string='Channel Type', default='channel')
|
||||
is_chat = fields.Boolean(string='Is a chat', compute='_compute_is_chat')
|
||||
description = fields.Text('Description')
|
||||
uuid = fields.Char('UUID', size=50, index=True, default=lambda self: str(uuid4()), copy=False)
|
||||
email_send = fields.Boolean('Send messages by email', default=False)
|
||||
# multi users channel
|
||||
# depends=['...'] is for `test_mail/tests/common.py`, class Moderation, `setUpClass`
|
||||
image_128 = fields.Image("Image", max_width=128, max_height=128, default=_get_default_image)
|
||||
# members (depends=['...'] is for `test_mail/tests/common.py`, class Moderation, `setUpClass`)
|
||||
channel_last_seen_partner_ids = fields.One2many('mail.channel.partner', 'channel_id', string='Last Seen', depends=['channel_partner_ids'])
|
||||
channel_partner_ids = fields.Many2many('res.partner', 'mail_channel_partner', 'channel_id', 'partner_id', string='Listeners', depends=['channel_last_seen_partner_ids'])
|
||||
channel_message_ids = fields.Many2many('mail.message', 'mail_message_mail_channel_rel')
|
||||
is_member = fields.Boolean('Is a member', compute='_compute_is_member')
|
||||
# access
|
||||
public = fields.Selection([
|
||||
('public', 'Everyone'),
|
||||
('private', 'Invited people only'),
|
||||
('groups', 'Selected group of users')],
|
||||
'Privacy', required=True, default='groups',
|
||||
help='This group is visible by non members. Invisible groups can add members through the invite button.')
|
||||
group_public_id = fields.Many2one('res.groups', string='Authorized Group',
|
||||
default=lambda self: self.env.ref('base.group_user'))
|
||||
is_subscribed = fields.Boolean(
|
||||
'Is Subscribed', compute='_compute_is_subscribed')
|
||||
group_ids = fields.Many2many(
|
||||
'res.groups', string='Auto Subscription',
|
||||
help="Members of those groups will automatically added as followers. "
|
||||
"Note that they will be able to manage their subscription manually "
|
||||
"if necessary.")
|
||||
image_128 = fields.Image("Image", max_width=128, max_height=128, default=_get_default_image)
|
||||
is_subscribed = fields.Boolean(
|
||||
'Is Subscribed', compute='_compute_is_subscribed')
|
||||
# access
|
||||
uuid = fields.Char('UUID', size=50, index=True, default=lambda self: str(uuid4()), copy=False)
|
||||
public = fields.Selection([
|
||||
('public', 'Everyone'),
|
||||
('private', 'Invited people only'),
|
||||
('groups', 'Selected group of users')], string='Privacy',
|
||||
required=True, default='groups',
|
||||
help='This group is visible by non members. Invisible groups can add members through the invite button.')
|
||||
group_public_id = fields.Many2one('res.groups', string='Authorized Group',
|
||||
default=lambda self: self.env.ref('base.group_user'))
|
||||
# moderation
|
||||
moderation = fields.Boolean(string='Moderate this channel')
|
||||
moderator_ids = fields.Many2many('res.users', 'mail_channel_moderator_rel', string='Moderators')
|
||||
@@ -142,6 +85,22 @@ class Channel(models.Model):
|
||||
moderation_guidelines = fields.Boolean(string="Send guidelines to new subscribers", help="Newcomers on this moderated channel will automatically receive the guidelines.")
|
||||
moderation_guidelines_msg = fields.Text(string="Guidelines")
|
||||
|
||||
# COMPUTE / INVERSE
|
||||
|
||||
@api.depends('channel_type')
|
||||
def _compute_is_chat(self):
|
||||
for record in self:
|
||||
record.is_chat = record.channel_type == 'chat'
|
||||
|
||||
def _compute_is_member(self):
|
||||
memberships = self.env['mail.channel.partner'].sudo().search([
|
||||
('channel_id', 'in', self.ids),
|
||||
('partner_id', '=', self.env.user.partner_id.id),
|
||||
])
|
||||
membership_ids = memberships.mapped('channel_id')
|
||||
for record in self:
|
||||
record.is_member = record in membership_ids
|
||||
|
||||
@api.depends('channel_partner_ids')
|
||||
def _compute_is_subscribed(self):
|
||||
for channel in self:
|
||||
@@ -159,6 +118,8 @@ class Channel(models.Model):
|
||||
for channel in self:
|
||||
channel.moderation_count = data.get(channel.id, 0)
|
||||
|
||||
# CONSTRAINTS
|
||||
|
||||
@api.constrains('moderator_ids')
|
||||
def _check_moderator_email(self):
|
||||
if any(not moderator.email for channel in self for moderator in channel.moderator_ids):
|
||||
@@ -184,19 +145,7 @@ class Channel(models.Model):
|
||||
if any(not channel.moderator_ids for channel in self if channel.moderation):
|
||||
raise ValidationError(_('Moderated channels must have moderators.'))
|
||||
|
||||
def _compute_is_member(self):
|
||||
memberships = self.env['mail.channel.partner'].sudo().search([
|
||||
('channel_id', 'in', self.ids),
|
||||
('partner_id', '=', self.env.user.partner_id.id),
|
||||
])
|
||||
membership_ids = memberships.mapped('channel_id')
|
||||
for record in self:
|
||||
record.is_member = record in membership_ids
|
||||
|
||||
@api.depends('channel_type')
|
||||
def _compute_is_chat(self):
|
||||
for record in self:
|
||||
record.is_chat = record.channel_type == 'chat'
|
||||
# ONCHANGE
|
||||
|
||||
@api.onchange('public')
|
||||
def _onchange_public(self):
|
||||
@@ -226,6 +175,10 @@ class Channel(models.Model):
|
||||
else:
|
||||
self.moderator_ids |= self.env.user
|
||||
|
||||
# ------------------------------------------------------------
|
||||
# CRUD
|
||||
# ------------------------------------------------------------
|
||||
|
||||
@api.model_create_multi
|
||||
def create(self, vals_list):
|
||||
defaults = self.default_get(['image_128'])
|
||||
@@ -305,12 +258,14 @@ class Channel(models.Model):
|
||||
|
||||
return result
|
||||
|
||||
def _alias_get_creation_values(self):
|
||||
values = super(Channel, self)._alias_get_creation_values()
|
||||
values['alias_model_id'] = self.env['ir.model']._get('mail.channel').id
|
||||
if self.id:
|
||||
values['alias_force_thread_id'] = self.id
|
||||
return values
|
||||
def init(self):
|
||||
self._cr.execute('SELECT indexname FROM pg_indexes WHERE indexname = %s', ('mail_channel_partner_seen_message_id_idx',))
|
||||
if not self._cr.fetchone():
|
||||
self._cr.execute('CREATE INDEX mail_channel_partner_seen_message_id_idx ON mail_channel_partner (channel_id,partner_id,seen_message_id)')
|
||||
|
||||
# ------------------------------------------------------------
|
||||
# MEMBERS MANAGEMENT
|
||||
# ------------------------------------------------------------
|
||||
|
||||
def _subscribe_users(self):
|
||||
to_create = []
|
||||
@@ -371,6 +326,85 @@ class Channel(models.Model):
|
||||
]).unlink()
|
||||
self.invalidate_cache(fnames=['channel_partner_ids', 'channel_last_seen_partner_ids'])
|
||||
|
||||
def channel_invite(self, partner_ids):
|
||||
""" Add the given partner_ids to the current channels and broadcast the channel header to them.
|
||||
:param partner_ids : list of partner id to add
|
||||
"""
|
||||
partners = self.env['res.partner'].browse(partner_ids)
|
||||
self._invite_check_access(partners)
|
||||
|
||||
# add the partner
|
||||
for channel in self:
|
||||
partners_to_add = partners - channel.channel_partner_ids
|
||||
channel.write({'channel_last_seen_partner_ids': [Command.create({'partner_id': partner_id}) for partner_id in partners_to_add.ids]})
|
||||
for partner in partners_to_add:
|
||||
if partner.id != self.env.user.partner_id.id:
|
||||
notification = _('<div class="o_mail_notification">%(author)s invited %(new_partner)s to <a href="#" class="o_channel_redirect" data-oe-id="%(channel_id)s">#%(channel_name)s</a></div>') % {
|
||||
'author': self.env.user.display_name,
|
||||
'new_partner': partner.display_name,
|
||||
'channel_id': channel.id,
|
||||
'channel_name': channel.name,
|
||||
}
|
||||
else:
|
||||
notification = _('<div class="o_mail_notification">joined <a href="#" class="o_channel_redirect" data-oe-id="%s">#%s</a></div>') % (channel.id, channel.name,)
|
||||
self.message_post(body=notification, message_type="notification", subtype_xmlid="mail.mt_comment", author_id=partner.id, notify_by_email=False)
|
||||
|
||||
# broadcast the channel header to the added partner
|
||||
self._broadcast(partner_ids)
|
||||
|
||||
def _invite_check_access(self, partners):
|
||||
""" Check invited partners could match channel access """
|
||||
failed = []
|
||||
if any(channel.public == 'groups' for channel in self):
|
||||
for channel in self.filtered(lambda c: c.public == 'groups'):
|
||||
invalid_partners = [partner for partner in partners if channel.group_public_id not in partner.mapped('user_ids.groups_id')]
|
||||
failed += [(channel, partner) for partner in invalid_partners]
|
||||
|
||||
if failed:
|
||||
raise UserError(
|
||||
_('Following invites are invalid as user groups do not match: %s') %
|
||||
', '.join('%s (channel %s)' % (partner.name, channel.name) for channel, partner in failed)
|
||||
)
|
||||
|
||||
def _can_invite(self, partner_id):
|
||||
"""Return True if the current user can invite the partner to the channel.
|
||||
|
||||
* public: ok;
|
||||
* private: must be member;
|
||||
* group: both current user and target must have group;
|
||||
|
||||
:return boolean: whether inviting is ok"""
|
||||
partner = self.env['res.partner'].browse(partner_id)
|
||||
|
||||
for channel in self.sudo():
|
||||
if channel.public == 'private' and not channel.is_member:
|
||||
return False
|
||||
if channel.public == 'groups':
|
||||
if not partner.user_ids or channel.group_public_id not in partner.user_ids.groups_id:
|
||||
return False
|
||||
if channel.group_public_id not in self.env.user.groups_id:
|
||||
return False
|
||||
return True
|
||||
|
||||
# ------------------------------------------------------------
|
||||
# MAILING
|
||||
# ------------------------------------------------------------
|
||||
|
||||
def _alias_get_creation_values(self):
|
||||
values = super(Channel, self)._alias_get_creation_values()
|
||||
values['alias_model_id'] = self.env['ir.model']._get('mail.channel').id
|
||||
if self.id:
|
||||
values['alias_force_thread_id'] = self.id
|
||||
return values
|
||||
|
||||
def _alias_get_error_message(self, message, message_dict, alias):
|
||||
if alias.alias_contact == 'followers' and self.ids:
|
||||
author = self.env['res.partner'].browse(message_dict.get('author_id', False))
|
||||
if not author or author not in self.channel_partner_ids:
|
||||
return _('restricted to channel members')
|
||||
return False
|
||||
return super(Channel, self)._alias_get_error_message(message, message_dict, alias)
|
||||
|
||||
def _notify_get_groups(self, msg_vals=None):
|
||||
""" All recipients of a message on a channel are considered as partners.
|
||||
This means they will receive a minimal email, without a link to access
|
||||
@@ -397,13 +431,6 @@ class Channel(models.Model):
|
||||
headers['X-Forge-To'] = list_to
|
||||
return headers
|
||||
|
||||
def _message_receive_bounce(self, email, partner):
|
||||
""" Override bounce management to unsubscribe bouncing addresses """
|
||||
for p in partner:
|
||||
if p.message_bounce >= self.MAX_BOUNCE_LIMIT:
|
||||
self._action_unfollow(p)
|
||||
return super(Channel, self)._message_receive_bounce(email, partner)
|
||||
|
||||
def _notify_email_recipient_values(self, recipient_ids):
|
||||
# Excluded Blacklisted
|
||||
whitelist = self.env['res.partner'].sudo().browse(recipient_ids).filtered(lambda p: not p.is_blacklisted)
|
||||
@@ -415,32 +442,19 @@ class Channel(models.Model):
|
||||
}
|
||||
return super(Channel, self)._notify_email_recipient_values(whitelist.ids)
|
||||
|
||||
def _extract_moderation_values(self, message_type, **kwargs):
|
||||
""" This method is used to compute moderation status before the creation
|
||||
of a message. For this operation the message's author email address is required.
|
||||
This address is returned with status for other computations. """
|
||||
moderation_status = 'accepted'
|
||||
email = ''
|
||||
if self.moderation and message_type in ['email', 'comment']:
|
||||
author_id = kwargs.get('author_id')
|
||||
if author_id and isinstance(author_id, int):
|
||||
email = self.env['res.partner'].browse([author_id]).email
|
||||
elif author_id:
|
||||
email = author_id.email
|
||||
elif kwargs.get('email_from'):
|
||||
email = tools.email_split(kwargs['email_from'])[0]
|
||||
else:
|
||||
email = self.env.user.email
|
||||
if email in self.mapped('moderator_ids.email'):
|
||||
return moderation_status, email
|
||||
status = self.env['mail.moderation'].sudo().search([('email', '=', email), ('channel_id', 'in', self.ids)]).mapped('status')
|
||||
if status and status[0] == 'allow':
|
||||
moderation_status = 'accepted'
|
||||
elif status and status[0] == 'ban':
|
||||
moderation_status = 'rejected'
|
||||
else:
|
||||
moderation_status = 'pending_moderation'
|
||||
return moderation_status, email
|
||||
def _notify_thread(self, message, msg_vals=False, **kwargs):
|
||||
# When posting a message on a mail channel, manage moderation and postpone notify users
|
||||
if not msg_vals or msg_vals.get('moderation_status') != 'pending_moderation':
|
||||
super(Channel, self)._notify_thread(message, msg_vals=msg_vals, **kwargs)
|
||||
else:
|
||||
message._notify_pending_by_chat()
|
||||
|
||||
def _message_receive_bounce(self, email, partner):
|
||||
""" Override bounce management to unsubscribe bouncing addresses """
|
||||
for p in partner:
|
||||
if p.message_bounce >= self.MAX_BOUNCE_LIMIT:
|
||||
self._action_unfollow(p)
|
||||
return super(Channel, self)._message_receive_bounce(email, partner)
|
||||
|
||||
@api.returns('mail.message', lambda value: value.id)
|
||||
def message_post(self, *, message_type='notification', **kwargs):
|
||||
@@ -473,22 +487,36 @@ class Channel(models.Model):
|
||||
self._set_last_seen_message(message)
|
||||
return super()._message_post_after_hook(message=message, msg_vals=msg_vals)
|
||||
|
||||
def _alias_get_error_message(self, message, message_dict, alias):
|
||||
if alias.alias_contact == 'followers' and self.ids:
|
||||
author = self.env['res.partner'].browse(message_dict.get('author_id', False))
|
||||
if not author or author not in self.channel_partner_ids:
|
||||
return _('restricted to channel members')
|
||||
return False
|
||||
return super(Channel, self)._alias_get_error_message(message, message_dict, alias)
|
||||
def _extract_moderation_values(self, message_type, **kwargs):
|
||||
""" This method is used to compute moderation status before the creation
|
||||
of a message. For this operation the message's author email address is required.
|
||||
This address is returned with status for other computations. """
|
||||
moderation_status = 'accepted'
|
||||
email = ''
|
||||
if self.moderation and message_type in ['email', 'comment']:
|
||||
author_id = kwargs.get('author_id')
|
||||
if author_id and isinstance(author_id, int):
|
||||
email = self.env['res.partner'].browse([author_id]).email
|
||||
elif author_id:
|
||||
email = author_id.email
|
||||
elif kwargs.get('email_from'):
|
||||
email = tools.email_split(kwargs['email_from'])[0]
|
||||
else:
|
||||
email = self.env.user.email
|
||||
if email in self.mapped('moderator_ids.email'):
|
||||
return moderation_status, email
|
||||
status = self.env['mail.moderation'].sudo().search([('email', '=', email), ('channel_id', 'in', self.ids)]).mapped('status')
|
||||
if status and status[0] == 'allow':
|
||||
moderation_status = 'accepted'
|
||||
elif status and status[0] == 'ban':
|
||||
moderation_status = 'rejected'
|
||||
else:
|
||||
moderation_status = 'pending_moderation'
|
||||
return moderation_status, email
|
||||
|
||||
def init(self):
|
||||
self._cr.execute('SELECT indexname FROM pg_indexes WHERE indexname = %s', ('mail_channel_partner_seen_message_id_idx',))
|
||||
if not self._cr.fetchone():
|
||||
self._cr.execute('CREATE INDEX mail_channel_partner_seen_message_id_idx ON mail_channel_partner (channel_id,partner_id,seen_message_id)')
|
||||
|
||||
# --------------------------------------------------
|
||||
# Moderation
|
||||
# --------------------------------------------------
|
||||
# ------------------------------------------------------------
|
||||
# MODERATION
|
||||
# ------------------------------------------------------------
|
||||
|
||||
def send_guidelines(self):
|
||||
""" Send guidelines to all channel members. """
|
||||
@@ -540,14 +568,9 @@ class Channel(models.Model):
|
||||
cmds += [Command.create({'email': email, 'status': status}) for email in not_moderated]
|
||||
return self.write({'moderation_ids': cmds})
|
||||
|
||||
#------------------------------------------------------
|
||||
# Instant Messaging API
|
||||
#------------------------------------------------------
|
||||
# A channel header should be broadcasted:
|
||||
# - when adding user to channel (only to the new added partners)
|
||||
# - when folding/minimizing a channel (only to the user making the action)
|
||||
# A message should be broadcasted:
|
||||
# - when a message is posted on a channel (to the channel, using _notify() method)
|
||||
# ------------------------------------------------------------
|
||||
# BROADCAST
|
||||
# ------------------------------------------------------------
|
||||
|
||||
# Anonymous method
|
||||
def _broadcast(self, partner_ids):
|
||||
@@ -573,13 +596,6 @@ class Channel(models.Model):
|
||||
notifications.append([(self._cr.dbname, 'res.partner', partner.id), channel_info])
|
||||
return notifications
|
||||
|
||||
def _notify_thread(self, message, msg_vals=False, **kwargs):
|
||||
# When posting a message on a mail channel, manage moderation and postpone notify users
|
||||
if not msg_vals or msg_vals.get('moderation_status') != 'pending_moderation':
|
||||
super(Channel, self)._notify_thread(message, msg_vals=msg_vals, **kwargs)
|
||||
else:
|
||||
message._notify_pending_by_chat()
|
||||
|
||||
def _channel_message_notifications(self, message, message_format=False):
|
||||
""" Generate the bus notifications for the given message
|
||||
:param message : the mail.message to sent
|
||||
@@ -594,8 +610,18 @@ class Channel(models.Model):
|
||||
notifications.append([channel.uuid, dict(message_format)])
|
||||
return notifications
|
||||
|
||||
# ------------------------------------------------------------
|
||||
# INSTANT MESSAGING API
|
||||
# ------------------------------------------------------------
|
||||
# A channel header should be broadcasted:
|
||||
# - when adding user to channel (only to the new added partners)
|
||||
# - when folding/minimizing a channel (only to the user making the action)
|
||||
# A message should be broadcasted:
|
||||
# - when a message is posted on a channel (to the channel, using _notify() method)
|
||||
# ------------------------------------------------------------
|
||||
|
||||
@api.model
|
||||
def partner_info(self, all_partners, direct_partners):
|
||||
def _get_channel_partner_info(self, all_partners, direct_partners):
|
||||
"""
|
||||
Return the information needed by channel to display channel members
|
||||
:param all_partners: list of res.parner():
|
||||
@@ -628,7 +654,7 @@ class Channel(models.Model):
|
||||
all_partners = all_partner_channel.mapped('partner_id')
|
||||
direct_channel_partners = all_partner_channel.filtered(lambda pc: channel_dict[pc.channel_id.id].channel_type == 'chat')
|
||||
direct_partners = direct_channel_partners.mapped('partner_id')
|
||||
partner_infos = self.partner_info(all_partners, direct_partners)
|
||||
partner_infos = self._get_channel_partner_info(all_partners, direct_partners)
|
||||
|
||||
# add last message preview (only used in mobile)
|
||||
addPreview = self._context.get('isMobile', False)
|
||||
@@ -893,66 +919,6 @@ class Channel(models.Model):
|
||||
}
|
||||
self.env['bus.bus'].sendmany([[(self._cr.dbname, 'mail.channel', channel.id), data]])
|
||||
|
||||
def channel_invite(self, partner_ids):
|
||||
""" Add the given partner_ids to the current channels and broadcast the channel header to them.
|
||||
:param partner_ids : list of partner id to add
|
||||
"""
|
||||
partners = self.env['res.partner'].browse(partner_ids)
|
||||
self._invite_check_access(partners)
|
||||
|
||||
# add the partner
|
||||
for channel in self:
|
||||
partners_to_add = partners - channel.channel_partner_ids
|
||||
channel.write({'channel_last_seen_partner_ids': [Command.create({'partner_id': partner_id}) for partner_id in partners_to_add.ids]})
|
||||
for partner in partners_to_add:
|
||||
if partner.id != self.env.user.partner_id.id:
|
||||
notification = _('<div class="o_mail_notification">%(author)s invited %(new_partner)s to <a href="#" class="o_channel_redirect" data-oe-id="%(channel_id)s">#%(channel_name)s</a></div>') % {
|
||||
'author': self.env.user.display_name,
|
||||
'new_partner': partner.display_name,
|
||||
'channel_id': channel.id,
|
||||
'channel_name': channel.name,
|
||||
}
|
||||
else:
|
||||
notification = _('<div class="o_mail_notification">joined <a href="#" class="o_channel_redirect" data-oe-id="%s">#%s</a></div>') % (channel.id, channel.name,)
|
||||
self.message_post(body=notification, message_type="notification", subtype_xmlid="mail.mt_comment", author_id=partner.id, notify_by_email=False)
|
||||
|
||||
# broadcast the channel header to the added partner
|
||||
self._broadcast(partner_ids)
|
||||
|
||||
def _invite_check_access(self, partners):
|
||||
""" Check invited partners could match channel access """
|
||||
failed = []
|
||||
if any(channel.public == 'groups' for channel in self):
|
||||
for channel in self.filtered(lambda c: c.public == 'groups'):
|
||||
invalid_partners = [partner for partner in partners if channel.group_public_id not in partner.mapped('user_ids.groups_id')]
|
||||
failed += [(channel, partner) for partner in invalid_partners]
|
||||
|
||||
if failed:
|
||||
raise UserError(
|
||||
_('Following invites are invalid as user groups do not match: %s') %
|
||||
', '.join('%s (channel %s)' % (partner.name, channel.name) for channel, partner in failed)
|
||||
)
|
||||
|
||||
def _can_invite(self, partner_id):
|
||||
"""Return True if the current user can invite the partner to the channel.
|
||||
|
||||
* public: ok;
|
||||
* private: must be member;
|
||||
* group: both current user and target must have group;
|
||||
|
||||
:return boolean: whether inviting is ok"""
|
||||
partner = self.env['res.partner'].browse(partner_id)
|
||||
|
||||
for channel in self.sudo():
|
||||
if channel.public == 'private' and not channel.is_member:
|
||||
return False
|
||||
if channel.public == 'groups':
|
||||
if not partner.user_ids or channel.group_public_id not in partner.user_ids.groups_id:
|
||||
return False
|
||||
if channel.group_public_id not in self.env.user.groups_id:
|
||||
return False
|
||||
return True
|
||||
|
||||
@api.model
|
||||
def channel_set_custom_name(self, channel_id, name=False):
|
||||
domain = [('partner_id', '=', self.env.user.partner_id.id), ('channel_id.id', '=', channel_id)]
|
||||
@@ -977,9 +943,10 @@ class Channel(models.Model):
|
||||
notifications.append([channel.uuid, data]) # notify frontend users
|
||||
self.env['bus.bus'].sendmany(notifications)
|
||||
|
||||
#------------------------------------------------------
|
||||
# Instant Messaging View Specific (Slack Client Action)
|
||||
#------------------------------------------------------
|
||||
# ------------------------------------------------------------
|
||||
# IM VIEW SPECIFIC (Slack Client Action)
|
||||
# ------------------------------------------------------------
|
||||
|
||||
@api.model
|
||||
def channel_fetch_slot(self):
|
||||
""" Return the channels of the user grouped by 'slot' (channel, direct_message or private_group), and
|
||||
@@ -1106,9 +1073,10 @@ class Channel(models.Model):
|
||||
""", (tuple(self.ids),))
|
||||
return self.env.cr.dictfetchall()
|
||||
|
||||
#------------------------------------------------------
|
||||
# Commands
|
||||
#------------------------------------------------------
|
||||
# ------------------------------------------------------------
|
||||
# COMMANDS
|
||||
# ------------------------------------------------------------
|
||||
|
||||
@api.model
|
||||
@ormcache()
|
||||
def get_mention_commands(self):
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from odoo import api, fields, models, _
|
||||
from odoo.exceptions import AccessError
|
||||
|
||||
|
||||
class ChannelPartner(models.Model):
|
||||
_name = 'mail.channel.partner'
|
||||
_description = 'Listeners of a Channel'
|
||||
_table = 'mail_channel_partner'
|
||||
_rec_name = 'partner_id'
|
||||
|
||||
custom_channel_name = fields.Char('Custom channel name')
|
||||
partner_id = fields.Many2one('res.partner', string='Recipient', ondelete='cascade')
|
||||
partner_email = fields.Char('Email', related='partner_id.email', readonly=False)
|
||||
channel_id = fields.Many2one('mail.channel', string='Channel', ondelete='cascade')
|
||||
fetched_message_id = fields.Many2one('mail.message', string='Last Fetched')
|
||||
seen_message_id = fields.Many2one('mail.message', string='Last Seen')
|
||||
fold_state = fields.Selection([('open', 'Open'), ('folded', 'Folded'), ('closed', 'Closed')], string='Conversation Fold State', default='open')
|
||||
is_minimized = fields.Boolean("Conversation is minimized")
|
||||
is_pinned = fields.Boolean("Is pinned on the interface", default=True)
|
||||
|
||||
@api.model_create_multi
|
||||
def create(self, vals_list):
|
||||
"""Similar access rule as the access rule of the mail channel.
|
||||
|
||||
It can not be implemented in XML, because when the record will be created, the
|
||||
partner will be added in the channel and the security rule will always authorize
|
||||
the creation.
|
||||
"""
|
||||
if not self.env.is_admin():
|
||||
for vals in vals_list:
|
||||
if 'channel_id' in vals:
|
||||
channel_id = self.env['mail.channel'].browse(vals['channel_id'])
|
||||
if not channel_id._can_invite(vals.get('partner_id')):
|
||||
raise AccessError(_('This user can not be added in this channel'))
|
||||
return super(ChannelPartner, self).create(vals_list)
|
||||
|
||||
def write(self, vals):
|
||||
if not self.env.is_admin():
|
||||
if {'channel_id', 'partner_id', 'partner_email'} & set(vals):
|
||||
raise AccessError(_('You can not write on this field'))
|
||||
return super(ChannelPartner, self).write(vals)
|
||||
@@ -0,0 +1,20 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from odoo import fields, models
|
||||
|
||||
|
||||
class Moderation(models.Model):
|
||||
_name = 'mail.moderation'
|
||||
_description = 'Channel black/white list'
|
||||
|
||||
email = fields.Char(string="Email", index=True, required=True)
|
||||
status = fields.Selection([
|
||||
('allow', 'Always Allow'),
|
||||
('ban', 'Permanent Ban')],
|
||||
string="Status", required=True)
|
||||
channel_id = fields.Many2one('mail.channel', string="Channel", index=True, required=True)
|
||||
|
||||
_sql_constraints = [
|
||||
('channel_email_uniq', 'unique (email,channel_id)', 'The email address must be unique per channel !')
|
||||
]
|
||||
@@ -1028,7 +1028,7 @@ MockServer.include({
|
||||
this._widget.call('bus_service', 'trigger', 'notification', notifications);
|
||||
},
|
||||
/**
|
||||
* Simulates `partner_info` on `mail.channel`.
|
||||
* Simulates `_get_channel_partner_info` on `mail.channel`.
|
||||
*
|
||||
* @private
|
||||
* @param {integer[]} all_partners
|
||||
|
||||
@@ -85,12 +85,6 @@
|
||||
<field name="view_mode">tree,form</field>
|
||||
</record>
|
||||
|
||||
<menuitem
|
||||
id="menu_mail_activity_type"
|
||||
action="mail_activity_type_action"
|
||||
parent="mail.mail_menu_technical"
|
||||
sequence="10"
|
||||
/>
|
||||
|
||||
<record id="mail_activity_action" model="ir.actions.act_window">
|
||||
<field name="name">Activities</field>
|
||||
@@ -98,13 +92,6 @@
|
||||
<field name="view_mode">tree,form</field>
|
||||
</record>
|
||||
|
||||
<menuitem
|
||||
id="menu_mail_activities"
|
||||
action="mail_activity_action"
|
||||
parent="mail.mail_menu_technical"
|
||||
sequence="11"
|
||||
/>
|
||||
|
||||
<record id="mail_activity_view_form_popup" model="ir.ui.view">
|
||||
<field name="name">mail.activity.view.form.popup</field>
|
||||
<field name="model">mail.activity</field>
|
||||
|
||||
@@ -78,11 +78,5 @@
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<menuitem id="mail_alias_menu"
|
||||
parent="base.menu_email"
|
||||
action="action_view_mail_alias"
|
||||
sequence="11"
|
||||
groups="base.group_no_one"/>
|
||||
|
||||
</data>
|
||||
</odoo>
|
||||
|
||||
@@ -67,11 +67,4 @@
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<!-- Technical Menu -->
|
||||
<menuitem id="mail_blacklist_menu"
|
||||
name="Email Blacklist"
|
||||
action="mail_blacklist_action"
|
||||
parent="mail.mail_menu_technical"
|
||||
sequence="22"/>
|
||||
|
||||
</odoo>
|
||||
|
||||
@@ -0,0 +1,39 @@
|
||||
<?xml version="1.0"?>
|
||||
<odoo><data>
|
||||
<!-- mail.channel.partner -->
|
||||
<record id="mail_channel_partner_view_tree" model="ir.ui.view">
|
||||
<field name="name">mail.channel.partner.tree</field>
|
||||
<field name="model">mail.channel.partner</field>
|
||||
<field name="priority">10</field>
|
||||
<field name="arch" type="xml">
|
||||
<tree string="Channels">
|
||||
<field name="partner_id"/>
|
||||
<field name="channel_id"/>
|
||||
<field name="seen_message_id"/>
|
||||
</tree>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="mail_channel_partner_view_form" model="ir.ui.view">
|
||||
<field name="name">mail.channel.partner.form</field>
|
||||
<field name="model">mail.channel.partner</field>
|
||||
<field name="arch" type="xml">
|
||||
<form string="Channel">
|
||||
<sheet>
|
||||
<group>
|
||||
<field name="partner_id"/>
|
||||
<field name="channel_id"/>
|
||||
<field name="seen_message_id"/>
|
||||
</group>
|
||||
</sheet>
|
||||
</form>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="mail_channel_partner_action" model="ir.actions.act_window">
|
||||
<field name="name">Channels/Partner</field>
|
||||
<field name="res_model">mail.channel.partner</field>
|
||||
<field name="view_mode">tree,form</field>
|
||||
</record>
|
||||
|
||||
</data></odoo>
|
||||
@@ -1,50 +1,6 @@
|
||||
<?xml version="1.0"?>
|
||||
<odoo>
|
||||
<data>
|
||||
|
||||
<!-- mail.channel.partner -->
|
||||
<record id="mail_channel_partner_view_tree" model="ir.ui.view">
|
||||
<field name="name">mail.channel.partner.tree</field>
|
||||
<field name="model">mail.channel.partner</field>
|
||||
<field name="priority">10</field>
|
||||
<field name="arch" type="xml">
|
||||
<tree string="Channels">
|
||||
<field name="partner_id"/>
|
||||
<field name="channel_id"/>
|
||||
<field name="seen_message_id"/>
|
||||
</tree>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="mail_channel_partner_view_form" model="ir.ui.view">
|
||||
<field name="name">mail.channel.partner.form</field>
|
||||
<field name="model">mail.channel.partner</field>
|
||||
<field name="arch" type="xml">
|
||||
<form string="Channel">
|
||||
<sheet>
|
||||
<group>
|
||||
<field name="partner_id"/>
|
||||
<field name="channel_id"/>
|
||||
<field name="seen_message_id"/>
|
||||
</group>
|
||||
</sheet>
|
||||
</form>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="mail_channel_partner_action" model="ir.actions.act_window">
|
||||
<field name="name">Channels/Partner</field>
|
||||
<field name="res_model">mail.channel.partner</field>
|
||||
<field name="view_mode">tree,form</field>
|
||||
</record>
|
||||
|
||||
<menuitem name="Channels/Partner"
|
||||
id="mail_channel_partner_menu"
|
||||
parent="base.menu_email"
|
||||
action="mail_channel_partner_action"
|
||||
sequence="21"
|
||||
groups="base.group_no_one"/>
|
||||
|
||||
<!-- mail.channel -->
|
||||
<record id="mail_channel_view_kanban" model="ir.ui.view">
|
||||
<field name="name">mail.channel.kanban</field>
|
||||
@@ -192,11 +148,6 @@
|
||||
<field name="search_view_id" ref="mail_channel_view_search"/>
|
||||
</record>
|
||||
|
||||
<!-- settings !-->
|
||||
<menuitem id="mail_channel_menu_settings" parent="base.menu_email" sequence="20"
|
||||
name="Channels" action="mail_channel_action_view"
|
||||
groups="base.group_no_one"/>
|
||||
|
||||
<record id="action_discuss" model="ir.actions.client">
|
||||
<field name="name">Discuss</field>
|
||||
<field name="tag">mail.widgets.discuss</field>
|
||||
@@ -204,24 +155,5 @@
|
||||
<field name="params" eval=""{ 'default_active_id': 'mail.box_inbox' }""/>
|
||||
</record>
|
||||
|
||||
<menuitem
|
||||
id="mail.menu_root_discuss"
|
||||
name="Discuss"
|
||||
action="action_discuss"
|
||||
web_icon="mail,static/description/icon.png"
|
||||
groups="base.group_user"
|
||||
sequence="1"
|
||||
/>
|
||||
|
||||
<!--
|
||||
This menuitem will be activated by integrations modules (like github, twitter, ...). It
|
||||
is a hook to ease other modules to plug into mail.
|
||||
-->
|
||||
<record id="mail.mail_channel_integrations_menu" model="ir.ui.menu">
|
||||
<field name="name">Integrations</field>
|
||||
<field name="parent_id" ref="mail.menu_root_discuss"></field>
|
||||
<field name="active" eval="False"></field>
|
||||
</record>
|
||||
|
||||
</data>
|
||||
</odoo>
|
||||
|
||||
@@ -45,13 +45,5 @@
|
||||
<field name="view_mode">tree,form</field>
|
||||
</record>
|
||||
|
||||
<!-- Add followers related menu entries in Settings/Email -->
|
||||
<menuitem name="Followers"
|
||||
id="menu_email_followers"
|
||||
parent="mail.mail_menu_technical"
|
||||
action="action_view_followers"
|
||||
sequence="21"
|
||||
groups="base.group_no_one"/>
|
||||
|
||||
</data>
|
||||
</odoo>
|
||||
|
||||
@@ -123,11 +123,5 @@
|
||||
<field name="search_view_id" ref="view_mail_search"/>
|
||||
</record>
|
||||
|
||||
<!-- Add menu entry in Settings/Email -->
|
||||
<menuitem name="Emails"
|
||||
id="menu_mail_mail"
|
||||
parent="base.menu_email"
|
||||
action="action_view_mail_mail"
|
||||
sequence="1"/>
|
||||
</data>
|
||||
</odoo>
|
||||
|
||||
@@ -1,11 +1,111 @@
|
||||
<?xml version="1.0"?>
|
||||
<odoo><data>
|
||||
<menuitem name="Discuss"
|
||||
id="mail.menu_root_discuss"
|
||||
action="action_discuss"
|
||||
web_icon="mail,static/description/icon.png"
|
||||
groups="base.group_user"
|
||||
sequence="1"
|
||||
/>
|
||||
|
||||
<record id="base.menu_email" model="ir.ui.menu">
|
||||
<field name="sequence">3</field>
|
||||
</record>
|
||||
|
||||
<!-- Under Technical/Email -->
|
||||
<menuitem name="Emails"
|
||||
id="menu_mail_mail"
|
||||
parent="base.menu_email"
|
||||
action="action_view_mail_mail"
|
||||
sequence="1"/>
|
||||
<menuitem id="menu_email_templates"
|
||||
parent="base.menu_email"
|
||||
action="action_email_template_tree_all"
|
||||
sequence="10"/>
|
||||
<menuitem id="mail_alias_menu"
|
||||
parent="base.menu_email"
|
||||
action="action_view_mail_alias"
|
||||
sequence="11"
|
||||
groups="base.group_no_one"/>
|
||||
<menuitem id="mail_channel_menu_settings"
|
||||
name="Channels"
|
||||
parent="base.menu_email"
|
||||
action="mail_channel_action_view"
|
||||
sequence="20"
|
||||
groups="base.group_no_one"/>
|
||||
<menuitem name="Channels/Partner"
|
||||
id="mail_channel_partner_menu"
|
||||
parent="base.menu_email"
|
||||
action="mail_channel_partner_action"
|
||||
sequence="21"
|
||||
groups="base.group_no_one"/>
|
||||
<menuitem name="Channel Moderation"
|
||||
id="mail_moderation_menu"
|
||||
parent="base.menu_email"
|
||||
sequence="22"
|
||||
action="mail_moderation_action"/>
|
||||
|
||||
<!-- Under Technical/Discuss -->
|
||||
<menuitem name="Discuss"
|
||||
id="mail_menu_technical"
|
||||
parent="base.menu_custom"
|
||||
sequence="1"/>
|
||||
|
||||
<menuitem name="Messages"
|
||||
id="menu_mail_message"
|
||||
parent="mail.mail_menu_technical"
|
||||
action="action_view_mail_message"
|
||||
sequence="1"/>
|
||||
<menuitem name="Subtypes"
|
||||
id="menu_message_subtype"
|
||||
parent="mail.mail_menu_technical"
|
||||
action="action_view_message_subtype"
|
||||
sequence="4"/>
|
||||
<menuitem name="Tracking Values"
|
||||
id="menu_mail_tracking_value"
|
||||
parent="mail.mail_menu_technical"
|
||||
action="action_view_mail_tracking_value"
|
||||
sequence="5"/>
|
||||
|
||||
<menuitem
|
||||
id="menu_mail_activity_type"
|
||||
action="mail_activity_type_action"
|
||||
parent="mail.mail_menu_technical"
|
||||
sequence="10"
|
||||
/>
|
||||
<menuitem
|
||||
id="menu_mail_activities"
|
||||
action="mail_activity_action"
|
||||
parent="mail.mail_menu_technical"
|
||||
sequence="11"
|
||||
/>
|
||||
|
||||
<menuitem name="Notifications"
|
||||
id="mail_notification_menu"
|
||||
parent="mail.mail_menu_technical"
|
||||
action="mail_notification_action"
|
||||
sequence="20"
|
||||
groups="base.group_no_one"/>
|
||||
<menuitem name="Followers"
|
||||
id="menu_email_followers"
|
||||
parent="mail.mail_menu_technical"
|
||||
action="action_view_followers"
|
||||
sequence="21"
|
||||
groups="base.group_no_one"/>
|
||||
<menuitem id="mail_blacklist_menu"
|
||||
name="Email Blacklist"
|
||||
action="mail_blacklist_action"
|
||||
parent="mail.mail_menu_technical"
|
||||
sequence="22"/>
|
||||
|
||||
<!--
|
||||
This menuitem will be activated by integrations modules (like github, twitter, ...). It
|
||||
is a hook to ease other modules to plug into mail.
|
||||
-->
|
||||
<record id="mail.mail_channel_integrations_menu" model="ir.ui.menu">
|
||||
<field name="name">Integrations</field>
|
||||
<field name="parent_id" ref="mail.menu_root_discuss"></field>
|
||||
<field name="active" eval="False"></field>
|
||||
</record>
|
||||
|
||||
</data></odoo>
|
||||
|
||||
@@ -47,11 +47,6 @@
|
||||
<field name="res_model">mail.message.subtype</field>
|
||||
<field name="view_mode">tree,form</field>
|
||||
</record>
|
||||
<menuitem name="Subtypes"
|
||||
id="menu_message_subtype"
|
||||
parent="mail.mail_menu_technical"
|
||||
action="action_view_message_subtype"
|
||||
sequence="4"/>
|
||||
|
||||
</data>
|
||||
</odoo>
|
||||
|
||||
@@ -125,13 +125,6 @@
|
||||
<field name="search_view_id" ref="view_message_search"/>
|
||||
</record>
|
||||
|
||||
<!-- Add menu entry in Settings/Email -->
|
||||
<menuitem name="Messages"
|
||||
id="menu_mail_message"
|
||||
parent="mail.mail_menu_technical"
|
||||
action="action_view_mail_message"
|
||||
sequence="1"/>
|
||||
|
||||
<record model="ir.ui.view" id="view_document_file_kanban">
|
||||
<field name="name">ir.attachment kanban</field>
|
||||
<field name="model">ir.attachment</field>
|
||||
|
||||
@@ -42,11 +42,5 @@
|
||||
<field name="context">{'search_default_status_ban': 1}</field>
|
||||
</record>
|
||||
|
||||
<!-- Add menu entry in Settings/Email -->
|
||||
<menuitem name="Channel Moderation"
|
||||
id="mail_moderation_menu"
|
||||
parent="base.menu_email"
|
||||
sequence="22"
|
||||
action="mail_moderation_action"/>
|
||||
</data>
|
||||
</odoo>
|
||||
|
||||
@@ -48,11 +48,4 @@
|
||||
<field name="view_mode">tree,form</field>
|
||||
</record>
|
||||
|
||||
<menuitem name="Notifications"
|
||||
id="mail_notification_menu"
|
||||
parent="mail.mail_menu_technical"
|
||||
action="mail_notification_action"
|
||||
sequence="20"
|
||||
groups="base.group_no_one"/>
|
||||
|
||||
</data></odoo>
|
||||
|
||||
@@ -131,8 +131,5 @@
|
||||
<field name="search_view_id" ref="view_email_template_search"/>
|
||||
</record>
|
||||
|
||||
<menuitem id="menu_email_templates" parent="base.menu_email" action="action_email_template_tree_all"
|
||||
sequence="10"/>
|
||||
|
||||
</data>
|
||||
</odoo>
|
||||
|
||||
@@ -65,11 +65,6 @@
|
||||
<field name="res_model">mail.tracking.value</field>
|
||||
<field name="view_mode">tree,form</field>
|
||||
</record>
|
||||
<menuitem name="Tracking Values"
|
||||
id="menu_mail_tracking_value"
|
||||
parent="mail.mail_menu_technical"
|
||||
action="action_view_mail_tracking_value"
|
||||
sequence="5"/>
|
||||
|
||||
</data>
|
||||
</odoo>
|
||||
@@ -270,7 +270,7 @@ class TestMailAPIPerformance(BaseMailPerformance):
|
||||
'partner_ids': [(4, customer_id)],
|
||||
})
|
||||
|
||||
with self.assertQueryCount(__system__=29, emp=32):
|
||||
with self.assertQueryCount(__system__=25, emp=32):
|
||||
composer.send_mail()
|
||||
|
||||
@users('__system__', 'emp')
|
||||
@@ -290,7 +290,7 @@ class TestMailAPIPerformance(BaseMailPerformance):
|
||||
}).create({})
|
||||
composer.onchange_template_id_wrapper()
|
||||
|
||||
with self.assertQueryCount(__system__=37, emp=40):
|
||||
with self.assertQueryCount(__system__=33, emp=40):
|
||||
composer.send_mail()
|
||||
|
||||
# remove created partner to ensure tests are the same each run
|
||||
@@ -331,7 +331,7 @@ class TestMailAPIPerformance(BaseMailPerformance):
|
||||
def test_message_log_with_post(self):
|
||||
record = self.env['mail.test.simple'].create({'name': 'Test'})
|
||||
|
||||
with self.assertQueryCount(__system__=5, emp=6):
|
||||
with self.assertQueryCount(__system__=3, emp=6):
|
||||
record.message_post(
|
||||
body='<p>Test message_post as log</p>',
|
||||
subtype_xmlid='mail.mt_note',
|
||||
@@ -342,7 +342,7 @@ class TestMailAPIPerformance(BaseMailPerformance):
|
||||
def test_message_post_no_notification(self):
|
||||
record = self.env['mail.test.simple'].create({'name': 'Test'})
|
||||
|
||||
with self.assertQueryCount(__system__=5, emp=6):
|
||||
with self.assertQueryCount(__system__=3, emp=6):
|
||||
record.message_post(
|
||||
body='<p>Test Post Performances basic</p>',
|
||||
partner_ids=[],
|
||||
@@ -355,7 +355,7 @@ class TestMailAPIPerformance(BaseMailPerformance):
|
||||
def test_message_post_one_email_notification(self):
|
||||
record = self.env['mail.test.simple'].create({'name': 'Test'})
|
||||
|
||||
with self.assertQueryCount(__system__=30, emp=31):
|
||||
with self.assertQueryCount(__system__=28, emp=31):
|
||||
record.message_post(
|
||||
body='<p>Test Post Performances with an email ping</p>',
|
||||
partner_ids=self.customer.ids,
|
||||
@@ -367,7 +367,7 @@ class TestMailAPIPerformance(BaseMailPerformance):
|
||||
def test_message_post_one_inbox_notification(self):
|
||||
record = self.env['mail.test.simple'].create({'name': 'Test'})
|
||||
|
||||
with self.assertQueryCount(__system__=14, emp=17):
|
||||
with self.assertQueryCount(__system__=12, emp=17):
|
||||
record.message_post(
|
||||
body='<p>Test Post Performances with an inbox ping</p>',
|
||||
partner_ids=self.user_test.partner_id.ids,
|
||||
@@ -951,7 +951,7 @@ class TestMailHeavyPerformancePost(BaseMailPerformance):
|
||||
]
|
||||
self.attachements = self.env['ir.attachment'].with_user(self.env.user).create(self.vals)
|
||||
attachement_ids = self.attachements.ids
|
||||
with self.assertQueryCount(emp=81):
|
||||
with self.assertQueryCount(emp=79):
|
||||
self.cr.sql_log = self.warm and self.cr.sql_log_count
|
||||
record.with_context({}).message_post(
|
||||
body='<p>Test body <img src="cid:cid1"> <img src="cid:cid2"></p>',
|
||||
|
||||
Reference in New Issue
Block a user