Compare commits
45 Commits
f1a32120d6
..
CSRF
| Author | SHA1 | Date | |
|---|---|---|---|
| a785d862d8 | |||
| a728a8a556 | |||
| ae34afda15 | |||
| 59e0b9f111 | |||
| cd50840b19 | |||
| 3bce65a1a5 | |||
| 50b29c96d6 | |||
| d5fc5f3065 | |||
| 0427d52a58 | |||
| cc804757ff | |||
| b55a6592f0 | |||
| e12a98cc98 | |||
| 320141855a | |||
| 12801344f1 | |||
| 83387de103 | |||
| dfb8e1dc89 | |||
| bdbc38a339 | |||
| e6356525b9 | |||
| ca74690952 | |||
| 75f9fdbeb8 | |||
| 81a27135d5 | |||
| a58576637a | |||
| 997957a5d3 | |||
| ec794db706 | |||
| ba749bca68 | |||
| 12a6af55b0 | |||
| 400f018104 | |||
| 80f4b623f9 | |||
| 6546d264b0 | |||
| e7e120e5b8 | |||
| ec66889a1f | |||
| f1703a476e | |||
| bac296b58a | |||
| bfb2d2b0d8 | |||
| ad9a00fd49 | |||
| 42faab17e8 | |||
| 1da4842847 | |||
| afd90086d8 | |||
| a7a54f877c | |||
| 9b9f71d21c | |||
| 4b84f0ab09 | |||
| 8c8b148bf4 | |||
| 6bf335ce46 | |||
| 144a1015f1 | |||
| c0a94b2eee |
Generated
+1
-1
@@ -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-261.25134.95">
|
||||||
<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>"</identifier-quote-string>
|
<identifier-quote-string>"</identifier-quote-string>
|
||||||
|
|||||||
+59
-19
@@ -4,12 +4,30 @@ 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">
|
||||||
|
|
||||||
|
<?php csrf_field(); ?>
|
||||||
|
|
||||||
<main class="editor-main">
|
<main class="editor-main">
|
||||||
<?php include_once "includes/alertMessages.php"?>
|
<?php include_once "includes/alertMessages.php"?>
|
||||||
@@ -18,27 +36,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); ?>">
|
||||||
|
|
||||||
<!-- Plus-Button und das Pop-up-Menü -->
|
<?php if ($blockType === 'text'): ?>
|
||||||
<div id="add-block-control" class="article-editor-scope add-block-control">
|
<textarea name="blocks[<?php echo (int)$i; ?>][text]"
|
||||||
<button type="button" id="plus-button" class="article-editor-scope plus-button">+</button>
|
placeholder="Schreibe deinen Textblock..."><?php echo htmlspecialchars($block['value'] ?? ''); ?></textarea>
|
||||||
<div id="block-popup" class="article-editor-scope block-popup hidden">
|
<?php else: /* image */ ?>
|
||||||
<button type="button" data-type="text">Textblock</button>
|
<?php if (!empty($block['value'])): ?>
|
||||||
<button type="button" data-type="image">Bild einfügen</button>
|
<img src="<?php echo htmlspecialchars($block['value']); ?>"
|
||||||
</div>
|
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>
|
</div>
|
||||||
|
|
||||||
<!-- Unsichtbares Textfeld, das die JSON-Daten hält und an den Controller postet -->
|
<div id="add-block-control" class="article-editor-scope add-block-control">
|
||||||
<textarea id="content" name="content" style="display:none;"><?php
|
<button type="button" id="plus-button" class="article-editor-scope plus-button">+</button>
|
||||||
if (isset($_SESSION['old_content']) && !empty($_SESSION['old_content'])){
|
<div id="block-popup" class="article-editor-scope block-popup">
|
||||||
echo htmlspecialchars($_SESSION['old_content']);
|
<button type="submit" name="editor_action" value="add_text" data-type="text">Textblock</button>
|
||||||
unset($_SESSION['old_content']);
|
<button type="submit" name="editor_action" value="add_image" data-type="image">Bild einfügen</button>
|
||||||
} else {
|
</div>
|
||||||
echo '[]'; // Standardmäßig ein leeres JSON-Array
|
</div>
|
||||||
}
|
|
||||||
?></textarea>
|
|
||||||
</main>
|
</main>
|
||||||
|
|
||||||
<!-- Seitenleiste -->
|
<!-- Seitenleiste -->
|
||||||
|
|||||||
+5
-1
@@ -18,6 +18,8 @@ $isEditMode = (isset($_GET["edit"]) && $_GET["edit"] === "1") || !empty($error);
|
|||||||
|
|
||||||
<form method="post" action="index.php?pfad=profile">
|
<form method="post" action="index.php?pfad=profile">
|
||||||
|
|
||||||
|
<?php csrf_field(); ?>
|
||||||
|
|
||||||
<label class="input-label">Vorname</label>
|
<label class="input-label">Vorname</label>
|
||||||
<input type="text"
|
<input type="text"
|
||||||
name="vorname"
|
name="vorname"
|
||||||
@@ -82,9 +84,10 @@ $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">
|
||||||
|
<?php csrf_field(); ?>
|
||||||
<button type="submit"
|
<button type="submit"
|
||||||
class="button"
|
class="button"
|
||||||
onclick="return confirm('Möchtest du deinen Account wirklich unwiderruflich löschen?');">
|
onclick="return confirm('Möchtest du deinen Account und alle deine Beiträge wirklich unwiderruflich löschen?');">
|
||||||
Account löschen
|
Account löschen
|
||||||
</button>
|
</button>
|
||||||
</form>
|
</form>
|
||||||
@@ -155,6 +158,7 @@ $isEditMode = (isset($_GET["edit"]) && $_GET["edit"] === "1") || !empty($error);
|
|||||||
</a>
|
</a>
|
||||||
|
|
||||||
<form action="php/controller/deleteArticle-controller.php" method="POST">
|
<form action="php/controller/deleteArticle-controller.php" method="POST">
|
||||||
|
<?php csrf_field(); ?>
|
||||||
<input type="hidden"
|
<input type="hidden"
|
||||||
name="id"
|
name="id"
|
||||||
value="<?php echo htmlspecialchars($userArticle->getID()); ?>">
|
value="<?php echo htmlspecialchars($userArticle->getID()); ?>">
|
||||||
|
|||||||
+32
-17
@@ -45,26 +45,31 @@ if ($replyAuthor === null) {
|
|||||||
<!-- Metadaten & Titel -->
|
<!-- Metadaten & Titel -->
|
||||||
<div class="article-view-top-section">
|
<div class="article-view-top-section">
|
||||||
|
|
||||||
<div class="category-and-likes-row">
|
<div class="category-and-likes-row">
|
||||||
<?php if (isset($category) && !empty($category)): ?>
|
<?php if (isset($category) && !empty($category)): ?>
|
||||||
<span class="article-view-category"><?php echo htmlspecialchars($category); ?></span>
|
<span class="article-view-category"><?php echo htmlspecialchars($category); ?></span>
|
||||||
<?php endif; ?>
|
<?php endif; ?>
|
||||||
|
|
||||||
<!-- Like-Anzeige und dynamischer Like-Button -->
|
<!-- Like-Anzeige und dynamischer Like-Button -->
|
||||||
<?php if (isset($articleObj) && $articleObj !== null): ?>
|
<?php if (isset($articleObj) && $articleObj !== null): ?>
|
||||||
<div class="article-view-likes">
|
<div class="article-view-likes">
|
||||||
<span>❤️ <span class="like-count"><?php echo $articleObj->getLikeCount(); ?></span></span>
|
<span>❤️ <span class="like-count"><?php echo $articleObj->getLikeCount(); ?></span></span>
|
||||||
|
|
||||||
<?php if (isset($_SESSION["user_email"])): ?>
|
<?php if (isset($_SESSION["user_email"])): ?>
|
||||||
<a href="php/controller/like-controller.php?id=<?php echo $articleObj->getId(); ?>" class="like-toggle-btn">
|
<form method="post"
|
||||||
|
action="php/controller/like-controller.php?id=<?php echo $articleObj->getId(); ?>"
|
||||||
|
class="like-toggle-form">
|
||||||
|
<?php csrf_field(); ?>
|
||||||
|
<button type="submit" class="like-toggle-btn">
|
||||||
<?php echo $articleObj->hasLiked($_SESSION["user_email"]) ? '👎 Gefällt mir nicht mehr' : '👍 Gefällt mir'; ?>
|
<?php echo $articleObj->hasLiked($_SESSION["user_email"]) ? '👎 Gefällt mir nicht mehr' : '👍 Gefällt mir'; ?>
|
||||||
</a>
|
</button>
|
||||||
<?php else: ?>
|
</form>
|
||||||
<span class="login-hint">(Anmelden zum Liken)</span>
|
<?php else: ?>
|
||||||
<?php endif; ?>
|
<span class="login-hint">(Anmelden zum Liken)</span>
|
||||||
</div>
|
<?php endif; ?>
|
||||||
<?php endif; ?>
|
</div>
|
||||||
</div>
|
<?php endif; ?>
|
||||||
|
</div>
|
||||||
|
|
||||||
<h1 class="article-view-title">
|
<h1 class="article-view-title">
|
||||||
<?php if (isset($title)) { echo htmlspecialchars($title); } ?>
|
<?php if (isset($title)) { echo htmlspecialchars($title); } ?>
|
||||||
@@ -202,6 +207,8 @@ if ($replyAuthor === null) {
|
|||||||
action="index.php?pfad=updateComment"
|
action="index.php?pfad=updateComment"
|
||||||
class="edit-comment-form">
|
class="edit-comment-form">
|
||||||
|
|
||||||
|
<?php csrf_field(); ?>
|
||||||
|
|
||||||
<input type="hidden"
|
<input type="hidden"
|
||||||
name="comment_id"
|
name="comment_id"
|
||||||
value="<?php echo htmlspecialchars(
|
value="<?php echo htmlspecialchars(
|
||||||
@@ -239,6 +246,8 @@ if ($replyAuthor === null) {
|
|||||||
action="index.php?pfad=deleteComment"
|
action="index.php?pfad=deleteComment"
|
||||||
class="delete-comment-form">
|
class="delete-comment-form">
|
||||||
|
|
||||||
|
<?php csrf_field(); ?>
|
||||||
|
|
||||||
<input type="hidden"
|
<input type="hidden"
|
||||||
name="comment_id"
|
name="comment_id"
|
||||||
value="<?php echo htmlspecialchars(
|
value="<?php echo htmlspecialchars(
|
||||||
@@ -334,6 +343,8 @@ if ($replyAuthor === null) {
|
|||||||
action="index.php?pfad=updateComment"
|
action="index.php?pfad=updateComment"
|
||||||
class="edit-comment-form">
|
class="edit-comment-form">
|
||||||
|
|
||||||
|
<?php csrf_field(); ?>
|
||||||
|
|
||||||
<input type="hidden"
|
<input type="hidden"
|
||||||
name="comment_id"
|
name="comment_id"
|
||||||
value="<?php echo htmlspecialchars(
|
value="<?php echo htmlspecialchars(
|
||||||
@@ -371,6 +382,8 @@ if ($replyAuthor === null) {
|
|||||||
action="index.php?pfad=deleteComment"
|
action="index.php?pfad=deleteComment"
|
||||||
class="delete-comment-form">
|
class="delete-comment-form">
|
||||||
|
|
||||||
|
<?php csrf_field(); ?>
|
||||||
|
|
||||||
<input type="hidden"
|
<input type="hidden"
|
||||||
name="comment_id"
|
name="comment_id"
|
||||||
value="<?php echo htmlspecialchars(
|
value="<?php echo htmlspecialchars(
|
||||||
@@ -412,6 +425,8 @@ if ($replyAuthor === null) {
|
|||||||
method="post"
|
method="post"
|
||||||
action="php/ajax/add-comment.php">
|
action="php/ajax/add-comment.php">
|
||||||
|
|
||||||
|
<?php csrf_field(); ?>
|
||||||
|
|
||||||
<input type="hidden"
|
<input type="hidden"
|
||||||
name="article_id"
|
name="article_id"
|
||||||
value="<?php echo htmlspecialchars(
|
value="<?php echo htmlspecialchars(
|
||||||
|
|||||||
@@ -27,6 +27,8 @@ $blocks = array_values($blocks);
|
|||||||
-->
|
-->
|
||||||
<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">
|
||||||
|
|
||||||
|
<?php csrf_field(); ?>
|
||||||
|
|
||||||
<main class="editor-main">
|
<main class="editor-main">
|
||||||
<?php include_once "includes/alertMessages.php"?>
|
<?php include_once "includes/alertMessages.php"?>
|
||||||
|
|
||||||
|
|||||||
@@ -285,6 +285,32 @@ CSS für die navbar
|
|||||||
border-bottom: 1px solid #333d43;
|
border-bottom: 1px solid #333d43;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.nav__logout-form {
|
||||||
|
display: contents;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.nav__logout-form .nav__button {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.nav__mobile-logout-button {
|
||||||
|
color: #fff;
|
||||||
|
text-decoration: none;
|
||||||
|
font-size: 1.2rem;
|
||||||
|
font-weight: 600;
|
||||||
|
display: block;
|
||||||
|
width: 100%;
|
||||||
|
text-align: left;
|
||||||
|
padding: 0.5rem 1rem;
|
||||||
|
border: none;
|
||||||
|
border-bottom: 1px solid #333d43;
|
||||||
|
background: none;
|
||||||
|
cursor: pointer;
|
||||||
|
font-family: inherit;
|
||||||
|
}
|
||||||
|
|
||||||
.nav__mobile-submenu {
|
.nav__mobile-submenu {
|
||||||
display: block;
|
display: block;
|
||||||
list-style: none;
|
list-style: none;
|
||||||
|
|||||||
@@ -89,6 +89,12 @@
|
|||||||
Es ist ein Datenbankfehler aufgetreten. Bitte versuche es erneut.
|
Es ist ein Datenbankfehler aufgetreten. Bitte versuche es erneut.
|
||||||
</p>
|
</p>
|
||||||
<?php endif; ?>
|
<?php endif; ?>
|
||||||
|
<?php if (isset($_SESSION["message"]) && $_SESSION["message"] == "invalid_csrf_token"): ?>
|
||||||
|
<p class="alert-message is-error">
|
||||||
|
Deine Sitzung ist abgelaufen oder die Anfrage konnte nicht überprüft werden.
|
||||||
|
Bitte lade die Seite neu und versuche es erneut.
|
||||||
|
</p>
|
||||||
|
<?php endif; ?>
|
||||||
<?php
|
<?php
|
||||||
unset($_SESSION["message"]);
|
unset($_SESSION["message"]);
|
||||||
?>
|
?>
|
||||||
@@ -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;
|
||||||
|
}
|
||||||
@@ -0,0 +1,88 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* CSRF-Schutz nach dem Synchronizer-Token-Pattern.
|
||||||
|
*
|
||||||
|
* Pro Session wird ein einziges, zufälliges Token erzeugt,
|
||||||
|
* das in jedem Formular als verstecktes Feld mitgeschickt und bei jeder
|
||||||
|
* zustandsändernden Anfrage serverseitig mit dem Session-Token verglichen
|
||||||
|
* wird.
|
||||||
|
*
|
||||||
|
* @author Niklas Ortmann
|
||||||
|
*/
|
||||||
|
|
||||||
|
if (session_status() === PHP_SESSION_NONE) {
|
||||||
|
session_start();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gibt das aktuelle CSRF-Token der Session zurück.
|
||||||
|
*
|
||||||
|
* Existiert noch kein Token, wird eines erzeugt und in der Session
|
||||||
|
* gespeichert.
|
||||||
|
*
|
||||||
|
* @return string Das CSRF-Token
|
||||||
|
*/
|
||||||
|
function csrf_token(): string
|
||||||
|
{
|
||||||
|
if (empty($_SESSION["csrf_token"]) || !is_string($_SESSION["csrf_token"])) {
|
||||||
|
$_SESSION["csrf_token"] = bin2hex(random_bytes(32));
|
||||||
|
}
|
||||||
|
|
||||||
|
return $_SESSION["csrf_token"];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gibt ein verstecktes Formularfeld mit dem aktuellen CSRF-Token aus.
|
||||||
|
*
|
||||||
|
* Wird in jedem Formular benötigt, das eine zustandsändernde
|
||||||
|
* Aktion auslöst.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
function csrf_field(): void
|
||||||
|
{
|
||||||
|
echo '<input type="hidden" name="csrf_token" value="'
|
||||||
|
. htmlspecialchars(csrf_token())
|
||||||
|
. '">';
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Prüft, ob das per POST gesendete CSRF-Token zum Session-Token passt.
|
||||||
|
*
|
||||||
|
* Der Vergleich erfolgt zeitkonstant über hash_equals(), um
|
||||||
|
* Timing-Angriffe auf den Vergleich selbst auszuschließen.
|
||||||
|
*
|
||||||
|
* @return bool true, wenn das Token gültig ist
|
||||||
|
*/
|
||||||
|
function csrf_verify(): bool
|
||||||
|
{
|
||||||
|
$sentToken = $_POST["csrf_token"] ?? "";
|
||||||
|
$sessionToken = $_SESSION["csrf_token"] ?? "";
|
||||||
|
|
||||||
|
if (!is_string($sentToken) || $sentToken === "" || $sessionToken === "") {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return hash_equals($sessionToken, $sentToken);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Bricht die Anfrage ab und leitet mit einer Fehlermeldung um,
|
||||||
|
* wenn das mitgesendete CSRF-Token ungültig oder nicht vorhanden ist.
|
||||||
|
*
|
||||||
|
* Muss am Anfang jeder zustandsändernden POST-Aktion aufgerufen werden,
|
||||||
|
* bevor irgendeine Änderung an Daten vorgenommen wird.
|
||||||
|
*
|
||||||
|
* @param string $redirectTo Ziel-URL, zu der bei ungültigem Token
|
||||||
|
* weitergeleitet wird
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
function csrf_require_valid(string $redirectTo = "index.php"): void
|
||||||
|
{
|
||||||
|
if (!csrf_verify()) {
|
||||||
|
http_response_code(403);
|
||||||
|
$_SESSION["message"] = "invalid_csrf_token";
|
||||||
|
header("Location: " . $redirectTo);
|
||||||
|
exit();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -2,6 +2,7 @@
|
|||||||
if (session_status() === PHP_SESSION_NONE) {
|
if (session_status() === PHP_SESSION_NONE) {
|
||||||
session_start();
|
session_start();
|
||||||
}
|
}
|
||||||
|
include_once "includes/csrf.php";
|
||||||
include_once "php/controller/index-controller.php";
|
include_once "php/controller/index-controller.php";
|
||||||
?>
|
?>
|
||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
|
|||||||
@@ -109,6 +109,12 @@ document.addEventListener("DOMContentLoaded", function () {
|
|||||||
action="index.php?pfad=updateComment"
|
action="index.php?pfad=updateComment"
|
||||||
class="edit-comment-form"
|
class="edit-comment-form"
|
||||||
>
|
>
|
||||||
|
<input
|
||||||
|
type="hidden"
|
||||||
|
name="csrf_token"
|
||||||
|
value="${escapeHtml(formData.get("csrf_token"))}"
|
||||||
|
>
|
||||||
|
|
||||||
<input
|
<input
|
||||||
type="hidden"
|
type="hidden"
|
||||||
name="comment_id"
|
name="comment_id"
|
||||||
@@ -137,6 +143,12 @@ document.addEventListener("DOMContentLoaded", function () {
|
|||||||
action="index.php?pfad=deleteComment"
|
action="index.php?pfad=deleteComment"
|
||||||
class="delete-comment-form"
|
class="delete-comment-form"
|
||||||
>
|
>
|
||||||
|
<input
|
||||||
|
type="hidden"
|
||||||
|
name="csrf_token"
|
||||||
|
value="${escapeHtml(formData.get("csrf_token"))}"
|
||||||
|
>
|
||||||
|
|
||||||
<input
|
<input
|
||||||
type="hidden"
|
type="hidden"
|
||||||
name="comment_id"
|
name="comment_id"
|
||||||
@@ -187,6 +199,12 @@ document.addEventListener("DOMContentLoaded", function () {
|
|||||||
action="index.php?pfad=updateComment"
|
action="index.php?pfad=updateComment"
|
||||||
class="edit-comment-form"
|
class="edit-comment-form"
|
||||||
>
|
>
|
||||||
|
<input
|
||||||
|
type="hidden"
|
||||||
|
name="csrf_token"
|
||||||
|
value="${escapeHtml(formData.get("csrf_token"))}"
|
||||||
|
>
|
||||||
|
|
||||||
<input
|
<input
|
||||||
type="hidden"
|
type="hidden"
|
||||||
name="comment_id"
|
name="comment_id"
|
||||||
@@ -215,6 +233,12 @@ document.addEventListener("DOMContentLoaded", function () {
|
|||||||
action="index.php?pfad=deleteComment"
|
action="index.php?pfad=deleteComment"
|
||||||
class="delete-comment-form"
|
class="delete-comment-form"
|
||||||
>
|
>
|
||||||
|
<input
|
||||||
|
type="hidden"
|
||||||
|
name="csrf_token"
|
||||||
|
value="${escapeHtml(formData.get("csrf_token"))}"
|
||||||
|
>
|
||||||
|
|
||||||
<input
|
<input
|
||||||
type="hidden"
|
type="hidden"
|
||||||
name="comment_id"
|
name="comment_id"
|
||||||
|
|||||||
@@ -4,6 +4,8 @@ if (session_status() === PHP_SESSION_NONE) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
require_once "../model/CommentManager.php";
|
require_once "../model/CommentManager.php";
|
||||||
|
require_once "../model/ArticleManager.php";
|
||||||
|
require_once "../../includes/csrf.php";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Prüft, ob die Anfrage durch JavaScript per AJAX gesendet wurde.
|
* Prüft, ob die Anfrage durch JavaScript per AJAX gesendet wurde.
|
||||||
@@ -96,6 +98,17 @@ if (!isset($_SESSION["user_email"])) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* CSRF-Token prüfen, bevor irgendeine Änderung vorgenommen wird.
|
||||||
|
*/
|
||||||
|
if (!csrf_verify()) {
|
||||||
|
sendCommentResponse(
|
||||||
|
false,
|
||||||
|
"Deine Sitzung ist abgelaufen. Bitte lade die Seite neu und versuche es erneut.",
|
||||||
|
$articleId !== false ? $articleId : null
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Weitere Formulardaten einlesen.
|
* Weitere Formulardaten einlesen.
|
||||||
*/
|
*/
|
||||||
@@ -128,6 +141,18 @@ if ($articleId === false || $articleId === null) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Der Beitrag muss tatsächlich existieren.
|
||||||
|
*/
|
||||||
|
$existingArticle = ArticleManager::getInstance()->getArticle($articleId);
|
||||||
|
if ($existingArticle === null) {
|
||||||
|
sendCommentResponse(
|
||||||
|
false,
|
||||||
|
"Der zugehörige Beitrag wurde nicht gefunden.",
|
||||||
|
null
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
if ($content === "") {
|
if ($content === "") {
|
||||||
sendCommentResponse(
|
sendCommentResponse(
|
||||||
false,
|
false,
|
||||||
@@ -152,6 +177,30 @@ if (
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Falls eine Eltern-ID angegeben wurde, muss dieser Kommentar
|
||||||
|
* tatsächlich existieren und zum selben Beitrag gehören.
|
||||||
|
*/
|
||||||
|
if ($parentCommentId !== null) {
|
||||||
|
$existingComments = CommentManager::getInstance()->getCommentsByArticle($articleId);
|
||||||
|
$parentExists = false;
|
||||||
|
|
||||||
|
foreach ($existingComments as $existingComment) {
|
||||||
|
if ($existingComment->getId() === $parentCommentId) {
|
||||||
|
$parentExists = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!$parentExists) {
|
||||||
|
sendCommentResponse(
|
||||||
|
false,
|
||||||
|
"Der ausgewählte Kommentar wurde nicht gefunden.",
|
||||||
|
$articleId
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
$commentManager = CommentManager::getInstance();
|
$commentManager = CommentManager::getInstance();
|
||||||
|
|
||||||
|
|||||||
@@ -5,134 +5,128 @@ 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';
|
||||||
|
require_once '../../includes/csrf.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") {
|
|
||||||
$_SESSION["old_title"] = $_POST["title"] ?? '';
|
|
||||||
$_SESSION["old_content"] = $_POST["content"] ?? '';
|
|
||||||
$_SESSION["old_category"] = $_POST["category"] ?? '';
|
|
||||||
$_SESSION["old_tags"] = $_POST["tags"] ?? '';
|
|
||||||
|
|
||||||
if(!isset($_POST["title"]) ||!isset($_POST["content"]) || !isset($_POST["category"])){
|
if ($_SERVER["REQUEST_METHOD"] === "POST") {
|
||||||
$_SESSION["message"] = "missing_parameters";
|
|
||||||
|
// CSRF-Token prüfen, bevor irgendeine Änderung vorgenommen wird
|
||||||
|
if (!csrf_verify()) {
|
||||||
|
$_SESSION["message"] = "invalid_csrf_token";
|
||||||
|
header("location: ../../index.php?pfad=createArticle");
|
||||||
|
exit();
|
||||||
|
}
|
||||||
|
|
||||||
|
$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_category"] = $_POST["category"] ?? '';
|
||||||
|
$_SESSION["old_tags"] = $_POST["tags"] ?? '';
|
||||||
|
|
||||||
|
$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";
|
||||||
|
header("location: ../../index.php?pfad=createArticle");
|
||||||
|
exit();
|
||||||
|
} else {
|
||||||
|
$title = $_POST["title"];
|
||||||
|
$content = json_encode($blocks, JSON_UNESCAPED_UNICODE);
|
||||||
|
$author = $_SESSION["user_email"];
|
||||||
|
$category = $_POST["category"];
|
||||||
|
$tags = $_POST['tags'] ?? '';
|
||||||
|
|
||||||
|
// -------------------------------- Validierung der Daten: -------------------------
|
||||||
|
if (!articleTitleValidator($title)) {
|
||||||
|
$_SESSION["message"] = "invalid_title";
|
||||||
|
header("location: ../../index.php?pfad=createArticle");
|
||||||
|
exit();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!articleContentValidator($content)) {
|
||||||
|
$_SESSION["message"] = "invalid_content";
|
||||||
|
header("location: ../../index.php?pfad=createArticle");
|
||||||
|
exit();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!articleCategoryValidator($category)) {
|
||||||
|
$_SESSION["message"] = "invalid_category";
|
||||||
|
header("location: ../../index.php?pfad=createArticle");
|
||||||
|
exit();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!articleTagValidator($tags)) {
|
||||||
|
$_SESSION["message"] = "invalid_tags";
|
||||||
header("location: ../../index.php?pfad=createArticle");
|
header("location: ../../index.php?pfad=createArticle");
|
||||||
exit();
|
exit();
|
||||||
} else {
|
} else {
|
||||||
$title = $_POST["title"];
|
$cleanedTags = [];
|
||||||
$content = $_POST["content"];
|
$rawTags = explode(',', $tags);
|
||||||
$author = $_SESSION["user_email"];
|
foreach ($rawTags as $rawTag) {
|
||||||
$category = $_POST["category"];
|
// Leerzeichen am Anfang/Ende des einzelnen Tags entfernen:
|
||||||
$tags = $_POST['tags'] ?? '';
|
$tag = trim($rawTag);
|
||||||
|
$cleanedTags[] = $tag;
|
||||||
// -------------------------------- Validierung der Daten: -------------------------
|
|
||||||
if (!articleTitleValidator($title)) {
|
|
||||||
$_SESSION["message"] = "invalid_title";
|
|
||||||
header("location: ../../index.php?pfad=createArticle");
|
|
||||||
exit();
|
|
||||||
}
|
}
|
||||||
|
// Duplikate entfernen:
|
||||||
|
$cleanedTags = array_unique($cleanedTags);
|
||||||
|
$cleanedTags = implode(',', $cleanedTags);
|
||||||
|
}
|
||||||
|
|
||||||
if (!articleContentValidator($content)) {
|
// ----------------- Übertragung der validierten Daten in ArticleManager: ---------------------------
|
||||||
$_SESSION["message"] = "invalid_content";
|
try {
|
||||||
header("location: ../../index.php?pfad=createArticle");
|
$articleManager = ArticleManager::getInstance();
|
||||||
exit();
|
// $content enthält bereits die finalen "uploads/..."-Pfade (kein Base64 mehr),
|
||||||
}
|
// da rebuildBlocksFromPost() Datei-Uploads sofort verarbeitet.
|
||||||
|
$articleManager->addArticle($title, $content, $author, $category, $cleanedTags);
|
||||||
|
|
||||||
if (!articleCategoryValidator($category)) {
|
// Formulardaten nach erfolgreichem Erstellen aus der Session löschen
|
||||||
$_SESSION["message"] = "invalid_category";
|
unset($_SESSION["old_title"], $_SESSION["old_content"], $_SESSION["old_category"], $_SESSION["old_tags"]);
|
||||||
header("location: ../../index.php?pfad=createArticle");
|
|
||||||
exit();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!articleTagValidator($tags)) {
|
} catch (\Throwable $e) {
|
||||||
$_SESSION["message"] = "invalid_tags";
|
$_SESSION["message"] = "internal_error";
|
||||||
header("location: ../../index.php?pfad=createArticle");
|
header("location: ../../index.php?pfad=createArticle");
|
||||||
exit();
|
|
||||||
} else {
|
|
||||||
$cleanedTags = [];
|
|
||||||
$rawTags = explode(',', $tags);
|
|
||||||
foreach ($rawTags as $rawTag) {
|
|
||||||
// Leerzeichen am Anfang/Ende des einzelnen Tags entfernen:
|
|
||||||
$tag = trim($rawTag);
|
|
||||||
$cleanedTags[] = $tag;
|
|
||||||
}
|
|
||||||
// Duplikate entfernen:
|
|
||||||
$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);
|
|
||||||
}
|
|
||||||
|
|
||||||
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: ---------------------------
|
|
||||||
try {
|
|
||||||
$articleManager = ArticleManager::getInstance();
|
|
||||||
$articleManager->addArticle($title, $content, $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"]);
|
|
||||||
|
|
||||||
} catch (\Throwable $e){
|
|
||||||
$_SESSION["message"] = "internal_error";
|
|
||||||
header("location: ../../index.php?pfad=createArticle");
|
|
||||||
exit();
|
|
||||||
}
|
|
||||||
|
|
||||||
$_SESSION["message"] = "new_article";
|
|
||||||
// Weiterleitung zur Homepage
|
|
||||||
header("location: ../../index.php");
|
|
||||||
exit();
|
exit();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$_SESSION["message"] = "new_article";
|
||||||
|
// Weiterleitung zur Homepage
|
||||||
|
header("location: ../../index.php");
|
||||||
|
exit();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
?>
|
?>
|
||||||
@@ -5,12 +5,24 @@ if (session_status() === PHP_SESSION_NONE) {
|
|||||||
|
|
||||||
require_once __DIR__ . "/../model/UserManager.php";
|
require_once __DIR__ . "/../model/UserManager.php";
|
||||||
require_once __DIR__ . "/../model/ArticleManager.php";
|
require_once __DIR__ . "/../model/ArticleManager.php";
|
||||||
|
require_once __DIR__ . "/../../includes/csrf.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") {
|
||||||
|
header("Location: ../../index.php?pfad=profile");
|
||||||
|
exit();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!csrf_verify()) {
|
||||||
|
$_SESSION["message"] = "invalid_csrf_token";
|
||||||
|
header("Location: ../../index.php?pfad=profile");
|
||||||
|
exit();
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Deregistrierung
|
Deregistrierung
|
||||||
Funktion: Entfernt User aus der Datenbank und beendet die Session
|
Funktion: Entfernt User aus der Datenbank und beendet die Session
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ if (session_status() === PHP_SESSION_NONE) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
require_once __DIR__ . "/../model/ArticleManager.php";
|
require_once __DIR__ . "/../model/ArticleManager.php";
|
||||||
|
require_once __DIR__ . "/../../includes/csrf.php";
|
||||||
|
|
||||||
if (!isset($_SESSION["user"])) {
|
if (!isset($_SESSION["user"])) {
|
||||||
header("Location: index.php?pfad=login");
|
header("Location: index.php?pfad=login");
|
||||||
@@ -12,6 +13,13 @@ if (!isset($_SESSION["user"])) {
|
|||||||
|
|
||||||
if ($_SERVER["REQUEST_METHOD"] === "POST") {
|
if ($_SERVER["REQUEST_METHOD"] === "POST") {
|
||||||
|
|
||||||
|
// CSRF-Token prüfen, bevor irgendeine Änderung vorgenommen wird
|
||||||
|
if (!csrf_verify()) {
|
||||||
|
$_SESSION["message"] = "invalid_csrf_token";
|
||||||
|
header("location: ../../index.php?pfad=profile");
|
||||||
|
exit();
|
||||||
|
}
|
||||||
|
|
||||||
if (isset($_SESSION["user_email"])) {
|
if (isset($_SESSION["user_email"])) {
|
||||||
$user = $_SESSION["user_email"];
|
$user = $_SESSION["user_email"];
|
||||||
} else {
|
} else {
|
||||||
@@ -22,9 +30,10 @@ if ($_SERVER["REQUEST_METHOD"] === "POST") {
|
|||||||
exit();
|
exit();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isset($_POST["id"]) && !empty($_POST["id"])) {
|
// Die Beitrags-ID muss eine gültige numerische ID sein.
|
||||||
$id = $_POST["id"];
|
$id = filter_input(INPUT_POST, "id", FILTER_VALIDATE_INT);
|
||||||
} else {
|
|
||||||
|
if ($id === false || $id === null) {
|
||||||
$_SESSION["message"] = "missing_id";
|
$_SESSION["message"] = "missing_id";
|
||||||
header("location: ../../index.php?pfad=profile");
|
header("location: ../../index.php?pfad=profile");
|
||||||
exit();
|
exit();
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ if (session_status() === PHP_SESSION_NONE) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
require_once __DIR__ . "/../model/CommentManager.php";
|
require_once __DIR__ . "/../model/CommentManager.php";
|
||||||
|
require_once __DIR__ . "/../../includes/csrf.php";
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Kommentare dürfen nur über ein POST-Formular gelöscht werden.
|
* Kommentare dürfen nur über ein POST-Formular gelöscht werden.
|
||||||
@@ -44,6 +45,23 @@ if (!isset($_SESSION["user_email"])) {
|
|||||||
exit();
|
exit();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// CSRF-Token prüfen, bevor irgendeine Änderung vorgenommen wird
|
||||||
|
if (!csrf_verify()) {
|
||||||
|
$_SESSION["comment_message"] = "Deine Sitzung ist abgelaufen. Bitte lade die Seite neu.";
|
||||||
|
$_SESSION["comment_message_type"] = "error";
|
||||||
|
|
||||||
|
if ($articleId !== false && $articleId !== null) {
|
||||||
|
header(
|
||||||
|
"Location: index.php?pfad=showArticle&id="
|
||||||
|
. urlencode((string) $articleId)
|
||||||
|
. "#comments"
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
header("Location: index.php");
|
||||||
|
}
|
||||||
|
exit();
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Kommentar-ID und Beitrags-ID müssen gültige Zahlen sein.
|
* Kommentar-ID und Beitrags-ID müssen gültige Zahlen sein.
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -5,6 +5,12 @@ if (session_status() === PHP_SESSION_NONE) {
|
|||||||
|
|
||||||
require_once __DIR__ . '/../model/Article.php';
|
require_once __DIR__ . '/../model/Article.php';
|
||||||
require_once __DIR__ . '/../model/ArticleManager.php';
|
require_once __DIR__ . '/../model/ArticleManager.php';
|
||||||
|
require_once __DIR__ . '/../../includes/csrf.php';
|
||||||
|
|
||||||
|
if ($_SERVER["REQUEST_METHOD"] !== "POST") {
|
||||||
|
header("Location: ../../index.php");
|
||||||
|
exit();
|
||||||
|
}
|
||||||
|
|
||||||
// 2. Prüfen, ob eine gültige Artikel-ID übergeben wurde
|
// 2. Prüfen, ob eine gültige Artikel-ID übergeben wurde
|
||||||
if (isset($_GET["id"]) && !empty($_GET["id"])) {
|
if (isset($_GET["id"]) && !empty($_GET["id"])) {
|
||||||
@@ -17,6 +23,13 @@ if (isset($_GET["id"]) && !empty($_GET["id"])) {
|
|||||||
exit();
|
exit();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// CSRF-Token prüfen, bevor der Like-Status verändert wird
|
||||||
|
if (!csrf_verify()) {
|
||||||
|
$_SESSION["message"] = "invalid_csrf_token";
|
||||||
|
header("Location: ../../index.php?pfad=showArticle&id=" . $articleId);
|
||||||
|
exit();
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
$articleManager = ArticleManager::getInstance();
|
$articleManager = ArticleManager::getInstance();
|
||||||
$articleManager->toggleLike($articleId, $userEmail);
|
$articleManager->toggleLike($articleId, $userEmail);
|
||||||
|
|||||||
@@ -1,4 +1,8 @@
|
|||||||
<?php
|
<?php
|
||||||
|
if (session_status() === PHP_SESSION_NONE) {
|
||||||
|
session_start();
|
||||||
|
}
|
||||||
|
|
||||||
$_SESSION = [];
|
$_SESSION = [];
|
||||||
session_destroy();
|
session_destroy();
|
||||||
|
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ require_once "php/model/Article.php";
|
|||||||
require_once "php/model/ArticleManager.php";
|
require_once "php/model/ArticleManager.php";
|
||||||
require_once "php/model/CommentManager.php";
|
require_once "php/model/CommentManager.php";
|
||||||
require_once "php/validator/user-validator.php";
|
require_once "php/validator/user-validator.php";
|
||||||
|
require_once "includes/csrf.php";
|
||||||
|
|
||||||
$error = null;
|
$error = null;
|
||||||
|
|
||||||
@@ -27,6 +28,7 @@ try {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if ($_SERVER["REQUEST_METHOD"] === "POST" && isset($_POST["saveProfile"])) {
|
if ($_SERVER["REQUEST_METHOD"] === "POST" && isset($_POST["saveProfile"])) {
|
||||||
|
|
||||||
$oldEmail = $_SESSION["user_email"];
|
$oldEmail = $_SESSION["user_email"];
|
||||||
|
|
||||||
$newEmail = trim($_POST["email"] ?? "");
|
$newEmail = trim($_POST["email"] ?? "");
|
||||||
@@ -34,7 +36,12 @@ try {
|
|||||||
$nachname = trim($_POST["nachname"] ?? "");
|
$nachname = trim($_POST["nachname"] ?? "");
|
||||||
$password = $_POST["password"] ?? "";
|
$password = $_POST["password"] ?? "";
|
||||||
|
|
||||||
if (!userEmailValidator($newEmail)) {
|
if (!csrf_verify()) {
|
||||||
|
// CSRF-Token prüfen, bevor irgendeine Änderung vorgenommen wird
|
||||||
|
$error = "Deine Sitzung ist abgelaufen. Bitte lade die Seite neu und versuche es erneut.";
|
||||||
|
$_GET["edit"] = "1";
|
||||||
|
|
||||||
|
} elseif (!userEmailValidator($newEmail)) {
|
||||||
$error = "Bitte gib eine gültige E-Mail-Adresse ein.";
|
$error = "Bitte gib eine gültige E-Mail-Adresse ein.";
|
||||||
$_GET["edit"] = "1";
|
$_GET["edit"] = "1";
|
||||||
|
|
||||||
|
|||||||
@@ -7,10 +7,14 @@ require_once 'php/model/Article.php';
|
|||||||
require_once 'php/model/ArticleManager.php';
|
require_once 'php/model/ArticleManager.php';
|
||||||
require_once 'php/model/UserManager.php';
|
require_once 'php/model/UserManager.php';
|
||||||
require_once 'php/model/CommentManager.php';
|
require_once 'php/model/CommentManager.php';
|
||||||
|
require_once 'php/validator/article-validator.php';
|
||||||
|
|
||||||
if (isset($_GET["id"]) && !empty($_GET["id"])){
|
// Die übergebene ID muss eine gültige, positive Zahl sein, bevor sie
|
||||||
|
// weiterverwendet wird. Vorher wurde jeder nicht-leere Wert akzeptiert.
|
||||||
|
$id = isset($_GET["id"]) ? articleIdValidator($_GET["id"]) : false;
|
||||||
|
|
||||||
|
if ($id !== false) {
|
||||||
try {
|
try {
|
||||||
$id = $_GET["id"];
|
|
||||||
$articleManager = ArticleManager::getInstance();
|
$articleManager = ArticleManager::getInstance();
|
||||||
$article = $articleManager->getArticle($id);
|
$article = $articleManager->getArticle($id);
|
||||||
if($article != null){
|
if($article != null){
|
||||||
@@ -38,7 +42,7 @@ if (isset($_GET["id"]) && !empty($_GET["id"])){
|
|||||||
}
|
}
|
||||||
|
|
||||||
$commentManager = CommentManager::getInstance();
|
$commentManager = CommentManager::getInstance();
|
||||||
$comments = $commentManager->getCommentsByArticle($_GET["id"]);
|
$comments = $commentManager->getCommentsByArticle($id); // NEU: validierte ID statt rohem $_GET["id"]
|
||||||
|
|
||||||
foreach ($comments as $comment) {
|
foreach ($comments as $comment) {
|
||||||
if ($comment->isReply()) {
|
if ($comment->isReply()) {
|
||||||
|
|||||||
@@ -7,83 +7,27 @@ 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';
|
||||||
|
require_once '../../includes/csrf.php'; // NEU: CSRF-Schutz
|
||||||
|
|
||||||
if (!isset($_SESSION["user_email"])) {
|
if (!isset($_SESSION["user"])) {
|
||||||
header("Location: index.php?pfad=login");
|
header("Location: index.php?pfad=login");
|
||||||
exit();
|
exit();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* 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), damit neu ausgewählte Bilder in jedem Fall persistiert
|
|
||||||
* werden, bevor PHP die temporäre Upload-Datei nach Request-Ende verwirft.
|
|
||||||
*
|
|
||||||
* @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;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($_SERVER["REQUEST_METHOD"] === "POST") {
|
if ($_SERVER["REQUEST_METHOD"] === "POST") {
|
||||||
|
|
||||||
if (isset($_GET["id"]) && !empty($_GET["id"])) {
|
// CSRF-Token prüfen, bevor irgendeine Änderung vorgenommen wird
|
||||||
$id = $_GET["id"];
|
if (!csrf_verify()) {
|
||||||
} else {
|
$_SESSION["message"] = "invalid_csrf_token";
|
||||||
|
header("location: ../../index.php?pfad=updateArticle");
|
||||||
|
exit();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Die Beitrags-ID muss eine gültige numerische ID sein
|
||||||
|
$id = filter_input(INPUT_GET, "id", FILTER_VALIDATE_INT);
|
||||||
|
|
||||||
|
if ($id === false || $id === null) {
|
||||||
$_SESSION["message"] = "missing_id";
|
$_SESSION["message"] = "missing_id";
|
||||||
header("location: ../../index.php?pfad=updateArticle");
|
header("location: ../../index.php?pfad=updateArticle");
|
||||||
exit();
|
exit();
|
||||||
@@ -92,6 +36,14 @@ if ($_SERVER["REQUEST_METHOD"] === "POST") {
|
|||||||
try {
|
try {
|
||||||
$articleManager = ArticleManager::getInstance();
|
$articleManager = ArticleManager::getInstance();
|
||||||
$article = $articleManager->getArticle($id);
|
$article = $articleManager->getArticle($id);
|
||||||
|
|
||||||
|
// Existenz des Beitrags prüfen, bevor auf $article zugegriffen wird.
|
||||||
|
if ($article === null) {
|
||||||
|
$_SESSION["message"] = "missing_id";
|
||||||
|
header("location: ../../index.php?pfad=updateArticle");
|
||||||
|
exit();
|
||||||
|
}
|
||||||
|
|
||||||
if ($article->getAuthor() != $_SESSION["user_email"]) {
|
if ($article->getAuthor() != $_SESSION["user_email"]) {
|
||||||
$_SESSION["message"] = "unauthorized_access";
|
$_SESSION["message"] = "unauthorized_access";
|
||||||
header("location: ../../index.php");
|
header("location: ../../index.php");
|
||||||
@@ -118,7 +70,9 @@ if ($_SERVER["REQUEST_METHOD"] === "POST") {
|
|||||||
$_SESSION["old_content"] = json_encode($blocks, JSON_UNESCAPED_UNICODE);
|
$_SESSION["old_content"] = json_encode($blocks, JSON_UNESCAPED_UNICODE);
|
||||||
|
|
||||||
// ---------------------------------------------------------------------
|
// ---------------------------------------------------------------------
|
||||||
// Zwischenspeichern
|
// 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'] !== '') {
|
if (isset($_POST['editor_action']) && $_POST['editor_action'] !== '') {
|
||||||
$action = $_POST['editor_action'];
|
$action = $_POST['editor_action'];
|
||||||
@@ -134,7 +88,7 @@ if ($_SERVER["REQUEST_METHOD"] === "POST") {
|
|||||||
}
|
}
|
||||||
|
|
||||||
$_SESSION["old_content"] = json_encode($blocks, JSON_UNESCAPED_UNICODE);
|
$_SESSION["old_content"] = json_encode($blocks, JSON_UNESCAPED_UNICODE);
|
||||||
header("location: ../../content/updateArticle.php?id=$id");
|
header("location: ../../index.php?pfad=updateArticle&id=$id");
|
||||||
exit();
|
exit();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -191,7 +145,12 @@ if ($_SERVER["REQUEST_METHOD"] === "POST") {
|
|||||||
// ----------------- Verwaiste Bilder aufräumen -----------------
|
// ----------------- Verwaiste Bilder aufräumen -----------------
|
||||||
// Bilder, die im alten (gespeicherten) Content vorkamen, im neuen aber nicht
|
// Bilder, die im alten (gespeicherten) Content vorkamen, im neuen aber nicht
|
||||||
// mehr referenziert werden, wurden vom Nutzer entfernt oder ersetzt -> löschen.
|
// mehr referenziert werden, wurden vom Nutzer entfernt oder ersetzt -> löschen.
|
||||||
// TODO: Bilder, die innerhalb derselben Bearbeitungs-Sitzung neu hochgeladen und noch vor dem finalen Speichern wieder entfernt wurden, 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
|
// Hinweis/TODO: Bilder, die innerhalb derselben Bearbeitungs-Sitzung neu
|
||||||
|
// hochgeladen und noch vor dem finalen Speichern wieder entfernt wurden,
|
||||||
|
// 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
|
||||||
|
// mit den in der Datenbank referenzierten Pfaden abgleicht.
|
||||||
$oldBlocks = json_decode($article->getContent(), true);
|
$oldBlocks = json_decode($article->getContent(), true);
|
||||||
$oldImagePaths = [];
|
$oldImagePaths = [];
|
||||||
if (is_array($oldBlocks)) {
|
if (is_array($oldBlocks)) {
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ if (session_status() === PHP_SESSION_NONE) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
require_once "php/model/CommentManager.php";
|
require_once "php/model/CommentManager.php";
|
||||||
|
require_once "includes/csrf.php";
|
||||||
|
|
||||||
if ($_SERVER["REQUEST_METHOD"] !== "POST") {
|
if ($_SERVER["REQUEST_METHOD"] !== "POST") {
|
||||||
header("Location: index.php");
|
header("Location: index.php");
|
||||||
@@ -16,6 +17,14 @@ if (!isset($_SESSION["user_email"])) {
|
|||||||
exit();
|
exit();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// CSRF-Token prüfen, bevor irgendeine Änderung vorgenommen wird
|
||||||
|
if (!csrf_verify()) {
|
||||||
|
$_SESSION["comment_message"] = "Deine Sitzung ist abgelaufen. Bitte lade die Seite neu.";
|
||||||
|
$_SESSION["comment_message_type"] = "error";
|
||||||
|
header("Location: index.php");
|
||||||
|
exit();
|
||||||
|
}
|
||||||
|
|
||||||
$commentId = filter_input(
|
$commentId = filter_input(
|
||||||
INPUT_POST,
|
INPUT_POST,
|
||||||
"comment_id",
|
"comment_id",
|
||||||
|
|||||||
@@ -63,12 +63,9 @@ class DatabaseArticleManager implements ArticleManagerDAO {
|
|||||||
VALUES (:title, :content, :author, :category, :tags);";
|
VALUES (:title, :content, :author, :category, :tags);";
|
||||||
|
|
||||||
$command = $db->prepare($sql);
|
$command = $db->prepare($sql);
|
||||||
if (!$command) {
|
|
||||||
throw new InternalServerErrorException("internal_error");
|
|
||||||
}
|
|
||||||
|
|
||||||
// Verknüpft die übergebenen Parameter exakt mit den SQL-Platzhaltern
|
// Verknüpft die übergebenen Parameter exakt mit den SQL-Platzhaltern
|
||||||
$success = $command->execute([
|
$command->execute([
|
||||||
":title" => $title,
|
":title" => $title,
|
||||||
":content" => $content,
|
":content" => $content,
|
||||||
":author" => $author,
|
":author" => $author,
|
||||||
@@ -76,14 +73,14 @@ class DatabaseArticleManager implements ArticleManagerDAO {
|
|||||||
":tags" => $tags
|
":tags" => $tags
|
||||||
]);
|
]);
|
||||||
|
|
||||||
if (!$success) {
|
|
||||||
throw new InternalServerErrorException("internal_error");
|
|
||||||
}
|
|
||||||
|
|
||||||
return intval($db->lastInsertId());
|
return intval($db->lastInsertId());
|
||||||
|
|
||||||
} catch (PDOException $e) {
|
} catch (PDOException $e) {
|
||||||
throw new InternalServerErrorException($e->getMessage());
|
// NEU: Die rohe PDO-Fehlermeldung wird nicht mehr direkt in die
|
||||||
|
// eigene Exception übernommen (Kapselung), sondern durch eine
|
||||||
|
// generische, sprechende Meldung ersetzt - analog zu den übrigen
|
||||||
|
// Methoden dieser Klasse und zu DatabaseUserManager.
|
||||||
|
throw new InternalServerErrorException("internal_error");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -106,11 +103,8 @@ class DatabaseArticleManager implements ArticleManagerDAO {
|
|||||||
WHERE id = :id;";
|
WHERE id = :id;";
|
||||||
|
|
||||||
$command = $db->prepare($sql);
|
$command = $db->prepare($sql);
|
||||||
if (!$command) {
|
|
||||||
throw new InternalServerErrorException("internal_error");
|
|
||||||
}
|
|
||||||
|
|
||||||
$success = $command->execute([
|
$command->execute([
|
||||||
":id" => $id,
|
":id" => $id,
|
||||||
":title" => $article->getTitle(),
|
":title" => $article->getTitle(),
|
||||||
":content" => $article->getContent(),
|
":content" => $article->getContent(),
|
||||||
@@ -120,7 +114,7 @@ class DatabaseArticleManager implements ArticleManagerDAO {
|
|||||||
]);
|
]);
|
||||||
|
|
||||||
// rowCount() prüft, ob eine Zeile mit dieser ID existierte und geändert werden konnte
|
// rowCount() prüft, ob eine Zeile mit dieser ID existierte und geändert werden konnte
|
||||||
if (!$success || $command->rowCount() === 0) {
|
if ($command->rowCount() === 0) {
|
||||||
// Falls die ID nicht existiert, prüfen wir, ob sie überhaupt da ist
|
// Falls die ID nicht existiert, prüfen wir, ob sie überhaupt da ist
|
||||||
if (!$this->getArticle($id)) {
|
if (!$this->getArticle($id)) {
|
||||||
throw new NotFoundException("missing_id");
|
throw new NotFoundException("missing_id");
|
||||||
@@ -148,13 +142,7 @@ class DatabaseArticleManager implements ArticleManagerDAO {
|
|||||||
$sql = "DELETE FROM articles WHERE id = :id;";
|
$sql = "DELETE FROM articles WHERE id = :id;";
|
||||||
|
|
||||||
$command = $db->prepare($sql);
|
$command = $db->prepare($sql);
|
||||||
if (!$command) {
|
$command->execute([":id" => $id]);
|
||||||
throw new InternalServerErrorException("internal_error");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!$command->execute([":id" => $id])) {
|
|
||||||
throw new InternalServerErrorException("internal_error");
|
|
||||||
}
|
|
||||||
} catch (PDOException $exc) {
|
} catch (PDOException $exc) {
|
||||||
throw new InternalServerErrorException("internal_error");
|
throw new InternalServerErrorException("internal_error");
|
||||||
}
|
}
|
||||||
@@ -167,10 +155,6 @@ class DatabaseArticleManager implements ArticleManagerDAO {
|
|||||||
$sql = "SELECT * FROM articles WHERE id = :id;";
|
$sql = "SELECT * FROM articles WHERE id = :id;";
|
||||||
|
|
||||||
$command = $db->prepare($sql);
|
$command = $db->prepare($sql);
|
||||||
if (!$command) {
|
|
||||||
throw new InternalServerErrorException("internal_error");
|
|
||||||
}
|
|
||||||
|
|
||||||
$command->execute([":id" => $id]);
|
$command->execute([":id" => $id]);
|
||||||
$row = $command->fetch(PDO::FETCH_ASSOC);
|
$row = $command->fetch(PDO::FETCH_ASSOC);
|
||||||
|
|
||||||
@@ -202,10 +186,6 @@ class DatabaseArticleManager implements ArticleManagerDAO {
|
|||||||
$sql = "SELECT * FROM articles;";
|
$sql = "SELECT * FROM articles;";
|
||||||
|
|
||||||
$command = $db->query($sql);
|
$command = $db->query($sql);
|
||||||
if (!$command) {
|
|
||||||
throw new InternalServerErrorException("internal_error");
|
|
||||||
}
|
|
||||||
|
|
||||||
$rows = $command->fetchAll(PDO::FETCH_ASSOC);
|
$rows = $command->fetchAll(PDO::FETCH_ASSOC);
|
||||||
$articles = [];
|
$articles = [];
|
||||||
|
|
||||||
@@ -234,10 +214,6 @@ class DatabaseArticleManager implements ArticleManagerDAO {
|
|||||||
$sql = "SELECT * FROM articles WHERE author = :author;";
|
$sql = "SELECT * FROM articles WHERE author = :author;";
|
||||||
|
|
||||||
$command = $db->prepare($sql);
|
$command = $db->prepare($sql);
|
||||||
if (!$command) {
|
|
||||||
throw new InternalServerErrorException("internal_error");
|
|
||||||
}
|
|
||||||
|
|
||||||
$command->execute([":author" => $author]);
|
$command->execute([":author" => $author]);
|
||||||
$rows = $command->fetchAll(PDO::FETCH_ASSOC);
|
$rows = $command->fetchAll(PDO::FETCH_ASSOC);
|
||||||
$filteredArticles = [];
|
$filteredArticles = [];
|
||||||
@@ -270,10 +246,6 @@ class DatabaseArticleManager implements ArticleManagerDAO {
|
|||||||
$sql = "SELECT * FROM articles WHERE category = :category;";
|
$sql = "SELECT * FROM articles WHERE category = :category;";
|
||||||
|
|
||||||
$command = $db->prepare($sql);
|
$command = $db->prepare($sql);
|
||||||
if (!$command) {
|
|
||||||
throw new InternalServerErrorException("internal_error");
|
|
||||||
}
|
|
||||||
|
|
||||||
$command->execute([":category" => $category]);
|
$command->execute([":category" => $category]);
|
||||||
$rows = $command->fetchAll(PDO::FETCH_ASSOC);
|
$rows = $command->fetchAll(PDO::FETCH_ASSOC);
|
||||||
$filteredArticles = [];
|
$filteredArticles = [];
|
||||||
@@ -318,21 +290,14 @@ class DatabaseArticleManager implements ArticleManagerDAO {
|
|||||||
OR tags LIKE :keyword";
|
OR tags LIKE :keyword";
|
||||||
|
|
||||||
$command = $db->prepare($sql);
|
$command = $db->prepare($sql);
|
||||||
if (!$command) {
|
|
||||||
throw new InternalServerErrorException("internal_error");
|
|
||||||
}
|
|
||||||
|
|
||||||
// Wildcards für die Suche hinzufügen
|
// Wildcards für die Suche hinzufügen
|
||||||
$searchParam = '%' . $cleankeyword . '%';
|
$searchParam = '%' . $cleankeyword . '%';
|
||||||
|
|
||||||
$success = $command->execute([
|
$command->execute([
|
||||||
":keyword" => $searchParam
|
":keyword" => $searchParam
|
||||||
]);
|
]);
|
||||||
|
|
||||||
if (!$success) {
|
|
||||||
throw new InternalServerErrorException("internal_error");
|
|
||||||
}
|
|
||||||
|
|
||||||
$rows = $command->fetchAll(PDO::FETCH_ASSOC);
|
$rows = $command->fetchAll(PDO::FETCH_ASSOC);
|
||||||
$filteredArticles = [];
|
$filteredArticles = [];
|
||||||
|
|
||||||
|
|||||||
@@ -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]
|
||||||
|
|||||||
@@ -1,5 +1,20 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* NEU: Prüft, ob ein übergebener Wert eine gültige, positive
|
||||||
|
* Beitrags-ID ist. Wird überall dort verwendet, wo eine Artikel-ID
|
||||||
|
* aus $_GET oder $_POST entgegengenommen wird
|
||||||
|
*
|
||||||
|
* @param mixed $id
|
||||||
|
* @return int|false Die validierte ID als int, oder false bei Ungültigkeit
|
||||||
|
*/
|
||||||
|
function articleIdValidator($id)
|
||||||
|
{
|
||||||
|
$options = ["options" => ["min_range" => 1]];
|
||||||
|
|
||||||
|
return filter_var($id, FILTER_VALIDATE_INT, $options);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Prüft, ob der Titel die folgenden Bedingungen erfüllt:
|
* Prüft, ob der Titel die folgenden Bedingungen erfüllt:
|
||||||
* Buchstaben von a-z; A-Z
|
* Buchstaben von a-z; A-Z
|
||||||
|
|||||||
Reference in New Issue
Block a user