[FIX] message_route: removed owner of created documents using aliases taken from...
[odoo/odoo.git] / addons / mail / mail_thread.py
index 7663524..12e43d0 100644 (file)
@@ -26,11 +26,10 @@ import email
 import logging
 import pytz
 import time
-from openerp import tools
 import xmlrpclib
-
 from email.message import Message
 
+from openerp import tools
 from openerp import SUPERUSER_ID
 from openerp.addons.mail.mail_message import decode
 from openerp.osv import fields, osv
@@ -74,8 +73,8 @@ class mail_thread(osv.AbstractModel):
     # Automatic logging system if mail installed
     # _track = {
     #   'field': {
-    #       'module.subtype_xml': lambda self, cr, uid, obj, context=None: obj.state == done,
-    #       'module.subtype_xml2': lambda self, cr, uid, obj, context=None: obj.state != done,
+    #       'module.subtype_xml': lambda self, cr, uid, obj, context=None: obj[state] == done,
+    #       'module.subtype_xml2': lambda self, cr, uid, obj, context=None: obj[state] != done,
     #   },
     #   'field2': {
     #       ...
@@ -302,7 +301,7 @@ class mail_thread(osv.AbstractModel):
         lst = []
         for name, column_info in self._all_columns.items():
             visibility = getattr(column_info.column, 'track_visibility', False)
-            if visibility == 2 or (visibility == 1 and name in updated_fields) or name in self._track:
+            if visibility == 'always' or (visibility == 'onchange' and name in updated_fields) or name in self._track:
                 lst.append(name)
         if not lst:
             return lst
@@ -310,13 +309,15 @@ class mail_thread(osv.AbstractModel):
 
     def message_track(self, cr, uid, ids, tracked_fields, initial_values, context=None):
 
-        def convert_for_display(value, field_obj):
+        def convert_for_display(value, col_info):
+            if not value and col_info['type'] == 'boolean':
+                return 'False'
             if not value:
                 return ''
-            if field_obj['type'] == 'many2one':
+            if col_info['type'] == 'many2one':
                 return value[1]
-            if field_obj['type'] == 'selection':
-                return dict(field_obj['selection'])[value]
+            if col_info['type'] == 'selection':
+                return dict(col_info['selection'])[value]
             return value
 
         def format_message(message_description, tracked_values):
@@ -490,7 +491,13 @@ class mail_thread(osv.AbstractModel):
                 for alias in mail_alias.browse(cr, uid, alias_ids, context=context):
                     user_id = alias.alias_user_id.id
                     if not user_id:
-                        user_id = self._message_find_user_id(cr, uid, message, context=context)
+                        # TDE note: this could cause crashes, because no clue that the user
+                        # that send the email has the right to create or modify a new document
+                        # Fallback on user_id = uid
+                        # Note: recognized partners will be added as followers anyway
+                        # user_id = self._message_find_user_id(cr, uid, message, context=context)
+                        user_id = uid
+                        _logger.debug('Routing mail with Message-Id %s: direct alias match: %r', message_id, routes)
                     routes.append((alias.alias_model_id.model, alias.alias_force_thread_id, \
                                    eval(alias.alias_defaults), user_id))
                 _logger.debug('Routing mail with Message-Id %s: direct alias match: %r', message_id, routes)
@@ -592,6 +599,10 @@ class mail_thread(osv.AbstractModel):
                 model_pool = self.pool.get('mail.thread')
             new_msg_id = model_pool.message_post_user_api(cr, uid, [thread_id], context=context, content_subtype='html', **msg)
 
+            # when posting an incoming email to a document: subscribe the author, if a partner, as follower
+            if model and thread_id and msg.get('author_id'):
+                model_pool.message_subscribe(cr, uid, [thread_id], [msg.get('author_id')], context=context)
+
             if partner_ids:
                 # postponed after message_post, because this is an external message and we don't want to create
                 # duplicate emails due to notifications
@@ -825,7 +836,7 @@ class mail_thread(osv.AbstractModel):
         mail_message = self.pool.get('mail.message')
         model = context.get('thread_model', self._name) if thread_id else False
 
-        attachment_ids = []
+        attachment_ids = kwargs.pop('attachment_ids', [])
         for name, content in attachments:
             if isinstance(content, unicode):
                 content = content.encode('utf-8')
@@ -883,9 +894,9 @@ class mail_thread(osv.AbstractModel):
 
         return mail_message.create(cr, uid, values, context=context)
 
-    def message_post_user_api(self, cr, uid, thread_id, body='', subject=False, parent_id=False,
-                                attachment_ids=None, context=None, content_subtype='plaintext',
-                                extra_email=[], **kwargs):
+    def message_post_user_api(self, cr, uid, thread_id, body='', parent_id=False,
+                                attachment_ids=None, extra_emails=None, content_subtype='plaintext',
+                                context=None, **kwargs):
         """ Wrapper on message_post, used for user input :
             - mail gateway
             - quick reply in Chatter (refer to mail.js), not
@@ -899,35 +910,41 @@ class mail_thread(osv.AbstractModel):
                 to the related document. Should only be set by Chatter.
             - extra_email: [ 'Fabien <fpi@openerp.com>', 'al@openerp.com' ]
         """
+        partner_obj = self.pool.get('res.partner')
+        mail_message_obj = self.pool.get('mail.message')
         ir_attachment = self.pool.get('ir.attachment')
