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-03-27 07:30:53 +00:00
result = dict . fromkeys ( ids , ' ' )
for message in self . browse ( cr , uid , ids , context = context ) :
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-08-28 09:53:23 +00:00
def _get_unread ( self , cr , uid , ids , name , arg , context = None ) :
""" Compute if the message is unread by the current user. """
2012-08-28 15:02:44 +00:00
res = dict ( ( id , { ' unread ' : 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 ) :
res [ notif . message_id . id ] [ ' unread ' ] = True
return res
def _search_unread ( self , cr , uid , obj , name , domain , context = None ) :
2012-09-05 15:53:19 +00:00
""" Search for messages unread 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-08-28 09:53:23 +00:00
' unread ' : fields . function ( _get_unread , fnct_search = _search_unread ,
type = ' boolean ' , string = ' Unread ' ,
help = ' Functional field to search for unread messages linked to uid ' ) ,
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-08-28 09:53:23 +00:00
return [ ( ' unread ' , ' = ' , 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-08-27 14:47:53 +00:00
def _message_dict_get ( self , cr , uid , msg , context = None ) :
2012-09-19 10:13:39 +00:00
""" Return a dict representation of the message browse record. A read
is performed to because of access rights issues ( reading many2one
fields allow to have the foreign record name without having
to check external access rights ) .
"""
2012-09-11 12:01:17 +00:00
child_nbr = len ( msg . child_ids )
2012-09-18 12:21:39 +00:00
has_voted = False
2012-09-19 07:50:51 +00:00
vote_ids = self . pool . get ( ' res.users ' ) . name_get ( cr , SUPERUSER_ID , [ user . id for user in msg . vote_user_ids ] , context = context )
2012-09-18 15:05:44 +00:00
for vote in vote_ids :
if vote [ 0 ] == uid :
2012-09-18 12:21:39 +00:00
has_voted = True
break
2012-09-19 10:13:39 +00:00
try :
attachment_ids = [ { ' id ' : attach [ 0 ] , ' name ' : attach [ 1 ] } for attach in self . pool . get ( ' ir.attachment ' ) . name_get ( cr , uid , [ x . id for x in msg . attachment_ids ] , context = context ) ]
except ( orm . except_orm , osv . except_osv ) :
attachment_ids = [ ]
try :
author_id = self . pool . get ( ' res.partner ' ) . name_get ( cr , uid , [ msg . author_id . id ] , context = context ) [ 0 ]
is_author = uid in msg . author_id . user_ids
except ( orm . except_orm , osv . except_osv ) :
author_id = False
is_author = False
try :
partner_ids = self . pool . get ( ' res.partner ' ) . name_get ( cr , uid , [ x . id for x in msg . partner_ids ] , context = context )
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 {
' id ' : msg . id ,
' type ' : msg . type ,
2012-08-27 14:47:53 +00:00
' attachment_ids ' : attachment_ids ,
2012-08-20 09:39:22 +00:00
' body ' : msg . body ,
' model ' : msg . model ,
' res_id ' : msg . res_id ,
' record_name ' : msg . record_name ,
' subject ' : msg . subject ,
' date ' : msg . date ,
2012-08-27 14:47:53 +00:00
' author_id ' : author_id ,
2012-09-19 10:13:39 +00:00
' is_author ' : is_author ,
2012-08-20 10:57:49 +00:00
' partner_ids ' : partner_ids ,
2012-08-27 14:47:53 +00:00
' child_ids ' : [ ] ,
2012-09-11 12:01:17 +00:00
' child_nbr ' : child_nbr ,
2012-09-26 07:17:09 +00:00
' parent_id ' : msg . parent_id and msg . parent_id . id or False ,
2012-09-07 12:10:28 +00:00
' vote_user_ids ' : vote_ids ,
2012-09-27 13:48:23 +00:00
' has_voted ' : has_voted ,
' unread ' : msg . unread and msg . unread [ ' unread ' ] or False
2012-08-20 09:39:22 +00:00
}
2012-09-07 16:09:20 +00:00
def message_read_tree_get_expandable ( self , cr , uid , parent_message , last_message , domain = [ ] , current_level = 0 , level = 0 , context = None ) :
""" . """
base_domain = [ ( ' id ' , ' < ' , last_message [ ' id ' ] ) ]
if parent_message and current_level < level :
base_domain + = [ ( ' parent_id ' , ' = ' , parent_message [ ' id ' ] ) ]
elif parent_message :
2012-09-20 14:46:45 +00:00
base_domain + = [ ( ' id ' , ' child_of ' , parent_message [ ' id ' ] ) , ( ' id ' , ' != ' , parent_message [ ' id ' ] ) ]
2012-09-07 16:09:20 +00:00
if domain :
base_domain + = domain
extension = { ' type ' : ' expandable ' ,
' domain ' : base_domain ,
' thread_level ' : current_level ,
' context ' : context ,
' id ' : - 1 ,
}
return extension
2012-09-20 14:46:45 +00:00
def message_read_tree_flatten ( self , cr , uid , parent_message , messages , domain = [ ] , level = 0 , current_level = 0 , context = None , limit = None , add_expandable = True ) :
2012-08-28 14:26:34 +00:00
""" Given a tree with several roots of following structure :
2012-08-31 13:15:03 +00:00
[ { ' id ' : 1 , ' child_ids ' : [
{ ' id ' : 11 , ' child_ids ' : [ . . . ] } , ] ,
{ . . . } ]
2012-09-05 15:53:19 +00:00
Flatten it to have a maximum number of levels , 0 being flat and
sort messages in a level according to a key of the messages .
2012-08-28 14:26:34 +00:00
Perform the flattening at leafs if above the maximum depth , then get
back in the tree .
2012-09-05 15:53:19 +00:00
: param context : ` ` sort_key ` ` : key for sorting ( id by default )
: param context : ` ` sort_reverse ` ` : reverser order for sorting ( True by default )
2012-07-20 09:25:45 +00:00
"""
2012-08-28 14:26:34 +00:00
def _flatten ( msg_dict ) :
""" from { ' id ' : x, ' child_ids ' : [ {child1} , {child2} ]}
get [ { ' id ' : x , ' child_ids ' : [ ] } , { child1 } , { child2 } ]
"""
child_ids = msg_dict . pop ( ' child_ids ' , [ ] )
msg_dict [ ' child_ids ' ] = [ ]
return [ msg_dict ] + child_ids
2012-09-20 14:46:45 +00:00
2012-09-05 15:53:19 +00:00
context = context or { }
2012-09-11 12:01:17 +00:00
limit = limit or self . _message_read_limit
2012-09-20 14:46:45 +00:00
2012-08-28 14:26:34 +00:00
# Depth-first flattening
for message in messages :
2012-08-31 14:37:07 +00:00
if message . get ( ' type ' ) == ' expandable ' :
2012-08-31 13:15:03 +00:00
continue
2012-09-20 14:46:45 +00:00
message [ ' child_ids ' ] = self . message_read_tree_flatten ( cr , uid , message , message [ ' child_ids ' ] , domain , level , current_level + 1 , context = context , limit = limit )
2012-09-11 12:01:17 +00:00
for child in message [ ' child_ids ' ] :
2012-09-20 14:46:45 +00:00
if child . get ( ' type ' ) == ' expandable ' :
continue
2012-09-11 12:01:17 +00:00
message [ ' child_nbr ' ] + = child [ ' child_nbr ' ]
2012-08-28 14:26:34 +00:00
# Flatten if above maximum depth
if current_level < level :
2012-09-05 15:53:19 +00:00
return_list = messages
else :
2012-09-07 16:09:20 +00:00
return_list = [ flat_message for message in messages for flat_message in _flatten ( message ) ]
2012-08-28 14:26:34 +00:00
2012-09-07 16:09:20 +00:00
# Add expandable
return_list = sorted ( return_list , key = itemgetter ( context . get ( ' sort_key ' , ' id ' ) ) , reverse = context . get ( ' sort_reverse ' , True ) )
2012-09-20 14:46:45 +00:00
if return_list and current_level == 0 and add_expandable :
expandable = self . message_read_tree_get_expandable ( cr , uid , parent_message , return_list and return_list [ - 1 ] or parent_message , domain , current_level , level , context = context )
return_list . append ( expandable )
elif return_list and current_level < = level and add_expandable :
expandable = self . message_read_tree_get_expandable ( cr , uid , parent_message , return_list and return_list [ - 1 ] or parent_message , domain , current_level , level , context = context )
return_list . append ( expandable )
2012-09-07 16:09:20 +00:00
return return_list
2012-09-20 14:46:45 +00:00
def message_read ( self , cr , uid , ids = False , domain = [ ] , level = 0 , 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 level : level of threads to display , 0 being flat
: 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
if context and context . get ( ' message_loaded ' ) :
2012-10-01 17:52:25 +00:00
domain + = [ [ ' id ' , ' not in ' , context . get ( ' message_loaded ' ) ] ] ;
2012-09-28 13:27:23 +00:00
2012-08-27 16:22:37 +00:00
limit = limit or self . _message_read_limit
context = context or { }
2012-08-31 14:37:07 +00:00
if not ids :
2012-09-14 12:21:39 +00:00
ids = self . search ( cr , SUPERUSER_ID , domain , context = context , limit = limit )
2012-10-01 17:52:25 +00:00
# if the user can read a message, he can read all the thread
ids = ids + self . search ( cr , SUPERUSER_ID , [ [ ' parent_id ' , ' in ' , ids ] ] , None , limit = limit )
2012-07-06 09:41:41 +00:00
messages = self . browse ( cr , uid , ids , context = context )
2012-09-20 14:46:45 +00:00
add_expandable = ( len ( messages ) > = limit )
2012-08-27 16:22:37 +00:00
2012-09-11 12:01:17 +00:00
# key: ID, value: record
tree = { }
2012-08-27 16:22:37 +00:00
result = [ ]
2012-08-20 09:39:22 +00:00
for msg in messages :
2012-09-11 12:01:17 +00:00
record = self . _message_dict_get ( cr , uid , msg , context = context )
while msg . parent_id and msg . parent_id . id != parent_id :
if msg . parent_id . id in tree :
record_parent = tree [ msg . parent_id . id ]
else :
record_parent = self . _message_dict_get ( cr , uid , msg . parent_id , context = context )
if msg . parent_id . parent_id :
tree [ msg . parent_id . id ] = record_parent
if record [ ' id ' ] not in [ x [ ' id ' ] for x in record_parent [ ' child_ids ' ] ] :
record_parent [ ' child_ids ' ] . append ( record )
record = record_parent
msg = msg . parent_id
2012-09-28 13:27:23 +00:00
# if not in record and not in message_loded list
if msg . id not in tree and not ( context and context . get ( ' message_loaded ' ) and msg . id in context . get ( ' message_loaded ' ) ) :
2012-09-11 12:01:17 +00:00
result . append ( record )
tree [ msg . id ] = record
2012-08-28 14:26:34 +00:00
# Flatten the result
2012-10-01 09:36:33 +00:00
result2 = self . message_read_tree_flatten ( cr , uid , None , result , [ ] , level , context = context , limit = limit , add_expandable = add_expandable )
2012-09-28 13:27:23 +00:00
return result2
2012-08-20 09:06:36 +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-09-05 15:53:19 +00:00
values [ ' message_id ' ] = tools . generate_tracking_message_id ( ' %(model)s - %(res_id)s ' % values )
2012-08-15 13:36:43 +00:00
newid = super ( mail_message , self ) . create ( cr , uid , values , context )
2012-08-31 08:42:35 +00:00
self . notify ( cr , uid , 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-08-31 08:42:35 +00:00
def notify ( self , cr , uid , newid , context = None ) :
2012-08-23 18:06:48 +00:00
""" Add the related record followers to the destination partner_ids.
Call mail_notification . notify to manage the email sending
"""
message = self . browse ( cr , uid , newid , context = context )
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-08-31 08:42:35 +00:00
partners_to_notify | = extra_notified
2012-09-28 13:27:23 +00:00
2012-09-28 14:51:47 +00:00
# add myself if I wrote on my wall,
# unless remove myself author
if ( ( message . model == " res.partner " and message . res_id == message . author_id . id ) ) :
2012-09-28 13:27:23 +00:00
self . write ( cr , SUPERUSER_ID , [ newid ] , { ' partner_ids ' : [ ( 4 , message . author_id . id ) ] } , context = context )
2012-09-28 14:51:47 +00:00
# add myself if this message have a parent message and I recive parent message
# ! subtype_id: pure log message => do this in read_message
# or (message.parent_id and self.pool.get('mail.notification').search(cr, uid, [('partner_id','=',message.author_id.id),('message_id','=',message.parent_id)])):
2012-09-28 13:27:23 +00:00
else :
self . write ( cr , SUPERUSER_ID , [ newid ] , { ' partner_ids ' : [ ( 3 , message . author_id . id ) ] } , context = context )
2012-08-30 12:21:12 +00:00
self . pool . get ( ' mail.notification ' ) . notify ( cr , uid , list ( partners_to_notify ) , 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 ,
}
}