DSP Project first push, date: 29/01/2026
This commit is contained in:
119
admin/app_log.php
Normal file
119
admin/app_log.php
Normal file
@@ -0,0 +1,119 @@
|
||||
<?php
|
||||
session_start();
|
||||
require_once __DIR__ . '/../config.php';
|
||||
require_once __DIR__ . '/../includes/auth.php';
|
||||
|
||||
redirect_if_not_role('DAC Staff', '../index.php');
|
||||
|
||||
$logPath = realpath(__DIR__ . '/../logs/app.log');
|
||||
$canReadLog = $logPath && is_readable($logPath);
|
||||
$lineLimit = filter_input(INPUT_GET, 'lines', FILTER_VALIDATE_INT);
|
||||
$lineLimit = $lineLimit ?: 200;
|
||||
$lineLimit = max(50, min(2000, $lineLimit));
|
||||
$logEntries = [];
|
||||
$logMeta = [
|
||||
'size' => null,
|
||||
'modified' => null,
|
||||
];
|
||||
|
||||
if ($canReadLog) {
|
||||
$logMeta['size'] = filesize($logPath);
|
||||
$logMeta['modified'] = filemtime($logPath);
|
||||
|
||||
if (isset($_GET['download'])) {
|
||||
header('Content-Type: text/plain');
|
||||
header('Content-Disposition: attachment; filename="app.log"');
|
||||
header('Content-Length: ' . $logMeta['size']);
|
||||
readfile($logPath);
|
||||
exit;
|
||||
}
|
||||
|
||||
try {
|
||||
$logEntries = tail_file($logPath, $lineLimit);
|
||||
} catch (RuntimeException $e) {
|
||||
$canReadLog = false;
|
||||
$logError = $e->getMessage();
|
||||
}
|
||||
} else {
|
||||
$logError = 'The application log file is missing or unreadable.';
|
||||
}
|
||||
|
||||
function tail_file(string $path, int $lines): array
|
||||
{
|
||||
$file = new SplFileObject($path, 'r');
|
||||
$file->seek(PHP_INT_MAX);
|
||||
$lastLine = $file->key();
|
||||
$startLine = max($lastLine - $lines + 1, 0);
|
||||
$file->seek($startLine);
|
||||
|
||||
$buffer = [];
|
||||
while (!$file->eof()) {
|
||||
$buffer[] = rtrim($file->current(), "\r\n");
|
||||
$file->next();
|
||||
}
|
||||
|
||||
return $buffer;
|
||||
}
|
||||
|
||||
$lastUpdated = $logMeta['modified'] ? date('Y-m-d H:i:s', $logMeta['modified']) . ' UTC' : 'Unknown';
|
||||
$logSizeHuman = $logMeta['size'] !== null ? number_format($logMeta['size'] / 1024, 1) . ' KB' : 'Unknown';
|
||||
|
||||
?>
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<?php include_once __DIR__ . '/../includes/header_admin.php'; ?>
|
||||
<body>
|
||||
<div class="wrapper">
|
||||
<?php include_once __DIR__ . '/../includes/nav_admin.php'; ?>
|
||||
<div class="main-content">
|
||||
<div class="d-flex justify-content-between align-items-center mb-4">
|
||||
<div>
|
||||
<h1 class="h4 mb-1">Application Log</h1>
|
||||
<p class="text-muted mb-0">Streaming the latest <?php echo htmlspecialchars($lineLimit); ?> lines from <code>logs/app.log</code>.</p>
|
||||
</div>
|
||||
<div class="text-end">
|
||||
<small class="text-muted d-block">Last updated: <?php echo htmlspecialchars($lastUpdated); ?></small>
|
||||
<small class="text-muted">Size: <?php echo htmlspecialchars($logSizeHuman); ?></small>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card shadow-sm border-0 mb-4">
|
||||
<div class="card-body d-flex flex-wrap gap-2 align-items-center">
|
||||
<form class="d-flex align-items-center gap-2" method="get">
|
||||
<label for="lines" class="form-label mb-0">Lines to display</label>
|
||||
<select class="form-select form-select-sm w-auto" id="lines" name="lines" onchange="this.form.submit()">
|
||||
<?php foreach ([100, 200, 500, 1000, 2000] as $option): ?>
|
||||
<option value="<?php echo $option; ?>" <?php echo ($lineLimit === $option) ? 'selected' : ''; ?>>
|
||||
<?php echo $option; ?>
|
||||
</option>
|
||||
<?php endforeach; ?>
|
||||
</select>
|
||||
</form>
|
||||
<?php if ($canReadLog): ?>
|
||||
<a class="btn btn-sm btn-outline-secondary" href="?lines=<?php echo $lineLimit; ?>">
|
||||
<i class="fas fa-sync-alt me-1"></i> Refresh
|
||||
</a>
|
||||
<a class="btn btn-sm btn-outline-primary" href="?download=1">
|
||||
<i class="fas fa-download me-1"></i> Download
|
||||
</a>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<?php if (!$canReadLog): ?>
|
||||
<div class="alert alert-danger rounded shadow-sm">
|
||||
<strong>Cannot read log file.</strong>
|
||||
<div><?php echo htmlspecialchars($logError ?? 'Unknown error.'); ?></div>
|
||||
</div>
|
||||
<?php else: ?>
|
||||
<div class="card shadow-sm border-0">
|
||||
<div class="card-body">
|
||||
<pre class="bg-dark text-light p-3 rounded small" style="max-height: 60vh; overflow:auto;"><?php echo htmlspecialchars(implode("\n", $logEntries)); ?></pre>
|
||||
</div>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
</div>
|
||||
<?php include_once __DIR__ . '/../includes/footer_admin.php'; ?>
|
||||
</body>
|
||||
</html>
|
||||
171
admin/dashboard.php
Normal file
171
admin/dashboard.php
Normal file
@@ -0,0 +1,171 @@
|
||||
<?php
|
||||
// Start session and include necessary files
|
||||
session_start();
|
||||
require_once '../config.php';
|
||||
require_once '../includes/auth.php';
|
||||
require_once '../classes/User.php';
|
||||
require_once '../classes/Announcement.php';
|
||||
require_once '../classes/DataSource.php'; // Still needed for overall data source counts
|
||||
require_once '../classes/Classifications.php'; // For classification counts
|
||||
require_once '../classes/Aboutus.php';
|
||||
require_once '../classes/Contactus.php'; // For feedback/contact messages
|
||||
require_once '../classes/Faq.php';
|
||||
require_once '../classes/Slide.php'; // Include Slide class
|
||||
|
||||
// Redirect if not logged in or not a DAC Staff
|
||||
redirect_if_not_logged_in('../index.php');
|
||||
redirect_if_not_role('DAC Staff', '../index.php');
|
||||
|
||||
// Initialize classes
|
||||
$user = new User($pdo);
|
||||
$announcement = new Announcement($pdo);
|
||||
$dataSource = new DataSource($pdo);
|
||||
$classification = new Classifications($pdo);
|
||||
$aboutUs = new Aboutus($pdo);
|
||||
$contactUs = new Contactus($pdo);
|
||||
$faq = new Faq($pdo);
|
||||
$slideManager = new Slide($pdo);
|
||||
|
||||
// Fetch dashboard data
|
||||
$totalUsers = $user->getTotalUsers();
|
||||
$totalAnnouncements = $announcement->getTotalAnnouncements();
|
||||
$totalDataSources = $dataSource->getTotalDataSources();
|
||||
$totalCategories = $classification->getTotalCategories();
|
||||
$totalDataTypes = $classification->getTotalDataTypes();
|
||||
$totalFeedback = $contactUs->getTotalFeedback();
|
||||
$totalFaqs = $faq->getTotalFaqs();
|
||||
$totalSlides = $slideManager->getTotalSlides();
|
||||
$pendingPermissions = $dataSource->getPendingPermissionRequestsCount(); // This correctly gets ALL pending for admin dashboard
|
||||
?>
|
||||
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<!-- Header -->
|
||||
<?php
|
||||
// Include header file for admin pages
|
||||
include_once("../includes/header_admin.php");
|
||||
?>
|
||||
|
||||
<body>
|
||||
<div class="wrapper">
|
||||
<!-- Sidebar -->
|
||||
<?php
|
||||
// Include header file for admin pages
|
||||
include_once("../includes/nav_admin.php");
|
||||
?>
|
||||
<!-- Main Content -->
|
||||
<div class="main-content">
|
||||
<nav class="navbar navbar-expand-lg navbar-light bg-light mb-4 rounded-3">
|
||||
<div class="container-fluid">
|
||||
<a class="navbar-brand" href="#">Admin Dashboard</a>
|
||||
<div class="d-flex">
|
||||
<span class="navbar-text me-3">
|
||||
Welcome, <?php echo htmlspecialchars($_SESSION['username']); ?>!
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<?php
|
||||
// Display session messages
|
||||
if (isset($_SESSION['message'])) {
|
||||
echo '<div class="alert alert-' . $_SESSION['message_type'] . ' alert-dismissible fade show rounded-pill" role="alert">' . htmlspecialchars($_SESSION['message']) . '<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button></div>';
|
||||
unset($_SESSION['message']);
|
||||
unset($_SESSION['message_type']);
|
||||
}
|
||||
?>
|
||||
|
||||
<div class="row g-4">
|
||||
<div class="col-md-4 col-sm-6">
|
||||
<div class="card text-center p-4">
|
||||
<div class="card-body">
|
||||
<i class="fas fa-users card-icon mb-3"></i>
|
||||
<h5 class="card-title">Total Users</h5>
|
||||
<p class="card-text fs-2 fw-bold text-primary"><?php echo $totalUsers; ?></p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-4 col-sm-6">
|
||||
<div class="card text-center p-4">
|
||||
<div class="card-body">
|
||||
<i class="fas fa-bullhorn card-icon mb-3"></i>
|
||||
<h5 class="card-title">Total Announcements</h5>
|
||||
<p class="card-text fs-2 fw-bold text-success"><?php echo $totalAnnouncements; ?></p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-4 col-sm-6">
|
||||
<div class="card text-center p-4">
|
||||
<div class="card-body">
|
||||
<i class="fas fa-database card-icon mb-3"></i>
|
||||
<h5 class="card-title">Total Data Sources</h5>
|
||||
<p class="card-text fs-2 fw-bold text-info"><?php echo $totalDataSources; ?></p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-4 col-sm-6">
|
||||
<div class="card text-center p-4">
|
||||
<div class="card-body">
|
||||
<i class="fas fa-folder-open card-icon mb-3"></i>
|
||||
<h5 class="card-title">Data Categories</h5>
|
||||
<p class="card-text fs-2 fw-bold text-warning"><?php echo $totalCategories; ?></p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-4 col-sm-6">
|
||||
<div class="card text-center p-4">
|
||||
<div class="card-body">
|
||||
<i class="fas fa-file-alt card-icon mb-3"></i>
|
||||
<h5 class="card-title">Data Types</h5>
|
||||
<p class="card-text fs-2 fw-bold text-danger"><?php echo $totalDataTypes; ?></p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-4 col-sm-6">
|
||||
<div class="card text-center p-4">
|
||||
<div class="card-body">
|
||||
<i class="fas fa-handshake card-icon mb-3"></i>
|
||||
<h5 class="card-title">Pending Permissions</h5>
|
||||
<p class="card-text fs-2 fw-bold text-secondary"><?php echo $pendingPermissions; ?></p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-4 col-sm-6">
|
||||
<div class="card text-center p-4">
|
||||
<div class="card-body">
|
||||
<i class="fas fa-comments card-icon mb-3"></i>
|
||||
<h5 class="card-title">New Feedback</h5>
|
||||
<p class="card-text fs-2 fw-bold text-primary"><?php echo $totalFeedback; ?></p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-4 col-sm-6">
|
||||
<div class="card text-center p-4">
|
||||
<div class="card-body">
|
||||
<i class="fas fa-question card-icon mb-3"></i>
|
||||
<h5 class="card-title">Total FAQs</h5>
|
||||
<p class="card-text fs-2 fw-bold text-success"><?php echo $totalFaqs; ?></p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-4 col-sm-6">
|
||||
<div class="card text-center p-4">
|
||||
<div class="card-body">
|
||||
<i class="fas fa-images card-icon mb-3"></i>
|
||||
<h5 class="card-title">Total Slides</h5>
|
||||
<p class="card-text fs-2 fw-bold text-warning"><?php echo $totalSlides; ?></p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Footer -->
|
||||
<?php
|
||||
// Include Footer file for owner pages
|
||||
include_once("../includes/footer_admin.php");
|
||||
?>
|
||||
</body>
|
||||
</html>
|
||||
216
admin/manage_aboutus.php
Normal file
216
admin/manage_aboutus.php
Normal file
@@ -0,0 +1,216 @@
|
||||
<?php
|
||||
// Start session and include necessary files
|
||||
session_start();
|
||||
require_once '../config.php';
|
||||
require_once '../includes/auth.php';
|
||||
require_once '../classes/Aboutus.php';
|
||||
require_once '../classes/User.php'; // To get person_id for the Aboutus class
|
||||
|
||||
// Redirect if not logged in or not a DAC Staff
|
||||
redirect_if_not_logged_in('../index.php');
|
||||
redirect_if_not_role('DAC Staff', '../index.php');
|
||||
|
||||
// Initialize Aboutus class
|
||||
$aboutUs = new Aboutus($pdo);
|
||||
$user = new User($pdo); // To get the person_id of the logged-in user
|
||||
|
||||
$action = $_GET['action'] ?? '';
|
||||
$id = $_GET['id'] ?? null;
|
||||
|
||||
// Handle form submissions
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
$action_type = $_POST['action_type'] ?? ''; // 'add' or 'edit' or 'delete'
|
||||
$title = trim($_POST['title'] ?? '');
|
||||
$description = trim($_POST['description'] ?? '');
|
||||
$about_id = $_POST['about_id'] ?? null;
|
||||
|
||||
// Get the person_id of the currently logged-in user
|
||||
$currentUserDetails = $user->getUserDetails($_SESSION['user_id']);
|
||||
$fkisp_id_of = $currentUserDetails['fkisp_id_of'];
|
||||
|
||||
if ($action_type === 'delete') {
|
||||
if ($about_id) {
|
||||
try {
|
||||
$aboutUs->deleteAboutUs($about_id);
|
||||
set_message('About Us entry deleted successfully!', 'success');
|
||||
} catch (Exception $e) {
|
||||
set_message('Error deleting About Us entry: ' . $e->getMessage(), 'danger');
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (empty($title) || empty($description)) {
|
||||
set_message('Title and description cannot be empty.', 'danger');
|
||||
} else {
|
||||
try {
|
||||
if ($action_type === 'add') {
|
||||
$aboutUs->addAboutUs($title, $description, $_SESSION['user_id'], $fkisp_id_of);
|
||||
set_message('About Us entry added successfully!', 'success');
|
||||
} elseif ($action_type === 'edit' && $about_id) {
|
||||
$aboutUs->updateAboutUs($about_id, $title, $description, $_SESSION['user_id'], $fkisp_id_of);
|
||||
set_message('About Us entry updated successfully!', 'success');
|
||||
}
|
||||
} catch (Exception $e) {
|
||||
set_message('Error: ' . $e->getMessage(), 'danger');
|
||||
}
|
||||
}
|
||||
}
|
||||
header('Location: manage_aboutus.php');
|
||||
exit();
|
||||
}
|
||||
|
||||
// Fetch About Us entries for display
|
||||
$aboutUsEntries = $aboutUs->getAllAboutUs();
|
||||
|
||||
// Prepare data for editing if action is 'edit'
|
||||
$editAboutUs = null;
|
||||
if ($action === 'edit' && $id) {
|
||||
$editAboutUs = $aboutUs->getAboutUsById($id);
|
||||
}
|
||||
?>
|
||||
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<!-- Header -->
|
||||
<?php
|
||||
// Include header file for admin pages
|
||||
include_once("../includes/header_admin.php");
|
||||
?>
|
||||
<body>
|
||||
<div class="wrapper">
|
||||
<!-- Sidebar -->
|
||||
<?php
|
||||
// Include header file for admin pages
|
||||
include_once("../includes/nav_admin.php");
|
||||
?>
|
||||
|
||||
<!-- Main Content -->
|
||||
<div class="main-content">
|
||||
<nav class="navbar navbar-expand-lg navbar-light bg-light mb-4 rounded-3">
|
||||
<div class="container-fluid">
|
||||
<a class="navbar-brand" href="#">Manage About Us</a>
|
||||
<div class="d-flex">
|
||||
<span class="navbar-text me-3">
|
||||
Welcome, <?php echo htmlspecialchars($_SESSION['username']); ?>!
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<?php
|
||||
// Display session messages
|
||||
if (isset($_SESSION['message'])) {
|
||||
echo '<div class="alert alert-' . $_SESSION['message_type'] . ' alert-dismissible fade show rounded" role="alert">' . htmlspecialchars($_SESSION['message']) . '<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button></div>';
|
||||
unset($_SESSION['message']);
|
||||
unset($_SESSION['message_type']);
|
||||
}
|
||||
?>
|
||||
|
||||
<div class="card mb-4">
|
||||
<div class="card-header bg-primary text-white">
|
||||
<h5 class="mb-0"><?php echo $editAboutUs ? 'Edit' : 'Add New'; ?> About Us Entry</h5>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<form action="manage_aboutus.php" method="POST">
|
||||
<input type="hidden" name="action_type" value="<?php echo $editAboutUs ? 'edit' : 'add'; ?>">
|
||||
<?php if ($editAboutUs): ?>
|
||||
<input type="hidden" name="about_id" value="<?php echo htmlspecialchars($editAboutUs['pkdspsabout_id']); ?>">
|
||||
<?php endif; ?>
|
||||
|
||||
<div class="mb-3">
|
||||
<label for="title" class="form-label">Title</label>
|
||||
<input type="text" class="form-control rounded" id="title" name="title" value="<?php echo htmlspecialchars($editAboutUs['dspsabout_title_en'] ?? ''); ?>" required>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="description" class="form-label d-flex justify-content-between align-items-center">
|
||||
<span>Description</span>
|
||||
</label>
|
||||
<textarea class="form-control rounded-3" id="description" name="description" rows="8" required><?php echo htmlspecialchars($editAboutUs['dspsabout_description'] ?? ''); ?></textarea>
|
||||
<div class="form-text">Format paragraphs, bullet lists, and links so the public About Us page is easy to read.</div>
|
||||
</div>
|
||||
<button type="submit" class="btn btn-primary rounded">
|
||||
<i class="fas fa-<?php echo $editAboutUs ? 'save' : 'plus'; ?> me-2"></i> <?php echo $editAboutUs ? 'Update' : 'Add'; ?> Entry
|
||||
</button>
|
||||
<?php if ($editAboutUs): ?>
|
||||
<a href="manage_aboutus.php" class="btn btn-secondary rounded ms-2">Cancel Edit</a>
|
||||
<?php endif; ?>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card">
|
||||
<div class="card-header text-white" style="background-color: #28a745;">
|
||||
<h5 class="mb-0">All About Us Entries</h5>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="table-responsive">
|
||||
<table class="table table-hover table-striped">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>ID</th>
|
||||
<th>Title</th>
|
||||
<th>Description</th>
|
||||
<th>Reg. Date</th>
|
||||
<th>Mod. Date</th>
|
||||
<th>Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<?php if (!empty($aboutUsEntries)): ?>
|
||||
<?php foreach ($aboutUsEntries as $entry): ?>
|
||||
<tr>
|
||||
<td><?php echo htmlspecialchars($entry['pkdspsabout_id']); ?></td>
|
||||
<td><?php echo htmlspecialchars($entry['dspsabout_title_en']); ?></td>
|
||||
<td><?php echo htmlspecialchars(substr($entry['dspsabout_description'], 0, 100)) . (strlen($entry['dspsabout_description']) > 100 ? '...' : ''); ?></td>
|
||||
<td><?php echo htmlspecialchars($entry['dspsabout_reg_datetime']); ?></td>
|
||||
<td><?php echo htmlspecialchars($entry['dspsabout_mod_datetime']); ?></td>
|
||||
<td>
|
||||
<a href="manage_aboutus.php?action=edit&id=<?php echo htmlspecialchars($entry['pkdspsabout_id']); ?>" class="btn btn-sm btn-warning rounded btn-action">
|
||||
<i class="fas fa-edit"></i>
|
||||
</a>
|
||||
<form action="manage_aboutus.php" method="POST" class="d-inline" onsubmit="return confirm('Are you sure you want to delete this About Us entry?');">
|
||||
<input type="hidden" name="action_type" value="delete">
|
||||
<input type="hidden" name="about_id" value="<?php echo htmlspecialchars($entry['pkdspsabout_id']); ?>">
|
||||
<button type="submit" class="btn btn-sm btn-danger rounded btn-action">
|
||||
<i class="fas fa-trash-alt"></i>
|
||||
</button>
|
||||
</form>
|
||||
</td>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
<?php else: ?>
|
||||
<tr>
|
||||
<td colspan="6" class="text-center">No About Us entries found.</td>
|
||||
</tr>
|
||||
<?php endif; ?>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Footer -->
|
||||
<?php
|
||||
// Include Footer file for owner pages
|
||||
include_once("../includes/footer_admin.php");
|
||||
?>
|
||||
<script src="https://cdn.jsdelivr.net/npm/@ckeditor/ckeditor5-build-classic@38.1.1/build/ckeditor.js"></script>
|
||||
<script>
|
||||
document.addEventListener('DOMContentLoaded', function () {
|
||||
var textarea = document.querySelector('#description');
|
||||
if (textarea && typeof ClassicEditor !== 'undefined') {
|
||||
ClassicEditor
|
||||
.create(textarea, {
|
||||
toolbar: [
|
||||
'heading','|','bold','italic','underline','bulletedList','numberedList','blockQuote',
|
||||
'|','link','insertTable','undo','redo'
|
||||
]
|
||||
})
|
||||
.catch(function (error) {
|
||||
console.error('Failed to initialise rich text editor', error);
|
||||
});
|
||||
}
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
260
admin/manage_announcements.php
Normal file
260
admin/manage_announcements.php
Normal file
@@ -0,0 +1,260 @@
|
||||
<?php
|
||||
// Start session and include necessary files
|
||||
session_start();
|
||||
require_once '../config.php';
|
||||
require_once '../includes/auth.php';
|
||||
require_once '../classes/Announcement.php';
|
||||
|
||||
// Redirect if not logged in or not a DAC Staff
|
||||
redirect_if_not_logged_in('../index.php');
|
||||
redirect_if_not_role('DAC Staff', '../index.php');
|
||||
|
||||
// Initialize Announcement class
|
||||
$announcement = new Announcement($pdo);
|
||||
|
||||
$action = $_GET['action'] ?? '';
|
||||
$id = $_GET['id'] ?? null;
|
||||
|
||||
// Handle form submissions
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
$action_type = $_POST['action_type'] ?? ''; // 'add' or 'edit' or 'delete'
|
||||
$title = trim($_POST['title'] ?? '');
|
||||
$description = trim($_POST['description'] ?? '');
|
||||
$status = $_POST['status'] ?? 'Draft';
|
||||
$announcement_id = $_POST['announcement_id'] ?? null;
|
||||
$current_photo = $_POST['current_photo'] ?? ''; // For editing, keep track of existing photo
|
||||
|
||||
if ($action_type === 'delete') {
|
||||
if ($announcement_id) {
|
||||
try {
|
||||
$announcement->deleteAnnouncement($announcement_id);
|
||||
set_message('Announcement deleted successfully!', 'success');
|
||||
} catch (Exception $e) {
|
||||
set_message('Error deleting announcement: ' . $e->getMessage(), 'danger');
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Handle photo upload
|
||||
$photoPath = $current_photo; // Default to current photo if not uploading new
|
||||
if (isset($_FILES['photo']) && $_FILES['photo']['error'] === UPLOAD_ERR_OK) {
|
||||
try {
|
||||
$photoPath = $announcement->handlePhotoUpload($_FILES['photo']);
|
||||
// If editing and a new photo is uploaded, delete the old one
|
||||
if ($action_type === 'edit' && !empty($current_photo) && $current_photo !== $photoPath) {
|
||||
unlink('../uploads/announcements/' . $current_photo); // Delete old file
|
||||
}
|
||||
} catch (Exception $e) {
|
||||
set_message('Photo upload error: ' . $e->getMessage(), 'danger');
|
||||
header('Location: manage_announcements.php');
|
||||
exit();
|
||||
}
|
||||
}
|
||||
|
||||
if (empty($title) || empty($description)) {
|
||||
set_message('Title and description cannot be empty.', 'danger');
|
||||
} else {
|
||||
try {
|
||||
if ($action_type === 'add') {
|
||||
$announcement->addAnnouncement($title, $description, $photoPath, $status, $_SESSION['user_id']);
|
||||
set_message('Announcement added successfully!', 'success');
|
||||
} elseif ($action_type === 'edit' && $announcement_id) {
|
||||
$announcement->updateAnnouncement($announcement_id, $title, $description, $photoPath, $status, $_SESSION['user_id']);
|
||||
set_message('Announcement updated successfully!', 'success');
|
||||
}
|
||||
} catch (Exception $e) {
|
||||
set_message('Error: ' . $e->getMessage(), 'danger');
|
||||
}
|
||||
}
|
||||
}
|
||||
header('Location: manage_announcements.php');
|
||||
exit();
|
||||
}
|
||||
|
||||
// Fetch announcements for display
|
||||
$announcements = $announcement->getAllAnnouncements();
|
||||
|
||||
// Prepare data for editing if action is 'edit'
|
||||
$editAnnouncement = null;
|
||||
if ($action === 'edit' && $id) {
|
||||
$editAnnouncement = $announcement->getAnnouncementById($id);
|
||||
}
|
||||
?>
|
||||
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<!-- Header -->
|
||||
<?php
|
||||
// Include header file for admin pages
|
||||
include_once("../includes/header_admin.php");
|
||||
?>
|
||||
<body>
|
||||
<div class="wrapper">
|
||||
<!-- Sidebar -->
|
||||
<?php
|
||||
// Include header file for admin pages
|
||||
include_once("../includes/nav_admin.php");
|
||||
?>
|
||||
<!-- Main Content -->
|
||||
<div class="main-content">
|
||||
<nav class="navbar navbar-expand-lg navbar-light bg-light mb-4 rounded-3">
|
||||
<div class="container-fluid">
|
||||
<a class="navbar-brand" href="#">Manage Announcements</a>
|
||||
<div class="d-flex">
|
||||
<span class="navbar-text me-3">
|
||||
Welcome, <?php echo htmlspecialchars($_SESSION['username']); ?>!
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<?php
|
||||
// Display session messages
|
||||
if (isset($_SESSION['message'])) {
|
||||
echo '<div class="alert alert-' . $_SESSION['message_type'] . ' alert-dismissible fade show rounded" role="alert">' . htmlspecialchars($_SESSION['message']) . '<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button></div>';
|
||||
unset($_SESSION['message']);
|
||||
unset($_SESSION['message_type']);
|
||||
}
|
||||
?>
|
||||
|
||||
<div class="card mb-4">
|
||||
<div class="card-header bg-primary text-white">
|
||||
<h5 class="mb-0"><?php echo $editAnnouncement ? 'Edit' : 'Add New'; ?> Announcement</h5>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<form action="manage_announcements.php" method="POST" enctype="multipart/form-data">
|
||||
<input type="hidden" name="action_type" value="<?php echo $editAnnouncement ? 'edit' : 'add'; ?>">
|
||||
<?php if ($editAnnouncement): ?>
|
||||
<input type="hidden" name="announcement_id" value="<?php echo htmlspecialchars($editAnnouncement['pkdspsann_id']); ?>">
|
||||
<input type="hidden" name="current_photo" value="<?php echo htmlspecialchars($editAnnouncement['dspsann_photopath']); ?>">
|
||||
<?php endif; ?>
|
||||
|
||||
<div class="mb-3">
|
||||
<label for="title" class="form-label">Title</label>
|
||||
<input type="text" class="form-control rounded" id="title" name="title" value="<?php echo htmlspecialchars($editAnnouncement['dspsann_title'] ?? ''); ?>" required>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="description" class="form-label d-flex justify-content-between align-items-center">
|
||||
<span>Description</span>
|
||||
</label>
|
||||
<textarea class="form-control rounded-3" id="description" name="description" rows="8" required><?php echo htmlspecialchars($editAnnouncement['dspsann_description'] ?? ''); ?></textarea>
|
||||
<div class="form-text">Use the toolbar to format bullet lists, emphasize important actions, and link to additional resources.</div>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="photo" class="form-label">Photo (Optional)</label>
|
||||
<input type="file" class="form-control rounded" id="photo" name="photo" accept="image/*">
|
||||
<?php if ($editAnnouncement && !empty($editAnnouncement['dspsann_photopath'])): ?>
|
||||
<div class="mt-2">
|
||||
Current Photo: <img src="../uploads/announcements/<?php echo htmlspecialchars($editAnnouncement['dspsann_photopath']); ?>" alt="Announcement Photo" class="announcement-img">
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="status" class="form-label">Status</label>
|
||||
<select class="form-select rounded" id="status" name="status" required>
|
||||
<option value="Draft" <?php echo ($editAnnouncement && $editAnnouncement['dspsann_status'] == 'Draft') ? 'selected' : ''; ?>>Draft</option>
|
||||
<option value="Published" <?php echo ($editAnnouncement && $editAnnouncement['dspsann_status'] == 'Published') ? 'selected' : ''; ?>>Published</option>
|
||||
<option value="Archived" <?php echo ($editAnnouncement && $editAnnouncement['dspsann_status'] == 'Archived') ? 'selected' : ''; ?>>Archived</option>
|
||||
</select>
|
||||
</div>
|
||||
<button type="submit" class="btn btn-primary rounded">
|
||||
<i class="fas fa-<?php echo $editAnnouncement ? 'save' : 'plus'; ?> me-2"></i> <?php echo $editAnnouncement ? 'Update' : 'Add'; ?> Announcement
|
||||
</button>
|
||||
<?php if ($editAnnouncement): ?>
|
||||
<a href="manage_announcements.php" class="btn btn-secondary rounded ms-2">Cancel Edit</a>
|
||||
<?php endif; ?>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card">
|
||||
<div class="card-header text-white" style="background-color: #28a745;">
|
||||
<h5 class="mb-0">All Announcements</h5>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="table-responsive">
|
||||
<table class="table table-hover table-striped">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>ID</th>
|
||||
<th>Title</th>
|
||||
<th>Description</th>
|
||||
<th>Photo</th>
|
||||
<th>Status</th>
|
||||
<th>Reg. Date</th>
|
||||
<th>Mod. Date</th>
|
||||
<th>Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<?php if (!empty($announcements)): ?>
|
||||
<?php foreach ($announcements as $ann): ?>
|
||||
<tr>
|
||||
<td><?php echo htmlspecialchars($ann['pkdspsann_id']); ?></td>
|
||||
<td><?php echo htmlspecialchars($ann['dspsann_title']); ?></td>
|
||||
<td><?php echo htmlspecialchars(substr($ann['dspsann_description'], 0, 100)) . (strlen($ann['dspsann_description']) > 100 ? '...' : ''); ?></td>
|
||||
<td>
|
||||
<?php if (!empty($ann['dspsann_photopath'])): ?>
|
||||
<img src="../uploads/announcements/<?php echo htmlspecialchars($ann['dspsann_photopath']); ?>" alt="Photo" class="announcement-img">
|
||||
<?php else: ?>
|
||||
N/A
|
||||
<?php endif; ?>
|
||||
</td>
|
||||
<td><span class="badge bg-<?php
|
||||
if ($ann['dspsann_status'] == 'Published') echo 'success';
|
||||
else if ($ann['dspsann_status'] == 'Draft') echo 'warning';
|
||||
else echo 'secondary';
|
||||
?>"><?php echo htmlspecialchars($ann['dspsann_status']); ?></span></td>
|
||||
<td><?php echo htmlspecialchars($ann['dspsann_reg_datetime']); ?></td>
|
||||
<td><?php echo htmlspecialchars($ann['dspsann_mod_datetime']); ?></td>
|
||||
<td>
|
||||
<a href="manage_announcements.php?action=edit&id=<?php echo htmlspecialchars($ann['pkdspsann_id']); ?>" class="btn btn-sm btn-warning rounded btn-action">
|
||||
<i class="fas fa-edit"></i>
|
||||
</a>
|
||||
<form action="manage_announcements.php" method="POST" class="d-inline" onsubmit="return confirm('Are you sure you want to delete this announcement? This action cannot be undone.');">
|
||||
<input type="hidden" name="action_type" value="delete">
|
||||
<input type="hidden" name="announcement_id" value="<?php echo htmlspecialchars($ann['pkdspsann_id']); ?>">
|
||||
<button type="submit" class="btn btn-sm btn-danger rounded btn-action">
|
||||
<i class="fas fa-trash-alt"></i>
|
||||
</button>
|
||||
</form>
|
||||
</td>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
<?php else: ?>
|
||||
<tr>
|
||||
<td colspan="8" class="text-center">No announcements found.</td>
|
||||
</tr>
|
||||
<?php endif; ?>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Footer -->
|
||||
<?php
|
||||
// Include Footer file for owner pages
|
||||
include_once("../includes/footer_admin.php");
|
||||
?>
|
||||
<script src="https://cdn.jsdelivr.net/npm/@ckeditor/ckeditor5-build-classic@38.1.1/build/ckeditor.js"></script>
|
||||
<script>
|
||||
document.addEventListener('DOMContentLoaded', function () {
|
||||
var textarea = document.querySelector('#description');
|
||||
if (textarea && typeof ClassicEditor !== 'undefined') {
|
||||
ClassicEditor
|
||||
.create(textarea, {
|
||||
toolbar: [
|
||||
'heading','|','bold','italic','underline','bulletedList','numberedList','blockQuote',
|
||||
'|','link','insertTable','undo','redo'
|
||||
]
|
||||
})
|
||||
.catch(function (error) {
|
||||
console.error('Failed to initialise rich text editor', error);
|
||||
});
|
||||
}
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
269
admin/manage_classifications.php
Normal file
269
admin/manage_classifications.php
Normal file
@@ -0,0 +1,269 @@
|
||||
<?php
|
||||
// Start session and include necessary files
|
||||
session_start();
|
||||
require_once '../config.php';
|
||||
require_once '../includes/auth.php';
|
||||
require_once '../classes/Classifications.php'; // New Classifications class
|
||||
|
||||
// Redirect if not logged in or not a DAC Staff
|
||||
redirect_if_not_logged_in('../index.php');
|
||||
redirect_if_not_role('DAC Staff', '../index.php');
|
||||
|
||||
// Initialize Classifications class
|
||||
$classification = new Classifications($pdo);
|
||||
|
||||
$action = $_GET['action'] ?? '';
|
||||
$id = $_GET['id'] ?? null;
|
||||
|
||||
// Handle form submissions
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
$type = $_POST['type'] ?? ''; // 'datatype' or 'category'
|
||||
$action_type = $_POST['action_type'] ?? ''; // 'add' or 'edit'
|
||||
$name_en = trim($_POST['name_en'] ?? '');
|
||||
$name_kh = trim($_POST['name_kh'] ?? '');
|
||||
$details = trim($_POST['details'] ?? '');
|
||||
$record_id = $_POST['record_id'] ?? null;
|
||||
|
||||
if (empty($name_en)) {
|
||||
set_message('English name cannot be empty.', 'danger');
|
||||
header('Location: manage_classifications.php');
|
||||
exit();
|
||||
}
|
||||
|
||||
try {
|
||||
if ($type === 'datatype') {
|
||||
if ($action_type === 'add') {
|
||||
$classification->addDataType($name_en, $name_kh, $_SESSION['user_id']);
|
||||
set_message('Data Type added successfully!', 'success');
|
||||
} elseif ($action_type === 'edit' && $record_id) {
|
||||
$classification->updateDataType($record_id, $name_en, $name_kh, $_SESSION['user_id']);
|
||||
set_message('Data Type updated successfully!', 'success');
|
||||
} elseif ($action_type === 'delete' && $record_id) {
|
||||
$classification->deleteDataType($record_id);
|
||||
set_message('Data Type deleted successfully!', 'success');
|
||||
}
|
||||
} elseif ($type === 'category') {
|
||||
if ($action_type === 'add') {
|
||||
$classification->addCategory($name_en, $details, $_SESSION['user_id']);
|
||||
set_message('Category added successfully!', 'success');
|
||||
} elseif ($action_type === 'edit' && $record_id) {
|
||||
$classification->updateCategory($record_id, $name_en, $details, $_SESSION['user_id']);
|
||||
set_message('Category updated successfully!', 'success');
|
||||
} elseif ($action_type === 'delete' && $record_id) {
|
||||
$classification->deleteCategory($record_id);
|
||||
set_message('Category deleted successfully!', 'success');
|
||||
}
|
||||
}
|
||||
} catch (Exception $e) {
|
||||
set_message('Error: ' . $e->getMessage(), 'danger');
|
||||
}
|
||||
header('Location: manage_classifications.php');
|
||||
exit();
|
||||
}
|
||||
|
||||
// Fetch data for display
|
||||
$dataTypes = $classification->getAllDataTypes();
|
||||
$categories = $classification->getAllCategories();
|
||||
|
||||
// Prepare data for editing if action is 'edit'
|
||||
$editDataType = null;
|
||||
$editCategory = null;
|
||||
if ($action === 'edit' && $id) {
|
||||
if ($_GET['type'] === 'datatype') {
|
||||
$editDataType = $classification->getDataTypeById($id);
|
||||
} elseif ($_GET['type'] === 'category') {
|
||||
$editCategory = $classification->getCategoryById($id);
|
||||
}
|
||||
}
|
||||
?>
|
||||
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<!-- Header -->
|
||||
<?php
|
||||
// Include header file for admin pages
|
||||
include_once("../includes/header_admin.php");
|
||||
?>
|
||||
<body>
|
||||
<div class="wrapper">
|
||||
<!-- Sidebar -->
|
||||
<?php
|
||||
// Include header file for admin pages
|
||||
include_once("../includes/nav_admin.php");
|
||||
?>
|
||||
|
||||
<!-- Main Content -->
|
||||
<div class="main-content">
|
||||
<nav class="navbar navbar-expand-lg navbar-light bg-light mb-4 rounded-3">
|
||||
<div class="container-fluid">
|
||||
<a class="navbar-brand" href="#">Manage Classifications</a>
|
||||
<div class="d-flex">
|
||||
<span class="navbar-text me-3">
|
||||
Welcome, <?php echo htmlspecialchars($_SESSION['username']); ?>!
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<?php
|
||||
// Display session messages
|
||||
if (isset($_SESSION['message'])) {
|
||||
echo '<div class="alert alert-' . $_SESSION['message_type'] . ' alert-dismissible fade show rounded" role="alert">' . htmlspecialchars($_SESSION['message']) . '<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button></div>';
|
||||
unset($_SESSION['message']);
|
||||
unset($_SESSION['message_type']);
|
||||
}
|
||||
?>
|
||||
|
||||
<!-- Data Type Management -->
|
||||
<div class="card mb-4">
|
||||
<div class="card-header bg-primary text-white">
|
||||
<h5 class="mb-0">Manage Data Types</h5>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<form action="manage_classifications.php" method="POST" class="mb-4">
|
||||
<input type="hidden" name="type" value="datatype">
|
||||
<input type="hidden" name="action_type" value="<?php echo $editDataType ? 'edit' : 'add'; ?>">
|
||||
<?php if ($editDataType): ?>
|
||||
<input type="hidden" name="record_id" value="<?php echo htmlspecialchars($editDataType['pkdspstds_id']); ?>">
|
||||
<?php endif; ?>
|
||||
<div class="row g-3 align-items-end">
|
||||
<div class="col-md-5">
|
||||
<label for="dataTypeNameEn" class="form-label">Data Type Name (English)</label>
|
||||
<input type="text" class="form-control rounded" id="dataTypeNameEn" name="name_en" value="<?php echo htmlspecialchars($editDataType['dspstds_name_en'] ?? ''); ?>" required>
|
||||
</div>
|
||||
<div class="col-md-5">
|
||||
<label for="dataTypeNameKh" class="form-label">Data Type Name (Khmer)</label>
|
||||
<input type="text" class="form-control rounded" id="dataTypeNameKh" name="name_kh" value="<?php echo htmlspecialchars($editDataType['dspstds_name_kh'] ?? ''); ?>">
|
||||
</div>
|
||||
<div class="col-md-2">
|
||||
<button type="submit" class="btn btn-primary w-100 rounded">
|
||||
<i class="fas fa-<?php echo $editDataType ? 'save' : 'plus'; ?> me-2"></i> <?php echo $editDataType ? 'Update' : 'Add'; ?> Data Type
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<div class="table-responsive">
|
||||
<table class="table table-hover table-striped">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>ID</th>
|
||||
<th>English Name</th>
|
||||
<th>Khmer Name</th>
|
||||
<th>Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<?php if (!empty($dataTypes)): ?>
|
||||
<?php foreach ($dataTypes as $dataType): ?>
|
||||
<tr>
|
||||
<td><?php echo htmlspecialchars($dataType['pkdspstds_id']); ?></td>
|
||||
<td><?php echo htmlspecialchars($dataType['dspstds_name_en']); ?></td>
|
||||
<td><?php echo htmlspecialchars($dataType['dspstds_name_kh']); ?></td>
|
||||
<td>
|
||||
<a href="manage_classifications.php?action=edit&type=datatype&id=<?php echo htmlspecialchars($dataType['pkdspstds_id']); ?>" class="btn btn-sm btn-warning rounded btn-action">
|
||||
<i class="fas fa-edit"></i>
|
||||
</a>
|
||||
<form action="manage_classifications.php" method="POST" class="d-inline" onsubmit="return confirm('Are you sure you want to delete this Data Type?');">
|
||||
<input type="hidden" name="type" value="datatype">
|
||||
<input type="hidden" name="action_type" value="delete">
|
||||
<input type="hidden" name="record_id" value="<?php echo htmlspecialchars($dataType['pkdspstds_id']); ?>">
|
||||
<button type="submit" class="btn btn-sm btn-danger rounded btn-action">
|
||||
<i class="fas fa-trash-alt"></i>
|
||||
</button>
|
||||
</form>
|
||||
</td>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
<?php else: ?>
|
||||
<tr>
|
||||
<td colspan="4" class="text-center">No data types found.</td>
|
||||
</tr>
|
||||
<?php endif; ?>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Category Management -->
|
||||
<div class="card">
|
||||
<div class="card-header bg-success text-white">
|
||||
<h5 class="mb-0">Manage Categories</h5>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<form action="manage_classifications.php" method="POST" class="mb-4">
|
||||
<input type="hidden" name="type" value="category">
|
||||
<input type="hidden" name="action_type" value="<?php echo $editCategory ? 'edit' : 'add'; ?>">
|
||||
<?php if ($editCategory): ?>
|
||||
<input type="hidden" name="record_id" value="<?php echo htmlspecialchars($editCategory['pkdspscate_id']); ?>">
|
||||
<?php endif; ?>
|
||||
<div class="row g-3">
|
||||
<div class="col-md-6">
|
||||
<label for="categoryTitleEn" class="form-label">Category Title (English)</label>
|
||||
<input type="text" class="form-control rounded" id="categoryTitleEn" name="name_en" value="<?php echo htmlspecialchars($editCategory['dspscate_title_en'] ?? ''); ?>" required>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<label for="categoryDetails" class="form-label">Details</label>
|
||||
<textarea class="form-control rounded-3" id="categoryDetails" name="details" rows="3"><?php echo htmlspecialchars($editCategory['dspscate_details'] ?? ''); ?></textarea>
|
||||
</div>
|
||||
<div class="col-12">
|
||||
<button type="submit" class="btn btn-success rounded">
|
||||
<i class="fas fa-<?php echo $editCategory ? 'save' : 'plus'; ?> me-2"></i> <?php echo $editCategory ? 'Update' : 'Add'; ?> Category
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<div class="table-responsive">
|
||||
<table class="table table-hover table-striped">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>ID</th>
|
||||
<th>English Title</th>
|
||||
<th>Details</th>
|
||||
<th>Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<?php if (!empty($categories)): ?>
|
||||
<?php foreach ($categories as $category): ?>
|
||||
<tr>
|
||||
<td><?php echo htmlspecialchars($category['pkdspscate_id']); ?></td>
|
||||
<td><?php echo htmlspecialchars($category['dspscate_title_en']); ?></td>
|
||||
<td><?php echo htmlspecialchars($category['dspscate_details']); ?></td>
|
||||
<td>
|
||||
<a href="manage_classifications.php?action=edit&type=category&id=<?php echo htmlspecialchars($category['pkdspscate_id']); ?>" class="btn btn-sm btn-warning rounded btn-action">
|
||||
<i class="fas fa-edit"></i>
|
||||
</a>
|
||||
<form action="manage_classifications.php" method="POST" class="d-inline" onsubmit="return confirm('Are you sure you want to delete this Category?');">
|
||||
<input type="hidden" name="type" value="category">
|
||||
<input type="hidden" name="action_type" value="delete">
|
||||
<input type="hidden" name="record_id" value="<?php echo htmlspecialchars($category['pkdspscate_id']); ?>">
|
||||
<button type="submit" class="btn btn-sm btn-danger rounded btn-action">
|
||||
<i class="fas fa-trash-alt"></i>
|
||||
</button>
|
||||
</form>
|
||||
</td>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
<?php else: ?>
|
||||
<tr>
|
||||
<td colspan="4" class="text-center">No categories found.</td>
|
||||
</tr>
|
||||
<?php endif; ?>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Footer -->
|
||||
<?php
|
||||
// Include Footer file for owner pages
|
||||
include_once("../includes/footer_admin.php");
|
||||
?>
|
||||
</body>
|
||||
</html>
|
||||
213
admin/manage_contactus.php
Normal file
213
admin/manage_contactus.php
Normal file
@@ -0,0 +1,213 @@
|
||||
<?php
|
||||
// Start session and include necessary files
|
||||
session_start();
|
||||
require_once '../config.php';
|
||||
require_once '../includes/auth.php';
|
||||
require_once '../classes/Contactus.php'; // This class handles dsps_tbl_feedback
|
||||
|
||||
// Redirect if not logged in or not a DAC Staff
|
||||
redirect_if_not_logged_in('../index.php');
|
||||
redirect_if_not_role('DAC Staff', '../index.php');
|
||||
|
||||
// Initialize Contactus class (for feedback management)
|
||||
$contactUs = new Contactus($pdo);
|
||||
|
||||
$action = $_GET['action'] ?? '';
|
||||
$id = $_GET['id'] ?? null;
|
||||
|
||||
// Handle form submissions for responding to feedback
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
$action_type = $_POST['action_type'] ?? ''; // 'respond' or 'delete'
|
||||
$feedback_id = $_POST['feedback_id'] ?? null;
|
||||
$respond_text = trim($_POST['respond_text'] ?? '');
|
||||
$status = $_POST['status'] ?? 'New'; // Default status for response
|
||||
|
||||
if ($action_type === 'respond') {
|
||||
if (empty($respond_text)) {
|
||||
set_message('Response text cannot be empty.', 'danger');
|
||||
} elseif ($feedback_id) {
|
||||
try {
|
||||
$contactUs->respondToFeedback($feedback_id, $respond_text, $status, $_SESSION['user_id']);
|
||||
set_message('Feedback responded to successfully!', 'success');
|
||||
} catch (Exception $e) {
|
||||
set_message('Error responding to feedback: ' . $e->getMessage(), 'danger');
|
||||
}
|
||||
}
|
||||
} elseif ($action_type === 'delete') {
|
||||
if ($feedback_id) {
|
||||
try {
|
||||
$contactUs->deleteFeedback($feedback_id);
|
||||
set_message('Feedback deleted successfully!', 'success');
|
||||
} catch (Exception $e) {
|
||||
set_message('Error deleting feedback: ' . $e->getMessage(), 'danger');
|
||||
}
|
||||
}
|
||||
}
|
||||
header('Location: manage_contactus.php');
|
||||
exit();
|
||||
}
|
||||
|
||||
// Fetch feedback entries for display
|
||||
$feedbackEntries = $contactUs->getAllFeedback();
|
||||
|
||||
// Prepare data for responding if action is 'respond'
|
||||
$respondFeedback = null;
|
||||
if ($action === 'respond' && $id) {
|
||||
$respondFeedback = $contactUs->getFeedbackById($id);
|
||||
}
|
||||
?>
|
||||
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<!-- Header -->
|
||||
<?php
|
||||
// Include header file for admin pages
|
||||
include_once("../includes/header_admin.php");
|
||||
?>
|
||||
<body>
|
||||
<div class="wrapper">
|
||||
|
||||
<!-- Sidebar -->
|
||||
<?php
|
||||
// Include header file for admin pages
|
||||
include_once("../includes/nav_admin.php");
|
||||
?>
|
||||
|
||||
<!-- Main Content -->
|
||||
<div class="main-content">
|
||||
<nav class="navbar navbar-expand-lg navbar-light bg-light mb-4 rounded-3">
|
||||
<div class="container-fluid">
|
||||
<a class="navbar-brand" href="#">Manage Contact Us (Feedback)</a>
|
||||
<div class="d-flex">
|
||||
<span class="navbar-text me-3">
|
||||
Welcome, <?php echo htmlspecialchars($_SESSION['username']); ?>!
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<?php
|
||||
// Display session messages
|
||||
if (isset($_SESSION['message'])) {
|
||||
echo '<div class="alert alert-' . $_SESSION['message_type'] . ' alert-dismissible fade show rounded" role="alert">' . htmlspecialchars($_SESSION['message']) . '<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button></div>';
|
||||
unset($_SESSION['message']);
|
||||
unset($_SESSION['message_type']);
|
||||
}
|
||||
?>
|
||||
|
||||
<?php if ($respondFeedback): ?>
|
||||
<div class="card mb-4">
|
||||
<div class="card-header bg-primary text-white">
|
||||
<h5 class="mb-0">Respond to Feedback #<?php echo htmlspecialchars($respondFeedback['pkdspsfb_id']); ?></h5>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="mb-3">
|
||||
<strong>From:</strong> <?php echo htmlspecialchars($respondFeedback['dspsfb_name']); ?> (<?php echo htmlspecialchars($respondFeedback['dspsfb_email']); ?>)
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<strong>Submitted On:</strong> <?php echo htmlspecialchars($respondFeedback['dspsfb_reg_datetime']); ?>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<strong>Message:</strong>
|
||||
<p class="border p-3 rounded-3 bg-light"><?php echo nl2br(htmlspecialchars($respondFeedback['dspsfb_body_text'])); ?></p>
|
||||
</div>
|
||||
<?php if (!empty($respondFeedback['dspsfb_respond_text'])): ?>
|
||||
<div class="mb-3">
|
||||
<strong>Previous Response:</strong>
|
||||
<p class="border p-3 rounded-3 bg-light text-muted"><?php echo nl2br(htmlspecialchars($respondFeedback['dspsfb_respond_text'])); ?></p>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
|
||||
<form action="manage_contactus.php" method="POST">
|
||||
<input type="hidden" name="action_type" value="respond">
|
||||
<input type="hidden" name="feedback_id" value="<?php echo htmlspecialchars($respondFeedback['pkdspsfb_id']); ?>">
|
||||
|
||||
<div class="mb-3">
|
||||
<label for="respond_text" class="form-label">Your Response</label>
|
||||
<textarea class="form-control rounded-3" id="respond_text" name="respond_text" rows="5" required><?php echo htmlspecialchars($respondFeedback['dspsfb_respond_text'] ?? ''); ?></textarea>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="status" class="form-label">Status</label>
|
||||
<select class="form-select rounded" id="status" name="status" required>
|
||||
<option value="New" <?php echo ($respondFeedback['dspsfb_status'] == 'New') ? 'selected' : ''; ?>>New</option>
|
||||
<option value="In Progress" <?php echo ($respondFeedback['dspsfb_status'] == 'In Progress') ? 'selected' : ''; ?>>In Progress</option>
|
||||
<option value="Resolved" <?php echo ($respondFeedback['dspsfb_status'] == 'Resolved') ? 'selected' : ''; ?>>Resolved</option>
|
||||
<option value="Archived" <?php echo ($respondFeedback['dspsfb_status'] == 'Archived') ? 'selected' : ''; ?>>Archived</option>
|
||||
</select>
|
||||
</div>
|
||||
<button type="submit" class="btn btn-primary rounded">
|
||||
<i class="fas fa-reply me-2"></i> Send Response
|
||||
</button>
|
||||
<a href="manage_contactus.php" class="btn btn-secondary rounded ms-2">Cancel</a>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
|
||||
<div class="card">
|
||||
<div class="card-header text-white" style="background-color: #28a745;">
|
||||
<h5 class="mb-0">All Feedback Messages</h5>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="table-responsive">
|
||||
<table class="table table-hover table-striped">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>ID</th>
|
||||
<th>Name</th>
|
||||
<th>Email</th>
|
||||
<th>Message</th>
|
||||
<th>Status</th>
|
||||
<th>Submitted On</th>
|
||||
<th>Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<?php if (!empty($feedbackEntries)): ?>
|
||||
<?php foreach ($feedbackEntries as $feedback): ?>
|
||||
<tr>
|
||||
<td><?php echo htmlspecialchars($feedback['pkdspsfb_id']); ?></td>
|
||||
<td><?php echo htmlspecialchars($feedback['dspsfb_name']); ?></td>
|
||||
<td><?php echo htmlspecialchars($feedback['dspsfb_email']); ?></td>
|
||||
<td><?php echo htmlspecialchars(substr($feedback['dspsfb_body_text'], 0, 100)) . (strlen($feedback['dspsfb_body_text']) > 100 ? '...' : ''); ?></td>
|
||||
<td><span class="badge bg-<?php
|
||||
if ($feedback['dspsfb_status'] == 'New') echo 'danger';
|
||||
else if ($feedback['dspsfb_status'] == 'In Progress') echo 'warning';
|
||||
else if ($feedback['dspsfb_status'] == 'Resolved') echo 'success';
|
||||
else echo 'secondary';
|
||||
?>"><?php echo htmlspecialchars($feedback['dspsfb_status']); ?></span></td>
|
||||
<td><?php echo htmlspecialchars($feedback['dspsfb_reg_datetime']); ?></td>
|
||||
<td>
|
||||
<a href="manage_contactus.php?action=respond&id=<?php echo htmlspecialchars($feedback['pkdspsfb_id']); ?>" class="btn btn-sm btn-primary rounded btn-action">
|
||||
<i class="fas fa-reply"></i> Respond
|
||||
</a>
|
||||
<form action="manage_contactus.php" method="POST" class="d-inline" onsubmit="return confirm('Are you sure you want to delete this feedback?');">
|
||||
<input type="hidden" name="action_type" value="delete">
|
||||
<input type="hidden" name="feedback_id" value="<?php echo htmlspecialchars($feedback['pkdspsfb_id']); ?>">
|
||||
<button type="submit" class="btn btn-sm btn-danger rounded btn-action">
|
||||
<i class="fas fa-trash-alt"></i> Delete
|
||||
</button>
|
||||
</form>
|
||||
</td>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
<?php else: ?>
|
||||
<tr>
|
||||
<td colspan="7" class="text-center">No feedback messages found.</td>
|
||||
</tr>
|
||||
<?php endif; ?>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Footer -->
|
||||
<?php
|
||||
// Include Footer file for owner pages
|
||||
include_once("../includes/footer_admin.php");
|
||||
?>
|
||||
</body>
|
||||
</html>
|
||||
195
admin/manage_datasources.php
Normal file
195
admin/manage_datasources.php
Normal file
@@ -0,0 +1,195 @@
|
||||
<?php
|
||||
// admin/manage_datasources.php
|
||||
session_start();
|
||||
require_once '../config.php';
|
||||
require_once '../includes/auth.php';
|
||||
require_once '../classes/DataSource.php';
|
||||
|
||||
redirect_if_not_logged_in('../index.php');
|
||||
redirect_if_not_role('DAC Staff', '../index.php');
|
||||
|
||||
$dataSourceManager = new DataSource($pdo);
|
||||
$search_query = trim($_GET['search'] ?? '');
|
||||
$status_filter = trim($_GET['status_filter'] ?? '');
|
||||
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
$action = $_POST['action'] ?? '';
|
||||
$datasourceId = isset($_POST['datasource_id']) ? (int)$_POST['datasource_id'] : 0;
|
||||
|
||||
if ($action === 'change_status' && $datasourceId > 0) {
|
||||
$newStatus = trim($_POST['new_status'] ?? '');
|
||||
try {
|
||||
$dataSourceManager->updateDataSourceStatus($datasourceId, $newStatus, (int) $_SESSION['user_id']);
|
||||
set_message('Data source status updated successfully.', 'success');
|
||||
} catch (Exception $e) {
|
||||
set_message('Failed to update status: ' . $e->getMessage(), 'danger');
|
||||
}
|
||||
} elseif ($action === 'delete' && $datasourceId > 0) {
|
||||
try {
|
||||
if ($dataSourceManager->deleteDataSource($datasourceId)) {
|
||||
set_message('Data source deleted.', 'success');
|
||||
} else {
|
||||
set_message('Unable to delete data source.', 'danger');
|
||||
}
|
||||
} catch (Exception $e) {
|
||||
set_message('Deletion failed: ' . $e->getMessage(), 'danger');
|
||||
}
|
||||
}
|
||||
|
||||
$redirectUrl = 'manage_datasources.php';
|
||||
$params = [];
|
||||
if ($search_query !== '') {
|
||||
$params['search'] = urlencode($search_query);
|
||||
}
|
||||
if ($status_filter !== '') {
|
||||
$params['status_filter'] = urlencode($status_filter);
|
||||
}
|
||||
if (!empty($params)) {
|
||||
$redirectUrl .= '?' . http_build_query($params);
|
||||
}
|
||||
header('Location: ' . $redirectUrl);
|
||||
exit();
|
||||
}
|
||||
|
||||
$dataSources = $dataSourceManager->getAllDataSourcesDetailed(
|
||||
$search_query !== '' ? $search_query : null,
|
||||
$status_filter !== '' ? $status_filter : null
|
||||
);
|
||||
|
||||
$statuses = ['Active', 'Inactive', 'Pending Review', 'Published'];
|
||||
$uploadsWebPath = '../uploads/datasources/';
|
||||
?>
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<?php include_once("../includes/header_admin.php"); ?>
|
||||
<body>
|
||||
<div class="wrapper">
|
||||
<?php include_once("../includes/nav_admin.php"); ?>
|
||||
<div class="main-content">
|
||||
<nav class="navbar navbar-expand-lg navbar-light bg-light mb-4 rounded-3">
|
||||
<div class="container-fluid">
|
||||
<a class="navbar-brand" href="#">Manage Data Sources</a>
|
||||
<div class="d-flex">
|
||||
<span class="navbar-text me-3">
|
||||
Welcome, <?php echo htmlspecialchars($_SESSION['username']); ?>!
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<?php
|
||||
if (isset($_SESSION['message'])) {
|
||||
echo '<div class="alert alert-' . $_SESSION['message_type'] . ' alert-dismissible fade show rounded" role="alert">'
|
||||
. htmlspecialchars($_SESSION['message']) .
|
||||
'<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button></div>';
|
||||
unset($_SESSION['message'], $_SESSION['message_type']);
|
||||
}
|
||||
?>
|
||||
|
||||
<div class="card mb-4">
|
||||
<div class="card-header bg-primary text-white d-flex justify-content-between align-items-center">
|
||||
<h5 class="mb-0">All Data Sources</h5>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<form class="row g-3 align-items-end mb-4" method="GET" action="manage_datasources.php">
|
||||
<div class="col-md-5">
|
||||
<label for="searchDataSource" class="form-label">Search</label>
|
||||
<input type="text" class="form-control rounded" id="searchDataSource" name="search" value="<?= htmlspecialchars($search_query) ?>" placeholder="Title, owner, or type">
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<label for="statusFilter" class="form-label">Status</label>
|
||||
<select class="form-select rounded" id="statusFilter" name="status_filter">
|
||||
<option value="">All statuses</option>
|
||||
<?php foreach ($statuses as $status): ?>
|
||||
<option value="<?= htmlspecialchars($status) ?>" <?= $status_filter === $status ? 'selected' : '' ?>><?= htmlspecialchars($status) ?></option>
|
||||
<?php endforeach; ?>
|
||||
</select>
|
||||
</div>
|
||||
<div class="col-md-3">
|
||||
<button type="submit" class="btn btn-info rounded me-2"><i class="fas fa-filter me-2"></i>Apply</button>
|
||||
<a href="manage_datasources.php" class="btn btn-outline-secondary rounded"><i class="fas fa-sync-alt me-2"></i>Reset</a>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<div class="table-responsive">
|
||||
<table class="table table-hover align-middle">
|
||||
<thead class="table-light">
|
||||
<tr>
|
||||
<th>ID</th>
|
||||
<th>Title</th>
|
||||
<th>Owner</th>
|
||||
<th>Type</th>
|
||||
<th>Category</th>
|
||||
<th>Status</th>
|
||||
<th>Files</th>
|
||||
<th>Registered</th>
|
||||
<th>Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<?php if (!empty($dataSources)): ?>
|
||||
<?php foreach ($dataSources as $ds): ?>
|
||||
<tr>
|
||||
<td><?= htmlspecialchars($ds['pkdspsds_id']) ?></td>
|
||||
<td><?= htmlspecialchars($ds['dspsds_title_en']) ?></td>
|
||||
<td><?= htmlspecialchars($ds['owner_name']) ?></td>
|
||||
<td><?= htmlspecialchars($ds['data_type_name']) ?></td>
|
||||
<td><?= htmlspecialchars($ds['category_name']) ?></td>
|
||||
<td>
|
||||
<form class="d-flex align-items-center gap-2" method="POST">
|
||||
<input type="hidden" name="datasource_id" value="<?= (int)$ds['pkdspsds_id'] ?>">
|
||||
<input type="hidden" name="action" value="change_status">
|
||||
<select class="form-select form-select-sm" name="new_status">
|
||||
<?php foreach ($statuses as $status): ?>
|
||||
<option value="<?= htmlspecialchars($status) ?>" <?= $ds['dspsds_status'] === $status ? 'selected' : '' ?>><?= htmlspecialchars($status) ?></option>
|
||||
<?php endforeach; ?>
|
||||
</select>
|
||||
<button type="submit" class="btn btn-sm btn-outline-primary rounded">Update</button>
|
||||
</form>
|
||||
</td>
|
||||
<td>
|
||||
<?php
|
||||
$fileColumns = [
|
||||
'dspsds_filename' => 'Primary',
|
||||
'dspsds_filename1' => 'Doc 1',
|
||||
'dspsds_filename2' => 'Doc 2',
|
||||
'dspsds_filename3' => 'Doc 3',
|
||||
];
|
||||
$links = [];
|
||||
foreach ($fileColumns as $column => $label) {
|
||||
$fileName = $ds[$column] ?? '';
|
||||
if ($fileName === '') {
|
||||
continue;
|
||||
}
|
||||
$isUrl = preg_match('/^https?:\\/\\//i', $fileName) === 1;
|
||||
$target = $isUrl ? $fileName : $uploadsWebPath . rawurlencode($fileName);
|
||||
$links[] = '<a class="btn btn-sm btn-outline-secondary rounded-pill me-1 mb-1" href="' . htmlspecialchars($target) . '" target="_blank" rel="noopener" title="' . htmlspecialchars($label) . '"><i class="fas fa-paperclip"></i></a>';
|
||||
}
|
||||
echo !empty($links) ? implode('', $links) : '<span class="text-muted">—</span>';
|
||||
?>
|
||||
</td>
|
||||
<td><?= htmlspecialchars(date('Y-m-d', strtotime($ds['dspsds_reg_datetime'] ?? 'now'))) ?></td>
|
||||
<td>
|
||||
<form method="POST" onsubmit="return confirm('Delete this data source? This cannot be undone.');">
|
||||
<input type="hidden" name="datasource_id" value="<?= (int)$ds['pkdspsds_id'] ?>">
|
||||
<input type="hidden" name="action" value="delete">
|
||||
<button type="submit" class="btn btn-sm btn-outline-danger rounded"><i class="fas fa-trash-alt"></i></button>
|
||||
</form>
|
||||
</td>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
<?php else: ?>
|
||||
<tr>
|
||||
<td colspan="9" class="text-center text-muted py-4">No data sources found.</td>
|
||||
</tr>
|
||||
<?php endif; ?>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<?php include_once("../includes/footer_admin.php"); ?>
|
||||
</body>
|
||||
</html>
|
||||
218
admin/manage_faq.php
Normal file
218
admin/manage_faq.php
Normal file
@@ -0,0 +1,218 @@
|
||||
<?php
|
||||
// Start session and include necessary files
|
||||
session_start();
|
||||
require_once '../config.php';
|
||||
require_once '../includes/auth.php';
|
||||
require_once '../classes/Faq.php';
|
||||
require_once '../classes/User.php'; // To get person_id for the Faq class
|
||||
|
||||
// Redirect if not logged in or not a DAC Staff
|
||||
redirect_if_not_logged_in('../index.php');
|
||||
redirect_if_not_role('DAC Staff', '../index.php');
|
||||
|
||||
// Initialize Faq class
|
||||
$faq = new Faq($pdo);
|
||||
$user = new User($pdo); // To get the person_id of the logged-in user
|
||||
|
||||
$action = $_GET['action'] ?? '';
|
||||
$id = $_GET['id'] ?? null;
|
||||
|
||||
// Handle form submissions
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
$action_type = $_POST['action_type'] ?? ''; // 'add' or 'edit' or 'delete'
|
||||
$title = trim($_POST['title'] ?? ''); // This is the question
|
||||
$description = trim($_POST['description'] ?? ''); // This is the answer
|
||||
$faq_id = $_POST['faq_id'] ?? null;
|
||||
|
||||
// Get the person_id of the currently logged-in user
|
||||
$currentUserDetails = $user->getUserDetails($_SESSION['user_id']);
|
||||
$fkisp_id_of = $currentUserDetails['fkisp_id_of'];
|
||||
|
||||
if ($action_type === 'delete') {
|
||||
if ($faq_id) {
|
||||
try {
|
||||
$faq->deleteFaq($faq_id);
|
||||
set_message('FAQ entry deleted successfully!', 'success');
|
||||
} catch (Exception $e) {
|
||||
set_message('Error deleting FAQ entry: ' . $e->getMessage(), 'danger');
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (empty($title) || empty($description)) {
|
||||
set_message('Question and Answer cannot be empty.', 'danger');
|
||||
} else {
|
||||
try {
|
||||
if ($action_type === 'add') {
|
||||
$faq->addFaq($title, $description, $_SESSION['user_id'], $fkisp_id_of);
|
||||
set_message('FAQ entry added successfully!', 'success');
|
||||
} elseif ($action_type === 'edit' && $faq_id) {
|
||||
$faq->updateFaq($faq_id, $title, $description, $_SESSION['user_id'], $fkisp_id_of);
|
||||
set_message('FAQ entry updated successfully!', 'success');
|
||||
}
|
||||
} catch (Exception $e) {
|
||||
set_message('Error: ' . $e->getMessage(), 'danger');
|
||||
}
|
||||
}
|
||||
}
|
||||
header('Location: manage_faq.php');
|
||||
exit();
|
||||
}
|
||||
|
||||
// Fetch FAQ entries for display
|
||||
$faqEntries = $faq->getAllFaqs();
|
||||
|
||||
// Prepare data for editing if action is 'edit'
|
||||
$editFaq = null;
|
||||
if ($action === 'edit' && $id) {
|
||||
$editFaq = $faq->getFaqById($id);
|
||||
}
|
||||
?>
|
||||
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<!-- Header -->
|
||||
<?php
|
||||
// Include header file for admin pages
|
||||
include_once("../includes/header_admin.php");
|
||||
?>
|
||||
|
||||
<body>
|
||||
<div class="wrapper">
|
||||
<!-- Sidebar -->
|
||||
<?php
|
||||
// Include header file for admin pages
|
||||
include_once("../includes/nav_admin.php");
|
||||
?>
|
||||
|
||||
<!-- Main Content -->
|
||||
<div class="main-content">
|
||||
<nav class="navbar navbar-expand-lg navbar-light bg-light mb-4 rounded-3">
|
||||
<div class="container-fluid">
|
||||
<a class="navbar-brand" href="#">Manage FAQ</a>
|
||||
<div class="d-flex">
|
||||
<span class="navbar-text me-3">
|
||||
Welcome, <?php echo htmlspecialchars($_SESSION['username']); ?>!
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<?php
|
||||
// Display session messages
|
||||
if (isset($_SESSION['message'])) {
|
||||
echo '<div class="alert alert-' . $_SESSION['message_type'] . ' alert-dismissible fade show rounded" role="alert">' . htmlspecialchars($_SESSION['message']) . '<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button></div>';
|
||||
unset($_SESSION['message']);
|
||||
unset($_SESSION['message_type']);
|
||||
}
|
||||
?>
|
||||
|
||||
<div class="card mb-4">
|
||||
<div class="card-header bg-primary text-white">
|
||||
<h5 class="mb-0"><?php echo $editFaq ? 'Edit' : 'Add New'; ?> FAQ Entry</h5>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<form action="manage_faq.php" method="POST">
|
||||
<input type="hidden" name="action_type" value="<?php echo $editFaq ? 'edit' : 'add'; ?>">
|
||||
<?php if ($editFaq): ?>
|
||||
<input type="hidden" name="faq_id" value="<?php echo htmlspecialchars($editFaq['pkdspsfaq_id']); ?>">
|
||||
<?php endif; ?>
|
||||
|
||||
<div class="mb-3">
|
||||
<label for="title" class="form-label">Question</label>
|
||||
<input type="text" class="form-control rounded" id="title" name="title" value="<?php echo htmlspecialchars($editFaq['dspsfaq_title_en'] ?? ''); ?>" required>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="description" class="form-label d-flex justify-content-between align-items-center">
|
||||
<span>Answer</span>
|
||||
</label>
|
||||
<textarea class="form-control rounded-3" id="description" name="description" rows="6" required><?php echo htmlspecialchars($editFaq['dspsfaq_description'] ?? ''); ?></textarea>
|
||||
<div class="form-text">Rich formatting appears on the public FAQ page—emphasise key steps and link to related resources.</div>
|
||||
</div>
|
||||
<button type="submit" class="btn btn-primary rounded">
|
||||
<i class="fas fa-<?php echo $editFaq ? 'save' : 'plus'; ?> me-2"></i> <?php echo $editFaq ? 'Update' : 'Add'; ?> FAQ
|
||||
</button>
|
||||
<?php if ($editFaq): ?>
|
||||
<a href="manage_faq.php" class="btn btn-secondary rounded ms-2">Cancel Edit</a>
|
||||
<?php endif; ?>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card">
|
||||
<div class="card-header text-white" style="background-color: #28a745;">
|
||||
<h5 class="mb-0">All FAQ Entries</h5>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="table-responsive">
|
||||
<table class="table table-hover table-striped">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>ID</th>
|
||||
<th>Question</th>
|
||||
<th>Answer</th>
|
||||
<th>Reg. Date</th>
|
||||
<th>Mod. Date</th>
|
||||
<th>Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<?php if (!empty($faqEntries)): ?>
|
||||
<?php foreach ($faqEntries as $entry): ?>
|
||||
<tr>
|
||||
<td><?php echo htmlspecialchars($entry['pkdspsfaq_id']); ?></td>
|
||||
<td><?php echo htmlspecialchars($entry['dspsfaq_title_en']); ?></td>
|
||||
<td><?php echo htmlspecialchars(substr($entry['dspsfaq_description'], 0, 100)) . (strlen($entry['dspsfaq_description']) > 100 ? '...' : ''); ?></td>
|
||||
<td><?php echo htmlspecialchars($entry['dspsfaq_reg_datetime']); ?></td>
|
||||
<td><?php echo htmlspecialchars($entry['dspsfaq_mod_datetime']); ?></td>
|
||||
<td>
|
||||
<a href="manage_faq.php?action=edit&id=<?php echo htmlspecialchars($entry['pkdspsfaq_id']); ?>" class="btn btn-sm btn-warning rounded btn-action">
|
||||
<i class="fas fa-edit"></i>
|
||||
</a>
|
||||
<form action="manage_faq.php" method="POST" class="d-inline" onsubmit="return confirm('Are you sure you want to delete this FAQ entry?');">
|
||||
<input type="hidden" name="action_type" value="delete">
|
||||
<input type="hidden" name="faq_id" value="<?php echo htmlspecialchars($entry['pkdspsfaq_id']); ?>">
|
||||
<button type="submit" class="btn btn-sm btn-danger rounded btn-action">
|
||||
<i class="fas fa-trash-alt"></i>
|
||||
</button>
|
||||
</form>
|
||||
</td>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
<?php else: ?>
|
||||
<tr>
|
||||
<td colspan="6" class="text-center">No FAQ entries found.</td>
|
||||
</tr>
|
||||
<?php endif; ?>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Footer -->
|
||||
<?php
|
||||
// Include Footer file for owner pages
|
||||
include_once("../includes/footer_admin.php");
|
||||
?>
|
||||
<script src="https://cdn.jsdelivr.net/npm/@ckeditor/ckeditor5-build-classic@38.1.1/build/ckeditor.js"></script>
|
||||
<script>
|
||||
document.addEventListener('DOMContentLoaded', function () {
|
||||
var textarea = document.querySelector('#description');
|
||||
if (textarea && typeof ClassicEditor !== 'undefined') {
|
||||
ClassicEditor
|
||||
.create(textarea, {
|
||||
toolbar: [
|
||||
'heading','|','bold','italic','underline','bulletedList','numberedList','blockQuote',
|
||||
'|','link','insertTable','undo','redo'
|
||||
]
|
||||
})
|
||||
.catch(function (error) {
|
||||
console.error('Failed to initialise rich text editor', error);
|
||||
});
|
||||
}
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
310
admin/manage_permissions_admin.php
Normal file
310
admin/manage_permissions_admin.php
Normal file
@@ -0,0 +1,310 @@
|
||||
<?php
|
||||
// Start session and include necessary files
|
||||
session_start();
|
||||
require_once '../config.php';
|
||||
require_once '../includes/auth.php';
|
||||
require_once '../classes/DataSource.php'; // Contains permission management methods
|
||||
|
||||
// Redirect if not logged in or not a DAC Staff
|
||||
redirect_if_not_logged_in('../index.php');
|
||||
redirect_if_not_role('DAC Staff', '../index.php');
|
||||
|
||||
// Initialize DataSource class
|
||||
$dataSourceManager = new DataSource($pdo);
|
||||
|
||||
// --- Handle Search and Filter Parameters ---
|
||||
$search_query = trim($_GET['search'] ?? '');
|
||||
$filter_status = trim($_GET['status_filter'] ?? '');
|
||||
|
||||
// Handle form submissions for updating permission status
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
$action_type = $_POST['action_type'] ?? '';
|
||||
$permission_id = $_POST['permission_id'] ?? null;
|
||||
|
||||
if ($action_type === 'update_permission_status' && $permission_id) {
|
||||
$new_status = trim($_POST['new_status'] ?? '');
|
||||
$notes = trim($_POST['notes'] ?? '');
|
||||
|
||||
// --- SECURITY CHECK: Verify permission ID and associated data source existence ---
|
||||
try {
|
||||
$permission_details = $dataSourceManager->getPermissionRequestById($permission_id);
|
||||
|
||||
if (!$permission_details) {
|
||||
set_message("Permission request not found or invalid.", "danger");
|
||||
header('Location: manage_permissions_admin.php');
|
||||
exit();
|
||||
}
|
||||
|
||||
// Optional: You could add a check here to ensure the data source itself is still active
|
||||
// $dataSource = $dataSourceManager->getDataSourceById($permission_details['fkdspsds_id']);
|
||||
// if (!$dataSource) { /* handle error */ }
|
||||
|
||||
if (!in_array($new_status, ['Approved', 'Pending', 'Rejected', 'Revoked'])) {
|
||||
set_message('Invalid permission status selected.', 'danger');
|
||||
} else {
|
||||
// The reg_by for permission updates is the user who is logged in (DAC Staff)
|
||||
$dataSourceManager->updatePermissionStatus(
|
||||
(int) $permission_id,
|
||||
$new_status,
|
||||
(int) $_SESSION['user_id'],
|
||||
$notes
|
||||
);
|
||||
set_message('Permission status updated successfully!', 'success');
|
||||
}
|
||||
} catch (Exception $e) {
|
||||
set_message('Error updating permission status: ' . $e->getMessage(), 'danger');
|
||||
}
|
||||
}
|
||||
// Redirect to self, preserving search/filter parameters if they exist
|
||||
$redirect_url = 'manage_permissions_admin.php';
|
||||
$query_params = [];
|
||||
if (!empty($search_query)) {
|
||||
$query_params['search'] = urlencode($search_query);
|
||||
}
|
||||
if (!empty($filter_status)) {
|
||||
$query_params['status_filter'] = urlencode($filter_status);
|
||||
}
|
||||
if (!empty($query_params)) {
|
||||
$redirect_url .= '?' . http_build_query($query_params);
|
||||
}
|
||||
header('Location: ' . $redirect_url);
|
||||
exit();
|
||||
}
|
||||
|
||||
// Fetch all permission requests based on search and filter parameters
|
||||
$allPermissions = $dataSourceManager->getAllPermissionRequests($filter_status, $search_query);
|
||||
|
||||
?>
|
||||
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<!-- Header -->
|
||||
<?php
|
||||
// Include header file for admin pages
|
||||
include_once("../includes/header_admin.php");
|
||||
?>
|
||||
<body>
|
||||
<div class="wrapper">
|
||||
<!-- Sidebar -->
|
||||
<?php
|
||||
// Include header file for admin pages
|
||||
include_once("../includes/nav_admin.php");
|
||||
?>
|
||||
|
||||
<!-- Main Content -->
|
||||
<div class="main-content">
|
||||
<nav class="navbar navbar-expand-lg navbar-light bg-light mb-4 rounded-3">
|
||||
<div class="container-fluid">
|
||||
<a class="navbar-brand" href="#">Manage All Permissions</a>
|
||||
<div class="d-flex">
|
||||
<span class="navbar-text me-3">
|
||||
Welcome, <?php echo htmlspecialchars($_SESSION['username']); ?>!
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<?php
|
||||
// Display session messages
|
||||
if (isset($_SESSION['message'])) {
|
||||
echo '<div class="alert alert-' . $_SESSION['message_type'] . ' alert-dismissible fade show rounded-pill" role="alert">' . htmlspecialchars($_SESSION['message']) . '<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button></div>';
|
||||
unset($_SESSION['message']);
|
||||
unset($_SESSION['message_type']);
|
||||
}
|
||||
?>
|
||||
|
||||
<div class="card mb-4">
|
||||
<div class="card-header bg-primary text-white d-flex justify-content-between align-items-center">
|
||||
<h5 class="mb-0">All Data Access Requests</h5>
|
||||
<!-- Removed the "Create Mock Data Source" button -->
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<!-- Search and Filter Form -->
|
||||
<form action="manage_permissions_admin.php" method="GET" class="mb-4">
|
||||
<div class="row g-3 align-items-end">
|
||||
<div class="col-md-5">
|
||||
<label for="searchPermissionInput" class="form-label visually-hidden">Search Permissions</label>
|
||||
<input type="text" class="form-control rounded-pill" id="searchPermissionInput" name="search" placeholder="Search by data source, requester, owner..." value="<?= htmlspecialchars($search_query) ?>">
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<label for="statusFilter" class="form-label visually-hidden">Filter by Status</label>
|
||||
<select class="form-select rounded-pill" id="statusFilter" name="status_filter">
|
||||
<option value="">All Statuses</option>
|
||||
<option value="Pending" <?= ($filter_status == 'Pending' ? 'selected' : '') ?>>Pending</option>
|
||||
<option value="Approved" <?= ($filter_status == 'Approved' ? 'selected' : '') ?>>Approved</option>
|
||||
<option value="Rejected" <?= ($filter_status == 'Rejected' ? 'selected' : '') ?>>Rejected</option>
|
||||
<option value="Revoked" <?= ($filter_status == 'Revoked' ? 'selected' : '') ?>>Revoked</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="col-md-3 d-flex justify-content-end">
|
||||
<button type="submit" class="btn btn-info rounded-pill me-2"><i class="fas fa-filter me-2"></i>Apply Filter</button>
|
||||
<a href="manage_permissions_admin.php" class="btn btn-secondary rounded-pill"><i class="fas fa-sync-alt me-2"></i>Reset</a>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<div class="table-responsive">
|
||||
<table class="table table-hover table-striped">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Req. ID</th>
|
||||
<th>Data Source</th>
|
||||
<th>Requester</th>
|
||||
<th>Data Owner</th>
|
||||
<th>Permission Type</th>
|
||||
<th>Request Notes</th>
|
||||
<th>Proof</th>
|
||||
<th>Status</th>
|
||||
<th>Requested On</th>
|
||||
<th>Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<?php if (!empty($allPermissions)): ?>
|
||||
<?php foreach ($allPermissions as $permission): ?>
|
||||
<tr>
|
||||
<td><?php echo htmlspecialchars($permission['pkdspsdsp_id']); ?></td>
|
||||
<td><?php echo htmlspecialchars($permission['dspsds_title_en']); ?></td>
|
||||
<td><?php echo htmlspecialchars($permission['requester_firstname'] . ' ' . $permission['requester_lastname']); ?></td>
|
||||
<td><?php echo htmlspecialchars($permission['owner_firstname'] . ' ' . $permission['owner_lastname']); ?></td>
|
||||
<td><?php echo htmlspecialchars($permission['dspsdsp_permission']); ?></td>
|
||||
<td><?php echo !empty($permission['dspsdsp_notes']) ? nl2br(htmlspecialchars($permission['dspsdsp_notes'])) : '<span class="text-muted">—</span>'; ?></td>
|
||||
<td>
|
||||
<?php if (!empty($permission['dspsdsp_proof_path'])): ?>
|
||||
<?php
|
||||
$proofPath = $permission['dspsdsp_proof_path'];
|
||||
$isExternal = preg_match('/^https?:\\/\\//i', $proofPath) === 1;
|
||||
$linkTarget = $isExternal ? $proofPath : '../uploads/' . $proofPath;
|
||||
?>
|
||||
<a href="<?php echo htmlspecialchars($linkTarget); ?>" class="btn btn-sm btn-outline-primary rounded-pill" target="_blank" rel="noopener">
|
||||
<i class="fas fa-file-pdf me-1"></i> View
|
||||
</a>
|
||||
<?php else: ?>
|
||||
<span class="text-muted">N/A</span>
|
||||
<?php endif; ?>
|
||||
</td>
|
||||
<td>
|
||||
<span class="badge bg-<?php
|
||||
if ($permission['dspsdsp_status'] == 'Approved') echo 'success';
|
||||
else if ($permission['dspsdsp_status'] == 'Pending') echo 'warning';
|
||||
else if ($permission['dspsdsp_status'] == 'Rejected') echo 'danger';
|
||||
else echo 'secondary';
|
||||
?>">
|
||||
<?php echo htmlspecialchars($permission['dspsdsp_status']); ?>
|
||||
</span>
|
||||
</td>
|
||||
<td><?php echo htmlspecialchars($permission['dspsdsp_reg_datetime']); ?></td>
|
||||
<td>
|
||||
<button type="button" class="btn btn-sm btn-info rounded-pill btn-action" data-bs-toggle="modal" data-bs-target="#managePermissionModal"
|
||||
data-permission-id="<?php echo htmlspecialchars($permission['pkdspsdsp_id']); ?>"
|
||||
data-data-source="<?php echo htmlspecialchars($permission['dspsds_title_en']); ?>"
|
||||
data-requester="<?php echo htmlspecialchars($permission['requester_firstname'] . ' ' . $permission['requester_lastname']); ?>"
|
||||
data-permission-type="<?php echo htmlspecialchars($permission['dspsdsp_permission']); ?>"
|
||||
data-notes="<?php echo htmlspecialchars($permission['dspsdsp_notes']); ?>"
|
||||
data-current-status="<?php echo htmlspecialchars($permission['dspsdsp_status']); ?>">
|
||||
<i class="fas fa-cogs"></i> Manage
|
||||
</button>
|
||||
</td>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
<?php else: ?>
|
||||
<tr>
|
||||
<td colspan="9" class="text-center">No permission requests found.</td>
|
||||
</tr>
|
||||
<?php endif; ?>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Manage Permission Modal -->
|
||||
<div class="modal fade" id="managePermissionModal" tabindex="-1" aria-labelledby="managePermissionModalLabel" aria-hidden="true">
|
||||
<div class="modal-dialog modal-dialog-centered">
|
||||
<div class="modal-content rounded shadow-lg">
|
||||
<div class="modal-header text-white rounded-top" style="background-color: #28a745;">
|
||||
<h5 class="modal-title" id="managePermissionModalLabel">Manage Permission Request</h5>
|
||||
<button type="button" class="btn-close btn-close-white" data-bs-dismiss="modal" aria-label="Close"></button>
|
||||
</div>
|
||||
<div class="modal-body p-4">
|
||||
<form action="manage_permissions_admin.php" method="POST">
|
||||
<input type="hidden" name="action_type" value="update_permission_status">
|
||||
<input type="hidden" name="permission_id" id="modalPermissionId">
|
||||
|
||||
<div class="mb-3">
|
||||
<label for="modalDataSource" class="form-label">Data Source</label>
|
||||
<input type="text" class="form-control rounded-pill" id="modalDataSource" readonly>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="modalRequester" class="form-label">Requester</label>
|
||||
<input type="text" class="form-control rounded-pill" id="modalRequester" readonly>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="modalPermissionType" class="form-label">Permission Type</label>
|
||||
<input type="text" class="form-control rounded-pill" id="modalPermissionType" readonly>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="modalRequestNotes" class="form-label">Requester Notes</label>
|
||||
<textarea class="form-control rounded-3" id="modalRequestNotes" rows="3" readonly></textarea>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="newPermissionStatus" class="form-label">Update Status</label>
|
||||
<select class="form-select rounded-pill" id="newPermissionStatus" name="new_status" required>
|
||||
<option value="Pending">Pending</option>
|
||||
<option value="Approved">Approved</option>
|
||||
<option value="Rejected">Rejected</option>
|
||||
<option value="Revoked">Revoked</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="adminNotes" class="form-label">Admin Notes (Optional)</label>
|
||||
<textarea class="form-control rounded-3" id="adminNotes" name="notes" rows="3"></textarea>
|
||||
</div>
|
||||
<div class="d-grid">
|
||||
<button type="submit" class="btn btn-primary rounded-pill">Update Permission</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
// JavaScript to populate the modal fields when "Manage" button is clicked
|
||||
var managePermissionModal = document.getElementById('managePermissionModal');
|
||||
managePermissionModal.addEventListener('show.bs.modal', function (event) {
|
||||
var button = event.relatedTarget; // Button that triggered the modal
|
||||
var permissionId = button.getAttribute('data-permission-id');
|
||||
var dataSource = button.getAttribute('data-data-source');
|
||||
var requester = button.getAttribute('data-requester');
|
||||
var permissionType = button.getAttribute('data-permission-type');
|
||||
var notes = button.getAttribute('data-notes');
|
||||
var currentStatus = button.getAttribute('data-current-status');
|
||||
|
||||
var modalPermissionId = managePermissionModal.querySelector('#modalPermissionId');
|
||||
var modalDataSource = managePermissionModal.querySelector('#modalDataSource');
|
||||
var modalRequester = managePermissionModal.querySelector('#modalRequester');
|
||||
var modalPermissionType = managePermissionModal.querySelector('#modalPermissionType');
|
||||
var modalRequestNotes = managePermissionModal.querySelector('#modalRequestNotes');
|
||||
var newPermissionStatusSelect = managePermissionModal.querySelector('#newPermissionStatus');
|
||||
|
||||
modalPermissionId.value = permissionId;
|
||||
modalDataSource.value = dataSource;
|
||||
modalRequester.value = requester;
|
||||
modalPermissionType.value = permissionType;
|
||||
modalRequestNotes.value = notes;
|
||||
newPermissionStatusSelect.value = currentStatus; // Set default selected option to current status
|
||||
// Clear admin notes field for new entry
|
||||
managePermissionModal.querySelector('#adminNotes').value = '';
|
||||
});
|
||||
</script>
|
||||
|
||||
<!-- Footer -->
|
||||
<?php
|
||||
// Include Footer file for owner pages
|
||||
include_once("../includes/footer_admin.php");
|
||||
?>
|
||||
</body>
|
||||
</html>
|
||||
258
admin/manage_slides.php
Normal file
258
admin/manage_slides.php
Normal file
@@ -0,0 +1,258 @@
|
||||
<?php
|
||||
// Start session and include necessary files
|
||||
session_start();
|
||||
require_once '../config.php';
|
||||
require_once '../includes/auth.php';
|
||||
require_once '../classes/Slide.php';
|
||||
require_once '../classes/User.php'; // Needed to get fkisp_id_of for slide creation/modification
|
||||
|
||||
// Redirect if not logged in or not a DAC Staff
|
||||
redirect_if_not_logged_in('../index.php');
|
||||
redirect_if_not_role('DAC Staff', '../index.php');
|
||||
|
||||
// Initialize Slide and User classes
|
||||
$slideManager = new Slide($pdo);
|
||||
$userManager = new User($pdo);
|
||||
|
||||
$action = $_GET['action'] ?? '';
|
||||
$id = $_GET['id'] ?? null;
|
||||
|
||||
// Get current user's person ID for fkisp_id_of
|
||||
$currentUserDetails = $userManager->getUserDetails($_SESSION['user_id']);
|
||||
$fkisp_id_of = $currentUserDetails['fkisp_id_of'];
|
||||
|
||||
// Handle form submissions
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
$action_type = $_POST['action_type'] ?? ''; // 'add' or 'edit' or 'delete'
|
||||
$title_en = trim($_POST['title_en'] ?? '');
|
||||
$description = trim($_POST['description'] ?? '');
|
||||
$slide_id = $_POST['slide_id'] ?? null;
|
||||
$current_photo = $_POST['current_photo'] ?? ''; // For editing, keep track of existing photo
|
||||
|
||||
if ($action_type === 'delete') {
|
||||
if ($slide_id) {
|
||||
try {
|
||||
$slideManager->deleteSlide($slide_id);
|
||||
set_message('Slide deleted successfully!', 'success');
|
||||
} catch (Exception $e) {
|
||||
set_message('Error deleting slide: ' . $e->getMessage(), 'danger');
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Handle photo upload
|
||||
$photoPath = $current_photo; // Default to current photo if not uploading new
|
||||
if (isset($_FILES['photo']) && $_FILES['photo']['error'] === UPLOAD_ERR_OK) {
|
||||
try {
|
||||
$photoPath = $slideManager->handlePhotoUpload($_FILES['photo']);
|
||||
// If editing and a new photo is uploaded, delete the old one
|
||||
if ($action_type === 'edit' && !empty($current_photo) && $current_photo !== $photoPath) {
|
||||
// Ensure the old photo path is not empty and different from the new one
|
||||
if (!empty($current_photo) && file_exists('../uploads/slides/' . $current_photo)) {
|
||||
unlink('../uploads/slides/' . $current_photo); // Delete old file
|
||||
}
|
||||
}
|
||||
} catch (Exception $e) {
|
||||
set_message('Photo upload error: ' . $e->getMessage(), 'danger');
|
||||
header('Location: manage_slides.php');
|
||||
exit();
|
||||
}
|
||||
} elseif ($action_type === 'add' && (!isset($_FILES['photo']) || $_FILES['photo']['error'] !== UPLOAD_ERR_OK)) {
|
||||
// For adding, a photo is required.
|
||||
set_message('Please upload a photo for the slide.', 'danger');
|
||||
header('Location: manage_slides.php');
|
||||
exit();
|
||||
}
|
||||
|
||||
|
||||
if (empty($title_en) || empty($description)) {
|
||||
set_message('Title and description cannot be empty.', 'danger');
|
||||
} else {
|
||||
try {
|
||||
if ($action_type === 'add') {
|
||||
$slideManager->addSlide($title_en, $description, $photoPath, $_SESSION['user_id'], $fkisp_id_of);
|
||||
set_message('Slide added successfully!', 'success');
|
||||
} elseif ($action_type === 'edit' && $slide_id) {
|
||||
$slideManager->updateSlide($slide_id, $title_en, $description, $photoPath, $_SESSION['user_id'], $fkisp_id_of);
|
||||
set_message('Slide updated successfully!', 'success');
|
||||
}
|
||||
} catch (Exception $e) {
|
||||
set_message('Error: ' . $e->getMessage(), 'danger');
|
||||
}
|
||||
}
|
||||
}
|
||||
header('Location: manage_slides.php');
|
||||
exit();
|
||||
}
|
||||
|
||||
// Fetch slides for display
|
||||
$slides = $slideManager->getAllSlides();
|
||||
|
||||
// Prepare data for editing if action is 'edit'
|
||||
$editSlide = null;
|
||||
if ($action === 'edit' && $id) {
|
||||
$editSlide = $slideManager->getSlideById($id);
|
||||
}
|
||||
?>
|
||||
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<!-- Header -->
|
||||
<?php
|
||||
// Include header file for admin pages
|
||||
include_once("../includes/header_admin.php");
|
||||
?>
|
||||
<body>
|
||||
<div class="wrapper">
|
||||
<!-- Sidebar -->
|
||||
<?php
|
||||
// Include header file for admin pages
|
||||
include_once("../includes/nav_admin.php");
|
||||
?>
|
||||
<!-- Main Content -->
|
||||
<div class="main-content">
|
||||
<nav class="navbar navbar-expand-lg navbar-light bg-light mb-4 rounded-3">
|
||||
<div class="container-fluid">
|
||||
<a class="navbar-brand" href="#">Manage Slides</a>
|
||||
<div class="d-flex">
|
||||
<span class="navbar-text me-3">
|
||||
Welcome, <?php echo htmlspecialchars($_SESSION['username']); ?>!
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<?php
|
||||
// Display session messages
|
||||
if (isset($_SESSION['message'])) {
|
||||
echo '<div class="alert alert-' . $_SESSION['message_type'] . ' alert-dismissible fade show rounded" role="alert">' . htmlspecialchars($_SESSION['message']) . '<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button></div>';
|
||||
unset($_SESSION['message']);
|
||||
unset($_SESSION['message_type']);
|
||||
}
|
||||
?>
|
||||
|
||||
<div class="card mb-4">
|
||||
<div class="card-header bg-primary text-white">
|
||||
<h5 class="mb-0"><?php echo $editSlide ? 'Edit' : 'Add New'; ?> Slide</h5>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<form action="manage_slides.php" method="POST" enctype="multipart/form-data">
|
||||
<input type="hidden" name="action_type" value="<?php echo $editSlide ? 'edit' : 'add'; ?>">
|
||||
<?php if ($editSlide): ?>
|
||||
<input type="hidden" name="slide_id" value="<?php echo htmlspecialchars($editSlide['pkdspsslide_id']); ?>">
|
||||
<input type="hidden" name="current_photo" value="<?php echo htmlspecialchars($editSlide['dspsslide_photoname']); ?>">
|
||||
<?php endif; ?>
|
||||
|
||||
<div class="mb-3">
|
||||
<label for="title_en" class="form-label">Slide Title (English)</label>
|
||||
<input type="text" class="form-control rounded" id="title_en" name="title_en" value="<?php echo htmlspecialchars($editSlide['dspsslide_title_en'] ?? ''); ?>" required>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="description" class="form-label d-flex justify-content-between align-items-center">
|
||||
<span>Description</span>
|
||||
</label>
|
||||
<textarea class="form-control rounded-3" id="description" name="description" rows="5" required><?php echo htmlspecialchars($editSlide['dspsslide_description'] ?? ''); ?></textarea>
|
||||
<div class="form-text">Formatted text appears on the public carousel, so emphasise key phrases and provide concise summaries.</div>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="photo" class="form-label">Slide Photo (JPG, PNG, GIF)</label>
|
||||
<input type="file" class="form-control rounded" id="photo" name="photo" accept="image/*" <?php echo $editSlide ? '' : 'required'; ?>>
|
||||
<?php if ($editSlide && !empty($editSlide['dspsslide_photoname'])): ?>
|
||||
<div class="mt-2">
|
||||
Current Photo: <img src="../uploads/slides/<?php echo htmlspecialchars($editSlide['dspsslide_photoname']); ?>" alt="Slide Photo" class="slide-img-thumbnail">
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
<button type="submit" class="btn btn-primary rounded">
|
||||
<i class="fas fa-<?php echo $editSlide ? 'save' : 'plus'; ?> me-2"></i> <?php echo $editSlide ? 'Update' : 'Add'; ?> Slide
|
||||
</button>
|
||||
<?php if ($editSlide): ?>
|
||||
<a href="manage_slides.php" class="btn btn-secondary rounded ms-2">Cancel Edit</a>
|
||||
<?php endif; ?>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card">
|
||||
<div class="card-header text-white" style="background-color: #28a745;">
|
||||
<h5 class="mb-0">All Slides</h5>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="table-responsive">
|
||||
<table class="table table-hover table-striped">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>ID</th>
|
||||
<th>Title</th>
|
||||
<th>Description</th>
|
||||
<th>Photo</th>
|
||||
<th>Reg. Date</th>
|
||||
<th>Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<?php if (!empty($slides)): ?>
|
||||
<?php foreach ($slides as $slide): ?>
|
||||
<tr>
|
||||
<td><?php echo htmlspecialchars($slide['pkdspsslide_id']); ?></td>
|
||||
<td><?php echo htmlspecialchars($slide['dspsslide_title_en']); ?></td>
|
||||
<td><?php echo htmlspecialchars(substr($slide['dspsslide_description'], 0, 100)) . (strlen($slide['dspsslide_description']) > 100 ? '...' : ''); ?></td>
|
||||
<td>
|
||||
<?php if (!empty($slide['dspsslide_photoname'])): ?>
|
||||
<img src="../uploads/slides/<?php echo htmlspecialchars($slide['dspsslide_photoname']); ?>" alt="Slide Photo" class="slide-img-thumbnail">
|
||||
<?php else: ?>
|
||||
N/A
|
||||
<?php endif; ?>
|
||||
</td>
|
||||
<td><?php echo htmlspecialchars($slide['dspsslide_reg_datetime']); ?></td>
|
||||
<td>
|
||||
<a href="manage_slides.php?action=edit&id=<?php echo htmlspecialchars($slide['pkdspsslide_id']); ?>" class="btn btn-sm btn-warning rounded btn-action">
|
||||
<i class="fas fa-edit"></i>
|
||||
</a>
|
||||
<form action="manage_slides.php" method="POST" class="d-inline" onsubmit="return confirm('Are you sure you want to delete this slide? This action cannot be undone.');">
|
||||
<input type="hidden" name="action_type" value="delete">
|
||||
<input type="hidden" name="slide_id" value="<?php echo htmlspecialchars($slide['pkdspsslide_id']); ?>">
|
||||
<button type="submit" class="btn btn-sm btn-danger rounded btn-action">
|
||||
<i class="fas fa-trash-alt"></i>
|
||||
</button>
|
||||
</form>
|
||||
</td>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
<?php else: ?>
|
||||
<tr>
|
||||
<td colspan="6" class="text-center">No slides found.</td>
|
||||
</tr>
|
||||
<?php endif; ?>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Footer -->
|
||||
<?php
|
||||
// Include Footer file for owner pages
|
||||
include_once("../includes/footer_admin.php");
|
||||
?>
|
||||
<script src="https://cdn.jsdelivr.net/npm/@ckeditor/ckeditor5-build-classic@38.1.1/build/ckeditor.js"></script>
|
||||
<script>
|
||||
document.addEventListener('DOMContentLoaded', function () {
|
||||
var textarea = document.querySelector('#description');
|
||||
if (textarea && typeof ClassicEditor !== 'undefined') {
|
||||
ClassicEditor
|
||||
.create(textarea, {
|
||||
toolbar: [
|
||||
'heading','|','bold','italic','underline','bulletedList','numberedList','blockQuote',
|
||||
'|','link','insertTable','undo','redo'
|
||||
]
|
||||
})
|
||||
.catch(function (error) {
|
||||
console.error('Failed to initialise rich text editor', error);
|
||||
});
|
||||
}
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
782
admin/manage_users.php
Normal file
782
admin/manage_users.php
Normal file
@@ -0,0 +1,782 @@
|
||||
<?php
|
||||
// Start session and include necessary files
|
||||
session_start();
|
||||
require_once '../config.php';
|
||||
require_once '../includes/auth.php';
|
||||
require_once '../classes/User.php';
|
||||
|
||||
// Redirect if not logged in or not a DAC Staff
|
||||
redirect_if_not_logged_in('../index.php');
|
||||
redirect_if_not_role('DAC Staff', '../index.php');
|
||||
|
||||
// Initialize User class
|
||||
$userManager = new User($pdo);
|
||||
|
||||
// --- Handle Search and Filter Parameters ---
|
||||
$search_query = trim($_GET['search'] ?? '');
|
||||
$filter_status = trim($_GET['status_filter'] ?? ''); // New filter for user status
|
||||
$filters_active = ($search_query !== '' || $filter_status !== '');
|
||||
|
||||
// Handle form submissions
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
$action_type = $_POST['action_type'] ?? '';
|
||||
$user_id = $_POST['user_id'] ?? null;
|
||||
|
||||
if ($action_type === 'update_status' && $user_id) {
|
||||
$new_status = $_POST['new_status'] ?? '';
|
||||
$requested_r_access = $_POST['can_run_r'] ?? null;
|
||||
$has_valid_r_flag = in_array($requested_r_access, ['0', '1'], true);
|
||||
|
||||
// Prevent a DAC Staff from deactivating themselves
|
||||
if ($user_id == $_SESSION['user_id'] && $new_status == 'Inactive') {
|
||||
set_message('You cannot deactivate your own account.', 'danger');
|
||||
header('Location: manage_users.php');
|
||||
exit();
|
||||
}
|
||||
|
||||
if (!in_array($new_status, ['DAC Staff', 'Data Contributor', 'Data Owner', 'Data User', 'Inactive'])) {
|
||||
set_message('Invalid user status selected.', 'danger');
|
||||
} else {
|
||||
try {
|
||||
$admin_user_id = (int) $_SESSION['user_id'];
|
||||
$userManager->updateUserStatus($user_id, $new_status, $admin_user_id);
|
||||
|
||||
if ($has_valid_r_flag) {
|
||||
$userManager->updateUserRJupyterAccess($user_id, $requested_r_access === '1', $admin_user_id);
|
||||
|
||||
if ((int)$user_id === (int)$_SESSION['user_id']) {
|
||||
$_SESSION['can_run_r'] = ($requested_r_access === '1');
|
||||
}
|
||||
}
|
||||
|
||||
$message = 'User status updated successfully!';
|
||||
if ($has_valid_r_flag) {
|
||||
$message = 'User status and R/Jupyter access updated successfully!';
|
||||
}
|
||||
set_message($message, 'success');
|
||||
} catch (Exception $e) {
|
||||
set_message('Error updating user status: ' . $e->getMessage(), 'danger');
|
||||
}
|
||||
}
|
||||
} elseif ($action_type === 'reset_password' && $user_id) {
|
||||
$new_password = $_POST['new_password'] ?? '';
|
||||
$confirm_password = $_POST['confirm_password'] ?? '';
|
||||
|
||||
if (empty($new_password) || empty($confirm_password)) {
|
||||
set_message('Please provide and confirm the new password.', 'danger');
|
||||
} elseif ($new_password !== $confirm_password) {
|
||||
set_message('Passwords do not match. Please try again.', 'danger');
|
||||
} elseif (strlen($new_password) < 8) {
|
||||
set_message('Password must be at least 8 characters long.', 'danger');
|
||||
} else {
|
||||
try {
|
||||
$admin_user_id = (int) $_SESSION['user_id'];
|
||||
$userManager->changePassword((int)$user_id, $new_password, $admin_user_id);
|
||||
set_message('Password reset successfully.', 'success');
|
||||
} catch (Exception $e) {
|
||||
set_message('Error resetting password: ' . $e->getMessage(), 'danger');
|
||||
}
|
||||
}
|
||||
} elseif ($action_type === 'add_user') {
|
||||
// --- Handle Add New User Submission ---
|
||||
$id_card = trim($_POST['id_card'] ?? '');
|
||||
$first_name_en = trim($_POST['first_name_en'] ?? '');
|
||||
$last_name_en = trim($_POST['last_name_en'] ?? '');
|
||||
$sex = trim($_POST['sex'] ?? '');
|
||||
$dob = trim($_POST['dob'] ?? '');
|
||||
$phone_number = trim($_POST['phone_number'] ?? '');
|
||||
$email = trim($_POST['email'] ?? '');
|
||||
$username = trim($_POST['username'] ?? '');
|
||||
$password = $_POST['password'] ?? '';
|
||||
$confirm_password = $_POST['confirm_password'] ?? '';
|
||||
$user_role_new = trim($_POST['user_role_new'] ?? 'Data User'); // Role for new user
|
||||
|
||||
// Server-side validation for new user
|
||||
if (empty($first_name_en) || empty($last_name_en) || empty($sex) || empty($dob) || empty($username) || empty($password) || empty($confirm_password)) {
|
||||
set_message("All required fields for new user must be filled.", "danger");
|
||||
} elseif ($password !== $confirm_password) {
|
||||
set_message("Passwords do not match for new user.", "danger");
|
||||
} elseif (!empty($email) && !filter_var($email, FILTER_VALIDATE_EMAIL)) {
|
||||
set_message("Invalid email format for new user.", "danger");
|
||||
} else {
|
||||
// Prepare data for User class
|
||||
$person_data = [
|
||||
'id_card' => $id_card,
|
||||
'first_name_en' => $first_name_en,
|
||||
'last_name_en' => $last_name_en,
|
||||
'sex' => $sex,
|
||||
'dob' => $dob,
|
||||
'pob' => null, // Add if you collect this
|
||||
'nationality' => 'Cambodian', // Default or collect
|
||||
'marital_status' => 'Single', // Default or collect
|
||||
'phone_number' => $phone_number,
|
||||
'email' => $email,
|
||||
'telegram' => null, // Add if you collect this
|
||||
'note' => null // Add if you collect this
|
||||
];
|
||||
|
||||
$user_data = [
|
||||
'username' => $username,
|
||||
'password' => $password,
|
||||
'status' => 'Data User', // Default status for new registrations
|
||||
'can_run_r' => !empty($_POST['user_can_run_r'])
|
||||
];
|
||||
|
||||
try {
|
||||
if ($userManager->registerUser($person_data, $user_data)) {
|
||||
set_message("New user '" . htmlspecialchars($username) . "' registered successfully!", "success");
|
||||
} else {
|
||||
// This else might be redundant if registerUser always throws on failure
|
||||
set_message("Failed to register new user due to an unknown error.", "danger");
|
||||
}
|
||||
} catch (Exception $e) {
|
||||
set_message('Error registering new user: ' . $e->getMessage(), 'danger');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Redirect to self, preserving search/filter parameters if they exist
|
||||
$redirect_url = 'manage_users.php';
|
||||
$query_params = [];
|
||||
if (!empty($search_query)) {
|
||||
$query_params['search'] = urlencode($search_query);
|
||||
}
|
||||
if (!empty($filter_status)) {
|
||||
$query_params['status_filter'] = urlencode($filter_status);
|
||||
}
|
||||
if (!empty($query_params)) {
|
||||
$redirect_url .= '?' . http_build_query($query_params);
|
||||
}
|
||||
header('Location: ' . $redirect_url);
|
||||
exit();
|
||||
}
|
||||
|
||||
// Fetch users based on search and filter parameters
|
||||
// We will modify getAllUsers in classes/User.php to accept these parameters
|
||||
$users = $userManager->getAllUsers($search_query, $filter_status);
|
||||
|
||||
$totalUsers = count($users);
|
||||
$activeUsers = 0;
|
||||
$inactiveUsers = 0;
|
||||
$dacStaffCount = 0;
|
||||
$ownerCount = 0;
|
||||
$contributorCount = 0;
|
||||
$rAccessCount = 0;
|
||||
|
||||
foreach ($users as $user) {
|
||||
$status = $user['isu_status'] ?? '';
|
||||
$isActive = $status !== 'Inactive';
|
||||
if ($isActive) {
|
||||
$activeUsers++;
|
||||
} else {
|
||||
$inactiveUsers++;
|
||||
}
|
||||
|
||||
if ($status === 'DAC Staff') {
|
||||
$dacStaffCount++;
|
||||
} elseif ($status === 'Data Owner') {
|
||||
$ownerCount++;
|
||||
} elseif ($status === 'Data Contributor') {
|
||||
$contributorCount++;
|
||||
}
|
||||
|
||||
if (!empty($user['isu_can_run_r'])) {
|
||||
$rAccessCount++;
|
||||
}
|
||||
}
|
||||
|
||||
$summaryMetrics = [
|
||||
[
|
||||
'label' => 'Total Users',
|
||||
'value' => $totalUsers,
|
||||
'icon' => 'fa-users',
|
||||
'class' => 'bg-primary-subtle text-primary',
|
||||
'icon_class' => 'text-primary'
|
||||
],
|
||||
[
|
||||
'label' => 'Active Accounts',
|
||||
'value' => $activeUsers,
|
||||
'icon' => 'fa-user-check',
|
||||
'class' => 'bg-success-subtle text-success',
|
||||
'icon_class' => 'text-success'
|
||||
],
|
||||
[
|
||||
'label' => 'With R/Jupyter',
|
||||
'value' => $rAccessCount,
|
||||
'icon' => 'fa-flask',
|
||||
'class' => 'bg-info-subtle text-info',
|
||||
'icon_class' => 'text-info'
|
||||
],
|
||||
[
|
||||
'label' => 'Inactive',
|
||||
'value' => $inactiveUsers,
|
||||
'icon' => 'fa-user-slash',
|
||||
'class' => 'bg-warning-subtle text-warning',
|
||||
'icon_class' => 'text-warning'
|
||||
],
|
||||
];
|
||||
|
||||
?>
|
||||
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<!-- Header -->
|
||||
<?php
|
||||
// Include header file for admin pages
|
||||
include_once("../includes/header_admin.php");
|
||||
?>
|
||||
<style>
|
||||
.summary-cards .card {
|
||||
border: none;
|
||||
border-left: 4px solid rgba(0, 0, 0, 0.05);
|
||||
transition: transform 0.2s ease, box-shadow 0.2s ease;
|
||||
}
|
||||
.summary-cards .card:hover {
|
||||
transform: translateY(-4px);
|
||||
box-shadow: 0 0.75rem 1.5rem rgba(15, 68, 130, 0.15);
|
||||
}
|
||||
.summary-icon {
|
||||
font-size: 1.75rem;
|
||||
width: 3.25rem;
|
||||
height: 3.25rem;
|
||||
display: grid;
|
||||
place-items: center;
|
||||
border-radius: 0.85rem;
|
||||
background: rgba(255, 255, 255, 0.6);
|
||||
}
|
||||
.summary-value {
|
||||
font-size: 1.75rem;
|
||||
font-weight: 700;
|
||||
letter-spacing: -0.02em;
|
||||
}
|
||||
.summary-label {
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.08em;
|
||||
font-size: 0.75rem;
|
||||
font-weight: 600;
|
||||
}
|
||||
.status-badges .badge {
|
||||
font-weight: 600;
|
||||
padding: 0.45rem 0.65rem;
|
||||
}
|
||||
.table thead th {
|
||||
background-color: #f1f5f9;
|
||||
border-bottom: none;
|
||||
text-transform: uppercase;
|
||||
font-size: 0.75rem;
|
||||
letter-spacing: 0.08em;
|
||||
}
|
||||
.table-hover tbody tr:hover {
|
||||
background-color: rgba(20, 86, 185, 0.05);
|
||||
}
|
||||
.table td {
|
||||
vertical-align: middle;
|
||||
}
|
||||
.table-responsive {
|
||||
scrollbar-width: thin;
|
||||
}
|
||||
.table-responsive::-webkit-scrollbar {
|
||||
height: 6px;
|
||||
}
|
||||
.table-responsive::-webkit-scrollbar-thumb {
|
||||
background-color: rgba(0, 0, 0, 0.2);
|
||||
border-radius: 3px;
|
||||
}
|
||||
.btn-action-group .btn {
|
||||
min-width: 9rem;
|
||||
}
|
||||
@media (max-width: 767.98px) {
|
||||
.summary-value {
|
||||
font-size: 1.4rem;
|
||||
}
|
||||
.btn-action-group .btn {
|
||||
min-width: auto;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
<body>
|
||||
<div class="wrapper">
|
||||
<!-- Sidebar -->
|
||||
<?php
|
||||
// Include header file for admin pages
|
||||
include_once("../includes/nav_admin.php");
|
||||
?>
|
||||
|
||||
<!-- Main Content -->
|
||||
<div class="main-content">
|
||||
<nav class="navbar navbar-expand-lg navbar-light bg-light mb-4 rounded-3">
|
||||
<div class="container-fluid">
|
||||
<a class="navbar-brand" href="#">Manage Users</a>
|
||||
<div class="d-flex align-items-center gap-2">
|
||||
<span class="navbar-text d-none d-md-inline">
|
||||
Welcome, <?php echo htmlspecialchars($_SESSION['username']); ?>!
|
||||
</span>
|
||||
<button class="btn btn-outline-primary btn-sm d-md-none" type="button" data-bs-toggle="collapse" data-bs-target="#manageUsersToolbar" aria-expanded="false" aria-controls="manageUsersToolbar">
|
||||
<i class="fas fa-sliders-h"></i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<?php
|
||||
// Display session messages
|
||||
if (isset($_SESSION['message'])) {
|
||||
echo '<div class="alert alert-' . $_SESSION['message_type'] . ' alert-dismissible fade show rounded" role="alert">' . htmlspecialchars($_SESSION['message']) . '<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button></div>';
|
||||
unset($_SESSION['message']);
|
||||
unset($_SESSION['message_type']);
|
||||
}
|
||||
?>
|
||||
|
||||
<div class="summary-cards row g-3 mb-3">
|
||||
<?php foreach ($summaryMetrics as $metric): ?>
|
||||
<div class="col-12 col-md-6 col-xl-3">
|
||||
<div class="card shadow-sm rounded-4 <?php echo $metric['class']; ?>">
|
||||
<div class="card-body d-flex align-items-center gap-3">
|
||||
<div class="summary-icon <?php echo htmlspecialchars($metric['icon_class']); ?>">
|
||||
<i class="fas <?php echo htmlspecialchars($metric['icon']); ?>"></i>
|
||||
</div>
|
||||
<div>
|
||||
<div class="summary-value"><?php echo number_format($metric['value']); ?></div>
|
||||
<div class="summary-label text-secondary-emphasis">
|
||||
<?php echo htmlspecialchars($metric['label']); ?>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<?php endforeach; ?>
|
||||
</div>
|
||||
|
||||
<div class="status-badges d-flex flex-wrap gap-2 mb-4">
|
||||
<span class="badge bg-primary-subtle text-primary">DAC Staff: <?php echo number_format($dacStaffCount); ?></span>
|
||||
<span class="badge bg-info-subtle text-info">Data Owners: <?php echo number_format($ownerCount); ?></span>
|
||||
<span class="badge bg-primary-subtle text-primary">Contributors: <?php echo number_format($contributorCount); ?></span>
|
||||
<span class="badge bg-success-subtle text-success">With R/Jupyter: <?php echo number_format($rAccessCount); ?></span>
|
||||
</div>
|
||||
|
||||
<div class="card mb-4">
|
||||
<div class="card-header bg-primary text-white d-flex justify-content-between align-items-center">
|
||||
<h5 class="mb-0">All Registered Users</h5>
|
||||
<div class="d-flex gap-2">
|
||||
<button type="button" class="btn btn-outline-light d-none d-md-inline-flex rounded" data-bs-toggle="collapse" data-bs-target="#manageUsersToolbar" aria-expanded="<?php echo $filters_active ? 'true' : 'false'; ?>" aria-controls="manageUsersToolbar">
|
||||
<i class="fas fa-filter me-2"></i>Filters
|
||||
</button>
|
||||
<button type="button" class="btn btn-success rounded" data-bs-toggle="modal" data-bs-target="#addUserModal">
|
||||
<i class="fas fa-user-plus me-2"></i> Add New User
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<!-- Search and Filter Form -->
|
||||
<div class="collapse<?php echo $filters_active ? ' show' : ''; ?>" id="manageUsersToolbar">
|
||||
<form action="manage_users.php" method="GET" class="mb-4">
|
||||
<div class="row g-3 align-items-end">
|
||||
<div class="col-12 col-md-5">
|
||||
<label for="searchUserInput" class="form-label visually-hidden">Search Users</label>
|
||||
<div class="input-group shadow-sm rounded-pill overflow-hidden">
|
||||
<span class="input-group-text bg-white border-0 text-muted">
|
||||
<i class="fas fa-magnifying-glass"></i>
|
||||
</span>
|
||||
<input type="text" class="form-control border-0" id="searchUserInput" name="search" placeholder="Search by username, name, email..." value="<?= htmlspecialchars($search_query) ?>" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-12 col-md-4">
|
||||
<label for="statusFilter" class="form-label visually-hidden">Filter by Role</label>
|
||||
<div class="input-group shadow-sm rounded-pill overflow-hidden">
|
||||
<span class="input-group-text bg-white border-0 text-muted">
|
||||
<i class="fas fa-layer-group"></i>
|
||||
</span>
|
||||
<select class="form-select border-0" id="statusFilter" name="status_filter">
|
||||
<option value="">All Roles</option>
|
||||
<option value="DAC Staff" <?= ($filter_status == 'DAC Staff' ? 'selected' : '') ?>>DAC Staff</option>
|
||||
<option value="Data Contributor" <?= ($filter_status == 'Data Contributor' ? 'selected' : '') ?>>Data Contributor</option>
|
||||
<option value="Data Owner" <?= ($filter_status == 'Data Owner' ? 'selected' : '') ?>>Data Owner</option>
|
||||
<option value="Data User" <?= ($filter_status == 'Data User' ? 'selected' : '') ?>>Data User</option>
|
||||
<option value="Inactive" <?= ($filter_status == 'Inactive' ? 'selected' : '') ?>>Inactive</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-12 col-md-3">
|
||||
<div class="d-flex gap-2 justify-content-md-end">
|
||||
<button type="submit" class="btn btn-info rounded-pill flex-fill flex-md-grow-0 px-md-4">
|
||||
<i class="fas fa-filter me-2"></i>Apply
|
||||
</button>
|
||||
<a href="manage_users.php" class="btn btn-outline-secondary rounded-pill flex-fill flex-md-grow-0 px-md-4">
|
||||
<i class="fas fa-sync-alt me-2"></i>Reset
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<div class="table-responsive">
|
||||
<table class="table table-striped table-hover align-middle">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>No.</th>
|
||||
<th>Username</th>
|
||||
<th>Full Name</th>
|
||||
<th>Email</th>
|
||||
<th>Phone</th>
|
||||
<th>Current Role</th>
|
||||
<th>R/Jupyter</th>
|
||||
<th>Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<?php if (!empty($users)): ?>
|
||||
<?php $rowNumber = 1; ?>
|
||||
<?php foreach ($users as $user): ?>
|
||||
<tr>
|
||||
<td><?php echo $rowNumber++; ?></td>
|
||||
<td><?php echo htmlspecialchars($user['isu_name']); ?></td>
|
||||
<td><?php echo htmlspecialchars($user['isp_firstname_en'] . ' ' . $user['isp_lastname_en']); ?></td>
|
||||
<td><?php echo htmlspecialchars($user['isp_email'] ?? 'N/A'); ?></td>
|
||||
<td><?php echo htmlspecialchars($user['isp_phone_number'] ?? 'N/A'); ?></td>
|
||||
<td>
|
||||
<?php
|
||||
$roleClass = 'bg-secondary-subtle text-secondary';
|
||||
if ($user['isu_status'] == 'DAC Staff') {
|
||||
$roleClass = 'bg-danger-subtle text-danger';
|
||||
} elseif ($user['isu_status'] == 'Data Owner') {
|
||||
$roleClass = 'bg-info-subtle text-info';
|
||||
} elseif ($user['isu_status'] == 'Data User') {
|
||||
$roleClass = 'bg-success-subtle text-success';
|
||||
} elseif ($user['isu_status'] == 'Data Contributor') {
|
||||
$roleClass = 'bg-primary-subtle text-primary';
|
||||
}
|
||||
?>
|
||||
<span class="badge rounded-pill <?php echo $roleClass; ?>">
|
||||
<?php echo htmlspecialchars($user['isu_status']); ?>
|
||||
</span>
|
||||
</td>
|
||||
<td>
|
||||
<?php if (!empty($user['isu_can_run_r'])): ?>
|
||||
<span class="badge rounded-pill bg-success-subtle text-success">Enabled</span>
|
||||
<?php else: ?>
|
||||
<span class="badge rounded-pill bg-secondary-subtle text-secondary">Disabled</span>
|
||||
<?php endif; ?>
|
||||
</td>
|
||||
<td>
|
||||
<div class="d-flex flex-wrap gap-2 btn-action-group">
|
||||
<button type="button"
|
||||
class="btn btn-outline-primary rounded-pill"
|
||||
data-bs-toggle="modal"
|
||||
data-bs-target="#editUserModal"
|
||||
data-bs-tooltip="true"
|
||||
title="Change role or adjust R/Jupyter access"
|
||||
data-user-id="<?php echo htmlspecialchars($user['pkisu_id']); ?>"
|
||||
data-username="<?php echo htmlspecialchars($user['isu_name']); ?>"
|
||||
data-current-status="<?php echo htmlspecialchars($user['isu_status']); ?>"
|
||||
data-can-run-r="<?php echo !empty($user['isu_can_run_r']) ? '1' : '0'; ?>">
|
||||
<i class="fas fa-user-gear"></i>
|
||||
</button>
|
||||
<button type="button"
|
||||
class="btn btn-outline-danger rounded-pill"
|
||||
data-bs-toggle="modal"
|
||||
data-bs-target="#resetPasswordModal"
|
||||
data-bs-tooltip="true"
|
||||
title="Set a new password for this account"
|
||||
data-user-id="<?php echo htmlspecialchars($user['pkisu_id']); ?>"
|
||||
data-username="<?php echo htmlspecialchars($user['isu_name']); ?>">
|
||||
<i class="fas fa-key"></i>
|
||||
</button>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
<?php else: ?>
|
||||
<tr>
|
||||
<td colspan="8" class="text-center py-4 text-muted">No users match the current filters.</td>
|
||||
</tr>
|
||||
<?php endif; ?>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Edit User Role Modal -->
|
||||
<div class="modal fade" id="editUserModal" tabindex="-1" aria-labelledby="editUserModalLabel" aria-hidden="true">
|
||||
<div class="modal-dialog modal-dialog-centered">
|
||||
<div class="modal-content rounded shadow-lg">
|
||||
<div class="modal-header text-white rounded-top" style="background-color: #28a745;">
|
||||
<h5 class="modal-title" id="editUserModalLabel">Change User Role</h5>
|
||||
<button type="button" class="btn-close btn-close-white" data-bs-dismiss="modal" aria-label="Close"></button>
|
||||
</div>
|
||||
<div class="modal-body p-4">
|
||||
<form action="manage_users.php" method="POST">
|
||||
<input type="hidden" name="action_type" value="update_status">
|
||||
<input type="hidden" name="user_id" id="modalUserId">
|
||||
<div class="mb-3">
|
||||
<label for="modalUsername" class="form-label">Username</label>
|
||||
<input type="text" class="form-control rounded" id="modalUsername" readonly>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="modalCurrentStatus" class="form-label">Current Role</label>
|
||||
<input type="text" class="form-control rounded" id="modalCurrentStatus" readonly>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="newStatus" class="form-label">New Role</label>
|
||||
<select class="form-select rounded" id="newStatus" name="new_status" required>
|
||||
<option value="Data User">Data User</option>
|
||||
<option value="Data Contributor">Data Contributor</option>
|
||||
<option value="Data Owner">Data Owner</option>
|
||||
<option value="DAC Staff">DAC Staff</option>
|
||||
<option value="Inactive">Inactive</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="modalRAccess" class="form-label">R/Jupyter Access</label>
|
||||
<select class="form-select rounded" id="modalRAccess" name="can_run_r" required>
|
||||
<option value="1">Enabled</option>
|
||||
<option value="0">Disabled</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="d-grid">
|
||||
<button type="submit" class="btn btn-primary rounded">Update Role</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Reset Password Modal -->
|
||||
<div class="modal fade" id="resetPasswordModal" tabindex="-1" aria-labelledby="resetPasswordModalLabel" aria-hidden="true">
|
||||
<div class="modal-dialog modal-dialog-centered">
|
||||
<div class="modal-content rounded shadow-lg">
|
||||
<div class="modal-header text-white rounded-top" style="background-color: #28a745;">
|
||||
<h5 class="modal-title" id="resetPasswordModalLabel">Reset User Password</h5>
|
||||
<button type="button" class="btn-close btn-close-white" data-bs-dismiss="modal" aria-label="Close"></button>
|
||||
</div>
|
||||
<div class="modal-body p-4">
|
||||
<form action="manage_users.php" method="POST" id="resetPasswordForm" autocomplete="off">
|
||||
<input type="hidden" name="action_type" value="reset_password">
|
||||
<input type="hidden" name="user_id" id="resetUserId">
|
||||
<div class="mb-3">
|
||||
<label for="resetUsername" class="form-label">Username</label>
|
||||
<input type="text" class="form-control rounded" id="resetUsername" readonly>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="newPassword" class="form-label">New Password <span class="text-danger">*</span></label>
|
||||
<input type="password" class="form-control rounded" id="newPassword" name="new_password" required minlength="8">
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="confirmNewPassword" class="form-label">Confirm New Password <span class="text-danger">*</span></label>
|
||||
<input type="password" class="form-control rounded" id="confirmNewPassword" name="confirm_password" required minlength="8">
|
||||
</div>
|
||||
<div class="alert alert-warning rounded d-flex align-items-start gap-2">
|
||||
<i class="fas fa-exclamation-triangle mt-1"></i>
|
||||
<span>Resetting the password will immediately replace the user's existing credentials. Provide the new password to the user securely.</span>
|
||||
</div>
|
||||
<div class="d-grid">
|
||||
<button type="submit" class="btn btn-primary rounded"><i class="fas fa-key me-2"></i>Reset Password</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Add New User Modal -->
|
||||
<div class="modal fade" id="addUserModal" tabindex="-1" aria-labelledby="addUserModalLabel" aria-hidden="true">
|
||||
<div class="modal-dialog modal-dialog-centered modal-lg">
|
||||
<div class="modal-content rounded shadow-lg">
|
||||
<div class="modal-header text-white rounded-top" style="background-color: #28a745;">
|
||||
<h5 class="modal-title" id="addUserModalLabel">Add New User</h5>
|
||||
<button type="button" class="btn-close btn-close-white" data-bs-dismiss="modal" aria-label="Close"></button>
|
||||
</div>
|
||||
<div class="modal-body p-4">
|
||||
<form action="manage_users.php" method="POST" id="addUserForm">
|
||||
<input type="hidden" name="action_type" value="add_user">
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<h6 class="mb-3 text-primary">Personal Information</h6>
|
||||
<div class="mb-3">
|
||||
<label for="addFirstName" class="form-label">First Name (EN) <span class="text-danger">*</span></label>
|
||||
<input type="text" class="form-control rounded" id="addFirstName" name="first_name_en" required>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="addLastName" class="form-label">Last Name (EN) <span class="text-danger">*</span></label>
|
||||
<input type="text" class="form-control rounded" id="addLastName" name="last_name_en" required>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="addSex" class="form-label">Sex <span class="text-danger">*</span></label>
|
||||
<select class="form-select rounded" id="addSex" name="sex" required>
|
||||
<option value="">Select...</option>
|
||||
<option value="Male">Male</option>
|
||||
<option value="Female">Female</option>
|
||||
<option value="Other">Other</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="addDob" class="form-label">Date of Birth <span class="text-danger">*</span></label>
|
||||
<input type="date" class="form-control rounded" id="addDob" name="dob" required>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="addPob" class="form-label">Place of Birth</label>
|
||||
<input type="text" class="form-control rounded" id="addPob" name="pob">
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="addNationality" class="form-label">Nationality</label>
|
||||
<input type="text" class="form-control rounded" id="addNationality" name="nationality" value="Cambodian">
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<h6 class="mb-3 text-primary">Contact Information</h6>
|
||||
<div class="mb-3">
|
||||
<label for="addPhoneNumber" class="form-label">Phone Number</label>
|
||||
<input type="tel" class="form-control rounded" id="addPhoneNumber" name="phone_number">
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="addEmail" class="form-label">Email address</label>
|
||||
<input type="email" class="form-control rounded" id="addEmail" name="email">
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="addTelegram" class="form-label">Telegram</label>
|
||||
<input type="text" class="form-control rounded" id="addTelegram" name="telegram">
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="addNote" class="form-label">Note</label>
|
||||
<textarea class="form-control rounded" id="addNote" name="note" rows="2"></textarea>
|
||||
</div>
|
||||
<h6 class="mb-3 text-primary">Account Information</h6>
|
||||
<div class="mb-3">
|
||||
<label for="addUsername" class="form-label">Username <span class="text-danger">*</span></label>
|
||||
<input type="text" class="form-control rounded" id="addUsername" name="username" required>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="addPassword" class="form-label">Password <span class="text-danger">*</span></label>
|
||||
<input type="password" class="form-control rounded" id="addPassword" name="password" required>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="addConfirmPassword" class="form-label">Confirm Password <span class="text-danger">*</span></label>
|
||||
<input type="password" class="form-control rounded" id="addConfirmPassword" name="confirm_password" required>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="addUserRoleNew" class="form-label">Assign Role</label>
|
||||
<select class="form-select rounded" id="addUserRoleNew" name="user_role_new" required>
|
||||
<option value="Data User">Data User</option>
|
||||
<option value="Data Contributor">Data Contributor</option>
|
||||
<option value="Data Owner">Data Owner</option>
|
||||
<option value="DAC Staff">DAC Staff</option>
|
||||
<option value="Inactive">Inactive</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="form-check form-switch mb-3">
|
||||
<input class="form-check-input" type="checkbox" role="switch" id="addUserRAccess" name="user_can_run_r" value="1">
|
||||
<label class="form-check-label" for="addUserRAccess">Allow R/Jupyter Access</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="d-grid mt-4">
|
||||
<button type="submit" class="btn btn-primary rounded">Add User</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
window.addEventListener('DOMContentLoaded', function () {
|
||||
var tooltipTriggerList = [].slice.call(document.querySelectorAll('[data-bs-tooltip="true"]'));
|
||||
tooltipTriggerList.forEach(function (tooltipTriggerEl) {
|
||||
if (window.bootstrap && bootstrap.Tooltip) {
|
||||
new bootstrap.Tooltip(tooltipTriggerEl);
|
||||
}
|
||||
});
|
||||
|
||||
// JavaScript to populate the modal fields when "Change Role" button is clicked
|
||||
var editUserModal = document.getElementById('editUserModal');
|
||||
if (editUserModal) {
|
||||
editUserModal.addEventListener('show.bs.modal', function (event) {
|
||||
var button = event.relatedTarget; // Button that triggered the modal
|
||||
var userId = button.getAttribute('data-user-id');
|
||||
var username = button.getAttribute('data-username');
|
||||
var currentStatus = button.getAttribute('data-current-status');
|
||||
var canRunR = button.getAttribute('data-can-run-r') || '0';
|
||||
|
||||
var modalUserId = editUserModal.querySelector('#modalUserId');
|
||||
var modalUsername = editUserModal.querySelector('#modalUsername');
|
||||
var modalCurrentStatus = editUserModal.querySelector('#modalCurrentStatus');
|
||||
var newStatusSelect = editUserModal.querySelector('#newStatus');
|
||||
var modalRAccess = editUserModal.querySelector('#modalRAccess');
|
||||
|
||||
modalUserId.value = userId;
|
||||
modalUsername.value = username;
|
||||
modalCurrentStatus.value = currentStatus;
|
||||
newStatusSelect.value = currentStatus; // Set default selected option to current status
|
||||
if (modalRAccess) {
|
||||
modalRAccess.value = canRunR;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// JavaScript for password confirmation in Add New User Modal
|
||||
var addUserForm = document.getElementById('addUserForm');
|
||||
if (addUserForm) {
|
||||
addUserForm.addEventListener('submit', function(event) {
|
||||
var password = document.getElementById('addPassword').value;
|
||||
var confirmPassword = document.getElementById('addConfirmPassword').value;
|
||||
|
||||
if (password !== confirmPassword) {
|
||||
alert('Passwords do not match!');
|
||||
event.preventDefault(); // Prevent form submission
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
// Populate Reset Password modal
|
||||
var resetPasswordModal = document.getElementById('resetPasswordModal');
|
||||
if (resetPasswordModal) {
|
||||
resetPasswordModal.addEventListener('show.bs.modal', function (event) {
|
||||
var trigger = event.relatedTarget;
|
||||
var userIdField = document.getElementById('resetUserId');
|
||||
var usernameField = document.getElementById('resetUsername');
|
||||
var newPasswordField = document.getElementById('newPassword');
|
||||
var confirmPasswordField = document.getElementById('confirmNewPassword');
|
||||
|
||||
if (trigger && userIdField && usernameField) {
|
||||
userIdField.value = trigger.getAttribute('data-user-id') || '';
|
||||
usernameField.value = trigger.getAttribute('data-username') || '';
|
||||
}
|
||||
if (newPasswordField) newPasswordField.value = '';
|
||||
if (confirmPasswordField) confirmPasswordField.value = '';
|
||||
});
|
||||
}
|
||||
|
||||
var resetPasswordForm = document.getElementById('resetPasswordForm');
|
||||
if (resetPasswordForm) {
|
||||
resetPasswordForm.addEventListener('submit', function (event) {
|
||||
var newPassword = document.getElementById('newPassword').value;
|
||||
var confirmPassword = document.getElementById('confirmNewPassword').value;
|
||||
if (newPassword !== confirmPassword) {
|
||||
alert('Passwords do not match!');
|
||||
event.preventDefault();
|
||||
return false;
|
||||
}
|
||||
if (newPassword.length < 8) {
|
||||
alert('Password must be at least 8 characters.');
|
||||
event.preventDefault();
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
});
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<!-- Footer -->
|
||||
<?php
|
||||
// Include Footer file for owner pages
|
||||
include_once("../includes/footer_admin.php");
|
||||
?>
|
||||
</body>
|
||||
</html>
|
||||
162
admin/r_in_jupyter.php
Normal file
162
admin/r_in_jupyter.php
Normal file
@@ -0,0 +1,162 @@
|
||||
<?php
|
||||
// admin/r_in_jupyter.php
|
||||
session_start();
|
||||
require_once '../config.php';
|
||||
require_once '../includes/auth.php';
|
||||
require_once '../classes/DataSource.php';
|
||||
require_once '../includes/jupyter_helpers.php';
|
||||
|
||||
// Limit access to DAC Staff (Admin role)
|
||||
redirect_if_not_role('DAC Staff', '../index.php');
|
||||
|
||||
$hasRJupyterAccess = has_r_access();
|
||||
$workspaceSync = ['synced' => [], 'missing' => [], 'workspace_dir' => null];
|
||||
$workspaceRelativeDir = null;
|
||||
$workspaceError = null;
|
||||
|
||||
if ($hasRJupyterAccess && isset($_SESSION['person_id'])) {
|
||||
$dataSourceManager = new DataSource($pdo);
|
||||
try {
|
||||
$workspaceSync = $dataSourceManager->prepareJupyterWorkspace(
|
||||
(int) $_SESSION['person_id'],
|
||||
dirname(__DIR__) . '/uploads/jupyter_workspace'
|
||||
);
|
||||
$workspaceRelativeDir = 'datasources/user_' . (int) $_SESSION['person_id'];
|
||||
} catch (Exception $e) {
|
||||
$workspaceError = $e->getMessage();
|
||||
}
|
||||
}
|
||||
|
||||
$jupyterBaseUrl = dsp_jupyter_base_url();
|
||||
$jupyterToken = dsp_jupyter_token();
|
||||
$jupyterIframeUrl = dsp_jupyter_iframe_url(
|
||||
$jupyterBaseUrl,
|
||||
$jupyterToken,
|
||||
isset($_SESSION['person_id']) ? (int) $_SESSION['person_id'] : null
|
||||
);
|
||||
?>
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<?php include_once("../includes/header_admin.php"); ?>
|
||||
<body>
|
||||
<div class="wrapper">
|
||||
<?php include_once("../includes/nav_admin.php"); ?>
|
||||
|
||||
<div class="main-content">
|
||||
<nav class="navbar navbar-expand-lg navbar-light bg-light mb-4 rounded-3">
|
||||
<div class="container-fluid">
|
||||
<a class="navbar-brand" href="#">R in JupyterHub</a>
|
||||
<div class="d-flex">
|
||||
<span class="navbar-text me-3">
|
||||
Welcome, <?php echo htmlspecialchars($_SESSION['username']); ?>!
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<div class="card shadow-sm rounded mb-4">
|
||||
<div class="card-header bg-light rounded-top d-flex align-items-center justify-content-between">
|
||||
<h5 class="mb-0">Full JupyterLab Access</h5>
|
||||
<?php if ($hasRJupyterAccess): ?>
|
||||
<span class="badge bg-success-subtle text-success rounded-pill">
|
||||
<i class="fas fa-flask me-1"></i> Enabled
|
||||
</span>
|
||||
<?php else: ?>
|
||||
<span class="badge bg-warning-subtle text-warning rounded-pill">
|
||||
<i class="fas fa-lock me-1"></i> Disabled
|
||||
</span>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<?php if ($hasRJupyterAccess): ?>
|
||||
<?php if ($workspaceError): ?>
|
||||
<div class="alert alert-danger rounded mb-3">
|
||||
<strong>Workspace error:</strong> <?= htmlspecialchars($workspaceError) ?>
|
||||
</div>
|
||||
<?php else: ?>
|
||||
<p class="mb-3">
|
||||
Approved data sources have been synced to
|
||||
<code><?= htmlspecialchars($workspaceRelativeDir ?: 'datasources') ?></code>
|
||||
inside the Jupyter environment. Only files you are approved to use are available.
|
||||
</p>
|
||||
<?php if (!empty($workspaceSync['synced'])): ?>
|
||||
<div class="table-responsive mb-3">
|
||||
<table class="table table-sm table-striped align-middle">
|
||||
<thead class="table-light">
|
||||
<tr>
|
||||
<th>#</th>
|
||||
<th>Data Source</th>
|
||||
<th>Data Type</th>
|
||||
<th>Category</th>
|
||||
<th>Filename</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<?php foreach ($workspaceSync['synced'] as $idx => $syncedItem): ?>
|
||||
<tr>
|
||||
<td><?= $idx + 1 ?></td>
|
||||
<td><?= htmlspecialchars($syncedItem['title']) ?></td>
|
||||
<td><?= htmlspecialchars($syncedItem['data_type'] ?? 'N/A') ?></td>
|
||||
<td><?= htmlspecialchars($syncedItem['category'] ?? 'N/A') ?></td>
|
||||
<td><code><?= htmlspecialchars(basename($syncedItem['relative_path'])) ?></code></td>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<?php else: ?>
|
||||
<div class="alert alert-info rounded mb-3">
|
||||
No approved data sources were found for your account. Use Manage Users to approve access.
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
<?php if (!empty($workspaceSync['missing'])): ?>
|
||||
<div class="alert alert-warning rounded mb-3">
|
||||
<strong>Some datasets could not be synced:</strong>
|
||||
<ul class="mb-0">
|
||||
<?php foreach ($workspaceSync['missing'] as $missingItem): ?>
|
||||
<li><?= htmlspecialchars($missingItem['title']) ?> — <?= htmlspecialchars($missingItem['reason']) ?></li>
|
||||
<?php endforeach; ?>
|
||||
</ul>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
<?php endif; ?>
|
||||
<p class="mb-3">
|
||||
Use the embedded Jupyter workspace to manage R notebooks, explore uploaded datasets, and
|
||||
collaborate with Data Owners. This view runs with your admin permissions.
|
||||
</p>
|
||||
<div class="ratio ratio-16x9 border rounded overflow-hidden">
|
||||
<iframe
|
||||
src="<?= htmlspecialchars($jupyterIframeUrl, ENT_QUOTES, 'UTF-8') ?>"
|
||||
title="R in JupyterHub for DAC Staff"
|
||||
allowfullscreen
|
||||
loading="lazy"
|
||||
referrerpolicy="no-referrer"
|
||||
></iframe>
|
||||
</div>
|
||||
<p class="mt-3 mb-0">
|
||||
Prefer the full window? <a href="<?= htmlspecialchars($jupyterIframeUrl, ENT_QUOTES, 'UTF-8') ?>" target="_blank" rel="noopener">Open Jupyter in a new tab</a>.
|
||||
</p>
|
||||
<?php else: ?>
|
||||
<div class="alert alert-warning rounded d-flex align-items-start gap-3">
|
||||
<i class="fas fa-circle-info mt-1"></i>
|
||||
<div>
|
||||
<strong>R in JupyterHub is currently disabled for your account.</strong><br>
|
||||
Visit <a href="manage_users.php">Manage Users</a> to enable R/Jupyter access for yourself or ask another DAC Staff member to toggle the permission.
|
||||
</div>
|
||||
</div>
|
||||
<p class="mb-0">
|
||||
Once access is enabled, refresh this page to launch the JupyterLab workspace.
|
||||
</p>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
</div>
|
||||
<div class="alert alert-secondary border-0 bg-light-subtle text-muted small">
|
||||
Need the current Jupyter configuration? Visit
|
||||
<a href="../install_config.php#r-in-jupyter-service" class="alert-link">Install & Configuration</a>
|
||||
for defaults, overrides, and runtime details.
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<?php include_once("../includes/footer_admin.php"); ?>
|
||||
</body>
|
||||
</html>
|
||||
Reference in New Issue
Block a user