Просмотр исходного кода

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 лет назад
Родитель
Сommit
ef5e10d360

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

@@ -1,3 +1,4 @@
 from .config import *
 from .data 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
     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.auth.mixins import UserPassesTestMixin
 from django.core.cache import cache
 from django.http import HttpResponseForbidden
 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}")
 
         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.auth.mixins import LoginRequiredMixin
 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('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
 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
 
 
-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)
 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.graphql.schema import schema
 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 .admin import admin_site
 
@@ -73,7 +73,6 @@ _patterns = [
 
     # Admin
     path('admin/background-tasks/', include('django_rq.urls')),
-    path('admin/plugins/', include(plugin_admin_patterns)),
     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 %}