File: /home/nmel17aacb/domains/argentaclassic.be/public_html/admin/index.php
<?php
declare(strict_types=1);
session_set_cookie_params([
'lifetime' => 0,
'path' => '/',
'secure' => (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off'),
'httponly' => true,
'samesite' => 'Lax',
]);
session_start();
require_once __DIR__ . '/../inc/db.php';
mysqli_report(MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT);
$db->set_charset('utf8');
$sections = [
'nieuws' => [
'label' => 'Nieuws',
'table' => 'nieuws',
'order' => 'datum DESC',
'upload' => 'nieuws',
'fields' => [
'titel' => ['label' => 'Titel', 'type' => 'text', 'required' => true],
'nieuws' => ['label' => 'Bericht', 'type' => 'textarea', 'required' => true, 'full' => true],
'link' => ['label' => 'Link', 'type' => 'url'],
'datum' => ['label' => 'Datum', 'type' => 'datetime'],
'actief' => ['label' => 'Actief', 'type' => 'checkbox'],
'image' => ['label' => 'Afbeelding', 'type' => 'image'],
],
'list' => ['titel', 'datum', 'actief', 'image'],
],
'sponsor' => [
'label' => 'Sponsors',
'table' => 'sponsor',
'order' => 'volgorde ASC, titel ASC',
'upload' => 'sponsors',
'fields' => [
'titel' => ['label' => 'Naam', 'type' => 'text'],
'link' => ['label' => 'Link', 'type' => 'url'],
'volgorde' => ['label' => 'Volgorde', 'type' => 'number'],
'actief' => ['label' => 'Actief', 'type' => 'checkbox'],
'image' => ['label' => 'Logo', 'type' => 'image'],
],
'list' => ['titel', 'volgorde', 'actief', 'image'],
],
'federaties' => [
'label' => 'Federaties',
'table' => 'federaties',
'order' => 'id DESC',
'upload' => 'federaties',
'fields' => [
'link' => ['label' => 'Link', 'type' => 'url'],
'actief' => ['label' => 'Actief', 'type' => 'checkbox'],
'image' => ['label' => 'Logo', 'type' => 'image'],
],
'list' => ['link', 'actief', 'image'],
],
'agenda' => [
'label' => 'Agenda',
'table' => 'agenda',
'order' => 'datum DESC',
'fields' => [
'titel' => ['label' => 'Titel', 'type' => 'text', 'required' => true],
'datum' => ['label' => 'Datum', 'type' => 'datetime'],
'info' => ['label' => 'Info', 'type' => 'textarea', 'full' => true],
'locatie' => ['label' => 'Locatie', 'type' => 'text'],
'start_tijd' => ['label' => 'Starttijd', 'type' => 'text'],
'stop_tijd' => ['label' => 'Eindtijd', 'type' => 'text'],
'afstand' => ['label' => 'Afstand', 'type' => 'text'],
'link' => ['label' => 'Link', 'type' => 'url'],
'mail' => ['label' => 'E-mail', 'type' => 'email'],
'actief' => ['label' => 'Actief', 'type' => 'checkbox'],
],
'list' => ['titel', 'datum', 'locatie', 'actief'],
],
'bestuur' => [
'label' => 'Bestuur',
'table' => 'bestuur',
'order' => 'volgorde ASC',
'upload' => 'bestuur',
'fields' => [
'titel' => ['label' => 'Titel', 'type' => 'text'],
'naam' => ['label' => 'Naam', 'type' => 'text'],
'functie' => ['label' => 'Functie/tekst', 'type' => 'textarea', 'full' => true],
'gsm' => ['label' => 'GSM', 'type' => 'text'],
'telefoon' => ['label' => 'Telefoon', 'type' => 'text'],
'mail' => ['label' => 'E-mail', 'type' => 'email'],
'volgorde' => ['label' => 'Volgorde', 'type' => 'number'],
'actief' => ['label' => 'Actief', 'type' => 'checkbox'],
'image' => ['label' => 'Foto', 'type' => 'image'],
],
'list' => ['titel', 'naam', 'volgorde', 'actief', 'image'],
],
'neven' => [
'label' => 'Nevenactiviteiten',
'table' => 'neven',
'order' => 'id DESC',
'upload' => 'sponsors',
'fields' => [
'regio' => ['label' => 'Regio/categorie', 'type' => 'text'],
'titel' => ['label' => 'Titel/tekst', 'type' => 'textarea', 'full' => true],
'link' => ['label' => 'Link', 'type' => 'url'],
'actief' => ['label' => 'Actief', 'type' => 'checkbox'],
'image' => ['label' => 'Afbeelding', 'type' => 'image'],
],
'list' => ['regio', 'titel', 'actief', 'image'],
],
'links' => [
'label' => 'Links',
'table' => 'links',
'order' => 'titel ASC',
'fields' => [
'titel' => ['label' => 'Titel', 'type' => 'text', 'required' => true],
'link' => ['label' => 'Link', 'type' => 'url', 'required' => true],
'actief' => ['label' => 'Actief', 'type' => 'checkbox'],
],
'list' => ['titel', 'link', 'actief'],
],
'pers' => [
'label' => 'Pers',
'table' => 'pers',
'order' => 'datum DESC',
'upload' => 'pers',
'fields' => [
'titel' => ['label' => 'Titel', 'type' => 'text', 'required' => true],
'bron' => ['label' => 'Bron', 'type' => 'text'],
'tekst' => ['label' => 'Tekst', 'type' => 'textarea', 'full' => true],
'link' => ['label' => 'Link', 'type' => 'url'],
'datum' => ['label' => 'Datum', 'type' => 'datetime'],
'actief' => ['label' => 'Actief', 'type' => 'checkbox'],
'image' => ['label' => 'Afbeelding', 'type' => 'image'],
],
'list' => ['titel', 'bron', 'datum', 'actief', 'image'],
],
];
function h(?string $value): string
{
return htmlspecialchars((string) $value, ENT_QUOTES, 'UTF-8');
}
function redirect_to(string $url): void
{
header('Location: ' . $url);
exit;
}
function bind_stmt_params(mysqli_stmt $stmt, string $types, array $params): void
{
$refs = [];
foreach ($params as $key => $value) {
$refs[$key] = $value;
}
$bind = [$types];
foreach ($refs as $key => &$value) {
$bind[] = &$value;
}
$stmt->bind_param(...$bind);
}
function is_logged_in(): bool
{
return isset($_SESSION['admin_user_id']);
}
function find_user(mysqli $db, string $login): ?array
{
$stmt = $db->prepare('SELECT user_id, user_name, user_email, user_password FROM users WHERE user_email = ? OR user_name = ? LIMIT 1');
bind_stmt_params($stmt, 'ss', [$login, $login]);
$stmt->execute();
$result = $stmt->get_result();
$user = $result->fetch_assoc();
return $user ?: null;
}
function fetch_row(mysqli $db, array $section, int $id): ?array
{
$sql = 'SELECT * FROM `' . $section['table'] . '` WHERE id = ? LIMIT 1';
if ($section['table'] === 'users') {
$sql = 'SELECT * FROM `users` WHERE user_id = ? LIMIT 1';
}
$stmt = $db->prepare($sql);
bind_stmt_params($stmt, 'i', [$id]);
$stmt->execute();
$row = $stmt->get_result()->fetch_assoc();
return $row ?: null;
}
function upload_image(string $folder, string $field, string $current = ''): string
{
if (!isset($_FILES[$field]) || $_FILES[$field]['error'] === UPLOAD_ERR_NO_FILE) {
return $current;
}
if ($_FILES[$field]['error'] !== UPLOAD_ERR_OK) {
throw new RuntimeException('Upload mislukt. Probeer opnieuw met een kleinere of geldige afbeelding.');
}
$tmp = $_FILES[$field]['tmp_name'];
$info = @getimagesize($tmp);
if (!$info) {
throw new RuntimeException('Dit bestand lijkt geen geldige afbeelding.');
}
$allowed = [
IMAGETYPE_JPEG => 'jpg',
IMAGETYPE_PNG => 'png',
IMAGETYPE_GIF => 'gif',
IMAGETYPE_WEBP => 'webp',
IMAGETYPE_BMP => 'bmp',
];
if (!isset($allowed[$info[2]])) {
throw new RuntimeException('Gebruik JPG, PNG, GIF, WEBP of BMP.');
}
$original = pathinfo($_FILES[$field]['name'], PATHINFO_FILENAME);
$base = preg_replace('/[^a-zA-Z0-9_-]+/', '-', strtolower($original));
$base = trim($base, '-') ?: 'afbeelding';
$filename = $base . '-' . date('YmdHis') . '.' . $allowed[$info[2]];
$targetDir = dirname(__DIR__) . '/files/' . $folder;
if (!is_dir($targetDir)) {
mkdir($targetDir, 0775, true);
}
$target = $targetDir . '/' . $filename;
if (!move_uploaded_file($tmp, $target)) {
throw new RuntimeException('De afbeelding kon niet worden opgeslagen.');
}
return $filename;
}
function normalize_value(array $field, string $name, array $row = []): string
{
if (($field['type'] ?? 'text') === 'checkbox') {
return isset($_POST[$name]) ? '1' : '0';
}
if (($field['type'] ?? 'text') === 'datetime') {
$value = trim((string) ($_POST[$name] ?? ''));
return $value === '' ? date('Y-m-d H:i:s') : str_replace('T', ' ', $value) . (strlen($value) === 16 ? ':00' : '');
}
if (($field['type'] ?? 'text') === 'image') {
return (string) ($row[$name] ?? '');
}
return trim((string) ($_POST[$name] ?? ''));
}
if (!is_logged_in() && $_SERVER['REQUEST_METHOD'] === 'POST' && ($_POST['action'] ?? '') === 'login') {
$login = trim((string) ($_POST['login'] ?? ''));
$password = (string) ($_POST['password'] ?? '');
$user = find_user($db, $login);
if ($user && password_verify($password, $user['user_password'])) {
session_regenerate_id(true);
$_SESSION['admin_user_id'] = (int) $user['user_id'];
$_SESSION['admin_user_name'] = $user['user_name'];
redirect_to('index.php');
}
$loginError = 'Ongeldige gebruikersnaam of wachtwoord.';
}
if (!is_logged_in()) {
?>
<!doctype html>
<html lang="nl">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="robots" content="noindex,nofollow">
<title>Admin login - Argenta Classic</title>
<link rel="stylesheet" href="assets/admin.css">
</head>
<body class="login-page">
<main class="panel login-card">
<div class="panel-header">
<h1>Admin</h1>
</div>
<div class="panel-body">
<?php if (!empty($loginError)): ?>
<div class="message error"><?= h($loginError) ?></div>
<?php endif; ?>
<form method="post">
<input type="hidden" name="action" value="login">
<div>
<label for="login">Gebruiker of e-mail</label>
<input id="login" name="login" type="text" autocomplete="username" required>
</div>
<div style="margin-top: 14px;">
<label for="password">Wachtwoord</label>
<input id="password" name="password" type="password" autocomplete="current-password" required>
</div>
<div class="actions">
<button class="btn btn-primary" type="submit">Inloggen</button>
</div>
</form>
</div>
</main>
</body>
</html>
<?php
exit;
}
$sectionKey = $_GET['section'] ?? 'nieuws';
if (!isset($sections[$sectionKey])) {
$sectionKey = 'nieuws';
}
$section = $sections[$sectionKey];
$action = $_GET['action'] ?? 'list';
$id = max(0, (int) ($_GET['id'] ?? 0));
$message = $_GET['message'] ?? '';
$error = '';
$editRow = $id ? fetch_row($db, $section, $id) : null;
if ($_SERVER['REQUEST_METHOD'] === 'POST' && ($_POST['action'] ?? '') === 'save') {
try {
$id = max(0, (int) ($_POST['id'] ?? 0));
$currentRow = $id ? fetch_row($db, $section, $id) : [];
$values = [];
foreach ($section['fields'] as $name => $field) {
$values[$name] = normalize_value($field, $name, $currentRow ?: []);
if (($field['type'] ?? '') === 'image') {
$values[$name] = upload_image($section['upload'] ?? '', $name, $values[$name]);
}
if (!empty($field['required']) && $values[$name] === '') {
throw new RuntimeException($field['label'] . ' is verplicht.');
}
}
if ($id > 0) {
$set = implode(', ', array_map(fn($name) => '`' . $name . '` = ?', array_keys($values)));
$sql = 'UPDATE `' . $section['table'] . '` SET ' . $set . ' WHERE id = ?';
$stmt = $db->prepare($sql);
$types = str_repeat('s', count($values)) . 'i';
$params = array_values($values);
$params[] = $id;
bind_stmt_params($stmt, $types, $params);
$stmt->execute();
redirect_to('index.php?section=' . urlencode($sectionKey) . '&message=updated');
}
$columns = implode(', ', array_map(fn($name) => '`' . $name . '`', array_keys($values)));
$placeholders = implode(', ', array_fill(0, count($values), '?'));
$sql = 'INSERT INTO `' . $section['table'] . '` (' . $columns . ') VALUES (' . $placeholders . ')';
$stmt = $db->prepare($sql);
$types = str_repeat('s', count($values));
$params = array_values($values);
bind_stmt_params($stmt, $types, $params);
$stmt->execute();
redirect_to('index.php?section=' . urlencode($sectionKey) . '&message=created');
} catch (Throwable $e) {
$error = $e->getMessage();
$action = $id > 0 ? 'edit' : 'new';
$editRow = $_POST;
}
}
if ($action === 'delete' && $id > 0) {
$stmt = $db->prepare('DELETE FROM `' . $section['table'] . '` WHERE id = ? LIMIT 1');
bind_stmt_params($stmt, 'i', [$id]);
$stmt->execute();
redirect_to('index.php?section=' . urlencode($sectionKey) . '&message=deleted');
}
$rows = [];
if ($action === 'list') {
$result = $db->query('SELECT * FROM `' . $section['table'] . '` ORDER BY ' . $section['order'] . ' LIMIT 250');
while ($row = $result->fetch_assoc()) {
$rows[] = $row;
}
}
$messages = [
'created' => 'Item toegevoegd.',
'updated' => 'Wijzigingen opgeslagen.',
'deleted' => 'Item verwijderd.',
];
function render_value(array $row, string $name): string
{
$value = (string) ($row[$name] ?? '');
if ($name === 'actief') {
return ((int) $value === 1) ? '<span class="status on">Actief</span>' : '<span class="status off">Uit</span>';
}
if ($name === 'datum' && $value !== '') {
return h(date('d/m/Y H:i', strtotime($value)));
}
$plain = strip_tags($value);
return h(strlen($plain) > 90 ? substr($plain, 0, 90) . '...' : $plain);
}
function image_path(string $sectionKey, array $section, string $image): string
{
if ($image === '' || empty($section['upload'])) {
return '';
}
return '../files/' . $section['upload'] . '/' . $image;
}
?>
<!doctype html>
<html lang="nl">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="robots" content="noindex,nofollow">
<title>Admin - Argenta Classic</title>
<link rel="stylesheet" href="assets/admin.css">
</head>
<body>
<div class="admin-shell">
<aside class="admin-sidebar">
<div class="admin-brand">
<img src="../resources/logoac2e.png" alt="">
<div>
<strong>Argenta Classic</strong>
<span>Beheer</span>
</div>
</div>
<nav class="admin-nav">
<?php foreach ($sections as $key => $item): ?>
<a class="<?= $key === $sectionKey ? 'active' : '' ?>" href="?section=<?= h($key) ?>"><?= h($item['label']) ?></a>
<?php endforeach; ?>
</nav>
</aside>
<main class="admin-content">
<div class="admin-topbar">
<div class="admin-title">
<h1><?= h($section['label']) ?></h1>
<p>Ingelogd als <?= h($_SESSION['admin_user_name'] ?? 'admin') ?></p>
</div>
<div class="actions">
<a class="btn btn-secondary" href="../" target="_blank">Website bekijken</a>
<a class="btn btn-secondary" href="logout.php">Uitloggen</a>
</div>
</div>
<?php if ($message && isset($messages[$message])): ?>
<div class="message success"><?= h($messages[$message]) ?></div>
<?php endif; ?>
<?php if ($error): ?>
<div class="message error"><?= h($error) ?></div>
<?php endif; ?>
<?php if ($action === 'new' || $action === 'edit'): ?>
<?php $isEdit = $action === 'edit' && $editRow; ?>
<section class="panel">
<div class="panel-header">
<h2><?= $isEdit ? 'Bewerken' : 'Nieuw item' ?></h2>
<a class="btn btn-secondary" href="?section=<?= h($sectionKey) ?>">Terug</a>
</div>
<div class="panel-body">
<form method="post" enctype="multipart/form-data">
<input type="hidden" name="action" value="save">
<input type="hidden" name="id" value="<?= h((string) ($editRow['id'] ?? 0)) ?>">
<div class="admin-grid">
<?php foreach ($section['fields'] as $name => $field): ?>
<?php
$type = $field['type'] ?? 'text';
$value = (string) ($editRow[$name] ?? '');
if ($type === 'datetime' && $value !== '') {
$value = date('Y-m-d\TH:i', strtotime($value));
}
?>
<div class="<?= !empty($field['full']) || $type === 'image' ? 'field-full' : '' ?>">
<?php if ($type === 'checkbox'): ?>
<div class="check-field">
<input id="<?= h($name) ?>" name="<?= h($name) ?>" type="checkbox" value="1" <?= (int) ($editRow[$name] ?? 1) === 1 ? 'checked' : '' ?>>
<label for="<?= h($name) ?>"><?= h($field['label']) ?></label>
</div>
<?php elseif ($type === 'textarea'): ?>
<label for="<?= h($name) ?>"><?= h($field['label']) ?></label>
<textarea id="<?= h($name) ?>" name="<?= h($name) ?>" <?= !empty($field['required']) ? 'required' : '' ?>><?= h($value) ?></textarea>
<div class="help">HTML zoals <br>, <b> en links mag blijven staan.</div>
<?php elseif ($type === 'image'): ?>
<label for="<?= h($name) ?>"><?= h($field['label']) ?></label>
<?php $img = image_path($sectionKey, $section, (string) ($editRow[$name] ?? '')); ?>
<?php if ($img): ?>
<div class="thumb" style="margin-bottom: 10px;"><img src="<?= h($img) ?>" alt=""></div>
<div class="help">Huidig bestand: <?= h((string) $editRow[$name]) ?></div>
<?php endif; ?>
<input id="<?= h($name) ?>" name="<?= h($name) ?>" type="file" accept="image/*">
<?php else: ?>
<label for="<?= h($name) ?>"><?= h($field['label']) ?></label>
<input id="<?= h($name) ?>" name="<?= h($name) ?>" type="<?= h($type) ?>" value="<?= h($value) ?>" <?= !empty($field['required']) ? 'required' : '' ?>>
<?php endif; ?>
</div>
<?php endforeach; ?>
</div>
<div class="actions">
<button class="btn btn-primary" type="submit">Opslaan</button>
<a class="btn btn-secondary" href="?section=<?= h($sectionKey) ?>">Annuleren</a>
</div>
</form>
</div>
</section>
<?php else: ?>
<section class="panel">
<div class="panel-header">
<h2>Overzicht</h2>
<a class="btn btn-primary" href="?section=<?= h($sectionKey) ?>&action=new">Nieuw item</a>
</div>
<div class="table-wrap">
<table>
<thead>
<tr>
<?php foreach ($section['list'] as $column): ?>
<th><?= h($column === 'image' ? 'Afbeelding' : ucfirst($column)) ?></th>
<?php endforeach; ?>
<th>Acties</th>
</tr>
</thead>
<tbody>
<?php if (!$rows): ?>
<tr><td colspan="<?= count($section['list']) + 1 ?>">Nog geen items.</td></tr>
<?php endif; ?>
<?php foreach ($rows as $row): ?>
<tr>
<?php foreach ($section['list'] as $column): ?>
<td>
<?php if ($column === 'image'): ?>
<?php $img = image_path($sectionKey, $section, (string) ($row[$column] ?? '')); ?>
<div class="thumb">
<?php if ($img): ?><img src="<?= h($img) ?>" alt=""><?php endif; ?>
</div>
<?php else: ?>
<?= render_value($row, $column) ?>
<?php endif; ?>
</td>
<?php endforeach; ?>
<td>
<div class="actions" style="margin: 0;">
<a class="btn btn-secondary" href="?section=<?= h($sectionKey) ?>&action=edit&id=<?= (int) $row['id'] ?>">Bewerken</a>
<a class="btn btn-danger" href="?section=<?= h($sectionKey) ?>&action=delete&id=<?= (int) $row['id'] ?>" onclick="return confirm('Dit item verwijderen?')">Verwijderen</a>
</div>
</td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
</div>
</section>
<?php endif; ?>
</main>
</div>
</body>
</html>