Compare commits
42 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 325df51812 | |||
| 97afd1ea06 | |||
| e3903b4f3e | |||
| 031f2afd25 | |||
| 7189133861 | |||
| 8c961a56ce | |||
| 4f95a3d423 | |||
| 5310083e11 | |||
| b564306fb3 | |||
| 7b7208ad6b | |||
| ada97ec538 | |||
| 0b4c5f988d | |||
| 59eeb05e4b | |||
| 5d8239822a | |||
| 31585511d0 | |||
| 2ec1b70c91 | |||
| 8f8b25249c | |||
| 8be6e6d61c | |||
| bad0c08e6f | |||
| 55695f3f82 | |||
| 74cbc60cbe | |||
| c09c7ecfa3 | |||
| 944e332b49 | |||
| 4306785be9 | |||
| 266b70a094 | |||
| bd8593d212 | |||
| 0d009262b6 | |||
| bb2c368b1f | |||
| a712f5a993 | |||
| 53bee08833 | |||
| 7a2a3ac1e9 | |||
| a5f02c9a6c | |||
| f77f09ec6f | |||
| 535df85cff | |||
| 82e8c8a1a3 | |||
| c0d8fdede6 | |||
| 958ee1c1f5 | |||
| 1dd128d480 | |||
| dca716ddc5 | |||
| c1dd4fce2b | |||
| 16cfa92721 | |||
| 3b2ed78c1c |
Generated
+1
-1
@@ -1,6 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="dataSourceStorageLocal" created-in="IU-261.25134.95">
|
||||
<component name="dataSourceStorageLocal" created-in="IU-253.32098.101">
|
||||
<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">
|
||||
<identifier-quote-string>"</identifier-quote-string>
|
||||
|
||||
@@ -36,6 +36,9 @@
|
||||
- JavaScript wird verwendet, um im erweitertem Beitragseditor clientseitig einzelne Content-Boxen erstellen und löschen
|
||||
zu können.
|
||||
- JavaScript wird ebenfalls verwendet, um in die Suchergebnisse clientseitig zu sortieren.
|
||||
- Die Kommentarfunktion ist zusätzlich auch ohne JavaScript nutzbar. Kommentare und Antworten können auch ohne JavaScript erstellt werden.
|
||||
- Eigene Kommentare und Antworten können bearbeitet und gelöscht werden. Die Bearbeitung und das Löschen funktionieren ebenfalls ohne JavaScript über normale Formulare und serverseitige Verarbeitung.
|
||||
|
||||
|
||||
## Sonstiges
|
||||
- Das Datenschema befindet sich unter /planung/Datenschema.pdf
|
||||
|
||||
+1
-2
@@ -14,7 +14,6 @@
|
||||
?>
|
||||
<h1>404 - Seite nicht vorhanden</h1>
|
||||
<p>
|
||||
Später im Projekt sollen über index.php?pfad= ... der Inhalt der index.php dynamisch gesetzt werden.
|
||||
Alle Content-Links der Navbar führen zunächst hier her. Exemplarisch wurden für die Fächer Informatik, Physik, Mathematik der Content als Seite erstellt und kann über die Navbar ausgekundschaftet werden.
|
||||
Die Seite, nach der du suchst, existiert hier leider nicht. Entweder haben wir sie verlegt, ein hungriger Bug hat sie gefressen, oder du hast dich beim Tippen vertippt. (Wir schieben die Schuld einfach mal ganz unauffällig auf dich.)
|
||||
</p>
|
||||
</main>
|
||||
+3
-6
@@ -11,12 +11,9 @@ include_once 'php/controller/home-controller.php';
|
||||
|
||||
<h1>Home</h1>
|
||||
<p>
|
||||
Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et
|
||||
dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet
|
||||
clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet,
|
||||
consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat,
|
||||
sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no
|
||||
sea takimata sanctus est Lorem ipsum dolor sit amet.
|
||||
Schön, dass du da bist. EduForge ist deine digitale Werkstatt für Lernerfolg. Wir unterstützen Schüler und
|
||||
Studierende dabei, die passenden Lernmaterialien zu finden, Wissenslücken zu schließen und Gelerntes gezielt
|
||||
zu vertiefen.
|
||||
</p>
|
||||
|
||||
<!-- Flexbox für aktive Kategorien -->
|
||||
|
||||
+306
-22
@@ -5,6 +5,34 @@ $repliesByParent = [];
|
||||
$articleObj = null;
|
||||
|
||||
include_once 'php/controller/showArticle-controller.php';
|
||||
/*
|
||||
* Ermittelt, ob ohne JavaScript auf einen Kommentar
|
||||
* geantwortet werden soll.
|
||||
*/
|
||||
$replyTo = filter_input(
|
||||
INPUT_GET,
|
||||
"reply_to",
|
||||
FILTER_VALIDATE_INT
|
||||
);
|
||||
|
||||
$replyAuthor = null;
|
||||
|
||||
if ($replyTo !== false && $replyTo !== null) {
|
||||
foreach ($mainComments as $mainComment) {
|
||||
if ($mainComment->getId() === $replyTo) {
|
||||
$replyAuthor = $mainComment->getAuthor();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Eine Antwort darf nur auf einen existierenden
|
||||
* Hauptkommentar geschrieben werden.
|
||||
*/
|
||||
if ($replyAuthor === null) {
|
||||
$replyTo = null;
|
||||
}
|
||||
?>
|
||||
<!--
|
||||
Seite: Anzeige für Beiträge
|
||||
@@ -15,8 +43,6 @@ include_once 'php/controller/showArticle-controller.php';
|
||||
<?php include_once "includes/alertMessages.php"?>
|
||||
|
||||
<!-- Metadaten & Titel -->
|
||||
<div class="article-view-top-section">
|
||||
|
||||
<div class="article-view-top-section">
|
||||
|
||||
<div class="category-and-likes-row">
|
||||
@@ -45,8 +71,8 @@ include_once 'php/controller/showArticle-controller.php';
|
||||
</h1>
|
||||
|
||||
<div class="article-view-meta">
|
||||
<?php if (isset($author) && !empty($author)): ?>
|
||||
<span class="article-view-author">Von: <strong><?php echo htmlspecialchars($author); ?></strong></span>
|
||||
<?php if (isset($name) && !empty($name)): ?>
|
||||
<span class="article-view-author">Von: <strong><?php echo htmlspecialchars($name); ?></strong></span>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
|
||||
@@ -106,39 +132,268 @@ include_once 'php/controller/showArticle-controller.php';
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
|
||||
<section class="article-comments-section">
|
||||
<section class="article-comments-section" id="comments">
|
||||
<h2>Kommentare</h2>
|
||||
|
||||
<?php if (isset($_SESSION["comment_message"])): ?>
|
||||
<div class="alert-message <?php
|
||||
echo ($_SESSION["comment_message_type"] ?? "") === "success"
|
||||
? "is-success"
|
||||
: "is-error";
|
||||
?>">
|
||||
<?php echo htmlspecialchars($_SESSION["comment_message"]); ?>
|
||||
</div>
|
||||
|
||||
<?php
|
||||
unset($_SESSION["comment_message"]);
|
||||
unset($_SESSION["comment_message_type"]);
|
||||
?>
|
||||
<?php endif; ?>
|
||||
|
||||
<div id="comments-list">
|
||||
<?php if (!empty($mainComments)): ?>
|
||||
<?php foreach ($mainComments as $comment): ?>
|
||||
<div class="comment-item" data-comment-id="<?php echo htmlspecialchars($comment->getId()); ?>">
|
||||
<p>
|
||||
<strong><?php echo htmlspecialchars($comment->getAuthor()); ?></strong>
|
||||
<span><?php echo htmlspecialchars($comment->getCreated()); ?></span>
|
||||
<div class="comment-item"
|
||||
data-comment-id="<?php echo htmlspecialchars(
|
||||
(string) $comment->getId()
|
||||
); ?>">
|
||||
|
||||
<?php
|
||||
$isDeleted = $comment->getContent()
|
||||
=== "Dieser Kommentar wurde gelöscht.";
|
||||
?>
|
||||
|
||||
<?php if ($isDeleted): ?>
|
||||
|
||||
<p class="deleted-comment">
|
||||
Dieser Kommentar wurde gelöscht.
|
||||
</p>
|
||||
|
||||
<p><?php echo nl2br(htmlspecialchars($comment->getContent())); ?></p>
|
||||
<?php else: ?>
|
||||
|
||||
<p>
|
||||
<strong>
|
||||
<?php echo htmlspecialchars($comment->getAuthor()); ?>
|
||||
</strong>
|
||||
|
||||
<span>
|
||||
<?php echo htmlspecialchars($comment->getCreated()); ?>
|
||||
</span>
|
||||
</p>
|
||||
|
||||
<p>
|
||||
<?php
|
||||
echo nl2br(
|
||||
htmlspecialchars($comment->getContent())
|
||||
);
|
||||
?>
|
||||
</p>
|
||||
|
||||
<?php if (
|
||||
isset($_SESSION["user_email"])
|
||||
&& $_SESSION["user_email"] === $comment->getAuthor()
|
||||
): ?>
|
||||
<details class="edit-comment-details">
|
||||
<summary class="edit-comment-button">
|
||||
Kommentar bearbeiten
|
||||
</summary>
|
||||
|
||||
<form method="post"
|
||||
action="index.php?pfad=updateComment"
|
||||
class="edit-comment-form">
|
||||
|
||||
<input type="hidden"
|
||||
name="comment_id"
|
||||
value="<?php echo htmlspecialchars(
|
||||
(string) $comment->getId()
|
||||
); ?>">
|
||||
|
||||
<input type="hidden"
|
||||
name="article_id"
|
||||
value="<?php echo htmlspecialchars(
|
||||
(string) $comment->getArticleId()
|
||||
); ?>">
|
||||
|
||||
<label for="edit-comment-<?php
|
||||
echo htmlspecialchars((string) $comment->getId());
|
||||
?>">
|
||||
Kommentar bearbeiten
|
||||
</label>
|
||||
|
||||
<textarea
|
||||
id="edit-comment-<?php
|
||||
echo htmlspecialchars((string) $comment->getId());
|
||||
?>"
|
||||
name="content"
|
||||
required><?php echo htmlspecialchars(
|
||||
$comment->getContent()
|
||||
); ?></textarea>
|
||||
|
||||
<button type="submit" class="button">
|
||||
Änderungen speichern
|
||||
</button>
|
||||
</form>
|
||||
</details>
|
||||
|
||||
<form method="post"
|
||||
action="index.php?pfad=deleteComment"
|
||||
class="delete-comment-form">
|
||||
|
||||
<input type="hidden"
|
||||
name="comment_id"
|
||||
value="<?php echo htmlspecialchars(
|
||||
(string) $comment->getId()
|
||||
); ?>">
|
||||
|
||||
<input type="hidden"
|
||||
name="article_id"
|
||||
value="<?php echo htmlspecialchars(
|
||||
(string) $comment->getArticleId()
|
||||
); ?>">
|
||||
|
||||
<button type="submit"
|
||||
class="delete-comment-button"
|
||||
onclick="return confirm('Möchtest du diesen Kommentar wirklich löschen?');">
|
||||
Kommentar löschen
|
||||
</button>
|
||||
</form>
|
||||
|
||||
<?php endif; ?>
|
||||
|
||||
<?php if (isset($_SESSION["user_email"])): ?>
|
||||
<button type="button"
|
||||
|
||||
<a href="index.php?pfad=<?php
|
||||
echo urlencode($_GET["pfad"] ?? "showArticle");
|
||||
?>&id=<?php
|
||||
echo urlencode((string) $comment->getArticleId());
|
||||
?>&reply_to=<?php
|
||||
echo urlencode((string) $comment->getId());
|
||||
?>#comment-form"
|
||||
class="reply-button"
|
||||
data-comment-id="<?php echo htmlspecialchars($comment->getId()); ?>"
|
||||
data-author="<?php echo htmlspecialchars($comment->getAuthor()); ?>">
|
||||
data-comment-id="<?php echo htmlspecialchars(
|
||||
(string) $comment->getId()
|
||||
); ?>"
|
||||
data-author="<?php echo htmlspecialchars(
|
||||
$comment->getAuthor()
|
||||
); ?>">
|
||||
|
||||
Antworten
|
||||
</button>
|
||||
</a>
|
||||
|
||||
<?php endif; ?>
|
||||
|
||||
<?php endif; ?>
|
||||
|
||||
<div class="comment-replies">
|
||||
<?php if (isset($repliesByParent[$comment->getId()])): ?>
|
||||
<?php foreach ($repliesByParent[$comment->getId()] as $reply): ?>
|
||||
<div class="comment-item comment-reply">
|
||||
<p>
|
||||
<strong><?php echo htmlspecialchars($reply->getAuthor()); ?></strong>
|
||||
<span><?php echo htmlspecialchars($reply->getCreated()); ?></span>
|
||||
|
||||
<?php
|
||||
$isReplyDeleted = $reply->getContent()
|
||||
=== "Dieser Kommentar wurde gelöscht.";
|
||||
?>
|
||||
|
||||
<?php if ($isReplyDeleted): ?>
|
||||
|
||||
<p class="deleted-comment">
|
||||
Dieser Kommentar wurde gelöscht.
|
||||
</p>
|
||||
|
||||
<p><?php echo nl2br(htmlspecialchars($reply->getContent())); ?></p>
|
||||
<?php else: ?>
|
||||
|
||||
<p>
|
||||
<strong>
|
||||
<?php echo htmlspecialchars($reply->getAuthor()); ?>
|
||||
</strong>
|
||||
|
||||
<span>
|
||||
<?php echo htmlspecialchars($reply->getCreated()); ?>
|
||||
</span>
|
||||
</p>
|
||||
|
||||
<p>
|
||||
<?php
|
||||
echo nl2br(
|
||||
htmlspecialchars($reply->getContent())
|
||||
);
|
||||
?>
|
||||
</p>
|
||||
|
||||
<?php if (
|
||||
isset($_SESSION["user_email"])
|
||||
&& $_SESSION["user_email"] === $reply->getAuthor()
|
||||
): ?>
|
||||
|
||||
<details class="edit-comment-details">
|
||||
<summary class="edit-comment-button">
|
||||
Antwort bearbeiten
|
||||
</summary>
|
||||
|
||||
<form method="post"
|
||||
action="index.php?pfad=updateComment"
|
||||
class="edit-comment-form">
|
||||
|
||||
<input type="hidden"
|
||||
name="comment_id"
|
||||
value="<?php echo htmlspecialchars(
|
||||
(string) $reply->getId()
|
||||
); ?>">
|
||||
|
||||
<input type="hidden"
|
||||
name="article_id"
|
||||
value="<?php echo htmlspecialchars(
|
||||
(string) $reply->getArticleId()
|
||||
); ?>">
|
||||
|
||||
<label for="edit-reply-<?php
|
||||
echo htmlspecialchars((string) $reply->getId());
|
||||
?>">
|
||||
Antwort bearbeiten
|
||||
</label>
|
||||
|
||||
<textarea
|
||||
id="edit-reply-<?php
|
||||
echo htmlspecialchars((string) $reply->getId());
|
||||
?>"
|
||||
name="content"
|
||||
required><?php echo htmlspecialchars(
|
||||
$reply->getContent()
|
||||
); ?></textarea>
|
||||
|
||||
<button type="submit" class="button">
|
||||
Änderungen speichern
|
||||
</button>
|
||||
</form>
|
||||
</details>
|
||||
|
||||
<form method="post"
|
||||
action="index.php?pfad=deleteComment"
|
||||
class="delete-comment-form">
|
||||
|
||||
<input type="hidden"
|
||||
name="comment_id"
|
||||
value="<?php echo htmlspecialchars(
|
||||
(string) $reply->getId()
|
||||
); ?>">
|
||||
|
||||
<input type="hidden"
|
||||
name="article_id"
|
||||
value="<?php echo htmlspecialchars(
|
||||
(string) $reply->getArticleId()
|
||||
); ?>">
|
||||
|
||||
<button type="submit"
|
||||
class="delete-comment-button"
|
||||
onclick="return confirm('Möchtest du diesen Kommentar wirklich löschen?');">
|
||||
Kommentar löschen
|
||||
</button>
|
||||
</form>
|
||||
|
||||
<?php endif; ?>
|
||||
|
||||
<?php endif; ?>
|
||||
|
||||
</div>
|
||||
<?php endforeach; ?>
|
||||
<?php endif; ?>
|
||||
@@ -153,17 +408,46 @@ include_once 'php/controller/showArticle-controller.php';
|
||||
</div>
|
||||
|
||||
<?php if (isset($_SESSION["user_email"])): ?>
|
||||
<form id="comment-form">
|
||||
<form id="comment-form"
|
||||
method="post"
|
||||
action="php/ajax/add-comment.php">
|
||||
|
||||
<input type="hidden"
|
||||
name="article_id"
|
||||
value="<?php echo htmlspecialchars($_GET["id"] ?? ""); ?>">
|
||||
value="<?php echo htmlspecialchars(
|
||||
(string) ($_GET["id"] ?? "")
|
||||
); ?>">
|
||||
|
||||
<input type="hidden"
|
||||
name="parent_comment_id"
|
||||
id="parent-comment-id"
|
||||
value="">
|
||||
value="<?php echo $replyTo !== null
|
||||
? htmlspecialchars((string) $replyTo)
|
||||
: "";
|
||||
?>">
|
||||
|
||||
<p id="reply-info" class="reply-info" style="display: none;"></p>
|
||||
<p id="reply-info"
|
||||
class="reply-info"
|
||||
<?php if ($replyAuthor === null): ?>
|
||||
style="display: none;"
|
||||
<?php endif; ?>>
|
||||
|
||||
<?php if ($replyAuthor !== null): ?>
|
||||
Antwort auf <?php echo htmlspecialchars($replyAuthor); ?>
|
||||
|
||||
<a href="index.php?pfad=<?php
|
||||
echo urlencode($_GET["pfad"] ?? "showArticle");
|
||||
?>&id=<?php
|
||||
echo urlencode((string) ($_GET["id"] ?? ""));
|
||||
?>#comment-form">
|
||||
Abbrechen
|
||||
</a>
|
||||
<?php endif; ?>
|
||||
</p>
|
||||
|
||||
<label for="comment-content">
|
||||
Kommentar
|
||||
</label>
|
||||
|
||||
<textarea name="content"
|
||||
id="comment-content"
|
||||
|
||||
+95
-16
@@ -1,32 +1,111 @@
|
||||
<?php
|
||||
include_once "php/controller/showCategory-controller.php";
|
||||
|
||||
$currentSort = isset($sortStyle) ? $sortStyle : 'alphabet';
|
||||
$currentPage = isset($page) ? $page : 1;
|
||||
?>
|
||||
|
||||
<main>
|
||||
<?php include_once "includes/alertMessages.php"?>
|
||||
<noscript>
|
||||
Bitte JavaScript aktivieren!
|
||||
</noscript>
|
||||
|
||||
<h1><?php if (isset($category) && !empty($category)){ echo htmlspecialchars($category); } ?></h1>
|
||||
<!-- Das Formular umschließt jetzt das gesamte Layout -->
|
||||
<form id="search-form-id" action="index.php" method="GET" style="display: contents;">
|
||||
<input type="hidden" name="pfad" value="showCategory">
|
||||
<input type="hidden" name="category" value="<?php echo (isset($category) && !empty($category)) ? htmlspecialchars($category) : ''; ?>">
|
||||
<input type="hidden" id="s-res-page-input" name="page" value="<?php echo $currentPage; ?>">
|
||||
|
||||
<div class="s-res-list">
|
||||
<?php
|
||||
if (!empty($articles)): ?>
|
||||
<div class="cat-view-container">
|
||||
|
||||
<!-- LINKE SPALTE: SEITENLEISTE FÜR SORTIERUNG -->
|
||||
<aside class="cat-sidebar">
|
||||
<div class="cat-sidebar-box">
|
||||
<h3 class="cat-sidebar-title">Sortierung</h3>
|
||||
<div class="cat-filter-group">
|
||||
<label class="cat-filter-option">
|
||||
<input type="radio" name="sort" value="alphabet" <?php echo ($currentSort === 'alphabet') ? 'checked' : ''; ?> onchange="this.form.submit();">
|
||||
Alphabetisch
|
||||
</label>
|
||||
<label class="cat-filter-option">
|
||||
<input type="radio" name="sort" value="likes" <?php echo ($currentSort === 'likes') ? 'checked' : ''; ?> onchange="this.form.submit();">
|
||||
Beliebtheit (Likes)
|
||||
</label>
|
||||
<label class="cat-filter-option">
|
||||
<input type="radio" name="sort" value="newest" <?php echo ($currentSort === 'newest') ? 'checked' : ''; ?> onchange="this.form.submit();">
|
||||
Neueste Beiträge
|
||||
</label>
|
||||
<label class="cat-filter-option">
|
||||
<input type="radio" name="sort" value="oldest" <?php echo ($currentSort === 'oldest') ? 'checked' : ''; ?> onchange="this.form.submit();">
|
||||
Älteste Beiträge
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</aside>
|
||||
|
||||
<!-- RECHTE SPALTE: INHALT & PAGINATOR -->
|
||||
<main class="cat-view-main">
|
||||
<div class="cat-view-header">
|
||||
<h1 class="cat-view-title">Kategorie: <?php echo (isset($category) && !empty($category)) ? htmlspecialchars($category) : 'Unbekannt'; ?></h1>
|
||||
<p class="cat-view-meta-text">
|
||||
<?php echo (isset($totalArticles)) ? $totalArticles : 0; ?> Treffer in dieser Kategorie
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<!-- Beiträge -->
|
||||
<div class="cat-view-list">
|
||||
<?php if (isset($articles) && !empty($articles)): ?>
|
||||
<?php foreach ($articles as $article): ?>
|
||||
<div class="s-res-item">
|
||||
<div class="s-res-content">
|
||||
<h2 class="s-res-item-title">
|
||||
<a href="index.php?pfad=showArticle&id=<?php echo $article->getId(); ?>" class="s-res-link">
|
||||
<?php echo htmlspecialchars($article->getTitle()); ?>
|
||||
<div class="cat-view-item">
|
||||
<div class="cat-view-content">
|
||||
<h2 class="cat-view-item-title">
|
||||
<a href="index.php?pfad=showArticle&id=<?php echo htmlspecialchars($article->getID()); ?>" class="cat-view-link">
|
||||
<?php echo (isset($article) && !empty($article->getTitle())) ? htmlspecialchars($article->getTitle()) : 'Kein Titel'; ?>
|
||||
</a>
|
||||
</h2>
|
||||
<p class="s-res-author">Von: <span class="s-res-author-name"><?php echo htmlspecialchars($article->getAuthor()); ?></span></p>
|
||||
|
||||
<div class="cat-view-meta-row">
|
||||
<p class="cat-view-author">
|
||||
von <span class="cat-view-author-name"><?php echo (isset($article) && !empty($article->getAuthor())) ? htmlspecialchars($article->getAuthor()) : 'Anonym'; ?></span>
|
||||
</p>
|
||||
<span class="cat-view-likes">
|
||||
❤️ <?php echo (isset($article) && !empty($article->getLikes())) ? htmlspecialchars($article->getLikes()) : '0'; ?>
|
||||
</span>
|
||||
</div>
|
||||
<div class="s-res-arrow">→</div>
|
||||
|
||||
</div>
|
||||
<div class="cat-view-arrow">→</div>
|
||||
</div>
|
||||
<?php endforeach; ?>
|
||||
<?php
|
||||
else: ?>
|
||||
<p> Es sind noch keine Beiträge in dieser Kategorie enthalten.</p>
|
||||
<?php else: ?>
|
||||
<p class="cat-view-meta-text">Keine Artikel in dieser Kategorie gefunden.</p>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
|
||||
<!-- Footer Paginator -->
|
||||
<?php if (isset($totalPages) && $totalPages > 1): ?>
|
||||
<div class="cat-view-pagination-footer">
|
||||
<div></div>
|
||||
<div class="s-res-page-navigation">
|
||||
<button type="button" class="s-res-page-btn" data-page="<?php echo $currentPage - 1; ?>" <?php echo ($currentPage <= 1) ? 'disabled' : ''; ?>>
|
||||
«
|
||||
</button>
|
||||
|
||||
<?php for ($i = 1; $i <= $totalPages; $i++): ?>
|
||||
<button type="button"
|
||||
class="s-res-page-btn <?php echo ($i === $currentPage) ? 'cat-active-btn' : ''; ?>"
|
||||
data-page="<?php echo $i; ?>"
|
||||
<?php echo ($i === $currentPage) ? 'disabled' : ''; ?>>
|
||||
<?php echo $i; ?>
|
||||
</button>
|
||||
<?php endfor; ?>
|
||||
|
||||
<button type="button" class="s-res-page-btn" data-page="<?php echo $currentPage + 1; ?>" <?php echo ($currentPage >= $totalPages) ? 'disabled' : ''; ?>>
|
||||
»
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
</main>
|
||||
|
||||
</div>
|
||||
</form>
|
||||
|
||||
@@ -0,0 +1,227 @@
|
||||
/* --- ZWEISPALTIIGES GRID-LAYOUT --- */
|
||||
.cat-view-container {
|
||||
box-sizing: border-box;
|
||||
max-width: 95%; /* 95% Monitorbreite wie das Original */
|
||||
width: 95%;
|
||||
margin: 2rem auto;
|
||||
padding: 0 1rem;
|
||||
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
|
||||
color: #212529;
|
||||
line-height: 1.5;
|
||||
|
||||
/* Grid für die Zweispaltigkeit */
|
||||
display: grid;
|
||||
grid-template-columns: 320px 1fr; /* 320px Sidebar, Rest für Content */
|
||||
gap: 2.5rem;
|
||||
}
|
||||
|
||||
.cat-view-container * {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
/* --- SEITENLEISTE (SORTIERUNG) --- */
|
||||
.cat-sidebar {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 1.5rem;
|
||||
}
|
||||
|
||||
.cat-sidebar-box {
|
||||
background-color: #ffffff;
|
||||
border: 1px solid #e2e8f0;
|
||||
border-radius: 8px;
|
||||
padding: 1.25rem;
|
||||
}
|
||||
|
||||
.cat-sidebar-title {
|
||||
font-size: 1rem;
|
||||
font-weight: 700;
|
||||
color: #1a202c;
|
||||
margin: 0 0 1rem 0;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.05em;
|
||||
}
|
||||
|
||||
.cat-filter-group {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.75rem;
|
||||
}
|
||||
|
||||
.cat-filter-option {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
cursor: pointer;
|
||||
font-size: 0.95rem;
|
||||
color: #4a5568;
|
||||
}
|
||||
|
||||
.cat-filter-option input[type="radio"] {
|
||||
margin: 0;
|
||||
accent-color: #3182ce;
|
||||
}
|
||||
|
||||
/* --- HAUPTINHALT --- */
|
||||
.cat-view-main {
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.cat-view-header {
|
||||
margin-bottom: 2rem;
|
||||
border-bottom: 2px solid #dee2e6;
|
||||
padding-bottom: 1rem;
|
||||
}
|
||||
|
||||
.cat-view-title {
|
||||
font-size: 2rem;
|
||||
color: #1a202c;
|
||||
margin: 0 0 0.5rem 0;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.cat-view-meta-text {
|
||||
color: #6c757d;
|
||||
font-size: 0.95rem;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
/* --- BEITRÄGE LISTE --- */
|
||||
.cat-view-list {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 1rem;
|
||||
}
|
||||
|
||||
.cat-view-item {
|
||||
background-color: #ffffff;
|
||||
border: 1px solid #e2e8f0;
|
||||
border-radius: 8px;
|
||||
padding: 1.25rem;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
transition: transform 0.2s ease, box-shadow 0.2s ease, border-color 0.2s ease;
|
||||
}
|
||||
|
||||
.cat-view-item:hover {
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.05);
|
||||
border-color: #cbd5e1;
|
||||
}
|
||||
|
||||
.cat-view-content {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.cat-view-item-title {
|
||||
font-size: 1.25rem;
|
||||
margin: 0 0 0.25rem 0;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.cat-view-link {
|
||||
color: #3182ce;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.cat-view-link:hover {
|
||||
text-decoration: underline;
|
||||
color: #2b6cb0;
|
||||
}
|
||||
|
||||
.cat-view-meta-row {
|
||||
display: flex;
|
||||
gap: 15px;
|
||||
align-items: center;
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
.cat-view-author {
|
||||
font-size: 0.875rem;
|
||||
color: #4a5568;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.cat-view-author-name {
|
||||
font-weight: 600;
|
||||
color: #2d3748;
|
||||
}
|
||||
|
||||
.cat-view-likes {
|
||||
font-size: 0.9em;
|
||||
color: #475569;
|
||||
}
|
||||
|
||||
.cat-view-arrow {
|
||||
font-size: 1.5rem;
|
||||
color: #a0aec0;
|
||||
padding-left: 1rem;
|
||||
transition: color 0.2s ease, transform 0.2s ease;
|
||||
}
|
||||
|
||||
.cat-view-item:hover .cat-view-arrow {
|
||||
color: #3182ce;
|
||||
transform: translateX(3px);
|
||||
}
|
||||
|
||||
/* --- FOOTER PAGINATION --- */
|
||||
.cat-view-pagination-footer {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-top: 2rem;
|
||||
padding-top: 1.5rem;
|
||||
border-top: 1px solid #e2e8f0;
|
||||
}
|
||||
|
||||
.cat-view-pagination-footer .s-res-page-navigation {
|
||||
display: flex;
|
||||
gap: 0.25rem;
|
||||
}
|
||||
|
||||
.cat-view-pagination-footer .s-res-page-btn {
|
||||
background-color: #ffffff;
|
||||
border: 1px solid #cbd5e1;
|
||||
color: #4a5568;
|
||||
padding: 0.35rem 0.75rem;
|
||||
font-size: 0.9rem;
|
||||
font-weight: 500;
|
||||
border-radius: 4px;
|
||||
cursor: pointer;
|
||||
transition: all 0.2s ease;
|
||||
}
|
||||
|
||||
.cat-view-pagination-footer .s-res-page-btn:hover:not(:disabled) {
|
||||
background-color: #f8f9fa;
|
||||
border-color: #cbd5e1;
|
||||
color: #2d3748;
|
||||
}
|
||||
|
||||
.cat-view-pagination-footer .cat-active-btn {
|
||||
background-color: #3182ce;
|
||||
border-color: #3182ce;
|
||||
color: #ffffff;
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
.cat-view-pagination-footer .s-res-page-btn:disabled:not(.cat-active-btn) {
|
||||
background-color: #f1f5f9;
|
||||
border-color: #e2e8f0;
|
||||
color: #94a3b8;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
/* RESPONSIVE ANPASSUNG (Unter 768px) */
|
||||
@media (max-width: 768px) {
|
||||
.cat-view-container {
|
||||
grid-template-columns: 1fr; /* Stapelt Sidebar und Inhalt untereinander */
|
||||
gap: 1.5rem;
|
||||
margin: 1rem auto;
|
||||
}
|
||||
.cat-view-pagination-footer {
|
||||
flex-direction: column;
|
||||
gap: 1rem;
|
||||
align-items: flex-start;
|
||||
}
|
||||
}
|
||||
@@ -34,11 +34,20 @@ if ($pfad === "logout") {
|
||||
include_once "php/controller/logout-controller.php";
|
||||
exit();
|
||||
}
|
||||
|
||||
if ($pfad === "deleteAccount") {
|
||||
include_once "php/controller/deleteAccount-controller.php";
|
||||
exit();
|
||||
}
|
||||
|
||||
if ($pfad === "updateComment") {
|
||||
include_once "php/controller/updateComment-controller.php";
|
||||
exit();
|
||||
}
|
||||
|
||||
if ($pfad === "deleteComment") {
|
||||
include_once "php/controller/deleteComment-controller.php";
|
||||
exit();
|
||||
}
|
||||
?>
|
||||
|
||||
<!--
|
||||
@@ -63,6 +72,7 @@ if ($pfad === "deleteAccount") {
|
||||
<link rel="stylesheet" href="css/profile.css">
|
||||
<link rel="stylesheet" href="css/showArticle.css">
|
||||
<link rel="stylesheet" href="css/message.css">
|
||||
<link rel="stylesheet" href="css/showCategory.css">
|
||||
|
||||
<script src="js/paginator.js" async></script>
|
||||
<script src="js/sorter.js" async></script>
|
||||
|
||||
+174
-25
@@ -11,20 +11,31 @@ document.addEventListener("DOMContentLoaded", function () {
|
||||
const parentCommentInput = document.getElementById("parent-comment-id");
|
||||
const replyInfo = document.getElementById("reply-info");
|
||||
|
||||
if (!form || !commentsList || !commentContent || !parentCommentInput || !replyInfo) {
|
||||
if (!form || !commentsList || !commentContent || !parentCommentInput) {
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
* Aktiviert einen einzelnen Antworten-Button.
|
||||
* Aktiviert einen einzelnen Antworten-Link.
|
||||
*
|
||||
* @param {HTMLButtonElement} button Antworten-Button
|
||||
* @param {HTMLAnchorElement} replyLink Antworten-Link
|
||||
*/
|
||||
function registerReplyButton(button) {
|
||||
button.addEventListener("click", function () {
|
||||
parentCommentInput.value = button.dataset.commentId;
|
||||
replyInfo.textContent = "Antwort auf " + button.dataset.author;
|
||||
function registerReplyButton(replyLink) {
|
||||
replyLink.addEventListener("click", function (event) {
|
||||
/*
|
||||
* Mit JavaScript wird die Seite nicht neu geladen.
|
||||
* Ohne JavaScript funktioniert der normale Link.
|
||||
*/
|
||||
event.preventDefault();
|
||||
|
||||
parentCommentInput.value = replyLink.dataset.commentId;
|
||||
|
||||
if (replyInfo) {
|
||||
replyInfo.textContent =
|
||||
"Antwort auf " + replyLink.dataset.author;
|
||||
replyInfo.style.display = "block";
|
||||
}
|
||||
|
||||
commentContent.focus();
|
||||
});
|
||||
}
|
||||
@@ -45,18 +56,29 @@ document.addEventListener("DOMContentLoaded", function () {
|
||||
const formData = new FormData(form);
|
||||
const parentCommentId = parentCommentInput.value;
|
||||
|
||||
fetch("php/ajax/add-comment.php", {
|
||||
fetch(form.action, {
|
||||
method: "POST",
|
||||
body: formData
|
||||
body: formData,
|
||||
headers: {
|
||||
"X-Requested-With": "XMLHttpRequest"
|
||||
}
|
||||
})
|
||||
.then(response => response.json())
|
||||
.then(data => {
|
||||
.then(function (response) {
|
||||
if (!response.ok) {
|
||||
throw new Error("Fehlerhafte Serverantwort.");
|
||||
}
|
||||
|
||||
return response.json();
|
||||
})
|
||||
.then(function (data) {
|
||||
if (!data.success) {
|
||||
alert(data.message);
|
||||
return;
|
||||
}
|
||||
|
||||
const emptyMessage = commentsList.querySelector(".no-comments-message");
|
||||
const emptyMessage = commentsList.querySelector(
|
||||
".no-comments-message"
|
||||
);
|
||||
|
||||
if (emptyMessage) {
|
||||
emptyMessage.remove();
|
||||
@@ -64,8 +86,9 @@ document.addEventListener("DOMContentLoaded", function () {
|
||||
|
||||
const commentElement = document.createElement("div");
|
||||
commentElement.classList.add("comment-item");
|
||||
commentElement.dataset.commentId = data.commentId;
|
||||
|
||||
if (parentCommentId) {
|
||||
if (parentCommentId !== "") {
|
||||
commentElement.classList.add("comment-reply");
|
||||
|
||||
commentElement.innerHTML = `
|
||||
@@ -73,40 +96,162 @@ document.addEventListener("DOMContentLoaded", function () {
|
||||
<strong>${escapeHtml(data.author)}</strong>
|
||||
<span>${escapeHtml(data.created)}</span>
|
||||
</p>
|
||||
|
||||
<p>${escapeHtml(data.content).replace(/\n/g, "<br>")}</p>
|
||||
|
||||
<details class="edit-comment-details">
|
||||
<summary class="edit-comment-button">
|
||||
Antwort bearbeiten
|
||||
</summary>
|
||||
|
||||
<form
|
||||
method="post"
|
||||
action="index.php?pfad=updateComment"
|
||||
class="edit-comment-form"
|
||||
>
|
||||
<input
|
||||
type="hidden"
|
||||
name="comment_id"
|
||||
value="${escapeHtml(data.commentId)}"
|
||||
>
|
||||
|
||||
<input
|
||||
type="hidden"
|
||||
name="article_id"
|
||||
value="${escapeHtml(formData.get("article_id"))}"
|
||||
>
|
||||
|
||||
<textarea
|
||||
name="content"
|
||||
required
|
||||
>${escapeHtml(data.content)}</textarea>
|
||||
|
||||
<button type="submit" class="button">
|
||||
Änderungen speichern
|
||||
</button>
|
||||
</form>
|
||||
</details>
|
||||
|
||||
<form
|
||||
method="post"
|
||||
action="index.php?pfad=deleteComment"
|
||||
class="delete-comment-form"
|
||||
>
|
||||
<input
|
||||
type="hidden"
|
||||
name="comment_id"
|
||||
value="${escapeHtml(data.commentId)}"
|
||||
>
|
||||
|
||||
<input
|
||||
type="hidden"
|
||||
name="article_id"
|
||||
value="${escapeHtml(formData.get("article_id"))}"
|
||||
>
|
||||
|
||||
<button
|
||||
type="submit"
|
||||
class="delete-comment-button"
|
||||
onclick="return confirm('Möchtest du diesen Kommentar wirklich löschen?');"
|
||||
>
|
||||
Kommentar löschen
|
||||
</button>
|
||||
</form>
|
||||
`;
|
||||
|
||||
const parentReplies = document.querySelector(
|
||||
const parentReplies = commentsList.querySelector(
|
||||
`.comment-item[data-comment-id="${parentCommentId}"] .comment-replies`
|
||||
);
|
||||
|
||||
if (parentReplies) {
|
||||
parentReplies.appendChild(commentElement);
|
||||
}
|
||||
|
||||
} else {
|
||||
commentElement.dataset.commentId = data.commentId;
|
||||
|
||||
commentsList.prepend(commentElement);
|
||||
}
|
||||
} else {
|
||||
commentElement.innerHTML = `
|
||||
<p>
|
||||
<strong>${escapeHtml(data.author)}</strong>
|
||||
<span>${escapeHtml(data.created)}</span>
|
||||
</p>
|
||||
|
||||
<p>${escapeHtml(data.content).replace(/\n/g, "<br>")}</p>
|
||||
|
||||
<button type="button"
|
||||
<details class="edit-comment-details">
|
||||
<summary class="edit-comment-button">
|
||||
Kommentar bearbeiten
|
||||
</summary>
|
||||
|
||||
<form
|
||||
method="post"
|
||||
action="index.php?pfad=updateComment"
|
||||
class="edit-comment-form"
|
||||
>
|
||||
<input
|
||||
type="hidden"
|
||||
name="comment_id"
|
||||
value="${escapeHtml(data.commentId)}"
|
||||
>
|
||||
|
||||
<input
|
||||
type="hidden"
|
||||
name="article_id"
|
||||
value="${escapeHtml(formData.get("article_id"))}"
|
||||
>
|
||||
|
||||
<textarea
|
||||
name="content"
|
||||
required
|
||||
>${escapeHtml(data.content)}</textarea>
|
||||
|
||||
<button type="submit" class="button">
|
||||
Änderungen speichern
|
||||
</button>
|
||||
</form>
|
||||
</details>
|
||||
|
||||
<form
|
||||
method="post"
|
||||
action="index.php?pfad=deleteComment"
|
||||
class="delete-comment-form"
|
||||
>
|
||||
<input
|
||||
type="hidden"
|
||||
name="comment_id"
|
||||
value="${escapeHtml(data.commentId)}"
|
||||
>
|
||||
|
||||
<input
|
||||
type="hidden"
|
||||
name="article_id"
|
||||
value="${escapeHtml(formData.get("article_id"))}"
|
||||
>
|
||||
|
||||
<button
|
||||
type="submit"
|
||||
class="delete-comment-button"
|
||||
onclick="return confirm('Möchtest du diesen Kommentar wirklich löschen?');"
|
||||
>
|
||||
Kommentar löschen
|
||||
</button>
|
||||
</form>
|
||||
|
||||
<a
|
||||
href="#comment-form"
|
||||
class="reply-button"
|
||||
data-comment-id="${escapeHtml(data.commentId)}"
|
||||
data-author="${escapeHtml(data.author)}">
|
||||
data-author="${escapeHtml(data.author)}"
|
||||
>
|
||||
Antworten
|
||||
</button>
|
||||
</a>
|
||||
|
||||
<div class="comment-replies"></div>
|
||||
`;
|
||||
|
||||
commentsList.prepend(commentElement);
|
||||
|
||||
const newReplyButton = commentElement.querySelector(".reply-button");
|
||||
const newReplyButton =
|
||||
commentElement.querySelector(".reply-button");
|
||||
|
||||
if (newReplyButton) {
|
||||
registerReplyButton(newReplyButton);
|
||||
@@ -115,10 +260,14 @@ document.addEventListener("DOMContentLoaded", function () {
|
||||
|
||||
commentContent.value = "";
|
||||
parentCommentInput.value = "";
|
||||
|
||||
if (replyInfo) {
|
||||
replyInfo.textContent = "";
|
||||
replyInfo.style.display = "none";
|
||||
}
|
||||
})
|
||||
.catch(() => {
|
||||
.catch(function (error) {
|
||||
console.error(error);
|
||||
alert("Kommentar konnte nicht gesendet werden.");
|
||||
});
|
||||
});
|
||||
@@ -126,12 +275,12 @@ document.addEventListener("DOMContentLoaded", function () {
|
||||
/**
|
||||
* Entfernt HTML-Sonderzeichen aus Nutzereingaben.
|
||||
*
|
||||
* @param {string} text Zu bereinigender Text
|
||||
* @param {*} text Zu bereinigender Text
|
||||
* @returns {string} Sicherer Text
|
||||
*/
|
||||
function escapeHtml(text) {
|
||||
const div = document.createElement("div");
|
||||
div.textContent = text;
|
||||
div.textContent = String(text ?? "");
|
||||
return div.innerHTML;
|
||||
}
|
||||
});
|
||||
+151
-25
@@ -3,32 +3,153 @@ if (session_status() === PHP_SESSION_NONE) {
|
||||
session_start();
|
||||
}
|
||||
|
||||
header("Content-Type: application/json");
|
||||
|
||||
require_once "../model/CommentManager.php";
|
||||
|
||||
if (!isset($_SESSION["user_email"])) {
|
||||
echo json_encode([
|
||||
"success" => false,
|
||||
"message" => "Du musst angemeldet sein, um zu kommentieren."
|
||||
]);
|
||||
/**
|
||||
* Prüft, ob die Anfrage durch JavaScript per AJAX gesendet wurde.
|
||||
*/
|
||||
$isAjaxRequest = isset($_SERVER["HTTP_X_REQUESTED_WITH"])
|
||||
&& strtolower($_SERVER["HTTP_X_REQUESTED_WITH"]) === "xmlhttprequest";
|
||||
|
||||
/**
|
||||
* Gibt das Ergebnis entweder als JSON zurück oder leitet
|
||||
* bei einem normalen Formularaufruf wieder zum Beitrag zurück.
|
||||
*
|
||||
* @param bool $success War das Speichern erfolgreich?
|
||||
* @param string $message Rückmeldung für den Benutzer
|
||||
* @param int|null $articleId ID des Beitrags
|
||||
* @param array $additionalData Zusätzliche Daten für AJAX
|
||||
*/
|
||||
function sendCommentResponse(
|
||||
$success,
|
||||
$message,
|
||||
$articleId,
|
||||
$additionalData = []
|
||||
) {
|
||||
global $isAjaxRequest;
|
||||
|
||||
if ($isAjaxRequest) {
|
||||
header("Content-Type: application/json; charset=utf-8");
|
||||
|
||||
echo json_encode(
|
||||
array_merge(
|
||||
[
|
||||
"success" => $success,
|
||||
"message" => $message
|
||||
],
|
||||
$additionalData
|
||||
)
|
||||
);
|
||||
|
||||
exit();
|
||||
}
|
||||
|
||||
$articleId = $_POST["article_id"] ?? null;
|
||||
$content = trim($_POST["content"] ?? "");
|
||||
$parentCommentId = $_POST["parent_comment_id"] ?? null;
|
||||
/*
|
||||
* Bei deaktiviertem JavaScript wird die Rückmeldung
|
||||
* in der Session gespeichert und die Beitragsseite neu geladen.
|
||||
*/
|
||||
$_SESSION["comment_message"] = $message;
|
||||
$_SESSION["comment_message_type"] = $success ? "success" : "error";
|
||||
|
||||
if ($parentCommentId === "" || $parentCommentId === "0") {
|
||||
if ($articleId !== null) {
|
||||
header(
|
||||
"Location: ../../index.php?pfad=showArticle&id="
|
||||
. urlencode((string) $articleId)
|
||||
. "#comments"
|
||||
);
|
||||
} else {
|
||||
header("Location: ../../index.php");
|
||||
}
|
||||
|
||||
exit();
|
||||
}
|
||||
|
||||
/*
|
||||
* Nur POST-Anfragen dürfen Kommentare erstellen.
|
||||
*/
|
||||
if ($_SERVER["REQUEST_METHOD"] !== "POST") {
|
||||
sendCommentResponse(
|
||||
false,
|
||||
"Ungültige Anfrage.",
|
||||
null
|
||||
);
|
||||
}
|
||||
|
||||
/*
|
||||
* Die Beitrags-ID wird zuerst eingelesen,
|
||||
* damit bei Fehlern wieder zum Beitrag zurückgeleitet werden kann.
|
||||
*/
|
||||
$articleId = filter_input(
|
||||
INPUT_POST,
|
||||
"article_id",
|
||||
FILTER_VALIDATE_INT
|
||||
);
|
||||
|
||||
/*
|
||||
* Ein Benutzer muss angemeldet sein.
|
||||
*/
|
||||
if (!isset($_SESSION["user_email"])) {
|
||||
sendCommentResponse(
|
||||
false,
|
||||
"Du musst angemeldet sein, um zu kommentieren.",
|
||||
$articleId !== false ? $articleId : null
|
||||
);
|
||||
}
|
||||
|
||||
/*
|
||||
* Weitere Formulardaten einlesen.
|
||||
*/
|
||||
|
||||
$content = trim($_POST["content"] ?? "");
|
||||
|
||||
$parentCommentId = filter_input(
|
||||
INPUT_POST,
|
||||
"parent_comment_id",
|
||||
FILTER_VALIDATE_INT
|
||||
);
|
||||
|
||||
/*
|
||||
* Ein leerer Wert bedeutet, dass es sich um einen
|
||||
* normalen Hauptkommentar handelt.
|
||||
*/
|
||||
if (
|
||||
!isset($_POST["parent_comment_id"])
|
||||
|| $_POST["parent_comment_id"] === ""
|
||||
|| $_POST["parent_comment_id"] === "0"
|
||||
) {
|
||||
$parentCommentId = null;
|
||||
}
|
||||
|
||||
if (empty($articleId) || empty($content)) {
|
||||
echo json_encode([
|
||||
"success" => false,
|
||||
"message" => "Kommentar darf nicht leer sein."
|
||||
]);
|
||||
exit();
|
||||
if ($articleId === false || $articleId === null) {
|
||||
sendCommentResponse(
|
||||
false,
|
||||
"Der zugehörige Beitrag ist ungültig.",
|
||||
null
|
||||
);
|
||||
}
|
||||
|
||||
if ($content === "") {
|
||||
sendCommentResponse(
|
||||
false,
|
||||
"Der Kommentar darf nicht leer sein.",
|
||||
$articleId
|
||||
);
|
||||
}
|
||||
|
||||
/*
|
||||
* Eine ungültige Eltern-ID darf nicht gespeichert werden.
|
||||
*/
|
||||
if (
|
||||
isset($_POST["parent_comment_id"])
|
||||
&& $_POST["parent_comment_id"] !== ""
|
||||
&& $_POST["parent_comment_id"] !== "0"
|
||||
&& $parentCommentId === false
|
||||
) {
|
||||
sendCommentResponse(
|
||||
false,
|
||||
"Der ausgewählte Kommentar ist ungültig.",
|
||||
$articleId
|
||||
);
|
||||
}
|
||||
|
||||
try {
|
||||
@@ -41,18 +162,23 @@ try {
|
||||
$parentCommentId
|
||||
);
|
||||
|
||||
echo json_encode([
|
||||
"success" => true,
|
||||
sendCommentResponse(
|
||||
true,
|
||||
"Der Kommentar wurde erfolgreich gespeichert.",
|
||||
$articleId,
|
||||
[
|
||||
"commentId" => $commentId,
|
||||
"author" => $_SESSION["user_email"],
|
||||
"content" => $content,
|
||||
"created" => date("Y-m-d H:i:s"),
|
||||
"parentCommentId" => $parentCommentId
|
||||
]);
|
||||
]
|
||||
);
|
||||
|
||||
} catch (Exception $e) {
|
||||
echo json_encode([
|
||||
"success" => false,
|
||||
"message" => "Kommentar konnte nicht gespeichert werden."
|
||||
]);
|
||||
} catch (Throwable $e) {
|
||||
sendCommentResponse(
|
||||
false,
|
||||
"Der Kommentar konnte nicht gespeichert werden.",
|
||||
$articleId
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,105 @@
|
||||
<?php
|
||||
|
||||
if (session_status() === PHP_SESSION_NONE) {
|
||||
session_start();
|
||||
}
|
||||
|
||||
require_once __DIR__ . "/../model/CommentManager.php";
|
||||
|
||||
/*
|
||||
* Kommentare dürfen nur über ein POST-Formular gelöscht werden.
|
||||
*/
|
||||
if ($_SERVER["REQUEST_METHOD"] !== "POST") {
|
||||
header("Location: index.php");
|
||||
exit();
|
||||
}
|
||||
$commentId = filter_input(
|
||||
INPUT_POST,
|
||||
"comment_id",
|
||||
FILTER_VALIDATE_INT
|
||||
);
|
||||
|
||||
$articleId = filter_input(
|
||||
INPUT_POST,
|
||||
"article_id",
|
||||
FILTER_VALIDATE_INT
|
||||
);
|
||||
|
||||
/*
|
||||
* Nur angemeldete Nutzer dürfen Kommentare löschen.
|
||||
*/
|
||||
if (!isset($_SESSION["user_email"])) {
|
||||
$_SESSION["comment_message"] = "Du musst angemeldet sein.";
|
||||
$_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.
|
||||
*/
|
||||
if (
|
||||
$commentId === false
|
||||
|| $commentId === null
|
||||
|| $articleId === false
|
||||
|| $articleId === null
|
||||
) {
|
||||
$_SESSION["comment_message"] =
|
||||
"Der Kommentar konnte nicht gelöscht werden.";
|
||||
|
||||
$_SESSION["comment_message_type"] = "error";
|
||||
|
||||
header("Location: index.php");
|
||||
exit();
|
||||
}
|
||||
|
||||
try {
|
||||
$commentManager = CommentManager::getInstance();
|
||||
|
||||
/*
|
||||
* Die E-Mail-Adresse aus der Session wird mitgegeben.
|
||||
* Dadurch kann der Nutzer nur eigene Kommentare löschen.
|
||||
*/
|
||||
$deleted = $commentManager->deleteComment(
|
||||
$commentId,
|
||||
$_SESSION["user_email"]
|
||||
);
|
||||
|
||||
if ($deleted) {
|
||||
$_SESSION["comment_message"] =
|
||||
"Der Kommentar wurde gelöscht.";
|
||||
|
||||
$_SESSION["comment_message_type"] = "success";
|
||||
} else {
|
||||
$_SESSION["comment_message"] =
|
||||
"Der Kommentar wurde nicht gefunden oder gehört nicht dir.";
|
||||
|
||||
$_SESSION["comment_message_type"] = "error";
|
||||
}
|
||||
|
||||
} catch (Throwable $e) {
|
||||
$_SESSION["comment_message"] =
|
||||
"Der Kommentar konnte nicht gelöscht werden.";
|
||||
|
||||
$_SESSION["comment_message_type"] = "error";
|
||||
}
|
||||
|
||||
/*
|
||||
* Anschließend wird wieder zum Beitrag und zu den Kommentaren geleitet.
|
||||
*/
|
||||
header(
|
||||
"Location: index.php?pfad=showArticle&id="
|
||||
. urlencode((string) $articleId)
|
||||
. "#comments"
|
||||
);
|
||||
|
||||
exit();
|
||||
@@ -2,8 +2,8 @@
|
||||
if (session_status() === PHP_SESSION_NONE) {
|
||||
session_start();
|
||||
}
|
||||
require_once '../model/LocalArticleManager.php';
|
||||
require_once '../model/ArticleManager.php';
|
||||
require_once '../model/UserManager.php';
|
||||
require_once '../model/Article.php';
|
||||
require_once '../validator/search-validator.php';
|
||||
|
||||
@@ -18,6 +18,7 @@ if ($_SERVER["REQUEST_METHOD"] === "GET" && isset($_GET["q"])) {
|
||||
try {
|
||||
|
||||
$articleManager = ArticleManager::getInstance();
|
||||
$userManager = UserManager::getInstance();
|
||||
|
||||
$results = $articleManager->search($search);
|
||||
|
||||
@@ -52,7 +53,7 @@ if ($_SERVER["REQUEST_METHOD"] === "GET" && isset($_GET["q"])) {
|
||||
"id" => $obj->getId(),
|
||||
"title" => $obj->getTitle(),
|
||||
"content" => $obj->getContent(),
|
||||
"author" => $obj->getAuthor(),
|
||||
"author" => $userManager->findUser($obj->getAuthor())["vorname"] . " " . $userManager->findUser($obj->getAuthor())["nachname"],
|
||||
"category" => $obj->getCategory(),
|
||||
"tags" => $obj->getTags(),
|
||||
"creationDate" => $obj->getCreationDate(),
|
||||
|
||||
@@ -5,6 +5,7 @@ if (session_status() === PHP_SESSION_NONE) {
|
||||
|
||||
require_once 'php/model/Article.php';
|
||||
require_once 'php/model/ArticleManager.php';
|
||||
require_once 'php/model/UserManager.php';
|
||||
require_once 'php/model/CommentManager.php';
|
||||
|
||||
if (isset($_GET["id"]) && !empty($_GET["id"])){
|
||||
@@ -19,6 +20,9 @@ if (isset($_GET["id"]) && !empty($_GET["id"])){
|
||||
$author = $article->getAuthor();
|
||||
$tags = $article->getTags();
|
||||
$articleObj = $article; // Objekt für die Like-Abfagen sichern
|
||||
|
||||
$userManager = UserManager::getInstance();
|
||||
$name = $userManager->findUser($author)["vorname"]." ".$userManager->findUser($author)["nachname"];
|
||||
}else{
|
||||
//header("location: index.php?pfad=404");
|
||||
include_once "content/404.php";
|
||||
@@ -45,8 +49,9 @@ if (isset($_GET["id"]) && !empty($_GET["id"])){
|
||||
}
|
||||
}
|
||||
|
||||
} catch (Exception $e){
|
||||
} catch (Throwable $e) {
|
||||
$_SESSION["message"] = "internal_error";
|
||||
header("Location: index.php");
|
||||
exit();
|
||||
}
|
||||
}else{
|
||||
|
||||
@@ -1,13 +1,59 @@
|
||||
<?php
|
||||
if (session_status() === PHP_SESSION_NONE) {
|
||||
session_start();
|
||||
}
|
||||
|
||||
require_once 'php/model/Article.php';
|
||||
require_once 'php/model/ArticleManager.php';
|
||||
require_once 'php/model/UserManager.php';
|
||||
require_once 'php/validator/article-validator.php';
|
||||
|
||||
if (isset($_GET["category"]) && !empty($_GET["category"]) && articleCategoryValidator($_GET["category"])){
|
||||
$category = $_GET["category"];
|
||||
|
||||
// Sortierung auslesen (Standard: alphabet)
|
||||
$sortStyle = isset($_GET['sort']) ? trim($_GET['sort']) : 'alphabet';
|
||||
|
||||
// Aktuelle Seite auslesen
|
||||
$page = isset($_GET['page']) ? (int)$_GET['page'] : 1;
|
||||
if ($page < 1) { $page = 1; }
|
||||
|
||||
try {
|
||||
$articleManager = ArticleManager::getInstance();
|
||||
$articles = $articleManager->getArticlesByCategory($category);
|
||||
$userManager = UserManager::getInstance();
|
||||
$allArticles = $articleManager->getArticlesByCategory($category);
|
||||
foreach ($allArticles as $article) {
|
||||
$article->setAuthor($userManager->findUser($article->getAuthor())["vorname"] . " " . $userManager->findUser($article->getAuthor())["nachname"]);
|
||||
}
|
||||
$totalArticles = count($allArticles);
|
||||
|
||||
// --- SORTIERUNG LOGIK ---
|
||||
if ($sortStyle === 'alphabet') {
|
||||
usort($allArticles, function($a, $b) {
|
||||
return strcasecmp($a->getTitle(), $b->getTitle());
|
||||
});
|
||||
} elseif ($sortStyle === 'likes') {
|
||||
usort($allArticles, function($a, $b) {
|
||||
return $b->getLikes() <=> $a->getLikes(); // Absteigend nach Likes
|
||||
});
|
||||
} elseif ($sortStyle === 'newest') {
|
||||
usort($allArticles, function($a, $b) {
|
||||
return strtotime($b->getCreationDate()) <=> strtotime($a->getCreationDate()); // Neueste zuerst
|
||||
});
|
||||
} elseif ($sortStyle === 'oldest') {
|
||||
usort($allArticles, function($a, $b) {
|
||||
return strtotime($a->getCreationDate()) <=> strtotime($b->getCreationDate()); // Älteste zuerst
|
||||
});
|
||||
}
|
||||
|
||||
// Paginierung konfigurieren
|
||||
$limit = 10;
|
||||
$totalPages = ceil($totalArticles / $limit);
|
||||
if ($page > $totalPages && $totalPages > 0) { $page = $totalPages; }
|
||||
|
||||
$offset = ($page - 1) * $limit;
|
||||
$articles = array_slice($allArticles, $offset, $limit);
|
||||
|
||||
} catch (Exception $e) {
|
||||
$_SESSION["message"] = "internal_error";
|
||||
include_once "content/404.php";
|
||||
|
||||
@@ -0,0 +1,92 @@
|
||||
<?php
|
||||
|
||||
if (session_status() === PHP_SESSION_NONE) {
|
||||
session_start();
|
||||
}
|
||||
|
||||
require_once "php/model/CommentManager.php";
|
||||
|
||||
if ($_SERVER["REQUEST_METHOD"] !== "POST") {
|
||||
header("Location: index.php");
|
||||
exit();
|
||||
}
|
||||
|
||||
if (!isset($_SESSION["user_email"])) {
|
||||
header("Location: index.php?pfad=login");
|
||||
exit();
|
||||
}
|
||||
|
||||
$commentId = filter_input(
|
||||
INPUT_POST,
|
||||
"comment_id",
|
||||
FILTER_VALIDATE_INT
|
||||
);
|
||||
|
||||
$articleId = filter_input(
|
||||
INPUT_POST,
|
||||
"article_id",
|
||||
FILTER_VALIDATE_INT
|
||||
);
|
||||
|
||||
$content = trim($_POST["content"] ?? "");
|
||||
|
||||
/*
|
||||
* Ohne gültige Beitrags-ID kann nicht sicher
|
||||
* zum ursprünglichen Beitrag zurückgeleitet werden.
|
||||
*/
|
||||
if (!$articleId) {
|
||||
header("Location: index.php");
|
||||
exit();
|
||||
}
|
||||
|
||||
/*
|
||||
* Kommentar-ID und Inhalt werden geprüft.
|
||||
*/
|
||||
if (!$commentId || $content === "") {
|
||||
$_SESSION["comment_message"] =
|
||||
"Der Kommentar darf nicht leer sein.";
|
||||
|
||||
$_SESSION["comment_message_type"] = "error";
|
||||
|
||||
header(
|
||||
"Location: index.php?pfad=showArticle&id="
|
||||
. urlencode((string) $articleId)
|
||||
. "#comments"
|
||||
);
|
||||
exit();
|
||||
}
|
||||
|
||||
try {
|
||||
$commentManager = CommentManager::getInstance();
|
||||
|
||||
$updated = $commentManager->updateComment(
|
||||
$commentId,
|
||||
$_SESSION["user_email"],
|
||||
$content
|
||||
);
|
||||
|
||||
if ($updated) {
|
||||
$_SESSION["comment_message"] =
|
||||
"Der Kommentar wurde erfolgreich bearbeitet.";
|
||||
|
||||
$_SESSION["comment_message_type"] = "success";
|
||||
} else {
|
||||
$_SESSION["comment_message"] =
|
||||
"Der Kommentar konnte nicht bearbeitet werden.";
|
||||
|
||||
$_SESSION["comment_message_type"] = "error";
|
||||
}
|
||||
|
||||
} catch (Throwable $e) {
|
||||
$_SESSION["comment_message"] =
|
||||
"Beim Bearbeiten des Kommentars ist ein Fehler aufgetreten.";
|
||||
|
||||
$_SESSION["comment_message_type"] = "error";
|
||||
}
|
||||
|
||||
header(
|
||||
"Location: index.php?pfad=showArticle&id="
|
||||
. urlencode((string) $articleId)
|
||||
. "#comments"
|
||||
);
|
||||
exit();
|
||||
@@ -97,6 +97,16 @@ class Article
|
||||
return $this->author;
|
||||
}
|
||||
|
||||
/**
|
||||
* Setzt einen neuen Autor eines Beitrages.
|
||||
* @param string $author
|
||||
* @return void
|
||||
*/
|
||||
public function setAuthor(string $author)
|
||||
{
|
||||
$this->author = $author;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gibt das Veröffentlichungsdatum des Beitrags zurück.
|
||||
* @return string
|
||||
|
||||
@@ -13,7 +13,7 @@ class Comment
|
||||
private int $id;
|
||||
private int $articleId;
|
||||
private ?int $parentCommentId;
|
||||
private string $author;
|
||||
private ?string $author;
|
||||
private string $content;
|
||||
private string $created;
|
||||
|
||||
@@ -23,7 +23,7 @@ class Comment
|
||||
* @param int $id Eindeutige ID des Kommentars
|
||||
* @param int $articleId ID des zugehörigen Beitrags
|
||||
* @param int|null $parentCommentId ID des Eltern-Kommentars oder null
|
||||
* @param string $author Autor des Kommentars
|
||||
* @param string|null $author Autor des Kommentars oder null bei gelöschten Kommentaren
|
||||
* @param string $content Inhalt des Kommentars
|
||||
* @param string $created Erstellungsdatum des Kommentars
|
||||
*/
|
||||
@@ -31,7 +31,7 @@ class Comment
|
||||
int $id,
|
||||
int $articleId,
|
||||
?int $parentCommentId,
|
||||
string $author,
|
||||
?string $author,
|
||||
string $content,
|
||||
string $created
|
||||
) {
|
||||
@@ -86,9 +86,9 @@ class Comment
|
||||
/**
|
||||
* Gibt den Autor des Kommentars zurück.
|
||||
*
|
||||
* @return string Autor
|
||||
* @return string|null Autor oder null bei gelöschten Kommentaren
|
||||
*/
|
||||
public function getAuthor(): string
|
||||
public function getAuthor(): ?string
|
||||
{
|
||||
return $this->author;
|
||||
}
|
||||
|
||||
@@ -51,4 +51,40 @@ interface CommentManagerDAO
|
||||
* @return Comment[] Liste der Kommentare
|
||||
*/
|
||||
public function getCommentsByAuthor($author);
|
||||
/**
|
||||
* Löscht einen einzelnen Kommentar des angemeldeten Nutzers.
|
||||
*
|
||||
* Kommentare ohne Antworten werden vollständig entfernt.
|
||||
* Kommentare mit Antworten bleiben als anonymer Platzhalter erhalten.
|
||||
*
|
||||
* @param int $commentId ID des Kommentars
|
||||
* @param string $author E-Mail-Adresse des Autors
|
||||
* @return bool true, wenn der Kommentar gefunden und gelöscht wurde
|
||||
*/
|
||||
public function deleteComment(int $commentId, string $author): bool;
|
||||
|
||||
/**
|
||||
* Löscht beziehungsweise anonymisiert alle Kommentare eines Nutzers.
|
||||
*
|
||||
* Diese Methode wird bei der Löschung eines Benutzerkontos verwendet.
|
||||
*
|
||||
* @param string $author E-Mail-Adresse des Nutzers
|
||||
* @return void
|
||||
*/
|
||||
public function deleteCommentsByAuthor(string $author): void;
|
||||
/**
|
||||
* Bearbeitet einen Kommentar des angemeldeten Nutzers.
|
||||
*
|
||||
* Nur der Autor des Kommentars darf den Inhalt ändern.
|
||||
*
|
||||
* @param int $commentId ID des Kommentars
|
||||
* @param string $author E-Mail-Adresse des Autors
|
||||
* @param string $content Neuer Kommentarinhalt
|
||||
* @return bool true, wenn der Kommentar bearbeitet wurde
|
||||
*/
|
||||
public function updateComment(
|
||||
int $commentId,
|
||||
string $author,
|
||||
string $content
|
||||
): bool;
|
||||
}
|
||||
@@ -17,7 +17,7 @@ class DatabaseCommentManager implements CommentManagerDAO
|
||||
/**
|
||||
* Erstellt die Kommentartabelle, falls diese noch nicht existiert.
|
||||
*/
|
||||
public function __construct()
|
||||
private function __construct()
|
||||
{
|
||||
$this->dbPath = __DIR__ . '/../../db/eduforgeDB.db';
|
||||
DatabaseInitializer::initialize($this->dbPath);
|
||||
@@ -223,4 +223,194 @@ class DatabaseCommentManager implements CommentManagerDAO
|
||||
|
||||
return $comments;
|
||||
}
|
||||
/**
|
||||
* Bearbeitet einen eigenen Kommentar.
|
||||
*
|
||||
* Der Kommentar wird nur geändert, wenn er dem
|
||||
* angemeldeten Nutzer gehört.
|
||||
*
|
||||
* @param int $commentId ID des Kommentars
|
||||
* @param string $author E-Mail-Adresse des Autors
|
||||
* @param string $content Neuer Kommentarinhalt
|
||||
* @return bool true, wenn der Kommentar bearbeitet wurde
|
||||
*/
|
||||
public function updateComment(
|
||||
int $commentId,
|
||||
string $author,
|
||||
string $content
|
||||
): bool {
|
||||
try {
|
||||
$db = $this->getConnection();
|
||||
|
||||
$command = $db->prepare("
|
||||
UPDATE comments
|
||||
SET content = :content
|
||||
WHERE id = :commentId
|
||||
AND author = :author
|
||||
");
|
||||
|
||||
$command->execute([
|
||||
":content" => $content,
|
||||
":commentId" => $commentId,
|
||||
":author" => $author
|
||||
]);
|
||||
|
||||
return $command->rowCount() > 0;
|
||||
|
||||
} catch (PDOException $e) {
|
||||
throw new RuntimeException("internal_error");
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Löscht einen eigenen Kommentar.
|
||||
*
|
||||
* Hat der Kommentar Antworten, wird er anonymisiert.
|
||||
* Hat er keine Antworten, wird er vollständig gelöscht.
|
||||
*
|
||||
* @param int $commentId ID des Kommentars
|
||||
* @param string $author E-Mail-Adresse des Autors
|
||||
* @return bool true, wenn der Kommentar gelöscht wurde
|
||||
*/
|
||||
public function deleteComment(int $commentId, string $author): bool
|
||||
{
|
||||
try {
|
||||
$db = $this->getConnection();
|
||||
|
||||
/*
|
||||
* Zuerst wird geprüft, ob der Kommentar existiert
|
||||
* und wirklich dem angemeldeten Nutzer gehört.
|
||||
*/
|
||||
$checkCommand = $db->prepare("
|
||||
SELECT id
|
||||
FROM comments
|
||||
WHERE id = :commentId
|
||||
AND author = :author
|
||||
");
|
||||
|
||||
$checkCommand->execute([
|
||||
":commentId" => $commentId,
|
||||
":author" => $author
|
||||
]);
|
||||
|
||||
if ($checkCommand->fetch() === false) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
* Danach wird geprüft, ob Antworten auf den Kommentar existieren.
|
||||
*/
|
||||
$replyCommand = $db->prepare("
|
||||
SELECT COUNT(*)
|
||||
FROM comments
|
||||
WHERE parent_comment_id = :commentId
|
||||
");
|
||||
|
||||
$replyCommand->execute([
|
||||
":commentId" => $commentId
|
||||
]);
|
||||
|
||||
$hasReplies = (int) $replyCommand->fetchColumn() > 0;
|
||||
|
||||
if ($hasReplies) {
|
||||
/*
|
||||
* Der Kommentar wird für den Kommentarbaum benötigt.
|
||||
* Deshalb bleibt er als anonymer Platzhalter erhalten.
|
||||
*/
|
||||
$deleteCommand = $db->prepare("
|
||||
UPDATE comments
|
||||
SET author = NULL,
|
||||
content = 'Dieser Kommentar wurde gelöscht.'
|
||||
WHERE id = :commentId
|
||||
AND author = :author
|
||||
");
|
||||
} else {
|
||||
/*
|
||||
* Ohne Antworten kann der Kommentar vollständig
|
||||
* aus der Datenbank entfernt werden.
|
||||
*/
|
||||
$deleteCommand = $db->prepare("
|
||||
DELETE FROM comments
|
||||
WHERE id = :commentId
|
||||
AND author = :author
|
||||
");
|
||||
}
|
||||
|
||||
$deleteCommand->execute([
|
||||
":commentId" => $commentId,
|
||||
":author" => $author
|
||||
]);
|
||||
|
||||
return $deleteCommand->rowCount() > 0;
|
||||
|
||||
} catch (PDOException $e) {
|
||||
throw new RuntimeException("internal_error");
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Löscht beziehungsweise anonymisiert alle Kommentare eines Nutzers.
|
||||
*
|
||||
* Kommentare ohne Antworten werden vollständig gelöscht.
|
||||
* Kommentare mit Antworten bleiben als anonyme Platzhalter erhalten.
|
||||
*
|
||||
* @param string $author E-Mail-Adresse des Nutzers
|
||||
* @return void
|
||||
*/
|
||||
public function deleteCommentsByAuthor(string $author): void
|
||||
{
|
||||
$db = $this->getConnection();
|
||||
|
||||
try {
|
||||
$db->beginTransaction();
|
||||
|
||||
/*
|
||||
* Zuerst werden alle Kommentare ohne Antworten gelöscht.
|
||||
*
|
||||
* Die Schleife ist wichtig, weil durch das Löschen einer Antwort
|
||||
* eventuell auch der darüberliegende Kommentar keine Antworten
|
||||
* mehr besitzt und anschließend ebenfalls gelöscht werden kann.
|
||||
*/
|
||||
do {
|
||||
$deleteCommand = $db->prepare("
|
||||
DELETE FROM comments
|
||||
WHERE author = :author
|
||||
AND NOT EXISTS (
|
||||
SELECT 1
|
||||
FROM comments AS replies
|
||||
WHERE replies.parent_comment_id = comments.id
|
||||
)
|
||||
");
|
||||
|
||||
$deleteCommand->execute([
|
||||
":author" => $author
|
||||
]);
|
||||
|
||||
$deletedRows = $deleteCommand->rowCount();
|
||||
|
||||
} while ($deletedRows > 0);
|
||||
|
||||
/*
|
||||
* Kommentare, auf die noch Antworten anderer Nutzer folgen,
|
||||
* müssen für den Kommentarbaum erhalten bleiben.
|
||||
*/
|
||||
$placeholderCommand = $db->prepare("
|
||||
UPDATE comments
|
||||
SET author = NULL,
|
||||
content = 'Dieser Kommentar wurde gelöscht.'
|
||||
WHERE author = :author
|
||||
");
|
||||
|
||||
$placeholderCommand->execute([
|
||||
":author" => $author
|
||||
]);
|
||||
|
||||
$db->commit();
|
||||
|
||||
} catch (PDOException $e) {
|
||||
if ($db->inTransaction()) {
|
||||
$db->rollBack();
|
||||
}
|
||||
|
||||
throw new RuntimeException("internal_error");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -49,11 +49,11 @@ class DatabaseInitializer {
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
article_id INTEGER NOT NULL,
|
||||
parent_comment_id INTEGER NULL,
|
||||
author TEXT NOT NULL,
|
||||
author TEXT NULL,
|
||||
content TEXT NOT NULL,
|
||||
created TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
FOREIGN KEY (article_id) REFERENCES articles(id) ON DELETE CASCADE,
|
||||
FOREIGN KEY (author) REFERENCES users(email) ON DELETE CASCADE,
|
||||
FOREIGN KEY (author) REFERENCES users(email) ON DELETE SET NULL,
|
||||
FOREIGN KEY (parent_comment_id) REFERENCES comments(id) ON DELETE CASCADE
|
||||
);
|
||||
");
|
||||
|
||||
Reference in New Issue
Block a user