Compare commits

...

22 Commits

Author SHA1 Message Date
rirat-0 c117c7e641 Merge branch 'dev' into SuchergebnisseJS 2026-06-17 21:48:58 +02:00
rirat-0 12ad9759cf aufraeumen 2 2026-06-17 20:42:31 +02:00
rirat-0 057639be7c anpassung der error message 2026-06-17 20:38:19 +02:00
rirat-0 87e7be1c10 Implementation des validators und korrektes einbinden 2026-06-17 20:30:45 +02:00
rirat-0 1db77346d0 Revert "Implementierung des validators"
This reverts commit 1e6ac0fcd9.
2026-06-17 20:14:59 +02:00
rirat-0 1e6ac0fcd9 Implementierung des validators 2026-06-17 20:14:07 +02:00
rirat-0 b3c74c2172 Debugging 8 2026-06-17 19:54:31 +02:00
rirat-0 0805958033 Anpassung der filter.js
es gab ein problem bei der sortierung, da nur 10 ergebnisse angezeigt werden. mit dieser anpassung koennen ergebnisse 'nachruecken'
2026-06-17 19:47:33 +02:00
rirat-0 5c0e978c93 Debugging 7 2026-06-17 19:38:00 +02:00
rirat-0 520298bae4 Debugging 6 2026-06-17 19:35:02 +02:00
rirat-0 969adc4836 Debugging 5 2026-06-17 19:33:09 +02:00
rirat-0 d9eca06197 Debugging 4 2026-06-17 19:30:11 +02:00
rirat-0 48f2e90e49 Debugging 3 2026-06-17 19:21:58 +02:00
rirat-0 358247a2a1 Filter funktion implementiert 2026-06-17 19:19:31 +02:00
rirat-0 de4d2fe5c0 sortieren nach likes nun moeglich 2026-06-17 17:58:40 +02:00
rirat-0 e1102eb7db Merge branch 'dev' into SuchergebnisseJS 2026-06-17 17:48:08 +02:00
rirat-0 a8df9590fd updater sorter.js fuer client seite 2026-06-17 15:38:59 +02:00
rirat-0 3e453e22ec verschieben der script aufrufe in die index.php 2026-06-17 15:31:05 +02:00
rirat-0 beeab0ec90 aufraeumen 2026-06-17 14:50:05 +02:00
rirat-0 9353a7eaaa Debugging 2 2026-06-17 14:48:33 +02:00
rirat-0 cac8f3046d Debugging 1 2026-06-17 14:45:01 +02:00
rirat-0 66eeac372c Anpassung der results seite und erstellung der script-datei 2026-06-17 14:41:05 +02:00
6 changed files with 250 additions and 30 deletions
+51 -28
View File
@@ -27,7 +27,8 @@ if ($currentPage < 1) {
$offset = ($currentPage - 1) * $limit;
// Nur die Ergebnisse für die aktuelle Seite ausschneiden
$results = array_slice($all_results, $offset, $limit);
//$results = array_slice($all_results, $offset, $limit);
$results = $all_results;
$resultCount = count($results);
?>
@@ -44,12 +45,11 @@ $resultCount = count($results);
<!-- Links: Seitenleiste für Filter und Suche -->
<aside class="s-res-sidebar">
<!-- Sortierfuntion Box und Such Box-->
<form id="search-form-id" action="php/controller/search-results-controller.php" method="GET" class="s-res-sidebar-form">
<!-- Dieses Feld hält die aktuelle Seitenzahl für den Submit bereit -->
<input type="hidden" name="page" id="s-res-page-input" value="<?php echo $currentPage; ?>">
<form action="php/controller/search-results-controller.php" method="GET" id="search-form-id" class="s-res-sidebar-form">
<input type="hidden" id="s-res-page-input" name="page" value="<?php echo $_GET['page'] ?? 1; ?>">
<div class="s-res-sidebar-box">
<div class="s-res-sidebar-box">
<h3 class="s-res-sidebar-title">Suche anpassen</h3>
<input type="search" id="site-search" name="q" placeholder="Suchen..." class="nav__search" value="<?php echo htmlspecialchars($query); ?>" maxlength="50" required>
<button type="submit" class="nav__search-button">Suchen</button>
@@ -60,25 +60,57 @@ $resultCount = count($results);
<?php $currentSort = $_SESSION['search_sort'] ?? 'alphabet'; ?>
<div class="s-res-filter-group">
<label class="s-res-filter-option">
<input type="radio" name="sort" value="alphabet" <?php echo $currentSort === 'alphabet' ? 'checked' : ''; ?> onchange="this.form.submit()">
<input type="radio" name="sort" value="alphabet" class="sort-radio" <?php echo $currentSort === 'alphabet' ? 'checked' : ''; ?>>
<span>Alphabetisch</span>
</label>
<!-- Noch disabled, da likes noch nicht implementiert-->
<label class="s-res-filter-option">
<input type="radio" name="sort" value="likes" <?php echo $currentSort === 'likes' ? 'checked' : ''; ?> onchange="this.form.submit()">
<span>Beliebtheit (Likes)</span>
</label>
<label class="s-res-filter-option">
<input type="radio" name="sort" value="newest" <?php echo $currentSort === 'newest' ? 'checked' : ''; ?> onchange="this.form.submit()">
<input type="radio" name="sort" value="newest" class="sort-radio" <?php echo $currentSort === 'newest' ? 'checked' : ''; ?>>
<span>Neueste Beiträge</span>
</label>
<label class="s-res-filter-option">
<input type="radio" name="sort" value="oldest" <?php echo $currentSort === 'oldest' ? 'checked' : ''; ?> onchange="this.form.submit()">
<input type="radio" name="sort" value="oldest" class="sort-radio" <?php echo $currentSort === 'oldest' ? 'checked' : ''; ?>>
<span>Älteste Beiträge</span>
</label>
</div>
</div>
<div class="s-res-sidebar-box">
<h3 class="s-res-sidebar-title">Kategorie filtern</h3>
<select id="category-filter" class="s-res-limit-select" style="width: 100%; padding: 8px; border-radius: 6px; border: 1px solid #cbd5e1;">
<option value="all">Alle Kategorien</option>
<option value="Deutsch">Deutsch</option>
<option value="Englisch">Englisch</option>
<option value="Franzoesisch">Französisch</option>
<option value="Latein">Latein</option>
<option value="Literatur">Literatur</option>
<option value="Mathematik">Mathematik</option>
<option value="Biologie">Biologie</option>
<option value="Informatik">Informatik</option>
<option value="Chemie">Chemie</option>
<option value="Physik">Physik</option>
<option value="Astronomie">Astronomie</option>
<option value="Geschichte">Geschichte</option>
<option value="Erdkunde">Erdkunde</option>
<option value="Sozialkunde">Sozialkunde</option>
<option value="Wirtschaftskunde">Wirtschaftskunde</option>
<option value="Religion">Religion</option>
<option value="Ethikunterricht">Ethikunterricht</option>
<option value="Philosophie">Philosophie</option>
<option value="Psychologie">Psychologie</option>
<option value="Kunst">Kunst</option>
<option value="Musik">Musik</option>
<option value="Theater">Theater</option>
<option value="Technik">Technik</option>
<option value="Werken">Werken</option>
<option value="Hauswirtschaft">Hauswirtschaft</option>
<option value="Sport">Sport</option>
</select>
</div>
</form>
</aside>
@@ -96,7 +128,7 @@ $resultCount = count($results);
if (!empty($results)): ?>
<?php foreach ($results as $item): ?>
<div class="s-res-item">
<div class="s-res-item" data-likes="<?php echo $item['likes'] ?? 0; ?>" data-category="<?php echo strtolower($item['category'] ?? ''); ?>">
<div class="s-res-content">
<h2 class="s-res-item-title">
<a href="index.php?pfad=showArticle&id=<?php echo $item['id']; ?>" class="s-res-link">
@@ -120,8 +152,8 @@ $resultCount = count($results);
elseif (isset($_SESSION["search_query"]) && $_SESSION["search_query"] !== "" && $resultCount === 0): ?>
<p>Keine Beiträge zu diesem Suchbegriff gefunden.</p>
<?php
elseif (isset($_SESSION["message"]) && $_SESSION["message"] == "missing_parameters"): ?>
<p>Bitte überprüfe deine Sucheingabe und versuche es erneut!</p>
elseif (isset($_SESSION["message"]) && $_SESSION["message"] == "invalid_search_query"): ?>
<p>Unzulässige Suchanfrage</p>
<?php endif; ?>
<?php
@@ -143,23 +175,14 @@ $resultCount = count($results);
</div>
<div class="s-res-page-navigation">
<button type="button" class="s-res-page-btn" data-page="<?php echo $currentPage - 1; ?>" <?php echo $currentPage <= 1 ? 'disabled' : ''; ?>>
«
</button>
<!-- Dynamische Seitenzahlen -->
<?php for ($i = 1; $i <= $totalPages; $i++): ?>
<button type="button"
class="s-res-page-btn <?php echo $i === $currentPage ? 's-res-page-btn-active' : ''; ?>"
data-page="<?php echo $i; ?>">
<?php echo $i; ?>
</button>
<?php endfor; ?>
<button type="button" class="s-res-page-btn" data-page="<?php echo $currentPage + 1; ?>" <?php echo $currentPage >= $totalPages ? 'disabled' : ''; ?>>
»
</button>
<button type="button" class="s-res-page-btn" id="prev-page-btn" data-page="0">«</button>
<span id="dynamic-page-numbers"></span>
<button type="button" class="s-res-page-btn" id="next-page-btn" data-page="2">»</button>
</div>
</div>
</main>
</div>
</div>
+2
View File
@@ -53,8 +53,10 @@ if ($pfad === "deleteAccount") {
<link rel="stylesheet" href="css/message.css">
<script src="js/paginator.js" async></script>
<script src="js/sorter.js" async></script>
<script src="js/comments.js" defer></script>
<script src="js/editor.js" async></script>
<script src="js/filter.js" async></script>
<title>EduForge</title>
</head>
+101
View File
@@ -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();
}
+50
View File
@@ -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 -2
View File
@@ -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();
+40
View File
@@ -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);
}