浏览代码

Initial state

Alejandro Rosales 2 年之前
当前提交
c3c0108143
共有 100 个文件被更改,包括 9975 次插入0 次删除
  1. 16 0
      .gitignore
  2. 5 0
      .prettierrc
  3. 43 0
      action/action_asistencias.php
  4. 100 0
      action/action_asistencias_excel.php
  5. 9 0
      action/action_avisos_delete.php
  6. 32 0
      action/action_avisos_insert.php
  7. 52 0
      action/action_avisos_update.php
  8. 24 0
      action/action_carreras.php
  9. 19 0
      action/action_carreras_insert.php
  10. 16 0
      action/action_carreras_select.php
  11. 19 0
      action/action_carreras_update.php
  12. 15 0
      action/action_diasfestivos_borra.php
  13. 48 0
      action/action_diasfestivos_insert.php
  14. 19 0
      action/action_diasfestivos_select.php
  15. 31 0
      action/action_diasfestivos_update.php
  16. 11 0
      action/action_facultad.php
  17. 23 0
      action/action_facultades_insert.php
  18. 15 0
      action/action_facultades_select.php
  19. 17 0
      action/action_facultades_update.php
  20. 24 0
      action/action_grupo.php
  21. 35 0
      action/action_horario.php
  22. 41 0
      action/action_horario_create.php
  23. 38 0
      action/action_horario_delete.php
  24. 51 0
      action/action_horario_excel.php
  25. 4 0
      action/action_horario_profesor.php
  26. 48 0
      action/action_horario_update.php
  27. 33 0
      action/action_justificar.php
  28. 40 0
      action/action_login.php
  29. 26 0
      action/action_materias.php
  30. 8 0
      action/action_materias_select.php
  31. 11 0
      action/action_materias_update.php
  32. 17 0
      action/action_new_horario.php
  33. 17 0
      action/action_periodos_insert.php
  34. 8 0
      action/action_periodos_select.php
  35. 18 0
      action/action_periodos_update.php
  36. 27 0
      action/action_periodousuario_update.php
  37. 40 0
      action/action_permisos_update.php
  38. 14 0
      action/action_profesor.php
  39. 40 0
      action/action_profesor_faltas.php
  40. 7 0
      action/action_profesores_borra.php
  41. 75 0
      action/action_profesores_insert.php
  42. 10 0
      action/action_profesores_select.php
  43. 36 0
      action/action_profesores_update.php
  44. 50 0
      action/action_revisar_excel.php
  45. 11 0
      action/action_roles_insert.php
  46. 11 0
      action/action_roles_select.php
  47. 11 0
      action/action_roles_update.php
  48. 37 0
      action/action_tiempos_update.php
  49. 22 0
      action/action_usuario.php
  50. 22 0
      action/action_usuarios_insert.php
  51. 8 0
      action/action_usuarios_select.php
  52. 15 0
      action/action_usuarios_update.php
  53. 10 0
      action/carrera_find.php
  54. 37 0
      action/force_session.php
  55. 二进制
      action/one_row.xlsx
  56. 26 0
      action/usuario_find.php
  57. 286 0
      alta_de_horario.php
  58. 264 0
      avisos.php
  59. 435 0
      avisos_crear.php
  60. 431 0
      avisos_editar.php
  61. 58 0
      base.php
  62. 140 0
      bypass.php
  63. 813 0
      carreras.php
  64. 74 0
      class/c_logasistencia.php
  65. 86 0
      class/c_login.php
  66. 15 0
      class/c_menu.php
  67. 57 0
      class/connection.php
  68. 0 0
      coldbypass.php
  69. 7 0
      composer.json
  70. 1155 0
      composer.lock
  71. 1608 0
      consultar_horario.php
  72. 二进制
      css/bg-progress-bar.png
  73. 0 0
      css/bootstrap-ulsa.min.css
  74. 39 0
      css/calendar.css
  75. 46 0
      css/checador.css
  76. 二进制
      css/cif-icons-20xy.png
  77. 143 0
      css/clockpicker.css
  78. 9 0
      css/custominputfile.min.css
  79. 4 0
      css/fa_all.css
  80. 二进制
      css/images/ui-icons_444444_256x240.png
  81. 二进制
      css/images/ui-icons_555555_256x240.png
  82. 二进制
      css/images/ui-icons_777620_256x240.png
  83. 二进制
      css/images/ui-icons_777777_256x240.png
  84. 二进制
      css/images/ui-icons_cc0000_256x240.png
  85. 二进制
      css/images/ui-icons_ffffff_256x240.png
  86. 30 0
      css/index.css
  87. 154 0
      css/indivisa.css
  88. 3 0
      css/jquery-ui.css
  89. 105 0
      css/lasalle.css
  90. 190 0
      css/richtext.css
  91. 1056 0
      css/sgi.css
  92. 7 0
      css/style.css
  93. 142 0
      css/toggle.css
  94. 343 0
      días_festivos.php
  95. 111 0
      editar_horario.php
  96. 209 0
      excel_horario.php
  97. 283 0
      facultades.php
  98. 120 0
      horario_profesor.php
  99. 5 0
      import/html_css_files.php
  100. 105 0
      import/html_footer.php

+ 16 - 0
.gitignore

@@ -0,0 +1,16 @@
+composer.phar
+/vendor/
+
+/temp/
+/template/
+/node_modules/
+/include/nusoap/
+/fonts/
+/imagenes/
+/concept/
+/backup/
+/.vscode/
+/export/
+
+/include/.env
+*/.env

+ 5 - 0
.prettierrc

@@ -0,0 +1,5 @@
+{
+  "tabWidth": 2,
+  "useTabs": false,
+  "bracketSpacing": false
+}

+ 43 - 0
action/action_asistencias.php

@@ -0,0 +1,43 @@
+<?php
+$ruta = "../";
+require_once "../class/c_login.php";
+
+// check if the session is started
+if (!isset($_SESSION['user']))
+    die(json_encode(['error' => 'No se ha iniciado sesión']));
+
+$user = unserialize($_SESSION['user']);
+
+extract($_POST);
+
+$initial_date = DateTime::createFromFormat('d/m/Y', $fecha_inicial);
+$final_date = DateTime::createFromFormat('d/m/Y', $fecha_final);
+
+if ($initial_date > $final_date) {
+    echo json_encode(['error' => 'La fecha inicial no puede ser mayor a la fecha final']);
+    die;
+}
+// Nombre del profesor es opcional
+$params = [
+    ':carrera' => empty($carrera) ? null : $carrera,
+    ':periodo' => $periodo,
+    ':nombre' => empty($nombre) ? null : $nombre,
+    ':clave' => empty($clave) ? null : $clave,
+    ':initial_date' => $initial_date->format('Y-m-d'),
+    ':final_date' => $final_date->format('Y-m-d'),
+    ':facultad' => $facultad,
+];
+
+$response = json_encode(
+    [
+        "retardo" => query("SELECT FS_HAS_RETARDO(:facultad) retardo", [
+            'facultad' => $facultad
+        ]),
+        "reporte" => queryAll(
+            "SELECT * FROM fs_asistencia_reporte(:carrera, :periodo, :clave, :nombre, :facultad, :initial_date, :final_date) where total > 0",
+            $params
+        )
+    ]
+);
+$user->print_to_log("Genera reporte de asistencias", old: $params);
+echo $response;

+ 100 - 0
action/action_asistencias_excel.php

@@ -0,0 +1,100 @@
+<?php
+$ruta = "../";
+require_once "../vendor/autoload.php";
+require_once "../class/c_login.php";
+    
+if (!isset($_SESSION['user']))
+    die(json_encode(['error' => 'No se ha iniciado sesión']));
+
+$user = unserialize($_SESSION['user']);
+$user->print_to_log('Genera excel de asistencias');
+
+use PhpOffice\PhpSpreadsheet\Spreadsheet;
+
+$spreadsheet = new Spreadsheet();
+$sheet = $spreadsheet->getActiveSheet();
+
+//crea imagen 
+$drawing = new \PhpOffice\PhpSpreadsheet\Worksheet\Drawing();
+$drawing->setName('La Salle');
+$drawing->setDescription('La Salle');
+$drawing->setPath('../imagenes/logo.png'); // put your path and image here
+$drawing->setCoordinates('A1');
+$drawing->setHeight(100);
+$drawing->setOffsetX(10);
+//agrega imagen
+$drawing->setWorksheet($spreadsheet->getActiveSheet());
+
+
+// In POST
+/** Array
+ * * nombre
+ * * clave
+ * * id
+ * * total
+ * * asistencias
+ * * faltas
+ * * justificaciones
+ * * retardos
+ */
+
+$retardo = query("SELECT COALESCE(FS_HAS_RETARDO(:facultad), FALSE) AS retardo", [':facultad' => $user->facultad['facultad_id']])['retardo'];
+extract($_POST);
+
+$row = 6;
+
+$sheet->setCellValue("A$row", 'Clave');
+$sheet->setCellValue("B$row", 'Profesor');
+$sheet->setCellValue("C$row", 'Asistencias');
+$sheet->setCellValue("D$row", 'Faltas');
+$sheet->setCellValue("E$row", 'Justificaciones');
+$sheet->setCellValue("F$row", 'Retardos');
+$sheet->setCellValue("G$row", 'Total');
+
+// $row++;
+$col = 0;
+# die(print_r($asistencias, true));
+foreach (json_decode($asistencias, true) as $profesor) {
+    $row++;
+    $sheet->setCellValue("A$row", $profesor['profesor_clave']);
+    $sheet->setCellValue("B$row", $profesor['profesor_nombre']);
+    $sheet->setCellValue("C$row", $profesor['asistencias']);
+    $sheet->setCellValue("D$row", $profesor['faltas']);
+    $sheet->setCellValue("E$row", $profesor['justificaciones']);
+    $sheet->setCellValue("F$row", $profesor['retardos']);
+    $sheet->setCellValue("G$row", $profesor['total']);
+}
+
+# Style
+$sheet->getStyle("A6:G$row")->getBorders()->getAllBorders()->setBorderStyle(\PhpOffice\PhpSpreadsheet\Style\Border::BORDER_THIN);
+$sheet->getStyle("A6:G$row")->getAlignment()->setHorizontal(\PhpOffice\PhpSpreadsheet\Style\Alignment::HORIZONTAL_CENTER);
+$sheet->getStyle("A6:G$row")->getAlignment()->setVertical(\PhpOffice\PhpSpreadsheet\Style\Alignment::VERTICAL_CENTER);
+$sheet->getStyle("A6:G$row")->getAlignment()->setWrapText(true);
+$sheet->getStyle("A6:G$row")->getFont()->setSize(12);
+$sheet->getStyle("A6:G$row")->getFont()->setName('Indivisa Sans');
+# Autosize columns
+foreach (range('A', 'G') as $column) {
+    $sheet->getColumnDimension($column)->setAutoSize(true);
+}
+# filters in the column
+$sheet->setAutoFilter("A6:G6");
+
+if (!$retardo) # hide column
+    $sheet->getColumnDimension('F')->setVisible(false);
+
+#$writer = new Xlsx($spreadsheet);
+$writer = \PhpOffice\PhpSpreadsheet\IOFactory::createWriter($spreadsheet, 'Xlsx');
+# $writer->save('asistencias.xlsx');
+
+// download
+header('Content-Type: application/vnd.openxmlformats-officedocument.spreadsheetml.sheet');
+header('Content-Disposition: attachment;filename="asistencias.xlsx"');
+header('Cache-Control: max-age=0');
+
+// cache expires in 60 seconds (1 minute)
+header('Expires: mon 26 jul 1997 05:00:00 gmt');
+header('Last-Modified: ' . gmdate('D, d M Y H:i:s') . ' GMT');
+header('Cache-Control: cache, must-revalidate');
+header('Pragma: public');
+
+$writer->save('php://output');

+ 9 - 0
action/action_avisos_delete.php

@@ -0,0 +1,9 @@
+<?php
+$ruta = '../';
+require_once '../include/bd_pdo.php';
+global $pdo;
+
+$sql = "SELECT fu_update_estado_aviso(false, :id)";
+$params = [':id' => $_POST['id']];
+echo json_encode(query($sql, $params, false));
+?>

+ 32 - 0
action/action_avisos_insert.php

@@ -0,0 +1,32 @@
+<?php
+$ruta = '../';
+require_once '../include/bd_pdo.php';
+global $pdo;
+
+print_r($_POST);
+
+$profesores = [];
+if(isset($_POST['tipo'])){
+    foreach($_POST['tipo'] as $tipo){
+        $profesores_carrera = query("SELECT profesor_id FROM fs_profesor_carrera(:carrera_id)", [':carrera_id' => $tipo], false);
+        foreach($profesores_carrera as $profesor){
+            array_push($profesores, $profesor['profesor_id']);
+        }
+
+    }
+}
+foreach($_POST['usuario'] as $profesor){
+    array_push($profesores, $profesor);
+}
+$sql = "SELECT fi_aviso(:fecha_inicial, :fecha_final, :texto, :facultad)";
+$params = [':fecha_inicial' => $_POST['fecha_inicial'], ':fecha_final' => $_POST['fecha_final'], ':texto' => $_POST['texto'], ':facultad' => $_POST['facultad']];
+$aviso_id = query($sql, $params, true);
+
+$sql = "SELECT fi_aviso_profesor(:aviso_id, :profesor_id)";
+foreach($profesores as $profesor_id){
+    $params = [':aviso_id' => $aviso_id['fi_aviso'], ':profesor_id' => $profesor_id];
+    query($sql, $params, false);
+}
+header("Location: ../avisos.php");
+exit();
+?>

+ 52 - 0
action/action_avisos_update.php

@@ -0,0 +1,52 @@
+<?php
+$ruta = '../';
+require_once '../include/bd_pdo.php';
+global $pdo;
+
+$aviso = query("SELECT * FROM fs_aviso(:id, null, null, null, 0, null)", [':id' => $_POST['aviso_id']], true);
+if(isset($_POST['fecha_final'])){
+    $fecha_fin = $_POST['fecha_final'];
+}else{
+    $fecha_fin = $aviso['aviso_fecha_final'];
+}
+if(isset($_POST['texto'])){
+    $texto = $_POST['texto'];
+}else{
+    $texto = $aviso['aviso_texto'];
+}
+if(isset($_POST['fecha_inicial'])){
+    $fecha_inicio = $_POST['fecha_inicial'];
+}else{
+    $fecha_inicio = $aviso['aviso_fecha_inicial'];
+}
+
+$sql = "SELECT fu_update_aviso(:id, :fecha_fin, :texto, :fecha_inicio)";
+$params = [':id' => $_POST['aviso_id'], ':fecha_fin' => $fecha_fin, ':texto' => $texto, ':fecha_inicio' => $fecha_inicio];
+query($sql, $params, true);
+
+query("SELECT fd_aviso_profesor(:aviso_id)", [':aviso_id' => $_POST['aviso_id']], false);
+
+$profesores = [];
+if(isset($_POST['tipo'])){
+    foreach($_POST['tipo'] as $tipo){
+        $profesores_carrera = query("SELECT profesor_id FROM fs_profesor_carrera(:carrera_id)", [':carrera_id' => $tipo], false);
+        foreach($profesores_carrera as $profesor){
+            array_push($profesores, $profesor['profesor_id']);
+        }
+
+    }
+}
+
+
+foreach($_POST['usuario'] as $profesor){
+    array_push($profesores, $profesor);
+}
+$sql = "SELECT fi_aviso_profesor(:aviso_id, :profesor_id)";
+foreach($profesores as $profesor_id){
+    $params = [':aviso_id' => $_POST['aviso_id'], ':profesor_id' => $profesor_id];
+    query($sql, $params, false);
+}
+
+header("Location: ../avisos.php");
+exit();
+?>

+ 24 - 0
action/action_carreras.php

@@ -0,0 +1,24 @@
+<?php
+header('Content-Type: application/json');
+
+$ruta = "../";
+require_once "../class/c_login.php";
+
+// check if the session is started
+if (!isset($_SESSION['user']))
+    die(json_encode(['error' => 'No se ha iniciado sesión']));
+
+$user = unserialize($_SESSION['user']);
+
+$ruta = "../";
+require_once "../include/bd_pdo.php";
+
+$nivel = $db->where("id", $_POST['periodo'])->getOne("fs_periodo", "nivel_id");
+$carreras = $db
+    ->where("nivel", $nivel)
+    ->where("facultad", $_POST['facultad'])
+    ->get("fs_carrera", null, "id, carrera");
+
+$user->print_to_log("Crea carrera", old: $_POST);
+
+die(json_encode($carreras));

+ 19 - 0
action/action_carreras_insert.php

@@ -0,0 +1,19 @@
+<?php
+$ruta = "../";
+require_once "../class/c_login.php";
+
+// check if the session is started
+if (!isset($_SESSION['user']))
+    die(json_encode(['error' => 'No se ha iniciado sesión']));
+
+$user = unserialize($_SESSION['user']);
+$ruta = "../";
+require_once "../include/bd_pdo.php";
+$sql = "SELECT fi_carrera(:nombre, :idfacultad, :idnivel, true, :estado)";
+$params = [':nombre' => mb_strtoupper($_POST['nombre']), ':idfacultad' => $_POST['facultad'], ':idnivel' => $_POST['nivel'], ':estado' => $_POST['estado']];
+
+print_r($_POST);
+echo json_encode(query($sql, $params, true));
+$user->print_to_log("Crea carrera", new: $params);
+header("Location: ../carreras.php?facultad=" . $_POST['facultad']);
+exit();

+ 16 - 0
action/action_carreras_select.php

@@ -0,0 +1,16 @@
+<?php
+$ruta = "../";
+require_once "../class/c_login.php";
+
+// check if the session is started
+if (!isset($_SESSION['user']))
+    die(json_encode(['error' => 'No se ha iniciado sesión']));
+
+$user = unserialize($_SESSION['user']);
+$ruta = "../";
+require_once "../include/bd_pdo.php";
+global $pdo;
+$sql = "SELECT * FROM  fs_carreras(:idfacultad, :idcarrera, null)";
+$params = [':idfacultad' => $_POST['idfacultad'], ':idcarrera' => $_POST['idcarrera']];
+$user->print_to_log("Crea carrera", old: $params);
+echo json_encode(query($sql, $params, true));

+ 19 - 0
action/action_carreras_update.php

@@ -0,0 +1,19 @@
+<?php
+$ruta = "../";
+require_once "../class/c_login.php";
+
+// check if the session is started
+if (!isset($_SESSION['user']))
+    die(json_encode(['error' => 'No se ha iniciado sesión']));
+
+$user = unserialize($_SESSION['user']);
+$ruta = "../";
+require_once "../include/bd_pdo.php";
+$old = query("SELECT * FROM FS_CARRERA WHERE ID = :id", [':id' => $_POST['id']]);
+$sql = "SELECT fu_updatecarrera(:idcarrera, :nombre, :activa, :idnivel)";
+print_r($_POST);
+$params = [':idcarrera' => $_POST['id'], ':nombre' => mb_strtoupper($_POST['nombre']), ':activa' => $_POST['estado'], ':idnivel' => $_POST['nivel']];
+query($sql, $params, true);
+$user->print_to_log("Actualiza carrera.", old: $old, new: $params);
+header("Location: ../carreras.php?facultad=" . $_POST['facultad']);
+exit();

+ 15 - 0
action/action_diasfestivos_borra.php

@@ -0,0 +1,15 @@
+<?php
+$ruta = "../";
+require_once "../class/c_login.php";
+
+// check if the session is started
+if (!isset($_SESSION['user']))
+    die(json_encode(['error' => 'No se ha iniciado sesión']));
+
+$user = unserialize($_SESSION['user']);
+$ruta = "../";
+require_once "../include/bd_pdo.php";
+#$sql = "SELECT * FROM diasfestivos WHERE diasfestivos_id = :id";
+$sql = "DELETE FROM diasfestivos WHERE diasfestivos_id = :id";
+$params = [':id' => $_POST['id']];
+echo json_encode(query($sql, $params, false));

+ 48 - 0
action/action_diasfestivos_insert.php

@@ -0,0 +1,48 @@
+<?php
+$ruta = "../";
+require_once "../class/c_login.php";
+
+// check if the session is started
+if (!isset($_SESSION['user']))
+    die(json_encode(['error' => 'No se ha iniciado sesión']));
+
+$user = unserialize($_SESSION['user']);
+$ruta = "../";
+require_once "../include/bd_pdo.php";
+global $pdo;
+print_r($_POST);
+if ($_POST['periodo'] == 0) {
+    $periodo = null;
+} else {
+    $periodo = $_POST['periodo'];
+}
+if (isset($_POST['rango'])) {
+    $diaInicio  = new DateTime(date("Y-m-d", strtotime(str_replace("/", "-", $_POST['diaFestivo']))));
+    $diaFin = new DateTime(date("Y-m-d", strtotime(str_replace("/", "-", $_POST['diaFestivoFin']))));
+    $cantidad = $diaFin->diff($diaInicio);
+    $date = date("Y-m-d", strtotime(str_replace("/", "-", $_POST['diaFestivo'])));
+    for ($dias = 0; $dias <= $cantidad->days; $dias++) {
+        $sql = "SELECT fi_diasfestivos(:periodo, :dia)";
+        $params = [':periodo' => $periodo, ':dia' => $date];
+        query($sql, $params, false);
+        $date = date("Y-m-d", strtotime($date . "+ 1 days"));
+    }
+    header("Location: ../días_festivos.php");
+    exit();
+} else {
+    $sql = "SELECT * FROM fs_diasfestivos(null, :dia)";
+    $params = [':dia' => $_POST['diaFestivo']];
+    $dia_general = query($sql, $params, false);
+    $sql = "SELECT * FROM fs_diasfestivos(null, null, :periodo, :dia)";
+    $params = [':periodo' => $periodo, ":dia" => $_POST['diaFestivo']];
+    $dia = query($sql, $params, false);
+    if (!$dia && !$dia_general) { //no hay repetidos
+        $sql = "SELECT fi_diasfestivos(:periodo, :dia)";
+        $id = query($sql, $params, false);
+        header("Location: ../días_festivos.php");
+        exit();
+    } else {
+        header("Location: ../días_festivos.php?error=1");
+        exit();
+    }
+}

+ 19 - 0
action/action_diasfestivos_select.php

@@ -0,0 +1,19 @@
+<?php
+$ruta = "../";
+require_once "../class/c_login.php";
+
+// check if the session is started
+if (!isset($_SESSION['user']))
+    die(json_encode(['error' => 'No se ha iniciado sesión']));
+
+$user = unserialize($_SESSION['user']);
+$ruta = "../";
+require_once "../include/bd_pdo.php";
+global $pdo;
+$params = [':id' => $_POST['id']];
+if ($_POST['periodo'] == 0) {
+    $sql = "SELECT * FROM fs_diasfestivos(:id, null)";
+} else {
+    $sql = "SELECT * FROM fs_diasfestivos(null, :id, null, null)";
+}
+echo json_encode(query($sql, $params, true));

+ 31 - 0
action/action_diasfestivos_update.php

@@ -0,0 +1,31 @@
+<?php
+$ruta = "../";
+require_once "../class/c_login.php";
+
+// check if the session is started
+if (!isset($_SESSION['user']))
+    die(json_encode(['error' => 'No se ha iniciado sesión']));
+
+$user = unserialize($_SESSION['user']);
+$ruta = "../";
+require_once "../include/bd_pdo.php";
+global $pdo;
+if ($_POST['periodo'] == 0) {
+    $periodo = null;
+} else
+    $periodo = $_POST['periodo'];
+$sql = "SELECT * FROM fs_diasfestivos(null, :dia) WHERE diasfestivos_id != :id";
+$params = [':dia' => $_POST['diaFestivo'], ':id' => $_POST['id']];
+$dia_general = query($sql, $params, false);
+$sql = "SELECT * FROM fs_diasfestivos(null, null, :periodo, :dia) WHERE diasfestivos_id != :id";
+$params = [':periodo' => $periodo, ':dia' => $_POST['diaFestivo'], ':id' => $_POST['id']];
+$dia = query($sql, $params, false);
+if (!$dia && !$dia_general) { //no hay repetidos
+    $sql = "SELECT fu_update_diasfestivos(:id, :dia, :periodo)";
+    query($sql, $params, false);
+    header("Location: ../días_festivos.php");
+    exit();
+} else { //es repetido
+    header("Location: ../días_festivos.php?error=1");
+    exit();
+}

+ 11 - 0
action/action_facultad.php

@@ -0,0 +1,11 @@
+<?php
+$ruta = "../";
+require_once "../class/c_login.php";
+
+// check if the session is started
+if (!isset($_SESSION['user']))
+    die(json_encode(['error' => 'No se ha iniciado sesión']));
+
+$user = unserialize($_SESSION['user']);
+$ruta = "../";
+require '../include/bd_pdo.php';

+ 23 - 0
action/action_facultades_insert.php

@@ -0,0 +1,23 @@
+<?php
+$ruta = "../";
+require_once "../class/c_login.php";
+
+// check if the session is started
+if (!isset($_SESSION['user']))
+    die(json_encode(['error' => 'No se ha iniciado sesión']));
+
+$user = unserialize($_SESSION['user']);
+$ruta = "../";
+require_once "../include/bd_pdo.php";
+global $pdo;
+$sql = "SELECT fi_facultad(:nombre, :activa)";
+$params = [':nombre' => mb_strtoupper($_POST['nombre']), ':activa' => $_POST['estado']];
+$fac_id = query($sql, $params, true);
+$sql = "SELECT fi_tiempo_checado(:idfacultad, :idnivel, :antes, :despues, :retardo)";
+$params = [':idfacultad' => $fac_id['fi_facultad'], ':idnivel' => 1, ':antes' => -15, ':despues' => 16, ':retardo' => 31];
+query($sql, $params, false);
+$params = [':idfacultad' => $fac_id['fi_facultad'], ':idnivel' => 2, ':antes' => -15, ':despues' => 16, ':retardo' => 31];
+query($sql, $params, false);
+print_r($fac_id);
+header("Location: ../carreras.php?facultad=" . $fac_id['fi_facultad']);
+exit();

+ 15 - 0
action/action_facultades_select.php

@@ -0,0 +1,15 @@
+<?php
+$ruta = "../";
+require_once "../class/c_login.php";
+
+// check if the session is started
+if (!isset($_SESSION['user']))
+    die(json_encode(['error' => 'No se ha iniciado sesión']));
+
+$user = unserialize($_SESSION['user']);
+$ruta = "../";
+require_once "../include/bd_pdo.php";
+global $pdo;
+$sql = "SELECT * FROM facultad WHERE facultad_id = :idFacultad";
+$params = [':idFacultad' => $_POST['id_facultad']];
+echo json_encode(query($sql, $params, false));

+ 17 - 0
action/action_facultades_update.php

@@ -0,0 +1,17 @@
+<?php
+$ruta = "../";
+require_once "../class/c_login.php";
+
+// check if the session is started
+if (!isset($_SESSION['user']))
+    die(json_encode(['error' => 'No se ha iniciado sesión']));
+
+$user = unserialize($_SESSION['user']);
+$ruta = "../";
+require_once "../include/bd_pdo.php";
+global $pdo;
+$sql = "SELECT fu_updatefacultad(:nombre, :activa, :id)";
+$params = [':nombre' => mb_strtoupper($_POST['nombre']), ':activa' => $_POST['estado'], ':id' => $_POST['id']];
+query($sql, $params, false);
+header("Location: ../facultades.php");
+exit();

+ 24 - 0
action/action_grupo.php

@@ -0,0 +1,24 @@
+<?php
+$ruta = "../";
+require_once "../class/c_login.php";
+
+// check if the session is started
+if (!isset($_SESSION['user']))
+    die(json_encode(['error' => 'No se ha iniciado sesión']));
+
+$user = unserialize($_SESSION['user']);
+$ruta = "../";
+require_once("../include/bd_pdo.php");
+extract($_POST);
+$params = ['per' => $periodo, 'fac' => $facultad, 'car' => $carrera];
+
+$user->print_to_log("Acceso a grupos", old: $params);
+$grupos = queryAll("SELECT DISTINCT LENGTH(GRUPO), GRUPO FROM fs_horario_basic WHERE PERIODO_ID = COALESCE(:per, PERIODO_ID) AND FACULTAD_ID = COALESCE(:fac, FACULTAD_ID) AND CARRERA_ID = COALESCE(:car, CARRERA_ID) ORDER BY LENGTH(GRUPO), GRUPO", $params);
+$grupos = array_map(function ($grupo) {
+    return $grupo['grupo'];
+}, $grupos);
+
+echo json_encode([
+    'status' => 'success',
+    'grupos' => $grupos
+]);

+ 35 - 0
action/action_horario.php

@@ -0,0 +1,35 @@
+<?php
+header('Content-Type: application/json');
+
+$ruta = "../";
+require_once("../include/bd_pdo.php");
+
+extract($_POST);
+
+$dias = array("domingo", "lunes", "martes", "miércoles", "jueves", "viernes", "sábado");
+$horarios = $db
+    ->get("fs_horario($periodo, $carrera, '$grupo', true)");
+
+// get each id from $horarios (might be duplicate)
+
+try {
+    $horarios = array_map(function ($horario) use ($dias, $db) {
+        $horario['profesores'] = array_map(
+            fn ($profesor) =>
+            $db->where("id", $profesor)->getOne("fs_profesor"),
+            explode(",", substr($horario['profesores'], 1, -1))
+        );
+        $horario['dia'] = $dias[$horario['dia']];
+        return $horario;
+    }, $horarios);
+} catch (Exception $e) {
+    die(json_encode([
+        "status" => "error",
+        "message" => $e->getMessage(),
+    ]));
+}
+?>
+<?= json_encode([
+    "status" => "success",
+    "horario" => $horarios,
+]) ?>

+ 41 - 0
action/action_horario_create.php

@@ -0,0 +1,41 @@
+<?php
+$ruta = "../";
+require_once "../class/c_login.php";
+
+// check if the session is started
+if (!isset($_SESSION['user']))
+	die(json_encode(['error' => 'No se ha iniciado sesión']));
+
+$user = unserialize($_SESSION['user']);
+$ruta = "../";
+require_once "../include/bd_pdo.php";
+
+extract($_POST);
+
+$params = [
+	"hora" => $hora,
+	"salon" => $salón,
+	"facultad_id" => $facultad,
+	"periodo" => $periodo,
+	"grupo" => $grupo,
+	"materia_id" => $materia,
+	"dia" => $día,
+	"duracion" => $duración,
+	"profesores" => "{{$profesores}}",
+];
+
+header("Content-Type: application/json");
+$user->print_to_log("Creación de horario", new: $params);
+
+try {
+	$db->insert("fs_horario", $params);
+} catch (Exception $e) {
+	die(json_encode([
+		"status" => "error",
+		"message" => "No se pudo crear el horario",
+	]));
+}
+die(json_encode([
+	"status" => "success",
+	"message" => "Horario creado correctamente",
+]));

+ 38 - 0
action/action_horario_delete.php

@@ -0,0 +1,38 @@
+<?php
+$ruta = "../";
+require_once "../class/c_login.php";
+
+// check if the session is started
+if (!isset($_SESSION['user']))
+	die(json_encode(['error' => 'No se ha iniciado sesión']));
+
+$user = unserialize($_SESSION['user']);
+
+extract($_POST);
+try {
+    $old = $db
+        ->where('horario_id', $id)
+        ->getOne('horario');
+
+    $user->print_to_log("Eliminación de horario", old: $old);
+    
+    $horario = $db
+        ->where('id', $id)
+        ->delete('fs_horario');
+} catch (Exception $e) {
+    // if message contains "Integrity constraint violation"
+    $message = (strpos($e->getMessage(), 'Foreign') !== false)
+                ? "No se puede eliminar el registro, tiene datos asociados"
+                : "Error al eliminar el registro";
+
+    die(json_encode([
+        "status" => "error",
+        "message" => $message,
+        "response" => $e->getMessage(),
+    ]));
+}
+
+die(json_encode([ 
+    "status" => "success",
+    "message" => "Horario eliminado correctamente",
+]));

+ 51 - 0
action/action_horario_excel.php

@@ -0,0 +1,51 @@
+<?php
+$ruta = "../";
+require_once "../include/bd_pdo.php";
+require_once "../include/func_excel.php";
+
+extract($_POST);
+
+# $carrera;
+# $facultad;
+
+$horarios = json_decode($data, true);
+
+// make sure profesores are in the database
+foreach ($horarios as $horario) {
+    $params = [
+        'materia' => $horario['materia'],
+        'carrera' => $carrera,
+    ];
+    $horario['materia'] = query("SELECT FI_MATERIA(:materia, :carrera) id", $params)['id'];
+
+    $params = [
+        'clave' => $horario['clave'],
+        'nombre' => $horario['nombre'],
+        'correo' => $horario['correo'],
+        'grado' => $horario['grado'],
+        'facultad' => $facultad,
+    ];
+
+    $horario['profesor'] = query("SELECT FI_PROFESOR(:nombre, :clave, :facultad, :correo, :grado) id", $params)['id'];
+    $horario = array_diff_key($horario, array_flip(['clave', 'nombre', 'correo', 'grado', '']));
+    $horario['periodo'] = $periodo;
+    $horario['facultad'] = $facultad;
+    
+    try {
+        query(
+            "SELECT FI_HORARIO(:horario::VARCHAR, :profesor::INT, :materia::INT, :facultad::INT, :periodo::INT, :grupo::VARCHAR, :salon::VARCHAR)",
+            $horario
+        );
+    } catch (Exception $e) {
+        die(json_encode([
+            "status" => "error",
+            "sql" => $e->getMessage(),
+            "message" => "Error al cargar el archivo",
+        ]));
+    }
+}
+?>
+<?= json_encode([
+    "status" => "success",
+    "message" => "Horarios guardado con éxito",
+]) ?>

+ 4 - 0
action/action_horario_profesor.php

@@ -0,0 +1,4 @@
+<?php
+die(json_encode([
+    'message' => 'ok',
+]));

+ 48 - 0
action/action_horario_update.php

@@ -0,0 +1,48 @@
+<?php
+header('Content-Type: application/json');
+$ruta = "../";
+require_once "../class/c_login.php";
+
+// check if the session is started
+if (!isset($_SESSION['user']))
+    die(json_encode(['error' => 'No se ha iniciado sesión']));
+
+$user = unserialize($_SESSION['user']);
+
+$horario = array_map(fn ($value) => $_POST[$value], array_filter([
+    "hora" => "hora",
+    "dia" => "día",
+    "salon" => "salón",
+    "duracion" => "duración",
+], fn ($value) => !empty($_POST[$value])));
+
+if (!empty($_POST['profesores']))
+    $horario["profesores"] = "{ {$_POST['profesores']} }";
+
+try {
+    $id = $_POST['id'] ?? 0;
+
+    $old = $db
+        ->where("horario_id", $id)
+        ->getOne("horario");
+    
+    $horario = $db
+        ->where("id", $id)
+        ->update("fs_horario", $horario);
+
+    $new = $db
+        ->orderBy("horario_id", "DESC")
+        ->getOne("horario");
+        
+    $user->print_to_log("Actualización de horario", old: $old, new: $new);
+} catch (Exception $e) {
+    die(json_encode([
+        "status" => "error",
+        "message" => $e->getMessage(),
+        'POST' => $_POST,
+    ]));
+}
+
+die(json_encode([
+    "status" => "success",
+]));

+ 33 - 0
action/action_justificar.php

@@ -0,0 +1,33 @@
+<?php
+$ruta = "../";
+require_once "../class/c_login.php";
+
+extract($_POST);
+
+/* print_r($claves);
+exit; */
+
+$fecha = DateTime::createFromFormat('d/m/Y', $fecha);
+
+if (isset($hora)) {
+    $claves = [[
+        'clave' => $clave,
+        'hora' => $hora,
+        'id' => $id
+    ]];
+}
+foreach ($claves as $horario)
+    try {
+        $profesor_id = $horario["clave"];
+        query("SELECT fi_registrar_asistencia(:id::INT, FALSE, ARRAY[ NOW(), :fecha::DATE + :hora::TIME ]::TIMESTAMP[], :profesor_id::INT, TRUE)", [
+            ":fecha" => $fecha->format('Y-m-d'),
+            ":hora" => $horario["hora"],
+            ":id" => $horario["id"],
+            ":profesor_id" => $profesor_id
+        ]);
+    }
+    catch (Exception $e) {
+        die( json_encode(["error" => $e->getMessage()]) );
+    }
+
+die(json_encode(["success" => true]));

+ 40 - 0
action/action_login.php

@@ -0,0 +1,40 @@
+    <?php
+    /* 
+    * Valida usuario con la BD y devuelve contraseña para validar con PHP
+    * 
+    * Recibe:
+    *  POST: usuario, password
+    * 
+    * Error:
+    *  0 - No se recibieron datos
+    *  1 - Usuario/Contraseña incorrectos
+    *  2 - Usuario no esta en BD
+    *  3 - No existe usuario
+    * 
+    * Success:
+    *  Redirecciona a inicio.php
+    */
+    include_once("../include/nocache.php"); //continue on error
+    $ruta = "../";
+    require_once("../include/bd_pdo.php"); //die on error
+    require_once("../class/c_login.php");
+    require_once("../include/util.php");
+    require_once("../include/nusoap/nusoap.php");
+
+    if (!isset($_POST["username"]) || !isset($_POST["passwd"]))
+        die(header("Location: ../index.php?error=0"));
+
+    $usr = trim(filter_input(INPUT_POST, "username")); //limpia texto
+    $pass = $_POST["passwd"];
+
+    $user =  Login::validUser($usr, $pass);
+
+    if ($user === false) {
+        $_SESSION['error'] = true;
+        header("Location: ../");
+    } else {
+        $_SESSION['user'] = serialize($user);
+        header("Location: ../main.php");
+    }
+
+    exit;

+ 26 - 0
action/action_materias.php

@@ -0,0 +1,26 @@
+<?php
+$ruta = "../";
+require_once "../class/c_login.php";
+
+extract($_POST);
+# print_r($_POST); exit;
+
+if (!isset($_SESSION['user']))
+    die(header('Location: index.php'));
+
+$user = unserialize($_SESSION['user']);
+
+if (!$user->admin && ($access = $user->access('asistencia')) == 'n')
+    die(json_encode(['error' => true]));
+
+$user->print_to_log('Consultar materias');
+$materias = queryAll(
+    "SELECT id, nombre FROM FS_MATERIA WHERE carrera = COALESCE(:carrera, carrera) ORDER BY nombre",
+    [':carrera' => empty($carrera) ? null : $carrera]
+);
+?>
+
+<?= json_encode([
+    'status' => 'success',
+    'materias' => $materias,
+]); ?>

+ 8 - 0
action/action_materias_select.php

@@ -0,0 +1,8 @@
+<?php
+    $ruta = "../";
+    require_once "../include/bd_pdo.php";
+    global $pdo;
+    $sql="SELECT * FROM materia WHERE materia_id = :idMateria";
+    $params = ['idMateria' => $_POST['idmateria']];
+    echo json_encode(query($sql, $params, false));
+?>

+ 11 - 0
action/action_materias_update.php

@@ -0,0 +1,11 @@
+<?php
+    $ruta = "../";
+    require_once "../include/bd_pdo.php";
+    global $pdo;
+
+    $sql = "UPDATE materia SET materia_nombre = :nombre WHERE materia_id = :id";
+    $params = array(':nombre' => mb_strtoupper($_POST["nombre"]), ':id' => $_POST["id"]);
+    $hecho = query($sql, $params, false);
+    header("Location: ../materias.php");
+    exit();
+?>

+ 17 - 0
action/action_new_horario.php

@@ -0,0 +1,17 @@
+<!-- fi_horario(
+	p_hora character varying,
+	p_materia character varying,
+	p_clave character varying,
+	p_nombre character varying,
+	p_grado character varying,
+	p_correo character varying,
+	p_facultad integer,
+	p_carrera integer,
+	p_grupo character varying DEFAULT NULL::character varying,
+	p_salon character varying DEFAULT NULL::character varying) -->
+
+<?php
+$ruta = "../";
+require_once "../include/bd_pdo.php";
+
+$sql = "SELECT fi_horario(:hora, :materia, :clave, :nombre, :grado, :correo, :facultad, :carrera, :grupo, :salon)";

+ 17 - 0
action/action_periodos_insert.php

@@ -0,0 +1,17 @@
+<?php
+    $ruta = "../";
+    require_once "../include/bd_pdo.php";
+    global $pdo;
+    $sql = "SELECT fi_periodo(:fecha_inicio, :fecha_fin, :estado, :nombre, :nivel, :facultad)";
+    $params = [
+        ':fecha_inicio' => $_POST['fecha_inicial'],
+        ':fecha_fin' => $_POST['fecha_final'],
+        ':estado' => $_POST['estadoP'],
+        ':nombre' => mb_strtoupper($_POST['nombreP']),
+        ':nivel' => $_POST['nivelP'],
+        ':facultad' => $_POST['facultadP']
+    ];
+    echo json_encode(query($sql, $params, true));
+    header("Location: ../carreras.php?facultad=".$_POST['facultadP']);
+    exit();
+?>

+ 8 - 0
action/action_periodos_select.php

@@ -0,0 +1,8 @@
+<?php
+    $ruta = "../";
+    require_once "../include/bd_pdo.php";
+    global $pdo;
+    $sql = "SELECT * FROM fs_periodo WHERE facultad_id = :idfacultad AND id = :idperiodo";
+    $params = [':idfacultad' => $_POST['idfacultad'], ':idperiodo' => $_POST['idperiodo']];
+    echo json_encode(query($sql, $params, true));
+?>

+ 18 - 0
action/action_periodos_update.php

@@ -0,0 +1,18 @@
+<?php
+    $ruta = "../";
+    require_once "../include/bd_pdo.php";
+    global $pdo;
+    print_r($_POST);
+    $sql = "SELECT fu_update_periodo(:periodo_id, :fecha_inicio, :fecha_final, :estado, :nombre, :nivel)";
+    $params = [
+        ':periodo_id' => $_POST['idP'],
+        ':fecha_inicio' => $_POST['fecha_inicial'],
+        ':fecha_final' => $_POST['fecha_final'],
+        ':estado' => $_POST['estadoP'],
+        ':nombre' => mb_strtoupper($_POST['nombreP']),
+        ':nivel' => $_POST['nivelP']
+    ];
+    echo json_encode(query($sql, $params, true));
+    header("Location: ../carreras.php?facultad=".$_POST['facultadP']);
+    exit();
+?>

+ 27 - 0
action/action_periodousuario_update.php

@@ -0,0 +1,27 @@
+<?php
+$ruta = "../";
+require_once "../class/c_login.php";
+
+if (!isset($_SESSION['user'])) {
+    header('Location: index.php');
+    exit;
+} else
+    $user = unserialize($_SESSION['user']);
+
+$params = array(':id' => $user->user['id'], ':per' => $_POST['id']);
+$user->print_to_log('Actualizando periodo from ' . $user->periodo . ' to ' . $_POST['id']);
+
+query("SELECT FU_UPDATEPERIODO(:id, :per)", $params);
+$user->periodo = $params[':per'];
+
+# if the user is admin, also update the facultad in user object
+if ($user->admin) {
+    $facultad = query("SELECT FACULTAD_ID id, FACULTAD f FROM FS_PERIODO WHERE ID = :id", [':id' => $user->periodo]);
+    $user->facultad = array(
+        'facultad_id' => $facultad["id"],
+        'facultad' => $facultad["f"],
+    );
+}
+
+$_SESSION['user'] = serialize($user);
+header("Location: {$_POST["target"]}");

+ 40 - 0
action/action_permisos_update.php

@@ -0,0 +1,40 @@
+<?php
+    $ruta = "../";
+    require_once "../include/bd_pdo.php";
+    global $pdo;
+    if(isset($_POST['lectura']))
+        $ver = $_POST['lectura'];
+    if(isset($_POST['editar']))
+        $editar = $_POST['editar'];
+    foreach($editar as $edit){
+        $edit_separado = explode("_", $edit);
+        $completo[]=$edit_separado;
+    }
+    #echo "<br><br><br><br>";
+    #print_r($ver);
+    #print_r($editar);
+    query("SELECT fd_permiso()", null, false);
+    foreach($ver as $lectura){
+        $igual=false;
+        $ver_separado = explode("_", $lectura);
+        #print_r($ver_separado);
+        foreach($completo as $comp){
+            if($ver_separado[0] == $comp[0] && $ver_separado[1] == $comp[1]){
+                #echo " igual";
+                $igual=true;
+                break;
+            }
+        }
+        #echo "<br>";
+        if(!$igual)
+            $completo[]=$ver_separado;
+    }
+    #print_r($completo);
+    foreach($completo as $actual){
+        $sql = "SELECT fi_permiso(:pagina, :rol, :tipo)";
+        $params = [':pagina' => $actual['0'], ':rol' => $actual['1'], ':tipo' => $actual['2']];
+        query($sql, $params, false);
+    }
+    header("Location: ../permisos.php");
+    exit();
+?>

+ 14 - 0
action/action_profesor.php

@@ -0,0 +1,14 @@
+<?php
+$ruta = "../";
+require_once("../include/bd_pdo.php");
+
+extract($_GET);
+
+$profesores = $db
+    ->where("facultad_id", $facultad ?? 0)
+    ->get("fs_profesor");
+
+echo json_encode([
+    "status" => "success",
+    "profesores" => $profesores
+]);

+ 40 - 0
action/action_profesor_faltas.php

@@ -0,0 +1,40 @@
+<?php
+$ruta = "../";
+require_once "../include/bd_pdo.php";
+
+// die(print_r($_POST, true));
+
+extract($_POST);
+// if hora fin is null, then subtract half an hour from hora inicio and set hora fin to hora inicio + half an hour
+$hora_fin = empty($hora_fin) ? $hora_inicio : $hora_fin;
+
+$hora_inicio = date('H:i:s', strtotime($hora_inicio < '07:00' ? '07:00' : $hora_inicio) - 1800);
+$hora_fin = date('H:i:s', strtotime($hora_fin > '22:00' ? '22:00' : $hora_fin) + 1800);
+
+die(json_encode(
+    array_map(fn ($row) => array_merge(
+        $db->where('id', $row['profesor_id'])->getOne('fs_profesor'),
+        $db->where('id', $row['materia_id'])->getOne('fs_materia'),
+        $row
+    ),
+    queryAll(   
+    "SELECT REPORTE.*
+        FROM fs_asistencia_profesorreporte(null, :periodo, null, :fecha, :fecha) AS REPORTE
+        JOIN PROFESOR P ON P.PROFESOR_ID = REPORTE.PROFESOR_ID
+        WHERE HORA_CHECADO IS NULL
+        AND HORA BETWEEN :inicio AND :fin
+        AND P.PROFESOR_CLAVE ILIKE COALESCE(:clave, P.PROFESOR_CLAVE) and UNACCENT(P.PROFESOR_NOMBRE) ILIKE UNACCENT(COALESCE(:nombre, P.PROFESOR_NOMBRE))
+        AND FECHA = :fecha
+    ORDER BY HORA, MATERIA",
+    [
+        'periodo' => $periodo,
+        'fecha' => $fecha,
+        'inicio' => $hora_inicio,
+        'fin' => $hora_fin,
+        'clave' => empty($clave) ? null : "%$clave%",
+        'nombre' => empty($nombre) ? null : "%$nombre%"
+    ]
+))));
+
+
+#ECHO "$hora_inicio - $hora_fin";

+ 7 - 0
action/action_profesores_borra.php

@@ -0,0 +1,7 @@
+<?php
+    $ruta = "../";
+    require_once "../include/bd_pdo.php";
+    $sql = "SELECT fu_estado_facultad_profesor(:idprofesor, :idfacultad, :estado)";
+    $params = [':idprofesor' => $_POST['id_profesor'], ':idfacultad' => $_POST['id_facultad'], ':estado' => $_POST['estado']];
+    echo json_encode(query($sql, $params, false));
+?>

+ 75 - 0
action/action_profesores_insert.php

@@ -0,0 +1,75 @@
+<?php
+    $ruta = "../";
+    require_once "../include/bd_pdo.php";
+    $id = trim(filter_input(INPUT_POST, "id", FILTER_SANITIZE_STRING,array('flags' => FILTER_FLAG_STRIP_LOW)));
+    if(isset($_POST["dlfacultad"]))
+        $facultad = trim(filter_input(INPUT_POST, "dlfacultad", FILTER_SANITIZE_STRING,array('flags' => FILTER_FLAG_STRIP_LOW)));
+    else
+        $facultad = trim(filter_input(INPUT_POST, "mfacultad", FILTER_SANITIZE_STRING,array('flags' => FILTER_FLAG_STRIP_LOW)));
+    $clave = trim(filter_input(INPUT_POST, "mclave", FILTER_SANITIZE_STRING,array('flags' => FILTER_FLAG_STRIP_LOW)));
+    $grado = trim(filter_input(INPUT_POST, "grado", FILTER_SANITIZE_STRING,array('flags' => FILTER_FLAG_STRIP_LOW)));
+    $nombre = trim(filter_input(INPUT_POST, "nombre", FILTER_SANITIZE_STRING,array('flags' => FILTER_FLAG_STRIP_LOW)));
+    $grado = mb_strtoupper($grado);
+    if(!empty($grado)){
+        if(!ctype_space($grado)){
+            if($grado[strlen($grado)-1] != '.')
+                $grado.='.';
+        }
+        else{
+            $grado="";
+        }
+    }
+    $fs_profesores = query(//revisar si existe la clave del profesor
+        "SELECT * FROM fs_profesor WHERE clave = :clave",
+        array(":clave" => $_POST["mclave"]),
+        true
+    );
+    if(!$fs_profesores){//hay que crearlo desde 0 (profesor) y agregarlo a su facultad(facultad_profesor)
+        $profesor_id = query(
+            "SELECT public.fi_profesor(
+                :nombre, 
+                :clave, 
+                :facultad, 
+                null, 
+                :grado
+            )",
+            array(":nombre" => mb_strtoupper($nombre), ":clave" => $clave, ":facultad" => $facultad, ":grado" => $grado),
+            true
+        );
+        header("Location: ../profesores.php");
+        exit();
+    }
+    else{//el profesor ya existe
+        $profac = query(
+            "SELECT * FROM facultad_profesor WHERE facultad_id = :facultad AND profesor_id = :profesor",
+            array(":facultad" => $facultad, ":profesor" => $fs_profesores["id"]),
+            true
+        );
+        if(!$profac){//agregarlo a la facultad (facultad_profesor)
+            query(
+                "SELECT fi_facultad_profesor(
+                    :facultad, 
+                    :profesor
+                )",
+                array(":facultad" => $facultad, ":profesor" => $fs_profesores["id"]),
+                true
+            );
+            header("Location: ../profesores.php");
+            exit();
+        }
+        else{//regresar error (ya existe este profesor en esta facultad)
+            //print_r($profac);
+            if(!$profac['fp_activo']){
+                query(
+                    "SELECT fu_estado_facultad_profesor(:idprofesor, :idfacultad, :estado)",
+                    array(":idprofesor" => $fs_profesores["id"], ":idfacultad" => $facultad, ":estado" => true),
+                    true
+                );
+                header("Location: ../profesores.php");
+                exit();
+            }
+            header("Location: ../profesores.php?error=1");
+            #exit();
+        }
+    }
+?>

+ 10 - 0
action/action_profesores_select.php

@@ -0,0 +1,10 @@
+<?php
+$ruta = "../";
+require_once "../include/bd_pdo.php";
+
+global $pdo;
+
+$sql = "SELECT * FROM profesor WHERE profesor_id = :idProfesor";
+$params = [':idProfesor' => $_POST['profesor']];
+
+echo json_encode(query($sql, $params, false));

+ 36 - 0
action/action_profesores_update.php

@@ -0,0 +1,36 @@
+<?php
+    $ruta = "../";
+    require_once "../include/bd_pdo.php";
+    global $pdo;
+
+    $profesor = query(
+        "SELECT * FROM profesor WHERE :clave = profesor_clave",
+        array(":clave" => $_POST["mclave"]),
+        true
+    );
+    if($profesor){
+        if($profesor['profesor_id'] != $_POST['id']){
+            echo "clave en uso";
+            header("Location: ../profesores.php?error=2");
+            exit();
+        }
+    }
+    $grado = $_POST['grado'];
+    $grado = mb_strtoupper($grado);
+    if(!empty($grado)){
+        if(!ctype_space($grado)){
+            if($grado[strlen($grado)-1] != '.')
+                $grado.='.';
+        }
+        else{
+            $grado="";
+        }
+    }
+    print_r($_POST);
+    $sql = "SELECT public.fu_updateprofesor(:id, :clave, :nombre, :grado)";
+    $params = array(':id' => $_POST["id"], ':clave' => $_POST["mclave"], ':nombre' => mb_strtoupper($_POST["nombre"]), ':grado' => $grado);
+    $hecho = query($sql, $params, false);
+
+    header("Location: ../profesores.php", true, 307);
+    exit();
+?>

+ 50 - 0
action/action_revisar_excel.php

@@ -0,0 +1,50 @@
+<?php
+#display PHP errors
+
+$ruta = "../";
+require_once "../include/bd_pdo.php";
+require_once "../include/func_excel.php";
+require_once "../include/func_string.php";
+
+use PhpOffice\PhpSpreadsheet\IOFactory;
+
+$reader = IOFactory::createReader("Xlsx");
+$reader->setReadDataOnly(true);
+
+$file = $_FILES['archivo'];
+
+
+$spreadsheet = $reader->load($file['tmp_name'][0]);
+
+$data = [];
+
+try {
+
+    foreach_sheet(
+        $spreadsheet, // object $spreadsheet
+        function (array $row_data, int $i, string $sheet) {
+            global $horario, $data;
+
+            if (renglón_vacío($row_data)) return;
+            validar_registro($row_data, $i);
+
+            $horario["horario"] = horario($row_data, $i, $sheet);
+
+            foreach (array_filter($row_data) as $key => $value)
+                $horario = array_merge($horario, ($key == 'maestro') ? columna_nombre($value) : [$key => $value]);
+
+            $data[] = $horario;
+        }
+    );
+
+    die(json_encode([
+        "status" => "success",
+        "message" => "Horario revisado con éxito, se leyeron: " . count($data) . " registros",
+        "data" => $data
+    ]));
+} catch (Exception $e) {
+    die(json_encode([
+        "status" => "error",
+        "message" => $e->getMessage(),
+    ]));
+}

+ 11 - 0
action/action_roles_insert.php

@@ -0,0 +1,11 @@
+<?php
+    $ruta = "../";
+    require_once "../include/bd_pdo.php";
+    global $pdo;
+    print_r($_POST);
+    $sql = "INSERT INTO rol (rol_titulo) VALUES (:nombre)";
+    $params = [':nombre' => mb_strtoupper($_POST['mtitulo'])];
+    $hecho = query($sql, $params, true);
+    header("Location: ../roles.php");
+    exit();
+?>

+ 11 - 0
action/action_roles_select.php

@@ -0,0 +1,11 @@
+<?php
+    $ruta = "../";
+    require_once "../include/bd_pdo.php";
+
+    global $pdo;
+
+    $sql = "SELECT * FROM rol WHERE rol_id = :idRol";
+    $params = [':idRol' => $_POST['rol']];
+    
+    echo json_encode( query($sql, $params, true));
+?>

+ 11 - 0
action/action_roles_update.php

@@ -0,0 +1,11 @@
+<?php
+    $ruta = "../";
+    require_once "../include/bd_pdo.php";
+    global $pdo;
+    $sql = "UPDATE rol SET rol_titulo = :nombre WHERE rol_id = :id";
+    $params = array(':nombre' => mb_strtoupper($_POST['mtitulo']), ':id' => $_POST['id']);
+    print_r($_POST);
+    $hecho = query($sql, $params, false);
+    header("Location: ../roles.php", true, 307);
+    exit();
+?>

+ 37 - 0
action/action_tiempos_update.php

@@ -0,0 +1,37 @@
+<?php
+$ruta = "../";
+require_once "../include/bd_pdo.php";
+print_r($_POST);
+
+$fs_tiempo = query(
+    "SELECT * FROM fs_tiempo_checado(:facultad, 1)", [':facultad' => $_POST['facultadT']], true
+);
+
+if($fs_tiempo){
+    $sql = "SELECT fu_update_tiempo_checado(:idfacultad, :idnivel, :antes, :despues, :retardo)";
+    $params = [':idfacultad' => $_POST['facultadT'], ':idnivel' => 1, ':antes' => -1*$_POST['antesL'], ':despues' => $_POST['despuesL']+1, ':retardo' => $_POST['retardoL']+$_POST['despuesL']+1];
+}
+else{
+    $sql = "SELECT fi_tiempo_checado(:idfacultad, :idnivel, :antes, :despues, :retardo)";
+    $params = [':idfacultad' => $_POST['facultadT'], ':idnivel' => 1, ':antes' => -1*$_POST['antesL'], ':despues' => $_POST['despuesL']+1, ':retardo' => $_POST['retardoL']+$_POST['despuesL']+1];
+}
+query($sql, $params, false);
+
+
+$fs_tiempo2 = query(
+    "SELECT * FROM fs_tiempo_checado(:facultad, 2)", [':facultad' => $_POST['facultadT']], true
+);
+
+if($fs_tiempo2){
+    $sql = "SELECT fu_update_tiempo_checado(:idfacultad, :idnivel, :antes, :despues, :retardo)";
+    $params = [':idfacultad' => $_POST['facultadT'], ':idnivel' => 2, ':antes' => -1*$_POST['antesP'], ':despues' => $_POST['despuesP']+1, ':retardo' => $_POST['retardoP']+$_POST['despuesP']+1];
+}
+else{
+    $sql = "SELECT fi_tiempo_checado(:idfacultad, :idnivel, :antes, :despues, :retardo)";
+    $params = [':idfacultad' => $_POST['facultadT'], ':idnivel' => 2, ':antes' => -1*$_POST['antesP'], ':despues' => $_POST['despuesP']+1, ':retardo' => $_POST['retardoP']+$_POST['despuesP']+1];
+}
+query($sql, $params, false);
+
+header("Location: ../carreras.php?facultad=".$_POST['facultadT']);
+exit();
+?>

+ 22 - 0
action/action_usuario.php

@@ -0,0 +1,22 @@
+<?php
+$ruta = "../";
+require_once '../class/c_login.php';
+
+// print_r($_POST); exit;
+
+if (($user = Login::validUser($_POST['username'], $_POST['passwd'])) === false) {
+    echo json_encode("error");
+    exit;
+}
+
+$facultades = queryAll("SELECT DISTINCT ID, FACULTAD FROM FS_FACULTAD WHERE ACTIVA");
+
+for ($i = 0; $i < count($facultades); $i++) {
+    # print_r($facultades[$i]);
+    $facultades[$i]['usuarios'] = queryAll(
+        "SELECT ID, USERNAME FROM FS_USUARIO WHERE facultad = :facultad",
+        array(":facultad" => $facultades[$i]["id"])
+    );
+}
+
+echo json_encode($facultades);

+ 22 - 0
action/action_usuarios_insert.php

@@ -0,0 +1,22 @@
+<?php
+    $ruta = "../";
+    require_once "../include/bd_pdo.php";
+    global $pdo;
+    if(isset($_POST['dlfacultad']))
+        $facultad=$_POST['dlfacultad'];
+    else
+        $facultad=$_POST['mfacultad'];
+
+    $hecho = query("SELECT * FROM fs_usuario WHERE clave = :clave", [':clave' => $_POST['mclave']], true);
+    if(!$hecho){
+        $sql = "SELECT fi_usuario(:nombre, :correo, :clave, :rol, :facultad)";
+        $params = [':nombre' => mb_strtoupper($_POST['mnombre']), ':correo' => $_POST['mcorreo'], ':clave' => $_POST['mclave'], ':rol' => $_POST['mrol'], ':facultad' => $facultad];
+        $hecho = query($sql, $params, true);
+        header("Location: ../usuarios.php", true, 307);
+        exit();
+    }
+    else{
+        header("Location: ../usuarios.php?error=1");
+        exit();
+    }    
+?>

+ 8 - 0
action/action_usuarios_select.php

@@ -0,0 +1,8 @@
+<?php
+    $ruta = "../";
+    require_once "../include/bd_pdo.php";
+    global $pdo;
+    $sql = "SELECT * FROM usuario WHERE usuario_id = :idUsuario";
+    $params = [':idUsuario' => $_POST['usuario']];
+    echo json_encode(query($sql, $params, true));
+?>

+ 15 - 0
action/action_usuarios_update.php

@@ -0,0 +1,15 @@
+<?php
+    $ruta = "../";
+    require_once "../include/bd_pdo.php";
+    global $pdo;
+    if(isset($_POST['dlfacultad']))
+        $facultad=$_POST['dlfacultad'];
+    else
+        $facultad=$_POST['mfacultad'];
+    $sql = "SELECT fu_update_usuario(:id, :nombre, :correo, :clave, :rol, :facultad)";
+    $params = array(':id' => $_POST['id'], ':nombre' => mb_strtoupper($_POST['mnombre']), ':correo' => $_POST['mcorreo'], ':clave' => $_POST['mclave'], ':rol' => $_POST['mrol'], ':facultad' => $facultad);
+    #print_r($_POST);
+    $hecho = query($sql, $params, false);
+    header("Location: ../usuarios.php", true, 307);
+    exit();
+?>

+ 10 - 0
action/carrera_find.php

@@ -0,0 +1,10 @@
+<?php
+$ruta = '../';
+require_once '../include/bd_pdo.php';
+global $pdo;
+
+$sql = "SELECT * FROM fs_carreras(:fac, null, null)";
+$params = [':fac' => $_POST['fac_id']];
+
+echo json_encode(query($sql, $params, false));
+?>

+ 37 - 0
action/force_session.php

@@ -0,0 +1,37 @@
+<?php
+$ruta = "../";
+require_once '../class/c_login.php';
+
+# print_r($_POST); exit;
+extract($_POST); // $usuario
+Login::log_out();
+
+$user = query("SELECT * FROM FS_USUARIO WHERE ID = :id", [":id" => $usuario]);
+// die(json_encode($user));
+
+$facultad = [
+    "facultad_id" => $user["facultad"],
+    "facultad" => $user["facultad_nombre"]
+];
+
+$rol = [
+    "rol_id" => $user["rol"],
+    "rol" => $user["titulo"]
+];
+
+$admin = false;
+
+$periodo = $user["periodo"];
+
+$user = [
+    "id" => $user["id"],
+    "nombre" => $user["username"]
+];
+
+$user = new Login($user, $facultad, $rol, $admin, $periodo);
+
+session_start();
+$_SESSION['user'] = serialize($user);
+
+header("Location: ../main.php");
+exit;

二进制
action/one_row.xlsx


+ 26 - 0
action/usuario_find.php

@@ -0,0 +1,26 @@
+<?php 
+$ruta = '../';
+require_once '../include/bd_pdo.php';
+global $pdo;
+
+if($_POST['nombre']==""){
+    $nombre = null;
+}else{
+    $nombre = $_POST['nombre'];
+}
+if($_POST['clave']==""){
+    $clave = null;
+}else{
+    $clave = $_POST['clave'];
+}
+if($_POST['facultad']==""){
+    $facultad = null;
+}else{
+    $facultad = $_POST['facultad'];
+}
+
+$sql = "SELECT * FROM fs_profesores(:nombre, :clave, :facultad) ORDER BY profesor_nombre";
+$params = [':nombre' => $nombre, ':clave' => $clave, ':facultad' => $facultad];
+
+echo json_encode(query($sql, $params, false));
+?>

+ 286 - 0
alta_de_horario.php

@@ -0,0 +1,286 @@
+<?php
+require_once 'class/c_login.php';
+if (!isset($_SESSION['user'])) 
+    die(header('Location: index.php'));
+    
+$user = unserialize($_SESSION['user']);
+$user->access();
+if (!$user->admin && in_array($user->acceso, ['r', 'n']))
+    die(header('Location: main.php?error=1'));
+
+$user->print_to_log('Consultar: Alta de horario');
+?>
+<!DOCTYPE html>
+<html lang="en">
+
+<head>
+    <title>Cargar horario desde Excel | <?= $user->facultad['facultad'] ?? 'General' ?></title>
+    <meta charset="utf-8">
+    <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
+    <?php include_once "import/html_css_files.php"; ?>
+</head>
+
+<body style="display: block;">
+    <?php
+    include('include/constantes.php');
+    include("import/html_header.php");
+    html_header("Cargar horario desde Excel", "Sistema de gestión de checador");
+    ?>
+    <main class="container content marco content-margin" id="local-app">
+        <div class="row mb-3">
+            <div class="col-12 text-right">
+                <button class="btn btn-outline-secondary" data-toggle="modal" data-target="#modalDescargarPlantilla">
+                    <span class="ing-descarga ing-fw"></span>
+                    Descargar plantilla
+                </button>
+            </div>
+        </div>
+        <section id="message"></section>
+        <?php require('import/periodo.php') ?>
+        <form>
+            <div class="form-group">
+                <div class="form-box">
+                    <?php
+                    $periodo = $db->where('id', $user->periodo)->getOne('fs_periodo');
+                    $carreras = $db
+                    ->where('nivel', $periodo['nivel_id'])
+                    ->where('facultad', $user->facultad['facultad_id'])
+                    ->orderBy('carrera')
+                    ->get('fs_carrera');
+                    ?>
+                    <div class="form-group row" id="input-carrera">
+
+                        <label for="filter_carrera" class="col-4 col-form-label">Carrera</label>
+                        <div class="col-6">
+                            <div id="dlcarrera" class="datalist datalist-select mb-1 w-100">
+                                <div class="datalist-input">Seleccionar carrera</div>
+                                <span class="ing-buscar icono"></span>
+                                <ul style="display:none">
+                                    <?php
+                                    foreach ($carreras as $carrera) {
+                                    ?>
+                                        <li data-id="<?= $carrera['id'] ?>">
+                                            <?= $carrera['carrera'] ?>
+                                        </li>
+                                    <?php
+                                    }
+                                    ?>
+                                </ul>
+                                <input type="hidden" id="filter_carrera" name="carrera" value="">
+                            </div>
+                        </div>
+
+                    </div>
+
+                    <div class="form-group row" id="input-file">
+                        <label for="excel" class="col-4 col-form-label">Archivo de horarios</label>
+                        <div class="col-8 col-sm-6">
+                            <input class="form-control-file" id="excel" name="archivo" accept=".xlsx, .xls" require>
+                        </div>
+                    </div>
+
+                    <div class="form-group mt-5 row justify-content-center">
+                        <!-- on click reload -->
+                        <button id="btn-cancelar" type="button" class="btn btn-danger mx-2" onclick="location.reload()">
+                            <span class="ing-cancelar"></span>
+                            Cancelar
+                        </button>
+
+                        <button id="btn-cargar" type="button" class="btn btn-primary" onclick="submit_files()">
+                            <span class="ing-guardar"></span>
+                            Guardar horario
+                        </button>
+                    </div>
+                </div>
+            </div>
+
+        </form>
+    </main>
+
+    <div class="modal fade" id="modalDescargarPlantilla" tabindex="-1" aria-labelledby="descargarPlantillaModalLabel" aria-hidden="true">
+        <div class="modal-dialog modal-xl modal-dialog-centered modal-dialog-scrollable">
+            <div class="modal-content">
+                <div class="modal-header">
+                    <h5 class="modal-title" id="descargarPlantillaModalLabel">Instrucciones</h5>
+                    <button type="button" class="close text-white" data-dismiss="modal" aria-label="Close">
+                        <span aria-hidden="true">&times;</span>
+                    </button>
+                </div>
+                <div class="modal-body">
+                    <ol>
+                        <li> Los encabezados deberán mantenerse en todo momento, tal cual se encuentran en la plantilla. </li>
+                        <li> Las horas válidas son de 7:00 a 22:00 en bloques de 15 minutos. </li>
+                        <li> La columna DURACIÓN es la duración de una clase en horas. Si la clase es de 7:15–8:45 se pone que la hora de la clase es de 7:15 con una duración de 180. </li>
+                        <!-- <li> Únicamente las columnas de … </li> -->
+                        <li> Si se encuentra un error en la revisión del archivo no se guardará ningún registro. </li>
+                        <li> Una vez revisado el archivo haz clic en el botón [Guardar horario] para que se guarde la información. </li>
+
+                    </ol>
+                </div>
+
+                <div class="modal-footer">
+                    <button type="button" class="btn btn-danger" data-dismiss="modal">Aceptar</button>
+                    <button type="button" class="btn btn-primary" id="descargarPlantilla"><span class="ing-descarga"></span> Descargar</button>
+                    <button type="button" class="btn btn-primary" id="descargarPlantillaEjemplo"><span class="ing-descarga"></span> Descargar ejemplo</button>
+                </div>
+            </div>
+        </div>
+    </div>
+
+</body>
+<?php
+require_once("import/html_footer.php");
+require_once("js/messages.php")
+?>
+<script src="js/scrollables.js"></script>
+<script src="js/jquery.min.js"></script>
+<script src="js/bootstrap/bootstrap.min.js"></script>
+
+<script src="js/custominputfile.min-es.js"></script>
+<link rel="stylesheet" href="css/custominputfile.min.css">
+<script src="js/fetchlib.js"></script>
+<script>
+    var datum = []
+
+    $(document).ready(function() {
+
+        $('#excel').customFile({
+            allowed: ['xlsx', 'xls'],
+            maxFiles: 1,
+            callbacks: {
+                onSuccess: async function(item) {
+                    var formData = $.customFile.serialize('archivo');
+
+                    const {
+                        status,
+                        message,
+                        data
+                    } = await fetch('action/action_revisar_excel.php', {
+                            method: 'POST',
+                            body: formData,
+                        })
+                        .then(response => response.json())
+                        .catch(error => {
+                            return {
+                                status: 'error',
+                                message: 'Error al cargar el archivo',
+                            }
+                        });
+
+                    if (status == 'error') {
+                        triggerMessage(message, 'Error en el formato del archivo');
+                        item.destroy();
+                        return
+                    }
+
+                    triggerMessage(message + " Haz clic en el botón <b>Guardar horario</b> para guardar los datos", `Archivo revisado, aún <b>no guardado</b>`, 'primary');
+                    datum = data;
+                    // hide form
+                    document.querySelector('#input-file').classList.add('d-none');
+                    document.querySelector('#input-carrera').classList.add('d-none');
+
+                    // show button
+                    document.querySelector('#btn-cargar').classList.remove('d-none');
+                    document.querySelector('#btn-cancelar').classList.remove('d-none');
+                },
+            }
+        });
+
+        // hide
+        document.querySelector('#input-file').classList.add('d-none');
+        document.querySelector('#btn-cancelar').classList.add('d-none');
+        document.querySelector('#btn-cargar').classList.add('d-none');
+        // on click in carrera
+        [... document.querySelectorAll('#dlcarrera ul li')].forEach(
+            li => li.addEventListener('click', () => {
+                document.querySelector('#input-file').classList.remove('d-none')
+            })
+        )
+    })
+
+    async function submit_files() {
+        // disable button
+        const button = document.querySelector('#btn-cargar');
+        // add class disabled to button
+        button.classList.add('disabled');
+        // disable button
+        button.disabled = true;
+
+        // add loading icon
+        button.innerHTML = '<span class="ing-cargando"></span> Cargando...';
+
+        let missing = [];
+
+        let carrera = $('#filter_carrera').val();
+        if (carrera == '') missing.push('Carrera');
+
+        if (datum.length == 0) missing.push('Archivo de horarios');
+
+        let facultad = <?= $user->facultad['facultad_id'] ?>;
+
+        if (missing.length > 0) {
+            messageMissingInputs(missing);
+
+            // remove class disabled to button
+            button.classList.remove('disabled');
+            // enable button
+            button.disabled = false;
+
+            // remove loading icon
+            button.innerHTML = '<span class="ing-guardar"></span> Guardar horario';
+
+            return;
+        }
+
+        const formData = new FormData();
+
+        formData.append('carrera', carrera);
+        formData.append('facultad', facultad);
+        formData.append('periodo', <?= $user->periodo ?>);
+        formData.append('data', JSON.stringify(datum));
+
+
+        const {
+            status,
+            message
+        } = await fetch('action/action_horario_excel.php', {
+                method: 'POST',
+                body: formData
+            })
+            .then(response => response.json())
+            .catch(error => {
+                return {
+                    status: 'error',
+                    message: 'Error al cargar el archivo',
+                }
+            });
+
+        triggerMessage(message, (status == 'error') ? 'Error al guardar el archivo' : 'Horarios guardados', (status == 'error') ? 'danger' : 'success');
+
+        // await 1 second
+        // await setTimeout(() => {}, 1000);
+
+        // remove class disabled to
+        button.classList.remove('disabled');
+        // enable button
+        button.disabled = false;
+        button.innerHTML = 'Cargar otro horario';
+
+        // refresh page
+        button.onclick = () => location.reload()
+
+        // hide button
+        document.querySelector('#btn-cancelar').classList.add('d-none');
+    }
+
+    document.querySelector('#descargarPlantilla').addEventListener('click', function() {
+        window.open('template/plantilla.xlsx', '_blank');
+    });
+
+    document.querySelector('#descargarPlantillaEjemplo').addEventListener('click', function() {
+        window.open('template/ejemplo.xlsx', '_blank');
+    });
+</script>
+
+
+</html>

+ 264 - 0
avisos.php

@@ -0,0 +1,264 @@
+<?php
+require_once 'class/c_login.php';
+require_once 'include/bd_pdo.php';
+
+if(!isset($_SESSION['user'])){
+    header('Location: index.php');
+    exit;
+}
+else
+    $user = unserialize($_SESSION['user']);
+$user->access();
+if(!$user->admin && $user->acceso == 'n'){
+    header('Location: main.php?error=1');
+}else{
+    $user->print_to_log('Avisos');
+}
+$fac = $user->facultad['facultad_id'];
+if($user->admin){
+    $fac = null;
+}
+$limit = 20;
+if(isset($_POST['filter_fecha']) && $_POST['filter_fecha'] != ""){
+    $filter_fecha = $_POST['filter_fecha'];
+}
+else{
+    $filter_fecha = null;
+}
+if(isset($_GET['pag'])){
+    $pag = $_GET['pag'] - 1;
+}else{
+    $pag = 0;
+}
+if($user->admin){
+    $count = query("SELECT count(1) FROM fs_aviso(null, :fecha, :facultad_id, null, 0, null)", [':fecha' => $filter_fecha, ':facultad_id' => $fac], true);
+    $fs_avisos = query("SELECT * FROM fs_aviso(null, :fecha, :facultad_id, :limite, :offset, null)", [':fecha' => $filter_fecha, ':facultad_id' => $fac, ':limite' => $limit, ':offset' => $pag * $limit], false);
+}else{
+    $count = query("SELECT count(1) FROM fs_aviso(null, :fecha, :facultad_id, null, 0, true)", [':fecha' => $filter_fecha, ':facultad_id' => $fac], true);
+    $fs_avisos = query("SELECT * FROM fs_aviso(null, :fecha, :facultad_id, :limite, :offset, true)", [':fecha' => $filter_fecha, ':facultad_id' => $fac, ':limite' => $limit, ':offset' => $pag * $limit], false);
+}
+$paginas = ceil($count['count'] / $limit);
+?>
+<!DOCTYPE html>
+<html lang="en">
+
+<head>
+    <meta charset="UTF-8">
+    <meta name="viewport" content="width=device-width, initial-scale=1.0">
+    <title>Avisos</title>
+    <link rel="stylesheet" href="css/jquery-ui.css">
+    <link rel="stylesheet" href="css/calendar.css">
+    <?php
+    include 'import/html_css_files.php';
+    ?>
+</head>
+
+<body>
+    <?php
+    include "import/html_header.php";
+    html_header(
+        "Avisos",
+        "Gestión de Checador "
+    );
+    $user->access();
+    ?>
+    <main class="content marco">
+        <?php if($user->acceso == 'w') {?>
+        <div class="row">
+            <div class="col-12 text-right">
+                <a href="avisos_crear.php">
+                    <button type="button" class="btn btn-outline-secondary"><span class="ing-mas ing-fw"></span>Crear Aviso</button>
+                </a>
+            </div>
+        </div>
+        <?php } ?>
+        <!-- Filtro -->
+        <div class="row">
+            <div class="col-12">
+                <form action="avisos.php" method="post">
+                    <div class="form-box">
+                        <div class="form-group row">
+                            <label for="filter_fecha" class="col-4 con-form-label">Fecha</label>
+                            <div class="col-8 col-sm-4">
+                                <input id="filter_fecha" name="filter_fecha" type="text" class="form-control date-picker" placeholder="dd/mm/aaaa" maxlength="10" required="required" readonly="" value="<?php if(isset($filter_fecha)){ echo $filter_fecha; } ?>">
+                            </div>
+                        </div>
+                        <p class="offset-4">Se muestran los avisos posteriores a la fecha</p>
+                    </div>
+                    <div class="form-group">
+                        <div class="col-12 text-center">
+                            <button type="submit" class="btn btn-outline-primary">
+                                <span class="ing-buscar ing-fw"></span>
+                                Filtrar
+                            </button>
+                            <button type="button" class="btn btn-outline-danger btn-reset">
+                                <span class="ing-borrar ing-fw"></span>
+                                Limpiar
+                            </button>
+                        </div>
+                    </div>
+                </form>
+            </div>
+        </div>
+        <div id="message"></div>
+        <!-- Tabla -->
+        <div class="row">
+            <div class="col-12 table-responsive">
+                <table class="table table-sm table-striped table-white">
+                    <thead class="thead-dark">
+                        <tr>
+                            <th>Estado</th>
+                            <?php if($user->acceso == 'w') {?>
+                                <th>Facultad</th>
+                                <?php }?>
+                                <th>Fechas</th>
+                                <th>Mensaje</th>
+                            <?php if($user->acceso == 'w') {?>
+                            <th>Acciones</th>
+                            <?php }?>
+                        </tr>
+                    </thead>
+                    <tbody>
+                        <?php $today = date('Y-m-j');
+                            foreach($fs_avisos as $aviso){
+                            $color2 = 'danger';
+                            $title2 = 'Inactivo';
+                            $color = 'danger';
+                            $title = 'Fuera de tiempo';
+                            $icono = 'retardo';
+                            if($today >= $aviso['aviso_fecha_inicial'] && $today <= $aviso['aviso_fecha_final']){
+                                $color = 'success';
+                                $title = 'En tiempo';
+                                $icono = 'reloj';
+                            }else if($today < $aviso['aviso_fecha_inicial']){
+                                $color = 'warning';
+                                $title = 'Antes de tiempo';
+                            }
+                            if($aviso['aviso_estado'] == true){
+                                $color2 = 'success';
+                                $title2 = 'Activo';
+                            }
+                            $day = explode("-", $aviso['aviso_fecha_inicial']);
+                            $dia_inicial = $day['2'].'/'.$day['1'].'/'.$day[0];
+                            $day = explode("-", $aviso['aviso_fecha_final']);
+                            $dia_final = $day['2'].'/'.$day['1'].'/'.$day[0];
+                            ?>
+                            <tr data-id="<?= $aviso['aviso_id'] ?>" id="<?= $aviso['aviso_id'] ?>">
+                                <td class="text-center"><?php if($user->admin){ ?> <span class="ing-bullet text-<?= $color2 ?>" title="<?= $title2 ?>"></span> <?php } ?> <span class="ing-<?= $icono ?> text-<?= $color ?>" title="<?= $title ?>"></span></td>
+                                <?php if($user->acceso == 'w') {?>
+                                    <td><?= $aviso['facultad_nombre'] ?></td>
+                                <?php }?>
+                                <td><?= $dia_inicial ?> - <?= $dia_final ?></td>
+                                <td><?= $aviso['aviso_texto'] ?></td>
+                                <?php if($user->acceso == 'w') {?>
+                                    <td class="text-center">
+                                        <a href="avisos_editar.php?id=<?= $aviso['aviso_id'] ?>" title="Editar"><span class="ing-editar"></span></a>
+                                        <?php if($aviso['aviso_estado'] == true){ ?>
+                                            <a href="#" data-toggle="modal" data-target="#modal_confirm" title="Borrar"><span class="ing-basura"></span></a>
+                                        <?php } ?>
+                                    </td>
+                                <?php }?>
+                            </tr>
+                        <?php } ?>
+                    </tbody>
+                </table>
+            </div>
+        </div>
+        <nav aria-label="paginas">
+            <ul class="pagination justify-content-end">
+                <?php if($pag != 0){ ?>
+                    <li class="page-item">
+                    <a class="page-link" href="avisos.php?pag=<?= $pag ?>" aria-label="Previous">
+                        <span aria-hidden="true">&laquo;</span>
+                    </a>
+                    </li>
+                <?php } ?>
+                <?php for($i = 0; $i < $paginas; $i++){ ?>
+                    <li class="page-item <?php if($i == $pag){ echo"active"; } ?>" <?php if($i == $pag){ echo 'aria-current="page"'; } ?>><a class="page-link" href="avisos.php?pag=<?= $i+1 ?>"><?= $i+1 ?></a></li>
+
+                <?php }
+                if(($pag + 1) < $paginas){ ?>
+                    <li class="page-item">
+                    <a class="page-link" href="avisos.php?pag=<?= $pag + 2 ?>" aria-label="Next">
+                        <span aria-hidden="true">&raquo;</span>
+                    </a>
+                    </li>
+                <?php } ?>
+            </ul>
+        </nav>
+    </main>
+    <?php
+    include "import/html_footer.php";
+    ?>
+
+    <?php if($user->acceso == 'w') {?>
+    <div class="modal fade" id="modal_confirm" tabindex="-1" role="dialog" aria-labelledby="modal" aria-hidden="true">
+        <div class="modal-dialog modal-dialog-centered" role="document">
+            <div class="modal-content">
+                <div class="modal-body">
+                    <div class="row">
+                        <div class="col">
+                            <p class="font-weight-bold">¿Estás seguro de que quieres borrar el aviso?</p>
+                            <p>Esta acción no se puede deshacer.</p>
+                        </div>
+                    </div>
+                </div>
+                <div class="modal-footer">
+                    <input type="hidden" id="id_borrar" value="">
+                    <button type="button" class="btn btn-outline-primary btn-borrar"><?php echo $ICO["aceptar"];?> Borrar</button>
+                    <button type="button" class="btn btn-outline-danger" data-dismiss="modal" aria-label="Close"><?php echo $ICO["cancelar"];?> Cancelar</button>
+                </div>
+            </div>
+        </div>    
+    </div>
+    <?php } ?>
+
+    <script src="js/jquery.min.js"></script>
+    <script src="js/jquery-ui.js"></script>
+    <script src="js/bootstrap/bootstrap.min.js"></script>
+    <script src="js/datalist.js"></script>
+    <script src="js/datepicker-es.js"></script>
+    <?php
+        require_once 'js/messages.php';
+    ?>
+    <script>
+        var today = new Date();
+        $(".date-picker").datepicker($.datepicker.regional["es"]);
+        $(".date-picker").datepicker({
+            dateFormat: "dd/mm/yyyy",
+            changeMonth: true,
+        });
+
+        $(document).on( "click", ".btn-reset", function(event){
+            var forma = $(this).parents("form");
+            forma.find("input[type=text]").val("");
+            forma.find("select").prop("selectedIndex",0);
+            forma.submit();
+        });
+
+        $('#modal_confirm').on('show.bs.modal', function (event) {
+            var button = $(event.relatedTarget); // Button that triggered the modal
+            var id = button.parents("tr").data("id");
+            $("#id_borrar").val(id);
+        });
+
+        $('.btn-borrar').click(function(){
+            var cid = $('#id_borrar').val();
+            $.ajax({
+                url: './action/action_avisos_delete.php',
+                type: 'POST',
+                dataType: 'json',
+                data: {id: cid},
+                success: function(result){
+                    console.log("bien");
+                },
+                error: function(){
+                    console.log(cid);
+                }
+            });
+            $('#modal_confirm').modal("hide");
+            location.reload();
+        });
+    </script>
+</body>
+</html>

+ 435 - 0
avisos_crear.php

@@ -0,0 +1,435 @@
+<?php
+require_once 'class/c_login.php';
+require_once 'include/bd_pdo.php';
+
+if(!isset($_SESSION['user'])){
+    header('Location: index.php');
+    exit;
+}
+else
+    $user = unserialize($_SESSION['user']);
+$user->access('Avisos');
+if(!$user->admin && $user->acceso == 'n'){
+    header('Location: main.php?error=1');
+}else{
+    $user->print_to_log('Avisos Crear');
+}
+$fac = $user->facultad['facultad_id'];
+if($user->admin){
+    $fac = null;
+}
+$fs_carreras = query('SELECT * FROM fs_carreras(:fac, null, null)', [':fac' => $fac], false);
+$fs_facultades = query('SELECT * FROM facultad WHERE facultad_activa = true', null, false);
+?>
+<!DOCTYPE html>
+<html lang="en">
+
+<head>
+    <meta charset="UTF-8">
+    <meta name="viewport" content="width=device-width, initial-scale=1.0">
+    <title>Avisos Crear</title>
+    <link rel="stylesheet" href="css/jquery-ui.css">
+    <link rel="stylesheet" href="css/calendar.css">
+    <link rel="stylesheet" href="css/richtext.css" type="text/css">
+    <link rel="stylesheet" href="css/fa_all.css" type="text/css">
+    <?php
+    include 'import/html_css_files.php';
+    ?>
+</head>
+
+<body>
+    <?php
+    include "import/html_header.php";
+    html_header(
+        "CREAR AVISO",
+        "Gestión de Checador "
+    );
+    $user->access('Avisos');
+    ?>
+    <main class="content marco">
+        <div class="row">
+            <div class="col-12">
+                <form action="./action/action_avisos_insert.php" method="post" id="formaModal" onsubmit="return validaCampos()">
+                    <div class="form-box">
+                        <?php if($user->admin){ ?>
+                            <div class="form-group row">
+                                <label for="facultad" class="col-4 col-form-label">Facultad *</label>
+                                <div class="col-8 col-sm-4">
+                                    <div class="datalist datalist-select mb-1 w-100">
+                                        <div class="datalist-input">Mostrar todas</div>
+                                        <span class="ing-buscar icono"></span>
+                                        <ul style="display: none;">
+                                            <?php foreach($fs_facultades as $facultad){ ?>
+                                            <li data-id="<?= $facultad['facultad_id'] ?>" class="pl-4"><?= $facultad['facultad_nombre'] ?></li>
+                                            <?php } ?>
+                                        </ul>
+                                        <input type="hidden" id="facultad" name="facultad" value="">
+                                    </div>
+                                </div>
+                            </div>
+                        <?php }else {?>
+                            <input type="hidden" name="facultad" id="facultad" value="<?= $fac ?>">
+                        <?php }?>
+                        <div class="form-group row">
+                            <label for="fecha_inicial" class="col-4 col-form-label">Fecha inicial *</label>
+                            <div class="col-8 col-sm-4">
+                                <input id="fecha_inicial" name="fecha_inicial" type="text" class="form-control date-picker" placeholder="dd/mm/aaaa" maxlength="10" required="required" readonly="readonly">
+                                <div class="invalid-feedback">No es una fecha valida</div>
+                            </div>
+                        </div>
+                        <div class="form-group row">
+                            <label for="fecha_final" class="col-4 col-form-label">Fecha Final *</label>
+                            <div class="col-8 col-sm-4">
+                                <input id="fecha_final" name="fecha_final" type="text" class="form-control date-picker" placeholder="dd/mm/aaaa" maxlength="10" required="required" readonly="readonly">
+                                <div class="invalid-feedback">No es una fecha valida</div>
+                            </div>
+                        </div>
+                        <div class="form-group row">
+                            <label for="texto" class="col-4 col-form-label">Aviso *</label>
+                            <div class="col-8">
+                                <textarea name="texto" id="texto" class="richtext" rows="10"></textarea>
+                                <div class="invalid-feedback">No puede estar vacio</div>
+                            </div>
+                        </div>
+                        <div class="form-group row tipo_aviso">
+                            <label class="col-4 col-form-label">Enviar aviso:</label>
+                            <div class="col-4 pt-2">
+                                <div class="custom-control custom-switch">
+                                    <input type="checkbox" class="custom-control-input tipo-switch" name="bloque_tipo" id="bloque_tipo" value="1" data-box="profesorBox" data-select="tipo_box">
+                                    <label class="custom-control-label" for="bloque_tipo">Por carrera</label>
+                                </div>
+                            </div>
+                            <div class="col-4 pt-2">
+                                <div class="custom-control custom-switch">
+                                    <input type="checkbox" class="custom-control-input tipo-switch"  name="bloque_usr" id="bloque_usr" value="1" data-box="administrativoBox" data-select="usuario_box">
+                                    <label class="custom-control-label" for="bloque_usr">Por nombre</label>
+                                </div>
+                            </div>
+                        </div>
+                        <div class="invalid-feedback offset-4">No hay profesores para mandar el aviso</div>
+                        <div class="collapse" id="profesorBox">
+                            <h3>Carreras</h3>
+                            <p>Utiliza el botón para asignar las carreras que recibirán el aviso.</p>
+                            <div class="form-group row">
+                                <div class="col-10">
+                                    <select name="tipo[]" id="tipo_box" class="form-control" multiple="multiple" size="5">
+                                    </select>
+                                </div>
+                                <div class="col-2">
+                                    <p><button type="button" class="btn btn-outline-primary"  data-toggle="modal" data-target="#modal_carrera"><span class="ing-mas ing-fw"></span> Asignar</button></p>
+                                    <p><button type="button" class="btn btn-outline-danger btn-quita-tipo"><span class="ing-menos ing-fw"></span> Quitar</button></p>
+                                </div>
+                            </div>
+                        </div>
+                        
+                        <div class="collapse" id="administrativoBox">
+                            <h3>Nombre de profesores</h3>
+                            <p>Utiliza el botón para asignar los profesores que recibirán el aviso.</p>
+                            <div class="form-group row">
+                                <div class="col-10">
+                                    <select name="usuario[]" id="usuario_box" class="form-control" multiple="multiple" size="5">
+                                    </select>
+                                    </div>
+                                    <div class="col-2">
+                                        <p><button type="button" class="btn btn-outline-primary"  data-toggle="modal" data-target="#modal_usr"><span class="ing-mas ing-fw"></span> Asignar</button></p>
+                                        <p><button type="button" class="btn btn-outline-danger btn-quita-usr"><span class="ing-menos ing-fw"></span> Quitar</button></p>
+                                    </div>
+                                </div>
+                            </div>
+                        </div>
+                        <div class="form-group row mt-2">
+                            <div class="col-12 text-center">
+                                <button type="submit" class="btn btn-outline-primary" id="submitBtn" data-tipo="1"><span class="ing-aceptar"></span> Guardar</button>
+                                <a href="avisos.php" class="btn btn-outline-danger"><span class="ing-cancelar"></span> Cancelar</a>
+                            </div>
+                        </div>
+                    </form>
+                </div>
+            </div>
+        </main>
+        <!-- Footer -->
+    <?php
+    include "import/html_footer.php";
+    ?>
+
+    <!-- Modal -->
+    <div class="modal fade" id="modal_carrera" tabindex="-1" role="dialog" aria-labelledby="modal" aria-hidden="true" data-backdrop="static" data-keyboard="false">
+        <div class="modal-dialog modal-dialog-centered" role="document">
+            <div class="modal-content">
+                <div class="modal-header">
+                    <h4 class="col-12 modal-title text-center">Carreras
+                        <button type="button" class="close text-white" data-dismiss="modal" aria-label="Close">
+                            <span aria-hidden="true">&times;</span>
+                        </button>
+                    </h4>
+                </div>
+                <div class="modal-body">
+                    <div style="max-height:400px; overflow-y:auto; overflow-x:hidden;">
+                        <table class="table table-sm table-striped table-white">
+                            <thead class="thead-dark">
+                                <tr>
+                                    <th>Carrera</th>
+                                    <th>Asignar</th>
+                                </tr>
+                            </thead>
+                            <tbody id="table-result-carrera">
+                                <tr class="tipo-row">
+                                    <td class="carrera-nombre"></td>
+                                    <td class="carrera-agrega text-center"><button type="button" class="btn btn-outline-primary btn-sm invisible btn-agrega-carrera"><span class="ing-mas"></span></button></td>
+                                </tr>    
+                            </tbody>
+                        </table>
+                    </div>
+                </div>
+            </div>
+        </div>
+    </div>
+
+    <div class="modal fade" id="modal_usr" tabindex="-1" role="dialog" aria-labelledby="modal" aria-hidden="true" data-backdrop="static" data-keyboard="false">
+        <div class="modal-dialog modal-dialog-centered modal-lg" role="document">
+            <div class="modal-content">
+                <div class="modal-header">
+                    <h4 class="col-12 modal-title text-center">Busca Usuarios
+                        <button type="button" class="close text-white" data-dismiss="modal" aria-label="Close">
+                            <span aria-hidden="true">&times;</span>
+                        </button>
+                    </h4>
+                </div>
+                <div class="modal-body">
+                    <form id="forma_buscar-usr" onsubmit="return false">
+                        <div class="form-box">
+                            <div class="form-group row">
+                                <label for="filter_desc" class="col-4 col-form-label">Nombre</label>
+                                <div class="col-8">
+                                    <input id="filter_desc" name="desc" type="text" class="form-control">
+                                </div>
+                            </div>
+                            <div class="form-group row">
+                                <label for="filter_clave" class="col-4 col-form-label">Clave ULSA</label>
+                                <div class="col-8">
+                                    <input id="filter_clave" name="clave" type="text" class="form-control">
+                                </div>
+                            </div>
+                        </div>
+                        <div class="for-group row">
+                            <div class="offset-4">
+                                <button type="submit" class="btn btn-outline-primary" id="btn-busca-usr"><span class="ing-buscar"></span> Buscar</button>
+                                <button type="button" class="btn btn-outline-danger" data-dismiss="modal" aria-label="Close"><span class="ing-cancelar"></span> Cerrar</button>
+                            </div>
+                        </div>
+                    </form>
+                    <div style="max-height:400px; overflow-y:auto; overflow-x:hidden;">
+                        <table class="table table-sm table-striped table-white mt-3">
+                            <thead class="thead-dark">
+                                <tr>
+                                    <th>Usuario</th>
+                                    <th style="width: 20%;">Asignar</th>
+                                </tr>
+                            </thead>
+                            <tbody id="table-result-usr">
+                                <tr class="usr-row">
+                                    <td class="usr-nombre"></td>
+                                    <td class="usr-agrega text-center"><button type="button" class="btn btn-outline-primary btn-sm invisible btn-agrega-usr"><span class="ing-mas"></span></button></td>
+                                </tr>
+                            </tbody>
+                        </table>
+                    </div>
+                </div>
+            </div>
+        </div>
+    </div>
+
+    <script src="js/jquery.min.js"></script>
+    <script src="js/jquery-ui.js"></script>
+    <script src="js/bootstrap/bootstrap.min.js"></script>
+    <script src="js/datalist.js"></script>
+    <script src="js/datepicker-es.js"></script>
+    <script src="./js/richtext.js"></script>
+    <?php
+        require_once 'js/messages.php';
+    ?>
+    <script>
+        var today = new Date();
+        $(".date-picker").datepicker($.datepicker.regional["es"]);
+        $(".date-picker").datepicker({
+            dateFormat: "dd/mm/yyyy",
+            changeMonth: true,
+        });
+        $(document).ready(function(){
+            $('.richtext').richText();
+            //var today = new Date();
+            $("#fecha_inicial").datepicker("option", "minDate", today);
+            $("#fecha_final").datepicker("option", "minDate", today);
+            $('#fecha_inicial').datepicker("setDate", today);
+            $('#fecha_final').datepicker("setDate", today);
+            setDatalistFirst('#facultad');
+        });
+
+        $(document).on( "change", ".tipo-switch", function(event){
+            if($(this).data("box") !== undefined){
+                if($(this).prop('checked')){
+                    $('#'+$(this).data("box")).collapse('show');
+                }else{
+                    $('#'+$(this).data("box")).collapse('hide');
+                }
+            }
+            $('#bloque_tipo').removeClass("is-invalid");
+            $('#bloque_usr').removeClass("is-invalid");
+        });
+
+        $(document).on( "click", ".btn-agrega-carrera", function(event){
+            var id = $(this).data("id");
+            var text = $(this).data("text");
+            if($('#tipo_box option[value="' + id + '"]').length == 0){
+                $("#tipo_box").append($("<option></option>").attr("value",id).text(text));
+            }
+            $(this).parents("tr").addClass("d-none");
+        });
+
+        $(document).on( "click", ".btn-quita-tipo", function(event){
+            var id = $("#tipo_box option:selected").val();
+            $("#arow_"+id).removeClass("d-none");
+            $("#tipo_box option:selected").remove();
+        });
+
+        $(document).on( "click", ".modal-open", function(event){
+            $(".area-row").removeClass("d-none");
+            $('#area > option').each(function() {
+                $("#row_"+$(this).val()).addClass("d-none");
+            });
+            $('#modal').modal("show");
+        });
+
+        $(document).on('click', '#btn-busca-usr', function(event){
+            var nombre = $('#filter_desc').val();
+            var clave = $('#filter_clave').val();
+            var fac = $('#facultad').val();
+            $('#table-result-usr').show();
+            $.ajax({
+                url: 'action/usuario_find.php',
+                type: 'POST',
+                dataType: 'json',
+                data: {nombre: nombre, clave: clave, facultad: fac},
+                success: function(result){
+                    console.log(result.length);
+                    $("#table-result-usr").find(".usr-nombre").html("");
+                    $("#table-result-usr").find(".usr-agrega button").addClass("invisible");
+                    var rows = $("#table-result-usr > tr").length;//limpia tabla actual
+                    if(rows > result.length){
+                        while(rows > result.length && rows > 1){
+                            $("#table-result-usr .usr-row:last-child").remove();
+                            rows--;
+                        }
+                    }else{
+                        for(var i=rows; i<result.length; i++){
+                            $("#table-result-usr .usr-row:first-child").clone(true).appendTo("#table-result-usr");
+                        }
+                    }
+                    if(result.length != 0){
+                        $('#table-result-usr').children().each(function(index){
+                            if(index < result.length){
+                                $(this).find('.usr-nombre').html(result[index]['profesor_nombre']);
+                                $(this).find('.usr-agrega button').data("id", result[index]['profesor_id']);
+                                $(this).find('.usr-agrega button').data("text", result[index]['profesor_nombre']);
+                                $(this).find(".usr-agrega button").removeClass("invisible");
+                            }
+                        })
+                    }
+                },
+                error: function(){
+                    console.log('error');
+                }
+            });
+        });
+
+        $(document).on( "click", ".btn-agrega-usr", function(event){
+            var id = $(this).data("id");
+            var text = $(this).data("text");
+            var rows = $("#table-result-usr > tr").length;//limpia tabla actual
+
+            if($('#usuario_box option[value="' + id + '"]').length == 0){
+                $("#usuario_box").append($("<option></option>").attr("value",id).text(text));
+            }
+            if(rows > 1)
+                $(this).parents("tr").remove();
+            else{
+                $(this).parents("tr").hide();
+                $("#filter_desc-usr").val("");
+            }
+        });
+
+        $(document).on( "click", ".btn-quita-usr", function(event){
+            $("#usuario_box option:selected").remove();
+        });
+
+        function validaCampos(){
+            var error = false;
+            var inicio = $('#fecha_inicial').val();
+            var fin = $('#fecha_final').val();
+            var aux = inicio.split('/');
+            inicio = aux[2]+'-'+aux[1]+'-'+aux[0];
+            aux = fin.split('/');
+            fin = aux[2]+'-'+aux[1]+'-'+aux[0];
+            if(fin < inicio){
+                $('#fecha_final').addClass('is-invalid');
+                error = true;
+            }
+            if($('#texto').val() == ""){
+                $('#texto').addClass('is-invalid');
+                error = true;
+            }
+            if($('select').find('option').length == 0){
+                error = true;
+                $('.tipo_aviso').addClass('is-invalid');
+            }else{
+                $('.tipo_aviso').removeClass('is-invalid');
+            }
+            if(error == true){
+                return false;
+            }else{
+                $('#tipo_box option').prop('selected', true);
+                $('#usuario_box option').prop('selected', true);
+                $('#formaModal').prop('action', './action/action_avisos_insert.php');
+            }
+        }
+
+        $('#modal_carrera').on('show.bs.modal', function(event){
+            var fac_id = $('#facultad').val();
+            $.ajax({
+                url: 'action/carrera_find.php',
+                type: 'POST',
+                dataType: 'json',
+                data: {fac_id: fac_id},
+                success: function(result){
+                    $("#table-result-carrera").find(".carrera-nombre").html("");
+                    $("#table-result-carrera").find(".carrera-agrega button").addClass("invisible");
+                    var rows = $("#table-result-carrera > tr").length;
+                    if(rows > result.length){
+                        while(rows > result.length && rows > 1){
+                            $("#table-result-carrera .tipo-row:last-child").remove();
+                            rows--;
+                        }
+                    }else{
+                        for(var i=rows; i<result.length; i++){
+                            $("#table-result-carrera .tipo-row:first-child").clone(true).appendTo("#table-result-carrera");
+                        }
+                    }
+                    if(result.length != 0){
+                        $("#table-result-carrera").children().each(function(index){
+                            if(index < result.length){
+                                $(this).find('.carrera-nombre').html(result[index]['carrera_nombre']);
+                                $(this).find('.carrera-agrega button').data("id", result[index]['carrera_id']);
+                                $(this).find('.carrera-agrega button').data("text", result[index]['carrera_nombre']);
+                                $(this).find(".carrera-agrega button").removeClass("invisible");
+                            }
+                        })
+                    }
+                },
+                error: function(){
+                    console.log("Error");
+                }
+            });
+        })
+    </script>
+</body>
+</html>

+ 431 - 0
avisos_editar.php

@@ -0,0 +1,431 @@
+<?php
+require_once 'class/c_login.php';
+require_once 'include/bd_pdo.php';
+
+if(!isset($_SESSION['user'])){
+    header('Location: index.php');
+    exit;
+}
+else
+    $user = unserialize($_SESSION['user']);
+$user->access('Avisos');
+if(!$user->admin && $user->acceso == 'n'){
+    header('Location: main.php?error=1');
+}else{
+    $user->print_to_log('Avisos Editar');
+}
+$fac = $user->facultad['facultad_id'];
+if($user->admin){
+    $fac = null;
+}
+$fs_aviso = query('SELECT * FROM fs_aviso(:aviso_id, null, :facultad_id, null, 0, null)', [':aviso_id' => $_GET['id'], ':facultad_id' => $fac], true);
+$fs_carreras = query('SELECT * FROM fs_carreras(:fac, null, null)', [':fac' => $fs_aviso['facultad_id']], false);
+$fs_usr = query('SELECT * FROM fs_profesor_aviso(:aviso_id)', [':aviso_id' => $_GET['id']], false);
+$today = date('Y-m-d');
+$edit = true;
+if($today >= $fs_aviso['aviso_fecha_inicial']){
+    $edit = false;
+}
+?>
+<!DOCTYPE html>
+<html lang="en">
+
+<head>
+    <meta charset="UTF-8">
+    <meta name="viewport" content="width=device-width, initial-scale=1.0">
+    <title>Avisos Crear</title>
+    <link rel="stylesheet" href="css/jquery-ui.css">
+    <link rel="stylesheet" href="css/calendar.css">
+    <link rel="stylesheet" href="css/richtext.css" type="text/css">
+    <link rel="stylesheet" href="css/fa_all.css" type="text/css">
+    <?php
+    include 'import/html_css_files.php';
+    ?>
+</head>
+
+<body>
+    <?php
+    include "import/html_header.php";
+    html_header(
+        "EDITAR AVISO",
+        "Gestión de Checador "
+    );
+    $user->access('Avisos');
+    ?>
+    <main class="content marco">
+        <div class="row">
+            <div class="col-12">
+                <form action="./action/action_avisos_update.php" method="post" id="formaModal" onsubmit="return validaCampos()">
+                    <input type="hidden" name="aviso_id" id="aviso_id" value="<?= $fs_aviso['aviso_id'] ?>">
+                    <div class="form-box">
+                        <div class="form-group row">
+                            <label for="fecha_inicial" class="col-4 col-form-label">Fecha inicial</label>
+                            <div class="col-8 col-sm-4">
+                                <?php if($edit == true){ ?>
+                                    <input id="fecha_inicial" name="fecha_inicial" type="text" class="form-control date-picker" placeholder="dd/mm/aaaa" maxlength="10" required="required" readonly="readonly" value="<?php $day = explode("-", $fs_aviso['aviso_fecha_inicial']); $dia_inicial = $day['2'].'/'.$day['1'].'/'.$day[0]; echo $dia_inicial;?>">
+                                    <div class="invalid-feedback">No es una fecha valida</div>
+                                <?php }else{ ?>
+                                    <div>
+                                        <?php $day = explode("-", $fs_aviso['aviso_fecha_inicial']); $dia_inicial = $day['2'].'/'.$day['1'].'/'.$day[0]; echo $dia_inicial;?>
+                                    </div>
+                                <?php } ?>
+                            </div>
+                        </div>
+                        <div class="form-group row">
+                            <label for="fecha_final" class="col-4 col-form-label">Fecha Final *</label>
+                            <div class="col-8 col-sm-4">
+                                <input id="fecha_final" name="fecha_final" type="text" class="form-control date-picker" placeholder="dd/mm/aaaa" maxlength="10" required="required" readonly="readonly" value="<?php $day = explode("-", $fs_aviso['aviso_fecha_final']); $dia_final = $day['2'].'/'.$day['1'].'/'.$day[0]; echo $dia_final;?>">
+                                <div class="invalid-feedback">No es una fecha valida</div>
+                            </div>
+                        </div>
+                        <div class="form-group row">
+                            <label for="texto" class="col-4 col-form-label">Aviso *</label>
+                            <div class="col-8">
+                            <?php if($edit == true){ ?>
+                                <textarea name="texto" id="texto" class="richtext" rows="10" readonly="readonly"><?= $fs_aviso['aviso_texto'] ?></textarea>
+                            <?php }else{ ?>
+                                <p><?= $fs_aviso['aviso_texto'] ?></p>
+                            <?php } ?>
+                            </div>
+                        </div>
+                        <?php if($edit){ ?>
+                            <div class="form-group row tipo_aviso">
+                                <label class="col-4 col-form-label">Enviar aviso:</label>
+                                <div class="col-4 pt-2">
+                                    <div class="custom-control custom-switch">
+                                        <input type="checkbox" class="custom-control-input tipo-switch" name="bloque_tipo" id="bloque_tipo" value="1" data-box="profesorBox" data-select="tipo_box">
+                                        <label class="custom-control-label" for="bloque_tipo">Por carrera</label>
+                                    </div>
+                                </div>
+                                <div class="col-4 pt-2">
+                                    <div class="custom-control custom-switch">
+                                        <input type="checkbox" class="custom-control-input tipo-switch"  name="bloque_usr" id="bloque_usr" value="1" data-box="administrativoBox" data-select="usuario_box">
+                                        <label class="custom-control-label" for="bloque_usr">Por nombre</label>
+                                    </div>
+                                </div>
+                            </div>
+                            <div class="invalid-feedback offset-4">No hay profesores para mandar el aviso</div>
+                            <div class="collapse" id="profesorBox">
+                                <h3>Carreras</h3>
+                                <p>Utiliza el botón para asignar las carreras que recibirán el aviso.</p>
+                                <div class="form-group row">
+                                    <div class="col-10">
+                                        <select name="tipo[]" id="tipo_box" class="form-control" multiple="multiple" size="5">
+                                        </select>
+                                    </div>
+                                    <div class="col-2">
+                                        <p><button type="button" class="btn btn-outline-primary"  data-toggle="modal" data-target="#modal_carrera"><span class="ing-mas ing-fw"></span> Asignar</button></p>
+                                        <p><button type="button" class="btn btn-outline-danger btn-quita-tipo"><span class="ing-menos ing-fw"></span> Quitar</button></p>
+                                    </div>
+                                </div>
+                            </div>
+                            
+                            <div class="collapse" id="administrativoBox">
+                                <h3>Nombre de profesores</h3>
+                                <p>Utiliza el botón para asignar los profesores que recibirán el aviso.</p>
+                                <div class="form-group row">
+                                    <div class="col-10">
+                                    <select name="usuario[]" id="usuario_box" class="form-control" multiple="multiple" size="5">
+                                    <?php
+                                    foreach($fs_usr as $usuario){
+                                        echo "<option value='".$usuario['profesor_id']."'>".$usuario['profesor_nombre']."</option>";
+                                    }
+                                    ?>    
+                                    </select>
+                                        </div>
+                                        <div class="col-2">
+                                            <p><button type="button" class="btn btn-outline-primary"  data-toggle="modal" data-target="#modal_usr"><span class="ing-mas ing-fw"></span> Asignar</button></p>
+                                            <p><button type="button" class="btn btn-outline-danger btn-quita-usr"><span class="ing-menos ing-fw"></span> Quitar</button></p>
+                                        </div>
+                                    </div>
+                                </div>
+                            </div>
+                        <?php }else{
+                            $num_prof = round(count($fs_usr)/2);
+                            ?>
+                            <h3>Profesores que recibirán el aviso</h3>
+                            <div class="row" style="max-height:400px; overflow-y:auto; overflow-x:hidden;">
+                                <div class="col-5">
+                                    <div>
+                                        <ul>
+                                            <?php for($i = 0; $i < $num_prof; $i++){
+                                                echo "<li>".$fs_usr[$i]['profesor_nombre']."</li>";
+                                            } ?>
+                                        </ul>
+                                    </div>
+                                </div>
+                                <div class="col-5">
+                                    <div>
+                                        <ul>
+                                            <?php for($i = $i; $i < count($fs_usr); $i++){
+                                                echo "<li>".$fs_usr[$i]['profesor_nombre']."</li>";
+                                            } ?>
+                                        </ul>
+                                    </div>
+                                </div>
+                            </div>
+                        <?php } ?>
+                        <div class="form-group row mt-2">
+                            <div class="col-12 text-center">
+                                <button type="submit" class="btn btn-outline-primary" id="submitBtn" data-tipo="1"><span class="ing-aceptar"></span> Guardar</button>
+                                <a href="avisos.php" class="btn btn-outline-danger"><span class="ing-cancelar"></span> Cancelar</a>
+                            </div>
+                        </div>
+                    </form>
+                </div>
+            </div>
+        </main>
+        <!-- Footer -->
+    <?php
+    include "import/html_footer.php";
+    ?>
+
+    <!-- Modal -->
+    <div class="modal fade" id="modal_carrera" tabindex="-1" role="dialog" aria-labelledby="modal" aria-hidden="true" data-backdrop="static" data-keyboard="false">
+        <div class="modal-dialog modal-dialog-centered" role="document">
+            <div class="modal-content">
+                <div class="modal-header">
+                    <h4 class="col-12 modal-title text-center">Carreras
+                        <button type="button" class="close text-white" data-dismiss="modal" aria-label="Close">
+                            <span aria-hidden="true">&times;</span>
+                        </button>
+                    </h4>
+                </div>
+                <div class="modal-body">
+                    <table class="table table-sm table-striped table-white">
+                        <thead class="thead-dark">
+                            <tr>
+                                <th>Carrera</th>
+                                <th>Asignar</th>
+                            </tr>
+                        </thead>
+                        <tbody>
+                            <?php $count = 1;
+                            foreach($fs_carreras as $carrera){ ?>
+                            <tr class="tipo-row" id="arow_<?= $carrera['carrera_id'] ?>">
+                                <td><?= $carrera['carrera_nombre'] ?></td>
+                                <td class="text-center"><button type="button" class="btn btn-outline-primary btn-sm btn-agrega-tipo" data-id="<?= $carrera['carrera_id'] ?>" data-text="<?= $carrera['carrera_nombre'] ?>"><span class="ing-mas"></span></button></td>
+                            </tr>    
+                            <?php $count++;
+                            } ?>
+                        </tbody>
+                    </table>
+                </div>
+            </div>
+        </div>
+    </div>
+
+    <div class="modal fade" id="modal_usr" tabindex="-1" role="dialog" aria-labelledby="modal" aria-hidden="true" data-backdrop="static" data-keyboard="false">
+        <div class="modal-dialog modal-dialog-centered modal-lg" role="document">
+            <div class="modal-content">
+                <div class="modal-header">
+                    <h4 class="col-12 modal-title text-center">Busca Usuarios
+                        <button type="button" class="close text-white" data-dismiss="modal" aria-label="Close">
+                            <span aria-hidden="true">&times;</span>
+                        </button>
+                    </h4>
+                </div>
+                <div class="modal-body">
+                    <form id="forma_buscar-usr" onsubmit="return false">
+                    <input type="hidden" id="facultad" name="facultad" value="<?= $fac ?>">
+                        <div class="form-box">
+                            <div class="form-group row">
+                                <label for="filter_desc" class="col-4 col-form-label">Nombre</label>
+                                <div class="col-8">
+                                    <input id="filter_desc" name="desc" type="text" class="form-control">
+                                </div>
+                            </div>
+                            <div class="form-group row">
+                                <label for="filter_clave" class="col-4 col-form-label">Clave ULSA</label>
+                                <div class="col-8">
+                                    <input id="filter_clave" name="clave" type="text" class="form-control">
+                                </div>
+                            </div>
+                        </div>
+                        <div class="for-group row">
+                            <div class="col-12 text-center">
+                                <button type="submit" class="btn btn-outline-primary" id="btn-busca-usr"><span class="ing-buscar"></span> Buscar</button>
+                                <button type="button" class="btn btn-outline-danger" data-dismiss="modal" aria-label="Close"><span class="ing-cancelar"></span> Cerrar</button>
+                            </div>
+                        </div>
+                    </form>
+                    <div style="max-height:400px; overflow-y:auto; overflow-x:hidden;">
+                        <table class="table table-sm table-striped table-white mt-3">
+                            <thead class="thead-dark">
+                                <tr>
+                                    <th>Usuario</th>
+                                    <th style="width: 20%;">Asignar</th>
+                                </tr>
+                            </thead>
+                            <tbody id="table-result-usr">
+                                <tr class="usr-row">
+                                    <td class="usr-nombre"></td>
+                                    <td class="usr-agrega text-center"><button type="button" class="btn btn-outline-primary btn-sm invisible btn-agrega-usr"><span class="ing-mas"></span></button></td>
+                                </tr>
+                            </tbody>
+                        </table>
+                    </div>
+                </div>
+            </div>
+        </div>
+    </div>
+
+    <script src="js/jquery.min.js"></script>
+    <script src="js/jquery-ui.js"></script>
+    <script src="js/bootstrap/bootstrap.min.js"></script>
+    <script src="js/datalist.js"></script>
+    <script src="js/datepicker-es.js"></script>
+    <script src="./js/richtext.js"></script>
+    <?php
+        require_once 'js/messages.php';
+    ?>
+    <script>
+        var today = new Date();
+        $(".date-picker").datepicker($.datepicker.regional["es"]);
+        $(".date-picker").datepicker({
+            dateFormat: "dd/mm/yyyy",
+            changeMonth: true,
+        });
+        $(document).ready(function(){
+            $('.richtext').richText();
+            $("#fecha_final").datepicker("option", "minDate", today);
+            $("#fecha_inicial").datepicker("option", "minDate", today);
+            <?php
+            if(count($fs_usr) > 0){ ?>
+                $('#bloque_usr').prop("checked", true);
+                $('#administrativoBox').collapse('show');
+            <? }
+            if($edit == false){ ?>
+                $('.richText-editor').removeAttr("contentEditable");
+            <?php }
+            ?>
+            
+        });
+
+        $(document).on( "change", ".tipo-switch", function(event){
+            if($(this).data("box") !== undefined){
+                if($(this).prop('checked')){
+                    $('#'+$(this).data("box")).collapse('show');
+                }else{
+                    $('#'+$(this).data("box")).collapse('hide');
+                }
+            }
+            $('#bloque_tipo').removeClass("is-invalid");
+            $('#bloque_usr').removeClass("is-invalid");
+        });
+
+        $(document).on( "click", ".btn-agrega-tipo", function(event){
+            var id = $(this).data("id");
+            var text = $(this).data("text");
+            if($('#tipo_box option[value="' + id + '"]').length == 0){
+                $("#tipo_box").append($("<option></option>").attr("value",id).text(text));
+            }
+            $(this).parents("tr").addClass("d-none");
+        });
+
+        $(document).on( "click", ".btn-quita-tipo", function(event){
+            var id = $("#tipo_box option:selected").val();
+            $("#arow_"+id).removeClass("d-none");
+            $("#tipo_box option:selected").remove();
+        });
+
+        $(document).on( "click", ".modal-open", function(event){
+            $(".area-row").removeClass("d-none");
+            $('#area > option').each(function() {
+                $("#row_"+$(this).val()).addClass("d-none");
+            });
+            $('#modal').modal("show");
+        });
+
+        $(document).on('click', '#btn-busca-usr', function(event){
+            var nombre = $('#filter_desc').val();
+            var clave = $('#filter_clave').val();
+            var fac = $('#facultad').val();
+            $('#table-result-usr').show();
+            $.ajax({
+                url: 'action/usuario_find.php',
+                type: 'POST',
+                dataType: 'json',
+                data: {nombre: nombre, clave: clave, facultad: fac},
+                success: function(result){
+                    console.log(result.length);
+                    $("#table-result-usr").find(".usr-nombre").html("");
+                    $("#table-result-usr").find(".usr-agrega button").addClass("invisible");
+                    var rows = $("#table-result-usr > tr").length;//limpia tabla actual
+                    if(rows > result.length){
+                        while(rows > result.length && rows > 1){
+                            $("#table-result-usr .usr-row:last-child").remove();
+                            rows--;
+                        }
+                    }else{
+                        for(var i=rows; i<result.length; i++){
+                            $("#table-result-usr .usr-row:first-child").clone(true).appendTo("#table-result-usr");
+                        }
+                    }
+                    if(result.length != 0){
+                        $('#table-result-usr').children().each(function(index){
+                            if(index < result.length){
+                                $(this).find('.usr-nombre').html(result[index]['profesor_nombre']);
+                                $(this).find('.usr-agrega button').data("id", result[index]['profesor_id']);
+                                $(this).find('.usr-agrega button').data("text", result[index]['profesor_nombre']);
+                                $(this).show();
+                                $(this).find(".usr-agrega button").removeClass("invisible");
+                            }
+                        })
+                    }
+                },
+                error: function(){
+                    console.log('error');
+                }
+            });
+        });
+
+        $(document).on( "click", ".btn-agrega-usr", function(event){
+            var id = $(this).data("id");
+            var text = $(this).data("text");
+            var rows = $("#table-result-usr > tr").length;//limpia tabla actual
+
+            if($('#usuario_box option[value="' + id + '"]').length == 0){
+                $("#usuario_box").append($("<option></option>").attr("value",id).text(text));
+            }
+            if(rows > 1)
+                $(this).parents("tr").remove();
+            else{
+                $(this).parents("tr").hide();
+                $("#filter_desc-usr").val("");
+            }
+        });
+
+        $(document).on( "click", ".btn-quita-usr", function(event){
+            $("#usuario_box option:selected").remove();
+        });
+
+        function validaCampos(){
+            var error = false;
+            var inicio = $('#fecha_inicial').val();
+            var fin = $('#fecha_final').val();
+            var aux = inicio.split('/');
+            inicio = aux[2]+'-'+aux[1]+'-'+aux[0];
+            aux = fin.split('/');
+            fin = aux[2]+'-'+aux[1]+'-'+aux[0];
+            if(fin < inicio){
+                $('#fecha_final').addClass('is-invalid');
+                error = true;
+            }
+            if($('select').find('option').length == 0){
+                error = true;
+                $('.tipo_aviso').addClass('is-invalid');
+            }else{
+                $('.tipo_aviso').removeClass('is-invalid');
+            }
+            if(error){
+                return false;
+            }else{
+                $('#tipo_box option').prop('selected', true);
+                $('#usuario_box option').prop('selected', true);
+                $('#formaModal').prp('action', './action/action_avisos_update.php');
+            }
+        }
+    </script>
+</body>
+</html>

+ 58 - 0
base.php

@@ -0,0 +1,58 @@
+<?php
+require_once 'class/c_login.php';
+require_once 'include/bd_pdo.php';
+
+if(!isset($_SESSION['user'])){
+    header('Location: index.php');
+    exit;
+}
+else
+    $user = unserialize($_SESSION['user']);
+$user->access('usuarios');
+if(!$user->admin && $user->acceso == 'n'){
+    header('Location: main.php?error=1');
+}else{
+    $user->print_to_log('Base');
+}
+$fac = $user->facultad['facultad_id'] ?? -1;
+if($user->admin){
+    $fac=null;
+}
+?>
+<!DOCTYPE html>
+<html lang="en">
+
+<head>
+    <meta charset="UTF-8">
+    <meta name="viewport" content="width=device-width, initial-scale=1.0">
+    <title>Base</title>
+    <?php
+    include 'import/html_css_files.php';
+    ?>
+    <script src="js/messages.js" defer></script>
+</head>
+
+<body>
+    <?php
+    include "import/html_header.php";
+    html_header(
+        "PROFESORES",
+        "Gestión de Checador "
+    );
+    ?>
+    <main class="content marco">
+    </main>
+    <?php
+    include "import/html_footer.php";
+    ?>
+    <script src="js/jquery.min.js"></script>
+    <script src="js/bootstrap/bootstrap.min.js"></script>
+    <?php
+        require_once 'js/messages.php';
+    ?>
+    <script>
+        
+    </script>
+</body>
+
+</html>

+ 140 - 0
bypass.php

@@ -0,0 +1,140 @@
+<?php
+require_once 'class/c_login.php';
+if (isset($_GET["error"]) && is_numeric($_GET["error"])) {
+    switch ($_GET["error"]) {
+        case 0:
+            $errorDesc = "No se reciberon datos.";
+            break;
+        case 1:
+            $errorDesc = "El usuario y/o contraseña son incorrectos.";
+            break;
+        case 2:
+            $errorDesc = "El usuario no tiene permisos de ingresar.";
+            break;
+        case 3:
+            $errorDesc = "El usuario y/o contraseña son incorrectos.";
+            break;
+    }
+}
+?>
+<!DOCTYPE html>
+<html lang="es" prefix="og: http://ogp.me/ns#">
+
+<head>
+    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
+    <title>.: Administrador de checador :.</title>
+
+    <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
+    <link rel="icon" type="image/png" href="imagenes/favicon.png" />
+    <link rel="stylesheet" href="css/bootstrap-ulsa.min.css" type="text/css">
+    <link rel="stylesheet" href="css/indivisa.css" type="text/css">
+    <link rel="stylesheet" href="css/sgi.css?rand=<?= rand() ?>" type="text/css">
+    <link rel="stylesheet" href="css/index.css" type="text/css">
+
+<body>
+    <!-- HEADER -->
+    <?php include "import/html_header.php";
+    html_header("Checador inicio de sesión"); ?>
+    <main class="container-fluid content d-flex justify-content-center align-items-center">
+        <div class="logSize p-5 bg-white defaultShadow">
+            <div class="row mb-4">
+                <div class="col-sm-12">
+                    <h1 class="mb-1">Iniciar sesión</h1>
+                </div>
+            </div>
+            <form method="post" action="action/force_session.php" id="session">
+                <div class="row user">
+                    <div class="col">
+                        <p class="text-center font-weight-bold text-info">Utiliza tu usuario y contraseña institucionales</p>
+                    </div>
+                </div>
+                <div class="form-group row user">
+                    <div class="input-group px-4">
+                        <div class="input-group-prepend secondary">
+                            <div class="input-group-text bg-primary text-white"><i class="ing-usuario ing-fw"></i></div>
+                        </div>
+                        <input class="form-control form-control-lg" type="text" autocomplete="username" placeholder="Usuario (ad)" id="username" name="username" value="" autofocus="true" maxlength="10" />
+                    </div>
+                </div>
+                <div class="form-group row user">
+                    <div class="input-group mb-2 px-4">
+                        <div class="input-group-prepend">
+                            <div class="input-group-text bg-primary text-white"><i class="ing-pass ing-fw"></i></div>
+                        </div>
+                        <input class="form-control form-control-lg" type="password" autocomplete="current-password" placeholder="Contraseña" id="passwd" name="passwd" value="" maxlength="50" />
+                    </div>
+                </div>
+                <!-- Usuario -->
+                <div class="form-group row" id="cold-bypass">
+                    <div class="input-group mb-2 px-4">
+                        <div id="dlUsuario" class="datalist datalist-select mb-1 w-100">
+                            <div class="datalist-input">Selecciona un usuario</div>
+                            <span class="ing-buscar icono"></span>
+                            <ul style="display:none">
+
+                            </ul>
+                            <input type="hidden" id="user" name="usuario" value="">
+                        </div>
+                    </div>
+                </div>
+                <div class="error">
+                    <?php if (isset($_GET["error"])) { ?>
+                        <p class="text-danger text-center font-weight-bold">¡ERROR! <?= $errorDesc ?></p>
+                    <?php } ?>
+                </div>
+                <p class="text-center">
+                    <button type="submit" class="btn btn-lg btn-outline-primary btn-ing arrow">Ingresar</button>
+                </p>
+            </form>
+        </div>
+    </main>
+    <!--- FOOTER--->
+    <?php require_once("import/html_footer.php"); ?>
+
+    <script src="js/jquery.min.js"></script>
+    <script src="js/bootstrap/bootstrap.min.js"></script>
+    <script src="js/bootstrap/popper.min.js"></script>
+    <script src="js/sidebarmenu.js"></script>
+    <script src="js/datalist.js"></script>
+    <script>
+        $("#cold-bypass").hide();
+        // on submit, prevent default
+        $("#session").submit(function(e) {
+            if ($("#user").val() != "") {
+                $(this).unbind('submit').submit();
+                return;
+            }
+            e.preventDefault();
+            // dlUsuario has a value force session and redirect to main.php
+            // get the form data
+            var formData = {
+                'username': $('input[name=username]').val(),
+                'passwd': $('input[name=passwd]').val(),
+            };
+
+            $.post("action/action_usuario.php", formData, function(data) {
+                console.log(data);
+                $(".error").html("");
+                if (data == "error") {
+                    $(".error").html("<p class='text-danger text-center font-weight-bold'>¡ERROR! El usuario y/o contraseña son incorrectos.</p>");
+                    return;
+                }
+                $("#cold-bypass").show();
+                data.forEach(function(element) {
+                    // console.log(element);
+                    $("#dlUsuario ul").append(`<li class="not-selectable" data-id="${element.id}">${element.facultad}</li>`);
+                    element.usuarios.forEach(function(usuario) {
+                        $("#dlUsuario ul").append(`<li data-id="${usuario.id}">${usuario.username}</li>`);
+                    });
+                });
+
+                // hide username and password
+                $(".user").hide();
+
+            }, 'json');
+        });
+    </script>
+
+</body>
+
+</html>

+ 813 - 0
carreras.php

@@ -0,0 +1,813 @@
+<?php
+require_once 'class/c_login.php';
+require_once 'include/bd_pdo.php';
+
+if (!isset($_SESSION['user'])){
+    header('Location: index.php');
+    exit;
+}
+else
+    $user = unserialize($_SESSION['user']);
+$user->access('facultades');
+if(!$user->admin && $user->acceso == 'n'){
+    header('Location: main.php?error=1');
+}else{
+    $user->print_to_log('Carreras');
+}
+if(!$user->admin && $user->facultad['facultad_id']!=$_GET['facultad']){
+    header('Location: carreras.php?facultad='.$user->facultad['facultad_id']);
+    $mal=true;
+}
+$mal=false;
+?>
+<!DOCTYPE html>
+<html lang="en">
+
+<head>
+    <meta charset="UTF-8">
+    <meta name="viewport" content="width=device-width, initial-scale=1.0">
+    <title>Carreras</title>
+    <link rel="stylesheet" href="css/jquery-ui.css">
+    <link rel="stylesheet" href="css/calendar.css">
+    <link rel="stylesheet" href="css/toggle.css" type="text/css">
+    <?php
+    include 'import/html_css_files.php';
+    ?>
+</head>
+
+<body>
+    <?php
+    if(isset($_GET['facultad'])){
+        $facultad=query("SELECT facultad_nombre FROM facultad WHERE facultad_id = :facultad", array(":facultad" => $_GET['facultad']), true);
+        $fs_carreras = query(
+            "SELECT * FROM fs_carreras(:idfacultad, null, null)",
+            array(':idfacultad' => $_GET['facultad']),
+            single:false
+        );
+    }
+    include "import/html_header.php";
+    html_header(
+        "CARRERAS | " . $facultad['facultad_nombre'],
+        "Gestión de Checador "
+    );
+    $user->access('facultades');
+    
+
+    $fs_niveles = query(
+        "SELECT * FROM nivel", null, false
+    );
+
+    $fs_periodos = query(
+        "SELECT * FROM fs_periodos(:idfacultad) WHERE estado = 'Activo' ",
+        array(':idfacultad' => $_GET['facultad']),
+        false
+    );
+
+    $fs_tiempoLic = query(
+        "SELECT * FROM fs_tiempo_checado(:idfacultad, 1)",
+        array(':idfacultad' => $_GET['facultad']),
+        true
+    );
+    $fs_tiempoPos = query(
+        "SELECT * FROM fs_tiempo_checado(:idfacultad, 2)",
+        array(':idfacultad' => $_GET['facultad']),
+        true
+    );
+    ?>
+    <main class="content marco">
+        <?php #if($mal==true){ ?>
+            <div class="row">
+                <div class="col-12 text-left">
+                    <a href="facultades.php" title="Volver">
+                        <button type="button" class="btn btn-outline-secondary"><span class="ing-regresar ing-fw"></span>Volver</button>
+                    </a>
+                </div>
+            </div>
+        <?php #} ?>
+        <div id="message"></div>
+        <ul class="nav nav-tabs mt-3" id="myTab" role="tablist">
+            <li class="nav-item" role="presentation">
+                <button class="nav-link active" id="periodo-tab" data-toggle="tab" data-target="#periodo" type="button" role="tab" aria-controls="periodo" aria-selected="true">Periodo</button>
+            </li>
+            <li class="nav-item" role="presentation">
+                <button class="nav-link" id="carrera-tab" data-toggle="tab" data-target="#carrera" type="button" role="tab" aria-controls="carrera" aria-selected="false">Carrera</button>
+            </li>
+            <li class="nav-item" role="presentation">
+                <button class="nav-link" id="carrera-tab" data-toggle="tab" data-target="#tiempos" type="button" role="tab" aria-controls="tiempos" aria-selected="false">Tiempos</button>
+            </li>
+        </ul>
+        <div class="tab-content" id="myTabContent">
+            <!-- PERIODOS -->
+            
+            <div class="tab-pane fade show active" id="periodo" role="tabpanel" aria-labelledby="periodo-tab">
+                <div class="row mt-3">
+                    <?php if($user->acceso == 'w')  {?>
+                    <div class="col-12 text-right">
+                        <button type="button" class="btn btn-outline-secondary" data-toggle="modal" data-target="#modal_periodo" data-tipo="1"><span class="ing-mas ing-fw"></span>Agregar periodo</button>
+                    </div>
+                    <?php }?>
+                </div>
+                <!-- Tabla -->
+                <div class="row mt-3">
+                    <div class="col-12 table-responsive">
+                        <table class="table table-sm table-striped table-white">
+                            <thead class="thead-dark">
+                                <tr>
+                                    <th>Estado</th>
+                                    <th>Nivel</th>
+                                    <th>Periodo</th>
+                                    <th>Inicio</th>
+                                    <th>Fin</th>
+                                    <?php if($user->acceso == 'w')  {?>
+                                    <th>Acciones</th>
+                                    <?php }?>
+                                </tr>
+                            </thead>
+                            <tbody>
+                                <?php foreach($fs_periodos as $periodo){
+                                    $title=$periodo['estado'];
+                                    if($title=='Activo')
+                                        $color="success";
+                                    else
+                                        $color="danger";
+                                    ?>
+                                <tr data-id="<?= $periodo['id']?>" id="<?= $periodo['id']?>" >
+                                    <td class="text-<?= $color ?> text-center" title="<?= $title?>">
+                                        <span class="ing-bullet"></span>
+                                    </td>
+                                    <td class="text-primary">
+                                        <?= $periodo['nivel']?>
+                                    </td>
+                                    <td class="text-primary">
+                                        <?= $periodo['periodo']?>
+                                    </td>
+                                    <td class="text-primary">
+                                        <?= $periodo['inicio']?>
+                                    </td>
+                                    <td class="text-primary">
+                                        <?= $periodo['fin']?>
+                                    </td>
+                                    <?php if($user->acceso == 'w')  {?>
+                                    <td class="text-center icono-acciones">
+                                        <a href="#" data-toggle="modal" data-target="#modal_periodo" data-tipo="2" title="Editar"><span class="ing-editar ing-fw"></span></a>
+                                    </td>
+                                    <?php }?>
+                                </tr>
+                                <?php } ?>
+                            </tbody>
+                        </table>
+                    </div>
+                </div>
+            </div>
+            <!-- CARRERAS -->
+            <div class="tab-pane fade" id="carrera" role="tabpanel" aria-labelledby="carrera-tab">
+                <div class="row mt-3">
+                    <?php if($user->acceso == 'w')  {?>
+                    <div class="col-12 text-right">
+                        <button type="button" class="btn btn-outline-secondary" data-toggle="modal" data-target="#modal" data-tipo="1"><span class="ing-mas ing-fw"></span>Crear carrera</button>
+                    </div>
+                    <?php }?>
+                </div>
+                <!-- Tabla -->
+                <div class="row mt-3">
+                    <div class="col-12 table-responsive">
+                        <table class="table table-sm table-striped table-white">
+                            <thead class="thead-dark">
+                                <tr>
+                                    <th>Estado</th>
+                                    <th>Nivel</th>
+                                    <th>Carrera</th>
+                                    <?php if($user->acceso == 'w')  {?>
+                                    <th>Acciones</th>
+                                    <?php }?>
+                                </tr>
+                            </thead>
+                            <tbody>
+                                <?php
+                                foreach($fs_carreras as $carrera){
+                                    $color = "danger";
+                                    $title = "Inactiva";
+                                    if($carrera["carrera_activa"]==1){
+                                    $color ="success";
+                                    $title="Activa";
+                                    }
+                                    $nivel='Licenciatura';
+                                    if($carrera['nivel_id']==2)
+                                        $nivel='Posgrado';
+                                ?>
+                                <tr data-id="<?php echo $carrera['carrera_id'];?>" id="<?php echo $carrera['carrera_id'];?>">
+                                    <td class="text-<?php echo $color;?> text-center" title="<?php echo $title;?>">
+                                        <span class="ing-bullet"></span>
+                                    </td>
+                                    <td class="text-primary"><?php echo $nivel;?></td>
+                                    <td class="text-primary"><?php echo $carrera["carrera_nombre"];?></td>
+                                    <?php if($user->acceso == 'w')  {?>
+                                    <td class="text-center icono-acciones">
+                                        <a href="#" data-toggle="modal" data-target="#modal" data-tipo="2" title="Editar"><span class="ing-editar ing-fw"></span></a>
+                                    </td>
+                                    <?php }?>
+                                </tr>
+                                <?php }?>
+                            </tbody>
+                        </table>
+                    </div>
+                </div>
+            </div>
+            <!-- Tiempos -->
+            <div class="tab-pane fade" id="tiempos" role="tabpanel" aria-labelledby="tiempos-tab">
+                <p class="mt-4">Asigna los minutos de tolerancia antes y después del horario de clase</p>
+                
+                <form action="" method="post" id="formaModalTiempos" onsubmit="return valida_camposT()">
+                    <input type="hidden" name="facultadT" id="facultadT">
+                    <h3 class="text-center">Licenciatura</h3>
+                <div class="row mt-3" style="border:solid 2px; border-radius: 8px;">
+                    <div class="offset-1 col-2 text-center">
+                        Antes
+                        <input id="antesL" name="antesL" type="number" class="form-control text-center" maxlenth="10">
+                            <div class="invalid-feedback">
+                                Debe ser un numero mayor que 0
+                            </div>
+                            min 
+                        </div>
+                        <div class="col-2 text-center bg-light mt-4 mb-4">
+                            <h5 class="mt-2">Hora de clase</h5>
+                        </div>
+                        <div class="col-2 text-center">
+                            Despues
+                            <input id="despuesL" name="despuesL" type="number" class="form-control text-center" maxlenth="10">
+                            <div class="invalid-feedback">
+                                Debe ser un numero mayor que 0
+                            </div>
+                            min
+                        </div>
+                        <div class="col-2 text-center retardoLic">
+                            Retardos
+                            <input id="retardoL" name="retardoL" type="number" class="form-control text-center" maxlenth="10">
+                            <div class="invalid-feedback">
+                                Debe ser un numero mayor que 0
+                            </div>
+                            min
+                        </div>
+                        <div class="col-3 text-center">
+                            ¿Tiene retardos?<br>
+                            <div class="custom-control custom-switch mt-2">
+                                <input type="checkbox" class="custom-control-input tipo-switch"  name="retardoLic" id="retardoLic" value="1">
+                                <label class="custom-control-label" for="retardoLic">Si</label>
+                            </div>
+                        </div>
+                    </div>
+                    <br><br>
+                    <h3 class="text-center">Posgrado</h3>
+                    <div class="row mt-3" style="border:solid 2px; border-radius: 8px;">
+                        <div class="offset-1 col-2 text-center">
+                            Antes
+                            <input id="antesP" name="antesP" type="number" class="form-control text-center" maxlenth="10">
+                            <div class="invalid-feedback">
+                                Debe ser un numero mayor que 0
+                            </div>
+                            min
+                        </div>
+                        <div class="col-2 text-center bg-light mt-4 mb-4">
+                            <h5 class="mt-2">Hora de clase</h5>
+                        </div>
+                        <div class="col-2 text-center">
+                            Despues
+                            <input id="despuesP" name="despuesP" type="number" class="form-control text-center" maxlenth="10">
+                            <div class="invalid-feedback">
+                                Debe ser un numero mayor que 0
+                            </div>
+                            min
+                        </div>
+                        <div class="col-2 text-center retardoPos">
+                            Retardo
+                            <input id="retardoP" name="retardoP" type="number" class="form-control text-center" maxlenth="10">
+                            <div class="invalid-feedback">
+                                Debe ser un numero mayor que 0
+                            </div>
+                            min
+                        </div>
+                        <div class="col-3 text-center">
+                            ¿Tiene retardos?<br>
+                            <div class="custom-control custom-switch mt-2">
+                                <input type="checkbox" class="custom-control-input tipo-switch"  name="retardoPos" id="retardoPos" value="1">
+                                <label class="custom-control-label" for="retardoPos"></label>
+                            </div>
+                        </div>
+                    </div>
+                    <br>
+                </form>
+                <div class="form-group row mt-3">
+                    <div class="offset-4 col-8">
+                        <button class="btn btn-outline-primary" id="submitBtnT">
+                            <span class="ing-aceptar ing-fw"></span> Guardar
+                        </button>
+                        <button type="reset" id="reset" class="btn btn-outline-danger" data-dismiss="modal">
+                            <span class="ing-cancelar ing-fw"></span> Limpiar
+                        </button>
+                    </div>
+                </div>
+            </div>
+        </div>
+    </main>
+    <!-- Footer -->
+    <?php
+    include "import/html_footer.php";
+    ?>
+    <!-- Modal -->
+    <div class="modal fade" id="modal_periodo" tabindex="-1" role="dialog" arialabelledby="modal" aria-hidden="true">
+        <div class="modal-dialog modal-dialog-centered" role="document">
+            <div class="modal-content">
+                <div class="modal-header">
+                    <h4 class="col-12 modal-title text-center">
+                        <span id="modalLabelP">
+                            Editar periodo
+                        </span>
+                        <button type="button" class="close text-white" data-dismiss="modal" aria-label="close">
+                            <span aria-hidden="true">&times;</span>
+                        </button>
+                    </h4>
+                </div>
+                <div class="modal-body">
+                    <form action="" method="post" id="formaModalP" onsubmit="return valida_camposP()">
+                        <input type="hidden" name="idP" id="idP">
+                        <input type="hidden" name="facultadP" id="facultadP" value="<?php echo $_GET['facultad']; ?>">
+                        <div class="form-box">
+                            <div class="form-group row">
+                                <label for="nombreP" class="col-4 col-form-label">Nombre *</label>
+                                <div class="col-8">
+                                    <input id="nombreP" name="nombreP" type="text" class="form-control" maxlength="100">
+                                    <div class="invalid-feedback" id="nombreP-error">Campo obligatorio</div>
+                                </div>
+                            </div>
+                            <div class="form-group row">
+                                <label for="inicio" class="col-4 col-form-label">Fecha de inicio *</label>
+                                <div class="col-8">
+                                    <input id="fecha_inicial" name="fecha_inicial" type="text" class="form-control date-picker" placeholder="dd/mm/aaaa" maxlength="10" required="required" readonly="">
+                                    <div class="invalid-feedback">Debe seleccionar una fecha</div>
+                                </div>
+                            </div>
+                            <div class="form-group row">
+                                <label for="fin" class="col-4 col-form-label">Fecha de fin *</label>
+                                <div class="col-8">
+                                    <input id="fecha_final" name="fecha_final" type="text" class="form-control date-picker" placeholder="dd/mm/aaaa" maxlength="10" required="required" readonly="">
+                                    <div class="invalid-feedback">Debe seleccionar una fecha</div>
+                                </div>
+                            </div>
+                            <div class="form-group row">
+                                <label for="nivelP" class="col-4 col-form-label">Nivel *</label>
+                                <div class="col-8">
+                                    <div class="datalist datalist-select mb-1 w-100">
+                                        <div class="datalist-input">Mostrar todos</div>
+                                        <span class="ing-buscar icono"></span>
+                                        <ul style="display:none">
+                                            <?php foreach($fs_niveles as $pnivel){?>
+                                                <li data-id="<?php echo $pnivel['nivel_id']?>" class="pl-4"><?php echo $pnivel['nivel_nombre'] ?></li>
+                                            <?php }?>
+                                        </ul>
+                                        <input type="hidden" id="nivelP" name="nivelP" value="">
+                                    </div>
+                                    <div class="invalid-feedback">Debe seleccionar un nivel</div>
+                                </div>
+                            </div>
+                            <div class="form-group row">
+                                <label for="estadoP" class="col-4 col-form-label">Estado *</label>
+                                <div class="col-4">
+                                    <div class="form-check form-check-inline">
+                                        <input class="form-check-input radio-lg" type="radio" id="estado_activoP" name="estadoP" value="1" checked="checked">
+                                        <label for="estado_activoP" class="col-form-label">Activo</label>
+                                    </div>
+                                </div>
+                                <div class="col-4">
+                                    <div class="form-check form-check-inline">
+                                        <input class="form-check-input radio-lg" type="radio" id="estado_inactivoP" name="estadoP" value="2">
+                                        <label for="estado_inactivoP" class="col-form-label">Inactivo</label>
+                                    </div>
+                                </div>
+                            </div>
+                            <div class="from-group row">
+                                <div class="offset-4 col-8">
+                                    <button type="submit" class="btn btn-outline-primary" id="submitBtnP" data-tipo="1">
+                                        <span class="ing-aceptar ing-fw"></span> Guardar
+                                    </button>
+                                    <button type="reset" class="btn btn-outline-danger" data-dismiss="modal">
+                                        <span class="ing-cancelar ing-fw"></span> Cancelar
+                                    </button>
+                                </div>
+                            </div>
+                        </div>
+                    </form>
+                </div>
+            </div>
+        </div>
+    </div>
+    <div class="modal fade" id="modal" tabindex="-1" role="dialog" arialabelledby="modal" aria-hidden="true">
+        <div class="modal-dialog modal-dialog-centered" role="document">
+            <div class="modal-content">
+                <div class="modal-header">
+                    <h4 class="col-12 modal-title text-center">
+                        <span id="modalLabel">
+                            Editar nombre de Carrera
+                        </span>
+                        <button type="button" class="close text-white" data-dismiss="modal" aria-label="close">
+                            <span aria-hidden="true">&times;</span>
+                        </button>
+                    </h4>
+                </div>
+                <div class="modal-body">
+                    <form action="" method="post" id="formaModal" onsubmit="return valida_campos()">
+                        <input type="hidden" name="id" id="id">
+                        <input type="hidden" name="facultad" id="facultad" value="<?php echo $_GET['facultad']; ?>">
+                        <div class="form-box">
+                            <div class="form-group row">
+                                <label for="nivel" class="col-4 col-form-label">Nivel *</label>
+                                <div class="col-8">
+                                    <div class="datalist datalist-select mb-1 w-100">
+                                        <div class="datalist-input">Mostrar todos</div>
+                                        <span class="ing-buscar icono"></span>
+                                        <ul style="display:none">
+                                            
+                                            <?php foreach($fs_niveles as $pnivel){?>
+                                                <li data-id="<?php echo $pnivel['nivel_id']?>" class="pl-4"><?php echo $pnivel['nivel_nombre'] ?></li>
+                                            <?php }?>
+                                        </ul>
+                                        <input type="hidden" id="nivel" name="nivel" value="">
+                                    </div>
+                                </div>
+                            </div>
+                            <div class="form-group row">
+                                <label for="nombre" class="col-4 col-form-label">Nombre *</label>
+                                <div class="col-8">
+                                    <input id="nombre" name="nombre" type="text" class="form-control" maxlength="100">
+                                    <div class="invalid-feedback" id="nombre-error">Campo obligatorio</div>
+                                </div>
+                            </div>
+                            <div class="form-group row">
+                                <label for="estado" class="col-4 col-form-label">Estado *</label>
+                                <div class="col-4">
+                                    <div class="form-check form-check-inline">
+                                        <input class="form-check-input radio-lg" type="radio" id="estado_activo" name="estado" value="1" checked="checked">
+                                        <label for="estado_activo" class="col-form-label">Activo</label>
+                                    </div>
+                                </div>
+                                <div class="col-4">
+                                    <div class="form-check form-check-inline">
+                                        <input class="form-check-input radio-lg" type="radio" id="estado_inactivo" name="estado" value="0">
+                                        <label for="estado_inactivo" class="col-form-label">Inactivo</label>
+                                    </div>
+                                </div>
+                            </div>
+                            <div class="from-group row">
+                                <div class="offset-4 col-8">
+                                    <button type="submit" class="btn btn-outline-primary" id="submitBtn" data-tipo="1">
+                                        <span class="ing-aceptar ing-fw"></span> Guardar
+                                    </button>
+                                    <button type="reset" class="btn btn-outline-danger" id="reset" data-dismiss="modal">
+                                        <span class="ing-cancelar ing-fw"></span> Cancelar
+                                    </button>
+                                </div>
+                            </div>
+                        </div>
+                    </form>
+                </div>
+            </div>
+        </div>
+    </div>
+    <script src="js/jquery.min.js"></script>
+    <script src="js/jquery-ui.js"></script>
+    <script src="js/bootstrap/bootstrap.min.js"></script>
+    <script src="js/datalist.js"></script>
+    <script src="js/datepicker-es.js"></script>
+    <script src="js/toggle.js"></script>
+    <?php
+        require_once 'js/messages.php';
+    ?>
+    <script>
+        $('#retardoLic').change(function(){
+            if($(this).is(':checked')){
+                $('.retardoLic').show();
+            }
+            else{
+                $('.retardoLic').hide();
+                $('#retardoL').val("0");
+            }
+        });
+        $('#retardoPos').change(function(){
+            if($(this).is(':checked')){
+                $('.retardoPos').show();
+            }
+            else{
+                $('.retardoPos').hide();
+                $('#retardoP').val("0");
+            }
+        });
+        $('#reset').on('click', function(){
+            $('#antesL').val("<?= -1*($fs_tiempoLic['desde_asistencia'] ?? 0) ?>");
+            $('#despuesL').val("<?= ($fs_tiempoLic['hasta_asistencia'] ?? 1)-1 ?>");
+            $('#retardoL').val("<?= ($fs_tiempoLic['hasta_retardo'] ?? 0) - ($fs_tiempoLic['hasta_asistencia'] ?? 0) ?>");
+            $('#antesP').val("<?= -1*($fs_tiempoPos['desde_asistencia'] ?? 0) ?>");
+            $('#despuesP').val("<?= ($fs_tiempoPos['hasta_asistencia'] ?? 1)-1 ?>");
+            $('#retardoP').val("<?= ($fs_tiempoPos['hasta_retardo'] ?? 0) - ($fs_tiempoPos['hasta_asistencia'] ?? 0) ?>");
+            <?php
+                if(($fs_tiempoLic['hasta_asistencia'] ?? 0) == ($fs_tiempoLic['hasta_retardo'] ?? 0)){ ?>
+                    $('.retardoLic').hide();
+                    $('#retardoL').val("0");
+                    $('#retardoLic').prop("checked", false).change();
+                    <?php }
+                else{ ?>
+                        $('#retardoLic').prop("checked", true).change();
+                <?php }
+                if(($fs_tiempoPos['hasta_asistencia'] ?? 0) == ($fs_tiempoPos['hasta_retardo'] ?? 0)){ ?>
+                    $('.retardoPos').hide();
+                    $('#retardoP').val("0");
+                    $('#retardoPos').prop("checked", false).change();
+                <?php }
+                else{ ?>
+                        $('#retardoPos').prop("checked", true).change();
+                <?php }
+                ?>
+            $('#antesL').removeClass("is-invalid");
+            $('#despuesL').removeClass("is-invalid");
+            $('#retardoL').removeClass("is-invalid");
+            $('#antesP').removeClass("is-invalid");
+            $('#despuesP').removeClass("is-invalid");
+            $('#retardoP').removeClass("is-invalid");
+        });
+        $(".date-picker").datepicker($.datepicker.regional["es"]);
+        $(".date-picker").datepicker({
+            dateFormat: "dd/mm/yyyy",
+            changeMonth: true,
+        });
+
+        $('#submitBtnT').on('click', function(){
+            //$('#antesL').addClass("is-invalid");
+            $('#formaModalTiempos').submit();
+        });
+
+        var today = new Date();
+
+        function valida_camposT(){
+            var error=false;
+            if($('#antesL').val()==""){
+                $('#antesL').addClass("is-invalid");
+                error=true;
+            }
+            if($('#antesL').val()<0){
+                $('#antesL').addClass("is-invalid");
+                error=true;
+            }
+            if(isNaN($('#antesL').val())){
+                $('#antesL').addClass("is-invalid");
+                error=true;
+            }
+            if($('#despuesL').val()==""){
+                $('#despuesL').addClass("is-invalid");
+                error=true;
+            }
+            if($('#despuesL').val()<0){
+                $('#despuesL').addClass("is-invalid");
+                error=true;
+            }
+            if(isNaN($('#despuesL').val())){
+                $('#despuesL').addClass("is-invalid");
+                error=true;
+            }
+            if($('#retardoL').val()==""){
+                $('#retardoL').addClass("is-invalid");
+                error=true;
+            }
+            if($('#retardoL').val()<0){
+                $('#retardoL').addClass("is-invalid");
+                error=true;
+            }
+            if(isNaN($('#retardoL').val())){
+                $('#retardoL').addClass("is-invalid");
+                error=true;
+            }
+            if($('#antesP').val()==""){
+                $('#antesP').addClass("is-invalid");
+                error=true;
+            }
+            if(isNaN($('#antesP').val())){
+                $('#antesP').addClass("is-invalid");
+                error=true;
+            }
+            if($('#despuesP').val()==""){
+                $('#despuesP').addClass("is-invalid");
+                error=true;
+            }
+            if(isNaN($('#despuesP').val())){
+                $('#despuesP').addClass("is-invalid");
+                error=true;
+            }
+            if($('#retardoP').val()==""){
+                $('#retardoP').addClass("is-invalid");
+                error=true;
+            }
+            if(isNaN($('#retardoP').val())){
+                $('#retardoP').addClass("is-invalid");
+                error=true;
+            }
+            if(!error){
+                $('#formaModalTiempos').prop("action", "./action/action_tiempos_update.php");
+            }else{
+                return false;
+            }
+        }
+
+        function valida_camposP(){
+            var error=false;
+            if($("#fecha_inicial").val()==""){
+                $("#fecha_inicial").addClass("is-invalid");
+                error=true;
+            }
+            if($("#fecha_final").val()==""){
+                $("#fecha_final").addClass("is-invalid");
+                error=true;
+            }
+            if($("#nombreP").val()==""){
+                $("#nombreP").addClass("is-invalid");
+                $("#nombreP-error").html("Campo obligatorio");
+                error=true;
+            }
+            if($("#nombreP").val()[0]==" "){
+                $("#nombreP").addClass("is-invalid");
+                $("#nombreP-error").html("No puede haber espacios al inicio");
+                error=true;
+            }
+            if($("#nivelP").val()==""){
+                error=true;
+                $("#nivelP").addClass("is-invalid");
+            }
+            if(error){
+                return false;
+            }else{
+                var btn = $("#submitBtnP");
+                if(btn.data("tipo")==2)//update
+                    $("#formaModalP").prop("action", "./action/action_periodos_update.php");
+                else{//insert
+                    $("#formaModalP").prop("action", "./action/action_periodos_insert.php");
+                }
+            }
+        }
+
+        <?php if(!$fs_carreras && !$fs_periodos){ ?>
+            triggerMessage("No se encontraron carreras ni periodos en esta facultad", "Error");
+        <?php } else if(!$fs_carreras){?>
+            triggerMessage("No se encontraron carreras en esta facultad", "Error");
+        <?php } else if(!$fs_periodos){?>
+            triggerMessage("No se encontraron periodos en esta facultad", "Error");
+        <?php }?>
+
+        function valida_campos(){
+            var error=false;
+            if($("#nombre").val()==""){
+                $("#nombre").addClass("is-invalid");
+                $("#nombre-error").html("Campo obligatorio");
+                error=true;
+            }
+            if($("#nombre").val()[0]==" "){
+                $("#nombre").addClass("is-invalid");
+                $("#nombre-error").html("No puede haber espacios al inicio");
+                error=true;
+            }
+            if($("#nivel").val()==""){
+                error=true;
+            }
+            if($('#estado_activo').prop('checked') == false && $('#estado_inactivo').prop('checked') == false){
+                error=true;
+            }
+            if(error){
+                return false;
+            }else{
+                var btn = $('#submitBtn');
+                if(btn.data("tipo")==2)//update
+                    $("#formaModal").prop("action", "./action/action_carreras_update.php");
+                else//insert
+                    $("#formaModal").prop("action", "./action/action_carreras_insert.php");
+            }
+        }
+
+        $('#modal_periodo').on('show.bs.modal', function(event){//datos periodo 
+            var button = $(event.relatedTarget);
+            var tipo = button.data('tipo');
+            $("#nombreP").removeClass("is-invalid");
+            if(tipo==1){//crear
+                $('#modalLabelP').html("Agregar periodo");
+                $("#submitBtnP").data("tipo", 1);
+                $("#fecha_inicial").datepicker("setDate", today);
+                $("#fecha_final").datepicker("setDate", today);
+                $("#nombreP").val("");
+                $("#estado_activoP").prop("checked", true);
+                setDatalist("#nivelP",1);
+                $("li").removeClass("selected");
+                var fi = $("#fecha_inicial").datepicker("getDate");
+                //$("#fecha_final").datepicker("option", "minDate", fi);
+            }else{//editar
+                $('#modalLabelP').html("Editar periodo");
+                $("#submitBtnP").data("tipo", 2);
+                var id = $(event.relatedTarget).parents("tr").data("id");
+                var fac = $("#facultadP").val();
+                $.ajax({
+                    url:"action/action_periodos_select.php",
+                    type:"post",
+                    dataType:"json",
+                    data:{idfacultad: fac, idperiodo: id},
+                    success:function(result){
+                        //console.log(result);
+                        $("#idP").val(result["id"]);
+                        $("#facultadP").val(result["facultad_id"]);
+                        $("#nombreP").val(result["periodo"]);
+                        var date = new Date(result["inicio"])
+                        date.setDate(date.getDate() + 1);
+                        $("#fecha_inicial").datepicker("setDate", date);
+                        date = new Date(result["fin"])
+                        date.setDate(date.getDate() + 1);
+                        $("#fecha_final").datepicker("setDate", date);
+                        //$(".datalist-input").html(result["nivel"]);
+                        setDatalist("#nivelP",result["nivel_id"]);
+                        var fi = $("#fecha_inicial").datepicker("getDate");
+                        //$("#fecha_final").datepicker("option", "minDate", fi);
+                        var ff = $("#fecha_final").datepicker("getDate");
+                        //$("#fecha_inicial").datepicker("option", "maxDate", ff);
+                        if(result['estado']=="Activo"){
+                            $('#estado_activoP').prop('checked', true);
+                        }else{
+                            $('#estado_inactivoP').prop('checked', true);
+                        }
+                    },
+                    error: function(){console.log("Error")}
+                });
+            }
+        })
+
+        $('#modal').on('show.bs.modal', function(event){
+            var button = $(event.relatedTarget);
+            var tipo = button.data('tipo');
+            var modal = $(this);
+            $("#nombre").removeClass("is-invalid");
+            if(tipo == 1){//crear
+                $("#submitBtn").data('tipo', 1);
+                $("#modalLabel").html("Crear Carrera");
+                $("#nombre").val("");
+                $('#estado_activo').prop('checked', true);
+                $('li').removeClass('selected');
+                $(".datalist-input").html("Mostrar todas");
+                $("#nivel").val("");
+            }else{//editar
+                $("#submitBtn").data('tipo', 2);
+                $("#modalLabel").html("Editar Carrera");
+                $("#nombre").val("");
+                $('#estado_activo').prop('checked', true);
+                var id = $(event.relatedTarget).parents("tr").data("id");
+                var fac = $("#facultad").val();
+                $.ajax({
+                    url:"action/action_carreras_select.php",
+                    type:"post",
+                    dataType:"json",
+                    data:{idfacultad: fac, idcarrera: id},
+                    success:function(result){
+                        //console.log(result);
+                        $("#id").val(result["carrera_id"]);
+                        $("#nombre").val(result["carrera_nombre"])
+                        if(result['carrera_activa']==1){
+                            $('#estado_activo').prop('checked', true);
+                        }else{
+                            $('#estado_inactivo').prop('checked', true);
+                        }
+                        setDatalist("#nivel", result["nivel_id"]);
+                    },
+                    error: function(){console.log("Error")}
+                });
+            }
+        });
+        $(document).ready(function(){
+            $('#antesL').val("<?= -1*($fs_tiempoLic['desde_asistencia'] ?? 0) ?>");
+            $('#despuesL').val("<?= ($fs_tiempoLic['hasta_asistencia'] ?? 1)-1 ?>");
+            $('#retardoL').val("<?= ($fs_tiempoLic['hasta_retardo'] ?? 1) - ($fs_tiempoLic['hasta_asistencia'] ?? 0) ?>");
+            $('#antesP').val("<?= -1*($fs_tiempoPos['desde_asistencia'] ?? 0) ?>");
+            $('#despuesP').val("<?= ($fs_tiempoPos['hasta_asistencia'] ?? 1) -1 ?>");
+            $('#retardoP').val("<?= ($fs_tiempoPos['hasta_retardo'] ?? 1) - ($fs_tiempoPos['hasta_asistencia'] ?? 0) ?>");
+            $('#facultadT').val("<?= $_GET['facultad'] ?>");
+            <?php
+                if(($fs_tiempoLic['hasta_asistencia'] ?? 0) == ($fs_tiempoLic['hasta_retardo'] ?? 0)){ ?>
+                    $('.retardoLic').hide();
+                    $('#retardoL').val("0");
+                    $('#retardoLic').prop("checked", false).change();
+                    <?php }
+                else{ ?>
+                        $('#retardoLic').prop("checked", true).change();
+                    <?php }
+                if(($fs_tiempoPos['hasta_asistencia'] ?? 0) == ($fs_tiempoPos['hasta_retardo'] ?? 0)){ ?>
+                    $('.retardoPos').hide();
+                    $('#retardoP').val("0");
+                    $('#retardoPos').prop("checked", false).change();
+                <?php }
+                else{ ?>
+                        $('#retardoPos').prop("checked", true).change();
+                <?php }
+            ?>
+        });
+    </script>
+</body>
+
+</html>

+ 74 - 0
class/c_logasistencia.php

@@ -0,0 +1,74 @@
+<?php
+/* 
+ * Objeto para leer y escribir datos de log de intentos de asistencia realizadas por el usuario
+ */
+
+namespace classes;
+
+define("MAX_LINES", 200);
+class LogAsistencias
+{
+    //put your code here
+    private $file, $month, $year;
+    private $dir;
+
+    function __construct($ruta = null)
+    {
+        // die ruta
+        $this->month = date("m");
+        $this->year = date("Y");
+        $this->dir = ($ruta ?? '') . "log/";
+        $this->updateFilename();
+    }
+
+    function setMes(string $mes)
+    {
+        $this->month = $mes;
+        $this->updateFilename();
+    }
+    function setAno(string $ano)
+    {
+        $this->year = $ano;
+        $this->updateFilename();
+    }
+
+    private function updateFilename()
+    {
+        $this->file = "asistencias_" . $this->year . "_" . $this->month . ".log";
+    }
+    private function cleanLog($text)
+    { //remueve || de los textos
+        return trim(str_ireplace("||", "", $text));
+    }
+
+    function appendLog($claveULSA, $nombre, $desc)
+    {
+        $filename = $this->dir . $this->file;
+        if (!file_exists($this->dir)) {
+            echo "$this->dir no existe, creando...";
+            mkdir($this->dir, 0755, true);
+        }
+        if (file_exists($this->dir)) {
+            $data = date('Y-m-d H:i:s') . "||" . $this->cleanLog($claveULSA) . "||" . $this->cleanLog($desc) . "||" . $this->cleanLog($nombre) . "\n";
+            /*echo*/
+            file_put_contents($filename, $data, FILE_APPEND);
+        }
+    }
+    function getLog($mes = "", $ano = "")
+    {
+        if ($mes != "") $this->setMes($mes);
+        if ($ano != "") $this->setAno($ano);
+        $filename = $this->dir . $this->file;
+        if (file_exists($filename)) {
+            //return array_slice(file ($filename , FILE_SKIP_EMPTY_LINES) , -10);
+            $lines = file($filename, FILE_SKIP_EMPTY_LINES);
+            //echo "antes: ".count($lines);
+            if (count($lines) > MAX_LINES) {
+                $lines = array_slice($lines, MAX_LINES * (-1));
+            }
+            //echo "despues: ".count($lines);
+            return $lines;
+        } else
+            return array();
+    }
+}

+ 86 - 0
class/c_login.php

@@ -0,0 +1,86 @@
+<?php
+require_once ($ruta ?? '') . "include/bd_pdo.php";
+require_once ($ruta ?? '') . "class/c_logasistencia.php";
+require_once ($ruta ?? '') . "include/nusoap/nusoap.php";
+
+session_start();
+class Login
+{
+    public string $acceso;
+    public function __construct(public array $user, public array $facultad, public array $rol, public bool $admin, public ?int $periodo)
+    {
+    }
+    public function print_to_log(string $desc, array $old = null, array $new = null): void
+    {
+        $log = new classes\LogAsistencias($_ENV["RUTA_RAIZ"]);
+        if ($old) $desc .= " |#| OLD:" . json_encode($old);
+        if ($new) $desc .= " |#| NEW:" . json_encode($new);
+        $log->appendLog($this->user["id"], $this->user["nombre"], $desc);
+    }
+    public function access(string $pagina = null): void
+    {
+        if ($this->admin) {
+            $this->acceso = "w";
+            return;
+        }
+
+        # print_r( $access );
+        $this->acceso =  query('SELECT tipo FROM PERMISO_VIEW WHERE ID = :usr AND PAGINA_RUTA ILIKE :ruta', array(
+            ':usr' => $this->user["id"],
+            ':ruta' => $pagina ?? substr(basename($_SERVER['PHP_SELF']), 0, -4)
+        ))["tipo"] ?? 'n';
+    }
+    public function __toString(): string
+    {
+        return "Usuario: {$this->user["nombre"]} ({$this->user["id"]})";
+    }
+    private static function validaUsuario($user, $pass): bool
+    {
+        file_put_contents('php://stderr', $user);
+        if (in_array($user, ['ad017045']) and $pass == "admin") return true    ;
+        $client = new nusoap_client('http://200.13.89.2/validacion.php?wsdl', 'wsdl');
+        $error  = $client->getError();
+
+        if ($error) return false;
+
+        $pass = utf8_decode($pass);
+        $result = $client->call("valida_user", array($user, $pass));
+
+        if ($client->fault) return false;
+            
+        return $result;
+    }
+    public static function validUser(string $user, string $pass): Login | false
+    {
+        $fs_validaclaveulsa = query(
+            'SELECT * FROM FS_VALIDACLAVEULSA(:usr)', [':usr' => $user]
+        );
+        
+        if (empty($fs_validaclaveulsa["id"])) return false;
+        #die (Login::validaUsuario($user, $pass));
+        if (!Login::validaUsuario($user, $pass)) return false;
+ 
+        $user = array(
+            'id' => $fs_validaclaveulsa["id"],
+            'nombre' => $fs_validaclaveulsa["nombre"],
+        );
+        $facultades = query("SELECT FACULTAD_ID id, FACULTAD f FROM FS_PERIODO WHERE ID = :id", [':id' => $fs_validaclaveulsa["periodo_id"]]);
+        $facultad = array(
+            'facultad_id' => $fs_validaclaveulsa["facultad_id"] ?? $facultades["id"],
+            'facultad' => $fs_validaclaveulsa["facultad"] ?? $facultades["f"],
+        );
+        $rol = array(
+            'id' => $fs_validaclaveulsa["rol_id"],
+            'rol' => $fs_validaclaveulsa["rol"]
+        );
+
+        $admin = $fs_validaclaveulsa["is_admin"];
+        $periodo = $fs_validaclaveulsa["periodo_id"];
+        return new Login($user, $facultad, $rol, $admin, $periodo);
+    }
+    public static function log_out(): void
+    {
+        session_start();
+        session_destroy();
+    }
+}

+ 15 - 0
class/c_menu.php

@@ -0,0 +1,15 @@
+<?php
+class Menu {
+    private array $menu = [];
+
+    public function __construct() {
+        $this->conn = new Connection();
+    }
+
+    public function getMenu() {
+        $sql = "SELECT * FROM menu";
+        $result = $this->conn->getConnection()->query($sql);
+        $this->menu = $result->fetchAll();
+        return $this->menu;
+    }
+}

+ 57 - 0
class/connection.php

@@ -0,0 +1,57 @@
+<?php
+define("DB_HOST",($_SERVER["SERVER_NAME"] == "localhost") ? "200.13.89.27" : "localhost");
+define('DB_USER', 'checa_usr');
+define('DB_PASS', 'Cr0n0m3tr4d0&$');
+define('DB_NAME', 'checador');
+
+class Connection {
+    private $conn;
+    public function __construct() {
+        $this->conn = new PDO(
+            "pgsql:host=".DB_HOST.";dbname=".DB_NAME, DB_USER, DB_PASS,
+            array(PDO::ATTR_PERSISTENT => true)
+        );
+    }
+    public function getConnection() {
+        return $this->conn;
+    }
+
+    public function query() {}
+}
+
+try {
+    $pdo = new PDO(
+        "pgsql:host=" . DB_HOST . ";dbname=" . DB_NAME, DB_USER, DB_PASS,
+        array(
+            PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
+            PDO::ATTR_PERSISTENT => true
+        )
+    );
+} catch (PDOException $e) {
+    print "Error!: " . $e->getMessage() . "<br/>";
+    die();
+}
+
+function SQL(string $sql, array $params = [])
+{
+    global $pdo;
+    $stmt = $pdo->prepare($sql);
+    foreach ($params as $key => $value) {
+        // bind Parameter
+        $stmt->bindParam($key, $value);
+    }
+    $stmt->execute($params);
+    return $stmt->fetchAll();
+}
+
+function filter_by(array $array, array $fields): array
+{
+    $result = [];
+    foreach ($array as $key => $value) {
+        $result[$key] = [];
+        foreach ($fields as $field) {
+            $result[$key][$field] = $value[$field];
+        }
+    }
+    return $result;
+}

+ 0 - 0
coldbypass.php


+ 7 - 0
composer.json

@@ -0,0 +1,7 @@
+{
+    "require": {
+        "vlucas/phpdotenv": "^5.5",
+        "phpoffice/phpspreadsheet": "^1.25",
+        "seinopsys/postgresql-database-class": "^3.1"
+    }
+}

+ 1155 - 0
composer.lock

@@ -0,0 +1,1155 @@
+{
+    "_readme": [
+        "This file locks the dependencies of your project to a known state",
+        "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
+        "This file is @generated automatically"
+    ],
+    "content-hash": "5e701c768afe8ce8feabe1b539fa7234",
+    "packages": [
+        {
+            "name": "ezyang/htmlpurifier",
+            "version": "v4.16.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/ezyang/htmlpurifier.git",
+                "reference": "523407fb06eb9e5f3d59889b3978d5bfe94299c8"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/ezyang/htmlpurifier/zipball/523407fb06eb9e5f3d59889b3978d5bfe94299c8",
+                "reference": "523407fb06eb9e5f3d59889b3978d5bfe94299c8",
+                "shasum": ""
+            },
+            "require": {
+                "php": "~5.6.0 || ~7.0.0 || ~7.1.0 || ~7.2.0 || ~7.3.0 || ~7.4.0 || ~8.0.0 || ~8.1.0 || ~8.2.0"
+            },
+            "require-dev": {
+                "cerdic/css-tidy": "^1.7 || ^2.0",
+                "simpletest/simpletest": "dev-master"
+            },
+            "suggest": {
+                "cerdic/css-tidy": "If you want to use the filter 'Filter.ExtractStyleBlocks'.",
+                "ext-bcmath": "Used for unit conversion and imagecrash protection",
+                "ext-iconv": "Converts text to and from non-UTF-8 encodings",
+                "ext-tidy": "Used for pretty-printing HTML"
+            },
+            "type": "library",
+            "autoload": {
+                "files": [
+                    "library/HTMLPurifier.composer.php"
+                ],
+                "psr-0": {
+                    "HTMLPurifier": "library/"
+                },
+                "exclude-from-classmap": [
+                    "/library/HTMLPurifier/Language/"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "LGPL-2.1-or-later"
+            ],
+            "authors": [
+                {
+                    "name": "Edward Z. Yang",
+                    "email": "admin@htmlpurifier.org",
+                    "homepage": "http://ezyang.com"
+                }
+            ],
+            "description": "Standards compliant HTML filter written in PHP",
+            "homepage": "http://htmlpurifier.org/",
+            "keywords": [
+                "html"
+            ],
+            "support": {
+                "issues": "https://github.com/ezyang/htmlpurifier/issues",
+                "source": "https://github.com/ezyang/htmlpurifier/tree/v4.16.0"
+            },
+            "time": "2022-09-18T07:06:19+00:00"
+        },
+        {
+            "name": "graham-campbell/result-type",
+            "version": "v1.1.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/GrahamCampbell/Result-Type.git",
+                "reference": "a878d45c1914464426dc94da61c9e1d36ae262a8"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/GrahamCampbell/Result-Type/zipball/a878d45c1914464426dc94da61c9e1d36ae262a8",
+                "reference": "a878d45c1914464426dc94da61c9e1d36ae262a8",
+                "shasum": ""
+            },
+            "require": {
+                "php": "^7.2.5 || ^8.0",
+                "phpoption/phpoption": "^1.9"
+            },
+            "require-dev": {
+                "phpunit/phpunit": "^8.5.28 || ^9.5.21"
+            },
+            "type": "library",
+            "autoload": {
+                "psr-4": {
+                    "GrahamCampbell\\ResultType\\": "src/"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Graham Campbell",
+                    "email": "hello@gjcampbell.co.uk",
+                    "homepage": "https://github.com/GrahamCampbell"
+                }
+            ],
+            "description": "An Implementation Of The Result Type",
+            "keywords": [
+                "Graham Campbell",
+                "GrahamCampbell",
+                "Result Type",
+                "Result-Type",
+                "result"
+            ],
+            "support": {
+                "issues": "https://github.com/GrahamCampbell/Result-Type/issues",
+                "source": "https://github.com/GrahamCampbell/Result-Type/tree/v1.1.0"
+            },
+            "funding": [
+                {
+                    "url": "https://github.com/GrahamCampbell",
+                    "type": "github"
+                },
+                {
+                    "url": "https://tidelift.com/funding/github/packagist/graham-campbell/result-type",
+                    "type": "tidelift"
+                }
+            ],
+            "time": "2022-07-30T15:56:11+00:00"
+        },
+        {
+            "name": "maennchen/zipstream-php",
+            "version": "2.2.1",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/maennchen/ZipStream-PHP.git",
+                "reference": "211e9ba1530ea5260b45d90c9ea252f56ec52729"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/maennchen/ZipStream-PHP/zipball/211e9ba1530ea5260b45d90c9ea252f56ec52729",
+                "reference": "211e9ba1530ea5260b45d90c9ea252f56ec52729",
+                "shasum": ""
+            },
+            "require": {
+                "myclabs/php-enum": "^1.5",
+                "php": "^7.4 || ^8.0",
+                "psr/http-message": "^1.0",
+                "symfony/polyfill-mbstring": "^1.0"
+            },
+            "require-dev": {
+                "ext-zip": "*",
+                "guzzlehttp/guzzle": "^6.5.3 || ^7.2.0",
+                "mikey179/vfsstream": "^1.6",
+                "php-coveralls/php-coveralls": "^2.4",
+                "phpunit/phpunit": "^8.5.8 || ^9.4.2",
+                "vimeo/psalm": "^4.1"
+            },
+            "type": "library",
+            "autoload": {
+                "psr-4": {
+                    "ZipStream\\": "src/"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Paul Duncan",
+                    "email": "pabs@pablotron.org"
+                },
+                {
+                    "name": "Jonatan Männchen",
+                    "email": "jonatan@maennchen.ch"
+                },
+                {
+                    "name": "Jesse Donat",
+                    "email": "donatj@gmail.com"
+                },
+                {
+                    "name": "András Kolesár",
+                    "email": "kolesar@kolesar.hu"
+                }
+            ],
+            "description": "ZipStream is a library for dynamically streaming dynamic zip files from PHP without writing to the disk at all on the server.",
+            "keywords": [
+                "stream",
+                "zip"
+            ],
+            "support": {
+                "issues": "https://github.com/maennchen/ZipStream-PHP/issues",
+                "source": "https://github.com/maennchen/ZipStream-PHP/tree/2.2.1"
+            },
+            "funding": [
+                {
+                    "url": "https://opencollective.com/zipstream",
+                    "type": "open_collective"
+                }
+            ],
+            "time": "2022-05-18T15:52:06+00:00"
+        },
+        {
+            "name": "markbaker/complex",
+            "version": "3.0.1",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/MarkBaker/PHPComplex.git",
+                "reference": "ab8bc271e404909db09ff2d5ffa1e538085c0f22"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/MarkBaker/PHPComplex/zipball/ab8bc271e404909db09ff2d5ffa1e538085c0f22",
+                "reference": "ab8bc271e404909db09ff2d5ffa1e538085c0f22",
+                "shasum": ""
+            },
+            "require": {
+                "php": "^7.2 || ^8.0"
+            },
+            "require-dev": {
+                "dealerdirect/phpcodesniffer-composer-installer": "^0.7.0",
+                "phpcompatibility/php-compatibility": "^9.0",
+                "phpunit/phpunit": "^7.0 || ^8.0 || ^9.3",
+                "squizlabs/php_codesniffer": "^3.4"
+            },
+            "type": "library",
+            "autoload": {
+                "psr-4": {
+                    "Complex\\": "classes/src/"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Mark Baker",
+                    "email": "mark@lange.demon.co.uk"
+                }
+            ],
+            "description": "PHP Class for working with complex numbers",
+            "homepage": "https://github.com/MarkBaker/PHPComplex",
+            "keywords": [
+                "complex",
+                "mathematics"
+            ],
+            "support": {
+                "issues": "https://github.com/MarkBaker/PHPComplex/issues",
+                "source": "https://github.com/MarkBaker/PHPComplex/tree/3.0.1"
+            },
+            "time": "2021-06-29T15:32:53+00:00"
+        },
+        {
+            "name": "markbaker/matrix",
+            "version": "3.0.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/MarkBaker/PHPMatrix.git",
+                "reference": "c66aefcafb4f6c269510e9ac46b82619a904c576"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/MarkBaker/PHPMatrix/zipball/c66aefcafb4f6c269510e9ac46b82619a904c576",
+                "reference": "c66aefcafb4f6c269510e9ac46b82619a904c576",
+                "shasum": ""
+            },
+            "require": {
+                "php": "^7.1 || ^8.0"
+            },
+            "require-dev": {
+                "dealerdirect/phpcodesniffer-composer-installer": "^0.7.0",
+                "phpcompatibility/php-compatibility": "^9.0",
+                "phpdocumentor/phpdocumentor": "2.*",
+                "phploc/phploc": "^4.0",
+                "phpmd/phpmd": "2.*",
+                "phpunit/phpunit": "^7.0 || ^8.0 || ^9.3",
+                "sebastian/phpcpd": "^4.0",
+                "squizlabs/php_codesniffer": "^3.4"
+            },
+            "type": "library",
+            "autoload": {
+                "psr-4": {
+                    "Matrix\\": "classes/src/"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Mark Baker",
+                    "email": "mark@demon-angel.eu"
+                }
+            ],
+            "description": "PHP Class for working with matrices",
+            "homepage": "https://github.com/MarkBaker/PHPMatrix",
+            "keywords": [
+                "mathematics",
+                "matrix",
+                "vector"
+            ],
+            "support": {
+                "issues": "https://github.com/MarkBaker/PHPMatrix/issues",
+                "source": "https://github.com/MarkBaker/PHPMatrix/tree/3.0.0"
+            },
+            "time": "2021-07-01T19:01:15+00:00"
+        },
+        {
+            "name": "myclabs/php-enum",
+            "version": "1.8.4",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/myclabs/php-enum.git",
+                "reference": "a867478eae49c9f59ece437ae7f9506bfaa27483"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/myclabs/php-enum/zipball/a867478eae49c9f59ece437ae7f9506bfaa27483",
+                "reference": "a867478eae49c9f59ece437ae7f9506bfaa27483",
+                "shasum": ""
+            },
+            "require": {
+                "ext-json": "*",
+                "php": "^7.3 || ^8.0"
+            },
+            "require-dev": {
+                "phpunit/phpunit": "^9.5",
+                "squizlabs/php_codesniffer": "1.*",
+                "vimeo/psalm": "^4.6.2"
+            },
+            "type": "library",
+            "autoload": {
+                "psr-4": {
+                    "MyCLabs\\Enum\\": "src/"
+                },
+                "classmap": [
+                    "stubs/Stringable.php"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "PHP Enum contributors",
+                    "homepage": "https://github.com/myclabs/php-enum/graphs/contributors"
+                }
+            ],
+            "description": "PHP Enum implementation",
+            "homepage": "http://github.com/myclabs/php-enum",
+            "keywords": [
+                "enum"
+            ],
+            "support": {
+                "issues": "https://github.com/myclabs/php-enum/issues",
+                "source": "https://github.com/myclabs/php-enum/tree/1.8.4"
+            },
+            "funding": [
+                {
+                    "url": "https://github.com/mnapoli",
+                    "type": "github"
+                },
+                {
+                    "url": "https://tidelift.com/funding/github/packagist/myclabs/php-enum",
+                    "type": "tidelift"
+                }
+            ],
+            "time": "2022-08-04T09:53:51+00:00"
+        },
+        {
+            "name": "phpoffice/phpspreadsheet",
+            "version": "1.25.2",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/PHPOffice/PhpSpreadsheet.git",
+                "reference": "a317a09e7def49852400a4b3eca4a4b0790ceeb5"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/PHPOffice/PhpSpreadsheet/zipball/a317a09e7def49852400a4b3eca4a4b0790ceeb5",
+                "reference": "a317a09e7def49852400a4b3eca4a4b0790ceeb5",
+                "shasum": ""
+            },
+            "require": {
+                "ext-ctype": "*",
+                "ext-dom": "*",
+                "ext-fileinfo": "*",
+                "ext-gd": "*",
+                "ext-iconv": "*",
+                "ext-libxml": "*",
+                "ext-mbstring": "*",
+                "ext-simplexml": "*",
+                "ext-xml": "*",
+                "ext-xmlreader": "*",
+                "ext-xmlwriter": "*",
+                "ext-zip": "*",
+                "ext-zlib": "*",
+                "ezyang/htmlpurifier": "^4.15",
+                "maennchen/zipstream-php": "^2.1",
+                "markbaker/complex": "^3.0",
+                "markbaker/matrix": "^3.0",
+                "php": "^7.3 || ^8.0",
+                "psr/http-client": "^1.0",
+                "psr/http-factory": "^1.0",
+                "psr/simple-cache": "^1.0 || ^2.0 || ^3.0"
+            },
+            "require-dev": {
+                "dealerdirect/phpcodesniffer-composer-installer": "dev-master",
+                "dompdf/dompdf": "^1.0 || ^2.0",
+                "friendsofphp/php-cs-fixer": "^3.2",
+                "mitoteam/jpgraph": "10.2.4",
+                "mpdf/mpdf": "8.1.1",
+                "phpcompatibility/php-compatibility": "^9.3",
+                "phpstan/phpstan": "^1.1",
+                "phpstan/phpstan-phpunit": "^1.0",
+                "phpunit/phpunit": "^8.5 || ^9.0",
+                "squizlabs/php_codesniffer": "^3.7",
+                "tecnickcom/tcpdf": "6.5"
+            },
+            "suggest": {
+                "dompdf/dompdf": "Option for rendering PDF with PDF Writer",
+                "ext-intl": "PHP Internationalization Functions",
+                "mitoteam/jpgraph": "Option for rendering charts, or including charts with PDF or HTML Writers",
+                "mpdf/mpdf": "Option for rendering PDF with PDF Writer",
+                "tecnickcom/tcpdf": "Option for rendering PDF with PDF Writer"
+            },
+            "type": "library",
+            "autoload": {
+                "psr-4": {
+                    "PhpOffice\\PhpSpreadsheet\\": "src/PhpSpreadsheet"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Maarten Balliauw",
+                    "homepage": "https://blog.maartenballiauw.be"
+                },
+                {
+                    "name": "Mark Baker",
+                    "homepage": "https://markbakeruk.net"
+                },
+                {
+                    "name": "Franck Lefevre",
+                    "homepage": "https://rootslabs.net"
+                },
+                {
+                    "name": "Erik Tilt"
+                },
+                {
+                    "name": "Adrien Crivelli"
+                }
+            ],
+            "description": "PHPSpreadsheet - Read, Create and Write Spreadsheet documents in PHP - Spreadsheet engine",
+            "homepage": "https://github.com/PHPOffice/PhpSpreadsheet",
+            "keywords": [
+                "OpenXML",
+                "excel",
+                "gnumeric",
+                "ods",
+                "php",
+                "spreadsheet",
+                "xls",
+                "xlsx"
+            ],
+            "support": {
+                "issues": "https://github.com/PHPOffice/PhpSpreadsheet/issues",
+                "source": "https://github.com/PHPOffice/PhpSpreadsheet/tree/1.25.2"
+            },
+            "time": "2022-09-25T17:21:01+00:00"
+        },
+        {
+            "name": "phpoption/phpoption",
+            "version": "1.9.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/schmittjoh/php-option.git",
+                "reference": "dc5ff11e274a90cc1c743f66c9ad700ce50db9ab"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/schmittjoh/php-option/zipball/dc5ff11e274a90cc1c743f66c9ad700ce50db9ab",
+                "reference": "dc5ff11e274a90cc1c743f66c9ad700ce50db9ab",
+                "shasum": ""
+            },
+            "require": {
+                "php": "^7.2.5 || ^8.0"
+            },
+            "require-dev": {
+                "bamarni/composer-bin-plugin": "^1.8",
+                "phpunit/phpunit": "^8.5.28 || ^9.5.21"
+            },
+            "type": "library",
+            "extra": {
+                "bamarni-bin": {
+                    "bin-links": true,
+                    "forward-command": true
+                },
+                "branch-alias": {
+                    "dev-master": "1.9-dev"
+                }
+            },
+            "autoload": {
+                "psr-4": {
+                    "PhpOption\\": "src/PhpOption/"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "Apache-2.0"
+            ],
+            "authors": [
+                {
+                    "name": "Johannes M. Schmitt",
+                    "email": "schmittjoh@gmail.com",
+                    "homepage": "https://github.com/schmittjoh"
+                },
+                {
+                    "name": "Graham Campbell",
+                    "email": "hello@gjcampbell.co.uk",
+                    "homepage": "https://github.com/GrahamCampbell"
+                }
+            ],
+            "description": "Option Type for PHP",
+            "keywords": [
+                "language",
+                "option",
+                "php",
+                "type"
+            ],
+            "support": {
+                "issues": "https://github.com/schmittjoh/php-option/issues",
+                "source": "https://github.com/schmittjoh/php-option/tree/1.9.0"
+            },
+            "funding": [
+                {
+                    "url": "https://github.com/GrahamCampbell",
+                    "type": "github"
+                },
+                {
+                    "url": "https://tidelift.com/funding/github/packagist/phpoption/phpoption",
+                    "type": "tidelift"
+                }
+            ],
+            "time": "2022-07-30T15:51:26+00:00"
+        },
+        {
+            "name": "psr/http-client",
+            "version": "1.0.1",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/php-fig/http-client.git",
+                "reference": "2dfb5f6c5eff0e91e20e913f8c5452ed95b86621"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/php-fig/http-client/zipball/2dfb5f6c5eff0e91e20e913f8c5452ed95b86621",
+                "reference": "2dfb5f6c5eff0e91e20e913f8c5452ed95b86621",
+                "shasum": ""
+            },
+            "require": {
+                "php": "^7.0 || ^8.0",
+                "psr/http-message": "^1.0"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "1.0.x-dev"
+                }
+            },
+            "autoload": {
+                "psr-4": {
+                    "Psr\\Http\\Client\\": "src/"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "PHP-FIG",
+                    "homepage": "http://www.php-fig.org/"
+                }
+            ],
+            "description": "Common interface for HTTP clients",
+            "homepage": "https://github.com/php-fig/http-client",
+            "keywords": [
+                "http",
+                "http-client",
+                "psr",
+                "psr-18"
+            ],
+            "support": {
+                "source": "https://github.com/php-fig/http-client/tree/master"
+            },
+            "time": "2020-06-29T06:28:15+00:00"
+        },
+        {
+            "name": "psr/http-factory",
+            "version": "1.0.1",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/php-fig/http-factory.git",
+                "reference": "12ac7fcd07e5b077433f5f2bee95b3a771bf61be"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/php-fig/http-factory/zipball/12ac7fcd07e5b077433f5f2bee95b3a771bf61be",
+                "reference": "12ac7fcd07e5b077433f5f2bee95b3a771bf61be",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=7.0.0",
+                "psr/http-message": "^1.0"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "1.0.x-dev"
+                }
+            },
+            "autoload": {
+                "psr-4": {
+                    "Psr\\Http\\Message\\": "src/"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "PHP-FIG",
+                    "homepage": "http://www.php-fig.org/"
+                }
+            ],
+            "description": "Common interfaces for PSR-7 HTTP message factories",
+            "keywords": [
+                "factory",
+                "http",
+                "message",
+                "psr",
+                "psr-17",
+                "psr-7",
+                "request",
+                "response"
+            ],
+            "support": {
+                "source": "https://github.com/php-fig/http-factory/tree/master"
+            },
+            "time": "2019-04-30T12:38:16+00:00"
+        },
+        {
+            "name": "psr/http-message",
+            "version": "1.0.1",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/php-fig/http-message.git",
+                "reference": "f6561bf28d520154e4b0ec72be95418abe6d9363"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/php-fig/http-message/zipball/f6561bf28d520154e4b0ec72be95418abe6d9363",
+                "reference": "f6561bf28d520154e4b0ec72be95418abe6d9363",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=5.3.0"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "1.0.x-dev"
+                }
+            },
+            "autoload": {
+                "psr-4": {
+                    "Psr\\Http\\Message\\": "src/"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "PHP-FIG",
+                    "homepage": "http://www.php-fig.org/"
+                }
+            ],
+            "description": "Common interface for HTTP messages",
+            "homepage": "https://github.com/php-fig/http-message",
+            "keywords": [
+                "http",
+                "http-message",
+                "psr",
+                "psr-7",
+                "request",
+                "response"
+            ],
+            "support": {
+                "source": "https://github.com/php-fig/http-message/tree/master"
+            },
+            "time": "2016-08-06T14:39:51+00:00"
+        },
+        {
+            "name": "psr/simple-cache",
+            "version": "3.0.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/php-fig/simple-cache.git",
+                "reference": "764e0b3939f5ca87cb904f570ef9be2d78a07865"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/php-fig/simple-cache/zipball/764e0b3939f5ca87cb904f570ef9be2d78a07865",
+                "reference": "764e0b3939f5ca87cb904f570ef9be2d78a07865",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=8.0.0"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "3.0.x-dev"
+                }
+            },
+            "autoload": {
+                "psr-4": {
+                    "Psr\\SimpleCache\\": "src/"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "PHP-FIG",
+                    "homepage": "https://www.php-fig.org/"
+                }
+            ],
+            "description": "Common interfaces for simple caching",
+            "keywords": [
+                "cache",
+                "caching",
+                "psr",
+                "psr-16",
+                "simple-cache"
+            ],
+            "support": {
+                "source": "https://github.com/php-fig/simple-cache/tree/3.0.0"
+            },
+            "time": "2021-10-29T13:26:27+00:00"
+        },
+        {
+            "name": "seinopsys/postgresql-database-class",
+            "version": "v3.1.1",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/SeinopSys/PHP-PostgreSQL-Database-Class.git",
+                "reference": "47a648fdb67c2fc27e8ff35091c8e36cd95596e9"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/SeinopSys/PHP-PostgreSQL-Database-Class/zipball/47a648fdb67c2fc27e8ff35091c8e36cd95596e9",
+                "reference": "47a648fdb67c2fc27e8ff35091c8e36cd95596e9",
+                "shasum": ""
+            },
+            "require": {
+                "ext-pdo": "*",
+                "ext-pdo_pgsql": "*",
+                "php": ">=5.4"
+            },
+            "require-dev": {
+                "squizlabs/php_codesniffer": "3.*"
+            },
+            "type": "library",
+            "autoload": {
+                "psr-4": {
+                    "SeinopSys\\": "src/"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "GPL-3.0-or-later"
+            ],
+            "authors": [
+                {
+                    "name": "SeinopSys",
+                    "email": "seinopsys@gmail.com",
+                    "homepage": "https://github.com/SeinopSys",
+                    "role": "Developer"
+                }
+            ],
+            "description": "PHP wrapper class for PDO-based interaction with PostgreSQL databases, heavily based on ThingEngineer's MysqliDb class",
+            "support": {
+                "issues": "https://github.com/SeinopSys/PHP-PostgreSQL-Database-Class/issues",
+                "source": "https://github.com/SeinopSys/PHP-PostgreSQL-Database-Class/tree/master"
+            },
+            "time": "2019-05-26T17:41:40+00:00"
+        },
+        {
+            "name": "symfony/polyfill-ctype",
+            "version": "v1.27.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/symfony/polyfill-ctype.git",
+                "reference": "5bbc823adecdae860bb64756d639ecfec17b050a"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/5bbc823adecdae860bb64756d639ecfec17b050a",
+                "reference": "5bbc823adecdae860bb64756d639ecfec17b050a",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=7.1"
+            },
+            "provide": {
+                "ext-ctype": "*"
+            },
+            "suggest": {
+                "ext-ctype": "For best performance"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-main": "1.27-dev"
+                },
+                "thanks": {
+                    "name": "symfony/polyfill",
+                    "url": "https://github.com/symfony/polyfill"
+                }
+            },
+            "autoload": {
+                "files": [
+                    "bootstrap.php"
+                ],
+                "psr-4": {
+                    "Symfony\\Polyfill\\Ctype\\": ""
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Gert de Pagter",
+                    "email": "BackEndTea@gmail.com"
+                },
+                {
+                    "name": "Symfony Community",
+                    "homepage": "https://symfony.com/contributors"
+                }
+            ],
+            "description": "Symfony polyfill for ctype functions",
+            "homepage": "https://symfony.com",
+            "keywords": [
+                "compatibility",
+                "ctype",
+                "polyfill",
+                "portable"
+            ],
+            "support": {
+                "source": "https://github.com/symfony/polyfill-ctype/tree/v1.27.0"
+            },
+            "funding": [
+                {
+                    "url": "https://symfony.com/sponsor",
+                    "type": "custom"
+                },
+                {
+                    "url": "https://github.com/fabpot",
+                    "type": "github"
+                },
+                {
+                    "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+                    "type": "tidelift"
+                }
+            ],
+            "time": "2022-11-03T14:55:06+00:00"
+        },
+        {
+            "name": "symfony/polyfill-mbstring",
+            "version": "v1.27.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/symfony/polyfill-mbstring.git",
+                "reference": "8ad114f6b39e2c98a8b0e3bd907732c207c2b534"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/8ad114f6b39e2c98a8b0e3bd907732c207c2b534",
+                "reference": "8ad114f6b39e2c98a8b0e3bd907732c207c2b534",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=7.1"
+            },
+            "provide": {
+                "ext-mbstring": "*"
+            },
+            "suggest": {
+                "ext-mbstring": "For best performance"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-main": "1.27-dev"
+                },
+                "thanks": {
+                    "name": "symfony/polyfill",
+                    "url": "https://github.com/symfony/polyfill"
+                }
+            },
+            "autoload": {
+                "files": [
+                    "bootstrap.php"
+                ],
+                "psr-4": {
+                    "Symfony\\Polyfill\\Mbstring\\": ""
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Nicolas Grekas",
+                    "email": "p@tchwork.com"
+                },
+                {
+                    "name": "Symfony Community",
+                    "homepage": "https://symfony.com/contributors"
+                }
+            ],
+            "description": "Symfony polyfill for the Mbstring extension",
+            "homepage": "https://symfony.com",
+            "keywords": [
+                "compatibility",
+                "mbstring",
+                "polyfill",
+                "portable",
+                "shim"
+            ],
+            "support": {
+                "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.27.0"
+            },
+            "funding": [
+                {
+                    "url": "https://symfony.com/sponsor",
+                    "type": "custom"
+                },
+                {
+                    "url": "https://github.com/fabpot",
+                    "type": "github"
+                },
+                {
+                    "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+                    "type": "tidelift"
+                }
+            ],
+            "time": "2022-11-03T14:55:06+00:00"
+        },
+        {
+            "name": "symfony/polyfill-php80",
+            "version": "v1.27.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/symfony/polyfill-php80.git",
+                "reference": "7a6ff3f1959bb01aefccb463a0f2cd3d3d2fd936"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/7a6ff3f1959bb01aefccb463a0f2cd3d3d2fd936",
+                "reference": "7a6ff3f1959bb01aefccb463a0f2cd3d3d2fd936",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=7.1"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-main": "1.27-dev"
+                },
+                "thanks": {
+                    "name": "symfony/polyfill",
+                    "url": "https://github.com/symfony/polyfill"
+                }
+            },
+            "autoload": {
+                "files": [
+                    "bootstrap.php"
+                ],
+                "psr-4": {
+                    "Symfony\\Polyfill\\Php80\\": ""
+                },
+                "classmap": [
+                    "Resources/stubs"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Ion Bazan",
+                    "email": "ion.bazan@gmail.com"
+                },
+                {
+                    "name": "Nicolas Grekas",
+                    "email": "p@tchwork.com"
+                },
+                {
+                    "name": "Symfony Community",
+                    "homepage": "https://symfony.com/contributors"
+                }
+            ],
+            "description": "Symfony polyfill backporting some PHP 8.0+ features to lower PHP versions",
+            "homepage": "https://symfony.com",
+            "keywords": [
+                "compatibility",
+                "polyfill",
+                "portable",
+                "shim"
+            ],
+            "support": {
+                "source": "https://github.com/symfony/polyfill-php80/tree/v1.27.0"
+            },
+            "funding": [
+                {
+                    "url": "https://symfony.com/sponsor",
+                    "type": "custom"
+                },
+                {
+                    "url": "https://github.com/fabpot",
+                    "type": "github"
+                },
+                {
+                    "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+                    "type": "tidelift"
+                }
+            ],
+            "time": "2022-11-03T14:55:06+00:00"
+        },
+        {
+            "name": "vlucas/phpdotenv",
+            "version": "v5.5.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/vlucas/phpdotenv.git",
+                "reference": "1a7ea2afc49c3ee6d87061f5a233e3a035d0eae7"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/vlucas/phpdotenv/zipball/1a7ea2afc49c3ee6d87061f5a233e3a035d0eae7",
+                "reference": "1a7ea2afc49c3ee6d87061f5a233e3a035d0eae7",
+                "shasum": ""
+            },
+            "require": {
+                "ext-pcre": "*",
+                "graham-campbell/result-type": "^1.0.2",
+                "php": "^7.1.3 || ^8.0",
+                "phpoption/phpoption": "^1.8",
+                "symfony/polyfill-ctype": "^1.23",
+                "symfony/polyfill-mbstring": "^1.23.1",
+                "symfony/polyfill-php80": "^1.23.1"
+            },
+            "require-dev": {
+                "bamarni/composer-bin-plugin": "^1.4.1",
+                "ext-filter": "*",
+                "phpunit/phpunit": "^7.5.20 || ^8.5.30 || ^9.5.25"
+            },
+            "suggest": {
+                "ext-filter": "Required to use the boolean validator."
+            },
+            "type": "library",
+            "extra": {
+                "bamarni-bin": {
+                    "bin-links": true,
+                    "forward-command": true
+                },
+                "branch-alias": {
+                    "dev-master": "5.5-dev"
+                }
+            },
+            "autoload": {
+                "psr-4": {
+                    "Dotenv\\": "src/"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "BSD-3-Clause"
+            ],
+            "authors": [
+                {
+                    "name": "Graham Campbell",
+                    "email": "hello@gjcampbell.co.uk",
+                    "homepage": "https://github.com/GrahamCampbell"
+                },
+                {
+                    "name": "Vance Lucas",
+                    "email": "vance@vancelucas.com",
+                    "homepage": "https://github.com/vlucas"
+                }
+            ],
+            "description": "Loads environment variables from `.env` to `getenv()`, `$_ENV` and `$_SERVER` automagically.",
+            "keywords": [
+                "dotenv",
+                "env",
+                "environment"
+            ],
+            "support": {
+                "issues": "https://github.com/vlucas/phpdotenv/issues",
+                "source": "https://github.com/vlucas/phpdotenv/tree/v5.5.0"
+            },
+            "funding": [
+                {
+                    "url": "https://github.com/GrahamCampbell",
+                    "type": "github"
+                },
+                {
+                    "url": "https://tidelift.com/funding/github/packagist/vlucas/phpdotenv",
+                    "type": "tidelift"
+                }
+            ],
+            "time": "2022-10-16T01:01:54+00:00"
+        }
+    ],
+    "packages-dev": [],
+    "aliases": [],
+    "minimum-stability": "stable",
+    "stability-flags": [],
+    "prefer-stable": false,
+    "prefer-lowest": false,
+    "platform": [],
+    "platform-dev": [],
+    "plugin-api-version": "2.2.0"
+}

+ 1608 - 0
consultar_horario.php

@@ -0,0 +1,1608 @@
+    <?php
+    require_once 'class/c_login.php';
+    if (!isset($_SESSION['user']))
+        die(header('Location: index.php'));
+
+    $user = unserialize($_SESSION['user']);
+    $user->access();
+    if (!$user->admin && in_array($user->acceso, ['n']))
+        die(header('Location: main.php?error=1'));
+
+    $user->print_to_log('Consultar horario');
+
+    $write = $user->admin || in_array($user->acceso, ['w']);
+    // var_dump($user);
+    ?>
+    <!DOCTYPE html>
+    <html lang="en">
+
+    <head>
+        <title>Consultar horario | <?= $user->facultad['facultad'] ?? 'General' ?></title>
+        <meta charset="utf-8">
+        <meta http-equiv="content-type" content="text/plain; charset=UTF-8" />
+        <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
+        <?php include_once "import/html_css_files.php"; ?>
+    </head>
+    <!--  -->
+
+    <body style="display: block;">
+        <?php
+        include('include/constantes.php');
+        include("import/html_header.php");
+        html_header("Consultar horario", "Sistema de gestión de checador");
+        ?>
+        <?= "<!-- $user -->" ?>
+        <main class="container content marco content-margin" id="local-app">
+            <section id="message"></section>
+            <?php require('import/periodo.php') ?>
+
+            <!-- Nuevo horario -->
+            <form>
+                <div class="form-group">
+                    <div class="form-box">
+                        <?php
+                        #$carreras = query("SELECT * FROM FS_CARRERA WHERE FACULTAD = :fac AND PERIODO = COALESCE(:per, PERIODO) ORDER BY CARRERA", [":fac" => $user->facultad['facultad_id'], ":per" => $user->periodo], single: false);
+                        // repliaction of the query in the database with database class
+                        $nivel = $db->where('id', $user->periodo)->getOne('fs_periodo');
+                        $carreras = $db
+                            ->orderBy('carrera')
+                            ->where('facultad', $nivel['facultad_id'])
+                            ->where('nivel', $nivel['nivel_id'])
+                            ->get('fs_carrera', 100, 'id, carrera');
+
+                        ?>
+                        <div class="form-group row">
+                            <label for="filter_carrera" class="col-4 col-form-label">Carrera</label>
+                            <div class="col-6 ">
+                                <div id="dlcarrera" class="datalist datalist-select mb-1 w-100">
+                                    <div class="datalist-input">Seleccionar carrera</div>
+                                    <span class="ing-buscar icono"></span>
+                                    <ul style="display:none">
+                                        <?php
+                                        foreach ($carreras as $carrera) {
+                                        ?>
+                                            <li data-id="<?= $carrera['id'] ?>">
+                                                <?= $carrera['carrera'] ?>
+                                            </li>
+                                        <?php
+                                        }
+                                        ?>
+                                    </ul>
+                                    <input type="hidden" id="filter_carrera" name="carrera" value="">
+                                </div>
+                            </div>
+                        </div>
+
+                        <!-- Grupo -->
+
+                        <div class="form-group row">
+                            <label for="filter_grupo" class="col-4 col-form-label">Grupo</label>
+                            <div class="col-6 ">
+                                <div id="dlgrupo" class="datalist datalist-select mb-1 w-100">
+                                    <div class="datalist-input">Seleccionar grupo</div>
+                                    <span class="ing-buscar icono"></span>
+                                    <ul style="display:none">
+                                    </ul>
+                                    <input type="hidden" id="filter_grupo" name="grupo" value="">
+                                </div>
+                            </div>
+                        </div>
+
+                        <div class="form-group mt-4 row justify-content-center">
+                            <?php if ($write) { ?>
+                                <button type="button" id="nuevo" class="btn btn-outline-primary ml-4 d-none" title="Nuevo horario" data-toggle="modal" data-target="#modal-editar">
+                                    <span class="ing-mas ing-fw"></span> Nuevo
+                                </button>
+                            <?php } ?>
+                        </div>
+                    </div>
+                </div>
+
+            </form>
+            <!-- Horario is a (table with one a cell) within a table 
+            7:15 - 8:45, 8:45 - 10:15, 10:30 - 12:00, 12:00 - 13:30
+            de lunes a viernes, a excepción de que tenga sábado
+        -->
+            <div id="btn-excel-horario" class="mb-2 float-right hidden">
+                <button class="btn btn-outline-secondary " title="Exportar a Excel">
+                    <span class="ing-descarga ing-fw"></span> Exportar a Excel
+                </button>
+            </div>
+            <!-- Table responsive -->
+            <div class="table-responsive">
+                <table class="table table-bordered table-sm table-responsive-sm" id="table-horario">
+                    <thead class="thead-dark">
+                        <tr id="headers">
+                            <th scope="col" class="text-center">Hora</th>
+                            <th scope="col" class="text-center">Lunes</th>
+                            <th scope="col" class="text-center">Martes</th>
+                            <th scope="col" class="text-center">Miércoles</th>
+                            <th scope="col" class="text-center">Jueves</th>
+                            <th scope="col" class="text-center">Viernes</th>
+                            <th scope="col" class="text-center">Sábado</th>
+                        </tr>
+                    </thead>
+                    <tbody id="horario"></tbody>
+                </table>
+            </div>
+
+            <div class="modal fade" id="modal-editar" tabindex="-1" aria-labelledby="modal-editar" aria-hidden="true" data-backdrop="static" data-keyboard="false">
+                <div class="modal-dialog modal-dialog-centered modal-lg">
+                    <div class="modal-content">
+                        <div class="modal-header">
+                            <h5 class="col-12 modal-title text-center">Horario
+                                <button type="button" class="close text-white" data-dismiss="modal" aria-label="Close">
+                                    <span aria-hidden="true">&times;</span>
+                                </button>
+                            </h5>
+                        </div>
+                        <div class="modal-body">
+                            <section id="message_editar"></section>
+
+                            <!-- Hora inicio CLOCKPICKER -->
+                            <div class="form-box">
+                                <div class="form-group row">
+                                    <label for="dli-grupo" class="col-4 col-form-label">Grupo</label>
+                                    <div class="col-6">
+                                        <div id="grupoModal" class="datalist datalist-select mb-1 w-100">
+                                            <div class="datalist-input text-center"></div>
+                                            <span class="ing-buscar icono"></span>
+                                            <ul style="display:none">
+                                                <li data-id="" class="text-center">Nuevo grupo</li>
+                                            </ul>
+                                            <input type="hidden" name="dli-grupo" id="dli-grupo" value="" />
+                                        </div>
+                                    </div>
+                                </div>
+                                <div class="form-grupo row mb-3">
+                                    <div class="col-4"></div>
+                                    <div class="col-6">
+                                        <input type="text" id="grupo" name="grupo" value="" class="form-control" placeholder="Grupo" required="required" hidden>
+                                        <div class="invalid-feedback">
+                                            Por favor, ingrese un grupo.
+                                        </div>
+                                    </div>
+                                </div>
+                                <div class="form-group row">
+                                    <label for="materia" class="col-4 col-form-label">Materia</label>
+                                    <div class="col-6">
+                                        <input list="lista_materias" name="dlMateria" id="dlMateria" class="form-control text-center" placeholder="Materia" required="required">
+                                        <datalist id="lista_materias"></datalist>
+                                        <input type="hidden" id="materia" name="materia" value="">
+                                        <div class="invalid-feedback">
+                                            Por favor, seleccione una materia.
+                                        </div>
+                                    </div>
+                                </div>
+
+                                <div class="form-group row">
+                                    <label for="editor_hora" class="col-4 col-form-label">Hora</label>
+                                    <div class="col-3">
+                                        <div id="dlhora" class="datalist datalist-select mb-1">
+                                            <div class="datalist-input text-center"></div>
+                                            <span class="ing-buscar icono"></span>
+                                            <ul style="display:none">
+                                                <?php foreach (range(7, 21) as $hora) { ?>
+                                                    <li data-id='<?= $hora ?>'><?= str_pad($hora, 2, "0", STR_PAD_LEFT) ?></li>
+                                                <?php } ?>
+                                            </ul>
+                                            <input type="hidden" id="selector_horas" name="horas" value="">
+                                        </div>
+                                    </div>
+                                    <div class="col-3">
+                                        <div id="dlminuto" class="datalist datalist-select mb-1">
+                                            <div class="datalist-input text-center"></div>
+                                            <span class="ing-buscar icono"></span>
+                                            <ul style="display:none">
+                                                <?php foreach (range(0, 45, 15) as $minuto) { ?>
+                                                    <li data-id='<?= $minuto ?>'><?= str_pad($minuto, 2, "0", STR_PAD_LEFT) ?></li>
+                                                <?php } ?>
+                                            </ul>
+                                            <input type="hidden" id="selector_minutos" name="minutos" value="">
+                                        </div>
+                                    </div>
+                                </div>
+
+                                <!-- Día -->
+                                <div class="form-group row">
+                                    <label for="editor_dia" class="col-4 col-form-label">Día</label>
+                                    <div class="col-6 ">
+                                        <div id="dldia" class="datalist datalist-select mb-1 w-100">
+                                            <div class="datalist-input">Seleccionar día</div>
+                                            <span class="ing-buscar icono"></span>
+                                            <ul style="display:none">
+                                                <li data-id="1">Lunes</li>
+                                                <li data-id="2">Martes</li>
+                                                <li data-id="3">Miércoles</li>
+                                                <li data-id="4">Jueves</li>
+                                                <li data-id="5">Viernes</li>
+                                                <li data-id="6">Sábado</li>
+                                            </ul>
+                                            <input type="hidden" id="editor_dia" name="dia" value="">
+                                        </div>
+                                    </div>
+                                </div>
+
+                                <!-- Duración -->
+                                <div class="form-group row">
+                                    <label for="editor_duración" class="col-4 col-form-label">Duración</label>
+                                    <div class="col-6 ">
+                                        <div id="dlduración" class="datalist datalist-select mb-1 w-100">
+                                            <div class="datalist-input">Seleccionar duración</div>
+                                            <span class="ing-buscar icono"></span>
+                                            <ul style="display:none">
+                                                <?php
+                                                $duraciones = $db->orderBy('duracion_bloques')->get("duracion");
+                                                foreach ($duraciones as $duración) {
+                                                    $nombre = $duración['duracion_nombre'];
+                                                    $id = $duración['duracion_id'];
+                                                    $bloques = $duración['duracion_bloques'];
+                                                ?>
+                                                    <li data-id="<?= $id; ?>" data-bloques="<?= $bloques; ?>"><?= $nombre; ?></li>
+                                                <?php
+                                                }
+                                                ?>
+                                            </ul>
+                                            <input type="hidden" id="editor_duración" name="duración" value="">
+                                        </div>
+                                        <div class="invalid-feedback">
+                                            La duración supera el límite (22:00).
+                                        </div>
+                                    </div>
+                                </div>
+
+                                <!-- Profesor -->
+                                <div class="form-group row">
+                                    <label for="editor_profesor" class="col-4 col-form-label">Profesor</label>
+                                    <div class="col-6">
+                                        <input list="lista_profesores" name="dlProfesor" id="dlProfesor" class="form-control" placeholder="Profesor" required="required">
+                                        <div class="valid-feedback">
+                                            Profesor encontrado
+                                        </div>
+                                        <div class="invalid-feedback">
+                                            Profesor no encontrado
+                                        </div>
+                                        <datalist id="lista_profesores">
+                                            <?php
+                                            $profesores = $db->where('facultad_id', $user->facultad['facultad_id'])->get("fs_profesor");
+                                            foreach ($profesores as $profesor) {
+                                                extract($profesor);
+                                            ?>
+                                                <option data-grado="<?= $grado ?>" data-clave="<?= $clave ?>" data-profesor="<?= $profesor ?>" data-id="<?= $id; ?>" value="<?= "$clave | $grado $profesor" ?>"></option>
+                                            <?php
+                                            }
+                                            ?>
+                                        </datalist>
+                                        <ul class="list-group" id="profesores"></ul>
+                                        <input type="hidden" id="editor_profesor" name="profesor" value="">
+                                    </div>
+                                </div>
+
+                                <!-- Salón -->
+                                <div class="form-group row">
+                                    <label for="editor_salón" class="col-4 col-form-label">Salón</label>
+                                    <div class="col-6">
+                                        <input type="text" class="form-control" id="editor_salón" name="salón" placeholder="Salón" maxlength="10" required="required">
+                                        <div class="invalid-feedback">
+                                            El salón no puede estar vacío.
+                                        </div>
+                                    </div>
+                                </div>
+                            </div>
+                        </div>
+                        <div class="modal-footer">
+                            <button data-id="" type="button" class="btn btn-primary" id="btn-guardar"><i class="ing-guardar ing"></i> Guardar</button>
+                            <button type="button" class="btn btn-outline-primary" data-dismiss="modal">Cancelar</button>
+                        </div>
+                    </div>
+                </div>
+            </div>
+            <div class="modal" id="modal-borrar" tabindex="-1" aria-labelledby="modal-borrar" aria-hidden="true">
+                <div class="modal-dialog modal-dialog-centered modal-lg">
+                    <div class="modal-content">
+                        <div class="modal-header">
+                            <h5 class="col-12 modal-title text-center">Horario
+                                <button type="button" class="close text-white" data-dismiss="modal" aria-label="Close">
+                                    <span aria-hidden="true">&times;</span>
+                                </button>
+                            </h5>
+                        </div>
+                        <div class="modal-body text-center">
+                            <h5>¿Está seguro de eliminar el horario?</h5>
+
+                            <div class="form-group mt-4 row justify-content-center" style="gap: 1rem;">
+                                <button id="btn-borrar" type="button" class="btn btn-danger">
+                                    <i class="ing-borrar ing"></i>
+                                    Borrar
+                                </button>
+
+                                <button type="button" class="btn btn-secondary" data-dismiss="modal">Cancelar</button>
+                            </div>
+
+                        </div>
+                    </div>
+                </div>
+            </div>
+            <div class="modal fade" id="modal-choose" tabindex="-1" aria-labelledby="modal-choose" aria-hidden="true">
+                <div class="modal-dialog modal-dialog-centered modal-lg">
+                    <div class="modal-content">
+                        <div class="modal-header">
+                            <h5 class="col-12 modal-title text-center">Seleccionar horarios en conflicto
+                                <button type="button" class="close text-white" data-dismiss="modal" aria-label="Close">
+                                    <span aria-hidden="true">&times;</span>
+                                </button>
+                            </h5>
+                        </div>
+                        <div class="modal-body">
+                            <div class="row">
+                                <div class="col-12">
+                                    <div class="alert alert-danger" role="alert">
+                                        <h4 class="alert-heading">
+                                            <i class="ing-importante ing"></i>
+                                            ¡Atención!
+                                        </h4>
+                                        <p>Los siguientes horarios tienen conflicto con el horario que intenta guardar.</p>
+                                    </div>
+                                </div>
+                            </div>
+                            <div class="row">
+                                <div class="col-12">
+                                    <div class="table-responsive">
+                                        <table class="table table-striped table-hover">
+                                            <thead>
+                                                <tr>
+                                                    <th scope="col">Hora</th>
+                                                    <th scope="col">Materia</th>
+                                                    <th scope="col">Profesores</th>
+                                                    <th scope="col">Salón</th>
+
+                                                    <?php if ($write == "true") { ?>
+                                                        <th class="text-center" scope="col">Editar</th>
+                                                        <th class="text-center" scope="col">Borrar</th>
+                                                    <?php } ?>
+
+                                                </tr>
+                                            </thead>
+                                            <tbody id="conflictos">
+                                            </tbody>
+                                        </table>
+                                    </div>
+                                </div>
+                            </div>
+                        </div>
+                    </div>
+                </div>
+            </div>
+        </main>
+    </body>
+    <?php
+    require_once("import/html_footer.php");
+    ?>
+    <script src="js/scrollables.js"></script>
+
+    <script src="js/bootstrap/bootstrap.min.js"></script>
+    <!-- use the latest version -->
+    <script src="js/moment.js"></script>
+    <!-- <script src="js/datalist.js"></script> -->
+    <script type="module">
+        import {
+            triggerMessage
+        } from './module/messages.js';
+        var gHorarios = [];
+        var datalists = {
+            día: [document.querySelector('#dldia div'), document.querySelector('#dldia div').innerText],
+            duración: [document.querySelector('#dlduración div'), document.querySelector('#dlduración div').innerText],
+        }
+
+        /**@Auxiliary functions */
+        function listProfesor({
+            id,
+            grado,
+            profesor,
+            clave
+        }) {
+            const lista = document.getElementById("dlProfesor");
+            lista.innerHTML = "";
+            lista.classList.remove("is-invalid");
+            const li = document.createElement('li');
+            li.setAttribute('data-id', id);
+            li.classList.add('list-group-item', 'd-flex', 'justify-content-between', 'align-items-center');
+            li.innerHTML = `${clave} | ${grado ?? ''} ${profesor}`
+
+            const btn = document.createElement('button');
+            btn.setAttribute('type', 'button');
+            btn.classList.add('badge', 'badge-danger', 'badge-pill', 'border-0');
+            btn.onclick = _ => li.remove();
+
+            const i = document.createElement('i');
+            i.classList.add('ing-cancelar');
+            btn.appendChild(i);
+            li.appendChild(btn);
+
+            document.getElementById("profesores").appendChild(li);
+        }
+
+        function compareHours(hora1, hora2) {
+            // parseInt each hour and minute
+            const [h1, m1, ] = hora1.split(":").map(x => parseInt(x));
+            const [h2, m2, ] = hora2.split(":").map(x => parseInt(x));
+
+            if (h1 > h2)
+                return 1;
+            else if (h1 < h2)
+                return -1;
+            else if (m1 > m2)
+                return 1;
+            else if (m1 < m2)
+                return -1;
+            else
+                return 0;
+        }
+        async function buscarGrupo() {
+            const carrera = document.querySelector("#filter_carrera").value;
+            const grupo = document.querySelector("#filter_grupo").value;
+
+            if (carrera == "" || grupo == "") {
+                triggerMessage("El nombre del grupo y la carrera son requeridos", "Faltan campos");
+                return;
+            }
+            const formData = new FormData();
+
+            formData.append("carrera", carrera);
+            formData.append("grupo", grupo);
+            formData.append("periodo", document.querySelector("#periodo").value);
+            formData.append('facultad', '<?= $user->facultad['facultad_id'] ?>');
+
+            try {
+                gHorarios = [];
+                const {
+                    status,
+                    horario: horarios,
+                } = await fetch("action/action_horario.php", {
+                    method: "POST",
+                    body: formData
+                }).then(res => res.json());
+
+                if (status != "success") {
+                    triggerMessage(response.message, "Error");
+                }
+                // show the table
+                table.style.display = "table";
+                document.querySelectorAll('.hidden').forEach(element => element.style.display = "block");
+                // clear the table
+                table.innerHTML = empty_table;
+
+                if (horarios.length == 0) {
+                    window.location.reload();
+                    return;
+                }
+                // fill the table
+                gHorarios = horarios;
+
+                function conflicts(horario1, horario2) {
+                    const {
+                        hora: hora_inicio1,
+                        hora_final: hora_final1,
+                        dia: dia1,
+                    } = horario1;
+
+                    const {
+                        hora: hora_inicio2,
+                        hora_final: hora_final2,
+                        dia: dia2,
+                    } = horario2;
+
+
+                    // if the days are different, there is no conflict
+                    if (dia1 != dia2) {
+                        return false;
+                    }
+
+                    const compareInicios = compareHours(hora_inicio1, hora_inicio2);
+                    const compareFinales = compareHours(hora_final1, hora_final2);
+                    const compareInicioFinal = compareHours(hora_inicio1, hora_final2);
+                    const compareFinalInicio = compareHours(hora_final1, hora_inicio2);
+
+                    // if the horario is inside the other
+                    if (compareInicios === 0 || compareFinales === 0)
+                        return true
+                    if (compareInicios === 1 && compareInicioFinal === -1)
+                        return true
+                    if (compareFinales === -1 && compareFinalInicio === 1)
+                        return true
+
+                    // if the horario is outside the other
+                    if (compareInicios === -1 && compareFinales === 1)
+                        return true
+
+                    return false;
+                }
+                // remove the next 5 cells
+                function removeNextCells(horas, minutos, dia, cells = 5) {
+                    [...Array(cells).keys()].map(i => i + 1).forEach(i => {
+                        const minute = (minutos + i * 15)
+                        const next_minute = (minute % 60).toString().padStart(2, "0");
+                        const next_hour = (horas + Math.floor(minute / 60))
+
+                        document.getElementById(`hora-${next_hour}:${next_minute}-${dia}`).remove()
+                    });
+                }
+
+                function newBlock(horario, edit = false) {
+                    function move(horario, cells) {
+                        const {
+                            hora: hora_inicio,
+                            dia,
+                        } = horario;
+
+                        const [horas, minutos] = hora_inicio.split(":").map(x => parseInt(x));
+
+                        const cell = document.getElementById(`hora-${horas}:${minutos.toString().padStart(2, "0")}-${dia}`);
+
+                        const {
+                            top,
+                            left
+                        } = cell.getBoundingClientRect();
+
+                        const block = document.getElementById(`block-${horario.id}`);
+
+                        block.style.top = `${top}px`;
+                        block.style.left = `${left}px`;
+
+                        removeNextCells(horas, minutos, dia, cells);
+                    }
+                    const {
+                        id,
+                        materia,
+                        salon,
+                        profesores,
+                        hora: hora_inicio,
+                        dia,
+                        bloques
+                    } = horario;
+
+                    const [horas, minutos] = hora_inicio.split(":").map(x => parseInt(x));
+                    const hora = `${horas}:${minutos.toString().padStart(2, "0")}`;
+                    horario.hora = hora;
+
+                    const cell = document.getElementById(`hora-${hora}-${dia}`);
+
+
+                    cell.dataset.ids = id;
+
+                    const float_menu = /*html*/
+                        `<div class="menu-flotante p-2" style="opacity: .7;" >
+                            <a class="mx-2" href="#" data-toggle="modal" data-target="#modal-editar">
+                                <i class="ing-editar ing"></i>
+                            </a>
+                            <a class="mx-2" href="#" data-toggle="modal" data-target="#modal-borrar">
+                                <i class="ing-basura ing" > </i>
+                            </a>
+                        </div>
+                        `
+
+                    cell.innerHTML = /*html*/ `
+                    <div style="overflow-y: auto; overflow-x: hidden; height: 100%;" id="block-${id}" class="position-absolute w-100 h-100">
+                        <small class="text-gray">${hora}</small> 
+                        <b class="title">${materia}</b> <br>
+                        <br><span>Salón: </span>${salon} <br>
+                        <small class="my-2">
+                            ${profesores.map(({grado, profesor}) => /*html*/ ` <span class="ing ing-formacion mx-1"></span>${grado ?? ''} ${profesor}`).join("<br>")}
+                        </small>
+                    </div>
+                    ${edit && float_menu}`
+                        
+                    cell.classList.add("bloque-clase", "position-relative");
+                    cell.rowSpan = bloques;
+                    // draggable
+                    cell.draggable = <?= $write ? "true" : "false" ?>;
+
+                    bloques > 0 ? removeNextCells(horas, minutos, dia, bloques - 1) : null;
+                }
+
+                function newConflictBlock(horarios, edit = false) {
+                    const first_horario = horarios[0];
+                    const [horas, minutos] = first_horario.hora.split(":").map(x => parseInt(x));
+                    const hora = `${horas}:${minutos.toString().padStart(2, "0")}`;
+                    const ids = horarios.map(horario => horario.id);
+                    const cell = document.getElementById(`hora-${hora}-${first_horario.dia}`);
+                    if (cell == null) {
+                        console.error(`Error: No se encontró la celda: hora-${hora}-${first_horario.dia}`);
+                        return;
+                    }
+                    cell.dataset.ids = ids.join(",");
+
+                    // replace the content of the cell
+                    cell.innerHTML = /* HTML */ `
+                    <small class='text-danger'>
+                        ${hora}
+                    </small>
+                    <div class="d-flex justify-content-center align-items-center mt-4">
+                        <div class="d-flex flex-column justify-content-center align-items-center">
+                            <span class="ing ing-importante text-danger" style="font-size: 2rem;"></span>
+                            <b class='text-danger'>
+                                Empalme de ${ids.length} horarios
+                            </b>
+                            <hr>
+                            <i class="text-danger"> Ver horarios &#8230;</i>
+                        </div>
+                    </div>
+                    `;
+
+                    // the text must be centered
+                    // cell.style.backgroundColor = "#f6cfd6";
+                    cell.classList.add("conflict", "bloque-clase");
+                    // add cursor clickable role = "button"
+                    cell.setAttribute("role", "button");
+
+                    // add the event listener for the cell
+                    cell.addEventListener("click", () => {
+                        $("#modal-choose").modal("show");
+                        const ids = cell.getAttribute("data-ids").split(",").map(x => parseInt(x));
+                        const horarios = gHorarios.filter(horario => ids.includes(horario.id));
+                        const tbody = document.querySelector("#modal-choose tbody");
+                        tbody.innerHTML = "";
+                        horarios.sort((a, b) => compareHours(a.hora, b.hora)).forEach(horario => {
+                            const {
+                                materia,
+                                dia,
+                                hora,
+                                hora_final,
+                                profesores,
+                                salon,
+                                id,
+                            } = horario;
+
+                            // remove the last 00 of the hour
+                            const [horaFmt, hora_finalFmt] = [hora, hora_final].map(hora => hora.slice(0, -3))
+                            const buttons = /* HTML */ `
+                                    <td class="text-center">
+                                        <button class="btn btn-sm btn-primary dismiss-editar" data-toggle="modal" data-target="#modal-editar">
+                                            <i class="ing-editar ing"></i>
+                                        </button>
+                                    </td>
+                                    <td class="text-center">
+                                        <button class="btn btn-sm btn-danger dismiss-editar" data-toggle="modal" data-target="#modal-borrar">
+                                            <i class="ing-basura ing"></i>
+                                        </button>
+                                    </td>`
+
+                            tbody.innerHTML += /* HTML */ `
+                                <tr data-ids="${id}">
+                                    <td><small>${horaFmt}-${hora_finalFmt}</small></td>
+                                    <td>${materia}</td>
+                                    <td>
+                                        ${profesores.map(({ grado, profesor }) => `${grado ?? ''} ${profesor}`).join(", ")}
+                                    </td>
+                                    <td>${salon}</td>
+                                    ${edit ? buttons : ""}
+                                </tr>`;
+                        });
+
+                        document.querySelectorAll(".dismiss-editar").forEach(btn => {
+                            btn.addEventListener("click", () => $("#modal-choose").modal("hide"));
+                        });
+
+                    });
+
+                    // blocks is (firsthour - lasthour) / 15 minutes
+                    // sum the duracion_bloques of each horario
+                    function getDuration(hora_i, hora_f) {
+                        const [horas_i, minutos_i] = hora_i.split(":").map(x => parseInt(x));
+                        const [horas_f, minutos_f] = hora_f.split(":").map(x => parseInt(x));
+                        const date_i = new Date(0, 0, 0, horas_i, minutos_i);
+                        const date_f = new Date(0, 0, 0, horas_f, minutos_f);
+                        const diff = date_f - date_i;
+                        return diff / (1000 * 60 * 15);
+                    }
+
+                    const lastHorario = horarios[horarios.length - 1];
+                    // remove the next cells
+                    // get the max hora_final
+                    const maxHoraFinal = horarios.reduce((max, horario) => {
+                        const [horas, minutos] = horario.hora_final.split(":").map(x => parseInt(x));
+                        const date = new Date(0, 0, 0, horas, minutos);
+                        return date > max ? date : max;
+                    }, new Date(0, 0, 0, 0, 0));
+
+                    const blocks = getDuration(first_horario.hora, `${maxHoraFinal.getHours()}:${maxHoraFinal.getMinutes()}`);
+                    cell.rowSpan = blocks
+                    removeNextCells(horas, minutos, first_horario.dia, blocks - 1);
+                }
+
+                const conflictBlocks = horarios.filter((horario, index, arrayHorario) =>
+                        arrayHorario.filter((_, i) => i != index).some(horario2 =>
+                            conflicts(horario, horario2)))
+                    .sort((a, b) => compareHours(a.hora, b.hora));
+
+                const classes = horarios.filter(horario => !conflictBlocks.includes(horario));
+
+                const conflictBlocksPacked = []; // array of sets
+                conflictBlocks.forEach(horario => {
+                    const setIndex = conflictBlocksPacked.findIndex(set => set.some(horario2 => conflicts(horario, horario2)));
+                    if (setIndex === -1) {
+                        conflictBlocksPacked.push([horario]);
+                    } else {
+                        conflictBlocksPacked[setIndex].push(horario);
+                    }
+                })
+
+                classes.forEach(horario =>
+                    newBlock(horario, <?= $write ?>)
+                )
+
+                conflictBlocksPacked.forEach(horarios =>
+                    newConflictBlock(horarios, <?= $write ?>)
+                )
+
+                // remove the elements that are not in the limits
+                let max_hour = Math.max(...horarios.map(horario => {
+                    const lastMoment = moment(horario.hora, "HH:mm").add(horario.bloques * 15, "minutes");
+                    const lastHour = moment(`${lastMoment.hours()}:00`, "HH:mm");
+                    const hourInt = parseInt(lastMoment.format("HH"));
+
+                    return lastMoment.isSame(lastHour) ? hourInt - 1 : hourInt;
+                }));
+
+                let min_hour = Math.min(...horarios.map(horario => parseInt(horario.hora.split(":")[0])));
+
+                document.querySelectorAll("tbody#horario tr").forEach(hora => {
+                    const hora_id = parseInt(hora.id.split("-")[1].split(":")[0]);
+                    (hora_id < min_hour || hora_id > max_hour) ? hora.remove(): null;
+                })
+
+                // if there is no sábado, remove the column
+                if (!horarios.some(horario => horario.dia == "sábado")) {
+                    document.querySelectorAll("tbody#horario td").forEach(td => {
+                        if (td.id.split("-")[2] == "sábado") {
+                            td.remove();
+                        }
+                    });
+
+                    // remove the header (the last)
+                    document.querySelector("#headers").lastElementChild.remove();
+                }
+                // adjust width
+                const ths = document.querySelectorAll("tr#headers th");
+                ths.forEach((th, key) =>
+                    th.style.width = (key == 0) ? "5%" : `${95 / (ths.length - 1)}%`
+                );
+
+                // search item animation
+                const menúFlontantes = document.querySelectorAll(".menu-flotante");
+                menúFlontantes.forEach((element) => {
+                    element.classList.add("d-none");
+                    element.parentElement.addEventListener("mouseover", () =>
+                        element.classList.remove("d-none")
+                    );
+                    element.parentElement.addEventListener("mouseout", (e) =>
+                        element.classList.add("d-none")
+                    );
+                });
+
+            } catch (error) {
+                triggerMessage("Error al cargar el horario", "Error");
+                triggerMessage(error, "Error");
+            }
+
+            // droppables
+            // forall the .bloque-elements add the event listeners for drag and drop
+            <?php if ($write) : ?>
+                document.querySelectorAll(".bloque-clase").forEach(element => {
+                    function dragStart() {
+                        this.classList.add("dragging");
+                    }
+
+                    function dragEnd() {
+                        this.classList.remove("dragging");
+                    }
+
+                    element.addEventListener("dragstart", dragStart);
+                    element.addEventListener("dragend", dragEnd);
+                });
+
+                // forall the cells that are not .bloque-clase add the event listeners for drag and drop
+                document.querySelectorAll("td:not(.bloque-clase)").forEach(element => {
+                    function dragOver(e) {
+                        e.preventDefault();
+                        this.classList.add("dragging-over");
+                    }
+
+                    function dragLeave() {
+                        this.classList.remove("dragging-over");
+                    }
+
+                    function drop() {
+                        this.classList.remove("dragging-over");
+                        const dragging = document.querySelector(".dragging");
+
+                        const id = /* for data-ids */ dragging.getAttribute("data-ids");
+                        const hora = /* for data-hora */ this.id.split("-")[1];
+                        const días = ["lunes", "martes", "miércoles", "jueves", "viernes", "sábado"];
+                        let día = /* for data-dia */ this.id.split("-")[2];
+                        día = días.indexOf(día) + 1;
+
+                        //  rowspan
+                        const bloques = parseInt(dragging.getAttribute("rowspan"));
+                        const horaMoment = moment(hora, "HH:mm");
+                        const horaFin = horaMoment.add(bloques * 15, "minutes");
+
+                        const limit = moment('22:00', 'HH:mm');
+
+                        if (horaFin.isAfter(limit)) {
+                            triggerMessage("No se puede mover el bloque a esa hora", "Error");
+
+                            // scroll to the top
+                            window.scrollTo(0, 0);
+                            return;
+                        }
+
+                        // get the horario
+                        // remove the horario
+                        const bloque = document.querySelector(`.bloque-clase[data-ids="${id}"]`);
+
+                        // remove all children
+                        while (bloque.firstChild) {
+                            bloque.removeChild(bloque.firstChild);
+                        }
+
+                        // prepend a loading child
+                        const loading = `<div class="spinner-border" role="status" style="width: 3rem; height: 3rem;">
+                                            <span class="sr-only">Loading...</span>
+                                        </div>`;
+                        bloque.insertAdjacentHTML("afterbegin", loading);
+                        // add style vertical-align: middle
+                        bloque.style.verticalAlign = "middle";
+                        bloque.classList.add("text-center");
+                        // remove draggable
+                        bloque.removeAttribute("draggable");
+
+                        moveHorario(id, día, hora);
+                    }
+
+                    element.addEventListener("dragover", dragOver);
+                    element.addEventListener("dragleave", dragLeave);
+                    element.addEventListener("drop", drop);
+                });
+            <?php endif; ?>
+        }
+        async function guardar(id) {
+            const btn = document.querySelector("#btn-guardar");
+            const clone = btn.cloneNode(true);
+
+            const data = {
+                dia: document.querySelector("#editor_dia"),
+                salon: document.querySelector("#editor_salón"),
+                //vprofesor: document.querySelector("#editor_profesor"),
+                duración: document.querySelector("#editor_duración")
+            };
+
+            const dia = data.dia.value;
+            const salon = data.salon.value;
+
+            if (salon == "") {
+                return;
+            }
+
+            const hora = document.querySelector("#selector_horas").value;
+            const minutos = document.querySelector("#selector_minutos").value;
+            const bloques = document.querySelector("#dlduración li.selected")?.getAttribute("data-bloques");
+
+            if (bloques == null) {
+                document.querySelector("#dlduración").classList.add("is-invalid");
+                return;
+            }
+
+            const start = moment(`${hora}:${minutos}`, "HH:mm").add(bloques * 15, "minutes");
+            const end = moment("22:00", "HH:mm");
+
+            if (start.isAfter(end)) {
+                document.querySelector("#dlduración").classList.add("is-invalid");
+                return;
+            }
+
+            const profesoresList = [...document.querySelectorAll("#profesores li")];
+
+            if (profesoresList.length == 0) {
+                return;
+            }
+
+            const formData = new FormData();
+            // const id = btn.getAttribute("data-id");
+            formData.append("id", id);
+            formData.append("hora", `${hora}:${minutos}`);
+            formData.append("día", dia);
+            formData.append("salón", salon);
+            const profesores = profesoresList.map(li => li.getAttribute("data-id"));
+            formData.append("duración", data.duración.value);
+            formData.append("profesores", profesores.join(","));
+
+            // loading btn
+            btn.innerHTML = 'Guardando...';
+            // disable btn
+            btn.disabled = true;
+
+            const response = await fetch("action/action_horario_update.php", {
+                method: "POST",
+                body: formData
+            }).then(res => res.json());
+
+            if (response.status == "success") {
+                triggerMessage("Horario actualizado", "Éxito", "success");
+                btn.innerHTML = '<i class="ing-aceptar ing"></i> Guardado';
+                btn.classList.add("btn-success");
+                btn.classList.remove("btn-primary");
+
+                // return to the initial state
+                setTimeout(() => {
+                    buscarGrupo();
+                    btn.replaceWith(clone);
+                    $("#modal-editar").modal("hide");
+                }, 100);
+            } else {
+                triggerMessage(response.message, "Error");
+                btn.replaceWith(clone);
+
+                $("#modal-editar").modal("hide");
+            }
+
+        }
+        async function borrarHorario(id) {
+            const btn = document.querySelector("#btn-borrar");
+            const clone = btn.cloneNode(true);
+
+            const formData = new FormData();
+            formData.append("id", id);
+
+            const response = await fetch("action/action_horario_delete.php", {
+                method: "POST",
+                body: formData
+            }).then(res => res.json());
+
+            if (response.status == "success") {
+                triggerMessage('Horario borrado', "Éxito", "success");
+                btn.innerHTML = '<i class="ing-aceptar ing"></i> Borrado';
+                btn.classList.add("btn-success");
+                btn.classList.remove("btn-danger");
+
+                // return to the initial state
+                setTimeout(() => {
+                    buscarGrupo();
+                    btn.replaceWith(clone);
+                    $("#modal-borrar").modal("hide");
+                }, 1000);
+            } else {
+                triggerMessage(response.message, "Error");
+                btn.replaceWith(clone);
+
+                $("#modal-borrar").modal("hide");
+            }
+
+        }
+
+        function guardarHorario() {
+            let goBack = false;
+            const data = {
+                grupo: document.querySelector("#grupo"),
+                dia: document.querySelector("#editor_dia"),
+                salon: document.querySelector("#editor_salón"),
+                profesor: document.querySelector("#new_profesor"),
+                duración: document.querySelector("#editor_duración"),
+                materia: document.querySelector("#dlMateria")
+            };
+
+            const dia = data.dia.value;
+            const salon = data.salon.value;
+            const profesor = data.profesor.value;
+            const materia = data.materia.value;
+            const duración = data.duración.value;
+
+            if (duración == "") {
+                invalidDatalist("#new_duración");
+                goBack = true;
+            }
+
+            if (profesor == "") {
+                data.profesor.classList.add("is-invalid");
+                goBack = true;
+            }
+
+            if (salon == "") {
+                data.salon.classList.add("is-invalid");
+                goBack = true;
+            }
+
+            if (dia == "") {
+                invalidDatalist("#new_dia");
+                goBack = true;
+            }
+
+            if (materia == "") {
+                data.materia.classList.add("is-invalid");
+                goBack = true;
+            }
+
+            const hora = document.querySelector("#new_horas").value;
+            if (hora == "") {
+                invalidDatalist("#new_horas");
+                goBack = true;
+            }
+            const minutos = document.querySelector("#new_minutos").value;
+            if (minutos == "") {
+                invalidDatalist("#new_minutos");
+                goBack = true;
+            }
+
+            const bloques = document.querySelector("#dlNewDuración li.selected")?.getAttribute("data-bloques");
+            const start = moment(`${hora}:${minutos}`, "HH:mm").add(bloques * 15, "minutes");
+            const end = moment("22:00", "HH:mm");
+
+            if (start.isAfter(end)) {
+                document.querySelector("#dlNewDuración").classList.add("is-invalid");
+                goBack = true;
+            }
+
+            if (goBack) {
+                return;
+            }
+
+            const formData = new FormData();
+            formData.append("grupo", document.querySelector("#filter_grupo").value);
+            formData.append("hora", `${hora}:${minutos}`);
+            formData.append("día", dia);
+            formData.append("salón", salon);
+            formData.append("profesor", profesor);
+            formData.append("duración", data.duración.value);
+            formData.append("periodo", "<?= $user->periodo ?>");
+            formData.append("materia", materia);
+            formData.append("facultad", "<?= $user->facultad['facultad_id'] ?>");
+
+
+            fetch("action/action_horario_create.php", {
+                method: "POST",
+                body: formData
+            }).then(res => res.json()).then(response => {
+                const carrera = document.querySelector("#dlcarrera li.selected")
+                // call it's click event
+                carrera?.click();
+                if (response.status == "success") {
+                    triggerMessage("Horario guardado", "Éxito", "success");
+                    btn.innerHTML = /* html */ `<i class="ing-aceptar ing"></i> Guardado`;
+                    btn.classList.add("btn-success");
+                    btn.classList.remove("btn-primary");
+
+                    // return to the initial state
+                    setTimeout(() => {
+                        btn.classList.remove("btn-success");
+                        btn.classList.add("btn-primary");
+
+
+                        // this modal
+                        $("#modal-nuevo").modal("hide");
+                        const modalNuevo = document.querySelector("#modal-nuevo");
+                        modalNuevo.querySelectorAll("input").forEach(input => {
+                            input.value = "";
+                        });
+
+                        // reset the datalist
+                        modalNuevo.querySelectorAll(".datalist").forEach(datalist => {
+                            const cloneDatalist = modalNuevoClone.querySelector(`#${datalist.id}`).cloneNode(true);
+                            // log
+                            datalist.replaceWith(cloneDatalist);
+                        });
+                        // remove all is-valid and is-invalid
+                        modalNuevo.querySelectorAll(".is-valid, .is-invalid").forEach(el => {
+                            el.classList.remove("is-valid", "is-invalid");
+                        });
+
+                    }, 100);
+                } else {
+                    triggerMessage(response.message, "Error");
+                    $("#modal-nuevo").modal("hide");
+                }
+            }).then(() => {
+                buscarGrupo();
+            }).catch(err => {
+                triggerMessage(err, "Error");
+
+                $("#modal-nuevo").modal("hide");
+            });
+
+            // buscarGrupo();
+        }
+
+        function moveHorario(id, día, hora) {
+
+            const formData = new FormData();
+
+            formData.append("id", id);
+            formData.append("hora", hora);
+            formData.append("día", día);
+
+            fetch("action/action_horario_update.php", {
+                method: "POST",
+                body: formData
+            }).then(res => res.json()).then(response => {
+                if (response.status == "success") {
+                    triggerMessage("Horario movido", "Éxito", "success");
+                } else {
+                    triggerMessage(response.message, "Error");
+                }
+            }).then(() => {
+                buscarGrupo();
+            }).catch(err => {
+                triggerMessage(err, "Error");
+            });
+
+        }
+
+        function extractFromModal() {
+            // remove all is-valid and is-invalid
+            document.querySelectorAll(".is-valid, .is-invalid").forEach(el =>
+                el.classList.remove("is-valid", "is-invalid")
+            );
+            let goBack = false;
+            const modalNuevo = document.querySelector("#modal-nuevo");
+            const data = {
+                dia: document.querySelector("#editor_dia"),
+                salon: document.querySelector("#editor_salón"),
+                duración: document.querySelector("#editor_duración")
+            };
+
+            const dia = data.dia.value;
+            const salon = data.salon.value;
+            const hora = document.querySelector("#selector_horas").value;
+            const minutos = document.querySelector("#selector_minutos").value;
+            const bloques = document.querySelector("#dlduración li.selected")?.getAttribute("data-bloques");
+            const materia = document.querySelector("#dlMateria");
+            const grupo = document.querySelector("input#grupo");
+
+            const start = moment(`${hora}:${minutos}`, "HH:mm").add(bloques * 15, "minutes");
+            const end = moment("22:00", "HH:mm");
+            const profesoresList = [...document.querySelectorAll("#profesores li")];
+
+            if (data.duración.value == "") {
+                invalidDatalist("#editor_duración");
+                goBack = true;
+            }
+
+            if (dia == "") {
+                invalidDatalist("#editor_dia");
+                goBack = true;
+            }
+
+            if (start.isAfter(end)) {
+                document.querySelector("#dlduración").classList.add("is-invalid");
+                goBack = true;
+            }
+
+            if (salon == "") {
+                data.salon.classList.add("is-invalid");
+                goBack = true;
+            }
+
+            if (profesoresList.length == 0) {
+                document.querySelector("#dlProfesor").classList.add("is-invalid");
+                goBack = true;
+            }
+
+            if (materia.value == "" || materia.classList.contains("is-invalid")) {
+                document.querySelector("#dlMateria").classList.add("is-invalid");
+                goBack = true;
+            }
+
+            if (grupo.value == "") {
+                grupo.classList.add("is-invalid");
+                goBack = true;
+            }
+
+
+            if (goBack) return null;
+
+            const materia_id = document.querySelector(`#lista_materias option[value="${materia.value}"]`).getAttribute("data-id");
+
+            const formData = new FormData();
+            formData.append("hora", `${hora}:${minutos}`);
+            formData.append("día", dia);
+            formData.append("salón", salon);
+            const profesores = profesoresList.map(li => li.getAttribute("data-id"));
+            formData.append("duración", data.duración.value);
+            formData.append("profesores", profesores.join(","));
+            formData.append("materia", materia_id);
+
+            return formData;
+        }
+
+        function resetFormModal() {
+            const modalNuevo = document.querySelector("#modal-editar");
+            modalNuevo.querySelectorAll("input").forEach(input => input.value = "");
+
+            // remove all is-valid and is-invalid
+            modalNuevo.querySelectorAll(".is-valid, .is-invalid").forEach(el => el.classList.remove("is-valid", "is-invalid"));
+            modalNuevo.querySelectorAll(".datalist-invalid").forEach(el => el.classList.remove("datalist-invalid"));
+            modalNuevo.querySelector("#profesores").innerHTML = "";
+
+            // reset the datalist
+            for (var key in datalists)
+                datalists[key][0].innerText = datalists[key][1];
+            // remove disabled attribute
+            modalNuevo.querySelectorAll("[disabled]").forEach(el => el.removeAttribute("disabled"));
+            // remove bg-info
+            modalNuevo.querySelectorAll(".bg-info").forEach(el => el.classList.remove("bg-info"));
+        }
+
+        function insertHorario(horario) {
+            const fetchOptions = {
+                method: "POST",
+                body: horario
+            };
+
+            return fetch("action/action_horario_create.php", fetchOptions).then(res => res.json()).then(response => {
+                if (response.status == "success") {
+                    triggerMessage("Horario insertado", "Éxito", "success");
+                } else {
+                    triggerMessage(response.message, "Error");
+                }
+            }).then(() => {
+                // if the table isn't displayed, just show a message
+                if (table.style.display == "none") {
+                    triggerMessage("Horario insertado", "Éxito", "success");
+                    return;
+                }
+
+                buscarGrupo();
+            }).catch(err => {
+                triggerMessage(err, "Error");
+            });
+        }
+
+        // initial state
+        {
+            // fill the table with empty cells
+            [...Array(16).keys()].map(x => x + 7).forEach(hora => {
+                // add 7 rows for each hour
+                [0, 15, 30, 45].map(minute => minute.toString().padStart(2, '0')).forEach(minute => {
+                    const tr = document.createElement("tr");
+                    tr.id = `hora-${hora}:${minute}`;
+                    tr.classList.add(hora > 13 ? "tarde" : "mañana");
+                    if (minute == 0) {
+                        const th = document.createElement("th");
+                        th.classList.add("text-center");
+                        th.scope = "row";
+                        th.rowSpan = 4;
+                        th.innerText = `${hora}:00`;
+                        th.style.verticalAlign = "middle";
+                        tr.appendChild(th);
+                    }
+
+                    ["lunes", "martes", "miércoles", "jueves", "viernes", "sábado"].forEach(día => {
+                        const td = document.createElement("td");
+                        td.id = `hora-${hora}:${minute}-${día}`;
+                        tr.appendChild(td);
+                    });
+                    document.querySelector("tbody#horario").appendChild(tr);
+                });
+            })
+
+            // add an inital height to the table cells
+            // query selector All tds and ths inside the tbody#horario
+            // previous query selector: "tbody#horario td, tbody#horario tr"
+            document.querySelectorAll("tbody#horario td, tbody#horario tr").forEach(element => element.style.height = "2.5rem");
+            document.getElementById('dlProfesor').addEventListener('input', function(e) {
+                var input = document.getElementById('dlProfesor');
+                var value = input.value;
+                var option = document.querySelector(`option[value="${value}"]`);
+
+                if (value == "") {
+                    input.classList.remove("is-invalid");
+                    return;
+                }
+
+                if (!option) {
+                    document.getElementById('editor_profesor').value = "";
+                    // remove is valid class
+                    input.classList.remove("is-valid");
+                    // add is invalid class
+                    input.classList.add("is-invalid");
+                    return
+                }
+
+                var id = option.getAttribute('data-id');
+                if (!document.querySelector(`li[data-id="${id}"]`))
+                    listProfesor({
+                        id: id,
+                        grado: option.getAttribute('data-grado'),
+                        profesor: option.getAttribute('data-profesor'),
+                        clave: option.getAttribute('data-clave')
+                    });
+                e.target.value = "";
+            });
+            document.getElementById('dlMateria').addEventListener('input', function(e) {
+                var input = document.getElementById('dlMateria');
+                var value = input.value;
+                var option = document.querySelector(`option[value="${value}"]`);
+
+                if (value == "") {
+                    input.classList.remove("is-invalid");
+                    return;
+                }
+
+                if (!option) {
+                    // remove is valid class
+                    input.classList.remove("is-valid");
+                    // add is invalid class
+                    input.classList.add("is-invalid");
+                    return;
+                }
+
+                input.classList.remove("is-invalid");
+            });
+        }
+
+        // state
+        const table = document.querySelector("table");
+        const empty_table = table.innerHTML;
+        document.querySelectorAll('.hidden').forEach(element => element.style.display = "none");
+        // hide the table
+        table.style.display = "none";
+        disableDatalist("#filter_grupo");
+
+        document.getElementById("btn-excel-horario").addEventListener("click", () => {
+            const formData = new FormData();
+            const grupo = document.getElementById("filter_grupo").value;
+            formData.append("grupo", grupo);
+            formData.append('sábado', gHorarios.some(horario => horario.dia == "sábado"));
+            formData.append("horarios", JSON.stringify(gHorarios));
+            // min and max hour
+            formData.append("min", gHorarios.reduce((min, horario) => {
+                const hora = horario.hora.split(":").map(x => parseInt(x))[0]
+                return Math.min(hora, min);
+            }, 24));
+            formData.append("max", gHorarios.reduce((max, horario) => {
+                const {
+                    hora,
+                    bloques
+                } = horario;
+                // after hour
+                const lastHour = moment(hora, "HH:mm").add(bloques * 15, "minutes");
+                const lastMoment = moment(`${lastHour.format("HH")}:00`, "HH:mm");
+                const intHour = parseInt(lastHour.format("HH"));
+
+                return Math.max(lastMoment.isSame(lastHour) ? intHour - 1 : intHour, max);
+            }, 0));
+
+            fetch("export/horario_excel.php", {
+                    method: "POST",
+                    body: formData
+                })
+                .then(response => response.blob())
+                .then(blob => {
+                    const url = window.URL.createObjectURL(blob);
+                    const a = document.createElement('a');
+                    a.style.display = 'none';
+                    a.setAttribute('href', url);
+                    a.setAttribute('download', `horario-${grupo}.xlsx`);
+                    document.body.appendChild(a);
+                    a.click();
+                    window.URL.revokeObjectURL(url);
+                })
+                .catch(() => triggerMessage("error", "Error al exportar el horario"));
+        });
+        // on click the li element, inside datalist #dlcarera
+        document.querySelectorAll("#dlcarrera li").forEach(async li => {
+            li.addEventListener("click", async () => {
+                // get the data-id from the li element
+                const carrera = li.getAttribute("data-id");
+                const facultad = '<?= $user->facultad['facultad_id'] ?>';
+                const periodo = '<?= $user->periodo ?>';
+
+                const formData = new FormData();
+                formData.append("carrera", carrera);
+                formData.append("facultad", facultad);
+                formData.append("periodo", periodo);
+
+                const dlgrupo = document.querySelector("#dlgrupo ul");
+                const grupoModal = document.querySelector("#grupoModal ul");
+                const promptDL = document.querySelector("#dlgrupo .datalist-input");
+
+                try {
+                    const {
+                        status,
+                        grupos
+                    } = await fetch("action/action_grupo.php", {
+                        method: "POST",
+                        body: formData
+                    }).then(res => res.json());
+
+                    dlgrupo.innerHTML = "";
+
+                    if (status != "success") {
+                        throw new Error("Error al cargar los grupos");
+                    } else if (grupos.length == 0) {
+                        throw new Error("No hay grupos para esta carrera, puedes crear uno nuevo");
+                    }
+
+                    document.getElementById("message").innerHTML = "";
+                    grupoModal.querySelectorAll("li:not(:first-child)").forEach(li => li.remove());
+                    document.querySelector("#grupo").setAttribute("hidden", true);
+                    grupos.forEach(grupo => {
+                        const li = document.createElement("li");
+                        // data-id is the id of the group
+                        li.setAttribute("data-id", grupo);
+                        li.textContent = grupo;
+                        dlgrupo.appendChild(li);
+                        li.addEventListener("click", async () => await setTimeout(buscarGrupo, 0))
+
+                        // remove from grupoModal all the li elements except the first one
+                        grupoModal.appendChild(li.cloneNode(true));
+                    });
+
+                    // on click the li element, inside datalist #grupoModal
+                    document.querySelectorAll("#grupoModal li").forEach(li => {
+                        li.addEventListener("click", () => {
+                            const grupo = li.getAttribute("data-id");
+                            const filter_grupo = document.querySelector("#grupo");
+                            filter_grupo.classList.remove("is-invalid");
+                            if (grupo == "")
+                                filter_grupo.removeAttribute("hidden");
+                            else
+                                filter_grupo.setAttribute("hidden", true);
+                            filter_grupo.value = grupo;
+                        });
+                    });
+                    disableDatalist("#filter_grupo", false);
+
+                } catch (error) {
+                    // remove Error: from the error message
+                    error = error.toString().replace("Error: ", "");
+                    triggerMessage(error, "Error", "warning");
+                    console.error(error);
+                    disableDatalist("#filter_grupo", true);
+                } finally {
+                    document.querySelector("#nuevo").classList.remove("d-none");
+                    // write Seleccionar grupo
+                    promptDL.textContent = "Seleccionar grupo";
+                    console.log(promptDL);
+                    // to #filter_grupo input value
+                    const filter_grupo = document.querySelector("#filter_grupo");
+                    filter_grupo.value = "";
+                    table.style.display = "none";
+                }
+
+                const datalist = document.querySelector("#materias");
+                const materias = await fetch("action/action_materias.php", {
+                    method: "POST",
+                    body: formData
+                }).then(res => res.json());
+            });
+
+        })
+        // on modal edit, show the data
+        $("#modal-editar").on("show.bs.modal", async function(event) {
+            document.querySelectorAll("#modal-editar .is-invalid, #modal-editar .is-valid")?.forEach(element => element.classList.remove("is-invalid", "is-valid"));
+
+
+            const button = event.relatedTarget;
+            const parentId = button.parentElement.parentElement.getAttribute("data-ids");
+            const grupo = document.querySelector("#filter_grupo").value;
+            setDatalist("#dli-grupo", grupo);
+            document.querySelector("#grupo").value = grupo;
+            document.querySelector("#grupo").setAttribute("hidden", true);
+
+            if (parentId == null) {
+                resetFormModal();
+                disableDatalist("#dli-grupo", false);
+                setDatalist("#dli-grupo", grupo);
+                document.querySelector("#grupo").value = grupo;
+                if (grupo == "")
+                    document.querySelector("#grupo").removeAttribute("hidden");
+
+                // search the data-id of the day and click it
+                setDatalistFirst("#selector_horas");
+                setDatalist("#selector_minutos", 15);
+                const input_materia = document.getElementById('dlMateria');
+                const formData = new FormData();
+                formData.append("carrera", document.querySelector("#filter_carrera").value);
+
+                try {
+                    const {
+                        status,
+                        materias,
+                    } = await fetch("action/action_materias.php", {
+                        method: "POST",
+                        body: formData
+                    }).then(res => res.json());
+
+                    if (status != "success")
+                        throw new Error("Error al cargar las materias");
+                    else if (materias.length == 0)
+                        throw new Error("No hay materias para esta carrera");
+
+                    const dlMateria = document.querySelector("#lista_materias");
+                    dlMateria.innerHTML = "";
+                    materias.forEach(materia => {
+                        const option = document.createElement("option");
+                        option.setAttribute("data-id", materia.id);
+                        option.setAttribute("value", materia.nombre);
+                        dlMateria.appendChild(option);
+                    });
+
+                } catch (error) {
+                    // remove Error: from the error message
+                    error = error.toString().replace("Error: ", "");
+                    triggerMessage(error, "Error, no se pudo cargar las materias");
+                    console.error(error);
+                }
+
+                const btnGuardar = document.getElementById('btn-guardar');
+                // btnGuardar.onclick = () => guardar(id);
+
+
+                btnGuardar.onclick = async () => {
+                    const formData = extractFromModal();
+                    if (formData == null)
+                        return;
+                    // check if a field is empty
+                    for (const value of formData.values())
+                        if (value == "")
+                            return triggerMessage("Todos los campos son obligatorios", "Error", "danger", "message_editar", true);
+                    formData.append("facultad", <?= $user->facultad['facultad_id'] ?>);
+                    formData.append("periodo", <?= $user->periodo ?>);
+                    formData.append("grupo", document.querySelector("input#grupo").value);
+
+                    await insertHorario(formData);
+                    resetFormModal();
+                    $("#modal-editar").modal("hide");
+                };
+
+                return;
+            }
+            const {
+                id,
+                dia,
+                hora,
+                materia,
+                salon,
+                profesores,
+                duracion
+            } = gHorarios?.find(horario => horario.id == parentId);
+
+            // search the data-id of the day and click it
+            const númeroDía = ['domingo', 'lunes', 'martes', 'miércoles', 'jueves', 'viernes', 'sábado'].reduce((acc, curr, key) => (curr == dia) ? key : acc);
+            document.querySelector(`#dldia li[data-id="${númeroDía}"]`)?.click();
+            document.querySelector(`#dlduración li[data-id="${duracion}"]`)?.click();
+            const horas = hora.split(":")[0];
+            const minutos = hora.split(":")[1];
+            setDatalist("#selector_horas", horas);
+            setDatalist("#selector_minutos", minutos);
+            document.getElementById('editor_salón').value = salon;
+            const input_materia = document.getElementById('dlMateria');
+            input_materia.value = materia;
+            // disable the input
+            input_materia.setAttribute("disabled", true);
+            input_materia.classList.add("bg-info");
+
+            disableDatalist("#dli-grupo")
+
+            // get the option with data-id profesor
+            document.getElementById("profesores").innerHTML = "";
+            profesores.forEach(listProfesor);
+
+            const btnGuardar = document.getElementById('btn-guardar');
+            btnGuardar.onclick = () => guardar(id);
+        })
+        $("#modal-borrar").modal({
+            show: false,
+            backdrop: "static",
+            keyboard: false,
+        })
+        $("#modal-borrar").on("show.bs.modal", async function(event) {
+            const button = event.relatedTarget;
+            const id = button.parentElement.parentElement.getAttribute("data-ids");
+
+            const btnBorrar = document.getElementById('btn-borrar');
+            btnBorrar.onclick = () => borrarHorario(id);
+        })
+    </script>
+
+    </html>

二进制
css/bg-progress-bar.png


文件差异内容过多而无法显示
+ 0 - 0
css/bootstrap-ulsa.min.css


+ 39 - 0
css/calendar.css

@@ -0,0 +1,39 @@
+/*
+Colores de date picker
+*/
+.ui-widget-header{ background-color:#f0f0f0;}
+/*.ui-state-default, .ui-widget-content .ui-state-default, .ui-widget-header .ui-state-default, .ui-button, html .ui-button.ui-state-disabled:hover, html .ui-button.ui-state-disabled:active {
+    border: 1px solid #c5c5c5; background: #f7f7f8;
+}*/
+.ui-state-highlight, .ui-widget-content .ui-state-highlight, .ui-widget-header .ui-state-highlight { background: #d21034; color: #fff;}
+.ui-state-active, .ui-widget-content .ui-state-active, .ui-widget-header .ui-state-active, a.ui-button:active, .ui-button:active, .ui-button.ui-state-active:hover {
+    border: 1px solid #ffffff; background: #001d68; color:white!important;
+}
+
+
+/* Month Picker */
+/*
+.month-picker-previous .ui-icon-circle-triangle-w {
+    display: inline-block;
+    -webkit-transform: rotate(90deg);
+    -moz-transform: rotate(90deg);
+    -o-transform: rotate(90deg);
+}
+.month-picker-previous .ui-icon-circle-triangle-w:before {
+    font-family: "ingfont";
+    content: '\e90b';
+}
+.month-picker-next .ui-icon-circle-triangle-e {
+    display: inline-block;
+    -webkit-transform: rotate(-90deg);
+    -moz-transform: rotate(-90deg);
+    -o-transform: rotate(-90deg);
+}
+.month-picker-next .ui-icon-circle-triangle-e:before {
+    font-family: "ingfont";
+    content: '\e90b';
+}*/
+.month-picker-year-table .ui-button {
+    color: #001D68 !important;
+}
+

+ 46 - 0
css/checador.css

@@ -0,0 +1,46 @@
+
+body {
+    font-family: 'indivisa-text';
+    /*background-image: linear-gradient(rgba(0,29,104, 0.3), rgba(0,29,104, 0.3)), url('../imagenes/fondochecador.png');*/
+    background-image:  url('../imagenes/fondochecador.jpg');
+    background-repeat: no-repeat;
+    background-attachment: fixed;
+    background-size: cover;
+    background-position: center;
+    height: 100%;
+    overflow: hidden;
+}
+
+.hora{font-size: 4rem; border-right: 2px solid #d21034; line-height: 1.1; padding-bottom: 8px;}
+.fecha{font-size: 1.9rem; line-height: 1.2em; }
+.facultad{font-size: 1.5rem; font-weight: bold; color: #aaa;}
+.checa-box{width:800px; min-height: 380px; -webkit-box-shadow: 0px 0px 5px 1px rgba(150,150,150,0.2); -moz-box-shadow: 0px 0px 5px 1px rgba(150,150,150,0.2); box-shadow: 0px 0px 5px 1px rgba(150,150,150,0.2);}
+
+h1, h2, h3 {letter-spacing: 1px; position: relative; color: #001e61;}
+.subtitle:before{ content: ''; position: absolute; bottom: -8px; left: 0; width: 80px; display: block; background: #d21034; height: 3px; }
+
+.text-big{font-size:3.6rem;}
+#list-result{font-size:1.15rem;}
+#list-result li{margin:0.2em 0; border-bottom: 1px dashed #969696;}
+
+.text-clave{ outline: none; color: #b7b7b7 !important; background: transparent; border: 1px solid #dbdcdd; border-radius: 5px; -webkit-box-sizing: border-box; -moz-box-sizing: border-box; box-sizing: border-box; font-size: 12px; font-weight: 300; vertical-align: middle; text-align:center; padding: 10px 20px;}
+.text-clave:focus{ color: #001d68 !important; border-color: #001d68;}
+.sin-servicio, .text-clave{font-size:1.7rem;}
+.aviso{ font-size:120%;}
+.mensaje{ font-size: 7vh; }
+
+@media (max-width: 768px) {
+    .hora{ font-size: 3.2rem;}
+    .fecha{font-size: 1.4rem;}
+    .checa-box{width:100%;}
+    .text-big{font-size:3rem;}
+    .sin-servicio, .text-clave{font-size:1.2rem;}
+}
+@media (max-width: 576px) {
+    .hora{font-size: 2rem !important;}
+    .fecha, .titulo{font-size: 1rem;}
+    #logo{width: 75%;}
+    .facultad{font-size: 1.2rem}
+    .text-big{font-size:2.8rem;}
+    
+}

二进制
css/cif-icons-20xy.png


+ 143 - 0
css/clockpicker.css

@@ -0,0 +1,143 @@
+/*!
+ * ClockPicker v{package.version} for Bootstrap (http://weareoutman.github.io/clockpicker/)
+ * Copyright 2014 Wang Shenwei.
+ * Licensed under MIT (https://github.com/weareoutman/clockpicker/blob/gh-pages/LICENSE)
+ */
+
+.clockpicker .input-group-addon {
+	cursor: pointer;
+}
+.clockpicker-moving {
+	cursor: move;
+}
+
+.clockpicker-popover .popover-title {
+	background: #D6D8DB;
+	color: #777777;
+	font-size: 1.5rem;
+	line-height: 2rem;
+	text-align: center;
+}
+.clockpicker-popover .popover-title span {
+	cursor: pointer;
+}
+.clockpicker-popover .popover-content {
+	padding: 0.5rem;
+}
+.popover-content:last-child {
+	border-bottom-left-radius: 5px;
+	border-bottom-right-radius: 5px;
+}
+.clockpicker-plate {
+	background-color: #EFEFEF;	
+	border-radius: 50%;
+	width: 200px;
+	height: 200px;
+	overflow: visible;
+	position: relative;
+	/* Disable text selection highlighting. Thanks to Hermanya */
+	-webkit-touch-callout: none;
+	-webkit-user-select: none;
+	-khtml-user-select: none;
+	-moz-user-select: none;
+	-ms-user-select: none;
+	user-select: none;
+}
+.clockpicker-canvas,
+.clockpicker-dial {
+	width: 200px;
+	height: 200px;
+	position: absolute;
+	left: -1px;
+	top: -1px;
+}
+.clockpicker-minutes {
+	visibility: hidden;
+}
+.clockpicker-tick {
+	border-radius: 50%;
+	color: #777777;
+	line-height: 1.5rem;
+	text-align: center;
+	width: 1.5rem;
+	height: 1.5rem;
+	position: absolute;
+	cursor: pointer;
+}
+.clockpicker-tick.active{
+	color: #FFFFFF;
+}
+.clockpicker-tick:hover {
+	background-color:  #D6D8DB;
+}
+.clockpicker-button {
+	background-image: none;
+	background-color: #fff;
+	border-width: 1px 0 0;
+	border-top-left-radius: 0;
+	border-top-right-radius: 0;
+	margin: 0;
+	padding: 10px 0;
+}
+.clockpicker-button:hover {
+	background-image: none;
+	background-color: #ebebeb;
+}
+.clockpicker-button:focus {
+	outline: none!important;
+}
+.clockpicker-dial {
+	-webkit-transition: -webkit-transform 350ms, opacity 350ms;
+	-moz-transition: -moz-transform 350ms, opacity 350ms;
+	-ms-transition: -ms-transform 350ms, opacity 350ms;
+	-o-transition: -o-transform 350ms, opacity 350ms;
+	transition: transform 350ms, opacity 350ms;
+}
+.clockpicker-dial-out {
+	opacity: 0;
+}
+.clockpicker-hours.clockpicker-dial-out {
+	-webkit-transform: scale(1.2, 1.2);
+	-moz-transform: scale(1.2, 1.2);
+	-ms-transform: scale(1.2, 1.2);
+	-o-transform: scale(1.2, 1.2);
+	transform: scale(1.2, 1.2);
+}
+.clockpicker-minutes.clockpicker-dial-out {
+	-webkit-transform: scale(.8, .8);
+	-moz-transform: scale(.8, .8);
+	-ms-transform: scale(.8, .8);
+	-o-transform: scale(.8, .8);
+	transform: scale(.8, .8);
+}
+.clockpicker-canvas {
+	-webkit-transition: opacity 175ms;
+	-moz-transition: opacity 175ms;
+	-ms-transition: opacity 175ms;
+	-o-transition: opacity 175ms;
+	transition: opacity 175ms;
+}
+.clockpicker-canvas-out {
+	opacity: 0.25;
+}
+.clockpicker-canvas-bearing,
+.clockpicker-canvas-fg {
+	stroke: none;
+	fill: #006094;
+}
+.clockpicker-canvas-bg {
+	stroke: none;
+	fill: #006094;
+}
+.clockpicker-canvas-bg-trans {
+	fill: rgba(0, 96, 148, 0.25);
+}
+.clockpicker-canvas line {
+	stroke: #006094;
+	stroke-width: 1;
+	stroke-linecap: round;
+	/*shape-rendering: crispEdges;*/
+}
+.clock[readonly]{
+    background-color: transparent;
+}

文件差异内容过多而无法显示
+ 9 - 0
css/custominputfile.min.css


文件差异内容过多而无法显示
+ 4 - 0
css/fa_all.css


二进制
css/images/ui-icons_444444_256x240.png


二进制
css/images/ui-icons_555555_256x240.png


二进制
css/images/ui-icons_777620_256x240.png


二进制
css/images/ui-icons_777777_256x240.png


二进制
css/images/ui-icons_cc0000_256x240.png


二进制
css/images/ui-icons_ffffff_256x240.png


+ 30 - 0
css/index.css

@@ -0,0 +1,30 @@
+/*
+To change this license header, choose License Headers in Project Properties.
+To change this template file, choose Tools | Templates
+and open the template in the editor.
+*/
+/* 
+    Created on : 26/03/2020, 06:44:41 PM
+    Author     : Ale
+*/
+.content{height: 700px;background: url('../imagenes/fondochecador.jpg') no-repeat; object-fit: fit;}
+.logSize{ width: 50% !important; max-width: 600px; }
+.icon{ font-size:2rem; color: #001D68; }
+.defaultShadow{ -webkit-box-shadow: 0 0 5px 1px rgba(150,150,150,.2); box-shadow: 0 0 5px 1px rgba(150,150,150,.2); }
+
+.btn-lg.arrow:after{margin-top:7px !important;}
+
+@media (max-width: 768px){
+    .content{
+        height: 400px;
+    }
+    .logSize{
+        width: 90% !important;
+    }
+    .logSize h1{font-size: 2em;}
+}
+@media (max-height: 768px){
+    .content{
+        height: 500px;
+    }
+}

+ 154 - 0
css/indivisa.css

@@ -0,0 +1,154 @@
+/* 
+    Created on : 5/12/2018, 01:25:27 PM
+    Author     : Alejandro
+    Indivisa Fonts
+*/
+
+@font-face {
+    font-family: 'indivisa-title';
+    src: url('../fonts/indivisaFont/eot/IndivisaTextSans-BoldItalic.eot');
+    src:
+         url('../fonts/indivisaFont/woff/IndivisaTextSans-BoldItalic.woff'),
+         url('../fonts/indivisaFont/ttf/IndivisaTextSans-BoldItalic.ttf') ,
+         url('../fonts/indivisaFont/eot/IndivisaTextSans-BoldItalic.IndivisaTextSans-BoldItalic');
+    font-weight: normal;
+    font-style: normal;
+}
+@font-face {
+    font-family: 'indivisa-text';
+    src: url('../fonts/indivisaFont/eot/IndivisaTextSans-Regular.eot');
+    src:
+         url('../fonts/indivisaFont/woff/IndivisaTextSans-Regular.woff'),
+         url('../fonts/indivisaFont/ttf/IndivisaTextSans-Regular.ttf'),
+         url('../fonts/indivisaFont/eot/IndivisaTextSans-Regular.svg#IndivisaTextSans-Regular');
+    font-weight: normal;
+    font-style: normal;
+}
+@font-face {
+    font-family: 'indivisa-text-black';
+    src: url('../fonts/indivisaFont/eot/IndivisaTextSans-Black.eot');
+    src:
+         url('../fonts/indivisaFont/woff/IndivisaTextSans-Black.woff'),
+         url('../fonts/indivisaFont/ttf/IndivisaTextSans-Black.ttf'),
+         url('../fonts/indivisaFont/eot/IndivisaTextSans-Black.svg#IndivisaTextSans-Black');
+    font-weight: normal;
+    font-style: normal;
+}
+@font-face {
+    font-family: 'indivisa-text-bold';
+    src: url('../fonts/indivisaFont/eot/IndivisaTextSans-Bold.eot');
+    src:
+         url('../fonts/indivisaFont/woff/IndivisaTextSans-Bold.woff'),
+         url('../fonts/indivisaFont/ttf/IndivisaTextSans-Bold.ttf'),
+         url('../fonts/indivisaFont/eot/IndivisaTextSans-Bold.svg#IndivisaTextSans-Bold');
+    font-weight: normal;
+    font-style: normal;
+}
+.indivisa-display{font-family: 'indivisa-display' !important;}
+.indivisa-title{font-family: 'indivisa-title' !important;}
+
+/* INGENIERIA FONT */
+@font-face {
+  font-family: 'ingfont';
+  src:  url('../fonts/ingenieria/ingfont.eot?1fng03');
+  src:  url('../fonts/ingenieria/ingfont.eot?1fng03#iefix') format('embedded-opentype'),
+    url('../fonts/ingenieria/ingfont.ttf?1fng03') format('truetype'),
+    url('../fonts/ingenieria/ingfont.woff?1fng03') format('woff'),
+    url('../fonts/ingenieria/ingfont.svg?1fng03#ingfont') format('svg');
+  font-weight: normal;
+  font-style: normal;
+  font-display: block;
+}
+
+.ing-lg{font-size:1.33333em;line-height:.75em;vertical-align:-.0667em}.ing-2x{font-size:2em}.ing-3x{font-size:3em}.ing-8x{font-size:8em}
+.ing-fw{text-align:center;width:1.4em}/*1.25*/
+.ing-ul{list-style-type:none;margin-left:2.5em;padding-left:0}.ing-ul>li{position:relative}.ing-li{left:-2em;position:absolute;text-align:center;width:2em;line-height:inherit}
+.ing-rotate-90{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=1)";-webkit-transform:rotate(90deg);transform:rotate(90deg)}.ing-rotate-180{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=2)";-webkit-transform:rotate(180deg);transform:rotate(180deg)}.ing-rotate-270{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=3)";-webkit-transform:rotate(270deg);transform:rotate(270deg)}.ing-flip-horizontal{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=0, mirror=1)";-webkit-transform:scaleX(-1);transform:scaleX(-1)}.ing-flip-vertical{-webkit-transform:scaleY(-1);transform:scaleY(-1)}.ing-flip-both,.ing-flip-horizontal.ing-flip-vertical,.ing-flip-vertical{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=2, mirror=1)"}.ing-flip-both,.ing-flip-horizontal.ing-flip-vertical{-webkit-transform:scale(-1);transform:scale(-1)}:root .ing-flip-both,:root .ing-flip-horizontal,:root .ing-flip-vertical,:root .ing-rotate-90,:root .ing-rotate-180,:root .ing-rotate-270{-webkit-filter:none;filter:none}
+
+[class^="ing-"], [class*=" ing-"] {
+  /* use !important to prevent issues with browser extensions that change fonts */
+  font-family: 'ingfont' !important;  speak: never;  font-style: normal;  font-weight: normal;  font-variant: normal;  text-transform: none;  line-height: 1;display: inline-block;
+
+  /* Better Font Rendering =========== */
+  -webkit-font-smoothing: antialiased;
+  -moz-osx-font-smoothing: grayscale;
+}
+
+.ing-fb1:before {content: "\e932";}
+.ing-fb2:before {  content: "\e933";}
+.ing-tw1:before {  content: "\e912";}
+.ing-tw2:before {  content: "\e900";}
+.ing-in1:before {  content: "\e91a";}
+.ing-in2:before {  content: "\e902";}
+.ing-instra1:before {  content: "\e924";}
+.ing-instra2:before {  content: "\e923";}
+.ing-youtube:before {  content: "\e90e";}
+.ing-telefono:before {  content: "\e911";}
+.ing-mail:before {  content: "\e907";}
+.ing-link:before {  content: "\e919";}
+.ing-ubicacion:before {  content: "\e908";}
+.ing-puntos:before {  content: "\e917";}
+.ing-usuario:before {  content: "\e90d";}
+.ing-pass:before {  content: "\e906";}
+.ing-menu:before {  content: "\e901";}
+.ing-salir:before {  content: "\e90f";}
+.ing-flecha:before {  content: "\e905";}
+.ing-cambiar:before {  content: "\e93c";}
+.ing-caret:before {  content: "\e90b";}
+.ing-aceptar:before {  content: "\e916";}
+.ing-cancelar:before {  content: "\e910";}
+.ing-mas:before {  content: "\e91d";}
+.ing-menos:before {  content: "\e91e";}
+.ing-editar:before {  content: "\e938";}
+.ing-buscar:before {  content: "\e939";}
+.ing-ojo:before {  content: "\e92a";}
+.ing-borrar:before {  content: "\e942";}
+.ing-basura:before {  content: "\e941";}
+.ing-camara:before {  content: "\e909";}
+.ing-importante:before {  content: "\e935";}
+.ing-bullet:before {  content: "\e943";}
+.ing-home:before {  content: "\e934";}
+.ing-formacion:before {  content: "\e914";}
+.ing-empleo:before {  content: "\e915";}
+.ing-insignia1:before {  content: "\e920";}
+.ing-insignia2:before {  content: "\e91f";}
+.ing-insignia3:before {  content: "\e921";}
+.ing-insignia4:before {  content: "\e922";}
+.ing-eventos:before {  content: "\e90a";}
+.ing-reporte:before {  content: "\e918";}
+.ing-catalogo:before {  content: "\e936";}
+.ing-evalua-cartel:before {  content: "\e913";}
+.ing-revision-cartel:before {  content: "\e90c";}
+.ing-reporte-resultados:before {  content: "\e929";}
+.ing-mi-cartel:before {  content: "\e91b";}
+.ing-galeria1:before {  content: "\e91c";}
+.ing-galeria2:before {  content: "\e925";}
+.ing-iniciar-sesion:before {  content: "\e926";}
+.ing-finalistas:before {  content: "\e927";}
+.ing-comite:before {  content: "\e92b";}
+.ing-administrador:before {  content: "\e92c";}
+.ing-estrella1:before {  content: "\e903";}
+.ing-estrella2:before {  content: "\e904";}
+.ing-carga-archivo:before {  content: "\e93d";}
+.ing-carga-multiple:before {  content: "\e93e";}
+.ing-descarga:before {  content: "\e928";}
+.ing-autorizar:before {  content: "\e92d";}
+.ing-negar:before {  content: "\e92e";}
+.ing-no-cargado:before {  content: "\e92f";}
+.ing-alumnos:before {  content: "\e91c";}
+.ing-cardex:before {  content: "\e93f";}
+.ing-configuracion:before {  content: "\e940";}
+.ing-listado-menus:before {  content: "\e944";}
+.ing-mi-cuenta:before {  content: "\e945";}
+.ing-ver:before {  content: "\e946";}
+.ing-grafica:before {content: "\e930";}
+.ing-clic:before {content: "\e931";}
+.ing-guardar:before {content: "\e937";}
+.ing-regresar:before {content: "\e93a";}
+.ing-cuadrado:before {content: "\e93b";}
+.ing-imprimir:before {content: "\e947";}
+.ing-importante2:before {content: "\e948";}
+.ing-copiar:before {content: "\e949";}
+.ing-reloj:before {content: "\e94a";}
+.ing-retardo:before {content: "\e94b";}
+.ing-justificar:before {content: "\e94c";}

文件差异内容过多而无法显示
+ 3 - 0
css/jquery-ui.css


+ 105 - 0
css/lasalle.css

@@ -0,0 +1,105 @@
+/*
+Iconografía de La Salle
+*/
+@font-face {
+  font-family: 'lasalle';
+  src: url("../fonts/lasalle/lasalle.eot");
+  src: url("../fonts/lasalle/lasalle.eot?#iefix") format('embedded-opentype'), url("../fonts/lasalle/lasalle.woff") format('woff'), url("../fonts/lasalle/lasalle.ttf") format('truetype'), url("../fonts/lasalle/lasalle.svg#lasalle") format('svg');
+  font-weight: normal;
+  font-style: normal;
+}
+.icon {
+  font-family: 'lasalle' !important;
+  speak: none;
+  font-style: normal;
+  font-weight: normal;
+  font-variant: normal;
+  text-transform: none;
+  /*line-height: 1;*/
+/* Better Font Rendering =========== */
+  -webkit-font-smoothing: antialiased;
+  -moz-osx-font-smoothing: grayscale;
+}
+.icon-fe:before {content: "\e952";}
+.icon-compras:before {content: "\e94d";}
+.icon-arte:before {content: "\e94e";}
+.icon-restaurante:before {content: "\e94f";}
+.icon-cafe:before {content: "\e950";}
+.icon-biblioteca:before {content: "\e951";}
+.icon-markFull:before {content: "\e94c";}
+.icon-comment:before {content: "\e947";}
+.icon-faith:before {content: "\e948";}
+.icon-justice:before {content: "\e949";}
+.icon-compromise:before {content: "\e94a";}
+.icon-fraternity:before {content: "\e94b";}
+.icon-telephone:before {content: "\e943";}
+.icon-onSpeaking:before {content: "\e944";}
+.icon-offSpeaking:before { content: "\e945";}
+.icon-audio:before {content: "\e946";}
+.icon-play:before {content: "\e91c";}
+.icon-link:before {content: "\e936";}
+.icon-ym:before { content: "\e937";}
+.icon-wp:before {content: "\e938";}
+.icon-read:before { content: "\e939";}
+.icon-certificate:before {content: "\e93a";}
+.icon-school:before {content: "\e93b";}
+.icon-speaker:before {content: "\e93c";}
+.icon-atom:before {content: "\e93d";}
+.icon-bag:before {content: "\e93e";}
+.icon-carbuy:before {content: "\e93f";}
+.icon-idea:before {content: "\e940";}
+.icon-hands:before {content: "\e941";}
+.icon-arrowprev:before {content: "\e942";}
+.icon-mouse:before {content: "\e900";}
+.icon-mail:before {content: "\e901";}
+.icon-down:before {content: "\e902";}
+.icon-up:before {content: "\e903";}
+.icon-right:before {content: "\e904";}
+.icon-left:before {content: "\e905";}
+.icon-headphones:before {content: "\e906";}
+.icon-download:before {content: "\e907";}
+.icon-chat:before {content: "\e908";}
+.icon-books:before {content: "\e909";}
+.icon-calculator:before {content: "\e90a";}
+.icon-wrong:before {content: "\e90b";}
+.icon-conversation:before { content: "\e90c";}
+.icon-correct:before {content: "\e90d";}
+.icon-error:before {content: "\e90e";}
+.icon-interchange:before {content: "\e90f";}
+.icon-conectivity:before {content: "\e910";}
+.icon-video:before {content: "\e911";}
+.icon-desktop:before {content: "\e912";}
+.icon-document:before {content: "\e913";}
+.icon-stethoscope:before { content: "\e914";}
+.icon-student:before {content: "\e915";}
+.icon-smartphone:before {content: "\e916";}
+.icon-pencil:before {content: "\e917";}
+.icon-sitemap:before {content: "\e918";}
+.icon-medal:before {content: "\e919";}
+.icon-microphone:before {content: "\e91a";}
+.icon-wireless:before {content: "\e91b";}
+.icon-fountain:before {content: "\e91d";}
+.icon-feather:before {content: "\e91e";}
+.icon-pen:before {content: "\e91f";}
+.icon-pentwo:before {content: "\e920";}
+.icon-watercolor:before {content: "\e921";}
+.icon-search:before {content: "\e922";}
+.icon-security:before {content: "\e923";}
+.icon-consult:before {content: "\e924";}
+.icon-sound:before {content: "\e925";}
+.icon-files:before {content: "\e926";}
+.icon-upload:before {content: "\e927";}
+.icon-close:before {content: "\e928";}
+.icon-arrow:before {content: "\e929";}
+.icon-mark:before {content: "\e92a";}
+.icon-time:before {content: "\e92b";}
+.icon-phone:before {content: "\e92c";}
+.icon-share:before {content: "\e92d";}
+.icon-seeker:before {content: "\e92e";}
+.icon-fb:before {content: "\e92f";}
+.icon-tw:before {content: "\e930";}
+.icon-yt:before {content: "\e931";}
+.icon-ig:before {content: "\e932";}
+.icon-in:before {content: "\e933";}
+.icon-sc:before {content: "\e934";}
+.icon-chk:before {content: "\e935";}

+ 190 - 0
css/richtext.css

@@ -0,0 +1,190 @@
+.richText {
+	position: relative;
+	border: 1px solid #D6D8D8;
+	background: #D6D8D8;
+	width: 100%;
+	border-radius: 0.25rem;
+}
+
+.richText .richText-toolbar ul {
+	padding: 0 !important;
+	margin: 0 !important;
+	display: flex;
+	flex-wrap: wrap;
+        justify-content: left;/*center*/
+}
+
+.richText .richText-toolbar ul li {
+	list-style: none;
+}
+
+.richText .richText-toolbar ul li a {
+	display: block;
+	padding: 0.25rem 0.5rem;
+	cursor: pointer;
+	-webkit-transition: color 0.4s;
+	-moz-transition: color 0.4s;
+	transition: color 0.4s;
+}
+
+.richText .richText-toolbar ul li a:hover {
+	color: #001d68;
+}
+
+.richText .richText-toolbar ul li a .fa, .richText .richText-toolbar ul li a .fas, .richText .richText-toolbar ul li a .far, .richText .richText-toolbar ul li a svg {
+	pointer-events: none;
+}
+
+.richText .richText-toolbar ul li[data-disable="true"] {
+	opacity: 0.1;
+}
+
+.richText .richText-toolbar ul li[data-disable="true"] a {
+	cursor: default;
+}
+
+.richText .richText-toolbar ul li:not([data-disable="true"]).is-selected .richText-dropdown-outer {
+	display: block;
+}
+
+.richText .richText-toolbar ul:after {
+	display: block;
+	content: "";
+	clear: both;
+}
+
+.richText .richText-toolbar:last-child {
+	font-size: 0.75rem;
+}
+
+.richText .richText-toolbar:after {
+	display: block;
+	clear: both;
+	content: "";
+}
+
+.richText .richText-form select {cursor: pointer;}
+
+.richText .richText-editor {
+	padding: 0.5rem;
+	background-color: #FFFFFF;
+	height: 15rem;
+	outline: none;
+	overflow-y: scroll;
+	overflow-x: auto;
+}
+
+.richText .richText-toolbar ul li a .richText-dropdown-outer {
+	display: none;
+	position: absolute;
+	top: 0;
+	left: 0;
+	right: 0;
+	bottom: 0;
+	background-color: rgba(0, 0, 0, 0.3);
+	cursor: default;
+}
+
+.richText .richText-toolbar ul li a .richText-dropdown-outer .richText-dropdown {
+	position: relative;
+	z-index:10;
+	display: block;
+	margin: 3% auto 0 auto;
+	background-color: #D6D8D8;
+	border: 0.1rem solid #777777;
+	border-radius: 0.25rem;
+	    padding: 0.75rem;
+    padding-top: 1.5rem;
+	max-width: 90%;
+	-webkit-box-shadow: 0 0 5px 0 #777777;
+	-moz-box-shadow: 0 0 5px 0 #777777;
+	box-shadow: 0 0 5px 0 #777777;
+}
+
+.richText .richText-toolbar ul li a .richText-dropdown-outer .richText-dropdown .richText-dropdown-close {
+	position: absolute;
+	top: 0.25rem;
+	right: 0.5rem;
+	color: #CE0E2D;
+	cursor: pointer;
+}
+
+.richText .richText-toolbar ul li a .richText-dropdown-outer .richText-dropdown .richText-dropdown-close:hover {
+	opacity: 0.5
+}
+
+.richText-form-item{
+    position: relative;
+    display: flex;
+    flex-wrap: wrap;
+    align-items: stretch;
+    width: 100%;
+}
+
+.richText .richText-form label {
+   display: flex;
+    align-items: left;/*center*/
+    padding: 0.375rem 0.75rem;
+    margin-bottom: 0;
+    font-size: 1rem;
+    font-weight: 400;
+    line-height: 1.5;
+	font-weight: 600;
+    text-align: left;/*center*/
+    white-space: nowrap;
+}
+
+.richText .richText-form input[type="text"], .richText .richText-form input[type="file"], .richText .richText-form input[type="number"], .richText .richText-form select {
+	display: block;
+    height: calc(1.5em + 0.75rem + 2px);
+    padding: 0.375rem 0.75rem;
+    font-size: 1rem;
+    font-weight: 400;
+    line-height: 1.5;
+    color: #777777;
+    background-color: #FFFFFF;
+    background-clip: padding-box;
+    border: 1px solid #D6D8DB;
+    border-radius: 0.25rem;
+    transition: border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out;
+    position: relative;
+    flex: 1 1 auto;
+    width: 1%;
+    margin-bottom: 0;
+}
+
+.richText .richText-toolbar ul li a .richText-dropdown-outer ul.richText-dropdown {list-style: none;}
+.richText .richText-toolbar ul li a .richText-dropdown-outer ul.richText-dropdown li {display: block;float: none;font-family: Calibri,Verdana,Helvetica,sans-serif;}
+.richText .richText-toolbar ul li a .richText-dropdown-outer ul.richText-dropdown li a {display: block;padding: 10px 15px;border-bottom: #EFEFEF solid 1px;}
+.richText .richText-toolbar ul li a .richText-dropdown-outer ul.richText-dropdown li a:hover {background-color: #FFFFFF;}
+.richText .richText-toolbar ul li a .richText-dropdown-outer ul.richText-dropdown li.inline {margin: 10px 6px;float: left;}
+.richText .richText-toolbar ul li a .richText-dropdown-outer ul.richText-dropdown li.inline a {display: block;padding: 0;margin: 0;border: none;-webkit-border-radius: 50%;-moz-border-radius: 50%;border-radius: 50%;-webkit-box-shadow: 0 0 10px 0 #999;-moz-box-shadow: 0 0 10px 0 #999;box-shadow: 0 0 10px 0 #999;}
+.richText .richText-toolbar ul li a .richText-dropdown-outer ul.richText-dropdown li.inline a span {display: block;height: 30px;width: 30px;-webkit-border-radius: 50%;-moz-border-radius: 50%;border-radius: 50%;}
+
+
+.richText .richText-undo, .richText .richText-redo {
+	float: left;
+	display: block;
+	padding: 0.5rem;
+	cursor: pointer;
+}
+
+.richText .richText-undo.is-disabled, .richText .richText-redo.is-disabled {
+	opacity: 0.4;
+}
+
+.richText .richText-help {
+	display: none;
+}
+
+/*
+.richText .richText-initial {margin-bottom: -4px;padding: 10px;background-color: #282828;border: none;color: #33FF33;font-family: Monospace,Calibri,Verdana,Helvetica,sans-serif;max-width: 100%;min-width: 100%;width: 100%;min-height: 400px;height: 400px;}
+.richText .richText-editor ul, .richText .richText-editor ol {margin: 10px 25px;}
+.richText .richText-editor:focus {border-left: #3498db solid 2px;}
+.richText .richText-editor table {margin: 10px 0;border-spacing: 0;width: 100%;}
+.richText .richText-editor table td, .richText .richText-editor table th {padding: 10px;border: #EFEFEF solid 1px;}
+.richText .richText-help {float: right;display: block;padding: 10px 15px;cursor: pointer;}
+.richText .richText-help-popup a {color: #3498db;text-decoration: underline;}
+.richText .richText-help-popup hr {margin: 10px auto 5px auto;border: none;border-top: #EFEFEF solid 1px;}
+.richText .richText-list.list-rightclick {position: absolute;background-color: #FAFAFA;border-right: #EFEFEF solid 1px;border-bottom: #EFEFEF solid 1px;}
+.richText .richText-list.list-rightclick li {padding: 5px 7px;cursor: pointer;list-style: none;}*/

+ 1056 - 0
css/sgi.css

@@ -0,0 +1,1056 @@
+/* 
+    Created on : 5/12/2018, 01:34:49 PM
+    Author     : Alejandro
+*/
+
+/* General */
+.container-fluid {
+    padding: 0;
+}
+
+#logo {
+    max-height: 64px;
+}
+
+body {
+    font-family: 'indivisa-text', Arial;
+    font-size: 16px !important;
+    color: #001D68;
+    background-color: white;
+}
+
+.bg-head {
+    background-color: white;
+}
+
+.bg-info {
+    background-color: #F0F0F0 !important;
+}
+
+.bloque-clase {
+    box-sizing: border-box;
+    background-color: #dee2e6;
+    padding: 10px;
+    margin-bottom: 10px;
+    min-height: 100%;
+    border-collapse: collapse;
+    border: .2rem solid white !important;
+}
+
+.bloque-clase.conflict {
+    border: .2rem solid var(--danger) !important;
+    background-color: #f6cfd6;
+}
+
+.bloque-clase:hover {
+    background-color: hsl(207, 12%, 85%);
+}
+
+.bloque-clase.conflict:hover {
+    background-color: hsl(0, 100%, 85%);
+}
+
+/* Make cursor move if draggable */
+.bloque-clase[draggable=true] {
+    cursor: move;
+}
+
+.bloque-clase.dragging {
+    opacity: .5;
+    border: .2rem solid var(--primary) !important;
+
+}
+
+.dragging-over {
+    border: .2rem solid var(--primary) !important;
+    background-color: #dee2e6;
+}
+
+.menu-flotante {
+    z-index: 500;
+    position: absolute;
+    bottom: 0;
+    right: 0;
+    background-color: #fff;
+    border-radius: 6px 0 0 0;
+    padding-top: 2px;
+}
+
+/* SOBREESCRIBE BOOTSTRAP */
+.marco {
+    max-width: 960px;
+    width: 100%;
+    margin-left: auto;
+    margin-right: auto;
+}
+
+.marco-wide {
+    max-width: 1366px;
+    width: 100%;
+    margin-left: auto;
+    margin-right: auto;
+}
+
+.content {
+    min-height: 480px;
+    padding: 1em;
+}
+
+.menu {
+    max-width: 960px;
+    width: 100%;
+    margin-left: auto;
+    margin-right: auto;
+    display: flex;
+    padding: 0 15px;
+}
+
+.menu-list .sistema:not(:last-child):after {
+    content: "|";
+    margin-left: 10px;
+    margin-right: 10px;
+}
+
+.sistema-active {
+    color: #d21034 !important;
+    font-weight: bold;
+}
+
+/*.font-small{font-size:14px;}*/
+
+/* Contenidos */
+h1,
+h2,
+h3 {
+    letter-spacing: 1px;
+    position: relative;
+}
+
+h1 {
+    margin-bottom: 40px;
+}
+
+.subtitle:before {
+    content: '';
+    position: absolute;
+    bottom: -8px;
+    left: 0;
+    width: 80px;
+    display: block;
+    background: #d21034;
+    height: 3px;
+}
+
+.main-title {
+    font-size: 3.2rem;
+    margin-bottom: 60px;
+}
+
+/* Otros */
+.alert-heading .ing-caret,
+.card-header .ing-caret,
+.side-menu .ing-caret {
+    transition: .3s transform ease-in-out;
+}
+
+.alert-heading .collapsed .ing-caret,
+.card-header .collapsed .ing-caret {
+    transform: rotate(90deg);
+}
+
+#accordionMenu .collapsed .ing-caret {
+    transform: rotate(-90deg);
+}
+
+.alert-heading .fa,
+.card-header .fa {
+    transition: .3s transform ease-in-out;
+}
+
+.alert-heading .collapsed .fa,
+.card-header .collapsed .fa {
+    transform: rotate(90deg);
+}
+
+#accordionMenu .collapsed .fa {
+    transform: rotate(-90deg);
+}
+
+.border-mid:not(:last-child) {
+    border-bottom: 1px dotted #ccc
+}
+
+.pointer {
+    cursor: pointer;
+}
+
+/* TABLAS */
+.table-white .thead-dark th {
+    text-align: center;
+    border-color: #fff;
+    text-transform: uppercase;
+}
+
+.table-white tr td,
+.table-white tr th {
+    border-left: 1px !important;
+    border-color: #fff;
+    border-style: solid;
+}
+
+.table-white tr td:first-child,
+.table-white tr th:first-child {
+    border-left: 0;
+}
+
+.table-nostriped tbody tr:nth-of-type(odd) {
+    background-color: transparent;
+}
+
+.rotate-text {
+    writing-mode: vertical-lr;
+    transform: rotate(180deg);
+    height: max-content;
+    height: -moz-max-content;
+    height: -webkit-max-content;
+    height: -o-max-content;
+    height: -ms-max-content;
+}
+
+.icono-acciones span,
+.icono-acciones i {
+    margin: 0 3px;
+    text-decoration: none !important;
+}
+
+.icono-acciones a:focus,
+.icono-acciones a:hover,
+.icono-acciones a:active {
+    text-decoration: none !important;
+}
+
+/* FORMAS  */
+.form-box {
+    margin-bottom: 28px;
+}
+
+.form-box>.form-group {
+    margin-bottom: 10px
+}
+
+.form-box>.form-group>label {
+    font-weight: bold;
+    text-align: right;
+    color: #001d68;
+    padding-left: 0
+}
+
+.form-box>.form-group>label.disabled {
+    color: #969696;
+}
+
+.form-box>.form-group>label:before {
+    content: '';
+    position: absolute;
+    top: -8px;
+    right: 0px;
+    width: 2px;
+    height: calc(100% + 16px);
+    display: block;
+    background: #d21034;
+}
+
+.form-box>.form-group>label.disabled:before {
+    background: #969696 !important;
+}
+
+.form-box-info>.form-group>div {
+    background-color: #f7f7f7;
+    padding-bottom: 10px;
+    padding-left: 8px;
+}
+
+.form-box-info>.form-group>div:first-child {
+    margin-left: 7px;
+}
+
+.form-box-info>.form-group:first-child>label {
+    padding-top: 27px;
+}
+
+.form-box-info>.form-group:first-child>div {
+    padding-top: 20px;
+}
+
+.form-box-info>.form-group:last-child>div {
+    padding-bottom: 20px;
+}
+
+.form-box-info>.form-group.row {
+    margin-bottom: 0 !important;
+}
+
+.modal .form-box-info>.form-group>div {
+    margin-left: 0px;
+}
+
+.radio-md {
+    width: 1em;
+    height: 1em;
+}
+
+.radio-lg {
+    width: 1.5em;
+    height: 1.5em;
+}
+
+.radio-xl {
+    width: 2em;
+    height: 2em;
+}
+
+select:disabled {
+    color: #969696 !important;
+}
+
+.input-info {
+    color: #969696;
+}
+
+.barra-right:before {
+    content: '';
+    position: absolute;
+    top: -8px;
+    right: 0px;
+    width: 2px;
+    height: calc(100% + 16px);
+    display: block;
+    background: #d21034;
+}
+
+/* Uso independiente */
+.barra-right.disabled:before {
+    background: #969696 !important;
+}
+
+/* Uso independiente */
+
+textarea {
+    resize: none;
+    overflow-x: hidden;
+    overflow-wrap: break-word;
+    overflow-y: auto;
+}
+
+.clock[readonly] {
+    background-color: #fff !important;
+}
+
+.hasDatepicker[readonly] {
+    background-color: #fff !important;
+}
+
+.badge {
+    padding: 0.5em 1.4em;
+}
+
+.ui-autocomplete {
+    max-height: 160px;
+    overflow-y: auto;
+    overflow-x: hidden;
+    font-size: 90%
+}
+
+/* Data list*/
+.datalist {
+    position: relative;
+    border: 1px solid #969696;
+    border-radius: .25rem;
+}
+
+.datalist-input {
+    padding: 6px 30px 6px 12px !important;
+    background: #FFFFFF !important;
+    border-radius: .25rem;
+    cursor: pointer;
+    border: 0;
+}
+
+.datalist.disabled .datalist-input {
+    background: #f7f7f7 !important;
+    cursor: default;
+    color: #969696;
+}
+
+.datalist.disabled .icono {
+    opacity: 0;
+}
+
+.datalist .icono {
+    position: absolute;
+    font-size: 20px;
+    right: 10px;
+    top: 9px;
+    color: #969696;
+    transition: transform 0.2s ease;
+}
+
+/*.iconoAzul{color: #001D68 !important;} Usar text-primary*/
+.datalist>ul {
+    position: absolute;
+    margin: 0;
+    padding: 0;
+    width: 100%;
+    max-height: 204px;
+    top: 100%;
+    left: 0;
+    list-style: none;
+    border-radius: 2px;
+    background: #FFFFFF;
+    border: 1px solid #001D68;
+    overflow: hidden;
+    overflow-y: auto;
+    z-index: 100;
+}
+
+.datalist>ul li {
+    display: flex;
+    align-items: center;
+    justify-content: start;
+    padding: 0.3em 1em
+        /*0.8em 1em 0.8em 1em*/
+    ;
+    color: #969696;
+}
+
+.datalist>ul li:not(.not-selectable):hover {
+    background: #D21034;
+    color: #FFFFFF;
+    cursor: pointer;
+}
+
+.datalist .selected {
+    background: #D21034;
+    color: #FFFFFF;
+}
+
+.datalist .not-selectable {
+    text-align: center;
+    font-weight: bold;
+    cursor: default;
+    color: #001d68;
+    padding-left: 10px !important;
+}
+
+.datalist-invalid {
+    border-color: #d21034;
+}
+
+.datalist-invalid .icono {
+    color: #d21034 !important;
+}
+
+/* Icono alerta */
+.alerta {
+    color: #ffb700 !important;
+    text-shadow: -1px -1px 0 #3f2f06, 1px -1px 0 #3f2f06, -1px 1px 0 #3f2f06, 1px 1px 0 #3f2f06;
+}
+
+/* Modal */
+.modal-content {
+    border: 4px solid #001d68;
+}
+
+.modal-header {
+    background-color: #001D68 !important;
+    color: #fff !important;
+    border-top-left-radius: 0;
+    border-top-right-radius: 0;
+    padding: 4px 8px 8px 8px;
+}
+
+.modal-title {
+    font-weight: normal;
+    text-align: center;
+    padding: 0 30px;
+}
+
+.modal-header .close {
+    position: absolute;
+    top: 0;
+    right: 0;
+}
+
+.modal-header .close:focus {
+    border: none;
+    outline: none;
+}
+
+/* The side navigation menu */
+#sidebar {
+    width: 400px;
+    position: fixed;
+    top: 0;
+    right: -400px;
+    height: 100vh;
+    z-index: 1023;
+    transition: all 0.3s;
+    overflow-y: auto;
+}
+
+#sidebar.active {
+    right: 0;
+}
+
+.overlay {
+    display: none;
+    position: fixed;
+    width: 100vw;
+    height: 100vh;
+    background: rgba(0, 0, 0, 0.6);
+    z-index: 1022;
+    opacity: 0;
+    transition: all 0.5s ease-in-out;
+}
+
+.overlay.active {
+    display: block;
+    opacity: 1;
+}
+
+#sidebar a:hover {
+    text-decoration: none;
+    color: #d12034;
+}
+
+#sidebar a {
+    transition: color 0.6s ease;
+}
+
+/* ICONOS MENU */
+header {
+    padding: 20px 0;
+    height: 110px;
+}
+
+header .logotipo {
+    float: left;
+    clear: none;
+    text-align: inherit;
+    width: 20%;
+    margin-left: 0;
+    margin-right: 0;
+}
+
+header .logotipo:before {
+    content: '';
+    display: table;
+}
+
+header .logotipo img,
+aside .logotipo img {
+    max-width: 200px;
+}
+
+.mainMenu {
+    min-width: 85px;
+}
+
+.menu .nav-item {
+    border-right: 1px solid #969696;
+}
+
+.menu .nav-item:last-child {
+    border-right: 0;
+}
+
+.menu .nav-item>a,
+.menu .nav-item>span {
+    color: #969696;
+    padding: 1px 10px;
+}
+
+.menu .nav-item>a:hover {
+    color: #D21034;
+    text-decoration: none;
+}
+
+.menu .nav-item>a {
+    transition: color 0.6s ease;
+}
+
+.max-h {
+    height: 45px !important;
+    max-height: 45px;
+}
+
+.max-w {
+    width: 45px !important;
+    max-width: 45px;
+}
+
+.iconSesion {
+    margin-right: -20px;
+}
+
+.iconLogin,
+.iconOff {
+    font-size: 16px;
+    display: block;
+    width: 60px;
+    -webkit-box-sizing: border-box;
+    -moz-box-sizing: border-box;
+    box-sizing: border-box;
+    border-radius: 30px 0 0 30px;
+}
+
+.iconOff:hover,
+.iconOff:active {
+    text-decoration: none;
+    background: #D21034;
+    color: #FFFFFF !important;
+    height: 40px;
+}
+
+.iconOff {
+    color: #D21034 !important;
+}
+
+.iconLogin:hover,
+.iconLogin:active,
+.iconOff:hover,
+.iconOff:active {
+    text-decoration: none;
+    color: #FFFFFF !important;
+    height: 40px;
+}
+
+.iconLogin {
+    color: #339933;
+}
+
+.iconLogin:hover,
+.iconLogin:active {
+    background: #339933;
+}
+
+.iconMenu {
+    font-size: 32px;
+}
+
+.menuicon:hover {
+    color: #101097 !important;
+}
+
+.cerraricon {
+    height: 40px !important;
+    max-height: 40px;
+    width: 40px !important;
+    max-width: 40px;
+    cursor: pointer;
+}
+
+.cerraricon:hover {
+    background: #101097 !important;
+}
+
+.fa-ul {
+    list-style-type: none;
+    margin-left: 2.5em;
+    padding-left: 0;
+}
+
+/* BUTTONS */
+/*.btn .fa-circle{color:rgba(255,255,255,0.15);}
+.btn .icon{font-weight: bold;}
+.btn-round{border-radius:30px; padding-left: 0.5rem!important; padding-right: 0.5rem!important;}*/
+
+.btn-ing {
+    position: relative;
+    padding-right: 35px;
+    padding-left: 20px;
+}
+
+.btn.arrow:after {
+    content: "\e905";
+    font-size: 14px;
+    position: absolute;
+    font-family: "ingfont";
+    right: 14px;
+    margin-top: 3px;
+    font-weight: bold;
+    -webkit-transition: 0.6s all ease;
+    -moz-transition: 0.6s all ease;
+    -o-transition: 0.6s all ease;
+    -ms-transition: 0.6s all ease;
+    transition: 0.6s all ease;
+    vertical-align: middle;
+}
+
+.btn-outline-secondary {
+    border: 0;
+}
+
+/* SOBREESCRIBE BOOTSTRAP */
+.btn-outline-primary.arrow:after {
+    color: #D21034;
+}
+
+.btn-outline-danger.arrow:after {
+    color: #001D68;
+}
+
+.btn-outline-secondary:hover.arrow:after {
+    color: #D21034;
+}
+
+.btn-outline-info:hover.arrow:after {
+    color: #001D68;
+}
+
+/***** SCROLLBAR *****/
+div ::-webkit-scrollbar {
+    width: 8px;
+}
+
+/*Ancho*/
+div ::-webkit-scrollbar-track {
+    background: #f7f7f7;
+}
+
+/*Riel*/
+div ::-webkit-scrollbar-thumb {
+    background: #969696;
+}
+
+/* Handle */
+div ::-webkit-scrollbar-thumb:hover {
+    background: #001d68;
+}
+
+/* Effects */
+/* Vars for primary, secondary, success, info, warning, danger, light, dark */
+
+:root {
+    --primary-color: #001d68;
+    --secondary-color: #001d68;
+    --success-color: #339933;
+    --danger-color: #d21034;
+    --warning-color: #ffc107;
+    --info-color: #969696;
+    --light-color: #f7f7f7;
+    --dark-color: #343a40;
+}
+
+.glow-primary {
+    background: var(--primary-color);
+    color: #fff;
+    border-radius: 5px;
+    padding: 5px;
+    box-shadow: 0 0 5px var(--primary-color);
+    transition: all 0.5s ease;
+}
+
+.glow-secondary {
+    background: var(--secondary-color);
+    color: #fff;
+    border-radius: 5px;
+    padding: 5px;
+    box-shadow: 0 0 5px var(--secondary-color);
+    transition: all 0.5s ease;
+}
+
+.glow-success {
+    background: var(--success-color);
+    color: #fff;
+    border-radius: 5px;
+    padding: 5px;
+    box-shadow: 0 0 5px var(--success-color);
+    transition: all 0.5s ease;
+}
+
+.glow-danger {
+    background: var(--danger-color);
+    color: #fff;
+    border-radius: 5px;
+    padding: 5px;
+    box-shadow: 0 0 5px var(--danger-color);
+    transition: all 0.5s ease;
+}
+
+.glow-warning {
+    background: var(--warning-color);
+    color: #fff;
+    border-radius: 5px;
+    padding: 5px;
+    box-shadow: 0 0 5px var(--warning-color);
+    transition: all 0.5s ease;
+}
+
+.glow-info {
+    background: var(--info-color);
+    color: #fff;
+    border-radius: 5px;
+    padding: 5px;
+    box-shadow: 0 0 5px var(--info-color);
+    transition: all 0.5s ease;
+}
+
+.glow-light {
+    background: var(--light-color);
+    color: #fff;
+    border-radius: 5px;
+    padding: 5px;
+    box-shadow: 0 0 5px var(--light-color);
+    transition: all 0.5s ease;
+}
+
+.glow-dark {
+    background: var(--dark-color);
+    color: #fff;
+    border-radius: 5px;
+    padding: 5px;
+    box-shadow: 0 0 5px var(--dark-color);
+    transition: all 0.5s ease;
+}
+
+/*Hover Handle */
+
+/***** FOOTER *****/
+footer {
+    font-size: 14px;
+    color: #fff;
+}
+
+footer .footerTop {
+    background: #001d68;
+    padding: 15px 0;
+}
+
+footer .footerTop .logotipo {
+    overflow: hidden;
+}
+
+footer .footerTop .logotipo h3 {
+    display: inline-block;
+    vertical-align: top;
+    color: #fff;
+    margin: 0;
+    float: right;
+    text-align: right;
+    font-size: 25px;
+    font-family: 'indivisa-text'
+}
+
+footer .footerTop .logotipo h3 span {
+    display: block;
+}
+
+footer .footerTop .menuFooter h3 {
+    font-size: 12px;
+    font-family: 'indivisa-text';
+    color: #fff !important;
+}
+
+footer .footerTop .menuFooter ul {
+    overflow: hidden;
+}
+
+footer ul {
+    list-style: none;
+    padding: 0;
+    margin: 0;
+}
+
+footer .footerTop .menuFooter ul>li {
+    *zoom: 1;
+    float: left;
+    clear: none;
+    text-align: inherit;
+    width: 16%;
+    margin-left: 0;
+    margin-right: 3%;
+}
+
+footer .footerTop .menuFooter ul>li ul li a {
+    font-size: 10px;
+}
+
+footer ul>li {
+    display: inline-block;
+    vertical-align: top;
+}
+
+.footerMore {
+    position: relative;
+    display: none;
+    padding: 5px 0;
+}
+
+footer a {
+    color: #fff;
+    -webkit-transition: color 0.5s;
+    transition: color 0.5s;
+}
+
+footer a:hover {
+    color: #ce0e2d !important;
+    text-decoration: none !important;
+}
+
+footer .footerTop .menuFooter ul>li ul li {
+    display: block;
+    width: 100%;
+    margin-bottom: 0px;
+}
+
+footer .ubicacion {
+    margin-top: 20px;
+    overflow: hidden;
+}
+
+footer .ubicacion .address {
+    display: inline-block;
+    /*width: 65%;*/
+    vertical-align: bottom;
+}
+
+footer .ubicacion .address h4,
+footer .ubicacion .address h4 a {
+    color: #0fb7f1;
+    font-size: 14px;
+    margin: 0 0 0 -5px;
+    position: relative;
+}
+
+footer .ubicacion .address h4 a {
+    display: inline-block;
+}
+
+footer .ubicacion .redes {
+    display: inline-block;
+    vertical-align: bottom;
+    /*width:32%;text-align:right*/
+}
+
+footer .ubicacion .redes h4 {
+    display: inline-block;
+    vertical-align: middle;
+    margin: 0;
+    font-size: 16px !important;
+    font-weight: bold;
+}
+
+footer .ubicacion .redes ul {
+    display: inline-block;
+    vertical-align: middle
+}
+
+footer .ubicacion .redes ul li {
+    margin-left: 2px
+}
+
+footer .footerMiddle {
+    background: #071e58;
+    overflow: hidden;
+}
+
+footer .footerMiddle nav ul {
+    text-align: center;
+}
+
+footer .footerMiddle nav ul li {
+    border-right: 1px solid #fff;
+    padding: 1px 10px;
+    display: inline-block;
+    margin-bottom: 10px;
+}
+
+footer ul>li {
+    display: inline-block;
+    vertical-align: top;
+}
+
+footer .footerBottom {
+    background: #091941;
+    overflow: hidden;
+    padding: 15px 0;
+}
+
+.footerBottom .logotipos {
+    display: inline-block;
+    vertical-align: middle;
+    width: 20%
+}
+
+footer .footerBottom .logotipos a {
+    display: inline-block;
+    width: 80px;
+    margin-right: 6px
+}
+
+footer .footerBottom .logotipos a.internacional {
+    width: 80px
+}
+
+footer .footerBottom .logotipos a.red {
+    width: 75px
+}
+
+footer .footerBottom .legales {
+    text-align: right;
+    float: right;
+    width: 60%;
+    margin-right: 0;
+    margin-left: auto;
+    padding-top: 10px;
+    padding-bottom: 0
+}
+
+footer .footerBottom .legales ul li {
+    border-right: 1px solid #fff;
+    padding: 1px 10px
+}
+
+footer .footerBottom .legales ul li:last-child {
+    border: 0
+}
+
+footer .tab-pane p {
+    font-size: 12px;
+    line-height: 18px;
+}
+
+@media (max-width: 800px) {
+
+    .menu,
+    .subMenu {
+        position: relative;
+    }
+
+    .iconoMenu {
+        position: absolute;
+        display: block;
+        right: 20px;
+        top: 10px;
+    }
+
+    .menuOculto {
+        display: none;
+    }
+
+    .form-box-info>.form-group>div {
+        margin-left: 0px;
+    }
+
+    /*
+    .responsive{
+        float: none;
+        text-align: center;
+        display: flex;
+        flex-direction: column;
+        flex-wrap: wrap;
+        justify-content: center;
+        align-items: center;
+    }*/
+}

+ 7 - 0
css/style.css

@@ -0,0 +1,7 @@
+.bg-azul {
+    background-color: #00a6CE;
+}
+
+.azul {
+    color: #00a6CE;
+}

+ 142 - 0
css/toggle.css

@@ -0,0 +1,142 @@
+/*\
+|*| ========================================================================
+|*| Bootstrap Toggle: bootstrap4-toggle.css v3.6.1
+|*| https://gitbrent.github.io/bootstrap4-toggle/
+|*| ========================================================================
+|*| Copyright 2018-2019 Brent Ely
+|*| Licensed under MIT
+|*| ========================================================================
+\*/
+
+/*
+* @added 3.0.0: Return support for "*-xs" removed in Bootstrap-4
+* @see: [Comment](https://github.com/twbs/bootstrap/issues/21881#issuecomment-341972830)
+*/
+.btn-group-xs > .btn, .btn-xs {
+	padding: .35rem .4rem .25rem .4rem;
+	font-size: .875rem;
+	line-height: .5;
+	border-radius: 20rem;
+}
+
+.checkbox label .toggle, .checkbox-inline .toggle {
+	margin-left: -1.25rem;
+	margin-right: .35rem;
+}
+
+.toggle {
+	position: relative;
+	overflow: hidden;
+}
+.toggle.btn.btn-light, .toggle.btn.btn-outline-light {
+	/* bootstrap-4 - add a border so toggle is delineated */
+	border-color: rgba(0, 0, 0, .15);
+}
+.toggle input[type="checkbox"] {
+	display: none;
+}
+.toggle-group {
+	position: absolute;
+	width: 200%;
+	top: 0;
+	bottom: 0;
+	left: 0;
+	transition: left 0.35s;
+	-webkit-transition: left 0.35s;
+	-moz-user-select: none;
+	-webkit-user-select: none;
+}
+.toggle-group label, .toggle-group span { cursor: pointer; }
+.toggle.off .toggle-group {
+	left: -100%;
+}
+.toggle-on {
+	position: absolute;
+	top: 0;
+	bottom: 0;
+	left: 0;
+	right: 50%;
+	margin: 0;
+	border: 0;
+	border-radius: 20rem;
+}
+.toggle-off {
+	position: absolute;
+	top: 0;
+	bottom: 0;
+	left: 50%;
+	right: 0;
+	margin: 0;
+	border: 0;
+	border-radius: 20rem;
+	box-shadow: none; /* Bootstrap 4.0 Support via (Issue #186)[https://github.com/minhur/bootstrap-toggle/issues/186]) */
+}
+.toggle-handle {
+	position: relative;
+	margin: 0 auto;
+	padding-top: 0px;
+	padding-bottom: 0px;
+	height: 100%;
+	width: 0px;
+	border-width: 0 1px;
+	background-color: #FFFFFF;
+}
+
+.toggle.btn-outline-primary .toggle-handle {
+	background-color: var(--primary);
+	border-color: var(--primary);
+}
+.toggle.btn-outline-secondary .toggle-handle {
+	background-color: var(--secondary);
+	border-color: var(--secondary);
+}
+.toggle.btn-outline-success .toggle-handle {
+	background-color: var(--success);
+	border-color: var(--success);
+}
+.toggle.btn-outline-danger .toggle-handle {
+	background-color: var(--danger);
+	border-color: var(--danger);
+}
+.toggle.btn-outline-warning .toggle-handle {
+	background-color: var(--warning);
+	border-color: var(--warning);
+}
+.toggle.btn-outline-info .toggle-handle {
+	background-color: var(--info);
+	border-color: var(--info);
+}
+.toggle.btn-outline-light .toggle-handle {
+	background-color: var(--light);
+	border-color: var(--light);
+}
+.toggle.btn-outline-dark .toggle-handle {
+	background-color: var(--dark);
+	border-color: var(--dark);
+}
+.toggle[class*="btn-outline"]:hover .toggle-handle {
+	background-color: var(--light);
+	opacity: 0.5;
+}
+
+/* NOTE: Must come first, so classes below override as needed */
+/* [default] (bootstrap-4.1.3 - .btn - h:38px) */
+.toggle.btn { min-width: 3.7rem; min-height: 2.15rem; }
+.toggle-on.btn { padding-right: 1.5rem; }
+.toggle-off.btn { padding-left: 1.5rem; }
+
+/* `lg` (bootstrap-4.1.3 - .btn - h:48px) */
+.toggle.btn-lg { min-width: 5rem; min-height: 2.815rem; }
+.toggle-on.btn-lg { padding-right: 2rem; }
+.toggle-off.btn-lg { padding-left: 2rem; }
+.toggle-handle.btn-lg { width: 2.5rem; }
+
+/* `sm` (bootstrap-4.1.3 - .btn - h:31px) */
+.toggle.btn-sm { min-width: 3.125rem; min-height: 1.938rem; }
+.toggle-on.btn-sm { padding-right: 1rem; }
+.toggle-off.btn-sm { padding-left: 1rem; }
+
+/* `xs` (bootstrap-3.3 - .btn - h:22px) */
+.toggle.btn-xs { min-width: 2.19rem; min-height: 1.375rem; }
+.toggle-on.btn-xs { padding-right: .8rem; }
+.toggle-off.btn-xs { padding-left: .8rem; }

+ 343 - 0
días_festivos.php

@@ -0,0 +1,343 @@
+<?php
+require_once 'class/c_login.php';
+require_once 'include/bd_pdo.php';
+
+if(!isset($_SESSION['user'])){
+    header('Location: index.php');
+    exit;
+}
+else
+    $user = unserialize($_SESSION['user']);
+$user->access();
+if(!$user->admin && $user->acceso == 'n'){
+    header('Location: main.php?error=1');
+}else{
+    $user->print_to_log('Dias_festivos');
+}
+if(isset($_GET['facultad'])){
+    $fac = $_GET['facultad'];
+}else if($user->admin){
+    $fac = null;
+}else{
+    $fac = $user->facultad['facultad_id'];
+}
+?>
+<!DOCTYPE html>
+<html lang="en">
+
+<head>
+    <meta charset="UTF-8">
+    <meta name="viewport" content="width=device-width, initial-scale=1.0">
+    <title>Días Festivos</title>
+    <link rel="stylesheet" href="css/jquery-ui.css">
+    <link rel="stylesheet" href="css/calendar.css">
+    <link rel="stylesheet" href="css/toggle.css" type="text/css">
+    <?php
+    include 'import/html_css_files.php';
+    ?>
+</head>
+
+<body>
+    <?php
+    include "import/html_header.php";
+    html_header(
+        "DÍAS FESTIVOS",
+        "Gestión de Checador "
+    );
+    $user->access();
+    $fs_dias_festivos = query("SELECT * FROM fs_diasfestivos(:facultad, null, null, null) ORDER BY diasfestivos_dia", [':facultad' => $fac], false);
+    $fs_periodos = query("SELECT * FROM fs_periodos(null, null) WHERE estado = 'Activo'", null, false);
+    $fs_dias_festivos_generales = query("SELECT * FROM fs_diasfestivos(null, null) ORDER BY diasfestivos_dia", null, false);
+    ?>
+    <main class="content marco">
+        <?php if($user->admin){ ?>
+        <div class="col-12 text-right">
+            <button type="button" class="btn btn-outline-secondary" data-toggle="modal" data-target="#modal" data-tipo="1"><span class="ing-mas ing-fw"></span> Agregar Día Festivo</button>
+        </div>
+        <?php } ?>
+        <div id="message"></div>
+        <!-- Tabla -->
+        <?php if($fs_dias_festivos || $fs_dias_festivos_generales){ ?>
+            <div class="row mt-3">
+                <div class="col-12 table-responsive">
+                    <table class="table table-sm table-striped table-white">
+                        <thead class="thead-dark">
+                            <tr>
+                                <th>Día</th>
+                                <th>Periodo</th>
+                                <th>Nivel</th>
+                                <?php if($user->admin){ ?>
+                                <th>Facultad</th>
+                                <th>Acciones</th>
+                                <?php } ?>
+                            </tr>
+                        </thead>
+                        <tbody>
+                            <?php foreach($fs_dias_festivos as $dia){ ?>
+                            <tr data-id="<?= $dia['diasfestivos_id'] ?>" id="<?= $dia['diasfestivos_id'] ?>" data-periodo="1">
+                                <td class="text-center"><?php $day =  explode("-", $dia['diasfestivos_dia']);
+                                                            echo $day['2']."-".$day['1']."-".$day['0'];
+                                                        ?></td>
+                                <td class="text-center"><?= $dia['periodo_nombre'] ?></td>
+                                <td class="text-center"><?= $dia['nivel_nombre'] ?></td>
+                                <?php if($user->admin){ ?>
+                                <td class="text-center"><?= $dia['facultad_nombre'] ?></td>
+                                <td class="text-center icono-acciones">
+                                    <a href="#" data-toggle="modal" data-target="#modal" data-tipo="2" title="Editar"><span class="ing-editar ing-fw"></span></a>
+                                    <a href="#" data-toggle="modal" data-target="#modal_confirm" title="Borrar"><span class="ing-basura ing-fw"></span></a>
+                                </td>
+                                <?php } ?>
+                            </tr>
+                            <?php } ?>
+                            <?php foreach($fs_dias_festivos_generales as $dia){ ?>
+                            <tr data-id="<?= $dia['diasfestivos_id'] ?>" id="<?= $dia['diasfestivos_id'] ?>" data-periodo="0">
+                                <td class="text-center"><?php $day =  explode("-", $dia['diasfestivos_dia']);
+                                                            echo $day['2']."-".$day['1']."-".$day['0'];
+                                                        ?></td>
+                                <td class="text-center">Todos</td>
+                                <td class="text-center">Todos</td>    
+                                <?php if($user->admin){ ?>
+                                <td class="text-center">Todas</td>
+                                <td class="text-center icono-acciones">
+                                    <a href="#" data-toggle="modal" data-target="#modal" data-tipo="2" title="Editar"><span class="ing-editar ing-fw"></span></a>
+                                    <a href="#" data-toggle="modal" data-target="#modal_confirm" title="Borrar"><span class="ing-basura ing-fw"></span></a>
+                                </td>
+                                <?php } ?>
+                            </tr>    
+                            <?php } ?>
+                        </tbody>
+                    </table>
+                </div>
+            </div>
+        <?php } ?>
+    </main>
+    <!-- Footer -->
+    <?php
+    include "import/html_footer.php";
+    ?>
+    <!-- Modal -->
+    <div class="modal fade" id="modal" tabindex="-1" role="dialog" aria-labelledby="modal" aria-hidden="true">
+        <div class="modal-dialog modal-dialog-centered" role="document">
+            <div class="modal-content">
+                <div class="modal-header">
+                    <h4 class="col-12 modal-title text-center">
+                        <span id="modalLabel">
+                            Editar Día Festivo
+                        </span>
+                        <button type="button" class="close text-white" data-dismiss="modal" aria-label="Close">
+                            <span aria-hidden="true">&times;</span>
+                        </button>
+                    </h4>
+                </div>
+                <div class="modal-body">
+                    <form action="" method="post" id="formaModal" onsubmit="return valida_campos()">
+                        <input type="hidden" name="id" id="id">
+                        <div class="form-box">
+                            <div class="form-group row">
+                                <label for="periodo" class="col-3 col-form-label">Periodo *</label>
+                                <div class="col-8">
+                                    <div class="datalist datalist-select mb-1 w-100">
+                                        <div class="datalist-input" id="fac">Mostrar todos</div>
+                                        <span class="ing-buscar icono"></span>
+                                        <ul style="display:none">
+                                            <li data-id="0" class="pl4-">Todos</li>
+                                            <?php foreach($fs_periodos as $periodo){ ?>
+                                                <li data-id="<?= $periodo['id'] ?>" class="pl4-"> <?php echo $periodo['periodo']." ".$periodo['facultad']." - ".$periodo['nivel']; ?></li>
+                                            <?php } ?>
+                                            </ul>
+                                        <input type="hidden" id="periodo" name="periodo" value="">
+                                    </div>
+                                </div>
+                            </div>
+                            <div class="form-group row" id="rangoDiv">
+                                <label for="rango" class="col-3 col-form-label">Rango</label>
+                                <div class="col-8">
+                                    <input type="checkbox" data-toggle="toggle" data-onstyle="success" data-offstyle="danger" data-on="SI" data-off="NO" data-size="s" id="rango" name="rango">
+                                </div>
+                            </div>
+                            <div class="form-group row" id="diasFestivos">
+                                <label for="diaFestivo" class="col-3 col-form-label">Día festivo *</label>
+                                <div class="col-4" id="dia">
+                                    <input id="diaFestivo" name="diaFestivo" type="text" class="form-control date-picker" placeholder="dd/mm/aaaa" maxlength="10" required="required" readonly="">
+                                </div>
+                                <div class="col-1 diaFestivoRango">
+                                    -
+                                </div>
+                                <div class="col-4 diaFestivoRango" id="diaFestivoRango">
+                                    <input id="diaFestivoFin" name="diaFestivoFin" type="text" class="form-control date-picker" placeholder="dd/mm/aaaa" maxlength="10" required="required" readonly="">
+                                </div>
+                                <div class="invalid-feedback">Debe seleccionar una fecha</div>
+                            </div>
+                            <div class="form-group row">
+                                <div class="offset-4 col-8">
+                                    <button type="submit" class="btn btn-outline-primary" id="submitBtn" data-tipo="1">
+                                        <span class="ing-aceptar ing-fw"></span> Guardar
+                                    </button>
+                                    <button type="reset" class="btn btn-outline-danger" data-dismiss="modal">
+                                        <span class="ing-cancelar ing-fw"></span> Cancelar
+                                    </button>
+                                </div>
+                            </div>
+                        </div>
+                    </form>
+                </div>
+            </div>
+        </div>
+    </div>
+
+    <div class="modal fade" id="modal_confirm" tabindex="-1" role="dialog" aria-labelledby="modal" aria-hidden="true">
+        <div class="modal-dialog modal-dialog-centered" role="document">
+            <div class="modal-content">
+                <div class="modal-body">
+                    <div class="row">
+                        <div class="col">
+                            <p class="font-weight-bold">¿Estás seguro de querer borrar el día festivo?</p>
+                        </div>
+                    </div>
+                </div>
+                <div class="modal-footer">
+                    <input type="hidden" id="id_borrar" value="">
+                    <button type="button" class="btn btn-outline-primary btn-borrar"><span class="ing-aceptar ing-fw"></span> Borrar</button>
+                    <button type="button" class="btn btn-outline-danger" data-dismiss="modal" aria-label="Close"><span class="ing-cancelar ing-fw"></span> Cancelar</button>
+                </div>
+            </div>
+        </div>
+    </div>
+
+    <script src="js/jquery.min.js"></script>
+    <script src="js/jquery-ui.js"></script>
+    <script src="js/bootstrap/bootstrap.min.js"></script>
+    <script src="js/datalist.js"></script>
+    <script src="js/datepicker-es.js"></script>
+    <script src="js/toggle.js"></script>
+    <?php
+        require_once 'js/messages.php';
+    ?>
+    <script>
+        $(document).ready(function(){
+            $('.diaFestivoRango').hide();
+        });
+        $('#rango').change(function(){
+            if($(this).is(':checked')){
+                $('#diaFestivoFin').val('');
+                $('.diaFestivoRango').show();
+                $('#diaFestivoFin').datepicker("setDate", today);
+            }
+            else{
+                $('.diaFestivoRango').hide();
+            }
+        });
+
+        $('#modal_confirm').on('show.bs.modal', function(event){
+            var button = $(event.relatedTarget);
+            var id = button.parents("tr").data("id");
+            $("#id_borrar").val(id);
+        });
+
+        $(".btn-borrar").click(function(){
+            var id = $("#id_borrar").val();
+            $.ajax({
+                url: 'action/action_diasfestivos_borra.php',
+                type: 'post',
+                dataType: 'json',
+                data:{id: id},
+                success:function(result){
+                    location.reload();
+                },
+                error: function(){console.log("Error")}
+            });
+        });
+
+        var today = new Date();
+        $(".date-picker").datepicker($.datepicker.regional["es"]);
+        $(".date-picker").datepicker({
+            dateFormat: "dd/mm/yyyy",
+            changeMonth: true,
+        });
+
+    <?php if(!$fs_dias_festivos && !$fs_dias_festivos_generales){ ?>
+        triggerMessage("No se encontraron días festivos", "Error");
+    <?php } ?>
+
+        $('#modal').on('show.bs.modal', function (event){
+            var button = $(event.relatedTarget);
+            var tipo = button.data('tipo');
+            var modal = $(this);
+            if(tipo==1){//agregar
+                $('#modalLabel').html("Agregar Día Festivo");
+                $('#submitBtn').data('tipo', 1);
+                $('#diaFestivo').datepicker("setDate", today);
+                $('#diaFestivoFin').datepicker("setDate", today);
+                setDatalistFirst("#periodo");
+                $('#rangoDiv').show();
+                $('#dia').removeClass('col-9');
+                $('#dia').addClass('col-4');
+            }
+            else{
+                $('#modalLabel').html("Editar Día Festivo");
+                $('#submitBtn').data('tipo', 2);
+                $('#rangoDiv').hide();
+                $('#dia').removeClass('col-4');
+                $('#dia').addClass('col-9');
+                var id = $(event.relatedTarget).parents('tr').data('id');
+                var periodo = $(event.relatedTarget).parents('tr').data('periodo');
+                $.ajax({
+                    url:"action/action_diasfestivos_select.php",
+                    type:"post",
+                    dataType:"json",
+                    data:{id: id, periodo: periodo},
+                    success:function(result){
+                        console.log(result);
+                        $('#id').val(result['diasfestivos_id']);
+                        if(!result['periodo_id']){
+                            setDatalist('#periodo', 0);
+                        }else{
+                            setDatalist('#periodo', result['periodo_id']);
+                        }
+                        var date = new Date(result['diasfestivos_dia']);
+                        date.setDate(date.getDate() + 1);
+                        $('#diaFestivo').datepicker("setDate", date);
+                    },
+                    error: function(){console.log("Error")}
+                });
+            }
+        });
+
+        function valida_campos(){
+            var error=false;
+            if($("#diaFestivo").val()==""){
+                $("#diaFestivo").addClass("is-invalid");
+                error=true;
+            }
+            var inicio = $("#diaFestivo").val();
+            var fin = $("#diaFestivoFin").val();
+            var aux = inicio.split("/");
+            inicio = aux[2] +"-"+ aux[1] +"-"+ aux[0];
+            aux = fin.split("/");
+            fin = aux[2] +"-"+ aux[1] +"-"+ aux[0];
+            console.log(inicio);
+            if((fin < inicio || fin == inicio)  && $("#rango").is(':checked')){
+                $('#diaFestivoFin').addClass("is-invalid");
+                error=true;
+            }
+            if(error){
+                return false;
+            }else{
+                var btn = $("#submitBtn");
+                if(btn.data("tipo") == 2){//update
+                    $("#formaModal").prop("action", "./action/action_diasfestivos_update.php");
+                }
+                else{//insert
+                    $("#formaModal").prop("action", "./action/action_diasfestivos_insert.php");
+                }
+            }
+        }
+        
+        <?php if(isset($_GET['error'])){ 
+            if($_GET['error'] == 1){ ?>
+                triggerMessage("Este dia festivo ya existe", "Error");
+        <?php }
+        } ?>
+    </script>
+</body>
+</html>

+ 111 - 0
editar_horario.php

@@ -0,0 +1,111 @@
+<?php
+require_once 'class/c_login.php';
+
+if (!isset($_SESSION['user'])) {
+    header('Location: index.php');
+    exit;
+} else
+    $user = unserialize($_SESSION['user']);
+
+if (!$user->admin)
+    header('Location: main.php?error=1');
+?>
+<!DOCTYPE html>
+<html lang="en">
+
+<head>
+    <meta charset="UTF-8">
+    <meta http-equiv="X-UA-Compatible" content="IE=edge">
+    <meta name="viewport" content="width=device-width, initial-scale=1.0">
+    <title>Editar Horarios | <?php echo $_SESSION['facultad'] ?? "Administrador"; ?></title>
+    <link rel="icon" type="image/png" href="imagenes/favicon.png" />
+    <link rel="stylesheet" href="css/bootstrap-ulsa.min.css" type="text/css">
+    <link rel="stylesheet" href="css/indivisa.css" type="text/css">
+    <link rel="stylesheet" href="css/sgi.css?rand=<?php echo rand(); ?>" type="text/css">
+</head>
+
+<body>
+    <?php
+    include "import/html_header.php";
+    html_header("Editar Horarios", "Gestión de Checador");
+    ?>
+
+    <!-- Create a schedule design -->
+    <main class="content marco">
+        <div class="container-fluid">
+            <div class="row">
+                <div class="col-sm-12">
+                    <!-- Nivel select option -->
+                    <div class="form-group">
+                        <label for="nivel">Nivel</label>
+                        <select class="form-control" id="nivel">
+                            <option value="0">Selecciona un nivel</option>
+                            <option value="1">Nivel 1</option>
+                            <option value="2">Nivel 2</option>
+                            <option value="3">Nivel 3</option>
+                            <option value="4">Nivel 4</option>
+                            <option value="5">Nivel 5</option>
+                            <option value="6">Nivel 6</option>
+                            <option value="7">Nivel 7</option>
+                            <option value="8">Nivel 8</option>
+                            <option value="9">Nivel 9</option>
+                            <option value="10">Nivel 10</option>
+                        </select>
+
+                    </div>
+
+                </div>
+            </div>
+
+            <!-- Table Schedule -->
+            <div class="row">
+                <div class="col-sm-12">
+                    <table class="table table-bordered table-hover">
+                        <thead>
+                            <tr>
+                                <th scope="col">Hora</th>
+                                <th scope="col">Lunes</th>
+                                <th scope="col">Martes</th>
+                                <th scope="col">Miércoles</th>
+                                <th scope="col">Jueves</th>
+                                <th scope="col">Viernes</th>
+                                <th scope="col">Sábado</th>
+                                <th scope="col">Domingo</th>
+                            </tr>
+                        </thead>
+                        <tbody>
+                            <tr>
+                                <th scope="row">7:00 - 8:00</th>
+                                <!-- Matemáticas Martes id horario: 3 edit -->
+                                <td>
+                                </td>
+                        </tbody>
+                    </table>
+                </div>
+            </div>
+        </div>
+    </main>
+    <?php
+    include "import/html_footer.php";
+    ?>
+</body>
+
+</html>
+<?php
+
+function get_horarios($facultad_id): array
+{
+    $dias = array("lun", "mar", "mié", "jue", "vie", "sáb",);
+    foreach ($dias as $dia) {
+        $horarios = query(
+            "SELECT * FROM HORARIO_VIEW
+            WHERE FACULTAD_ID = :facultad_id AND DIA = :dia
+            ORDER BY HORA",
+            array(":facultad_id" => $facultad_id, ":dia" => $dia)
+        );
+        foreach ($horarios as $h)
+            $horario[$dia][] = $h;
+    }
+    return $horario;
+}
+?>

+ 209 - 0
excel_horario.php

@@ -0,0 +1,209 @@
+<?php
+require_once 'class/c_login.php';
+if (!isset($_SESSION['user'])) {
+    header('Location: index.php');
+    exit;
+} else
+    $user = unserialize($_SESSION['user']);
+
+
+$user->access('excel_horario');
+
+if (!$user->admin && in_array($user->acceso, ['r', 'n'])) {
+    // die($access);
+    header('Location: main.php?error=1');
+} else {
+    $user->print_to_log('Consultar asistencia');
+}
+?>
+<!DOCTYPE html>
+<html lang="en">
+
+<head>
+    <title>Cargar horario desde Excel | <?= $user->facultad['facultad'] ?? 'General' ?></title>
+    <meta charset="utf-8">
+    <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
+    <?php include_once "import/html_css_files.php"; ?>
+</head>
+
+<body style="display: block;">
+    <?php
+    include('include/constantes.php');
+    include("import/html_header.php");
+    html_header("Cargar horario desde Excel", "Gestión de Checador");
+    ?>
+    <main class="container content content-margin" id="local-app">
+        <section id="message"></section>
+        <?php require('import/periodo.php') ?>
+        <form>
+            <div class="form-group">
+                <div class="form-box">
+                    <?php
+                    $carreras = query("SELECT * FROM FS_CARRERA WHERE FACULTAD = COALESCE(:fac, FACULTAD) AND PERIODO = COALESCE(:per, PERIODO) ORDER BY CARRERA", [":fac" => $user->facultad['facultad_id'], ":per" => $user->periodo], single: false);
+                    #die(print_r($carreras, true));
+                    ?>
+                    <div class="form-group row">
+                        <label for="filter_carrera" class="col-4 col-form-label">Carrera</label>
+                        <div class="col-6 ">
+                            <div id="dlcarrera" class="datalist datalist-select mb-1 w-100">
+                                <div class="datalist-input">Seleccionar carrera</div>
+                                <span class="ing-buscar icono"></span>
+                                <ul style="display:none">
+                                    <?php
+                                    foreach ($carreras as $carrera) {
+                                    ?>
+                                        <li data-id="<?= $carrera['id'] ?>">
+                                            <?= $carrera['carrera'] ?>
+                                        </li>
+                                    <?php
+                                    }
+                                    ?>
+                                </ul>
+                                <input type="hidden" id="filter_carrera" name="carrera" value="">
+                            </div>
+                        </div>
+                    </div>
+
+                    <div class="form-group row">
+                        <label for="excel" class="col-4 col-form-label">Archivo de horarios</label>
+                        <div class="col-8 col-sm-6">
+                            <input class="form-control-file" id="excel" name="archivo" accept=".xlsx, .xls" require>
+                        </div>
+                    </div>
+
+                    <div class="form-group mt-5 row justify-content-center">
+
+                        <button id="btn-cargar" type="button" class="btn btn-primary" onclick="submit_files()">
+                            <span class="ing-guardar"></span>
+                            Cargar horario
+                        </button>
+                    </div>
+                </div>
+            </div>
+
+        </form>
+    </main>
+</body>
+<?php
+require_once("import/html_footer.php");
+require_once("js/messages.php")
+?>
+<script src="js/scrollables.js"></script>
+<script src="js/jquery.min.js"></script>
+<script src="js/bootstrap/bootstrap.min.js"></script>
+
+<script src="js/custominputfile.min-es.js"></script>
+<link rel="stylesheet" href="css/custominputfile.min.css">
+<script src="js/fetchlib.js"></script>
+<script lang="jquery">
+    var datum = []
+
+    $(document).ready(function() {
+
+        $('#excel').customFile({
+            allowed: ['xlsx', 'xls'],
+            maxFiles: 1,
+            callbacks: {
+                onSuccess: async function(item) {
+                    var formData = $.customFile.serialize('archivo');
+
+                    const {
+                        status,
+                        message,
+                        data
+                    } = await fetch('action/action_revisar_excel.php', {
+                            method: 'POST',
+                            body: formData
+                        })
+                        .then(response => response.json())
+                        .catch(error => {
+                            return {
+                                status: 'error',
+                                message: 'Error al cargar el archivo',
+                            }
+                        });
+
+                    if (status == 'error') {
+                        triggerMessage(message, 'Error en el formato del archivo');
+                        item.destroy();
+                        return
+                    }
+
+                    triggerMessage(message, `Archivo revisado`, 'success');
+                    datum = data;
+                },
+            }
+        });
+    })
+
+    async function submit_files() {
+        // disable button
+        const button = document.querySelector('#btn-cargar');
+        // add class disabled to button
+        button.classList.add('disabled');
+        // disable button
+        button.disabled = true;
+
+        // add loading icon
+        button.innerHTML = '<span class="ing-cargando"></span> Cargando...';
+
+        let missing = [];
+
+        let carrera = $('#filter_carrera').val();
+        if (carrera == '') missing.push('Carrera');
+
+        if (datum.length == 0) missing.push('Archivo de horarios');
+
+        let facultad = <?= $user->facultad['facultad_id'] ?>;
+
+        if (missing.length > 0) {
+            messageMissingInputs(missing);
+
+            // remove class disabled to button
+            button.classList.remove('disabled');
+            // enable button
+            button.disabled = false;
+
+            // remove loading icon
+            button.innerHTML = '<span class="ing-guardar"></span> Cargar horario';
+
+            return;
+        }
+
+        formData.append('carrera', carrera);
+        formData.append('facultad', facultad);
+        formData.append('data', JSON.stringify(datum));
+
+
+        const {
+            status,
+            message
+        } = await fetch('action/action_horario_excel.php', {
+                method: 'POST',
+                body: formData
+            })
+            .then(response => response.json())
+            .catch(error => {
+                return {
+                    status: 'error',
+                    message: 'Error al cargar el archivo',
+                }
+            });
+
+        if (status == 'error') {
+            triggerMessage(message, 'Error al guardar el archivo');
+            return
+        }
+
+        triggerMessage(message, `Horarios guardados`, 'success');
+
+        // await 1 second
+        await new Promise(resolve => setTimeout(resolve, 1000));
+
+        // reload page
+        location.reload();
+    }
+</script>
+
+
+</html>

+ 283 - 0
facultades.php

@@ -0,0 +1,283 @@
+<?php
+require_once 'class/c_login.php';
+require_once 'include/bd_pdo.php';
+
+if (!isset($_SESSION['user'])){
+    header('Location: index.php');
+    exit;
+}
+else
+    $user = unserialize($_SESSION['user']);
+$user->access();
+if(!$user->admin && $user->acceso == 'n'){
+    header('Location: main.php?error=1');
+}else{
+    $user->print_to_log('Facultades');
+}
+if($user->admin!=true){
+    header('Location: carreras.php?facultad='.$user->facultad['facultad_id']);
+}
+?>
+<!DOCTYPE html>
+<html lang="en">
+
+<head>
+    <meta charset="UTF-8">
+    <meta name="viewport" content="width=device-width, initial-scale=1.0">
+    <title>Facultades</title>
+    <?php
+    include 'import/html_css_files.php';
+    ?>
+</head>
+
+<body>
+    <?php
+    include "import/html_header.php";
+    html_header(
+        "FACULTADES",
+        "Gestión de Checador "
+    );
+    $user->access();
+    if(isset($_POST["estado"])){
+        echo "estado=".$_POST["estado"];
+    }
+
+    if(isset($_POST["desc"])){
+        $desc=$_POST["desc"];
+        $filter_desc = trim(filter_input(INPUT_POST, "desc", FILTER_SANITIZE_STRING, array('flags' => FILTER_FLAG_STRIP_LOW)));
+    }else{
+        $desc=null;
+    }
+    if($user->admin==true){
+    $fs_facultades = query(
+        "SELECT * FROM fs_facultades(:nombre)",
+        array(":nombre" => $desc),
+        single:false
+    );
+    }else{
+        $fs_facultades = query(
+            "SELECT * FROM fs_facultades(:nombre) where facultad_id = :facultad",
+            array(":nombre" => $desc, ":facultad" => $user->facultad["facultad_id"]),
+            single:false
+        );
+    }
+    ?>
+    <main class="content marco">
+        <?php if($user->admin==true)  {?>
+        <div class="row">
+            <div class="col-12 text-right">
+                <button type="button" class="btn btn-outline-secondary" data-toggle="modal" data-target="#modal" data-tipo="1"><span class="ing-mas ing-fw"></span>Crear facultad</button>
+            </div>
+        </div>
+        <?php }?>
+        <!-- Filtro -->
+        <div class="row">
+            <div class="col-12">
+                <form action="facultades.php" method="post">
+                    <div class="form-box">
+                        <div class="form-group row">
+                            <label for="filter_desc" class="col-4 col-form-label">Facultad</label>
+                            <div class="col-8 col-sm-4">
+                                <input id="filter_desc" name="desc" type="text" class="form-control"<?php if(isset($filter_desc)){ echo'value="'.$filter_desc.'"';}?>>
+                            </div>
+                        </div>
+                    </div>
+                    <div class="form-group row">
+                        <div class="col-12 text-center">
+                            <button type="submit" class="btn btn-outline-primary">
+                                <span class="ing-buscar ing-fw"></span>
+                                Filtrar
+                            </button>
+                            <button type="submit" class="btn btn-outline-danger btn-reset">
+                                <span class="ing-borrar ing-fw"></span>
+                                Limpiar
+                            </button>
+                        </div>
+                    </div>
+                </form>
+            </div>
+        </div>
+
+        <!-- Tabla -->
+        <div class="row">
+            <div class="col-12 table-responsive">
+                <table class="table table-sm table-striped table-white">
+                    <thead class="thead-dark">
+                        <tr>
+                            <th>Estado</th>
+                            <th>Facultad</th>
+                            <?php if($user->acceso == 'w')  {?>
+                            <th>Acciones</th>
+                            <?php }?>
+                        </tr>
+                    </thead>
+                    <tbody>
+                        <?php
+                        foreach($fs_facultades as $facultad){
+                        ?>
+                        <tr data-id="<?php echo $facultad["facultad_id"];?>" id="<?php echo $facultad["facultad_id"];?>">
+                            <?php
+                                $color = "danger";
+                                $title = "Inactiva";
+                                if($facultad["facultad_activa"]==1){
+                                $color ="success";
+                                $title="Activa";
+                                }
+                            ?>
+                            <td class="text-<?php echo $color;?> text-center" title="<?php echo $title;?>">
+                                <span class="ing-bullet"></span>
+                            </td>
+                            <td class="text-primary"><?php echo $facultad["facultad_nombre"];?></td>
+                            <?php if($user->acceso == 'w')  {?>
+                            <td class="text-center icono-acciones">
+                                <a href="#" data-toggle="modal" data-target="#modal" data-tipo="2" title="Editar"><span class="ing-editar ing-fw"></span></a>
+                                <a href="carreras.php?facultad=<?php echo $facultad["facultad_id"];?>" title="Agregar carreras o periodos"><span class="ing-mas inf-fw"></span></a>
+                            </td>
+                            <?php }?>
+                        </tr>
+                        <?php }?>
+                    </tbody>
+                </table>
+                <div id="message"></div>
+            </div>
+        </div>
+    </main>
+    <!-- Footer -->
+    <?php
+    include "import/html_footer.php";
+    ?>
+    <!-- Modal -->
+    <div class="modal fade" id="modal" tabindex="-1" role="dialog" arialabelledby="modal" aria-hidden="true">
+        <div class="modal-dialog modal-dialog-centered" role="document">
+            <div class="modal-content">
+                <div class="modal-header">
+                    <h4 class="col-12 modal-title text-center">
+                        <span id="modalLabel">
+                            Editar nombre de Facultad
+                        </span>
+                        <button type="button" class="close text-white" data-dismiss="modal" aria-label="Close">
+                            <span aria-hidden="true">&times;</span>
+                        </button>
+                    </h4>
+                </div>
+                <div class="modal-body">
+                    <form action="" method="post" id="formaModal" onsubmit="return valida_campos()">
+                        <input type="hidden" name="id" id="id">
+                        <div class="form-box">
+                            <div class="form-group row">
+                                <label for="nombre" class="col-4 col-form-label">Nombre *</label>
+                                <div class="col-8">
+                                    <input id="nombre" name="nombre" type="text" class="form-control" maxlength="100">
+                                </div>
+                            </div>
+                            <div class="form-group row">
+                                <label for="estado" class="col-4 col-form-label">Estado *</label>
+                                <div class="col-4">
+                                    <div class="form-check form-check-inline">
+                                        <input class="form-check-input radio-lg" type="radio" id="estado_activo" name="estado" value="1" checked="checked">
+                                        <label for="estado_activo" class="col-form-label">Activo</label>
+                                    </div>
+                                </div>
+                                <div class="col-4">
+                                    <div class="form-check form-check-inline">
+                                        <input class="form-check-input radio-lg" type="radio" id="estado_inactivo" name="estado" value="0">
+                                        <label for="estado_inactivo" class="col-form-label">Inactivo</label>
+                                    </div>
+                                </div>
+                            </div>
+                            <div class ="form-group row mt-3">
+                                <div class="offset-4 col-8">
+                                    <button type="submit" class="btn btn-outline-primary" id="submitBtn" data-tipo="1">
+                                        <span class="ing-aceptar ing-fw"></span> Guardar
+                                    </button>
+                                    <button type="reset" class="btn btn-outline-danger" data-dismiss="modal">
+                                        <span class="ing-cancelar ing-fw"></span> Cancelar
+                                    </button>
+                                </div>
+                            </div>
+                        </div>
+                    </form>
+                </div>
+            </div>
+        </div>
+    </div>
+    <script src="js/jquery.min.js"></script>
+    <script src="js/bootstrap/bootstrap.min.js"></script>
+    <?php
+        require_once 'js/messages.php';
+    ?>
+    <script>
+        <?php if(!$fs_facultades){?>
+            triggerMessage("No se encontraron facultades con estos datos", "Error");
+        <?php }?>
+
+        function valida_campos(){
+            var error=false;
+            if($("#nombre").val()==""){
+                $("#nombre").addClass("is-invalid");
+                error=true;
+            }else{
+                $("#nombre").removeClass("is-invalid");
+            }
+            if($('#estado_activo').prop('checked') == false && $('#estado_inactivo').prop('checked') == false){
+                error=true;
+            }
+            if(error){
+                return false;
+            }else{
+                var btn = $('#submitBtn');
+                if(btn.data("tipo")==2)//update
+                    $('#formaModal').prop("action", "./action/action_facultades_update.php");
+                else//insert
+                    $('#formaModal').prop("action", "./action/action_facultades_insert.php");
+            }
+        }
+
+        $(document).on("click", ".btn-reset", function(event){
+            var forma = $(this).parents("form");
+            forma.find("input[type=text]").val("");
+            setDatalistFirst("#filter_desc");
+            forma.submit();
+        });
+
+        $('#modal').on('show.bs.modal', function(event){
+            var button = $(event.relatedTarget);
+            var tipo = button.data('tipo');
+            var modal = $(this);
+            if(tipo == 1){//crear
+                $("#submitBtn").data('tipo', 1);
+                $("#modalLabel").html("Crear Facultad");
+                $("#nombre").val("");
+                $('#estado_inactivo').prop('checked', false);
+                $('#estado_activo').prop('checked', true);
+            }else{//editar
+                $("#submitBtn").data('tipo', 2);
+                $("#modalLabel").html("Editar Facultad");
+                $("#nombre").val("");
+                $('#estado_inactivo').prop('checked', false);
+                $('#estado_activo').prop('checked', true);
+                var id = $(event.relatedTarget).parents("tr").data("id");
+                $.ajax({
+                    url:"action/action_facultades_select.php",
+                    type:"post",
+                    dataType:"json",
+                    data:{id_facultad: id},
+                    success:function(result){
+                        $("#id").val(result[0]["facultad_id"]);
+                        $("#nombre").val(result[0]["facultad_nombre"]);
+                        if(result[0]["facultad_activa"]==1){
+                            $('#estado_inactivo').prop('checked', false);
+                            $('#estado_activo').prop('checked', true);
+                        }else{
+                            $('#estado_inactivo').prop('checked', true);
+                            $('#estado_activo').prop('checked', false);
+                        }
+                    },
+                    error: function(){console.log("Error")}
+                });
+            }
+        });
+    </script>
+</body>
+
+</html>

+ 120 - 0
horario_profesor.php

@@ -0,0 +1,120 @@
+<?php
+require_once 'class/c_login.php';
+if (!isset($_SESSION['user']))
+    die(header('Location: index.php'));
+
+$user = unserialize($_SESSION['user']);
+$user->access();
+if (!$user->admin && in_array($user->acceso, ['n']))
+    die(header('Location: main.php?error=1'));
+
+$user->print_to_log('Consultar horario');
+
+$write = $user->admin || in_array($user->acceso, ['w']);
+// var_dump($user);
+?>
+<!DOCTYPE html>
+<html lang="en">
+
+<head>
+    <title>Consultar horario | <?= $user->facultad['facultad'] ?? 'General' ?></title>
+    <meta charset="utf-8">
+    <meta http-equiv="content-type" content="text/plain; charset=UTF-8" />
+    <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
+    <?php include_once "import/html_css_files.php"; ?>
+
+    <script src="js/scrollables.js" defer></script>
+    <script src="js/jquery.min.js" defer></script>
+    <script src="js/bootstrap/bootstrap.min.js" defer></script>
+
+    <script src="js/messages.js" defer></script>
+    <script src="js/horarios_profesor.js" defer></script>
+</head>
+<!--  -->
+
+<body style="display: block;">
+    <?php
+    include('include/constantes.php');
+    include("import/html_header.php");
+    html_header("Consultar horario", "Sistema de gestión de checador");
+    ?>
+    <?= "<!-- $user -->" ?>
+    <main class="container content marco content-margin" id="local-app">
+        <section id="message"></section>
+        <?php require('import/periodo.php') ?>
+
+        <form id="form" class="form-horizontal">
+            <div class="form-group">
+                <div class="form-box">
+                    <input type="hidden" name="periodo" value="<?= $user->periodo ?>" />
+                    <div class="form-box">
+                        <div class="form-group row">
+                            <label for="clave" class="col-4 col-form-label">Carrera</label>
+                            <div class="col-6">
+                                <input type="text" class="form-control" id="clave" name="clave" placeholder="Clave del profesor (opcional)" value="<?= $clave ?? '' ?>" pattern="(do)?[0-9]{3,6}" title="La clave debe tener 8 caracteres, los primeros 2 deben ser letras y los últimos 6 números" minlength="3" maxlength="8">
+                            </div>
+                        </div>
+                        <div class="form-group row">
+                            <label for="profesor" class="col-4 col-form-label">Nombre</label>
+                            <div class="col-6 ">
+                                <input type="text" class="form-control" id="profesor" name="nombre" placeholder="Nombre del profesor (opcional)">
+                            </div>
+                        </div>
+
+                    </div>
+
+                    <!-- ICO-BUSCAR FILTRAR & ICO-BORRAR LIMPIAR -->
+                    <div class="form-group row justify-content-center">
+                        <button class="btn btn-outline-primary mr-2">
+                            <span class="ing-buscar icono"></span>
+                            Buscar horario
+                        </button>
+                        <button type="button" class="btn btn-outline-danger" onclick="">
+                            <span class="ing-borrar icono"></span>
+                            Limpiar
+                        </button>
+                    </div>
+                </div>
+            </div>
+        </form>
+        <div class="form-group mt-4 row justify-content-center">
+            <?php if ($write) { ?>
+                <button type="button" id="nuevo" class="btn btn-outline-primary ml-4 d-none" title="Nuevo horario" data-toggle="modal" data-target="#modal-editar">
+                    <span class="ing-mas ing-fw"></span> Nuevo
+                </button>
+            <?php } ?>
+        </div>
+        </div>
+        </div>
+
+        </form>
+        <!-- Horario is a (table with one a cell) within a table 
+            7:15 - 8:45, 8:45 - 10:15, 10:30 - 12:00, 12:00 - 13:30
+            de lunes a viernes, a excepción de que tenga sábado
+        -->
+        <div id="btn-excel-horario" class="mb-2 float-right hidden">
+            <button class="btn btn-outline-secondary " title="Exportar a Excel">
+                <span class="ing-descarga ing-fw"></span> Exportar a Excel
+            </button>
+        </div>
+        <!-- Table responsive -->
+        <div class="table-responsive">
+            <table class="table table-bordered table-sm table-responsive-sm" id="table-horario">
+                <thead class="thead-dark">
+                    <tr id="headers">
+                        <th scope="col" class="text-center">Hora</th>
+                        <th scope="col" class="text-center">Lunes</th>
+                        <th scope="col" class="text-center">Martes</th>
+                        <th scope="col" class="text-center">Miércoles</th>
+                        <th scope="col" class="text-center">Jueves</th>
+                        <th scope="col" class="text-center">Viernes</th>
+                        <th scope="col" class="text-center">Sábado</th>
+                    </tr>
+                </thead>
+                <tbody id="horario"></tbody>
+            </table>
+        </div>
+    </main>
+</body>
+
+</html>

+ 5 - 0
import/html_css_files.php

@@ -0,0 +1,5 @@
+<link rel="icon" type="image/png" href="imagenes/favicon.png" />
+<link rel="stylesheet" href="css/bootstrap-ulsa.min.css" type="text/css">
+<link rel="stylesheet" href="css/indivisa.css" type="text/css">
+<link rel="stylesheet" href="css/sgi.css?rand=<?php echo rand(); ?>" type="text/css">
+<link rel="stylesheet" href="css/style.css">

+ 105 - 0
import/html_footer.php

@@ -0,0 +1,105 @@
+<div class="container-fluid">
+    <footer class="footer" >
+        <div class="footerTop">
+        <div class="container">
+                <div class="logotipo">
+                    <img src="imagenes/lasalle-logo-blanco.png" alt="Universidad La Salle" width="15%">
+                    <h3> <span>Profesionales</span>con <strong>Valor</strong></h3>
+                </div>
+                <div class="ubicacion">
+                    <div class="address">
+                        <div class="tabs">
+                            <ul class="nav list-inline" id="tabsFooter" role="tablist">
+                                <li class="list-inline-item">
+                                    <a class="nav-link  active" id="unidad1-tab" data-toggle="tab" href="#unidad1" role="tab" aria-controls="calendario" aria-selected="true">Unidad Condesa</a>
+                                </li>
+                                <li class="list-inline-item">
+                                    <a class="nav-link " id="unidad2-tab" data-toggle="tab" href="#unidad2" role="tab" aria-controls="lista" aria-selected="false">Unidad Santa Teresa</a>
+                                </li>
+                                <li class="list-inline-item">
+                                    <a class="nav-link " id="unidad3-tab" data-toggle="tab" href="#unidad3" role="tab" aria-controls="lista" aria-selected="false">Unidad San Fernando</a>
+                                </li>
+                                <li class="list-inline-item">
+                                    <a class="nav-link" id="unidad4-tab" data-toggle="tab" href="#unidad4" role="tab" aria-controls="lista" aria-selected="false">Unidad Santa Lucía</a>
+                                </li>
+                            </ul>
+                            <div class="tab-content" id="tabsCont">
+                                <div class="tab-pane fade show active" id="unidad1" role="tabpanel" aria-labelledby="unidad1-tab">
+                                    <p>Benjamín Franklin No 45, Col. Condesa, Alc. Cuauhtémoc, CDMX, CP 06140 <span class="tel">Tel. <a href="tel:+525552789500">55 5278-9500</a> / <a href="tel:+8005272553">800 LASALLE</a></span><br>
+                                    <a class="btnMap " href="https://www.google.com/maps/place/Universidad+La+Salle/@19.4085702,-99.1810039,15z/data=!4m5!3m4!1s0x0:0x3108b5797f9c9ecd!8m2!3d19.4085702!4d-99.1810039" target="_blank"> <span class="fas fa-map-marker-alt mr-1"></span>¿Cómo llegar?</a></p>
+                                </div>
+                                <div class="tab-pane fade" id="unidad2" role="tabpanel" aria-labelledby="unidad2-tab">
+                                    <p>Camino a Santa Teresa 811, Col. Rinconada del Pedregal, Alc. Tlalpan, CDMX, CP 14010 <span class="tel">Tel. <a href="tel:5552789500">55 5278-9500</a> / <a href="tel:+8005272553">800 LASALLE</a></span><br>
+                                    <a class="btnMap " href="https://www.google.com/maps/place/Universidad+La+Salle+Unidad+Santa+Teresa/@19.299013,-99.196093,15z/data=!4m5!3m4!1s0x0:0xdfc2b61c9b67aac2!8m2!3d19.299013!4d-99.196093" target="_blank"> <span class="fas fa-map-marker-alt mr-1"></span>¿Cómo llegar?</a></p>
+                                </div>
+                                <div class="tab-pane fade" id="unidad3" role="tabpanel" aria-labelledby="unidad3-tab">
+                                    <p>Av. De Las Fuentes 17, Col. Tlalpan, Alc. Tlalpan, CDMX, CP 14000 <span class="tel">Tel. <a href="tel:+525552789500">55 5278-9500</a> / <a href="tel:+8005272553">800 LASALLE</a></span><br>
+                                    <a class="btnMap " href="https://www.google.com/maps/place/Universidad+La+Salle+Facultad+de+Medicina/@19.2930318,-99.1720808,15z/data=!4m5!3m4!1s0x0:0x29b7725e5a004277!8m2!3d19.2930318!4d-99.1720808" target="_blank"> <span class="fas fa-map-marker-alt mr-1"></span>¿Cómo llegar?</a></p>
+                                </div>
+                                <div class="tab-pane fade" id="unidad4" role="tabpanel" aria-labelledby="unidad4-tab">
+                                    <p>Av. Tamaulipas 3, Col. Zona Federal, Alc. Álvaro Obregón, CDMX, CP 01357 <span class="tel">Tel. <a href="tel:5556021130">55 5602-1130</a> </span><br>
+                                    <a class="btnMap " href="https://www.google.com/maps/place/Unidad+Deportiva+La+Salle/@19.3662852,-99.2421597,15z/data=!4m5!3m4!1s0x0:0x88e0334f044bc518!8m2!3d19.3662852!4d-99.2421597" target="_blank"> <span class="fas fa-map-marker-alt mr-1"></span>¿Cómo llegar?</a></p>
+                                </div>
+                            </div>
+                        </div>
+                    </div>
+                    <div class="redes">
+                        <h4>Compartir :</h4>
+                        <ul>
+                            <li><a href="https://www.facebook.com/LaSalleMXIngenieria" target="_blank"><i class="fab fa-facebook-f fa-fw"></i></a></li>
+                            <!--<li><a href="https://twitter.com/lasalle_mx" target="_blank"><i class="fab fa-twitter fa-fw"></i></a></li>-->
+                            <li><a href="https://www.youtube.com/user/IngenieriaLaSalle/" target="_blank"><i class="fab fa-youtube fa-fw"></i></a></li>
+                            <!--<li><a href="https://www.instagram.com/lasalle_mx/" target="_blank"><i class="fab fa-instagram fa-fw"></i></a></li>-->
+                            <!--<li><a href="https://www.linkedin.com/school/universidad-la-salle?pathWildcard=24227" target="_blank"><i class="fab fa-linkedin-in fa-fw"></i></a></li>-->
+                        </ul>
+                    </div>
+                </div>
+            </div>
+        </div>
+        <div class="footerMiddle">
+            <div class="container">
+                <div class="row justify-content-md-center">
+                    <nav class="col-12 col-md-10">
+                        <a class="footerMore menuMore" href="#">Sistema y Red La Salle</a>
+                        <ul>
+                            <li><a href="http://bajio.delasalle.edu.mx/" target="_blank">Bajío</a></li>
+                            <li><a href="http://www.lasalle.mx/" target="_blank">Ciudad de México</a></li>
+                            <li><a href="http://lasallecancun.edu.mx/" target="_blank">Cancún</a></li>
+                            <li><a href="http://www.ulsapuebla.mx/" target="_blank">Puebla</a></li>
+                            <li><a href="http://www.ulsapuebla.mx/" target="_blank">Chihuahua</a></li>
+                            <li><a href="http://www.lasallecuernavaca.edu.mx/wp/" target="_blank">Cuernavaca</a></li>
+                            <li><a href="http://www.ulsalaguna.edu.mx/" target="_blank">Laguna</a></li>
+                            <li><a href="http://www.lasallemorelia.edu.mx/" target="_blank">Morelia</a></li>
+                            <li><a href="http://www.ulsaneza.edu.mx/" target="_blank">Nezahualcóyotl</a></li>
+                            <li><a href="http://www.ulsa-noroeste.edu.mx/n2015/" target="_blank">Noroeste</a></li>
+                            <li><a href="http://www.ulsaoaxaca.edu.mx/" target="_blank">Oaxaca</a></li>
+                            <li><a href="http://www.lasallep.edu.mx/" target="_blank">Pachuca</a></li>
+                            <li><a href="https://www.ulsasaltillo.edu.mx/" target="_blank">Saltillo</a></li>
+                            <li><a href="https://www.lasallevictoria.edu.mx/" target="_blank">Victoria</a></li>
+                        </ul>
+                    </nav>
+                </div>
+            </div>
+        </div>
+        <div class="footerBottom">
+            <div class="container">
+                <div class="logotipos">
+                    <ul>
+                        <li><a href="http://redlasalle.mx/" target="_blank"><img src="imagenes/la-salle-logo-red-universidades.png" alt="La Salle - logotipo" class="img-responsive" width="80"></a></li>
+                    <li><a href="http://ialu.org/english/" target="_blank"><img src="imagenes/la-salle-logo-international-ia.png" alt="La Salle - logotipo" class="img-responsive" width="80"></a></li>
+                    </ul>
+                </div>
+                <div class="legales">
+                    <a class="footerMore menuMore" href="#">Legales</a>
+                    <ul>
+                        <li><a href="https://lasalle.mx/globales/contacto.html" target="_blank">Contacto</a></li>
+                        <li><a href="https://lasalle.mx/globales/terminos-y-condiciones.html" target="_blank">Términos y condiciones</a></li>
+                        <li><a href="https://lasalle.mx/globales/aviso-de-privacidad.html" target="_blank">Aviso de Privacidad</a></li>
+                        <!--<li><a href="https://lasalle.mx/globales/mapa-de-sitio.html" target="_blank">Mapa de sitio</a></li>
+                        <li><a href="https://lasalle.mx/globales/preguntas-frecuentes/" target="_blank">Preguntas frecuentes</a></li>-->
+                    </ul>
+                </div>
+            </div>
+        </div>
+    </footer>
+</div>

部分文件因为文件数量过多而无法显示