39038-vm/admin_platform_users.php
2026-03-24 05:40:05 +00:00

321 lines
16 KiB
PHP

<?php
declare(strict_types=1);
require_once __DIR__ . '/includes/layout.php'; require_role('admin');
// Ensure user is logged in and is an admin
if (!isset($_SESSION['user_id']) || ($_SESSION['user_role'] ?? '') !== 'admin') {
header('Location: login.php');
exit;
}
// Check permission
if (!has_permission('manage_platform_users')) {
render_header(t('nav_platform_users'), 'platform_users');
$uid = $_SESSION['user_id'] ?? 'unknown';
echo '<div class="container py-5"><div class="alert alert-danger">Access Denied. You do not have permission to manage platform users. (Error Code: 403-U' . $uid . ')</div></div>';
render_footer();
exit;
}
$pdo = db();
$message = '';
$error = '';
// Handle Actions
if ($_SERVER['REQUEST_METHOD'] === 'POST') { validate_csrf_token();
$action = $_POST['action'] ?? '';
if ($action === 'create' || $action === 'edit') {
$id = isset($_POST['id']) ? (int)$_POST['id'] : null;
$email = trim($_POST['email'] ?? '');
$fullName = trim($_POST['full_name'] ?? '');
$password = $_POST['password'] ?? '';
$selectedPermissions = $_POST['permissions'] ?? [];
if (empty($email) || empty($fullName)) {
$error = t('error_required');
} else {
try {
$pdo->beginTransaction();
if ($action === 'create') {
// Check if email exists
$stmtCheck = $pdo->prepare("SELECT id FROM users WHERE email = ?");
$stmtCheck->execute([$email]);
if ($stmtCheck->fetch()) {
$error = t('error_email_exists');
} else {
if (empty($password)) {
$error = t('error_required');
} else {
$stmt = $pdo->prepare("INSERT INTO users (email, password, full_name, role, status) VALUES (?, ?, ?, 'admin', 'active')");
$stmt->execute([$email, password_hash($password, PASSWORD_DEFAULT), $fullName]);
$id = (int)$pdo->lastInsertId();
$message = t('user_created');
}
}
} else { // Edit
// Check if email exists for other user
$stmtCheck = $pdo->prepare("SELECT id FROM users WHERE email = ? AND id != ?");
$stmtCheck->execute([$email, $id]);
if ($stmtCheck->fetch()) {
$error = t('error_email_exists');
} else {
$sql = "UPDATE users SET email = ?, full_name = ? WHERE id = ?";
$params = [$email, $fullName, $id];
if (!empty($password)) {
$sql = "UPDATE users SET email = ?, full_name = ?, password = ? WHERE id = ?";
$params = [$email, $fullName, password_hash($password, PASSWORD_DEFAULT), $id];
}
$stmt = $pdo->prepare($sql);
$stmt->execute($params);
$message = t('user_updated');
}
}
if (!$error && $id) {
// Update Permissions
$pdo->prepare("DELETE FROM user_permissions WHERE user_id = ?")->execute([$id]);
if (!empty($selectedPermissions)) {
$stmtPerm = $pdo->prepare("INSERT INTO user_permissions (user_id, permission_id) VALUES (?, ?)");
foreach ($selectedPermissions as $permId) {
$stmtPerm->execute([$id, $permId]);
}
}
}
if (!$error) {
$pdo->commit();
} else {
$pdo->rollBack();
}
} catch (Exception $e) {
$pdo->rollBack();
$error = $e->getMessage();
}
}
} elseif ($action === 'delete') {
$id = (int)($_POST['id'] ?? 0);
if ($id === $_SESSION['user_id']) {
$error = "You cannot delete your own account.";
} else {
$pdo->prepare("DELETE FROM users WHERE id = ? AND role = 'admin'")->execute([$id]);
$message = t('user_deleted');
}
}
}
// Pagination
$page = max(1, (int)($_GET['page'] ?? 1));
$limit = 20;
$offset = ($page - 1) * $limit;
$total = (int)$pdo->query("SELECT COUNT(*) FROM users WHERE role = 'admin'")->fetchColumn();
$totalPages = (int)ceil($total / $limit);
// Fetch Users
$stmtUsers = $pdo->query("SELECT id, email, full_name, created_at FROM users WHERE role = 'admin' ORDER BY created_at DESC LIMIT $limit OFFSET $offset");
$users = $stmtUsers->fetchAll();
// Fetch Permissions
$stmtPerms = $pdo->query("SELECT id, slug, name, description FROM permissions ORDER BY name ASC");
$allPermissions = $stmtPerms->fetchAll();
render_header(t('nav_platform_users'), 'platform_users', true);
?>
<div class="row g-0">
<div class="col-md-2 bg-white border-end min-vh-100">
<?php render_admin_sidebar('platform_users'); ?>
</div>
<div class="col-md-10 p-4">
<div class="d-flex justify-content-between align-items-center mb-4">
<h1 class="h3 fw-bold mb-0 text-dark"><?= e(t('nav_platform_users')) ?></h1>
<button class="btn btn-primary rounded-pill fw-bold px-4 shadow-sm" data-bs-toggle="modal" data-bs-target="#userModal" onclick="resetForm()">
<i class="bi bi-plus-lg me-2"></i><?= e(t('create_user')) ?>
</button>
</div>
<?php if ($message): ?>
<div class="alert alert-success shadow-sm border-0 rounded-3 mb-4"><?= e($message) ?></div>
<?php endif; ?>
<?php if ($error): ?>
<div class="alert alert-danger shadow-sm border-0 rounded-3 mb-4"><?= e($error) ?></div>
<?php endif; ?>
<div class="card shadow-sm border-0 rounded-4">
<div class="card-body p-0">
<div class="table-responsive">
<table class="table table-hover align-middle mb-0">
<thead class="bg-light">
<tr>
<th class="ps-4 py-3 text-secondary text-uppercase small fw-bold">ID</th>
<th class="py-3 text-secondary text-uppercase small fw-bold"><?= e(t('full_name')) ?></th>
<th class="py-3 text-secondary text-uppercase small fw-bold"><?= e(t('email_address')) ?></th>
<th class="py-3 text-secondary text-uppercase small fw-bold"><?= e(t('created_at')) ?></th>
<th class="pe-4 py-3 text-end text-secondary text-uppercase small fw-bold"><?= e(t('actions')) ?></th>
</tr>
</thead>
<tbody>
<?php if (empty($users)): ?>
<tr>
<td colspan="5" class="text-center py-5 text-muted"><?= e(t('no_users')) ?></td>
</tr>
<?php else: ?>
<?php foreach ($users as $user): ?>
<tr>
<td class="ps-4 fw-bold">#<?= e($user['id']) ?></td>
<td><?= e($user['full_name']) ?></td>
<td><?= e($user['email']) ?></td>
<td class="text-muted small"><?= e(date('M j, Y', strtotime($user['created_at']))) ?></td>
<td class="pe-4 text-end">
<button class="btn btn-sm p-1 border-0 bg-transparent text-primary" onclick="editUser(<?= e(json_encode($user)) ?>)">
<i class="bi bi-pencil"></i>
</button>
<?php if ($user['id'] !== $_SESSION['user_id']): ?>
<button class="btn btn-sm p-1 border-0 bg-transparent text-danger" onclick="confirmDelete(<?= $user['id'] ?>)">
<i class="bi bi-trash"></i>
</button>
<?php endif; ?>
</td>
</tr>
<?php endforeach; ?>
<?php endif; ?>
</tbody>
</table>
</div>
<?php if ($totalPages > 1): ?>
<div class="px-4 py-3 border-top d-flex justify-content-between align-items-center">
<span class="text-muted small"><?= e(t('shown')) ?> <?= count($users) ?> of <?= $total ?> users</span>
<ul class="pagination pagination-sm mb-0">
<li class="page-item <?= $page <= 1 ? 'disabled' : '' ?>">
<a class="page-link" href="?page=<?= $page - 1 ?>">Previous</a>
</li>
<?php for ($i = 1; $i <= $totalPages; $i++): ?>
<li class="page-item <?= $i === $page ? 'active' : '' ?>">
<a class="page-link" href="?page=<?= $i ?>"><?= $i ?></a>
</li>
<?php endfor; ?>
<li class="page-item <?= $page >= $totalPages ? 'disabled' : '' ?>">
<a class="page-link" href="?page=<?= $page + 1 ?>">Next</a>
</li>
</ul>
</div>
<?php endif; ?>
</div>
</div>
</div>
</div>
<!-- User Modal -->
<div class="modal fade" id="userModal" tabindex="-1" aria-hidden="true">
<div class="modal-dialog modal-lg">
<div class="modal-content border-0 shadow rounded-4">
<div class="modal-header border-0 pb-0">
<h5 class="modal-title fw-bold" id="modalTitle"><?= e(t('create_user')) ?></h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<form method="post" id="userForm"> <?= csrf_field() ?>
<div class="modal-body p-4">
<input type="hidden" name="action" id="formAction" value="create">
<input type="hidden" name="id" id="userId">
<div class="row g-3 mb-4">
<div class="col-md-6">
<label class="form-label fw-bold"><?= e(t('full_name')) ?></label>
<input type="text" name="full_name" id="fullName" class="form-control rounded-3" required>
</div>
<div class="col-md-6">
<label class="form-label fw-bold"><?= e(t('email_address')) ?></label>
<input type="email" name="email" id="email" class="form-control rounded-3" required>
</div>
<div class="col-md-12">
<label class="form-label fw-bold"><?= e(t('password')) ?> <span class="text-muted fw-normal small" id="passwordHint">(Leave empty to keep current)</span></label>
<input type="password" name="password" id="password" class="form-control rounded-3" autocomplete="new-password">
</div>
</div>
<h6 class="fw-bold mb-3 border-bottom pb-2"><?= e(t('manage_permissions')) ?></h6>
<div class="row g-3">
<?php foreach ($allPermissions as $perm): ?>
<div class="col-md-6">
<div class="form-check p-3 border rounded-3 bg-light h-100">
<input class="form-check-input" type="checkbox" name="permissions[]" value="<?= $perm['id'] ?>" id="perm_<?= $perm['id'] ?>">
<label class="form-check-label w-100" for="perm_<?= $perm['id'] ?>">
<div class="fw-bold text-dark"><?= e($perm['name']) ?></div>
<div class="small text-muted"><?= e($perm['description']) ?></div>
</label>
</div>
</div>
<?php endforeach; ?>
</div>
</div>
<div class="modal-footer border-0 pt-0 pb-4 pe-4">
<button type="button" class="btn btn-light rounded-pill px-4" data-bs-dismiss="modal">Cancel</button>
<button type="submit" class="btn btn-primary rounded-pill px-4 fw-bold shadow-sm">Save Changes</button>
</div>
</form>
</div>
</div>
</div>
<!-- Delete Form -->
<form method="post" id="deleteForm"> <?= csrf_field() ?>
<input type="hidden" name="action" value="delete">
<input type="hidden" name="id" id="deleteId">
</form>
<script>
function resetForm() {
document.getElementById('userForm').reset();
document.getElementById('formAction').value = 'create';
document.getElementById('userId').value = '';
document.getElementById('modalTitle').innerText = '<?= e(t('create_user')) ?>';
document.getElementById('passwordHint').innerText = '(Required)';
document.getElementById('password').required = true;
// Uncheck all permissions
document.querySelectorAll('input[name="permissions[]"]').forEach(el => el.checked = false);
}
function editUser(user) {
resetForm();
document.getElementById('formAction').value = 'edit';
document.getElementById('userId').value = user.id;
document.getElementById('fullName').value = user.full_name;
document.getElementById('email').value = user.email;
document.getElementById('modalTitle').innerText = '<?= e(t('edit_user')) ?>';
document.getElementById('passwordHint').innerText = '(Leave empty to keep current)';
document.getElementById('password').required = false;
// Fetch user permissions via AJAX (or simpler, just reload the page - but for UX let's fetch)
// For simplicity in this demo, we'll fetch them via a separate hidden endpoint or just pre-load all permissions for all users in PHP?
// Let's pre-load permissions for all users to avoid AJAX complexity here.
const userPermissions = <?= json_encode($pdo->query("SELECT user_id, permission_id FROM user_permissions")->fetchAll(PDO::FETCH_GROUP | PDO::FETCH_COLUMN)) ?>;
const perms = userPermissions[user.id] || [];
perms.forEach(permId => {
const el = document.getElementById('perm_' + permId);
if (el) el.checked = true;
});
const modal = new bootstrap.Modal(document.getElementById('userModal'));
modal.show();
}
function confirmDelete(id) {
if (confirm('<?= e(t('confirm_delete')) ?>')) {
document.getElementById('deleteId').value = id;
document.getElementById('deleteForm').submit();
}
}
</script>
<?php render_footer(); ?>