feat: ZIP-Export auf durchsuchbare Ordnerauswahl umstellen
This commit is contained in:
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user