adding role groups

This commit is contained in:
Flatlogic Bot 2026-04-07 18:26:56 +00:00
parent 43c2d25666
commit 547de74ee7
12 changed files with 471 additions and 19 deletions

View File

@ -180,7 +180,7 @@ render_head(
</li>
<li class="nav-item w-100 mt-3 border-top pt-3">
<a href="#settingsCollapse" data-bs-toggle="collapse" class="nav-link link-dark d-flex align-items-center justify-content-between text-secondary fw-bold px-3 py-2" aria-expanded="<?= in_array($page, ['profile', 'integrations', 'landing', 'users']) ? 'true' : 'false' ?>" aria-controls="settingsCollapse" style="font-size: 0.95rem; letter-spacing: 0.5px;">
<a href="#settingsCollapse" data-bs-toggle="collapse" class="nav-link link-dark d-flex align-items-center justify-content-between text-secondary fw-bold px-3 py-2" aria-expanded="<?= in_array($page, ['profile', 'integrations', 'landing', 'users', 'roles']) ? 'true' : 'false' ?>" aria-controls="settingsCollapse" style="font-size: 0.95rem; letter-spacing: 0.5px;">
<div class="d-flex align-items-center gap-2">
<svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" fill="currentColor" class="bi bi-gear" viewBox="0 0 16 16">
<path d="M8 4.754a3.246 3.246 0 1 0 0 6.492 3.246 3.246 0 0 0 0-6.492M5.754 8a2.246 2.246 0 1 1 4.492 0 2.246 2.246 0 0 1-4.492 0"/>
@ -193,7 +193,7 @@ render_head(
</svg>
</a>
<div class="collapse <?= in_array($page, ['profile', 'integrations', 'landing', 'users']) ? 'show' : '' ?>" id="settingsCollapse">
<div class="collapse <?= in_array($page, ['profile', 'integrations', 'landing', 'users', 'roles']) ? 'show' : '' ?>" id="settingsCollapse">
<ul class="nav flex-column ms-3 mb-2">
<li class="nav-item mb-1 mt-2">
<a href="<?= h(app_url('admin.php', ['page' => 'profile'])) ?>" class="nav-link <?= $page === 'profile' ? 'active' : 'link-dark' ?> d-flex align-items-center gap-2" <?= $page === 'profile' ? 'style="background-color: var(--accent); color: white; border-radius: 6px;"' : '' ?>>
@ -221,7 +221,7 @@ render_head(
<?= h(t('Landing Page', 'الصفحة الرئيسية')) ?>
</a>
</li>
<?php if (get_logged_in_user()['role'] === 'admin'): ?>
<?php if (has_permission('users', 'view')): ?>
<li class="nav-item">
<a href="<?= h(app_url('admin.php', ['page' => 'users'])) ?>" class="nav-link <?= $page === 'users' ? 'active' : 'link-dark' ?> d-flex align-items-center gap-2" <?= $page === 'users' ? 'style="background-color: var(--accent); color: white; border-radius: 6px;"' : '' ?> >
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-shield-lock" viewBox="0 0 16 16">
@ -231,6 +231,16 @@ render_head(
<?= h(t('Platform Users', 'مستخدمي المنصة')) ?>
</a>
</li>
<?php if (has_permission('roles', 'view')): ?>
<li class="nav-item mb-1">
<a href="<?= h(app_url('admin.php', ['page' => 'roles'])) ?>" class="nav-link <?= $page === 'roles' ? 'active' : 'link-dark' ?> d-flex align-items-center gap-2" <?= $page === 'roles' ? 'style="background-color: var(--accent); color: white; border-radius: 6px;"' : '' ?> >
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-person-lines-fill" viewBox="0 0 16 16">
<path d="M6 8a3 3 0 1 0 0-6 3 3 0 0 0 0 6zm-5 6s-1 0-1-1 1-4 6-4 6 3 6 4-1 1-1 1H1zM11 3.5a.5.5 0 0 1 .5-.5h4a.5.5 0 0 1 0 1h-4a.5.5 0 0 1-.5-.5zm.5 2.5a.5.5 0 0 0 0 1h4a.5.5 0 0 0 0-1h-4zm2 3a.5.5 0 0 0 0 1h2a.5.5 0 0 0 0-1h-2zm0 3a.5.5 0 0 0 0 1h2a.5.5 0 0 0 0-1h-2z"/>
</svg>
<?= h(t('Role Groups', 'مجموعات الصلاحيات')) ?>
</a>
</li>
<?php endif; ?>
<?php endif; ?>
</ul>
</div>
@ -243,6 +253,7 @@ render_head(
<main class="flex-grow-1 d-flex flex-column" style="height: calc(100vh - 61px); overflow-y: auto;">
<div class="p-4 p-md-5 flex-grow-1">
<?php if ($page === 'dashboard'): ?>
<?php require_permission('dashboard', 'view'); ?>
<div class="section-header mb-4">
<div>
<span class="eyebrow"><?= h(t('Admin', 'الإدارة')) ?></span>
@ -296,6 +307,7 @@ render_head(
</div>
<?php elseif ($page === 'profile'): ?>
<?php if (get_logged_in_user()['role'] !== 'admin' || !empty(get_logged_in_user()['role_id'])) die('Access Denied'); ?>
<?php $prof = get_platform_profile(); ?>
<div class="section-header mb-4">
<div>
@ -378,6 +390,8 @@ render_head(
<?php require_once __DIR__ . '/admin_integrations.php'; ?>
<?php elseif ($page === 'landing'): ?>
<?php require_once __DIR__ . '/admin_landing.php'; ?>
<?php elseif ($page === 'roles'): ?>
<?php require_once __DIR__ . '/admin_roles.php'; ?>
<?php else: ?>
<div class="section-header mb-4">
<div>

View File

@ -1,6 +1,9 @@
<?php
// admin_assignments.php
require_once __DIR__ . '/includes/app.php';
require_once __DIR__ . '/includes/auth.php';
require_permission('assignments', 'view');
// Initialize table if not exists
db()->exec("CREATE TABLE IF NOT EXISTS teacher_assignments (
@ -20,6 +23,7 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$post_id = (int)($_POST['id'] ?? $id);
if ($post_action === 'delete' && $post_id > 0) {
require_permission('assignments', 'delete');
$stmt = db()->prepare("DELETE FROM teacher_assignments WHERE id = ?");
$stmt->execute([$post_id]);
header('Location: ' . app_url('admin.php', ['page' => 'assignments']));
@ -95,7 +99,9 @@ $items = $stmt->fetchAll(PDO::FETCH_ASSOC);
<div>
<h1 class="section-title mb-2"><?= h(t('Teacher Assignments', 'تعيينات المعلمين')) ?></h1>
</div>
<?php if (has_permission('assignments', 'add')): ?>
<button type="button" class="btn btn-primary" data-bs-toggle="modal" data-bs-target="#addAssignmentModal" style="background-color: var(--accent); border-color: var(--accent);">+ <?= h(t('Add Assignment', 'إضافة تعيين')) ?></button>
<?php endif; ?>
</div>
<div class="panel-card mb-4">

View File

@ -1,6 +1,9 @@
<?php
// admin_classes.php
require_once __DIR__ . '/includes/app.php';
require_once __DIR__ . '/includes/auth.php';
require_permission('classes', 'view');
$action = $_GET['action'] ?? 'list';
$id = (int)($_GET['id'] ?? 0);
@ -12,6 +15,7 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$post_id = (int)($_POST['id'] ?? $id);
if ($post_action === 'delete' && $post_id > 0) {
require_permission('classes', 'delete');
$stmt = db()->prepare("DELETE FROM classes WHERE id = ?");
$stmt->execute([$post_id]);
header('Location: ' . app_url('admin.php', ['page' => 'classes']));
@ -19,6 +23,7 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
}
if ($post_action === 'edit' || $post_action === 'add') {
require_permission('classes', $post_action === 'add' ? 'add' : 'edit');
$name_en = $_POST['name_en'] ?? '';
$name_ar = $_POST['name_ar'] ?? '';
$desc_en = $_POST['description_en'] ?? '';
@ -64,7 +69,9 @@ $items = $stmt->fetchAll(PDO::FETCH_ASSOC);
<div>
<h1 class="section-title mb-2"><?= h(t('Classes', 'الصفوف')) ?></h1>
</div>
<?php if (has_permission('classes', 'add')): ?>
<button type="button" class="btn btn-primary" data-bs-toggle="modal" data-bs-target="#addClassModal" style="background-color: var(--accent); border-color: var(--accent);">+ <?= h(t('Add Class', 'إضافة صف')) ?></button>
<?php endif; ?>
</div>
<div class="panel-card mb-4">
@ -108,12 +115,16 @@ $items = $stmt->fetchAll(PDO::FETCH_ASSOC);
<?= h(current_lang() === 'ar' ? $row['description_ar'] : $row['description_en']) ?>
</td>
<td>
<?php if (has_permission('classes', 'edit')): ?>
<button type="button" class="btn btn-sm btn-outline-primary" data-bs-toggle="modal" data-bs-target="#editClassModal<?= $row['id'] ?>" title="<?= h(t('Edit', 'تعديل')) ?>">
<svg width="16" height="16" fill="currentColor" viewBox="0 0 16 16"><path d="M12.146.146a.5.5 0 0 1 .708 0l3 3a.5.5 0 0 1 0 .708l-10 10a.5.5 0 0 1-.168.11l-5 2a.5.5 0 0 1-.65-.65l2-5a.5.5 0 0 1 .11-.168l10-10zM11.207 2.5 13.5 4.793 14.793 3.5 12.5 1.207 11.207 2.5zm1.586 3L10.5 3.207 4 9.707V10h.5a.5.5 0 0 1 .5.5v.5h.5a.5.5 0 0 1 .5.5v.5h.293l6.5-6.5zm-9.761 5.175-.106.106-1.528 3.821 3.821-1.528.106-.106A.5.5 0 0 1 5 12.5V12h-.5a.5.5 0 0 1-.5-.5V11h-.5a.5.5 0 0 1-.468-.325z"/></svg>
</button>
<?php endif; ?>
<?php if (has_permission('classes', 'delete')): ?>
<form method="post" action="<?= h(app_url('admin.php', ['page'=>'classes', 'action'=>'delete', 'id'=>$row['id']])) ?>" class="d-inline" onsubmit="return confirm('<?= h(t('Are you sure?', 'هل أنت متأكد؟')) ?>');">
<button type="submit" class="btn btn-sm btn-outline-danger" title="<?= h(t('Delete', 'حذف')) ?>"><svg width="16" height="16" fill="currentColor" viewBox="0 0 16 16"><path d="M5.5 5.5A.5.5 0 0 1 6 6v6a.5.5 0 0 1-1 0V6a.5.5 0 0 1 .5-.5zm2.5 0a.5.5 0 0 1 .5.5v6a.5.5 0 0 1-1 0V6a.5.5 0 0 1 .5-.5zm3 .5a.5.5 0 0 0-1 0v6a.5.5 0 0 0 1 0V6z"/><path fill-rule="evenodd" d="M14.5 3a1 1 0 0 1-1 1H13v9a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V4h-.5a1 1 0 0 1-1-1V2a1 1 0 0 1 1-1H6a1 1 0 0 1 1-1h2a1 1 0 0 1 1 1h3.5a1 1 0 0 1 1 1v1zM4.118 4 4 4.059V13a1 1 0 0 0 1 1h6a1 1 0 0 0 1-1V4.059L11.882 4H4.118zM2.5 3V2h11v1h-11z"/></svg></button>
</form>
<?php endif; ?>
</td>
</tr>

View File

@ -1,6 +1,9 @@
<?php
// admin_courses.php
require_once __DIR__ . '/includes/app.php';
require_once __DIR__ . '/includes/auth.php';
require_permission('courses', 'view');
$action = $_GET['action'] ?? 'list';
$id = (int)($_GET['id'] ?? 0);
@ -10,6 +13,7 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$post_id = (int)($_POST['id'] ?? $id);
if ($post_action === 'delete' && $post_id > 0) {
require_permission('courses', 'delete');
$stmt = db()->prepare("DELETE FROM courses WHERE id = ?");
$stmt->execute([$post_id]);
header('Location: ' . app_url('admin.php', ['page' => 'courses']));
@ -33,6 +37,7 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
}
if ($post_action === 'edit' || $post_action === 'add') {
require_permission('courses', $post_action === 'add' ? 'add' : 'edit');
$name_en = $_POST['name_en'] ?? '';
$name_ar = $_POST['name_ar'] ?? '';
$desc_en = $_POST['description_en'] ?? '';
@ -124,7 +129,8 @@ $items = $stmt->fetchAll(PDO::FETCH_ASSOC);
<h1 class="section-title mb-2"><?= h(t('Courses', 'الدورات')) ?></h1>
</div>
<div class="d-flex flex-wrap gap-2 justify-content-end">
<form method="post" action="<?= h(app_url('admin.php', ['page'=>'courses'])) ?>" class="m-0" onsubmit="return confirm('<?= h(t('Close registration for all courses?', 'هل أنت متأكد من إغلاق التسجيل لجميع الدورات؟')) ?>');">
<?php if (has_permission('courses', 'delete')): ?>
<form method="post" action="<?= h(app_url('admin.php', ['page'=>'courses'])) ?>" class="m-0" onsubmit="return confirm('<?= h(t('Close registration for all courses?', 'هل أنت متأكد من إغلاق التسجيل لجميع الدورات؟')) ?>');">
<input type="hidden" name="action" value="close_all_registration">
<button type="submit" class="btn btn-outline-warning btn-sm"><?= h(t('Close All', 'إغلاق الكل')) ?></button>
</form>
@ -216,12 +222,15 @@ $items = $stmt->fetchAll(PDO::FETCH_ASSOC);
<path d="M15 14s1 0 1-1-1-4-5-4-5 3-5 4 1 1 1 1zm-7.978-1L7 12.996c.001-.264.167-1.03.76-1.72C8.312 10.629 9.282 10 11 10c1.717 0 2.687.63 3.24 1.276.593.69.758 1.457.76 1.72l-.008.002-.014.002zM11 7a2 2 0 1 0 0-4 2 2 0 0 0 0 4m3-2a3 3 0 1 1-6 0 3 3 0 0 1 6 0M6.936 9.28a6 6 0 0 0-1.23-.247A7 7 0 0 0 5 9c-4 0-5 3-5 4q0 1 1 1h4.216A2.24 2.24 0 0 1 5 13c0-1.01.377-2.042 1.09-2.904.243-.294.526-.569.846-.816M4.92 10A5.5 5.5 0 0 0 4 13H1c0-.26.164-1.03.76-1.724.545-.636 1.492-1.256 3.16-1.275ZM1.5 5.5a3 3 0 1 1 6 0 3 3 0 0 1-6 0m3-2a2 2 0 1 0 0 4 2 2 0 0 0 0-4"/>
</svg>
</button>
<?php if (has_permission('courses', 'edit')): ?>
<button type="button" class="btn btn-sm btn-outline-primary" data-bs-toggle="modal" data-bs-target="#editCourseModal<?= $row['id'] ?>" title="<?= h(t('Edit', 'تعديل')) ?>">
<svg width="16" height="16" fill="currentColor" viewBox="0 0 16 16"><path d="M12.146.146a.5.5 0 0 1 .708 0l3 3a.5.5 0 0 1 0 .708l-10 10a.5.5 0 0 1-.168.11l-5 2a.5.5 0 0 1-.65-.65l2-5a.5.5 0 0 1 .11-.168l10-10zM11.207 2.5 13.5 4.793 14.793 3.5 12.5 1.207 11.207 2.5zm1.586 3L10.5 3.207 4 9.707V10h.5a.5.5 0 0 1 .5.5v.5h.5a.5.5 0 0 1 .5.5v.5h.293l6.5-6.5zm-9.761 5.175-.106.106-1.528 3.821 3.821-1.528.106-.106A.5.5 0 0 1 5 12.5V12h-.5a.5.5 0 0 1-.5-.5V11h-.5a.5.5 0 0 1-.468-.325z"/></svg>
</button>
<?php endif; ?>
<form method="post" action="<?= h(app_url('admin.php', ['page'=>'courses', 'action'=>'delete', 'id'=>$row['id']])) ?>" class="d-inline" onsubmit="return confirm('<?= h(t('Are you sure?', 'هل أنت متأكد؟')) ?>');">
<button type="submit" class="btn btn-sm btn-outline-danger" title="<?= h(t('Delete', 'حذف')) ?>"><svg width="16" height="16" fill="currentColor" viewBox="0 0 16 16"><path d="M5.5 5.5A.5.5 0 0 1 6 6v6a.5.5 0 0 1-1 0V6a.5.5 0 0 1 .5-.5zm2.5 0a.5.5 0 0 1 .5.5v6a.5.5 0 0 1-1 0V6a.5.5 0 0 1 .5-.5zm3 .5a.5.5 0 0 0-1 0v6a.5.5 0 0 0 1 0V6z"/><path fill-rule="evenodd" d="M14.5 3a1 1 0 0 1-1 1H13v9a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V4h-.5a1 1 0 0 1-1-1V2a1 1 0 0 1 1-1H6a1 1 0 0 1 1-1h2a1 1 0 0 1 1 1h3.5a1 1 0 0 1 1 1v1zM4.118 4 4 4.059V13a1 1 0 0 0 1 1h6a1 1 0 0 0 1-1V4.059L11.882 4H4.118zM2.5 3V2h11v1h-11z"/></svg></button>
</form>
<?php endif; ?>
</td>
</tr>

View File

@ -1,6 +1,9 @@
<?php
// admin_plans.php
require_once __DIR__ . '/includes/app.php';
require_once __DIR__ . '/includes/auth.php';
require_permission('plans', 'view');
$action = $_GET['action'] ?? 'list';
$id = (int)($_GET['id'] ?? 0);
@ -10,6 +13,7 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$post_id = (int)($_POST['id'] ?? $id);
if ($post_action === 'delete' && $post_id > 0) {
require_permission('plans', 'delete');
$stmt = db()->prepare("DELETE FROM plans WHERE id = ?");
$stmt->execute([$post_id]);
header('Location: ' . app_url('admin.php', ['page' => 'plans']));
@ -17,6 +21,7 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
}
if ($post_action === 'edit' || $post_action === 'add') {
require_permission('plans', $post_action === 'add' ? 'add' : 'edit');
$plan_key = $_POST['plan_key'] ?? '';
$name_en = $_POST['name_en'] ?? '';
$name_ar = $_POST['name_ar'] ?? '';
@ -121,12 +126,16 @@ $items = $stmt->fetchAll(PDO::FETCH_ASSOC);
<td><?= h(format_price((float)$row['price_yearly'])) ?></td>
<td><?= h((string)$row['subjects_limit']) ?></td>
<td class="text-end">
<?php if (has_permission('plans', 'edit')): ?>
<button type="button" class="btn btn-sm btn-outline-primary" data-bs-toggle="modal" data-bs-target="#editPlanModal<?= $row['id'] ?>" title="<?= h(t('Edit', 'تعديل')) ?>">
<svg width="16" height="16" fill="currentColor" viewBox="0 0 16 16"><path d="M12.146.146a.5.5 0 0 1 .708 0l3 3a.5.5 0 0 1 0 .708l-10 10a.5.5 0 0 1-.168.11l-5 2a.5.5 0 0 1-.65-.65l2-5a.5.5 0 0 1 .11-.168l10-10zM11.207 2.5 13.5 4.793 14.793 3.5 12.5 1.207 11.207 2.5zm1.586 3L10.5 3.207 4 9.707V10h.5a.5.5 0 0 1 .5.5v.5h.5a.5.5 0 0 1 .5.5v.5h.293l6.5-6.5zm-9.761 5.175-.106.106-1.528 3.821 3.821-1.528.106-.106A.5.5 0 0 1 5 12.5V12h-.5a.5.5 0 0 1-.5-.5V11h-.5a.5.5 0 0 1-.468-.325z"/></svg>
</button>
<?php endif; ?>
<?php if (has_permission('plans', 'delete')): ?>
<form method="post" action="<?= h(app_url('admin.php', ['page'=>'plans', 'action'=>'delete', 'id'=>$row['id']])) ?>" class="d-inline" onsubmit="return confirm('<?= h(t('Are you sure?', 'هل أنت متأكد؟')) ?>');">
<button type="submit" class="btn btn-sm btn-outline-danger" title="<?= h(t('Delete', 'حذف')) ?>"><svg width="16" height="16" fill="currentColor" viewBox="0 0 16 16"><path d="M5.5 5.5A.5.5 0 0 1 6 6v6a.5.5 0 0 1-1 0V6a.5.5 0 0 1 .5-.5zm2.5 0a.5.5 0 0 1 .5.5v6a.5.5 0 0 1-1 0V6a.5.5 0 0 1 .5-.5zm3 .5a.5.5 0 0 0-1 0v6a.5.5 0 0 0 1 0V6z"/><path fill-rule="evenodd" d="M14.5 3a1 1 0 0 1-1 1H13v9a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V4h-.5a1 1 0 0 1-1-1V2a1 1 0 0 1 1-1H6a1 1 0 0 1 1-1h2a1 1 0 0 1 1 1h3.5a1 1 0 0 1 1 1v1zM4.118 4 4 4.059V13a1 1 0 0 0 1 1h6a1 1 0 0 0 1-1V4.059L11.882 4H4.118zM2.5 3V2h11v1h-11z"/></svg></button>
</form>
<?php endif; ?>
</td>
</tr>

258
admin_roles.php Normal file
View File

@ -0,0 +1,258 @@
<?php
// admin_roles.php
require_once __DIR__ . '/includes/app.php';
require_once __DIR__ . '/includes/auth.php';
require_permission('roles', 'view');
$pages = [
'dashboard' => t('Dashboard', 'لوحة التحكم'),
'classes' => t('Classes', 'الصفوف'),
'subjects' => t('Subjects', 'المواد'),
'teachers' => t('Teachers', 'المعلمين'),
'courses' => t('Courses', 'الدورات'),
'assignments' => t('Assignments', 'الواجبات'),
'students' => t('Students', 'الطلاب'),
'plans' => t('Plans', 'الخطط'),
'users' => t('Platform Users', 'مستخدمي المنصة'),
'roles' => t('Role Groups', 'مجموعات الصلاحيات'),
'integrations' => t('Integrations', 'عمليات الربط'),
'landing' => t('Landing Page', 'الصفحة الرئيسية'),
];
$action = $_GET['action'] ?? 'list';
$id = (int)($_GET['id'] ?? 0);
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$post_action = $_POST['action'] ?? $action;
$post_id = (int)($_POST['id'] ?? $id);
if ($post_action === 'delete' && $post_id > 0) {
require_permission('roles', 'delete');
// check if system role
$stmt = db()->prepare("SELECT is_system FROM roles WHERE id = ?");
$stmt->execute([$post_id]);
$role = $stmt->fetch(PDO::FETCH_ASSOC);
if ($role && !$role['is_system']) {
$stmt = db()->prepare("DELETE FROM roles WHERE id = ?");
$stmt->execute([$post_id]);
}
header('Location: ' . app_url('admin.php', ['page' => 'roles']));
exit;
}
if ($post_action === 'edit' || $post_action === 'add') {
require_permission('roles', $post_action === 'add' ? 'add' : 'edit');
$name = trim($_POST['name'] ?? '');
$description = trim($_POST['description'] ?? '');
$perms = $_POST['perms'] ?? []; // e.g. ['classes' => ['view'=>1, 'add'=>1]]
if ($post_action === 'edit' && $post_id > 0) {
$stmt = db()->prepare("UPDATE roles SET name=?, description=? WHERE id=?");
$stmt->execute([$name, $description, $post_id]);
$role_id = $post_id;
} else {
$stmt = db()->prepare("INSERT INTO roles (name, description) VALUES (?, ?)");
$stmt->execute([$name, $description]);
$role_id = db()->lastInsertId();
}
// update permissions
if ($role_id) {
$stmt = db()->prepare("DELETE FROM role_permissions WHERE role_id = ?");
$stmt->execute([$role_id]);
$stmt = db()->prepare("INSERT INTO role_permissions (role_id, page, can_view, can_add, can_edit, can_delete) VALUES (?, ?, ?, ?, ?, ?)");
foreach ($pages as $p => $pname) {
if (isset($perms[$p])) {
$v = !empty($perms[$p]['view']) ? 1 : 0;
$a = !empty($perms[$p]['add']) ? 1 : 0;
$e = !empty($perms[$p]['edit']) ? 1 : 0;
$d = !empty($perms[$p]['delete']) ? 1 : 0;
if ($v || $a || $e || $d) {
$stmt->execute([$role_id, $p, $v, $a, $e, $d]);
}
}
}
}
header('Location: ' . app_url('admin.php', ['page' => 'roles']));
exit;
}
}
$roles = db()->query("SELECT * FROM roles ORDER BY id DESC")->fetchAll(PDO::FETCH_ASSOC);
// Fetch permissions for all roles
$stmt = db()->query("SELECT * FROM role_permissions");
$all_perms = $stmt->fetchAll(PDO::FETCH_ASSOC);
$role_perms = [];
foreach ($all_perms as $p) {
$role_perms[$p['role_id']][$p['page']] = $p;
}
// Merge permissions into roles for JS
foreach ($roles as &$r) {
$r['permissions'] = $role_perms[$r['id']] ?? [];
}
unset($r);
?>
<div class="section-header mb-4 d-flex justify-content-between align-items-center">
<div>
<h1 class="section-title mb-2"><?= h(t('Role Groups', 'مجموعات الصلاحيات')) ?></h1>
<p class="text-secondary mb-0"><?= h(t('Manage roles and their page permissions.', 'إدارة الأدوار وصلاحيات الصفحات الخاصة بها.')) ?></p>
</div>
<?php if (has_permission('roles', 'add')): ?>
<button class="btn btn-dark" data-bs-toggle="modal" data-bs-target="#roleModal" onclick="editRole(null)">
<?= h(t('Add Role Group', 'إضافة مجموعة صلاحيات')) ?>
</button>
<?php endif; ?>
</div>
<div class="panel-card p-0 overflow-hidden">
<div class="table-responsive">
<table class="table table-hover align-middle mb-0">
<thead class="bg-light">
<tr>
<th class="px-4 py-3"><?= h(t('Role Name', 'اسم الدور')) ?></th>
<th class="py-3"><?= h(t('Description', 'الوصف')) ?></th>
<th class="px-4 py-3 text-end"><?= h(t('Actions', 'الإجراءات')) ?></th>
</tr>
</thead>
<tbody>
<?php foreach ($roles as $r): ?>
<tr>
<td class="px-4 py-3 fw-bold text-dark">
<?= h($r['name']) ?>
<?php if ($r['is_system']): ?>
<span class="badge bg-secondary ms-2"><?= h(t('System', 'نظام')) ?></span>
<?php endif; ?>
</td>
<td class="py-3 text-secondary"><?= h($r['description']) ?></td>
<td class="px-4 py-3 text-end">
<div class="btn-group btn-group-sm">
<?php if (has_permission('roles', 'edit')): ?>
<button class="btn btn-outline-dark" onclick='editRole(<?= htmlspecialchars(json_encode($r), ENT_QUOTES) ?>)'>
<?= h(t($r['is_system'] ? 'View' : 'Edit', $r['is_system'] ? 'عرض' : 'تعديل')) ?>
</button>
<?php endif; ?>
<?php if (!$r['is_system'] && has_permission('roles', 'delete')): ?>
<form method="POST" action="admin_roles.php" class="d-inline" onsubmit="return confirm('<?= h(t('Delete this role?', 'حذف هذا الدور؟')) ?>');">
<input type="hidden" name="action" value="delete">
<input type="hidden" name="id" value="<?= h($r['id']) ?>">
<button class="btn btn-outline-danger" type="submit">
<?= h(t('Delete', 'حذف')) ?>
</button>
</form>
<?php endif; ?>
</div>
</td>
</tr>
<?php endforeach; ?>
<?php if (empty($roles)): ?>
<tr><td colspan="3" class="text-center py-4 text-secondary"><?= h(t('No roles found.', 'لا توجد أدوار.')) ?></td></tr>
<?php endif; ?>
</tbody>
</table>
</div>
</div>
<!-- Role Modal -->
<div class="modal fade" id="roleModal" tabindex="-1">
<div class="modal-dialog modal-lg">
<form class="modal-content" method="POST" action="admin_roles.php">
<input type="hidden" name="action" id="roleAction" value="add">
<input type="hidden" name="id" id="roleId" value="0">
<div class="modal-header">
<h5 class="modal-title" id="roleModalTitle"><?= h(t('Add Role Group', 'إضافة مجموعة صلاحيات')) ?></h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
<div class="row mb-3">
<div class="col-md-6">
<label class="form-label"><?= h(t('Role Name', 'اسم الدور')) ?></label>
<input type="text" class="form-control" name="name" id="roleName" required>
</div>
<div class="col-md-6">
<label class="form-label"><?= h(t('Description', 'الوصف')) ?></label>
<input type="text" class="form-control" name="description" id="roleDesc">
</div>
</div>
<h6 class="mt-4 mb-3"><?= h(t('Permissions', 'الصلاحيات')) ?></h6>
<div class="table-responsive">
<table class="table table-bordered table-sm align-middle">
<thead class="bg-light text-center">
<tr>
<th class="text-start"><?= h(t('Page', 'الصفحة')) ?></th>
<th><?= h(t('View', 'عرض')) ?></th>
<th><?= h(t('Add', 'إضافة')) ?></th>
<th><?= h(t('Edit', 'تعديل')) ?></th>
<th><?= h(t('Delete', 'حذف')) ?></th>
</tr>
</thead>
<tbody id="permissionsTbody">
<?php foreach($pages as $p => $pname): ?>
<tr>
<td class="text-start fw-bold"><?= h($pname) ?></td>
<td class="text-center"><input class="form-check-input perm-chk perm-chk-view" type="checkbox" name="perms[<?= h($p) ?>][view]" value="1" data-page="<?= h($p) ?>"></td>
<td class="text-center"><input class="form-check-input perm-chk perm-chk-add" type="checkbox" name="perms[<?= h($p) ?>][add]" value="1" data-page="<?= h($p) ?>"></td>
<td class="text-center"><input class="form-check-input perm-chk perm-chk-edit" type="checkbox" name="perms[<?= h($p) ?>][edit]" value="1" data-page="<?= h($p) ?>"></td>
<td class="text-center"><input class="form-check-input perm-chk perm-chk-delete" type="checkbox" name="perms[<?= h($p) ?>][delete]" value="1" data-page="<?= h($p) ?>"></td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-outline-secondary" data-bs-dismiss="modal"><?= h(t('Cancel', 'إلغاء')) ?></button>
<button type="submit" class="btn btn-dark" id="btnSaveRole"><?= h(t('Save', 'حفظ')) ?></button>
</div>
</form>
</div>
</div>
<script>
function editRole(role) {
// reset checkboxes
document.querySelectorAll('.perm-chk').forEach(c => c.checked = false);
if (role) {
document.getElementById('roleAction').value = 'edit';
document.getElementById('roleId').value = role.id;
document.getElementById('roleModalTitle').innerText = '<?= h(t('Edit Role Group', 'تعديل مجموعة الصلاحيات')) ?>';
document.getElementById('roleName').value = role.name;
document.getElementById('roleDesc').value = role.description || '';
if (role.permissions) {
for (let p in role.permissions) {
let perms = role.permissions[p];
if (perms.can_view) { let el = document.querySelector(`input[name="perms[${p}][view]"]`); if(el) el.checked = true; }
if (perms.can_add) { let el = document.querySelector(`input[name="perms[${p}][add]"]`); if(el) el.checked = true; }
if (perms.can_edit) { let el = document.querySelector(`input[name="perms[${p}][edit]"]`); if(el) el.checked = true; }
if (perms.can_delete) { let el = document.querySelector(`input[name="perms[${p}][delete]"]`); if(el) el.checked = true; }
}
}
let isSystem = role.is_system == 1;
document.getElementById('roleName').readOnly = isSystem;
document.getElementById('roleDesc').readOnly = isSystem;
document.querySelectorAll('.perm-chk').forEach(c => c.disabled = isSystem);
document.getElementById('btnSaveRole').style.display = isSystem ? 'none' : 'block';
} else {
document.getElementById('roleAction').value = 'add';
document.getElementById('roleId').value = '0';
document.getElementById('roleModalTitle').innerText = '<?= h(t('Add Role Group', 'إضافة مجموعة صلاحيات')) ?>';
document.getElementById('roleName').value = '';
document.getElementById('roleDesc').value = '';
document.getElementById('roleName').readOnly = false;
document.getElementById('roleDesc').readOnly = false;
document.querySelectorAll('.perm-chk').forEach(c => c.disabled = false);
document.getElementById('btnSaveRole').style.display = 'block';
}
}
</script>

View File

@ -1,6 +1,9 @@
<?php
// admin_students.php
require_once __DIR__ . '/includes/app.php';
require_once __DIR__ . '/includes/auth.php';
require_permission('students', 'view');
$action = $_GET['action'] ?? 'list';
$id = (int)($_GET['id'] ?? 0);
@ -10,6 +13,7 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$post_id = (int)($_POST['id'] ?? $id);
if ($post_action === 'delete' && $post_id > 0) {
require_permission('students', 'delete');
$stmt = db()->prepare("DELETE FROM student_subscriptions WHERE id = ?");
$stmt->execute([$post_id]);
header('Location: ' . app_url('admin.php', ['page' => 'students']));
@ -17,6 +21,7 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
}
if ($post_action === 'edit' || $post_action === 'add') {
require_permission('students', $post_action === 'add' ? 'add' : 'edit');
$full_name = $_POST['full_name'] ?? '';
$email = $_POST['email'] ?? '';
$whatsapp = $_POST['whatsapp'] ?? '';
@ -140,14 +145,18 @@ $items = $stmt->fetchAll(PDO::FETCH_ASSOC);
<?php endif; ?>
</td>
<td>
<?php if (has_permission('students', 'edit')): ?>
<button type="button" class="btn btn-sm btn-outline-primary" data-bs-toggle="modal" data-bs-target="#editStudentModal<?= $row['id'] ?>" title="<?= h(t('Edit', 'تعديل')) ?>">
<svg width="16" height="16" fill="currentColor" viewBox="0 0 16 16"><path d="M12.146.146a.5.5 0 0 1 .708 0l3 3a.5.5 0 0 1 0 .708l-10 10a.5.5 0 0 1-.168.11l-5 2a.5.5 0 0 1-.65-.65l2-5a.5.5 0 0 1 .11-.168l10-10zM11.207 2.5 13.5 4.793 14.793 3.5 12.5 1.207 11.207 2.5zm1.586 3L10.5 3.207 4 9.707V10h.5a.5.5 0 0 1 .5.5v.5h.5a.5.5 0 0 1 .5.5v.5h.293l6.5-6.5zm-9.761 5.175-.106.106-1.528 3.821 3.821-1.528.106-.106A.5.5 0 0 1 5 12.5V12h-.5a.5.5 0 0 1-.5-.5V11h-.5a.5.5 0 0 1-.468-.325z"/></svg>
</button>
<?php endif; ?>
<?php if (has_permission('students', 'delete')): ?>
<form method="post" action="<?= h(app_url('admin.php', ['page'=>'students', 'action'=>'delete', 'id'=>$row['id']])) ?>" class="d-inline" onsubmit="return confirm('<?= h(t('Are you sure?', 'هل أنت متأكد؟')) ?>');">
<button type="submit" class="btn btn-sm btn-outline-danger" title="<?= h(t('Delete', 'حذف')) ?>">
<svg width="16" height="16" fill="currentColor" viewBox="0 0 16 16"><path d="M5.5 5.5A.5.5 0 0 1 6 6v6a.5.5 0 0 1-1 0V6a.5.5 0 0 1 .5-.5zm2.5 0a.5.5 0 0 1 .5.5v6a.5.5 0 0 1-1 0V6a.5.5 0 0 1 .5-.5zm3 .5a.5.5 0 0 0-1 0v6a.5.5 0 0 0 1 0V6z"/><path fill-rule="evenodd" d="M14.5 3a1 1 0 0 1-1 1H13v9a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V4h-.5a1 1 0 0 1-1-1V2a1 1 0 0 1 1-1H6a1 1 0 0 1 1-1h2a1 1 0 0 1 1 1h3.5a1 1 0 0 1 1 1v1zM4.118 4 4 4.059V13a1 1 0 0 0 1 1h6a1 1 0 0 0 1-1V4.059L11.882 4H4.118zM2.5 3V2h11v1h-11z"/></svg>
</button>
</form>
<?php endif; ?>
</td>
</tr>

View File

@ -1,6 +1,9 @@
<?php
// admin_subjects.php
require_once __DIR__ . '/includes/app.php';
require_once __DIR__ . '/includes/auth.php';
require_permission('subjects', 'view');
$action = $_GET['action'] ?? 'list';
$id = (int)($_GET['id'] ?? 0);
@ -14,6 +17,7 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$post_id = (int)($_POST['id'] ?? $id);
if ($post_action === 'delete' && $post_id > 0) {
require_permission('subjects', 'delete');
$stmt = db()->prepare("DELETE FROM subjects WHERE id = ?");
$stmt->execute([$post_id]);
header('Location: ' . app_url('admin.php', ['page' => 'subjects']));
@ -21,6 +25,7 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
}
if ($post_action === 'edit' || $post_action === 'add') {
require_permission('subjects', $post_action === 'add' ? 'add' : 'edit');
$class_id = !empty($_POST['class_id']) ? (int)$_POST['class_id'] : null;
$title_en = $_POST['title_en'] ?? '';
$title_ar = $_POST['title_ar'] ?? '';
@ -89,7 +94,9 @@ $items = $stmt->fetchAll(PDO::FETCH_ASSOC);
<div>
<h1 class="section-title mb-2"><?= h(t('Subjects', 'المواد')) ?></h1>
</div>
<?php if (has_permission('subjects', 'add')): ?>
<button type="button" class="btn btn-primary" data-bs-toggle="modal" data-bs-target="#addSubjectModal" style="background-color: var(--accent); border-color: var(--accent);">+ <?= h(t('Add Subject', 'إضافة مادة')) ?></button>
<?php endif; ?>
</div>
<div class="panel-card mb-4">
@ -149,12 +156,16 @@ $items = $stmt->fetchAll(PDO::FETCH_ASSOC);
<?php endif; ?>
</td>
<td>
<?php if (has_permission('subjects', 'edit')): ?>
<button type="button" class="btn btn-sm btn-outline-primary" data-bs-toggle="modal" data-bs-target="#editSubjectModal<?= $row['id'] ?>" title="<?= h(t('Edit', 'تعديل')) ?>">
<svg width="16" height="16" fill="currentColor" viewBox="0 0 16 16"><path d="M12.146.146a.5.5 0 0 1 .708 0l3 3a.5.5 0 0 1 0 .708l-10 10a.5.5 0 0 1-.168.11l-5 2a.5.5 0 0 1-.65-.65l2-5a.5.5 0 0 1 .11-.168l10-10zM11.207 2.5 13.5 4.793 14.793 3.5 12.5 1.207 11.207 2.5zm1.586 3L10.5 3.207 4 9.707V10h.5a.5.5 0 0 1 .5.5v.5h.5a.5.5 0 0 1 .5.5v.5h.293l6.5-6.5zm-9.761 5.175-.106.106-1.528 3.821 3.821-1.528.106-.106A.5.5 0 0 1 5 12.5V12h-.5a.5.5 0 0 1-.5-.5V11h-.5a.5.5 0 0 1-.468-.325z"/></svg>
</button>
<?php endif; ?>
<?php if (has_permission('subjects', 'delete')): ?>
<form method="post" action="<?= h(app_url('admin.php', ['page'=>'subjects', 'action'=>'delete', 'id'=>$row['id']])) ?>" class="d-inline" onsubmit="return confirm('<?= h(t('Are you sure?', 'هل أنت متأكد؟')) ?>');">
<button type="submit" class="btn btn-sm btn-outline-danger" title="<?= h(t('Delete', 'حذف')) ?>"><svg width="16" height="16" fill="currentColor" viewBox="0 0 16 16"><path d="M5.5 5.5A.5.5 0 0 1 6 6v6a.5.5 0 0 1-1 0V6a.5.5 0 0 1 .5-.5zm2.5 0a.5.5 0 0 1 .5.5v6a.5.5 0 0 1-1 0V6a.5.5 0 0 1 .5-.5zm3 .5a.5.5 0 0 0-1 0v6a.5.5 0 0 0 1 0V6z"/><path fill-rule="evenodd" d="M14.5 3a1 1 0 0 1-1 1H13v9a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V4h-.5a1 1 0 0 1-1-1V2a1 1 0 0 1 1-1H6a1 1 0 0 1 1-1h2a1 1 0 0 1 1 1h3.5a1 1 0 0 1 1 1v1zM4.118 4 4 4.059V13a1 1 0 0 0 1 1h6a1 1 0 0 0 1-1V4.059L11.882 4H4.118zM2.5 3V2h11v1h-11z"/></svg></button>
</form>
<?php endif; ?>
</td>
</tr>

View File

@ -1,6 +1,9 @@
<?php
// admin_teachers.php
require_once __DIR__ . '/includes/app.php';
require_once __DIR__ . '/includes/auth.php';
require_permission('teachers', 'view');
$action = $_GET['action'] ?? 'list';
$id = (int)($_GET['id'] ?? 0);
@ -10,6 +13,7 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$post_id = (int)($_POST['id'] ?? $id);
if ($post_action === 'delete' && $post_id > 0) {
require_permission('teachers', 'delete');
$stmt = db()->prepare("DELETE FROM teachers WHERE id = ?");
$stmt->execute([$post_id]);
header('Location: ' . app_url('admin.php', ['page' => 'teachers']));
@ -17,6 +21,7 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
}
if ($post_action === 'edit' || $post_action === 'add') {
require_permission('teachers', $post_action === 'add' ? 'add' : 'edit');
$name = $_POST['name'] ?? '';
$email = $_POST['email'] ?? '';
$phone = $_POST['phone'] ?? '';
@ -100,7 +105,9 @@ $items = $stmt->fetchAll(PDO::FETCH_ASSOC);
<div>
<h1 class="section-title mb-2"><?= h(t('Teachers', 'المعلمون')) ?></h1>
</div>
<?php if (has_permission('teachers', 'add')): ?>
<button type="button" class="btn btn-primary" data-bs-toggle="modal" data-bs-target="#addTeacherModal" style="background-color: var(--accent); border-color: var(--accent);">+ <?= h(t('Add Teacher', 'إضافة معلم')) ?></button>
<?php endif; ?>
</div>
<div class="panel-card mb-4">
@ -153,14 +160,18 @@ $items = $stmt->fetchAll(PDO::FETCH_ASSOC);
<?php if (!empty($row['phone'])): ?><div><small><?= h($row['phone']) ?></small></div><?php endif; ?>
</td>
<td>
<?php if (has_permission('teachers', 'edit')): ?>
<button type="button" class="btn btn-sm btn-outline-primary" data-bs-toggle="modal" data-bs-target="#editTeacherModal<?= $row['id'] ?>" title="<?= h(t('Edit', 'تعديل')) ?>">
<svg width="16" height="16" fill="currentColor" viewBox="0 0 16 16"><path d="M12.146.146a.5.5 0 0 1 .708 0l3 3a.5.5 0 0 1 0 .708l-10 10a.5.5 0 0 1-.168.11l-5 2a.5.5 0 0 1-.65-.65l2-5a.5.5 0 0 1 .11-.168l10-10zM11.207 2.5 13.5 4.793 14.793 3.5 12.5 1.207 11.207 2.5zm1.586 3L10.5 3.207 4 9.707V10h.5a.5.5 0 0 1 .5.5v.5h.5a.5.5 0 0 1 .5.5v.5h.293l6.5-6.5zm-9.761 5.175-.106.106-1.528 3.821 3.821-1.528.106-.106A.5.5 0 0 1 5 12.5V12h-.5a.5.5 0 0 1-.5-.5V11h-.5a.5.5 0 0 1-.468-.325z"/></svg>
</button>
<?php endif; ?>
<?php if (has_permission('teachers', 'delete')): ?>
<form method="post" action="<?= h(app_url('admin.php', ['page'=>'teachers', 'action'=>'delete', 'id'=>$row['id']])) ?>" class="d-inline" onsubmit="return confirm('<?= h(t('Are you sure?', 'هل أنت متأكد؟')) ?>');">
<button type="submit" class="btn btn-sm btn-outline-danger" title="<?= h(t('Delete', 'حذف')) ?>">
<svg width="16" height="16" fill="currentColor" viewBox="0 0 16 16"><path d="M5.5 5.5A.5.5 0 0 1 6 6v6a.5.5 0 0 1-1 0V6a.5.5 0 0 1 .5-.5zm2.5 0a.5.5 0 0 1 .5.5v6a.5.5 0 0 1-1 0V6a.5.5 0 0 1 .5-.5zm3 .5a.5.5 0 0 0-1 0v6a.5.5 0 0 0 1 0V6z"/><path fill-rule="evenodd" d="M14.5 3a1 1 0 0 1-1 1H13v9a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V4h-.5a1 1 0 0 1-1-1V2a1 1 0 0 1 1-1H6a1 1 0 0 1 1-1h2a1 1 0 0 1 1 1h3.5a1 1 0 0 1 1 1v1zM4.118 4 4 4.059V13a1 1 0 0 0 1 1h6a1 1 0 0 0 1-1V4.059L11.882 4H4.118zM2.5 3V2h11v1h-11z"/></svg>
</button>
</form>
<?php endif; ?>
</td>
</tr>

View File

@ -1,11 +1,11 @@
<?php
// admin_users.php
require_once __DIR__ . '/includes/app.php';
require_once __DIR__ . '/includes/auth.php';
require_permission('users', 'view');
$current_u = get_logged_in_user();
if ($current_u['role'] !== 'admin') {
die('Access Denied');
}
$action = $_GET['action'] ?? 'list';
$id = (int)($_GET['id'] ?? 0);
@ -15,6 +15,7 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$post_id = (int)($_POST['id'] ?? $id);
if ($post_action === 'delete' && $post_id > 0) {
require_permission('users', 'delete');
// Prevent deleting oneself
if ($post_id !== (int)$_SESSION['user_id']) {
$stmt = db()->prepare("DELETE FROM users WHERE id = ?");
@ -25,6 +26,7 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
}
if ($post_action === 'edit' || $post_action === 'add') {
require_permission('users', $post_action === 'add' ? 'add' : 'edit');
$name = trim($_POST['name'] ?? '');
$email = trim($_POST['email'] ?? '');
$phone = trim($_POST['phone'] ?? '');
@ -32,6 +34,8 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
if (!in_array($role, ['admin', 'user'])) {
$role = 'user';
}
$role_id = $_POST['role_id'] ?? '';
if ($role_id === '') $role_id = null;
$raw_password = $_POST['password'] ?? '';
$profile_picture = '';
@ -71,11 +75,11 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
}
if ($post_action === 'edit' && $post_id > 0) {
$stmt = db()->prepare("UPDATE users SET name=?, email=?, phone=?, role=?, profile_picture=?, password=? WHERE id=?");
$stmt->execute([$name, $email, $phone, $role, $profile_picture, $final_password, $post_id]);
$stmt = db()->prepare("UPDATE users SET name=?, email=?, phone=?, role=?, profile_picture=?, password=?, role_id=? WHERE id=?");
$stmt->execute([$name, $email, $phone, $role, $profile_picture, $final_password, $role_id, $post_id]);
} else {
$stmt = db()->prepare("INSERT INTO users (name, email, phone, role, profile_picture, password) VALUES (?, ?, ?, ?, ?, ?)");
$stmt->execute([$name, $email, $phone, $role, $profile_picture, $final_password]);
$stmt = db()->prepare("INSERT INTO users (name, email, phone, role, profile_picture, password, role_id) VALUES (?, ?, ?, ?, ?, ?, ?)");
$stmt->execute([$name, $email, $phone, $role, $profile_picture, $final_password, $role_id]);
}
header('Location: ' . app_url('admin.php', ['page' => 'users']));
exit;
@ -90,17 +94,17 @@ $offset = ($page_num - 1) * $limit;
$where = "1=1";
$params = [];
if ($search) {
$where .= " AND (name LIKE ? OR email LIKE ?)";
$where .= " AND (u.name LIKE ? OR u.email LIKE ?)";
$params[] = "%$search%";
$params[] = "%$search%";
}
$stmt = db()->prepare("SELECT COUNT(*) FROM users WHERE $where");
$stmt = db()->prepare("SELECT COUNT(*) FROM users u WHERE $where");
$stmt->execute($params);
$total = $stmt->fetchColumn();
$total_pages = ceil($total / $limit);
$stmt = db()->prepare("SELECT * FROM users WHERE $where ORDER BY id DESC LIMIT ? OFFSET ?");
$stmt = db()->prepare("SELECT u.*, r.name as role_name FROM users u LEFT JOIN roles r ON u.role_id = r.id WHERE $where ORDER BY u.id DESC LIMIT ? OFFSET ?");
$params[] = $limit;
$params[] = $offset;
foreach($params as $k => $v) {
@ -109,15 +113,20 @@ foreach($params as $k => $v) {
$stmt->execute();
$users = $stmt->fetchAll(PDO::FETCH_ASSOC);
$stmt = db()->query("SELECT id, name FROM roles ORDER BY name ASC");
$roles_list = $stmt->fetchAll(PDO::FETCH_ASSOC);
?>
<div class="section-header mb-4 d-flex justify-content-between align-items-center">
<div>
<h1 class="section-title mb-2"><?= h(t('Platform Users', 'مستخدمي المنصة')) ?></h1>
<p class="text-secondary mb-0"><?= h(t('Manage administrators and staff.', 'إدارة المسؤولين والموظفين.')) ?></p>
</div>
<?php if (has_permission('users', 'add')): ?>
<button class="btn btn-dark" data-bs-toggle="modal" data-bs-target="#userModal" onclick="editUser(null)">
<?= h(t('Add User', 'إضافة مستخدم')) ?>
</button>
<?php endif; ?>
</div>
<div class="panel-card mb-4">
@ -139,7 +148,7 @@ $users = $stmt->fetchAll(PDO::FETCH_ASSOC);
<th class="px-4 py-3"><?= h(t('Name', 'الاسم')) ?></th>
<th class="py-3"><?= h(t('Email', 'البريد الإلكتروني')) ?></th>
<th class="py-3"><?= h(t('Phone', 'الهاتف')) ?></th>
<th class="py-3"><?= h(t('Role', 'الدور')) ?></th>
<th class="py-3"><?= h(t('Role Group', 'الصلاحيات')) ?></th>
<th class="px-4 py-3 text-end"><?= h(t('Actions', 'الإجراءات')) ?></th>
</tr>
</thead>
@ -163,7 +172,9 @@ $users = $stmt->fetchAll(PDO::FETCH_ASSOC);
<td class="py-3"><?= h($u['email']) ?></td>
<td class="py-3"><?= h($u['phone']) ?></td>
<td class="py-3">
<?php if ($u['role'] === 'admin'): ?>
<?php if ($u['role_name']): ?>
<span class="badge bg-info text-dark"><?= h($u['role_name']) ?></span>
<?php elseif ($u['role'] === 'admin'): ?>
<span class="badge bg-primary text-white"><?= h(t('Admin', 'مسؤول')) ?></span>
<?php else: ?>
<span class="badge bg-secondary text-white"><?= h(t('User', 'مستخدم')) ?></span>
@ -171,10 +182,13 @@ $users = $stmt->fetchAll(PDO::FETCH_ASSOC);
</td>
<td class="px-4 py-3 text-end">
<div class="btn-group btn-group-sm">
<?php if (has_permission('users', 'edit')): ?>
<button class="btn btn-outline-dark" onclick='editUser(<?= htmlspecialchars(json_encode($u), ENT_QUOTES) ?>)'>
<?= h(t('Edit', 'تعديل')) ?>
</button>
<?php if ((int)$u['id'] !== (int)$_SESSION['user_id']): ?>
<?php endif; ?>
<?php if (has_permission('users', 'delete') && (int)$u['id'] !== (int)$_SESSION['user_id']): ?>
<form method="POST" action="admin_users.php" class="d-inline" onsubmit="return confirm('<?= h(t('Delete this user?', 'حذف هذا المستخدم؟')) ?>');">
<input type="hidden" name="action" value="delete">
<input type="hidden" name="id" value="<?= h($u['id']) ?>">
@ -231,7 +245,16 @@ $users = $stmt->fetchAll(PDO::FETCH_ASSOC);
<input type="text" class="form-control" name="phone" id="userPhone">
</div>
<div class="mb-3">
<label class="form-label"><?= h(t('Role', 'الدور')) ?></label>
<label class="form-label"><?= h(t('Role Group', 'مجموعة الصلاحيات')) ?></label>
<select class="form-select" name="role_id" id="userRoleId">
<option value=""><?= h(t('None (Basic User)', 'بدون (مستخدم عادي)')) ?></option>
<?php foreach($roles_list as $rl): ?>
<option value="<?= $rl['id'] ?>"><?= h($rl['name']) ?></option>
<?php endforeach; ?>
</select>
</div>
<div class="mb-3" style="display:none;">
<label class="form-label"><?= h(t('Role (Legacy)', 'الدور القديم')) ?></label>
<select class="form-select" name="role" id="userRole">
<option value="user"><?= h(t('User', 'مستخدم')) ?></option>
<option value="admin"><?= h(t('Admin', 'مسؤول')) ?></option>
@ -264,6 +287,7 @@ function editUser(user) {
document.getElementById('userEmail').value = user.email;
document.getElementById('userPhone').value = user.phone || '';
document.getElementById('userRole').value = user.role;
document.getElementById('userRoleId').value = user.role_id || '';
document.getElementById('userPassword').required = false;
document.getElementById('userPasswordHelp').style.display = 'inline';
} else {
@ -274,8 +298,9 @@ function editUser(user) {
document.getElementById('userEmail').value = '';
document.getElementById('userPhone').value = '';
document.getElementById('userRole').value = 'user';
document.getElementById('userRoleId').value = '';
document.getElementById('userPassword').required = true;
document.getElementById('userPasswordHelp').style.display = 'none';
}
}
</script>
</script>

View File

@ -0,0 +1,57 @@
<?php
require_once __DIR__ . '/../../includes/app.php';
try {
$pdo = db();
// Create roles table
$pdo->exec("CREATE TABLE IF NOT EXISTS `roles` (
`id` INT AUTO_INCREMENT PRIMARY KEY,
`name` VARCHAR(255) NOT NULL,
`description` TEXT,
`is_system` TINYINT(1) DEFAULT 0,
`created_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;");
// Create role_permissions table
$pdo->exec("CREATE TABLE IF NOT EXISTS `role_permissions` (
`id` INT AUTO_INCREMENT PRIMARY KEY,
`role_id` INT NOT NULL,
`page` VARCHAR(50) NOT NULL,
`can_view` TINYINT(1) DEFAULT 0,
`can_add` TINYINT(1) DEFAULT 0,
`can_edit` TINYINT(1) DEFAULT 0,
`can_delete` TINYINT(1) DEFAULT 0,
UNIQUE KEY `role_page` (`role_id`, `page`),
FOREIGN KEY (`role_id`) REFERENCES `roles`(`id`) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;");
// Alter users table to add role_id
// Check if column exists
$stmt = $pdo->prepare("SHOW COLUMNS FROM `users` LIKE 'role_id'");
$stmt->execute();
if ($stmt->rowCount() == 0) {
$pdo->exec("ALTER TABLE `users` ADD COLUMN `role_id` INT NULL DEFAULT NULL");
$pdo->exec("ALTER TABLE `users` ADD CONSTRAINT `fk_user_role` FOREIGN KEY (`role_id`) REFERENCES `roles`(`id`) ON DELETE SET NULL");
}
// Insert 'Super Admin' role if it doesn't exist
$stmt = $pdo->prepare("SELECT id FROM roles WHERE name = 'Super Admin'");
$stmt->execute();
$adminRole = $stmt->fetch(PDO::FETCH_ASSOC);
if (!$adminRole) {
$pdo->exec("INSERT INTO roles (name, description, is_system) VALUES ('Super Admin', 'Full access to all pages and actions', 1)");
$adminRoleId = $pdo->lastInsertId();
} else {
$adminRoleId = $adminRole['id'];
}
// Assign 'Super Admin' role to all existing admins
$stmt = $pdo->prepare("UPDATE users SET role_id = ? WHERE role = 'admin' AND role_id IS NULL");
$stmt->execute([$adminRoleId]);
echo "Migration completed successfully.";
} catch (PDOException $e) {
echo "Migration failed: " . $e->getMessage();
}

View File

@ -14,3 +14,35 @@ function get_logged_in_user() {
$stmt->execute([$_SESSION['user_id']]);
return $stmt->fetch(PDO::FETCH_ASSOC) ?: null;
}
function has_permission($page, $action = 'view') {
$user = get_logged_in_user();
if (!$user) return false;
// Super Admins bypass permissions. Fallback logic.
if ($user['role'] === 'admin' && empty($user['role_id'])) return true;
if (!empty($user['role_id'])) {
$stmt = db()->prepare("SELECT is_system FROM roles WHERE id = ?");
$stmt->execute([$user['role_id']]);
$role = $stmt->fetch(PDO::FETCH_ASSOC);
if ($role && $role['is_system']) return true; // Super admin
$stmt = db()->prepare("SELECT * FROM role_permissions WHERE role_id = ? AND page = ?");
$stmt->execute([$user['role_id'], $page]);
$perms = $stmt->fetch(PDO::FETCH_ASSOC);
if ($perms) {
$col = 'can_' . $action;
return !empty($perms[$col]);
}
}
return false;
}
function require_permission($page, $action = 'view') {
if (!has_permission($page, $action)) {
http_response_code(403);
die("403 Forbidden - You don't have permission to perform this action.");
}
}