DSP Project first push, date: 29/01/2026
This commit is contained in:
214
data_owner/dashboard.php
Normal file
214
data_owner/dashboard.php
Normal file
@@ -0,0 +1,214 @@
|
||||
<?php
|
||||
// data_owner/dashboard.php
|
||||
session_start();
|
||||
require_once '../config.php';
|
||||
require_once '../includes/auth.php';
|
||||
require_once '../classes/User.php';
|
||||
require_once '../classes/DataSource.php'; // For counts
|
||||
|
||||
// Ensure only Data Owners can access this dashboard
|
||||
redirect_if_not_role('Data Owner');
|
||||
|
||||
$user_id = $_SESSION['user_id'];
|
||||
$person_id = $_SESSION['person_id']; // The fkisp_id_of for the data owner
|
||||
$username = $_SESSION['username'];
|
||||
$user_status = $_SESSION['user_status'];
|
||||
|
||||
// Instantiate classes
|
||||
$user_manager = new User($pdo);
|
||||
$data_source_manager = new DataSource($pdo);
|
||||
|
||||
// Get dynamic counts for the Data Owner
|
||||
$my_data_sources_count = count($data_source_manager->getDataSources($person_id));
|
||||
$pending_permissions_count = count($data_source_manager->getPermissionRequestsForOwner($person_id, 'Pending'));
|
||||
$usageByUser = $data_source_manager->getUsageByUserForOwner($person_id, 6);
|
||||
$data_accesses_last_30_days = 0;
|
||||
if (!empty($usageByUser)) {
|
||||
foreach ($usageByUser as $row) {
|
||||
$data_accesses_last_30_days += (int) ($row['usage_count'] ?? 0);
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<!-- Header -->
|
||||
<?php
|
||||
// Include header file for admin pages
|
||||
include_once("../includes/header_owner.php");
|
||||
?>
|
||||
|
||||
<body>
|
||||
<div class="wrapper">
|
||||
<!-- Sidebar -->
|
||||
<?php
|
||||
// Include header file for admin pages
|
||||
include_once("../includes/nav_owner.php");
|
||||
?>
|
||||
|
||||
<!-- Page 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="#"> Dashboard</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'])): ?>
|
||||
<div class="alert alert-<?= $_SESSION['message_type'] ?> alert-dismissible fade show rounded" role="alert">
|
||||
<?= $_SESSION['message'] ?>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
|
||||
</div>
|
||||
<?php
|
||||
unset($_SESSION['message']);
|
||||
unset($_SESSION['message_type']);
|
||||
?>
|
||||
<?php endif; ?>
|
||||
|
||||
<div class="row g-4">
|
||||
<div class="col-md-4">
|
||||
<div class="card shadow-sm rounded">
|
||||
<div class="card-body">
|
||||
<h5 class="card-title text-primary"><i class="fas fa-database me-2"></i> My Data Sources</h5>
|
||||
<p class="card-text fs-2"><?= $my_data_sources_count ?></p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<div class="card shadow-sm rounded">
|
||||
<div class="card-body">
|
||||
<h5 class="card-title text-warning"><i class="fas fa-hourglass-half me-2"></i> Pending Permissions</h5>
|
||||
<p class="card-text fs-2"><?= $pending_permissions_count ?></p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<div class="card shadow-sm rounded">
|
||||
<div class="card-body">
|
||||
<h5 class="card-title text-success"><i class="fas fa-download me-2"></i> Data Accesses (Last 30 Days)</h5>
|
||||
<p class="card-text fs-2"><?= $data_accesses_last_30_days ?></p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<?php
|
||||
$chartLabels = [];
|
||||
$chartValues = [];
|
||||
if (!empty($usageByUser)) {
|
||||
foreach ($usageByUser as $row) {
|
||||
$chartLabels[] = $row['username'] ?? 'Unknown';
|
||||
$chartValues[] = (int) ($row['usage_count'] ?? 0);
|
||||
}
|
||||
}
|
||||
?>
|
||||
|
||||
<div class="row mt-5">
|
||||
<div class="col-lg-6">
|
||||
<div class="card shadow-sm rounded h-100">
|
||||
<div class="card-header bg-light rounded-top">
|
||||
<h5 class="mb-0">Usage Breakdown by User</h5>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<?php if (!empty($chartValues)): ?>
|
||||
<canvas id="usagePieChart" height="280"></canvas>
|
||||
<?php else: ?>
|
||||
<div class="alert alert-info rounded mb-0">No data usage recorded yet.</div>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mt-5">
|
||||
<h3>Quick Actions</h3>
|
||||
<div class="d-flex flex-wrap gap-3">
|
||||
<a href="manage_my_datasources.php?action=add" class="btn btn-primary rounded"><i class="fas fa-plus-circle me-2"></i> Add New Data Source</a>
|
||||
<a href="manage_permissions.php" class="btn btn-warning rounded"><i class="fas fa-user-check me-2"></i> Review Permissions</a>
|
||||
<a href="my_analytics.php" class="btn btn-info rounded"><i class="fas fa-chart-pie me-2"></i> View My Data Usage</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Placeholder for recent activity related to their data sources -->
|
||||
<div class="mt-5">
|
||||
<h3>Recent Activity on My Data Sources</h3>
|
||||
<ul class="list-group shadow-sm rounded">
|
||||
<?php
|
||||
// Example recent activities (replace with actual data from dsps_tbl_datasource_used and dsps_tbl_anonymous)
|
||||
// You'd need to fetch these from the database, potentially joining with dsps_tbl_datasource
|
||||
$recent_activities = [
|
||||
['text' => 'User John Doe requested access to \'Population Census 2023\'.', 'time' => 'Just now', 'type' => 'info'],
|
||||
['text' => '\'Health Data Q1 2024\' was downloaded 5 times today.', 'time' => '2 hours ago', 'type' => 'success'],
|
||||
['text' => 'You updated \'Education Statistics 2022\'.', 'time' => 'Yesterday', 'type' => 'secondary'],
|
||||
];
|
||||
foreach ($recent_activities as $activity) {
|
||||
echo '<li class="list-group-item d-flex justify-content-between align-items-center">';
|
||||
echo htmlspecialchars($activity['text']);
|
||||
echo '<span class="badge bg-' . htmlspecialchars($activity['type']) . '">' . htmlspecialchars($activity['time']) . '</span>';
|
||||
echo '</li>';
|
||||
}
|
||||
?>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
<!-- Footer -->
|
||||
<?php
|
||||
// Include Footer file for owner pages
|
||||
include_once("../includes/footer_owner.php");
|
||||
?>
|
||||
<script src="https://cdn.jsdelivr.net/npm/chart.js@4.4.6/dist/chart.umd.min.js" integrity="sha256-VjZ1tcHTul3e8DqRL3OjaxAg/P070MqxsVXni4eWh05=" crossorigin="anonymous"></script>
|
||||
<script>
|
||||
(function () {
|
||||
const labels = <?= json_encode(array_map('htmlspecialchars', $chartLabels)) ?>;
|
||||
const values = <?= json_encode($chartValues) ?>;
|
||||
|
||||
if (!Array.isArray(labels) || !Array.isArray(values) || labels.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
const ctx = document.getElementById('usagePieChart');
|
||||
if (!ctx) {
|
||||
return;
|
||||
}
|
||||
|
||||
const backgroundColors = [
|
||||
'#0d6efd', '#198754', '#dc3545', '#fd7e14', '#6f42c1', '#20c997', '#0dcaf0'
|
||||
];
|
||||
|
||||
new Chart(ctx, {
|
||||
type: 'pie',
|
||||
data: {
|
||||
labels: labels,
|
||||
datasets: [{
|
||||
data: values,
|
||||
backgroundColor: backgroundColors,
|
||||
borderWidth: 1
|
||||
}]
|
||||
},
|
||||
options: {
|
||||
responsive: true,
|
||||
plugins: {
|
||||
legend: { position: 'bottom' },
|
||||
tooltip: {
|
||||
callbacks: {
|
||||
label: function(context) {
|
||||
const label = context.label || '';
|
||||
const value = context.parsed || 0;
|
||||
return `${label}: ${value}`;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
})();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
542
data_owner/manage_my_datasources.php
Normal file
542
data_owner/manage_my_datasources.php
Normal file
@@ -0,0 +1,542 @@
|
||||
<?php
|
||||
// data_owner/manage_my_datasources.php
|
||||
session_start();
|
||||
require_once '../config.php';
|
||||
require_once '../includes/auth.php';
|
||||
require_once '../classes/DataSource.php';
|
||||
|
||||
// Ensure only Data Owners can access this page
|
||||
redirect_if_not_role('Data Owner');
|
||||
|
||||
$data_source_manager = new DataSource($pdo);
|
||||
$user_id = $_SESSION['user_id'];
|
||||
$owner_person_id = $_SESSION['person_id'];
|
||||
|
||||
$action = $_GET['action'] ?? 'list';
|
||||
$ds_id = $_GET['id'] ?? null;
|
||||
$datasource_data = [];
|
||||
|
||||
// Fetch dropdown data
|
||||
$data_types = $data_source_manager->getAllDataTypes();
|
||||
$categories = $data_source_manager->getAllCategories();
|
||||
$primaryRulesMap = [];
|
||||
foreach ($data_types as $type) {
|
||||
$typeName = $type['dspstds_name_en'] ?? null;
|
||||
$rules = $data_source_manager->getPrimaryFileRulesForType($typeName);
|
||||
$acceptList = [];
|
||||
foreach ($rules['extensions'] ?? [] as $ext) {
|
||||
$acceptList[] = '.' . strtolower($ext);
|
||||
}
|
||||
$primaryRulesMap[$type['pkdspstds_id']] = [
|
||||
'accept' => $acceptList,
|
||||
'description' => $rules['description'] ?? 'CSV, JSON, PDF, XLS, XLSX',
|
||||
];
|
||||
}
|
||||
$defaultPrimaryRules = $data_source_manager->getPrimaryFileRulesForType(null);
|
||||
$defaultPrimaryAccept = [];
|
||||
foreach ($defaultPrimaryRules['extensions'] ?? [] as $ext) {
|
||||
$defaultPrimaryAccept[] = '.' . strtolower($ext);
|
||||
}
|
||||
$initialPrimaryDescription = $defaultPrimaryRules['description'] ?? 'CSV, JSON, PDF, XLS, XLSX';
|
||||
|
||||
|
||||
// Handle form submissions
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
$title_en = trim($_POST['title_en'] ?? '');
|
||||
$title_kh = trim($_POST['title_kh'] ?? '');
|
||||
$description = trim($_POST['description'] ?? '');
|
||||
$type_id = filter_var($_POST['type_id'] ?? '', FILTER_SANITIZE_NUMBER_INT);
|
||||
$category_id = filter_var($_POST['category_id'] ?? '', FILTER_SANITIZE_NUMBER_INT);
|
||||
$public_date = trim($_POST['public_date'] ?? '');
|
||||
$status = trim($_POST['status'] ?? 'Pending Review');
|
||||
$selectedDataType = null;
|
||||
if (!empty($type_id)) {
|
||||
$selectedDataType = $data_source_manager->getDataTypeById((int)$type_id);
|
||||
}
|
||||
$current_files = [
|
||||
'dspsds_filename' => trim($_POST['current_filename'] ?? ''),
|
||||
'dspsds_filename1' => trim($_POST['current_filename1'] ?? ''),
|
||||
'dspsds_filename2' => trim($_POST['current_filename2'] ?? ''),
|
||||
'dspsds_filename3' => trim($_POST['current_filename3'] ?? ''),
|
||||
];
|
||||
|
||||
$final_files = $current_files;
|
||||
$file_inputs = [
|
||||
'dspsds_filename' => 'data_file',
|
||||
'dspsds_filename1' => 'data_file1',
|
||||
'dspsds_filename2' => 'data_file2',
|
||||
'dspsds_filename3' => 'data_file3',
|
||||
];
|
||||
$file_labels = [
|
||||
'dspsds_filename' => 'Primary Data File',
|
||||
'dspsds_filename1' => 'Questionnaire / Data Dictionary',
|
||||
'dspsds_filename2' => 'Protocol / User Guide',
|
||||
'dspsds_filename3' => 'Other Supporting Document',
|
||||
];
|
||||
$remove_files = $_POST['remove_files'] ?? [];
|
||||
if (!is_array($remove_files)) {
|
||||
$remove_files = [$remove_files];
|
||||
}
|
||||
|
||||
foreach ($file_inputs as $column => $inputName) {
|
||||
if (!isset($_FILES[$inputName]) || $_FILES[$inputName]['error'] === UPLOAD_ERR_NO_FILE) {
|
||||
continue;
|
||||
}
|
||||
|
||||
try {
|
||||
if ($_FILES[$inputName]['error'] !== UPLOAD_ERR_OK) {
|
||||
throw new Exception('Upload error code: ' . $_FILES[$inputName]['error']);
|
||||
}
|
||||
$fileRules = null;
|
||||
if ($column === 'dspsds_filename') {
|
||||
$fileRules = $data_source_manager->getPrimaryFileRulesForType($selectedDataType['dspstds_name_en'] ?? null);
|
||||
}
|
||||
$uploadedName = $data_source_manager->handleDataSourceFileUpload($_FILES[$inputName], $fileRules);
|
||||
if ($uploadedName) {
|
||||
if (!empty($current_files[$column]) && $current_files[$column] !== $uploadedName) {
|
||||
$oldPath = $data_source_manager->getUploadDir() . $current_files[$column];
|
||||
if (is_file($oldPath)) {
|
||||
unlink($oldPath);
|
||||
}
|
||||
}
|
||||
$final_files[$column] = $uploadedName;
|
||||
}
|
||||
} catch (Exception $e) {
|
||||
$friendlyLabel = $file_labels[$column] ?? $inputName;
|
||||
set_message('File upload failed for ' . htmlspecialchars($friendlyLabel) . ': ' . $e->getMessage(), 'danger');
|
||||
$final_files[$column] = $current_files[$column];
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($remove_files as $column) {
|
||||
if (!array_key_exists($column, $final_files)) {
|
||||
continue;
|
||||
}
|
||||
if (!empty($current_files[$column])) {
|
||||
$oldPath = $data_source_manager->getUploadDir() . $current_files[$column];
|
||||
if (is_file($oldPath)) {
|
||||
unlink($oldPath);
|
||||
}
|
||||
}
|
||||
$final_files[$column] = '';
|
||||
}
|
||||
|
||||
// Basic validation for required fields
|
||||
if (empty($title_en) || empty($type_id) || empty($category_id)) {
|
||||
set_message("Title, Data Type, and Category are required.", "danger");
|
||||
// Redirect to preserve form data or re-display form with errors
|
||||
// For now, we'll just redirect to list, but a better UX would be to stay on the form
|
||||
header("Location: manage_my_datasources.php?action=" . ($action === 'add_submit' ? 'add' : 'edit&id=' . $ds_id));
|
||||
exit();
|
||||
}
|
||||
|
||||
// Determine the public date to pass to the add/update methods
|
||||
// The DataSource class's add/update methods have logic for this, so we'll pass it as a string or null
|
||||
$final_public_date = (!empty($public_date) && $status === 'Active') ? $public_date : null;
|
||||
|
||||
|
||||
if ($action === 'add_submit') {
|
||||
try {
|
||||
// Corrected call to addDataSource
|
||||
if ($data_source_manager->addDataSource(
|
||||
$type_id,
|
||||
$category_id,
|
||||
$owner_person_id, // Data owner is the logged-in person
|
||||
$final_files['dspsds_filename'],
|
||||
$title_en,
|
||||
$title_kh,
|
||||
$description,
|
||||
$status,
|
||||
$user_id, // User who registered it (logged-in user)
|
||||
$final_files['dspsds_filename1'],
|
||||
$final_files['dspsds_filename2'],
|
||||
$final_files['dspsds_filename3']
|
||||
)) {
|
||||
set_message("Data source added successfully!", "success");
|
||||
} else {
|
||||
set_message("Failed to add data source.", "danger");
|
||||
}
|
||||
} catch (Exception $e) {
|
||||
set_message("Error adding data source: " . $e->getMessage(), "danger");
|
||||
}
|
||||
} elseif ($action === 'edit_submit' && $ds_id) {
|
||||
try {
|
||||
// Corrected call to updateDataSource
|
||||
if ($data_source_manager->updateDataSource(
|
||||
$ds_id,
|
||||
$type_id,
|
||||
$category_id,
|
||||
$owner_person_id, // Data owner is the logged-in person
|
||||
$final_files['dspsds_filename'],
|
||||
$title_en,
|
||||
$title_kh,
|
||||
$description,
|
||||
$status,
|
||||
$user_id, // User who modified it (logged-in user)
|
||||
$final_files['dspsds_filename1'],
|
||||
$final_files['dspsds_filename2'],
|
||||
$final_files['dspsds_filename3']
|
||||
)) {
|
||||
set_message("Data source updated successfully!", "success");
|
||||
} else {
|
||||
set_message("Failed to update data source.", "danger");
|
||||
}
|
||||
} catch (Exception $e) {
|
||||
set_message("Error updating data source: " . $e->getMessage(), "danger");
|
||||
}
|
||||
}
|
||||
// Redirect after POST to prevent form resubmission
|
||||
header("Location: manage_my_datasources.php");
|
||||
exit();
|
||||
}
|
||||
|
||||
// Handle GET actions
|
||||
if ($action === 'edit' && $ds_id) {
|
||||
$datasource_data = $data_source_manager->getDataSourceById($ds_id);
|
||||
// Crucial security check: Ensure the logged-in owner actually owns this data source
|
||||
if (!$datasource_data || $datasource_data['fkisp_id_of'] != $owner_person_id) {
|
||||
set_message("Data source not found or you don't have permission to edit it.", "danger");
|
||||
header("Location: manage_my_datasources.php");
|
||||
exit();
|
||||
}
|
||||
} elseif ($action === 'delete' && $ds_id) {
|
||||
$datasource = $data_source_manager->getDataSourceById($ds_id);
|
||||
// Crucial security check: Ensure the logged-in owner actually owns this data source
|
||||
if ($datasource && $datasource['fkisp_id_of'] == $owner_person_id) {
|
||||
// Delete associated file on the server
|
||||
$fileColumns = ['dspsds_filename', 'dspsds_filename1', 'dspsds_filename2', 'dspsds_filename3'];
|
||||
foreach ($fileColumns as $column) {
|
||||
if (!empty($datasource[$column])) {
|
||||
$filePath = $data_source_manager->getUploadDir() . $datasource[$column];
|
||||
if (is_file($filePath)) {
|
||||
unlink($filePath);
|
||||
}
|
||||
}
|
||||
}
|
||||
if ($data_source_manager->deleteDataSource($ds_id)) {
|
||||
set_message("Data source deleted successfully!", "success");
|
||||
} else {
|
||||
set_message("Failed to delete data source.", "danger");
|
||||
}
|
||||
} else {
|
||||
set_message("Data source not found or you don't have permission to delete it.", "warning");
|
||||
}
|
||||
header("Location: manage_my_datasources.php");
|
||||
exit();
|
||||
}
|
||||
|
||||
// Fetch data sources for the current owner for display
|
||||
$my_data_sources = $data_source_manager->getDataSources($owner_person_id);
|
||||
$uploadsWebPath = '../uploads/datasources/';
|
||||
|
||||
?>
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<!-- Header -->
|
||||
<?php
|
||||
// Include header file for admin pages
|
||||
include_once("../includes/header_owner.php");
|
||||
?>
|
||||
<body>
|
||||
<div class="wrapper">
|
||||
<!-- Sidebar -->
|
||||
<?php
|
||||
// Include header file for admin pages
|
||||
include_once("../includes/nav_owner.php");
|
||||
?>
|
||||
|
||||
<!-- Page 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="#"> My Data Sources</a>
|
||||
<div class="d-flex">
|
||||
<span class="navbar-text me-3">
|
||||
Welcome, <?php echo htmlspecialchars($_SESSION['username']); ?>!
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<div class="d-flex justify-content-between align-items-center mb-4">
|
||||
<a href="manage_my_datasources.php?action=add" class="btn btn-primary rounded">
|
||||
<i class="fas fa-plus-circle me-2"></i> Add New Data Source
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<?php if (isset($_SESSION['message'])): ?>
|
||||
<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>
|
||||
<?php
|
||||
unset($_SESSION['message']);
|
||||
unset($_SESSION['message_type']);
|
||||
?>
|
||||
<?php endif; ?>
|
||||
|
||||
<?php if ($action === 'add' || $action === 'edit'): ?>
|
||||
<div class="card shadow-sm rounded mb-4">
|
||||
<div class="card-header bg-light rounded-top">
|
||||
<h5 class="mb-0"><?= ($action === 'add' ? 'Add New' : 'Edit') ?> Data Source</h5>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<form action="manage_my_datasources.php?action=<?= ($action === 'add' ? 'add_submit' : 'edit_submit&id=' . htmlspecialchars($ds_id)) ?>" method="POST" enctype="multipart/form-data">
|
||||
<input type="hidden" name="current_filename" value="<?= htmlspecialchars($datasource_data['dspsds_filename'] ?? '') ?>">
|
||||
<input type="hidden" name="current_filename1" value="<?= htmlspecialchars($datasource_data['dspsds_filename1'] ?? '') ?>">
|
||||
<input type="hidden" name="current_filename2" value="<?= htmlspecialchars($datasource_data['dspsds_filename2'] ?? '') ?>">
|
||||
<input type="hidden" name="current_filename3" value="<?= htmlspecialchars($datasource_data['dspsds_filename3'] ?? '') ?>">
|
||||
<div class="mb-3">
|
||||
<label for="dsTitleEn" class="form-label">Title (English)</label>
|
||||
<input type="text" class="form-control rounded" id="dsTitleEn" name="title_en" value="<?= htmlspecialchars($datasource_data['dspsds_title_en'] ?? '') ?>" required>
|
||||
</div>
|
||||
<!--
|
||||
<div class="mb-3">
|
||||
<label for="dsTitleKh" class="form-label">Title (Khmer)</label>
|
||||
<input type="text" class="form-control rounded" id="dsTitleKh" name="title_kh" value="<?//= htmlspecialchars($datasource_data['dspsds_title_kh'] ?? '') ?>">
|
||||
</div>
|
||||
-->
|
||||
<div class="mb-3">
|
||||
<label for="dsDescription" class="form-label">Description</label>
|
||||
<textarea class="form-control rounded" id="dsDescription" name="description" rows="5"><?= htmlspecialchars($datasource_data['dspsds_description'] ?? '') ?></textarea>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="dsType" class="form-label">Data Type</label>
|
||||
<select class="form-select rounded" id="dsType" name="type_id" required>
|
||||
<option value="">Select Type...</option>
|
||||
<?php foreach ($data_types as $type): ?>
|
||||
<option value="<?= htmlspecialchars($type['pkdspstds_id']) ?>"
|
||||
<?= (isset($datasource_data['fkdspstds_id']) && $datasource_data['fkdspstds_id'] == $type['pkdspstds_id']) ? 'selected' : '' ?>>
|
||||
<?= htmlspecialchars($type['dspstds_name_en']) ?>
|
||||
</option>
|
||||
<?php endforeach; ?>
|
||||
</select>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="dsCategory" class="form-label">Category</label>
|
||||
<select class="form-select rounded" id="dsCategory" name="category_id" required>
|
||||
<option value="">Select Category...</option>
|
||||
<?php foreach ($categories as $category): ?>
|
||||
<option value="<?= htmlspecialchars($category['pkdspscate_id']) ?>"
|
||||
<?= (isset($datasource_data['fkdspscate_id']) && $datasource_data['fkdspscate_id'] == $category['pkdspscate_id']) ? 'selected' : '' ?>>
|
||||
<?= htmlspecialchars($category['dspscate_title_en']) ?>
|
||||
</option>
|
||||
<?php endforeach; ?>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
|
||||
<?php
|
||||
$currentTypeForDescription = $datasource_data['fkdspstds_id'] ?? null;
|
||||
if ($currentTypeForDescription && isset($primaryRulesMap[$currentTypeForDescription])) {
|
||||
$initialPrimaryDescription = $primaryRulesMap[$currentTypeForDescription]['description'] ?? $initialPrimaryDescription;
|
||||
}
|
||||
$fileInputsConfig = [
|
||||
[
|
||||
'label' => 'Primary Data File',
|
||||
'id' => 'dsDataFile',
|
||||
'name' => 'data_file',
|
||||
'column' => 'dspsds_filename',
|
||||
'help' => 'Upload a file that matches the selected data type.',
|
||||
],
|
||||
[
|
||||
'label' => 'Questionnaire / Data Dictionary',
|
||||
'id' => 'dsDataFile1',
|
||||
'name' => 'data_file1',
|
||||
'column' => 'dspsds_filename1',
|
||||
'help' => 'Upload a supporting document (PDF, XLSX, etc.)',
|
||||
],
|
||||
[
|
||||
'label' => 'Protocol / User Guide',
|
||||
'id' => 'dsDataFile2',
|
||||
'name' => 'data_file2',
|
||||
'column' => 'dspsds_filename2',
|
||||
'help' => 'Upload a protocol or user guide (PDF, DOCX, etc.)',
|
||||
],
|
||||
[
|
||||
'label' => 'Other Supporting Document',
|
||||
'id' => 'dsDataFile3',
|
||||
'name' => 'data_file3',
|
||||
'column' => 'dspsds_filename3',
|
||||
'help' => 'Optional additional document.',
|
||||
],
|
||||
];
|
||||
?>
|
||||
|
||||
<?php foreach ($fileInputsConfig as $config): ?>
|
||||
<?php $existingFile = $datasource_data[$config['column']] ?? ''; ?>
|
||||
<div class="mb-3">
|
||||
<label for="<?= htmlspecialchars($config['id']) ?>" class="form-label"><?= htmlspecialchars($config['label']) ?></label>
|
||||
<input type="file" class="form-control rounded" id="<?= htmlspecialchars($config['id']) ?>" name="<?= htmlspecialchars($config['name']) ?>">
|
||||
<?php if ($config['column'] === 'dspsds_filename'): ?>
|
||||
<small class="form-text text-muted">
|
||||
Allowed formats: <span id="primaryFileFormats"><?= htmlspecialchars($initialPrimaryDescription) ?></span>
|
||||
</small>
|
||||
<?php endif; ?>
|
||||
<?php if (!empty($existingFile)): ?>
|
||||
<?php
|
||||
$isUrl = preg_match('/^https?:\/\//i', $existingFile) === 1;
|
||||
$linkTarget = $isUrl ? $existingFile : $uploadsWebPath . rawurlencode($existingFile);
|
||||
$linkLabel = $isUrl ? 'View Link' : 'Download File';
|
||||
?>
|
||||
<small class="form-text text-muted mt-2">
|
||||
Current file: <a href="<?= htmlspecialchars($linkTarget) ?>" target="_blank" rel="noopener"><?= htmlspecialchars($linkLabel) ?></a>
|
||||
</small>
|
||||
<div class="form-check mt-2">
|
||||
<input class="form-check-input" type="checkbox" id="remove_<?= htmlspecialchars($config['id']) ?>" name="remove_files[]" value="<?= htmlspecialchars($config['column']) ?>">
|
||||
<label class="form-check-label" for="remove_<?= htmlspecialchars($config['id']) ?>">Remove existing file</label>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
<?php if (!empty($config['help'])): ?>
|
||||
<small class="form-text text-muted"><?= htmlspecialchars($config['help']) ?></small>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
<?php endforeach; ?>
|
||||
|
||||
<div class="mb-3">
|
||||
<label for="dsPublicDate" class="form-label">Public Date (Optional)</label>
|
||||
<input type="date" class="form-control rounded" id="dsPublicDate" name="public_date" value="<?= htmlspecialchars($datasource_data['dspsds_public_date'] ?? '') ?>">
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="dsStatus" class="form-label">Status</label>
|
||||
<select class="form-select rounded" id="dsStatus" name="status" required>
|
||||
<option value="Pending Review" <?= (isset($datasource_data['dspsds_status']) && $datasource_data['dspsds_status'] == 'Pending Review') ? 'selected' : '' ?>>Pending Review</option>
|
||||
<option value="Active" <?= (isset($datasource_data['dspsds_status']) && $datasource_data['dspsds_status'] == 'Active') ? 'selected' : '' ?>>Active</option>
|
||||
<option value="Inactive" <?= (isset($datasource_data['dspsds_status']) && $datasource_data['dspsds_status'] == 'Inactive') ? 'selected' : '' ?>>Inactive</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="d-flex justify-content-end gap-2">
|
||||
<a href="manage_my_datasources.php" class="btn btn-secondary rounded">Cancel</a>
|
||||
<button type="submit" class="btn btn-primary rounded"><?= ($action === 'add' ? 'Add' : 'Update') ?> Data Source</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
|
||||
<div class="card shadow-sm rounded">
|
||||
<div class="card-header bg-light rounded-top">
|
||||
<h5 class="mb-0">My Data Sources</h5>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<?php if (!empty($my_data_sources)): ?>
|
||||
<div class="table-responsive">
|
||||
<table class="table table-hover table-striped">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>ID</th>
|
||||
<th>Title (EN)</th>
|
||||
<th>Type</th>
|
||||
<th>Category</th>
|
||||
<th>Status</th>
|
||||
<th>Data Source</th>
|
||||
<th>Questionnaire</th>
|
||||
<th>User Guide</th>
|
||||
<th>Supporting Doc</th>
|
||||
<th>Registered Date</th>
|
||||
<th>Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<?php foreach ($my_data_sources as $ds): ?>
|
||||
<tr>
|
||||
<td><?= htmlspecialchars($ds['pkdspsds_id']) ?></td>
|
||||
<td><?= htmlspecialchars($ds['dspsds_title_en']) ?></td>
|
||||
<td><?= htmlspecialchars($ds['data_type_name']) ?></td> <!-- Corrected key -->
|
||||
<td><?= htmlspecialchars($ds['category_name']) ?></td> <!-- Corrected key -->
|
||||
<td>
|
||||
<span class="badge <?= ($ds['dspsds_status'] == 'Active' ? 'bg-success' : ($ds['dspsds_status'] == 'Pending Review' ? 'bg-warning' : 'bg-secondary')) ?>">
|
||||
<?= htmlspecialchars($ds['dspsds_status']) ?>
|
||||
</span>
|
||||
</td>
|
||||
<?php
|
||||
$primaryFile = $ds['dspsds_filename'] ?? '';
|
||||
echo '<td>';
|
||||
if (!empty($primaryFile)) {
|
||||
$isUrlPrimary = preg_match('/^https?:\/\//i', $primaryFile) === 1;
|
||||
$primaryTarget = $isUrlPrimary ? $primaryFile : $uploadsWebPath . rawurlencode($primaryFile);
|
||||
$primaryTitle = $isUrlPrimary ? 'External link' : 'Download Data Source';
|
||||
echo '<a class="btn btn-sm btn-outline-primary rounded-pill" href="' . htmlspecialchars($primaryTarget) . '" target="_blank" rel="noopener" title="' . htmlspecialchars($primaryTitle) . '"><i class="fas fa-database"></i></a>';
|
||||
} else {
|
||||
echo '<span class="text-muted">—</span>';
|
||||
}
|
||||
echo '</td>';
|
||||
?>
|
||||
<?php
|
||||
$fileCells = [
|
||||
'dspsds_filename1' => 'Questionnaire / Data Dictionary',
|
||||
'dspsds_filename2' => 'Protocol / User Guide',
|
||||
'dspsds_filename3' => 'Other Supporting Document',
|
||||
];
|
||||
foreach ($fileCells as $column => $label) {
|
||||
$fileName = $ds[$column] ?? '';
|
||||
echo '<td>';
|
||||
if (!empty($fileName)) {
|
||||
$isUrl = preg_match('/^https?:\/\//i', $fileName) === 1;
|
||||
$linkTarget = $isUrl ? $fileName : $uploadsWebPath . rawurlencode($fileName);
|
||||
$titleAttr = $isUrl ? 'External link' : 'Download ' . $label;
|
||||
echo '<a class="btn btn-sm btn-outline-primary rounded-pill" href="' . htmlspecialchars($linkTarget) . '" target="_blank" rel="noopener" title="' . htmlspecialchars($titleAttr) . '"><i class="fas fa-paperclip"></i></a>';
|
||||
} else {
|
||||
echo '<span class="text-muted">—</span>';
|
||||
}
|
||||
echo '</td>';
|
||||
}
|
||||
?>
|
||||
<td><?= date('Y-m-d H:i', strtotime($ds['dspsds_reg_datetime'])) ?></td>
|
||||
<td>
|
||||
<a href="manage_my_datasources.php?action=edit&id=<?= htmlspecialchars($ds['pkdspsds_id']) ?>" class="btn btn-sm btn-info rounded me-1" title="Edit"><i class="fas fa-edit"></i></a>
|
||||
<a href="manage_my_datasources.php?action=delete&id=<?= htmlspecialchars($ds['pkdspsds_id']) ?>" class="btn btn-sm btn-danger rounded" title="Delete" onclick="return confirm('Are you sure you want to delete this data source? This will also delete related permissions and usage logs.');"><i class="fas fa-trash-alt"></i></a>
|
||||
</td>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<?php else: ?>
|
||||
<div class="alert alert-info rounded mb-0">You have not added any data sources yet.</div>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Footer -->
|
||||
<?php
|
||||
// Include Footer file for owner pages
|
||||
include_once("../includes/footer_owner.php");
|
||||
?>
|
||||
<script>
|
||||
document.addEventListener('DOMContentLoaded', function () {
|
||||
var typeSelect = document.getElementById('dsType');
|
||||
var fileInput = document.getElementById('dsDataFile');
|
||||
var formatsLabel = document.getElementById('primaryFileFormats');
|
||||
var primaryRules = <?php echo json_encode($primaryRulesMap, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE); ?>;
|
||||
var defaultRule = <?php echo json_encode([
|
||||
'accept' => $defaultPrimaryAccept,
|
||||
'description' => $defaultPrimaryRules['description'] ?? 'CSV, JSON, PDF, XLS, XLSX',
|
||||
], JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE); ?>;
|
||||
|
||||
function applyPrimaryFileRules() {
|
||||
if (!fileInput) {
|
||||
return;
|
||||
}
|
||||
var selected = typeSelect ? typeSelect.value : null;
|
||||
var rule = (selected && primaryRules[selected]) ? primaryRules[selected] : defaultRule;
|
||||
if (rule && Array.isArray(rule.accept) && rule.accept.length > 0) {
|
||||
fileInput.setAttribute('accept', rule.accept.join(','));
|
||||
} else {
|
||||
fileInput.removeAttribute('accept');
|
||||
}
|
||||
if (formatsLabel && rule && rule.description) {
|
||||
formatsLabel.textContent = rule.description;
|
||||
}
|
||||
}
|
||||
|
||||
applyPrimaryFileRules();
|
||||
if (typeSelect) {
|
||||
typeSelect.addEventListener('change', applyPrimaryFileRules);
|
||||
}
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
252
data_owner/manage_permissions.php
Normal file
252
data_owner/manage_permissions.php
Normal file
@@ -0,0 +1,252 @@
|
||||
<?php
|
||||
// data_owner/manage_permissions.php
|
||||
session_start();
|
||||
require_once '../config.php';
|
||||
require_once '../includes/auth.php';
|
||||
require_once '../classes/DataSource.php'; // For permission methods
|
||||
|
||||
// Ensure only Data Owners can access this page
|
||||
redirect_if_not_role('Data Owner');
|
||||
|
||||
$data_source_manager = new DataSource($pdo);
|
||||
$user_id = $_SESSION['user_id'];
|
||||
$owner_person_id = $_SESSION['person_id'];
|
||||
|
||||
$action = $_GET['action'] ?? 'list';
|
||||
$permission_id = $_GET['id'] ?? null;
|
||||
|
||||
// Handle form submissions for updating permission status
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['action_type']) && $_POST['action_type'] === 'update_permission') {
|
||||
$permission_id_to_update = filter_var($_POST['permission_id'], FILTER_SANITIZE_NUMBER_INT);
|
||||
$new_status = trim($_POST['new_status']);
|
||||
$notes = trim($_POST['notes'] ?? '');
|
||||
|
||||
// Basic validation
|
||||
if (empty($permission_id_to_update) || !in_array($new_status, ['Approved', 'Rejected', 'Revoked'])) {
|
||||
set_message("Invalid request to update permission.", "danger");
|
||||
header("Location: manage_permissions.php");
|
||||
exit();
|
||||
}
|
||||
|
||||
// You might want to add a check here to ensure the data owner is indeed the owner of the data source
|
||||
// related to this permission_id, to prevent tampering.
|
||||
// This would involve fetching the permission request and then checking the data source's fkisp_id_of.
|
||||
|
||||
if ($data_source_manager->updatePermissionStatus($permission_id_to_update, $new_status, $user_id, $notes)) {
|
||||
set_message("Permission request updated successfully!", "success");
|
||||
} else {
|
||||
set_message("Failed to update permission request.", "danger");
|
||||
}
|
||||
header("Location: manage_permissions.php");
|
||||
exit();
|
||||
}
|
||||
|
||||
$pending_requests = $data_source_manager->getPermissionRequestsForOwner($owner_person_id, 'Pending');
|
||||
$all_requests = $data_source_manager->getPermissionRequestsForOwner($owner_person_id); // All statuses
|
||||
?>
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<!-- Header -->
|
||||
<?php
|
||||
// Include header file for admin pages
|
||||
include_once("../includes/header_owner.php");
|
||||
?>
|
||||
<body>
|
||||
<div class="wrapper">
|
||||
<!-- Sidebar -->
|
||||
<?php
|
||||
// Include header file for admin pages
|
||||
include_once("../includes/nav_owner.php");
|
||||
?>
|
||||
|
||||
<!-- Page 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="#"> Permissions</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'])): ?>
|
||||
<div class="alert alert-<?= $_SESSION['message_type'] ?> alert-dismissible fade show rounded" role="alert">
|
||||
<?= $_SESSION['message'] ?>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
|
||||
</div>
|
||||
<?php
|
||||
unset($_SESSION['message']);
|
||||
unset($_SESSION['message_type']);
|
||||
?>
|
||||
<?php endif; ?>
|
||||
|
||||
<h3 class="mb-3">Pending Requests</h3>
|
||||
<div class="card shadow-sm rounded mb-4">
|
||||
<div class="card-body">
|
||||
<?php if (!empty($pending_requests)): ?>
|
||||
<div class="table-responsive">
|
||||
<table class="table table-hover table-striped">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>ID</th>
|
||||
<th>Data Source</th>
|
||||
<th>Requested By</th>
|
||||
<th>Permission Type</th>
|
||||
<th>Requested Date</th>
|
||||
<th>Notes</th>
|
||||
<th>Proof</th>
|
||||
<th>Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<?php foreach ($pending_requests as $req): ?>
|
||||
<tr>
|
||||
<td><?= htmlspecialchars($req['pkdspsdsp_id']) ?></td>
|
||||
<td><?= htmlspecialchars($req['dspsds_title_en']) ?></td>
|
||||
<td><?= htmlspecialchars($req['isp_firstname_en'] . ' ' . $req['isp_lastname_en']) ?></td>
|
||||
<td><span class="badge bg-info"><?= htmlspecialchars($req['dspsdsp_permission']) ?></span></td>
|
||||
<td><?= date('Y-m-d H:i', strtotime($req['dspsdsp_reg_datetime'])) ?></td>
|
||||
<td>
|
||||
<?php
|
||||
$notes = $req['dspsdsp_notes'] ?? '';
|
||||
echo $notes !== ''
|
||||
? nl2br(htmlspecialchars($notes))
|
||||
: '<span class="text-muted">—</span>';
|
||||
?>
|
||||
</td>
|
||||
<td>
|
||||
<?php if (!empty($req['dspsdsp_proof_path'])): ?>
|
||||
<?php
|
||||
$proofPath = $req['dspsdsp_proof_path'];
|
||||
$isExternal = preg_match('/^https?:\\/\\//i', $proofPath) === 1;
|
||||
$cleanPath = ltrim($proofPath, '/');
|
||||
$linkTarget = $isExternal ? $proofPath : '../uploads/' . $cleanPath;
|
||||
?>
|
||||
<a href="<?= 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>
|
||||
<form action="manage_permissions.php" method="POST" class="d-inline">
|
||||
<input type="hidden" name="action_type" value="update_permission">
|
||||
<input type="hidden" name="permission_id" value="<?= htmlspecialchars($req['pkdspsdsp_id']) ?>">
|
||||
<input type="hidden" name="new_status" value="Approved">
|
||||
<button type="submit" class="btn btn-sm btn-success rounded me-1" title="Approve">
|
||||
<i class="fas fa-check"></i>
|
||||
</button>
|
||||
</form>
|
||||
<form action="manage_permissions.php" method="POST" class="d-inline">
|
||||
<input type="hidden" name="action_type" value="update_permission">
|
||||
<input type="hidden" name="permission_id" value="<?= htmlspecialchars($req['pkdspsdsp_id']) ?>">
|
||||
<input type="hidden" name="new_status" value="Rejected">
|
||||
<button type="submit" class="btn btn-sm btn-danger rounded" title="Reject">
|
||||
<i class="fas fa-times"></i>
|
||||
</button>
|
||||
</form>
|
||||
</td>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<?php else: ?>
|
||||
<div class="alert alert-info rounded mb-0">No pending permission requests.</div>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<h3 class="mb-3 mt-5">All Permission Requests</h3>
|
||||
<div class="card shadow-sm rounded">
|
||||
<div class="card-body">
|
||||
<?php if (!empty($all_requests)): ?>
|
||||
<div class="table-responsive">
|
||||
<table class="table table-hover table-striped">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>ID</th>
|
||||
<th>Data Source</th>
|
||||
<th>Requested By</th>
|
||||
<th>Permission Type</th>
|
||||
<th>Status</th>
|
||||
<th>Requested Date</th>
|
||||
<th>Notes</th>
|
||||
<th>Proof</th>
|
||||
<th>Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<?php foreach ($all_requests as $req): ?>
|
||||
<tr>
|
||||
<td><?= htmlspecialchars($req['pkdspsdsp_id']) ?></td>
|
||||
<td><?= htmlspecialchars($req['dspsds_title_en']) ?></td>
|
||||
<td><?= htmlspecialchars($req['isp_firstname_en'] . ' ' . $req['isp_lastname_en']) ?></td>
|
||||
<td><span class="badge bg-info"><?= htmlspecialchars($req['dspsdsp_permission']) ?></span></td>
|
||||
<td>
|
||||
<span class="badge <?= ($req['dspsdsp_status'] == 'Approved' ? 'bg-success' : ($req['dspsdsp_status'] == 'Pending' ? 'bg-warning' : 'bg-danger')) ?>">
|
||||
<?= htmlspecialchars($req['dspsdsp_status']) ?>
|
||||
</span>
|
||||
</td>
|
||||
<td><?= date('Y-m-d H:i', strtotime($req['dspsdsp_reg_datetime'])) ?></td>
|
||||
<td>
|
||||
<?php
|
||||
$notes = $req['dspsdsp_notes'] ?? '';
|
||||
echo $notes !== ''
|
||||
? nl2br(htmlspecialchars($notes))
|
||||
: '<span class="text-muted">—</span>';
|
||||
?>
|
||||
</td>
|
||||
<td>
|
||||
<?php if (!empty($req['dspsdsp_proof_path'])): ?>
|
||||
<?php
|
||||
$proofPath = $req['dspsdsp_proof_path'];
|
||||
$isExternal = preg_match('/^https?:\\/\\//i', $proofPath) === 1;
|
||||
$cleanPath = ltrim($proofPath, '/');
|
||||
$linkTarget = $isExternal ? $proofPath : '../uploads/' . $cleanPath;
|
||||
?>
|
||||
<a href="<?= 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>
|
||||
<?php if ($req['dspsdsp_status'] == 'Approved'): ?>
|
||||
<form action="manage_permissions.php" method="POST" class="d-inline">
|
||||
<input type="hidden" name="action_type" value="update_permission">
|
||||
<input type="hidden" name="permission_id" value="<?= htmlspecialchars($req['pkdspsdsp_id']) ?>">
|
||||
<input type="hidden" name="new_status" value="Revoked">
|
||||
<button type="submit" class="btn btn-sm btn-secondary rounded" title="Revoke" onclick="return confirm('Are you sure you want to revoke this permission?');">
|
||||
<i class="fas fa-ban"></i> Revoke
|
||||
</button>
|
||||
</form>
|
||||
<?php else: ?>
|
||||
<span class="text-muted">No action</span>
|
||||
<?php endif; ?>
|
||||
</td>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<?php else: ?>
|
||||
<div class="alert alert-info rounded mb-0">No permission requests found.</div>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
<!-- Footer -->
|
||||
<?php
|
||||
// Include Footer file for owner pages
|
||||
include_once("../includes/footer_owner.php");
|
||||
?>
|
||||
</body>
|
||||
</html>
|
||||
140
data_owner/my_analytics.php
Normal file
140
data_owner/my_analytics.php
Normal file
@@ -0,0 +1,140 @@
|
||||
<?php
|
||||
// data_owner/my_analytics.php
|
||||
session_start();
|
||||
require_once '../config.php';
|
||||
require_once '../includes/auth.php';
|
||||
require_once '../classes/DataSource.php';
|
||||
|
||||
// Ensure only Data Owners can access this page
|
||||
redirect_if_not_role('Data Owner');
|
||||
|
||||
$data_source_manager = new DataSource($pdo);
|
||||
$user_id = $_SESSION['user_id'];
|
||||
$owner_person_id = $_SESSION['person_id'];
|
||||
$username = $_SESSION['username'];
|
||||
|
||||
// Fetch analytics data (placeholders for now)
|
||||
// You would typically query dsps_tbl_datasource_used and dsps_tbl_anonymous
|
||||
// filtered by data sources owned by $owner_person_id.
|
||||
|
||||
// Example: Total downloads for my data sources
|
||||
$total_downloads = 0;
|
||||
try {
|
||||
$stmt = $pdo->prepare("
|
||||
SELECT COUNT(dsu.pkdspsdspused_id)
|
||||
FROM dsps_tbl_datasource_used dsu
|
||||
JOIN dsps_tbl_datasource ds ON dsu.fkdspsdsused_id = ds.pkdspsds_id
|
||||
WHERE ds.fkisp_id_of = :owner_person_id AND dsu.dspsdspused_action = 'Downloaded'
|
||||
");
|
||||
$stmt->execute(['owner_person_id' => $owner_person_id]);
|
||||
$total_downloads = $stmt->fetchColumn();
|
||||
} catch (PDOException $e) {
|
||||
error_log("Error fetching total downloads: " . $e->getMessage());
|
||||
}
|
||||
|
||||
// Example: Most viewed data sources (from anonymous views or usage logs)
|
||||
$most_viewed_datasources = [];
|
||||
try {
|
||||
$stmt = $pdo->prepare("
|
||||
SELECT ds.dspsds_title_en, COUNT(da.pkdspsano_id) AS view_count
|
||||
FROM dsps_tbl_anonymous da
|
||||
JOIN dsps_tbl_datasource ds ON da.fkdspsds_id = ds.pkdspsds_id
|
||||
WHERE ds.fkisp_id_of = :owner_person_id
|
||||
GROUP BY ds.pkdspsds_id, ds.dspsds_title_en
|
||||
ORDER BY view_count DESC
|
||||
LIMIT 5
|
||||
");
|
||||
$stmt->execute(['owner_person_id' => $owner_person_id]);
|
||||
$most_viewed_datasources = $stmt->fetchAll();
|
||||
} catch (PDOException $e) {
|
||||
error_log("Error fetching most viewed data sources: " . $e->getMessage());
|
||||
}
|
||||
|
||||
?>
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<!-- Header -->
|
||||
<?php
|
||||
// Include header file for admin pages
|
||||
include_once("../includes/header_owner.php");
|
||||
?>
|
||||
<body>
|
||||
<div class="wrapper">
|
||||
<!-- Sidebar -->
|
||||
<?php
|
||||
// Include header file for admin pages
|
||||
include_once("../includes/nav_owner.php");
|
||||
?>
|
||||
|
||||
<!-- Page 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="#"> My Data Analytics</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'])): ?>
|
||||
<div class="alert alert-<?= $_SESSION['message_type'] ?> alert-dismissible fade show rounded" role="alert">
|
||||
<?= $_SESSION['message'] ?>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
|
||||
</div>
|
||||
<?php
|
||||
unset($_SESSION['message']);
|
||||
unset($_SESSION['message_type']);
|
||||
?>
|
||||
<?php endif; ?>
|
||||
|
||||
<div class="row g-4 mb-5">
|
||||
<div class="col-md-6">
|
||||
<div class="card shadow-sm rounded">
|
||||
<div class="card-body">
|
||||
<h5 class="card-title text-primary"><i class="fas fa-download me-2"></i> Total Downloads of My Data</h5>
|
||||
<p class="card-text fs-2"><?= $total_downloads ?></p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<div class="card shadow-sm rounded">
|
||||
<div class="card-body">
|
||||
<h5 class="card-title text-info"><i class="fas fa-eye me-2"></i> Total Views of My Data Introductions</h5>
|
||||
<p class="card-text fs-2">1500</p> <!-- Placeholder, implement actual count from dsps_tbl_anonymous -->
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card shadow-sm rounded mb-5">
|
||||
<div class="card-header bg-light rounded-top">
|
||||
<h5 class="mb-0">Most Viewed Data Sources (Top 5)</h5>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<?php if (!empty($most_viewed_datasources)): ?>
|
||||
<ul class="list-group list-group-flush">
|
||||
<?php foreach ($most_viewed_datasources as $ds): ?>
|
||||
<li class="list-group-item d-flex justify-content-between align-items-center">
|
||||
<?= htmlspecialchars($ds['dspsds_title_en']) ?>
|
||||
<span class="badge bg-primary rounded"><?= htmlspecialchars($ds['view_count']) ?> Views</span>
|
||||
</li>
|
||||
<?php endforeach; ?>
|
||||
</ul>
|
||||
<?php else: ?>
|
||||
<div class="alert alert-info rounded mb-0">No view data available for your sources yet.</div>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
<!-- Footer -->
|
||||
<?php
|
||||
// Include Footer file for owner pages
|
||||
include_once("../includes/footer_owner.php");
|
||||
?>
|
||||
</body>
|
||||
</html>
|
||||
166
data_owner/run_r_scripts.php
Normal file
166
data_owner/run_r_scripts.php
Normal file
@@ -0,0 +1,166 @@
|
||||
<?php
|
||||
// data_owner/run_r_scripts.php
|
||||
session_start();
|
||||
require_once '../config.php';
|
||||
require_once '../includes/auth.php';
|
||||
require_once '../classes/DataSource.php';
|
||||
require_once '../includes/jupyter_helpers.php';
|
||||
// Ensure only Data Owners can access this page
|
||||
redirect_if_not_role('Data Owner');
|
||||
|
||||
// Build the Jupyter URL for the iframe. Default to localhost access with the dev token.
|
||||
$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
|
||||
);
|
||||
$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();
|
||||
}
|
||||
}
|
||||
?>
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<!-- Header -->
|
||||
<?php
|
||||
// Include header file for admin pages
|
||||
include_once("../includes/header_owner.php");
|
||||
?>
|
||||
<body>
|
||||
<div class="wrapper">
|
||||
<!-- Sidebar -->
|
||||
<?php
|
||||
// Include header file for admin pages
|
||||
include_once("../includes/nav_owner.php");
|
||||
?>
|
||||
|
||||
<!-- Page 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="#">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">Work with R inside JupyterLab</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 you manage or have access to are synced to
|
||||
<code><?= htmlspecialchars($workspaceRelativeDir ?: 'datasources') ?></code> inside Jupyter.
|
||||
Only datasets with Approved permissions appear here.
|
||||
</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">
|
||||
You do not currently have any Approved data sources. Approve requests in the permissions panel to make them available here.
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
<?php if (!empty($workspaceSync['missing'])): ?>
|
||||
<div class="alert alert-warning rounded mb-3">
|
||||
<strong>Some files were skipped:</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">
|
||||
Launch the embedded Jupyter environment below to build, run, and share your R notebooks. Use the
|
||||
toolbar menu inside Jupyter to create new R notebooks and access saved work under <em>Files</em>.
|
||||
</p>
|
||||
<div class="ratio ratio-16x9 border rounded overflow-hidden">
|
||||
<iframe
|
||||
src="<?= htmlspecialchars($jupyterIframeUrl, ENT_QUOTES, 'UTF-8') ?>"
|
||||
title="R in JupyterHub"
|
||||
allowfullscreen
|
||||
loading="lazy"
|
||||
referrerpolicy="no-referrer"
|
||||
></iframe>
|
||||
</div>
|
||||
<p class="mt-3 mb-0">
|
||||
Need more room? <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>
|
||||
Contact a DAC Staff administrator to enable R/Jupyter access so you can analyse datasets directly from this workspace.
|
||||
</div>
|
||||
</div>
|
||||
<p class="mb-0">
|
||||
After your access is approved, refresh this page to launch the embedded notebook environment.
|
||||
</p>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
<!-- Footer -->
|
||||
<?php
|
||||
// Include Footer file for owner pages
|
||||
include_once("../includes/footer_owner.php");
|
||||
?>
|
||||
</body>
|
||||
</html>
|
||||
Reference in New Issue
Block a user