Compare commits
24 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 17034c928b | |||
| 395041fd44 | |||
| c117c7e641 | |||
| 12ad9759cf | |||
| 057639be7c | |||
| 87e7be1c10 | |||
| 1db77346d0 | |||
| 1e6ac0fcd9 | |||
| b3c74c2172 | |||
| 0805958033 | |||
| 5c0e978c93 | |||
| 520298bae4 | |||
| 969adc4836 | |||
| d9eca06197 | |||
| 48f2e90e49 | |||
| 358247a2a1 | |||
| de4d2fe5c0 | |||
| e1102eb7db | |||
| a8df9590fd | |||
| 3e453e22ec | |||
| beeab0ec90 | |||
| 9353a7eaaa | |||
| cac8f3046d | |||
| 66eeac372c |
Generated
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<project version="4">
|
<project version="4">
|
||||||
<component name="dataSourceStorageLocal" created-in="IU-261.24374.151">
|
<component name="dataSourceStorageLocal" created-in="IU-261.25134.95">
|
||||||
<data-source name="articles" uuid="315cb5c9-2b0f-435b-b602-59823b160908">
|
<data-source name="articles" uuid="315cb5c9-2b0f-435b-b602-59823b160908">
|
||||||
<database-info product="SQLite" version="3.51.1" jdbc-version="4.2" driver-name="SQLite JDBC" driver-version="3.51.1.0" dbms="SQLITE" exact-version="3.51.1" exact-driver-version="3.51">
|
<database-info product="SQLite" version="3.51.1" jdbc-version="4.2" driver-name="SQLite JDBC" driver-version="3.51.1.0" dbms="SQLITE" exact-version="3.51.1" exact-driver-version="3.51">
|
||||||
<identifier-quote-string>"</identifier-quote-string>
|
<identifier-quote-string>"</identifier-quote-string>
|
||||||
|
|||||||
@@ -16,21 +16,18 @@
|
|||||||
|
|
||||||
## Bekannte Fehler und Mängel
|
## Bekannte Fehler und Mängel
|
||||||
- Bitte auf die gesetzten TODO's achten. Wenn Inhalte fehlen, sind sie i.d.R. als TODO kommentiert.
|
- Bitte auf die gesetzten TODO's achten. Wenn Inhalte fehlen, sind sie i.d.R. als TODO kommentiert.
|
||||||
- Die Kategorieseite listet momentan alle passenden Beiträge untereinander. Später sollen mit einem Paginator die neusten
|
- Die Suchseite und Kategorieseite packen momentan alle passenden Beiträge untereinander. Später sollen zunächst 10
|
||||||
Beiträge nacheinander aufgelistet werden (ähnlich wie bei der Suche, wenn nach Fach gefiltert wird).
|
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.
|
- 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.
|
- id in showArticle-controller.php und updateArticle-controller.php wird nicht als gültige numerische ID geprüft.
|
||||||
- Bilder im Beitragseditor sollen zukünftig eine Bildunterschrift bekommen und größenverstellbar sein.
|
- sort in search-results-controller.php wird nicht gegen erlaubte Werte validiert.
|
||||||
- Die Elemente eines Contents im Beitrag werden momentan stumpf untereinander aufgelistet. Soll später
|
|
||||||
sich responisve auch nebeneinander orientieren usw.
|
|
||||||
|
|
||||||
## Besonderheiten des Projektes
|
## Besonderheiten des Projektes
|
||||||
- Es wurde AJAX verwendet, um asynchrone Erstellung von Kommentaren zu implementieren. Es ermöglicht dem Nutzer, einen
|
- Es wurde ein einfacher Beitrags-Editor erstellt. Mit diesem können Beiträge erstellt oder bearbeitet werden.
|
||||||
Kommentar abzusenden, ohne dass die gesamte Webseite neu geladen werden muss.
|
Es handelt es sich um eine einfache Version. Später sollen z.B. Bilder und die Positionierung der Elemente folgen.
|
||||||
- Mit JavaScript werden auch clientseitig die Kommentare visuell hinzugefügt und die Kommentarbäume aufgebaut.
|
- Es sind drei Dummy-Beiträge für den Nutzer max.mustermann hinterlegt.
|
||||||
- JavaScript wird verwendet, um im erweitertem Beitragseditor clientseitig einzelne Content-Boxen erstellen und löschen
|
- Die Such-Seite umfasst eine Such- und Sortierfunktion. Jedoch fehlt noch eine
|
||||||
zu können.
|
Filterfunktion (z.B. nur Mathe anzeigen).
|
||||||
- JavaScript wird ebenfalls verwendet, um in die Suchergebnisse clientseitig zu sortieren.
|
|
||||||
|
|
||||||
## Sonstiges
|
## Sonstiges
|
||||||
- Das Datenschema befindet sich unter /planung/Datenschema.pdf
|
- Das Datenschema befindet sich unter /planung/Datenschema.pdf
|
||||||
+57
-32
@@ -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' : ''; ?> onchange="this.form.submit()">
|
<input type="radio" name="sort" value="likes" class="sort-radio" <?php echo $currentSort === 'likes' ? 'checked' : ''; ?>>
|
||||||
<span>Beliebtheit (Likes)</span>
|
<span>Beliebtheit (Likes)</span>
|
||||||
</label>
|
</label>
|
||||||
<label class="s-res-filter-option">
|
<label class="s-res-filter-option">
|
||||||
<input type="radio" name="sort" value="newest" <?php echo $currentSort === 'newest' ? 'checked' : ''; ?> onchange="this.form.submit()">
|
<input type="radio" name="sort" value="newest" class="sort-radio" <?php echo $currentSort === 'newest' ? 'checked' : ''; ?>>
|
||||||
<span>Neueste Beiträge</span>
|
<span>Neueste Beiträge</span>
|
||||||
</label>
|
</label>
|
||||||
<label class="s-res-filter-option">
|
<label class="s-res-filter-option">
|
||||||
<input type="radio" name="sort" value="oldest" <?php echo $currentSort === 'oldest' ? 'checked' : ''; ?> onchange="this.form.submit()">
|
<input type="radio" name="sort" value="oldest" class="sort-radio" <?php echo $currentSort === 'oldest' ? 'checked' : ''; ?>>
|
||||||
<span>Älteste Beiträge</span>
|
<span>Älteste Beiträge</span>
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="s-res-sidebar-box">
|
||||||
|
<h3 class="s-res-sidebar-title">Kategorie filtern</h3>
|
||||||
|
<select id="category-filter" class="s-res-limit-select" style="width: 100%; padding: 8px; border-radius: 6px; border: 1px solid #cbd5e1;">
|
||||||
|
<option value="all">Alle Kategorien</option>
|
||||||
|
<option value="Deutsch">Deutsch</option>
|
||||||
|
<option value="Englisch">Englisch</option>
|
||||||
|
<option value="Franzoesisch">Französisch</option>
|
||||||
|
<option value="Latein">Latein</option>
|
||||||
|
<option value="Literatur">Literatur</option>
|
||||||
|
<option value="Mathematik">Mathematik</option>
|
||||||
|
<option value="Biologie">Biologie</option>
|
||||||
|
<option value="Informatik">Informatik</option>
|
||||||
|
<option value="Chemie">Chemie</option>
|
||||||
|
<option value="Physik">Physik</option>
|
||||||
|
<option value="Astronomie">Astronomie</option>
|
||||||
|
<option value="Geschichte">Geschichte</option>
|
||||||
|
<option value="Erdkunde">Erdkunde</option>
|
||||||
|
<option value="Sozialkunde">Sozialkunde</option>
|
||||||
|
<option value="Wirtschaftskunde">Wirtschaftskunde</option>
|
||||||
|
<option value="Religion">Religion</option>
|
||||||
|
<option value="Ethikunterricht">Ethikunterricht</option>
|
||||||
|
<option value="Philosophie">Philosophie</option>
|
||||||
|
<option value="Psychologie">Psychologie</option>
|
||||||
|
<option value="Kunst">Kunst</option>
|
||||||
|
<option value="Musik">Musik</option>
|
||||||
|
<option value="Theater">Theater</option>
|
||||||
|
<option value="Technik">Technik</option>
|
||||||
|
<option value="Werken">Werken</option>
|
||||||
|
<option value="Hauswirtschaft">Hauswirtschaft</option>
|
||||||
|
<option value="Sport">Sport</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
</aside>
|
</aside>
|
||||||
@@ -95,8 +127,11 @@ $resultCount = count($results);
|
|||||||
<?php
|
<?php
|
||||||
if (!empty($results)): ?>
|
if (!empty($results)): ?>
|
||||||
|
|
||||||
<?php foreach ($results as $item): ?>
|
<?php foreach ($results as $item):
|
||||||
<div class="s-res-item">
|
// Anzahl der Likes ermitteln (falls es ein Array ist, zählen; falls Zahl, direkt nutzen)
|
||||||
|
$likesCount = isset($item['likes']) && is_array($item['likes']) ? count($item['likes']) : ($item['likes'] ?? 0);
|
||||||
|
?>
|
||||||
|
<div class="s-res-item" data-likes="<?php echo $likesCount; ?>" data-category="<?php echo strtolower($item['category'] ?? ''); ?>">
|
||||||
<div class="s-res-content">
|
<div class="s-res-content">
|
||||||
<h2 class="s-res-item-title">
|
<h2 class="s-res-item-title">
|
||||||
<a href="index.php?pfad=showArticle&id=<?php echo $item['id']; ?>" class="s-res-link">
|
<a href="index.php?pfad=showArticle&id=<?php echo $item['id']; ?>" class="s-res-link">
|
||||||
@@ -107,10 +142,9 @@ $resultCount = count($results);
|
|||||||
<p class="s-res-author">Von: <span class="s-res-author-name"><?php echo htmlspecialchars($item['author']); ?></span></p>
|
<p class="s-res-author">Von: <span class="s-res-author-name"><?php echo htmlspecialchars($item['author']); ?></span></p>
|
||||||
|
|
||||||
<span class="s-res-likes">
|
<span class="s-res-likes">
|
||||||
❤️ <?php echo isset($item['likes']) && is_array($item['likes']) ? count($item['likes']) : 0; ?>
|
❤️ <?php echo $likesCount; ?>
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
<div class="s-res-arrow">→</div>
|
<div class="s-res-arrow">→</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -120,8 +154,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
|
||||||
@@ -143,23 +177,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>
|
||||||
|
|||||||
@@ -53,8 +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/editor.js" async></script>
|
||||||
|
<script src="js/filter.js" async></script>
|
||||||
|
|
||||||
<title>EduForge</title>
|
<title>EduForge</title>
|
||||||
</head>
|
</head>
|
||||||
|
|||||||
+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();
|
||||||
|
}
|
||||||
@@ -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 {
|
||||||
|
|
||||||
@@ -70,6 +71,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,7 +8,7 @@ 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_email"])) {
|
if (!isset($_SESSION["user"])) {
|
||||||
header("Location: index.php?pfad=login");
|
header("Location: index.php?pfad=login");
|
||||||
exit();
|
exit();
|
||||||
}
|
}
|
||||||
@@ -16,7 +16,7 @@ if (!isset($_SESSION["user_email"])) {
|
|||||||
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"] ?? '';
|
||||||
$_SESSION["old_category"] = $_POST["category"] ?? '';
|
$_SESSION["old_category"] = $_POST["category"] ?? ''; // TODO: die Kategorie im Dropdown setzen, wenn der Editor erneut geöffnet wird.
|
||||||
$_SESSION["old_tags"] = $_POST["tags"] ?? '';
|
$_SESSION["old_tags"] = $_POST["tags"] ?? '';
|
||||||
|
|
||||||
if (isset($_GET["id"]) && !empty($_GET["id"])) {
|
if (isset($_GET["id"]) && !empty($_GET["id"])) {
|
||||||
@@ -30,7 +30,7 @@ if ($_SERVER["REQUEST_METHOD"] === "POST") {
|
|||||||
try {
|
try {
|
||||||
$articleManager = ArticleManager::getInstance();
|
$articleManager = ArticleManager::getInstance();
|
||||||
$article = $articleManager->getArticle($id);
|
$article = $articleManager->getArticle($id);
|
||||||
if ($article->getAuthor() != $_SESSION["user_email"]) {
|
if ($article->getAuthor() != $_SESSION["user"]->getUsername()) {
|
||||||
$_SESSION["message"] = "unauthorized_access";
|
$_SESSION["message"] = "unauthorized_access";
|
||||||
header("location: ../../index.php");
|
header("location: ../../index.php");
|
||||||
exit();
|
exit();
|
||||||
|
|||||||
@@ -0,0 +1,40 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Prüft, ob eine Suchanfrage gültig ist.
|
||||||
|
*
|
||||||
|
* Erlaubt werden Buchstaben, Zahlen, Umlaute, typische Satzzeichen und Leerzeichen.
|
||||||
|
* Die Länge muss zwischen 1 und 50 Zeichen liegen.
|
||||||
|
*
|
||||||
|
* @param string $query Zu prüfender Suchbegriff
|
||||||
|
*
|
||||||
|
* @return bool true wenn die Suche gültig ist, sonst false
|
||||||
|
*/
|
||||||
|
function searchQueryValidator($query)
|
||||||
|
{
|
||||||
|
$query = trim($query);
|
||||||
|
|
||||||
|
// Mindestens 1 Zeichen, maximal 50 Zeichen
|
||||||
|
$length = mb_strlen($query);
|
||||||
|
if ($length < 1 || $length > 50) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Erlaubt Buchstaben (inkl. Umlaut/ß), Zahlen, Leerzeichen sowie ?, !, ., -, _
|
||||||
|
$searchPattern = '/^[a-zA-Z0-9äöüÄÖÜß\s?!.,\-_]+$/u';
|
||||||
|
|
||||||
|
return preg_match($searchPattern, $query) === 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Prüft, ob das gewählte Treffer-Limit erlaubt ist.
|
||||||
|
*
|
||||||
|
* @param int|string $limit Das zu prüfende Limit
|
||||||
|
*
|
||||||
|
* @return bool true wenn das Limit 10, 20, 50 oder 100 ist, sonst false
|
||||||
|
*/
|
||||||
|
function searchLimitValidator($limit)
|
||||||
|
{
|
||||||
|
$allowedLimits = [10, 20, 50, 100];
|
||||||
|
return in_array((int)$limit, $allowedLimits, true);
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user