diff --git a/frappe/__init__.py b/frappe/__init__.py index defa6e3336..96aa2b1b5f 100644 --- a/frappe/__init__.py +++ b/frappe/__init__.py @@ -1203,7 +1203,7 @@ def read_file(path, raise_not_found=False): def get_attr(method_string): """Get python method object from its name.""" app_name = method_string.split(".")[0] - if not local.flags.in_install and app_name not in get_installed_apps(): + if not local.flags.in_uninstall and not local.flags.in_install and app_name not in get_installed_apps(): throw(_("App {0} is not installed").format(app_name), AppNotInstalledError) modulename = '.'.join(method_string.split('.')[:-1]) diff --git a/frappe/installer.py b/frappe/installer.py index cd6526c788..b50fa4a3b5 100755 --- a/frappe/installer.py +++ b/frappe/installer.py @@ -208,6 +208,7 @@ def remove_app(app_name, dry_run=False, yes=False, no_backup=False, force=False) import click site = frappe.local.site + app_hooks = frappe.get_hooks(app_name=app_name) # dont allow uninstall app if not installed unless forced if not force: @@ -233,6 +234,9 @@ def remove_app(app_name, dry_run=False, yes=False, no_backup=False, force=False) frappe.flags.in_uninstall = True + for before_uninstall in app_hooks.before_uninstall or []: + frappe.get_attr(before_uninstall)() + modules = frappe.get_all("Module Def", filters={"app_name": app_name}, pluck="name") drop_doctypes = _delete_modules(modules, dry_run=dry_run) @@ -243,6 +247,9 @@ def remove_app(app_name, dry_run=False, yes=False, no_backup=False, force=False) frappe.get_single('Installed Applications').update_versions() frappe.db.commit() + for after_uninstall in app_hooks.after_uninstall or []: + frappe.get_attr(after_uninstall)() + click.secho(f"Uninstalled App {app_name} from Site {site}", fg="green") frappe.flags.in_uninstall = False diff --git a/frappe/utils/boilerplate.py b/frappe/utils/boilerplate.py index 91f7dbb2f8..6c405ce467 100755 --- a/frappe/utils/boilerplate.py +++ b/frappe/utils/boilerplate.py @@ -203,6 +203,12 @@ app_license = "{app_license}" # before_install = "{app_name}.install.before_install" # after_install = "{app_name}.install.after_install" +# Uninstallation +# ------------ + +# before_uninstall = "{app_name}.uninstall.before_uninstall" +# after_uninstall = "{app_name}.uninstall.after_uninstall" + # Desk Notifications # ------------------ # See frappe.core.notifications.get_notification_config