feat: druckoptimierte PDF-Ansicht für Dokumentationen ergänzen
This commit is contained in:
@@ -11,6 +11,7 @@ Ein in NetBox integriertes Markdown-Wiki für Betriebsdokumentationen und Anleit
|
||||
- Bilder direkt vom eigenen Gerät in die Dokumentation hochladen
|
||||
- Formatierte Word-Inhalte inklusive Tabellen und unterstützten Zwischenablage-Bildern einfügen
|
||||
- Mehrere Dokumentationen mit Ordnern, Zuordnungen und Anhängen als ZIP exportieren und wieder importieren
|
||||
- Druckoptimierte A4-Ansicht zum Drucken oder Speichern als PDF
|
||||
- Eine Dokumentation mehreren Objekten zuordnen und umgekehrt
|
||||
- Unbegrenzt viele Objektzuordnungen pro Dokumentation; nur identische Doppelzuordnungen werden verhindert
|
||||
- Unterstützte Standardobjekte: Region, Standort, Location, Rack, Gerät, VM, VM-Cluster und Mandant/Kunde
|
||||
@@ -21,7 +22,7 @@ Ein in NetBox integriertes Markdown-Wiki für Betriebsdokumentationen und Anleit
|
||||
|
||||
## Kompatibilität
|
||||
|
||||
Die Version `0.4.1` zielt auf NetBox 4.x (mindestens 4.0). Vor einem produktiven Rollout sollte das Plugin gegen die konkret eingesetzte NetBox-Minor-Version in einer Testinstanz geprüft werden.
|
||||
Die Version `0.4.2` zielt auf NetBox 4.x (mindestens 4.0). Vor einem produktiven Rollout sollte das Plugin gegen die konkret eingesetzte NetBox-Minor-Version in einer Testinstanz geprüft werden.
|
||||
|
||||
## Installation
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@ class DocumentationConfig(PluginConfig):
|
||||
name = "netbox_documentation"
|
||||
verbose_name = "NetBox Dokumentation"
|
||||
description = "Wiki und Office-Dokumentation direkt in NetBox"
|
||||
version = "0.4.1"
|
||||
version = "0.4.2"
|
||||
author = "LKE"
|
||||
base_url = "documentation"
|
||||
min_version = "4.0.0"
|
||||
|
||||
@@ -1,5 +1,10 @@
|
||||
{% extends 'generic/object.html' %}
|
||||
{% load helpers %}
|
||||
{% block extra_controls %}
|
||||
<a href="{% url 'plugins:netbox_documentation:document_print' pk=object.pk %}" target="_blank" class="btn btn-outline-secondary">
|
||||
<i class="mdi mdi-printer"></i> Drucken / PDF
|
||||
</a>
|
||||
{% endblock extra_controls %}
|
||||
{% block content %}
|
||||
<div class="row">
|
||||
<div class="col col-md-9">
|
||||
|
||||
@@ -0,0 +1,52 @@
|
||||
<!doctype html>
|
||||
<html lang="de">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>{{ object.title }} – Druckansicht</title>
|
||||
<style>
|
||||
:root { color-scheme: light; }
|
||||
* { box-sizing: border-box; }
|
||||
body { margin: 0; background: #eef1f4; color: #1f2937; font: 11pt Arial, Helvetica, sans-serif; line-height: 1.5; }
|
||||
.toolbar { position: sticky; top: 0; z-index: 2; display: flex; justify-content: center; gap: .75rem; padding: .8rem; background: #fff; border-bottom: 1px solid #ccd2d8; }
|
||||
.toolbar button, .toolbar a { border: 1px solid #6c757d; border-radius: .3rem; padding: .55rem .9rem; background: #fff; color: #212529; text-decoration: none; cursor: pointer; font: inherit; }
|
||||
.toolbar button { color: #fff; background: #4263eb; border-color: #4263eb; }
|
||||
article { width: min(210mm, calc(100% - 2rem)); min-height: 297mm; margin: 1.5rem auto; padding: 18mm 17mm; background: #fff; box-shadow: 0 .2rem 1rem rgba(0,0,0,.12); }
|
||||
h1 { margin: 0 0 .25rem; font-size: 24pt; line-height: 1.2; }
|
||||
.summary { color: #4b5563; font-size: 13pt; margin: 0 0 1rem; }
|
||||
.meta { display: flex; flex-wrap: wrap; gap: .4rem 1.25rem; padding: .65rem 0; margin-bottom: 1.4rem; color: #6b7280; border-top: 1px solid #ddd; border-bottom: 1px solid #ddd; font-size: 9pt; }
|
||||
.content img { max-width: 100%; height: auto; }
|
||||
.content table { width: 100%; border-collapse: collapse; margin: 1em 0; }
|
||||
.content th, .content td { border: 1px solid #777; padding: .35rem .5rem; vertical-align: top; }
|
||||
.content pre, .content code { white-space: pre-wrap; overflow-wrap: anywhere; font-family: Consolas, monospace; }
|
||||
.content a { color: #174ea6; overflow-wrap: anywhere; }
|
||||
@page { size: A4; margin: 15mm; }
|
||||
@media print {
|
||||
body { background: #fff; }
|
||||
.toolbar { display: none !important; }
|
||||
article { width: auto; min-height: 0; margin: 0; padding: 0; box-shadow: none; }
|
||||
a { color: inherit !important; text-decoration: none; }
|
||||
table, figure, img, pre, blockquote { break-inside: avoid; }
|
||||
h1, h2, h3, h4 { break-after: avoid; }
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="toolbar">
|
||||
<button type="button" onclick="window.print()">Drucken / Als PDF speichern</button>
|
||||
<a href="{{ object.get_absolute_url }}">Zurück zur Dokumentation</a>
|
||||
</div>
|
||||
<article>
|
||||
<header>
|
||||
<h1>{{ object.title }}</h1>
|
||||
{% if object.summary %}<p class="summary">{{ object.summary }}</p>{% endif %}
|
||||
<div class="meta">
|
||||
{% if object.category %}<span>Ordner: {{ object.category }}</span>{% endif %}
|
||||
<span>Stand: {{ object.last_updated|date:"d.m.Y H:i" }}</span>
|
||||
{% if object.assignments.all %}<span>Zugeordnet: {% for assignment in object.assignments.all %}{{ assignment.assigned_object }}{% if not forloop.last %}, {% endif %}{% endfor %}</span>{% endif %}
|
||||
</div>
|
||||
</header>
|
||||
<main class="content">{{ object.rendered_body|safe }}</main>
|
||||
</article>
|
||||
</body>
|
||||
</html>
|
||||
@@ -9,6 +9,7 @@ urlpatterns = (
|
||||
path("documents/import/", views.DocumentImportView.as_view(), name="document_import"),
|
||||
path("archive/", views.DocumentArchiveView.as_view(), name="document_archive"),
|
||||
path("documents/<int:pk>/", views.DocumentView.as_view(), name="document"),
|
||||
path("documents/<int:pk>/print/", views.DocumentPrintView.as_view(), name="document_print"),
|
||||
path("documents/<int:pk>/edit/", views.DocumentEditView.as_view(), name="document_edit"),
|
||||
path("documents/<int:pk>/media-upload/", views.DocumentMediaUploadView.as_view(), name="document_media_upload"),
|
||||
path("documents/<int:pk>/delete/", views.DocumentDeleteView.as_view(), name="document_delete"),
|
||||
|
||||
@@ -47,6 +47,18 @@ class DocumentView(generic.ObjectView):
|
||||
queryset = Document.objects.prefetch_related("assignments", "attachments")
|
||||
|
||||
|
||||
class DocumentPrintView(PermissionRequiredMixin, View):
|
||||
permission_required = "netbox_documentation.view_document"
|
||||
template_name = "netbox_documentation/document_print.html"
|
||||
|
||||
def get(self, request, pk):
|
||||
document = get_object_or_404(
|
||||
Document.objects.restrict(request.user, "view").select_related("category").prefetch_related("assignments"),
|
||||
pk=pk,
|
||||
)
|
||||
return render(request, self.template_name, {"object": document})
|
||||
|
||||
|
||||
class DocumentEditView(generic.ObjectEditView):
|
||||
queryset = Document.objects.all()
|
||||
form = DocumentForm
|
||||
|
||||
+1
-1
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
||||
|
||||
[project]
|
||||
name = "netbox-documentation"
|
||||
version = "0.4.1"
|
||||
version = "0.4.2"
|
||||
description = "Integrated Markdown wiki and office document importer for NetBox"
|
||||
readme = "README.md"
|
||||
requires-python = ">=3.10"
|
||||
|
||||
Reference in New Issue
Block a user