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')