Compare commits

..

18 Commits

Author SHA1 Message Date
caroline.slt ef3699d5f5 Reviewänderungen 2026-07-19 21:54:44 +02:00
caroline.slt fedb6f59f5 . 2026-07-19 20:58:52 +02:00
caroline.slt 5eca2f162b . 2026-07-19 20:10:51 +02:00
caroline.slt d7d121b541 . 2026-07-19 19:20:12 +02:00
niklas.ortmann 96f38ce72e Merge pull request 'Suche nutzt nun Tags' (#59) from suchergebnisseTags into dev
Reviewed-on: #59
2026-07-19 16:29:40 +02:00
niklas.ortmann b81f0c9e5c Update DatabaseArticleManager.php 2026-07-19 16:28:29 +02:00
niklas.ortmann f8aed4283e Update dataSources.local.xml 2026-07-19 16:28:27 +02:00
niklas.ortmann afd90086d8 Update profile.php 2026-07-19 14:20:43 +02:00
niklas.ortmann a7a54f877c Merge pull request 'Beitragseditor funktioniert mit und ohne JS (+Bilder löschen)' (#57) from BeitragseditorOhneJS into dev
Reviewed-on: #57
2026-07-19 13:45:33 +02:00
NOrtmann1 9b9f71d21c Update DatabaseInitializer.php 2026-07-19 13:44:27 +02:00
NOrtmann1 4b84f0ab09 Update updateArticle-controller.php 2026-07-19 13:30:39 +02:00
NOrtmann1 8c8b148bf4 Update updateArticle-controller.php 2026-07-19 13:15:23 +02:00
NOrtmann1 6bf335ce46 Update createArticle.php 2026-07-19 13:15:20 +02:00
NOrtmann1 144a1015f1 Create article-block-helper.php 2026-07-19 13:15:17 +02:00
NOrtmann1 c0a94b2eee Update createArticle-controller.php 2026-07-19 13:15:14 +02:00
NOrtmann1 f1a32120d6 Update editor.js 2026-07-19 12:48:41 +02:00
NOrtmann1 97fa1de999 Update updateArticle-controller.php 2026-07-19 12:48:38 +02:00
NOrtmann1 7341fe7733 Update updateArticle.php 2026-07-19 12:48:33 +02:00
15 changed files with 563 additions and 402 deletions
+1 -1
View File
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<project version="4"> <project version="4">
<component name="dataSourceStorageLocal" created-in="IU-261.24374.151"> <component name="dataSourceStorageLocal" created-in="IU-253.32098.101">
<data-source name="articles" uuid="315cb5c9-2b0f-435b-b602-59823b160908"> <data-source name="articles" uuid="315cb5c9-2b0f-435b-b602-59823b160908">
<database-info product="SQLite" version="3.51.1" jdbc-version="4.2" driver-name="SQLite JDBC" driver-version="3.51.1.0" dbms="SQLITE" exact-version="3.51.1" exact-driver-version="3.51"> <database-info product="SQLite" version="3.51.1" jdbc-version="4.2" driver-name="SQLite JDBC" driver-version="3.51.1.0" dbms="SQLITE" exact-version="3.51.1" exact-driver-version="3.51">
<identifier-quote-string>&quot;</identifier-quote-string> <identifier-quote-string>&quot;</identifier-quote-string>
+55 -17
View File
@@ -4,12 +4,28 @@ if (!isset($_SESSION["user"])) {
header("Location: index.php?pfad=login"); header("Location: index.php?pfad=login");
exit(); exit();
} }
// -------------------------------------------------------------------------
// Aktuelle Blockliste ermitteln: Session-Fallback (nach Validierungsfehler
// oder Zwischen-Submit "add_block"/"delete_block") oder leeres Array (Start).
// -------------------------------------------------------------------------
$rawContent = '[]';
if (isset($_SESSION['old_content']) && !empty($_SESSION['old_content'])) {
$rawContent = $_SESSION['old_content'];
unset($_SESSION['old_content']);
}
$blocks = json_decode($rawContent, true);
if (!is_array($blocks)) {
$blocks = [];
}
$blocks = array_values($blocks); // sequentielle Indizes 0..n-1 sicherstellen
?> ?>
<!-- <!--
Seite: Beitrag erstellen Seite: Beitrag erstellen
Inhalt: Formular für die Erstellung eines neuen Beitrags Inhalt: Formular für die Erstellung eines neuen Beitrags
--> -->
<form method="post" action="php/controller/createArticle-controller.php" id="editor-form" class="article-editor-scope.editor-container article-editor-scope editor-container"> <form method="post" action="php/controller/createArticle-controller.php" id="editor-form" enctype="multipart/form-data" class="article-editor-scope.editor-container article-editor-scope editor-container">
<main class="editor-main"> <main class="editor-main">
<?php include_once "includes/alertMessages.php"?> <?php include_once "includes/alertMessages.php"?>
@@ -18,27 +34,49 @@ if (!isset($_SESSION["user"])) {
value="<?php echo htmlspecialchars($_SESSION['old_title'] ?? ''); unset($_SESSION['old_title']); ?>" value="<?php echo htmlspecialchars($_SESSION['old_title'] ?? ''); unset($_SESSION['old_title']); ?>"
placeholder="Titel hier eingeben" required> placeholder="Titel hier eingeben" required>
<!-- Hier werden die dynamischen divs via JavaScript eingefügt --> <!--
<div id="block-container"></div> Content-Blöcke: werden serverseitig als echte, benannte Formularfelder gerendert
(blocks[i][type], blocks[i][text] bzw. blocks[i][image]). Dadurch funktioniert das
Hinzufügen/Entfernen von Blöcken und der Bild-Upload auch ganz ohne JavaScript über
einen normalen Formular-Submit. Ist JavaScript aktiv, fängt js/editor.js diese
Submits ab und erledigt dieselbe Änderung lokal im DOM, ohne den Server zu belasten.
-->
<div id="block-container">
<?php foreach ($blocks as $i => $block): ?>
<?php
$blockType = $block['type'] ?? '';
if ($blockType !== 'text' && $blockType !== 'image') {
continue; // unbekannter/kaputter Block wird übersprungen
}
?>
<div class="editor-block article-editor-scope" data-index="<?php echo (int)$i; ?>">
<input type="hidden" name="blocks[<?php echo (int)$i; ?>][type]" value="<?php echo htmlspecialchars($blockType); ?>">
<?php if ($blockType === 'text'): ?>
<textarea name="blocks[<?php echo (int)$i; ?>][text]"
placeholder="Schreibe deinen Textblock..."><?php echo htmlspecialchars($block['value'] ?? ''); ?></textarea>
<?php else: /* image */ ?>
<?php if (!empty($block['value'])): ?>
<img src="<?php echo htmlspecialchars($block['value']); ?>"
class="block-image-preview"
style="max-width:200px;display:block;margin-top:10px;">
<input type="hidden" name="blocks[<?php echo (int)$i; ?>][existing_image]" value="<?php echo htmlspecialchars($block['value']); ?>">
<?php endif; ?>
<input type="file" name="blocks[<?php echo (int)$i; ?>][image]" accept="image/*">
<?php endif; ?>
<button type="submit" name="editor_action" value="delete_block:<?php echo (int)$i; ?>" class="delete-block-btn">✕</button>
</div>
<?php endforeach; ?>
</div>
<!-- Plus-Button und das Pop-up-Menü -->
<div id="add-block-control" class="article-editor-scope add-block-control"> <div id="add-block-control" class="article-editor-scope add-block-control">
<button type="button" id="plus-button" class="article-editor-scope plus-button">+</button> <button type="button" id="plus-button" class="article-editor-scope plus-button">+</button>
<div id="block-popup" class="article-editor-scope block-popup hidden"> <div id="block-popup" class="article-editor-scope block-popup">
<button type="button" data-type="text">Textblock</button> <button type="submit" name="editor_action" value="add_text" data-type="text">Textblock</button>
<button type="button" data-type="image">Bild einfügen</button> <button type="submit" name="editor_action" value="add_image" data-type="image">Bild einfügen</button>
</div> </div>
</div> </div>
<!-- Unsichtbares Textfeld, das die JSON-Daten hält und an den Controller postet -->
<textarea id="content" name="content" style="display:none;"><?php
if (isset($_SESSION['old_content']) && !empty($_SESSION['old_content'])){
echo htmlspecialchars($_SESSION['old_content']);
unset($_SESSION['old_content']);
} else {
echo '[]'; // Standardmäßig ein leeres JSON-Array
}
?></textarea>
</main> </main>
<!-- Seitenleiste --> <!-- Seitenleiste -->
+12 -10
View File
@@ -81,10 +81,12 @@ $isEditMode = (isset($_GET["edit"]) && $_GET["edit"] === "1") || !empty($error);
<br> <br>
<form action="php/controller/deleteAccount-controller.php" method="POST"> <form action="php/controller/deleteAccount-controller.php"
method="POST"
class="confirm-delete-account">
<button type="submit" <button type="submit"
class="button" class="button">
onclick="return confirm('Möchtest du deinen Account wirklich unwiderruflich löschen?');">
Account löschen Account löschen
</button> </button>
</form> </form>
@@ -154,14 +156,16 @@ $isEditMode = (isset($_GET["edit"]) && $_GET["edit"] === "1") || !empty($error);
Bearbeiten Bearbeiten
</a> </a>
<form action="php/controller/deleteArticle-controller.php" method="POST"> <form action="php/controller/deleteArticle-controller.php"
method="POST"
class="confirm-delete-article">
<input type="hidden" <input type="hidden"
name="id" name="id"
value="<?php echo htmlspecialchars($userArticle->getID()); ?>"> value="<?php echo htmlspecialchars($userArticle->getID()); ?>">
<button type="submit" <button type="submit"
class="button" class="button">
onclick="return confirm('Möchtest du diesen Artikel wirklich löschen?');">
Löschen Löschen
</button> </button>
</form> </form>
@@ -172,11 +176,9 @@ $isEditMode = (isset($_GET["edit"]) && $_GET["edit"] === "1") || !empty($error);
<p>Du hast noch keine Beiträge erstellt.</p> <p>Du hast noch keine Beiträge erstellt.</p>
<button type="button" <a href="index.php?pfad=createArticle" class="button">
class="button"
onclick="window.location.href='index.php?pfad=createArticle';">
Beitrag erstellen! Beitrag erstellen!
</button> </a>
<?php endif; ?> <?php endif; ?>
</div> </div>
+26 -7
View File
@@ -124,7 +124,9 @@ $categories = [
</div> </div>
<noscript> <noscript>
<button type="submit" class="nav__search-button">Filter anwenden</button> <button type="submit" class="nav__search-button">
Filter anwenden
</button>
</noscript> </noscript>
</form> </form>
@@ -193,17 +195,34 @@ $categories = [
</div> </div>
<nav class="s-res-page-navigation" aria-label="Seitennavigation"> <nav class="s-res-page-navigation" aria-label="Seitennavigation">
<!-- No-JS-Fallback: -->
<noscript> <noscript>
<?php echo renderNoJsPagination($currentPage, $totalPages, $query, $currentSort, $currentCategory, $limit); ?> <?php echo renderNoJsPagination(
$currentPage,
$totalPages,
$query,
$currentSort,
$currentCategory,
$limit
); ?>
</noscript> </noscript>
<!-- JS-Version: wird per search-results.js befüllt/eingeblendet: --> <div id="js-page-navigation" style="display: none;">
<div id="js-page-navigation" style="display:none;"> <button type="button"
<button type="button" class="s-res-page-btn" id="prev-page-btn">«</button> class="s-res-page-btn"
id="prev-page-btn">
«
</button>
<span id="dynamic-page-numbers"></span> <span id="dynamic-page-numbers"></span>
<button type="button" class="s-res-page-btn" id="next-page-btn">»</button>
<button type="button"
class="s-res-page-btn"
id="next-page-btn">
»
</button>
</div> </div>
</nav> </nav>
</div> </div>
+5 -7
View File
@@ -237,7 +237,7 @@ if ($replyAuthor === null) {
<form method="post" <form method="post"
action="index.php?pfad=deleteComment" action="index.php?pfad=deleteComment"
class="delete-comment-form"> class="delete-comment-form confirm-delete-comment">
<input type="hidden" <input type="hidden"
name="comment_id" name="comment_id"
@@ -252,8 +252,7 @@ if ($replyAuthor === null) {
); ?>"> ); ?>">
<button type="submit" <button type="submit"
class="delete-comment-button" class="delete-comment-button">
onclick="return confirm('Möchtest du diesen Kommentar wirklich löschen?');">
Kommentar löschen Kommentar löschen
</button> </button>
</form> </form>
@@ -369,7 +368,7 @@ if ($replyAuthor === null) {
<form method="post" <form method="post"
action="index.php?pfad=deleteComment" action="index.php?pfad=deleteComment"
class="delete-comment-form"> class="delete-comment-form confirm-delete-comment">
<input type="hidden" <input type="hidden"
name="comment_id" name="comment_id"
@@ -384,9 +383,8 @@ if ($replyAuthor === null) {
); ?>"> ); ?>">
<button type="submit" <button type="submit"
class="delete-comment-button" class="delete-comment-button">
onclick="return confirm('Möchtest du diesen Kommentar wirklich löschen?');"> Antwort löschen
Kommentar löschen
</button> </button>
</form> </form>
+55 -23
View File
@@ -5,10 +5,25 @@ if (!isset($_SESSION["user"])) {
exit(); exit();
} }
include_once 'php/controller/showArticle-controller.php'; include_once 'php/controller/showArticle-controller.php';
// Aktuelle Blockliste ermitteln:
$rawContent = '[]';
if (isset($_SESSION['old_content']) && !empty($_SESSION['old_content'])) {
$rawContent = $_SESSION['old_content'];
unset($_SESSION['old_content']);
} elseif (isset($content) && !empty($content)) {
$rawContent = $content;
}
$blocks = json_decode($rawContent, true);
if (!is_array($blocks)) {
$blocks = [];
}
$blocks = array_values($blocks);
?> ?>
<!-- <!--
Seite: Beitrag erstellen Seite: Beitrag bearbeiten
Inhalt: Formular für die Erstellung eines neuen Beitrags Inhalt: Formular für die Bearbeitung eines Beitrags
--> -->
<form method="post" action="php/controller/updateArticle-controller.php?id=<?php if(isset($id) && !empty($id)){echo htmlspecialchars($id);}else{$_SESSION["message"] = "missing_id";} ?>" id="editor-form" enctype="multipart/form-data" class="article-editor-scope.editor-container article-editor-scope editor-container"> <form method="post" action="php/controller/updateArticle-controller.php?id=<?php if(isset($id) && !empty($id)){echo htmlspecialchars($id);}else{$_SESSION["message"] = "missing_id";} ?>" id="editor-form" enctype="multipart/form-data" class="article-editor-scope.editor-container article-editor-scope editor-container">
@@ -27,32 +42,49 @@ include_once 'php/controller/showArticle-controller.php';
?>" ?>"
placeholder="Titel hier eingeben" required> placeholder="Titel hier eingeben" required>
<!-- Hier werden die dynamischen divs via JavaScript eingefügt --> <!--
<div id="block-container"></div> Content-Blöcke: werden serverseitig als echte, benannte Formularfelder gerendert
(blocks[i][type]...). Dadurch funktioniert das Hinzufügen/Entfernen von Blöcken und
der Bild-Upload auch ganz ohne JavaScript über einen normalen Formular-Submit.
Ist JavaScript aktiv, fängt js/editor.js diese Submits ab und erledigt dieselbe
Änderung lokal im DOM, ohne den Server zu belasten.
-->
<div id="block-container">
<?php foreach ($blocks as $i => $block): ?>
<?php
$blockType = $block['type'] ?? '';
if ($blockType !== 'text' && $blockType !== 'image') {
continue; // unbekannter/kaputter Block wird übersprungen
}
?>
<div class="editor-block article-editor-scope" data-index="<?php echo (int)$i; ?>">
<input type="hidden" name="blocks[<?php echo (int)$i; ?>][type]" value="<?php echo htmlspecialchars($blockType); ?>">
<?php if ($blockType === 'text'): ?>
<textarea name="blocks[<?php echo (int)$i; ?>][text]"
placeholder="Schreibe deinen Textblock..."><?php echo htmlspecialchars($block['value'] ?? ''); ?></textarea>
<?php else: /* image */ ?>
<?php if (!empty($block['value'])): ?>
<img src="<?php echo htmlspecialchars($block['value']); ?>"
class="block-image-preview"
style="max-width:200px;display:block;margin-top:10px;">
<input type="hidden" name="blocks[<?php echo (int)$i; ?>][existing_image]" value="<?php echo htmlspecialchars($block['value']); ?>">
<?php endif; ?>
<input type="file" name="blocks[<?php echo (int)$i; ?>][image]" accept="image/*">
<?php endif; ?>
<button type="submit" name="editor_action" value="delete_block:<?php echo (int)$i; ?>" class="delete-block-btn">✕</button>
</div>
<?php endforeach; ?>
</div>
<!-- Plus-Button und das Pop-up-Menü -->
<div id="add-block-control" class="article-editor-scope add-block-control"> <div id="add-block-control" class="article-editor-scope add-block-control">
<button type="button" id="plus-button" class="article-editor-scope plus-button">+</button> <button type="button" id="plus-button" class="article-editor-scope plus-button">+</button>
<div id="block-popup" class="article-editor-scope block-popup hidden"> <div id="block-popup" class="article-editor-scope block-popup">
<button type="button" data-type="text">Textblock</button> <button type="submit" name="editor_action" value="add_text" data-type="text">Textblock</button>
<button type="button" data-type="image">Bild einfügen</button> <button type="submit" name="editor_action" value="add_image" data-type="image">Bild einfügen</button>
</div> </div>
</div> </div>
<!-- Unsichtbares Textfeld, das die JSON-Daten hält und an den Controller postet -->
<textarea id="content" name="content" style="display:none;"><?php
if (isset($_SESSION['old_content']) && !empty($_SESSION['old_content'])){
echo htmlspecialchars($_SESSION['old_content']);
unset($_SESSION['old_content']);
}elseif (isset($content) && !empty($content)){
echo htmlspecialchars($content);
} else {
echo '[]';
}
?></textarea>
<!-- unsichtbares Input, um die zu löschenden Bilder zu übergeben-->
<input type="hidden" id="deleted-images" name="deleted_images" value="[]">
</main> </main>
<!-- Seitenleiste --> <!-- Seitenleiste -->
+69
View File
@@ -0,0 +1,69 @@
<?php
/**
* Baut die Blockliste aus den POST-Daten (blocks[i][type], blocks[i][text],
* blocks[i][existing_image]) und ggf. hochgeladenen Dateien (blocks[i][image])
* zusammen. Läuft bei JEDEM Submit (Zwischen-Schritt "Block hinzufügen/löschen"
* UND finales Speichern/Veröffentlichen), damit neu ausgewählte Bilder in jedem
* Fall persistiert werden, bevor PHP die temporäre Upload-Datei nach
* Request-Ende verwirft.
*
* Wird sowohl vom createArticle- als auch vom updateArticle-Controller genutzt.
*
* @param array $postBlocks $_POST['blocks'] ?? []
* @param array $fileBlocks $_FILES['blocks'] ?? []
* @param string $uploadDir absoluter Pfad zum uploads-Verzeichnis (mit trailing slash)
* @return array Liste von ['type' => 'text'|'image', 'value' => string]
*/
function rebuildBlocksFromPost(array $postBlocks, array $fileBlocks, string $uploadDir): array {
$allowedExtensions = ['jpg', 'jpeg', 'png', 'gif', 'webp'];
$keys = array_keys($postBlocks);
if (isset($fileBlocks['name']) && is_array($fileBlocks['name'])) {
$keys = array_unique(array_merge($keys, array_keys($fileBlocks['name'])));
}
sort($keys, SORT_NUMERIC);
$blocks = [];
foreach ($keys as $key) {
$type = $postBlocks[$key]['type'] ?? null;
if ($type === 'text') {
$blocks[] = [
'type' => 'text',
'value' => $postBlocks[$key]['text'] ?? '',
];
} elseif ($type === 'image') {
// Vorbelegung: bereits vorhandenes Server-Bild (falls Datei nicht ersetzt wird)
$value = $postBlocks[$key]['existing_image'] ?? '';
$hasUpload = isset($fileBlocks['error'][$key]['image'])
&& $fileBlocks['error'][$key]['image'] === UPLOAD_ERR_OK;
if ($hasUpload) {
$tmpName = $fileBlocks['tmp_name'][$key]['image'];
$originalName = $fileBlocks['name'][$key]['image'];
$extension = strtolower(pathinfo($originalName, PATHINFO_EXTENSION));
if (!in_array($extension, $allowedExtensions, true)) {
$extension = 'jpg';
}
$fileName = 'img_' . uniqid() . '.' . $extension;
$destination = $uploadDir . $fileName;
if (move_uploaded_file($tmpName, $destination)) {
$value = 'uploads/' . $fileName;
}
// Bei Fehler: alter Wert (falls vorhanden) bleibt erhalten, Block wird nicht verworfen
}
$blocks[] = [
'type' => 'image',
'value' => $value,
];
}
// unbekannter/fehlender type -> Block wird ignoriert
}
return $blocks;
}
+2 -1
View File
@@ -24,9 +24,10 @@ include_once "php/controller/index-controller.php";
<link rel="stylesheet" href="css/showCategory.css"> <link rel="stylesheet" href="css/showCategory.css">
<script src="js/comments.js" defer></script> <script src="js/comments.js" defer></script>
<script src="js/profile.js" defer></script>
<script src="js/editor.js" async></script> <script src="js/editor.js" async></script>
<script src="js/search-results.js" async></script>
<script src="js/showCategory.js" async></script> <script src="js/showCategory.js" async></script>
<script src="js/search.js" async></script>
<title>EduForge</title> <title>EduForge</title>
+20 -2
View File
@@ -11,6 +11,26 @@ document.addEventListener("DOMContentLoaded", function () {
const parentCommentInput = document.getElementById("parent-comment-id"); const parentCommentInput = document.getElementById("parent-comment-id");
const replyInfo = document.getElementById("reply-info"); const replyInfo = document.getElementById("reply-info");
/**
* Fragt vor dem Löschen eines Kommentars nach einer Bestätigung.
*
* Der Listener funktioniert auch für Kommentare,
* die später per AJAX eingefügt werden.
*/
document.addEventListener("submit", function (event) {
if (!event.target.classList.contains("delete-comment-form")) {
return;
}
const confirmed = confirm(
"Möchtest du diesen Kommentar wirklich löschen?"
);
if (!confirmed) {
event.preventDefault();
}
});
if (!form || !commentsList || !commentContent || !parentCommentInput) { if (!form || !commentsList || !commentContent || !parentCommentInput) {
return; return;
} }
@@ -152,7 +172,6 @@ document.addEventListener("DOMContentLoaded", function () {
<button <button
type="submit" type="submit"
class="delete-comment-button" class="delete-comment-button"
onclick="return confirm('Möchtest du diesen Kommentar wirklich löschen?');"
> >
Kommentar löschen Kommentar löschen
</button> </button>
@@ -230,7 +249,6 @@ document.addEventListener("DOMContentLoaded", function () {
<button <button
type="submit" type="submit"
class="delete-comment-button" class="delete-comment-button"
onclick="return confirm('Möchtest du diesen Kommentar wirklich löschen?');"
> >
Kommentar löschen Kommentar löschen
</button> </button>
+84 -123
View File
@@ -1,172 +1,133 @@
console.log("Die JavaScript-Datei wurde erfolgreich geladen!"); //console.log("editor.js wurde erfolgreich geladen!");
function initEditor() { function initEditor() {
const form = document.getElementById("editor-form"); const form = document.getElementById("editor-form");
if (!form) { if (!form) {
console.error("Skript abgebrochen: Formular nicht gefunden!"); console.error("editor.js abgebrochen: Formular nicht gefunden!");
return; return;
} else { } else {
console.log("Formular gefunden und Editor initialisiert:", form); console.log("Formular gefunden und editor.js initialisiert:", form);
} }
const container = document.getElementById("block-container"); const container = document.getElementById("block-container");
const plusButton = document.getElementById("plus-button"); const plusButton = document.getElementById("plus-button");
const popup = document.getElementById("block-popup"); const popup = document.getElementById("block-popup");
const hiddenContentInput = document.getElementById("content");
const initialImages = []; // Fortlaufender Zähler für eindeutige Block-Indizes. Wird nie wiederverwendet
// (auch nicht nach dem Löschen eines Blocks), damit sich neue und übrig
// gebliebene Blöcke beim finalen Submit nie einen Namen teilen.
let blockIndex = container.querySelectorAll(".editor-block").length;
// Ohne JS sind Textblock-/Bild-Button im Pop-up immer sichtbar und ganz normale
// Submit-Buttons. Erst mit JS blenden wir das Pop-up standardmäßig aus und
// steuern die Sichtbarkeit über den Plus-Button.
popup.classList.add("hidden");
// Pop-up umschalten bei Klick auf das Plus
plusButton.addEventListener("click", () => { plusButton.addEventListener("click", () => {
popup.classList.toggle("hidden"); popup.classList.toggle("hidden");
}); });
// Klick auf eine Block-Option im Pop-up // Klick auf "Textblock" / "Bild einfügen": lokal im DOM anlegen statt zum
popup.querySelectorAll("button").forEach(btn => { // Server zu submitten (entlastet den Server, kein Page-Reload nötig).
btn.addEventListener("click", function() { popup.querySelectorAll('[name="editor_action"]').forEach(btn => {
btn.addEventListener("click", function (e) {
e.preventDefault();
const type = this.getAttribute("data-type"); const type = this.getAttribute("data-type");
addBlockElement(type, ""); addBlockElement(type, "");
popup.classList.add("hidden"); popup.classList.add("hidden");
}); });
}); });
// Erstellt ein visuelles HTML-Element im Editor // Bereits vom Server gerenderte Blöcke (z.B. beim Bearbeiten eines bestehenden
function addBlockElement(type, value = "") { // Artikels oder nach einem Validierungsfehler) ebenfalls mit JS-Verhalten ausstatten.
const blockDiv = document.createElement("div"); container.querySelectorAll(".editor-block").forEach(blockDiv => {
blockDiv.classList.add("editor-block"); bindDeleteButton(blockDiv);
blockDiv.setAttribute("data-type", type); bindImageInput(blockDiv);
// Wenn es ein existierendes Server-Bild beim Laden ist, Pfad im globalen Array sichern
if (type === "image" && value && typeof value === 'string' && value.startsWith('uploads/')) {
initialImages.push(value);
blockDiv.setAttribute("data-value", value);
}
// Löschen-Button
const deleteBtn = document.createElement("button");
deleteBtn.type = "button";
deleteBtn.innerHTML = "✕";
deleteBtn.classList.add("delete-block-btn");
deleteBtn.addEventListener("click", () => {
// ANPASSUNG 2B: Logik hier komplett geleert. Das '✕' entfernt den Block jetzt nur noch sicher aus dem HTML.
blockDiv.remove();
}); });
blockDiv.appendChild(deleteBtn);
// Erstellt einen neuen Block inkl. echter, benannter Formularfelder
// (blocks[i][type], blocks[i][text] bzw. blocks[i][image]). Diese Felder werden
// beim finalen Submit ganz normal vom Browser als multipart/form-data verschickt
// es ist kein manuelles Zusammenbauen von JSON beim Absenden mehr nötig.
function addBlockElement(type, value = "") {
const index = blockIndex++;
const blockDiv = document.createElement("div");
blockDiv.classList.add("editor-block", "article-editor-scope");
blockDiv.setAttribute("data-index", String(index));
const typeInput = document.createElement("input");
typeInput.type = "hidden";
typeInput.name = `blocks[${index}][type]`;
typeInput.value = type;
blockDiv.appendChild(typeInput);
if (type === "text") { if (type === "text") {
const textarea = document.createElement("textarea"); const textarea = document.createElement("textarea");
textarea.name = `blocks[${index}][text]`;
textarea.placeholder = "Schreibe deinen Textblock..."; textarea.placeholder = "Schreibe deinen Textblock...";
textarea.value = value; textarea.value = value;
blockDiv.appendChild(textarea); blockDiv.appendChild(textarea);
} else if (type === "image") { } else if (type === "image") {
const fileInput = document.createElement("input"); const fileInput = document.createElement("input");
fileInput.type = "file"; fileInput.type = "file";
fileInput.name = `blocks[${index}][image]`;
fileInput.accept = "image/*"; fileInput.accept = "image/*";
blockDiv.appendChild(fileInput);
}
const imgPreview = document.createElement("img"); const deleteBtn = document.createElement("button");
deleteBtn.type = "submit";
deleteBtn.name = "editor_action";
deleteBtn.value = `delete_block:${index}`;
deleteBtn.classList.add("delete-block-btn");
deleteBtn.innerHTML = "✕";
blockDiv.appendChild(deleteBtn);
container.appendChild(blockDiv);
bindDeleteButton(blockDiv);
bindImageInput(blockDiv);
}
// Löschen-Button eines Blocks lokal abfangen: entfernt den Block nur aus dem DOM,
// statt das Formular zum Server zu senden.
function bindDeleteButton(blockDiv) {
const btn = blockDiv.querySelector(".delete-block-btn");
if (!btn) return;
btn.addEventListener("click", (e) => {
e.preventDefault();
blockDiv.remove();
});
}
// Zeigt bei Auswahl einer Bilddatei sofort eine Vorschau an. Rein optisch
// der eigentliche Datei-Upload läuft nativ über das <input type="file">.
function bindImageInput(blockDiv) {
const fileInput = blockDiv.querySelector('input[type="file"]');
if (!fileInput) return;
let imgPreview = blockDiv.querySelector(".block-image-preview");
if (!imgPreview) {
imgPreview = document.createElement("img");
imgPreview.classList.add("block-image-preview");
imgPreview.style.maxWidth = "200px"; imgPreview.style.maxWidth = "200px";
imgPreview.style.display = "block"; imgPreview.style.display = "none";
imgPreview.style.marginTop = "10px"; imgPreview.style.marginTop = "10px";
blockDiv.insertBefore(imgPreview, fileInput.nextSibling);
if (value && typeof value === 'string') {
if (value.startsWith('uploads/') || value.startsWith('data:image/')) {
imgPreview.src = value;
blockDiv.setAttribute("data-value", value);
}
} }
fileInput.addEventListener("change", function() { fileInput.addEventListener("change", function () {
if (this.files && this.files[0]) { if (this.files && this.files[0]) {
const reader = new FileReader(); const reader = new FileReader();
reader.onload = function(e) { reader.onload = (e) => {
imgPreview.src = e.target.result; imgPreview.src = e.target.result;
blockDiv.setAttribute("data-value", e.target.result); imgPreview.style.display = "block";
} };
reader.readAsDataURL(this.files[0]); reader.readAsDataURL(this.files[0]);
} }
}); });
blockDiv.appendChild(fileInput);
blockDiv.appendChild(imgPreview);
}
container.appendChild(blockDiv);
}
// beim Abschicken verbleibende Blöcke auslesen UND gelöschte Bilder ermitteln
form.addEventListener("submit", function(e) {
const blocks = [];
const currentImages = [];
// alle aktuell im Formular verbliebenen Blöcke scannen
container.querySelectorAll(".editor-block").forEach(blockDiv => {
const type = blockDiv.getAttribute("data-type");
let value = "";
if (type === "text") {
value = blockDiv.querySelector("textarea").value;
} else if (type === "image") {
const imgTag = blockDiv.querySelector("img");
if (imgTag) {
const srcValue = imgTag.getAttribute("src") || "";
// Wenn es ein neues Bild ist, nutzen wir das data-value (Base64)
if (srcValue.startsWith('data:image/')) {
value = blockDiv.getAttribute("data-value") || "";
} else {
value = srcValue;
}
}
// Pfade sammeln, die der Nutzer NICHT gelöscht hat (für den Abgleich)
if (value && value.startsWith('uploads/')) {
currentImages.push(value);
}
}
blocks.push({ type: type, value: value });
});
// das reguläre unsichtbare Content-Feld befüllen
hiddenContentInput.value = JSON.stringify(blocks);
// Differenz berechnen: Welche Bilder aus 'initialImages' fehlen in 'currentImages' ?
const deletedImages = initialImages.filter(img => !currentImages.includes(img));
// das 'deleted_images'-Feld dynamisch befüllen und an den Controller senden
let deletedInput = document.getElementById("deleted-images");
if (!deletedInput) {
deletedInput = document.createElement("input");
deletedInput.type = "hidden";
deletedInput.id = "deleted-images";
deletedInput.name = "deleted_images";
form.appendChild(deletedInput);
}
deletedInput.value = JSON.stringify(deletedImages);
});
// Existierende Blöcke laden (stellt alte Daten aus der Session wieder her)
try {
const initialBlocks = JSON.parse(hiddenContentInput.value.trim());
if (Array.isArray(initialBlocks)) {
initialBlocks.forEach(b => {
if (b.type === "image" && b.value && typeof b.value === 'string' && !b.value.startsWith('data:image/')) {
let cleanPath = b.value.trim().replace(/\\\//g, '/'); // Verwandelt \/ in /
initialImages.push(cleanPath);
addBlockElement(b.type, cleanPath);
} else {
addBlockElement(b.type, b.value);
}
});
console.log("Erfolgreich registrierte Start-Bilder:", initialImages);
}
} catch(e) {
if (hiddenContentInput.value.trim() !== "") {
addBlockElement("text", hiddenContentInput.value);
}
} }
} }
+29
View File
@@ -0,0 +1,29 @@
document.addEventListener("DOMContentLoaded", function () {
const deleteAccountForm = document.querySelector(".confirm-delete-account");
if (deleteAccountForm) {
deleteAccountForm.addEventListener("submit", function (event) {
if (!confirm("Möchtest du deinen Account und alle deine Beiträge wirklich unwiderruflich löschen?")) {
event.preventDefault();
}
});
}
const deleteArticleForms = document.querySelectorAll(".confirm-delete-article");
deleteArticleForms.forEach(function (form) {
form.addEventListener("submit", function (event) {
if (!confirm("Möchtest du diesen Artikel wirklich löschen?")) {
event.preventDefault();
}
});
});
});
+44 -58
View File
@@ -5,24 +5,60 @@ if (session_status() === PHP_SESSION_NONE) {
require_once '../model/LocalArticleManager.php'; require_once '../model/LocalArticleManager.php';
require_once '../model/ArticleManager.php'; require_once '../model/ArticleManager.php';
require_once '../validator/article-validator.php'; require_once '../validator/article-validator.php';
require_once '../../includes/article-block-helper.php';
if (!isset($_SESSION["user"])) { if (!isset($_SESSION["user"])) {
header("Location: index.php?pfad=login"); header("Location: index.php?pfad=login");
exit(); exit();
} }
if ($_SERVER["REQUEST_METHOD"] === "POST") {
if ($_SERVER["REQUEST_METHOD"] === "POST") {
$uploadDir = __DIR__ . '/../../uploads/';
if (!file_exists($uploadDir)) {
mkdir($uploadDir, 0755, true);
}
// Formularzustand (Titel/Tags/Kategorie/Blöcke) immer sichern, damit er nach einem
// Redirect (PRG-Pattern oder Validierungsfehler) wieder angezeigt werden kann.
$_SESSION["old_title"] = $_POST["title"] ?? ''; $_SESSION["old_title"] = $_POST["title"] ?? '';
$_SESSION["old_content"] = $_POST["content"] ?? '';
$_SESSION["old_category"] = $_POST["category"] ?? ''; $_SESSION["old_category"] = $_POST["category"] ?? '';
$_SESSION["old_tags"] = $_POST["tags"] ?? ''; $_SESSION["old_tags"] = $_POST["tags"] ?? '';
if(!isset($_POST["title"]) ||!isset($_POST["content"]) || !isset($_POST["category"])){ $blocks = rebuildBlocksFromPost($_POST['blocks'] ?? [], $_FILES['blocks'] ?? [], $uploadDir);
$_SESSION["old_content"] = json_encode($blocks, JSON_UNESCAPED_UNICODE);
// ---------------------------------------------------------------------
// Zwischenspeichern
// ---------------------------------------------------------------------
if (isset($_POST['editor_action']) && $_POST['editor_action'] !== '') {
$action = $_POST['editor_action'];
if ($action === 'add_text') {
$blocks[] = ['type' => 'text', 'value' => ''];
} elseif ($action === 'add_image') {
$blocks[] = ['type' => 'image', 'value' => ''];
} elseif (str_starts_with($action, 'delete_block:')) {
$deleteIndex = (int) substr($action, strlen('delete_block:'));
unset($blocks[$deleteIndex]);
$blocks = array_values($blocks);
}
$_SESSION["old_content"] = json_encode($blocks, JSON_UNESCAPED_UNICODE);
header("location: ../../index.php?pfad=createArticle");
exit();
}
// ---------------------------------------------------------------------
// Echtes Veröffentlichen
// ---------------------------------------------------------------------
if (!isset($_POST["title"]) || !isset($_POST["category"])) {
$_SESSION["message"] = "missing_parameters"; $_SESSION["message"] = "missing_parameters";
header("location: ../../index.php?pfad=createArticle"); header("location: ../../index.php?pfad=createArticle");
exit(); exit();
} else { } else {
$title = $_POST["title"]; $title = $_POST["title"];
$content = $_POST["content"]; $content = json_encode($blocks, JSON_UNESCAPED_UNICODE);
$author = $_SESSION["user_email"]; $author = $_SESSION["user_email"];
$category = $_POST["category"]; $category = $_POST["category"];
$tags = $_POST['tags'] ?? ''; $tags = $_POST['tags'] ?? '';
@@ -63,67 +99,17 @@ if (!isset($_SESSION["user"])) {
$cleanedTags = implode(',', $cleanedTags); $cleanedTags = implode(',', $cleanedTags);
} }
// ----------------- Base64-Bilder verarbeiten und auf Server speichern -----------------
$blocks = json_decode($content, true);
$uploadDir = __DIR__ . '/../../uploads/';
if (!file_exists($uploadDir)) {
mkdir($uploadDir, 0755, true);
}
if (is_array($blocks)) {
foreach ($blocks as &$block) {
// sicherstellen, dass 'type' und 'value' existieren:
if (isset($block['type']) && isset($block['value']) && $block['type'] === 'image' && str_starts_with($block['value'], 'data:image/')) {
// Base64-String zerlegen
$parts = explode(',', $block['value']);
// falls der String korrupt ist und kein Komma hat
if (count($parts) < 2) {
continue;
}
$metadata = $parts[0];
$base64Data = $parts[1];
// Dateiendung ermitteln
preg_match('/data:image\/(?<extension>.*?);/', $metadata, $matches);
$extension = $matches['extension'] ?? 'jpg';
if ($extension === 'jpeg') {
$extension = 'jpg';
}
// Eindeutigen Dateinamen generieren
$fileName = 'img_' . uniqid() . '.' . $extension;
$filePath = $uploadDir . $fileName;
// Datei im /uploads speichern:
if (file_put_contents($filePath, base64_decode($base64Data)) !== false) {
// temporären Base64-String durch den echten Pfad ersetzen
$block['value'] = 'uploads/' . $fileName;
} else {
$_SESSION["message"] = "image_upload_error";
header("location: ../../index.php?pfad=createArticle");
exit();
}
}
}
unset($block);
}
// Aktualisiertes Array wieder in JSON konvertieren
$finalContent = json_encode($blocks, JSON_UNESCAPED_UNICODE);
// ----------------- Übertragung der validierten Daten in ArticleManager: --------------------------- // ----------------- Übertragung der validierten Daten in ArticleManager: ---------------------------
try { try {
$articleManager = ArticleManager::getInstance(); $articleManager = ArticleManager::getInstance();
// $content enthält bereits die finalen "uploads/..."-Pfade (kein Base64 mehr),
// da rebuildBlocksFromPost() Datei-Uploads sofort verarbeitet.
$articleManager->addArticle($title, $content, $author, $category, $cleanedTags); $articleManager->addArticle($title, $content, $author, $category, $cleanedTags);
// Formulardaten nach erfolgreichem Erstellen aus der Session löschen // Formulardaten nach erfolgreichem Erstellen aus der Session löschen
unset($_SESSION["old_title"], $_SESSION["old_content"], $_SESSION["old_category"], $_SESSION["old_tags"]); unset($_SESSION["old_title"], $_SESSION["old_content"], $_SESSION["old_category"], $_SESSION["old_tags"]);
} catch (\Throwable $e){ } catch (\Throwable $e) {
$_SESSION["message"] = "internal_error"; $_SESSION["message"] = "internal_error";
header("location: ../../index.php?pfad=createArticle"); header("location: ../../index.php?pfad=createArticle");
exit(); exit();
@@ -134,5 +120,5 @@ if (!isset($_SESSION["user"])) {
header("location: ../../index.php"); header("location: ../../index.php");
exit(); exit();
} }
} }
?> ?>
+77 -75
View File
@@ -7,17 +7,14 @@ require_once '../model/LocalArticleManager.php';
require_once '../model/ArticleManager.php'; require_once '../model/ArticleManager.php';
require_once '../model/Article.php'; require_once '../model/Article.php';
require_once '../validator/article-validator.php'; require_once '../validator/article-validator.php';
require_once '../../includes/article-block-helper.php';
if (!isset($_SESSION["user_email"])) { if (!isset($_SESSION["user"])) {
header("Location: index.php?pfad=login"); header("Location: index.php?pfad=login");
exit(); exit();
} }
if ($_SERVER["REQUEST_METHOD"] === "POST") { if ($_SERVER["REQUEST_METHOD"] === "POST") {
$_SESSION["old_title"] = $_POST["title"] ?? '';
$_SESSION["old_content"] = $_POST["content"] ?? '';
$_SESSION["old_category"] = $_POST["category"] ?? '';
$_SESSION["old_tags"] = $_POST["tags"] ?? '';
if (isset($_GET["id"]) && !empty($_GET["id"])) { if (isset($_GET["id"]) && !empty($_GET["id"])) {
$id = $_GET["id"]; $id = $_GET["id"];
@@ -41,13 +38,53 @@ if ($_SERVER["REQUEST_METHOD"] === "POST") {
exit(); exit();
} }
if (!isset($_POST["title"]) ||!isset($_POST["content"]) || !isset($_POST["category"])){ $uploadDir = __DIR__ . '/../../uploads/';
if (!file_exists($uploadDir)) {
mkdir($uploadDir, 0755, true);
}
// Formularzustand (Titel/Tags/Kategorie/Blöcke) immer sichern, damit er nach einem
// Redirect (PRG-Pattern oder Validierungsfehler) wieder angezeigt werden kann.
$_SESSION["old_title"] = $_POST["title"] ?? '';
$_SESSION["old_tags"] = $_POST["tags"] ?? '';
$_SESSION["old_category"] = $_POST["category"] ?? '';
$blocks = rebuildBlocksFromPost($_POST['blocks'] ?? [], $_FILES['blocks'] ?? [], $uploadDir);
$_SESSION["old_content"] = json_encode($blocks, JSON_UNESCAPED_UNICODE);
// ---------------------------------------------------------------------
// Zwischen-Schritt: Block hinzufügen oder entfernen (kein echtes Speichern).
// Wird bei aktivem JavaScript per preventDefault() abgefangen und lokal im
// DOM erledigt (js/editor.js) ohne JS läuft dieser Server-Roundtrip.
// ---------------------------------------------------------------------
if (isset($_POST['editor_action']) && $_POST['editor_action'] !== '') {
$action = $_POST['editor_action'];
if ($action === 'add_text') {
$blocks[] = ['type' => 'text', 'value' => ''];
} elseif ($action === 'add_image') {
$blocks[] = ['type' => 'image', 'value' => ''];
} elseif (str_starts_with($action, 'delete_block:')) {
$deleteIndex = (int) substr($action, strlen('delete_block:'));
unset($blocks[$deleteIndex]);
$blocks = array_values($blocks);
}
$_SESSION["old_content"] = json_encode($blocks, JSON_UNESCAPED_UNICODE);
header("location: ../../index.php?pfad=updateArticle&id=$id");
exit();
}
// ---------------------------------------------------------------------
// Echtes Speichern
// ---------------------------------------------------------------------
if (!isset($_POST["title"]) || !isset($_POST["category"])) {
$_SESSION["message"] = "missing_parameters"; $_SESSION["message"] = "missing_parameters";
header("location: ../../index.php?pfad=updateArticle&id=$id"); header("location: ../../index.php?pfad=updateArticle&id=$id");
exit(); exit();
}else{ } else {
$title = $_POST["title"]; $title = $_POST["title"];
$content = $_POST["content"]; $content = json_encode($blocks, JSON_UNESCAPED_UNICODE);
$author = $_SESSION["user_email"]; $author = $_SESSION["user_email"];
$category = $_POST["category"]; $category = $_POST["category"];
$tags = $_POST['tags'] ?? ''; $tags = $_POST['tags'] ?? '';
@@ -88,91 +125,56 @@ if ($_SERVER["REQUEST_METHOD"] === "POST") {
$cleanedTags = implode(',', $cleanedTags); $cleanedTags = implode(',', $cleanedTags);
} }
// --------------------------------------- Base64-Bilder speichern --------------------------------------------- // ----------------- Verwaiste Bilder aufräumen -----------------
$blocks = json_decode($content, true); // Bilder, die im alten (gespeicherten) Content vorkamen, im neuen aber nicht
$uploadDir = __DIR__ . '/../../uploads/'; // mehr referenziert werden, wurden vom Nutzer entfernt oder ersetzt -> löschen.
// Hinweis/TODO: Bilder, die innerhalb derselben Bearbeitungs-Sitzung neu
if (!file_exists($uploadDir)) { // hochgeladen und noch vor dem finalen Speichern wieder entfernt wurden,
mkdir($uploadDir, 0755, true); // werden hierüber nicht erfasst (sie tauchten nie im alten Content auf) und
} // bleiben als Datei liegen. Für eine vollständige Bereinigung würde sich ein
// regelmäßiger Cleanup-Job anbieten, der verwaiste Dateien im uploads/-Ordner
// ----------------- Gelöschte Bilder über die JS-Löschliste entfernen ----------------- TODO: Gelöschte Bilder über die JS-Löschliste entfernen // mit den in der Datenbank referenzierten Pfaden abgleicht.
/*if (isset($_POST['deleted_images'])) { $oldBlocks = json_decode($article->getContent(), true);
$deletedImages = json_decode($_POST['deleted_images'], true); $oldImagePaths = [];
if (is_array($oldBlocks)) {
// Wir ermitteln den physisch echten, absoluten Pfad zum uploads-Ordner auf der Festplatte foreach ($oldBlocks as $oldBlock) {
$uploadDir = realpath(__DIR__ . '/../../uploads') . DIRECTORY_SEPARATOR; if (($oldBlock['type'] ?? '') === 'image'
&& !empty($oldBlock['value'])
if (is_array($deletedImages)) { && is_string($oldBlock['value'])
foreach ($deletedImages as $imagePath) { && str_starts_with($oldBlock['value'], 'uploads/')) {
// Nur den reinen Dateinamen heraustrennen (z.B. img_65a123.jpg) $oldImagePaths[] = $oldBlock['value'];
$filename = basename($imagePath);
$fullDeletePath = $uploadDir . $filename;
// Debugging & Löschen:
if (file_exists($fullDeletePath)) {
// Versuchen zu löschen. Wenn es fehlschlägt, Fehlermeldung erzwingen
if (!@unlink($fullDeletePath)) {
$error = error_get_last();
die("Datei existiert, aber PHP darf sie nicht löschen! Grund: " . $error['message']);
}
} else {
// Wenn PHP die Datei an diesem Pfad nicht findet, brechen wir zum Debuggen ab
// die("PHP findet die Datei nicht unter dem Pfad: " . $fullDeletePath);
} }
} }
} }
}*/
// ----------------------- NEU hinzugefügte Base64-Bilder: -------------------------- $newImagePaths = [];
if (is_array($blocks)) { foreach ($blocks as $block) {
foreach ($blocks as &$block) { if (($block['type'] ?? '') === 'image' && !empty($block['value'])) {
// Prüfen, ob der Block ein Bild ist und ein NEUES Bild (Base64-Format) enthält $newImagePaths[] = $block['value'];
if (isset($block['type']) && isset($block['value']) && $block['type'] === 'image' && is_string($block['value'])) {
if (str_starts_with($block['value'], 'data:image/')) {
$parts = explode(',', $block['value']);
if (count($parts) >= 2) {
$metadata = $parts[0];
$base64Data = $parts[1];
preg_match('/data:image\/(?<extension>.*?);/', $metadata, $matches);
$extension = $matches['extension'] ?? 'jpg';
if ($extension === 'jpeg') { $extension = 'jpg'; }
$fileName = 'img_' . uniqid() . '.' . $extension;
$filePath = $uploadDir . $fileName;
if (file_put_contents($filePath, base64_decode($base64Data)) !== false) {
$block['value'] = 'uploads/' . $fileName;
} else {
$_SESSION["message"] = "image_upload_error";
header("location: ../../index.php?pfad=updateArticle&id=$id");
exit();
} }
} }
}
}
}
unset($block);
}
// Aktualisiertes Array wieder in JSON konvertieren $orphanedImages = array_diff($oldImagePaths, $newImagePaths);
$finalContent = json_encode($blocks, JSON_UNESCAPED_UNICODE); foreach ($orphanedImages as $orphanedImage) {
$absolutePath = __DIR__ . '/../../' . $orphanedImage;
if (is_file($absolutePath)) {
@unlink($absolutePath);
}
}
// ----------------- Übertragung der validierten Daten in ArticleManager: --------------------------- // ----------------- Übertragung der validierten Daten in ArticleManager: ---------------------------
try { try {
$articleManager = ArticleManager::getInstance(); $articleManager = ArticleManager::getInstance();
$article = $articleManager->getArticle($id); $article = $articleManager->getArticle($id);
$article->setTitle($title); $article->setTitle($title);
$article->setContent($finalContent); $article->setContent($content);
$article->setCategory($category); $article->setCategory($category);
$article->setTags($cleanedTags); $article->setTags($cleanedTags);
$articleManager->updateArticle($id ,$article, $author); $articleManager->updateArticle($id, $article, $author);
unset($_SESSION["old_title"], $_SESSION["old_content"], $_SESSION["old_category"], $_SESSION["old_tags"]); unset($_SESSION["old_title"], $_SESSION["old_content"], $_SESSION["old_category"], $_SESSION["old_tags"]);
} catch (\Throwable $e){ } catch (\Throwable $e) {
$_SESSION["message"] = $e->getMessage(); $_SESSION["message"] = $e->getMessage();
header("location: ../../index.php?pfad=updateArticle&id=$id"); header("location: ../../index.php?pfad=updateArticle&id=$id");
exit(); exit();
+6 -5
View File
@@ -315,16 +315,15 @@ class DatabaseArticleManager implements ArticleManagerDAO {
FROM articles FROM articles
WHERE title LIKE :keyword WHERE title LIKE :keyword
OR content LIKE :keyword OR content LIKE :keyword
OR tags LIKE :keyword"; OR tags LIKE :keyword;";
$command = $db->prepare($sql); $command = $db->prepare($sql);
if (!$command) { if (!$command) {
throw new InternalServerErrorException("internal_error"); throw new InternalServerErrorException("internal_error");
} }
// Wildcards für die Suche hinzufügen // Wildcards für die SQL-Suche hinzufügen
$searchParam = '%' . $cleankeyword . '%'; $searchParam = '%' . $cleankeyword . '%';
$success = $command->execute([ $success = $command->execute([
":keyword" => $searchParam ":keyword" => $searchParam
]); ]);
@@ -337,10 +336,11 @@ class DatabaseArticleManager implements ArticleManagerDAO {
$filteredArticles = []; $filteredArticles = [];
foreach ($rows as $row) { foreach ($rows as $row) {
$likes = $this->getLikesForArticle(intval($row['id'])); $articleId = intval($row['id']);
$likes = $this->getLikesForArticle($articleId);
$filteredArticles[] = new Article( $filteredArticles[] = new Article(
intval($row['id']), $articleId,
$row['title'] ?? '', $row['title'] ?? '',
$row['content'] ?? '', $row['content'] ?? '',
$row['author'] ?? '', $row['author'] ?? '',
@@ -358,6 +358,7 @@ class DatabaseArticleManager implements ArticleManagerDAO {
} }
} }
/** /**
* Holt alle User-IDs, die einen bestimmten Beitrag geliked haben. * Holt alle User-IDs, die einen bestimmten Beitrag geliked haben.
* *
+6 -1
View File
@@ -234,9 +234,14 @@ class DatabaseInitializer {
// Bestimmt per Zufall einen Autor aus dem Pool der gültigen E-Mails // Bestimmt per Zufall einen Autor aus dem Pool der gültigen E-Mails
$randomAuthor = $availableEmails[array_rand($availableEmails)]; $randomAuthor = $availableEmails[array_rand($availableEmails)];
// Text in (blocks[i][type]/[text]/[image])-Format umwandeln:
$content = json_encode([
['type' => 'text', 'value' => $article[1]]
], JSON_UNESCAPED_UNICODE);
$articleInsertStmt->execute([ $articleInsertStmt->execute([
':title' => $article[0], ':title' => $article[0],
':content' => $article[1], ':content' => $content,
':author' => $randomAuthor, ':author' => $randomAuthor,
':category' => $article[2], ':category' => $article[2],
':tags' => $article[3] ':tags' => $article[3]