DSP Project first push, date: 29/01/2026

This commit is contained in:
Sok Ponlork
2026-01-29 14:31:48 +07:00
parent 951262afb3
commit 644b624d2d
1857 changed files with 163516 additions and 0 deletions

View File

@@ -0,0 +1,341 @@
<?php
// data_user/browse_datasources.php
// This page allows users (including guests) to browse available data sources.
// Enable detailed error reporting for debugging purposes (log only)
error_reporting(E_ALL);
ini_set('display_errors', 0);
// Check if a session is not already active before starting one.
// This prevents the "session already active" notice.
if (session_status() === PHP_SESSION_NONE) {
session_start();
}
// Use __DIR__ to get the absolute path to this file's directory,
// ensuring the path to the required files is always correct.
require_once(__DIR__ . '/../config.php');
require_once(__DIR__ . '/../includes/auth.php');
require_once(__DIR__ . '/../classes/DataSource.php');
require_once(__DIR__ . '/../classes/Classifications.php');
require_once(__DIR__ . '/../classes/User.php');
require_once(__DIR__ . '/../classes/Permission.php'); // Correctly include the Permission class
// Check if user is logged in
$is_logged_in = isset($_SESSION['user_id']);
$user_id = $_SESSION['user_id'] ?? null;
$person_id = $_SESSION['person_id'] ?? null;
$username = $_SESSION['username'] ?? null;
$usernameLabel = $username ? htmlspecialchars($username, ENT_QUOTES, 'UTF-8') : 'Guest';
$currentPage = basename($_SERVER['PHP_SELF']);
// Instantiate classes
$dataSourceManager = new DataSource($pdo);
$classificationManager = new Classifications($pdo);
$userManager = new User($pdo);
$permissionManager = new Permission($pdo); // Instantiate the Permission class
// Get user details if logged in
$currentUserDetails = $is_logged_in ? $userManager->getUserDetails($user_id) : null;
$uploadsWebPath = '../uploads/datasources/';
// Get filter parameters from GET request
$filter_category_id = $_GET['category_id'] ?? null;
if ($filter_category_id !== null) {
$filter_category_id = filter_var($filter_category_id, FILTER_VALIDATE_INT);
if ($filter_category_id === false) {
$filter_category_id = null;
}
}
$search_query = htmlspecialchars($_GET['search'] ?? '', ENT_QUOTES, 'UTF-8');
// Fetch data sources based on filters
$data_sources = [];
try {
$data_sources = $dataSourceManager->getDataSources(
null, // No owner filter for public browsing
'Active',
$filter_category_id,
$search_query
);
} catch (Exception $e) {
set_message('Error retrieving data sources: ' . $e->getMessage(), 'danger');
}
// Fetch all categories for the filter dropdown
$all_categories = [];
try {
$all_categories = $classificationManager->getAllCategories();
} catch (Exception $e) {
error_log("Error fetching categories for browse_datasources: " . $e->getMessage());
}
?>
<!DOCTYPE html>
<html lang="en">
<!-- Header -->
<?php
// Include header file for admin pages
include_once("../includes/header_contributor.php");
?>
<body>
<div class="wrapper">
<!-- Sidebar -->
<?php
// Include header file for admin pages
include_once("../includes/nav_contributor.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="#"> Browse All Data</a>
<div class="d-flex">
<span class="navbar-text me-3">
Welcome, <?php echo $usernameLabel; ?>!
</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; ?>
<!-- Filter and Search Form -->
<div class="card shadow-sm rounded mb-4 p-3">
<div class="card-body">
<form action="browse_datasources.php" method="GET" class="row g-3 align-items-end">
<div class="col-md-4">
<label for="categoryFilter" class="form-label">Filter by Category:</label>
<select class="form-select rounded" id="categoryFilter" name="category_id">
<option value="">All Categories</option>
<?php foreach ($all_categories as $category): ?>
<option value="<?php echo htmlspecialchars($category['pkdspscate_id']); ?>"
<?php echo ($filter_category_id == $category['pkdspscate_id']) ? 'selected' : ''; ?>>
<?php echo htmlspecialchars($category['dspscate_title_en']); ?>
</option>
<?php endforeach; ?>
</select>
</div>
<div class="col-md-5">
<label for="searchQuery" class="form-label">Search by Title/Description:</label>
<input type="text" class="form-control rounded" id="searchQuery" name="search"
value="<?php echo htmlspecialchars($search_query); ?>" placeholder="Enter keywords...">
</div>
<div class="col-md-3 d-grid">
<button type="submit" class="btn btn-primary rounded">
<i class="fas fa-filter me-2"></i> Apply Filters
</button>
</div>
</form>
</div>
</div>
<!-- Data Sources List -->
<div class="row row-cols-1 row-cols-md-2 row-cols-lg-3 g-4 mt-4">
<?php if (!empty($data_sources)): ?>
<?php foreach ($data_sources as $ds): ?>
<div class="col">
<div class="card h-100 shadow-sm rounded">
<div class="card-body d-flex flex-column">
<h5 class="card-title text-primary"><?php echo htmlspecialchars($ds['dspsds_title_en']); ?></h5>
<h6 class="card-subtitle mb-2 text-muted">Category: <?php echo htmlspecialchars($ds['category_name']); ?></h6>
<h6 class="card-subtitle mb-2 text-muted">Type: <?php echo htmlspecialchars($ds['data_type_name']); ?></h6>
<p class="card-text flex-grow-1"><?php echo htmlspecialchars(substr($ds['dspsds_description'], 0, 150)) . (strlen($ds['dspsds_description']) > 150 ? '...' : ''); ?></p>
<div class="mt-auto">
<ul class="list-unstyled small text-muted">
<li><i class="fas fa-user me-1"></i> Data Owner: <?php echo htmlspecialchars($ds['isp_firstname_en'] . ' ' . $ds['isp_lastname_en']); ?></li>
<li><i class="fas fa-calendar-alt me-1"></i> Published:
<?php if (!empty($ds['dspsds_public_date'])): ?>
<?= htmlspecialchars(date('M d, Y', strtotime($ds['dspsds_public_date']))); ?>
<?php else: ?>
Not specified
<?php endif; ?>
</li>
</ul>
<?php
$supportingFiles = [
'dspsds_filename1' => ['label' => 'Questionnaire / Data Dictionary', 'icon' => 'fa-clipboard-list'],
'dspsds_filename2' => ['label' => 'Protocol / User Guide', 'icon' => 'fa-book'],
'dspsds_filename3' => ['label' => 'Other Supporting Document', 'icon' => 'fa-file-alt'],
];
?>
<div class="bg-light-subtle border rounded p-3 mb-3">
<span class="d-block text-uppercase text-muted fw-semibold small mb-2">Supporting Documents</span>
<ul class="list-unstyled mb-0 small">
<?php foreach ($supportingFiles as $column => $meta): ?>
<?php
$fileName = $ds[$column] ?? '';
$label = $meta['label'];
$icon = $meta['icon'];
?>
<li class="mb-2">
<i class="fas <?= htmlspecialchars($icon, ENT_QUOTES, 'UTF-8') ?> me-1"></i>
<?php if (!empty($fileName)): ?>
<?php
$isUrl = preg_match('/^https?:\/\//i', $fileName) === 1;
$linkTarget = $isUrl ? $fileName : $uploadsWebPath . rawurlencode($fileName);
?>
<a href="<?= htmlspecialchars($linkTarget, ENT_QUOTES, 'UTF-8') ?>" target="_blank" rel="noopener">
<?= htmlspecialchars($label) ?>
</a>
<?php else: ?>
<span class="text-muted"><?= htmlspecialchars($label) ?> (Not provided)</span>
<?php endif; ?>
</li>
<?php endforeach; ?>
</ul>
</div>
<?php if ($is_logged_in): ?>
<?php
$has_read_permission = false;
$has_download_permission = false;
try {
$has_read_permission = $permissionManager->hasPermission($person_id, $ds['pkdspsds_id'], 'Read');
$has_download_permission = $permissionManager->hasPermission($person_id, $ds['pkdspsds_id'], 'Download');
} catch (Exception $e) {
error_log("Permission check error for user " . $person_id . " on DS " . $ds['pkdspsds_id'] . ": " . $e->getMessage());
}
?>
<?php if ($has_read_permission): ?>
<a href="#" class="btn btn-sm btn-outline-success rounded me-2 disabled">
<i class="fas fa-check-circle me-1"></i> Read Access Granted
</a>
<?php else: ?>
<button type="button" class="btn btn-sm btn-success rounded me-2" data-bs-toggle="modal" data-bs-target="#requestPermissionModal"
data-ds-id="<?php echo htmlspecialchars($ds['pkdspsds_id']); ?>"
data-ds-title="<?php echo htmlspecialchars($ds['dspsds_title_en']); ?>"
data-permission-type="Read">
<i class="fas fa-file-alt me-1"></i> Request Read Access
</button>
<?php endif; ?>
<?php if ($has_download_permission): ?>
<a href="download.php?dspsds_id=<?php echo htmlspecialchars($ds['pkdspsds_id']); ?>" class="btn btn-sm btn-outline-primary rounded">
<i class="fas fa-download me-1"></i> Download File
</a>
<?php else: ?>
<button type="button" class="btn btn-sm btn-primary rounded" data-bs-toggle="modal" data-bs-target="#requestPermissionModal"
data-ds-id="<?php echo htmlspecialchars($ds['pkdspsds_id']); ?>"
data-ds-title="<?php echo htmlspecialchars($ds['dspsds_title_en']); ?>"
data-permission-type="Download">
<i class="fas fa-cloud-download-alt me-1"></i> Request Download
</button>
<?php endif; ?>
<?php else: ?>
<button type="button" class="btn btn-sm btn-secondary rounded" data-bs-toggle="modal" data-bs-target="#loginModal">
<i class="fas fa-sign-in-alt me-1"></i> Login to Request Access
</button>
<?php endif; ?>
</div>
</div>
</div>
</div>
<?php endforeach; ?>
<?php else: ?>
<div class="col-12">
<div class="alert alert-info rounded text-center">
No active data sources found matching your criteria.
</div>
</div>
<?php endif; ?>
</div>
</div>
</div>
<!-- Request Permission Modal -->
<div class="modal fade" id="requestPermissionModal" tabindex="-1" aria-labelledby="requestPermissionModalLabel" aria-hidden="true">
<div class="modal-dialog modal-dialog-centered">
<div class="modal-content rounded shadow-lg">
<div class="modal-header bg-success text-white rounded-top">
<h5 class="modal-title" id="requestPermissionModalLabel">Request Data Access Permission</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="../data_user/process_request_permission.php" method="POST" enctype="multipart/form-data">
<input type="hidden" name="data_source_id" id="modalDataSourceId">
<input type="hidden" name="permission_type" id="modalPermissionType">
<div class="mb-3">
<label for="modalDataSourceTitle" class="form-label">Data Source</label>
<input type="text" class="form-control rounded" id="modalDataSourceTitle" readonly>
</div>
<div class="mb-3">
<label for="modalRequestedPermission" class="form-label">Requested Permission</label>
<input type="text" class="form-control rounded" id="modalRequestedPermission" readonly>
</div>
<div class="mb-3">
<label for="requestNotes" class="form-label">Reason for Request (Required)</label>
<textarea class="form-control rounded-3" id="requestNotes" name="notes" rows="4" placeholder="Please explain why you need this data and how you plan to use it." required></textarea>
</div>
<div class="mb-3">
<label for="proofFile" class="form-label">Upload Proof (PDF only)</label>
<input type="file" class="form-control rounded" id="proofFile" name="proof_file" accept="application/pdf" required>
<div class="form-text">Attach an official letter, approval memo, or supporting document in PDF format (max 10 MB).</div>
</div>
<div class="d-grid">
<button type="submit" class="btn btn-success rounded">Submit Request</button>
</div>
</form>
</div>
</div>
</div>
</div>
<!-- Login Modal for guests -->
<div class="modal fade" id="loginModal" tabindex="-1" aria-labelledby="loginModalLabel" aria-hidden="true">
<div class="modal-dialog modal-dialog-centered">
<div class="modal-content rounded shadow-lg">
<div class="modal-header bg-primary text-white rounded-top">
<h5 class="modal-title" id="loginModalLabel">Login Required</h5>
<button type="button" class="btn-close btn-close-white" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body text-center p-4">
<p>You must be logged in to request access to data sources.</p>
<a href="../index.php?page=login" class="btn btn-primary rounded me-2">Login</a>
<a href="../index.php?page=register" class="btn btn-outline-primary rounded">Register</a>
</div>
</div>
</div>
</div>
<!-- Footer -->
<?php
// Include Footer file for owner pages
include_once("../includes/footer_contributor.php");
?>
<script>
// JavaScript to populate the permission request modal
var requestPermissionModal = document.getElementById('requestPermissionModal');
if (requestPermissionModal) {
requestPermissionModal.addEventListener('show.bs.modal', function (event) {
var button = event.relatedTarget; // Button that triggered the modal
var dsId = button.getAttribute('data-ds-id');
var dsTitle = button.getAttribute('data-ds-title');
var permissionType = button.getAttribute('data-permission-type');
var modalDataSourceId = requestPermissionModal.querySelector('#modalDataSourceId');
var modalDataSourceTitle = requestPermissionModal.querySelector('#modalDataSourceTitle');
var modalPermissionType = requestPermissionModal.querySelector('#modalPermissionType');
var modalRequestedPermission = requestPermissionModal.querySelector('#modalRequestedPermission');
modalDataSourceId.value = dsId;
modalDataSourceTitle.value = dsTitle;
modalPermissionType.value = permissionType;
modalRequestedPermission.value = permissionType; // Display the type in the modal
requestPermissionModal.querySelector('#requestNotes').value = ''; // Clear notes on new open
});
}
</script>
</body>
</html>

213
data_hybrid/dashboard.php Normal file
View File

@@ -0,0 +1,213 @@
<?php
// data_hybrid/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 Contributor can access this dashboard
redirect_if_not_role('Data Contributor');
$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'));
$usageByDataSource = $data_source_manager->getUsageByDataSourceForUser($person_id, 8);
$data_accesses_last_30_days = 0;
if (!empty($usageByDataSource)) {
foreach ($usageByDataSource 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_contributor.php");
?>
<body>
<div class="wrapper">
<!-- Sidebar -->
<?php
// Include header file for admin pages
include_once("../includes/nav_contributor.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($usageByDataSource)) {
foreach ($usageByDataSource as $row) {
$chartLabels[] = $row['dspsds_title_en'] ?? ('Data Source #' . $row['pkdspsds_id']);
$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 of Data Sources</h5>
</div>
<div class="card-body">
<?php if (!empty($chartValues)): ?>
<canvas id="contributorUsagePieChart" height="300"></canvas>
<?php else: ?>
<div class="alert alert-info rounded mb-0">No usage recorded yet for your account.</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_contributor.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('contributorUsagePieChart');
if (!ctx) {
return;
}
const backgroundColors = [
'#0d6efd', '#198754', '#dc3545', '#fd7e14', '#6f42c1', '#20c997', '#0dcaf0', '#ffc107'
];
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>

111
data_hybrid/download.php Normal file
View File

@@ -0,0 +1,111 @@
<?php
// This script handles the file download and logs the action to the database.
// Start the session to access user info
if (session_status() === PHP_SESSION_NONE) {
session_start();
}
require_once '../config.php';
require_once '../includes/auth.php'; // Make sure your auth.php includes a redirect_if_not_logged_in or similar function
// --- 1. Get User and Datasource IDs ---
// Ensure the user is logged in
redirect_if_not_logged_in('../login.php');
// Get the user's person_id from the session
$person_id = $_SESSION['person_id'];
// Get the datasource_id from the URL parameter
$datasource_id = $_GET['dspsds_id'] ?? null;
// Validate the datasource_id
if (!$datasource_id || !filter_var($datasource_id, FILTER_VALIDATE_INT)) {
die("Invalid or missing datasource ID.");
}
// --- 2. Log the Download Action ---
// This code inserts a new record for every download.
try {
$sql_insert = "
INSERT INTO dsps_tbl_datasource_used
(fkdspsdsused_id, fkisp_id_of, dspsdspused_action)
VALUES
(?, ?, ?)
";
$stmt_insert = $pdo->prepare($sql_insert);
$action = "Downloaded";
$stmt_insert->execute([$datasource_id, $person_id, $action]);
} catch (PDOException $e) {
// We now log the error and set a user-facing message
error_log("Error logging download: " . $e->getMessage());
// Redirect with an error message, but still try to serve the file
set_message("An error occurred while logging the download.", "danger");
// We do not die here, as we still want to try and serve the file
}
// --- 3. Retrieve File Path and Name ---
$file_path = null;
$file_name = null;
try {
$sql_select = "
SELECT dspsds_filename, dspsds_title_en
FROM dsps_tbl_datasource
WHERE pkdspsds_id = ?
";
$stmt = $pdo->prepare($sql_select);
$stmt->execute([$datasource_id]);
$row = $stmt->fetch();
if ($row) {
$file_name = $row['dspsds_filename'];
$download_label = $row['dspsds_title_en'] ?: 'datasource_' . $datasource_id;
}
} catch (PDOException $e) {
error_log("Error retrieving file info: " . $e->getMessage());
die("An error occurred while retrieving file information.");
}
if (empty($file_name)) {
die("File not found in the database.");
}
// Handle external URLs
if (preg_match('/^https?:\\/\\//i', $file_name)) {
header('Location: ' . $file_name);
exit;
}
$uploadsDir = realpath(__DIR__ . '/../uploads/datasources');
if (!$uploadsDir) {
error_log('Uploads directory not found for download.');
die('File storage directory is unavailable.');
}
$file_path = $uploadsDir . '/' . $file_name;
// --- 4. Serve the File to the User ---
// Check if the file exists on the server
if (file_exists($file_path)) {
// Set headers to force a download
header('Content-Description: File Transfer');
header('Content-Type: application/octet-stream');
header('Content-Disposition: attachment; filename="' . basename($download_label . '_' . $file_name) . '"');
header('Expires: 0');
header('Cache-Control: must-revalidate');
header('Pragma: public');
header('Content-Length: ' . filesize($file_path));
// Clear output buffer
if (ob_get_level()) {
ob_clean();
}
flush();
// Read the file and send it to the output buffer
readfile($file_path);
exit;
} else {
die("The file could not be found on the server at the specified path.");
}
?>

View File

@@ -0,0 +1,537 @@
<?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 Contributor');
$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); // Delete the file
}
}
}
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_contributor.php");
?>
<body>
<div class="wrapper">
<!-- Sidebar -->
<?php
// Include header file for admin pages
include_once("../includes/nav_contributor.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_contributor.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>

View 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 Contributor');
$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_contributor.php");
?>
<body>
<div class="wrapper">
<!-- Sidebar -->
<?php
// Include header file for admin pages
include_once("../includes/nav_contributor.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_contributor.php");
?>
</body>
</html>

View File

@@ -0,0 +1,134 @@
<?php
// data_user/my_downloads.php
session_start();
require_once '../config.php';
require_once '../includes/auth.php';
require_once '../classes/DataSource.php';
// Ensure only Data Users can access this page
redirect_if_not_role('Data Contributor');
$user_id = $_SESSION['user_id'];
$person_id = $_SESSION['person_id']; // This is the correct ID to use for the join
$username = $_SESSION['username'];
$data_source_manager = new DataSource($pdo);
// Fetch download history for the current user
$download_history = [];
try {
$stmt = $pdo->prepare("
SELECT dsu.*,
ds.dspsds_title_en,
ds.dspsds_filename,
tds.dspstds_name_en
FROM dsps_tbl_datasource_used dsu
JOIN dsps_tbl_datasource ds
ON dsu.fkdspsdsused_id = ds.pkdspsds_id
JOIN dsps_tbl_typedatasource tds
ON ds.fkdspstds_id = tds.pkdspstds_id
WHERE dsu.fkisp_id_of = :person_id
AND dsu.dspsdspused_action = 'Downloaded'
ORDER BY dsu.dspsdspused_datetime DESC
");
$stmt->execute(['person_id' => $person_id]);
$download_history = $stmt->fetchAll();
} catch (PDOException $e) {
error_log("Error fetching download history: " . $e->getMessage());
set_message("Error fetching download history.", "danger");
}
?>
<!DOCTYPE html>
<html lang="en">
<!-- Header -->
<?php
// Include header file for admin pages
include_once("../includes/header_contributor.php");
?>
<body>
<div class="wrapper">
<!-- Sidebar -->
<?php
// Include header file for admin pages
include_once("../includes/nav_contributor.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 Downloads History</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="card shadow-sm rounded">
<div class="card-header bg-light rounded-top">
<h5 class="mb-0">Downloaded Data Sources</h5>
</div>
<div class="card-body">
<?php if (!empty($download_history)): ?>
<div class="table-responsive">
<table class="table table-hover table-striped">
<thead>
<tr>
<th>Data Source</th>
<th>Type</th>
<th>Download Date</th>
<th>File</th>
</tr>
</thead>
<tbody>
<?php foreach ($download_history as $item): ?>
<tr>
<td><?= htmlspecialchars($item['dspsds_title_en']) ?></td>
<td><?= htmlspecialchars($item['dspstds_name_en']) ?></td>
<td><?= date('Y-m-d H:i', strtotime($item['dspsdspused_datetime'])) ?></td>
<td>
<?php if (!empty($item['dspsds_filename'])): ?>
<!--
This is the CORRECTED line. It now points to download.php
and passes the ID of the data source.
-->
<a href="download.php?dspsds_id=<?= htmlspecialchars($item['fkdspsdsused_id']) ?>" class="btn btn-sm btn-outline-primary text-dark badge">
<i class="fas fa-download me-1"></i> Download Again
</a>
<?php else: ?>
N/A (API or no direct file)
<?php endif; ?>
</td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
</div>
<?php else: ?>
<div class="alert alert-info rounded mb-0">You have not downloaded any data sources yet.</div>
<?php endif; ?>
</div>
</div>
</div>
</div>
<!-- Footer -->
<?php
// Include Footer file for owner pages
include_once("../includes/footer_contributor.php");
?>
</body>
</html>

View File

@@ -0,0 +1,140 @@
<?php
// data_user/my_permissions.php
// This page shows the user a list of all their data access requests and their statuses.
error_reporting(E_ALL);
ini_set('display_errors', 1);
session_start();
require_once '../config.php';
// We'll assume these files exist based on your original code
require_once '../includes/auth.php';
require_once '../classes/Permission.php';
require_once '../classes/User.php';
// Redirect if not logged in
if (!isset($_SESSION['user_id'])) {
header('Location: ../index.php?page=login');
exit;
}
$user_id = $_SESSION['user_id'];
$person_id = $_SESSION['person_id'];
$username = $_SESSION['username'];
$currentPage = basename($_SERVER['PHP_SELF']);
// Instantiate classes
$permissionManager = new Permission($pdo);
$userManager = new User($pdo);
// Get user details
$currentUserDetails = $userManager->getUserDetails($user_id);
// Fetch all permission requests for the logged-in user
$permissionRequests = [];
try {
$permissionRequests = $permissionManager->getPermissionsByPersonId($person_id);
} catch (Exception $e) {
set_message('Error retrieving permission requests: ' . $e->getMessage(), 'danger');
}
?>
<!DOCTYPE html>
<html lang="en">
<!-- Header -->
<?php
// Include header file for admin pages
include_once("../includes/header_contributor.php");
?>
<body>
<div class="wrapper">
<!-- Sidebar -->
<?php
// Include header file for admin pages
include_once("../includes/nav_contributor.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 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; ?>
<div class="card shadow-sm rounded-lg">
<div class="card-body p-4">
<?php if (!empty($permissionRequests)): ?>
<div class="table-responsive">
<table class="table table-hover table-borderless align-middle">
<thead class="table-light">
<tr>
<th scope="col">Data Source</th>
<th scope="col">Requested For</th>
<th scope="col">Date Submitted</th>
<th scope="col">Status</th>
</tr>
</thead>
<tbody>
<?php foreach ($permissionRequests as $request): ?>
<tr>
<td class="fw-bold"><?= htmlspecialchars($request['ds_title']) ?></td>
<td><?= htmlspecialchars($request['dspspr_permission_type']) ?></td>
<td><?= htmlspecialchars(date('M d, Y', strtotime($request['dspspr_request_date']))) ?></td>
<td>
<?php
$status_class = '';
switch ($request['dspspr_status']) {
case 'Approved':
$status_class = 'bg-success badge text-white';
break;
case 'Pending':
$status_class = 'bg-warning badge text-dark';
break;
case 'Denied':
$status_class = 'bg-danger badge text-white';
break;
default:
$status_class = 'bg-secondary badge text-white';
break;
}
?>
<span class="status-badge <?= $status_class ?>"><?= htmlspecialchars($request['dspspr_status']) ?></span>
</td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
</div>
<?php else: ?>
<div class="alert alert-info text-center rounded m-0">
You have not submitted any permission requests yet. <a href="browse_datasources.php" class="alert-link">Browse data sources</a> to get started.
</div>
<?php endif; ?>
</div>
</div>
</div>
</div>
<!-- Footer -->
<?php
// Include Footer file for owner pages
include_once("../includes/footer_user.php");
?>
</body>
</html>

View File

@@ -0,0 +1,162 @@
<?php
// data_hybrid/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';
redirect_if_not_role('Data Contributor');
$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_contributor.php"); ?>
<body>
<div class="wrapper">
<?php include_once("../includes/nav_contributor.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>
<?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'], $_SESSION['message_type']);
?>
<?php endif; ?>
<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">Collaborative R Workspace</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">
Only Approved data sources are copied into
<code><?= htmlspecialchars($workspaceRelativeDir ?: 'datasources') ?></code>
inside Jupyter. Use these files when collaborating with Data Owners.
</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 have any Approved data sources yet. Once your requests are approved, refresh this page.
</div>
<?php endif; ?>
<?php if (!empty($workspaceSync['missing'])): ?>
<div class="alert alert-warning rounded mb-3">
<strong>Some items 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 JupyterLab environment to build notebooks, explore shared datasets, and collaborate with Data Owners.
Use the <em>Files</em> panel to open existing work or create a new R Notebook from the launcher.
</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">
Prefer a dedicated 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>
Contact a DAC Staff administrator to enable R/Jupyter access so you can analyse data directly from this workspace.
</div>
</div>
<p class="mb-0">
Once access is enabled, refresh this page to launch the JupyterLab environment.
</p>
<?php endif; ?>
</div>
</div>
</div>
</div>
<?php include_once("../includes/footer_contributor.php"); ?>
</body>
</html>