From 53a05224175e66d3185c570e167fc6a81ec9b67d Mon Sep 17 00:00:00 2001 From: root Date: Mon, 23 Feb 2026 16:11:35 +0100 Subject: [PATCH] Initial commit --- .gitignore | 14 + Classifications.php | 477 ++++++++++++ Corporates.php | 476 ++++++++++++ Persons.php | 532 ++++++++++++++ Publishers.php | 476 ++++++++++++ README.md | 22 + Subjects.php | 545 ++++++++++++++ Treeview.php | 671 +++++++++++++++++ ajax/Autocomplete.php | 31 + ajax/UpdateTerm.php | 84 +++ ajax/db_connection.php.example | 27 + ajax/deleteRelation.php | 86 +++ ajax/deleteTerm.php | 59 ++ ajax/getAuthorityData.php | 167 +++++ ajax/getAuthorityDataRaw.php | 111 +++ ajax/getClassificationData.php | 8 + ajax/getCorporateData.php | 8 + ajax/getDSpaceCount.php | 65 ++ ajax/getDashboardStats.php | 125 ++++ ajax/getDetailAuthorityData.php | 122 ++++ ajax/getPersonData.php | 8 + ajax/getPublisherData.php | 8 + ajax/getRelations.php | 104 +++ ajax/getSubjectData.php | 8 + ajax/getSynonyms.php | 82 +++ ajax/getTreeData.php | 75 ++ ajax/getTreePath.php | 46 ++ ajax/libAuthorities.php | 561 ++++++++++++++ ajax/newClassification.php | 134 ++++ ajax/newCorporate.php | 52 ++ ajax/newPerson.php | 51 ++ ajax/newPublisher.php | 52 ++ ajax/newSubject.php | 50 ++ ajax/saveSynonym.php | 123 ++++ ajax/searchTreeData.php | 75 ++ ajax/writeNewRelation.php | 113 +++ dspace-api/getAuthorityCount.php | 115 +++ getTreePath.php | 46 ++ index.php | 539 ++++++++++++++ js/ClassificationScript.js | 639 ++++++++++++++++ js/CorporateScript.js | 633 ++++++++++++++++ js/PersonScript.js | 629 ++++++++++++++++ js/PublisherScript.js | 634 ++++++++++++++++ js/SubjectScript.js | 882 +++++++++++++++++++++++ migrate_tsdata.sh | 305 ++++++++ setup-libs.sh | 263 +++++++ styles.css | 840 +++++++++++++++++++++ templates/ClassificationDetailsModal.tpl | 55 ++ templates/ClassificationsModal.tpl | 1 + templates/CorporateDetailsModal.tpl | 317 ++++++++ templates/CorporatesModal.tpl | 1 + templates/Footer.html | 16 + templates/Header.html | 709 ++++++++++++++++++ templates/PersonDetailsModal.tpl | 318 ++++++++ templates/PersonsModal.tpl | 1 + templates/PublisherDetailsModal.tpl | 317 ++++++++ templates/PublishersModal.tpl | 1 + templates/RelationsModal.tpl | 349 +++++++++ templates/SubjectDetailsModal.tpl | 745 +++++++++++++++++++ templates/SubjectsModal.tpl | 2 + templates/newClassification.tpl | 111 +++ templates/newCorporate.tpl | 115 +++ templates/newPerson.tpl | 103 +++ templates/newPublisher.tpl | 114 +++ templates/newSubject.tpl | 157 ++++ 65 files changed, 14605 insertions(+) create mode 100644 .gitignore create mode 100644 Classifications.php create mode 100644 Corporates.php create mode 100644 Persons.php create mode 100644 Publishers.php create mode 100644 README.md create mode 100644 Subjects.php create mode 100644 Treeview.php create mode 100644 ajax/Autocomplete.php create mode 100644 ajax/UpdateTerm.php create mode 100644 ajax/db_connection.php.example create mode 100644 ajax/deleteRelation.php create mode 100644 ajax/deleteTerm.php create mode 100644 ajax/getAuthorityData.php create mode 100644 ajax/getAuthorityDataRaw.php create mode 100644 ajax/getClassificationData.php create mode 100644 ajax/getCorporateData.php create mode 100644 ajax/getDSpaceCount.php create mode 100644 ajax/getDashboardStats.php create mode 100644 ajax/getDetailAuthorityData.php create mode 100644 ajax/getPersonData.php create mode 100644 ajax/getPublisherData.php create mode 100644 ajax/getRelations.php create mode 100644 ajax/getSubjectData.php create mode 100644 ajax/getSynonyms.php create mode 100644 ajax/getTreeData.php create mode 100644 ajax/getTreePath.php create mode 100644 ajax/libAuthorities.php create mode 100644 ajax/newClassification.php create mode 100644 ajax/newCorporate.php create mode 100644 ajax/newPerson.php create mode 100644 ajax/newPublisher.php create mode 100644 ajax/newSubject.php create mode 100644 ajax/saveSynonym.php create mode 100644 ajax/searchTreeData.php create mode 100644 ajax/writeNewRelation.php create mode 100644 dspace-api/getAuthorityCount.php create mode 100644 getTreePath.php create mode 100644 index.php create mode 100644 js/ClassificationScript.js create mode 100644 js/CorporateScript.js create mode 100644 js/PersonScript.js create mode 100644 js/PublisherScript.js create mode 100644 js/SubjectScript.js create mode 100755 migrate_tsdata.sh create mode 100755 setup-libs.sh create mode 100644 styles.css create mode 100644 templates/ClassificationDetailsModal.tpl create mode 100644 templates/ClassificationsModal.tpl create mode 100644 templates/CorporateDetailsModal.tpl create mode 100644 templates/CorporatesModal.tpl create mode 100644 templates/Footer.html create mode 100644 templates/Header.html create mode 100644 templates/PersonDetailsModal.tpl create mode 100644 templates/PersonsModal.tpl create mode 100644 templates/PublisherDetailsModal.tpl create mode 100644 templates/PublishersModal.tpl create mode 100644 templates/RelationsModal.tpl create mode 100644 templates/SubjectDetailsModal.tpl create mode 100644 templates/SubjectsModal.tpl create mode 100644 templates/newClassification.tpl create mode 100644 templates/newCorporate.tpl create mode 100644 templates/newPerson.tpl create mode 100644 templates/newPublisher.tpl create mode 100644 templates/newSubject.tpl diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..04def2a --- /dev/null +++ b/.gitignore @@ -0,0 +1,14 @@ +# Datenbankverbindung (enthält Passwörter) +ajax/db_connection.php + +# Logdateien +*.log + +# Backup-Dateien +*.bak + +# Datenbankdumps +*.sql + +# Versionierte Backup-Dateien +*.20* diff --git a/Classifications.php b/Classifications.php new file mode 100644 index 0000000..fbafb39 --- /dev/null +++ b/Classifications.php @@ -0,0 +1,477 @@ + + + + + + + + +
+
+ + +
+
+

🗂️ Klassifikationsverwaltung

+ +
+ + + +
+
+ + +
+
+
+ +
+
+ + + + + + + + + + + + + + + + + + diff --git a/Corporates.php b/Corporates.php new file mode 100644 index 0000000..c48bf6b --- /dev/null +++ b/Corporates.php @@ -0,0 +1,476 @@ + + + + + + + + +
+
+ + +
+
+

🏢 Körperschaftsverwaltung

+ +
+ + + +
+
+ + +
+
+
+ +
+
+ + + + + + + + + + + + + + + + + + diff --git a/Persons.php b/Persons.php new file mode 100644 index 0000000..814fc49 --- /dev/null +++ b/Persons.php @@ -0,0 +1,532 @@ + + + + + + + + +
+
+ + +
+
+

👥 Personenverwaltung

+ +
+ + + +
+
+ + +
+
+
+ +
+
+ + + + + + + + + + + + + + + + + + diff --git a/Publishers.php b/Publishers.php new file mode 100644 index 0000000..28f385f --- /dev/null +++ b/Publishers.php @@ -0,0 +1,476 @@ + + + + + + + + +
+
+ + +
+
+

📚 Verlagsverwaltung

+ +
+ + + +
+
+ + +
+
+
+ +
+
+ + + + + + + + + + + + + + + + + + diff --git a/README.md b/README.md new file mode 100644 index 0000000..8ac34bc --- /dev/null +++ b/README.md @@ -0,0 +1,22 @@ +# Thesaurus + +Webbasierte Thesaurus-Verwaltung für Schlagwörter, Personen, Körperschaften, Verlage und Klassifikationen. + +## Voraussetzungen + +- PHP 7.4 oder höher +- MySQL/MariaDB +- Apache Webserver + +## Installation + +1. Repository klonen +2. `ajax/db_connection.php.example` kopieren und umbenennen: + ```bash + cp ajax/db_connection.php.example ajax/db_connection.php + ``` +3. Zugangsdaten in `ajax/db_connection.php` eintragen +4. Abhängigkeiten installieren: + ```bash + bash setup-libs.sh + ``` diff --git a/Subjects.php b/Subjects.php new file mode 100644 index 0000000..ab55c6c --- /dev/null +++ b/Subjects.php @@ -0,0 +1,545 @@ + + + + + + + + +
+
+ + +
+
+

🏷️ Schlagwortverwaltung

+ +
+ + + +
+
+ + +
+
+
+ +
+
+ + + + + + + + + + + + + + + + + + diff --git a/Treeview.php b/Treeview.php new file mode 100644 index 0000000..bfb9ac9 --- /dev/null +++ b/Treeview.php @@ -0,0 +1,671 @@ + + + + + + + + + + + + + +
+
+ + +
+
+

🌳 Klassifikationsbaum

+
+ + +
+ + + + +
+ + +
+ +
+
+
+ + +
+
+
+
+ +

Wählen Sie eine Klassifikation aus dem Baum aus, um Details anzuzeigen.

+
+
+
+
+
+ +
+ +
+
+ + + + + + + + diff --git a/ajax/Autocomplete.php b/ajax/Autocomplete.php new file mode 100644 index 0000000..235837d --- /dev/null +++ b/ajax/Autocomplete.php @@ -0,0 +1,31 @@ +getEntryText($authType, $request); + + $data = []; + foreach ($response as $entry) { + $data[] = $entry['Text'] . " (ID: " . $entry['ID'] . ")"; + } + + echo json_encode($data); + +} catch (Exception $e) { + echo json_encode([]); +} +?> diff --git a/ajax/UpdateTerm.php b/ajax/UpdateTerm.php new file mode 100644 index 0000000..f701678 --- /dev/null +++ b/ajax/UpdateTerm.php @@ -0,0 +1,84 @@ + false, 'error' => 'Datenbankverbindung fehlgeschlagen']); + exit(); +} + +mysqli_set_charset($conn, "utf8"); + +// Parameter aus JavaScript +$id = isset($_POST['AnchorID']) ? intval($_POST['AnchorID']) : 0; +if ($id === 0) { + $id = isset($_POST['ID']) ? intval($_POST['ID']) : 0; +} +$text = isset($_POST['term']) ? trim($_POST['term']) : ''; +$detailType = isset($_POST['detailtype']) ? trim($_POST['detailtype']) : ''; +$classification = isset($_POST['classification']) ? trim($_POST['classification']) : ''; +$comment = isset($_POST['scopenote']) ? trim($_POST['scopenote']) : ''; + +if ($id === 0) { + echo json_encode(['success' => false, 'error' => 'ID muss angegeben werden']); + exit(); +} + +if (empty($text)) { + echo json_encode(['success' => false, 'error' => 'Text darf nicht leer sein']); + exit(); +} + +mysqli_begin_transaction($conn); + +try { + // 1. Anchor aktualisieren (Text, DetailType, Classification) + $updateAnchorSql = "UPDATE Anchor SET + Text = '" . mysqli_real_escape_string($conn, $text) . "', + DetailType = '" . mysqli_real_escape_string($conn, $detailType) . "', + Classification = '" . mysqli_real_escape_string($conn, $classification) . "' + WHERE ID = $id"; + + if (!mysqli_query($conn, $updateAnchorSql)) { + throw new Exception('Fehler beim Aktualisieren des Anchor: ' . mysqli_error($conn)); + } + + // 2. Entry aktualisieren + $updateEntrySql = "UPDATE Entry SET + Text = '" . mysqli_real_escape_string($conn, $text) . "', + CompleteText = '" . mysqli_real_escape_string($conn, $text) . "', + Comment = '" . mysqli_real_escape_string($conn, $comment) . "' + WHERE ID = $id"; + + if (!mysqli_query($conn, $updateEntrySql)) { + throw new Exception('Fehler beim Aktualisieren des Entry: ' . mysqli_error($conn)); + } + + mysqli_commit($conn); + + echo json_encode([ + 'success' => true, + 'message' => 'Eintrag erfolgreich aktualisiert' + ]); + +} catch (Exception $e) { + mysqli_rollback($conn); + echo json_encode(['success' => false, 'error' => $e->getMessage()]); +} + +mysqli_close($conn); +?> diff --git a/ajax/db_connection.php.example b/ajax/db_connection.php.example new file mode 100644 index 0000000..09332fd --- /dev/null +++ b/ajax/db_connection.php.example @@ -0,0 +1,27 @@ + PDO::ERRMODE_EXCEPTION, + PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC, + PDO::ATTR_EMULATE_PREPARES => FALSE, + ); + $dsn = 'mysql:host=' . HOST . ';dbname=' . DATABASE . ';charset=' . CHARSET; + $instance = new PDO($dsn, USER, PASSWORD, $opt); + } + return $instance; +} +?> diff --git a/ajax/deleteRelation.php b/ajax/deleteRelation.php new file mode 100644 index 0000000..c9cf1a4 --- /dev/null +++ b/ajax/deleteRelation.php @@ -0,0 +1,86 @@ + false, 'error' => 'Datenbankverbindung fehlgeschlagen']); + exit(); +} + +mysqli_set_charset($conn, "utf8"); + +$idAnchor = isset($_POST['IDAnchor']) ? intval($_POST['IDAnchor']) : 0; +$linkingId = isset($_POST['LinkingID']) ? intval($_POST['LinkingID']) : 0; + +if ($linkingId === 0) { + echo json_encode(['success' => false, 'error' => 'LinkingID muss angegeben werden']); + exit(); +} + +// Zuerst Relation lesen für reziproke Löschung +$getSql = "SELECT IDAnchor, IDEntry, Relationtype FROM Linking WHERE ID = $linkingId"; +$getResult = mysqli_query($conn, $getSql); + +if (!$getResult || mysqli_num_rows($getResult) === 0) { + echo json_encode(['success' => false, 'error' => 'Relation nicht gefunden']); + mysqli_close($conn); + exit(); +} + +$relation = mysqli_fetch_assoc($getResult); +$targetId = $relation['IDEntry']; +$relationType = $relation['Relationtype']; + +// Reziproke Relation bestimmen +$reciprocalType = null; +switch ($relationType) { + case 'BT': + $reciprocalType = 'NT'; + break; + case 'NT': + $reciprocalType = 'BT'; + break; + case 'RT': + $reciprocalType = 'RT'; + break; + case 'USE': + $reciprocalType = 'UF'; + break; + case 'UF': + $reciprocalType = 'USE'; + break; +} + +// Relation löschen +$deleteSql = "DELETE FROM Linking WHERE ID = $linkingId"; + +if (mysqli_query($conn, $deleteSql)) { + $affectedRows = mysqli_affected_rows($conn); + + // Reziproke Relation ebenfalls löschen + if ($reciprocalType) { + $deleteRecSql = "DELETE FROM Linking WHERE IDAnchor = $targetId AND IDEntry = " . $relation['IDAnchor'] . " AND Relationtype = '$reciprocalType'"; + mysqli_query($conn, $deleteRecSql); + } + + echo json_encode([ + 'success' => true, + 'message' => 'Relation erfolgreich gelöscht', + 'affected' => $affectedRows + ]); +} else { + echo json_encode(['success' => false, 'error' => 'Fehler beim Löschen: ' . mysqli_error($conn)]); +} + +mysqli_close($conn); +?> diff --git a/ajax/deleteTerm.php b/ajax/deleteTerm.php new file mode 100644 index 0000000..7061a5c --- /dev/null +++ b/ajax/deleteTerm.php @@ -0,0 +1,59 @@ + false, 'error' => 'Datenbankverbindung fehlgeschlagen']); + exit(); +} +mysqli_set_charset($conn, "utf8"); + +// Parameter: AnchorID (von JS) oder ID (Fallback) +$id = 0; +if (isset($_POST['AnchorID']) && intval($_POST['AnchorID']) > 0) { + $id = intval($_POST['AnchorID']); +} elseif (isset($_POST['ID']) && intval($_POST['ID']) > 0) { + $id = intval($_POST['ID']); +} + +if ($id === 0) { + echo json_encode(['success' => false, 'error' => 'Gültige ID muss angegeben werden']); + exit(); +} + +// Transaktion starten +mysqli_begin_transaction($conn); +try { + // 1. Alle Relationen löschen (als Anchor und als Entry) + mysqli_query($conn, "DELETE FROM Linking WHERE IDAnchor = $id"); + mysqli_query($conn, "DELETE FROM Linking WHERE IDEntry = $id"); + + // 2. Entry löschen + mysqli_query($conn, "DELETE FROM Entry WHERE ID = $id"); + + // 3. Anchor löschen + mysqli_query($conn, "DELETE FROM Anchor WHERE ID = $id"); + + mysqli_commit($conn); + + echo json_encode([ + 'success' => true, + 'message' => 'Eintrag erfolgreich gelöscht' + ]); + +} catch (Exception $e) { + mysqli_rollback($conn); + echo json_encode(['success' => false, 'error' => 'Fehler beim Löschen: ' . $e->getMessage()]); +} + +mysqli_close($conn); +?> diff --git a/ajax/getAuthorityData.php b/ajax/getAuthorityData.php new file mode 100644 index 0000000..67516d9 --- /dev/null +++ b/ajax/getAuthorityData.php @@ -0,0 +1,167 @@ + 0, 'rows' => [], 'error' => 'Datenbankverbindung fehlgeschlagen']); + exit(); +} + +mysqli_set_charset($conn, "utf8"); + +// Parameter auslesen +$limit = isset($_GET['limit']) ? $_GET['limit'] : 25; +$offset = isset($_GET['offset']) ? intval($_GET['offset']) : 0; + +// "all" oder 0 bedeutet: alle Treffer ohne Limit +if ($limit === 'all' || $limit === '0' || intval($limit) <= 0) { + $limitAll = true; + $limit = 0; +} else { + $limitAll = false; + $limit = intval($limit); +} +$search = isset($_GET['search']) ? trim($_GET['search']) : ''; +$authType = isset($_GET['authType']) ? trim($_GET['authType']) : 'Subject'; + +// Erlaubte Typen +$allowedTypes = ['Subject', 'Person', 'Corporate', 'Publisher', 'Classification']; +if (!in_array($authType, $allowedTypes)) { + $authType = 'Subject'; +} + +// Basis-SQL +$baseSql = "FROM Anchor a + LEFT JOIN Entry e ON a.ID = e.ID + WHERE a.Type = '" . mysqli_real_escape_string($conn, $authType) . "'"; + +// Suchbedingung hinzufügen (case-insensitive) +// Scope Note (e.Comment) wird bewusst nicht durchsucht +if (!empty($search)) { + // Trunkierung auswerten + $leftTrunc = (substr($search, 0, 1) === '*'); + $rightTrunc = (substr($search, -1) === '*'); + + // Sternchen entfernen, dann escapen + $clean = trim($search, '*'); + $searchEscaped = mysqli_real_escape_string($conn, $clean); + // LIKE-Sonderzeichen in den Daten escapen + $searchEscaped = str_replace(['%', '_'], ['\\%', '\\_'], $searchEscaped); + + // Pattern bauen + $pattern = ($leftTrunc ? '%' : '') . + $searchEscaped . + ($rightTrunc ? '%' : '%'); // rechts immer trunkieren = bisheriges Verhalten + + $baseSql .= " AND (LOWER(e.Text) LIKE LOWER('{$pattern}') + OR LOWER(e.CompleteText) LIKE LOWER('{$pattern}'))"; +} + +// Gesamtanzahl ermitteln +$countSql = "SELECT COUNT(*) as total " . $baseSql; +$countResult = mysqli_query($conn, $countSql); +$total = 0; +if ($countResult) { + $countRow = mysqli_fetch_assoc($countResult); + $total = intval($countRow['total']); +} + +// ORDER BY je nach authType +// Classification: numerisch nach Notation (z.B. T 4.10 nach T 4.9) +// Alle anderen: deutsch-korrekt nach Unicode-Kollation (Umlaute, Groß/Klein, Sonderzeichen) +if ($authType === 'Classification') { + // Dreistufige Sortierung: + // 1. Buchstabenpräfix alphabetisch (G, S, T ...) + // 2. Erste Zahl numerisch (Hauptgruppe vor Untergruppe: NULL kommt zuerst) + // 3. Zweite Zahl numerisch (T 4.9 vor T 4.10) + $orderClause = " + SUBSTRING_INDEX(a.Classification, ' ', 1), + CAST( + NULLIF(SUBSTRING_INDEX(SUBSTRING_INDEX(a.Classification, ' ', -1), '.', 1), + a.Classification) + AS UNSIGNED + ), + CAST( + NULLIF(SUBSTRING_INDEX(a.Classification, '.', -1), + a.Classification) + AS UNSIGNED + ) + "; +} else { + // utf8_unicode_ci: Umlaute korrekt (Ä=A, Ö=O, Ü=U), + // Sonderzeichen nach Grundbuchstabe, Groß-/Klein ignoriert + $orderClause = "e.Text COLLATE utf8_unicode_ci ASC"; +} + +// Daten abrufen +$dataSql = "SELECT a.ID, a.Type, a.DetailType, a.Classification, + e.Text, e.CompleteText, e.Comment, + e.DateCreated, e.DateModified " . $baseSql . " + ORDER BY " . $orderClause; + +if (!$limitAll) { + $dataSql .= " LIMIT $offset, $limit"; +} + +$dataResult = mysqli_query($conn, $dataSql); + +$rows = []; +if ($dataResult) { + while ($row = mysqli_fetch_assoc($dataResult)) { + // Relationen zählen + $relSql = "SELECT COUNT(*) as cnt FROM Linking WHERE IDAnchor = " . intval($row['ID']); + $relResult = mysqli_query($conn, $relSql); + $relCount = 0; + if ($relResult) { + $relRow = mysqli_fetch_assoc($relResult); + $relCount = intval($relRow['cnt']); + } + + // Deskriptor-Status ermitteln (USE-Relation = Non-Deskriptor) + $useSql = "SELECT COUNT(*) as cnt FROM Linking WHERE IDAnchor = " . intval($row['ID']) . " AND UPPER(Relationtype) = 'USE'"; + $useResult = mysqli_query($conn, $useSql); + $isDescriptor = true; + if ($useResult) { + $useRow = mysqli_fetch_assoc($useResult); + if (intval($useRow['cnt']) > 0) { + $isDescriptor = false; + } + } + + $rows[] = [ + 'ID' => $row['ID'], + 'Type' => $row['Type'], + 'DetailType' => $row['DetailType'] ?? '', + 'Classification' => $row['Classification'] ?? '', + 'Text' => $row['Text'] ?? '', + 'CompleteText' => $row['CompleteText'] ?? '', + 'Comment' => $row['Comment'] ?? '', + 'DateCreated' => $row['DateCreated'] ?? '', + 'DateModified' => $row['DateModified'] ?? '', + 'RelationCount' => $relCount, + 'Descriptor' => $isDescriptor + ]; + } +} + +mysqli_close($conn); + +echo json_encode([ + 'total' => $total, + 'rows' => $rows +], JSON_UNESCAPED_UNICODE); +?> diff --git a/ajax/getAuthorityDataRaw.php b/ajax/getAuthorityDataRaw.php new file mode 100644 index 0000000..90b7f7f --- /dev/null +++ b/ajax/getAuthorityDataRaw.php @@ -0,0 +1,111 @@ + []]); + exit(); +} + +mysqli_set_charset($conn, "utf8"); + +// Prüfen ob ID-Abfrage (für ModifyTerm) +$id = isset($_GET['id']) ? intval($_GET['id']) : 0; + +if ($id > 0) { + // Einzelnen Eintrag per ID laden + $sql = "SELECT a.ID, a.Type, a.DetailType, a.Classification, + e.Text, e.CompleteText, e.Comment as Scopenote + FROM Anchor a + LEFT JOIN Entry e ON a.ID = e.ID + WHERE a.ID = $id"; + + $result = mysqli_query($conn, $sql); + + if ($result && mysqli_num_rows($result) > 0) { + $row = mysqli_fetch_assoc($result); + + // Descriptor erstellen + $descriptor = strtolower($row['Text'] ?? ''); + $descriptor = str_replace(' ', '_', $descriptor); + + echo json_encode([ + 'rows' => [[ + 'ID' => $row['ID'], + 'Type' => $row['Type'], + 'DetailType' => $row['DetailType'] ?? '', + 'Classification' => $row['Classification'] ?? '', + 'Text' => $row['Text'] ?? '', + 'CompleteText' => $row['CompleteText'] ?? '', + 'Scopenote' => $row['Scopenote'] ?? '', + 'Descriptor' => $descriptor + ]] + ], JSON_UNESCAPED_UNICODE); + } else { + echo json_encode(['rows' => []]); + } + + mysqli_close($conn); + exit(); +} + +// Sonst: Autocomplete-Suche +$search = isset($_GET['search']) ? trim($_GET['search']) : ''; +$authType = isset($_GET['authType']) ? trim($_GET['authType']) : ''; +$limit = isset($_GET['limit']) ? intval($_GET['limit']) : 10; + +if (strlen($search) < 2) { + echo json_encode([]); + exit(); +} + +// Erlaubte Typen +$allowedTypes = ['Subject', 'Person', 'Corporate', 'Publisher', 'Classification']; + +// SQL aufbauen +$searchEscaped = mysqli_real_escape_string($conn, $search); + +$sql = "SELECT a.ID, a.Type, a.DetailType, e.Text, e.CompleteText + FROM Anchor a + LEFT JOIN Entry e ON a.ID = e.ID + WHERE (e.Text LIKE '%{$searchEscaped}%' OR e.CompleteText LIKE '%{$searchEscaped}%')"; + +// Typ-Filter falls angegeben +if (!empty($authType) && in_array($authType, $allowedTypes)) { + $sql .= " AND a.Type = '" . mysqli_real_escape_string($conn, $authType) . "'"; +} + +$sql .= " ORDER BY e.Text ASC LIMIT $limit"; + +$result = mysqli_query($conn, $sql); + +$data = []; +if ($result) { + while ($row = mysqli_fetch_assoc($result)) { + $data[] = [ + 'id' => $row['ID'], + 'value' => $row['Text'], + 'text' => $row['Text'], + 'type' => $row['Type'], + 'detailType' => $row['DetailType'] ?? '', + 'completeText' => $row['CompleteText'] ?? '' + ]; + } +} + +mysqli_close($conn); + +echo json_encode($data, JSON_UNESCAPED_UNICODE); +?> diff --git a/ajax/getClassificationData.php b/ajax/getClassificationData.php new file mode 100644 index 0000000..4eb7478 --- /dev/null +++ b/ajax/getClassificationData.php @@ -0,0 +1,8 @@ + diff --git a/ajax/getCorporateData.php b/ajax/getCorporateData.php new file mode 100644 index 0000000..da54733 --- /dev/null +++ b/ajax/getCorporateData.php @@ -0,0 +1,8 @@ + diff --git a/ajax/getDSpaceCount.php b/ajax/getDSpaceCount.php new file mode 100644 index 0000000..f5eef0d --- /dev/null +++ b/ajax/getDSpaceCount.php @@ -0,0 +1,65 @@ + 'Invalid authType', 'count' => 0)); + exit; +} + +if (strlen($query) === 0) { + echo json_encode(array('error' => 'Missing query', 'count' => 0)); + exit; +} + +// DSpace API aufrufen +$url = $DSPACE_API_URL . '?authType=' . urlencode($authType) . '&query=' . urlencode($query); + +$context = stream_context_create(array( + 'http' => array( + 'timeout' => 15, + 'header' => "Accept: application/json\r\n" + ), + 'ssl' => array( + 'verify_peer' => false, + 'verify_peer_name' => false + ) +)); + +$response = @file_get_contents($url, false, $context); + +if ($response === false) { + // Fallback: Count 0 zurückgeben, nicht die ganze Anzeige blockieren + echo json_encode(array( + 'query' => $query, + 'authType' => $authType, + 'count' => -1, + 'error' => 'DSpace API not reachable' + )); + exit; +} + +// Response direkt durchreichen +echo $response; +?> diff --git a/ajax/getDashboardStats.php b/ajax/getDashboardStats.php new file mode 100644 index 0000000..7aa5dfa --- /dev/null +++ b/ajax/getDashboardStats.php @@ -0,0 +1,125 @@ + 'Datenbankverbindung fehlgeschlagen']); + exit(); +} + +mysqli_set_charset($conn, "utf8"); + +$stats = []; + +// 1. Anzahl Datensätze pro Typ +$types = ['Subject', 'Person', 'Corporate', 'Publisher', 'Classification']; +$counts = []; + +foreach ($types as $type) { + $sql = "SELECT COUNT(*) as cnt FROM Anchor WHERE Type = '$type'"; + $res = mysqli_query($conn, $sql); + if ($res) { + $row = mysqli_fetch_assoc($res); + $counts[$type] = intval($row['cnt']); + } else { + $counts[$type] = 0; + } +} + +$stats['counts'] = $counts; +$stats['total'] = array_sum($counts); + +// 2. Anzahl Relationen +$sql = "SELECT COUNT(*) as cnt FROM Linking"; +$res = mysqli_query($conn, $sql); +if ($res) { + $row = mysqli_fetch_assoc($res); + $stats['relationsTotal'] = intval($row['cnt']); +} else { + $stats['relationsTotal'] = 0; +} + +// 3. Zuletzt geänderte Einträge (Top 10) +$sql = "SELECT a.ID, a.Text, a.Type, e.DateModified + FROM Anchor a + JOIN Entry e ON a.Text = e.Text + WHERE e.DateModified > '2000-01-01' + ORDER BY e.DateModified DESC + LIMIT 10"; +$res = mysqli_query($conn, $sql); +$recentlyModified = []; +if ($res) { + while ($row = mysqli_fetch_assoc($res)) { + $recentlyModified[] = [ + 'id' => $row['ID'], + 'text' => $row['Text'], + 'type' => $row['Type'], + 'date' => $row['DateModified'] + ]; + } +} +$stats['recentlyModified'] = $recentlyModified; + +// 4. Zuletzt hinzugefügte Einträge (Top 10) +$sql = "SELECT a.ID, a.Text, a.Type, e.DateCreated + FROM Anchor a + JOIN Entry e ON a.Text = e.Text + WHERE e.DateCreated > '2000-01-01' + ORDER BY e.DateCreated DESC + LIMIT 10"; +$res = mysqli_query($conn, $sql); +$recentlyCreated = []; +if ($res) { + while ($row = mysqli_fetch_assoc($res)) { + $recentlyCreated[] = [ + 'id' => $row['ID'], + 'text' => $row['Text'], + 'type' => $row['Type'], + 'date' => $row['DateCreated'] + ]; + } +} +$stats['recentlyCreated'] = $recentlyCreated; + +// 5. Qualitäts-Check: Einträge ohne Relationen (Waisen) +$orphanCounts = []; +foreach ($types as $type) { + $sql = "SELECT COUNT(*) as cnt FROM Anchor a + WHERE a.Type = '$type' + AND a.ID NOT IN (SELECT DISTINCT IDAnchor FROM Linking)"; + $res = mysqli_query($conn, $sql); + if ($res) { + $row = mysqli_fetch_assoc($res); + $orphanCounts[$type] = intval($row['cnt']); + } else { + $orphanCounts[$type] = 0; + } +} +$stats['orphans'] = $orphanCounts; +$stats['orphansTotal'] = array_sum($orphanCounts); + +// 6. Deskriptoren vs. Non-Deskriptoren (für Schlagworte) +$sql = "SELECT + SUM(CASE WHEN DetailType = 'Deskriptor' OR DetailType = 'deskriptor' THEN 1 ELSE 0 END) as descriptors, + SUM(CASE WHEN DetailType != 'Deskriptor' AND DetailType != 'deskriptor' THEN 1 ELSE 0 END) as nonDescriptors + FROM Anchor WHERE Type = 'Subject'"; +$res = mysqli_query($conn, $sql); +if ($res) { + $row = mysqli_fetch_assoc($res); + $stats['descriptors'] = intval($row['descriptors']); + $stats['nonDescriptors'] = intval($row['nonDescriptors']); +} else { + $stats['descriptors'] = 0; + $stats['nonDescriptors'] = 0; +} + +mysqli_close($conn); + +echo json_encode($stats, JSON_UNESCAPED_UNICODE); +?> diff --git a/ajax/getDetailAuthorityData.php b/ajax/getDetailAuthorityData.php new file mode 100644 index 0000000..106b641 --- /dev/null +++ b/ajax/getDetailAuthorityData.php @@ -0,0 +1,122 @@ + []]); + exit(); +} + +mysqli_set_charset($conn, "utf8"); + +// Beide Parameter-Namen unterstützen (JS sendet 'id') +$id = isset($_GET['id']) ? intval($_GET['id']) : 0; +if ($id === 0) { + $id = isset($_GET['ID']) ? intval($_GET['ID']) : 0; +} + +if ($id === 0) { + echo json_encode(['rows' => []]); + exit(); +} + +// Hauptdaten laden +$sql = "SELECT a.ID, a.Type, a.DetailType, a.Classification, a.Text as AnchorText, + e.Text, e.CompleteText, e.Comment, e.DateCreated, e.DateModified + FROM Anchor a + LEFT JOIN Entry e ON a.ID = e.ID + WHERE a.ID = $id"; + +$result = mysqli_query($conn, $sql); + +if (!$result || mysqli_num_rows($result) === 0) { + echo json_encode(['rows' => []]); + mysqli_close($conn); + exit(); +} + +$data = mysqli_fetch_assoc($result); + +// Relationen laden im Format das JS erwartet +$relSql = "SELECT l.ID as LinkingID, l.IDAnchor, l.IDEntry, l.Relationtype, + e.Text as EntryText, a.Type as EntryType + FROM Linking l + LEFT JOIN Entry e ON l.IDEntry = e.ID + LEFT JOIN Anchor a ON l.IDEntry = a.ID + WHERE l.IDAnchor = $id + ORDER BY l.Relationtype, e.Text"; + +$relResult = mysqli_query($conn, $relSql); + +$relations = []; +if ($relResult) { + while ($relRow = mysqli_fetch_assoc($relResult)) { + $relations[] = [ + 'Relationtype' => $relRow['Relationtype'], + 'TextRelation' => $relRow['EntryText'] ?? '', + 'IDRelation' => $relRow['IDEntry'] + ]; + } +} + +// Synonyme laden (nur für Subject) +$synonymsText = ''; +if ($data['Type'] === 'Subject' && !empty($data['Text'])) { + $desc = strtolower($data['Text']); + $desc = str_replace(' ', '_', $desc); + + $synSql = "SELECT IDLinking FROM Synonyms WHERE Descriptor = '" . mysqli_real_escape_string($conn, $desc) . "' LIMIT 1"; + $synResult = mysqli_query($conn, $synSql); + + if ($synResult && mysqli_num_rows($synResult) > 0) { + $synRow = mysqli_fetch_assoc($synResult); + $idLinking = $synRow['IDLinking']; + + $synAllSql = "SELECT Text FROM Synonyms WHERE IDLinking = $idLinking AND Descriptor != '" . mysqli_real_escape_string($conn, $desc) . "' ORDER BY Text"; + $synAllResult = mysqli_query($conn, $synAllSql); + + $synTexts = []; + if ($synAllResult) { + while ($syn = mysqli_fetch_assoc($synAllResult)) { + $synTexts[] = $syn['Text']; + } + } + $synonymsText = implode(', ', $synTexts); + } +} + +// Descriptor erstellen +$descriptor = strtolower($data['Text'] ?? ''); +$descriptor = str_replace(' ', '_', $descriptor); + +mysqli_close($conn); + +// Rückgabe im Format das JavaScript erwartet +echo json_encode([ + 'rows' => [[ + 'ID' => $data['ID'], + 'Type' => $data['Type'], + 'Detailtype' => $data['DetailType'] ?? '', + 'Classification' => $data['Classification'] ?? '', + 'Text' => $data['Text'] ?? '', + 'CompleteText' => $data['CompleteText'] ?? '', + 'Scopenote' => $data['Comment'] ?? '', + 'Descriptor' => $descriptor, + 'Synonyms' => $synonymsText, + 'Relations' => $relations, + 'DateCreated' => $data['DateCreated'] ?? '', + 'DateModified' => $data['DateModified'] ?? '' + ]] +], JSON_UNESCAPED_UNICODE); +?> diff --git a/ajax/getPersonData.php b/ajax/getPersonData.php new file mode 100644 index 0000000..eef28de --- /dev/null +++ b/ajax/getPersonData.php @@ -0,0 +1,8 @@ + diff --git a/ajax/getPublisherData.php b/ajax/getPublisherData.php new file mode 100644 index 0000000..7ea7408 --- /dev/null +++ b/ajax/getPublisherData.php @@ -0,0 +1,8 @@ + diff --git a/ajax/getRelations.php b/ajax/getRelations.php new file mode 100644 index 0000000..96d57bc --- /dev/null +++ b/ajax/getRelations.php @@ -0,0 +1,104 @@ +'; + $html .= '' . $relType . ''; + $html .= '' . $entryText . ''; + $html .= ''; + $html .= ''; + $html .= ''; + $html .= ''; +} + +mysqli_close($conn); + +echo $html; +?> diff --git a/ajax/getSubjectData.php b/ajax/getSubjectData.php new file mode 100644 index 0000000..90cf8b2 --- /dev/null +++ b/ajax/getSubjectData.php @@ -0,0 +1,8 @@ + diff --git a/ajax/getSynonyms.php b/ajax/getSynonyms.php new file mode 100644 index 0000000..81138a6 --- /dev/null +++ b/ajax/getSynonyms.php @@ -0,0 +1,82 @@ + false, 'error' => 'Datenbankverbindung fehlgeschlagen']); + exit(); +} + +mysqli_set_charset($conn, "utf8"); + +$text = isset($_GET['text']) ? trim($_GET['text']) : ''; + +if (empty($text)) { + echo json_encode(['success' => true, 'synonyms' => [], 'html' => '']); + exit(); +} + +// Descriptor erstellen (Kleinbuchstaben, Leerzeichen durch Unterstrich) +function prepare_desc($text) { + $desc = strtolower($text); + $desc = str_replace(' ', '_', $desc); + return $desc; +} + +$desc = prepare_desc($text); + +// Erst IDLinking für den Begriff finden +$sql = "SELECT IDLinking FROM Synonyms WHERE Descriptor = '" . mysqli_real_escape_string($conn, $desc) . "' LIMIT 1"; +$res = mysqli_query($conn, $sql); + +$synonyms = []; +$html = ''; + +if ($res && mysqli_num_rows($res) > 0) { + $row = mysqli_fetch_assoc($res); + $idLinking = $row['IDLinking']; + + // Alle Synonyme mit dieser IDLinking laden (außer dem Begriff selbst) + $sql2 = "SELECT ID, Text, Descriptor FROM Synonyms WHERE IDLinking = $idLinking AND Descriptor != '" . mysqli_real_escape_string($conn, $desc) . "' ORDER BY Text"; + $res2 = mysqli_query($conn, $sql2); + + if ($res2) { + while ($syn = mysqli_fetch_assoc($res2)) { + $synonyms[] = [ + 'id' => $syn['ID'], + 'text' => $syn['Text'], + 'descriptor' => $syn['Descriptor'] + ]; + + // HTML für Anzeige generieren + $html .= ''; + $html .= 'SYN'; + $html .= '' . htmlspecialchars($syn['Text']) . ''; + $html .= ''; + $html .= ''; + $html .= ''; + $html .= ''; + } + } +} + +echo json_encode([ + 'success' => true, + 'synonyms' => $synonyms, + 'html' => $html, + 'count' => count($synonyms) +]); + +mysqli_close($conn); +?> diff --git a/ajax/getTreeData.php b/ajax/getTreeData.php new file mode 100644 index 0000000..e43ede4 --- /dev/null +++ b/ajax/getTreeData.php @@ -0,0 +1,75 @@ + 0); + + $result[] = [ + 'id' => strval($row['id']), + 'text' => $row['text'] ? $row['text'] : $row['name'], + 'children' => $hasChildren, + 'data' => [ + 'name' => $row['name'], + 'href' => $row['href'] + ], + 'icon' => $hasChildren ? 'fas fa-folder text-warning' : 'fas fa-file text-secondary' + ]; +} + +mysqli_close($conn); + +echo json_encode($result, JSON_UNESCAPED_UNICODE); +?> diff --git a/ajax/getTreePath.php b/ajax/getTreePath.php new file mode 100644 index 0000000..d347bfc --- /dev/null +++ b/ajax/getTreePath.php @@ -0,0 +1,46 @@ + 0 && $limit-- > 0) { + $res = mysqli_query($conn, "SELECT id, parent_id FROM Treeview WHERE id = $curId LIMIT 1"); + if (!$res) break; + $row = mysqli_fetch_assoc($res); + if (!$row) break; + + array_unshift($path, intval($row['id'])); // vorne einfügen → Root zuerst + $parentId = intval($row['parent_id']); + if ($parentId === 0) break; + $curId = $parentId; +} + +mysqli_close($conn); +echo json_encode($path, JSON_UNESCAPED_UNICODE); +?> diff --git a/ajax/libAuthorities.php b/ajax/libAuthorities.php new file mode 100644 index 0000000..b93011a --- /dev/null +++ b/ajax/libAuthorities.php @@ -0,0 +1,561 @@ +db = DB(); + } + + function __destruct() + { + $this->db = null; + } + + /** + * Validiert den authType gegen Whitelist + */ + private function validateAuthType($authType) + { + if (!in_array($authType, $this->allowedTypes)) { + throw new InvalidArgumentException("Ungültiger Typ: $authType"); + } + return $authType; + } + + /** + * Validiert und bereinigt Sortierfeld + */ + private function validateSortField($sort) + { + if (empty($sort)) { + return 'Entry.Text'; + } + + $sortField = trim(str_replace(['ASC', 'DESC', 'asc', 'desc'], '', $sort)); + $sortField = trim($sortField); + + if (!in_array($sortField, $this->allowedSortFields)) { + return 'Entry.Text'; + } + + return $sort; + } + + /** + * Validiert Integer-Werte + */ + private function validateInt($value, $min = 0) + { + $intVal = intval($value); + return ($intVal >= $min) ? $intVal : $min; + } + + /** + * Baut ein SQL-LIKE-Pattern aus einem Suchterm. + * Führendes * → Linkstrunkierung (%...) + * Abschließendes * → Rechtstrunkierung (...%) + * Kein * → nur Rechtstrunkierung (Standard-Verhalten) + */ + private function buildSearchPattern(string $term): string + { + $leftTrunc = str_starts_with($term, '*'); + $rightTrunc = str_ends_with($term, '*'); + + $clean = trim($term, '*'); + $clean = addcslashes($clean, '%_\\'); + + return ($leftTrunc ? '%' : '') . + $clean . + ($rightTrunc ? '%' : '%'); + } + + /** + * Dubletten-Prüfung über Anchor.Text (normalisierte Form). + * + * Anchor.Text enthält den normalisierten Descriptor (Kleinbuchstaben, + * Sonderzeichen durch '_' ersetzt, erzeugt durch prepare_desc()). + * Diese Prüfung ist robuster als ein Vergleich auf Entry.Text, weil + * sie Schreibvarianten (Groß-/Kleinschreibung, Umlaute, Leerzeichen) + * zuverlässig als Dublette erkennt. + * + * @param string $authType Typ-Whitelist-Wert ('Subject', 'Person', ...) + * @param string $normalizedText Ergebnis von prepare_desc($term) + * @return int Anzahl gefundener Einträge (0 = keine Dublette) + */ + public function checkDuplicateByNormalizedText($authType, $normalizedText) + { + $authType = $this->validateAuthType($authType); + + $stmt = $this->db->prepare( + "SELECT COUNT(*) AS count + FROM Anchor + WHERE Text = :normalizedText + AND Type = :authType" + ); + $stmt->execute([ + ':normalizedText' => $normalizedText, + ':authType' => $authType + ]); + + $rec = $stmt->fetch(PDO::FETCH_ASSOC); + return $rec ? intval($rec['count']) : 0; + } + + public function readAuthorityNumbersOfRecords($authType, $search, $id, $sharp) + { + error_log("DEBUG search='" . $search . "' pattern='" . $this->buildSearchPattern($search) . "'"); + + $authType = $this->validateAuthType($authType); + $id = $this->validateInt($id); + + $params = [':authType' => $authType]; + + $query = "SELECT COUNT(*) as count FROM Anchor + JOIN Entry ON (Anchor.ID = Entry.ID) + WHERE Type = :authType"; + + if (strlen($search) > 0 && $sharp == false) { + $query .= " AND LOWER(Entry.CompleteText) LIKE LOWER(:search)"; + $params[':search'] = $this->buildSearchPattern($search); + } + if (strlen($search) > 0 && $sharp == true) { + $query .= " AND LOWER(Entry.Text) = LOWER(:search)"; + $params[':search'] = $search; + } + if ($id > 0) { + $query .= " AND Entry.ID = :id"; + $params[':id'] = $id; + } + + $stmt = $this->db->prepare($query); + $stmt->execute($params); + + $rec = $stmt->fetch(PDO::FETCH_ASSOC); + return $rec ? intval($rec['count']) : 0; + } + + public function readAuthorityEntriesByID($authType, $id) + { + $authType = $this->validateAuthType($authType); + $id = $this->validateInt($id, 1); + + $query = "SELECT Anchor.ID AS ID, Anchor.DetailType AS DetailType, + Anchor.Type AS Type, Anchor.Classification AS Classification, + Entry.Text as Text, Entry.Comment as Scopenote + FROM Anchor + JOIN Entry ON (Anchor.ID = Entry.ID) + WHERE Anchor.ID = :id AND Type = :authType"; + + $stmt = $this->db->prepare($query); + $stmt->execute([':id' => $id, ':authType' => $authType]); + + $count = $stmt->rowCount(); + $ret = []; + + while ($rec = $stmt->fetch(PDO::FETCH_ASSOC)) { + $relations = $this->getRelations($authType, $rec['ID']); + $ret[] = [ + "ID" => $rec['ID'], + "Descriptor" => $relations['Descriptor'], + "Text" => $rec['Text'], + "DetailType" => $rec['DetailType'], + "Type" => $rec['Type'], + "Scopenote" => $rec['Scopenote'], + "Relations" => $relations['Data'], + "Classification" => $rec['Classification'] + ]; + } + + return ["COUNT" => $count, "RECORDS" => $ret]; + } + + public function readAuthorityEntries($authType, $offset, $search, $id, $sort, $max) + { + error_log("DEBUG search='" . $search . "' pattern='" . $this->buildSearchPattern($search) . "'", 3, "error.log"); + + + $authType = $this->validateAuthType($authType); + $offset = $this->validateInt($offset); + $max = $this->validateInt($max, 1); + $id = $this->validateInt($id); + $sort = $this->validateSortField($sort); + + $params = [':authType' => $authType]; + + $query = "SELECT Anchor.ID AS ID, Anchor.DetailType AS DetailType, + Anchor.Type AS Type, Anchor.Classification AS Classification, + Entry.Text as Text, Entry.Comment as Scopenote + FROM Anchor + JOIN Entry ON (Anchor.ID = Entry.ID) + WHERE Type = :authType"; + + if (strlen($search) > 0) { + $query .= " AND LOWER(Entry.CompleteText) LIKE LOWER(:search)"; + $params[':search'] = $this->buildSearchPattern($search); + } + if ($id > 0) { + $query .= " AND Entry.ID = :id"; + $params[':id'] = $id; + } + + // $query .= " ORDER BY " . $sort . " LIMIT " . intval($offset) . "," . intval($max); + +if ($authType === 'Classification') { + $orderClause = " + SUBSTRING_INDEX(Anchor.Text, ' ', 1), + CAST( + CASE + WHEN LOCATE(' ', Anchor.Text) > 0 + THEN SUBSTRING_INDEX(SUBSTRING_INDEX(Anchor.Text, ' ', -1), '.', 1) + ELSE NULL + END AS UNSIGNED + ), + CAST( + CASE + WHEN LOCATE('.', Anchor.Text) > 0 + THEN SUBSTRING_INDEX(Anchor.Text, '.', -1) + ELSE NULL + END AS UNSIGNED + ) + "; +} else { + $orderClause = $sort . " COLLATE utf8mb4_unicode_ci"; +} + +error_log("DEBUG ORDER BY: " . $orderClause, 3, "/var/www/tools/html/Thesaurus/error.log"); + +$query .= " ORDER BY " . $orderClause . " LIMIT " . intval($offset) . "," . intval($max); + + + $stmt = $this->db->prepare($query); + $stmt->execute($params); + + $count = $stmt->rowCount(); + $ret = []; + + while ($rec = $stmt->fetch(PDO::FETCH_ASSOC)) { + $relations = $this->getRelations($authType, $rec['ID']); + + if ($authType === 'Subject') { + $synonyms = $this->getSynonyms($rec['Text']); + $ret[] = [ + "ID" => $rec['ID'], + "Descriptor" => $relations['Descriptor'], + "Text" => $rec['Text'], + "DetailType" => $rec['DetailType'], + "Type" => $rec['Type'], + "Scopenote" => $rec['Scopenote'], + "Relations" => $relations['Data'], + "Classification" => $rec['Classification'], + "Synonyms" => $synonyms['Synonyms'], + "Search" => $synonyms['Search'] + ]; + } else { + $ret[] = [ + "ID" => $rec['ID'], + "Descriptor" => $relations['Descriptor'], + "Text" => $rec['Text'], + "DetailType" => $rec['DetailType'], + "Type" => $rec['Type'], + "Scopenote" => $rec['Scopenote'], + "Relations" => $relations['Data'], + "Classification" => $rec['Classification'] + ]; + } + } + + return ["COUNT" => $count, "RECORDS" => $ret]; + } + + public function getRelations($authType, $id) + { + $authType = $this->validateAuthType($authType); + $id = $this->validateInt($id, 1); + + $query = "SELECT Linking.ID as LinkingID, IDEntry, Entry.Text, Entry.Comment, + Relationtype, DetailType + FROM Linking + JOIN Entry ON (Linking.IDEntry = Entry.ID) + JOIN Anchor ON (IDAnchor = Anchor.ID) + WHERE IDAnchor = :id AND Anchor.Type = :authType AND Relationtype != '' + ORDER BY Relationtype"; + + $stmt = $this->db->prepare($query); + $stmt->execute([':id' => $id, ':authType' => $authType]); + + $descriptor = true; + $ret = []; + + while ($rec = $stmt->fetch(PDO::FETCH_ASSOC)) { + if (strtolower($rec['Relationtype']) === "use") { + $descriptor = false; + } + $ret[] = [ + "IDLinking" => $rec['LinkingID'], + "IDEntry" => $rec['IDEntry'], + "IDRelation" => $rec['IDEntry'], + "Detailtype" => $rec['DetailType'], + "TextRelation" => $rec['Text'], + "CommentRelation"=> $rec['Comment'], + "Relationtype" => $rec['Relationtype'] + ]; + } + + return ["Descriptor" => $descriptor, "Data" => $ret]; + } + + public function getSynonyms($input) + { + $desc = $this->prepare_desc($input); + + $query = "SELECT IDLinking FROM Synonyms WHERE Descriptor = :desc"; + $stmt = $this->db->prepare($query); + $stmt->execute([':desc' => $desc]); + + $ret = ''; + $retsearch = "topic:'" . addslashes($input) . "' OR "; + + while ($rec = $stmt->fetch(PDO::FETCH_ASSOC)) { + $idSearch = intval($rec['IDLinking']); + + $query2 = "SELECT Text FROM Synonyms WHERE IDLinking = :idLinking"; + $stmt2 = $this->db->prepare($query2); + $stmt2->execute([':idLinking' => $idSearch]); + + while ($rec2 = $stmt2->fetch(PDO::FETCH_ASSOC)) { + if ($rec2['Text'] === $input) continue; + $ret .= $rec2['Text'] . ", "; + $retsearch .= "topic:'" . addslashes($rec2['Text']) . "' OR "; + } + } + + if (strlen($ret) == 0) { + return ["Synonyms" => '', "Search" => '']; + } else { + $synonyms = trim(substr($ret, 0, -2)); + $search = trim(substr($retsearch, 0, -4)); + return ["Synonyms" => $synonyms, "Search" => $search]; + } + } + + public function getEntryText($authType, $request) + { + $authType = $this->validateAuthType($authType); + + $query = "SELECT Anchor.ID AS AnchorID, Entry.Text as Text + FROM Anchor + JOIN Entry ON (Anchor.ID = Entry.ID) + WHERE Anchor.Text LIKE LOWER(:request) AND Anchor.Type = :authType + ORDER BY Entry.Text"; + + $stmt = $this->db->prepare($query); + $stmt->execute([':request' => '%' . $request . '%', ':authType' => $authType]); + + $ret = []; + while ($rec = $stmt->fetch(PDO::FETCH_ASSOC)) { + $ret[] = ["Text" => $rec['Text'], "ID" => $rec['AnchorID']]; + } + + return $ret; + } + + public function insertNewTerm($authType, $term, $desc, $detailtype, $classification, $scopenote, $completetext) + { + $authType = $this->validateAuthType($authType); + + $query = $this->db->prepare( + "INSERT INTO Anchor (ID, Text, DetailType, Type, Classification) + VALUES (:ID, :Text, :DetailType, :Type, :Classification)" + ); + + $query->execute([ + ':ID' => null, + ':Text' => $desc, + ':DetailType' => $detailtype, + ':Type' => $authType, + ':Classification' => $classification + ]); + + $IDAnchor = $this->db->lastInsertId(); + + $query = $this->db->prepare( + "INSERT INTO Entry (ID, Text, Comment, Language, CompleteText, DateCreated, DateModified) + VALUES (:ID, :Text, :Comment, :Language, :CompleteText, :DateCreated, :DateModified)" + ); + + $query->execute([ + ':ID' => $IDAnchor, + ':Text' => $term, + ':Comment' => $scopenote, + ':Language' => 'de', + ':CompleteText' => $completetext, + ':DateCreated' => date('Y-m-d H:i:s'), + ':DateModified' => '0000-00-00 00:00:00' + ]); + + $IDEntry = $IDAnchor; + + return ["IDAnchor" => $IDAnchor, "IDEntry" => $IDEntry, "IDLinking" => 0]; + } + + public function writeNewRelation($anchorID, $relationType, $relationID) + { + $anchorID = $this->validateInt($anchorID, 1); + $relationID = $this->validateInt($relationID, 1); + + $allowedRelations = ['BT', 'NT', 'RT', 'USE', 'UF']; + if (!in_array(strtoupper($relationType), $allowedRelations)) { + throw new InvalidArgumentException("Ungültiger Relationstyp: $relationType"); + } + + $query = $this->db->prepare( + "INSERT INTO Linking (ID, IDAnchor, IDEntry, Relationtype) + VALUES (:ID, :IDAnchor, :IDEntry, :Relationtype)" + ); + + $query->execute([ + ':ID' => null, + ':IDAnchor' => $anchorID, + ':IDEntry' => $relationID, + ':Relationtype' => $relationType + ]); + + return $this->db->lastInsertId(); + } + + public function deleteRelation($anchorID, $linkingID) + { + $linkingID = $this->validateInt($linkingID, 1); + + $query = $this->db->prepare("DELETE FROM Linking WHERE ID = :id"); + $query->execute([':id' => $linkingID]); + + return $linkingID; + } + + public function deleteTerm($anchorID) + { + $anchorID = $this->validateInt($anchorID, 1); + $ret = []; + + $query = $this->db->prepare("DELETE FROM Linking WHERE IDAnchor = :id"); + $query->execute([':id' => $anchorID]); + $ret[] = "Linking gelöscht"; + + $query = $this->db->prepare("DELETE FROM Entry WHERE ID = :id"); + $query->execute([':id' => $anchorID]); + $ret[] = "Entry gelöscht"; + + $query = $this->db->prepare("DELETE FROM Anchor WHERE ID = :id"); + $query->execute([':id' => $anchorID]); + $ret[] = "Anchor gelöscht"; + + return $ret; + } + + public function updateTerm($anchorID, $authType, $desc, $term, $type, $detailtype, $classification, $scopenote, $completetext) + { + $anchorID = $this->validateInt($anchorID, 1); + $authType = $this->validateAuthType($authType); + + $query = $this->db->prepare( + "UPDATE Anchor + SET Text = :text, Type = :type, DetailType = :detailtype, Classification = :classification + WHERE ID = :id" + ); + + $query->execute([ + ':text' => $desc, + ':type' => $authType, + ':detailtype' => $detailtype, + ':classification' => $classification, + ':id' => $anchorID + ]); + + $query = $this->db->prepare( + "UPDATE Entry + SET Text = :text, Comment = :comment, Language = :language, + CompleteText = :completetext, DateModified = :datemodified + WHERE ID = :id" + ); + + $query->execute([ + ':text' => $term, + ':comment' => $scopenote, + ':language' => 'de', + ':completetext' => $completetext, + ':datemodified' => date('Y-m-d H:i:s'), + ':id' => $anchorID + ]); + + return "OK"; + } + + public function getStatistics($dateStart, $dateEnd) + { + if (!preg_match('/^\d{4}-\d{2}-\d{2}/', $dateStart) || + !preg_match('/^\d{4}-\d{2}-\d{2}/', $dateEnd)) { + throw new InvalidArgumentException("Ungültiges Datumsformat"); + } + + $r = []; + $dateTypes = ["DateCreated", "DateModified"]; + + foreach ($this->allowedTypes as $type) { + foreach ($dateTypes as $dateType) { + $query = $this->db->prepare( + "SELECT COUNT(*) AS COUNT + FROM Anchor + JOIN Entry ON (Anchor.ID = Entry.ID) + WHERE Type = :type AND $dateType BETWEEN :dateStart AND :dateEnd" + ); + + $query->execute([ + ':type' => $type, + ':dateStart' => $dateStart, + ':dateEnd' => $dateEnd + ]); + + $rec = $query->fetch(PDO::FETCH_ASSOC); + $r[$type][$dateType] = $rec ? intval($rec['COUNT']) : 0; + } + } + + return $r; + } + + public function prepare_desc($text) + { + $desc = is_array($text) ? $text[0] : $text; + $text = strtolower($desc); + $desc = str_replace(' ', '_', $text); + + $search = ['ü', 'ä', 'ö', 'ß', '.', ',', 'Ö', 'Ü', 'Ä', '[', ']', '<', '>', '""']; + $replace = ['ue', 'ae', 'oe', 'ss', '', '', 'oe', 'ue', 'ae', '_', '_', '<', '>', '"']; + $desc = str_replace($search, $replace, $desc); + + return $desc; + } +} +?> diff --git a/ajax/newClassification.php b/ajax/newClassification.php new file mode 100644 index 0000000..776a8ed --- /dev/null +++ b/ajax/newClassification.php @@ -0,0 +1,134 @@ + 400, 'message' => 'Bitte Text eingeben!']); + exit(); +} + +/** + * Ermittelt die Eltern-Notation durch Abschneiden des letzten Segments. + * + * Beispiele: + * "T 4.10" → "T 4" + * "G 1.1.1" → "G 1.1" + * "G 1.1" → "G 1" + * "G 1" → "G" + * "G" → null (Root-Ebene) + */ +function getParentNotation($notation) { + $lastDot = strrpos($notation, '.'); + if ($lastDot !== false) { + return substr($notation, 0, $lastDot); + } + $lastSpace = strrpos($notation, ' '); + if ($lastSpace !== false) { + return substr($notation, 0, $lastSpace); + } + return null; // Root +} + +/** + * Sucht die Treeview-ID für eine gegebene Notation. + * Die Notation steht am Anfang des name-Feldes, gefolgt von einem Leerzeichen + * oder ist identisch mit dem name-Feld (bei Blatt-Knoten ohne Titel). + * + * @param mysqli $conn + * @param string $notation z.B. "T 4" + * @return int Treeview-ID oder 0 wenn nicht gefunden + */ +function findTreeviewIdByNotation($conn, $notation) { + $escaped = mysqli_real_escape_string($conn, $notation); + // Entweder exakter Match oder Notation + Leerzeichen am Anfang + $sql = "SELECT id FROM Treeview + WHERE name = '$escaped' + OR name LIKE '$escaped %' + ORDER BY id + LIMIT 1"; + $res = mysqli_query($conn, $sql); + if ($res) { + $row = mysqli_fetch_assoc($res); + if ($row) return intval($row['id']); + } + return 0; +} + +try { + $crud = new CRUD(); + + // Normalisierten Descriptor erzeugen + $desc = $crud->prepare_desc($term); + $completetext = $term; + + // Dubletten-Prüfung (normalisiert) + $existing = $crud->checkDuplicateByNormalizedText('Classification', $desc); + if ($existing > 0) { + echo json_encode(['status' => 409, 'message' => 'Eine Klassifikation mit diesem Text existiert bereits']); + exit(); + } + + // Neuen Eintrag in Anchor + Entry erstellen + $result = $crud->insertNewTerm('Classification', $term, $desc, $detailType, $classification, $scopenote, $completetext); + + // --- Treeview-Eintrag anlegen --- + $conn = mysqli_connect(HOST, USER, PASSWORD, DATABASE); + if (!mysqli_connect_errno()) { + mysqli_set_charset($conn, "utf8"); + + // Parent-ID ermitteln + $parentId = 0; // Default: Root + if (!empty($classification)) { + $parentNotation = getParentNotation($classification); + if ($parentNotation !== null) { + $foundId = findTreeviewIdByNotation($conn, $parentNotation); + if ($foundId > 0) { + $parentId = $foundId; + } + // Hinweis: wenn Parent nicht gefunden → landet im Root (parent_id = 0) + } + } + + // name und text = vollständiger Term (Notation + Bezeichnung), link = '#' + $nameEscaped = mysqli_real_escape_string($conn, $term); + $insertSql = "INSERT INTO Treeview (name, text, link, parent_id) + VALUES ('$nameEscaped', '$nameEscaped', '#', '$parentId')"; + mysqli_query($conn, $insertSql); + $treeviewId = mysqli_insert_id($conn); + mysqli_close($conn); + } + + echo json_encode([ + 'status' => 200, + 'message' => 'Klassifikation erfolgreich erstellt', + 'Anchor' => $result['IDAnchor'], + 'Entry' => $result['IDEntry'], + 'Linking' => $result['IDLinking'], + 'TreeviewID' => $treeviewId ?? 0 + ]); + +} catch (Exception $e) { + echo json_encode(['status' => 500, 'message' => $e->getMessage()]); +} +?> diff --git a/ajax/newCorporate.php b/ajax/newCorporate.php new file mode 100644 index 0000000..ff57630 --- /dev/null +++ b/ajax/newCorporate.php @@ -0,0 +1,52 @@ + 400, 'message' => 'Bitte Namen eingeben!']); + exit(); +} + +try { + $crud = new CRUD(); + + // Descriptor erstellen (für Anchor.Text) + $desc = $crud->prepare_desc($term); + $completetext = $term; + + // Prüfen ob bereits existiert + $existing = $crud->readAuthorityNumbersOfRecords('Corporate', $term, 0, true); + if ($existing > 0) { + echo json_encode(['status' => 409, 'message' => 'Eine Körperschaft mit diesem Namen existiert bereits']); + exit(); + } + + // Neuen Eintrag erstellen + $result = $crud->insertNewTerm('Corporate', $term, $desc, $detailType, $classification, $scopenote, $completetext); + + echo json_encode([ + 'status' => 200, + 'message' => 'Körperschaft erfolgreich erstellt', + 'Anchor' => $result['IDAnchor'], + 'Entry' => $result['IDEntry'], + 'Linking' => $result['IDLinking'] + ]); + +} catch (Exception $e) { + echo json_encode(['status' => 500, 'message' => $e->getMessage()]); +} +?> diff --git a/ajax/newPerson.php b/ajax/newPerson.php new file mode 100644 index 0000000..6d943ef --- /dev/null +++ b/ajax/newPerson.php @@ -0,0 +1,51 @@ + 400, 'message' => 'Bitte Namen eingeben!']); + exit(); +} + +try { + $crud = new CRUD(); + + // Normalisierten Descriptor-Text erzeugen (entspricht Anchor.Text in der DB) + $desc = $crud->prepare_desc($term); + $completetext = $term; + + // Dubletten-Prüfung gegen Anchor.Text (normalisierte Form) + // Verhindert Fehlalarme durch abweichende Groß-/Kleinschreibung oder Sonderzeichen + $existing = $crud->checkDuplicateByNormalizedText('Person', $desc); + if ($existing > 0) { + echo json_encode(['status' => 409, 'message' => 'Eine Person mit diesem Namen existiert bereits']); + exit(); + } + + // Neuen Eintrag erstellen + $result = $crud->insertNewTerm('Person', $term, $desc, $detailType, $classification, $scopenote, $completetext); + + echo json_encode([ + 'status' => 200, + 'message' => 'Person erfolgreich erstellt', + 'Anchor' => $result['IDAnchor'], + 'Entry' => $result['IDEntry'], + 'Linking' => $result['IDLinking'] + ]); + +} catch (Exception $e) { + echo json_encode(['status' => 500, 'message' => $e->getMessage()]); +} +?> diff --git a/ajax/newPublisher.php b/ajax/newPublisher.php new file mode 100644 index 0000000..477bd2e --- /dev/null +++ b/ajax/newPublisher.php @@ -0,0 +1,52 @@ + 400, 'message' => 'Bitte Namen eingeben!']); + exit(); +} + +try { + $crud = new CRUD(); + + // Descriptor erstellen (für Anchor.Text) + $desc = $crud->prepare_desc($term); + $completetext = $term; + + // Prüfen ob bereits existiert + $existing = $crud->readAuthorityNumbersOfRecords('Publisher', $term, 0, true); + if ($existing > 0) { + echo json_encode(['status' => 409, 'message' => 'Ein Verlag mit diesem Namen existiert bereits']); + exit(); + } + + // Neuen Eintrag erstellen + $result = $crud->insertNewTerm('Publisher', $term, $desc, $detailType, $classification, $scopenote, $completetext); + + echo json_encode([ + 'status' => 200, + 'message' => 'Verlag erfolgreich erstellt', + 'Anchor' => $result['IDAnchor'], + 'Entry' => $result['IDEntry'], + 'Linking' => $result['IDLinking'] + ]); + +} catch (Exception $e) { + echo json_encode(['status' => 500, 'message' => $e->getMessage()]); +} +?> diff --git a/ajax/newSubject.php b/ajax/newSubject.php new file mode 100644 index 0000000..c151a73 --- /dev/null +++ b/ajax/newSubject.php @@ -0,0 +1,50 @@ + 400, 'message' => 'Bitte Schlagwort eingeben!']); + exit(); +} + +try { + $crud = new CRUD(); + + // Normalisierten Descriptor-Text erzeugen (entspricht Anchor.Text in der DB) + $desc = $crud->prepare_desc($term); + $completetext = $term; + + // Dubletten-Prüfung gegen Anchor.Text (normalisierte Form) + $existing = $crud->checkDuplicateByNormalizedText('Subject', $desc); + if ($existing > 0) { + echo json_encode(['status' => 409, 'message' => 'Ein Schlagwort mit diesem Begriff existiert bereits']); + exit(); + } + + // Neuen Eintrag erstellen + $result = $crud->insertNewTerm('Subject', $term, $desc, $detailType, $classification, $scopenote, $completetext); + + echo json_encode([ + 'status' => 200, + 'message' => 'Schlagwort erfolgreich erstellt', + 'Anchor' => $result['IDAnchor'], + 'Entry' => $result['IDEntry'], + 'Linking' => $result['IDLinking'] + ]); + +} catch (Exception $e) { + echo json_encode(['status' => 500, 'message' => $e->getMessage()]); +} +?> diff --git a/ajax/saveSynonym.php b/ajax/saveSynonym.php new file mode 100644 index 0000000..178f08d --- /dev/null +++ b/ajax/saveSynonym.php @@ -0,0 +1,123 @@ + false, 'error' => 'Datenbankverbindung fehlgeschlagen']); + exit(); +} + +mysqli_set_charset($conn, "utf8"); + +$text1 = isset($_POST['text1']) ? trim($_POST['text1']) : ''; +$text2 = isset($_POST['text2']) ? trim($_POST['text2']) : ''; +$action = isset($_POST['action']) ? $_POST['action'] : 'add'; + +if (empty($text1) || empty($text2)) { + echo json_encode(['success' => false, 'error' => 'Beide Begriffe müssen angegeben werden']); + exit(); +} + +// Descriptor erstellen (Kleinbuchstaben, Leerzeichen durch Unterstrich) +function prepare_desc($text) { + $desc = strtolower($text); + $desc = str_replace(' ', '_', $desc); + return $desc; +} + +$desc1 = prepare_desc($text1); +$desc2 = prepare_desc($text2); + +if ($action === 'add') { + // Prüfen ob text1 bereits eine IDLinking hat + $sql = "SELECT IDLinking FROM Synonyms WHERE Descriptor = '" . mysqli_real_escape_string($conn, $desc1) . "' LIMIT 1"; + $res = mysqli_query($conn, $sql); + $idLinking = null; + + if ($res && mysqli_num_rows($res) > 0) { + $row = mysqli_fetch_assoc($res); + $idLinking = $row['IDLinking']; + } + + // Prüfen ob text2 bereits eine IDLinking hat + $sql2 = "SELECT IDLinking FROM Synonyms WHERE Descriptor = '" . mysqli_real_escape_string($conn, $desc2) . "' LIMIT 1"; + $res2 = mysqli_query($conn, $sql2); + $idLinking2 = null; + + if ($res2 && mysqli_num_rows($res2) > 0) { + $row2 = mysqli_fetch_assoc($res2); + $idLinking2 = $row2['IDLinking']; + } + + // Fall 1: Beide haben noch keine IDLinking - neue Gruppe erstellen + if ($idLinking === null && $idLinking2 === null) { + // Höchste IDLinking finden und +1 + $sqlMax = "SELECT MAX(IDLinking) as maxId FROM Synonyms"; + $resMax = mysqli_query($conn, $sqlMax); + $rowMax = mysqli_fetch_assoc($resMax); + $newIdLinking = ($rowMax['maxId'] ?? 0) + 1; + + // Beide Begriffe einfügen + $sqlInsert1 = "INSERT INTO Synonyms (IDLinking, Text, Descriptor) VALUES ($newIdLinking, '" . mysqli_real_escape_string($conn, $text1) . "', '" . mysqli_real_escape_string($conn, $desc1) . "')"; + $sqlInsert2 = "INSERT INTO Synonyms (IDLinking, Text, Descriptor) VALUES ($newIdLinking, '" . mysqli_real_escape_string($conn, $text2) . "', '" . mysqli_real_escape_string($conn, $desc2) . "')"; + + mysqli_query($conn, $sqlInsert1); + mysqli_query($conn, $sqlInsert2); + + echo json_encode(['success' => true, 'message' => 'Neue Synonym-Gruppe erstellt', 'idLinking' => $newIdLinking]); + } + // Fall 2: text1 hat bereits eine IDLinking - text2 zur Gruppe hinzufügen + else if ($idLinking !== null && $idLinking2 === null) { + $sqlInsert = "INSERT INTO Synonyms (IDLinking, Text, Descriptor) VALUES ($idLinking, '" . mysqli_real_escape_string($conn, $text2) . "', '" . mysqli_real_escape_string($conn, $desc2) . "')"; + mysqli_query($conn, $sqlInsert); + + echo json_encode(['success' => true, 'message' => 'Synonym zur bestehenden Gruppe hinzugefügt', 'idLinking' => $idLinking]); + } + // Fall 3: text2 hat bereits eine IDLinking - text1 zur Gruppe hinzufügen + else if ($idLinking === null && $idLinking2 !== null) { + $sqlInsert = "INSERT INTO Synonyms (IDLinking, Text, Descriptor) VALUES ($idLinking2, '" . mysqli_real_escape_string($conn, $text1) . "', '" . mysqli_real_escape_string($conn, $desc1) . "')"; + mysqli_query($conn, $sqlInsert); + + echo json_encode(['success' => true, 'message' => 'Begriff zur bestehenden Synonym-Gruppe hinzugefügt', 'idLinking' => $idLinking2]); + } + // Fall 4: Beide haben bereits IDLinking - Gruppen zusammenführen + else { + if ($idLinking == $idLinking2) { + echo json_encode(['success' => false, 'error' => 'Diese Begriffe sind bereits Synonyme']); + } else { + // Alle Einträge von idLinking2 auf idLinking umstellen + $sqlUpdate = "UPDATE Synonyms SET IDLinking = $idLinking WHERE IDLinking = $idLinking2"; + mysqli_query($conn, $sqlUpdate); + + echo json_encode(['success' => true, 'message' => 'Synonym-Gruppen zusammengeführt', 'idLinking' => $idLinking]); + } + } +} +else if ($action === 'remove') { + // Synonym entfernen (nur den einen Eintrag löschen) + $sqlDelete = "DELETE FROM Synonyms WHERE Descriptor = '" . mysqli_real_escape_string($conn, $desc2) . "'"; + mysqli_query($conn, $sqlDelete); + + if (mysqli_affected_rows($conn) > 0) { + echo json_encode(['success' => true, 'message' => 'Synonym entfernt']); + } else { + echo json_encode(['success' => false, 'error' => 'Synonym nicht gefunden']); + } +} +else { + echo json_encode(['success' => false, 'error' => 'Unbekannte Aktion']); +} + +mysqli_close($conn); +?> diff --git a/ajax/searchTreeData.php b/ajax/searchTreeData.php new file mode 100644 index 0000000..d2baf65 --- /dev/null +++ b/ajax/searchTreeData.php @@ -0,0 +1,75 @@ + strval($row['id']), + 'text' => $row['text'] ? $row['text'] : $row['name'], + 'parent_id' => $row['parent_id'] + ]; +} + +mysqli_close($conn); +echo json_encode($result, JSON_UNESCAPED_UNICODE); +?> diff --git a/ajax/writeNewRelation.php b/ajax/writeNewRelation.php new file mode 100644 index 0000000..6aa950c --- /dev/null +++ b/ajax/writeNewRelation.php @@ -0,0 +1,113 @@ + false, 'error' => 'Datenbankverbindung fehlgeschlagen']); + exit(); +} + +mysqli_set_charset($conn, "utf8"); + +// Parameter aus JavaScript (AnchorID, relationType, relationID) +$idAnchor = isset($_POST['AnchorID']) ? intval($_POST['AnchorID']) : 0; +if ($idAnchor === 0) { + $idAnchor = isset($_POST['IDAnchor']) ? intval($_POST['IDAnchor']) : 0; +} + +$idEntry = isset($_POST['relationID']) ? intval($_POST['relationID']) : 0; +if ($idEntry === 0) { + $idEntry = isset($_POST['IDEntry']) ? intval($_POST['IDEntry']) : 0; +} + +$relationtype = isset($_POST['relationType']) ? trim($_POST['relationType']) : ''; +if (empty($relationtype)) { + $relationtype = isset($_POST['Relationtype']) ? trim($_POST['Relationtype']) : ''; +} + +if ($idAnchor === 0 || $idEntry === 0) { + echo json_encode(['success' => false, 'error' => 'IDAnchor und IDEntry müssen angegeben werden']); + exit(); +} + +if (empty($relationtype)) { + echo json_encode(['success' => false, 'error' => 'Relationtype muss angegeben werden']); + exit(); +} + +// Erlaubte Relationstypen +$allowedTypes = ['BT', 'NT', 'RT', 'USE', 'UF']; +if (!in_array($relationtype, $allowedTypes)) { + echo json_encode(['success' => false, 'error' => 'Ungültiger Relationstyp']); + exit(); +} + +// Prüfen ob Relation bereits existiert +$checkSql = "SELECT ID FROM Linking WHERE IDAnchor = $idAnchor AND IDEntry = $idEntry AND Relationtype = '" . mysqli_real_escape_string($conn, $relationtype) . "'"; +$checkResult = mysqli_query($conn, $checkSql); + +if ($checkResult && mysqli_num_rows($checkResult) > 0) { + echo json_encode(['success' => false, 'error' => 'Diese Relation existiert bereits']); + mysqli_close($conn); + exit(); +} + +// Relation einfügen +$insertSql = "INSERT INTO Linking (IDAnchor, IDEntry, Relationtype) VALUES ($idAnchor, $idEntry, '" . mysqli_real_escape_string($conn, $relationtype) . "')"; + +if (mysqli_query($conn, $insertSql)) { + $newId = mysqli_insert_id($conn); + + // Reziproke Relation erstellen (BT <-> NT, RT <-> RT) + $reciprocalType = null; + switch ($relationtype) { + case 'BT': + $reciprocalType = 'NT'; + break; + case 'NT': + $reciprocalType = 'BT'; + break; + case 'RT': + $reciprocalType = 'RT'; + break; + case 'USE': + $reciprocalType = 'UF'; + break; + case 'UF': + $reciprocalType = 'USE'; + break; + } + + if ($reciprocalType) { + // Prüfen ob reziproke Relation schon existiert + $checkRecSql = "SELECT ID FROM Linking WHERE IDAnchor = $idEntry AND IDEntry = $idAnchor AND Relationtype = '$reciprocalType'"; + $checkRecResult = mysqli_query($conn, $checkRecSql); + + if (!$checkRecResult || mysqli_num_rows($checkRecResult) === 0) { + $insertRecSql = "INSERT INTO Linking (IDAnchor, IDEntry, Relationtype) VALUES ($idEntry, $idAnchor, '$reciprocalType')"; + mysqli_query($conn, $insertRecSql); + } + } + + echo json_encode([ + 'success' => true, + 'message' => 'Relation erfolgreich erstellt', + 'id' => $newId + ]); +} else { + echo json_encode(['success' => false, 'error' => 'Fehler beim Speichern: ' . mysqli_error($conn)]); +} + +mysqli_close($conn); +?> diff --git a/dspace-api/getAuthorityCount.php b/dspace-api/getAuthorityCount.php new file mode 100644 index 0000000..0866106 --- /dev/null +++ b/dspace-api/getAuthorityCount.php @@ -0,0 +1,115 @@ + {"query":"Berufsbildung","authType":"Subject","count":42} + */ + +header('Content-Type: application/json; charset=utf-8'); +header('Access-Control-Allow-Origin: *'); + +// IP-Prüfung +require_once __DIR__ . '/checkAccess.php'; + +// Parameter +$authType = isset($_GET['authType']) ? trim($_GET['authType']) : ''; +$query = isset($_GET['query']) ? trim($_GET['query']) : ''; + +// Validierung +$allowedTypes = array('Subject', 'Person'); +if (!in_array($authType, $allowedTypes)) { + http_response_code(400); + echo json_encode(array('error' => 'Invalid authType. Allowed: Subject, Person')); + exit; +} + +if (strlen($query) === 0) { + http_response_code(400); + echo json_encode(array('error' => 'Missing parameter: query')); + exit; +} + +// Solr-Feld je nach authType +switch ($authType) { + case 'Subject': + $queryField = 'dc.subject.other'; + break; + case 'Person': + $queryField = 'dc.contributor.author'; + break; +} + +// Query bereinigen +$query = urldecode($query); +$query = str_replace("'", "", $query); + +// Solr-Abfrage +$solr_baseurl = "http://localhost:8080/solr/search/query?"; +$solr_query = $queryField . ':' . $query; +$rows = "&rows=10000"; +$fl = "&fl=" . $queryField; +$solr_URL = $solr_baseurl . "wt=phps" . $fl . $rows . "&q=" . urlencode($solr_query . " AND withdrawn:false"); + +$context = stream_context_create(array( + 'http' => array('timeout' => 10) +)); + +$response = @file_get_contents($solr_URL, false, $context); + +if ($response === false) { + http_response_code(500); + echo json_encode(array('error' => 'Solr query failed')); + exit; +} + +$result = unserialize($response); + +if (!isset($result['response']['docs'])) { + echo json_encode(array( + 'query' => $query, + 'authType' => $authType, + 'count' => 0 + )); + exit; +} + +// Exakte Treffer zählen (analog zu DSpaceCountModel) +$count = 0; +foreach ($result['response']['docs'] as $entry) { + foreach ($entry as $hit) { + if (!is_array($hit)) { + $hit = array($hit); + } + foreach ($hit as $h) { + if ($authType === "Subject") { + if (strcmp($query, trim($h)) === 0) { + $count++; + } + } else { + // Person: Authority-ID in eckigen Klammern abschneiden + $name = trim(substr($h, 0, strpos($h, "["))); + if (strlen($name) === 0) { + $name = trim($h); + } + if (strcmp($query, $name) === 0) { + $count++; + } + } + } + } +} + +echo json_encode(array( + 'query' => $query, + 'authType' => $authType, + 'count' => $count +)); +?> diff --git a/getTreePath.php b/getTreePath.php new file mode 100644 index 0000000..d347bfc --- /dev/null +++ b/getTreePath.php @@ -0,0 +1,46 @@ + 0 && $limit-- > 0) { + $res = mysqli_query($conn, "SELECT id, parent_id FROM Treeview WHERE id = $curId LIMIT 1"); + if (!$res) break; + $row = mysqli_fetch_assoc($res); + if (!$row) break; + + array_unshift($path, intval($row['id'])); // vorne einfügen → Root zuerst + $parentId = intval($row['parent_id']); + if ($parentId === 0) break; + $curId = $parentId; +} + +mysqli_close($conn); +echo json_encode($path, JSON_UNESCAPED_UNICODE); +?> diff --git a/index.php b/index.php new file mode 100644 index 0000000..3a91bce --- /dev/null +++ b/index.php @@ -0,0 +1,539 @@ + + + + + +
+
+ + + + + +
+

+ Übersicht +

+ + +
+
+ + Lade Statistiken... +
+
+
+ + +
+ + +
+
+ Zuletzt geändert +
+
+
+ +
+
+
+ + +
+
+ Zuletzt hinzugefügt +
+
+
+ +
+
+
+ +
+ + +
+

+ Qualitäts-Check +

+

Einträge ohne Relationen (Waisen)

+
+
+ +
+
+
+ +
+
+ + + + diff --git a/js/ClassificationScript.js b/js/ClassificationScript.js new file mode 100644 index 0000000..705b6b7 --- /dev/null +++ b/js/ClassificationScript.js @@ -0,0 +1,639 @@ +/** + * Created by Roland on 2021/07/01. + * Updated for BIBB Layout 2024 + */ +// CLASSIFICATIONS + +// Show blank screen to add new Classification +function newClassificationsShow() { + + // Sichere Element-Zugriffe mit Null-Checks + var saveBtn = document.getElementById("newClassificationSave"); + var dismissBtn = document.getElementById("newClassificationDismiss"); + var modifyBtn = document.getElementById("ClassificationModifySave"); + var termField = document.getElementById("new_term"); + var notationField = document.getElementById("new_notation"); + var scopenoteField = document.getElementById("new_scopenote"); + var relationLabel = document.getElementById("new_relation_label"); + var relationDismissBtn = document.getElementById("newRelationClassificationDismiss"); + var relationTable = document.getElementById("relation_table"); + var errorDiv = document.getElementById("errorNewClassification"); + + if (saveBtn) saveBtn.style.display = 'inline'; + if (dismissBtn) dismissBtn.style.display = 'inline'; + if (modifyBtn) modifyBtn.style.display = 'none'; + if (termField) termField.disabled = false; + if (notationField) notationField.disabled = false; + if (scopenoteField) scopenoteField.disabled = false; + + // Relationen-Bereich verstecken bei neuer Klassifikation + if (relationLabel) relationLabel.style.display = 'none'; + if (relationDismissBtn) relationDismissBtn.style.display = 'none'; + showRelations(0,0,0); + if (relationTable) relationTable.style.display = 'none'; + if (errorDiv) errorDiv.style.display = 'none'; + + $("#new_term").val(""); + $("#new_notation").val(""); + $("#new_scopenote").val(""); + + $("#NewClassificationModal").modal("show"); +} + +// Insert new classification record +function CreateNewEntry(type) { + // get values + + var term = $("#new_term").val(); + term = term.trim(); + var notation = $("#new_notation").val(); + notation = notation.trim(); + var detailtype = ""; + var classification = ""; + var scopenote = $("#new_scopenote").val(); + scopenote = scopenote.trim(); + + if (term.length == 0) { + var a = document.getElementById("errorNewClassification"); + a.classList.add('alert-danger'); + a.textContent = 'Bitte Klassifikation eingeben!'; + a.style.display = 'block'; + return; + } + + var a = document.getElementById("errorNewClassification"); + a.classList.remove('alert-danger'); + a.textContent = ''; + a.style.display = 'none'; + + $.post("ajax/newClassification.php", { + term : term, + type : type, + notation : notation, + detailtype : detailtype, + classification: classification, + scopenote : scopenote + }, function (data, status) { + + var dt = (typeof data === 'string') ? JSON.parse(data) : data; + var e = document.getElementById("errorNewClassification"); + + if (dt['status'] == 200) + { + var AnchorID = dt["Anchor"]; + var EntryID = dt["Entry"]; + var LinkingID = dt["Linking"]; + + var b = document.getElementById("newClassificationSave"); + b.style.display='none'; + var c = document.getElementById("newClassificationDismiss"); + c.style.display='none'; + document.getElementById("new_term").disabled = true; + document.getElementById("new_notation").disabled = true; + document.getElementById("new_scopenote").disabled = true; + $table.bootstrapTable('refresh'); + + e.classList.remove('alert-danger'); + e.classList.add('alert-success'); + e.textContent = 'Neue Klassifikation aufgenommen'; + e.style.display = 'block'; + + // ID setzen für Relationen + $('#ID').val(AnchorID); + + // Relationen-Bereich anzeigen nach Speichern + document.getElementById("new_relation_label").style.display = 'block'; + document.getElementById("newRelationClassificationDismiss").style.display = 'inline'; + showRelations(AnchorID,EntryID,LinkingID); + } else { + e.classList.remove('alert-success'); + e.classList.add('alert-danger'); + e.textContent = dt['message']; + e.style.display = 'block'; + return; + } + + }); + +} + +// Show relations of a classification record +function showRelations(Anchor,Entry,Linking) +{ + $.get("ajax/getRelations.php", { + anchorID:Anchor, + authType:"Classification" + }, + function (data, status) { + if (status=="success") + { + $(".new_modal_content").html(data); + // Tabelle nur anzeigen wenn Relationen vorhanden + var relationTable = document.getElementById("relation_table"); + if (relationTable) { + if (data && data.trim().length > 0) { + relationTable.style.display = 'block'; + } else { + relationTable.style.display = 'none'; + } + } + $("#NewClassificationModal").modal("show"); + } + } + ); + +} + + +// create new relation for a classification record +function CreateNewRelation(AnchorID, relationID, event) { + if (event) { + event.preventDefault(); + event.stopPropagation(); + } + + console.log('🚀 CreateNewRelation:', { AnchorID, relationID }); + + try { + // Validierung + if (!AnchorID || !relationID) { + showError('Fehlende Parameter: AnchorID oder relationID'); + return false; + } + + var relationTypeElement = document.getElementById("new_relationtype"); + + if (!relationTypeElement) { + showError('Relationstyp-Element nicht gefunden'); + return false; + } + + var relationType = relationTypeElement.value; + + if (!relationType || relationType.trim() === '') { + showError('Bitte wählen Sie einen Relationstyp aus'); + return false; + } + + // Loading anzeigen + showLoading(); + + $.ajax({ + url: "ajax/writeNewRelation.php", + type: "POST", + data: { + AnchorID: AnchorID, + relationType: relationType, + relationID: relationID + }, + dataType: "text", + timeout: 10000, + + success: function(response) { + console.log("✅ Success! Response:", response); + + try { + var data = JSON.parse(response); + + if (data.error) { + showError('Serverfehler:
' + data.error); + return; + } + + if (data.success === true) { + showSuccess('Neue Relation erfolgreich aufgenommen!'); + + if (typeof showRelations === 'function') { + setTimeout(function() { + showRelations(AnchorID, 0, 0); + }, 500); + } + + resetForm(); + } else { + showError('Unerwartete Antwort vom Server'); + } + + } catch (parseError) { + console.log("⚠️ Keine JSON-Antwort, prüfe Plain-Text"); + + if (response && response.length > 0 && !isNaN(response.trim())) { + showSuccess('Neue Relation erfolgreich aufgenommen! (ID: ' + response.trim() + ')'); + + if (typeof showRelations === 'function') { + setTimeout(function() { + showRelations(AnchorID, 0, 0); + }, 500); + } + + resetForm(); + } else { + showError('Unerwartete Antwort:
' + + '' + response.substring(0, 200) + ''); + } + } + }, + + error: function(xhr, status, error) { + console.error("❌ AJAX Error:", { status, error, xhr }); + + var errorMessage = 'Fehler beim Speichern der Relation

