From ac644cfd4f6f12e9b9b7d2a3570f54cb2ad83bb3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thibault=20Delavall=C3=A9e?= Date: Tue, 13 Mar 2012 16:06:35 +0100 Subject: [PATCH] [IMP] mail.thread: cleaned code; added unlink management (subscriptions and messages are deleted, notifications are automatically deleted when messages are deleted because of foreign key); improved code and comments bzr revid: tde@openerp.com-20120313150635-wwbvs30s0w7ru8qk --- addons/mail/mail_thread.py | 119 +++++++++++++++++++------------------ 1 file changed, 62 insertions(+), 57 deletions(-) diff --git a/addons/mail/mail_thread.py b/addons/mail/mail_thread.py index 6ea9d806c7b..80091b5a4cb 100644 --- a/addons/mail/mail_thread.py +++ b/addons/mail/mail_thread.py @@ -2,7 +2,7 @@ ############################################################################## # # OpenERP, Open Source Management Solution -# Copyright (C) 2009-Today OpenERP SA () +# Copyright (C) 2009-today OpenERP SA () # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as @@ -41,16 +41,23 @@ class mail_thread(osv.osv): name collisions with methods of the models that will inherit from this mixin. - ``mail.thread`` adds a one2many of mail.messages, acting as the - thread's history, and a few methods that may be overridden to - implement model-specific behavior upon arrival of new messages. + ``mail.thread`` is designed to work without adding any field + to the extended models. All functionalities and expected behavior + are managed by mail.thread, using model name and record ids. + A widget has been designed for the 6.1 and following version of OpenERP + web-client. However, due to technical limitations, ``mail.thread`` + adds a simulated one2many field, to display the web widget by + overriding the default field displayed. Using this field + is not recommanded has it will disappeear in future version + of OpenERP, leading to a pure mixin class. Inheriting classes are not required to implement any method, as the default implementation will work for any model. However it is common 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. - + creation and update of a thread; and ``message_get_subscribers`` + to manage more precisely the social aspect of the thread through + the followers. ''' _name = 'mail.thread' _description = 'Email Thread' @@ -63,12 +70,10 @@ class mail_thread(osv.osv): res[thread.id] = [obj['id'] for obj in records] return res - # OpenSocial: removed message_ids and copy method, this will be replaced by message_load + # OpenSocial: message_ids_social is a dummy field that should not be used _columns = { 'message_ids_social': fields.function(_get_message_ids, method=True, - type='one2many', obj='mail.message', string='Temp messages', - ), - #widget='mail.ThreadView'), + type='one2many', obj='mail.message', string='Temp messages'), } #------------------------------------------------------ @@ -86,6 +91,28 @@ class mail_thread(osv.osv): self.message_subscribe(cr, uid, ids, [uid], context=context) return write_res; + def unlink(self, cr, uid, ids, context=None): + """Override unlink, to automatically delete + - subscriptions + - messages + that are linked with res_model and res_id, not through + a foreign key with a 'cascade' ondelete attribute. + Notifications will be deleted with messages + """ + if context is None: + context = {} + + subscr_obj = self.pool.get('mail.subscription') + msg_obj = self.pool.get('mail.message') + + subscr_to_del_ids = subscr_obj.search(cr, uid, [('res_model', '=', self._name), ('res_id', 'in', ids)], context=context) + subscr_obj.unlink(cr, uid, subscr_to_del_ids, context=context) + + msg_to_del_ids = msg_obj.search(cr, uid, [('model', '=', self._name), ('res_id', 'in', ids)], context=context) + msg_obj.unlink(cr, uid, msg_to_del_ids, context=context) + + super(mail_thread, self).unlink(cr, uid, ids, context=context) + #------------------------------------------------------ # Generic message api #------------------------------------------------------ @@ -98,34 +125,30 @@ class mail_thread(osv.osv): if context is None: context = {} + need_action_pushed = False + user_to_push_ids = [] subscription_obj = self.pool.get('mail.subscription') notification_obj = self.pool.get('mail.notification') - need_action_pushed = False - # create message msg_id = self.pool.get('mail.message').create(cr, uid, vals, context=context) - obj = self.browse(cr, uid, [thread_id], context=context)[0] + thread_obj = self.browse(cr, uid, [thread_id], context=context)[0] # automatically subscribe the writer of the message if not subscribed - if vals['user_id']: - if not self.message_is_subscriber(cr, uid, [thread_id], context=context): - self.message_subscribe(cr, uid, [thread_id], context=context) + if vals['user_id'] and not self.message_is_subscriber(cr, vals['user_id'], [thread_id], context=context): + self.message_subscribe(cr, uid, [thread_id], [vals['user_id']], context=context) # push the message to suscribed users users = self.message_get_subscribers(cr, uid, [thread_id], context=context) - for user in users: - notification_obj.create(cr, uid, {'user_id': user['id'], 'message_id': msg_id}, context=context) - # push to need_action_user_id - if obj.need_action_user_id == user['id']: need_action_pushed = True - # push to need_action_user_id if user does not follow the object - if obj.need_action_user_id and not need_action_pushed: - notification_obj.create(cr, uid, {'user_id': obj.need_action_user_id.id, 'message_id': msg_id}, context=context) - + user_to_push_ids += [user['id'] for user in users] # parse message to get requested users - user_ids = self.message_parse_users(cr, uid, [msg_id], vals['body_text'], context=context) - for user_id in user_ids: - notification_obj.create(cr, uid, {'user_id': user_id, 'message_id': msg_id}, context=context) + user_to_push_ids += self.message_parse_users(cr, uid, [msg_id], vals['body_text'], context=context) + # push to need_action_user_id if set + if thread_obj.need_action_user_id and thread_obj.need_action_user_id.id not in user_to_push_ids: + user_to_push_ids.append(thread_obj.need_action_user_id.id) + + # effectively subscribe users + self.message_subscribe(cr, uid, [thread_id], user_to_push_ids, context=context) return msg_id @@ -261,8 +284,6 @@ class mail_thread(osv.osv): 'reply_to': reply_to, 'original': original, } - #mail_message.create(cr, uid, data, context=context) - context['mail.thread'] = True self.message_create(cr, uid, thread.id, data, context=context) return True @@ -282,7 +303,7 @@ class mail_thread(osv.osv): to determine the model of the thread to update (instead of the current model). """ - # 6.2 OpenSocial feature: add default email type for old API + # OpenSocial: add default email type for old API if not 'type' in msg_dict: msg_dict['type'] = 'email' return self.message_append(cr, uid, ids, subject = msg_dict.get('subject'), @@ -307,7 +328,8 @@ class mail_thread(osv.osv): # Message loading def _message_get_parent_ids(self, cr, uid, ids, child_ids, root_ids, context=None): - if context is None: context = {} + if context is None: + context = {} msg_obj = self.pool.get('mail.message') msgs_tmp = msg_obj.read(cr, uid, child_ids, context=context) parent_ids = [msg['parent_id'][0] for msg in msgs_tmp if msg['parent_id'] not in root_ids and msg['parent_id'][0] not in child_ids] @@ -325,7 +347,8 @@ class mail_thread(osv.osv): loading messages: search in mail.messages where res_id = ids, (res_)model = current model see get_pushed_messages for parameters explanation """ - if context is None: context = {} + if context is None: + context = {} msg_obj = self.pool.get('mail.message') msg_ids = msg_obj.search(cr, uid, ['&', ('res_id', 'in', ids), ('model', '=', self._name)] + domain, limit=limit, offset=offset, context=context) @@ -365,17 +388,13 @@ class mail_thread(osv.osv): # Message tools def message_get_discussions_nbr(self, cr, uid, ids, context=None): - count = 0 message_obj = self.pool.get('mail.message') - for id in ids: - count += message_obj.search(cr, uid, [('model', '=', self._name), ('res_id', '=', id)], count=True) # TODO: add parent_id when merging branch + count = message_obj.search(cr, uid, [('model', '=', self._name), ('res_id', '=', ids), ('parent_id', '=', False)], count=True) return count def message_get_messages_nbr(self, cr, uid, ids, context=None): - count = 0 message_obj = self.pool.get('mail.message') - for id in ids: - count += message_obj.search(cr, uid, [('model', '=', self._name), ('res_id', '=', id)], count=True) + count = message_obj.search(cr, uid, [('model', '=', self._name), ('res_id', 'in', ids)], count=True) return count #------------------------------------------------------ @@ -627,33 +646,19 @@ class mail_thread(osv.osv): def message_append_note(self, cr, uid, ids, subject, body, parent_id=False, type='notification', context=None): return self.message_append(cr, uid, ids, subject, body_text=body, parent_id=parent_id, type=type, context=context) - - # old log overrided method: now calls message_append_note - def log(self, cr, uid, id, message, secondary=False, context=None): - """ OpenSocial add: new res_log implementation - A res.log is now a mail.message, as all messages in OpenERP - It has a notification type. - It can have a need_action flag attached if an user - has to perform a given action. - See mail.message, mail.subscription and mail.notification for more details. - """ - if context and context.get('disable_log'): - #return True # old behavior - print 'Log diabled, but we do not care currently about that. We want you to have our logs !' - #return self.message_append_note(cr, uid, [id], 'System notification', message, context=context) #------------------------------------------------------ # Subscription mechanism #------------------------------------------------------ - def _message_get_subscribers_ids(self, cr, uid, ids, context=None): - subscription_obj = self.pool.get('mail.subscription') - sub_ids = subscription_obj.search(cr, uid, ['&', ('res_model', '=', self._name), ('res_id', 'in', ids)], context=context) - subs = subscription_obj.read(cr, uid, sub_ids, context=context) + def message_get_subscribers_ids(self, cr, uid, ids, context=None): + subscr_obj = self.pool.get('mail.subscription') + subscr_ids = subscr_obj.search(cr, uid, ['&', ('res_model', '=', self._name), ('res_id', 'in', ids)], context=context) + subs = subscr_obj.read(cr, uid, subscr_ids, context=context) return [sub['user_id'][0] for sub in subs] def message_get_subscribers(self, cr, uid, ids, context=None): - user_ids = self._message_get_subscribers_ids(cr, uid, ids, context=context) + user_ids = self.message_get_subscribers_ids(cr, uid, ids, context=context) users = self.pool.get('res.users').read(cr, uid, user_ids, fields=['id', 'name', 'avatar_mini'], context=context) return users