From b7f50ce30f1edea9e5e2b8e717977cec31352c79 Mon Sep 17 00:00:00 2001 From: "Adrian A. Baumann" Date: Wed, 7 Jan 2026 14:43:59 +0100 Subject: [PATCH] XML export added --- argocd/deployment.yaml | 2 +- dokumente/management/commands/export_xml.py | 144 +++++++ .../templates/standards/standard_detail.html | 35 +- dokumente/tests.py | 383 ++++++++++++++++++ dokumente/urls.py | 1 + dokumente/views.py | 135 +++++- 6 files changed, 683 insertions(+), 17 deletions(-) create mode 100644 dokumente/management/commands/export_xml.py diff --git a/argocd/deployment.yaml b/argocd/deployment.yaml index e9c8013..0d13795 100644 --- a/argocd/deployment.yaml +++ b/argocd/deployment.yaml @@ -25,7 +25,7 @@ spec: mountPath: /data containers: - name: web - image: git.baumann.gr/adebaumann/vui:0.973 + image: git.baumann.gr/adebaumann/vui:0.974 imagePullPolicy: Always ports: - containerPort: 8000 diff --git a/dokumente/management/commands/export_xml.py b/dokumente/management/commands/export_xml.py new file mode 100644 index 0000000..ab98e40 --- /dev/null +++ b/dokumente/management/commands/export_xml.py @@ -0,0 +1,144 @@ +from django.core.management.base import BaseCommand +import xml.etree.ElementTree as ET +from datetime import datetime +from dokumente.models import Dokument, Vorgabe, VorgabeKurztext, VorgabeLangtext, Checklistenfrage + + +class Command(BaseCommand): + help = 'Export all dokumente as XML' + + def add_arguments(self, parser): + parser.add_argument( + '--output', + type=str, + help='Output file path (default: stdout)', + ) + + def handle(self, *args, **options): + dokumente = Dokument.objects.filter(aktiv=True).prefetch_related( + 'autoren', 'pruefende', 'vorgaben__thema', + 'vorgaben__referenzen', 'vorgaben__stichworte', + 'vorgaben__checklistenfragen', 'vorgaben__vorgabekurztext_set', + 'vorgaben__vorgabelangtext_set', 'geltungsbereich_set', + 'einleitung_set', 'changelog__autoren' + ).order_by('nummer') + + root = ET.Element('Vorgabendokumente') + + for dokument in dokumente: + doc_element = ET.SubElement(root, 'Vorgabendokument') + + ET.SubElement(doc_element, 'Typ').text = dokument.dokumententyp.name if dokument.dokumententyp else "" + ET.SubElement(doc_element, 'Nummer').text = dokument.nummer + ET.SubElement(doc_element, 'Name').text = dokument.name + + autoren_element = ET.SubElement(doc_element, 'Autoren') + for autor in dokument.autoren.all(): + ET.SubElement(autoren_element, 'Autor').text = autor.name + + pruefende_element = ET.SubElement(doc_element, 'Pruefende') + for pruefender in dokument.pruefende.all(): + ET.SubElement(pruefende_element, 'Pruefender').text = pruefender.name + + gueltigkeit_element = ET.SubElement(doc_element, 'Gueltigkeit') + ET.SubElement(gueltigkeit_element, 'Von').text = dokument.gueltigkeit_von.strftime("%Y-%m-%d") if dokument.gueltigkeit_von else "" + ET.SubElement(gueltigkeit_element, 'Bis').text = dokument.gueltigkeit_bis.strftime("%Y-%m-%d") if dokument.gueltigkeit_bis else None + + ET.SubElement(doc_element, 'SignaturCSO').text = dokument.signatur_cso + + geltungsbereich_sections = dokument.geltungsbereich_set.all().order_by('order') + if geltungsbereich_sections: + geltungsbereich_element = ET.SubElement(doc_element, 'Geltungsbereich') + abschnitt_element = ET.SubElement(geltungsbereich_element, 'Abschnitt') + for gb in geltungsbereich_sections: + section = ET.SubElement(abschnitt_element, 'Teil') + section.set('typ', gb.abschnitttyp.abschnitttyp if gb.abschnitttyp else "text") + section.text = gb.inhalt + + einleitung_sections = dokument.einleitung_set.all().order_by('order') + if einleitung_sections: + einleitung_element = ET.SubElement(doc_element, 'Einleitung') + abschnitt_element = ET.SubElement(einleitung_element, 'Abschnitt') + for ei in einleitung_sections: + section = ET.SubElement(abschnitt_element, 'Teil') + section.set('typ', ei.abschnitttyp.abschnitttyp if ei.abschnitttyp else "text") + section.text = ei.inhalt + + ET.SubElement(doc_element, 'Ziel').text = "" + ET.SubElement(doc_element, 'Grundlagen').text = "" + + changelog_element = ET.SubElement(doc_element, 'Changelog') + for cl in dokument.changelog.all().order_by('-datum'): + entry = ET.SubElement(changelog_element, 'Eintrag') + ET.SubElement(entry, 'Datum').text = cl.datum.strftime("%Y-%m-%d") + autoren = ET.SubElement(entry, 'Autoren') + for autor in cl.autoren.all(): + ET.SubElement(autoren, 'Autor').text = autor.name + ET.SubElement(entry, 'Aenderung').text = cl.aenderung + + anhaenge_element = ET.SubElement(doc_element, 'Anhaenge') + ET.SubElement(anhaenge_element, 'Anhang').text = dokument.anhaenge + + ET.SubElement(doc_element, 'Verantwortlich').text = "Information Security Management BIT" + ET.SubElement(doc_element, 'Klassifizierung').text = "" + + glossar_element = ET.SubElement(doc_element, 'Glossar') + + vorgaben_element = ET.SubElement(doc_element, 'Vorgaben') + + for vorgabe in dokument.vorgaben.all().order_by('order'): + vorgabe_el = ET.SubElement(vorgaben_element, 'Vorgabe') + + ET.SubElement(vorgabe_el, 'Nummer').text = str(vorgabe.nummer) + ET.SubElement(vorgabe_el, 'Titel').text = vorgabe.titel + ET.SubElement(vorgabe_el, 'Thema').text = vorgabe.thema.name if vorgabe.thema else "" + + kurztext_sections = vorgabe.vorgabekurztext_set.all().order_by('order') + if kurztext_sections: + kurztext_element = ET.SubElement(vorgabe_el, 'Kurztext') + abschnitt = ET.SubElement(kurztext_element, 'Abschnitt') + for kt in kurztext_sections: + teil = ET.SubElement(abschnitt, 'Teil') + teil.set('typ', kt.abschnitttyp.abschnitttyp if kt.abschnitttyp else "text") + teil.text = kt.inhalt + + langtext_sections = vorgabe.vorgabelangtext_set.all().order_by('order') + if langtext_sections: + langtext_element = ET.SubElement(vorgabe_el, 'Langtext') + abschnitt = ET.SubElement(langtext_element, 'Abschnitt') + for lt in langtext_sections: + teil = ET.SubElement(abschnitt, 'Teil') + teil.set('typ', lt.abschnitttyp.abschnitttyp if lt.abschnitttyp else "text") + teil.text = lt.inhalt + + referenz_element = ET.SubElement(vorgabe_el, 'Referenzen') + for ref in vorgabe.referenzen.all(): + ref_text = f"{ref.name_nummer}: {ref.name_text}" if ref.name_text else ref.name_nummer + ET.SubElement(referenz_element, 'Referenz').text = ref_text + + vorgabe_gueltigkeit = ET.SubElement(vorgabe_el, 'Gueltigkeit') + ET.SubElement(vorgabe_gueltigkeit, 'Von').text = vorgabe.gueltigkeit_von.strftime("%Y-%m-%d") if vorgabe.gueltigkeit_von else "" + ET.SubElement(vorgabe_gueltigkeit, 'Bis').text = vorgabe.gueltigkeit_bis.strftime("%Y-%m-%d") if vorgabe.gueltigkeit_bis else None + + checklistenfragen_element = ET.SubElement(vorgabe_el, 'Checklistenfragen') + for cf in vorgabe.checklistenfragen.all(): + ET.SubElement(checklistenfragen_element, 'Frage').text = cf.frage + + stichworte_element = ET.SubElement(vorgabe_el, 'Stichworte') + for stw in vorgabe.stichworte.all(): + ET.SubElement(stichworte_element, 'Stichwort').text = stw.stichwort + + xml_str = ET.tostring(root, encoding='unicode', method='xml') + xml_output = self._prettify_xml(xml_str) + + if options['output']: + with open(options['output'], 'w', encoding='utf-8') as f: + f.write(xml_output) + self.stdout.write(self.style.SUCCESS(f'XML exported to {options["output"]}')) + else: + self.stdout.write(xml_output) + + def _prettify_xml(self, xml_string): + import xml.dom.minidom + dom = xml.dom.minidom.parseString(xml_string) + return dom.toprettyxml(indent=" ", encoding="UTF-8").decode('utf-8') diff --git a/dokumente/templates/standards/standard_detail.html b/dokumente/templates/standards/standard_detail.html index 45de3c2..3ee153d 100644 --- a/dokumente/templates/standards/standard_detail.html +++ b/dokumente/templates/standards/standard_detail.html @@ -193,23 +193,28 @@