'; + + if (xhr.status === 0) { + errorMessage += '❌ Keine Verbindung zum Server möglich
'; + errorMessage += 'Prüfen Sie Ihre Internetverbindung'; + } else if (xhr.status === 404) { + errorMessage += '❌ Datei nicht gefunden (404)
'; + errorMessage += 'ajax/writeNewRelation.php existiert nicht'; + } else if (xhr.status === 500) { + errorMessage += '❌ Interner Serverfehler (500)
'; + if (xhr.responseText) { + errorMessage += '
Details anzeigen
' +
+                                      xhr.responseText.substring(0, 500) + '
'; + } + } else if (status === 'timeout') { + errorMessage += '❌ Zeitüberschreitung
'; + errorMessage += 'Der Server hat nicht rechtzeitig geantwortet'; + } else if (status === 'parsererror') { + errorMessage += '❌ Fehler beim Verarbeiten der Server-Antwort
'; + errorMessage += 'Die Server-Antwort war ungültig'; + } else { + errorMessage += '❌ ' + status + ' (' + xhr.status + ')'; + if (error) { + errorMessage += '
' + error + ''; + } + } + + showError(errorMessage); + }, + + complete: function(xhr, status) { + console.log("🏁 Request abgeschlossen:", status); + if (status !== 'success') { + hideLoading(); + } + } + }); + + return false; + + } catch (e) { + hideLoading(); + console.error("💥 Exception:", e); + showError('JavaScript-Fehler:
' + e.message); + return false; + } +} + +function showError(message) { + var errorElement = document.getElementById("errorNewClassification"); + if (errorElement) { + errorElement.className = 'alert alert-danger'; + errorElement.innerHTML = '' + message.replace(/\n/g, '
'); + errorElement.style.display = 'block'; + + errorElement.scrollIntoView({ behavior: 'smooth', block: 'nearest' }); + + setTimeout(function() { + fadeOutAlert(); + }, 8000); + } +} + +function showSuccess(message) { + var errorElement = document.getElementById("errorNewClassification"); + if (errorElement) { + errorElement.className = 'alert alert-success'; + errorElement.innerHTML = '' + message; + errorElement.style.display = 'block'; + + errorElement.scrollIntoView({ behavior: 'smooth', block: 'nearest' }); + + setTimeout(function() { + fadeOutAlert(); + }, 3000); + } +} + +function showWarning(message) { + var errorElement = document.getElementById("errorNewClassification"); + if (errorElement) { + errorElement.className = 'alert alert-warning'; + errorElement.innerHTML = '' + message; + errorElement.style.display = 'block'; + + errorElement.scrollIntoView({ behavior: 'smooth', block: 'nearest' }); + + setTimeout(function() { + fadeOutAlert(); + }, 5000); + } +} + +function showLoading() { + var errorElement = document.getElementById("errorNewClassification"); + if (errorElement) { + errorElement.className = 'alert alert-info'; + errorElement.innerHTML = '
Laden...
Speichere Relation...
'; + errorElement.style.display = 'block'; + + errorElement.scrollIntoView({ behavior: 'smooth', block: 'nearest' }); + } +} + +function hideLoading() { + var errorElement = document.getElementById("errorNewClassification"); + if (errorElement) { + errorElement.style.display = 'none'; + } +} + +function hideAlert() { + var errorElement = document.getElementById("errorNewClassification"); + if (errorElement) { + errorElement.style.display = 'none'; + } +} + +function fadeOutAlert() { + var errorElement = document.getElementById("errorNewClassification"); + if (errorElement && errorElement.style.display !== 'none') { + $(errorElement).fadeOut(500, function() { + errorElement.style.display = 'none'; + }); + } +} + +function resetForm() { + var relationTextElement = document.getElementById("search_relation"); + if (relationTextElement) { + relationTextElement.value = ''; + } + $('#classification-anlegen').removeData('record-id'); + $('#classification-anlegen').removeData('relation-id'); + hideAlert(); +} + + +// Delete relation for a classification +function DeleteRelation(AnchorID, LinkingID) +{ + if (!confirm('Möchten Sie diese Relation wirklich löschen?')) { + return; + } + + $.post("ajax/deleteRelation.php", { + AnchorID : AnchorID, + LinkingID : LinkingID + }, + function (data, status) { + if (status=="success") + { + var e = document.getElementById("errorNewClassification"); + e.classList.add('alert-success'); + e.textContent = ' Relation gelöscht'; + e.style.display = 'block'; + + showRelations(AnchorID,0,0) + } + + } + ); +} + +// delete the whole classification entry +function DeleteTerm(AnchorID) +{ + if (!confirm('Möchten Sie diesen Eintrag wirklich löschen?')) { + return; + } + + $.post("ajax/deleteTerm.php", { + AnchorID: AnchorID[0] + }, + function (data, status) { + var metadata = (typeof data === 'string') ? JSON.parse(data) : data; + + if (status === "success" && metadata.success === true) { + // Tabelle neu laden – Zeile ist serverseitig wirklich gelöscht + $table.bootstrapTable('refresh'); + } else { + // Serverseitiger Fehler: Zeile wieder einblenden und Meldung anzeigen + $table.bootstrapTable('refresh'); + alert('Fehler beim Löschen: ' + (metadata.error || 'Unbekannter Fehler')); + } + } + ); +} + +// Modify classification and its relations +function ModifyTerm(AnchorID) +{ + $("#ID").val(AnchorID[0]); + + // Sichere Element-Zugriffe mit Null-Checks + var termField = document.getElementById("new_term"); + var notationField = document.getElementById("new_notation"); + var scopenoteField = document.getElementById("new_scopenote"); + var relationLabel = document.getElementById("new_relation_label"); + var relationTable = document.getElementById("relation_table"); + var saveBtn = document.getElementById("newClassificationSave"); + var dismissBtn = document.getElementById("newClassificationDismiss"); + var modifyBtn = document.getElementById("ClassificationModifySave"); + var relationDismissBtn = document.getElementById("newRelationClassificationDismiss"); + var errorDiv = document.getElementById("errorNewClassification"); + + // Name-Feld deaktivieren (darf nicht geändert werden) + if (termField) termField.disabled = true; + + // Andere Felder aktivieren + if (notationField) notationField.disabled = false; + if (scopenoteField) scopenoteField.disabled = false; + + // Relationen-Bereich sofort sichtbar machen bei Bearbeitung! + if (relationLabel) relationLabel.style.display = 'block'; + if (relationTable) relationTable.style.display = 'block'; + + // Buttons konfigurieren + if (saveBtn) saveBtn.style.display = 'none'; + if (dismissBtn) dismissBtn.style.display = 'inline'; + if (modifyBtn) modifyBtn.style.display = 'inline'; + if (relationDismissBtn) relationDismissBtn.style.display = 'none'; + + // Alert verstecken + if (errorDiv) errorDiv.style.display = 'none'; + + $("#NewClassificationModalHeadline").text("Klassifikation ändern"); + + $.get("/Thesaurus/ajax/getAuthorityDataRaw.php", { + id : AnchorID[0], + authType :"Classification" + }, + function (data, status) { + console.log('📥 getAuthorityDataRaw status:', status); + console.log('📥 getAuthorityDataRaw data:', data); + if (status == "success") + { + var metadata = (typeof data === 'string') ? JSON.parse(data) : data; + console.log('📥 metadata:', metadata); + dt = (metadata['rows'][0]); + console.log('📥 dt:', dt); + if (dt) { + $("#new_term").val(dt.Text); + $("#new_id").val(dt.ID); + $("#new_notation").val(dt.Notation); + $("#new_descriptor").val(dt.Descriptor); + $("#new_scopenote").val(dt.Scopenote); + console.log('✅ Felder befüllt mit:', dt.Text); + } else { + console.error('❌ Keine Daten in rows[0]'); + } + } + + } + ).fail(function(xhr, status, error) { + console.error('❌ AJAX Fehler:', status, error); + }); + + $.get("ajax/getRelations.php", { + anchorID:AnchorID[0], + authType: "Classification" + }, + function (data, status) { + if (status=="success") + { + $(".new_modal_content").html(data); + $("#NewClassificationModal").modal("show"); + + // Autocomplete neu initialisieren nach Modal-Öffnung + setTimeout(function() { + if (typeof searchRelation === 'function') { + console.log('🔄 Autocomplete wird neu initialisiert...'); + searchRelation(); + } + }, 300); + } + } + ); +} + +// rewrite records after updating a classification entry +function UpdateEntry(authType) +{ + var AnchorID = $("#ID").val(); + var term = $("#new_term").val(); + term = term.trim(); + var notation = $("#new_notation").val(); + notation = notation.trim(); + var type = 'Classification'; + var detailtype = ''; + var classification = ''; + var scopenote = $("#new_scopenote").val(); + scopenote = scopenote.trim(); + + if (term.length == 0) { + var a = document.getElementById("errorNewClassification"); + a.classList.add('alert-danger'); + a.textContent = 'Bitte Klassifikation eingeben!'; + a.style.display = 'block'; + return; + } + + var a = document.getElementById("errorNewClassification"); + a.classList.remove('alert-danger'); + a.textContent = ''; + a.style.display = 'none'; + + + + $.post("ajax/UpdateTerm.php", { + AnchorID: AnchorID, + authType: "Classification", + term: term, + notation: notation, + type: type, + detailtype: detailtype, + classification: classification, + scopenote: scopenote + }, + function (data, status) { + if (status=="success") + { + document.getElementById("relation_table").style.display = 'block' + document.getElementById("newClassificationSave").style.display = 'none' + document.getElementById("newClassificationDismiss").style.display = 'inline' + document.getElementById("ClassificationModifySave").style.display = 'none' + document.getElementById("newRelationClassificationDismiss").style.display = 'none' + var e = document.getElementById("errorNewClassification"); + e.classList.add('alert-success'); + e.textContent = ' Klassifikation geändert '; + e.style.display = 'block'; + + $("#NewClassificationModalHeadline").text("Klassifikation ändern"); + $("#NewClassificationModal").modal("show"); + } + } + ); + $table.bootstrapTable('refresh') +} + +function ShowModalDetails(id) { + + $.get("/Thesaurus/ajax/getDetailAuthorityData.php", { + authType : 'Classification', + offset : 0, + id : id, + sort : 'Anchor.Text', + limit : 1 + }, + function(data, status) { + if (status == "success") { + var metadata = (typeof data === 'string') ? JSON.parse(data) : data; + var row = metadata['rows']; + //Assign existing values to the modal popup fields + $("#display_term").val(row[0].Text); + $("#display_id").val(row[0].ID); + $("#display_notation").val(row[0].Notation); + $("#display_descriptor").val(row[0].Descriptor); + $("#display_scopenote").val(row[0].Scopenote); + + var html = [] + var txt = '' + $.each(row[0].Relations, function (key, value) { + txt = txt +'
'; + txt = txt +'
'; + txt = txt + '
'; + txt = txt + '
'; + txt = txt + '
'; + txt = txt +'
'; + }) + $(".modal_content").html(txt); + $.each(row[0].Relations, function (key, value) { + $("#display_relationtype_" +key).val(value['Relationtype']); + $("#display_textrelation_" +key).val(value['TextRelation']); + $("#display_IDRelation_" +key).val(value['IDRelation']); + }) + $("#display_relations").val(txt); + } + } + ); + + $("#DetailsModal").modal("show"); +} + + + +function refreshPage() +{ + location.reload(); +} + +function CopyLink(value) +{ + var tempInput = document.createElement("input"); + tempInput.value = value; + document.body.appendChild(tempInput); + tempInput.select(); + document.execCommand("copy"); + document.body.removeChild(tempInput); +} diff --git a/js/CorporateScript.js b/js/CorporateScript.js new file mode 100644 index 0000000..01fc7a0 --- /dev/null +++ b/js/CorporateScript.js @@ -0,0 +1,633 @@ +/** + * Created by Roland on 2021/07/01. + * Updated for BIBB Layout 2024 + */ +// CORPORATES + +// Show blank screen to add new Corporate +function newCorporatesShow() { + + var b = document.getElementById("newCorporateSave"); + b.style.display='inline'; + var c = document.getElementById("newCorporateDismiss"); + c.style.display='inline'; + document.getElementById("CorporateModifySave").style.display = 'none'; + document.getElementById("new_term").disabled = false; + document.getElementById("new_type").disabled = false; + document.getElementById("new_scopenote").disabled = false; + + // Relationen-Bereich verstecken bei neuer Körperschaft + document.getElementById("new_relation_label").style.display = 'none'; + document.getElementById("newRelationCorporateDismiss").style.display = 'none'; + showRelations(0,0,0); + var a = document.getElementById("relation_table"); + if (a != undefined) { + document.getElementById("relation_table").style.display = 'none'; + } + var e = document.getElementById("errorNewCorporate"); + e.style.display = 'none'; + + $("#new_term").val(""); + $("#new_scopenote").val(""); + + $("#NewCorporateModal").modal("show"); +} + +// Insert new corporate record +function CreateNewEntry(type) { + // get values + + var term = $("#new_term").val(); + term = term.trim(); + var type = $("#new_type").val(); + type = type.trim(); + var detailtype = ""; + var classification = ""; + var scopenote = $("#new_scopenote").val(); + scopenote = scopenote.trim(); + + if (term.length == 0) { + var a = document.getElementById("errorNewCorporate"); + a.classList.add('alert-danger'); + a.textContent = 'Bitte Körperschaft eingeben!'; + a.style.display = 'block'; + return; + } + + var a = document.getElementById("errorNewCorporate"); + a.classList.remove('alert-danger'); + a.textContent = ''; + a.style.display = 'none'; + + $.post("ajax/newCorporate.php", { + term : term, + type : type, + detailtype : detailtype, + classification: classification, + scopenote : scopenote + }, function (data, status) { + + var dt = (typeof data === 'string') ? JSON.parse(data) : data; + var e = document.getElementById("errorNewCorporate"); + + if (dt['status'] == 200) + { + var AnchorID = dt["Anchor"]; + var EntryID = dt["Entry"]; + var LinkingID = dt["Linking"]; + + var b = document.getElementById("newCorporateSave"); + b.style.display='none'; + var c = document.getElementById("newCorporateDismiss"); + c.style.display='none'; + document.getElementById("new_term").disabled = true; + document.getElementById("new_type").disabled = true; + document.getElementById("new_scopenote").disabled = true; + $table.bootstrapTable('refresh'); + + e.classList.remove('alert-danger'); + e.classList.add('alert-success'); + e.textContent = 'Neue Körperschaft aufgenommen'; + e.style.display = 'block'; + + // ID setzen für Relationen + $('#ID').val(AnchorID); + + // Relationen-Bereich anzeigen nach Speichern + document.getElementById("new_relation_label").style.display = 'block'; + document.getElementById("newRelationCorporateDismiss").style.display = 'inline'; + showRelations(AnchorID,EntryID,LinkingID); + } else { + e.classList.remove('alert-success'); + e.classList.add('alert-danger'); + e.textContent = dt['message']; + e.style.display = 'block'; + return; + } + + }); + +} + +// Show relations of a corporate record +function showRelations(Anchor,Entry,Linking) +{ + $.get("ajax/getRelations.php", { + anchorID:Anchor, + authType:"Corporate" + }, + function (data, status) { + if (status=="success") + { + $(".new_modal_content").html(data); + // Tabelle nur anzeigen wenn Relationen vorhanden + var relationTable = document.getElementById("relation_table"); + if (relationTable) { + if (data && data.trim().length > 0) { + relationTable.style.display = 'block'; + } else { + relationTable.style.display = 'none'; + } + } + $("#NewCorporateModal").modal("show"); + } + } + ); + +} + + +// create new relation for a corporate record +function CreateNewRelation(AnchorID, relationID, event) { + if (event) { + event.preventDefault(); + event.stopPropagation(); + } + + console.log('🚀 CreateNewRelation:', { AnchorID, relationID }); + + try { + // Validierung + if (!AnchorID || !relationID) { + showError('Fehlende Parameter: AnchorID oder relationID'); + return false; + } + + var relationTypeElement = document.getElementById("new_relationtype"); + + if (!relationTypeElement) { + showError('Relationstyp-Element nicht gefunden'); + return false; + } + + var relationType = relationTypeElement.value; + + if (!relationType || relationType.trim() === '') { + showError('Bitte wählen Sie einen Relationstyp aus'); + return false; + } + + // Loading anzeigen + showLoading(); + + $.ajax({ + url: "ajax/writeNewRelation.php", + type: "POST", + data: { + AnchorID: AnchorID, + relationType: relationType, + relationID: relationID + }, + dataType: "text", + timeout: 10000, + + success: function(response) { + console.log("✅ Success! Response:", response); + + try { + var data = JSON.parse(response); + + if (data.error) { + showError('Serverfehler:
' + data.error); + return; + } + + if (data.success === true) { + showSuccess('Neue Relation erfolgreich aufgenommen!'); + + if (typeof showRelations === 'function') { + setTimeout(function() { + showRelations(AnchorID, 0, 0); + }, 500); + } + + resetForm(); + } else { + showError('Unerwartete Antwort vom Server'); + } + + } catch (parseError) { + console.log("⚠️ Keine JSON-Antwort, prüfe Plain-Text"); + + if (response && response.length > 0 && !isNaN(response.trim())) { + showSuccess('Neue Relation erfolgreich aufgenommen! (ID: ' + response.trim() + ')'); + + if (typeof showRelations === 'function') { + setTimeout(function() { + showRelations(AnchorID, 0, 0); + }, 500); + } + + resetForm(); + } else { + showError('Unerwartete Antwort:
' + + '' + response.substring(0, 200) + ''); + } + } + }, + + error: function(xhr, status, error) { + console.error("❌ AJAX Error:", { status, error, xhr }); + + var errorMessage = 'Fehler beim Speichern der Relation

'; + + if (xhr.status === 0) { + errorMessage += '❌ Keine Verbindung zum Server möglich
'; + errorMessage += 'Prüfen Sie Ihre Internetverbindung'; + } else if (xhr.status === 404) { + errorMessage += '❌ Datei nicht gefunden (404)
'; + errorMessage += 'ajax/writeNewRelation.php existiert nicht'; + } else if (xhr.status === 500) { + errorMessage += '❌ Interner Serverfehler (500)
'; + if (xhr.responseText) { + errorMessage += '
Details anzeigen
' +
+                                      xhr.responseText.substring(0, 500) + '
'; + } + } else if (status === 'timeout') { + errorMessage += '❌ Zeitüberschreitung
'; + errorMessage += 'Der Server hat nicht rechtzeitig geantwortet'; + } else if (status === 'parsererror') { + errorMessage += '❌ Fehler beim Verarbeiten der Server-Antwort
'; + errorMessage += 'Die Server-Antwort war ungültig'; + } else { + errorMessage += '❌ ' + status + ' (' + xhr.status + ')'; + if (error) { + errorMessage += '
' + error + ''; + } + } + + showError(errorMessage); + }, + + complete: function(xhr, status) { + console.log("🏁 Request abgeschlossen:", status); + if (status !== 'success') { + hideLoading(); + } + } + }); + + return false; + + } catch (e) { + hideLoading(); + console.error("💥 Exception:", e); + showError('JavaScript-Fehler:
' + e.message); + return false; + } +} + +function showError(message) { + var errorElement = document.getElementById("errorNewCorporate"); + if (errorElement) { + errorElement.className = 'alert alert-danger'; + errorElement.innerHTML = '' + message.replace(/\n/g, '
'); + errorElement.style.display = 'block'; + + errorElement.scrollIntoView({ behavior: 'smooth', block: 'nearest' }); + + setTimeout(function() { + fadeOutAlert(); + }, 8000); + } +} + +function showSuccess(message) { + var errorElement = document.getElementById("errorNewCorporate"); + if (errorElement) { + errorElement.className = 'alert alert-success'; + errorElement.innerHTML = '' + message; + errorElement.style.display = 'block'; + + errorElement.scrollIntoView({ behavior: 'smooth', block: 'nearest' }); + + setTimeout(function() { + fadeOutAlert(); + }, 3000); + } +} + +function showWarning(message) { + var errorElement = document.getElementById("errorNewCorporate"); + if (errorElement) { + errorElement.className = 'alert alert-warning'; + errorElement.innerHTML = '' + message; + errorElement.style.display = 'block'; + + errorElement.scrollIntoView({ behavior: 'smooth', block: 'nearest' }); + + setTimeout(function() { + fadeOutAlert(); + }, 5000); + } +} + +function showLoading() { + var errorElement = document.getElementById("errorNewCorporate"); + if (errorElement) { + errorElement.className = 'alert alert-info'; + errorElement.innerHTML = '
Laden...
Speichere Relation...
'; + errorElement.style.display = 'block'; + + errorElement.scrollIntoView({ behavior: 'smooth', block: 'nearest' }); + } +} + +function hideLoading() { + var errorElement = document.getElementById("errorNewCorporate"); + if (errorElement) { + errorElement.style.display = 'none'; + } +} + +function hideAlert() { + var errorElement = document.getElementById("errorNewCorporate"); + if (errorElement) { + errorElement.style.display = 'none'; + } +} + +function fadeOutAlert() { + var errorElement = document.getElementById("errorNewCorporate"); + if (errorElement && errorElement.style.display !== 'none') { + $(errorElement).fadeOut(500, function() { + errorElement.style.display = 'none'; + }); + } +} + +function resetForm() { + var relationTextElement = document.getElementById("search_relation"); + if (relationTextElement) { + relationTextElement.value = ''; + } + $('#corporate-anlegen').removeData('record-id'); + $('#corporate-anlegen').removeData('relation-id'); + hideAlert(); +} + + +// Delete relation for a corporate +function DeleteRelation(AnchorID, LinkingID) +{ + if (!confirm('Möchten Sie diese Relation wirklich löschen?')) { + return; + } + + $.post("ajax/deleteRelation.php", { + AnchorID : AnchorID, + LinkingID : LinkingID + }, + function (data, status) { + if (status=="success") + { + var e = document.getElementById("errorNewCorporate"); + e.classList.add('alert-success'); + e.textContent = ' Relation gelöscht'; + e.style.display = 'block'; + + showRelations(AnchorID,0,0) + } + + } + ); +} + +// delete the whole corporate entry +function DeleteTerm(AnchorID) +{ + if (!confirm('Möchten Sie diesen Eintrag wirklich löschen?')) { + return; + } + + $.post("ajax/deleteTerm.php", { + AnchorID: AnchorID[0] + }, + function (data, status) { + var metadata = (typeof data === 'string') ? JSON.parse(data) : data; + + if (status === "success" && metadata.success === true) { + // Tabelle neu laden – Zeile ist serverseitig wirklich gelöscht + $table.bootstrapTable('refresh'); + } else { + // Serverseitiger Fehler: Zeile wieder einblenden und Meldung anzeigen + $table.bootstrapTable('refresh'); + alert('Fehler beim Löschen: ' + (metadata.error || 'Unbekannter Fehler')); + } + } + ); +} + + +// Modify corporate and its relations +function ModifyTerm(AnchorID) +{ + // Name-Feld deaktivieren (darf nicht geändert werden) + document.getElementById("new_term").disabled = true; + + // Andere Felder aktivieren + document.getElementById("new_type").disabled = false; + document.getElementById("new_scopenote").disabled = false; + + $("#ID").val(AnchorID[0]); + + // Relationen-Bereich sofort sichtbar machen bei Bearbeitung! + document.getElementById("new_relation_label").style.display = 'block'; + document.getElementById("relation_table").style.display = 'block'; + + // Buttons konfigurieren + document.getElementById("newCorporateSave").style.display = 'none'; + document.getElementById("newCorporateDismiss").style.display = 'inline'; + document.getElementById("CorporateModifySave").style.display = 'inline'; + document.getElementById("newRelationCorporateDismiss").style.display = 'none'; + + // Alert verstecken + document.getElementById("errorNewCorporate").style.display = 'none'; + + $("#NewCorporateModalHeadline").text("Körperschaft ändern"); + + $.get("/Thesaurus/ajax/getAuthorityDataRaw.php", { + id : AnchorID[0], + authType :"Corporate" + }, + function (data, status) { + console.log('📥 getAuthorityDataRaw status:', status); + console.log('📥 getAuthorityDataRaw data:', data); + if (status == "success") + { + var metadata = (typeof data === 'string') ? JSON.parse(data) : data; + console.log('📥 metadata:', metadata); + dt = (metadata['rows'][0]); + console.log('📥 dt:', dt); + if (dt) { + $("#new_term").val(dt.Text); + $("#new_id").val(dt.ID); + $("#new_descriptor").val(dt.Descriptor); + $("#new_type").val(dt.Type); + $("#new_scopenote").val(dt.Scopenote); + console.log('✅ Felder befüllt mit:', dt.Text); + } else { + console.error('❌ Keine Daten in rows[0]'); + } + } + + } + ).fail(function(xhr, status, error) { + console.error('❌ AJAX Fehler:', status, error); + }); + + $.get("ajax/getRelations.php", { + anchorID:AnchorID[0], + authType: "Corporate" + }, + function (data, status) { + if (status=="success") + { + $(".new_modal_content").html(data); + $("#NewCorporateModal").modal("show"); + + // Autocomplete neu initialisieren nach Modal-Öffnung + setTimeout(function() { + if (typeof searchRelation === 'function') { + console.log('🔄 Autocomplete wird neu initialisiert...'); + searchRelation(); + } + }, 300); + } + } + ); +} + +// rewrite records after updating a corporate entry +function UpdateEntry(authType) +{ + var AnchorID = $("#ID").val(); + var term = $("#new_term").val(); + term = term.trim(); + var type = $("#new_type").val(); + type = type.trim(); + var detailtype = ''; + var classification = ''; + var scopenote = $("#new_scopenote").val(); + scopenote = scopenote.trim(); + + if (term.length == 0) { + var a = document.getElementById("errorNewCorporate"); + a.classList.add('alert-danger'); + a.textContent = 'Bitte Körperschaft eingeben!'; + a.style.display = 'block'; + return; + } + + var a = document.getElementById("errorNewCorporate"); + a.classList.remove('alert-danger'); + a.textContent = ''; + a.style.display = 'none'; + + + + $.post("ajax/UpdateTerm.php", { + AnchorID: AnchorID, + authType: "Corporate", + term: term, + type: type, + detailtype: detailtype, + classification: classification, + scopenote: scopenote + }, + function (data, status) { + if (status=="success") + { + document.getElementById("relation_table").style.display = 'block' + document.getElementById("newCorporateSave").style.display = 'none' + document.getElementById("newCorporateDismiss").style.display = 'inline' + document.getElementById("CorporateModifySave").style.display = 'none' + document.getElementById("newRelationCorporateDismiss").style.display = 'none' + var e = document.getElementById("errorNewCorporate"); + e.classList.add('alert-success'); + e.textContent = ' Körperschaft geändert '; + e.style.display = 'block'; + + $("#NewCorporateModalHeadline").text("Körperschaft ändern"); + $("#NewCorporateModal").modal("show"); + } + } + ); + $table.bootstrapTable('refresh') +} + +function ShowModalDetails(id) { + + $.get("/Thesaurus/ajax/getDetailAuthorityData.php", { + authType : 'Corporate', + offset : 0, + id : id, + sort : 'Anchor.Text', + limit : 1 + }, + function(data, status) { + if (status == "success") { + var metadata = (typeof data === 'string') ? JSON.parse(data) : data; + var row = metadata['rows']; + + if (!row || row.length === 0) { + console.error('❌ Keine Daten gefunden für ID:', id); + return; + } + + //Assign existing values to the modal popup fields + $("#display_term").val(row[0].Text); + $("#display_id").val(row[0].ID); + $("#display_id_show").val(row[0].ID); + $("#display_descriptor").val(row[0].Descriptor); + $("#display_type").val(row[0].Type); + $("#display_scopenote").val(row[0].Scopenote); + + var html = [] + var txt = '' + $.each(row[0].Relations, function (key, value) { + txt = txt +'
'; + txt = txt +'
'; + txt = txt + '
'; + txt = txt + '
'; + txt = txt + '
'; + txt = txt +'
'; + }) + $(".modal_content").html(txt); + $.each(row[0].Relations, function (key, value) { + $("#display_relationtype_" +key).val(value['Relationtype']); + $("#display_textrelation_" +key).val(value['TextRelation']); + $("#display_IDRelation_" +key).val(value['IDRelation']); + }) + $("#display_relations").val(txt); + + // Modal anzeigen + $("#DetailsModal").modal("show"); + + // Semantisches Netz erstellen (nach Modal sichtbar) + setTimeout(function() { + if (typeof createSemanticNetwork === 'function') { + createSemanticNetwork(row[0].Text, row[0].ID, row[0].Relations); + } + }, 300); + } + } + ); +} + + + +function refreshPage() +{ + location.reload(); +} + +function CopyLink(value) +{ + var tempInput = document.createElement("input"); + tempInput.value = value; + document.body.appendChild(tempInput); + tempInput.select(); + document.execCommand("copy"); + document.body.removeChild(tempInput); +} diff --git a/js/PersonScript.js b/js/PersonScript.js new file mode 100644 index 0000000..4a518ba --- /dev/null +++ b/js/PersonScript.js @@ -0,0 +1,629 @@ +/** + * Created by Roland on 2021/07/01. + * Updated for BIBB Layout 2024 + */ +// PERSONS + +// Show blank screen to add new Person +function newPersonShow() { + + var b = document.getElementById("newSubjectSave"); + b.style.display='inline'; + var c = document.getElementById("newSubjectDismiss"); + c.style.display='inline'; + document.getElementById("SubjectModifySave").style.display = 'none'; + document.getElementById("new_term").disabled = false; + document.getElementById("new_scopenote").disabled = false; + + // Relationen-Bereich verstecken bei neuer Person + document.getElementById("new_relation_label").style.display = 'none'; + document.getElementById("newRelationSubjectDismiss").style.display = 'none'; + showRelations(0,0,0); + var a = document.getElementById("relation_table"); + if (a != undefined) { + document.getElementById("relation_table").style.display = 'none'; + } + var e = document.getElementById("errorNewSubject"); + e.style.display = 'none'; + + $("#new_term").val(""); + $("#new_scopenote").val(""); + + $("#NewSubjectModal").modal("show"); +} + +// Insert new person record +function CreateNewEntry(type) { + // get values + + var term = $("#new_term").val(); + term = term.trim(); + var detailtype = ''; + var classification = ''; + var scopenote = $("#new_scopenote").val(); + scopenote = scopenote.trim(); + + if (term.length == 0) { + var a = document.getElementById("errorNewSubject"); + a.classList.add('alert-danger'); + a.textContent = 'Bitte Name eingeben!'; + a.style.display = 'block'; + return; + } + + var a = document.getElementById("errorNewSubject"); + a.classList.remove('alert-danger'); + a.textContent = ''; + a.style.display = 'none'; + + $.post("ajax/newPerson.php", { + term : term, + type : type, + detailtype : detailtype, + classification: classification, + scopenote : scopenote + }, function (data, status) { + + var dt = (typeof data === 'string') ? JSON.parse(data) : data; + var e = document.getElementById("errorNewSubject"); + + if (dt['status'] == 200) + { + var AnchorID = dt["Anchor"]; + var EntryID = dt["Entry"]; + var LinkingID = dt["Linking"]; + + var b = document.getElementById("newSubjectSave"); + b.style.display='none'; + var c = document.getElementById("newSubjectDismiss"); + c.style.display='none'; + document.getElementById("new_term").disabled = true; + document.getElementById("new_scopenote").disabled = true; + $table.bootstrapTable('refresh'); + + e.classList.remove('alert-danger'); + e.classList.add('alert-success'); + e.textContent = 'Neuen Namen aufgenommen'; + e.style.display = 'block'; + + // ID setzen für Relationen + $('#ID').val(AnchorID); + + // Relationen-Bereich anzeigen nach Speichern + document.getElementById("new_relation_label").style.display = 'block'; + document.getElementById("newRelationSubjectDismiss").style.display = 'inline'; + showRelations(AnchorID,EntryID,LinkingID); + } else { + e.classList.remove('alert-success'); + e.classList.add('alert-danger'); + e.textContent = dt['message']; + e.style.display = 'block'; + return; + } + + }); + +} + +// Show relations of a person record +function showRelations(Anchor,Entry,Linking) +{ + $.get("ajax/getRelations.php", { + anchorID:Anchor, + authType:"Person" + }, + function (data, status) { + if (status=="success") + { + $(".new_modal_content").html(data); + // Tabelle nur anzeigen wenn Relationen vorhanden + var relationTable = document.getElementById("relation_table"); + if (relationTable) { + if (data && data.trim().length > 0) { + relationTable.style.display = 'block'; + } else { + relationTable.style.display = 'none'; + } + } + $("#NewSubjectModal").modal("show"); + } + } + ); + +} + + +// create new relation for a person record +function CreateNewRelation(AnchorID, relationID, event) { + if (event) { + event.preventDefault(); + event.stopPropagation(); + } + + console.log('🚀 CreateNewRelation:', { AnchorID, relationID }); + + try { + // Validierung + if (!AnchorID || !relationID) { + showError('Fehlende Parameter: AnchorID oder relationID'); + return false; + } + + var relationTypeElement = document.getElementById("new_relationtype"); + + if (!relationTypeElement) { + showError('Relationstyp-Element nicht gefunden'); + return false; + } + + var relationType = relationTypeElement.value; + + if (!relationType || relationType.trim() === '') { + showError('Bitte wählen Sie einen Relationstyp aus'); + return false; + } + + // Loading anzeigen + showLoading(); + + $.ajax({ + url: "ajax/writeNewRelation.php", + type: "POST", + data: { + AnchorID: AnchorID, + relationType: relationType, + relationID: relationID + }, + dataType: "text", + timeout: 10000, + + success: function(response) { + console.log("✅ Success! Response:", response); + + try { + var data = JSON.parse(response); + + if (data.error) { + showError('Serverfehler:
' + data.error); + return; + } + + if (data.success === true) { + showSuccess('Neue Relation erfolgreich aufgenommen!'); + + if (typeof showRelations === 'function') { + setTimeout(function() { + showRelations(AnchorID, 0, 0); + }, 500); + } + + resetForm(); + } else { + showError('Unerwartete Antwort vom Server'); + } + + } catch (parseError) { + console.log("⚠️ Keine JSON-Antwort, prüfe Plain-Text"); + + if (response && response.length > 0 && !isNaN(response.trim())) { + showSuccess('Neue Relation erfolgreich aufgenommen! (ID: ' + response.trim() + ')'); + + if (typeof showRelations === 'function') { + setTimeout(function() { + showRelations(AnchorID, 0, 0); + }, 500); + } + + resetForm(); + } else { + showError('Unerwartete Antwort:
' + + '' + response.substring(0, 200) + ''); + } + } + }, + + error: function(xhr, status, error) { + console.error("❌ AJAX Error:", { status, error, xhr }); + + var errorMessage = 'Fehler beim Speichern der Relation

