diff --git a/content/search-results.php b/content/search-results.php index c33b8b5..5d2081f 100644 --- a/content/search-results.php +++ b/content/search-results.php @@ -27,7 +27,8 @@ if ($currentPage < 1) { $offset = ($currentPage - 1) * $limit; // Nur die Ergebnisse für die aktuelle Seite ausschneiden -$results = array_slice($all_results, $offset, $limit); +//$results = array_slice($all_results, $offset, $limit); +$results = $all_results; $resultCount = count($results); ?> @@ -44,12 +45,11 @@ $resultCount = count($results); @@ -95,8 +127,11 @@ $resultCount = count($results); - -
+ +
-
@@ -120,8 +154,8 @@ $resultCount = count($results); elseif (isset($_SESSION["search_query"]) && $_SESSION["search_query"] !== "" && $resultCount === 0): ?>

Keine Beiträge zu diesem Suchbegriff gefunden.

-

Bitte überprüfe deine Sucheingabe und versuche es erneut!

+ elseif (isset($_SESSION["message"]) && $_SESSION["message"] == "invalid_search_query"): ?> +

Unzulässige Suchanfrage

- - - - - - + + + + +
- \ No newline at end of file + diff --git a/index.php b/index.php index 0db18ae..6064334 100644 --- a/index.php +++ b/index.php @@ -53,8 +53,10 @@ if ($pfad === "deleteAccount") { + + EduForge diff --git a/js/filter.js b/js/filter.js new file mode 100644 index 0000000..a20f242 --- /dev/null +++ b/js/filter.js @@ -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 += ` `; + } + 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(); +} \ No newline at end of file diff --git a/js/sorter.js b/js/sorter.js new file mode 100644 index 0000000..5a1ee59 --- /dev/null +++ b/js/sorter.js @@ -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(); +} \ No newline at end of file diff --git a/php/controller/search-results-controller.php b/php/controller/search-results-controller.php index bc33cef..9907a69 100644 --- a/php/controller/search-results-controller.php +++ b/php/controller/search-results-controller.php @@ -5,14 +5,15 @@ if (session_status() === PHP_SESSION_NONE) { require_once '../model/LocalArticleManager.php'; require_once '../model/ArticleManager.php'; require_once '../model/Article.php'; +require_once '../validator/search-validator.php'; if ($_SERVER["REQUEST_METHOD"] === "GET" && isset($_GET["q"])) { $search = trim($_GET["q"]); - if (empty($search)) { + if (!searchQueryValidator($search)) { $_SESSION["search_results"] = []; $_SESSION["search_query"] = ""; - $_SESSION["message"] = "missing_parameters"; + $_SESSION["message"] = "invalid_search_query"; } else { try { @@ -70,6 +71,9 @@ if ($_SERVER["REQUEST_METHOD"] === "GET" && isset($_GET["q"])) { $sort = $_GET['sort'] ?? 'alphabet'; $limit = isset($_GET['limit']) ? (int)$_GET['limit'] : 10; + if (!searchLimitValidator($limit)) { + $limit = 10; + } $page = isset($_GET['page']) ? (int)$_GET['page'] : 1; header("Location: ../../index.php?pfad=search-results&q=" . urlencode($search) . "&sort=" . urlencode($sort) . "&limit=" . $limit . "&page=" . $page); exit(); diff --git a/php/validator/search-validator.php b/php/validator/search-validator.php new file mode 100644 index 0000000..8c0ea63 --- /dev/null +++ b/php/validator/search-validator.php @@ -0,0 +1,40 @@ + 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); +} \ No newline at end of file