Small modifs
[odoo/odoo.git] / bin / addons / base / ir / ir_model.py
1 ##############################################################################
2 #
3 # Copyright (c) 2004-2006 TINY SPRL. (http://tiny.be) All Rights Reserved.
4 #
5 # WARNING: This program as such is intended to be used by professional
6 # programmers who take the whole responsability of assessing all potential
7 # consequences resulting from its eventual inadequacies and bugs
8 # End users who are looking for a ready-to-use solution with commercial
9 # garantees and support are strongly adviced to contract a Free Software
10 # Service Company
11 #
12 # This program is Free Software; you can redistribute it and/or
13 # modify it under the terms of the GNU General Public License
14 # as published by the Free Software Foundation; either version 2
15 # of the License, or (at your option) any later version.
16 #
17 # This program is distributed in the hope that it will be useful,
18 # but WITHOUT ANY WARRANTY; without even the implied warranty of
19 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
20 # GNU General Public License for more details.
21 #
22 # You should have received a copy of the GNU General Public License
23 # along with this program; if not, write to the Free Software
24 # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
25 #
26 ##############################################################################
27
28 from osv import fields,osv
29 import ir
30 import netsvc
31
32 import time
33 import tools
34 import pooler
35
36 class ir_model(osv.osv):
37         _name = 'ir.model'
38         _rec_name = 'model'
39         _columns = {
40                 'name': fields.char('Model name', size=64, translate=True),
41                 'model': fields.char('Object name', size=64, required=True),
42                 'info': fields.text('Information'),
43                 'field_id': fields.one2many('ir.model.fields', 'model_id', 'Fields', required=True),
44         }
45         _defaults = {
46                 'name': lambda *a: 'No Name',
47         }
48 ir_model()
49
50 class ir_model_fields(osv.osv):
51         _name = 'ir.model.fields'
52         _columns = {
53                 'name': fields.char('Name', size=64),
54                 'model': fields.char('Model Name', size=64, required=True),
55 # on pourrait egalement changer ca en many2one, mais le prob c'est qu'alors faut
56 # faire une jointure a chaque fois qu'on recherche vu que le client ne connait que le nom
57 # de l'objet et pas son id
58                 'relation': fields.char('Model Relation', size=64),
59                 'model_id': fields.many2one('ir.model', 'Model id', required=True, select=True),
60 # in fact, this is the field label
61                 'field_description': fields.char('Field Description', size=256),
62                 'ttype': fields.char('Field Type', size=64),
63                 'relate': fields.boolean('Click and Relate'),
64
65                 'groups': fields.many2many('res.groups', 'ir_model_fields_group_rel', 'field_id', 'group_id', 'Groups'),
66                 'group_name': fields.char('Group Name', size=128),
67                 'view_load': fields.boolean('View Auto-Load'),
68         }
69         _defaults = {
70                 'relate': lambda *a: 0,
71                 'view_load': lambda *a: 0,
72                 'name': lambda *a: 'No Name',
73                 'field_description': lambda *a: 'No description available',
74         }
75         _order = "id"
76 ir_model_fields()
77
78 class ir_model_access(osv.osv):
79         _name = 'ir.model.access'
80         _columns = {
81                 'name': fields.char('Name', size=64, required=True),
82                 'model_id': fields.many2one('ir.model', 'Model', required=True),
83                 'group_id': fields.many2one('res.groups', 'Group'),
84                 'perm_read': fields.boolean('Read Access'),
85                 'perm_write': fields.boolean('Write Access'),
86                 'perm_create': fields.boolean('Create Access'),
87                 'perm_unlink': fields.boolean('Delete Permission'),
88         }
89         def check(self, cr, uid, model_name, mode='read'):
90                 return True
91                 assert mode in ['read','write','create','unlink'], 'Invalid access mode for security'
92                 if uid==1:
93                         return True
94                 cr.execute('select bool_or(perm_'+mode+') from ir_model_access a join ir_model m on (a.model_id=m.id) join res_groups_users_rel gu on (gu.gid = a.group_id) where m.model= %s and gu.uid= %s',(model_name,uid,))
95                 r= cr.fetchall()
96                 if r[0][0] == None:
97                         cr.execute(' select bool_or(perm_'+mode+') from ir_model_access a join ir_model m on (a.model_id=m.id) where a.group_id is null and m.model=%s',(model_name,))
98                         r= cr.fetchall()
99                         if r[0][0] == None : return True
100
101                 if not r[0][0]:
102                         raise osv.except_osv('Access denied !', 'You can not %s this resource !' % mode)
103                 return True
104                 
105         check = tools.cache()(check)
106
107         #
108         # Methods to clean the cache on the Check Method.
109         #
110         def write(self, cr, uid, *args, **argv):
111                 res = super(ir_model_access, self).write(cr, uid, *args, **argv)
112                 self.check()
113                 return res
114         def create(self, cr, uid, *args, **argv):
115                 res = super(ir_model_access, self).create(cr, uid, *args, **argv)
116                 self.check()
117                 return res
118         def unlink(self, cr, uid, *args, **argv):
119                 res = super(ir_model_access, self).unlink(cr, uid, *args, **argv)
120                 self.check()
121                 return res
122 ir_model_access()
123
124 class ir_model_data(osv.osv):
125         _name = 'ir.model.data'
126         _columns = {
127                 'name': fields.char('XML Identifier', required=True, size=64),
128                 'model': fields.char('Model', required=True, size=64),
129                 'module': fields.char('Module', required=True, size=64),
130                 'res_id': fields.integer('Resource ID'),
131                 'noupdate': fields.boolean('Non Updatable'),
132                 'date_update': fields.datetime('Update Date'),
133                 'date_init': fields.datetime('Init Date')
134         }
135         _defaults = {
136                 'date_init': lambda *a: time.strftime('%Y-%m-%d %H:%M:%S'),
137                 'date_update': lambda *a: time.strftime('%Y-%m-%d %H:%M:%S'),
138                 'noupdate': lambda *a: False
139         }
140
141         def __init__(self, pool):
142                 osv.osv.__init__(self, pool)
143                 self.loads = {}
144                 self.doinit = True
145                 self.unlink_mark = {}
146
147         def _get_id(self,cr, uid, module, xml_id):
148                 ids = self.search(cr, uid, [('module','=',module),('name','=', xml_id)])
149                 assert len(ids)==1, '%d reference(s) to %s. You should have only one !' % (len(ids),xml_id)
150                 return ids[0]
151
152         def _update_dummy(self,cr, uid, model, module, xml_id=False, store=True):
153                 if not xml_id:
154                         return False
155                 try:
156                         id = self.read(cr, uid, [self._get_id(cr, uid, module, xml_id)], ['res_id'])[0]['res_id']
157                         self.loads[(module,xml_id)] = (model,id)
158                 except:
159                         id = False
160                 return id
161
162         def _update(self,cr, uid, model, module, values, xml_id=False, store=True, noupdate=False, mode='init', res_id=False):
163                 warning = True
164                 if xml_id and ('.' in xml_id):
165                         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)
166                         warning = False
167                         module, xml_id = xml_id.split('.')
168                 if (not xml_id) and (not self.doinit):
169                         return False
170                 action_id = False
171                 if xml_id:
172                         cr.execute('select id,res_id from ir_model_data where module=%s and name=%s', (module,xml_id))
173                         results = cr.fetchall()
174                         for action_id2,res_id2 in results:
175                                 cr.execute('select id from '+self.pool.get(model)._table+' where id=%d', (res_id2,))
176                                 result3 = cr.fetchone()
177                                 if not result3:
178                                         cr.execute('delete from ir_model_data where id=%d', (action_id2,))
179                                 else:
180                                         res_id,action_id = res_id2,action_id2
181
182                 if action_id and res_id:
183                         self.pool.get(model).write(cr, uid, [res_id], values)
184                         self.write(cr, uid, [action_id], {'date_update': time.strftime('%Y-%m-%d %H:%M:%S')})
185                 elif res_id:
186                         self.pool.get(model).write(cr, uid, [res_id], values)
187                         if xml_id:
188                                 self.create(cr, uid, {'name':xml_id, 'model':model, 'module':module, 'res_id':res_id, 'noupdate':noupdate})
189                 else:
190                         if mode=='init' or (mode=='update' and xml_id):
191                                 res_id = self.pool.get(model).create(cr, uid, values)
192                                 if xml_id:
193                                         self.create(cr, uid, {'name':xml_id, 'model':model, 'module':module, 'res_id':res_id, 'noupdate':noupdate})
194                 if xml_id:
195                         if warning:
196                                 assert (module,xml_id) not in self.loads, "id '%s' is already defined in module '%s' !" % (xml_id, module)
197                         if res_id:
198                                 self.loads[(module,xml_id)] = (model, res_id)
199                 return res_id
200
201         def _unlink(self, cr, uid, model, ids, direct=False):
202                 #self.pool.get(model).unlink(cr, uid, ids)
203                 for id in ids:
204                         self.unlink_mark[(model, id)]=False
205                         cr.execute('delete from ir_model_data where res_id=%d and model=\'%s\'', (id,model))
206                 return True
207
208         def ir_set(self, cr, uid, key, key2, name, models, value, replace=True, isobject=False, meta=None):
209                 obj = self.pool.get('ir.values')
210                 if type(models[0])==type([]) or type(models[0])==type(()):
211                         model,res_id = models[0]
212                 else:
213                         res_id=None
214                         model = models[0]
215
216                 if res_id:
217                         where = ' and res_id=%d' % (res_id,)
218                 else:
219                         where = ' and (res_id is null)'
220
221                 if key2:
222                         where += ' and key2=\'%s\'' % (key2,)
223                 else:
224                         where += ' and (key2 is null)'
225
226                 cr.execute('select * from ir_values where model=%s and key=%s and name=%s'+where,(model, key, name))
227                 res = cr.fetchone()
228                 if not res:
229                         res = ir.ir_set(cr, uid, key, key2, name, models, value, replace, isobject, meta)
230                 return True
231
232         def _process_end(self, cr, uid, modules):
233                 if not modules:
234                         return True
235                 module_str = ["'%s'" % m for m in modules]
236                 cr.execute('select id,name,model,res_id,module from ir_model_data where module in ('+','.join(module_str)+') and not noupdate')
237                 wkf_todo = []
238                 for (id, name, model, res_id,module) in cr.fetchall():
239                         if (module,name) not in self.loads:
240                                 self.unlink_mark[(model,res_id)] = id
241                                 if model=='workflow.activity':
242                                         cr.execute('select res_type,res_id from wkf_instance where id in (select inst_id from wkf_workitem where act_id=%d)', (res_id,))
243                                         wkf_todo.extend(cr.fetchall())
244                                         cr.execute("update wkf_transition set condition='True', role_id=NULL, signal=NULL,act_to=act_from,act_from=%d where act_to=%d", (res_id,res_id))
245                                         cr.execute("delete from wkf_transition where act_to=%d", (res_id,))
246
247                 for model,id in wkf_todo:
248                         wf_service = netsvc.LocalService("workflow")
249                         wf_service.trg_write(uid, model, id, cr)
250
251                 cr.commit()
252                 for (model,id) in self.unlink_mark.keys():
253                         if self.pool.get(model):
254                                 logger = netsvc.Logger()
255                                 logger.notifyChannel('init', netsvc.LOG_INFO, 'Deleting %s@%s' % (id, model))
256                                 try:
257                                         self.pool.get(model).unlink(cr, uid, [id])
258                                         if self.unlink_mark[(model,id)]:
259                                                 self.unlink(cr, uid, [self.unlink_mark[(model,id)]])
260                                         cr.commit()
261                                 except:
262                                         logger.notifyChannel('init', netsvc.LOG_ERROR, 'Could not delete id: %d of model %s\tThere should be some relation that points to this resource\tYou should manually fix this and restart --update=module' % (id, model))
263                 return True
264 ir_model_data()
265