diff --git a/netbox/extras/ui/panels.py b/netbox/extras/ui/panels.py index 991a4aa3d..4ab50ded7 100644 --- a/netbox/extras/ui/panels.py +++ b/netbox/extras/ui/panels.py @@ -10,7 +10,7 @@ __all__ = ( ) -class CustomFieldsPanel(panels.Panel): +class CustomFieldsPanel(panels.ObjectPanel): template_name = 'ui/panels/custom_fields.html' title = _('Custom Fields') @@ -39,7 +39,7 @@ class ImageAttachmentsPanel(panels.ObjectsTablePanel): super().__init__('extras.imageattachment', **kwargs) -class TagsPanel(panels.Panel): +class TagsPanel(panels.ObjectPanel): template_name = 'ui/panels/tags.html' title = _('Tags') diff --git a/netbox/netbox/ui/actions.py b/netbox/netbox/ui/actions.py index d0a374f58..2dd8482b2 100644 --- a/netbox/netbox/ui/actions.py +++ b/netbox/netbox/ui/actions.py @@ -25,13 +25,63 @@ class PanelAction: button_class: Bootstrap CSS class for the button button_icon: Name of the button's MDI icon """ - template_name = 'ui/actions/link.html' + template_name = None label = None button_class = 'primary' button_icon = None - # TODO: Refactor URL parameters to AddObject - def __init__(self, view_name, view_kwargs=None, url_params=None, permissions=None, label=None): + def __init__(self, label=None, permissions=None): + """ + Initialize a new PanelAction. + + Parameters: + label: The human-friendly button text + permissions: A list of permissions required to display the action + """ + if label is not None: + self.label = label + self.permissions = permissions + + def get_context(self, context): + """ + Return the template context used to render the action element. + + Parameters: + context: The template context + """ + return { + 'label': self.label, + 'button_class': self.button_class, + 'button_icon': self.button_icon, + } + + def render(self, context): + """ + Render the action as HTML. + + Parameters: + context: The template context + """ + # Enforce permissions + user = context['request'].user + if not user.has_perms(self.permissions): + return '' + + return render_to_string(self.template_name, self.get_context(context)) + + +class LinkAction(PanelAction): + """ + A hyperlink (typically a button) within a panel to perform some associated action, such as adding an object. + + Attributes: + label: The default human-friendly button text + button_class: Bootstrap CSS class for the button + button_icon: Name of the button's MDI icon + """ + template_name = 'ui/actions/link.html' + + def __init__(self, view_name, view_kwargs=None, url_params=None, **kwargs): """ Initialize a new PanelAction. @@ -42,12 +92,11 @@ class PanelAction: permissions: A list of permissions required to display the action label: The human-friendly button text """ + super().__init__(**kwargs) + self.view_name = view_name self.view_kwargs = view_kwargs or {} self.url_params = url_params or {} - self.permissions = permissions - if label is not None: - self.label = label def get_url(self, context): """ @@ -68,27 +117,14 @@ class PanelAction: url = f'{url}?{urlencode(url_params)}' return url - def render(self, context): - """ - Render the action as HTML. - - Parameters: - context: The template context - """ - # Enforce permissions - user = context['request'].user - if not user.has_perms(self.permissions): - return '' - - return render_to_string(self.template_name, { + def get_context(self, context): + return { + **super().get_context(context), 'url': self.get_url(context), - 'label': self.label, - 'button_class': self.button_class, - 'button_icon': self.button_icon, - }) + } -class AddObject(PanelAction): +class AddObject(LinkAction): """ An action to add a new object. """ @@ -112,22 +148,28 @@ class AddObject(PanelAction): raise ValueError(f"Invalid model label: {model}") view_name = get_viewname(model, 'add') - super().__init__(view_name=view_name, label=label, url_params=url_params) + super().__init__(view_name=view_name, url_params=url_params, label=label) # Require "add" permission on the model self.permissions = [get_permission_for_model(model, 'add')] -class CopyContent: +class CopyContent(PanelAction): """ An action to copy the contents of a panel to the clipboard. """ template_name = 'ui/actions/copy_content.html' label = _('Copy') - button_class = 'primary' button_icon = 'content-copy' - def __init__(self, target_id): + def __init__(self, target_id, **kwargs): + """ + Instantiate a new CopyContent action. + + Parameters: + target_id: The ID of the target element containing the content to be copied + """ + super().__init__(**kwargs) self.target_id = target_id def render(self, context): diff --git a/netbox/netbox/ui/panels.py b/netbox/netbox/ui/panels.py index 970a4fd73..b20d8fed5 100644 --- a/netbox/netbox/ui/panels.py +++ b/netbox/netbox/ui/panels.py @@ -54,7 +54,7 @@ class Panel(ABC): """ if title is not None: self.title = title - self.actions = actions or [] + self.actions = actions or self.actions or [] def get_context(self, context): """