diff --git a/netbox/netbox/models/features.py b/netbox/netbox/models/features.py index 0d519a8ba..5325cbcd1 100644 --- a/netbox/netbox/models/features.py +++ b/netbox/netbox/models/features.py @@ -13,7 +13,7 @@ from extras.utils import is_taggable, register_features from netbox.signals import post_clean from utilities.json import CustomFieldJSONEncoder from utilities.utils import serialize_object -from utilities.views import register_model_view +from utilities.views import ViewTab, register_model_view __all__ = ( 'ChangeLoggingMixin', @@ -300,9 +300,6 @@ def _register_features(sender, **kwargs): sender, 'journal', 'netbox.views.generic.ObjectJournalView', - tab_label='Journal', - tab_badge=lambda x: x.journal_entries.count(), - tab_permission='extras.view_journalentry', kwargs={'model': sender} ) if issubclass(sender, ChangeLoggingMixin): @@ -310,7 +307,5 @@ def _register_features(sender, **kwargs): sender, 'changelog', 'netbox.views.generic.ObjectChangeLogView', - tab_label='Changelog', - tab_permission='extras.view_objectchange', kwargs={'model': sender} ) diff --git a/netbox/netbox/views/generic/feature_views.py b/netbox/netbox/views/generic/feature_views.py index 85e675a69..963fad196 100644 --- a/netbox/netbox/views/generic/feature_views.py +++ b/netbox/netbox/views/generic/feature_views.py @@ -1,10 +1,12 @@ from django.contrib.contenttypes.models import ContentType from django.db.models import Q from django.shortcuts import get_object_or_404, render +from django.utils.translation import gettext as _ from django.views.generic import View from extras import forms, tables from extras.models import * +from utilities.views import ViewTab __all__ = ( 'ObjectChangeLogView', @@ -23,6 +25,10 @@ class ObjectChangeLogView(View): base_template: The name of the template to extend. If not provided, "{app}/{model}.html" will be used. """ base_template = None + tab = ViewTab( + label=_('Changelog'), + permission='extras.view_objectchange' + ) def get(self, request, model, **kwargs): @@ -71,6 +77,11 @@ class ObjectJournalView(View): base_template: The name of the template to extend. If not provided, "{app}/{model}.html" will be used. """ base_template = None + tab = ViewTab( + label=_('Journal'), + badge=lambda obj: obj.journal_entries.count(), + permission='extras.view_journalentry' + ) def get(self, request, model, **kwargs): diff --git a/netbox/utilities/urls.py b/netbox/utilities/urls.py index 3920889b3..2db8bc91f 100644 --- a/netbox/utilities/urls.py +++ b/netbox/utilities/urls.py @@ -22,14 +22,17 @@ def get_model_urls(app_label, model_name): # No views have been registered for this model views = [] - for view in views: + for config in views: # Import the view class or function - callable = import_string(view['path']) - if issubclass(callable, View): - callable = callable.as_view() + if type(config['view']) is str: + view_ = import_string(config['view']) + else: + view_ = config['view'] + if issubclass(view_, View): + view_ = view_.as_view() # Create a path to the view paths.append( - path(f"{view['name']}/", callable, name=f"{model_name}_{view['name']}", kwargs=view['kwargs']) + path(f"{config['name']}/", view_, name=f"{model_name}_{config['name']}", kwargs=config['kwargs']) ) return paths diff --git a/netbox/utilities/views.py b/netbox/utilities/views.py index a4f5c79a9..1200112be 100644 --- a/netbox/utilities/views.py +++ b/netbox/utilities/views.py @@ -10,6 +10,7 @@ __all__ = ( 'ContentTypePermissionRequiredMixin', 'GetReturnURLMixin', 'ObjectPermissionRequiredMixin', + 'ViewTab', 'register_model_view', ) @@ -132,18 +133,23 @@ class GetReturnURLMixin: return reverse('home') -def register_model_view(model, name, view_path, tab_label=None, tab_badge=None, tab_permission=None, kwargs=None): +class ViewTab: + + def __init__(self, label, badge=None, permission=None, always_display=True): + self.label = label + self.badge = badge + self.permission = permission + self.always_display = always_display + + +def register_model_view(model, name, view, kwargs=None): """ Register a subview for a core model. Args: model: The Django model class with which this view will be associated name: The name to register when creating a URL path - view_path: A dotted path to the view class or function (e.g. 'myplugin.views.FooView') - tab_label: The label to display for the view's tab under the model view (optional) - tab_badge: A static value or callable to display a badge within the view's tab (optional). If a callable is - specified, it must accept the current object as its single positional argument. - tab_permission: The name of the permission required to display the tab (optional) + view: A class-based or function view, or the dotted path to it (e.g. 'myplugin.views.FooView') kwargs: A dictionary of keyword arguments to send to the view (optional) """ app_label = model._meta.app_label @@ -154,9 +160,6 @@ def register_model_view(model, name, view_path, tab_label=None, tab_badge=None, registry['views'][app_label][model_name].append({ 'name': name, - 'path': view_path, - 'tab_label': tab_label, - 'tab_badge': tab_badge, - 'tab_permission': tab_permission, + 'view': view, 'kwargs': kwargs or {}, })