NetBox Customer Export plugin with configurable branding
Plugin for NetBox 4.6.8–4.6.99 that exports customer documentation (PDF, Word, HTML preview) for a site, location, tenant or tenant group. Technicians pick sections, device roles and columns before exporting. Branding profiles (colors, font, logo, footer, Word template incl. .dotx, table style, extra CSS) are configurable via PLUGINS_CONFIG and selectable per export. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,79 @@
|
||||
from django import forms
|
||||
from dcim.models import DeviceRole, Location, Site
|
||||
from netbox.plugins import get_plugin_config
|
||||
from tenancy.models import Tenant, TenantGroup
|
||||
from utilities.forms.fields import DynamicModelChoiceField
|
||||
|
||||
from .branding import get_brandings
|
||||
from .sections import DEFAULT_DEVICE_COLUMNS, DEVICE_COLUMNS, SECTIONS
|
||||
|
||||
FORMAT_CHOICES = (("pdf", "PDF"), ("docx", "Word (.docx)"), ("html", "HTML-Vorschau"))
|
||||
|
||||
|
||||
class ScopeSelectForm(forms.Form):
|
||||
site = DynamicModelChoiceField(queryset=Site.objects.all(), required=False, label="Standort")
|
||||
location = DynamicModelChoiceField(queryset=Location.objects.all(), required=False, label="Lokation")
|
||||
tenant = DynamicModelChoiceField(queryset=Tenant.objects.all(), required=False, label="Mandant")
|
||||
tenantgroup = DynamicModelChoiceField(queryset=TenantGroup.objects.all(), required=False, label="Mandantengruppe")
|
||||
|
||||
def clean(self):
|
||||
data = super().clean()
|
||||
chosen = [(k, v) for k, v in data.items() if v]
|
||||
if len(chosen) != 1:
|
||||
raise forms.ValidationError("Bitte genau einen Scope auswählen.")
|
||||
data["kind"], data["obj"] = chosen[0]
|
||||
return data
|
||||
|
||||
|
||||
class ExportForm(forms.Form):
|
||||
format = forms.ChoiceField(choices=FORMAT_CHOICES, initial="pdf", label="Format")
|
||||
title = forms.CharField(max_length=200, label="Titel")
|
||||
subtitle = forms.CharField(max_length=200, required=False, label="Untertitel")
|
||||
author = forms.CharField(max_length=100, required=False, label="Erstellt von")
|
||||
branding = forms.ChoiceField(choices=(), label="Design / Firma")
|
||||
|
||||
sections = forms.MultipleChoiceField(
|
||||
choices=[(s.key, s.label) for s in SECTIONS.values()],
|
||||
initial=[s.key for s in SECTIONS.values() if s.default],
|
||||
widget=forms.CheckboxSelectMultiple,
|
||||
label="Inhalte",
|
||||
)
|
||||
|
||||
roles = forms.ModelMultipleChoiceField(
|
||||
queryset=DeviceRole.objects.none(), required=False, widget=forms.CheckboxSelectMultiple, label="Geräterollen"
|
||||
)
|
||||
device_columns = forms.MultipleChoiceField(
|
||||
choices=[(k, v[0]) for k, v in DEVICE_COLUMNS.items()],
|
||||
initial=DEFAULT_DEVICE_COLUMNS,
|
||||
required=False,
|
||||
widget=forms.CheckboxSelectMultiple,
|
||||
label="Gerätespalten",
|
||||
)
|
||||
group_by_site = forms.BooleanField(required=False, initial=True, label="Geräte nach Standort gruppieren")
|
||||
|
||||
rack_svg = forms.BooleanField(required=False, initial=True, label="Rack-Ansichten (SVG) einbetten")
|
||||
rack_faces = forms.MultipleChoiceField(
|
||||
choices=(("front", "Vorderseite"), ("rear", "Rückseite")),
|
||||
initial=["front"],
|
||||
required=False,
|
||||
widget=forms.CheckboxSelectMultiple,
|
||||
label="Rack-Seiten",
|
||||
)
|
||||
rack_devices = forms.BooleanField(required=False, initial=True, label="Geräteliste je Rack")
|
||||
rack_pagebreak = forms.BooleanField(required=False, initial=True, label="Seitenumbruch nach jedem Rack")
|
||||
|
||||
include_empty = forms.BooleanField(required=False, label="Leere Abschnitte trotzdem aufführen")
|
||||
include_secrets = forms.BooleanField(required=False, label="Geheimnisse (z.B. WLAN-PSK) mit exportieren")
|
||||
|
||||
def __init__(self, *args, scope=None, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
self.fields["branding"].choices = [(k, b["name"]) for k, b in get_brandings().items()]
|
||||
if scope is not None:
|
||||
role_ids = scope.devices().values("role")
|
||||
roles = DeviceRole.objects.filter(pk__in=role_ids).order_by("name")
|
||||
self.fields["roles"].queryset = roles
|
||||
self.fields["roles"].initial = list(roles)
|
||||
self.fields["title"].initial = f"Netzwerkdokumentation {scope.obj}"
|
||||
self.fields["subtitle"].initial = f"{scope.label}: {scope.obj}"
|
||||
if not get_plugin_config("netbox_customer_export", "allow_secrets"):
|
||||
del self.fields["include_secrets"]
|
||||
Reference in New Issue
Block a user