mirror of
https://github.com/netbox-community/netbox.git
synced 2025-08-08 16:48:16 -06:00
14731 change to table
This commit is contained in:
parent
6136154588
commit
e9dd15443c
@ -4,4 +4,3 @@ from .data import *
|
|||||||
from .jobs import *
|
from .jobs import *
|
||||||
from .tasks import *
|
from .tasks import *
|
||||||
from .plugins import *
|
from .plugins import *
|
||||||
from .plugin import *
|
|
||||||
|
@ -1,40 +0,0 @@
|
|||||||
from datetime import datetime
|
|
||||||
from django.contrib.humanize.templatetags.humanize import naturalday
|
|
||||||
import django_tables2 as tables
|
|
||||||
from django.utils.translation import gettext_lazy as _
|
|
||||||
from netbox.tables import BaseTable
|
|
||||||
|
|
||||||
__all__ = (
|
|
||||||
'CertifiedPluginTable',
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
class CertifiedPluginTable(BaseTable):
|
|
||||||
version = tables.Column(
|
|
||||||
verbose_name=_('Version')
|
|
||||||
)
|
|
||||||
last_updated = tables.Column(
|
|
||||||
accessor=tables.A('date'),
|
|
||||||
verbose_name=_('Last Updated')
|
|
||||||
)
|
|
||||||
min_version = tables.Column(
|
|
||||||
accessor=tables.A('netbox_min_version'),
|
|
||||||
verbose_name=_('Minimum NetBox Version')
|
|
||||||
)
|
|
||||||
max_version = tables.Column(
|
|
||||||
accessor=tables.A('netbox_max_version'),
|
|
||||||
verbose_name=_('Maximum NetBox Version')
|
|
||||||
)
|
|
||||||
|
|
||||||
class Meta(BaseTable.Meta):
|
|
||||||
empty_text = _('No plugin data found')
|
|
||||||
fields = (
|
|
||||||
'version', 'last_updated', 'min_version', 'max_version',
|
|
||||||
)
|
|
||||||
default_columns = (
|
|
||||||
'version', 'last_updated', 'min_version', 'max_version',
|
|
||||||
)
|
|
||||||
orderable = False
|
|
||||||
|
|
||||||
def render_last_updated(self, value, record):
|
|
||||||
return naturalday(datetime.fromisoformat(value))
|
|
@ -1,13 +1,17 @@
|
|||||||
|
from datetime import datetime
|
||||||
import django_tables2 as tables
|
import django_tables2 as tables
|
||||||
|
from django.contrib.humanize.templatetags.humanize import naturalday
|
||||||
from django.utils.translation import gettext_lazy as _
|
from django.utils.translation import gettext_lazy as _
|
||||||
from netbox.tables import BaseTable
|
from netbox.tables import BaseTable
|
||||||
|
|
||||||
__all__ = (
|
__all__ = (
|
||||||
'PluginTable',
|
'CertifiedPluginTable',
|
||||||
|
'InstalledPluginTable',
|
||||||
|
'PluginVersionTable',
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class PluginTable(BaseTable):
|
class InstalledPluginTable(BaseTable):
|
||||||
name = tables.Column(
|
name = tables.Column(
|
||||||
accessor=tables.A('verbose_name'),
|
accessor=tables.A('verbose_name'),
|
||||||
verbose_name=_('Name')
|
verbose_name=_('Name')
|
||||||
@ -37,3 +41,71 @@ class PluginTable(BaseTable):
|
|||||||
default_columns = (
|
default_columns = (
|
||||||
'name', 'version', 'package', 'description',
|
'name', 'version', 'package', 'description',
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class PluginVersionTable(BaseTable):
|
||||||
|
version = tables.Column(
|
||||||
|
verbose_name=_('Version')
|
||||||
|
)
|
||||||
|
last_updated = tables.Column(
|
||||||
|
accessor=tables.A('date'),
|
||||||
|
verbose_name=_('Last Updated')
|
||||||
|
)
|
||||||
|
min_version = tables.Column(
|
||||||
|
accessor=tables.A('netbox_min_version'),
|
||||||
|
verbose_name=_('Minimum NetBox Version')
|
||||||
|
)
|
||||||
|
max_version = tables.Column(
|
||||||
|
accessor=tables.A('netbox_max_version'),
|
||||||
|
verbose_name=_('Maximum NetBox Version')
|
||||||
|
)
|
||||||
|
|
||||||
|
class Meta(BaseTable.Meta):
|
||||||
|
empty_text = _('No plugin data found')
|
||||||
|
fields = (
|
||||||
|
'version', 'last_updated', 'min_version', 'max_version',
|
||||||
|
)
|
||||||
|
default_columns = (
|
||||||
|
'version', 'last_updated', 'min_version', 'max_version',
|
||||||
|
)
|
||||||
|
orderable = False
|
||||||
|
|
||||||
|
def render_last_updated(self, value, record):
|
||||||
|
return naturalday(datetime.fromisoformat(value))
|
||||||
|
|
||||||
|
|
||||||
|
class CertifiedPluginTable(BaseTable):
|
||||||
|
name = tables.Column(
|
||||||
|
linkify=('core:plugin', [tables.A('slug')]),
|
||||||
|
verbose_name=_('Name')
|
||||||
|
)
|
||||||
|
status = tables.Column(
|
||||||
|
verbose_name=_('Status')
|
||||||
|
)
|
||||||
|
author = tables.Column(
|
||||||
|
verbose_name=_('Author')
|
||||||
|
)
|
||||||
|
is_local = tables.BooleanColumn(
|
||||||
|
verbose_name=_('Local')
|
||||||
|
)
|
||||||
|
is_installed = tables.BooleanColumn(
|
||||||
|
verbose_name=_('Installed')
|
||||||
|
)
|
||||||
|
is_certified = tables.BooleanColumn(
|
||||||
|
verbose_name=_('Certified')
|
||||||
|
)
|
||||||
|
created = tables.Column(
|
||||||
|
verbose_name=_('Published')
|
||||||
|
)
|
||||||
|
updated = tables.Column(
|
||||||
|
verbose_name=_('Updated')
|
||||||
|
)
|
||||||
|
|
||||||
|
class Meta(BaseTable.Meta):
|
||||||
|
empty_text = _('No plugin data found')
|
||||||
|
fields = (
|
||||||
|
'name', 'status', 'author', 'is_local', 'is_installed', 'is_certified', 'created', 'updated',
|
||||||
|
)
|
||||||
|
default_columns = (
|
||||||
|
'name', 'status', 'author', 'is_local', 'is_installed', 'is_certified', 'created', 'updated',
|
||||||
|
)
|
||||||
|
@ -41,7 +41,7 @@ from utilities.views import ContentTypePermissionRequiredMixin, GetRelatedModels
|
|||||||
from . import filtersets, forms, tables
|
from . import filtersets, forms, tables
|
||||||
from .choices import PluginSortChoices, PluginStatusChoices
|
from .choices import PluginSortChoices, PluginStatusChoices
|
||||||
from .models import *
|
from .models import *
|
||||||
from .tables import CertifiedPluginTable
|
from .tables import CertifiedPluginTable, PluginVersionTable
|
||||||
|
|
||||||
|
|
||||||
#
|
#
|
||||||
@ -648,7 +648,7 @@ class SystemView(UserPassesTestMixin, View):
|
|||||||
response['Content-Disposition'] = 'attachment; filename="netbox.json"'
|
response['Content-Disposition'] = 'attachment; filename="netbox.json"'
|
||||||
return response
|
return response
|
||||||
|
|
||||||
plugins_table = tables.PluginTable(plugins, orderable=False)
|
plugins_table = tables.InstalledPluginTable(plugins, orderable=False)
|
||||||
plugins_table.configure(request)
|
plugins_table.configure(request)
|
||||||
|
|
||||||
return render(request, 'core/system.html', {
|
return render(request, 'core/system.html', {
|
||||||
@ -675,8 +675,8 @@ def get_local_plugins(plugins):
|
|||||||
'tag_line': plugin_config.description,
|
'tag_line': plugin_config.description,
|
||||||
'description_short': plugin_config.description,
|
'description_short': plugin_config.description,
|
||||||
'author': plugin_config.author or _('Unknown Author'),
|
'author': plugin_config.author or _('Unknown Author'),
|
||||||
'created': datetime.min.replace(tzinfo=timezone.utc),
|
'created': None,
|
||||||
'updated': datetime.min.replace(tzinfo=timezone.utc),
|
'updated': None,
|
||||||
'is_local': True,
|
'is_local': True,
|
||||||
'is_installed': True,
|
'is_installed': True,
|
||||||
'is_certified': False,
|
'is_certified': False,
|
||||||
@ -768,8 +768,8 @@ def get_catalog_plugins(plugins):
|
|||||||
|
|
||||||
|
|
||||||
def get_plugins():
|
def get_plugins():
|
||||||
if plugins := cache.get('plugins-catalog-feed'):
|
# if plugins := cache.get('plugins-catalog-feed'):
|
||||||
return plugins
|
# return plugins
|
||||||
|
|
||||||
plugins = {}
|
plugins = {}
|
||||||
plugins = get_local_plugins(plugins)
|
plugins = get_local_plugins(plugins)
|
||||||
@ -785,30 +785,22 @@ class PluginListView(UserPassesTestMixin, View):
|
|||||||
return self.request.user.is_staff
|
return self.request.user.is_staff
|
||||||
|
|
||||||
def get(self, request):
|
def get(self, request):
|
||||||
sort = request.GET.get('sort', PluginSortChoices.SORT_NAME_AZ)
|
|
||||||
status = request.GET.get('status', PluginStatusChoices.STATUS_ALL)
|
|
||||||
q = request.GET.get('q', None)
|
q = request.GET.get('q', None)
|
||||||
|
|
||||||
plugins = get_plugins()
|
plugins = get_plugins()
|
||||||
if status == PluginStatusChoices.STATUS_INSTALLED:
|
plugins = [v for k, v in plugins.items()]
|
||||||
plugins = [v for k, v in plugins.items() if v['is_installed']]
|
|
||||||
else:
|
table = CertifiedPluginTable(plugins, user=request.user)
|
||||||
plugins = [v for k, v in plugins.items()]
|
table.configure(request)
|
||||||
if sort == PluginSortChoices.SORT_NAME_ZA:
|
|
||||||
plugins = sorted(plugins, key=lambda d: d['name'], reverse=True)
|
# If this is an HTMX request, return only the rendered table HTML
|
||||||
elif sort == PluginSortChoices.SORT_UPDATED:
|
if htmx_partial(request):
|
||||||
plugins = sorted(plugins, key=lambda d: d['updated'])
|
return render(request, 'htmx/table.html', {
|
||||||
elif sort == PluginSortChoices.SORT_PUBLISHED:
|
'table': table,
|
||||||
plugins = sorted(plugins, key=lambda d: d['created'])
|
})
|
||||||
else:
|
|
||||||
plugins = sorted(plugins, key=lambda d: d['name'])
|
|
||||||
|
|
||||||
return render(request, 'core/plugin_list.html', {
|
return render(request, 'core/plugin_list.html', {
|
||||||
'plugins': plugins,
|
'table': table,
|
||||||
'sort_choices': dict(PluginSortChoices),
|
|
||||||
'status_choices': dict(PluginStatusChoices),
|
|
||||||
'sort': sort,
|
|
||||||
'status': status,
|
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
||||||
@ -822,7 +814,7 @@ class PluginView(UserPassesTestMixin, View):
|
|||||||
plugins = get_plugins()
|
plugins = get_plugins()
|
||||||
plugin = plugins[name]
|
plugin = plugins[name]
|
||||||
|
|
||||||
table = CertifiedPluginTable(plugin['versions'], user=request.user)
|
table = PluginVersionTable(plugin['versions'], user=request.user)
|
||||||
table.configure(request)
|
table.configure(request)
|
||||||
|
|
||||||
return render(request, 'core/plugin.html', {
|
return render(request, 'core/plugin.html', {
|
||||||
|
@ -2,41 +2,8 @@
|
|||||||
{% load helpers %}
|
{% load helpers %}
|
||||||
{% load i18n %}
|
{% load i18n %}
|
||||||
|
|
||||||
{% for plugin in plugins %}
|
<div class="card">
|
||||||
<div class="col-md-3">
|
<div class="table-responsive" id="object_list">
|
||||||
<a href="{% url 'core:plugin' plugin.slug %}">
|
{% include 'htmx/table.html' %}
|
||||||
<div class="card">
|
|
||||||
<div class="card-body">
|
|
||||||
<h5 class="card-title">{{ plugin.name }}</h5>
|
|
||||||
<p>By {{ plugin.author }}</p>
|
|
||||||
<div>
|
|
||||||
{% if plugin.is_local %}
|
|
||||||
<button type="button" class="btn">
|
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="icon icon-tabler icons-tabler-outline icon-tabler-server"><path stroke="none" d="M0 0h24v24H0z" fill="none"/><path d="M3 4m0 3a3 3 0 0 1 3 -3h12a3 3 0 0 1 3 3v2a3 3 0 0 1 -3 3h-12a3 3 0 0 1 -3 -3z" /><path d="M3 12m0 3a3 3 0 0 1 3 -3h12a3 3 0 0 1 3 3v2a3 3 0 0 1 -3 3h-12a3 3 0 0 1 -3 -3z" /><path d="M7 8l0 .01" /><path d="M7 16l0 .01" /></svg>
|
|
||||||
Local
|
|
||||||
</button>
|
|
||||||
{% endif %}
|
|
||||||
{% if plugin.is_certified %}
|
|
||||||
<button type="button" class="btn">
|
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="currentColor" class="icon icon-tabler icons-tabler-filled icon-tabler-award"><path stroke="none" d="M0 0h24v24H0z" fill="none"/><path d="M19.496 13.983l1.966 3.406a1.001 1.001 0 0 1 -.705 1.488l-.113 .011l-.112 -.001l-2.933 -.19l-1.303 2.636a1.001 1.001 0 0 1 -1.608 .26l-.082 -.094l-.072 -.11l-1.968 -3.407a8.994 8.994 0 0 0 6.93 -3.999z" /><path d="M11.43 17.982l-1.966 3.408a1.001 1.001 0 0 1 -1.622 .157l-.076 -.1l-.064 -.114l-1.304 -2.635l-2.931 .19a1.001 1.001 0 0 1 -1.022 -1.29l.04 -.107l.05 -.1l1.968 -3.409a8.994 8.994 0 0 0 6.927 4.001z" /><path d="M12 2l.24 .004a7 7 0 0 1 6.76 6.996l-.003 .193l-.007 .192l-.018 .245l-.026 .242l-.024 .178a6.985 6.985 0 0 1 -.317 1.268l-.116 .308l-.153 .348a7.001 7.001 0 0 1 -12.688 -.028l-.13 -.297l-.052 -.133l-.08 -.217l-.095 -.294a6.96 6.96 0 0 1 -.093 -.344l-.06 -.271l-.049 -.271l-.02 -.139l-.039 -.323l-.024 -.365l-.006 -.292a7 7 0 0 1 6.76 -6.996l.24 -.004z" /></svg>
|
|
||||||
Certified
|
|
||||||
</button>
|
|
||||||
{% endif %}
|
|
||||||
{% if plugin.is_community %}
|
|
||||||
<button type="button" class="btn">
|
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="icon icon-tabler icons-tabler-outline icon-tabler-users-group"><path stroke="none" d="M0 0h24v24H0z" fill="none"/><path d="M10 13a2 2 0 1 0 4 0a2 2 0 0 0 -4 0" /><path d="M8 21v-1a2 2 0 0 1 2 -2h4a2 2 0 0 1 2 2v1" /><path d="M15 5a2 2 0 1 0 4 0a2 2 0 0 0 -4 0" /><path d="M17 10h2a2 2 0 0 1 2 2v1" /><path d="M5 5a2 2 0 1 0 4 0a2 2 0 0 0 -4 0" /><path d="M3 13v-1a2 2 0 0 1 2 -2h2" /></svg>
|
|
||||||
Community
|
|
||||||
</button>
|
|
||||||
{% endif %}
|
|
||||||
{% if plugin.is_installed %}
|
|
||||||
<button type="button" class="btn">
|
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="icon icon-tabler icons-tabler-outline icon-tabler-check"><path stroke="none" d="M0 0h24v24H0z" fill="none"/><path d="M5 12l5 5l10 -10" /></svg>
|
|
||||||
Installed
|
|
||||||
</button>
|
|
||||||
{% endif %}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</a>
|
|
||||||
</div>
|
</div>
|
||||||
{% endfor %}
|
</div>
|
||||||
|
@ -31,51 +31,6 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="col-auto ms-auto d-print-none">
|
|
||||||
|
|
||||||
<div class="row">
|
|
||||||
<div class="col-auto">
|
|
||||||
<div class="px-2 d-print-none">
|
|
||||||
{% trans "Status" %}
|
|
||||||
<div class="dropdown">
|
|
||||||
<button class="btn btn-outline-secondary dropdown-toggle" type="button" data-bs-toggle="dropdown" aria-expanded="false">
|
|
||||||
{{ status_choices|get_key:status }}
|
|
||||||
</button>
|
|
||||||
<div class="dropdown-menu">
|
|
||||||
{% for level, name in status_choices.items %}
|
|
||||||
<a class="dropdown-item d-flex justify-content-between" href="{% url 'core:plugin_list' %}?status={{ level }}">
|
|
||||||
{{ name }}
|
|
||||||
{% if level == status %}<span class="badge bg-green ms-auto"></span>{% endif %}
|
|
||||||
</a>
|
|
||||||
{% endfor %}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="col-auto">
|
|
||||||
<div class="px-2 d-print-none">
|
|
||||||
{% trans "Sort" %}
|
|
||||||
<div class="dropdown">
|
|
||||||
<button class="btn btn-outline-secondary dropdown-toggle" type="button" data-bs-toggle="dropdown" aria-expanded="false">
|
|
||||||
{{ sort_choices|get_key:sort }}
|
|
||||||
</button>
|
|
||||||
<div class="dropdown-menu">
|
|
||||||
{% for level, name in sort_choices.items %}
|
|
||||||
<a class="dropdown-item d-flex justify-content-between" href="{% url 'core:plugin_list' %}?sort={{ level }}">
|
|
||||||
{{ name }}
|
|
||||||
{% if level == sort %}<span class="badge bg-green ms-auto"></span>{% endif %}
|
|
||||||
</a>
|
|
||||||
{% endfor %}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{# System status #}
|
{# System status #}
|
||||||
|
Loading…
Reference in New Issue
Block a user