424 lines
22 KiB
PHP
424 lines
22 KiB
PHP
<?php
|
|
declare(strict_types=1);
|
|
|
|
require_once __DIR__ . '/includes/admin_layout.php';
|
|
|
|
library_bootstrap();
|
|
|
|
$errors = [];
|
|
|
|
// Handle POST request
|
|
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
|
$action = $_POST['action'] ?? '';
|
|
$id = isset($_POST['id']) ? (int)$_POST['id'] : 0;
|
|
|
|
try {
|
|
if ($action === 'create_document') {
|
|
library_create_document($_POST, $_FILES['document_file'] ?? [], $_FILES['cover_file'] ?? []);
|
|
library_set_flash('success', 'Document created successfully.');
|
|
header('Location: /admin_documents.php');
|
|
exit;
|
|
} elseif ($action === 'update_document') {
|
|
if (!$id) {
|
|
throw new RuntimeException('Invalid Document ID.');
|
|
}
|
|
library_update_document($id, $_POST, $_FILES['document_file'] ?? [], $_FILES['cover_file'] ?? []);
|
|
library_set_flash('success', 'Document updated successfully.');
|
|
header('Location: /admin_documents.php');
|
|
exit;
|
|
} elseif ($action === 'delete_document') {
|
|
if (!$id) {
|
|
throw new RuntimeException('Invalid Document ID.');
|
|
}
|
|
library_delete_document($id);
|
|
library_set_flash('success', 'Document deleted successfully.');
|
|
header('Location: /admin_documents.php');
|
|
exit;
|
|
}
|
|
} catch (Throwable $exception) {
|
|
$errors[] = $exception->getMessage();
|
|
}
|
|
}
|
|
|
|
// Search Logic
|
|
$search = isset($_GET['search']) ? trim($_GET['search']) : '';
|
|
// We can implement search in fetch_documents if needed, currently it supports filters
|
|
// For now, fetch all and filter in PHP or improve SQL later if specific search needed.
|
|
// library_fetch_documents doesn't have search param yet, let's just fetch all.
|
|
$documents = library_fetch_documents(false);
|
|
// Basic search filter in PHP for now
|
|
if ($search !== '') {
|
|
$documents = array_filter($documents, function($doc) use ($search) {
|
|
return stripos($doc['title_en'] ?? '', $search) !== false
|
|
|| stripos($doc['title_ar'] ?? '', $search) !== false
|
|
|| stripos($doc['author'] ?? '', $search) !== false;
|
|
});
|
|
}
|
|
|
|
$categories = library_get_categories();
|
|
$allSubcategories = library_get_subcategories(null);
|
|
$types = library_get_types();
|
|
|
|
admin_render_header('Material Entry', 'documents');
|
|
?>
|
|
<!-- Page Content -->
|
|
<?php if ($errors): ?>
|
|
<div class="alert alert-danger"><?= h(implode(' ', $errors)) ?></div>
|
|
<?php endif; ?>
|
|
|
|
<div class="d-flex justify-content-between align-items-center mb-4">
|
|
<p class="text-secondary mb-0">Manage library documents (Material Entry).</p>
|
|
<button class="btn btn-primary" onclick="openCreateModal()">
|
|
<i class="bi bi-plus-lg me-1"></i> Add New Document
|
|
</button>
|
|
</div>
|
|
|
|
<!-- Search Bar -->
|
|
<div class="card shadow-sm border-0 mb-4">
|
|
<div class="card-body">
|
|
<form method="get" action="/admin_documents.php" class="row g-2 align-items-center">
|
|
<div class="col-auto flex-grow-1">
|
|
<input type="text" name="search" class="form-control" placeholder="Search by title or author..." value="<?= h($search) ?>">
|
|
</div>
|
|
<div class="col-auto">
|
|
<button type="submit" class="btn btn-outline-primary">
|
|
<i class="bi bi-search"></i> Search
|
|
</button>
|
|
<?php if ($search): ?>
|
|
<a href="/admin_documents.php" class="btn btn-outline-secondary">Clear</a>
|
|
<?php endif; ?>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="card shadow-sm border-0">
|
|
<div class="card-body p-0">
|
|
<div class="table-responsive">
|
|
<table class="table table-hover align-middle mb-0">
|
|
<thead class="table-light">
|
|
<tr>
|
|
<th class="ps-4">ID</th>
|
|
<th>Cover</th>
|
|
<th>Title / Author</th>
|
|
<th>Type / Category</th>
|
|
<th>Year</th>
|
|
<th class="text-end pe-4">Actions</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
<?php if (empty($documents)): ?>
|
|
<tr><td colspan="6" class="text-center py-5 text-muted">No documents found.</td></tr>
|
|
<?php else: ?>
|
|
<?php foreach ($documents as $doc): ?>
|
|
<tr>
|
|
<td class="ps-4 text-muted small">#<?= $doc['id'] ?></td>
|
|
<td>
|
|
<?php if (!empty($doc['cover_image_path'])): ?>
|
|
<img src="/<?= h($doc['cover_image_path']) ?>" alt="Cover" class="rounded" style="width: 40px; height: 50px; object-fit: cover;">
|
|
<?php else: ?>
|
|
<div class="bg-light rounded d-flex align-items-center justify-content-center text-muted small" style="width: 40px; height: 50px;">
|
|
<i class="bi bi-image"></i>
|
|
</div>
|
|
<?php endif; ?>
|
|
</td>
|
|
<td>
|
|
<div class="fw-medium text-dark"><?= h($doc['title_en']) ?></div>
|
|
<div class="small text-muted" dir="rtl"><?= h($doc['title_ar']) ?></div>
|
|
<?php if (!empty($doc['author'])): ?>
|
|
<div class="small text-primary mt-1"><i class="bi bi-person me-1"></i><?= h($doc['author']) ?></div>
|
|
<?php endif; ?>
|
|
</td>
|
|
<td>
|
|
<?php if (!empty($doc['type_en'])): ?>
|
|
<span class="badge bg-info bg-opacity-10 text-info border border-info border-opacity-25 mb-1"><?= h($doc['type_en']) ?></span><br>
|
|
<?php endif; ?>
|
|
<span class="badge bg-light text-dark border"><?= h($doc['cat_en'] ?? $doc['category']) ?></span>
|
|
<?php if (!empty($doc['sub_en'])): ?>
|
|
<i class="bi bi-chevron-right text-muted small"></i>
|
|
<span class="badge bg-light text-dark border"><?= h($doc['sub_en']) ?></span>
|
|
<?php endif; ?>
|
|
</td>
|
|
<td><?= h((string)$doc['publish_year']) ?></td>
|
|
<td class="text-end pe-4">
|
|
<a href="/document.php?id=<?= $doc['id'] ?>" target="_blank" class="btn btn-sm btn-outline-secondary me-1" title="View">
|
|
<i class="bi bi-eye"></i>
|
|
</a>
|
|
<button class="btn btn-sm btn-outline-primary me-1"
|
|
onclick='openEditModal(<?= json_encode($doc) ?>)'>
|
|
<i class="bi bi-pencil"></i> Edit
|
|
</button>
|
|
<button class="btn btn-sm btn-outline-danger" onclick="deleteDocument(<?= $doc['id'] ?>)">
|
|
<i class="bi bi-trash"></i>
|
|
</button>
|
|
</td>
|
|
</tr>
|
|
<?php endforeach; ?>
|
|
<?php endif; ?>
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Document Modal -->
|
|
<div class="modal fade" id="documentModal" tabindex="-1" aria-hidden="true">
|
|
<div class="modal-dialog modal-lg modal-dialog-scrollable">
|
|
<div class="modal-content">
|
|
<form method="post" action="/admin_documents.php" id="documentForm" enctype="multipart/form-data">
|
|
<input type="hidden" name="action" id="doc_action" value="create_document">
|
|
<input type="hidden" name="id" id="doc_id" value="">
|
|
|
|
<div class="modal-header bg-primary text-white">
|
|
<h5 class="modal-title" id="documentModalTitle">Add New Material</h5>
|
|
<div class="ms-auto">
|
|
<button type="button" class="btn btn-link text-white text-decoration-none me-2" data-bs-dismiss="modal">Cancel</button>
|
|
<button type="submit" class="btn btn-light text-primary fw-bold">Save Changes</button>
|
|
</div>
|
|
</div>
|
|
<div class="modal-body">
|
|
<div class="row g-3">
|
|
<!-- Titles -->
|
|
<div class="col-md-6">
|
|
<label class="form-label small fw-bold text-uppercase text-muted">Title (English)</label>
|
|
<div class="input-group">
|
|
<input type="text" class="form-control" name="title_en" id="doc_title_en" required>
|
|
<button class="btn btn-outline-secondary" type="button" onclick="translateText('doc_title_en', 'doc_title_ar', 'Arabic')"><i class="bi bi-translate"></i></button>
|
|
</div>
|
|
</div>
|
|
<div class="col-md-6">
|
|
<label class="form-label small fw-bold text-uppercase text-muted">Title (Arabic)</label>
|
|
<div class="input-group">
|
|
<input type="text" class="form-control" name="title_ar" id="doc_title_ar" dir="rtl">
|
|
<button class="btn btn-outline-secondary" type="button" onclick="translateText('doc_title_ar', 'doc_title_en', 'English')"><i class="bi bi-translate"></i></button>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Basic Info -->
|
|
<div class="col-md-6">
|
|
<label class="form-label small fw-bold text-uppercase text-muted">Author</label>
|
|
<input type="text" class="form-control" name="author" id="doc_author">
|
|
</div>
|
|
<div class="col-md-3">
|
|
<label class="form-label small fw-bold text-uppercase text-muted">Publisher</label>
|
|
<input type="text" class="form-control" name="publisher" id="doc_publisher">
|
|
</div>
|
|
<div class="col-md-3">
|
|
<label class="form-label small fw-bold text-uppercase text-muted">Year</label>
|
|
<input type="number" class="form-control" name="publish_year" id="doc_publish_year" min="1000" max="2100">
|
|
</div>
|
|
|
|
<!-- Classification -->
|
|
<div class="col-md-4">
|
|
<label class="form-label small fw-bold text-uppercase text-muted">Type</label>
|
|
<select class="form-select" name="type_id" id="doc_type_id">
|
|
<option value="">Select Type...</option>
|
|
<?php foreach ($types as $t): ?>
|
|
<option value="<?= $t['id'] ?>"><?= h($t['name_en']) ?> / <?= h($t['name_ar']) ?></option>
|
|
<?php endforeach; ?>
|
|
</select>
|
|
</div>
|
|
<div class="col-md-4">
|
|
<label class="form-label small fw-bold text-uppercase text-muted">Category</label>
|
|
<select class="form-select" name="category_id" id="doc_category_id" onchange="updateSubcategories()">
|
|
<option value="">Select Category...</option>
|
|
<?php foreach ($categories as $c): ?>
|
|
<option value="<?= $c['id'] ?>"><?= h($c['name_en']) ?> / <?= h($c['name_ar']) ?></option>
|
|
<?php endforeach; ?>
|
|
</select>
|
|
</div>
|
|
<div class="col-md-4">
|
|
<label class="form-label small fw-bold text-uppercase text-muted">Subcategory</label>
|
|
<select class="form-select" name="subcategory_id" id="doc_subcategory_id">
|
|
<option value="">Select Category First...</option>
|
|
</select>
|
|
</div>
|
|
|
|
<!-- Details -->
|
|
<div class="col-md-6">
|
|
<label class="form-label small fw-bold text-uppercase text-muted">Country</label>
|
|
<input type="text" class="form-control" name="country" id="doc_country">
|
|
</div>
|
|
<div class="col-md-6">
|
|
<label class="form-label small fw-bold text-uppercase text-muted">Total Pages</label>
|
|
<input type="number" class="form-control" name="page_count" id="doc_page_count" min="0">
|
|
</div>
|
|
|
|
<!-- Summaries -->
|
|
<div class="col-md-6">
|
|
<label class="form-label small fw-bold text-uppercase text-muted">Summary (English)</label>
|
|
<div class="input-group">
|
|
<textarea class="form-control" name="summary_en" id="doc_summary_en" rows="3"></textarea>
|
|
<button class="btn btn-outline-secondary" type="button" onclick="translateText('doc_summary_en', 'doc_summary_ar', 'Arabic')"><i class="bi bi-translate"></i></button>
|
|
</div>
|
|
</div>
|
|
<div class="col-md-6">
|
|
<label class="form-label small fw-bold text-uppercase text-muted">Summary (Arabic)</label>
|
|
<div class="input-group">
|
|
<textarea class="form-control" name="summary_ar" id="doc_summary_ar" rows="3" dir="rtl"></textarea>
|
|
<button class="btn btn-outline-secondary" type="button" onclick="translateText('doc_summary_ar', 'doc_summary_en', 'English')"><i class="bi bi-translate"></i></button>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Files -->
|
|
<div class="col-md-6">
|
|
<label class="form-label small fw-bold text-uppercase text-muted">Front Cover Image</label>
|
|
<input class="form-control" type="file" name="cover_file" accept="image/*">
|
|
<div id="current_cover_preview" class="mt-2 d-none">
|
|
<small class="text-muted d-block mb-1">Current Cover:</small>
|
|
<img src="" class="rounded border" style="height: 60px;">
|
|
</div>
|
|
</div>
|
|
<div class="col-md-6">
|
|
<label class="form-label small fw-bold text-uppercase text-muted">Document File</label>
|
|
<input class="form-control" type="file" name="document_file">
|
|
<div id="current_file_info" class="mt-2 d-none">
|
|
<small class="text-muted"><i class="bi bi-file-earmark"></i> <span id="current_filename"></span></small>
|
|
</div>
|
|
<small class="text-muted d-block mt-1">Leave empty to keep existing file on edit.</small>
|
|
</div>
|
|
|
|
<!-- Settings -->
|
|
<div class="col-12">
|
|
<div class="card bg-light border-0">
|
|
<div class="card-body">
|
|
<h6 class="card-subtitle mb-2 text-muted text-uppercase small fw-bold">Visibility & Permissions</h6>
|
|
<div class="row">
|
|
<div class="col-md-3">
|
|
<select class="form-select form-select-sm" name="visibility" id="doc_visibility">
|
|
<option value="public">Public</option>
|
|
<option value="private">Private</option>
|
|
</select>
|
|
</div>
|
|
<div class="col-md-9 d-flex align-items-center gap-3">
|
|
<div class="form-check">
|
|
<input class="form-check-input" type="checkbox" name="allow_download" id="doc_allow_download" value="1">
|
|
<label class="form-check-label small" for="doc_allow_download">Download</label>
|
|
</div>
|
|
<div class="form-check">
|
|
<input class="form-check-input" type="checkbox" name="allow_print" id="doc_allow_print" value="1">
|
|
<label class="form-check-label small" for="doc_allow_print">Print</label>
|
|
</div>
|
|
<div class="form-check">
|
|
<input class="form-check-input" type="checkbox" name="allow_copy" id="doc_allow_copy" value="1">
|
|
<label class="form-check-label small" for="doc_allow_copy">Copy</label>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Delete Confirmation Form -->
|
|
<form method="post" action="/admin_documents.php" id="deleteForm">
|
|
<input type="hidden" name="action" id="deleteAction" value="">
|
|
<input type="hidden" name="id" id="deleteId" value="">
|
|
</form>
|
|
|
|
<script>
|
|
let documentModal;
|
|
const allSubcategories = <?= json_encode($allSubcategories) ?>;
|
|
|
|
document.addEventListener('DOMContentLoaded', function() {
|
|
documentModal = new bootstrap.Modal(document.getElementById('documentModal'));
|
|
});
|
|
|
|
function updateSubcategories(selectedSubId = null) {
|
|
const catSelect = document.getElementById('doc_category_id');
|
|
const subSelect = document.getElementById('doc_subcategory_id');
|
|
const catId = catSelect.value;
|
|
|
|
subSelect.innerHTML = '<option value="">Select Subcategory...</option>';
|
|
|
|
if (catId) {
|
|
const subs = allSubcategories.filter(s => s.category_id == catId);
|
|
subs.forEach(s => {
|
|
const opt = document.createElement('option');
|
|
opt.value = s.id;
|
|
opt.textContent = s.name_en + ' / ' + s.name_ar;
|
|
if (selectedSubId && s.id == selectedSubId) opt.selected = true;
|
|
subSelect.appendChild(opt);
|
|
});
|
|
}
|
|
}
|
|
|
|
function openCreateModal() {
|
|
document.getElementById('documentModalTitle').innerText = 'Add New Material';
|
|
document.getElementById('doc_action').value = 'create_document';
|
|
document.getElementById('doc_id').value = '';
|
|
document.getElementById('documentForm').reset();
|
|
|
|
// Clear previews
|
|
document.getElementById('current_cover_preview').classList.add('d-none');
|
|
document.getElementById('current_file_info').classList.add('d-none');
|
|
|
|
// Reset dynamic selects
|
|
updateSubcategories();
|
|
|
|
documentModal.show();
|
|
}
|
|
|
|
function openEditModal(doc) {
|
|
document.getElementById('documentModalTitle').innerText = 'Edit Material: ' + doc.title_en;
|
|
document.getElementById('doc_action').value = 'update_document';
|
|
document.getElementById('doc_id').value = doc.id;
|
|
|
|
// Fill fields
|
|
document.getElementById('doc_title_en').value = doc.title_en || '';
|
|
document.getElementById('doc_title_ar').value = doc.title_ar || '';
|
|
document.getElementById('doc_author').value = doc.author || '';
|
|
document.getElementById('doc_publisher').value = doc.publisher || '';
|
|
document.getElementById('doc_publish_year').value = doc.publish_year || '';
|
|
document.getElementById('doc_country').value = doc.country || '';
|
|
document.getElementById('doc_page_count').value = doc.page_count || '';
|
|
document.getElementById('doc_summary_en').value = doc.summary_en || '';
|
|
document.getElementById('doc_summary_ar').value = doc.summary_ar || '';
|
|
|
|
document.getElementById('doc_type_id').value = doc.type_id || '';
|
|
document.getElementById('doc_visibility').value = doc.visibility || 'public';
|
|
|
|
document.getElementById('doc_allow_download').checked = !!parseInt(doc.allow_download);
|
|
document.getElementById('doc_allow_print').checked = !!parseInt(doc.allow_print);
|
|
document.getElementById('doc_allow_copy').checked = !!parseInt(doc.allow_copy);
|
|
|
|
// Handle Category & Subcategory
|
|
document.getElementById('doc_category_id').value = doc.category_id || '';
|
|
updateSubcategories(doc.subcategory_id);
|
|
|
|
// Previews
|
|
const coverDiv = document.getElementById('current_cover_preview');
|
|
if (doc.cover_image_path) {
|
|
coverDiv.classList.remove('d-none');
|
|
coverDiv.querySelector('img').src = '/' + doc.cover_image_path;
|
|
} else {
|
|
coverDiv.classList.add('d-none');
|
|
}
|
|
|
|
const fileDiv = document.getElementById('current_file_info');
|
|
if (doc.file_name) {
|
|
fileDiv.classList.remove('d-none');
|
|
document.getElementById('current_filename').innerText = doc.file_name;
|
|
} else {
|
|
fileDiv.classList.add('d-none');
|
|
}
|
|
|
|
documentModal.show();
|
|
}
|
|
|
|
function deleteDocument(id) {
|
|
if (confirm('Are you sure you want to delete this document?')) {
|
|
document.getElementById('deleteAction').value = 'delete_document';
|
|
document.getElementById('deleteId').value = id;
|
|
document.getElementById('deleteForm').submit();
|
|
}
|
|
}
|
|
</script>
|
|
|
|
<?php admin_render_footer(); ?>
|