Compare commits
113 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| c117c7e641 | |||
| 01fc8537cf | |||
| 12ad9759cf | |||
| 057639be7c | |||
| 87e7be1c10 | |||
| 1db77346d0 | |||
| 1e6ac0fcd9 | |||
| b3c74c2172 | |||
| 0805958033 | |||
| 5c0e978c93 | |||
| 520298bae4 | |||
| 969adc4836 | |||
| d9eca06197 | |||
| 48f2e90e49 | |||
| 358247a2a1 | |||
| de4d2fe5c0 | |||
| e1102eb7db | |||
| a8df9590fd | |||
| 3e453e22ec | |||
| beeab0ec90 | |||
| 9353a7eaaa | |||
| cac8f3046d | |||
| 66eeac372c | |||
| 4a6a3cf708 | |||
| cb44c3f5a8 | |||
| eb0a34e959 | |||
| 8d683cc1a0 | |||
| ee41dd4cfe | |||
| 3d50e6e3b3 | |||
| 4f8d11881d | |||
| 5c924d3277 | |||
| 7f703a8386 | |||
| f063ea4741 | |||
| ca643dd298 | |||
| e19225e49e | |||
| 9ffbca679e | |||
| d90a10e462 | |||
| b6f25d041b | |||
| 4321c4bee5 | |||
| d1b3641754 | |||
| 5281c1bc7d | |||
| aae78bf479 | |||
| af17764346 | |||
| a8d321609d | |||
| 23ccbe2d02 | |||
| 47239016c5 | |||
| f4f6960b9d | |||
| c4c299eed6 | |||
| e20b59b43d | |||
| 901232b683 | |||
| 40c5fa69f5 | |||
| 7694396454 | |||
| dafc149bba | |||
| f23ec27247 | |||
| efb179dec2 | |||
| 86222aa92e | |||
| a5fb5cd360 | |||
| 6f6e53a483 | |||
| f13a2c6f1e | |||
| 59012cacfb | |||
| 21c5471d73 | |||
| e1ee70b46f | |||
| eb56b8d6a0 | |||
| d597046f41 | |||
| ae1c873536 | |||
| 1f7a2a77f6 | |||
| 9269465f6b | |||
| 883adfd242 | |||
| 1e890f9a28 | |||
| 2bb13d2e8c | |||
| cb02e05d55 | |||
| 05777f6109 | |||
| f3f042b3b9 | |||
| dfb13b1f96 | |||
| 6fa5038589 | |||
| 55f53e3346 | |||
| f36037d36d | |||
| 500bca80c1 | |||
| fc0361ed3a | |||
| df1485ce49 | |||
| b8bceb31c6 | |||
| f0308c3505 | |||
| e7ece1ed13 | |||
| 8ed7f7ec8f | |||
| feaef4a624 | |||
| 7d9aae2a59 | |||
| a88f8ca638 | |||
| be6a5f9add | |||
| ed4498578d | |||
| 118be50b3c | |||
| 1e2184ae20 | |||
| 8c1e1a64bf | |||
| b9c28968fe | |||
| 703d8d7519 | |||
| 8e52d99ba4 | |||
| d4020671c1 | |||
| 5c432e5801 | |||
| aac13297de | |||
| f60a4a4f60 | |||
| 0ed4de74b0 | |||
| 48bd26342a | |||
| b8e884a5aa | |||
| 1dbef5ca4b | |||
| e43ae7f96a | |||
| eab256d17f | |||
| bbdcbbf0ce | |||
| ec9bc3fe1f | |||
| 80f92a384e | |||
| f9c1c67a38 | |||
| f2ef3c08a7 | |||
| 8a66e8af3f | |||
| 41d1cc6013 | |||
| c9c6a4ae81 |
@@ -7,7 +7,7 @@
|
||||
|
||||
## Login-Informationen für Dummy-User
|
||||
`Anmeldename, Passwort, Mailadresse`:
|
||||
- `max.mustermann, test12345, mustermann@web.de`
|
||||
- `max.mustermann, test12345, max.mustermann@web.de`
|
||||
|
||||
## Weitere Voraussetzungen zur Nutzung
|
||||
- Per Klick auf das Logo gelangt man auf die Home-Seite.
|
||||
@@ -18,6 +18,9 @@
|
||||
- Bitte auf die gesetzten TODO's achten. Wenn Inhalte fehlen, sind sie i.d.R. als TODO kommentiert.
|
||||
- Die Suchseite und Kategorieseite packen momentan alle passenden Beiträge untereinander. Später sollen zunächst 10
|
||||
Ergebnisse auf einer Seite angezeigt werden.
|
||||
- Wenn ein Bild aus einem Beitrag entfernt wird, dann wird noch nicht die Datei im Pfad /uploads gelöscht.
|
||||
- id in showArticle-controller.php und updateArticle-controller.php wird nicht als gültige numerische ID geprüft.
|
||||
- sort in search-results-controller.php wird nicht gegen erlaubte Werte validiert.
|
||||
|
||||
## Besonderheiten des Projektes
|
||||
- Es wurde ein einfacher Beitrags-Editor erstellt. Mit diesem können Beiträge erstellt oder bearbeitet werden.
|
||||
|
||||
+214
-28
@@ -17,8 +17,28 @@ if (!isset($_SESSION["user"])) {
|
||||
<input type="text" id="title" name="title"
|
||||
value="<?php echo htmlspecialchars($_SESSION['old_title'] ?? ''); unset($_SESSION['old_title']); ?>"
|
||||
placeholder="Titel hier eingeben" required>
|
||||
<textarea id="content" name="content" placeholder="Schreibe deinen Beitrag..."><?php if (isset($_SESSION['old_content']) && !empty($_SESSION['old_content'])){echo htmlspecialchars($_SESSION['old_content']); unset($_SESSION['old_content']);}elseif (isset($content) && !empty($content)){echo htmlspecialchars($content);}?></textarea>
|
||||
|
||||
<!-- Hier werden die dynamischen divs via JavaScript eingefügt -->
|
||||
<div id="block-container"></div>
|
||||
|
||||
<!-- Plus-Button und das Pop-up-Menü -->
|
||||
<div id="add-block-control" class="article-editor-scope add-block-control">
|
||||
<button type="button" id="plus-button" class="article-editor-scope plus-button">+</button>
|
||||
<div id="block-popup" class="article-editor-scope block-popup hidden">
|
||||
<button type="button" data-type="text">Textblock</button>
|
||||
<button type="button" data-type="image">Bild einfügen</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Unsichtbares Textfeld, das die JSON-Daten hält und an den Controller postet -->
|
||||
<textarea id="content" name="content" style="display:none;"><?php
|
||||
if (isset($_SESSION['old_content']) && !empty($_SESSION['old_content'])){
|
||||
echo htmlspecialchars($_SESSION['old_content']);
|
||||
unset($_SESSION['old_content']);
|
||||
} else {
|
||||
echo '[]'; // Standardmäßig ein leeres JSON-Array
|
||||
}
|
||||
?></textarea>
|
||||
</main>
|
||||
|
||||
<!-- Seitenleiste -->
|
||||
@@ -31,45 +51,211 @@ if (!isset($_SESSION["user"])) {
|
||||
<div class="sidebar-block">
|
||||
<label for="category">Kategorie <span class="required">*</span></label>
|
||||
<select id="category" name="category" required>
|
||||
<option value="" disabled selected>Kategorie wählen...</option>
|
||||
<option disabled <?php
|
||||
if ((!isset($_SESSION['old_category']) || empty($_SESSION['old_category'])) && (!isset($category) || empty($category))) {
|
||||
echo 'selected';
|
||||
}
|
||||
?>>Kategorie wählen...</option>
|
||||
|
||||
<optgroup label="Sprachen">
|
||||
<option value="deutsch">Deutsch</option>
|
||||
<option value="englisch">Englisch</option>
|
||||
<option value="franzoesisch">Französisch</option>
|
||||
<option value="latein">Latein</option>
|
||||
<option value="literatur">Literatur</option>
|
||||
<option value="deutsch" <?php
|
||||
if (isset($_SESSION['old_category']) && !empty($_SESSION['old_category'])) {
|
||||
if ($_SESSION['old_category'] === 'deutsch') { echo 'selected'; }
|
||||
} elseif (isset($category) && !empty($category)) {
|
||||
if ($category === 'deutsch') { echo 'selected'; }
|
||||
}
|
||||
?>>Deutsch</option>
|
||||
<option value="englisch" <?php
|
||||
if (isset($_SESSION['old_category']) && !empty($_SESSION['old_category'])) {
|
||||
if ($_SESSION['old_category'] === 'englisch') { echo 'selected'; }
|
||||
} elseif (isset($category) && !empty($category)) {
|
||||
if ($category === 'englisch') { echo 'selected'; }
|
||||
}
|
||||
?>>Englisch</option>
|
||||
<option value="franzoesisch" <?php
|
||||
if (isset($_SESSION['old_category']) && !empty($_SESSION['old_category'])) {
|
||||
if ($_SESSION['old_category'] === 'franzoesisch') { echo 'selected'; }
|
||||
} elseif (isset($category) && !empty($category)) {
|
||||
if ($category === 'franzoesisch') { echo 'selected'; }
|
||||
}
|
||||
?>>Französisch</option>
|
||||
<option value="latein" <?php
|
||||
if (isset($_SESSION['old_category']) && !empty($_SESSION['old_category'])) {
|
||||
if ($_SESSION['old_category'] === 'latein') { echo 'selected'; }
|
||||
} elseif (isset($category) && !empty($category)) {
|
||||
if ($category === 'latein') { echo 'selected'; }
|
||||
}
|
||||
?>>Latein</option>
|
||||
<option value="literatur" <?php
|
||||
if (isset($_SESSION['old_category']) && !empty($_SESSION['old_category'])) {
|
||||
if ($_SESSION['old_category'] === 'literatur') { echo 'selected'; }
|
||||
} elseif (isset($category) && !empty($category)) {
|
||||
if ($category === 'literatur') { echo 'selected'; }
|
||||
}
|
||||
?>>Literatur</option>
|
||||
</optgroup>
|
||||
|
||||
<optgroup label="MINT">
|
||||
<option value="mathe">Mathematik</option>
|
||||
<option value="biologie">Biologie</option>
|
||||
<option value="chemie">Chemie</option>
|
||||
<option value="physik">Physik</option>
|
||||
<option value="informatik">Informatik</option>
|
||||
<option value="astronomie">Astronomie</option>
|
||||
<option value="mathe" <?php
|
||||
if (isset($_SESSION['old_category']) && !empty($_SESSION['old_category'])) {
|
||||
if ($_SESSION['old_category'] === 'mathe') { echo 'selected'; }
|
||||
} elseif (isset($category) && !empty($category)) {
|
||||
if ($category === 'mathe') { echo 'selected'; }
|
||||
}
|
||||
?>>Mathematik</option>
|
||||
<option value="biologie" <?php
|
||||
if (isset($_SESSION['old_category']) && !empty($_SESSION['old_category'])) {
|
||||
if ($_SESSION['old_category'] === 'biologie') { echo 'selected'; }
|
||||
} elseif (isset($category) && !empty($category)) {
|
||||
if ($category === 'biologie') { echo 'selected'; }
|
||||
}
|
||||
?>>Biologie</option>
|
||||
<option value="chemie" <?php
|
||||
if (isset($_SESSION['old_category']) && !empty($_SESSION['old_category'])) {
|
||||
if ($_SESSION['old_category'] === 'chemie') { echo 'selected'; }
|
||||
} elseif (isset($category) && !empty($category)) {
|
||||
if ($category === 'chemie') { echo 'selected'; }
|
||||
}
|
||||
?>>Chemie</option>
|
||||
<option value="physik" <?php
|
||||
if (isset($_SESSION['old_category']) && !empty($_SESSION['old_category'])) {
|
||||
if ($_SESSION['old_category'] === 'physik') { echo 'selected'; }
|
||||
} elseif (isset($category) && !empty($category)) {
|
||||
if ($category === 'physik') { echo 'selected'; }
|
||||
}
|
||||
?>>Physik</option>
|
||||
<option value="informatik" <?php
|
||||
if (isset($_SESSION['old_category']) && !empty($_SESSION['old_category'])) {
|
||||
if ($_SESSION['old_category'] === 'informatik') { echo 'selected'; }
|
||||
} elseif (isset($category) && !empty($category)) {
|
||||
if ($category === 'informatik') { echo 'selected'; }
|
||||
}
|
||||
?>>Informatik</option>
|
||||
<option value="astronomie" <?php
|
||||
if (isset($_SESSION['old_category']) && !empty($_SESSION['old_category'])) {
|
||||
if ($_SESSION['old_category'] === 'astronomie') { echo 'selected'; }
|
||||
} elseif (isset($category) && !empty($category)) {
|
||||
if ($category === 'astronomie') { echo 'selected'; }
|
||||
}
|
||||
?>>Astronomie</option>
|
||||
</optgroup>
|
||||
|
||||
<optgroup label="Gesellschaft & Werte">
|
||||
<option value="geschichte">Geschichte</option>
|
||||
<option value="erdkunde">Erdkunde</option>
|
||||
<option value="sozialkunde">Sozialkunde</option>
|
||||
<option value="wirtschaft">Wirtschaftskunde</option>
|
||||
<option value="religion">Religion</option>
|
||||
<option value="ethik">Ethikunterricht</option>
|
||||
<option value="philosophie">Philosophie</option>
|
||||
<option value="psychologie">Psychologie</option>
|
||||
<option value="kunst">Kunst</option>
|
||||
<option value="musik">Musik</option>
|
||||
<option value="theater">Theater</option>
|
||||
<option value="geschichte" <?php
|
||||
if (isset($_SESSION['old_category']) && !empty($_SESSION['old_category'])) {
|
||||
if ($_SESSION['old_category'] === 'geschichte') { echo 'selected'; }
|
||||
} elseif (isset($category) && !empty($category)) {
|
||||
if ($category === 'geschichte') { echo 'selected'; }
|
||||
}
|
||||
?>>Geschichte</option>
|
||||
<option value="erdkunde" <?php
|
||||
if (isset($_SESSION['old_category']) && !empty($_SESSION['old_category'])) {
|
||||
if ($_SESSION['old_category'] === 'erdkunde') { echo 'selected'; }
|
||||
} elseif (isset($category) && !empty($category)) {
|
||||
if ($category === 'erdkunde') { echo 'selected'; }
|
||||
}
|
||||
?>>Erdkunde</option>
|
||||
<option value="sozialkunde" <?php
|
||||
if (isset($_SESSION['old_category']) && !empty($_SESSION['old_category'])) {
|
||||
if ($_SESSION['old_category'] === 'sozialkunde') { echo 'selected'; }
|
||||
} elseif (isset($category) && !empty($category)) {
|
||||
if ($category === 'sozialkunde') { echo 'selected'; }
|
||||
}
|
||||
?>>Sozialkunde</option>
|
||||
<option value="wirtschaft" <?php
|
||||
if (isset($_SESSION['old_category']) && !empty($_SESSION['old_category'])) {
|
||||
if ($_SESSION['old_category'] === 'wirtschaft') { echo 'selected'; }
|
||||
} elseif (isset($category) && !empty($category)) {
|
||||
if ($category === 'wirtschaft') { echo 'selected'; }
|
||||
}
|
||||
?>>Wirtschaftskunde</option>
|
||||
<option value="religion" <?php
|
||||
if (isset($_SESSION['old_category']) && !empty($_SESSION['old_category'])) {
|
||||
if ($_SESSION['old_category'] === 'religion') { echo 'selected'; }
|
||||
} elseif (isset($category) && !empty($category)) {
|
||||
if ($category === 'religion') { echo 'selected'; }
|
||||
}
|
||||
?>>Religion</option>
|
||||
<option value="ethik" <?php
|
||||
if (isset($_SESSION['old_category']) && !empty($_SESSION['old_category'])) {
|
||||
if ($_SESSION['old_category'] === 'ethik') { echo 'selected'; }
|
||||
} elseif (isset($category) && !empty($category)) {
|
||||
if ($category === 'ethik') { echo 'selected'; }
|
||||
}
|
||||
?>>Ethikunterricht</option>
|
||||
<option value="philosophie" <?php
|
||||
if (isset($_SESSION['old_category']) && !empty($_SESSION['old_category'])) {
|
||||
if ($_SESSION['old_category'] === 'philosophie') { echo 'selected'; }
|
||||
} elseif (isset($category) && !empty($category)) {
|
||||
if ($category === 'philosophie') { echo 'selected'; }
|
||||
}
|
||||
?>>Philosophie</option>
|
||||
<option value="psychologie" <?php
|
||||
if (isset($_SESSION['old_category']) && !empty($_SESSION['old_category'])) {
|
||||
if ($_SESSION['old_category'] === 'psychologie') { echo 'selected'; }
|
||||
} elseif (isset($category) && !empty($category)) {
|
||||
if ($category === 'psychologie') { echo 'selected'; }
|
||||
}
|
||||
?>>Psychologie</option>
|
||||
<option value="kunst" <?php
|
||||
if (isset($_SESSION['old_category']) && !empty($_SESSION['old_category'])) {
|
||||
if ($_SESSION['old_category'] === 'kunst') { echo 'selected'; }
|
||||
} elseif (isset($category) && !empty($category)) {
|
||||
if ($category === 'kunst') { echo 'selected'; }
|
||||
}
|
||||
?>>Kunst</option>
|
||||
<option value="musik" <?php
|
||||
if (isset($_SESSION['old_category']) && !empty($_SESSION['old_category'])) {
|
||||
if ($_SESSION['old_category'] === 'musik') { echo 'selected'; }
|
||||
} elseif (isset($category) && !empty($category)) {
|
||||
if ($category === 'musik') { echo 'selected'; }
|
||||
}
|
||||
?>>Musik</option>
|
||||
<option value="theater" <?php
|
||||
if (isset($_SESSION['old_category']) && !empty($_SESSION['old_category'])) {
|
||||
if ($_SESSION['old_category'] === 'theater') { echo 'selected'; }
|
||||
} elseif (isset($category) && !empty($category)) {
|
||||
if ($category === 'theater') { echo 'selected'; }
|
||||
}
|
||||
?>>Theater</option>
|
||||
</optgroup>
|
||||
|
||||
<optgroup label="Technik & Praxis">
|
||||
<option value="technik">Technik</option>
|
||||
<option value="werken">Werken</option>
|
||||
<option value="hauswirtschaft">Hauswirtschaft</option>
|
||||
<option value="sport">Sport</option>
|
||||
<option value="technik" <?php
|
||||
if (isset($_SESSION['old_category']) && !empty($_SESSION['old_category'])) {
|
||||
if ($_SESSION['old_category'] === 'technik') { echo 'selected'; }
|
||||
} elseif (isset($category) && !empty($category)) {
|
||||
if ($category === 'technik') { echo 'selected'; }
|
||||
}
|
||||
?>>Technik</option>
|
||||
<option value="werken" <?php
|
||||
if (isset($_SESSION['old_category']) && !empty($_SESSION['old_category'])) {
|
||||
if ($_SESSION['old_category'] === 'werken') { echo 'selected'; }
|
||||
} elseif (isset($category) && !empty($category)) {
|
||||
if ($category === 'werken') { echo 'selected'; }
|
||||
}
|
||||
?>>Werken</option>
|
||||
<option value="hauswirtschaft" <?php
|
||||
if (isset($_SESSION['old_category']) && !empty($_SESSION['old_category'])) {
|
||||
if ($_SESSION['old_category'] === 'hauswirtschaft') { echo 'selected'; }
|
||||
} elseif (isset($category) && !empty($category)) {
|
||||
if ($category === 'hauswirtschaft') { echo 'selected'; }
|
||||
}
|
||||
?>>Hauswirtschaft</option>
|
||||
<option value="sport" <?php
|
||||
if (isset($_SESSION['old_category']) && !empty($_SESSION['old_category'])) {
|
||||
if ($_SESSION['old_category'] === 'sport') { echo 'selected'; }
|
||||
} elseif (isset($category) && !empty($category)) {
|
||||
if ($category === 'sport') { echo 'selected'; }
|
||||
}
|
||||
?>>Sport</option>
|
||||
</optgroup>
|
||||
|
||||
<?php
|
||||
if (isset($_SESSION['old_category'])) {
|
||||
unset($_SESSION['old_category']);
|
||||
}
|
||||
?>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
|
||||
+27
-21
@@ -19,28 +19,34 @@ include_once 'php/controller/home-controller.php';
|
||||
sea takimata sanctus est Lorem ipsum dolor sit amet.
|
||||
</p>
|
||||
|
||||
<!-- Flexbox -->
|
||||
<div class="flexbox">
|
||||
<div class="container">
|
||||
<a href="index.php?pfad=showCategory&category=informatik" class="category-link">Informatik</a>
|
||||
<div class="article-link">
|
||||
<a href="index.php?pfad=showArticle&id=<?php echo $dummy3->getId()?>"><?php if(isset($dummy3)){echo $dummy3->getTitle();}else{echo "Fehler: Beitrag nicht gefunden!";} ?></a>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Flexbox für aktive Kategorien -->
|
||||
<div class="flexbox">
|
||||
<?php
|
||||
if (!empty($categoriesWithArticles) && is_array($categoriesWithArticles)):
|
||||
foreach ($categoriesWithArticles as $category):
|
||||
?>
|
||||
<div class="container">
|
||||
<a href="index.php?pfad=showCategory&category=<?php echo htmlspecialchars($category['slug']); ?>" class="category-link">
|
||||
<?php echo htmlspecialchars($category['name']); ?>
|
||||
</a>
|
||||
|
||||
<div class="container">
|
||||
<a href="index.php?pfad=showCategory&category=mathe" class="category-link">Mathe</a>
|
||||
<div class="article-link">
|
||||
<a href="index.php?pfad=showArticle&id=<?php echo $dummy1->getId()?>"><?php if(isset($dummy1)){echo $dummy1->getTitle();}else{echo "Fehler: Beitrag nicht gefunden!";} ?></a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="container">
|
||||
<a href="index.php?pfad=showCategory&category=physik" class="category-link">Physik</a>
|
||||
<div class="article-link">
|
||||
<a href="index.php?pfad=showArticle&id=<?php echo $dummy2->getId()?>"><?php if(isset($dummy2)){echo $dummy2->getTitle();}else{echo "Fehler: Beitrag nicht gefunden!";} ?></a>
|
||||
</div>
|
||||
</div>
|
||||
<!-- die 5 neuesten Beiträge der Kategorie -->
|
||||
<div class="article-links-wrapper">
|
||||
<?php foreach ($category['articles'] as $article): ?>
|
||||
<div class="article-link">
|
||||
<a href="index.php?pfad=showArticle&id=<?php echo htmlspecialchars($article->getId()); ?>">
|
||||
<?php echo htmlspecialchars($article->getTitle()); ?>
|
||||
</a>
|
||||
</div>
|
||||
<?php endforeach; ?>
|
||||
</div>
|
||||
</div>
|
||||
<?php
|
||||
endforeach;
|
||||
else:
|
||||
?>
|
||||
<p>Aktuell sind keine Beiträge in den Kategorien vorhanden.</p>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
|
||||
</main>
|
||||
+50
-7
@@ -7,8 +7,8 @@ $isEditMode = (isset($_GET["edit"]) && $_GET["edit"] === "1") || !empty($error);
|
||||
|
||||
<main class="form-page">
|
||||
<div class="flexbox">
|
||||
|
||||
<div class="container">
|
||||
<?php include_once "includes/alertMessages.php" ?>
|
||||
|
||||
<?php if (!empty($error)): ?>
|
||||
<p class="alert-message is-error">
|
||||
@@ -93,8 +93,6 @@ $isEditMode = (isset($_GET["edit"]) && $_GET["edit"] === "1") || !empty($error);
|
||||
|
||||
<div class="container">
|
||||
|
||||
<?php include_once "includes/alertMessages.php"?>
|
||||
|
||||
<h2 class="section-title">Meine Beiträge</h2>
|
||||
|
||||
<div class="articles-list">
|
||||
@@ -157,7 +155,9 @@ $isEditMode = (isset($_GET["edit"]) && $_GET["edit"] === "1") || !empty($error);
|
||||
</a>
|
||||
|
||||
<form action="php/controller/deleteArticle-controller.php" method="POST">
|
||||
<input type="hidden" name="id" value="<?php echo htmlspecialchars($userArticle->getID()); ?>">
|
||||
<input type="hidden"
|
||||
name="id"
|
||||
value="<?php echo htmlspecialchars($userArticle->getID()); ?>">
|
||||
|
||||
<button type="submit"
|
||||
class="button"
|
||||
@@ -165,7 +165,6 @@ $isEditMode = (isset($_GET["edit"]) && $_GET["edit"] === "1") || !empty($error);
|
||||
Löschen
|
||||
</button>
|
||||
</form>
|
||||
|
||||
</div>
|
||||
<?php endforeach; ?>
|
||||
|
||||
@@ -180,10 +179,54 @@ $isEditMode = (isset($_GET["edit"]) && $_GET["edit"] === "1") || !empty($error);
|
||||
</button>
|
||||
|
||||
<?php endif; ?>
|
||||
|
||||
<?php unset($_SESSION["message"]); ?>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="container">
|
||||
<div class="comments-section">
|
||||
|
||||
<h2 class="section-title">Meine Kommentare</h2>
|
||||
|
||||
<!-- Liste aller Kommentare des Nutzers -->
|
||||
<div class="comments-list">
|
||||
|
||||
<?php if (isset($userComments) && count($userComments) > 0): ?>
|
||||
|
||||
<?php foreach ($userComments as $comment): ?>
|
||||
<div class="article-item">
|
||||
|
||||
<!-- Erstellungsdatum des Kommentars -->
|
||||
<div class="article-meta">
|
||||
<span class="article-date">
|
||||
<?php echo htmlspecialchars($comment->getCreated()); ?>
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<!-- Inhalt des Kommentars -->
|
||||
<p>
|
||||
<?php echo nl2br(htmlspecialchars($comment->getContent())); ?>
|
||||
</p>
|
||||
|
||||
<!-- Link zum Beitrag, unter dem der Kommentar geschrieben wurde -->
|
||||
<a href="index.php?pfad=showArticle&id=<?php echo htmlspecialchars($comment->getArticleId()); ?>"
|
||||
class="edit-link-button">
|
||||
Zum Beitrag
|
||||
</a>
|
||||
|
||||
</div>
|
||||
<?php endforeach; ?>
|
||||
|
||||
<?php else: ?>
|
||||
|
||||
<p>Du hast noch keine Kommentare geschrieben.</p>
|
||||
|
||||
<?php endif; ?>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
<?php unset($_SESSION["message"]); ?>
|
||||
</div>
|
||||
|
||||
</main>
|
||||
+61
-31
@@ -27,7 +27,8 @@ if ($currentPage < 1) {
|
||||
$offset = ($currentPage - 1) * $limit;
|
||||
|
||||
// Nur die Ergebnisse für die aktuelle Seite ausschneiden
|
||||
$results = array_slice($all_results, $offset, $limit);
|
||||
//$results = array_slice($all_results, $offset, $limit);
|
||||
$results = $all_results;
|
||||
$resultCount = count($results);
|
||||
|
||||
?>
|
||||
@@ -44,12 +45,11 @@ $resultCount = count($results);
|
||||
<!-- Links: Seitenleiste für Filter und Suche -->
|
||||
<aside class="s-res-sidebar">
|
||||
|
||||
<!-- Sortierfuntion Box und Such Box-->
|
||||
<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; ?>">
|
||||
<form action="php/controller/search-results-controller.php" method="GET" id="search-form-id" class="s-res-sidebar-form">
|
||||
|
||||
<input type="hidden" id="s-res-page-input" name="page" value="<?php echo $_GET['page'] ?? 1; ?>">
|
||||
|
||||
<div class="s-res-sidebar-box">
|
||||
<div class="s-res-sidebar-box">
|
||||
<h3 class="s-res-sidebar-title">Suche anpassen</h3>
|
||||
<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>
|
||||
@@ -60,25 +60,57 @@ $resultCount = count($results);
|
||||
<?php $currentSort = $_SESSION['search_sort'] ?? 'alphabet'; ?>
|
||||
<div class="s-res-filter-group">
|
||||
<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>
|
||||
</label>
|
||||
<!-- Noch disabled, da likes noch nicht implementiert-->
|
||||
<label class="s-res-filter-option">
|
||||
<input type="radio" name="sort" value="likes" <?php echo $currentSort === 'likes' ? 'checked' : ''; ?> disabled>
|
||||
<span style="color: #94a3b8;">Beliebtheit (Likes)</span>
|
||||
<input type="radio" name="sort" value="likes" <?php echo $currentSort === 'likes' ? 'checked' : ''; ?> onchange="this.form.submit()">
|
||||
<span>Beliebtheit (Likes)</span>
|
||||
</label>
|
||||
<label class="s-res-filter-option">
|
||||
<input type="radio" name="sort" value="newest" <?php echo $currentSort === 'newest' ? 'checked' : ''; ?> onchange="this.form.submit()">
|
||||
<input type="radio" name="sort" value="newest" class="sort-radio" <?php echo $currentSort === 'newest' ? 'checked' : ''; ?>>
|
||||
<span>Neueste Beiträge</span>
|
||||
</label>
|
||||
<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>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="s-res-sidebar-box">
|
||||
<h3 class="s-res-sidebar-title">Kategorie filtern</h3>
|
||||
<select id="category-filter" class="s-res-limit-select" style="width: 100%; padding: 8px; border-radius: 6px; border: 1px solid #cbd5e1;">
|
||||
<option value="all">Alle Kategorien</option>
|
||||
<option value="Deutsch">Deutsch</option>
|
||||
<option value="Englisch">Englisch</option>
|
||||
<option value="Franzoesisch">Französisch</option>
|
||||
<option value="Latein">Latein</option>
|
||||
<option value="Literatur">Literatur</option>
|
||||
<option value="Mathematik">Mathematik</option>
|
||||
<option value="Biologie">Biologie</option>
|
||||
<option value="Informatik">Informatik</option>
|
||||
<option value="Chemie">Chemie</option>
|
||||
<option value="Physik">Physik</option>
|
||||
<option value="Astronomie">Astronomie</option>
|
||||
<option value="Geschichte">Geschichte</option>
|
||||
<option value="Erdkunde">Erdkunde</option>
|
||||
<option value="Sozialkunde">Sozialkunde</option>
|
||||
<option value="Wirtschaftskunde">Wirtschaftskunde</option>
|
||||
<option value="Religion">Religion</option>
|
||||
<option value="Ethikunterricht">Ethikunterricht</option>
|
||||
<option value="Philosophie">Philosophie</option>
|
||||
<option value="Psychologie">Psychologie</option>
|
||||
<option value="Kunst">Kunst</option>
|
||||
<option value="Musik">Musik</option>
|
||||
<option value="Theater">Theater</option>
|
||||
<option value="Technik">Technik</option>
|
||||
<option value="Werken">Werken</option>
|
||||
<option value="Hauswirtschaft">Hauswirtschaft</option>
|
||||
<option value="Sport">Sport</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
</form>
|
||||
|
||||
</aside>
|
||||
@@ -96,14 +128,21 @@ $resultCount = count($results);
|
||||
if (!empty($results)): ?>
|
||||
|
||||
<?php foreach ($results as $item): ?>
|
||||
<div class="s-res-item">
|
||||
<div class="s-res-item" data-likes="<?php echo $item['likes'] ?? 0; ?>" data-category="<?php echo strtolower($item['category'] ?? ''); ?>">
|
||||
<div class="s-res-content">
|
||||
<h2 class="s-res-item-title">
|
||||
<a href="index.php?pfad=showArticle&id=<?php echo $item['id']; ?>" class="s-res-link">
|
||||
<?php echo htmlspecialchars($item['title']); ?>
|
||||
</a>
|
||||
</h2>
|
||||
<p class="s-res-author">Von: <span class="s-res-author-name"><?php echo htmlspecialchars($item['author']); ?></span></p>
|
||||
<div class="s-res-meta-row">
|
||||
<p class="s-res-author">Von: <span class="s-res-author-name"><?php echo htmlspecialchars($item['author']); ?></span></p>
|
||||
|
||||
<span class="s-res-likes">
|
||||
❤️ <?php echo isset($item['likes']) && is_array($item['likes']) ? count($item['likes']) : 0; ?>
|
||||
</span>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<div class="s-res-arrow">→</div>
|
||||
</div>
|
||||
@@ -113,8 +152,8 @@ $resultCount = count($results);
|
||||
elseif (isset($_SESSION["search_query"]) && $_SESSION["search_query"] !== "" && $resultCount === 0): ?>
|
||||
<p>Keine Beiträge zu diesem Suchbegriff gefunden.</p>
|
||||
<?php
|
||||
elseif (isset($_SESSION["message"]) && $_SESSION["message"] == "missing_parameters"): ?>
|
||||
<p>Bitte überprüfe deine Sucheingabe und versuche es erneut!</p>
|
||||
elseif (isset($_SESSION["message"]) && $_SESSION["message"] == "invalid_search_query"): ?>
|
||||
<p>Unzulässige Suchanfrage</p>
|
||||
|
||||
<?php endif; ?>
|
||||
<?php
|
||||
@@ -136,23 +175,14 @@ $resultCount = count($results);
|
||||
</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>
|
||||
<!-- Dynamische Seitenzahlen -->
|
||||
<?php for ($i = 1; $i <= $totalPages; $i++): ?>
|
||||
<button type="button"
|
||||
class="s-res-page-btn <?php echo $i === $currentPage ? 's-res-page-btn-active' : ''; ?>"
|
||||
data-page="<?php echo $i; ?>">
|
||||
<?php echo $i; ?>
|
||||
</button>
|
||||
<?php endfor; ?>
|
||||
<button type="button" class="s-res-page-btn" data-page="<?php echo $currentPage + 1; ?>" <?php echo $currentPage >= $totalPages ? 'disabled' : ''; ?>>
|
||||
»
|
||||
</button>
|
||||
<button type="button" class="s-res-page-btn" id="prev-page-btn" data-page="0">«</button>
|
||||
|
||||
<span id="dynamic-page-numbers"></span>
|
||||
|
||||
<button type="button" class="s-res-page-btn" id="next-page-btn" data-page="2">»</button>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</main>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
+134
-34
@@ -1,44 +1,45 @@
|
||||
<?php
|
||||
$comments = [];
|
||||
$mainComments = [];
|
||||
$repliesByParent = [];
|
||||
$articleObj = null;
|
||||
|
||||
include_once 'php/controller/showArticle-controller.php';
|
||||
?>
|
||||
<!--
|
||||
Seite: Anzeige für Beiträge
|
||||
Funktion: Stellt einen übergebenen Beitrag dar.
|
||||
-->
|
||||
|
||||
Seite: Anzeige für Beiträge
|
||||
Funktion: Stellt einen übergebenen Beitrag dar.
|
||||
-->
|
||||
<!-- Hauptcontainer für die Beitragsansicht (Ausschließlich der Content-Bereich) -->
|
||||
<main class="article-view-container">
|
||||
<?php if (isset($_SESSION["message"]) && $_SESSION["message"] == "internal_error"): ?>
|
||||
<p class="alert-message is-error">
|
||||
Es ist ein interner Fehler aufgetreten. Bitte versuche es erneut.
|
||||
</p>
|
||||
<?php endif; ?>
|
||||
<?php if (isset($_SESSION["message"]) && $_SESSION["message"] == "missing_id"): ?>
|
||||
<p class="alert-message is-error">
|
||||
Es ist ein Fehler aufgetreten. Die ID konnte nicht ausgelesen werden. Bitte versuche es erneut.
|
||||
</p>
|
||||
<?php endif; ?>
|
||||
|
||||
<?php if (isset($_SESSION["message"]) && $_SESSION["message"] == "missing_parameters"): ?>
|
||||
<p class="alert-message is-error">
|
||||
Jeder Beitrag muss einen Titel, Kategorie und Inhalt besitzen.
|
||||
</p>
|
||||
<?php endif; ?>
|
||||
<?php if (isset($_SESSION["message"]) && $_SESSION["message"] == "article_updated"): ?>
|
||||
<p class="alert-message is-success">
|
||||
Dein Beitrag wurde erfolgreich bearbeitet!
|
||||
</p>
|
||||
<?php endif; ?>
|
||||
<?php
|
||||
unset($_SESSION["message"]);
|
||||
?>
|
||||
<?php include_once "includes/alertMessages.php"?>
|
||||
|
||||
<!-- Metadaten & Titel -->
|
||||
<div class="article-view-top-section">
|
||||
|
||||
<?php if (isset($category) && !empty($category)): ?>
|
||||
<span class="article-view-category"><?php echo htmlspecialchars($category); ?></span>
|
||||
<?php endif; ?>
|
||||
<div class="article-view-top-section">
|
||||
|
||||
<div class="category-and-likes-row">
|
||||
<?php if (isset($category) && !empty($category)): ?>
|
||||
<span class="article-view-category"><?php echo htmlspecialchars($category); ?></span>
|
||||
<?php endif; ?>
|
||||
|
||||
<!-- Like-Anzeige und dynamischer Like-Button -->
|
||||
<?php if (isset($articleObj) && $articleObj !== null): ?>
|
||||
<div class="article-view-likes">
|
||||
<span>❤️ <span class="like-count"><?php echo $articleObj->getLikeCount(); ?></span></span>
|
||||
|
||||
<?php if (isset($_SESSION["user_email"])): ?>
|
||||
<a href="php/controller/like-controller.php?id=<?php echo $articleObj->getId(); ?>" class="like-toggle-btn">
|
||||
<?php echo $articleObj->hasLiked($_SESSION["user_email"]) ? '👎 Gefällt mir nicht mehr' : '👍 Gefällt mir'; ?>
|
||||
</a>
|
||||
<?php else: ?>
|
||||
<span class="login-hint">(Anmelden zum Liken)</span>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
|
||||
<h1 class="article-view-title">
|
||||
<?php if (isset($title)) { echo htmlspecialchars($title); } ?>
|
||||
</h1>
|
||||
@@ -54,8 +55,33 @@ include_once 'php/controller/showArticle-controller.php';
|
||||
<!-- Beitrags-Inhalt -->
|
||||
<div class="article-view-content">
|
||||
<?php if (isset($content)): ?>
|
||||
<!-- nl2br für Zeilenumbrüche -->
|
||||
<div class="article-view-body"><?php echo nl2br(htmlspecialchars($content)); ?></div>
|
||||
<?php
|
||||
// Versuchen, den Inhalt von JSON in ein PHP-Array umzuwandeln
|
||||
$blocks = json_decode($content, true);
|
||||
|
||||
// Wenn das JSON valide ist und Blöcke enthält
|
||||
if (json_last_error() === JSON_ERROR_NONE && is_array($blocks)):
|
||||
foreach ($blocks as $block):
|
||||
if (isset($block['type']) && isset($block['value'])):
|
||||
if ($block['type'] === 'text'): ?>
|
||||
<!-- Textblock mit XSS-Schutz und Erhalt von Zeilenumbrüchen -->
|
||||
<div class="article-view-body block-text">
|
||||
<?php echo nl2br(htmlspecialchars($block['value'])); ?>
|
||||
</div>
|
||||
<?php elseif ($block['type'] === 'image'): ?>
|
||||
<!-- Bildblock, der auf den relativen Pfad im uploads-Ordner verweist -->
|
||||
<div class="article-view-body block-image">
|
||||
<img src="<?php echo htmlspecialchars($block['value']); ?>" alt="Beitragsbild">
|
||||
</div>
|
||||
<?php endif;
|
||||
endif;
|
||||
endforeach;
|
||||
else: ?>
|
||||
<!-- Fallback: Wenn der Beitrag alten Reintext aus der DB enthält -->
|
||||
<div class="article-view-body block-text">
|
||||
<?php echo nl2br(htmlspecialchars($content)); ?>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
|
||||
@@ -80,5 +106,79 @@ include_once 'php/controller/showArticle-controller.php';
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
|
||||
</main>
|
||||
<section class="article-comments-section">
|
||||
<h2>Kommentare</h2>
|
||||
|
||||
<div id="comments-list">
|
||||
<?php if (!empty($mainComments)): ?>
|
||||
<?php foreach ($mainComments as $comment): ?>
|
||||
<div class="comment-item" data-comment-id="<?php echo htmlspecialchars($comment->getId()); ?>">
|
||||
<p>
|
||||
<strong><?php echo htmlspecialchars($comment->getAuthor()); ?></strong>
|
||||
<span><?php echo htmlspecialchars($comment->getCreated()); ?></span>
|
||||
</p>
|
||||
|
||||
<p><?php echo nl2br(htmlspecialchars($comment->getContent())); ?></p>
|
||||
|
||||
<?php if (isset($_SESSION["user_email"])): ?>
|
||||
<button type="button"
|
||||
class="reply-button"
|
||||
data-comment-id="<?php echo htmlspecialchars($comment->getId()); ?>"
|
||||
data-author="<?php echo htmlspecialchars($comment->getAuthor()); ?>">
|
||||
Antworten
|
||||
</button>
|
||||
<?php endif; ?>
|
||||
|
||||
<div class="comment-replies">
|
||||
<?php if (isset($repliesByParent[$comment->getId()])): ?>
|
||||
<?php foreach ($repliesByParent[$comment->getId()] as $reply): ?>
|
||||
<div class="comment-item comment-reply">
|
||||
<p>
|
||||
<strong><?php echo htmlspecialchars($reply->getAuthor()); ?></strong>
|
||||
<span><?php echo htmlspecialchars($reply->getCreated()); ?></span>
|
||||
</p>
|
||||
|
||||
<p><?php echo nl2br(htmlspecialchars($reply->getContent())); ?></p>
|
||||
</div>
|
||||
<?php endforeach; ?>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
</div>
|
||||
<?php endforeach; ?>
|
||||
<?php else: ?>
|
||||
<p class="no-comments-message">
|
||||
Noch keine Kommentare vorhanden.
|
||||
</p>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
|
||||
<?php if (isset($_SESSION["user_email"])): ?>
|
||||
<form id="comment-form">
|
||||
<input type="hidden"
|
||||
name="article_id"
|
||||
value="<?php echo htmlspecialchars($_GET["id"] ?? ""); ?>">
|
||||
|
||||
<input type="hidden"
|
||||
name="parent_comment_id"
|
||||
id="parent-comment-id"
|
||||
value="">
|
||||
|
||||
<p id="reply-info" class="reply-info" style="display: none;"></p>
|
||||
|
||||
<textarea name="content"
|
||||
id="comment-content"
|
||||
placeholder="Schreibe einen Kommentar..."
|
||||
required></textarea>
|
||||
|
||||
<button type="submit" class="button">
|
||||
Kommentar senden
|
||||
</button>
|
||||
</form>
|
||||
<?php else: ?>
|
||||
<div class="comment-login-hint">
|
||||
<p>Melde dich an, um einen Kommentar zu schreiben.</p>
|
||||
<a href="index.php?pfad=login" class="button">Jetzt anmelden</a>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
</section>
|
||||
</main>
|
||||
+220
-29
@@ -10,7 +10,7 @@ include_once 'php/controller/showArticle-controller.php';
|
||||
Seite: Beitrag erstellen
|
||||
Inhalt: Formular für die Erstellung eines neuen Beitrags
|
||||
-->
|
||||
<form method="post" action="php/controller/updateArticle-controller.php?id=<?php if(isset($id) && !empty($id)){echo htmlspecialchars($id);}else{$_SESSION["message"] = "missing_id";} ?>" id="editor-form" class="article-editor-scope.editor-container article-editor-scope editor-container">
|
||||
<form method="post" action="php/controller/updateArticle-controller.php?id=<?php if(isset($id) && !empty($id)){echo htmlspecialchars($id);}else{$_SESSION["message"] = "missing_id";} ?>" id="editor-form" enctype="multipart/form-data" class="article-editor-scope.editor-container article-editor-scope editor-container">
|
||||
|
||||
<main class="editor-main">
|
||||
<?php include_once "includes/alertMessages.php"?>
|
||||
@@ -26,8 +26,33 @@ include_once 'php/controller/showArticle-controller.php';
|
||||
|
||||
?>"
|
||||
placeholder="Titel hier eingeben" required>
|
||||
<textarea id="content" name="content" placeholder="Schreibe deinen Beitrag..."><?php if (isset($_SESSION['old_content']) && !empty($_SESSION['old_content'])){echo htmlspecialchars($_SESSION['old_content']); unset($_SESSION['old_content']);}elseif (isset($content) && !empty($content)){echo htmlspecialchars($content);}?></textarea>
|
||||
|
||||
<!-- Hier werden die dynamischen divs via JavaScript eingefügt -->
|
||||
<div id="block-container"></div>
|
||||
|
||||
<!-- Plus-Button und das Pop-up-Menü -->
|
||||
<div id="add-block-control" class="article-editor-scope add-block-control">
|
||||
<button type="button" id="plus-button" class="article-editor-scope plus-button">+</button>
|
||||
<div id="block-popup" class="article-editor-scope block-popup hidden">
|
||||
<button type="button" data-type="text">Textblock</button>
|
||||
<button type="button" data-type="image">Bild einfügen</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Unsichtbares Textfeld, das die JSON-Daten hält und an den Controller postet -->
|
||||
<textarea id="content" name="content" style="display:none;"><?php
|
||||
if (isset($_SESSION['old_content']) && !empty($_SESSION['old_content'])){
|
||||
echo htmlspecialchars($_SESSION['old_content']);
|
||||
unset($_SESSION['old_content']);
|
||||
}elseif (isset($content) && !empty($content)){
|
||||
echo htmlspecialchars($content);
|
||||
} else {
|
||||
echo '[]';
|
||||
}
|
||||
?></textarea>
|
||||
|
||||
<!-- unsichtbares Input, um die zu löschenden Bilder zu übergeben-->
|
||||
<input type="hidden" id="deleted-images" name="deleted_images" value="[]">
|
||||
</main>
|
||||
|
||||
<!-- Seitenleiste -->
|
||||
@@ -40,45 +65,211 @@ include_once 'php/controller/showArticle-controller.php';
|
||||
<div class="sidebar-block">
|
||||
<label for="category">Kategorie <span class="required">*</span></label>
|
||||
<select id="category" name="category" required>
|
||||
<option disabled selected>Kategorie wählen...</option>
|
||||
<option disabled <?php
|
||||
if ((!isset($_SESSION['old_category']) || empty($_SESSION['old_category'])) && (!isset($category) || empty($category))) {
|
||||
echo 'selected';
|
||||
}
|
||||
?>>Kategorie wählen...</option>
|
||||
|
||||
<optgroup label="Sprachen">
|
||||
<option value="deutsch">Deutsch</option>
|
||||
<option value="englisch">Englisch</option>
|
||||
<option value="franzoesisch">Französisch</option>
|
||||
<option value="latein">Latein</option>
|
||||
<option value="literatur">Literatur</option>
|
||||
<option value="deutsch" <?php
|
||||
if (isset($_SESSION['old_category']) && !empty($_SESSION['old_category'])) {
|
||||
if ($_SESSION['old_category'] === 'deutsch') { echo 'selected'; }
|
||||
} elseif (isset($category) && !empty($category)) {
|
||||
if ($category === 'deutsch') { echo 'selected'; }
|
||||
}
|
||||
?>>Deutsch</option>
|
||||
<option value="englisch" <?php
|
||||
if (isset($_SESSION['old_category']) && !empty($_SESSION['old_category'])) {
|
||||
if ($_SESSION['old_category'] === 'englisch') { echo 'selected'; }
|
||||
} elseif (isset($category) && !empty($category)) {
|
||||
if ($category === 'englisch') { echo 'selected'; }
|
||||
}
|
||||
?>>Englisch</option>
|
||||
<option value="franzoesisch" <?php
|
||||
if (isset($_SESSION['old_category']) && !empty($_SESSION['old_category'])) {
|
||||
if ($_SESSION['old_category'] === 'franzoesisch') { echo 'selected'; }
|
||||
} elseif (isset($category) && !empty($category)) {
|
||||
if ($category === 'franzoesisch') { echo 'selected'; }
|
||||
}
|
||||
?>>Französisch</option>
|
||||
<option value="latein" <?php
|
||||
if (isset($_SESSION['old_category']) && !empty($_SESSION['old_category'])) {
|
||||
if ($_SESSION['old_category'] === 'latein') { echo 'selected'; }
|
||||
} elseif (isset($category) && !empty($category)) {
|
||||
if ($category === 'latein') { echo 'selected'; }
|
||||
}
|
||||
?>>Latein</option>
|
||||
<option value="literatur" <?php
|
||||
if (isset($_SESSION['old_category']) && !empty($_SESSION['old_category'])) {
|
||||
if ($_SESSION['old_category'] === 'literatur') { echo 'selected'; }
|
||||
} elseif (isset($category) && !empty($category)) {
|
||||
if ($category === 'literatur') { echo 'selected'; }
|
||||
}
|
||||
?>>Literatur</option>
|
||||
</optgroup>
|
||||
|
||||
<optgroup label="MINT">
|
||||
<option value="mathe">Mathematik</option>
|
||||
<option value="biologie">Biologie</option>
|
||||
<option value="chemie">Chemie</option>
|
||||
<option value="physik">Physik</option>
|
||||
<option value="informatik">Informatik</option>
|
||||
<option value="astronomie">Astronomie</option>
|
||||
<option value="mathe" <?php
|
||||
if (isset($_SESSION['old_category']) && !empty($_SESSION['old_category'])) {
|
||||
if ($_SESSION['old_category'] === 'mathe') { echo 'selected'; }
|
||||
} elseif (isset($category) && !empty($category)) {
|
||||
if ($category === 'mathe') { echo 'selected'; }
|
||||
}
|
||||
?>>Mathematik</option>
|
||||
<option value="biologie" <?php
|
||||
if (isset($_SESSION['old_category']) && !empty($_SESSION['old_category'])) {
|
||||
if ($_SESSION['old_category'] === 'biologie') { echo 'selected'; }
|
||||
} elseif (isset($category) && !empty($category)) {
|
||||
if ($category === 'biologie') { echo 'selected'; }
|
||||
}
|
||||
?>>Biologie</option>
|
||||
<option value="chemie" <?php
|
||||
if (isset($_SESSION['old_category']) && !empty($_SESSION['old_category'])) {
|
||||
if ($_SESSION['old_category'] === 'chemie') { echo 'selected'; }
|
||||
} elseif (isset($category) && !empty($category)) {
|
||||
if ($category === 'chemie') { echo 'selected'; }
|
||||
}
|
||||
?>>Chemie</option>
|
||||
<option value="physik" <?php
|
||||
if (isset($_SESSION['old_category']) && !empty($_SESSION['old_category'])) {
|
||||
if ($_SESSION['old_category'] === 'physik') { echo 'selected'; }
|
||||
} elseif (isset($category) && !empty($category)) {
|
||||
if ($category === 'physik') { echo 'selected'; }
|
||||
}
|
||||
?>>Physik</option>
|
||||
<option value="informatik" <?php
|
||||
if (isset($_SESSION['old_category']) && !empty($_SESSION['old_category'])) {
|
||||
if ($_SESSION['old_category'] === 'informatik') { echo 'selected'; }
|
||||
} elseif (isset($category) && !empty($category)) {
|
||||
if ($category === 'informatik') { echo 'selected'; }
|
||||
}
|
||||
?>>Informatik</option>
|
||||
<option value="astronomie" <?php
|
||||
if (isset($_SESSION['old_category']) && !empty($_SESSION['old_category'])) {
|
||||
if ($_SESSION['old_category'] === 'astronomie') { echo 'selected'; }
|
||||
} elseif (isset($category) && !empty($category)) {
|
||||
if ($category === 'astronomie') { echo 'selected'; }
|
||||
}
|
||||
?>>Astronomie</option>
|
||||
</optgroup>
|
||||
|
||||
<optgroup label="Gesellschaft & Werte">
|
||||
<option value="geschichte">Geschichte</option>
|
||||
<option value="erdkunde">Erdkunde</option>
|
||||
<option value="sozialkunde">Sozialkunde</option>
|
||||
<option value="wirtschaft">Wirtschaftskunde</option>
|
||||
<option value="religion">Religion</option>
|
||||
<option value="ethik">Ethikunterricht</option>
|
||||
<option value="philosophie">Philosophie</option>
|
||||
<option value="psychologie">Psychologie</option>
|
||||
<option value="kunst">Kunst</option>
|
||||
<option value="musik">Musik</option>
|
||||
<option value="theater">Theater</option>
|
||||
<option value="geschichte" <?php
|
||||
if (isset($_SESSION['old_category']) && !empty($_SESSION['old_category'])) {
|
||||
if ($_SESSION['old_category'] === 'geschichte') { echo 'selected'; }
|
||||
} elseif (isset($category) && !empty($category)) {
|
||||
if ($category === 'geschichte') { echo 'selected'; }
|
||||
}
|
||||
?>>Geschichte</option>
|
||||
<option value="erdkunde" <?php
|
||||
if (isset($_SESSION['old_category']) && !empty($_SESSION['old_category'])) {
|
||||
if ($_SESSION['old_category'] === 'erdkunde') { echo 'selected'; }
|
||||
} elseif (isset($category) && !empty($category)) {
|
||||
if ($category === 'erdkunde') { echo 'selected'; }
|
||||
}
|
||||
?>>Erdkunde</option>
|
||||
<option value="sozialkunde" <?php
|
||||
if (isset($_SESSION['old_category']) && !empty($_SESSION['old_category'])) {
|
||||
if ($_SESSION['old_category'] === 'sozialkunde') { echo 'selected'; }
|
||||
} elseif (isset($category) && !empty($category)) {
|
||||
if ($category === 'sozialkunde') { echo 'selected'; }
|
||||
}
|
||||
?>>Sozialkunde</option>
|
||||
<option value="wirtschaft" <?php
|
||||
if (isset($_SESSION['old_category']) && !empty($_SESSION['old_category'])) {
|
||||
if ($_SESSION['old_category'] === 'wirtschaft') { echo 'selected'; }
|
||||
} elseif (isset($category) && !empty($category)) {
|
||||
if ($category === 'wirtschaft') { echo 'selected'; }
|
||||
}
|
||||
?>>Wirtschaftskunde</option>
|
||||
<option value="religion" <?php
|
||||
if (isset($_SESSION['old_category']) && !empty($_SESSION['old_category'])) {
|
||||
if ($_SESSION['old_category'] === 'religion') { echo 'selected'; }
|
||||
} elseif (isset($category) && !empty($category)) {
|
||||
if ($category === 'religion') { echo 'selected'; }
|
||||
}
|
||||
?>>Religion</option>
|
||||
<option value="ethik" <?php
|
||||
if (isset($_SESSION['old_category']) && !empty($_SESSION['old_category'])) {
|
||||
if ($_SESSION['old_category'] === 'ethik') { echo 'selected'; }
|
||||
} elseif (isset($category) && !empty($category)) {
|
||||
if ($category === 'ethik') { echo 'selected'; }
|
||||
}
|
||||
?>>Ethikunterricht</option>
|
||||
<option value="philosophie" <?php
|
||||
if (isset($_SESSION['old_category']) && !empty($_SESSION['old_category'])) {
|
||||
if ($_SESSION['old_category'] === 'philosophie') { echo 'selected'; }
|
||||
} elseif (isset($category) && !empty($category)) {
|
||||
if ($category === 'philosophie') { echo 'selected'; }
|
||||
}
|
||||
?>>Philosophie</option>
|
||||
<option value="psychologie" <?php
|
||||
if (isset($_SESSION['old_category']) && !empty($_SESSION['old_category'])) {
|
||||
if ($_SESSION['old_category'] === 'psychologie') { echo 'selected'; }
|
||||
} elseif (isset($category) && !empty($category)) {
|
||||
if ($category === 'psychologie') { echo 'selected'; }
|
||||
}
|
||||
?>>Psychologie</option>
|
||||
<option value="kunst" <?php
|
||||
if (isset($_SESSION['old_category']) && !empty($_SESSION['old_category'])) {
|
||||
if ($_SESSION['old_category'] === 'kunst') { echo 'selected'; }
|
||||
} elseif (isset($category) && !empty($category)) {
|
||||
if ($category === 'kunst') { echo 'selected'; }
|
||||
}
|
||||
?>>Kunst</option>
|
||||
<option value="musik" <?php
|
||||
if (isset($_SESSION['old_category']) && !empty($_SESSION['old_category'])) {
|
||||
if ($_SESSION['old_category'] === 'musik') { echo 'selected'; }
|
||||
} elseif (isset($category) && !empty($category)) {
|
||||
if ($category === 'musik') { echo 'selected'; }
|
||||
}
|
||||
?>>Musik</option>
|
||||
<option value="theater" <?php
|
||||
if (isset($_SESSION['old_category']) && !empty($_SESSION['old_category'])) {
|
||||
if ($_SESSION['old_category'] === 'theater') { echo 'selected'; }
|
||||
} elseif (isset($category) && !empty($category)) {
|
||||
if ($category === 'theater') { echo 'selected'; }
|
||||
}
|
||||
?>>Theater</option>
|
||||
</optgroup>
|
||||
|
||||
<optgroup label="Technik & Praxis">
|
||||
<option value="technik">Technik</option>
|
||||
<option value="werken">Werken</option>
|
||||
<option value="hauswirtschaft">Hauswirtschaft</option>
|
||||
<option value="sport">Sport</option>
|
||||
<option value="technik" <?php
|
||||
if (isset($_SESSION['old_category']) && !empty($_SESSION['old_category'])) {
|
||||
if ($_SESSION['old_category'] === 'technik') { echo 'selected'; }
|
||||
} elseif (isset($category) && !empty($category)) {
|
||||
if ($category === 'technik') { echo 'selected'; }
|
||||
}
|
||||
?>>Technik</option>
|
||||
<option value="werken" <?php
|
||||
if (isset($_SESSION['old_category']) && !empty($_SESSION['old_category'])) {
|
||||
if ($_SESSION['old_category'] === 'werken') { echo 'selected'; }
|
||||
} elseif (isset($category) && !empty($category)) {
|
||||
if ($category === 'werken') { echo 'selected'; }
|
||||
}
|
||||
?>>Werken</option>
|
||||
<option value="hauswirtschaft" <?php
|
||||
if (isset($_SESSION['old_category']) && !empty($_SESSION['old_category'])) {
|
||||
if ($_SESSION['old_category'] === 'hauswirtschaft') { echo 'selected'; }
|
||||
} elseif (isset($category) && !empty($category)) {
|
||||
if ($category === 'hauswirtschaft') { echo 'selected'; }
|
||||
}
|
||||
?>>Hauswirtschaft</option>
|
||||
<option value="sport" <?php
|
||||
if (isset($_SESSION['old_category']) && !empty($_SESSION['old_category'])) {
|
||||
if ($_SESSION['old_category'] === 'sport') { echo 'selected'; }
|
||||
} elseif (isset($category) && !empty($category)) {
|
||||
if ($category === 'sport') { echo 'selected'; }
|
||||
}
|
||||
?>>Sport</option>
|
||||
</optgroup>
|
||||
|
||||
<?php
|
||||
if (isset($_SESSION['old_category'])) {
|
||||
unset($_SESSION['old_category']);
|
||||
}
|
||||
?>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -90,6 +90,135 @@
|
||||
padding: 18px 12px;
|
||||
}
|
||||
|
||||
/* Container für die dynamisch per JS eingefügten Blöcke */
|
||||
.article-editor-scope #block-container {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 20px;
|
||||
}
|
||||
|
||||
/* Styling für jeden einzelnen dynamisch generierten Block */
|
||||
.article-editor-scope .editor-block {
|
||||
position: relative; /* Wichtig für die absolute Positionierung des Lösch-Buttons */
|
||||
width: 100%;
|
||||
padding: 15px;
|
||||
background-color: #fafafa;
|
||||
border: 1px dashed #cccccc;
|
||||
border-radius: 6px;
|
||||
}
|
||||
|
||||
/* Textarea innerhalb eines dynamischen Textblocks */
|
||||
.article-editor-scope .editor-block textarea {
|
||||
width: 100%;
|
||||
min-height: 120px;
|
||||
padding: 10px;
|
||||
border: 1px solid #dddddd;
|
||||
border-radius: 4px;
|
||||
font-family: inherit;
|
||||
font-size: 1.1rem;
|
||||
line-height: 1.6;
|
||||
resize: vertical;
|
||||
outline: none;
|
||||
background: #ffffff;
|
||||
}
|
||||
|
||||
/* Komfort-Löschbutton oben rechts an jedem Block */
|
||||
.article-editor-scope .delete-block-btn {
|
||||
position: absolute;
|
||||
top: -10px;
|
||||
right: -10px;
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
border-radius: 50%;
|
||||
background-color: #e74c3c;
|
||||
color: #ffffff;
|
||||
border: none;
|
||||
font-size: 12px;
|
||||
font-weight: bold;
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
box-shadow: 0 2px 5px rgba(0, 0, 0, 0.2);
|
||||
transition: background-color 0.2s ease;
|
||||
}
|
||||
|
||||
.article-editor-scope .delete-block-btn:hover {
|
||||
background-color: #c0392b;
|
||||
}
|
||||
|
||||
/* Steuerungselement für den Plus-Button und das Pop-up */
|
||||
.article-editor-scope .add-block-control {
|
||||
position: relative; /* Dient als Anker für das absolut positionierte Pop-up */
|
||||
margin-top: 10px;
|
||||
display: inline-block;
|
||||
align-self: flex-start; /* Verhindert, dass der Button die volle Breite spannt */
|
||||
}
|
||||
|
||||
/* Der runde Plus-Button */
|
||||
.article-editor-scope .plus-button {
|
||||
width: 45px;
|
||||
height: 45px;
|
||||
border-radius: 50%;
|
||||
background-color: #3498db;
|
||||
color: #ffffff;
|
||||
border: none;
|
||||
font-size: 26px;
|
||||
font-weight: 300;
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
box-shadow: 0 2px 6px rgba(0, 0, 0, 0.15);
|
||||
transition: background-color 0.2s ease, transform 0.2s ease;
|
||||
}
|
||||
|
||||
.article-editor-scope .plus-button:hover {
|
||||
background-color: #2980b9;
|
||||
transform: scale(1.05);
|
||||
}
|
||||
|
||||
/* Das Pop-up Menü */
|
||||
.article-editor-scope .block-popup {
|
||||
position: absolute;
|
||||
left: 60px; /* Platziert das Menü rechts neben dem Plus-Button */
|
||||
top: 50%;
|
||||
transform: translateY(-50%); /* Zentriert das Menü vertikal zum Button */
|
||||
background-color: #ffffff;
|
||||
border: 1px solid #e0e0e0;
|
||||
border-radius: 8px;
|
||||
padding: 8px;
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
box-shadow: 0 4px 15px rgba(0, 0, 0, 0.15);
|
||||
z-index: 999;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
/* Die entscheidende Klasse zum Ausblenden */
|
||||
.article-editor-scope .block-popup.hidden {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
/* Buttons im Pop-up (Textblock / Bild einfügen) */
|
||||
.article-editor-scope .block-popup button {
|
||||
background-color: #f8f9fa;
|
||||
border: 1px solid #dcdde1;
|
||||
padding: 8px 16px;
|
||||
border-radius: 6px;
|
||||
cursor: pointer;
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
color: #2f3640;
|
||||
transition: background-color 0.15s ease, border-color 0.15s ease;
|
||||
}
|
||||
|
||||
.article-editor-scope .block-popup button:hover {
|
||||
background-color: #f1f2f6;
|
||||
border-color: #b2bec3;
|
||||
}
|
||||
|
||||
/* Responsive Anpassungen unter 760px (für z.B. Smartphones) */
|
||||
@media (max-width: 760px) {
|
||||
.article-editor-scope.editor-container {
|
||||
@@ -113,4 +242,8 @@
|
||||
border-left: none;
|
||||
border-top: 1px solid #e0e0e0;
|
||||
}
|
||||
|
||||
.article-editor-scope .editor-block textarea {
|
||||
min-height: 150px;
|
||||
}
|
||||
}
|
||||
@@ -247,6 +247,17 @@ CSS für die Suchergebnis-Seite
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
.s-res-meta-row {
|
||||
display: flex;
|
||||
gap: 15px;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.s-res-likes {
|
||||
font-size: 0.9em;
|
||||
color: #475569;
|
||||
}
|
||||
|
||||
/* Responsive Anpassungen unter 760px (für z.B. Smartphones) */
|
||||
@media (max-width: 768px) {
|
||||
.s-res-layout-grid {
|
||||
|
||||
@@ -105,6 +105,37 @@
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
.article-view-content {
|
||||
margin-bottom: 3rem;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 1.5rem; /* Erzeugt einen sauberen Abstand zwischen den einzelnen Blöcken */
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.article-view-body {
|
||||
font-size: 1.125rem;
|
||||
color: #2d3748;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.article-view-body.block-text {
|
||||
white-space: pre-line;
|
||||
word-break: break-word;
|
||||
}
|
||||
|
||||
.article-view-body.block-image {
|
||||
display: flex;
|
||||
justify-content: center; /* Zentriert das Bild horizontal */
|
||||
}
|
||||
|
||||
.article-view-body.block-image img {
|
||||
max-width: 100%; /* Verhindert das Ausbrechen aus der Lesebreite */
|
||||
height: auto; /* Behält das originale Seitenverhältnis bei */
|
||||
border-radius: 6px; /* Optionale leichte Rundung für ein moderneres Layout */
|
||||
box-shadow: 0 4px 10px rgba(0, 0, 0, 0.03); /* Minimaler, eleganter Schatten */
|
||||
}
|
||||
|
||||
/* Responsive Anpassungen unter 760px (für z.B. Smarticlephones) */
|
||||
@media (max-width: 760px) {
|
||||
.article-view-container {
|
||||
@@ -116,3 +147,140 @@
|
||||
font-size: 1.85rem;
|
||||
}
|
||||
}
|
||||
|
||||
/* --- KOMMENTARE --- */
|
||||
|
||||
.article-comments-section {
|
||||
margin-top: 3rem;
|
||||
padding-top: 2rem;
|
||||
border-top: 1px solid #e2e8f0;
|
||||
}
|
||||
|
||||
.article-comments-section h2 {
|
||||
font-size: 2rem;
|
||||
margin-bottom: 1.5rem;
|
||||
}
|
||||
|
||||
#comments-list {
|
||||
margin-bottom: 2rem;
|
||||
}
|
||||
|
||||
.comment-item {
|
||||
background-color: #f8fafc;
|
||||
border: 1px solid #e2e8f0;
|
||||
border-radius: 10px;
|
||||
padding: 1rem 1.25rem;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
#comment-form {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 1rem;
|
||||
}
|
||||
|
||||
#comment-content {
|
||||
width: 100%;
|
||||
min-height: 130px;
|
||||
padding: 1rem;
|
||||
border: 1px solid #cbd5e1;
|
||||
border-radius: 8px;
|
||||
font-size: 1rem;
|
||||
font-family: inherit;
|
||||
resize: vertical;
|
||||
}
|
||||
|
||||
#comment-form .button {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.reply-button {
|
||||
display: inline-block;
|
||||
margin-top: 0.75rem;
|
||||
background: none;
|
||||
border: none;
|
||||
color: #2563eb;
|
||||
font-weight: 700;
|
||||
cursor: pointer;
|
||||
padding: 0;
|
||||
font-size: 0.95rem;
|
||||
}
|
||||
|
||||
.reply-button:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
.comment-replies {
|
||||
margin-top: 1rem;
|
||||
margin-left: 2rem;
|
||||
padding-left: 1rem;
|
||||
border-left: 3px solid #cbd5e1;
|
||||
}
|
||||
|
||||
.comment-reply {
|
||||
background-color: #eef6ff;
|
||||
margin-top: 1rem;
|
||||
}
|
||||
|
||||
.reply-info {
|
||||
margin: 0.5rem 0;
|
||||
color: #475569;
|
||||
font-weight: 600;
|
||||
}
|
||||
.comment-login-hint {
|
||||
margin-top: 2rem;
|
||||
padding: 1.5rem;
|
||||
background-color: #f8fafc;
|
||||
border: 1px solid #e2e8f0;
|
||||
border-radius: 10px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.comment-login-hint p {
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
/*
|
||||
Like-Button etc.
|
||||
*/
|
||||
|
||||
.category-and-likes-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.article-view-likes {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
font-size: 0.95em;
|
||||
}
|
||||
|
||||
.article-view-likes .like-count {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.article-view-likes .login-hint {
|
||||
font-size: 0.8em;
|
||||
color: #777;
|
||||
}
|
||||
|
||||
/* Interaktiver Like/Unlike-Button */
|
||||
.like-toggle-btn {
|
||||
text-decoration: none;
|
||||
padding: 4px 10px;
|
||||
border: 1px solid #bbb;
|
||||
border-radius: 4px;
|
||||
background-color: #f5f5f5;
|
||||
color: #333;
|
||||
font-weight: 500;
|
||||
transition: background-color 0.2s ease, border-color 0.2s ease;
|
||||
}
|
||||
|
||||
.like-toggle-btn:hover {
|
||||
background-color: #eaeaea;
|
||||
border-color: #999;
|
||||
}
|
||||
@@ -15,7 +15,7 @@
|
||||
<?php endif; ?>
|
||||
<?php if (isset($_SESSION["message"]) && $_SESSION["message"] == "invalid_content"): ?>
|
||||
<p class="alert-message is-error">
|
||||
Der Text erlaubt eine Länge von 10 bis maximal 7.000 Zeichen (ca. 1.000 Wörter).
|
||||
Ein Beitrag muss Inhalt besitzen. Text- und Bildelemente dürfen nicht leer sein!
|
||||
</p>
|
||||
<?php endif; ?>
|
||||
<?php if (isset($_SESSION["message"]) && $_SESSION["message"] == "invalid_category"): ?>
|
||||
@@ -63,6 +63,21 @@
|
||||
Dein Beitrag wurde erfolgreich veröffentlicht!
|
||||
</p>
|
||||
<?php endif; ?>
|
||||
<?php if (isset($_SESSION["message"]) && $_SESSION["message"] == "article_updated"): ?>
|
||||
<p class="alert-message is-success">
|
||||
Der Beitrag wurde erfolgreich bearbeitet und gespeichert.
|
||||
</p>
|
||||
<?php endif; ?>
|
||||
<?php if (isset($_SESSION["message"]) && $_SESSION["message"] == "profile_updated"): ?>
|
||||
<p class="alert-message is-success">
|
||||
Das Profil wurde erfolgreich bearbeitet.
|
||||
</p>
|
||||
<?php endif; ?>
|
||||
<?php if (isset($_SESSION["message"]) && $_SESSION["message"] == "image_upload_error"): ?>
|
||||
<p class="alert-message is-error">
|
||||
Das Bild konnte nicht hochgeladen werden. Bitte versuche es erneut oder verwende ein anderes Bildformat.
|
||||
</p>
|
||||
<?php endif; ?>
|
||||
<?php
|
||||
unset($_SESSION["message"]);
|
||||
?>
|
||||
|
||||
@@ -53,6 +53,10 @@ if ($pfad === "deleteAccount") {
|
||||
<link rel="stylesheet" href="css/message.css">
|
||||
|
||||
<script src="js/paginator.js" async></script>
|
||||
<script src="js/sorter.js" async></script>
|
||||
<script src="js/comments.js" defer></script>
|
||||
<script src="js/editor.js" async></script>
|
||||
<script src="js/filter.js" async></script>
|
||||
|
||||
<title>EduForge</title>
|
||||
</head>
|
||||
|
||||
+137
@@ -0,0 +1,137 @@
|
||||
/**
|
||||
* Initialisiert die Kommentarfunktion.
|
||||
*
|
||||
* Kommentare werden per AJAX gespeichert,
|
||||
* ohne dass die Seite neu geladen werden muss.
|
||||
*/
|
||||
document.addEventListener("DOMContentLoaded", function () {
|
||||
const form = document.getElementById("comment-form");
|
||||
const commentsList = document.getElementById("comments-list");
|
||||
const commentContent = document.getElementById("comment-content");
|
||||
const parentCommentInput = document.getElementById("parent-comment-id");
|
||||
const replyInfo = document.getElementById("reply-info");
|
||||
|
||||
if (!form || !commentsList || !commentContent || !parentCommentInput || !replyInfo) {
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
* Aktiviert einen einzelnen Antworten-Button.
|
||||
*
|
||||
* @param {HTMLButtonElement} button Antworten-Button
|
||||
*/
|
||||
function registerReplyButton(button) {
|
||||
button.addEventListener("click", function () {
|
||||
parentCommentInput.value = button.dataset.commentId;
|
||||
replyInfo.textContent = "Antwort auf " + button.dataset.author;
|
||||
replyInfo.style.display = "block";
|
||||
commentContent.focus();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Registriert alle bereits vorhandenen Antwort-Buttons.
|
||||
*/
|
||||
document.querySelectorAll(".reply-button").forEach(function (button) {
|
||||
registerReplyButton(button);
|
||||
});
|
||||
|
||||
/**
|
||||
* Sendet Kommentare per AJAX an den Server.
|
||||
*/
|
||||
form.addEventListener("submit", function (event) {
|
||||
event.preventDefault();
|
||||
|
||||
const formData = new FormData(form);
|
||||
const parentCommentId = parentCommentInput.value;
|
||||
|
||||
fetch("php/ajax/add-comment.php", {
|
||||
method: "POST",
|
||||
body: formData
|
||||
})
|
||||
.then(response => response.json())
|
||||
.then(data => {
|
||||
if (!data.success) {
|
||||
alert(data.message);
|
||||
return;
|
||||
}
|
||||
|
||||
const emptyMessage = commentsList.querySelector(".no-comments-message");
|
||||
|
||||
if (emptyMessage) {
|
||||
emptyMessage.remove();
|
||||
}
|
||||
|
||||
const commentElement = document.createElement("div");
|
||||
commentElement.classList.add("comment-item");
|
||||
|
||||
if (parentCommentId) {
|
||||
commentElement.classList.add("comment-reply");
|
||||
|
||||
commentElement.innerHTML = `
|
||||
<p>
|
||||
<strong>${escapeHtml(data.author)}</strong>
|
||||
<span>${escapeHtml(data.created)}</span>
|
||||
</p>
|
||||
<p>${escapeHtml(data.content).replace(/\n/g, "<br>")}</p>
|
||||
`;
|
||||
|
||||
const parentReplies = document.querySelector(
|
||||
`.comment-item[data-comment-id="${parentCommentId}"] .comment-replies`
|
||||
);
|
||||
|
||||
if (parentReplies) {
|
||||
parentReplies.appendChild(commentElement);
|
||||
}
|
||||
|
||||
} else {
|
||||
commentElement.dataset.commentId = data.commentId;
|
||||
|
||||
commentElement.innerHTML = `
|
||||
<p>
|
||||
<strong>${escapeHtml(data.author)}</strong>
|
||||
<span>${escapeHtml(data.created)}</span>
|
||||
</p>
|
||||
<p>${escapeHtml(data.content).replace(/\n/g, "<br>")}</p>
|
||||
|
||||
<button type="button"
|
||||
class="reply-button"
|
||||
data-comment-id="${escapeHtml(data.commentId)}"
|
||||
data-author="${escapeHtml(data.author)}">
|
||||
Antworten
|
||||
</button>
|
||||
|
||||
<div class="comment-replies"></div>
|
||||
`;
|
||||
|
||||
commentsList.prepend(commentElement);
|
||||
|
||||
const newReplyButton = commentElement.querySelector(".reply-button");
|
||||
|
||||
if (newReplyButton) {
|
||||
registerReplyButton(newReplyButton);
|
||||
}
|
||||
}
|
||||
|
||||
commentContent.value = "";
|
||||
parentCommentInput.value = "";
|
||||
replyInfo.textContent = "";
|
||||
replyInfo.style.display = "none";
|
||||
})
|
||||
.catch(() => {
|
||||
alert("Kommentar konnte nicht gesendet werden.");
|
||||
});
|
||||
});
|
||||
|
||||
/**
|
||||
* Entfernt HTML-Sonderzeichen aus Nutzereingaben.
|
||||
*
|
||||
* @param {string} text Zu bereinigender Text
|
||||
* @returns {string} Sicherer Text
|
||||
*/
|
||||
function escapeHtml(text) {
|
||||
const div = document.createElement("div");
|
||||
div.textContent = text;
|
||||
return div.innerHTML;
|
||||
}
|
||||
});
|
||||
+179
@@ -0,0 +1,179 @@
|
||||
console.log("Die JavaScript-Datei wurde erfolgreich geladen!");
|
||||
|
||||
function initEditor() {
|
||||
const form = document.getElementById("editor-form");
|
||||
|
||||
if (!form) {
|
||||
console.error("Skript abgebrochen: Formular nicht gefunden!");
|
||||
return;
|
||||
} else {
|
||||
console.log("Formular gefunden und Editor initialisiert:", form);
|
||||
}
|
||||
|
||||
const container = document.getElementById("block-container");
|
||||
const plusButton = document.getElementById("plus-button");
|
||||
const popup = document.getElementById("block-popup");
|
||||
const hiddenContentInput = document.getElementById("content");
|
||||
|
||||
const initialImages = [];
|
||||
|
||||
// Pop-up umschalten bei Klick auf das Plus
|
||||
plusButton.addEventListener("click", () => {
|
||||
popup.classList.toggle("hidden");
|
||||
});
|
||||
|
||||
// Klick auf eine Block-Option im Pop-up
|
||||
popup.querySelectorAll("button").forEach(btn => {
|
||||
btn.addEventListener("click", function() {
|
||||
const type = this.getAttribute("data-type");
|
||||
addBlockElement(type, "");
|
||||
popup.classList.add("hidden");
|
||||
});
|
||||
});
|
||||
|
||||
// Erstellt ein visuelles HTML-Element im Editor
|
||||
function addBlockElement(type, value = "") {
|
||||
const blockDiv = document.createElement("div");
|
||||
blockDiv.classList.add("editor-block");
|
||||
blockDiv.setAttribute("data-type", type);
|
||||
|
||||
// Wenn es ein existierendes Server-Bild beim Laden ist, Pfad im globalen Array sichern
|
||||
if (type === "image" && value && typeof value === 'string' && value.startsWith('uploads/')) {
|
||||
initialImages.push(value);
|
||||
blockDiv.setAttribute("data-value", value);
|
||||
}
|
||||
|
||||
// Löschen-Button
|
||||
const deleteBtn = document.createElement("button");
|
||||
deleteBtn.type = "button";
|
||||
deleteBtn.innerHTML = "✕";
|
||||
deleteBtn.classList.add("delete-block-btn");
|
||||
deleteBtn.addEventListener("click", () => {
|
||||
// ANPASSUNG 2B: Logik hier komplett geleert. Das '✕' entfernt den Block jetzt nur noch sicher aus dem HTML.
|
||||
blockDiv.remove();
|
||||
});
|
||||
blockDiv.appendChild(deleteBtn);
|
||||
|
||||
if (type === "text") {
|
||||
const textarea = document.createElement("textarea");
|
||||
textarea.placeholder = "Schreibe deinen Textblock...";
|
||||
textarea.value = value;
|
||||
blockDiv.appendChild(textarea);
|
||||
} else if (type === "image") {
|
||||
const fileInput = document.createElement("input");
|
||||
fileInput.type = "file";
|
||||
fileInput.accept = "image/*";
|
||||
|
||||
const imgPreview = document.createElement("img");
|
||||
imgPreview.style.maxWidth = "200px";
|
||||
imgPreview.style.display = "block";
|
||||
imgPreview.style.marginTop = "10px";
|
||||
|
||||
if (value && typeof value === 'string') {
|
||||
if (value.startsWith('uploads/') || value.startsWith('data:image/')) {
|
||||
imgPreview.src = value;
|
||||
blockDiv.setAttribute("data-value", value);
|
||||
}
|
||||
}
|
||||
|
||||
fileInput.addEventListener("change", function() {
|
||||
if (this.files && this.files[0]) {
|
||||
const reader = new FileReader();
|
||||
reader.onload = function(e) {
|
||||
imgPreview.src = e.target.result;
|
||||
blockDiv.setAttribute("data-value", e.target.result);
|
||||
}
|
||||
reader.readAsDataURL(this.files[0]);
|
||||
}
|
||||
});
|
||||
|
||||
blockDiv.appendChild(fileInput);
|
||||
blockDiv.appendChild(imgPreview);
|
||||
}
|
||||
|
||||
container.appendChild(blockDiv);
|
||||
}
|
||||
|
||||
// beim Abschicken verbleibende Blöcke auslesen UND gelöschte Bilder ermitteln
|
||||
form.addEventListener("submit", function(e) {
|
||||
const blocks = [];
|
||||
const currentImages = [];
|
||||
|
||||
// alle aktuell im Formular verbliebenen Blöcke scannen
|
||||
container.querySelectorAll(".editor-block").forEach(blockDiv => {
|
||||
const type = blockDiv.getAttribute("data-type");
|
||||
let value = "";
|
||||
|
||||
if (type === "text") {
|
||||
value = blockDiv.querySelector("textarea").value;
|
||||
} else if (type === "image") {
|
||||
|
||||
const imgTag = blockDiv.querySelector("img");
|
||||
if (imgTag) {
|
||||
const srcValue = imgTag.getAttribute("src") || "";
|
||||
// Wenn es ein neues Bild ist, nutzen wir das data-value (Base64)
|
||||
if (srcValue.startsWith('data:image/')) {
|
||||
value = blockDiv.getAttribute("data-value") || "";
|
||||
} else {
|
||||
value = srcValue;
|
||||
}
|
||||
}
|
||||
|
||||
// Pfade sammeln, die der Nutzer NICHT gelöscht hat (für den Abgleich)
|
||||
if (value && value.startsWith('uploads/')) {
|
||||
currentImages.push(value);
|
||||
}
|
||||
}
|
||||
|
||||
blocks.push({ type: type, value: value });
|
||||
});
|
||||
|
||||
// das reguläre unsichtbare Content-Feld befüllen
|
||||
hiddenContentInput.value = JSON.stringify(blocks);
|
||||
|
||||
// Differenz berechnen: Welche Bilder aus 'initialImages' fehlen in 'currentImages' ?
|
||||
const deletedImages = initialImages.filter(img => !currentImages.includes(img));
|
||||
|
||||
// das 'deleted_images'-Feld dynamisch befüllen und an den Controller senden
|
||||
let deletedInput = document.getElementById("deleted-images");
|
||||
if (!deletedInput) {
|
||||
deletedInput = document.createElement("input");
|
||||
deletedInput.type = "hidden";
|
||||
deletedInput.id = "deleted-images";
|
||||
deletedInput.name = "deleted_images";
|
||||
form.appendChild(deletedInput);
|
||||
}
|
||||
deletedInput.value = JSON.stringify(deletedImages);
|
||||
});
|
||||
|
||||
// Existierende Blöcke laden (stellt alte Daten aus der Session wieder her)
|
||||
try {
|
||||
const initialBlocks = JSON.parse(hiddenContentInput.value.trim());
|
||||
if (Array.isArray(initialBlocks)) {
|
||||
initialBlocks.forEach(b => {
|
||||
if (b.type === "image" && b.value && typeof b.value === 'string' && !b.value.startsWith('data:image/')) {
|
||||
let cleanPath = b.value.trim().replace(/\\\//g, '/'); // Verwandelt \/ in /
|
||||
|
||||
initialImages.push(cleanPath);
|
||||
addBlockElement(b.type, cleanPath);
|
||||
|
||||
} else {
|
||||
addBlockElement(b.type, b.value);
|
||||
}
|
||||
});
|
||||
console.log("Erfolgreich registrierte Start-Bilder:", initialImages);
|
||||
}
|
||||
} catch(e) {
|
||||
if (hiddenContentInput.value.trim() !== "") {
|
||||
addBlockElement("text", hiddenContentInput.value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// SICHERER START: Prüft, ob das HTML bereits bereit ist
|
||||
if (document.readyState === "loading") {
|
||||
document.addEventListener("DOMContentLoaded", initEditor);
|
||||
} else {
|
||||
// Falls das DOM schon fertig geladen ist, führen wir es direkt aus
|
||||
initEditor();
|
||||
}
|
||||
+101
@@ -0,0 +1,101 @@
|
||||
let currentClientPage = 1;
|
||||
const itemsPerPage = 10;
|
||||
|
||||
function initFilter() {
|
||||
const filterSelect = document.getElementById('category-filter');
|
||||
const listContainer = document.querySelector('.s-res-list');
|
||||
|
||||
if (!filterSelect || !listContainer) return;
|
||||
|
||||
updateVisibility();
|
||||
|
||||
filterSelect.addEventListener('change', function() {
|
||||
currentClientPage = 1;
|
||||
updateVisibility();
|
||||
});
|
||||
|
||||
const navigationContainer = document.querySelector('.s-res-page-navigation');
|
||||
if (navigationContainer) {
|
||||
navigationContainer.addEventListener('click', function(e) {
|
||||
const button = e.target.closest('.s-res-page-btn');
|
||||
if (!button || button.disabled) return;
|
||||
|
||||
e.preventDefault();
|
||||
|
||||
const targetPage = button.getAttribute('data-page');
|
||||
if (targetPage) {
|
||||
currentClientPage = parseInt(targetPage, 10);
|
||||
updateVisibility();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function updateVisibility() {
|
||||
const filterSelect = document.getElementById('category-filter');
|
||||
const listContainer = document.querySelector('.s-res-list');
|
||||
const selectedCategory = filterSelect.value.toLowerCase().trim();
|
||||
const cards = listContainer.querySelectorAll('.s-res-item');
|
||||
|
||||
let visibleCards = [];
|
||||
cards.forEach(card => {
|
||||
const cardCategory = (card.getAttribute('data-category') || '').toLowerCase().trim();
|
||||
if (selectedCategory === 'all' || cardCategory.includes(selectedCategory) || selectedCategory.includes(cardCategory)) {
|
||||
visibleCards.push(card);
|
||||
} else {
|
||||
card.style.display = 'none';
|
||||
}
|
||||
});
|
||||
|
||||
const totalVisible = visibleCards.length;
|
||||
const totalPages = Math.max(1, Math.ceil(totalVisible / itemsPerPage));
|
||||
|
||||
if (currentClientPage < 1) currentClientPage = 1;
|
||||
if (currentClientPage > totalPages) currentClientPage = totalPages;
|
||||
|
||||
const startOffset = (currentClientPage - 1) * itemsPerPage;
|
||||
const endOffset = startOffset + itemsPerPage;
|
||||
|
||||
visibleCards.forEach((card, index) => {
|
||||
if (index >= startOffset && index < endOffset) {
|
||||
card.style.display = 'flex';
|
||||
} else {
|
||||
card.style.display = 'none';
|
||||
}
|
||||
});
|
||||
|
||||
updatePaginatorUI(currentClientPage, totalPages);
|
||||
}
|
||||
|
||||
function updatePaginatorUI(currentPage, totalPages) {
|
||||
const prevBtn = document.getElementById('prev-page-btn');
|
||||
const nextBtn = document.getElementById('next-page-btn');
|
||||
const numbersContainer = document.getElementById('dynamic-page-numbers');
|
||||
|
||||
if (!prevBtn || !nextBtn || !numbersContainer) return;
|
||||
|
||||
prevBtn.setAttribute('data-page', currentPage - 1);
|
||||
prevBtn.disabled = (currentPage <= 1);
|
||||
|
||||
nextBtn.setAttribute('data-page', currentPage + 1);
|
||||
nextBtn.disabled = (currentPage >= totalPages);
|
||||
|
||||
let buttonsHTML = '';
|
||||
for (let i = 1; i <= totalPages; i++) {
|
||||
const activeClass = (i === currentPage) ? 's-res-page-btn-active' : '';
|
||||
buttonsHTML += `<button type="button" class="s-res-page-btn ${activeClass}" data-page="${i}">${i}</button> `;
|
||||
}
|
||||
numbersContainer.innerHTML = buttonsHTML;
|
||||
}
|
||||
|
||||
// Hilfsfunktion für Math.ceil in JS
|
||||
function ceil(val) { return Math.ceil(val); }
|
||||
|
||||
// ist das DOM bereits vollständig aufgebaut?
|
||||
if (document.readyState === 'loading') {
|
||||
// Falls noch geladen wird, auf das Event warten
|
||||
document.addEventListener('DOMContentLoaded', initFilter);
|
||||
} else {
|
||||
// Falls das HTML bereits komplett da ist, sofort ausführen
|
||||
initFilter();
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
|
||||
function initSorter() {
|
||||
const listContainer = document.querySelector('.s-res-list');
|
||||
const sortRadios = document.querySelectorAll('.sort-radio');
|
||||
|
||||
// wenn keine liste vorhanden, abbrechen
|
||||
if (!listContainer || sortRadios.length === 0) return;
|
||||
|
||||
sortRadios.forEach(radio => {
|
||||
radio.addEventListener('change', function() {
|
||||
const cards = Array.from(listContainer.querySelectorAll('.s-res-item'));
|
||||
const sortValue = this.value;
|
||||
|
||||
cards.sort((a, b) => {
|
||||
if (sortValue === 'likes') {
|
||||
const likesA = parseInt(a.getAttribute('data-likes') || '0', 10);
|
||||
const likesB = parseInt(b.getAttribute('data-likes') || '0', 10);
|
||||
return likesB - likesA;
|
||||
}
|
||||
else if (sortValue === 'alphabet') {
|
||||
// alphabetische sortierung
|
||||
const titleA = a.querySelector('.s-res-link').textContent.trim().toLowerCase();
|
||||
const titleB = b.querySelector('.s-res-link').textContent.trim().toLowerCase();
|
||||
return titleA.localeCompare(titleB);
|
||||
}
|
||||
else if (sortValue === 'newest' || sortValue === 'oldest') {
|
||||
// hoehere ID wird als neuer gesehen
|
||||
const urlA = a.querySelector('.s-res-link').getAttribute('href');
|
||||
const urlB = b.querySelector('.s-res-link').getAttribute('href');
|
||||
|
||||
const idA = parseInt(urlA.match(/id=(\d+)/)[1], 10);
|
||||
const idB = parseInt(urlB.match(/id=(\d+)/)[1], 10);
|
||||
|
||||
return sortValue === 'newest' ? idB - idA : idA - idB;
|
||||
}
|
||||
return 0;
|
||||
});
|
||||
|
||||
listContainer.innerHTML = '';
|
||||
cards.forEach(card => listContainer.appendChild(card));
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// ist das DOM bereits vollständig aufgebaut?
|
||||
if (document.readyState === 'loading') {
|
||||
document.addEventListener('DOMContentLoaded', initSorter);
|
||||
} else {
|
||||
initSorter();
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
<?php
|
||||
if (session_status() === PHP_SESSION_NONE) {
|
||||
session_start();
|
||||
}
|
||||
|
||||
header("Content-Type: application/json");
|
||||
|
||||
require_once "../model/CommentManager.php";
|
||||
|
||||
if (!isset($_SESSION["user_email"])) {
|
||||
echo json_encode([
|
||||
"success" => false,
|
||||
"message" => "Du musst angemeldet sein, um zu kommentieren."
|
||||
]);
|
||||
exit();
|
||||
}
|
||||
|
||||
$articleId = $_POST["article_id"] ?? null;
|
||||
$content = trim($_POST["content"] ?? "");
|
||||
$parentCommentId = $_POST["parent_comment_id"] ?? null;
|
||||
|
||||
if ($parentCommentId === "" || $parentCommentId === "0") {
|
||||
$parentCommentId = null;
|
||||
}
|
||||
|
||||
if (empty($articleId) || empty($content)) {
|
||||
echo json_encode([
|
||||
"success" => false,
|
||||
"message" => "Kommentar darf nicht leer sein."
|
||||
]);
|
||||
exit();
|
||||
}
|
||||
|
||||
try {
|
||||
$commentManager = CommentManager::getInstance();
|
||||
|
||||
$commentId = $commentManager->addComment(
|
||||
$articleId,
|
||||
$_SESSION["user_email"],
|
||||
$content,
|
||||
$parentCommentId
|
||||
);
|
||||
|
||||
echo json_encode([
|
||||
"success" => true,
|
||||
"commentId" => $commentId,
|
||||
"author" => $_SESSION["user_email"],
|
||||
"content" => $content,
|
||||
"created" => date("Y-m-d H:i:s"),
|
||||
"parentCommentId" => $parentCommentId
|
||||
]);
|
||||
|
||||
} catch (Exception $e) {
|
||||
echo json_encode([
|
||||
"success" => false,
|
||||
"message" => "Kommentar konnte nicht gespeichert werden."
|
||||
]);
|
||||
}
|
||||
@@ -6,6 +6,10 @@ require_once '../model/LocalArticleManager.php';
|
||||
require_once '../model/ArticleManager.php';
|
||||
require_once '../validator/article-validator.php';
|
||||
|
||||
if (!isset($_SESSION["user"])) {
|
||||
header("Location: index.php?pfad=login");
|
||||
exit();
|
||||
}
|
||||
if ($_SERVER["REQUEST_METHOD"] === "POST") {
|
||||
$_SESSION["old_title"] = $_POST["title"] ?? '';
|
||||
$_SESSION["old_content"] = $_POST["content"] ?? '';
|
||||
@@ -24,12 +28,6 @@ require_once '../validator/article-validator.php';
|
||||
$tags = $_POST['tags'] ?? '';
|
||||
|
||||
// -------------------------------- Validierung der Daten: -------------------------
|
||||
if (!articleAuthorValidator($author)) {
|
||||
$_SESSION["message"] = "author_not_valid";
|
||||
header("location: ../../index.php?pfad=createArticle");
|
||||
exit();
|
||||
}
|
||||
|
||||
if (!articleTitleValidator($title)) {
|
||||
$_SESSION["message"] = "invalid_title";
|
||||
header("location: ../../index.php?pfad=createArticle");
|
||||
@@ -64,6 +62,59 @@ require_once '../validator/article-validator.php';
|
||||
$cleanedTags = array_unique($cleanedTags);
|
||||
$cleanedTags = implode(',', $cleanedTags);
|
||||
}
|
||||
|
||||
// ----------------- Base64-Bilder verarbeiten und auf Server speichern -----------------
|
||||
$blocks = json_decode($content, true);
|
||||
$uploadDir = __DIR__ . '/../../uploads/';
|
||||
|
||||
if (!file_exists($uploadDir)) {
|
||||
mkdir($uploadDir, 0755, true);
|
||||
}
|
||||
|
||||
if (is_array($blocks)) {
|
||||
foreach ($blocks as &$block) {
|
||||
// sicherstellen, dass 'type' und 'value' existieren:
|
||||
if (isset($block['type']) && isset($block['value']) && $block['type'] === 'image' && str_starts_with($block['value'], 'data:image/')) {
|
||||
|
||||
// Base64-String zerlegen
|
||||
$parts = explode(',', $block['value']);
|
||||
|
||||
// falls der String korrupt ist und kein Komma hat
|
||||
if (count($parts) < 2) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$metadata = $parts[0];
|
||||
$base64Data = $parts[1];
|
||||
|
||||
// Dateiendung ermitteln
|
||||
preg_match('/data:image\/(?<extension>.*?);/', $metadata, $matches);
|
||||
$extension = $matches['extension'] ?? 'jpg';
|
||||
if ($extension === 'jpeg') {
|
||||
$extension = 'jpg';
|
||||
}
|
||||
|
||||
// Eindeutigen Dateinamen generieren
|
||||
$fileName = 'img_' . uniqid() . '.' . $extension;
|
||||
$filePath = $uploadDir . $fileName;
|
||||
|
||||
// Datei im /uploads speichern:
|
||||
if (file_put_contents($filePath, base64_decode($base64Data)) !== false) {
|
||||
// temporären Base64-String durch den echten Pfad ersetzen
|
||||
$block['value'] = 'uploads/' . $fileName;
|
||||
} else {
|
||||
$_SESSION["message"] = "image_upload_error";
|
||||
header("location: ../../index.php?pfad=createArticle");
|
||||
exit();
|
||||
}
|
||||
}
|
||||
}
|
||||
unset($block);
|
||||
}
|
||||
|
||||
// Aktualisiertes Array wieder in JSON konvertieren
|
||||
$finalContent = json_encode($blocks, JSON_UNESCAPED_UNICODE);
|
||||
|
||||
// ----------------- Übertragung der validierten Daten in ArticleManager: ---------------------------
|
||||
try {
|
||||
$articleManager = ArticleManager::getInstance();
|
||||
@@ -72,7 +123,7 @@ require_once '../validator/article-validator.php';
|
||||
// Formulardaten nach erfolgreichem Erstellen aus der Session löschen
|
||||
unset($_SESSION["old_title"], $_SESSION["old_content"], $_SESSION["old_category"], $_SESSION["old_tags"]);
|
||||
|
||||
} catch (Exception $e){
|
||||
} catch (\Throwable $e){
|
||||
$_SESSION["message"] = "internal_error";
|
||||
header("location: ../../index.php?pfad=createArticle");
|
||||
exit();
|
||||
@@ -84,7 +135,4 @@ require_once '../validator/article-validator.php';
|
||||
exit();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
?>
|
||||
@@ -6,6 +6,11 @@ if (session_status() === PHP_SESSION_NONE) {
|
||||
require_once __DIR__ . "/../model/UserManager.php";
|
||||
require_once __DIR__ . "/../model/ArticleManager.php";
|
||||
|
||||
if (!isset($_SESSION["user"])) {
|
||||
header("Location: index.php?pfad=login");
|
||||
exit();
|
||||
}
|
||||
|
||||
/*
|
||||
Deregistrierung
|
||||
Funktion: Entfernt User aus der Datenbank und beendet die Session
|
||||
|
||||
@@ -5,6 +5,11 @@ if (session_status() === PHP_SESSION_NONE) {
|
||||
|
||||
require_once __DIR__ . "/../model/ArticleManager.php";
|
||||
|
||||
if (!isset($_SESSION["user"])) {
|
||||
header("Location: index.php?pfad=login");
|
||||
exit();
|
||||
}
|
||||
|
||||
if ($_SERVER["REQUEST_METHOD"] === "POST") {
|
||||
|
||||
if (isset($_SESSION["user_email"])) {
|
||||
|
||||
@@ -6,14 +6,44 @@ require_once 'php/model/Article.php';
|
||||
require_once 'php/model/ArticleManager.php';
|
||||
require_once 'php/model/LocalArticleManager.php';
|
||||
|
||||
$categoriesWithArticles = [];
|
||||
|
||||
$allowedCategories = [
|
||||
'deutsch', 'englisch', 'franzoesisch', 'latein', 'literatur',
|
||||
'mathe', 'biologie', 'chemie', 'physik', 'informatik', 'astronomie',
|
||||
'geschichte', 'erdkunde', 'sozialkunde', 'wirtschaft', 'religion',
|
||||
'ethik', 'philosophie', 'psychologie', 'kunst', 'musik', 'theater',
|
||||
'technik', 'werken', 'hauswirtschaft', 'sport'
|
||||
];
|
||||
|
||||
try {
|
||||
$articleManager = ArticleManager::getInstance();
|
||||
// Beziehen der Dummy-Beiträge aus dem ArticleManager:
|
||||
$dummy1 = $articleManager->getArticle(1);
|
||||
$dummy2 = $articleManager->getArticle(2);
|
||||
$dummy3 = $articleManager->getArticle(3);
|
||||
|
||||
foreach ($allowedCategories as $categorySlug) {
|
||||
$allCategoryArticles = $articleManager->getArticlesByCategory($categorySlug);
|
||||
|
||||
// nur Kategorien, die Beiträge enthält:
|
||||
if (!empty($allCategoryArticles) && is_array($allCategoryArticles)) {
|
||||
|
||||
// Beiträge nach Erstellungsdatum sortieren:
|
||||
usort($allCategoryArticles, function($a, $b) {
|
||||
$dateA = strtotime($a->getCreationDate());
|
||||
$dateB = strtotime($b->getCreationDate());
|
||||
return $dateB <=> $dateA; // Absteigende Sortierung
|
||||
});
|
||||
|
||||
// auf die 5 zuletzt hinzugefügten Beiträge begrenzen:
|
||||
$limitedArticles = array_slice($allCategoryArticles, 0, 5);
|
||||
|
||||
$categoriesWithArticles[] = [
|
||||
'slug' => $categorySlug,
|
||||
'name' => ucfirst($categorySlug), // Erster Buchstabe groß für die Ansicht
|
||||
'articles' => $limitedArticles
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
} catch (Exception $e){
|
||||
$_SESSION["message"] = "internal_error";
|
||||
echo "Fehler aufgetreten: " . $e->getMessage();
|
||||
}
|
||||
?>
|
||||
@@ -0,0 +1,42 @@
|
||||
<?php
|
||||
if (session_status() === PHP_SESSION_NONE) {
|
||||
session_start();
|
||||
}
|
||||
|
||||
require_once __DIR__ . '/../model/Article.php';
|
||||
require_once __DIR__ . '/../model/ArticleManager.php';
|
||||
|
||||
// 2. Prüfen, ob eine gültige Artikel-ID übergeben wurde
|
||||
if (isset($_GET["id"]) && !empty($_GET["id"])) {
|
||||
$articleId = intval($_GET["id"]);
|
||||
$userEmail = $_SESSION["user_email"];
|
||||
|
||||
if (!isset($_SESSION["user_email"]) || empty($_SESSION["user_email"])) {
|
||||
$_SESSION["message"] = "unauthorized_access";
|
||||
header("Location: ../../index.php?pfad=showArticle&id=" . $articleId);
|
||||
exit();
|
||||
}
|
||||
|
||||
try {
|
||||
$articleManager = ArticleManager::getInstance();
|
||||
$articleManager->toggleLike($articleId, $userEmail);
|
||||
|
||||
header("Location: ../../index.php?pfad=showArticle&id=" . $articleId);
|
||||
exit();
|
||||
|
||||
} catch (NotFoundException $e) {
|
||||
$_SESSION["message"] = "missing_id";
|
||||
header("Location: ../../index.php");
|
||||
exit();
|
||||
} catch (Exception $e) {
|
||||
$_SESSION["message"] = "internal_error";
|
||||
header("Location: ../../index.php");
|
||||
exit();
|
||||
}
|
||||
} else {
|
||||
$_SESSION["message"] = "missing_id";
|
||||
header("Location: ../../index.php");
|
||||
exit();
|
||||
}
|
||||
|
||||
?>
|
||||
@@ -3,6 +3,7 @@
|
||||
require_once "php/model/UserManager.php";
|
||||
require_once "php/model/Article.php";
|
||||
require_once "php/model/ArticleManager.php";
|
||||
require_once "php/model/CommentManager.php";
|
||||
require_once "php/validator/user-validator.php";
|
||||
|
||||
$error = null;
|
||||
@@ -62,6 +63,7 @@ try {
|
||||
$_SESSION["user"] = $vorname . " " . $nachname;
|
||||
$_SESSION["user_email"] = $newEmail;
|
||||
|
||||
$_SESSION["message"] = "profile_updated";
|
||||
header("Location: index.php?pfad=profile");
|
||||
exit();
|
||||
} else {
|
||||
@@ -76,6 +78,9 @@ try {
|
||||
$articleManager = ArticleManager::getInstance();
|
||||
$userArticles = $articleManager->getArticlesByAuthor($_SESSION["user_email"]);
|
||||
|
||||
$commentManager = CommentManager::getInstance();
|
||||
$userComments = $commentManager->getCommentsByAuthor($_SESSION["user_email"]);
|
||||
|
||||
if (!isset($userArticles)) {
|
||||
$_SESSION["message"] = "user_has_no_articles";
|
||||
}
|
||||
|
||||
@@ -5,14 +5,15 @@ if (session_status() === PHP_SESSION_NONE) {
|
||||
require_once '../model/LocalArticleManager.php';
|
||||
require_once '../model/ArticleManager.php';
|
||||
require_once '../model/Article.php';
|
||||
require_once '../validator/search-validator.php';
|
||||
|
||||
if ($_SERVER["REQUEST_METHOD"] === "GET" && isset($_GET["q"])) {
|
||||
|
||||
$search = trim($_GET["q"]);
|
||||
if (empty($search)) {
|
||||
if (!searchQueryValidator($search)) {
|
||||
$_SESSION["search_results"] = [];
|
||||
$_SESSION["search_query"] = "";
|
||||
$_SESSION["message"] = "missing_parameters";
|
||||
$_SESSION["message"] = "invalid_search_query";
|
||||
} else {
|
||||
try {
|
||||
|
||||
@@ -25,18 +26,22 @@ if ($_SERVER["REQUEST_METHOD"] === "GET" && isset($_GET["q"])) {
|
||||
|
||||
if ($sortStyle === 'alphabet') {
|
||||
// Titel aufsteigend alphabetiisch sortiert
|
||||
usort($results, function ($a, $b) {
|
||||
return strcasecmp($a->getTitle(), $b->getTitle());
|
||||
});
|
||||
} elseif ($sortStyle === 'likes') {
|
||||
usort($results, function($a, $b) {
|
||||
return strcasecmp($a->title, $b->title);
|
||||
return $b->getLikeCount() <=> $a->getLikeCount();
|
||||
});
|
||||
} elseif ($sortStyle === 'newest') {
|
||||
// Datum neu zu alt sortiert
|
||||
usort($results, function($a, $b) {
|
||||
return strcmp($b->creationDate, $a->creationDate);
|
||||
return strcmp($b->getCreationDate(), $a->getCreationDate());
|
||||
});
|
||||
} elseif ($sortStyle === 'oldest') {
|
||||
// Datum alt zu neu sortiert
|
||||
usort($results, function($a, $b) {
|
||||
return strcmp($a->creationDate, $b->creationDate);
|
||||
return strcmp($a->getCreationDate(), $b->getCreationDate());
|
||||
});
|
||||
}
|
||||
|
||||
@@ -44,13 +49,14 @@ if ($_SERVER["REQUEST_METHOD"] === "GET" && isset($_GET["q"])) {
|
||||
$safeArrayResults = [];
|
||||
foreach ($results as $obj) {
|
||||
$safeArrayResults[] = [
|
||||
"id" => $obj->id,
|
||||
"title" => $obj->title,
|
||||
"content" => $obj->content,
|
||||
"author" => $obj->author,
|
||||
"category" => $obj->category,
|
||||
"tags" => $obj->tags,
|
||||
"creationDate" => $obj->creationDate
|
||||
"id" => $obj->getId(),
|
||||
"title" => $obj->getTitle(),
|
||||
"content" => $obj->getContent(),
|
||||
"author" => $obj->getAuthor(),
|
||||
"category" => $obj->getCategory(),
|
||||
"tags" => $obj->getTags(),
|
||||
"creationDate" => $obj->getCreationDate(),
|
||||
"likes" => $obj->getLikes(),
|
||||
];
|
||||
}
|
||||
|
||||
@@ -65,6 +71,9 @@ if ($_SERVER["REQUEST_METHOD"] === "GET" && isset($_GET["q"])) {
|
||||
|
||||
$sort = $_GET['sort'] ?? 'alphabet';
|
||||
$limit = isset($_GET['limit']) ? (int)$_GET['limit'] : 10;
|
||||
if (!searchLimitValidator($limit)) {
|
||||
$limit = 10;
|
||||
}
|
||||
$page = isset($_GET['page']) ? (int)$_GET['page'] : 1;
|
||||
header("Location: ../../index.php?pfad=search-results&q=" . urlencode($search) . "&sort=" . urlencode($sort) . "&limit=" . $limit . "&page=" . $page);
|
||||
exit();
|
||||
|
||||
@@ -5,6 +5,7 @@ if (session_status() === PHP_SESSION_NONE) {
|
||||
|
||||
require_once 'php/model/Article.php';
|
||||
require_once 'php/model/ArticleManager.php';
|
||||
require_once 'php/model/CommentManager.php';
|
||||
|
||||
if (isset($_GET["id"]) && !empty($_GET["id"])){
|
||||
try {
|
||||
@@ -17,11 +18,25 @@ if (isset($_GET["id"]) && !empty($_GET["id"])){
|
||||
$category = $article->getCategory();
|
||||
$author = $article->getAuthor();
|
||||
$tags = $article->getTags();
|
||||
$articleObj = $article; // Objekt für die Like-Abfagen sichern
|
||||
}else{
|
||||
//header("location: index.php?pfad=404");
|
||||
include_once "content/404.php";
|
||||
exit();
|
||||
}
|
||||
|
||||
$commentManager = CommentManager::getInstance();
|
||||
$comments = $commentManager->getCommentsByArticle($_GET["id"]);
|
||||
|
||||
foreach ($comments as $comment) {
|
||||
if ($comment->isReply()) {
|
||||
$parentId = $comment->getParentCommentId();
|
||||
$repliesByParent[$parentId][] = $comment;
|
||||
} else {
|
||||
$mainComments[] = $comment;
|
||||
}
|
||||
}
|
||||
|
||||
} catch (Exception $e){
|
||||
$_SESSION["message"] = "internal_error";
|
||||
exit();
|
||||
|
||||
@@ -8,6 +8,11 @@ require_once '../model/ArticleManager.php';
|
||||
require_once '../model/Article.php';
|
||||
require_once '../validator/article-validator.php';
|
||||
|
||||
if (!isset($_SESSION["user"])) {
|
||||
header("Location: index.php?pfad=login");
|
||||
exit();
|
||||
}
|
||||
|
||||
if ($_SERVER["REQUEST_METHOD"] === "POST") {
|
||||
$_SESSION["old_title"] = $_POST["title"] ?? '';
|
||||
$_SESSION["old_content"] = $_POST["content"] ?? '';
|
||||
@@ -22,6 +27,20 @@ if ($_SERVER["REQUEST_METHOD"] === "POST") {
|
||||
exit();
|
||||
}
|
||||
|
||||
try {
|
||||
$articleManager = ArticleManager::getInstance();
|
||||
$article = $articleManager->getArticle($id);
|
||||
if ($article->getAuthor() != $_SESSION["user"]->getUsername()) {
|
||||
$_SESSION["message"] = "unauthorized_access";
|
||||
header("location: ../../index.php");
|
||||
exit();
|
||||
}
|
||||
} catch (Exception $e) {
|
||||
$_SESSION["message"] = $e->getMessage();
|
||||
header("location: ../../index.php?pfad=updateArticle&id=$id");
|
||||
exit();
|
||||
}
|
||||
|
||||
if (!isset($_POST["title"]) ||!isset($_POST["content"]) || !isset($_POST["category"])){
|
||||
$_SESSION["message"] = "missing_parameters";
|
||||
header("location: ../../index.php?pfad=updateArticle&id=$id");
|
||||
@@ -34,12 +53,6 @@ if ($_SERVER["REQUEST_METHOD"] === "POST") {
|
||||
$tags = $_POST['tags'] ?? '';
|
||||
|
||||
// -------------------------------- Validierung der Daten: -------------------------
|
||||
if (!articleAuthorValidator($author)) {
|
||||
$_SESSION["message"] = "author_not_valid";
|
||||
header("location: ../../index.php?pfad=updateArticle&id=$id");
|
||||
exit();
|
||||
}
|
||||
|
||||
if (!articleTitleValidator($title)) {
|
||||
$_SESSION["message"] = "invalid_title";
|
||||
header("location: ../../index.php?pfad=updateArticle&id=$id");
|
||||
@@ -75,16 +88,91 @@ if ($_SERVER["REQUEST_METHOD"] === "POST") {
|
||||
$cleanedTags = implode(',', $cleanedTags);
|
||||
}
|
||||
|
||||
// --------------------------------------- Base64-Bilder speichern ---------------------------------------------
|
||||
$blocks = json_decode($content, true);
|
||||
$uploadDir = __DIR__ . '/../../uploads/';
|
||||
|
||||
if (!file_exists($uploadDir)) {
|
||||
mkdir($uploadDir, 0755, true);
|
||||
}
|
||||
|
||||
// ----------------- Gelöschte Bilder über die JS-Löschliste entfernen ----------------- TODO: Gelöschte Bilder über die JS-Löschliste entfernen
|
||||
/*if (isset($_POST['deleted_images'])) {
|
||||
$deletedImages = json_decode($_POST['deleted_images'], true);
|
||||
|
||||
// Wir ermitteln den physisch echten, absoluten Pfad zum uploads-Ordner auf der Festplatte
|
||||
$uploadDir = realpath(__DIR__ . '/../../uploads') . DIRECTORY_SEPARATOR;
|
||||
|
||||
if (is_array($deletedImages)) {
|
||||
foreach ($deletedImages as $imagePath) {
|
||||
// Nur den reinen Dateinamen heraustrennen (z.B. img_65a123.jpg)
|
||||
$filename = basename($imagePath);
|
||||
$fullDeletePath = $uploadDir . $filename;
|
||||
|
||||
// Debugging & Löschen:
|
||||
if (file_exists($fullDeletePath)) {
|
||||
// Versuchen zu löschen. Wenn es fehlschlägt, Fehlermeldung erzwingen
|
||||
if (!@unlink($fullDeletePath)) {
|
||||
$error = error_get_last();
|
||||
die("Datei existiert, aber PHP darf sie nicht löschen! Grund: " . $error['message']);
|
||||
}
|
||||
} else {
|
||||
// Wenn PHP die Datei an diesem Pfad nicht findet, brechen wir zum Debuggen ab
|
||||
// die("PHP findet die Datei nicht unter dem Pfad: " . $fullDeletePath);
|
||||
}
|
||||
}
|
||||
}
|
||||
}*/
|
||||
|
||||
// ----------------------- NEU hinzugefügte Base64-Bilder: --------------------------
|
||||
if (is_array($blocks)) {
|
||||
foreach ($blocks as &$block) {
|
||||
// Prüfen, ob der Block ein Bild ist und ein NEUES Bild (Base64-Format) enthält
|
||||
if (isset($block['type']) && isset($block['value']) && $block['type'] === 'image' && is_string($block['value'])) {
|
||||
|
||||
if (str_starts_with($block['value'], 'data:image/')) {
|
||||
$parts = explode(',', $block['value']);
|
||||
if (count($parts) >= 2) {
|
||||
$metadata = $parts[0];
|
||||
$base64Data = $parts[1];
|
||||
|
||||
preg_match('/data:image\/(?<extension>.*?);/', $metadata, $matches);
|
||||
$extension = $matches['extension'] ?? 'jpg';
|
||||
if ($extension === 'jpeg') { $extension = 'jpg'; }
|
||||
|
||||
$fileName = 'img_' . uniqid() . '.' . $extension;
|
||||
$filePath = $uploadDir . $fileName;
|
||||
|
||||
if (file_put_contents($filePath, base64_decode($base64Data)) !== false) {
|
||||
$block['value'] = 'uploads/' . $fileName;
|
||||
} else {
|
||||
$_SESSION["message"] = "image_upload_error";
|
||||
header("location: ../../index.php?pfad=updateArticle&id=$id");
|
||||
exit();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
unset($block);
|
||||
}
|
||||
|
||||
// Aktualisiertes Array wieder in JSON konvertieren
|
||||
$finalContent = json_encode($blocks, JSON_UNESCAPED_UNICODE);
|
||||
|
||||
// ----------------- Übertragung der validierten Daten in ArticleManager: ---------------------------
|
||||
try {
|
||||
$articleManager = ArticleManager::getInstance();
|
||||
$article = $articleManager->getArticle($id);
|
||||
$article->setTitle($title);
|
||||
$article->setContent($content);
|
||||
$article->setContent($finalContent);
|
||||
$article->setCategory($category);
|
||||
$article->setTags($cleanedTags);
|
||||
$articleManager->updateArticle($id ,$article, $author);
|
||||
} catch (Exception $e){
|
||||
|
||||
unset($_SESSION["old_title"], $_SESSION["old_content"], $_SESSION["old_category"], $_SESSION["old_tags"]);
|
||||
|
||||
} catch (\Throwable $e){
|
||||
$_SESSION["message"] = $e->getMessage();
|
||||
header("location: ../../index.php?pfad=updateArticle&id=$id");
|
||||
exit();
|
||||
|
||||
+39
-10
@@ -7,13 +7,14 @@
|
||||
*/
|
||||
class Article
|
||||
{
|
||||
public $id;
|
||||
public $title;
|
||||
public $content;
|
||||
public $author;
|
||||
public $creationDate;
|
||||
public $category;
|
||||
public $tags;
|
||||
private $id;
|
||||
private $title;
|
||||
private $content;
|
||||
private $author;
|
||||
private $creationDate;
|
||||
private $category;
|
||||
private $tags;
|
||||
private $likes;
|
||||
|
||||
/**
|
||||
* Konstruktor
|
||||
@@ -26,7 +27,7 @@ class Article
|
||||
* @param $tags string optionale Schlagworte für eine bessere Suche
|
||||
* @param $creationDate string Datum der Beitragserstellung
|
||||
*/
|
||||
public function __construct(int $id, string $title, string $content, string $author, string $category, string $tags, string $creationDate)
|
||||
public function __construct(int $id, string $title, string $content, string $author, string $category, string $tags, string $creationDate, array $likes = [])
|
||||
{
|
||||
$this->id = $id;
|
||||
$this->title = $title;
|
||||
@@ -35,6 +36,7 @@ class Article
|
||||
$this->creationDate = $creationDate;
|
||||
$this->category = $category;
|
||||
$this->tags = $tags;
|
||||
$this->likes = $likes;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -67,7 +69,7 @@ class Article
|
||||
|
||||
/**
|
||||
* Gibt den Content eines Beitrags zurück.
|
||||
* TODO: Content muss noch definiert werden.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getContent(): string
|
||||
@@ -77,7 +79,7 @@ class Article
|
||||
|
||||
/**
|
||||
* Setzt den Content eines Beitrags.
|
||||
* TODO: Content muss noch definiert werden.
|
||||
*
|
||||
* @param $content
|
||||
* @return void
|
||||
*/
|
||||
@@ -141,7 +143,34 @@ class Article
|
||||
$this->tags = $tags;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gibt alle User-IDs zurück, die diesen Beitrag geliked haben.
|
||||
* @return array
|
||||
*/
|
||||
public function getLikes(): array
|
||||
{
|
||||
return $this->likes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gibt die Gesamtzahl der Likes zurück.
|
||||
* @return int
|
||||
*/
|
||||
public function getLikeCount(): int
|
||||
{
|
||||
return count($this->likes);
|
||||
}
|
||||
|
||||
/**
|
||||
* Prüft, ob ein bestimmter Nutzer den Beitrag bereits geliked hat.
|
||||
*
|
||||
* @param string $userId
|
||||
* @return bool
|
||||
*/
|
||||
public function hasLiked(string $userId): bool
|
||||
{
|
||||
return in_array($userId, $this->likes);
|
||||
}
|
||||
|
||||
}
|
||||
?>
|
||||
@@ -13,7 +13,7 @@ class ArticleManager
|
||||
/**
|
||||
* Diese Methode erstellt, falls noch keine existiert, eine Instanz einer ArticleManager-Implementierung und
|
||||
* erstellt Dummy-Beiträge.
|
||||
*
|
||||
*
|
||||
* @throws InternalServerErrorException
|
||||
*/
|
||||
public static function getInstance()
|
||||
@@ -145,9 +145,17 @@ class ArticleManager
|
||||
// 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
|
||||
$data[1], // Inhalt
|
||||
$jsonContent, // Inhalt
|
||||
$authorEmail, // Rotierende Autoren-E-Mail
|
||||
$data[2], // Kategorie
|
||||
$data[3] // Tags
|
||||
|
||||
@@ -92,6 +92,18 @@ interface ArticleManagerDAO
|
||||
*/
|
||||
public function getArticlesByCategory($category);
|
||||
|
||||
/**
|
||||
* Registriert oder entfernt ein Like eines Nutzers für einen Beitrag.
|
||||
* Wenn schon geliked -> Unlike
|
||||
* Wenn noch nicht geliked -> Like
|
||||
*
|
||||
* @param int $articleId Die ID des Beitrags
|
||||
* @param string $userId Die ID des Nutzers
|
||||
* @return bool True wenn geliked, False wenn unliked
|
||||
* @throws InternalServerErrorException
|
||||
* @throws NotFoundException
|
||||
*/
|
||||
public function toggleLike(int $articleId, string $userId): bool;
|
||||
|
||||
}
|
||||
?>
|
||||
@@ -0,0 +1,115 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Repräsentiert einen Kommentar unter einem Beitrag.
|
||||
*
|
||||
* Ein Kommentar kann entweder ein Hauptkommentar sein
|
||||
* oder eine Antwort auf einen anderen Kommentar.
|
||||
*
|
||||
* @author Caroline Schulte
|
||||
*/
|
||||
class Comment
|
||||
{
|
||||
private int $id;
|
||||
private int $articleId;
|
||||
private ?int $parentCommentId;
|
||||
private string $author;
|
||||
private string $content;
|
||||
private string $created;
|
||||
|
||||
/**
|
||||
* Erstellt einen neuen Kommentar.
|
||||
*
|
||||
* @param int $id Eindeutige ID des Kommentars
|
||||
* @param int $articleId ID des zugehörigen Beitrags
|
||||
* @param int|null $parentCommentId ID des Eltern-Kommentars oder null
|
||||
* @param string $author Autor des Kommentars
|
||||
* @param string $content Inhalt des Kommentars
|
||||
* @param string $created Erstellungsdatum des Kommentars
|
||||
*/
|
||||
public function __construct(
|
||||
int $id,
|
||||
int $articleId,
|
||||
?int $parentCommentId,
|
||||
string $author,
|
||||
string $content,
|
||||
string $created
|
||||
) {
|
||||
$this->id = $id;
|
||||
$this->articleId = $articleId;
|
||||
$this->parentCommentId = $parentCommentId;
|
||||
$this->author = $author;
|
||||
$this->content = $content;
|
||||
$this->created = $created;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gibt die ID des Kommentars zurück.
|
||||
*
|
||||
* @return int Kommentar-ID
|
||||
*/
|
||||
public function getId(): int
|
||||
{
|
||||
return $this->id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gibt die ID des zugehörigen Beitrags zurück.
|
||||
*
|
||||
* @return int Beitrags-ID
|
||||
*/
|
||||
public function getArticleId(): int
|
||||
{
|
||||
return $this->articleId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gibt die ID des Eltern-Kommentars zurück.
|
||||
*
|
||||
* @return int|null ID des Eltern-Kommentars oder null
|
||||
*/
|
||||
public function getParentCommentId(): ?int
|
||||
{
|
||||
return $this->parentCommentId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gibt zurück, ob der Kommentar eine Antwort ist.
|
||||
*
|
||||
* @return bool true wenn der Kommentar eine Antwort ist, sonst false
|
||||
*/
|
||||
public function isReply(): bool
|
||||
{
|
||||
return $this->parentCommentId !== null && $this->parentCommentId !== 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gibt den Autor des Kommentars zurück.
|
||||
*
|
||||
* @return string Autor
|
||||
*/
|
||||
public function getAuthor(): string
|
||||
{
|
||||
return $this->author;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gibt den Inhalt des Kommentars zurück.
|
||||
*
|
||||
* @return string Kommentarinhalt
|
||||
*/
|
||||
public function getContent(): string
|
||||
{
|
||||
return $this->content;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gibt das Erstellungsdatum des Kommentars zurück.
|
||||
*
|
||||
* @return string Erstellungsdatum
|
||||
*/
|
||||
public function getCreated(): string
|
||||
{
|
||||
return $this->created;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
<?php
|
||||
|
||||
require_once "DatabaseCommentManager.php";
|
||||
|
||||
/**
|
||||
* Zentrale Zugriffsschicht für Kommentare.
|
||||
*
|
||||
* Die Anwendung arbeitet ausschließlich
|
||||
* mit dem CommentManager und kennt die
|
||||
* konkrete Speicherimplementierung nicht.
|
||||
*
|
||||
* @author Caroline Schulte
|
||||
*/
|
||||
class CommentManager
|
||||
{
|
||||
/**
|
||||
* Gibt die aktive Kommentarverwaltung zurück.
|
||||
*
|
||||
* @return CommentManagerDAO
|
||||
*/
|
||||
public static function getInstance()
|
||||
{
|
||||
return DatabaseCommentManager::getInstance();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
<?php
|
||||
|
||||
require_once "Comment.php";
|
||||
|
||||
/**
|
||||
* Schnittstelle für die Verwaltung von Kommentaren.
|
||||
*
|
||||
* Definiert die grundlegenden Methoden zum
|
||||
* Speichern und Laden von Kommentaren.
|
||||
*
|
||||
* @author Caroline Schulte
|
||||
*/
|
||||
interface CommentManagerDAO
|
||||
{
|
||||
/**
|
||||
* Speichert einen neuen Kommentar zu einem Beitrag.
|
||||
*
|
||||
* Optional kann eine parentCommentId übergeben werden,
|
||||
* wenn der Kommentar eine Antwort auf einen anderen Kommentar ist.
|
||||
*
|
||||
* @param int $articleId ID des Beitrags
|
||||
* @param string $author Autor des Kommentars
|
||||
* @param string $content Inhalt des Kommentars
|
||||
* @param int|null $parentCommentId ID des Eltern-Kommentars oder null
|
||||
*
|
||||
* @return int ID des neu gespeicherten Kommentars
|
||||
*/
|
||||
public function addComment(
|
||||
$articleId,
|
||||
$author,
|
||||
$content,
|
||||
$parentCommentId = null
|
||||
);
|
||||
|
||||
/**
|
||||
* Gibt alle Kommentare eines Beitrags zurück.
|
||||
*
|
||||
* @param int $articleId ID des Beitrags
|
||||
*
|
||||
* @return Comment[] Liste der Kommentare
|
||||
*/
|
||||
public function getCommentsByArticle(
|
||||
$articleId
|
||||
);
|
||||
|
||||
/**
|
||||
* Gibt alle Kommentare eines Autors zurück.
|
||||
*
|
||||
* @param string $author E-Mail-Adresse des Autors
|
||||
*
|
||||
* @return Comment[] Liste der Kommentare
|
||||
*/
|
||||
public function getCommentsByAuthor($author);
|
||||
}
|
||||
@@ -23,6 +23,7 @@ class DatabaseArticleManager implements ArticleManagerDAO {
|
||||
|
||||
$db = $this->getConnection();
|
||||
|
||||
// Tabelle für Beiträge
|
||||
$db->exec("
|
||||
CREATE TABLE articles (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
@@ -33,6 +34,15 @@ class DatabaseArticleManager implements ArticleManagerDAO {
|
||||
tags TEXT,
|
||||
created TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
||||
);");
|
||||
|
||||
// Tabelle für Likes
|
||||
$db->exec("
|
||||
CREATE TABLE likes (
|
||||
article_id INTEGER,
|
||||
user_id TEXT,
|
||||
PRIMARY KEY (article_id, user_id),
|
||||
FOREIGN KEY (article_id) REFERENCES articles(id) ON DELETE CASCADE
|
||||
);");
|
||||
unset($db);
|
||||
} catch (PDOException $e) {
|
||||
throw new InternalServerErrorException($e->getMessage());
|
||||
@@ -189,6 +199,8 @@ class DatabaseArticleManager implements ArticleManagerDAO {
|
||||
$row = $command->fetch(PDO::FETCH_ASSOC);
|
||||
|
||||
if ($row) {
|
||||
$likes = $this->getLikesForArticle(intval($row['id']));
|
||||
|
||||
return new Article(
|
||||
intval($row['id']),
|
||||
$row['title'],
|
||||
@@ -196,7 +208,8 @@ class DatabaseArticleManager implements ArticleManagerDAO {
|
||||
$row['author'],
|
||||
$row['category'],
|
||||
$row['tags'],
|
||||
$row['created']
|
||||
$row['created'],
|
||||
$likes
|
||||
);
|
||||
}
|
||||
|
||||
@@ -254,6 +267,8 @@ class DatabaseArticleManager implements ArticleManagerDAO {
|
||||
$filteredArticles = [];
|
||||
|
||||
foreach ($rows as $row) {
|
||||
$likes = $this->getLikesForArticle(intval($row['id']));
|
||||
|
||||
$filteredArticles[] = new Article(
|
||||
intval($row['id']),
|
||||
$row['title'],
|
||||
@@ -261,7 +276,8 @@ class DatabaseArticleManager implements ArticleManagerDAO {
|
||||
$row['author'],
|
||||
$row['category'],
|
||||
$row['tags'],
|
||||
$row['created']
|
||||
$row['created'],
|
||||
$likes
|
||||
);
|
||||
}
|
||||
|
||||
@@ -287,6 +303,8 @@ class DatabaseArticleManager implements ArticleManagerDAO {
|
||||
$filteredArticles = [];
|
||||
|
||||
foreach ($rows as $row) {
|
||||
$likes = $this->getLikesForArticle(intval($row['id']));
|
||||
|
||||
$filteredArticles[] = new Article(
|
||||
intval($row['id']),
|
||||
$row['title'],
|
||||
@@ -294,7 +312,8 @@ class DatabaseArticleManager implements ArticleManagerDAO {
|
||||
$row['author'],
|
||||
$row['category'],
|
||||
$row['tags'],
|
||||
$row['created']
|
||||
$row['created'],
|
||||
$likes
|
||||
);
|
||||
}
|
||||
|
||||
@@ -341,6 +360,8 @@ class DatabaseArticleManager implements ArticleManagerDAO {
|
||||
$filteredArticles = [];
|
||||
|
||||
foreach ($rows as $row) {
|
||||
$likes = $this->getLikesForArticle(intval($row['id']));
|
||||
|
||||
$filteredArticles[] = new Article(
|
||||
intval($row['id']),
|
||||
$row['title'] ?? '',
|
||||
@@ -348,7 +369,8 @@ class DatabaseArticleManager implements ArticleManagerDAO {
|
||||
$row['author'] ?? '',
|
||||
$row['category'] ?? '',
|
||||
$row['tags'] ?? '',
|
||||
$row['created'] ?? '' // Nutzt 'created' aus deiner DB-Struktur
|
||||
$row['created'] ?? '',
|
||||
$likes
|
||||
);
|
||||
}
|
||||
|
||||
@@ -359,4 +381,69 @@ class DatabaseArticleManager implements ArticleManagerDAO {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Holt alle User-IDs, die einen bestimmten Beitrag geliked haben.
|
||||
*
|
||||
* @return String[] UserIDs
|
||||
* @throws InternalServerErrorException
|
||||
*/
|
||||
private function getLikesForArticle(int $articleId): array
|
||||
{
|
||||
try {
|
||||
$db = $this->getConnection();
|
||||
$sql = "SELECT user_id FROM likes WHERE article_id = :article_id;";
|
||||
$command = $db->prepare($sql);
|
||||
$command->execute([':article_id' => $articleId]);
|
||||
|
||||
return $command->fetchAll(PDO::FETCH_COLUMN) ?: [];
|
||||
} catch (PDOException $e) {
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
public function toggleLike(int $articleId, string $userId): bool
|
||||
{
|
||||
// prüfen, ob der Artikel überhaupt existiert
|
||||
$article = $this->getArticle($articleId);
|
||||
if (!$article) {
|
||||
throw new NotFoundException("missing_id");
|
||||
}
|
||||
|
||||
try {
|
||||
$db = $this->getConnection();
|
||||
|
||||
// prüfen, ob das Like bereits existiert
|
||||
$checkSql = "SELECT COUNT(*) FROM likes WHERE article_id = :article_id AND user_id = :user_id;";
|
||||
$checkCommand = $db->prepare($checkSql);
|
||||
$checkCommand->execute([
|
||||
':article_id' => $articleId,
|
||||
':user_id' => $userId
|
||||
]);
|
||||
|
||||
$hasLiked = (int)$checkCommand->fetchColumn() > 0;
|
||||
|
||||
if ($hasLiked) {
|
||||
// wenn bereits geliked -> Unlike
|
||||
$deleteSql = "DELETE FROM likes WHERE article_id = :article_id AND user_id = :user_id;";
|
||||
$deleteCommand = $db->prepare($deleteSql);
|
||||
$deleteCommand->execute([
|
||||
':article_id' => $articleId,
|
||||
':user_id' => $userId
|
||||
]);
|
||||
return false; // gibt false zurück, da der Beitrag jetzt nicht mehr geliked ist
|
||||
} else {
|
||||
// wenn noch nicht geliked -> Like
|
||||
$insertSql = "INSERT INTO likes (article_id, user_id) VALUES (:article_id, :user_id);";
|
||||
$insertCommand = $db->prepare($insertSql);
|
||||
$insertCommand->execute([
|
||||
':article_id' => $articleId,
|
||||
':user_id' => $userId
|
||||
]);
|
||||
return true; // gibt true zurück, da der Beitrag jetzt geliked ist
|
||||
}
|
||||
|
||||
} catch (PDOException $e) {
|
||||
throw new InternalServerErrorException("internal_error");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,257 @@
|
||||
<?php
|
||||
|
||||
require_once "CommentManagerDAO.php";
|
||||
require_once "Comment.php";
|
||||
|
||||
/**
|
||||
* Verwaltet die Speicherung und das Laden von Kommentaren
|
||||
* über eine SQLite-Datenbank.
|
||||
*
|
||||
* @author Caroline Schulte
|
||||
*/
|
||||
class DatabaseCommentManager implements CommentManagerDAO
|
||||
{
|
||||
private static $instance = null;
|
||||
|
||||
/**
|
||||
* Erstellt die Kommentartabelle, falls diese noch nicht existiert.
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
try {
|
||||
$db = $this->getConnection();
|
||||
|
||||
$db->exec("
|
||||
CREATE TABLE IF NOT EXISTS comments (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
article_id INTEGER NOT NULL,
|
||||
parent_comment_id INTEGER NULL,
|
||||
author TEXT NOT NULL,
|
||||
content TEXT NOT NULL,
|
||||
created TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
||||
);
|
||||
");
|
||||
|
||||
$columns = $db->query("PRAGMA table_info(comments);")->fetchAll(PDO::FETCH_ASSOC);
|
||||
$hasParentColumn = false;
|
||||
|
||||
foreach ($columns as $column) {
|
||||
if ($column["name"] === "parent_comment_id") {
|
||||
$hasParentColumn = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!$hasParentColumn) {
|
||||
$db->exec("ALTER TABLE comments ADD COLUMN parent_comment_id INTEGER NULL;");
|
||||
}
|
||||
|
||||
} catch (PDOException $e) {
|
||||
throw new RuntimeException("internal_error");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Baut die Verbindung zur SQLite-Datenbank auf.
|
||||
*
|
||||
* @return PDO Datenbankverbindung
|
||||
*/
|
||||
private function getConnection()
|
||||
{
|
||||
try {
|
||||
$dsn = 'sqlite:' . __DIR__ . '/../../db/comments.db';
|
||||
|
||||
$db = new PDO($dsn, null, null);
|
||||
$db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
|
||||
|
||||
return $db;
|
||||
|
||||
} catch (PDOException $e) {
|
||||
throw new RuntimeException("internal_error");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gibt die Singleton-Instanz zurück.
|
||||
*
|
||||
* @return DatabaseCommentManager
|
||||
*/
|
||||
public static function getInstance()
|
||||
{
|
||||
if (self::$instance === null) {
|
||||
self::$instance = new DatabaseCommentManager();
|
||||
}
|
||||
|
||||
return self::$instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* Speichert einen neuen Kommentar oder eine Antwort.
|
||||
*
|
||||
* @param int $articleId ID des Beitrags
|
||||
* @param string $author Autor des Kommentars
|
||||
* @param string $content Inhalt des Kommentars
|
||||
* @param int|null $parentCommentId ID des Eltern-Kommentars oder null
|
||||
*
|
||||
* @return int ID des neu gespeicherten Kommentars
|
||||
*/
|
||||
public function addComment(
|
||||
$articleId,
|
||||
$author,
|
||||
$content,
|
||||
$parentCommentId = null
|
||||
) {
|
||||
try {
|
||||
$db = $this->getConnection();
|
||||
|
||||
if ($parentCommentId === "" || $parentCommentId === 0 || $parentCommentId === "0") {
|
||||
$parentCommentId = null;
|
||||
}
|
||||
|
||||
$sql = "
|
||||
INSERT INTO comments (
|
||||
article_id,
|
||||
parent_comment_id,
|
||||
author,
|
||||
content
|
||||
)
|
||||
VALUES (
|
||||
:articleId,
|
||||
:parentCommentId,
|
||||
:author,
|
||||
:content
|
||||
)
|
||||
";
|
||||
|
||||
$command = $db->prepare($sql);
|
||||
|
||||
$command->execute([
|
||||
":articleId" => $articleId,
|
||||
":parentCommentId" => $parentCommentId,
|
||||
":author" => $author,
|
||||
":content" => $content
|
||||
]);
|
||||
|
||||
return intval($db->lastInsertId());
|
||||
|
||||
} catch (PDOException $e) {
|
||||
throw new RuntimeException("internal_error");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Lädt alle Kommentare eines Beitrags.
|
||||
*
|
||||
* @param int $articleId ID des Beitrags
|
||||
*
|
||||
* @return Comment[]
|
||||
*/
|
||||
public function getCommentsByArticle($articleId)
|
||||
{
|
||||
try {
|
||||
$db = $this->getConnection();
|
||||
|
||||
$sql = "
|
||||
SELECT
|
||||
id,
|
||||
article_id,
|
||||
CASE
|
||||
WHEN parent_comment_id IS NULL THEN NULL
|
||||
WHEN parent_comment_id = '' THEN NULL
|
||||
WHEN parent_comment_id = 0 THEN NULL
|
||||
ELSE parent_comment_id
|
||||
END AS parent_comment_id,
|
||||
author,
|
||||
content,
|
||||
created
|
||||
FROM comments
|
||||
WHERE article_id = :articleId
|
||||
ORDER BY created ASC
|
||||
";
|
||||
|
||||
$command = $db->prepare($sql);
|
||||
$command->execute([":articleId" => $articleId]);
|
||||
|
||||
return $this->mapRowsToComments($command);
|
||||
|
||||
} catch (PDOException $e) {
|
||||
throw new RuntimeException("internal_error");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Lädt alle Kommentare eines Autors.
|
||||
*
|
||||
* @param string $author E-Mail-Adresse des Autors
|
||||
*
|
||||
* @return Comment[]
|
||||
*/
|
||||
public function getCommentsByAuthor($author)
|
||||
{
|
||||
try {
|
||||
$db = $this->getConnection();
|
||||
|
||||
$sql = "
|
||||
SELECT
|
||||
id,
|
||||
article_id,
|
||||
CASE
|
||||
WHEN parent_comment_id IS NULL THEN NULL
|
||||
WHEN parent_comment_id = '' THEN NULL
|
||||
WHEN parent_comment_id = 0 THEN NULL
|
||||
ELSE parent_comment_id
|
||||
END AS parent_comment_id,
|
||||
author,
|
||||
content,
|
||||
created
|
||||
FROM comments
|
||||
WHERE author = :author
|
||||
ORDER BY created DESC
|
||||
";
|
||||
|
||||
$command = $db->prepare($sql);
|
||||
$command->execute([":author" => $author]);
|
||||
|
||||
return $this->mapRowsToComments($command);
|
||||
|
||||
} catch (PDOException $e) {
|
||||
throw new RuntimeException("internal_error");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Wandelt Datenbankzeilen in Comment-Objekte um.
|
||||
*
|
||||
* @param PDOStatement $command Ausgeführtes Statement
|
||||
*
|
||||
* @return Comment[]
|
||||
*/
|
||||
private function mapRowsToComments($command)
|
||||
{
|
||||
$comments = [];
|
||||
|
||||
while ($row = $command->fetch(PDO::FETCH_ASSOC)) {
|
||||
$parentCommentId = null;
|
||||
|
||||
if (
|
||||
isset($row["parent_comment_id"])
|
||||
&& $row["parent_comment_id"] !== null
|
||||
&& $row["parent_comment_id"] !== ""
|
||||
&& intval($row["parent_comment_id"]) !== 0
|
||||
) {
|
||||
$parentCommentId = intval($row["parent_comment_id"]);
|
||||
}
|
||||
|
||||
$comments[] = new Comment(
|
||||
intval($row["id"]),
|
||||
intval($row["article_id"]),
|
||||
$parentCommentId,
|
||||
$row["author"],
|
||||
$row["content"],
|
||||
$row["created"]
|
||||
);
|
||||
}
|
||||
|
||||
return $comments;
|
||||
}
|
||||
}
|
||||
@@ -62,7 +62,8 @@ class LocalArticleManager implements ArticleManagerDAO {
|
||||
"author" => $author,
|
||||
"category" => $category,
|
||||
"tags" => $tags,
|
||||
"creationDate" => date("Y-m-d H:i:s")
|
||||
"creationDate" => date("Y-m-d H:i:s"),
|
||||
"likes" => []
|
||||
];
|
||||
|
||||
$this->saveArticle($articles);
|
||||
@@ -92,7 +93,8 @@ class LocalArticleManager implements ArticleManagerDAO {
|
||||
"author" => $author,
|
||||
"category" => $article->getCategory(),
|
||||
"tags" => $article->getTags(),
|
||||
"creationDate" => $article->getCreationDate()
|
||||
"creationDate" => $article->getCreationDate(),
|
||||
"likes" => $storedArticle['likes'] ?? []
|
||||
];
|
||||
$updated = true;
|
||||
break;
|
||||
@@ -142,7 +144,17 @@ class LocalArticleManager implements ArticleManagerDAO {
|
||||
|
||||
foreach ($articles as $article) {
|
||||
if (isset($article['id']) && $article['id'] == $id) {
|
||||
return new Article(intval($article['id']), $article['title'], $article['content'], $article['author'], $article['category'], $article['tags'], $article['creationDate']);
|
||||
$likes = isset($article['likes']) && is_array($article['likes']) ? $article['likes'] : [];
|
||||
return new Article(
|
||||
intval($article['id']),
|
||||
$article['title'],
|
||||
$article['content'],
|
||||
$article['author'],
|
||||
$article['category'],
|
||||
$article['tags'],
|
||||
$article['creationDate'],
|
||||
$likes
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -168,6 +180,7 @@ class LocalArticleManager implements ArticleManagerDAO {
|
||||
|
||||
foreach ($articles as $article) {
|
||||
if (isset($article['author']) && $article['author'] == $author) {
|
||||
$likes = isset($article['likes']) && is_array($article['likes']) ? $article['likes'] : [];
|
||||
$filteredArticles[] = new Article(
|
||||
intval($article['id']),
|
||||
$article['title'],
|
||||
@@ -175,7 +188,8 @@ class LocalArticleManager implements ArticleManagerDAO {
|
||||
$article['author'],
|
||||
$article['category'],
|
||||
$article['tags'],
|
||||
$article['creationDate']
|
||||
$article['creationDate'],
|
||||
$likes
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -201,6 +215,7 @@ class LocalArticleManager implements ArticleManagerDAO {
|
||||
if (($cleanKeyword !== '' && strpos($title, $cleanKeyword) !== false) ||
|
||||
($cleanKeyword !== '' && strpos($content, $cleanKeyword) !== false)) {
|
||||
|
||||
$likes = isset($article['likes']) && is_array($article['likes']) ? $article['likes'] : [];
|
||||
$filteredArticles[] = new Article(
|
||||
intval($article['id'] ?? 0),
|
||||
$article['title'] ?? '',
|
||||
@@ -208,7 +223,8 @@ class LocalArticleManager implements ArticleManagerDAO {
|
||||
$article['author'] ?? '',
|
||||
$article['category'] ?? '',
|
||||
$article['tags'] ?? '',
|
||||
$article['creationDate'] ?? ''
|
||||
$article['creationDate'] ?? '',
|
||||
$likes
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -223,6 +239,7 @@ class LocalArticleManager implements ArticleManagerDAO {
|
||||
|
||||
foreach ($articles as $article) {
|
||||
if (isset($article['category']) && $article['category'] == $category) {
|
||||
$likes = isset($article['likes']) && is_array($article['likes']) ? $article['likes'] : [];
|
||||
$filteredArticles[] = new Article(
|
||||
intval($article['id']),
|
||||
$article['title'],
|
||||
@@ -230,12 +247,53 @@ class LocalArticleManager implements ArticleManagerDAO {
|
||||
$article['author'],
|
||||
$article['category'],
|
||||
$article['tags'],
|
||||
$article['creationDate']
|
||||
$article['creationDate'],
|
||||
$likes
|
||||
);
|
||||
}
|
||||
}
|
||||
return $filteredArticles;
|
||||
}
|
||||
|
||||
public function toggleLike(int $articleId, string $userId): bool
|
||||
{
|
||||
$articles = $this->getAllArticles();
|
||||
$articleFound = false;
|
||||
$isLikedNow = false;
|
||||
|
||||
foreach ($articles as $index => $article) {
|
||||
if (isset($article['id']) && $article['id'] == $articleId) {
|
||||
$articleFound = true;
|
||||
|
||||
// Likes-Array initialisieren, falls nicht vorhanden
|
||||
if (!isset($articles[$index]['likes']) || !is_array($articles[$index]['likes'])) {
|
||||
$articles[$index]['likes'] = [];
|
||||
}
|
||||
|
||||
$likeIndex = array_search($userId, $articles[$index]['likes']);
|
||||
|
||||
if ($likeIndex !== false) {
|
||||
// Bereits geliked -> Unlike
|
||||
unset($articles[$index]['likes'][$likeIndex]);
|
||||
// Array-Keys neu indizieren, damit JSON sauber bleibt
|
||||
$articles[$index]['likes'] = array_values($articles[$index]['likes']);
|
||||
$isLikedNow = false;
|
||||
} else {
|
||||
// Noch nicht geliked -> Like (User-ID hinzufügen)
|
||||
$articles[$index]['likes'][] = $userId;
|
||||
$isLikedNow = true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!$articleFound) {
|
||||
throw new NotFoundException("missing_id");
|
||||
}
|
||||
|
||||
$this->saveArticle($articles);
|
||||
return $isLikedNow;
|
||||
}
|
||||
|
||||
}
|
||||
?>
|
||||
@@ -1,16 +1,5 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Prüft, ob der Autor auch der Eigentümer des Beitrags ist.
|
||||
* @param $author
|
||||
* @return true
|
||||
* TODO: Implement this.
|
||||
*/
|
||||
function articleAuthorValidator($author)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Prüft, ob der Titel die folgenden Bedingungen erfüllt:
|
||||
* Buchstaben von a-z; A-Z
|
||||
@@ -33,19 +22,73 @@ function articleTitleValidator($title)
|
||||
}
|
||||
|
||||
/**
|
||||
* Prüft, ob der Contenttext 10-7000 Zeichen enthält.
|
||||
* @param $content
|
||||
* Prüft, ob der Content valides JSON ist UND ob alle enthaltenen Blöcke
|
||||
* die inhaltlichen Kriterien erfüllen:
|
||||
* Textblöcke sind nicht leer
|
||||
* Bilder sind in Bildblöcken vorhanden
|
||||
*
|
||||
* @param mixed $content Der zu prüfende JSON-String
|
||||
* @return bool
|
||||
*/
|
||||
function articleContentValidator($content)
|
||||
{
|
||||
$content = trim($content);
|
||||
$zeichenAnzahl = mb_strlen($content);
|
||||
if ($zeichenAnzahl <= 7000 && $zeichenAnzahl >= 10) {
|
||||
return true;
|
||||
}else{
|
||||
// 1. Grundlegende Typprüfung
|
||||
if (!is_string($content)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// 2. Formale JSON-Prüfung (Kompatibel mit PHP 8.2)
|
||||
$blocks = json_decode($content, true);
|
||||
if (json_last_error() !== JSON_ERROR_NONE) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// 3. Inhaltliche Validierung der einzelnen Blöcke
|
||||
// Falls das JSON zwar valide, aber kein Array ist (z.B. nur ein String/Zahl)
|
||||
if (!is_array($blocks)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Mindestens ein Block sollte vorhanden sein (optional, verhindert leere Beiträge)
|
||||
if (empty($blocks)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
foreach ($blocks as $block) {
|
||||
// Jeder Block muss die Keys 'type' und 'value' besitzen
|
||||
if (!isset($block['type']) || !isset($block['value'])) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$type = $block['type'];
|
||||
$value = $block['value'];
|
||||
|
||||
if ($type === 'text') {
|
||||
// Validierung für Text: Darf nach dem Trimmen nicht leer sein
|
||||
if (trim($value) === '') {
|
||||
return false;
|
||||
}
|
||||
} elseif ($type === 'image') {
|
||||
// Validierung für Bild: Muss entweder mit uploads/ starten (Bestand)
|
||||
// oder mit data:image/ beginnen (neues Base64-Bild aus dem Editor)
|
||||
if (!is_string($value)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$isValidPath = str_starts_with($value, 'uploads/');
|
||||
$isValidBase64 = str_starts_with($value, 'data:image/');
|
||||
|
||||
if (!$isValidPath && !$isValidBase64) {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
// Unbekannter Blocktyp wird zur Sicherheit abgewiesen
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Wenn alle Prüfungen bestanden wurden
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
# Verhindert das Auflisten aller Dateien im Ordner
|
||||
Options -Indexes
|
||||
|
||||
# Schaltet die PHP-Ausführung in diesem Ordner komplett ab
|
||||
<FilesMatch "\.(php|php[0-9]|phtml|pl|py|jsp|sh|cgi)$">
|
||||
Order Deny,Allow
|
||||
Deny from all
|
||||
</FilesMatch>
|
||||
|
||||
# erlaubt nur den Zugriff auf folgende Dateien:
|
||||
<FilesMatch "\.(?i:jpg|jpeg|png|gif|webp|ico)$">
|
||||
Order Allow,Deny
|
||||
Allow from all
|
||||
</FilesMatch>
|
||||
Reference in New Issue
Block a user