diff --git a/addons/mail/mail_message.py b/addons/mail/mail_message.py
index d545f3782b4..7ff6e749ad8 100644
--- a/addons/mail/mail_message.py
+++ b/addons/mail/mail_message.py
@@ -350,9 +350,15 @@ class mail_message(osv.Model):
vote_nb = len(message.vote_user_ids)
has_voted = uid in [user.id for user in message.vote_user_ids]
+ # TDE tmp hack
+ if not message.body:
+ body = ''
+ else:
+ body = html_email_clean(message.body or '')
+
return {'id': message.id,
'type': message.type,
- 'body': html_email_clean(message.body or ''),
+ 'body': body,
'model': message.model,
'res_id': message.res_id,
'record_name': message.record_name,
diff --git a/addons/mail/mail_message_subtype.py b/addons/mail/mail_message_subtype.py
index d6d983611a0..f4383ff9957 100644
--- a/addons/mail/mail_message_subtype.py
+++ b/addons/mail/mail_message_subtype.py
@@ -41,8 +41,8 @@ class mail_message_subtype(osv.osv):
'subtype. If void, no message will be added.'),
'parent_id': fields.many2one('mail.message.subtype', string='Parent',
ondelete='set null'),
- 'parent_field': fields.char('Parent field',
- help='Field used to find the parent model'),
+ 'relation_field': fields.char('Relation field',
+ help='Field used to link the related model to the subtype model'),
'res_model': fields.char('Model',
help="Model the subtype applies to. If False, this subtype exists for all models."),
'default': fields.boolean('Default',
diff --git a/addons/mail/mail_message_subtype.xml b/addons/mail/mail_message_subtype.xml
index da138858446..a79f5815bd1 100644
--- a/addons/mail/mail_message_subtype.xml
+++ b/addons/mail/mail_message_subtype.xml
@@ -27,7 +27,7 @@
-
+
diff --git a/addons/mail/mail_thread.py b/addons/mail/mail_thread.py
index ace8394314f..94011163574 100644
--- a/addons/mail/mail_thread.py
+++ b/addons/mail/mail_thread.py
@@ -30,7 +30,6 @@ from openerp import tools
import xmlrpclib
from email.message import Message
-from mako.template import Template as MakoTemplate
from openerp import SUPERUSER_ID
from openerp.addons.mail.mail_message import decode
@@ -88,20 +87,6 @@ class mail_thread(osv.AbstractModel):
# :param obj: is a browse_record
# :param function lambda: returns whether the tracking should record using this subtype
_track = {}
- _TRACK_TEMPLATE = """
- %if message_description:
- ${message_description}
- %endif
- %for name, change in tracked_values.items():
-
- • ${change.get('col_info')}:
- %if change.get('old_value'):
- ${change.get('old_value')} →
- %endif
- ${change.get('new_value')}
-
- %endfor
- """
def _get_message_data(self, cr, uid, ids, name, args, context=None):
""" Computes:
@@ -258,7 +243,7 @@ class mail_thread(osv.AbstractModel):
# subscribe uid unless asked not to
if not context.get('mail_nosubscribe'):
self.message_subscribe_users(cr, uid, [thread_id], [uid], context=context)
- self.message_subscribe_from_parent(cr, uid, [thread_id], context=context)
+ self.message_subscribe_from_parent(cr, uid, [thread_id], values.keys(), context=context)
# automatic logging unless asked not to (mainly for various testing purpose)
if not context.get('mail_nolog'):
@@ -268,11 +253,17 @@ class mail_thread(osv.AbstractModel):
def write(self, cr, uid, ids, values, context=None):
if isinstance(ids, (int, long)):
ids = [ids]
+ # Track initial values of tracked fields
tracked_fields = self._get_tracked_fields(cr, uid, values.keys(), context=context)
if tracked_fields:
initial = self.read(cr, uid, ids, tracked_fields.keys(), context=context)
initial_values = dict((item['id'], item) for item in initial)
+
+ # Perform write, update followers
result = super(mail_thread, self).write(cr, uid, ids, values, context=context)
+ self.message_subscribe_from_parent(cr, uid, ids, values.keys(), context=context)
+
+ # Perform the tracking
if tracked_fields:
self.message_track(cr, uid, ids, tracked_fields, initial_values, context=context)
return result
@@ -328,6 +319,17 @@ class mail_thread(osv.AbstractModel):
return dict(field_obj['selection'])[value]
return value
+ def format_message(message_description, tracked_values):
+ message = ''
+ if message_description:
+ message = '%s' % message_description
+ for name, change in tracked_values.items():
+ message += ' • %s: ' % change.get('col_info')
+ if change.get('old_value'):
+ message += '%s → ' % change.get('old_value')
+ message += '%s
' % change.get('new_value')
+ return message
+
if not tracked_fields:
return True
@@ -367,11 +369,11 @@ class mail_thread(osv.AbstractModel):
except ValueError, e:
_logger.debug('subtype %s not found, giving error "%s"' % (subtype, e))
continue
- message = MakoTemplate(self._TRACK_TEMPLATE).render_unicode(message_description=subtype_rec.description, tracked_values=tracked_values)
+ message = format_message(subtype_rec.description, tracked_values)
self.message_post(cr, uid, record['id'], body=message, subtype=subtype, context=context)
posted = True
if not posted:
- message = MakoTemplate(self._TRACK_TEMPLATE).render_unicode(message_description='Document modified', tracked_values=tracked_values)
+ message = format_message('', tracked_values)
self.message_post(cr, uid, record['id'], body=message, context=context)
return True
@@ -966,52 +968,57 @@ class mail_thread(osv.AbstractModel):
self.check_access_rights(cr, uid, 'read')
return self.write(cr, SUPERUSER_ID, ids, {'message_follower_ids': [(3, pid) for pid in partner_ids]}, context=context)
- def message_subscribe_from_parent(self, cr, uid, ids, context=None):
-
+ def message_subscribe_from_parent(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
+ """
subtype_obj = self.pool.get('mail.message.subtype')
follower_obj = self.pool.get('mail.followers')
- # fetch record subtypes
- subtype_ids = subtype_obj.search(cr, uid, ['|', ('parent_id.res_model', '=', False), ('parent_id.res_model', '=', self._name)], context=context)
- if not subtype_ids:
- return
- subtypes = subtype_obj.browse(cr, uid, subtype_ids, 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):
+ return True
for record in self.browse(cr, uid, ids, context=context):
new_followers = dict()
- for subtype in subtypes:
- if subtype.parent_field and subtype.parent_id:
- if subtype.parent_field in self._columns and getattr(record, subtype.parent_field):
- parent_res_id = getattr(record, subtype.parent_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)
+ 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 not parent_res_id or not parent_model:
+ continue
+
+ 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)
for pid, subtypes in new_followers.items():
self.message_subscribe(cr, uid, [record.id], [pid], list(subtypes), context=context)
-
- def _subscribe_followers_subtype(self, cr, uid, ids, res_id, model, context=None):
- """ TDE note: not the best way to do this, we could override _get_followers
- of task, and perform a better mapping of subtypes than a mapping
- based on names.
- However we will keep this implementation, maybe to be refactored
- in 7.1 of future versions. """
- subtype_obj = self.pool.get('mail.message.subtype')
- follower_obj = self.pool.get('mail.followers')
- # create mapping
- subtype_ids = subtype_obj.search(cr, uid, ['|', ('res_model', '=', False), ('res_model', '=', self._name)], context=context)
- subtypes = subtype_obj.browse(cr, uid, subtype_ids, context=context)
- # fetch subscriptions
- follower_ids = follower_obj.search(cr, uid, [('res_model', '=', model), ('res_id', '=', res_id)], context=context)
- # copy followers
- for follower in follower_obj.browse(cr, uid, follower_ids, context=context):
- if not follower.subtype_ids:
- continue
- subtype_names = [follower_subtype.name for follower_subtype in follower.subtype_ids]
- subtype_ids = [subtype.id for subtype in subtypes if subtype.name in subtype_names]
- self.message_subscribe(cr, uid, ids, [follower.partner_id.id],
- subtype_ids=subtype_ids, context=context)
+ return True
#------------------------------------------------------
# Thread state