summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRaphael Collet <rco@openerp.com>2012-10-08 17:12:34 +0200
committerRaphael Collet <rco@openerp.com>2012-10-08 17:12:34 +0200
commitf26a1cd5fda85589387fcf3afbd7c1af192b2d4a (patch)
treed62bf0e1c51e6619933a6cfd9d207eb1eeff04cd
parenta1922458f5eeafd67cf364dca2c003c4bc63c7ae (diff)
[FIX] mail: replace buggy many2many_reference field by function field; this fixes the search on field mail_follower_ids
bzr revid: rco@openerp.com-20121008151234-9oovjatffaw48k1d
-rw-r--r--addons/mail/mail_thread.py118
-rw-r--r--addons/mail/tests/test_mail.py2
2 files changed, 59 insertions, 61 deletions
diff --git a/addons/mail/mail_thread.py b/addons/mail/mail_thread.py
index 9f53b4c08b1..09acc85d9e1 100644
--- a/addons/mail/mail_thread.py
+++ b/addons/mail/mail_thread.py
@@ -39,63 +39,6 @@ _logger = logging.getLogger(__name__)
def decode_header(message, header, separator=' '):
return separator.join(map(decode, message.get_all(header, [])))
-class many2many_reference(fields.many2many):
- """ many2many_reference manages many2many fields where one id is found
- by a reference-like key (a char column in addition to the foreign id).
- The reference_column attribute on the many2many fields is used;
- if not defined, ``res_model`` is used. """
-
- def _get_query_and_where_params(self, cr, model, ids, values, where_params):
- """ Add in where condition like mail_followers.res_model = 'crm.lead' """
- reference_column = self.reference_column if self.reference_column else 'res_model'
- values.update(reference_column=reference_column, reference_value=model._name)
- query = 'SELECT %(rel)s.%(id2)s, %(rel)s.%(id1)s \
- FROM %(rel)s, %(from_c)s \
- WHERE %(rel)s.%(id1)s IN %%s \
- AND %(rel)s.%(id2)s = %(tbl)s.id \
- AND %(rel)s.%(reference_column)s = \'%(reference_value)s\' \
- %(where_c)s \
- %(order_by)s \
- %(limit)s \
- OFFSET %(offset)d' \
- % values
- return query, where_params
-
- def set(self, cr, model, id, name, values, user=None, context=None):
- """ Override to add the reference field in queries. """
- if not values: return
- rel, id1, id2 = self._sql_names(model)
- obj = model.pool.get(self._obj)
- # reference column name: given by attribute or res_model
- reference_column = self.reference_column if self.reference_column else 'res_model'
- for act in values:
- if not (isinstance(act, list) or isinstance(act, tuple)) or not act:
- continue
- if act[0] == 0:
- idnew = obj.create(cr, user, act[2], context=context)
- cr.execute('INSERT INTO '+rel+' ('+id1+','+id2+','+reference_column+') VALUES (%s,%s,%s)', (id, idnew, model._name))
- elif act[0] == 3:
- cr.execute('DELETE FROM '+rel+' WHERE '+id1+'=%s AND '+id2+'=%s AND '+reference_column+'=%s', (id, act[1], model._name))
- elif act[0] == 4:
- # following queries are in the same transaction - so should be relatively safe
- cr.execute('SELECT 1 FROM '+rel+' WHERE '+id1+'=%s AND '+id2+'=%s AND '+reference_column+'=%s', (id, act[1], model._name))
- if not cr.fetchone():
- cr.execute('INSERT INTO '+rel+' ('+id1+','+id2+','+reference_column+') VALUES (%s,%s,%s)', (id, act[1], model._name))
- elif act[0] == 5:
- cr.execute('delete from '+rel+' where '+id1+' = %s AND '+reference_column+'=%s', (id, model._name))
- elif act[0] == 6:
- d1, d2,tables = obj.pool.get('ir.rule').domain_get(cr, user, obj._name, context=context)
- if d1:
- d1 = ' and ' + ' and '.join(d1)
- else:
- d1 = ''
- cr.execute('DELETE FROM '+rel+' WHERE '+id1+'=%s AND '+reference_column+'=%s AND '+id2+' IN (SELECT '+rel+'.'+id2+' FROM '+rel+', '+','.join(tables)+' WHERE '+rel+'.'+id1+'=%s AND '+rel+'.'+id2+' = '+obj._table+'.id '+ d1 +')', [id, model._name, id]+d2)
- for act_nbr in act[2]:
- cr.execute('INSERT INTO '+rel+' ('+id1+','+id2+','+reference_column+') VALUES (%s,%s,%s)', (id, act_nbr, model._name))
- # cases 1, 2: performs write and unlink -> default implementation is ok
- else:
- return super(many2many_reference, self).set(cr, model, id, name, values, user, context)
-
class mail_thread(osv.AbstractModel):
''' mail_thread model is meant to be inherited by any model that needs to
@@ -186,6 +129,62 @@ class mail_thread(osv.AbstractModel):
res[notif.message_id.res_id] = True
return [('id', 'in', res.keys())]
+ def _get_followers(self, cr, uid, ids, name, arg, context=None):
+ fol_obj = self.pool.get('mail.followers')
+ fol_ids = fol_obj.search(cr, SUPERUSER_ID, [('res_model', '=', self._name), ('res_id', 'in', ids)])
+ res = dict((res_id, []) for res_id in ids)
+ for fol in fol_obj.browse(cr, SUPERUSER_ID, fol_ids):
+ res[fol.res_id].append(fol.partner_id.id)
+ return res
+
+ def _set_followers(self, cr, uid, id, name, value, arg, context=None):
+ partner_obj = self.pool.get('res.partner')
+ fol_obj = self.pool.get('mail.followers')
+
+ # read the old set of followers, and determine the new set of followers
+ fol_ids = fol_obj.search(cr, SUPERUSER_ID, [('res_model', '=', self._name), ('res_id', '=', id)])
+ old = set(fol.partner_id.id for fol in fol_obj.browse(cr, SUPERUSER_ID, fol_ids))
+ new = set(old)
+
+ for command in value:
+ if isinstance(command, (int, long)):
+ new.add(command)
+ elif command[0] == 0:
+ new.add(partner_obj.create(cr, uid, command[2], context=context))
+ elif command[0] == 1:
+ partner_obj.write(cr, uid, [command[1]], command[2], context=context)
+ new.add(command[1])
+ elif command[0] == 2:
+ partner_obj.unlink(cr, uid, [command[1]], context=context)
+ new.discard(command[1])
+ elif command[0] == 3:
+ new.discard(command[1])
+ elif command[0] == 4:
+ new.add(command[1])
+ elif command[0] == 5:
+ new.clear()
+ elif command[0] == 6:
+ new = set(command[2])
+
+ # remove partners that are no longer followers
+ fol_ids = fol_obj.search(cr, SUPERUSER_ID,
+ [('res_model', '=', self._name), ('res_id', '=', id), ('partner_id', 'not in', list(new))])
+ fol_obj.unlink(cr, SUPERUSER_ID, fol_ids)
+
+ # add new followers
+ for partner_id in new - old:
+ fol_obj.create(cr, SUPERUSER_ID, {'res_model': self._name, 'res_id': id, 'partner_id': partner_id})
+
+ def _search_followers(self, cr, uid, obj, name, args, context):
+ fol_obj = self.pool.get('mail.followers')
+ res = []
+ for field, operator, value in args:
+ assert field == name
+ fol_ids = fol_obj.search(cr, SUPERUSER_ID, [('res_model', '=', self._name), ('partner_id', operator, value)])
+ res_ids = [fol.res_id for fol in fol_obj.browse(cr, SUPERUSER_ID, fol_ids)]
+ res.append(('id', 'in', res_ids))
+ return res
+
_columns = {
'message_is_follower': fields.function(_get_subscription_data,
type='boolean', string='Is a Follower', multi='_get_subscription_data,'),
@@ -194,9 +193,8 @@ class mail_thread(osv.AbstractModel):
help="Holds data about the subtypes. The content of this field "\
"is a structure holding the current model subtypes, and the "\
"current document followed subtypes."),
- 'message_follower_ids': many2many_reference('res.partner',
- 'mail_followers', 'res_id', 'partner_id',
- reference_column='res_model', string='Followers'),
+ 'message_follower_ids': fields.function(_get_followers, fnct_inv=_set_followers,
+ fnct_search=_search_followers, type='many2many', obj='res.partner', string='Followers'),
'message_comment_ids': fields.one2many('mail.message', 'res_id',
domain=lambda self: [('model', '=', self._name), ('type', 'in', ('comment', 'email'))],
string='Comments and emails',
diff --git a/addons/mail/tests/test_mail.py b/addons/mail/tests/test_mail.py
index 7ec37d952bc..1fc44ed81af 100644
--- a/addons/mail/tests/test_mail.py
+++ b/addons/mail/tests/test_mail.py
@@ -194,7 +194,7 @@ class test_mail(TestMailMockups):
'plaintext mail incorrectly parsed')
def test_10_many2many_reference_field(self):
- """ Tests designed for the many2many_reference field (follower_ids).
+ """ Tests designed for the many2many function field 'follower_ids'.
We will test to perform writes using the many2many commands 0, 3, 4,
5 and 6. """
cr, uid = self.cr, self.uid