diff --git a/content/createArticle.php b/content/createArticle.php index 7dd9274..6554348 100644 --- a/content/createArticle.php +++ b/content/createArticle.php @@ -17,8 +17,29 @@ if (!isset($_SESSION["user"])) { - + +
+ + +
+ + +
+ + + diff --git a/includes/alertMessages.php b/includes/alertMessages.php index f88f3ae..72d63d6 100644 --- a/includes/alertMessages.php +++ b/includes/alertMessages.php @@ -63,6 +63,11 @@ Dein Beitrag wurde erfolgreich veröffentlicht!

+ +

+ Das Bild konnte nicht hochgeladen werden. Bitte versuche es erneut oder verwende ein anderes Bildformat. +

+ diff --git a/index.php b/index.php index 175830a..5df0b58 100644 --- a/index.php +++ b/index.php @@ -42,6 +42,7 @@ if ($pfad === "deleteAccount") { + @@ -50,6 +51,8 @@ if ($pfad === "deleteAccount") { + + EduForge diff --git a/js/editor.js b/js/editor.js new file mode 100644 index 0000000..b22027c --- /dev/null +++ b/js/editor.js @@ -0,0 +1,119 @@ +/** + * Editor-Steuerung für dynamische Inhaltsblöcke + */ +document.addEventListener("DOMContentLoaded", function() { + const form = document.getElementById("editor-form"); + + // Falls das Formular auf der aktuellen Seite nicht existiert, Skript abbrechen + if (!form) { + return; + } + + const container = document.getElementById("block-container"); + const plusButton = document.getElementById("plus-button"); + const popup = document.getElementById("block-popup"); + const hiddenContentInput = document.getElementById("content"); + + // Pop-up umschalten bei Klick auf das Plus + plusButton.addEventListener("click", () => { + popup.classList.toggle("hidden"); + }); + + // Klick auf eine Block-Option im Pop-up + popup.querySelectorAll("button").forEach(btn => { + btn.addEventListener("click", function() { + const type = this.getAttribute("data-type"); + addBlockElement(type, ""); + popup.classList.add("hidden"); + }); + }); + + // Erstellt ein visuelles HTML-Element im Editor + function addBlockElement(type, value = "") { + const blockDiv = document.createElement("div"); + blockDiv.classList.add("editor-block"); + blockDiv.setAttribute("data-type", type); + + // Löschen-Button für den Block + const deleteBtn = document.createElement("button"); + deleteBtn.type = "button"; + deleteBtn.innerHTML = "✕"; + deleteBtn.classList.add("delete-block-btn"); + deleteBtn.addEventListener("click", () => { + blockDiv.remove(); + }); + blockDiv.appendChild(deleteBtn); + + if (type === "text") { + const textarea = document.createElement("textarea"); + textarea.placeholder = "Schreibe deinen Textblock..."; + textarea.value = value; + blockDiv.appendChild(textarea); + } else if (type === "image") { + const fileInput = document.createElement("input"); + fileInput.type = "file"; + fileInput.accept = "image/*"; + + const imgPreview = document.createElement("img"); + imgPreview.style.maxWidth = "200px"; + imgPreview.style.display = "block"; + imgPreview.style.marginTop = "10px"; + + // Falls bereits ein Bildpfad existiert (z.B. nach einem Reload bei Validierungsfehlern) + if (value && typeof value === 'string' && value.startsWith('uploads/')) { + imgPreview.src = value; + blockDiv.setAttribute("data-value", value); + } + + fileInput.addEventListener("change", function() { + if (this.files && this.files[0]) { + const reader = new FileReader(); + reader.onload = function(e) { + imgPreview.src = e.target.result; + // Temporäre Speicherung des Base64-Strings im HTML-Attribut + blockDiv.setAttribute("data-value", e.target.result); + } + reader.readAsDataURL(this.files[0]); + } + }); + + blockDiv.appendChild(fileInput); + blockDiv.appendChild(imgPreview); + } + + container.appendChild(blockDiv); + } + + // Beim Abschicken: HTML-Blöcke auslesen und als JSON-String serialisieren + form.addEventListener("submit", function(e) { + const blocks = []; + 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") { + value = blockDiv.getAttribute("data-value") || ""; + } + + blocks.push({ type: type, value: value }); + }); + + // JSON-Daten in das unsichtbare Formularfeld schreiben + hiddenContentInput.value = JSON.stringify(blocks); + }); + + // Existierende Blöcke laden (stellt alte Daten aus der Session wieder her) + try { + const initialBlocks = JSON.parse(hiddenContentInput.value); + if (Array.isArray(initialBlocks)) { + initialBlocks.forEach(b => addBlockElement(b.type, b.value)); + } + } catch(e) { + // Fallback für alten Reintext aus Altbeständen + if (hiddenContentInput.value.trim() !== "") { + addBlockElement("text", hiddenContentInput.value); + } + } +}); diff --git a/php/controller/createArticle-controller.php b/php/controller/createArticle-controller.php index 53c7f3a..6c39386 100644 --- a/php/controller/createArticle-controller.php +++ b/php/controller/createArticle-controller.php @@ -64,10 +64,54 @@ require_once '../validator/article-validator.php'; $cleanedTags = array_unique($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); + } + + foreach ($blocks as &$block) { + // Base64-Format-Prüfung: + if ($block['type'] === 'image' && str_starts_with($block['value'], 'data:image/')) { + + // Base64-String zerlegen: + log_alert("Bild erkannt, verarbeite..."); + $parts = explode(',', $block['value']); + $metadata = $parts[0]; + $base64Data = $parts[1]; + + // Dateiendung ermitteln + preg_match('/data:image\/(?.*?);/', $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: --------------------------- try { $articleManager = ArticleManager::getInstance(); - $articleManager->addArticle($title, $content, $author, $category, $cleanedTags); + $articleManager->addArticle($title, $finalContent, $author, $category, $cleanedTags); // Formulardaten nach erfolgreichem Erstellen aus der Session löschen unset($_SESSION["old_title"], $_SESSION["old_content"], $_SESSION["old_category"], $_SESSION["old_tags"]); @@ -84,7 +128,4 @@ require_once '../validator/article-validator.php'; exit(); } } - - - ?> \ No newline at end of file diff --git a/php/model/ArticleManager.php b/php/model/ArticleManager.php index 435ac90..e20caa0 100644 --- a/php/model/ArticleManager.php +++ b/php/model/ArticleManager.php @@ -19,7 +19,7 @@ class ArticleManager public static function getInstance() { $articleManager = DatabaseArticleManager::getInstance(); // Hier kann zwischen dem lokalen und datenbankbasiertem ArticleManager gewechselt werden. - + /* // 100 fiktionale Fachbeiträge: $dummyArticles = [ // --- INFORMATIK & MATHE (1-20) --- @@ -154,7 +154,7 @@ class ArticleManager ); } } - + */ return $articleManager; } diff --git a/php/validator/article-validator.php b/php/validator/article-validator.php index d708258..2f4c877 100644 --- a/php/validator/article-validator.php +++ b/php/validator/article-validator.php @@ -33,19 +33,18 @@ function articleTitleValidator($title) } /** - * Prüft, ob der Contenttext 10-7000 Zeichen enthält. - * @param $content + * Prüft, ob der übergebene Content ein formal valider JSON-String ist. + * @param mixed $content Der zu prüfende Inhalt * @return bool */ function articleContentValidator($content) { - $content = trim($content); - $zeichenAnzahl = mb_strlen($content); - if ($zeichenAnzahl <= 7000 && $zeichenAnzahl >= 10) { - return true; - }else{ + if (!is_string($content)) { return false; } + + // Prüft direkt, ob der String valides JSON enthält + return json_validate($content); } /**