[CLEAN] mail: cleaned and updated automatic followers, now also possible on write without having to add something specific. parent_field -> relation_field on message.subtype model. Templates of automatic tracking do not use mako anymore, not necessary for small messages. Added a tmp hack in mail_message to avoid a crash, do not forget to remove it.

bzr revid: tde@openerp.com-20121219164239-3vbj4zfqv4j6ct32
This commit is contained in:
Thibault Delavallée 2012-12-19 17:42:39 +01:00
parent 91e98f9d17
commit 20863bd787
4 changed files with 72 additions and 59 deletions

View File

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

View File

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

View File

@ -27,7 +27,7 @@
<field name="default"/>
<field name="description"/>
<field name="parent_id"/>
<field name="parent_field"/>
<field name="relation_field"/>
</group>
</sheet>
</form>

View File

@ -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:
<span>${message_description}</span>
%endif
%for name, change in tracked_values.items():
<div>
&nbsp; &nbsp; &bull; <b>${change.get('col_info')}</b>:
%if change.get('old_value'):
${change.get('old_value')} &rarr;
%endif
${change.get('new_value')}
</div>
%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 = '<span>%s</span>' % message_description
for name, change in tracked_values.items():
message += '<div> &nbsp; &nbsp; &bull; <b>%s</b>: ' % change.get('col_info')
if change.get('old_value'):
message += '%s &rarr; ' % change.get('old_value')
message += '%s</div>' % 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 <b>modified</b>', 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