Compare commits

..
5 Commits
Author SHA1 Message Date
mattieserver 4dd962f002 Prepare release v3.5.0 (#288)
* bump version

* bump version
2023-04-30 20:13:20 +02:00
Mario 5c58f2a9c2 Closes #64: Show neighbors option (#286)
* Add option to show neighbors

* Updated documentation and help text

* Added newlines at end of files where they where missing

* Updated options screenshot

* fix function vars

---------
2023-04-30 19:09:36 +02:00
Marioandmattieserver 5e9aaecf9c Closes #189, Closes #158: Add draw.io XML export (#277)
* Added export to draw.io XML

* Corrected missing newline at end of file

* init

* moved to api call

---------

Co-authored-by: mattieserver <3049868+mattieserver@users.noreply.github.com>
2023-04-30 15:22:38 +02:00
Mario 7d23cfc7eb Merge pull request #287 from dreng/285_icons_info
Fixes #285: Remove 'Icons info' from Readme
2023-04-29 15:14:44 +02:00
dreng 0af736df0e Removed 'Icons info' section 2023-04-29 13:14:15 +02:00
18 changed files with 715 additions and 288 deletions
+5 -12
View File
@@ -99,15 +99,13 @@ Go to the plugins tab in the navbar and click topology or go to `$NETBOX_URL/plu
Select your options for the topology view: Select your options for the topology view:
![selection_options](https://user-images.githubusercontent.com/20901110/220410144-5633d24c-6383-4cba-8d6d-ba77ef3d3cdd.png) ![topology_options](https://user-images.githubusercontent.com/20901110/235302073-234c3ef7-2c2b-4d0c-a674-9909d490d7ea.png)
<dl> <dl>
<dt>Save Coordinates</dt> <dt>Save Coordinates</dt>
<dd>Save the coordinates of devices in the topology view.</dd> <dd>Save the coordinates of devices in the topology view.</dd>
<dt>Show Unconnected</dt> <dt>Show Unconnected</dt>
<dd>Show devices that have no connections or for which no connection is displayed. This option depends on other parameters like 'Show Cables' and 'Show Logical Connections'.</dd> <dd>Show devices that have no connections or for which no connection is displayed. This option depends on other parameters like 'Show Cables' and 'Show Logical Connections'.</dd>
<dt>Show Circuit Terminations</dt>
<dd>Show connections which end at a circuit termination in the topology view. These connections are displayed as blue dashed lines.</dd>
<dt>Show Cables</dt> <dt>Show Cables</dt>
<dd>Show connections between interfaces, front / rear ports, etc., that are connected with one or more cables. These connections are displayed as solid lines in the color of the cable.</dd> <dd>Show connections between interfaces, front / rear ports, etc., that are connected with one or more cables. These connections are displayed as solid lines in the color of the cable.</dd>
<dt>Show Logical Connections</dt> <dt>Show Logical Connections</dt>
@@ -116,6 +114,10 @@ Select your options for the topology view:
intermediate front / rear port connections, etc. This is similar to what was referred to as 'end-to-end' connections in previous versions. These connections are displayed as yellow dotted lines.</dd> intermediate front / rear port connections, etc. This is similar to what was referred to as 'end-to-end' connections in previous versions. These connections are displayed as yellow dotted lines.</dd>
<dt>Show redundant Cable and Locigal Connection</dt> <dt>Show redundant Cable and Locigal Connection</dt>
<dd>Shows a logical connection (in addition to a cable), even if a cable is directly connected. Leaving this option disabled prevents that redundant display. This option only has an effect if 'Show Logical Connections' is activated.</dd> <dd>Shows a logical connection (in addition to a cable), even if a cable is directly connected. Leaving this option disabled prevents that redundant display. This option only has an effect if 'Show Logical Connections' is activated.</dd>
<dt>Show Neighbors</dt>
<dd>Adds neighbors to the filter result set automatically. Link peers will be added if 'Show Cables' is ticked, far-end terminations will be added if 'Show Logical Connections' is ticked.</dd>
<dt>Show Circuit Terminations</dt>
<dd>Show connections which end at a circuit termination in the topology view. These connections are displayed as blue dashed lines.</dd>
<dt>Show Power Feeds</dt> <dt>Show Power Feeds</dt>
<dd>Displays connections between power outlets and power ports. These connections are displayed as solid lines in the color of the cable. This option depends on 'Show Cables'.</dd> <dd>Displays connections between power outlets and power ports. These connections are displayed as solid lines in the color of the cable. This option depends on 'Show Cables'.</dd>
<dt>Show Wireless Links</dt> <dt>Show Wireless Links</dt>
@@ -150,12 +152,3 @@ To view `/plugins/netbox_topology-views/image`:
To view `/plugins/netbox_topology-views/individualoptions`: To view `/plugins/netbox_topology-views/individualoptions`:
+ netbox topology views | individualoptions | change + netbox topology views | individualoptions | change
## Icons info
Power icons created by [Freepik - Flaticon](https://www.flaticon.com/free-icons/power).
Power icons created by [Flat Icons - Flaticon](https://www.flaticon.com/free-icons/power)
Provider icons created by [Good Ware - Flaticon](https://www.flaticon.com/free-icons/provider)
+1 -1
View File
@@ -5,7 +5,7 @@ class TopologyViewsConfig(PluginConfig):
name = "netbox_topology_views" name = "netbox_topology_views"
verbose_name = "Topology views" verbose_name = "Topology views"
description = "An plugin to render topology maps" description = "An plugin to render topology maps"
version = "3.4.0" version = "3.5.0"
author = "Mattijs Vanhaverbeke" author = "Mattijs Vanhaverbeke"
author_email = "author@example.com" author_email = "author@example.com"
base_url = "netbox_topology_views" base_url = "netbox_topology_views"
+2 -1
View File
@@ -25,4 +25,5 @@ class DeviceRoleSerializer(ModelSerializer):
class IndividualOptionsSerializer(NetBoxModelSerializer): class IndividualOptionsSerializer(NetBoxModelSerializer):
class Meta: class Meta:
model = IndividualOptions model = IndividualOptions
fields = ("ignore_cable_type", "show_unconnected", "show_cables", "show_logical_connections", "show_single_cable_logical_conns", "show_circuit", "show_power", "show_wireless", "draw_default_layout") fields = ("ignore_cable_type", "show_unconnected", "show_cables", "show_logical_connections", "show_single_cable_logical_conns", "show_neighbors", "show_circuit", "show_power", "show_wireless", "draw_default_layout")
+1
View File
@@ -6,5 +6,6 @@ router = NetBoxRouter()
router.register("save-coords", views.SaveCoordsViewSet) router.register("save-coords", views.SaveCoordsViewSet)
router.register("images", views.SaveRoleImageViewSet) router.register("images", views.SaveRoleImageViewSet)
router.register("xml-export", views.ExportTopoToXML)
urlpatterns = router.urls urlpatterns = router.urls
+45 -4
View File
@@ -5,7 +5,7 @@ from dcim.models import Device, DeviceRole, PowerFeed, PowerPanel
from django.conf import settings from django.conf import settings
from django.contrib.auth.mixins import PermissionRequiredMixin from django.contrib.auth.mixins import PermissionRequiredMixin
from django.contrib.contenttypes.models import ContentType from django.contrib.contenttypes.models import ContentType
from django.http import JsonResponse from django.http import HttpResponse, JsonResponse
from rest_framework.decorators import action from rest_framework.decorators import action
from rest_framework.response import Response from rest_framework.response import Response
from rest_framework.viewsets import ReadOnlyModelViewSet, ViewSet from rest_framework.viewsets import ReadOnlyModelViewSet, ViewSet
@@ -14,9 +14,10 @@ from netbox_topology_views.api.serializers import (
RoleImageSerializer, RoleImageSerializer,
TopologyDummySerializer, TopologyDummySerializer,
) )
from netbox_topology_views.models import RoleImage from netbox_topology_views.models import RoleImage, IndividualOptions
from netbox_topology_views.utils import get_image_from_url from netbox_topology_views.views import get_topology_data
from netbox_topology_views.utils import get_image_from_url, export_data_to_xml, get_query_settings
from netbox_topology_views.filters import DeviceFilterSet
class SaveCoordsViewSet(ReadOnlyModelViewSet): class SaveCoordsViewSet(ReadOnlyModelViewSet):
queryset = Device.objects.none() queryset = Device.objects.none()
@@ -62,6 +63,46 @@ class SaveCoordsViewSet(ReadOnlyModelViewSet):
return Response({"status": "saved coords"}) return Response({"status": "saved coords"})
class ExportTopoToXML(PermissionRequiredMixin, ViewSet):
queryset = Device.objects.none()
permission_required = ("dcim.view_site", "dcim.view_device")
serializer_class = TopologyDummySerializer
def list(self, request):
self.filterset = DeviceFilterSet
self.queryset = Device.objects.all().select_related(
"device_type", "device_role"
)
self.queryset = self.filterset(request.GET, self.queryset).qs
individualOptions, created = IndividualOptions.objects.get_or_create(
user_id=request.user.id,
)
if request.GET:
save_coords, show_unconnected, show_power, show_circuit, show_logical_connections, show_single_cable_logical_conns, show_cables, show_wireless, show_neighbors = get_query_settings(request)
topo_data = get_topology_data(
queryset=self.queryset,
individualOptions=individualOptions,
save_coords=save_coords,
show_unconnected=show_unconnected,
show_cables=show_cables,
show_logical_connections=show_logical_connections,
show_single_cable_logical_conns=show_single_cable_logical_conns,
show_neighbors=show_neighbors,
show_circuit=show_circuit,
show_power=show_power,
show_wireless=show_wireless,
)
xml_data = export_data_to_xml(topo_data).decode('utf-8')
return HttpResponse(xml_data, content_type="application/xml; charset=utf-8")
else:
return JsonResponse(
{"status": "Missing or malformed request parameters"}, status=400
)
class SaveRoleImageViewSet(PermissionRequiredMixin, ViewSet): class SaveRoleImageViewSet(PermissionRequiredMixin, ViewSet):
queryset = DeviceRole.objects.none() queryset = DeviceRole.objects.none()
+23 -9
View File
@@ -33,9 +33,10 @@ class DeviceFilterForm(TenancyFilterForm, NetBoxModelFilterSetForm):
"save_coords", "save_coords",
"show_unconnected", "show_unconnected",
"show_cables", "show_cables",
"show_circuit",
"show_logical_connections", "show_logical_connections",
"show_single_cable_logical_conns", "show_single_cable_logical_conns",
"show_neighbors",
"show_circuit",
"show_power", "show_power",
"show_wireless", "show_wireless",
), ),
@@ -118,17 +119,17 @@ class DeviceFilterForm(TenancyFilterForm, NetBoxModelFilterSetForm):
show_unconnected = forms.BooleanField( show_unconnected = forms.BooleanField(
label=_("Show Unconnected"), required=False, initial=False label=_("Show Unconnected"), required=False, initial=False
) )
show_cables = forms.BooleanField(
label =_("Show Cables"), required=False, initial=False
)
show_logical_connections = forms.BooleanField( show_logical_connections = forms.BooleanField(
label =_("Show Logical Connections"), required=False, initial=False label =_("Show Logical Connections"), required=False, initial=False
) )
show_single_cable_logical_conns = forms.BooleanField( show_single_cable_logical_conns = forms.BooleanField(
label =_("Show redundant Cable and Locigal Connection"), required=False, initial=False label =_("Show redundant Cable and Locigal Connection"), required=False, initial=False
) )
show_cables = forms.BooleanField( show_neighbors = forms.BooleanField(
label =_("Show Cables"), required=False, initial=False label =_("Show Neighbors"), required=False, initial=False
)
show_wireless = forms.BooleanField(
label =_("Show Wireless Links"), required=False, initial=False
) )
show_circuit = forms.BooleanField( show_circuit = forms.BooleanField(
label=_("Show Circuit Terminations"), required=False, initial=False label=_("Show Circuit Terminations"), required=False, initial=False
@@ -136,6 +137,9 @@ class DeviceFilterForm(TenancyFilterForm, NetBoxModelFilterSetForm):
show_power = forms.BooleanField( show_power = forms.BooleanField(
label=_("Show Power Feeds"), required=False, initial=False label=_("Show Power Feeds"), required=False, initial=False
) )
show_wireless = forms.BooleanField(
label =_("Show Wireless Links"), required=False, initial=False
)
class IndividualOptionsForm(NetBoxModelForm): class IndividualOptionsForm(NetBoxModelForm):
fieldsets = ( fieldsets = (
@@ -148,9 +152,10 @@ class IndividualOptionsForm(NetBoxModelForm):
"preselected_tags", "preselected_tags",
"show_unconnected", "show_unconnected",
"show_cables", "show_cables",
"show_circuit",
"show_logical_connections", "show_logical_connections",
"show_single_cable_logical_conns", "show_single_cable_logical_conns",
"show_neighbors",
"show_circuit",
"show_power", "show_power",
"show_wireless", "show_wireless",
"draw_default_layout", "draw_default_layout",
@@ -215,6 +220,14 @@ class IndividualOptionsForm(NetBoxModelForm):
"disabled prevents that redundant display. This option only " "disabled prevents that redundant display. This option only "
"has an effect if 'Show Logical Connections' is activated.") "has an effect if 'Show Logical Connections' is activated.")
) )
show_neighbors = forms.BooleanField(
label =_("Show Neighbors"),
required=False,
initial=False,
help_text=_("Adds neighbors to the filter result set automatically. "
"Link peers will be added if 'Show Cables' is ticked, far-end "
"terminations will be added if 'Show Logical Connections' is ticked.")
)
show_circuit = forms.BooleanField( show_circuit = forms.BooleanField(
label=_("Show Circuit Terminations"), label=_("Show Circuit Terminations"),
required=False, required=False,
@@ -248,5 +261,6 @@ class IndividualOptionsForm(NetBoxModelForm):
class Meta: class Meta:
model = IndividualOptions model = IndividualOptions
fields = [ fields = [
'user_id', 'ignore_cable_type', 'preselected_device_roles', 'preselected_tags', 'show_unconnected', 'show_cables', 'show_logical_connections', 'show_single_cable_logical_conns', 'show_circuit', 'show_power', 'show_wireless', 'draw_default_layout' 'user_id', 'ignore_cable_type', 'preselected_device_roles', 'preselected_tags', 'show_unconnected', 'show_cables', 'show_logical_connections', 'show_single_cable_logical_conns', 'show_neighbors', 'show_circuit', 'show_power', 'show_wireless', 'draw_default_layout'
] ]
@@ -0,0 +1,18 @@
# Generated by Django 4.1.8 on 2023-04-29 09:17
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('netbox_topology_views', '0002_individualoptions'),
]
operations = [
migrations.AddField(
model_name='individualoptions',
name='show_neighbors',
field=models.BooleanField(default=False),
),
]
+3
View File
@@ -137,6 +137,9 @@ class IndividualOptions(NetBoxModel):
show_single_cable_logical_conns = models.BooleanField( show_single_cable_logical_conns = models.BooleanField(
default=False default=False
) )
show_neighbors = models.BooleanField(
default=False
)
show_circuit = models.BooleanField( show_circuit = models.BooleanField(
default=False default=False
) )
File diff suppressed because one or more lines are too long
@@ -133,6 +133,29 @@ function performGraphDownload() {
document.body.removeChild(tempDownloadLink) document.body.removeChild(tempDownloadLink)
} }
// Download XML
const downloadXmlButton = document.querySelector('#btnDownloadXml')
downloadXmlButton.addEventListener('click', (e) => {
performXmlDownload()
})
function performXmlDownload() {
const tempDownloadLink = document.createElement('a');
fetch('/api/plugins/netbox_topology_views/xml-export/?' + new URLSearchParams(window.location.search)).then(response => response.text())
.then(data => {
var blob = new Blob([data ], { type: "text/plain" });
tempDownloadLink.setAttribute("href", window.URL.createObjectURL(blob));
tempDownloadLink.setAttribute("download", 'topology.xml');
tempDownloadLink.dataset.downloadurl = ["text/plain", tempDownloadLink.download, tempDownloadLink.href].join(":");
tempDownloadLink.click();
});
}
// Theme switching // Theme switching
const observer = new MutationObserver((mutations) => const observer = new MutationObserver((mutations) =>
mutations.forEach((mutation) => { mutations.forEach((mutation) => {
+1 -1
View File
@@ -1,6 +1,6 @@
{ {
"name": "netbox_topology_views", "name": "netbox_topology_views",
"version": "3.4.0", "version": "3.5.0",
"lockfileVersion": 1, "lockfileVersion": 1,
"requires": true, "requires": true,
"dependencies": { "dependencies": {
@@ -1,7 +1,7 @@
{ {
"private": true, "private": true,
"name": "netbox_topology_views", "name": "netbox_topology_views",
"version": "3.4.0", "version": "3.5.0",
"scripts": { "scripts": {
"bundle": "node bundle.js", "bundle": "node bundle.js",
"bundle:styles": "node bundle.js --styles", "bundle:styles": "node bundle.js --styles",
@@ -13,10 +13,10 @@
"vis-util": "^5.0.3" "vis-util": "^5.0.3"
}, },
"devDependencies": { "devDependencies": {
"esbuild": "^0.12.24",
"esbuild-sass-plugin": "^1.5.2",
"@egjs/hammerjs": "^2.0.0", "@egjs/hammerjs": "^2.0.0",
"component-emitter": "^1.3.0", "component-emitter": "^1.3.0",
"esbuild": "^0.12.24",
"esbuild-sass-plugin": "^1.5.2",
"keycharm": "^0.4.0", "keycharm": "^0.4.0",
"timsort": "^0.3.0", "timsort": "^0.3.0",
"uuid": "^8.0.0" "uuid": "^8.0.0"
+317 -189
View File
@@ -3,249 +3,377 @@
"@egjs/hammerjs@^2.0.0": "@egjs/hammerjs@^2.0.0":
version "2.0.17" "integrity" "sha512-XQsZgjm2EcVUiZQf11UBJQfmZeEmOW8DpI1gsFeln6w0ae0ii4dMQEQ0kjl6DspdWX1aGY1/loyXnP0JS06e/A=="
resolved "https://registry.npmjs.org/@egjs/hammerjs/-/hammerjs-2.0.17.tgz" "resolved" "https://registry.npmjs.org/@egjs/hammerjs/-/hammerjs-2.0.17.tgz"
integrity sha512-XQsZgjm2EcVUiZQf11UBJQfmZeEmOW8DpI1gsFeln6w0ae0ii4dMQEQ0kjl6DspdWX1aGY1/loyXnP0JS06e/A== "version" "2.0.17"
dependencies: dependencies:
"@types/hammerjs" "^2.0.36" "@types/hammerjs" "^2.0.36"
"@esbuild/linux-loong64@0.14.54":
"integrity" "sha512-bZBrLAIX1kpWelV0XemxBZllyRmM6vgFQQG2GdNb+r3Fkp0FOh1NJSvekXDs7jq70k4euu1cryLMfU+mTXlEpw=="
"resolved" "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.14.54.tgz"
"version" "0.14.54"
"@types/hammerjs@^2.0.36": "@types/hammerjs@^2.0.36":
version "2.0.41" "integrity" "sha512-ewXv/ceBaJprikMcxCmWU1FKyMAQ2X7a9Gtmzw8fcg2kIePI1crERDM818W+XYrxqdBBOdlf2rm137bU+BltCA=="
resolved "https://registry.npmjs.org/@types/hammerjs/-/hammerjs-2.0.41.tgz" "resolved" "https://registry.npmjs.org/@types/hammerjs/-/hammerjs-2.0.41.tgz"
integrity sha512-ewXv/ceBaJprikMcxCmWU1FKyMAQ2X7a9Gtmzw8fcg2kIePI1crERDM818W+XYrxqdBBOdlf2rm137bU+BltCA== "version" "2.0.41"
anymatch@~3.1.2: "anymatch@~3.1.2":
version "3.1.3" "integrity" "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw=="
resolved "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz" "resolved" "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz"
integrity sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw== "version" "3.1.3"
dependencies: dependencies:
normalize-path "^3.0.0" "normalize-path" "^3.0.0"
picomatch "^2.0.4" "picomatch" "^2.0.4"
binary-extensions@^2.0.0: "binary-extensions@^2.0.0":
version "2.2.0" "integrity" "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA=="
resolved "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz" "resolved" "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz"
integrity sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA== "version" "2.2.0"
braces@~3.0.2: "braces@~3.0.2":
version "3.0.2" "integrity" "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A=="
resolved "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz" "resolved" "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz"
integrity sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A== "version" "3.0.2"
dependencies: dependencies:
fill-range "^7.0.1" "fill-range" "^7.0.1"
"chokidar@>=3.0.0 <4.0.0": "chokidar@>=3.0.0 <4.0.0":
version "3.5.3" "integrity" "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw=="
resolved "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz" "resolved" "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz"
integrity sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw== "version" "3.5.3"
dependencies: dependencies:
anymatch "~3.1.2" "anymatch" "~3.1.2"
braces "~3.0.2" "braces" "~3.0.2"
glob-parent "~5.1.2" "fsevents" "~2.3.2"
is-binary-path "~2.1.0" "glob-parent" "~5.1.2"
is-glob "~4.0.1" "is-binary-path" "~2.1.0"
normalize-path "~3.0.0" "is-glob" "~4.0.1"
readdirp "~3.6.0" "normalize-path" "~3.0.0"
"readdirp" "~3.6.0"
optionalDependencies: optionalDependencies:
fsevents "~2.3.2" "fsevents" "~2.3.2"
component-emitter@^1.3.0: "component-emitter@^1.3.0":
version "1.3.0" "integrity" "sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg=="
resolved "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.0.tgz" "resolved" "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.0.tgz"
integrity sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg== "version" "1.3.0"
esbuild-linux-64@0.14.54: "esbuild-android-64@0.14.54":
version "0.14.54" "integrity" "sha512-Tz2++Aqqz0rJ7kYBfz+iqyE3QMycD4vk7LBRyWaAVFgFtQ/O8EJOnVmTOiDWYZ/uYzB4kvP+bqejYdVKzE5lAQ=="
resolved "https://registry.npmjs.org/esbuild-linux-64/-/esbuild-linux-64-0.14.54.tgz" "resolved" "https://registry.npmjs.org/esbuild-android-64/-/esbuild-android-64-0.14.54.tgz"
integrity sha512-EgjAgH5HwTbtNsTqQOXWApBaPVdDn7XcK+/PtJwZLT1UmpLoznPd8c5CxqsH2dQK3j05YsB3L17T8vE7cp4cCg== "version" "0.14.54"
esbuild-sass-plugin@^1.5.2: "esbuild-android-arm64@0.14.54":
version "1.8.2" "integrity" "sha512-F9E+/QDi9sSkLaClO8SOV6etqPd+5DgJje1F9lOWoNncDdOBL2YF59IhsWATSt0TLZbYCf3pNlTHvVV5VfHdvg=="
resolved "https://registry.npmjs.org/esbuild-sass-plugin/-/esbuild-sass-plugin-1.8.2.tgz" "resolved" "https://registry.npmjs.org/esbuild-android-arm64/-/esbuild-android-arm64-0.14.54.tgz"
integrity sha512-ZBjONsRSpmzMKvxSNohnNXCNaBBVlYLqYhyZtN8leGGkJktCvVMKC6g9V5TWIemk1SEaW2XK+YFxz3Whw3+YYw== "version" "0.14.54"
"esbuild-darwin-64@0.14.54":
"integrity" "sha512-jtdKWV3nBviOd5v4hOpkVmpxsBy90CGzebpbO9beiqUYVMBtSc0AL9zGftFuBon7PNDcdvNCEuQqw2x0wP9yug=="
"resolved" "https://registry.npmjs.org/esbuild-darwin-64/-/esbuild-darwin-64-0.14.54.tgz"
"version" "0.14.54"
"esbuild-darwin-arm64@0.14.54":
"integrity" "sha512-OPafJHD2oUPyvJMrsCvDGkRrVCar5aVyHfWGQzY1dWnzErjrDuSETxwA2HSsyg2jORLY8yBfzc1MIpUkXlctmw=="
"resolved" "https://registry.npmjs.org/esbuild-darwin-arm64/-/esbuild-darwin-arm64-0.14.54.tgz"
"version" "0.14.54"
"esbuild-freebsd-64@0.14.54":
"integrity" "sha512-OKwd4gmwHqOTp4mOGZKe/XUlbDJ4Q9TjX0hMPIDBUWWu/kwhBAudJdBoxnjNf9ocIB6GN6CPowYpR/hRCbSYAg=="
"resolved" "https://registry.npmjs.org/esbuild-freebsd-64/-/esbuild-freebsd-64-0.14.54.tgz"
"version" "0.14.54"
"esbuild-freebsd-arm64@0.14.54":
"integrity" "sha512-sFwueGr7OvIFiQT6WeG0jRLjkjdqWWSrfbVwZp8iMP+8UHEHRBvlaxL6IuKNDwAozNUmbb8nIMXa7oAOARGs1Q=="
"resolved" "https://registry.npmjs.org/esbuild-freebsd-arm64/-/esbuild-freebsd-arm64-0.14.54.tgz"
"version" "0.14.54"
"esbuild-linux-32@0.14.54":
"integrity" "sha512-1ZuY+JDI//WmklKlBgJnglpUL1owm2OX+8E1syCD6UAxcMM/XoWd76OHSjl/0MR0LisSAXDqgjT3uJqT67O3qw=="
"resolved" "https://registry.npmjs.org/esbuild-linux-32/-/esbuild-linux-32-0.14.54.tgz"
"version" "0.14.54"
"esbuild-linux-64@0.14.54":
"integrity" "sha512-EgjAgH5HwTbtNsTqQOXWApBaPVdDn7XcK+/PtJwZLT1UmpLoznPd8c5CxqsH2dQK3j05YsB3L17T8vE7cp4cCg=="
"resolved" "https://registry.npmjs.org/esbuild-linux-64/-/esbuild-linux-64-0.14.54.tgz"
"version" "0.14.54"
"esbuild-linux-arm@0.14.54":
"integrity" "sha512-qqz/SjemQhVMTnvcLGoLOdFpCYbz4v4fUo+TfsWG+1aOu70/80RV6bgNpR2JCrppV2moUQkww+6bWxXRL9YMGw=="
"resolved" "https://registry.npmjs.org/esbuild-linux-arm/-/esbuild-linux-arm-0.14.54.tgz"
"version" "0.14.54"
"esbuild-linux-arm64@0.14.54":
"integrity" "sha512-WL71L+0Rwv+Gv/HTmxTEmpv0UgmxYa5ftZILVi2QmZBgX3q7+tDeOQNqGtdXSdsL8TQi1vIaVFHUPDe0O0kdig=="
"resolved" "https://registry.npmjs.org/esbuild-linux-arm64/-/esbuild-linux-arm64-0.14.54.tgz"
"version" "0.14.54"
"esbuild-linux-mips64le@0.14.54":
"integrity" "sha512-qTHGQB8D1etd0u1+sB6p0ikLKRVuCWhYQhAHRPkO+OF3I/iSlTKNNS0Lh2Oc0g0UFGguaFZZiPJdJey3AGpAlw=="
"resolved" "https://registry.npmjs.org/esbuild-linux-mips64le/-/esbuild-linux-mips64le-0.14.54.tgz"
"version" "0.14.54"
"esbuild-linux-ppc64le@0.14.54":
"integrity" "sha512-j3OMlzHiqwZBDPRCDFKcx595XVfOfOnv68Ax3U4UKZ3MTYQB5Yz3X1mn5GnodEVYzhtZgxEBidLWeIs8FDSfrQ=="
"resolved" "https://registry.npmjs.org/esbuild-linux-ppc64le/-/esbuild-linux-ppc64le-0.14.54.tgz"
"version" "0.14.54"
"esbuild-linux-riscv64@0.14.54":
"integrity" "sha512-y7Vt7Wl9dkOGZjxQZnDAqqn+XOqFD7IMWiewY5SPlNlzMX39ocPQlOaoxvT4FllA5viyV26/QzHtvTjVNOxHZg=="
"resolved" "https://registry.npmjs.org/esbuild-linux-riscv64/-/esbuild-linux-riscv64-0.14.54.tgz"
"version" "0.14.54"
"esbuild-linux-s390x@0.14.54":
"integrity" "sha512-zaHpW9dziAsi7lRcyV4r8dhfG1qBidQWUXweUjnw+lliChJqQr+6XD71K41oEIC3Mx1KStovEmlzm+MkGZHnHA=="
"resolved" "https://registry.npmjs.org/esbuild-linux-s390x/-/esbuild-linux-s390x-0.14.54.tgz"
"version" "0.14.54"
"esbuild-netbsd-64@0.14.54":
"integrity" "sha512-PR01lmIMnfJTgeU9VJTDY9ZerDWVFIUzAtJuDHwwceppW7cQWjBBqP48NdeRtoP04/AtO9a7w3viI+PIDr6d+w=="
"resolved" "https://registry.npmjs.org/esbuild-netbsd-64/-/esbuild-netbsd-64-0.14.54.tgz"
"version" "0.14.54"
"esbuild-openbsd-64@0.14.54":
"integrity" "sha512-Qyk7ikT2o7Wu76UsvvDS5q0amJvmRzDyVlL0qf5VLsLchjCa1+IAvd8kTBgUxD7VBUUVgItLkk609ZHUc1oCaw=="
"resolved" "https://registry.npmjs.org/esbuild-openbsd-64/-/esbuild-openbsd-64-0.14.54.tgz"
"version" "0.14.54"
"esbuild-sass-plugin@^1.5.2":
"integrity" "sha512-ZBjONsRSpmzMKvxSNohnNXCNaBBVlYLqYhyZtN8leGGkJktCvVMKC6g9V5TWIemk1SEaW2XK+YFxz3Whw3+YYw=="
"resolved" "https://registry.npmjs.org/esbuild-sass-plugin/-/esbuild-sass-plugin-1.8.2.tgz"
"version" "1.8.2"
dependencies: dependencies:
esbuild "^0.14.5" "esbuild" "^0.14.5"
picomatch "^2.3.0" "picomatch" "^2.3.0"
resolve "^1.20.0" "resolve" "^1.20.0"
sass "^1.45.0" "sass" "^1.45.0"
esbuild@^0.12.24: "esbuild-sunos-64@0.14.54":
version "0.12.29" "integrity" "sha512-28GZ24KmMSeKi5ueWzMcco6EBHStL3B6ubM7M51RmPwXQGLe0teBGJocmWhgwccA1GeFXqxzILIxXpHbl9Q/Kw=="
resolved "https://registry.npmjs.org/esbuild/-/esbuild-0.12.29.tgz" "resolved" "https://registry.npmjs.org/esbuild-sunos-64/-/esbuild-sunos-64-0.14.54.tgz"
integrity sha512-w/XuoBCSwepyiZtIRsKsetiLDUVGPVw1E/R3VTFSecIy8UR7Cq3SOtwKHJMFoVqqVG36aGkzh4e8BvpO1Fdc7g== "version" "0.14.54"
esbuild@^0.14.5: "esbuild-windows-32@0.14.54":
version "0.14.54" "integrity" "sha512-T+rdZW19ql9MjS7pixmZYVObd9G7kcaZo+sETqNH4RCkuuYSuv9AGHUVnPoP9hhuE1WM1ZimHz1CIBHBboLU7w=="
resolved "https://registry.npmjs.org/esbuild/-/esbuild-0.14.54.tgz" "resolved" "https://registry.npmjs.org/esbuild-windows-32/-/esbuild-windows-32-0.14.54.tgz"
integrity sha512-Cy9llcy8DvET5uznocPyqL3BFRrFXSVqbgpMJ9Wz8oVjZlh/zUSNbPRbov0VX7VxN2JH1Oa0uNxZ7eLRb62pJA== "version" "0.14.54"
"esbuild-windows-64@0.14.54":
"integrity" "sha512-AoHTRBUuYwXtZhjXZbA1pGfTo8cJo3vZIcWGLiUcTNgHpJJMC1rVA44ZereBHMJtotyN71S8Qw0npiCIkW96cQ=="
"resolved" "https://registry.npmjs.org/esbuild-windows-64/-/esbuild-windows-64-0.14.54.tgz"
"version" "0.14.54"
"esbuild-windows-arm64@0.14.54":
"integrity" "sha512-M0kuUvXhot1zOISQGXwWn6YtS+Y/1RT9WrVIOywZnJHo3jCDyewAc79aKNQWFCQm+xNHVTq9h8dZKvygoXQQRg=="
"resolved" "https://registry.npmjs.org/esbuild-windows-arm64/-/esbuild-windows-arm64-0.14.54.tgz"
"version" "0.14.54"
"esbuild@^0.12.24":
"integrity" "sha512-w/XuoBCSwepyiZtIRsKsetiLDUVGPVw1E/R3VTFSecIy8UR7Cq3SOtwKHJMFoVqqVG36aGkzh4e8BvpO1Fdc7g=="
"resolved" "https://registry.npmjs.org/esbuild/-/esbuild-0.12.29.tgz"
"version" "0.12.29"
"esbuild@^0.14.5":
"integrity" "sha512-Cy9llcy8DvET5uznocPyqL3BFRrFXSVqbgpMJ9Wz8oVjZlh/zUSNbPRbov0VX7VxN2JH1Oa0uNxZ7eLRb62pJA=="
"resolved" "https://registry.npmjs.org/esbuild/-/esbuild-0.14.54.tgz"
"version" "0.14.54"
dependencies:
"@esbuild/linux-loong64" "0.14.54"
"esbuild-android-64" "0.14.54"
"esbuild-android-arm64" "0.14.54"
"esbuild-darwin-64" "0.14.54"
"esbuild-darwin-arm64" "0.14.54"
"esbuild-freebsd-64" "0.14.54"
"esbuild-freebsd-arm64" "0.14.54"
"esbuild-linux-32" "0.14.54"
"esbuild-linux-64" "0.14.54"
"esbuild-linux-arm" "0.14.54"
"esbuild-linux-arm64" "0.14.54"
"esbuild-linux-mips64le" "0.14.54"
"esbuild-linux-ppc64le" "0.14.54"
"esbuild-linux-riscv64" "0.14.54"
"esbuild-linux-s390x" "0.14.54"
"esbuild-netbsd-64" "0.14.54"
"esbuild-openbsd-64" "0.14.54"
"esbuild-sunos-64" "0.14.54"
"esbuild-windows-32" "0.14.54"
"esbuild-windows-64" "0.14.54"
"esbuild-windows-arm64" "0.14.54"
optionalDependencies: optionalDependencies:
"@esbuild/linux-loong64" "0.14.54" "@esbuild/linux-loong64" "0.14.54"
esbuild-android-64 "0.14.54" "esbuild-android-64" "0.14.54"
esbuild-android-arm64 "0.14.54" "esbuild-android-arm64" "0.14.54"
esbuild-darwin-64 "0.14.54" "esbuild-darwin-64" "0.14.54"
esbuild-darwin-arm64 "0.14.54" "esbuild-darwin-arm64" "0.14.54"
esbuild-freebsd-64 "0.14.54" "esbuild-freebsd-64" "0.14.54"
esbuild-freebsd-arm64 "0.14.54" "esbuild-freebsd-arm64" "0.14.54"
esbuild-linux-32 "0.14.54" "esbuild-linux-32" "0.14.54"
esbuild-linux-64 "0.14.54" "esbuild-linux-64" "0.14.54"
esbuild-linux-arm "0.14.54" "esbuild-linux-arm" "0.14.54"
esbuild-linux-arm64 "0.14.54" "esbuild-linux-arm64" "0.14.54"
esbuild-linux-mips64le "0.14.54" "esbuild-linux-mips64le" "0.14.54"
esbuild-linux-ppc64le "0.14.54" "esbuild-linux-ppc64le" "0.14.54"
esbuild-linux-riscv64 "0.14.54" "esbuild-linux-riscv64" "0.14.54"
esbuild-linux-s390x "0.14.54" "esbuild-linux-s390x" "0.14.54"
esbuild-netbsd-64 "0.14.54" "esbuild-netbsd-64" "0.14.54"
esbuild-openbsd-64 "0.14.54" "esbuild-openbsd-64" "0.14.54"
esbuild-sunos-64 "0.14.54" "esbuild-sunos-64" "0.14.54"
esbuild-windows-32 "0.14.54" "esbuild-windows-32" "0.14.54"
esbuild-windows-64 "0.14.54" "esbuild-windows-64" "0.14.54"
esbuild-windows-arm64 "0.14.54" "esbuild-windows-arm64" "0.14.54"
fill-range@^7.0.1: "fill-range@^7.0.1":
version "7.0.1" "integrity" "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ=="
resolved "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz" "resolved" "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz"
integrity sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ== "version" "7.0.1"
dependencies: dependencies:
to-regex-range "^5.0.1" "to-regex-range" "^5.0.1"
function-bind@^1.1.1: "fsevents@~2.3.2":
version "1.1.1" "integrity" "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA=="
resolved "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz" "resolved" "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz"
integrity sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A== "version" "2.3.2"
glob-parent@~5.1.2: "function-bind@^1.1.1":
version "5.1.2" "integrity" "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A=="
resolved "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz" "resolved" "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz"
integrity sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow== "version" "1.1.1"
"glob-parent@~5.1.2":
"integrity" "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow=="
"resolved" "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz"
"version" "5.1.2"
dependencies: dependencies:
is-glob "^4.0.1" "is-glob" "^4.0.1"
has@^1.0.3: "has@^1.0.3":
version "1.0.3" "integrity" "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw=="
resolved "https://registry.npmjs.org/has/-/has-1.0.3.tgz" "resolved" "https://registry.npmjs.org/has/-/has-1.0.3.tgz"
integrity sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw== "version" "1.0.3"
dependencies: dependencies:
function-bind "^1.1.1" "function-bind" "^1.1.1"
immutable@^4.0.0: "immutable@^4.0.0":
version "4.1.0" "integrity" "sha512-oNkuqVTA8jqG1Q6c+UglTOD1xhC1BtjKI7XkCXRkZHrN5m18/XsnUp8Q89GkQO/z+0WjonSvl0FLhDYftp46nQ=="
resolved "https://registry.npmjs.org/immutable/-/immutable-4.1.0.tgz" "resolved" "https://registry.npmjs.org/immutable/-/immutable-4.1.0.tgz"
integrity sha512-oNkuqVTA8jqG1Q6c+UglTOD1xhC1BtjKI7XkCXRkZHrN5m18/XsnUp8Q89GkQO/z+0WjonSvl0FLhDYftp46nQ== "version" "4.1.0"
is-binary-path@~2.1.0: "is-binary-path@~2.1.0":
version "2.1.0" "integrity" "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw=="
resolved "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz" "resolved" "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz"
integrity sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw== "version" "2.1.0"
dependencies: dependencies:
binary-extensions "^2.0.0" "binary-extensions" "^2.0.0"
is-core-module@^2.9.0: "is-core-module@^2.9.0":
version "2.11.0" "integrity" "sha512-RRjxlvLDkD1YJwDbroBHMb+cukurkDWNyHx7D3oNB5x9rb5ogcksMC5wHCadcXoo67gVr/+3GFySh3134zi6rw=="
resolved "https://registry.npmjs.org/is-core-module/-/is-core-module-2.11.0.tgz" "resolved" "https://registry.npmjs.org/is-core-module/-/is-core-module-2.11.0.tgz"
integrity sha512-RRjxlvLDkD1YJwDbroBHMb+cukurkDWNyHx7D3oNB5x9rb5ogcksMC5wHCadcXoo67gVr/+3GFySh3134zi6rw== "version" "2.11.0"
dependencies: dependencies:
has "^1.0.3" "has" "^1.0.3"
is-extglob@^2.1.1: "is-extglob@^2.1.1":
version "2.1.1" "integrity" "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ=="
resolved "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz" "resolved" "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz"
integrity sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ== "version" "2.1.1"
is-glob@^4.0.1, is-glob@~4.0.1: "is-glob@^4.0.1", "is-glob@~4.0.1":
version "4.0.3" "integrity" "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg=="
resolved "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz" "resolved" "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz"
integrity sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg== "version" "4.0.3"
dependencies: dependencies:
is-extglob "^2.1.1" "is-extglob" "^2.1.1"
is-number@^7.0.0: "is-number@^7.0.0":
version "7.0.0" "integrity" "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng=="
resolved "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz" "resolved" "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz"
integrity sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng== "version" "7.0.0"
"keycharm@^0.2.0 || ^0.3.0 || ^0.4.0", keycharm@^0.4.0: "keycharm@^0.4.0":
version "0.4.0" "integrity" "sha512-TyQTtsabOVv3MeOpR92sIKk/br9wxS+zGj4BG7CR8YbK4jM3tyIBaF0zhzeBUMx36/Q/iQLOKKOT+3jOQtemRQ=="
resolved "https://registry.npmjs.org/keycharm/-/keycharm-0.4.0.tgz" "resolved" "https://registry.npmjs.org/keycharm/-/keycharm-0.4.0.tgz"
integrity sha512-TyQTtsabOVv3MeOpR92sIKk/br9wxS+zGj4BG7CR8YbK4jM3tyIBaF0zhzeBUMx36/Q/iQLOKKOT+3jOQtemRQ== "version" "0.4.0"
normalize-path@^3.0.0, normalize-path@~3.0.0: "normalize-path@^3.0.0", "normalize-path@~3.0.0":
version "3.0.0" "integrity" "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA=="
resolved "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz" "resolved" "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz"
integrity sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA== "version" "3.0.0"
path-parse@^1.0.7: "path-parse@^1.0.7":
version "1.0.7" "integrity" "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw=="
resolved "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz" "resolved" "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz"
integrity sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw== "version" "1.0.7"
picomatch@^2.0.4, picomatch@^2.2.1, picomatch@^2.3.0: "picomatch@^2.0.4", "picomatch@^2.2.1", "picomatch@^2.3.0":
version "2.3.1" "integrity" "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA=="
resolved "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz" "resolved" "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz"
integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA== "version" "2.3.1"
readdirp@~3.6.0: "readdirp@~3.6.0":
version "3.6.0" "integrity" "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA=="
resolved "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz" "resolved" "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz"
integrity sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA== "version" "3.6.0"
dependencies: dependencies:
picomatch "^2.2.1" "picomatch" "^2.2.1"
resolve@^1.20.0: "resolve@^1.20.0":
version "1.22.1" "integrity" "sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw=="
resolved "https://registry.npmjs.org/resolve/-/resolve-1.22.1.tgz" "resolved" "https://registry.npmjs.org/resolve/-/resolve-1.22.1.tgz"
integrity sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw== "version" "1.22.1"
dependencies: dependencies:
is-core-module "^2.9.0" "is-core-module" "^2.9.0"
path-parse "^1.0.7" "path-parse" "^1.0.7"
supports-preserve-symlinks-flag "^1.0.0" "supports-preserve-symlinks-flag" "^1.0.0"
sass@^1.45.0: "sass@^1.45.0":
version "1.56.2" "integrity" "sha512-ciEJhnyCRwzlBCB+h5cCPM6ie/6f8HrhZMQOf5vlU60Y1bI1rx5Zb0vlDZvaycHsg/MqFfF1Eq2eokAa32iw8w=="
resolved "https://registry.npmjs.org/sass/-/sass-1.56.2.tgz" "resolved" "https://registry.npmjs.org/sass/-/sass-1.56.2.tgz"
integrity sha512-ciEJhnyCRwzlBCB+h5cCPM6ie/6f8HrhZMQOf5vlU60Y1bI1rx5Zb0vlDZvaycHsg/MqFfF1Eq2eokAa32iw8w== "version" "1.56.2"
dependencies: dependencies:
chokidar ">=3.0.0 <4.0.0" "chokidar" ">=3.0.0 <4.0.0"
immutable "^4.0.0" "immutable" "^4.0.0"
source-map-js ">=0.6.2 <2.0.0" "source-map-js" ">=0.6.2 <2.0.0"
"source-map-js@>=0.6.2 <2.0.0": "source-map-js@>=0.6.2 <2.0.0":
version "1.0.2" "integrity" "sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw=="
resolved "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz" "resolved" "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz"
integrity sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw== "version" "1.0.2"
supports-preserve-symlinks-flag@^1.0.0: "supports-preserve-symlinks-flag@^1.0.0":
version "1.0.0" "integrity" "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w=="
resolved "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz" "resolved" "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz"
integrity sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w== "version" "1.0.0"
timsort@^0.3.0: "timsort@^0.3.0":
version "0.3.0" "integrity" "sha512-qsdtZH+vMoCARQtyod4imc2nIJwg9Cc7lPRrw9CzF8ZKR0khdr8+2nX80PBhET3tcyTtJDxAffGh2rXH4tyU8A=="
resolved "https://registry.npmjs.org/timsort/-/timsort-0.3.0.tgz" "resolved" "https://registry.npmjs.org/timsort/-/timsort-0.3.0.tgz"
integrity sha512-qsdtZH+vMoCARQtyod4imc2nIJwg9Cc7lPRrw9CzF8ZKR0khdr8+2nX80PBhET3tcyTtJDxAffGh2rXH4tyU8A== "version" "0.3.0"
to-regex-range@^5.0.1: "to-regex-range@^5.0.1":
version "5.0.1" "integrity" "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ=="
resolved "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz" "resolved" "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz"
integrity sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ== "version" "5.0.1"
dependencies: dependencies:
is-number "^7.0.0" "is-number" "^7.0.0"
"uuid@^3.4.0 || ^7.0.0 || ^8.0.0", "uuid@^7.0.0 || ^8.0.0", uuid@^8.0.0: "uuid@^8.0.0":
version "8.3.2" "integrity" "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg=="
resolved "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz" "resolved" "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz"
integrity sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg== "version" "8.3.2"
vis-data@^7.0.0, vis-data@^7.1.4: "vis-data@^7.1.4":
version "7.1.4" "integrity" "sha512-usy+ePX1XnArNvJ5BavQod7YRuGQE1pjFl+pu7IS6rCom2EBoG0o1ZzCqf3l5US6MW51kYkLR+efxRbnjxNl7w=="
resolved "https://registry.npmjs.org/vis-data/-/vis-data-7.1.4.tgz" "resolved" "https://registry.npmjs.org/vis-data/-/vis-data-7.1.4.tgz"
integrity sha512-usy+ePX1XnArNvJ5BavQod7YRuGQE1pjFl+pu7IS6rCom2EBoG0o1ZzCqf3l5US6MW51kYkLR+efxRbnjxNl7w== "version" "7.1.4"
vis-network@^9.1.2: "vis-network@^9.1.2":
version "9.1.2" "integrity" "sha512-BdapguKg7sk3NvdZaDsM7T6rNhOBFz0/F4ZScxctK4klRzQPLQPTEcmbioXaZhMkkgWymzBR3lFCxL1q+eYyAw=="
resolved "https://registry.npmjs.org/vis-network/-/vis-network-9.1.2.tgz" "resolved" "https://registry.npmjs.org/vis-network/-/vis-network-9.1.2.tgz"
integrity sha512-BdapguKg7sk3NvdZaDsM7T6rNhOBFz0/F4ZScxctK4klRzQPLQPTEcmbioXaZhMkkgWymzBR3lFCxL1q+eYyAw== "version" "9.1.2"
"vis-util@^4.0.0 || ^5.0.0", vis-util@^5.0.1, vis-util@^5.0.3: "vis-util@^5.0.3":
version "5.0.3" "integrity" "sha512-Wf9STUcFrDzK4/Zr7B6epW2Kvm3ORNWF+WiwEz2dpf5RdWkLUXFSbLcuB88n1W6tCdFwVN+v3V4/Xmn9PeL39g=="
resolved "https://registry.npmjs.org/vis-util/-/vis-util-5.0.3.tgz" "resolved" "https://registry.npmjs.org/vis-util/-/vis-util-5.0.3.tgz"
integrity sha512-Wf9STUcFrDzK4/Zr7B6epW2Kvm3ORNWF+WiwEz2dpf5RdWkLUXFSbLcuB88n1W6tCdFwVN+v3V4/Xmn9PeL39g== "version" "5.0.3"
@@ -8,9 +8,13 @@
<div class="controls"> <div class="controls">
<div class="control-group"> <div class="control-group">
{% block extra_controls %}{% endblock %} {% block extra_controls %}{% endblock %}
<a id="btnDownloadXml" class="btn btn-sm btn-info" href="#">
<i class="mdi mdi-download"></i>
Download XML
</a>
<a id="btnDownloadImage" class="btn btn-sm btn-info" href="#"> <a id="btnDownloadImage" class="btn btn-sm btn-info" href="#">
<i class="mdi mdi-download"></i> <i class="mdi mdi-download"></i>
Download Download PNG
</a> </a>
<a class="btn btn-sm btn-danger" data-bs-dismiss="modal"> <a class="btn btn-sm btn-danger" data-bs-dismiss="modal">
<i class="mdi mdi-close-thick"></i> <i class="mdi mdi-close-thick"></i>
@@ -36,4 +40,4 @@
const brokenImage = '{{ broken_image }}'; const brokenImage = '{{ broken_image }}';
const topologyData = {{ topology_data | safe }}; const topologyData = {{ topology_data | safe }};
</script> </script>
<script src="{% static 'netbox_topology_views/js/app.js' %}?ver={{ epoch }} " defer></script> <script src="{% static 'netbox_topology_views/js/app.js' %}?ver={{ epoch }} " defer></script>
@@ -16,9 +16,13 @@
<div class="controls"> <div class="controls">
<div class="control-group"> <div class="control-group">
{% block extra_controls %}{% endblock %} {% block extra_controls %}{% endblock %}
<a id="btnDownloadXml" class="btn btn-sm btn-info" href="#">
<i class="mdi mdi-download"></i>
Download XML
</a>
<a id="btnDownloadImage" class="btn btn-sm btn-info" href="#"> <a id="btnDownloadImage" class="btn btn-sm btn-info" href="#">
<i class="mdi mdi-download"></i> <i class="mdi mdi-download"></i>
Download Download PNG
</a> </a>
</div> </div>
</div> </div>
+208
View File
@@ -2,6 +2,8 @@ from dataclasses import dataclass
from functools import lru_cache from functools import lru_cache
from pathlib import Path from pathlib import Path
from typing import Type from typing import Type
import base64
import xml.dom.minidom
import sys import sys
@@ -9,6 +11,7 @@ from django.conf import settings
from django.db.models import Model from django.db.models import Model
from django.templatetags.static import static from django.templatetags.static import static
from django.utils.text import camel_case_to_spaces, re_camel_case from django.utils.text import camel_case_to_spaces, re_camel_case
from django.contrib import messages
IMAGE_DIR = Path(settings.STATIC_ROOT) / "netbox_topology_views/img" IMAGE_DIR = Path(settings.STATIC_ROOT) / "netbox_topology_views/img"
if sys.version_info >= (3,9,0): if sys.version_info >= (3,9,0):
@@ -103,3 +106,208 @@ def get_model_role(model: Type[Model]) -> Role:
slug=get_model_slug(model), slug=get_model_slug(model),
name=re_camel_case.sub(r" \1", model.__name__), name=re_camel_case.sub(r" \1", model.__name__),
) )
def get_query_settings(request):
save_coords = False
if "save_coords" in request.GET:
if request.GET["save_coords"] == "on":
save_coords = True
# General options overrides
if save_coords == True and settings.PLUGINS_CONFIG["netbox_topology_views"]["allow_coordinates_saving"] == False:
save_coords = False
messages.warning(request, "Coordinate saving not allowed. Setting has been overridden")
elif settings.PLUGINS_CONFIG["netbox_topology_views"]["always_save_coordinates"] == True:
save_coords = True
# Individual options
show_unconnected = False
if "show_unconnected" in request.GET:
if request.GET["show_unconnected"] == "on":
show_unconnected = True
show_power = False
if "show_power" in request.GET:
if request.GET["show_power"] == "on":
show_power = True
show_circuit = False
if "show_circuit" in request.GET:
if request.GET["show_circuit"] == "on":
show_circuit = True
show_logical_connections = False
if "show_logical_connections" in request.GET:
if request.GET["show_logical_connections"] == "on" :
show_logical_connections = True
show_single_cable_logical_conns = False
if "show_single_cable_logical_conns" in request.GET:
if request.GET["show_single_cable_logical_conns"] == "on" :
show_single_cable_logical_conns = True
show_cables = False
if "show_cables" in request.GET:
if request.GET["show_cables"] == "on" :
show_cables = True
show_wireless = False
if "show_wireless" in request.GET:
if request.GET["show_wireless"] == "on" :
show_wireless = True
show_neighbors = False
if "show_neighbors" in request.GET:
if request.GET["show_neighbors"] == "on" :
show_neighbors = True
return save_coords, show_unconnected, show_power, show_circuit, show_logical_connections, show_single_cable_logical_conns, show_cables, show_wireless, show_neighbors
class LinePattern():
wireless = [2, 10, 2, 10]
power = [5, 5, 3, 3]
logical = [1, 10, 1, 10]
def export_data_to_xml(data: dict):
if data is None:
return ''
# static xml header
doc = xml.dom.minidom.Document()
mxfile = doc.createElement('mxfile')
mxfile.setAttribute('host', 'app.diagrams.net')
mxfile.setAttribute('type', 'device')
diagram = doc.createElement('diagram')
diagram.setAttribute('name', 'topology')
diagram.setAttribute('id', 'someid')
doc.appendChild(mxfile)
mxfile.appendChild(diagram)
# mxGraphModel
mxgraphmodel = doc.createElement('mxGraphModel')
mxgraphmodel.setAttribute('dx', '0')
mxgraphmodel.setAttribute('dy', '0')
mxgraphmodel.setAttribute('grid', '0')
mxgraphmodel.setAttribute('guides', '1')
mxgraphmodel.setAttribute('tooltips', '1')
mxgraphmodel.setAttribute('connect', '1')
mxgraphmodel.setAttribute('arrows', '1')
mxgraphmodel.setAttribute('fold', '1')
mxgraphmodel.setAttribute('page', '1')
mxgraphmodel.setAttribute('pageScale', '1')
mxgraphmodel.setAttribute('pageWidth', '827')
mxgraphmodel.setAttribute('pageHeight', '1169')
mxgraphmodel.setAttribute('background', 'none')
mxgraphmodel.setAttribute('math', '0')
mxgraphmodel.setAttribute('shadow', '0')
diagram.appendChild(mxgraphmodel)
# root
root = doc.createElement('root')
mxgraphmodel.appendChild(root)
# mxCells
# header
mxcell = doc.createElement('mxCell')
mxcell.setAttribute('id', '0')
root.appendChild(mxcell)
mxcell = doc.createElement('mxCell')
mxcell.setAttribute('id', '1')
mxcell.setAttribute('parent', '0')
root.appendChild(mxcell)
# edges
for edge in data['edges']:
dashes = ''
if 'dashes' in edge:
if edge['dashes'] == LinePattern().wireless:
dashes = 'dashed=1;dashPattern=3 6;'
if edge['dashes'] == LinePattern().power:
dashes = 'dashed=1;dashPattern=6 6;'
if edge['dashes'] == LinePattern().logical:
dashes = 'dashed=1;dashPattern=1 4;strokeWidth=2;'
mxcell = doc.createElement('mxCell')
mxcell.setAttribute('id', 'edge_' + str(edge['id']))
mxcell.setAttribute('style', 'rounded=0;orthogonalLoop=1;jettySize=auto;html=1;endArrow=none;endFill=0;strokeColor=' + edge['color'] + ';' + dashes)
mxcell.setAttribute('edge', '1')
mxcell.setAttribute('parent', '1')
mxcell.setAttribute('source', 'node_' + str(edge['from']))
mxcell.setAttribute('target', 'node_' + str(edge['to']))
root.appendChild(mxcell)
mxgeometry = doc.createElement('mxGeometry')
mxgeometry.setAttribute('relative', '1')
mxgeometry.setAttribute('as', 'geometry')
mxcell.appendChild(mxgeometry)
# nodes
noPositionX = 0
noPositionY = 1000
for node in data['nodes']:
with open(str(Path(settings.BASE_DIR)) + node['image'], "rb") as img:
svg = base64.b64encode(img.read()).decode('utf-8')
mxcell = doc.createElement('mxCell')
mxcell.setAttribute('id', 'node_' + str(node['id']))
mxcell.setAttribute('value', str(node['label']))
mxcell.setAttribute('style', 'shape=image;verticalLabelPosition=bottom;verticalAlign=top;imageAspect=0;aspect=fixed;image=data:image/svg+xml,' + svg + ';perimeter=elippsePerimeter;fontSize=10;strokeWidth=1;fontColor=#000000;')
mxcell.setAttribute('vertex', '1')
mxcell.setAttribute('parent', '1')
root.appendChild(mxcell)
mxgeometry = doc.createElement('mxGeometry')
# Check if x and y values are stored in the database and set pseudo
# coordinates if not. Otherwise icons will not be exported / KeyError is raised
if 'x' in node:
mxgeometry.setAttribute('x', str(node['x']))
else:
# Set next pseudo coordinates
if noPositionX > 2500:
noPositionX = 100
noPositionY = noPositionY + 100
else:
noPositionX = noPositionX + 100
mxgeometry.setAttribute('x', str(noPositionX))
if 'y' in node:
mxgeometry.setAttribute('y', str(node['y']))
else:
mxgeometry.setAttribute('y', str(noPositionY))
mxgeometry.setAttribute('width', '50')
mxgeometry.setAttribute('height', '50')
mxgeometry.setAttribute('as', 'geometry')
mxcell.appendChild(mxgeometry)
# Place a warning if one or more icons are misplaced because of missing coordinates
if noPositionX > 0:
mxcell = doc.createElement('mxCell')
mxcell.setAttribute('id', 'noPositionWarning')
mxcell.setAttribute('value', 'One or more icons are misplaced. This happens if no coordinates are stored for a device. Turn on coordinates saving and make sure to drag all icons in the topology to a specific position before exporting to XML.')
mxcell.setAttribute('style', 'text;html=1;strokeColor=none;fillColor=none;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;fontColor=#FF0000;fontStyle=0;fontSize=35;labelBackgroundColor=#FFFF99;labelBorderColor=none;strokeWidth=20;')
mxcell.setAttribute('vertex', '1')
mxcell.setAttribute('parent', '1')
root.appendChild(mxcell)
mxgeometry = doc.createElement('mxGeometry')
mxgeometry.setAttribute('x', '0')
mxgeometry.setAttribute('y', '0')
mxgeometry.setAttribute('width', '870')
mxgeometry.setAttribute('height', '100')
mxgeometry.setAttribute('as', 'geometry')
mxcell.appendChild(mxgeometry)
# debug output
# print(doc.toprettyxml())
return doc.toxml(encoding='UTF-8')
+50 -61
View File
@@ -2,6 +2,7 @@ import json
from functools import reduce from functools import reduce
from typing import DefaultDict, Dict, Optional, Union from typing import DefaultDict, Dict, Optional, Union
import time import time
from itertools import chain
from utilities.htmx import is_htmx from utilities.htmx import is_htmx
from circuits.models import Circuit, CircuitTermination from circuits.models import Circuit, CircuitTermination
@@ -38,6 +39,8 @@ from netbox_topology_views.utils import (
get_model_role, get_model_role,
get_model_slug, get_model_slug,
image_static_url, image_static_url,
LinePattern,
get_query_settings
) )
@@ -198,6 +201,7 @@ def create_edge(
edge["id"] = edge_id edge["id"] = edge_id
edge["from"] = termination_a["device_id"] edge["from"] = termination_a["device_id"]
edge["to"] = termination_b["device_id"] edge["to"] = termination_b["device_id"]
edge["color"] = '#2b7ce9'
title = "Cable" title = "Cable"
if circuit is not None: if circuit is not None:
@@ -205,17 +209,17 @@ def create_edge(
title = f"Circuit provider: {circuit['provider_name']}<br>Termination" title = f"Circuit provider: {circuit['provider_name']}<br>Termination"
elif wireless is not None: elif wireless is not None:
edge["dashes"] = [2, 10, 2, 10] edge["dashes"] = LinePattern().wireless
title = "Wireless Connection" title = "Wireless Connection"
elif power is not None: elif power is not None:
edge["dashes"] = [5, 5, 3, 3] edge["dashes"] = LinePattern().power
title = "Power Connection" title = "Power Connection"
elif interface is not None: elif interface is not None:
title = "Interface Connection" title = "Interface Connection"
edge["width"] = 3 edge["width"] = 3
edge["dashes"] = [1, 10, 1, 10] edge["dashes"] = LinePattern().logical
edge["color"] = '#f1c232' edge["color"] = '#f1c232'
edge["href"] = interface.get_absolute_url() + "trace" edge["href"] = interface.get_absolute_url() + "trace"
@@ -260,6 +264,7 @@ def get_topology_data(
show_circuit: bool, show_circuit: bool,
show_logical_connections: bool, show_logical_connections: bool,
show_single_cable_logical_conns: bool, show_single_cable_logical_conns: bool,
show_neighbors: bool,
show_power: bool, show_power: bool,
show_wireless: bool, show_wireless: bool,
): ):
@@ -287,6 +292,31 @@ def get_topology_data(
device_ids = [d.pk for d in queryset] device_ids = [d.pk for d in queryset]
site_ids = [d.site_id for d in queryset] site_ids = [d.site_id for d in queryset]
if show_neighbors:
interfaces = Interface.objects.filter(
Q(device_id__in=device_ids)
)
frontports = FrontPort.objects.filter(
Q(device_id__in=device_ids)
)
rearports = RearPort.objects.filter(
Q(device_id__in=device_ids)
)
ports = chain(interfaces, frontports, rearports)
for port in ports:
for link_peer in port.link_peers:
if link_peer.device.id not in device_ids:
device_ids.append(link_peer.device.id)
if show_logical_connections:
path_complete_interfaces = Interface.objects.filter(
Q(_path__is_complete=True) & Q(device_id__in=device_ids)
)
for path_complete_interface in path_complete_interfaces:
for connected_endpoint in path_complete_interface.connected_endpoints:
device_ids.append(connected_endpoint.device.id)
if show_circuit: if show_circuit:
circuit_terminations = CircuitTermination.objects.filter( circuit_terminations = CircuitTermination.objects.filter(
Q(site_id__in=site_ids) | Q(provider_network__isnull=False) Q(site_id__in=site_ids) | Q(provider_network__isnull=False)
@@ -596,65 +626,22 @@ class TopologyHomeView(PermissionRequiredMixin, View):
) )
if request.GET: if request.GET:
save_coords = False
if "save_coords" in request.GET:
if request.GET["save_coords"] == "on":
save_coords = True
# General options overrides
if save_coords == True and settings.PLUGINS_CONFIG["netbox_topology_views"]["allow_coordinates_saving"] == False:
save_coords = False
messages.warning(request, "Coordinate saving not allowed. Setting has been overridden")
elif settings.PLUGINS_CONFIG["netbox_topology_views"]["always_save_coordinates"] == True:
save_coords = True
# Individual options
show_unconnected = False
if "show_unconnected" in request.GET:
if request.GET["show_unconnected"] == "on":
show_unconnected = True
show_power = False
if "show_power" in request.GET:
if request.GET["show_power"] == "on":
show_power = True
show_circuit = False
if "show_circuit" in request.GET:
if request.GET["show_circuit"] == "on":
show_circuit = True
show_logical_connections = False
if "show_logical_connections" in request.GET:
if request.GET["show_logical_connections"] == "on" :
show_logical_connections = True
show_single_cable_logical_conns = False
if "show_single_cable_logical_conns" in request.GET:
if request.GET["show_single_cable_logical_conns"] == "on" :
show_single_cable_logical_conns = True
show_cables = False
if "show_cables" in request.GET:
if request.GET["show_cables"] == "on" :
show_cables = True
show_wireless = False
if "show_wireless" in request.GET:
if request.GET["show_wireless"] == "on" :
show_wireless = True
save_coords, show_unconnected, show_power, show_circuit, show_logical_connections, show_single_cable_logical_conns, show_cables, show_wireless, show_neighbors = get_query_settings(request)
if not "draw_init" in request.GET or "draw_init" in request.GET and request.GET["draw_init"].lower() == "true": if not "draw_init" in request.GET or "draw_init" in request.GET and request.GET["draw_init"].lower() == "true":
topo_data = get_topology_data( topo_data = get_topology_data(
self.queryset, queryset=self.queryset,
individualOptions, individualOptions=individualOptions,
show_unconnected, save_coords=save_coords,
save_coords, show_unconnected=show_unconnected,
show_cables, show_cables=show_cables,
show_circuit, show_logical_connections=show_logical_connections,
show_logical_connections, show_single_cable_logical_conns=show_single_cable_logical_conns,
show_single_cable_logical_conns, show_neighbors=show_neighbors,
show_power, show_circuit=show_circuit,
show_wireless, show_power=show_power,
show_wireless=show_wireless,
) )
else: else:
@@ -670,6 +657,7 @@ class TopologyHomeView(PermissionRequiredMixin, View):
if individualOptions.show_cables: q['show_cables'] = "on" if individualOptions.show_cables: q['show_cables'] = "on"
if individualOptions.show_logical_connections: q['show_logical_connections'] = "on" if individualOptions.show_logical_connections: q['show_logical_connections'] = "on"
if individualOptions.show_single_cable_logical_conns: q['show_single_cable_logical_conns'] = "on" if individualOptions.show_single_cable_logical_conns: q['show_single_cable_logical_conns'] = "on"
if individualOptions.show_neighbors: q['show_neighbors'] = "on"
if individualOptions.show_circuit: q['show_circuit'] = "on" if individualOptions.show_circuit: q['show_circuit'] = "on"
if individualOptions.show_power: q['show_power'] = "on" if individualOptions.show_power: q['show_power'] = "on"
if individualOptions.show_wireless: q['show_wireless'] = "on" if individualOptions.show_wireless: q['show_wireless'] = "on"
@@ -690,7 +678,7 @@ class TopologyHomeView(PermissionRequiredMixin, View):
"filter_form": DeviceFilterForm(request.GET, label_suffix=""), "filter_form": DeviceFilterForm(request.GET, label_suffix=""),
"topology_data": json.dumps(topo_data), "topology_data": json.dumps(topo_data),
"broken_image": find_image_url("role-unknown"), "broken_image": find_image_url("role-unknown"),
"epoch": int(time.time()), "epoch": int(time.time())
}, },
) )
@@ -701,7 +689,7 @@ class TopologyHomeView(PermissionRequiredMixin, View):
"filter_form": DeviceFilterForm(request.GET, label_suffix=""), "filter_form": DeviceFilterForm(request.GET, label_suffix=""),
"topology_data": json.dumps(topo_data), "topology_data": json.dumps(topo_data),
"broken_image": find_image_url("role-unknown"), "broken_image": find_image_url("role-unknown"),
"model": self.model, "model": self.model
}, },
) )
@@ -792,6 +780,7 @@ class TopologyIndividualOptionsView(PermissionRequiredMixin, View):
'show_cables': queryset.show_cables, 'show_cables': queryset.show_cables,
'show_logical_connections': queryset.show_logical_connections, 'show_logical_connections': queryset.show_logical_connections,
'show_single_cable_logical_conns': queryset.show_single_cable_logical_conns, 'show_single_cable_logical_conns': queryset.show_single_cable_logical_conns,
'show_neighbors': queryset.show_neighbors,
'show_circuit': queryset.show_circuit, 'show_circuit': queryset.show_circuit,
'show_power': queryset.show_power, 'show_power': queryset.show_power,
'show_wireless': queryset.show_wireless, 'show_wireless': queryset.show_wireless,
+1 -1
View File
@@ -7,7 +7,7 @@ long_description = readme.read_text()
setup( setup(
name="netbox-topology-views", name="netbox-topology-views",
version="3.4.0", version="3.5.0",
description="An NetBox plugin to create Topology maps", description="An NetBox plugin to create Topology maps",
long_description=long_description, long_description=long_description,
long_description_content_type="text/markdown", long_description_content_type="text/markdown",