-        mail_message = self.pool.get('mail.message')
-
-        # 1. Pre-processing: body, partner_ids, type and subtype
-        if content_subtype == 'plaintext':
-            body = tools.plaintext2html(body)
-
-        for partner in extra_email:
-            part_ids = self.pool.get('res.partner').search(cr, uid, [('email', '=', partner)], context=context)
-            if not part_ids:
-                part_ids = [self.pool.get('res.partner').name_create(cr, uid, partner, context=context)[0]]
-            self.message_subscribe(cr, uid, [thread_id], part_ids, context=context)
-
-        partner_ids = kwargs.pop('partner_ids', [])
+        extra_emails = extra_emails or []
+
+        # 1.A.1: pre-process partners and incoming extra_emails
+        partner_ids = set([])
+        for email in extra_emails:
+            partner_id = partner_obj.find_or_create(cr, uid, email, context=context)
+            # link mail with this from mail to the new partner id
+            partner_msg_ids = mail_message_obj.search(cr, SUPERUSER_ID, [('email_from', '=', email), ('author_id', '=', False)], context=context)
+            if partner_id and partner_msg_ids:
+                mail_message_obj.write(cr, SUPERUSER_ID, partner_msg_ids, {'email_from': None, 'author_id': partner_id}, context=context)
+            partner_ids.add((4, partner_id))
+        if partner_ids:
+            self.message_subscribe(cr, uid, [thread_id], [item[1] for item in partner_ids], context=context)
+
+        # 1.A.2: add recipients of parent message
         if parent_id:
-            parent_message = self.pool.get('mail.message').browse(cr, uid, parent_id, context=context)
-            partner_ids += [(4, partner.id) for partner in parent_message.partner_ids]
+            parent_message = mail_message_obj.browse(cr, uid, parent_id, context=context)
+            partner_ids |= set([(4, partner.id) for partner in parent_message.partner_ids])
             # TDE FIXME HACK: mail.thread -> private message
             if self._name == 'mail.thread' and parent_message.author_id.id:
-                partner_ids.append((4, parent_message.author_id.id))
+                partner_ids.add((4, parent_message.author_id.id))
 
-        message_type = kwargs.pop('type', 'comment')
-        message_subtype = kwargs.pop('subtype', 'mail.mt_comment')
+        # 1.A.3: add specified recipients
+        partner_ids |= set(kwargs.pop('partner_ids', []))
 
-        # 2. Post message
-        new_message_id = self.message_post(cr, uid, thread_id=thread_id, body=body, subject=subject, type=message_type,
-                        subtype=message_subtype, parent_id=parent_id, context=context, partner_ids=partner_ids, **kwargs)
+        # 1.B: handle body, message_type and message_subtype
+        if content_subtype == 'plaintext':
+            body = tools.plaintext2html(body)
+        msg_type = kwargs.pop('type', 'comment')
+        msg_subtype = kwargs.pop('subtype', 'mail.mt_comment')
 
-        # 3. Post-processing
+        # 2. Pre-processing: attachments
         # HACK TDE FIXME: Chatter: attachments linked to the document (not done JS-side), load the message
         if attachment_ids:
             # TDE FIXME (?): when posting a private message, we use mail.thread as a model
@@ -943,9 +960,14 @@ class mail_thread(osv.AbstractModel):
             if filtered_attachment_ids:
                 if thread_id and model:
                     ir_attachment.write(cr, SUPERUSER_ID, attachment_ids, {'res_model': model, 'res_id': thread_id}, context=context)
-                mail_message.write(cr, SUPERUSER_ID, [new_message_id], {'attachment_ids': [(6, 0, [pid for pid in attachment_ids])]}, context=context)
+        else:
+            attachment_ids = []
+        attachment_ids = [(4, id) for id in attachment_ids]
 
-        return new_message_id
+        # 3. Post message
+        return self.message_post(cr, uid, thread_id=thread_id, body=body,
+                            type=msg_type, subtype=msg_subtype, parent_id=parent_id,
+                            attachment_ids=attachment_ids, partner_ids=list(partner_ids), context=context, **kwargs)
 
     #------------------------------------------------------
     # Followers API
@@ -965,7 +987,12 @@ class mail_thread(osv.AbstractModel):
 
     def message_subscribe(self, cr, uid, ids, partner_ids, subtype_ids=None, context=None):
         """ Add partners to the records followers. """
-        self.check_access_rights(cr, uid, 'read')
+        user_pid = self.pool.get('res.users').read(cr, uid, uid, ['partner_id'], context=context)['partner_id'][0]
+        if set(partner_ids) == set([user_pid]):
+            self.check_access_rights(cr, uid, 'read')
+        else:
+            self.check_access_rights(cr, uid, 'write')
+
         self.write(cr, SUPERUSER_ID, ids, {'message_follower_ids': [(4, pid) for pid in partner_ids]}, context=context)
         # if subtypes are not specified (and not set to a void list), fetch default ones
         if subtype_ids is None:
@@ -987,7 +1014,11 @@ class mail_thread(osv.AbstractModel):
 
     def message_unsubscribe(self, cr, uid, ids, partner_ids, context=None):
         """ Remove partners from the records followers. """
-        self.check_access_rights(cr, uid, 'read')
+        user_pid = self.pool.get('res.users').read(cr, uid, uid, ['partner_id'], context=context)['partner_id'][0]
+        if set(partner_ids) == set([user_pid]):
+            self.check_access_rights(cr, uid, 'read')
+        else:
+            self.check_access_rights(cr, uid, 'write')
         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, updated_fields, context=None):