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-13 12:11:10 +00:00
import openerp
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-09-05 15:53:19 +00:00
from operator import itemgetter
2012-09-19 10:13:39 +00:00
from osv import osv , orm , fields
2012-09-12 13:35:22 +00:00
from 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-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 '
2011-07-22 16:34:57 +00:00
2012-08-27 09:42:28 +00:00
_message_read_limit = 10
_message_record_name_length = 18
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-08-27 09:42:28 +00:00
""" Return the related document name, using get_name. """
2012-10-17 13:44:49 +00:00
result = dict . fromkeys ( ids , False )
for message in self . browse ( cr , 1 , ids , context = context ) :
2012-03-27 07:30:53 +00:00
if not message . model or not message . res_id :
continue
2012-09-13 12:11:10 +00:00
try :
result [ message . id ] = self . _shorten_name ( self . pool . get ( message . model ) . name_get ( cr , uid , [ message . res_id ] , context = context ) [ 0 ] [ 1 ] )
2012-09-19 10:13:39 +00:00
except ( orm . except_orm , osv . except_osv ) :
2012-09-13 12:11:10 +00:00
pass
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-17 15:57:33 +00:00
res = dict ( ( id , { ' to_read ' : 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 ] ) ,
( ' message_id ' , ' in ' , ids ) ,
( ' read ' , ' = ' , False )
] , context = context )
for notif in notif_obj . browse ( cr , uid , notif_ids , context = context ) :
2012-10-17 15:57:33 +00:00
res [ notif . message_id . id ] [ ' to_read ' ] = 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 ] :
read_cond = ' (read = false or read is null) '
else :
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-08-15 17:08:22 +00:00
' author_id ' : fields . many2one ( ' res.partner ' , ' Author ' , required = True ) ,
2012-08-23 18:54:43 +00:00
' partner_ids ' : fields . many2many ( ' res.partner ' , ' mail_notification ' , ' message_id ' , ' partner_id ' , ' Recipients ' ) ,
' attachment_ids ' : fields . many2many ( ' ir.attachment ' , ' message_attachment_rel ' ,
' message_id ' , ' attachment_id ' , ' Attachments ' ) ,
' 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-08-28 09:53:23 +00:00
' record_name ' : fields . function ( _get_record_name , type = ' string ' ,
2012-07-06 09:41:41 +00:00
string = ' Message Record Name ' ,
help = " Name get of the related document. " ) ,
2012-08-20 11:31:50 +00:00
' notification_ids ' : fields . one2many ( ' mail.notification ' , ' message_id ' , ' Notifications ' ) ,
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-08-30 11:24:26 +00:00
' subtype_id ' : fields . many2one ( ' mail.message.subtype ' , ' Subtype ' ) ,
2012-09-18 15:05:44 +00:00
' vote_user_ids ' : fields . many2many ( ' res.users ' , ' mail_vote ' , ' message_id ' , ' user_id ' , string = ' Votes ' ,
help = ' Users that voted for this message ' ) ,
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 15:57:33 +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-09-13 17:05:00 +00:00
# remove context to avoid possible hack in browse with superadmin using context keys that could trigger a specific behavior
return self . pool . get ( ' res.users ' ) . browse ( cr , SUPERUSER_ID , uid , context = None ) . partner_id . id
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-09-12 12:56:23 +00:00
def vote_toggle ( self , cr , uid , ids , user_ids = None , context = None ) :
2012-09-19 07:50:51 +00:00
''' Toggles voting. Done as SUPERUSER_ID because of write access on
mail . message not always granted . '''
2012-09-18 12:21:39 +00:00
if not user_ids :
user_ids = [ uid ]
for message in self . read ( cr , uid , ids , [ ' vote_user_ids ' ] , context = context ) :
for user_id in user_ids :
2012-09-18 15:05:44 +00:00
has_voted = user_id in message . get ( ' vote_user_ids ' )
2012-09-18 12:21:39 +00:00
if not has_voted :
2012-09-19 07:50:51 +00:00
self . write ( cr , SUPERUSER_ID , message . get ( ' id ' ) , { ' vote_user_ids ' : [ ( 4 , user_id ) ] } , context = context )
2012-09-18 12:21:39 +00:00
else :
2012-09-19 07:50:51 +00:00
self . write ( cr , SUPERUSER_ID , message . get ( ' id ' ) , { ' vote_user_ids ' : [ ( 3 , user_id ) ] } , context = context )
2012-10-01 09:36:33 +00:00
return not ( has_voted ) or False
2012-08-15 17:08:22 +00:00
2012-08-20 09:06:36 +00:00
#------------------------------------------------------
# Message loading for web interface
#------------------------------------------------------
2012-10-17 13:44:49 +00:00
def _message_get_dict ( self , cr , uid , message , context = None ) :
""" Return a dict representation of the message.
: param dict message : read result of a mail . message
2012-09-19 10:13:39 +00:00
"""
2012-10-17 13:44:49 +00:00
if uid in message [ ' vote_user_ids ' ] :
has_voted = True
else :
has_voted = False
2012-09-19 10:13:39 +00:00
try :
2012-10-17 13:54:51 +00:00
attachment_ids = [ { ' id ' : attach [ 0 ] , ' name ' : attach [ 1 ] } for attach in self . pool . get ( ' ir.attachment ' ) . name_get ( cr , uid , message [ ' attachment_ids ' ] , context = context ) ]
2012-09-19 10:13:39 +00:00
except ( orm . except_orm , osv . except_osv ) :
attachment_ids = [ ]
2012-10-17 13:44:49 +00:00
2012-09-19 10:13:39 +00:00
try :
2012-10-17 13:54:51 +00:00
partner_ids = self . pool . get ( ' res.partner ' ) . name_get ( cr , uid , message [ ' partner_ids ' ] , context = context )
2012-09-19 10:13:39 +00:00
except ( orm . except_orm , osv . except_osv ) :
partner_ids = [ ]
2012-09-28 13:27:23 +00:00
2012-08-20 09:39:22 +00:00
return {
2012-10-17 13:44:49 +00:00
' id ' : message [ ' id ' ] ,
' type ' : message [ ' type ' ] ,
2012-08-27 14:47:53 +00:00
' attachment_ids ' : attachment_ids ,
2012-10-17 13:44:49 +00:00
' body ' : message [ ' body ' ] ,
' model ' : message [ ' model ' ] ,
' res_id ' : message [ ' res_id ' ] ,
' record_name ' : message [ ' record_name ' ] ,
' subject ' : message [ ' subject ' ] ,
' date ' : message [ ' date ' ] ,
' author_id ' : message [ ' author_id ' ] ,
' is_author ' : message [ ' author_id ' ] and message [ ' author_id ' ] [ 0 ] == uid ,
2012-08-20 10:57:49 +00:00
' partner_ids ' : partner_ids ,
2012-10-17 13:44:49 +00:00
' parent_id ' : message [ ' parent_id ' ] and message [ ' parent_id ' ] [ 0 ] or False ,
# 'vote_user_ids': vote_ids,
2012-09-27 13:48:23 +00:00
' has_voted ' : has_voted ,
2012-10-17 15:57:33 +00:00
' to_read ' : message [ ' to_read ' ] ,
2012-08-20 09:39:22 +00:00
}
2012-10-03 10:09:52 +00:00
def _message_read_expandable ( self , cr , uid , tree , result , message_loaded , domain , context , parent_id , limit ) :
2012-10-17 13:44:49 +00:00
""" Create the expandable message for all parent message read
this function is used by message_read
TDE note : place use default values for args , and comment your vars ! !
2012-09-20 14:46:45 +00:00
2012-10-17 13:44:49 +00:00
: param dict tree : tree of message ids
"""
tree_not = [ ]
2012-10-03 07:28:45 +00:00
# expandable for not show message
2012-10-17 13:44:49 +00:00
for msg_id in tree :
2012-10-03 07:28:45 +00:00
# get all childs
2012-10-17 13:44:49 +00:00
not_loaded_ids = self . search ( cr , SUPERUSER_ID , [
( ' parent_id ' , ' = ' , msg_id ) ,
( ' id ' , ' not in ' , message_loaded )
] , context = context , limit = 1000 )
2012-10-03 07:28:45 +00:00
# group childs not read
2012-10-17 13:44:49 +00:00
id_min = None
id_max = None
nb = 0
2012-10-03 07:28:45 +00:00
for not_loaded_id in not_loaded_ids :
if not_loaded_id not in tree :
2012-10-17 13:44:49 +00:00
nb + = 1
if id_min == None or id_min > not_loaded_id :
id_min = not_loaded_id
if id_max == None or id_max < not_loaded_id :
id_max = not_loaded_id
2012-10-03 07:28:45 +00:00
tree_not . append ( not_loaded_id )
else :
2012-10-17 13:44:49 +00:00
if nb > 0 :
2012-10-03 07:28:45 +00:00
result . append ( {
2012-10-17 13:44:49 +00:00
' domain ' : [ ( ' id ' , ' >= ' , id_min ) , ( ' id ' , ' <= ' , id_max ) , ( ' parent_id ' , ' = ' , msg_id ) ] ,
2012-10-03 07:28:45 +00:00
' nb_messages ' : nb ,
2012-10-17 13:44:49 +00:00
' type ' : ' expandable ' ,
' parent_id ' : msg_id ,
' id ' : id_min ,
2012-10-03 07:28:45 +00:00
} )
2012-10-17 13:44:49 +00:00
id_min = None
id_max = None
nb = 0
if nb > 0 :
2012-10-03 07:28:45 +00:00
result . append ( {
2012-10-17 13:44:49 +00:00
' domain ' : [ ( ' id ' , ' >= ' , id_min ) , ( ' id ' , ' <= ' , id_max ) , ( ' parent_id ' , ' = ' , msg_id ) ] ,
2012-10-03 07:28:45 +00:00
' nb_messages ' : nb ,
2012-10-17 13:44:49 +00:00
' type ' : ' expandable ' ,
' parent_id ' : msg_id ,
2012-10-03 07:28:45 +00:00
' id ' : id_min
} )
2012-09-20 14:46:45 +00:00
2012-10-03 07:28:45 +00:00
# expandable for limit max
2012-10-17 13:44:49 +00:00
ids = self . search ( cr , SUPERUSER_ID , domain + [ ( ' id ' , ' not in ' , message_loaded + tree + tree_not ) ] , context = context , limit = 1 )
2012-10-03 07:28:45 +00:00
if len ( ids ) > 0 :
2012-10-17 13:44:49 +00:00
result . append ( {
2012-10-03 07:28:45 +00:00
' domain ' : domain ,
' nb_messages ' : 0 ,
2012-10-17 13:44:49 +00:00
' type ' : ' expandable ' ,
' parent_id ' : parent_id ,
2012-10-03 07:28:45 +00:00
' id ' : - 1
2012-10-17 13:44:49 +00:00
} )
2012-10-03 07:28:45 +00:00
result = sorted ( result , key = lambda k : k [ ' id ' ] )
return result
2012-09-07 16:09:20 +00:00
2012-10-17 15:57:33 +00:00
_message_read_fields = [ ' id ' , ' parent_id ' , ' model ' , ' res_id ' , ' body ' , ' subject ' , ' date ' , ' to_read ' ,
' type ' , ' vote_user_ids ' , ' attachment_ids ' , ' author_id ' , ' partner_ids ' , ' record_name ' ]
2012-10-17 13:44:49 +00:00
def _get_parent ( self , cr , uid , message , context = None ) :
""" Tools method that try to get the parent of a mail.message. If
no parent , or if uid has no access right on the parent , False
is returned .
: param dict message : read result of a mail . message
"""
if not message [ ' parent_id ' ] :
return False
parent_id = message [ ' parent_id ' ] [ 0 ]
try :
return self . read ( cr , uid , parent_id , self . _message_read_fields , context = context )
except ( orm . except_orm , osv . except_osv ) :
return False
def message_read ( self , cr , uid , ids = False , domain = [ ] , context = None , parent_id = False , limit = None ) :
2012-09-07 16:09:20 +00:00
""" Read messages from mail.message, and get back a structured tree
of messages to be displayed as discussion threads . If IDs is set ,
fetch these records . Otherwise use the domain to fetch messages .
After having fetch messages , their parents will be added to obtain
well formed threads .
2012-08-28 14:26:34 +00:00
2012-09-07 16:09:20 +00:00
: param domain : optional domain for searching ids
: param limit : number of messages to fetch
2012-09-11 12:01:17 +00:00
: param parent_id : if parent_id reached , stop searching for
2012-09-07 16:09:20 +00:00
further parents
2012-08-31 14:37:07 +00:00
: return list : list of trees of messages
2012-07-20 09:25:45 +00:00
"""
2012-09-28 13:27:23 +00:00
# don't read the message display by .js, in context message_loaded list
2012-10-17 13:44:49 +00:00
# TDE note: use an argument, do not use context
if context is None :
context = { }
if context . get ( ' message_loaded ' ) :
domain + = [ ( ' id ' , ' not in ' , context . get ( ' message_loaded ' ) ) ]
2012-08-27 16:22:37 +00:00
limit = limit or self . _message_read_limit
2012-10-17 13:44:49 +00:00
# tree = []
# result = []
2012-10-02 13:48:43 +00:00
record = None
2012-10-02 15:12:45 +00:00
2012-10-17 13:44:49 +00:00
id_tree = [ ]
message_list = [ ]
2012-10-02 15:12:45 +00:00
# select ids
2012-10-17 13:44:49 +00:00
# TDE note: should not receive [None] !!
if ids and ids != [ None ] :
for message in self . read ( cr , uid , ids , self . _message_read_fields , context = context ) :
2012-10-17 14:33:35 +00:00
message_list . append ( self . _message_get_dict ( cr , uid , message , context = context ) )
2012-10-17 13:44:49 +00:00
return message_list
2012-10-02 15:12:45 +00:00
# key: ID, value: record
ids = self . search ( cr , SUPERUSER_ID , domain , context = context , limit = limit )
2012-10-17 13:44:49 +00:00
for message in self . read ( cr , uid , ids , self . _message_read_fields , context = context ) :
2012-09-28 13:27:23 +00:00
# if not in record and not in message_loded list
2012-10-17 13:44:49 +00:00
if message [ ' id ' ] not in id_tree and message [ ' id ' ] not in context . get ( ' message_loaded ' , [ ] ) :
record = self . _message_get_dict ( cr , uid , message , context = context )
id_tree . append ( message [ ' id ' ] )
message_list . append ( record )
parent = self . _get_parent ( cr , uid , message , context = context )
while parent and parent [ ' id ' ] != parent_id :
if parent [ ' id ' ] not in id_tree :
message = parent
id_tree . append ( message [ ' id ' ] )
2012-10-02 10:52:35 +00:00
# if not in record and not in message_loded list
2012-10-17 13:44:49 +00:00
if message [ ' id ' ] not in context . get ( ' message_loaded ' , [ ] ) :
record = self . _message_get_dict ( cr , uid , message , context = context )
message_list . append ( record )
2012-10-17 13:54:51 +00:00
parent = self . _get_parent ( cr , uid , parent , context = context )
2012-08-28 14:26:34 +00:00
2012-10-17 13:44:49 +00:00
message_list = sorted ( message_list , key = lambda k : k [ ' id ' ] )
2012-10-02 13:48:43 +00:00
2012-10-17 13:44:49 +00:00
message_list = self . _message_read_expandable ( cr , uid , id_tree , message_list , context . get ( ' message_loaded ' , [ ] ) , domain , context , parent_id , limit )
2012-10-02 13:48:43 +00:00
2012-10-17 13:44:49 +00:00
return message_list
2012-08-20 09:06:36 +00:00
2012-10-17 13:44:49 +00:00
# TDE Note: do we need this ?
# def user_free_attachment(self, cr, uid, context=None):
# attachment_list = []
2012-10-09 08:44:36 +00:00
2012-10-17 13:44:49 +00:00
# attachment = self.pool.get('ir.attachment')
# attachment_ids = attachment.search(cr, uid, [('res_model','=',''),('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)]
2012-10-09 08:44:36 +00:00
2012-10-17 13:44:49 +00:00
# return attachment_list
2012-10-09 13:28:24 +00:00
2012-02-02 09:48:45 +00:00
#------------------------------------------------------
2012-06-25 13:42:53 +00:00
# Email api
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-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
- notification exist ( I receive pushed message ) OR
- author_id = pid ( I am the author ) OR
- I can read the related document if res_model , res_id
- Otherwise : raise
- create : if
- I am in the document message_follower_ids OR
- I can write on the related document if res_model , res_id
- Otherwise : raise
- write : if
- I can write on the related document if res_model , res_id
- Otherwise : raise
- unlink : if
- I can write on the related document if res_model , res_id
- 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
model_record_ids = { }
message_values = dict . fromkeys ( 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 :
model_record_ids . setdefault ( rmod , set ( ) ) . add ( rid )
2012-09-14 11:51:07 +00:00
2012-09-14 16:16:33 +00:00
# Read: Check for received notifications -> 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 ' :
2012-09-13 17:05:00 +00:00
not_obj = self . pool . get ( ' mail.notification ' )
2012-09-14 11:51:07 +00:00
not_ids = not_obj . search ( cr , SUPERUSER_ID , [
2012-09-13 17:05:00 +00:00
( ' partner_id ' , ' = ' , partner_id ) ,
( ' message_id ' , ' in ' , ids ) ,
] , context = context )
2012-09-14 11:51:07 +00:00
notified_ids = [ notification . message_id . id for notification in not_obj . browse ( cr , SUPERUSER_ID , not_ids , context = context ) ]
else :
notified_ids = [ ]
2012-09-14 16:16:33 +00:00
# Read: Check messages you are author -> 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 ]
else :
author_ids = [ ]
2012-09-13 17:05:00 +00:00
2012-09-14 16:16:33 +00:00
# Create: Check message_follower_ids
2012-09-14 11:51:07 +00:00
if operation == ' create ' :
doc_follower_ids = [ ]
for model , mids in model_record_ids . items ( ) :
fol_obj = self . pool . get ( ' mail.followers ' )
fol_ids = fol_obj . search ( cr , SUPERUSER_ID , [
( ' res_model ' , ' = ' , model ) ,
( ' res_id ' , ' in ' , list ( mids ) ) ,
( ' partner_id ' , ' = ' , partner_id ) ,
] , context = context )
fol_mids = [ follower . res_id for follower in fol_obj . browse ( cr , SUPERUSER_ID , fol_ids , context = context ) ]
doc_follower_ids + = [ mid for mid , message in message_values . iteritems ( )
2012-09-14 16:16:33 +00:00
if message . get ( ' res_model ' ) == model and message . get ( ' res_id ' ) in fol_mids ]
2012-09-14 11:51:07 +00:00
else :
doc_follower_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-09-14 11:51:07 +00:00
other_ids = set ( ids ) . difference ( set ( notified_ids ) , set ( author_ids ) , set ( doc_follower_ids ) )
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-09-14 11:51:07 +00:00
other_ids = set ( ids ) . difference ( set ( notified_ids ) , set ( author_ids ) , set ( doc_follower_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-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 . """
res = super ( mail_message , self ) . read ( cr , uid , ids , fields = fields , context = context , load = load )
self . check_access_rule ( cr , uid , ids , ' read ' , context = context )
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-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-03 14:27:12 +00:00
def _notify_followers ( self , cr , uid , newid , message , context = None ) :
2012-08-23 18:06:48 +00:00
""" Add the related record followers to the destination partner_ids.
"""
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
if not message . subtype_id :
message . write ( { ' partner_ids ' : [ 5 ] } )
return True
# all partner_ids of the mail.message have to be notified
2012-08-23 18:06:48 +00:00
if message . partner_ids :
2012-08-31 08:42:35 +00:00
partners_to_notify | = set ( partner . id for partner in message . 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-08-23 18:06:48 +00:00
if message . model and message . res_id :
2012-09-20 10:17:04 +00:00
fol_obj = self . pool . get ( " mail.followers " )
fol_ids = fol_obj . search ( cr , uid , [ ( ' res_model ' , ' = ' , message . model ) , ( ' res_id ' , ' = ' , message . res_id ) , ( ' subtype_ids ' , ' in ' , message . subtype_id . id ) ] , context = context )
fol_objs = fol_obj . browse ( cr , uid , fol_ids , context = context )
extra_notified = set ( fol . partner_id . id for fol in fol_objs )
2012-08-30 12:21:12 +00:00
missing_notified = extra_notified - partners_to_notify
2012-09-28 13:27:23 +00:00
missing_notified = missing_notified
2012-09-20 10:17:04 +00:00
if missing_notified :
2012-09-14 11:51:07 +00:00
self . write ( cr , SUPERUSER_ID , [ newid ] , { ' partner_ids ' : [ ( 4 , p_id ) for p_id in missing_notified ] } , context = context )
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
"""
message = self . browse ( cr , uid , newid , context = context )
2012-10-17 14:19:33 +00:00
if message and message . model and message . res_id :
2012-10-03 14:27:12 +00:00
self . _notify_followers ( cr , uid , newid , message , context = context )
2012-10-17 14:19:33 +00:00
# add myself if I wrote on my wall, otherwise remove myself author
if ( ( message . model == " res.partner " and message . res_id == message . author_id . id ) ) :
2012-10-05 10:46:54 +00:00
self . write ( cr , SUPERUSER_ID , [ newid ] , { ' partner_ids ' : [ ( 4 , message . author_id . id ) ] } , context = context )
else :
self . write ( cr , SUPERUSER_ID , [ newid ] , { ' partner_ids ' : [ ( 3 , message . author_id . id ) ] } , context = context )
2012-10-01 21:06:53 +00:00
self . pool . get ( ' mail.notification ' ) . _notify ( cr , uid , newid , context = context )
2012-08-23 18:06:48 +00:00
2011-08-23 17:58:09 +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 = { }
2012-08-15 18:44:03 +00:00
default . update ( message_id = False , headers = False )
2012-09-05 15:53:19 +00:00
return super ( mail_message , self ) . copy ( cr , uid , id , default = default , context = context )
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 ,
}
}