Compare commits
106 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 | |||
| cebbe8869a | |||
| e07051b356 | |||
| 2d2bbeb39f | |||
| c00443b6fa | |||
| 1d541e285c | |||
| b96c588697 | |||
| fde9c59f91 | |||
| 72c7363a07 | |||
| 0b36050558 | |||
| 00dbf83da4 | |||
| c3e060b963 | |||
| a382d9254e | |||
| 2b7f44b817 | |||
| b5e4f44252 | |||
| 8561b02a08 | |||
| 01f4ff2d28 | |||
| b7e4387373 | |||
| e9a92eaec7 | |||
| 7a4f2d738f | |||
| a0a469a9df | |||
| 54209929fe | |||
| 5321534d71 | |||
| 7cf542c270 | |||
| c45d23a6b3 | |||
| ab094429b7 | |||
| cc5f46c00e | |||
| 1b2ab1cccb | |||
| adbf488e01 | |||
| 15ea5ddbfb | |||
| cbb662d159 | |||
| c5e7439541 | |||
| a748d94305 | |||
| 6c1a893f80 | |||
| 788e803e84 | |||
| 4d0f1de01c | |||
| e71ee8300e | |||
| f39885da10 | |||
| b7ca5bcfe2 | |||
| 57b0782577 | |||
| 4f524db221 | |||
| 17034c928b | |||
| 395041fd44 | |||
| c117c7e641 | |||
| 12ad9759cf | |||
| 057639be7c | |||
| 87e7be1c10 | |||
| 1db77346d0 | |||
| 1e6ac0fcd9 | |||
| b3c74c2172 | |||
| 0805958033 | |||
| 5c0e978c93 | |||
| 520298bae4 | |||
| 969adc4836 | |||
| d9eca06197 | |||
| 48f2e90e49 | |||
| 358247a2a1 | |||
| de4d2fe5c0 | |||
| e1102eb7db | |||
| a8df9590fd | |||
| 3e453e22ec | |||
| beeab0ec90 | |||
| 9353a7eaaa | |||
| cac8f3046d | |||
| 66eeac372c |
Generated
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<project version="4">
|
<project version="4">
|
||||||
<component name="dataSourceStorageLocal" created-in="IU-261.25134.95">
|
<component name="dataSourceStorageLocal" created-in="IU-253.32098.101">
|
||||||
<data-source name="articles" uuid="315cb5c9-2b0f-435b-b602-59823b160908">
|
<data-source name="articles" uuid="315cb5c9-2b0f-435b-b602-59823b160908">
|
||||||
<database-info product="SQLite" version="3.51.1" jdbc-version="4.2" driver-name="SQLite JDBC" driver-version="3.51.1.0" dbms="SQLITE" exact-version="3.51.1" exact-driver-version="3.51">
|
<database-info product="SQLite" version="3.51.1" jdbc-version="4.2" driver-name="SQLite JDBC" driver-version="3.51.1.0" dbms="SQLITE" exact-version="3.51.1" exact-driver-version="3.51">
|
||||||
<identifier-quote-string>"</identifier-quote-string>
|
<identifier-quote-string>"</identifier-quote-string>
|
||||||
|
|||||||
@@ -13,6 +13,10 @@
|
|||||||
- Per Klick auf das Logo gelangt man auf die Home-Seite.
|
- Per Klick auf das Logo gelangt man auf die Home-Seite.
|
||||||
|
|
||||||
## Ausgelassene Teilaufgaben
|
## Ausgelassene Teilaufgaben
|
||||||
|
- Das Anerkennen der Datenschutzerklärung und das Einhalten der
|
||||||
|
Nutzungsbedingungen (bspw. was Rechte an eingestellten Texten und
|
||||||
|
hochgeladenen Fotos angeht) muss ein Nutzer bei der Registrierung via einer
|
||||||
|
Checkbox und dem Opt-In-Verfahren fehlt noch.
|
||||||
|
|
||||||
## Bekannte Fehler und Mängel
|
## Bekannte Fehler und Mängel
|
||||||
- Bitte auf die gesetzten TODO's achten. Wenn Inhalte fehlen, sind sie i.d.R. als TODO kommentiert.
|
- Bitte auf die gesetzten TODO's achten. Wenn Inhalte fehlen, sind sie i.d.R. als TODO kommentiert.
|
||||||
@@ -23,6 +27,7 @@
|
|||||||
- Bilder im Beitragseditor sollen zukünftig eine Bildunterschrift bekommen und größenverstellbar sein.
|
- Bilder im Beitragseditor sollen zukünftig eine Bildunterschrift bekommen und größenverstellbar sein.
|
||||||
- Die Elemente eines Contents im Beitrag werden momentan stumpf untereinander aufgelistet. Soll später
|
- Die Elemente eines Contents im Beitrag werden momentan stumpf untereinander aufgelistet. Soll später
|
||||||
sich responisve auch nebeneinander orientieren usw.
|
sich responisve auch nebeneinander orientieren usw.
|
||||||
|
- Die Datenbank ist noch in die verschiedenen Manager unterteilt. Zukünftig werden alle Daten in einer Datenbank gespeichert.
|
||||||
|
|
||||||
## Besonderheiten des Projektes
|
## Besonderheiten des Projektes
|
||||||
- Es wurde AJAX verwendet, um asynchrone Erstellung von Kommentaren zu implementieren. Es ermöglicht dem Nutzer, einen
|
- Es wurde AJAX verwendet, um asynchrone Erstellung von Kommentaren zu implementieren. Es ermöglicht dem Nutzer, einen
|
||||||
@@ -31,6 +36,10 @@
|
|||||||
- JavaScript wird verwendet, um im erweitertem Beitragseditor clientseitig einzelne Content-Boxen erstellen und löschen
|
- JavaScript wird verwendet, um im erweitertem Beitragseditor clientseitig einzelne Content-Boxen erstellen und löschen
|
||||||
zu können.
|
zu können.
|
||||||
- JavaScript wird ebenfalls verwendet, um in die Suchergebnisse clientseitig zu sortieren.
|
- 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
|
## Sonstiges
|
||||||
- Das Datenschema befindet sich unter /planung/Datenschema.pdf
|
- Das Datenschema befindet sich unter /planung/Datenschema.pdf
|
||||||
|
- Im Impressum, sowie der Datenschutzerklaerung wurden Dummy-Daten verwendet
|
||||||
+1
-2
@@ -14,7 +14,6 @@
|
|||||||
?>
|
?>
|
||||||
<h1>404 - Seite nicht vorhanden</h1>
|
<h1>404 - Seite nicht vorhanden</h1>
|
||||||
<p>
|
<p>
|
||||||
Später im Projekt sollen über index.php?pfad= ... der Inhalt der index.php dynamisch gesetzt 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.)
|
||||||
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.
|
|
||||||
</p>
|
</p>
|
||||||
</main>
|
</main>
|
||||||
@@ -0,0 +1,66 @@
|
|||||||
|
<!--
|
||||||
|
Seite: Barrierefreiheitserklärung
|
||||||
|
Inhalt: Erklärung zur Barrierefreiheit der Plattform
|
||||||
|
-->
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="de">
|
||||||
|
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<meta name="description" content="EduForge">
|
||||||
|
<meta name="author" content="Niklas Ortmann">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<link rel="icon" type="image/x-icon" href="../images/logos/logo_icon.ico">
|
||||||
|
<link rel="stylesheet" href="../css/main.css">
|
||||||
|
<link rel="stylesheet" href="../css/navbar.css">
|
||||||
|
<link rel="stylesheet" href="../css/footer.css">
|
||||||
|
<title>EduForge</title>
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
|
||||||
|
<?php
|
||||||
|
include_once 'includes/navbar.php';
|
||||||
|
?>
|
||||||
|
|
||||||
|
<main>
|
||||||
|
|
||||||
|
<h1>Barrierefreiheitserklärung</h1>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
Wir bemühen uns, die Plattform EduForge möglichst barrierefrei zugänglich zu gestalten.
|
||||||
|
Die Website wurde im Rahmen eines Hochschulprojekts erstellt.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<h2>Stand der Vereinbarkeit</h2>
|
||||||
|
<p>
|
||||||
|
Diese Website ist teilweise barrierefrei. Einzelne Funktionen oder Inhalte können
|
||||||
|
noch nicht vollständig barrierefrei nutzbar sein.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<h2>Nicht barrierefreie Inhalte</h2>
|
||||||
|
<p>
|
||||||
|
Einzelne Bilder verfügen möglicherweise noch nicht über vollständige Alternativtexte.
|
||||||
|
Außerdem kann die Bedienung mit Tastatur oder Screenreader in einzelnen Bereichen
|
||||||
|
eingeschränkt sein.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<h2>Feedback und Kontakt</h2>
|
||||||
|
<p>
|
||||||
|
Sollten Ihnen Barrieren auf unserer Website auffallen, kontaktieren Sie uns bitte
|
||||||
|
über die im Impressum angegebenen Kontaktdaten.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<h2>Erstellungsdatum</h2>
|
||||||
|
<p>
|
||||||
|
Diese Erklärung wurde im Rahmen eines Hochschulprojekts erstellt.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
</main>
|
||||||
|
|
||||||
|
<?php
|
||||||
|
include_once 'includes/footer.php';
|
||||||
|
?>
|
||||||
|
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
<?php
|
||||||
|
/** @var string $title */
|
||||||
|
/** @var string $message */
|
||||||
|
/** @var string $link */
|
||||||
|
/** @var bool $isSuccess */
|
||||||
|
|
||||||
|
$title = $title ?? "Passwort zurücksetzen";
|
||||||
|
$message = $message ?? "Der Bestätigungslink konnte nicht verarbeitet werden.";
|
||||||
|
$link = $link ?? "";
|
||||||
|
$isSuccess = $isSuccess ?? false;
|
||||||
|
?>
|
||||||
|
|
||||||
|
<!--
|
||||||
|
Ansicht: Passwortbestätigung
|
||||||
|
Funktion: Zeigt das Ergebnis der Passwortänderung an.
|
||||||
|
-->
|
||||||
|
|
||||||
|
<main class="login-page">
|
||||||
|
<div class="login-container">
|
||||||
|
|
||||||
|
<h1><?php echo htmlspecialchars($title); ?></h1>
|
||||||
|
|
||||||
|
<p class="alert-message <?php echo $isSuccess ? 'is-success' : 'is-error'; ?> confirm-message">
|
||||||
|
<?php echo htmlspecialchars($message); ?>
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<?php echo $link; ?>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</main>
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
<?php
|
||||||
|
/** @var string $title */
|
||||||
|
/** @var string $message */
|
||||||
|
/** @var string $link */
|
||||||
|
/** @var bool $isSuccess */
|
||||||
|
|
||||||
|
$title = $title ?? "Registrierung";
|
||||||
|
$message = $message ?? "Der Registrierungslink konnte nicht verarbeitet werden.";
|
||||||
|
$link = $link ?? "";
|
||||||
|
$isSuccess = $isSuccess ?? false;
|
||||||
|
?>
|
||||||
|
|
||||||
|
<!--
|
||||||
|
Ansicht: Registrierungsbestätigung
|
||||||
|
Funktion: Zeigt das Ergebnis der Registrierungsbestätigung an.
|
||||||
|
-->
|
||||||
|
<main class="login-page">
|
||||||
|
<div class="login-container">
|
||||||
|
|
||||||
|
<h1><?php echo htmlspecialchars($title); ?></h1>
|
||||||
|
|
||||||
|
<p class="alert-message <?php echo $isSuccess ? 'is-success' : 'is-error'; ?> confirm-message">
|
||||||
|
<?php echo htmlspecialchars($message); ?>
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<?php echo $link; ?>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</main>
|
||||||
+180
-9
@@ -4,15 +4,186 @@
|
|||||||
-->
|
-->
|
||||||
<main>
|
<main>
|
||||||
|
|
||||||
<h1>Datenschutz</h1>
|
<h1>Datenschutz­erklärung</h1>
|
||||||
|
<h2>1. Datenschutz auf einen Blick</h2>
|
||||||
|
<h3>Allgemeine Hinweise</h3> <p>Die folgenden Hinweise geben einen einfachen Überblick darüber,
|
||||||
|
was mit Ihren personenbezogenen Daten passiert, wenn Sie diese Website besuchen. Personenbezogene Daten sind
|
||||||
|
alle Daten, mit denen Sie persönlich identifiziert werden können. Ausführliche Informationen zum
|
||||||
|
Thema Datenschutz entnehmen Sie unserer unter diesem Text aufgeführten
|
||||||
|
Datenschutzerklärung.</p>
|
||||||
|
<h3>Datenerfassung auf dieser Website</h3> <h4>Wer ist verantwortlich für die Datenerfassung auf dieser
|
||||||
|
Website?</h4> <p>Die Datenverarbeitung auf dieser Website erfolgt durch den Websitebetreiber. Dessen
|
||||||
|
Kontaktdaten können Sie dem Abschnitt „Hinweis zur verantwortlichen Stelle“ in dieser
|
||||||
|
Datenschutzerklärung entnehmen.</p> <h4>Wie erfassen wir Ihre Daten?</h4> <p>Ihre Daten werden zum
|
||||||
|
einen dadurch erhoben, dass Sie uns diese mitteilen. Hierbei kann es sich z. B. um Daten handeln, die Sie in ein
|
||||||
|
Kontaktformular eingeben.</p> <p>Andere Daten werden automatisch oder nach Ihrer Einwilligung beim Besuch
|
||||||
|
der Website durch unsere IT-Systeme erfasst. Das sind vor allem technische Daten (z. B. Internetbrowser,
|
||||||
|
Betriebssystem oder Uhrzeit des Seitenaufrufs). Die Erfassung dieser Daten erfolgt automatisch, sobald Sie diese
|
||||||
|
Website betreten.</p> <h4>Wofür nutzen wir Ihre Daten?</h4> <p>Ein Teil der Daten wird erhoben, um eine
|
||||||
|
fehlerfreie Bereitstellung der Website zu gewährleisten. Andere Daten können zur Analyse Ihres
|
||||||
|
Nutzerverhaltens verwendet werden. Sofern über die Website Verträge geschlossen oder angebahnt
|
||||||
|
werden können, werden die übermittelten Daten auch für Vertragsangebote, Bestellungen oder
|
||||||
|
sonstige Auftragsanfragen verarbeitet.</p> <h4>Welche Rechte haben Sie bezüglich Ihrer Daten?</h4>
|
||||||
|
<p>Sie haben jederzeit das Recht, unentgeltlich Auskunft über Herkunft, Empfänger und Zweck Ihrer
|
||||||
|
gespeicherten personenbezogenen Daten zu erhalten. Sie haben außerdem ein Recht, die Berichtigung oder
|
||||||
|
Löschung dieser Daten zu verlangen. Wenn Sie eine Einwilligung zur Datenverarbeitung erteilt haben,
|
||||||
|
können Sie diese Einwilligung jederzeit für die Zukunft widerrufen. Außerdem haben Sie das Recht,
|
||||||
|
unter bestimmten Umständen die Einschränkung der Verarbeitung Ihrer personenbezogenen Daten zu
|
||||||
|
verlangen. Des Weiteren steht Ihnen ein Beschwerderecht bei der zuständigen Aufsichtsbehörde
|
||||||
|
zu.</p> <p>Hierzu sowie zu weiteren Fragen zum Thema Datenschutz können Sie sich jederzeit an uns
|
||||||
|
wenden.</p>
|
||||||
|
<h2>2. Hosting</h2>
|
||||||
|
<p>Wir hosten die Inhalte unserer Website bei folgendem Anbieter:</p>
|
||||||
|
<h3>Externes Hosting</h3> <p>Diese Website wird extern gehostet. Die personenbezogenen Daten, die auf dieser
|
||||||
|
Website erfasst werden, werden auf den Servern des Hosters / der Hoster gespeichert. Hierbei kann es sich
|
||||||
|
v. a. um IP-Adressen, Kontaktanfragen, Meta- und Kommunikationsdaten, Vertragsdaten, Kontaktdaten,
|
||||||
|
Namen, Websitezugriffe und sonstige Daten, die über eine Website generiert werden, handeln.</p> <p>Das
|
||||||
|
externe Hosting erfolgt zum Zwecke der Vertragserfüllung gegenüber unseren potenziellen und
|
||||||
|
bestehenden Kunden (Art. 6 Abs. 1 lit. b DSGVO) und im Interesse einer sicheren, schnellen und effizienten
|
||||||
|
Bereitstellung unseres Online-Angebots durch einen professionellen Anbieter (Art. 6 Abs. 1 lit. f DSGVO). Sofern
|
||||||
|
eine entsprechende Einwilligung abgefragt wurde, erfolgt die Verarbeitung ausschließlich auf Grundlage von
|
||||||
|
Art. 6 Abs. 1 lit. a DSGVO und § 25 Abs. 1 TDDDG, soweit die Einwilligung die Speicherung von Cookies oder
|
||||||
|
den Zugriff auf Informationen im Endgerät des Nutzers (z. B. Device-Fingerprinting) im Sinne des
|
||||||
|
TDDDG umfasst. Die Einwilligung ist jederzeit widerrufbar.</p> <p>Unser(e) Hoster wird bzw. werden Ihre Daten
|
||||||
|
nur insoweit verarbeiten, wie dies zur Erfüllung seiner Leistungspflichten erforderlich ist und unsere
|
||||||
|
Weisungen in Bezug auf diese Daten befolgen.</p> <p>Wir setzen folgende(n) Hoster ein:</p>
|
||||||
|
<p>-Platzhalter-</p>
|
||||||
|
<h4>Auftragsverarbeitung</h4> <p>Wir haben einen Vertrag über Auftragsverarbeitung (AVV) zur Nutzung
|
||||||
|
des oben genannten Dienstes geschlossen. Hierbei handelt es sich um einen datenschutzrechtlich vorgeschriebenen
|
||||||
|
Vertrag, der gewährleistet, dass dieser die personenbezogenen Daten unserer Websitebesucher nur nach
|
||||||
|
unseren Weisungen und unter Einhaltung der DSGVO verarbeitet.</p>
|
||||||
|
<h2>3. Allgemeine Hinweise und Pflicht­informationen</h2>
|
||||||
|
<h3>Datenschutz</h3> <p>Die Betreiber dieser Seiten nehmen den Schutz Ihrer persönlichen Daten sehr
|
||||||
|
ernst. Wir behandeln Ihre personenbezogenen Daten vertraulich und entsprechend den gesetzlichen
|
||||||
|
Datenschutzvorschriften sowie dieser Datenschutzerklärung.</p> <p>Wenn Sie diese Website benutzen,
|
||||||
|
werden verschiedene personenbezogene Daten erhoben. Personenbezogene Daten sind Daten, mit denen Sie
|
||||||
|
persönlich identifiziert werden können. Die vorliegende Datenschutzerklärung erläutert,
|
||||||
|
welche Daten wir erheben und wofür wir sie nutzen. Sie erläutert auch, wie und zu welchem Zweck das
|
||||||
|
geschieht.</p> <p>Wir weisen darauf hin, dass die Datenübertragung im Internet (z. B. bei der
|
||||||
|
Kommunikation per E-Mail) Sicherheitslücken aufweisen kann. Ein lückenloser Schutz der Daten vor
|
||||||
|
dem Zugriff durch Dritte ist nicht möglich.</p>
|
||||||
|
<h3>Hinweis zur verantwortlichen Stelle</h3> <p>Die verantwortliche Stelle für die Datenverarbeitung auf
|
||||||
|
dieser Website ist:</p> <p>Ammerlaender Heerstrasse, 114-118</p>
|
||||||
|
<p>Telefon: 0123456789<br />
|
||||||
|
E-Mail: GruppeC@uol.de</p>
|
||||||
|
<p>Verantwortliche Stelle ist die natürliche oder juristische Person, die allein oder gemeinsam mit anderen
|
||||||
|
über die Zwecke und Mittel der Verarbeitung von personenbezogenen Daten (z. B. Namen, E-Mail-
|
||||||
|
Adressen o. Ä.) entscheidet.</p>
|
||||||
|
<h3>Speicherdauer</h3> <p>Soweit innerhalb dieser Datenschutzerklärung keine speziellere Speicherdauer
|
||||||
|
genannt wurde, verbleiben Ihre personenbezogenen Daten bei uns, bis der Zweck für die Datenverarbeitung
|
||||||
|
entfällt. Wenn Sie ein berechtigtes Löschersuchen geltend machen oder eine Einwilligung zur
|
||||||
|
Datenverarbeitung widerrufen, werden Ihre Daten gelöscht, sofern wir keine anderen rechtlich
|
||||||
|
zulässigen Gründe für die Speicherung Ihrer personenbezogenen Daten haben (z. B. steuer-
|
||||||
|
oder handelsrechtliche Aufbewahrungsfristen); im letztgenannten Fall erfolgt die Löschung nach Fortfall
|
||||||
|
dieser Gründe.</p>
|
||||||
|
<h3>Allgemeine Hinweise zu den Rechtsgrundlagen der Datenverarbeitung auf dieser Website</h3> <p>Sofern Sie
|
||||||
|
in die Datenverarbeitung eingewilligt haben, verarbeiten wir Ihre personenbezogenen Daten auf Grundlage von Art.
|
||||||
|
6 Abs. 1 lit. a DSGVO bzw. Art. 9 Abs. 2 lit. a DSGVO, sofern besondere Datenkategorien nach Art. 9 Abs. 1 DSGVO
|
||||||
|
verarbeitet werden. Im Falle einer ausdrücklichen Einwilligung in die Übertragung personenbezogener
|
||||||
|
Daten in Drittstaaten erfolgt die Datenverarbeitung außerdem auf Grundlage von Art. 49 Abs. 1 lit. a DSGVO.
|
||||||
|
Sofern Sie in die Speicherung von Cookies oder in den Zugriff auf Informationen in Ihr Endgerät (z. B. via
|
||||||
|
Device-Fingerprinting) eingewilligt haben, erfolgt die Datenverarbeitung zusätzlich auf Grundlage von §
|
||||||
|
25 Abs. 1 TDDDG. Die Einwilligung ist jederzeit widerrufbar. Sind Ihre Daten zur Vertragserfüllung oder zur
|
||||||
|
Durchführung vorvertraglicher Maßnahmen erforderlich, verarbeiten wir Ihre Daten auf Grundlage des
|
||||||
|
Art. 6 Abs. 1 lit. b DSGVO. Des Weiteren verarbeiten wir Ihre Daten, sofern diese zur Erfüllung einer
|
||||||
|
rechtlichen Verpflichtung erforderlich sind, auf Grundlage von Art. 6 Abs. 1 lit. c DSGVO. Die Datenverarbeitung
|
||||||
|
kann ferner auf Grundlage unseres berechtigten Interesses nach Art. 6 Abs. 1 lit. f DSGVO erfolgen. Über die
|
||||||
|
jeweils im Einzelfall einschlägigen Rechtsgrundlagen wird in den folgenden Absätzen dieser
|
||||||
|
Datenschutzerklärung informiert.</p>
|
||||||
|
<h3>Empfänger von personenbezogenen Daten</h3> <p>Im Rahmen unserer Geschäftstätigkeit
|
||||||
|
arbeiten wir mit verschiedenen externen Stellen zusammen. Dabei ist teilweise auch eine Übermittlung von
|
||||||
|
personenbezogenen Daten an diese externen Stellen erforderlich. Wir geben personenbezogene Daten nur dann an
|
||||||
|
externe Stellen weiter, wenn dies im Rahmen einer Vertragserfüllung erforderlich ist, wenn wir gesetzlich
|
||||||
|
hierzu verpflichtet sind (z. B. Weitergabe von Daten an Steuerbehörden), wenn wir ein berechtigtes
|
||||||
|
Interesse nach Art. 6 Abs. 1 lit. f DSGVO an der Weitergabe haben oder wenn eine sonstige Rechtsgrundlage die
|
||||||
|
Datenweitergabe erlaubt. Beim Einsatz von Auftragsverarbeitern geben wir personenbezogene Daten unserer
|
||||||
|
Kunden nur auf Grundlage eines gültigen Vertrags über Auftragsverarbeitung weiter. Im Falle einer
|
||||||
|
gemeinsamen Verarbeitung wird ein Vertrag über gemeinsame Verarbeitung geschlossen.</p>
|
||||||
|
<h3>Widerruf Ihrer Einwilligung zur Datenverarbeitung</h3> <p>Viele Datenverarbeitungsvorgänge sind
|
||||||
|
nur mit Ihrer ausdrücklichen Einwilligung möglich. Sie können eine bereits erteilte Einwilligung
|
||||||
|
jederzeit widerrufen. Die Rechtmäßigkeit der bis zum Widerruf erfolgten Datenverarbeitung bleibt vom
|
||||||
|
Widerruf unberührt.</p>
|
||||||
|
<h3>Widerspruchsrecht gegen die Datenerhebung in besonderen Fällen sowie gegen Direktwerbung (Art. 21
|
||||||
|
DSGVO)</h3> <p>WENN DIE DATENVERARBEITUNG AUF GRUNDLAGE VON ART. 6 ABS. 1 LIT. E ODER F
|
||||||
|
DSGVO ERFOLGT, HABEN SIE JEDERZEIT DAS RECHT, AUS GRÜNDEN, DIE SICH AUS IHRER
|
||||||
|
BESONDEREN SITUATION ERGEBEN, GEGEN DIE VERARBEITUNG IHRER PERSONENBEZOGENEN DATEN
|
||||||
|
WIDERSPRUCH EINZULEGEN; DIES GILT AUCH FÜR EIN AUF DIESE BESTIMMUNGEN GESTÜTZTES
|
||||||
|
PROFILING. DIE JEWEILIGE RECHTSGRUNDLAGE, AUF DENEN EINE VERARBEITUNG BERUHT, ENTNEHMEN
|
||||||
|
SIE DIESER DATENSCHUTZERKLÄRUNG. WENN SIE WIDERSPRUCH EINLEGEN, WERDEN WIR IHRE
|
||||||
|
BETROFFENEN PERSONENBEZOGENEN DATEN NICHT MEHR VERARBEITEN, ES SEI DENN, WIR
|
||||||
|
KÖNNEN ZWINGENDE SCHUTZWÜRDIGE GRÜNDE FÜR DIE VERARBEITUNG
|
||||||
|
NACHWEISEN, DIE IHRE INTERESSEN, RECHTE UND FREIHEITEN ÜBERWIEGEN ODER DIE
|
||||||
|
VERARBEITUNG DIENT DER GELTENDMACHUNG, AUSÜBUNG ODER VERTEIDIGUNG VON
|
||||||
|
RECHTSANSPRÜCHEN (WIDERSPRUCH NACH ART. 21 ABS. 1 DSGVO).</p> <p>WERDEN IHRE
|
||||||
|
PERSONENBEZOGENEN DATEN VERARBEITET, UM DIREKTWERBUNG ZU BETREIBEN, SO HABEN SIE DAS
|
||||||
|
RECHT, JEDERZEIT WIDERSPRUCH GEGEN DIE VERARBEITUNG SIE BETREFFENDER PERSONENBEZOGENER
|
||||||
|
DATEN ZUM ZWECKE DERARTIGER WERBUNG EINZULEGEN; DIES GILT AUCH FÜR DAS PROFILING,
|
||||||
|
SOWEIT ES MIT SOLCHER DIREKTWERBUNG IN VERBINDUNG STEHT. WENN SIE WIDERSPRECHEN,
|
||||||
|
WERDEN IHRE PERSONENBEZOGENEN DATEN ANSCHLIESSEND NICHT MEHR ZUM ZWECKE DER
|
||||||
|
DIREKTWERBUNG VERWENDET (WIDERSPRUCH NACH ART. 21 ABS. 2 DSGVO).</p>
|
||||||
|
<h3>Beschwerde­recht bei der zuständigen Aufsichts­behörde</h3> <p>Im Falle von
|
||||||
|
Verstößen gegen die DSGVO steht den Betroffenen ein Beschwerderecht bei einer
|
||||||
|
Aufsichtsbehörde, insbesondere in dem Mitgliedstaat ihres gewöhnlichen Aufenthalts, ihres
|
||||||
|
Arbeitsplatzes oder des Orts des mutmaßlichen Verstoßes zu. Das Beschwerderecht besteht
|
||||||
|
unbeschadet anderweitiger verwaltungsrechtlicher oder gerichtlicher Rechtsbehelfe.</p>
|
||||||
|
<h3>Recht auf Daten­übertrag­barkeit</h3> <p>Sie haben das Recht, Daten, die wir auf Grundlage
|
||||||
|
Ihrer Einwilligung oder in Erfüllung eines Vertrags automatisiert verarbeiten, an sich oder an einen Dritten in
|
||||||
|
einem gängigen, maschinenlesbaren Format aushändigen zu lassen. Sofern Sie die direkte
|
||||||
|
Übertragung der Daten an einen anderen Verantwortlichen verlangen, erfolgt dies nur, soweit es technisch
|
||||||
|
machbar ist.</p>
|
||||||
|
<h3>Auskunft, Berichtigung und Löschung</h3> <p>Sie haben im Rahmen der geltenden gesetzlichen
|
||||||
|
Bestimmungen jederzeit das Recht auf unentgeltliche Auskunft über Ihre gespeicherten personenbezogenen
|
||||||
|
Daten, deren Herkunft und Empfänger und den Zweck der Datenverarbeitung und ggf. ein Recht auf
|
||||||
|
Berichtigung oder Löschung dieser Daten. Hierzu sowie zu weiteren Fragen zum Thema personenbezogene
|
||||||
|
Daten können Sie sich jederzeit an uns wenden.</p>
|
||||||
|
<h3>Recht auf Einschränkung der Verarbeitung</h3> <p>Sie haben das Recht, die Einschränkung der
|
||||||
|
Verarbeitung Ihrer personenbezogenen Daten zu verlangen. Hierzu können Sie sich jederzeit an uns wenden.
|
||||||
|
Das Recht auf Einschränkung der Verarbeitung besteht in folgenden Fällen:</p> <ul> <li>Wenn Sie die
|
||||||
|
Richtigkeit Ihrer bei uns gespeicherten personenbezogenen Daten bestreiten, benötigen wir in der Regel Zeit,
|
||||||
|
um dies zu überprüfen. Für die Dauer der Prüfung haben Sie das Recht, die
|
||||||
|
Einschränkung der Verarbeitung Ihrer personenbezogenen Daten zu verlangen.</li> <li>Wenn die
|
||||||
|
Verarbeitung Ihrer personenbezogenen Daten unrechtmäßig geschah/geschieht, können Sie statt
|
||||||
|
der Löschung die Einschränkung der Datenverarbeitung verlangen.</li> <li>Wenn wir Ihre
|
||||||
|
personenbezogenen Daten nicht mehr benötigen, Sie sie jedoch zur Ausübung, Verteidigung oder
|
||||||
|
Geltendmachung von Rechtsansprüchen benötigen, haben Sie das Recht, statt der Löschung die
|
||||||
|
Einschränkung der Verarbeitung Ihrer personenbezogenen Daten zu verlangen.</li> <li>Wenn Sie einen
|
||||||
|
Widerspruch nach Art. 21 Abs. 1 DSGVO eingelegt haben, muss eine Abwägung zwischen Ihren und unseren
|
||||||
|
Interessen vorgenommen werden. Solange noch nicht feststeht, wessen Interessen überwiegen, haben Sie das
|
||||||
|
Recht, die Einschränkung der Verarbeitung Ihrer personenbezogenen Daten zu verlangen.</li> </ul>
|
||||||
|
<p>Wenn Sie die Verarbeitung Ihrer personenbezogenen Daten eingeschränkt haben, dürfen diese
|
||||||
|
Daten – von ihrer Speicherung abgesehen – nur mit Ihrer Einwilligung oder zur Geltendmachung,
|
||||||
|
Ausübung oder Verteidigung von Rechtsansprüchen oder zum Schutz der Rechte einer anderen
|
||||||
|
natürlichen oder juristischen Person oder aus Gründen eines wichtigen öffentlichen Interesses der
|
||||||
|
Europäischen Union oder eines Mitgliedstaats verarbeitet werden.</p>
|
||||||
|
|
||||||
<p>
|
<h4>Cookies</h4>
|
||||||
Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et
|
<p>
|
||||||
dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet
|
Unsere Internetseiten verwenden so genannte „Cookies“. Cookies sind kleine Datenpakete und richten auf
|
||||||
clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet,
|
Ihrem Endgerät keinen Schaden an. Sie werden entweder vorübergehend für die Dauer einer Sitzung
|
||||||
consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat,
|
(Session-Cookies) oder dauerhaft (permanente Cookies) auf Ihrem Endgerät gespeichert. Session-Cookies
|
||||||
sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no
|
werden nach Ende Ihres Besuchs automatisch gelöscht. Permanente Cookies bleiben auf Ihrem Endgerät
|
||||||
sea takimata sanctus est Lorem ipsum dolor sit amet.
|
gespeichert, bis Sie diese selbst löschen oder eine automatische Löschung durch Ihren Webbrowser erfolgt.
|
||||||
</p>
|
Cookies haben verschiedene Funktionen. Zahlreiche Cookies sind technisch notwendig, da bestimmte
|
||||||
|
Webseitenfunktionen ohne diese nicht funktionieren würden (z. B. die Anmeldung). Andere Cookies können zur
|
||||||
|
Auswertung des Nutzerverhaltens oder zu Werbezwecken verwendet werden.
|
||||||
|
Cookies, die zur Durchführung des elektronischen Kommunikationsvorgangs, zur Bereitstellung
|
||||||
|
bestimmter, von Ihnen erwünschter Funktionen oder zur Optimierung der
|
||||||
|
Website (z. B. Cookies zur Messung des Webpublikums) erforderlich sind (notwendige Cookies), werden auf
|
||||||
|
Grundlage von Art. 6 Abs. 1 lit. f DSGVO gespeichert, sofern keine andere Rechtsgrundlage angegeben wird.
|
||||||
|
Der Websitebetreiber hat ein berechtigtes Interesse an der Speicherung von notwendigen Cookies zur
|
||||||
|
technisch fehlerfreien und optimierten Bereitstellung seiner Dienste. Sofern eine Einwilligung zur
|
||||||
|
Speicherung von Cookies und vergleichbaren Wiedererkennungstechnologien abgefragt wurde, erfolgt die
|
||||||
|
Verarbeitung ausschließlich auf Grundlage dieser Einwilligung (Art. 6 Abs. 1 lit. a DSGVO und § 25 Abs. 1
|
||||||
|
TDDDG); die Einwilligung ist jederzeit widerrufbar.
|
||||||
|
Sie können Ihren Browser so einstellen, dass Sie über das Setzen von Cookies informiert werden und
|
||||||
|
Cookies nur im Einzelfall erlauben, die Annahme von Cookies für bestimmte Fälle oder generell ausschließen
|
||||||
|
sowie das automatische Löschen der Cookies beim Schließen des Browsers aktivieren. Bei der
|
||||||
|
Deaktivierung von Cookies kann die Funktionalität dieser Website eingeschränkt sein.
|
||||||
|
Sofern weitere Cookies und Dienste auf dieser Website eingesetzt werden, können Sie dies dieser
|
||||||
|
Datenschutzerklärung entnehmen.
|
||||||
|
|
||||||
|
Auf dieser Webseite werden jedoch nur technisch notwendige Cookies verwendet.
|
||||||
|
</p>
|
||||||
|
<p>Quelle: <a href="https://www.e-recht24.de">https://www.e-recht24.de</a></p>
|
||||||
|
|
||||||
</main>
|
</main>
|
||||||
|
|||||||
+3
-6
@@ -11,12 +11,9 @@ include_once 'php/controller/home-controller.php';
|
|||||||
|
|
||||||
<h1>Home</h1>
|
<h1>Home</h1>
|
||||||
<p>
|
<p>
|
||||||
Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et
|
Schön, dass du da bist. EduForge ist deine digitale Werkstatt für Lernerfolg. Wir unterstützen Schüler und
|
||||||
dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet
|
Studierende dabei, die passenden Lernmaterialien zu finden, Wissenslücken zu schließen und Gelerntes gezielt
|
||||||
clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet,
|
zu vertiefen.
|
||||||
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.
|
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<!-- Flexbox für aktive Kategorien -->
|
<!-- Flexbox für aktive Kategorien -->
|
||||||
|
|||||||
@@ -6,14 +6,12 @@
|
|||||||
<main>
|
<main>
|
||||||
|
|
||||||
<h1>Impressum</h1>
|
<h1>Impressum</h1>
|
||||||
|
<p>Gruppe C<br />
|
||||||
<p>
|
Ammerlaender Heerstrasse, 114-118<br />
|
||||||
Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et
|
26129 Oldenburg</p>
|
||||||
dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet
|
<h2>Kontakt</h2>
|
||||||
clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet,
|
<p>Telefon: 0123456789<br />
|
||||||
consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat,
|
E-Mail: GruppeC@uni-oldenburg.de</p>
|
||||||
sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no
|
<p>Quelle: <a href="https://www.e-recht24.de/impressum-generator.html">https://www.e-recht24.de/impressum-generator.html</a></p>
|
||||||
sea takimata sanctus est Lorem ipsum dolor sit amet.
|
|
||||||
</p>
|
|
||||||
|
|
||||||
</main>
|
</main>
|
||||||
|
|||||||
+10
-2
@@ -11,8 +11,10 @@ $error = $error ?? null;
|
|||||||
|
|
||||||
<h1>Bitte anmelden</h1>
|
<h1>Bitte anmelden</h1>
|
||||||
|
|
||||||
<?php if ($error): ?>
|
<?php include_once "includes/alertMessages.php"; ?>
|
||||||
<p style="color:red;">
|
|
||||||
|
<?php if (!empty($error)): ?>
|
||||||
|
<p class="alert-message is-error">
|
||||||
<?php echo htmlspecialchars($error); ?>
|
<?php echo htmlspecialchars($error); ?>
|
||||||
</p>
|
</p>
|
||||||
<?php endif; ?>
|
<?php endif; ?>
|
||||||
@@ -41,6 +43,12 @@ $error = $error ?? null;
|
|||||||
anmelden
|
anmelden
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
|
<div class="register-link">
|
||||||
|
<a href="index.php?pfad=password-forgotten">
|
||||||
|
Passwort vergessen?
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="register-link">
|
<div class="register-link">
|
||||||
<a href="index.php?pfad=register">
|
<a href="index.php?pfad=register">
|
||||||
Noch keinen Account? Jetzt hier registrieren!
|
Noch keinen Account? Jetzt hier registrieren!
|
||||||
|
|||||||
@@ -25,16 +25,55 @@ include_once 'includes/navbar.php';
|
|||||||
|
|
||||||
<main>
|
<main>
|
||||||
|
|
||||||
<h1>Nutzungsbedingungen</h1>
|
<main>
|
||||||
|
|
||||||
<p>
|
<h1>Nutzungsbedingungen</h1>
|
||||||
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
|
<p>
|
||||||
clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet,
|
Diese Nutzungsbedingungen regeln die Nutzung der Plattform EduForge.
|
||||||
consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat,
|
Mit der Registrierung und Nutzung der Website erkennen Nutzerinnen und Nutzer
|
||||||
sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no
|
die nachfolgenden Bedingungen an.
|
||||||
sea takimata sanctus est Lorem ipsum dolor sit amet.
|
</p>
|
||||||
</p>
|
|
||||||
|
<h2>1. Nutzung der Plattform</h2>
|
||||||
|
<p>
|
||||||
|
EduForge dient dem Austausch von Lerninhalten, Beiträgen und Kommentaren.
|
||||||
|
Die Nutzung der Plattform darf ausschließlich im Rahmen der geltenden Gesetze erfolgen.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<h2>2. Verhalten der Nutzerinnen und Nutzer</h2>
|
||||||
|
<p>
|
||||||
|
Nutzerinnen und Nutzer verpflichten sich zu einem respektvollen Umgang miteinander.
|
||||||
|
Das Veröffentlichen von beleidigenden, diskriminierenden, rechtswidrigen oder
|
||||||
|
anderweitig unangemessenen Inhalten ist untersagt.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<h2>3. Veröffentlichung von Inhalten</h2>
|
||||||
|
<p>
|
||||||
|
Nutzerinnen und Nutzer sind für die von ihnen veröffentlichten Inhalte selbst verantwortlich.
|
||||||
|
Dies gilt insbesondere für Beiträge, Kommentare, Bilder und sonstige hochgeladene Dateien.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<h2>4. Urheberrechte</h2>
|
||||||
|
<p>
|
||||||
|
Es dürfen ausschließlich Inhalte veröffentlicht werden, an denen die Nutzerinnen und Nutzer
|
||||||
|
die erforderlichen Rechte besitzen. Durch das Einstellen von Inhalten versichern die
|
||||||
|
Nutzerinnen und Nutzer, keine Rechte Dritter zu verletzen.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<h2>5. Entfernung von Inhalten</h2>
|
||||||
|
<p>
|
||||||
|
Inhalte, die gegen diese Nutzungsbedingungen oder geltendes Recht verstoßen,
|
||||||
|
können vom Betreiber der Plattform entfernt werden.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<h2>6. Änderungen der Nutzungsbedingungen</h2>
|
||||||
|
<p>
|
||||||
|
Der Betreiber behält sich vor, diese Nutzungsbedingungen anzupassen, sofern dies
|
||||||
|
für den Betrieb oder die Weiterentwicklung der Plattform erforderlich ist.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
</main>
|
||||||
|
|
||||||
</main>
|
</main>
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,54 @@
|
|||||||
|
<?php
|
||||||
|
$error = $error ?? null;
|
||||||
|
$success = $success ?? null;
|
||||||
|
?>
|
||||||
|
|
||||||
|
<main class="login-page">
|
||||||
|
<div class="login-container">
|
||||||
|
|
||||||
|
<h1>Passwort vergessen</h1>
|
||||||
|
|
||||||
|
<?php if (!empty($error)): ?>
|
||||||
|
<p class="alert-message is-error">
|
||||||
|
<?php echo htmlspecialchars($error); ?>
|
||||||
|
</p>
|
||||||
|
<?php endif; ?>
|
||||||
|
|
||||||
|
<?php if (!empty($success)): ?>
|
||||||
|
<p class="alert-message is-success">
|
||||||
|
<?php echo $success; ?>
|
||||||
|
</p>
|
||||||
|
<?php endif; ?>
|
||||||
|
|
||||||
|
<form method="post" action="index.php?pfad=password-forgotten">
|
||||||
|
|
||||||
|
<p class="input-label">E-Mail-Adresse:</p>
|
||||||
|
<input type="email"
|
||||||
|
name="email"
|
||||||
|
class="login-input"
|
||||||
|
placeholder="E-Mail-Adresse"
|
||||||
|
required>
|
||||||
|
|
||||||
|
<p class="input-label">Neues Passwort:</p>
|
||||||
|
<input type="password"
|
||||||
|
name="password"
|
||||||
|
class="login-input"
|
||||||
|
placeholder="Neues Passwort"
|
||||||
|
required>
|
||||||
|
|
||||||
|
<button type="submit"
|
||||||
|
name="passwordForgottenSubmit"
|
||||||
|
class="button">
|
||||||
|
Passwort zurücksetzen
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<div class="register-link">
|
||||||
|
<a href="index.php?pfad=login">
|
||||||
|
Zurück zum Login
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</form>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</main>
|
||||||
@@ -1,5 +1,6 @@
|
|||||||
<?php
|
<?php
|
||||||
$error = $error ?? null;
|
$error = $error ?? null;
|
||||||
|
$success = $success ?? null;
|
||||||
?>
|
?>
|
||||||
|
|
||||||
<!--
|
<!--
|
||||||
@@ -12,11 +13,17 @@ $error = $error ?? null;
|
|||||||
<h1>Jetzt Registrieren!</h1>
|
<h1>Jetzt Registrieren!</h1>
|
||||||
|
|
||||||
<?php if (!empty($error)): ?>
|
<?php if (!empty($error)): ?>
|
||||||
<p class="alert-message is-error" style="color:red;">
|
<p class="alert-message is-error">
|
||||||
<?php echo htmlspecialchars($error); ?>
|
<?php echo htmlspecialchars($error); ?>
|
||||||
</p>
|
</p>
|
||||||
<?php endif; ?>
|
<?php endif; ?>
|
||||||
|
|
||||||
|
<?php if (!empty($success)): ?>
|
||||||
|
<p class="alert-message is-success">
|
||||||
|
<?php echo $success; ?>
|
||||||
|
</p>
|
||||||
|
<?php endif; ?>
|
||||||
|
|
||||||
<form method="post" action="index.php?pfad=register">
|
<form method="post" action="index.php?pfad=register">
|
||||||
|
|
||||||
<p class="input-label">Email:</p>
|
<p class="input-label">Email:</p>
|
||||||
|
|||||||
+59
-33
@@ -27,7 +27,8 @@ if ($currentPage < 1) {
|
|||||||
$offset = ($currentPage - 1) * $limit;
|
$offset = ($currentPage - 1) * $limit;
|
||||||
|
|
||||||
// Nur die Ergebnisse für die aktuelle Seite ausschneiden
|
// Nur die Ergebnisse für die aktuelle Seite ausschneiden
|
||||||
$results = array_slice($all_results, $offset, $limit);
|
//$results = array_slice($all_results, $offset, $limit);
|
||||||
|
$results = $all_results;
|
||||||
$resultCount = count($results);
|
$resultCount = count($results);
|
||||||
|
|
||||||
?>
|
?>
|
||||||
@@ -44,12 +45,12 @@ $resultCount = count($results);
|
|||||||
<!-- Links: Seitenleiste für Filter und Suche -->
|
<!-- Links: Seitenleiste für Filter und Suche -->
|
||||||
<aside class="s-res-sidebar">
|
<aside class="s-res-sidebar">
|
||||||
|
|
||||||
<!-- Sortierfuntion Box und Such Box-->
|
<!-- Sortierfuntion Box und Such Box-->
|
||||||
<form id="search-form-id" action="php/controller/search-results-controller.php" method="GET" class="s-res-sidebar-form">
|
<form action="php/controller/search-results-controller.php" method="GET" id="search-form-id" class="s-res-sidebar-form">
|
||||||
<!-- 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; ?>">
|
<input type="hidden" id="s-res-page-input" name="page" value="<?php echo $_GET['page'] ?? 1; ?>">
|
||||||
|
|
||||||
<div class="s-res-sidebar-box">
|
<div class="s-res-sidebar-box">
|
||||||
<h3 class="s-res-sidebar-title">Suche anpassen</h3>
|
<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>
|
<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>
|
<button type="submit" class="nav__search-button">Suchen</button>
|
||||||
@@ -60,25 +61,57 @@ $resultCount = count($results);
|
|||||||
<?php $currentSort = $_SESSION['search_sort'] ?? 'alphabet'; ?>
|
<?php $currentSort = $_SESSION['search_sort'] ?? 'alphabet'; ?>
|
||||||
<div class="s-res-filter-group">
|
<div class="s-res-filter-group">
|
||||||
<label class="s-res-filter-option">
|
<label class="s-res-filter-option">
|
||||||
<input type="radio" name="sort" value="alphabet" <?php echo $currentSort === 'alphabet' ? 'checked' : ''; ?> onchange="this.form.submit()">
|
<input type="radio" name="sort" value="alphabet" class="sort-radio" <?php echo $currentSort === 'alphabet' ? 'checked' : ''; ?>>
|
||||||
<span>Alphabetisch</span>
|
<span>Alphabetisch</span>
|
||||||
</label>
|
</label>
|
||||||
<!-- Noch disabled, da likes noch nicht implementiert-->
|
|
||||||
<label class="s-res-filter-option">
|
<label class="s-res-filter-option">
|
||||||
<input type="radio" name="sort" value="likes" <?php echo $currentSort === 'likes' ? 'checked' : ''; ?> onchange="this.form.submit()">
|
<input type="radio" name="sort" value="likes" class="sort-radio" <?php echo $currentSort === 'likes' ? 'checked' : ''; ?>>
|
||||||
<span>Beliebtheit (Likes)</span>
|
<span>Beliebtheit (Likes)</span>
|
||||||
</label>
|
</label>
|
||||||
<label class="s-res-filter-option">
|
<label class="s-res-filter-option">
|
||||||
<input type="radio" name="sort" value="newest" <?php echo $currentSort === 'newest' ? 'checked' : ''; ?> onchange="this.form.submit()">
|
<input type="radio" name="sort" value="newest" class="sort-radio" <?php echo $currentSort === 'newest' ? 'checked' : ''; ?>>
|
||||||
<span>Neueste Beiträge</span>
|
<span>Neueste Beiträge</span>
|
||||||
</label>
|
</label>
|
||||||
<label class="s-res-filter-option">
|
<label class="s-res-filter-option">
|
||||||
<input type="radio" name="sort" value="oldest" <?php echo $currentSort === 'oldest' ? 'checked' : ''; ?> onchange="this.form.submit()">
|
<input type="radio" name="sort" value="oldest" class="sort-radio" <?php echo $currentSort === 'oldest' ? 'checked' : ''; ?>>
|
||||||
<span>Älteste Beiträge</span>
|
<span>Älteste Beiträge</span>
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="s-res-sidebar-box">
|
||||||
|
<h3 class="s-res-sidebar-title">Kategorie filtern</h3>
|
||||||
|
<select id="category-filter" class="s-res-limit-select" style="width: 100%; padding: 8px; border-radius: 6px; border: 1px solid #cbd5e1;">
|
||||||
|
<option value="all">Alle Kategorien</option>
|
||||||
|
<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="Mathematik">Mathematik</option>
|
||||||
|
<option value="Biologie">Biologie</option>
|
||||||
|
<option value="Informatik">Informatik</option>
|
||||||
|
<option value="Chemie">Chemie</option>
|
||||||
|
<option value="Physik">Physik</option>
|
||||||
|
<option value="Astronomie">Astronomie</option>
|
||||||
|
<option value="Geschichte">Geschichte</option>
|
||||||
|
<option value="Erdkunde">Erdkunde</option>
|
||||||
|
<option value="Sozialkunde">Sozialkunde</option>
|
||||||
|
<option value="Wirtschaftskunde">Wirtschaftskunde</option>
|
||||||
|
<option value="Religion">Religion</option>
|
||||||
|
<option value="Ethikunterricht">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="Technik">Technik</option>
|
||||||
|
<option value="Werken">Werken</option>
|
||||||
|
<option value="Hauswirtschaft">Hauswirtschaft</option>
|
||||||
|
<option value="Sport">Sport</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
</aside>
|
</aside>
|
||||||
@@ -95,8 +128,11 @@ $resultCount = count($results);
|
|||||||
<?php
|
<?php
|
||||||
if (!empty($results)): ?>
|
if (!empty($results)): ?>
|
||||||
|
|
||||||
<?php foreach ($results as $item): ?>
|
<?php foreach ($results as $item):
|
||||||
<div class="s-res-item">
|
// Anzahl der Likes ermitteln (falls es ein Array ist, zählen; falls Zahl, direkt nutzen)
|
||||||
|
$likesCount = isset($item['likes']) && is_array($item['likes']) ? count($item['likes']) : ($item['likes'] ?? 0);
|
||||||
|
?>
|
||||||
|
<div class="s-res-item" data-likes="<?php echo $likesCount; ?>" data-category="<?php echo strtolower($item['category'] ?? ''); ?>">
|
||||||
<div class="s-res-content">
|
<div class="s-res-content">
|
||||||
<h2 class="s-res-item-title">
|
<h2 class="s-res-item-title">
|
||||||
<a href="index.php?pfad=showArticle&id=<?php echo $item['id']; ?>" class="s-res-link">
|
<a href="index.php?pfad=showArticle&id=<?php echo $item['id']; ?>" class="s-res-link">
|
||||||
@@ -107,10 +143,9 @@ $resultCount = count($results);
|
|||||||
<p class="s-res-author">Von: <span class="s-res-author-name"><?php echo htmlspecialchars($item['author']); ?></span></p>
|
<p class="s-res-author">Von: <span class="s-res-author-name"><?php echo htmlspecialchars($item['author']); ?></span></p>
|
||||||
|
|
||||||
<span class="s-res-likes">
|
<span class="s-res-likes">
|
||||||
❤️ <?php echo isset($item['likes']) && is_array($item['likes']) ? count($item['likes']) : 0; ?>
|
❤️ <?php echo $likesCount; ?>
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
<div class="s-res-arrow">→</div>
|
<div class="s-res-arrow">→</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -120,8 +155,8 @@ $resultCount = count($results);
|
|||||||
elseif (isset($_SESSION["search_query"]) && $_SESSION["search_query"] !== "" && $resultCount === 0): ?>
|
elseif (isset($_SESSION["search_query"]) && $_SESSION["search_query"] !== "" && $resultCount === 0): ?>
|
||||||
<p>Keine Beiträge zu diesem Suchbegriff gefunden.</p>
|
<p>Keine Beiträge zu diesem Suchbegriff gefunden.</p>
|
||||||
<?php
|
<?php
|
||||||
elseif (isset($_SESSION["message"]) && $_SESSION["message"] == "missing_parameters"): ?>
|
elseif (isset($_SESSION["message"]) && $_SESSION["message"] == "invalid_search_query"): ?>
|
||||||
<p>Bitte überprüfe deine Sucheingabe und versuche es erneut!</p>
|
<p>Unzulässige Suchanfrage</p>
|
||||||
|
|
||||||
<?php endif; ?>
|
<?php endif; ?>
|
||||||
<?php
|
<?php
|
||||||
@@ -134,7 +169,7 @@ $resultCount = count($results);
|
|||||||
<!-- Auswahl der Ergebnisse pro Seite -->
|
<!-- Auswahl der Ergebnisse pro Seite -->
|
||||||
<div class="s-res-limit-selector">
|
<div class="s-res-limit-selector">
|
||||||
<label for="s-res-per-page" class="s-res-limit-label">Ergebnisse pro Seite:</label>
|
<label for="s-res-per-page" class="s-res-limit-label">Ergebnisse pro Seite:</label>
|
||||||
<select id="s-res-per-page" name="limit" class="s-res-limit-select" form="search-form-id" onchange="this.form.submit()">
|
<select id="s-res-per-page" name="limit" class="s-res-limit-select">
|
||||||
<option value="10" <?php echo $limit === 10 ? 'selected' : ''; ?>>10</option>
|
<option value="10" <?php echo $limit === 10 ? 'selected' : ''; ?>>10</option>
|
||||||
<option value="20" <?php echo $limit === 20 ? 'selected' : ''; ?>>20</option>
|
<option value="20" <?php echo $limit === 20 ? 'selected' : ''; ?>>20</option>
|
||||||
<option value="50" <?php echo $limit === 50 ? 'selected' : ''; ?>>50</option>
|
<option value="50" <?php echo $limit === 50 ? 'selected' : ''; ?>>50</option>
|
||||||
@@ -143,23 +178,14 @@ $resultCount = count($results);
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="s-res-page-navigation">
|
<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 type="button" class="s-res-page-btn" id="prev-page-btn" data-page="0">«</button>
|
||||||
«
|
|
||||||
</button>
|
<span id="dynamic-page-numbers"></span>
|
||||||
<!-- Dynamische Seitenzahlen -->
|
|
||||||
<?php for ($i = 1; $i <= $totalPages; $i++): ?>
|
<button type="button" class="s-res-page-btn" id="next-page-btn" data-page="2">»</button>
|
||||||
<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>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</main>
|
</main>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -0,0 +1,24 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Zeigt den Inhalt einer simulierten E-Mail an.
|
||||||
|
* Die Datei wird über einen zufällig erzeugten Token geladen.
|
||||||
|
*/
|
||||||
|
|
||||||
|
$token = basename($_GET["token"] ?? "");
|
||||||
|
$file = __DIR__ . "/../data/mails/" . $token . ".html";
|
||||||
|
|
||||||
|
if (!file_exists($file)) {
|
||||||
|
echo "<p>Datei nicht gefunden.</p>";
|
||||||
|
exit();
|
||||||
|
}
|
||||||
|
|
||||||
|
?>
|
||||||
|
|
||||||
|
<main class="login-page">
|
||||||
|
<div class="login-container">
|
||||||
|
<h1>Simulierte E-Mail</h1>
|
||||||
|
|
||||||
|
<?php include $file; ?>
|
||||||
|
</div>
|
||||||
|
</main>
|
||||||
+312
-28
@@ -5,6 +5,34 @@ $repliesByParent = [];
|
|||||||
$articleObj = null;
|
$articleObj = null;
|
||||||
|
|
||||||
include_once 'php/controller/showArticle-controller.php';
|
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
|
Seite: Anzeige für Beiträge
|
||||||
@@ -17,8 +45,6 @@ include_once 'php/controller/showArticle-controller.php';
|
|||||||
<!-- Metadaten & Titel -->
|
<!-- Metadaten & Titel -->
|
||||||
<div class="article-view-top-section">
|
<div class="article-view-top-section">
|
||||||
|
|
||||||
<div class="article-view-top-section">
|
|
||||||
|
|
||||||
<div class="category-and-likes-row">
|
<div class="category-and-likes-row">
|
||||||
<?php if (isset($category) && !empty($category)): ?>
|
<?php if (isset($category) && !empty($category)): ?>
|
||||||
<span class="article-view-category"><?php echo htmlspecialchars($category); ?></span>
|
<span class="article-view-category"><?php echo htmlspecialchars($category); ?></span>
|
||||||
@@ -45,8 +71,8 @@ include_once 'php/controller/showArticle-controller.php';
|
|||||||
</h1>
|
</h1>
|
||||||
|
|
||||||
<div class="article-view-meta">
|
<div class="article-view-meta">
|
||||||
<?php if (isset($author) && !empty($author)): ?>
|
<?php if (isset($name) && !empty($name)): ?>
|
||||||
<span class="article-view-author">Von: <strong><?php echo htmlspecialchars($author); ?></strong></span>
|
<span class="article-view-author">Von: <strong><?php echo htmlspecialchars($name); ?></strong></span>
|
||||||
<?php endif; ?>
|
<?php endif; ?>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -106,39 +132,268 @@ include_once 'php/controller/showArticle-controller.php';
|
|||||||
</div>
|
</div>
|
||||||
<?php endif; ?>
|
<?php endif; ?>
|
||||||
|
|
||||||
<section class="article-comments-section">
|
<section class="article-comments-section" id="comments">
|
||||||
<h2>Kommentare</h2>
|
<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">
|
<div id="comments-list">
|
||||||
<?php if (!empty($mainComments)): ?>
|
<?php if (!empty($mainComments)): ?>
|
||||||
<?php foreach ($mainComments as $comment): ?>
|
<?php foreach ($mainComments as $comment): ?>
|
||||||
<div class="comment-item" data-comment-id="<?php echo htmlspecialchars($comment->getId()); ?>">
|
<div class="comment-item"
|
||||||
<p>
|
data-comment-id="<?php echo htmlspecialchars(
|
||||||
<strong><?php echo htmlspecialchars($comment->getAuthor()); ?></strong>
|
(string) $comment->getId()
|
||||||
<span><?php echo htmlspecialchars($comment->getCreated()); ?></span>
|
); ?>">
|
||||||
</p>
|
|
||||||
|
|
||||||
<p><?php echo nl2br(htmlspecialchars($comment->getContent())); ?></p>
|
<?php
|
||||||
|
$isDeleted = $comment->getContent()
|
||||||
|
=== "Dieser Kommentar wurde gelöscht.";
|
||||||
|
?>
|
||||||
|
|
||||||
|
<?php if ($isDeleted): ?>
|
||||||
|
|
||||||
|
<p class="deleted-comment">
|
||||||
|
Dieser Kommentar wurde gelöscht.
|
||||||
|
</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"])): ?>
|
||||||
|
|
||||||
|
<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(
|
||||||
|
(string) $comment->getId()
|
||||||
|
); ?>"
|
||||||
|
data-author="<?php echo htmlspecialchars(
|
||||||
|
$comment->getAuthor()
|
||||||
|
); ?>">
|
||||||
|
|
||||||
|
Antworten
|
||||||
|
</a>
|
||||||
|
|
||||||
|
<?php endif; ?>
|
||||||
|
|
||||||
<?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; ?>
|
<?php endif; ?>
|
||||||
|
|
||||||
<div class="comment-replies">
|
<div class="comment-replies">
|
||||||
<?php if (isset($repliesByParent[$comment->getId()])): ?>
|
<?php if (isset($repliesByParent[$comment->getId()])): ?>
|
||||||
<?php foreach ($repliesByParent[$comment->getId()] as $reply): ?>
|
<?php foreach ($repliesByParent[$comment->getId()] as $reply): ?>
|
||||||
<div class="comment-item comment-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>
|
<?php
|
||||||
|
$isReplyDeleted = $reply->getContent()
|
||||||
|
=== "Dieser Kommentar wurde gelöscht.";
|
||||||
|
?>
|
||||||
|
|
||||||
|
<?php if ($isReplyDeleted): ?>
|
||||||
|
|
||||||
|
<p class="deleted-comment">
|
||||||
|
Dieser Kommentar wurde gelöscht.
|
||||||
|
</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>
|
</div>
|
||||||
<?php endforeach; ?>
|
<?php endforeach; ?>
|
||||||
<?php endif; ?>
|
<?php endif; ?>
|
||||||
@@ -153,17 +408,46 @@ include_once 'php/controller/showArticle-controller.php';
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<?php if (isset($_SESSION["user_email"])): ?>
|
<?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"
|
<input type="hidden"
|
||||||
name="article_id"
|
name="article_id"
|
||||||
value="<?php echo htmlspecialchars($_GET["id"] ?? ""); ?>">
|
value="<?php echo htmlspecialchars(
|
||||||
|
(string) ($_GET["id"] ?? "")
|
||||||
|
); ?>">
|
||||||
|
|
||||||
<input type="hidden"
|
<input type="hidden"
|
||||||
name="parent_comment_id"
|
name="parent_comment_id"
|
||||||
id="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"
|
<textarea name="content"
|
||||||
id="comment-content"
|
id="comment-content"
|
||||||
@@ -181,4 +465,4 @@ include_once 'php/controller/showArticle-controller.php';
|
|||||||
</div>
|
</div>
|
||||||
<?php endif; ?>
|
<?php endif; ?>
|
||||||
</section>
|
</section>
|
||||||
</main>
|
</main>
|
||||||
|
|||||||
+103
-24
@@ -1,32 +1,111 @@
|
|||||||
<?php
|
<?php
|
||||||
include_once "php/controller/showCategory-controller.php";
|
include_once "php/controller/showCategory-controller.php";
|
||||||
|
|
||||||
|
$currentSort = isset($sortStyle) ? $sortStyle : 'alphabet';
|
||||||
|
$currentPage = isset($page) ? $page : 1;
|
||||||
?>
|
?>
|
||||||
|
|
||||||
<main>
|
<noscript>
|
||||||
<?php include_once "includes/alertMessages.php"?>
|
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">
|
<div class="cat-view-container">
|
||||||
<?php
|
|
||||||
if (!empty($articles)): ?>
|
<!-- LINKE SPALTE: SEITENLEISTE FÜR SORTIERUNG -->
|
||||||
<?php foreach ($articles as $article): ?>
|
<aside class="cat-sidebar">
|
||||||
<div class="s-res-item">
|
<div class="cat-sidebar-box">
|
||||||
<div class="s-res-content">
|
<h3 class="cat-sidebar-title">Sortierung</h3>
|
||||||
<h2 class="s-res-item-title">
|
<div class="cat-filter-group">
|
||||||
<a href="index.php?pfad=showArticle&id=<?php echo $article->getId(); ?>" class="s-res-link">
|
<label class="cat-filter-option">
|
||||||
<?php echo htmlspecialchars($article->getTitle()); ?>
|
<input type="radio" name="sort" value="alphabet" <?php echo ($currentSort === 'alphabet') ? 'checked' : ''; ?> onchange="this.form.submit();">
|
||||||
</a>
|
Alphabetisch
|
||||||
</h2>
|
</label>
|
||||||
<p class="s-res-author">Von: <span class="s-res-author-name"><?php echo htmlspecialchars($article->getAuthor()); ?></span></p>
|
<label class="cat-filter-option">
|
||||||
</div>
|
<input type="radio" name="sort" value="likes" <?php echo ($currentSort === 'likes') ? 'checked' : ''; ?> onchange="this.form.submit();">
|
||||||
<div class="s-res-arrow">→</div>
|
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>
|
||||||
<?php endforeach; ?>
|
</div>
|
||||||
<?php
|
</aside>
|
||||||
else: ?>
|
|
||||||
<p> Es sind noch keine Beiträge in dieser Kategorie enthalten.</p>
|
|
||||||
<?php endif; ?>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</main>
|
<!-- 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="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>
|
||||||
|
|
||||||
|
<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>
|
||||||
|
<div class="cat-view-arrow">→</div>
|
||||||
|
</div>
|
||||||
|
<?php endforeach; ?>
|
||||||
|
<?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>
|
||||||
|
|||||||
+15
-15
@@ -130,13 +130,6 @@ h1 {
|
|||||||
color: #1f2937;
|
color: #1f2937;
|
||||||
}
|
}
|
||||||
|
|
||||||
.input-label {
|
|
||||||
margin-bottom: 5px;
|
|
||||||
font-weight: bold;
|
|
||||||
width: 100%;
|
|
||||||
color: #1f2937;
|
|
||||||
}
|
|
||||||
|
|
||||||
.login-input {
|
.login-input {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
padding: 12px;
|
padding: 12px;
|
||||||
@@ -210,14 +203,6 @@ h1 {
|
|||||||
.form-container {
|
.form-container {
|
||||||
width: 90%;
|
width: 90%;
|
||||||
max-width: 600px;
|
max-width: 600px;
|
||||||
padding: 30px;
|
|
||||||
background-color: white;
|
|
||||||
border: 1px solid #dbe3ec;
|
|
||||||
border-radius: 10px;
|
|
||||||
box-shadow: 0 6px 20px rgba(0,0,0,0.1);
|
|
||||||
}
|
|
||||||
|
|
||||||
.form-container {
|
|
||||||
flex: 1 1 450px;
|
flex: 1 1 450px;
|
||||||
padding: 30px;
|
padding: 30px;
|
||||||
background-color: white;
|
background-color: white;
|
||||||
@@ -225,4 +210,19 @@ h1 {
|
|||||||
border-radius: 10px;
|
border-radius: 10px;
|
||||||
box-shadow: 0 6px 20px rgba(0,0,0,0.1);
|
box-shadow: 0 6px 20px rgba(0,0,0,0.1);
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Darstellung der Registrierungsbestätigung */
|
||||||
|
.confirm-message {
|
||||||
|
text-align: center;
|
||||||
|
margin: 20px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Anpassung des Login-Buttons auf der Bestätigungsseite */
|
||||||
|
.confirm-button {
|
||||||
|
display: block;
|
||||||
|
width: 100%;
|
||||||
|
text-align: center;
|
||||||
|
text-decoration: none;
|
||||||
|
box-sizing: border-box;
|
||||||
}
|
}
|
||||||
@@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -78,6 +78,17 @@
|
|||||||
Das Bild konnte nicht hochgeladen werden. Bitte versuche es erneut oder verwende ein anderes Bildformat.
|
Das Bild konnte nicht hochgeladen werden. Bitte versuche es erneut oder verwende ein anderes Bildformat.
|
||||||
</p>
|
</p>
|
||||||
<?php endif; ?>
|
<?php endif; ?>
|
||||||
|
<?php if (isset($_SESSION["message"]) && $_SESSION["message"] == "registration_confirmed"): ?>
|
||||||
|
<p class="alert-message is-success">
|
||||||
|
Ihre Registrierung wurde erfolgreich abgeschlossen. Sie können sich jetzt anmelden.
|
||||||
|
</p>
|
||||||
|
<?php endif; ?>
|
||||||
|
|
||||||
|
<?php if (isset($_SESSION["message"]) && $_SESSION["message"] == "password_changed"): ?>
|
||||||
|
<p class="alert-message is-success">
|
||||||
|
Ihr Passwort wurde erfolgreich geändert. Sie können sich jetzt anmelden.
|
||||||
|
</p>
|
||||||
|
<?php endif; ?>
|
||||||
<?php if (isset($_SESSION["message"]) && $_SESSION["message"] == "database_error"): ?>
|
<?php if (isset($_SESSION["message"]) && $_SESSION["message"] == "database_error"): ?>
|
||||||
<p class="alert-message is-error">
|
<p class="alert-message is-error">
|
||||||
Es ist ein Datenbankfehler aufgetreten. Bitte versuche es erneut.
|
Es ist ein Datenbankfehler aufgetreten. Bitte versuche es erneut.
|
||||||
|
|||||||
@@ -2,4 +2,5 @@
|
|||||||
<a href="index.php?pfad=impressum">Impressum</a>
|
<a href="index.php?pfad=impressum">Impressum</a>
|
||||||
<a href="index.php?pfad=datenschutz">Datenschutz</a>
|
<a href="index.php?pfad=datenschutz">Datenschutz</a>
|
||||||
<a href="index.php?pfad=nutzungsbedingungen">Nutzungsbedingungen</a>
|
<a href="index.php?pfad=nutzungsbedingungen">Nutzungsbedingungen</a>
|
||||||
|
<a href="index.php?pfad=accessibility">Barrierefreiheit</a>
|
||||||
</footer>
|
</footer>
|
||||||
@@ -18,16 +18,36 @@ if ($pfad === "login") {
|
|||||||
if ($pfad === "register") {
|
if ($pfad === "register") {
|
||||||
include_once "php/controller/register-controller.php";
|
include_once "php/controller/register-controller.php";
|
||||||
}
|
}
|
||||||
|
if ($pfad === "password-forgotten") {
|
||||||
|
include_once "php/controller/password-forgotten-controller.php";
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($pfad === "confirm-register") {
|
||||||
|
include_once "php/controller/confirm-register-controller.php";
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($pfad === "confirm-password") {
|
||||||
|
include_once "php/controller/confirm-password-controller.php";
|
||||||
|
}
|
||||||
|
|
||||||
if ($pfad === "logout") {
|
if ($pfad === "logout") {
|
||||||
include_once "php/controller/logout-controller.php";
|
include_once "php/controller/logout-controller.php";
|
||||||
exit();
|
exit();
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($pfad === "deleteAccount") {
|
if ($pfad === "deleteAccount") {
|
||||||
include_once "php/controller/deleteAccount-controller.php";
|
include_once "php/controller/deleteAccount-controller.php";
|
||||||
exit();
|
exit();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ($pfad === "updateComment") {
|
||||||
|
include_once "php/controller/updateComment-controller.php";
|
||||||
|
exit();
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($pfad === "deleteComment") {
|
||||||
|
include_once "php/controller/deleteComment-controller.php";
|
||||||
|
exit();
|
||||||
|
}
|
||||||
?>
|
?>
|
||||||
|
|
||||||
<!--
|
<!--
|
||||||
@@ -52,10 +72,13 @@ if ($pfad === "deleteAccount") {
|
|||||||
<link rel="stylesheet" href="css/profile.css">
|
<link rel="stylesheet" href="css/profile.css">
|
||||||
<link rel="stylesheet" href="css/showArticle.css">
|
<link rel="stylesheet" href="css/showArticle.css">
|
||||||
<link rel="stylesheet" href="css/message.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/paginator.js" async></script>
|
||||||
|
<script src="js/sorter.js" async></script>
|
||||||
<script src="js/comments.js" defer></script>
|
<script src="js/comments.js" defer></script>
|
||||||
<script src="js/editor.js" async></script>
|
<script src="js/editor.js" async></script>
|
||||||
|
<script src="js/filter.js" async></script>
|
||||||
|
|
||||||
<title>EduForge</title>
|
<title>EduForge</title>
|
||||||
</head>
|
</head>
|
||||||
|
|||||||
+192
-43
@@ -11,20 +11,31 @@ document.addEventListener("DOMContentLoaded", function () {
|
|||||||
const parentCommentInput = document.getElementById("parent-comment-id");
|
const parentCommentInput = document.getElementById("parent-comment-id");
|
||||||
const replyInfo = document.getElementById("reply-info");
|
const replyInfo = document.getElementById("reply-info");
|
||||||
|
|
||||||
if (!form || !commentsList || !commentContent || !parentCommentInput || !replyInfo) {
|
if (!form || !commentsList || !commentContent || !parentCommentInput) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Aktiviert einen einzelnen Antworten-Button.
|
* Aktiviert einen einzelnen Antworten-Link.
|
||||||
*
|
*
|
||||||
* @param {HTMLButtonElement} button Antworten-Button
|
* @param {HTMLAnchorElement} replyLink Antworten-Link
|
||||||
*/
|
*/
|
||||||
function registerReplyButton(button) {
|
function registerReplyButton(replyLink) {
|
||||||
button.addEventListener("click", function () {
|
replyLink.addEventListener("click", function (event) {
|
||||||
parentCommentInput.value = button.dataset.commentId;
|
/*
|
||||||
replyInfo.textContent = "Antwort auf " + button.dataset.author;
|
* Mit JavaScript wird die Seite nicht neu geladen.
|
||||||
replyInfo.style.display = "block";
|
* 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();
|
commentContent.focus();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -45,18 +56,29 @@ document.addEventListener("DOMContentLoaded", function () {
|
|||||||
const formData = new FormData(form);
|
const formData = new FormData(form);
|
||||||
const parentCommentId = parentCommentInput.value;
|
const parentCommentId = parentCommentInput.value;
|
||||||
|
|
||||||
fetch("php/ajax/add-comment.php", {
|
fetch(form.action, {
|
||||||
method: "POST",
|
method: "POST",
|
||||||
body: formData
|
body: formData,
|
||||||
|
headers: {
|
||||||
|
"X-Requested-With": "XMLHttpRequest"
|
||||||
|
}
|
||||||
})
|
})
|
||||||
.then(response => response.json())
|
.then(function (response) {
|
||||||
.then(data => {
|
if (!response.ok) {
|
||||||
|
throw new Error("Fehlerhafte Serverantwort.");
|
||||||
|
}
|
||||||
|
|
||||||
|
return response.json();
|
||||||
|
})
|
||||||
|
.then(function (data) {
|
||||||
if (!data.success) {
|
if (!data.success) {
|
||||||
alert(data.message);
|
alert(data.message);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const emptyMessage = commentsList.querySelector(".no-comments-message");
|
const emptyMessage = commentsList.querySelector(
|
||||||
|
".no-comments-message"
|
||||||
|
);
|
||||||
|
|
||||||
if (emptyMessage) {
|
if (emptyMessage) {
|
||||||
emptyMessage.remove();
|
emptyMessage.remove();
|
||||||
@@ -64,49 +86,172 @@ document.addEventListener("DOMContentLoaded", function () {
|
|||||||
|
|
||||||
const commentElement = document.createElement("div");
|
const commentElement = document.createElement("div");
|
||||||
commentElement.classList.add("comment-item");
|
commentElement.classList.add("comment-item");
|
||||||
|
commentElement.dataset.commentId = data.commentId;
|
||||||
|
|
||||||
if (parentCommentId) {
|
if (parentCommentId !== "") {
|
||||||
commentElement.classList.add("comment-reply");
|
commentElement.classList.add("comment-reply");
|
||||||
|
|
||||||
commentElement.innerHTML = `
|
commentElement.innerHTML = `
|
||||||
<p>
|
<p>
|
||||||
<strong>${escapeHtml(data.author)}</strong>
|
<strong>${escapeHtml(data.author)}</strong>
|
||||||
<span>${escapeHtml(data.created)}</span>
|
<span>${escapeHtml(data.created)}</span>
|
||||||
</p>
|
</p>
|
||||||
<p>${escapeHtml(data.content).replace(/\n/g, "<br>")}</p>
|
|
||||||
`;
|
|
||||||
|
|
||||||
const parentReplies = document.querySelector(
|
<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 = commentsList.querySelector(
|
||||||
`.comment-item[data-comment-id="${parentCommentId}"] .comment-replies`
|
`.comment-item[data-comment-id="${parentCommentId}"] .comment-replies`
|
||||||
);
|
);
|
||||||
|
|
||||||
if (parentReplies) {
|
if (parentReplies) {
|
||||||
parentReplies.appendChild(commentElement);
|
parentReplies.appendChild(commentElement);
|
||||||
|
} else {
|
||||||
|
commentsList.prepend(commentElement);
|
||||||
}
|
}
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
commentElement.dataset.commentId = data.commentId;
|
|
||||||
|
|
||||||
commentElement.innerHTML = `
|
commentElement.innerHTML = `
|
||||||
<p>
|
<p>
|
||||||
<strong>${escapeHtml(data.author)}</strong>
|
<strong>${escapeHtml(data.author)}</strong>
|
||||||
<span>${escapeHtml(data.created)}</span>
|
<span>${escapeHtml(data.created)}</span>
|
||||||
</p>
|
</p>
|
||||||
<p>${escapeHtml(data.content).replace(/\n/g, "<br>")}</p>
|
|
||||||
|
|
||||||
<button type="button"
|
<p>${escapeHtml(data.content).replace(/\n/g, "<br>")}</p>
|
||||||
class="reply-button"
|
|
||||||
data-comment-id="${escapeHtml(data.commentId)}"
|
|
||||||
data-author="${escapeHtml(data.author)}">
|
|
||||||
Antworten
|
|
||||||
</button>
|
|
||||||
|
|
||||||
<div class="comment-replies"></div>
|
<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)}"
|
||||||
|
>
|
||||||
|
Antworten
|
||||||
|
</a>
|
||||||
|
|
||||||
|
<div class="comment-replies"></div>
|
||||||
|
`;
|
||||||
|
|
||||||
commentsList.prepend(commentElement);
|
commentsList.prepend(commentElement);
|
||||||
|
|
||||||
const newReplyButton = commentElement.querySelector(".reply-button");
|
const newReplyButton =
|
||||||
|
commentElement.querySelector(".reply-button");
|
||||||
|
|
||||||
if (newReplyButton) {
|
if (newReplyButton) {
|
||||||
registerReplyButton(newReplyButton);
|
registerReplyButton(newReplyButton);
|
||||||
@@ -115,10 +260,14 @@ document.addEventListener("DOMContentLoaded", function () {
|
|||||||
|
|
||||||
commentContent.value = "";
|
commentContent.value = "";
|
||||||
parentCommentInput.value = "";
|
parentCommentInput.value = "";
|
||||||
replyInfo.textContent = "";
|
|
||||||
replyInfo.style.display = "none";
|
if (replyInfo) {
|
||||||
|
replyInfo.textContent = "";
|
||||||
|
replyInfo.style.display = "none";
|
||||||
|
}
|
||||||
})
|
})
|
||||||
.catch(() => {
|
.catch(function (error) {
|
||||||
|
console.error(error);
|
||||||
alert("Kommentar konnte nicht gesendet werden.");
|
alert("Kommentar konnte nicht gesendet werden.");
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -126,12 +275,12 @@ document.addEventListener("DOMContentLoaded", function () {
|
|||||||
/**
|
/**
|
||||||
* Entfernt HTML-Sonderzeichen aus Nutzereingaben.
|
* Entfernt HTML-Sonderzeichen aus Nutzereingaben.
|
||||||
*
|
*
|
||||||
* @param {string} text Zu bereinigender Text
|
* @param {*} text Zu bereinigender Text
|
||||||
* @returns {string} Sicherer Text
|
* @returns {string} Sicherer Text
|
||||||
*/
|
*/
|
||||||
function escapeHtml(text) {
|
function escapeHtml(text) {
|
||||||
const div = document.createElement("div");
|
const div = document.createElement("div");
|
||||||
div.textContent = text;
|
div.textContent = String(text ?? "");
|
||||||
return div.innerHTML;
|
return div.innerHTML;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
+116
@@ -0,0 +1,116 @@
|
|||||||
|
let currentClientPage = 1;
|
||||||
|
let itemsPerPage = 10;
|
||||||
|
|
||||||
|
function initFilter() {
|
||||||
|
const filterSelect = document.getElementById('category-filter');
|
||||||
|
const listContainer = document.querySelector('.s-res-list');
|
||||||
|
const limitSelect = document.getElementById('s-res-per-page');
|
||||||
|
|
||||||
|
if (!filterSelect || !listContainer) return;
|
||||||
|
|
||||||
|
if (limitSelect) {
|
||||||
|
itemsPerPage = parseInt(limitSelect.value, 10) || 10;
|
||||||
|
}
|
||||||
|
|
||||||
|
updateVisibility();
|
||||||
|
|
||||||
|
if (limitSelect) {
|
||||||
|
limitSelect.addEventListener('change', function() {
|
||||||
|
|
||||||
|
//neues limit einlesen
|
||||||
|
itemsPerPage = parseInt(this.value, 10);
|
||||||
|
currentClientPage = 1;
|
||||||
|
updateVisibility();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
filterSelect.addEventListener('change', function() {
|
||||||
|
currentClientPage = 1;
|
||||||
|
updateVisibility();
|
||||||
|
});
|
||||||
|
|
||||||
|
const navigationContainer = document.querySelector('.s-res-page-navigation');
|
||||||
|
if (navigationContainer) {
|
||||||
|
navigationContainer.addEventListener('click', function(e) {
|
||||||
|
const button = e.target.closest('.s-res-page-btn');
|
||||||
|
if (!button || button.disabled) return;
|
||||||
|
|
||||||
|
e.preventDefault();
|
||||||
|
|
||||||
|
const targetPage = button.getAttribute('data-page');
|
||||||
|
if (targetPage) {
|
||||||
|
currentClientPage = parseInt(targetPage, 10);
|
||||||
|
updateVisibility();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateVisibility() {
|
||||||
|
const filterSelect = document.getElementById('category-filter');
|
||||||
|
const listContainer = document.querySelector('.s-res-list');
|
||||||
|
const selectedCategory = filterSelect.value.toLowerCase().trim();
|
||||||
|
const cards = listContainer.querySelectorAll('.s-res-item');
|
||||||
|
|
||||||
|
let visibleCards = [];
|
||||||
|
cards.forEach(card => {
|
||||||
|
const cardCategory = (card.getAttribute('data-category') || '').toLowerCase().trim();
|
||||||
|
if (selectedCategory === 'all' || cardCategory.includes(selectedCategory) || selectedCategory.includes(cardCategory)) {
|
||||||
|
visibleCards.push(card);
|
||||||
|
} else {
|
||||||
|
card.style.display = 'none';
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const totalVisible = visibleCards.length;
|
||||||
|
const totalPages = Math.max(1, Math.ceil(totalVisible / itemsPerPage));
|
||||||
|
|
||||||
|
if (currentClientPage < 1) currentClientPage = 1;
|
||||||
|
if (currentClientPage > totalPages) currentClientPage = totalPages;
|
||||||
|
|
||||||
|
const startOffset = (currentClientPage - 1) * itemsPerPage;
|
||||||
|
const endOffset = startOffset + itemsPerPage;
|
||||||
|
|
||||||
|
visibleCards.forEach((card, index) => {
|
||||||
|
if (index >= startOffset && index < endOffset) {
|
||||||
|
card.style.display = 'flex';
|
||||||
|
} else {
|
||||||
|
card.style.display = 'none';
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
updatePaginatorUI(currentClientPage, totalPages);
|
||||||
|
}
|
||||||
|
|
||||||
|
function updatePaginatorUI(currentPage, totalPages) {
|
||||||
|
const prevBtn = document.getElementById('prev-page-btn');
|
||||||
|
const nextBtn = document.getElementById('next-page-btn');
|
||||||
|
const numbersContainer = document.getElementById('dynamic-page-numbers');
|
||||||
|
|
||||||
|
if (!prevBtn || !nextBtn || !numbersContainer) return;
|
||||||
|
|
||||||
|
prevBtn.setAttribute('data-page', currentPage - 1);
|
||||||
|
prevBtn.disabled = (currentPage <= 1);
|
||||||
|
|
||||||
|
nextBtn.setAttribute('data-page', currentPage + 1);
|
||||||
|
nextBtn.disabled = (currentPage >= totalPages);
|
||||||
|
|
||||||
|
let buttonsHTML = '';
|
||||||
|
for (let i = 1; i <= totalPages; i++) {
|
||||||
|
const activeClass = (i === currentPage) ? 's-res-page-btn-active' : '';
|
||||||
|
buttonsHTML += `<button type="button" class="s-res-page-btn ${activeClass}" data-page="${i}">${i}</button> `;
|
||||||
|
}
|
||||||
|
numbersContainer.innerHTML = buttonsHTML;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Hilfsfunktion für Math.ceil in JS
|
||||||
|
function ceil(val) { return Math.ceil(val); }
|
||||||
|
|
||||||
|
// ist das DOM bereits vollständig aufgebaut?
|
||||||
|
if (document.readyState === 'loading') {
|
||||||
|
// Falls noch geladen wird, auf das Event warten
|
||||||
|
document.addEventListener('DOMContentLoaded', initFilter);
|
||||||
|
} else {
|
||||||
|
// Falls das HTML bereits komplett da ist, sofort ausführen
|
||||||
|
initFilter();
|
||||||
|
}
|
||||||
@@ -0,0 +1,50 @@
|
|||||||
|
|
||||||
|
function initSorter() {
|
||||||
|
const listContainer = document.querySelector('.s-res-list');
|
||||||
|
const sortRadios = document.querySelectorAll('.sort-radio');
|
||||||
|
|
||||||
|
// wenn keine liste vorhanden, abbrechen
|
||||||
|
if (!listContainer || sortRadios.length === 0) return;
|
||||||
|
|
||||||
|
sortRadios.forEach(radio => {
|
||||||
|
radio.addEventListener('change', function() {
|
||||||
|
const cards = Array.from(listContainer.querySelectorAll('.s-res-item'));
|
||||||
|
const sortValue = this.value;
|
||||||
|
|
||||||
|
cards.sort((a, b) => {
|
||||||
|
if (sortValue === 'likes') {
|
||||||
|
const likesA = parseInt(a.getAttribute('data-likes') || '0', 10);
|
||||||
|
const likesB = parseInt(b.getAttribute('data-likes') || '0', 10);
|
||||||
|
return likesB - likesA;
|
||||||
|
}
|
||||||
|
else if (sortValue === 'alphabet') {
|
||||||
|
// alphabetische sortierung
|
||||||
|
const titleA = a.querySelector('.s-res-link').textContent.trim().toLowerCase();
|
||||||
|
const titleB = b.querySelector('.s-res-link').textContent.trim().toLowerCase();
|
||||||
|
return titleA.localeCompare(titleB);
|
||||||
|
}
|
||||||
|
else if (sortValue === 'newest' || sortValue === 'oldest') {
|
||||||
|
// hoehere ID wird als neuer gesehen
|
||||||
|
const urlA = a.querySelector('.s-res-link').getAttribute('href');
|
||||||
|
const urlB = b.querySelector('.s-res-link').getAttribute('href');
|
||||||
|
|
||||||
|
const idA = parseInt(urlA.match(/id=(\d+)/)[1], 10);
|
||||||
|
const idB = parseInt(urlB.match(/id=(\d+)/)[1], 10);
|
||||||
|
|
||||||
|
return sortValue === 'newest' ? idB - idA : idA - idB;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
});
|
||||||
|
|
||||||
|
listContainer.innerHTML = '';
|
||||||
|
cards.forEach(card => listContainer.appendChild(card));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// ist das DOM bereits vollständig aufgebaut?
|
||||||
|
if (document.readyState === 'loading') {
|
||||||
|
document.addEventListener('DOMContentLoaded', initSorter);
|
||||||
|
} else {
|
||||||
|
initSorter();
|
||||||
|
}
|
||||||
+156
-30
@@ -3,32 +3,153 @@ if (session_status() === PHP_SESSION_NONE) {
|
|||||||
session_start();
|
session_start();
|
||||||
}
|
}
|
||||||
|
|
||||||
header("Content-Type: application/json");
|
|
||||||
|
|
||||||
require_once "../model/CommentManager.php";
|
require_once "../model/CommentManager.php";
|
||||||
|
|
||||||
if (!isset($_SESSION["user_email"])) {
|
/**
|
||||||
echo json_encode([
|
* Prüft, ob die Anfrage durch JavaScript per AJAX gesendet wurde.
|
||||||
"success" => false,
|
*/
|
||||||
"message" => "Du musst angemeldet sein, um zu kommentieren."
|
$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();
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 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 ($articleId !== null) {
|
||||||
|
header(
|
||||||
|
"Location: ../../index.php?pfad=showArticle&id="
|
||||||
|
. urlencode((string) $articleId)
|
||||||
|
. "#comments"
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
header("Location: ../../index.php");
|
||||||
|
}
|
||||||
|
|
||||||
exit();
|
exit();
|
||||||
}
|
}
|
||||||
|
|
||||||
$articleId = $_POST["article_id"] ?? null;
|
/*
|
||||||
$content = trim($_POST["content"] ?? "");
|
* Nur POST-Anfragen dürfen Kommentare erstellen.
|
||||||
$parentCommentId = $_POST["parent_comment_id"] ?? null;
|
*/
|
||||||
|
if ($_SERVER["REQUEST_METHOD"] !== "POST") {
|
||||||
|
sendCommentResponse(
|
||||||
|
false,
|
||||||
|
"Ungültige Anfrage.",
|
||||||
|
null
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
if ($parentCommentId === "" || $parentCommentId === "0") {
|
/*
|
||||||
|
* 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;
|
$parentCommentId = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (empty($articleId) || empty($content)) {
|
if ($articleId === false || $articleId === null) {
|
||||||
echo json_encode([
|
sendCommentResponse(
|
||||||
"success" => false,
|
false,
|
||||||
"message" => "Kommentar darf nicht leer sein."
|
"Der zugehörige Beitrag ist ungültig.",
|
||||||
]);
|
null
|
||||||
exit();
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
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 {
|
try {
|
||||||
@@ -41,18 +162,23 @@ try {
|
|||||||
$parentCommentId
|
$parentCommentId
|
||||||
);
|
);
|
||||||
|
|
||||||
echo json_encode([
|
sendCommentResponse(
|
||||||
"success" => true,
|
true,
|
||||||
"commentId" => $commentId,
|
"Der Kommentar wurde erfolgreich gespeichert.",
|
||||||
"author" => $_SESSION["user_email"],
|
$articleId,
|
||||||
"content" => $content,
|
[
|
||||||
"created" => date("Y-m-d H:i:s"),
|
"commentId" => $commentId,
|
||||||
"parentCommentId" => $parentCommentId
|
"author" => $_SESSION["user_email"],
|
||||||
]);
|
"content" => $content,
|
||||||
|
"created" => date("Y-m-d H:i:s"),
|
||||||
|
"parentCommentId" => $parentCommentId
|
||||||
|
]
|
||||||
|
);
|
||||||
|
|
||||||
} catch (Exception $e) {
|
} catch (Throwable $e) {
|
||||||
echo json_encode([
|
sendCommentResponse(
|
||||||
"success" => false,
|
false,
|
||||||
"message" => "Kommentar konnte nicht gespeichert werden."
|
"Der Kommentar konnte nicht gespeichert werden.",
|
||||||
]);
|
$articleId
|
||||||
|
);
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,65 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
require_once "php/model/UserManager.php";
|
||||||
|
|
||||||
|
/*
|
||||||
|
Verarbeitung der Passwortbestätigung
|
||||||
|
Funktion: Liest die zwischengespeicherten Passwortdaten aus,
|
||||||
|
aktualisiert das Passwort des Benutzers und gibt das Ergebnis
|
||||||
|
für die View zurück.
|
||||||
|
*/
|
||||||
|
|
||||||
|
$title = "Passwort zurücksetzen";
|
||||||
|
$message = "";
|
||||||
|
$link = "";
|
||||||
|
$isSuccess = false;
|
||||||
|
|
||||||
|
$token = basename($_GET["token"] ?? "");
|
||||||
|
$file = "data/pending-password/" . $token . ".json";
|
||||||
|
|
||||||
|
if (empty($token) || !file_exists($file)) {
|
||||||
|
|
||||||
|
$message = "Der Bestätigungslink ist ungültig oder bereits abgelaufen.";
|
||||||
|
|
||||||
|
} else {
|
||||||
|
|
||||||
|
$data = json_decode(file_get_contents($file), true);
|
||||||
|
|
||||||
|
if ($data === null || empty($data["email"]) || empty($data["password"])) {
|
||||||
|
|
||||||
|
$message = "Die Daten zur Passwortänderung konnten nicht gelesen werden.";
|
||||||
|
|
||||||
|
} else {
|
||||||
|
|
||||||
|
try {
|
||||||
|
|
||||||
|
$dao = UserManager::getInstance();
|
||||||
|
$user = $dao->findUser($data["email"]);
|
||||||
|
|
||||||
|
if ($user !== null) {
|
||||||
|
|
||||||
|
$dao->updateUser(
|
||||||
|
$user["email"],
|
||||||
|
$user["email"],
|
||||||
|
$user["vorname"],
|
||||||
|
$user["nachname"],
|
||||||
|
$data["password"]
|
||||||
|
);
|
||||||
|
|
||||||
|
$_SESSION["message"] = "password_changed";
|
||||||
|
header("Location: index.php?pfad=login");
|
||||||
|
exit();
|
||||||
|
|
||||||
|
} else {
|
||||||
|
|
||||||
|
$message = "Der Benutzer konnte nicht gefunden werden.";
|
||||||
|
}
|
||||||
|
|
||||||
|
unlink($file);
|
||||||
|
|
||||||
|
} catch (Exception $e) {
|
||||||
|
|
||||||
|
$message = "Das Passwort konnte nicht geändert werden.";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,64 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
require_once "php/model/UserManager.php";
|
||||||
|
|
||||||
|
/*
|
||||||
|
Verarbeitung der Registrierungsbestätigung
|
||||||
|
Funktion: Liest die zwischengespeicherten Registrierungsdaten aus,
|
||||||
|
legt den Benutzer an und gibt das Ergebnis für die View zurück.
|
||||||
|
*/
|
||||||
|
|
||||||
|
$title = "Registrierung";
|
||||||
|
$message = "";
|
||||||
|
$link = "";
|
||||||
|
$isSuccess = false;
|
||||||
|
|
||||||
|
$token = basename($_GET["token"] ?? "");
|
||||||
|
$file = "data/pending/" . $token . ".json";
|
||||||
|
|
||||||
|
if (empty($token) || !file_exists($file)) {
|
||||||
|
|
||||||
|
$message = "Der Registrierungslink ist ungültig oder bereits abgelaufen.";
|
||||||
|
|
||||||
|
} else {
|
||||||
|
|
||||||
|
$data = json_decode(file_get_contents($file), true);
|
||||||
|
|
||||||
|
if (
|
||||||
|
$data === null ||
|
||||||
|
empty($data["email"]) ||
|
||||||
|
empty($data["vorname"]) ||
|
||||||
|
empty($data["nachname"]) ||
|
||||||
|
empty($data["password"])
|
||||||
|
) {
|
||||||
|
|
||||||
|
$message = "Die Registrierungsdaten konnten nicht gelesen werden.";
|
||||||
|
|
||||||
|
} else {
|
||||||
|
|
||||||
|
try {
|
||||||
|
|
||||||
|
$dao = UserManager::getInstance();
|
||||||
|
|
||||||
|
if ($dao->findUser($data["email"]) === null) {
|
||||||
|
|
||||||
|
$dao->addUser(
|
||||||
|
$data["email"],
|
||||||
|
$data["vorname"],
|
||||||
|
$data["nachname"],
|
||||||
|
$data["password"]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
unlink($file);
|
||||||
|
|
||||||
|
$_SESSION["message"] = "registration_confirmed";
|
||||||
|
header("Location: index.php?pfad=login");
|
||||||
|
exit();
|
||||||
|
|
||||||
|
} catch (Exception $e) {
|
||||||
|
|
||||||
|
$message = "Die Registrierung konnte nicht abgeschlossen werden.";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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();
|
||||||
@@ -0,0 +1,76 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
require_once "php/model/UserManager.php";
|
||||||
|
require_once "php/validator/user-validator.php";
|
||||||
|
|
||||||
|
$error = null;
|
||||||
|
$success = null;
|
||||||
|
|
||||||
|
if ($_SERVER["REQUEST_METHOD"] === "POST") {
|
||||||
|
|
||||||
|
$email = trim($_POST["email"] ?? "");
|
||||||
|
$plainPassword = $_POST["password"] ?? "";
|
||||||
|
|
||||||
|
if (!userEmailValidator($email)) {
|
||||||
|
$error = "Bitte gib eine gültige E-Mail-Adresse ein.";
|
||||||
|
} elseif (!userPasswordValidator($plainPassword)) {
|
||||||
|
$error = "Das Passwort muss 5 bis 12 Zeichen lang sein.";
|
||||||
|
} else {
|
||||||
|
try {
|
||||||
|
$dao = UserManager::getInstance();
|
||||||
|
|
||||||
|
$token = bin2hex(random_bytes(16));
|
||||||
|
$existingUser = $dao->findUser($email);
|
||||||
|
|
||||||
|
if (!is_dir("data/mails") && !mkdir("data/mails", 0777, true)) {
|
||||||
|
throw new RuntimeException("Ordner data/mails konnte nicht erstellt werden.");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!is_dir("data/pending-password") && !mkdir("data/pending-password", 0777, true)) {
|
||||||
|
throw new RuntimeException("Ordner data/pending-password konnte nicht erstellt werden.");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!is_writable("data/mails") || !is_writable("data/pending-password")) {
|
||||||
|
throw new RuntimeException("Ordner sind nicht beschreibbar.");
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($existingUser !== null) {
|
||||||
|
$pendingData = [
|
||||||
|
"email" => $email,
|
||||||
|
"password" => $plainPassword
|
||||||
|
];
|
||||||
|
|
||||||
|
file_put_contents(
|
||||||
|
"data/pending-password/" . $token . ".json",
|
||||||
|
json_encode($pendingData, JSON_PRETTY_PRINT)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($existingUser !== null) {
|
||||||
|
$mailContent = "
|
||||||
|
<h2>Passwort zurücksetzen</h2>
|
||||||
|
<p>Falls Sie diese Anfrage nicht gestellt haben, können Sie diese Nachricht ignorieren.</p>
|
||||||
|
<p>
|
||||||
|
<a href='index.php?pfad=confirm-password&token=$token'>
|
||||||
|
Passwortänderung bestätigen
|
||||||
|
</a>
|
||||||
|
</p>
|
||||||
|
";
|
||||||
|
} else {
|
||||||
|
$mailContent = "
|
||||||
|
<h2>Passwort zurücksetzen</h2>
|
||||||
|
<p>Falls Sie diese Anfrage nicht gestellt haben, können Sie diese Nachricht ignorieren.</p>
|
||||||
|
<p>Für diese E-Mail-Adresse wurde kein Konto gefunden.</p>
|
||||||
|
";
|
||||||
|
}
|
||||||
|
|
||||||
|
file_put_contents("data/mails/" . $token . ".html", $mailContent);
|
||||||
|
|
||||||
|
$success = 'Weitere Infos finden Sie in der Datei
|
||||||
|
<a href="index.php?pfad=show-mail&token=' . htmlspecialchars($token) . '" target="_blank">xy</a>.';
|
||||||
|
|
||||||
|
} catch (Exception $e) {
|
||||||
|
$error = "Die Passwortänderung konnte nicht verarbeitet werden.";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -4,6 +4,7 @@ require_once "php/model/UserManager.php";
|
|||||||
require_once "php/validator/user-validator.php";
|
require_once "php/validator/user-validator.php";
|
||||||
|
|
||||||
$error = null;
|
$error = null;
|
||||||
|
$success = null;
|
||||||
|
|
||||||
if ($_SERVER["REQUEST_METHOD"] === "POST") {
|
if ($_SERVER["REQUEST_METHOD"] === "POST") {
|
||||||
|
|
||||||
@@ -24,20 +25,71 @@ if ($_SERVER["REQUEST_METHOD"] === "POST") {
|
|||||||
try {
|
try {
|
||||||
$dao = UserManager::getInstance();
|
$dao = UserManager::getInstance();
|
||||||
|
|
||||||
$password = password_hash($plainPassword, PASSWORD_DEFAULT);
|
// Token für die simulierte E-Mail und die spätere Bestätigung erzeugen.
|
||||||
|
$token = bin2hex(random_bytes(16));
|
||||||
|
$existingUser = $dao->findUser($email);
|
||||||
|
|
||||||
$dao->addUser($email, $vorname, $nachname, $password);
|
if (!is_dir("data/mails") && !mkdir("data/mails", 0777, true)) {
|
||||||
|
throw new RuntimeException("Ordner data/mails konnte nicht erstellt werden.");
|
||||||
|
}
|
||||||
|
|
||||||
$_SESSION["user"] = $vorname . " " . $nachname;
|
if (!is_dir("data/pending") && !mkdir("data/pending", 0777, true)) {
|
||||||
$_SESSION["user_email"] = $email;
|
throw new RuntimeException("Ordner data/pending konnte nicht erstellt werden.");
|
||||||
|
}
|
||||||
|
|
||||||
header("Location: index.php");
|
if (!is_writable("data/mails")) {
|
||||||
exit();
|
throw new RuntimeException("Ordner data/mails ist nicht beschreibbar.");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!is_writable("data/pending")) {
|
||||||
|
throw new RuntimeException("Ordner data/pending ist nicht beschreibbar.");
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($existingUser === null) {
|
||||||
|
$password = password_hash($plainPassword, PASSWORD_DEFAULT);
|
||||||
|
|
||||||
|
$pendingData = [
|
||||||
|
"email" => $email,
|
||||||
|
"vorname" => $vorname,
|
||||||
|
"nachname" => $nachname,
|
||||||
|
"password" => $password
|
||||||
|
];
|
||||||
|
|
||||||
|
file_put_contents(
|
||||||
|
"data/pending/" . $token . ".json",
|
||||||
|
json_encode($pendingData, JSON_PRETTY_PRINT)
|
||||||
|
);
|
||||||
|
|
||||||
|
$mailContent = "
|
||||||
|
<h2>Registrierung bestätigen</h2>
|
||||||
|
<p>Bitte ignorieren Sie diese Nachricht, wenn Sie sich nicht registrieren wollten.</p>
|
||||||
|
<p>
|
||||||
|
<a href='index.php?pfad=confirm-register&token=$token'>
|
||||||
|
Registrierung bestätigen
|
||||||
|
</a>
|
||||||
|
</p>
|
||||||
|
";
|
||||||
|
} else {
|
||||||
|
$mailContent = "
|
||||||
|
<h2>Registrierung</h2>
|
||||||
|
<p>Bitte ignorieren Sie diese Nachricht, wenn Sie sich nicht registrieren wollten.</p>
|
||||||
|
<p>Sie sind bereits registriert.</p>
|
||||||
|
<p>
|
||||||
|
<a href='index.php?pfad=password-forgotten'>
|
||||||
|
Passwort vergessen
|
||||||
|
</a>
|
||||||
|
</p>
|
||||||
|
";
|
||||||
|
}
|
||||||
|
|
||||||
|
file_put_contents("data/mails/" . $token . ".html", $mailContent);
|
||||||
|
|
||||||
|
// Neutrale Meldung, damit nicht sichtbar wird, ob die E-Mail bereits registriert ist.
|
||||||
|
$success = 'Weitere Infos finden Sie in der Datei
|
||||||
|
<a href="index.php?pfad=show-mail&token=' . htmlspecialchars($token) . '" target="_blank">xy</a>.';
|
||||||
|
|
||||||
} catch (InvalidArgumentException $e) {
|
|
||||||
$error = $e->getMessage();
|
|
||||||
} catch (Exception $e) {
|
} catch (Exception $e) {
|
||||||
$error = "Die Registrierung konnte nicht gespeichert werden.";
|
$error = "Die Registrierung konnte nicht verarbeitet werden.";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -2,21 +2,23 @@
|
|||||||
if (session_status() === PHP_SESSION_NONE) {
|
if (session_status() === PHP_SESSION_NONE) {
|
||||||
session_start();
|
session_start();
|
||||||
}
|
}
|
||||||
require_once '../model/LocalArticleManager.php';
|
|
||||||
require_once '../model/ArticleManager.php';
|
require_once '../model/ArticleManager.php';
|
||||||
|
require_once '../model/UserManager.php';
|
||||||
require_once '../model/Article.php';
|
require_once '../model/Article.php';
|
||||||
|
require_once '../validator/search-validator.php';
|
||||||
|
|
||||||
if ($_SERVER["REQUEST_METHOD"] === "GET" && isset($_GET["q"])) {
|
if ($_SERVER["REQUEST_METHOD"] === "GET" && isset($_GET["q"])) {
|
||||||
|
|
||||||
$search = trim($_GET["q"]);
|
$search = trim($_GET["q"]);
|
||||||
if (empty($search)) {
|
if (!searchQueryValidator($search)) {
|
||||||
$_SESSION["search_results"] = [];
|
$_SESSION["search_results"] = [];
|
||||||
$_SESSION["search_query"] = "";
|
$_SESSION["search_query"] = "";
|
||||||
$_SESSION["message"] = "missing_parameters";
|
$_SESSION["message"] = "invalid_search_query";
|
||||||
} else {
|
} else {
|
||||||
try {
|
try {
|
||||||
|
|
||||||
$articleManager = ArticleManager::getInstance();
|
$articleManager = ArticleManager::getInstance();
|
||||||
|
$userManager = UserManager::getInstance();
|
||||||
|
|
||||||
$results = $articleManager->search($search);
|
$results = $articleManager->search($search);
|
||||||
|
|
||||||
@@ -51,7 +53,7 @@ if ($_SERVER["REQUEST_METHOD"] === "GET" && isset($_GET["q"])) {
|
|||||||
"id" => $obj->getId(),
|
"id" => $obj->getId(),
|
||||||
"title" => $obj->getTitle(),
|
"title" => $obj->getTitle(),
|
||||||
"content" => $obj->getContent(),
|
"content" => $obj->getContent(),
|
||||||
"author" => $obj->getAuthor(),
|
"author" => $userManager->findUser($obj->getAuthor())["vorname"] . " " . $userManager->findUser($obj->getAuthor())["nachname"],
|
||||||
"category" => $obj->getCategory(),
|
"category" => $obj->getCategory(),
|
||||||
"tags" => $obj->getTags(),
|
"tags" => $obj->getTags(),
|
||||||
"creationDate" => $obj->getCreationDate(),
|
"creationDate" => $obj->getCreationDate(),
|
||||||
@@ -70,6 +72,9 @@ if ($_SERVER["REQUEST_METHOD"] === "GET" && isset($_GET["q"])) {
|
|||||||
|
|
||||||
$sort = $_GET['sort'] ?? 'alphabet';
|
$sort = $_GET['sort'] ?? 'alphabet';
|
||||||
$limit = isset($_GET['limit']) ? (int)$_GET['limit'] : 10;
|
$limit = isset($_GET['limit']) ? (int)$_GET['limit'] : 10;
|
||||||
|
if (!searchLimitValidator($limit)) {
|
||||||
|
$limit = 10;
|
||||||
|
}
|
||||||
$page = isset($_GET['page']) ? (int)$_GET['page'] : 1;
|
$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);
|
header("Location: ../../index.php?pfad=search-results&q=" . urlencode($search) . "&sort=" . urlencode($sort) . "&limit=" . $limit . "&page=" . $page);
|
||||||
exit();
|
exit();
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ if (session_status() === PHP_SESSION_NONE) {
|
|||||||
|
|
||||||
require_once 'php/model/Article.php';
|
require_once 'php/model/Article.php';
|
||||||
require_once 'php/model/ArticleManager.php';
|
require_once 'php/model/ArticleManager.php';
|
||||||
|
require_once 'php/model/UserManager.php';
|
||||||
require_once 'php/model/CommentManager.php';
|
require_once 'php/model/CommentManager.php';
|
||||||
|
|
||||||
if (isset($_GET["id"]) && !empty($_GET["id"])){
|
if (isset($_GET["id"]) && !empty($_GET["id"])){
|
||||||
@@ -19,12 +20,23 @@ if (isset($_GET["id"]) && !empty($_GET["id"])){
|
|||||||
$author = $article->getAuthor();
|
$author = $article->getAuthor();
|
||||||
$tags = $article->getTags();
|
$tags = $article->getTags();
|
||||||
$articleObj = $article; // Objekt für die Like-Abfagen sichern
|
$articleObj = $article; // Objekt für die Like-Abfagen sichern
|
||||||
|
|
||||||
|
$userManager = UserManager::getInstance();
|
||||||
|
$name = $userManager->findUser($author)["vorname"]." ".$userManager->findUser($author)["nachname"];
|
||||||
}else{
|
}else{
|
||||||
//header("location: index.php?pfad=404");
|
//header("location: index.php?pfad=404");
|
||||||
include_once "content/404.php";
|
include_once "content/404.php";
|
||||||
exit();
|
exit();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if($_GET["pfad"] == "updateArticle"){
|
||||||
|
if($article->getAuthor() != $_SESSION["user_email"]){
|
||||||
|
$_SESSION["message"] = "unauthorized_access";
|
||||||
|
header("location: index.php");
|
||||||
|
exit();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
$commentManager = CommentManager::getInstance();
|
$commentManager = CommentManager::getInstance();
|
||||||
$comments = $commentManager->getCommentsByArticle($_GET["id"]);
|
$comments = $commentManager->getCommentsByArticle($_GET["id"]);
|
||||||
|
|
||||||
@@ -37,8 +49,9 @@ if (isset($_GET["id"]) && !empty($_GET["id"])){
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
} catch (Exception $e){
|
} catch (Throwable $e) {
|
||||||
$_SESSION["message"] = "internal_error";
|
$_SESSION["message"] = "internal_error";
|
||||||
|
header("Location: index.php");
|
||||||
exit();
|
exit();
|
||||||
}
|
}
|
||||||
}else{
|
}else{
|
||||||
|
|||||||
@@ -1,19 +1,65 @@
|
|||||||
<?php
|
<?php
|
||||||
|
if (session_status() === PHP_SESSION_NONE) {
|
||||||
|
session_start();
|
||||||
|
}
|
||||||
|
|
||||||
require_once 'php/model/Article.php';
|
require_once 'php/model/Article.php';
|
||||||
require_once 'php/model/ArticleManager.php';
|
require_once 'php/model/ArticleManager.php';
|
||||||
|
require_once 'php/model/UserManager.php';
|
||||||
require_once 'php/validator/article-validator.php';
|
require_once 'php/validator/article-validator.php';
|
||||||
|
|
||||||
if (isset($_GET["category"]) && !empty($_GET["category"]) && articleCategoryValidator($_GET["category"])){
|
if (isset($_GET["category"]) && !empty($_GET["category"]) && articleCategoryValidator($_GET["category"])){
|
||||||
$category = $_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 {
|
try {
|
||||||
$articleManager = ArticleManager::getInstance();
|
$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) {
|
} catch (Exception $e) {
|
||||||
$_SESSION["message"] = "internal_error";
|
$_SESSION["message"] = "internal_error";
|
||||||
include_once "content/404.php";
|
include_once "content/404.php";
|
||||||
exit();
|
exit();
|
||||||
}
|
}
|
||||||
}else{
|
} else {
|
||||||
$_SESSION["message"] = "invalid_category";
|
$_SESSION["message"] = "invalid_category";
|
||||||
include_once "content/404.php";
|
include_once "content/404.php";
|
||||||
exit();
|
exit();
|
||||||
|
|||||||
@@ -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;
|
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.
|
* Gibt das Veröffentlichungsdatum des Beitrags zurück.
|
||||||
* @return string
|
* @return string
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ class Comment
|
|||||||
private int $id;
|
private int $id;
|
||||||
private int $articleId;
|
private int $articleId;
|
||||||
private ?int $parentCommentId;
|
private ?int $parentCommentId;
|
||||||
private string $author;
|
private ?string $author;
|
||||||
private string $content;
|
private string $content;
|
||||||
private string $created;
|
private string $created;
|
||||||
|
|
||||||
@@ -23,7 +23,7 @@ class Comment
|
|||||||
* @param int $id Eindeutige ID des Kommentars
|
* @param int $id Eindeutige ID des Kommentars
|
||||||
* @param int $articleId ID des zugehörigen Beitrags
|
* @param int $articleId ID des zugehörigen Beitrags
|
||||||
* @param int|null $parentCommentId ID des Eltern-Kommentars oder null
|
* @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 $content Inhalt des Kommentars
|
||||||
* @param string $created Erstellungsdatum des Kommentars
|
* @param string $created Erstellungsdatum des Kommentars
|
||||||
*/
|
*/
|
||||||
@@ -31,7 +31,7 @@ class Comment
|
|||||||
int $id,
|
int $id,
|
||||||
int $articleId,
|
int $articleId,
|
||||||
?int $parentCommentId,
|
?int $parentCommentId,
|
||||||
string $author,
|
?string $author,
|
||||||
string $content,
|
string $content,
|
||||||
string $created
|
string $created
|
||||||
) {
|
) {
|
||||||
@@ -86,9 +86,9 @@ class Comment
|
|||||||
/**
|
/**
|
||||||
* Gibt den Autor des Kommentars zurück.
|
* 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;
|
return $this->author;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -51,4 +51,40 @@ interface CommentManagerDAO
|
|||||||
* @return Comment[] Liste der Kommentare
|
* @return Comment[] Liste der Kommentare
|
||||||
*/
|
*/
|
||||||
public function getCommentsByAuthor($author);
|
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;
|
||||||
}
|
}
|
||||||
@@ -314,7 +314,8 @@ class DatabaseArticleManager implements ArticleManagerDAO {
|
|||||||
$sql = "SELECT id, title, content, author, category, tags, created
|
$sql = "SELECT id, title, content, author, category, tags, created
|
||||||
FROM articles
|
FROM articles
|
||||||
WHERE title LIKE :keyword
|
WHERE title LIKE :keyword
|
||||||
OR content LIKE :keyword;";
|
OR content LIKE :keyword
|
||||||
|
OR tags LIKE :keyword";
|
||||||
|
|
||||||
$command = $db->prepare($sql);
|
$command = $db->prepare($sql);
|
||||||
if (!$command) {
|
if (!$command) {
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ class DatabaseCommentManager implements CommentManagerDAO
|
|||||||
/**
|
/**
|
||||||
* Erstellt die Kommentartabelle, falls diese noch nicht existiert.
|
* Erstellt die Kommentartabelle, falls diese noch nicht existiert.
|
||||||
*/
|
*/
|
||||||
public function __construct()
|
private function __construct()
|
||||||
{
|
{
|
||||||
$this->dbPath = __DIR__ . '/../../db/eduforgeDB.db';
|
$this->dbPath = __DIR__ . '/../../db/eduforgeDB.db';
|
||||||
DatabaseInitializer::initialize($this->dbPath);
|
DatabaseInitializer::initialize($this->dbPath);
|
||||||
@@ -223,4 +223,194 @@ class DatabaseCommentManager implements CommentManagerDAO
|
|||||||
|
|
||||||
return $comments;
|
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");
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -46,22 +46,24 @@ class DatabaseInitializer {
|
|||||||
|
|
||||||
$db->exec("
|
$db->exec("
|
||||||
CREATE TABLE IF NOT EXISTS comments (
|
CREATE TABLE IF NOT EXISTS comments (
|
||||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||||
article_id INTEGER NOT NULL,
|
article_id INTEGER NOT NULL,
|
||||||
parent_comment_id INTEGER NULL,
|
parent_comment_id INTEGER NULL,
|
||||||
author TEXT NOT NULL,
|
author TEXT NULL,
|
||||||
content TEXT NOT NULL,
|
content TEXT NOT NULL,
|
||||||
created TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
created TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||||
FOREIGN KEY (article_id) REFERENCES articles(id) ON DELETE CASCADE,
|
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
|
FOREIGN KEY (parent_comment_id) REFERENCES comments(id) ON DELETE CASCADE
|
||||||
);
|
);
|
||||||
");
|
");
|
||||||
|
|
||||||
$initializer = new self();
|
$initializer = new self();
|
||||||
$availableEmails = $initializer->seedDummyUsers($db);
|
$availableEmails = $initializer->seedDummyUsers($db);
|
||||||
$initializer->seedDummyArticles($db, $availableEmails);
|
$initializer->seedDummyArticles($db, $availableEmails);
|
||||||
|
|
||||||
|
unset($db);
|
||||||
|
|
||||||
} catch (PDOException $e) {
|
} catch (PDOException $e) {
|
||||||
throw new RuntimeException("internal-error");
|
throw new RuntimeException("internal-error");
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -211,9 +211,11 @@ class LocalArticleManager implements ArticleManagerDAO {
|
|||||||
foreach ($articles as $article) {
|
foreach ($articles as $article) {
|
||||||
$title = isset($article['title']) ? strtolower((string)$article['title']) : '';
|
$title = isset($article['title']) ? strtolower((string)$article['title']) : '';
|
||||||
$content = isset($article['content']) ? strtolower((string)$article['content']) : '';
|
$content = isset($article['content']) ? strtolower((string)$article['content']) : '';
|
||||||
|
$tags = isset($article['tags']) ? strtolower((string)$article['tags']) : '';
|
||||||
|
|
||||||
if (($cleanKeyword !== '' && strpos($title, $cleanKeyword) !== false) ||
|
if (($cleanKeyword !== '' && strpos($title, $cleanKeyword) !== false) ||
|
||||||
($cleanKeyword !== '' && strpos($content, $cleanKeyword) !== false)) {
|
($cleanKeyword !== '' && strpos($content, $cleanKeyword) !== false) ||
|
||||||
|
($cleanKeyword !== '' && strpos($tags, $cleanKeyword) !== false)) {
|
||||||
|
|
||||||
$likes = isset($article['likes']) && is_array($article['likes']) ? $article['likes'] : [];
|
$likes = isset($article['likes']) && is_array($article['likes']) ? $article['likes'] : [];
|
||||||
$filteredArticles[] = new Article(
|
$filteredArticles[] = new Article(
|
||||||
|
|||||||
@@ -0,0 +1,40 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Prüft, ob eine Suchanfrage gültig ist.
|
||||||
|
*
|
||||||
|
* Erlaubt werden Buchstaben, Zahlen, Umlaute, typische Satzzeichen und Leerzeichen.
|
||||||
|
* Die Länge muss zwischen 1 und 50 Zeichen liegen.
|
||||||
|
*
|
||||||
|
* @param string $query Zu prüfender Suchbegriff
|
||||||
|
*
|
||||||
|
* @return bool true wenn die Suche gültig ist, sonst false
|
||||||
|
*/
|
||||||
|
function searchQueryValidator($query)
|
||||||
|
{
|
||||||
|
$query = trim($query);
|
||||||
|
|
||||||
|
// Mindestens 1 Zeichen, maximal 50 Zeichen
|
||||||
|
$length = mb_strlen($query);
|
||||||
|
if ($length < 1 || $length > 50) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Erlaubt Buchstaben (inkl. Umlaut/ß), Zahlen, Leerzeichen sowie ?, !, ., -, _
|
||||||
|
$searchPattern = '/^[a-zA-Z0-9äöüÄÖÜß\s?!.,\-_]+$/u';
|
||||||
|
|
||||||
|
return preg_match($searchPattern, $query) === 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Prüft, ob das gewählte Treffer-Limit erlaubt ist.
|
||||||
|
*
|
||||||
|
* @param int|string $limit Das zu prüfende Limit
|
||||||
|
*
|
||||||
|
* @return bool true wenn das Limit 10, 20, 50 oder 100 ist, sonst false
|
||||||
|
*/
|
||||||
|
function searchLimitValidator($limit)
|
||||||
|
{
|
||||||
|
$allowedLimits = [10, 20, 50, 100];
|
||||||
|
return in_array((int)$limit, $allowedLimits, true);
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user