Compare commits

...

49 Commits

Author SHA1 Message Date
niklas.ortmann 01fc8537cf Merge pull request 'Bewertungssystem' (#36) from Bewertungssystem into dev
Reviewed-on: #36
2026-06-17 21:29:24 +02:00
niklas.ortmann 4a6a3cf708 search-results -> Likes-Sortierung 2026-06-17 13:23:39 +02:00
niklas.ortmann cb44c3f5a8 Update search-results-controller.php 2026-06-17 13:11:03 +02:00
niklas.ortmann eb0a34e959 Create like-controller.php 2026-06-17 13:00:12 +02:00
niklas.ortmann 8d683cc1a0 showArticle -> like-Button 2026-06-17 12:53:33 +02:00
niklas.ortmann ee41dd4cfe Update LocalArticleManager.php 2026-06-17 12:35:17 +02:00
niklas.ortmann 3d50e6e3b3 ArticleManager um LIKES erweitert...
Einführung einer dynamischen Like-Funktion für Beiträge
2026-06-17 12:26:18 +02:00
niklas.ortmann 4f8d11881d Merge pull request 'home zeigt nun dynamisch die Kategorien an' (#35) from Startseite into dev
Reviewed-on: #35
2026-06-17 12:08:10 +02:00
niklas.ortmann 5c924d3277 Update profile.php 2026-06-17 12:06:49 +02:00
niklas.ortmann 7f703a8386 Update profile.php 2026-06-17 12:03:25 +02:00
niklas.ortmann f063ea4741 Update profile.php 2026-06-17 12:02:41 +02:00
niklas.ortmann ca643dd298 Update profile.php 2026-06-17 12:02:20 +02:00
niklas.ortmann e19225e49e Update profile.php 2026-06-17 12:01:38 +02:00
niklas.ortmann 9ffbca679e Update profile.php 2026-06-17 12:00:59 +02:00
niklas.ortmann d90a10e462 Update profile.php 2026-06-17 12:00:36 +02:00
niklas.ortmann b6f25d041b Anmerkungen von Maximilian zu Blatt 04
Ist der PHP-Code gut verständlich und übersichtlich strukturiert? Beinahe vollständig. ArticleManager::getInstance() erzeugt Dummy-Daten direkt beim Abruf der Instanz; etwas unübersichtlich, aber im Kontext der Dummy-Daten nicht zu gewichten (heißt es wird nicht negativ angerechnet).

Wie ist die Code-Qualität? Teilweise. articleAuthorValidator() ist mit TODO versehen und gibt immer true zurück. In home.php wird $dummy3->getId() aufgerufen, bevor geprüft wird, ob $dummy3 überhaupt existiert.

Wird redundanter Code vermieden? Teilweise. Die Kategorie-Listen stehen mehrfach separat in createArticle.php, updateArticle.php, navbar.php und article-validator.php. Fehlerausgaben für Artikel-Erstellung und Artikel-Bearbeitung sind ebenfalls fast identisch doppelt vorhanden.

Findet eine adäquate Trennung von PHP-Code und HTML-Code statt? Beinahe vollständig. In den Views werden teilweise noch Controller direkt eingebunden, z. B. profile.php bindet profile-controller.php ein und updateArticle.php nutzt showArticle-controller.php, um Daten zu laden (kann man theoretisch so machen, an dieser Stelle ist die Trennung dann aber nicht mehr wirklich sauber auf einem fundamentalen Level).

Ist eine Trennung in Model, View und Controller ersichtlich und auch korrekt durchgeführt? Teilweise. content-Dateien übernehmen teilweise Routing-/Controller-Aufgaben. Views binden Controller selbst ein.

Wurde das DAO-Pattern korrekt umgesetzt? Ok hier und da mit extends ein wenig fragwürdig aber kein Fehler.

Werden Sessions korrekt eingesetzt? Ok. session_start() wird mehrfach aufgerufen, z. B. in index.php, navbar.php, home-controller.php und search-results.php. Nach Login oder Registrierung wird keine neue Session-ID erzeugt (dies ist kein muss).

Findet in jedem Skript falls notwendig zunächst eine Authentifizierung angemeldeter Nutzer statt? Teilweise. createArticle-controller.php prüft bei direktem Aufruf nicht, ob ein Nutzer angemeldet ist. updateArticle-controller.php prüft ebenfalls nicht zu Beginn sauber die Authentifizierung. Die Authentifizierung liegt teilweise nur in der Formularseite, nicht im verarbeitenden Controller.

Ist bei „Eintrag bearbeiten/löschen“ sichergestellt, dass nur der Eigentümer den Eintrag verändern kann? Beinahe vollständig. Die eigentliche Aktualisierung prüft den Autor. Das Bearbeitungsformular updateArticle.php kann aber von jedem eingeloggten Nutzer mit fremder Artikel-ID geöffnet werden.

Werden in jedem Skript alle GET- bzw. POST-Parameter überprüft und ggf. auf Validität überprüft? Nein. pfad in index.php wird nicht per Whitelist validiert. id in showArticle-controller.php und updateArticle-controller.php wird nicht als gültige numerische ID geprüft. sort in search-results-controller.php wird nicht gegen erlaubte Werte validiert.

Werden bei Eingabefehlern durch den Nutzer die von ihm eingegebenen Daten bei der erneuten Formularanzeige übernommen? Teilweise. In createArticle.php wird old_category zwar gespeichert, aber nicht wieder im Dropdown ausgewählt (in updateArticle.php als TODO vermerkt). Beim Login wird die eingegebene E-Mail-Adresse nach einem Fehler nicht erneut angezeigt.

Wird eine adäquate Überprüfung von potentiellen Fehlern und Fehlerbehandlung durchgeführt? Teilweise. LocalArticleManager::saveArticle() prüft nicht, ob file_put_contents() erfolgreich war. getAllArticles() ignoriert fehlerhaftes JSON und gibt dann einfach ein leeres Array zurück. home-controller.php gibt im Fehlerfall direkt die Exception-Message aus.

Erfolgen bei Aktionen des Nutzers sowohl positive als auch negative Meldungen auf der nachfolgend angezeigten Seite? Teilweise. Nach erfolgreichem Login oder Registrierung gibt es keine sichtbare Erfolgsmeldung. Nach erfolgreicher Profiländerung gibt es ebenfalls keine positive Meldung. Beim Löschen des Accounts erfolgt nur eine Weiterleitung ohne Feedback.

Werden alle Eingabedaten beim Einbau in die HTML-Seite mit htmlspecialchars bzw. htmlentities HTML-kodiert? Beinahe vollständig. In home.php werden die Titel der Dummy-/Artikel-Links ohne htmlspecialchars() ausgegeben.

Werden alle Eingabedaten beim Einbau in eine URL mit urlencode URL-kodiert? Ok. Artikel-IDs werden mehrfach direkt in URLs eingesetzt, z. B. in profile.php, search-results.php und showCategory.php (Anmerkung: Rechne ich hier nicht negativ an). Auch die Formular-Action in updateArticle.php baut die ID ohne urlencode() in die URL ein (rechne ich ebenfalls nicht negativ an).
2026-06-17 11:58:49 +02:00
niklas.ortmann 4321c4bee5 home zeigt nun dynamisch die Kategorien an 2026-06-17 11:31:23 +02:00
niklas.ortmann d1b3641754 Update dataSources.local.xml 2026-06-17 11:11:19 +02:00
niklas.ortmann 5281c1bc7d Merge branch 'erweiterterEditor' into dev 2026-06-17 10:30:01 +02:00
niklas.ortmann af17764346 Merge pull request 'Forum implementiert' (#34) from Forum into dev
Reviewed-on: #34
Reviewed-by: niklas.ortmann <ortmann.niklas@yahoo.de>
2026-06-17 10:24:24 +02:00
caroline.slt a8d321609d Reviewänderungen 2026-06-16 22:31:49 +02:00
caroline.slt 23ccbe2d02 Reviewänderungen 2026-06-16 22:09:17 +02:00
caroline.slt 47239016c5 Fehlerbehebung 2026-06-16 00:02:06 +02:00
caroline.slt f4f6960b9d Kommentare im Profil anzeigen 2026-06-15 23:55:10 +02:00
caroline.slt c4c299eed6 Fehlerbehebung 2026-06-15 23:41:28 +02:00
caroline.slt e20b59b43d Kommentarfunktion für Nutzer einschränken 2026-06-15 23:22:00 +02:00
caroline.slt 901232b683 Antwortmöglichkeit 2026-06-15 22:59:44 +02:00
caroline.slt 40c5fa69f5 Antwortmöglichkeit 2026-06-15 22:58:22 +02:00
caroline.slt 7694396454 Antwortmöglichkeit 2026-06-15 22:56:00 +02:00
caroline.slt dafc149bba Antwortmöglichkeit 2026-06-15 22:50:55 +02:00
caroline.slt f23ec27247 Antwortmöglichkeit 2026-06-15 22:47:50 +02:00
caroline.slt efb179dec2 Antwortmöglichkeit 2026-06-15 22:41:05 +02:00
caroline.slt 86222aa92e Antwortmöglichkeit 2026-06-15 22:37:50 +02:00
caroline.slt a5fb5cd360 Antwortmöglichkeit 2026-06-15 22:35:42 +02:00
caroline.slt 6f6e53a483 Antwortmöglichkeit 2026-06-15 22:32:00 +02:00
caroline.slt f13a2c6f1e css design 2026-06-15 22:17:44 +02:00
caroline.slt 59012cacfb css design 2026-06-15 22:14:51 +02:00
caroline.slt 21c5471d73 fehlerkorrektur 2026-06-15 22:08:46 +02:00
caroline.slt e1ee70b46f ajax implementiert 2026-06-15 21:54:06 +02:00
caroline.slt eb56b8d6a0 Implementierung 2026-06-15 21:45:17 +02:00
niklas.ortmann d597046f41 Merge pull request 'Paginator in der search-results implementiert mit js' (#32) from Search_Paginator into dev
Reviewed-on: #32
2026-06-14 23:42:29 +02:00
niklas.ortmann ae1c873536 Merge pull request 'Beitrag erstellen oder bearbeiten: Bei einer Fehlermeldung wird die Kategorie nicht mehr zurückgesetzt' (#31) from bugfix_KategorieNichtZuruecksetzen into dev
Reviewed-on: #31
2026-06-14 23:42:20 +02:00
niklas.ortmann 95251d23ad Update search-results-controller.php 2026-06-13 12:26:49 +02:00
niklas.ortmann c16bf574ce Update search-results.php 2026-06-13 12:21:38 +02:00
niklas.ortmann 0e44d92baa paginator.js 2026-06-13 12:19:37 +02:00
NOrtmann1 f2ef3c08a7 Update createArticle.php 2026-06-11 15:36:45 +02:00
NOrtmann1 8a66e8af3f Update updateArticle.php 2026-06-11 12:25:18 +02:00
NOrtmann1 41d1cc6013 Update updateArticle.php 2026-06-11 12:12:40 +02:00
NOrtmann1 c9c6a4ae81 Update dataSources.local.xml 2026-06-11 12:12:35 +02:00
31 changed files with 1812 additions and 138 deletions
+2
View File
@@ -19,6 +19,8 @@
- Die Suchseite und Kategorieseite packen momentan alle passenden Beiträge untereinander. Später sollen zunächst 10
Ergebnisse auf einer Seite angezeigt werden.
- Wenn ein Bild aus einem Beitrag entfernt wird, dann wird noch nicht die Datei im Pfad /uploads gelöscht.
- id in showArticle-controller.php und updateArticle-controller.php wird nicht als gültige numerische ID geprüft.
- sort in search-results-controller.php wird nicht gegen erlaubte Werte validiert.
## Besonderheiten des Projektes
- Es wurde ein einfacher Beitrags-Editor erstellt. Mit diesem können Beiträge erstellt oder bearbeitet werden.
+193 -27
View File
@@ -51,45 +51,211 @@ if (!isset($_SESSION["user"])) {
<div class="sidebar-block">
<label for="category">Kategorie <span class="required">*</span></label>
<select id="category" name="category" required>
<option value="" disabled selected>Kategorie wählen...</option>
<option disabled <?php
if ((!isset($_SESSION['old_category']) || empty($_SESSION['old_category'])) && (!isset($category) || empty($category))) {
echo 'selected';
}
?>>Kategorie wählen...</option>
<optgroup label="Sprachen">
<option value="deutsch">Deutsch</option>
<option value="englisch">Englisch</option>
<option value="franzoesisch">Französisch</option>
<option value="latein">Latein</option>
<option value="literatur">Literatur</option>
<option value="deutsch" <?php
if (isset($_SESSION['old_category']) && !empty($_SESSION['old_category'])) {
if ($_SESSION['old_category'] === 'deutsch') { echo 'selected'; }
} elseif (isset($category) && !empty($category)) {
if ($category === 'deutsch') { echo 'selected'; }
}
?>>Deutsch</option>
<option value="englisch" <?php
if (isset($_SESSION['old_category']) && !empty($_SESSION['old_category'])) {
if ($_SESSION['old_category'] === 'englisch') { echo 'selected'; }
} elseif (isset($category) && !empty($category)) {
if ($category === 'englisch') { echo 'selected'; }
}
?>>Englisch</option>
<option value="franzoesisch" <?php
if (isset($_SESSION['old_category']) && !empty($_SESSION['old_category'])) {
if ($_SESSION['old_category'] === 'franzoesisch') { echo 'selected'; }
} elseif (isset($category) && !empty($category)) {
if ($category === 'franzoesisch') { echo 'selected'; }
}
?>>Französisch</option>
<option value="latein" <?php
if (isset($_SESSION['old_category']) && !empty($_SESSION['old_category'])) {
if ($_SESSION['old_category'] === 'latein') { echo 'selected'; }
} elseif (isset($category) && !empty($category)) {
if ($category === 'latein') { echo 'selected'; }
}
?>>Latein</option>
<option value="literatur" <?php
if (isset($_SESSION['old_category']) && !empty($_SESSION['old_category'])) {
if ($_SESSION['old_category'] === 'literatur') { echo 'selected'; }
} elseif (isset($category) && !empty($category)) {
if ($category === 'literatur') { echo 'selected'; }
}
?>>Literatur</option>
</optgroup>
<optgroup label="MINT">
<option value="mathe">Mathematik</option>
<option value="biologie">Biologie</option>
<option value="chemie">Chemie</option>
<option value="physik">Physik</option>
<option value="informatik">Informatik</option>
<option value="astronomie">Astronomie</option>
<option value="mathe" <?php
if (isset($_SESSION['old_category']) && !empty($_SESSION['old_category'])) {
if ($_SESSION['old_category'] === 'mathe') { echo 'selected'; }
} elseif (isset($category) && !empty($category)) {
if ($category === 'mathe') { echo 'selected'; }
}
?>>Mathematik</option>
<option value="biologie" <?php
if (isset($_SESSION['old_category']) && !empty($_SESSION['old_category'])) {
if ($_SESSION['old_category'] === 'biologie') { echo 'selected'; }
} elseif (isset($category) && !empty($category)) {
if ($category === 'biologie') { echo 'selected'; }
}
?>>Biologie</option>
<option value="chemie" <?php
if (isset($_SESSION['old_category']) && !empty($_SESSION['old_category'])) {
if ($_SESSION['old_category'] === 'chemie') { echo 'selected'; }
} elseif (isset($category) && !empty($category)) {
if ($category === 'chemie') { echo 'selected'; }
}
?>>Chemie</option>
<option value="physik" <?php
if (isset($_SESSION['old_category']) && !empty($_SESSION['old_category'])) {
if ($_SESSION['old_category'] === 'physik') { echo 'selected'; }
} elseif (isset($category) && !empty($category)) {
if ($category === 'physik') { echo 'selected'; }
}
?>>Physik</option>
<option value="informatik" <?php
if (isset($_SESSION['old_category']) && !empty($_SESSION['old_category'])) {
if ($_SESSION['old_category'] === 'informatik') { echo 'selected'; }
} elseif (isset($category) && !empty($category)) {
if ($category === 'informatik') { echo 'selected'; }
}
?>>Informatik</option>
<option value="astronomie" <?php
if (isset($_SESSION['old_category']) && !empty($_SESSION['old_category'])) {
if ($_SESSION['old_category'] === 'astronomie') { echo 'selected'; }
} elseif (isset($category) && !empty($category)) {
if ($category === 'astronomie') { echo 'selected'; }
}
?>>Astronomie</option>
</optgroup>
<optgroup label="Gesellschaft & Werte">
<option value="geschichte">Geschichte</option>
<option value="erdkunde">Erdkunde</option>
<option value="sozialkunde">Sozialkunde</option>
<option value="wirtschaft">Wirtschaftskunde</option>
<option value="religion">Religion</option>
<option value="ethik">Ethikunterricht</option>
<option value="philosophie">Philosophie</option>
<option value="psychologie">Psychologie</option>
<option value="kunst">Kunst</option>
<option value="musik">Musik</option>
<option value="theater">Theater</option>
<option value="geschichte" <?php
if (isset($_SESSION['old_category']) && !empty($_SESSION['old_category'])) {
if ($_SESSION['old_category'] === 'geschichte') { echo 'selected'; }
} elseif (isset($category) && !empty($category)) {
if ($category === 'geschichte') { echo 'selected'; }
}
?>>Geschichte</option>
<option value="erdkunde" <?php
if (isset($_SESSION['old_category']) && !empty($_SESSION['old_category'])) {
if ($_SESSION['old_category'] === 'erdkunde') { echo 'selected'; }
} elseif (isset($category) && !empty($category)) {
if ($category === 'erdkunde') { echo 'selected'; }
}
?>>Erdkunde</option>
<option value="sozialkunde" <?php
if (isset($_SESSION['old_category']) && !empty($_SESSION['old_category'])) {
if ($_SESSION['old_category'] === 'sozialkunde') { echo 'selected'; }
} elseif (isset($category) && !empty($category)) {
if ($category === 'sozialkunde') { echo 'selected'; }
}
?>>Sozialkunde</option>
<option value="wirtschaft" <?php
if (isset($_SESSION['old_category']) && !empty($_SESSION['old_category'])) {
if ($_SESSION['old_category'] === 'wirtschaft') { echo 'selected'; }
} elseif (isset($category) && !empty($category)) {
if ($category === 'wirtschaft') { echo 'selected'; }
}
?>>Wirtschaftskunde</option>
<option value="religion" <?php
if (isset($_SESSION['old_category']) && !empty($_SESSION['old_category'])) {
if ($_SESSION['old_category'] === 'religion') { echo 'selected'; }
} elseif (isset($category) && !empty($category)) {
if ($category === 'religion') { echo 'selected'; }
}
?>>Religion</option>
<option value="ethik" <?php
if (isset($_SESSION['old_category']) && !empty($_SESSION['old_category'])) {
if ($_SESSION['old_category'] === 'ethik') { echo 'selected'; }
} elseif (isset($category) && !empty($category)) {
if ($category === 'ethik') { echo 'selected'; }
}
?>>Ethikunterricht</option>
<option value="philosophie" <?php
if (isset($_SESSION['old_category']) && !empty($_SESSION['old_category'])) {
if ($_SESSION['old_category'] === 'philosophie') { echo 'selected'; }
} elseif (isset($category) && !empty($category)) {
if ($category === 'philosophie') { echo 'selected'; }
}
?>>Philosophie</option>
<option value="psychologie" <?php
if (isset($_SESSION['old_category']) && !empty($_SESSION['old_category'])) {
if ($_SESSION['old_category'] === 'psychologie') { echo 'selected'; }
} elseif (isset($category) && !empty($category)) {
if ($category === 'psychologie') { echo 'selected'; }
}
?>>Psychologie</option>
<option value="kunst" <?php
if (isset($_SESSION['old_category']) && !empty($_SESSION['old_category'])) {
if ($_SESSION['old_category'] === 'kunst') { echo 'selected'; }
} elseif (isset($category) && !empty($category)) {
if ($category === 'kunst') { echo 'selected'; }
}
?>>Kunst</option>
<option value="musik" <?php
if (isset($_SESSION['old_category']) && !empty($_SESSION['old_category'])) {
if ($_SESSION['old_category'] === 'musik') { echo 'selected'; }
} elseif (isset($category) && !empty($category)) {
if ($category === 'musik') { echo 'selected'; }
}
?>>Musik</option>
<option value="theater" <?php
if (isset($_SESSION['old_category']) && !empty($_SESSION['old_category'])) {
if ($_SESSION['old_category'] === 'theater') { echo 'selected'; }
} elseif (isset($category) && !empty($category)) {
if ($category === 'theater') { echo 'selected'; }
}
?>>Theater</option>
</optgroup>
<optgroup label="Technik & Praxis">
<option value="technik">Technik</option>
<option value="werken">Werken</option>
<option value="hauswirtschaft">Hauswirtschaft</option>
<option value="sport">Sport</option>
<option value="technik" <?php
if (isset($_SESSION['old_category']) && !empty($_SESSION['old_category'])) {
if ($_SESSION['old_category'] === 'technik') { echo 'selected'; }
} elseif (isset($category) && !empty($category)) {
if ($category === 'technik') { echo 'selected'; }
}
?>>Technik</option>
<option value="werken" <?php
if (isset($_SESSION['old_category']) && !empty($_SESSION['old_category'])) {
if ($_SESSION['old_category'] === 'werken') { echo 'selected'; }
} elseif (isset($category) && !empty($category)) {
if ($category === 'werken') { echo 'selected'; }
}
?>>Werken</option>
<option value="hauswirtschaft" <?php
if (isset($_SESSION['old_category']) && !empty($_SESSION['old_category'])) {
if ($_SESSION['old_category'] === 'hauswirtschaft') { echo 'selected'; }
} elseif (isset($category) && !empty($category)) {
if ($category === 'hauswirtschaft') { echo 'selected'; }
}
?>>Hauswirtschaft</option>
<option value="sport" <?php
if (isset($_SESSION['old_category']) && !empty($_SESSION['old_category'])) {
if ($_SESSION['old_category'] === 'sport') { echo 'selected'; }
} elseif (isset($category) && !empty($category)) {
if ($category === 'sport') { echo 'selected'; }
}
?>>Sport</option>
</optgroup>
<?php
if (isset($_SESSION['old_category'])) {
unset($_SESSION['old_category']);
}
?>
</select>
</div>
+27 -21
View File
@@ -19,28 +19,34 @@ include_once 'php/controller/home-controller.php';
sea takimata sanctus est Lorem ipsum dolor sit amet.
</p>
<!-- Flexbox -->
<div class="flexbox">
<div class="container">
<a href="index.php?pfad=showCategory&category=informatik" class="category-link">Informatik</a>
<div class="article-link">
<a href="index.php?pfad=showArticle&id=<?php echo $dummy3->getId()?>"><?php if(isset($dummy3)){echo $dummy3->getTitle();}else{echo "Fehler: Beitrag nicht gefunden!";} ?></a>
</div>
</div>
<!-- Flexbox für aktive Kategorien -->
<div class="flexbox">
<?php
if (!empty($categoriesWithArticles) && is_array($categoriesWithArticles)):
foreach ($categoriesWithArticles as $category):
?>
<div class="container">
<a href="index.php?pfad=showCategory&category=<?php echo htmlspecialchars($category['slug']); ?>" class="category-link">
<?php echo htmlspecialchars($category['name']); ?>
</a>
<div class="container">
<a href="index.php?pfad=showCategory&category=mathe" class="category-link">Mathe</a>
<div class="article-link">
<a href="index.php?pfad=showArticle&id=<?php echo $dummy1->getId()?>"><?php if(isset($dummy1)){echo $dummy1->getTitle();}else{echo "Fehler: Beitrag nicht gefunden!";} ?></a>
</div>
</div>
<div class="container">
<a href="index.php?pfad=showCategory&category=physik" class="category-link">Physik</a>
<div class="article-link">
<a href="index.php?pfad=showArticle&id=<?php echo $dummy2->getId()?>"><?php if(isset($dummy2)){echo $dummy2->getTitle();}else{echo "Fehler: Beitrag nicht gefunden!";} ?></a>
</div>
</div>
<!-- die 5 neuesten Beiträge der Kategorie -->
<div class="article-links-wrapper">
<?php foreach ($category['articles'] as $article): ?>
<div class="article-link">
<a href="index.php?pfad=showArticle&id=<?php echo htmlspecialchars($article->getId()); ?>">
<?php echo htmlspecialchars($article->getTitle()); ?>
</a>
</div>
<?php endforeach; ?>
</div>
</div>
<?php
endforeach;
else:
?>
<p>Aktuell sind keine Beiträge in den Kategorien vorhanden.</p>
<?php endif; ?>
</div>
</main>
+50 -7
View File
@@ -7,8 +7,8 @@ $isEditMode = (isset($_GET["edit"]) && $_GET["edit"] === "1") || !empty($error);
<main class="form-page">
<div class="flexbox">
<div class="container">
<?php include_once "includes/alertMessages.php" ?>
<?php if (!empty($error)): ?>
<p class="alert-message is-error">
@@ -93,8 +93,6 @@ $isEditMode = (isset($_GET["edit"]) && $_GET["edit"] === "1") || !empty($error);
<div class="container">
<?php include_once "includes/alertMessages.php"?>
<h2 class="section-title">Meine Beiträge</h2>
<div class="articles-list">
@@ -157,7 +155,9 @@ $isEditMode = (isset($_GET["edit"]) && $_GET["edit"] === "1") || !empty($error);
</a>
<form action="php/controller/deleteArticle-controller.php" method="POST">
<input type="hidden" name="id" value="<?php echo htmlspecialchars($userArticle->getID()); ?>">
<input type="hidden"
name="id"
value="<?php echo htmlspecialchars($userArticle->getID()); ?>">
<button type="submit"
class="button"
@@ -165,7 +165,6 @@ $isEditMode = (isset($_GET["edit"]) && $_GET["edit"] === "1") || !empty($error);
Löschen
</button>
</form>
</div>
<?php endforeach; ?>
@@ -180,10 +179,54 @@ $isEditMode = (isset($_GET["edit"]) && $_GET["edit"] === "1") || !empty($error);
</button>
<?php endif; ?>
<?php unset($_SESSION["message"]); ?>
</div>
</div>
<div class="container">
<div class="comments-section">
<h2 class="section-title">Meine Kommentare</h2>
<!-- Liste aller Kommentare des Nutzers -->
<div class="comments-list">
<?php if (isset($userComments) && count($userComments) > 0): ?>
<?php foreach ($userComments as $comment): ?>
<div class="article-item">
<!-- Erstellungsdatum des Kommentars -->
<div class="article-meta">
<span class="article-date">
<?php echo htmlspecialchars($comment->getCreated()); ?>
</span>
</div>
<!-- Inhalt des Kommentars -->
<p>
<?php echo nl2br(htmlspecialchars($comment->getContent())); ?>
</p>
<!-- Link zum Beitrag, unter dem der Kommentar geschrieben wurde -->
<a href="index.php?pfad=showArticle&id=<?php echo htmlspecialchars($comment->getArticleId()); ?>"
class="edit-link-button">
Zum Beitrag
</a>
</div>
<?php endforeach; ?>
<?php else: ?>
<p>Du hast noch keine Kommentare geschrieben.</p>
<?php endif; ?>
</div>
</div>
</div>
<?php unset($_SESSION["message"]); ?>
</div>
</main>
+51 -12
View File
@@ -2,19 +2,38 @@
if (session_status() === PHP_SESSION_NONE) {
session_start();
}
$results = $_SESSION["search_results"] ?? [];
$all_results = $_SESSION["search_results"] ?? [];
$query = $_SESSION["search_query"] ?? "";
$totalResultsCount = count($all_results);
$limit = isset($_GET['limit']) ? (int)$_GET['limit'] : 10;
if (!in_array($limit, [10, 20, 50, 100])) {
$limit = 10;
}
$results = array_slice($results, 0, $limit);
// Gesamtseitenzahl
$totalPages = max(1, ceil($totalResultsCount / $limit));
// Aktuelle Seite auslesen und validieren
$currentPage = isset($_GET['page']) ? (int)$_GET['page'] : 1;
if ($currentPage < 1) {
$currentPage = 1;
} elseif ($currentPage > $totalPages) {
$currentPage = $totalPages;
}
// Startpunkt im Array berechnen (Offset)
$offset = ($currentPage - 1) * $limit;
// Nur die Ergebnisse für die aktuelle Seite ausschneiden
$results = array_slice($all_results, $offset, $limit);
$resultCount = count($results);
?>
<noscript>
Bitte JavaScript aktivieren!
</noscript>
<!--
Seite: Suchergebnisse
Inhalt: Zeigt die Ergebnisse einer Suche an
@@ -27,8 +46,10 @@ $resultCount = count($results);
<!-- Sortierfuntion Box und Such Box-->
<form id="search-form-id" action="php/controller/search-results-controller.php" method="GET" class="s-res-sidebar-form">
<div class="s-res-sidebar-box">
<!-- Dieses Feld hält die aktuelle Seitenzahl für den Submit bereit -->
<input type="hidden" name="page" id="s-res-page-input" value="<?php echo $currentPage; ?>">
<div class="s-res-sidebar-box">
<h3 class="s-res-sidebar-title">Suche anpassen</h3>
<input type="search" id="site-search" name="q" placeholder="Suchen..." class="nav__search" value="<?php echo htmlspecialchars($query); ?>" maxlength="50" required>
<button type="submit" class="nav__search-button">Suchen</button>
@@ -44,8 +65,8 @@ $resultCount = count($results);
</label>
<!-- Noch disabled, da likes noch nicht implementiert-->
<label class="s-res-filter-option">
<input type="radio" name="sort" value="likes" <?php echo $currentSort === 'likes' ? 'checked' : ''; ?> disabled>
<span style="color: #94a3b8;">Beliebtheit (Likes)</span>
<input type="radio" name="sort" value="likes" <?php echo $currentSort === 'likes' ? 'checked' : ''; ?> onchange="this.form.submit()">
<span>Beliebtheit (Likes)</span>
</label>
<label class="s-res-filter-option">
<input type="radio" name="sort" value="newest" <?php echo $currentSort === 'newest' ? 'checked' : ''; ?> onchange="this.form.submit()">
@@ -66,7 +87,7 @@ $resultCount = count($results);
<div class="s-res-header">
<h1 class="s-res-main-title">Suchergebnisse</h1>
<p class="s-res-meta"><?php echo $resultCount; ?> Treffer für Ihre Suchanfrage "<?php echo htmlspecialchars($query); ?>"</p>
<p class="s-res-meta"><?php echo $totalResultsCount; ?> Treffer für Ihre Suchanfrage "<?php echo htmlspecialchars($query); ?>"</p>
</div>
<!-- Ergebnisliste -->
@@ -82,7 +103,14 @@ $resultCount = count($results);
<?php echo htmlspecialchars($item['title']); ?>
</a>
</h2>
<p class="s-res-author">Von: <span class="s-res-author-name"><?php echo htmlspecialchars($item['author']); ?></span></p>
<div class="s-res-meta-row">
<p class="s-res-author">Von: <span class="s-res-author-name"><?php echo htmlspecialchars($item['author']); ?></span></p>
<span class="s-res-likes">
❤️ <?php echo isset($item['likes']) && is_array($item['likes']) ? count($item['likes']) : 0; ?>
</span>
</div>
</div>
<div class="s-res-arrow">&rarr;</div>
</div>
@@ -115,9 +143,20 @@ $resultCount = count($results);
</div>
<div class="s-res-page-navigation">
<button type="button" class="s-res-page-btn" disabled>&laquo;</button>
<button type="button" class="s-res-page-btn s-res-page-btn-active">1</button>
<button type="button" class="s-res-page-btn">&raquo;</button>
<button type="button" class="s-res-page-btn" data-page="<?php echo $currentPage - 1; ?>" <?php echo $currentPage <= 1 ? 'disabled' : ''; ?>>
«
</button>
<!-- Dynamische Seitenzahlen -->
<?php for ($i = 1; $i <= $totalPages; $i++): ?>
<button type="button"
class="s-res-page-btn <?php echo $i === $currentPage ? 's-res-page-btn-active' : ''; ?>"
data-page="<?php echo $i; ?>">
<?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>
+106 -8
View File
@@ -1,11 +1,15 @@
<?php
$comments = [];
$mainComments = [];
$repliesByParent = [];
$articleObj = null;
include_once 'php/controller/showArticle-controller.php';
?>
<!--
Seite: Anzeige für Beiträge
Funktion: Stellt einen übergebenen Beitrag dar.
-->
Seite: Anzeige für Beiträge
Funktion: Stellt einen übergebenen Beitrag dar.
-->
<!-- Hauptcontainer für die Beitragsansicht (Ausschließlich der Content-Bereich) -->
<main class="article-view-container">
<?php include_once "includes/alertMessages.php"?>
@@ -13,9 +17,29 @@ include_once 'php/controller/showArticle-controller.php';
<!-- Metadaten & Titel -->
<div class="article-view-top-section">
<?php if (isset($category) && !empty($category)): ?>
<span class="article-view-category"><?php echo htmlspecialchars($category); ?></span>
<?php endif; ?>
<div class="article-view-top-section">
<div class="category-and-likes-row">
<?php if (isset($category) && !empty($category)): ?>
<span class="article-view-category"><?php echo htmlspecialchars($category); ?></span>
<?php endif; ?>
<!-- Like-Anzeige und dynamischer Like-Button -->
<?php if (isset($articleObj) && $articleObj !== null): ?>
<div class="article-view-likes">
<span>❤️ <span class="like-count"><?php echo $articleObj->getLikeCount(); ?></span></span>
<?php if (isset($_SESSION["user_email"])): ?>
<a href="php/controller/like-controller.php?id=<?php echo $articleObj->getId(); ?>" class="like-toggle-btn">
<?php echo $articleObj->hasLiked($_SESSION["user_email"]) ? '👎 Gefällt mir nicht mehr' : '👍 Gefällt mir'; ?>
</a>
<?php else: ?>
<span class="login-hint">(Anmelden zum Liken)</span>
<?php endif; ?>
</div>
<?php endif; ?>
</div>
<h1 class="article-view-title">
<?php if (isset($title)) { echo htmlspecialchars($title); } ?>
</h1>
@@ -82,5 +106,79 @@ include_once 'php/controller/showArticle-controller.php';
</div>
<?php endif; ?>
</main>
<section class="article-comments-section">
<h2>Kommentare</h2>
<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>
</p>
<p><?php echo nl2br(htmlspecialchars($comment->getContent())); ?></p>
<?php if (isset($_SESSION["user_email"])): ?>
<button type="button"
class="reply-button"
data-comment-id="<?php echo htmlspecialchars($comment->getId()); ?>"
data-author="<?php echo htmlspecialchars($comment->getAuthor()); ?>">
Antworten
</button>
<?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>
</p>
<p><?php echo nl2br(htmlspecialchars($reply->getContent())); ?></p>
</div>
<?php endforeach; ?>
<?php endif; ?>
</div>
</div>
<?php endforeach; ?>
<?php else: ?>
<p class="no-comments-message">
Noch keine Kommentare vorhanden.
</p>
<?php endif; ?>
</div>
<?php if (isset($_SESSION["user_email"])): ?>
<form id="comment-form">
<input type="hidden"
name="article_id"
value="<?php echo htmlspecialchars($_GET["id"] ?? ""); ?>">
<input type="hidden"
name="parent_comment_id"
id="parent-comment-id"
value="">
<p id="reply-info" class="reply-info" style="display: none;"></p>
<textarea name="content"
id="comment-content"
placeholder="Schreibe einen Kommentar..."
required></textarea>
<button type="submit" class="button">
Kommentar senden
</button>
</form>
<?php else: ?>
<div class="comment-login-hint">
<p>Melde dich an, um einen Kommentar zu schreiben.</p>
<a href="index.php?pfad=login" class="button">Jetzt anmelden</a>
</div>
<?php endif; ?>
</section>
</main>
+193 -27
View File
@@ -65,45 +65,211 @@ include_once 'php/controller/showArticle-controller.php';
<div class="sidebar-block">
<label for="category">Kategorie <span class="required">*</span></label>
<select id="category" name="category" required>
<option disabled selected>Kategorie wählen...</option>
<option disabled <?php
if ((!isset($_SESSION['old_category']) || empty($_SESSION['old_category'])) && (!isset($category) || empty($category))) {
echo 'selected';
}
?>>Kategorie wählen...</option>
<optgroup label="Sprachen">
<option value="deutsch">Deutsch</option>
<option value="englisch">Englisch</option>
<option value="franzoesisch">Französisch</option>
<option value="latein">Latein</option>
<option value="literatur">Literatur</option>
<option value="deutsch" <?php
if (isset($_SESSION['old_category']) && !empty($_SESSION['old_category'])) {
if ($_SESSION['old_category'] === 'deutsch') { echo 'selected'; }
} elseif (isset($category) && !empty($category)) {
if ($category === 'deutsch') { echo 'selected'; }
}
?>>Deutsch</option>
<option value="englisch" <?php
if (isset($_SESSION['old_category']) && !empty($_SESSION['old_category'])) {
if ($_SESSION['old_category'] === 'englisch') { echo 'selected'; }
} elseif (isset($category) && !empty($category)) {
if ($category === 'englisch') { echo 'selected'; }
}
?>>Englisch</option>
<option value="franzoesisch" <?php
if (isset($_SESSION['old_category']) && !empty($_SESSION['old_category'])) {
if ($_SESSION['old_category'] === 'franzoesisch') { echo 'selected'; }
} elseif (isset($category) && !empty($category)) {
if ($category === 'franzoesisch') { echo 'selected'; }
}
?>>Französisch</option>
<option value="latein" <?php
if (isset($_SESSION['old_category']) && !empty($_SESSION['old_category'])) {
if ($_SESSION['old_category'] === 'latein') { echo 'selected'; }
} elseif (isset($category) && !empty($category)) {
if ($category === 'latein') { echo 'selected'; }
}
?>>Latein</option>
<option value="literatur" <?php
if (isset($_SESSION['old_category']) && !empty($_SESSION['old_category'])) {
if ($_SESSION['old_category'] === 'literatur') { echo 'selected'; }
} elseif (isset($category) && !empty($category)) {
if ($category === 'literatur') { echo 'selected'; }
}
?>>Literatur</option>
</optgroup>
<optgroup label="MINT">
<option value="mathe">Mathematik</option>
<option value="biologie">Biologie</option>
<option value="chemie">Chemie</option>
<option value="physik">Physik</option>
<option value="informatik">Informatik</option>
<option value="astronomie">Astronomie</option>
<option value="mathe" <?php
if (isset($_SESSION['old_category']) && !empty($_SESSION['old_category'])) {
if ($_SESSION['old_category'] === 'mathe') { echo 'selected'; }
} elseif (isset($category) && !empty($category)) {
if ($category === 'mathe') { echo 'selected'; }
}
?>>Mathematik</option>
<option value="biologie" <?php
if (isset($_SESSION['old_category']) && !empty($_SESSION['old_category'])) {
if ($_SESSION['old_category'] === 'biologie') { echo 'selected'; }
} elseif (isset($category) && !empty($category)) {
if ($category === 'biologie') { echo 'selected'; }
}
?>>Biologie</option>
<option value="chemie" <?php
if (isset($_SESSION['old_category']) && !empty($_SESSION['old_category'])) {
if ($_SESSION['old_category'] === 'chemie') { echo 'selected'; }
} elseif (isset($category) && !empty($category)) {
if ($category === 'chemie') { echo 'selected'; }
}
?>>Chemie</option>
<option value="physik" <?php
if (isset($_SESSION['old_category']) && !empty($_SESSION['old_category'])) {
if ($_SESSION['old_category'] === 'physik') { echo 'selected'; }
} elseif (isset($category) && !empty($category)) {
if ($category === 'physik') { echo 'selected'; }
}
?>>Physik</option>
<option value="informatik" <?php
if (isset($_SESSION['old_category']) && !empty($_SESSION['old_category'])) {
if ($_SESSION['old_category'] === 'informatik') { echo 'selected'; }
} elseif (isset($category) && !empty($category)) {
if ($category === 'informatik') { echo 'selected'; }
}
?>>Informatik</option>
<option value="astronomie" <?php
if (isset($_SESSION['old_category']) && !empty($_SESSION['old_category'])) {
if ($_SESSION['old_category'] === 'astronomie') { echo 'selected'; }
} elseif (isset($category) && !empty($category)) {
if ($category === 'astronomie') { echo 'selected'; }
}
?>>Astronomie</option>
</optgroup>
<optgroup label="Gesellschaft & Werte">
<option value="geschichte">Geschichte</option>
<option value="erdkunde">Erdkunde</option>
<option value="sozialkunde">Sozialkunde</option>
<option value="wirtschaft">Wirtschaftskunde</option>
<option value="religion">Religion</option>
<option value="ethik">Ethikunterricht</option>
<option value="philosophie">Philosophie</option>
<option value="psychologie">Psychologie</option>
<option value="kunst">Kunst</option>
<option value="musik">Musik</option>
<option value="theater">Theater</option>
<option value="geschichte" <?php
if (isset($_SESSION['old_category']) && !empty($_SESSION['old_category'])) {
if ($_SESSION['old_category'] === 'geschichte') { echo 'selected'; }
} elseif (isset($category) && !empty($category)) {
if ($category === 'geschichte') { echo 'selected'; }
}
?>>Geschichte</option>
<option value="erdkunde" <?php
if (isset($_SESSION['old_category']) && !empty($_SESSION['old_category'])) {
if ($_SESSION['old_category'] === 'erdkunde') { echo 'selected'; }
} elseif (isset($category) && !empty($category)) {
if ($category === 'erdkunde') { echo 'selected'; }
}
?>>Erdkunde</option>
<option value="sozialkunde" <?php
if (isset($_SESSION['old_category']) && !empty($_SESSION['old_category'])) {
if ($_SESSION['old_category'] === 'sozialkunde') { echo 'selected'; }
} elseif (isset($category) && !empty($category)) {
if ($category === 'sozialkunde') { echo 'selected'; }
}
?>>Sozialkunde</option>
<option value="wirtschaft" <?php
if (isset($_SESSION['old_category']) && !empty($_SESSION['old_category'])) {
if ($_SESSION['old_category'] === 'wirtschaft') { echo 'selected'; }
} elseif (isset($category) && !empty($category)) {
if ($category === 'wirtschaft') { echo 'selected'; }
}
?>>Wirtschaftskunde</option>
<option value="religion" <?php
if (isset($_SESSION['old_category']) && !empty($_SESSION['old_category'])) {
if ($_SESSION['old_category'] === 'religion') { echo 'selected'; }
} elseif (isset($category) && !empty($category)) {
if ($category === 'religion') { echo 'selected'; }
}
?>>Religion</option>
<option value="ethik" <?php
if (isset($_SESSION['old_category']) && !empty($_SESSION['old_category'])) {
if ($_SESSION['old_category'] === 'ethik') { echo 'selected'; }
} elseif (isset($category) && !empty($category)) {
if ($category === 'ethik') { echo 'selected'; }
}
?>>Ethikunterricht</option>
<option value="philosophie" <?php
if (isset($_SESSION['old_category']) && !empty($_SESSION['old_category'])) {
if ($_SESSION['old_category'] === 'philosophie') { echo 'selected'; }
} elseif (isset($category) && !empty($category)) {
if ($category === 'philosophie') { echo 'selected'; }
}
?>>Philosophie</option>
<option value="psychologie" <?php
if (isset($_SESSION['old_category']) && !empty($_SESSION['old_category'])) {
if ($_SESSION['old_category'] === 'psychologie') { echo 'selected'; }
} elseif (isset($category) && !empty($category)) {
if ($category === 'psychologie') { echo 'selected'; }
}
?>>Psychologie</option>
<option value="kunst" <?php
if (isset($_SESSION['old_category']) && !empty($_SESSION['old_category'])) {
if ($_SESSION['old_category'] === 'kunst') { echo 'selected'; }
} elseif (isset($category) && !empty($category)) {
if ($category === 'kunst') { echo 'selected'; }
}
?>>Kunst</option>
<option value="musik" <?php
if (isset($_SESSION['old_category']) && !empty($_SESSION['old_category'])) {
if ($_SESSION['old_category'] === 'musik') { echo 'selected'; }
} elseif (isset($category) && !empty($category)) {
if ($category === 'musik') { echo 'selected'; }
}
?>>Musik</option>
<option value="theater" <?php
if (isset($_SESSION['old_category']) && !empty($_SESSION['old_category'])) {
if ($_SESSION['old_category'] === 'theater') { echo 'selected'; }
} elseif (isset($category) && !empty($category)) {
if ($category === 'theater') { echo 'selected'; }
}
?>>Theater</option>
</optgroup>
<optgroup label="Technik & Praxis">
<option value="technik">Technik</option>
<option value="werken">Werken</option>
<option value="hauswirtschaft">Hauswirtschaft</option>
<option value="sport">Sport</option>
<option value="technik" <?php
if (isset($_SESSION['old_category']) && !empty($_SESSION['old_category'])) {
if ($_SESSION['old_category'] === 'technik') { echo 'selected'; }
} elseif (isset($category) && !empty($category)) {
if ($category === 'technik') { echo 'selected'; }
}
?>>Technik</option>
<option value="werken" <?php
if (isset($_SESSION['old_category']) && !empty($_SESSION['old_category'])) {
if ($_SESSION['old_category'] === 'werken') { echo 'selected'; }
} elseif (isset($category) && !empty($category)) {
if ($category === 'werken') { echo 'selected'; }
}
?>>Werken</option>
<option value="hauswirtschaft" <?php
if (isset($_SESSION['old_category']) && !empty($_SESSION['old_category'])) {
if ($_SESSION['old_category'] === 'hauswirtschaft') { echo 'selected'; }
} elseif (isset($category) && !empty($category)) {
if ($category === 'hauswirtschaft') { echo 'selected'; }
}
?>>Hauswirtschaft</option>
<option value="sport" <?php
if (isset($_SESSION['old_category']) && !empty($_SESSION['old_category'])) {
if ($_SESSION['old_category'] === 'sport') { echo 'selected'; }
} elseif (isset($category) && !empty($category)) {
if ($category === 'sport') { echo 'selected'; }
}
?>>Sport</option>
</optgroup>
<?php
if (isset($_SESSION['old_category'])) {
unset($_SESSION['old_category']);
}
?>
</select>
</div>
+11
View File
@@ -247,6 +247,17 @@ CSS für die Suchergebnis-Seite
cursor: not-allowed;
}
.s-res-meta-row {
display: flex;
gap: 15px;
align-items: center;
}
.s-res-likes {
font-size: 0.9em;
color: #475569;
}
/* Responsive Anpassungen unter 760px (für z.B. Smartphones) */
@media (max-width: 768px) {
.s-res-layout-grid {
+137
View File
@@ -147,3 +147,140 @@
font-size: 1.85rem;
}
}
/* --- KOMMENTARE --- */
.article-comments-section {
margin-top: 3rem;
padding-top: 2rem;
border-top: 1px solid #e2e8f0;
}
.article-comments-section h2 {
font-size: 2rem;
margin-bottom: 1.5rem;
}
#comments-list {
margin-bottom: 2rem;
}
.comment-item {
background-color: #f8fafc;
border: 1px solid #e2e8f0;
border-radius: 10px;
padding: 1rem 1.25rem;
margin-bottom: 1rem;
}
#comment-form {
width: 100%;
display: flex;
flex-direction: column;
gap: 1rem;
}
#comment-content {
width: 100%;
min-height: 130px;
padding: 1rem;
border: 1px solid #cbd5e1;
border-radius: 8px;
font-size: 1rem;
font-family: inherit;
resize: vertical;
}
#comment-form .button {
width: 100%;
}
.reply-button {
display: inline-block;
margin-top: 0.75rem;
background: none;
border: none;
color: #2563eb;
font-weight: 700;
cursor: pointer;
padding: 0;
font-size: 0.95rem;
}
.reply-button:hover {
text-decoration: underline;
}
.comment-replies {
margin-top: 1rem;
margin-left: 2rem;
padding-left: 1rem;
border-left: 3px solid #cbd5e1;
}
.comment-reply {
background-color: #eef6ff;
margin-top: 1rem;
}
.reply-info {
margin: 0.5rem 0;
color: #475569;
font-weight: 600;
}
.comment-login-hint {
margin-top: 2rem;
padding: 1.5rem;
background-color: #f8fafc;
border: 1px solid #e2e8f0;
border-radius: 10px;
text-align: center;
}
.comment-login-hint p {
margin-bottom: 1rem;
}
/*
Like-Button etc.
*/
.category-and-likes-row {
display: flex;
align-items: center;
justify-content: space-between;
margin-bottom: 10px;
}
.article-view-likes {
display: flex;
align-items: center;
gap: 10px;
font-size: 0.95em;
}
.article-view-likes .like-count {
font-weight: bold;
}
.article-view-likes .login-hint {
font-size: 0.8em;
color: #777;
}
/* Interaktiver Like/Unlike-Button */
.like-toggle-btn {
text-decoration: none;
padding: 4px 10px;
border: 1px solid #bbb;
border-radius: 4px;
background-color: #f5f5f5;
color: #333;
font-weight: 500;
transition: background-color 0.2s ease, border-color 0.2s ease;
}
.like-toggle-btn:hover {
background-color: #eaeaea;
border-color: #999;
}
+10
View File
@@ -63,6 +63,16 @@
Dein Beitrag wurde erfolgreich veröffentlicht!
</p>
<?php endif; ?>
<?php if (isset($_SESSION["message"]) && $_SESSION["message"] == "article_updated"): ?>
<p class="alert-message is-success">
Der Beitrag wurde erfolgreich bearbeitet und gespeichert.
</p>
<?php endif; ?>
<?php if (isset($_SESSION["message"]) && $_SESSION["message"] == "profile_updated"): ?>
<p class="alert-message is-success">
Das Profil wurde erfolgreich bearbeitet.
</p>
<?php endif; ?>
<?php if (isset($_SESSION["message"]) && $_SESSION["message"] == "image_upload_error"): ?>
<p class="alert-message is-error">
Das Bild konnte nicht hochgeladen werden. Bitte versuche es erneut oder verwende ein anderes Bildformat.
+3
View File
@@ -52,7 +52,10 @@ if ($pfad === "deleteAccount") {
<link rel="stylesheet" href="css/showArticle.css">
<link rel="stylesheet" href="css/message.css">
<script src="js/paginator.js" async></script>
<script src="js/comments.js" defer></script>
<script src="js/editor.js" async></script>
<title>EduForge</title>
</head>
+137
View File
@@ -0,0 +1,137 @@
/**
* Initialisiert die Kommentarfunktion.
*
* Kommentare werden per AJAX gespeichert,
* ohne dass die Seite neu geladen werden muss.
*/
document.addEventListener("DOMContentLoaded", function () {
const form = document.getElementById("comment-form");
const commentsList = document.getElementById("comments-list");
const commentContent = document.getElementById("comment-content");
const parentCommentInput = document.getElementById("parent-comment-id");
const replyInfo = document.getElementById("reply-info");
if (!form || !commentsList || !commentContent || !parentCommentInput || !replyInfo) {
return;
}
/**
* Aktiviert einen einzelnen Antworten-Button.
*
* @param {HTMLButtonElement} button Antworten-Button
*/
function registerReplyButton(button) {
button.addEventListener("click", function () {
parentCommentInput.value = button.dataset.commentId;
replyInfo.textContent = "Antwort auf " + button.dataset.author;
replyInfo.style.display = "block";
commentContent.focus();
});
}
/**
* Registriert alle bereits vorhandenen Antwort-Buttons.
*/
document.querySelectorAll(".reply-button").forEach(function (button) {
registerReplyButton(button);
});
/**
* Sendet Kommentare per AJAX an den Server.
*/
form.addEventListener("submit", function (event) {
event.preventDefault();
const formData = new FormData(form);
const parentCommentId = parentCommentInput.value;
fetch("php/ajax/add-comment.php", {
method: "POST",
body: formData
})
.then(response => response.json())
.then(data => {
if (!data.success) {
alert(data.message);
return;
}
const emptyMessage = commentsList.querySelector(".no-comments-message");
if (emptyMessage) {
emptyMessage.remove();
}
const commentElement = document.createElement("div");
commentElement.classList.add("comment-item");
if (parentCommentId) {
commentElement.classList.add("comment-reply");
commentElement.innerHTML = `
<p>
<strong>${escapeHtml(data.author)}</strong>
<span>${escapeHtml(data.created)}</span>
</p>
<p>${escapeHtml(data.content).replace(/\n/g, "<br>")}</p>
`;
const parentReplies = document.querySelector(
`.comment-item[data-comment-id="${parentCommentId}"] .comment-replies`
);
if (parentReplies) {
parentReplies.appendChild(commentElement);
}
} else {
commentElement.dataset.commentId = data.commentId;
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"
class="reply-button"
data-comment-id="${escapeHtml(data.commentId)}"
data-author="${escapeHtml(data.author)}">
Antworten
</button>
<div class="comment-replies"></div>
`;
commentsList.prepend(commentElement);
const newReplyButton = commentElement.querySelector(".reply-button");
if (newReplyButton) {
registerReplyButton(newReplyButton);
}
}
commentContent.value = "";
parentCommentInput.value = "";
replyInfo.textContent = "";
replyInfo.style.display = "none";
})
.catch(() => {
alert("Kommentar konnte nicht gesendet werden.");
});
});
/**
* Entfernt HTML-Sonderzeichen aus Nutzereingaben.
*
* @param {string} text Zu bereinigender Text
* @returns {string} Sicherer Text
*/
function escapeHtml(text) {
const div = document.createElement("div");
div.textContent = text;
return div.innerHTML;
}
});
+27
View File
@@ -0,0 +1,27 @@
function initPaginator() {
const form = document.getElementById('search-form-id');
const pageInput = document.getElementById('s-res-page-input');
const pageButtons = document.querySelectorAll('.s-res-page-navigation .s-res-page-btn');
pageButtons.forEach(button => {
button.addEventListener('click', function() {
if (this.disabled) return;
const targetPage = this.getAttribute('data-page');
if (targetPage && form && pageInput) {
pageInput.value = targetPage;
form.submit();
}
});
});
}
// ist das DOM bereits vollständig aufgebaut?
if (document.readyState === 'loading') {
// Falls noch geladen wird, auf das Event warten
document.addEventListener('DOMContentLoaded', initPaginator);
} else {
// Falls das HTML bereits komplett da ist, sofort ausführen
initPaginator();
}
+58
View File
@@ -0,0 +1,58 @@
<?php
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."
]);
exit();
}
$articleId = $_POST["article_id"] ?? null;
$content = trim($_POST["content"] ?? "");
$parentCommentId = $_POST["parent_comment_id"] ?? null;
if ($parentCommentId === "" || $parentCommentId === "0") {
$parentCommentId = null;
}
if (empty($articleId) || empty($content)) {
echo json_encode([
"success" => false,
"message" => "Kommentar darf nicht leer sein."
]);
exit();
}
try {
$commentManager = CommentManager::getInstance();
$commentId = $commentManager->addComment(
$articleId,
$_SESSION["user_email"],
$content,
$parentCommentId
);
echo json_encode([
"success" => true,
"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."
]);
}
@@ -6,6 +6,10 @@ require_once '../model/LocalArticleManager.php';
require_once '../model/ArticleManager.php';
require_once '../validator/article-validator.php';
if (!isset($_SESSION["user"])) {
header("Location: index.php?pfad=login");
exit();
}
if ($_SERVER["REQUEST_METHOD"] === "POST") {
$_SESSION["old_title"] = $_POST["title"] ?? '';
$_SESSION["old_content"] = $_POST["content"] ?? '';
@@ -6,6 +6,11 @@ if (session_status() === PHP_SESSION_NONE) {
require_once __DIR__ . "/../model/UserManager.php";
require_once __DIR__ . "/../model/ArticleManager.php";
if (!isset($_SESSION["user"])) {
header("Location: index.php?pfad=login");
exit();
}
/*
Deregistrierung
Funktion: Entfernt User aus der Datenbank und beendet die Session
@@ -5,6 +5,11 @@ if (session_status() === PHP_SESSION_NONE) {
require_once __DIR__ . "/../model/ArticleManager.php";
if (!isset($_SESSION["user"])) {
header("Location: index.php?pfad=login");
exit();
}
if ($_SERVER["REQUEST_METHOD"] === "POST") {
if (isset($_SESSION["user_email"])) {
+35 -5
View File
@@ -6,14 +6,44 @@ require_once 'php/model/Article.php';
require_once 'php/model/ArticleManager.php';
require_once 'php/model/LocalArticleManager.php';
$categoriesWithArticles = [];
$allowedCategories = [
'deutsch', 'englisch', 'franzoesisch', 'latein', 'literatur',
'mathe', 'biologie', 'chemie', 'physik', 'informatik', 'astronomie',
'geschichte', 'erdkunde', 'sozialkunde', 'wirtschaft', 'religion',
'ethik', 'philosophie', 'psychologie', 'kunst', 'musik', 'theater',
'technik', 'werken', 'hauswirtschaft', 'sport'
];
try {
$articleManager = ArticleManager::getInstance();
// Beziehen der Dummy-Beiträge aus dem ArticleManager:
$dummy1 = $articleManager->getArticle(1);
$dummy2 = $articleManager->getArticle(2);
$dummy3 = $articleManager->getArticle(3);
foreach ($allowedCategories as $categorySlug) {
$allCategoryArticles = $articleManager->getArticlesByCategory($categorySlug);
// nur Kategorien, die Beiträge enthält:
if (!empty($allCategoryArticles) && is_array($allCategoryArticles)) {
// Beiträge nach Erstellungsdatum sortieren:
usort($allCategoryArticles, function($a, $b) {
$dateA = strtotime($a->getCreationDate());
$dateB = strtotime($b->getCreationDate());
return $dateB <=> $dateA; // Absteigende Sortierung
});
// auf die 5 zuletzt hinzugefügten Beiträge begrenzen:
$limitedArticles = array_slice($allCategoryArticles, 0, 5);
$categoriesWithArticles[] = [
'slug' => $categorySlug,
'name' => ucfirst($categorySlug), // Erster Buchstabe groß für die Ansicht
'articles' => $limitedArticles
];
}
}
} catch (Exception $e){
$_SESSION["message"] = "internal_error";
echo "Fehler aufgetreten: " . $e->getMessage();
}
?>
+42
View File
@@ -0,0 +1,42 @@
<?php
if (session_status() === PHP_SESSION_NONE) {
session_start();
}
require_once __DIR__ . '/../model/Article.php';
require_once __DIR__ . '/../model/ArticleManager.php';
// 2. Prüfen, ob eine gültige Artikel-ID übergeben wurde
if (isset($_GET["id"]) && !empty($_GET["id"])) {
$articleId = intval($_GET["id"]);
$userEmail = $_SESSION["user_email"];
if (!isset($_SESSION["user_email"]) || empty($_SESSION["user_email"])) {
$_SESSION["message"] = "unauthorized_access";
header("Location: ../../index.php?pfad=showArticle&id=" . $articleId);
exit();
}
try {
$articleManager = ArticleManager::getInstance();
$articleManager->toggleLike($articleId, $userEmail);
header("Location: ../../index.php?pfad=showArticle&id=" . $articleId);
exit();
} catch (NotFoundException $e) {
$_SESSION["message"] = "missing_id";
header("Location: ../../index.php");
exit();
} catch (Exception $e) {
$_SESSION["message"] = "internal_error";
header("Location: ../../index.php");
exit();
}
} else {
$_SESSION["message"] = "missing_id";
header("Location: ../../index.php");
exit();
}
?>
+5
View File
@@ -3,6 +3,7 @@
require_once "php/model/UserManager.php";
require_once "php/model/Article.php";
require_once "php/model/ArticleManager.php";
require_once "php/model/CommentManager.php";
require_once "php/validator/user-validator.php";
$error = null;
@@ -62,6 +63,7 @@ try {
$_SESSION["user"] = $vorname . " " . $nachname;
$_SESSION["user_email"] = $newEmail;
$_SESSION["message"] = "profile_updated";
header("Location: index.php?pfad=profile");
exit();
} else {
@@ -76,6 +78,9 @@ try {
$articleManager = ArticleManager::getInstance();
$userArticles = $articleManager->getArticlesByAuthor($_SESSION["user_email"]);
$commentManager = CommentManager::getInstance();
$userComments = $commentManager->getCommentsByAuthor($_SESSION["user_email"]);
if (!isset($userArticles)) {
$_SESSION["message"] = "user_has_no_articles";
}
+20 -11
View File
@@ -25,18 +25,22 @@ if ($_SERVER["REQUEST_METHOD"] === "GET" && isset($_GET["q"])) {
if ($sortStyle === 'alphabet') {
// Titel aufsteigend alphabetiisch sortiert
usort($results, function ($a, $b) {
return strcasecmp($a->getTitle(), $b->getTitle());
});
} elseif ($sortStyle === 'likes') {
usort($results, function($a, $b) {
return strcasecmp($a->title, $b->title);
return $b->getLikeCount() <=> $a->getLikeCount();
});
} elseif ($sortStyle === 'newest') {
// Datum neu zu alt sortiert
usort($results, function($a, $b) {
return strcmp($b->creationDate, $a->creationDate);
return strcmp($b->getCreationDate(), $a->getCreationDate());
});
} elseif ($sortStyle === 'oldest') {
// Datum alt zu neu sortiert
usort($results, function($a, $b) {
return strcmp($a->creationDate, $b->creationDate);
return strcmp($a->getCreationDate(), $b->getCreationDate());
});
}
@@ -44,13 +48,14 @@ if ($_SERVER["REQUEST_METHOD"] === "GET" && isset($_GET["q"])) {
$safeArrayResults = [];
foreach ($results as $obj) {
$safeArrayResults[] = [
"id" => $obj->id,
"title" => $obj->title,
"content" => $obj->content,
"author" => $obj->author,
"category" => $obj->category,
"tags" => $obj->tags,
"creationDate" => $obj->creationDate
"id" => $obj->getId(),
"title" => $obj->getTitle(),
"content" => $obj->getContent(),
"author" => $obj->getAuthor(),
"category" => $obj->getCategory(),
"tags" => $obj->getTags(),
"creationDate" => $obj->getCreationDate(),
"likes" => $obj->getLikes(),
];
}
@@ -62,7 +67,11 @@ if ($_SERVER["REQUEST_METHOD"] === "GET" && isset($_GET["q"])) {
$_SESSION["message"] = "internal_error";
}
}
header("Location: ../../index.php?pfad=search-results");
$sort = $_GET['sort'] ?? 'alphabet';
$limit = isset($_GET['limit']) ? (int)$_GET['limit'] : 10;
$page = isset($_GET['page']) ? (int)$_GET['page'] : 1;
header("Location: ../../index.php?pfad=search-results&q=" . urlencode($search) . "&sort=" . urlencode($sort) . "&limit=" . $limit . "&page=" . $page);
exit();
}
+15
View File
@@ -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/CommentManager.php';
if (isset($_GET["id"]) && !empty($_GET["id"])){
try {
@@ -17,11 +18,25 @@ if (isset($_GET["id"]) && !empty($_GET["id"])){
$category = $article->getCategory();
$author = $article->getAuthor();
$tags = $article->getTags();
$articleObj = $article; // Objekt für die Like-Abfagen sichern
}else{
//header("location: index.php?pfad=404");
include_once "content/404.php";
exit();
}
$commentManager = CommentManager::getInstance();
$comments = $commentManager->getCommentsByArticle($_GET["id"]);
foreach ($comments as $comment) {
if ($comment->isReply()) {
$parentId = $comment->getParentCommentId();
$repliesByParent[$parentId][] = $comment;
} else {
$mainComments[] = $comment;
}
}
} catch (Exception $e){
$_SESSION["message"] = "internal_error";
exit();
@@ -8,6 +8,11 @@ require_once '../model/ArticleManager.php';
require_once '../model/Article.php';
require_once '../validator/article-validator.php';
if (!isset($_SESSION["user"])) {
header("Location: index.php?pfad=login");
exit();
}
if ($_SERVER["REQUEST_METHOD"] === "POST") {
$_SESSION["old_title"] = $_POST["title"] ?? '';
$_SESSION["old_content"] = $_POST["content"] ?? '';
@@ -22,6 +27,20 @@ if ($_SERVER["REQUEST_METHOD"] === "POST") {
exit();
}
try {
$articleManager = ArticleManager::getInstance();
$article = $articleManager->getArticle($id);
if ($article->getAuthor() != $_SESSION["user"]->getUsername()) {
$_SESSION["message"] = "unauthorized_access";
header("location: ../../index.php");
exit();
}
} catch (Exception $e) {
$_SESSION["message"] = $e->getMessage();
header("location: ../../index.php?pfad=updateArticle&id=$id");
exit();
}
if (!isset($_POST["title"]) ||!isset($_POST["content"]) || !isset($_POST["category"])){
$_SESSION["message"] = "missing_parameters";
header("location: ../../index.php?pfad=updateArticle&id=$id");
+39 -10
View File
@@ -7,13 +7,14 @@
*/
class Article
{
public $id;
public $title;
public $content;
public $author;
public $creationDate;
public $category;
public $tags;
private $id;
private $title;
private $content;
private $author;
private $creationDate;
private $category;
private $tags;
private $likes;
/**
* Konstruktor
@@ -26,7 +27,7 @@ class Article
* @param $tags string optionale Schlagworte für eine bessere Suche
* @param $creationDate string Datum der Beitragserstellung
*/
public function __construct(int $id, string $title, string $content, string $author, string $category, string $tags, string $creationDate)
public function __construct(int $id, string $title, string $content, string $author, string $category, string $tags, string $creationDate, array $likes = [])
{
$this->id = $id;
$this->title = $title;
@@ -35,6 +36,7 @@ class Article
$this->creationDate = $creationDate;
$this->category = $category;
$this->tags = $tags;
$this->likes = $likes;
}
/**
@@ -67,7 +69,7 @@ class Article
/**
* Gibt den Content eines Beitrags zurück.
* TODO: Content muss noch definiert werden.
*
* @return string
*/
public function getContent(): string
@@ -77,7 +79,7 @@ class Article
/**
* Setzt den Content eines Beitrags.
* TODO: Content muss noch definiert werden.
*
* @param $content
* @return void
*/
@@ -141,7 +143,34 @@ class Article
$this->tags = $tags;
}
/**
* Gibt alle User-IDs zurück, die diesen Beitrag geliked haben.
* @return array
*/
public function getLikes(): array
{
return $this->likes;
}
/**
* Gibt die Gesamtzahl der Likes zurück.
* @return int
*/
public function getLikeCount(): int
{
return count($this->likes);
}
/**
* Prüft, ob ein bestimmter Nutzer den Beitrag bereits geliked hat.
*
* @param string $userId
* @return bool
*/
public function hasLiked(string $userId): bool
{
return in_array($userId, $this->likes);
}
}
?>
+12
View File
@@ -92,6 +92,18 @@ interface ArticleManagerDAO
*/
public function getArticlesByCategory($category);
/**
* Registriert oder entfernt ein Like eines Nutzers für einen Beitrag.
* Wenn schon geliked -> Unlike
* Wenn noch nicht geliked -> Like
*
* @param int $articleId Die ID des Beitrags
* @param string $userId Die ID des Nutzers
* @return bool True wenn geliked, False wenn unliked
* @throws InternalServerErrorException
* @throws NotFoundException
*/
public function toggleLike(int $articleId, string $userId): bool;
}
?>
+115
View File
@@ -0,0 +1,115 @@
<?php
/**
* Repräsentiert einen Kommentar unter einem Beitrag.
*
* Ein Kommentar kann entweder ein Hauptkommentar sein
* oder eine Antwort auf einen anderen Kommentar.
*
* @author Caroline Schulte
*/
class Comment
{
private int $id;
private int $articleId;
private ?int $parentCommentId;
private string $author;
private string $content;
private string $created;
/**
* Erstellt einen neuen Kommentar.
*
* @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 $content Inhalt des Kommentars
* @param string $created Erstellungsdatum des Kommentars
*/
public function __construct(
int $id,
int $articleId,
?int $parentCommentId,
string $author,
string $content,
string $created
) {
$this->id = $id;
$this->articleId = $articleId;
$this->parentCommentId = $parentCommentId;
$this->author = $author;
$this->content = $content;
$this->created = $created;
}
/**
* Gibt die ID des Kommentars zurück.
*
* @return int Kommentar-ID
*/
public function getId(): int
{
return $this->id;
}
/**
* Gibt die ID des zugehörigen Beitrags zurück.
*
* @return int Beitrags-ID
*/
public function getArticleId(): int
{
return $this->articleId;
}
/**
* Gibt die ID des Eltern-Kommentars zurück.
*
* @return int|null ID des Eltern-Kommentars oder null
*/
public function getParentCommentId(): ?int
{
return $this->parentCommentId;
}
/**
* Gibt zurück, ob der Kommentar eine Antwort ist.
*
* @return bool true wenn der Kommentar eine Antwort ist, sonst false
*/
public function isReply(): bool
{
return $this->parentCommentId !== null && $this->parentCommentId !== 0;
}
/**
* Gibt den Autor des Kommentars zurück.
*
* @return string Autor
*/
public function getAuthor(): string
{
return $this->author;
}
/**
* Gibt den Inhalt des Kommentars zurück.
*
* @return string Kommentarinhalt
*/
public function getContent(): string
{
return $this->content;
}
/**
* Gibt das Erstellungsdatum des Kommentars zurück.
*
* @return string Erstellungsdatum
*/
public function getCreated(): string
{
return $this->created;
}
}
+25
View File
@@ -0,0 +1,25 @@
<?php
require_once "DatabaseCommentManager.php";
/**
* Zentrale Zugriffsschicht für Kommentare.
*
* Die Anwendung arbeitet ausschließlich
* mit dem CommentManager und kennt die
* konkrete Speicherimplementierung nicht.
*
* @author Caroline Schulte
*/
class CommentManager
{
/**
* Gibt die aktive Kommentarverwaltung zurück.
*
* @return CommentManagerDAO
*/
public static function getInstance()
{
return DatabaseCommentManager::getInstance();
}
}
+54
View File
@@ -0,0 +1,54 @@
<?php
require_once "Comment.php";
/**
* Schnittstelle für die Verwaltung von Kommentaren.
*
* Definiert die grundlegenden Methoden zum
* Speichern und Laden von Kommentaren.
*
* @author Caroline Schulte
*/
interface CommentManagerDAO
{
/**
* Speichert einen neuen Kommentar zu einem Beitrag.
*
* Optional kann eine parentCommentId übergeben werden,
* wenn der Kommentar eine Antwort auf einen anderen Kommentar ist.
*
* @param int $articleId ID des Beitrags
* @param string $author Autor des Kommentars
* @param string $content Inhalt des Kommentars
* @param int|null $parentCommentId ID des Eltern-Kommentars oder null
*
* @return int ID des neu gespeicherten Kommentars
*/
public function addComment(
$articleId,
$author,
$content,
$parentCommentId = null
);
/**
* Gibt alle Kommentare eines Beitrags zurück.
*
* @param int $articleId ID des Beitrags
*
* @return Comment[] Liste der Kommentare
*/
public function getCommentsByArticle(
$articleId
);
/**
* Gibt alle Kommentare eines Autors zurück.
*
* @param string $author E-Mail-Adresse des Autors
*
* @return Comment[] Liste der Kommentare
*/
public function getCommentsByAuthor($author);
}
+91 -4
View File
@@ -23,6 +23,7 @@ class DatabaseArticleManager implements ArticleManagerDAO {
$db = $this->getConnection();
// Tabelle für Beiträge
$db->exec("
CREATE TABLE articles (
id INTEGER PRIMARY KEY AUTOINCREMENT,
@@ -33,6 +34,15 @@ class DatabaseArticleManager implements ArticleManagerDAO {
tags TEXT,
created TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);");
// Tabelle für Likes
$db->exec("
CREATE TABLE likes (
article_id INTEGER,
user_id TEXT,
PRIMARY KEY (article_id, user_id),
FOREIGN KEY (article_id) REFERENCES articles(id) ON DELETE CASCADE
);");
unset($db);
} catch (PDOException $e) {
throw new InternalServerErrorException($e->getMessage());
@@ -189,6 +199,8 @@ class DatabaseArticleManager implements ArticleManagerDAO {
$row = $command->fetch(PDO::FETCH_ASSOC);
if ($row) {
$likes = $this->getLikesForArticle(intval($row['id']));
return new Article(
intval($row['id']),
$row['title'],
@@ -196,7 +208,8 @@ class DatabaseArticleManager implements ArticleManagerDAO {
$row['author'],
$row['category'],
$row['tags'],
$row['created']
$row['created'],
$likes
);
}
@@ -254,6 +267,8 @@ class DatabaseArticleManager implements ArticleManagerDAO {
$filteredArticles = [];
foreach ($rows as $row) {
$likes = $this->getLikesForArticle(intval($row['id']));
$filteredArticles[] = new Article(
intval($row['id']),
$row['title'],
@@ -261,7 +276,8 @@ class DatabaseArticleManager implements ArticleManagerDAO {
$row['author'],
$row['category'],
$row['tags'],
$row['created']
$row['created'],
$likes
);
}
@@ -287,6 +303,8 @@ class DatabaseArticleManager implements ArticleManagerDAO {
$filteredArticles = [];
foreach ($rows as $row) {
$likes = $this->getLikesForArticle(intval($row['id']));
$filteredArticles[] = new Article(
intval($row['id']),
$row['title'],
@@ -294,7 +312,8 @@ class DatabaseArticleManager implements ArticleManagerDAO {
$row['author'],
$row['category'],
$row['tags'],
$row['created']
$row['created'],
$likes
);
}
@@ -341,6 +360,8 @@ class DatabaseArticleManager implements ArticleManagerDAO {
$filteredArticles = [];
foreach ($rows as $row) {
$likes = $this->getLikesForArticle(intval($row['id']));
$filteredArticles[] = new Article(
intval($row['id']),
$row['title'] ?? '',
@@ -348,7 +369,8 @@ class DatabaseArticleManager implements ArticleManagerDAO {
$row['author'] ?? '',
$row['category'] ?? '',
$row['tags'] ?? '',
$row['created'] ?? '' // Nutzt 'created' aus deiner DB-Struktur
$row['created'] ?? '',
$likes
);
}
@@ -359,4 +381,69 @@ class DatabaseArticleManager implements ArticleManagerDAO {
}
}
/**
* Holt alle User-IDs, die einen bestimmten Beitrag geliked haben.
*
* @return String[] UserIDs
* @throws InternalServerErrorException
*/
private function getLikesForArticle(int $articleId): array
{
try {
$db = $this->getConnection();
$sql = "SELECT user_id FROM likes WHERE article_id = :article_id;";
$command = $db->prepare($sql);
$command->execute([':article_id' => $articleId]);
return $command->fetchAll(PDO::FETCH_COLUMN) ?: [];
} catch (PDOException $e) {
return [];
}
}
public function toggleLike(int $articleId, string $userId): bool
{
// prüfen, ob der Artikel überhaupt existiert
$article = $this->getArticle($articleId);
if (!$article) {
throw new NotFoundException("missing_id");
}
try {
$db = $this->getConnection();
// prüfen, ob das Like bereits existiert
$checkSql = "SELECT COUNT(*) FROM likes WHERE article_id = :article_id AND user_id = :user_id;";
$checkCommand = $db->prepare($checkSql);
$checkCommand->execute([
':article_id' => $articleId,
':user_id' => $userId
]);
$hasLiked = (int)$checkCommand->fetchColumn() > 0;
if ($hasLiked) {
// wenn bereits geliked -> Unlike
$deleteSql = "DELETE FROM likes WHERE article_id = :article_id AND user_id = :user_id;";
$deleteCommand = $db->prepare($deleteSql);
$deleteCommand->execute([
':article_id' => $articleId,
':user_id' => $userId
]);
return false; // gibt false zurück, da der Beitrag jetzt nicht mehr geliked ist
} else {
// wenn noch nicht geliked -> Like
$insertSql = "INSERT INTO likes (article_id, user_id) VALUES (:article_id, :user_id);";
$insertCommand = $db->prepare($insertSql);
$insertCommand->execute([
':article_id' => $articleId,
':user_id' => $userId
]);
return true; // gibt true zurück, da der Beitrag jetzt geliked ist
}
} catch (PDOException $e) {
throw new InternalServerErrorException("internal_error");
}
}
}
+257
View File
@@ -0,0 +1,257 @@
<?php
require_once "CommentManagerDAO.php";
require_once "Comment.php";
/**
* Verwaltet die Speicherung und das Laden von Kommentaren
* über eine SQLite-Datenbank.
*
* @author Caroline Schulte
*/
class DatabaseCommentManager implements CommentManagerDAO
{
private static $instance = null;
/**
* Erstellt die Kommentartabelle, falls diese noch nicht existiert.
*/
public function __construct()
{
try {
$db = $this->getConnection();
$db->exec("
CREATE TABLE IF NOT EXISTS comments (
id INTEGER PRIMARY KEY AUTOINCREMENT,
article_id INTEGER NOT NULL,
parent_comment_id INTEGER NULL,
author TEXT NOT NULL,
content TEXT NOT NULL,
created TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
");
$columns = $db->query("PRAGMA table_info(comments);")->fetchAll(PDO::FETCH_ASSOC);
$hasParentColumn = false;
foreach ($columns as $column) {
if ($column["name"] === "parent_comment_id") {
$hasParentColumn = true;
break;
}
}
if (!$hasParentColumn) {
$db->exec("ALTER TABLE comments ADD COLUMN parent_comment_id INTEGER NULL;");
}
} catch (PDOException $e) {
throw new RuntimeException("internal_error");
}
}
/**
* Baut die Verbindung zur SQLite-Datenbank auf.
*
* @return PDO Datenbankverbindung
*/
private function getConnection()
{
try {
$dsn = 'sqlite:' . __DIR__ . '/../../db/comments.db';
$db = new PDO($dsn, null, null);
$db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
return $db;
} catch (PDOException $e) {
throw new RuntimeException("internal_error");
}
}
/**
* Gibt die Singleton-Instanz zurück.
*
* @return DatabaseCommentManager
*/
public static function getInstance()
{
if (self::$instance === null) {
self::$instance = new DatabaseCommentManager();
}
return self::$instance;
}
/**
* Speichert einen neuen Kommentar oder eine Antwort.
*
* @param int $articleId ID des Beitrags
* @param string $author Autor des Kommentars
* @param string $content Inhalt des Kommentars
* @param int|null $parentCommentId ID des Eltern-Kommentars oder null
*
* @return int ID des neu gespeicherten Kommentars
*/
public function addComment(
$articleId,
$author,
$content,
$parentCommentId = null
) {
try {
$db = $this->getConnection();
if ($parentCommentId === "" || $parentCommentId === 0 || $parentCommentId === "0") {
$parentCommentId = null;
}
$sql = "
INSERT INTO comments (
article_id,
parent_comment_id,
author,
content
)
VALUES (
:articleId,
:parentCommentId,
:author,
:content
)
";
$command = $db->prepare($sql);
$command->execute([
":articleId" => $articleId,
":parentCommentId" => $parentCommentId,
":author" => $author,
":content" => $content
]);
return intval($db->lastInsertId());
} catch (PDOException $e) {
throw new RuntimeException("internal_error");
}
}
/**
* Lädt alle Kommentare eines Beitrags.
*
* @param int $articleId ID des Beitrags
*
* @return Comment[]
*/
public function getCommentsByArticle($articleId)
{
try {
$db = $this->getConnection();
$sql = "
SELECT
id,
article_id,
CASE
WHEN parent_comment_id IS NULL THEN NULL
WHEN parent_comment_id = '' THEN NULL
WHEN parent_comment_id = 0 THEN NULL
ELSE parent_comment_id
END AS parent_comment_id,
author,
content,
created
FROM comments
WHERE article_id = :articleId
ORDER BY created ASC
";
$command = $db->prepare($sql);
$command->execute([":articleId" => $articleId]);
return $this->mapRowsToComments($command);
} catch (PDOException $e) {
throw new RuntimeException("internal_error");
}
}
/**
* Lädt alle Kommentare eines Autors.
*
* @param string $author E-Mail-Adresse des Autors
*
* @return Comment[]
*/
public function getCommentsByAuthor($author)
{
try {
$db = $this->getConnection();
$sql = "
SELECT
id,
article_id,
CASE
WHEN parent_comment_id IS NULL THEN NULL
WHEN parent_comment_id = '' THEN NULL
WHEN parent_comment_id = 0 THEN NULL
ELSE parent_comment_id
END AS parent_comment_id,
author,
content,
created
FROM comments
WHERE author = :author
ORDER BY created DESC
";
$command = $db->prepare($sql);
$command->execute([":author" => $author]);
return $this->mapRowsToComments($command);
} catch (PDOException $e) {
throw new RuntimeException("internal_error");
}
}
/**
* Wandelt Datenbankzeilen in Comment-Objekte um.
*
* @param PDOStatement $command Ausgeführtes Statement
*
* @return Comment[]
*/
private function mapRowsToComments($command)
{
$comments = [];
while ($row = $command->fetch(PDO::FETCH_ASSOC)) {
$parentCommentId = null;
if (
isset($row["parent_comment_id"])
&& $row["parent_comment_id"] !== null
&& $row["parent_comment_id"] !== ""
&& intval($row["parent_comment_id"]) !== 0
) {
$parentCommentId = intval($row["parent_comment_id"]);
}
$comments[] = new Comment(
intval($row["id"]),
intval($row["article_id"]),
$parentCommentId,
$row["author"],
$row["content"],
$row["created"]
);
}
return $comments;
}
}
+64 -6
View File
@@ -62,7 +62,8 @@ class LocalArticleManager implements ArticleManagerDAO {
"author" => $author,
"category" => $category,
"tags" => $tags,
"creationDate" => date("Y-m-d H:i:s")
"creationDate" => date("Y-m-d H:i:s"),
"likes" => []
];
$this->saveArticle($articles);
@@ -92,7 +93,8 @@ class LocalArticleManager implements ArticleManagerDAO {
"author" => $author,
"category" => $article->getCategory(),
"tags" => $article->getTags(),
"creationDate" => $article->getCreationDate()
"creationDate" => $article->getCreationDate(),
"likes" => $storedArticle['likes'] ?? []
];
$updated = true;
break;
@@ -142,7 +144,17 @@ class LocalArticleManager implements ArticleManagerDAO {
foreach ($articles as $article) {
if (isset($article['id']) && $article['id'] == $id) {
return new Article(intval($article['id']), $article['title'], $article['content'], $article['author'], $article['category'], $article['tags'], $article['creationDate']);
$likes = isset($article['likes']) && is_array($article['likes']) ? $article['likes'] : [];
return new Article(
intval($article['id']),
$article['title'],
$article['content'],
$article['author'],
$article['category'],
$article['tags'],
$article['creationDate'],
$likes
);
}
}
@@ -168,6 +180,7 @@ class LocalArticleManager implements ArticleManagerDAO {
foreach ($articles as $article) {
if (isset($article['author']) && $article['author'] == $author) {
$likes = isset($article['likes']) && is_array($article['likes']) ? $article['likes'] : [];
$filteredArticles[] = new Article(
intval($article['id']),
$article['title'],
@@ -175,7 +188,8 @@ class LocalArticleManager implements ArticleManagerDAO {
$article['author'],
$article['category'],
$article['tags'],
$article['creationDate']
$article['creationDate'],
$likes
);
}
}
@@ -201,6 +215,7 @@ class LocalArticleManager implements ArticleManagerDAO {
if (($cleanKeyword !== '' && strpos($title, $cleanKeyword) !== false) ||
($cleanKeyword !== '' && strpos($content, $cleanKeyword) !== false)) {
$likes = isset($article['likes']) && is_array($article['likes']) ? $article['likes'] : [];
$filteredArticles[] = new Article(
intval($article['id'] ?? 0),
$article['title'] ?? '',
@@ -208,7 +223,8 @@ class LocalArticleManager implements ArticleManagerDAO {
$article['author'] ?? '',
$article['category'] ?? '',
$article['tags'] ?? '',
$article['creationDate'] ?? ''
$article['creationDate'] ?? '',
$likes
);
}
}
@@ -223,6 +239,7 @@ class LocalArticleManager implements ArticleManagerDAO {
foreach ($articles as $article) {
if (isset($article['category']) && $article['category'] == $category) {
$likes = isset($article['likes']) && is_array($article['likes']) ? $article['likes'] : [];
$filteredArticles[] = new Article(
intval($article['id']),
$article['title'],
@@ -230,12 +247,53 @@ class LocalArticleManager implements ArticleManagerDAO {
$article['author'],
$article['category'],
$article['tags'],
$article['creationDate']
$article['creationDate'],
$likes
);
}
}
return $filteredArticles;
}
public function toggleLike(int $articleId, string $userId): bool
{
$articles = $this->getAllArticles();
$articleFound = false;
$isLikedNow = false;
foreach ($articles as $index => $article) {
if (isset($article['id']) && $article['id'] == $articleId) {
$articleFound = true;
// Likes-Array initialisieren, falls nicht vorhanden
if (!isset($articles[$index]['likes']) || !is_array($articles[$index]['likes'])) {
$articles[$index]['likes'] = [];
}
$likeIndex = array_search($userId, $articles[$index]['likes']);
if ($likeIndex !== false) {
// Bereits geliked -> Unlike
unset($articles[$index]['likes'][$likeIndex]);
// Array-Keys neu indizieren, damit JSON sauber bleibt
$articles[$index]['likes'] = array_values($articles[$index]['likes']);
$isLikedNow = false;
} else {
// Noch nicht geliked -> Like (User-ID hinzufügen)
$articles[$index]['likes'][] = $userId;
$isLikedNow = true;
}
break;
}
}
if (!$articleFound) {
throw new NotFoundException("missing_id");
}
$this->saveArticle($articles);
return $isLikedNow;
}
}
?>