675 lines
29 KiB
PHP
675 lines
29 KiB
PHP
<?php
|
|
// =====================================================================
|
|
// Moderation Page
|
|
// Lists Contributions for Review. Moderators can approve, reject,
|
|
// edit and delete Contributions. Includes Map Preview and Filtering.
|
|
//
|
|
// ToDo's:
|
|
// - Comment Moderation Tab
|
|
// - News Management Tab
|
|
// - User Management Tab
|
|
// - Analytics Tab
|
|
// =====================================================================
|
|
|
|
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' => 'lohne']); # TODO: add slug as env var
|
|
$municipality = $stmt->fetch();
|
|
|
|
// 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,
|
|
geom_type, status, likes_count, dislikes_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="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="admin.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="admin-header">
|
|
<h1><i class="fa-solid fa-shield-halved"></i> Moderationsportal <?= htmlspecialchars($municipality['name']) ?></h1>
|
|
<div class="admin-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 class="admin-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('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">
|
|
|
|
<!-- Statistics Cards -->
|
|
<div class="stats-grid">
|
|
<div class="stat-card">
|
|
<div class="stat-number"><?= $counts['total'] ?></div>
|
|
<div class="stat-label">Alle</div>
|
|
</div>
|
|
<div class="stat-card">
|
|
<div class="stat-number"><?= $counts['pending'] ?></div>
|
|
<div class="stat-label">Ausstehend</div>
|
|
</div>
|
|
<div class="stat-card">
|
|
<div class="stat-number"><?= $counts['approved'] ?></div>
|
|
<div class="stat-label">Akzeptiert</div>
|
|
</div>
|
|
<div class="stat-card">
|
|
<div class="stat-number"><?= $counts['rejected'] ?></div>
|
|
<div class="stat-label">Abgelehnt</div>
|
|
</div>
|
|
|
|
</div>
|
|
|
|
<!-- 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-<?= $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 Preview -->
|
|
<div class="detail-map" id="map-<?= $item['contribution_id'] ?>"
|
|
data-contribution-id="<?= $item['contribution_id'] ?>">
|
|
</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 <?= $cat['faIcon'] ?>" style="color:<?= $cat['color'] ?>;"></i>
|
|
<?= $cat['label'] ?>
|
|
</span>
|
|
<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'] ?>
|
|
</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-edit" onclick="changeStatus(<?= $item['contribution_id'] ?>, 'pending')" style="background:#f57f17;">
|
|
<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>
|
|
|
|
|
|
<!-- ========================================================= -->
|
|
<!-- Placeholder Tabs for future Features -->
|
|
<!-- ========================================================= -->
|
|
<div id="tab-news" class="page-tab-content" style="display:none;">
|
|
<div class="placeholder-content">
|
|
<i class="fa-solid fa-newspaper"></i>
|
|
<p>Neuigkeiten verwalten - geplant in zukünftiger Version.</p>
|
|
</div>
|
|
</div>
|
|
|
|
<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>
|
|
|
|
|
|
<!-- ============================================================= -->
|
|
<!-- JavaScript: Leaflet, Interactions, API Calls -->
|
|
<!-- ============================================================= -->
|
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/leaflet/1.9.4/leaflet.min.js"></script>
|
|
|
|
<script>
|
|
// Municipality Configuration for Map Previews
|
|
const MUNICIPALITY_CENTER = [<?= $municipality['center_lat'] ?>, <?= $municipality['center_lng'] ?>];
|
|
const MUNICIPALITY_ID = <?= $municipality['municipality_id'] ?>;
|
|
const API_URL = 'api/contributions.php';
|
|
const PRIMARY_COLOR = '<?= htmlspecialchars($municipality['primary_color']) ?>';
|
|
|
|
// Current Status Filter
|
|
let currentFilter = 'all';
|
|
|
|
|
|
// =============================================================
|
|
// Page Tab Navigation
|
|
// =============================================================
|
|
|
|
function showPageTab(tabName) {
|
|
// Hides all Tab Contents
|
|
document.querySelectorAll('.page-tab-content').forEach(function (el) {
|
|
el.style.display = 'none';
|
|
});
|
|
|
|
// Deactivates all Tab Buttons
|
|
document.querySelectorAll('.page-tab').forEach(function (el) {
|
|
el.classList.remove('active');
|
|
});
|
|
|
|
// Shows selected Tab and activates Button
|
|
document.getElementById('tab-' + tabName).style.display = 'block';
|
|
event.currentTarget.classList.add('active');
|
|
}
|
|
|
|
|
|
// =============================================================
|
|
// Collapsible Rows
|
|
// =============================================================
|
|
|
|
function toggleRow(row) {
|
|
const wasOpen = row.classList.contains('open');
|
|
|
|
// Closes all open Rows
|
|
document.querySelectorAll('.contribution-row.open').forEach(function (el) {
|
|
el.classList.remove('open');
|
|
});
|
|
|
|
// Toggles clicked Row
|
|
if (!wasOpen) {
|
|
row.classList.add('open');
|
|
|
|
// Loads Map Preview if not already loaded
|
|
const mapDiv = row.querySelector('.detail-map');
|
|
if (mapDiv && !mapDiv.dataset.loaded) {
|
|
loadMapPreview(mapDiv);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
// =============================================================
|
|
// Map Preview (Leaflet Mini Map per Contribution)
|
|
// =============================================================
|
|
|
|
function loadMapPreview(mapDiv) {
|
|
const contributionId = mapDiv.dataset.contributionId;
|
|
|
|
// Fetches all Contributions to find the Geometry
|
|
const formData = new FormData();
|
|
formData.append('action', 'read');
|
|
formData.append('municipality_id', MUNICIPALITY_ID);
|
|
formData.append('status', 'all');
|
|
|
|
fetch(API_URL, { method: 'POST', body: formData })
|
|
.then(function (r) { return r.json(); })
|
|
.then(function (data) {
|
|
if (!data.features) return;
|
|
|
|
// Finds specific Contribution
|
|
const feature = data.features.find(function (f) {
|
|
return f.properties.contribution_id == contributionId;
|
|
});
|
|
|
|
if (!feature) {
|
|
mapDiv.innerHTML = '<div style="padding:20px;color:#999;text-align:center;font-size:0.8rem;">Geometrie nicht gefunden.</div>';
|
|
return;
|
|
}
|
|
|
|
// Creates Leaflet Mini Map
|
|
const miniMap = L.map(mapDiv, {
|
|
zoomControl: false,
|
|
attributionControl: false,
|
|
dragging: true,
|
|
scrollWheelZoom: false
|
|
});
|
|
|
|
L.tileLayer('https://{s}.basemaps.cartocdn.com/light_all/{z}/{x}/{y}{r}.png', {
|
|
maxZoom: 20
|
|
}).addTo(miniMap);
|
|
|
|
// Adds Geometry to Mini Map
|
|
const geojsonLayer = L.geoJSON(feature, {
|
|
style: { color: PRIMARY_COLOR, weight: 3, fillOpacity: 0.2 },
|
|
pointToLayer: function (f, latlng) {
|
|
return L.circleMarker(latlng, {
|
|
radius: 8, color: '#ffffff', weight: 2,
|
|
fillColor: PRIMARY_COLOR, fillOpacity: 0.9
|
|
});
|
|
}
|
|
}).addTo(miniMap);
|
|
|
|
// Fits Map to Geometry Bounds
|
|
const bounds = geojsonLayer.getBounds();
|
|
if (bounds.isValid()) {
|
|
miniMap.fitBounds(bounds, { padding: [25, 25], maxZoom: 17 });
|
|
} else {
|
|
miniMap.setView(MUNICIPALITY_CENTER, 15);
|
|
}
|
|
|
|
mapDiv.dataset.loaded = 'true';
|
|
})
|
|
.catch(function () {
|
|
mapDiv.innerHTML = '<div style="padding:20px;color:#999;text-align:center;font-size:0.8rem;">Karte nicht verfügbar.</div>';
|
|
});
|
|
}
|
|
|
|
|
|
// =============================================================
|
|
// Status Filter
|
|
// =============================================================
|
|
|
|
function filterByStatus(status, tabButton) {
|
|
currentFilter = status;
|
|
|
|
// Updates active Tab
|
|
document.querySelectorAll('.filter-tab').forEach(function (el) {
|
|
el.classList.remove('active');
|
|
});
|
|
tabButton.classList.add('active');
|
|
|
|
// Shows/Hides Contribution Rows
|
|
let visibleCount = 0;
|
|
document.querySelectorAll('.contribution-row').forEach(function (row) {
|
|
if (status === 'all' || row.dataset.status === status) {
|
|
row.style.display = '';
|
|
visibleCount++;
|
|
} else {
|
|
row.style.display = 'none';
|
|
}
|
|
});
|
|
|
|
// Updates Count Display
|
|
document.getElementById('visible-count').textContent = visibleCount + ' Beiträge';
|
|
}
|
|
|
|
|
|
// =============================================================
|
|
// Sort Contributions
|
|
// =============================================================
|
|
|
|
function sortContributions(sortBy) {
|
|
const container = document.getElementById('contributions-container');
|
|
const rows = Array.from(container.querySelectorAll('.contribution-row'));
|
|
|
|
rows.sort(function (a, b) {
|
|
if (sortBy === 'date-desc') {
|
|
return new Date(b.dataset.date) - new Date(a.dataset.date);
|
|
} else if (sortBy === 'date-asc') {
|
|
return new Date(a.dataset.date) - new Date(b.dataset.date);
|
|
} else if (sortBy === 'category') {
|
|
return a.dataset.category.localeCompare(b.dataset.category);
|
|
}
|
|
return 0;
|
|
});
|
|
|
|
// Reappends sorted Rows
|
|
rows.forEach(function (row) {
|
|
container.appendChild(row);
|
|
});
|
|
}
|
|
|
|
|
|
// =============================================================
|
|
// API Helper
|
|
// =============================================================
|
|
|
|
function apiCall(data) {
|
|
const formData = new FormData();
|
|
for (const key in data) {
|
|
formData.append(key, data[key]);
|
|
}
|
|
return fetch(API_URL, { method: 'POST', body: formData })
|
|
.then(function (r) { return r.json(); });
|
|
}
|
|
|
|
|
|
// =============================================================
|
|
// Change Contribution Status
|
|
// =============================================================
|
|
|
|
function changeStatus(contributionId, newStatus) {
|
|
const labels = { approved: 'freigeben', rejected: 'ablehnen', pending: 'zurücksetzen' };
|
|
|
|
Swal.fire({
|
|
title: 'Beitrag ' + labels[newStatus] + '?',
|
|
showCancelButton: true,
|
|
confirmButtonText: 'Ja',
|
|
cancelButtonText: 'Abbrechen',
|
|
confirmButtonColor: PRIMARY_COLOR
|
|
}).then(function (result) {
|
|
if (!result.isConfirmed) return;
|
|
|
|
apiCall({
|
|
action: 'update',
|
|
contribution_id: contributionId,
|
|
status: newStatus
|
|
}).then(function (response) {
|
|
if (response.error) {
|
|
Swal.fire('Fehler', response.error, 'error');
|
|
return;
|
|
}
|
|
// Reloads Page to reflect Changes
|
|
location.reload();
|
|
});
|
|
});
|
|
}
|
|
|
|
|
|
// =============================================================
|
|
// Edit Contribution (Title and Description)
|
|
// =============================================================
|
|
|
|
function editContribution(contributionId, currentTitle, currentDescription) {
|
|
Swal.fire({
|
|
title: 'Beitrag bearbeiten',
|
|
html:
|
|
'<div style="text-align:left;">' +
|
|
'<label style="font-weight:600;font-size:0.85rem;">Titel</label>' +
|
|
'<input id="swal-title" class="swal2-input" value="' + currentTitle + '">' +
|
|
'<label style="font-weight:600;font-size:0.85rem;">Beschreibung</label>' +
|
|
'<textarea id="swal-description" class="swal2-textarea">' + currentDescription + '</textarea>' +
|
|
'</div>',
|
|
showCancelButton: true,
|
|
confirmButtonText: 'Speichern',
|
|
cancelButtonText: 'Abbrechen',
|
|
confirmButtonColor: PRIMARY_COLOR,
|
|
preConfirm: function () {
|
|
return {
|
|
title: document.getElementById('swal-title').value.trim(),
|
|
description: document.getElementById('swal-description').value.trim()
|
|
};
|
|
}
|
|
}).then(function (result) {
|
|
if (!result.isConfirmed) return;
|
|
|
|
apiCall({
|
|
action: 'update',
|
|
contribution_id: contributionId,
|
|
title: result.value.title,
|
|
description: result.value.description
|
|
}).then(function (response) {
|
|
if (response.error) {
|
|
Swal.fire('Fehler', response.error, 'error');
|
|
return;
|
|
}
|
|
Swal.fire('Gespeichert!', 'Beitrag wurde aktualisiert.', 'success')
|
|
.then(function () { location.reload(); });
|
|
});
|
|
});
|
|
}
|
|
|
|
|
|
// =============================================================
|
|
// Delete Contribution
|
|
// =============================================================
|
|
|
|
function deleteContribution(contributionId) {
|
|
Swal.fire({
|
|
title: 'Beitrag löschen?',
|
|
text: 'Diese Aktion kann nicht rückgängig gemacht werden.',
|
|
icon: 'warning',
|
|
showCancelButton: true,
|
|
confirmButtonText: 'Beitrag löschen',
|
|
cancelButtonText: 'Abbrechen',
|
|
confirmButtonColor: '#c62828'
|
|
}).then(function (result) {
|
|
if (!result.isConfirmed) return;
|
|
|
|
apiCall({
|
|
action: 'delete',
|
|
contribution_id: contributionId
|
|
}).then(function (response) {
|
|
if (response.error) {
|
|
Swal.fire('Fehler', response.error, 'error');
|
|
return;
|
|
}
|
|
Swal.fire('Gelöscht!', 'Beitrag wurde gelöscht.', 'success')
|
|
.then(function () { location.reload(); });
|
|
});
|
|
});
|
|
}
|
|
</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="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="admin.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"><?= 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
|
|
}
|
|
?>
|