Compare commits
21 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 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"?>
|
||||
<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">
|
||||
<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>
|
||||
|
||||
@@ -16,21 +16,18 @@
|
||||
|
||||
## Bekannte Fehler und Mängel
|
||||
- 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
|
||||
Beiträge nacheinander aufgelistet werden (ähnlich wie bei der Suche, wenn nach Fach gefiltert wird).
|
||||
- Die Suchseite und Kategorieseite packen momentan alle passenden Beiträge untereinander. Später sollen zunächst 10
|
||||
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.
|
||||
- Bilder im Beitragseditor sollen zukünftig eine Bildunterschrift bekommen und größenverstellbar sein.
|
||||
- Die Elemente eines Contents im Beitrag werden momentan stumpf untereinander aufgelistet. Soll später
|
||||
sich responisve auch nebeneinander orientieren usw.
|
||||
- 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
|
||||
- Es wurde AJAX verwendet, um asynchrone Erstellung von Kommentaren zu implementieren. Es ermöglicht dem Nutzer, einen
|
||||
Kommentar abzusenden, ohne dass die gesamte Webseite neu geladen werden muss.
|
||||
- Mit JavaScript werden auch clientseitig die Kommentare visuell hinzugefügt und die Kommentarbäume aufgebaut.
|
||||
- JavaScript wird verwendet, um im erweitertem Beitragseditor clientseitig einzelne Content-Boxen erstellen und löschen
|
||||
zu können.
|
||||
- JavaScript wird ebenfalls verwendet, um in die Suchergebnisse clientseitig zu sortieren.
|
||||
- Es wurde ein einfacher Beitrags-Editor erstellt. Mit diesem können Beiträge erstellt oder bearbeitet werden.
|
||||
Es handelt es sich um eine einfache Version. Später sollen z.B. Bilder und die Positionierung der Elemente folgen.
|
||||
- Es sind drei Dummy-Beiträge für den Nutzer max.mustermann hinterlegt.
|
||||
- Die Such-Seite umfasst eine Such- und Sortierfunktion. Jedoch fehlt noch eine
|
||||
Filterfunktion (z.B. nur Mathe anzeigen).
|
||||
|
||||
## Sonstiges
|
||||
- Das Datenschema befindet sich unter /planung/Datenschema.pdf
|
||||
+55
-38
@@ -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>
|
||||
<input type="radio" name="sort" value="likes" class="sort-radio" <?php echo $currentSort === 'likes' ? 'checked' : ''; ?>>
|
||||
<span>Beliebtheit</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,21 +128,15 @@ $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">
|
||||
<?php echo htmlspecialchars($item['title']); ?>
|
||||
</a>
|
||||
</h2>
|
||||
<div class="s-res-meta-row">
|
||||
<p class="s-res-author">Von: <span class="s-res-author-name"><?php echo htmlspecialchars($item['author']); ?></span></p>
|
||||
|
||||
<span class="s-res-likes">
|
||||
❤️ <?php echo isset($item['likes']) && is_array($item['likes']) ? count($item['likes']) : 0; ?>
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<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 class="s-res-arrow">→</div>
|
||||
</div>
|
||||
@@ -120,8 +146,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 +169,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>
|
||||
|
||||
+23
-24
@@ -1,10 +1,28 @@
|
||||
<?php
|
||||
include_once 'php/controller/showArticle-controller.php';
|
||||
require_once 'php/model/CommentManager.php';
|
||||
|
||||
$comments = [];
|
||||
$mainComments = [];
|
||||
$repliesByParent = [];
|
||||
$articleObj = null;
|
||||
|
||||
include_once 'php/controller/showArticle-controller.php';
|
||||
if (isset($_GET["id"])) {
|
||||
try {
|
||||
$commentManager = CommentManager::getInstance();
|
||||
$comments = $commentManager->getCommentsByArticle($_GET["id"]);
|
||||
|
||||
foreach ($comments as $comment) {
|
||||
if ($comment->isReply()) {
|
||||
$parentId = $comment->getParentCommentId();
|
||||
$repliesByParent[$parentId][] = $comment;
|
||||
} else {
|
||||
$mainComments[] = $comment;
|
||||
}
|
||||
}
|
||||
} catch (Exception $e) {
|
||||
$_SESSION["message"] = "internal_error";
|
||||
}
|
||||
}
|
||||
?>
|
||||
<!--
|
||||
Seite: Anzeige für Beiträge
|
||||
@@ -17,28 +35,9 @@ include_once 'php/controller/showArticle-controller.php';
|
||||
<!-- Metadaten & Titel -->
|
||||
<div class="article-view-top-section">
|
||||
|
||||
<div class="article-view-top-section">
|
||||
|
||||
<div class="category-and-likes-row">
|
||||
<?php if (isset($category) && !empty($category)): ?>
|
||||
<span class="article-view-category"><?php echo htmlspecialchars($category); ?></span>
|
||||
<?php endif; ?>
|
||||
|
||||
<!-- Like-Anzeige und dynamischer Like-Button -->
|
||||
<?php if (isset($articleObj) && $articleObj !== null): ?>
|
||||
<div class="article-view-likes">
|
||||
<span>❤️ <span class="like-count"><?php echo $articleObj->getLikeCount(); ?></span></span>
|
||||
|
||||
<?php if (isset($_SESSION["user_email"])): ?>
|
||||
<a href="php/controller/like-controller.php?id=<?php echo $articleObj->getId(); ?>" class="like-toggle-btn">
|
||||
<?php echo $articleObj->hasLiked($_SESSION["user_email"]) ? '👎 Gefällt mir nicht mehr' : '👍 Gefällt mir'; ?>
|
||||
</a>
|
||||
<?php else: ?>
|
||||
<span class="login-hint">(Anmelden zum Liken)</span>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
<?php if (isset($category) && !empty($category)): ?>
|
||||
<span class="article-view-category"><?php echo htmlspecialchars($category); ?></span>
|
||||
<?php endif; ?>
|
||||
|
||||
<h1 class="article-view-title">
|
||||
<?php if (isset($title)) { echo htmlspecialchars($title); } ?>
|
||||
|
||||
@@ -247,17 +247,6 @@ CSS für die Suchergebnis-Seite
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
.s-res-meta-row {
|
||||
display: flex;
|
||||
gap: 15px;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.s-res-likes {
|
||||
font-size: 0.9em;
|
||||
color: #475569;
|
||||
}
|
||||
|
||||
/* Responsive Anpassungen unter 760px (für z.B. Smartphones) */
|
||||
@media (max-width: 768px) {
|
||||
.s-res-layout-grid {
|
||||
|
||||
@@ -239,48 +239,4 @@
|
||||
|
||||
.comment-login-hint p {
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
/*
|
||||
Like-Button etc.
|
||||
*/
|
||||
|
||||
.category-and-likes-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.article-view-likes {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
font-size: 0.95em;
|
||||
}
|
||||
|
||||
.article-view-likes .like-count {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.article-view-likes .login-hint {
|
||||
font-size: 0.8em;
|
||||
color: #777;
|
||||
}
|
||||
|
||||
/* Interaktiver Like/Unlike-Button */
|
||||
.like-toggle-btn {
|
||||
text-decoration: none;
|
||||
padding: 4px 10px;
|
||||
border: 1px solid #bbb;
|
||||
border-radius: 4px;
|
||||
background-color: #f5f5f5;
|
||||
color: #333;
|
||||
font-weight: 500;
|
||||
transition: background-color 0.2s ease, border-color 0.2s ease;
|
||||
}
|
||||
|
||||
.like-toggle-btn:hover {
|
||||
background-color: #eaeaea;
|
||||
border-color: #999;
|
||||
}
|
||||
@@ -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
@@ -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();
|
||||
}
|
||||
@@ -1,42 +0,0 @@
|
||||
<?php
|
||||
if (session_status() === PHP_SESSION_NONE) {
|
||||
session_start();
|
||||
}
|
||||
|
||||
require_once __DIR__ . '/../model/Article.php';
|
||||
require_once __DIR__ . '/../model/ArticleManager.php';
|
||||
|
||||
// 2. Prüfen, ob eine gültige Artikel-ID übergeben wurde
|
||||
if (isset($_GET["id"]) && !empty($_GET["id"])) {
|
||||
$articleId = intval($_GET["id"]);
|
||||
$userEmail = $_SESSION["user_email"];
|
||||
|
||||
if (!isset($_SESSION["user_email"]) || empty($_SESSION["user_email"])) {
|
||||
$_SESSION["message"] = "unauthorized_access";
|
||||
header("Location: ../../index.php?pfad=showArticle&id=" . $articleId);
|
||||
exit();
|
||||
}
|
||||
|
||||
try {
|
||||
$articleManager = ArticleManager::getInstance();
|
||||
$articleManager->toggleLike($articleId, $userEmail);
|
||||
|
||||
header("Location: ../../index.php?pfad=showArticle&id=" . $articleId);
|
||||
exit();
|
||||
|
||||
} catch (NotFoundException $e) {
|
||||
$_SESSION["message"] = "missing_id";
|
||||
header("Location: ../../index.php");
|
||||
exit();
|
||||
} catch (Exception $e) {
|
||||
$_SESSION["message"] = "internal_error";
|
||||
header("Location: ../../index.php");
|
||||
exit();
|
||||
}
|
||||
} else {
|
||||
$_SESSION["message"] = "missing_id";
|
||||
header("Location: ../../index.php");
|
||||
exit();
|
||||
}
|
||||
|
||||
?>
|
||||
@@ -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 {
|
||||
|
||||
@@ -25,22 +26,18 @@ if ($_SERVER["REQUEST_METHOD"] === "GET" && isset($_GET["q"])) {
|
||||
|
||||
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();
|
||||
return strcasecmp($a->title, $b->title);
|
||||
});
|
||||
} elseif ($sortStyle === 'newest') {
|
||||
// Datum neu zu alt sortiert
|
||||
usort($results, function($a, $b) {
|
||||
return strcmp($b->getCreationDate(), $a->getCreationDate());
|
||||
return strcmp($b->creationDate, $a->creationDate);
|
||||
});
|
||||
} elseif ($sortStyle === 'oldest') {
|
||||
// Datum alt zu neu sortiert
|
||||
usort($results, function($a, $b) {
|
||||
return strcmp($a->getCreationDate(), $b->getCreationDate());
|
||||
return strcmp($a->creationDate, $b->creationDate);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -48,14 +45,14 @@ if ($_SERVER["REQUEST_METHOD"] === "GET" && isset($_GET["q"])) {
|
||||
$safeArrayResults = [];
|
||||
foreach ($results as $obj) {
|
||||
$safeArrayResults[] = [
|
||||
"id" => $obj->getId(),
|
||||
"title" => $obj->getTitle(),
|
||||
"content" => $obj->getContent(),
|
||||
"author" => $obj->getAuthor(),
|
||||
"category" => $obj->getCategory(),
|
||||
"tags" => $obj->getTags(),
|
||||
"creationDate" => $obj->getCreationDate(),
|
||||
"likes" => $obj->getLikes(),
|
||||
"id" => $obj->id,
|
||||
"title" => $obj->title,
|
||||
"content" => $obj->content,
|
||||
"author" => $obj->author,
|
||||
"category" => $obj->category,
|
||||
"tags" => $obj->tags,
|
||||
"creationDate" => $obj->creationDate
|
||||
//"likes" => $obj->likes
|
||||
];
|
||||
}
|
||||
|
||||
@@ -70,6 +67,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();
|
||||
|
||||
@@ -5,7 +5,6 @@ if (session_status() === PHP_SESSION_NONE) {
|
||||
|
||||
require_once 'php/model/Article.php';
|
||||
require_once 'php/model/ArticleManager.php';
|
||||
require_once 'php/model/CommentManager.php';
|
||||
|
||||
if (isset($_GET["id"]) && !empty($_GET["id"])){
|
||||
try {
|
||||
@@ -18,25 +17,11 @@ if (isset($_GET["id"]) && !empty($_GET["id"])){
|
||||
$category = $article->getCategory();
|
||||
$author = $article->getAuthor();
|
||||
$tags = $article->getTags();
|
||||
$articleObj = $article; // Objekt für die Like-Abfagen sichern
|
||||
}else{
|
||||
//header("location: index.php?pfad=404");
|
||||
include_once "content/404.php";
|
||||
exit();
|
||||
}
|
||||
|
||||
$commentManager = CommentManager::getInstance();
|
||||
$comments = $commentManager->getCommentsByArticle($_GET["id"]);
|
||||
|
||||
foreach ($comments as $comment) {
|
||||
if ($comment->isReply()) {
|
||||
$parentId = $comment->getParentCommentId();
|
||||
$repliesByParent[$parentId][] = $comment;
|
||||
} else {
|
||||
$mainComments[] = $comment;
|
||||
}
|
||||
}
|
||||
|
||||
} catch (Exception $e){
|
||||
$_SESSION["message"] = "internal_error";
|
||||
exit();
|
||||
|
||||
@@ -8,7 +8,7 @@ require_once '../model/ArticleManager.php';
|
||||
require_once '../model/Article.php';
|
||||
require_once '../validator/article-validator.php';
|
||||
|
||||
if (!isset($_SESSION["user_email"])) {
|
||||
if (!isset($_SESSION["user"])) {
|
||||
header("Location: index.php?pfad=login");
|
||||
exit();
|
||||
}
|
||||
@@ -16,7 +16,7 @@ if (!isset($_SESSION["user_email"])) {
|
||||
if ($_SERVER["REQUEST_METHOD"] === "POST") {
|
||||
$_SESSION["old_title"] = $_POST["title"] ?? '';
|
||||
$_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"] ?? '';
|
||||
|
||||
if (isset($_GET["id"]) && !empty($_GET["id"])) {
|
||||
@@ -30,7 +30,7 @@ if ($_SERVER["REQUEST_METHOD"] === "POST") {
|
||||
try {
|
||||
$articleManager = ArticleManager::getInstance();
|
||||
$article = $articleManager->getArticle($id);
|
||||
if ($article->getAuthor() != $_SESSION["user_email"]) {
|
||||
if ($article->getAuthor() != $_SESSION["user"]->getUsername()) {
|
||||
$_SESSION["message"] = "unauthorized_access";
|
||||
header("location: ../../index.php");
|
||||
exit();
|
||||
|
||||
+10
-39
@@ -7,14 +7,13 @@
|
||||
*/
|
||||
class Article
|
||||
{
|
||||
private $id;
|
||||
private $title;
|
||||
private $content;
|
||||
private $author;
|
||||
private $creationDate;
|
||||
private $category;
|
||||
private $tags;
|
||||
private $likes;
|
||||
public $id;
|
||||
public $title;
|
||||
public $content;
|
||||
public $author;
|
||||
public $creationDate;
|
||||
public $category;
|
||||
public $tags;
|
||||
|
||||
/**
|
||||
* Konstruktor
|
||||
@@ -27,7 +26,7 @@ class Article
|
||||
* @param $tags string optionale Schlagworte für eine bessere Suche
|
||||
* @param $creationDate string Datum der Beitragserstellung
|
||||
*/
|
||||
public function __construct(int $id, string $title, string $content, string $author, string $category, string $tags, string $creationDate, array $likes = [])
|
||||
public function __construct(int $id, string $title, string $content, string $author, string $category, string $tags, string $creationDate)
|
||||
{
|
||||
$this->id = $id;
|
||||
$this->title = $title;
|
||||
@@ -36,7 +35,6 @@ class Article
|
||||
$this->creationDate = $creationDate;
|
||||
$this->category = $category;
|
||||
$this->tags = $tags;
|
||||
$this->likes = $likes;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -69,7 +67,7 @@ class Article
|
||||
|
||||
/**
|
||||
* Gibt den Content eines Beitrags zurück.
|
||||
*
|
||||
* TODO: Content muss noch definiert werden.
|
||||
* @return string
|
||||
*/
|
||||
public function getContent(): string
|
||||
@@ -79,7 +77,7 @@ class Article
|
||||
|
||||
/**
|
||||
* Setzt den Content eines Beitrags.
|
||||
*
|
||||
* TODO: Content muss noch definiert werden.
|
||||
* @param $content
|
||||
* @return void
|
||||
*/
|
||||
@@ -143,34 +141,7 @@ class Article
|
||||
$this->tags = $tags;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gibt alle User-IDs zurück, die diesen Beitrag geliked haben.
|
||||
* @return array
|
||||
*/
|
||||
public function getLikes(): array
|
||||
{
|
||||
return $this->likes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gibt die Gesamtzahl der Likes zurück.
|
||||
* @return int
|
||||
*/
|
||||
public function getLikeCount(): int
|
||||
{
|
||||
return count($this->likes);
|
||||
}
|
||||
|
||||
/**
|
||||
* Prüft, ob ein bestimmter Nutzer den Beitrag bereits geliked hat.
|
||||
*
|
||||
* @param string $userId
|
||||
* @return bool
|
||||
*/
|
||||
public function hasLiked(string $userId): bool
|
||||
{
|
||||
return in_array($userId, $this->likes);
|
||||
}
|
||||
|
||||
}
|
||||
?>
|
||||
@@ -92,18 +92,6 @@ interface ArticleManagerDAO
|
||||
*/
|
||||
public function getArticlesByCategory($category);
|
||||
|
||||
/**
|
||||
* Registriert oder entfernt ein Like eines Nutzers für einen Beitrag.
|
||||
* Wenn schon geliked -> Unlike
|
||||
* Wenn noch nicht geliked -> Like
|
||||
*
|
||||
* @param int $articleId Die ID des Beitrags
|
||||
* @param string $userId Die ID des Nutzers
|
||||
* @return bool True wenn geliked, False wenn unliked
|
||||
* @throws InternalServerErrorException
|
||||
* @throws NotFoundException
|
||||
*/
|
||||
public function toggleLike(int $articleId, string $userId): bool;
|
||||
|
||||
}
|
||||
?>
|
||||
@@ -23,7 +23,6 @@ class DatabaseArticleManager implements ArticleManagerDAO {
|
||||
|
||||
$db = $this->getConnection();
|
||||
|
||||
// Tabelle für Beiträge
|
||||
$db->exec("
|
||||
CREATE TABLE articles (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
@@ -34,15 +33,6 @@ class DatabaseArticleManager implements ArticleManagerDAO {
|
||||
tags TEXT,
|
||||
created TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
||||
);");
|
||||
|
||||
// Tabelle für Likes
|
||||
$db->exec("
|
||||
CREATE TABLE likes (
|
||||
article_id INTEGER,
|
||||
user_id TEXT,
|
||||
PRIMARY KEY (article_id, user_id),
|
||||
FOREIGN KEY (article_id) REFERENCES articles(id) ON DELETE CASCADE
|
||||
);");
|
||||
unset($db);
|
||||
} catch (PDOException $e) {
|
||||
throw new InternalServerErrorException($e->getMessage());
|
||||
@@ -199,8 +189,6 @@ class DatabaseArticleManager implements ArticleManagerDAO {
|
||||
$row = $command->fetch(PDO::FETCH_ASSOC);
|
||||
|
||||
if ($row) {
|
||||
$likes = $this->getLikesForArticle(intval($row['id']));
|
||||
|
||||
return new Article(
|
||||
intval($row['id']),
|
||||
$row['title'],
|
||||
@@ -208,8 +196,7 @@ class DatabaseArticleManager implements ArticleManagerDAO {
|
||||
$row['author'],
|
||||
$row['category'],
|
||||
$row['tags'],
|
||||
$row['created'],
|
||||
$likes
|
||||
$row['created']
|
||||
);
|
||||
}
|
||||
|
||||
@@ -267,8 +254,6 @@ class DatabaseArticleManager implements ArticleManagerDAO {
|
||||
$filteredArticles = [];
|
||||
|
||||
foreach ($rows as $row) {
|
||||
$likes = $this->getLikesForArticle(intval($row['id']));
|
||||
|
||||
$filteredArticles[] = new Article(
|
||||
intval($row['id']),
|
||||
$row['title'],
|
||||
@@ -276,8 +261,7 @@ class DatabaseArticleManager implements ArticleManagerDAO {
|
||||
$row['author'],
|
||||
$row['category'],
|
||||
$row['tags'],
|
||||
$row['created'],
|
||||
$likes
|
||||
$row['created']
|
||||
);
|
||||
}
|
||||
|
||||
@@ -303,8 +287,6 @@ class DatabaseArticleManager implements ArticleManagerDAO {
|
||||
$filteredArticles = [];
|
||||
|
||||
foreach ($rows as $row) {
|
||||
$likes = $this->getLikesForArticle(intval($row['id']));
|
||||
|
||||
$filteredArticles[] = new Article(
|
||||
intval($row['id']),
|
||||
$row['title'],
|
||||
@@ -312,8 +294,7 @@ class DatabaseArticleManager implements ArticleManagerDAO {
|
||||
$row['author'],
|
||||
$row['category'],
|
||||
$row['tags'],
|
||||
$row['created'],
|
||||
$likes
|
||||
$row['created']
|
||||
);
|
||||
}
|
||||
|
||||
@@ -360,8 +341,6 @@ class DatabaseArticleManager implements ArticleManagerDAO {
|
||||
$filteredArticles = [];
|
||||
|
||||
foreach ($rows as $row) {
|
||||
$likes = $this->getLikesForArticle(intval($row['id']));
|
||||
|
||||
$filteredArticles[] = new Article(
|
||||
intval($row['id']),
|
||||
$row['title'] ?? '',
|
||||
@@ -369,8 +348,7 @@ class DatabaseArticleManager implements ArticleManagerDAO {
|
||||
$row['author'] ?? '',
|
||||
$row['category'] ?? '',
|
||||
$row['tags'] ?? '',
|
||||
$row['created'] ?? '',
|
||||
$likes
|
||||
$row['created'] ?? '' // Nutzt 'created' aus deiner DB-Struktur
|
||||
);
|
||||
}
|
||||
|
||||
@@ -381,69 +359,4 @@ class DatabaseArticleManager implements ArticleManagerDAO {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Holt alle User-IDs, die einen bestimmten Beitrag geliked haben.
|
||||
*
|
||||
* @return String[] UserIDs
|
||||
* @throws InternalServerErrorException
|
||||
*/
|
||||
private function getLikesForArticle(int $articleId): array
|
||||
{
|
||||
try {
|
||||
$db = $this->getConnection();
|
||||
$sql = "SELECT user_id FROM likes WHERE article_id = :article_id;";
|
||||
$command = $db->prepare($sql);
|
||||
$command->execute([':article_id' => $articleId]);
|
||||
|
||||
return $command->fetchAll(PDO::FETCH_COLUMN) ?: [];
|
||||
} catch (PDOException $e) {
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
public function toggleLike(int $articleId, string $userId): bool
|
||||
{
|
||||
// prüfen, ob der Artikel überhaupt existiert
|
||||
$article = $this->getArticle($articleId);
|
||||
if (!$article) {
|
||||
throw new NotFoundException("missing_id");
|
||||
}
|
||||
|
||||
try {
|
||||
$db = $this->getConnection();
|
||||
|
||||
// prüfen, ob das Like bereits existiert
|
||||
$checkSql = "SELECT COUNT(*) FROM likes WHERE article_id = :article_id AND user_id = :user_id;";
|
||||
$checkCommand = $db->prepare($checkSql);
|
||||
$checkCommand->execute([
|
||||
':article_id' => $articleId,
|
||||
':user_id' => $userId
|
||||
]);
|
||||
|
||||
$hasLiked = (int)$checkCommand->fetchColumn() > 0;
|
||||
|
||||
if ($hasLiked) {
|
||||
// wenn bereits geliked -> Unlike
|
||||
$deleteSql = "DELETE FROM likes WHERE article_id = :article_id AND user_id = :user_id;";
|
||||
$deleteCommand = $db->prepare($deleteSql);
|
||||
$deleteCommand->execute([
|
||||
':article_id' => $articleId,
|
||||
':user_id' => $userId
|
||||
]);
|
||||
return false; // gibt false zurück, da der Beitrag jetzt nicht mehr geliked ist
|
||||
} else {
|
||||
// wenn noch nicht geliked -> Like
|
||||
$insertSql = "INSERT INTO likes (article_id, user_id) VALUES (:article_id, :user_id);";
|
||||
$insertCommand = $db->prepare($insertSql);
|
||||
$insertCommand->execute([
|
||||
':article_id' => $articleId,
|
||||
':user_id' => $userId
|
||||
]);
|
||||
return true; // gibt true zurück, da der Beitrag jetzt geliked ist
|
||||
}
|
||||
|
||||
} catch (PDOException $e) {
|
||||
throw new InternalServerErrorException("internal_error");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -62,8 +62,7 @@ class LocalArticleManager implements ArticleManagerDAO {
|
||||
"author" => $author,
|
||||
"category" => $category,
|
||||
"tags" => $tags,
|
||||
"creationDate" => date("Y-m-d H:i:s"),
|
||||
"likes" => []
|
||||
"creationDate" => date("Y-m-d H:i:s")
|
||||
];
|
||||
|
||||
$this->saveArticle($articles);
|
||||
@@ -93,8 +92,7 @@ class LocalArticleManager implements ArticleManagerDAO {
|
||||
"author" => $author,
|
||||
"category" => $article->getCategory(),
|
||||
"tags" => $article->getTags(),
|
||||
"creationDate" => $article->getCreationDate(),
|
||||
"likes" => $storedArticle['likes'] ?? []
|
||||
"creationDate" => $article->getCreationDate()
|
||||
];
|
||||
$updated = true;
|
||||
break;
|
||||
@@ -144,17 +142,7 @@ class LocalArticleManager implements ArticleManagerDAO {
|
||||
|
||||
foreach ($articles as $article) {
|
||||
if (isset($article['id']) && $article['id'] == $id) {
|
||||
$likes = isset($article['likes']) && is_array($article['likes']) ? $article['likes'] : [];
|
||||
return new Article(
|
||||
intval($article['id']),
|
||||
$article['title'],
|
||||
$article['content'],
|
||||
$article['author'],
|
||||
$article['category'],
|
||||
$article['tags'],
|
||||
$article['creationDate'],
|
||||
$likes
|
||||
);
|
||||
return new Article(intval($article['id']), $article['title'], $article['content'], $article['author'], $article['category'], $article['tags'], $article['creationDate']);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -180,7 +168,6 @@ class LocalArticleManager implements ArticleManagerDAO {
|
||||
|
||||
foreach ($articles as $article) {
|
||||
if (isset($article['author']) && $article['author'] == $author) {
|
||||
$likes = isset($article['likes']) && is_array($article['likes']) ? $article['likes'] : [];
|
||||
$filteredArticles[] = new Article(
|
||||
intval($article['id']),
|
||||
$article['title'],
|
||||
@@ -188,8 +175,7 @@ class LocalArticleManager implements ArticleManagerDAO {
|
||||
$article['author'],
|
||||
$article['category'],
|
||||
$article['tags'],
|
||||
$article['creationDate'],
|
||||
$likes
|
||||
$article['creationDate']
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -215,7 +201,6 @@ class LocalArticleManager implements ArticleManagerDAO {
|
||||
if (($cleanKeyword !== '' && strpos($title, $cleanKeyword) !== false) ||
|
||||
($cleanKeyword !== '' && strpos($content, $cleanKeyword) !== false)) {
|
||||
|
||||
$likes = isset($article['likes']) && is_array($article['likes']) ? $article['likes'] : [];
|
||||
$filteredArticles[] = new Article(
|
||||
intval($article['id'] ?? 0),
|
||||
$article['title'] ?? '',
|
||||
@@ -223,8 +208,7 @@ class LocalArticleManager implements ArticleManagerDAO {
|
||||
$article['author'] ?? '',
|
||||
$article['category'] ?? '',
|
||||
$article['tags'] ?? '',
|
||||
$article['creationDate'] ?? '',
|
||||
$likes
|
||||
$article['creationDate'] ?? ''
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -239,7 +223,6 @@ class LocalArticleManager implements ArticleManagerDAO {
|
||||
|
||||
foreach ($articles as $article) {
|
||||
if (isset($article['category']) && $article['category'] == $category) {
|
||||
$likes = isset($article['likes']) && is_array($article['likes']) ? $article['likes'] : [];
|
||||
$filteredArticles[] = new Article(
|
||||
intval($article['id']),
|
||||
$article['title'],
|
||||
@@ -247,53 +230,12 @@ class LocalArticleManager implements ArticleManagerDAO {
|
||||
$article['author'],
|
||||
$article['category'],
|
||||
$article['tags'],
|
||||
$article['creationDate'],
|
||||
$likes
|
||||
$article['creationDate']
|
||||
);
|
||||
}
|
||||
}
|
||||
return $filteredArticles;
|
||||
}
|
||||
|
||||
public function toggleLike(int $articleId, string $userId): bool
|
||||
{
|
||||
$articles = $this->getAllArticles();
|
||||
$articleFound = false;
|
||||
$isLikedNow = false;
|
||||
|
||||
foreach ($articles as $index => $article) {
|
||||
if (isset($article['id']) && $article['id'] == $articleId) {
|
||||
$articleFound = true;
|
||||
|
||||
// Likes-Array initialisieren, falls nicht vorhanden
|
||||
if (!isset($articles[$index]['likes']) || !is_array($articles[$index]['likes'])) {
|
||||
$articles[$index]['likes'] = [];
|
||||
}
|
||||
|
||||
$likeIndex = array_search($userId, $articles[$index]['likes']);
|
||||
|
||||
if ($likeIndex !== false) {
|
||||
// Bereits geliked -> Unlike
|
||||
unset($articles[$index]['likes'][$likeIndex]);
|
||||
// Array-Keys neu indizieren, damit JSON sauber bleibt
|
||||
$articles[$index]['likes'] = array_values($articles[$index]['likes']);
|
||||
$isLikedNow = false;
|
||||
} else {
|
||||
// Noch nicht geliked -> Like (User-ID hinzufügen)
|
||||
$articles[$index]['likes'][] = $userId;
|
||||
$isLikedNow = true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!$articleFound) {
|
||||
throw new NotFoundException("missing_id");
|
||||
}
|
||||
|
||||
$this->saveArticle($articles);
|
||||
return $isLikedNow;
|
||||
}
|
||||
|
||||
}
|
||||
?>
|
||||
@@ -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