Revisionsverlauf eingebaut (Closes #70); Fix für Dokumenten-Anhang (fehlende Werte)

parent 2b105d04
......@@ -258,6 +258,14 @@
Attach INT NOT NULL DEFAULT 0, -- Dokument an Abschluss-PDF anhängen?
Date DATE NOT NULL DEFAULT (datetime(CURRENT_TIMESTAMP, 'localtime')), -- Letztes Änderungsdatum
FOREIGN KEY (ProcessID) REFERENCES verfahren(ID) ON UPDATE CASCADE ON DELETE CASCADE
);",
"CREATE TABLE revisions ( -- Speichert die Revisionen einer Dokumentation
ProcessID INT NOT NULL, -- ID der Dokumentation
Revision INT NOT NULL, -- Nummer der Revision
Comment TEXT NOT NULL DEFAULT '', -- Kommentar
Editor TEXT NOT NULL, -- Name des Bearbeiters
Date DATE NOT NULL DEFAULT (datetime(CURRENT_TIMESTAMP, 'localtime')), -- Änderungsdatum
FOREIGN KEY (ProcessID) REFERENCES verfahren(ID) ON UPDATE CASCADE ON DELETE CASCADE
);"
];
......@@ -403,6 +411,15 @@
Attach INT NOT NULL DEFAULT 0, -- Dokument an Abschluss-PDF anhängen?
Date DATE NOT NULL DEFAULT (datetime(CURRENT_TIMESTAMP, 'localtime')), -- Letztes Änderungsdatum
FOREIGN KEY (ProcessID) REFERENCES verfahren(ID) ON UPDATE CASCADE ON DELETE CASCADE
);",
# 18
"CREATE TABLE revisions ( -- Speichert die Revisionen einer Dokumentation
ProcessID INT NOT NULL, -- ID der Dokumentation
Revision INT NOT NULL, -- Nummer der Revision
Comment TEXT NOT NULL DEFAULT '', -- Kommentar
Editor TEXT NOT NULL, -- Name des Bearbeiters
Date DATE NOT NULL DEFAULT (datetime(CURRENT_TIMESTAMP, 'localtime')), -- Änderungsdatum
FOREIGN KEY (ProcessID) REFERENCES verfahren(ID) ON UPDATE CASCADE ON DELETE CASCADE
);"
];
......@@ -610,6 +627,7 @@
$this->pdo->beginTransaction();
# Neue Tabelle für angehängte Dokumente
$this->pdo->exec(self::TABLES[17]);
$this->pdo->exec(self::TABLES[18]);
# Alte TOMs auf 'unbearbeitet' setzen, wenn nicht umgesetzt und kein Kommentar hinterlegt
$sth1 = $this->pdo->prepare('SELECT ID, JSON FROM verfahren WHERE NOT Status = 3;');
......@@ -632,6 +650,7 @@
$sth2->execute([json_encode($parsedJSON), $entry['ID']]);
}
$this->pdo->exec("PRAGMA user_version = 11;");
$this->pdo->commit();
}
......@@ -2078,6 +2097,92 @@
return TRUE;
}
/**
* Fügt einen Revisionseintrag hinzu.
*
* @param int $verfahrensId ID einer Dokumentation
* @param string $comment Revisionskommentar
* @param string $editor Name des Bearbeiters
* @return bool TRUE
* @throws PDOException
* @throws Exception
*/
public function addRevision($verfahrensId, $comment, $editor) {
if(!$this->isConnected()) {
throw new Exception("DBCon.class.php -> Keine aktive Datenbank-Verbindung!");
}
# Letzte Revision holen
$sql = 'SELECT * FROM revisions WHERE ProcessID = ? ORDER BY Revision DESC LIMIT 1;';
$sth1 = $this->pdo->prepare($sql);
$sth1->execute([$verfahrensId]);
ob_start();
$sth1->debugDumpParams();
$sqlDump = ob_get_clean();
print "DBCon.class.php -> addRevision() Execute: $sqlDump";
$lastRevision = $sth1->fetch();
if(empty($lastRevision)) {
$newRevisionNumber = 1;
}
else {
$newRevisionNumber = intval($lastRevision['Revision']) + 1;
}
# Neue Revision eintragen
$sql = 'INSERT INTO revisions (ProcessID, Revision, Comment, Editor) VALUES (?, ?, ?, ?);';
$sth2 = $this->pdo->prepare($sql);
$sth2->execute([$verfahrensId, $newRevisionNumber, $comment, $editor]);
ob_start();
$sth2->debugDumpParams();
$sqlDump = ob_get_clean();
print "DBCon.class.php -> addRevision() Execute: $sqlDump";
$changedRows = $sth2->rowCount();
if($changedRows !== 1) {
throw new Exception("DBCon.class.php -> Fehler beim Hinzufügen der Revision! (Fehler: Unbekannter Fehler - Anzahl geänderter Reihen: $changedRows)");
}
return TRUE;
}
/**
* Listet die Revisionen einer Dokumentation auf.
*
* @param int $verfahrensId ID einer Dokumentation
* @return mixed[] Liste der Revisionen [['ProcessID' => 1, 'Revision' => 2, 'Comment' => 'Test', 'Editr' => 'Name', 'Date' => '20202-09-15'],...]
* @throws PDOException
* @throws Exception
*/
public function listRevisions($verfahrensId) {
if(!$this->isConnected()) {
throw new Exception("DBCon.class.php -> Keine aktive Datenbank-Verbindung!");
}
# Letzte Revision holen
$sql = 'SELECT * FROM revisions WHERE ProcessID = ? ORDER BY Revision DESC;';
$sth1 = $this->pdo->prepare($sql);
$sth1->execute([$verfahrensId]);
ob_start();
$sth1->debugDumpParams();
$sqlDump = ob_get_clean();
print "DBCon.class.php -> listRevisions() Execute: $sqlDump";
$lastRevisions = $sth1->fetchAll();
if(empty($lastRevisions)) {
return [];
}
else {
return $lastRevisions;
}
}
/**
* Schreibt einen Eintrag in die Bearbeitungs-Historie.
* Sollte immer nur in Verbindung mit einer Aktion genutzt werden.
......
......@@ -293,6 +293,36 @@
$html = str_replace('$docurl$', $prog_url . "?id=$verfahrensId", $html);
$html = str_replace('$baseurl$', $prog_url, $html);
# Revisionen holen und einfügen
$revisionsHTML = <<<EOH
<p class="info-text text-ul text-center">Revisionen</p>
<table id="abschluss_revisionen" class="table table-hover btn-table" style="border: 1px solid darkgray; padding: 5px;">
<thead>
<tr>
<th style="border: 1px solid darkgray; padding: 5px; background-color: lightgray;">Revision</th>
<th style="border: 1px solid darkgray; padding: 5px; background-color: lightgray;">Datum</th>
<th style="border: 1px solid darkgray; padding: 5px; background-color: lightgray;">Bearbeiter</th>
<th style="border: 1px solid darkgray; padding: 5px; background-color: lightgray;">Kommentar</th>
</tr>
</thead>
<tbody>
EOH;
foreach($dbcon->listRevisions($verfahrensId) as $revision) {
$revisionsHTML .= <<<EOH
<tr>
<td style="border: 1px solid darkgray; padding: 5px;">{$revision['Revision']}</td>
<td style="border: 1px solid darkgray; padding: 5px;">{$revision['Date']}</td>
<td style="border: 1px solid darkgray; padding: 5px;">{$revision['Editor']}</td>
<td style="border: 1px solid darkgray; padding: 5px;">{$revision['Comment']}</td>
</tr>
EOH;
}
$revisionsHTML .= <<<EOH
</tbody>
</table>
EOH;
$html = str_replace('$docrevisions$', $revisionsHTML, $html);
# HTML aufbauen
$finalHTML = <<<EOH
<body>
......@@ -1339,8 +1369,8 @@ EOH;
returnError('Keine ID für ein Verfahren wurde übergeben!');
}
if(empty($data)) {
returnError('Kein JSON-kodierter Inhalt zum Abschluss wurde übergeben (title, pdfCode)!');
if(empty($data) || !array_key_exists('title', $data) || !array_key_exists('pdfCode', $data) || !array_key_exists('lastupdate', $data) || !array_key_exists('comment', $data)) {
returnError('Kein JSON-kodierter Inhalt zum Abschluss wurde übergeben (title, pdfCode, lastupdate, comment)!');
}
// Überprüfen, ob das Verfahren nicht durch eine andere Person seit dem letzten Laden bearbeitet wurde
......@@ -1356,6 +1386,11 @@ EOH;
returnError('Kein Verfahren wurde aktualisiert, da entweder das Verfahren nicht gefunden wurde oder Sie keine Berechtigung haben!');
}
# Revision anlegen
$editor = Utils::searchUsers($userId, TRUE);
$editor = (empty($editor) || empty($editor[0]['name'])) ? $userId : $editor[0]['name'];
$dbcon->addRevision($verfahrensId, htmlspecialchars($data['comment'], ENT_QUOTES, 'UTF-8', FALSE), $editor);
if($success) {
$output['gentxt'] = generateTXT($dbcon, $userId, $userGroups, $userIsDSB, $verfahrensId);
$output['genpdf'] = generatePDF(htmlspecialchars($data['title'], ENT_QUOTES, 'UTF-8', FALSE), $data['pdfCode'], $verfahrensId);
......@@ -1922,6 +1957,23 @@ EOH;
break;
}
case 'listrevisions': {
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!');
}
$revisions = $dbcon->listRevisions($verfahrensId);
$output['success'] = TRUE;
$output['data'] = $revisions;
$output['count'] = count($revisions);
break;
}
case 'login': {
$output['data']['msg'] = 'Erfolgreich eingeloggt';
break;
......@@ -1941,7 +1993,7 @@ EOH;
# Falls keine bekannte Aktion angegeben wurde
default: {
$output['error'] = 'Es wurde kein oder kein unterstützter Modus (list, listdsb, get, create, update, delete, finish, updatecomment, history, dependencies, searchperson, serachabteilung, searchivv, getusergroups, searchapp, searchos, searchipdns, getaufstellungsort, getstats, gettoms, getsuggestions, gencombinedpdf) angegeben!';
$output['error'] = 'Es wurde kein oder kein unterstützter Modus (list, listdsb, get, create, update, delete, finish, updatecomment, history, dependencies, searchperson, serachabteilung, searchivv, getusergroups, searchapp, searchos, searchipdns, getaufstellungsort, getstats, gettoms, getsuggestions, gencombinedpdf, addDocument, listDocuments, updateDocument, deleteDocument, listRevisions) angegeben!';
break;
}
}
......
......@@ -63,7 +63,7 @@
text-align: left;
}
i[data-toggle="tooltip"] {
i[data-toggle="tooltip"], .cursor-help {
cursor: help;
}
......@@ -177,6 +177,10 @@ input.customError {
cursor: pointer !important;
}
.cursor-progress {
cursor: progress !important;
}
.strong {
font-weight: 700 !important;
}
......
......@@ -481,6 +481,22 @@ Art. 25 DSGVO Datenschutz durch Technikgestaltung und durch datenschutzfreundlic
<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 dsbOnly">
<h6 class="info-text text-ul-dot">Revisionen</h6>
<table id="abschluss_revisionen" class="table table-hover btn-table">
<thead>
<tr>
<th>Revision</th>
<th>Datum</th>
<th>Bearbeiter</th>
<th>Kommentar</th>
</tr>
</thead>
<tbody>
</tbody>
</table>
</div>
<div class="col-sm-offset-1 col-sm-10 printHide">
<h6 class="info-text text-ul-dot">Zugriffsberechtigungen innerhalb von SecDoc</h6>
<p>Hier können Berechtigungen zum Lesen und/oder Bearbeiten festgelegt werden, um beispielsweise Kollegen die Mitarbeit in SecDoc zu ermöglichen. Eingetragene Nutzer oder Gruppen bekommen Lesezugriff. Optional kann auch Schreibzugriff gewährt werden. <em>Der/die Datenschutzbeauftragte kann unabhängig von den hier getätigten Einstellungen alle Informationen einsehen.</em></p>
......
......@@ -476,6 +476,22 @@ Art. 25 DSGVO Datenschutz durch Technikgestaltung und durch datenschutzfreundlic
<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 dsbOnly">
<h6 class="info-text text-ul-dot">Revisionen</h6>
<table id="abschluss_revisionen" class="table table-hover btn-table">
<thead>
<tr>
<th>Revision</th>
<th>Datum</th>
<th>Bearbeiter</th>
<th>Kommentar</th>
</tr>
</thead>
<tbody>
</tbody>
</table>
</div>
<div class="col-sm-offset-1 col-sm-10 printHide">
<h6 class="info-text text-ul-dot">Zugriffsberechtigungen innerhalb von SecDoc</h6>
<p>Hier können Berechtigungen zum Lesen und/oder Bearbeiten festgelegt werden, um beispielsweise Kollegen die Mitarbeit in SecDoc zu ermöglichen. Eingetragene Nutzer oder Gruppen bekommen Lesezugriff. Optional kann auch Schreibzugriff gewährt werden. <em>Der/die Datenschutzbeauftragte kann unabhängig von den hier getätigten Einstellungen alle Informationen einsehen.</em></p>
......
......@@ -164,6 +164,22 @@ Art. 25 DSGVO Datenschutz durch Technikgestaltung und durch datenschutzfreundlic
<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 dsbOnly">
<h6 class="info-text text-ul-dot">Revisionen</h6>
<table id="abschluss_revisionen" class="table table-hover btn-table">
<thead>
<tr>
<th>Revision</th>
<th>Datum</th>
<th>Bearbeiter</th>
<th>Kommentar</th>
</tr>
</thead>
<tbody>
</tbody>
</table>
</div>
<div class="col-sm-offset-1 col-sm-10 printHide">
<h6 class="info-text text-ul-dot">Zugriffsberechtigungen innerhalb von SecDoc</h6>
<p>Hier können Berechtigungen zum Lesen und/oder Bearbeiten festgelegt werden, um beispielsweise Kollegen die Mitarbeit in SecDoc zu ermöglichen. Eingetragene Nutzer oder Gruppen bekommen Lesezugriff. Optional kann auch Schreibzugriff gewährt werden. <em>Der/die Datenschutzbeauftragte kann unabhängig von den hier getätigten Einstellungen alle Informationen einsehen.</em></p>
......
......@@ -608,6 +608,22 @@ Art. 35 DSGVO" class="fa fa-question-circle-o fa-lg"></i></h6>
<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 dsbOnly">
<h6 class="info-text text-ul-dot">Revisionen</h6>
<table id="abschluss_revisionen" class="table table-hover btn-table">
<thead>
<tr>
<th>Revision</th>
<th>Datum</th>
<th>Bearbeiter</th>
<th>Kommentar</th>
</tr>
</thead>
<tbody>
</tbody>
</table>
</div>
<div class="col-sm-offset-1 col-sm-10 printHide">
<h6 class="info-text text-ul-dot">Zugriffsberechtigungen innerhalb von SecDoc</h6>
<p>Hier können Berechtigungen zum Lesen und/oder Bearbeiten festgelegt werden, um beispielsweise Kollegen die Mitarbeit in SecDoc zu ermöglichen. Eingetragene Nutzer oder Gruppen bekommen Lesezugriff. Optional kann auch Schreibzugriff gewährt werden. <em>Der/die Datenschutzbeauftragte kann unabhängig von den hier getätigten Einstellungen alle Informationen einsehen.</em></p>
......
......@@ -148,7 +148,7 @@ function loadTables(tier) {
newEntry.append('<td>' + (data['data'][c]['FachKontakt'] ? data['data'][c]['FachKontakt'] : '-- nicht angegeben --') + '</td>');
newEntry.append('<td>' + (data['data'][c]['TechKontakt'] ? data['data'][c]['TechKontakt'] : '-- nicht angegeben --') + '</td>');
newEntry.append('<td>' + data['data'][c]['Erstelldatum'] + '</td>');
newEntry.append('<td>' + data['data'][c]['Aktualisierung'] + '</td>');
newEntry.append('<td>' + data['data'][c]['Aktualisierung'] + ' <i class="fa fa-history cursor-progress revisionload" data-id="' + data['data'][c]['ID'] + '"></i></td>');
newEntry.append('<td><textarea class="form-control comment" data-id="' + data['data'][c]['ID'] + '" style="resize: both;">' + htmlDecode(data['data'][c]['DSBKommentar']) + '</textarea></td>');
newEntry.append('<td><div class="btn-group inline"><a class="btn" href="?id=' + data['data'][c]['ID'] + (debug ? '&debug=true' : '') + '" target="_blank"><i class="fa fa-edit"></i> Bearbeiten</a><a class="btn" href="?copy=' + data['data'][c]['ID'] + (debug ? '&debug=true' : '') + '" target="_blank"><i class="fa fa-copy"></i> Kopieren</a><button type="button" class="btn pdfdownload" data-id="' + data['data'][c]['ID'] + '" ' + (data['data'][c]['PDF'] ? '' : 'disabled') + '><i class="fa fa-file-pdf-o"></i> ' + (parseInt(data['data'][c]['Status']) === 0 ? 'Letzte abgeschlossene PDF anzeigen' : 'PDF anzeigen') + '</button></div> <button type="button" data-id="' + data['data'][c]['ID'] +'" data-name="' + data['data'][c]['Bezeichnung'] +'" class="btn del btn-danger"><i class="fa fa-minus"></i> Löschen</button></td>');
......@@ -325,8 +325,40 @@ function loadTables(tier) {
]
});
// Tooltips intialisieren
$('#content').find('[data-toggle="tooltip"]').tooltip({container: 'body'});
$('#content').find('i.revisionload').on('mouseover', (evt) => {
let evtTarget = $(evt.target);
if(!evtTarget.hasClass('revisionload')) return;
evtTarget.addClass('hover');
evtTarget.removeClass('revisionload');
$.get(backendPath, {'action': 'listrevisions', 'debug': debug, 'id': evtTarget.data('id')}).done((data) => {
if(!data['success']) {
evtTarget.prop('title', '<Fehler beim Holen der Revisionen>');
}
else {
let revisions = $('<div><p>Letzte 5 Revisionen:</p><ul></ul></div>');
let revList = revisions.find('ul');
for(let c=0; c < data['count'] && c < 5; c++) {
revList.append('<li>#' + data['data'][c]['Revision'] + ' - ' + data['data'][c]['Date'] + ' - ' + data['data'][c]['Editor'] + (data['data'][c]['Comment'] !== '' ? ' - ' + data['data'][c]['Comment'] : ''));
}
if(data['count'] === 0) revList.replaceWith('<Keine Revisionen vorhanden>');
evtTarget.prop('title', revisions[0].outerHTML);
}
}).fail((jqXHR, error, errorThrown) => {
evtTarget.prop('title', '<Fehler beim Holen der Revisionen>');
}).always(() => {
evtTarget.removeClass('cursor-progress').addClass('cursor-help');
evtTarget.tooltip({container: 'body', html: true});
if(evtTarget.hasClass('hover')) evtTarget.tooltip('show');
});
});
$('#content').find('i.revisionload').on('mouseleave', (evt) => { $(evt.target).removeClass('hover'); });
// Tabellengröße anpassen, wenn der Tab gewechselt wird
$('a[data-toggle="tab"]').on("shown.bs.tab", function (e) {
$($.fn.dataTable.tables(true)).DataTable().columns.adjust().responsive.recalc();
......
This diff is collapsed.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment