commit 53a05224175e66d3185c570e167fc6a81ec9b67d Author: root Date: Mon Feb 23 16:11:35 2026 +0100 Initial commit 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 @@ + + +