Coverage for netbox/plugins/navigation.py: 30%

65 statements  

« prev     ^ index     » next       coverage.py v7.15.2, created at 2026-10-10 18:35 +0000

1from django.urls import reverse_lazy 

2from django.utils.text import slugify 

3from django.utils.translation import gettext as _ 

4 

5from netbox.choices import ButtonColorChoices 

6from netbox.navigation import MenuGroup 

7 

8__all__ = ( 

9 'PluginMenu', 

10 'PluginMenuButton', 

11 'PluginMenuItem', 

12) 

13 

14 

15class PluginMenu: 

16 icon_class = 'mdi mdi-puzzle' 

17 

18 def __init__(self, label, groups, icon_class=None): 

19 self.label = label 

20 self.groups = [ 

21 MenuGroup(label, items) for label, items in groups 

22 ] 

23 if icon_class is not None: 

24 self.icon_class = icon_class 

25 

26 @property 

27 def name(self): 

28 return slugify(self.label) 

29 

30 

31class PluginMenuItem: 

32 """ 

33 This class represents a navigation menu item. This constitutes primary link and its text, but also allows for 

34 specifying additional link buttons that appear to the right of the item in the van menu. 

35 

36 Links are specified as Django reverse URL strings suitable for rendering via {% url item.link %}. 

37 Alternatively, a pre-generated url can be set on the object which will be rendered literally. 

38 Buttons are each specified as a list of PluginMenuButton instances. 

39 """ 

40 _url = None 

41 

42 def __init__( 

43 self, link, link_text, auth_required=False, staff_only=False, permissions=None, buttons=None 

44 ): 

45 self.link = link 

46 self.link_text = link_text 

47 self.auth_required = auth_required 

48 self.staff_only = staff_only 

49 if link: 

50 self._url = reverse_lazy(link) 

51 if permissions is not None: 

52 if type(permissions) not in (list, tuple): 

53 raise TypeError(_("Permissions must be passed as a tuple or list.")) 

54 self.permissions = permissions 

55 else: 

56 self.permissions = [] 

57 if buttons is not None: 

58 if type(buttons) not in (list, tuple): 

59 raise TypeError(_("Buttons must be passed as a tuple or list.")) 

60 self.buttons = buttons 

61 else: 

62 self.buttons = [] 

63 

64 @property 

65 def url(self): 

66 return self._url 

67 

68 @url.setter 

69 def url(self, value): 

70 self._url = value 

71 

72 

73class PluginMenuButton: 

74 """ 

75 This class represents a button within a PluginMenuItem. Note that button colors should come from 

76 ButtonColorChoices. 

77 """ 

78 color = ButtonColorChoices.DEFAULT 

79 _url = None 

80 

81 def __init__(self, link, title, icon_class, color=None, permissions=None): 

82 self.link = link 

83 self.title = title 

84 self.icon_class = icon_class 

85 if link: 

86 self._url = reverse_lazy(link) 

87 if permissions is not None: 

88 if type(permissions) not in (list, tuple): 

89 raise TypeError(_("Permissions must be passed as a tuple or list.")) 

90 self.permissions = permissions 

91 else: 

92 self.permissions = [] 

93 if color is not None: 

94 if color not in ButtonColorChoices.values(): 

95 raise ValueError(_("Button color must be a choice within ButtonColorChoices.")) 

96 self.color = color 

97 

98 @property 

99 def url(self): 

100 return self._url 

101 

102 @url.setter 

103 def url(self, value): 

104 self._url = value