Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[IMP] remove and uninstall modules and themes recursively #135

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
78 changes: 61 additions & 17 deletions src/util/modules.py
Original file line number Diff line number Diff line change
Expand Up @@ -98,16 +98,49 @@ def module_installed(cr, module):
return modules_installed(cr, module)


def uninstall_module(cr, module):
def module_dependencies(cr, module):
"""Get dependencies of given module.

:param str module: name of the module
:return: list names of the dependencies
:rtype: list(str)
"""
found = []
while True:
cr.execute(
"""
SELECT ARRAY_AGG(DISTINCT m.name ORDER BY m.name)
FROM ir_module_module m
INNER JOIN ir_module_module_dependency d ON d.module_id = m.id
WHERE d.name = ANY(%s)
""",
(found + [module],),
)
new = cr.fetchone()[0]
if new == found:
return found
found = new


def uninstall_module(cr, module, with_dependencies=False):
"""
Uninstall and remove all records owned by a module.

:param str module: name of the module to uninstall
:param bool with_dependencies: whether to also remove dependencies of the module
:return: set of uninstalled module names
:rtype: set(str)
"""
result = set()
cr.execute("SELECT id FROM ir_module_module WHERE name=%s", (module,))
(mod_id,) = cr.fetchone() or [None]
if not mod_id:
return
return result

if with_dependencies:
dependencies = module_dependencies(cr, module)
for dep in dependencies:
result.union(uninstall_module(cr, dep, with_dependencies=with_dependencies))

# delete constraints only owned by this module
cr.execute(
Expand Down Expand Up @@ -219,14 +252,18 @@ def uninstall_module(cr, module):
if table_exists(cr, "ir_translation"):
cr.execute("DELETE FROM ir_translation WHERE module=%s", [module])
cr.execute("UPDATE ir_module_module SET state='uninstalled' WHERE name=%s", (module,))
return result | {module}


def uninstall_theme(cr, theme, base_theme=None):
def uninstall_theme(cr, theme, base_theme=None, with_dependencies=False):
"""
Uninstall a theme module and remove it from websites.

:param str theme: name of the theme module to uninstall
:param str or None base_theme: if not `None`, unload first this base theme
:param bool with_dependencies: whether to also remove dependencies of the theme
:return: set of uninstalled module names
:rtype: set(str)

.. warning::

Expand All @@ -238,7 +275,7 @@ def uninstall_theme(cr, theme, base_theme=None):
cr.execute("SELECT id FROM ir_module_module WHERE name=%s AND state in %s", [theme, INSTALLED_MODULE_STATES])
(theme_id,) = cr.fetchone() or [None]
if not theme_id:
return
return None

env_ = env(cr)
IrModuleModule = env_["ir.module.module"]
Expand All @@ -253,17 +290,20 @@ def uninstall_theme(cr, theme, base_theme=None):
for website in websites:
IrModuleModule._theme_remove(website)
flush(env_["base"])
uninstall_module(cr, theme)
return uninstall_module(cr, theme, with_dependencies=with_dependencies)


def remove_module(cr, module):
def remove_module(cr, module, with_dependencies=False):
"""
Completely remove a module.

This operation is equivalent to uninstall and removal of *all* references to
the module - no trace of it is left in the database.

:param str module: name of the module to remove
:param bool with_dependencies: whether to also remove dependencies of the module
:return: set of uninstalled module names
:rtype: set(str)

.. warning::
Since this function removes *all* data associated to the module. Ensure to
Expand All @@ -273,15 +313,17 @@ def remove_module(cr, module):
# module need to be currently installed and running as deletions
# are made using orm.

uninstall_module(cr, module)
cr.execute("DELETE FROM ir_module_module_dependency WHERE name=%s", (module,))
cr.execute("DELETE FROM ir_module_module WHERE name=%s RETURNING id", (module,))
result = uninstall_module(cr, module, with_dependencies=with_dependencies)
names = list(result)
cr.execute("DELETE FROM ir_module_module_dependency WHERE name = ANY(%s)", (names,))
cr.execute("DELETE FROM ir_module_module WHERE name = ANY(%s) RETURNING id", (names,))
if cr.rowcount:
[mod_id] = cr.fetchone()
cr.execute("DELETE FROM ir_model_data WHERE model='ir.module.module' AND res_id=%s", [mod_id])
ids = [id_ for (id_,) in cr.fetchall()]
cr.execute("DELETE FROM ir_model_data WHERE model='ir.module.module' AND res_id = ANY(%s)", (ids,))
return result


def remove_theme(cr, theme, base_theme=None):
def remove_theme(cr, theme, base_theme=None, with_dependencies=False):
"""
Uninstall a theme module.

Expand All @@ -290,12 +332,14 @@ def remove_theme(cr, theme, base_theme=None):

See :func:`remove_module` and :func:`uninstall_theme`.
"""
uninstall_theme(cr, theme, base_theme=base_theme)
cr.execute("DELETE FROM ir_module_module_dependency WHERE name=%s", (theme,))
cr.execute("DELETE FROM ir_module_module WHERE name=%s RETURNING id", (theme,))
result = uninstall_theme(cr, theme, base_theme=base_theme, with_dependencies=with_dependencies)
themes = list(result)
cr.execute("DELETE FROM ir_module_module_dependency WHERE name = ANY(%s)", (themes,))
cr.execute("DELETE FROM ir_module_module WHERE name = ANY(%s) RETURNING id", (themes,))
if cr.rowcount:
[mod_id] = cr.fetchone()
cr.execute("DELETE FROM ir_model_data WHERE model='ir.module.module' AND res_id=%s", [mod_id])
ids = [id_ for (id_,) in cr.fetchall()]
cr.execute("DELETE FROM ir_model_data WHERE model='ir.module.module' AND res_id = ANY(%s)", (ids,))
return result


def _update_view_key(cr, old, new):
Expand Down