2018-01-16 06:58:15 +01:00
# -*- coding: utf-8 -*-
2018-01-16 11:34:37 +01:00
# Part of Odoo, Flectra. See LICENSE file for full copyright and licensing details.
2018-01-16 06:58:15 +01:00
2018-01-16 11:34:37 +01:00
from flectra import api , fields , models
2018-01-16 06:58:15 +01:00
class Followers ( models . Model ) :
""" mail_followers holds the data related to the follow mechanism inside
2018-01-16 11:34:37 +01:00
Flectra . Partners can choose to follow documents ( records ) of any kind
2018-01-16 06:58:15 +01:00
that inherits from mail . thread . Following documents allow to receive
notifications for new messages . A subscription is characterized by :
: param : res_model : model of the followed objects
: param : res_id : ID of resource ( may be 0 for every objects )
"""
_name = ' mail.followers '
_rec_name = ' partner_id '
_log_access = False
_description = ' Document Followers '
# Note. There is no integrity check on model names for performance reasons.
# However, followers of unlinked models are deleted by models themselves
# (see 'ir.model' inheritance).
res_model = fields . Char (
' Related Document Model Name ' , required = True , index = True )
res_id = fields . Integer (
' Related Document ID ' , index = True , help = ' Id of the followed resource ' )
partner_id = fields . Many2one (
' res.partner ' , string = ' Related Partner ' , ondelete = ' cascade ' , index = True )
channel_id = fields . Many2one (
' mail.channel ' , string = ' Listener ' , ondelete = ' cascade ' , index = True )
subtype_ids = fields . Many2many (
' mail.message.subtype ' , string = ' Subtype ' ,
help = " Message subtypes followed, meaning subtypes that will be pushed onto the user ' s Wall. " )
@api.model
def _add_follower_command ( self , res_model , res_ids , partner_data , channel_data , force = True ) :
""" Please upate me
: param force : if True , delete existing followers before creating new one
using the subtypes given in the parameters
"""
force_mode = force or ( all ( partner_data . values ( ) ) and all ( channel_data . values ( ) ) )
generic = [ ]
specific = { }
existing = { } # {res_id: follower_ids}
p_exist = { } # {partner_id: res_ids}
c_exist = { } # {channel_id: res_ids}
followers = self . sudo ( ) . search ( [
' & ' ,
' & ' , ( ' res_model ' , ' = ' , res_model ) , ( ' res_id ' , ' in ' , res_ids ) ,
' | ' , ( ' partner_id ' , ' in ' , list ( partner_data ) ) , ( ' channel_id ' , ' in ' , list ( channel_data ) ) ] )
if force_mode :
followers . unlink ( )
else :
for follower in followers :
existing . setdefault ( follower . res_id , list ( ) ) . append ( follower )
if follower . partner_id :
p_exist . setdefault ( follower . partner_id . id , list ( ) ) . append ( follower . res_id )
if follower . channel_id :
c_exist . setdefault ( follower . channel_id . id , list ( ) ) . append ( follower . res_id )
default_subtypes , _internal_subtypes , external_subtypes = \
self . env [ ' mail.message.subtype ' ] . default_subtypes ( res_model )
if force_mode :
employee_pids = self . env [ ' res.users ' ] . sudo ( ) . search ( [ ( ' partner_id ' , ' in ' , list ( partner_data ) ) , ( ' share ' , ' = ' , False ) ] ) . mapped ( ' partner_id ' ) . ids
for pid , data in partner_data . items ( ) :
if not data :
if pid not in employee_pids :
partner_data [ pid ] = external_subtypes . ids
else :
partner_data [ pid ] = default_subtypes . ids
for cid , data in channel_data . items ( ) :
if not data :
channel_data [ cid ] = default_subtypes . ids
# create new followers, batch ok
gen_new_pids = [ pid for pid in partner_data if pid not in p_exist ]
gen_new_cids = [ cid for cid in channel_data if cid not in c_exist ]
for pid in gen_new_pids :
generic . append ( [ 0 , 0 , { ' res_model ' : res_model , ' partner_id ' : pid , ' subtype_ids ' : [ ( 6 , 0 , partner_data . get ( pid ) or default_subtypes . ids ) ] } ] )
for cid in gen_new_cids :
generic . append ( [ 0 , 0 , { ' res_model ' : res_model , ' channel_id ' : cid , ' subtype_ids ' : [ ( 6 , 0 , channel_data . get ( cid ) or default_subtypes . ids ) ] } ] )
# create new followers, each document at a time because of existing followers to avoid erasing
if not force_mode :
for res_id in res_ids :
command = [ ]
doc_followers = existing . get ( res_id , list ( ) )
new_pids = set ( partner_data ) - set ( [ sub . partner_id . id for sub in doc_followers if sub . partner_id ] ) - set ( gen_new_pids )
new_cids = set ( channel_data ) - set ( [ sub . channel_id . id for sub in doc_followers if sub . channel_id ] ) - set ( gen_new_cids )
# subscribe new followers
for new_pid in new_pids :
command . append ( ( 0 , 0 , {
' res_model ' : res_model ,
' partner_id ' : new_pid ,
' subtype_ids ' : [ ( 6 , 0 , partner_data . get ( new_pid ) or default_subtypes . ids ) ] ,
} ) )
for new_cid in new_cids :
command . append ( ( 0 , 0 , {
' res_model ' : res_model ,
' channel_id ' : new_cid ,
' subtype_ids ' : [ ( 6 , 0 , channel_data . get ( new_cid ) or default_subtypes . ids ) ] ,
} ) )
if command :
specific [ res_id ] = command
return generic , specific
#
# Modifying followers change access rights to individual documents. As the
# cache may contain accessible/inaccessible data, one has to refresh it.
#
@api.multi
def _invalidate_documents ( self ) :
""" Invalidate the cache of the documents followed by ``self``. """
for record in self :
if record . res_id :
self . env [ record . res_model ] . invalidate_cache ( ids = [ record . res_id ] )
@api.model
def create ( self , vals ) :
res = super ( Followers , self ) . create ( vals )
res . _invalidate_documents ( )
return res
@api.multi
def write ( self , vals ) :
if ' res_model ' in vals or ' res_id ' in vals :
self . _invalidate_documents ( )
res = super ( Followers , self ) . write ( vals )
self . _invalidate_documents ( )
return res
@api.multi
def unlink ( self ) :
self . _invalidate_documents ( )
return super ( Followers , self ) . unlink ( )
_sql_constraints = [
( ' mail_followers_res_partner_res_model_id_uniq ' , ' unique(res_model,res_id,partner_id) ' , ' Error, a partner cannot follow twice the same object. ' ) ,
( ' mail_followers_res_channel_res_model_id_uniq ' , ' unique(res_model,res_id,channel_id) ' , ' Error, a channel cannot follow twice the same object. ' ) ,
( ' partner_xor_channel ' , ' CHECK((partner_id IS NULL) != (channel_id IS NULL)) ' , ' Error: A follower must be either a partner or a channel (but not both). ' )
]