2009-12-02 05:36:57 +00:00
# -*- coding: utf-8 -*-
##############################################################################
2010-10-11 12:09:52 +00:00
#
2009-12-02 05:36:57 +00:00
# OpenERP, Open Source Management Solution
2010-01-12 09:18:39 +00:00
# Copyright (C) 2004-2010 Tiny SPRL (<http://tiny.be>).
2009-12-02 05:36: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
2010-10-11 12:09:52 +00:00
# along with this program. If not, see <http://www.gnu.org/licenses/>.
2009-12-02 05:36:57 +00:00
#
##############################################################################
from osv import osv , fields
2010-10-27 10:24:28 +00:00
from osv . orm import except_orm
2012-06-22 06:48:39 +00:00
import logging
2009-12-02 05:36:57 +00:00
import nodes
2009-12-15 10:48:10 +00:00
from tools . translate import _
2012-06-25 09:06:19 +00:00
_logger = logging . getLogger ( __name__ )
2009-12-02 05:36:57 +00:00
class document_directory ( osv . osv ) :
_name = ' document.directory '
2010-05-19 18:32:32 +00:00
_description = ' Directory '
2010-10-11 12:09:52 +00:00
_order = ' name '
2009-12-02 05:36:57 +00:00
_columns = {
' name ' : fields . char ( ' Name ' , size = 64 , required = True , select = 1 ) ,
' write_date ' : fields . datetime ( ' Date Modified ' , readonly = True ) ,
' write_uid ' : fields . many2one ( ' res.users ' , ' Last Modification User ' , readonly = True ) ,
' create_date ' : fields . datetime ( ' Date Created ' , readonly = True ) ,
' create_uid ' : fields . many2one ( ' res.users ' , ' Creator ' , readonly = True ) ,
' domain ' : fields . char ( ' Domain ' , size = 128 , help = " Use a domain if you want to apply an automatic filter on visible resources. " ) ,
' user_id ' : fields . many2one ( ' res.users ' , ' Owner ' ) ,
2010-10-27 10:23:24 +00:00
' storage_id ' : fields . many2one ( ' document.storage ' , ' Storage ' , change_default = True ) ,
2009-12-02 05:36:57 +00:00
' group_ids ' : fields . many2many ( ' res.groups ' , ' document_directory_group_rel ' , ' item_id ' , ' group_id ' , ' Groups ' ) ,
2010-10-27 10:23:24 +00:00
' parent_id ' : fields . many2one ( ' document.directory ' , ' Parent Directory ' , select = 1 , change_default = True ) ,
2009-12-02 05:36:57 +00:00
' child_ids ' : fields . one2many ( ' document.directory ' , ' parent_id ' , ' Children ' ) ,
' file_ids ' : fields . one2many ( ' ir.attachment ' , ' parent_id ' , ' Files ' ) ,
' content_ids ' : fields . one2many ( ' document.directory.content ' , ' directory_id ' , ' Virtual Files ' ) ,
2010-10-11 12:09:52 +00:00
' type ' : fields . selection ( [
2010-06-27 20:18:18 +00:00
( ' directory ' , ' Static Directory ' ) ,
( ' ressource ' , ' Folders per resource ' ) ,
2010-07-08 22:50:42 +00:00
] ,
2010-10-27 10:23:24 +00:00
' Type ' , required = True , select = 1 , change_default = True ,
2010-10-11 12:09:52 +00:00
help = " Each directory can either have the type Static or be linked to another resource. A static directory, as with Operating Systems, is the classic directory that can contain a set of files. The directories linked to systems resources automatically possess sub-directories for each of resource types defined in the parent directory. " ) ,
2010-10-27 10:23:24 +00:00
' ressource_type_id ' : fields . many2one ( ' ir.model ' , ' Resource model ' , change_default = True ,
2010-06-27 20:18:26 +00:00
help = " Select an object here and there will be one folder per record of that resource. " ) ,
2010-04-08 06:23:16 +00:00
' resource_field ' : fields . many2one ( ' ir.model.fields ' , ' Name field ' , help = ' Field to be used as name on resource directories. If empty, the " name " will be used. ' ) ,
2010-07-13 10:29:58 +00:00
' resource_find_all ' : fields . boolean ( ' Find all resources ' , required = True ,
help = " If true, all attachments that match this resource will " \
" be located. If false, only ones that have this as parent. " ) ,
2010-10-27 10:23:24 +00:00
' ressource_parent_type_id ' : fields . many2one ( ' ir.model ' , ' Parent Model ' , change_default = True ,
2009-12-02 05:36:57 +00:00
help = " If you put an object here, this directory template will appear bellow all of these objects. " \
2010-10-27 10:23:23 +00:00
" Such directories are \" attached \" to the specific model or record, just like attachments. " \
2009-12-02 05:36:57 +00:00
" Don ' t put a parent directory if you select a parent model. " ) ,
2010-10-27 10:23:23 +00:00
' ressource_id ' : fields . integer ( ' Resource ID ' ,
help = " Along with Parent Model, this ID attaches this folder to a specific record of Parent Model. " ) ,
2009-12-02 05:36:57 +00:00
' ressource_tree ' : fields . boolean ( ' Tree Structure ' ,
help = " Check this if you want to use the same tree structure as the object selected in the system. " ) ,
' dctx_ids ' : fields . one2many ( ' document.directory.dctx ' , ' dir_id ' , ' Context fields ' ) ,
2010-10-27 10:23:24 +00:00
' company_id ' : fields . many2one ( ' res.company ' , ' Company ' , change_default = True ) ,
2009-12-02 05:36:57 +00:00
}
2010-04-08 06:23:16 +00:00
2009-12-02 05:36:57 +00:00
def _get_root_directory ( self , cr , uid , context = None ) :
objid = self . pool . get ( ' ir.model.data ' )
try :
2010-04-08 06:23:16 +00:00
mid = objid . _get_id ( cr , uid , ' document ' , ' dir_root ' )
2009-12-02 05:36:57 +00:00
if not mid :
2010-03-10 10:53:34 +00:00
return False
2010-03-16 08:53:40 +00:00
root_id = objid . read ( cr , uid , mid , [ ' res_id ' ] ) [ ' res_id ' ]
return root_id
2009-12-02 05:36:57 +00:00
except Exception , e :
2012-06-22 06:48:39 +00:00
2012-06-25 09:06:19 +00:00
_logger . warning ( ' Cannot set directory root: ' + str ( e ) )
2010-03-10 10:53:34 +00:00
return False
2009-12-02 05:36:57 +00:00
return objid . browse ( cr , uid , mid , context = context ) . res_id
2010-06-16 11:51:39 +00:00
def _get_def_storage ( self , cr , uid , context = None ) :
2009-12-02 05:36:57 +00:00
if context and context . has_key ( ' default_parent_id ' ) :
# Use the same storage as the parent..
2010-06-16 11:51:39 +00:00
diro = self . browse ( cr , uid , context [ ' default_parent_id ' ] )
2009-12-02 05:36:57 +00:00
if diro . storage_id :
return diro . storage_id . id
objid = self . pool . get ( ' ir.model.data ' )
try :
mid = objid . _get_id ( cr , uid , ' document ' , ' storage_default ' )
return objid . browse ( cr , uid , mid , context = context ) . res_id
except Exception :
return None
2010-10-11 12:09:52 +00:00
2009-12-02 05:36:57 +00:00
_defaults = {
2010-06-23 11:53:49 +00:00
' company_id ' : lambda s , cr , uid , c : s . pool . get ( ' res.company ' ) . _company_default_get ( cr , uid , ' document.directory ' , context = c ) ,
2009-12-02 05:36:57 +00:00
' user_id ' : lambda self , cr , uid , ctx : uid ,
2010-10-27 10:23:24 +00:00
' domain ' : ' [] ' ,
' type ' : ' directory ' ,
' ressource_id ' : 0 ,
' storage_id ' : _get_def_storage , # Still, it is bad practice to set it everywhere.
2010-07-13 10:29:58 +00:00
' resource_find_all ' : True ,
2009-12-02 05:36:57 +00:00
}
_sql_constraints = [
( ' dirname_uniq ' , ' unique (name,parent_id,ressource_id,ressource_parent_type_id) ' , ' The directory name must be unique ! ' ) ,
2010-06-27 20:18:18 +00:00
( ' no_selfparent ' , ' check(parent_id <> id) ' , ' Directory cannot be parent of itself! ' ) ,
( ' dir_parented ' , ' check(parent_id IS NOT NULL OR storage_id IS NOT NULL) ' , ' Directory must have a parent or a storage ' )
2009-12-02 05:36:57 +00:00
]
2010-08-27 07:38:58 +00:00
def name_get ( self , cr , uid , ids , context = None ) :
2010-01-26 20:12:47 +00:00
res = [ ]
2010-04-27 12:20:42 +00:00
if not self . search ( cr , uid , [ ( ' id ' , ' in ' , ids ) ] ) :
ids = [ ]
2010-01-26 19:44:40 +00:00
for d in self . browse ( cr , uid , ids , context = context ) :
2010-01-26 20:12:47 +00:00
s = ' '
d2 = d
2010-01-27 00:33:18 +00:00
while d2 and d2 . parent_id :
2010-01-26 20:12:47 +00:00
s = d2 . name + ( s and ( ' / ' + s ) or ' ' )
d2 = d2 . parent_id
2010-06-24 09:48:27 +00:00
res . append ( ( d . id , s or d . name ) )
2010-01-26 19:44:40 +00:00
return res
2009-12-02 05:36:57 +00:00
2010-03-16 08:53:40 +00:00
def get_full_path ( self , cr , uid , dir_id , context = None ) :
""" Return the full path to this directory, in a list, root first
"""
2010-10-12 11:44:36 +00:00
if isinstance ( dir_id , ( tuple , list ) ) :
assert len ( dir_id ) == 1
dir_id = dir_id [ 0 ]
2010-03-16 08:53:40 +00:00
def _parent ( dir_id , path ) :
2010-06-16 11:51:39 +00:00
parent = self . browse ( cr , uid , dir_id )
2010-03-16 08:53:40 +00:00
if parent . parent_id and not parent . ressource_parent_type_id :
_parent ( parent . parent_id . id , path )
path . append ( parent . name )
else :
path . append ( parent . name )
return path
path = [ ]
_parent ( dir_id , path )
return path
2010-11-19 13:48:01 +00:00
def _check_recursion ( self , cr , uid , ids , context = None ) :
2009-12-02 05:36:57 +00:00
level = 100
while len ( ids ) :
cr . execute ( ' select distinct parent_id from document_directory where id in ( ' + ' , ' . join ( map ( str , ids ) ) + ' ) ' )
ids = filter ( None , map ( lambda x : x [ 0 ] , cr . fetchall ( ) ) )
if not level :
return False
level - = 1
return True
_constraints = [
( _check_recursion , ' Error! You can not create recursive Directories. ' , [ ' parent_id ' ] )
]
2010-10-11 12:09:52 +00:00
2009-12-02 05:36:57 +00:00
def __init__ ( self , * args , * * kwargs ) :
2010-08-10 12:29:57 +00:00
super ( document_directory , self ) . __init__ ( * args , * * kwargs )
2009-12-02 05:36:57 +00:00
def onchange_content_id ( self , cr , uid , ids , ressource_type_id ) :
return { }
"""
PRE :
uri : of the form " Sales Order/SO001 "
PORT :
uri
object : the object . directory or object . directory . content
object2 : the other object linked ( if object . directory . content )
"""
def get_object ( self , cr , uid , uri , context = None ) :
""" Return a node object for the given uri.
This fn merely passes the call to node_context
"""
2010-10-11 12:09:52 +00:00
2010-06-29 14:05:19 +00:00
return nodes . get_node_context ( cr , uid , context ) . get_uri ( cr , uri )
2009-12-02 05:36:57 +00:00
2010-10-12 10:40:19 +00:00
def get_node_class ( self , cr , uid , ids , dbro = None , dynamic = False , context = None ) :
2010-10-12 10:38:56 +00:00
""" Retrieve the class of nodes for this directory
This function can be overriden by inherited classes ; )
@param dbro The browse object , if caller already has it
"""
if dbro is None :
dbro = self . browse ( cr , uid , ids , context = context )
2010-10-12 10:40:19 +00:00
if dynamic :
return nodes . node_res_obj
elif dbro . type == ' directory ' :
2010-10-12 10:38:56 +00:00
return nodes . node_dir
elif dbro . type == ' ressource ' :
return nodes . node_res_dir
else :
raise ValueError ( " dir node for %s type " , dbro . type )
2010-12-13 06:43:09 +00:00
def _prepare_context ( self , cr , uid , nctx , context = None ) :
2010-10-12 10:38:56 +00:00
""" Fill nctx with properties for this database
@param nctx instance of nodes . node_context , to be filled
@param context ORM context ( dict ) for us
Note that this function is called * without * a list of ids ,
it should behave the same for the whole database ( based on the
ORM instance of document . directory ) .
Some databases may override this and attach properties to the
node_context . See WebDAV , CalDAV .
"""
return
2010-12-13 06:43:09 +00:00
def get_dir_permissions ( self , cr , uid , ids , context = None ) :
2010-07-11 14:25:27 +00:00
""" Check what permission user ' uid ' has on directory ' id '
"""
assert len ( ids ) == 1
2010-10-11 12:09:52 +00:00
2010-10-27 10:24:28 +00:00
res = 0
for pperms in [ ( ' read ' , 5 ) , ( ' write ' , 2 ) , ( ' unlink ' , 8 ) ] :
try :
self . check_access_rule ( cr , uid , ids , pperms [ 0 ] , context = context )
res | = pperms [ 1 ]
except except_orm :
pass
return res
2009-12-02 05:36:57 +00:00
2010-04-13 11:03:20 +00:00
def _locate_child ( self , cr , uid , root_id , uri , nparent , ncontext ) :
2009-12-02 05:36:57 +00:00
""" try to locate the node in uri,
Return a tuple ( node_dir , remaining_path )
"""
2010-08-10 12:29:57 +00:00
return ( nodes . node_database ( context = ncontext ) , uri )
2010-10-11 12:09:52 +00:00
2009-12-02 05:36:57 +00:00
def copy ( self , cr , uid , id , default = None , context = None ) :
if not default :
default = { }
name = self . read ( cr , uid , [ id ] ) [ 0 ] [ ' name ' ]
default . update ( { ' name ' : name + " (copy) " } )
2010-11-19 13:48:01 +00:00
return super ( document_directory , self ) . copy ( cr , uid , id , default , context = context )
2009-12-02 05:36:57 +00:00
2010-06-16 11:51:39 +00:00
def _check_duplication ( self , cr , uid , vals , ids = [ ] , op = ' create ' ) :
2009-12-02 05:36:57 +00:00
name = vals . get ( ' name ' , False )
parent_id = vals . get ( ' parent_id ' , False )
ressource_parent_type_id = vals . get ( ' ressource_parent_type_id ' , False )
ressource_id = vals . get ( ' ressource_id ' , 0 )
if op == ' write ' :
2010-06-16 11:51:39 +00:00
for directory in self . browse ( cr , uid , ids ) :
2009-12-02 05:36:57 +00:00
if not name :
name = directory . name
if not parent_id :
parent_id = directory . parent_id and directory . parent_id . id or False
2010-10-12 10:40:15 +00:00
# TODO fix algo
2009-12-02 05:36:57 +00:00
if not ressource_parent_type_id :
ressource_parent_type_id = directory . ressource_parent_type_id and directory . ressource_parent_type_id . id or False
if not ressource_id :
ressource_id = directory . ressource_id and directory . ressource_id or 0
res = self . search ( cr , uid , [ ( ' id ' , ' <> ' , directory . id ) , ( ' name ' , ' = ' , name ) , ( ' parent_id ' , ' = ' , parent_id ) , ( ' ressource_parent_type_id ' , ' = ' , ressource_parent_type_id ) , ( ' ressource_id ' , ' = ' , ressource_id ) ] )
if len ( res ) :
return False
if op == ' create ' :
res = self . search ( cr , uid , [ ( ' name ' , ' = ' , name ) , ( ' parent_id ' , ' = ' , parent_id ) , ( ' ressource_parent_type_id ' , ' = ' , ressource_parent_type_id ) , ( ' ressource_id ' , ' = ' , ressource_id ) ] )
if len ( res ) :
return False
return True
def write ( self , cr , uid , ids , vals , context = None ) :
2010-06-16 11:51:39 +00:00
if not self . _check_duplication ( cr , uid , vals , ids , op = ' write ' ) :
2009-12-02 05:36:57 +00:00
raise osv . except_osv ( _ ( ' ValidateError ' ) , _ ( ' Directory name must be unique! ' ) )
2010-06-16 11:51:39 +00:00
return super ( document_directory , self ) . write ( cr , uid , ids , vals , context = context )
2009-12-02 05:36:57 +00:00
def create ( self , cr , uid , vals , context = None ) :
2010-06-16 11:51:39 +00:00
if not self . _check_duplication ( cr , uid , vals ) :
2009-12-02 05:36:57 +00:00
raise osv . except_osv ( _ ( ' ValidateError ' ) , _ ( ' Directory name must be unique! ' ) )
2010-10-12 10:40:15 +00:00
newname = vals . get ( ' name ' , False )
if newname :
for illeg in ( ' / ' , ' @ ' , ' $ ' , ' # ' ) :
if illeg in newname :
raise osv . except_osv ( _ ( ' ValidateError ' ) , _ ( ' Directory name contains special characters! ' ) )
2009-12-02 05:36:57 +00:00
return super ( document_directory , self ) . create ( cr , uid , vals , context )
2010-07-09 08:23:16 +00:00
# TODO def unlink(...
2009-12-02 05:36:57 +00:00
document_directory ( )
class document_directory_dctx ( osv . osv ) :
""" In order to evaluate dynamic folders, child items could have a limiting
domain expression . For that , their parents will export a context where useful
information will be passed on .
If you define sth like " s_id " = " this.id " at a folder iterating over sales , its
children could have a domain like [ ( ' sale_id ' , = , dctx_s_id ) ]
This system should be used recursively , that is , parent dynamic context will be
appended to all children down the tree .
"""
_name = ' document.directory.dctx '
2010-05-19 18:32:32 +00:00
_description = ' Directory Dynamic Context '
2009-12-02 05:36:57 +00:00
_columns = {
2010-10-12 10:41:20 +00:00
' dir_id ' : fields . many2one ( ' document.directory ' , ' Directory ' , required = True , ondelete = " cascade " ) ,
2009-12-02 05:36:57 +00:00
' field ' : fields . char ( ' Field ' , size = 20 , required = True , select = 1 , help = " The name of the field. Note that the prefix \" dctx_ \" will be prepended to what is typed here. " ) ,
' expr ' : fields . char ( ' Expression ' , size = 64 , required = True , help = " A python expression used to evaluate the field. \n " + \
" You can use ' dir_id ' for current dir, ' res_id ' , ' res_model ' as a reference to the current record, in dynamic folders " ) ,
}
document_directory_dctx ( )
class document_directory_node ( osv . osv ) :
_inherit = ' process.node '
_columns = {
' directory_id ' : fields . many2one ( ' document.directory ' , ' Document directory ' , ondelete = " set null " ) ,
}
document_directory_node ( )
2011-11-22 08:51:38 +00:00
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: