فهرست منبع

14728 Move installed plugins list from admin UI to NetBox UI (#14768)

* 14728 move plugins view from admin

* 14728 move plugins view from admin

* 14728 remove plugins view from admin

* Update template for #12128

* 14728 review fixes

* 14728 review fixes

* 14728 review fixes

* 14728 review fixes

* 14728 configure table

* Clean up table columns

* Fix app config lookup for plugins referenced by dotted path

* Move template; fix table display

* Fix user table configuration

* Remove nonfunctional quick search

* Limit PluginListView to staff users

---------

Co-authored-by: Jeremy Stretch <jstretch@netboxlabs.com>
Arthur Hanson 2 سال پیش
والد
کامیت
ef5e10d360

+ 1 - 0
netbox/core/tables/__init__.py

@@ -1,3 +1,4 @@
 from .config import *
 from .config import *
 from .data import *
 from .data import *
 from .jobs import *
 from .jobs import *
+from .plugins import *

+ 39 - 0
netbox/core/tables/plugins.py

@@ -0,0 +1,39 @@
+import django_tables2 as tables
+from django.utils.translation import gettext_lazy as _
+from netbox.tables import BaseTable
+
+__all__ = (
+    'PluginTable',
+)
+
+
+class PluginTable(BaseTable):
+    name = tables.Column(
+        accessor=tables.A('verbose_name'),
+        verbose_name=_('Name')
+    )
+    version = tables.Column(
+        verbose_name=_('Version')
+    )
+    package = tables.Column(
+        accessor=tables.A('name'),
+        verbose_name=_('Package')
+    )
+    author = tables.Column(
+        verbose_name=_('Author')
+    )
+    author_email = tables.Column(
+        verbose_name=_('Author Email')
+    )
+    description = tables.Column(
+        verbose_name=_('Description')
+    )
+
+    class Meta(BaseTable.Meta):
+        empty_text = _('No plugins found')
+        fields = (
+            'name', 'version', 'package', 'author', 'author_email', 'description',
+        )
+        default_columns = (
+            'name', 'version', 'package', 'author', 'author_email', 'description',
+        )

+ 2 - 0
netbox/core/urls.py

@@ -35,4 +35,6 @@ urlpatterns = (
     # Configuration
     # Configuration
     path('config/', views.ConfigView.as_view(), name='config'),
     path('config/', views.ConfigView.as_view(), name='config'),
 
 
+    # Plugins
+    path('plugins/', views.PluginListView.as_view(), name='plugin_list'),
 )
 )

+ 27 - 0
netbox/core/views.py

@@ -1,4 +1,7 @@
+from django.apps import apps
+from django.conf import settings
 from django.contrib import messages
 from django.contrib import messages
+from django.contrib.auth.mixins import UserPassesTestMixin
 from django.core.cache import cache
 from django.core.cache import cache
 from django.http import HttpResponseForbidden
 from django.http import HttpResponseForbidden
 from django.shortcuts import get_object_or_404, redirect, render
 from django.shortcuts import get_object_or_404, redirect, render
@@ -232,3 +235,27 @@ class ConfigRevisionRestoreView(ContentTypePermissionRequiredMixin, View):
         messages.success(request, f"Restored configuration revision #{pk}")
         messages.success(request, f"Restored configuration revision #{pk}")
 
 
         return redirect(candidate_config.get_absolute_url())
         return redirect(candidate_config.get_absolute_url())
+
+
+#
+# Plugins
+#
+
+class PluginListView(UserPassesTestMixin, View):
+
+    def test_func(self):
+        return self.request.user.is_staff
+
+    def get(self, request):
+        plugins = [
+            # Look up app config by package name
+            apps.get_app_config(plugin.rsplit('.', 1)[-1]) for plugin in settings.PLUGINS
+        ]
+        table = tables.PluginTable(plugins, user=request.user)
+        table.configure(request)
+
+        return render(request, 'core/plugin_list.html', {
+            'plugins': plugins,
+            'active_tab': 'api-tokens',
+            'table': table,
+        })

+ 2 - 0
netbox/extras/views.py

@@ -1,3 +1,5 @@
+from django.apps import apps
+from django.conf import settings
 from django.contrib import messages
 from django.contrib import messages
 from django.contrib.auth.mixins import LoginRequiredMixin
 from django.contrib.auth.mixins import LoginRequiredMixin
 from django.contrib.contenttypes.models import ContentType
 from django.contrib.contenttypes.models import ContentType

+ 10 - 0
netbox/netbox/navigation/menu.py

@@ -450,6 +450,16 @@ ADMIN_MENU = Menu(
                 ),
                 ),
             ),
             ),
         ),
         ),
+        MenuGroup(
+            label=_('Plugins'),
+            items=(
+                MenuItem(
+                    link='core:plugin_list',
+                    link_text=_('Plugins'),
+                    staff_only=True
+                ),
+            ),
+        ),
     ),
     ),
 )
 )
 
 

+ 0 - 3
netbox/netbox/plugins/urls.py

@@ -15,9 +15,6 @@ plugin_api_patterns = [
     path('', views.PluginsAPIRootView.as_view(), name='api-root'),
     path('', views.PluginsAPIRootView.as_view(), name='api-root'),
     path('installed-plugins/', views.InstalledPluginsAPIView.as_view(), name='plugins-list')
     path('installed-plugins/', views.InstalledPluginsAPIView.as_view(), name='plugins-list')
 ]
 ]
-plugin_admin_patterns = [
-    path('installed-plugins/', staff_member_required(views.InstalledPluginsAdminView.as_view()), name='plugins_list')
-]
 
 
 # Register base/API URL patterns for each plugin
 # Register base/API URL patterns for each plugin
 for plugin_path in settings.PLUGINS:
 for plugin_path in settings.PLUGINS:

+ 0 - 11
netbox/netbox/plugins/views.py

@@ -12,17 +12,6 @@ from rest_framework.reverse import reverse
 from rest_framework.views import APIView
 from rest_framework.views import APIView
 
 
 
 
-class InstalledPluginsAdminView(View):
-    """
-    Admin view for listing all installed plugins
-    """
-    def get(self, request):
-        plugins = [apps.get_app_config(plugin) for plugin in settings.PLUGINS]
-        return render(request, 'extras/admin/plugins_list.html', {
-            'plugins': plugins,
-        })
-
-
 @extend_schema(exclude=True)
 @extend_schema(exclude=True)
 class InstalledPluginsAPIView(APIView):
 class InstalledPluginsAPIView(APIView):
     """
     """

+ 1 - 2
netbox/netbox/urls.py

@@ -9,7 +9,7 @@ from account.views import LoginView, LogoutView
 from netbox.api.views import APIRootView, StatusView
 from netbox.api.views import APIRootView, StatusView
 from netbox.graphql.schema import schema
 from netbox.graphql.schema import schema
 from netbox.graphql.views import GraphQLView
 from netbox.graphql.views import GraphQLView
-from netbox.plugins.urls import plugin_admin_patterns, plugin_patterns, plugin_api_patterns
+from netbox.plugins.urls import plugin_patterns, plugin_api_patterns
 from netbox.views import HomeView, StaticMediaFailureView, SearchView, htmx
 from netbox.views import HomeView, StaticMediaFailureView, SearchView, htmx
 from .admin import admin_site
 from .admin import admin_site
 
 
@@ -73,7 +73,6 @@ _patterns = [
 
 
     # Admin
     # Admin
     path('admin/background-tasks/', include('django_rq.urls')),
     path('admin/background-tasks/', include('django_rq.urls')),
-    path('admin/plugins/', include(plugin_admin_patterns)),
     path('admin/', admin_site.urls),
     path('admin/', admin_site.urls),
 ]
 ]
 
 

+ 36 - 0
netbox/templates/core/plugin_list.html

@@ -0,0 +1,36 @@
+{% extends 'generic/_base.html' %}
+{% load buttons %}
+{% load helpers %}
+{% load i18n %}
+{% load render_table from django_tables2 %}
+
+{% block title %}{% trans "Plugins" %}{% endblock %}
+
+{% block tabs %}
+  <ul class="nav nav-tabs px-3">
+    <li class="nav-item" role="presentation">
+      <a class="nav-link active" role="tab">{% trans "Plugins" %}</a>
+    </li>
+  </ul>
+{% endblock tabs %}
+
+{% block content %}
+  <div class="row mb-3">
+    <div class="col-auto ms-auto d-print-none">
+      {# Table configuration button #}
+      <div class="table-configure input-group">
+        <button type="button" data-bs-toggle="modal" title="{% trans "Configure Table" %}" data-bs-target="#ObjectTable_config" class="btn">
+          <i class="mdi mdi-cog"></i> {% trans "Configure Table" %}
+        </button>
+      </div>
+    </div>
+  </div>
+
+  <div class="card">
+    {% render_table table %}
+  </div>
+{% endblock content %}
+
+{% block modals %}
+  {% table_config_form table table_name="ObjectTable" %}
+{% endblock modals %}

+ 0 - 58
netbox/templates/extras/admin/plugins_list.html

@@ -1,58 +0,0 @@
-{% extends "admin/base_site.html" %}
-{% load i18n %}
-
-{% block title %}{% trans "Installed Plugins" %} {{ block.super }}{% endblock %}
-
-{% block breadcrumbs %}
-    <div class="breadcrumbs">
-        <a href="{% url 'admin:index' %}">{% trans "Home" %}</a> &rsaquo;
-        <a href="{% url 'plugins_list' %}">{% trans "Installed Plugins" %}</a>
-    </div>
-{% endblock %}
-
-{% block content_title %}<h1>{% trans "Installed Plugins" %}{{ queue.name }}</h1>{% endblock %}
-
-{% block content %}
-<div id="content-main">
-    <div class="module" id="changelist">
-        <div class="results">
-            <table id="result_list">
-                <thead>
-                    <tr>
-                        <th><div class="text"><span>{% trans "Name" %}</span></div></th>
-                        <th><div class="text"><span>{% trans "Package Name" %}</span></div></th>
-                        <th><div class="text"><span>{% trans "Author" %}</span></div></th>
-                        <th><div class="text"><span>{% trans "Author Email" %}</span></div></th>
-                        <th><div class="text"><span>{% trans "Description" %}</span></div></th>
-                        <th><div class="text"><span>{% trans "Version" %}</span></div></th>
-                    </tr>
-                </thead>
-                <tbody>
-                    {% for plugin in plugins %}
-                        <tr class="{% cycle 'row1' 'row2' %}">
-                            <td>
-                                {{ plugin.verbose_name }}
-                            </td>
-                            <td>
-                                {{ plugin.name }}
-                            </td>
-                            <td>
-                                {{ plugin.author }}
-                            </td>
-                            <td>
-                                {{ plugin.author_email }}
-                            </td>
-                            <td>
-                                {{ plugin.description }}
-                            </td>
-                            <td>
-                                {{ plugin.version }}
-                            </td>
-                        </tr>
-                    {% endfor %}
-                </tbody>
-            </table>
-        </div>
-    </div>
-</div>
-{% endblock %}