diff --git a/addons/mail/mail_group.py b/addons/mail/mail_group.py index 3febe34be51..644718fdfb9 100644 --- a/addons/mail/mail_group.py +++ b/addons/mail/mail_group.py @@ -30,7 +30,7 @@ class mail_group(osv.Model): group. The group mechanics are based on the followers. """ _description = 'Discussion group' _name = 'mail.group' - _mail_autothread = False + _mail_flat_thread = False _inherit = ['mail.thread'] _inherits = {'mail.alias': 'alias_id', 'ir.ui.menu': 'menu_id'} diff --git a/addons/mail/mail_thread.py b/addons/mail/mail_thread.py index e5b93ac4000..6388ad76457 100644 --- a/addons/mail/mail_thread.py +++ b/addons/mail/mail_thread.py @@ -57,10 +57,16 @@ class mail_thread(osv.AbstractModel): to override at least the ``message_new`` and ``message_update`` methods (calling ``super``) to add model-specific behavior at creation and update of a thread when processing incoming emails. + + Options: + - _mail_flat_thread: if set to True, all messages without parent_id + are automatically attached to the first message posted on the + ressource. If set to False, the display of Chatter is done using + threads, and no parent_id is automatically set. ''' _name = 'mail.thread' _description = 'Email Thread' - _mail_autothread = True + _mail_flat_thread = True def _get_message_data(self, cr, uid, ids, name, args, context=None): """ Computes: @@ -84,7 +90,7 @@ class mail_thread(osv.AbstractModel): res[thread.id]['message_summary'] = "9 %d + %d" % (cls, len(thread.message_comment_ids), len(thread.message_follower_ids)) return res - + def _get_subscription_data(self, cr, uid, ids, name, args, context=None): """ Computes: - message_is_follower: is uid in the document followers @@ -113,7 +119,7 @@ class mail_thread(osv.AbstractModel): for subtype in fol.subtype_ids: thread_subtype_dict[subtype.name]['followed'] = True res[fol.res_id]['message_subtype_data'] = thread_subtype_dict - + return res def _search_unread(self, cr, uid, obj=None, name=None, domain=None, context=None): @@ -633,8 +639,10 @@ class mail_thread(osv.AbstractModel): (isinstance(thread_id, (list, tuple)) and len(thread_id) == 1), "Invalid thread_id" if isinstance(thread_id, (list, tuple)): thread_id = thread_id and thread_id[0] + mail_message = self.pool.get('mail.message') + model = context.get('thread_model', self._name) if thread_id else False - attachment_ids=[] + attachment_ids = [] for name, content in attachments: if isinstance(content, unicode): content = content.encode('utf-8') @@ -648,24 +656,20 @@ class mail_thread(osv.AbstractModel): } attachment_ids.append((0, 0, data_attach)) - # get subtype - if not subtype: - subtype = 'mail.mt_comment' - s = subtype.split('.') - if len(s)==1: - s = ('mail', s[0]) - ref = self.pool.get('ir.model.data').get_object_reference(cr, uid, s[0], s[1]) - subtype_id = ref and ref[1] or False - - model = context.get('thread_model', self._name) if thread_id else False - messages = self.pool.get('mail.message') - - #auto link messages for same id and object - if self._mail_autothread and thread_id: - message_ids = messages.search(cr, uid, ['&',('res_id', '=', thread_id),('model','=',model)], context=context) - if len(message_ids): - parent_id = min(message_ids) + # fetch subtype + if subtype: + s_data = subtype.split('.') + if len(s_data) == 1: + s_data = ('mail', s_data[0]) + ref = self.pool.get('ir.model.data').get_object_reference(cr, uid, s_data[0], s_data[1]) + subtype_id = ref and ref[1] or False + else: + subtype_id = False + # _mail_flat_thread: automatically set free messages to the first posted message + if self._mail_flat_thread and not parent_id and thread_id: + message_ids = mail_message.search(cr, uid, ['&', ('res_id', '=', thread_id), ('model', '=', model)], context=context, order="id ASC", limit=1) + parent_id = message_ids and message_ids[0] or False values = kwargs values.update({ @@ -681,51 +685,47 @@ class mail_thread(osv.AbstractModel): # if the parent is private, the message must be private if parent_id: - msg = messages.browse(cr, uid, parent_id, context=context) - if msg.is_private: - values["is_private"] = msg.is_private + parent_message = mail_message.browse(cr, uid, parent_id, context=context) + if parent_message.is_private: + values["is_private"] = parent_message.is_private # Avoid warnings about non-existing fields for x in ('from', 'to', 'cc'): values.pop(x, None) - return messages.create(cr, uid, values, context=context) + return mail_message.create(cr, uid, values, context=context) + + def message_post_api(self, cr, uid, thread_id, body='', subject=False, type='notification', + subtype=None, parent_id=False, attachments=None, context=None, **kwargs): + # when writing on res.partner, without specific thread_id -> redirect to the user's partner + if self._name == 'res.partner' and not thread_id: + thread_id = self.pool.get('res.users').read(cr, uid, uid, ['partner_id'], context=context)['partner_id'][0] + new_message_id = self.message_post(cr, uid, thread_id=thread_id, body=body, subject=subject, type=type, + subtype=subtype, parent_id=parent_id, context=context) + # Chatter: attachments linked to the document (not done JS-side), load the message + if attachments: + ir_attachment = self.pool.get('ir.attachment') + mail_message = self.pool.get('mail.message') + attachment_ids = ir_attachment.search(cr, SUPERUSER_ID, [('res_model', '=', False), ('res_id', '=', False), ('user_id', '=', uid), ('id', 'in', attachments)], context=context) + if attachment_ids: + ir_attachment.write(cr, SUPERUSER_ID, attachment_ids, {'res_model': self._name, 'res_id': thread_id}, context=context) + mail_message.write(cr, SUPERUSER_ID, [new_message_id], {'attachment_ids': [(6, 0, [pid for pid in attachment_ids])]}) + new_message = self.pool.get('mail.message').message_read(cr, uid, [new_message_id]) + return new_message #------------------------------------------------------ # Followers API #------------------------------------------------------ - def message_post_api(self, cr, uid, thread_id, body='', subject=False, type='notification', - subtype=None, parent_id=False, attachments=None, context=None, **kwargs): - # if the user write on his wall - if self._name=='res.partner' and not thread_id: - user = self.pool.get('res.users').browse(cr, uid, uid, context=context) - thread_id = user.partner_id.id - - added_message_id = self.message_post(cr, uid, thread_id=thread_id, body=body, subject=subject, type=type, - subtype=subtype, parent_id=parent_id, context=context) - - attachment_ids=[] - if attachments: - ir_attachment = self.pool.get('ir.attachment') - attachment_ids = ir_attachment.search(cr, 1, [('res_model', '=', ""), ('res_id', '=', ""), ('user_id', '=', uid), ('id', 'in', attachments)], context=context) - if attachment_ids: - self.pool.get('ir.attachment').write(cr, 1, attachment_ids, { 'res_model': self._name, 'res_id': thread_id }, context=context) - self.pool.get('mail.message').write(cr, 1, [added_message_id], {'attachment_ids': [(6, 0, [pid for pid in attachment_ids])]} ) - - added_message = self.pool.get('mail.message').message_read(cr, uid, [added_message_id]) - return added_message - def get_message_subtypes(self, cr, uid, ids, context=None): - """ message_subtype_data: data about document subtypes: which are - available, which are followed if any """ + """ Wrapper to get subtypes. """ return self._get_subscription_data(cr, uid, ids, None, None, context=context) def message_subscribe_users(self, cr, uid, ids, user_ids=None, subtype_ids=None, context=None): """ Wrapper on message_subscribe, using users. If user_ids is not provided, subscribe uid instead. """ - if not user_ids: - return False + if user_ids is None: + user_ids = [uid] partner_ids = [user.partner_id.id for user in self.pool.get('res.users').browse(cr, uid, user_ids, context=context)] return self.message_subscribe(cr, uid, ids, partner_ids, subtype_ids=subtype_ids, context=context) @@ -738,14 +738,14 @@ class mail_thread(osv.AbstractModel): subtype_ids = subtype_obj.search(cr, uid, [('default', '=', True), '|', ('res_model', '=', self._name), ('res_model', '=', False)], context=context) # update the subscriptions fol_obj = self.pool.get('mail.followers') - fol_ids = fol_obj.search(cr, 1, [('res_model', '=', self._name), ('res_id', 'in', ids), ('partner_id', 'in', partner_ids)], context=context) - fol_obj.write(cr, 1, fol_ids, {'subtype_ids': [(6, 0, subtype_ids)]}, context=context) + fol_ids = fol_obj.search(cr, SUPERUSER_ID, [('res_model', '=', self._name), ('res_id', 'in', ids), ('partner_id', 'in', partner_ids)], context=context) + fol_obj.write(cr, SUPERUSER_ID, fol_ids, {'subtype_ids': [(6, 0, subtype_ids)]}, context=context) return True def message_unsubscribe_users(self, cr, uid, ids, user_ids=None, context=None): """ Wrapper on message_subscribe, using users. If user_ids is not provided, unsubscribe uid instead. """ - if not user_ids: + if user_ids is None: user_ids = [uid] partner_ids = [user.partner_id.id for user in self.pool.get('res.users').browse(cr, uid, user_ids, context=context)] return self.message_unsubscribe(cr, uid, ids, partner_ids, context=context) diff --git a/addons/mail/res_partner.py b/addons/mail/res_partner.py index 363ea74d16c..6b13fe6ad20 100644 --- a/addons/mail/res_partner.py +++ b/addons/mail/res_partner.py @@ -25,7 +25,7 @@ class res_partner_mail(osv.Model): """ Update partner to add a field about notification preferences """ _name = "res.partner" _inherit = ['res.partner', 'mail.thread'] - _mail_autothread = False + _mail_flat_thread = False _columns = { 'notification_email_send': fields.selection([ diff --git a/addons/mail/static/src/js/mail.js b/addons/mail/static/src/js/mail.js index 15a81be4c54..7aa8a11f62a 100644 --- a/addons/mail/static/src/js/mail.js +++ b/addons/mail/static/src/js/mail.js @@ -387,7 +387,7 @@ openerp.mail = function(session) { mail.ChatterUtils.get_text2html(body), false, 'comment', - false, + 'mail.mt_comment',, this.context.default_parent_id, attachments] ).then(this.parent_thread.proxy('switch_new_message'));