<?php
declare(strict_types=1);
require_once 'includes/config.php';
require_once 'includes/functions.php';
$user = require_login();
if (!is_admin($user)) {
flash('error', 'Access denied.');
redirect('dashboard.php');
}
/* ── Handle POST actions ──────────────────────────────────────── */
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
if (!verify_csrf()) {
flash('error', 'Invalid request token.');
redirect('admin.php');
}
$action = $_POST['action'] ?? '';
if ($action === 'save_footer') {
$text = trim($_POST['footer_text'] ?? '');
if ($text === '') $text = DEFAULT_FOOTER_TEXT;
$cfg = get_site_config();
$cfg['footer_text'] = $text;
save_site_config($cfg);
flash('success', 'Footer text saved.');
redirect('admin.php');
}
if ($action === 'reset_footer') {
$cfg = get_site_config();
$cfg['footer_text'] = DEFAULT_FOOTER_TEXT;
save_site_config($cfg);
flash('success', 'Footer text reset to default.');
redirect('admin.php');
}
if ($action === 'upload_og_logo') {
init_uploads();
$file = $_FILES['og_logo'] ?? null;
if (!$file || $file['error'] === UPLOAD_ERR_NO_FILE) {
flash('error', 'No file selected.');
} else {
$filename = save_uploaded_image($file, UPLOADS_DIR, 'og_logo');
if ($filename === false) {
flash('error', 'Upload failed. Use JPG, PNG, GIF or WebP, max 2 MB.');
} else {
$cfg = get_site_config();
$cfg['og_logo'] = $filename;
save_site_config($cfg);
flash('success', 'OG logo updated.');
}
}
redirect('admin.php');
}
if ($action === 'remove_og_logo') {
delete_upload(UPLOADS_DIR, 'og_logo');
$cfg = get_site_config();
unset($cfg['og_logo']);
save_site_config($cfg);
flash('success', 'OG logo removed.');
redirect('admin.php');
}
if ($action === 'toggle_admin') {
$target = strtolower(trim($_POST['target_user'] ?? ''));
if ($target === $user['username']) {
flash('error', 'You cannot remove your own admin status.');
} elseif ($target !== '') {
$tfile = USERS_DIR . '/' . $target . '/profile.json';
$tprofile = read_json($tfile);
if ($tprofile) {
$tprofile['admin'] = !($tprofile['admin'] ?? false);
save_json($tfile, $tprofile);
$state = $tprofile['admin'] ? 'granted' : 'revoked';
flash('success', "Admin access $state for @$target.");
}
}
redirect('admin.php');
}
}
/* ── Load data ────────────────────────────────────────────────── */
$cfg = get_site_config();
$users = read_json(USERS_INDEX);
ksort($users);
$page_title = 'Site Settings';
require_once 'includes/header.php';
?>
<div class="page-wrap" style="max-width:700px; margin-top:12px; margin-bottom:16px;">
<!-- ── OG Logo ────────────────────────────────────────── -->
<div class="window">
<div class="window-title"><span>📷 OG / Share Logo</span></div>
<div class="window-body">
<p style="font-size:12px; color:var(--chrome-darker); margin-bottom:10px;">
This image appears when TaskGram links are shared on social media (Open Graph).
Recommended: 1200×630 px, JPG or PNG.
</p>
<?php $og_logo_url = get_og_logo_url(); ?>
<?php if ($og_logo_url !== ''): ?>
<div style="margin-bottom:10px; display:flex; align-items:flex-start; gap:12px;">
<img src="<?= h($og_logo_url) ?>" alt="OG Logo"
style="max-width:200px; max-height:100px; object-fit:contain;
border-top:2px solid var(--chrome-light);
border-left:2px solid var(--chrome-light);
border-bottom:2px solid var(--chrome-darker);
border-right:2px solid var(--chrome-darker);">
<form method="post" action="admin.php">
<?= csrf_field() ?>
<input type="hidden" name="action" value="remove_og_logo">
<button type="submit" class="btn btn-sm btn-danger">Remove Logo</button>
</form>
</div>
<?php else: ?>
<p style="font-size:12px; color:var(--chrome-dark); margin-bottom:8px;">No logo uploaded yet.</p>
<?php endif; ?>
<form method="post" action="admin.php" enctype="multipart/form-data">
<?= csrf_field() ?>
<input type="hidden" name="action" value="upload_og_logo">
<div class="form-row-inline" style="margin:0;">
<div class="stretch">
<input type="file" name="og_logo"
accept=".jpg,.jpeg,.png,.gif,.webp,image/*"
style="border:none; background:transparent; padding:2px 0; width:100%;">
</div>
<div>
<button type="submit" class="btn btn-primary btn-sm">Upload</button>
</div>
</div>
</form>
</div>
</div>
<!-- ── Footer text ────────────────────────────────────── -->
<div class="window">
<div class="window-title"><span>⚙ Site Settings — Footer Text</span></div>
<div class="window-body">
<div class="form-row" style="margin-bottom:10px;">
<div style="font-size:12px; color:var(--chrome-darker); margin-bottom:6px;">
Use <code style="background:#FFF;border:1px solid #999;padding:1px 4px;">{year}</code>
to insert the current year automatically.
</div>
<div style="padding:6px 8px; background:#FFF;
border-top:2px solid var(--chrome-dark);
border-left:2px solid var(--chrome-dark);
border-bottom:2px solid var(--chrome-light);
border-right:2px solid var(--chrome-light);
font-size:12px; color:var(--chrome-darker); margin-bottom:8px;">
Preview: <strong><?= h(get_footer_text()) ?></strong>
</div>
</div>
<form method="post" action="admin.php">
<?= csrf_field() ?>
<input type="hidden" name="action" value="save_footer">
<div class="form-row">
<label for="footer_text">Footer Text</label>
<input type="text" id="footer_text" name="footer_text"
value="<?= h($cfg['footer_text'] ?? DEFAULT_FOOTER_TEXT) ?>"
maxlength="256">
</div>
<div class="d-flex gap-6 mt-6">
<button type="submit" class="btn btn-primary">Save Footer</button>
</div>
</form>
<div class="sep"></div>
<form method="post" action="admin.php">
<?= csrf_field() ?>
<input type="hidden" name="action" value="reset_footer">
<button type="submit" class="btn btn-sm"
title="Restore to default text">Reset to Default</button>
<span style="font-size:11px; color:var(--chrome-dark); margin-left:6px;">
Default: <?= h(str_replace('{year}', date('Y'), DEFAULT_FOOTER_TEXT)) ?>
</span>
</form>
</div>
</div>
<!-- ── User admin management ──────────────────────────── -->
<div class="window mt-10">
<div class="window-title">
<span>👤 User Management</span>
<span style="font-size:11px;opacity:.8;"><?= count($users) ?> user<?= count($users) !== 1 ? 's' : '' ?></span>
</div>
<div class="window-body" style="padding:0;">
<table>
<thead>
<tr>
<th>Username</th>
<th>Display Name</th>
<th>Joined</th>
<th>Role</th>
<th>Action</th>
</tr>
</thead>
<tbody>
<?php foreach ($users as $uname => $udata):
$uprofile = read_json(USERS_DIR . '/' . $uname . '/profile.json');
$uadmin = !empty($uprofile['admin']);
$is_self = $uname === $user['username'];
?>
<tr>
<td>
<a href="profile.php?user=<?= h($uname) ?>"
style="color:var(--title-start);text-decoration:none;">@<?= h($uname) ?></a>
</td>
<td><?= h($udata['display_name'] ?? $uname) ?></td>
<td style="white-space:nowrap; font-size:11px;">
<?= date('d M Y', strtotime($udata['created'] ?? '')) ?>
</td>
<td>
<?php if ($uadmin): ?>
<span style="color:var(--title-start); font-weight:bold; font-size:11px;">★ Admin</span>
<?php else: ?>
<span style="font-size:11px; color:var(--chrome-dark);">User</span>
<?php endif; ?>
</td>
<td>
<?php if (!$is_self): ?>
<form method="post" action="admin.php" style="display:inline;">
<?= csrf_field() ?>
<input type="hidden" name="action" value="toggle_admin">
<input type="hidden" name="target_user" value="<?= h($uname) ?>">
<button type="submit" class="btn btn-sm <?= $uadmin ? 'btn-danger' : '' ?>">
<?= $uadmin ? 'Revoke Admin' : 'Grant Admin' ?>
</button>
</form>
<?php else: ?>
<span style="font-size:11px; color:var(--chrome-dark);">(you)</span>
<?php endif; ?>
</td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
</div>
</div>
</div>
<?php require_once 'includes/footer.php'; ?>