[FIX] module: fix dependency computation to work recursively
bzr revid: odo@openerp.com-20120326231839-q7x3zr1bgenom3pc
This commit is contained in:
parent
8bd0cf42f3
commit
c6f72008ed
|
@ -2,8 +2,7 @@
|
||||||
##############################################################################
|
##############################################################################
|
||||||
#
|
#
|
||||||
# OpenERP, Open Source Management Solution
|
# OpenERP, Open Source Management Solution
|
||||||
# Copyright (C) 2004-2009 Tiny SPRL (<http://tiny.be>).
|
# Copyright (C) 2004-2012 OpenERP S.A. (<http://openerp.com>).
|
||||||
# Copyright (C) 2010 OpenERP s.a. (<http://openerp.com>).
|
|
||||||
#
|
#
|
||||||
# This program is free software: you can redistribute it and/or modify
|
# This program is free software: you can redistribute it and/or modify
|
||||||
# it under the terms of the GNU Affero General Public License as
|
# it under the terms of the GNU Affero General Public License as
|
||||||
|
@ -113,7 +112,7 @@ class module(osv.osv):
|
||||||
if field_name is None or 'menus_by_module' in field_name:
|
if field_name is None or 'menus_by_module' in field_name:
|
||||||
dmodels.append('ir.ui.menu')
|
dmodels.append('ir.ui.menu')
|
||||||
assert dmodels, "no models for %s" % field_name
|
assert dmodels, "no models for %s" % field_name
|
||||||
|
|
||||||
for module_rec in self.browse(cr, uid, ids, context=context):
|
for module_rec in self.browse(cr, uid, ids, context=context):
|
||||||
res[module_rec.id] = {
|
res[module_rec.id] = {
|
||||||
'menus_by_module': [],
|
'menus_by_module': [],
|
||||||
|
@ -174,7 +173,7 @@ class module(osv.osv):
|
||||||
# installed_version refer the latest version (the one on disk)
|
# installed_version refer the latest version (the one on disk)
|
||||||
# latest_version refer the installed version (the one in database)
|
# latest_version refer the installed version (the one in database)
|
||||||
# published_version refer the version available on the repository
|
# published_version refer the version available on the repository
|
||||||
'installed_version': fields.function(_get_latest_version,
|
'installed_version': fields.function(_get_latest_version,
|
||||||
string='Latest version', type='char'),
|
string='Latest version', type='char'),
|
||||||
'latest_version': fields.char('Installed version', size=64, readonly=True),
|
'latest_version': fields.char('Installed version', size=64, readonly=True),
|
||||||
'published_version': fields.char('Published Version', size=64, readonly=True),
|
'published_version': fields.char('Published Version', size=64, readonly=True),
|
||||||
|
@ -364,46 +363,50 @@ class module(osv.osv):
|
||||||
def button_install_cancel(self, cr, uid, ids, context=None):
|
def button_install_cancel(self, cr, uid, ids, context=None):
|
||||||
self.write(cr, uid, ids, {'state': 'uninstalled', 'demo':False})
|
self.write(cr, uid, ids, {'state': 'uninstalled', 'demo':False})
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def module_uninstall(self, cr, uid, ids, context=None):
|
def module_uninstall(self, cr, uid, ids, context=None):
|
||||||
|
# uninstall must be done respecting the reverse-dependency order
|
||||||
# you have to uninstall in the right order, not all modules at the same time
|
ir_model_data = self.pool.get('ir.model.data')
|
||||||
|
modules_to_remove = [m.name for m in self.browse(cr, uid, ids, context)]
|
||||||
model_data = self.pool.get('ir.model.data')
|
data_ids = ir_model_data.search(cr, uid, [('module', 'in', modules_to_remove)])
|
||||||
remove_modules = map(lambda x: x.name, self.browse(cr, uid, ids, context))
|
ir_model_data._pre_process_unlink(cr, uid, data_ids, context)
|
||||||
|
ir_model_data.unlink(cr, uid, data_ids, context)
|
||||||
data_ids = model_data.search(cr, uid, [('module', 'in', remove_modules)])
|
|
||||||
|
|
||||||
model_data._pre_process_unlink(cr, uid, data_ids, context)
|
|
||||||
model_data.unlink(cr, uid, data_ids, context)
|
|
||||||
|
|
||||||
self.write(cr, uid, ids, {'state': 'uninstalled'})
|
self.write(cr, uid, ids, {'state': 'uninstalled'})
|
||||||
|
|
||||||
# should we call process_end istead of loading, or both ?
|
# should we call process_end instead of loading, or both ?
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def check_dependancy(self, cr, uid, ids, context):
|
def downstream_dependencies(self, cr, uid, ids, known_dep_ids=None,
|
||||||
res = []
|
exclude_states=['uninstalled','uninstallable','to remove'],
|
||||||
for module in self.browse(cr, uid, ids):
|
context=None):
|
||||||
cr.execute('''select m.id
|
"""Return the ids of all modules that directly or indirectly depend
|
||||||
from
|
on the given module `ids`, and that satisfy the `exclude_states`
|
||||||
ir_module_module_dependency d
|
filter"""
|
||||||
join
|
if not ids: return []
|
||||||
ir_module_module m on (d.module_id=m.id)
|
known_dep_ids = set(known_dep_ids or [])
|
||||||
where
|
cr.execute('''SELECT DISTINCT m.id
|
||||||
d.name=%s and
|
FROM
|
||||||
m.state not in ('uninstalled','uninstallable','to remove')''', (module.name,))
|
ir_module_module_dependency d
|
||||||
res = cr.fetchall()
|
JOIN
|
||||||
return res
|
ir_module_module m ON (d.module_id=m.id)
|
||||||
|
WHERE
|
||||||
|
d.name IN (SELECT name from ir_module_module where id in %s) AND
|
||||||
|
m.state NOT IN %s AND
|
||||||
|
m.id NOT IN %s ''',
|
||||||
|
(tuple(ids),tuple(exclude_states), tuple(known_dep_ids or ids)))
|
||||||
|
new_dep_ids = set([m[0] for m in cr.fetchall()])
|
||||||
|
missing_mod_ids = new_dep_ids - known_dep_ids
|
||||||
|
known_dep_ids |= new_dep_ids
|
||||||
|
if missing_mod_ids:
|
||||||
|
known_dep_ids |= set(self.downstream_dependencies(cr, uid, list(missing_mod_ids),
|
||||||
|
known_dep_ids, exclude_states,context))
|
||||||
|
return list(known_dep_ids)
|
||||||
|
|
||||||
def button_uninstall(self, cr, uid, ids, context=None):
|
def button_uninstall(self, cr, uid, ids, context=None):
|
||||||
res = self.check_dependancy(cr, uid, ids, context)
|
if any(m.name == 'base' for m in self.browse(cr, uid, ids)):
|
||||||
for i in range(0,len(res)):
|
raise orm.except_orm(_('Error'), _("The `base` module cannot be uninstalled"))
|
||||||
res_depend = self.check_dependancy(cr, uid, [res[i][0]], context)
|
dep_ids = self.downstream_dependencies(cr, uid, ids, context=context)
|
||||||
for j in range(0,len(res_depend)):
|
self.write(cr, uid, ids + dep_ids, {'state': 'to remove'})
|
||||||
ids.append(res_depend[j][0])
|
|
||||||
ids.append(res[i][0])
|
|
||||||
self.write(cr, uid, ids, {'state': 'to remove'})
|
|
||||||
return dict(ACTION_DICT, name=_('Uninstall'))
|
return dict(ACTION_DICT, name=_('Uninstall'))
|
||||||
|
|
||||||
def button_uninstall_cancel(self, cr, uid, ids, context=None):
|
def button_uninstall_cancel(self, cr, uid, ids, context=None):
|
||||||
|
@ -486,7 +489,7 @@ class module(osv.osv):
|
||||||
updated_values = {}
|
updated_values = {}
|
||||||
for key in values:
|
for key in values:
|
||||||
old = getattr(mod, key)
|
old = getattr(mod, key)
|
||||||
updated = isinstance(values[key], basestring) and tools.ustr(values[key]) or values[key]
|
updated = isinstance(values[key], basestring) and tools.ustr(values[key]) or values[key]
|
||||||
if not old == updated:
|
if not old == updated:
|
||||||
updated_values[key] = values[key]
|
updated_values[key] = values[key]
|
||||||
if terp.get('installable', True) and mod.state == 'uninstallable':
|
if terp.get('installable', True) and mod.state == 'uninstallable':
|
||||||
|
|
Loading…
Reference in New Issue