2011-07-22 16:34:57 +00:00
# -*- coding: utf-8 -*-
##############################################################################
#
# OpenERP, Open Source Management Solution
2012-03-13 13:59:49 +00:00
# Copyright (C) 2010-today OpenERP SA (<http://www.openerp.com>)
2011-07-22 16:34:57 +00:00
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>
#
##############################################################################
import logging
2012-09-05 15:53:19 +00:00
import tools
2011-07-22 16:34:57 +00:00
from email . header import decode_header
2012-09-13 17:05:00 +00:00
from openerp import SUPERUSER_ID
2012-10-26 11:33:36 +00:00
from openerp . osv import osv , orm , fields
2012-11-07 08:59:26 +00:00
from openerp . tools import html_email_clean
2012-10-26 11:33:36 +00:00
from openerp . tools . translate import _
2011-07-22 16:34:57 +00:00
2012-06-22 06:48:54 +00:00
_logger = logging . getLogger ( __name__ )
2011-07-22 16:34:57 +00:00
2012-10-26 11:33:36 +00:00
try :
from mako . template import Template as MakoTemplate
except ImportError :
_logger . warning ( " payment_acquirer: mako templates not available, payment acquirer will not work! " )
2012-07-06 09:41:41 +00:00
""" Some tools for parsing / creating email fields """
2011-07-22 16:34:57 +00:00
def decode ( text ) :
""" Returns unicode() string conversion of the the given encoded smtp header text """
if text :
text = decode_header ( text . replace ( ' \r ' , ' ' ) )
return ' ' . join ( [ tools . ustr ( x [ 0 ] , x [ 1 ] ) for x in text ] )
2012-09-13 15:48:44 +00:00
2012-05-08 13:56:00 +00:00
class mail_message ( osv . Model ) :
2012-08-31 09:01:20 +00:00
""" Messages model: system notification (replacing res.log notifications),
comments ( OpenChatter discussion ) and incoming emails . """
2011-07-22 16:34:57 +00:00
_name = ' mail.message '
2012-08-15 17:08:22 +00:00
_description = ' Message '
2012-08-20 19:10:58 +00:00
_inherit = [ ' ir.needaction_mixin ' ]
2012-08-15 17:08:22 +00:00
_order = ' id desc '
2012-11-02 10:46:04 +00:00
_rec_name = ' record_name '
2011-07-22 16:34:57 +00:00
2012-11-07 08:24:42 +00:00
_message_read_limit = 30
2012-10-25 13:50:20 +00:00
_message_read_fields = [ ' id ' , ' parent_id ' , ' model ' , ' res_id ' , ' body ' , ' subject ' , ' date ' , ' to_read ' , ' email_from ' ,
2012-10-18 15:23:22 +00:00
' type ' , ' vote_user_ids ' , ' attachment_ids ' , ' author_id ' , ' partner_ids ' , ' record_name ' , ' favorite_user_ids ' ]
2012-08-27 09:42:28 +00:00
_message_record_name_length = 18
2012-10-18 15:23:22 +00:00
_message_read_more_limit = 1024
2012-08-27 09:42:28 +00:00
2012-11-21 10:19:17 +00:00
def default_get ( self , cr , uid , fields , context = None ) :
# protection for `default_type` values leaking from menu action context (e.g. for invoices)
if context and context . get ( ' default_type ' ) and context . get ( ' default_type ' ) not in self . _columns [ ' type ' ] . selection :
context = dict ( context , default_type = None )
return super ( mail_message , self ) . default_get ( cr , uid , fields , context = context )
2012-08-27 09:42:28 +00:00
def _shorten_name ( self , name ) :
2012-09-05 15:53:19 +00:00
if len ( name ) < = ( self . _message_record_name_length + 3 ) :
2012-08-27 09:42:28 +00:00
return name
2012-08-28 17:39:01 +00:00
return name [ : self . _message_record_name_length ] + ' ... '
2012-08-27 09:42:28 +00:00
2012-08-28 09:53:23 +00:00
def _get_record_name ( self , cr , uid , ids , name , arg , context = None ) :
2012-10-25 15:11:23 +00:00
""" Return the related document name, using name_get. It is done using
SUPERUSER_ID , to be sure to have the record name correctly stored . """
2012-10-29 11:21:17 +00:00
# TDE note: regroup by model/ids, to have less queries to perform
2012-10-17 13:44:49 +00:00
result = dict . fromkeys ( ids , False )
2012-10-17 16:12:45 +00:00
for message in self . read ( cr , uid , ids , [ ' model ' , ' res_id ' ] , context = context ) :
2012-10-25 15:11:23 +00:00
if not message . get ( ' model ' ) or not message . get ( ' res_id ' ) :
2012-03-27 07:30:53 +00:00
continue
2012-10-25 15:11:23 +00:00
result [ message [ ' id ' ] ] = self . _shorten_name ( self . pool . get ( message [ ' model ' ] ) . name_get ( cr , SUPERUSER_ID , [ message [ ' res_id ' ] ] , context = context ) [ 0 ] [ 1 ] )
2012-03-27 07:30:53 +00:00
return result
2012-06-29 15:27:58 +00:00
2012-10-17 15:57:33 +00:00
def _get_to_read ( self , cr , uid , ids , name , arg , context = None ) :
2012-08-28 09:53:23 +00:00
""" Compute if the message is unread by the current user. """
2012-10-18 13:06:01 +00:00
res = dict ( ( id , False ) for id in ids )
2012-09-14 16:16:33 +00:00
partner_id = self . pool . get ( ' res.users ' ) . read ( cr , uid , uid , [ ' partner_id ' ] , context = context ) [ ' partner_id ' ] [ 0 ]
2012-08-28 09:53:23 +00:00
notif_obj = self . pool . get ( ' mail.notification ' )
notif_ids = notif_obj . search ( cr , uid , [
( ' partner_id ' , ' in ' , [ partner_id ] ) ,
2012-10-29 11:21:17 +00:00
( ' message_id ' , ' in ' , ids ) ,
( ' read ' , ' = ' , False ) ,
2012-08-28 09:53:23 +00:00
] , context = context )
for notif in notif_obj . browse ( cr , uid , notif_ids , context = context ) :
2012-10-29 11:21:17 +00:00
res [ notif . message_id . id ] = True
2012-08-28 09:53:23 +00:00
return res
2012-10-17 15:57:33 +00:00
def _search_to_read ( self , cr , uid , obj , name , domain , context = None ) :
""" Search for messages to read by the current user. Condition is
2012-08-31 09:01:20 +00:00
inversed because we search unread message on a read column . """
2012-08-31 11:23:53 +00:00
if domain [ 0 ] [ 2 ] :
2012-10-16 08:27:18 +00:00
read_cond = " (read = False OR read IS NULL) "
2012-08-31 11:23:53 +00:00
else :
2012-10-16 08:27:18 +00:00
read_cond = " read = True "
2012-09-17 09:51:10 +00:00
partner_id = self . pool . get ( ' res.users ' ) . read ( cr , uid , uid , [ ' partner_id ' ] , context = context ) [ ' partner_id ' ] [ 0 ]
2012-08-31 11:23:53 +00:00
cr . execute ( " SELECT message_id FROM mail_notification " \
" WHERE partner_id = %% s AND %s " % read_cond ,
( partner_id , ) )
2012-08-31 09:01:20 +00:00
return [ ( ' id ' , ' in ' , [ r [ 0 ] for r in cr . fetchall ( ) ] ) ]
2012-08-28 09:53:23 +00:00
2012-06-29 15:27:58 +00:00
def name_get ( self , cr , uid , ids , context = None ) :
2012-08-10 14:43:39 +00:00
# name_get may receive int id instead of an id list
if isinstance ( ids , ( int , long ) ) :
ids = [ ids ]
2012-06-29 15:27:58 +00:00
res = [ ]
for message in self . browse ( cr , uid , ids , context = context ) :
2012-08-27 09:42:28 +00:00
name = ' %s : %s ' % ( message . subject or ' ' , message . body or ' ' )
res . append ( ( message . id , self . _shorten_name ( name . lstrip ( ' : ' ) ) ) )
2012-06-29 15:27:58 +00:00
return res
2011-07-22 16:34:57 +00:00
_columns = {
2012-04-02 16:24:25 +00:00
' type ' : fields . selection ( [
2012-08-31 09:01:20 +00:00
( ' email ' , ' Email ' ) ,
2012-04-02 16:24:25 +00:00
( ' comment ' , ' Comment ' ) ,
( ' notification ' , ' System notification ' ) ,
2012-07-20 09:25:45 +00:00
] , ' Type ' ,
help = " Message type: email for email message, notification for system " \
2012-08-23 18:54:43 +00:00
" message, comment for other messages such as user replies " ) ,
2012-10-25 13:50:20 +00:00
' email_from ' : fields . char ( ' From ' ,
2012-10-29 11:21:17 +00:00
help = " Email address of the sender. This field is set when no matching partner is found for incoming emails. " ) ,
2012-11-12 14:04:08 +00:00
' author_id ' : fields . many2one ( ' res.partner ' , ' Author ' , select = 1 ,
ondelete = ' set null ' ,
2012-10-29 11:21:17 +00:00
help = " Author of the message. If not set, email_from may hold an email address that did not match any partner. " ) ,
2012-10-25 11:30:48 +00:00
' partner_ids ' : fields . many2many ( ' res.partner ' , string = ' Recipients ' ) ,
' notified_partner_ids ' : fields . many2many ( ' res.partner ' , ' mail_notification ' ,
2012-11-02 14:23:22 +00:00
' message_id ' , ' partner_id ' , ' Notified partners ' ,
help = ' Partners that have a notification pushing this message in their mailboxes ' ) ,
2012-08-23 18:54:43 +00:00
' attachment_ids ' : fields . many2many ( ' ir.attachment ' , ' message_attachment_rel ' ,
' message_id ' , ' attachment_id ' , ' Attachments ' ) ,
2012-11-12 14:04:08 +00:00
' parent_id ' : fields . many2one ( ' mail.message ' , ' Parent Message ' , select = True ,
ondelete = ' set null ' , help = " Initial thread message. " ) ,
2012-08-15 20:01:26 +00:00
' child_ids ' : fields . one2many ( ' mail.message ' , ' parent_id ' , ' Child Messages ' ) ,
2012-04-23 12:18:28 +00:00
' model ' : fields . char ( ' Related Document Model ' , size = 128 , select = 1 ) ,
2012-03-13 13:59:49 +00:00
' res_id ' : fields . integer ( ' Related Document ID ' , select = 1 ) ,
2012-10-25 15:11:23 +00:00
' record_name ' : fields . function ( _get_record_name , type = ' char ' ,
store = True , string = ' Message Record Name ' ,
2012-07-06 09:41:41 +00:00
help = " Name get of the related document. " ) ,
2012-11-12 14:04:08 +00:00
' notification_ids ' : fields . one2many ( ' mail.notification ' , ' message_id ' ,
string = ' Notifications ' ,
help = ' Technical field holding the message notifications. Use notified_partner_ids to access notified partners. ' ) ,
2012-08-23 18:54:43 +00:00
' subject ' : fields . char ( ' Subject ' ) ,
2011-07-22 16:34:57 +00:00
' date ' : fields . datetime ( ' Date ' ) ,
2012-08-23 18:54:43 +00:00
' message_id ' : fields . char ( ' Message-Id ' , help = ' Message unique identifier ' , select = 1 , readonly = 1 ) ,
2012-08-29 15:00:02 +00:00
' body ' : fields . html ( ' Contents ' , help = ' Automatically sanitized HTML contents ' ) ,
2012-10-17 15:57:33 +00:00
' to_read ' : fields . function ( _get_to_read , fnct_search = _search_to_read ,
type = ' boolean ' , string = ' To read ' ,
help = ' Functional field to search for messages the current user has to read ' ) ,
2012-11-12 14:04:08 +00:00
' subtype_id ' : fields . many2one ( ' mail.message.subtype ' , ' Subtype ' ,
ondelete = ' set null ' , select = 1 , ) ,
2012-10-18 15:23:22 +00:00
' vote_user_ids ' : fields . many2many ( ' res.users ' , ' mail_vote ' ,
' message_id ' , ' user_id ' , string = ' Votes ' ,
2012-09-18 15:05:44 +00:00
help = ' Users that voted for this message ' ) ,
2012-10-18 15:23:22 +00:00
' favorite_user_ids ' : fields . many2many ( ' res.users ' , ' mail_favorite ' ,
' message_id ' , ' user_id ' , string = ' Favorite ' ,
help = ' Users that set this message in their favorites ' ) ,
2011-07-22 16:34:57 +00:00
}
2012-08-28 09:53:23 +00:00
def _needaction_domain_get ( self , cr , uid , context = None ) :
2012-08-22 13:37:23 +00:00
if self . _needaction :
2012-10-17 09:35:59 +00:00
return [ ( ' to_read ' , ' = ' , True ) ]
2012-08-22 13:37:23 +00:00
return [ ]
2012-08-28 09:53:23 +00:00
def _get_default_author ( self , cr , uid , context = None ) :
2012-10-18 15:23:22 +00:00
return self . pool . get ( ' res.users ' ) . read ( cr , uid , uid , [ ' partner_id ' ] , context = context ) [ ' partner_id ' ] [ 0 ]
2012-08-17 11:19:36 +00:00
2011-07-22 16:34:57 +00:00
_defaults = {
2012-04-03 17:34:49 +00:00
' type ' : ' email ' ,
2012-08-16 09:26:16 +00:00
' date ' : lambda * a : fields . datetime . now ( ) ,
2012-09-05 15:53:19 +00:00
' author_id ' : lambda self , cr , uid , ctx = { } : self . _get_default_author ( cr , uid , ctx ) ,
2012-09-04 11:54:16 +00:00
' body ' : ' ' ,
2011-07-22 16:34:57 +00:00
}
2012-09-18 12:21:39 +00:00
#------------------------------------------------------
# Vote/Like
#------------------------------------------------------
2012-10-18 15:23:22 +00:00
def vote_toggle ( self , cr , uid , ids , context = None ) :
2012-10-19 12:06:16 +00:00
''' Toggles vote. Performed using read to avoid access rights issues.
Done as SUPERUSER_ID because uid may vote for a message he cannot modify . '''
2012-10-18 15:23:22 +00:00
for message in self . read ( cr , uid , ids , [ ' vote_user_ids ' ] , context = context ) :
new_has_voted = not ( uid in message . get ( ' vote_user_ids ' ) )
if new_has_voted :
2012-10-19 12:06:16 +00:00
self . write ( cr , SUPERUSER_ID , message . get ( ' id ' ) , { ' vote_user_ids ' : [ ( 4 , uid ) ] } , context = context )
2012-10-18 15:23:22 +00:00
else :
2012-10-19 12:06:16 +00:00
self . write ( cr , SUPERUSER_ID , message . get ( ' id ' ) , { ' vote_user_ids ' : [ ( 3 , uid ) ] } , context = context )
2012-10-18 15:23:22 +00:00
return new_has_voted or False
2012-08-15 17:08:22 +00:00
2012-10-12 15:25:05 +00:00
#------------------------------------------------------
2012-10-18 15:23:22 +00:00
# Favorite
2012-10-12 15:25:05 +00:00
#------------------------------------------------------
2012-10-18 15:23:22 +00:00
def favorite_toggle ( self , cr , uid , ids , context = None ) :
2012-10-19 12:06:16 +00:00
''' Toggles favorite. Performed using read to avoid access rights issues.
Done as SUPERUSER_ID because uid may star a message he cannot modify . '''
2012-10-18 15:23:22 +00:00
for message in self . read ( cr , uid , ids , [ ' favorite_user_ids ' ] , context = context ) :
new_is_favorite = not ( uid in message . get ( ' favorite_user_ids ' ) )
if new_is_favorite :
2012-10-19 12:06:16 +00:00
self . write ( cr , SUPERUSER_ID , message . get ( ' id ' ) , { ' favorite_user_ids ' : [ ( 4 , uid ) ] } , context = context )
2012-11-28 15:19:39 +00:00
# when setting a favorite, set the related notification as unread, or create an unread notification if not existing
notification_obj = self . pool . get ( ' mail.notification ' )
pid = self . pool . get ( ' res.users ' ) . read ( cr , uid , uid , [ ' partner_id ' ] , context = None ) [ ' partner_id ' ] [ 0 ]
notif_id = notification_obj . search ( cr , SUPERUSER_ID , [ ( ' message_id ' , ' = ' , message . get ( ' id ' ) ) , ( ' partner_id ' , ' = ' , pid ) ] , context = context )
if notif_id :
notification_obj . write ( cr , SUPERUSER_ID , notif_id , { ' read ' : False } , context = context )
else :
notification_obj . create ( cr , SUPERUSER_ID , { ' message_id ' : message . get ( ' id ' ) , ' partner_id ' : pid , ' read ' : False } , context = context )
2012-10-18 15:23:22 +00:00
else :
2012-10-19 12:06:16 +00:00
self . write ( cr , SUPERUSER_ID , message . get ( ' id ' ) , { ' favorite_user_ids ' : [ ( 3 , uid ) ] } , context = context )
2012-10-18 15:23:22 +00:00
return new_is_favorite or False
2012-10-12 15:25:05 +00:00
2012-08-20 09:06:36 +00:00
#------------------------------------------------------
# Message loading for web interface
#------------------------------------------------------
2012-10-31 15:40:26 +00:00
def _message_read_dict_postprocess ( self , cr , uid , messages , message_tree , context = None ) :
""" Post-processing on values given by message_read. This method will
handle partners in batch to avoid doing numerous queries .
2012-10-12 13:26:00 +00:00
2012-10-31 15:40:26 +00:00
: param list messages : list of message , as get_dict result
: param dict message_tree : { [ msg . id ] : msg browse record }
2012-09-19 10:13:39 +00:00
"""
2012-10-31 15:40:26 +00:00
res_partner_obj = self . pool . get ( ' res.partner ' )
ir_attachment_obj = self . pool . get ( ' ir.attachment ' )
pid = self . pool . get ( ' res.users ' ) . read ( cr , uid , uid , [ ' partner_id ' ] , context = None ) [ ' partner_id ' ] [ 0 ]
# 1. Aggregate partners (author_id and partner_ids) and attachments
partner_ids = set ( )
attachment_ids = set ( )
for key , message in message_tree . iteritems ( ) :
if message . author_id :
partner_ids | = set ( [ message . author_id . id ] )
if message . partner_ids :
partner_ids | = set ( [ partner . id for partner in message . partner_ids ] )
if message . attachment_ids :
attachment_ids | = set ( [ attachment . id for attachment in message . attachment_ids ] )
# Filter author_ids uid can see
# partner_ids = self.pool.get('res.partner').search(cr, uid, [('id', 'in', partner_ids)], context=context)
partners = res_partner_obj . name_get ( cr , uid , list ( partner_ids ) , context = context )
partner_tree = dict ( ( partner [ 0 ] , partner ) for partner in partners )
# 2. Attachments
2012-11-30 13:43:56 +00:00
attachments = ir_attachment_obj . read ( cr , SUPERUSER_ID , list ( attachment_ids ) , [ ' id ' , ' datas_fname ' ] , context = context )
2012-11-01 10:44:04 +00:00
attachments_tree = dict ( ( attachment [ ' id ' ] , { ' id ' : attachment [ ' id ' ] , ' filename ' : attachment [ ' datas_fname ' ] } ) for attachment in attachments )
2012-10-31 15:40:26 +00:00
# 3. Update message dictionaries
for message_dict in messages :
message_id = message_dict . get ( ' id ' )
message = message_tree [ message_id ]
if message . author_id :
author = partner_tree [ message . author_id . id ]
else :
author = ( 0 , message . email_from )
partner_ids = [ ]
for partner in message . partner_ids :
if partner . id in partner_tree :
partner_ids . append ( partner_tree [ partner . id ] )
2012-09-19 10:13:39 +00:00
attachment_ids = [ ]
2012-10-31 15:40:26 +00:00
for attachment in message . attachment_ids :
if attachment . id in attachments_tree :
attachment_ids . append ( attachments_tree [ attachment . id ] )
message_dict . update ( {
' is_author ' : pid == author [ 0 ] ,
' author_id ' : author ,
' partner_ids ' : partner_ids ,
' attachment_ids ' : attachment_ids ,
} )
return True
2012-09-28 13:27:23 +00:00
2012-10-31 15:40:26 +00:00
def _message_read_dict ( self , cr , uid , message , parent_id = False , context = None ) :
""" Return a dict representation of the message. This representation is
used in the JS client code , to display the messages . Partners and
attachments related stuff will be done in post - processing in batch .
2012-10-12 13:26:00 +00:00
2012-10-31 15:40:26 +00:00
: param dict message : mail . message browse record
"""
# private message: no model, no res_id
is_private = False
if not message . model or not message . res_id :
is_private = True
# votes and favorites: res.users ids, no prefetching should be done
vote_nb = len ( message . vote_user_ids )
has_voted = uid in [ user . id for user in message . vote_user_ids ]
is_favorite = uid in [ user . id for user in message . favorite_user_ids ]
return { ' id ' : message . id ,
' type ' : message . type ,
2012-11-14 08:39:00 +00:00
' body ' : html_email_clean ( message . body ) ,
2012-10-31 15:40:26 +00:00
' model ' : message . model ,
' res_id ' : message . res_id ,
' record_name ' : message . record_name ,
' subject ' : message . subject ,
' date ' : message . date ,
' to_read ' : message . to_read ,
' parent_id ' : parent_id ,
' is_private ' : is_private ,
' author_id ' : False ,
' is_author ' : False ,
' partner_ids ' : [ ] ,
' vote_nb ' : vote_nb ,
' has_voted ' : has_voted ,
' is_favorite ' : is_favorite ,
' attachment_ids ' : [ ] ,
}
2012-10-12 13:26:00 +00:00
2012-11-02 10:22:48 +00:00
def _message_read_add_expandables ( self , cr , uid , messages , message_tree , parent_tree ,
message_unload_ids = [ ] , thread_level = 0 , domain = [ ] , parent_id = False , context = None ) :
2012-10-23 11:41:24 +00:00
""" Create expandables for message_read, to load new messages.
1. get the expandable for new threads
if display is flat ( thread_level == 0 ) :
fetch message_ids < min ( already displayed ids ) , because we
want a flat display , ordered by id
else :
fetch message_ids that are not childs of already displayed
messages
2. get the expandables for new messages inside threads if display
is not flat
for each thread header , search for its childs
for each hole in the child list based on message displayed ,
create an expandable
2012-10-31 15:40:26 +00:00
: param list messages : list of message structure for the Chatter
2012-10-23 11:41:24 +00:00
widget to which expandables are added
2012-10-31 15:40:26 +00:00
: param dict message_tree : dict [ id ] : browse record of this message
2012-11-02 10:22:48 +00:00
: param dict parent_tree : dict [ parent_id ] : [ child_ids ]
2012-10-31 15:40:26 +00:00
: param list message_unload_ids : list of message_ids we do not want
to load
2012-10-23 11:41:24 +00:00
: return bool : True
2012-07-20 09:25:45 +00:00
"""
2012-11-05 11:58:52 +00:00
def _get_expandable ( domain , message_nb , parent_id , max_limit ) :
2012-10-22 16:46:38 +00:00
return {
' domain ' : domain ,
' nb_messages ' : message_nb ,
' type ' : ' expandable ' ,
2012-10-26 08:00:05 +00:00
' parent_id ' : parent_id ,
2012-11-05 11:58:52 +00:00
' max_limit ' : max_limit ,
2012-10-22 16:46:38 +00:00
}
2012-10-23 11:41:24 +00:00
2012-10-31 15:40:26 +00:00
if not messages :
return True
message_ids = sorted ( message_tree . keys ( ) )
2012-10-23 11:41:24 +00:00
# 1. get the expandable for new threads
if thread_level == 0 :
2012-10-31 15:40:26 +00:00
exp_domain = domain + [ ( ' id ' , ' < ' , min ( message_unload_ids + message_ids ) ) ]
2012-10-23 11:41:24 +00:00
else :
2012-11-02 10:22:48 +00:00
exp_domain = domain + [ ' ! ' , ( ' id ' , ' child_of ' , message_unload_ids + parent_tree . keys ( ) ) ]
2012-10-23 11:41:24 +00:00
ids = self . search ( cr , uid , exp_domain , context = context , limit = 1 )
if ids :
2012-11-02 10:22:48 +00:00
# inside a thread: prepend
if parent_id :
2012-11-05 11:58:52 +00:00
messages . insert ( 0 , _get_expandable ( exp_domain , - 1 , parent_id , True ) )
2012-11-02 10:22:48 +00:00
# new threads: append
else :
2012-11-05 11:58:52 +00:00
messages . append ( _get_expandable ( exp_domain , - 1 , parent_id , True ) )
2012-10-23 11:41:24 +00:00
# 2. get the expandables for new messages inside threads if display is not flat
if thread_level == 0 :
return True
2012-10-31 15:40:26 +00:00
for message_id in message_ids :
message = message_tree [ message_id ]
2012-10-19 09:13:29 +00:00
2012-10-31 15:40:26 +00:00
# generate only for thread header messages (TDE note: parent_id may be False is uid cannot see parent_id, seems ok)
if message . parent_id :
2012-10-22 16:46:38 +00:00
continue
2012-10-31 15:40:26 +00:00
# check there are message for expandable
2012-11-02 10:22:48 +00:00
child_ids = set ( [ child . id for child in message . child_ids ] ) - set ( message_unload_ids )
child_ids = sorted ( list ( child_ids ) , reverse = True )
if not child_ids :
2012-10-22 16:46:38 +00:00
continue
2012-10-18 13:06:01 +00:00
2012-10-31 15:40:26 +00:00
# make groups of unread messages
2012-11-02 10:22:48 +00:00
id_min , id_max , nb = max ( child_ids ) , 0 , 0
for child_id in child_ids :
if not child_id in message_ids :
2012-10-17 13:44:49 +00:00
nb + = 1
2012-11-02 10:22:48 +00:00
if id_min > child_id :
id_min = child_id
if id_max < child_id :
id_max = child_id
2012-10-22 16:46:38 +00:00
elif nb > 0 :
exp_domain = [ ( ' id ' , ' >= ' , id_min ) , ( ' id ' , ' <= ' , id_max ) , ( ' id ' , ' child_of ' , message_id ) ]
2012-11-14 15:58:10 +00:00
idx = [ msg . get ( ' id ' ) for msg in messages ] . index ( child_id ) + 1
# messages.append(_get_expandable(exp_domain, nb, message_id, False))
messages . insert ( idx , _get_expandable ( exp_domain , nb , message_id , False ) )
2012-11-02 10:22:48 +00:00
id_min , id_max , nb = max ( child_ids ) , 0 , 0
2012-10-03 07:28:45 +00:00
else :
2012-11-02 10:22:48 +00:00
id_min , id_max , nb = max ( child_ids ) , 0 , 0
2012-10-17 13:44:49 +00:00
if nb > 0 :
2012-10-22 16:46:38 +00:00
exp_domain = [ ( ' id ' , ' >= ' , id_min ) , ( ' id ' , ' <= ' , id_max ) , ( ' id ' , ' child_of ' , message_id ) ]
2012-11-02 10:22:48 +00:00
idx = [ msg . get ( ' id ' ) for msg in messages ] . index ( message_id ) + 1
# messages.append(_get_expandable(exp_domain, nb, message_id, id_min))
2012-11-05 11:58:52 +00:00
messages . insert ( idx , _get_expandable ( exp_domain , nb , message_id , False ) )
2012-10-03 07:28:45 +00:00
2012-10-23 11:41:24 +00:00
return True
2012-10-18 13:06:01 +00:00
2012-10-31 15:40:26 +00:00
def message_read ( self , cr , uid , ids = None , domain = None , message_unload_ids = None ,
thread_level = 0 , context = None , parent_id = False , limit = None ) :
2012-10-19 14:04:39 +00:00
""" Read messages from mail.message, and get back a list of structured
messages to be displayed as discussion threads . If IDs is set ,
2012-09-07 16:09:20 +00:00
fetch these records . Otherwise use the domain to fetch messages .
2012-10-19 14:04:39 +00:00
After having fetch messages , their ancestors will be added to obtain
well formed threads , if uid has access to them .
2012-10-23 11:41:24 +00:00
After reading the messages , expandable messages are added in the
2012-10-19 14:04:39 +00:00
message list ( see ` ` _message_read_add_expandables ` ` ) . It consists
in messages holding the ' read more ' data : number of messages to
read , domain to apply .
: param list ids : optional IDs to fetch
: param list domain : optional domain for searching ids if ids not set
: param list message_unload_ids : optional ids we do not want to fetch ,
because i . e . they are already displayed somewhere
2012-10-23 13:27:55 +00:00
: param int parent_id : context of parent_id
- if parent_id reached when adding ancestors , stop going further
in the ancestor search
- if set in flat mode , ancestor_id is set to parent_id
2012-10-19 14:04:39 +00:00
: param int limit : number of messages to fetch , before adding the
ancestors and expandables
: return list : list of message structure for the Chatter widget
2012-07-20 09:25:45 +00:00
"""
2012-10-23 11:41:24 +00:00
assert thread_level in [ 0 , 1 ] , ' message_read() thread_level should be 0 (flat) or 1 (1 level of thread); given %s . ' % thread_level
2012-10-22 11:36:51 +00:00
domain = domain if domain is not None else [ ]
message_unload_ids = message_unload_ids if message_unload_ids is not None else [ ]
2012-10-19 14:04:39 +00:00
if message_unload_ids :
domain + = [ ( ' id ' , ' not in ' , message_unload_ids ) ]
2012-08-27 16:22:37 +00:00
limit = limit or self . _message_read_limit
2012-10-31 15:40:26 +00:00
message_tree = { }
2012-10-18 13:06:01 +00:00
message_list = [ ]
2012-10-31 15:40:26 +00:00
parent_tree = { }
2012-10-02 10:52:35 +00:00
2012-10-25 07:51:10 +00:00
# no specific IDS given: fetch messages according to the domain, add their parents if uid has access to
if ids is None :
ids = self . search ( cr , uid , domain , context = context , limit = limit )
2012-10-19 13:13:58 +00:00
2012-10-31 15:40:26 +00:00
# fetch parent if threaded, sort messages
for message in self . browse ( cr , uid , ids , context = context ) :
message_id = message . id
if message_id in message_tree :
continue
message_tree [ message_id ] = message
2012-10-18 15:23:22 +00:00
2012-10-31 15:40:26 +00:00
# find parent_id
if thread_level == 0 :
tree_parent_id = parent_id
else :
tree_parent_id = message_id
parent = message
while parent . parent_id and parent . parent_id . id != parent_id :
parent = parent . parent_id
tree_parent_id = parent . id
if not parent . id in message_tree :
message_tree [ parent . id ] = parent
# newest messages first
parent_tree . setdefault ( tree_parent_id , [ ] )
if tree_parent_id != message_id :
parent_tree [ tree_parent_id ] . append ( self . _message_read_dict ( cr , uid , message_tree [ message_id ] , parent_id = tree_parent_id , context = context ) )
if thread_level :
for key , message_id_list in parent_tree . iteritems ( ) :
message_id_list . sort ( key = lambda item : item [ ' id ' ] )
message_id_list . insert ( 0 , self . _message_read_dict ( cr , uid , message_tree [ key ] , context = context ) )
parent_list = parent_tree . items ( )
parent_list = sorted ( parent_list , key = lambda item : max ( [ msg . get ( ' id ' ) for msg in item [ 1 ] ] ) if item [ 1 ] else item [ 0 ] , reverse = True )
message_list = [ message for ( key , msg_list ) in parent_list for message in msg_list ]
2012-10-19 13:13:58 +00:00
2012-10-19 09:59:19 +00:00
# get the child expandable messages for the tree
2012-10-31 15:40:26 +00:00
self . _message_read_dict_postprocess ( cr , uid , message_list , message_tree , context = context )
2012-11-02 10:22:48 +00:00
self . _message_read_add_expandables ( cr , uid , message_list , message_tree , parent_tree ,
thread_level = thread_level , message_unload_ids = message_unload_ids , domain = domain , parent_id = parent_id , context = context )
2012-10-18 13:18:50 +00:00
return message_list
2012-10-09 08:44:36 +00:00
2012-10-17 13:44:49 +00:00
# TDE Note: do we need this ?
2012-10-19 09:59:19 +00:00
# def user_free_attachment(self, cr, uid, context=None):
# attachment = self.pool.get('ir.attachment')
# attachment_list = []
# attachment_ids = attachment.search(cr, uid, [('res_model', '=', 'mail.message'), ('create_uid', '=', uid)])
# if len(attachment_ids):
# attachment_list = [{'id': attach.id, 'name': attach.name, 'date': attach.create_date} for attach in attachment.browse(cr, uid, attachment_ids, context=context)]
# return attachment_list
2012-10-09 13:28:24 +00:00
2012-02-02 09:48:45 +00:00
#------------------------------------------------------
2012-10-24 14:18:06 +00:00
# mail_message internals
2012-02-02 09:48:45 +00:00
#------------------------------------------------------
2012-08-15 17:08:22 +00:00
2011-07-22 16:34:57 +00:00
def init ( self , cr ) :
cr . execute ( """ SELECT indexname FROM pg_indexes WHERE indexname = ' mail_message_model_res_id_idx ' """ )
if not cr . fetchone ( ) :
cr . execute ( """ CREATE INDEX mail_message_model_res_id_idx ON mail_message (model, res_id) """ )
2012-10-24 14:18:06 +00:00
def _search ( self , cr , uid , args , offset = 0 , limit = None , order = None ,
context = None , count = False , access_rights_uid = None ) :
""" Override that adds specific access rights of mail.message, to remove
ids uid could not see according to our custom rules . Please refer
to check_access_rule for more details about those rules .
After having received ids of a classic search , keep only :
- if author_id == pid , uid is the author , OR
- a notification ( id , pid ) exists , uid has been notified , OR
- uid have read access to the related document is model , res_id
- otherwise : remove the id
"""
# Rules do not apply to administrator
if uid == SUPERUSER_ID :
return super ( mail_message , self ) . _search ( cr , uid , args , offset = offset , limit = limit , order = order ,
2012-10-29 11:21:17 +00:00
context = context , count = count , access_rights_uid = access_rights_uid )
2012-10-24 14:18:06 +00:00
# Perform a super with count as False, to have the ids, not a counter
ids = super ( mail_message , self ) . _search ( cr , uid , args , offset = offset , limit = limit , order = order ,
context = context , count = False , access_rights_uid = access_rights_uid )
if not ids and count :
return 0
elif not ids :
return ids
2012-10-24 15:53:29 +00:00
pid = self . pool . get ( ' res.users ' ) . read ( cr , uid , uid , [ ' partner_id ' ] ) [ ' partner_id ' ] [ 0 ]
author_ids , partner_ids , allowed_ids = set ( [ ] ) , set ( [ ] ) , set ( [ ] )
2012-10-24 14:18:06 +00:00
model_ids = { }
2012-10-25 11:30:48 +00:00
messages = super ( mail_message , self ) . read ( cr , uid , ids , [ ' author_id ' , ' model ' , ' res_id ' , ' notified_partner_ids ' ] , context = context )
2012-10-24 14:18:06 +00:00
for message in messages :
if message . get ( ' author_id ' ) and message . get ( ' author_id ' ) [ 0 ] == pid :
author_ids . add ( message . get ( ' id ' ) )
2012-10-25 11:30:48 +00:00
elif pid in message . get ( ' notified_partner_ids ' ) :
2012-10-24 14:18:06 +00:00
partner_ids . add ( message . get ( ' id ' ) )
elif message . get ( ' model ' ) and message . get ( ' res_id ' ) :
model_ids . setdefault ( message . get ( ' model ' ) , { } ) . setdefault ( message . get ( ' res_id ' ) , set ( ) ) . add ( message . get ( ' id ' ) )
model_access_obj = self . pool . get ( ' ir.model.access ' )
for doc_model , doc_dict in model_ids . iteritems ( ) :
if not model_access_obj . check ( cr , uid , doc_model , ' read ' , False ) :
continue
doc_ids = doc_dict . keys ( )
allowed_doc_ids = self . pool . get ( doc_model ) . search ( cr , uid , [ ( ' id ' , ' in ' , doc_ids ) ] , context = context )
allowed_ids | = set ( [ message_id for allowed_doc_id in allowed_doc_ids for message_id in doc_dict [ allowed_doc_id ] ] )
final_ids = author_ids | partner_ids | allowed_ids
if count :
return len ( final_ids )
else :
return list ( final_ids )
2012-08-31 09:01:20 +00:00
def check_access_rule ( self , cr , uid , ids , operation , context = None ) :
2012-09-14 11:51:07 +00:00
""" Access rules of mail.message:
- read : if
2012-10-24 15:53:29 +00:00
- author_id == pid , uid is the author , OR
- mail_notification ( id , pid ) exists , uid has been notified , OR
- uid have read access to the related document if model , res_id
- otherwise : raise
2012-09-14 11:51:07 +00:00
- create : if
2012-10-24 15:53:29 +00:00
- no model , no res_id , I create a private message
- pid in message_follower_ids if model , res_id OR
- uid have write access on the related document if model , res_id , OR
- otherwise : raise
2012-09-14 11:51:07 +00:00
- write : if
2012-10-24 15:53:29 +00:00
- uid has write access on the related document if model , res_id
2012-09-14 11:51:07 +00:00
- Otherwise : raise
- unlink : if
2012-10-24 15:53:29 +00:00
- uid has write access on the related document if model , res_id
2012-09-14 11:51:07 +00:00
- Otherwise : raise
"""
if uid == SUPERUSER_ID :
return
2012-05-08 13:56:00 +00:00
if isinstance ( ids , ( int , long ) ) :
ids = [ ids ]
2012-09-14 16:16:33 +00:00
partner_id = self . pool . get ( ' res.users ' ) . read ( cr , uid , uid , [ ' partner_id ' ] , context = None ) [ ' partner_id ' ] [ 0 ]
2012-08-15 20:01:26 +00:00
2012-09-14 11:51:07 +00:00
# Read mail_message.ids to have their values
message_values = dict . fromkeys ( ids )
2012-10-24 15:53:29 +00:00
model_record_ids = { }
2012-09-17 16:04:35 +00:00
cr . execute ( ' SELECT DISTINCT id, model, res_id, author_id FROM " %s " WHERE id = ANY ( %% s) ' % self . _table , ( ids , ) )
2012-09-14 11:51:07 +00:00
for id , rmod , rid , author_id in cr . fetchall ( ) :
message_values [ id ] = { ' res_model ' : rmod , ' res_id ' : rid , ' author_id ' : author_id }
2012-09-14 16:16:33 +00:00
if rmod :
2012-10-24 15:53:29 +00:00
model_record_ids . setdefault ( rmod , dict ( ) ) . setdefault ( rid , set ( ) ) . add ( id )
2012-09-14 11:51:07 +00:00
2012-10-24 15:53:29 +00:00
# Author condition, for read and create (private message) -> could become an ir.rule, but not till we do not have a many2one variable field
2012-09-14 11:51:07 +00:00
if operation == ' read ' :
author_ids = [ mid for mid , message in message_values . iteritems ( )
if message . get ( ' author_id ' ) and message . get ( ' author_id ' ) == partner_id ]
2012-10-19 09:59:19 +00:00
elif operation == ' create ' :
author_ids = [ mid for mid , message in message_values . iteritems ( )
if not message . get ( ' model ' ) and not message . get ( ' res_id ' ) ]
2012-09-14 11:51:07 +00:00
else :
author_ids = [ ]
2012-09-13 17:05:00 +00:00
2012-10-24 15:53:29 +00:00
# Notification condition, for read (check for received notifications and create (in message_follower_ids)) -> could become an ir.rule, but not till we do not have a many2one variable field
if operation == ' read ' :
not_obj = self . pool . get ( ' mail.notification ' )
not_ids = not_obj . search ( cr , SUPERUSER_ID , [
( ' partner_id ' , ' = ' , partner_id ) ,
( ' message_id ' , ' in ' , ids ) ,
] , context = context )
notified_ids = [ notification . message_id . id for notification in not_obj . browse ( cr , SUPERUSER_ID , not_ids , context = context ) ]
elif operation == ' create ' :
notified_ids = [ ]
for doc_model , doc_dict in model_record_ids . items ( ) :
2012-09-14 11:51:07 +00:00
fol_obj = self . pool . get ( ' mail.followers ' )
fol_ids = fol_obj . search ( cr , SUPERUSER_ID , [
2012-10-24 15:53:29 +00:00
( ' res_model ' , ' = ' , doc_model ) ,
( ' res_id ' , ' in ' , list ( doc_dict . keys ( ) ) ) ,
2012-09-14 11:51:07 +00:00
( ' partner_id ' , ' = ' , partner_id ) ,
] , context = context )
fol_mids = [ follower . res_id for follower in fol_obj . browse ( cr , SUPERUSER_ID , fol_ids , context = context ) ]
2012-10-24 15:53:29 +00:00
notified_ids + = [ mid for mid , message in message_values . iteritems ( )
if message . get ( ' res_model ' ) == doc_model and message . get ( ' res_id ' ) in fol_mids ]
2012-09-14 11:51:07 +00:00
else :
2012-10-24 15:53:29 +00:00
notified_ids = [ ]
2012-08-31 09:01:20 +00:00
2012-09-14 16:16:33 +00:00
# Calculate remaining ids, and related model/res_ids
2012-08-31 09:01:20 +00:00
model_record_ids = { }
2012-10-24 15:53:29 +00:00
other_ids = set ( ids ) . difference ( set ( author_ids ) , set ( notified_ids ) )
2012-09-14 11:51:07 +00:00
for id in other_ids :
2012-09-14 16:16:33 +00:00
if message_values [ id ] [ ' res_model ' ] :
model_record_ids . setdefault ( message_values [ id ] [ ' res_model ' ] , set ( ) ) . add ( message_values [ id ] [ ' res_id ' ] )
2012-09-14 11:51:07 +00:00
# CRUD: Access rights related to the document
document_related_ids = [ ]
2012-08-31 09:01:20 +00:00
for model , mids in model_record_ids . items ( ) :
model_obj = self . pool . get ( model )
mids = model_obj . exists ( cr , uid , mids )
2012-09-14 11:51:07 +00:00
if operation in [ ' create ' , ' write ' , ' unlink ' ] :
model_obj . check_access_rights ( cr , uid , ' write ' )
model_obj . check_access_rule ( cr , uid , mids , ' write ' , context = context )
else :
model_obj . check_access_rights ( cr , uid , operation )
model_obj . check_access_rule ( cr , uid , mids , operation , context = context )
document_related_ids + = [ mid for mid , message in message_values . iteritems ( )
if message . get ( ' res_model ' ) == model and message . get ( ' res_id ' ) in mids ]
2012-08-31 09:01:20 +00:00
2012-09-14 16:16:33 +00:00
# Calculate remaining ids: if not void, raise an error
2012-10-24 15:53:29 +00:00
other_ids = other_ids - set ( document_related_ids )
2012-09-14 16:16:33 +00:00
if not other_ids :
return
2012-09-19 10:13:39 +00:00
raise orm . except_orm ( _ ( ' Access Denied ' ) ,
2012-09-14 16:16:33 +00:00
_ ( ' The requested operation cannot be completed due to security restrictions. Please contact your system administrator. \n \n (Document type: %s , Operation: %s ) ' ) % \
( self . _description , operation ) )
2012-08-15 17:08:22 +00:00
2012-05-08 13:56:00 +00:00
def create ( self , cr , uid , values , context = None ) :
2012-08-28 17:39:01 +00:00
if not values . get ( ' message_id ' ) and values . get ( ' res_id ' ) and values . get ( ' model ' ) :
2012-10-09 13:50:40 +00:00
values [ ' message_id ' ] = tools . generate_tracking_message_id ( ' %(res_id)s - %(model)s ' % values )
2012-11-08 16:41:38 +00:00
elif not values . get ( ' message_id ' ) :
values [ ' message_id ' ] = tools . generate_tracking_message_id ( ' private ' )
2012-08-15 13:36:43 +00:00
newid = super ( mail_message , self ) . create ( cr , uid , values , context )
2012-10-04 04:46:24 +00:00
self . _notify ( cr , SUPERUSER_ID , newid , context = context )
2012-08-15 13:36:43 +00:00
return newid
2012-05-08 13:56:00 +00:00
2012-09-14 16:16:33 +00:00
def read ( self , cr , uid , ids , fields = None , context = None , load = ' _classic_read ' ) :
""" Override to explicitely call check_access_rule, that is not called
by the ORM . It instead directly fetches ir . rules and apply them . """
self . check_access_rule ( cr , uid , ids , ' read ' , context = context )
2012-10-11 16:16:14 +00:00
res = super ( mail_message , self ) . read ( cr , uid , ids , fields = fields , context = context , load = load )
2012-09-14 16:16:33 +00:00
return res
2012-09-05 15:19:50 +00:00
def unlink ( self , cr , uid , ids , context = None ) :
# cascade-delete attachments that are directly attached to the message (should only happen
2012-09-07 11:54:22 +00:00
# for mail.messages that act as parent for a standalone mail.mail record).
2012-10-11 16:16:14 +00:00
self . check_access_rule ( cr , uid , ids , ' unlink ' , context = context )
2012-09-05 15:19:50 +00:00
attachments_to_delete = [ ]
2012-09-06 13:42:01 +00:00
for message in self . browse ( cr , uid , ids , context = context ) :
for attach in message . attachment_ids :
2012-09-07 11:54:22 +00:00
if attach . res_model == self . _name and attach . res_id == message . id :
2012-09-05 15:19:50 +00:00
attachments_to_delete . append ( attach . id )
if attachments_to_delete :
self . pool . get ( ' ir.attachment ' ) . unlink ( cr , uid , attachments_to_delete , context = context )
2012-09-06 12:52:17 +00:00
return super ( mail_message , self ) . unlink ( cr , uid , ids , context = context )
2012-09-05 15:19:50 +00:00
2012-10-26 11:33:36 +00:00
def copy ( self , cr , uid , id , default = None , context = None ) :
""" Overridden to avoid duplicating fields that are unique to each email """
if default is None :
default = { }
default . update ( message_id = False , headers = False )
return super ( mail_message , self ) . copy ( cr , uid , id , default = default , context = context )
2012-10-24 14:18:06 +00:00
#------------------------------------------------------
# Messaging API
#------------------------------------------------------
2012-10-29 11:21:17 +00:00
# TDE note: this code is not used currently, will be improved in a future merge, when quoted context
# will be added to email send for notifications. Currently only WIP.
2012-10-26 11:33:36 +00:00
MAIL_TEMPLATE = """ <div>
% if message :
$ { display_message ( message ) }
% endif
% for ctx_msg in context_messages :
$ { display_message ( ctx_msg ) }
% endfor
% if add_expandable :
$ { display_expandable ( ) }
% endif
$ { display_message ( header_message ) }
< / div >
< % def name = " display_message(message) " >
< div >
Subject : $ { message . subject } < br / >
Body : $ { message . body }
< / div >
< / % def >
< % def name = " display_expandable() " >
< div > This is an expandable . < / div >
< / % def >
"""
def message_quote_context ( self , cr , uid , id , context = None , limit = 3 , add_original = False ) :
2012-08-23 18:06:48 +00:00
"""
2012-10-26 11:33:36 +00:00
1. message . parent_id = False : new thread , no quote_context
2. get the lasts messages in the thread before message
3. get the message header
4. add an expandable between them
: param dict quote_context : options for quoting
: return string : html quote
"""
add_expandable = False
message = self . browse ( cr , uid , id , context = context )
if not message . parent_id :
return ' '
context_ids = self . search ( cr , uid , [
( ' parent_id ' , ' = ' , message . parent_id . id ) ,
( ' id ' , ' < ' , message . id ) ,
] , limit = limit , context = context )
if len ( context_ids ) > = limit :
add_expandable = True
context_ids = context_ids [ 0 : - 1 ]
context_ids . append ( message . parent_id . id )
context_messages = self . browse ( cr , uid , context_ids , context = context )
header_message = context_messages . pop ( )
try :
if not add_original :
message = False
result = MakoTemplate ( self . MAIL_TEMPLATE ) . render_unicode ( message = message ,
context_messages = context_messages ,
header_message = header_message ,
add_expandable = add_expandable ,
# context kw would clash with mako internals
ctx = context ,
format_exceptions = True )
result = result . strip ( )
return result
except Exception :
_logger . exception ( " failed to render mako template for quoting message " )
return ' '
return result
2012-09-28 13:27:23 +00:00
2012-10-03 14:27:12 +00:00
def _notify ( self , cr , uid , newid , context = None ) :
""" Add the related record followers to the destination partner_ids if is not a private message.
Call mail_notification . notify to manage the email sending
"""
2012-11-30 14:48:51 +00:00
message = self . read ( cr , uid , newid , [ ' model ' , ' res_id ' , ' author_id ' , ' subtype_id ' , ' partner_ids ' , ' parent_id ' ] , context = context )
2012-10-17 14:19:33 +00:00
2012-08-30 12:21:12 +00:00
partners_to_notify = set ( [ ] )
2012-09-20 10:17:04 +00:00
# message has no subtype_id: pure log message -> no partners, no one notified
2012-10-25 11:30:48 +00:00
if not message . get ( ' subtype_id ' ) :
2012-09-20 10:17:04 +00:00
return True
# all partner_ids of the mail.message have to be notified
2012-10-25 11:30:48 +00:00
if message . get ( ' partner_ids ' ) :
partners_to_notify | = set ( message . get ( ' partner_ids ' ) )
2012-09-20 10:17:04 +00:00
# all followers of the mail.message document have to be added as partners and notified
2012-10-25 11:30:48 +00:00
if message . get ( ' model ' ) and message . get ( ' res_id ' ) :
2012-09-20 10:17:04 +00:00
fol_obj = self . pool . get ( " mail.followers " )
2012-10-25 11:30:48 +00:00
fol_ids = fol_obj . search ( cr , uid , [
( ' res_model ' , ' = ' , message . get ( ' model ' ) ) ,
( ' res_id ' , ' = ' , message . get ( ' res_id ' ) ) ,
( ' subtype_ids ' , ' in ' , message . get ( ' subtype_id ' ) [ 0 ] )
] , context = context )
fol_objs = fol_obj . read ( cr , uid , fol_ids , [ ' partner_id ' ] , context = context )
partners_to_notify | = set ( fol [ ' partner_id ' ] [ 0 ] for fol in fol_objs )
2012-11-07 10:51:48 +00:00
# remove me from notified partners, unless the message is written on my own wall
2012-10-25 11:30:48 +00:00
if message . get ( ' author_id ' ) and message . get ( ' model ' ) == " res.partner " and message . get ( ' res_id ' ) == message . get ( ' author_id ' ) [ 0 ] :
partners_to_notify | = set ( [ message . get ( ' author_id ' ) [ 0 ] ] )
2012-10-25 13:50:20 +00:00
elif message . get ( ' author_id ' ) :
2012-10-25 11:30:48 +00:00
partners_to_notify = partners_to_notify - set ( [ message . get ( ' author_id ' ) [ 0 ] ] )
2012-10-25 13:50:20 +00:00
2012-10-25 11:30:48 +00:00
if partners_to_notify :
self . write ( cr , SUPERUSER_ID , [ newid ] , { ' notified_partner_ids ' : [ ( 4 , p_id ) for p_id in partners_to_notify ] } , context = context )
2012-10-05 10:46:54 +00:00
2012-11-30 14:48:51 +00:00
notification_obj = self . pool . get ( ' mail.notification ' )
notification_obj . _notify ( cr , uid , newid , context = context )
2012-11-30 14:50:39 +00:00
# An error appear when a user receive a notify to a message without notify to his parent message.
# Add a notification with read = true to the parented message if there are no notification
2012-11-30 14:48:51 +00:00
if message . get ( ' parent_id ' ) and message . get ( ' parent_id ' ) [ 0 ] :
partners_to_parent_notify = set ( partners_to_notify ) - set ( self . read ( cr , SUPERUSER_ID , message . get ( ' parent_id ' ) [ 0 ] , [ ' notified_partner_ids ' ] , context = context ) . get ( ' notified_partner_ids ' ) )
for partner_id in partners_to_parent_notify :
notification_obj . create ( cr , uid , {
' message_id ' : message . get ( ' parent_id ' ) [ 0 ] ,
' partner_id ' : partner_id ,
' read ' : True ,
} , context = context )
2012-08-23 18:06:48 +00:00
2012-09-12 13:35:22 +00:00
#------------------------------------------------------
# Tools
#------------------------------------------------------
2012-09-13 15:48:44 +00:00
def check_partners_email ( self , cr , uid , partner_ids , context = None ) :
2012-09-12 13:35:22 +00:00
""" Verify that selected partner_ids have an email_address defined.
Otherwise throw a warning . """
partner_wo_email_lst = [ ]
for partner in self . pool . get ( ' res.partner ' ) . browse ( cr , uid , partner_ids , context = context ) :
if not partner . email :
partner_wo_email_lst . append ( partner )
if not partner_wo_email_lst :
return { }
warning_msg = _ ( ' The following partners chosen as recipients for the email have no email address linked : ' )
for partner in partner_wo_email_lst :
warning_msg + = ' \n - %s ' % ( partner . name )
return { ' warning ' : {
' title ' : _ ( ' Partners email addresses not found ' ) ,
' message ' : warning_msg ,
}
}