Merge pull request #35067 from sokumon/remove-orphans
This commit is contained in:
commit
c22a1937b5
5 changed files with 147 additions and 0 deletions
|
|
@ -697,6 +697,7 @@ def create_desktop_icons_from_workspace():
|
|||
if w.module:
|
||||
app_name = w.app or frappe.db.get_value("Module Def", w.module, "app_name")
|
||||
if app_name in frappe.get_installed_apps():
|
||||
icon.app_name = app_name
|
||||
app_title = frappe.get_hooks("app_title", app_name=app_name)[0]
|
||||
app_icon = frappe.db.exists("Desktop Icon", {"label": app_title, "icon_type": "App"})
|
||||
if app_icon:
|
||||
|
|
@ -744,6 +745,7 @@ def create_desktop_icons_from_installed_apps():
|
|||
icon.standard = 1
|
||||
icon.idx = index
|
||||
icon.icon_type = "App"
|
||||
icon.app = a
|
||||
icon.link = app_details[0]["route"]
|
||||
icon.logo_url = app_details[0]["logo"]
|
||||
if not frappe.db.exists("Desktop Icon", [{"label": icon.label, "icon_type": icon.icon_type}]):
|
||||
|
|
|
|||
|
|
@ -127,6 +127,8 @@ class Workspace(Document):
|
|||
def on_trash(self):
|
||||
if self.public and not is_workspace_manager():
|
||||
frappe.throw(_("You need to be Workspace Manager to delete a public workspace."))
|
||||
self.delete_desktop_icon()
|
||||
self.delete_workspace_sidebar()
|
||||
self.delete_from_my_workspaces()
|
||||
|
||||
def delete_from_my_workspaces(self):
|
||||
|
|
@ -143,6 +145,25 @@ class Workspace(Document):
|
|||
if self.module and frappe.conf.developer_mode:
|
||||
delete_folder(self.module, "Workspace", self.title)
|
||||
|
||||
def delete_desktop_icon(self):
|
||||
if self.public:
|
||||
desktop_icon = frappe.get_all(
|
||||
"Desktop Icon",
|
||||
filters=[{"link_type": "Workspace"}, {"link_to": self.name}],
|
||||
limit=1,
|
||||
pluck="name",
|
||||
)
|
||||
if desktop_icon:
|
||||
frappe.delete_doc("Desktop Icon", desktop_icon[0])
|
||||
|
||||
def delete_workspace_sidebar(self):
|
||||
if self.public:
|
||||
workspace_sidebar = frappe.get_all(
|
||||
"Workspace Sidebar", filters=[{"name": self.name}], limit=1, pluck="name"
|
||||
)
|
||||
if workspace_sidebar:
|
||||
frappe.delete_doc("Workspace Sidebar", workspace_sidebar[0])
|
||||
|
||||
@staticmethod
|
||||
def get_module_wise_workspaces():
|
||||
workspaces = frappe.get_all(
|
||||
|
|
|
|||
|
|
@ -182,6 +182,9 @@ class SiteMigration:
|
|||
print("Removing orphan doctypes...")
|
||||
frappe.model.sync.remove_orphan_doctypes()
|
||||
|
||||
frappe.model.sync.remove_orphan_entities()
|
||||
frappe.model.sync.delete_duplicate_icons()
|
||||
|
||||
print("Syncing portal menu...")
|
||||
frappe.get_single("Portal Settings").sync_menu()
|
||||
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ perms will get synced only if none exist
|
|||
"""
|
||||
|
||||
import os
|
||||
import re
|
||||
|
||||
import frappe
|
||||
from frappe.cache_manager import clear_controller_cache
|
||||
|
|
@ -197,3 +198,94 @@ def remove_orphan_doctypes():
|
|||
update_progress_bar("Deleting orphaned DocTypes", i, len(orphan_doctypes))
|
||||
frappe.db.commit()
|
||||
print()
|
||||
|
||||
|
||||
def remove_orphan_entities():
|
||||
entites = ["Workspace", "Dashboard", "Page", "Report"]
|
||||
entity_filter_map = {
|
||||
"Workspace": {"public": 1},
|
||||
"Page": {"standard": "Yes"},
|
||||
"Report": {"is_standard": "Yes"},
|
||||
"Dashboard": {"is_standard": True},
|
||||
}
|
||||
entity_file_map = create_entity_file_map(entites)
|
||||
for entity in entites:
|
||||
print(f"Removing orphan {entity}s")
|
||||
all_enitities = frappe.get_all(
|
||||
entity, filters=entity_filter_map.get(entity), fields=["name", "module"]
|
||||
)
|
||||
for i, w in enumerate(all_enitities):
|
||||
try:
|
||||
entity_file_map[entity][w.name]
|
||||
except KeyError:
|
||||
try:
|
||||
print(f"Deleting entity {entity} {w.name}")
|
||||
frappe.delete_doc(entity, w.name, force=True, ignore_missing=True)
|
||||
update_progress_bar(f"Deleting orphaned {entity}", i, len(all_enitities))
|
||||
print()
|
||||
|
||||
except Exception as e:
|
||||
print(f"Error occurred while deleting entity: {entity} {w.name}")
|
||||
print(e)
|
||||
# save the deleted icons
|
||||
frappe.db.commit() # nosemgrep
|
||||
|
||||
|
||||
def create_entity_file_map(entities):
|
||||
import glob
|
||||
|
||||
from frappe.modules.import_file import read_doc_from_file
|
||||
|
||||
entity_file_map = {}
|
||||
for entity in entities:
|
||||
entity_file_map[entity] = {}
|
||||
for app in frappe.get_installed_apps():
|
||||
app_path = frappe.get_app_path(app)
|
||||
for entity in entities:
|
||||
entity_folder = entity.lower()
|
||||
if entity.lower() == "dashboard":
|
||||
entity_folder = f"*_{entity_folder}"
|
||||
entity_files = list(glob.glob(f"{app_path}/**/{entity_folder}/**/*.json", recursive=True))
|
||||
for file in entity_files:
|
||||
entity_json = read_doc_from_file(file)
|
||||
if isinstance(entity_json, dict):
|
||||
entity_file_map[entity][entity_json.get("name")] = file
|
||||
elif isinstance(entity_json, list):
|
||||
if len(entity_json) > 0:
|
||||
entity_file_map[entity][entity_json[0].get("name")] = file
|
||||
|
||||
return entity_file_map
|
||||
|
||||
|
||||
def check_if_record_exists(type=None, path=None, entity_type=None, name=None, module_name=None):
|
||||
scrubbed_name = frappe.scrub(name.lower())
|
||||
scrubbed_entity_type = frappe.scrub(entity_type.lower())
|
||||
if scrubbed_entity_type == "dashboard" and module_name:
|
||||
scrubbed_entity_type = f"{frappe.scrub(module_name.lower())}_dashboard"
|
||||
|
||||
def build_path(entity_name):
|
||||
if type == "app":
|
||||
return os.path.join(path, scrubbed_entity_type, f"{entity_name}.json")
|
||||
return os.path.join(path, scrubbed_entity_type, entity_name, f"{entity_name}.json")
|
||||
|
||||
entity_path = build_path(scrubbed_name)
|
||||
if os.path.exists(entity_path):
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
|
||||
def delete_duplicate_icons():
|
||||
# This handles app icons which are renamed. Removes the old entry from db.
|
||||
for app in frappe.get_installed_apps():
|
||||
icons = frappe.get_all("Desktop Icon", filters=[{"icon_type": "App"}, {"app": app}], pluck="name")
|
||||
|
||||
if len(icons) > 1:
|
||||
for i in icons:
|
||||
app_path = frappe.get_app_path(app)
|
||||
if not check_if_record_exists(type="app", path=app_path, entity_type="Desktop Icon", name=i):
|
||||
print(f"Deleting icon {i}")
|
||||
frappe.delete_doc("Desktop Icon", i)
|
||||
|
||||
# save the deleted icons
|
||||
frappe.db.commit() # nosemgrep
|
||||
|
|
|
|||
29
frappe/tests/test_removing_orphans.py
Normal file
29
frappe/tests/test_removing_orphans.py
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
import frappe
|
||||
from frappe.model.sync import remove_orphan_entities
|
||||
from frappe.modules.export_file import delete_folder
|
||||
from frappe.tests import IntegrationTestCase
|
||||
|
||||
|
||||
class TestRemovingOrphans(IntegrationTestCase):
|
||||
def test_removing_orphan(self):
|
||||
_before = frappe.conf.developer_mode
|
||||
frappe.conf.developer_mode = True
|
||||
# Create a new report
|
||||
report = frappe.new_doc("Report")
|
||||
args = {
|
||||
"doctype": "Report",
|
||||
"report_name": "Orphan Report",
|
||||
"ref_doctype": "DocType",
|
||||
"is_standard": "Yes",
|
||||
"module": "Custom",
|
||||
}
|
||||
report.update(args)
|
||||
report.save()
|
||||
print(f"Created report: {report.name}")
|
||||
# delete only fixture (emulating that the export/entity is deleted by the developer)
|
||||
delete_folder("Custom", "Report", report.name)
|
||||
self.assertTrue(frappe.db.exists("Report", report.name))
|
||||
if frappe.db.exists("Report", report.name):
|
||||
remove_orphan_entities()
|
||||
self.assertFalse(frappe.db.exists("Report", report.name))
|
||||
frappe.conf.developer_mode = _before
|
||||
Loading…
Add table
Reference in a new issue