from openerp import SUPERUSER_ID
from openerp.addons.mail.mail_message import decode
from openerp.osv import fields, osv, orm
+from openerp.osv.orm import browse_record, browse_null
from openerp.tools.safe_eval import safe_eval as eval
from openerp.tools.translate import _
new = set(command[2])
# remove partners that are no longer followers
- fol_ids = fol_obj.search(cr, SUPERUSER_ID,
- [('res_model', '=', self._name), ('res_id', '=', id), ('partner_id', 'not in', list(new))])
- fol_obj.unlink(cr, SUPERUSER_ID, fol_ids)
-
+ self.message_unsubscribe(cr, uid, [id], list(old-new), context=context)
# add new followers
- for partner_id in new - old:
- fol_obj.create(cr, SUPERUSER_ID, {'res_model': self._name, 'res_id': id, 'partner_id': partner_id})
+ self.message_subscribe(cr, uid, [id], list(new-old), context=context)
def _search_followers(self, cr, uid, obj, name, args, context):
"""Search function for message_follower_ids
'message_is_follower': fields.function(_get_followers, type='boolean',
fnct_search=_search_is_follower, string='Is a Follower', multi='_get_followers,'),
'message_follower_ids': fields.function(_get_followers, fnct_inv=_set_followers,
- fnct_search=_search_followers, type='many2many',
+ fnct_search=_search_followers, type='many2many', priority=-10,
obj='res.partner', string='Followers', multi='_get_followers'),
'message_ids': fields.one2many('mail.message', 'res_id',
domain=lambda self: [('model', '=', self._name)],
"""
if context is None:
context = {}
- thread_id = super(mail_thread, self).create(cr, uid, values, context=context)
+
+ # subscribe uid unless asked not to
+ if not context.get('mail_create_nosubscribe'):
+ pid = self.pool['res.users'].browse(cr, SUPERUSER_ID, uid).partner_id.id
+ message_follower_ids = values.get('message_follower_ids') or [] # webclient can send None or False
+ message_follower_ids.append([4, pid])
+ values['message_follower_ids'] = message_follower_ids
+ # add operation to ignore access rule checking for subscription
+ context_operation = dict(context, operation='create')
+ else:
+ context_operation = context
+ thread_id = super(mail_thread, self).create(cr, uid, values, context=context_operation)
# automatic logging unless asked not to (mainly for various testing purpose)
if not context.get('mail_create_nolog'):
self.message_post(cr, uid, thread_id, body=_('%s created') % (self._description), context=context)
- # subscribe uid unless asked not to
- if not context.get('mail_create_nosubscribe'):
- self.message_subscribe_users(cr, uid, [thread_id], [uid], context=context)
# auto_subscribe: take values and defaults into account
- create_values = set(values.keys())
+ create_values = dict(values)
for key, val in context.iteritems():
if key.startswith('default_'):
- create_values.add(key[8:])
- self.message_auto_subscribe(cr, uid, [thread_id], list(create_values), context=context)
+ create_values[key[8:]] = val
+ self.message_auto_subscribe(cr, uid, [thread_id], create_values.keys(), context=context, values=create_values)
# track values
- tracked_fields = self._get_tracked_fields(cr, uid, values.keys(), context=context)
- if tracked_fields:
- initial_values = {thread_id: dict((item, False) for item in tracked_fields)}
- self.message_track(cr, uid, [thread_id], tracked_fields, initial_values, context=context)
-
+ track_ctx = dict(context)
+ if 'lang' not in track_ctx:
+ track_ctx['lang'] = self.pool.get('res.users').browse(cr, uid, uid, context=context).lang
+ if not context.get('mail_notrack'):
+ tracked_fields = self._get_tracked_fields(cr, uid, values.keys(), context=track_ctx)
+ if tracked_fields:
+ initial_values = {thread_id: dict((item, False) for item in tracked_fields)}
+ self.message_track(cr, uid, [thread_id], tracked_fields, initial_values, context=track_ctx)
return thread_id
def write(self, cr, uid, ids, values, context=None):
+ if context is None:
+ context = {}
if isinstance(ids, (int, long)):
ids = [ids]
# Track initial values of tracked fields
- tracked_fields = self._get_tracked_fields(cr, uid, values.keys(), context=context)
+ track_ctx = dict(context)
+ if 'lang' not in track_ctx:
+ track_ctx['lang'] = self.pool.get('res.users').browse(cr, uid, uid, context=context).lang
+ tracked_fields = self._get_tracked_fields(cr, uid, values.keys(), context=track_ctx)
if tracked_fields:
- records = self.browse(cr, uid, ids, context=context)
+ records = self.browse(cr, uid, ids, context=track_ctx)
initial_values = dict((this.id, dict((key, getattr(this, key)) for key in tracked_fields.keys())) for this in records)
# Perform write, update followers
result = super(mail_thread, self).write(cr, uid, ids, values, context=context)
- self.message_auto_subscribe(cr, uid, ids, values.keys(), context=context)
+ self.message_auto_subscribe(cr, uid, ids, values.keys(), context=context, values=values)
- # Perform the tracking
+ if not context.get('mail_notrack'):
+ # Perform the tracking
+ tracked_fields = self._get_tracked_fields(cr, uid, values.keys(), context=context)
+ else:
+ tracked_fields = None
if tracked_fields:
- self.message_track(cr, uid, ids, tracked_fields, initial_values, context=context)
+ self.message_track(cr, uid, ids, tracked_fields, initial_values, context=track_ctx)
return result
def unlink(self, cr, uid, ids, context=None):
return res
def copy(self, cr, uid, id, default=None, context=None):
+ # avoid tracking multiple temporary changes during copy
+ context = dict(context or {}, mail_notrack=True)
+
default = default or {}
default['message_ids'] = []
default['message_follower_ids'] = []
return action
if msg_id and not (model and res_id):
msg = self.pool.get('mail.message').browse(cr, uid, msg_id, context=context)
- model, res_id = msg.model, msg.res_id
+ if msg.exists():
+ model, res_id = msg.model, msg.res_id
# if model + res_id found: try to redirect to the document or fallback on the Inbox
if model and res_id:
# Private message: should not contain any thread_id
if not model and thread_id:
if assert_model:
- assert thread_id == 0, 'Routing: posting a message without model should be with a null res_id (private message).'
+ if thread_id:
+ raise ValueError('Routing: posting a message without model should be with a null res_id (private message).')
_warn('posting a message without model should be with a null res_id (private message), resetting thread_id')
thread_id = 0
# Private message: should have a parent_id (only answers)
if not model and not message_dict.get('parent_id'):
if assert_model:
- assert message_dict.get('parent_id'), 'Routing: posting a message without model should be with a parent_id (private mesage).'
+ if not message_dict.get('parent_id'):
+ raise ValueError('Routing: posting a message without model should be with a parent_id (private mesage).')
_warn('posting a message without model should be with a parent_id (private mesage), skipping')
return ()
# New Document: check model accepts the mailgateway
if not thread_id and model and not hasattr(model_pool, 'message_new'):
if assert_model:
- assert hasattr(model_pool, 'message_new'), 'Model %s does not accept document creation, crashing' % model
+ if not hasattr(model_pool, 'message_new'):
+ raise ValueError(
+ 'Model %s does not accept document creation, crashing' % model
+ )
_warn('model %s does not accept document creation, skipping' % model)
return ()
to which this mail should be attached. Only used if the message
does not reply to an existing thread and does not match any mail alias.
:return: list of [model, thread_id, custom_values, user_id, alias]
+
+ :raises: ValueError, TypeError
"""
- assert isinstance(message, Message), 'message must be an email.message.Message at this point'
+ if not isinstance(message, Message):
+ raise TypeError('message must be an email.message.Message at this point')
fallback_model = model
# Get email.message.Message variables for future processing
return [route]
# AssertionError if no routes found and if no bounce occured
- assert False, \
- "No possible route found for incoming message from %s to %s (Message-Id %s:)." \
- "Create an appropriate mail.alias or force the destination model." % (email_from, email_to, message_id)
+ raise ValueError(
+ 'No possible route found for incoming message from %s to %s (Message-Id %s:). '
+ 'Create an appropriate mail.alias or force the destination model.' %
+ (email_from, email_to, message_id)
+ )
def message_route_process(self, cr, uid, message, message_dict, routes, context=None):
# postpone setting message_dict.partner_ids after message_post, to avoid double notifications
context.update({'thread_model': model})
if model:
model_pool = self.pool[model]
- assert thread_id and hasattr(model_pool, 'message_update') or hasattr(model_pool, 'message_new'), \
- "Undeliverable mail with Message-Id %s, model %s does not accept incoming emails" % \
- (message_dict['message_id'], model)
+ if not (thread_id and hasattr(model_pool, 'message_update') or hasattr(model_pool, 'message_new')):
+ raise ValueError(
+ "Undeliverable mail with Message-Id %s, model %s does not accept incoming emails" %
+ (message_dict['message_id'], model)
+ )
# disabled subscriptions during message_new/update to avoid having the system user running the
# email gateway become a follower of all inbound messages
else:
thread_id = model_pool.message_new(cr, user_id, message_dict, custom_values, context=nosub_ctx)
else:
- assert thread_id == 0, "Posting a message without model should be with a null res_id, to create a private message."
+ if thread_id:
+ raise ValueError("Posting a message without model should be with a null res_id, to create a private message.")
model_pool = self.pool.get('mail.thread')
if not hasattr(model_pool, 'message_post'):
context['thread_model'] = model
alternative = True
if part.get_content_maintype() == 'multipart':
continue # skip container
- filename = part.get_filename() # None if normal part
+ # part.get_filename returns decoded value if able to decode, coded otherwise.
+ # original get_filename is not able to decode iso-8859-1 (for instance).
+ # therefore, iso encoded attachements are not able to be decoded properly with get_filename
+ # code here partially copy the original get_filename method, but handle more encoding
+ filename=part.get_param('filename', None, 'content-disposition')
+ if not filename:
+ filename=part.get_param('name', None)
+ if filename:
+ if isinstance(filename, tuple):
+ # RFC2231
+ filename=email.utils.collapse_rfc2231_value(filename).strip()
+ else:
+ filename=decode(filename)
encoding = part.get_content_charset() # None if attachment
# 1) Explicit Attachments -> attachments
if filename or part.get('content-disposition', '').strip().startswith('attachment'):
mail_message_obj.write(cr, SUPERUSER_ID, message_ids, {'author_id': partner_info['partner_id']}, context=context)
return result
+ def _message_preprocess_attachments(self, cr, uid, attachments, attachment_ids, attach_model, attach_res_id, context=None):
+ """ Preprocess attachments for mail_thread.message_post() or mail_mail.create().
+
+ :param list attachments: list of attachment tuples in the form ``(name,content)``,
+ where content is NOT base64 encoded
+ :param list attachment_ids: a list of attachment ids, not in tomany command form
+ :param str attach_model: the model of the attachments parent record
+ :param integer attach_res_id: the id of the attachments parent record
+ """
+ Attachment = self.pool['ir.attachment']
+ m2m_attachment_ids = []
+ if attachment_ids:
+ filtered_attachment_ids = Attachment.search(cr, SUPERUSER_ID, [
+ ('res_model', '=', 'mail.compose.message'),
+ ('create_uid', '=', uid),
+ ('id', 'in', attachment_ids)], context=context)
+ if filtered_attachment_ids:
+ Attachment.write(cr, SUPERUSER_ID, filtered_attachment_ids, {'res_model': attach_model, 'res_id': attach_res_id}, context=context)
+ m2m_attachment_ids += [(4, id) for id in attachment_ids]
+ # Handle attachments parameter, that is a dictionary of attachments
+ for name, content in attachments:
+ if isinstance(content, unicode):
+ content = content.encode('utf-8')
+ data_attach = {
+ 'name': name,
+ 'datas': base64.b64encode(str(content)),
+ 'datas_fname': name,
+ 'description': name,
+ 'res_model': attach_model,
+ 'res_id': attach_res_id,
+ }
+ m2m_attachment_ids.append((0, 0, data_attach))
+ return m2m_attachment_ids
+
def message_post(self, cr, uid, thread_id, body='', subject=None, type='notification',
subtype=None, parent_id=False, attachments=None, context=None,
content_subtype='html', **kwargs):
# 3. Attachments
# - HACK TDE FIXME: Chatter: attachments linked to the document (not done JS-side), load the message
- attachment_ids = kwargs.pop('attachment_ids', []) or [] # because we could receive None (some old code sends None)
- if attachment_ids:
- filtered_attachment_ids = ir_attachment.search(cr, SUPERUSER_ID, [
- ('res_model', '=', 'mail.compose.message'),
- ('create_uid', '=', uid),
- ('id', 'in', attachment_ids)], context=context)
- if filtered_attachment_ids:
- ir_attachment.write(cr, SUPERUSER_ID, filtered_attachment_ids, {'res_model': model, 'res_id': thread_id}, context=context)
- attachment_ids = [(4, id) for id in attachment_ids]
- # Handle attachments parameter, that is a dictionary of attachments
- for name, content in attachments:
- if isinstance(content, unicode):
- content = content.encode('utf-8')
- data_attach = {
- 'name': name,
- 'datas': base64.b64encode(str(content)),
- 'datas_fname': name,
- 'description': name,
- 'res_model': model,
- 'res_id': thread_id,
- }
- attachment_ids.append((0, 0, data_attach))
+ attachment_ids = self._message_preprocess_attachments(cr, uid, attachments, kwargs.pop('attachment_ids', []), model, thread_id, context)
# 4: mail.message.subtype
subtype_id = False
def message_subscribe(self, cr, uid, ids, partner_ids, subtype_ids=None, context=None):
""" Add partners to the records followers. """
+ if context is None:
+ context = {}
+ # not necessary for computation, but saves an access right check
+ if not partner_ids:
+ return True
+
mail_followers_obj = self.pool.get('mail.followers')
subtype_obj = self.pool.get('mail.message.subtype')
if set(partner_ids) == set([user_pid]):
try:
self.check_access_rights(cr, uid, 'read')
+ if context.get('operation', '') == 'create':
+ self.check_access_rule(cr, uid, ids, 'create')
+ else:
+ self.check_access_rule(cr, uid, ids, 'read')
except (osv.except_osv, orm.except_orm):
- return
+ return False
else:
self.check_access_rights(cr, uid, 'write')
+ self.check_access_rule(cr, uid, ids, 'write')
+
+ existing_pids_dict = {}
+ fol_ids = mail_followers_obj.search(cr, SUPERUSER_ID, ['&', '&', ('res_model', '=', self._name), ('res_id', 'in', ids), ('partner_id', 'in', partner_ids)])
+ for fol in mail_followers_obj.browse(cr, SUPERUSER_ID, fol_ids, context=context):
+ existing_pids_dict.setdefault(fol.res_id, set()).add(fol.partner_id.id)
+
+ # subtype_ids specified: update already subscribed partners
+ if subtype_ids and fol_ids:
+ mail_followers_obj.write(cr, SUPERUSER_ID, fol_ids, {'subtype_ids': [(6, 0, subtype_ids)]}, context=context)
+ # subtype_ids not specified: do not update already subscribed partner, fetch default subtypes for new partners
+ if subtype_ids is None:
+ subtype_ids = subtype_obj.search(
+ cr, uid, [
+ ('default', '=', True), '|', ('res_model', '=', self._name), ('res_model', '=', False)], context=context)
- for record in self.browse(cr, SUPERUSER_ID, ids, context=context):
- existing_pids = set([f.id for f in record.message_follower_ids
- if f.id in partner_ids])
+ for id in ids:
+ existing_pids = existing_pids_dict.get(id, set())
new_pids = set(partner_ids) - existing_pids
- # subtype_ids specified: update already subscribed partners
- if subtype_ids and existing_pids:
- fol_ids = mail_followers_obj.search(cr, SUPERUSER_ID, [
- ('res_model', '=', self._name),
- ('res_id', '=', record.id),
- ('partner_id', 'in', list(existing_pids)),
- ], context=context)
- mail_followers_obj.write(cr, SUPERUSER_ID, fol_ids, {'subtype_ids': [(6, 0, subtype_ids)]}, context=context)
- # subtype_ids not specified: do not update already subscribed partner, fetch default subtypes for new partners
- elif subtype_ids is None:
- subtype_ids = subtype_obj.search(cr, uid, [
- ('default', '=', True),
- '|',
- ('res_model', '=', self._name),
- ('res_model', '=', False)
- ], context=context)
# subscribe new followers
for new_pid in new_pids:
- mail_followers_obj.create(cr, SUPERUSER_ID, {
- 'res_model': self._name,
- 'res_id': record.id,
- 'partner_id': new_pid,
- 'subtype_ids': [(6, 0, subtype_ids)],
- }, context=context)
+ mail_followers_obj.create(
+ cr, SUPERUSER_ID, {
+ 'res_model': self._name,
+ 'res_id': id,
+ 'partner_id': new_pid,
+ 'subtype_ids': [(6, 0, subtype_ids)],
+ }, context=context)
return True
def message_unsubscribe(self, cr, uid, ids, partner_ids, context=None):
""" Remove partners from the records followers. """
+ # not necessary for computation, but saves an access right check
+ if not partner_ids:
+ return True
user_pid = self.pool.get('res.users').read(cr, uid, uid, ['partner_id'], context=context)['partner_id'][0]
if set(partner_ids) == set([user_pid]):
self.check_access_rights(cr, uid, 'read')
+ self.check_access_rule(cr, uid, ids, 'read')
else:
self.check_access_rights(cr, uid, 'write')
- return self.write(cr, SUPERUSER_ID, ids, {'message_follower_ids': [(3, pid) for pid in partner_ids]}, context=context)
+ self.check_access_rule(cr, uid, ids, 'write')
+ fol_obj = self.pool['mail.followers']
+ fol_ids = fol_obj.search(
+ cr, SUPERUSER_ID, [
+ ('res_model', '=', self._name),
+ ('res_id', 'in', ids),
+ ('partner_id', 'in', partner_ids)
+ ], context=context)
+ return fol_obj.unlink(cr, SUPERUSER_ID, fol_ids, context=context)
def _message_get_auto_subscribe_fields(self, cr, uid, updated_fields, auto_follow_fields=['user_id'], context=None):
""" Returns the list of relational fields linking to res.users that should
user_field_lst.append(name)
return user_field_lst
- def message_auto_subscribe(self, cr, uid, ids, updated_fields, context=None):
- """
- 1. fetch project subtype related to task (parent_id.res_model = 'project.task')
- 2. for each project subtype: subscribe the follower to the task
+ def message_auto_subscribe(self, cr, uid, ids, updated_fields, context=None, values=None):
+ """ Handle auto subscription. Two methods for auto subscription exist:
+
+ - tracked res.users relational fields, such as user_id fields. Those fields
+ must be relation fields toward a res.users record, and must have the
+ track_visilibity attribute set.
+ - using subtypes parent relationship: check if the current model being
+ modified has an header record (such as a project for tasks) whose followers
+ can be added as followers of the current records. Example of structure
+ with project and task:
+
+ - st_project_1.parent_id = st_task_1
+ - st_project_1.res_model = 'project.project'
+ - st_project_1.relation_field = 'project_id'
+ - st_task_1.model = 'project.task'
+
+ :param list updated_fields: list of updated fields to track
+ :param dict values: updated values; if None, the first record will be browsed
+ to get the values. Added after releasing 7.0, therefore
+ not merged with updated_fields argumment.
"""
subtype_obj = self.pool.get('mail.message.subtype')
follower_obj = self.pool.get('mail.followers')
+ new_followers = dict()
- # fetch auto_follow_fields
+ # fetch auto_follow_fields: res.users relation fields whose changes are tracked for subscription
user_field_lst = self._message_get_auto_subscribe_fields(cr, uid, updated_fields, context=context)
- # fetch related record subtypes
- related_subtype_ids = subtype_obj.search(cr, uid, ['|', ('res_model', '=', False), ('parent_id.res_model', '=', self._name)], context=context)
- subtypes = subtype_obj.browse(cr, uid, related_subtype_ids, context=context)
- default_subtypes = [subtype for subtype in subtypes if subtype.res_model == False]
- related_subtypes = [subtype for subtype in subtypes if subtype.res_model != False]
- relation_fields = set([subtype.relation_field for subtype in subtypes if subtype.relation_field != False])
- if (not related_subtypes or not any(relation in updated_fields for relation in relation_fields)) and not user_field_lst:
+ # fetch header subtypes
+ header_subtype_ids = subtype_obj.search(cr, uid, ['|', ('res_model', '=', False), ('parent_id.res_model', '=', self._name)], context=context)
+ subtypes = subtype_obj.browse(cr, uid, header_subtype_ids, context=context)
+
+ # if no change in tracked field or no change in tracked relational field: quit
+ relation_fields = set([subtype.relation_field for subtype in subtypes if subtype.relation_field is not False])
+ if not any(relation in updated_fields for relation in relation_fields) and not user_field_lst:
return True
- for record in self.browse(cr, uid, ids, context=context):
- new_followers = dict()
- parent_res_id = False
- parent_model = False
- for subtype in related_subtypes:
- if not subtype.relation_field or not subtype.parent_id:
- continue
- if not subtype.relation_field in self._columns or not getattr(record, subtype.relation_field, False):
- continue
- parent_res_id = getattr(record, subtype.relation_field).id
- parent_model = subtype.res_model
- follower_ids = follower_obj.search(cr, SUPERUSER_ID, [
- ('res_model', '=', parent_model),
- ('res_id', '=', parent_res_id),
- ('subtype_ids', 'in', [subtype.id])
- ], context=context)
- for follower in follower_obj.browse(cr, SUPERUSER_ID, follower_ids, context=context):
- new_followers.setdefault(follower.partner_id.id, set()).add(subtype.parent_id.id)
-
- if parent_res_id and parent_model:
- for subtype in default_subtypes:
- follower_ids = follower_obj.search(cr, SUPERUSER_ID, [
- ('res_model', '=', parent_model),
- ('res_id', '=', parent_res_id),
- ('subtype_ids', 'in', [subtype.id])
- ], context=context)
- for follower in follower_obj.browse(cr, SUPERUSER_ID, follower_ids, context=context):
- new_followers.setdefault(follower.partner_id.id, set()).add(subtype.id)
-
- # add followers coming from res.users relational fields that are tracked
- user_ids = [getattr(record, name).id for name in user_field_lst if getattr(record, name)]
- user_id_partner_ids = [user.partner_id.id for user in self.pool.get('res.users').browse(cr, SUPERUSER_ID, user_ids, context=context)]
- for partner_id in user_id_partner_ids:
- new_followers.setdefault(partner_id, None)
-
- for pid, subtypes in new_followers.items():
- subtypes = list(subtypes) if subtypes is not None else None
- self.message_subscribe(cr, uid, [record.id], [pid], subtypes, context=context)
-
- # find first email message, set it as unread for auto_subscribe fields for them to have a notification
- if user_id_partner_ids:
- msg_ids = self.pool.get('mail.message').search(cr, uid, [
- ('model', '=', self._name),
- ('res_id', '=', record.id),
- ('type', '=', 'email')], limit=1, context=context)
- if not msg_ids and record.message_ids:
- msg_ids = [record.message_ids[-1].id]
+ # legacy behavior: if values is not given, compute the values by browsing
+ # @TDENOTE: remove me in 8.0
+ if values is None:
+ record = self.browse(cr, uid, ids[0], context=context)
+ for updated_field in updated_fields:
+ field_value = getattr(record, updated_field)
+ if isinstance(field_value, browse_record):
+ field_value = field_value.id
+ elif isinstance(field_value, browse_null):
+ field_value = False
+ values[updated_field] = field_value
+
+ # find followers of headers, update structure for new followers
+ headers = set()
+ for subtype in subtypes:
+ if subtype.relation_field and values.get(subtype.relation_field):
+ headers.add((subtype.res_model, values.get(subtype.relation_field)))
+ if headers:
+ header_domain = ['|'] * (len(headers) - 1)
+ for header in headers:
+ header_domain += ['&', ('res_model', '=', header[0]), ('res_id', '=', header[1])]
+ header_follower_ids = follower_obj.search(
+ cr, SUPERUSER_ID,
+ header_domain,
+ context=context
+ )
+ for header_follower in follower_obj.browse(cr, SUPERUSER_ID, header_follower_ids, context=context):
+ for subtype in header_follower.subtype_ids:
+ if subtype.parent_id and subtype.parent_id.res_model == self._name:
+ new_followers.setdefault(header_follower.partner_id.id, set()).add(subtype.parent_id.id)
+ elif subtype.res_model is False:
+ new_followers.setdefault(header_follower.partner_id.id, set()).add(subtype.id)
+
+ # add followers coming from res.users relational fields that are tracked
+ user_ids = [values[name] for name in user_field_lst if values.get(name)]
+ user_pids = [user.partner_id.id for user in self.pool.get('res.users').browse(cr, SUPERUSER_ID, user_ids, context=context)]
+ for partner_id in user_pids:
+ new_followers.setdefault(partner_id, None)
+
+ for pid, subtypes in new_followers.items():
+ subtypes = list(subtypes) if subtypes is not None else None
+ self.message_subscribe(cr, uid, ids, [pid], subtypes, context=context)
+
+ # find first email message, set it as unread for auto_subscribe fields for them to have a notification
+ if user_pids:
+ for record_id in ids:
+ message_obj = self.pool.get('mail.message')
+ msg_ids = message_obj.search(cr, SUPERUSER_ID, [
+ ('model', '=', self._name),
+ ('res_id', '=', record_id),
+ ('type', '=', 'email')], limit=1, context=context)
+ if not msg_ids:
+ msg_ids = message_obj.search(cr, SUPERUSER_ID, [
+ ('model', '=', self._name),
+ ('res_id', '=', record_id)], limit=1, context=context)
if msg_ids:
- self.pool.get('mail.notification')._notify(cr, uid, msg_ids[0], partners_to_notify=user_id_partner_ids, context=context)
+ self.pool.get('mail.notification')._notify(cr, uid, msg_ids[0], partners_to_notify=user_pids, context=context)
return True