summaryrefslogtreecommitdiff
path: root/addons/mail/mail_thread.py
diff options
context:
space:
mode:
authorVijaykumar Baladaniya <vba@tinyerp.com>2012-07-09 18:31:13 +0530
committerVijaykumar Baladaniya <vba@tinyerp.com>2012-07-09 18:31:13 +0530
commitbe2e95656aaa113addea2b510fcdcf2f890eaaa4 (patch)
treeabcb3dd4ef350a1b8ad0eaffdd2b9a1cb91521af /addons/mail/mail_thread.py
parentb982ea278b33132def3927417b7a854b98b5028a (diff)
parent3925f76ce85854e5d4834902a05f57bcfc5ace85 (diff)
[MERGE]merged with trunk-survey-layout-tta
bzr revid: vba@tinyerp.com-20120709130113-3lchipgil9oqpglg
Diffstat (limited to 'addons/mail/mail_thread.py')
-rw-r--r--addons/mail/mail_thread.py195
1 files changed, 143 insertions, 52 deletions
diff --git a/addons/mail/mail_thread.py b/addons/mail/mail_thread.py
index 0f404155f42..5668709af17 100644
--- a/addons/mail/mail_thread.py
+++ b/addons/mail/mail_thread.py
@@ -34,7 +34,7 @@ import xmlrpclib
_logger = logging.getLogger(__name__)
-class mail_thread(osv.osv):
+class mail_thread(osv.Model):
'''Mixin model, meant to be inherited by any model that needs to
act as a discussion topic on which messages can be attached.
Public methods are prefixed with ``message_`` in order to avoid
@@ -62,16 +62,39 @@ class mail_thread(osv.osv):
_name = 'mail.thread'
_description = 'Email Thread'
- def _get_message_ids(self, cr, uid, ids, name, arg, context=None):
+ def _get_message_ids(self, cr, uid, ids, name, args, context=None):
res = {}
for id in ids:
- res[id] = self.message_load_ids(cr, uid, [id], context=context)
+ message_ids = self.message_load_ids(cr, uid, [id], context=context)
+ subscriber_ids = self.message_get_subscribers(cr, uid, [id], context=context)
+ res[id] = {
+ 'message_ids': message_ids,
+ 'message_summary': "<span>Msg: %d</span> . <span>Fol: %d</span>" % (len(message_ids), len(subscriber_ids)),
+ }
return res
- # OpenChatter: message_ids is a dummy field that should not be used
+ def _search_message_ids(self, cr, uid, obj, name, args, context=None):
+ msg_obj = self.pool.get('mail.message')
+ msg_ids = msg_obj.search(cr, uid, ['&', ('res_id', 'in', args[0][2]), ('model', '=', self._name)], context=context)
+ return [('id', 'in', msg_ids)]
+
_columns = {
'message_ids': fields.function(_get_message_ids, method=True,
- type='one2many', obj='mail.message', string='Temp messages', _fields_id = 'res_id'),
+ fnct_search=_search_message_ids,
+ type='one2many', obj='mail.message', _fields_id = 'res_id',
+ string='Temp messages', multi="_get_message_ids",
+ help="Functional field holding messages related to the current document."),
+ 'message_state': fields.boolean('Read',
+ help="When checked, new messages require your attention."),
+ 'message_summary': fields.function(_get_message_ids, method=True,
+ type='text', string='Summary', multi="_get_message_ids",
+ help="Holds the Chatter summary (number of messages, ...). "\
+ "This summary is directly in html format in order to "\
+ "be inserted in kanban views."),
+ }
+
+ _defaults = {
+ 'message_state': True,
}
#------------------------------------------------------
@@ -79,10 +102,11 @@ class mail_thread(osv.osv):
#------------------------------------------------------
def create(self, cr, uid, vals, context=None):
- """Automatically subscribe the creator"""
- thread_id = super(mail_thread, self).create(cr, uid, vals, context=context);
- self.message_subscribe(cr, uid, [thread_id], [uid], context=context)
- return thread_id;
+ """Automatically subscribe the creator """
+ thread_id = super(mail_thread, self).create(cr, uid, vals, context=context)
+ if thread_id:
+ self.message_subscribe(cr, uid, [thread_id], [uid], context=context)
+ return thread_id
def write(self, cr, uid, ids, vals, context=None):
"""Automatically subscribe the writer"""
@@ -101,8 +125,6 @@ class mail_thread(osv.osv):
a foreign key with a 'cascade' ondelete attribute.
Notifications will be deleted with messages
"""
- if context is None:
- context = {}
subscr_obj = self.pool.get('mail.subscription')
msg_obj = self.pool.get('mail.message')
# delete subscriptions
@@ -115,17 +137,18 @@ class mail_thread(osv.osv):
return super(mail_thread, self).unlink(cr, uid, ids, context=context)
#------------------------------------------------------
- # Generic message api
+ # mail.message wrappers and tools
#------------------------------------------------------
def message_create(self, cr, uid, thread_id, vals, context=None):
- """OpenSocial: wrapper of mail.message create method
+ """ OpenChatter: wrapper of mail.message create method
- creates the mail.message
- automatically subscribe the message writer
- push the message to subscribed users
"""
if context is None:
context = {}
+
message_obj = self.pool.get('mail.message')
subscription_obj = self.pool.get('mail.subscription')
notification_obj = self.pool.get('mail.notification')
@@ -162,7 +185,10 @@ class mail_thread(osv.osv):
# create message
msg_id = message_obj.create(cr, uid, vals, context=context)
-
+
+ # Set as unread if writer is not the document responsible
+ self.message_create_set_unread(cr, uid, [thread_id], context=context)
+
# special: if install mode, do not push demo data
if context.get('install_mode', False):
return True
@@ -181,7 +207,7 @@ class mail_thread(osv.osv):
body = new_msg_vals.get('body_html', '') if new_msg_vals.get('subtype', 'plain') == 'html' else new_msg_vals.get('body_text', '')
for thread_id in thread_ids:
# add subscribers
- notif_user_ids += [user['id'] for user in self.message_get_subscribers(cr, uid, [thread_id], context=context)]
+ notif_user_ids += self.message_get_subscribers(cr, uid, [thread_id], context=context)
# add users requested via parsing message (@login)
notif_user_ids += self.message_parse_users(cr, uid, [thread_id], body, context=context)
# add users requested to perform an action (need_action mechanism)
@@ -209,6 +235,10 @@ class mail_thread(osv.osv):
user_ids = self.pool.get('res.users').search(cr, uid, [('login', 'in', login_lst)], context=context)
return user_ids
+ #------------------------------------------------------
+ # Generic message api
+ #------------------------------------------------------
+
def message_capable_models(self, cr, uid, context=None):
ret_dict = {}
for model_name in self.pool.obj_list():
@@ -294,7 +324,7 @@ class mail_thread(osv.osv):
}
to_attach.append(ir_attachment.create(cr, uid, data_attach, context=context))
- partner_id = hasattr(thread, 'partner_id') and (thread.partner_id and thread.partner_id.id or False) or False
+ partner_id = ('partner_id' in thread._columns.keys()) and (thread.partner_id and thread.partner_id.id or False) or False
if not partner_id and thread._name == 'res.partner':
partner_id = thread.id
data = {
@@ -371,7 +401,6 @@ class mail_thread(osv.osv):
original = msg_dict.get('original'),
context = context)
- # Message loading
def _message_add_ancestor_ids(self, cr, uid, ids, child_ids, root_ids, context=None):
""" Given message child_ids
Find their ancestors until root ids"""
@@ -415,6 +444,9 @@ class mail_thread(osv.osv):
msg_ids = self.message_load_ids(cr, uid, ids, limit, offset, domain, ascent, root_ids, context=context)
msgs = self.pool.get('mail.message').read(cr, uid, msg_ids, [], context=context)
+ # Set as read
+ self.message_check_and_set_read(cr, uid, ids, context=context)
+
""" Retrieve all attachments names """
map_id_to_name = {}
@@ -469,7 +501,7 @@ class mail_thread(osv.osv):
return msgs
#------------------------------------------------------
- # Email specific
+ # Mail gateway
#------------------------------------------------------
# message_process will call either message_new or message_update.
@@ -544,7 +576,7 @@ class mail_thread(osv.osv):
if res_id:
res_id = res_id.group(1)
if res_id:
- res_id = int(res_id)
+ res_id = res_id
if model_pool.exists(cr, uid, res_id):
if hasattr(model_pool, 'message_update'):
model_pool.message_update(cr, uid, [res_id], msg, {}, context=context)
@@ -553,7 +585,7 @@ class mail_thread(osv.osv):
res_id = False
if not res_id:
res_id = create_record(msg)
- #To forward the email to other followers
+ # To forward the email to other followers
self.message_forward(cr, uid, model, [res_id], msg_txt, context=context)
return res_id
@@ -589,29 +621,34 @@ class mail_thread(osv.osv):
fields = model_pool.fields_get(cr, uid, context=context)
data = model_pool.default_get(cr, uid, fields, context=context)
if 'name' in fields and not data.get('name'):
- data['name'] = msg_dict.get('from','')
+ data['name'] = msg_dict.get('from', '')
if custom_values and isinstance(custom_values, dict):
data.update(custom_values)
res_id = model_pool.create(cr, uid, data, context=context)
self.message_append_dict(cr, uid, [res_id], msg_dict, context=context)
return res_id
- def message_update(self, cr, uid, ids, msg_dict, vals={}, default_act=None, context=None):
- """Called by ``message_process`` when a new message is received
- for an existing thread. The default behavior is to create a
- new mail.message in the given thread (by calling
- ``message_append_dict``)
- Additional behavior may be implemented by overriding this
- method.
+ def message_update(self, cr, uid, ids, msg_dict, update_vals=None, context=None):
+ """ Called by ``message_process`` when a new message is received
+ for an existing thread. The default behavior is to create a
+ new mail.message in the given thread (by calling
+ ``message_append_dict``)
+ Additional behavior may be implemented by overriding this
+ method.
- :param dict msg_dict: a map containing the email details and
+ :param dict msg_dict: a map containing the email details and
attachments. See ``message_process`` and
``mail.message.parse()`` for details.
- :param dict context: if a ``thread_model`` value is present
+ :param dict vals: a dict containing values to update records
+ given their ids; if the dict is None or is
+ void, no write operation is performed.
+ :param dict context: if a ``thread_model`` value is present
in the context, its value will be used
to determine the model of the thread to
update (instead of the current model).
"""
+ if update_vals:
+ self.write(cr, uid, ids, update_vals, context=context)
return self.message_append_dict(cr, uid, ids, msg_dict, context=context)
def message_thread_followers(self, cr, uid, ids, context=None):
@@ -741,13 +778,8 @@ class mail_thread(osv.osv):
self.message_append_note(cr, uid, [id], 'res.log', message, context=context)
def message_append_note(self, cr, uid, ids, subject=None, body=None, parent_id=False, type='notification', subtype='html', context=None):
- if subject is None:
- if type == 'notification':
- subject = _('System notification')
- elif type == 'comment' and not parent_id:
- subject = _('Comment')
- elif type == 'comment' and parent_id:
- subject = _('Reply')
+ if type in ['notification', 'reply']:
+ subject = None
if subtype == 'html':
body_html = body
body_text = body
@@ -760,51 +792,77 @@ class mail_thread(osv.osv):
# Subscription mechanism
#------------------------------------------------------
- def message_get_subscribers_ids(self, cr, uid, ids, context=None):
+ def message_get_subscribers(self, cr, uid, ids, context=None):
+ """ Returns the current document followers. Basically this method
+ checks in mail.subscription for entries with matching res_model,
+ res_id.
+
+ :param get_ids: if set to True, return the ids of users; if set
+ to False, returns the result of a read in res.users
+ """
subscr_obj = self.pool.get('mail.subscription')
subscr_ids = subscr_obj.search(cr, uid, ['&', ('res_model', '=', self._name), ('res_id', 'in', ids)], context=context)
- subs = subscr_obj.read(cr, uid, subscr_ids, context=context)
- return [sub['user_id'][0] for sub in subs]
+ return [sub['user_id'][0] for sub in subscr_obj.read(cr, uid, subscr_ids, ['user_id'], context=context)]
- def message_get_subscribers(self, cr, uid, ids, context=None):
- user_ids = self.message_get_subscribers_ids(cr, uid, ids, context=context)
- users = self.pool.get('res.users').read(cr, uid, user_ids, fields=['id', 'name', 'avatar'], context=context)
- return users
+ def message_read_subscribers(self, cr, uid, ids, fields=['id', 'name', 'avatar'], context=None):
+ """ Returns the current document followers as a read result. Used
+ mainly for Chatter having only one method to call to have
+ details about users.
+ """
+ user_ids = self.message_get_subscribers(cr, uid, ids, context=context)
+ return self.pool.get('res.users').read(cr, uid, user_ids, fields=fields, context=context)
def message_is_subscriber(self, cr, uid, ids, user_id = None, context=None):
- users = self.message_get_subscribers(cr, uid, ids, context=context)
+ """ Check if uid or user_id (if set) is a subscriber to the current
+ document.
+
+ :param user_id: if set, check is done on user_id; if not set
+ check is done on uid
+ """
sub_user_id = uid if user_id is None else user_id
- if sub_user_id in [user['id'] for user in users]:
+ if sub_user_id in self.message_get_subscribers(cr, uid, ids, context=context):
return True
return False
def message_subscribe(self, cr, uid, ids, user_ids = None, context=None):
+ """ Subscribe the user (or user_ids) to the current document.
+
+ :param user_ids: a list of user_ids; if not set, subscribe
+ uid instead
+ """
subscription_obj = self.pool.get('mail.subscription')
to_subscribe_uids = [uid] if user_ids is None else user_ids
create_ids = []
for id in ids:
+ already_subscribed_user_ids = self.message_get_subscribers(cr, uid, [id], context=context)
for user_id in to_subscribe_uids:
- if self.message_is_subscriber(cr, uid, [id], user_id=user_id, context=context): continue
+ if user_id in already_subscribed_user_ids: continue
create_ids.append(subscription_obj.create(cr, uid, {'res_model': self._name, 'res_id': id, 'user_id': user_id}, context=context))
return create_ids
def message_unsubscribe(self, cr, uid, ids, user_ids = None, context=None):
- if not user_ids and not uid in self.message_get_subscribers_ids(cr, uid, ids, context=context):
+ """ Unsubscribe the user (or user_ids) from the current document.
+
+ :param user_ids: a list of user_ids; if not set, subscribe
+ uid instead
+ """
+ # Trying to unsubscribe somebody not in subscribers: returns False
+ # if special management is needed; allows to know that an automatically
+ # subscribed user tries to unsubscribe and allows to warn him
+ mail_thread_model = self.pool.get('mail.thread')
+ if not user_ids and not uid in mail_thread_model.message_get_subscribers(cr, uid, ids, context=context):
return False
subscription_obj = self.pool.get('mail.subscription')
to_unsubscribe_uids = [uid] if user_ids is None else user_ids
to_delete_sub_ids = subscription_obj.search(cr, uid,
['&', '&', ('res_model', '=', self._name), ('res_id', 'in', ids), ('user_id', 'in', to_unsubscribe_uids)], context=context)
- subscription_obj.unlink(cr, uid, to_delete_sub_ids, context=context)
- return True
+ return subscription_obj.unlink(cr, uid, to_delete_sub_ids, context=context)
#------------------------------------------------------
# Notification API
#------------------------------------------------------
def message_remove_pushed_notifications(self, cr, uid, ids, msg_ids, remove_childs=True, context=None):
- if context is None:
- context = {}
notif_obj = self.pool.get('mail.notification')
msg_obj = self.pool.get('mail.message')
if remove_childs:
@@ -814,4 +872,37 @@ class mail_thread(osv.osv):
to_del_notif_ids = notif_obj.search(cr, uid, ['&', ('user_id', '=', uid), ('message_id', 'in', notif_msg_ids)], context=context)
return notif_obj.unlink(cr, uid, to_del_notif_ids, context=context)
+ #------------------------------------------------------
+ # Thread_state
+ #------------------------------------------------------
+
+ def message_create_set_unread(self, cr, uid, ids, context=None):
+ """ When creating a new message, set as unread if uid is not the
+ object responsible. """
+ for obj in self.browse(cr, uid, ids, context=context):
+ if obj.message_state and hasattr(obj, 'user_id') and (not obj.user_id or obj.user_id.id != uid):
+ self.message_mark_as_unread(cr, uid, [obj.id], context=context)
+
+ def message_check_and_set_unread(self, cr, uid, ids, context=None):
+ """ Set unread if uid is the object responsible or if the object has
+ no responsible. """
+ for obj in self.browse(cr, uid, ids, context=context):
+ if obj.message_state and hasattr(obj, 'user_id') and (not obj.user_id or obj.user_id.id == uid):
+ self.message_mark_as_unread(cr, uid, [obj.id], context=context)
+
+ def message_mark_as_unread(self, cr, uid, ids, context=None):
+ """ Set as unread. """
+ return self.write(cr, uid, ids, {'message_state': False}, context=context)
+
+ def message_check_and_set_read(self, cr, uid, ids, context=None):
+ """ Set read if uid is the object responsible. """
+ for obj in self.browse(cr, uid, ids, context=context):
+ if not obj.message_state and hasattr(obj, 'user_id') and obj.user_id and obj.user_id.id == uid:
+ self.message_mark_as_read(cr, uid, [obj.id], context=context)
+
+ def message_mark_as_read(self, cr, uid, ids, context=None):
+ """ Set as read. """
+ return self.write(cr, uid, ids, {'message_state': True}, context=context)
+
+
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: