##############################################################################
#
-# OpenERP, Open Source Management Solution
-# Copyright (C) 2004-2009 Tiny SPRL (<http://tiny.be>).
+# OpenERP, Open Source Business Applications
+# Copyright (C) 2004-2012 OpenERP S.A. (<http://openerp.com>).
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
import time
import types
-from osv import fields,osv
-import netsvc
-from osv.orm import except_orm, browse_record
-import tools
-from tools.safe_eval import safe_eval as eval
-from tools import config
-from tools.translate import _
-import pooler
+from openerp.osv import fields,osv
+from openerp import netsvc, pooler, tools
+from openerp.osv.orm import except_orm, browse_record
+from openerp.tools.safe_eval import safe_eval as eval
+from openerp.tools import config
+from openerp.tools.translate import _
_logger = logging.getLogger(__name__)
+MODULE_UNINSTALL_FLAG = '_ir_module_uninstall'
+
def _get_fields_type(self, cr, uid, context=None):
# Avoid too many nested `if`s below, as RedHat's Python 2.6
- # break on it. See bug 939653.
+ # break on it. See bug 939653.
return sorted([(k,k) for k,v in fields.__dict__.iteritems()
if type(v) == types.TypeType and \
issubclass(v, fields._column) and \
return True
def unlink(self, cr, user, ids, context=None):
-# for model in self.browse(cr, user, ids, context):
-# if model.state != 'manual':
-# raise except_orm(_('Error'), _("You can not remove the model '%s' !") %(model.name,))
+ # 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,))
+
self._drop_table(cr, user, ids, context)
res = super(ir_model, self).unlink(cr, user, ids, context)
pooler.restart_pool(cr.dbname)
_sql_constraints = [
('size_gt_zero', 'CHECK (size>0)',_size_gt_zero_msg ),
]
-
+
def _drop_column(self, cr, uid, ids, context=None):
- field = 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)
+ 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)
return True
def unlink(self, cr, user, ids, context=None):
+ # 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!"))
+
self._drop_column(cr, user, ids, context)
res = super(ir_model_fields, self).unlink(cr, user, ids, context)
return res
# 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 = {}
-
+
self.loads = self.pool.model_data_reference_ids
def _auto_init(self, cr, context=None):
except:
id = False
return id
-
+
def unlink(self, cr, uid, ids, context=None):
""" Regular unlink method, but make sure to clear the caches. """
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
-
+
def _pre_process_unlink(self, cr, uid, ids, context=None):
+ 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
+
wkf_todo = []
to_unlink = []
to_drop_table = []
res_id = data.res_id
model_obj = self.pool.get(model)
name = data.name
+ # FIXME: replace custom keys with constants
if str(name).startswith('foreign_key_'):
name = name[12:]
- # test if constraint exists
+ # test if FK exists
cr.execute('select conname from pg_constraint where contype=%s and conname=%s',('f', name),)
if cr.fetchall():
cr.execute('ALTER TABLE "%s" DROP CONSTRAINT "%s"' % (model,name),)
- _logger.info('Drop CONSTRAINT %s@%s', name, model)
+ _logger.info('Drop FK CONSTRAINT %s@%s', name, model)
continue
-
+
if str(name).startswith('table_'):
cr.execute("SELECT table_name FROM information_schema.tables WHERE table_name='%s'"%(name[6:]))
column_name = cr.fetchone()
if column_name:
to_drop_table.append(name[6:])
continue
-
+
if str(name).startswith('constraint_'):
# test if constraint exists
cr.execute('select conname from pg_constraint where contype=%s and conname=%s',('u', name),)
cr.execute('ALTER TABLE "%s" DROP CONSTRAINT "%s"' % (model_obj._table,name[11:]),)
_logger.info('Drop CONSTRAINT %s@%s', name[11:], model)
continue
-
+
to_unlink.append((model, res_id))
if model=='workflow.activity':
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,))
# drop relation .table
for model in to_drop_table:
- cr.execute('DROP TABLE %s cascade'% (model),)
- _logger.info('Dropping table %s', model)
-
+ cr.execute('DROP TABLE %s CASCADE'% (model),)
+ _logger.info('Dropping table %s', model)
+
for (model, res_id) in to_unlink:
if model in ('ir.model','ir.model.fields', 'ir.model.data'):
continue
model_ids = self.search(cr, uid, [('model', '=', model),('res_id', '=', res_id)])
if len(model_ids) > 1:
- # if others module have defined this record, we do not delete it
+ # if other modules have defined this record, we do not delete it
continue
_logger.info('Deleting %s@%s', res_id, model)
try:
- self.pool.get(model).unlink(cr, uid, res_id)
+ self.pool.get(model).unlink(cr, uid, res_id, context=context)
except:
- _logger.info('Unable to delete %s@%s', res_id, model)
+ _logger.info('Unable to delete %s@%s', res_id, model, exc_info=True)
cr.commit()
for (model, res_id) in to_unlink:
continue
model_ids = self.search(cr, uid, [('model', '=', model),('res_id', '=', res_id)])
if len(model_ids) > 1:
- # if others module have defined this record, we do not delete it
+ # if other modules have defined this record, we do not delete it
continue
_logger.info('Deleting %s@%s', res_id, model)
- self.pool.get(model).unlink(cr, uid, res_id)
+ self.pool.get(model).unlink(cr, uid, res_id, context=context)
for (model, res_id) in to_unlink:
- if model not in ('ir.model',):
+ if model != 'ir.model':
continue
model_ids = self.search(cr, uid, [('model', '=', model),('res_id', '=', res_id)])
if len(model_ids) > 1:
- # if others module have defined this record, we do not delete it
+ # if other modules have defined this record, we do not delete it
continue
_logger.info('Deleting %s@%s', res_id, model)
- self.pool.get(model).unlink(cr, uid, [res_id])
+ self.pool.get(model).unlink(cr, uid, [res_id], context=context)
cr.commit()
def _process_end(self, cr, uid, modules):
if self.pool.get(model):
_logger.info('Deleting %s@%s', res_id, model)
self.pool.get(model).unlink(cr, uid, [res_id])
-
+
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: