From 9dc08f3d7016b9efa560b224ed6086cd8a565179 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thibault=20Delavall=C3=A9e?= Date: Tue, 27 Nov 2012 15:32:22 +0100 Subject: [PATCH 01/13] [IMP] mail_thread: improved search function of message_unread, now directly implemented in SQL to speedup the result. bzr revid: tde@openerp.com-20121127143222-nj9i2o5byor4m7oq --- addons/mail/mail_thread.py | 26 ++++++++++++-------------- 1 file changed, 12 insertions(+), 14 deletions(-) diff --git a/addons/mail/mail_thread.py b/addons/mail/mail_thread.py index 15bc53223ba..dce5bc861e4 100644 --- a/addons/mail/mail_thread.py +++ b/addons/mail/mail_thread.py @@ -121,18 +121,15 @@ class mail_thread(osv.AbstractModel): return res - def _search_unread(self, cr, uid, obj=None, name=None, domain=None, context=None): - partner_id = self.pool.get('res.users').read(cr, uid, uid, ['partner_id'], context=context)['partner_id'][0] - res = {} - notif_obj = self.pool.get('mail.notification') - notif_ids = notif_obj.search(cr, uid, [ - ('partner_id', '=', partner_id), - ('message_id.model', '=', self._name), - ('read', '=', False) - ], context=context) - for notif in notif_obj.browse(cr, uid, notif_ids, context=context): - res[notif.message_id.res_id] = True - return [('id', 'in', res.keys())] + def _search_message_unread(self, cr, uid, obj=None, name=None, domain=None, context=None): + user_pid = self.pool.get('res.users').read(cr, uid, uid, ['partner_id'], context=context)['partner_id'][0] + cr.execute(""" SELECT DISTINCT m.res_id FROM mail_message m + JOIN mail_notification n + ON (n.message_id = m.id AND n.partner_id = %s AND (n.read = False or n.read IS NULL)) + WHERE m.model = %s""", + (user_pid, self._name,)) + res_ids = [result[0] for result in cr.fetchall()] + return [('id', 'in', res_ids)] def _get_followers(self, cr, uid, ids, name, arg, context=None): fol_obj = self.pool.get('mail.followers') @@ -209,8 +206,9 @@ class mail_thread(osv.AbstractModel): domain=lambda self: [('model', '=', self._name)], string='Messages', help="Messages and communication history"), - 'message_unread': fields.function(_get_message_data, fnct_search=_search_unread, - type='boolean', string='Unread Messages', multi="_get_message_data", + 'message_unread': fields.function(_get_message_data, + fnct_search=_search_message_unread, multi="_get_message_data", + type='boolean', string='Unread Messages', help="If checked new messages require your attention."), 'message_summary': fields.function(_get_message_data, method=True, type='text', string='Summary', multi="_get_message_data", From e0fa7da24fc25be4f0a83572018d900dc6ca24c1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thibault=20Delavall=C3=A9e?= Date: Tue, 27 Nov 2012 15:35:20 +0100 Subject: [PATCH 02/13] [REM] mail_thread: removed field message_comment_ids, not really necessary. In message_summary, we display the true number of messages, not only comment number. This is more consistent with what you will see in Chatter, aka all messages. bzr revid: tde@openerp.com-20121127143520-cv4os3dosrqo01wb --- addons/mail/mail_thread.py | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/addons/mail/mail_thread.py b/addons/mail/mail_thread.py index dce5bc861e4..096d7d71045 100644 --- a/addons/mail/mail_thread.py +++ b/addons/mail/mail_thread.py @@ -88,7 +88,7 @@ class mail_thread(osv.AbstractModel): for thread in self.browse(cr, uid, ids, context=context): cls = res[thread.id]['message_unread'] and ' class="oe_kanban_mail_new"' or '' - res[thread.id]['message_summary'] = "9 %d + %d" % (cls, len(thread.message_comment_ids), len(thread.message_follower_ids)) + res[thread.id]['message_summary'] = "9 %d + %d" % (cls, len(thread.message_ids), len(thread.message_follower_ids)) return res @@ -198,10 +198,6 @@ class mail_thread(osv.AbstractModel): 'message_follower_ids': fields.function(_get_followers, fnct_inv=_set_followers, fnct_search=_search_followers, type='many2many', obj='res.partner', string='Followers', multi='_get_followers'), - 'message_comment_ids': fields.one2many('mail.message', 'res_id', - domain=lambda self: [('model', '=', self._name), ('type', 'in', ('comment', 'email'))], - string='Comments and emails', - help="Comments and emails"), 'message_ids': fields.one2many('mail.message', 'res_id', domain=lambda self: [('model', '=', self._name)], string='Messages', @@ -243,7 +239,6 @@ class mail_thread(osv.AbstractModel): def copy(self, cr, uid, id, default=None, context=None): default = default or {} default['message_ids'] = [] - default['message_comment_ids'] = [] default['message_follower_ids'] = [] return super(mail_thread, self).copy(cr, uid, id, default=default, context=context) From 2fcaf2252cb5c815f29489828e7d536f1f8d425c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thibault=20Delavall=C3=A9e?= Date: Tue, 27 Nov 2012 19:11:38 +0100 Subject: [PATCH 03/13] [IMP] mail_message model: now uses _auto_join. bzr revid: tde@openerp.com-20121127181138-1mdebb6ehal2r973 --- addons/mail/mail_message.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/addons/mail/mail_message.py b/addons/mail/mail_message.py index ba6d101541c..4b2878a6bcd 100644 --- a/addons/mail/mail_message.py +++ b/addons/mail/mail_message.py @@ -63,7 +63,7 @@ class mail_message(osv.Model): # protection for `default_type` values leaking from menu action context (e.g. for invoices) if context and context.get('default_type') and context.get('default_type') not in self._columns['type'].selection: context = dict(context, default_type=None) - return super(mail_message, self).default_get(cr, uid, fields, context=context) + return super(mail_message, self).default_get(cr, uid, fields, context=context) def _shorten_name(self, name): if len(name) <= (self._message_record_name_length + 3): @@ -146,7 +146,7 @@ class mail_message(osv.Model): store=True, string='Message Record Name', help="Name get of the related document."), 'notification_ids': fields.one2many('mail.notification', 'message_id', - string='Notifications', + string='Notifications', _auto_join=True, help='Technical field holding the message notifications. Use notified_partner_ids to access notified partners.'), 'subject': fields.char('Subject'), 'date': fields.datetime('Date'), From a123d95dcadfdceeddf1f9cb91629b61c0dd1c09 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thibault=20Delavall=C3=A9e?= Date: Thu, 29 Nov 2012 12:30:25 +0100 Subject: [PATCH 04/13] [IMP] mail_message: star feature is now on mail.notification. A starred message has a notification with starred=True. favorite_user_ids many2many field disappear. _search_to_read and _search_starred replaced by returning a domain on the notifications. Using auto_join, this should speedup chatter a lot. Updated tests accordingly (should be updated a bit more than that, though). Updated JS. Updated mailboxes domains, as to_read and starred already contain a reference to the pid. bzr revid: tde@openerp.com-20121129113025-rizon3eaf14vrla2 --- addons/mail/mail_followers.py | 29 +------- addons/mail/mail_message.py | 107 ++++++++++++++++++++++-------- addons/mail/mail_thread_view.xml | 19 ++++-- addons/mail/static/src/js/mail.js | 4 +- addons/mail/tests/test_mail.py | 15 +++-- 5 files changed, 107 insertions(+), 67 deletions(-) diff --git a/addons/mail/mail_followers.py b/addons/mail/mail_followers.py index 8d3876ec91f..f02835e5b3b 100644 --- a/addons/mail/mail_followers.py +++ b/addons/mail/mail_followers.py @@ -19,7 +19,6 @@ # ############################################################################## -from openerp import SUPERUSER_ID from osv import osv from osv import fields import tools @@ -63,12 +62,15 @@ class mail_notification(osv.Model): 'partner_id': fields.many2one('res.partner', string='Contact', ondelete='cascade', required=True, select=1), 'read': fields.boolean('Read', select=1), + 'starred': fields.boolean('Starred', select=1, + help='Starred message that goes into the todo mailbox'), 'message_id': fields.many2one('mail.message', string='Message', ondelete='cascade', required=True, select=1), } _defaults = { 'read': False, + 'starred': False, } def init(self, cr): @@ -83,31 +85,6 @@ class mail_notification(osv.Model): return super(mail_notification, self).create(cr, uid, vals, context=context) return False - def set_message_read(self, cr, uid, msg_ids, read=None, context=None): - """ Set messages as (un)read. Technically, the notifications related - to uid are set to (un)read. If for some msg_ids there are missing - notifications (i.e. due to load more or thread parent fetching), - they are created. - - :param bool read: (un)read notification - """ - user_pid = self.pool.get('res.users').read(cr, uid, uid, ['partner_id'], context=context)['partner_id'][0] - notif_ids = self.search(cr, uid, [ - ('partner_id', '=', user_pid), - ('message_id', 'in', msg_ids) - ], context=context) - - # all message have notifications: already set them as (un)read - if len(notif_ids) == len(msg_ids): - return self.write(cr, uid, notif_ids, {'read': read}, context=context) - - # some messages do not have notifications: find which one, create notification, update read status - notified_msg_ids = [notification.message_id.id for notification in self.browse(cr, uid, notif_ids, context=context)] - to_create_msg_ids = list(set(msg_ids) - set(notified_msg_ids)) - for msg_id in to_create_msg_ids: - self.create(cr, uid, {'partner_id': user_pid, 'read': read, 'message_id': msg_id}, context=context) - return self.write(cr, uid, notif_ids, {'read': read}, context=context) - def get_partners_to_notify(self, cr, uid, message, context=None): """ Return the list of partners to notify, based on their preferences. diff --git a/addons/mail/mail_message.py b/addons/mail/mail_message.py index 4b2878a6bcd..c1df5edf906 100644 --- a/addons/mail/mail_message.py +++ b/addons/mail/mail_message.py @@ -55,7 +55,7 @@ class mail_message(osv.Model): _message_read_limit = 30 _message_read_fields = ['id', 'parent_id', 'model', 'res_id', 'body', 'subject', 'date', 'to_read', 'email_from', - 'type', 'vote_user_ids', 'attachment_ids', 'author_id', 'partner_ids', 'record_name', 'favorite_user_ids'] + 'type', 'vote_user_ids', 'attachment_ids', 'author_id', 'partner_ids', 'record_name'] _message_record_name_length = 18 _message_read_more_limit = 1024 @@ -98,15 +98,28 @@ class mail_message(osv.Model): def _search_to_read(self, cr, uid, obj, name, domain, context=None): """ Search for messages to read by the current user. Condition is inversed because we search unread message on a read column. """ - if domain[0][2]: - read_cond = "(read = False OR read IS NULL)" - else: - read_cond = "read = True" + pid = self.pool.get('res.users').read(cr, uid, uid, ['partner_id'], context=context)['partner_id'][0] + return ['&', ('notification_ids.partner_id', '=', pid), ('notification_ids.read', '=', not domain[0][2])] + + def _get_starred(self, cr, uid, ids, name, arg, context=None): + """ Compute if the message is unread by the current user. """ + res = dict((id, False) for id in ids) partner_id = self.pool.get('res.users').read(cr, uid, uid, ['partner_id'], context=context)['partner_id'][0] - cr.execute("SELECT message_id FROM mail_notification "\ - "WHERE partner_id = %%s AND %s" % read_cond, - (partner_id,)) - return [('id', 'in', [r[0] for r in cr.fetchall()])] + notif_obj = self.pool.get('mail.notification') + notif_ids = notif_obj.search(cr, uid, [ + ('partner_id', 'in', [partner_id]), + ('message_id', 'in', ids), + ('starred', '=', True), + ], context=context) + for notif in notif_obj.browse(cr, uid, notif_ids, context=context): + res[notif.message_id.id] = True + return res + + def _search_starred(self, cr, uid, obj, name, domain, context=None): + """ Search for messages to read by the current user. Condition is + inversed because we search unread message on a read column. """ + pid = self.pool.get('res.users').read(cr, uid, uid, ['partner_id'], context=context)['partner_id'][0] + return ['&', ('notification_ids.partner_id', '=', pid), ('notification_ids.starred', '=', domain[0][2])] def name_get(self, cr, uid, ids, context=None): # name_get may receive int id instead of an id list @@ -154,15 +167,15 @@ class mail_message(osv.Model): 'body': fields.html('Contents', help='Automatically sanitized HTML contents'), 'to_read': fields.function(_get_to_read, fnct_search=_search_to_read, type='boolean', string='To read', - help='Functional field to search for messages the current user has to read'), + help='Current user has an unread notification linked to this message'), + 'starred': fields.function(_get_starred, fnct_search=_search_starred, + type='boolean', string='Starred', + help='Current user has a starred notification linked to this message'), 'subtype_id': fields.many2one('mail.message.subtype', 'Subtype', ondelete='set null', select=1,), 'vote_user_ids': fields.many2many('res.users', 'mail_vote', 'message_id', 'user_id', string='Votes', help='Users that voted for this message'), - 'favorite_user_ids': fields.many2many('res.users', 'mail_favorite', - 'message_id', 'user_id', string='Favorite', - help='Users that set this message in their favorites'), } def _needaction_domain_get(self, cr, uid, context=None): @@ -196,19 +209,62 @@ class mail_message(osv.Model): return new_has_voted or False #------------------------------------------------------ - # Favorite + # Notification API #------------------------------------------------------ - def favorite_toggle(self, cr, uid, ids, context=None): - ''' Toggles favorite. Performed using read to avoid access rights issues. - Done as SUPERUSER_ID because uid may star a message he cannot modify. ''' - for message in self.read(cr, uid, ids, ['favorite_user_ids'], context=context): - new_is_favorite = not (uid in message.get('favorite_user_ids')) - if new_is_favorite: - self.write(cr, SUPERUSER_ID, message.get('id'), {'favorite_user_ids': [(4, uid)]}, context=context) - else: - self.write(cr, SUPERUSER_ID, message.get('id'), {'favorite_user_ids': [(3, uid)]}, context=context) - return new_is_favorite or False + def set_message_read(self, cr, uid, msg_ids, read, context=None): + """ Set messages as (un)read. Technically, the notifications related + to uid are set to (un)read. If for some msg_ids there are missing + notifications (i.e. due to load more or thread parent fetching), + they are created. + + :param bool read: set notification as (un)read + """ + notification_obj = self.pool.get('mail.notification') + user_pid = self.pool.get('res.users').read(cr, uid, uid, ['partner_id'], context=context)['partner_id'][0] + notif_ids = notification_obj.search(cr, uid, [ + ('partner_id', '=', user_pid), + ('message_id', 'in', msg_ids) + ], context=context) + + # all message have notifications: already set them as (un)read + if len(notif_ids) == len(msg_ids): + return notification_obj.write(cr, uid, notif_ids, {'read': read}, context=context) + + # some messages do not have notifications: find which one, create notification, update read status + notified_msg_ids = [notification.message_id.id for notification in notification_obj.browse(cr, uid, notif_ids, context=context)] + to_create_msg_ids = list(set(msg_ids) - set(notified_msg_ids)) + for msg_id in to_create_msg_ids: + notification_obj.create(cr, uid, {'partner_id': user_pid, 'read': read, 'message_id': msg_id}, context=context) + return notification_obj.write(cr, uid, notif_ids, {'read': read}, context=context) + + def set_message_starred(self, cr, uid, msg_ids, starred, context=None): + """ Set messages as (un)starred. Technically, the notifications related + to uid are set to (un)starred. If for some msg_ids there are missing + notifications (i.e. due to load more or thread parent fetching), + they are created. + + :param bool starred: set notification as (un)starred + """ + notification_obj = self.pool.get('mail.notification') + user_pid = self.pool.get('res.users').read(cr, uid, uid, ['partner_id'], context=context)['partner_id'][0] + notif_ids = notification_obj.search(cr, uid, [ + ('partner_id', '=', user_pid), + ('message_id', 'in', msg_ids) + ], context=context) + + # all message have notifications: already set them as (un)starred + if len(notif_ids) == len(msg_ids): + notification_obj.write(cr, uid, notif_ids, {'starred': starred}, context=context) + return starred + + # some messages do not have notifications: find which one, create notification, update starred status + notified_msg_ids = [notification.message_id.id for notification in notification_obj.browse(cr, uid, notif_ids, context=context)] + to_create_msg_ids = list(set(msg_ids) - set(notified_msg_ids)) + for msg_id in to_create_msg_ids: + notification_obj.create(cr, uid, {'partner_id': user_pid, 'starred': starred, 'message_id': msg_id}, context=context) + notification_obj.write(cr, uid, notif_ids, {'starred': starred}, context=context) + return starred #------------------------------------------------------ # Message loading for web interface @@ -283,7 +339,6 @@ class mail_message(osv.Model): # votes and favorites: res.users ids, no prefetching should be done vote_nb = len(message.vote_user_ids) has_voted = uid in [user.id for user in message.vote_user_ids] - is_favorite = uid in [user.id for user in message.favorite_user_ids] return {'id': message.id, 'type': message.type, @@ -301,7 +356,7 @@ class mail_message(osv.Model): 'partner_ids': [], 'vote_nb': vote_nb, 'has_voted': has_voted, - 'is_favorite': is_favorite, + 'is_favorite': message.starred, 'attachment_ids': [], } diff --git a/addons/mail/mail_thread_view.xml b/addons/mail/mail_thread_view.xml index a232aa12642..fedcef36fda 100644 --- a/addons/mail/mail_thread_view.xml +++ b/addons/mail/mail_thread_view.xml @@ -11,9 +11,8 @@ } @@ -62,7 +63,9 @@ 'search_default_message_unread': True } @@ -83,7 +86,11 @@ 'default_res_id': uid }

diff --git a/addons/mail/static/src/js/mail.js b/addons/mail/static/src/js/mail.js index 44bb4e9ae09..3b521678fe8 100644 --- a/addons/mail/static/src/js/mail.js +++ b/addons/mail/static/src/js/mail.js @@ -851,7 +851,7 @@ openerp.mail = function (session) { } var message_ids = _.map(messages, function (val) { return val.id; }); - this.ds_notification.call('set_message_read', [message_ids, read_value, this.context]) + this.ds_message.call('set_message_read', [message_ids, read_value, this.context]) .then(function () { // apply modification _.each(messages, function (msg) { @@ -900,7 +900,7 @@ openerp.mail = function (session) { var self=this; var button = self.$('.oe_star:first'); - this.ds_message.call('favorite_toggle', [[self.id]]) + this.ds_message.call('set_message_starred', [[self.id], !self.is_favorite]) .then(function (star) { self.is_favorite=star; if (self.is_favorite) { diff --git a/addons/mail/tests/test_mail.py b/addons/mail/tests/test_mail.py index bc6df813ef7..c18dea46452 100644 --- a/addons/mail/tests/test_mail.py +++ b/addons/mail/tests/test_mail.py @@ -795,7 +795,7 @@ class test_mail(test_mail_mockup.TestMailMockups): # Test: msg has Bert as voter self.assertEqual(set(msg.vote_user_ids), set([user_raoul]), 'mail_message vote: after unvoting, Bert should be in the voter') - def test_70_message_favorite(self): + def test_70_message_star(self): """ Tests for favorites. """ cr, uid, user_admin, user_raoul, group_pigs = self.cr, self.uid, self.user_admin, self.user_raoul, self.group_pigs # Data: post a message on Pigs @@ -803,17 +803,18 @@ class test_mail(test_mail_mockup.TestMailMockups): msg = self.mail_message.browse(cr, uid, msg_id) # Do: Admin stars msg - self.mail_message.favorite_toggle(cr, uid, [msg.id]) + self.mail_message.set_message_starred(cr, uid, [msg.id], True) msg.refresh() # Test: msg starred by Admin - self.assertEqual(set(msg.favorite_user_ids), set([user_admin]), 'mail_message favorite: after starring, Admin should be in favorite_user_ids') + self.assertTrue(msg.starred, 'mail_message starred failed') # Do: Bert stars msg - self.mail_message.favorite_toggle(cr, user_raoul.id, [msg.id]) + self.mail_message.set_message_starred(cr, user_raoul.id, [msg.id], True) msg.refresh() # Test: msg starred by Admin and Raoul - self.assertEqual(set(msg.favorite_user_ids), set([user_admin, user_raoul]), 'mail_message favorite: after starring, Admin and Raoul should be in favorite_user_ids') + # self.assertTrue(msg.starred, set([user_admin, user_raoul]), 'mail_message favorite: after starring, Admin and Raoul should be in favorite_user_ids') # Do: Admin unvote for msg - self.mail_message.favorite_toggle(cr, uid, [msg.id]) + self.mail_message.set_message_starred(cr, uid, [msg.id], False) msg.refresh() # Test: msg starred by Raoul - self.assertEqual(set(msg.favorite_user_ids), set([user_raoul]), 'mail_message favorite: after unstarring, Raoul should be in favorite_user_ids') + self.assertFalse(msg.starred, 'mail_message starred failed') + # self.assertEqual(set(msg.favorite_user_ids), set([user_raoul]), 'mail_message favorite: after unstarring, Raoul should be in favorite_user_ids') From 9f84bbddfb3abfc69cdb6b3dc126b7e2381cc452 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thibault=20Delavall=C3=A9e?= Date: Thu, 29 Nov 2012 15:42:53 +0100 Subject: [PATCH 05/13] [IMP] mail: message_unread searchable field now returns a domain. This domain is quite complex, because it bypasses limitations of the _auto_join feature used on message_ids. It should be: message_ids.read = False. However, it takes into account the one2many model, as well as to_read being a function field also searchable. Ths parsing is not capable to handle this kind of thing correctly now without using an ugly hack. Updated _search_to_read and _search_starred. bzr revid: tde@openerp.com-20121129144253-i9e7m2kqjteq8ajr --- addons/mail/mail_message.py | 19 +++---------------- addons/mail/mail_thread.py | 19 +++++++++++-------- addons/mail/mail_thread_view.xml | 2 +- 3 files changed, 15 insertions(+), 25 deletions(-) diff --git a/addons/mail/mail_message.py b/addons/mail/mail_message.py index c1df5edf906..28ac087edc0 100644 --- a/addons/mail/mail_message.py +++ b/addons/mail/mail_message.py @@ -98,8 +98,7 @@ class mail_message(osv.Model): def _search_to_read(self, cr, uid, obj, name, domain, context=None): """ Search for messages to read by the current user. Condition is inversed because we search unread message on a read column. """ - pid = self.pool.get('res.users').read(cr, uid, uid, ['partner_id'], context=context)['partner_id'][0] - return ['&', ('notification_ids.partner_id', '=', pid), ('notification_ids.read', '=', not domain[0][2])] + return ['&', ('notification_ids.partner_id.user_ids', 'in', [uid]), ('notification_ids.read', '=', not domain[0][2])] def _get_starred(self, cr, uid, ids, name, arg, context=None): """ Compute if the message is unread by the current user. """ @@ -118,8 +117,7 @@ class mail_message(osv.Model): def _search_starred(self, cr, uid, obj, name, domain, context=None): """ Search for messages to read by the current user. Condition is inversed because we search unread message on a read column. """ - pid = self.pool.get('res.users').read(cr, uid, uid, ['partner_id'], context=context)['partner_id'][0] - return ['&', ('notification_ids.partner_id', '=', pid), ('notification_ids.starred', '=', domain[0][2])] + return ['&', ('notification_ids.partner_id.user_ids', 'in', [uid]), ('notification_ids.starred', '=', domain[0][2])] def name_get(self, cr, uid, ids, context=None): # name_get may receive int id instead of an id list @@ -179,9 +177,7 @@ class mail_message(osv.Model): } def _needaction_domain_get(self, cr, uid, context=None): - if self._needaction: - return [('to_read', '=', True)] - return [] + return [('to_read', '=', True)] def _get_default_author(self, cr, uid, context=None): return self.pool.get('res.users').read(cr, uid, uid, ['partner_id'], context=context)['partner_id'][0] @@ -529,15 +525,6 @@ class mail_message(osv.Model): thread_level=thread_level, message_unload_ids=message_unload_ids, domain=domain, parent_id=parent_id, context=context) return message_list - # TDE Note: do we need this ? - # def user_free_attachment(self, cr, uid, context=None): - # attachment = self.pool.get('ir.attachment') - # attachment_list = [] - # attachment_ids = attachment.search(cr, uid, [('res_model', '=', 'mail.message'), ('create_uid', '=', uid)]) - # if len(attachment_ids): - # attachment_list = [{'id': attach.id, 'name': attach.name, 'date': attach.create_date} for attach in attachment.browse(cr, uid, attachment_ids, context=context)] - # return attachment_list - #------------------------------------------------------ # mail_message internals #------------------------------------------------------ diff --git a/addons/mail/mail_thread.py b/addons/mail/mail_thread.py index 2240c91e4fa..40f13434717 100644 --- a/addons/mail/mail_thread.py +++ b/addons/mail/mail_thread.py @@ -122,14 +122,16 @@ class mail_thread(osv.AbstractModel): return res def _search_message_unread(self, cr, uid, obj=None, name=None, domain=None, context=None): - user_pid = self.pool.get('res.users').read(cr, uid, uid, ['partner_id'], context=context)['partner_id'][0] - cr.execute(""" SELECT DISTINCT m.res_id FROM mail_message m - JOIN mail_notification n - ON (n.message_id = m.id AND n.partner_id = %s AND (n.read = False or n.read IS NULL)) - WHERE m.model = %s""", - (user_pid, self._name,)) - res_ids = [result[0] for result in cr.fetchall()] - return [('id', 'in', res_ids)] + """ TDE FIXME in 7.1: searching unread messages + _auto_join is currently a very limited feature; therefore the returned + domain to search for unread messages take into acount the following + limitations of this feature : + - does not take into account the domain on the one2many field + (i.e. message_ids.model = self._name is needed) + - does not correctly handle function field on relational table + (i.e. message_ids.read crashes) + """ + return ['&', '&', ('message_ids.model', '=', self._name), ('message_ids.notification_ids.partner_id.user_ids', 'in', [uid]), ('message_ids.notification_ids.read', '=', False)] def _get_followers(self, cr, uid, ids, name, arg, context=None): fol_obj = self.pool.get('mail.followers') @@ -200,6 +202,7 @@ class mail_thread(osv.AbstractModel): obj='res.partner', string='Followers', multi='_get_followers'), 'message_ids': fields.one2many('mail.message', 'res_id', domain=lambda self: [('model', '=', self._name)], + _auto_join=True, string='Messages', help="Messages and communication history"), 'message_unread': fields.function(_get_message_data, diff --git a/addons/mail/mail_thread_view.xml b/addons/mail/mail_thread_view.xml index 80b9800f7ec..d5c111573ea 100644 --- a/addons/mail/mail_thread_view.xml +++ b/addons/mail/mail_thread_view.xml @@ -11,7 +11,7 @@ } comment - crm.lead @@ -651,7 +650,6 @@ Andrew - Inquiry @@ -669,6 +667,9 @@ Andrew Want to know features and benefits to use the new software. comment + diff --git a/addons/mail/mail_message.py b/addons/mail/mail_message.py index 28ac087edc0..cf691cceecf 100644 --- a/addons/mail/mail_message.py +++ b/addons/mail/mail_message.py @@ -682,12 +682,23 @@ class mail_message(osv.Model): (self._description, operation)) def create(self, cr, uid, values, context=None): + if context is None: + context = {} + default_starred = context.pop('default_starred', False) if not values.get('message_id') and values.get('res_id') and values.get('model'): values['message_id'] = tools.generate_tracking_message_id('%(res_id)s-%(model)s' % values) elif not values.get('message_id'): values['message_id'] = tools.generate_tracking_message_id('private') newid = super(mail_message, self).create(cr, uid, values, context) self._notify(cr, SUPERUSER_ID, newid, context=context) + # TDE FIXME: handle default_starred. Why not setting an inv on starred ? + # Because starred will call set_message_starred, that looks for notifications. + # When creating a new mail_message, it will create a notification to a message + # that does not exist, leading to an error (key not existing). Also this + # this means unread notifications will be created, yet we can not assure + # this is what we want. + if default_starred: + self.set_message_starred(cr, uid, [newid], True, context=context) return newid def read(self, cr, uid, ids, fields=None, context=None, load='_classic_read'): diff --git a/addons/mail/static/src/js/mail.js b/addons/mail/static/src/js/mail.js index 453b464a409..31e77a6ec7d 100644 --- a/addons/mail/static/src/js/mail.js +++ b/addons/mail/static/src/js/mail.js @@ -990,8 +990,7 @@ openerp.mail = function (session) { // add message composition form view if (!this.compose_message) { this.compose_message = new mail.ThreadComposeMessage(this, this, { - // TDE FIXME: default_favorite_user_ids does not exist anymore -> you should create the message, set starred then update - 'context': this.options.compose_as_todo && !this.thread_level ? this.context : this.context, + 'context': this.options.compose_as_todo && !this.thread_level ? _.extend(this.context, { 'default_starred': true }) : this.context, 'options': this.options, }); if (!this.thread_level || this.thread_level > this.options.display_indented_thread) { From 83d1e7cdf22d99d1a2269b84d805c541d6f847c0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thibault=20Delavall=C3=A9e?= Date: Tue, 4 Dec 2012 15:56:45 +0100 Subject: [PATCH 08/13] [IMP] mail_message: notification_ids as _auto_join; mail_thread: mail_message as _auto_join. Updated search domain of function fields accordingly. bzr revid: tde@openerp.com-20121204145645-vea4i1p93zmwztqx --- addons/mail/mail_message.py | 16 +++------------- addons/mail/mail_thread.py | 13 ++----------- addons/mail/mail_thread_view.xml | 1 - 3 files changed, 5 insertions(+), 25 deletions(-) diff --git a/addons/mail/mail_message.py b/addons/mail/mail_message.py index aec106f766b..95c34ad5c51 100644 --- a/addons/mail/mail_message.py +++ b/addons/mail/mail_message.py @@ -98,15 +98,7 @@ class mail_message(osv.Model): def _search_to_read(self, cr, uid, obj, name, domain, context=None): """ Search for messages to read by the current user. Condition is inversed because we search unread message on a read column. """ - if domain[0][2]: - read_cond = "(read = False OR read IS NULL)" - else: - read_cond = "read = True" - partner_id = self.pool.get('res.users').read(cr, uid, uid, ['partner_id'], context=context)['partner_id'][0] - cr.execute("SELECT message_id FROM mail_notification "\ - "WHERE partner_id = %%s AND %s" % read_cond, - (partner_id,)) - return [('id', 'in', [r[0] for r in cr.fetchall()])] + return ['&', ('notification_ids.partner_id.user_ids', 'in', [uid]), ('notification_ids.read', '=', not domain[0][2])] def name_get(self, cr, uid, ids, context=None): # name_get may receive int id instead of an id list @@ -146,7 +138,7 @@ class mail_message(osv.Model): store=True, string='Message Record Name', help="Name get of the related document."), 'notification_ids': fields.one2many('mail.notification', 'message_id', - string='Notifications', + string='Notifications', _auto_join=True, help='Technical field holding the message notifications. Use notified_partner_ids to access notified partners.'), 'subject': fields.char('Subject'), 'date': fields.datetime('Date'), @@ -166,9 +158,7 @@ class mail_message(osv.Model): } def _needaction_domain_get(self, cr, uid, context=None): - if self._needaction: - return [('to_read', '=', True)] - return [] + return [('to_read', '=', True)] def _get_default_author(self, cr, uid, context=None): return self.pool.get('res.users').read(cr, uid, uid, ['partner_id'], context=context)['partner_id'][0] diff --git a/addons/mail/mail_thread.py b/addons/mail/mail_thread.py index 30954be4ed8..d308105d81e 100644 --- a/addons/mail/mail_thread.py +++ b/addons/mail/mail_thread.py @@ -122,17 +122,7 @@ class mail_thread(osv.AbstractModel): return res def _search_unread(self, cr, uid, obj=None, name=None, domain=None, context=None): - partner_id = self.pool.get('res.users').read(cr, uid, uid, ['partner_id'], context=context)['partner_id'][0] - res = {} - notif_obj = self.pool.get('mail.notification') - notif_ids = notif_obj.search(cr, uid, [ - ('partner_id', '=', partner_id), - ('message_id.model', '=', self._name), - ('read', '=', False) - ], context=context) - for notif in notif_obj.browse(cr, uid, notif_ids, context=context): - res[notif.message_id.res_id] = True - return [('id', 'in', res.keys())] + return ['&', ('message_ids.model', '=', self._name), ('message_ids.notification_ids.to_read', '=', False)] def _get_followers(self, cr, uid, ids, name, arg, context=None): fol_obj = self.pool.get('mail.followers') @@ -207,6 +197,7 @@ class mail_thread(osv.AbstractModel): help="Comments and emails"), 'message_ids': fields.one2many('mail.message', 'res_id', domain=lambda self: [('model', '=', self._name)], + _auto_join=True, string='Messages', help="Messages and communication history"), 'message_unread': fields.function(_get_message_data, fnct_search=_search_unread, diff --git a/addons/mail/mail_thread_view.xml b/addons/mail/mail_thread_view.xml index 4106eb2e024..1ad0eee3406 100644 --- a/addons/mail/mail_thread_view.xml +++ b/addons/mail/mail_thread_view.xml @@ -11,7 +11,6 @@ } Date: Wed, 5 Dec 2012 16:36:09 +0100 Subject: [PATCH 12/13] [CLEAN] _auto_join -> auto_join. bzr revid: tde@openerp.com-20121205153609-tncmotw8jnoa53e7 --- addons/mail/mail_message.py | 2 +- addons/mail/mail_thread.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/addons/mail/mail_message.py b/addons/mail/mail_message.py index fbcffff1e4b..a5080e48373 100644 --- a/addons/mail/mail_message.py +++ b/addons/mail/mail_message.py @@ -157,7 +157,7 @@ class mail_message(osv.Model): store=True, string='Message Record Name', help="Name get of the related document."), 'notification_ids': fields.one2many('mail.notification', 'message_id', - string='Notifications', _auto_join=True, + string='Notifications', auto_join=True, help='Technical field holding the message notifications. Use notified_partner_ids to access notified partners.'), 'subject': fields.char('Subject'), 'date': fields.datetime('Date'), diff --git a/addons/mail/mail_thread.py b/addons/mail/mail_thread.py index 4145fabd0e5..e706ad66a33 100644 --- a/addons/mail/mail_thread.py +++ b/addons/mail/mail_thread.py @@ -193,7 +193,7 @@ class mail_thread(osv.AbstractModel): obj='res.partner', string='Followers', multi='_get_followers'), 'message_ids': fields.one2many('mail.message', 'res_id', domain=lambda self: [('model', '=', self._name)], - _auto_join=True, + auto_join=True, string='Messages', help="Messages and communication history"), 'message_unread': fields.function(_get_message_data, From a4083348d5f571dfb7ffcdd5b389c757b4998cf6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thibault=20Delavall=C3=A9e?= Date: Mon, 10 Dec 2012 09:37:18 +0100 Subject: [PATCH 13/13] =?UTF-8?q?[FIX]=20Portal:=20portal=20inbox=20domain?= =?UTF-8?q?=20fixed,=20favorite=5Fuser=5Fids=20does=20not=20exist=20anymor?= =?UTF-8?q?e.=C3=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit bzr revid: tde@openerp.com-20121210083718-idod6rjgaasz001y --- addons/portal/portal_view.xml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/addons/portal/portal_view.xml b/addons/portal/portal_view.xml index b7765e63ffd..4aad43716b6 100644 --- a/addons/portal/portal_view.xml +++ b/addons/portal/portal_view.xml @@ -57,9 +57,8 @@ }