Files
NetBox-Customer-Export/netbox_customer_export/branding_views.py
T
MrBlakeandClaude Opus 5.5 f24e42a516 feat: manage branding profiles in the NetBox UI
New BrandingProfile model (migration 0001) with logo upload, color
pickers, font, Word template upload, table style and extra CSS, plus
list/edit/delete views, an HTML preview and a menu entry with add
button. Access is controlled via the model's Django permissions.

Profiles from the database are offered first in the export form (the
default profile preselected); profiles from PLUGINS_CONFIG still work.

Bump version to 0.2.0 - requires "manage.py migrate".

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-23 13:21:11 +02:00

94 lines
3.7 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""Verwaltung der Branding-Profile in der NetBox-Oberfläche."""
from django.contrib import messages
from django.contrib.auth.mixins import PermissionRequiredMixin
from django.http import HttpResponse
from django.shortcuts import get_object_or_404, redirect, render
from django.utils import timezone
from django.views import View
from .blocks import KV, P, T, Document, H
from .branding import _normalize
from .forms import BrandingProfileForm
from .models import BrandingProfile
from .renderers.html import render_html
APP = "netbox_customer_export"
class BrandingListView(PermissionRequiredMixin, View):
permission_required = f"{APP}.view_brandingprofile"
def get(self, request):
return render(request, f"{APP}/branding_list.html", {"profiles": BrandingProfile.objects.all()})
class BrandingEditView(PermissionRequiredMixin, View):
template_name = f"{APP}/branding_edit.html"
def get_permission_required(self):
return [f"{APP}.{'change' if self.kwargs.get('pk') else 'add'}_brandingprofile"]
def _obj(self, pk):
return get_object_or_404(BrandingProfile, pk=pk) if pk else BrandingProfile()
def get(self, request, pk=None):
obj = self._obj(pk)
return render(request, self.template_name, {"object": obj, "form": BrandingProfileForm(instance=obj)})
def post(self, request, pk=None):
obj = self._obj(pk)
form = BrandingProfileForm(request.POST, request.FILES, instance=obj)
if form.is_valid():
obj = form.save()
messages.success(request, f"Branding-Profil „{obj}“ gespeichert.")
return redirect(f"plugins:{APP}:branding_list")
return render(request, self.template_name, {"object": obj, "form": form})
class BrandingDeleteView(PermissionRequiredMixin, View):
permission_required = f"{APP}.delete_brandingprofile"
def get(self, request, pk):
return render(request, f"{APP}/branding_delete.html", {"object": get_object_or_404(BrandingProfile, pk=pk)})
def post(self, request, pk):
obj = get_object_or_404(BrandingProfile, pk=pk)
name = str(obj)
for f in (obj.logo, obj.docx_template):
if f:
f.delete(save=False)
obj.delete()
messages.success(request, f"Branding-Profil „{name}“ gelöscht.")
return redirect(f"plugins:{APP}:branding_list")
class BrandingPreviewView(PermissionRequiredMixin, View):
"""Beispieldokument (HTML) mit dem Profil – zum Prüfen von Farben und Logo."""
permission_required = f"{APP}.view_brandingprofile"
def get(self, request, pk):
obj = get_object_or_404(BrandingProfile, pk=pk)
blocks = [
H(1, "Übersicht"),
KV([("Standort", "Musterstandort"), ("Status", "Aktiv"), ("Anschrift", "Musterstraße 1\n12345 Musterstadt")]),
H(2, "Geräte"),
T(
["Name", "Hersteller / Modell", "Rack / HE", "Management-IP"],
[
["SW-CORE-01", "HPE Aruba 6100", "Rack A / HE 20", "10.0.0.2/24"],
["SW-ACC-01", "HPE Aruba 6000", "Rack A / HE 18", "10.0.0.3/24"],
["FW-01", "Sophos XGS 2100", "Rack A / HE 16", "10.0.0.1/24"],
],
),
P("So sehen Überschriften, Tabellen und Fließtext mit diesem Profil aus."),
]
doc = Document(
title="Netzwerkdokumentation Vorschau",
subtitle="Beispielkunde",
blocks=blocks,
meta={"author": request.user.username, "date": timezone.localtime().strftime("%d.%m.%Y")},
branding=_normalize(obj.as_branding()),
)
return HttpResponse(render_html(doc), content_type="text/html; charset=utf-8")