'; + + if (xhr.status === 0) { + errorMessage += '❌ Keine Verbindung zum Server möglich
'; + errorMessage += 'Prüfen Sie Ihre Internetverbindung'; + } else if (xhr.status === 404) { + errorMessage += '❌ Datei nicht gefunden (404)
'; + errorMessage += 'ajax/writeNewRelation.php existiert nicht'; + } else if (xhr.status === 500) { + errorMessage += '❌ Interner Serverfehler (500)
'; + if (xhr.responseText) { + errorMessage += '
Details anzeigen
' +
+                                      xhr.responseText.substring(0, 500) + '
'; + } + } else if (status === 'timeout') { + errorMessage += '❌ Zeitüberschreitung
'; + errorMessage += 'Der Server hat nicht rechtzeitig geantwortet'; + } else if (status === 'parsererror') { + errorMessage += '❌ Fehler beim Verarbeiten der Server-Antwort
'; + errorMessage += 'Die Server-Antwort war ungültig'; + } else { + errorMessage += '❌ ' + status + ' (' + xhr.status + ')'; + if (error) { + errorMessage += '
' + error + ''; + } + } + + showError(errorMessage); + }, + + complete: function(xhr, status) { + console.log("🏁 Request abgeschlossen:", status); + if (status !== 'success') { + hideLoading(); + } + } + }); + + return false; + + } catch (e) { + hideLoading(); + console.error("💥 Exception:", e); + showError('JavaScript-Fehler:
' + e.message); + return false; + } +} + +function showError(message) { + var errorElement = document.getElementById("errorNewSubject"); + if (errorElement) { + errorElement.className = 'alert alert-danger'; + errorElement.innerHTML = '' + message.replace(/\n/g, '
'); + errorElement.style.display = 'block'; + + errorElement.scrollIntoView({ behavior: 'smooth', block: 'nearest' }); + + setTimeout(function() { + fadeOutAlert(); + }, 8000); + } +} + +function showSuccess(message) { + var errorElement = document.getElementById("errorNewSubject"); + if (errorElement) { + errorElement.className = 'alert alert-success'; + errorElement.innerHTML = '' + message; + errorElement.style.display = 'block'; + + errorElement.scrollIntoView({ behavior: 'smooth', block: 'nearest' }); + + setTimeout(function() { + fadeOutAlert(); + }, 3000); + } +} + +function showWarning(message) { + var errorElement = document.getElementById("errorNewSubject"); + if (errorElement) { + errorElement.className = 'alert alert-warning'; + errorElement.innerHTML = '' + message; + errorElement.style.display = 'block'; + + errorElement.scrollIntoView({ behavior: 'smooth', block: 'nearest' }); + + setTimeout(function() { + fadeOutAlert(); + }, 5000); + } +} + +function showLoading() { + var errorElement = document.getElementById("errorNewSubject"); + if (errorElement) { + errorElement.className = 'alert alert-info'; + errorElement.innerHTML = '
Laden...
Speichere Relation...
'; + errorElement.style.display = 'block'; + + errorElement.scrollIntoView({ behavior: 'smooth', block: 'nearest' }); + } +} + +function hideLoading() { + var errorElement = document.getElementById("errorNewSubject"); + if (errorElement) { + errorElement.style.display = 'none'; + } +} + +function hideAlert() { + var errorElement = document.getElementById("errorNewSubject"); + if (errorElement) { + errorElement.style.display = 'none'; + } +} + +function fadeOutAlert() { + var errorElement = document.getElementById("errorNewSubject"); + if (errorElement && errorElement.style.display !== 'none') { + $(errorElement).fadeOut(500, function() { + errorElement.style.display = 'none'; + }); + } +} + +function resetForm() { + var relationTextElement = document.getElementById("search_relation"); + if (relationTextElement) { + relationTextElement.value = ''; + } + $('#subject-anlegen').removeData('record-id'); + $('#subject-anlegen').removeData('relation-id'); + hideAlert(); +} + + +// Delete relation for a person +function DeleteRelation(AnchorID, LinkingID) +{ + if (!confirm('Möchten Sie diese Relation wirklich löschen?')) { + return; + } + + $.post("ajax/deleteRelation.php", { + AnchorID : AnchorID, + LinkingID : LinkingID + }, + function (data, status) { + if (status=="success") + { + var e = document.getElementById("errorNewSubject"); + e.classList.add('alert-success'); + e.textContent = ' Relation gelöscht'; + e.style.display = 'block'; + + showRelations(AnchorID,0,0) + } + + } + ); +} + +// delete the whole person entry +function DeleteTerm(AnchorID) +{ + if (!confirm('Möchten Sie diesen Eintrag wirklich löschen?')) { + return; + } + + $.post("ajax/deleteTerm.php", { + AnchorID: AnchorID[0] + }, + function (data, status) { + var metadata = (typeof data === 'string') ? JSON.parse(data) : data; + + if (status === "success" && metadata.success === true) { + // Tabelle neu laden – Zeile ist serverseitig wirklich gelöscht + $table.bootstrapTable('refresh'); + } else { + // Serverseitiger Fehler: Zeile wieder einblenden und Meldung anzeigen + $table.bootstrapTable('refresh'); + alert('Fehler beim Löschen: ' + (metadata.error || 'Unbekannter Fehler')); + } + } + ); +} + + +// Modify person and its relations +function ModifyPerson(AnchorID) +{ + // Name-Feld deaktivieren (darf nicht geändert werden) + document.getElementById("new_term").disabled = true; + + // Andere Felder aktivieren + document.getElementById("new_scopenote").disabled = false; + + $("#ID").val(AnchorID[0]); + + // Relationen-Bereich sofort sichtbar machen bei Bearbeitung! + document.getElementById("new_relation_label").style.display = 'block'; + document.getElementById("relation_table").style.display = 'block'; + + // Buttons konfigurieren + document.getElementById("newSubjectSave").style.display = 'none'; + document.getElementById("newSubjectDismiss").style.display = 'inline'; + document.getElementById("SubjectModifySave").style.display = 'inline'; + document.getElementById("newRelationSubjectDismiss").style.display = 'none'; + + // Alert verstecken + document.getElementById("errorNewSubject").style.display = 'none'; + + $("#NewSubjectModalHeadline").text("Person ändern"); + + $.get("/Thesaurus/ajax/getAuthorityDataRaw.php", { + id : AnchorID[0], + authType :"Person" + }, + function (data, status) { + console.log('📥 getAuthorityDataRaw status:', status); + console.log('📥 getAuthorityDataRaw data:', data); + if (status == "success") + { + var metadata = (typeof data === 'string') ? JSON.parse(data) : data; + console.log('📥 metadata:', metadata); + dt = (metadata['rows'][0]); + console.log('📥 dt:', dt); + if (dt) { + $("#new_term").val(dt.Text); + $("#new_id").val(dt.ID); + $("#new_descriptor").val(dt.Descriptor); + $("#new_scopenote").val(dt.Scopenote); + console.log('✅ Felder befüllt mit:', dt.Text); + } else { + console.error('❌ Keine Daten in rows[0]'); + } + } + + } + ).fail(function(xhr, status, error) { + console.error('❌ AJAX Fehler:', status, error); + }); + + $.get("ajax/getRelations.php", { + anchorID:AnchorID[0], + authType: "Person" + }, + function (data, status) { + if (status=="success") + { + $(".new_modal_content").html(data); + $("#NewSubjectModal").modal("show"); + + // Autocomplete neu initialisieren nach Modal-Öffnung + setTimeout(function() { + if (typeof searchRelation === 'function') { + console.log('🔄 Autocomplete wird neu initialisiert...'); + searchRelation(); + } + }, 300); + } + } + ); +} + +// rewrite records after updating a person entry +function UpdateEntry(authType) +{ + var AnchorID = $("#ID").val(); + var term = $("#new_term").val(); + term = term.trim(); + var type = 'Person'; + type = type.trim(); + var detailtype = ''; + detailtype = detailtype.trim(); + var classification = '' + classification = classification.trim(); + var scopenote = $("#new_scopenote").val(); + scopenote = scopenote.trim(); + + if (term.length == 0) { + var a = document.getElementById("errorNewSubject"); + a.classList.add('alert-danger'); + a.textContent = 'Bitte Name eingeben!'; + a.style.display = 'block'; + return; + } + + var a = document.getElementById("errorNewSubject"); + a.classList.remove('alert-danger'); + a.textContent = ''; + a.style.display = 'none'; + + + + $.post("ajax/UpdateTerm.php", { + AnchorID: AnchorID, + authType: "Person", + term: term, + type: type, + detailtype: detailtype, + classification: classification, + scopenote: scopenote + }, + function (data, status) { + if (status=="success") + { + document.getElementById("relation_table").style.display = 'block' + document.getElementById("newSubjectSave").style.display = 'none' + document.getElementById("newSubjectDismiss").style.display = 'inline' + document.getElementById("SubjectModifySave").style.display = 'none' + document.getElementById("newRelationSubjectDismiss").style.display = 'none' + var e = document.getElementById("errorNewSubject"); + e.classList.add('alert-success'); + e.textContent = ' Personenname geändert '; + e.style.display = 'block'; + + $("#NewSubjectModalHeadline").text("Name ändern"); + $("#NewSubjectModal").modal("show"); + } + } + ); + $table.bootstrapTable('refresh') +} + +function ShowModalDetails(id) { + + $.get("/Thesaurus/ajax/getDetailAuthorityData.php", { + authType : 'Person', + offset : 0, + id : id, + sort : 'Anchor.Text', + limit : 1 + }, + function(data, status) { + if (status == "success") { + var metadata = (typeof data === 'string') ? JSON.parse(data) : data; + var row = metadata['rows']; + + if (!row || row.length === 0) { + console.error('❌ Keine Daten gefunden für ID:', id); + return; + } + + //Assign existing values to the modal popup fields + $("#display_term").val(row[0].Text); + $("#display_id").val(row[0].ID); + $("#display_id_show").val(row[0].ID); + $("#display_descriptor").val(row[0].Descriptor); + $("#display_type").val(row[0].Type); + $("#display_scopenote").val(row[0].Scopenote); + + var html = [] + var txt = '' + $.each(row[0].Relations, function (key, value) { + txt = txt +'
'; + txt = txt +'
'; + txt = txt + '
'; + txt = txt + '
'; + txt = txt + '
'; + txt = txt +'
'; + }) + $(".modal_content").html(txt); + $.each(row[0].Relations, function (key, value) { + $("#display_relationtype_" +key).val(value['Relationtype']); + $("#display_textrelation_" +key).val(value['TextRelation']); + $("#display_IDRelation_" +key).val(value['IDRelation']); + }) + $("#display_relations").val(txt); + + // Modal anzeigen + $("#DetailsModal").modal("show"); + + // Semantisches Netz erstellen (nach Modal sichtbar) + setTimeout(function() { + if (typeof createSemanticNetwork === 'function') { + createSemanticNetwork(row[0].Text, row[0].ID, row[0].Relations); + } + }, 300); + } + } + ); +} + + + +function refreshPage() +{ + location.reload(); +} + +function CopyLink(value) +{ + var tempInput = document.createElement("input"); + tempInput.value = value; + document.body.appendChild(tempInput); + tempInput.select(); + document.execCommand("copy"); + document.body.removeChild(tempInput); +} diff --git a/js/PublisherScript.js b/js/PublisherScript.js new file mode 100644 index 0000000..3329f88 --- /dev/null +++ b/js/PublisherScript.js @@ -0,0 +1,634 @@ +/** + * Created by Roland on 2021/07/01. + * Updated for BIBB Layout 2024 + */ +// PUBLISHERS + +// Show blank screen to add new Publisher +function newPublishersShow() { + + var b = document.getElementById("newPublisherSave"); + b.style.display='inline'; + var c = document.getElementById("newPublisherDismiss"); + c.style.display='inline'; + document.getElementById("PublisherModifySave").style.display = 'none'; + document.getElementById("new_term").disabled = false; + document.getElementById("new_type").disabled = false; + document.getElementById("new_scopenote").disabled = false; + + // Relationen-Bereich verstecken bei neuem Verlag + document.getElementById("new_relation_label").style.display = 'none'; + document.getElementById("newRelationPublisherDismiss").style.display = 'none'; + showRelations(0,0,0); + var a = document.getElementById("relation_table"); + if (a != undefined) { + document.getElementById("relation_table").style.display = 'none'; + } + var e = document.getElementById("errorNewPublisher"); + e.style.display = 'none'; + + $("#new_term").val(""); + $("#new_scopenote").val(""); + + $("#NewPublisherModal").modal("show"); +} + +// Insert new publisher record +function CreateNewEntry(type) { + // get values + + var term = $("#new_term").val(); + term = term.trim(); + var type = $("#new_type").val(); + type = type.trim(); + var detailtype = ""; + var classification = ""; + var scopenote = $("#new_scopenote").val(); + scopenote = scopenote.trim(); + + if (term.length == 0) { + var a = document.getElementById("errorNewPublisher"); + a.classList.add('alert-danger'); + a.textContent = 'Bitte Verlag eingeben!'; + a.style.display = 'block'; + return; + } + + var a = document.getElementById("errorNewPublisher"); + a.classList.remove('alert-danger'); + a.textContent = ''; + a.style.display = 'none'; + + $.post("ajax/newPublisher.php", { + term : term, + type : type, + detailtype : detailtype, + classification: classification, + scopenote : scopenote + }, function (data, status) { + + var dt = (typeof data === 'string') ? JSON.parse(data) : data; + var e = document.getElementById("errorNewPublisher"); + + if (dt['status'] == 200) + { + var AnchorID = dt["Anchor"]; + var EntryID = dt["Entry"]; + var LinkingID = dt["Linking"]; + + var b = document.getElementById("newPublisherSave"); + b.style.display='none'; + var c = document.getElementById("newPublisherDismiss"); + c.style.display='none'; + document.getElementById("new_term").disabled = true; + document.getElementById("new_type").disabled = true; + document.getElementById("new_scopenote").disabled = true; + $table.bootstrapTable('refresh'); + + e.classList.remove('alert-danger'); + e.classList.add('alert-success'); + e.textContent = 'Neuen Verlag aufgenommen'; + e.style.display = 'block'; + + // ID setzen für Relationen + $('#ID').val(AnchorID); + + // Relationen-Bereich anzeigen nach Speichern + document.getElementById("new_relation_label").style.display = 'block'; + document.getElementById("newRelationPublisherDismiss").style.display = 'inline'; + showRelations(AnchorID,EntryID,LinkingID); + } else { + e.classList.remove('alert-success'); + e.classList.add('alert-danger'); + e.textContent = dt['message']; + e.style.display = 'block'; + return; + } + + }); + +} + +// Show relations of a publisher record +function showRelations(Anchor,Entry,Linking) +{ + $.get("ajax/getRelations.php", { + anchorID:Anchor, + authType:"Publisher" + }, + function (data, status) { + if (status=="success") + { + $(".new_modal_content").html(data); + // Tabelle nur anzeigen wenn Relationen vorhanden + var relationTable = document.getElementById("relation_table"); + if (relationTable) { + if (data && data.trim().length > 0) { + relationTable.style.display = 'block'; + } else { + relationTable.style.display = 'none'; + } + } + $("#NewPublisherModal").modal("show"); + } + } + ); + +} + + +// create new relation for a publisher record +function CreateNewRelation(AnchorID, relationID, event) { + if (event) { + event.preventDefault(); + event.stopPropagation(); + } + + console.log('🚀 CreateNewRelation:', { AnchorID, relationID }); + + try { + // Validierung + if (!AnchorID || !relationID) { + showError('Fehlende Parameter: AnchorID oder relationID'); + return false; + } + + var relationTypeElement = document.getElementById("new_relationtype"); + + if (!relationTypeElement) { + showError('Relationstyp-Element nicht gefunden'); + return false; + } + + var relationType = relationTypeElement.value; + + if (!relationType || relationType.trim() === '') { + showError('Bitte wählen Sie einen Relationstyp aus'); + return false; + } + + // Loading anzeigen + showLoading(); + + $.ajax({ + url: "ajax/writeNewRelation.php", + type: "POST", + data: { + AnchorID: AnchorID, + relationType: relationType, + relationID: relationID + }, + dataType: "text", + timeout: 10000, + + success: function(response) { + console.log("✅ Success! Response:", response); + + try { + var data = JSON.parse(response); + + if (data.error) { + showError('Serverfehler:
' + data.error); + return; + } + + if (data.success === true) { + showSuccess('Neue Relation erfolgreich aufgenommen!'); + + if (typeof showRelations === 'function') { + setTimeout(function() { + showRelations(AnchorID, 0, 0); + }, 500); + } + + resetForm(); + } else { + showError('Unerwartete Antwort vom Server'); + } + + } catch (parseError) { + console.log("⚠️ Keine JSON-Antwort, prüfe Plain-Text"); + + if (response && response.length > 0 && !isNaN(response.trim())) { + showSuccess('Neue Relation erfolgreich aufgenommen! (ID: ' + response.trim() + ')'); + + if (typeof showRelations === 'function') { + setTimeout(function() { + showRelations(AnchorID, 0, 0); + }, 500); + } + + resetForm(); + } else { + showError('Unerwartete Antwort:
' + + '' + response.substring(0, 200) + ''); + } + } + }, + + error: function(xhr, status, error) { + console.error("❌ AJAX Error:", { status, error, xhr }); + + var errorMessage = 'Fehler beim Speichern der Relation

'; + + if (xhr.status === 0) { + errorMessage += '❌ Keine Verbindung zum Server möglich
'; + errorMessage += 'Prüfen Sie Ihre Internetverbindung'; + } else if (xhr.status === 404) { + errorMessage += '❌ Datei nicht gefunden (404)
'; + errorMessage += 'ajax/writeNewRelation.php existiert nicht'; + } else if (xhr.status === 500) { + errorMessage += '❌ Interner Serverfehler (500)
'; + if (xhr.responseText) { + errorMessage += '
Details anzeigen
' +
+                                      xhr.responseText.substring(0, 500) + '
'; + } + } else if (status === 'timeout') { + errorMessage += '❌ Zeitüberschreitung
'; + errorMessage += 'Der Server hat nicht rechtzeitig geantwortet'; + } else if (status === 'parsererror') { + errorMessage += '❌ Fehler beim Verarbeiten der Server-Antwort
'; + errorMessage += 'Die Server-Antwort war ungültig'; + } else { + errorMessage += '❌ ' + status + ' (' + xhr.status + ')'; + if (error) { + errorMessage += '
' + error + ''; + } + } + + showError(errorMessage); + }, + + complete: function(xhr, status) { + console.log("🏁 Request abgeschlossen:", status); + if (status !== 'success') { + hideLoading(); + } + } + }); + + return false; + + } catch (e) { + hideLoading(); + console.error("💥 Exception:", e); + showError('JavaScript-Fehler:
' + e.message); + return false; + } +} + +function showError(message) { + var errorElement = document.getElementById("errorNewPublisher"); + if (errorElement) { + errorElement.className = 'alert alert-danger'; + errorElement.innerHTML = '' + message.replace(/\n/g, '
'); + errorElement.style.display = 'block'; + + errorElement.scrollIntoView({ behavior: 'smooth', block: 'nearest' }); + + setTimeout(function() { + fadeOutAlert(); + }, 8000); + } +} + +function showSuccess(message) { + var errorElement = document.getElementById("errorNewPublisher"); + if (errorElement) { + errorElement.className = 'alert alert-success'; + errorElement.innerHTML = '' + message; + errorElement.style.display = 'block'; + + errorElement.scrollIntoView({ behavior: 'smooth', block: 'nearest' }); + + setTimeout(function() { + fadeOutAlert(); + }, 3000); + } +} + +function showWarning(message) { + var errorElement = document.getElementById("errorNewPublisher"); + if (errorElement) { + errorElement.className = 'alert alert-warning'; + errorElement.innerHTML = '' + message; + errorElement.style.display = 'block'; + + errorElement.scrollIntoView({ behavior: 'smooth', block: 'nearest' }); + + setTimeout(function() { + fadeOutAlert(); + }, 5000); + } +} + +function showLoading() { + var errorElement = document.getElementById("errorNewPublisher"); + if (errorElement) { + errorElement.className = 'alert alert-info'; + errorElement.innerHTML = '
Laden...
Speichere Relation...
'; + errorElement.style.display = 'block'; + + errorElement.scrollIntoView({ behavior: 'smooth', block: 'nearest' }); + } +} + +function hideLoading() { + var errorElement = document.getElementById("errorNewPublisher"); + if (errorElement) { + errorElement.style.display = 'none'; + } +} + +function hideAlert() { + var errorElement = document.getElementById("errorNewPublisher"); + if (errorElement) { + errorElement.style.display = 'none'; + } +} + +function fadeOutAlert() { + var errorElement = document.getElementById("errorNewPublisher"); + if (errorElement && errorElement.style.display !== 'none') { + $(errorElement).fadeOut(500, function() { + errorElement.style.display = 'none'; + }); + } +} + +function resetForm() { + var relationTextElement = document.getElementById("search_relation"); + if (relationTextElement) { + relationTextElement.value = ''; + } + $('#publisher-anlegen').removeData('record-id'); + $('#publisher-anlegen').removeData('relation-id'); + hideAlert(); +} + + +// Delete relation for a publisher +function DeleteRelation(AnchorID, LinkingID) +{ + if (!confirm('Möchten Sie diese Relation wirklich löschen?')) { + return; + } + + $.post("ajax/deleteRelation.php", { + AnchorID : AnchorID, + LinkingID : LinkingID + }, + function (data, status) { + if (status=="success") + { + var e = document.getElementById("errorNewPublisher"); + e.classList.add('alert-success'); + e.textContent = ' Relation gelöscht'; + e.style.display = 'block'; + + showRelations(AnchorID,0,0) + } + + } + ); +} + +// delete the whole publisher entry +function DeleteTerm(AnchorID) +{ + if (!confirm('Möchten Sie diesen Eintrag wirklich löschen?')) { + return; + } + + $.post("ajax/deleteTerm.php", { + AnchorID: AnchorID[0] + }, + function (data, status) { + var metadata = (typeof data === 'string') ? JSON.parse(data) : data; + + if (status === "success" && metadata.success === true) { + // Tabelle neu laden – Zeile ist serverseitig wirklich gelöscht + $table.bootstrapTable('refresh'); + } else { + // Serverseitiger Fehler: Zeile wieder einblenden und Meldung anzeigen + $table.bootstrapTable('refresh'); + alert('Fehler beim Löschen: ' + (metadata.error || 'Unbekannter Fehler')); + } + } + ); +} + + +// Modify publisher and its relations +function ModifyTerm(AnchorID) +{ + // Name-Feld deaktivieren (darf nicht geändert werden) + document.getElementById("new_term").disabled = true; + + // Andere Felder aktivieren + document.getElementById("new_type").disabled = false; + document.getElementById("new_scopenote").disabled = false; + + $("#ID").val(AnchorID[0]); + + // Relationen-Bereich sofort sichtbar machen bei Bearbeitung! + document.getElementById("new_relation_label").style.display = 'block'; + document.getElementById("relation_table").style.display = 'block'; + + // Buttons konfigurieren + document.getElementById("newPublisherSave").style.display = 'none'; + document.getElementById("newPublisherDismiss").style.display = 'inline'; + document.getElementById("PublisherModifySave").style.display = 'inline'; + document.getElementById("newRelationPublisherDismiss").style.display = 'none'; + + // Alert verstecken + document.getElementById("errorNewPublisher").style.display = 'none'; + + $("#NewPublisherModalHeadline").text("Verlag ändern"); + + $.get("/Thesaurus/ajax/getAuthorityDataRaw.php", { + id : AnchorID[0], + authType :"Publisher" + }, + function (data, status) { + console.log('📥 getAuthorityDataRaw status:', status); + console.log('📥 getAuthorityDataRaw data:', data); + if (status == "success") + { + var metadata = (typeof data === 'string') ? JSON.parse(data) : data; + console.log('📥 metadata:', metadata); + dt = (metadata['rows'][0]); + console.log('📥 dt:', dt); + if (dt) { + $("#new_term").val(dt.Text); + $("#new_id").val(dt.ID); + $("#new_descriptor").val(dt.Descriptor); + $("#new_type").val(dt.Type); + $("#new_scopenote").val(dt.Scopenote); + console.log('✅ Felder befüllt mit:', dt.Text); + } else { + console.error('❌ Keine Daten in rows[0]'); + } + } + + } + ).fail(function(xhr, status, error) { + console.error('❌ AJAX Fehler:', status, error); + }); + + $.get("ajax/getRelations.php", { + anchorID:AnchorID[0], + authType: "Publisher" + }, + function (data, status) { + if (status=="success") + { + $(".new_modal_content").html(data); + $("#NewPublisherModal").modal("show"); + + // Autocomplete neu initialisieren nach Modal-Öffnung + setTimeout(function() { + if (typeof searchRelation === 'function') { + console.log('🔄 Autocomplete wird neu initialisiert...'); + searchRelation(); + } + }, 300); + } + } + ); +} + +// rewrite records after updating a publisher entry +function UpdateEntry(authType) +{ + var AnchorID = $("#ID").val(); + var term = $("#new_term").val(); + term = term.trim(); + var type = $("#new_type").val(); + type = type.trim(); + var detailtype = ''; + var classification = ''; + var scopenote = $("#new_scopenote").val(); + scopenote = scopenote.trim(); + + if (term.length == 0) { + var a = document.getElementById("errorNewPublisher"); + a.classList.add('alert-danger'); + a.textContent = 'Bitte Verlag eingeben!'; + a.style.display = 'block'; + return; + } + + var a = document.getElementById("errorNewPublisher"); + a.classList.remove('alert-danger'); + a.textContent = ''; + a.style.display = 'none'; + + + + $.post("ajax/UpdateTerm.php", { + AnchorID: AnchorID, + authType: "Publisher", + term: term, + type: type, + detailtype: detailtype, + classification: classification, + scopenote: scopenote + }, + function (data, status) { + if (status=="success") + { + document.getElementById("relation_table").style.display = 'block' + document.getElementById("newPublisherSave").style.display = 'none' + document.getElementById("newPublisherDismiss").style.display = 'inline' + document.getElementById("PublisherModifySave").style.display = 'none' + document.getElementById("newRelationPublisherDismiss").style.display = 'none' + var e = document.getElementById("errorNewPublisher"); + e.classList.add('alert-success'); + e.textContent = ' Verlag geändert '; + e.style.display = 'block'; + + $("#NewPublisherModalHeadline").text("Verlag ändern"); + $("#NewPublisherModal").modal("show"); + } + } + ); + $table.bootstrapTable('refresh') +} + +function ShowModalDetails(id) { + + $.get("/Thesaurus/ajax/getDetailAuthorityData.php", { + authType : 'Publisher', + offset : 0, + id : id, + sort : 'Anchor.Text', + limit : 1 + }, + function(data, status) { + if (status == "success") { + var metadata = (typeof data === 'string') ? JSON.parse(data) : data; + var row = metadata['rows']; + + if (!row || row.length === 0) { + console.error('❌ Keine Daten gefunden für ID:', id); + return; + } + + //Assign existing values to the modal popup fields + $("#display_term").val(row[0].Text); + $("#display_id").val(row[0].ID); + $("#display_id_show").val(row[0].ID); + $("#display_descriptor").val(row[0].Descriptor); + $("#display_type").val(row[0].Type); + $("#display_scopenote").val(row[0].Scopenote); + + var html = [] + var txt = '' + $.each(row[0].Relations, function (key, value) { + txt = txt +'
'; + txt = txt +'
'; + txt = txt + '
'; + txt = txt + '
'; + txt = txt + '
'; + txt = txt +'
'; + }) + $(".modal_content").html(txt); + $.each(row[0].Relations, function (key, value) { + $("#display_relationtype_" +key).val(value['Relationtype']); + $("#display_textrelation_" +key).val(value['TextRelation']); + $("#display_IDRelation_" +key).val(value['IDRelation']); + }) + $("#display_relations").val(txt); + + // Modal anzeigen + $("#DetailsModal").modal("show"); + + // Semantisches Netz erstellen (nach Modal sichtbar) + setTimeout(function() { + if (typeof createSemanticNetwork === 'function') { + createSemanticNetwork(row[0].Text, row[0].ID, row[0].Relations); + } + }, 300); + } + } + ); +} + + + +function refreshPage() +{ + location.reload(); +} + +function CopyLink(value) +{ + var tempInput = document.createElement("input"); + var words = value.split('('); + tempInput.value = words[0]; + document.body.appendChild(tempInput); + tempInput.select(); + document.execCommand("copy"); + document.body.removeChild(tempInput); +} diff --git a/js/SubjectScript.js b/js/SubjectScript.js new file mode 100644 index 0000000..2f3599e --- /dev/null +++ b/js/SubjectScript.js @@ -0,0 +1,882 @@ +/** + * Created by Roland on 2021/07/01. + */ +// SUBJECTS + +// Show blank screen to add new Subject +function newSubjectShow() { + + var b = document.getElementById("newSubjectSave"); + b.style.display='inline'; + var c = document.getElementById("newSubjectDismiss"); + c.style.display='inline'; + document.getElementById("SubjectModifySave").style.display = 'none' + document.getElementById("new_term").disabled = false; + document.getElementById("new_type").disabled = false; + document.getElementById("new_detailtype").disabled = false; + document.getElementById("new_classification").disabled = false; + document.getElementById("new_scopenote").disabled = false; + + // Relationen-Bereich verstecken bei neuem Schlagwort + document.getElementById("new_relation_label").style.display = 'none'; + document.getElementById("newRelationSubjectDismiss").style.display = 'none'; + + // Synonyme-Bereich verstecken bei neuem Schlagwort + var synSection = document.getElementById("synonyms_section"); + if (synSection) { + synSection.style.display = 'none'; + var tbody = document.getElementById("synonyms_body"); + if (tbody) tbody.innerHTML = ''; + } + + showRelations(0,0,0); + var a = document.getElementById("relation_table"); + if (a != undefined) { + document.getElementById("relation_table").style.display = 'none'; + } + var e = document.getElementById("errorNewSubject"); + e.style.display = 'none'; + + $("#new_term").val(""); + $("#new_classification").val(""); + $("#new_scopenote").val(""); + + $("#NewSubjectModal").modal("show"); +} + +// Insert new subject record +function CreateNewEntry(type) { + // get values + + var term = $("#new_term").val(); + term = term.trim(); + var type = $("#new_type").val(); + type = type.trim(); + var detailtype = $("#new_detailtype").val(); + detailtype = detailtype.trim(); + var classification = $("#new_classification").val(); + classification = classification.trim(); + var scopenote = $("#new_scopenote").val(); + scopenote = scopenote.trim(); + + if (term.length == 0) { + var a = document.getElementById("errorNewSubject"); + a.classList.add('alert-danger'); + a.textContent = 'Bitte Schlagwort eingeben!'; + a.style.display = 'block'; + return; + } + + var a = document.getElementById("errorNewSubject"); + a.classList.remove('alert-danger'); + a.textContent = ''; + a.style.display = 'none'; + + $.post("ajax/newSubject.php", { + term : term, + type : type, + detailtype : detailtype, + classification: classification, + scopenote : scopenote + }, function (data, status) { + + var dt = (typeof data === 'string') ? JSON.parse(data) : data; + var e = document.getElementById("errorNewSubject"); + + if (dt['status'] == 200) + { + var AnchorID = dt["Anchor"]; + var EntryID = dt["Entry"]; + var LinkingID = dt["Linking"]; + + // alert ("ANCHOR: " +AnchorID); + var b = document.getElementById("newSubjectSave"); + b.style.display='none'; + var c = document.getElementById("newSubjectDismiss"); + c.style.display='none'; + document.getElementById("new_term").disabled = true; + document.getElementById("new_type").disabled = true; + document.getElementById("new_detailtype").disabled = true; + document.getElementById("new_classification").disabled = true; + document.getElementById("new_scopenote").disabled = true; + $table.bootstrapTable('refresh'); + + e.classList.remove('alert-danger'); + e.classList.add('alert-success'); + e.textContent = 'Neues Schlagwort aufgenommen'; + e.style.display = 'block'; + + // ID setzen für Relationen + $('#ID').val(AnchorID); + + // Relationen-Bereich anzeigen nach Speichern + document.getElementById("new_relation_label").style.display = 'block'; + document.getElementById("newRelationSubjectDismiss").style.display = 'inline'; + showRelations(AnchorID,EntryID,LinkingID); + } else { + e.classList.remove('alert-success'); + e.classList.add('alert-danger'); + e.textContent = dt['message']; + e.style.display = 'block'; + return; + } + + }); + +} + +// Show relations of a subect record +function showRelations(Anchor,Entry,Linking) +{ + // alert ("showRelations AnchorID : " + Anchor); + $.get("ajax/getRelations.php", { + anchorID:Anchor, + authType: "Subject" + }, + function (data, status) { + if (status=="success") + { + $(".new_modal_content").html(data); + // Tabelle nur anzeigen wenn Relationen vorhanden + var relationTable = document.getElementById("relation_table"); + if (relationTable) { + if (data && data.trim().length > 0) { + relationTable.style.display = 'block'; + } else { + relationTable.style.display = 'none'; + } + } + $("#NewSubjectModal").modal("show"); + } + } + ); + +} + + +// create new relation for a subject record + +function CreateNewRelation(AnchorID, relationID, event) { + if (event) { + event.preventDefault(); + event.stopPropagation(); + } + + console.log('🚀 CreateNewRelation:', { AnchorID, relationID }); + + try { + // Validierung + if (!AnchorID || !relationID) { + showError('Fehlende Parameter: AnchorID oder relationID'); + return false; + } + + var relationTypeElement = document.getElementById("new_relationtype"); + + if (!relationTypeElement) { + showError('Relationstyp-Element nicht gefunden'); + return false; + } + + var relationType = relationTypeElement.value; + + if (!relationType || relationType.trim() === '') { + showError('Bitte wählen Sie einen Relationstyp aus'); + return false; + } + + // Loading anzeigen + showLoading(); + + $.ajax({ + url: "ajax/writeNewRelation.php", + type: "POST", + data: { + AnchorID: AnchorID, + relationType: relationType, + relationID: relationID + }, + dataType: "text", + timeout: 10000, + + success: function(response) { + console.log("✅ Success! Response:", response); + + try { + var data = JSON.parse(response); + + if (data.error) { + showError('Serverfehler:
' + data.error); + return; + } + + if (data.success === true) { + showSuccess('Neue Relation erfolgreich aufgenommen!'); + + // Relations neu laden nach kurzer Verzögerung + if (typeof showRelations === 'function') { + setTimeout(function() { + showRelations(AnchorID, 0, 0); + }, 500); + } + + resetForm(); + } else { + showError('Unerwartete Antwort vom Server'); + } + + } catch (parseError) { + console.log("⚠️ Keine JSON-Antwort, prüfe Plain-Text"); + + // Fallback für alte API (nur ID zurück) + if (response && response.length > 0 && !isNaN(response.trim())) { + showSuccess('Neue Relation erfolgreich aufgenommen! (ID: ' + response.trim() + ')'); + + if (typeof showRelations === 'function') { + setTimeout(function() { + showRelations(AnchorID, 0, 0); + }, 500); + } + + resetForm(); + } else { + showError('Unerwartete Antwort:
' + + '' + response.substring(0, 200) + ''); + } + } + }, + + error: function(xhr, status, error) { + console.error("❌ AJAX Error:", { status, error, xhr }); + + var errorMessage = 'Fehler beim Speichern der Relation

'; + + if (xhr.status === 0) { + errorMessage += '❌ Keine Verbindung zum Server möglich
'; + errorMessage += 'Prüfen Sie Ihre Internetverbindung'; + } else if (xhr.status === 404) { + errorMessage += '❌ Datei nicht gefunden (404)
'; + errorMessage += 'ajax/writeNewRelation.php existiert nicht'; + } else if (xhr.status === 500) { + errorMessage += '❌ Interner Serverfehler (500)
'; + if (xhr.responseText) { + errorMessage += '
Details anzeigen
' +
+                                      xhr.responseText.substring(0, 500) + '
'; + } + } else if (status === 'timeout') { + errorMessage += '❌ Zeitüberschreitung
'; + errorMessage += 'Der Server hat nicht rechtzeitig geantwortet'; + } else if (status === 'parsererror') { + errorMessage += '❌ Fehler beim Verarbeiten der Server-Antwort
'; + errorMessage += 'Die Server-Antwort war ungültig'; + } else { + errorMessage += '❌ ' + status + ' (' + xhr.status + ')'; + if (error) { + errorMessage += '
' + error + ''; + } + } + + showError(errorMessage); + }, + + complete: function(xhr, status) { + console.log("🏁 Request abgeschlossen:", status); + // Loading nur ausblenden wenn kein Erfolg (Success zeigt eigene Meldung) + if (status !== 'success') { + hideLoading(); + } + } + }); + + return false; + + } catch (e) { + hideLoading(); + console.error("💥 Exception:", e); + showError('JavaScript-Fehler:
' + e.message); + return false; + } +} + +// Neue Funktion für Synonyme +function CreateNewSynonym(text1, text2, anchorID, event) { + if (event) { + event.preventDefault(); + event.stopPropagation(); + } + + console.log('🔗 CreateNewSynonym:', { text1, text2 }); + + try { + // Validierung + if (!text1 || !text2) { + showError('Fehlende Parameter: text1 oder text2'); + return false; + } + + if (text1.trim() === text2.trim()) { + showError('Ein Begriff kann nicht sein eigenes Synonym sein'); + return false; + } + + // Loading anzeigen + showLoading(); + + $.ajax({ + url: "/Thesaurus/ajax/saveSynonym.php", + type: "POST", + data: { + text1: text1, + text2: text2, + action: 'add' + }, + dataType: "json", + timeout: 10000, + + success: function(response) { + console.log("✅ Synonym Success! Response:", response); + + if (response.error) { + showError('Fehler:
' + response.error); + return; + } + + if (response.success === true) { + showSuccess('Synonym erfolgreich hinzugefügt!'); + + // Synonyme neu laden + var termText = $('#new_term').val(); + if (termText) { + loadSynonyms(termText); + } + + // Relations neu laden nach kurzer Verzögerung + if (typeof showRelations === 'function' && anchorID) { + setTimeout(function() { + showRelations(anchorID, 0, 0); + }, 500); + } + + resetForm(); + } else { + showError('Unerwartete Antwort vom Server'); + } + }, + + error: function(xhr, status, error) { + console.error("❌ AJAX Error:", { status, error, xhr }); + + var errorMessage = 'Fehler beim Speichern des Synonyms

'; + + if (xhr.status === 0) { + errorMessage += '❌ Keine Verbindung zum Server möglich'; + } else if (xhr.status === 404) { + errorMessage += '❌ Datei nicht gefunden (404)
'; + errorMessage += 'ajax/saveSynonym.php existiert nicht'; + } else if (xhr.status === 500) { + errorMessage += '❌ Interner Serverfehler (500)'; + } else { + errorMessage += '❌ ' + status + ' (' + xhr.status + ')'; + } + + showError(errorMessage); + }, + + complete: function() { + hideLoading(); + } + }); + + return false; + + } catch (e) { + hideLoading(); + console.error("💥 Exception:", e); + showError('JavaScript-Fehler:
' + e.message); + return false; + } +} + +// Synonyme für einen Begriff laden +function loadSynonyms(text) { + if (!text) return; + + console.log('📋 Lade Synonyme für:', text); + + $.ajax({ + url: "/Thesaurus/ajax/getSynonyms.php", + type: "GET", + data: { text: text }, + dataType: "json", + success: function(response) { + console.log('✅ Synonyme geladen:', response); + + var section = document.getElementById('synonyms_section'); + var tbody = document.getElementById('synonyms_body'); + var noSyn = document.getElementById('no_synonyms'); + + if (response.success && response.count > 0) { + // Synonyme vorhanden - Bereich anzeigen + if (section) section.style.display = 'block'; + if (tbody) tbody.innerHTML = response.html; + if (noSyn) noSyn.style.display = 'none'; + } else { + // Keine Synonyme - Bereich komplett ausblenden + if (section) section.style.display = 'none'; + if (tbody) tbody.innerHTML = ''; + } + }, + error: function(xhr, status, error) { + console.error('❌ Fehler beim Laden der Synonyme:', error); + } + }); +} + +// Synonym löschen +function DeleteSynonym(text1, text2) { + if (!confirm('Möchten Sie das Synonym "' + text2 + '" wirklich entfernen?')) { + return; + } + + console.log('🗑️ Lösche Synonym:', { text1, text2 }); + + $.ajax({ + url: "/Thesaurus/ajax/saveSynonym.php", + type: "POST", + data: { + text1: text1, + text2: text2, + action: 'remove' + }, + dataType: "json", + success: function(response) { + console.log('✅ Synonym gelöscht:', response); + + if (response.success) { + showSuccess('Synonym entfernt!'); + // Synonyme neu laden + loadSynonyms(text1); + } else { + showError(response.error || 'Fehler beim Löschen'); + } + }, + error: function(xhr, status, error) { + console.error('❌ Fehler beim Löschen:', error); + showError('Fehler beim Löschen des Synonyms'); + } + }); +} + +function showError(message) { + var errorElement = document.getElementById("errorNewSubject"); + if (errorElement) { + errorElement.className = 'alert alert-danger'; + errorElement.innerHTML = '' + message.replace(/\n/g, '
'); + errorElement.style.display = 'block'; + + // Scroll zum Alert + errorElement.scrollIntoView({ behavior: 'smooth', block: 'nearest' }); + + // Optional: Auto-Hide nach 8 Sekunden + setTimeout(function() { + fadeOutAlert(); + }, 8000); + } +} + +function showSuccess(message) { + var errorElement = document.getElementById("errorNewSubject"); + if (errorElement) { + errorElement.className = 'alert alert-success'; + errorElement.innerHTML = '' + message; + errorElement.style.display = 'block'; + + // Scroll zum Alert + errorElement.scrollIntoView({ behavior: 'smooth', block: 'nearest' }); + + // Auto-Hide nach 3 Sekunden + setTimeout(function() { + fadeOutAlert(); + }, 3000); + } +} + +function showWarning(message) { + var errorElement = document.getElementById("errorNewSubject"); + if (errorElement) { + errorElement.className = 'alert alert-warning'; + errorElement.innerHTML = '' + message; + errorElement.style.display = 'block'; + + // Scroll zum Alert + errorElement.scrollIntoView({ behavior: 'smooth', block: 'nearest' }); + + // Auto-Hide nach 5 Sekunden + setTimeout(function() { + fadeOutAlert(); + }, 5000); + } +} + +function showLoading() { + var errorElement = document.getElementById("errorNewSubject"); + if (errorElement) { + errorElement.className = 'alert alert-info'; + errorElement.innerHTML = '
Laden...
Speichere Relation...
'; + errorElement.style.display = 'block'; + + // Scroll zum Alert + errorElement.scrollIntoView({ behavior: 'smooth', block: 'nearest' }); + } +} + +function hideLoading() { + var errorElement = document.getElementById("errorNewSubject"); + if (errorElement) { + errorElement.style.display = 'none'; + } +} + +function hideAlert() { + var errorElement = document.getElementById("errorNewSubject"); + if (errorElement) { + errorElement.style.display = 'none'; + } +} + +function fadeOutAlert() { + var errorElement = document.getElementById("errorNewSubject"); + if (errorElement && errorElement.style.display !== 'none') { + $(errorElement).fadeOut(500, function() { + errorElement.style.display = 'none'; + }); + } +} + +function resetForm() { + var relationTextElement = document.getElementById("search_relation"); + if (relationTextElement) { + relationTextElement.value = ''; + } + // Data-Attribute zurücksetzen + $('#subject-anlegen').removeData('record-id'); + $('#subject-anlegen').removeData('record-text'); + $('#subject-anlegen').removeData('relation-id'); + $('#subject-anlegen').removeData('relation-text'); + hideAlert(); +} + + +// Delete relation for a subjects +function DeleteRelation(AnchorID, LinkingID) +{ + if (!confirm('Möchten Sie diese Relation wirklich löschen?')) { + return; + } + + $.post("ajax/deleteRelation.php", { + AnchorID : AnchorID, + LinkingID : LinkingID + }, + function (data, status) { + if (status=="success") + { + var e = document.getElementById("errorNewSubject"); + e.classList.add('alert-success'); + e.textContent = ' Relation gelöscht'; + e.style.display = 'block'; + + showRelations(AnchorID,0,0) + } + + } + ); +} + +// delete the whole subject entry + +function DeleteTerm(AnchorID) +{ + if (!confirm('Möchten Sie diesen Eintrag wirklich löschen?')) { + return; + } + + $.post("ajax/deleteTerm.php", { + AnchorID: AnchorID[0] + }, + function (data, status) { + var metadata = (typeof data === 'string') ? JSON.parse(data) : data; + + if (status === "success" && metadata.success === true) { + // Tabelle neu laden – Zeile ist serverseitig wirklich gelöscht + $table.bootstrapTable('refresh'); + } else { + // Serverseitiger Fehler: Zeile wieder einblenden und Meldung anzeigen + $table.bootstrapTable('refresh'); + alert('Fehler beim Löschen: ' + (metadata.error || 'Unbekannter Fehler')); + } + } + ); +} + +// Modify subject and its relations +function ModifyTerm(AnchorID) +{ + // Schlagwort-Feld deaktivieren (darf nicht geändert werden) + document.getElementById("new_term").disabled = true; + + // Andere Felder aktivieren + document.getElementById("new_type").disabled = false; + document.getElementById("new_detailtype").disabled = false; + document.getElementById("new_classification").disabled = false; + document.getElementById("new_scopenote").disabled = false; + + $("#ID").val(AnchorID[0]); + + // Relationen-Bereich sofort sichtbar machen bei Bearbeitung! + document.getElementById("new_relation_label").style.display = 'block'; + document.getElementById("relation_table").style.display = 'block'; + + // Synonyme-Bereich sichtbar machen + var synSection = document.getElementById("synonyms_section"); + if (synSection) synSection.style.display = 'block'; + + // Buttons konfigurieren + document.getElementById("newSubjectSave").style.display = 'none'; + document.getElementById("newSubjectDismiss").style.display = 'inline'; + document.getElementById("SubjectModifySave").style.display = 'inline'; + document.getElementById("newRelationSubjectDismiss").style.display = 'none'; + + // Alert verstecken + document.getElementById("errorNewSubject").style.display = 'none'; + + $("#NewSubjectModalHeadline").text("Schlagwort ändern"); + + $.get("/Thesaurus/ajax/getAuthorityDataRaw.php", { + id : AnchorID[0] + }, + function (data, status) { + console.log('📥 getAuthorityDataRaw status:', status); + console.log('📥 getAuthorityDataRaw data:', data); + if (status == "success") + { + var metadata = (typeof data === 'string') ? JSON.parse(data) : data; + console.log('📥 metadata:', metadata); + dt = (metadata['rows'][0]); + console.log('📥 dt:', dt); + if (dt) { + $("#new_term").val(dt.Text); + $("#new_id").val(dt.ID); + $("#new_detailtype").val(dt.DetailType); + $("#new_classification").val(dt.Classification); + $("#new_descriptor").val(dt.Descriptor); + $("#new_type").val(dt.Type); + $("#new_scopenote").val(dt.Scopenote); + console.log('✅ Felder befüllt mit:', dt.Text); + + // Synonyme laden + if (typeof loadSynonyms === 'function') { + loadSynonyms(dt.Text); + } + } else { + console.error('❌ Keine Daten in rows[0]'); + } + } + + } + ).fail(function(xhr, status, error) { + console.error('❌ AJAX Fehler:', status, error); + }); + + $.get("ajax/getRelations.php", { + anchorID:AnchorID[0], + authType: "Subject" + }, + function (data, status) { + if (status=="success") + { + $(".new_modal_content").html(data); + $("#NewSubjectModal").modal("show"); + + // Autocomplete neu initialisieren nach Modal-Öffnung + setTimeout(function() { + if (typeof searchRelation === 'function') { + console.log('🔄 Autocomplete wird neu initialisiert...'); + searchRelation(); + } + }, 300); + } + } + ); +} + +// rewrite records after updating a subject entry +function UpdateEntry(authType) +{ + var AnchorID = $("#ID").val(); + var term = $("#new_term").val(); + term = term.trim(); + var type = $("#new_type").val(); + type = type.trim(); + var detailtype = $("#new_detailtype").val(); + detailtype = detailtype.trim(); + var classification = $("#new_classification").val(); + classification = classification.trim(); + var scopenote = $("#new_scopenote").val(); + scopenote = scopenote.trim(); + + if (term.length == 0) { + var a = document.getElementById("errorNewSubject"); + a.classList.add('alert-danger'); + a.textContent = 'Bitte Schlagwort eingeben!'; + a.style.display = 'block'; + return; + } + + var a = document.getElementById("errorNewSubject"); + a.classList.remove('alert-danger'); + a.textContent = ''; + a.style.display = 'none'; + + + + $.post("ajax/UpdateTerm.php", { + AnchorID: AnchorID, + authType: authType, + term: term, + type: type, + detailtype: detailtype, + classification: classification, + scopenote: scopenote + }, + function (data, status) { + if (status=="success") + { + document.getElementById("relation_table").style.display = 'block' + document.getElementById("newSubjectSave").style.display = 'none' + document.getElementById("newSubjectDismiss").style.display = 'inline' + document.getElementById("SubjectModifySave").style.display = 'none' + document.getElementById("newRelationSubjectDismiss").style.display = 'none' + var e = document.getElementById("errorNewSubject"); + e.classList.add('alert-success'); + e.textContent = ' Schlagwort geändert '; + e.style.display = 'block'; + + $("#NewSubjectModalHeadline").text("Schlagwort ändern"); + $("#NewSubjectModal").modal("show"); + } + } + ); + $table.bootstrapTable('refresh') +} + +function ShowModalDetails(id) { + + $.get("/Thesaurus/ajax/getDetailAuthorityData.php", { + authType : 'Subject', + offset : 0, + id : id, + sort : 'Anchor.Text', + limit : 1 + }, + function(data, status) { + if (status == "success") { + var metadata = (typeof data === 'string') ? JSON.parse(data) : data; + var row = metadata['rows']; + + if (!row || row.length === 0) { + console.error('❌ Keine Daten gefunden für ID:', id); + return; + } + + //Assign existing values to the modal popup fields + $("#display_term").val(row[0].Text); + $("#display_id").val(row[0].ID); + $("#display_id_show").val(row[0].ID); + $("#display_detailtype").val(row[0].Detailtype); + $("#display_classification").val(row[0].Classification); + $("#display_descriptor").val(row[0].Descriptor); + $("#display_type").val(row[0].Type); + $("#display_scopenote").val(row[0].Scopenote); + $("#display_synonyms").val(row[0].Synonyms || ''); + + // Alte tabellarische Darstellung (versteckt) + var html = [] + var txt = '' + $.each(row[0].Relations, function (key, value) { + txt = txt +'
'; + txt = txt +'
'; + txt = txt + '
'; + txt = txt + '
'; + txt = txt + '
'; + txt = txt +'
'; + }) + $(".modal_content").html(txt); + $.each(row[0].Relations, function (key, value) { + $("#display_relationtype_" +key).val(value['Relationtype']); + $("#display_textrelation_" +key).val(value['TextRelation']); + $("#display_IDRelation_" +key).val(value['IDRelation']); + }) + $("#display_relations").val(txt); + + // Modal anzeigen + $("#DetailsModal").modal("show"); + + // Semantisches Netz erstellen (nach Modal sichtbar) + setTimeout(function() { + if (typeof createSemanticNetwork === 'function') { + var synonyms = row[0].Synonyms || ''; + + // Falls keine Synonyme im Response, versuche sie separat zu laden + if (!synonyms || synonyms.trim() === '') { + console.log('📝 Keine Synonyme im Response, lade separat...'); + + $.ajax({ + url: "/Thesaurus/ajax/getSynonyms.php", + type: "GET", + data: { text: row[0].Text }, + dataType: "json", + async: false, // Synchron um auf Ergebnis zu warten + success: function(synResponse) { + if (synResponse.success && synResponse.synonyms && synResponse.synonyms.length > 0) { + synonyms = synResponse.synonyms.map(function(s) { return s.text; }).join(', '); + console.log('✅ Synonyme separat geladen:', synonyms); + // Auch das Textfeld aktualisieren + $("#display_synonyms").val(synonyms); + } + } + }); + } + + createSemanticNetwork(row[0].Text, row[0].ID, row[0].Relations, synonyms); + } else { + console.warn('⚠️ createSemanticNetwork Funktion nicht gefunden'); + } + }, 300); + } + } + ); +} + + + + + + +function CopyLink(value) +{ + var tempInput = document.createElement("input"); + tempInput.value = value; + document.body.appendChild(tempInput); + tempInput.select(); + document.execCommand("copy"); + document.body.removeChild(tempInput); +} + + + + +function refreshPage() +{ + location.reload(); +} diff --git a/migrate_tsdata.sh b/migrate_tsdata.sh new file mode 100755 index 0000000..536d8ab --- /dev/null +++ b/migrate_tsdata.sh @@ -0,0 +1,305 @@ +#!/bin/bash +# +# TSData Migration Script +# Migriert Daten von DSpace (MySQL 5.5) nach Tools (MariaDB 10.5) +# +# Verwendung: +# 1. Skript auf DSpace ausführen: ./migrate_tsdata.sh export +# 2. SQL-Datei auf Tools kopieren +# 3. Skript auf Tools ausführen: ./migrate_tsdata.sh import /pfad/zur/datei.sql +# + +set -e + +# Konfiguration +DB_NAME="TSData" +DB_USER="root" +BACKUP_DIR="/tmp/tsdata_migration" +TIMESTAMP=$(date +%Y%m%d_%H%M%S) +EXPORT_FILE="${BACKUP_DIR}/TSData_export_${TIMESTAMP}.sql" + +# Farben für Output +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +NC='\033[0m' # No Color + +log_info() { + echo -e "${GREEN}[INFO]${NC} $1" +} + +log_warn() { + echo -e "${YELLOW}[WARN]${NC} $1" +} + +log_error() { + echo -e "${RED}[ERROR]${NC} $1" +} + +# Verzeichnis erstellen +mkdir -p "$BACKUP_DIR" + +####################################### +# EXPORT (auf DSpace ausführen) +####################################### +do_export() { + log_info "Starte Export auf DSpace..." + + # Prüfen ob Datenbank existiert + if ! mysql -u "$DB_USER" -p -e "USE $DB_NAME" 2>/dev/null; then + log_error "Datenbank $DB_NAME nicht gefunden!" + exit 1 + fi + + log_info "Erstelle Daten-Export (ohne Struktur)..." + + # Export mit vollständigen INSERT-Statements + mysqldump -u "$DB_USER" -p "$DB_NAME" \ + --no-create-info \ + --complete-insert \ + --skip-triggers \ + --single-transaction \ + --quick \ + --lock-tables=false \ + --set-charset \ + --default-character-set=utf8 \ + > "$EXPORT_FILE" + + # Wrapper für SQL-Mode hinzufügen + TEMP_FILE="${EXPORT_FILE}.tmp" + cat > "$TEMP_FILE" << 'HEADER' +-- TSData Migration Export +-- Erstellt am: TIMESTAMP_PLACEHOLDER +-- Quelle: DSpace Server + +SET @OLD_SQL_MODE=@@SQL_MODE; +SET SQL_MODE='NO_AUTO_VALUE_ON_ZERO,ALLOW_INVALID_DATES'; +SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS; +SET FOREIGN_KEY_CHECKS=0; +SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS; +SET UNIQUE_CHECKS=0; + +-- DateModified '0000-00-00' Werte korrigieren (für MariaDB Strict Mode) +-- Dies geschieht nach dem Import + +HEADER + + sed -i "s/TIMESTAMP_PLACEHOLDER/$(date)/" "$TEMP_FILE" + cat "$EXPORT_FILE" >> "$TEMP_FILE" + + cat >> "$TEMP_FILE" << 'FOOTER' + +SET SQL_MODE=@OLD_SQL_MODE; +SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS; +SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS; + +-- Export Ende +FOOTER + + mv "$TEMP_FILE" "$EXPORT_FILE" + + # Statistiken anzeigen + log_info "Export abgeschlossen: $EXPORT_FILE" + log_info "Dateigröße: $(du -h "$EXPORT_FILE" | cut -f1)" + + echo "" + log_info "Datensätze pro Tabelle:" + mysql -u "$DB_USER" -p -N -e " + SELECT 'Anchor', COUNT(*) FROM $DB_NAME.Anchor + UNION ALL SELECT 'Entry', COUNT(*) FROM $DB_NAME.Entry + UNION ALL SELECT 'EntryNew', COUNT(*) FROM $DB_NAME.EntryNew + UNION ALL SELECT 'IDSynonym', COUNT(*) FROM $DB_NAME.IDSynonym + UNION ALL SELECT 'Linking', COUNT(*) FROM $DB_NAME.Linking + UNION ALL SELECT 'Synonyms', COUNT(*) FROM $DB_NAME.Synonyms + UNION ALL SELECT 'Treeview', COUNT(*) FROM $DB_NAME.Treeview; + " 2>/dev/null | column -t + + echo "" + log_info "Nächster Schritt: Datei auf Tools-Server kopieren:" + echo " scp $EXPORT_FILE user@tools.bibb.de:/tmp/" +} + +####################################### +# IMPORT (auf Tools ausführen) +####################################### +do_import() { + IMPORT_FILE="$1" + + if [ -z "$IMPORT_FILE" ]; then + log_error "Keine Import-Datei angegeben!" + echo "Verwendung: $0 import /pfad/zur/export_datei.sql" + exit 1 + fi + + if [ ! -f "$IMPORT_FILE" ]; then + log_error "Datei nicht gefunden: $IMPORT_FILE" + exit 1 + fi + + log_info "Starte Import auf Tools..." + + # Backup der bestehenden Daten erstellen + BACKUP_FILE="${BACKUP_DIR}/TSData_backup_tools_${TIMESTAMP}.sql" + log_info "Erstelle Backup der bestehenden Daten: $BACKUP_FILE" + + mysqldump -u "$DB_USER" -p "$DB_NAME" \ + --single-transaction \ + --quick \ + > "$BACKUP_FILE" + + log_info "Backup erstellt: $(du -h "$BACKUP_FILE" | cut -f1)" + + # Bestätigung anfordern + echo "" + log_warn "ACHTUNG: Alle bestehenden Daten in $DB_NAME werden gelöscht!" + read -p "Fortfahren? (ja/nein): " CONFIRM + + if [ "$CONFIRM" != "ja" ]; then + log_info "Abgebrochen." + exit 0 + fi + + # Tabellen leeren + log_info "Leere bestehende Tabellen..." + mysql -u "$DB_USER" -p "$DB_NAME" << 'EOF' +SET FOREIGN_KEY_CHECKS=0; +TRUNCATE TABLE Synonyms; +TRUNCATE TABLE Linking; +TRUNCATE TABLE EntryNew; +TRUNCATE TABLE Entry; +TRUNCATE TABLE Anchor; +TRUNCATE TABLE IDSynonym; +TRUNCATE TABLE Treeview; +SET FOREIGN_KEY_CHECKS=1; +EOF + + # Daten importieren + log_info "Importiere Daten aus $IMPORT_FILE..." + mysql -u "$DB_USER" -p "$DB_NAME" < "$IMPORT_FILE" + + # DateModified '0000-00-00' Werte korrigieren + log_info "Korrigiere ungültige Datumswerte..." + mysql -u "$DB_USER" -p "$DB_NAME" << 'EOF' +UPDATE Entry +SET DateModified = DateCreated +WHERE DateModified = '0000-00-00 00:00:00' + OR DateModified IS NULL; +EOF + + # Verifizierung + echo "" + log_info "Import abgeschlossen. Verifizierung:" + mysql -u "$DB_USER" -p -N -e " + SELECT 'Anchor', COUNT(*) FROM $DB_NAME.Anchor + UNION ALL SELECT 'Entry', COUNT(*) FROM $DB_NAME.Entry + UNION ALL SELECT 'EntryNew', COUNT(*) FROM $DB_NAME.EntryNew + UNION ALL SELECT 'IDSynonym', COUNT(*) FROM $DB_NAME.IDSynonym + UNION ALL SELECT 'Linking', COUNT(*) FROM $DB_NAME.Linking + UNION ALL SELECT 'Synonyms', COUNT(*) FROM $DB_NAME.Synonyms + UNION ALL SELECT 'Treeview', COUNT(*) FROM $DB_NAME.Treeview; + " 2>/dev/null | column -t + + echo "" + log_info "Migration erfolgreich abgeschlossen!" + log_info "Backup liegt unter: $BACKUP_FILE" +} + +####################################### +# VERIFY (Datenintegrität prüfen) +####################################### +do_verify() { + log_info "Prüfe Datenintegrität..." + + mysql -u "$DB_USER" -p "$DB_NAME" << 'EOF' +-- Prüfe ob alle Linking-Einträge gültige Anchor-IDs haben +SELECT 'Linking -> Anchor (fehlend)' AS Check_Type, COUNT(*) AS Anzahl +FROM Linking l +LEFT JOIN Anchor a ON l.IDAnchor = a.ID +WHERE a.ID IS NULL + +UNION ALL + +-- Prüfe ob alle Linking-Einträge gültige Entry-IDs haben +SELECT 'Linking -> Entry (fehlend)', COUNT(*) +FROM Linking l +LEFT JOIN Entry e ON l.IDEntry = e.ID +WHERE e.ID IS NULL + +UNION ALL + +-- Prüfe auf ungültige Datumswerte in Entry +SELECT 'Entry mit ungültigem DateModified', COUNT(*) +FROM Entry +WHERE DateModified = '0000-00-00 00:00:00'; +EOF + + log_info "Integritätsprüfung abgeschlossen." +} + +####################################### +# ROLLBACK (Backup wiederherstellen) +####################################### +do_rollback() { + BACKUP_FILE="$1" + + if [ -z "$BACKUP_FILE" ]; then + log_info "Verfügbare Backups:" + ls -la ${BACKUP_DIR}/TSData_backup_tools_*.sql 2>/dev/null || echo "Keine Backups gefunden." + echo "" + echo "Verwendung: $0 rollback /pfad/zum/backup.sql" + exit 1 + fi + + if [ ! -f "$BACKUP_FILE" ]; then + log_error "Backup-Datei nicht gefunden: $BACKUP_FILE" + exit 1 + fi + + log_warn "Stelle Backup wieder her: $BACKUP_FILE" + read -p "Fortfahren? (ja/nein): " CONFIRM + + if [ "$CONFIRM" != "ja" ]; then + log_info "Abgebrochen." + exit 0 + fi + + mysql -u "$DB_USER" -p "$DB_NAME" < "$BACKUP_FILE" + log_info "Rollback abgeschlossen." +} + +####################################### +# MAIN +####################################### +case "$1" in + export) + do_export + ;; + import) + do_import "$2" + ;; + verify) + do_verify + ;; + rollback) + do_rollback "$2" + ;; + *) + echo "TSData Migration Script" + echo "" + echo "Verwendung: $0 {export|import|verify|rollback} [optionen]" + echo "" + echo "Befehle:" + echo " export Daten aus DSpace exportieren" + echo " import Daten in Tools importieren" + echo " verify Datenintegrität prüfen" + echo " rollback Backup wiederherstellen" + echo "" + echo "Workflow:" + echo " 1. Auf DSpace: ./migrate_tsdata.sh export" + echo " 2. Kopieren: scp /tmp/tsdata_migration/TSData_export_*.sql user@tools:/tmp/" + echo " 3. Auf Tools: ./migrate_tsdata.sh import /tmp/TSData_export_*.sql" + echo " 4. Prüfen: ./migrate_tsdata.sh verify" + exit 1 + ;; +esac + diff --git a/setup-libs.sh b/setup-libs.sh new file mode 100755 index 0000000..9d6f238 --- /dev/null +++ b/setup-libs.sh @@ -0,0 +1,263 @@ +#!/bin/bash +# +# setup-libs.sh - Richtet lokale JavaScript/CSS Libraries ein +# +# Verwendung: +# chmod +x setup-libs.sh +# sudo ./setup-libs.sh +# +# Struktur: +# /var/www/html/libs/ +# ├── bootstrap/ +# │ ├── 5.3.0/ +# │ └── current -> 5.3.0 +# ├── jquery/ +# │ ├── 3.6.0/ +# │ └── current -> 3.6.0 +# ... +# +# Nutzung in HTML: +# +# + + + diff --git a/templates/CorporatesModal.tpl b/templates/CorporatesModal.tpl new file mode 100644 index 0000000..c03dd89 --- /dev/null +++ b/templates/CorporatesModal.tpl @@ -0,0 +1 @@ + diff --git a/templates/Footer.html b/templates/Footer.html new file mode 100644 index 0000000..de7e7ff --- /dev/null +++ b/templates/Footer.html @@ -0,0 +1,16 @@ + + + + + diff --git a/templates/Header.html b/templates/Header.html new file mode 100644 index 0000000..932a39c --- /dev/null +++ b/templates/Header.html @@ -0,0 +1,709 @@ + + + + + + BIBB Thesaurus-Verwaltung + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/templates/PersonDetailsModal.tpl b/templates/PersonDetailsModal.tpl new file mode 100644 index 0000000..faddefc --- /dev/null +++ b/templates/PersonDetailsModal.tpl @@ -0,0 +1,318 @@ + + + + + + + + + + diff --git a/templates/PersonsModal.tpl b/templates/PersonsModal.tpl new file mode 100644 index 0000000..4f57d61 --- /dev/null +++ b/templates/PersonsModal.tpl @@ -0,0 +1 @@ + diff --git a/templates/PublisherDetailsModal.tpl b/templates/PublisherDetailsModal.tpl new file mode 100644 index 0000000..455cb07 --- /dev/null +++ b/templates/PublisherDetailsModal.tpl @@ -0,0 +1,317 @@ + + + + + + + + + diff --git a/templates/PublishersModal.tpl b/templates/PublishersModal.tpl new file mode 100644 index 0000000..c351983 --- /dev/null +++ b/templates/PublishersModal.tpl @@ -0,0 +1 @@ + diff --git a/templates/RelationsModal.tpl b/templates/RelationsModal.tpl new file mode 100644 index 0000000..c820b74 --- /dev/null +++ b/templates/RelationsModal.tpl @@ -0,0 +1,349 @@ + + + + + + + diff --git a/templates/SubjectDetailsModal.tpl b/templates/SubjectDetailsModal.tpl new file mode 100644 index 0000000..6439fcd --- /dev/null +++ b/templates/SubjectDetailsModal.tpl @@ -0,0 +1,745 @@ + + + + + + + + + + diff --git a/templates/SubjectsModal.tpl b/templates/SubjectsModal.tpl new file mode 100644 index 0000000..ee06bbb --- /dev/null +++ b/templates/SubjectsModal.tpl @@ -0,0 +1,2 @@ + + diff --git a/templates/newClassification.tpl b/templates/newClassification.tpl new file mode 100644 index 0000000..6646c9d --- /dev/null +++ b/templates/newClassification.tpl @@ -0,0 +1,111 @@ + + + diff --git a/templates/newCorporate.tpl b/templates/newCorporate.tpl new file mode 100644 index 0000000..5e445f5 --- /dev/null +++ b/templates/newCorporate.tpl @@ -0,0 +1,115 @@ + + + diff --git a/templates/newPerson.tpl b/templates/newPerson.tpl new file mode 100644 index 0000000..232588b --- /dev/null +++ b/templates/newPerson.tpl @@ -0,0 +1,103 @@ + + + diff --git a/templates/newPublisher.tpl b/templates/newPublisher.tpl new file mode 100644 index 0000000..e198c58 --- /dev/null +++ b/templates/newPublisher.tpl @@ -0,0 +1,114 @@ + + + diff --git a/templates/newSubject.tpl b/templates/newSubject.tpl new file mode 100644 index 0000000..803194a --- /dev/null +++ b/templates/newSubject.tpl @@ -0,0 +1,157 @@ + + +