33from django_smartbase_admin .engine .menu_item import SBAdminMenuItem
44
55
6+ class MenuView :
7+ def __init__ (self , view_id , * , url = "/view/" , module_allowed = True , perms = None ):
8+ self .view_id = view_id
9+ self .url = url
10+ self .module_allowed = module_allowed
11+ self .perms = perms or {"view" : True }
12+ self .permission_check_count = 0
13+
14+ def get_id (self ):
15+ return self .view_id
16+
17+ def get_menu_label (self ):
18+ return self .view_id
19+
20+ def get_menu_view_url (self , request ):
21+ return self .url
22+
23+ def has_module_permission (self , request ):
24+ return self .module_allowed
25+
26+ def get_model_perms (self , request ):
27+ self .permission_check_count += 1
28+ return self .perms
29+
30+ def has_view_permission (self , request ):
31+ self .permission_check_count += 1
32+ return self .perms .get ("view" , False )
33+
34+ def has_change_permission (self , request ):
35+ self .permission_check_count += 1
36+ return self .perms .get ("change" , False )
37+
38+ def has_add_permission (self , request ):
39+ self .permission_check_count += 1
40+ return self .perms .get ("add" , False )
41+
42+ def has_delete_permission (self , request ):
43+ self .permission_check_count += 1
44+ return self .perms .get ("delete" , False )
45+
46+
47+ class MenuPermissionView (MenuView ):
48+ def __init__ (self , view_id , * , menu_allowed ):
49+ super ().__init__ (view_id , module_allowed = False )
50+ self .menu_allowed = menu_allowed
51+
52+ def has_menu_permission (self , request ):
53+ return self .menu_allowed
54+
55+
56+ def menu_item_for_view (view ):
57+ item = SBAdminMenuItem (view_id = view .get_id ())
58+ item .view = view
59+ return item
60+
61+
662def test_menu_item_serializes_css_class ():
763 item = SBAdminMenuItem (
864 label = "Feedback" ,
@@ -14,3 +70,99 @@ def test_menu_item_serializes_css_class():
1470
1571 assert active is False
1672 assert serialized ["get_css_class" ] == "feedback-menu-item"
73+
74+
75+ def test_menu_item_without_permissions_is_not_serialized ():
76+ view = MenuView (
77+ "hidden" ,
78+ perms = {"add" : False , "change" : False , "delete" : False , "view" : False },
79+ )
80+ item = menu_item_for_view (view )
81+
82+ serialized , active = item .process_and_serialize (
83+ SimpleNamespace (), SimpleNamespace (view = None )
84+ )
85+
86+ assert serialized is None
87+ assert active is False
88+
89+
90+ def test_parent_menu_item_keeps_only_permitted_children ():
91+ visible_view = MenuView ("visible" , url = "/visible/" )
92+ hidden_view = MenuView (
93+ "hidden" ,
94+ perms = {"add" : False , "change" : False , "delete" : False , "view" : False },
95+ )
96+ parent = SBAdminMenuItem (
97+ label = "Settings" ,
98+ sub_items = [
99+ menu_item_for_view (visible_view ),
100+ menu_item_for_view (hidden_view ),
101+ ],
102+ )
103+
104+ serialized , active = parent .process_and_serialize (
105+ SimpleNamespace (), SimpleNamespace (view = "visible" )
106+ )
107+
108+ assert active is True
109+ assert len (serialized ["sub_items" ]) == 1
110+ assert serialized ["sub_items" ][0 ]["get_id" ] == "visible"
111+
112+
113+ def test_parent_menu_item_with_denied_view_uses_first_child_url ():
114+ parent_view = MenuView (
115+ "parent" ,
116+ url = "/parent/" ,
117+ perms = {"add" : False , "change" : False , "delete" : False , "view" : False },
118+ )
119+ child_view = MenuView ("child" , url = "/child/" )
120+ parent = menu_item_for_view (parent_view )
121+ parent .sub_items = [menu_item_for_view (child_view )]
122+
123+ serialized , active = parent .process_and_serialize (
124+ SimpleNamespace (), SimpleNamespace (view = None )
125+ )
126+
127+ assert active is False
128+ assert serialized ["get_url" ] == ""
129+ assert serialized ["sub_items" ][0 ]["get_url" ] == "/child/"
130+
131+
132+ def test_menu_permission_is_cached_per_request ():
133+ view = MenuView ("cached" )
134+ request_data = SimpleNamespace (view = None )
135+
136+ request = SimpleNamespace ()
137+
138+ menu_item_for_view (view ).process_and_serialize (request , request_data )
139+ menu_item_for_view (view ).process_and_serialize (request , request_data )
140+
141+ assert view .permission_check_count == 1
142+
143+
144+ def test_menu_permission_hook_overrides_model_permission_checks ():
145+ view = MenuPermissionView ("menu-visible" , menu_allowed = True )
146+ item = menu_item_for_view (view )
147+
148+ serialized , active = item .process_and_serialize (
149+ SimpleNamespace (), SimpleNamespace (view = None )
150+ )
151+
152+ assert active is False
153+ assert serialized ["get_id" ] == "menu-visible"
154+ assert view .permission_check_count == 0
155+
156+
157+ def test_menu_item_uses_selected_view_menu_highlight_id_for_active_state ():
158+ menu_view = MenuView ("connector" )
159+ item = menu_item_for_view (menu_view )
160+ request_data = SimpleNamespace (
161+ view = "connector_orders" ,
162+ selected_view = SimpleNamespace (menu_highlight_view_id = "connector" ),
163+ )
164+
165+ serialized , active = item .process_and_serialize (SimpleNamespace (), request_data )
166+
167+ assert active is True
168+ assert serialized ["is_active" ] is True
0 commit comments