Compare commits
122 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| e1529da4b0 | |||
| 986b85bed9 | |||
| 39d90c9fe0 | |||
| db62cee09b | |||
| b8d32de25f | |||
| 470d98940a | |||
| cab41ade7a | |||
| cf76f86f09 | |||
| bf33afb15b | |||
| 193c523de6 | |||
| 36c47bb7a6 | |||
| 6031fc0185 | |||
| e77c60d8c8 | |||
| 7f5cea8c1e | |||
| 71c213ef1e | |||
| 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 | |||
| 6aec353d50 | |||
| 7a4f2d738f | |||
| a0a469a9df | |||
| 54209929fe | |||
| 5321534d71 | |||
| 7cf542c270 | |||
| c45d23a6b3 | |||
| ab094429b7 | |||
| cc5f46c00e | |||
| 1b2ab1cccb | |||
| adbf488e01 | |||
| 15ea5ddbfb | |||
| b93ca321a2 | |||
| 97847c76c4 | |||
| 3dae5ee720 | |||
| cbb662d159 | |||
| 6c1a893f80 | |||
| 788e803e84 | |||
| 4d0f1de01c | |||
| e71ee8300e | |||
| f39885da10 | |||
| b7ca5bcfe2 | |||
| 57b0782577 | |||
| 4f524db221 | |||
| c2b7462307 | |||
| cdbf81878b | |||
| 68ec6dedd7 | |||
| 12782463d7 | |||
| 21a935edf3 | |||
| 3d65f79d2c | |||
| ed31210e3a | |||
| 5030caa029 | |||
| 4a87152531 | |||
| 1401c3afe7 | |||
| 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.24374.151">
|
<component name="dataSourceStorageLocal" created-in="IU-261.25134.95">
|
||||||
<data-source name="articles" uuid="315cb5c9-2b0f-435b-b602-59823b160908">
|
<data-source name="articles" uuid="315cb5c9-2b0f-435b-b602-59823b160908">
|
||||||
<database-info product="SQLite" version="3.51.1" jdbc-version="4.2" driver-name="SQLite JDBC" driver-version="3.51.1.0" dbms="SQLITE" exact-version="3.51.1" exact-driver-version="3.51">
|
<database-info product="SQLite" version="3.51.1" jdbc-version="4.2" driver-name="SQLite JDBC" driver-version="3.51.1.0" dbms="SQLITE" exact-version="3.51.1" exact-driver-version="3.51">
|
||||||
<identifier-quote-string>"</identifier-quote-string>
|
<identifier-quote-string>"</identifier-quote-string>
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
+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>
|
||||||
@@ -155,6 +155,35 @@ Daten – von ihrer Speicherung abgesehen – nur mit Ihrer Einwilligung
|
|||||||
Ausübung oder Verteidigung von Rechtsansprüchen oder zum Schutz der Rechte einer anderen
|
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
|
natürlichen oder juristischen Person oder aus Gründen eines wichtigen öffentlichen Interesses der
|
||||||
Europäischen Union oder eines Mitgliedstaats verarbeitet werden.</p>
|
Europäischen Union oder eines Mitgliedstaats verarbeitet werden.</p>
|
||||||
|
|
||||||
|
<h4>Cookies</h4>
|
||||||
|
<p>
|
||||||
|
Unsere Internetseiten verwenden so genannte „Cookies“. Cookies sind kleine Datenpakete und richten auf
|
||||||
|
Ihrem Endgerät keinen Schaden an. Sie werden entweder vorübergehend für die Dauer einer Sitzung
|
||||||
|
(Session-Cookies) oder dauerhaft (permanente Cookies) auf Ihrem Endgerät gespeichert. Session-Cookies
|
||||||
|
werden nach Ende Ihres Besuchs automatisch gelöscht. Permanente Cookies bleiben auf Ihrem Endgerät
|
||||||
|
gespeichert, bis Sie diese selbst löschen oder eine automatische Löschung durch Ihren Webbrowser erfolgt.
|
||||||
|
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>
|
<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 -->
|
||||||
|
|||||||
+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>
|
||||||
|
|||||||
+139
-72
@@ -4,18 +4,48 @@ if (session_status() === PHP_SESSION_NONE) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
$all_results = $_SESSION["search_results"] ?? [];
|
$all_results = $_SESSION["search_results"] ?? [];
|
||||||
$query = $_SESSION["search_query"] ?? "";
|
$query = $_SESSION["search_query"] ?? "";
|
||||||
$totalResultsCount = count($all_results);
|
|
||||||
|
// ---------------------------------------------------------------
|
||||||
|
// Parameter aus der URL lesen & validieren
|
||||||
|
// ---------------------------------------------------------------
|
||||||
|
$allowedCategories = [
|
||||||
|
'all', 'Deutsch', 'Englisch', 'Franzoesisch', 'Latein', 'Literatur',
|
||||||
|
'Mathematik', 'Biologie', 'Informatik', 'Chemie', 'Physik', 'Astronomie',
|
||||||
|
'Geschichte', 'Erdkunde', 'Sozialkunde', 'Wirtschaftskunde', 'Religion',
|
||||||
|
'Ethikunterricht', 'Philosophie', 'Psychologie', 'Kunst', 'Musik',
|
||||||
|
'Theater', 'Technik', 'Werken', 'Hauswirtschaft', 'Sport',
|
||||||
|
];
|
||||||
|
|
||||||
|
$category = $_GET['category'] ?? 'all';
|
||||||
|
if (!in_array($category, $allowedCategories, true)) {
|
||||||
|
$category = 'all';
|
||||||
|
}
|
||||||
|
|
||||||
$limit = isset($_GET['limit']) ? (int)$_GET['limit'] : 10;
|
$limit = isset($_GET['limit']) ? (int)$_GET['limit'] : 10;
|
||||||
if (!in_array($limit, [10, 20, 50, 100])) {
|
if (!in_array($limit, [10, 20, 50, 100], true)) {
|
||||||
$limit = 10;
|
$limit = 10;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Gesamtseitenzahl
|
$currentSort = $_SESSION['search_sort'] ?? 'alphabet';
|
||||||
$totalPages = max(1, ceil($totalResultsCount / $limit));
|
|
||||||
|
// ---------------------------------------------------------------
|
||||||
|
// Diese Berechnung ist der Server-seitige Fallback für Nutzer ohne
|
||||||
|
// JavaScript. Sobald JS aktiv ist, übernimmt js/search-results.js
|
||||||
|
// Filterung, Sortierung und Pagination komplett im Browser (auf
|
||||||
|
// Basis des unten eingebetteten JSON-Datensatzes) und überschreibt
|
||||||
|
// diese Ausgabe live, ohne dass eine neue Seite geladen wird.
|
||||||
|
// ---------------------------------------------------------------
|
||||||
|
$filteredResults = $all_results;
|
||||||
|
if ($category !== 'all') {
|
||||||
|
$filteredResults = array_values(array_filter($filteredResults, function ($item) use ($category) {
|
||||||
|
return ($item['category'] ?? '') === $category;
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
$totalResultsCount = count($filteredResults);
|
||||||
|
$totalPages = max(1, (int)ceil($totalResultsCount / $limit));
|
||||||
|
|
||||||
// Aktuelle Seite auslesen und validieren
|
|
||||||
$currentPage = isset($_GET['page']) ? (int)$_GET['page'] : 1;
|
$currentPage = isset($_GET['page']) ? (int)$_GET['page'] : 1;
|
||||||
if ($currentPage < 1) {
|
if ($currentPage < 1) {
|
||||||
$currentPage = 1;
|
$currentPage = 1;
|
||||||
@@ -23,80 +53,123 @@ if ($currentPage < 1) {
|
|||||||
$currentPage = $totalPages;
|
$currentPage = $totalPages;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Startpunkt im Array berechnen (Offset)
|
$offset = ($currentPage - 1) * $limit;
|
||||||
$offset = ($currentPage - 1) * $limit;
|
$results = array_slice($filteredResults, $offset, $limit);
|
||||||
|
|
||||||
// Nur die Ergebnisse für die aktuelle Seite ausschneiden
|
|
||||||
$results = array_slice($all_results, $offset, $limit);
|
|
||||||
$resultCount = count($results);
|
$resultCount = count($results);
|
||||||
|
|
||||||
|
// Hilfsfunktion, um Pagination-Links zu bauen, die alle aktuellen
|
||||||
|
// Parameter (q, sort, limit, category) beibehalten.
|
||||||
|
function buildPageUrl(int $page, string $query, string $sort, int $limit, string $category): string
|
||||||
|
{
|
||||||
|
$params = [
|
||||||
|
'pfad' => 'search-results',
|
||||||
|
'q' => $query,
|
||||||
|
'sort' => $sort,
|
||||||
|
'limit' => $limit,
|
||||||
|
'category' => $category,
|
||||||
|
'page' => $page,
|
||||||
|
];
|
||||||
|
return 'index.php?' . http_build_query($params);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------
|
||||||
|
// JSON-Datensatz für die clientseitige Aufbereitung (Filter,
|
||||||
|
// Sortierung, Pagination) durch js/search-results.js. Enthält ALLE
|
||||||
|
// Treffer der Suche (unabhängig von Kategorie/Sortierung/Seite),
|
||||||
|
// damit im Browser ohne weitere Serveranfragen gearbeitet werden kann.
|
||||||
|
// ---------------------------------------------------------------
|
||||||
|
$jsonItems = array_map(function ($item) {
|
||||||
|
$likes = isset($item['likes']) && is_array($item['likes']) ? count($item['likes']) : ($item['likes'] ?? 0);
|
||||||
|
return [
|
||||||
|
'id' => $item['id'] ?? null,
|
||||||
|
'title' => $item['title'] ?? '',
|
||||||
|
'author' => $item['author'] ?? '',
|
||||||
|
'category' => $item['category'] ?? '',
|
||||||
|
'likes' => $likes,
|
||||||
|
'creationDate'=> $item['creationDate'] ?? '',
|
||||||
|
];
|
||||||
|
}, $all_results);
|
||||||
?>
|
?>
|
||||||
<noscript>
|
<noscript>
|
||||||
Bitte JavaScript aktivieren!
|
Bitte JavaScript aktivieren, um die volle Funktionalität (Live-Filterung ohne Neuladen) zu nutzen. Die Seite funktioniert auch ohne JavaScript – Sortierung, Filterung und Seitenwechsel erfolgen dann über die Formular- bzw. Link-Auswahl.
|
||||||
</noscript>
|
</noscript>
|
||||||
<!--
|
<!--
|
||||||
Seite: Suchergebnisse
|
Seite: Suchergebnisse
|
||||||
Inhalt: Zeigt die Ergebnisse einer Suche an
|
Inhalt: Zeigt die Ergebnisse einer Suche an
|
||||||
-->
|
-->
|
||||||
<div class="s-res-layout-grid">
|
<div class="s-res-layout-grid">
|
||||||
<?php include_once "includes/alertMessages.php"?>
|
<?php include_once "includes/alertMessages.php" ?>
|
||||||
|
|
||||||
<!-- 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 -->
|
||||||
|
<form action="php/controller/search-results-controller.php" method="GET" id="searchform-id" class="s-res-sidebar-form">
|
||||||
|
|
||||||
<!-- Sortierfuntion Box und Such Box-->
|
<input type="hidden" id="s-res-page-input" name="page" value="<?php echo (int)$currentPage; ?>">
|
||||||
<form id="search-form-id" action="php/controller/search-results-controller.php" method="GET" 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; ?>">
|
|
||||||
|
|
||||||
<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>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="s-res-sidebar-box">
|
<div class="s-res-sidebar-box">
|
||||||
<h3 class="s-res-sidebar-title">Sortierung</h3>
|
<h3 class="s-res-sidebar-title">Sortierung</h3>
|
||||||
<?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>
|
||||||
|
|
||||||
</form>
|
<div class="s-res-sidebar-box">
|
||||||
|
<h3 class="s-res-sidebar-title">Kategorie filtern</h3>
|
||||||
|
<select id="category-filter" name="category" class="s-res-limit-select" style="width: 100%; padding: 8px; border-radius: 6px; border: 1px solid #cbd5e1;">
|
||||||
|
<?php foreach ($allowedCategories as $catValue):
|
||||||
|
$label = $catValue === 'all' ? 'Alle Kategorien' : ($catValue === 'Franzoesisch' ? 'Französisch' : $catValue);
|
||||||
|
?>
|
||||||
|
<option value="<?php echo htmlspecialchars($catValue); ?>" <?php echo $category === $catValue ? 'selected' : ''; ?>>
|
||||||
|
<?php echo htmlspecialchars($label); ?>
|
||||||
|
</option>
|
||||||
|
<?php endforeach; ?>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Für Nutzer ohne JavaScript: expliziter Button, um Sortierung/Kategorie anzuwenden -->
|
||||||
|
<noscript>
|
||||||
|
<button type="submit" class="nav__search-button">Filter anwenden</button>
|
||||||
|
</noscript>
|
||||||
|
</form>
|
||||||
</aside>
|
</aside>
|
||||||
|
|
||||||
<main class="s-res-main-content">
|
<main class="s-res-main-content">
|
||||||
|
|
||||||
<div class="s-res-header">
|
<div class="s-res-header">
|
||||||
<h1 class="s-res-main-title">Suchergebnisse</h1>
|
<h1 class="s-res-main-title">Suchergebnisse</h1>
|
||||||
<p class="s-res-meta"><?php echo $totalResultsCount; ?> Treffer für Ihre Suchanfrage "<?php echo htmlspecialchars($query); ?>"</p>
|
<p class="s-res-meta" data-query="<?php echo htmlspecialchars($query); ?>">
|
||||||
|
<?php echo $totalResultsCount; ?> Treffer für Ihre Suchanfrage "<?php echo htmlspecialchars($query); ?>"
|
||||||
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Ergebnisliste -->
|
<!-- Ergebnisliste: serverseitig gerendert (No-JS-Fallback). Wird bei aktivem JS durch js/search-results.js live ersetzt. -->
|
||||||
<div class="s-res-list">
|
<div class="s-res-list">
|
||||||
<?php
|
<?php if (!empty($results)): ?>
|
||||||
if (!empty($results)): ?>
|
<?php foreach ($results as $item):
|
||||||
|
$likesCount = isset($item['likes']) && is_array($item['likes']) ? count($item['likes']) : ($item['likes'] ?? 0);
|
||||||
<?php foreach ($results as $item): ?>
|
?>
|
||||||
<div class="s-res-item">
|
<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">
|
||||||
@@ -105,36 +178,25 @@ $resultCount = count($results);
|
|||||||
</h2>
|
</h2>
|
||||||
<div class="s-res-meta-row">
|
<div class="s-res-meta-row">
|
||||||
<p class="s-res-author">Von: <span class="s-res-author-name"><?php echo htmlspecialchars($item['author']); ?></span></p>
|
<p class="s-res-author">Von: <span class="s-res-author-name"><?php echo htmlspecialchars($item['author']); ?></span></p>
|
||||||
|
<span class="s-res-likes"> ❤️ <?php echo $likesCount; ?></span>
|
||||||
<span class="s-res-likes">
|
|
||||||
❤️ <?php echo isset($item['likes']) && is_array($item['likes']) ? count($item['likes']) : 0; ?>
|
|
||||||
</span>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
<div class="s-res-arrow">→</div>
|
<div class="s-res-arrow">→</div>
|
||||||
</div>
|
</div>
|
||||||
<?php endforeach; ?>
|
<?php endforeach; ?>
|
||||||
|
<?php elseif (isset($_SESSION["search_query"]) && $_SESSION["search_query"] !== "" && $resultCount === 0): ?>
|
||||||
<?php
|
<p>Keine Beiträge zu diesem Suchbegriff gefunden.</p>
|
||||||
elseif (isset($_SESSION["search_query"]) && $_SESSION["search_query"] !== "" && $resultCount === 0): ?>
|
<?php elseif (isset($_SESSION["message"]) && $_SESSION["message"] == "invalid_search_query"): ?>
|
||||||
<p>Keine Beiträge zu diesem Suchbegriff gefunden.</p>
|
<p>Unzulässige Suchanfrage</p>
|
||||||
<?php
|
|
||||||
elseif (isset($_SESSION["message"]) && $_SESSION["message"] == "missing_parameters"): ?>
|
|
||||||
<p>Bitte überprüfe deine Sucheingabe und versuche es erneut!</p>
|
|
||||||
|
|
||||||
<?php endif; ?>
|
<?php endif; ?>
|
||||||
<?php
|
<?php unset($_SESSION["message"]); ?>
|
||||||
unset($_SESSION["message"]);
|
|
||||||
?>
|
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
<div class="s-res-pagination-footer">
|
|
||||||
|
|
||||||
|
<div class="s-res-pagination-footer">
|
||||||
<!-- 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" form="searchform-id" 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>
|
||||||
@@ -142,24 +204,29 @@ $resultCount = count($results);
|
|||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- No-JS-Fallback: echte Links, die serverseitig neu paginieren -->
|
||||||
<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' : ''; ?>>
|
<?php if ($currentPage > 1): ?>
|
||||||
«
|
<a class="s-res-page-btn" href="<?php echo htmlspecialchars(buildPageUrl($currentPage - 1, $query, $currentSort, $limit, $category)); ?>">«</a>
|
||||||
</button>
|
<?php else: ?>
|
||||||
<!-- Dynamische Seitenzahlen -->
|
<span class="s-res-page-btn" aria-disabled="true">«</span>
|
||||||
<?php for ($i = 1; $i <= $totalPages; $i++): ?>
|
<?php endif; ?>
|
||||||
<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>
|
|
||||||
|
|
||||||
|
<span id="dynamic-page-numbers">Seite <?php echo $currentPage; ?> von <?php echo $totalPages; ?></span>
|
||||||
|
|
||||||
|
<?php if ($currentPage < $totalPages): ?>
|
||||||
|
<a class="s-res-page-btn" href="<?php echo htmlspecialchars(buildPageUrl($currentPage + 1, $query, $currentSort, $limit, $category)); ?>">»</a>
|
||||||
|
<?php else: ?>
|
||||||
|
<span class="s-res-page-btn" aria-disabled="true">»</span>
|
||||||
|
<?php endif; ?>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</main>
|
</main>
|
||||||
|
</div>
|
||||||
|
|
||||||
</div>
|
<!-- Vollständiger, ungefilterter Ergebnis-Datensatz für die clientseitige
|
||||||
|
Filterung/Sortierung/Pagination (siehe js/search-results.js).
|
||||||
|
JSON_HEX_* verhindert das Ausbrechen aus dem <script>-Tag bzw. XSS. -->
|
||||||
|
<script type="application/json" id="s-res-data">
|
||||||
|
<?php echo json_encode($jsonItems, JSON_HEX_TAG | JSON_HEX_APOS | JSON_HEX_QUOT | JSON_HEX_AMP); ?>
|
||||||
|
</script>
|
||||||
@@ -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>
|
||||||
@@ -45,8 +45,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>
|
||||||
|
|
||||||
|
|||||||
+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,22 @@
|
|||||||
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"): ?>
|
||||||
|
<p class="alert-message is-error">
|
||||||
|
Es ist ein Datenbankfehler aufgetreten. Bitte versuche es erneut.
|
||||||
|
</p>
|
||||||
|
<?php endif; ?>
|
||||||
<?php
|
<?php
|
||||||
unset($_SESSION["message"]);
|
unset($_SESSION["message"]);
|
||||||
?>
|
?>
|
||||||
|
|||||||
@@ -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>
|
||||||
@@ -3,6 +3,7 @@ if (session_status() === PHP_SESSION_NONE) {
|
|||||||
session_start();
|
session_start();
|
||||||
}
|
}
|
||||||
ob_start();
|
ob_start();
|
||||||
|
include_once("php/controller/index.php");
|
||||||
|
|
||||||
$pfad = $_GET["pfad"] ?? "home";
|
$pfad = $_GET["pfad"] ?? "home";
|
||||||
|
|
||||||
@@ -17,6 +18,17 @@ 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";
|
||||||
@@ -51,10 +63,14 @@ 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>-->
|
||||||
|
<script src="js/search-results.js" async></script>
|
||||||
|
|
||||||
<title>EduForge</title>
|
<title>EduForge</title>
|
||||||
</head>
|
</head>
|
||||||
|
|||||||
+3
-3
@@ -1,13 +1,13 @@
|
|||||||
console.log("Die JavaScript-Datei wurde erfolgreich geladen!");
|
console.log("editor.js wurde erfolgreich geladen!");
|
||||||
|
|
||||||
function initEditor() {
|
function initEditor() {
|
||||||
const form = document.getElementById("editor-form");
|
const form = document.getElementById("editor-form");
|
||||||
|
|
||||||
if (!form) {
|
if (!form) {
|
||||||
console.error("Skript abgebrochen: Formular nicht gefunden!");
|
console.error("editor.js wurde abgebrochen: Formular nicht gefunden!");
|
||||||
return;
|
return;
|
||||||
} else {
|
} else {
|
||||||
console.log("Formular gefunden und Editor initialisiert:", form);
|
console.log("Formular gefunden und editor.js initialisiert:", form);
|
||||||
}
|
}
|
||||||
|
|
||||||
const container = document.getElementById("block-container");
|
const container = document.getElementById("block-container");
|
||||||
|
|||||||
+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();
|
||||||
|
}
|
||||||
+198
-22
@@ -1,27 +1,203 @@
|
|||||||
function initPaginator() {
|
/**
|
||||||
const form = document.getElementById('search-form-id');
|
* Übernimmt Filterung (Kategorie), Sortierung und Pagination der
|
||||||
const pageInput = document.getElementById('s-res-page-input');
|
* Suchergebnisse vollständig clientseitig, ohne Neuladen der Seite.
|
||||||
const pageButtons = document.querySelectorAll('.s-res-page-navigation .s-res-page-btn');
|
*
|
||||||
|
* Voraussetzung: Im HTML liegt ein <script type="application/json" id="s-res-data">
|
||||||
|
* mit allen Treffern der aktuellen Suche (siehe content/search-results.php).
|
||||||
|
*
|
||||||
|
* Ist kein JavaScript aktiv, bleibt die serverseitig gerenderte Ausgabe
|
||||||
|
* (inkl. echter Pagination-Links und Formular-Submit) unverändert nutzbar.
|
||||||
|
*/
|
||||||
|
(function () {
|
||||||
|
const DEFAULT_PER_PAGE = 10;
|
||||||
|
|
||||||
pageButtons.forEach(button => {
|
const state = {
|
||||||
button.addEventListener('click', function() {
|
all: [],
|
||||||
if (this.disabled) return;
|
filtered: [],
|
||||||
|
category: 'all',
|
||||||
|
sort: 'alphabet',
|
||||||
|
perPage: DEFAULT_PER_PAGE,
|
||||||
|
page: 1,
|
||||||
|
};
|
||||||
|
|
||||||
const targetPage = this.getAttribute('data-page');
|
const els = {};
|
||||||
|
|
||||||
if (targetPage && form && pageInput) {
|
function init() {
|
||||||
pageInput.value = targetPage;
|
const dataScript = document.getElementById('s-res-data');
|
||||||
form.submit();
|
const listContainer = document.querySelector('.s-res-list');
|
||||||
}
|
if (!dataScript || !listContainer) {
|
||||||
|
return; // keine Ergebnisliste auf dieser Seite vorhanden
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
state.all = JSON.parse(dataScript.textContent);
|
||||||
|
} catch (e) {
|
||||||
|
console.error('Suchergebnis-Daten konnten nicht gelesen werden.', e);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
els.list = listContainer;
|
||||||
|
els.categorySelect = document.getElementById('category-filter');
|
||||||
|
els.perPageSelect = document.getElementById('s-res-per-page');
|
||||||
|
els.sortRadios = document.querySelectorAll('.sort-radio');
|
||||||
|
els.pagerContainer = document.querySelector('.s-res-page-navigation');
|
||||||
|
els.metaCount = document.querySelector('.s-res-meta');
|
||||||
|
|
||||||
|
// Ausgangszustand aus dem serverseitig gerenderten Markup übernehmen,
|
||||||
|
// damit Browser-zurück / geteilte Links konsistent bleiben.
|
||||||
|
if (els.categorySelect) state.category = els.categorySelect.value || 'all';
|
||||||
|
if (els.perPageSelect) state.perPage = parseInt(els.perPageSelect.value, 10) || DEFAULT_PER_PAGE;
|
||||||
|
const checkedSort = document.querySelector('.sort-radio:checked');
|
||||||
|
if (checkedSort) state.sort = checkedSort.value;
|
||||||
|
|
||||||
|
// Ab hier übernimmt JS: Formular-Steuerelemente lösen keinen
|
||||||
|
// Seiten-Reload mehr aus, sondern rendern direkt neu.
|
||||||
|
if (els.categorySelect) {
|
||||||
|
els.categorySelect.addEventListener('change', function () {
|
||||||
|
state.category = this.value;
|
||||||
|
state.page = 1;
|
||||||
|
render();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (els.perPageSelect) {
|
||||||
|
els.perPageSelect.addEventListener('change', function () {
|
||||||
|
state.perPage = parseInt(this.value, 10) || DEFAULT_PER_PAGE;
|
||||||
|
state.page = 1;
|
||||||
|
render();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
els.sortRadios.forEach(function (radio) {
|
||||||
|
radio.addEventListener('change', function () {
|
||||||
|
state.sort = this.value;
|
||||||
|
state.page = 1;
|
||||||
|
render();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// ist das DOM bereits vollständig aufgebaut?
|
render();
|
||||||
if (document.readyState === 'loading') {
|
}
|
||||||
// Falls noch geladen wird, auf das Event warten
|
|
||||||
document.addEventListener('DOMContentLoaded', initPaginator);
|
function sortItems(items, sortValue) {
|
||||||
} else {
|
const copy = items.slice();
|
||||||
// Falls das HTML bereits komplett da ist, sofort ausführen
|
switch (sortValue) {
|
||||||
initPaginator();
|
case 'likes':
|
||||||
}
|
copy.sort(function (a, b) { return (b.likes || 0) - (a.likes || 0); });
|
||||||
|
break;
|
||||||
|
case 'newest':
|
||||||
|
copy.sort(function (a, b) { return (b.creationDate || '').localeCompare(a.creationDate || ''); });
|
||||||
|
break;
|
||||||
|
case 'oldest':
|
||||||
|
copy.sort(function (a, b) { return (a.creationDate || '').localeCompare(b.creationDate || ''); });
|
||||||
|
break;
|
||||||
|
case 'alphabet':
|
||||||
|
default:
|
||||||
|
copy.sort(function (a, b) { return a.title.localeCompare(b.title, 'de', { sensitivity: 'base' }); });
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return copy;
|
||||||
|
}
|
||||||
|
|
||||||
|
function render() {
|
||||||
|
let items = state.all;
|
||||||
|
if (state.category && state.category !== 'all') {
|
||||||
|
items = items.filter(function (it) {
|
||||||
|
return (it.category || '').toLowerCase() === state.category.toLowerCase();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
state.filtered = sortItems(items, state.sort);
|
||||||
|
|
||||||
|
const totalResults = state.filtered.length;
|
||||||
|
const totalPages = Math.max(1, Math.ceil(totalResults / state.perPage));
|
||||||
|
if (state.page > totalPages) state.page = totalPages;
|
||||||
|
if (state.page < 1) state.page = 1;
|
||||||
|
|
||||||
|
const start = (state.page - 1) * state.perPage;
|
||||||
|
const pageItems = state.filtered.slice(start, start + state.perPage);
|
||||||
|
|
||||||
|
renderList(pageItems);
|
||||||
|
renderPager(totalPages);
|
||||||
|
renderMeta(totalResults);
|
||||||
|
}
|
||||||
|
|
||||||
|
function renderList(items) {
|
||||||
|
els.list.innerHTML = '';
|
||||||
|
|
||||||
|
if (items.length === 0) {
|
||||||
|
const p = document.createElement('p');
|
||||||
|
p.textContent = 'Keine Beiträge zu diesem Suchbegriff gefunden.';
|
||||||
|
els.list.appendChild(p);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
items.forEach(function (item) {
|
||||||
|
const div = document.createElement('div');
|
||||||
|
div.className = 's-res-item';
|
||||||
|
div.setAttribute('data-likes', item.likes || 0);
|
||||||
|
div.setAttribute('data-category', (item.category || '').toLowerCase());
|
||||||
|
|
||||||
|
div.innerHTML =
|
||||||
|
'<div class="s-res-content">' +
|
||||||
|
'<h2 class="s-res-item-title">' +
|
||||||
|
'<a class="s-res-link"></a>' +
|
||||||
|
'</h2>' +
|
||||||
|
'<div class="s-res-meta-row">' +
|
||||||
|
'<p class="s-res-author">Von: <span class="s-res-author-name"></span></p>' +
|
||||||
|
'<span class="s-res-likes"></span>' +
|
||||||
|
'</div>' +
|
||||||
|
'</div>' +
|
||||||
|
'<div class="s-res-arrow">→</div>';
|
||||||
|
|
||||||
|
const link = div.querySelector('.s-res-link');
|
||||||
|
link.href = 'index.php?pfad=showArticle&id=' + encodeURIComponent(item.id);
|
||||||
|
link.textContent = item.title;
|
||||||
|
div.querySelector('.s-res-author-name').textContent = item.author;
|
||||||
|
div.querySelector('.s-res-likes').textContent = item.likes || 0;
|
||||||
|
|
||||||
|
els.list.appendChild(div);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function renderPager(totalPages) {
|
||||||
|
if (!els.pagerContainer) return;
|
||||||
|
els.pagerContainer.innerHTML = '';
|
||||||
|
|
||||||
|
const prevBtn = document.createElement('button');
|
||||||
|
prevBtn.type = 'button';
|
||||||
|
prevBtn.className = 's-res-page-btn';
|
||||||
|
prevBtn.textContent = '\u00AB';
|
||||||
|
prevBtn.disabled = state.page <= 1;
|
||||||
|
prevBtn.addEventListener('click', function () { goToPage(state.page - 1); });
|
||||||
|
els.pagerContainer.appendChild(prevBtn);
|
||||||
|
|
||||||
|
const span = document.createElement('span');
|
||||||
|
span.id = 'dynamic-page-numbers';
|
||||||
|
span.textContent = 'Seite ' + state.page + ' von ' + totalPages;
|
||||||
|
els.pagerContainer.appendChild(span);
|
||||||
|
|
||||||
|
const nextBtn = document.createElement('button');
|
||||||
|
nextBtn.type = 'button';
|
||||||
|
nextBtn.className = 's-res-page-btn';
|
||||||
|
nextBtn.textContent = '\u00BB';
|
||||||
|
nextBtn.disabled = state.page >= totalPages;
|
||||||
|
nextBtn.addEventListener('click', function () { goToPage(state.page + 1); });
|
||||||
|
els.pagerContainer.appendChild(nextBtn);
|
||||||
|
}
|
||||||
|
|
||||||
|
function renderMeta(totalResults) {
|
||||||
|
if (!els.metaCount) return;
|
||||||
|
const query = els.metaCount.getAttribute('data-query') || '';
|
||||||
|
els.metaCount.textContent = totalResults + ' Treffer für Ihre Suchanfrage "' + query + '"';
|
||||||
|
}
|
||||||
|
|
||||||
|
function goToPage(page) {
|
||||||
|
state.page = page;
|
||||||
|
render();
|
||||||
|
els.list.scrollIntoView({ behavior: 'smooth', block: 'start' });
|
||||||
|
}
|
||||||
|
|
||||||
|
if (document.readyState === 'loading') {
|
||||||
|
document.addEventListener('DOMContentLoaded', init);
|
||||||
|
} else {
|
||||||
|
init();
|
||||||
|
}
|
||||||
|
})();
|
||||||
@@ -0,0 +1,203 @@
|
|||||||
|
/**
|
||||||
|
* Übernimmt Filterung (Kategorie), Sortierung und Pagination der
|
||||||
|
* Suchergebnisse vollständig clientseitig, ohne Neuladen der Seite.
|
||||||
|
*
|
||||||
|
* Voraussetzung: Im HTML liegt ein <script type="application/json" id="s-res-data">
|
||||||
|
* mit allen Treffern der aktuellen Suche (siehe content/search-results.php).
|
||||||
|
*
|
||||||
|
* Ist kein JavaScript aktiv, bleibt die serverseitig gerenderte Ausgabe
|
||||||
|
* (inkl. echter Pagination-Links und Formular-Submit) unverändert nutzbar.
|
||||||
|
*/
|
||||||
|
(function () {
|
||||||
|
const DEFAULT_PER_PAGE = 10;
|
||||||
|
|
||||||
|
const state = {
|
||||||
|
all: [],
|
||||||
|
filtered: [],
|
||||||
|
category: 'all',
|
||||||
|
sort: 'alphabet',
|
||||||
|
perPage: DEFAULT_PER_PAGE,
|
||||||
|
page: 1,
|
||||||
|
};
|
||||||
|
|
||||||
|
const els = {};
|
||||||
|
|
||||||
|
function init() {
|
||||||
|
const dataScript = document.getElementById('s-res-data');
|
||||||
|
const listContainer = document.querySelector('.s-res-list');
|
||||||
|
if (!dataScript || !listContainer) {
|
||||||
|
return; // keine Ergebnisliste auf dieser Seite vorhanden
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
state.all = JSON.parse(dataScript.textContent);
|
||||||
|
} catch (e) {
|
||||||
|
console.error('Suchergebnis-Daten konnten nicht gelesen werden.', e);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
els.list = listContainer;
|
||||||
|
els.categorySelect = document.getElementById('category-filter');
|
||||||
|
els.perPageSelect = document.getElementById('s-res-per-page');
|
||||||
|
els.sortRadios = document.querySelectorAll('.sort-radio');
|
||||||
|
els.pagerContainer = document.querySelector('.s-res-page-navigation');
|
||||||
|
els.metaCount = document.querySelector('.s-res-meta');
|
||||||
|
|
||||||
|
// Ausgangszustand aus dem serverseitig gerenderten Markup übernehmen,
|
||||||
|
// damit Browser-zurück / geteilte Links konsistent bleiben.
|
||||||
|
if (els.categorySelect) state.category = els.categorySelect.value || 'all';
|
||||||
|
if (els.perPageSelect) state.perPage = parseInt(els.perPageSelect.value, 10) || DEFAULT_PER_PAGE;
|
||||||
|
const checkedSort = document.querySelector('.sort-radio:checked');
|
||||||
|
if (checkedSort) state.sort = checkedSort.value;
|
||||||
|
|
||||||
|
// Ab hier übernimmt JS: Formular-Steuerelemente lösen keinen
|
||||||
|
// Seiten-Reload mehr aus, sondern rendern direkt neu.
|
||||||
|
if (els.categorySelect) {
|
||||||
|
els.categorySelect.addEventListener('change', function () {
|
||||||
|
state.category = this.value;
|
||||||
|
state.page = 1;
|
||||||
|
render();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (els.perPageSelect) {
|
||||||
|
els.perPageSelect.addEventListener('change', function () {
|
||||||
|
state.perPage = parseInt(this.value, 10) || DEFAULT_PER_PAGE;
|
||||||
|
state.page = 1;
|
||||||
|
render();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
els.sortRadios.forEach(function (radio) {
|
||||||
|
radio.addEventListener('change', function () {
|
||||||
|
state.sort = this.value;
|
||||||
|
state.page = 1;
|
||||||
|
render();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
render();
|
||||||
|
}
|
||||||
|
|
||||||
|
function sortItems(items, sortValue) {
|
||||||
|
const copy = items.slice();
|
||||||
|
switch (sortValue) {
|
||||||
|
case 'likes':
|
||||||
|
copy.sort(function (a, b) { return (b.likes || 0) - (a.likes || 0); });
|
||||||
|
break;
|
||||||
|
case 'newest':
|
||||||
|
copy.sort(function (a, b) { return (b.creationDate || '').localeCompare(a.creationDate || ''); });
|
||||||
|
break;
|
||||||
|
case 'oldest':
|
||||||
|
copy.sort(function (a, b) { return (a.creationDate || '').localeCompare(b.creationDate || ''); });
|
||||||
|
break;
|
||||||
|
case 'alphabet':
|
||||||
|
default:
|
||||||
|
copy.sort(function (a, b) { return a.title.localeCompare(b.title, 'de', { sensitivity: 'base' }); });
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return copy;
|
||||||
|
}
|
||||||
|
|
||||||
|
function render() {
|
||||||
|
let items = state.all;
|
||||||
|
if (state.category && state.category !== 'all') {
|
||||||
|
items = items.filter(function (it) {
|
||||||
|
return (it.category || '').toLowerCase() === state.category.toLowerCase();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
state.filtered = sortItems(items, state.sort);
|
||||||
|
|
||||||
|
const totalResults = state.filtered.length;
|
||||||
|
const totalPages = Math.max(1, Math.ceil(totalResults / state.perPage));
|
||||||
|
if (state.page > totalPages) state.page = totalPages;
|
||||||
|
if (state.page < 1) state.page = 1;
|
||||||
|
|
||||||
|
const start = (state.page - 1) * state.perPage;
|
||||||
|
const pageItems = state.filtered.slice(start, start + state.perPage);
|
||||||
|
|
||||||
|
renderList(pageItems);
|
||||||
|
renderPager(totalPages);
|
||||||
|
renderMeta(totalResults);
|
||||||
|
}
|
||||||
|
|
||||||
|
function renderList(items) {
|
||||||
|
els.list.innerHTML = '';
|
||||||
|
|
||||||
|
if (items.length === 0) {
|
||||||
|
const p = document.createElement('p');
|
||||||
|
p.textContent = 'Keine Beiträge zu diesem Suchbegriff gefunden.';
|
||||||
|
els.list.appendChild(p);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
items.forEach(function (item) {
|
||||||
|
const div = document.createElement('div');
|
||||||
|
div.className = 's-res-item';
|
||||||
|
div.setAttribute('data-likes', item.likes || 0);
|
||||||
|
div.setAttribute('data-category', (item.category || '').toLowerCase());
|
||||||
|
|
||||||
|
div.innerHTML =
|
||||||
|
'<div class="s-res-content">' +
|
||||||
|
'<h2 class="s-res-item-title">' +
|
||||||
|
'<a class="s-res-link"></a>' +
|
||||||
|
'</h2>' +
|
||||||
|
'<div class="s-res-meta-row">' +
|
||||||
|
'<p class="s-res-author">Von: <span class="s-res-author-name"></span></p>' +
|
||||||
|
'<span class="s-res-likes"></span>' +
|
||||||
|
'</div>' +
|
||||||
|
'</div>' +
|
||||||
|
'<div class="s-res-arrow">→</div>';
|
||||||
|
|
||||||
|
const link = div.querySelector('.s-res-link');
|
||||||
|
link.href = 'index.php?pfad=showArticle&id=' + encodeURIComponent(item.id);
|
||||||
|
link.textContent = item.title;
|
||||||
|
div.querySelector('.s-res-author-name').textContent = item.author;
|
||||||
|
div.querySelector('.s-res-likes').textContent = item.likes || 0;
|
||||||
|
|
||||||
|
els.list.appendChild(div);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function renderPager(totalPages) {
|
||||||
|
if (!els.pagerContainer) return;
|
||||||
|
els.pagerContainer.innerHTML = '';
|
||||||
|
|
||||||
|
const prevBtn = document.createElement('button');
|
||||||
|
prevBtn.type = 'button';
|
||||||
|
prevBtn.className = 's-res-page-btn';
|
||||||
|
prevBtn.textContent = '\u00AB';
|
||||||
|
prevBtn.disabled = state.page <= 1;
|
||||||
|
prevBtn.addEventListener('click', function () { goToPage(state.page - 1); });
|
||||||
|
els.pagerContainer.appendChild(prevBtn);
|
||||||
|
|
||||||
|
const span = document.createElement('span');
|
||||||
|
span.id = 'dynamic-page-numbers';
|
||||||
|
span.textContent = 'Seite ' + state.page + ' von ' + totalPages;
|
||||||
|
els.pagerContainer.appendChild(span);
|
||||||
|
|
||||||
|
const nextBtn = document.createElement('button');
|
||||||
|
nextBtn.type = 'button';
|
||||||
|
nextBtn.className = 's-res-page-btn';
|
||||||
|
nextBtn.textContent = '\u00BB';
|
||||||
|
nextBtn.disabled = state.page >= totalPages;
|
||||||
|
nextBtn.addEventListener('click', function () { goToPage(state.page + 1); });
|
||||||
|
els.pagerContainer.appendChild(nextBtn);
|
||||||
|
}
|
||||||
|
|
||||||
|
function renderMeta(totalResults) {
|
||||||
|
if (!els.metaCount) return;
|
||||||
|
const query = els.metaCount.getAttribute('data-query') || '';
|
||||||
|
els.metaCount.textContent = totalResults + ' Treffer für Ihre Suchanfrage "' + query + '"';
|
||||||
|
}
|
||||||
|
|
||||||
|
function goToPage(page) {
|
||||||
|
state.page = page;
|
||||||
|
render();
|
||||||
|
els.list.scrollIntoView({ behavior: 'smooth', block: 'start' });
|
||||||
|
}
|
||||||
|
|
||||||
|
if (document.readyState === 'loading') {
|
||||||
|
document.addEventListener('DOMContentLoaded', init);
|
||||||
|
} else {
|
||||||
|
init();
|
||||||
|
}
|
||||||
|
})();
|
||||||
@@ -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();
|
||||||
|
}
|
||||||
@@ -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.";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,3 +1,3 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
?>
|
?>
|
||||||
|
|||||||
@@ -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,78 +2,88 @@
|
|||||||
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);
|
||||||
|
|
||||||
$sortStyle = $_GET['sort'] ?? 'alphabet';
|
$sortStyle = $_GET['sort'] ?? 'alphabet';
|
||||||
$_SESSION['search_sort'] = $sortStyle;
|
$_SESSION['search_sort'] = $sortStyle;
|
||||||
|
|
||||||
if ($sortStyle === 'alphabet') {
|
if ($sortStyle === 'alphabet') {
|
||||||
// Titel aufsteigend alphabetiisch sortiert
|
// Titel aufsteigend alphabetisch sortiert
|
||||||
usort($results, function ($a, $b) {
|
usort($results, function ($a, $b) {
|
||||||
return strcasecmp($a->getTitle(), $b->getTitle());
|
return strcasecmp($a->getTitle(), $b->getTitle());
|
||||||
});
|
});
|
||||||
} elseif ($sortStyle === 'likes') {
|
} elseif ($sortStyle === 'likes') {
|
||||||
usort($results, function($a, $b) {
|
usort($results, function ($a, $b) {
|
||||||
return $b->getLikeCount() <=> $a->getLikeCount();
|
return $b->getLikeCount() <=> $a->getLikeCount();
|
||||||
});
|
});
|
||||||
} elseif ($sortStyle === 'newest') {
|
} elseif ($sortStyle === 'newest') {
|
||||||
// Datum neu zu alt sortiert
|
// Datum neu zu alt sortiert
|
||||||
usort($results, function($a, $b) {
|
usort($results, function ($a, $b) {
|
||||||
return strcmp($b->getCreationDate(), $a->getCreationDate());
|
return strcmp($b->getCreationDate(), $a->getCreationDate());
|
||||||
});
|
});
|
||||||
} elseif ($sortStyle === 'oldest') {
|
} elseif ($sortStyle === 'oldest') {
|
||||||
// Datum alt zu neu sortiert
|
// Datum alt zu neu sortiert
|
||||||
usort($results, function($a, $b) {
|
usort($results, function ($a, $b) {
|
||||||
return strcmp($a->getCreationDate(), $b->getCreationDate());
|
return strcmp($a->getCreationDate(), $b->getCreationDate());
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ergebnisse werden in ein flaches array umgewandelt, da sont incomplete-PHP error im Ergebnis
|
// Ergebnisse werden in ein flaches Array umgewandelt, da sonst incomplete-PHP-Fehler im Ergebnis
|
||||||
$safeArrayResults = [];
|
$safeArrayResults = [];
|
||||||
foreach ($results as $obj) {
|
foreach ($results as $obj) {
|
||||||
$safeArrayResults[] = [
|
$safeArrayResults[] = [
|
||||||
"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(),
|
||||||
"likes" => $obj->getLikes(),
|
"likes" => $obj->getLikes(),
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
$_SESSION["search_results"] = $safeArrayResults;
|
$_SESSION["search_results"] = $safeArrayResults;
|
||||||
$_SESSION["search_query"] = $search;
|
$_SESSION["search_query"] = $search;
|
||||||
$_SESSION["message"] = "new_search_results";
|
$_SESSION["message"] = "new_search_results";
|
||||||
|
} catch (Exception $e) {
|
||||||
} catch (Exception $e){
|
|
||||||
$_SESSION["message"] = "internal_error";
|
$_SESSION["message"] = "internal_error";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$sort = $_GET['sort'] ?? 'alphabet';
|
$sort = $_GET['sort'] ?? 'alphabet';
|
||||||
$limit = isset($_GET['limit']) ? (int)$_GET['limit'] : 10;
|
$limit = isset($_GET['limit']) ? (int)$_GET['limit'] : 10;
|
||||||
$page = isset($_GET['page']) ? (int)$_GET['page'] : 1;
|
if (!searchLimitValidator($limit)) {
|
||||||
header("Location: ../../index.php?pfad=search-results&q=" . urlencode($search) . "&sort=" . urlencode($sort) . "&limit=" . $limit . "&page=" . $page);
|
$limit = 10;
|
||||||
exit();
|
}
|
||||||
|
$page = isset($_GET['page']) ? (int)$_GET['page'] : 1;
|
||||||
}
|
|
||||||
|
|
||||||
|
// Kategorie-Filter durchreichen (Whitelist-Prüfung erfolgt in der View)
|
||||||
|
$category = isset($_GET['category']) ? $_GET['category'] : 'all';
|
||||||
|
|
||||||
|
header(
|
||||||
|
"Location: ../../index.php?pfad=search-results&q=" . urlencode($search) .
|
||||||
|
"&sort=" . urlencode($sort) .
|
||||||
|
"&limit=" . $limit .
|
||||||
|
"&category=" . urlencode($category) .
|
||||||
|
"&page=" . $page
|
||||||
|
);
|
||||||
|
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"]);
|
||||||
|
|
||||||
|
|||||||
@@ -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();
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -18,152 +18,9 @@ class ArticleManager
|
|||||||
*/
|
*/
|
||||||
public static function getInstance()
|
public static function getInstance()
|
||||||
{
|
{
|
||||||
$articleManager = DatabaseArticleManager::getInstance(); // Hier kann zwischen dem lokalen und datenbankbasiertem ArticleManager gewechselt werden.
|
|
||||||
|
|
||||||
// 100 fiktionale Fachbeiträge:
|
return DatabaseArticleManager::getInstance(); // Hier kann zwischen dem lokalen und datenbankbasiertem ArticleManager gewechselt werden.
|
||||||
$dummyArticles = [
|
|
||||||
// --- INFORMATIK & MATHE (1-20) ---
|
|
||||||
1 => ["Satz des Pythagoras", "Der Satz des Pythagoras beschreibt das Verhältnis der Seitenlängen in einem rechtwinkligen Dreieck.", "mathe", "Dreiecke, Geometrie"],
|
|
||||||
2 => ["Tunneleffekt", "Der Tunneleffekt beschreibt das quantenmechanische Phänomen, dass Teilchen Barrieren überwinden können.", "physik", "Quantenphysik, Energie"],
|
|
||||||
3 => ["Datenschutz vs Datensicherheit", "Datenschutz schützt Personen, während Datensicherheit Systeme vor unbefugten Zugriffen schützt.", "informatik", "Daten, DSGVO"],
|
|
||||||
4 => ["Einführung in Algorithmen", "Ein Algorithmus ist eine präzise Handlungsanweisung zur Lösung eines vordefinierten Problems.", "informatik", "Code, Logik"],
|
|
||||||
5 => ["Primzahlen im Detail", "Primzahlen sind natürliche Zahlen, die nur durch eins und sich selbst ohne Rest teilbar sind.", "mathe", "Zahlentheorie"],
|
|
||||||
6 => ["Lineare Algebra", "Vektoren und Matrizen bilden das Fundament für moderne Computergrafik und 3D-Engines.", "mathe", "Matrizen, Vektoren"],
|
|
||||||
7 => ["Objektorientierte Programmierung", "Die OOP nutzt Klassen und Objekte, um reale Strukturen im Quellcode abzubilden.", "informatik", "OOP, Klassen"],
|
|
||||||
8 => ["Grundlagen von HTML und CSS", "HTML strukturiert den Inhalt einer Webseite, während CSS für das optische Design zuständig ist.", "informatik", "Web, Frontend"],
|
|
||||||
9 => ["Die Relativitätstheorie", "Einsteins Theorie revolutionierte unser Verständnis von Raum, Zeit und Gravitation massiv.", "physik", "Einstein, Gravitation"],
|
|
||||||
10 => ["Datenbanken und SQL", "Strukturierte Abfragesprachen erlauben das effiziente Speichern und Abrufen großer Datenmengen.", "informatik", "SQL, Datenbank"],
|
|
||||||
11 => ["Wahrscheinlichkeitsrechnung", "Die Stochastik befasst sich mit der mathematischen Analyse von Zufallsexperimenten.", "mathe", "Zufall, Stochastik"],
|
|
||||||
12 => ["Quantencomputing", "Künftige Quantencomputer nutzen Qubits, um komplexe Berechnungen in Rekordzeit zu lösen.", "informatik", "Hardware, Zukunft"],
|
|
||||||
13 => ["Die Fibonacci-Folge", "Diese Zahlenreihe beschreibt mathematische Wachstumsmuster, die oft in der Natur vorkommen.", "mathe", "Zahlen, Natur"],
|
|
||||||
14 => ["Thermodynamik", "Die Hauptsätze der Thermodynamik regeln den Energieaustausch und die Entropie in Systemen.", "physik", "Energie, Wärme"],
|
|
||||||
15 => ["Cybersecurity Grundlagen", "Die Absicherung von Netzwerken erfordert Firewalls, Verschlüsselung und regelmäßige Audits.", "informatik", "Sicherheit, Hacker"],
|
|
||||||
16 => ["Kryptographie", "Asymmetrische Verschlüsselungsverfahren sichern heutzutage den gesamten Datenverkehr im Web.", "informatik", "Krypto, Security"],
|
|
||||||
17 => ["Analysis und Ableitungen", "Die Differentialrechnung untersucht die lokalen Änderungsraten von mathematischen Funktionen.", "mathe", "Analysis, Funktionen"],
|
|
||||||
18 => ["Schwarze Löcher", "Diese Regionen im Raum besitzen eine so starke Gravitation, dass selbst Licht nicht entkommt.", "physik", "Astronomie, Kosmos"],
|
|
||||||
19 => ["Git Versionsverwaltung", "Git erlaubt Entwicklern das parallele Arbeiten an Code-Projekten ohne Datenverlust.", "informatik", "Git, DevOps"],
|
|
||||||
20 => ["Künstliche Intelligenz", "Neuronale Netze versuchen das menschliche Gehirn für maschinelles Lernen nachzubilden.", "informatik", "KI, Software"],
|
|
||||||
|
|
||||||
// --- CHEMIE & BIOLOGIE (21-40) ---
|
|
||||||
21 => ["Das Periodensystem", "Die Elemente sind nach ihrer Ordnungszahl und chemischen Eigenschaften geordnet.", "chemie", "Elemente, Moleküle"],
|
|
||||||
22 => ["Photosynthese", "Pflanzen wandeln mithilfe von Sonnenlicht Kohlendioxid und Wasser in Glukose und Sauerstoff um.", "biologie", "Pflanzen, Energie"],
|
|
||||||
23 => ["Aufbau einer Zelle", "Die Zelle ist die kleinste lebende Einheit aller Organismen mit spezialisierten Organellen.", "biologie", "Zellen, Biologie"],
|
|
||||||
24 => ["Säuren und Basen", "Der pH-Wert misst die Konzentration von Wasserstoff-Ionen in einer wässrigen Lösung.", "chemie", "Labor, pH-Wert"],
|
|
||||||
25 => ["Die DNA-Struktur", "Die Doppelhelix enthält den genetischen Bauplan für die Entwicklung aller Lebewesen.", "biologie", "Genetik, Erbgut"],
|
|
||||||
26 => ["Chemische Bindungen", "Kovalente Bindungen entstehen durch das Teilen von Elektronenpaaren zwischen Atomen.", "chemie", "Atome, Bindung"],
|
|
||||||
27 => ["Das Immunsystem", "Weiße Blutkörperchen und Antikörper schützen den menschlichen Körper vor Krankheitserregern.", "biologie", "Gesundheit, Abwehr"],
|
|
||||||
28 => ["Katalysatoren", "Katalysatoren beschleunigen chemische Reaktionen, ohne dabei selbst verbraucht zu werden.", "chemie", "Reaktion, Chemie"],
|
|
||||||
29 => ["Ökosystem Wald", "Das Zusammenspiel von Flora, Fauna und Klima bildet ein hochsensibles ökologisches System.", "biologie", "Natur, Wald"],
|
|
||||||
30 => ["Die Mendelschen Regeln", "Diese Grundgesetze der Vererbung beschreiben, wie Merkmale an Nachkommen weitergegeben werden.", "biologie", "Genetik, Erbung"],
|
|
||||||
31 => ["Zustandsformen der Materie", "Fest, flüssig und gasförmig sind die klassischen Aggregatzustände von Stoffen.", "chemie", "Physik, Materie"],
|
|
||||||
32 => ["Evolutionstheorie", "Charles Darwin begründete die Theorie der natürlichen Auslese und Anpassung von Arten.", "biologie", "Darwin, Evolution"],
|
|
||||||
33 => ["Organische Chemie", "Die Chemie der Kohlenstoffverbindungen bildet die Basis für alles bekannte Leben.", "chemie", "Kohlenstoff, Chemie"],
|
|
||||||
34 => ["Das menschliche Gehirn", "Milliarden von Neuronen kommunizieren über Synapsen, um Reize und Gedanken zu verarbeiten.", "biologie", "Neurologie, Nerven"],
|
|
||||||
35 => ["Der Wasserkreislauf", "Verdunstung, Kondensation und Niederschlag halten das Wasser auf der Erde in Bewegung.", "geographie", "Wasser, Klima"],
|
|
||||||
36 => ["Aggregatzustände von Wasser", "Wasser zeigt ungewöhnliche Eigenschaften wie die Dichteanomalie beim Gefrieren.", "chemie", "Wasser, Eis"],
|
|
||||||
37 => ["Blutkreislauf des Menschen", "Das Herz pumpt sauerstoffreiches Blut durch Arterien in alle Organe des Körpers.", "biologie", "Herz, Medizin"],
|
|
||||||
38 => ["Das Ohmsche Gesetz", "Es beschreibt den direkten Zusammenhang zwischen Spannung, Stromstärke und Widerstand.", "physik", "Strom, Elektronik"],
|
|
||||||
39 => ["Plattentektonik", "Die Bewegung der Kontinentalplatten führt zu Erdbeben, Vulkanismus und Gebirgsbildung.", "geographie", "Erde, Geologie"],
|
|
||||||
40 => ["Proteine und Enzyme", "Enzyme wirken als Biokatalysatoren und steuern fast alle Stoffwechselprozesse.", "biologie", "Biochemie, Enzyme"],
|
|
||||||
|
|
||||||
// --- GESCHICHTE & WIRTSCHAFT (41-60) ---
|
|
||||||
41 => ["Das Römische Reich", "Vom Stadtstaat zum Weltreich prägte Rom die Rechts- und Kulturgeschichte Europas.", "geschichte", "Antike, Rom"],
|
|
||||||
42 => ["Die Französische Revolution", "Freiheit, Gleichheit, Brüderlichkeit beendeten 1789 die absolute Monarchie in Frankreich.", "geschichte", "Europa, Politik"],
|
|
||||||
43 => ["Inflation erklärt", "Inflation bezeichnet die kontinuierliche Geldentwertung und den Kaufkraftverlust.", "wirtschaft", "Geld, Finanzen"],
|
|
||||||
44 => ["Die Industrielle Revolution", "Die Erfindung der Dampfmaschine veränderte die Produktion und die Gesellschaft tiefgreifend.", "geschichte", "Industrie, Arbeit"],
|
|
||||||
45 => ["Angebot und Nachfrage", "Dieses fundamentale Marktgesetz bestimmt den Preis von Gütern in einer freien Wirtschaft.", "wirtschaft", "Markt, Preise"],
|
|
||||||
46 => ["Der Buchdruck", "Johannes Gutenbergs Erfindung revolutionierte die Verbreitung von Wissen im Mittelalter.", "geschichte", "Medien, Wissen"],
|
|
||||||
47 => ["Die Entdeckung Amerikas", "Kolumbus' Seereise im Jahr 1492 leitete das Zeitalter der Kolonialisierung ein.", "geschichte", "Entdeckung, Seefahrt"],
|
|
||||||
48 => ["Kryptowährungen", "Bitcoin nutzt Blockchain-Technologie, um dezentralen digitalen Werttransfer zu erlauben.", "wirtschaft", "Blockchain, Finanzen"],
|
|
||||||
49 => ["Das antike Griechenland", "Die Wiege der Demokratie und Philosophie brachte Denker wie Sokrates und Platon hervor.", "geschichte", "Antike, Philosophie"],
|
|
||||||
50 => ["Globalisierung", "Die weltweite Verflechtung in Wirtschaft, Kultur und Politik bringt Chancen und Risiken.", "wirtschaft", "Weltwirtschaft, Handel"],
|
|
||||||
51 => ["Der Dreißigjährige Krieg", "Ein religiöser und politischer Konflikt verwüstete zwischen 1618 und 1648 Mitteleuropa.", "geschichte", "Krieg, Europa"],
|
|
||||||
52 => ["Die Weimarer Republik", "Die erste deutsche Demokratie scheiterte an wirtschaftlichen und politischen Krisen.", "geschichte", "Deutschland, Weimar"],
|
|
||||||
53 => ["Aktien und Börse", "Unternehmen beschaffen sich Kapital durch die Ausgabe von Anteilen an Investoren.", "wirtschaft", "Aktien, Investieren"],
|
|
||||||
54 => ["Das alte Ägypten", "Pharaonen, Pyramiden und Hieroglyphen zeugen von einer hochentwickelten Hochkultur am Nil.", "geschichte", "Ägypten, Antike"],
|
|
||||||
55 => ["Der Kalte Krieg", "Das Wettrüsten zwischen USA und UdSSR prägte die globale Politik der Nachkriegszeit.", "geschichte", "Ost-West, Politik"],
|
|
||||||
56 => ["Zentralbanken und Leitzins", "Durch Zinsänderungen steuern Notenbanken die Geldmenge und bekämpfen Inflation.", "wirtschaft", "Zinsen, Geldpolitik"],
|
|
||||||
57 => ["Die Seidenstraße", "Das historische Netzwerk von Handelsrouten verband über Jahrhunderte Asien und Europa.", "geschichte", "Handel, Asien"],
|
|
||||||
58 => ["Das Mittelalter", "Ritter, Burgen und das Feudalsystem prägten diese tausendjährige Epoche Europas.", "geschichte", "Mittelalter, Feudalismus"],
|
|
||||||
59 => ["Planwirtschaft vs Marktwirtschaft", "Zentrale staatliche Steuerung steht dem freien Spiel der Marktkräfte gegenüber.", "wirtschaft", "Systeme, Wirtschaft"],
|
|
||||||
60 => ["Die Berliner Mauer", "Ihr Bau 1961 zementierte die Teilung Deutschlands, ihr Fall 1989 beendete sie.", "geschichte", "DDR, Wiedervereinigung"],
|
|
||||||
|
|
||||||
// --- ANWENDUNGEN & WEITERE THEMEN (61-100) ---
|
|
||||||
61 => ["Cloud Computing", "Das Auslagern von Rechenleistung in das Internet spart lokale IT-Infrastruktur ein.", "informatik", "Cloud, Web"],
|
|
||||||
62 => ["Responsive Webdesign", "Moderne Webseiten passen ihr Layout dynamisch an Smartphones und Desktops an.", "informatik", "Design, CSS"],
|
|
||||||
63 => ["Der Treibhauseffekt", "Gase in der Atmosphäre verhindern das Entweichen von Wärme ins Weltall.", "physik", "Klima, Umwelt"],
|
|
||||||
64 => ["Mechanik und Kräfte", "Die Newtonschen Axiome beschreiben, wie Kräfte auf Körper wirken und sie bewegen.", "physik", "Newton, Kraft"],
|
|
||||||
65 => ["Die Mendelschen Gesetze", "Die Vererbung von Genen folgt klaren statistischen Wahrscheinlichkeiten.", "biologie", "Genetik, Erbsen"],
|
|
||||||
66 => ["Lichtgeschwindigkeit", "Im Vakuum bewegt sich Licht mit knapp 300.000 Kilometern pro Sekunde.", "physik", "Licht, Relativität"],
|
|
||||||
67 => ["Die Funktion von APIs", "Programmierschnittstellen erlauben den Datenaustausch zwischen verschiedenen Systemen.", "informatik", "API, Schnittstelle"],
|
|
||||||
68 => ["Der Goldstandard", "Ein historischen Währungssystem, bei dem Geld durch echtes Gold gedeckt war.", "wirtschaft", "Gold, Währung"],
|
|
||||||
69 => ["Der Wiener Kongress", "1815 ordneten die europäischen Mächte die Landkarte nach den Napoleonischen Kriegen neu.", "geschichte", "Europa, Diplomatie"],
|
|
||||||
70 => ["Integrierte Schaltkreise", "Mikrochips enthalten Millionen Transistoren auf kleinstem Raum für Logikschaltungen.", "informatik", "Hardware, Chips"],
|
|
||||||
71 => ["Die Magellan-Expedition", "Die erste erfolgreiche Weltumsegelung bewies praktisch die Kugelgestalt der Erde.", "geschichte", "Seefahrt, Erde"],
|
|
||||||
72 => ["Das Internet der Dinge", "Alltagsgegenstände werden vernetzt, um smarte Automatisierungen zu ermöglichen.", "informatik", "IoT, SmartHome"],
|
|
||||||
73 => ["Halbwertszeit", "Die Zeitspanne, in der sich die Hälfte der instabilen Atome radioaktiv abbaut.", "physik", "Atomphysik, Strahlung"],
|
|
||||||
74 => ["Elektromagnetismus", "Die Verknüpfung von elektrischen Strömen und magnetischen Feldern treibt Motoren an.", "physik", "Strom, Magnet"],
|
|
||||||
75 => ["Das Ökosystem Meer", "Ozeane regulieren das Weltklima und bieten Lebensraum für unzählige Arten.", "biologie", "Meer, Ökologie"],
|
|
||||||
76 => ["Einführung in Docker", "Containerisierung isoliert Anwendungen samt Abhängigkeiten für stabilen Betrieb.", "informatik", "Docker, DevOps"],
|
|
||||||
77 => ["Das Römische Recht", "Viele moderne europäische Gesetzbücher basieren auf antiken römischen Rechtsprinzipien.", "geschichte", "Recht, Gesetz"],
|
|
||||||
78 => ["Verhaltensbiologie", "Untersuchung von angeborenen und erlernten Verhaltensweisen bei Mensch und Tier.", "biologie", "Verhalten, Tiere"],
|
|
||||||
79 => ["Verschlüsselung im Alltag", "HTTPS schützt Passwörter und Zahlungsdaten beim Surfen vor dem Mitlesen.", "informatik", "Web, HTTPS"],
|
|
||||||
80 => ["Die Magna Carta", "1215 schränkte dieses Dokument die absolute Macht des englischen Königs ein.", "geschichte", "England, Verfassung"],
|
|
||||||
81 => ["Marktversagen", "Wenn der freie Markt Ressourcen unvollständig verteilt, muss der Staat eingreifen.", "wirtschaft", "Markt, Staat"],
|
|
||||||
82 => ["Optische Linsen", "Konvexe und konkave Linsen brechen Licht für Brillen, Mikroskope und Kameras.", "physik", "Optik, Licht"],
|
|
||||||
83 => ["Die Entstehung der Erde", "Vor rund 4,5 Milliarden Jahren ballte sich kosmischer Staub zu unserem Planeten.", "geographie", "Erde, Kosmos"],
|
|
||||||
84 => ["Grundlagen von JavaScript", "Diese Skriptsprache macht statische Webseiten interaktiv und dynamisch nutzbar.", "informatik", "JS, Webentwicklung"],
|
|
||||||
85 => ["Die industrielle Landwirtschaft", "Moderne Techniken sichern Welternährung, belasten jedoch oft die Umwelt.", "biologie", "Landwirtschaft, Umwelt"],
|
|
||||||
86 => ["Das Schwarze Jahr 1929", "Der New Yorker Börsencrash löste die verheerende Weltwirtschaftskrise aus.", "geschichte", "Krise, Finanzen"],
|
|
||||||
87 => ["Die Evolution des Menschen", "Der Stammbaum des Homo Sapiens entwickelte sich über Millionen Jahre in Afrika.", "biologie", "Mensch, Evolution"],
|
|
||||||
88 => ["Einführung in Linux", "Das Open-Source-Betriebssystem bildet das Rückgrat moderner Server-Infrastrukturen.", "informatik", "Linux, OS"],
|
|
||||||
89 => ["Der Urknall", "Die Urknalltheorie beschreibt den Beginn des Universums aus einer Singularität.", "physik", "Astronomie, Urknall"],
|
|
||||||
90 => ["Das Periodensystem der Elemente", "Dmitri Mendelejew ordnete Elemente logisch nach ihren Atommassen.", "chemie", "Periodensystem, Chemie"],
|
|
||||||
91 => ["Die Hanse", "Ein mächtiger mittelalterlicher Bund von Kaufleuten dominierte den Nordseehandel.", "geschichte", "Handel, Mittelalter"],
|
|
||||||
92 => ["Wirtschaftswachstum", "Die Steigerung des Bruttoinlandsprodukts gilt oft als Indikator für Wohlstand.", "wirtschaft", "BIP, Finanzen"],
|
|
||||||
93 => ["Neuronale Netze", "Diese Strukturen lernen durch mathematische Gewichtung aus riesigen Datenmengen.", "informatik", "KI, Mathematik"],
|
|
||||||
94 => ["Der Erste Weltkrieg", "Der globale Konflikt von 1914 bis 1918 zerstörte das alte europäische Machtgefüge.", "geschichte", "Europa, Krieg"],
|
|
||||||
95 => ["Das Gehirn und Hormone", "Botenstoffe steuern Gefühle, Schlafzyklen und Reaktionen des Körpers.", "biologie", "Medizin, Hormone"],
|
|
||||||
96 => ["SQL Joins erklärt", "Joins verknüpfen Daten aus mehreren Tabellen über gemeinsame Schlüssel.", "informatik", "SQL, Datenbanken"],
|
|
||||||
97 => ["Wellen-Teilchen-Dualismus", "Quantenobjekte zeigen je nach Messaufbau Eigenschaften von Wellen oder Teilchen.", "physik", "Quanten, Licht"],
|
|
||||||
98 => ["Der Absolutismus", "Der Sonnenkönig Ludwig XIV. verkörperte die unbeschränkte Herrschaft des Monarchen.", "geschichte", "Frankreich, Monarchie"],
|
|
||||||
99 => ["Die Funktion von Routern", "Netzwerkgeräte leiten Datenpakete über IP-Adressen an den richtigen Empfänger.", "informatik", "Netzwerk, Internet"],
|
|
||||||
100 => ["Die Entstehung des Geldes", "Vom Tauschhandel über Naturalgeld bis hin zu modernen digitalen Fiat-Währungen.", "wirtschaft", "Geld, Geschichte"]
|
|
||||||
];
|
|
||||||
|
|
||||||
// 10 Dummy-User:
|
|
||||||
$authors = [
|
|
||||||
'max.mustermann@web.de', 'erika.mustermann@web.de', 'john.doe@gmail.com',
|
|
||||||
'jane.doe@gmail.com', 'anna.schmidt@gmx.de', 'thomas.mueller@gmx.de',
|
|
||||||
'sabine.fischer@outlook.com', 'michael.weber@outlook.com', 'julia.wagner@t-online.de',
|
|
||||||
'stefan.becker@t-online.de'
|
|
||||||
];
|
|
||||||
|
|
||||||
foreach ($dummyArticles as $id => $data) {
|
|
||||||
// Falls der Artikel mit der ID noch nicht existiert, lege ihn an
|
|
||||||
if ($articleManager->getArticle($id) == null) {
|
|
||||||
// Verteilt die 10 Autoren gleichmäßig (ID 1 -> Autor 1, ID 10 -> Autor 10, ID 11 -> Autor 1)
|
|
||||||
$authorEmail = $authors[($id - 1) % 10];
|
|
||||||
|
|
||||||
$blockStructure = [
|
|
||||||
[
|
|
||||||
'type' => 'text',
|
|
||||||
'value' => $data[1] // Der originale Text aus dem Dummy-Array
|
|
||||||
]
|
|
||||||
];
|
|
||||||
$jsonContent = json_encode($blockStructure, JSON_UNESCAPED_UNICODE);
|
|
||||||
|
|
||||||
$articleManager->addArticle(
|
|
||||||
$data[0], // Titel
|
|
||||||
$jsonContent, // Inhalt
|
|
||||||
$authorEmail, // Rotierende Autoren-E-Mail
|
|
||||||
$data[2], // Kategorie
|
|
||||||
$data[3] // Tags
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return $articleManager;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -1,4 +1,5 @@
|
|||||||
<?php
|
<?php
|
||||||
|
require_once 'DatabaseInitializer.php';
|
||||||
require_once 'ArticleManagerDAO.php';
|
require_once 'ArticleManagerDAO.php';
|
||||||
require_once 'Article.php';
|
require_once 'Article.php';
|
||||||
/**
|
/**
|
||||||
@@ -9,60 +10,35 @@ require_once 'Article.php';
|
|||||||
class DatabaseArticleManager implements ArticleManagerDAO {
|
class DatabaseArticleManager implements ArticleManagerDAO {
|
||||||
|
|
||||||
private static $instance = null;
|
private static $instance = null;
|
||||||
|
private $dbPath;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Konstruktor
|
* Konstruktor
|
||||||
*
|
*
|
||||||
* Erstellt die Datenbankverbindung und die Tabelle articles.
|
* Erstellt die Datenbankverbindung und die Tabelle articles.
|
||||||
* @throws InternalServerErrorException
|
* @throws RuntimeException
|
||||||
*/
|
*/
|
||||||
public function __construct()
|
public function __construct()
|
||||||
{
|
{
|
||||||
if (!file_exists(__DIR__ . '/../../db/articles.db')) {
|
$this->dbPath = __DIR__ . '/../../db/eduforgeDB.db';
|
||||||
try {
|
DatabaseInitializer::initialize($this->dbPath);
|
||||||
|
|
||||||
$db = $this->getConnection();
|
|
||||||
|
|
||||||
// Tabelle für Beiträge
|
|
||||||
$db->exec("
|
|
||||||
CREATE TABLE articles (
|
|
||||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
||||||
title TEXT,
|
|
||||||
content TEXT,
|
|
||||||
author TEXT,
|
|
||||||
category TEXT,
|
|
||||||
tags TEXT,
|
|
||||||
created TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
|
||||||
);");
|
|
||||||
|
|
||||||
// Tabelle für Likes
|
|
||||||
$db->exec("
|
|
||||||
CREATE TABLE likes (
|
|
||||||
article_id INTEGER,
|
|
||||||
user_id TEXT,
|
|
||||||
PRIMARY KEY (article_id, user_id),
|
|
||||||
FOREIGN KEY (article_id) REFERENCES articles(id) ON DELETE CASCADE
|
|
||||||
);");
|
|
||||||
unset($db);
|
|
||||||
} catch (PDOException $e) {
|
|
||||||
throw new InternalServerErrorException($e->getMessage());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Baut die Verbindung zur Datenbank auf.
|
* Baut die Verbindung zur Datenbank auf.
|
||||||
* @throws InternalServerErrorException
|
*
|
||||||
|
* @return PDO Datenbankverbindung
|
||||||
|
* @throws RuntimeException
|
||||||
*/
|
*/
|
||||||
private function getConnection()
|
private function getConnection()
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
$user = 'root';
|
$db = new PDO('sqlite:' . $this->dbPath, null, null);
|
||||||
$pw = null;
|
$db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
|
||||||
$dsn = 'sqlite:' . __DIR__ . '/../../db/articles.db';
|
$db->exec('PRAGMA foreign_keys = ON;');
|
||||||
return new PDO($dsn, $user, $pw);
|
return $db;
|
||||||
} catch (PDOException $e) {
|
} catch (PDOException $e) {
|
||||||
throw new InternalServerErrorException($e->getMessage());
|
throw new RuntimeException("internal_error");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -338,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) {
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
<?php
|
<?php
|
||||||
|
require_once 'DatabaseInitializer.php';
|
||||||
require_once "CommentManagerDAO.php";
|
require_once "CommentManagerDAO.php";
|
||||||
require_once "Comment.php";
|
require_once "Comment.php";
|
||||||
|
|
||||||
@@ -12,43 +12,15 @@ require_once "Comment.php";
|
|||||||
class DatabaseCommentManager implements CommentManagerDAO
|
class DatabaseCommentManager implements CommentManagerDAO
|
||||||
{
|
{
|
||||||
private static $instance = null;
|
private static $instance = null;
|
||||||
|
private $dbPath;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Erstellt die Kommentartabelle, falls diese noch nicht existiert.
|
* Erstellt die Kommentartabelle, falls diese noch nicht existiert.
|
||||||
*/
|
*/
|
||||||
public function __construct()
|
public function __construct()
|
||||||
{
|
{
|
||||||
try {
|
$this->dbPath = __DIR__ . '/../../db/eduforgeDB.db';
|
||||||
$db = $this->getConnection();
|
DatabaseInitializer::initialize($this->dbPath);
|
||||||
|
|
||||||
$db->exec("
|
|
||||||
CREATE TABLE IF NOT EXISTS comments (
|
|
||||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
||||||
article_id INTEGER NOT NULL,
|
|
||||||
parent_comment_id INTEGER NULL,
|
|
||||||
author TEXT NOT NULL,
|
|
||||||
content TEXT NOT NULL,
|
|
||||||
created TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
|
||||||
);
|
|
||||||
");
|
|
||||||
|
|
||||||
$columns = $db->query("PRAGMA table_info(comments);")->fetchAll(PDO::FETCH_ASSOC);
|
|
||||||
$hasParentColumn = false;
|
|
||||||
|
|
||||||
foreach ($columns as $column) {
|
|
||||||
if ($column["name"] === "parent_comment_id") {
|
|
||||||
$hasParentColumn = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!$hasParentColumn) {
|
|
||||||
$db->exec("ALTER TABLE comments ADD COLUMN parent_comment_id INTEGER NULL;");
|
|
||||||
}
|
|
||||||
|
|
||||||
} catch (PDOException $e) {
|
|
||||||
throw new RuntimeException("internal_error");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -59,13 +31,10 @@ class DatabaseCommentManager implements CommentManagerDAO
|
|||||||
private function getConnection()
|
private function getConnection()
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
$dsn = 'sqlite:' . __DIR__ . '/../../db/comments.db';
|
$db = new PDO('sqlite:' . $this->dbPath, null, null);
|
||||||
|
|
||||||
$db = new PDO($dsn, null, null);
|
|
||||||
$db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
|
$db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
|
||||||
|
$db->exec('PRAGMA foreign_keys = ON;');
|
||||||
return $db;
|
return $db;
|
||||||
|
|
||||||
} catch (PDOException $e) {
|
} catch (PDOException $e) {
|
||||||
throw new RuntimeException("internal_error");
|
throw new RuntimeException("internal_error");
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,246 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* Diese Klasse legt einmalig beim allerersten Start in der exakt korrekten Reihenfolge die Tabellen an
|
||||||
|
*
|
||||||
|
* @author Niklas Ortmann
|
||||||
|
*/
|
||||||
|
class DatabaseInitializer {
|
||||||
|
public static function initialize($dbPath) {
|
||||||
|
if (!file_exists($dbPath)) {
|
||||||
|
try {
|
||||||
|
$db = new PDO('sqlite:' . $dbPath);
|
||||||
|
$db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
|
||||||
|
$db->exec('PRAGMA foreign_keys = ON;');
|
||||||
|
|
||||||
|
$db->exec("
|
||||||
|
CREATE TABLE IF NOT EXISTS users (
|
||||||
|
email TEXT PRIMARY KEY,
|
||||||
|
vorname TEXT NOT NULL,
|
||||||
|
nachname TEXT NOT NULL,
|
||||||
|
password TEXT NOT NULL
|
||||||
|
);
|
||||||
|
");
|
||||||
|
|
||||||
|
$db->exec("
|
||||||
|
CREATE TABLE IF NOT EXISTS articles (
|
||||||
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||||
|
title TEXT,
|
||||||
|
content TEXT,
|
||||||
|
author TEXT,
|
||||||
|
category TEXT,
|
||||||
|
tags TEXT,
|
||||||
|
created TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
FOREIGN KEY (author) REFERENCES users(email) ON DELETE SET NULL ON UPDATE CASCADE
|
||||||
|
);
|
||||||
|
");
|
||||||
|
|
||||||
|
$db->exec("
|
||||||
|
CREATE TABLE IF NOT EXISTS likes (
|
||||||
|
article_id INTEGER,
|
||||||
|
user_id TEXT,
|
||||||
|
PRIMARY KEY (article_id, user_id),
|
||||||
|
FOREIGN KEY (article_id) REFERENCES articles(id) ON DELETE CASCADE,
|
||||||
|
FOREIGN KEY (user_id) REFERENCES users(email) ON DELETE CASCADE
|
||||||
|
);
|
||||||
|
");
|
||||||
|
|
||||||
|
$db->exec("
|
||||||
|
CREATE TABLE IF NOT EXISTS comments (
|
||||||
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||||
|
article_id INTEGER NOT NULL,
|
||||||
|
parent_comment_id INTEGER NULL,
|
||||||
|
author TEXT NOT NULL,
|
||||||
|
content TEXT NOT NULL,
|
||||||
|
created TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
FOREIGN KEY (article_id) REFERENCES articles(id) ON DELETE CASCADE,
|
||||||
|
FOREIGN KEY (author) REFERENCES users(email) ON DELETE CASCADE,
|
||||||
|
FOREIGN KEY (parent_comment_id) REFERENCES comments(id) ON DELETE CASCADE
|
||||||
|
);
|
||||||
|
");
|
||||||
|
|
||||||
|
$initializer = new self();
|
||||||
|
$availableEmails = $initializer->seedDummyUsers($db);
|
||||||
|
$initializer->seedDummyArticles($db, $availableEmails);
|
||||||
|
|
||||||
|
unset($db);
|
||||||
|
|
||||||
|
} catch (PDOException $e) {
|
||||||
|
throw new RuntimeException("internal-error");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Erstellt die Dummy-Benutzer in der Datenbank.
|
||||||
|
*
|
||||||
|
* @param PDO $db Die aktive Datenbankverbindung
|
||||||
|
* @return array Liste der erfolgreich angelegten E-Mail-Adressen
|
||||||
|
*/
|
||||||
|
private function seedDummyUsers(PDO $db): array {
|
||||||
|
$dummyUsers = [
|
||||||
|
['email' => 'max.mustermann@web.de', 'vorname' => 'Max', 'nachname' => 'Mustermann'],
|
||||||
|
['email' => 'erika.mustermann@web.de', 'vorname' => 'Erika', 'nachname' => 'Mustermann'],
|
||||||
|
['email' => 'john.doe@gmail.com', 'vorname' => 'John', 'nachname' => 'Doe'],
|
||||||
|
['email' => 'jane.doe@gmail.com', 'vorname' => 'Jane', 'nachname' => 'Doe'],
|
||||||
|
['email' => 'anna.schmidt@gmx.de', 'vorname' => 'Anna', 'nachname' => 'Schmidt'],
|
||||||
|
['email' => 'thomas.mueller@gmx.de', 'vorname' => 'Thomas', 'nachname' => 'Müller'],
|
||||||
|
['email' => 'sabine.fischer@outlook.com', 'vorname' => 'Sabine', 'nachname' => 'Fischer'],
|
||||||
|
['email' => 'michael.weber@outlook.com', 'vorname' => 'Michael', 'nachname' => 'Weber'],
|
||||||
|
['email' => 'julia.wagner@t-online.de', 'vorname' => 'Julia', 'nachname' => 'Wagner'],
|
||||||
|
['email' => 'stefan.becker@t-online.de', 'vorname' => 'Stefan', 'nachname' => 'Becker']
|
||||||
|
];
|
||||||
|
|
||||||
|
$passwordHash = password_hash("test12345", PASSWORD_DEFAULT);
|
||||||
|
$userInsertStmt = $db->prepare("INSERT INTO users (email, vorname, nachname, password) VALUES (:email, :vorname, :nachname, :password);");
|
||||||
|
|
||||||
|
$availableEmails = [];
|
||||||
|
foreach ($dummyUsers as $user) {
|
||||||
|
$userInsertStmt->execute([
|
||||||
|
':email' => $user['email'],
|
||||||
|
':vorname' => $user['vorname'],
|
||||||
|
':nachname' => $user['nachname'],
|
||||||
|
':password' => $passwordHash
|
||||||
|
]);
|
||||||
|
$availableEmails[] = $user['email'];
|
||||||
|
}
|
||||||
|
|
||||||
|
return $availableEmails;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Erstellt die 100 Fachbeiträge in der Datenbank.
|
||||||
|
*
|
||||||
|
* @param PDO $db Die aktive Datenbankverbindung
|
||||||
|
* @param array $availableEmails Pool für die Foreign-Key-Zuweisung des Autors
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
private function seedDummyArticles(PDO $db, array $availableEmails): void
|
||||||
|
{
|
||||||
|
$dummyArticles = [
|
||||||
|
// --- INFORMATIK & MATHE (1-20) ---
|
||||||
|
1 => ["Satz des Pythagoras", "Der Satz des Pythagoras beschreibt das Verhältnis der Seitenlängen in einem rechtwinkligen Dreieck.", "mathe", "Dreiecke, Geometrie"],
|
||||||
|
2 => ["Tunneleffekt", "Der Tunneleffekt beschreibt das quantenmechanische Phänomen, dass Teilchen Barrieren überwinden können.", "physik", "Quantenphysik, Energie"],
|
||||||
|
3 => ["Datenschutz vs Datensicherheit", "Datenschutz schützt Personen, während Datensicherheit Systeme vor unbefugten Zugriffen schützt.", "informatik", "Daten, DSGVO"],
|
||||||
|
4 => ["Einführung in Algorithmen", "Ein Algorithmus ist eine präzise Handlungsanweisung zur Lösung eines vordefinierten Problems.", "informatik", "Code, Logik"],
|
||||||
|
5 => ["Primzahlen im Detail", "Primzahlen sind natürliche Zahlen, die nur durch eins und sich selbst ohne Rest teilbar sind.", "mathe", "Zahlentheorie"],
|
||||||
|
6 => ["Lineare Algebra", "Vektoren und Matrizen bilden das Fundament für moderne Computergrafik und 3D-Engines.", "mathe", "Matrizen, Vektoren"],
|
||||||
|
7 => ["Objektorientierte Programmierung", "Die OOP nutzt Klassen und Objekte, um reale Strukturen im Quellcode abzubilden.", "informatik", "OOP, Klassen"],
|
||||||
|
8 => ["Grundlagen von HTML und CSS", "HTML strukturiert den Inhalt einer Webseite, während CSS für das optische Design zuständig ist.", "informatik", "Web, Frontend"],
|
||||||
|
9 => ["Die Relativitätstheorie", "Einsteins Theorie revolutionierte unser Verständnis von Raum, Zeit und Gravitation massiv.", "physik", "Einstein, Gravitation"],
|
||||||
|
10 => ["Datenbanken und SQL", "Strukturierte Abfragesprachen erlauben das effiziente Speichern und Abrufen großer Datenmengen.", "informatik", "SQL, Datenbank"],
|
||||||
|
11 => ["Wahrscheinlichkeitsrechnung", "Die Stochastik befasst sich mit der mathematischen Analyse von Zufallsexperimenten.", "mathe", "Zufall, Stochastik"],
|
||||||
|
12 => ["Quantencomputing", "Künftige Quantencomputer nutzen Qubits, um komplexe Berechnungen in Rekordzeit zu lösen.", "informatik", "Hardware, Zukunft"],
|
||||||
|
13 => ["Die Fibonacci-Folge", "Diese Zahlenreihe beschreibt mathematische Wachstumsmuster, die oft in der Natur vorkommen.", "mathe", "Zahlen, Natur"],
|
||||||
|
14 => ["Thermodynamik", "Die Hauptsätze der Thermodynamik regeln den Energieaustausch und die Entropie in Systemen.", "physik", "Energie, Wärme"],
|
||||||
|
15 => ["Cybersecurity Grundlagen", "Die Absicherung von Netzwerken erfordert Firewalls, Verschlüsselung und regelmäßige Audits.", "informatik", "Sicherheit, Hacker"],
|
||||||
|
16 => ["Kryptographie", "Asymmetrische Verschlüsselungsverfahren sichern heutzutage den gesamten Datenverkehr im Web.", "informatik", "Krypto, Security"],
|
||||||
|
17 => ["Analysis und Ableitungen", "Die Differentialrechnung untersucht die lokalen Änderungsraten von mathematischen Funktionen.", "mathe", "Analysis, Funktionen"],
|
||||||
|
18 => ["Schwarze Löcher", "Diese Regionen im Raum besitzen eine so starke Gravitation, dass selbst Licht nicht entkommt.", "physik", "Astronomie, Kosmos"],
|
||||||
|
19 => ["Git Versionsverwaltung", "Git erlaubt Entwicklern das parallele Arbeiten an Code-Projekten ohne Datenverlust.", "informatik", "Git, DevOps"],
|
||||||
|
20 => ["Künstliche Intelligenz", "Neuronale Netze versuchen das menschliche Gehirn für maschinelles Lernen nachzubilden.", "informatik", "KI, Software"],
|
||||||
|
|
||||||
|
// --- CHEMIE & BIOLOGIE (21-40) ---
|
||||||
|
21 => ["Das Periodensystem", "Die Elemente sind nach ihrer Ordnungszahl und chemischen Eigenschaften geordnet.", "chemie", "Elemente, Moleküle"],
|
||||||
|
22 => ["Photosynthese", "Pflanzen wandeln mithilfe von Sonnenlicht Kohlendioxid und Wasser in Glukose und Sauerstoff um.", "biologie", "Pflanzen, Energie"],
|
||||||
|
23 => ["Aufbau einer Zelle", "Die Zelle ist die kleinste lebende Einheit aller Organismen mit spezialisierten Organellen.", "biologie", "Zellen, Biologie"],
|
||||||
|
24 => ["Säuren und Basen", "Der pH-Wert misst die Konzentration von Wasserstoff-Ionen in einer wässrigen Lösung.", "chemie", "Labor, pH-Wert"],
|
||||||
|
25 => ["Die DNA-Struktur", "Die Doppelhelix enthält den genetischen Bauplan für die Entwicklung aller Lebewesen.", "biologie", "Genetik, Erbgut"],
|
||||||
|
26 => ["Chemische Bindungen", "Kovalente Bindungen entstehen durch das Teilen von Elektronenpaaren zwischen Atomen.", "chemie", "Atome, Bindung"],
|
||||||
|
27 => ["Das Immunsystem", "Weiße Blutkörperchen und Antikörper schützen den menschlichen Körper vor Krankheitserregern.", "biologie", "Gesundheit, Abwehr"],
|
||||||
|
28 => ["Katalysatoren", "Katalysatoren beschleunigen chemische Reaktionen, ohne dabei selbst verbraucht zu werden.", "chemie", "Reaktion, Chemie"],
|
||||||
|
29 => ["Ökosystem Wald", "Das Zusammenspiel von Flora, Fauna und Klima bildet ein hochsensibles ökologisches System.", "biologie", "Natur, Wald"],
|
||||||
|
30 => ["Die Mendelschen Regeln", "Diese Grundgesetze der Vererbung beschreiben, wie Merkmale an Nachkommen weitergegeben werden.", "biologie", "Genetik, Erbung"],
|
||||||
|
31 => ["Zustandsformen der Materie", "Fest, flüssig und gasförmig sind die klassischen Aggregatzustände von Stoffen.", "chemie", "Physik, Materie"],
|
||||||
|
32 => ["Evolutionstheorie", "Charles Darwin begründete die Theorie der natürlichen Auslese und Anpassung von Arten.", "biologie", "Darwin, Evolution"],
|
||||||
|
33 => ["Organische Chemie", "Die Chemie der Kohlenstoffverbindungen bildet die Basis für alles bekannte Leben.", "chemie", "Kohlenstoff, Chemie"],
|
||||||
|
34 => ["Das menschliche Gehirn", "Milliarden von Neuronen kommunizieren über Synapsen, um Reize und Gedanken zu verarbeiten.", "biologie", "Neurologie, Nerven"],
|
||||||
|
35 => ["Der Wasserkreislauf", "Verdunstung, Kondensation und Niederschlag halten das Wasser auf der Erde in Bewegung.", "geographie", "Wasser, Klima"],
|
||||||
|
36 => ["Aggregatzustände von Wasser", "Wasser zeigt ungewöhnliche Eigenschaften wie die Dichteanomalie beim Gefrieren.", "chemie", "Wasser, Eis"],
|
||||||
|
37 => ["Blutkreislauf des Menschen", "Das Herz pumpt sauerstoffreiches Blut durch Arterien in alle Organe des Körpers.", "biologie", "Herz, Medizin"],
|
||||||
|
38 => ["Das Ohmsche Gesetz", "Es beschreibt den direkten Zusammenhang zwischen Spannung, Stromstärke und Widerstand.", "physik", "Strom, Elektronik"],
|
||||||
|
39 => ["Plattentektonik", "Die Bewegung der Kontinentalplatten führt zu Erdbeben, Vulkanismus und Gebirgsbildung.", "geographie", "Erde, Geologie"],
|
||||||
|
40 => ["Proteine und Enzyme", "Enzyme wirken als Biokatalysatoren und steuern fast alle Stoffwechselprozesse.", "biologie", "Biochemie, Enzyme"],
|
||||||
|
|
||||||
|
// --- GESCHICHTE & WIRTSCHAFT (41-60) ---
|
||||||
|
41 => ["Das Römische Reich", "Vom Stadtstaat zum Weltreich prägte Rom die Rechts- und Kulturgeschichte Europas.", "geschichte", "Antike, Rom"],
|
||||||
|
42 => ["Die Französische Revolution", "Freiheit, Gleichheit, Brüderlichkeit beendeten 1789 die absolute Monarchie in Frankreich.", "geschichte", "Europa, Politik"],
|
||||||
|
43 => ["Inflation erklärt", "Inflation bezeichnet die kontinuierliche Geldentwertung und den Kaufkraftverlust.", "wirtschaft", "Geld, Finanzen"],
|
||||||
|
44 => ["Die Industrielle Revolution", "Die Erfindung der Dampfmaschine veränderte die Produktion und die Gesellschaft tiefgreifend.", "geschichte", "Industrie, Arbeit"],
|
||||||
|
45 => ["Angebot und Nachfrage", "Dieses fundamentale Marktgesetz bestimmt den Preis von Gütern in einer freien Wirtschaft.", "wirtschaft", "Markt, Preise"],
|
||||||
|
46 => ["Der Buchdruck", "Johannes Gutenbergs Erfindung revolutionierte die Verbreitung von Wissen im Mittelalter.", "geschichte", "Medien, Wissen"],
|
||||||
|
47 => ["Die Entdeckung Amerikas", "Kolumbus' Seereise im Jahr 1492 leitete das Zeitalter der Kolonialisierung ein.", "geschichte", "Entdeckung, Seefahrt"],
|
||||||
|
48 => ["Kryptowährungen", "Bitcoin nutzt Blockchain-Technologie, um dezentralen digitalen Werttransfer zu erlauben.", "wirtschaft", "Blockchain, Finanzen"],
|
||||||
|
49 => ["Das antike Griechenland", "Die Wiege der Demokratie und Philosophie brachte Denker wie Sokrates und Platon hervor.", "geschichte", "Antike, Philosophie"],
|
||||||
|
50 => ["Globalisierung", "Die weltweite Verflechtung in Wirtschaft, Kultur und Politik bringt Chancen und Risiken.", "wirtschaft", "Weltwirtschaft, Handel"],
|
||||||
|
51 => ["Der Dreißigjährige Krieg", "Ein religiöser und politischer Konflikt verwüstete zwischen 1618 und 1648 Mitteleuropa.", "geschichte", "Krieg, Europa"],
|
||||||
|
52 => ["Die Weimarer Republik", "Die erste deutsche Demokratie scheiterte an wirtschaftlichen und politischen Krisen.", "geschichte", "Deutschland, Weimar"],
|
||||||
|
53 => ["Aktien und Börse", "Unternehmen beschaffen sich Kapital durch die Ausgabe von Anteilen an Investoren.", "wirtschaft", "Aktien, Investieren"],
|
||||||
|
54 => ["Das alte Ägypten", "Pharaonen, Pyramiden und Hieroglyphen zeugen von einer hochentwickelten Hochkultur am Nil.", "geschichte", "Ägypten, Antike"],
|
||||||
|
55 => ["Der Kalte Krieg", "Das Wettrüsten zwischen USA und UdSSR prägte die globale Politik der Nachkriegszeit.", "geschichte", "Ost-West, Politik"],
|
||||||
|
56 => ["Zentralbanken und Leitzins", "Durch Zinsänderungen steuern Notenbanken die Geldmenge und bekämpfen Inflation.", "wirtschaft", "Zinsen, Geldpolitik"],
|
||||||
|
57 => ["Die Seidenstraße", "Das historische Netzwerk von Handelsrouten verband über Jahrhunderte Asien und Europa.", "geschichte", "Handel, Asien"],
|
||||||
|
58 => ["Das Mittelalter", "Ritter, Burgen und das Feudalsystem prägten diese tausendjährige Epoche Europas.", "geschichte", "Mittelalter, Feudalismus"],
|
||||||
|
59 => ["Planwirtschaft vs Marktwirtschaft", "Zentrale staatliche Steuerung steht dem freien Spiel der Marktkräfte gegenüber.", "wirtschaft", "Systeme, Wirtschaft"],
|
||||||
|
60 => ["Die Berliner Mauer", "Ihr Bau 1961 zementierte die Teilung Deutschlands, ihr Fall 1989 beendete sie.", "geschichte", "DDR, Wiedervereinigung"],
|
||||||
|
|
||||||
|
// --- ANWENDUNGEN & WEITERE THEMEN (61-100) ---
|
||||||
|
61 => ["Cloud Computing", "Das Auslagern von Rechenleistung in das Internet spart lokale IT-Infrastruktur ein.", "informatik", "Cloud, Web"],
|
||||||
|
62 => ["Responsive Webdesign", "Moderne Webseiten passen ihr Layout dynamisch an Smartphones und Desktops an.", "informatik", "Design, CSS"],
|
||||||
|
63 => ["Der Treibhauseffekt", "Gase in der Atmosphäre verhindern das Entweichen von Wärme ins Weltall.", "physik", "Klima, Umwelt"],
|
||||||
|
64 => ["Mechanik und Kräfte", "Die Newtonschen Axiome beschreiben, wie Kräfte auf Körper wirken und sie bewegen.", "physik", "Newton, Kraft"],
|
||||||
|
65 => ["Die Mendelschen Gesetze", "Die Vererbung von Genen folgt klaren statistischen Wahrscheinlichkeiten.", "biologie", "Genetik, Erbsen"],
|
||||||
|
66 => ["Lichtgeschwindigkeit", "Im Vakuum bewegt sich Licht mit knapp 300.000 Kilometern pro Sekunde.", "physik", "Licht, Relativität"],
|
||||||
|
67 => ["Die Funktion von APIs", "Programmierschnittstellen erlauben den Datenaustausch zwischen verschiedenen Systemen.", "informatik", "API, Schnittstelle"],
|
||||||
|
68 => ["Der Goldstandard", "Ein historischen Währungssystem, bei dem Geld durch echtes Gold gedeckt war.", "wirtschaft", "Gold, Währung"],
|
||||||
|
69 => ["Der Wiener Kongress", "1815 ordneten die europäischen Mächte die Landkarte nach den Napoleonischen Kriegen neu.", "geschichte", "Europa, Diplomatie"],
|
||||||
|
70 => ["Integrierte Schaltkreise", "Mikrochips enthalten Millionen Transistoren auf kleinstem Raum für Logikschaltungen.", "informatik", "Hardware, Chips"],
|
||||||
|
71 => ["Die Magellan-Expedition", "Die erste erfolgreiche Weltumsegelung bewies praktisch die Kugelgestalt der Erde.", "geschichte", "Seefahrt, Erde"],
|
||||||
|
72 => ["Das Internet der Dinge", "Alltagsgegenstände werden vernetzt, um smarte Automatisierungen zu ermöglichen.", "informatik", "IoT, SmartHome"],
|
||||||
|
73 => ["Halbwertszeit", "Die Zeitspanne, in der sich die Hälfte der instabilen Atome radioaktiv abbaut.", "physik", "Atomphysik, Strahlung"],
|
||||||
|
74 => ["Elektromagnetismus", "Die Verknüpfung von elektrischen Strömen und magnetischen Feldern treibt Motoren an.", "physik", "Strom, Magnet"],
|
||||||
|
75 => ["Das Ökosystem Meer", "Ozeane regulieren das Weltklima und bieten Lebensraum für unzählige Arten.", "biologie", "Meer, Ökologie"],
|
||||||
|
76 => ["Einführung in Docker", "Containerisierung isoliert Anwendungen samt Abhängigkeiten für stabilen Betrieb.", "informatik", "Docker, DevOps"],
|
||||||
|
77 => ["Das Römische Recht", "Viele moderne europäische Gesetzbücher basieren auf antiken römischen Rechtsprinzipien.", "geschichte", "Recht, Gesetz"],
|
||||||
|
78 => ["Verhaltensbiologie", "Untersuchung von angeborenen und erlernten Verhaltensweisen bei Mensch und Tier.", "biologie", "Verhalten, Tiere"],
|
||||||
|
79 => ["Verschlüsselung im Alltag", "HTTPS schützt Passwörter und Zahlungsdaten beim Surfen vor dem Mitlesen.", "informatik", "Web, HTTPS"],
|
||||||
|
80 => ["Die Magna Carta", "1215 schränkte dieses Dokument die absolute Macht des englischen Königs ein.", "geschichte", "England, Verfassung"],
|
||||||
|
81 => ["Marktversagen", "Wenn der freie Markt Ressourcen unvollständig verteilt, muss der Staat eingreifen.", "wirtschaft", "Markt, Staat"],
|
||||||
|
82 => ["Optische Linsen", "Konvexe und konkave Linsen brechen Licht für Brillen, Mikroskope und Kameras.", "physik", "Optik, Licht"],
|
||||||
|
83 => ["Die Entstehung der Erde", "Vor rund 4,5 Milliarden Jahren ballte sich kosmischer Staub zu unserem Planeten.", "geographie", "Erde, Kosmos"],
|
||||||
|
84 => ["Grundlagen von JavaScript", "Diese Skriptsprache macht statische Webseiten interaktiv und dynamisch nutzbar.", "informatik", "JS, Webentwicklung"],
|
||||||
|
85 => ["Die industrielle Landwirtschaft", "Moderne Techniken sichern Welternährung, belasten jedoch oft die Umwelt.", "biologie", "Landwirtschaft, Umwelt"],
|
||||||
|
86 => ["Das Schwarze Jahr 1929", "Der New Yorker Börsencrash löste die verheerende Weltwirtschaftskrise aus.", "geschichte", "Krise, Finanzen"],
|
||||||
|
87 => ["Die Evolution des Menschen", "Der Stammbaum des Homo Sapiens entwickelte sich über Millionen Jahre in Afrika.", "biologie", "Mensch, Evolution"],
|
||||||
|
88 => ["Einführung in Linux", "Das Open-Source-Betriebssystem bildet das Rückgrat moderner Server-Infrastrukturen.", "informatik", "Linux, OS"],
|
||||||
|
89 => ["Der Urknall", "Die Urknalltheorie beschreibt den Beginn des Universums aus einer Singularität.", "physik", "Astronomie, Urknall"],
|
||||||
|
90 => ["Das Periodensystem der Elemente", "Dmitri Mendelejew ordnete Elemente logisch nach ihren Atommassen.", "chemie", "Periodensystem, Chemie"],
|
||||||
|
91 => ["Die Hanse", "Ein mächtiger mittelalterlicher Bund von Kaufleuten dominierte den Nordseehandel.", "geschichte", "Handel, Mittelalter"],
|
||||||
|
92 => ["Wirtschaftswachstum", "Die Steigerung des Bruttoinlandsprodukts gilt oft als Indikator für Wohlstand.", "wirtschaft", "BIP, Finanzen"],
|
||||||
|
93 => ["Neuronale Netze", "Diese Strukturen lernen durch mathematische Gewichtung aus riesigen Datenmengen.", "informatik", "KI, Mathematik"],
|
||||||
|
94 => ["Der Erste Weltkrieg", "Der globale Konflikt von 1914 bis 1918 zerstörte das alte europäische Machtgefüge.", "geschichte", "Europa, Krieg"],
|
||||||
|
95 => ["Das Gehirn und Hormone", "Botenstoffe steuern Gefühle, Schlafzyklen und Reaktionen des Körpers.", "biologie", "Medizin, Hormone"],
|
||||||
|
96 => ["SQL Joins erklärt", "Joins verknüpfen Daten aus mehreren Tabellen über gemeinsame Schlüssel.", "informatik", "SQL, Datenbanken"],
|
||||||
|
97 => ["Wellen-Teilchen-Dualismus", "Quantenobjekte zeigen je nach Messaufbau Eigenschaften von Wellen oder Teilchen.", "physik", "Quanten, Licht"],
|
||||||
|
98 => ["Der Absolutismus", "Der Sonnenkönig Ludwig XIV. verkörperte die unbeschränkte Herrschaft des Monarchen.", "geschichte", "Frankreich, Monarchie"],
|
||||||
|
99 => ["Die Funktion von Routern", "Netzwerkgeräte leiten Datenpakete über IP-Adressen an den richtigen Empfänger.", "informatik", "Netzwerk, Internet"],
|
||||||
|
100 => ["Die Entstehung des Geldes", "Vom Tauschhandel über Naturalgeld bis hin zu modernen digitalen Fiat-Währungen.", "wirtschaft", "Geld, Geschichte"]
|
||||||
|
|
||||||
|
];
|
||||||
|
$articleInsertStmt = $db->prepare("
|
||||||
|
INSERT INTO articles (title, content, author, category, tags)
|
||||||
|
VALUES (:title, :content, :author, :category, :tags);");
|
||||||
|
|
||||||
|
foreach ($dummyArticles as $article) {
|
||||||
|
// Bestimmt per Zufall einen Autor aus dem Pool der gültigen E-Mails
|
||||||
|
$randomAuthor = $availableEmails[array_rand($availableEmails)];
|
||||||
|
|
||||||
|
$articleInsertStmt->execute([
|
||||||
|
':title' => $article[0],
|
||||||
|
':content' => $article[1],
|
||||||
|
':author' => $randomAuthor,
|
||||||
|
':category' => $article[2],
|
||||||
|
':tags' => $article[3]
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
<?php
|
<?php
|
||||||
|
require_once 'DatabaseInitializer.php';
|
||||||
require_once "UserManagerDAO.php";
|
require_once "UserManagerDAO.php";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -11,6 +11,7 @@ require_once "UserManagerDAO.php";
|
|||||||
class DatabaseUserManager implements UserManagerDAO {
|
class DatabaseUserManager implements UserManagerDAO {
|
||||||
|
|
||||||
private static $instance = null;
|
private static $instance = null;
|
||||||
|
private $dbPath;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Konstruktor.
|
* Konstruktor.
|
||||||
@@ -22,23 +23,8 @@ class DatabaseUserManager implements UserManagerDAO {
|
|||||||
*/
|
*/
|
||||||
public function __construct()
|
public function __construct()
|
||||||
{
|
{
|
||||||
try {
|
$this->dbPath = __DIR__ . '/../../db/eduforgeDB.db';
|
||||||
$db = $this->getConnection();
|
DatabaseInitializer::initialize($this->dbPath);
|
||||||
|
|
||||||
$db->exec("
|
|
||||||
CREATE TABLE IF NOT EXISTS users (
|
|
||||||
email TEXT PRIMARY KEY,
|
|
||||||
vorname TEXT NOT NULL,
|
|
||||||
nachname TEXT NOT NULL,
|
|
||||||
password TEXT NOT NULL
|
|
||||||
);
|
|
||||||
");
|
|
||||||
|
|
||||||
unset($db);
|
|
||||||
|
|
||||||
} catch (PDOException $e) {
|
|
||||||
throw new RuntimeException("Benutzerdatenbank konnte nicht erstellt werden.");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -51,15 +37,12 @@ class DatabaseUserManager implements UserManagerDAO {
|
|||||||
private function getConnection()
|
private function getConnection()
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
$dsn = 'sqlite:' . __DIR__ . '/../../db/users.db';
|
$db = new PDO('sqlite:' . $this->dbPath, null, null);
|
||||||
|
|
||||||
$db = new PDO($dsn, null, null);
|
|
||||||
$db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
|
$db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
|
||||||
|
$db->exec('PRAGMA foreign_keys = ON;');
|
||||||
return $db;
|
return $db;
|
||||||
|
|
||||||
} catch (PDOException $e) {
|
} catch (PDOException $e) {
|
||||||
throw new RuntimeException("Verbindung zur Benutzerdatenbank fehlgeschlagen.");
|
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(
|
||||||
|
|||||||
@@ -12,38 +12,8 @@ require_once "DatabaseUserManager.php";
|
|||||||
class UserManager {
|
class UserManager {
|
||||||
|
|
||||||
public static function getInstance() {
|
public static function getInstance() {
|
||||||
$userManager = DatabaseUserManager::getInstance();
|
|
||||||
|
|
||||||
/*
|
return DatabaseUserManager::getInstance();
|
||||||
* Dummy-User anlegen, falls sie noch nicht existieren.
|
|
||||||
* Passwort für alle User: test12345
|
|
||||||
*/
|
|
||||||
$dummyUsers = [
|
|
||||||
['email' => 'max.mustermann@web.de', 'vorname' => 'Max', 'nachname' => 'Mustermann'],
|
|
||||||
['email' => 'erika.mustermann@web.de', 'vorname' => 'Erika', 'nachname' => 'Mustermann'],
|
|
||||||
['email' => 'john.doe@gmail.com', 'vorname' => 'John', 'nachname' => 'Doe'],
|
|
||||||
['email' => 'jane.doe@gmail.com', 'vorname' => 'Jane', 'nachname' => 'Doe'],
|
|
||||||
['email' => 'anna.schmidt@gmx.de', 'vorname' => 'Anna', 'nachname' => 'Schmidt'],
|
|
||||||
['email' => 'thomas.mueller@gmx.de', 'vorname' => 'Thomas', 'nachname' => 'Müller'],
|
|
||||||
['email' => 'sabine.fischer@outlook.com', 'vorname' => 'Sabine', 'nachname' => 'Fischer'],
|
|
||||||
['email' => 'michael.weber@outlook.com', 'vorname' => 'Michael', 'nachname' => 'Weber'],
|
|
||||||
['email' => 'julia.wagner@t-online.de', 'vorname' => 'Julia', 'nachname' => 'Wagner'],
|
|
||||||
['email' => 'stefan.becker@t-online.de', 'vorname' => 'Stefan', 'nachname' => 'Becker']
|
|
||||||
];
|
|
||||||
|
|
||||||
$passwordHash = password_hash("test12345", PASSWORD_DEFAULT);
|
|
||||||
|
|
||||||
foreach ($dummyUsers as $user) {
|
|
||||||
if ($userManager->findUser($user['email']) == null) {
|
|
||||||
$userManager->addUser(
|
|
||||||
$user['email'],
|
|
||||||
$user['vorname'],
|
|
||||||
$user['nachname'],
|
|
||||||
$passwordHash
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return $userManager;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -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