Compare commits

..

5 Commits

3 changed files with 252 additions and 83 deletions

View File

@@ -0,0 +1,247 @@
<?php
// browse_datasources.php
// This file displays a list of data sources based on optional search and category filters.
// It is intended to be included by index.php or accessed directly via a link.
// --- DEBUGGING START ---
ini_set('display_errors', 1);
ini_set('display_startup_errors', 1);
error_reporting(E_ALL);
// --- DEBUGGING END ---
// Ensure all necessary dependencies are included
require_once 'config.php';
require_once 'classes/DataSource.php';
require_once 'classes/Classifications.php';
require_once 'classes/PermissionManager.php'; // Ensure this class is available
// Instantiate the DataSource and Classifications classes
$data_source_manager = new DataSource($pdo);
$classification_manager = new Classifications($pdo);
$permissionManager = new PermissionManager($pdo); // Assuming this is available
// Get filter parameters from the URL
$category_id = $_GET['category_id'] ?? null;
$search_query = $_GET['search'] ?? null;
// Sanitize the input to prevent security vulnerabilities
$category_id = filter_var($category_id, FILTER_SANITIZE_NUMBER_INT);
$search_query = filter_var($search_query, FILTER_SANITIZE_FULL_SPECIAL_CHARS);
// Fetch data sources from the database based on the filters
$data_sources = [];
try {
$data_sources = $data_source_manager->getFilteredDataSources($category_id, $search_query);
} catch (Exception $e) {
// Log the error for debugging.
error_log("Error fetching data sources: " . $e->getMessage());
}
// Fetch all categories for the filter dropdown
$all_categories = [];
try {
$all_categories = $classification_manager->getAllCategories();
} catch (Exception $e) {
error_log("Error fetching categories: " . $e->getMessage());
}
// These variables should be defined in your main index.php
// For this file to work correctly, you need to ensure $is_logged_in, $person_id,
// and the $permissionManager object are created and available.
$is_logged_in = false; // Placeholder, replace with actual logic
$person_id = null; // Placeholder, replace with actual logic
$uploadsWebPath = 'uploads/datasources/';
?>
<div class="container main-content">
<h2 class="text-center mb-4">Browse Data Sources</h2>
<!-- Filter and Search Form -->
<div class="card shadow-sm rounded-3 mb-4 p-3">
<div class="card-body">
<form action="index.php" method="GET" class="row g-3 align-items-end">
<!-- IMPORTANT: This hidden input tells index.php to load this page's content -->
<input type="hidden" name="page" value="browse_datasources">
<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): ?>
<?php
$category_id_val = $category['pkdspscate_id'] ?? '';
$category_title = $category['dspscate_title_en'] ?? 'Unknown Category';
?>
<option value="<?= htmlspecialchars($category_id_val) ?>"
<?= ($category_id == $category_id_val) ? 'selected' : ''; ?>>
<?= htmlspecialchars($category_title) ?>
</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="<?= 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-search me-2"></i> Search Data
</button>
</div>
</form>
</div>
</div>
<?php if (!empty($search_query) || !empty($category_id)): ?>
<div class="alert alert-info rounded shadow-sm text-center">
<?php
$message = "Showing results for: ";
$filters = [];
if (!empty($search_query)) {
$filters[] = "Search Term: '<strong>" . htmlspecialchars($search_query) . "</strong>'";
}
if (!empty($category_id)) {
// Get category details for the message
$category_details = null;
foreach ($all_categories as $cat) {
if (($cat['pkdspscate_id'] ?? '') == $category_id) {
$category_details = $cat;
break;
}
}
if ($category_details) {
$category_title = $category_details['dspscate_title_en'] ?? 'Unknown Category';
$filters[] = "Category: '<strong>" . htmlspecialchars($category_title) . "</strong>'";
}
}
echo $message . implode(" and ", $filters);
?>
</div>
<?php endif; ?>
<?php if (!empty($data_sources)): ?>
<div class="row row-cols-1 row-cols-md-2 row-cols-lg-3 g-4">
<?php foreach ($data_sources as $ds): ?>
<div class="col">
<div class="card h-100 shadow-sm rounded-3 overflow-hidden border border-gray-200">
<?php
$title = $ds['dspsds_title_en'] ?? '';
$description = $ds['dspsds_description'] ?? '';
$id = $ds['pkdspsds_id'] ?? '';
?>
<div class="card-body d-flex flex-column">
<h5 class="card-title text-primary fw-bold"><?= htmlspecialchars($title) ?></h5>
<h6 class="card-subtitle mb-2 text-muted">Category: <?= htmlspecialchars($ds['category_name']) ?></h6>
<h6 class="card-subtitle mb-2 text-muted">Type: <?= htmlspecialchars($ds['data_type_name']) ?></h6>
<p class="card-text text-muted flex-grow-1">
<?php
$shortDescription = substr($description, 0, 100);
if (strlen($description) > 100) {
$shortDescription .= '...';
}
echo htmlspecialchars($shortDescription);
?>
</p>
<div class="mt-auto">
<ul class="list-unstyled small text-muted">
<li><i class="fas fa-user me-1"></i> Data Owner: <?= 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="<?= htmlspecialchars($ds['pkdspsds_id']); ?>"
data-ds-title="<?= 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=<?= 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="<?= htmlspecialchars($ds['pkdspsds_id']); ?>"
data-ds-title="<?= 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; ?>
</div>
<?php else: ?>
<div class="alert alert-warning text-center rounded-3">
<h4 class="alert-heading">No Data Sources Found</h4>
<p>We couldn't find any data sources matching your criteria. Try adjusting your search term or selecting a different category.</p>
</div>
<?php endif; ?>
</div>

View File

@@ -14,11 +14,11 @@ if (is_dir($logDir) && is_writable($logDir)) {
} }
// Database credentials (allow override via environment variables for Docker support) // Database credentials (allow override via environment variables for Docker support)
define('DB_HOST', getenv('DB_HOST') ?: 'localhost'); define('DB_HOST', getenv('DB_HOST') ?: 'k8s.niph.org.kh');
define('DB_PORT', getenv('DB_PORT') ?: '3306'); define('DB_PORT', getenv('DB_PORT') ?: '32364');
define('DB_USER', getenv('DB_USER') ?: 'root'); define('DB_USER', getenv('DB_USER') ?: 'sponlork');
define('DB_PASS', getenv('DB_PASS') !== false ? getenv('DB_PASS') : ''); define('DB_PASS', getenv('DB_PASS') !== false ? getenv('DB_PASS') : 'Aa@123456');
define('DB_NAME', getenv('DB_NAME') ?: 'niph_dsps'); define('DB_NAME', getenv('DB_NAME') ?: 'uat-dsp');
// Establish PDO connection // Establish PDO connection
try { try {

View File

@@ -1,78 +0,0 @@
version: '3.9'
services:
app:
build: .
container_name: dsp_app
restart: unless-stopped
env_file:
- .env
ports:
- "4010:80"
- "8082:80"
environment:
DB_HOST: db
DB_PORT: 3306
DB_NAME: niph_dsps
DB_USER: dsp_user
DB_PASS: dsp_pass
JUPYTER_EXTERNAL_URL: ${JUPYTER_EXTERNAL_URL:-}
JUPYTER_PORT: ${JUPYTER_PORT:-443}
DSP_APP_ORIGINS: ${DSP_APP_ORIGINS:-}
DSP_FRAME_ANCESTORS: ${DSP_FRAME_ANCESTORS:-}
volumes:
- ./:/var/www/html
depends_on:
- db
db:
image: mysql:8.0
container_name: dsp_db
restart: unless-stopped
environment:
MYSQL_ROOT_PASSWORD: rootpassword
MYSQL_DATABASE: niph_dsps
MYSQL_USER: dsp_user
MYSQL_PASSWORD: dsp_pass
ports:
- "3307:3306"
volumes:
- mysql_data:/var/lib/mysql
- ./db/niph_dsps.sql:/docker-entrypoint-initdb.d/01-init.sql:ro
phpmyadmin:
image: phpmyadmin:latest
container_name: dsp_phpmyadmin
restart: unless-stopped
ports:
- "8081:80"
environment:
PMA_HOST: db
PMA_USER: dsp_user
PMA_PASSWORD: dsp_pass
UPLOAD_LIMIT: 64M
depends_on:
- db
jupyterhub:
build:
context: ./docker/jupyterhub
container_name: dsp_jupyterhub
restart: unless-stopped
env_file:
- .env
ports:
- "${JUPYTERHUB_PORT:-443}:8000"
- "8888:8000"
environment:
DSP_JH_NETWORK: dsp_default
DSP_APP_CONTAINER: dsp_app
DSP_WORKSPACE_ROOT: ${DSP_WORKSPACE_ROOT:-/var/www/html/uploads/jupyter_workspace}
volumes:
- ./uploads/jupyter_workspace:/var/www/html/uploads/jupyter_workspace
- /var/run/docker.sock:/var/run/docker.sock
depends_on:
- app
volumes:
mysql_data: