Commit c2c666b3 authored by Thorsten Küfer's avatar Thorsten Küfer

Merge remote-tracking branch 'origin/16-dokumentenmanagement' into kosmetik

parents 6abd9d0d 42283878
This diff is collapsed.
......@@ -21,6 +21,7 @@
require_once('DBCon.class.php');
require_once('Utils.class.php');
require_once('auth/Auth.class.php');
require_once('docmgmt/DocMGMT.class.php');
# ----------------------------------------
# Globale Konfiguration setzen
......@@ -165,11 +166,23 @@
'employee' => '(|(memberof=CN=u0mitarb,OU=Projekt-Gruppen,DC=wwu,DC=de)(memberof=CN=e0mitwwu,OU=Projekt-Gruppen,DC=wwu,DC=de))'
];
# ----------------------------------------
# Konfiguration der Dokumentenverwaltung
# ----------------------------------------
$docmgmt_method = 'local'; # Aktuell nur local unterstützt
$docmgmt_class = $docmgmt_method . 'DocMGMT';
# ----------------------------------------
# Lokale Konfiguration laden
# ----------------------------------------
include_once("$secret_dir/secdoc.conf.php");
# ----------------------------------------
# Dokumentenverwaltungsklasse laden
# ----------------------------------------
if(!@require_once("docmgmt/{$docmgmt_class}.class.php")) throw new Exception("config.inc.php Fehler: Dokumentenverwaltungsklasse '{$docmgmt_class}.class.php' wurde nicht gefunden oder konnte nicht eingebunden werden!");
$docmgmtClass = new $docmgmt_class;
# ----------------------------------------
# Authentifizierungsklasse laden
# ----------------------------------------
......
<?php
/**
* Enthält eine abstrakte Implementierung der Klasse zur Dokumentenverwaltung.
*
* @author Dustin Gawron <dustin.gawron@uni-muenster.de>
* @copyright (c) 2020 Westfälische Wilhelms-Universität Münster
* @license AGPL-3.0-or-later <https://www.gnu.org/licenses/agpl.html>
*/
/**
* Abstrakte Klasse als Vorlage zur Dokumentenverwaltung.
*
* @abstract
*/
abstract class DocMGMT {
/**
* Holt den Inhalt eines Dokuments.
*
* @abstract
* @param int $processID ID der Dokumentation
* @param string $fileRef Dateireferenz
* @return string[] Dateiname und Dateiinhalt (base64 kodiert) (['fileName' => 'test.pdf', 'fileContent' => '...'])
*/
abstract public function getDocument($processID, $fileRef);
/**
* Fügt ein neues Dokument hinzu.
*
* @abstract
* @param int $processID ID der Dokumentation
* @param string $fileName Dateiname
* @param string $fileContent Dateiinhalt (base64 kodiert)
* @return string Referenz zum Dokument
*/
abstract public function addDocument($processID, $fileName, $fileContent);
/**
* Aktualisiert ein Dokument.
*
* @abstract
* @param int $processID ID der Dokumentation
* @param string $fileRef Dateireferenz
* @param string $fileName Dateiname
* @param string $fileContent Dateiinhalt (base64 kodiert)
* @return bool TRUE bei Erfolg, sonst FALSE
*/
abstract public function updateDocument($processID, $fileRef, $fileName, $fileContent);
/**
* Löscht ein Dokument.
*
* @abstract
* @param int $processID ID der Dokumentation
* @param string $fileRef Dateireferenz
* @return bool TRUE bei Erfolg, sonst FALSE
*/
abstract public function deleteDocument($processID, $fileRef);
}
?>
<?php
/**
* Implementierung einer lokalen Dateiverwaltung.
*
* @author Dustin Gawron <dustin.gawron@uni-muenster.de>
* @copyright (c) 2020 Westfälische Wilhelms-Universität Münster
* @license AGPL-3.0-or-later <https://www.gnu.org/licenses/agpl.html>
*/
/**
* Klasse zur Implementierung einer lokalen Dateiverwaltung.
* Erweitert die Grundklasse {@link DocMGMT}.
*/
class localDocMGMT extends DocMGMT {
/**
* Holt den Inhalt eines Dokuments.
*
* @abstract
* @param int $processID ID der Dokumentation
* @param string $fileRef Dateireferenz
* @return string[] Dateiname und Dateiinhalt (base64 kodiert) (['fileName' => 'test.pdf', 'fileContent' => '...'])
* @throws Exception
*/
public function getDocument($processID, $fileRef) {
global $pdf_dir;
$filePath = $pdf_dir . DIRECTORY_SEPARATOR . $processID . DIRECTORY_SEPARATOR . $fileRef;
$returnVal = [
'fileName' => $fileRef,
'fileContent' => ''
];
if(file_exists($filePath) && is_readable($filePath)) {
$returnVal['fileContent'] = base64_encode(file_get_contents($filePath));
}
else {
trigger_error("[SecDoc] localDocMGMT.php -> Datei '$filePath' konnte nicht gelesen werden");
error_log("[SecDoc] localDocMGMT.php -> Datei '$filePath' konnte nicht gelesen werden");
}
return $returnVal;
}
/**
* Fügt ein neues Dokument hinzu.
*
* @abstract
* @param int $processID ID der Dokumentation
* @param string $fileName Dateiname
* @param string $fileContent Dateiinhalt (base64 kodiert)
* @return string Referenz zum Dokument
* @throws Exception
*/
public function addDocument($processID, $fileName, $fileContent) {
global $pdf_dir;
if(!is_writable($pdf_dir)) throw new Exception("Fehlende Schreibberechtigung in '$pdf_dir'");
$processDir = $pdf_dir . DIRECTORY_SEPARATOR . $processID;
if(!file_exists($processDir)) {
if(!mkdir($processDir)) throw new Exception("Kann Verzeichnis '$processDir' nicht erstellen");
}
# Sonderzeichen entschärfen
$fileName = str_replace('.pdf', '', $fileName);
$fileName = preg_replace('/[^A-Za-z0-9_\-]/', '_', $fileName);
$fileName .= '.pdf';
$filePath = $pdf_dir . DIRECTORY_SEPARATOR . $processID . DIRECTORY_SEPARATOR . $fileName;
if(file_exists($filePath)) {
$fileName = time() . '_' . $fileName;
$filePath = $pdf_dir . DIRECTORY_SEPARATOR . $processID . DIRECTORY_SEPARATOR . $fileName;
}
if(file_put_contents($filePath, base64_decode($fileContent)) === FALSE) {
trigger_error("[SecDoc] localDocMGMT.php -> Datei '$filePath' konnte nicht geschrieben werden");
error_log("[SecDoc] localDocMGMT.php -> Datei '$filePath' konnte nicht geschrieben werden");
return '';
}
else {
return $fileName;
}
}
/**
* Aktualisiert ein Dokument.
*
* @abstract
* @param int $processID ID der Dokumentation
* @param string $fileRef Dateireferenz
* @param string $fileName Dateiname
* @param string $fileContent Dateiinhalt (base64 kodiert)
* @return bool Neue Referenz zum Dokument
* @throws Exception
*/
public function updateDocument($processID, $fileRef, $fileName, $fileContent){
global $pdf_dir;
if(!is_writable($pdf_dir)) throw new Exception("Fehlende Schreibberechtigung in '$pdf_dir'");
# Sonderzeichen entschärfen
$fileName = str_replace('.pdf', '', $fileName);
$fileName = preg_replace('/[^A-Za-z0-9_\-]/', '_', $fileName);
$fileName .= '.pdf';
$filePathOld = $pdf_dir . DIRECTORY_SEPARATOR . $processID . DIRECTORY_SEPARATOR . $fileRef;
$filePathNew = $pdf_dir . DIRECTORY_SEPARATOR . $processID . DIRECTORY_SEPARATOR . $fileName;
if($filePathOld !== $filePathNew && file_exists($filePathNew)) {
$fileName = time() . $fileName;
$filePathNew = $pdf_dir . DIRECTORY_SEPARATOR . $processID . DIRECTORY_SEPARATOR . $fileName;
}
if(file_put_contents($filePathNew, base64_decode($fileContent)) === FALSE) {
trigger_error("[SecDoc] localDocMGMT.php -> Datei '$filePathNew' konnte nicht geschrieben werden");
error_log("[SecDoc] localDocMGMT.php -> Datei '$filePathNew' konnte nicht geschrieben werden");
return '';
}
else {
if($filePathOld !== $filePathNew) {
if(!unlink($filePathOld)) {
trigger_error("[SecDoc] localDocMGMT.php -> Datei '$filePathOld' konnte nicht gelöscht werden");
error_log("[SecDoc] localDocMGMT.php -> Datei '$filePathOld' konnte nicht gelöscht werden");
}
}
return $fileName;
}
}
/**
* Löscht ein Dokument.
*
* @abstract
* @param int $processID ID der Dokumentation
* @param string $fileRef Dateireferenz
* @return bool TRUE bei Erfolg, sonst FALSE
* @throws Exception
*/
public function deleteDocument($processID, $fileRef) {
global $pdf_dir;
if(!is_writable($pdf_dir)) throw new Exception("Fehlende Schreibberechtigung in '$pdf_dir'");
$filePath = $pdf_dir . DIRECTORY_SEPARATOR . $processID . DIRECTORY_SEPARATOR . $fileRef;
if(unlink($filePath)) {
if(count(glob($pdf_dir . DIRECTORY_SEPARATOR . $processID . DIRECTORY_SEPARATOR . '*')) === 0) {
if(!rmdir($pdf_dir . DIRECTORY_SEPARATOR . $processID)) {
trigger_error("[SecDoc] localDocMGMT.php -> Leerer Ordner '" . $pdf_dir . DIRECTORY_SEPARATOR . $processID . "' konnte nicht gelöscht werden");
error_log("[SecDoc] localDocMGMT.php -> Leerer Ordner '" . $pdf_dir . DIRECTORY_SEPARATOR . $processID . "' konnte nicht gelöscht werden");
}
}
return TRUE;
}
else {
trigger_error("[SecDoc] localDocMGMT.php -> Datei '$filePath' konnte nicht gelöscht werden");
error_log("[SecDoc] localDocMGMT.php -> Datei '$filePath' konnte nicht gelöscht werden");
return FALSE;
}
}
}
......@@ -249,7 +249,7 @@
require_once '../vendor/autoload.php';
global $dbcon, $userId, $pdf_dir, $prog_name, $prog_version, $prog_url;
global $dbcon, $userId, $pdf_dir, $prog_name, $prog_version, $prog_url, $docmgmtClass, $temp_dir;
$res = Utils::searchUsers($userId, TRUE);
$author = !empty($res) ? $res[0]['name'] : '';
......@@ -320,6 +320,46 @@ EOH;
$mpdf->WriteHTML($part,2);
}
if(!$isDraft) {
# Angehängte PDF-Dokumente einbetten
$attachedDocs = $dbcon->listDocuments($verfahrensId);
if(count($attachedDocs) > 0) {
$mpdf->Bookmark("Angehängte Dokumente", 0);
}
foreach($attachedDocs as $doc) {
$file = $docmgmtClass->getDocument($verfahrensId, $doc['FileRef']);
$tmpFile = $temp_dir . DIRECTORY_SEPARATOR . "{$verfahrensId}_{$doc['DocID']}.pdf";
if(file_put_contents($tmpFile, base64_decode($file['fileContent'])) === FALSE) {
error_log("[SecDoc] verwaltung.php -> Konnte temporäre PDF von angehängtem Dokument nicht erstellen (Dokumentation #{$verfahrensId} - Dokument #{$doc['DocID']})");
continue;
}
$mpdf->WriteHTML('<pagebreak />');
$tocTitle = empty($doc['Description']) ? $doc['FileRef'] : $doc['Description'];
if(strlen($tocTitle) > 43) {
$tocTitle = trim(substr($tocTitle, 0, 40)) . '...';
}
$mpdf->Bookmark(htmlspecialchars($tocTitle), 1);
try {
$pageCount = $mpdf->SetSourceFile($tmpFile);
for($c = 1; $c <= $pageCount; $c++) {
$mpdf->UseTemplate($mpdf->ImportPage($c));
if($c !== $pageCount) $mpdf->WriteHTML('<pagebreak />');
}
} catch(\Exception $e) {
$mpdf->WriteHTML('<h3>Die angehängte PDF konnte nicht gelesen werden! Möglicherweise wird das Format nicht unterstützt.</h3>');
error_log("[SecDoc] verwaltung.php -> PDF konnte nicht gelesen werden (Dokument #{$doc['DocID']}) (Fehler: " . $e->getMessage() . ")");
unlink($tmpFile);
continue;
}
unlink($tmpFile);
}
}
# PDF generieren und zurückgeben
#$title = preg_replace('/\W/', '_', $title); # Alle Nicht-Wort-Zeichen in Unterstriche umwandeln
$pdf_content = $mpdf->Output(
......@@ -1314,12 +1354,16 @@ EOH;
break;
}
# Löscht ein Verfahren
# Löscht ein Verfahren (es wird aktuell nur als gelöscht markiert)
case 'delete': {
if(empty($verfahrensId)) {
returnError('Keine ID für ein Verfahren wurde übergeben!');
}
if(!$userIsDSB && $dbcon->getPermissionLevel($verfahrensId, $userId, $userGroups) < 2) {
returnError('Keine Schreibberechtigung für die gewählte Dokumentation!');
}
$dependencies = $dbcon->getDependencies($verfahrensId, $userId, $userGroups, $userIsDSB);
if(is_array($dependencies) && count($dependencies) > 0) {
......@@ -1328,20 +1372,6 @@ EOH;
$success = $dbcon->delVerfahren($verfahrensId, $userId, $userGroups, $userIsDSB);
if($success) {
if(!unlink($includes_dir . DIRECTORY_SEPARATOR . $verfahrensId . '.txt')) {
error_log('[SecDoc] verwaltung.php -> Konnte Include-Textbaustein zur ID "' . $verfahrensId . '" nicht löschen!');
}
if(!unlink($pdf_dir . DIRECTORY_SEPARATOR . $verfahrensId . '.pdf')) {
error_log('[SecDoc] verwaltung.php -> Konnte PDF zur ID "' . $verfahrensId . '" nicht löschen!');
}
if(!unlink($pdf_dir . DIRECTORY_SEPARATOR . $verfahrensId . '_DRAFT.pdf')) {
error_log('[SecDoc] verwaltung.php -> Konnte Vorschau-PDF zur ID "' . $verfahrensId . '" nicht löschen!');
}
}
if(!$success) {
returnError('Kein Verfahren wurde gelöscht, da entweder das Verfahren nicht gefunden wurde oder Sie keine Berechtigung haben!');
}
......@@ -1729,6 +1759,145 @@ EOH;
break;
}
case 'adddocument': {
if(empty($verfahrensId)) {
returnError('Keine ID für ein Verfahren wurde übergeben!');
}
if(empty($data) || empty($data['filename']) || empty($data['filecontent'])) {
returnError('Dateiinformationen fehlen!');
}
if(!$userIsDSB && $dbcon->getPermissionLevel($verfahrensId, $userId, $userGroups) < 2) {
returnError('Keine Schreibberechtigung für die gewählte Dokumentation!');
}
# Dateityp prüfen
$finfo = finfo_open();
if(!in_array(finfo_buffer($finfo, base64_decode($data['filecontent']), FILEINFO_MIME_TYPE), ['application/pdf'])) {
returnError('Es können nur PDF Dateien hinterlegt werden!');
}
global $docmgmtClass;
$newFileRef = $docmgmtClass->addDocument($verfahrensId, $data['filename'], $data['filecontent']);
if(empty($newFileRef)) returnError('Konnte Dokument nicht abspeichern!');
$newDocID = $dbcon->addDocument($verfahrensId, !empty($data['description']) ? $data['description'] : '', $newFileRef);
if($newDocID === -1) returnError('Konnte Document nicht in Datenbank anlegen!');
$output['success'] = TRUE;
break;
}
case 'updatedocument': {
if(empty($data) || empty($data['docid'])) {
returnError('Dokumentinformationen fehlen!');
}
$docDetails = $dbcon->getDocumentDetails(intval($data['docid']));
if(empty($docDetails)) {
returnError('Zu aktualisierendes Dokument existiert nicht!');
}
if(!$userIsDSB && $dbcon->getPermissionLevel($docDetails['ProcessID'], $userId, $userGroups) < 2) {
returnError('Keine Schreibberechtigung für die gewählte Dokumentation!');
}
if(!empty($data['filename']) && !empty($data['filecontent'])) {
# Dateityp prüfen
$finfo = finfo_open();
if(!in_array(finfo_buffer($finfo, base64_decode($data['filecontent']), FILEINFO_MIME_TYPE), ['application/pdf'])) {
returnError('Es können nur PDF Dateien hinterlegt werden!');
}
global $docmgmtClass;
$newFileRef = $docmgmtClass->updateDocument($docDetails['ProcessID'], $docDetails['FileRef'], $data['filename'], $data['filecontent']);
if(empty($newFileRef)) returnError('Konnte Dokument nicht abspeichern!');
}
else {
$newFileRef = $docDetails['FileRef'];
}
if(!$dbcon->updateDocument($docDetails['DocID'], !empty($data['description']) ? $data['description'] : '', $newFileRef)) returnError('Konnte Document nicht in Datenbank aktualisieren!');
$output['success'] = TRUE;
break;
}
case 'getdocument': {
if(empty($data['docid'])) {
returnError('Dokumenten ID fehlt!');
}
$docDetails = $dbcon->getDocumentDetails(intval($data['docid']));
if(empty($docDetails)) {
returnError('Angefragtes Dokument existiert nicht!');
}
if(!$userIsDSB && $dbcon->getPermissionLevel($docDetails['ProcessID'], $userId, $userGroups) < 1) {
returnError('Keine Leseberechtigung für das angefragte Dokument!');
}
global $docmgmtClass;
$file = $docmgmtClass->getDocument($docDetails['ProcessID'], $docDetails['FileRef']);
if(empty($file) || empty($file['fileName']) || empty($file['fileContent'])) returnError('Dokument konnte nicht gelesen werden!');
$output['success'] = TRUE;
$output['data'] = $file;
break;
}
case 'deletedocument': {
if(empty($data['docid'])) {
returnError('Dokumenten ID fehlt!');
}
$docDetails = $dbcon->getDocumentDetails(intval($data['docid']));
if(empty($docDetails)) {
returnError('Angefragtes Dokument existiert nicht!');
}
if(!$userIsDSB && $dbcon->getPermissionLevel($docDetails['ProcessID'], $userId, $userGroups) < 2) {
returnError('Keine Schreibberechtigung für das angefragte Dokument!');
}
global $docmgmtClass;
$delSuccess = $docmgmtClass->deleteDocument($docDetails['ProcessID'], $docDetails['FileRef']);
if(!$delSuccess) returnError('Dokument konnte nicht gelöscht werden!');
$dbcon->deleteDocument(intval($data['docid']));
$output['success'] = TRUE;
break;
}
case 'listdocuments': {
if(empty($verfahrensId)) {
returnError('Keine ID für ein Verfahren wurde übergeben!');
}
if(!$userIsDSB && $dbcon->getPermissionLevel($verfahrensId, $userId, $userGroups) < 1) {
returnError('Keine Leseberechtigung für die angefragte Dokumentation!');
}
$documents = $dbcon->listDocuments($verfahrensId);
$output['success'] = TRUE;
$output['data'] = $documents;
$output['count'] = count($documents);
break;
}
case 'login': {
$output['data']['msg'] = 'Erfolgreich eingeloggt';
break;
......
......@@ -460,53 +460,22 @@ Art. 25 DSGVO Datenschutz durch Technikgestaltung und durch datenschutzfreundlic
<h5 class="info-text text-ul">Abschluss</h5>
</div>
<div class="col-sm-offset-1 col-sm-10">
<h6 class="info-text text-ul-dot">Referenzdokumente</h6>
<p class="printHide">Hier haben Sie die Möglichkeit auf bereits vorhandene Dokumente zu verweisen, die beim fachlichen oder technischen Verantwortlichen eingesehen werden können.</p>
<ul class="list-unstyled">
<li>
<div class="form-check">
<label class="form-check-label"><input class="form-check-input additionalDocs" name="abschluss_dokumente_risikoanalyse" type="checkbox" value="1"> Risikoanalyse</label>
<input type="text" class="form-control docInputs hidden" name="abschluss_dokumente_risikoanalyse_url" placeholder="URL zu Datei-Speicherort bzw. Hinweis auf Dokument-Ablageort">
</div>
</li>
<li>
<div class="form-check">
<label class="form-check-label"><input class="form-check-input additionalDocs" name="abschluss_dokumente_datensicherheitbeschreibung" type="checkbox" value="1"> Allgemeine Datensicherheitsbeschreibung</label>
<input type="text" class="form-control docInputs hidden" name="abschluss_dokumente_datensicherheitbeschreibung_url" placeholder="URL zu Datei-Speicherort bzw. Hinweis auf Dokument-Ablageort">
</div>
</li>
<li>
<div class="form-check">
<label class="form-check-label"><input class="form-check-input additionalDocs" name="abschluss_dokumente_datensicherheitskonzept" type="checkbox" value="1"> Umfassendes Datensicherheitskonzept</label>
<input type="text" class="form-control docInputs hidden" name="abschluss_dokumente_datensicherheitskonzept_url" placeholder="URL zu Datei-Speicherort bzw. Hinweis auf Dokument-Ablageort">
</div>
</li>
<li>
<div class="form-check">
<label class="form-check-label"><input class="form-check-input additionalDocs" name="abschluss_dokumente_wiederanlaufkonzept" type="checkbox" value="1"> Notfallkonzept / Wiederanlaufkonzept</label>
<input type="text" class="form-control docInputs hidden" name="abschluss_dokumente_wiederanlaufkonzept_url" placeholder="URL zu Datei-Speicherort bzw. Hinweis auf Dokument-Ablageort">
</div>
</li>
<li>
<div class="form-check">
<label class="form-check-label"><input class="form-check-input additionalDocs" name="abschluss_dokumente_netzstruktur" type="checkbox" value="1"> Netzstrukturbeschreibung</label>
<input type="text" class="form-control docInputs hidden" name="abschluss_dokumente_netzstruktur_url" placeholder="URL zu Datei-Speicherort bzw. Hinweis auf Dokument-Ablageort">
</div>
</li>
<li>
<div class="form-check">
<label class="form-check-label"><input class="form-check-input additionalDocs" name="abschluss_dokumente_verhaltensregeln" type="checkbox" value="1"> Interne Verhaltensregeln</label>
<input type="text" class="form-control docInputs hidden" name="abschluss_dokumente_verhaltensregeln_url" placeholder="URL zu Datei-Speicherort bzw. Hinweis auf Dokument-Ablageort">
</div>
</li>
<li>
<div class="form-check">
<label class="form-check-label"><input class="form-check-input additionalDocs" name="abschluss_dokumente_sonstige_label" type="checkbox" value="1"> Sonstige Dokumente</label>
<input type="text" class="form-control docInputs hidden" name="abschluss_dokumente_sonstige" placeholder="URL zu Datei-Speicherort bzw. Hinweis auf Dokument-Ablageort">
</div>
</li>
</ul>
<div class="col-sm-offset-1 col-sm-10 printHide">
<h6 class="info-text text-ul-dot">Angehängte Dokumente</h6>
<p>Hier können vorhandene Dokumente im PDF-Format an die Dokumentation angehangen und hinterlegt werden.</p>
<table id="attached_documents" class="table table-hover btn-table">
<thead>
<tr>
<th>Dateireferenz</th>
<th>Beschreibung</th>
<th>Letzte Aktualisierung</th>
<th></th>
</tr>
</thead>
<tbody>
</tbody>
</table>
<button type="button" id="attached_documents_add" class="btn btn-wd btn-success"><i class="fa fa-plus"></i> Hinzufügen</button>
</div>
<div class="col-sm-offset-1 col-sm-10 printHide">
......
......@@ -455,53 +455,22 @@ Art. 25 DSGVO Datenschutz durch Technikgestaltung und durch datenschutzfreundlic
<h5 class="info-text text-ul">Abschluss</h5>
</div>
<div class="col-sm-offset-1 col-sm-10">
<h6 class="info-text text-ul-dot">Referenzdokumente</h6>
<p class="printHide">Hier haben Sie die Möglichkeit auf bereits vorhandene Dokumente zu verweisen, die beim fachlichen oder technischen Verantwortlichen eingesehen werden können.</p>
<ul class="list-unstyled">
<li>
<div class="form-check">
<label class="form-check-label"><input class="form-check-input additionalDocs" name="abschluss_dokumente_risikoanalyse" type="checkbox" value="1"> Risikoanalyse</label>
<input type="text" class="form-control docInputs hidden" name="abschluss_dokumente_risikoanalyse_url" placeholder="URL zu Datei-Speicherort bzw. Hinweis auf Dokument-Ablageort">
</div>
</li>
<li>
<div class="form-check">
<label class="form-check-label"><input class="form-check-input additionalDocs" name="abschluss_dokumente_datensicherheitbeschreibung" type="checkbox" value="1"> Allgemeine Datensicherheitsbeschreibung</label>
<input type="text" class="form-control docInputs hidden" name="abschluss_dokumente_datensicherheitbeschreibung_url" placeholder="URL zu Datei-Speicherort bzw. Hinweis auf Dokument-Ablageort">
</div>
</li>
<li>
<div class="form-check">
<label class="form-check-label"><input class="form-check-input additionalDocs" name="abschluss_dokumente_datensicherheitskonzept" type="checkbox" value="1"> Umfassendes Datensicherheitskonzept</label>
<input type="text" class="form-control docInputs hidden" name="abschluss_dokumente_datensicherheitskonzept_url" placeholder="URL zu Datei-Speicherort bzw. Hinweis auf Dokument-Ablageort">
</div>
</li>
<li>
<div class="form-check">
<label class="form-check-label"><input class="form-check-input additionalDocs" name="abschluss_dokumente_wiederanlaufkonzept" type="checkbox" value="1"> Notfallkonzept / Wiederanlaufkonzept</label>
<input type="text" class="form-control docInputs hidden" name="abschluss_dokumente_wiederanlaufkonzept_url" placeholder="URL zu Datei-Speicherort bzw. Hinweis auf Dokument-Ablageort">
</div>
</li>
<li>
<div class="form-check">
<label class="form-check-label"><input class="form-check-input additionalDocs" name="abschluss_dokumente_netzstruktur" type="checkbox" value="1"> Netzstrukturbeschreibung</label>
<input type="text" class="form-control docInputs hidden" name="abschluss_dokumente_netzstruktur_url" placeholder="URL zu Datei-Speicherort bzw. Hinweis auf Dokument-Ablageort">
</div>
</li>
<li>
<div class="form-check">
<label class="form-check-label"><input class="form-check-input additionalDocs" name="abschluss_dokumente_verhaltensregeln" type="checkbox" value="1"> Interne Verhaltensregeln</label>
<input type="text" class="form-control docInputs hidden" name="abschluss_dokumente_verhaltensregeln_url" placeholder="URL zu Datei-Speicherort bzw. Hinweis auf Dokument-Ablageort">
</div>
</li>
<li>
<div class="form-check">
<label class="form-check-label"><input class="form-check-input additionalDocs" name="abschluss_dokumente_sonstige_label" type="checkbox" value="1"> Sonstige Dokumente</label>
<input type="text" class="form-control docInputs hidden" name="abschluss_dokumente_sonstige" placeholder="URL zu Datei-Speicherort bzw. Hinweis auf Dokument-Ablageort">
</div>
</li>
</ul>
<div class="col-sm-offset-1 col-sm-10 printHide">
<h6 class="info-text text-ul-dot">Angehängte Dokumente</h6>
<p>Hier können vorhandene Dokumente im PDF-Format an die Dokumentation angehangen und hinterlegt werden.</p>
<table id="attached_documents" class="table table-hover btn-table">
<thead>
<tr>
<th>Dateireferenz</th>