585 lines
30 KiB
PHP
585 lines
30 KiB
PHP
<?php
|
|
// =====================================================================
|
|
// Moderation Page
|
|
// Lists Contributions for Review. Moderators can approve, reject,
|
|
// edit and delete Contributions. Includes Map Preview and Filtering.
|
|
// =====================================================================
|
|
|
|
// Reads Environment Configfile
|
|
$envFile = __DIR__ . '/../../.env';
|
|
if (file_exists($envFile)) {
|
|
$lines = file($envFile, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES);
|
|
foreach ($lines as $line) {
|
|
if (strpos(trim($line), '#') === 0) continue;
|
|
list($key, $value) = array_map('trim', explode('=', $line, 2));
|
|
putenv("$key=$value");
|
|
}
|
|
}
|
|
|
|
require_once __DIR__ . '/api/db.php';
|
|
require_once __DIR__ . '/api/auth.php';
|
|
|
|
|
|
// -----------------------------------------------------------------
|
|
// Routing: Login, Logout, or Main Page
|
|
// -----------------------------------------------------------------
|
|
$page = $_GET['page'] ?? 'main';
|
|
|
|
// Handles Login
|
|
if ($page === 'login' && $_SERVER['REQUEST_METHOD'] === 'POST') {
|
|
$password = $_POST['password'] ?? '';
|
|
if (admin_login($password)) {
|
|
header('Location: admin.php');
|
|
exit;
|
|
} else {
|
|
$login_error = 'Falsches Passwort.';
|
|
}
|
|
}
|
|
|
|
// Handles Logout
|
|
if ($page === 'logout') {
|
|
admin_logout();
|
|
header('Location: admin.php?page=login');
|
|
exit;
|
|
}
|
|
|
|
// -----------------------------------------------------------------
|
|
// Loads Municipality Configuration for Theming
|
|
// -----------------------------------------------------------------
|
|
$pdo = get_db();
|
|
$stmt = $pdo->prepare("SELECT * FROM municipalities WHERE slug = :slug");
|
|
$stmt->execute([':slug' => getenv('MUNICIPALITY_SLUG')]);
|
|
$municipality = $stmt->fetch();
|
|
|
|
|
|
// Loads News for Moderation
|
|
$stmt = $pdo->prepare("
|
|
SELECT news_id, title, content, author_name, published_at, created_at
|
|
FROM news
|
|
WHERE municipality_id = :mid
|
|
ORDER BY published_at DESC
|
|
");
|
|
$stmt->execute([':mid' => $municipality['municipality_id']]);
|
|
$news_items = $stmt->fetchAll();
|
|
|
|
|
|
// Loads all Comments with Contribution Titles for Moderation
|
|
$stmt = $pdo->prepare("
|
|
SELECT cm.comment_id, cm.contribution_id, cm.author_name, cm.browser_id,
|
|
cm.content, cm.status, cm.created_at,
|
|
co.title AS contribution_title, co.category AS contribution_category
|
|
FROM comments cm
|
|
JOIN contributions co ON cm.contribution_id = co.contribution_id
|
|
WHERE co.municipality_id = :mid
|
|
ORDER BY cm.created_at DESC
|
|
");
|
|
$stmt->execute([':mid' => $municipality['municipality_id']]);
|
|
$all_comments = $stmt->fetchAll();
|
|
|
|
// Counts Comments per Status
|
|
$comment_counts = ['pending' => 0, 'approved' => 0, 'rejected' => 0];
|
|
foreach ($all_comments as $c) {
|
|
if (isset($comment_counts[$c['status']])) {
|
|
$comment_counts[$c['status']]++;
|
|
}
|
|
}
|
|
$comment_counts['total'] = count($all_comments);
|
|
|
|
|
|
// Shows Login Page if not authenticated
|
|
if ($page === 'login' || !is_admin()) {
|
|
show_login_page($municipality, $login_error ?? null);
|
|
exit;
|
|
}
|
|
|
|
// -----------------------------------------------------------------
|
|
// Loads shared Category Definitions
|
|
// -----------------------------------------------------------------
|
|
$categories = get_categories();
|
|
|
|
// -----------------------------------------------------------------
|
|
// Loads Contributions and Statistics
|
|
// -----------------------------------------------------------------
|
|
|
|
// Loads all Contributions for Municipality
|
|
$stmt = $pdo->prepare("
|
|
SELECT contribution_id, title, category, description, author_name, photo_path,
|
|
geom_type, status, likes_count, dislikes_count, comment_count, created_at, updated_at
|
|
FROM contributions
|
|
WHERE municipality_id = :mid
|
|
ORDER BY created_at DESC
|
|
");
|
|
$stmt->execute([':mid' => $municipality['municipality_id']]);
|
|
$all_contributions = $stmt->fetchAll();
|
|
|
|
// Counts per Status
|
|
$counts = ['pending' => 0, 'approved' => 0, 'rejected' => 0];
|
|
foreach ($all_contributions as $item) {
|
|
if (isset($counts[$item['status']])) {
|
|
$counts[$item['status']]++;
|
|
}
|
|
}
|
|
$counts['total'] = count($all_contributions);
|
|
|
|
// -----------------------------------------------------------------
|
|
// Renders Main Page
|
|
// -----------------------------------------------------------------
|
|
?>
|
|
<!DOCTYPE html>
|
|
<html lang="de">
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
<title>Moderation — <?= htmlspecialchars($municipality['name']) ?></title>
|
|
<link rel="icon" href="<?= htmlspecialchars($municipality['logo_path'] ?? 'assets/icon-municipality.png') ?>" type="image/png">
|
|
|
|
<!-- Loads CSS Dependencies -->
|
|
|
|
<!-- Font Awesome for Icons -->
|
|
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.1/css/all.min.css">
|
|
|
|
<!-- Leaflet -->
|
|
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/leaflet/1.9.4/leaflet.min.css">
|
|
|
|
<!-- Application Styles -->
|
|
<link rel="stylesheet" href="styles.css">
|
|
|
|
|
|
<!-- Loads JavaScript Dependencies -->
|
|
|
|
<!-- SweetAlert2 -->
|
|
<script src="https://cdn.jsdelivr.net/npm/sweetalert2@11.14.0/dist/sweetalert2.all.min.js"></script>
|
|
|
|
|
|
<!-- Loads Municipality Theme from Database -->
|
|
<style>:root { --color-primary: <?= htmlspecialchars($municipality['primary_color']) ?>; }</style>
|
|
</head>
|
|
<body>
|
|
|
|
<!-- ============================================================= -->
|
|
<!-- Header -->
|
|
<!-- ============================================================= -->
|
|
<div class="page-header">
|
|
<div class="page-header-inner">
|
|
<h1><i class="fa-solid fa-shield-halved"></i> Moderationsportal <?= htmlspecialchars($municipality['name']) ?></h1>
|
|
<div class="page-header-nav">
|
|
<a href="index.php"><i class="fa-solid fa-map"></i> Bürgerportal</a>
|
|
<a href="admin.php?page=logout"><i class="fa-solid fa-right-from-bracket"></i> Abmelden</a>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="page-container">
|
|
|
|
<!-- ========================================================= -->
|
|
<!-- Page Navigation Tabs -->
|
|
<!-- ========================================================= -->
|
|
<div class="page-tabs">
|
|
<button class="page-tab active" onclick="showPageTab('contributions')">
|
|
<i class="fa-solid fa-list-check"></i> Beiträge
|
|
</button>
|
|
<button class="page-tab" onclick="showPageTab('comments')">
|
|
<i class="fa-solid fa-comments"></i> Kommentare
|
|
</button>
|
|
<button class="page-tab" onclick="showPageTab('news')">
|
|
<i class="fa-solid fa-newspaper"></i> Neuigkeiten
|
|
</button>
|
|
<button class="page-tab" onclick="showPageTab('stats')">
|
|
<i class="fa-solid fa-chart-bar"></i> Statistik
|
|
</button>
|
|
<button class="page-tab" onclick="showPageTab('users')">
|
|
<i class="fa-solid fa-users"></i> Benutzer
|
|
</button>
|
|
</div>
|
|
|
|
|
|
<!-- ========================================================= -->
|
|
<!-- Contributions Tab -->
|
|
<!-- ========================================================= -->
|
|
<div id="tab-contributions" class="page-tab-content">
|
|
|
|
<!-- Status Filter Tabs -->
|
|
<div class="filter-tabs">
|
|
<button class="filter-tab active" onclick="filterByStatus('all', this)">
|
|
Alle <span class="tab-count"><?= $counts['total'] ?></span>
|
|
</button>
|
|
<button class="filter-tab" onclick="filterByStatus('pending', this)">
|
|
Ausstehend <span class="tab-count"><?= $counts['pending'] ?></span>
|
|
</button>
|
|
<button class="filter-tab" onclick="filterByStatus('approved', this)">
|
|
Akzeptiert <span class="tab-count"><?= $counts['approved'] ?></span>
|
|
</button>
|
|
<button class="filter-tab" onclick="filterByStatus('rejected', this)">
|
|
Abgelehnt <span class="tab-count"><?= $counts['rejected'] ?></span>
|
|
</button>
|
|
</div>
|
|
|
|
<!-- Sort Controls -->
|
|
<div class="sort-controls">
|
|
<span id="visible-count"><?= $counts['total'] ?> Beiträge</span>
|
|
<select onchange="sortContributions(this.value)">
|
|
<option value="date-desc">Neueste zuerst</option>
|
|
<option value="date-asc">Älteste zuerst</option>
|
|
<option value="category">Nach Kategorie</option>
|
|
</select>
|
|
</div>
|
|
|
|
<!-- Contribution List -->
|
|
<div id="contributions-container">
|
|
<?php if (empty($all_contributions)): ?>
|
|
<div class="empty-state">
|
|
<i class="fa-solid fa-inbox" style="font-size:2rem;margin-bottom:8px;display:block;"></i>
|
|
Noch keine Beiträge vorhanden.
|
|
</div>
|
|
<?php else: ?>
|
|
<?php foreach ($all_contributions as $item):
|
|
$cat = $categories[$item['category']] ?? ['label' => $item['category'], 'faIcon' => 'fa-question', 'color' => '#999'];
|
|
$status_label = ['pending' => 'Ausstehend', 'approved' => 'Akzeptiert', 'rejected' => 'Abgelehnt'];
|
|
?>
|
|
<div class="contribution-row"
|
|
data-status="<?= $item['status'] ?>"
|
|
data-category="<?= htmlspecialchars($item['category']) ?>"
|
|
data-date="<?= $item['created_at'] ?>"
|
|
data-id="<?= $item['contribution_id'] ?>">
|
|
|
|
<!-- Collapsed Header: Title + Status -->
|
|
<div class="contribution-row-header" onclick="toggleRow(this.parentElement)">
|
|
<div class="contribution-row-summary">
|
|
<span class="title"><?= htmlspecialchars($item['title']) ?></span>
|
|
<span class="badge badge-category">
|
|
<i class="fa-solid <?= $cat['faIcon'] ?>"></i>
|
|
<?= $cat['label'] ?>
|
|
</span>
|
|
<span class="badge badge-<?= $item['status'] ?>"><?= $status_label[$item['status']] ?? $item['status'] ?></span>
|
|
</div>
|
|
<i class="fa-solid fa-chevron-down collapse-icon"></i>
|
|
</div>
|
|
|
|
<!-- Expanded Detail -->
|
|
<div class="contribution-row-detail">
|
|
<div class="detail-layout">
|
|
<!-- Map and Photo Slider -->
|
|
<div class="detail-slider" id="slider-<?= $item['contribution_id'] ?>">
|
|
<!-- Slide 1: Map -->
|
|
<div class="detail-slide active" data-slide="map">
|
|
<div class="detail-map" id="map-<?= $item['contribution_id'] ?>"
|
|
data-contribution-id="<?= $item['contribution_id'] ?>">
|
|
</div>
|
|
</div>
|
|
<?php if (!empty($item['photo_path'])): ?>
|
|
<!-- Slide 2: Photo -->
|
|
<div class="detail-slide" data-slide="photo" style="display:none;">
|
|
<img src="<?= htmlspecialchars($item['photo_path']) ?>" alt="Foto"
|
|
class="detail-slide-photo" onclick="window.open('<?= htmlspecialchars($item['photo_path']) ?>', '_blank')">
|
|
</div>
|
|
<!-- Slider Arrows -->
|
|
<button class="slider-arrow slider-arrow-left" onclick="slideDetail(<?= $item['contribution_id'] ?>, -1)">
|
|
<i class="fa-solid fa-chevron-left"></i>
|
|
</button>
|
|
<button class="slider-arrow slider-arrow-right" onclick="slideDetail(<?= $item['contribution_id'] ?>, 1)">
|
|
<i class="fa-solid fa-chevron-right"></i>
|
|
</button>
|
|
<?php endif; ?>
|
|
</div>
|
|
|
|
<!-- Content -->
|
|
<div class="detail-content">
|
|
<?php if ($item['description']): ?>
|
|
<div class="description"><?= htmlspecialchars($item['description']) ?></div>
|
|
<?php else: ?>
|
|
<div class="description empty">Keine Beschreibung vorhanden.</div>
|
|
<?php endif; ?>
|
|
|
|
<div class="detail-meta">
|
|
<span><i class="fa-solid fa-user"></i> <?= htmlspecialchars($item['author_name']) ?></span>
|
|
<span><i class="fa-solid fa-calendar"></i> <?= date('d.m.Y, H:i', strtotime($item['created_at'])) ?> Uhr</span>
|
|
<span>
|
|
<i class="fa-solid fa-thumbs-up"></i> <?= $item['likes_count'] ?>
|
|
·
|
|
<i class="fa-solid fa-thumbs-down"></i> <?= $item['dislikes_count'] ?>
|
|
·
|
|
<i class="fa-solid fa-comment"></i> <?= $item['comment_count'] ?? 0 ?>
|
|
|
|
|
|
</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Action Buttons -->
|
|
<div class="action-buttons">
|
|
<?php if ($item['status'] !== 'approved'): ?>
|
|
<button class="btn btn-approve" onclick="changeStatus(<?= $item['contribution_id'] ?>, 'approved')">
|
|
<i class="fa-solid fa-check"></i> Akzeptieren
|
|
</button>
|
|
<?php endif; ?>
|
|
|
|
<?php if ($item['status'] !== 'rejected'): ?>
|
|
<button class="btn btn-reject" onclick="changeStatus(<?= $item['contribution_id'] ?>, 'rejected')">
|
|
<i class="fa-solid fa-xmark"></i> Ablehnen
|
|
</button>
|
|
<?php endif; ?>
|
|
|
|
<?php if ($item['status'] !== 'pending'): ?>
|
|
<button class="btn btn-reset" onclick="changeStatus(<?= $item['contribution_id'] ?>, 'pending')">
|
|
<i class="fa-solid fa-rotate-left"></i> Zurücksetzen
|
|
</button>
|
|
<?php endif; ?>
|
|
|
|
<button class="btn btn-edit" onclick="editContribution(<?= $item['contribution_id'] ?>, '<?= htmlspecialchars(addslashes($item['title']), ENT_QUOTES) ?>', '<?= htmlspecialchars(addslashes($item['description'] ?? ''), ENT_QUOTES) ?>')">
|
|
<i class="fa-solid fa-pen"></i> Bearbeiten
|
|
</button>
|
|
|
|
<button class="btn btn-delete" onclick="deleteContribution(<?= $item['contribution_id'] ?>)">
|
|
<i class="fa-solid fa-trash"></i> Löschen
|
|
</button>
|
|
|
|
<a class="btn btn-map" href="index.php" target="_blank">
|
|
<i class="fa-solid fa-map-location-dot"></i> Karte
|
|
</a>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<?php endforeach; ?>
|
|
<?php endif; ?>
|
|
</div>
|
|
</div>
|
|
|
|
|
|
<!-- ========================================================= -->
|
|
<!-- Comments Moderation Tab -->
|
|
<!-- ========================================================= -->
|
|
<div id="tab-comments" class="page-tab-content" style="display:none;">
|
|
|
|
<!-- Status Filter Tabs for Comments -->
|
|
<div class="filter-tabs" id="comment-filter-tabs">
|
|
<button class="filter-tab active" onclick="filterCommentsByStatus('all', this)">
|
|
Alle <span class="tab-count"><?= $comment_counts['total'] ?></span>
|
|
</button>
|
|
<button class="filter-tab" onclick="filterCommentsByStatus('pending', this)">
|
|
Ausstehend <span class="tab-count"><?= $comment_counts['pending'] ?></span>
|
|
</button>
|
|
<button class="filter-tab" onclick="filterCommentsByStatus('approved', this)">
|
|
Akzeptiert <span class="tab-count"><?= $comment_counts['approved'] ?></span>
|
|
</button>
|
|
<button class="filter-tab" onclick="filterCommentsByStatus('rejected', this)">
|
|
Abgelehnt <span class="tab-count"><?= $comment_counts['rejected'] ?></span>
|
|
</button>
|
|
</div>
|
|
|
|
<!-- Sort Controls -->
|
|
<div class="sort-controls">
|
|
<span id="comment-visible-count"><?= $comment_counts['total'] ?> Kommentare</span>
|
|
<select onchange="sortCommentRows(this.value)">
|
|
<option value="date-desc">Neueste zuerst</option>
|
|
<option value="date-asc">Älteste zuerst</option>
|
|
<option value="contribution">Nach Beitrag</option>
|
|
</select>
|
|
</div>
|
|
|
|
<!-- Comments List -->
|
|
<div id="comments-mod-container">
|
|
<?php if (empty($all_comments)): ?>
|
|
<div class="empty-state">
|
|
<i class="fa-solid fa-comments" style="font-size:2rem;margin-bottom:8px;display:block;"></i>
|
|
Noch keine Kommentare vorhanden.
|
|
</div>
|
|
<?php else: ?>
|
|
<?php foreach ($all_comments as $comment):
|
|
$comment_cat = $categories[$comment['contribution_category'] ?? ''] ?? ['label' => 'Unbekannt', 'faIcon' => 'fa-question', 'color' => '#999'];
|
|
$comment_status_label = ['pending' => 'Ausstehend', 'approved' => 'Akzeptiert', 'rejected' => 'Abgelehnt'];
|
|
?>
|
|
<div class="contribution-row comment-mod-row"
|
|
data-status="<?= $comment['status'] ?>"
|
|
data-date="<?= $comment['created_at'] ?>"
|
|
data-contribution="<?= htmlspecialchars($comment['contribution_title']) ?>">
|
|
|
|
<!-- Collapsed: Contribution Title + Comment Status + Category -->
|
|
<div class="contribution-row-header" onclick="toggleRow(this.parentElement)">
|
|
<div class="contribution-row-summary">
|
|
<span class="title"><?= htmlspecialchars($comment['contribution_title']) ?></span>
|
|
<span class="badge badge-<?= $comment['status'] ?>"><?= $comment_status_label[$comment['status']] ?? $comment['status'] ?></span>
|
|
<span class="badge badge-category">
|
|
<i class="fa-solid <?= $comment_cat['faIcon'] ?>"></i>
|
|
<?= $comment_cat['label'] ?>
|
|
</span>
|
|
</div>
|
|
<i class="fa-solid fa-chevron-down collapse-icon"></i>
|
|
</div>
|
|
|
|
<!-- Expanded Detail -->
|
|
<div class="contribution-row-detail">
|
|
<div style="padding:12px 0;">
|
|
<!-- Comment Content -->
|
|
<div style="font-size:0.9rem;line-height:1.6;color:var(--color-text);margin-bottom:12px;">
|
|
<?= nl2br(htmlspecialchars($comment['content'])) ?>
|
|
</div>
|
|
<!-- Meta -->
|
|
<div class="detail-meta">
|
|
<span><i class="fa-solid fa-user"></i> <?= htmlspecialchars($comment['author_name']) ?></span>
|
|
<span><i class="fa-solid fa-calendar"></i> <?= date('d.m.Y, H:i', strtotime($comment['created_at'])) ?> Uhr</span>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Action Buttons -->
|
|
<div class="action-buttons">
|
|
<?php if ($comment['status'] !== 'approved'): ?>
|
|
<button class="btn btn-approve" onclick="changeCommentStatus(<?= $comment['comment_id'] ?>, 'approved')">
|
|
<i class="fa-solid fa-check"></i> Akzeptieren
|
|
</button>
|
|
<?php endif; ?>
|
|
<?php if ($comment['status'] !== 'rejected'): ?>
|
|
<button class="btn btn-reject" onclick="changeCommentStatus(<?= $comment['comment_id'] ?>, 'rejected')">
|
|
<i class="fa-solid fa-xmark"></i> Ablehnen
|
|
</button>
|
|
<?php endif; ?>
|
|
<?php if ($comment['status'] !== 'pending'): ?>
|
|
<button class="btn btn-reset" onclick="changeCommentStatus(<?= $comment['comment_id'] ?>, 'pending')">
|
|
<i class="fa-solid fa-rotate-left"></i> Zurücksetzen
|
|
</button>
|
|
<?php endif; ?>
|
|
<button class="btn btn-edit" onclick="editModComment(<?= $comment['comment_id'] ?>, '<?= htmlspecialchars(addslashes($comment['content']), ENT_QUOTES) ?>')">
|
|
<i class="fa-solid fa-pen"></i> Bearbeiten
|
|
</button>
|
|
<button class="btn btn-delete" onclick="deleteModComment(<?= $comment['comment_id'] ?>)">
|
|
<i class="fa-solid fa-trash"></i> Löschen
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<?php endforeach; ?>
|
|
<?php endif; ?>
|
|
</div>
|
|
</div>
|
|
|
|
|
|
<!-- ========================================================= -->
|
|
<!-- News Article Tab -->
|
|
<!-- ========================================================= -->
|
|
<div id="tab-news" class="page-tab-content" style="display:none;">
|
|
<div style="display:flex;justify-content:space-between;align-items:center;margin-bottom:20px;">
|
|
<h2 style="margin:0;border:none;padding:0;"><i class="fa-solid fa-newspaper"></i> Neuigkeiten</h2>
|
|
<button class="btn btn-approve" onclick="createNews()">
|
|
<i class="fa-solid fa-plus"></i> Nachricht hinzufügen
|
|
</button>
|
|
</div>
|
|
|
|
<?php if (empty($news_items)): ?>
|
|
<div class="empty-state">
|
|
<i class="fa-solid fa-newspaper" style="font-size:2rem;margin-bottom:8px;display:block;"></i>
|
|
Noch keine Neuigkeiten veröffentlicht.
|
|
</div>
|
|
<?php else: ?>
|
|
<?php foreach ($news_items as $news): ?>
|
|
<div class="contribution-row" data-id="<?= $news['news_id'] ?>">
|
|
<div class="contribution-row-header" onclick="toggleRow(this.parentElement)">
|
|
<div class="contribution-row-summary">
|
|
<span class="title"><?= htmlspecialchars($news['title']) ?></span>
|
|
<span style="font-size:0.8rem;color:#999;">
|
|
<?= date('d.m.Y', strtotime($news['published_at'])) ?>
|
|
· <?= htmlspecialchars($news['author_name']) ?>
|
|
</span>
|
|
</div>
|
|
<i class="fa-solid fa-chevron-down collapse-icon"></i>
|
|
</div>
|
|
<div class="contribution-row-detail">
|
|
<div style="padding:12px 0;font-size:0.9rem;line-height:1.6;color:#5a5a7a;">
|
|
<?= nl2br(htmlspecialchars($news['content'])) ?>
|
|
</div>
|
|
<div class="action-buttons">
|
|
<button class="btn btn-edit" onclick="editNews(<?= $news['news_id'] ?>, '<?= htmlspecialchars(addslashes($news['title']), ENT_QUOTES) ?>', '<?= htmlspecialchars(addslashes($news['content']), ENT_QUOTES) ?>', '<?= htmlspecialchars(addslashes($news['author_name']), ENT_QUOTES) ?>')">
|
|
<i class="fa-solid fa-pen"></i> Bearbeiten
|
|
</button>
|
|
<button class="btn btn-delete" onclick="deleteNews(<?= $news['news_id'] ?>)">
|
|
<i class="fa-solid fa-trash"></i> Löschen
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<?php endforeach; ?>
|
|
<?php endif; ?>
|
|
</div>
|
|
|
|
|
|
<!-- ========================================================= -->
|
|
<!-- Placeholder Tabs for future Features -->
|
|
<!-- ========================================================= -->
|
|
<div id="tab-stats" class="page-tab-content" style="display:none;">
|
|
<div class="placeholder-content">
|
|
<i class="fa-solid fa-chart-bar"></i>
|
|
<p>Statistiken und Analysen - geplant in zukünftiger Version.</p>
|
|
</div>
|
|
</div>
|
|
|
|
<div id="tab-users" class="page-tab-content" style="display:none;">
|
|
<div class="placeholder-content">
|
|
<i class="fa-solid fa-users"></i>
|
|
<p>Benutzerverwaltung - geplant in zukünftiger Version.</p>
|
|
</div>
|
|
</div>
|
|
|
|
</div>
|
|
|
|
|
|
<!-- ============================================================= -->
|
|
<!-- Loads JavaScript Dependencies -->
|
|
<!-- ============================================================= -->
|
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/leaflet/1.9.4/leaflet.min.js"></script>
|
|
|
|
<!-- ============================================================= -->
|
|
<!-- Admin Configuration passed to JavaScript -->
|
|
<!-- ============================================================= -->
|
|
<script>
|
|
const ADMIN_CONFIG = {
|
|
id: <?= $municipality['municipality_id'] ?>,
|
|
name: "<?= htmlspecialchars($municipality['name'], ENT_QUOTES) ?>",
|
|
slug: "<?= htmlspecialchars($municipality['slug'], ENT_QUOTES) ?>",
|
|
center: [<?= $municipality['center_lat'] ?>, <?= $municipality['center_lng'] ?>],
|
|
zoom: <?= $municipality['default_zoom'] ?>,
|
|
primaryColor: "<?= htmlspecialchars($municipality['primary_color'], ENT_QUOTES) ?>"
|
|
};
|
|
</script>
|
|
|
|
<!-- Application Logic -->
|
|
<script src="js/admin.js"></script>
|
|
|
|
</body>
|
|
</html>
|
|
|
|
|
|
<?php
|
|
// -----------------------------------------------------------------
|
|
// Login Page
|
|
// -----------------------------------------------------------------
|
|
function show_login_page($municipality, $error = null) {
|
|
?>
|
|
<!DOCTYPE html>
|
|
<html lang="de">
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
<title>Moderation - Anmeldung</title>
|
|
<link rel="icon" href="<?= htmlspecialchars($municipality['logo_path'] ?? 'assets/icon-municipality.png') ?>" type="image/png"> <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.1/css/all.min.css">
|
|
<link rel="stylesheet" href="styles.css">
|
|
<style>:root { --color-primary: <?= htmlspecialchars($municipality['primary_color']) ?>; }</style>
|
|
</head>
|
|
<body>
|
|
<div class="login-wrapper">
|
|
<div class="login-box">
|
|
<h1><i class="fa-solid fa-shield-halved"></i> Moderationsportal</h1>
|
|
<p>Bitte geben Sie das Moderationspasswort ein.</p>
|
|
<?php if ($error): ?>
|
|
<div class="login-error"><i class="fa-solid fa-triangle-exclamation"></i> <?= htmlspecialchars($error) ?></div>
|
|
<?php endif; ?>
|
|
<form method="POST" action="admin.php?page=login">
|
|
<input type="password" name="password" placeholder="Passwort" autofocus>
|
|
<button type="submit"><i class="fa-solid fa-right-to-bracket"></i> Anmelden</button>
|
|
</form>
|
|
<div class="back-link"><i class="fa fa-arrow-left"></i></i> <a href="index.php">Zurück zum Bürgerportal</a></div>
|
|
</div>
|
|
</div>
|
|
</body>
|
|
</html>
|
|
<?php
|
|
}
|
|
?>
|