Compare commits

..

3 Commits

Author SHA1 Message Date
niklas.ortmann e77c60d8c8 js übernimmt Sortierung + Fallback 2026-07-18 14:04:13 +02:00
niklas.ortmann 7f5cea8c1e revert 2026-07-18 13:33:15 +02:00
niklas.ortmann 71c213ef1e js sortiert die Ergebnisse 2026-07-18 13:18:04 +02:00
5 changed files with 336 additions and 147 deletions
+12 -12
View File
@@ -45,9 +45,9 @@ $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--> <!-- 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 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; ?>"> <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">
@@ -125,13 +125,13 @@ $resultCount = count($results);
<!-- Ergebnisliste --> <!-- Ergebnisliste -->
<div class="s-res-list"> <div class="s-res-list">
<?php <?php
if (!empty($results)): ?> if (!empty($results)): ?>
<?php foreach ($results as $item): <?php foreach ($results as $item):
// Anzahl der Likes ermitteln (falls es ein Array ist, zählen; falls Zahl, direkt nutzen) // 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); $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-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">
@@ -151,13 +151,13 @@ $resultCount = count($results);
</div> </div>
<?php endforeach; ?> <?php endforeach; ?>
<?php <?php
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"] == "invalid_search_query"): ?> elseif (isset($_SESSION["message"]) && $_SESSION["message"] == "invalid_search_query"): ?>
<p>Unzulässige Suchanfrage</p> <p>Unzulässige Suchanfrage</p>
<?php endif; ?> <?php endif; ?>
<?php <?php
unset($_SESSION["message"]); unset($_SESSION["message"]);
@@ -179,9 +179,9 @@ $resultCount = count($results);
<div class="s-res-page-navigation"> <div class="s-res-page-navigation">
<button type="button" class="s-res-page-btn" id="prev-page-btn" data-page="0">«</button> <button type="button" class="s-res-page-btn" id="prev-page-btn" data-page="0">«</button>
<span id="dynamic-page-numbers"></span> <span id="dynamic-page-numbers"></span>
<button type="button" class="s-res-page-btn" id="next-page-btn" data-page="2">»</button> <button type="button" class="s-res-page-btn" id="next-page-btn" data-page="2">»</button>
</div> </div>
+90 -40
View File
@@ -2,49 +2,99 @@
if (session_status() === PHP_SESSION_NONE) { if (session_status() === PHP_SESSION_NONE) {
session_start(); session_start();
} }
include_once "php/controller/index-controller.php"; ob_start();
?> include_once("php/controller/index.php");
<!DOCTYPE html>
<html lang="de">
<head>
<meta charset="utf-8">
<meta name="description" content="EduForge">
<meta name="author" content="Niklas Ortmann">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="icon" type="image/x-icon" href="images/logos/logo_icon.ico">
<link rel="stylesheet" href="css/main.css"> $pfad = $_GET["pfad"] ?? "home";
<link rel="stylesheet" href="css/navbar.css">
<link rel="stylesheet" href="css/footer.css">
<link rel="stylesheet" href="css/search-results.css">
<link rel="stylesheet" href="css/createArticle.css">
<link rel="stylesheet" href="css/profile.css">
<link rel="stylesheet" href="css/showArticle.css">
<link rel="stylesheet" href="css/message.css">
<link rel="stylesheet" href="css/showCategory.css">
<script src="js/paginator.js" async></script> /*
<script src="js/sorter.js" async></script> Controller für Aktionen werden vor der HTML-Ausgabe geladen,
<script src="js/comments.js" defer></script> damit Weiterleitungen mit header() funktionieren.
<script src="js/editor.js" async></script> */
<script src="js/filter.js" async></script> if ($pfad === "login") {
include_once "php/controller/login-controller.php";
<title>EduForge</title>
</head>
<body>
<?php
include_once 'includes/navbar.php';
// Dynamischer Inhalt
if (isset($pfad) && $pfad !== "404" && file_exists('content/' . $pfad . '.php')) {
include_once 'content/' . $pfad . '.php';
} else {
include_once 'content/404.php';
} }
include_once 'includes/footer.php'; if ($pfad === "register") {
include_once "php/controller/register-controller.php";
}
if ($pfad === "password-forgotten") {
include_once "php/controller/password-forgotten-controller.php";
}
if ($pfad === "confirm-register") {
include_once "php/controller/confirm-register-controller.php";
}
if ($pfad === "confirm-password") {
include_once "php/controller/confirm-password-controller.php";
}
if ($pfad === "logout") {
include_once "php/controller/logout-controller.php";
exit();
}
if ($pfad === "deleteAccount") {
include_once "php/controller/deleteAccount-controller.php";
exit();
}
?> ?>
</body> <!--
</html> Seite: Index der Lernplattform
Funktion: Webseitengerüst, Anzeigen von Content
-->
<!DOCTYPE html>
<html lang="de">
<head>
<meta charset="utf-8">
<meta name="description" content="EduForge">
<meta name="author" content="Niklas Ortmann">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="icon" type="image/x-icon" href="images/logos/logo_icon.ico">
<link rel="stylesheet" href="css/main.css">
<link rel="stylesheet" href="css/navbar.css">
<link rel="stylesheet" href="css/footer.css">
<link rel="stylesheet" href="css/search-results.css">
<link rel="stylesheet" href="css/createArticle.css">
<link rel="stylesheet" href="css/profile.css">
<link rel="stylesheet" href="css/showArticle.css">
<link rel="stylesheet" href="css/message.css">
<link rel="stylesheet" href="css/showCategory.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>
<body>
<?php
include_once 'includes/navbar.php';
/*
Dynamischer Inhalt:
Je nach pfad-Parameter wird die passende Datei aus content geladen.
*/
if (file_exists('content/' . $pfad . '.php')) {
include_once 'content/' . $pfad . '.php';
} else {
include_once 'content/404.php';
}
include_once 'includes/footer.php';
?>
</body>
</html>
<?php
ob_end_flush();
?>
+223 -22
View File
@@ -1,27 +1,228 @@
function initPaginator() { (function () {
const form = document.getElementById('search-form-id'); 'use strict';
const pageInput = document.getElementById('s-res-page-input');
const pageButtons = document.querySelectorAll('.s-res-page-navigation .s-res-page-btn');
pageButtons.forEach(button => { const state = {
button.addEventListener('click', function() { items: [],
if (this.disabled) return; sorted: [],
category: 'all',
sort: 'alphabet',
limit: 10,
page: 1,
};
const targetPage = this.getAttribute('data-page'); function loadData() {
const el = document.getElementById('s-res-data');
if (!el) return [];
try {
return JSON.parse(el.textContent);
} catch (e) {
console.error('Suchergebnis-Daten konnten nicht gelesen werden', e);
return [];
}
}
if (targetPage && form && pageInput) { function applyFilterAndSort() {
pageInput.value = targetPage; let list = state.items.slice();
form.submit();
} if (state.category && state.category !== 'all') {
list = list.filter(item => (item.category || '') === state.category);
}
switch (state.sort) {
case 'likes':
list.sort((a, b) => (b.likes || 0) - (a.likes || 0));
break;
case 'newest':
list.sort((a, b) => (b.creationDate || '').localeCompare(a.creationDate || ''));
break;
case 'oldest':
list.sort((a, b) => (a.creationDate || '').localeCompare(b.creationDate || ''));
break;
default:
list.sort((a, b) => (a.title || '').localeCompare(b.title || '', 'de', { sensitivity: 'base' }));
}
state.sorted = list;
const totalPages = Math.max(1, Math.ceil(state.sorted.length / state.limit));
state.page = Math.min(Math.max(state.page, 1), totalPages);
}
function el(tag, className, text) {
const e = document.createElement(tag);
if (className) e.className = className;
if (text !== undefined) e.textContent = text;
return e;
}
function renderList() {
const container = document.querySelector('.s-res-list');
if (!container) return;
const start = (state.page - 1) * state.limit;
const pageItems = state.sorted.slice(start, start + state.limit);
container.innerHTML = '';
if (pageItems.length === 0) {
container.appendChild(el('p', null, 'Keine Beiträge zu diesem Suchbegriff gefunden.'));
return;
}
pageItems.forEach(item => {
const item_el = el('div', 's-res-item');
item_el.dataset.likes = item.likes;
item_el.dataset.category = item.category;
const link = el('a', 's-res-link', item.title);
link.href = 'index.php?pfad=showArticle&id=' + encodeURIComponent(item.id);
const title = el('h2', 's-res-item-title');
title.appendChild(link);
const authorName = el('span', 's-res-author-name', item.author);
const author = el('p', 's-res-author', 'Von: ');
author.appendChild(authorName);
const likes = el('span', 's-res-likes', '❤️ ' + item.likes);
const metaRow = el('div', 's-res-meta-row');
metaRow.appendChild(author);
metaRow.appendChild(likes);
const content = el('div', 's-res-content');
content.appendChild(title);
content.appendChild(metaRow);
item_el.appendChild(content);
item_el.appendChild(el('div', 's-res-arrow', '→'));
container.appendChild(item_el);
}); });
}); }
}
// ist das DOM bereits vollständig aufgebaut? function renderMeta() {
if (document.readyState === 'loading') { const metaEl = document.querySelector('.s-res-meta');
// Falls noch geladen wird, auf das Event warten const queryInput = document.getElementById('site-search');
document.addEventListener('DOMContentLoaded', initPaginator); if (metaEl) {
} else { const q = queryInput ? queryInput.value : '';
// Falls das HTML bereits komplett da ist, sofort ausführen metaEl.textContent = state.sorted.length + ' Treffer für Ihre Suchanfrage "' + q + '"';
initPaginator(); }
} }
function renderPagination() {
const nav = document.querySelector('.s-res-page-navigation');
if (!nav) return;
const totalPages = Math.max(1, Math.ceil(state.sorted.length / state.limit));
nav.innerHTML = '';
const makeBtn = (label, page, disabled, active) => {
const btn = el('button', 's-res-page-btn' + (active ? ' is-active' : ''), label);
btn.type = 'button';
btn.disabled = !!disabled;
btn.addEventListener('click', () => goToPage(page));
return btn;
};
nav.appendChild(makeBtn('«', state.page - 1, state.page <= 1));
const windowSize = 2;
for (let p = 1; p <= totalPages; p++) {
const edge = p === 1 || p === totalPages;
const near = Math.abs(p - state.page) <= windowSize;
if (!edge && !near) {
if (p === 2 || p === totalPages - 1) nav.appendChild(el('span', 's-res-page-dots', '…'));
continue;
}
nav.appendChild(makeBtn(String(p), p, false, p === state.page));
}
nav.appendChild(makeBtn('»', state.page + 1, state.page >= totalPages));
}
function updateUrl() {
const url = new URL(window.location.href);
url.searchParams.set('sort', state.sort);
url.searchParams.set('category', state.category);
url.searchParams.set('limit', state.limit);
url.searchParams.set('page', state.page);
window.history.replaceState(null, '', url);
}
function rerender() {
applyFilterAndSort();
renderList();
renderMeta();
renderPagination();
updateUrl();
}
function goToPage(page) {
const totalPages = Math.max(1, Math.ceil(state.sorted.length / state.limit));
state.page = Math.min(Math.max(page, 1), totalPages);
renderList();
renderPagination();
updateUrl();
}
function initPaginator() {
state.items = loadData();
const checkedSort = document.querySelector('.sort-radio:checked');
state.sort = checkedSort ? checkedSort.value : 'alphabet';
const categorySelect = document.getElementById('category-filter');
state.category = categorySelect ? categorySelect.value.toLowerCase() : 'all';
const limitSelect = document.getElementById('s-res-per-page');
state.limit = limitSelect ? parseInt(limitSelect.value, 10) || 10 : 10;
const params = new URLSearchParams(window.location.search);
state.page = parseInt(params.get('page') || '1', 10) || 1;
document.querySelectorAll('.sort-radio').forEach(radio => {
radio.addEventListener('change', function () {
state.sort = this.value;
state.page = 1;
rerender();
});
});
if (categorySelect) {
categorySelect.addEventListener('change', function () {
state.category = this.value.toLowerCase();
state.page = 1;
rerender();
});
}
if (limitSelect) {
limitSelect.addEventListener('change', function () {
state.limit = parseInt(this.value, 10) || 10;
state.page = 1;
rerender();
});
}
// "Filter anwenden"-Button wird bei aktivem JS nicht gebraucht
const applyBtn = document.querySelector('.s-res-apply-btn');
if (applyBtn) applyBtn.style.display = 'none';
// Server-gerenderte Pagination-Links durch clientseitige Steuerung ersetzen
document.querySelectorAll('.s-res-page-navigation .s-res-page-btn').forEach(link => {
link.addEventListener('click', function (evt) {
evt.preventDefault();
const target = parseInt(this.getAttribute('data-page'), 10);
if (!isNaN(target)) goToPage(target);
});
});
rerender();
}
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', initPaginator);
} else {
initPaginator();
}
})();
+1 -33
View File
@@ -1,35 +1,3 @@
<?php <?php
// Standardpfad
$pfad = $_GET["pfad"] ?? "home";
if ($pfad === "logout") { ?>
include_once "php/controller/logout-controller.php";
exit();
} elseif ($pfad === "deleteAccount") {
include_once "php/controller/deleteAccount-controller.php";
exit();
}
if ($pfad === "login") {
include_once "php/controller/login-controller.php";
} elseif ($pfad === "register") {
include_once "php/controller/register-controller.php";
} elseif ($pfad === "password-forgotten") {
include_once "php/controller/password-forgotten-controller.php";
} elseif ($pfad === "confirm-register") {
include_once "php/controller/confirm-register-controller.php";
} elseif ($pfad === "confirm-password") {
include_once "php/controller/confirm-password-controller.php";
}
// Whitelist
$erlaubte_content_seiten = [
"accessibility", "confirm-password", "confirm-register", "createArticle",
"datenschutz", "home", "impressum", "login", "nutzungsbedingungen",
"password-forgotten", "profile", "register", "search-results",
"show-mail", "showArticle", "showCategory", "updateArticle"
];
if (!in_array($pfad, $erlaubte_content_seiten)) {
$pfad = "404";
}
+10 -40
View File
@@ -16,37 +16,11 @@ if ($_SERVER["REQUEST_METHOD"] === "GET" && isset($_GET["q"])) {
$_SESSION["message"] = "invalid_search_query"; $_SESSION["message"] = "invalid_search_query";
} else { } else {
try { try {
$articleManager = ArticleManager::getInstance(); $articleManager = ArticleManager::getInstance();
$userManager = UserManager::getInstance(); $userManager = UserManager::getInstance();
$results = $articleManager->search($search); $results = $articleManager->search($search);
$sortStyle = $_GET['sort'] ?? 'alphabet';
$_SESSION['search_sort'] = $sortStyle;
if ($sortStyle === 'alphabet') {
// Titel aufsteigend alphabetiisch sortiert
usort($results, function ($a, $b) {
return strcasecmp($a->getTitle(), $b->getTitle());
});
} elseif ($sortStyle === 'likes') {
usort($results, function($a, $b) {
return $b->getLikeCount() <=> $a->getLikeCount();
});
} elseif ($sortStyle === 'newest') {
// Datum neu zu alt sortiert
usort($results, function($a, $b) {
return strcmp($b->getCreationDate(), $a->getCreationDate());
});
} elseif ($sortStyle === 'oldest') {
// Datum alt zu neu sortiert
usort($results, function($a, $b) {
return strcmp($a->getCreationDate(), $b->getCreationDate());
});
}
// Ergebnisse werden in ein flaches array umgewandelt, da sont incomplete-PHP error im Ergebnis
$safeArrayResults = []; $safeArrayResults = [];
foreach ($results as $obj) { foreach ($results as $obj) {
$safeArrayResults[] = [ $safeArrayResults[] = [
@@ -60,25 +34,21 @@ if ($_SERVER["REQUEST_METHOD"] === "GET" && isset($_GET["q"])) {
"likes" => $obj->getLikes(), "likes" => $obj->getLikes(),
]; ];
} }
$_SESSION["search_results"] = $safeArrayResults; $_SESSION["search_results"] = $safeArrayResults;
$_SESSION["search_query"] = $search; $_SESSION["search_query"] = $search;
$_SESSION["message"] = "new_search_results"; $_SESSION["message"] = "new_search_results";
} catch (Exception $e){ // Bei einer neuen Suche Anzeige-Filter zurücksetzen
$_SESSION['search_sort'] = 'alphabet';
$_SESSION['search_category'] = 'all';
$_SESSION['search_limit'] = 10;
} catch (Exception $e) {
$_SESSION["message"] = "internal_error"; $_SESSION["message"] = "internal_error";
} }
} }
$sort = $_GET['sort'] ?? 'alphabet'; header("Location: ../../index.php?pfad=search-results&q=" . urlencode($search));
$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(); exit();
}
}
?>