fix: Mandant vor erstem Speichern importierter Ordner setzen
This commit is contained in:
@@ -29,7 +29,7 @@ Ein in NetBox integriertes Markdown-Wiki für Betriebsdokumentationen und Anleit
|
|||||||
|
|
||||||
## Kompatibilität
|
## Kompatibilität
|
||||||
|
|
||||||
Die Version `0.9.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.
|
Die Version `0.9.3` 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
|
## Installation
|
||||||
|
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ class DocumentationConfig(PluginConfig):
|
|||||||
name = "netbox_documentation"
|
name = "netbox_documentation"
|
||||||
verbose_name = "NetBox Dokumentation"
|
verbose_name = "NetBox Dokumentation"
|
||||||
description = "Wiki und Office-Dokumentation direkt in NetBox"
|
description = "Wiki und Office-Dokumentation direkt in NetBox"
|
||||||
version = "0.9.2"
|
version = "0.9.3"
|
||||||
author = "LKE"
|
author = "LKE"
|
||||||
base_url = "documentation"
|
base_url = "documentation"
|
||||||
min_version = "4.0.0"
|
min_version = "4.0.0"
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ from zipfile import ZIP_DEFLATED, BadZipFile, ZipFile
|
|||||||
|
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.contrib.contenttypes.models import ContentType
|
from django.contrib.contenttypes.models import ContentType
|
||||||
|
from django.core.exceptions import ValidationError
|
||||||
from django.db import transaction
|
from django.db import transaction
|
||||||
from django.utils.text import slugify
|
from django.utils.text import slugify
|
||||||
from tenancy.models import Tenant, TenantGroup
|
from tenancy.models import Tenant, TenantGroup
|
||||||
@@ -21,6 +22,14 @@ class ArchiveFailure(ValueError):
|
|||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
def _save_imported_object(instance, context, **save_kwargs):
|
||||||
|
try:
|
||||||
|
instance.save(**save_kwargs)
|
||||||
|
except ValidationError as exc:
|
||||||
|
messages = getattr(exc, "messages", None) or [str(exc)]
|
||||||
|
raise ArchiveFailure(f"{context}: {'; '.join(messages)}") from exc
|
||||||
|
|
||||||
|
|
||||||
def documents_for_categories(categories, include_subfolders=True, user=None):
|
def documents_for_categories(categories, include_subfolders=True, user=None):
|
||||||
"""Return distinct permitted documents contained in the selected category trees."""
|
"""Return distinct permitted documents contained in the selected category trees."""
|
||||||
category_ids = set(categories.values_list("pk", flat=True))
|
category_ids = set(categories.values_list("pk", flat=True))
|
||||||
@@ -173,15 +182,32 @@ def _category_from_path(names, tenancy_path=None):
|
|||||||
if not name:
|
if not name:
|
||||||
continue
|
continue
|
||||||
slug = slugify(name)[:100] or "ordner"
|
slug = slugify(name)[:100] or "ordner"
|
||||||
|
tenancy = tenancy_path[index] if index < len(tenancy_path) and isinstance(tenancy_path[index], dict) else {}
|
||||||
|
context = f"Ordner „{name}“"
|
||||||
|
tenant_group = _resolve_tenant_group(tenancy.get("tenant_group"), context)
|
||||||
|
tenant = _resolve_tenant(tenancy.get("tenant"), context)
|
||||||
|
if tenant and tenant_group and tenant.group_id != tenant_group.pk:
|
||||||
|
raise ArchiveFailure(
|
||||||
|
f"{context}: Der Mandant „{tenant.slug}“ gehört nicht zur "
|
||||||
|
f"Mandantengruppe „{tenant_group.slug}“ auf dieser Instanz."
|
||||||
|
)
|
||||||
category = DocumentCategory.objects.filter(parent=parent, slug=slug).first()
|
category = DocumentCategory.objects.filter(parent=parent, slug=slug).first()
|
||||||
if not category:
|
if not category:
|
||||||
category = DocumentCategory.objects.create(name=name, slug=slug, parent=parent)
|
# Set tenancy before the first save. NetBox installations may
|
||||||
tenancy = tenancy_path[index] if index < len(tenancy_path) and isinstance(tenancy_path[index], dict) else {}
|
# enforce a tenant via custom validation rules, so creating the
|
||||||
if tenancy:
|
# category first and assigning its tenant in a second save fails.
|
||||||
context = f"Ordner „{name}“"
|
category = DocumentCategory(
|
||||||
category.tenant_group = _resolve_tenant_group(tenancy.get("tenant_group"), context)
|
name=name, slug=slug, parent=parent,
|
||||||
category.tenant = _resolve_tenant(tenancy.get("tenant"), context)
|
tenant_group=tenant_group, tenant=tenant,
|
||||||
category.save(update_fields=("tenant_group", "tenant", "last_updated"))
|
)
|
||||||
|
_save_imported_object(category, context)
|
||||||
|
elif tenancy:
|
||||||
|
category.tenant_group = tenant_group
|
||||||
|
category.tenant = tenant
|
||||||
|
_save_imported_object(
|
||||||
|
category, context,
|
||||||
|
update_fields=("tenant_group", "tenant", "last_updated"),
|
||||||
|
)
|
||||||
parent = category
|
parent = category
|
||||||
return parent
|
return parent
|
||||||
|
|
||||||
@@ -237,7 +263,12 @@ def import_archive(upload, update_existing=False):
|
|||||||
context = f"Dokument „{document.title}“"
|
context = f"Dokument „{document.title}“"
|
||||||
document.tenant_group = _resolve_tenant_group(record.get("tenant_group"), context)
|
document.tenant_group = _resolve_tenant_group(record.get("tenant_group"), context)
|
||||||
document.tenant = _resolve_tenant(record.get("tenant"), context)
|
document.tenant = _resolve_tenant(record.get("tenant"), context)
|
||||||
document.save()
|
if document.tenant and document.tenant_group and document.tenant.group_id != document.tenant_group_id:
|
||||||
|
raise ArchiveFailure(
|
||||||
|
f"{context}: Der Mandant „{document.tenant.slug}“ gehört nicht zur "
|
||||||
|
f"Mandantengruppe „{document.tenant_group.slug}“ auf dieser Instanz."
|
||||||
|
)
|
||||||
|
_save_imported_object(document, context)
|
||||||
for assignment in record.get("assignments") or []:
|
for assignment in record.get("assignments") or []:
|
||||||
label = f"{assignment.get('app_label')}.{assignment.get('model')}"
|
label = f"{assignment.get('app_label')}.{assignment.get('model')}"
|
||||||
if label not in allowed:
|
if label not in allowed:
|
||||||
@@ -280,4 +311,4 @@ def import_archive(upload, update_existing=False):
|
|||||||
if item.get("source_url"):
|
if item.get("source_url"):
|
||||||
document.body = document.body.replace(str(item["source_url"]), attachment.file.url)
|
document.body = document.body.replace(str(item["source_url"]), attachment.file.url)
|
||||||
document.save(update_fields=("body", "last_updated"))
|
document.save(update_fields=("body", "last_updated"))
|
||||||
return {"created": created, "updated": updated, "skipped_assignments": skipped_assignments}
|
return {"created": created, "updated": updated, "skipped_assignments": skipped_assignments}
|
||||||
|
|||||||
+1
-1
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|||||||
|
|
||||||
[project]
|
[project]
|
||||||
name = "netbox-documentation"
|
name = "netbox-documentation"
|
||||||
version = "0.9.2"
|
version = "0.9.3"
|
||||||
description = "Integrated Markdown wiki and office document importer for NetBox"
|
description = "Integrated Markdown wiki and office document importer for NetBox"
|
||||||
readme = "README.md"
|
readme = "README.md"
|
||||||
requires-python = ">=3.10"
|
requires-python = ">=3.10"
|
||||||
|
|||||||
Reference in New Issue
Block a user