bfce915894eb34f066f39a0c54fdb3a8a8d1a82f
[odoo/odoo.git] / bin / addons / base / ir / ir_model.py
1 # -*- coding: utf-8 -*-
2 ##############################################################################
3 #
4 #    OpenERP, Open Source Management Solution
5 #    Copyright (C) 2004-2009 Tiny SPRL (<http://tiny.be>).
6 #
7 #    This program is free software: you can redistribute it and/or modify
8 #    it under the terms of the GNU Affero General Public License as
9 #    published by the Free Software Foundation, either version 3 of the
10 #    License, or (at your option) any later version.
11 #
12 #    This program is distributed in the hope that it will be useful,
13 #    but WITHOUT ANY WARRANTY; without even the implied warranty of
14 #    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 #    GNU Affero General Public License for more details.
16 #
17 #    You should have received a copy of the GNU Affero General Public License
18 #    along with this program.  If not, see <http://www.gnu.org/licenses/>.
19 #
20 ##############################################################################
21 import logging
22 from operator import itemgetter
23 from osv import fields,osv
24 import ir, re
25 import netsvc
26 from osv.orm import except_orm, browse_record
27
28 import time
29 import tools
30 from tools import config
31 from tools.translate import _
32 import pooler
33
34 def _get_fields_type(self, cr, uid, context=None):
35     cr.execute('select distinct ttype,ttype from ir_model_fields')
36     field_types = cr.fetchall()
37     field_types_copy = field_types
38     for types in field_types_copy:
39         if not hasattr(fields,types[0]):
40             field_types.remove(types)
41     return field_types
42
43 class ir_model(osv.osv):
44     _name = 'ir.model'
45     _description = "Objects"
46     _rec_name = 'name'
47
48     def _is_osv_memory(self, cr, uid, ids, field_name, arg, context=None):
49         models = self.browse(cr, uid, ids, context=context)
50         res = dict.fromkeys(ids)
51         for model in models:
52             res[model.id] = isinstance(self.pool.get(model.model), osv.osv_memory)
53         return res
54
55     def _search_osv_memory(self, cr, uid, model, name, domain, context=None):
56         if not domain:
57             return []
58         field, operator, value = domain[0]
59         if operator not in ['=', '!=']:
60             raise osv.except_osv('Invalid search criterions','The osv_memory field can only be compared with = and != operator.')
61         value = bool(value) if operator == '=' else not bool(value)
62         all_model_ids = self.search(cr, uid, [], context=context)
63         is_osv_mem = self._is_osv_memory(cr, uid, all_model_ids, 'osv_memory', arg=None, context=context)
64         return [('id', 'in', [id for id in is_osv_mem if bool(is_osv_mem[id]) == value])]
65
66     _columns = {
67         'name': fields.char('Object Name', size=64, translate=True, required=True),
68         'model': fields.char('Object', size=64, required=True, select=1),
69         'info': fields.text('Information'),
70         'field_id': fields.one2many('ir.model.fields', 'model_id', 'Fields', required=True),
71         'state': fields.selection([('manual','Custom Object'),('base','Base Object')],'Type',readonly=True),
72         'access_ids': fields.one2many('ir.model.access', 'model_id', 'Access'),
73         'osv_memory': fields.function(_is_osv_memory, method=True, string='In-memory model', type='boolean',
74             fnct_search=_search_osv_memory,
75             help="Indicates whether this object model lives in memory only, i.e. is not persisted (osv.osv_memory)")
76     }
77     _defaults = {
78         'model': lambda *a: 'x_',
79         'state': lambda self,cr,uid,ctx={}: (ctx and ctx.get('manual',False)) and 'manual' or 'base',
80     }
81     def _check_model_name(self, cr, uid, ids):
82         for model in self.browse(cr, uid, ids):
83             if model.state=='manual':
84                 if not model.model.startswith('x_'):
85                     return False
86             if not re.match('^[a-z_A-Z0-9.]+$',model.model):
87                 return False
88         return True
89
90     _constraints = [
91         (_check_model_name, 'The Object name must start with x_ and not contain any special character !', ['model']),
92     ]
93
94     # overridden to allow searching both on model name (model field)
95     # and model description (name field)
96     def name_search(self, cr, uid, name='', args=None, operator='ilike',  context=None, limit=None):
97         if args is None:
98             args = []
99         domain = args + ['|', ('model', operator, name), ('name', operator, name)]
100         return super(ir_model, self).name_search(cr, uid, None, domain,
101                         operator=operator, limit=limit, context=context)
102
103
104     def unlink(self, cr, user, ids, context=None):
105         for model in self.browse(cr, user, ids, context):
106             if model.state != 'manual':
107                 raise except_orm(_('Error'), _("You can not remove the model '%s' !") %(model.name,))
108         res = super(ir_model, self).unlink(cr, user, ids, context)
109         pooler.restart_pool(cr.dbname)
110         return res
111
112     def write(self, cr, user, ids, vals, context=None):
113         if context:
114             context.pop('__last_update', None)
115         return super(ir_model,self).write(cr, user, ids, vals, context)
116
117     def create(self, cr, user, vals, context=None):
118         if  context is None:
119             context = {}
120         if context and context.get('manual',False):
121             vals['state']='manual'
122         res = super(ir_model,self).create(cr, user, vals, context)
123         if vals.get('state','base')=='manual':
124             self.instanciate(cr, user, vals['model'], context)
125             self.pool.get(vals['model']).__init__(self.pool, cr)
126             ctx = context.copy()
127             ctx.update({'field_name':vals['name'],'field_state':'manual','select':vals.get('select_level','0')})
128             self.pool.get(vals['model'])._auto_init(cr, ctx)
129             #pooler.restart_pool(cr.dbname)
130         return res
131
132     def instanciate(self, cr, user, model, context={}):
133         class x_custom_model(osv.osv):
134             pass
135         x_custom_model._name = model
136         x_custom_model._module = False
137         a = x_custom_model.createInstance(self.pool, '', cr)
138         if (not a._columns) or ('x_name' in a._columns.keys()):
139             x_name = 'x_name'
140         else:
141             x_name = a._columns.keys()[0]
142         x_custom_model._rec_name = x_name
143 ir_model()
144
145
146 class ir_model_grid(osv.osv):
147     _name = 'ir.model.grid'
148     _table = 'ir_model'
149     _inherit = 'ir.model'
150     _description = "Objects Security Grid"
151
152     def create(self, cr, uid, vals, context=None):
153         raise osv.except_osv('Error !', 'You cannot add an entry to this view !')
154
155     def unlink(self, *args, **argv):
156         raise osv.except_osv('Error !', 'You cannot delete an entry of this view !')
157
158     def read(self, cr, uid, ids, fields=None, context=None, load='_classic_read'):
159         result = super(osv.osv, self).read(cr, uid, ids, fields, context, load)
160         allgr = self.pool.get('res.groups').search(cr, uid, [], context=context)
161         acc_obj = self.pool.get('ir.model.access')
162
163         if not isinstance(result,list):
164             result=[result]
165
166         for res in result:
167             rules = acc_obj.search(cr, uid, [('model_id', '=', res['id'])])
168             rules_br = acc_obj.browse(cr, uid, rules, context=context)
169             for g in allgr:
170                 res['group_'+str(g)] = ''
171             for rule in rules_br:
172                 perm_list = []
173                 if rule.perm_read:
174                     perm_list.append('r')
175                 if rule.perm_write:
176                     perm_list.append('w')
177                 if rule.perm_create:
178                     perm_list.append('c')
179                 if rule.perm_unlink:
180                     perm_list.append('u')
181                 perms = ",".join(perm_list)
182                 if rule.group_id:
183                     res['group_%d'%rule.group_id.id] = perms
184                 else:
185                     res['group_0'] = perms
186         return result
187
188     #
189     # This function do not write fields from ir.model because
190     # access rights may be different for managing models and
191     # access rights
192     #
193     def write(self, cr, uid, ids, vals, context=None):
194         vals_new = vals.copy()
195         acc_obj = self.pool.get('ir.model.access')
196         for grid in self.browse(cr, uid, ids, context=context):
197             model_id = grid.id
198             perms_rel = ['read','write','create','unlink']
199             for val in vals:
200                 if not val[:6]=='group_':
201                     continue
202                 group_id = int(val[6:]) or False
203                 rules = acc_obj.search(cr, uid, [('model_id', '=', model_id),('group_id', '=', group_id)])
204                 if not rules:
205                     rules = [acc_obj.create(cr, uid, {
206                         'name': grid.name,
207                         'model_id':model_id,
208                         'group_id':group_id
209                     }) ]
210                 vals2 = dict(map(lambda x: ('perm_'+x, x[0] in (vals[val] or '')), perms_rel))
211                 acc_obj.write(cr, uid, rules, vals2, context=context)
212         return True
213
214     def fields_get(self, cr, uid, fields=None, context=None):
215         result = super(ir_model_grid, self).fields_get(cr, uid, fields, context)
216         groups = self.pool.get('res.groups').search(cr, uid, [])
217         groups_br = self.pool.get('res.groups').browse(cr, uid, groups)
218         result['group_0'] = {'string': 'All Users','type': 'char','size': 7}
219         for group in groups_br:
220             result['group_%d'%group.id] = {'string': '%s'%group.name,'type': 'char','size': 7}
221         return result
222
223     def fields_view_get(self, cr, uid, view_id=None, view_type='form', context={}, toolbar=False, submenu=False):
224         result = super(ir_model_grid, self).fields_view_get(cr, uid, view_id, view_type, context=context, toolbar=toolbar, submenu=submenu)
225         groups = self.pool.get('res.groups').search(cr, uid, [])
226         groups_br = self.pool.get('res.groups').browse(cr, uid, groups)
227         cols = ['model', 'name']
228         xml = '''<?xml version="1.0"?>
229 <%s editable="bottom">
230     <field name="name" select="1" readonly="1" required="1"/>
231     <field name="model" select="1" readonly="1" required="1"/>
232     <field name="group_0"/>
233     ''' % (view_type,)
234         for group in groups_br:
235             xml += '''<field name="group_%d"/>''' % (group.id, )
236         xml += '''</%s>''' % (view_type,)
237         result['arch'] = xml
238         result['fields'] = self.fields_get(cr, uid, cols, context)
239         return result
240 ir_model_grid()
241
242 class ir_model_fields(osv.osv):
243     _name = 'ir.model.fields'
244     _description = "Fields"
245     _columns = {
246         'name': fields.char('Name', required=True, size=64, select=1),
247         'model': fields.char('Object Name', size=64, required=True, select=1),
248         'relation': fields.char('Object Relation', size=64),
249         'relation_field': fields.char('Relation Field', size=64),
250         'model_id': fields.many2one('ir.model', 'Object ID', required=True, select=True, ondelete='cascade'),
251         'field_description': fields.char('Field Label', required=True, size=256),
252         'ttype': fields.selection(_get_fields_type, 'Field Type',size=64, required=True),
253         'selection': fields.char('Field Selection',size=128),
254         'required': fields.boolean('Required'),
255         'readonly': fields.boolean('Readonly'),
256         'select_level': fields.selection([('0','Not Searchable'),('1','Always Searchable'),('2','Advanced Search')],'Searchable', required=True),
257         'translate': fields.boolean('Translate'),
258         'size': fields.integer('Size'),
259         'state': fields.selection([('manual','Custom Field'),('base','Base Field')],'Manually Created', required=True, readonly=True, select=1),
260         'on_delete': fields.selection([('cascade','Cascade'),('set null','Set NULL')], 'On delete', help='On delete property for many2one fields'),
261         'domain': fields.char('Domain', size=256),
262         'groups': fields.many2many('res.groups', 'ir_model_fields_group_rel', 'field_id', 'group_id', 'Groups'),
263         'view_load': fields.boolean('View Auto-Load'),
264         'selectable': fields.boolean('Selectable'),
265     }
266     _rec_name='field_description'
267     _defaults = {
268         'view_load': lambda *a: 0,
269         'selection': lambda *a: "[]",
270         'domain': lambda *a: "[]",
271         'name': lambda *a: 'x_',
272         'state': lambda self,cr,uid,ctx={}: (ctx and ctx.get('manual',False)) and 'manual' or 'base',
273         'on_delete': lambda *a: 'set null',
274         'select_level': lambda *a: '0',
275         'size': lambda *a: 64,
276         'field_description': lambda *a: '',
277         'selectable': lambda *a: 1,
278     }
279     _order = "id"
280     _sql_constraints = [
281         ('size_gt_zero', 'CHECK (size>0)', 'Size of the field can never be less than 1 !'),
282     ]
283     def unlink(self, cr, user, ids, context=None):
284         for field in self.browse(cr, user, ids, context):
285             if field.state <> 'manual':
286                 raise except_orm(_('Error'), _("You cannot remove the field '%s' !") %(field.name,))
287         #
288         # MAY BE ADD A ALTER TABLE DROP ?
289         #
290             #Removing _columns entry for that table
291             self.pool.get(field.model)._columns.pop(field.name,None)
292         return super(ir_model_fields, self).unlink(cr, user, ids, context)
293
294     def create(self, cr, user, vals, context=None):
295         if 'model_id' in vals:
296             model_data = self.pool.get('ir.model').browse(cr, user, vals['model_id'])
297             vals['model'] = model_data.model
298         if context is None:
299             context = {}
300         if context and context.get('manual',False):
301             vals['state'] = 'manual'
302         res = super(ir_model_fields,self).create(cr, user, vals, context)
303         if vals.get('state','base') == 'manual':
304             if not vals['name'].startswith('x_'):
305                 raise except_orm(_('Error'), _("Custom fields must have a name that starts with 'x_' !"))
306
307             if vals.get('relation',False) and not self.pool.get('ir.model').search(cr, user, [('model','=',vals['relation'])]):
308                  raise except_orm(_('Error'), _("Model %s Does not Exist !" % vals['relation']))
309
310             if self.pool.get(vals['model']):
311                 self.pool.get(vals['model']).__init__(self.pool, cr)
312                 #Added context to _auto_init for special treatment to custom field for select_level
313                 ctx = context.copy()
314                 ctx.update({'field_name':vals['name'],'field_state':'manual','select':vals.get('select_level','0'),'update_custom_fields':True})
315                 self.pool.get(vals['model'])._auto_init(cr, ctx)
316
317         return res
318
319 ir_model_fields()
320
321 class ir_model_access(osv.osv):
322     _name = 'ir.model.access'
323     _columns = {
324         'name': fields.char('Name', size=64, required=True, select=True),
325         'model_id': fields.many2one('ir.model', 'Object', required=True, domain=[('osv_memory','=', False)], select=True),
326         'group_id': fields.many2one('res.groups', 'Group', ondelete='cascade', select=True),
327         'perm_read': fields.boolean('Read Access'),
328         'perm_write': fields.boolean('Write Access'),
329         'perm_create': fields.boolean('Create Access'),
330         'perm_unlink': fields.boolean('Delete Permission'),
331     }
332
333     def check_groups(self, cr, uid, group):
334         res = False
335         grouparr  = group.split('.')
336         if not grouparr:
337             return False
338
339         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],))
340         return bool(cr.fetchone())
341
342     def check_group(self, cr, uid, model, mode, group_ids):
343         """ Check if a specific group has the access mode to the specified model"""
344         assert mode in ['read','write','create','unlink'], 'Invalid access mode'
345
346         if isinstance(model, browse_record):
347             assert model._table_name == 'ir.model', 'Invalid model object'
348             model_name = model.name
349         else:
350             model_name = model
351
352         if isinstance(group_ids, (int, long)):
353             group_ids = [group_ids]
354         for group_id in group_ids:
355             cr.execute("SELECT perm_" + mode + " "
356                    "  FROM ir_model_access a "
357                    "  JOIN ir_model m ON (m.id = a.model_id) "
358                    " WHERE m.model = %s AND a.group_id = %s", (model_name, group_id)
359                    )
360             r = cr.fetchone()
361             if r is None:
362                 cr.execute("SELECT perm_" + mode + " "
363                        "  FROM ir_model_access a "
364                        "  JOIN ir_model m ON (m.id = a.model_id) "
365                        " WHERE m.model = %s AND a.group_id IS NULL", (model_name, )
366                        )
367                 r = cr.fetchone()
368
369             access = bool(r and r[0])
370             if access:
371                 return True
372         # pass no groups -> no access
373         return False
374
375     def check(self, cr, uid, model, mode='read', raise_exception=True, context=None):
376         if uid==1:
377             # User root have all accesses
378             # TODO: exclude xml-rpc requests
379             return True
380
381         assert mode in ['read','write','create','unlink'], 'Invalid access mode'
382
383         if isinstance(model, browse_record):
384             assert model._table_name == 'ir.model', 'Invalid model object'
385             model_name = model.name
386         else:
387             model_name = model
388
389         # osv_memory objects can be read by everyone, as they only return
390         # results that belong to the current user (except for superuser)
391         model_obj = self.pool.get(model_name)
392         if isinstance(model_obj, osv.osv_memory):
393             return True
394
395         # We check if a specific rule exists
396         cr.execute('SELECT MAX(CASE WHEN perm_' + mode + ' THEN 1 ELSE 0 END) '
397                    '  FROM ir_model_access a '
398                    '  JOIN ir_model m ON (m.id = a.model_id) '
399                    '  JOIN res_groups_users_rel gu ON (gu.gid = a.group_id) '
400                    ' WHERE m.model = %s '
401                    '   AND gu.uid = %s '
402                    , (model_name, uid,)
403                    )
404         r = cr.fetchone()[0]
405
406         if r is None:
407             # there is no specific rule. We check the generic rule
408             cr.execute('SELECT MAX(CASE WHEN perm_' + mode + ' THEN 1 ELSE 0 END) '
409                        '  FROM ir_model_access a '
410                        '  JOIN ir_model m ON (m.id = a.model_id) '
411                        ' WHERE a.group_id IS NULL '
412                        '   AND m.model = %s '
413                        , (model_name,)
414                        )
415             r = cr.fetchone()[0]
416
417         if not r and raise_exception:
418             msgs = {
419                 'read':   _('You can not read this document! (%s)'),
420                 'write':  _('You can not write in this document! (%s)'),
421                 'create': _('You can not create this kind of document! (%s)'),
422                 'unlink': _('You can not delete this document! (%s)'),
423             }
424
425             raise except_orm(_('AccessError'), msgs[mode] % model_name )
426         return r
427
428     check = tools.cache()(check)
429
430     __cache_clearing_methods = []
431
432     def register_cache_clearing_method(self, model, method):
433         self.__cache_clearing_methods.append((model, method))
434
435     def unregister_cache_clearing_method(self, model, method):
436         try:
437             i = self.__cache_clearing_methods.index((model, method))
438             del self.__cache_clearing_methods[i]
439         except ValueError:
440             pass
441
442     def call_cache_clearing_methods(self, cr):
443         self.check.clear_cache(cr.dbname)    # clear the cache of check function
444         for model, method in self.__cache_clearing_methods:
445             getattr(self.pool.get(model), method)()
446
447     #
448     # Check rights on actions
449     #
450     def write(self, cr, uid, *args, **argv):
451         self.call_cache_clearing_methods(cr)
452         res = super(ir_model_access, self).write(cr, uid, *args, **argv)
453         return res
454
455     def create(self, cr, uid, *args, **argv):
456         self.call_cache_clearing_methods(cr)
457         res = super(ir_model_access, self).create(cr, uid, *args, **argv)
458         return res
459
460     def unlink(self, cr, uid, *args, **argv):
461         self.call_cache_clearing_methods(cr)
462         res = super(ir_model_access, self).unlink(cr, uid, *args, **argv)
463         return res
464
465 ir_model_access()
466
467 class ir_model_data(osv.osv):
468     _name = 'ir.model.data'
469     __logger = logging.getLogger('addons.base.'+_name)
470     _columns = {
471         'name': fields.char('XML Identifier', required=True, size=128, select=1),
472         'model': fields.char('Object', required=True, size=64, select=1),
473         'module': fields.char('Module', required=True, size=64, select=1),
474         'res_id': fields.integer('Resource ID', select=1),
475         'noupdate': fields.boolean('Non Updatable'),
476         'date_update': fields.datetime('Update Date'),
477         'date_init': fields.datetime('Init Date')
478     }
479     _defaults = {
480         'date_init': lambda *a: time.strftime('%Y-%m-%d %H:%M:%S'),
481         'date_update': lambda *a: time.strftime('%Y-%m-%d %H:%M:%S'),
482         'noupdate': lambda *a: False,
483         'module': lambda *a: ''
484     }
485     _sql_constraints = [
486         ('module_name_uniq', 'unique(name, module)', 'You cannot have multiple records with the same id for the same module'),
487     ]
488
489     def __init__(self, pool, cr):
490         osv.osv.__init__(self, pool, cr)
491         self.loads = {}
492         self.doinit = True
493         self.unlink_mark = {}
494
495     @tools.cache()
496     def _get_id(self, cr, uid, module, xml_id):
497         """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"""
498         ids = self.search(cr, uid, [('module','=',module), ('name','=', xml_id)])
499         if not ids:
500             raise ValueError('No references to %s.%s' % (module, xml_id))
501         # the sql constraints ensure us we have only one result
502         return ids[0]
503
504     @tools.cache()
505     def get_object_reference(self, cr, uid, module, xml_id):
506         """Returns (model, res_id) corresponding to a given module and xml_id (cached) or raise ValueError if not found"""
507         data_id = self._get_id(cr, uid, module, xml_id)
508         res = self.read(cr, uid, data_id, ['model', 'res_id'])
509         return (res['model'], res['res_id'])
510
511     def get_object(self, cr, uid, module, xml_id, context=None):
512         """Returns a browsable record for the given module name and xml_id or raise ValueError if not found"""
513         res_model, res_id = self.get_object_reference(cr, uid, module, xml_id)
514         return self.pool.get(res_model).browse(cr, uid, res_id, context=context)
515
516     def _update_dummy(self,cr, uid, model, module, xml_id=False, store=True):
517         if not xml_id:
518             return False
519         try:
520             id = self.read(cr, uid, [self._get_id(cr, uid, module, xml_id)], ['res_id'])[0]['res_id']
521             self.loads[(module,xml_id)] = (model,id)
522         except:
523             id = False
524         return id
525
526     def _update(self,cr, uid, model, module, values, xml_id=False, store=True, noupdate=False, mode='init', res_id=False, context=None):
527         warning = True
528         model_obj = self.pool.get(model)
529         if not context:
530             context = {}
531         if xml_id and ('.' in xml_id):
532             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)
533             warning = False
534             module, xml_id = xml_id.split('.')
535         if (not xml_id) and (not self.doinit):
536             return False
537         action_id = False
538
539         if xml_id:
540             cr.execute('select id,res_id from ir_model_data where module=%s and name=%s', (module,xml_id))
541             results = cr.fetchall()
542             for action_id2,res_id2 in results:
543                 cr.execute('select id from '+model_obj._table+' where id=%s', (res_id2,))
544                 result3 = cr.fetchone()
545                 if not result3:
546                     self._get_id.clear_cache(cr.dbname, uid, module, xml_id)
547                     self.get_object_reference.clear_cache(cr.dbname, uid, module, xml_id)
548                     cr.execute('delete from ir_model_data where id=%s', (action_id2,))
549                     res_id = False
550                 else:
551                     res_id,action_id = res_id2,action_id2
552
553         if action_id and res_id:
554             model_obj.write(cr, uid, [res_id], values, context=context)
555             self.write(cr, uid, [action_id], {
556                 'date_update': time.strftime('%Y-%m-%d %H:%M:%S'),
557                 },context=context)
558         elif res_id:
559             model_obj.write(cr, uid, [res_id], values, context=context)
560             if xml_id:
561                 self.create(cr, uid, {
562                     'name': xml_id,
563                     'model': model,
564                     'module':module,
565                     'res_id':res_id,
566                     'noupdate': noupdate,
567                     },context=context)
568                 if model_obj._inherits:
569                     for table in model_obj._inherits:
570                         inherit_id = model_obj.browse(cr, uid,
571                                 res_id,context=context)[model_obj._inherits[table]]
572                         self.create(cr, uid, {
573                             'name': xml_id + '_' + table.replace('.', '_'),
574                             'model': table,
575                             'module': module,
576                             'res_id': inherit_id,
577                             'noupdate': noupdate,
578                             },context=context)
579         else:
580             if mode=='init' or (mode=='update' and xml_id):
581                 res_id = model_obj.create(cr, uid, values, context=context)
582                 if xml_id:
583                     self.create(cr, uid, {
584                         'name': xml_id,
585                         'model': model,
586                         'module': module,
587                         'res_id': res_id,
588                         'noupdate': noupdate
589                         },context=context)
590                     if model_obj._inherits:
591                         for table in model_obj._inherits:
592                             inherit_id = model_obj.browse(cr, uid,
593                                     res_id,context=context)[model_obj._inherits[table]]
594                             self.create(cr, uid, {
595                                 'name': xml_id + '_' + table.replace('.', '_'),
596                                 'model': table,
597                                 'module': module,
598                                 'res_id': inherit_id,
599                                 'noupdate': noupdate,
600                                 },context=context)
601         if xml_id:
602             if res_id:
603                 self.loads[(module, xml_id)] = (model, res_id)
604                 if model_obj._inherits:
605                     for table in model_obj._inherits:
606                         inherit_field = model_obj._inherits[table]
607                         inherit_id = model_obj.read(cr, uid, res_id,
608                                 [inherit_field])[inherit_field]
609                         self.loads[(module, xml_id + '_' + \
610                                 table.replace('.', '_'))] = (table, inherit_id)
611         return res_id
612
613     def _unlink(self, cr, uid, model, res_ids):
614         for res_id in res_ids:
615             self.unlink_mark[(model, res_id)] = False
616             cr.execute('delete from ir_model_data where res_id=%s and model=%s', (res_id, model))
617         return True
618
619     def ir_set(self, cr, uid, key, key2, name, models, value, replace=True, isobject=False, meta=None, xml_id=False):
620         obj = self.pool.get('ir.values')
621         if type(models[0])==type([]) or type(models[0])==type(()):
622             model,res_id = models[0]
623         else:
624             res_id=None
625             model = models[0]
626
627         if res_id:
628             where = ' and res_id=%s' % (res_id,)
629         else:
630             where = ' and (res_id is null)'
631
632         if key2:
633             where += ' and key2=\'%s\'' % (key2,)
634         else:
635             where += ' and (key2 is null)'
636
637         cr.execute('select * from ir_values where model=%s and key=%s and name=%s'+where,(model, key, name))
638         res = cr.fetchone()
639         if not res:
640             res = ir.ir_set(cr, uid, key, key2, name, models, value, replace, isobject, meta)
641         elif xml_id:
642             cr.execute('UPDATE ir_values set value=%s WHERE model=%s and key=%s and name=%s'+where,(value, model, key, name))
643         return True
644
645     def _process_end(self, cr, uid, modules):
646         if not modules:
647             return True
648         modules = list(modules)
649         module_in = ",".join(["%s"] * len(modules))
650         cr.execute('select id,name,model,res_id,module from ir_model_data where module IN (' + module_in + ') and noupdate=%s', modules + [False])
651         wkf_todo = []
652         for (id, name, model, res_id,module) in cr.fetchall():
653             if (module,name) not in self.loads:
654                 self.unlink_mark[(model,res_id)] = id
655                 if model=='workflow.activity':
656                     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,))
657                     wkf_todo.extend(cr.fetchall())
658                     cr.execute("update wkf_transition set condition='True', role_id=NULL, signal=NULL,act_to=act_from,act_from=%s where act_to=%s", (res_id,res_id))
659                     cr.execute("delete from wkf_transition where act_to=%s", (res_id,))
660
661         for model,id in wkf_todo:
662             wf_service = netsvc.LocalService("workflow")
663             wf_service.trg_write(uid, model, id, cr)
664
665         cr.commit()
666         if not config.get('import_partial'):
667             for (model, res_id) in self.unlink_mark.keys():
668                 if self.pool.get(model):
669                     self.__logger.info('Deleting %s@%s', res_id, model)
670                     try:
671                         self.pool.get(model).unlink(cr, uid, [res_id])
672                         if id:
673                             ids = self.search(cr, uid, [('res_id','=',res_id),
674                                                         ('model','=',model)])
675                             self.__logger.debug('=> Deleting %s: %s',
676                                                 self._name, ids)
677                             if len(ids) > 1 and \
678                                self.__logger.isEnabledFor(logging.WARNING):
679                                 self.__logger.warn(
680                                     'Got %d %s for (%s, %d): %s',
681                                     len(ids), self._name, model, res_id,
682                                     map(itemgetter('module','name'),
683                                         self.read(cr, uid, ids,
684                                                   ['name', 'module'])))
685                             self.unlink(cr, uid, ids)
686                             cr.execute(
687                                 'DELETE FROM ir_values WHERE value=%s',
688                                 ('%s,%s'%(model, res_id),))
689                         cr.commit()
690                     except Exception:
691                         cr.rollback()
692                         self.__logger.exception(
693                             'Could not delete id: %d of model %s\nThere '
694                             'should be some relation that points to this '
695                             'resource\nYou should manually fix this and '
696                             'restart with --update=module', res_id, model)
697         return True
698 ir_model_data()
699
700 # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: