summaryrefslogtreecommitdiff
path: root/addons/mail
diff options
context:
space:
mode:
authorChristophe Simonis <chs@openerp.com>2013-11-25 16:00:54 +0100
committerChristophe Simonis <chs@openerp.com>2013-11-25 16:00:54 +0100
commitbb1e256f5b24bb4fc4b64e1fa973f86a30e4bace (patch)
tree95f1de1c67b507b0d10786d04aa0f98bcd22243a /addons/mail
parentf148061b9455e39d3b3e6f316a5c7d586e1aad88 (diff)
parent7baee2822180db919e6754f04c0fa9bc8aaafe92 (diff)
[MERGE] forward port of branch saas-2 up to revid 9010 dle@openerp.com-20131125143003-vjtzj1c5051pdnk3
bzr revid: chs@openerp.com-20131125150054-b135qshkx6icakgk
Diffstat (limited to 'addons/mail')
-rw-r--r--addons/mail/mail_message.py15
-rw-r--r--addons/mail/mail_thread.py157
-rw-r--r--addons/mail/tests/test_mail_features.py4
3 files changed, 102 insertions, 74 deletions
diff --git a/addons/mail/mail_message.py b/addons/mail/mail_message.py
index 72ccdc83bb6..de8e4c68fda 100644
--- a/addons/mail/mail_message.py
+++ b/addons/mail/mail_message.py
@@ -698,8 +698,9 @@ class mail_message(osv.Model):
"""
model_record_ids = {}
for id in msg_ids:
- if msg_val[id]['model'] and msg_val[id]['res_id']:
- model_record_ids.setdefault(msg_val[id]['model'], dict()).setdefault(msg_val[id]['res_id'], set()).add(msg_val[id]['res_id'])
+ vals = msg_val.get(id, {})
+ if vals.get('model') and vals.get('res_id'):
+ model_record_ids.setdefault(vals['model'], set()).add(vals['res_id'])
return model_record_ids
if uid == SUPERUSER_ID:
@@ -711,7 +712,7 @@ class mail_message(osv.Model):
partner_id = self.pool['res.users'].browse(cr, SUPERUSER_ID, uid, context=None).partner_id.id
# Read mail_message.ids to have their values
- message_values = dict.fromkeys(ids)
+ message_values = dict.fromkeys(ids, {})
cr.execute('SELECT DISTINCT id, model, res_id, author_id, parent_id FROM "%s" WHERE id = ANY (%%s)' % self._table, (ids,))
for id, rmod, rid, author_id, parent_id in cr.fetchall():
message_values[id] = {'model': rmod, 'res_id': rid, 'author_id': author_id, 'parent_id': parent_id}
@@ -745,10 +746,10 @@ class mail_message(osv.Model):
], context=context)
notified_ids = [notification.message_id.id for notification in not_obj.browse(cr, SUPERUSER_ID, not_ids, context=context)]
elif operation == 'create':
- for doc_model, doc_dict in model_record_ids.items():
+ for doc_model, doc_ids in model_record_ids.items():
fol_ids = fol_obj.search(cr, SUPERUSER_ID, [
('res_model', '=', doc_model),
- ('res_id', 'in', list(doc_dict.keys())),
+ ('res_id', 'in', list(doc_ids)),
('partner_id', '=', partner_id),
], context=context)
fol_mids = [follower.res_id for follower in fol_obj.browse(cr, SUPERUSER_ID, fol_ids, context=context)]
@@ -759,9 +760,9 @@ class mail_message(osv.Model):
other_ids = other_ids.difference(set(notified_ids))
model_record_ids = _generate_model_record_ids(message_values, other_ids)
document_related_ids = []
- for model, doc_dict in model_record_ids.items():
+ for model, doc_ids in model_record_ids.items():
model_obj = self.pool[model]
- mids = model_obj.exists(cr, uid, doc_dict.keys())
+ mids = model_obj.exists(cr, uid, list(doc_ids))
if hasattr(model_obj, 'check_mail_message_access'):
model_obj.check_mail_message_access(cr, uid, mids, operation, context=context)
else:
diff --git a/addons/mail/mail_thread.py b/addons/mail/mail_thread.py
index a5d6d9a3614..dfee2a04d4d 100644
--- a/addons/mail/mail_thread.py
+++ b/addons/mail/mail_thread.py
@@ -38,6 +38,7 @@ from openerp import tools
from openerp import SUPERUSER_ID
from openerp.addons.mail.mail_message import decode
from openerp.osv import fields, osv, orm
+from openerp.osv.orm import browse_record, browse_null
from openerp.tools.safe_eval import safe_eval as eval
from openerp.tools.translate import _
@@ -355,11 +356,11 @@ class mail_thread(osv.AbstractModel):
if not context.get('mail_create_nosubscribe'):
self.message_subscribe_users(cr, uid, [thread_id], [uid], context=context)
# auto_subscribe: take values and defaults into account
- create_values = set(values.keys())
+ create_values = dict(values)
for key, val in context.iteritems():
if key.startswith('default_'):
- create_values.add(key[8:])
- self.message_auto_subscribe(cr, uid, [thread_id], list(create_values), context=context)
+ create_values[key[8:]] = val
+ self.message_auto_subscribe(cr, uid, [thread_id], create_values.keys(), context=context, values=create_values)
# track values
tracked_fields = self._get_tracked_fields(cr, uid, values.keys(), context=context)
@@ -380,7 +381,7 @@ class mail_thread(osv.AbstractModel):
# Perform write, update followers
result = super(mail_thread, self).write(cr, uid, ids, values, context=context)
- self.message_auto_subscribe(cr, uid, ids, values.keys(), context=context)
+ self.message_auto_subscribe(cr, uid, ids, values.keys(), context=context, values=values)
# Perform the tracking
if tracked_fields:
@@ -604,7 +605,8 @@ class mail_thread(osv.AbstractModel):
return action
if msg_id and not (model and res_id):
msg = self.pool.get('mail.message').browse(cr, uid, msg_id, context=context)
- model, res_id = msg.model, msg.res_id
+ if msg.exists():
+ model, res_id = msg.model, msg.res_id
# if model + res_id found: try to redirect to the document or fallback on the Inbox
if model and res_id:
@@ -1565,75 +1567,100 @@ class mail_thread(osv.AbstractModel):
user_field_lst.append(name)
return user_field_lst
- def message_auto_subscribe(self, cr, uid, ids, updated_fields, context=None):
- """
- 1. fetch project subtype related to task (parent_id.res_model = 'project.task')
- 2. for each project subtype: subscribe the follower to the task
+ def message_auto_subscribe(self, cr, uid, ids, updated_fields, context=None, values=None):
+ """ Handle auto subscription. Two methods for auto subscription exist:
+
+ - tracked res.users relational fields, such as user_id fields. Those fields
+ must be relation fields toward a res.users record, and must have the
+ track_visilibity attribute set.
+ - using subtypes parent relationship: check if the current model being
+ modified has an header record (such as a project for tasks) whose followers
+ can be added as followers of the current records. Example of structure
+ with project and task:
+
+ - st_project_1.parent_id = st_task_1
+ - st_project_1.res_model = 'project.project'
+ - st_project_1.relation_field = 'project_id'
+ - st_task_1.model = 'project.task'
+
+ :param list updated_fields: list of updated fields to track
+ :param dict values: updated values; if None, the first record will be browsed
+ to get the values. Added after releasing 7.0, therefore
+ not merged with updated_fields argumment.
"""
subtype_obj = self.pool.get('mail.message.subtype')
follower_obj = self.pool.get('mail.followers')
+ new_followers = dict()
- # fetch auto_follow_fields
+ # fetch auto_follow_fields: res.users relation fields whose changes are tracked for subscription
user_field_lst = self._message_get_auto_subscribe_fields(cr, uid, updated_fields, context=context)
- # fetch related record subtypes
- related_subtype_ids = subtype_obj.search(cr, uid, ['|', ('res_model', '=', False), ('parent_id.res_model', '=', self._name)], context=context)
- subtypes = subtype_obj.browse(cr, uid, related_subtype_ids, context=context)
- default_subtypes = [subtype for subtype in subtypes if subtype.res_model == False]
- related_subtypes = [subtype for subtype in subtypes if subtype.res_model != False]
- relation_fields = set([subtype.relation_field for subtype in subtypes if subtype.relation_field != False])
- if (not related_subtypes or not any(relation in updated_fields for relation in relation_fields)) and not user_field_lst:
+ # fetch header subtypes
+ header_subtype_ids = subtype_obj.search(cr, uid, ['|', ('res_model', '=', False), ('parent_id.res_model', '=', self._name)], context=context)
+ subtypes = subtype_obj.browse(cr, uid, header_subtype_ids, context=context)
+
+ # if no change in tracked field or no change in tracked relational field: quit
+ relation_fields = set([subtype.relation_field for subtype in subtypes if subtype.relation_field is not False])
+ if not any(relation in updated_fields for relation in relation_fields) and not user_field_lst:
return True
- for record in self.browse(cr, uid, ids, context=context):
- new_followers = dict()
- parent_res_id = False
- parent_model = False
- for subtype in related_subtypes:
- if not subtype.relation_field or not subtype.parent_id:
- continue
- if not subtype.relation_field in self._columns or not getattr(record, subtype.relation_field, False):
- continue
- parent_res_id = getattr(record, subtype.relation_field).id
- parent_model = subtype.res_model
- follower_ids = follower_obj.search(cr, SUPERUSER_ID, [
- ('res_model', '=', parent_model),
- ('res_id', '=', parent_res_id),
- ('subtype_ids', 'in', [subtype.id])
- ], context=context)
- for follower in follower_obj.browse(cr, SUPERUSER_ID, follower_ids, context=context):
- new_followers.setdefault(follower.partner_id.id, set()).add(subtype.parent_id.id)
-
- if parent_res_id and parent_model:
- for subtype in default_subtypes:
- follower_ids = follower_obj.search(cr, SUPERUSER_ID, [
- ('res_model', '=', parent_model),
- ('res_id', '=', parent_res_id),
- ('subtype_ids', 'in', [subtype.id])
- ], context=context)
- for follower in follower_obj.browse(cr, SUPERUSER_ID, follower_ids, context=context):
- new_followers.setdefault(follower.partner_id.id, set()).add(subtype.id)
-
- # add followers coming from res.users relational fields that are tracked
- user_ids = [getattr(record, name).id for name in user_field_lst if getattr(record, name)]
- user_id_partner_ids = [user.partner_id.id for user in self.pool.get('res.users').browse(cr, SUPERUSER_ID, user_ids, context=context)]
- for partner_id in user_id_partner_ids:
- new_followers.setdefault(partner_id, None)
-
- for pid, subtypes in new_followers.items():
- subtypes = list(subtypes) if subtypes is not None else None
- self.message_subscribe(cr, uid, [record.id], [pid], subtypes, context=context)
-
- # find first email message, set it as unread for auto_subscribe fields for them to have a notification
- if user_id_partner_ids:
- msg_ids = self.pool.get('mail.message').search(cr, uid, [
- ('model', '=', self._name),
- ('res_id', '=', record.id),
- ('type', '=', 'email')], limit=1, context=context)
- if not msg_ids and record.message_ids:
- msg_ids = [record.message_ids[-1].id]
+ # legacy behavior: if values is not given, compute the values by browsing
+ # @TDENOTE: remove me in 8.0
+ if values is None:
+ record = self.browse(cr, uid, ids[0], context=context)
+ for updated_field in updated_fields:
+ field_value = getattr(record, updated_field)
+ if isinstance(field_value, browse_record):
+ field_value = field_value.id
+ elif isinstance(field_value, browse_null):
+ field_value = False
+ values[updated_field] = field_value
+
+ # find followers of headers, update structure for new followers
+ headers = set()
+ for subtype in subtypes:
+ if subtype.relation_field and values.get(subtype.relation_field):
+ headers.add((subtype.res_model, values.get(subtype.relation_field)))
+ if headers:
+ header_domain = ['|'] * (len(headers) - 1)
+ for header in headers:
+ header_domain += ['&', ('res_model', '=', header[0]), ('res_id', '=', header[1])]
+ header_follower_ids = follower_obj.search(
+ cr, SUPERUSER_ID,
+ header_domain,
+ context=context
+ )
+ for header_follower in follower_obj.browse(cr, SUPERUSER_ID, header_follower_ids, context=context):
+ for subtype in header_follower.subtype_ids:
+ if subtype.parent_id and subtype.parent_id.res_model == self._name:
+ new_followers.setdefault(header_follower.partner_id.id, set()).add(subtype.parent_id.id)
+ elif subtype.res_model is False:
+ new_followers.setdefault(header_follower.partner_id.id, set()).add(subtype.id)
+
+ # add followers coming from res.users relational fields that are tracked
+ user_ids = [values[name] for name in user_field_lst if values.get(name)]
+ user_pids = [user.partner_id.id for user in self.pool.get('res.users').browse(cr, SUPERUSER_ID, user_ids, context=context)]
+ for partner_id in user_pids:
+ new_followers.setdefault(partner_id, None)
+
+ for pid, subtypes in new_followers.items():
+ subtypes = list(subtypes) if subtypes is not None else None
+ self.message_subscribe(cr, uid, ids, [pid], subtypes, context=context)
+
+ # find first email message, set it as unread for auto_subscribe fields for them to have a notification
+ if user_pids:
+ for record_id in ids:
+ message_obj = self.pool.get('mail.message')
+ msg_ids = message_obj.search(cr, SUPERUSER_ID, [
+ ('model', '=', self._name),
+ ('res_id', '=', record_id),
+ ('type', '=', 'email')], limit=1, context=context)
+ if not msg_ids:
+ msg_ids = message_obj.search(cr, SUPERUSER_ID, [
+ ('model', '=', self._name),
+ ('res_id', '=', record_id)], limit=1, context=context)
if msg_ids:
- self.pool.get('mail.notification')._notify(cr, uid, msg_ids[0], partners_to_notify=user_id_partner_ids, context=context)
+ self.pool.get('mail.notification')._notify(cr, uid, msg_ids[0], partners_to_notify=user_pids, context=context)
return True
diff --git a/addons/mail/tests/test_mail_features.py b/addons/mail/tests/test_mail_features.py
index 62c76db5a40..c7f1056c59a 100644
--- a/addons/mail/tests/test_mail_features.py
+++ b/addons/mail/tests/test_mail_features.py
@@ -294,9 +294,9 @@ class test_mail(TestMail):
'notification email: link should contain the user login')
self.assertIn('message_id=%s' % mail.mail_message_id.id, url,
'notification email: link based on message should contain the mail_message id')
- self.assertNotIn('model', url,
+ self.assertNotIn('model=mail.group', url,
'notification email: link based on message should not contain model')
- self.assertNotIn('res_id', url,
+ self.assertNotIn('res_id=%s' % group_pigs.id, url,
'notification email: link based on message should not contain res_id')
@mute_logger('openerp.addons.mail.mail_thread', 'openerp.osv.orm')