294 lines
14 KiB
PHP
294 lines
14 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');
|
|
}
|
|
}
|
|
}
|
|
|
|
// Fetch Users
|
|
$stmtUsers = $pdo->query("SELECT id, email, full_name, created_at FROM users WHERE role = 'admin' ORDER BY created_at DESC");
|
|
$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>
|
|
</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(); ?>
|