1from plain.urls import path, reverse_lazy
2
3
4class AdminViewRegistry:
5 def __init__(self):
6 # View classes that will be added to the admin automatically
7 self.registered_views = set()
8
9 def register_view(self, view=None):
10 def inner(view):
11 self.registered_views.add(view)
12 # TODO do this somewhere else...
13 # self.registered_views = set(self.registered_views, key=lambda v: v.title)
14 return view
15
16 if callable(view):
17 return inner(view)
18 else:
19 return inner
20
21 def register_viewset(self, viewset=None):
22 def inner(viewset):
23 for view in viewset.get_views():
24 self.register_view(view)
25 return viewset
26
27 if callable(viewset):
28 return inner(viewset)
29 else:
30 return inner
31
32 def get_nav_sections(self):
33 # class NavItem:
34 # def __init__(self, view, children):
35 # self.view = view
36 # self.children = children
37
38 sections = {}
39
40 for view in self.registered_views:
41 section = view.get_nav_section()
42 if not section:
43 continue
44 if section not in sections:
45 sections[section] = []
46 sections[section].append(view)
47
48 # Sort each section by nav_title
49 for section in sections.values():
50 section.sort(key=lambda v: v.get_nav_title())
51
52 # Sort sections dictionary by key
53 sections = dict(sorted(sections.items()))
54
55 return sections
56
57 # root_nav_items = []
58
59 # for view in sorted_views:
60 # if view.parent_view_class:
61 # continue
62 # children = [x for x in sorted_views if x.parent_view_class == view]
63 # root_nav_items.append(NavItem(view, children))
64
65 # return root_nav_items
66
67 def get_urls(self):
68 urls = []
69
70 paths_seen = {}
71
72 for view in self.registered_views:
73 view_path = view.get_path()
74
75 if not view_path:
76 raise ValueError(f"Path for {view} is empty")
77
78 if existing_view := paths_seen.get(view_path, None):
79 raise ValueError(
80 f"Duplicate admin path {view_path}\n{existing_view}\n{view}"
81 )
82
83 paths_seen[view_path] = view
84
85 # Append trailing slashes automatically
86 if not view_path.endswith("/"):
87 view_path += "/"
88
89 urls.append(path(f"p/{view_path}", view, name=view.view_name()))
90
91 return urls
92
93 def get_searchable_views(self):
94 views = [
95 view
96 for view in self.registered_views
97 if getattr(view, "allow_global_search", False)
98 ]
99 # Sort by slug since title isn't required by all views
100 views.sort(key=lambda v: v.get_slug())
101 return views
102
103 def get_model_detail_url(self, instance):
104 from plain.admin.views.base import URL_NAMESPACE
105 from plain.admin.views.models import AdminModelDetailView
106
107 if not getattr(instance, "pk", None):
108 # Has to actually be in the db
109 return
110
111 for view in self.registered_views:
112 if not issubclass(view, AdminModelDetailView):
113 continue
114
115 if view.model == instance.__class__:
116 return reverse_lazy(
117 f"{URL_NAMESPACE}:{view.view_name()}",
118 pk=instance.pk,
119 )
120
121
122registry = AdminViewRegistry()
123register_view = registry.register_view
124register_viewset = registry.register_viewset
125get_model_detail_url = registry.get_model_detail_url