feat: ZIP-Export auf durchsuchbare Ordnerauswahl umstellen
This commit is contained in:
@@ -21,7 +21,7 @@ Ein in NetBox integriertes Markdown-Wiki für Betriebsdokumentationen und Anleit
|
||||
|
||||
## Kompatibilität
|
||||
|
||||
Die Version `0.4.0` 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.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.
|
||||
|
||||
## Installation
|
||||
|
||||
@@ -131,7 +131,7 @@ Nach Aktivierung stehen die üblichen NetBox-Plugin-Endpunkte bereit:
|
||||
|
||||
## ZIP-Archiv
|
||||
|
||||
Unter **Dokumentation → ZIP Export & Import** können mehrere Dokumentationen ausgewählt und gemeinsam heruntergeladen werden. Das Archiv enthält:
|
||||
Unter **Dokumentation → ZIP Export & Import** können mehrere Ordner über ein Suchfeld hinzugefügt und gemeinsam heruntergeladen werden. Optional werden alle Unterordner rekursiv einbezogen. Dokumentationen, die durch überlappende Ordnerauswahlen mehrfach gefunden werden, landen nur einmal im Archiv. Das Archiv enthält:
|
||||
|
||||
- Dokumentinhalt und Metadaten
|
||||
- vollständige Ordnerpfade
|
||||
|
||||
@@ -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.0"
|
||||
version = "0.4.1"
|
||||
author = "LKE"
|
||||
base_url = "documentation"
|
||||
min_version = "4.0.0"
|
||||
|
||||
@@ -20,6 +20,20 @@ class ArchiveFailure(ValueError):
|
||||
pass
|
||||
|
||||
|
||||
def documents_for_categories(categories, include_subfolders=True, user=None):
|
||||
"""Return distinct permitted documents contained in the selected category trees."""
|
||||
category_ids = set(categories.values_list("pk", flat=True))
|
||||
frontier = set(category_ids)
|
||||
while include_subfolders and frontier:
|
||||
children = set(DocumentCategory.objects.filter(parent_id__in=frontier).values_list("pk", flat=True))
|
||||
frontier = children - category_ids
|
||||
category_ids.update(children)
|
||||
documents = Document.objects.filter(category_id__in=category_ids).distinct().order_by("title")
|
||||
if user is not None:
|
||||
documents = documents.restrict(user, "view")
|
||||
return documents
|
||||
|
||||
|
||||
def _category_path(category):
|
||||
path, seen = [], set()
|
||||
while category and category.pk not in seen:
|
||||
|
||||
@@ -3,7 +3,9 @@ from django.conf import settings
|
||||
from django.contrib.contenttypes.models import ContentType
|
||||
from django.db.models import Q
|
||||
from utilities.forms import get_field_value
|
||||
from utilities.forms.fields import ContentTypeChoiceField, DynamicModelChoiceField, SlugField
|
||||
from utilities.forms.fields import (
|
||||
ContentTypeChoiceField, DynamicModelChoiceField, DynamicModelMultipleChoiceField, SlugField,
|
||||
)
|
||||
from utilities.forms.rendering import FieldSet
|
||||
from utilities.forms.widgets import HTMXSelect
|
||||
from netbox.forms import NetBoxModelForm
|
||||
@@ -119,17 +121,22 @@ class ImportForm(forms.Form):
|
||||
|
||||
|
||||
class ArchiveExportForm(forms.Form):
|
||||
documents = forms.ModelMultipleChoiceField(
|
||||
queryset=Document.objects.none(), widget=forms.CheckboxSelectMultiple,
|
||||
label="Dokumentationen",
|
||||
categories = DynamicModelMultipleChoiceField(
|
||||
queryset=DocumentCategory.objects.all(), selector=True,
|
||||
label="Ordner und Kategorien",
|
||||
help_text="Einen oder mehrere Ordner suchen und hinzufügen.",
|
||||
)
|
||||
include_subfolders = forms.BooleanField(
|
||||
required=False, initial=True, label="Unterordner einbeziehen",
|
||||
help_text="Exportiert Dokumentationen aus allen untergeordneten Ordnern.",
|
||||
)
|
||||
|
||||
def __init__(self, *args, user=None, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
queryset = Document.objects.all().order_by("title")
|
||||
queryset = DocumentCategory.objects.all().order_by("name")
|
||||
if user is not None:
|
||||
queryset = queryset.restrict(user, "view")
|
||||
self.fields["documents"].queryset = queryset
|
||||
self.fields["categories"].queryset = queryset
|
||||
|
||||
|
||||
class ArchiveImportForm(forms.Form):
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
<div class="card h-100">
|
||||
<h5 class="card-header"><i class="mdi mdi-folder-zip"></i> Dokumentationen exportieren</h5>
|
||||
<div class="card-body">
|
||||
<p>Ausgewählte Dokumentationen gemeinsam mit Ordnern, Objektzuordnungen und Anhängen herunterladen.</p>
|
||||
<p>Mehrere Ordner suchen und hinzufügen. Alle enthaltenen Dokumentationen werden gemeinsam mit Objektzuordnungen und Anhängen heruntergeladen.</p>
|
||||
<form method="post">
|
||||
{% csrf_token %}
|
||||
<input type="hidden" name="action" value="export">
|
||||
|
||||
@@ -16,7 +16,7 @@ from .forms import (
|
||||
ArchiveExportForm, ArchiveImportForm, AssignmentForm, DocumentCategoryForm,
|
||||
DocumentForm, ImportForm,
|
||||
)
|
||||
from .archive import ArchiveFailure, export_documents, import_archive
|
||||
from .archive import ArchiveFailure, documents_for_categories, export_documents, import_archive
|
||||
from .importers import ImportFailure, import_document
|
||||
from .models import Document, DocumentAssignment, DocumentAttachment, DocumentCategory
|
||||
from .tables import DocumentTable, AssignmentTable, DocumentCategoryTable
|
||||
@@ -189,7 +189,13 @@ class DocumentArchiveView(View):
|
||||
form = ArchiveExportForm(request.POST, user=request.user)
|
||||
if not form.is_valid():
|
||||
return self._render(request, export_form=form)
|
||||
stream = export_documents(form.cleaned_data["documents"])
|
||||
documents = documents_for_categories(
|
||||
form.cleaned_data["categories"], form.cleaned_data["include_subfolders"], request.user
|
||||
)
|
||||
if not documents.exists():
|
||||
form.add_error("categories", "Die ausgewählten Ordner enthalten keine exportierbaren Dokumentationen.")
|
||||
return self._render(request, export_form=form)
|
||||
stream = export_documents(documents)
|
||||
response = FileResponse(stream, content_type="application/zip")
|
||||
response["Content-Disposition"] = 'attachment; filename="netbox-documentation-export.zip"'
|
||||
return response
|
||||
|
||||
+1
-1
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
||||
|
||||
[project]
|
||||
name = "netbox-documentation"
|
||||
version = "0.4.0"
|
||||
version = "0.4.1"
|
||||
description = "Integrated Markdown wiki and office document importer for NetBox"
|
||||
readme = "README.md"
|
||||
requires-python = ">=3.10"
|
||||
|
||||
Reference in New Issue
Block a user