Metadaten

-
-
Autoren:
-
{{ standard.autoren.all|join:", " }}
+
+
Autoren:
+
{{ standard.autoren.all|join:", " }}
-
Prüfende:
-
{{ standard.pruefende.all|join:", " }}
+
Prüfende:
+
{{ standard.pruefende.all|join:", " }}
-
Gültigkeit:
-
{{ standard.gueltigkeit_von }} bis {{ standard.gueltigkeit_bis|default_if_none:"auf weiteres" }}
-
-

- - JSON herunterladen - -

+
Gültigkeit:
+
{{ standard.gueltigkeit_von }} bis {{ standard.gueltigkeit_bis|default_if_none:"auf weiteres" }}
+
+

+ + JSON herunterladen + + + XML herunterladen + +

diff --git a/dokumente/tests.py b/dokumente/tests.py index ee80db6..9bf141a 100644 --- a/dokumente/tests.py +++ b/dokumente/tests.py @@ -1483,6 +1483,200 @@ class JSONExportManagementCommandTest(TestCase): self.assertIn('"Test Standard"', output) +class ExportXMLCommandTest(TestCase): + """Test cases for export_xml management command""" + + def setUp(self): + """Set up test data for XML export""" + # Create test data + self.dokumententyp = Dokumententyp.objects.create( + name="Standard IT-Sicherheit", + verantwortliche_ve="SR-SUR-SEC" + ) + + self.autor1 = Person.objects.create( + name="Max Mustermann", + funktion="Security Analyst" + ) + self.autor2 = Person.objects.create( + name="Erika Mustermann", + funktion="Security Manager" + ) + + self.thema = Thema.objects.create( + name="Access Control", + erklaerung="Zugangskontrolle" + ) + + self.dokument = Dokument.objects.create( + nummer="TEST-001", + dokumententyp=self.dokumententyp, + name="Test Standard", + gueltigkeit_von=date(2023, 1, 1), + gueltigkeit_bis=date(2025, 12, 31), + signatur_cso="CSO-123", + anhaenge="Anhang1.pdf, Anhang2.pdf", + aktiv=True + ) + self.dokument.autoren.add(self.autor1, self.autor2) + + self.vorgabe = Vorgabe.objects.create( + order=1, + nummer=1, + dokument=self.dokument, + thema=self.thema, + titel="Test Vorgabe", + gueltigkeit_von=date(2023, 1, 1), + gueltigkeit_bis=date(2025, 12, 31) + ) + + # Create text sections + self.abschnitttyp_text = AbschnittTyp.objects.create(abschnitttyp="text") + self.abschnitttyp_table = AbschnittTyp.objects.create(abschnitttyp="table") + + self.geltungsbereich = Geltungsbereich.objects.create( + geltungsbereich=self.dokument, + abschnitttyp=self.abschnitttyp_text, + inhalt="Dies ist der Geltungsbereich", + order=1 + ) + + self.einleitung = Einleitung.objects.create( + einleitung=self.dokument, + abschnitttyp=self.abschnitttyp_text, + inhalt="Dies ist die Einleitung", + order=1 + ) + + self.kurztext = VorgabeKurztext.objects.create( + abschnitt=self.vorgabe, + abschnitttyp=self.abschnitttyp_text, + inhalt="Dies ist der Kurztext", + order=1 + ) + + self.langtext = VorgabeLangtext.objects.create( + abschnitt=self.vorgabe, + abschnitttyp=self.abschnitttyp_table, + inhalt="Spalte1|Spalte2\nWert1|Wert2", + order=1 + ) + + self.checklistenfrage = Checklistenfrage.objects.create( + vorgabe=self.vorgabe, + frage="Ist die Zugriffskontrolle implementiert?" + ) + + self.changelog = Changelog.objects.create( + dokument=self.dokument, + datum=date(2023, 6, 1), + aenderung="Erste Version erstellt" + ) + self.changelog.autoren.add(self.autor1) + + def test_export_xml_command_stdout(self): + """Test export_xml command output to stdout""" + out = StringIO() + call_command('export_xml', stdout=out) + + output = out.getvalue() + + # Check that output contains expected XML structure + self.assertIn('', output) + self.assertIn('', output) + self.assertIn('Standard IT-Sicherheit', output) + self.assertIn('TEST-001', output) + self.assertIn('Test Standard', output) + self.assertIn('Max Mustermann', output) + self.assertIn('Erika Mustermann', output) + self.assertIn('2023-01-01', output) + self.assertIn('2025-12-31', output) + self.assertIn('CSO-123', output) + self.assertIn('Dies ist der Geltungsbereich', output) + self.assertIn('Dies ist die Einleitung', output) + self.assertIn('Dies ist der Kurztext', output) + self.assertIn('Ist die Zugriffskontrolle implementiert?', output) + self.assertIn('Erste Version erstellt', output) + + def test_export_xml_command_to_file(self): + """Test export_xml command output to file""" + import tempfile + import os + import xml.etree.ElementTree as ET + + with tempfile.NamedTemporaryFile(mode='w', delete=False, suffix='.xml') as tmp_file: + tmp_filename = tmp_file.name + + try: + call_command('export_xml', output=tmp_filename) + + # Read file content + with open(tmp_filename, 'r', encoding='utf-8') as f: + content = f.read() + + # Parse XML to ensure it's valid + root = ET.fromstring(content) + + # Verify structure + self.assertEqual(root.tag, 'Vorgabendokumente') + + docs = root.findall('Vorgabendokument') + self.assertEqual(len(docs), 1) + + doc = docs[0] + self.assertEqual(doc.find('Nummer').text, 'TEST-001') + self.assertEqual(doc.find('Name').text, 'Test Standard') + self.assertEqual(doc.find('Typ').text, 'Standard IT-Sicherheit') + + autoren = doc.find('Autoren').findall('Autor') + self.assertEqual(len(autoren), 2) + autor_names = [autor.text for autor in autoren] + self.assertIn('Max Mustermann', autor_names) + self.assertIn('Erika Mustermann', autor_names) + + finally: + # Clean up temporary file + if os.path.exists(tmp_filename): + os.unlink(tmp_filename) + + def test_export_xml_command_empty_database(self): + """Test export_xml command with no documents""" + # Delete all documents + Dokument.objects.all().delete() + + out = StringIO() + call_command('export_xml', stdout=out) + + output = out.getvalue() + + # Should output empty XML structure (accepts both formats) + self.assertIn('Vorgabendokumente', output) + self.assertNotIn('', output) + + def test_export_xml_command_inactive_documents(self): + """Test export_xml command filters inactive documents""" + # Create inactive document + inactive_doc = Dokument.objects.create( + nummer="INACTIVE-001", + dokumententyp=self.dokumententyp, + name="Inactive Document", + aktiv=False + ) + + out = StringIO() + call_command('export_xml', stdout=out) + + output = out.getvalue() + + # Should not contain inactive document + self.assertNotIn('INACTIVE-001', output) + self.assertNotIn('Inactive Document', output) + + # Should still contain active document + self.assertIn('TEST-001', output) + self.assertIn('Test Standard', output) + + class StandardJSONViewTest(TestCase): """Test cases for standard_json view""" @@ -1668,6 +1862,195 @@ class StandardJSONViewTest(TestCase): self.assertIn(' ', response.content.decode()) # Check for indentation +class StandardXMLViewTest(TestCase): + """Test cases for standard_xml view""" + + def setUp(self): + """Set up test data for XML view""" + self.client = Client() + + # Create test data + self.dokumententyp = Dokumententyp.objects.create( + name="Standard IT-Sicherheit", + verantwortliche_ve="SR-SUR-SEC" + ) + + self.autor = Person.objects.create( + name="Test Autor", + funktion="Security Analyst" + ) + + self.pruefender = Person.objects.create( + name="Test Pruefender", + funktion="Security Manager" + ) + + self.thema = Thema.objects.create( + name="Access Control", + erklaerung="Zugangskontrolle" + ) + + self.dokument = Dokument.objects.create( + nummer="XML-001", + dokumententyp=self.dokumententyp, + name="XML Test Standard", + gueltigkeit_von=date(2023, 1, 1), + gueltigkeit_bis=date(2025, 12, 31), + signatur_cso="CSO-456", + anhaenge="test.pdf", + aktiv=True + ) + self.dokument.autoren.add(self.autor) + self.dokument.pruefende.add(self.pruefender) + + self.vorgabe = Vorgabe.objects.create( + order=1, + nummer=1, + dokument=self.dokument, + thema=self.thema, + titel="XML Test Vorgabe", + gueltigkeit_von=date(2023, 1, 1), + gueltigkeit_bis=date(2025, 12, 31) + ) + + # Create text sections + self.abschnitttyp_text = AbschnittTyp.objects.create(abschnitttyp="text") + + self.geltungsbereich = Geltungsbereich.objects.create( + geltungsbereich=self.dokument, + abschnitttyp=self.abschnitttyp_text, + inhalt="XML Geltungsbereich", + order=1 + ) + + self.kurztext = VorgabeKurztext.objects.create( + abschnitt=self.vorgabe, + abschnitttyp=self.abschnitttyp_text, + inhalt="XML Kurztext", + order=1 + ) + + self.langtext = VorgabeLangtext.objects.create( + abschnitt=self.vorgabe, + abschnitttyp=self.abschnitttyp_text, + inhalt="XML Langtext", + order=1 + ) + + self.checklistenfrage = Checklistenfrage.objects.create( + vorgabe=self.vorgabe, + frage="XML Checklistenfrage?" + ) + + self.changelog = Changelog.objects.create( + dokument=self.dokument, + datum=date(2023, 6, 1), + aenderung="XML Changelog Eintrag" + ) + self.changelog.autoren.add(self.autor) + + def test_standard_xml_view_success(self): + """Test standard_xml view returns correct XML""" + url = reverse('standard_xml', kwargs={'nummer': 'XML-001'}) + response = self.client.get(url) + + self.assertEqual(response.status_code, 200) + self.assertEqual(response['Content-Type'], 'application/xml; charset=utf-8') + self.assertIn('attachment', response['Content-Disposition']) + self.assertIn('XML-001.xml', response['Content-Disposition']) + + # Parse XML response + import xml.etree.ElementTree as ET + root = ET.fromstring(response.content) + + # Verify document structure + self.assertEqual(root.tag, 'Vorgabendokument') + self.assertEqual(root.find('Nummer').text, 'XML-001') + self.assertEqual(root.find('Name').text, 'XML Test Standard') + self.assertEqual(root.find('Typ').text, 'Standard IT-Sicherheit') + self.assertEqual(root.find('Autoren').find('Autor').text, 'Test Autor') + self.assertEqual(root.find('Pruefende').find('Pruefender').text, 'Test Pruefender') + self.assertEqual(root.find('Gueltigkeit').find('Von').text, '2023-01-01') + self.assertEqual(root.find('Gueltigkeit').find('Bis').text, '2025-12-31') + self.assertEqual(root.find('SignaturCSO').text, 'CSO-456') + self.assertEqual(root.find('Anhaenge').find('Anhang').text, 'test.pdf') + self.assertEqual(root.find('Verantwortlich').text, 'Information Security Management BIT') + self.assertIn(root.find('Klassifizierung').text, ['', None]) # Empty string or None + + def test_standard_xml_view_not_found(self): + """Test standard_xml view returns 404 for non-existent document""" + url = reverse('standard_xml', kwargs={'nummer': 'NONEXISTENT'}) + response = self.client.get(url) + + self.assertEqual(response.status_code, 404) + + def test_standard_xml_view_empty_sections(self): + """Test standard_xml view handles empty sections correctly""" + # Create document without sections + empty_doc = Dokument.objects.create( + nummer="EMPTY-XML-001", + dokumententyp=self.dokumententyp, + name="Empty Document", + aktiv=True + ) + + url = reverse('standard_xml', kwargs={'nummer': 'EMPTY-XML-001'}) + response = self.client.get(url) + + # Parse XML response + import xml.etree.ElementTree as ET + root = ET.fromstring(response.content) + + # Verify empty sections are handled correctly + self.assertIsNone(root.find('Geltungsbereich')) + self.assertIsNone(root.find('Einleitung')) + self.assertEqual(len(root.find('Vorgaben')), 0) + self.assertEqual(len(root.find('Changelog')), 0) + + def test_standard_xml_view_null_dates(self): + """Test standard_xml view handles null dates correctly""" + # Create document with null dates + null_doc = Dokument.objects.create( + nummer="NULL-XML-001", + dokumententyp=self.dokumententyp, + name="Null Dates Document", + gueltigkeit_von=None, + gueltigkeit_bis=None, + aktiv=True + ) + + url = reverse('standard_xml', kwargs={'nummer': 'NULL-XML-001'}) + response = self.client.get(url) + + # Parse XML response + import xml.etree.ElementTree as ET + root = ET.fromstring(response.content) + + # Verify null dates are handled correctly + self.assertIn(root.find('Gueltigkeit').find('Von').text, ['', None]) + self.assertIsNone(root.find('Gueltigkeit').find('Bis').text) + + def test_standard_xml_view_xml_formatting(self): + """Test standard_xml view returns properly formatted XML""" + url = reverse('standard_xml', kwargs={'nummer': 'XML-001'}) + response = self.client.get(url) + + # Check that response is valid XML + import xml.etree.ElementTree as ET + try: + ET.fromstring(response.content) + xml_valid = True + except ET.ParseError: + xml_valid = False + + self.assertTrue(xml_valid) + + # Check that XML is properly indented (should be formatted) + self.assertIn('/history/', views.standard_detail, {"check_date":"today"}, name='standard_history'), path('/checkliste/', views.standard_checkliste, name='standard_checkliste'), path('/json/', views.standard_json, name='standard_json'), + path('/xml/', views.standard_xml, name='standard_xml'), path('comments//', views.get_vorgabe_comments, name='get_vorgabe_comments'), path('comments//add/', views.add_vorgabe_comment, name='add_vorgabe_comment'), path('comments/delete//', views.delete_vorgabe_comment, name='delete_vorgabe_comment'), diff --git a/dokumente/views.py b/dokumente/views.py index d7856bd..b6d51a8 100644 --- a/dokumente/views.py +++ b/dokumente/views.py @@ -1,12 +1,13 @@ from django.shortcuts import render, get_object_or_404 from django.contrib.auth.decorators import login_required, user_passes_test -from django.http import JsonResponse +from django.http import JsonResponse, HttpResponse from django.core.serializers.json import DjangoJSONEncoder from django.views.decorators.http import require_POST from django.views.decorators.csrf import csrf_exempt from django.utils.html import escape, mark_safe from django.utils.safestring import SafeString import json +import xml.etree.ElementTree as ET from .models import Dokument, Vorgabe, VorgabeKurztext, VorgabeLangtext, Checklistenfrage, VorgabeComment from abschnitte.utils import render_textabschnitte @@ -254,6 +255,138 @@ def standard_json(request, nummer): return JsonResponse(doc_data, json_dumps_params={'indent': 2, 'ensure_ascii': False}, encoder=DjangoJSONEncoder) +def standard_xml(request, nummer): + """ + Export a single Dokument as XML + """ + # Get the document with all related data + dokument = get_object_or_404( + Dokument.objects.prefetch_related( + 'autoren', 'pruefende', 'vorgaben__thema', + 'vorgaben__referenzen', 'vorgaben__stichworte', + 'vorgaben__checklistenfragen', 'vorgaben__vorgabekurztext_set', + 'vorgaben__vorgabelangtext_set', 'geltungsbereich_set', + 'einleitung_set', 'changelog__autoren' + ), + nummer=nummer + ) + + root = ET.Element('Vorgabendokument') + + ET.SubElement(root, 'Typ').text = dokument.dokumententyp.name if dokument.dokumententyp else "" + ET.SubElement(root, 'Nummer').text = dokument.nummer + ET.SubElement(root, 'Name').text = dokument.name + + autoren_element = ET.SubElement(root, 'Autoren') + for autor in dokument.autoren.all(): + ET.SubElement(autoren_element, 'Autor').text = autor.name + + pruefende_element = ET.SubElement(root, 'Pruefende') + for pruefender in dokument.pruefende.all(): + ET.SubElement(pruefende_element, 'Pruefender').text = pruefender.name + + gueltigkeit_element = ET.SubElement(root, 'Gueltigkeit') + ET.SubElement(gueltigkeit_element, 'Von').text = dokument.gueltigkeit_von.strftime("%Y-%m-%d") if dokument.gueltigkeit_von else "" + ET.SubElement(gueltigkeit_element, 'Bis').text = dokument.gueltigkeit_bis.strftime("%Y-%m-%d") if dokument.gueltigkeit_bis else None + + ET.SubElement(root, 'SignaturCSO').text = dokument.signatur_cso + + geltungsbereich_sections = dokument.geltungsbereich_set.all().order_by('order') + if geltungsbereich_sections: + geltungsbereich_element = ET.SubElement(root, 'Geltungsbereich') + abschnitt_element = ET.SubElement(geltungsbereich_element, 'Abschnitt') + for gb in geltungsbereich_sections: + section = ET.SubElement(abschnitt_element, 'Teil') + section.set('typ', gb.abschnitttyp.abschnitttyp if gb.abschnitttyp else "text") + section.text = gb.inhalt + + einleitung_sections = dokument.einleitung_set.all().order_by('order') + if einleitung_sections: + einleitung_element = ET.SubElement(root, 'Einleitung') + abschnitt_element = ET.SubElement(einleitung_element, 'Abschnitt') + for ei in einleitung_sections: + section = ET.SubElement(abschnitt_element, 'Teil') + section.set('typ', ei.abschnitttyp.abschnitttyp if ei.abschnitttyp else "text") + section.text = ei.inhalt + + ET.SubElement(root, 'Ziel').text = "" + ET.SubElement(root, 'Grundlagen').text = "" + + changelog_element = ET.SubElement(root, 'Changelog') + for cl in dokument.changelog.all().order_by('-datum'): + entry = ET.SubElement(changelog_element, 'Eintrag') + ET.SubElement(entry, 'Datum').text = cl.datum.strftime("%Y-%m-%d") + autoren = ET.SubElement(entry, 'Autoren') + for autor in cl.autoren.all(): + ET.SubElement(autoren, 'Autor').text = autor.name + ET.SubElement(entry, 'Aenderung').text = cl.aenderung + + anhaenge_element = ET.SubElement(root, 'Anhaenge') + ET.SubElement(anhaenge_element, 'Anhang').text = dokument.anhaenge + + ET.SubElement(root, 'Verantwortlich').text = "Information Security Management BIT" + ET.SubElement(root, 'Klassifizierung').text = "" + + glossar_element = ET.SubElement(root, 'Glossar') + + vorgaben_element = ET.SubElement(root, 'Vorgaben') + + for vorgabe in dokument.vorgaben.all().order_by('order'): + vorgabe_el = ET.SubElement(vorgaben_element, 'Vorgabe') + + ET.SubElement(vorgabe_el, 'Nummer').text = str(vorgabe.nummer) + ET.SubElement(vorgabe_el, 'Titel').text = vorgabe.titel + ET.SubElement(vorgabe_el, 'Thema').text = vorgabe.thema.name if vorgabe.thema else "" + + kurztext_sections = vorgabe.vorgabekurztext_set.all().order_by('order') + if kurztext_sections: + kurztext_element = ET.SubElement(vorgabe_el, 'Kurztext') + abschnitt = ET.SubElement(kurztext_element, 'Abschnitt') + for kt in kurztext_sections: + teil = ET.SubElement(abschnitt, 'Teil') + teil.set('typ', kt.abschnitttyp.abschnitttyp if kt.abschnitttyp else "text") + teil.text = kt.inhalt + + langtext_sections = vorgabe.vorgabelangtext_set.all().order_by('order') + if langtext_sections: + langtext_element = ET.SubElement(vorgabe_el, 'Langtext') + abschnitt = ET.SubElement(langtext_element, 'Abschnitt') + for lt in langtext_sections: + teil = ET.SubElement(abschnitt, 'Teil') + teil.set('typ', lt.abschnitttyp.abschnitttyp if lt.abschnitttyp else "text") + teil.text = lt.inhalt + + referenz_element = ET.SubElement(vorgabe_el, 'Referenzen') + for ref in vorgabe.referenzen.all(): + ref_text = f"{ref.name_nummer}: {ref.name_text}" if ref.name_text else ref.name_nummer + ET.SubElement(referenz_element, 'Referenz').text = ref_text + + vorgabe_gueltigkeit = ET.SubElement(vorgabe_el, 'Gueltigkeit') + ET.SubElement(vorgabe_gueltigkeit, 'Von').text = vorgabe.gueltigkeit_von.strftime("%Y-%m-%d") if vorgabe.gueltigkeit_von else "" + ET.SubElement(vorgabe_gueltigkeit, 'Bis').text = vorgabe.gueltigkeit_bis.strftime("%Y-%m-%d") if vorgabe.gueltigkeit_bis else None + + checklistenfragen_element = ET.SubElement(vorgabe_el, 'Checklistenfragen') + for cf in vorgabe.checklistenfragen.all(): + ET.SubElement(checklistenfragen_element, 'Frage').text = cf.frage + + stichworte_element = ET.SubElement(vorgabe_el, 'Stichworte') + for stw in vorgabe.stichworte.all(): + ET.SubElement(stichworte_element, 'Stichwort').text = stw.stichwort + + xml_str = ET.tostring(root, encoding='unicode', method='xml') + xml_output = _prettify_xml(xml_str) + + response = HttpResponse(xml_output, content_type='application/xml; charset=utf-8') + response['Content-Disposition'] = f'attachment; filename="{dokument.nummer}.xml"' + return response + + +def _prettify_xml(xml_string): + import xml.dom.minidom + dom = xml.dom.minidom.parseString(xml_string) + return dom.toprettyxml(indent=" ", encoding="UTF-8").decode('utf-8') + + @login_required def get_vorgabe_comments(request, vorgabe_id): """Get comments for a specific Vorgabe"""