One Hat Cyber Team
Your IP :
104.23.197.103
Server IP :
172.67.218.182
Server :
Linux 128-201-239-36.cprapid.com 3.10.0-1160.41.1.el7.x86_64 #1 SMP Tue Aug 31 14:52:47 UTC 2021 x86_64
Server Software :
Apache
PHP Version :
7.4.33
Buat File
|
Buat Folder
Eksekusi
Dir :
~
/
home
/
juscatamarca
/
www
/
campusjxj
/
public
/
admin
/
View File Name :
class_files.php
<?php declare(strict_types=1); require_once dirname(__DIR__, 2) . '/config/app.php'; require_once ROOT_PATH . '/helpers/functions.php'; require_once ROOT_PATH . '/helpers/auth.php'; require_once ROOT_PATH . '/helpers/admin_class_files.php'; require_role('admin'); $pageTitle = 'Archivos de clase'; $navbarTitle = 'Biblioteca de archivos de clase'; $currentPage = 'class_files'; $allowedTypes = ['study_material', 'exam', 'extra']; $allowedStatuses = ['active', 'inactive', 'archived']; $allowedExtensions = ['pdf', 'doc', 'docx']; $maxFileSizeBytes = 20 * 1024 * 1024; // 20 MB /** * Convierte valores de php.ini como "8M" o "2G" a bytes. */ function ini_size_to_bytes(string $value): int { $value = trim($value); if ($value === '') { return 0; } $unit = strtolower(substr($value, -1)); if ($unit >= '0' && $unit <= '9') { return (int) $value; } $number = (float) substr($value, 0, -1); switch ($unit) { case 'g': return (int) round($number * 1024 * 1024 * 1024); case 'm': return (int) round($number * 1024 * 1024); case 'k': return (int) round($number * 1024); default: return (int) $number; } } $phpUploadMaxBytes = ini_size_to_bytes((string) ini_get('upload_max_filesize')); $phpPostMaxBytes = ini_size_to_bytes((string) ini_get('post_max_size')); $errors = []; $flashSuccess = get_flash('success'); $flashError = get_flash('error'); $formData = [ 'id' => '0', 'class_id' => '', 'file_type' => 'study_material', 'status' => 'active', ]; $editId = (int) ($_GET['edit'] ?? 0); if ($editId > 0 && $_SERVER['REQUEST_METHOD'] !== 'POST') { $editingFile = get_admin_class_file_by_id($editId); if ($editingFile === null) { set_flash('error', 'El archivo seleccionado no existe.'); redirect('admin/class_files.php'); } $formData = [ 'id' => (string) ($editingFile['id'] ?? 0), 'class_id' => (string) ($editingFile['class_id'] ?? ''), 'file_type' => (string) ($editingFile['file_type'] ?? 'study_material'), 'status' => (string) ($editingFile['status'] ?? 'active'), ]; } if ($_SERVER['REQUEST_METHOD'] === 'POST') { $contentLength = (int) ($_SERVER['CONTENT_LENGTH'] ?? 0); if ($phpPostMaxBytes > 0 && $contentLength > $phpPostMaxBytes) { $errors[] = 'El archivo excede el tamaño máximo permitido de 20 MB.'; } $action = (string) ($_POST['action'] ?? 'create'); $fileId = (int) ($_POST['id'] ?? 0); if ($action === 'delete') { if ($fileId <= 0) { set_flash('error', 'El archivo seleccionado no existe.'); redirect('admin/class_files.php'); } try { delete_admin_class_file($fileId); set_flash('success', 'Archivo eliminado correctamente.'); redirect('admin/class_files.php'); } catch (Throwable $e) { set_flash('error', 'No se pudo eliminar el archivo.'); redirect('admin/class_files.php'); } } $formData = [ 'id' => (string) $fileId, 'class_id' => trim((string) ($_POST['class_id'] ?? '')), 'file_type' => trim((string) ($_POST['file_type'] ?? 'study_material')), 'status' => trim((string) ($_POST['status'] ?? 'active')), ]; $classId = (int) $formData['class_id']; $fileType = $formData['file_type']; $status = $formData['status']; if (empty($errors) && ($classId <= 0 || !admin_class_exists($classId))) { $errors[] = 'Debes seleccionar una clase válida.'; } if (empty($errors) && !in_array($fileType, $allowedTypes, true)) { $errors[] = 'El tipo de archivo no es válido.'; } if (empty($errors) && !in_array($status, $allowedStatuses, true)) { $errors[] = 'El estado del archivo no es válido.'; } if ($action === 'create' && empty($errors)) { $uploadedFile = $_FILES['uploaded_file'] ?? null; if (!is_array($uploadedFile) || (int) ($uploadedFile['error'] ?? UPLOAD_ERR_NO_FILE) === UPLOAD_ERR_NO_FILE) { $errors[] = 'Debes seleccionar un archivo para subir.'; } else { $uploadError = (int) ($uploadedFile['error'] ?? UPLOAD_ERR_OK); if ($uploadError !== UPLOAD_ERR_OK) { if ($uploadError === UPLOAD_ERR_INI_SIZE || $uploadError === UPLOAD_ERR_FORM_SIZE) { $errors[] = 'El archivo excede el tamaño máximo permitido de 20 MB.'; } else { $errors[] = 'Ocurrió un error durante la carga del archivo.'; } } else { $extension = strtolower((string) pathinfo((string) ($uploadedFile['name'] ?? ''), PATHINFO_EXTENSION)); $size = (int) ($uploadedFile['size'] ?? 0); if (!in_array($extension, $allowedExtensions, true)) { $errors[] = 'Extensión no permitida. Solo se aceptan pdf, doc y docx.'; } if ($size <= 0) { $errors[] = 'El archivo está vacío.'; } if ($size > $maxFileSizeBytes) { $errors[] = 'El archivo excede el tamaño máximo permitido de 20 MB.'; } } } } if (empty($errors)) { try { if ($action === 'update' && $fileId > 0) { // Si se sube un archivo, validar y reemplazar $uploadedFile = $_FILES['uploaded_file'] ?? null; $replaceFile = is_array($uploadedFile) && (int)($uploadedFile['error'] ?? UPLOAD_ERR_NO_FILE) === UPLOAD_ERR_OK; if ($replaceFile) { $extension = strtolower((string) pathinfo((string) ($uploadedFile['name'] ?? ''), PATHINFO_EXTENSION)); $size = (int) ($uploadedFile['size'] ?? 0); if (!in_array($extension, $allowedExtensions, true)) { $errors[] = 'Extensión no permitida. Solo se aceptan pdf, doc y docx.'; } if ($size <= 0) { $errors[] = 'El archivo está vacío.'; } if ($size > $maxFileSizeBytes) { $errors[] = 'El archivo excede el tamaño máximo permitido de 20 MB.'; } } if (empty($errors)) { update_admin_class_file($fileId, [ 'class_id' => $classId, 'file_type' => $fileType, 'status' => $status, ], $replaceFile ? $uploadedFile : null); set_flash('success', 'Archivo actualizado correctamente.'); redirect('admin/class_files.php'); } } $uploadedFile = $_FILES['uploaded_file']; $mimeType = ''; if (is_file((string) $uploadedFile['tmp_name'])) { $finfo = finfo_open(FILEINFO_MIME_TYPE); if ($finfo !== false) { $detected = finfo_file($finfo, (string) $uploadedFile['tmp_name']); $mimeType = $detected !== false ? (string) $detected : ''; finfo_close($finfo); } } create_admin_class_file([ 'class_id' => $classId, 'uploaded_by' => (int) (current_user()['id'] ?? 0), 'file_type' => $fileType, 'status' => $status, 'mime_type' => $mimeType, ], $uploadedFile); set_flash('success', 'Archivo cargado correctamente.'); redirect('admin/class_files.php'); } catch (Throwable $e) { $errors[] = 'No se pudo guardar el archivo en este momento.'; } } } $search = trim((string) ($_GET['q'] ?? '')); $typeFilter = trim((string) ($_GET['type'] ?? 'all')); $statusFilter = trim((string) ($_GET['status'] ?? 'all')); $typeParam = in_array($typeFilter, $allowedTypes, true) ? $typeFilter : null; $statusParam = in_array($statusFilter, $allowedStatuses, true) ? $statusFilter : null; $classOptions = []; $classFileRows = []; $listError = null; try { $classOptions = get_class_options(); $classFileRows = get_admin_class_files($search !== '' ? $search : null, $typeParam, $statusParam); } catch (Throwable $e) { $listError = 'No se pudo cargar la información del módulo.'; } $isEditMode = (int) $formData['id'] > 0; include ROOT_PATH . '/includes/layout/header.php'; ?> <?php include ROOT_PATH . '/includes/layout/sidebar_admin.php'; ?> <div class="main-panel"> <?php include ROOT_PATH . '/includes/layout/navbar.php'; ?> <main class="content-area"> <section class="hero-panel mb-4"> <div> <p class="hero-tag mb-2">Administración Académica</p> <h2 class="h3 fw-bold mb-2">Gestión de Archivos de Clase</h2> <p class="mb-0 text-muted">Administra materiales, exámenes y recursos extra asociados a cada clase.</p> </div> </section> <?php if ($flashSuccess): ?> <div class="alert alert-success" role="alert"><?= e($flashSuccess) ?></div> <?php endif; ?> <?php if ($flashError): ?> <div class="alert alert-danger" role="alert"><?= e($flashError) ?></div> <?php endif; ?> <?php if (!empty($errors)): ?> <div class="alert alert-danger" role="alert"> <strong>Revisa el formulario:</strong> <ul class="mb-0 mt-2"> <?php foreach ($errors as $error): ?> <li><?= e($error) ?></li> <?php endforeach; ?> </ul> </div> <?php endif; ?> <div class="card card-campus mb-4"> <div class="card-header-campus"> <h3 class="h6 mb-0"><?= $isEditMode ? 'Editar archivo de clase' : 'Cargar archivo de clase' ?></h3> <?php if ($isEditMode): ?> <a href="<?= e(base_url('admin/class_files.php')) ?>" class="btn btn-sm btn-outline-success">Cancelar edición</a> <?php endif; ?> </div> <div class="card-body"> <div class="alert alert-info mb-3" role="note"> <i class="fa-solid fa-circle-info me-1"></i> Tipos de archivo: <strong>Material de estudio</strong>, <strong>Examen</strong> y <strong>Recurso extra</strong>. Extensiones permitidas: pdf, doc, docx. Máximo 20 MB. </div> <form class="row g-3" method="post" enctype="multipart/form-data" novalidate> <input type="hidden" name="action" value="<?= $isEditMode ? 'update' : 'create' ?>"> <input type="hidden" name="id" value="<?= e($formData['id']) ?>"> <div class="col-md-6"> <label class="form-label">Clase asociada</label> <select class="form-select" name="class_id" required> <option value="">Seleccionar clase</option> <?php foreach ($classOptions as $classOpt): ?> <option value="<?= e((string) $classOpt['id']) ?>" <?= (string) $formData['class_id'] === (string) $classOpt['id'] ? 'selected' : '' ?>> <?= e((string) $classOpt['course_title']) ?> - Clase <?= e((string) $classOpt['class_order']) ?>: <?= e((string) $classOpt['class_title']) ?> </option> <?php endforeach; ?> </select> </div> <div class="col-md-6"> <label class="form-label">Tipo de archivo</label> <select class="form-select" name="file_type" required> <option value="study_material" <?= $formData['file_type'] === 'study_material' ? 'selected' : '' ?>>Material de estudio</option> <option value="exam" <?= $formData['file_type'] === 'exam' ? 'selected' : '' ?>>Examen</option> <option value="extra" <?= $formData['file_type'] === 'extra' ? 'selected' : '' ?>>Recurso extra</option> </select> </div> <div class="col-md-6"> <label class="form-label">Archivo</label> <input type="file" class="form-control" name="uploaded_file" accept=".pdf,.doc,.docx" <?= $isEditMode ? '' : 'required' ?>> <?php if ($isEditMode && !empty($formData['id'])): ?> <small class="text-muted d-block mt-1">Si seleccionas un archivo, reemplazará el actual. Deja vacío para mantener el archivo existente.</small> <?php endif; ?> </div> <div class="col-md-6"> <label class="form-label">Estado</label> <select class="form-select" name="status" required> <option value="active" <?= $formData['status'] === 'active' ? 'selected' : '' ?>>Activo</option> <option value="inactive" <?= $formData['status'] === 'inactive' ? 'selected' : '' ?>>Inactivo</option> <option value="archived" <?= $formData['status'] === 'archived' ? 'selected' : '' ?>>Archivado</option> </select> </div> <div class="col-12 text-end"> <button type="submit" class="btn btn-campus"> <i class="fa-solid fa-floppy-disk me-1"></i><?= $isEditMode ? 'Guardar cambios' : 'Cargar archivo' ?> </button> </div> </form> </div> </div> <div class="card card-campus"> <div class="card-header-campus"> <h3 class="h6 mb-0">Listado de archivos</h3> </div> <div class="card-body border-bottom"> <form method="get" class="row g-2 align-items-end"> <div class="col-12 col-md-5"> <label class="form-label mb-1">Buscar</label> <input type="text" class="form-control" name="q" value="<?= e($search) ?>" placeholder="Archivo, clase o curso"> </div> <div class="col-12 col-md-3"> <label class="form-label mb-1">Tipo</label> <select class="form-select" name="type"> <option value="all" <?= $typeFilter === 'all' ? 'selected' : '' ?>>Todos</option> <option value="study_material" <?= $typeFilter === 'study_material' ? 'selected' : '' ?>>Material de estudio</option> <option value="exam" <?= $typeFilter === 'exam' ? 'selected' : '' ?>>Examen</option> <option value="extra" <?= $typeFilter === 'extra' ? 'selected' : '' ?>>Extra</option> </select> </div> <div class="col-12 col-md-2"> <label class="form-label mb-1">Estado</label> <select class="form-select" name="status"> <option value="all" <?= $statusFilter === 'all' ? 'selected' : '' ?>>Todos</option> <option value="active" <?= $statusFilter === 'active' ? 'selected' : '' ?>>Activo</option> <option value="inactive" <?= $statusFilter === 'inactive' ? 'selected' : '' ?>>Inactivo</option> <option value="archived" <?= $statusFilter === 'archived' ? 'selected' : '' ?>>Archivado</option> </select> </div> <div class="col-12 col-md-2 d-flex gap-2"> <button type="submit" class="btn btn-campus flex-grow-1">Filtrar</button> <a href="<?= e(base_url('admin/class_files.php')) ?>" class="btn btn-outline-success">Limpiar</a> </div> </form> </div> <?php if ($listError !== null): ?> <div class="card-body"> <div class="alert alert-danger mb-0" role="alert"><?= e($listError) ?></div> </div> <?php elseif (empty($classFileRows)): ?> <div class="card-body text-center py-5"> <i class="fa-solid fa-inbox mb-3" style="font-size: 2rem; color: var(--campus-gray-500); opacity: 0.5"></i> <p class="mb-0 text-muted">No hay archivos para los filtros seleccionados.</p> </div> <?php else: ?> <div class="table-responsive"> <table class="table align-middle mb-0"> <thead> <tr> <th>Archivo</th> <th>Clase</th> <th>Curso</th> <th>Tipo</th> <th>Extensión</th> <th>Tamaño</th> <th>Estado</th> <th>Fecha</th> <th class="text-end">Acciones</th> </tr> </thead> <tbody> <?php foreach ($classFileRows as $classFile): ?> <?php $typeBadge = admin_file_type_badge((string) ($classFile['file_type'] ?? '')); $statusBadge = admin_file_status_badge((string) ($classFile['status'] ?? '')); ?> <tr> <td><?= e($classFile['original_name']) ?></td> <td> Clase <?= e((string) ($classFile['class_order'] ?? '')) ?> - <?= e((string) ($classFile['class_title'] ?? '')) ?> </td> <td><?= e((string) ($classFile['course_title'] ?? '')) ?> <small class="text-muted d-block"><?= e((string) ($classFile['geographic_department_name'] ?? 'Sin definir')) ?></small></td> <td><span class="badge <?= e($typeBadge['class']) ?>"><?= e($typeBadge['label']) ?></span></td> <td><?= e($classFile['file_extension']) ?></td> <td><?= e(format_bytes((int) $classFile['file_size_bytes'])) ?></td> <td><span class="badge <?= e($statusBadge['class']) ?>"><?= e($statusBadge['label']) ?></span></td> <td><?= e(date('d/m/Y H:i', (int) strtotime((string) $classFile['created_at']))) ?></td> <td class="text-end"> <a href="<?= e(base_url('admin/class_files.php?edit=' . (int) $classFile['id'])) ?>" class="btn btn-sm btn-outline-success"> <i class="fa-solid fa-pen-to-square me-1"></i>Editar </a> <form method="post" style="display: inline;" onsubmit="return confirm('¿Estás seguro de que deseas eliminar este archivo? Esta acción no se puede deshacer.');"> <input type="hidden" name="action" value="delete"> <input type="hidden" name="id" value="<?= (int) $classFile['id'] ?>"> <button type="submit" class="btn btn-sm btn-outline-danger"> <i class="fa-solid fa-trash me-1"></i>Eliminar </button> </form> </td> </tr> <?php endforeach; ?> </tbody> </table> </div> <?php endif; ?> </div> </main> </div> <?php include ROOT_PATH . '/includes/layout/footer.php'; ?>