2012-03-21 15:15:23 +00:00
# -*- coding: utf-8 -*-
2012-02-29 13:53:43 +00:00
2006-12-07 13:41:40 +00:00
##############################################################################
2010-04-29 12:01:58 +00:00
#
2012-03-26 23:18:29 +00:00
# OpenERP, Open Source Business Applications
# Copyright (C) 2004-2012 OpenERP S.A. (<http://openerp.com>).
2008-06-16 11:00:21 +00:00
#
2008-11-03 18:27:16 +00:00
# This program is free software: you can redistribute it and/or modify
2009-10-14 12:32:15 +00:00
# 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.
2006-12-07 13:41:40 +00:00
#
2008-11-03 18:27:16 +00:00
# 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
2009-10-14 12:32:15 +00:00
# GNU Affero General Public License for more details.
2006-12-07 13:41:40 +00:00
#
2009-10-14 12:32:15 +00:00
# You should have received a copy of the GNU Affero General Public License
2010-04-29 12:01:58 +00:00
# along with this program. If not, see <http://www.gnu.org/licenses/>.
2006-12-07 13:41:40 +00:00
#
##############################################################################
2010-03-03 15:40:41 +00:00
import logging
2011-01-06 17:38:28 +00:00
import re
import time
2011-12-22 18:24:05 +00:00
import types
2011-01-06 17:38:28 +00:00
2012-03-26 23:18:29 +00:00
from openerp . osv import fields , osv
from openerp import netsvc , pooler , tools
from openerp . tools . safe_eval import safe_eval as eval
from openerp . tools import config
from openerp . tools . translate import _
2012-06-01 13:14:14 +00:00
from openerp . osv . orm import except_orm , browse_record , EXT_ID_PREFIX_M2M_TABLE
2006-12-07 13:41:40 +00:00
2012-01-24 11:47:30 +00:00
_logger = logging . getLogger ( __name__ )
2012-03-30 21:49:48 +00:00
MODULE_UNINSTALL_FLAG = ' _force_unlink '
2012-03-26 23:18:29 +00:00
2008-03-26 13:15:13 +00:00
def _get_fields_type ( self , cr , uid , context = None ) :
2012-02-27 10:09:53 +00:00
# Avoid too many nested `if`s below, as RedHat's Python 2.6
2012-03-26 23:18:29 +00:00
# break on it. See bug 939653.
2011-12-22 18:24:05 +00:00
return sorted ( [ ( k , k ) for k , v in fields . __dict__ . iteritems ( )
2012-02-27 10:09:53 +00:00
if type ( v ) == types . TypeType and \
issubclass ( v , fields . _column ) and \
v != fields . _column and \
not v . _deprecated and \
not issubclass ( v , fields . function ) ] )
2008-03-26 13:15:13 +00:00
2011-01-18 15:52:19 +00:00
def _in_modules ( self , cr , uid , ids , field_name , arg , context = None ) :
#pseudo-method used by fields.function in ir.model/ir.model.fields
module_pool = self . pool . get ( " ir.module.module " )
installed_module_ids = module_pool . search ( cr , uid , [ ( ' state ' , ' = ' , ' installed ' ) ] )
installed_module_names = module_pool . read ( cr , uid , installed_module_ids , [ ' name ' ] , context = context )
installed_modules = set ( x [ ' name ' ] for x in installed_module_names )
result = { }
xml_ids = osv . osv . _get_xml_ids ( self , cr , uid , ids )
for k , v in xml_ids . iteritems ( ) :
result [ k ] = ' , ' . join ( sorted ( installed_modules & set ( xml_id . split ( ' . ' ) [ 0 ] for xml_id in v ) ) )
return result
2011-01-07 11:42:33 +00:00
2006-12-07 13:41:40 +00:00
class ir_model ( osv . osv ) :
2008-07-22 14:24:36 +00:00
_name = ' ir.model '
2011-09-19 14:48:21 +00:00
_description = " Models "
2010-12-10 22:42:58 +00:00
_order = ' model '
2010-06-24 15:56:02 +00:00
def _is_osv_memory ( self , cr , uid , ids , field_name , arg , context = None ) :
models = self . browse ( cr , uid , ids , context = context )
res = dict . fromkeys ( ids )
for model in models :
2011-08-16 09:57:55 +00:00
res [ model . id ] = self . pool . get ( model . model ) . is_transient ( )
2010-06-24 15:56:02 +00:00
return res
def _search_osv_memory ( self , cr , uid , model , name , domain , context = None ) :
if not domain :
return [ ]
2012-03-31 01:34:55 +00:00
__ , operator , value = domain [ 0 ]
2010-06-24 15:56:02 +00:00
if operator not in [ ' = ' , ' != ' ] :
2010-10-27 13:36:01 +00:00
raise osv . except_osv ( _ ( ' Invalid search criterions ' ) , _ ( ' The osv_memory field can only be compared with = and != operator. ' ) )
2010-06-24 15:56:02 +00:00
value = bool ( value ) if operator == ' = ' else not bool ( value )
all_model_ids = self . search ( cr , uid , [ ] , context = context )
is_osv_mem = self . _is_osv_memory ( cr , uid , all_model_ids , ' osv_memory ' , arg = None , context = context )
return [ ( ' id ' , ' in ' , [ id for id in is_osv_mem if bool ( is_osv_mem [ id ] ) == value ] ) ]
2011-01-05 14:07:08 +00:00
def _view_ids ( self , cr , uid , ids , field_name , arg , context = None ) :
2011-01-18 15:52:19 +00:00
models = self . browse ( cr , uid , ids )
res = { }
2011-01-05 14:07:08 +00:00
for model in models :
2011-01-18 15:52:19 +00:00
res [ model . id ] = self . pool . get ( " ir.ui.view " ) . search ( cr , uid , [ ( ' model ' , ' = ' , model . model ) ] )
2011-01-05 14:07:08 +00:00
return res
2010-09-29 11:12:00 +00:00
2008-07-22 14:24:36 +00:00
_columns = {
2011-09-19 14:48:21 +00:00
' name ' : fields . char ( ' Model Description ' , size = 64 , translate = True , required = True ) ,
' model ' : fields . char ( ' Model ' , size = 64 , required = True , select = 1 ) ,
2008-07-22 14:24:36 +00:00
' info ' : fields . text ( ' Information ' ) ,
' field_id ' : fields . one2many ( ' ir.model.fields ' , ' model_id ' , ' Fields ' , required = True ) ,
2010-08-23 12:00:47 +00:00
' state ' : fields . selection ( [ ( ' manual ' , ' Custom Object ' ) , ( ' base ' , ' Base Object ' ) ] , ' Type ' , readonly = True ) ,
2008-09-04 16:14:13 +00:00
' access_ids ' : fields . one2many ( ' ir.model.access ' , ' model_id ' , ' Access ' ) ,
2012-04-24 06:29:06 +00:00
' osv_memory ' : fields . function ( _is_osv_memory , string = ' In-Memory Model ' , type = ' boolean ' ,
2010-06-24 15:56:02 +00:00
fnct_search = _search_osv_memory ,
2011-01-05 14:07:08 +00:00
help = " Indicates whether this object model lives in memory only, i.e. is not persisted (osv.osv_memory) " ) ,
2012-04-24 06:11:28 +00:00
' modules ' : fields . function ( _in_modules , type = ' char ' , size = 128 , string = ' In Modules ' , help = ' List of modules in which the object is defined or inherited ' ) ,
2012-01-04 13:30:27 +00:00
' view_ids ' : fields . function ( _view_ids , type = ' one2many ' , obj = ' ir.ui.view ' , string = ' Views ' ) ,
2008-07-22 14:24:36 +00:00
}
2011-09-19 14:48:21 +00:00
2008-07-22 14:24:36 +00:00
_defaults = {
' model ' : lambda * a : ' x_ ' ,
2010-11-23 15:40:32 +00:00
' state ' : lambda self , cr , uid , ctx = None : ( ctx and ctx . get ( ' manual ' , False ) ) and ' manual ' or ' base ' ,
2008-07-22 14:24:36 +00:00
}
2011-09-19 14:48:21 +00:00
2010-12-09 10:57:33 +00:00
def _check_model_name ( self , cr , uid , ids , context = None ) :
for model in self . browse ( cr , uid , ids , context = context ) :
2008-07-22 14:24:36 +00:00
if model . state == ' manual ' :
if not model . model . startswith ( ' x_ ' ) :
return False
2008-12-16 09:45:49 +00:00
if not re . match ( ' ^[a-z_A-Z0-9.]+$ ' , model . model ) :
2008-07-22 14:24:36 +00:00
return False
return True
2008-06-19 15:42:15 +00:00
2010-11-18 16:47:21 +00:00
def _model_name_msg ( self , cr , uid , ids , context = None ) :
return _ ( ' The Object name must start with x_ and not contain any special character ! ' )
2011-09-19 14:48:21 +00:00
2008-07-22 14:24:36 +00:00
_constraints = [
2010-11-18 16:47:21 +00:00
( _check_model_name , _model_name_msg , [ ' model ' ] ) ,
2008-07-22 14:24:36 +00:00
]
2011-09-19 14:48:21 +00:00
_sql_constraints = [
( ' obj_name_uniq ' , ' unique (model) ' , ' Each model must be unique! ' ) ,
]
2010-06-02 11:18:26 +00:00
# overridden to allow searching both on model name (model field)
# and model description (name field)
2011-10-17 13:37:02 +00:00
def _name_search ( self , cr , uid , name = ' ' , args = None , operator = ' ilike ' , context = None , limit = 100 , name_get_uid = None ) :
2010-06-02 11:18:26 +00:00
if args is None :
args = [ ]
domain = args + [ ' | ' , ( ' model ' , operator , name ) , ( ' name ' , operator , name ) ]
2011-10-17 13:37:02 +00:00
return self . name_get ( cr , name_get_uid or uid ,
super ( ir_model , self ) . search ( cr , uid , domain , limit = limit , context = context ) ,
context = context )
2010-06-02 11:18:26 +00:00
2012-02-29 13:53:43 +00:00
def _drop_table ( self , cr , uid , ids , context = None ) :
for model in self . browse ( cr , uid , ids , context ) :
model_pool = self . pool . get ( model . model )
2012-03-12 08:42:17 +00:00
cr . execute ( ' select relkind from pg_class where relname= %s ' , ( model_pool . _table , ) )
2012-03-09 12:47:53 +00:00
result = cr . fetchone ( )
if result and result [ 0 ] == ' v ' :
2012-03-12 08:42:17 +00:00
cr . execute ( ' DROP view %s ' % ( model_pool . _table , ) )
2012-03-09 12:47:53 +00:00
elif result and result [ 0 ] == ' r ' :
2012-03-12 08:42:17 +00:00
cr . execute ( ' DROP TABLE %s ' % ( model_pool . _table , ) )
2012-02-29 13:53:43 +00:00
return True
2010-06-02 11:18:26 +00:00
2008-07-22 14:24:36 +00:00
def unlink ( self , cr , user , ids , context = None ) :
2012-03-26 23:18:29 +00:00
# Prevent manual deletion of module tables
if context is None : context = { }
if isinstance ( ids , ( int , long ) ) :
ids = [ ids ]
if not context . get ( MODULE_UNINSTALL_FLAG ) and \
any ( model . state != ' manual ' for model in self . browse ( cr , user , ids , context ) ) :
raise except_orm ( _ ( ' Error ' ) , _ ( " Model ' %s ' contains module data and cannot be removed! " ) % ( model . name , ) )
2012-03-09 12:47:53 +00:00
self . _drop_table ( cr , user , ids , context )
2008-07-22 14:24:36 +00:00
res = super ( ir_model , self ) . unlink ( cr , user , ids , context )
2012-03-30 16:34:22 +00:00
if not context . get ( MODULE_UNINSTALL_FLAG ) :
# only reload pool for normal unlink. For module uninstall the
# reload is done independently in openerp.modules.loading
pooler . restart_pool ( cr . dbname )
2008-07-22 14:24:36 +00:00
return res
2008-04-13 06:54:08 +00:00
2009-07-06 19:32:22 +00:00
def write ( self , cr , user , ids , vals , context = None ) :
if context :
2009-07-28 14:31:31 +00:00
context . pop ( ' __last_update ' , None )
2012-02-08 22:17:06 +00:00
# Filter out operations 4 link from field id, because openerp-web
# always write (4,id,False) even for non dirty items
if ' field_id ' in vals :
vals [ ' field_id ' ] = [ op for op in vals [ ' field_id ' ] if op [ 0 ] != 4 ]
2009-07-06 19:32:22 +00:00
return super ( ir_model , self ) . write ( cr , user , ids , vals , context )
2010-04-29 12:01:58 +00:00
2008-07-22 14:24:36 +00:00
def create ( self , cr , user , vals , context = None ) :
2010-05-12 11:59:33 +00:00
if context is None :
context = { }
2008-07-22 14:24:36 +00:00
if context and context . get ( ' manual ' , False ) :
vals [ ' state ' ] = ' manual '
res = super ( ir_model , self ) . create ( cr , user , vals , context )
if vals . get ( ' state ' , ' base ' ) == ' manual ' :
2008-11-25 23:33:17 +00:00
self . instanciate ( cr , user , vals [ ' model ' ] , context )
self . pool . get ( vals [ ' model ' ] ) . __init__ ( self . pool , cr )
2010-02-09 09:13:28 +00:00
ctx = context . copy ( )
ctx . update ( { ' field_name ' : vals [ ' name ' ] , ' field_state ' : ' manual ' , ' select ' : vals . get ( ' select_level ' , ' 0 ' ) } )
self . pool . get ( vals [ ' model ' ] ) . _auto_init ( cr , ctx )
2008-11-25 23:33:17 +00:00
#pooler.restart_pool(cr.dbname)
2008-07-22 14:24:36 +00:00
return res
2006-12-07 13:41:40 +00:00
2011-11-07 15:19:49 +00:00
def instanciate ( self , cr , user , model , context = None ) :
2008-07-22 14:24:36 +00:00
class x_custom_model ( osv . osv ) :
pass
x_custom_model . _name = model
x_custom_model . _module = False
2011-08-16 07:44:31 +00:00
a = x_custom_model . create_instance ( self . pool , cr )
2010-04-19 21:01:17 +00:00
if ( not a . _columns ) or ( ' x_name ' in a . _columns . keys ( ) ) :
x_name = ' x_name '
else :
x_name = a . _columns . keys ( ) [ 0 ]
x_custom_model . _rec_name = x_name
2008-04-13 06:54:08 +00:00
ir_model ( )
2008-03-26 21:46:53 +00:00
2006-12-07 13:41:40 +00:00
class ir_model_fields ( osv . osv ) :
2008-07-22 14:24:36 +00:00
_name = ' ir.model.fields '
_description = " Fields "
2011-01-05 14:07:08 +00:00
2008-07-22 14:24:36 +00:00
_columns = {
' name ' : fields . char ( ' Name ' , required = True , size = 64 , select = 1 ) ,
2011-01-06 17:38:28 +00:00
' model ' : fields . char ( ' Object Name ' , size = 64 , required = True , select = 1 ,
help = " The technical name of the model this field belongs to " ) ,
' relation ' : fields . char ( ' Object Relation ' , size = 64 ,
help = " For relationship fields, the technical name of the target model " ) ,
' relation_field ' : fields . char ( ' Relation Field ' , size = 64 ,
help = " For one2many fields, the field on the target model that implement the opposite many2one relationship " ) ,
' model_id ' : fields . many2one ( ' ir.model ' , ' Model ' , required = True , select = True , ondelete = ' cascade ' ,
help = " The model this field belongs to " ) ,
2008-07-22 14:24:36 +00:00
' field_description ' : fields . char ( ' Field Label ' , required = True , size = 256 ) ,
' ttype ' : fields . selection ( _get_fields_type , ' Field Type ' , size = 64 , required = True ) ,
2011-01-06 17:38:28 +00:00
' selection ' : fields . char ( ' Selection Options ' , size = 128 , help = " List of options for a selection field, "
" specified as a Python expression defining a list of (key, label) pairs. "
" For example: [( ' blue ' , ' Blue ' ),( ' yellow ' , ' Yellow ' )] " ) ,
2008-07-22 14:24:36 +00:00
' required ' : fields . boolean ( ' Required ' ) ,
' readonly ' : fields . boolean ( ' Readonly ' ) ,
2011-01-06 17:38:28 +00:00
' select_level ' : fields . selection ( [ ( ' 0 ' , ' Not Searchable ' ) , ( ' 1 ' , ' Always Searchable ' ) , ( ' 2 ' , ' Advanced Search (deprecated) ' ) ] , ' Searchable ' , required = True ) ,
' translate ' : fields . boolean ( ' Translate ' , help = " Whether values for this field can be translated (enables the translation mechanism for that field) " ) ,
2008-07-22 14:24:36 +00:00
' size ' : fields . integer ( ' Size ' ) ,
2010-08-23 13:41:51 +00:00
' state ' : fields . selection ( [ ( ' manual ' , ' Custom Field ' ) , ( ' base ' , ' Base Field ' ) ] , ' Type ' , required = True , readonly = True , select = 1 ) ,
2012-04-24 06:11:28 +00:00
' on_delete ' : fields . selection ( [ ( ' cascade ' , ' Cascade ' ) , ( ' set null ' , ' Set NULL ' ) ] , ' On Delete ' , help = ' On delete property for many2one fields ' ) ,
2011-01-06 17:38:28 +00:00
' domain ' : fields . char ( ' Domain ' , size = 256 , help = " The optional domain to restrict possible values for relationship fields, "
" specified as a Python expression defining a list of triplets. "
" For example: [( ' color ' , ' = ' , ' red ' )] " ) ,
2008-07-22 14:24:36 +00:00
' groups ' : fields . many2many ( ' res.groups ' , ' ir_model_fields_group_rel ' , ' field_id ' , ' group_id ' , ' Groups ' ) ,
' view_load ' : fields . boolean ( ' View Auto-Load ' ) ,
2010-04-29 12:01:58 +00:00
' selectable ' : fields . boolean ( ' Selectable ' ) ,
2012-04-24 06:11:28 +00:00
' modules ' : fields . function ( _in_modules , type = ' char ' , size = 128 , string = ' In Modules ' , help = ' List of modules in which the field is defined ' ) ,
2011-12-22 18:24:05 +00:00
' serialization_field_id ' : fields . many2one ( ' ir.model.fields ' , ' Serialization Field ' , domain = " [( ' ttype ' , ' = ' , ' serialized ' )] " ,
ondelete = ' cascade ' , help = " If set, this field will be stored in the sparse "
" structure of the serialization field, instead "
" of having its own database column. This cannot be "
" changed after creation. " ) ,
2008-07-22 14:24:36 +00:00
}
2008-10-28 23:15:42 +00:00
_rec_name = ' field_description '
2008-07-22 14:24:36 +00:00
_defaults = {
2011-01-07 09:26:55 +00:00
' view_load ' : 0 ,
' selection ' : " " ,
' domain ' : " [] " ,
' name ' : ' x_ ' ,
2008-07-22 14:24:36 +00:00
' state ' : lambda self , cr , uid , ctx = { } : ( ctx and ctx . get ( ' manual ' , False ) ) and ' manual ' or ' base ' ,
2011-01-07 09:26:55 +00:00
' on_delete ' : ' set null ' ,
' select_level ' : ' 0 ' ,
' size ' : 64 ,
' field_description ' : ' ' ,
' selectable ' : 1 ,
2008-07-22 14:24:36 +00:00
}
2010-12-10 22:42:58 +00:00
_order = " name "
2011-01-03 11:03:33 +00:00
2011-01-07 09:26:55 +00:00
def _check_selection ( self , cr , uid , selection , context = None ) :
2011-01-03 11:03:33 +00:00
try :
2011-01-07 09:26:55 +00:00
selection_list = eval ( selection )
2011-01-03 11:03:33 +00:00
except Exception :
2012-01-24 11:47:30 +00:00
_logger . warning ( ' Invalid selection list definition for fields.selection ' , exc_info = True )
2011-01-10 12:33:02 +00:00
raise except_orm ( _ ( ' Error ' ) ,
2011-01-07 09:26:55 +00:00
_ ( " The Selection Options expression is not a valid Pythonic expression. " \
" Please provide an expression in the [( ' key ' , ' Label ' ), ...] format. " ) )
check = True
2011-01-06 17:38:28 +00:00
if not ( isinstance ( selection_list , list ) and selection_list ) :
2011-01-07 09:26:55 +00:00
check = False
else :
for item in selection_list :
if not ( isinstance ( item , ( tuple , list ) ) and len ( item ) == 2 ) :
check = False
break
2011-01-03 11:03:33 +00:00
2011-01-07 09:26:55 +00:00
if not check :
2011-01-10 12:33:02 +00:00
raise except_orm ( _ ( ' Error ' ) ,
2011-01-07 09:26:55 +00:00
_ ( " The Selection Options expression is must be in the [( ' key ' , ' Label ' ), ...] format! " ) )
return True
2011-01-03 11:03:33 +00:00
2010-11-18 16:47:21 +00:00
def _size_gt_zero_msg ( self , cr , user , ids , context = None ) :
return _ ( ' Size of the field can never be less than 1 ! ' )
2009-07-15 13:45:30 +00:00
_sql_constraints = [
2010-11-18 16:47:21 +00:00
( ' size_gt_zero ' , ' CHECK (size>0) ' , _size_gt_zero_msg ) ,
2009-07-15 13:45:30 +00:00
]
2012-03-26 23:18:29 +00:00
2012-02-29 13:53:43 +00:00
def _drop_column ( self , cr , uid , ids , context = None ) :
2012-03-26 23:18:29 +00:00
for field in self . browse ( cr , uid , ids , context ) :
model = self . pool . get ( field . model )
cr . execute ( ' select relkind from pg_class where relname= %s ' , ( model . _table , ) )
result = cr . fetchone ( )
cr . execute ( " SELECT column_name FROM information_schema.columns WHERE table_name = ' %s ' and column_name= ' %s ' " % ( model . _table , field . name ) )
column_name = cr . fetchone ( )
if column_name and ( result and result [ 0 ] == ' r ' ) :
cr . execute ( ' ALTER table " %s " DROP column " %s " cascade ' % ( model . _table , field . name ) )
model . _columns . pop ( field . name , None )
2012-02-29 13:53:43 +00:00
return True
2011-01-07 09:26:55 +00:00
2008-07-22 14:24:36 +00:00
def unlink ( self , cr , user , ids , context = None ) :
2012-03-26 23:18:29 +00:00
# Prevent manual deletion of module columns
if context is None : context = { }
if isinstance ( ids , ( int , long ) ) :
ids = [ ids ]
if not context . get ( MODULE_UNINSTALL_FLAG ) and \
any ( field . state != ' manual ' for field in self . browse ( cr , user , ids , context ) ) :
raise except_orm ( _ ( ' Error ' ) , _ ( " This column contains module data and cannot be removed! " ) )
2012-02-29 13:53:43 +00:00
self . _drop_column ( cr , user , ids , context )
res = super ( ir_model_fields , self ) . unlink ( cr , user , ids , context )
return res
2008-12-16 10:14:22 +00:00
2008-07-22 14:24:36 +00:00
def create ( self , cr , user , vals , context = None ) :
if ' model_id ' in vals :
2008-12-16 10:14:22 +00:00
model_data = self . pool . get ( ' ir.model ' ) . browse ( cr , user , vals [ ' model_id ' ] )
vals [ ' model ' ] = model_data . model
2010-05-11 10:55:08 +00:00
if context is None :
context = { }
2008-07-22 14:24:36 +00:00
if context and context . get ( ' manual ' , False ) :
2008-12-16 10:14:22 +00:00
vals [ ' state ' ] = ' manual '
2011-01-07 09:26:55 +00:00
if vals . get ( ' ttype ' , False ) == ' selection ' :
if not vals . get ( ' selection ' , False ) :
raise except_orm ( _ ( ' Error ' ) , _ ( ' For selection fields, the Selection Options must be given! ' ) )
self . _check_selection ( cr , user , vals [ ' selection ' ] , context = context )
2009-12-29 13:08:41 +00:00
res = super ( ir_model_fields , self ) . create ( cr , user , vals , context )
2008-12-16 10:14:22 +00:00
if vals . get ( ' state ' , ' base ' ) == ' manual ' :
2008-07-22 14:24:36 +00:00
if not vals [ ' name ' ] . startswith ( ' x_ ' ) :
raise except_orm ( _ ( ' Error ' ) , _ ( " Custom fields must have a name that starts with ' x_ ' ! " ) )
2009-12-29 13:08:41 +00:00
2010-05-11 10:55:08 +00:00
if vals . get ( ' relation ' , False ) and not self . pool . get ( ' ir.model ' ) . search ( cr , user , [ ( ' model ' , ' = ' , vals [ ' relation ' ] ) ] ) :
2012-03-21 15:15:23 +00:00
raise except_orm ( _ ( ' Error ' ) , _ ( " Model %s does not exist! " ) % vals [ ' relation ' ] )
2009-12-29 13:08:41 +00:00
2009-05-11 11:17:38 +00:00
if self . pool . get ( vals [ ' model ' ] ) :
self . pool . get ( vals [ ' model ' ] ) . __init__ ( self . pool , cr )
2009-12-29 13:08:41 +00:00
#Added context to _auto_init for special treatment to custom field for select_level
2010-02-09 09:13:28 +00:00
ctx = context . copy ( )
ctx . update ( { ' field_name ' : vals [ ' name ' ] , ' field_state ' : ' manual ' , ' select ' : vals . get ( ' select_level ' , ' 0 ' ) , ' update_custom_fields ' : True } )
self . pool . get ( vals [ ' model ' ] ) . _auto_init ( cr , ctx )
2009-12-29 13:08:41 +00:00
2008-07-22 14:24:36 +00:00
return res
2010-04-29 12:01:58 +00:00
2011-01-07 09:26:55 +00:00
def write ( self , cr , user , ids , vals , context = None ) :
2011-01-07 09:29:17 +00:00
if context is None :
context = { }
if context and context . get ( ' manual ' , False ) :
vals [ ' state ' ] = ' manual '
2011-01-07 09:26:55 +00:00
2011-12-22 18:24:05 +00:00
#For the moment renaming a sparse field or changing the storing system is not allowed. This may be done later
2011-09-23 11:26:18 +00:00
if ' serialization_field_id ' in vals or ' name ' in vals :
2011-09-17 18:35:35 +00:00
for field in self . browse ( cr , user , ids , context = context ) :
2011-09-23 11:26:18 +00:00
if ' serialization_field_id ' in vals and field . serialization_field_id . id != vals [ ' serialization_field_id ' ] :
2012-02-08 00:41:24 +00:00
raise except_orm ( _ ( ' Error! ' ) , _ ( ' Changing the storing system for field " %s " is not allowed. ' ) % field . name )
2011-09-23 11:26:18 +00:00
if field . serialization_field_id and ( field . name != vals [ ' name ' ] ) :
2012-02-08 00:41:24 +00:00
raise except_orm ( _ ( ' Error! ' ) , _ ( ' Renaming sparse field " %s " is not allowed ' ) % field . name )
2011-01-07 09:29:17 +00:00
column_rename = None # if set, *one* column can be renamed here
obj = None
2011-01-10 12:33:02 +00:00
models_patch = { } # structs of (obj, [(field, prop, change_to),..])
# data to be updated on the orm model
2011-01-07 09:29:17 +00:00
# static table of properties
model_props = [ # (our-name, fields.prop, set_fn)
2011-03-29 11:38:24 +00:00
( ' field_description ' , ' string ' , str ) ,
2011-01-07 09:29:17 +00:00
( ' required ' , ' required ' , bool ) ,
( ' readonly ' , ' readonly ' , bool ) ,
2011-03-29 11:38:24 +00:00
( ' domain ' , ' _domain ' , eval ) ,
2011-01-07 09:29:17 +00:00
( ' size ' , ' size ' , int ) ,
( ' on_delete ' , ' ondelete ' , str ) ,
( ' translate ' , ' translate ' , bool ) ,
( ' view_load ' , ' view_load ' , bool ) ,
( ' selectable ' , ' selectable ' , bool ) ,
( ' select_level ' , ' select ' , int ) ,
( ' selection ' , ' selection ' , eval ) ,
]
if vals and ids :
checked_selection = False # need only check it once, so defer
2011-01-10 12:33:02 +00:00
2011-01-07 09:26:55 +00:00
for item in self . browse ( cr , user , ids , context = context ) :
2011-01-07 09:29:17 +00:00
if not ( obj and obj . _name == item . model ) :
obj = self . pool . get ( item . model )
2011-01-10 12:33:02 +00:00
if item . state != ' manual ' :
raise except_orm ( _ ( ' Error! ' ) ,
_ ( ' Properties of base fields cannot be altered in this manner! '
' Please modify them through Python code, '
' preferably through a custom addon! ' ) )
2011-01-07 09:29:17 +00:00
if item . ttype == ' selection ' and ' selection ' in vals \
and not checked_selection :
self . _check_selection ( cr , user , vals [ ' selection ' ] , context = context )
checked_selection = True
final_name = item . name
if ' name ' in vals and vals [ ' name ' ] != item . name :
# We need to rename the column
if column_rename :
raise except_orm ( _ ( ' Error! ' ) , _ ( ' Can only rename one column at a time! ' ) )
if vals [ ' name ' ] in obj . _columns :
raise except_orm ( _ ( ' Error! ' ) , _ ( ' Cannot rename column to %s , because that column already exists! ' ) % vals [ ' name ' ] )
if vals . get ( ' state ' , ' base ' ) == ' manual ' and not vals [ ' name ' ] . startswith ( ' x_ ' ) :
raise except_orm ( _ ( ' Error! ' ) , _ ( ' New column name must still start with x_ , because it is a custom field! ' ) )
if ' \' ' in vals [ ' name ' ] or ' " ' in vals [ ' name ' ] or ' ; ' in vals [ ' name ' ] :
raise ValueError ( ' Invalid character in column name ' )
column_rename = ( obj , ( obj . _table , item . name , vals [ ' name ' ] ) )
final_name = vals [ ' name ' ]
2011-01-10 12:33:02 +00:00
2011-01-07 09:29:17 +00:00
if ' model_id ' in vals and vals [ ' model_id ' ] != item . model_id :
raise except_orm ( _ ( " Error! " ) , _ ( " Changing the model of a field is forbidden! " ) )
2011-01-10 12:33:02 +00:00
2011-01-07 09:29:17 +00:00
if ' ttype ' in vals and vals [ ' ttype ' ] != item . ttype :
raise except_orm ( _ ( " Error! " ) , _ ( " Changing the type of a column is not yet supported. "
" Please drop it and create it again! " ) )
2011-01-10 12:33:02 +00:00
2011-01-07 09:29:17 +00:00
# We don't check the 'state', because it might come from the context
# (thus be set for multiple fields) and will be ignored anyway.
if obj :
2011-01-10 12:33:02 +00:00
models_patch . setdefault ( obj . _name , ( obj , [ ] ) )
# find out which properties (per model) we need to update
for field_name , field_property , set_fn in model_props :
if field_name in vals :
property_value = set_fn ( vals [ field_name ] )
if getattr ( obj . _columns [ item . name ] , field_property ) != property_value :
models_patch [ obj . _name ] [ 1 ] . append ( ( final_name , field_property , property_value ) )
2011-01-07 09:29:17 +00:00
# our dict is ready here, but no properties are changed so far
# These shall never be written (modified)
2011-01-10 12:33:02 +00:00
for column_name in ( ' model_id ' , ' model ' , ' state ' ) :
if column_name in vals :
del vals [ column_name ]
2011-01-07 09:26:55 +00:00
res = super ( ir_model_fields , self ) . write ( cr , user , ids , vals , context = context )
2011-01-07 09:29:17 +00:00
if column_rename :
cr . execute ( ' ALTER TABLE " %s " RENAME COLUMN " %s " TO " %s " ' % column_rename [ 1 ] )
# This is VERY risky, but let us have this feature:
# we want to change the key of column in obj._columns dict
col = column_rename [ 0 ] . _columns . pop ( column_rename [ 1 ] [ 1 ] ) # take object out, w/o copy
column_rename [ 0 ] . _columns [ column_rename [ 1 ] [ 2 ] ] = col
2011-01-10 12:33:02 +00:00
if models_patch :
# We have to update _columns of the model(s) and then call their
2011-01-07 09:29:17 +00:00
# _auto_init to sync the db with the model. Hopefully, since write()
# was called earlier, they will be in-sync before the _auto_init.
# Anything we don't update in _columns now will be reset from
# the model into ir.model.fields (db).
ctx = context . copy ( )
2011-01-10 12:33:02 +00:00
ctx . update ( { ' select ' : vals . get ( ' select_level ' , ' 0 ' ) , ' update_custom_fields ' : True } )
2012-03-31 01:34:55 +00:00
for __ , patch_struct in models_patch . items ( ) :
2011-01-10 12:33:02 +00:00
obj = patch_struct [ 0 ]
for col_name , col_prop , val in patch_struct [ 1 ] :
2011-01-07 09:29:17 +00:00
setattr ( obj . _columns [ col_name ] , col_prop , val )
obj . _auto_init ( cr , ctx )
2011-01-07 09:26:55 +00:00
return res
2006-12-07 13:41:40 +00:00
ir_model_fields ( )
class ir_model_access ( osv . osv ) :
2008-07-22 14:24:36 +00:00
_name = ' ir.model.access '
_columns = {
2010-07-05 17:58:05 +00:00
' name ' : fields . char ( ' Name ' , size = 64 , required = True , select = True ) ,
2011-01-14 19:00:38 +00:00
' model_id ' : fields . many2one ( ' ir.model ' , ' Object ' , required = True , domain = [ ( ' osv_memory ' , ' = ' , False ) ] , select = True , ondelete = ' cascade ' ) ,
2010-07-05 17:58:05 +00:00
' group_id ' : fields . many2one ( ' res.groups ' , ' Group ' , ondelete = ' cascade ' , select = True ) ,
2008-07-22 14:24:36 +00:00
' perm_read ' : fields . boolean ( ' Read Access ' ) ,
' perm_write ' : fields . boolean ( ' Write Access ' ) ,
' perm_create ' : fields . boolean ( ' Create Access ' ) ,
2010-09-29 07:58:01 +00:00
' perm_unlink ' : fields . boolean ( ' Delete Access ' ) ,
2008-07-22 14:24:36 +00:00
}
2008-09-22 06:48:49 +00:00
2008-07-22 14:24:36 +00:00
def check_groups ( self , cr , uid , group ) :
grouparr = group . split ( ' . ' )
2008-09-22 11:13:18 +00:00
if not grouparr :
return False
2010-06-18 14:05:37 +00:00
cr . execute ( " select 1 from res_groups_users_rel where uid= %s and gid IN (select res_id from ir_model_data where module= %s and name= %s ) " , ( uid , grouparr [ 0 ] , grouparr [ 1 ] , ) )
2008-09-22 11:13:18 +00:00
return bool ( cr . fetchone ( ) )
2008-09-22 06:48:49 +00:00
2008-10-17 16:15:53 +00:00
def check_group ( self , cr , uid , model , mode , group_ids ) :
2008-10-17 09:23:18 +00:00
""" Check if a specific group has the access mode to the specified model """
assert mode in [ ' read ' , ' write ' , ' create ' , ' unlink ' ] , ' Invalid access mode '
if isinstance ( model , browse_record ) :
assert model . _table_name == ' ir.model ' , ' Invalid model object '
model_name = model . name
else :
model_name = model
2008-12-16 10:14:22 +00:00
2008-10-17 16:15:53 +00:00
if isinstance ( group_ids , ( int , long ) ) :
group_ids = [ group_ids ]
for group_id in group_ids :
cr . execute ( " SELECT perm_ " + mode + " "
2008-10-17 09:23:18 +00:00
" FROM ir_model_access a "
" JOIN ir_model m ON (m.id = a.model_id) "
2008-12-09 12:37:22 +00:00
" WHERE m.model = %s AND a.group_id = %s " , ( model_name , group_id )
2008-10-17 09:23:18 +00:00
)
2008-10-17 16:15:53 +00:00
r = cr . fetchone ( )
if r is None :
cr . execute ( " SELECT perm_ " + mode + " "
2008-10-17 10:05:50 +00:00
" FROM ir_model_access a "
" JOIN ir_model m ON (m.id = a.model_id) "
" WHERE m.model = %s AND a.group_id IS NULL " , ( model_name , )
)
2008-10-17 16:15:53 +00:00
r = cr . fetchone ( )
2008-10-17 10:05:50 +00:00
2008-10-17 16:15:53 +00:00
access = bool ( r and r [ 0 ] )
if access :
return True
# pass no groups -> no access
return False
2008-09-22 06:48:49 +00:00
2011-03-24 11:14:27 +00:00
def group_names_with_access ( self , cr , model_name , access_mode ) :
""" Returns the names of visible groups which have been granted ``access_mode`` on
the model ` ` model_name ` ` .
: rtype : list
"""
assert access_mode in [ ' read ' , ' write ' , ' create ' , ' unlink ' ] , ' Invalid access mode: %s ' % access_mode
cr . execute ( ''' SELECT
g . name
FROM
ir_model_access a
JOIN ir_model m ON ( a . model_id = m . id )
JOIN res_groups g ON ( a . group_id = g . id )
WHERE
m . model = % s AND
a . perm_ ''' + access_mode, (model_name,))
return [ x [ 0 ] for x in cr . fetchall ( ) ]
2011-06-08 03:03:30 +00:00
@tools.ormcache ( )
2009-08-10 16:24:09 +00:00
def check ( self , cr , uid , model , mode = ' read ' , raise_exception = True , context = None ) :
2008-07-18 16:40:51 +00:00
if uid == 1 :
2008-12-16 10:14:22 +00:00
# User root have all accesses
2008-10-17 09:23:18 +00:00
# TODO: exclude xml-rpc requests
2008-08-24 15:03:23 +00:00
return True
2008-09-22 06:48:49 +00:00
2008-08-04 09:31:37 +00:00
assert mode in [ ' read ' , ' write ' , ' create ' , ' unlink ' ] , ' Invalid access mode '
2008-09-22 06:48:49 +00:00
2008-10-08 14:13:18 +00:00
if isinstance ( model , browse_record ) :
assert model . _table_name == ' ir.model ' , ' Invalid model object '
2011-09-23 15:45:14 +00:00
model_name = model . model
2008-10-08 14:13:18 +00:00
else :
model_name = model
2008-12-16 10:14:22 +00:00
2011-09-23 15:45:14 +00:00
# TransientModel records have no access rights, only an implicit access rule
2012-02-09 08:38:28 +00:00
if self . pool . get ( model_name ) . is_transient ( ) :
2010-08-05 18:14:58 +00:00
return True
2008-07-18 15:53:12 +00:00
# We check if a specific rule exists
2008-09-22 11:13:18 +00:00
cr . execute ( ' SELECT MAX(CASE WHEN perm_ ' + mode + ' THEN 1 ELSE 0 END) '
' FROM ir_model_access a '
' JOIN ir_model m ON (m.id = a.model_id) '
' JOIN res_groups_users_rel gu ON (gu.gid = a.group_id) '
' WHERE m.model = %s '
' AND gu.uid = %s '
, ( model_name , uid , )
)
r = cr . fetchone ( ) [ 0 ]
if r is None :
# there is no specific rule. We check the generic rule
cr . execute ( ' SELECT MAX(CASE WHEN perm_ ' + mode + ' THEN 1 ELSE 0 END) '
' FROM ir_model_access a '
' JOIN ir_model m ON (m.id = a.model_id) '
' WHERE a.group_id IS NULL '
' AND m.model = %s '
, ( model_name , )
)
r = cr . fetchone ( ) [ 0 ]
2008-09-22 06:48:49 +00:00
2008-09-22 11:13:18 +00:00
if not r and raise_exception :
2011-03-24 11:14:27 +00:00
groups = ' , ' . join ( self . group_names_with_access ( cr , model_name , mode ) ) or ' / '
2008-09-22 11:13:18 +00:00
msgs = {
2010-12-29 12:44:27 +00:00
' read ' : _ ( " You can not read this document ( %s ) ! Be sure your user belongs to one of these groups: %s . " ) ,
' write ' : _ ( " You can not write in this document ( %s ) ! Be sure your user belongs to one of these groups: %s . " ) ,
' create ' : _ ( " You can not create this document ( %s ) ! Be sure your user belongs to one of these groups: %s . " ) ,
' unlink ' : _ ( " You can not delete this document ( %s ) ! Be sure your user belongs to one of these groups: %s . " ) ,
2008-09-22 11:13:18 +00:00
}
2010-04-29 12:01:58 +00:00
2010-12-29 12:44:27 +00:00
raise except_orm ( _ ( ' AccessError ' ) , msgs [ mode ] % ( model_name , groups ) )
2011-09-23 15:45:14 +00:00
return r or False
2007-09-26 05:17:28 +00:00
2008-10-16 18:28:16 +00:00
__cache_clearing_methods = [ ]
def register_cache_clearing_method ( self , model , method ) :
self . __cache_clearing_methods . append ( ( model , method ) )
def unregister_cache_clearing_method ( self , model , method ) :
try :
i = self . __cache_clearing_methods . index ( ( model , method ) )
del self . __cache_clearing_methods [ i ]
except ValueError :
pass
2009-01-26 23:19:05 +00:00
def call_cache_clearing_methods ( self , cr ) :
2011-06-08 03:03:30 +00:00
self . check . clear_cache ( self ) # clear the cache of check function
2008-10-16 18:28:16 +00:00
for model , method in self . __cache_clearing_methods :
2011-01-20 16:00:20 +00:00
object_ = self . pool . get ( model )
if object_ :
getattr ( object_ , method ) ( )
2008-10-16 18:28:16 +00:00
2008-07-22 14:24:36 +00:00
#
2008-07-14 13:53:01 +00:00
# Check rights on actions
2008-07-22 14:24:36 +00:00
#
def write ( self , cr , uid , * args , * * argv ) :
2009-01-26 23:36:45 +00:00
self . call_cache_clearing_methods ( cr )
2008-07-22 14:24:36 +00:00
res = super ( ir_model_access , self ) . write ( cr , uid , * args , * * argv )
return res
2008-12-16 10:14:22 +00:00
2008-07-22 14:24:36 +00:00
def create ( self , cr , uid , * args , * * argv ) :
2009-01-26 23:36:45 +00:00
self . call_cache_clearing_methods ( cr )
2008-07-22 14:24:36 +00:00
res = super ( ir_model_access , self ) . create ( cr , uid , * args , * * argv )
return res
2008-10-16 18:28:16 +00:00
2008-07-22 14:24:36 +00:00
def unlink ( self , cr , uid , * args , * * argv ) :
2009-01-26 23:36:45 +00:00
self . call_cache_clearing_methods ( cr )
2008-07-22 14:24:36 +00:00
res = super ( ir_model_access , self ) . unlink ( cr , uid , * args , * * argv )
return res
2008-10-16 18:28:16 +00:00
2006-12-07 13:41:40 +00:00
ir_model_access ( )
class ir_model_data ( osv . osv ) :
2011-09-06 07:57:11 +00:00
""" Holds external identifier keys for records in the database.
This has two main uses :
* allows easy data integration with third - party systems ,
making import / export / sync of data possible , as records
can be uniquely identified across multiple systems
* allows tracking the origin of data installed by OpenERP
modules themselves , thus making it possible to later
update them seamlessly .
"""
2008-07-22 14:24:36 +00:00
_name = ' ir.model.data '
2010-12-10 22:42:58 +00:00
_order = ' module,model,name '
2008-07-22 14:24:36 +00:00
_columns = {
2011-09-06 07:57:11 +00:00
' name ' : fields . char ( ' External Identifier ' , required = True , size = 128 , select = 1 ,
help = " External Key/Identifier that can be used for "
" data integration with third-party systems " ) ,
' model ' : fields . char ( ' Model Name ' , required = True , size = 64 , select = 1 ) ,
2010-07-05 17:58:05 +00:00
' module ' : fields . char ( ' Module ' , required = True , size = 64 , select = 1 ) ,
2011-09-06 07:57:11 +00:00
' res_id ' : fields . integer ( ' Record ID ' , select = 1 ,
help = " ID of the target record in the database " ) ,
2008-07-22 14:24:36 +00:00
' noupdate ' : fields . boolean ( ' Non Updatable ' ) ,
' date_update ' : fields . datetime ( ' Update Date ' ) ,
' date_init ' : fields . datetime ( ' Init Date ' )
}
_defaults = {
' date_init ' : lambda * a : time . strftime ( ' % Y- % m- %d % H: % M: % S ' ) ,
' date_update ' : lambda * a : time . strftime ( ' % Y- % m- %d % H: % M: % S ' ) ,
2010-11-23 15:43:35 +00:00
' noupdate ' : False ,
' module ' : ' '
2008-07-22 14:24:36 +00:00
}
2009-06-29 12:40:45 +00:00
_sql_constraints = [
2011-09-06 07:57:11 +00:00
( ' module_name_uniq ' , ' unique(name, module) ' , ' You cannot have multiple records with the same external ID in the same module! ' ) ,
2009-06-29 12:40:45 +00:00
]
2006-12-07 13:41:40 +00:00
2008-07-22 14:24:36 +00:00
def __init__ ( self , pool , cr ) :
osv . osv . __init__ ( self , pool , cr )
self . doinit = True
2011-01-17 18:44:53 +00:00
# also stored in pool to avoid being discarded along with this osv instance
if getattr ( pool , ' model_data_reference_ids ' , None ) is None :
self . pool . model_data_reference_ids = { }
2012-03-26 23:18:29 +00:00
2011-01-17 18:44:53 +00:00
self . loads = self . pool . model_data_reference_ids
2010-11-27 03:04:50 +00:00
def _auto_init ( self , cr , context = None ) :
super ( ir_model_data , self ) . _auto_init ( cr , context )
cr . execute ( ' SELECT indexname FROM pg_indexes WHERE indexname = \' ir_model_data_module_name_index \' ' )
if not cr . fetchone ( ) :
cr . execute ( ' CREATE INDEX ir_model_data_module_name_index ON ir_model_data (module, name) ' )
2011-06-08 03:03:30 +00:00
@tools.ormcache ( )
2009-06-29 12:40:45 +00:00
def _get_id ( self , cr , uid , module , xml_id ) :
2010-07-09 11:02:52 +00:00
""" Returns the id of the ir.model.data record corresponding to a given module and xml_id (cached) or raise a ValueError if not found """
ids = self . search ( cr , uid , [ ( ' module ' , ' = ' , module ) , ( ' name ' , ' = ' , xml_id ) ] )
2009-06-29 12:40:45 +00:00
if not ids :
2011-09-15 09:40:55 +00:00
raise ValueError ( ' No such external ID currently defined in the system: %s . %s ' % ( module , xml_id ) )
2009-06-29 12:40:45 +00:00
# the sql constraints ensure us we have only one result
2008-07-22 14:24:36 +00:00
return ids [ 0 ]
2006-12-07 13:41:40 +00:00
2011-06-08 03:03:30 +00:00
@tools.ormcache ( )
2010-07-09 11:02:52 +00:00
def get_object_reference ( self , cr , uid , module , xml_id ) :
""" Returns (model, res_id) corresponding to a given module and xml_id (cached) or raise ValueError if not found """
data_id = self . _get_id ( cr , uid , module , xml_id )
res = self . read ( cr , uid , data_id , [ ' model ' , ' res_id ' ] )
2011-03-22 14:43:33 +00:00
if not res [ ' res_id ' ] :
2011-09-15 09:40:55 +00:00
raise ValueError ( ' No such external ID currently defined in the system: %s . %s ' % ( module , xml_id ) )
2010-07-09 11:02:52 +00:00
return ( res [ ' model ' ] , res [ ' res_id ' ] )
def get_object ( self , cr , uid , module , xml_id , context = None ) :
""" Returns a browsable record for the given module name and xml_id or raise ValueError if not found """
res_model , res_id = self . get_object_reference ( cr , uid , module , xml_id )
2011-03-22 21:05:22 +00:00
result = self . pool . get ( res_model ) . browse ( cr , uid , res_id , context = context )
if not result . exists ( ) :
raise ValueError ( ' No record found for unique ID %s . %s . It may have been deleted. ' % ( module , xml_id ) )
return result
2010-07-09 11:02:52 +00:00
2008-07-22 14:24:36 +00:00
def _update_dummy ( self , cr , uid , model , module , xml_id = False , store = True ) :
if not xml_id :
return False
try :
id = self . read ( cr , uid , [ self . _get_id ( cr , uid , module , xml_id ) ] , [ ' res_id ' ] ) [ 0 ] [ ' res_id ' ]
self . loads [ ( module , xml_id ) ] = ( model , id )
except :
id = False
return id
2012-03-26 23:18:29 +00:00
2006-12-07 13:41:40 +00:00
2011-05-12 10:00:38 +00:00
def unlink ( self , cr , uid , ids , context = None ) :
2011-06-22 13:53:44 +00:00
""" Regular unlink method, but make sure to clear the caches. """
2011-08-24 21:48:43 +00:00
self . _get_id . clear_cache ( self )
self . get_object_reference . clear_cache ( self )
2011-05-20 08:48:28 +00:00
return super ( ir_model_data , self ) . unlink ( cr , uid , ids , context = context )
2006-12-07 13:41:40 +00:00
2009-10-05 12:10:29 +00:00
def _update ( self , cr , uid , model , module , values , xml_id = False , store = True , noupdate = False , mode = ' init ' , res_id = False , context = None ) :
2008-07-22 14:24:36 +00:00
model_obj = self . pool . get ( model )
2009-10-05 12:10:29 +00:00
if not context :
context = { }
2012-04-06 08:17:26 +00:00
# records created during module install should not display the messages of OpenChatter
2012-04-03 07:47:35 +00:00
context = dict ( context , install_mode = True )
2008-07-22 14:24:36 +00:00
if xml_id and ( ' . ' in xml_id ) :
2010-10-12 05:27:02 +00:00
assert len ( xml_id . split ( ' . ' ) ) == 2 , _ ( " ' %s ' contains too many dots. XML ids should not contain dots ! These are used to refer to other modules data, as in module.reference_id " ) % ( xml_id )
2008-07-22 14:24:36 +00:00
module , xml_id = xml_id . split ( ' . ' )
if ( not xml_id ) and ( not self . doinit ) :
return False
action_id = False
if xml_id :
2011-10-13 16:33:16 +00:00
cr . execute ( ''' SELECT imd.id, imd.res_id, md.id, imd.model
2010-11-27 03:04:50 +00:00
FROM ir_model_data imd LEFT JOIN % s md ON ( imd . res_id = md . id )
WHERE imd . module = % % s AND imd . name = % % s ''' % model_obj._table,
( module , xml_id ) )
2008-07-22 14:24:36 +00:00
results = cr . fetchall ( )
2011-10-13 16:33:16 +00:00
for imd_id2 , res_id2 , real_id2 , real_model in results :
2010-11-27 03:04:50 +00:00
if not real_id2 :
2011-06-08 03:03:30 +00:00
self . _get_id . clear_cache ( self , uid , module , xml_id )
self . get_object_reference . clear_cache ( self , uid , module , xml_id )
2010-11-27 03:04:50 +00:00
cr . execute ( ' delete from ir_model_data where id= %s ' , ( imd_id2 , ) )
2009-03-05 12:16:34 +00:00
res_id = False
2008-07-22 14:24:36 +00:00
else :
2011-10-13 16:33:16 +00:00
assert model == real_model , " External ID conflict, %s already refers to a ` %s ` record, " \
" you can ' t define a ` %s ` record with this ID. " % ( xml_id , real_model , model )
2010-11-27 03:04:50 +00:00
res_id , action_id = res_id2 , imd_id2
2006-12-07 13:41:40 +00:00
2008-07-22 14:24:36 +00:00
if action_id and res_id :
2009-10-05 12:10:29 +00:00
model_obj . write ( cr , uid , [ res_id ] , values , context = context )
2008-07-22 14:24:36 +00:00
self . write ( cr , uid , [ action_id ] , {
' date_update ' : time . strftime ( ' % Y- % m- %d % H: % M: % S ' ) ,
2009-10-05 12:10:29 +00:00
} , context = context )
2008-07-22 14:24:36 +00:00
elif res_id :
2009-10-05 12:10:29 +00:00
model_obj . write ( cr , uid , [ res_id ] , values , context = context )
2008-07-22 14:24:36 +00:00
if xml_id :
self . create ( cr , uid , {
' name ' : xml_id ,
' model ' : model ,
' module ' : module ,
' res_id ' : res_id ,
' noupdate ' : noupdate ,
2009-10-05 12:10:29 +00:00
} , context = context )
2008-07-22 14:24:36 +00:00
if model_obj . _inherits :
for table in model_obj . _inherits :
inherit_id = model_obj . browse ( cr , uid ,
2009-10-05 12:10:29 +00:00
res_id , context = context ) [ model_obj . _inherits [ table ] ]
2008-07-22 14:24:36 +00:00
self . create ( cr , uid , {
' name ' : xml_id + ' _ ' + table . replace ( ' . ' , ' _ ' ) ,
' model ' : table ,
' module ' : module ,
2011-01-17 20:49:32 +00:00
' res_id ' : inherit_id . id ,
2008-07-22 14:24:36 +00:00
' noupdate ' : noupdate ,
2009-10-05 12:10:29 +00:00
} , context = context )
2008-07-22 14:24:36 +00:00
else :
if mode == ' init ' or ( mode == ' update ' and xml_id ) :
2009-10-05 12:10:29 +00:00
res_id = model_obj . create ( cr , uid , values , context = context )
2008-07-22 14:24:36 +00:00
if xml_id :
self . create ( cr , uid , {
' name ' : xml_id ,
' model ' : model ,
' module ' : module ,
' res_id ' : res_id ,
' noupdate ' : noupdate
2009-10-05 12:10:29 +00:00
} , context = context )
2008-07-22 14:24:36 +00:00
if model_obj . _inherits :
for table in model_obj . _inherits :
inherit_id = model_obj . browse ( cr , uid ,
2009-10-05 12:10:29 +00:00
res_id , context = context ) [ model_obj . _inherits [ table ] ]
2008-07-22 14:24:36 +00:00
self . create ( cr , uid , {
' name ' : xml_id + ' _ ' + table . replace ( ' . ' , ' _ ' ) ,
' model ' : table ,
' module ' : module ,
2011-01-17 20:49:32 +00:00
' res_id ' : inherit_id . id ,
2008-07-22 14:24:36 +00:00
' noupdate ' : noupdate ,
2009-10-05 12:10:29 +00:00
} , context = context )
2008-07-22 14:24:36 +00:00
if xml_id :
if res_id :
self . loads [ ( module , xml_id ) ] = ( model , res_id )
if model_obj . _inherits :
for table in model_obj . _inherits :
inherit_field = model_obj . _inherits [ table ]
inherit_id = model_obj . read ( cr , uid , res_id ,
[ inherit_field ] ) [ inherit_field ]
self . loads [ ( module , xml_id + ' _ ' + \
table . replace ( ' . ' , ' _ ' ) ) ] = ( table , inherit_id )
return res_id
2006-12-07 13:41:40 +00:00
2008-07-22 14:24:36 +00:00
def ir_set ( self , cr , uid , key , key2 , name , models , value , replace = True , isobject = False , meta = None , xml_id = False ) :
if type ( models [ 0 ] ) == type ( [ ] ) or type ( models [ 0 ] ) == type ( ( ) ) :
model , res_id = models [ 0 ]
else :
res_id = None
model = models [ 0 ]
2006-12-07 13:41:40 +00:00
2008-07-22 14:24:36 +00:00
if res_id :
2008-12-09 12:37:22 +00:00
where = ' and res_id= %s ' % ( res_id , )
2008-07-22 14:24:36 +00:00
else :
where = ' and (res_id is null) '
2006-12-07 13:41:40 +00:00
2008-07-22 14:24:36 +00:00
if key2 :
where + = ' and key2= \' %s \' ' % ( key2 , )
else :
where + = ' and (key2 is null) '
2006-12-07 13:41:40 +00:00
2008-07-22 14:24:36 +00:00
cr . execute ( ' select * from ir_values where model= %s and key= %s and name= %s ' + where , ( model , key , name ) )
res = cr . fetchone ( )
if not res :
2011-05-19 12:27:52 +00:00
ir_values_obj = pooler . get_pool ( cr . dbname ) . get ( ' ir.values ' )
res = ir_values_obj . set ( cr , uid , key , key2 , name , models , value , replace , isobject , meta )
2008-07-22 14:24:36 +00:00
elif xml_id :
cr . execute ( ' UPDATE ir_values set value= %s WHERE model= %s and key= %s and name= %s ' + where , ( value , model , key , name ) )
return True
2012-03-26 23:18:29 +00:00
2012-03-30 21:49:48 +00:00
def _module_data_uninstall ( self , cr , uid , ids , context = None ) :
2012-03-31 00:42:15 +00:00
""" Deletes all the records referenced by the ir.model.data entries
` ` ids ` ` along with their corresponding database backed ( including
dropping tables , columns , FKs , etc , as long as there is no other
ir . model . data entry holding a reference to them ( which indicates that
they are still owned by another module ) .
Attempts to perform the deletion in an appropriate order to maximize
the chance of gracefully deleting all records .
This step is performed as part of the full uninstallation of a module .
"""
2012-03-26 23:18:29 +00:00
if uid != 1 and not self . pool . get ( ' ir.model.access ' ) . check_groups ( cr , uid , " base.group_system " ) :
raise except_orm ( _ ( ' Permission Denied ' ) , ( _ ( ' Administrator access is required to uninstall a module ' ) ) )
context = dict ( context or { } )
context [ MODULE_UNINSTALL_FLAG ] = True # enable model/field deletion
2012-03-30 16:34:22 +00:00
ids_set = set ( ids )
2012-02-29 13:53:43 +00:00
wkf_todo = [ ]
to_unlink = [ ]
2012-03-09 12:47:53 +00:00
to_drop_table = [ ]
ids . sort ( )
ids . reverse ( )
2012-02-29 13:53:43 +00:00
for data in self . browse ( cr , uid , ids , context ) :
model = data . model
res_id = data . res_id
model_obj = self . pool . get ( model )
2012-03-31 00:42:15 +00:00
name = tools . ustr ( data . name )
2012-06-01 13:14:14 +00:00
if name . startswith ( EXT_ID_PREFIX_M2M_TABLE ) :
2012-03-31 00:42:15 +00:00
# double-check we are really going to delete all the owners of this schema element
cr . execute ( """ SELECT id from ir_model_data where name = %s and res_id IS NULL """ , ( data . name , ) )
external_ids = [ x [ 0 ] for x in cr . fetchall ( ) ]
if ( set ( external_ids ) - ids_set ) :
# as installed modules have defined this element we must not delete it!
continue
if name . startswith ( EXT_ID_PREFIX_M2M_TABLE ) :
name = name [ len ( EXT_ID_PREFIX_M2M_TABLE ) : ]
cr . execute ( " SELECT 1 FROM information_schema.tables WHERE table_name= %s " , ( name , ) )
if cr . fetchone ( ) and not name in to_drop_table :
to_drop_table . append ( name )
2012-03-09 12:47:53 +00:00
continue
2012-03-26 23:18:29 +00:00
2012-03-30 16:34:22 +00:00
pair_to_unlink = ( model , res_id )
if pair_to_unlink not in to_unlink :
to_unlink . append ( pair_to_unlink )
if model == ' workflow.activity ' :
2012-03-31 00:42:15 +00:00
# Special treatment for workflow activities: temporarily revert their
# incoming transition and trigger an update to force all workflow items
# to move out before deleting them
2012-02-29 13:53:43 +00:00
cr . execute ( ' select res_type,res_id from wkf_instance where id IN (select inst_id from wkf_workitem where act_id= %s ) ' , ( res_id , ) )
wkf_todo . extend ( cr . fetchall ( ) )
cr . execute ( " update wkf_transition set condition= ' True ' , group_id=NULL, signal=NULL,act_to=act_from,act_from= %s where act_to= %s " , ( res_id , res_id ) )
2012-03-09 12:47:53 +00:00
2012-03-31 00:42:15 +00:00
wf_service = netsvc . LocalService ( " workflow " )
2012-02-29 13:53:43 +00:00
for model , res_id in wkf_todo :
2012-03-09 12:47:53 +00:00
try :
2012-03-21 15:15:23 +00:00
wf_service . trg_write ( uid , model , res_id , cr )
2012-03-09 12:47:53 +00:00
except :
2012-03-31 00:42:15 +00:00
_logger . info ( ' Unable to force processing of workflow for item %s @ %s in order to leave activity to be deleted ' , res_id , model )
2012-03-09 12:47:53 +00:00
2012-03-31 00:42:15 +00:00
# drop m2m relation tables
for table in to_drop_table :
cr . execute ( ' DROP TABLE %s CASCADE ' % ( table ) , )
_logger . info ( ' Dropped table %s ' , table )
2012-03-26 23:18:29 +00:00
2012-03-31 21:25:15 +00:00
def unlink_if_refcount ( to_unlink ) :
for model , res_id in to_unlink :
external_ids = self . search ( cr , uid , [ ( ' model ' , ' = ' , model ) , ( ' res_id ' , ' = ' , res_id ) ] )
if ( set ( external_ids ) - ids_set ) :
# if other modules have defined this record, we must not delete it
return
_logger . info ( ' Deleting %s @ %s ' , res_id , model )
try :
self . pool . get ( model ) . unlink ( cr , uid , [ res_id ] , context = context )
except :
_logger . info ( ' Unable to delete %s @ %s ' , res_id , model , exc_info = True )
# Remove non-model records first, then model fields, and finish with models
unlink_if_refcount ( ( model , res_id ) for model , res_id in to_unlink
if model not in ( ' ir.model ' , ' ir.model.fields ' ) )
unlink_if_refcount ( ( model , res_id ) for model , res_id in to_unlink
if model == ' ir.model.fields ' )
unlink_if_refcount ( ( model , res_id ) for model , res_id in to_unlink
if model == ' ir.model ' )
2012-02-29 13:53:43 +00:00
2012-03-09 12:47:53 +00:00
cr . commit ( )
2006-12-07 13:41:40 +00:00
2008-07-22 14:24:36 +00:00
def _process_end ( self , cr , uid , modules ) :
2011-06-22 13:53:44 +00:00
""" Clear records removed from updated module data.
This method is called at the end of the module loading process .
It is meant to removed records that are no longer present in the
updated data . Such records are recognised as the one with an xml id
and a module in ir_model_data and noupdate set to false , but not
present in self . loads .
"""
2008-07-22 14:24:36 +00:00
if not modules :
return True
2011-05-31 09:24:26 +00:00
to_unlink = [ ]
2012-03-31 00:42:15 +00:00
cr . execute ( """ SELECT id,name,model,res_id,module FROM ir_model_data
WHERE module IN % s AND res_id IS NOT NULL AND noupdate = % s """ ,
( tuple ( modules ) , False ) )
for ( id , name , model , res_id , module ) in cr . fetchall ( ) :
2008-07-22 14:24:36 +00:00
if ( module , name ) not in self . loads :
2011-05-31 09:24:26 +00:00
to_unlink . append ( ( model , res_id ) )
2010-03-03 15:40:41 +00:00
if not config . get ( ' import_partial ' ) :
2011-05-31 09:24:26 +00:00
for ( model , res_id ) in to_unlink :
2008-11-25 15:19:14 +00:00
if self . pool . get ( model ) :
2012-01-24 11:47:30 +00:00
_logger . info ( ' Deleting %s @ %s ' , res_id , model )
2012-02-29 13:53:43 +00:00
self . pool . get ( model ) . unlink ( cr , uid , [ res_id ] )
2012-03-26 23:18:29 +00:00
2006-12-07 13:41:40 +00:00
2008-07-23 15:01:27 +00:00
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: