From 80f92a384e92bb2a379dcb06c6a5290fc57294e3 Mon Sep 17 00:00:00 2001
From: NOrtmann1
Date: Sun, 14 Jun 2026 10:44:17 +0200
Subject: [PATCH 01/44] erweiterter Beitragseditor
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
content ist nun im json-Format
Bilder können hochgeladen werden
Textblöcke können im Editor angehangen werden
---
content/createArticle.php | 23 +++-
includes/alertMessages.php | 5 +
index.php | 3 +
js/editor.js | 119 ++++++++++++++++++++
php/controller/createArticle-controller.php | 49 +++++++-
php/model/ArticleManager.php | 4 +-
php/validator/article-validator.php | 13 +--
7 files changed, 202 insertions(+), 14 deletions(-)
create mode 100644 js/editor.js
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);
}
/**
From ec9bc3fe1f6a69b01cfd723d0ea1d3c89f7e6d3e Mon Sep 17 00:00:00 2001
From: NOrtmann1
Date: Sun, 14 Jun 2026 10:53:21 +0200
Subject: [PATCH 02/44] css
---
content/createArticle.php | 25 ++++---
css/createArticle.css | 133 ++++++++++++++++++++++++++++++++++++++
2 files changed, 145 insertions(+), 13 deletions(-)
diff --git a/content/createArticle.php b/content/createArticle.php
index 6554348..999ad0b 100644
--- a/content/createArticle.php
+++ b/content/createArticle.php
@@ -22,24 +22,23 @@ if (!isset($_SESSION["user"])) {
-
-
-