Compare commits
78 Commits
Forum
..
12ad9759cf
| Author | SHA1 | Date | |
|---|---|---|---|
| 12ad9759cf | |||
| 057639be7c | |||
| 87e7be1c10 | |||
| 1db77346d0 | |||
| 1e6ac0fcd9 | |||
| b3c74c2172 | |||
| 0805958033 | |||
| 5c0e978c93 | |||
| 520298bae4 | |||
| 969adc4836 | |||
| d9eca06197 | |||
| 48f2e90e49 | |||
| 358247a2a1 | |||
| de4d2fe5c0 | |||
| e1102eb7db | |||
| a8df9590fd | |||
| 3e453e22ec | |||
| beeab0ec90 | |||
| 9353a7eaaa | |||
| cac8f3046d | |||
| 66eeac372c | |||
| 4f8d11881d | |||
| 5c924d3277 | |||
| 7f703a8386 | |||
| f063ea4741 | |||
| ca643dd298 | |||
| e19225e49e | |||
| 9ffbca679e | |||
| d90a10e462 | |||
| b6f25d041b | |||
| 4321c4bee5 | |||
| d1b3641754 | |||
| 5281c1bc7d | |||
| aae78bf479 | |||
| af17764346 | |||
| 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 |
Generated
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<project version="4">
|
<project version="4">
|
||||||
<component name="dataSourceStorageLocal" created-in="IU-253.32098.101">
|
<component name="dataSourceStorageLocal" created-in="IU-261.25134.95">
|
||||||
<data-source name="articles" uuid="315cb5c9-2b0f-435b-b602-59823b160908">
|
<data-source name="articles" uuid="315cb5c9-2b0f-435b-b602-59823b160908">
|
||||||
<database-info product="SQLite" version="3.51.1" jdbc-version="4.2" driver-name="SQLite JDBC" driver-version="3.51.1.0" dbms="SQLITE" exact-version="3.51.1" exact-driver-version="3.51">
|
<database-info product="SQLite" version="3.51.1" jdbc-version="4.2" driver-name="SQLite JDBC" driver-version="3.51.1.0" dbms="SQLITE" exact-version="3.51.1" exact-driver-version="3.51">
|
||||||
<identifier-quote-string>"</identifier-quote-string>
|
<identifier-quote-string>"</identifier-quote-string>
|
||||||
|
|||||||
@@ -7,7 +7,7 @@
|
|||||||
|
|
||||||
## Login-Informationen für Dummy-User
|
## Login-Informationen für Dummy-User
|
||||||
`Anmeldename, Passwort, Mailadresse`:
|
`Anmeldename, Passwort, Mailadresse`:
|
||||||
- `max.mustermann, test12345, mustermann@web.de`
|
- `max.mustermann, test12345, max.mustermann@web.de`
|
||||||
|
|
||||||
## Weitere Voraussetzungen zur Nutzung
|
## Weitere Voraussetzungen zur Nutzung
|
||||||
- Per Klick auf das Logo gelangt man auf die Home-Seite.
|
- 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.
|
- 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
|
- Die Suchseite und Kategorieseite packen momentan alle passenden Beiträge untereinander. Später sollen zunächst 10
|
||||||
Ergebnisse auf einer Seite angezeigt werden.
|
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
|
## Besonderheiten des Projektes
|
||||||
- Es wurde ein einfacher Beitrags-Editor erstellt. Mit diesem können Beiträge erstellt oder bearbeitet werden.
|
- Es wurde ein einfacher Beitrags-Editor erstellt. Mit diesem können Beiträge erstellt oder bearbeitet werden.
|
||||||
|
|||||||
@@ -17,8 +17,28 @@ if (!isset($_SESSION["user"])) {
|
|||||||
<input type="text" id="title" name="title"
|
<input type="text" id="title" name="title"
|
||||||
value="<?php echo htmlspecialchars($_SESSION['old_title'] ?? ''); unset($_SESSION['old_title']); ?>"
|
value="<?php echo htmlspecialchars($_SESSION['old_title'] ?? ''); unset($_SESSION['old_title']); ?>"
|
||||||
placeholder="Titel hier eingeben" required>
|
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>
|
</main>
|
||||||
|
|
||||||
<!-- Seitenleiste -->
|
<!-- Seitenleiste -->
|
||||||
|
|||||||
+27
-21
@@ -19,28 +19,34 @@ include_once 'php/controller/home-controller.php';
|
|||||||
sea takimata sanctus est Lorem ipsum dolor sit amet.
|
sea takimata sanctus est Lorem ipsum dolor sit amet.
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<!-- Flexbox -->
|
<!-- Flexbox für aktive Kategorien -->
|
||||||
<div class="flexbox">
|
<div class="flexbox">
|
||||||
<div class="container">
|
<?php
|
||||||
<a href="index.php?pfad=showCategory&category=informatik" class="category-link">Informatik</a>
|
if (!empty($categoriesWithArticles) && is_array($categoriesWithArticles)):
|
||||||
<div class="article-link">
|
foreach ($categoriesWithArticles as $category):
|
||||||
<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 class="container">
|
||||||
</div>
|
<a href="index.php?pfad=showCategory&category=<?php echo htmlspecialchars($category['slug']); ?>" class="category-link">
|
||||||
|
<?php echo htmlspecialchars($category['name']); ?>
|
||||||
|
</a>
|
||||||
|
|
||||||
<div class="container">
|
<!-- die 5 neuesten Beiträge der Kategorie -->
|
||||||
<a href="index.php?pfad=showCategory&category=mathe" class="category-link">Mathe</a>
|
<div class="article-links-wrapper">
|
||||||
<div class="article-link">
|
<?php foreach ($category['articles'] as $article): ?>
|
||||||
<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 class="article-link">
|
||||||
</div>
|
<a href="index.php?pfad=showArticle&id=<?php echo htmlspecialchars($article->getId()); ?>">
|
||||||
</div>
|
<?php echo htmlspecialchars($article->getTitle()); ?>
|
||||||
|
</a>
|
||||||
<div class="container">
|
</div>
|
||||||
<a href="index.php?pfad=showCategory&category=physik" class="category-link">Physik</a>
|
<?php endforeach; ?>
|
||||||
<div class="article-link">
|
</div>
|
||||||
<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>
|
<?php
|
||||||
</div>
|
endforeach;
|
||||||
|
else:
|
||||||
|
?>
|
||||||
|
<p>Aktuell sind keine Beiträge in den Kategorien vorhanden.</p>
|
||||||
|
<?php endif; ?>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</main>
|
</main>
|
||||||
+5
-10
@@ -7,8 +7,8 @@ $isEditMode = (isset($_GET["edit"]) && $_GET["edit"] === "1") || !empty($error);
|
|||||||
|
|
||||||
<main class="form-page">
|
<main class="form-page">
|
||||||
<div class="flexbox">
|
<div class="flexbox">
|
||||||
|
|
||||||
<div class="container">
|
<div class="container">
|
||||||
|
<?php include_once "includes/alertMessages.php" ?>
|
||||||
|
|
||||||
<?php if (!empty($error)): ?>
|
<?php if (!empty($error)): ?>
|
||||||
<p class="alert-message is-error">
|
<p class="alert-message is-error">
|
||||||
@@ -93,8 +93,6 @@ $isEditMode = (isset($_GET["edit"]) && $_GET["edit"] === "1") || !empty($error);
|
|||||||
|
|
||||||
<div class="container">
|
<div class="container">
|
||||||
|
|
||||||
<?php include_once "includes/alertMessages.php" ?>
|
|
||||||
|
|
||||||
<h2 class="section-title">Meine Beiträge</h2>
|
<h2 class="section-title">Meine Beiträge</h2>
|
||||||
|
|
||||||
<div class="articles-list">
|
<div class="articles-list">
|
||||||
@@ -182,10 +180,9 @@ $isEditMode = (isset($_GET["edit"]) && $_GET["edit"] === "1") || !empty($error);
|
|||||||
|
|
||||||
<?php endif; ?>
|
<?php endif; ?>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<br>
|
<div class="container">
|
||||||
|
|
||||||
<!-- Eigener Bereich für die Kommentare des Nutzers -->
|
|
||||||
<div class="comments-section">
|
<div class="comments-section">
|
||||||
|
|
||||||
<h2 class="section-title">Meine Kommentare</h2>
|
<h2 class="section-title">Meine Kommentare</h2>
|
||||||
@@ -228,10 +225,8 @@ $isEditMode = (isset($_GET["edit"]) && $_GET["edit"] === "1") || !empty($error);
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<?php unset($_SESSION["message"]); ?>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
<?php unset($_SESSION["message"]); ?>
|
||||||
|
</div>
|
||||||
|
|
||||||
</main>
|
</main>
|
||||||
+54
-30
@@ -27,7 +27,8 @@ if ($currentPage < 1) {
|
|||||||
$offset = ($currentPage - 1) * $limit;
|
$offset = ($currentPage - 1) * $limit;
|
||||||
|
|
||||||
// Nur die Ergebnisse für die aktuelle Seite ausschneiden
|
// Nur die Ergebnisse für die aktuelle Seite ausschneiden
|
||||||
$results = array_slice($all_results, $offset, $limit);
|
//$results = array_slice($all_results, $offset, $limit);
|
||||||
|
$results = $all_results;
|
||||||
$resultCount = count($results);
|
$resultCount = count($results);
|
||||||
|
|
||||||
?>
|
?>
|
||||||
@@ -44,12 +45,11 @@ $resultCount = count($results);
|
|||||||
<!-- Links: Seitenleiste für Filter und Suche -->
|
<!-- Links: Seitenleiste für Filter und Suche -->
|
||||||
<aside class="s-res-sidebar">
|
<aside class="s-res-sidebar">
|
||||||
|
|
||||||
<!-- Sortierfuntion Box und Such Box-->
|
<form action="php/controller/search-results-controller.php" method="GET" id="search-form-id" class="s-res-sidebar-form">
|
||||||
<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" id="s-res-page-input" name="page" value="<?php echo $_GET['page'] ?? 1; ?>">
|
||||||
<input type="hidden" name="page" id="s-res-page-input" value="<?php echo $currentPage; ?>">
|
|
||||||
|
|
||||||
<div class="s-res-sidebar-box">
|
<div class="s-res-sidebar-box">
|
||||||
<h3 class="s-res-sidebar-title">Suche anpassen</h3>
|
<h3 class="s-res-sidebar-title">Suche anpassen</h3>
|
||||||
<input type="search" id="site-search" name="q" placeholder="Suchen..." class="nav__search" value="<?php echo htmlspecialchars($query); ?>" maxlength="50" required>
|
<input type="search" id="site-search" name="q" placeholder="Suchen..." class="nav__search" value="<?php echo htmlspecialchars($query); ?>" maxlength="50" required>
|
||||||
<button type="submit" class="nav__search-button">Suchen</button>
|
<button type="submit" class="nav__search-button">Suchen</button>
|
||||||
@@ -60,25 +60,57 @@ $resultCount = count($results);
|
|||||||
<?php $currentSort = $_SESSION['search_sort'] ?? 'alphabet'; ?>
|
<?php $currentSort = $_SESSION['search_sort'] ?? 'alphabet'; ?>
|
||||||
<div class="s-res-filter-group">
|
<div class="s-res-filter-group">
|
||||||
<label class="s-res-filter-option">
|
<label class="s-res-filter-option">
|
||||||
<input type="radio" name="sort" value="alphabet" <?php echo $currentSort === 'alphabet' ? 'checked' : ''; ?> onchange="this.form.submit()">
|
<input type="radio" name="sort" value="alphabet" class="sort-radio" <?php echo $currentSort === 'alphabet' ? 'checked' : ''; ?>>
|
||||||
<span>Alphabetisch</span>
|
<span>Alphabetisch</span>
|
||||||
</label>
|
</label>
|
||||||
<!-- Noch disabled, da likes noch nicht implementiert-->
|
|
||||||
<label class="s-res-filter-option">
|
<label class="s-res-filter-option">
|
||||||
<input type="radio" name="sort" value="likes" <?php echo $currentSort === 'likes' ? 'checked' : ''; ?> disabled>
|
<input type="radio" name="sort" value="likes" class="sort-radio" <?php echo $currentSort === 'likes' ? 'checked' : ''; ?>>
|
||||||
<span style="color: #94a3b8;">Beliebtheit (Likes)</span>
|
<span>Beliebtheit</span>
|
||||||
</label>
|
</label>
|
||||||
<label class="s-res-filter-option">
|
<label class="s-res-filter-option">
|
||||||
<input type="radio" name="sort" value="newest" <?php echo $currentSort === 'newest' ? 'checked' : ''; ?> onchange="this.form.submit()">
|
<input type="radio" name="sort" value="newest" class="sort-radio" <?php echo $currentSort === 'newest' ? 'checked' : ''; ?>>
|
||||||
<span>Neueste Beiträge</span>
|
<span>Neueste Beiträge</span>
|
||||||
</label>
|
</label>
|
||||||
<label class="s-res-filter-option">
|
<label class="s-res-filter-option">
|
||||||
<input type="radio" name="sort" value="oldest" <?php echo $currentSort === 'oldest' ? 'checked' : ''; ?> onchange="this.form.submit()">
|
<input type="radio" name="sort" value="oldest" class="sort-radio" <?php echo $currentSort === 'oldest' ? 'checked' : ''; ?>>
|
||||||
<span>Älteste Beiträge</span>
|
<span>Älteste Beiträge</span>
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="s-res-sidebar-box">
|
||||||
|
<h3 class="s-res-sidebar-title">Kategorie filtern</h3>
|
||||||
|
<select id="category-filter" class="s-res-limit-select" style="width: 100%; padding: 8px; border-radius: 6px; border: 1px solid #cbd5e1;">
|
||||||
|
<option value="all">Alle Kategorien</option>
|
||||||
|
<option value="Deutsch">Deutsch</option>
|
||||||
|
<option value="Englisch">Englisch</option>
|
||||||
|
<option value="Franzoesisch">Französisch</option>
|
||||||
|
<option value="Latein">Latein</option>
|
||||||
|
<option value="Literatur">Literatur</option>
|
||||||
|
<option value="Mathematik">Mathematik</option>
|
||||||
|
<option value="Biologie">Biologie</option>
|
||||||
|
<option value="Informatik">Informatik</option>
|
||||||
|
<option value="Chemie">Chemie</option>
|
||||||
|
<option value="Physik">Physik</option>
|
||||||
|
<option value="Astronomie">Astronomie</option>
|
||||||
|
<option value="Geschichte">Geschichte</option>
|
||||||
|
<option value="Erdkunde">Erdkunde</option>
|
||||||
|
<option value="Sozialkunde">Sozialkunde</option>
|
||||||
|
<option value="Wirtschaftskunde">Wirtschaftskunde</option>
|
||||||
|
<option value="Religion">Religion</option>
|
||||||
|
<option value="Ethikunterricht">Ethikunterricht</option>
|
||||||
|
<option value="Philosophie">Philosophie</option>
|
||||||
|
<option value="Psychologie">Psychologie</option>
|
||||||
|
<option value="Kunst">Kunst</option>
|
||||||
|
<option value="Musik">Musik</option>
|
||||||
|
<option value="Theater">Theater</option>
|
||||||
|
<option value="Technik">Technik</option>
|
||||||
|
<option value="Werken">Werken</option>
|
||||||
|
<option value="Hauswirtschaft">Hauswirtschaft</option>
|
||||||
|
<option value="Sport">Sport</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
</aside>
|
</aside>
|
||||||
@@ -96,7 +128,7 @@ $resultCount = count($results);
|
|||||||
if (!empty($results)): ?>
|
if (!empty($results)): ?>
|
||||||
|
|
||||||
<?php foreach ($results as $item): ?>
|
<?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">
|
<div class="s-res-content">
|
||||||
<h2 class="s-res-item-title">
|
<h2 class="s-res-item-title">
|
||||||
<a href="index.php?pfad=showArticle&id=<?php echo $item['id']; ?>" class="s-res-link">
|
<a href="index.php?pfad=showArticle&id=<?php echo $item['id']; ?>" class="s-res-link">
|
||||||
@@ -104,6 +136,7 @@ $resultCount = count($results);
|
|||||||
</a>
|
</a>
|
||||||
</h2>
|
</h2>
|
||||||
<p class="s-res-author">Von: <span class="s-res-author-name"><?php echo htmlspecialchars($item['author']); ?></span></p>
|
<p class="s-res-author">Von: <span class="s-res-author-name"><?php echo htmlspecialchars($item['author']); ?></span></p>
|
||||||
|
<p style="font-size: 0.8rem; color: #3b82f6; margin-top: 2px;">Kategorie: <?php echo htmlspecialchars($item['category'] ?? 'Allgemein'); ?></p>
|
||||||
</div>
|
</div>
|
||||||
<div class="s-res-arrow">→</div>
|
<div class="s-res-arrow">→</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -113,8 +146,8 @@ $resultCount = count($results);
|
|||||||
elseif (isset($_SESSION["search_query"]) && $_SESSION["search_query"] !== "" && $resultCount === 0): ?>
|
elseif (isset($_SESSION["search_query"]) && $_SESSION["search_query"] !== "" && $resultCount === 0): ?>
|
||||||
<p>Keine Beiträge zu diesem Suchbegriff gefunden.</p>
|
<p>Keine Beiträge zu diesem Suchbegriff gefunden.</p>
|
||||||
<?php
|
<?php
|
||||||
elseif (isset($_SESSION["message"]) && $_SESSION["message"] == "missing_parameters"): ?>
|
elseif (isset($_SESSION["message"]) && $_SESSION["message"] == "invalid_search_query"): ?>
|
||||||
<p>Bitte überprüfe deine Sucheingabe und versuche es erneut!</p>
|
<p>Unzulässige Suchanfrage</p>
|
||||||
|
|
||||||
<?php endif; ?>
|
<?php endif; ?>
|
||||||
<?php
|
<?php
|
||||||
@@ -136,23 +169,14 @@ $resultCount = count($results);
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="s-res-page-navigation">
|
<div class="s-res-page-navigation">
|
||||||
<button type="button" class="s-res-page-btn" data-page="<?php echo $currentPage - 1; ?>" <?php echo $currentPage <= 1 ? 'disabled' : ''; ?>>
|
<button type="button" class="s-res-page-btn" id="prev-page-btn" data-page="0">«</button>
|
||||||
«
|
|
||||||
</button>
|
<span id="dynamic-page-numbers"></span>
|
||||||
<!-- Dynamische Seitenzahlen -->
|
|
||||||
<?php for ($i = 1; $i <= $totalPages; $i++): ?>
|
<button type="button" class="s-res-page-btn" id="next-page-btn" data-page="2">»</button>
|
||||||
<button type="button"
|
|
||||||
class="s-res-page-btn <?php echo $i === $currentPage ? 's-res-page-btn-active' : ''; ?>"
|
|
||||||
data-page="<?php echo $i; ?>">
|
|
||||||
<?php echo $i; ?>
|
|
||||||
</button>
|
|
||||||
<?php endfor; ?>
|
|
||||||
<button type="button" class="s-res-page-btn" data-page="<?php echo $currentPage + 1; ?>" <?php echo $currentPage >= $totalPages ? 'disabled' : ''; ?>>
|
|
||||||
»
|
|
||||||
</button>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</main>
|
</main>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
+35
-46
@@ -29,39 +29,12 @@ if (isset($_GET["id"])) {
|
|||||||
Funktion: Stellt einen übergebenen Beitrag dar.
|
Funktion: Stellt einen übergebenen Beitrag dar.
|
||||||
-->
|
-->
|
||||||
<!-- Hauptcontainer für die Beitragsansicht (Ausschließlich der Content-Bereich) -->
|
<!-- Hauptcontainer für die Beitragsansicht (Ausschließlich der Content-Bereich) -->
|
||||||
|
|
||||||
<main class="article-view-container">
|
<main class="article-view-container">
|
||||||
<?php if (isset($_SESSION["message"]) && $_SESSION["message"] == "internal_error"): ?>
|
<?php include_once "includes/alertMessages.php"?>
|
||||||
<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 unset($_SESSION["message"]); ?>
|
|
||||||
|
|
||||||
<!-- Metadaten & Titel -->
|
<!-- Metadaten & Titel -->
|
||||||
|
|
||||||
<div class="article-view-top-section">
|
<div class="article-view-top-section">
|
||||||
|
|
||||||
<?php if (isset($category) && !empty($category)): ?>
|
<?php if (isset($category) && !empty($category)): ?>
|
||||||
<span class="article-view-category"><?php echo htmlspecialchars($category); ?></span>
|
<span class="article-view-category"><?php echo htmlspecialchars($category); ?></span>
|
||||||
<?php endif; ?>
|
<?php endif; ?>
|
||||||
@@ -72,44 +45,60 @@ if (isset($_GET["id"])) {
|
|||||||
|
|
||||||
<div class="article-view-meta">
|
<div class="article-view-meta">
|
||||||
<?php if (isset($author) && !empty($author)): ?>
|
<?php if (isset($author) && !empty($author)): ?>
|
||||||
<span class="article-view-author">
|
<span class="article-view-author">Von: <strong><?php echo htmlspecialchars($author); ?></strong></span>
|
||||||
Von: <strong><?php echo htmlspecialchars($author); ?></strong>
|
|
||||||
</span>
|
|
||||||
<?php endif; ?>
|
<?php endif; ?>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Beitrags-Inhalt -->
|
<!-- Beitrags-Inhalt -->
|
||||||
|
|
||||||
<div class="article-view-content">
|
<div class="article-view-content">
|
||||||
<?php if (isset($content)): ?>
|
<?php if (isset($content)): ?>
|
||||||
<!-- nl2br für Zeilenumbrüche -->
|
<?php
|
||||||
<div class="article-view-body">
|
// Versuchen, den Inhalt von JSON in ein PHP-Array umzuwandeln
|
||||||
<?php echo nl2br(htmlspecialchars($content)); ?>
|
$blocks = json_decode($content, true);
|
||||||
</div>
|
|
||||||
|
// 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; ?>
|
<?php endif; ?>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Beitrags-Endbereich (Tags) -->
|
<!-- Beitrags-Endbereich (Tags) -->
|
||||||
|
|
||||||
|
|
||||||
<?php if (isset($tags) && !empty($tags)): ?>
|
<?php if (isset($tags) && !empty($tags)): ?>
|
||||||
<div class="article-view-bottom-section">
|
<div class="article-view-bottom-section">
|
||||||
<div class="article-view-tags-label">Tags:</div>
|
<div class="article-view-tags-label">Tags:</div>
|
||||||
|
|
||||||
<div class="article-view-tags-list">
|
<div class="article-view-tags-list">
|
||||||
<?php
|
<?php
|
||||||
// Falls $tags ein String ist (z.B. "Web, CSS"), in ein Array umwandeln
|
// Falls $tags ein String ist (z.B. "Web, CSS"), in ein Array umwandeln
|
||||||
$tagArray = is_array($tags) ? $tags : explode(',', $tags);
|
$tagArray = is_array($tags) ? $tags : explode(',', $tags);
|
||||||
|
|
||||||
foreach ($tagArray as $tag):
|
foreach ($tagArray as $tag):
|
||||||
$trimmedTag = trim($tag);
|
$trimmedTag = trim($tag);
|
||||||
|
|
||||||
if (!empty($trimmedTag)):
|
if (!empty($trimmedTag)):
|
||||||
?>
|
?>
|
||||||
<span class="article-view-tag-item">
|
<span class="article-view-tag-item"><?php echo htmlspecialchars($trimmedTag); ?></span>
|
||||||
<?php echo htmlspecialchars($trimmedTag); ?>
|
<?php
|
||||||
</span>
|
endif;
|
||||||
<?php endif;
|
|
||||||
endforeach;
|
endforeach;
|
||||||
?>
|
?>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ include_once 'php/controller/showArticle-controller.php';
|
|||||||
Seite: Beitrag erstellen
|
Seite: Beitrag erstellen
|
||||||
Inhalt: Formular für die Erstellung eines neuen Beitrags
|
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">
|
<main class="editor-main">
|
||||||
<?php include_once "includes/alertMessages.php"?>
|
<?php include_once "includes/alertMessages.php"?>
|
||||||
@@ -26,8 +26,33 @@ include_once 'php/controller/showArticle-controller.php';
|
|||||||
|
|
||||||
?>"
|
?>"
|
||||||
placeholder="Titel hier eingeben" required>
|
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>
|
</main>
|
||||||
|
|
||||||
<!-- Seitenleiste -->
|
<!-- Seitenleiste -->
|
||||||
|
|||||||
@@ -90,6 +90,135 @@
|
|||||||
padding: 18px 12px;
|
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) */
|
/* Responsive Anpassungen unter 760px (für z.B. Smartphones) */
|
||||||
@media (max-width: 760px) {
|
@media (max-width: 760px) {
|
||||||
.article-editor-scope.editor-container {
|
.article-editor-scope.editor-container {
|
||||||
@@ -113,4 +242,8 @@
|
|||||||
border-left: none;
|
border-left: none;
|
||||||
border-top: 1px solid #e0e0e0;
|
border-top: 1px solid #e0e0e0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.article-editor-scope .editor-block textarea {
|
||||||
|
min-height: 150px;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -105,6 +105,37 @@
|
|||||||
cursor: default;
|
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) */
|
/* Responsive Anpassungen unter 760px (für z.B. Smarticlephones) */
|
||||||
@media (max-width: 760px) {
|
@media (max-width: 760px) {
|
||||||
.article-view-container {
|
.article-view-container {
|
||||||
|
|||||||
@@ -15,7 +15,7 @@
|
|||||||
<?php endif; ?>
|
<?php endif; ?>
|
||||||
<?php if (isset($_SESSION["message"]) && $_SESSION["message"] == "invalid_content"): ?>
|
<?php if (isset($_SESSION["message"]) && $_SESSION["message"] == "invalid_content"): ?>
|
||||||
<p class="alert-message is-error">
|
<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>
|
</p>
|
||||||
<?php endif; ?>
|
<?php endif; ?>
|
||||||
<?php if (isset($_SESSION["message"]) && $_SESSION["message"] == "invalid_category"): ?>
|
<?php if (isset($_SESSION["message"]) && $_SESSION["message"] == "invalid_category"): ?>
|
||||||
@@ -63,6 +63,21 @@
|
|||||||
Dein Beitrag wurde erfolgreich veröffentlicht!
|
Dein Beitrag wurde erfolgreich veröffentlicht!
|
||||||
</p>
|
</p>
|
||||||
<?php endif; ?>
|
<?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
|
<?php
|
||||||
unset($_SESSION["message"]);
|
unset($_SESSION["message"]);
|
||||||
?>
|
?>
|
||||||
|
|||||||
@@ -53,7 +53,10 @@ if ($pfad === "deleteAccount") {
|
|||||||
<link rel="stylesheet" href="css/message.css">
|
<link rel="stylesheet" href="css/message.css">
|
||||||
|
|
||||||
<script src="js/paginator.js" async></script>
|
<script src="js/paginator.js" async></script>
|
||||||
|
<script src="js/sorter.js" async></script>
|
||||||
<script src="js/comments.js" defer></script>
|
<script src="js/comments.js" defer></script>
|
||||||
|
<script src="js/editor.js" async></script>
|
||||||
|
<script src="js/filter.js" async></script>
|
||||||
|
|
||||||
<title>EduForge</title>
|
<title>EduForge</title>
|
||||||
</head>
|
</head>
|
||||||
|
|||||||
+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();
|
||||||
|
}
|
||||||
@@ -6,6 +6,10 @@ require_once '../model/LocalArticleManager.php';
|
|||||||
require_once '../model/ArticleManager.php';
|
require_once '../model/ArticleManager.php';
|
||||||
require_once '../validator/article-validator.php';
|
require_once '../validator/article-validator.php';
|
||||||
|
|
||||||
|
if (!isset($_SESSION["user"])) {
|
||||||
|
header("Location: index.php?pfad=login");
|
||||||
|
exit();
|
||||||
|
}
|
||||||
if ($_SERVER["REQUEST_METHOD"] === "POST") {
|
if ($_SERVER["REQUEST_METHOD"] === "POST") {
|
||||||
$_SESSION["old_title"] = $_POST["title"] ?? '';
|
$_SESSION["old_title"] = $_POST["title"] ?? '';
|
||||||
$_SESSION["old_content"] = $_POST["content"] ?? '';
|
$_SESSION["old_content"] = $_POST["content"] ?? '';
|
||||||
@@ -24,12 +28,6 @@ require_once '../validator/article-validator.php';
|
|||||||
$tags = $_POST['tags'] ?? '';
|
$tags = $_POST['tags'] ?? '';
|
||||||
|
|
||||||
// -------------------------------- Validierung der Daten: -------------------------
|
// -------------------------------- Validierung der Daten: -------------------------
|
||||||
if (!articleAuthorValidator($author)) {
|
|
||||||
$_SESSION["message"] = "author_not_valid";
|
|
||||||
header("location: ../../index.php?pfad=createArticle");
|
|
||||||
exit();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!articleTitleValidator($title)) {
|
if (!articleTitleValidator($title)) {
|
||||||
$_SESSION["message"] = "invalid_title";
|
$_SESSION["message"] = "invalid_title";
|
||||||
header("location: ../../index.php?pfad=createArticle");
|
header("location: ../../index.php?pfad=createArticle");
|
||||||
@@ -64,6 +62,59 @@ require_once '../validator/article-validator.php';
|
|||||||
$cleanedTags = array_unique($cleanedTags);
|
$cleanedTags = array_unique($cleanedTags);
|
||||||
$cleanedTags = implode(',', $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: ---------------------------
|
// ----------------- Übertragung der validierten Daten in ArticleManager: ---------------------------
|
||||||
try {
|
try {
|
||||||
$articleManager = ArticleManager::getInstance();
|
$articleManager = ArticleManager::getInstance();
|
||||||
@@ -72,7 +123,7 @@ require_once '../validator/article-validator.php';
|
|||||||
// Formulardaten nach erfolgreichem Erstellen aus der Session löschen
|
// Formulardaten nach erfolgreichem Erstellen aus der Session löschen
|
||||||
unset($_SESSION["old_title"], $_SESSION["old_content"], $_SESSION["old_category"], $_SESSION["old_tags"]);
|
unset($_SESSION["old_title"], $_SESSION["old_content"], $_SESSION["old_category"], $_SESSION["old_tags"]);
|
||||||
|
|
||||||
} catch (Exception $e){
|
} catch (\Throwable $e){
|
||||||
$_SESSION["message"] = "internal_error";
|
$_SESSION["message"] = "internal_error";
|
||||||
header("location: ../../index.php?pfad=createArticle");
|
header("location: ../../index.php?pfad=createArticle");
|
||||||
exit();
|
exit();
|
||||||
@@ -84,7 +135,4 @@ require_once '../validator/article-validator.php';
|
|||||||
exit();
|
exit();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
?>
|
?>
|
||||||
@@ -6,6 +6,11 @@ if (session_status() === PHP_SESSION_NONE) {
|
|||||||
require_once __DIR__ . "/../model/UserManager.php";
|
require_once __DIR__ . "/../model/UserManager.php";
|
||||||
require_once __DIR__ . "/../model/ArticleManager.php";
|
require_once __DIR__ . "/../model/ArticleManager.php";
|
||||||
|
|
||||||
|
if (!isset($_SESSION["user"])) {
|
||||||
|
header("Location: index.php?pfad=login");
|
||||||
|
exit();
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Deregistrierung
|
Deregistrierung
|
||||||
Funktion: Entfernt User aus der Datenbank und beendet die Session
|
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";
|
require_once __DIR__ . "/../model/ArticleManager.php";
|
||||||
|
|
||||||
|
if (!isset($_SESSION["user"])) {
|
||||||
|
header("Location: index.php?pfad=login");
|
||||||
|
exit();
|
||||||
|
}
|
||||||
|
|
||||||
if ($_SERVER["REQUEST_METHOD"] === "POST") {
|
if ($_SERVER["REQUEST_METHOD"] === "POST") {
|
||||||
|
|
||||||
if (isset($_SESSION["user_email"])) {
|
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/ArticleManager.php';
|
||||||
require_once 'php/model/LocalArticleManager.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 {
|
try {
|
||||||
$articleManager = ArticleManager::getInstance();
|
$articleManager = ArticleManager::getInstance();
|
||||||
// Beziehen der Dummy-Beiträge aus dem ArticleManager:
|
|
||||||
$dummy1 = $articleManager->getArticle(1);
|
foreach ($allowedCategories as $categorySlug) {
|
||||||
$dummy2 = $articleManager->getArticle(2);
|
$allCategoryArticles = $articleManager->getArticlesByCategory($categorySlug);
|
||||||
$dummy3 = $articleManager->getArticle(3);
|
|
||||||
|
// 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){
|
} catch (Exception $e){
|
||||||
$_SESSION["message"] = "internal_error";
|
$_SESSION["message"] = "internal_error";
|
||||||
echo "Fehler aufgetreten: " . $e->getMessage();
|
|
||||||
}
|
}
|
||||||
?>
|
?>
|
||||||
@@ -63,6 +63,7 @@ try {
|
|||||||
$_SESSION["user"] = $vorname . " " . $nachname;
|
$_SESSION["user"] = $vorname . " " . $nachname;
|
||||||
$_SESSION["user_email"] = $newEmail;
|
$_SESSION["user_email"] = $newEmail;
|
||||||
|
|
||||||
|
$_SESSION["message"] = "profile_updated";
|
||||||
header("Location: index.php?pfad=profile");
|
header("Location: index.php?pfad=profile");
|
||||||
exit();
|
exit();
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -5,14 +5,15 @@ if (session_status() === PHP_SESSION_NONE) {
|
|||||||
require_once '../model/LocalArticleManager.php';
|
require_once '../model/LocalArticleManager.php';
|
||||||
require_once '../model/ArticleManager.php';
|
require_once '../model/ArticleManager.php';
|
||||||
require_once '../model/Article.php';
|
require_once '../model/Article.php';
|
||||||
|
require_once '../validator/search-validator.php';
|
||||||
|
|
||||||
if ($_SERVER["REQUEST_METHOD"] === "GET" && isset($_GET["q"])) {
|
if ($_SERVER["REQUEST_METHOD"] === "GET" && isset($_GET["q"])) {
|
||||||
|
|
||||||
$search = trim($_GET["q"]);
|
$search = trim($_GET["q"]);
|
||||||
if (empty($search)) {
|
if (!searchQueryValidator($search)) {
|
||||||
$_SESSION["search_results"] = [];
|
$_SESSION["search_results"] = [];
|
||||||
$_SESSION["search_query"] = "";
|
$_SESSION["search_query"] = "";
|
||||||
$_SESSION["message"] = "missing_parameters";
|
$_SESSION["message"] = "invalid_search_query";
|
||||||
} else {
|
} else {
|
||||||
try {
|
try {
|
||||||
|
|
||||||
@@ -51,6 +52,7 @@ if ($_SERVER["REQUEST_METHOD"] === "GET" && isset($_GET["q"])) {
|
|||||||
"category" => $obj->category,
|
"category" => $obj->category,
|
||||||
"tags" => $obj->tags,
|
"tags" => $obj->tags,
|
||||||
"creationDate" => $obj->creationDate
|
"creationDate" => $obj->creationDate
|
||||||
|
//"likes" => $obj->likes
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -65,6 +67,9 @@ if ($_SERVER["REQUEST_METHOD"] === "GET" && isset($_GET["q"])) {
|
|||||||
|
|
||||||
$sort = $_GET['sort'] ?? 'alphabet';
|
$sort = $_GET['sort'] ?? 'alphabet';
|
||||||
$limit = isset($_GET['limit']) ? (int)$_GET['limit'] : 10;
|
$limit = isset($_GET['limit']) ? (int)$_GET['limit'] : 10;
|
||||||
|
if (!searchLimitValidator($limit)) {
|
||||||
|
$limit = 10;
|
||||||
|
}
|
||||||
$page = isset($_GET['page']) ? (int)$_GET['page'] : 1;
|
$page = isset($_GET['page']) ? (int)$_GET['page'] : 1;
|
||||||
header("Location: ../../index.php?pfad=search-results&q=" . urlencode($search) . "&sort=" . urlencode($sort) . "&limit=" . $limit . "&page=" . $page);
|
header("Location: ../../index.php?pfad=search-results&q=" . urlencode($search) . "&sort=" . urlencode($sort) . "&limit=" . $limit . "&page=" . $page);
|
||||||
exit();
|
exit();
|
||||||
|
|||||||
@@ -8,6 +8,11 @@ require_once '../model/ArticleManager.php';
|
|||||||
require_once '../model/Article.php';
|
require_once '../model/Article.php';
|
||||||
require_once '../validator/article-validator.php';
|
require_once '../validator/article-validator.php';
|
||||||
|
|
||||||
|
if (!isset($_SESSION["user"])) {
|
||||||
|
header("Location: index.php?pfad=login");
|
||||||
|
exit();
|
||||||
|
}
|
||||||
|
|
||||||
if ($_SERVER["REQUEST_METHOD"] === "POST") {
|
if ($_SERVER["REQUEST_METHOD"] === "POST") {
|
||||||
$_SESSION["old_title"] = $_POST["title"] ?? '';
|
$_SESSION["old_title"] = $_POST["title"] ?? '';
|
||||||
$_SESSION["old_content"] = $_POST["content"] ?? '';
|
$_SESSION["old_content"] = $_POST["content"] ?? '';
|
||||||
@@ -22,6 +27,20 @@ if ($_SERVER["REQUEST_METHOD"] === "POST") {
|
|||||||
exit();
|
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"])){
|
if (!isset($_POST["title"]) ||!isset($_POST["content"]) || !isset($_POST["category"])){
|
||||||
$_SESSION["message"] = "missing_parameters";
|
$_SESSION["message"] = "missing_parameters";
|
||||||
header("location: ../../index.php?pfad=updateArticle&id=$id");
|
header("location: ../../index.php?pfad=updateArticle&id=$id");
|
||||||
@@ -34,12 +53,6 @@ if ($_SERVER["REQUEST_METHOD"] === "POST") {
|
|||||||
$tags = $_POST['tags'] ?? '';
|
$tags = $_POST['tags'] ?? '';
|
||||||
|
|
||||||
// -------------------------------- Validierung der Daten: -------------------------
|
// -------------------------------- Validierung der Daten: -------------------------
|
||||||
if (!articleAuthorValidator($author)) {
|
|
||||||
$_SESSION["message"] = "author_not_valid";
|
|
||||||
header("location: ../../index.php?pfad=updateArticle&id=$id");
|
|
||||||
exit();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!articleTitleValidator($title)) {
|
if (!articleTitleValidator($title)) {
|
||||||
$_SESSION["message"] = "invalid_title";
|
$_SESSION["message"] = "invalid_title";
|
||||||
header("location: ../../index.php?pfad=updateArticle&id=$id");
|
header("location: ../../index.php?pfad=updateArticle&id=$id");
|
||||||
@@ -75,16 +88,91 @@ if ($_SERVER["REQUEST_METHOD"] === "POST") {
|
|||||||
$cleanedTags = implode(',', $cleanedTags);
|
$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: ---------------------------
|
// ----------------- Übertragung der validierten Daten in ArticleManager: ---------------------------
|
||||||
try {
|
try {
|
||||||
$articleManager = ArticleManager::getInstance();
|
$articleManager = ArticleManager::getInstance();
|
||||||
$article = $articleManager->getArticle($id);
|
$article = $articleManager->getArticle($id);
|
||||||
$article->setTitle($title);
|
$article->setTitle($title);
|
||||||
$article->setContent($content);
|
$article->setContent($finalContent);
|
||||||
$article->setCategory($category);
|
$article->setCategory($category);
|
||||||
$article->setTags($cleanedTags);
|
$article->setTags($cleanedTags);
|
||||||
$articleManager->updateArticle($id ,$article, $author);
|
$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();
|
$_SESSION["message"] = $e->getMessage();
|
||||||
header("location: ../../index.php?pfad=updateArticle&id=$id");
|
header("location: ../../index.php?pfad=updateArticle&id=$id");
|
||||||
exit();
|
exit();
|
||||||
|
|||||||
@@ -145,9 +145,17 @@ class ArticleManager
|
|||||||
// Verteilt die 10 Autoren gleichmäßig (ID 1 -> Autor 1, ID 10 -> Autor 10, ID 11 -> Autor 1)
|
// Verteilt die 10 Autoren gleichmäßig (ID 1 -> Autor 1, ID 10 -> Autor 10, ID 11 -> Autor 1)
|
||||||
$authorEmail = $authors[($id - 1) % 10];
|
$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(
|
$articleManager->addArticle(
|
||||||
$data[0], // Titel
|
$data[0], // Titel
|
||||||
$data[1], // Inhalt
|
$jsonContent, // Inhalt
|
||||||
$authorEmail, // Rotierende Autoren-E-Mail
|
$authorEmail, // Rotierende Autoren-E-Mail
|
||||||
$data[2], // Kategorie
|
$data[2], // Kategorie
|
||||||
$data[3] // Tags
|
$data[3] // Tags
|
||||||
|
|||||||
@@ -1,16 +1,5 @@
|
|||||||
<?php
|
<?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:
|
* Prüft, ob der Titel die folgenden Bedingungen erfüllt:
|
||||||
* Buchstaben von a-z; A-Z
|
* Buchstaben von a-z; A-Z
|
||||||
@@ -33,19 +22,73 @@ function articleTitleValidator($title)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Prüft, ob der Contenttext 10-7000 Zeichen enthält.
|
* Prüft, ob der Content valides JSON ist UND ob alle enthaltenen Blöcke
|
||||||
* @param $content
|
* 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
|
* @return bool
|
||||||
*/
|
*/
|
||||||
function articleContentValidator($content)
|
function articleContentValidator($content)
|
||||||
{
|
{
|
||||||
$content = trim($content);
|
// 1. Grundlegende Typprüfung
|
||||||
$zeichenAnzahl = mb_strlen($content);
|
if (!is_string($content)) {
|
||||||
if ($zeichenAnzahl <= 7000 && $zeichenAnzahl >= 10) {
|
|
||||||
return true;
|
|
||||||
}else{
|
|
||||||
return false;
|
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