[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
This commit is contained in:
Thibault Delavallée 2012-11-29 12:30:25 +01:00
parent b4e8416486
commit a123d95dca
5 changed files with 107 additions and 67 deletions

View File

@ -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.

View File

@ -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': [],
}

View File

@ -11,9 +11,8 @@
}</field>
<field name="params" eval="&quot;{
'domain': [
('notification_ids.partner_id.user_ids', 'in', [uid]),
('to_read', '=', True),
('favorite_user_ids', 'not in', [uid])
('to_read', '=', False),
('starred', '=', False),
],
'view_mailbox': True,
'view_inbox': True,
@ -40,7 +39,9 @@
'search_default_message_unread': True
}</field>
<field name="params" eval="&quot;{
'domain': [('partner_ids.user_ids', 'in', [uid])],
'domain': [
('partner_ids.user_ids', 'in', [uid])
],
'view_mailbox': True,
'read_action': 'read', }&quot;"/>
<field name="help" type="html">
@ -62,7 +63,9 @@
'search_default_message_unread': True
}</field>
<field name="params" eval="&quot;{
'domain': [('favorite_user_ids', 'in', [uid])],
'domain': [
('starred', '=', True),
],
'view_mailbox': True,
'read_action': 'read', }&quot;"/>
<field name="help" type="html">
@ -83,7 +86,11 @@
'default_res_id': uid
}</field>
<field name="params" eval="&quot;{
'domain': ['|', ('notification_ids.partner_id.user_ids', 'in', [uid]), ('author_id.user_ids', 'in', [uid])],
'domain': [
'|',
('notification_ids.partner_id.user_ids', 'in', [uid]),
('author_id.user_ids', 'in', [uid]),
],
'view_mailbox': True, }&quot;"/>
<field name="help" type="html">
<p>

View File

@ -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) {

View File

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