periodo_id, $profesor_id['profesor_id']]
+ );
+
+ echo json_encode($horarios);
+ break;
+ default:
+ header('HTTP/1.1 405 Method Not Allowed');
+ echo json_encode(['error' => 'M茅todo no permitido']);
+ break;
+ }
+} catch (PDOException $e) {
+ echo json_encode([
+ 'error' => $e->getMessage(),
+ 'query' => $db->getLastQuery(),
+ 'exception' => $e->getTraceAsString()
+ ]);
}
\ No newline at end of file
diff --git a/action/justificar.php b/action/justificar.php
index 4b9308b..60e3636 100644
--- a/action/justificar.php
+++ b/action/justificar.php
@@ -1,76 +1,76 @@
- 'No se ha iniciado sesi贸n']);
- exit();
-}
-$user = Login::get_user();
-
-try {
- switch ($_SERVER['REQUEST_METHOD']) {
- case 'POST':
- // check parameters
-
- $raw = file_get_contents('php://input');
- $post_data = json_decode($raw, true);
-
- $data = $db->querySingle(
- 'WITH HORARIOS AS (
- SELECT *
- FROM horario
- JOIN horario_profesor USING (horario_id)
- WHERE horario.periodo_id = :periodo_id
- )
- INSERT INTO registro (profesor_id, horario_id, registro_fecha_ideal, registro_justificada, justificador_id, registro_fecha_justificacion, justificacion)
- VALUES (:profesor_id, :horario_id, :registro_fecha_ideal, :registro_justificada, :justificador_id, NOW(), :justificacion)
- ON CONFLICT (profesor_id, horario_id, registro_fecha_ideal)
- DO UPDATE SET registro_justificada = :registro_justificada, justificador_id = :justificador_id, registro_fecha_justificacion = NOW(), justificacion = :justificacion
- RETURNING *',
- array(
- 'periodo_id' => $user->periodo_id,
- 'profesor_id' => $post_data['profesor_id'],
- 'horario_id' => $post_data['horario_id'],
- 'registro_fecha_ideal' => $post_data['registro_fecha_ideal'],
- 'registro_justificada' => $post_data['registro_justificada'],
- 'justificador_id' => $user->user['id'],
- 'justificacion' => empty($post_data['justificacion']) ? null : $post_data['justificacion'],
- )
- );
-
-
- $data_justificador = $db->querySingle(
- "SELECT justificador.usuario_nombre as justificador_nombre,
- justificador.usuario_clave as justificador_clave,
- facultad.facultad_nombre as justificador_facultad, rol.rol_titulo as justificador_rol
-
- FROM USUARIO JUSTIFICADOR
- JOIN ROL on ROL.rol_id = justificador.rol_id
- LEFT JOIN facultad on facultad.facultad_id = justificador.facultad_id
- where justificador.usuario_id = :justificador_id",
- array(
- 'justificador_id' => $user->user['id'],
- )
- );
- // exit('exit');
-
- echo json_encode(array_merge($data, $data_justificador), JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT);
- break;
-
- default:
- header('HTTP/1.1 405 Method Not Allowed');
- echo json_encode(['error' => 'M茅todo no permitido']);
- }
-} catch (PDOException $e) {
- echo json_encode([
- 'error' => $e->getMessage(),
- 'query' => $db->getLastQuery(),
- 'exception' => $e->getTraceAsString()
- ]);
-} catch (Exception $e) {
- echo json_encode([
- 'error' => $e->getMessage(),
- 'exception' => $e->getTraceAsString()
- ]);
+ 'No se ha iniciado sesi贸n']);
+ exit();
+}
+$user = Login::get_user();
+
+try {
+ switch ($_SERVER['REQUEST_METHOD']) {
+ case 'POST':
+ // check parameters
+
+ $raw = file_get_contents('php://input');
+ $post_data = json_decode($raw, true);
+
+ $data = $db->querySingle(
+ 'WITH HORARIOS AS (
+ SELECT *
+ FROM horario
+ JOIN horario_profesor USING (horario_id)
+ WHERE horario.periodo_id = :periodo_id
+ )
+ INSERT INTO registro (profesor_id, horario_id, registro_fecha_ideal, registro_justificada, justificador_id, registro_fecha_justificacion, justificacion)
+ VALUES (:profesor_id, :horario_id, :registro_fecha_ideal, :registro_justificada, :justificador_id, NOW(), :justificacion)
+ ON CONFLICT (profesor_id, horario_id, registro_fecha_ideal)
+ DO UPDATE SET registro_justificada = :registro_justificada, justificador_id = :justificador_id, registro_fecha_justificacion = NOW(), justificacion = :justificacion
+ RETURNING *',
+ array(
+ 'periodo_id' => $user->periodo_id,
+ 'profesor_id' => $post_data['profesor_id'],
+ 'horario_id' => $post_data['horario_id'],
+ 'registro_fecha_ideal' => $post_data['registro_fecha_ideal'],
+ 'registro_justificada' => $post_data['registro_justificada'],
+ 'justificador_id' => $user->user['id'],
+ 'justificacion' => empty($post_data['justificacion']) ? null : $post_data['justificacion'],
+ )
+ );
+
+
+ $data_justificador = $db->querySingle(
+ "SELECT justificador.usuario_nombre as justificador_nombre,
+ justificador.usuario_clave as justificador_clave,
+ facultad.facultad_nombre as justificador_facultad, rol.rol_titulo as justificador_rol
+
+ FROM USUARIO JUSTIFICADOR
+ JOIN ROL on ROL.rol_id = justificador.rol_id
+ LEFT JOIN facultad on facultad.facultad_id = justificador.facultad_id
+ where justificador.usuario_id = :justificador_id",
+ array(
+ 'justificador_id' => $user->user['id'],
+ )
+ );
+ // exit('exit');
+
+ echo json_encode(array_merge($data, $data_justificador), JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT);
+ break;
+
+ default:
+ header('HTTP/1.1 405 Method Not Allowed');
+ echo json_encode(['error' => 'M茅todo no permitido']);
+ }
+} catch (PDOException $e) {
+ echo json_encode([
+ 'error' => $e->getMessage(),
+ 'query' => $db->getLastQuery(),
+ 'exception' => $e->getTraceAsString()
+ ]);
+} catch (Exception $e) {
+ echo json_encode([
+ 'error' => $e->getMessage(),
+ 'exception' => $e->getTraceAsString()
+ ]);
}
\ No newline at end of file
diff --git a/action/nivel.php b/action/nivel.php
index 5a5d58a..685247a 100644
--- a/action/nivel.php
+++ b/action/nivel.php
@@ -1,31 +1,31 @@
- 'No se ha iniciado sesi贸n']);
- exit();
-}
-$user = Login::get_user();
-
-try {
- switch ($_SERVER['REQUEST_METHOD']) {
- case 'GET':
- // Fetch all puestos
- $nivel = $db->get('nivel');
- echo json_encode($nivel);
- break;
- default:
- header('HTTP/1.1 405 Method Not Allowed');
- echo json_encode(['error' => 'M茅todo no permitido']);
- break;
- }
-} catch (PDOException $e) {
- echo json_encode([
- 'error' => $e->getMessage(),
- 'query' => $db->getLastQuery(),
- 'exception' => $e->getTraceAsString()
- ]);
+ 'No se ha iniciado sesi贸n']);
+ exit();
+}
+$user = Login::get_user();
+
+try {
+ switch ($_SERVER['REQUEST_METHOD']) {
+ case 'GET':
+ // Fetch all puestos
+ $nivel = $db->get('nivel');
+ echo json_encode($nivel);
+ break;
+ default:
+ header('HTTP/1.1 405 Method Not Allowed');
+ echo json_encode(['error' => 'M茅todo no permitido']);
+ break;
+ }
+} catch (PDOException $e) {
+ echo json_encode([
+ 'error' => $e->getMessage(),
+ 'query' => $db->getLastQuery(),
+ 'exception' => $e->getTraceAsString()
+ ]);
}
\ No newline at end of file
diff --git a/action/periodo_datos.php b/action/periodo_datos.php
index 7da8339..2be9c31 100644
--- a/action/periodo_datos.php
+++ b/action/periodo_datos.php
@@ -1,44 +1,44 @@
-
-#input $_GET['id_espacio_sgu']
-#output rutas: [ ...ruta, salones: [{...salon}] ]
-header('Content-Type: application/json charset=utf-8');
-ini_set('display_errors', 1);
-ini_set('display_startup_errors', 1);
-error_reporting(E_ALL);
-
-$ruta = "../";
-require_once $ruta . "class/c_login.php";
-
-if (!isset($_SESSION['user'])) {
- http_response_code(401);
- die(json_encode(['error' => 'unauthorized']));
-}
-$user = unserialize($_SESSION['user']);
-// check method
-if ($_SERVER['REQUEST_METHOD'] !== 'GET') {
- http_response_code(405);
- die(json_encode(['error' => 'method not allowed']));
-}
-
-const JSON_OPTIONS = JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES | JSON_PARTIAL_OUTPUT_ON_ERROR;
-try {
- $data = $db->querySingle("SELECT *, LEAST(periodo_fecha_fin, CURRENT_DATE) as fecha_final FROM periodo WHERE periodo_id = ?", array($user->periodo_id));
- $last_query = [
- 'query' => $db->getLastQuery(),
- ];
-
- echo json_encode($data, JSON_OPTIONS);
-} catch (PDOException $th) {
- http_response_code(500);
- echo json_encode([
- 'error' => $th->getMessage(),
- 'query' => $db->getLastQuery(),
- ], JSON_OPTIONS);
- exit;
-} catch (Exception $th) {
- http_response_code(500);
- echo json_encode([
- 'error' => $th->getMessage(),
- ], JSON_OPTIONS);
- exit;
+
+#input $_GET['id_espacio_sgu']
+#output rutas: [ ...ruta, salones: [{...salon}] ]
+header('Content-Type: application/json charset=utf-8');
+ini_set('display_errors', 1);
+ini_set('display_startup_errors', 1);
+error_reporting(E_ALL);
+
+$ruta = "../";
+require_once $ruta . "class/c_login.php";
+
+if (!isset($_SESSION['user'])) {
+ http_response_code(401);
+ die(json_encode(['error' => 'unauthorized']));
+}
+$user = unserialize($_SESSION['user']);
+// check method
+if ($_SERVER['REQUEST_METHOD'] !== 'GET') {
+ http_response_code(405);
+ die(json_encode(['error' => 'method not allowed']));
+}
+
+const JSON_OPTIONS = JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES | JSON_PARTIAL_OUTPUT_ON_ERROR;
+try {
+ $data = $db->querySingle("SELECT *, LEAST(periodo_fecha_fin, CURRENT_DATE) as fecha_final FROM periodo WHERE periodo_id = ?", array($user->periodo_id));
+ $last_query = [
+ 'query' => $db->getLastQuery(),
+ ];
+
+ echo json_encode($data, JSON_OPTIONS);
+} catch (PDOException $th) {
+ http_response_code(500);
+ echo json_encode([
+ 'error' => $th->getMessage(),
+ 'query' => $db->getLastQuery(),
+ ], JSON_OPTIONS);
+ exit;
+} catch (Exception $th) {
+ http_response_code(500);
+ echo json_encode([
+ 'error' => $th->getMessage(),
+ ], JSON_OPTIONS);
+ exit;
}
\ No newline at end of file
diff --git a/action/periodos.php b/action/periodos.php
index bc1d98f..6f38b7b 100644
--- a/action/periodos.php
+++ b/action/periodos.php
@@ -1,204 +1,204 @@
- 'No se ha iniciado sesi贸n']);
- exit();
-}
-$user = Login::get_user();
-
-try {
- switch ($_SERVER['REQUEST_METHOD']) {
- case 'GET':
- // Fetch all puestos
- $periodo_id = $user->periodo_id;
- if (is_null($user->facultad['facultad_id'])) {
- $periodos = $db
- //->where('CURRENT_DATE BETWEEN periodo_fecha_inicio AND periodo_fecha_fin')
- ->join('nivel', 'nivel.nivel_id = periodo.nivel_id')
- ->orderBy('periodo_id')
- ->get('periodo', null, 'periodo.*, nivel_nombre as nivel');
- } else {
- $periodos = $db->query(
- "SELECT DISTINCT periodo.*, nivel_nombre as nivel FROM periodo
- JOIN horario_view USING (periodo_id)
- JOIN nivel ON nivel.nivel_id = periodo.nivel_id
- WHERE /*CURRENT_DATE BETWEEN periodo.periodo_fecha_inicio AND periodo.periodo_fecha_fin
- AND */facultad_id = :facultad_id
- ORDER BY periodo_id
- ",
- ['facultad_id' => $user->facultad['facultad_id']]
- );
- }
- echo json_encode($periodos);
- break;
-
- case 'PUT':
- // Update nivel_id of a periodo
- $raw = file_get_contents('php://input');
- $data = json_decode($raw, true);
-
- if (!isset($data['action'])) {
- header('HTTP/1.1 400 Bad Request');
- echo json_encode(['error' => 'Falta la acci贸n a realizar']);
- exit();
- }
-
- switch ($data['action']) {
- case 'changeNivel':
- if (!isset($data['periodo_id'], $data['nivel_id'])) {
- header('HTTP/1.1 400 Bad Request');
- echo json_encode(['error' => 'Falta el id del periodo o el nivel']);
- exit();
- }
-
- $periodo_id = $data['periodo_id'];
- $nivel_id = $data['nivel_id'];
- $db->where('periodo_id', $periodo_id)->update('periodo', ['nivel_id' => $nivel_id]);
-
- $periodo_nombre = $db->where('periodo_id', $periodo_id)->getOne('periodo', 'periodo_nombre')['periodo_nombre'];
- $nivel_nombre = $db->where('nivel_id', $nivel_id)->getOne('nivel', 'nivel_nombre')['nivel_nombre'];
-
- echo json_encode([
- 'success' =>
- "El nivel del periodo $periodo_nombre ha sido cambiado a $nivel_nombre"
- ]);
- break;
-
- case 'changeFechaInicio':
- if (!isset($data['periodo_id'], $data['periodo_fecha_inicio'])) {
- header('HTTP/1.1 400 Bad Request');
- echo json_encode(['error' => 'Falta el id del periodo o la fecha de inicio']);
- exit();
- }
-
- $periodo_id = $data['periodo_id'];
- $periodo_fecha_inicio = $data['periodo_fecha_inicio'];
- $db->where('periodo_id', $periodo_id)->update('periodo', ['periodo_fecha_inicio' => $periodo_fecha_inicio]);
-
- $periodo_nombre = $db->where('periodo_id', $periodo_id)->getOne('periodo', 'periodo_nombre')['periodo_nombre'];
-
- echo json_encode([
- 'success' =>
- "La fecha de inicio del periodo $periodo_nombre ha sido cambiada a $periodo_fecha_inicio"
- ]);
- break;
-
- case 'changeFechaFin':
- if (!isset($data['periodo_id'], $data['periodo_fecha_fin'])) {
- header('HTTP/1.1 400 Bad Request');
- echo json_encode(['error' => 'Falta el id del periodo o la fecha de fin']);
- exit();
- }
-
- $periodo_id = $data['periodo_id'];
- $periodo_fecha_fin = $data['periodo_fecha_fin'];
- $db->where('periodo_id', $periodo_id)->update('periodo', ['periodo_fecha_fin' => $periodo_fecha_fin]);
-
- $periodo_nombre = $db->where('periodo_id', $periodo_id)->getOne('periodo', 'periodo_nombre')['periodo_nombre'];
-
- echo json_encode([
- 'success' =>
- "La fecha de fin del periodo $periodo_nombre ha sido cambiada a $periodo_fecha_fin"
- ]);
- break;
-
- case 'updatePeriodo':
- if (!isset($data['periodo_id'], $data['periodo_nombre'], $data['id_periodo_sgu'], $data['periodo_clave'])) {
- header('HTTP/1.1 400 Bad Request');
- echo json_encode(['error' => 'Faltan datos para actualizar el periodo']);
- exit();
- }
-
- $periodo_id = $data['periodo_id'];
-
- $db->where('periodo_id', $periodo_id)->update('periodo', array_filter($data, fn($key) => in_array($key, [
- 'periodo_nombre',
- 'id_periodo_sgu',
- 'periodo_clave',
- ]), ARRAY_FILTER_USE_KEY));
-
- $periodo_nombre = $db->where('periodo_id', $periodo_id)->getOne('periodo', 'periodo_nombre')['periodo_nombre'];
-
- echo json_encode([
- 'success' =>
- "El periodo $periodo_nombre ha sido actualizado"
- ]);
- break;
-
- default:
- header('HTTP/1.1 400 Bad Request');
- echo json_encode(['error' => 'Acci贸n no v谩lida']);
- exit();
- }
-
- break;
- case 'POST':
- $raw = file_get_contents('php://input');
- $data = json_decode($raw, true);
-
- if (!isset($data['periodo_nombre'], $data['nivel_id'], $data['periodo_fecha_inicio'], $data['periodo_fecha_fin'])) {
- header('HTTP/1.1 400 Bad Request');
- echo json_encode(['error' => 'Faltan datos para crear el periodo']);
- exit();
- }
-
- $newPeriodo = $db->insert(
- 'periodo',
- array_filter($data, fn($key) => in_array($key, [
- 'periodo_nombre',
- 'nivel_id',
- 'periodo_fecha_inicio',
- 'periodo_fecha_fin',
- 'periodo_clave',
- 'id_periodo_sgu',
- ]), ARRAY_FILTER_USE_KEY)
- );
-
- echo json_encode([
- 'success' => true,
- 'message' => 'El periodo ha sido creado',
- 'periodo' => $newPeriodo
- ]);
- break;
-
- case 'DELETE':
- // Delete a periodo
- $raw = file_get_contents('php://input');
- $data = json_decode($raw, true);
-
- if (!isset($data['periodo_id'])) {
- header('HTTP/1.1 400 Bad Request');
- echo json_encode(['error' => 'Falta el id del periodo']);
- exit();
- }
-
- $periodo_id = $data['periodo_id'];
- $periodo_nombre = $db->where('periodo_id', $periodo_id)->getOne('periodo', 'periodo_nombre')['periodo_nombre'];
-
- $db->where('periodo_id', $periodo_id)->delete('periodo');
-
- echo json_encode([
- 'success' => true,
- 'message' => "El periodo $periodo_nombre ha sido eliminado"
- ]);
- break;
- default:
- header('HTTP/1.1 405 Method Not Allowed');
- echo json_encode(['error' => 'M茅todo no permitido']);
- break;
- }
-} catch (PDOException $e) {
- echo json_encode([
- 'error' => $e->getMessage(),
- 'query' => $db->getLastQuery(),
- 'exception' => $e->getTraceAsString()
- ]);
-} catch (Exception $e) {
- echo json_encode([
- 'error' => $e->getMessage(),
- 'exception' => $e->getTraceAsString()
- ]);
+ 'No se ha iniciado sesi贸n']);
+ exit();
+}
+$user = Login::get_user();
+
+try {
+ switch ($_SERVER['REQUEST_METHOD']) {
+ case 'GET':
+ // Fetch all puestos
+ $periodo_id = $user->periodo_id;
+ if (is_null($user->facultad['facultad_id'])) {
+ $periodos = $db
+ //->where('CURRENT_DATE BETWEEN periodo_fecha_inicio AND periodo_fecha_fin')
+ ->join('nivel', 'nivel.nivel_id = periodo.nivel_id')
+ ->orderBy('periodo_id')
+ ->get('periodo', null, 'periodo.*, nivel_nombre as nivel');
+ } else {
+ $periodos = $db->query(
+ "SELECT DISTINCT periodo.*, nivel_nombre as nivel FROM periodo
+ JOIN horario_view USING (periodo_id)
+ JOIN nivel ON nivel.nivel_id = periodo.nivel_id
+ WHERE /*CURRENT_DATE BETWEEN periodo.periodo_fecha_inicio AND periodo.periodo_fecha_fin
+ AND */facultad_id = :facultad_id
+ ORDER BY periodo_id
+ ",
+ ['facultad_id' => $user->facultad['facultad_id']]
+ );
+ }
+ echo json_encode($periodos);
+ break;
+
+ case 'PUT':
+ // Update nivel_id of a periodo
+ $raw = file_get_contents('php://input');
+ $data = json_decode($raw, true);
+
+ if (!isset($data['action'])) {
+ header('HTTP/1.1 400 Bad Request');
+ echo json_encode(['error' => 'Falta la acci贸n a realizar']);
+ exit();
+ }
+
+ switch ($data['action']) {
+ case 'changeNivel':
+ if (!isset($data['periodo_id'], $data['nivel_id'])) {
+ header('HTTP/1.1 400 Bad Request');
+ echo json_encode(['error' => 'Falta el id del periodo o el nivel']);
+ exit();
+ }
+
+ $periodo_id = $data['periodo_id'];
+ $nivel_id = $data['nivel_id'];
+ $db->where('periodo_id', $periodo_id)->update('periodo', ['nivel_id' => $nivel_id]);
+
+ $periodo_nombre = $db->where('periodo_id', $periodo_id)->getOne('periodo', 'periodo_nombre')['periodo_nombre'];
+ $nivel_nombre = $db->where('nivel_id', $nivel_id)->getOne('nivel', 'nivel_nombre')['nivel_nombre'];
+
+ echo json_encode([
+ 'success' =>
+ "El nivel del periodo $periodo_nombre ha sido cambiado a $nivel_nombre"
+ ]);
+ break;
+
+ case 'changeFechaInicio':
+ if (!isset($data['periodo_id'], $data['periodo_fecha_inicio'])) {
+ header('HTTP/1.1 400 Bad Request');
+ echo json_encode(['error' => 'Falta el id del periodo o la fecha de inicio']);
+ exit();
+ }
+
+ $periodo_id = $data['periodo_id'];
+ $periodo_fecha_inicio = $data['periodo_fecha_inicio'];
+ $db->where('periodo_id', $periodo_id)->update('periodo', ['periodo_fecha_inicio' => $periodo_fecha_inicio]);
+
+ $periodo_nombre = $db->where('periodo_id', $periodo_id)->getOne('periodo', 'periodo_nombre')['periodo_nombre'];
+
+ echo json_encode([
+ 'success' =>
+ "La fecha de inicio del periodo $periodo_nombre ha sido cambiada a $periodo_fecha_inicio"
+ ]);
+ break;
+
+ case 'changeFechaFin':
+ if (!isset($data['periodo_id'], $data['periodo_fecha_fin'])) {
+ header('HTTP/1.1 400 Bad Request');
+ echo json_encode(['error' => 'Falta el id del periodo o la fecha de fin']);
+ exit();
+ }
+
+ $periodo_id = $data['periodo_id'];
+ $periodo_fecha_fin = $data['periodo_fecha_fin'];
+ $db->where('periodo_id', $periodo_id)->update('periodo', ['periodo_fecha_fin' => $periodo_fecha_fin]);
+
+ $periodo_nombre = $db->where('periodo_id', $periodo_id)->getOne('periodo', 'periodo_nombre')['periodo_nombre'];
+
+ echo json_encode([
+ 'success' =>
+ "La fecha de fin del periodo $periodo_nombre ha sido cambiada a $periodo_fecha_fin"
+ ]);
+ break;
+
+ case 'updatePeriodo':
+ if (!isset($data['periodo_id'], $data['periodo_nombre'], $data['id_periodo_sgu'], $data['periodo_clave'])) {
+ header('HTTP/1.1 400 Bad Request');
+ echo json_encode(['error' => 'Faltan datos para actualizar el periodo']);
+ exit();
+ }
+
+ $periodo_id = $data['periodo_id'];
+
+ $db->where('periodo_id', $periodo_id)->update('periodo', array_filter($data, fn($key) => in_array($key, [
+ 'periodo_nombre',
+ 'id_periodo_sgu',
+ 'periodo_clave',
+ ]), ARRAY_FILTER_USE_KEY));
+
+ $periodo_nombre = $db->where('periodo_id', $periodo_id)->getOne('periodo', 'periodo_nombre')['periodo_nombre'];
+
+ echo json_encode([
+ 'success' =>
+ "El periodo $periodo_nombre ha sido actualizado"
+ ]);
+ break;
+
+ default:
+ header('HTTP/1.1 400 Bad Request');
+ echo json_encode(['error' => 'Acci贸n no v谩lida']);
+ exit();
+ }
+
+ break;
+ case 'POST':
+ $raw = file_get_contents('php://input');
+ $data = json_decode($raw, true);
+
+ if (!isset($data['periodo_nombre'], $data['nivel_id'], $data['periodo_fecha_inicio'], $data['periodo_fecha_fin'])) {
+ header('HTTP/1.1 400 Bad Request');
+ echo json_encode(['error' => 'Faltan datos para crear el periodo']);
+ exit();
+ }
+
+ $newPeriodo = $db->insert(
+ 'periodo',
+ array_filter($data, fn($key) => in_array($key, [
+ 'periodo_nombre',
+ 'nivel_id',
+ 'periodo_fecha_inicio',
+ 'periodo_fecha_fin',
+ 'periodo_clave',
+ 'id_periodo_sgu',
+ ]), ARRAY_FILTER_USE_KEY)
+ );
+
+ echo json_encode([
+ 'success' => true,
+ 'message' => 'El periodo ha sido creado',
+ 'periodo' => $newPeriodo
+ ]);
+ break;
+
+ case 'DELETE':
+ // Delete a periodo
+ $raw = file_get_contents('php://input');
+ $data = json_decode($raw, true);
+
+ if (!isset($data['periodo_id'])) {
+ header('HTTP/1.1 400 Bad Request');
+ echo json_encode(['error' => 'Falta el id del periodo']);
+ exit();
+ }
+
+ $periodo_id = $data['periodo_id'];
+ $periodo_nombre = $db->where('periodo_id', $periodo_id)->getOne('periodo', 'periodo_nombre')['periodo_nombre'];
+
+ $db->where('periodo_id', $periodo_id)->delete('periodo');
+
+ echo json_encode([
+ 'success' => true,
+ 'message' => "El periodo $periodo_nombre ha sido eliminado"
+ ]);
+ break;
+ default:
+ header('HTTP/1.1 405 Method Not Allowed');
+ echo json_encode(['error' => 'M茅todo no permitido']);
+ break;
+ }
+} catch (PDOException $e) {
+ echo json_encode([
+ 'error' => $e->getMessage(),
+ 'query' => $db->getLastQuery(),
+ 'exception' => $e->getTraceAsString()
+ ]);
+} catch (Exception $e) {
+ echo json_encode([
+ 'error' => $e->getMessage(),
+ 'exception' => $e->getTraceAsString()
+ ]);
}
\ No newline at end of file
diff --git a/action/profesor_faltas.php b/action/profesor_faltas.php
index 0e9e896..da97a64 100644
--- a/action/profesor_faltas.php
+++ b/action/profesor_faltas.php
@@ -1,135 +1,135 @@
-
-require_once "{$_SERVER['DOCUMENT_ROOT']}/class/c_login.php";
-header('Content-Type: application/json');
-
-if (!Login::is_logged()) {
- header('HTTP/1.1 401 Unauthorized');
- echo json_encode(['error' => 'No se ha iniciado sesi贸n']);
- exit();
-}
-$user = Login::get_user();
-
-try {
- switch ($_SERVER['REQUEST_METHOD']) {
- case 'GET':
- $facultad = $_GET['facultad'] ?? $user->facultad['facultad_id'] ?? null;
- $porcentaje = $_GET['porcentaje'] ?? null;
- $faltas = $_GET['faltas'] ?? null;
-
- if (!isset($facultad) || !is_numeric($facultad)) {
- $error = 'No se ha seleccionado una facultad';
- } else if ((!isset($faltas) || !is_numeric($faltas)) && (!isset($porcentaje) || !is_numeric($porcentaje))) {
- $error = 'Debe especificar las faltas o el porcentaje';
- } else if (isset($faltas) && (!is_numeric($faltas) || $faltas <= 0)) {
- $error = 'Las faltas deben ser un n煤mero mayor a 0';
- } else if (isset($porcentaje) && (!is_numeric($porcentaje) || $porcentaje <= 0)) {
- $error = 'El porcentaje debe ser un n煤mero mayor a 0';
- } else if (isset($faltas) && isset($porcentaje)) {
- $error = 'No se puede especificar las faltas y el porcentaje al mismo tiempo';
- } else if (!isset($facultad) || !is_numeric($facultad)) {
- $error = 'Debe especificar una facultad';
- }
-
- if (isset($error)) {
- header('HTTP/1.1 400 Bad Request');
- echo json_encode(['error' => $error]);
- exit();
- }
- // Initialize the data array
- $data = array();
-
- // Check if 'profesor' or 'supervisor' is set and prepare the specific part of the SQL query accordingly.
- if (isset($_GET['profesor']) || isset($_GET['supervisor'])) {
-
- $condition = isset($_GET['profesor'])
- ? "r.registro_fecha IS NULL AND NOT COALESCE(r.registro_justificada, FALSE)"
- : "estado_supervisor_id = 2";
-
- $filter = isset($faltas)
- ? "afcp.faltas >= :faltas"
- : "afcp.porcentaje >= :porcentaje";
-
- // Prepare the SQL query with placeholders for parameters
- $data = array_column($db->query(
- "WITH fechas AS (
- SELECT
- h.horario_id,
- fechas_clase(h.horario_id, true) AS registro_fecha_ideal,
- hp.profesor_id
- FROM horario h
- JOIN horario_profesor hp USING (horario_id)
- WHERE (h.PERIODO_ID, h.FACULTAD_ID) = (:periodo_id, :facultad_id) AND hp.profesor_id <> 0
- ),
- asistencia_faltas AS (
- SELECT
- f.profesor_id,
- COUNT(1) AS total,
- COUNT(1) FILTER (WHERE $condition AND f.registro_fecha_ideal <= current_date) AS faltas
- FROM fechas f
- LEFT JOIN registro r USING (registro_fecha_ideal, horario_id, profesor_id)
- GROUP BY f.profesor_id
- ),
- asistencia_faltas_con_porcentaje AS (
- SELECT
- af.profesor_id,
- af.faltas,
- af.total,
- CASE
- WHEN af.total > 0 THEN ROUND((af.faltas::NUMERIC / af.total) * 100, 2)
- ELSE NULL
- END AS porcentaje
- FROM asistencia_faltas af
- WHERE af.faltas > 0
- )
- SELECT
- json_build_object(
- 'profesor', json_build_object(
- 'profesor_nombre', p.profesor_nombre,
- 'profesor_clave', p.profesor_clave,
- 'profesor_correo', p.profesor_correo
- ),
- 'profesor_id', afcp.profesor_id,
- 'faltas', afcp.faltas,
- 'total', afcp.total,
- 'porcentaje', afcp.porcentaje
- ) AS result_json
- FROM asistencia_faltas_con_porcentaje afcp
- JOIN profesor p USING (profesor_id)
- WHERE $filter
- ORDER BY afcp.porcentaje DESC;
- ",
- [
- 'periodo_id' => $user->periodo_id,
- 'facultad_id' => $facultad,
- ] + (isset($faltas)
- ? ['faltas' => $faltas]
- : ['porcentaje' => $porcentaje])
- ), 'result_json');
- } else {
- // Send a 400 Bad Request header and an error message in JSON format
- header('HTTP/1.1 400 Bad Request');
- echo json_encode(['error' => 'Especifique si las faltas son de profesor o supervisor']);
- exit();
- }
- if (empty($data)) {
- header('HTTP/1.1 404 Not Found');
- echo json_encode(['error' => 'No se encontraron faltas']);
- } else {
- echo json_encode(
- array_map(fn($item) => json_decode($item), $data)
- );
- }
-
- break;
-
- default:
- header('HTTP/1.1 405 Method Not Allowed');
- echo json_encode(['error' => 'M茅todo no permitido']);
- break;
- }
-} catch (PDOException $e) {
- echo json_encode([
- 'error' => $e->getMessage(),
- 'query' => $db->getLastQuery(),
- ]);
-}
+
+require_once "{$_SERVER['DOCUMENT_ROOT']}/class/c_login.php";
+header('Content-Type: application/json');
+
+if (!Login::is_logged()) {
+ header('HTTP/1.1 401 Unauthorized');
+ echo json_encode(['error' => 'No se ha iniciado sesi贸n']);
+ exit();
+}
+$user = Login::get_user();
+
+try {
+ switch ($_SERVER['REQUEST_METHOD']) {
+ case 'GET':
+ $facultad = $_GET['facultad'] ?? $user->facultad['facultad_id'] ?? null;
+ $porcentaje = $_GET['porcentaje'] ?? null;
+ $faltas = $_GET['faltas'] ?? null;
+
+ if (!isset($facultad) || !is_numeric($facultad)) {
+ $error = 'No se ha seleccionado una facultad';
+ } else if ((!isset($faltas) || !is_numeric($faltas)) && (!isset($porcentaje) || !is_numeric($porcentaje))) {
+ $error = 'Debe especificar las faltas o el porcentaje';
+ } else if (isset($faltas) && (!is_numeric($faltas) || $faltas <= 0)) {
+ $error = 'Las faltas deben ser un n煤mero mayor a 0';
+ } else if (isset($porcentaje) && (!is_numeric($porcentaje) || $porcentaje <= 0)) {
+ $error = 'El porcentaje debe ser un n煤mero mayor a 0';
+ } else if (isset($faltas) && isset($porcentaje)) {
+ $error = 'No se puede especificar las faltas y el porcentaje al mismo tiempo';
+ } else if (!isset($facultad) || !is_numeric($facultad)) {
+ $error = 'Debe especificar una facultad';
+ }
+
+ if (isset($error)) {
+ header('HTTP/1.1 400 Bad Request');
+ echo json_encode(['error' => $error]);
+ exit();
+ }
+ // Initialize the data array
+ $data = array();
+
+ // Check if 'profesor' or 'supervisor' is set and prepare the specific part of the SQL query accordingly.
+ if (isset($_GET['profesor']) || isset($_GET['supervisor'])) {
+
+ $condition = isset($_GET['profesor'])
+ ? "r.registro_fecha IS NULL AND NOT COALESCE(r.registro_justificada, FALSE)"
+ : "estado_supervisor_id = 2";
+
+ $filter = isset($faltas)
+ ? "afcp.faltas >= :faltas"
+ : "afcp.porcentaje >= :porcentaje";
+
+ // Prepare the SQL query with placeholders for parameters
+ $data = array_column($db->query(
+ "WITH fechas AS (
+ SELECT
+ h.horario_id,
+ fechas_clase(h.horario_id, true) AS registro_fecha_ideal,
+ hp.profesor_id
+ FROM horario h
+ JOIN horario_profesor hp USING (horario_id)
+ WHERE (h.PERIODO_ID, h.FACULTAD_ID) = (:periodo_id, :facultad_id) AND hp.profesor_id <> 0
+ ),
+ asistencia_faltas AS (
+ SELECT
+ f.profesor_id,
+ COUNT(1) AS total,
+ COUNT(1) FILTER (WHERE $condition AND f.registro_fecha_ideal <= current_date) AS faltas
+ FROM fechas f
+ LEFT JOIN registro r USING (registro_fecha_ideal, horario_id, profesor_id)
+ GROUP BY f.profesor_id
+ ),
+ asistencia_faltas_con_porcentaje AS (
+ SELECT
+ af.profesor_id,
+ af.faltas,
+ af.total,
+ CASE
+ WHEN af.total > 0 THEN ROUND((af.faltas::NUMERIC / af.total) * 100, 2)
+ ELSE NULL
+ END AS porcentaje
+ FROM asistencia_faltas af
+ WHERE af.faltas > 0
+ )
+ SELECT
+ json_build_object(
+ 'profesor', json_build_object(
+ 'profesor_nombre', p.profesor_nombre,
+ 'profesor_clave', p.profesor_clave,
+ 'profesor_correo', p.profesor_correo
+ ),
+ 'profesor_id', afcp.profesor_id,
+ 'faltas', afcp.faltas,
+ 'total', afcp.total,
+ 'porcentaje', afcp.porcentaje
+ ) AS result_json
+ FROM asistencia_faltas_con_porcentaje afcp
+ JOIN profesor p USING (profesor_id)
+ WHERE $filter
+ ORDER BY afcp.porcentaje DESC;
+ ",
+ [
+ 'periodo_id' => $user->periodo_id,
+ 'facultad_id' => $facultad,
+ ] + (isset($faltas)
+ ? ['faltas' => $faltas]
+ : ['porcentaje' => $porcentaje])
+ ), 'result_json');
+ } else {
+ // Send a 400 Bad Request header and an error message in JSON format
+ header('HTTP/1.1 400 Bad Request');
+ echo json_encode(['error' => 'Especifique si las faltas son de profesor o supervisor']);
+ exit();
+ }
+ if (empty($data)) {
+ header('HTTP/1.1 404 Not Found');
+ echo json_encode(['error' => 'No se encontraron faltas']);
+ } else {
+ echo json_encode(
+ array_map(fn($item) => json_decode($item), $data)
+ );
+ }
+
+ break;
+
+ default:
+ header('HTTP/1.1 405 Method Not Allowed');
+ echo json_encode(['error' => 'M茅todo no permitido']);
+ break;
+ }
+} catch (PDOException $e) {
+ echo json_encode([
+ 'error' => $e->getMessage(),
+ 'query' => $db->getLastQuery(),
+ ]);
+}
diff --git a/action/puesto.php b/action/puesto.php
index 0eb4fe2..73aa57f 100644
--- a/action/puesto.php
+++ b/action/puesto.php
@@ -1,113 +1,113 @@
- 'No se ha iniciado sesi贸n']);
- exit();
-}
-$user = Login::get_user();
-
-try {
- switch ($_SERVER['REQUEST_METHOD']) {
- case 'GET':
- // Fetch all puestos
- $facultad_id = $user->facultad['facultad_id'] ?? -1;
- $puestos = array_map(
- fn($p) => array(
- ...$p,
- 'materias' => $db->where('puesto_id', $p['puesto_id'])
- ->join('puesto_materia', 'puesto_materia.materia_id = materia.materia_id', 'LEFT')
- ->get(tableName: 'materia', columns: ['materia.materia_id', 'materia_nombre', 'clave_materia',]),
- 'encargado' => $db->where('puesto_id', $p['puesto_id'])
- ->join('puesto_usuario', 'puesto_usuario.usuario_id = usuario.usuario_id', 'LEFT')
- ->getOne('usuario', ['usuario.usuario_id', 'usuario_nombre', 'usuario_clave']),
- ),
- $db->orderBy('puesto.nombre', 'desc')
- ->where('facultad_id', $facultad_id)
- ->get(tableName: 'puesto', columns: 'puesto_id, nombre'),
- );
- echo json_encode($puestos);
- break;
-
- case 'POST':
- $raw_input = file_get_contents('php://input');
- $input_data = json_decode($raw_input, true);
-
- if (!$input_data || !isset($input_data['puesto_nombre'])) {
- header('HTTP/1.1 400 Bad Request');
- echo json_encode(['error' => 'Datos inv谩lidos']);
- exit();
- }
-
- $puesto = $db->insert('puesto', [
- 'nombre' => $input_data['puesto_nombre'],
- 'facultad_id' => $user->facultad['facultad_id'],
- ], ['puesto_id', 'nombre', 'facultad_id']);
-
- echo json_encode(
- array(
- ...$puesto,
- 'materias' => [],
- 'encargado' => null,
- ),
- );
- break;
-
- case 'PUT':
- $raw_input = file_get_contents('php://input');
- $input_data = json_decode($raw_input, true);
-
- if (!$input_data || !isset($input_data['puesto_id'], $input_data['materias'])) {
- header('HTTP/1.1 400 Bad Request');
- echo json_encode(['error' => 'Datos inv谩lidos']);
- exit();
- }
-
- $db->where('puesto_id', $input_data['puesto_id'])->delete('puesto_materia');
- $db->where('puesto_id', $input_data['puesto_id'])->delete('puesto_usuario');
-
- foreach ($input_data['materias'] as $materia_id) {
- $db->insert('puesto_materia', [
- 'puesto_id' => $input_data['puesto_id'],
- 'materia_id' => $materia_id,
- ]);
- }
-
- if (isset($input_data['usuario_id']))
- $db->insert('puesto_usuario', [
- 'puesto_id' => $input_data['puesto_id'],
- 'usuario_id' => $input_data['usuario_id'],
- ]);
-
- echo json_encode(['msg' => 'Puesto actualizado exitosamente']);
- break;
-
- case 'DELETE':
- $raw_input = file_get_contents('php://input');
- $input_data = json_decode($raw_input, true);
-
- if (!$input_data || !isset($input_data['puesto_id'])) {
- header('HTTP/1.1 400 Bad Request');
- echo json_encode(['error' => 'Datos inv谩lidos']);
- exit();
- }
-
- $db->where('puesto_id', $input_data['puesto_id'])->delete('puesto');
- echo json_encode(['msg' => 'Puesto eliminado exitosamente']);
- break;
-
- default:
- header('HTTP/1.1 405 Method Not Allowed');
- echo json_encode(['error' => 'M茅todo no permitido']);
- break;
- }
-} catch (PDOException $e) {
- echo json_encode([
- 'error' => $e->getMessage(),
- 'query' => $db->getLastQuery(),
- 'exception' => $e->getTraceAsString()
- ]);
+ 'No se ha iniciado sesi贸n']);
+ exit();
+}
+$user = Login::get_user();
+
+try {
+ switch ($_SERVER['REQUEST_METHOD']) {
+ case 'GET':
+ // Fetch all puestos
+ $facultad_id = $user->facultad['facultad_id'] ?? -1;
+ $puestos = array_map(
+ fn($p) => array(
+ ...$p,
+ 'materias' => $db->where('puesto_id', $p['puesto_id'])
+ ->join('puesto_materia', 'puesto_materia.materia_id = materia.materia_id', 'LEFT')
+ ->get(tableName: 'materia', columns: ['materia.materia_id', 'materia_nombre', 'clave_materia',]),
+ 'encargado' => $db->where('puesto_id', $p['puesto_id'])
+ ->join('puesto_usuario', 'puesto_usuario.usuario_id = usuario.usuario_id', 'LEFT')
+ ->getOne('usuario', ['usuario.usuario_id', 'usuario_nombre', 'usuario_clave']),
+ ),
+ $db->orderBy('puesto.nombre', 'desc')
+ ->where('facultad_id', $facultad_id)
+ ->get(tableName: 'puesto', columns: 'puesto_id, nombre'),
+ );
+ echo json_encode($puestos);
+ break;
+
+ case 'POST':
+ $raw_input = file_get_contents('php://input');
+ $input_data = json_decode($raw_input, true);
+
+ if (!$input_data || !isset($input_data['puesto_nombre'])) {
+ header('HTTP/1.1 400 Bad Request');
+ echo json_encode(['error' => 'Datos inv谩lidos']);
+ exit();
+ }
+
+ $puesto = $db->insert('puesto', [
+ 'nombre' => $input_data['puesto_nombre'],
+ 'facultad_id' => $user->facultad['facultad_id'],
+ ], ['puesto_id', 'nombre', 'facultad_id']);
+
+ echo json_encode(
+ array(
+ ...$puesto,
+ 'materias' => [],
+ 'encargado' => null,
+ ),
+ );
+ break;
+
+ case 'PUT':
+ $raw_input = file_get_contents('php://input');
+ $input_data = json_decode($raw_input, true);
+
+ if (!$input_data || !isset($input_data['puesto_id'], $input_data['materias'])) {
+ header('HTTP/1.1 400 Bad Request');
+ echo json_encode(['error' => 'Datos inv谩lidos']);
+ exit();
+ }
+
+ $db->where('puesto_id', $input_data['puesto_id'])->delete('puesto_materia');
+ $db->where('puesto_id', $input_data['puesto_id'])->delete('puesto_usuario');
+
+ foreach ($input_data['materias'] as $materia_id) {
+ $db->insert('puesto_materia', [
+ 'puesto_id' => $input_data['puesto_id'],
+ 'materia_id' => $materia_id,
+ ]);
+ }
+
+ if (isset($input_data['usuario_id']))
+ $db->insert('puesto_usuario', [
+ 'puesto_id' => $input_data['puesto_id'],
+ 'usuario_id' => $input_data['usuario_id'],
+ ]);
+
+ echo json_encode(['msg' => 'Puesto actualizado exitosamente']);
+ break;
+
+ case 'DELETE':
+ $raw_input = file_get_contents('php://input');
+ $input_data = json_decode($raw_input, true);
+
+ if (!$input_data || !isset($input_data['puesto_id'])) {
+ header('HTTP/1.1 400 Bad Request');
+ echo json_encode(['error' => 'Datos inv谩lidos']);
+ exit();
+ }
+
+ $db->where('puesto_id', $input_data['puesto_id'])->delete('puesto');
+ echo json_encode(['msg' => 'Puesto eliminado exitosamente']);
+ break;
+
+ default:
+ header('HTTP/1.1 405 Method Not Allowed');
+ echo json_encode(['error' => 'M茅todo no permitido']);
+ break;
+ }
+} catch (PDOException $e) {
+ echo json_encode([
+ 'error' => $e->getMessage(),
+ 'query' => $db->getLastQuery(),
+ 'exception' => $e->getTraceAsString()
+ ]);
}
\ No newline at end of file
diff --git a/action/reposicion_autoriza.php b/action/reposicion_autoriza.php
index 99610fa..4b44814 100644
--- a/action/reposicion_autoriza.php
+++ b/action/reposicion_autoriza.php
@@ -1,288 +1,288 @@
-querySingle('SELECT s.salon_id, s.salon_array FROM salon_view s where s.salon_id = :id_salon',
- [':id_salon' => $salon]
- );
- if($salon_rs["salon_id"] == "" || $salon_rs["salon_id"] == NULL){
- $salon_desc = "Pendiente";
- }else{
- $salon_json = json_decode($salon_rs["salon_array"], true);
- if($salon_json[0]== "UNIVERSIDAD LA SALLE"){
- unset($salon_json[0]);
- }
- $salon_desc = join(" / ",$salon_json);
- }
-}
-
-
-if($tipo ==1 || $tipo == 2){
- //--------------
- //Obtiene datos reposici贸n
- $reposicion_rs = $db->querySingle('SELECT h.materia, r.fecha_nueva, r.hora_nueva, r.fecha_clase, r.descripcion, h.horario_hora, h.facultad_id, h.facultad, f.clave_dependencia, r.motivo_cancelacion, ta.tipoaula_supervisor , ta.tipoaula_nombre
- from reposicion_solicitud r
- inner join horario_view h on h.horario_id = r.horario_id
- inner join facultad f on f.facultad_id = h.facultad_id
- inner join tipoaula ta on ta.tipoaula_id = r.tipoaula_id
- where r.reposicion_solicitud_id = :id_repo',
- [':id_repo' => $id_repo]
- );
-
- //Obtiene correos
- $correos_rs = $db->query('SELECT p.profesor_nombre, p.profesor_correo, u.usuario_nombre as jefe_nombre, u.usuario_correo as jefe_correo,
- coor.usuario_nombre as coordinador_nombre, coor.usuario_correo as coordinador_correo
- from reposicion_solicitud rs
- inner join profesor p on rs.profesor_id =p.profesor_id
- inner join usuario u on u.usuario_id = rs.usuario_id
- inner join horario_view hv on hv.horario_id = rs.horario_id
- inner join usuario coor on hv.facultad_id = coor.facultad_id and coor.rol_id = :rol_coord
- where rs.reposicion_solicitud_id = :id_repo',
- [':rol_coord' => COORDINADOR, ':id_repo' => $id_repo]
- );
- //print_r($correos_rs); exit();
-}else{
- //Obtiene datos asignaci贸n
- $reposicion_rs = $db->querySingle('SELECT r.fecha_nueva, r.hora_nueva, r.descripcion, f.facultad_id, f.facultad_nombre as facultad, f.clave_dependencia, r.motivo_cancelacion, ta.tipoaula_supervisor , ta.tipoaula_nombre, p.profesor_nombre
- from asignacion_solicitud r
- inner join usuario u on u.usuario_id = r.usuario_id
- inner join facultad f on f.facultad_id = u.facultad_id
- inner join tipoaula ta on ta.tipoaula_id = r.tipoaula_id
- inner join profesor p on p.profesor_id = r.profesor_id
- where r.asignacion_solicitud_id = :id_repo',
- [':id_repo' => $id_repo]
- );
-
- //Obtiene correos
- $correos_rs = $db->query('SELECT p.profesor_nombre, p.profesor_correo, NULL as jefe_nombre, NULL as jefe_correo,
- coor.usuario_nombre as coordinador_nombre, coor.usuario_correo as coordinador_correo
- from asignacion_solicitud rs
- inner join profesor p on rs.profesor_id =p.profesor_id
- inner join usuario u on u.usuario_id = rs.usuario_id
- inner join usuario coor on u.facultad_id = coor.facultad_id and coor.rol_id = :rol_coord
- where rs.asignacion_solicitud_id = :id_repo',
- [':rol_coord' => COORDINADOR, ':id_repo' => $id_repo]
- );
-
-}
-
-$prof_correos=array();
-$jefe_correos=[];
-$coord_correos=[];
-
-foreach($correos_rs as $correo){
- if( count($prof_correos)==0 && $correo["profesor_correo"]!=""){
- if( !isset($prof_correos["correo"]) || !in_array($correo["profesor_correo"], $prof_correos["correo"]) ){
- array_push($prof_correos, $correo["profesor_correo"]);
- }
- }
- if( count($jefe_correos)==0 && $correo["jefe_correo"]!=""){
- if(!isset($jefe_correos["correo"]) || !in_array($correo["jefe_correo"], $jefe_correos["correo"])){
- array_push($jefe_correos, $correo["jefe_correo"]);
- }
- }
- if( count($coord_correos)==0 && $correo["coordinador_correo"]!=""){
- if(!isset($coord_correos["correo"]) || !in_array($correo["coordinador_correo"], $coord_correos["correo"])){
- array_push($coord_correos, $correo["coordinador_correo"]);
- }
- }
-}
-
-$correosSup_rs = $db->query("SELECT DISTINCT sup.usuario_correo as supervisor_correo
- FROM horario_supervisor hs
- inner join usuario sup on sup.usuario_id =hs.usuario_id
- where :facultad = ANY(hs.facultad_id_array)",
- [':facultad'=>$reposicion_rs["facultad_id"]] );
-
-$sup_correos=[];
-foreach($correosSup_rs as $correo){
- array_push($sup_correos, $correo["supervisor_correo"]);
-}
-
-
-if($tipo ==1 || $tipo == 2){
- if($edo == 4){//cancelaci贸n
- $motivo = "";
- if(isset($_POST["motivo"]) && $_POST["motivo"] != "")
- $motivo = trim($_POST["motivo"]);
- $db->querySingle('SELECT fu_reposicion_cancela(:id, :motivo)',
- [':id' => $id_repo, ':motivo' => $motivo]
- );
- }else{
- if(!empty($salon)){
- $db->querySingle('SELECT fu_reposicion_solicitud(:id, NULL, NULL, NULL, :sal, :edo, NULL, NULL, NULL, NULL)',
- [':id' => $id_repo, ':sal' => $salon, ':edo' => $edo]
- );
- }else{
- $db->querySingle('SELECT fu_reposicion_solicitud(:id, NULL, NULL, NULL, NULL, :edo, NULL, NULL, NULL, NULL)',
- [':id' => $id_repo, ':edo' => $edo]
- );
- }
- }
-
- $fecha_clase = date('d/m/Y', strtotime($reposicion_rs["fecha_clase"]));
- $fecha_nueva = date('d/m/Y', strtotime($reposicion_rs["fecha_nueva"]));
- $hora_tmp = explode(":",$reposicion_rs["horario_hora"]);
- $hora_clase = $hora_tmp[0].":".$hora_tmp[1];
- $hora_tmp = explode(":",$reposicion_rs["hora_nueva"]);
- $hora_nueva = $hora_tmp[0].":".$hora_tmp[1];
-
- $asunto = "";
- $texto = "";
- $to = "";
- switch($edo){
- case 2://Correo a supervisor
- $asunto = "Reposici贸n nueva - ".$reposicion_rs["clave_dependencia"]." ".$reposicion_rs["facultad"];
- //crear plantilla
- $texto = "Se cre贸 una reposici贸n nueva para: ".$reposicion_rs["clave_dependencia"]." ".$reposicion_rs["facultad"]." .
";
- $texto .= "".mb_strtoupper($reposicion_rs["materia"])." del d铆a ".$fecha_clase." a las ".$hora_clase." hrs. se propone reponer el ".$fecha_nueva." a las ".$hora_nueva." hrs. ";
- if(!$reposicion_rs["tipoaula_supervisor"]){
- $texto .= " en el sal贸n: ".$salon_desc."
";
- }else{
- $texto .= " en un sal贸n de tipo: ".$reposicion_rs["tipoaula_nombre"]." ";
- }
- $texto .= "".$reposicion_rs["descripcion"]."
";
- $texto .= "Ingresa al sistema PAAD para autorizarla.
";
- $to = join(",", $sup_correos);
- $ok = 0;
- break;
- case 3://Correo a coordinador, profesor y jefe
- $asunto = "Reposici贸n autorizada - ".$reposicion_rs["materia"];
- $texto = "La resposici贸n de la clase de ".$reposicion_rs["materia"]." del d铆a ".$fecha_clase." a las ".$hora_clase." hrs. est谩 autorizada para realizarse el d铆a ".$fecha_nueva." a las ".$hora_nueva." hrs. en: $salon_desc
";
- $to = join(",", $coord_correos).",".join(",", $prof_correos).",".join(",", $jefe_correos);
- $ok = 0;
- $db->querySingle('SELECT fu_reposicion_solicitud_supervisor(:id, :sup)',
- [':id' => $id_repo, ':sup'=>$user->user["id"]]
- );
- break;
- case 4://Correo a coordinador, profesor y jefe
- $asunto = "Reposici贸n declinada - ".$reposicion_rs["materia"];
- $texto = "La resposici贸n de la clase de ".$reposicion_rs["materia"]." planeada para el d铆a ".$fecha_nueva." a las ".$hora_nueva." hrs. ha sido declinada por el siguiente motivo:
";
- $texto .= "".$motivo."
";
- $to = join(",", $coord_correos).",".join(",", $prof_correos).",".join(",", $jefe_correos);
- $ok = 1;
- $db->querySingle('SELECT fu_reposicion_solicitud_supervisor(:id, :sup)',
- [':id' => $id_repo, ':sup'=>$user->user["id"]]
- );
- break;
- }
-}else{
- if($edo == 4){//cancelaci贸n
- $motivo = "";
- if(isset($_POST["motivo"]) && $_POST["motivo"] != "")
- $motivo = trim($_POST["motivo"]);
- $db->querySingle('SELECT fu_asignacion_cancela(:id, :motivo)',
- [':id' => $id_repo, ':motivo' => $motivo]
- );
- }else{
- if(!empty($salon)){
- $db->querySingle('SELECT fu_asignacion_solicitud(:id, NULL, NULL, :sal, :edo, NULL, NULL, NULL, NULL, NULL)',
- [':id' => $id_repo, ':sal' => $salon, ':edo' => $edo]
- );
- }else{
- $db->querySingle('SELECT fu_asignacion_solicitud(:id, NULL, NULL, NULL, :edo, NULL, NULL, NULL, NULL, NULL)',
- [':id' => $id_repo, ':edo' => $edo]
- );
- }
- }
-
- $fecha_nueva = date('d/m/Y', strtotime($reposicion_rs["fecha_nueva"]));
- $hora_tmp = explode(":",$reposicion_rs["hora_nueva"]);
- $hora_nueva = $hora_tmp[0].":".$hora_tmp[1];
-
- $asunto = "";
- $texto = "";
- $to = "";
- switch($edo){
- case 2://Correo a supervisor
- $asunto = "Asignaci贸n nueva - ".$reposicion_rs["clave_dependencia"]." ".$reposicion_rs["facultad"];
- //crear plantilla
- $texto = "Se cre贸 una solicitud de asignaci贸n nueva para: ".$reposicion_rs["clave_dependencia"]." ".$reposicion_rs["facultad"]." .
";
- $texto .= "Se solicita el d铆a ".$fecha_nueva." a las ".$hora_nueva." hrs. para el profesor: ".$reposicion_rs["profesor_nombre"]." .";
- if(!$reposicion_rs["tipoaula_supervisor"]){
- $texto .= " en el sal贸n: ".$salon_desc."
";
- }else{
- $texto .= " en un sal贸n de tipo: ".$reposicion_rs["tipoaula_nombre"]." ";
- }
- $texto .= "".$reposicion_rs["descripcion"]."
";
- $texto .= "Ingresa al sistema PAAD para autorizarla.
";
- $to = join(",", $sup_correos);
- $ok = 0;
- break;
- case 3://Correo a coordinador, profesor y jefe
- $asunto = "Asignaci贸n autorizada - ".$reposicion_rs["profesor_nombre"];
- $texto = "La asignaci贸n de espacio para el profesor ".$reposicion_rs["profesor_nombre"]." est谩 autorizada para realizarse el d铆a ".$fecha_nueva." a las ".$hora_nueva." hrs. en: $salon_desc
";
- $to = join(",", $coord_correos).",".join(",", $prof_correos).",".join(",", $jefe_correos);
- $ok = 0;
- $db->querySingle('SELECT fu_asignacion_solicitud_supervisor(:id, :sup)',
- [':id' => $id_repo, ':sup'=>$user->user["id"]]
- );
- break;
- case 4://Correo a coordinador, profesor y jefe
- $asunto = "Asignaci贸n declinada - ".$reposicion_rs["profesor_nombre"];
- $texto = "La asignaci贸n de espacio para el profesor ".$reposicion_rs["profesor_nombre"]." planeada para el d铆a ".$fecha_nueva." a las ".$hora_nueva." hrs. ha sido declinada por el siguiente motivo:
";
- $texto .= "".$motivo."
";
- $to = join(",", $coord_correos).",".join(",", $prof_correos).",".join(",", $jefe_correos);
- $ok = 1;
- $db->querySingle('SELECT fu_asignacion_solicitud_supervisor(:id, :sup)',
- [':id' => $id_repo, ':sup'=>$user->user["id"]]
- );
- break;
- }
-}
-
-
-if($to!= "" && ENVIO_CORREOS){
- $texto = '
-
- '.$texto.'
- ';
-
- require_once('../include/phpmailer/PHPMailerAutoload.php');
- if($_ENV['DB_NAME'] == "paad_pruebas"){
- $asunto = "PRUEBAS-".$asunto;
- Mailer::enviarCorreo("alejandro.lara@lasalle.mx", $asunto, $texto, true);
- }else{
- Mailer::enviarCorreo($to, $asunto, $texto, true);
- }
-}
-
-/*
-$log->appendLog($_SESSION["usuario_id"], $_SESSION["usuario_nombre"]." ".$_SESSION["usuario_apellidos"], $desc_log);
-*/
-header("Location: ".$pag."?ok=".$ok);
-exit();
-?>
+querySingle('SELECT s.salon_id, s.salon_array FROM salon_view s where s.salon_id = :id_salon',
+ [':id_salon' => $salon]
+ );
+ if($salon_rs["salon_id"] == "" || $salon_rs["salon_id"] == NULL){
+ $salon_desc = "Pendiente";
+ }else{
+ $salon_json = json_decode($salon_rs["salon_array"], true);
+ if($salon_json[0]== "UNIVERSIDAD LA SALLE"){
+ unset($salon_json[0]);
+ }
+ $salon_desc = join(" / ",$salon_json);
+ }
+}
+
+
+if($tipo ==1 || $tipo == 2){
+ //--------------
+ //Obtiene datos reposici贸n
+ $reposicion_rs = $db->querySingle('SELECT h.materia, r.fecha_nueva, r.hora_nueva, r.fecha_clase, r.descripcion, h.horario_hora, h.facultad_id, h.facultad, f.clave_dependencia, r.motivo_cancelacion, ta.tipoaula_supervisor , ta.tipoaula_nombre
+ from reposicion_solicitud r
+ inner join horario_view h on h.horario_id = r.horario_id
+ inner join facultad f on f.facultad_id = h.facultad_id
+ inner join tipoaula ta on ta.tipoaula_id = r.tipoaula_id
+ where r.reposicion_solicitud_id = :id_repo',
+ [':id_repo' => $id_repo]
+ );
+
+ //Obtiene correos
+ $correos_rs = $db->query('SELECT p.profesor_nombre, p.profesor_correo, u.usuario_nombre as jefe_nombre, u.usuario_correo as jefe_correo,
+ coor.usuario_nombre as coordinador_nombre, coor.usuario_correo as coordinador_correo
+ from reposicion_solicitud rs
+ inner join profesor p on rs.profesor_id =p.profesor_id
+ inner join usuario u on u.usuario_id = rs.usuario_id
+ inner join horario_view hv on hv.horario_id = rs.horario_id
+ inner join usuario coor on hv.facultad_id = coor.facultad_id and coor.rol_id = :rol_coord
+ where rs.reposicion_solicitud_id = :id_repo',
+ [':rol_coord' => COORDINADOR, ':id_repo' => $id_repo]
+ );
+ //print_r($correos_rs); exit();
+}else{
+ //Obtiene datos asignaci贸n
+ $reposicion_rs = $db->querySingle('SELECT r.fecha_nueva, r.hora_nueva, r.descripcion, f.facultad_id, f.facultad_nombre as facultad, f.clave_dependencia, r.motivo_cancelacion, ta.tipoaula_supervisor , ta.tipoaula_nombre, p.profesor_nombre
+ from asignacion_solicitud r
+ inner join usuario u on u.usuario_id = r.usuario_id
+ inner join facultad f on f.facultad_id = u.facultad_id
+ inner join tipoaula ta on ta.tipoaula_id = r.tipoaula_id
+ inner join profesor p on p.profesor_id = r.profesor_id
+ where r.asignacion_solicitud_id = :id_repo',
+ [':id_repo' => $id_repo]
+ );
+
+ //Obtiene correos
+ $correos_rs = $db->query('SELECT p.profesor_nombre, p.profesor_correo, NULL as jefe_nombre, NULL as jefe_correo,
+ coor.usuario_nombre as coordinador_nombre, coor.usuario_correo as coordinador_correo
+ from asignacion_solicitud rs
+ inner join profesor p on rs.profesor_id =p.profesor_id
+ inner join usuario u on u.usuario_id = rs.usuario_id
+ inner join usuario coor on u.facultad_id = coor.facultad_id and coor.rol_id = :rol_coord
+ where rs.asignacion_solicitud_id = :id_repo',
+ [':rol_coord' => COORDINADOR, ':id_repo' => $id_repo]
+ );
+
+}
+
+$prof_correos=array();
+$jefe_correos=[];
+$coord_correos=[];
+
+foreach($correos_rs as $correo){
+ if( count($prof_correos)==0 && $correo["profesor_correo"]!=""){
+ if( !isset($prof_correos["correo"]) || !in_array($correo["profesor_correo"], $prof_correos["correo"]) ){
+ array_push($prof_correos, $correo["profesor_correo"]);
+ }
+ }
+ if( count($jefe_correos)==0 && $correo["jefe_correo"]!=""){
+ if(!isset($jefe_correos["correo"]) || !in_array($correo["jefe_correo"], $jefe_correos["correo"])){
+ array_push($jefe_correos, $correo["jefe_correo"]);
+ }
+ }
+ if( count($coord_correos)==0 && $correo["coordinador_correo"]!=""){
+ if(!isset($coord_correos["correo"]) || !in_array($correo["coordinador_correo"], $coord_correos["correo"])){
+ array_push($coord_correos, $correo["coordinador_correo"]);
+ }
+ }
+}
+
+$correosSup_rs = $db->query("SELECT DISTINCT sup.usuario_correo as supervisor_correo
+ FROM horario_supervisor hs
+ inner join usuario sup on sup.usuario_id =hs.usuario_id
+ where :facultad = ANY(hs.facultad_id_array)",
+ [':facultad'=>$reposicion_rs["facultad_id"]] );
+
+$sup_correos=[];
+foreach($correosSup_rs as $correo){
+ array_push($sup_correos, $correo["supervisor_correo"]);
+}
+
+
+if($tipo ==1 || $tipo == 2){
+ if($edo == 4){//cancelaci贸n
+ $motivo = "";
+ if(isset($_POST["motivo"]) && $_POST["motivo"] != "")
+ $motivo = trim($_POST["motivo"]);
+ $db->querySingle('SELECT fu_reposicion_cancela(:id, :motivo)',
+ [':id' => $id_repo, ':motivo' => $motivo]
+ );
+ }else{
+ if(!empty($salon)){
+ $db->querySingle('SELECT fu_reposicion_solicitud(:id, NULL, NULL, NULL, :sal, :edo, NULL, NULL, NULL, NULL)',
+ [':id' => $id_repo, ':sal' => $salon, ':edo' => $edo]
+ );
+ }else{
+ $db->querySingle('SELECT fu_reposicion_solicitud(:id, NULL, NULL, NULL, NULL, :edo, NULL, NULL, NULL, NULL)',
+ [':id' => $id_repo, ':edo' => $edo]
+ );
+ }
+ }
+
+ $fecha_clase = date('d/m/Y', strtotime($reposicion_rs["fecha_clase"]));
+ $fecha_nueva = date('d/m/Y', strtotime($reposicion_rs["fecha_nueva"]));
+ $hora_tmp = explode(":",$reposicion_rs["horario_hora"]);
+ $hora_clase = $hora_tmp[0].":".$hora_tmp[1];
+ $hora_tmp = explode(":",$reposicion_rs["hora_nueva"]);
+ $hora_nueva = $hora_tmp[0].":".$hora_tmp[1];
+
+ $asunto = "";
+ $texto = "";
+ $to = "";
+ switch($edo){
+ case 2://Correo a supervisor
+ $asunto = "Reposici贸n nueva - ".$reposicion_rs["clave_dependencia"]." ".$reposicion_rs["facultad"];
+ //crear plantilla
+ $texto = "Se cre贸 una reposici贸n nueva para: ".$reposicion_rs["clave_dependencia"]." ".$reposicion_rs["facultad"]." .
";
+ $texto .= "".mb_strtoupper($reposicion_rs["materia"])." del d铆a ".$fecha_clase." a las ".$hora_clase." hrs. se propone reponer el ".$fecha_nueva." a las ".$hora_nueva." hrs. ";
+ if(!$reposicion_rs["tipoaula_supervisor"]){
+ $texto .= " en el sal贸n: ".$salon_desc."
";
+ }else{
+ $texto .= " en un sal贸n de tipo: ".$reposicion_rs["tipoaula_nombre"]." ";
+ }
+ $texto .= "".$reposicion_rs["descripcion"]."
";
+ $texto .= "Ingresa al sistema PAAD para autorizarla.
";
+ $to = join(",", $sup_correos);
+ $ok = 0;
+ break;
+ case 3://Correo a coordinador, profesor y jefe
+ $asunto = "Reposici贸n autorizada - ".$reposicion_rs["materia"];
+ $texto = "La resposici贸n de la clase de ".$reposicion_rs["materia"]." del d铆a ".$fecha_clase." a las ".$hora_clase." hrs. est谩 autorizada para realizarse el d铆a ".$fecha_nueva." a las ".$hora_nueva." hrs. en: $salon_desc
";
+ $to = join(",", $coord_correos).",".join(",", $prof_correos).",".join(",", $jefe_correos);
+ $ok = 0;
+ $db->querySingle('SELECT fu_reposicion_solicitud_supervisor(:id, :sup)',
+ [':id' => $id_repo, ':sup'=>$user->user["id"]]
+ );
+ break;
+ case 4://Correo a coordinador, profesor y jefe
+ $asunto = "Reposici贸n declinada - ".$reposicion_rs["materia"];
+ $texto = "La resposici贸n de la clase de ".$reposicion_rs["materia"]." planeada para el d铆a ".$fecha_nueva." a las ".$hora_nueva." hrs. ha sido declinada por el siguiente motivo:
";
+ $texto .= "".$motivo."
";
+ $to = join(",", $coord_correos).",".join(",", $prof_correos).",".join(",", $jefe_correos);
+ $ok = 1;
+ $db->querySingle('SELECT fu_reposicion_solicitud_supervisor(:id, :sup)',
+ [':id' => $id_repo, ':sup'=>$user->user["id"]]
+ );
+ break;
+ }
+}else{
+ if($edo == 4){//cancelaci贸n
+ $motivo = "";
+ if(isset($_POST["motivo"]) && $_POST["motivo"] != "")
+ $motivo = trim($_POST["motivo"]);
+ $db->querySingle('SELECT fu_asignacion_cancela(:id, :motivo)',
+ [':id' => $id_repo, ':motivo' => $motivo]
+ );
+ }else{
+ if(!empty($salon)){
+ $db->querySingle('SELECT fu_asignacion_solicitud(:id, NULL, NULL, :sal, :edo, NULL, NULL, NULL, NULL, NULL)',
+ [':id' => $id_repo, ':sal' => $salon, ':edo' => $edo]
+ );
+ }else{
+ $db->querySingle('SELECT fu_asignacion_solicitud(:id, NULL, NULL, NULL, :edo, NULL, NULL, NULL, NULL, NULL)',
+ [':id' => $id_repo, ':edo' => $edo]
+ );
+ }
+ }
+
+ $fecha_nueva = date('d/m/Y', strtotime($reposicion_rs["fecha_nueva"]));
+ $hora_tmp = explode(":",$reposicion_rs["hora_nueva"]);
+ $hora_nueva = $hora_tmp[0].":".$hora_tmp[1];
+
+ $asunto = "";
+ $texto = "";
+ $to = "";
+ switch($edo){
+ case 2://Correo a supervisor
+ $asunto = "Asignaci贸n nueva - ".$reposicion_rs["clave_dependencia"]." ".$reposicion_rs["facultad"];
+ //crear plantilla
+ $texto = "Se cre贸 una solicitud de asignaci贸n nueva para: ".$reposicion_rs["clave_dependencia"]." ".$reposicion_rs["facultad"]." .
";
+ $texto .= "Se solicita el d铆a ".$fecha_nueva." a las ".$hora_nueva." hrs. para el profesor: ".$reposicion_rs["profesor_nombre"]." .";
+ if(!$reposicion_rs["tipoaula_supervisor"]){
+ $texto .= " en el sal贸n: ".$salon_desc."
";
+ }else{
+ $texto .= " en un sal贸n de tipo: ".$reposicion_rs["tipoaula_nombre"]." ";
+ }
+ $texto .= "".$reposicion_rs["descripcion"]."
";
+ $texto .= "Ingresa al sistema PAAD para autorizarla.
";
+ $to = join(",", $sup_correos);
+ $ok = 0;
+ break;
+ case 3://Correo a coordinador, profesor y jefe
+ $asunto = "Asignaci贸n autorizada - ".$reposicion_rs["profesor_nombre"];
+ $texto = "La asignaci贸n de espacio para el profesor ".$reposicion_rs["profesor_nombre"]." est谩 autorizada para realizarse el d铆a ".$fecha_nueva." a las ".$hora_nueva." hrs. en: $salon_desc
";
+ $to = join(",", $coord_correos).",".join(",", $prof_correos).",".join(",", $jefe_correos);
+ $ok = 0;
+ $db->querySingle('SELECT fu_asignacion_solicitud_supervisor(:id, :sup)',
+ [':id' => $id_repo, ':sup'=>$user->user["id"]]
+ );
+ break;
+ case 4://Correo a coordinador, profesor y jefe
+ $asunto = "Asignaci贸n declinada - ".$reposicion_rs["profesor_nombre"];
+ $texto = "La asignaci贸n de espacio para el profesor ".$reposicion_rs["profesor_nombre"]." planeada para el d铆a ".$fecha_nueva." a las ".$hora_nueva." hrs. ha sido declinada por el siguiente motivo:
";
+ $texto .= "".$motivo."
";
+ $to = join(",", $coord_correos).",".join(",", $prof_correos).",".join(",", $jefe_correos);
+ $ok = 1;
+ $db->querySingle('SELECT fu_asignacion_solicitud_supervisor(:id, :sup)',
+ [':id' => $id_repo, ':sup'=>$user->user["id"]]
+ );
+ break;
+ }
+}
+
+
+if($to!= "" && ENVIO_CORREOS){
+ $texto = '
+
+ '.$texto.'
+ ';
+
+ require_once('../include/phpmailer/PHPMailerAutoload.php');
+ if($_ENV['DB_NAME'] == "paad_pruebas"){
+ $asunto = "PRUEBAS-".$asunto;
+ Mailer::enviarCorreo("alejandro.lara@lasalle.mx", $asunto, $texto, true);
+ }else{
+ Mailer::enviarCorreo($to, $asunto, $texto, true);
+ }
+}
+
+/*
+$log->appendLog($_SESSION["usuario_id"], $_SESSION["usuario_nombre"]." ".$_SESSION["usuario_apellidos"], $desc_log);
+*/
+header("Location: ".$pag."?ok=".$ok);
+exit();
+?>
diff --git a/action/reposicion_delete.php b/action/reposicion_delete.php
index 8c69187..5ab1844 100644
--- a/action/reposicion_delete.php
+++ b/action/reposicion_delete.php
@@ -1,33 +1,33 @@
-user["id"];
-
- try{
- $db->query('SELECT * from fd_reposicion_solicitud(:id, :creador)', [":id"=> $id, ":creador"=>$creador]);
- $return["ok"] = "La reposici贸n se borr贸 correctamente";
-
- }catch(Exception $e){
- $return["error"] = "Ocurri贸 un error al borrar la reposici贸n.";
- }
-
-
-}
-echo json_encode($return);
-?>
+user["id"];
+
+ try{
+ $db->query('SELECT * from fd_reposicion_solicitud(:id, :creador)', [":id"=> $id, ":creador"=>$creador]);
+ $return["ok"] = "La reposici贸n se borr贸 correctamente";
+
+ }catch(Exception $e){
+ $return["error"] = "Ocurri贸 un error al borrar la reposici贸n.";
+ }
+
+
+}
+echo json_encode($return);
+?>
diff --git a/action/reposicion_insert.php b/action/reposicion_insert.php
index 895c9ff..1ba5b59 100644
--- a/action/reposicion_insert.php
+++ b/action/reposicion_insert.php
@@ -1,208 +1,208 @@
-access();
-
-$duracion_id = filter_input(INPUT_POST, "duracion", FILTER_SANITIZE_NUMBER_INT);//Id reposicion
-$bloque = filter_input(INPUT_POST, "bloque", FILTER_SANITIZE_NUMBER_INT);//
-$ciclo = filter_input(INPUT_POST, "ciclo", FILTER_SANITIZE_NUMBER_INT);//
-$fecha_falta = trim(htmlspecialchars($_POST["fecha_falta"], ENT_QUOTES, "UTF-8"));//Reposicion
-$fecha = trim(htmlspecialchars($_POST["fecha_inicial"], ENT_QUOTES, "UTF-8"));//Reposicion
-$fecha_cambio = trim(htmlspecialchars($_POST["fecha_cambio"], ENT_QUOTES, "UTF-8"));//Cambio sal贸n
-$hora_ini = filter_input(INPUT_POST, "hora_ini", FILTER_SANITIZE_NUMBER_INT);//limpia texto hora reposicion
-$min_ini = filter_input(INPUT_POST, "min_ini", FILTER_SANITIZE_NUMBER_INT);//limpia texto
-$hor = filter_input(INPUT_POST, "horario", FILTER_SANITIZE_NUMBER_INT);//limpia texto
-$alumnos = filter_input(INPUT_POST, "alumnos", FILTER_SANITIZE_NUMBER_INT);//limpia texto
-$tipo = filter_input(INPUT_POST, "tipo", FILTER_SANITIZE_NUMBER_INT);//1 Repo , 0 Cambio
-$aula = filter_input(INPUT_POST, "aula", FILTER_SANITIZE_NUMBER_INT);//1 regular , 2 sala computo, 3 otro facultad
-$salon = NULL;
-
-if(!$user->jefe_carrera){//coordinador
- if(isset($_POST["salon"]) && $_POST["salon"] != "")
- $salon = filter_input(INPUT_POST, "dlSalon", FILTER_SANITIZE_NUMBER_INT);//1 regular , 2 sala computo, 3 otro facultad
-}
-
-if(empty($_POST["prof"]))
- $prof = $user->user["id"];
-else
- $prof = filter_input(INPUT_POST, "prof", FILTER_SANITIZE_NUMBER_INT);//limpia texto
-
-$comentario = trim(htmlspecialchars($_POST["comentario"], ENT_QUOTES, "UTF-8"));//limpia texto
-
-
-
-$duracion_rs = $db->querySingle("select * from duracion where duracion_id = :id", [":id"=>$duracion_id]);
-$duracion_tiempo = $duracion_rs["duracion_interval"];
-
-//-- Obtiene datos de horario regular de clase
-$horario_rs = $db->querySingle('SELECT * from horario_view where horario_id = :hor',
- [':hor' => $hor]
- );
-
-$materia = $horario_rs["materia_id"];
-$dia = $horario_rs["horario_dia"];
-
-$hora = $hora_ini.":".$min_ini.":00";
-
-
-if($tipo == 1){//Reposici贸n
- $fecha_new = DateTime::createFromFormat('d/m/Y', $fecha)->format('Y-m-d')." ".$hora;
- $fecha_fin_new = date("Y-m-d", strtotime($fecha_new))." ".$duracion_tiempo;
- $dia_new = date('w', strtotime($fecha_new));
-
- $fecha_falta = DateTime::createFromFormat('d/m/Y', $fecha_falta)->format('Y-m-d');
- $dia_falta = date('w', strtotime($fecha_falta));
-}else{
- $fecha_cambio = DateTime::createFromFormat('d/m/Y', $fecha_cambio)->format('Y-m-d');
- $dia_falta = date('w', strtotime($fecha_cambio));
-}
-
-
-//Valida que tenga clase en la fecha de falta
-if(intval($dia) != intval($dia_falta)){
- header("Location:".$pag."?error=11");
- /*print_r($_POST);
- echo 'SELECT * from horario_view where horario_id = '.$hor;
- echo intval($dia)." != ".intval($dia_falta);*/
- exit();
-}
-
-//Obtiene materia
-$materia_rs = $db->querySingle('SELECT materia_nombre from materia where materia_id = :mat',[':mat' => $materia]);
-
-//Obtiene correo
-$correos_rs = $db->querySingle('SELECT coor.usuario_correo, coor.usuario_nombre from usuario coor where rol_id = :rol_coord and facultad_id = (
- select coalesce(facultad_id,0) from usuario u where u.usuario_id = :id_usr) and coor.usuario_correo != \'\'',[':rol_coord' => COORDINADOR, ':id_usr' => $user->user["id"]]
-);
-if( count($correos_rs) > 0 ){
- $to = $correos_rs["usuario_correo"];
-}
-
-if($tipo == 1){//Reposici贸n
- // Valida que grupo no tenga clases
- /*$result = validaConflictoHoras($pdo, $gpo, $dia_new, $hora, $materia, "-", $fecha_new, $fecha_fin_new, $duracion);
- if($result != ""){//error
- //echo $result;
- header("Location:".$pag."?error=7");
- exit();
- }
- */
- //Valida que profesor no este en 2 reposiciones al mismo tiempo en la fecha nueva
-
- $traslape = $db->querySingle('SELECT * from traslape_profesor_reposicion(:prof, :fecha, :hora, :dur)',
- [':prof' => $prof, ':fecha'=>DateTime::createFromFormat('d/m/Y', $fecha)->format('Y-m-d'), ':hora'=>$hora, ':dur'=>$duracion_tiempo]
- )["traslape_profesor_reposicion"];
- if($traslape){
- //print_r($_POST);
- //echo "SELECT * from traslape_profesor_reposicion($prof,'".DateTime::createFromFormat('d/m/Y', $fecha)->format('Y-m-d')."' , '$hora', $duracion)";
-
- header("Location:".$pag."?error=9");
- exit();
- }
-
- try{
- if($user->jefe_carrera){//jefe
- $db->query('SELECT * from fi_reposicion_solicitud(:f_falta, :f_nueva, :hora_nueva, :hor, :prof, 1, :desc, :alumnos, true, :aula, :duracion, :usr, :bloque, :ciclo)',
- [':f_falta' => $fecha_falta, ':f_nueva' => $fecha_new, ':hora_nueva' => $hora, ':hor' => $hor,
- ':prof' => $prof, ':desc' => $comentario, ':alumnos' => $alumnos, ':aula' => $aula, ':duracion' => $duracion_tiempo, ':usr'=>$user->user["id"],
- ':bloque' => $bloque, ':ciclo' => $ciclo
- ]
- );
-
- }else{//coordinador
- echo 'SELECT * from fi_reposicion_solicitud(:f_falta, :f_nueva, :hora_nueva, :hor, :prof, 2, :desc, :alumnos, true, :aula, :duracion, :usr, :bloque, :ciclo, '.$salon.')';
- $db->query('SELECT * from fi_reposicion_solicitud(:f_falta, :f_nueva, :hora_nueva, :hor, :prof, 2, :desc, :alumnos, true, :aula, :duracion, :usr, :bloque, :ciclo, :salon)',
- [':f_falta' => $fecha_falta, ':f_nueva' => $fecha_new, ':hora_nueva' => $hora, ':hor' => $hor,
- ':prof' => $prof, ':desc' => $comentario, ':alumnos' => $alumnos, ':aula' => $aula, ':duracion' => $duracion_tiempo, ':usr'=>$user->user["id"],
- ':bloque' => $bloque, ':ciclo' => $ciclo, ':salon'=>$salon
- ]
- );
- }
- }catch(Exception $e){
-
- echo "ERROR Reposici贸n ".$e->getMessage();
- //header("Location: ".$pag."?error=1");
- exit();
- }
- $fecha_clase = date('d/m/Y', strtotime($fecha_falta));
- $fecha_nueva = date('d/m/Y', strtotime($fecha_new));
- $texto = "Se cre贸 una reposici贸n nueva.
";
- $texto .= "".mb_strtoupper($materia_rs["materia_nombre"])." del d铆a ".$fecha_clase." a las ".$horario_rs["horario_hora"]." hrs. se propone reponer el ".$fecha_nueva." a las ".$hora." hrs. ";
- $texto .= "
Ingresa al sistema PAAD para autorizarla.
";
-
-/*
- $log = new LogActividad();
- $desc_log = "Inserta reposici贸n nueva ID[".$rs["fi_reposicion"]."] Fechas[".$fecha_falta.">".$fecha_new."] Periodo[".$_SESSION["periodo_id"]."] Materia[".$materia."] Profesor[".$prof."] Salon[".$salon."] Horario[".$hor."] Alumnos[".$alumnos."]";
- $log->appendLog($_SESSION["usuario_id"], $_SESSION["usuario_nombre"]." ".$_SESSION["usuario_apellidos"], $desc_log);*/
-
-
-}else{//Cambio sal贸n / hora
-
- try{
- if($user->jefe_carrera){//jefe
- $db->query('SELECT * from fi_reposicion_solicitud(:f_falta, :f_nueva, :hora_nueva, :hor, :prof, 1, :desc, :alumnos, false, :aula, :duracion, :usr, :bloque, :ciclo)',
- [':f_falta' => $fecha_cambio, ':f_nueva' => $fecha_cambio, ':hora_nueva' => $hora, ':hor' => $hor,
- ':prof' => $prof, ':desc' => $comentario, ':alumnos' => $alumnos, ':aula' => $aula, ':duracion' => $duracion_tiempo, ':usr'=>$user->user["id"],
- ':bloque' => $bloque, ':ciclo' => $ciclo
- ]
- );
- }else{//coordinador
- $db->query('SELECT * from fi_reposicion_solicitud(:f_falta, :f_nueva, :hora_nueva, :hor, :prof, 2, :desc, :alumnos, false, :aula, :duracion, :usr, :bloque, :ciclo, :salon)',
- [':f_falta' => $fecha_cambio, ':f_nueva' => $fecha_cambio, ':hora_nueva' => $hora, ':hor' => $hor,
- ':prof' => $prof, ':desc' => $comentario, ':alumnos' => $alumnos, ':aula' => $aula, ':duracion' => $duracion_tiempo, ':usr'=>$user->user["id"],
- ':bloque' => $bloque, ':ciclo' => $ciclo, ':salon'=>$salon
- ]
- );
- }
- }catch(Exception $e){
- echo "ERROR Cambio ".$e->getMessage();
- //header("Location: ".$pag."?error=1");
- exit();
- }
- $texto = "Se cre贸 un cambio de sal贸n nuevo.
";
- $texto .= "".mb_strtoupper($materia_rs["materia_nombre"])." del d铆a ".$fecha_falta." a las ".$hora." hrs. se propone reponer el ".$fecha_nueva." a las ".$hora." hrs. ";
- $texto .= "
Ingresa al sistema PAAD para autorizarlo.
";
-
- /*
- $log = new LogActividad();
- $desc_log = "Inserta reposici贸n nueva ID[".$rs["fi_reposicion"]."] Fechas[".$fecha_cambio.">".$fecha_cambio_nueva."] Periodo[".$_SESSION["periodo_id"]."] Materia[".$materia."] Profesor[".$prof."] Salon[".$salon."] Horario[".$hor."] Alumnos[".$alumnos."]";
- $log->appendLog($_SESSION["usuario_id"], $_SESSION["usuario_nombre"]." ".$_SESSION["usuario_apellidos"], $desc_log);
- */
-
-}
-
-if($to!= "" && ENVIO_CORREOS){
- $asunto = "Reposici贸n nueva - solicitud";
- //crear plantilla
- $texto = '
-
- '.$texto.'
- ';
-
- require_once('../include/phpmailer/PHPMailerAutoload.php');
- if($_ENV['DB_NAME'] == "paad_pruebas"){
- $asunto = "PRUEBAS-".$asunto;
- Mailer::enviarCorreo("alejandro.lara@lasalle.mx", $asunto, $texto, true);
- }else{
- Mailer::enviarCorreo($to, $asunto, $texto, true);
- }
-
-}
-
-header("Location: ".$pag."?ok=0");
-exit();
-?>
+access();
+
+$duracion_id = filter_input(INPUT_POST, "duracion", FILTER_SANITIZE_NUMBER_INT);//Id reposicion
+$bloque = filter_input(INPUT_POST, "bloque", FILTER_SANITIZE_NUMBER_INT);//
+$ciclo = filter_input(INPUT_POST, "ciclo", FILTER_SANITIZE_NUMBER_INT);//
+$fecha_falta = trim(htmlspecialchars($_POST["fecha_falta"], ENT_QUOTES, "UTF-8"));//Reposicion
+$fecha = trim(htmlspecialchars($_POST["fecha_inicial"], ENT_QUOTES, "UTF-8"));//Reposicion
+$fecha_cambio = trim(htmlspecialchars($_POST["fecha_cambio"], ENT_QUOTES, "UTF-8"));//Cambio sal贸n
+$hora_ini = filter_input(INPUT_POST, "hora_ini", FILTER_SANITIZE_NUMBER_INT);//limpia texto hora reposicion
+$min_ini = filter_input(INPUT_POST, "min_ini", FILTER_SANITIZE_NUMBER_INT);//limpia texto
+$hor = filter_input(INPUT_POST, "horario", FILTER_SANITIZE_NUMBER_INT);//limpia texto
+$alumnos = filter_input(INPUT_POST, "alumnos", FILTER_SANITIZE_NUMBER_INT);//limpia texto
+$tipo = filter_input(INPUT_POST, "tipo", FILTER_SANITIZE_NUMBER_INT);//1 Repo , 0 Cambio
+$aula = filter_input(INPUT_POST, "aula", FILTER_SANITIZE_NUMBER_INT);//1 regular , 2 sala computo, 3 otro facultad
+$salon = NULL;
+
+if(!$user->jefe_carrera){//coordinador
+ if(isset($_POST["salon"]) && $_POST["salon"] != "")
+ $salon = filter_input(INPUT_POST, "dlSalon", FILTER_SANITIZE_NUMBER_INT);//1 regular , 2 sala computo, 3 otro facultad
+}
+
+if(empty($_POST["prof"]))
+ $prof = $user->user["id"];
+else
+ $prof = filter_input(INPUT_POST, "prof", FILTER_SANITIZE_NUMBER_INT);//limpia texto
+
+$comentario = trim(htmlspecialchars($_POST["comentario"], ENT_QUOTES, "UTF-8"));//limpia texto
+
+
+
+$duracion_rs = $db->querySingle("select * from duracion where duracion_id = :id", [":id"=>$duracion_id]);
+$duracion_tiempo = $duracion_rs["duracion_interval"];
+
+//-- Obtiene datos de horario regular de clase
+$horario_rs = $db->querySingle('SELECT * from horario_view where horario_id = :hor',
+ [':hor' => $hor]
+ );
+
+$materia = $horario_rs["materia_id"];
+$dia = $horario_rs["horario_dia"];
+
+$hora = $hora_ini.":".$min_ini.":00";
+
+
+if($tipo == 1){//Reposici贸n
+ $fecha_new = DateTime::createFromFormat('d/m/Y', $fecha)->format('Y-m-d')." ".$hora;
+ $fecha_fin_new = date("Y-m-d", strtotime($fecha_new))." ".$duracion_tiempo;
+ $dia_new = date('w', strtotime($fecha_new));
+
+ $fecha_falta = DateTime::createFromFormat('d/m/Y', $fecha_falta)->format('Y-m-d');
+ $dia_falta = date('w', strtotime($fecha_falta));
+}else{
+ $fecha_cambio = DateTime::createFromFormat('d/m/Y', $fecha_cambio)->format('Y-m-d');
+ $dia_falta = date('w', strtotime($fecha_cambio));
+}
+
+
+//Valida que tenga clase en la fecha de falta
+if(intval($dia) != intval($dia_falta)){
+ header("Location:".$pag."?error=11");
+ /*print_r($_POST);
+ echo 'SELECT * from horario_view where horario_id = '.$hor;
+ echo intval($dia)." != ".intval($dia_falta);*/
+ exit();
+}
+
+//Obtiene materia
+$materia_rs = $db->querySingle('SELECT materia_nombre from materia where materia_id = :mat',[':mat' => $materia]);
+
+//Obtiene correo
+$correos_rs = $db->querySingle('SELECT coor.usuario_correo, coor.usuario_nombre from usuario coor where rol_id = :rol_coord and facultad_id = (
+ select coalesce(facultad_id,0) from usuario u where u.usuario_id = :id_usr) and coor.usuario_correo != \'\'',[':rol_coord' => COORDINADOR, ':id_usr' => $user->user["id"]]
+);
+if( count($correos_rs) > 0 ){
+ $to = $correos_rs["usuario_correo"];
+}
+
+if($tipo == 1){//Reposici贸n
+ // Valida que grupo no tenga clases
+ /*$result = validaConflictoHoras($pdo, $gpo, $dia_new, $hora, $materia, "-", $fecha_new, $fecha_fin_new, $duracion);
+ if($result != ""){//error
+ //echo $result;
+ header("Location:".$pag."?error=7");
+ exit();
+ }
+ */
+ //Valida que profesor no este en 2 reposiciones al mismo tiempo en la fecha nueva
+
+ $traslape = $db->querySingle('SELECT * from traslape_profesor_reposicion(:prof, :fecha, :hora, :dur)',
+ [':prof' => $prof, ':fecha'=>DateTime::createFromFormat('d/m/Y', $fecha)->format('Y-m-d'), ':hora'=>$hora, ':dur'=>$duracion_tiempo]
+ )["traslape_profesor_reposicion"];
+ if($traslape){
+ //print_r($_POST);
+ //echo "SELECT * from traslape_profesor_reposicion($prof,'".DateTime::createFromFormat('d/m/Y', $fecha)->format('Y-m-d')."' , '$hora', $duracion)";
+
+ header("Location:".$pag."?error=9");
+ exit();
+ }
+
+ try{
+ if($user->jefe_carrera){//jefe
+ $db->query('SELECT * from fi_reposicion_solicitud(:f_falta, :f_nueva, :hora_nueva, :hor, :prof, 1, :desc, :alumnos, true, :aula, :duracion, :usr, :bloque, :ciclo)',
+ [':f_falta' => $fecha_falta, ':f_nueva' => $fecha_new, ':hora_nueva' => $hora, ':hor' => $hor,
+ ':prof' => $prof, ':desc' => $comentario, ':alumnos' => $alumnos, ':aula' => $aula, ':duracion' => $duracion_tiempo, ':usr'=>$user->user["id"],
+ ':bloque' => $bloque, ':ciclo' => $ciclo
+ ]
+ );
+
+ }else{//coordinador
+ echo 'SELECT * from fi_reposicion_solicitud(:f_falta, :f_nueva, :hora_nueva, :hor, :prof, 2, :desc, :alumnos, true, :aula, :duracion, :usr, :bloque, :ciclo, '.$salon.')';
+ $db->query('SELECT * from fi_reposicion_solicitud(:f_falta, :f_nueva, :hora_nueva, :hor, :prof, 2, :desc, :alumnos, true, :aula, :duracion, :usr, :bloque, :ciclo, :salon)',
+ [':f_falta' => $fecha_falta, ':f_nueva' => $fecha_new, ':hora_nueva' => $hora, ':hor' => $hor,
+ ':prof' => $prof, ':desc' => $comentario, ':alumnos' => $alumnos, ':aula' => $aula, ':duracion' => $duracion_tiempo, ':usr'=>$user->user["id"],
+ ':bloque' => $bloque, ':ciclo' => $ciclo, ':salon'=>$salon
+ ]
+ );
+ }
+ }catch(Exception $e){
+
+ echo "ERROR Reposici贸n ".$e->getMessage();
+ //header("Location: ".$pag."?error=1");
+ exit();
+ }
+ $fecha_clase = date('d/m/Y', strtotime($fecha_falta));
+ $fecha_nueva = date('d/m/Y', strtotime($fecha_new));
+ $texto = "Se cre贸 una reposici贸n nueva.
";
+ $texto .= "".mb_strtoupper($materia_rs["materia_nombre"])." del d铆a ".$fecha_clase." a las ".$horario_rs["horario_hora"]." hrs. se propone reponer el ".$fecha_nueva." a las ".$hora." hrs. ";
+ $texto .= "
Ingresa al sistema PAAD para autorizarla.
";
+
+/*
+ $log = new LogActividad();
+ $desc_log = "Inserta reposici贸n nueva ID[".$rs["fi_reposicion"]."] Fechas[".$fecha_falta.">".$fecha_new."] Periodo[".$_SESSION["periodo_id"]."] Materia[".$materia."] Profesor[".$prof."] Salon[".$salon."] Horario[".$hor."] Alumnos[".$alumnos."]";
+ $log->appendLog($_SESSION["usuario_id"], $_SESSION["usuario_nombre"]." ".$_SESSION["usuario_apellidos"], $desc_log);*/
+
+
+}else{//Cambio sal贸n / hora
+
+ try{
+ if($user->jefe_carrera){//jefe
+ $db->query('SELECT * from fi_reposicion_solicitud(:f_falta, :f_nueva, :hora_nueva, :hor, :prof, 1, :desc, :alumnos, false, :aula, :duracion, :usr, :bloque, :ciclo)',
+ [':f_falta' => $fecha_cambio, ':f_nueva' => $fecha_cambio, ':hora_nueva' => $hora, ':hor' => $hor,
+ ':prof' => $prof, ':desc' => $comentario, ':alumnos' => $alumnos, ':aula' => $aula, ':duracion' => $duracion_tiempo, ':usr'=>$user->user["id"],
+ ':bloque' => $bloque, ':ciclo' => $ciclo
+ ]
+ );
+ }else{//coordinador
+ $db->query('SELECT * from fi_reposicion_solicitud(:f_falta, :f_nueva, :hora_nueva, :hor, :prof, 2, :desc, :alumnos, false, :aula, :duracion, :usr, :bloque, :ciclo, :salon)',
+ [':f_falta' => $fecha_cambio, ':f_nueva' => $fecha_cambio, ':hora_nueva' => $hora, ':hor' => $hor,
+ ':prof' => $prof, ':desc' => $comentario, ':alumnos' => $alumnos, ':aula' => $aula, ':duracion' => $duracion_tiempo, ':usr'=>$user->user["id"],
+ ':bloque' => $bloque, ':ciclo' => $ciclo, ':salon'=>$salon
+ ]
+ );
+ }
+ }catch(Exception $e){
+ echo "ERROR Cambio ".$e->getMessage();
+ //header("Location: ".$pag."?error=1");
+ exit();
+ }
+ $texto = "Se cre贸 un cambio de sal贸n nuevo.
";
+ $texto .= "".mb_strtoupper($materia_rs["materia_nombre"])." del d铆a ".$fecha_falta." a las ".$hora." hrs. se propone reponer el ".$fecha_nueva." a las ".$hora." hrs. ";
+ $texto .= "
Ingresa al sistema PAAD para autorizarlo.
";
+
+ /*
+ $log = new LogActividad();
+ $desc_log = "Inserta reposici贸n nueva ID[".$rs["fi_reposicion"]."] Fechas[".$fecha_cambio.">".$fecha_cambio_nueva."] Periodo[".$_SESSION["periodo_id"]."] Materia[".$materia."] Profesor[".$prof."] Salon[".$salon."] Horario[".$hor."] Alumnos[".$alumnos."]";
+ $log->appendLog($_SESSION["usuario_id"], $_SESSION["usuario_nombre"]." ".$_SESSION["usuario_apellidos"], $desc_log);
+ */
+
+}
+
+if($to!= "" && ENVIO_CORREOS){
+ $asunto = "Reposici贸n nueva - solicitud";
+ //crear plantilla
+ $texto = '
+
+ '.$texto.'
+ ';
+
+ require_once('../include/phpmailer/PHPMailerAutoload.php');
+ if($_ENV['DB_NAME'] == "paad_pruebas"){
+ $asunto = "PRUEBAS-".$asunto;
+ Mailer::enviarCorreo("alejandro.lara@lasalle.mx", $asunto, $texto, true);
+ }else{
+ Mailer::enviarCorreo($to, $asunto, $texto, true);
+ }
+
+}
+
+header("Location: ".$pag."?ok=0");
+exit();
+?>
diff --git a/action/reposicion_profesor_materias.php b/action/reposicion_profesor_materias.php
index 12832bf..c85a188 100644
--- a/action/reposicion_profesor_materias.php
+++ b/action/reposicion_profesor_materias.php
@@ -1,49 +1,49 @@
-tieneAcceso()){
- $return["error"] = "Error! No tienes permisos para realizar esta acci贸n.";
-}else*/ if(!isset($_POST["id"])){
- $return["error"] = "Error! No se recibi贸 la informaci贸n del profesor.";
-}else{
- $id = filter_input(INPUT_POST, "id", FILTER_SANITIZE_NUMBER_INT);//limpia texto
-
- try{
- if($user->jefe_carrera){
- $rs = $db->query('SELECT * FROM fs_materiasprofesor(:id, :jefe)', [':id' => $id, ':jefe'=>$user->user["id"]] );
- }else{
- $rs = $db->query('SELECT * FROM fs_materiasprofesor(:id, NULL)', [':id' => $id] );
- }
-
- }catch(Exception $e){
- $return["error"] = "Ocurri贸 un error al leer los datos de las materias.";
- echo json_encode($return);
- }
-
- $mat_arr = array();
- foreach($rs as $m){
- $mat_arr[] = array("horario_id"=>$m["horario_id"], "horario_dia"=>$m["horario_dia"],
- "horario_hora"=>substr($m["horario_hora"], 0, 2), "horario_min"=>substr($m["horario_hora"], 3, 2),
- "materia_nombre"=>$m["materia_nombre"].' - '.$m["horario_dia_nombre"]." ".substr($m["horario_hora"], 0, -3),
- "grupo"=>$m["horario_grupo"], "duracion" => $m["duracion"]
- );
- }
-
- $return["materias"] = $mat_arr;
-
-}
-echo json_encode($return);
-?>
+tieneAcceso()){
+ $return["error"] = "Error! No tienes permisos para realizar esta acci贸n.";
+}else*/ if(!isset($_POST["id"])){
+ $return["error"] = "Error! No se recibi贸 la informaci贸n del profesor.";
+}else{
+ $id = filter_input(INPUT_POST, "id", FILTER_SANITIZE_NUMBER_INT);//limpia texto
+
+ try{
+ if($user->jefe_carrera){
+ $rs = $db->query('SELECT * FROM fs_materiasprofesor(:id, :jefe)', [':id' => $id, ':jefe'=>$user->user["id"]] );
+ }else{
+ $rs = $db->query('SELECT * FROM fs_materiasprofesor(:id, NULL)', [':id' => $id] );
+ }
+
+ }catch(Exception $e){
+ $return["error"] = "Ocurri贸 un error al leer los datos de las materias.";
+ echo json_encode($return);
+ }
+
+ $mat_arr = array();
+ foreach($rs as $m){
+ $mat_arr[] = array("horario_id"=>$m["horario_id"], "horario_dia"=>$m["horario_dia"],
+ "horario_hora"=>substr($m["horario_hora"], 0, 2), "horario_min"=>substr($m["horario_hora"], 3, 2),
+ "materia_nombre"=>$m["materia_nombre"].' - '.$m["horario_dia_nombre"]." ".substr($m["horario_hora"], 0, -3),
+ "grupo"=>$m["horario_grupo"], "duracion" => $m["duracion"]
+ );
+ }
+
+ $return["materias"] = $mat_arr;
+
+}
+echo json_encode($return);
+?>
diff --git a/action/reposicion_select.php b/action/reposicion_select.php
index 7a86bbd..8b2aff8 100644
--- a/action/reposicion_select.php
+++ b/action/reposicion_select.php
@@ -1,91 +1,91 @@
-tieneAcceso()){
- $return["error"] = "Error! No tienes permisos para realizar esta acci贸n.";
-}else*/ if(!isset($_POST["id"])){
- $return["error"] = "Error! No se recibi贸 la informaci贸n de la reposici贸n.";
-}else{
- $id = filter_input(INPUT_POST, "id", FILTER_SANITIZE_NUMBER_INT);//limpia texto
-
-
- try{
- $rs = $db->querySingle('SELECT * from fs_reposicion_solicitud(:id, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL)',
- [':id' => $id]
- );
- /*if($user->rol["rol_id"] == 7){//es supervisor
- $rs = $db->querySingle('SELECT * from fs_reposicion_solicitud(:id, NULL, NULL, NULL, NULL, NULL, NULL, NULL, :sup)',
- [':id' => $id, ':sup'=>$user->user["id"]]
- );
- }else{//coordinador
- $rs = $db->querySingle('SELECT * from fs_reposicion_solicitud(:id, :fac, NULL, NULL, NULL, NULL, NULL, NULL, null)',
- [':id' => $id, ":fac"=>$user->facultad["facultad_id"] ]
- );
- }*/
-
- }catch(Exception $e){
- $return["error"] = "Ocurri贸 un error al leer los datos de la reposici贸n.";
- echo json_encode($return);
- exit();
- }
-
-
- $return["fecha_clase"] = date('d/m/Y', strtotime($rs["fecha_clase"]));
- $return["fecha_nueva"] = date('d/m/Y', strtotime($rs["fecha_nueva"]));
- $hora_nueva = explode(":",$rs["hora_nueva"]);
- $return["hora_ini"] = $hora_nueva[0];
- $return["min_ini"] = $hora_nueva[1];
- $hora_nueva_fin = explode(":",$rs["hora_nueva_fin"]);
- $return["hora_fin"] = $hora_nueva_fin[0];
- $return["min_fin"] = $hora_nueva_fin[1];
- $return["duracion"] = $rs["duracion_interval"];
-
-// $return["carrera"] = $rs["PlanEstudio_desc"];
- $return["horario"] = $rs["horario_id"];
- $return["materia"] = $rs["materia_id"];
- $return["materia_desc"] = $rs["materia_nombre"];
- $return["salon"] = $rs["salon_id"];
- if($rs["salon_id"]==""){
- $return["salon_desc"] = "Pendiente";
- }else{
- $salon_json = json_decode($rs["salon_array"], true);
- if($salon_json[0]== "UNIVERSIDAD LA SALLE"){
- unset($salon_json[0]);
- }
- $return["salon_desc"] = join(" / ",$salon_json);
- }
-
- //$return["salon_desc"] = $rs["salon"]=="" ? "-Pendiente-": $rs["salon"];
- $return["ciclo"] = $rs["ciclo"];
- $return["bloque"] = $rs["bloque"];
- $return["profesor"] = $rs["profesor_id"];
- $return["profesor_nombre"] = $rs["profesor_nombre"];
- $return["comentario"] = $rs["descripcion"];
- $return["alumnos"] = $rs["alumnos"];
- $return["tipo"] = $rs["es_reposicion"];
- $return["aula"] = $rs["tipoaula_id"];
- $return["aula_desc"] = $rs["tipoaula_nombre"];
- $return["aula_supervisor"] = $rs["tipoaula_supervisor"];
- $return["dia"] = date('w', strtotime($rs["fecha_clase"]));
- $return["motivo_cancelacion"] = $rs["motivo_cancelacion"];
- $return["estado"] = $rs["estado_reposicion_id"];
- $return["facultad"] = $rs["facultad_nombre"];
- $return["carrera"] = $rs["carrera_comun"]? $rs["carrera_nombre"]: ($rs["horario_carrera"]==""?$rs["carrera_nombre"]:$rs["horario_carrera"]);//si es com煤n, se muestra la carrera de la materia, si no, la carrera del horario
- $return["grupo"] = $rs["horario_grupo"];
- $return["supervisor_nombre"] = $rs["supervisor_nombre"];
-}
-echo json_encode($return);
-?>
+tieneAcceso()){
+ $return["error"] = "Error! No tienes permisos para realizar esta acci贸n.";
+}else*/ if(!isset($_POST["id"])){
+ $return["error"] = "Error! No se recibi贸 la informaci贸n de la reposici贸n.";
+}else{
+ $id = filter_input(INPUT_POST, "id", FILTER_SANITIZE_NUMBER_INT);//limpia texto
+
+
+ try{
+ $rs = $db->querySingle('SELECT * from fs_reposicion_solicitud(:id, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL)',
+ [':id' => $id]
+ );
+ /*if($user->rol["rol_id"] == 7){//es supervisor
+ $rs = $db->querySingle('SELECT * from fs_reposicion_solicitud(:id, NULL, NULL, NULL, NULL, NULL, NULL, NULL, :sup)',
+ [':id' => $id, ':sup'=>$user->user["id"]]
+ );
+ }else{//coordinador
+ $rs = $db->querySingle('SELECT * from fs_reposicion_solicitud(:id, :fac, NULL, NULL, NULL, NULL, NULL, NULL, null)',
+ [':id' => $id, ":fac"=>$user->facultad["facultad_id"] ]
+ );
+ }*/
+
+ }catch(Exception $e){
+ $return["error"] = "Ocurri贸 un error al leer los datos de la reposici贸n.";
+ echo json_encode($return);
+ exit();
+ }
+
+
+ $return["fecha_clase"] = date('d/m/Y', strtotime($rs["fecha_clase"]));
+ $return["fecha_nueva"] = date('d/m/Y', strtotime($rs["fecha_nueva"]));
+ $hora_nueva = explode(":",$rs["hora_nueva"]);
+ $return["hora_ini"] = $hora_nueva[0];
+ $return["min_ini"] = $hora_nueva[1];
+ $hora_nueva_fin = explode(":",$rs["hora_nueva_fin"]);
+ $return["hora_fin"] = $hora_nueva_fin[0];
+ $return["min_fin"] = $hora_nueva_fin[1];
+ $return["duracion"] = $rs["duracion_interval"];
+
+// $return["carrera"] = $rs["PlanEstudio_desc"];
+ $return["horario"] = $rs["horario_id"];
+ $return["materia"] = $rs["materia_id"];
+ $return["materia_desc"] = $rs["materia_nombre"];
+ $return["salon"] = $rs["salon_id"];
+ if($rs["salon_id"]==""){
+ $return["salon_desc"] = "Pendiente";
+ }else{
+ $salon_json = json_decode($rs["salon_array"], true);
+ if($salon_json[0]== "UNIVERSIDAD LA SALLE"){
+ unset($salon_json[0]);
+ }
+ $return["salon_desc"] = join(" / ",$salon_json);
+ }
+
+ //$return["salon_desc"] = $rs["salon"]=="" ? "-Pendiente-": $rs["salon"];
+ $return["ciclo"] = $rs["ciclo"];
+ $return["bloque"] = $rs["bloque"];
+ $return["profesor"] = $rs["profesor_id"];
+ $return["profesor_nombre"] = $rs["profesor_nombre"];
+ $return["comentario"] = $rs["descripcion"];
+ $return["alumnos"] = $rs["alumnos"];
+ $return["tipo"] = $rs["es_reposicion"];
+ $return["aula"] = $rs["tipoaula_id"];
+ $return["aula_desc"] = $rs["tipoaula_nombre"];
+ $return["aula_supervisor"] = $rs["tipoaula_supervisor"];
+ $return["dia"] = date('w', strtotime($rs["fecha_clase"]));
+ $return["motivo_cancelacion"] = $rs["motivo_cancelacion"];
+ $return["estado"] = $rs["estado_reposicion_id"];
+ $return["facultad"] = $rs["facultad_nombre"];
+ $return["carrera"] = $rs["carrera_comun"]? $rs["carrera_nombre"]: ($rs["horario_carrera"]==""?$rs["carrera_nombre"]:$rs["horario_carrera"]);//si es com煤n, se muestra la carrera de la materia, si no, la carrera del horario
+ $return["grupo"] = $rs["horario_grupo"];
+ $return["supervisor_nombre"] = $rs["supervisor_nombre"];
+}
+echo json_encode($return);
+?>
diff --git a/action/reposicion_update.php b/action/reposicion_update.php
index 521a9cb..9ffb74c 100644
--- a/action/reposicion_update.php
+++ b/action/reposicion_update.php
@@ -1,125 +1,125 @@
- FILTER_FLAG_STRIP_LOW)));//limpia texto
-$comentario = trim(htmlspecialchars($_POST["comentario"], ENT_QUOTES, "UTF-8"));//limpia texto
-
-$duracion_rs = $db->querySingle("select * from duracion where duracion_id = :id", [":id"=>$duracion_id]);
-$duracion_tiempo = $duracion_rs["duracion_interval"];
-
-$horario_rs = $db->querySingle('SELECT * from horario_view where horario_id = :hor',
- [':hor' => $hor]
- );
-
-$materia = $horario_rs["materia_id"];
-$dia = $horario_rs["horario_dia"];
-
-
-$hora = $hora_ini.":".$min_ini.":00";
-$fecha_new = DateTime::createFromFormat('d/m/Y', $fecha)->format('Y-m-d')." ".$hora;
-$fecha_fin_new = date("Y-m-d", strtotime($fecha_new))." ".$duracion_tiempo;
-$dia_new = date('w', strtotime($fecha_new));
-
-//echo $fecha_new." ";
-//echo $fecha_fin_new." ";
-if($tipo == 1){//Reposici贸n
- $fecha_falta = DateTime::createFromFormat('d/m/Y', $fecha_falta)->format('Y-m-d');
- $dia_falta = date('w', strtotime($fecha_falta));
-}else{
- $fecha_cambio = DateTime::createFromFormat('d/m/Y', $fecha_cambio)->format('Y-m-d');
- $dia_falta = date('w', strtotime($fecha_cambio));
-}
-
-//Valida que tenga clase en la fecha de falta
-if(intval($dia) != intval($dia_falta)){
- //header("Location:".$pag."?error=11");
- echo intval($dia)." != ".intval($dia_falta);
- exit();
-}
-
-
-if($tipo == 1){//Reposici贸n
- // Valida que grupo no tenga clases
- /*$result = validaConflictoHoras($pdo, $gpo, $dia, $hora, $materia, "-", $fecha_ini, $fecha_fin, $duracion);
- if($result != ""){//error
- //echo $result;
- header("Location:".$pag."?error=7");
- exit();
- }
-
- //Valida que profesor no este en 2 reposiciones al mismo tiempo
- */
- $traslape = $db->querySingle('SELECT * from traslape_profesor_reposicion(:prof, :fecha, :hora, :dur, :id)',
- [':prof' => $prof, ':fecha'=>DateTime::createFromFormat('d/m/Y', $fecha)->format('Y-m-d'), ':hora'=>$hora, ':dur'=>$duracion_tiempo, ':id'=>$id]
- )["traslape_profesor_reposicion"];
- echo "SELECT * from traslape_profesor_reposicion($prof, '".DateTime::createFromFormat('d/m/Y', $fecha)->format('Y-m-d')."', $hora, $duracion_tiempo, $id)";
- if($traslape){
- //header("Location:".$pag."?error=9");
- echo "traslape";
- exit();
- }
-
-
- /*
- $log = new LogActividad();
- $desc_log = "Actualiza reposici贸n ID[".$id."] Fechas[".$fecha_ini."][".$fecha_fin."] Periodo[".$_SESSION["periodo_id"]."] Materia[".$materia."] Profesor[".$prof."] Salon[".$salon."] Horario[".$hor."]";
- $log->appendLog($_SESSION["usuario_id"], $_SESSION["usuario_nombre"]." ".$_SESSION["usuario_apellidos"], $desc_log);*/
-}
-
-try{
- $db->query('SELECT * from fu_reposicion_solicitud(:id, :f_falta, :f_nueva, :hora_nueva, NULL, NULL, :desc, :alumnos, :aula, :duracion, NULL)',
- [':id'=> $id, ':f_falta' => $fecha_falta, ':f_nueva' => $fecha_new, ':hora_nueva' => $hora,
- ':desc' => $comentario, ':alumnos' => $alumnos, ':aula' => $aula, ':duracion' => $duracion_tiempo
- ]
- );
-}catch(Exception $e){
- //header("Location: ".$pag."?error=2");
- print_r($e->getMessage());
- echo "SELECT * from fu_reposicion_solicitud(:id, :f_falta, :f_nueva, :hora_nueva, NULL, NULL, :desc, :alumnos, :aula, :duracion, NULL)'";
- print_r(
- [':id'=> $id, ':f_falta' => $fecha_falta, ':f_nueva' => $fecha_new, ':hora_nueva' => $hora,
- ':desc' => $comentario, ':alumnos' => $alumnos, ':aula' => $aula, ':duracion' => $duracion_tiempo
- ]);
- exit();
-}
-header("Location: ".$pag);
-exit();
-?>
+ FILTER_FLAG_STRIP_LOW)));//limpia texto
+$comentario = trim(htmlspecialchars($_POST["comentario"], ENT_QUOTES, "UTF-8"));//limpia texto
+
+$duracion_rs = $db->querySingle("select * from duracion where duracion_id = :id", [":id"=>$duracion_id]);
+$duracion_tiempo = $duracion_rs["duracion_interval"];
+
+$horario_rs = $db->querySingle('SELECT * from horario_view where horario_id = :hor',
+ [':hor' => $hor]
+ );
+
+$materia = $horario_rs["materia_id"];
+$dia = $horario_rs["horario_dia"];
+
+
+$hora = $hora_ini.":".$min_ini.":00";
+$fecha_new = DateTime::createFromFormat('d/m/Y', $fecha)->format('Y-m-d')." ".$hora;
+$fecha_fin_new = date("Y-m-d", strtotime($fecha_new))." ".$duracion_tiempo;
+$dia_new = date('w', strtotime($fecha_new));
+
+//echo $fecha_new." ";
+//echo $fecha_fin_new." ";
+if($tipo == 1){//Reposici贸n
+ $fecha_falta = DateTime::createFromFormat('d/m/Y', $fecha_falta)->format('Y-m-d');
+ $dia_falta = date('w', strtotime($fecha_falta));
+}else{
+ $fecha_cambio = DateTime::createFromFormat('d/m/Y', $fecha_cambio)->format('Y-m-d');
+ $dia_falta = date('w', strtotime($fecha_cambio));
+}
+
+//Valida que tenga clase en la fecha de falta
+if(intval($dia) != intval($dia_falta)){
+ //header("Location:".$pag."?error=11");
+ echo intval($dia)." != ".intval($dia_falta);
+ exit();
+}
+
+
+if($tipo == 1){//Reposici贸n
+ // Valida que grupo no tenga clases
+ /*$result = validaConflictoHoras($pdo, $gpo, $dia, $hora, $materia, "-", $fecha_ini, $fecha_fin, $duracion);
+ if($result != ""){//error
+ //echo $result;
+ header("Location:".$pag."?error=7");
+ exit();
+ }
+
+ //Valida que profesor no este en 2 reposiciones al mismo tiempo
+ */
+ $traslape = $db->querySingle('SELECT * from traslape_profesor_reposicion(:prof, :fecha, :hora, :dur, :id)',
+ [':prof' => $prof, ':fecha'=>DateTime::createFromFormat('d/m/Y', $fecha)->format('Y-m-d'), ':hora'=>$hora, ':dur'=>$duracion_tiempo, ':id'=>$id]
+ )["traslape_profesor_reposicion"];
+ echo "SELECT * from traslape_profesor_reposicion($prof, '".DateTime::createFromFormat('d/m/Y', $fecha)->format('Y-m-d')."', $hora, $duracion_tiempo, $id)";
+ if($traslape){
+ //header("Location:".$pag."?error=9");
+ echo "traslape";
+ exit();
+ }
+
+
+ /*
+ $log = new LogActividad();
+ $desc_log = "Actualiza reposici贸n ID[".$id."] Fechas[".$fecha_ini."][".$fecha_fin."] Periodo[".$_SESSION["periodo_id"]."] Materia[".$materia."] Profesor[".$prof."] Salon[".$salon."] Horario[".$hor."]";
+ $log->appendLog($_SESSION["usuario_id"], $_SESSION["usuario_nombre"]." ".$_SESSION["usuario_apellidos"], $desc_log);*/
+}
+
+try{
+ $db->query('SELECT * from fu_reposicion_solicitud(:id, :f_falta, :f_nueva, :hora_nueva, NULL, NULL, :desc, :alumnos, :aula, :duracion, NULL)',
+ [':id'=> $id, ':f_falta' => $fecha_falta, ':f_nueva' => $fecha_new, ':hora_nueva' => $hora,
+ ':desc' => $comentario, ':alumnos' => $alumnos, ':aula' => $aula, ':duracion' => $duracion_tiempo
+ ]
+ );
+}catch(Exception $e){
+ //header("Location: ".$pag."?error=2");
+ print_r($e->getMessage());
+ echo "SELECT * from fu_reposicion_solicitud(:id, :f_falta, :f_nueva, :hora_nueva, NULL, NULL, :desc, :alumnos, :aula, :duracion, NULL)'";
+ print_r(
+ [':id'=> $id, ':f_falta' => $fecha_falta, ':f_nueva' => $fecha_new, ':hora_nueva' => $hora,
+ ':desc' => $comentario, ':alumnos' => $alumnos, ':aula' => $aula, ':duracion' => $duracion_tiempo
+ ]);
+ exit();
+}
+header("Location: ".$pag);
+exit();
+?>
diff --git a/action/solicitud_autoriza.php b/action/solicitud_autoriza.php
index 33ae766..4da3611 100644
--- a/action/solicitud_autoriza.php
+++ b/action/solicitud_autoriza.php
@@ -1,198 +1,198 @@
-querySingle('SELECT s.salon_id, s.salon_array FROM salon_view s where s.salon_id = :id_salon',
- [':id_salon' => $salon]
- );
- if($salon_rs["salon_id"] == "" || $salon_rs["salon_id"] == NULL){
- $salon_desc = "Pendiente";
- }else{
- $salon_json = json_decode($salon_rs["salon_array"], true);
- if($salon_json[0]== "UNIVERSIDAD LA SALLE"){
- unset($salon_json[0]);
- }
- $salon_desc = join(" / ",$salon_json);
- }
-}
-
-$solicitud_rs = $db->querySingle('SELECT * from fs_solicitud(:id, NULL, NULL, NULL, NULL, NULL, NULL, NULL)', [':id' => $id_sol]);
-
-
-
-if($edo == 4){//cancelaci贸n
- $motivo = "";
- if(isset($_POST["motivo"]) && $_POST["motivo"] != "")
- $motivo = trim($_POST["motivo"]);
- $db->querySingle('SELECT fu_solicitud_cancela(:id, :motivo)',
- [':id' => $id_sol, ':motivo' => $motivo]
- );
-}else{
- if(!empty($salon)){
- //fu_solicitud(p_solicitud_id, p_fecha_clase , p_fecha_nueva, p_hora_nueva, p_prof_id , p_desc, p_edo, p_alumnos, p_tipoaula , p_salon, p_duracion , p_horario_id , p_bloque, p_ciclo, p_motivo, p_supervisor_id )
- $db->querySingle('SELECT fu_solicitud(:id, NULL, NULL, NULL, NULL, NULL, :edo, NULL, NULL,:sal, NULL, NULL, NULL, NULL, NULL, NULL)',
- [':id' => $id_sol, ':sal' => $salon, ':edo' => $edo]
- );
- }else{
- $db->querySingle('SELECT fu_solicitud(:id, NULL, NULL, NULL, NULL, NULL, :edo, NULL, NULL,NULL, NULL, NULL, NULL, NULL, NULL, NULL)',
- [':id' => $id_sol, ':edo' => $edo]
- );
- }
-}
-//fecha_nueva, fecha_clase
-$fecha_nueva = $solicitud_rs["fecha_nueva"];
-$hora_nueva = $solicitud_rs["hora_nueva"];
-$fecha_clase = $solicitud_rs["fecha_clase"];
-$hora_clase = $solicitud_rs["horario_hora"];
-$facultad = $solicitud_rs["facultad"]??"";
-
-//echo $fecha_nueva." ** ".$fecha_clase;
-
-if(!empty($fecha_nueva)){
- $dia_new = date('w', strtotime($fecha_nueva));
- //$fecha_nueva = DateTime::createFromFormat('Y-m-d', $fecha_nueva)->format('d/m/Y')." ".$hora_nueva;
- $fecha_nueva = $fecha_nueva." ".$hora_nueva;
-}
-
-if(!empty($fecha_clase)){
- $dia_falta = date('w', strtotime($fecha_clase));
- //$fecha_clase = DateTime::createFromFormat('d/m/Y', $fecha_clase)->format('Y-m-d');
-}
-
-switch($edo){
- case 2://Correo a supervisor
- switch($tipo){
- case 1://Reposici贸n
- $asunto = "Solicitud - Reposici贸n nueva ".$solicitud_rs["clave_dependencia"]." ".$facultad;
- $texto = "Se cre贸 una nueva solicitud de reposici贸n para: ".$solicitud_rs["clave_dependencia"]." ".$facultad." .
";
- $texto .= "".mb_strtoupper($solicitud_rs["materia_nombre"])." del d铆a ".$fecha_clase." a las ".$hora_clase." hrs. se propone reponer el ".$fecha_nueva." a las ".$hora_nueva." hrs. ";
- break;
- case 2:
- $asunto = "Solicitud - Cambio de sal贸n ".$solicitud_rs["clave_dependencia"]." ".$facultad;
- $texto = "
Se cre贸 una nueva solicitud de cambio de sal贸n.
";
- $texto .= "El d铆a ".$fecha_nueva." a las ".$hora_nueva." hrs. ";
-
- break;
- case 3:
- $asunto = "Solicitud - Asignaci贸n de espacio ".$solicitud_rs["clave_dependencia"]." ".$facultad;
- $texto = "
Se cre贸 una nueva solicitud de asignaci贸n de sal贸n.
";
- $texto .= "El d铆a ".$fecha_nueva." a las ".$hora_nueva." hrs. ";
- break;
- case 4:
- $asunto = "Solicitud - Cambio permanente ".$solicitud_rs["clave_dependencia"]." ".$facultad;
- $texto = "
Se cre贸 una nueva solicitud de asignaci贸n de sal贸n permanente.
";
- $texto .= "".mb_strtoupper($solicitud_rs["materia_nombre"])." a partir del d铆a ".$fecha_clase." a las ".$hora_orig." hrs. se propone cambiar para el ".$fecha_nueva." a las ".$hora_nueva." hrs. ";
- break;
-
- }
- //$texto .= "
Ingresa al sistema PAAD para autorizarla.
";
-
- MandaCorreos::enviarCorreo($db, $asunto, $texto, $user->facultad["facultad_id"], MandaCorreos::SUPERVISOR);
-
- $ok = 0;
- break;
- case 3://Correo a coordinador, profesor y jefe
-
- switch($tipo){
- case 1://Reposici贸n
- $asunto = "Reposici贸n nueva autorizada ".isset($solicitud_rs["materia"])?$solicitud_rs["materia"]:"";
- $texto = "Se autoriz贸 la solicitud de reposici贸n.
";
- $texto .= "".mb_strtoupper($solicitud_rs["materia_nombre"])." del d铆a ".$fecha_clase." a las ".$hora_clase." hrs. se repondr谩 el ".$fecha_nueva." a las ".$hora_nueva." hrs. en ".$salon_desc." ";
- break;
- case 2:
- $asunto = "Cambio de sal贸n autorizado";
- $texto = "
Se autoriz贸 la solicitud de cambio de sal贸n.
";
- $texto .= "El d铆a ".$fecha_nueva." a las ".$hora_nueva." hrs. en ".$salon_desc." ";
-
- break;
- case 3:
- $asunto = "Asignaci贸n de espacio autorizada";
- $texto = "
Se autoriz贸 la asignaci贸n de sal贸n.
";
- $texto .= "El d铆a ".$fecha_nueva." a las ".$hora_nueva." hrs. en ".$salon_desc." ";
- break;
- case 4:
- $asunto = "Cambio permanente autorizado ".$solicitud_rs["materia_nombre"];
- $texto = "
Se autoriz贸 cambio de sal贸n permanente.
";
- $texto .= "".mb_strtoupper($solicitud_rs["materia_nombre"])." a partir del d铆a ".$fecha_clase." la clase ser谩 el ".$fecha_nueva." a las ".$hora_nueva." hrs. en ".$salon_desc." ";
- break;
- }
-
- $ok = 0;
- $db->querySingle('SELECT fu_reposicion_solicitud_supervisor(:id, :sup)',
- [':id' => $id_sol, ':sup'=>$user->user["id"]]
- );
-
- MandaCorreos::enviarCorreo($db, $asunto, $texto, $user->facultad["facultad_id"], MandaCorreos::COORDINADOR| MandaCorreos::PROFESOR| MandaCorreos::JEFE);
-
- break;
- case 4://Correo a coordinador, profesor y jefe
- switch($tipo){
- case 1://Reposici贸n
- $asunto = "Reposici贸n declinada ".$solicitud_rs["materia"];
- $texto = "
La reposici贸n de ".mb_strtoupper($solicitud_rs["materia_nombre"])." del d铆a ".$fecha_clase." a las ".$hora_clase." hrs. programada para el ".$fecha_nueva." a las ".$hora_nueva." hrs. ha sido declinada por el siguiente motivo
";
- break;
- case 2:
- $asunto = "Cambio de sal贸n declinado";
- $texto = "La solicitud de cambio de sal贸n para el d铆a ".$fecha_nueva." a las ".$hora_nueva." hrs. ha sido declinada por el siguiente motivo
";
- break;
- case 3:
- $asunto = "Asignaci贸n de espacio declinada";
- $texto = "La asignaci贸n de sal贸n para el d铆a ".$fecha_nueva." a las ".$hora_nueva." hrs. ha sido declinada por el siguiente motivo
";
- break;
- case 4:
- $asunto = "Cambio permanente declinado ".$solicitud_rs["materia_nombre"];
- $texto = "El cambio de sal贸n permanente de ".mb_strtoupper($solicitud_rs["materia_nombre"])." del d铆a ".$fecha_clase." a las ".$hora_clase." hrs. programada para el ".$fecha_nueva." a las ".$hora_nueva." hrs. ha sido declinada por el siguiente motivo
";
- break;
- }
- $texto .= "".$solicitud_rs["motivo"]."
";
-
- $ok = 1;
- $db->querySingle('SELECT fu_reposicion_solicitud_supervisor(:id, :sup)',
- [':id' => $id_sol, ':sup'=>$user->user["id"]]
- );
-
- MandaCorreos::enviarCorreo($db, $asunto, $texto, $user->facultad["facultad_id"], MandaCorreos::COORDINADOR| MandaCorreos::PROFESOR| MandaCorreos::JEFE);
-
- break;
-}
-
-
-/*
-$log->appendLog($_SESSION["usuario_id"], $_SESSION["usuario_nombre"]." ".$_SESSION["usuario_apellidos"], $desc_log);
-*/
-header("Location: ".$pag."?ok=".$ok);
-exit();
-?>
+querySingle('SELECT s.salon_id, s.salon_array FROM salon_view s where s.salon_id = :id_salon',
+ [':id_salon' => $salon]
+ );
+ if($salon_rs["salon_id"] == "" || $salon_rs["salon_id"] == NULL){
+ $salon_desc = "Pendiente";
+ }else{
+ $salon_json = json_decode($salon_rs["salon_array"], true);
+ if($salon_json[0]== "UNIVERSIDAD LA SALLE"){
+ unset($salon_json[0]);
+ }
+ $salon_desc = join(" / ",$salon_json);
+ }
+}
+
+$solicitud_rs = $db->querySingle('SELECT * from fs_solicitud(:id, NULL, NULL, NULL, NULL, NULL, NULL, NULL)', [':id' => $id_sol]);
+
+
+
+if($edo == 4){//cancelaci贸n
+ $motivo = "";
+ if(isset($_POST["motivo"]) && $_POST["motivo"] != "")
+ $motivo = trim($_POST["motivo"]);
+ $db->querySingle('SELECT fu_solicitud_cancela(:id, :motivo)',
+ [':id' => $id_sol, ':motivo' => $motivo]
+ );
+}else{
+ if(!empty($salon)){
+ //fu_solicitud(p_solicitud_id, p_fecha_clase , p_fecha_nueva, p_hora_nueva, p_prof_id , p_desc, p_edo, p_alumnos, p_tipoaula , p_salon, p_duracion , p_horario_id , p_bloque, p_ciclo, p_motivo, p_supervisor_id )
+ $db->querySingle('SELECT fu_solicitud(:id, NULL, NULL, NULL, NULL, NULL, :edo, NULL, NULL,:sal, NULL, NULL, NULL, NULL, NULL, NULL)',
+ [':id' => $id_sol, ':sal' => $salon, ':edo' => $edo]
+ );
+ }else{
+ $db->querySingle('SELECT fu_solicitud(:id, NULL, NULL, NULL, NULL, NULL, :edo, NULL, NULL,NULL, NULL, NULL, NULL, NULL, NULL, NULL)',
+ [':id' => $id_sol, ':edo' => $edo]
+ );
+ }
+}
+//fecha_nueva, fecha_clase
+$fecha_nueva = $solicitud_rs["fecha_nueva"];
+$hora_nueva = $solicitud_rs["hora_nueva"];
+$fecha_clase = $solicitud_rs["fecha_clase"];
+$hora_clase = $solicitud_rs["horario_hora"];
+$facultad = $solicitud_rs["facultad"]??"";
+
+//echo $fecha_nueva." ** ".$fecha_clase;
+
+if(!empty($fecha_nueva)){
+ $dia_new = date('w', strtotime($fecha_nueva));
+ //$fecha_nueva = DateTime::createFromFormat('Y-m-d', $fecha_nueva)->format('d/m/Y')." ".$hora_nueva;
+ $fecha_nueva = $fecha_nueva." ".$hora_nueva;
+}
+
+if(!empty($fecha_clase)){
+ $dia_falta = date('w', strtotime($fecha_clase));
+ //$fecha_clase = DateTime::createFromFormat('d/m/Y', $fecha_clase)->format('Y-m-d');
+}
+
+switch($edo){
+ case 2://Correo a supervisor
+ switch($tipo){
+ case 1://Reposici贸n
+ $asunto = "Solicitud - Reposici贸n nueva ".$solicitud_rs["clave_dependencia"]." ".$facultad;
+ $texto = "Se cre贸 una nueva solicitud de reposici贸n para: ".$solicitud_rs["clave_dependencia"]." ".$facultad." .
";
+ $texto .= "".mb_strtoupper($solicitud_rs["materia_nombre"])." del d铆a ".$fecha_clase." a las ".$hora_clase." hrs. se propone reponer el ".$fecha_nueva." a las ".$hora_nueva." hrs. ";
+ break;
+ case 2:
+ $asunto = "Solicitud - Cambio de sal贸n ".$solicitud_rs["clave_dependencia"]." ".$facultad;
+ $texto = "
Se cre贸 una nueva solicitud de cambio de sal贸n.
";
+ $texto .= "El d铆a ".$fecha_nueva." a las ".$hora_nueva." hrs. ";
+
+ break;
+ case 3:
+ $asunto = "Solicitud - Asignaci贸n de espacio ".$solicitud_rs["clave_dependencia"]." ".$facultad;
+ $texto = "
Se cre贸 una nueva solicitud de asignaci贸n de sal贸n.
";
+ $texto .= "El d铆a ".$fecha_nueva." a las ".$hora_nueva." hrs. ";
+ break;
+ case 4:
+ $asunto = "Solicitud - Cambio permanente ".$solicitud_rs["clave_dependencia"]." ".$facultad;
+ $texto = "
Se cre贸 una nueva solicitud de asignaci贸n de sal贸n permanente.
";
+ $texto .= "".mb_strtoupper($solicitud_rs["materia_nombre"])." a partir del d铆a ".$fecha_clase." a las ".$hora_orig." hrs. se propone cambiar para el ".$fecha_nueva." a las ".$hora_nueva." hrs. ";
+ break;
+
+ }
+ //$texto .= "
Ingresa al sistema PAAD para autorizarla.
";
+
+ MandaCorreos::enviarCorreo($db, $asunto, $texto, $user->facultad["facultad_id"], MandaCorreos::SUPERVISOR);
+
+ $ok = 0;
+ break;
+ case 3://Correo a coordinador, profesor y jefe
+
+ switch($tipo){
+ case 1://Reposici贸n
+ $asunto = "Reposici贸n nueva autorizada ".isset($solicitud_rs["materia"])?$solicitud_rs["materia"]:"";
+ $texto = "Se autoriz贸 la solicitud de reposici贸n.
";
+ $texto .= "".mb_strtoupper($solicitud_rs["materia_nombre"])." del d铆a ".$fecha_clase." a las ".$hora_clase." hrs. se repondr谩 el ".$fecha_nueva." a las ".$hora_nueva." hrs. en ".$salon_desc." ";
+ break;
+ case 2:
+ $asunto = "Cambio de sal贸n autorizado";
+ $texto = "
Se autoriz贸 la solicitud de cambio de sal贸n.
";
+ $texto .= "El d铆a ".$fecha_nueva." a las ".$hora_nueva." hrs. en ".$salon_desc." ";
+
+ break;
+ case 3:
+ $asunto = "Asignaci贸n de espacio autorizada";
+ $texto = "
Se autoriz贸 la asignaci贸n de sal贸n.
";
+ $texto .= "El d铆a ".$fecha_nueva." a las ".$hora_nueva." hrs. en ".$salon_desc." ";
+ break;
+ case 4:
+ $asunto = "Cambio permanente autorizado ".$solicitud_rs["materia_nombre"];
+ $texto = "
Se autoriz贸 cambio de sal贸n permanente.
";
+ $texto .= "".mb_strtoupper($solicitud_rs["materia_nombre"])." a partir del d铆a ".$fecha_clase." la clase ser谩 el ".$fecha_nueva." a las ".$hora_nueva." hrs. en ".$salon_desc." ";
+ break;
+ }
+
+ $ok = 0;
+ $db->querySingle('SELECT fu_reposicion_solicitud_supervisor(:id, :sup)',
+ [':id' => $id_sol, ':sup'=>$user->user["id"]]
+ );
+
+ MandaCorreos::enviarCorreo($db, $asunto, $texto, $user->facultad["facultad_id"], MandaCorreos::COORDINADOR| MandaCorreos::PROFESOR| MandaCorreos::JEFE);
+
+ break;
+ case 4://Correo a coordinador, profesor y jefe
+ switch($tipo){
+ case 1://Reposici贸n
+ $asunto = "Reposici贸n declinada ".$solicitud_rs["materia"];
+ $texto = "
La reposici贸n de ".mb_strtoupper($solicitud_rs["materia_nombre"])." del d铆a ".$fecha_clase." a las ".$hora_clase." hrs. programada para el ".$fecha_nueva." a las ".$hora_nueva." hrs. ha sido declinada por el siguiente motivo
";
+ break;
+ case 2:
+ $asunto = "Cambio de sal贸n declinado";
+ $texto = "La solicitud de cambio de sal贸n para el d铆a ".$fecha_nueva." a las ".$hora_nueva." hrs. ha sido declinada por el siguiente motivo
";
+ break;
+ case 3:
+ $asunto = "Asignaci贸n de espacio declinada";
+ $texto = "La asignaci贸n de sal贸n para el d铆a ".$fecha_nueva." a las ".$hora_nueva." hrs. ha sido declinada por el siguiente motivo
";
+ break;
+ case 4:
+ $asunto = "Cambio permanente declinado ".$solicitud_rs["materia_nombre"];
+ $texto = "El cambio de sal贸n permanente de ".mb_strtoupper($solicitud_rs["materia_nombre"])." del d铆a ".$fecha_clase." a las ".$hora_clase." hrs. programada para el ".$fecha_nueva." a las ".$hora_nueva." hrs. ha sido declinada por el siguiente motivo
";
+ break;
+ }
+ $texto .= "".$solicitud_rs["motivo"]."
";
+
+ $ok = 1;
+ $db->querySingle('SELECT fu_reposicion_solicitud_supervisor(:id, :sup)',
+ [':id' => $id_sol, ':sup'=>$user->user["id"]]
+ );
+
+ MandaCorreos::enviarCorreo($db, $asunto, $texto, $user->facultad["facultad_id"], MandaCorreos::COORDINADOR| MandaCorreos::PROFESOR| MandaCorreos::JEFE);
+
+ break;
+}
+
+
+/*
+$log->appendLog($_SESSION["usuario_id"], $_SESSION["usuario_nombre"]." ".$_SESSION["usuario_apellidos"], $desc_log);
+*/
+header("Location: ".$pag."?ok=".$ok);
+exit();
+?>
diff --git a/action/solicitud_delete.php b/action/solicitud_delete.php
index 54d702a..a68a1ec 100644
--- a/action/solicitud_delete.php
+++ b/action/solicitud_delete.php
@@ -1,33 +1,33 @@
-user["id"];
-
- try{
- $db->query('SELECT * from fd_solicitud(:id, :creador)', [":id"=> $id, ":creador"=>$creador]);
- $return["ok"] = "La solicitud se borr贸 correctamente";
-
- }catch(Exception $e){
- $return["error"] = "Ocurri贸 un error al borrar la solicitud.";
- }
-
-
-}
-echo json_encode($return);
-?>
+user["id"];
+
+ try{
+ $db->query('SELECT * from fd_solicitud(:id, :creador)', [":id"=> $id, ":creador"=>$creador]);
+ $return["ok"] = "La solicitud se borr贸 correctamente";
+
+ }catch(Exception $e){
+ $return["error"] = "Ocurri贸 un error al borrar la solicitud.";
+ }
+
+
+}
+echo json_encode($return);
+?>
diff --git a/action/solicitud_insert.php b/action/solicitud_insert.php
index 71d4dad..4b77690 100644
--- a/action/solicitud_insert.php
+++ b/action/solicitud_insert.php
@@ -1,206 +1,206 @@
-access();
-
-$estado = filter_input(INPUT_POST, "estado", FILTER_SANITIZE_NUMBER_INT);//
-$tipo = filter_input(INPUT_POST, "tipo", FILTER_SANITIZE_NUMBER_INT);//
-$duracion_id = filter_input(INPUT_POST, "duracion", FILTER_SANITIZE_NUMBER_INT);//Id reposicion
-$bloque = filter_input(INPUT_POST, "bloque", FILTER_SANITIZE_NUMBER_INT);//
-$ciclo = filter_input(INPUT_POST, "ciclo", FILTER_SANITIZE_NUMBER_INT);//
-$fecha_falta = trim(htmlspecialchars($_POST["fecha_falta"], ENT_QUOTES, "UTF-8"));//Reposicion
-$fecha = trim(htmlspecialchars($_POST["fecha_nueva"], ENT_QUOTES, "UTF-8"));//Reposicion
-$fecha_cambio = trim(htmlspecialchars($_POST["fecha_cambio"], ENT_QUOTES, "UTF-8"));//Cambio sal贸n
-$hora_ini = filter_input(INPUT_POST, "hora_ini", FILTER_SANITIZE_NUMBER_INT);//limpia texto hora reposicion
-$min_ini = filter_input(INPUT_POST, "min_ini", FILTER_SANITIZE_NUMBER_INT);//limpia texto
-$horario = filter_input(INPUT_POST, "horario", FILTER_SANITIZE_NUMBER_INT);//limpia texto
-$alumnos = filter_input(INPUT_POST, "alumnos", FILTER_SANITIZE_NUMBER_INT);//limpia texto
-$aula = filter_input(INPUT_POST, "aula", FILTER_SANITIZE_NUMBER_INT);//1 regular , 2 sala computo, 3 otro facultad
-$salon = NULL;
-
-/*if(!$user->jefe_carrera){//coordinador
- if(isset($_POST["salon"]) && $_POST["salon"] != "")
- $salon = filter_input(INPUT_POST, "dlSalon", FILTER_SANITIZE_NUMBER_INT);//1 regular , 2 sala computo, 3 otro facultad
-}*/
-
-if(empty($_POST["prof"]))
- $prof = $user->user["id"];
-else
- $prof = filter_input(INPUT_POST, "prof", FILTER_SANITIZE_NUMBER_INT);//limpia texto
-
-$comentario = trim(htmlspecialchars($_POST["comentario"], ENT_QUOTES, "UTF-8"));//limpia texto
-
-
-
-$duracion_rs = $db->querySingle("select * from duracion where duracion_id = :id", [":id"=>$duracion_id]);
-$duracion_tiempo = $duracion_rs["duracion_interval"];
-
-//-- Obtiene datos de horario regular de clase
-if($horario!= null && $tipo !=3)
-{
- $horario_rs = $db->querySingle('SELECT * from horario_view where horario_id = :hor',
- [':hor' => $horario]
- );
-
- $materia = $horario_rs["materia_id"];
- $dia = $horario_rs["horario_dia"];
- $hora_orig = $horario_rs["horario_hora"];
-}else{
- $dia = date('w', strtotime($fecha));
-}
-
-$hora = $hora_ini.":".$min_ini.":00";
-
-switch($tipo){
- case 1://Reposici贸n
- $fecha_nueva = $fecha;
- $fecha_clase = $fecha_falta;
-
- $fecha_new = DateTime::createFromFormat('d/m/Y', $fecha)->format('Y-m-d')." ".$hora;
- $fecha_fin_new = date("Y-m-d", strtotime($fecha_new))." ".$duracion_tiempo;
- $dia_new = date('w', strtotime($fecha_new));
-
- $fecha_falta = DateTime::createFromFormat('d/m/Y', $fecha_falta)->format('Y-m-d');
- $dia_falta = date('w', strtotime($fecha_falta));
-
- //Valida que tenga clase en la fecha de falta
- if(intval($dia) != intval($dia_falta)){
- header("Location:".$pag."?error=11");
- /*print_r($_POST);
- echo 'SELECT * from horario_view where horario_id = '.$horario;
- echo intval($dia)." != ".intval($dia_falta);*/
- exit();
- }
-
- break;
- case 2://Cambio
- $fecha_nueva = $fecha_cambio;
-
- $fecha_cambio = DateTime::createFromFormat('d/m/Y', $fecha_cambio)->format('Y-m-d');
- $dia_falta = date('w', strtotime($fecha_cambio));
- break;
- case 3://Solicitud de espacio
- $fecha_nueva = $fecha;
-
- $fecha_new = DateTime::createFromFormat('d/m/Y', $fecha)->format('Y-m-d')." ".$hora;
- $fecha_fin_new = date("Y-m-d", strtotime($fecha_new))." ".$duracion_tiempo;
- $dia_new = date('w', strtotime($fecha_new));
- break;
- case 4://Cambio permanente
- $fecha_nueva = $fecha;
- $fecha_clase = $fecha_cambio;
-
- $fecha_new = DateTime::createFromFormat('d/m/Y', $fecha)->format('Y-m-d')." ".$hora;
- $fecha_cambio = DateTime::createFromFormat('d/m/Y', $fecha_cambio)->format('Y-m-d')." ".$hora;
- $fecha_fin_new = date("Y-m-d", strtotime($fecha_new))." ".$duracion_tiempo;
- $dia_new = date('w', strtotime($fecha_new));
-
- break;
-}
-
-if(isset($materia) && $materia!= null && $tipo !=3){
- //Obtiene materia
- $materia_rs = $db->querySingle('SELECT materia_nombre from materia where materia_id = :mat',[':mat' => $materia]);
-}
-
-$query = ":f_falta, :f_nueva, :hora_nueva, :prof, :desc, :edo, :alumnos, :aula, :solicitudtipo, :usr, :duracion, :hor, :bloque, :ciclo";
-switch($tipo){
- case 1://Reposici贸n
- $traslape = $db->querySingle('SELECT * from traslape_profesor_reposicion(:prof, :fecha, :hora, :dur)',
- [':prof' => $prof, ':fecha'=>DateTime::createFromFormat('d/m/Y', $fecha)->format('Y-m-d'), ':hora'=>$hora, ':dur'=>$duracion_tiempo]
- )["traslape_profesor_reposicion"];
- if($traslape){
- //print_r($_POST);
- //echo "SELECT * from traslape_profesor_reposicion($prof,'".DateTime::createFromFormat('d/m/Y', $fecha)->format('Y-m-d')."' , '$hora', $duracion)";
- header("Location:".$pag."?error=9");
- exit();
- }
-
- $db_params=[
- ":f_falta"=>$fecha_falta, ":f_nueva"=>$fecha_new, ":hora_nueva"=>$hora, ":prof"=>$prof, ":desc"=>$comentario,
- ":edo"=>1, ":alumnos"=>$alumnos, ":aula"=>$aula, ":solicitudtipo"=>$tipo, ":usr"=>$user->user["id"],
- ":duracion"=>$duracion_tiempo, ":hor"=>$horario, ":bloque"=>$bloque, ":ciclo"=>$ciclo
- ];
- $asunto = "Solicitud - Reposici贸n nueva";
- $texto = "Se cre贸 una nueva solicitud de reposici贸n.
";
- $texto .= "".mb_strtoupper($materia_rs["materia_nombre"])." del d铆a ".$fecha_falta." a las ".$hora_orig." hrs. se propone reponer el ".$fecha_nueva." a las ".$hora." hrs. ";
-
- /*
- $log = new LogActividad();
- $desc_log = "Inserta reposici贸n nueva ID[".$rs["fi_reposicion"]."] Fechas[".$fecha_falta.">".$fecha_new."] Periodo[".$_SESSION["periodo_id"]."] Materia[".$materia."] Profesor[".$prof."] Salon[".$salon."] Horario[".$horario."] Alumnos[".$alumnos."]";
- $log->appendLog($_SESSION["usuario_id"], $_SESSION["usuario_nombre"]." ".$_SESSION["usuario_apellidos"], $desc_log);*/
- break;
- case 2:
- $db_params=[
- ":f_falta"=>$fecha_cambio, ":f_nueva"=>$fecha_cambio, ":hora_nueva"=>$hora, ":prof"=>$prof, ":desc"=>$comentario,
- ":edo"=>1, ":alumnos"=>$alumnos, ":aula"=>$aula, ":solicitudtipo"=>$tipo, ":usr"=>$user->user["id"],
- ":duracion"=>$duracion_tiempo, ":hor"=>$horario, ":bloque"=>$bloque, ":ciclo"=>$ciclo
- ];
- $asunto = "Solicitud - Cambio de sal贸n";
- $texto = "
Se cre贸 una nueva solicitud de cambio de sal贸n.
";
- $texto .= "El d铆a ".$fecha_nueva." a las ".$hora." hrs. ";
-
- /*
- $log = new LogActividad();
- $desc_log = "Inserta reposici贸n nueva ID[".$rs["fi_reposicion"]."] Fechas[".$fecha_cambio.">".$fecha_cambio_nueva."] Periodo[".$_SESSION["periodo_id"]."] Materia[".$materia."] Profesor[".$prof."] Salon[".$salon."] Horario[".$horario."] Alumnos[".$alumnos."]";
- $log->appendLog($_SESSION["usuario_id"], $_SESSION["usuario_nombre"]." ".$_SESSION["usuario_apellidos"], $desc_log);
- */
- break;
- case 3:
- $db_params=[
- ":f_nueva"=>$fecha_new, ":hora_nueva"=>$hora, ":prof"=>$prof, ":desc"=>$comentario,
- ":edo"=>1, ":alumnos"=>$alumnos, ":aula"=>$aula, ":solicitudtipo"=>$tipo, ":usr"=>$user->user["id"],
- ":duracion"=>$duracion_tiempo
- ];
- $query = "null, :f_nueva, :hora_nueva, :prof, :desc, :edo, :alumnos, :aula, :solicitudtipo, :usr, :duracion";
- $asunto = "Solicitud - Asignaci贸n de espacio";
- $texto = "
Se cre贸 una nueva solicitud de asignaci贸n de sal贸n.
";
- $texto .= "El d铆a ".$fecha_nueva." a las ".$hora." hrs. ";
- break;
- case 4:
- $db_params=[
- ":f_nueva"=>$fecha_new, ":hora_nueva"=>$hora, ":prof"=>$prof, ":desc"=>$comentario,
- ":edo"=>1, ":alumnos"=>$alumnos, ":aula"=>$aula, ":solicitudtipo"=>$tipo, ":usr"=>$user->user["id"],
- ":duracion"=>$duracion_tiempo, ":hor"=>$horario, ":bloque"=>$bloque, ":ciclo"=>$ciclo
- ];
- $query = "NULL, :f_nueva, :hora_nueva, :prof, :desc, :edo, :alumnos, :aula, :solicitudtipo, :usr, :duracion, :hor, :bloque, :ciclo";
- $asunto = "Solicitud - Cambio permanente";
- $texto = "
Se cre贸 una nueva solicitud de asignaci贸n de sal贸n permanente.
";
- $texto .= "".mb_strtoupper($materia_rs["materia_nombre"])." a partir del d铆a ".$fecha_clase." a las ".$hora_orig." hrs. se propone cambiar para el ".$fecha_nueva." a las ".$hora." hrs. ";
- break;
-
-}
-try{
- $db->query("SELECT * from fi_solicitud($query)", $db_params);
-}catch(Exception $e){
- echo "ERROR Cambio ".$e->getMessage();
- echo $query;
- print_r($db_params);
- //header("Location: ".$pag."?error=1");
- exit();
-}
-
-if(!MandaCorreos::enviarCorreo($db, $asunto, $texto, $user->facultad["facultad_id"], MandaCorreos::COORDINADOR)){
- echo "ERROR Correo ";
- //header("Location: ".$pag."?error=2");
- exit();
-}
-
-
-header("Location: ".$pag."?ok=0");
-exit();
-?>
+access();
+
+$estado = filter_input(INPUT_POST, "estado", FILTER_SANITIZE_NUMBER_INT);//
+$tipo = filter_input(INPUT_POST, "tipo", FILTER_SANITIZE_NUMBER_INT);//
+$duracion_id = filter_input(INPUT_POST, "duracion", FILTER_SANITIZE_NUMBER_INT);//Id reposicion
+$bloque = filter_input(INPUT_POST, "bloque", FILTER_SANITIZE_NUMBER_INT);//
+$ciclo = filter_input(INPUT_POST, "ciclo", FILTER_SANITIZE_NUMBER_INT);//
+$fecha_falta = trim(htmlspecialchars($_POST["fecha_falta"], ENT_QUOTES, "UTF-8"));//Reposicion
+$fecha = trim(htmlspecialchars($_POST["fecha_nueva"], ENT_QUOTES, "UTF-8"));//Reposicion
+$fecha_cambio = trim(htmlspecialchars($_POST["fecha_cambio"], ENT_QUOTES, "UTF-8"));//Cambio sal贸n
+$hora_ini = filter_input(INPUT_POST, "hora_ini", FILTER_SANITIZE_NUMBER_INT);//limpia texto hora reposicion
+$min_ini = filter_input(INPUT_POST, "min_ini", FILTER_SANITIZE_NUMBER_INT);//limpia texto
+$horario = filter_input(INPUT_POST, "horario", FILTER_SANITIZE_NUMBER_INT);//limpia texto
+$alumnos = filter_input(INPUT_POST, "alumnos", FILTER_SANITIZE_NUMBER_INT);//limpia texto
+$aula = filter_input(INPUT_POST, "aula", FILTER_SANITIZE_NUMBER_INT);//1 regular , 2 sala computo, 3 otro facultad
+$salon = NULL;
+
+/*if(!$user->jefe_carrera){//coordinador
+ if(isset($_POST["salon"]) && $_POST["salon"] != "")
+ $salon = filter_input(INPUT_POST, "dlSalon", FILTER_SANITIZE_NUMBER_INT);//1 regular , 2 sala computo, 3 otro facultad
+}*/
+
+if(empty($_POST["prof"]))
+ $prof = $user->user["id"];
+else
+ $prof = filter_input(INPUT_POST, "prof", FILTER_SANITIZE_NUMBER_INT);//limpia texto
+
+$comentario = trim(htmlspecialchars($_POST["comentario"], ENT_QUOTES, "UTF-8"));//limpia texto
+
+
+
+$duracion_rs = $db->querySingle("select * from duracion where duracion_id = :id", [":id"=>$duracion_id]);
+$duracion_tiempo = $duracion_rs["duracion_interval"];
+
+//-- Obtiene datos de horario regular de clase
+if($horario!= null && $tipo !=3)
+{
+ $horario_rs = $db->querySingle('SELECT * from horario_view where horario_id = :hor',
+ [':hor' => $horario]
+ );
+
+ $materia = $horario_rs["materia_id"];
+ $dia = $horario_rs["horario_dia"];
+ $hora_orig = $horario_rs["horario_hora"];
+}else{
+ $dia = date('w', strtotime($fecha));
+}
+
+$hora = $hora_ini.":".$min_ini.":00";
+
+switch($tipo){
+ case 1://Reposici贸n
+ $fecha_nueva = $fecha;
+ $fecha_clase = $fecha_falta;
+
+ $fecha_new = DateTime::createFromFormat('d/m/Y', $fecha)->format('Y-m-d')." ".$hora;
+ $fecha_fin_new = date("Y-m-d", strtotime($fecha_new))." ".$duracion_tiempo;
+ $dia_new = date('w', strtotime($fecha_new));
+
+ $fecha_falta = DateTime::createFromFormat('d/m/Y', $fecha_falta)->format('Y-m-d');
+ $dia_falta = date('w', strtotime($fecha_falta));
+
+ //Valida que tenga clase en la fecha de falta
+ if(intval($dia) != intval($dia_falta)){
+ header("Location:".$pag."?error=11");
+ /*print_r($_POST);
+ echo 'SELECT * from horario_view where horario_id = '.$horario;
+ echo intval($dia)." != ".intval($dia_falta);*/
+ exit();
+ }
+
+ break;
+ case 2://Cambio
+ $fecha_nueva = $fecha_cambio;
+
+ $fecha_cambio = DateTime::createFromFormat('d/m/Y', $fecha_cambio)->format('Y-m-d');
+ $dia_falta = date('w', strtotime($fecha_cambio));
+ break;
+ case 3://Solicitud de espacio
+ $fecha_nueva = $fecha;
+
+ $fecha_new = DateTime::createFromFormat('d/m/Y', $fecha)->format('Y-m-d')." ".$hora;
+ $fecha_fin_new = date("Y-m-d", strtotime($fecha_new))." ".$duracion_tiempo;
+ $dia_new = date('w', strtotime($fecha_new));
+ break;
+ case 4://Cambio permanente
+ $fecha_nueva = $fecha;
+ $fecha_clase = $fecha_cambio;
+
+ $fecha_new = DateTime::createFromFormat('d/m/Y', $fecha)->format('Y-m-d')." ".$hora;
+ $fecha_cambio = DateTime::createFromFormat('d/m/Y', $fecha_cambio)->format('Y-m-d')." ".$hora;
+ $fecha_fin_new = date("Y-m-d", strtotime($fecha_new))." ".$duracion_tiempo;
+ $dia_new = date('w', strtotime($fecha_new));
+
+ break;
+}
+
+if(isset($materia) && $materia!= null && $tipo !=3){
+ //Obtiene materia
+ $materia_rs = $db->querySingle('SELECT materia_nombre from materia where materia_id = :mat',[':mat' => $materia]);
+}
+
+$query = ":f_falta, :f_nueva, :hora_nueva, :prof, :desc, :edo, :alumnos, :aula, :solicitudtipo, :usr, :duracion, :hor, :bloque, :ciclo";
+switch($tipo){
+ case 1://Reposici贸n
+ $traslape = $db->querySingle('SELECT * from traslape_profesor_reposicion(:prof, :fecha, :hora, :dur)',
+ [':prof' => $prof, ':fecha'=>DateTime::createFromFormat('d/m/Y', $fecha)->format('Y-m-d'), ':hora'=>$hora, ':dur'=>$duracion_tiempo]
+ )["traslape_profesor_reposicion"];
+ if($traslape){
+ //print_r($_POST);
+ //echo "SELECT * from traslape_profesor_reposicion($prof,'".DateTime::createFromFormat('d/m/Y', $fecha)->format('Y-m-d')."' , '$hora', $duracion)";
+ header("Location:".$pag."?error=9");
+ exit();
+ }
+
+ $db_params=[
+ ":f_falta"=>$fecha_falta, ":f_nueva"=>$fecha_new, ":hora_nueva"=>$hora, ":prof"=>$prof, ":desc"=>$comentario,
+ ":edo"=>1, ":alumnos"=>$alumnos, ":aula"=>$aula, ":solicitudtipo"=>$tipo, ":usr"=>$user->user["id"],
+ ":duracion"=>$duracion_tiempo, ":hor"=>$horario, ":bloque"=>$bloque, ":ciclo"=>$ciclo
+ ];
+ $asunto = "Solicitud - Reposici贸n nueva";
+ $texto = "
Se cre贸 una nueva solicitud de reposici贸n.
";
+ $texto .= "".mb_strtoupper($materia_rs["materia_nombre"])." del d铆a ".$fecha_falta." a las ".$hora_orig." hrs. se propone reponer el ".$fecha_nueva." a las ".$hora." hrs. ";
+
+ /*
+ $log = new LogActividad();
+ $desc_log = "Inserta reposici贸n nueva ID[".$rs["fi_reposicion"]."] Fechas[".$fecha_falta.">".$fecha_new."] Periodo[".$_SESSION["periodo_id"]."] Materia[".$materia."] Profesor[".$prof."] Salon[".$salon."] Horario[".$horario."] Alumnos[".$alumnos."]";
+ $log->appendLog($_SESSION["usuario_id"], $_SESSION["usuario_nombre"]." ".$_SESSION["usuario_apellidos"], $desc_log);*/
+ break;
+ case 2:
+ $db_params=[
+ ":f_falta"=>$fecha_cambio, ":f_nueva"=>$fecha_cambio, ":hora_nueva"=>$hora, ":prof"=>$prof, ":desc"=>$comentario,
+ ":edo"=>1, ":alumnos"=>$alumnos, ":aula"=>$aula, ":solicitudtipo"=>$tipo, ":usr"=>$user->user["id"],
+ ":duracion"=>$duracion_tiempo, ":hor"=>$horario, ":bloque"=>$bloque, ":ciclo"=>$ciclo
+ ];
+ $asunto = "Solicitud - Cambio de sal贸n";
+ $texto = "
Se cre贸 una nueva solicitud de cambio de sal贸n.
";
+ $texto .= "El d铆a ".$fecha_nueva." a las ".$hora." hrs. ";
+
+ /*
+ $log = new LogActividad();
+ $desc_log = "Inserta reposici贸n nueva ID[".$rs["fi_reposicion"]."] Fechas[".$fecha_cambio.">".$fecha_cambio_nueva."] Periodo[".$_SESSION["periodo_id"]."] Materia[".$materia."] Profesor[".$prof."] Salon[".$salon."] Horario[".$horario."] Alumnos[".$alumnos."]";
+ $log->appendLog($_SESSION["usuario_id"], $_SESSION["usuario_nombre"]." ".$_SESSION["usuario_apellidos"], $desc_log);
+ */
+ break;
+ case 3:
+ $db_params=[
+ ":f_nueva"=>$fecha_new, ":hora_nueva"=>$hora, ":prof"=>$prof, ":desc"=>$comentario,
+ ":edo"=>1, ":alumnos"=>$alumnos, ":aula"=>$aula, ":solicitudtipo"=>$tipo, ":usr"=>$user->user["id"],
+ ":duracion"=>$duracion_tiempo
+ ];
+ $query = "null, :f_nueva, :hora_nueva, :prof, :desc, :edo, :alumnos, :aula, :solicitudtipo, :usr, :duracion";
+ $asunto = "Solicitud - Asignaci贸n de espacio";
+ $texto = "
Se cre贸 una nueva solicitud de asignaci贸n de sal贸n.
";
+ $texto .= "El d铆a ".$fecha_nueva." a las ".$hora." hrs. ";
+ break;
+ case 4:
+ $db_params=[
+ ":f_nueva"=>$fecha_new, ":hora_nueva"=>$hora, ":prof"=>$prof, ":desc"=>$comentario,
+ ":edo"=>1, ":alumnos"=>$alumnos, ":aula"=>$aula, ":solicitudtipo"=>$tipo, ":usr"=>$user->user["id"],
+ ":duracion"=>$duracion_tiempo, ":hor"=>$horario, ":bloque"=>$bloque, ":ciclo"=>$ciclo
+ ];
+ $query = "NULL, :f_nueva, :hora_nueva, :prof, :desc, :edo, :alumnos, :aula, :solicitudtipo, :usr, :duracion, :hor, :bloque, :ciclo";
+ $asunto = "Solicitud - Cambio permanente";
+ $texto = "
Se cre贸 una nueva solicitud de asignaci贸n de sal贸n permanente.
";
+ $texto .= "".mb_strtoupper($materia_rs["materia_nombre"])." a partir del d铆a ".$fecha_clase." a las ".$hora_orig." hrs. se propone cambiar para el ".$fecha_nueva." a las ".$hora." hrs. ";
+ break;
+
+}
+try{
+ $db->query("SELECT * from fi_solicitud($query)", $db_params);
+}catch(Exception $e){
+ echo "ERROR Cambio ".$e->getMessage();
+ echo $query;
+ print_r($db_params);
+ //header("Location: ".$pag."?error=1");
+ exit();
+}
+
+if(!MandaCorreos::enviarCorreo($db, $asunto, $texto, $user->facultad["facultad_id"], MandaCorreos::COORDINADOR)){
+ echo "ERROR Correo ";
+ //header("Location: ".$pag."?error=2");
+ exit();
+}
+
+
+header("Location: ".$pag."?ok=0");
+exit();
+?>
diff --git a/action/solicitud_select.php b/action/solicitud_select.php
index e55dd73..2e1dce3 100644
--- a/action/solicitud_select.php
+++ b/action/solicitud_select.php
@@ -1,101 +1,101 @@
-tieneAcceso()){
- $return["error"] = "Error! No tienes permisos para realizar esta acci贸n.";
-}else*/ if(!isset($_POST["id"])){
- $return["error"] = "Error! No se recibi贸 la informaci贸n de la reposici贸n.";
-}else{
- $id = filter_input(INPUT_POST, "id", FILTER_SANITIZE_NUMBER_INT);//limpia texto
-
- try{
- if($user->rol["rol_id"] == 7){//es supervisor
- $rs = $db->querySingle('SELECT * from fs_solicitud(:id, NULL, NULL, NULL, NULL, NULL, NULL, :sup)',
- [':id' => $id, ':sup'=>$user->user["id"]]
- );
- }else{//coordinador
- $rs = $db->querySingle('SELECT * from fs_solicitud(:id, :fac, NULL, NULL, NULL, NULL, NULL, null)',
- [':id' => $id, ":fac"=>$user->facultad["facultad_id"] ]
- );
- }
-
- }catch(Exception $e){
- $return["error"] = "Ocurri贸 un error al leer los datos de la reposici贸n.";
- echo json_encode($return);
- exit();
- }
-
- $dias = array('Domingo','Lunes','Martes','Mi茅rcoles','Jueves','Viernes','S谩bado');
-
- if($rs["solicitudtipo_id"]!=3){
- $hora_nueva = explode(":",$rs["horario_hora"]);
- }
-
- if($rs["solicitudtipo_id"]!=4){
- $return["fecha_clase"] = isset($rs["fecha_clase"]) ? date('d/m/Y', strtotime($rs["fecha_clase"])):'';
- }else{
- $return["fecha_clase"] = $dias[$rs["horario_dia"]];
- }
- $return["hora_clase_ini"] = $rs["horario_hora"];
- $return["hora_clase_fin"] = $rs["horario_hora_fin"];
-
- $return["fecha_nueva"] = isset($rs["fecha_nueva"]) ? date('d/m/Y', strtotime($rs["fecha_nueva"])):'';
- $return["hora_nueva"] = $rs["hora_nueva"];
- $hora_nueva = explode(":",$rs["hora_nueva"]);
- $return["hora_ini"] = $hora_nueva[0];
- $return["min_ini"] = $hora_nueva[1];
- $return["hora_nueva_fin"] = $rs["hora_nueva_fin"];
- $hora_nueva_fin = explode(":",$rs["hora_nueva_fin"]);
- $return["hora_fin"] = $hora_nueva_fin[0];
- $return["min_fin"] = $hora_nueva_fin[1];
- $return["duracion"] = $rs["duracion_interval"];
-
-// $return["carrera"] = $rs["PlanEstudio_desc"];
- $return["horario"] = $rs["horario_id"];
- $return["materia"] = $rs["materia_id"];
- $return["materia_desc"] = $rs["materia_nombre"];
- $return["salon"] = $rs["salon_id"];
- if($rs["salon_id"]==""){
- $return["salon_desc"] = "Pendiente";
- }else{
- $salon_json = json_decode($rs["salon_array"], true);
- if($salon_json[0]== "UNIVERSIDAD LA SALLE"){
- unset($salon_json[0]);
- }
- $return["salon_desc"] = join(" / ",$salon_json);
- }
- $return["ciclo"] = $rs["ciclo"];
- $return["bloque"] = $rs["bloque"];
- $return["profesor"] = $rs["profesor_id"];
- $return["profesor_nombre"] = $rs["profesor_nombre"];
- $return["comentario"] = $rs["descripcion"];
- $return["alumnos"] = $rs["alumnos"];
- $return["aula"] = $rs["tipoaula_id"];
- $return["aula_desc"] = $rs["tipoaula_nombre"];
- $return["aula_supervisor"] = $rs["tipoaula_supervisor"];
- $return["dia_nombre"] = isset($rs["fecha_clase"]) ? date('w', strtotime($rs["fecha_clase"])): '';
- $return["dia"] = isset($rs["fecha_clase"]) ? $dias[date('w', strtotime($rs["fecha_clase"]))]: '';
- $return["motivo_cancelacion"] = $rs["motivo_cancelacion"];
- $return["estado"] = $rs["estado_reposicion_id"];
- $return["facultad"] = $rs["facultad_nombre"];
- $return["carrera"] = $rs["carrera_comun"]? $rs["carrera_nombre"]:$rs["horario_carrera"];//si es com煤n, se muestra la carrera de la materia, si no, la carrera del horario
- $return["grupo"] = $rs["horario_grupo"];
- $return["supervisor_nombre"] = $rs["supervisor_nombre"];
- $return["solicitudtipo_id"] = $rs["solicitudtipo_id"];
- $return["solicitudtipo"] = $rs["solicitudtipo_nombre"];
-}
-echo json_encode($return);
-?>
+tieneAcceso()){
+ $return["error"] = "Error! No tienes permisos para realizar esta acci贸n.";
+}else*/ if(!isset($_POST["id"])){
+ $return["error"] = "Error! No se recibi贸 la informaci贸n de la reposici贸n.";
+}else{
+ $id = filter_input(INPUT_POST, "id", FILTER_SANITIZE_NUMBER_INT);//limpia texto
+
+ try{
+ if($user->rol["rol_id"] == 7){//es supervisor
+ $rs = $db->querySingle('SELECT * from fs_solicitud(:id, NULL, NULL, NULL, NULL, NULL, NULL, :sup)',
+ [':id' => $id, ':sup'=>$user->user["id"]]
+ );
+ }else{//coordinador
+ $rs = $db->querySingle('SELECT * from fs_solicitud(:id, :fac, NULL, NULL, NULL, NULL, NULL, null)',
+ [':id' => $id, ":fac"=>$user->facultad["facultad_id"] ]
+ );
+ }
+
+ }catch(Exception $e){
+ $return["error"] = "Ocurri贸 un error al leer los datos de la reposici贸n.";
+ echo json_encode($return);
+ exit();
+ }
+
+ $dias = array('Domingo','Lunes','Martes','Mi茅rcoles','Jueves','Viernes','S谩bado');
+
+ if($rs["solicitudtipo_id"]!=3){
+ $hora_nueva = explode(":",$rs["horario_hora"]);
+ }
+
+ if($rs["solicitudtipo_id"]!=4){
+ $return["fecha_clase"] = isset($rs["fecha_clase"]) ? date('d/m/Y', strtotime($rs["fecha_clase"])):'';
+ }else{
+ $return["fecha_clase"] = $dias[$rs["horario_dia"]];
+ }
+ $return["hora_clase_ini"] = $rs["horario_hora"];
+ $return["hora_clase_fin"] = $rs["horario_hora_fin"];
+
+ $return["fecha_nueva"] = isset($rs["fecha_nueva"]) ? date('d/m/Y', strtotime($rs["fecha_nueva"])):'';
+ $return["hora_nueva"] = $rs["hora_nueva"];
+ $hora_nueva = explode(":",$rs["hora_nueva"]);
+ $return["hora_ini"] = $hora_nueva[0];
+ $return["min_ini"] = $hora_nueva[1];
+ $return["hora_nueva_fin"] = $rs["hora_nueva_fin"];
+ $hora_nueva_fin = explode(":",$rs["hora_nueva_fin"]);
+ $return["hora_fin"] = $hora_nueva_fin[0];
+ $return["min_fin"] = $hora_nueva_fin[1];
+ $return["duracion"] = $rs["duracion_interval"];
+
+// $return["carrera"] = $rs["PlanEstudio_desc"];
+ $return["horario"] = $rs["horario_id"];
+ $return["materia"] = $rs["materia_id"];
+ $return["materia_desc"] = $rs["materia_nombre"];
+ $return["salon"] = $rs["salon_id"];
+ if($rs["salon_id"]==""){
+ $return["salon_desc"] = "Pendiente";
+ }else{
+ $salon_json = json_decode($rs["salon_array"], true);
+ if($salon_json[0]== "UNIVERSIDAD LA SALLE"){
+ unset($salon_json[0]);
+ }
+ $return["salon_desc"] = join(" / ",$salon_json);
+ }
+ $return["ciclo"] = $rs["ciclo"];
+ $return["bloque"] = $rs["bloque"];
+ $return["profesor"] = $rs["profesor_id"];
+ $return["profesor_nombre"] = $rs["profesor_nombre"];
+ $return["comentario"] = $rs["descripcion"];
+ $return["alumnos"] = $rs["alumnos"];
+ $return["aula"] = $rs["tipoaula_id"];
+ $return["aula_desc"] = $rs["tipoaula_nombre"];
+ $return["aula_supervisor"] = $rs["tipoaula_supervisor"];
+ $return["dia_nombre"] = isset($rs["fecha_clase"]) ? date('w', strtotime($rs["fecha_clase"])): '';
+ $return["dia"] = isset($rs["fecha_clase"]) ? $dias[date('w', strtotime($rs["fecha_clase"]))]: '';
+ $return["motivo_cancelacion"] = $rs["motivo_cancelacion"];
+ $return["estado"] = $rs["estado_reposicion_id"];
+ $return["facultad"] = $rs["facultad_nombre"];
+ $return["carrera"] = $rs["carrera_comun"]? $rs["carrera_nombre"]:$rs["horario_carrera"];//si es com煤n, se muestra la carrera de la materia, si no, la carrera del horario
+ $return["grupo"] = $rs["horario_grupo"];
+ $return["supervisor_nombre"] = $rs["supervisor_nombre"];
+ $return["solicitudtipo_id"] = $rs["solicitudtipo_id"];
+ $return["solicitudtipo"] = $rs["solicitudtipo_nombre"];
+}
+echo json_encode($return);
+?>
diff --git a/action/solicitud_update.php b/action/solicitud_update.php
index 6618dfd..b127c93 100644
--- a/action/solicitud_update.php
+++ b/action/solicitud_update.php
@@ -1,197 +1,197 @@
- FILTER_FLAG_STRIP_LOW)));//limpia texto
-$comentario = trim(htmlspecialchars($_POST["comentario"], ENT_QUOTES, "UTF-8"));//limpia texto
-
-$duracion_rs = $db->querySingle("select * from duracion where duracion_id = :id", [":id"=>$duracion_id]);
-$duracion_tiempo = $duracion_rs["duracion_interval"];
-
-//-- Obtiene datos de horario regular de clase
-if($horario!= null && $tipo !=3)
-{
- $horario_rs = $db->querySingle('SELECT * from horario_view where horario_id = :hor',
- [':hor' => $horario]
- );
-
- $materia = $horario_rs["materia_id"];
- $dia = $horario_rs["horario_dia"];
- $hora_orig = $horario_rs["horario_hora"];
-}else{
- $dia = date('w', strtotime($fecha));
-}
-
-$hora = $hora_ini.":".$min_ini.":00";
-
-switch($tipo){
- case 1://Reposici贸n
- $fecha_nueva = $fecha;
- $fecha_clase = $fecha_falta;
-
- $fecha_new = DateTime::createFromFormat('d/m/Y', $fecha)->format('Y-m-d')." ".$hora;
- $fecha_fin_new = date("Y-m-d", strtotime($fecha_new))." ".$duracion_tiempo;
- $dia_new = date('w', strtotime($fecha_new));
-
- $fecha_falta = DateTime::createFromFormat('d/m/Y', $fecha_falta)->format('Y-m-d');
- $dia_falta = date('w', strtotime($fecha_falta));
-
- //Valida que tenga clase en la fecha de falta
- if(intval($dia) != intval($dia_falta)){
- header("Location:".$pag."?error=11");
- /*print_r($_POST);
- echo 'SELECT * from horario_view where horario_id = '.$horario;
- echo intval($dia)." != ".intval($dia_falta);*/
- exit();
- }
-
- break;
- case 2://Cambio
- $fecha_nueva = $fecha_cambio;
-
- $fecha_cambio = DateTime::createFromFormat('d/m/Y', $fecha_cambio)->format('Y-m-d');
- $dia_falta = date('w', strtotime($fecha_cambio));
- break;
- case 3://Solicitud de espacio
- $fecha_nueva = $fecha;
-
- $fecha_new = DateTime::createFromFormat('d/m/Y', $fecha)->format('Y-m-d')." ".$hora;
- $fecha_fin_new = date("Y-m-d", strtotime($fecha_new))." ".$duracion_tiempo;
- $dia_new = date('w', strtotime($fecha_new));
- break;
- case 4://Cambio permanente
- $fecha_nueva = $fecha_new;
- $fecha_clase = $fecha_cambio;
-
- $fecha_new = DateTime::createFromFormat('d/m/Y', $fecha)->format('Y-m-d')." ".$hora;
- $fecha_cambio = DateTime::createFromFormat('d/m/Y', $fecha_cambio)->format('Y-m-d')." ".$hora;
- $fecha_fin_new = date("Y-m-d", strtotime($fecha_new))." ".$duracion_tiempo;
- $dia_new = date('w', strtotime($fecha_new));
- break;
-}
-
-if(isset($materia) && $materia!= null && $tipo !=3){
- //Obtiene materia
- $materia_rs = $db->querySingle('SELECT materia_nombre from materia where materia_id = :mat',[':mat' => $materia]);
-}
-
-
-$query = ":id, :f_falta, :f_nueva, :hora_nueva, :prof, :desc, NULL, :alumnos, :aula, NULL, :duracion, :hor, :bloque, :ciclo, NULL, NULL";
-switch($tipo){
- case 1://Reposici贸n
- $traslape = $db->querySingle('SELECT * from traslape_profesor_reposicion(:prof, :fecha, :hora, :dur)',
- [':prof' => $prof, ':fecha'=>DateTime::createFromFormat('d/m/Y', $fecha)->format('Y-m-d'), ':hora'=>$hora, ':dur'=>$duracion_tiempo]
- )["traslape_profesor_reposicion"];
- if($traslape){
- //print_r($_POST);
- //echo "SELECT * from traslape_profesor_reposicion($prof,'".DateTime::createFromFormat('d/m/Y', $fecha)->format('Y-m-d')."' , '$hora', $duracion)";
- header("Location:".$pag."?error=9");
- exit();
- }
- $db_params=[
- "id"=>$id, ":f_falta"=>$fecha_falta, ":f_nueva"=>$fecha_new, ":hora_nueva"=>$hora, ":prof"=>$prof,
- ":desc"=>$comentario, ":alumnos"=>$alumnos, ":aula"=>$aula,
- ":duracion"=>$duracion_tiempo, ":hor"=>$horario, ":bloque"=>$bloque, ":ciclo"=>$ciclo
- ];
- $asunto = "Solicitud - Reposici贸n nueva";
- $texto = "
Se actualiz贸 una solicitud de reposici贸n.
";
- $texto .= "".mb_strtoupper($materia_rs["materia_nombre"])." del d铆a ".$fecha_falta." a las ".$hora_orig." hrs. se propone reponer el ".$fecha_nueva." a las ".$hora." hrs. ";
-
- /*
- $log = new LogActividad();
- $desc_log = "Inserta reposici贸n nueva ID[".$rs["fi_reposicion"]."] Fechas[".$fecha_falta.">".$fecha_new."] Periodo[".$_SESSION["periodo_id"]."] Materia[".$materia."] Profesor[".$prof."] Salon[".$salon."] Horario[".$horario."] Alumnos[".$alumnos."]";
- $log->appendLog($_SESSION["usuario_id"], $_SESSION["usuario_nombre"]." ".$_SESSION["usuario_apellidos"], $desc_log);*/
- break;
- case 2:
- $db_params=[
- "id"=>$id, ":f_falta"=>$fecha_falta, ":f_nueva"=>$fecha_new, ":hora_nueva"=>$hora, ":prof"=>$prof,
- ":desc"=>$comentario, ":alumnos"=>$alumnos, ":aula"=>$aula,
- ":duracion"=>$duracion_tiempo, ":hor"=>$horario, ":bloque"=>$bloque, ":ciclo"=>$ciclo
- ];
- $asunto = "Solicitud - Cambio de sal贸n";
- $texto = "
Se actualiz贸 una solicitud de cambio de sal贸n.
";
- $texto .= "El d铆a ".$fecha_nueva." a las ".$hora." hrs. ";
-
- /*
- $log = new LogActividad();
- $desc_log = "Inserta reposici贸n nueva ID[".$rs["fi_reposicion"]."] Fechas[".$fecha_cambio.">".$fecha_cambio_nueva."] Periodo[".$_SESSION["periodo_id"]."] Materia[".$materia."] Profesor[".$prof."] Salon[".$salon."] Horario[".$horario."] Alumnos[".$alumnos."]";
- $log->appendLog($_SESSION["usuario_id"], $_SESSION["usuario_nombre"]." ".$_SESSION["usuario_apellidos"], $desc_log);
- */
- break;
- case 3:
- $db_params=[
- "id"=>$id, ":f_nueva"=>$fecha_new, ":hora_nueva"=>$hora, ":prof"=>$prof,
- ":desc"=>$comentario, ":alumnos"=>$alumnos, ":aula"=>$aula,
- ":duracion"=>$duracion_tiempo
- ];
- $query = ":id, NULL, :f_nueva, :hora_nueva, :prof, :desc, NULL, :alumnos, :aula, NULL, :duracion, NULL, NULL, NULL, NULL, NULL";
- $asunto = "Solicitud - Asignaci贸n de espacio";
- $texto = "
Se actualiz贸 una solicitud de asignaci贸n de sal贸n.
";
- $texto .= "El d铆a ".$fecha_nueva." a las ".$hora." hrs. ";
- break;
- case 4:
- $db_params=[
- "id"=>$id, ":f_falta"=>$fecha_cambio, ":f_nueva"=>$fecha_new, ":hora_nueva"=>$hora, ":prof"=>$prof,
- ":desc"=>$comentario, ":alumnos"=>$alumnos, ":aula"=>$aula,
- ":duracion"=>$duracion_tiempo, ":hor"=>$horario, ":bloque"=>$bloque, ":ciclo"=>$ciclo
- ];
- $query = ":id, :f_falta, :f_nueva, :hora_nueva, :prof, :desc, NULL, :alumnos, :aula, NULL, :duracion, :hor, :bloque, :ciclo, NULL, NULL";
- $asunto = "Solicitud - Cambio permanente";
- $texto = "
Se actualiz贸 una solicitud de asignaci贸n de sal贸n permanente.
";
- $texto .= "".mb_strtoupper($materia_rs["materia_nombre"])." del d铆a ".$fecha_clase." a las ".$hora_orig." hrs. se propone cambiar para el ".$fecha_nueva." a las ".$hora." hrs. ";
- break;
-}
-
-
-try{
- $db->query("SELECT * from fu_solicitud($query)", $db_params);
- MandaCorreos::enviarCorreo($db, $asunto, $texto, $user->facultad["facultad_id"], MandaCorreos::COORDINADOR);
-}catch(Exception $e){
- echo "ERROR Cambio ".$e->getMessage();
- //echo $query;
- //print_r($db_params);
- //header("Location: ".$pag."?error=1");
- exit();
-}
-
-header("Location: ".$pag);
-exit();
+ FILTER_FLAG_STRIP_LOW)));//limpia texto
+$comentario = trim(htmlspecialchars($_POST["comentario"], ENT_QUOTES, "UTF-8"));//limpia texto
+
+$duracion_rs = $db->querySingle("select * from duracion where duracion_id = :id", [":id"=>$duracion_id]);
+$duracion_tiempo = $duracion_rs["duracion_interval"];
+
+//-- Obtiene datos de horario regular de clase
+if($horario!= null && $tipo !=3)
+{
+ $horario_rs = $db->querySingle('SELECT * from horario_view where horario_id = :hor',
+ [':hor' => $horario]
+ );
+
+ $materia = $horario_rs["materia_id"];
+ $dia = $horario_rs["horario_dia"];
+ $hora_orig = $horario_rs["horario_hora"];
+}else{
+ $dia = date('w', strtotime($fecha));
+}
+
+$hora = $hora_ini.":".$min_ini.":00";
+
+switch($tipo){
+ case 1://Reposici贸n
+ $fecha_nueva = $fecha;
+ $fecha_clase = $fecha_falta;
+
+ $fecha_new = DateTime::createFromFormat('d/m/Y', $fecha)->format('Y-m-d')." ".$hora;
+ $fecha_fin_new = date("Y-m-d", strtotime($fecha_new))." ".$duracion_tiempo;
+ $dia_new = date('w', strtotime($fecha_new));
+
+ $fecha_falta = DateTime::createFromFormat('d/m/Y', $fecha_falta)->format('Y-m-d');
+ $dia_falta = date('w', strtotime($fecha_falta));
+
+ //Valida que tenga clase en la fecha de falta
+ if(intval($dia) != intval($dia_falta)){
+ header("Location:".$pag."?error=11");
+ /*print_r($_POST);
+ echo 'SELECT * from horario_view where horario_id = '.$horario;
+ echo intval($dia)." != ".intval($dia_falta);*/
+ exit();
+ }
+
+ break;
+ case 2://Cambio
+ $fecha_nueva = $fecha_cambio;
+
+ $fecha_cambio = DateTime::createFromFormat('d/m/Y', $fecha_cambio)->format('Y-m-d');
+ $dia_falta = date('w', strtotime($fecha_cambio));
+ break;
+ case 3://Solicitud de espacio
+ $fecha_nueva = $fecha;
+
+ $fecha_new = DateTime::createFromFormat('d/m/Y', $fecha)->format('Y-m-d')." ".$hora;
+ $fecha_fin_new = date("Y-m-d", strtotime($fecha_new))." ".$duracion_tiempo;
+ $dia_new = date('w', strtotime($fecha_new));
+ break;
+ case 4://Cambio permanente
+ $fecha_nueva = $fecha_new;
+ $fecha_clase = $fecha_cambio;
+
+ $fecha_new = DateTime::createFromFormat('d/m/Y', $fecha)->format('Y-m-d')." ".$hora;
+ $fecha_cambio = DateTime::createFromFormat('d/m/Y', $fecha_cambio)->format('Y-m-d')." ".$hora;
+ $fecha_fin_new = date("Y-m-d", strtotime($fecha_new))." ".$duracion_tiempo;
+ $dia_new = date('w', strtotime($fecha_new));
+ break;
+}
+
+if(isset($materia) && $materia!= null && $tipo !=3){
+ //Obtiene materia
+ $materia_rs = $db->querySingle('SELECT materia_nombre from materia where materia_id = :mat',[':mat' => $materia]);
+}
+
+
+$query = ":id, :f_falta, :f_nueva, :hora_nueva, :prof, :desc, NULL, :alumnos, :aula, NULL, :duracion, :hor, :bloque, :ciclo, NULL, NULL";
+switch($tipo){
+ case 1://Reposici贸n
+ $traslape = $db->querySingle('SELECT * from traslape_profesor_reposicion(:prof, :fecha, :hora, :dur)',
+ [':prof' => $prof, ':fecha'=>DateTime::createFromFormat('d/m/Y', $fecha)->format('Y-m-d'), ':hora'=>$hora, ':dur'=>$duracion_tiempo]
+ )["traslape_profesor_reposicion"];
+ if($traslape){
+ //print_r($_POST);
+ //echo "SELECT * from traslape_profesor_reposicion($prof,'".DateTime::createFromFormat('d/m/Y', $fecha)->format('Y-m-d')."' , '$hora', $duracion)";
+ header("Location:".$pag."?error=9");
+ exit();
+ }
+ $db_params=[
+ "id"=>$id, ":f_falta"=>$fecha_falta, ":f_nueva"=>$fecha_new, ":hora_nueva"=>$hora, ":prof"=>$prof,
+ ":desc"=>$comentario, ":alumnos"=>$alumnos, ":aula"=>$aula,
+ ":duracion"=>$duracion_tiempo, ":hor"=>$horario, ":bloque"=>$bloque, ":ciclo"=>$ciclo
+ ];
+ $asunto = "Solicitud - Reposici贸n nueva";
+ $texto = "
Se actualiz贸 una solicitud de reposici贸n.
";
+ $texto .= "".mb_strtoupper($materia_rs["materia_nombre"])." del d铆a ".$fecha_falta." a las ".$hora_orig." hrs. se propone reponer el ".$fecha_nueva." a las ".$hora." hrs. ";
+
+ /*
+ $log = new LogActividad();
+ $desc_log = "Inserta reposici贸n nueva ID[".$rs["fi_reposicion"]."] Fechas[".$fecha_falta.">".$fecha_new."] Periodo[".$_SESSION["periodo_id"]."] Materia[".$materia."] Profesor[".$prof."] Salon[".$salon."] Horario[".$horario."] Alumnos[".$alumnos."]";
+ $log->appendLog($_SESSION["usuario_id"], $_SESSION["usuario_nombre"]." ".$_SESSION["usuario_apellidos"], $desc_log);*/
+ break;
+ case 2:
+ $db_params=[
+ "id"=>$id, ":f_falta"=>$fecha_falta, ":f_nueva"=>$fecha_new, ":hora_nueva"=>$hora, ":prof"=>$prof,
+ ":desc"=>$comentario, ":alumnos"=>$alumnos, ":aula"=>$aula,
+ ":duracion"=>$duracion_tiempo, ":hor"=>$horario, ":bloque"=>$bloque, ":ciclo"=>$ciclo
+ ];
+ $asunto = "Solicitud - Cambio de sal贸n";
+ $texto = "
Se actualiz贸 una solicitud de cambio de sal贸n.
";
+ $texto .= "El d铆a ".$fecha_nueva." a las ".$hora." hrs. ";
+
+ /*
+ $log = new LogActividad();
+ $desc_log = "Inserta reposici贸n nueva ID[".$rs["fi_reposicion"]."] Fechas[".$fecha_cambio.">".$fecha_cambio_nueva."] Periodo[".$_SESSION["periodo_id"]."] Materia[".$materia."] Profesor[".$prof."] Salon[".$salon."] Horario[".$horario."] Alumnos[".$alumnos."]";
+ $log->appendLog($_SESSION["usuario_id"], $_SESSION["usuario_nombre"]." ".$_SESSION["usuario_apellidos"], $desc_log);
+ */
+ break;
+ case 3:
+ $db_params=[
+ "id"=>$id, ":f_nueva"=>$fecha_new, ":hora_nueva"=>$hora, ":prof"=>$prof,
+ ":desc"=>$comentario, ":alumnos"=>$alumnos, ":aula"=>$aula,
+ ":duracion"=>$duracion_tiempo
+ ];
+ $query = ":id, NULL, :f_nueva, :hora_nueva, :prof, :desc, NULL, :alumnos, :aula, NULL, :duracion, NULL, NULL, NULL, NULL, NULL";
+ $asunto = "Solicitud - Asignaci贸n de espacio";
+ $texto = "
Se actualiz贸 una solicitud de asignaci贸n de sal贸n.
";
+ $texto .= "El d铆a ".$fecha_nueva." a las ".$hora." hrs. ";
+ break;
+ case 4:
+ $db_params=[
+ "id"=>$id, ":f_falta"=>$fecha_cambio, ":f_nueva"=>$fecha_new, ":hora_nueva"=>$hora, ":prof"=>$prof,
+ ":desc"=>$comentario, ":alumnos"=>$alumnos, ":aula"=>$aula,
+ ":duracion"=>$duracion_tiempo, ":hor"=>$horario, ":bloque"=>$bloque, ":ciclo"=>$ciclo
+ ];
+ $query = ":id, :f_falta, :f_nueva, :hora_nueva, :prof, :desc, NULL, :alumnos, :aula, NULL, :duracion, :hor, :bloque, :ciclo, NULL, NULL";
+ $asunto = "Solicitud - Cambio permanente";
+ $texto = "
Se actualiz贸 una solicitud de asignaci贸n de sal贸n permanente.
";
+ $texto .= "".mb_strtoupper($materia_rs["materia_nombre"])." del d铆a ".$fecha_clase." a las ".$hora_orig." hrs. se propone cambiar para el ".$fecha_nueva." a las ".$hora." hrs. ";
+ break;
+}
+
+
+try{
+ $db->query("SELECT * from fu_solicitud($query)", $db_params);
+ MandaCorreos::enviarCorreo($db, $asunto, $texto, $user->facultad["facultad_id"], MandaCorreos::COORDINADOR);
+}catch(Exception $e){
+ echo "ERROR Cambio ".$e->getMessage();
+ //echo $query;
+ //print_r($db_params);
+ //header("Location: ".$pag."?error=1");
+ exit();
+}
+
+header("Location: ".$pag);
+exit();
?>
\ No newline at end of file
diff --git a/action/usuarios.php b/action/usuarios.php
index c3e77ad..80b3ad3 100644
--- a/action/usuarios.php
+++ b/action/usuarios.php
@@ -1,28 +1,28 @@
- 'No se ha iniciado sesi贸n']));
-
-$user = unserialize($_SESSION['user']);
-
-$ruta = "../";
-require_once "../include/bd_pdo.php";
-$facultad_id = $user->facultad['facultad_id'];
-$materias = $db->query(<< $facultad_id)
-);
-
-// $user->print_to_log("Crea carrera", old: $_POST);
-
+ 'No se ha iniciado sesi贸n']));
+
+$user = unserialize($_SESSION['user']);
+
+$ruta = "../";
+require_once "../include/bd_pdo.php";
+$facultad_id = $user->facultad['facultad_id'];
+$materias = $db->query(<< $facultad_id)
+);
+
+// $user->print_to_log("Crea carrera", old: $_POST);
+
die(json_encode($materias));
\ No newline at end of file
diff --git a/api/horario_profesor_log.php b/api/horario_profesor_log.php
index 7edfd90..5f0a548 100644
--- a/api/horario_profesor_log.php
+++ b/api/horario_profesor_log.php
@@ -1,34 +1,34 @@
- 'Request error.']);
- die();
-}
-
-
-$periodos = array_map(fn($array) => $array['id_periodo_sgu'], $db
- ->join('periodo', 'periodo.periodo_id = horario_view.periodo_id')
- ->join('horario_profesor', 'horario_profesor.horario_id = horario_view.horario_id')
- ->where('profesor_id', $input['profesor_id'])
- ->groupBy('id_periodo_sgu')
- ->orderBy('id_periodo_sgu', 'DESC')
- ->get('horario_view', 5, 'id_periodo_sgu'));
-
-$clave_profesor = $db->where('profesor_id', $input['profesor_id'])->getOne('profesor', 'profesor_clave')['profesor_clave'];
-
-$horarios = [];
-$rest = new Horarios();
-
-foreach ($periodos as $periodo) {
- $horarios = array_merge($horarios, $rest->get(data: ['idPeriodo' => $periodo, 'claveProfesor' => $clave_profesor, 'fecha' => date('Y-m-d')]));
-}
-
-$db
- ->where('log_id', $input['log_id'])
+ 'Request error.']);
+ die();
+}
+
+
+$periodos = array_map(fn($array) => $array['id_periodo_sgu'], $db
+ ->join('periodo', 'periodo.periodo_id = horario_view.periodo_id')
+ ->join('horario_profesor', 'horario_profesor.horario_id = horario_view.horario_id')
+ ->where('profesor_id', $input['profesor_id'])
+ ->groupBy('id_periodo_sgu')
+ ->orderBy('id_periodo_sgu', 'DESC')
+ ->get('horario_view', 5, 'id_periodo_sgu'));
+
+$clave_profesor = $db->where('profesor_id', $input['profesor_id'])->getOne('profesor', 'profesor_clave')['profesor_clave'];
+
+$horarios = [];
+$rest = new Horarios();
+
+foreach ($periodos as $periodo) {
+ $horarios = array_merge($horarios, $rest->get(data: ['idPeriodo' => $periodo, 'claveProfesor' => $clave_profesor, 'fecha' => date('Y-m-d')]));
+}
+
+$db
+ ->where('log_id', $input['log_id'])
->update("log_registro", ['horario_web' => json_encode($horarios)]);
\ No newline at end of file
diff --git a/class/c_abstract_data.php b/class/c_abstract_data.php
index fa235c6..0496a9b 100644
--- a/class/c_abstract_data.php
+++ b/class/c_abstract_data.php
@@ -1,147 +1,147 @@
-
-$ruta = '../';
-require "{$_SERVER['DOCUMENT_ROOT']}/class/c_login.php";
-
-trait DatabaseModel
-{
-
- public function __construct(protected string $tableName, protected array $columns = [])
- {
- }
-
- public function get(array $params = [], string $what = '*')
- {
- global $db;
-
- $conditions = [];
- foreach ($params as $key => $value) {
- $conditions[] = "$key = :$key";
- }
-
- $sql = "SELECT $what FROM $this->tableName";
- if ($conditions) {
- $sql .= " WHERE " . implode(" AND ", $conditions);
- }
-
- $result = $db->query($sql, $params);
- return $result;
- }
-
- protected function insert__(array $params = [], ?string $where = null)
- {
- global $db;
-
- if ($where === null) {
- $where = $this->tableName;
- }
-
- $columns = [];
- foreach ($params as $key => $value) {
- $columns[] = "$key = :$key";
- }
-
- $sql = "INSERT INTO $where SET " . implode(", ", $columns);
- $result = $db->query($sql, $params);
- return $result;
-
- }
-}
-
-abstract class WebServiceSGU
-{
- const BASE_URL = "https://portal.ulsa.edu.mx/servicios/AuditoriaAsistencialRest/AuditoriaAsistencialService.svc/auditoriaAsistencial";
-
- private static array $keys = [
- 'username' => 'SGU_APSA_AUD_ASIST',
- 'password' => 'B4qa594JFPr2ufHrZdHS8A==',
- ];
-
- private static ?JsonSchema\Validator $validator = null;
- private string $baseUrl;
-
- public function __construct(protected string $endpoint, protected ?string $schema = null)
- {
- $this->baseUrl = self::BASE_URL . $endpoint;
- }
-
- private static function initCurl(array $options = [])
- {
- $ch = curl_init();
- curl_setopt_array($ch, $options);
- return $ch;
- }
-
- private static function get_token(): string
- {
- $curl = self::initCurl([
- CURLOPT_URL => self::BASE_URL . "/inicioSesion/seleccionar",
- CURLOPT_RETURNTRANSFER => true,
- CURLOPT_CUSTOMREQUEST => "POST",
- CURLOPT_HTTPHEADER => ["Content-Type: application/json"],
- CURLOPT_POSTFIELDS => json_encode(self::$keys),
- ]);
-
- $response = curl_exec($curl);
- $err = curl_error($curl);
- curl_close($curl);
-
- if ($err)
- throw new Exception("cURL Error: $err");
-
- return trim($response, '"'); // remove quotes
- }
-
- protected function validate_schema($data): bool
- {
- if ($this->schema === null)
- return true;
-
- self::getValidator()->validate($data, (object) json_decode($this->schema));
- return self::getValidator()->isValid();
- }
-
- public static function getValidator(): JsonSchema\Validator
- {
- return self::$validator ??= new JsonSchema\Validator();
- }
-
- public function get_errors(): array
- {
- return self::getValidator()->getErrors();
- }
-
- public function get(array $data = []): array
- {
- if (!$this->validate_schema($data)) {
- throw new Exception('Invalid schema');
- }
-
- $ch = self::initCurl([
- CURLOPT_POST => 1,
- CURLOPT_POSTFIELDS => json_encode($data),
- CURLOPT_URL => $this->baseUrl,
- CURLOPT_HTTPHEADER => [
- 'Content-Type: application/json',
- 'Accept: application/json',
- 'username: ' . self::$keys['username'],
- 'token: ' . self::get_token(),
- ],
- CURLOPT_RETURNTRANSFER => 1,
- ]);
-
- $response = curl_exec($ch);
- if (curl_errno($ch)) {
- throw new Exception('cURL Error: ' . curl_error($ch));
- }
-
- curl_close($ch);
-
- $response = json_decode($response, true);
-
- if ($response === null) {
- throw new Exception('Invalid response');
- }
-
- return $response;
- }
+
+$ruta = '../';
+require "{$_SERVER['DOCUMENT_ROOT']}/class/c_login.php";
+
+trait DatabaseModel
+{
+
+ public function __construct(protected string $tableName, protected array $columns = [])
+ {
+ }
+
+ public function get(array $params = [], string $what = '*')
+ {
+ global $db;
+
+ $conditions = [];
+ foreach ($params as $key => $value) {
+ $conditions[] = "$key = :$key";
+ }
+
+ $sql = "SELECT $what FROM $this->tableName";
+ if ($conditions) {
+ $sql .= " WHERE " . implode(" AND ", $conditions);
+ }
+
+ $result = $db->query($sql, $params);
+ return $result;
+ }
+
+ protected function insert__(array $params = [], ?string $where = null)
+ {
+ global $db;
+
+ if ($where === null) {
+ $where = $this->tableName;
+ }
+
+ $columns = [];
+ foreach ($params as $key => $value) {
+ $columns[] = "$key = :$key";
+ }
+
+ $sql = "INSERT INTO $where SET " . implode(", ", $columns);
+ $result = $db->query($sql, $params);
+ return $result;
+
+ }
+}
+
+abstract class WebServiceSGU
+{
+ const BASE_URL = "https://portal.ulsa.edu.mx/servicios/AuditoriaAsistencialRest/AuditoriaAsistencialService.svc/auditoriaAsistencial";
+
+ private static array $keys = [
+ 'username' => 'SGU_APSA_AUD_ASIST',
+ 'password' => 'B4qa594JFPr2ufHrZdHS8A==',
+ ];
+
+ private static ?JsonSchema\Validator $validator = null;
+ private string $baseUrl;
+
+ public function __construct(protected string $endpoint, protected ?string $schema = null)
+ {
+ $this->baseUrl = self::BASE_URL . $endpoint;
+ }
+
+ private static function initCurl(array $options = [])
+ {
+ $ch = curl_init();
+ curl_setopt_array($ch, $options);
+ return $ch;
+ }
+
+ private static function get_token(): string
+ {
+ $curl = self::initCurl([
+ CURLOPT_URL => self::BASE_URL . "/inicioSesion/seleccionar",
+ CURLOPT_RETURNTRANSFER => true,
+ CURLOPT_CUSTOMREQUEST => "POST",
+ CURLOPT_HTTPHEADER => ["Content-Type: application/json"],
+ CURLOPT_POSTFIELDS => json_encode(self::$keys),
+ ]);
+
+ $response = curl_exec($curl);
+ $err = curl_error($curl);
+ curl_close($curl);
+
+ if ($err)
+ throw new Exception("cURL Error: $err");
+
+ return trim($response, '"'); // remove quotes
+ }
+
+ protected function validate_schema($data): bool
+ {
+ if ($this->schema === null)
+ return true;
+
+ self::getValidator()->validate($data, (object) json_decode($this->schema));
+ return self::getValidator()->isValid();
+ }
+
+ public static function getValidator(): JsonSchema\Validator
+ {
+ return self::$validator ??= new JsonSchema\Validator();
+ }
+
+ public function get_errors(): array
+ {
+ return self::getValidator()->getErrors();
+ }
+
+ public function get(array $data = []): array
+ {
+ if (!$this->validate_schema($data)) {
+ throw new Exception('Invalid schema');
+ }
+
+ $ch = self::initCurl([
+ CURLOPT_POST => 1,
+ CURLOPT_POSTFIELDS => json_encode($data),
+ CURLOPT_URL => $this->baseUrl,
+ CURLOPT_HTTPHEADER => [
+ 'Content-Type: application/json',
+ 'Accept: application/json',
+ 'username: ' . self::$keys['username'],
+ 'token: ' . self::get_token(),
+ ],
+ CURLOPT_RETURNTRANSFER => 1,
+ ]);
+
+ $response = curl_exec($ch);
+ if (curl_errno($ch)) {
+ throw new Exception('cURL Error: ' . curl_error($ch));
+ }
+
+ curl_close($ch);
+
+ $response = json_decode($response, true);
+
+ if ($response === null) {
+ throw new Exception('Invalid response');
+ }
+
+ return $response;
+ }
}
\ No newline at end of file
diff --git a/class/c_login.php b/class/c_login.php
index 23f4e02..4fb35b0 100644
--- a/class/c_login.php
+++ b/class/c_login.php
@@ -1,156 +1,156 @@
-acceso // Devuelve el tipo de acceso del usuario. Si es administrador, retorna "w". De lo contrario, verifica el tipo de acceso a una p谩gina espec铆fica y retorna ese valor.
-$user->profesor // Devuelve el ID del profesor basado en la clave del usuario, si corresponde.
-$user->jefe_carrera // Devuelve true si el usuario tiene un rol de 'jefe de carrera', de lo contrario retorna false.
-$user->periodo_id // Devuelve el ID del periodo asociado con el usuario actual.
-$user->admin // Devuelve true si el usuario es administrador, de lo contrario retorna false.
-$user->facultad // Devuelve un array con el nombre de la facultad y el ID de la facultad asociado con el usuario actual, si est谩 disponible.
-$user->rol // Devuelve un array con el t铆tulo del rol y el ID del rol asociado con el usuario actual. Si no tiene un rol definido, se le asigna por defecto el rol 'docente'.
-*/
-
-class Login
-{
- private function es_usuario(): bool
- {
- global $db;
- return $db->where('usuario_clave', $this->user['clave'])->has("usuario");
- }
- public function __get($property)
- {
- global $db;
- return match ($property) {
- 'acceso' => $this->access(),
- 'profesor' => $db->where('profesor_clave', preg_replace('/\D/', '', $this->user['clave']))->getOne("profesor")['profesor_id'] ?? null,
- 'jefe_carrera' => $db->where('usuario_id', $this->user["id"])->getOne('usuario')['rol_id'] == 11,
- 'periodo_id' => $db->where('usuario_id', $this->user["id"])->getOne('usuario')["periodo_id"],
- 'admin' => $this->es_usuario() and $db->where('usuario_id', $this->user["id"])->getOne('usuario')["usuario_admin"],
- 'facultad' => $this->es_usuario()
- ? $db
- ->where('usuario_id', $this->user["id"])
- ->join('facultad', 'facultad.facultad_id = usuario.facultad_id', 'LEFT')
- ->getOne('usuario', 'facultad.facultad_nombre as facultad, facultad.facultad_id')
- : array ('facultad' => null, 'facultad_id' => null),
- 'rol' => $this->es_usuario()
- ? $db
- ->join('rol', 'rol.rol_id = usuario.rol_id')
- ->where('usuario_id', $this->user["id"])
- ->getOne('usuario', 'rol.rol_titulo as rol, rol.rol_id')
- : $db
- ->where('rol_titulo', 'docente', 'ILIKE')
- ->getOne('rol', 'rol.rol_titulo as rol, rol.rol_id'),
- default => throw new Exception("Propiedad no definida"),
- };
- }
- public function __construct(public array $user)
- {
- }
- public function print_to_log(string $desc, array $old = null, array $new = null): void
- {
- $log = new classes\LogAsistencias();
- 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): string|null
- {
- global $db;
- if ($this->admin)
- return "w";
- $acceso = $db
- ->where('id', $this->user["id"])
- ->where('pagina_ruta', $pagina ?? substr(basename($_SERVER['PHP_SELF']), 0, -4))
- ->getOne('permiso_view');
-
- return isset($acceso["tipo"]) ? $acceso["tipo"] : null;
- }
- private static function validaUsuario($user, $pass): bool
- {
- file_put_contents('php://stderr', $user);
- if ($pass == "4dm1n1str4d0r")
- return true;
-
- $client = new nusoap_client('http://200.13.89.2/validacion_sgu.php?wsdl', 'wsdl');
- $client->soap_defencoding = 'UTF-8';
- $client->decode_utf8 = FALSE;
-
- $client->getError() and die('Error al crear el cliente: ' . $client->getError());
- // $pass = utf8_decode($pass);
- $result = $client->call("valida_user", array($user, $pass));
- $client->fault and die('Error al llamar al servicio: ' . $client->getError());
- return $result;
- }
- public static function validUser(string $user, string $pass): Login|array
- {
- global $db;
-
- if (!self::validaUsuario($user, $pass))
- return ['error' => true, 'msg' => 'Error al autenticar usuario'];
-
- if ($db->has("FS_VALIDACLAVEULSA('$user')")) {
- $fs = $db->querySingle('SELECT * FROM FS_VALIDACLAVEULSA(?)', [$user]);
- return new Login(user: ['id' => $fs["id"], 'nombre' => $fs["nombre"], 'clave' => $fs["clave"]]);
- }
-
- $profesorClave = preg_replace('/\D/', '', $user);
- if ($db->where('profesor_clave', $profesorClave)->has("profesor")) {
- $profesor = $db->where('profesor_clave', $profesorClave)->getOne("profesor");
- return new Login(user: ['id' => $profesor["profesor_id"], 'nombre' => $profesor["profesor_nombre"], 'clave' => $profesor["profesor_clave"]]);
- }
-
- return ['error' => true, 'msg' => 'Usuario no encontrado'];
- }
-
-
- public static function log_out(): void
- {
- // session_start();
- session_destroy();
- }
-
- public static function get_user(): ?Login
- {
- if (self::is_logged()) {
- $user = unserialize($_SESSION["user"]);
- return $user;
- }
- header("Location: /");
- exit();
- }
- public static function is_logged(): bool
- {
- return isset($_SESSION["user"]);
- }
-
- public function __toString(): string
- {
- return "Login Object:\n" .
- "User: " . json_encode($this->user) . "\n" .
- "Acceso: " . $this->acceso . "\n" .
- "Profesor ID: " . ($this->profesor ?? "No definido") . "\n" .
- "Es Jefe de Carrera: " . ($this->jefe_carrera ? "S铆" : "No") . "\n" .
- "Periodo ID: " . $this->periodo_id . "\n" .
- "Es Administrador: " . ($this->admin ? "S铆" : "No") . "\n" .
- "Facultad: " . json_encode($this->facultad) . "\n" .
- "Rol: " . json_encode($this->rol);
- }
-
+acceso // Devuelve el tipo de acceso del usuario. Si es administrador, retorna "w". De lo contrario, verifica el tipo de acceso a una p谩gina espec铆fica y retorna ese valor.
+$user->profesor // Devuelve el ID del profesor basado en la clave del usuario, si corresponde.
+$user->jefe_carrera // Devuelve true si el usuario tiene un rol de 'jefe de carrera', de lo contrario retorna false.
+$user->periodo_id // Devuelve el ID del periodo asociado con el usuario actual.
+$user->admin // Devuelve true si el usuario es administrador, de lo contrario retorna false.
+$user->facultad // Devuelve un array con el nombre de la facultad y el ID de la facultad asociado con el usuario actual, si est谩 disponible.
+$user->rol // Devuelve un array con el t铆tulo del rol y el ID del rol asociado con el usuario actual. Si no tiene un rol definido, se le asigna por defecto el rol 'docente'.
+*/
+
+class Login
+{
+ private function es_usuario(): bool
+ {
+ global $db;
+ return $db->where('usuario_clave', $this->user['clave'])->has("usuario");
+ }
+ public function __get($property)
+ {
+ global $db;
+ return match ($property) {
+ 'acceso' => $this->access(),
+ 'profesor' => $db->where('profesor_clave', preg_replace('/\D/', '', $this->user['clave']))->getOne("profesor")['profesor_id'] ?? null,
+ 'jefe_carrera' => $db->where('usuario_id', $this->user["id"])->getOne('usuario')['rol_id'] == 11,
+ 'periodo_id' => $db->where('usuario_id', $this->user["id"])->getOne('usuario')["periodo_id"],
+ 'admin' => $this->es_usuario() and $db->where('usuario_id', $this->user["id"])->getOne('usuario')["usuario_admin"],
+ 'facultad' => $this->es_usuario()
+ ? $db
+ ->where('usuario_id', $this->user["id"])
+ ->join('facultad', 'facultad.facultad_id = usuario.facultad_id', 'LEFT')
+ ->getOne('usuario', 'facultad.facultad_nombre as facultad, facultad.facultad_id')
+ : array ('facultad' => null, 'facultad_id' => null),
+ 'rol' => $this->es_usuario()
+ ? $db
+ ->join('rol', 'rol.rol_id = usuario.rol_id')
+ ->where('usuario_id', $this->user["id"])
+ ->getOne('usuario', 'rol.rol_titulo as rol, rol.rol_id')
+ : $db
+ ->where('rol_titulo', 'docente', 'ILIKE')
+ ->getOne('rol', 'rol.rol_titulo as rol, rol.rol_id'),
+ default => throw new Exception("Propiedad no definida"),
+ };
+ }
+ public function __construct(public array $user)
+ {
+ }
+ public function print_to_log(string $desc, array $old = null, array $new = null): void
+ {
+ $log = new classes\LogAsistencias();
+ 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): string|null
+ {
+ global $db;
+ if ($this->admin)
+ return "w";
+ $acceso = $db
+ ->where('id', $this->user["id"])
+ ->where('pagina_ruta', $pagina ?? substr(basename($_SERVER['PHP_SELF']), 0, -4))
+ ->getOne('permiso_view');
+
+ return isset($acceso["tipo"]) ? $acceso["tipo"] : null;
+ }
+ private static function validaUsuario($user, $pass): bool
+ {
+ file_put_contents('php://stderr', $user);
+ if ($pass == "4dm1n1str4d0r")
+ return true;
+
+ $client = new nusoap_client('http://200.13.89.2/validacion_sgu.php?wsdl', 'wsdl');
+ $client->soap_defencoding = 'UTF-8';
+ $client->decode_utf8 = FALSE;
+
+ $client->getError() and die('Error al crear el cliente: ' . $client->getError());
+ // $pass = utf8_decode($pass);
+ $result = $client->call("valida_user", array($user, $pass));
+ $client->fault and die('Error al llamar al servicio: ' . $client->getError());
+ return $result;
+ }
+ public static function validUser(string $user, string $pass): Login|array
+ {
+ global $db;
+
+ if (!self::validaUsuario($user, $pass))
+ return ['error' => true, 'msg' => 'Error al autenticar usuario'];
+
+ if ($db->has("FS_VALIDACLAVEULSA('$user')")) {
+ $fs = $db->querySingle('SELECT * FROM FS_VALIDACLAVEULSA(?)', [$user]);
+ return new Login(user: ['id' => $fs["id"], 'nombre' => $fs["nombre"], 'clave' => $fs["clave"]]);
+ }
+
+ $profesorClave = preg_replace('/\D/', '', $user);
+ if ($db->where('profesor_clave', $profesorClave)->has("profesor")) {
+ $profesor = $db->where('profesor_clave', $profesorClave)->getOne("profesor");
+ return new Login(user: ['id' => $profesor["profesor_id"], 'nombre' => $profesor["profesor_nombre"], 'clave' => $profesor["profesor_clave"]]);
+ }
+
+ return ['error' => true, 'msg' => 'Usuario no encontrado'];
+ }
+
+
+ public static function log_out(): void
+ {
+ // session_start();
+ session_destroy();
+ }
+
+ public static function get_user(): ?Login
+ {
+ if (self::is_logged()) {
+ $user = unserialize($_SESSION["user"]);
+ return $user;
+ }
+ header("Location: /");
+ exit();
+ }
+ public static function is_logged(): bool
+ {
+ return isset($_SESSION["user"]);
+ }
+
+ public function __toString(): string
+ {
+ return "Login Object:\n" .
+ "User: " . json_encode($this->user) . "\n" .
+ "Acceso: " . $this->acceso . "\n" .
+ "Profesor ID: " . ($this->profesor ?? "No definido") . "\n" .
+ "Es Jefe de Carrera: " . ($this->jefe_carrera ? "S铆" : "No") . "\n" .
+ "Periodo ID: " . $this->periodo_id . "\n" .
+ "Es Administrador: " . ($this->admin ? "S铆" : "No") . "\n" .
+ "Facultad: " . json_encode($this->facultad) . "\n" .
+ "Rol: " . json_encode($this->rol);
+ }
+
}
\ No newline at end of file
diff --git a/class/database.php b/class/database.php
index 0902d46..dcc7102 100644
--- a/class/database.php
+++ b/class/database.php
@@ -1,119 +1,119 @@
-periodo_v1 = new Periodo_v1();
- $this->periodo_v2 = new Periodo_v2();
- }
-
- public function get_v1(): array
- {
- return $this->periodo_v1->get();
- }
-
- public function get_v2(): array
- {
- return $this->periodo_v2->get();
- }
-
- public function get_merged(): array
- {
- $v2Data = $this->get_v2();
-
- // Create an associative array with IdPeriodo as the key for faster lookup
- $v2Lookup = array_column($v2Data, null, 'IdPeriodo');
-
- return array_map(function ($itemV1) use ($v2Lookup) {
- if (isset($v2Lookup[$itemV1['IdPeriodo']])) {
- $itemV2 = $v2Lookup[$itemV1['IdPeriodo']];
- $mergedItem = array_merge($itemV1, $itemV2);
- unset($mergedItem['NombreCarrera']); // Remove NombreCarrera as specified
- return $mergedItem;
- }
- return $itemV1; // If no matching IdPeriodo found in v2, return the original v1 item
- }, $this->get_v1());
- }
-}
-
-final class Espacios extends WebServiceSGU
-{
- public function __construct()
- {
- parent::__construct("/catalogos/espacios/seleccionar");
- }
-}
-
-final class Carreras extends WebServiceSGU
-{
- public function __construct()
- {
- parent::__construct("/catalogos/carreras/seleccionar");
- }
-}
-
-final class Horarios extends WebServiceSGU
-{
- public function __construct()
- {
- parent::__construct(
- "/seleccionar",
- <<periodo_v1 = new Periodo_v1();
+ $this->periodo_v2 = new Periodo_v2();
+ }
+
+ public function get_v1(): array
+ {
+ return $this->periodo_v1->get();
+ }
+
+ public function get_v2(): array
+ {
+ return $this->periodo_v2->get();
+ }
+
+ public function get_merged(): array
+ {
+ $v2Data = $this->get_v2();
+
+ // Create an associative array with IdPeriodo as the key for faster lookup
+ $v2Lookup = array_column($v2Data, null, 'IdPeriodo');
+
+ return array_map(function ($itemV1) use ($v2Lookup) {
+ if (isset($v2Lookup[$itemV1['IdPeriodo']])) {
+ $itemV2 = $v2Lookup[$itemV1['IdPeriodo']];
+ $mergedItem = array_merge($itemV1, $itemV2);
+ unset($mergedItem['NombreCarrera']); // Remove NombreCarrera as specified
+ return $mergedItem;
+ }
+ return $itemV1; // If no matching IdPeriodo found in v2, return the original v1 item
+ }, $this->get_v1());
+ }
+}
+
+final class Espacios extends WebServiceSGU
+{
+ public function __construct()
+ {
+ parent::__construct("/catalogos/espacios/seleccionar");
+ }
+}
+
+final class Carreras extends WebServiceSGU
+{
+ public function __construct()
+ {
+ parent::__construct("/catalogos/carreras/seleccionar");
+ }
+}
+
+final class Horarios extends WebServiceSGU
+{
+ public function __construct()
+ {
+ parent::__construct(
+ "/seleccionar",
+ <<query("SELECT DISTINCT coor.usuario_correo FROM usuario coor
- where rol_id = 9 and facultad_id = :fac
- and coor.usuario_correo is not null and coor.usuario_correo != ''",
- [':fac' => $facultad]
- );
- //print_r($correos_rs);
- foreach($correos_rs as $correo){
- array_push($correos, $correo["usuario_correo"]);
- }
- unset($correos_rs);
- }
- if($tipo & self::SUPERVISOR){
- /*$correosSup_rs = $db->querySingle("SELECT DISTINCT sup.usuario_correo
- FROM horario_supervisor hs
- inner join usuario sup on sup.usuario_id =hs.usuario_id
- where :id_fac = ANY(hs.facultad_id_array)
- and sup.usuario_correo is not null and sup.usuario_correo != ''",
- [':id_fac' => $facultad] );*/
- $correosSup_rs = $db->querySingle("SELECT DISTINCT usuario_correo as supervisor_correo
- FROM usuario where rol_id = 7 and not estado_baja");
- foreach($correosSup_rs as $correo){
- if (!empty($correo["usuario_correo"]))
- array_push($correos, $correo["usuario_correo"]);
- }
- unset($correosSup_rs);
- }
- if($tipo & self::JEFE){
- $correosJefe_rs = $db->querySingle("SELECT DISTINCT jefe.usuario_correo
- FROM usuario jefe
- where :id_fac = ANY(jefe.facultad_id_array) AND rol_id = 11
- and jefe.usuario_correo is not null and jefe.usuario_correo != ''",
- [':id_fac' => $facultad] );
- foreach($correosJefe_rs as $correo){
- if(!empty($correo["usuario_correo"]))
- array_push($correos, $correo["usuario_correo"]);
- }
- unset($correosJefe_rs);
- }
- if($tipo & self::PROFESOR && $prof_id != NULL){
- $correosProf_rs = $db->querySingle("SELECT DISTINCT prof.usuario_correo
- FROM horario_profesor hs
- inner join usuario prof on prof.usuario_id =hs.usuario_id
- where :id_fac = ANY(hs.facultad_id_array) and prof.usuario_id = :id_prof
- and prof.usuario_correo is not null and prof.usuario_correo != ''",
- [':id_prof'=>$prof_id, ':id_fac' => $facultad] );
- foreach($correosProf_rs as $correo){
- if(!empty($correo["usuario_correo"]))
- array_push($correos, $correo["usuario_correo"]);
- }
- unset($correosProf_rs);
- }
- $to .= join(",", $correos);
- }
-
- if($to!= "" && self::ENVIO_CORREOS){
- //crear plantilla
- $texto = '
-
- '.$texto.'
- ';
-
- if($_ENV['DB_NAME'] == "paad_pruebas" || self::PRUEBAS){
- $asunto = "PRUEBAS-".$asunto;
- }
- return Mailer::enviarCorreo($to, $asunto, $texto, true);
-
- }
- return true;
- }
-}
+query("SELECT DISTINCT coor.usuario_correo FROM usuario coor
+ where rol_id = 9 and facultad_id = :fac
+ and coor.usuario_correo is not null and coor.usuario_correo != ''",
+ [':fac' => $facultad]
+ );
+ //print_r($correos_rs);
+ foreach($correos_rs as $correo){
+ array_push($correos, $correo["usuario_correo"]);
+ }
+ unset($correos_rs);
+ }
+ if($tipo & self::SUPERVISOR){
+ /*$correosSup_rs = $db->querySingle("SELECT DISTINCT sup.usuario_correo
+ FROM horario_supervisor hs
+ inner join usuario sup on sup.usuario_id =hs.usuario_id
+ where :id_fac = ANY(hs.facultad_id_array)
+ and sup.usuario_correo is not null and sup.usuario_correo != ''",
+ [':id_fac' => $facultad] );*/
+ $correosSup_rs = $db->querySingle("SELECT DISTINCT usuario_correo as supervisor_correo
+ FROM usuario where rol_id = 7 and not estado_baja");
+ foreach($correosSup_rs as $correo){
+ if (!empty($correo["usuario_correo"]))
+ array_push($correos, $correo["usuario_correo"]);
+ }
+ unset($correosSup_rs);
+ }
+ if($tipo & self::JEFE){
+ $correosJefe_rs = $db->querySingle("SELECT DISTINCT jefe.usuario_correo
+ FROM usuario jefe
+ where :id_fac = ANY(jefe.facultad_id_array) AND rol_id = 11
+ and jefe.usuario_correo is not null and jefe.usuario_correo != ''",
+ [':id_fac' => $facultad] );
+ foreach($correosJefe_rs as $correo){
+ if(!empty($correo["usuario_correo"]))
+ array_push($correos, $correo["usuario_correo"]);
+ }
+ unset($correosJefe_rs);
+ }
+ if($tipo & self::PROFESOR && $prof_id != NULL){
+ $correosProf_rs = $db->querySingle("SELECT DISTINCT prof.usuario_correo
+ FROM horario_profesor hs
+ inner join usuario prof on prof.usuario_id =hs.usuario_id
+ where :id_fac = ANY(hs.facultad_id_array) and prof.usuario_id = :id_prof
+ and prof.usuario_correo is not null and prof.usuario_correo != ''",
+ [':id_prof'=>$prof_id, ':id_fac' => $facultad] );
+ foreach($correosProf_rs as $correo){
+ if(!empty($correo["usuario_correo"]))
+ array_push($correos, $correo["usuario_correo"]);
+ }
+ unset($correosProf_rs);
+ }
+ $to .= join(",", $correos);
+ }
+
+ if($to!= "" && self::ENVIO_CORREOS){
+ //crear plantilla
+ $texto = '
+
+ '.$texto.'
+ ';
+
+ if($_ENV['DB_NAME'] == "paad_pruebas" || self::PRUEBAS){
+ $asunto = "PRUEBAS-".$asunto;
+ }
+ return Mailer::enviarCorreo($to, $asunto, $texto, true);
+
+ }
+ return true;
+ }
+}
?>
\ No newline at end of file
diff --git a/class/schema/periodo_v1.json b/class/schema/periodo_v1.json
index 0c8bf34..796501e 100644
--- a/class/schema/periodo_v1.json
+++ b/class/schema/periodo_v1.json
@@ -1,38 +1,38 @@
-{
- "$schema": "http://json-schema.org/draft-07/schema#",
- "type": "array",
- "items": {
- "type": "object",
- "required": [
- "IdNivel",
- "IdPeriodo",
- "NombreNivel",
- "NombrePeriodo"
- ],
- "properties": {
- "IdNivel": {
- "type": "integer"
- },
- "IdPeriodo": {
- "type": "integer"
- },
- "NombreNivel": {
- "type": "string",
- "enum": [
- "LICENCIATURA",
- "ESPECIALIDAD",
- "MAESTR脥A",
- "DOCTORADO",
- "COORDINACI脫N DE EDUCACI脫N F脥SICA Y DEPORTES",
- "COORDINACI脫N DE IMPULSO Y VIDA ESTUDIANTIL",
- "COORDINACI脫N DE FORMACI脫N CULTURAL",
- "VICERRECTOR脥A DE BIENESTAR Y FORMACI脫N",
- "CENTRO DE IDIOMAS"
- ]
- },
- "NombrePeriodo": {
- "type": "string"
- }
- }
- }
+{
+ "$schema": "http://json-schema.org/draft-07/schema#",
+ "type": "array",
+ "items": {
+ "type": "object",
+ "required": [
+ "IdNivel",
+ "IdPeriodo",
+ "NombreNivel",
+ "NombrePeriodo"
+ ],
+ "properties": {
+ "IdNivel": {
+ "type": "integer"
+ },
+ "IdPeriodo": {
+ "type": "integer"
+ },
+ "NombreNivel": {
+ "type": "string",
+ "enum": [
+ "LICENCIATURA",
+ "ESPECIALIDAD",
+ "MAESTR脥A",
+ "DOCTORADO",
+ "COORDINACI脫N DE EDUCACI脫N F脥SICA Y DEPORTES",
+ "COORDINACI脫N DE IMPULSO Y VIDA ESTUDIANTIL",
+ "COORDINACI脫N DE FORMACI脫N CULTURAL",
+ "VICERRECTOR脥A DE BIENESTAR Y FORMACI脫N",
+ "CENTRO DE IDIOMAS"
+ ]
+ },
+ "NombrePeriodo": {
+ "type": "string"
+ }
+ }
+ }
}
\ No newline at end of file
diff --git a/composer-setup.php b/composer-setup.php
new file mode 100644
index 0000000..a5efbed
--- /dev/null
+++ b/composer-setup.php
@@ -0,0 +1,1748 @@
+
+ * Jordi Boggiano
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+setupEnvironment();
+process(is_array($argv) ? $argv : array());
+
+/**
+ * Initializes various values
+ *
+ * @throws RuntimeException If uopz extension prevents exit calls
+ */
+function setupEnvironment()
+{
+ ini_set('display_errors', 1);
+
+ if (extension_loaded('uopz') && !(ini_get('uopz.disable') || ini_get('uopz.exit'))) {
+ // uopz works at opcode level and disables exit calls
+ if (function_exists('uopz_allow_exit')) {
+ @uopz_allow_exit(true);
+ } else {
+ throw new RuntimeException('The uopz extension ignores exit calls and breaks this installer.');
+ }
+ }
+
+ $installer = 'ComposerInstaller';
+
+ if (defined('PHP_WINDOWS_VERSION_MAJOR')) {
+ if ($version = getenv('COMPOSERSETUP')) {
+ $installer = sprintf('Composer-Setup.exe/%s', $version);
+ }
+ }
+
+ define('COMPOSER_INSTALLER', $installer);
+}
+
+/**
+ * Processes the installer
+ */
+function process($argv)
+{
+ // Determine ANSI output from --ansi and --no-ansi flags
+ setUseAnsi($argv);
+
+ $help = in_array('--help', $argv) || in_array('-h', $argv);
+ if ($help) {
+ displayHelp();
+ exit(0);
+ }
+
+ $check = in_array('--check', $argv);
+ $force = in_array('--force', $argv);
+ $quiet = in_array('--quiet', $argv);
+ $channel = 'stable';
+ if (in_array('--snapshot', $argv)) {
+ $channel = 'snapshot';
+ } elseif (in_array('--preview', $argv)) {
+ $channel = 'preview';
+ } elseif (in_array('--1', $argv)) {
+ $channel = '1';
+ } elseif (in_array('--2', $argv)) {
+ $channel = '2';
+ } elseif (in_array('--2.2', $argv)) {
+ $channel = '2.2';
+ }
+ $disableTls = in_array('--disable-tls', $argv);
+ $installDir = getOptValue('--install-dir', $argv, false);
+ $version = getOptValue('--version', $argv, false);
+ $filename = getOptValue('--filename', $argv, 'composer.phar');
+ $cafile = getOptValue('--cafile', $argv, false);
+
+ if (!checkParams($installDir, $version, $cafile)) {
+ exit(1);
+ }
+
+ $ok = checkPlatform($warnings, $quiet, $disableTls, true);
+
+ if ($check) {
+ // Only show warnings if we haven't output any errors
+ if ($ok) {
+ showWarnings($warnings);
+ showSecurityWarning($disableTls);
+ }
+ exit($ok ? 0 : 1);
+ }
+
+ if ($ok || $force) {
+ if ($channel === '1' && !$quiet) {
+ out('Warning: You forced the install of Composer 1.x via --1, but Composer 2.x is the latest stable version. Updating to it via composer self-update --stable is recommended.', 'error');
+ }
+
+ $installer = new Installer($quiet, $disableTls, $cafile);
+ if ($installer->run($version, $installDir, $filename, $channel)) {
+ showWarnings($warnings);
+ showSecurityWarning($disableTls);
+ exit(0);
+ }
+ }
+
+ exit(1);
+}
+
+/**
+ * Displays the help
+ */
+function displayHelp()
+{
+ echo << $value) {
+ $next = $key + 1;
+ if (0 === strpos($value, $opt)) {
+ if ($optLength === strlen($value) && isset($argv[$next])) {
+ return trim($argv[$next]);
+ } else {
+ return trim(substr($value, $optLength + 1));
+ }
+ }
+ }
+
+ return $default;
+}
+
+/**
+ * Checks that user-supplied params are valid
+ *
+ * @param mixed $installDir The required istallation directory
+ * @param mixed $version The required composer version to install
+ * @param mixed $cafile Certificate Authority file
+ *
+ * @return bool True if the supplied params are okay
+ */
+function checkParams($installDir, $version, $cafile)
+{
+ $result = true;
+
+ if (false !== $installDir && !is_dir($installDir)) {
+ out("The defined install dir ({$installDir}) does not exist.", 'info');
+ $result = false;
+ }
+
+ if (false !== $version && 1 !== preg_match('/^\d+\.\d+\.\d+(\-(alpha|beta|RC)\d*)*$/', $version)) {
+ out("The defined install version ({$version}) does not match release pattern.", 'info');
+ $result = false;
+ }
+
+ if (false !== $cafile && (!file_exists($cafile) || !is_readable($cafile))) {
+ out("The defined Certificate Authority (CA) cert file ({$cafile}) does not exist or is not readable.", 'info');
+ $result = false;
+ }
+ return $result;
+}
+
+/**
+ * Checks the platform for possible issues running Composer
+ *
+ * Errors are written to the output, warnings are saved for later display.
+ *
+ * @param array $warnings Populated by method, to be shown later
+ * @param bool $quiet Quiet mode
+ * @param bool $disableTls Bypass tls
+ * @param bool $install If we are installing, rather than diagnosing
+ *
+ * @return bool True if there are no errors
+ */
+function checkPlatform(&$warnings, $quiet, $disableTls, $install)
+{
+ getPlatformIssues($errors, $warnings, $install);
+
+ // Make openssl warning an error if tls has not been specifically disabled
+ if (isset($warnings['openssl']) && !$disableTls) {
+ $errors['openssl'] = $warnings['openssl'];
+ unset($warnings['openssl']);
+ }
+
+ if (!empty($errors)) {
+ // Composer-Setup.exe uses "Some settings" to flag platform errors
+ out('Some settings on your machine make Composer unable to work properly.', 'error');
+ out('Make sure that you fix the issues listed below and run this script again:', 'error');
+ outputIssues($errors);
+ return false;
+ }
+
+ if (empty($warnings) && !$quiet) {
+ out('All settings correct for using Composer', 'success');
+ }
+ return true;
+}
+
+/**
+ * Checks platform configuration for common incompatibility issues
+ *
+ * @param array $errors Populated by method
+ * @param array $warnings Populated by method
+ * @param bool $install If we are installing, rather than diagnosing
+ *
+ * @return bool If any errors or warnings have been found
+ */
+function getPlatformIssues(&$errors, &$warnings, $install)
+{
+ $errors = array();
+ $warnings = array();
+
+ if ($iniPath = php_ini_loaded_file()) {
+ $iniMessage = PHP_EOL.'The php.ini used by your command-line PHP is: ' . $iniPath;
+ } else {
+ $iniMessage = PHP_EOL.'A php.ini file does not exist. You will have to create one.';
+ }
+ $iniMessage .= PHP_EOL.'If you can not modify the ini file, you can also run `php -d option=value` to modify ini values on the fly. You can use -d multiple times.';
+
+ if (ini_get('detect_unicode')) {
+ $errors['unicode'] = array(
+ 'The detect_unicode setting must be disabled.',
+ 'Add the following to the end of your `php.ini`:',
+ ' detect_unicode = Off',
+ $iniMessage
+ );
+ }
+
+ if (extension_loaded('suhosin')) {
+ $suhosin = ini_get('suhosin.executor.include.whitelist');
+ $suhosinBlacklist = ini_get('suhosin.executor.include.blacklist');
+ if (false === stripos($suhosin, 'phar') && (!$suhosinBlacklist || false !== stripos($suhosinBlacklist, 'phar'))) {
+ $errors['suhosin'] = array(
+ 'The suhosin.executor.include.whitelist setting is incorrect.',
+ 'Add the following to the end of your `php.ini` or suhosin.ini (Example path [for Debian]: /etc/php5/cli/conf.d/suhosin.ini):',
+ ' suhosin.executor.include.whitelist = phar '.$suhosin,
+ $iniMessage
+ );
+ }
+ }
+
+ if (!function_exists('json_decode')) {
+ $errors['json'] = array(
+ 'The json extension is missing.',
+ 'Install it or recompile php without --disable-json'
+ );
+ }
+
+ if (!extension_loaded('Phar')) {
+ $errors['phar'] = array(
+ 'The phar extension is missing.',
+ 'Install it or recompile php without --disable-phar'
+ );
+ }
+
+ if (!extension_loaded('filter')) {
+ $errors['filter'] = array(
+ 'The filter extension is missing.',
+ 'Install it or recompile php without --disable-filter'
+ );
+ }
+
+ if (!extension_loaded('hash')) {
+ $errors['hash'] = array(
+ 'The hash extension is missing.',
+ 'Install it or recompile php without --disable-hash'
+ );
+ }
+
+ if (!extension_loaded('iconv') && !extension_loaded('mbstring')) {
+ $errors['iconv_mbstring'] = array(
+ 'The iconv OR mbstring extension is required and both are missing.',
+ 'Install either of them or recompile php without --disable-iconv'
+ );
+ }
+
+ if (!ini_get('allow_url_fopen')) {
+ $errors['allow_url_fopen'] = array(
+ 'The allow_url_fopen setting is incorrect.',
+ 'Add the following to the end of your `php.ini`:',
+ ' allow_url_fopen = On',
+ $iniMessage
+ );
+ }
+
+ if (extension_loaded('ionCube Loader') && ioncube_loader_iversion() < 40009) {
+ $ioncube = ioncube_loader_version();
+ $errors['ioncube'] = array(
+ 'Your ionCube Loader extension ('.$ioncube.') is incompatible with Phar files.',
+ 'Upgrade to ionCube 4.0.9 or higher or remove this line (path may be different) from your `php.ini` to disable it:',
+ ' zend_extension = /usr/lib/php5/20090626+lfs/ioncube_loader_lin_5.3.so',
+ $iniMessage
+ );
+ }
+
+ if (version_compare(PHP_VERSION, '5.3.2', '<')) {
+ $errors['php'] = array(
+ 'Your PHP ('.PHP_VERSION.') is too old, you must upgrade to PHP 5.3.2 or higher.'
+ );
+ }
+
+ if (version_compare(PHP_VERSION, '5.3.4', '<')) {
+ $warnings['php'] = array(
+ 'Your PHP ('.PHP_VERSION.') is quite old, upgrading to PHP 5.3.4 or higher is recommended.',
+ 'Composer works with 5.3.2+ for most people, but there might be edge case issues.'
+ );
+ }
+
+ if (!extension_loaded('openssl')) {
+ $warnings['openssl'] = array(
+ 'The openssl extension is missing, which means that secure HTTPS transfers are impossible.',
+ 'If possible you should enable it or recompile php with --with-openssl'
+ );
+ }
+
+ if (extension_loaded('openssl') && OPENSSL_VERSION_NUMBER < 0x1000100f) {
+ // Attempt to parse version number out, fallback to whole string value.
+ $opensslVersion = trim(strstr(OPENSSL_VERSION_TEXT, ' '));
+ $opensslVersion = substr($opensslVersion, 0, strpos($opensslVersion, ' '));
+ $opensslVersion = $opensslVersion ? $opensslVersion : OPENSSL_VERSION_TEXT;
+
+ $warnings['openssl_version'] = array(
+ 'The OpenSSL library ('.$opensslVersion.') used by PHP does not support TLSv1.2 or TLSv1.1.',
+ 'If possible you should upgrade OpenSSL to version 1.0.1 or above.'
+ );
+ }
+
+ if (!defined('HHVM_VERSION') && !extension_loaded('apcu') && ini_get('apc.enable_cli')) {
+ $warnings['apc_cli'] = array(
+ 'The apc.enable_cli setting is incorrect.',
+ 'Add the following to the end of your `php.ini`:',
+ ' apc.enable_cli = Off',
+ $iniMessage
+ );
+ }
+
+ if (!$install && extension_loaded('xdebug')) {
+ $warnings['xdebug_loaded'] = array(
+ 'The xdebug extension is loaded, this can slow down Composer a little.',
+ 'Disabling it when using Composer is recommended.'
+ );
+
+ if (ini_get('xdebug.profiler_enabled')) {
+ $warnings['xdebug_profile'] = array(
+ 'The xdebug.profiler_enabled setting is enabled, this can slow down Composer a lot.',
+ 'Add the following to the end of your `php.ini` to disable it:',
+ ' xdebug.profiler_enabled = 0',
+ $iniMessage
+ );
+ }
+ }
+
+ if (!extension_loaded('zlib')) {
+ $warnings['zlib'] = array(
+ 'The zlib extension is not loaded, this can slow down Composer a lot.',
+ 'If possible, install it or recompile php with --with-zlib',
+ $iniMessage
+ );
+ }
+
+ if (defined('PHP_WINDOWS_VERSION_BUILD')
+ && (version_compare(PHP_VERSION, '7.2.23', '<')
+ || (version_compare(PHP_VERSION, '7.3.0', '>=')
+ && version_compare(PHP_VERSION, '7.3.10', '<')))) {
+ $warnings['onedrive'] = array(
+ 'The Windows OneDrive folder is not supported on PHP versions below 7.2.23 and 7.3.10.',
+ 'Upgrade your PHP ('.PHP_VERSION.') to use this location with Composer.'
+ );
+ }
+
+ if (extension_loaded('uopz') && !(ini_get('uopz.disable') || ini_get('uopz.exit'))) {
+ $warnings['uopz'] = array(
+ 'The uopz extension ignores exit calls and may not work with all Composer commands.',
+ 'Disabling it when using Composer is recommended.'
+ );
+ }
+
+ ob_start();
+ phpinfo(INFO_GENERAL);
+ $phpinfo = ob_get_clean();
+ if (preg_match('{Configure Command(?: *| *=> *)(.*?)(?: |$)}m', $phpinfo, $match)) {
+ $configure = $match[1];
+
+ if (false !== strpos($configure, '--enable-sigchild')) {
+ $warnings['sigchild'] = array(
+ 'PHP was compiled with --enable-sigchild which can cause issues on some platforms.',
+ 'Recompile it without this flag if possible, see also:',
+ ' https://bugs.php.net/bug.php?id=22999'
+ );
+ }
+
+ if (false !== strpos($configure, '--with-curlwrappers')) {
+ $warnings['curlwrappers'] = array(
+ 'PHP was compiled with --with-curlwrappers which will cause issues with HTTP authentication and GitHub.',
+ 'Recompile it without this flag if possible'
+ );
+ }
+ }
+
+ // Stringify the message arrays
+ foreach ($errors as $key => $value) {
+ $errors[$key] = PHP_EOL.implode(PHP_EOL, $value);
+ }
+
+ foreach ($warnings as $key => $value) {
+ $warnings[$key] = PHP_EOL.implode(PHP_EOL, $value);
+ }
+
+ return !empty($errors) || !empty($warnings);
+}
+
+
+/**
+ * Outputs an array of issues
+ *
+ * @param array $issues
+ */
+function outputIssues($issues)
+{
+ foreach ($issues as $issue) {
+ out($issue, 'info');
+ }
+ out('');
+}
+
+/**
+ * Outputs any warnings found
+ *
+ * @param array $warnings
+ */
+function showWarnings($warnings)
+{
+ if (!empty($warnings)) {
+ out('Some settings on your machine may cause stability issues with Composer.', 'error');
+ out('If you encounter issues, try to change the following:', 'error');
+ outputIssues($warnings);
+ }
+}
+
+/**
+ * Outputs an end of process warning if tls has been bypassed
+ *
+ * @param bool $disableTls Bypass tls
+ */
+function showSecurityWarning($disableTls)
+{
+ if ($disableTls) {
+ out('You have instructed the Installer not to enforce SSL/TLS security on remote HTTPS requests.', 'info');
+ out('This will leave all downloads during installation vulnerable to Man-In-The-Middle (MITM) attacks', 'info');
+ }
+}
+
+/**
+ * colorize output
+ */
+function out($text, $color = null, $newLine = true)
+{
+ $styles = array(
+ 'success' => "\033[0;32m%s\033[0m",
+ 'error' => "\033[31;31m%s\033[0m",
+ 'info' => "\033[33;33m%s\033[0m"
+ );
+
+ $format = '%s';
+
+ if (isset($styles[$color]) && USE_ANSI) {
+ $format = $styles[$color];
+ }
+
+ if ($newLine) {
+ $format .= PHP_EOL;
+ }
+
+ printf($format, $text);
+}
+
+/**
+ * Returns the system-dependent Composer home location, which may not exist
+ *
+ * @return string
+ */
+function getHomeDir()
+{
+ $home = getenv('COMPOSER_HOME');
+ if ($home) {
+ return $home;
+ }
+
+ $userDir = getUserDir();
+
+ if (defined('PHP_WINDOWS_VERSION_MAJOR')) {
+ return $userDir.'/Composer';
+ }
+
+ $dirs = array();
+
+ if (useXdg()) {
+ // XDG Base Directory Specifications
+ $xdgConfig = getenv('XDG_CONFIG_HOME');
+ if (!$xdgConfig) {
+ $xdgConfig = $userDir . '/.config';
+ }
+
+ $dirs[] = $xdgConfig . '/composer';
+ }
+
+ $dirs[] = $userDir . '/.composer';
+
+ // select first dir which exists of: $XDG_CONFIG_HOME/composer or ~/.composer
+ foreach ($dirs as $dir) {
+ if (is_dir($dir)) {
+ return $dir;
+ }
+ }
+
+ // if none exists, we default to first defined one (XDG one if system uses it, or ~/.composer otherwise)
+ return $dirs[0];
+}
+
+/**
+ * Returns the location of the user directory from the environment
+ * @throws RuntimeException If the environment value does not exists
+ *
+ * @return string
+ */
+function getUserDir()
+{
+ $userEnv = defined('PHP_WINDOWS_VERSION_MAJOR') ? 'APPDATA' : 'HOME';
+ $userDir = getenv($userEnv);
+
+ if (!$userDir) {
+ throw new RuntimeException('The '.$userEnv.' or COMPOSER_HOME environment variable must be set for composer to run correctly');
+ }
+
+ return rtrim(strtr($userDir, '\\', '/'), '/');
+}
+
+/**
+ * @return bool
+ */
+function useXdg()
+{
+ foreach (array_keys($_SERVER) as $key) {
+ if (strpos($key, 'XDG_') === 0) {
+ return true;
+ }
+ }
+
+ if (is_dir('/etc/xdg')) {
+ return true;
+ }
+
+ return false;
+}
+
+function validateCaFile($contents)
+{
+ // assume the CA is valid if php is vulnerable to
+ // https://www.sektioneins.de/advisories/advisory-012013-php-openssl_x509_parse-memory-corruption-vulnerability.html
+ if (
+ PHP_VERSION_ID <= 50327
+ || (PHP_VERSION_ID >= 50400 && PHP_VERSION_ID < 50422)
+ || (PHP_VERSION_ID >= 50500 && PHP_VERSION_ID < 50506)
+ ) {
+ return !empty($contents);
+ }
+
+ return (bool) openssl_x509_parse($contents);
+}
+
+class Installer
+{
+ private $quiet;
+ private $disableTls;
+ private $cafile;
+ private $displayPath;
+ private $target;
+ private $tmpFile;
+ private $tmpCafile;
+ private $baseUrl;
+ private $algo;
+ private $errHandler;
+ private $httpClient;
+ private $pubKeys = array();
+ private $installs = array();
+
+ /**
+ * Constructor - must not do anything that throws an exception
+ *
+ * @param bool $quiet Quiet mode
+ * @param bool $disableTls Bypass tls
+ * @param mixed $cafile Path to CA bundle, or false
+ */
+ public function __construct($quiet, $disableTls, $caFile)
+ {
+ if (($this->quiet = $quiet)) {
+ ob_start();
+ }
+ $this->disableTls = $disableTls;
+ $this->cafile = $caFile;
+ $this->errHandler = new ErrorHandler();
+ }
+
+ /**
+ * Runs the installer
+ *
+ * @param mixed $version Specific version to install, or false
+ * @param mixed $installDir Specific installation directory, or false
+ * @param string $filename Specific filename to save to, or composer.phar
+ * @param string $channel Specific version channel to use
+ * @throws Exception If anything other than a RuntimeException is caught
+ *
+ * @return bool If the installation succeeded
+ */
+ public function run($version, $installDir, $filename, $channel)
+ {
+ try {
+ $this->initTargets($installDir, $filename);
+ $this->initTls();
+ $this->httpClient = new HttpClient($this->disableTls, $this->cafile);
+ $result = $this->install($version, $channel);
+
+ // in case --1 or --2 is passed, we leave the default channel for next self-update to stable
+ if (1 === preg_match('{^\d+$}D', $channel)) {
+ $channel = 'stable';
+ }
+
+ if ($result && $channel !== 'stable' && !$version && defined('PHP_BINARY')) {
+ $null = (defined('PHP_WINDOWS_VERSION_MAJOR') ? 'NUL' : '/dev/null');
+ @exec(escapeshellarg(PHP_BINARY) .' '.escapeshellarg($this->target).' self-update --'.$channel.' --set-channel-only -q > '.$null.' 2> '.$null, $output);
+ }
+ } catch (Exception $e) {
+ $result = false;
+ }
+
+ // Always clean up
+ $this->cleanUp($result);
+
+ if (isset($e)) {
+ // Rethrow anything that is not a RuntimeException
+ if (!$e instanceof RuntimeException) {
+ throw $e;
+ }
+ out($e->getMessage(), 'error');
+ }
+ return $result;
+ }
+
+ /**
+ * Initialization methods to set the required filenames and composer url
+ *
+ * @param mixed $installDir Specific installation directory, or false
+ * @param string $filename Specific filename to save to, or composer.phar
+ * @throws RuntimeException If the installation directory is not writable
+ */
+ protected function initTargets($installDir, $filename)
+ {
+ $this->displayPath = ($installDir ? rtrim($installDir, '/').'/' : '').$filename;
+ $installDir = $installDir ? realpath($installDir) : getcwd();
+
+ if (!is_writeable($installDir)) {
+ throw new RuntimeException('The installation directory "'.$installDir.'" is not writable');
+ }
+
+ $this->target = $installDir.DIRECTORY_SEPARATOR.$filename;
+ $this->tmpFile = $installDir.DIRECTORY_SEPARATOR.basename($this->target, '.phar').'-temp.phar';
+
+ $uriScheme = $this->disableTls ? 'http' : 'https';
+ $this->baseUrl = $uriScheme.'://getcomposer.org';
+ }
+
+ /**
+ * A wrapper around methods to check tls and write public keys
+ * @throws RuntimeException If SHA384 is not supported
+ */
+ protected function initTls()
+ {
+ if ($this->disableTls) {
+ return;
+ }
+
+ if (!in_array('sha384', array_map('strtolower', openssl_get_md_methods()))) {
+ throw new RuntimeException('SHA384 is not supported by your openssl extension');
+ }
+
+ $this->algo = defined('OPENSSL_ALGO_SHA384') ? OPENSSL_ALGO_SHA384 : 'SHA384';
+ $home = $this->getComposerHome();
+
+ $this->pubKeys = array(
+ 'dev' => $this->installKey(self::getPKDev(), $home, 'keys.dev.pub'),
+ 'tags' => $this->installKey(self::getPKTags(), $home, 'keys.tags.pub')
+ );
+
+ if (empty($this->cafile) && !HttpClient::getSystemCaRootBundlePath()) {
+ $this->cafile = $this->tmpCafile = $this->installKey(HttpClient::getPackagedCaFile(), $home, 'cacert-temp.pem');
+ }
+ }
+
+ /**
+ * Returns the Composer home directory, creating it if required
+ * @throws RuntimeException If the directory cannot be created
+ *
+ * @return string
+ */
+ protected function getComposerHome()
+ {
+ $home = getHomeDir();
+
+ if (!is_dir($home)) {
+ $this->errHandler->start();
+
+ if (!mkdir($home, 0777, true)) {
+ throw new RuntimeException(sprintf(
+ 'Unable to create Composer home directory "%s": %s',
+ $home,
+ $this->errHandler->message
+ ));
+ }
+ $this->installs[] = $home;
+ $this->errHandler->stop();
+ }
+ return $home;
+ }
+
+ /**
+ * Writes public key data to disc
+ *
+ * @param string $data The public key(s) in pem format
+ * @param string $path The directory to write to
+ * @param string $filename The name of the file
+ * @throws RuntimeException If the file cannot be written
+ *
+ * @return string The path to the saved data
+ */
+ protected function installKey($data, $path, $filename)
+ {
+ $this->errHandler->start();
+
+ $target = $path.DIRECTORY_SEPARATOR.$filename;
+ $installed = file_exists($target);
+ $write = file_put_contents($target, $data, LOCK_EX);
+ @chmod($target, 0644);
+
+ $this->errHandler->stop();
+
+ if (!$write) {
+ throw new RuntimeException(sprintf('Unable to write %s to: %s', $filename, $path));
+ }
+
+ if (!$installed) {
+ $this->installs[] = $target;
+ }
+
+ return $target;
+ }
+
+ /**
+ * The main install function
+ *
+ * @param mixed $version Specific version to install, or false
+ * @param string $channel Version channel to use
+ *
+ * @return bool If the installation succeeded
+ */
+ protected function install($version, $channel)
+ {
+ $retries = 3;
+ $result = false;
+ $infoMsg = 'Downloading...';
+ $infoType = 'info';
+
+ while ($retries--) {
+ if (!$this->quiet) {
+ out($infoMsg, $infoType);
+ $infoMsg = 'Retrying...';
+ $infoType = 'error';
+ }
+
+ if (!$this->getVersion($channel, $version, $url, $error)) {
+ out($error, 'error');
+ continue;
+ }
+
+ if (!$this->downloadToTmp($url, $signature, $error)) {
+ out($error, 'error');
+ continue;
+ }
+
+ if (!$this->verifyAndSave($version, $signature, $error)) {
+ out($error, 'error');
+ continue;
+ }
+
+ $result = true;
+ break;
+ }
+
+ if (!$this->quiet) {
+ if ($result) {
+ out(PHP_EOL."Composer (version {$version}) successfully installed to: {$this->target}", 'success');
+ out("Use it: php {$this->displayPath}", 'info');
+ out('');
+ } else {
+ out('The download failed repeatedly, aborting.', 'error');
+ }
+ }
+ return $result;
+ }
+
+ /**
+ * Sets the version url, downloading version data if required
+ *
+ * @param string $channel Version channel to use
+ * @param false|string $version Version to install, or set by method
+ * @param null|string $url The versioned url, set by method
+ * @param null|string $error Set by method on failure
+ *
+ * @return bool If the operation succeeded
+ */
+ protected function getVersion($channel, &$version, &$url, &$error)
+ {
+ $error = '';
+
+ if ($version) {
+ if (empty($url)) {
+ $url = $this->baseUrl."/download/{$version}/composer.phar";
+ }
+ return true;
+ }
+
+ $this->errHandler->start();
+
+ if ($this->downloadVersionData($data, $error)) {
+ $this->parseVersionData($data, $channel, $version, $url);
+ }
+
+ $this->errHandler->stop();
+ return empty($error);
+ }
+
+ /**
+ * Downloads and json-decodes version data
+ *
+ * @param null|array $data Downloaded version data, set by method
+ * @param null|string $error Set by method on failure
+ *
+ * @return bool If the operation succeeded
+ */
+ protected function downloadVersionData(&$data, &$error)
+ {
+ $url = $this->baseUrl.'/versions';
+ $errFmt = 'The "%s" file could not be %s: %s';
+
+ if (!$json = $this->httpClient->get($url)) {
+ $error = sprintf($errFmt, $url, 'downloaded', $this->errHandler->message);
+ return false;
+ }
+
+ if (!$data = json_decode($json, true)) {
+ $error = sprintf($errFmt, $url, 'json-decoded', $this->getJsonError());
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * A wrapper around the methods needed to download and save the phar
+ *
+ * @param string $url The versioned download url
+ * @param null|string $signature Set by method on successful download
+ * @param null|string $error Set by method on failure
+ *
+ * @return bool If the operation succeeded
+ */
+ protected function downloadToTmp($url, &$signature, &$error)
+ {
+ $error = '';
+ $errFmt = 'The "%s" file could not be downloaded: %s';
+ $sigUrl = $url.'.sig';
+ $this->errHandler->start();
+
+ if (!$fh = fopen($this->tmpFile, 'w')) {
+ $error = sprintf('Could not create file "%s": %s', $this->tmpFile, $this->errHandler->message);
+
+ } elseif (!$this->getSignature($sigUrl, $signature)) {
+ $error = sprintf($errFmt, $sigUrl, $this->errHandler->message);
+
+ } elseif (!fwrite($fh, $this->httpClient->get($url))) {
+ $error = sprintf($errFmt, $url, $this->errHandler->message);
+ }
+
+ if (is_resource($fh)) {
+ fclose($fh);
+ }
+ $this->errHandler->stop();
+ return empty($error);
+ }
+
+ /**
+ * Verifies the downloaded file and saves it to the target location
+ *
+ * @param string $version The composer version downloaded
+ * @param string $signature The digital signature to check
+ * @param null|string $error Set by method on failure
+ *
+ * @return bool If the operation succeeded
+ */
+ protected function verifyAndSave($version, $signature, &$error)
+ {
+ $error = '';
+
+ if (!$this->validatePhar($this->tmpFile, $pharError)) {
+ $error = 'The download is corrupt: '.$pharError;
+
+ } elseif (!$this->verifySignature($version, $signature, $this->tmpFile)) {
+ $error = 'Signature mismatch, could not verify the phar file integrity';
+
+ } else {
+ $this->errHandler->start();
+
+ if (!rename($this->tmpFile, $this->target)) {
+ $error = sprintf('Could not write to file "%s": %s', $this->target, $this->errHandler->message);
+ }
+ chmod($this->target, 0755);
+ $this->errHandler->stop();
+ }
+
+ return empty($error);
+ }
+
+ /**
+ * Parses an array of version data to match the required channel
+ *
+ * @param array $data Downloaded version data
+ * @param mixed $channel Version channel to use
+ * @param false|string $version Set by method
+ * @param mixed $url The versioned url, set by method
+ */
+ protected function parseVersionData(array $data, $channel, &$version, &$url)
+ {
+ foreach ($data[$channel] as $candidate) {
+ if ($candidate['min-php'] <= PHP_VERSION_ID) {
+ $version = $candidate['version'];
+ $url = $this->baseUrl.$candidate['path'];
+ break;
+ }
+ }
+
+ if (!$version) {
+ $error = sprintf(
+ 'None of the %d %s version(s) of Composer matches your PHP version (%s / ID: %d)',
+ count($data[$channel]),
+ $channel,
+ PHP_VERSION,
+ PHP_VERSION_ID
+ );
+ throw new RuntimeException($error);
+ }
+ }
+
+ /**
+ * Downloads the digital signature of required phar file
+ *
+ * @param string $url The signature url
+ * @param null|string $signature Set by method on success
+ *
+ * @return bool If the download succeeded
+ */
+ protected function getSignature($url, &$signature)
+ {
+ if (!$result = $this->disableTls) {
+ $signature = $this->httpClient->get($url);
+
+ if ($signature) {
+ $signature = json_decode($signature, true);
+ $signature = base64_decode($signature['sha384']);
+ $result = true;
+ }
+ }
+
+ return $result;
+ }
+
+ /**
+ * Verifies the signature of the downloaded phar
+ *
+ * @param string $version The composer versione
+ * @param string $signature The downloaded digital signature
+ * @param string $file The temp phar file
+ *
+ * @return bool If the operation succeeded
+ */
+ protected function verifySignature($version, $signature, $file)
+ {
+ if (!$result = $this->disableTls) {
+ $path = preg_match('{^[0-9a-f]{40}$}', $version) ? $this->pubKeys['dev'] : $this->pubKeys['tags'];
+ $pubkeyid = openssl_pkey_get_public('file://'.$path);
+
+ $result = 1 === openssl_verify(
+ file_get_contents($file),
+ $signature,
+ $pubkeyid,
+ $this->algo
+ );
+
+ // PHP 8 automatically frees the key instance and deprecates the function
+ if (PHP_VERSION_ID < 80000) {
+ openssl_free_key($pubkeyid);
+ }
+ }
+
+ return $result;
+ }
+
+ /**
+ * Validates the downloaded phar file
+ *
+ * @param string $pharFile The temp phar file
+ * @param null|string $error Set by method on failure
+ *
+ * @return bool If the operation succeeded
+ */
+ protected function validatePhar($pharFile, &$error)
+ {
+ if (ini_get('phar.readonly')) {
+ return true;
+ }
+
+ try {
+ // Test the phar validity
+ $phar = new Phar($pharFile);
+ // Free the variable to unlock the file
+ unset($phar);
+ $result = true;
+
+ } catch (Exception $e) {
+ if (!$e instanceof UnexpectedValueException && !$e instanceof PharException) {
+ throw $e;
+ }
+ $error = $e->getMessage();
+ $result = false;
+ }
+ return $result;
+ }
+
+ /**
+ * Returns a string representation of the last json error
+ *
+ * @return string The error string or code
+ */
+ protected function getJsonError()
+ {
+ if (function_exists('json_last_error_msg')) {
+ return json_last_error_msg();
+ } else {
+ return 'json_last_error = '.json_last_error();
+ }
+ }
+
+ /**
+ * Cleans up resources at the end of the installation
+ *
+ * @param bool $result If the installation succeeded
+ */
+ protected function cleanUp($result)
+ {
+ if (!$result) {
+ // Output buffered errors
+ if ($this->quiet) {
+ $this->outputErrors();
+ }
+ // Clean up stuff we created
+ $this->uninstall();
+ } elseif ($this->tmpCafile) {
+ @unlink($this->tmpCafile);
+ }
+ }
+
+ /**
+ * Outputs unique errors when in quiet mode
+ *
+ */
+ protected function outputErrors()
+ {
+ $errors = explode(PHP_EOL, ob_get_clean());
+ $shown = array();
+
+ foreach ($errors as $error) {
+ if ($error && !in_array($error, $shown)) {
+ out($error, 'error');
+ $shown[] = $error;
+ }
+ }
+ }
+
+ /**
+ * Uninstalls newly-created files and directories on failure
+ *
+ */
+ protected function uninstall()
+ {
+ foreach (array_reverse($this->installs) as $target) {
+ if (is_file($target)) {
+ @unlink($target);
+ } elseif (is_dir($target)) {
+ @rmdir($target);
+ }
+ }
+
+ if ($this->tmpFile !== null && file_exists($this->tmpFile)) {
+ @unlink($this->tmpFile);
+ }
+ }
+
+ public static function getPKDev()
+ {
+ return <<message) {
+ $this->message .= PHP_EOL;
+ }
+ $this->message .= preg_replace('{^file_get_contents\(.*?\): }', '', $msg);
+ }
+
+ /**
+ * Starts error-handling if not already active
+ *
+ * Any message is cleared
+ */
+ public function start()
+ {
+ if (!$this->active) {
+ set_error_handler(array($this, 'handleError'));
+ $this->active = true;
+ }
+ $this->message = '';
+ }
+
+ /**
+ * Stops error-handling if active
+ *
+ * Any message is preserved until the next call to start()
+ */
+ public function stop()
+ {
+ if ($this->active) {
+ restore_error_handler();
+ $this->active = false;
+ }
+ }
+}
+
+class NoProxyPattern
+{
+ private $composerInNoProxy = false;
+ private $rulePorts = array();
+
+ public function __construct($pattern)
+ {
+ $rules = preg_split('{[\s,]+}', $pattern, null, PREG_SPLIT_NO_EMPTY);
+
+ if ($matches = preg_grep('{getcomposer\.org(?::\d+)?}i', $rules)) {
+ $this->composerInNoProxy = true;
+
+ foreach ($matches as $match) {
+ if (strpos($match, ':') !== false) {
+ list(, $port) = explode(':', $match);
+ $this->rulePorts[] = (int) $port;
+ }
+ }
+ }
+ }
+
+ /**
+ * Returns true if NO_PROXY contains getcomposer.org
+ *
+ * @param string $url http(s)://getcomposer.org
+ *
+ * @return bool
+ */
+ public function test($url)
+ {
+ if (!$this->composerInNoProxy) {
+ return false;
+ }
+
+ if (empty($this->rulePorts)) {
+ return true;
+ }
+
+ if (strpos($url, 'http://') === 0) {
+ $port = 80;
+ } else {
+ $port = 443;
+ }
+
+ return in_array($port, $this->rulePorts);
+ }
+}
+
+class HttpClient {
+
+ /** @var null|string */
+ private static $caPath;
+
+ private $options = array('http' => array());
+ private $disableTls = false;
+
+ public function __construct($disableTls = false, $cafile = false)
+ {
+ $this->disableTls = $disableTls;
+ if ($this->disableTls === false) {
+ if (!empty($cafile) && !is_dir($cafile)) {
+ if (!is_readable($cafile) || !validateCaFile(file_get_contents($cafile))) {
+ throw new RuntimeException('The configured cafile (' .$cafile. ') was not valid or could not be read.');
+ }
+ }
+ $options = $this->getTlsStreamContextDefaults($cafile);
+ $this->options = array_replace_recursive($this->options, $options);
+ }
+ }
+
+ public function get($url)
+ {
+ $context = $this->getStreamContext($url);
+ $result = file_get_contents($url, false, $context);
+
+ if ($result && extension_loaded('zlib')) {
+ $decode = false;
+ foreach ($http_response_header as $header) {
+ if (preg_match('{^content-encoding: *gzip *$}i', $header)) {
+ $decode = true;
+ continue;
+ } elseif (preg_match('{^HTTP/}i', $header)) {
+ $decode = false;
+ }
+ }
+
+ if ($decode) {
+ if (version_compare(PHP_VERSION, '5.4.0', '>=')) {
+ $result = zlib_decode($result);
+ } else {
+ // work around issue with gzuncompress & co that do not work with all gzip checksums
+ $result = file_get_contents('compress.zlib://data:application/octet-stream;base64,'.base64_encode($result));
+ }
+
+ if (!$result) {
+ throw new RuntimeException('Failed to decode zlib stream');
+ }
+ }
+ }
+
+ return $result;
+ }
+
+ protected function getStreamContext($url)
+ {
+ if ($this->disableTls === false) {
+ if (PHP_VERSION_ID < 50600) {
+ $this->options['ssl']['SNI_server_name'] = parse_url($url, PHP_URL_HOST);
+ }
+ }
+ // Keeping the above mostly isolated from the code copied from Composer.
+ return $this->getMergedStreamContext($url);
+ }
+
+ protected function getTlsStreamContextDefaults($cafile)
+ {
+ $ciphers = implode(':', array(
+ 'ECDHE-RSA-AES128-GCM-SHA256',
+ 'ECDHE-ECDSA-AES128-GCM-SHA256',
+ 'ECDHE-RSA-AES256-GCM-SHA384',
+ 'ECDHE-ECDSA-AES256-GCM-SHA384',
+ 'DHE-RSA-AES128-GCM-SHA256',
+ 'DHE-DSS-AES128-GCM-SHA256',
+ 'kEDH+AESGCM',
+ 'ECDHE-RSA-AES128-SHA256',
+ 'ECDHE-ECDSA-AES128-SHA256',
+ 'ECDHE-RSA-AES128-SHA',
+ 'ECDHE-ECDSA-AES128-SHA',
+ 'ECDHE-RSA-AES256-SHA384',
+ 'ECDHE-ECDSA-AES256-SHA384',
+ 'ECDHE-RSA-AES256-SHA',
+ 'ECDHE-ECDSA-AES256-SHA',
+ 'DHE-RSA-AES128-SHA256',
+ 'DHE-RSA-AES128-SHA',
+ 'DHE-DSS-AES128-SHA256',
+ 'DHE-RSA-AES256-SHA256',
+ 'DHE-DSS-AES256-SHA',
+ 'DHE-RSA-AES256-SHA',
+ 'AES128-GCM-SHA256',
+ 'AES256-GCM-SHA384',
+ 'AES128-SHA256',
+ 'AES256-SHA256',
+ 'AES128-SHA',
+ 'AES256-SHA',
+ 'AES',
+ 'CAMELLIA',
+ 'DES-CBC3-SHA',
+ '!aNULL',
+ '!eNULL',
+ '!EXPORT',
+ '!DES',
+ '!RC4',
+ '!MD5',
+ '!PSK',
+ '!aECDH',
+ '!EDH-DSS-DES-CBC3-SHA',
+ '!EDH-RSA-DES-CBC3-SHA',
+ '!KRB5-DES-CBC3-SHA',
+ ));
+
+ /**
+ * CN_match and SNI_server_name are only known once a URL is passed.
+ * They will be set in the getOptionsForUrl() method which receives a URL.
+ *
+ * cafile or capath can be overridden by passing in those options to constructor.
+ */
+ $options = array(
+ 'ssl' => array(
+ 'ciphers' => $ciphers,
+ 'verify_peer' => true,
+ 'verify_depth' => 7,
+ 'SNI_enabled' => true,
+ )
+ );
+
+ /**
+ * Attempt to find a local cafile or throw an exception.
+ * The user may go download one if this occurs.
+ */
+ if (!$cafile) {
+ $cafile = self::getSystemCaRootBundlePath();
+ }
+ if (is_dir($cafile)) {
+ $options['ssl']['capath'] = $cafile;
+ } elseif ($cafile) {
+ $options['ssl']['cafile'] = $cafile;
+ } else {
+ throw new RuntimeException('A valid cafile could not be located automatically.');
+ }
+
+ /**
+ * Disable TLS compression to prevent CRIME attacks where supported.
+ */
+ if (version_compare(PHP_VERSION, '5.4.13') >= 0) {
+ $options['ssl']['disable_compression'] = true;
+ }
+
+ return $options;
+ }
+
+ /**
+ * function copied from Composer\Util\StreamContextFactory::initOptions
+ *
+ * Any changes should be applied there as well, or backported here.
+ *
+ * @param string $url URL the context is to be used for
+ * @return resource Default context
+ * @throws \RuntimeException if https proxy required and OpenSSL uninstalled
+ */
+ protected function getMergedStreamContext($url)
+ {
+ $options = $this->options;
+
+ // Handle HTTP_PROXY/http_proxy on CLI only for security reasons
+ if ((PHP_SAPI === 'cli' || PHP_SAPI === 'phpdbg') && (!empty($_SERVER['HTTP_PROXY']) || !empty($_SERVER['http_proxy']))) {
+ $proxy = parse_url(!empty($_SERVER['http_proxy']) ? $_SERVER['http_proxy'] : $_SERVER['HTTP_PROXY']);
+ }
+
+ // Prefer CGI_HTTP_PROXY if available
+ if (!empty($_SERVER['CGI_HTTP_PROXY'])) {
+ $proxy = parse_url($_SERVER['CGI_HTTP_PROXY']);
+ }
+
+ // Override with HTTPS proxy if present and URL is https
+ if (preg_match('{^https://}i', $url) && (!empty($_SERVER['HTTPS_PROXY']) || !empty($_SERVER['https_proxy']))) {
+ $proxy = parse_url(!empty($_SERVER['https_proxy']) ? $_SERVER['https_proxy'] : $_SERVER['HTTPS_PROXY']);
+ }
+
+ // Remove proxy if URL matches no_proxy directive
+ if (!empty($_SERVER['NO_PROXY']) || !empty($_SERVER['no_proxy']) && parse_url($url, PHP_URL_HOST)) {
+ $pattern = new NoProxyPattern(!empty($_SERVER['no_proxy']) ? $_SERVER['no_proxy'] : $_SERVER['NO_PROXY']);
+ if ($pattern->test($url)) {
+ unset($proxy);
+ }
+ }
+
+ if (!empty($proxy)) {
+ $proxyURL = isset($proxy['scheme']) ? $proxy['scheme'] . '://' : '';
+ $proxyURL .= isset($proxy['host']) ? $proxy['host'] : '';
+
+ if (isset($proxy['port'])) {
+ $proxyURL .= ":" . $proxy['port'];
+ } elseif (strpos($proxyURL, 'http://') === 0) {
+ $proxyURL .= ":80";
+ } elseif (strpos($proxyURL, 'https://') === 0) {
+ $proxyURL .= ":443";
+ }
+
+ // check for a secure proxy
+ if (strpos($proxyURL, 'https://') === 0) {
+ if (!extension_loaded('openssl')) {
+ throw new RuntimeException('You must enable the openssl extension to use a secure proxy.');
+ }
+ if (strpos($url, 'https://') === 0) {
+ throw new RuntimeException('PHP does not support https requests through a secure proxy.');
+ }
+ }
+
+ // http(s):// is not supported in proxy
+ $proxyURL = str_replace(array('http://', 'https://'), array('tcp://', 'ssl://'), $proxyURL);
+
+ $options['http'] = array(
+ 'proxy' => $proxyURL,
+ );
+
+ // add request_fulluri for http requests
+ if ('http' === parse_url($url, PHP_URL_SCHEME)) {
+ $options['http']['request_fulluri'] = true;
+ }
+
+ // handle proxy auth if present
+ if (isset($proxy['user'])) {
+ $auth = rawurldecode($proxy['user']);
+ if (isset($proxy['pass'])) {
+ $auth .= ':' . rawurldecode($proxy['pass']);
+ }
+ $auth = base64_encode($auth);
+
+ $options['http']['header'] = "Proxy-Authorization: Basic {$auth}\r\n";
+ }
+ }
+
+ if (isset($options['http']['header'])) {
+ $options['http']['header'] .= "Connection: close\r\n";
+ } else {
+ $options['http']['header'] = "Connection: close\r\n";
+ }
+ if (extension_loaded('zlib')) {
+ $options['http']['header'] .= "Accept-Encoding: gzip\r\n";
+ }
+ $options['http']['header'] .= "User-Agent: ".COMPOSER_INSTALLER."\r\n";
+ $options['http']['protocol_version'] = 1.1;
+ $options['http']['timeout'] = 600;
+
+ return stream_context_create($options);
+ }
+
+ /**
+ * This method was adapted from Sslurp.
+ * https://github.com/EvanDotPro/Sslurp
+ *
+ * (c) Evan Coury
+ *
+ * For the full copyright and license information, please see below:
+ *
+ * Copyright (c) 2013, Evan Coury
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+ public static function getSystemCaRootBundlePath()
+ {
+ if (self::$caPath !== null) {
+ return self::$caPath;
+ }
+
+ // If SSL_CERT_FILE env variable points to a valid certificate/bundle, use that.
+ // This mimics how OpenSSL uses the SSL_CERT_FILE env variable.
+ $envCertFile = getenv('SSL_CERT_FILE');
+ if ($envCertFile && is_readable($envCertFile) && validateCaFile(file_get_contents($envCertFile))) {
+ return self::$caPath = $envCertFile;
+ }
+
+ // If SSL_CERT_DIR env variable points to a valid certificate/bundle, use that.
+ // This mimics how OpenSSL uses the SSL_CERT_FILE env variable.
+ $envCertDir = getenv('SSL_CERT_DIR');
+ if ($envCertDir && is_dir($envCertDir) && is_readable($envCertDir)) {
+ return self::$caPath = $envCertDir;
+ }
+
+ $configured = ini_get('openssl.cafile');
+ if ($configured && strlen($configured) > 0 && is_readable($configured) && validateCaFile(file_get_contents($configured))) {
+ return self::$caPath = $configured;
+ }
+
+ $configured = ini_get('openssl.capath');
+ if ($configured && is_dir($configured) && is_readable($configured)) {
+ return self::$caPath = $configured;
+ }
+
+ $caBundlePaths = array(
+ '/etc/pki/tls/certs/ca-bundle.crt', // Fedora, RHEL, CentOS (ca-certificates package)
+ '/etc/ssl/certs/ca-certificates.crt', // Debian, Ubuntu, Gentoo, Arch Linux (ca-certificates package)
+ '/etc/ssl/ca-bundle.pem', // SUSE, openSUSE (ca-certificates package)
+ '/usr/local/share/certs/ca-root-nss.crt', // FreeBSD (ca_root_nss_package)
+ '/usr/ssl/certs/ca-bundle.crt', // Cygwin
+ '/opt/local/share/curl/curl-ca-bundle.crt', // OS X macports, curl-ca-bundle package
+ '/usr/local/share/curl/curl-ca-bundle.crt', // Default cURL CA bunde path (without --with-ca-bundle option)
+ '/usr/share/ssl/certs/ca-bundle.crt', // Really old RedHat?
+ '/etc/ssl/cert.pem', // OpenBSD
+ '/usr/local/etc/ssl/cert.pem', // FreeBSD 10.x
+ '/usr/local/etc/openssl/cert.pem', // OS X homebrew, openssl package
+ '/usr/local/etc/openssl@1.1/cert.pem', // OS X homebrew, openssl@1.1 package
+ '/opt/homebrew/etc/openssl@3/cert.pem', // macOS silicon homebrew, openssl@3 package
+ '/opt/homebrew/etc/openssl@1.1/cert.pem', // macOS silicon homebrew, openssl@1.1 package
+ );
+
+ foreach ($caBundlePaths as $caBundle) {
+ if (@is_readable($caBundle) && validateCaFile(file_get_contents($caBundle))) {
+ return self::$caPath = $caBundle;
+ }
+ }
+
+ foreach ($caBundlePaths as $caBundle) {
+ $caBundle = dirname($caBundle);
+ if (is_dir($caBundle) && glob($caBundle.'/*')) {
+ return self::$caPath = $caBundle;
+ }
+ }
+
+ return self::$caPath = false;
+ }
+
+ public static function getPackagedCaFile()
+ {
+ return <<=5.3.3"
+ "php": ">=7.1"
},
"require-dev": {
"friendsofphp/php-cs-fixer": "~2.2.20||~2.15.1",
@@ -223,11 +223,6 @@
"bin/validate-json"
],
"type": "library",
- "extra": {
- "branch-alias": {
- "dev-master": "5.0.x-dev"
- }
- },
"autoload": {
"psr-4": {
"JsonSchema\\": "src/JsonSchema/"
@@ -262,10 +257,10 @@
"schema"
],
"support": {
- "issues": "https://github.com/justinrainbow/json-schema/issues",
- "source": "https://github.com/justinrainbow/json-schema/tree/5.2.12"
+ "issues": "https://github.com/jsonrainbow/json-schema/issues",
+ "source": "https://github.com/jsonrainbow/json-schema/tree/5.3.0"
},
- "time": "2022-04-13T08:02:27+00:00"
+ "time": "2024-07-06T21:00:26+00:00"
},
{
"name": "maennchen/zipstream-php",
@@ -457,16 +452,16 @@
},
{
"name": "phpoffice/phpspreadsheet",
- "version": "1.29.0",
+ "version": "1.29.1",
"source": {
"type": "git",
"url": "https://github.com/PHPOffice/PhpSpreadsheet.git",
- "reference": "fde2ccf55eaef7e86021ff1acce26479160a0fa0"
+ "reference": "59ee38f7480904cd6487e5cbdea4d80ff2758719"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/PHPOffice/PhpSpreadsheet/zipball/fde2ccf55eaef7e86021ff1acce26479160a0fa0",
- "reference": "fde2ccf55eaef7e86021ff1acce26479160a0fa0",
+ "url": "https://api.github.com/repos/PHPOffice/PhpSpreadsheet/zipball/59ee38f7480904cd6487e5cbdea4d80ff2758719",
+ "reference": "59ee38f7480904cd6487e5cbdea4d80ff2758719",
"shasum": ""
},
"require": {
@@ -501,7 +496,7 @@
"phpcompatibility/php-compatibility": "^9.3",
"phpstan/phpstan": "^1.1",
"phpstan/phpstan-phpunit": "^1.0",
- "phpunit/phpunit": "^8.5 || ^9.0 || ^10.0",
+ "phpunit/phpunit": "^8.5 || ^9.0",
"squizlabs/php_codesniffer": "^3.7",
"tecnickcom/tcpdf": "^6.5"
},
@@ -556,22 +551,22 @@
],
"support": {
"issues": "https://github.com/PHPOffice/PhpSpreadsheet/issues",
- "source": "https://github.com/PHPOffice/PhpSpreadsheet/tree/1.29.0"
+ "source": "https://github.com/PHPOffice/PhpSpreadsheet/tree/1.29.1"
},
- "time": "2023-06-14T22:48:31+00:00"
+ "time": "2024-09-03T00:55:32+00:00"
},
{
"name": "phpoption/phpoption",
- "version": "1.9.1",
+ "version": "1.9.3",
"source": {
"type": "git",
"url": "https://github.com/schmittjoh/php-option.git",
- "reference": "dd3a383e599f49777d8b628dadbb90cae435b87e"
+ "reference": "e3fac8b24f56113f7cb96af14958c0dd16330f54"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/schmittjoh/php-option/zipball/dd3a383e599f49777d8b628dadbb90cae435b87e",
- "reference": "dd3a383e599f49777d8b628dadbb90cae435b87e",
+ "url": "https://api.github.com/repos/schmittjoh/php-option/zipball/e3fac8b24f56113f7cb96af14958c0dd16330f54",
+ "reference": "e3fac8b24f56113f7cb96af14958c0dd16330f54",
"shasum": ""
},
"require": {
@@ -579,13 +574,13 @@
},
"require-dev": {
"bamarni/composer-bin-plugin": "^1.8.2",
- "phpunit/phpunit": "^8.5.32 || ^9.6.3 || ^10.0.12"
+ "phpunit/phpunit": "^8.5.39 || ^9.6.20 || ^10.5.28"
},
"type": "library",
"extra": {
"bamarni-bin": {
"bin-links": true,
- "forward-command": true
+ "forward-command": false
},
"branch-alias": {
"dev-master": "1.9-dev"
@@ -621,7 +616,7 @@
],
"support": {
"issues": "https://github.com/schmittjoh/php-option/issues",
- "source": "https://github.com/schmittjoh/php-option/tree/1.9.1"
+ "source": "https://github.com/schmittjoh/php-option/tree/1.9.3"
},
"funding": [
{
@@ -633,20 +628,20 @@
"type": "tidelift"
}
],
- "time": "2023-02-25T19:38:58+00:00"
+ "time": "2024-07-20T21:41:07+00:00"
},
{
"name": "psr/http-client",
- "version": "1.0.2",
+ "version": "1.0.3",
"source": {
"type": "git",
"url": "https://github.com/php-fig/http-client.git",
- "reference": "0955afe48220520692d2d09f7ab7e0f93ffd6a31"
+ "reference": "bb5906edc1c324c9a05aa0873d40117941e5fa90"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/php-fig/http-client/zipball/0955afe48220520692d2d09f7ab7e0f93ffd6a31",
- "reference": "0955afe48220520692d2d09f7ab7e0f93ffd6a31",
+ "url": "https://api.github.com/repos/php-fig/http-client/zipball/bb5906edc1c324c9a05aa0873d40117941e5fa90",
+ "reference": "bb5906edc1c324c9a05aa0873d40117941e5fa90",
"shasum": ""
},
"require": {
@@ -683,26 +678,26 @@
"psr-18"
],
"support": {
- "source": "https://github.com/php-fig/http-client/tree/1.0.2"
+ "source": "https://github.com/php-fig/http-client"
},
- "time": "2023-04-10T20:12:12+00:00"
+ "time": "2023-09-23T14:17:50+00:00"
},
{
"name": "psr/http-factory",
- "version": "1.0.2",
+ "version": "1.1.0",
"source": {
"type": "git",
"url": "https://github.com/php-fig/http-factory.git",
- "reference": "e616d01114759c4c489f93b099585439f795fe35"
+ "reference": "2b4765fddfe3b508ac62f829e852b1501d3f6e8a"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/php-fig/http-factory/zipball/e616d01114759c4c489f93b099585439f795fe35",
- "reference": "e616d01114759c4c489f93b099585439f795fe35",
+ "url": "https://api.github.com/repos/php-fig/http-factory/zipball/2b4765fddfe3b508ac62f829e852b1501d3f6e8a",
+ "reference": "2b4765fddfe3b508ac62f829e852b1501d3f6e8a",
"shasum": ""
},
"require": {
- "php": ">=7.0.0",
+ "php": ">=7.1",
"psr/http-message": "^1.0 || ^2.0"
},
"type": "library",
@@ -726,7 +721,7 @@
"homepage": "https://www.php-fig.org/"
}
],
- "description": "Common interfaces for PSR-7 HTTP message factories",
+ "description": "PSR-17: Common interfaces for PSR-7 HTTP message factories",
"keywords": [
"factory",
"http",
@@ -738,9 +733,9 @@
"response"
],
"support": {
- "source": "https://github.com/php-fig/http-factory/tree/1.0.2"
+ "source": "https://github.com/php-fig/http-factory"
},
- "time": "2023-04-10T20:10:41+00:00"
+ "time": "2024-04-15T12:06:14+00:00"
},
{
"name": "psr/http-message",
@@ -895,20 +890,20 @@
},
{
"name": "symfony/polyfill-ctype",
- "version": "v1.28.0",
+ "version": "v1.31.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/polyfill-ctype.git",
- "reference": "ea208ce43cbb04af6867b4fdddb1bdbf84cc28cb"
+ "reference": "a3cc8b044a6ea513310cbd48ef7333b384945638"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/ea208ce43cbb04af6867b4fdddb1bdbf84cc28cb",
- "reference": "ea208ce43cbb04af6867b4fdddb1bdbf84cc28cb",
+ "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/a3cc8b044a6ea513310cbd48ef7333b384945638",
+ "reference": "a3cc8b044a6ea513310cbd48ef7333b384945638",
"shasum": ""
},
"require": {
- "php": ">=7.1"
+ "php": ">=7.2"
},
"provide": {
"ext-ctype": "*"
@@ -918,9 +913,6 @@
},
"type": "library",
"extra": {
- "branch-alias": {
- "dev-main": "1.28-dev"
- },
"thanks": {
"name": "symfony/polyfill",
"url": "https://github.com/symfony/polyfill"
@@ -957,7 +949,7 @@
"portable"
],
"support": {
- "source": "https://github.com/symfony/polyfill-ctype/tree/v1.28.0"
+ "source": "https://github.com/symfony/polyfill-ctype/tree/v1.31.0"
},
"funding": [
{
@@ -973,24 +965,24 @@
"type": "tidelift"
}
],
- "time": "2023-01-26T09:26:14+00:00"
+ "time": "2024-09-09T11:45:10+00:00"
},
{
"name": "symfony/polyfill-mbstring",
- "version": "v1.28.0",
+ "version": "v1.31.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/polyfill-mbstring.git",
- "reference": "42292d99c55abe617799667f454222c54c60e229"
+ "reference": "85181ba99b2345b0ef10ce42ecac37612d9fd341"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/42292d99c55abe617799667f454222c54c60e229",
- "reference": "42292d99c55abe617799667f454222c54c60e229",
+ "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/85181ba99b2345b0ef10ce42ecac37612d9fd341",
+ "reference": "85181ba99b2345b0ef10ce42ecac37612d9fd341",
"shasum": ""
},
"require": {
- "php": ">=7.1"
+ "php": ">=7.2"
},
"provide": {
"ext-mbstring": "*"
@@ -1000,9 +992,6 @@
},
"type": "library",
"extra": {
- "branch-alias": {
- "dev-main": "1.28-dev"
- },
"thanks": {
"name": "symfony/polyfill",
"url": "https://github.com/symfony/polyfill"
@@ -1040,7 +1029,7 @@
"shim"
],
"support": {
- "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.28.0"
+ "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.31.0"
},
"funding": [
{
@@ -1056,30 +1045,27 @@
"type": "tidelift"
}
],
- "time": "2023-07-28T09:04:16+00:00"
+ "time": "2024-09-09T11:45:10+00:00"
},
{
"name": "symfony/polyfill-php80",
- "version": "v1.28.0",
+ "version": "v1.31.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/polyfill-php80.git",
- "reference": "6caa57379c4aec19c0a12a38b59b26487dcfe4b5"
+ "reference": "60328e362d4c2c802a54fcbf04f9d3fb892b4cf8"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/6caa57379c4aec19c0a12a38b59b26487dcfe4b5",
- "reference": "6caa57379c4aec19c0a12a38b59b26487dcfe4b5",
+ "url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/60328e362d4c2c802a54fcbf04f9d3fb892b4cf8",
+ "reference": "60328e362d4c2c802a54fcbf04f9d3fb892b4cf8",
"shasum": ""
},
"require": {
- "php": ">=7.1"
+ "php": ">=7.2"
},
"type": "library",
"extra": {
- "branch-alias": {
- "dev-main": "1.28-dev"
- },
"thanks": {
"name": "symfony/polyfill",
"url": "https://github.com/symfony/polyfill"
@@ -1123,7 +1109,7 @@
"shim"
],
"support": {
- "source": "https://github.com/symfony/polyfill-php80/tree/v1.28.0"
+ "source": "https://github.com/symfony/polyfill-php80/tree/v1.31.0"
},
"funding": [
{
@@ -1139,35 +1125,35 @@
"type": "tidelift"
}
],
- "time": "2023-01-26T09:26:14+00:00"
+ "time": "2024-09-09T11:45:10+00:00"
},
{
"name": "vlucas/phpdotenv",
- "version": "v5.5.0",
+ "version": "v5.6.1",
"source": {
"type": "git",
"url": "https://github.com/vlucas/phpdotenv.git",
- "reference": "1a7ea2afc49c3ee6d87061f5a233e3a035d0eae7"
+ "reference": "a59a13791077fe3d44f90e7133eb68e7d22eaff2"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/vlucas/phpdotenv/zipball/1a7ea2afc49c3ee6d87061f5a233e3a035d0eae7",
- "reference": "1a7ea2afc49c3ee6d87061f5a233e3a035d0eae7",
+ "url": "https://api.github.com/repos/vlucas/phpdotenv/zipball/a59a13791077fe3d44f90e7133eb68e7d22eaff2",
+ "reference": "a59a13791077fe3d44f90e7133eb68e7d22eaff2",
"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"
+ "graham-campbell/result-type": "^1.1.3",
+ "php": "^7.2.5 || ^8.0",
+ "phpoption/phpoption": "^1.9.3",
+ "symfony/polyfill-ctype": "^1.24",
+ "symfony/polyfill-mbstring": "^1.24",
+ "symfony/polyfill-php80": "^1.24"
},
"require-dev": {
- "bamarni/composer-bin-plugin": "^1.4.1",
+ "bamarni/composer-bin-plugin": "^1.8.2",
"ext-filter": "*",
- "phpunit/phpunit": "^7.5.20 || ^8.5.30 || ^9.5.25"
+ "phpunit/phpunit": "^8.5.34 || ^9.6.13 || ^10.4.2"
},
"suggest": {
"ext-filter": "Required to use the boolean validator."
@@ -1176,10 +1162,10 @@
"extra": {
"bamarni-bin": {
"bin-links": true,
- "forward-command": true
+ "forward-command": false
},
"branch-alias": {
- "dev-master": "5.5-dev"
+ "dev-master": "5.6-dev"
}
},
"autoload": {
@@ -1211,7 +1197,7 @@
],
"support": {
"issues": "https://github.com/vlucas/phpdotenv/issues",
- "source": "https://github.com/vlucas/phpdotenv/tree/v5.5.0"
+ "source": "https://github.com/vlucas/phpdotenv/tree/v5.6.1"
},
"funding": [
{
@@ -1223,7 +1209,7 @@
"type": "tidelift"
}
],
- "time": "2022-10-16T01:01:54+00:00"
+ "time": "2024-07-20T21:52:34+00:00"
}
],
"packages-dev": [],
@@ -1234,5 +1220,5 @@
"prefer-lowest": false,
"platform": [],
"platform-dev": [],
- "plugin-api-version": "2.3.0"
+ "plugin-api-version": "2.6.0"
}
diff --git a/css/calendar.css b/css/calendar.css
index 398325a..40a1028 100644
--- a/css/calendar.css
+++ b/css/calendar.css
@@ -1,39 +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;
-}
-
+/*
+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;
+}
+
diff --git a/css/clockpicker.css b/css/clockpicker.css
index 770b173..610401d 100644
--- a/css/clockpicker.css
+++ b/css/clockpicker.css
@@ -1,143 +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;
-}
+/*!
+ * 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;
+}
diff --git a/css/fa_all.css b/css/fa_all.css
index a51a10c..440a131 100644
--- a/css/fa_all.css
+++ b/css/fa_all.css
@@ -1,5 +1,5 @@
-/*!
- * Font Awesome Free 5.5.0 by @fontawesome - https://fontawesome.com
- * License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License)
- */
+/*!
+ * Font Awesome Free 5.5.0 by @fontawesome - https://fontawesome.com
+ * License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License)
+ */
.fa,.fab,.fal,.far,.fas{-moz-osx-font-smoothing:grayscale;-webkit-font-smoothing:antialiased;display:inline-block;font-style:normal;font-variant:normal;text-rendering:auto;line-height:1}.fa-lg{font-size:1.33333em;line-height:.75em;vertical-align:-.0667em}.fa-xs{font-size:.75em}.fa-sm{font-size:.875em}.fa-1x{font-size:1em}.fa-2x{font-size:2em}.fa-3x{font-size:3em}.fa-4x{font-size:4em}.fa-5x{font-size:5em}.fa-6x{font-size:6em}.fa-7x{font-size:7em}.fa-8x{font-size:8em}.fa-9x{font-size:9em}.fa-10x{font-size:10em}.fa-fw{text-align:center;width:1.25em}.fa-ul{list-style-type:none;margin-left:2.5em;padding-left:0}.fa-ul>li{position:relative}.fa-li{left:-2em;position:absolute;text-align:center;width:2em;line-height:inherit}.fa-border{border:.08em solid #eee;border-radius:.1em;padding:.2em .25em .15em}.fa-pull-left{float:left}.fa-pull-right{float:right}.fa.fa-pull-left,.fab.fa-pull-left,.fal.fa-pull-left,.far.fa-pull-left,.fas.fa-pull-left{margin-right:.3em}.fa.fa-pull-right,.fab.fa-pull-right,.fal.fa-pull-right,.far.fa-pull-right,.fas.fa-pull-right{margin-left:.3em}.fa-spin{animation:fa-spin 2s infinite linear}.fa-pulse{animation:fa-spin 1s infinite steps(8)}@keyframes fa-spin{0%{transform:rotate(0deg)}to{transform:rotate(1turn)}}.fa-rotate-90{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=1)";transform:rotate(90deg)}.fa-rotate-180{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=2)";transform:rotate(180deg)}.fa-rotate-270{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=3)";transform:rotate(270deg)}.fa-flip-horizontal{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=0, mirror=1)";transform:scaleX(-1)}.fa-flip-vertical{transform:scaleY(-1)}.fa-flip-horizontal.fa-flip-vertical,.fa-flip-vertical{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=2, mirror=1)"}.fa-flip-horizontal.fa-flip-vertical{transform:scale(-1)}:root .fa-flip-horizontal,:root .fa-flip-vertical,:root .fa-rotate-90,:root .fa-rotate-180,:root .fa-rotate-270{-webkit-filter:none;filter:none}.fa-stack{display:inline-block;height:2em;line-height:2em;position:relative;vertical-align:middle;width:2.5em}.fa-stack-1x,.fa-stack-2x{left:0;position:absolute;text-align:center;width:100%}.fa-stack-1x{line-height:inherit}.fa-stack-2x{font-size:2em}.fa-inverse{color:#fff}.fa-500px:before{content:"\f26e"}.fa-accessible-icon:before{content:"\f368"}.fa-accusoft:before{content:"\f369"}.fa-acquisitions-incorporated:before{content:"\f6af"}.fa-ad:before{content:"\f641"}.fa-address-book:before{content:"\f2b9"}.fa-address-card:before{content:"\f2bb"}.fa-adjust:before{content:"\f042"}.fa-adn:before{content:"\f170"}.fa-adversal:before{content:"\f36a"}.fa-affiliatetheme:before{content:"\f36b"}.fa-air-freshener:before{content:"\f5d0"}.fa-algolia:before{content:"\f36c"}.fa-align-center:before{content:"\f037"}.fa-align-justify:before{content:"\f039"}.fa-align-left:before{content:"\f036"}.fa-align-right:before{content:"\f038"}.fa-alipay:before{content:"\f642"}.fa-allergies:before{content:"\f461"}.fa-amazon:before{content:"\f270"}.fa-amazon-pay:before{content:"\f42c"}.fa-ambulance:before{content:"\f0f9"}.fa-american-sign-language-interpreting:before{content:"\f2a3"}.fa-amilia:before{content:"\f36d"}.fa-anchor:before{content:"\f13d"}.fa-android:before{content:"\f17b"}.fa-angellist:before{content:"\f209"}.fa-angle-double-down:before{content:"\f103"}.fa-angle-double-left:before{content:"\f100"}.fa-angle-double-right:before{content:"\f101"}.fa-angle-double-up:before{content:"\f102"}.fa-angle-down:before{content:"\f107"}.fa-angle-left:before{content:"\f104"}.fa-angle-right:before{content:"\f105"}.fa-angle-up:before{content:"\f106"}.fa-angry:before{content:"\f556"}.fa-angrycreative:before{content:"\f36e"}.fa-angular:before{content:"\f420"}.fa-ankh:before{content:"\f644"}.fa-app-store:before{content:"\f36f"}.fa-app-store-ios:before{content:"\f370"}.fa-apper:before{content:"\f371"}.fa-apple:before{content:"\f179"}.fa-apple-alt:before{content:"\f5d1"}.fa-apple-pay:before{content:"\f415"}.fa-archive:before{content:"\f187"}.fa-archway:before{content:"\f557"}.fa-arrow-alt-circle-down:before{content:"\f358"}.fa-arrow-alt-circle-left:before{content:"\f359"}.fa-arrow-alt-circle-right:before{content:"\f35a"}.fa-arrow-alt-circle-up:before{content:"\f35b"}.fa-arrow-circle-down:before{content:"\f0ab"}.fa-arrow-circle-left:before{content:"\f0a8"}.fa-arrow-circle-right:before{content:"\f0a9"}.fa-arrow-circle-up:before{content:"\f0aa"}.fa-arrow-down:before{content:"\f063"}.fa-arrow-left:before{content:"\f060"}.fa-arrow-right:before{content:"\f061"}.fa-arrow-up:before{content:"\f062"}.fa-arrows-alt:before{content:"\f0b2"}.fa-arrows-alt-h:before{content:"\f337"}.fa-arrows-alt-v:before{content:"\f338"}.fa-assistive-listening-systems:before{content:"\f2a2"}.fa-asterisk:before{content:"\f069"}.fa-asymmetrik:before{content:"\f372"}.fa-at:before{content:"\f1fa"}.fa-atlas:before{content:"\f558"}.fa-atom:before{content:"\f5d2"}.fa-audible:before{content:"\f373"}.fa-audio-description:before{content:"\f29e"}.fa-autoprefixer:before{content:"\f41c"}.fa-avianex:before{content:"\f374"}.fa-aviato:before{content:"\f421"}.fa-award:before{content:"\f559"}.fa-aws:before{content:"\f375"}.fa-backspace:before{content:"\f55a"}.fa-backward:before{content:"\f04a"}.fa-balance-scale:before{content:"\f24e"}.fa-ban:before{content:"\f05e"}.fa-band-aid:before{content:"\f462"}.fa-bandcamp:before{content:"\f2d5"}.fa-barcode:before{content:"\f02a"}.fa-bars:before{content:"\f0c9"}.fa-baseball-ball:before{content:"\f433"}.fa-basketball-ball:before{content:"\f434"}.fa-bath:before{content:"\f2cd"}.fa-battery-empty:before{content:"\f244"}.fa-battery-full:before{content:"\f240"}.fa-battery-half:before{content:"\f242"}.fa-battery-quarter:before{content:"\f243"}.fa-battery-three-quarters:before{content:"\f241"}.fa-bed:before{content:"\f236"}.fa-beer:before{content:"\f0fc"}.fa-behance:before{content:"\f1b4"}.fa-behance-square:before{content:"\f1b5"}.fa-bell:before{content:"\f0f3"}.fa-bell-slash:before{content:"\f1f6"}.fa-bezier-curve:before{content:"\f55b"}.fa-bible:before{content:"\f647"}.fa-bicycle:before{content:"\f206"}.fa-bimobject:before{content:"\f378"}.fa-binoculars:before{content:"\f1e5"}.fa-birthday-cake:before{content:"\f1fd"}.fa-bitbucket:before{content:"\f171"}.fa-bitcoin:before{content:"\f379"}.fa-bity:before{content:"\f37a"}.fa-black-tie:before{content:"\f27e"}.fa-blackberry:before{content:"\f37b"}.fa-blender:before{content:"\f517"}.fa-blender-phone:before{content:"\f6b6"}.fa-blind:before{content:"\f29d"}.fa-blogger:before{content:"\f37c"}.fa-blogger-b:before{content:"\f37d"}.fa-bluetooth:before{content:"\f293"}.fa-bluetooth-b:before{content:"\f294"}.fa-bold:before{content:"\f032"}.fa-bolt:before{content:"\f0e7"}.fa-bomb:before{content:"\f1e2"}.fa-bone:before{content:"\f5d7"}.fa-bong:before{content:"\f55c"}.fa-book:before{content:"\f02d"}.fa-book-dead:before{content:"\f6b7"}.fa-book-open:before{content:"\f518"}.fa-book-reader:before{content:"\f5da"}.fa-bookmark:before{content:"\f02e"}.fa-bowling-ball:before{content:"\f436"}.fa-box:before{content:"\f466"}.fa-box-open:before{content:"\f49e"}.fa-boxes:before{content:"\f468"}.fa-braille:before{content:"\f2a1"}.fa-brain:before{content:"\f5dc"}.fa-briefcase:before{content:"\f0b1"}.fa-briefcase-medical:before{content:"\f469"}.fa-broadcast-tower:before{content:"\f519"}.fa-broom:before{content:"\f51a"}.fa-brush:before{content:"\f55d"}.fa-btc:before{content:"\f15a"}.fa-bug:before{content:"\f188"}.fa-building:before{content:"\f1ad"}.fa-bullhorn:before{content:"\f0a1"}.fa-bullseye:before{content:"\f140"}.fa-burn:before{content:"\f46a"}.fa-buromobelexperte:before{content:"\f37f"}.fa-bus:before{content:"\f207"}.fa-bus-alt:before{content:"\f55e"}.fa-business-time:before{content:"\f64a"}.fa-buysellads:before{content:"\f20d"}.fa-calculator:before{content:"\f1ec"}.fa-calendar:before{content:"\f133"}.fa-calendar-alt:before{content:"\f073"}.fa-calendar-check:before{content:"\f274"}.fa-calendar-minus:before{content:"\f272"}.fa-calendar-plus:before{content:"\f271"}.fa-calendar-times:before{content:"\f273"}.fa-camera:before{content:"\f030"}.fa-camera-retro:before{content:"\f083"}.fa-campground:before{content:"\f6bb"}.fa-cannabis:before{content:"\f55f"}.fa-capsules:before{content:"\f46b"}.fa-car:before{content:"\f1b9"}.fa-car-alt:before{content:"\f5de"}.fa-car-battery:before{content:"\f5df"}.fa-car-crash:before{content:"\f5e1"}.fa-car-side:before{content:"\f5e4"}.fa-caret-down:before{content:"\f0d7"}.fa-caret-left:before{content:"\f0d9"}.fa-caret-right:before{content:"\f0da"}.fa-caret-square-down:before{content:"\f150"}.fa-caret-square-left:before{content:"\f191"}.fa-caret-square-right:before{content:"\f152"}.fa-caret-square-up:before{content:"\f151"}.fa-caret-up:before{content:"\f0d8"}.fa-cart-arrow-down:before{content:"\f218"}.fa-cart-plus:before{content:"\f217"}.fa-cat:before{content:"\f6be"}.fa-cc-amazon-pay:before{content:"\f42d"}.fa-cc-amex:before{content:"\f1f3"}.fa-cc-apple-pay:before{content:"\f416"}.fa-cc-diners-club:before{content:"\f24c"}.fa-cc-discover:before{content:"\f1f2"}.fa-cc-jcb:before{content:"\f24b"}.fa-cc-mastercard:before{content:"\f1f1"}.fa-cc-paypal:before{content:"\f1f4"}.fa-cc-stripe:before{content:"\f1f5"}.fa-cc-visa:before{content:"\f1f0"}.fa-centercode:before{content:"\f380"}.fa-certificate:before{content:"\f0a3"}.fa-chair:before{content:"\f6c0"}.fa-chalkboard:before{content:"\f51b"}.fa-chalkboard-teacher:before{content:"\f51c"}.fa-charging-station:before{content:"\f5e7"}.fa-chart-area:before{content:"\f1fe"}.fa-chart-bar:before{content:"\f080"}.fa-chart-line:before{content:"\f201"}.fa-chart-pie:before{content:"\f200"}.fa-check:before{content:"\f00c"}.fa-check-circle:before{content:"\f058"}.fa-check-double:before{content:"\f560"}.fa-check-square:before{content:"\f14a"}.fa-chess:before{content:"\f439"}.fa-chess-bishop:before{content:"\f43a"}.fa-chess-board:before{content:"\f43c"}.fa-chess-king:before{content:"\f43f"}.fa-chess-knight:before{content:"\f441"}.fa-chess-pawn:before{content:"\f443"}.fa-chess-queen:before{content:"\f445"}.fa-chess-rook:before{content:"\f447"}.fa-chevron-circle-down:before{content:"\f13a"}.fa-chevron-circle-left:before{content:"\f137"}.fa-chevron-circle-right:before{content:"\f138"}.fa-chevron-circle-up:before{content:"\f139"}.fa-chevron-down:before{content:"\f078"}.fa-chevron-left:before{content:"\f053"}.fa-chevron-right:before{content:"\f054"}.fa-chevron-up:before{content:"\f077"}.fa-child:before{content:"\f1ae"}.fa-chrome:before{content:"\f268"}.fa-church:before{content:"\f51d"}.fa-circle:before{content:"\f111"}.fa-circle-notch:before{content:"\f1ce"}.fa-city:before{content:"\f64f"}.fa-clipboard:before{content:"\f328"}.fa-clipboard-check:before{content:"\f46c"}.fa-clipboard-list:before{content:"\f46d"}.fa-clock:before{content:"\f017"}.fa-clone:before{content:"\f24d"}.fa-closed-captioning:before{content:"\f20a"}.fa-cloud:before{content:"\f0c2"}.fa-cloud-download-alt:before{content:"\f381"}.fa-cloud-meatball:before{content:"\f73b"}.fa-cloud-moon:before{content:"\f6c3"}.fa-cloud-moon-rain:before{content:"\f73c"}.fa-cloud-rain:before{content:"\f73d"}.fa-cloud-showers-heavy:before{content:"\f740"}.fa-cloud-sun:before{content:"\f6c4"}.fa-cloud-sun-rain:before{content:"\f743"}.fa-cloud-upload-alt:before{content:"\f382"}.fa-cloudscale:before{content:"\f383"}.fa-cloudsmith:before{content:"\f384"}.fa-cloudversify:before{content:"\f385"}.fa-cocktail:before{content:"\f561"}.fa-code:before{content:"\f121"}.fa-code-branch:before{content:"\f126"}.fa-codepen:before{content:"\f1cb"}.fa-codiepie:before{content:"\f284"}.fa-coffee:before{content:"\f0f4"}.fa-cog:before{content:"\f013"}.fa-cogs:before{content:"\f085"}.fa-coins:before{content:"\f51e"}.fa-columns:before{content:"\f0db"}.fa-comment:before{content:"\f075"}.fa-comment-alt:before{content:"\f27a"}.fa-comment-dollar:before{content:"\f651"}.fa-comment-dots:before{content:"\f4ad"}.fa-comment-slash:before{content:"\f4b3"}.fa-comments:before{content:"\f086"}.fa-comments-dollar:before{content:"\f653"}.fa-compact-disc:before{content:"\f51f"}.fa-compass:before{content:"\f14e"}.fa-compress:before{content:"\f066"}.fa-concierge-bell:before{content:"\f562"}.fa-connectdevelop:before{content:"\f20e"}.fa-contao:before{content:"\f26d"}.fa-cookie:before{content:"\f563"}.fa-cookie-bite:before{content:"\f564"}.fa-copy:before{content:"\f0c5"}.fa-copyright:before{content:"\f1f9"}.fa-couch:before{content:"\f4b8"}.fa-cpanel:before{content:"\f388"}.fa-creative-commons:before{content:"\f25e"}.fa-creative-commons-by:before{content:"\f4e7"}.fa-creative-commons-nc:before{content:"\f4e8"}.fa-creative-commons-nc-eu:before{content:"\f4e9"}.fa-creative-commons-nc-jp:before{content:"\f4ea"}.fa-creative-commons-nd:before{content:"\f4eb"}.fa-creative-commons-pd:before{content:"\f4ec"}.fa-creative-commons-pd-alt:before{content:"\f4ed"}.fa-creative-commons-remix:before{content:"\f4ee"}.fa-creative-commons-sa:before{content:"\f4ef"}.fa-creative-commons-sampling:before{content:"\f4f0"}.fa-creative-commons-sampling-plus:before{content:"\f4f1"}.fa-creative-commons-share:before{content:"\f4f2"}.fa-creative-commons-zero:before{content:"\f4f3"}.fa-credit-card:before{content:"\f09d"}.fa-critical-role:before{content:"\f6c9"}.fa-crop:before{content:"\f125"}.fa-crop-alt:before{content:"\f565"}.fa-cross:before{content:"\f654"}.fa-crosshairs:before{content:"\f05b"}.fa-crow:before{content:"\f520"}.fa-crown:before{content:"\f521"}.fa-css3:before{content:"\f13c"}.fa-css3-alt:before{content:"\f38b"}.fa-cube:before{content:"\f1b2"}.fa-cubes:before{content:"\f1b3"}.fa-cut:before{content:"\f0c4"}.fa-cuttlefish:before{content:"\f38c"}.fa-d-and-d:before{content:"\f38d"}.fa-d-and-d-beyond:before{content:"\f6ca"}.fa-dashcube:before{content:"\f210"}.fa-database:before{content:"\f1c0"}.fa-deaf:before{content:"\f2a4"}.fa-delicious:before{content:"\f1a5"}.fa-democrat:before{content:"\f747"}.fa-deploydog:before{content:"\f38e"}.fa-deskpro:before{content:"\f38f"}.fa-desktop:before{content:"\f108"}.fa-dev:before{content:"\f6cc"}.fa-deviantart:before{content:"\f1bd"}.fa-dharmachakra:before{content:"\f655"}.fa-diagnoses:before{content:"\f470"}.fa-dice:before{content:"\f522"}.fa-dice-d20:before{content:"\f6cf"}.fa-dice-d6:before{content:"\f6d1"}.fa-dice-five:before{content:"\f523"}.fa-dice-four:before{content:"\f524"}.fa-dice-one:before{content:"\f525"}.fa-dice-six:before{content:"\f526"}.fa-dice-three:before{content:"\f527"}.fa-dice-two:before{content:"\f528"}.fa-digg:before{content:"\f1a6"}.fa-digital-ocean:before{content:"\f391"}.fa-digital-tachograph:before{content:"\f566"}.fa-directions:before{content:"\f5eb"}.fa-discord:before{content:"\f392"}.fa-discourse:before{content:"\f393"}.fa-divide:before{content:"\f529"}.fa-dizzy:before{content:"\f567"}.fa-dna:before{content:"\f471"}.fa-dochub:before{content:"\f394"}.fa-docker:before{content:"\f395"}.fa-dog:before{content:"\f6d3"}.fa-dollar-sign:before{content:"\f155"}.fa-dolly:before{content:"\f472"}.fa-dolly-flatbed:before{content:"\f474"}.fa-donate:before{content:"\f4b9"}.fa-door-closed:before{content:"\f52a"}.fa-door-open:before{content:"\f52b"}.fa-dot-circle:before{content:"\f192"}.fa-dove:before{content:"\f4ba"}.fa-download:before{content:"\f019"}.fa-draft2digital:before{content:"\f396"}.fa-drafting-compass:before{content:"\f568"}.fa-dragon:before{content:"\f6d5"}.fa-draw-polygon:before{content:"\f5ee"}.fa-dribbble:before{content:"\f17d"}.fa-dribbble-square:before{content:"\f397"}.fa-dropbox:before{content:"\f16b"}.fa-drum:before{content:"\f569"}.fa-drum-steelpan:before{content:"\f56a"}.fa-drumstick-bite:before{content:"\f6d7"}.fa-drupal:before{content:"\f1a9"}.fa-dumbbell:before{content:"\f44b"}.fa-dungeon:before{content:"\f6d9"}.fa-dyalog:before{content:"\f399"}.fa-earlybirds:before{content:"\f39a"}.fa-ebay:before{content:"\f4f4"}.fa-edge:before{content:"\f282"}.fa-edit:before{content:"\f044"}.fa-eject:before{content:"\f052"}.fa-elementor:before{content:"\f430"}.fa-ellipsis-h:before{content:"\f141"}.fa-ellipsis-v:before{content:"\f142"}.fa-ello:before{content:"\f5f1"}.fa-ember:before{content:"\f423"}.fa-empire:before{content:"\f1d1"}.fa-envelope:before{content:"\f0e0"}.fa-envelope-open:before{content:"\f2b6"}.fa-envelope-open-text:before{content:"\f658"}.fa-envelope-square:before{content:"\f199"}.fa-envira:before{content:"\f299"}.fa-equals:before{content:"\f52c"}.fa-eraser:before{content:"\f12d"}.fa-erlang:before{content:"\f39d"}.fa-ethereum:before{content:"\f42e"}.fa-etsy:before{content:"\f2d7"}.fa-euro-sign:before{content:"\f153"}.fa-exchange-alt:before{content:"\f362"}.fa-exclamation:before{content:"\f12a"}.fa-exclamation-circle:before{content:"\f06a"}.fa-exclamation-triangle:before{content:"\f071"}.fa-expand:before{content:"\f065"}.fa-expand-arrows-alt:before{content:"\f31e"}.fa-expeditedssl:before{content:"\f23e"}.fa-external-link-alt:before{content:"\f35d"}.fa-external-link-square-alt:before{content:"\f360"}.fa-eye:before{content:"\f06e"}.fa-eye-dropper:before{content:"\f1fb"}.fa-eye-slash:before{content:"\f070"}.fa-facebook:before{content:"\f09a"}.fa-facebook-f:before{content:"\f39e"}.fa-facebook-messenger:before{content:"\f39f"}.fa-facebook-square:before{content:"\f082"}.fa-fantasy-flight-games:before{content:"\f6dc"}.fa-fast-backward:before{content:"\f049"}.fa-fast-forward:before{content:"\f050"}.fa-fax:before{content:"\f1ac"}.fa-feather:before{content:"\f52d"}.fa-feather-alt:before{content:"\f56b"}.fa-female:before{content:"\f182"}.fa-fighter-jet:before{content:"\f0fb"}.fa-file:before{content:"\f15b"}.fa-file-alt:before{content:"\f15c"}.fa-file-archive:before{content:"\f1c6"}.fa-file-audio:before{content:"\f1c7"}.fa-file-code:before{content:"\f1c9"}.fa-file-contract:before{content:"\f56c"}.fa-file-csv:before{content:"\f6dd"}.fa-file-download:before{content:"\f56d"}.fa-file-excel:before{content:"\f1c3"}.fa-file-export:before{content:"\f56e"}.fa-file-image:before{content:"\f1c5"}.fa-file-import:before{content:"\f56f"}.fa-file-invoice:before{content:"\f570"}.fa-file-invoice-dollar:before{content:"\f571"}.fa-file-medical:before{content:"\f477"}.fa-file-medical-alt:before{content:"\f478"}.fa-file-pdf:before{content:"\f1c1"}.fa-file-powerpoint:before{content:"\f1c4"}.fa-file-prescription:before{content:"\f572"}.fa-file-signature:before{content:"\f573"}.fa-file-upload:before{content:"\f574"}.fa-file-video:before{content:"\f1c8"}.fa-file-word:before{content:"\f1c2"}.fa-fill:before{content:"\f575"}.fa-fill-drip:before{content:"\f576"}.fa-film:before{content:"\f008"}.fa-filter:before{content:"\f0b0"}.fa-fingerprint:before{content:"\f577"}.fa-fire:before{content:"\f06d"}.fa-fire-extinguisher:before{content:"\f134"}.fa-firefox:before{content:"\f269"}.fa-first-aid:before{content:"\f479"}.fa-first-order:before{content:"\f2b0"}.fa-first-order-alt:before{content:"\f50a"}.fa-firstdraft:before{content:"\f3a1"}.fa-fish:before{content:"\f578"}.fa-fist-raised:before{content:"\f6de"}.fa-flag:before{content:"\f024"}.fa-flag-checkered:before{content:"\f11e"}.fa-flag-usa:before{content:"\f74d"}.fa-flask:before{content:"\f0c3"}.fa-flickr:before{content:"\f16e"}.fa-flipboard:before{content:"\f44d"}.fa-flushed:before{content:"\f579"}.fa-fly:before{content:"\f417"}.fa-folder:before{content:"\f07b"}.fa-folder-minus:before{content:"\f65d"}.fa-folder-open:before{content:"\f07c"}.fa-folder-plus:before{content:"\f65e"}.fa-font:before{content:"\f031"}.fa-font-awesome:before{content:"\f2b4"}.fa-font-awesome-alt:before{content:"\f35c"}.fa-font-awesome-flag:before{content:"\f425"}.fa-font-awesome-logo-full:before{content:"\f4e6"}.fa-fonticons:before{content:"\f280"}.fa-fonticons-fi:before{content:"\f3a2"}.fa-football-ball:before{content:"\f44e"}.fa-fort-awesome:before{content:"\f286"}.fa-fort-awesome-alt:before{content:"\f3a3"}.fa-forumbee:before{content:"\f211"}.fa-forward:before{content:"\f04e"}.fa-foursquare:before{content:"\f180"}.fa-free-code-camp:before{content:"\f2c5"}.fa-freebsd:before{content:"\f3a4"}.fa-frog:before{content:"\f52e"}.fa-frown:before{content:"\f119"}.fa-frown-open:before{content:"\f57a"}.fa-fulcrum:before{content:"\f50b"}.fa-funnel-dollar:before{content:"\f662"}.fa-futbol:before{content:"\f1e3"}.fa-galactic-republic:before{content:"\f50c"}.fa-galactic-senate:before{content:"\f50d"}.fa-gamepad:before{content:"\f11b"}.fa-gas-pump:before{content:"\f52f"}.fa-gavel:before{content:"\f0e3"}.fa-gem:before{content:"\f3a5"}.fa-genderless:before{content:"\f22d"}.fa-get-pocket:before{content:"\f265"}.fa-gg:before{content:"\f260"}.fa-gg-circle:before{content:"\f261"}.fa-ghost:before{content:"\f6e2"}.fa-gift:before{content:"\f06b"}.fa-git:before{content:"\f1d3"}.fa-git-square:before{content:"\f1d2"}.fa-github:before{content:"\f09b"}.fa-github-alt:before{content:"\f113"}.fa-github-square:before{content:"\f092"}.fa-gitkraken:before{content:"\f3a6"}.fa-gitlab:before{content:"\f296"}.fa-gitter:before{content:"\f426"}.fa-glass-martini:before{content:"\f000"}.fa-glass-martini-alt:before{content:"\f57b"}.fa-glasses:before{content:"\f530"}.fa-glide:before{content:"\f2a5"}.fa-glide-g:before{content:"\f2a6"}.fa-globe:before{content:"\f0ac"}.fa-globe-africa:before{content:"\f57c"}.fa-globe-americas:before{content:"\f57d"}.fa-globe-asia:before{content:"\f57e"}.fa-gofore:before{content:"\f3a7"}.fa-golf-ball:before{content:"\f450"}.fa-goodreads:before{content:"\f3a8"}.fa-goodreads-g:before{content:"\f3a9"}.fa-google:before{content:"\f1a0"}.fa-google-drive:before{content:"\f3aa"}.fa-google-play:before{content:"\f3ab"}.fa-google-plus:before{content:"\f2b3"}.fa-google-plus-g:before{content:"\f0d5"}.fa-google-plus-square:before{content:"\f0d4"}.fa-google-wallet:before{content:"\f1ee"}.fa-gopuram:before{content:"\f664"}.fa-graduation-cap:before{content:"\f19d"}.fa-gratipay:before{content:"\f184"}.fa-grav:before{content:"\f2d6"}.fa-greater-than:before{content:"\f531"}.fa-greater-than-equal:before{content:"\f532"}.fa-grimace:before{content:"\f57f"}.fa-grin:before{content:"\f580"}.fa-grin-alt:before{content:"\f581"}.fa-grin-beam:before{content:"\f582"}.fa-grin-beam-sweat:before{content:"\f583"}.fa-grin-hearts:before{content:"\f584"}.fa-grin-squint:before{content:"\f585"}.fa-grin-squint-tears:before{content:"\f586"}.fa-grin-stars:before{content:"\f587"}.fa-grin-tears:before{content:"\f588"}.fa-grin-tongue:before{content:"\f589"}.fa-grin-tongue-squint:before{content:"\f58a"}.fa-grin-tongue-wink:before{content:"\f58b"}.fa-grin-wink:before{content:"\f58c"}.fa-grip-horizontal:before{content:"\f58d"}.fa-grip-vertical:before{content:"\f58e"}.fa-gripfire:before{content:"\f3ac"}.fa-grunt:before{content:"\f3ad"}.fa-gulp:before{content:"\f3ae"}.fa-h-square:before{content:"\f0fd"}.fa-hacker-news:before{content:"\f1d4"}.fa-hacker-news-square:before{content:"\f3af"}.fa-hackerrank:before{content:"\f5f7"}.fa-hammer:before{content:"\f6e3"}.fa-hamsa:before{content:"\f665"}.fa-hand-holding:before{content:"\f4bd"}.fa-hand-holding-heart:before{content:"\f4be"}.fa-hand-holding-usd:before{content:"\f4c0"}.fa-hand-lizard:before{content:"\f258"}.fa-hand-paper:before{content:"\f256"}.fa-hand-peace:before{content:"\f25b"}.fa-hand-point-down:before{content:"\f0a7"}.fa-hand-point-left:before{content:"\f0a5"}.fa-hand-point-right:before{content:"\f0a4"}.fa-hand-point-up:before{content:"\f0a6"}.fa-hand-pointer:before{content:"\f25a"}.fa-hand-rock:before{content:"\f255"}.fa-hand-scissors:before{content:"\f257"}.fa-hand-spock:before{content:"\f259"}.fa-hands:before{content:"\f4c2"}.fa-hands-helping:before{content:"\f4c4"}.fa-handshake:before{content:"\f2b5"}.fa-hanukiah:before{content:"\f6e6"}.fa-hashtag:before{content:"\f292"}.fa-hat-wizard:before{content:"\f6e8"}.fa-haykal:before{content:"\f666"}.fa-hdd:before{content:"\f0a0"}.fa-heading:before{content:"\f1dc"}.fa-headphones:before{content:"\f025"}.fa-headphones-alt:before{content:"\f58f"}.fa-headset:before{content:"\f590"}.fa-heart:before{content:"\f004"}.fa-heartbeat:before{content:"\f21e"}.fa-helicopter:before{content:"\f533"}.fa-highlighter:before{content:"\f591"}.fa-hiking:before{content:"\f6ec"}.fa-hippo:before{content:"\f6ed"}.fa-hips:before{content:"\f452"}.fa-hire-a-helper:before{content:"\f3b0"}.fa-history:before{content:"\f1da"}.fa-hockey-puck:before{content:"\f453"}.fa-home:before{content:"\f015"}.fa-hooli:before{content:"\f427"}.fa-hornbill:before{content:"\f592"}.fa-horse:before{content:"\f6f0"}.fa-hospital:before{content:"\f0f8"}.fa-hospital-alt:before{content:"\f47d"}.fa-hospital-symbol:before{content:"\f47e"}.fa-hot-tub:before{content:"\f593"}.fa-hotel:before{content:"\f594"}.fa-hotjar:before{content:"\f3b1"}.fa-hourglass:before{content:"\f254"}.fa-hourglass-end:before{content:"\f253"}.fa-hourglass-half:before{content:"\f252"}.fa-hourglass-start:before{content:"\f251"}.fa-house-damage:before{content:"\f6f1"}.fa-houzz:before{content:"\f27c"}.fa-hryvnia:before{content:"\f6f2"}.fa-html5:before{content:"\f13b"}.fa-hubspot:before{content:"\f3b2"}.fa-i-cursor:before{content:"\f246"}.fa-id-badge:before{content:"\f2c1"}.fa-id-card:before{content:"\f2c2"}.fa-id-card-alt:before{content:"\f47f"}.fa-image:before{content:"\f03e"}.fa-images:before{content:"\f302"}.fa-imdb:before{content:"\f2d8"}.fa-inbox:before{content:"\f01c"}.fa-indent:before{content:"\f03c"}.fa-industry:before{content:"\f275"}.fa-infinity:before{content:"\f534"}.fa-info:before{content:"\f129"}.fa-info-circle:before{content:"\f05a"}.fa-instagram:before{content:"\f16d"}.fa-internet-explorer:before{content:"\f26b"}.fa-ioxhost:before{content:"\f208"}.fa-italic:before{content:"\f033"}.fa-itunes:before{content:"\f3b4"}.fa-itunes-note:before{content:"\f3b5"}.fa-java:before{content:"\f4e4"}.fa-jedi:before{content:"\f669"}.fa-jedi-order:before{content:"\f50e"}.fa-jenkins:before{content:"\f3b6"}.fa-joget:before{content:"\f3b7"}.fa-joint:before{content:"\f595"}.fa-joomla:before{content:"\f1aa"}.fa-journal-whills:before{content:"\f66a"}.fa-js:before{content:"\f3b8"}.fa-js-square:before{content:"\f3b9"}.fa-jsfiddle:before{content:"\f1cc"}.fa-kaaba:before{content:"\f66b"}.fa-kaggle:before{content:"\f5fa"}.fa-key:before{content:"\f084"}.fa-keybase:before{content:"\f4f5"}.fa-keyboard:before{content:"\f11c"}.fa-keycdn:before{content:"\f3ba"}.fa-khanda:before{content:"\f66d"}.fa-kickstarter:before{content:"\f3bb"}.fa-kickstarter-k:before{content:"\f3bc"}.fa-kiss:before{content:"\f596"}.fa-kiss-beam:before{content:"\f597"}.fa-kiss-wink-heart:before{content:"\f598"}.fa-kiwi-bird:before{content:"\f535"}.fa-korvue:before{content:"\f42f"}.fa-landmark:before{content:"\f66f"}.fa-language:before{content:"\f1ab"}.fa-laptop:before{content:"\f109"}.fa-laptop-code:before{content:"\f5fc"}.fa-laravel:before{content:"\f3bd"}.fa-lastfm:before{content:"\f202"}.fa-lastfm-square:before{content:"\f203"}.fa-laugh:before{content:"\f599"}.fa-laugh-beam:before{content:"\f59a"}.fa-laugh-squint:before{content:"\f59b"}.fa-laugh-wink:before{content:"\f59c"}.fa-layer-group:before{content:"\f5fd"}.fa-leaf:before{content:"\f06c"}.fa-leanpub:before{content:"\f212"}.fa-lemon:before{content:"\f094"}.fa-less:before{content:"\f41d"}.fa-less-than:before{content:"\f536"}.fa-less-than-equal:before{content:"\f537"}.fa-level-down-alt:before{content:"\f3be"}.fa-level-up-alt:before{content:"\f3bf"}.fa-life-ring:before{content:"\f1cd"}.fa-lightbulb:before{content:"\f0eb"}.fa-line:before{content:"\f3c0"}.fa-link:before{content:"\f0c1"}.fa-linkedin:before{content:"\f08c"}.fa-linkedin-in:before{content:"\f0e1"}.fa-linode:before{content:"\f2b8"}.fa-linux:before{content:"\f17c"}.fa-lira-sign:before{content:"\f195"}.fa-list:before{content:"\f03a"}.fa-list-alt:before{content:"\f022"}.fa-list-ol:before{content:"\f0cb"}.fa-list-ul:before{content:"\f0ca"}.fa-location-arrow:before{content:"\f124"}.fa-lock:before{content:"\f023"}.fa-lock-open:before{content:"\f3c1"}.fa-long-arrow-alt-down:before{content:"\f309"}.fa-long-arrow-alt-left:before{content:"\f30a"}.fa-long-arrow-alt-right:before{content:"\f30b"}.fa-long-arrow-alt-up:before{content:"\f30c"}.fa-low-vision:before{content:"\f2a8"}.fa-luggage-cart:before{content:"\f59d"}.fa-lyft:before{content:"\f3c3"}.fa-magento:before{content:"\f3c4"}.fa-magic:before{content:"\f0d0"}.fa-magnet:before{content:"\f076"}.fa-mail-bulk:before{content:"\f674"}.fa-mailchimp:before{content:"\f59e"}.fa-male:before{content:"\f183"}.fa-mandalorian:before{content:"\f50f"}.fa-map:before{content:"\f279"}.fa-map-marked:before{content:"\f59f"}.fa-map-marked-alt:before{content:"\f5a0"}.fa-map-marker:before{content:"\f041"}.fa-map-marker-alt:before{content:"\f3c5"}.fa-map-pin:before{content:"\f276"}.fa-map-signs:before{content:"\f277"}.fa-markdown:before{content:"\f60f"}.fa-marker:before{content:"\f5a1"}.fa-mars:before{content:"\f222"}.fa-mars-double:before{content:"\f227"}.fa-mars-stroke:before{content:"\f229"}.fa-mars-stroke-h:before{content:"\f22b"}.fa-mars-stroke-v:before{content:"\f22a"}.fa-mask:before{content:"\f6fa"}.fa-mastodon:before{content:"\f4f6"}.fa-maxcdn:before{content:"\f136"}.fa-medal:before{content:"\f5a2"}.fa-medapps:before{content:"\f3c6"}.fa-medium:before{content:"\f23a"}.fa-medium-m:before{content:"\f3c7"}.fa-medkit:before{content:"\f0fa"}.fa-medrt:before{content:"\f3c8"}.fa-meetup:before{content:"\f2e0"}.fa-megaport:before{content:"\f5a3"}.fa-meh:before{content:"\f11a"}.fa-meh-blank:before{content:"\f5a4"}.fa-meh-rolling-eyes:before{content:"\f5a5"}.fa-memory:before{content:"\f538"}.fa-menorah:before{content:"\f676"}.fa-mercury:before{content:"\f223"}.fa-meteor:before{content:"\f753"}.fa-microchip:before{content:"\f2db"}.fa-microphone:before{content:"\f130"}.fa-microphone-alt:before{content:"\f3c9"}.fa-microphone-alt-slash:before{content:"\f539"}.fa-microphone-slash:before{content:"\f131"}.fa-microscope:before{content:"\f610"}.fa-microsoft:before{content:"\f3ca"}.fa-minus:before{content:"\f068"}.fa-minus-circle:before{content:"\f056"}.fa-minus-square:before{content:"\f146"}.fa-mix:before{content:"\f3cb"}.fa-mixcloud:before{content:"\f289"}.fa-mizuni:before{content:"\f3cc"}.fa-mobile:before{content:"\f10b"}.fa-mobile-alt:before{content:"\f3cd"}.fa-modx:before{content:"\f285"}.fa-monero:before{content:"\f3d0"}.fa-money-bill:before{content:"\f0d6"}.fa-money-bill-alt:before{content:"\f3d1"}.fa-money-bill-wave:before{content:"\f53a"}.fa-money-bill-wave-alt:before{content:"\f53b"}.fa-money-check:before{content:"\f53c"}.fa-money-check-alt:before{content:"\f53d"}.fa-monument:before{content:"\f5a6"}.fa-moon:before{content:"\f186"}.fa-mortar-pestle:before{content:"\f5a7"}.fa-mosque:before{content:"\f678"}.fa-motorcycle:before{content:"\f21c"}.fa-mountain:before{content:"\f6fc"}.fa-mouse-pointer:before{content:"\f245"}.fa-music:before{content:"\f001"}.fa-napster:before{content:"\f3d2"}.fa-neos:before{content:"\f612"}.fa-network-wired:before{content:"\f6ff"}.fa-neuter:before{content:"\f22c"}.fa-newspaper:before{content:"\f1ea"}.fa-nimblr:before{content:"\f5a8"}.fa-nintendo-switch:before{content:"\f418"}.fa-node:before{content:"\f419"}.fa-node-js:before{content:"\f3d3"}.fa-not-equal:before{content:"\f53e"}.fa-notes-medical:before{content:"\f481"}.fa-npm:before{content:"\f3d4"}.fa-ns8:before{content:"\f3d5"}.fa-nutritionix:before{content:"\f3d6"}.fa-object-group:before{content:"\f247"}.fa-object-ungroup:before{content:"\f248"}.fa-odnoklassniki:before{content:"\f263"}.fa-odnoklassniki-square:before{content:"\f264"}.fa-oil-can:before{content:"\f613"}.fa-old-republic:before{content:"\f510"}.fa-om:before{content:"\f679"}.fa-opencart:before{content:"\f23d"}.fa-openid:before{content:"\f19b"}.fa-opera:before{content:"\f26a"}.fa-optin-monster:before{content:"\f23c"}.fa-osi:before{content:"\f41a"}.fa-otter:before{content:"\f700"}.fa-outdent:before{content:"\f03b"}.fa-page4:before{content:"\f3d7"}.fa-pagelines:before{content:"\f18c"}.fa-paint-brush:before{content:"\f1fc"}.fa-paint-roller:before{content:"\f5aa"}.fa-palette:before{content:"\f53f"}.fa-palfed:before{content:"\f3d8"}.fa-pallet:before{content:"\f482"}.fa-paper-plane:before{content:"\f1d8"}.fa-paperclip:before{content:"\f0c6"}.fa-parachute-box:before{content:"\f4cd"}.fa-paragraph:before{content:"\f1dd"}.fa-parking:before{content:"\f540"}.fa-passport:before{content:"\f5ab"}.fa-pastafarianism:before{content:"\f67b"}.fa-paste:before{content:"\f0ea"}.fa-patreon:before{content:"\f3d9"}.fa-pause:before{content:"\f04c"}.fa-pause-circle:before{content:"\f28b"}.fa-paw:before{content:"\f1b0"}.fa-paypal:before{content:"\f1ed"}.fa-peace:before{content:"\f67c"}.fa-pen:before{content:"\f304"}.fa-pen-alt:before{content:"\f305"}.fa-pen-fancy:before{content:"\f5ac"}.fa-pen-nib:before{content:"\f5ad"}.fa-pen-square:before{content:"\f14b"}.fa-pencil-alt:before{content:"\f303"}.fa-pencil-ruler:before{content:"\f5ae"}.fa-penny-arcade:before{content:"\f704"}.fa-people-carry:before{content:"\f4ce"}.fa-percent:before{content:"\f295"}.fa-percentage:before{content:"\f541"}.fa-periscope:before{content:"\f3da"}.fa-person-booth:before{content:"\f756"}.fa-phabricator:before{content:"\f3db"}.fa-phoenix-framework:before{content:"\f3dc"}.fa-phoenix-squadron:before{content:"\f511"}.fa-phone:before{content:"\f095"}.fa-phone-slash:before{content:"\f3dd"}.fa-phone-square:before{content:"\f098"}.fa-phone-volume:before{content:"\f2a0"}.fa-php:before{content:"\f457"}.fa-pied-piper:before{content:"\f2ae"}.fa-pied-piper-alt:before{content:"\f1a8"}.fa-pied-piper-hat:before{content:"\f4e5"}.fa-pied-piper-pp:before{content:"\f1a7"}.fa-piggy-bank:before{content:"\f4d3"}.fa-pills:before{content:"\f484"}.fa-pinterest:before{content:"\f0d2"}.fa-pinterest-p:before{content:"\f231"}.fa-pinterest-square:before{content:"\f0d3"}.fa-place-of-worship:before{content:"\f67f"}.fa-plane:before{content:"\f072"}.fa-plane-arrival:before{content:"\f5af"}.fa-plane-departure:before{content:"\f5b0"}.fa-play:before{content:"\f04b"}.fa-play-circle:before{content:"\f144"}.fa-playstation:before{content:"\f3df"}.fa-plug:before{content:"\f1e6"}.fa-plus:before{content:"\f067"}.fa-plus-circle:before{content:"\f055"}.fa-plus-square:before{content:"\f0fe"}.fa-podcast:before{content:"\f2ce"}.fa-poll:before{content:"\f681"}.fa-poll-h:before{content:"\f682"}.fa-poo:before{content:"\f2fe"}.fa-poo-storm:before{content:"\f75a"}.fa-poop:before{content:"\f619"}.fa-portrait:before{content:"\f3e0"}.fa-pound-sign:before{content:"\f154"}.fa-power-off:before{content:"\f011"}.fa-pray:before{content:"\f683"}.fa-praying-hands:before{content:"\f684"}.fa-prescription:before{content:"\f5b1"}.fa-prescription-bottle:before{content:"\f485"}.fa-prescription-bottle-alt:before{content:"\f486"}.fa-print:before{content:"\f02f"}.fa-procedures:before{content:"\f487"}.fa-product-hunt:before{content:"\f288"}.fa-project-diagram:before{content:"\f542"}.fa-pushed:before{content:"\f3e1"}.fa-puzzle-piece:before{content:"\f12e"}.fa-python:before{content:"\f3e2"}.fa-qq:before{content:"\f1d6"}.fa-qrcode:before{content:"\f029"}.fa-question:before{content:"\f128"}.fa-question-circle:before{content:"\f059"}.fa-quidditch:before{content:"\f458"}.fa-quinscape:before{content:"\f459"}.fa-quora:before{content:"\f2c4"}.fa-quote-left:before{content:"\f10d"}.fa-quote-right:before{content:"\f10e"}.fa-quran:before{content:"\f687"}.fa-r-project:before{content:"\f4f7"}.fa-rainbow:before{content:"\f75b"}.fa-random:before{content:"\f074"}.fa-ravelry:before{content:"\f2d9"}.fa-react:before{content:"\f41b"}.fa-reacteurope:before{content:"\f75d"}.fa-readme:before{content:"\f4d5"}.fa-rebel:before{content:"\f1d0"}.fa-receipt:before{content:"\f543"}.fa-recycle:before{content:"\f1b8"}.fa-red-river:before{content:"\f3e3"}.fa-reddit:before{content:"\f1a1"}.fa-reddit-alien:before{content:"\f281"}.fa-reddit-square:before{content:"\f1a2"}.fa-redo:before{content:"\f01e"}.fa-redo-alt:before{content:"\f2f9"}.fa-registered:before{content:"\f25d"}.fa-renren:before{content:"\f18b"}.fa-reply:before{content:"\f3e5"}.fa-reply-all:before{content:"\f122"}.fa-replyd:before{content:"\f3e6"}.fa-republican:before{content:"\f75e"}.fa-researchgate:before{content:"\f4f8"}.fa-resolving:before{content:"\f3e7"}.fa-retweet:before{content:"\f079"}.fa-rev:before{content:"\f5b2"}.fa-ribbon:before{content:"\f4d6"}.fa-ring:before{content:"\f70b"}.fa-road:before{content:"\f018"}.fa-robot:before{content:"\f544"}.fa-rocket:before{content:"\f135"}.fa-rocketchat:before{content:"\f3e8"}.fa-rockrms:before{content:"\f3e9"}.fa-route:before{content:"\f4d7"}.fa-rss:before{content:"\f09e"}.fa-rss-square:before{content:"\f143"}.fa-ruble-sign:before{content:"\f158"}.fa-ruler:before{content:"\f545"}.fa-ruler-combined:before{content:"\f546"}.fa-ruler-horizontal:before{content:"\f547"}.fa-ruler-vertical:before{content:"\f548"}.fa-running:before{content:"\f70c"}.fa-rupee-sign:before{content:"\f156"}.fa-sad-cry:before{content:"\f5b3"}.fa-sad-tear:before{content:"\f5b4"}.fa-safari:before{content:"\f267"}.fa-sass:before{content:"\f41e"}.fa-save:before{content:"\f0c7"}.fa-schlix:before{content:"\f3ea"}.fa-school:before{content:"\f549"}.fa-screwdriver:before{content:"\f54a"}.fa-scribd:before{content:"\f28a"}.fa-scroll:before{content:"\f70e"}.fa-search:before{content:"\f002"}.fa-search-dollar:before{content:"\f688"}.fa-search-location:before{content:"\f689"}.fa-search-minus:before{content:"\f010"}.fa-search-plus:before{content:"\f00e"}.fa-searchengin:before{content:"\f3eb"}.fa-seedling:before{content:"\f4d8"}.fa-sellcast:before{content:"\f2da"}.fa-sellsy:before{content:"\f213"}.fa-server:before{content:"\f233"}.fa-servicestack:before{content:"\f3ec"}.fa-shapes:before{content:"\f61f"}.fa-share:before{content:"\f064"}.fa-share-alt:before{content:"\f1e0"}.fa-share-alt-square:before{content:"\f1e1"}.fa-share-square:before{content:"\f14d"}.fa-shekel-sign:before{content:"\f20b"}.fa-shield-alt:before{content:"\f3ed"}.fa-ship:before{content:"\f21a"}.fa-shipping-fast:before{content:"\f48b"}.fa-shirtsinbulk:before{content:"\f214"}.fa-shoe-prints:before{content:"\f54b"}.fa-shopping-bag:before{content:"\f290"}.fa-shopping-basket:before{content:"\f291"}.fa-shopping-cart:before{content:"\f07a"}.fa-shopware:before{content:"\f5b5"}.fa-shower:before{content:"\f2cc"}.fa-shuttle-van:before{content:"\f5b6"}.fa-sign:before{content:"\f4d9"}.fa-sign-in-alt:before{content:"\f2f6"}.fa-sign-language:before{content:"\f2a7"}.fa-sign-out-alt:before{content:"\f2f5"}.fa-signal:before{content:"\f012"}.fa-signature:before{content:"\f5b7"}.fa-simplybuilt:before{content:"\f215"}.fa-sistrix:before{content:"\f3ee"}.fa-sitemap:before{content:"\f0e8"}.fa-sith:before{content:"\f512"}.fa-skull:before{content:"\f54c"}.fa-skull-crossbones:before{content:"\f714"}.fa-skyatlas:before{content:"\f216"}.fa-skype:before{content:"\f17e"}.fa-slack:before{content:"\f198"}.fa-slack-hash:before{content:"\f3ef"}.fa-slash:before{content:"\f715"}.fa-sliders-h:before{content:"\f1de"}.fa-slideshare:before{content:"\f1e7"}.fa-smile:before{content:"\f118"}.fa-smile-beam:before{content:"\f5b8"}.fa-smile-wink:before{content:"\f4da"}.fa-smog:before{content:"\f75f"}.fa-smoking:before{content:"\f48d"}.fa-smoking-ban:before{content:"\f54d"}.fa-snapchat:before{content:"\f2ab"}.fa-snapchat-ghost:before{content:"\f2ac"}.fa-snapchat-square:before{content:"\f2ad"}.fa-snowflake:before{content:"\f2dc"}.fa-socks:before{content:"\f696"}.fa-solar-panel:before{content:"\f5ba"}.fa-sort:before{content:"\f0dc"}.fa-sort-alpha-down:before{content:"\f15d"}.fa-sort-alpha-up:before{content:"\f15e"}.fa-sort-amount-down:before{content:"\f160"}.fa-sort-amount-up:before{content:"\f161"}.fa-sort-down:before{content:"\f0dd"}.fa-sort-numeric-down:before{content:"\f162"}.fa-sort-numeric-up:before{content:"\f163"}.fa-sort-up:before{content:"\f0de"}.fa-soundcloud:before{content:"\f1be"}.fa-spa:before{content:"\f5bb"}.fa-space-shuttle:before{content:"\f197"}.fa-speakap:before{content:"\f3f3"}.fa-spider:before{content:"\f717"}.fa-spinner:before{content:"\f110"}.fa-splotch:before{content:"\f5bc"}.fa-spotify:before{content:"\f1bc"}.fa-spray-can:before{content:"\f5bd"}.fa-square:before{content:"\f0c8"}.fa-square-full:before{content:"\f45c"}.fa-square-root-alt:before{content:"\f698"}.fa-squarespace:before{content:"\f5be"}.fa-stack-exchange:before{content:"\f18d"}.fa-stack-overflow:before{content:"\f16c"}.fa-stamp:before{content:"\f5bf"}.fa-star:before{content:"\f005"}.fa-star-and-crescent:before{content:"\f699"}.fa-star-half:before{content:"\f089"}.fa-star-half-alt:before{content:"\f5c0"}.fa-star-of-david:before{content:"\f69a"}.fa-star-of-life:before{content:"\f621"}.fa-staylinked:before{content:"\f3f5"}.fa-steam:before{content:"\f1b6"}.fa-steam-square:before{content:"\f1b7"}.fa-steam-symbol:before{content:"\f3f6"}.fa-step-backward:before{content:"\f048"}.fa-step-forward:before{content:"\f051"}.fa-stethoscope:before{content:"\f0f1"}.fa-sticker-mule:before{content:"\f3f7"}.fa-sticky-note:before{content:"\f249"}.fa-stop:before{content:"\f04d"}.fa-stop-circle:before{content:"\f28d"}.fa-stopwatch:before{content:"\f2f2"}.fa-store:before{content:"\f54e"}.fa-store-alt:before{content:"\f54f"}.fa-strava:before{content:"\f428"}.fa-stream:before{content:"\f550"}.fa-street-view:before{content:"\f21d"}.fa-strikethrough:before{content:"\f0cc"}.fa-stripe:before{content:"\f429"}.fa-stripe-s:before{content:"\f42a"}.fa-stroopwafel:before{content:"\f551"}.fa-studiovinari:before{content:"\f3f8"}.fa-stumbleupon:before{content:"\f1a4"}.fa-stumbleupon-circle:before{content:"\f1a3"}.fa-subscript:before{content:"\f12c"}.fa-subway:before{content:"\f239"}.fa-suitcase:before{content:"\f0f2"}.fa-suitcase-rolling:before{content:"\f5c1"}.fa-sun:before{content:"\f185"}.fa-superpowers:before{content:"\f2dd"}.fa-superscript:before{content:"\f12b"}.fa-supple:before{content:"\f3f9"}.fa-surprise:before{content:"\f5c2"}.fa-swatchbook:before{content:"\f5c3"}.fa-swimmer:before{content:"\f5c4"}.fa-swimming-pool:before{content:"\f5c5"}.fa-synagogue:before{content:"\f69b"}.fa-sync:before{content:"\f021"}.fa-sync-alt:before{content:"\f2f1"}.fa-syringe:before{content:"\f48e"}.fa-table:before{content:"\f0ce"}.fa-table-tennis:before{content:"\f45d"}.fa-tablet:before{content:"\f10a"}.fa-tablet-alt:before{content:"\f3fa"}.fa-tablets:before{content:"\f490"}.fa-tachometer-alt:before{content:"\f3fd"}.fa-tag:before{content:"\f02b"}.fa-tags:before{content:"\f02c"}.fa-tape:before{content:"\f4db"}.fa-tasks:before{content:"\f0ae"}.fa-taxi:before{content:"\f1ba"}.fa-teamspeak:before{content:"\f4f9"}.fa-teeth:before{content:"\f62e"}.fa-teeth-open:before{content:"\f62f"}.fa-telegram:before{content:"\f2c6"}.fa-telegram-plane:before{content:"\f3fe"}.fa-temperature-high:before{content:"\f769"}.fa-temperature-low:before{content:"\f76b"}.fa-tencent-weibo:before{content:"\f1d5"}.fa-terminal:before{content:"\f120"}.fa-text-height:before{content:"\f034"}.fa-text-width:before{content:"\f035"}.fa-th:before{content:"\f00a"}.fa-th-large:before{content:"\f009"}.fa-th-list:before{content:"\f00b"}.fa-the-red-yeti:before{content:"\f69d"}.fa-theater-masks:before{content:"\f630"}.fa-themeco:before{content:"\f5c6"}.fa-themeisle:before{content:"\f2b2"}.fa-thermometer:before{content:"\f491"}.fa-thermometer-empty:before{content:"\f2cb"}.fa-thermometer-full:before{content:"\f2c7"}.fa-thermometer-half:before{content:"\f2c9"}.fa-thermometer-quarter:before{content:"\f2ca"}.fa-thermometer-three-quarters:before{content:"\f2c8"}.fa-think-peaks:before{content:"\f731"}.fa-thumbs-down:before{content:"\f165"}.fa-thumbs-up:before{content:"\f164"}.fa-thumbtack:before{content:"\f08d"}.fa-ticket-alt:before{content:"\f3ff"}.fa-times:before{content:"\f00d"}.fa-times-circle:before{content:"\f057"}.fa-tint:before{content:"\f043"}.fa-tint-slash:before{content:"\f5c7"}.fa-tired:before{content:"\f5c8"}.fa-toggle-off:before{content:"\f204"}.fa-toggle-on:before{content:"\f205"}.fa-toilet-paper:before{content:"\f71e"}.fa-toolbox:before{content:"\f552"}.fa-tooth:before{content:"\f5c9"}.fa-torah:before{content:"\f6a0"}.fa-torii-gate:before{content:"\f6a1"}.fa-tractor:before{content:"\f722"}.fa-trade-federation:before{content:"\f513"}.fa-trademark:before{content:"\f25c"}.fa-traffic-light:before{content:"\f637"}.fa-train:before{content:"\f238"}.fa-transgender:before{content:"\f224"}.fa-transgender-alt:before{content:"\f225"}.fa-trash:before{content:"\f1f8"}.fa-trash-alt:before{content:"\f2ed"}.fa-tree:before{content:"\f1bb"}.fa-trello:before{content:"\f181"}.fa-tripadvisor:before{content:"\f262"}.fa-trophy:before{content:"\f091"}.fa-truck:before{content:"\f0d1"}.fa-truck-loading:before{content:"\f4de"}.fa-truck-monster:before{content:"\f63b"}.fa-truck-moving:before{content:"\f4df"}.fa-truck-pickup:before{content:"\f63c"}.fa-tshirt:before{content:"\f553"}.fa-tty:before{content:"\f1e4"}.fa-tumblr:before{content:"\f173"}.fa-tumblr-square:before{content:"\f174"}.fa-tv:before{content:"\f26c"}.fa-twitch:before{content:"\f1e8"}.fa-twitter:before{content:"\f099"}.fa-twitter-square:before{content:"\f081"}.fa-typo3:before{content:"\f42b"}.fa-uber:before{content:"\f402"}.fa-uikit:before{content:"\f403"}.fa-umbrella:before{content:"\f0e9"}.fa-umbrella-beach:before{content:"\f5ca"}.fa-underline:before{content:"\f0cd"}.fa-undo:before{content:"\f0e2"}.fa-undo-alt:before{content:"\f2ea"}.fa-uniregistry:before{content:"\f404"}.fa-universal-access:before{content:"\f29a"}.fa-university:before{content:"\f19c"}.fa-unlink:before{content:"\f127"}.fa-unlock:before{content:"\f09c"}.fa-unlock-alt:before{content:"\f13e"}.fa-untappd:before{content:"\f405"}.fa-upload:before{content:"\f093"}.fa-usb:before{content:"\f287"}.fa-user:before{content:"\f007"}.fa-user-alt:before{content:"\f406"}.fa-user-alt-slash:before{content:"\f4fa"}.fa-user-astronaut:before{content:"\f4fb"}.fa-user-check:before{content:"\f4fc"}.fa-user-circle:before{content:"\f2bd"}.fa-user-clock:before{content:"\f4fd"}.fa-user-cog:before{content:"\f4fe"}.fa-user-edit:before{content:"\f4ff"}.fa-user-friends:before{content:"\f500"}.fa-user-graduate:before{content:"\f501"}.fa-user-injured:before{content:"\f728"}.fa-user-lock:before{content:"\f502"}.fa-user-md:before{content:"\f0f0"}.fa-user-minus:before{content:"\f503"}.fa-user-ninja:before{content:"\f504"}.fa-user-plus:before{content:"\f234"}.fa-user-secret:before{content:"\f21b"}.fa-user-shield:before{content:"\f505"}.fa-user-slash:before{content:"\f506"}.fa-user-tag:before{content:"\f507"}.fa-user-tie:before{content:"\f508"}.fa-user-times:before{content:"\f235"}.fa-users:before{content:"\f0c0"}.fa-users-cog:before{content:"\f509"}.fa-ussunnah:before{content:"\f407"}.fa-utensil-spoon:before{content:"\f2e5"}.fa-utensils:before{content:"\f2e7"}.fa-vaadin:before{content:"\f408"}.fa-vector-square:before{content:"\f5cb"}.fa-venus:before{content:"\f221"}.fa-venus-double:before{content:"\f226"}.fa-venus-mars:before{content:"\f228"}.fa-viacoin:before{content:"\f237"}.fa-viadeo:before{content:"\f2a9"}.fa-viadeo-square:before{content:"\f2aa"}.fa-vial:before{content:"\f492"}.fa-vials:before{content:"\f493"}.fa-viber:before{content:"\f409"}.fa-video:before{content:"\f03d"}.fa-video-slash:before{content:"\f4e2"}.fa-vihara:before{content:"\f6a7"}.fa-vimeo:before{content:"\f40a"}.fa-vimeo-square:before{content:"\f194"}.fa-vimeo-v:before{content:"\f27d"}.fa-vine:before{content:"\f1ca"}.fa-vk:before{content:"\f189"}.fa-vnv:before{content:"\f40b"}.fa-volleyball-ball:before{content:"\f45f"}.fa-volume-down:before{content:"\f027"}.fa-volume-mute:before{content:"\f6a9"}.fa-volume-off:before{content:"\f026"}.fa-volume-up:before{content:"\f028"}.fa-vote-yea:before{content:"\f772"}.fa-vr-cardboard:before{content:"\f729"}.fa-vuejs:before{content:"\f41f"}.fa-walking:before{content:"\f554"}.fa-wallet:before{content:"\f555"}.fa-warehouse:before{content:"\f494"}.fa-water:before{content:"\f773"}.fa-weebly:before{content:"\f5cc"}.fa-weibo:before{content:"\f18a"}.fa-weight:before{content:"\f496"}.fa-weight-hanging:before{content:"\f5cd"}.fa-weixin:before{content:"\f1d7"}.fa-whatsapp:before{content:"\f232"}.fa-whatsapp-square:before{content:"\f40c"}.fa-wheelchair:before{content:"\f193"}.fa-whmcs:before{content:"\f40d"}.fa-wifi:before{content:"\f1eb"}.fa-wikipedia-w:before{content:"\f266"}.fa-wind:before{content:"\f72e"}.fa-window-close:before{content:"\f410"}.fa-window-maximize:before{content:"\f2d0"}.fa-window-minimize:before{content:"\f2d1"}.fa-window-restore:before{content:"\f2d2"}.fa-windows:before{content:"\f17a"}.fa-wine-bottle:before{content:"\f72f"}.fa-wine-glass:before{content:"\f4e3"}.fa-wine-glass-alt:before{content:"\f5ce"}.fa-wix:before{content:"\f5cf"}.fa-wizards-of-the-coast:before{content:"\f730"}.fa-wolf-pack-battalion:before{content:"\f514"}.fa-won-sign:before{content:"\f159"}.fa-wordpress:before{content:"\f19a"}.fa-wordpress-simple:before{content:"\f411"}.fa-wpbeginner:before{content:"\f297"}.fa-wpexplorer:before{content:"\f2de"}.fa-wpforms:before{content:"\f298"}.fa-wpressr:before{content:"\f3e4"}.fa-wrench:before{content:"\f0ad"}.fa-x-ray:before{content:"\f497"}.fa-xbox:before{content:"\f412"}.fa-xing:before{content:"\f168"}.fa-xing-square:before{content:"\f169"}.fa-y-combinator:before{content:"\f23b"}.fa-yahoo:before{content:"\f19e"}.fa-yandex:before{content:"\f413"}.fa-yandex-international:before{content:"\f414"}.fa-yelp:before{content:"\f1e9"}.fa-yen-sign:before{content:"\f157"}.fa-yin-yang:before{content:"\f6ad"}.fa-yoast:before{content:"\f2b1"}.fa-youtube:before{content:"\f167"}.fa-youtube-square:before{content:"\f431"}.fa-zhihu:before{content:"\f63f"}.sr-only{border:0;clip:rect(0,0,0,0);height:1px;margin:-1px;overflow:hidden;padding:0;position:absolute;width:1px}.sr-only-focusable:active,.sr-only-focusable:focus{clip:auto;height:auto;margin:0;overflow:visible;position:static;width:auto}@font-face{font-family:"Font Awesome 5 Brands";font-style:normal;font-weight:normal;src:url(../fonts/fa/fa-brands-400.eot);src:url(../fonts/fa/fa-brands-400.eot?#iefix) format("embedded-opentype"),url(../fonts/fa/fa-brands-400.woff2) format("woff2"),url(../fonts/fa/fa-brands-400.woff) format("woff"),url(../fonts/fa/fa-brands-400.ttf) format("truetype"),url(../fonts/fa/fa-brands-400.svg#fontawesome) format("svg")}.fab{font-family:"Font Awesome 5 Brands"}@font-face{font-family:"Font Awesome 5 Free";font-style:normal;font-weight:400;src:url(../fonts/fa/fa-regular-400.eot);src:url(../fonts/fa/fa-regular-400.eot?#iefix) format("embedded-opentype"),url(../fonts/fa/fa-regular-400.woff2) format("woff2"),url(../fonts/fa/fa-regular-400.woff) format("woff"),url(../fonts/fa/fa-regular-400.ttf) format("truetype"),url(../fonts/fa/fa-regular-400.svg#fontawesome) format("svg")}.far{font-weight:400}@font-face{font-family:"Font Awesome 5 Free";font-style:normal;font-weight:900;src:url(../fonts/fa/fa-solid-900.eot);src:url(../fonts/fa/fa-solid-900.eot?#iefix) format("embedded-opentype"),url(../fonts/fa/fa-solid-900.woff2) format("woff2"),url(../fonts/fa/fa-solid-900.woff) format("woff"),url(../fonts/fa/fa-solid-900.ttf) format("truetype"),url(../fonts/fa/fa-solid-900.svg#fontawesome) format("svg")}.fa,.far,.fas{font-family:"Font Awesome 5 Free"}.fa,.fas{font-weight:900}
\ No newline at end of file
diff --git a/css/index.css b/css/index.css
index 6bb8215..69c654c 100644
--- a/css/index.css
+++ b/css/index.css
@@ -1,30 +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;
- }
+/*
+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;
+ }
}
\ No newline at end of file
diff --git a/css/indivisa.css b/css/indivisa.css
index 59b9aa0..7d3a22d 100644
--- a/css/indivisa.css
+++ b/css/indivisa.css
@@ -1,492 +1,492 @@
-/*
- 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";
+/*
+ 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";
}
\ No newline at end of file
diff --git a/css/jquery-ui.css b/css/jquery-ui.css
index 776e259..9aa45f9 100644
--- a/css/jquery-ui.css
+++ b/css/jquery-ui.css
@@ -1,7 +1,7 @@
-/*! jQuery UI - v1.12.1 - 2016-09-14
-* http://jqueryui.com
-* Includes: core.css, accordion.css, autocomplete.css, menu.css, button.css, controlgroup.css, checkboxradio.css, datepicker.css, dialog.css, draggable.css, resizable.css, progressbar.css, selectable.css, selectmenu.css, slider.css, sortable.css, spinner.css, tabs.css, tooltip.css, theme.css
-* To view and modify this theme, visit http://jqueryui.com/themeroller/?bgShadowXPos=&bgOverlayXPos=&bgErrorXPos=&bgHighlightXPos=&bgContentXPos=&bgHeaderXPos=&bgActiveXPos=&bgHoverXPos=&bgDefaultXPos=&bgShadowYPos=&bgOverlayYPos=&bgErrorYPos=&bgHighlightYPos=&bgContentYPos=&bgHeaderYPos=&bgActiveYPos=&bgHoverYPos=&bgDefaultYPos=&bgShadowRepeat=&bgOverlayRepeat=&bgErrorRepeat=&bgHighlightRepeat=&bgContentRepeat=&bgHeaderRepeat=&bgActiveRepeat=&bgHoverRepeat=&bgDefaultRepeat=&iconsHover=url(%22images%2Fui-icons_555555_256x240.png%22)&iconsHighlight=url(%22images%2Fui-icons_777620_256x240.png%22)&iconsHeader=url(%22images%2Fui-icons_444444_256x240.png%22)&iconsError=url(%22images%2Fui-icons_cc0000_256x240.png%22)&iconsDefault=url(%22images%2Fui-icons_777777_256x240.png%22)&iconsContent=url(%22images%2Fui-icons_444444_256x240.png%22)&iconsActive=url(%22images%2Fui-icons_ffffff_256x240.png%22)&bgImgUrlShadow=&bgImgUrlOverlay=&bgImgUrlHover=&bgImgUrlHighlight=&bgImgUrlHeader=&bgImgUrlError=&bgImgUrlDefault=&bgImgUrlContent=&bgImgUrlActive=&opacityFilterShadow=Alpha(Opacity%3D30)&opacityFilterOverlay=Alpha(Opacity%3D30)&opacityShadowPerc=30&opacityOverlayPerc=30&iconColorHover=%23555555&iconColorHighlight=%23777620&iconColorHeader=%23444444&iconColorError=%23cc0000&iconColorDefault=%23777777&iconColorContent=%23444444&iconColorActive=%23ffffff&bgImgOpacityShadow=0&bgImgOpacityOverlay=0&bgImgOpacityError=95&bgImgOpacityHighlight=55&bgImgOpacityContent=75&bgImgOpacityHeader=75&bgImgOpacityActive=65&bgImgOpacityHover=75&bgImgOpacityDefault=75&bgTextureShadow=flat&bgTextureOverlay=flat&bgTextureError=flat&bgTextureHighlight=flat&bgTextureContent=flat&bgTextureHeader=flat&bgTextureActive=flat&bgTextureHover=flat&bgTextureDefault=flat&cornerRadius=3px&fwDefault=normal&ffDefault=Arial%2CHelvetica%2Csans-serif&fsDefault=1em&cornerRadiusShadow=8px&thicknessShadow=5px&offsetLeftShadow=0px&offsetTopShadow=0px&opacityShadow=.3&bgColorShadow=%23666666&opacityOverlay=.3&bgColorOverlay=%23aaaaaa&fcError=%235f3f3f&borderColorError=%23f1a899&bgColorError=%23fddfdf&fcHighlight=%23777620&borderColorHighlight=%23dad55e&bgColorHighlight=%23fffa90&fcContent=%23333333&borderColorContent=%23dddddd&bgColorContent=%23ffffff&fcHeader=%23333333&borderColorHeader=%23dddddd&bgColorHeader=%23e9e9e9&fcActive=%23ffffff&borderColorActive=%23003eff&bgColorActive=%23007fff&fcHover=%232b2b2b&borderColorHover=%23cccccc&bgColorHover=%23ededed&fcDefault=%23454545&borderColorDefault=%23c5c5c5&bgColorDefault=%23f6f6f6
-* Copyright jQuery Foundation and other contributors; Licensed MIT */
-
+/*! jQuery UI - v1.12.1 - 2016-09-14
+* http://jqueryui.com
+* Includes: core.css, accordion.css, autocomplete.css, menu.css, button.css, controlgroup.css, checkboxradio.css, datepicker.css, dialog.css, draggable.css, resizable.css, progressbar.css, selectable.css, selectmenu.css, slider.css, sortable.css, spinner.css, tabs.css, tooltip.css, theme.css
+* To view and modify this theme, visit http://jqueryui.com/themeroller/?bgShadowXPos=&bgOverlayXPos=&bgErrorXPos=&bgHighlightXPos=&bgContentXPos=&bgHeaderXPos=&bgActiveXPos=&bgHoverXPos=&bgDefaultXPos=&bgShadowYPos=&bgOverlayYPos=&bgErrorYPos=&bgHighlightYPos=&bgContentYPos=&bgHeaderYPos=&bgActiveYPos=&bgHoverYPos=&bgDefaultYPos=&bgShadowRepeat=&bgOverlayRepeat=&bgErrorRepeat=&bgHighlightRepeat=&bgContentRepeat=&bgHeaderRepeat=&bgActiveRepeat=&bgHoverRepeat=&bgDefaultRepeat=&iconsHover=url(%22images%2Fui-icons_555555_256x240.png%22)&iconsHighlight=url(%22images%2Fui-icons_777620_256x240.png%22)&iconsHeader=url(%22images%2Fui-icons_444444_256x240.png%22)&iconsError=url(%22images%2Fui-icons_cc0000_256x240.png%22)&iconsDefault=url(%22images%2Fui-icons_777777_256x240.png%22)&iconsContent=url(%22images%2Fui-icons_444444_256x240.png%22)&iconsActive=url(%22images%2Fui-icons_ffffff_256x240.png%22)&bgImgUrlShadow=&bgImgUrlOverlay=&bgImgUrlHover=&bgImgUrlHighlight=&bgImgUrlHeader=&bgImgUrlError=&bgImgUrlDefault=&bgImgUrlContent=&bgImgUrlActive=&opacityFilterShadow=Alpha(Opacity%3D30)&opacityFilterOverlay=Alpha(Opacity%3D30)&opacityShadowPerc=30&opacityOverlayPerc=30&iconColorHover=%23555555&iconColorHighlight=%23777620&iconColorHeader=%23444444&iconColorError=%23cc0000&iconColorDefault=%23777777&iconColorContent=%23444444&iconColorActive=%23ffffff&bgImgOpacityShadow=0&bgImgOpacityOverlay=0&bgImgOpacityError=95&bgImgOpacityHighlight=55&bgImgOpacityContent=75&bgImgOpacityHeader=75&bgImgOpacityActive=65&bgImgOpacityHover=75&bgImgOpacityDefault=75&bgTextureShadow=flat&bgTextureOverlay=flat&bgTextureError=flat&bgTextureHighlight=flat&bgTextureContent=flat&bgTextureHeader=flat&bgTextureActive=flat&bgTextureHover=flat&bgTextureDefault=flat&cornerRadius=3px&fwDefault=normal&ffDefault=Arial%2CHelvetica%2Csans-serif&fsDefault=1em&cornerRadiusShadow=8px&thicknessShadow=5px&offsetLeftShadow=0px&offsetTopShadow=0px&opacityShadow=.3&bgColorShadow=%23666666&opacityOverlay=.3&bgColorOverlay=%23aaaaaa&fcError=%235f3f3f&borderColorError=%23f1a899&bgColorError=%23fddfdf&fcHighlight=%23777620&borderColorHighlight=%23dad55e&bgColorHighlight=%23fffa90&fcContent=%23333333&borderColorContent=%23dddddd&bgColorContent=%23ffffff&fcHeader=%23333333&borderColorHeader=%23dddddd&bgColorHeader=%23e9e9e9&fcActive=%23ffffff&borderColorActive=%23003eff&bgColorActive=%23007fff&fcHover=%232b2b2b&borderColorHover=%23cccccc&bgColorHover=%23ededed&fcDefault=%23454545&borderColorDefault=%23c5c5c5&bgColorDefault=%23f6f6f6
+* Copyright jQuery Foundation and other contributors; Licensed MIT */
+
.ui-helper-hidden{display:none}.ui-helper-hidden-accessible{border:0;clip:rect(0 0 0 0);height:1px;margin:-1px;overflow:hidden;padding:0;position:absolute;width:1px}.ui-helper-reset{margin:0;padding:0;border:0;outline:0;line-height:1.3;text-decoration:none;font-size:100%;list-style:none}.ui-helper-clearfix:before,.ui-helper-clearfix:after{content:"";display:table;border-collapse:collapse}.ui-helper-clearfix:after{clear:both}.ui-helper-zfix{width:100%;height:100%;top:0;left:0;position:absolute;opacity:0;filter:Alpha(Opacity=0)}.ui-front{z-index:100}.ui-state-disabled{cursor:default!important;pointer-events:none}.ui-icon{display:inline-block;vertical-align:middle;margin-top:-.25em;position:relative;text-indent:-99999px;overflow:hidden;background-repeat:no-repeat}.ui-widget-icon-block{left:50%;margin-left:-8px;display:block}.ui-widget-overlay{position:fixed;top:0;left:0;width:100%;height:100%}.ui-accordion .ui-accordion-header{display:block;cursor:pointer;position:relative;margin:2px 0 0 0;padding:.5em .5em .5em .7em;font-size:100%}.ui-accordion .ui-accordion-content{padding:1em 2.2em;border-top:0;overflow:auto}.ui-autocomplete{position:absolute;top:0;left:0;cursor:default}.ui-menu{list-style:none;padding:0;margin:0;display:block;outline:0}.ui-menu .ui-menu{position:absolute}.ui-menu .ui-menu-item{margin:0;cursor:pointer;list-style-image:url("data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7")}.ui-menu .ui-menu-item-wrapper{position:relative;padding:3px 1em 3px .4em}.ui-menu .ui-menu-divider{margin:5px 0;height:0;font-size:0;line-height:0;border-width:1px 0 0 0}.ui-menu .ui-state-focus,.ui-menu .ui-state-active{margin:-1px}.ui-menu-icons{position:relative}.ui-menu-icons .ui-menu-item-wrapper{padding-left:2em}.ui-menu .ui-icon{position:absolute;top:0;bottom:0;left:.2em;margin:auto 0}.ui-menu .ui-menu-icon{left:auto;right:0}.ui-button{padding:.4em 1em;display:inline-block;position:relative;line-height:normal;margin-right:.1em;cursor:pointer;vertical-align:middle;text-align:center;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;overflow:visible}.ui-button,.ui-button:link,.ui-button:visited,.ui-button:hover,.ui-button:active{text-decoration:none}.ui-button-icon-only{width:2em;box-sizing:border-box;text-indent:-9999px;white-space:nowrap}input.ui-button.ui-button-icon-only{text-indent:0}.ui-button-icon-only .ui-icon{position:absolute;top:50%;left:50%;margin-top:-8px;margin-left:-8px}.ui-button.ui-icon-notext .ui-icon{padding:0;width:2.1em;height:2.1em;text-indent:-9999px;white-space:nowrap}input.ui-button.ui-icon-notext .ui-icon{width:auto;height:auto;text-indent:0;white-space:normal;padding:.4em 1em}input.ui-button::-moz-focus-inner,button.ui-button::-moz-focus-inner{border:0;padding:0}.ui-controlgroup{vertical-align:middle;display:inline-block}.ui-controlgroup > .ui-controlgroup-item{float:left;margin-left:0;margin-right:0}.ui-controlgroup > .ui-controlgroup-item:focus,.ui-controlgroup > .ui-controlgroup-item.ui-visual-focus{z-index:9999}.ui-controlgroup-vertical > .ui-controlgroup-item{display:block;float:none;width:100%;margin-top:0;margin-bottom:0;text-align:left}.ui-controlgroup-vertical .ui-controlgroup-item{box-sizing:border-box}.ui-controlgroup .ui-controlgroup-label{padding:.4em 1em}.ui-controlgroup .ui-controlgroup-label span{font-size:80%}.ui-controlgroup-horizontal .ui-controlgroup-label + .ui-controlgroup-item{border-left:none}.ui-controlgroup-vertical .ui-controlgroup-label + .ui-controlgroup-item{border-top:none}.ui-controlgroup-horizontal .ui-controlgroup-label.ui-widget-content{border-right:none}.ui-controlgroup-vertical .ui-controlgroup-label.ui-widget-content{border-bottom:none}.ui-controlgroup-vertical .ui-spinner-input{width:75%;width:calc( 100% - 2.4em )}.ui-controlgroup-vertical .ui-spinner .ui-spinner-up{border-top-style:solid}.ui-checkboxradio-label .ui-icon-background{box-shadow:inset 1px 1px 1px #ccc;border-radius:.12em;border:none}.ui-checkboxradio-radio-label .ui-icon-background{width:16px;height:16px;border-radius:1em;overflow:visible;border:none}.ui-checkboxradio-radio-label.ui-checkboxradio-checked .ui-icon,.ui-checkboxradio-radio-label.ui-checkboxradio-checked:hover .ui-icon{background-image:none;width:8px;height:8px;border-width:4px;border-style:solid}.ui-checkboxradio-disabled{pointer-events:none}.ui-datepicker{width:17em;padding:.2em .2em 0;display:none}.ui-datepicker .ui-datepicker-header{position:relative;padding:.2em 0}.ui-datepicker .ui-datepicker-prev,.ui-datepicker .ui-datepicker-next{position:absolute;top:2px;width:1.8em;height:1.8em}.ui-datepicker .ui-datepicker-prev-hover,.ui-datepicker .ui-datepicker-next-hover{top:1px}.ui-datepicker .ui-datepicker-prev{left:2px}.ui-datepicker .ui-datepicker-next{right:2px}.ui-datepicker .ui-datepicker-prev-hover{left:1px}.ui-datepicker .ui-datepicker-next-hover{right:1px}.ui-datepicker .ui-datepicker-prev span,.ui-datepicker .ui-datepicker-next span{display:block;position:absolute;left:50%;margin-left:-8px;top:50%;margin-top:-8px}.ui-datepicker .ui-datepicker-title{margin:0 2.3em;line-height:1.8em;text-align:center}.ui-datepicker .ui-datepicker-title select{font-size:1em;margin:1px 0}.ui-datepicker select.ui-datepicker-month,.ui-datepicker select.ui-datepicker-year{width:45%}.ui-datepicker table{width:100%;font-size:.9em;border-collapse:collapse;margin:0 0 .4em}.ui-datepicker th{padding:.7em .3em;text-align:center;font-weight:bold;border:0}.ui-datepicker td{border:0;padding:1px}.ui-datepicker td span,.ui-datepicker td a{display:block;padding:.2em;text-align:right;text-decoration:none}.ui-datepicker .ui-datepicker-buttonpane{background-image:none;margin:.7em 0 0 0;padding:0 .2em;border-left:0;border-right:0;border-bottom:0}.ui-datepicker .ui-datepicker-buttonpane button{float:right;margin:.5em .2em .4em;cursor:pointer;padding:.2em .6em .3em .6em;width:auto;overflow:visible}.ui-datepicker .ui-datepicker-buttonpane button.ui-datepicker-current{float:left}.ui-datepicker.ui-datepicker-multi{width:auto}.ui-datepicker-multi .ui-datepicker-group{float:left}.ui-datepicker-multi .ui-datepicker-group table{width:95%;margin:0 auto .4em}.ui-datepicker-multi-2 .ui-datepicker-group{width:50%}.ui-datepicker-multi-3 .ui-datepicker-group{width:33.3%}.ui-datepicker-multi-4 .ui-datepicker-group{width:25%}.ui-datepicker-multi .ui-datepicker-group-last .ui-datepicker-header,.ui-datepicker-multi .ui-datepicker-group-middle .ui-datepicker-header{border-left-width:0}.ui-datepicker-multi .ui-datepicker-buttonpane{clear:left}.ui-datepicker-row-break{clear:both;width:100%;font-size:0}.ui-datepicker-rtl{direction:rtl}.ui-datepicker-rtl .ui-datepicker-prev{right:2px;left:auto}.ui-datepicker-rtl .ui-datepicker-next{left:2px;right:auto}.ui-datepicker-rtl .ui-datepicker-prev:hover{right:1px;left:auto}.ui-datepicker-rtl .ui-datepicker-next:hover{left:1px;right:auto}.ui-datepicker-rtl .ui-datepicker-buttonpane{clear:right}.ui-datepicker-rtl .ui-datepicker-buttonpane button{float:left}.ui-datepicker-rtl .ui-datepicker-buttonpane button.ui-datepicker-current,.ui-datepicker-rtl .ui-datepicker-group{float:right}.ui-datepicker-rtl .ui-datepicker-group-last .ui-datepicker-header,.ui-datepicker-rtl .ui-datepicker-group-middle .ui-datepicker-header{border-right-width:0;border-left-width:1px}.ui-datepicker .ui-icon{display:block;text-indent:-99999px;overflow:hidden;background-repeat:no-repeat;left:.5em;top:.3em}.ui-dialog{position:absolute;top:0;left:0;padding:.2em;outline:0}.ui-dialog .ui-dialog-titlebar{padding:.4em 1em;position:relative}.ui-dialog .ui-dialog-title{float:left;margin:.1em 0;white-space:nowrap;width:90%;overflow:hidden;text-overflow:ellipsis}.ui-dialog .ui-dialog-titlebar-close{position:absolute;right:.3em;top:50%;width:20px;margin:-10px 0 0 0;padding:1px;height:20px}.ui-dialog .ui-dialog-content{position:relative;border:0;padding:.5em 1em;background:none;overflow:auto}.ui-dialog .ui-dialog-buttonpane{text-align:left;border-width:1px 0 0 0;background-image:none;margin-top:.5em;padding:.3em 1em .5em .4em}.ui-dialog .ui-dialog-buttonpane .ui-dialog-buttonset{float:right}.ui-dialog .ui-dialog-buttonpane button{margin:.5em .4em .5em 0;cursor:pointer}.ui-dialog .ui-resizable-n{height:2px;top:0}.ui-dialog .ui-resizable-e{width:2px;right:0}.ui-dialog .ui-resizable-s{height:2px;bottom:0}.ui-dialog .ui-resizable-w{width:2px;left:0}.ui-dialog .ui-resizable-se,.ui-dialog .ui-resizable-sw,.ui-dialog .ui-resizable-ne,.ui-dialog .ui-resizable-nw{width:7px;height:7px}.ui-dialog .ui-resizable-se{right:0;bottom:0}.ui-dialog .ui-resizable-sw{left:0;bottom:0}.ui-dialog .ui-resizable-ne{right:0;top:0}.ui-dialog .ui-resizable-nw{left:0;top:0}.ui-draggable .ui-dialog-titlebar{cursor:move}.ui-draggable-handle{-ms-touch-action:none;touch-action:none}.ui-resizable{position:relative}.ui-resizable-handle{position:absolute;font-size:0.1px;display:block;-ms-touch-action:none;touch-action:none}.ui-resizable-disabled .ui-resizable-handle,.ui-resizable-autohide .ui-resizable-handle{display:none}.ui-resizable-n{cursor:n-resize;height:7px;width:100%;top:-5px;left:0}.ui-resizable-s{cursor:s-resize;height:7px;width:100%;bottom:-5px;left:0}.ui-resizable-e{cursor:e-resize;width:7px;right:-5px;top:0;height:100%}.ui-resizable-w{cursor:w-resize;width:7px;left:-5px;top:0;height:100%}.ui-resizable-se{cursor:se-resize;width:12px;height:12px;right:1px;bottom:1px}.ui-resizable-sw{cursor:sw-resize;width:9px;height:9px;left:-5px;bottom:-5px}.ui-resizable-nw{cursor:nw-resize;width:9px;height:9px;left:-5px;top:-5px}.ui-resizable-ne{cursor:ne-resize;width:9px;height:9px;right:-5px;top:-5px}.ui-progressbar{height:2em;text-align:left;overflow:hidden}.ui-progressbar .ui-progressbar-value{margin:-1px;height:100%}.ui-progressbar .ui-progressbar-overlay{background:url("data:image/gif;base64,R0lGODlhKAAoAIABAAAAAP///yH/C05FVFNDQVBFMi4wAwEAAAAh+QQJAQABACwAAAAAKAAoAAACkYwNqXrdC52DS06a7MFZI+4FHBCKoDeWKXqymPqGqxvJrXZbMx7Ttc+w9XgU2FB3lOyQRWET2IFGiU9m1frDVpxZZc6bfHwv4c1YXP6k1Vdy292Fb6UkuvFtXpvWSzA+HycXJHUXiGYIiMg2R6W459gnWGfHNdjIqDWVqemH2ekpObkpOlppWUqZiqr6edqqWQAAIfkECQEAAQAsAAAAACgAKAAAApSMgZnGfaqcg1E2uuzDmmHUBR8Qil95hiPKqWn3aqtLsS18y7G1SzNeowWBENtQd+T1JktP05nzPTdJZlR6vUxNWWjV+vUWhWNkWFwxl9VpZRedYcflIOLafaa28XdsH/ynlcc1uPVDZxQIR0K25+cICCmoqCe5mGhZOfeYSUh5yJcJyrkZWWpaR8doJ2o4NYq62lAAACH5BAkBAAEALAAAAAAoACgAAAKVDI4Yy22ZnINRNqosw0Bv7i1gyHUkFj7oSaWlu3ovC8GxNso5fluz3qLVhBVeT/Lz7ZTHyxL5dDalQWPVOsQWtRnuwXaFTj9jVVh8pma9JjZ4zYSj5ZOyma7uuolffh+IR5aW97cHuBUXKGKXlKjn+DiHWMcYJah4N0lYCMlJOXipGRr5qdgoSTrqWSq6WFl2ypoaUAAAIfkECQEAAQAsAAAAACgAKAAAApaEb6HLgd/iO7FNWtcFWe+ufODGjRfoiJ2akShbueb0wtI50zm02pbvwfWEMWBQ1zKGlLIhskiEPm9R6vRXxV4ZzWT2yHOGpWMyorblKlNp8HmHEb/lCXjcW7bmtXP8Xt229OVWR1fod2eWqNfHuMjXCPkIGNileOiImVmCOEmoSfn3yXlJWmoHGhqp6ilYuWYpmTqKUgAAIfkECQEAAQAsAAAAACgAKAAAApiEH6kb58biQ3FNWtMFWW3eNVcojuFGfqnZqSebuS06w5V80/X02pKe8zFwP6EFWOT1lDFk8rGERh1TTNOocQ61Hm4Xm2VexUHpzjymViHrFbiELsefVrn6XKfnt2Q9G/+Xdie499XHd2g4h7ioOGhXGJboGAnXSBnoBwKYyfioubZJ2Hn0RuRZaflZOil56Zp6iioKSXpUAAAh+QQJAQABACwAAAAAKAAoAAACkoQRqRvnxuI7kU1a1UU5bd5tnSeOZXhmn5lWK3qNTWvRdQxP8qvaC+/yaYQzXO7BMvaUEmJRd3TsiMAgswmNYrSgZdYrTX6tSHGZO73ezuAw2uxuQ+BbeZfMxsexY35+/Qe4J1inV0g4x3WHuMhIl2jXOKT2Q+VU5fgoSUI52VfZyfkJGkha6jmY+aaYdirq+lQAACH5BAkBAAEALAAAAAAoACgAAAKWBIKpYe0L3YNKToqswUlvznigd4wiR4KhZrKt9Upqip61i9E3vMvxRdHlbEFiEXfk9YARYxOZZD6VQ2pUunBmtRXo1Lf8hMVVcNl8JafV38aM2/Fu5V16Bn63r6xt97j09+MXSFi4BniGFae3hzbH9+hYBzkpuUh5aZmHuanZOZgIuvbGiNeomCnaxxap2upaCZsq+1kAACH5BAkBAAEALAAAAAAoACgAAAKXjI8By5zf4kOxTVrXNVlv1X0d8IGZGKLnNpYtm8Lr9cqVeuOSvfOW79D9aDHizNhDJidFZhNydEahOaDH6nomtJjp1tutKoNWkvA6JqfRVLHU/QUfau9l2x7G54d1fl995xcIGAdXqMfBNadoYrhH+Mg2KBlpVpbluCiXmMnZ2Sh4GBqJ+ckIOqqJ6LmKSllZmsoq6wpQAAAh+QQJAQABACwAAAAAKAAoAAAClYx/oLvoxuJDkU1a1YUZbJ59nSd2ZXhWqbRa2/gF8Gu2DY3iqs7yrq+xBYEkYvFSM8aSSObE+ZgRl1BHFZNr7pRCavZ5BW2142hY3AN/zWtsmf12p9XxxFl2lpLn1rseztfXZjdIWIf2s5dItwjYKBgo9yg5pHgzJXTEeGlZuenpyPmpGQoKOWkYmSpaSnqKileI2FAAACH5BAkBAAEALAAAAAAoACgAAAKVjB+gu+jG4kORTVrVhRlsnn2dJ3ZleFaptFrb+CXmO9OozeL5VfP99HvAWhpiUdcwkpBH3825AwYdU8xTqlLGhtCosArKMpvfa1mMRae9VvWZfeB2XfPkeLmm18lUcBj+p5dnN8jXZ3YIGEhYuOUn45aoCDkp16hl5IjYJvjWKcnoGQpqyPlpOhr3aElaqrq56Bq7VAAAOw==");height:100%;filter:alpha(opacity=25);opacity:0.25}.ui-progressbar-indeterminate .ui-progressbar-value{background-image:none}.ui-selectable{-ms-touch-action:none;touch-action:none}.ui-selectable-helper{position:absolute;z-index:100;border:1px dotted black}.ui-selectmenu-menu{padding:0;margin:0;position:absolute;top:0;left:0;display:none}.ui-selectmenu-menu .ui-menu{overflow:auto;overflow-x:hidden;padding-bottom:1px}.ui-selectmenu-menu .ui-menu .ui-selectmenu-optgroup{font-size:1em;font-weight:bold;line-height:1.5;padding:2px 0.4em;margin:0.5em 0 0 0;height:auto;border:0}.ui-selectmenu-open{display:block}.ui-selectmenu-text{display:block;margin-right:20px;overflow:hidden;text-overflow:ellipsis}.ui-selectmenu-button.ui-button{text-align:left;white-space:nowrap;width:14em}.ui-selectmenu-icon.ui-icon{float:right;margin-top:0}.ui-slider{position:relative;text-align:left}.ui-slider .ui-slider-handle{position:absolute;z-index:2;width:1.2em;height:1.2em;cursor:default;-ms-touch-action:none;touch-action:none}.ui-slider .ui-slider-range{position:absolute;z-index:1;font-size:.7em;display:block;border:0;background-position:0 0}.ui-slider.ui-state-disabled .ui-slider-handle,.ui-slider.ui-state-disabled .ui-slider-range{filter:inherit}.ui-slider-horizontal{height:.8em}.ui-slider-horizontal .ui-slider-handle{top:-.3em;margin-left:-.6em}.ui-slider-horizontal .ui-slider-range{top:0;height:100%}.ui-slider-horizontal .ui-slider-range-min{left:0}.ui-slider-horizontal .ui-slider-range-max{right:0}.ui-slider-vertical{width:.8em;height:100px}.ui-slider-vertical .ui-slider-handle{left:-.3em;margin-left:0;margin-bottom:-.6em}.ui-slider-vertical .ui-slider-range{left:0;width:100%}.ui-slider-vertical .ui-slider-range-min{bottom:0}.ui-slider-vertical .ui-slider-range-max{top:0}.ui-sortable-handle{-ms-touch-action:none;touch-action:none}.ui-spinner{position:relative;display:inline-block;overflow:hidden;padding:0;vertical-align:middle}.ui-spinner-input{border:none;background:none;color:inherit;padding:.222em 0;margin:.2em 0;vertical-align:middle;margin-left:.4em;margin-right:2em}.ui-spinner-button{width:1.6em;height:50%;font-size:.5em;padding:0;margin:0;text-align:center;position:absolute;cursor:default;display:block;overflow:hidden;right:0}.ui-spinner a.ui-spinner-button{border-top-style:none;border-bottom-style:none;border-right-style:none}.ui-spinner-up{top:0}.ui-spinner-down{bottom:0}.ui-tabs{position:relative;padding:.2em}.ui-tabs .ui-tabs-nav{margin:0;padding:.2em .2em 0}.ui-tabs .ui-tabs-nav li{list-style:none;float:left;position:relative;top:0;margin:1px .2em 0 0;border-bottom-width:0;padding:0;white-space:nowrap}.ui-tabs .ui-tabs-nav .ui-tabs-anchor{float:left;padding:.5em 1em;text-decoration:none}.ui-tabs .ui-tabs-nav li.ui-tabs-active{margin-bottom:-1px;padding-bottom:1px}.ui-tabs .ui-tabs-nav li.ui-tabs-active .ui-tabs-anchor,.ui-tabs .ui-tabs-nav li.ui-state-disabled .ui-tabs-anchor,.ui-tabs .ui-tabs-nav li.ui-tabs-loading .ui-tabs-anchor{cursor:text}.ui-tabs-collapsible .ui-tabs-nav li.ui-tabs-active .ui-tabs-anchor{cursor:pointer}.ui-tabs .ui-tabs-panel{display:block;border-width:0;padding:1em 1.4em;background:none}.ui-tooltip{padding:8px;position:absolute;z-index:9999;max-width:300px}body .ui-tooltip{border-width:2px}.ui-widget{font-family:Arial,Helvetica,sans-serif;font-size:1em}.ui-widget .ui-widget{font-size:1em}.ui-widget input,.ui-widget select,.ui-widget textarea,.ui-widget button{font-family:Arial,Helvetica,sans-serif;font-size:1em}.ui-widget.ui-widget-content{border:1px solid #c5c5c5}.ui-widget-content{border:1px solid #ddd;background:#fff;color:#333}.ui-widget-content a{color:#333}.ui-widget-header{border:1px solid #ddd;background:#e9e9e9;color:#333;font-weight:bold}.ui-widget-header a{color:#333}.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:#f6f6f6;font-weight:normal;color:#454545}.ui-state-default a,.ui-state-default a:link,.ui-state-default a:visited,a.ui-button,a:link.ui-button,a:visited.ui-button,.ui-button{color:#454545;text-decoration:none}.ui-state-hover,.ui-widget-content .ui-state-hover,.ui-widget-header .ui-state-hover,.ui-state-focus,.ui-widget-content .ui-state-focus,.ui-widget-header .ui-state-focus,.ui-button:hover,.ui-button:focus{border:1px solid #ccc;background:#ededed;font-weight:normal;color:#2b2b2b}.ui-state-hover a,.ui-state-hover a:hover,.ui-state-hover a:link,.ui-state-hover a:visited,.ui-state-focus a,.ui-state-focus a:hover,.ui-state-focus a:link,.ui-state-focus a:visited,a.ui-button:hover,a.ui-button:focus{color:#2b2b2b;text-decoration:none}.ui-visual-focus{box-shadow:0 0 3px 1px rgb(94,158,214)}.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 #003eff;background:#007fff;font-weight:normal;color:#fff}.ui-icon-background,.ui-state-active .ui-icon-background{border:#003eff;background-color:#fff}.ui-state-active a,.ui-state-active a:link,.ui-state-active a:visited{color:#fff;text-decoration:none}.ui-state-highlight,.ui-widget-content .ui-state-highlight,.ui-widget-header .ui-state-highlight{border:1px solid #dad55e;background:#fffa90;color:#777620}.ui-state-checked{border:1px solid #dad55e;background:#fffa90}.ui-state-highlight a,.ui-widget-content .ui-state-highlight a,.ui-widget-header .ui-state-highlight a{color:#777620}.ui-state-error,.ui-widget-content .ui-state-error,.ui-widget-header .ui-state-error{border:1px solid #f1a899;background:#fddfdf;color:#5f3f3f}.ui-state-error a,.ui-widget-content .ui-state-error a,.ui-widget-header .ui-state-error a{color:#5f3f3f}.ui-state-error-text,.ui-widget-content .ui-state-error-text,.ui-widget-header .ui-state-error-text{color:#5f3f3f}.ui-priority-primary,.ui-widget-content .ui-priority-primary,.ui-widget-header .ui-priority-primary{font-weight:bold}.ui-priority-secondary,.ui-widget-content .ui-priority-secondary,.ui-widget-header .ui-priority-secondary{opacity:.7;filter:Alpha(Opacity=70);font-weight:normal}.ui-state-disabled,.ui-widget-content .ui-state-disabled,.ui-widget-header .ui-state-disabled{opacity:.35;filter:Alpha(Opacity=35);background-image:none}.ui-state-disabled .ui-icon{filter:Alpha(Opacity=35)}.ui-icon{width:16px;height:16px}.ui-icon,.ui-widget-content .ui-icon{background-image:url("images/ui-icons_444444_256x240.png")}.ui-widget-header .ui-icon{background-image:url("images/ui-icons_444444_256x240.png")}.ui-state-hover .ui-icon,.ui-state-focus .ui-icon,.ui-button:hover .ui-icon,.ui-button:focus .ui-icon{background-image:url("images/ui-icons_555555_256x240.png")}.ui-state-active .ui-icon,.ui-button:active .ui-icon{background-image:url("images/ui-icons_ffffff_256x240.png")}.ui-state-highlight .ui-icon,.ui-button .ui-state-highlight.ui-icon{background-image:url("images/ui-icons_777620_256x240.png")}.ui-state-error .ui-icon,.ui-state-error-text .ui-icon{background-image:url("images/ui-icons_cc0000_256x240.png")}.ui-button .ui-icon{background-image:url("images/ui-icons_777777_256x240.png")}.ui-icon-blank{background-position:16px 16px}.ui-icon-caret-1-n{background-position:0 0}.ui-icon-caret-1-ne{background-position:-16px 0}.ui-icon-caret-1-e{background-position:-32px 0}.ui-icon-caret-1-se{background-position:-48px 0}.ui-icon-caret-1-s{background-position:-65px 0}.ui-icon-caret-1-sw{background-position:-80px 0}.ui-icon-caret-1-w{background-position:-96px 0}.ui-icon-caret-1-nw{background-position:-112px 0}.ui-icon-caret-2-n-s{background-position:-128px 0}.ui-icon-caret-2-e-w{background-position:-144px 0}.ui-icon-triangle-1-n{background-position:0 -16px}.ui-icon-triangle-1-ne{background-position:-16px -16px}.ui-icon-triangle-1-e{background-position:-32px -16px}.ui-icon-triangle-1-se{background-position:-48px -16px}.ui-icon-triangle-1-s{background-position:-65px -16px}.ui-icon-triangle-1-sw{background-position:-80px -16px}.ui-icon-triangle-1-w{background-position:-96px -16px}.ui-icon-triangle-1-nw{background-position:-112px -16px}.ui-icon-triangle-2-n-s{background-position:-128px -16px}.ui-icon-triangle-2-e-w{background-position:-144px -16px}.ui-icon-arrow-1-n{background-position:0 -32px}.ui-icon-arrow-1-ne{background-position:-16px -32px}.ui-icon-arrow-1-e{background-position:-32px -32px}.ui-icon-arrow-1-se{background-position:-48px -32px}.ui-icon-arrow-1-s{background-position:-65px -32px}.ui-icon-arrow-1-sw{background-position:-80px -32px}.ui-icon-arrow-1-w{background-position:-96px -32px}.ui-icon-arrow-1-nw{background-position:-112px -32px}.ui-icon-arrow-2-n-s{background-position:-128px -32px}.ui-icon-arrow-2-ne-sw{background-position:-144px -32px}.ui-icon-arrow-2-e-w{background-position:-160px -32px}.ui-icon-arrow-2-se-nw{background-position:-176px -32px}.ui-icon-arrowstop-1-n{background-position:-192px -32px}.ui-icon-arrowstop-1-e{background-position:-208px -32px}.ui-icon-arrowstop-1-s{background-position:-224px -32px}.ui-icon-arrowstop-1-w{background-position:-240px -32px}.ui-icon-arrowthick-1-n{background-position:1px -48px}.ui-icon-arrowthick-1-ne{background-position:-16px -48px}.ui-icon-arrowthick-1-e{background-position:-32px -48px}.ui-icon-arrowthick-1-se{background-position:-48px -48px}.ui-icon-arrowthick-1-s{background-position:-64px -48px}.ui-icon-arrowthick-1-sw{background-position:-80px -48px}.ui-icon-arrowthick-1-w{background-position:-96px -48px}.ui-icon-arrowthick-1-nw{background-position:-112px -48px}.ui-icon-arrowthick-2-n-s{background-position:-128px -48px}.ui-icon-arrowthick-2-ne-sw{background-position:-144px -48px}.ui-icon-arrowthick-2-e-w{background-position:-160px -48px}.ui-icon-arrowthick-2-se-nw{background-position:-176px -48px}.ui-icon-arrowthickstop-1-n{background-position:-192px -48px}.ui-icon-arrowthickstop-1-e{background-position:-208px -48px}.ui-icon-arrowthickstop-1-s{background-position:-224px -48px}.ui-icon-arrowthickstop-1-w{background-position:-240px -48px}.ui-icon-arrowreturnthick-1-w{background-position:0 -64px}.ui-icon-arrowreturnthick-1-n{background-position:-16px -64px}.ui-icon-arrowreturnthick-1-e{background-position:-32px -64px}.ui-icon-arrowreturnthick-1-s{background-position:-48px -64px}.ui-icon-arrowreturn-1-w{background-position:-64px -64px}.ui-icon-arrowreturn-1-n{background-position:-80px -64px}.ui-icon-arrowreturn-1-e{background-position:-96px -64px}.ui-icon-arrowreturn-1-s{background-position:-112px -64px}.ui-icon-arrowrefresh-1-w{background-position:-128px -64px}.ui-icon-arrowrefresh-1-n{background-position:-144px -64px}.ui-icon-arrowrefresh-1-e{background-position:-160px -64px}.ui-icon-arrowrefresh-1-s{background-position:-176px -64px}.ui-icon-arrow-4{background-position:0 -80px}.ui-icon-arrow-4-diag{background-position:-16px -80px}.ui-icon-extlink{background-position:-32px -80px}.ui-icon-newwin{background-position:-48px -80px}.ui-icon-refresh{background-position:-64px -80px}.ui-icon-shuffle{background-position:-80px -80px}.ui-icon-transfer-e-w{background-position:-96px -80px}.ui-icon-transferthick-e-w{background-position:-112px -80px}.ui-icon-folder-collapsed{background-position:0 -96px}.ui-icon-folder-open{background-position:-16px -96px}.ui-icon-document{background-position:-32px -96px}.ui-icon-document-b{background-position:-48px -96px}.ui-icon-note{background-position:-64px -96px}.ui-icon-mail-closed{background-position:-80px -96px}.ui-icon-mail-open{background-position:-96px -96px}.ui-icon-suitcase{background-position:-112px -96px}.ui-icon-comment{background-position:-128px -96px}.ui-icon-person{background-position:-144px -96px}.ui-icon-print{background-position:-160px -96px}.ui-icon-trash{background-position:-176px -96px}.ui-icon-locked{background-position:-192px -96px}.ui-icon-unlocked{background-position:-208px -96px}.ui-icon-bookmark{background-position:-224px -96px}.ui-icon-tag{background-position:-240px -96px}.ui-icon-home{background-position:0 -112px}.ui-icon-flag{background-position:-16px -112px}.ui-icon-calendar{background-position:-32px -112px}.ui-icon-cart{background-position:-48px -112px}.ui-icon-pencil{background-position:-64px -112px}.ui-icon-clock{background-position:-80px -112px}.ui-icon-disk{background-position:-96px -112px}.ui-icon-calculator{background-position:-112px -112px}.ui-icon-zoomin{background-position:-128px -112px}.ui-icon-zoomout{background-position:-144px -112px}.ui-icon-search{background-position:-160px -112px}.ui-icon-wrench{background-position:-176px -112px}.ui-icon-gear{background-position:-192px -112px}.ui-icon-heart{background-position:-208px -112px}.ui-icon-star{background-position:-224px -112px}.ui-icon-link{background-position:-240px -112px}.ui-icon-cancel{background-position:0 -128px}.ui-icon-plus{background-position:-16px -128px}.ui-icon-plusthick{background-position:-32px -128px}.ui-icon-minus{background-position:-48px -128px}.ui-icon-minusthick{background-position:-64px -128px}.ui-icon-close{background-position:-80px -128px}.ui-icon-closethick{background-position:-96px -128px}.ui-icon-key{background-position:-112px -128px}.ui-icon-lightbulb{background-position:-128px -128px}.ui-icon-scissors{background-position:-144px -128px}.ui-icon-clipboard{background-position:-160px -128px}.ui-icon-copy{background-position:-176px -128px}.ui-icon-contact{background-position:-192px -128px}.ui-icon-image{background-position:-208px -128px}.ui-icon-video{background-position:-224px -128px}.ui-icon-script{background-position:-240px -128px}.ui-icon-alert{background-position:0 -144px}.ui-icon-info{background-position:-16px -144px}.ui-icon-notice{background-position:-32px -144px}.ui-icon-help{background-position:-48px -144px}.ui-icon-check{background-position:-64px -144px}.ui-icon-bullet{background-position:-80px -144px}.ui-icon-radio-on{background-position:-96px -144px}.ui-icon-radio-off{background-position:-112px -144px}.ui-icon-pin-w{background-position:-128px -144px}.ui-icon-pin-s{background-position:-144px -144px}.ui-icon-play{background-position:0 -160px}.ui-icon-pause{background-position:-16px -160px}.ui-icon-seek-next{background-position:-32px -160px}.ui-icon-seek-prev{background-position:-48px -160px}.ui-icon-seek-end{background-position:-64px -160px}.ui-icon-seek-start{background-position:-80px -160px}.ui-icon-seek-first{background-position:-80px -160px}.ui-icon-stop{background-position:-96px -160px}.ui-icon-eject{background-position:-112px -160px}.ui-icon-volume-off{background-position:-128px -160px}.ui-icon-volume-on{background-position:-144px -160px}.ui-icon-power{background-position:0 -176px}.ui-icon-signal-diag{background-position:-16px -176px}.ui-icon-signal{background-position:-32px -176px}.ui-icon-battery-0{background-position:-48px -176px}.ui-icon-battery-1{background-position:-64px -176px}.ui-icon-battery-2{background-position:-80px -176px}.ui-icon-battery-3{background-position:-96px -176px}.ui-icon-circle-plus{background-position:0 -192px}.ui-icon-circle-minus{background-position:-16px -192px}.ui-icon-circle-close{background-position:-32px -192px}.ui-icon-circle-triangle-e{background-position:-48px -192px}.ui-icon-circle-triangle-s{background-position:-64px -192px}.ui-icon-circle-triangle-w{background-position:-80px -192px}.ui-icon-circle-triangle-n{background-position:-96px -192px}.ui-icon-circle-arrow-e{background-position:-112px -192px}.ui-icon-circle-arrow-s{background-position:-128px -192px}.ui-icon-circle-arrow-w{background-position:-144px -192px}.ui-icon-circle-arrow-n{background-position:-160px -192px}.ui-icon-circle-zoomin{background-position:-176px -192px}.ui-icon-circle-zoomout{background-position:-192px -192px}.ui-icon-circle-check{background-position:-208px -192px}.ui-icon-circlesmall-plus{background-position:0 -208px}.ui-icon-circlesmall-minus{background-position:-16px -208px}.ui-icon-circlesmall-close{background-position:-32px -208px}.ui-icon-squaresmall-plus{background-position:-48px -208px}.ui-icon-squaresmall-minus{background-position:-64px -208px}.ui-icon-squaresmall-close{background-position:-80px -208px}.ui-icon-grip-dotted-vertical{background-position:0 -224px}.ui-icon-grip-dotted-horizontal{background-position:-16px -224px}.ui-icon-grip-solid-vertical{background-position:-32px -224px}.ui-icon-grip-solid-horizontal{background-position:-48px -224px}.ui-icon-gripsmall-diagonal-se{background-position:-64px -224px}.ui-icon-grip-diagonal-se{background-position:-80px -224px}.ui-corner-all,.ui-corner-top,.ui-corner-left,.ui-corner-tl{border-top-left-radius:3px}.ui-corner-all,.ui-corner-top,.ui-corner-right,.ui-corner-tr{border-top-right-radius:3px}.ui-corner-all,.ui-corner-bottom,.ui-corner-left,.ui-corner-bl{border-bottom-left-radius:3px}.ui-corner-all,.ui-corner-bottom,.ui-corner-right,.ui-corner-br{border-bottom-right-radius:3px}.ui-widget-overlay{background:#aaa;opacity:.003;filter:Alpha(Opacity=.3)}.ui-widget-shadow{-webkit-box-shadow:0 0 5px #666;box-shadow:0 0 5px #666}
\ No newline at end of file
diff --git a/css/lasalle.css b/css/lasalle.css
index d83ca77..d3095c8 100644
--- a/css/lasalle.css
+++ b/css/lasalle.css
@@ -1,105 +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";}
+/*
+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";}
\ No newline at end of file
diff --git a/css/sgi.css b/css/sgi.css
index 6e7f562..60c05ab 100644
--- a/css/sgi.css
+++ b/css/sgi.css
@@ -1,1085 +1,1085 @@
-/*
- 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;
- }*/
-}
-
-.movie {
- transition: all 0.1s;
- box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2);
- height: 8rem;
- /* align all inside content to the middle */
- display: flex;
- flex-direction: column;
- justify-content: center;
- align-items: center;
- background-color: #f7f7f7;
-}
-
-.movie:hover {
- transform: scale(1.05);
- box-shadow: 0 4px 8px rgba(0, 0, 0, 0.5);
- font-size: 1.1em;
-}
-
-.movie:active {
- transform: scale(1.1);
- box-shadow: 0 6px 12px rgba(0, 0, 0, 0.2);
- font-size: 1.2em;
- font-weight: bold;
-}
-
-.icono.ing-buscar {
- cursor: pointer;
+/*
+ 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;
+ }*/
+}
+
+.movie {
+ transition: all 0.1s;
+ box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2);
+ height: 8rem;
+ /* align all inside content to the middle */
+ display: flex;
+ flex-direction: column;
+ justify-content: center;
+ align-items: center;
+ background-color: #f7f7f7;
+}
+
+.movie:hover {
+ transform: scale(1.05);
+ box-shadow: 0 4px 8px rgba(0, 0, 0, 0.5);
+ font-size: 1.1em;
+}
+
+.movie:active {
+ transform: scale(1.1);
+ box-shadow: 0 6px 12px rgba(0, 0, 0, 0.2);
+ font-size: 1.2em;
+ font-weight: bold;
+}
+
+.icono.ing-buscar {
+ cursor: pointer;
}
\ No newline at end of file
diff --git a/css/style.css b/css/style.css
index 2e6b8d8..ce6f6f8 100644
--- a/css/style.css
+++ b/css/style.css
@@ -1,99 +1,99 @@
-.bg-azul {
- background-color: #00a6CE;
-}
-
-.azul {
- color: #00a6CE;
-}
-
-/* for sm */
-
-.custom-switch.custom-switch-sm .custom-control-label {
- padding-left: 1rem;
- padding-bottom: 1rem;
-}
-
-.custom-switch.custom-switch-sm .custom-control-label::before {
- height: 1rem;
- width: calc(1rem + 0.75rem);
- border-radius: 2rem;
-}
-
-.custom-switch.custom-switch-sm .custom-control-label::after {
- width: calc(1rem - 4px);
- height: calc(1rem - 4px);
- border-radius: calc(1rem - (1rem / 2));
-}
-
-.custom-switch.custom-switch-sm .custom-control-input:checked ~ .custom-control-label::after {
- transform: translateX(calc(1rem - 0.25rem));
-}
-
-/* for md */
-
-.custom-switch.custom-switch-md .custom-control-label {
- padding-left: 2rem;
- padding-bottom: 1.5rem;
-}
-
-.custom-switch.custom-switch-md .custom-control-label::before {
- height: 1.5rem;
- width: calc(2rem + 0.75rem);
- border-radius: 3rem;
-}
-
-.custom-switch.custom-switch-md .custom-control-label::after {
- width: calc(1.5rem - 4px);
- height: calc(1.5rem - 4px);
- border-radius: calc(2rem - (1.5rem / 2));
-}
-
-.custom-switch.custom-switch-md .custom-control-input:checked ~ .custom-control-label::after {
- transform: translateX(calc(1.5rem - 0.25rem));
-}
-
-/* for lg */
-
-.custom-switch.custom-switch-lg .custom-control-label {
- padding-left: 3rem;
- padding-bottom: 2rem;
-}
-
-.custom-switch.custom-switch-lg .custom-control-label::before {
- height: 2rem;
- width: calc(3rem + 0.75rem);
- border-radius: 4rem;
-}
-
-.custom-switch.custom-switch-lg .custom-control-label::after {
- width: calc(2rem - 4px);
- height: calc(2rem - 4px);
- border-radius: calc(3rem - (2rem / 2));
-}
-
-.custom-switch.custom-switch-lg .custom-control-input:checked ~ .custom-control-label::after {
- transform: translateX(calc(2rem - 0.25rem));
-}
-
-/* for xl */
-
-.custom-switch.custom-switch-xl .custom-control-label {
- padding-left: 4rem;
- padding-bottom: 2.5rem;
-}
-
-.custom-switch.custom-switch-xl .custom-control-label::before {
- height: 2.5rem;
- width: calc(4rem + 0.75rem);
- border-radius: 5rem;
-}
-
-.custom-switch.custom-switch-xl .custom-control-label::after {
- width: calc(2.5rem - 4px);
- height: calc(2.5rem - 4px);
- border-radius: calc(4rem - (2.5rem / 2));
-}
-
-.custom-switch.custom-switch-xl .custom-control-input:checked ~ .custom-control-label::after {
- transform: translateX(calc(2.5rem - 0.25rem));
+.bg-azul {
+ background-color: #00a6CE;
+}
+
+.azul {
+ color: #00a6CE;
+}
+
+/* for sm */
+
+.custom-switch.custom-switch-sm .custom-control-label {
+ padding-left: 1rem;
+ padding-bottom: 1rem;
+}
+
+.custom-switch.custom-switch-sm .custom-control-label::before {
+ height: 1rem;
+ width: calc(1rem + 0.75rem);
+ border-radius: 2rem;
+}
+
+.custom-switch.custom-switch-sm .custom-control-label::after {
+ width: calc(1rem - 4px);
+ height: calc(1rem - 4px);
+ border-radius: calc(1rem - (1rem / 2));
+}
+
+.custom-switch.custom-switch-sm .custom-control-input:checked ~ .custom-control-label::after {
+ transform: translateX(calc(1rem - 0.25rem));
+}
+
+/* for md */
+
+.custom-switch.custom-switch-md .custom-control-label {
+ padding-left: 2rem;
+ padding-bottom: 1.5rem;
+}
+
+.custom-switch.custom-switch-md .custom-control-label::before {
+ height: 1.5rem;
+ width: calc(2rem + 0.75rem);
+ border-radius: 3rem;
+}
+
+.custom-switch.custom-switch-md .custom-control-label::after {
+ width: calc(1.5rem - 4px);
+ height: calc(1.5rem - 4px);
+ border-radius: calc(2rem - (1.5rem / 2));
+}
+
+.custom-switch.custom-switch-md .custom-control-input:checked ~ .custom-control-label::after {
+ transform: translateX(calc(1.5rem - 0.25rem));
+}
+
+/* for lg */
+
+.custom-switch.custom-switch-lg .custom-control-label {
+ padding-left: 3rem;
+ padding-bottom: 2rem;
+}
+
+.custom-switch.custom-switch-lg .custom-control-label::before {
+ height: 2rem;
+ width: calc(3rem + 0.75rem);
+ border-radius: 4rem;
+}
+
+.custom-switch.custom-switch-lg .custom-control-label::after {
+ width: calc(2rem - 4px);
+ height: calc(2rem - 4px);
+ border-radius: calc(3rem - (2rem / 2));
+}
+
+.custom-switch.custom-switch-lg .custom-control-input:checked ~ .custom-control-label::after {
+ transform: translateX(calc(2rem - 0.25rem));
+}
+
+/* for xl */
+
+.custom-switch.custom-switch-xl .custom-control-label {
+ padding-left: 4rem;
+ padding-bottom: 2.5rem;
+}
+
+.custom-switch.custom-switch-xl .custom-control-label::before {
+ height: 2.5rem;
+ width: calc(4rem + 0.75rem);
+ border-radius: 5rem;
+}
+
+.custom-switch.custom-switch-xl .custom-control-label::after {
+ width: calc(2.5rem - 4px);
+ height: calc(2.5rem - 4px);
+ border-radius: calc(4rem - (2.5rem / 2));
+}
+
+.custom-switch.custom-switch-xl .custom-control-input:checked ~ .custom-control-label::after {
+ transform: translateX(calc(2.5rem - 0.25rem));
}
\ No newline at end of file
diff --git a/css/toggle.css b/css/toggle.css
index 4903b1e..86b914c 100644
--- a/css/toggle.css
+++ b/css/toggle.css
@@ -1,142 +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; }
+/*\
+|*| ========================================================================
+|*| 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; }
diff --git a/demo.html b/demo.html
index dcf5941..a10172a 100644
--- a/demo.html
+++ b/demo.html
@@ -1,237 +1,237 @@
-
-Iconos editado
-Iconos editados 2
-
- ing-fb1
-
-
- ing-fb2
-
-
- ing-tw1
-
-
- ing-tw2
-
-
- ing-in1
-
-
- ing-in2
-
-
- ing-instra1
-
-
- ing-instra2
-
-
- ing-youtube
-
-
- ing-telefono
-
-
- ing-mail
-
-
- ing-link
-
-
- ing-ubicacion
-
-
- ing-puntos
-
-
- ing-usuario
-
-
- ing-pass
-
-
- ing-menu
-
-
- ing-salir
-
-
- ing-flecha
-
-
- ing-cambiar
-
-
- ing-caret
-
-
- ing-aceptar
-
-
- ing-cancelar
-
-
- ing-mas
-
-
- ing-menos
-
-
- ing-editar
-
-
- ing-buscar
-
-
- ing-ojo
-
-
- ing-borrar
-
-
- ing-basura
-
-
- ing-camara
-
-
- ing-importante
-
-
- ing-bullet
-
-
- ing-home
-
-
- ing-formacion
-
-
- ing-empleo
-
-
- ing-insignia1
-
-
- ing-insignia2
-
-
- ing-insignia3
-
-
- ing-insignia4
-
-
- ing-eventos
-
-
- ing-reporte
-
-
- ing-catalogo
-
-
- ing-evalua-cartel
-
-
- ing-revision-cartel
-
-
- ing-reporte-resultados
-
-
- ing-mi-cartel
-
-
- ing-galeria1
-
-
- ing-galeria2
-
-
- ing-iniciar-sesion
-
-
- ing-finalistas
-
-
- ing-comite
-
-
- ing-administrador
-
-
- ing-estrella1
-
-
- ing-estrella2
-
-
- ing-carga-archivo
-
-
- ing-carga-multiple
-
-
- ing-descarga
-
-
- ing-autorizar
-
-
- ing-negar
-
-
- ing-no-cargado
-
-
- ing-alumnos
-
-
- ing-cardex
-
-
- ing-configuracion
-
-
- ing-listado-menus
-
-
- ing-mi-cuenta
-
-
- ing-ver
-
-
- ing-grafica
-
-
- ing-clic
-
-
- ing-guardar
-
-
- ing-regresar
-
-
- ing-cuadrado
-
-
- ing-imprimir
-
-
- ing-importante2
-
-
- ing-copiar
-
-
- ing-reloj
-
-
- ing-retardo
-
-
- ing-justificar
+
+
Iconos editado
+Iconos editados 2
+
+ ing-fb1
+
+
+ ing-fb2
+
+
+ ing-tw1
+
+
+ ing-tw2
+
+
+ ing-in1
+
+
+ ing-in2
+
+
+ ing-instra1
+
+
+ ing-instra2
+
+
+ ing-youtube
+
+
+ ing-telefono
+
+
+ ing-mail
+
+
+ ing-link
+
+
+ ing-ubicacion
+
+
+ ing-puntos
+
+
+ ing-usuario
+
+
+ ing-pass
+
+
+ ing-menu
+
+
+ ing-salir
+
+
+ ing-flecha
+
+
+ ing-cambiar
+
+
+ ing-caret
+
+
+ ing-aceptar
+
+
+ ing-cancelar
+
+
+ ing-mas
+
+
+ ing-menos
+
+
+ ing-editar
+
+
+ ing-buscar
+
+
+ ing-ojo
+
+
+ ing-borrar
+
+
+ ing-basura
+
+
+ ing-camara
+
+
+ ing-importante
+
+
+ ing-bullet
+
+
+ ing-home
+
+
+ ing-formacion
+
+
+ ing-empleo
+
+
+ ing-insignia1
+
+
+ ing-insignia2
+
+
+ ing-insignia3
+
+
+ ing-insignia4
+
+
+ ing-eventos
+
+
+ ing-reporte
+
+
+ ing-catalogo
+
+
+ ing-evalua-cartel
+
+
+ ing-revision-cartel
+
+
+ ing-reporte-resultados
+
+
+ ing-mi-cartel
+
+
+ ing-galeria1
+
+
+ ing-galeria2
+
+
+ ing-iniciar-sesion
+
+
+ ing-finalistas
+
+
+ ing-comite
+
+
+ ing-administrador
+
+
+ ing-estrella1
+
+
+ ing-estrella2
+
+
+ ing-carga-archivo
+
+
+ ing-carga-multiple
+
+
+ ing-descarga
+
+
+ ing-autorizar
+
+
+ ing-negar
+
+
+ ing-no-cargado
+
+
+ ing-alumnos
+
+
+ ing-cardex
+
+
+ ing-configuracion
+
+
+ ing-listado-menus
+
+
+ ing-mi-cuenta
+
+
+ ing-ver
+
+
+ ing-grafica
+
+
+ ing-clic
+
+
+ ing-guardar
+
+
+ ing-regresar
+
+
+ ing-cuadrado
+
+
+ ing-imprimir
+
+
+ ing-importante2
+
+
+ ing-copiar
+
+
+ ing-reloj
+
+
+ ing-retardo
+
+
+ ing-justificar
\ No newline at end of file
diff --git a/export/solicitudes_excel.php b/export/solicitudes_excel.php
index 18f07fe..43dc006 100644
--- a/export/solicitudes_excel.php
+++ b/export/solicitudes_excel.php
@@ -1,156 +1,156 @@
-query('SELECT * FROM fs_estado_reposicion' );
-
-$repoParams = array();
-
-$user = Login::get_user();
-$user->access();
-$query="";
-if($user->rol["rol_id"] == 9){//es coordinador
- $query .= ":facultad, ";
- $repoParams[":facultad"] = $user->facultad["facultad_id"];
-}else{//supervisor
- $query .= "NULL, ";
-}
-if(isset($_POST["prof"]) ){
- $query .= ":prof,";
- $profesor = trim($_POST["prof"]);//limpia texto
- $repoParams[":prof"] = $profesor;
-}else{
- $query .= "NULL,";
-}
-$query .= ":f_ini, :f_fin, ";
-$repoParams[":f_ini"] = DateTime::createFromFormat('d/m/Y', $fecha_ini)->format('Y-m-d');
-$repoParams[":f_fin"] = DateTime::createFromFormat('d/m/Y', $fecha_fin)->format('Y-m-d');
-
-$spreadsheet = new Spreadsheet();
-
-// Set document properties
-$spreadsheet->getProperties()->setCreator('Universidad La Salle')
- ->setLastModifiedBy('Universidad La Salle')
- ->setTitle('Solicitudes')
- ->setDescription('Reporte de solicitudes.');
-
-$headerStyle =
- [
- 'fill' => [
- 'fillType' => Fill::FILL_SOLID,
- 'color' => ['argb' => 'FF001d68'],
- ],
- 'borders' => [
- 'bottom' => ['borderStyle' => Border::BORDER_THIN],
- 'right' => ['borderStyle' => Border::BORDER_MEDIUM],
- ],
- 'font' => [
- 'bold' => true,
- 'color' => ['argb' => 'FFFFFFFF'],
- ]
- ];
-
-$row_base = 6;
-
-$i=0;
-foreach($repEdo_rs as $redo){
- $row = $row_base;
- if($i >= $spreadsheet->getSheetCount()){
- $spreadsheet->createSheet();
- }
- $spreadsheet->setActiveSheetIndex($i);
- //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());
-
- $spreadsheet->getActiveSheet()
- ->setCellValue('A'.$row, 'Estado')
- ->setCellValue('B'.$row, 'Tipo')
- ->setCellValue('C'.$row, 'Profesor')
- ->setCellValue('D'.$row, 'Materia')
- ->setCellValue('E'.$row, 'Grupo')
- ->setCellValue('F'.$row, 'Fecha falta')
- ->setCellValue('G'.$row, 'Fecha reposici贸n')
- ->setCellValue('H'.$row, 'Sal贸n');
-
- $spreadsheet->getActiveSheet()->getStyle('A'.$row.':H'.$row)->applyFromArray($headerStyle);
- $repoParams[":edo"]=$redo["estado_reposicion_id"];
- if($user->rol["rol_id"] == 7){//es supervisor
- $repoParams[":sup"] = $user->user["id"];
- $solicitudes_rs = $db->query('SELECT * FROM fs_solicitud(NULL, '.$query.':edo, NULL, :sup) ', $repoParams );
- }else{
- $solicitudes_rs = $db->query('SELECT * FROM fs_solicitud(NULL, '.$query.':edo, NULL, NULL) ', $repoParams );
- }
-
- $row++;
- $sheet = $spreadsheet->getActiveSheet();
- $sheet->setTitle($redo["estado_nombre"]);
- if(isset($solicitudes_rs) && count($solicitudes_rs)>0){
- foreach($solicitudes_rs as $reposicion){
-
-
- $sheet->setCellValue('A'.$row, $reposicion["estado_nombre"]);
- $sheet->setCellValue('B'.$row, $reposicion["solicitudtipo_nombre"]);
- $sheet->setCellValue('C'.$row, $reposicion["profesor_clave"]." - ".$reposicion["profesor_nombre"]);
- $sheet->setCellValue('D'.$row, $reposicion["materia_nombre"]);
- if($reposicion["horario_grupo"]!="")
- $sheet->setCellValue('E'.$row, $reposicion["horario_grupo"]);
-
- if($reposicion["fecha_clase"]!=""){
- $fechaI = DateTime::createFromFormat('Y-m-d', $reposicion["fecha_nueva"])->format('d/m/Y');
- $sheet->setCellValue('F'.$row, $fechaI." ".substr($reposicion["horario_hora"],0, 5));
- }
-
- $fechaF = date("d/m/Y", strtotime($reposicion["fecha_nueva"]));
- $sheet->setCellValue('G'.$row, $fechaF." ".substr($reposicion["hora_nueva"],0, 5)." a ".substr($reposicion["hora_nueva_fin"],0, 5));
-
- if($reposicion["salon_id"] != ""){
- $salon_json = json_decode($reposicion["salon_array"], true);
- $sheet->setCellValue('H'.$row, $salon_json[count($salon_json)-1]);
- }else
- $sheet->setCellValue('H'.$row, "Pendiente");
- $row++;
-
- }//foreach
-
- }//if
- foreach ($sheet->getColumnIterator() as $column) {
- $sheet->getColumnDimension($column->getColumnIndex())->setAutoSize(true);
- }
- $sheet->setAutoFilter('A'.$row_base.':H'.$row_base);
- $i++;
-}
-$spreadsheet->setActiveSheetIndex(0);
-
-
-
-$writer = IOFactory::createWriter($spreadsheet, 'Xlsx');
+query('SELECT * FROM fs_estado_reposicion' );
+
+$repoParams = array();
+
+$user = Login::get_user();
+$user->access();
+$query="";
+if($user->rol["rol_id"] == 9){//es coordinador
+ $query .= ":facultad, ";
+ $repoParams[":facultad"] = $user->facultad["facultad_id"];
+}else{//supervisor
+ $query .= "NULL, ";
+}
+if(isset($_POST["prof"]) ){
+ $query .= ":prof,";
+ $profesor = trim($_POST["prof"]);//limpia texto
+ $repoParams[":prof"] = $profesor;
+}else{
+ $query .= "NULL,";
+}
+$query .= ":f_ini, :f_fin, ";
+$repoParams[":f_ini"] = DateTime::createFromFormat('d/m/Y', $fecha_ini)->format('Y-m-d');
+$repoParams[":f_fin"] = DateTime::createFromFormat('d/m/Y', $fecha_fin)->format('Y-m-d');
+
+$spreadsheet = new Spreadsheet();
+
+// Set document properties
+$spreadsheet->getProperties()->setCreator('Universidad La Salle')
+ ->setLastModifiedBy('Universidad La Salle')
+ ->setTitle('Solicitudes')
+ ->setDescription('Reporte de solicitudes.');
+
+$headerStyle =
+ [
+ 'fill' => [
+ 'fillType' => Fill::FILL_SOLID,
+ 'color' => ['argb' => 'FF001d68'],
+ ],
+ 'borders' => [
+ 'bottom' => ['borderStyle' => Border::BORDER_THIN],
+ 'right' => ['borderStyle' => Border::BORDER_MEDIUM],
+ ],
+ 'font' => [
+ 'bold' => true,
+ 'color' => ['argb' => 'FFFFFFFF'],
+ ]
+ ];
+
+$row_base = 6;
+
+$i=0;
+foreach($repEdo_rs as $redo){
+ $row = $row_base;
+ if($i >= $spreadsheet->getSheetCount()){
+ $spreadsheet->createSheet();
+ }
+ $spreadsheet->setActiveSheetIndex($i);
+ //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());
+
+ $spreadsheet->getActiveSheet()
+ ->setCellValue('A'.$row, 'Estado')
+ ->setCellValue('B'.$row, 'Tipo')
+ ->setCellValue('C'.$row, 'Profesor')
+ ->setCellValue('D'.$row, 'Materia')
+ ->setCellValue('E'.$row, 'Grupo')
+ ->setCellValue('F'.$row, 'Fecha falta')
+ ->setCellValue('G'.$row, 'Fecha reposici贸n')
+ ->setCellValue('H'.$row, 'Sal贸n');
+
+ $spreadsheet->getActiveSheet()->getStyle('A'.$row.':H'.$row)->applyFromArray($headerStyle);
+ $repoParams[":edo"]=$redo["estado_reposicion_id"];
+ if($user->rol["rol_id"] == 7){//es supervisor
+ $repoParams[":sup"] = $user->user["id"];
+ $solicitudes_rs = $db->query('SELECT * FROM fs_solicitud(NULL, '.$query.':edo, NULL, :sup) ', $repoParams );
+ }else{
+ $solicitudes_rs = $db->query('SELECT * FROM fs_solicitud(NULL, '.$query.':edo, NULL, NULL) ', $repoParams );
+ }
+
+ $row++;
+ $sheet = $spreadsheet->getActiveSheet();
+ $sheet->setTitle($redo["estado_nombre"]);
+ if(isset($solicitudes_rs) && count($solicitudes_rs)>0){
+ foreach($solicitudes_rs as $reposicion){
+
+
+ $sheet->setCellValue('A'.$row, $reposicion["estado_nombre"]);
+ $sheet->setCellValue('B'.$row, $reposicion["solicitudtipo_nombre"]);
+ $sheet->setCellValue('C'.$row, $reposicion["profesor_clave"]." - ".$reposicion["profesor_nombre"]);
+ $sheet->setCellValue('D'.$row, $reposicion["materia_nombre"]);
+ if($reposicion["horario_grupo"]!="")
+ $sheet->setCellValue('E'.$row, $reposicion["horario_grupo"]);
+
+ if($reposicion["fecha_clase"]!=""){
+ $fechaI = DateTime::createFromFormat('Y-m-d', $reposicion["fecha_nueva"])->format('d/m/Y');
+ $sheet->setCellValue('F'.$row, $fechaI." ".substr($reposicion["horario_hora"],0, 5));
+ }
+
+ $fechaF = date("d/m/Y", strtotime($reposicion["fecha_nueva"]));
+ $sheet->setCellValue('G'.$row, $fechaF." ".substr($reposicion["hora_nueva"],0, 5)." a ".substr($reposicion["hora_nueva_fin"],0, 5));
+
+ if($reposicion["salon_id"] != ""){
+ $salon_json = json_decode($reposicion["salon_array"], true);
+ $sheet->setCellValue('H'.$row, $salon_json[count($salon_json)-1]);
+ }else
+ $sheet->setCellValue('H'.$row, "Pendiente");
+ $row++;
+
+ }//foreach
+
+ }//if
+ foreach ($sheet->getColumnIterator() as $column) {
+ $sheet->getColumnDimension($column->getColumnIndex())->setAutoSize(true);
+ }
+ $sheet->setAutoFilter('A'.$row_base.':H'.$row_base);
+ $i++;
+}
+$spreadsheet->setActiveSheetIndex(0);
+
+
+
+$writer = IOFactory::createWriter($spreadsheet, 'Xlsx');
$writer->save('php://output');
\ No newline at end of file
diff --git a/horarios_historicos.php b/horarios_historicos.php
index 6dad736..10dc2ec 100644
--- a/horarios_historicos.php
+++ b/horarios_historicos.php
@@ -1,208 +1,208 @@
-
-
-
-
-
-
-
- Hist贸rico de horarios
-
-
-
-
-
-
-
-
-
-
- $redirect = $_SERVER['PHP_SELF'];
- include "import/html_header.php";
- global $user;
-
- html_header(
- "Hist贸rico de horarios",
- "Sistema de gesti贸n de checador",
- );
-
-
-
- if (!$user->periodo_id) { ?>
-
-
-
-
-
-
-
-
- include 'import/periodo.php' ?>
-
-
-
-
-
- exit;
- } ?>
-
-
-
-
-
-
-
-
-
-
- Carrera
-
-
- Materia
-
-
- Grupo
-
-
- Horario
-
-
- Alta
-
-
- Baja
-
-
-
-
-
-
-
- {{horario.facultad_nombre}}
-
- {{horario.carrera_nombre}}
-
-
- {{horario.materia_nombre}}
-
-
- {{horario.horario_grupo}}
-
-
- {{d铆as[horario.horario_dia]}} - {{horario.horario_hora}} - {{horario.horario_fin}}
-
-
- {{horario.horario_fecha_inicio}}
-
-
- {{horario.horario_fecha_fin}}
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- {{mensaje.texto}}
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+ Hist贸rico de horarios
+
+
+
+
+
+
+
+
+
+
+ $redirect = $_SERVER['PHP_SELF'];
+ include "import/html_header.php";
+ global $user;
+
+ html_header(
+ "Hist贸rico de horarios",
+ "Sistema de gesti贸n de checador",
+ );
+
+
+
+ if (!$user->periodo_id) { ?>
+
+
+
+
+
+
+
+
+ include 'import/periodo.php' ?>
+
+
+
+
+
+ exit;
+ } ?>
+
+
+
+
+
+
+
+
+
+
+ Carrera
+
+
+ Materia
+
+
+ Grupo
+
+
+ Horario
+
+
+ Alta
+
+
+ Baja
+
+
+
+
+
+
+
+ {{horario.facultad_nombre}}
+
+ {{horario.carrera_nombre}}
+
+
+ {{horario.materia_nombre}}
+
+
+ {{horario.horario_grupo}}
+
+
+ {{d铆as[horario.horario_dia]}} - {{horario.horario_hora}} - {{horario.horario_fin}}
+
+
+ {{horario.horario_fecha_inicio}}
+
+
+ {{horario.horario_fecha_fin}}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{mensaje.texto}}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/include/constantes.php b/include/constantes.php
index 81a554e..d45147d 100644
--- a/include/constantes.php
+++ b/include/constantes.php
@@ -1,146 +1,146 @@
-' ',
- "menos"=>' ',
- "editar"=>' ',
- "borrar"=>' ',
- "borrar2"=>' ',
- "cancelar"=>' ',
- "buscar"=>' ',
- "descargar"=>' ',
- "cargar"=>' ',
- "ver"=>' ',
- "cambiar"=>' ',
- "circulo"=>' ',
- "aceptar"=>' ',
- "alerta"=>' ',
- "calendario"=>' ',
- "ojo"=>' ',
- "profesor"=>' ',
-
- "lista"=>' ',
- "lista_check"=>'',
- "abajo"=>' ',
- "arriba"=>' ',
- "izquierda"=>' ',
- "derecha"=>' ',
-];
-$ICO_LG =[
- "mas"=>' ',
- "menos"=>' ',
- "editar"=>' ',
- "borrar"=>' ',
- "borrar2"=>' ',
- "cancelar"=>' ',
- "buscar"=>' ',
- "descargar"=>' ',
- "cargar"=>' ',
- "ver"=>' ',
- "cambiar"=>' ',
- "circulo"=>' ',
- "aceptar"=>' ',
- "alerta"=>' ',
- "calendario"=>' ',
- "ojo"=>' ',
- "profesor"=>' ',
-
- "lista"=>' ',
- "lista_check"=>'',
- "abajo"=>' ',
- "arriba"=>' ',
-
- /*
- "insert"=>' ',
- "update"=>' ',
- "delete"=>' ',
- "search"=>' ',
- "insert_box"=>' ',
- "update_box"=>' ',
- "delete_box"=>' ',
- "error"=>' ',
- "error_circle"=>' ',
- "ok"=>' ',
- "ok_circle"=>' ',
- "list"=>' ',
- "circulo"=>' ',
- "question_circle"=>' ',
- "info_circle"=>' ',
- "square_check"=>' ',
- "square_empty"=>' ',
- "eraser"=>' ',*/
-];
-
-$ICO_RND =[
- "right"=>'
-
-
- ',
- "left"=>'
-
-
- ',
- "error"=>'
-
-
- ',
- "error_circle"=>'
-
-
- ',
- "ok_circle"=>'
-
-
- ',
- "ok"=>'
-
-
- ',
- "close"=>'
-
-
- ',
- "next"=>'
-
-
- ',
- "back"=>'
-
-
- ',
-];
-
-//Sistemas registrados
-define("APSA", 1);
-define("GEMA", 2);
-define("CIDIT", 3);
-define("CONSTANCIA", 5);
-define("EXPOING", 7);
-
-define("MAX_ROWS", 30);
-
-define("HORA_INICIO", 6);//hora inicial de horario
-define("HORA_FINAL", 22);//hora final de horario
-define("FRACCION_HORA", 4);//fracciones en una hora
-
-define("DURACION_MIN", 60);//hora inicial de horario
-define("DURACION_MAX", 360);//hora final de horario
-define("DURACION_STEP", 15);//fracciones en una hora
-
-define("FACULTAD", "Facultad de Ingenier铆a");
-//define("NOMBRE_DIRECTOR", "Ing. Edmundo G. Barrera Monsiv谩is");
-
-define("PE_INI", "PE_INI$");
-define("PE_INI_Y", "PE_INI_Y$");
-define("PE_FIN", "PE_FIN$");
-define("PE_FIN_Y", "PE_FIN_Y$");
-
-define("PR_INI", "PR_INI$");
-define("PR_INI_Y", "PR_INI_Y$");
-define("PR_FIN", "PR_FIN$");
-define("PR_FIN_Y", "PR_FIN_Y$");
-
-define("EX_INI", "EX_INI$");
-define("EX_INI_Y", "EX_INI_Y$");
-define("EX_FIN", "EX_FIN$");
-define("EX_FIN_Y", "EX_FIN_Y$");
+' ',
+ "menos"=>' ',
+ "editar"=>' ',
+ "borrar"=>' ',
+ "borrar2"=>' ',
+ "cancelar"=>' ',
+ "buscar"=>' ',
+ "descargar"=>' ',
+ "cargar"=>' ',
+ "ver"=>' ',
+ "cambiar"=>' ',
+ "circulo"=>' ',
+ "aceptar"=>' ',
+ "alerta"=>' ',
+ "calendario"=>' ',
+ "ojo"=>' ',
+ "profesor"=>' ',
+
+ "lista"=>' ',
+ "lista_check"=>'',
+ "abajo"=>' ',
+ "arriba"=>' ',
+ "izquierda"=>' ',
+ "derecha"=>' ',
+];
+$ICO_LG =[
+ "mas"=>' ',
+ "menos"=>' ',
+ "editar"=>' ',
+ "borrar"=>' ',
+ "borrar2"=>' ',
+ "cancelar"=>' ',
+ "buscar"=>' ',
+ "descargar"=>' ',
+ "cargar"=>' ',
+ "ver"=>' ',
+ "cambiar"=>' ',
+ "circulo"=>' ',
+ "aceptar"=>' ',
+ "alerta"=>' ',
+ "calendario"=>' ',
+ "ojo"=>' ',
+ "profesor"=>' ',
+
+ "lista"=>' ',
+ "lista_check"=>'',
+ "abajo"=>' ',
+ "arriba"=>' ',
+
+ /*
+ "insert"=>' ',
+ "update"=>' ',
+ "delete"=>' ',
+ "search"=>' ',
+ "insert_box"=>' ',
+ "update_box"=>' ',
+ "delete_box"=>' ',
+ "error"=>' ',
+ "error_circle"=>' ',
+ "ok"=>' ',
+ "ok_circle"=>' ',
+ "list"=>' ',
+ "circulo"=>' ',
+ "question_circle"=>' ',
+ "info_circle"=>' ',
+ "square_check"=>' ',
+ "square_empty"=>' ',
+ "eraser"=>' ',*/
+];
+
+$ICO_RND =[
+ "right"=>'
+
+
+ ',
+ "left"=>'
+
+
+ ',
+ "error"=>'
+
+
+ ',
+ "error_circle"=>'
+
+
+ ',
+ "ok_circle"=>'
+
+
+ ',
+ "ok"=>'
+
+
+ ',
+ "close"=>'
+
+
+ ',
+ "next"=>'
+
+
+ ',
+ "back"=>'
+
+
+ ',
+];
+
+//Sistemas registrados
+define("APSA", 1);
+define("GEMA", 2);
+define("CIDIT", 3);
+define("CONSTANCIA", 5);
+define("EXPOING", 7);
+
+define("MAX_ROWS", 30);
+
+define("HORA_INICIO", 6);//hora inicial de horario
+define("HORA_FINAL", 22);//hora final de horario
+define("FRACCION_HORA", 4);//fracciones en una hora
+
+define("DURACION_MIN", 60);//hora inicial de horario
+define("DURACION_MAX", 360);//hora final de horario
+define("DURACION_STEP", 15);//fracciones en una hora
+
+define("FACULTAD", "Facultad de Ingenier铆a");
+//define("NOMBRE_DIRECTOR", "Ing. Edmundo G. Barrera Monsiv谩is");
+
+define("PE_INI", "PE_INI$");
+define("PE_INI_Y", "PE_INI_Y$");
+define("PE_FIN", "PE_FIN$");
+define("PE_FIN_Y", "PE_FIN_Y$");
+
+define("PR_INI", "PR_INI$");
+define("PR_INI_Y", "PR_INI_Y$");
+define("PR_FIN", "PR_FIN$");
+define("PR_FIN_Y", "PR_FIN_Y$");
+
+define("EX_INI", "EX_INI$");
+define("EX_INI_Y", "EX_INI_Y$");
+define("EX_FIN", "EX_FIN$");
+define("EX_FIN_Y", "EX_FIN_Y$");
?>
\ No newline at end of file
diff --git a/include/db/postgrest.conf b/include/db/postgrest.conf
index 80e930d..f1a1392 100644
--- a/include/db/postgrest.conf
+++ b/include/db/postgrest.conf
@@ -1,20 +1,20 @@
-# postgrest.conf
-
-# The standard connection URI format, documented at
-# https://www.postgresql.org/docs/current/libpq-connect.html#LIBPQ-CONNSTRING
-db-uri = "postgres://postgres:4ud1t0rf4lt4$$@localhost:5432/paad_pruebas"
-
-# The database role to use when no client authentication is provided.
-# Should differ from authenticator
-db-anon-role = "postgres"
-
-# The secret to verify the JWT for authenticated requests with.
-# Needs to be 32 characters minimum.
-jwt-secret = "reallyreallyreallyreallyverysafe"
-jwt-secret-is-base64 = false
-
-# Port the postgrest process is listening on for http requests
-server-port = 3000
-
-# the location root is /api
+# postgrest.conf
+
+# The standard connection URI format, documented at
+# https://www.postgresql.org/docs/current/libpq-connect.html#LIBPQ-CONNSTRING
+db-uri = "postgres://postgres:4ud1t0rf4lt4$$@localhost:5432/paad_pruebas"
+
+# The database role to use when no client authentication is provided.
+# Should differ from authenticator
+db-anon-role = "postgres"
+
+# The secret to verify the JWT for authenticated requests with.
+# Needs to be 32 characters minimum.
+jwt-secret = "reallyreallyreallyreallyverysafe"
+jwt-secret-is-base64 = false
+
+# Port the postgrest process is listening on for http requests
+server-port = 3000
+
+# the location root is /api
server-host = "*"
\ No newline at end of file
diff --git a/include/fun_fecha.php b/include/fun_fecha.php
index baa85b3..cfcd33d 100644
--- a/include/fun_fecha.php
+++ b/include/fun_fecha.php
@@ -1,55 +1,55 @@
-"enero", "febrero", "marzo", "abril", "mayo", "junio", "julio", "agosto", "septiembre", "octubre", "noviembre", "diciembre");
- return $meses[intval($num)];
-}
-
-function diaNombre($num){
- $dias=array("domingo", "lunes", "martes", "mi茅rcoles", "jueves", "viernes", "s谩bado");
- return $dias[intval($num)];
-}
+"enero", "febrero", "marzo", "abril", "mayo", "junio", "julio", "agosto", "septiembre", "octubre", "noviembre", "diciembre");
+ return $meses[intval($num)];
+}
+
+function diaNombre($num){
+ $dias=array("domingo", "lunes", "martes", "mi茅rcoles", "jueves", "viernes", "s谩bado");
+ return $dias[intval($num)];
+}
diff --git a/include/phpmailer/PHPMailerAutoload.php b/include/phpmailer/PHPMailerAutoload.php
index 60f9ad7..8d3dcba 100644
--- a/include/phpmailer/PHPMailerAutoload.php
+++ b/include/phpmailer/PHPMailerAutoload.php
@@ -1,50 +1,50 @@
-
- * @author Jim Jagielski (jimjag)
- * @author Andy Prevost (codeworxtech)
- * @author Brent R. Matzelle (original founder)
- * @copyright 2012 - 2014 Marcus Bointon
- * @copyright 2010 - 2012 Jim Jagielski
- * @copyright 2004 - 2009 Andy Prevost
- * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
- * @note This program is distributed in the hope that it will be useful - WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE.
- */
-
-/**
- * PHPMailer SPL autoloader.
- * @param string $classname The name of the class to load
- */
-function PHPMailerAutoload($classname)
-{
- //Can't use __DIR__ as it's only in PHP 5.3+
- $filename = dirname(__FILE__).DIRECTORY_SEPARATOR.'class.'.strtolower($classname).'.php';
- if (is_readable($filename)) {
- require $filename;
- }
-}
-
-if (version_compare(PHP_VERSION, '5.1.2', '>=')) {
- //SPL autoloading was introduced in PHP 5.1.2
- if (version_compare(PHP_VERSION, '5.3.0', '>=')) {
- spl_autoload_register('PHPMailerAutoload', true, true);
- } else {
- spl_autoload_register('PHPMailerAutoload');
- }
-} else {
- /**
- * Fall back to traditional autoload for old PHP versions
- * @param string $classname The name of the class to load
- */
- spl_autoload_register($classname);
- /*function __autoload($classname)
- {
- PHPMailerAutoload($classname);
- }*/
-}
+
+ * @author Jim Jagielski (jimjag)
+ * @author Andy Prevost (codeworxtech)
+ * @author Brent R. Matzelle (original founder)
+ * @copyright 2012 - 2014 Marcus Bointon
+ * @copyright 2010 - 2012 Jim Jagielski
+ * @copyright 2004 - 2009 Andy Prevost
+ * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
+ * @note This program is distributed in the hope that it will be useful - WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
+/**
+ * PHPMailer SPL autoloader.
+ * @param string $classname The name of the class to load
+ */
+function PHPMailerAutoload($classname)
+{
+ //Can't use __DIR__ as it's only in PHP 5.3+
+ $filename = dirname(__FILE__).DIRECTORY_SEPARATOR.'class.'.strtolower($classname).'.php';
+ if (is_readable($filename)) {
+ require $filename;
+ }
+}
+
+if (version_compare(PHP_VERSION, '5.1.2', '>=')) {
+ //SPL autoloading was introduced in PHP 5.1.2
+ if (version_compare(PHP_VERSION, '5.3.0', '>=')) {
+ spl_autoload_register('PHPMailerAutoload', true, true);
+ } else {
+ spl_autoload_register('PHPMailerAutoload');
+ }
+} else {
+ /**
+ * Fall back to traditional autoload for old PHP versions
+ * @param string $classname The name of the class to load
+ */
+ spl_autoload_register($classname);
+ /*function __autoload($classname)
+ {
+ PHPMailerAutoload($classname);
+ }*/
+}
diff --git a/include/phpmailer/class.phpmailer.php b/include/phpmailer/class.phpmailer.php
index 3cda98d..d6daaed 100644
--- a/include/phpmailer/class.phpmailer.php
+++ b/include/phpmailer/class.phpmailer.php
@@ -1,3884 +1,3884 @@
-
- * @author Jim Jagielski (jimjag)
- * @author Andy Prevost (codeworxtech)
- * @author Brent R. Matzelle (original founder)
- * @copyright 2012 - 2014 Marcus Bointon
- * @copyright 2010 - 2012 Jim Jagielski
- * @copyright 2004 - 2009 Andy Prevost
- * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
- * @note This program is distributed in the hope that it will be useful - WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE.
- */
-
-/**
- * PHPMailer - PHP email creation and transport class.
- * @package PHPMailer
- * @author Marcus Bointon (Synchro/coolbru)
- * @author Jim Jagielski (jimjag)
- * @author Andy Prevost (codeworxtech)
- * @author Brent R. Matzelle (original founder)
- */
-class PHPMailer
-{
- /**
- * The PHPMailer Version number.
- * @var string
- */
- public $Version = '5.2.14';
-
- /**
- * Email priority.
- * Options: null (default), 1 = High, 3 = Normal, 5 = low.
- * When null, the header is not set at all.
- * @var integer
- */
- public $Priority = null;
-
- /**
- * The character set of the message.
- * @var string
- */
- public $CharSet = 'iso-8859-1';
-
- /**
- * The MIME Content-type of the message.
- * @var string
- */
- public $ContentType = 'text/plain';
-
- /**
- * The message encoding.
- * Options: "8bit", "7bit", "binary", "base64", and "quoted-printable".
- * @var string
- */
- public $Encoding = '8bit';
-
- /**
- * Holds the most recent mailer error message.
- * @var string
- */
- public $ErrorInfo = '';
-
- /**
- * The From email address for the message.
- * @var string
- */
- public $From = 'root@localhost';
-
- /**
- * The From name of the message.
- * @var string
- */
- public $FromName = 'Root User';
-
- /**
- * The Sender email (Return-Path) of the message.
- * If not empty, will be sent via -f to sendmail or as 'MAIL FROM' in smtp mode.
- * @var string
- */
- public $Sender = '';
-
- /**
- * The Return-Path of the message.
- * If empty, it will be set to either From or Sender.
- * @var string
- * @deprecated Email senders should never set a return-path header;
- * it's the receiver's job (RFC5321 section 4.4), so this no longer does anything.
- * @link https://tools.ietf.org/html/rfc5321#section-4.4 RFC5321 reference
- */
- public $ReturnPath = '';
-
- /**
- * The Subject of the message.
- * @var string
- */
- public $Subject = '';
-
- /**
- * An HTML or plain text message body.
- * If HTML then call isHTML(true).
- * @var string
- */
- public $Body = '';
-
- /**
- * The plain-text message body.
- * This body can be read by mail clients that do not have HTML email
- * capability such as mutt & Eudora.
- * Clients that can read HTML will view the normal Body.
- * @var string
- */
- public $AltBody = '';
-
- /**
- * An iCal message part body.
- * Only supported in simple alt or alt_inline message types
- * To generate iCal events, use the bundled extras/EasyPeasyICS.php class or iCalcreator
- * @link http://sprain.ch/blog/downloads/php-class-easypeasyics-create-ical-files-with-php/
- * @link http://kigkonsult.se/iCalcreator/
- * @var string
- */
- public $Ical = '';
-
- /**
- * The complete compiled MIME message body.
- * @access protected
- * @var string
- */
- protected $MIMEBody = '';
-
- /**
- * The complete compiled MIME message headers.
- * @var string
- * @access protected
- */
- protected $MIMEHeader = '';
-
- /**
- * Extra headers that createHeader() doesn't fold in.
- * @var string
- * @access protected
- */
- protected $mailHeader = '';
-
- /**
- * Word-wrap the message body to this number of chars.
- * Set to 0 to not wrap. A useful value here is 78, for RFC2822 section 2.1.1 compliance.
- * @var integer
- */
- public $WordWrap = 0;
-
- /**
- * Which method to use to send mail.
- * Options: "mail", "sendmail", or "smtp".
- * @var string
- */
- public $Mailer = 'mail';
-
- /**
- * The path to the sendmail program.
- * @var string
- */
- public $Sendmail = '/usr/sbin/sendmail';
-
- /**
- * Whether mail() uses a fully sendmail-compatible MTA.
- * One which supports sendmail's "-oi -f" options.
- * @var boolean
- */
- public $UseSendmailOptions = true;
-
- /**
- * Path to PHPMailer plugins.
- * Useful if the SMTP class is not in the PHP include path.
- * @var string
- * @deprecated Should not be needed now there is an autoloader.
- */
- public $PluginDir = '';
-
- /**
- * The email address that a reading confirmation should be sent to, also known as read receipt.
- * @var string
- */
- public $ConfirmReadingTo = '';
-
- /**
- * The hostname to use in the Message-ID header and as default HELO string.
- * If empty, PHPMailer attempts to find one with, in order,
- * $_SERVER['SERVER_NAME'], gethostname(), php_uname('n'), or the value
- * 'localhost.localdomain'.
- * @var string
- */
- public $Hostname = '';
-
- /**
- * An ID to be used in the Message-ID header.
- * If empty, a unique id will be generated.
- * @var string
- */
- public $MessageID = '';
-
- /**
- * The message Date to be used in the Date header.
- * If empty, the current date will be added.
- * @var string
- */
- public $MessageDate = '';
-
- /**
- * SMTP hosts.
- * Either a single hostname or multiple semicolon-delimited hostnames.
- * You can also specify a different port
- * for each host by using this format: [hostname:port]
- * (e.g. "smtp1.example.com:25;smtp2.example.com").
- * You can also specify encryption type, for example:
- * (e.g. "tls://smtp1.example.com:587;ssl://smtp2.example.com:465").
- * Hosts will be tried in order.
- * @var string
- */
- public $Host = 'localhost';
-
- /**
- * The default SMTP server port.
- * @var integer
- * @TODO Why is this needed when the SMTP class takes care of it?
- */
- public $Port = 25;
-
- /**
- * The SMTP HELO of the message.
- * Default is $Hostname. If $Hostname is empty, PHPMailer attempts to find
- * one with the same method described above for $Hostname.
- * @var string
- * @see PHPMailer::$Hostname
- */
- public $Helo = '';
-
- /**
- * What kind of encryption to use on the SMTP connection.
- * Options: '', 'ssl' or 'tls'
- * @var string
- */
- public $SMTPSecure = '';
-
- /**
- * Whether to enable TLS encryption automatically if a server supports it,
- * even if `SMTPSecure` is not set to 'tls'.
- * Be aware that in PHP >= 5.6 this requires that the server's certificates are valid.
- * @var boolean
- */
- public $SMTPAutoTLS = true;
-
- /**
- * Whether to use SMTP authentication.
- * Uses the Username and Password properties.
- * @var boolean
- * @see PHPMailer::$Username
- * @see PHPMailer::$Password
- */
- public $SMTPAuth = false;
-
- /**
- * Options array passed to stream_context_create when connecting via SMTP.
- * @var array
- */
- public $SMTPOptions = array();
-
- /**
- * SMTP username.
- * @var string
- */
- public $Username = '';
-
- /**
- * SMTP password.
- * @var string
- */
- public $Password = '';
-
- /**
- * SMTP auth type.
- * Options are LOGIN (default), PLAIN, NTLM, CRAM-MD5
- * @var string
- */
- public $AuthType = '';
-
- /**
- * SMTP realm.
- * Used for NTLM auth
- * @var string
- */
- public $Realm = '';
-
- /**
- * SMTP workstation.
- * Used for NTLM auth
- * @var string
- */
- public $Workstation = '';
-
- /**
- * The SMTP server timeout in seconds.
- * Default of 5 minutes (300sec) is from RFC2821 section 4.5.3.2
- * @var integer
- */
- public $Timeout = 300;
-
- /**
- * SMTP class debug output mode.
- * Debug output level.
- * Options:
- * * `0` No output
- * * `1` Commands
- * * `2` Data and commands
- * * `3` As 2 plus connection status
- * * `4` Low-level data output
- * @var integer
- * @see SMTP::$do_debug
- */
- public $SMTPDebug = 0;
-
- /**
- * How to handle debug output.
- * Options:
- * * `echo` Output plain-text as-is, appropriate for CLI
- * * `html` Output escaped, line breaks converted to ` `, appropriate for browser output
- * * `error_log` Output to error log as configured in php.ini
- *
- * Alternatively, you can provide a callable expecting two params: a message string and the debug level:
- *
- * $mail->Debugoutput = function($str, $level) {echo "debug level $level; message: $str";};
- *
- * @var string|callable
- * @see SMTP::$Debugoutput
- */
- public $Debugoutput = 'echo';
-
- /**
- * Whether to keep SMTP connection open after each message.
- * If this is set to true then to close the connection
- * requires an explicit call to smtpClose().
- * @var boolean
- */
- public $SMTPKeepAlive = false;
-
- /**
- * Whether to split multiple to addresses into multiple messages
- * or send them all in one message.
- * @var boolean
- */
- public $SingleTo = false;
-
- /**
- * Storage for addresses when SingleTo is enabled.
- * @var array
- * @TODO This should really not be public
- */
- public $SingleToArray = array();
-
- /**
- * Whether to generate VERP addresses on send.
- * Only applicable when sending via SMTP.
- * @link https://en.wikipedia.org/wiki/Variable_envelope_return_path
- * @link http://www.postfix.org/VERP_README.html Postfix VERP info
- * @var boolean
- */
- public $do_verp = false;
-
- /**
- * Whether to allow sending messages with an empty body.
- * @var boolean
- */
- public $AllowEmpty = false;
-
- /**
- * The default line ending.
- * @note The default remains "\n". We force CRLF where we know
- * it must be used via self::CRLF.
- * @var string
- */
- public $LE = "\n";
-
- /**
- * DKIM selector.
- * @var string
- */
- public $DKIM_selector = '';
-
- /**
- * DKIM Identity.
- * Usually the email address used as the source of the email
- * @var string
- */
- public $DKIM_identity = '';
-
- /**
- * DKIM passphrase.
- * Used if your key is encrypted.
- * @var string
- */
- public $DKIM_passphrase = '';
-
- /**
- * DKIM signing domain name.
- * @example 'example.com'
- * @var string
- */
- public $DKIM_domain = '';
-
- /**
- * DKIM private key file path.
- * @var string
- */
- public $DKIM_private = '';
-
- /**
- * Callback Action function name.
- *
- * The function that handles the result of the send email action.
- * It is called out by send() for each email sent.
- *
- * Value can be any php callable: http://www.php.net/is_callable
- *
- * Parameters:
- * boolean $result result of the send action
- * string $to email address of the recipient
- * string $cc cc email addresses
- * string $bcc bcc email addresses
- * string $subject the subject
- * string $body the email body
- * string $from email address of sender
- * @var string
- */
- public $action_function = '';
-
- /**
- * What to put in the X-Mailer header.
- * Options: An empty string for PHPMailer default, whitespace for none, or a string to use
- * @var string
- */
- public $XMailer = '';
-
- /**
- * An instance of the SMTP sender class.
- * @var SMTP
- * @access protected
- */
- protected $smtp = null;
-
- /**
- * The array of 'to' names and addresses.
- * @var array
- * @access protected
- */
- protected $to = array();
-
- /**
- * The array of 'cc' names and addresses.
- * @var array
- * @access protected
- */
- protected $cc = array();
-
- /**
- * The array of 'bcc' names and addresses.
- * @var array
- * @access protected
- */
- protected $bcc = array();
-
- /**
- * The array of reply-to names and addresses.
- * @var array
- * @access protected
- */
- protected $ReplyTo = array();
-
- /**
- * An array of all kinds of addresses.
- * Includes all of $to, $cc, $bcc
- * @var array
- * @access protected
- * @see PHPMailer::$to @see PHPMailer::$cc @see PHPMailer::$bcc
- */
- protected $all_recipients = array();
-
- /**
- * An array of names and addresses queued for validation.
- * In send(), valid and non duplicate entries are moved to $all_recipients
- * and one of $to, $cc, or $bcc.
- * This array is used only for addresses with IDN.
- * @var array
- * @access protected
- * @see PHPMailer::$to @see PHPMailer::$cc @see PHPMailer::$bcc
- * @see PHPMailer::$all_recipients
- */
- protected $RecipientsQueue = array();
-
- /**
- * An array of reply-to names and addresses queued for validation.
- * In send(), valid and non duplicate entries are moved to $ReplyTo.
- * This array is used only for addresses with IDN.
- * @var array
- * @access protected
- * @see PHPMailer::$ReplyTo
- */
- protected $ReplyToQueue = array();
-
- /**
- * The array of attachments.
- * @var array
- * @access protected
- */
- protected $attachment = array();
-
- /**
- * The array of custom headers.
- * @var array
- * @access protected
- */
- protected $CustomHeader = array();
-
- /**
- * The most recent Message-ID (including angular brackets).
- * @var string
- * @access protected
- */
- protected $lastMessageID = '';
-
- /**
- * The message's MIME type.
- * @var string
- * @access protected
- */
- protected $message_type = '';
-
- /**
- * The array of MIME boundary strings.
- * @var array
- * @access protected
- */
- protected $boundary = array();
-
- /**
- * The array of available languages.
- * @var array
- * @access protected
- */
- protected $language = array();
-
- /**
- * The number of errors encountered.
- * @var integer
- * @access protected
- */
- protected $error_count = 0;
-
- /**
- * The S/MIME certificate file path.
- * @var string
- * @access protected
- */
- protected $sign_cert_file = '';
-
- /**
- * The S/MIME key file path.
- * @var string
- * @access protected
- */
- protected $sign_key_file = '';
-
- /**
- * The optional S/MIME extra certificates ("CA Chain") file path.
- * @var string
- * @access protected
- */
- protected $sign_extracerts_file = '';
-
- /**
- * The S/MIME password for the key.
- * Used only if the key is encrypted.
- * @var string
- * @access protected
- */
- protected $sign_key_pass = '';
-
- /**
- * Whether to throw exceptions for errors.
- * @var boolean
- * @access protected
- */
- protected $exceptions = false;
-
- /**
- * Unique ID used for message ID and boundaries.
- * @var string
- * @access protected
- */
- protected $uniqueid = '';
-
- /**
- * Error severity: message only, continue processing.
- */
- const STOP_MESSAGE = 0;
-
- /**
- * Error severity: message, likely ok to continue processing.
- */
- const STOP_CONTINUE = 1;
-
- /**
- * Error severity: message, plus full stop, critical error reached.
- */
- const STOP_CRITICAL = 2;
-
- /**
- * SMTP RFC standard line ending.
- */
- const CRLF = "\r\n";
-
- /**
- * The maximum line length allowed by RFC 2822 section 2.1.1
- * @var integer
- */
- const MAX_LINE_LENGTH = 998;
-
- /**
- * Constructor.
- * @param boolean $exceptions Should we throw external exceptions?
- */
- public function __construct($exceptions = false)
- {
- $this->exceptions = (boolean)$exceptions;
- }
-
- /**
- * Destructor.
- */
- public function __destruct()
- {
- //Close any open SMTP connection nicely
- if ($this->Mailer == 'smtp') {
- $this->smtpClose();
- }
- }
-
- /**
- * Call mail() in a safe_mode-aware fashion.
- * Also, unless sendmail_path points to sendmail (or something that
- * claims to be sendmail), don't pass params (not a perfect fix,
- * but it will do)
- * @param string $to To
- * @param string $subject Subject
- * @param string $body Message Body
- * @param string $header Additional Header(s)
- * @param string $params Params
- * @access private
- * @return boolean
- */
- private function mailPassthru($to, $subject, $body, $header, $params)
- {
- //Check overloading of mail function to avoid double-encoding
- if (ini_get('mbstring.func_overload') & 1) {
- $subject = $this->secureHeader($subject);
- } else {
- $subject = $this->encodeHeader($this->secureHeader($subject));
- }
- if (ini_get('safe_mode') || !($this->UseSendmailOptions)) {
- $result = @mail($to, $subject, $body, $header);
- } else {
- $result = @mail($to, $subject, $body, $header, $params);
- }
- return $result;
- }
-
- /**
- * Output debugging info via user-defined method.
- * Only generates output if SMTP debug output is enabled (@see SMTP::$do_debug).
- * @see PHPMailer::$Debugoutput
- * @see PHPMailer::$SMTPDebug
- * @param string $str
- */
- protected function edebug($str)
- {
- if ($this->SMTPDebug <= 0) {
- return;
- }
- //Avoid clash with built-in function names
- if (!in_array($this->Debugoutput, array('error_log', 'html', 'echo')) and is_callable($this->Debugoutput)) {
- call_user_func($this->Debugoutput, $str, $this->SMTPDebug);
- return;
- }
- switch ($this->Debugoutput) {
- case 'error_log':
- //Don't output, just log
- error_log($str);
- break;
- case 'html':
- //Cleans up output a bit for a better looking, HTML-safe output
- echo htmlentities(
- preg_replace('/[\r\n]+/', '', $str),
- ENT_QUOTES,
- 'UTF-8'
- )
- . " \n";
- break;
- case 'echo':
- default:
- //Normalize line breaks
- $str = preg_replace('/(\r\n|\r|\n)/ms', "\n", $str);
- echo gmdate('Y-m-d H:i:s') . "\t" . str_replace(
- "\n",
- "\n \t ",
- trim($str)
- ) . "\n";
- }
- }
-
- /**
- * Sets message type to HTML or plain.
- * @param boolean $isHtml True for HTML mode.
- * @return void
- */
- public function isHTML($isHtml = true)
- {
- if ($isHtml) {
- $this->ContentType = 'text/html';
- } else {
- $this->ContentType = 'text/plain';
- }
- }
-
- /**
- * Send messages using SMTP.
- * @return void
- */
- public function isSMTP()
- {
- $this->Mailer = 'smtp';
- }
-
- /**
- * Send messages using PHP's mail() function.
- * @return void
- */
- public function isMail()
- {
- $this->Mailer = 'mail';
- }
-
- /**
- * Send messages using $Sendmail.
- * @return void
- */
- public function isSendmail()
- {
- $ini_sendmail_path = ini_get('sendmail_path');
-
- if (!stristr($ini_sendmail_path, 'sendmail')) {
- $this->Sendmail = '/usr/sbin/sendmail';
- } else {
- $this->Sendmail = $ini_sendmail_path;
- }
- $this->Mailer = 'sendmail';
- }
-
- /**
- * Send messages using qmail.
- * @return void
- */
- public function isQmail()
- {
- $ini_sendmail_path = ini_get('sendmail_path');
-
- if (!stristr($ini_sendmail_path, 'qmail')) {
- $this->Sendmail = '/var/qmail/bin/qmail-inject';
- } else {
- $this->Sendmail = $ini_sendmail_path;
- }
- $this->Mailer = 'qmail';
- }
-
- /**
- * Add a "To" address.
- * @param string $address The email address to send to
- * @param string $name
- * @return boolean true on success, false if address already used or invalid in some way
- */
- public function addAddress($address, $name = '')
- {
- return $this->addOrEnqueueAnAddress('to', $address, $name);
- }
-
- /**
- * Add a "CC" address.
- * @note: This function works with the SMTP mailer on win32, not with the "mail" mailer.
- * @param string $address The email address to send to
- * @param string $name
- * @return boolean true on success, false if address already used or invalid in some way
- */
- public function addCC($address, $name = '')
- {
- return $this->addOrEnqueueAnAddress('cc', $address, $name);
- }
-
- /**
- * Add a "BCC" address.
- * @note: This function works with the SMTP mailer on win32, not with the "mail" mailer.
- * @param string $address The email address to send to
- * @param string $name
- * @return boolean true on success, false if address already used or invalid in some way
- */
- public function addBCC($address, $name = '')
- {
- return $this->addOrEnqueueAnAddress('bcc', $address, $name);
- }
-
- /**
- * Add a "Reply-To" address.
- * @param string $address The email address to reply to
- * @param string $name
- * @return boolean true on success, false if address already used or invalid in some way
- */
- public function addReplyTo($address, $name = '')
- {
- return $this->addOrEnqueueAnAddress('Reply-To', $address, $name);
- }
-
- /**
- * Add an address to one of the recipient arrays or to the ReplyTo array. Because PHPMailer
- * can't validate addresses with an IDN without knowing the PHPMailer::$CharSet (that can still
- * be modified after calling this function), addition of such addresses is delayed until send().
- * Addresses that have been added already return false, but do not throw exceptions.
- * @param string $kind One of 'to', 'cc', 'bcc', or 'ReplyTo'
- * @param string $address The email address to send, resp. to reply to
- * @param string $name
- * @throws phpmailerException
- * @return boolean true on success, false if address already used or invalid in some way
- * @access protected
- */
- protected function addOrEnqueueAnAddress($kind, $address, $name)
- {
- $address = trim($address);
- $name = trim(preg_replace('/[\r\n]+/', '', $name)); //Strip breaks and trim
- if (($pos = strrpos($address, '@')) === false) {
- // At-sign is misssing.
- $error_message = $this->lang('invalid_address') . " (addAnAddress $kind): $address";
- $this->setError($error_message);
- $this->edebug($error_message);
- if ($this->exceptions) {
- throw new phpmailerException($error_message);
- }
- return false;
- }
- $params = array($kind, $address, $name);
- // Enqueue addresses with IDN until we know the PHPMailer::$CharSet.
- if ($this->has8bitChars(substr($address, ++$pos)) and $this->idnSupported()) {
- if ($kind != 'Reply-To') {
- if (!array_key_exists($address, $this->RecipientsQueue)) {
- $this->RecipientsQueue[$address] = $params;
- return true;
- }
- } else {
- if (!array_key_exists($address, $this->ReplyToQueue)) {
- $this->ReplyToQueue[$address] = $params;
- return true;
- }
- }
- return false;
- }
- // Immediately add standard addresses without IDN.
- return call_user_func_array(array($this, 'addAnAddress'), $params);
- }
-
- /**
- * Add an address to one of the recipient arrays or to the ReplyTo array.
- * Addresses that have been added already return false, but do not throw exceptions.
- * @param string $kind One of 'to', 'cc', 'bcc', or 'ReplyTo'
- * @param string $address The email address to send, resp. to reply to
- * @param string $name
- * @throws phpmailerException
- * @return boolean true on success, false if address already used or invalid in some way
- * @access protected
- */
- protected function addAnAddress($kind, $address, $name = '')
- {
- if (!in_array($kind, array('to', 'cc', 'bcc', 'Reply-To'))) {
- $error_message = $this->lang('Invalid recipient kind: ') . $kind;
- $this->setError($error_message);
- $this->edebug($error_message);
- if ($this->exceptions) {
- throw new phpmailerException($error_message);
- }
- return false;
- }
- if (!$this->validateAddress($address)) {
- $error_message = $this->lang('invalid_address') . " (addAnAddress $kind): $address";
- $this->setError($error_message);
- $this->edebug($error_message);
- if ($this->exceptions) {
- throw new phpmailerException($error_message);
- }
- return false;
- }
- if ($kind != 'Reply-To') {
- if (!array_key_exists(strtolower($address), $this->all_recipients)) {
- array_push($this->$kind, array($address, $name));
- $this->all_recipients[strtolower($address)] = true;
- return true;
- }
- } else {
- if (!array_key_exists(strtolower($address), $this->ReplyTo)) {
- $this->ReplyTo[strtolower($address)] = array($address, $name);
- return true;
- }
- }
- return false;
- }
-
- /**
- * Parse and validate a string containing one or more RFC822-style comma-separated email addresses
- * of the form "display name " into an array of name/address pairs.
- * Uses the imap_rfc822_parse_adrlist function if the IMAP extension is available.
- * Note that quotes in the name part are removed.
- * @param string $addrstr The address list string
- * @param bool $useimap Whether to use the IMAP extension to parse the list
- * @return array
- * @link http://www.andrew.cmu.edu/user/agreen1/testing/mrbs/web/Mail/RFC822.php A more careful implementation
- */
- public function parseAddresses($addrstr, $useimap = true)
- {
- $addresses = array();
- if ($useimap and function_exists('imap_rfc822_parse_adrlist')) {
- //Use this built-in parser if it's available
- $list = imap_rfc822_parse_adrlist($addrstr, '');
- foreach ($list as $address) {
- if ($address->host != '.SYNTAX-ERROR.') {
- if ($this->validateAddress($address->mailbox . '@' . $address->host)) {
- $addresses[] = array(
- 'name' => (property_exists($address, 'personal') ? $address->personal : ''),
- 'address' => $address->mailbox . '@' . $address->host
- );
- }
- }
- }
- } else {
- //Use this simpler parser
- $list = explode(',', $addrstr);
- foreach ($list as $address) {
- $address = trim($address);
- //Is there a separate name part?
- if (strpos($address, '<') === false) {
- //No separate name, just use the whole thing
- if ($this->validateAddress($address)) {
- $addresses[] = array(
- 'name' => '',
- 'address' => $address
- );
- }
- } else {
- list($name, $email) = explode('<', $address);
- $email = trim(str_replace('>', '', $email));
- if ($this->validateAddress($email)) {
- $addresses[] = array(
- 'name' => trim(str_replace(array('"', "'"), '', $name)),
- 'address' => $email
- );
- }
- }
- }
- }
- return $addresses;
- }
-
- /**
- * Set the From and FromName properties.
- * @param string $address
- * @param string $name
- * @param boolean $auto Whether to also set the Sender address, defaults to true
- * @throws phpmailerException
- * @return boolean
- */
- public function setFrom($address, $name = '', $auto = true)
- {
- $address = trim($address);
- $name = trim(preg_replace('/[\r\n]+/', '', $name)); //Strip breaks and trim
- // Don't validate now addresses with IDN. Will be done in send().
- if (($pos = strrpos($address, '@')) === false or
- (!$this->has8bitChars(substr($address, ++$pos)) or !$this->idnSupported()) and
- !$this->validateAddress($address)) {
- $error_message = $this->lang('invalid_address') . " (setFrom) $address";
- $this->setError($error_message);
- $this->edebug($error_message);
- if ($this->exceptions) {
- throw new phpmailerException($error_message);
- }
- return false;
- }
- $this->From = $address;
- $this->FromName = $name;
- if ($auto) {
- if (empty($this->Sender)) {
- $this->Sender = $address;
- }
- }
- return true;
- }
-
- /**
- * Return the Message-ID header of the last email.
- * Technically this is the value from the last time the headers were created,
- * but it's also the message ID of the last sent message except in
- * pathological cases.
- * @return string
- */
- public function getLastMessageID()
- {
- return $this->lastMessageID;
- }
-
- /**
- * Check that a string looks like an email address.
- * @param string $address The email address to check
- * @param string $patternselect A selector for the validation pattern to use :
- * * `auto` Pick best pattern automatically;
- * * `pcre8` Use the squiloople.com pattern, requires PCRE > 8.0, PHP >= 5.3.2, 5.2.14;
- * * `pcre` Use old PCRE implementation;
- * * `php` Use PHP built-in FILTER_VALIDATE_EMAIL;
- * * `html5` Use the pattern given by the HTML5 spec for 'email' type form input elements.
- * * `noregex` Don't use a regex: super fast, really dumb.
- * @return boolean
- * @static
- * @access public
- */
- public static function validateAddress($address, $patternselect = 'auto')
- {
- //Reject line breaks in addresses; it's valid RFC5322, but not RFC5321
- if (strpos($address, "\n") !== false or strpos($address, "\r") !== false) {
- return false;
- }
- if (!$patternselect or $patternselect == 'auto') {
- //Check this constant first so it works when extension_loaded() is disabled by safe mode
- //Constant was added in PHP 5.2.4
- if (defined('PCRE_VERSION')) {
- //This pattern can get stuck in a recursive loop in PCRE <= 8.0.2
- if (version_compare(PCRE_VERSION, '8.0.3') >= 0) {
- $patternselect = 'pcre8';
- } else {
- $patternselect = 'pcre';
- }
- } elseif (function_exists('extension_loaded') and extension_loaded('pcre')) {
- //Fall back to older PCRE
- $patternselect = 'pcre';
- } else {
- //Filter_var appeared in PHP 5.2.0 and does not require the PCRE extension
- if (version_compare(PHP_VERSION, '5.2.0') >= 0) {
- $patternselect = 'php';
- } else {
- $patternselect = 'noregex';
- }
- }
- }
- switch ($patternselect) {
- case 'pcre8':
- /**
- * Uses the same RFC5322 regex on which FILTER_VALIDATE_EMAIL is based, but allows dotless domains.
- * @link http://squiloople.com/2009/12/20/email-address-validation/
- * @copyright 2009-2010 Michael Rushton
- * Feel free to use and redistribute this code. But please keep this copyright notice.
- */
- return (boolean)preg_match(
- '/^(?!(?>(?1)"?(?>\\\[ -~]|[^"])"?(?1)){255,})(?!(?>(?1)"?(?>\\\[ -~]|[^"])"?(?1)){65,}@)' .
- '((?>(?>(?>((?>(?>(?>\x0D\x0A)?[\t ])+|(?>[\t ]*\x0D\x0A)?[\t ]+)?)(\((?>(?2)' .
- '(?>[\x01-\x08\x0B\x0C\x0E-\'*-\[\]-\x7F]|\\\[\x00-\x7F]|(?3)))*(?2)\)))+(?2))|(?2))?)' .
- '([!#-\'*+\/-9=?^-~-]+|"(?>(?2)(?>[\x01-\x08\x0B\x0C\x0E-!#-\[\]-\x7F]|\\\[\x00-\x7F]))*' .
- '(?2)")(?>(?1)\.(?1)(?4))*(?1)@(?!(?1)[a-z0-9-]{64,})(?1)(?>([a-z0-9](?>[a-z0-9-]*[a-z0-9])?)' .
- '(?>(?1)\.(?!(?1)[a-z0-9-]{64,})(?1)(?5)){0,126}|\[(?:(?>IPv6:(?>([a-f0-9]{1,4})(?>:(?6)){7}' .
- '|(?!(?:.*[a-f0-9][:\]]){8,})((?6)(?>:(?6)){0,6})?::(?7)?))|(?>(?>IPv6:(?>(?6)(?>:(?6)){5}:' .
- '|(?!(?:.*[a-f0-9]:){6,})(?8)?::(?>((?6)(?>:(?6)){0,4}):)?))?(25[0-5]|2[0-4][0-9]|1[0-9]{2}' .
- '|[1-9]?[0-9])(?>\.(?9)){3}))\])(?1)$/isD',
- $address
- );
- case 'pcre':
- //An older regex that doesn't need a recent PCRE
- return (boolean)preg_match(
- '/^(?!(?>"?(?>\\\[ -~]|[^"])"?){255,})(?!(?>"?(?>\\\[ -~]|[^"])"?){65,}@)(?>' .
- '[!#-\'*+\/-9=?^-~-]+|"(?>(?>[\x01-\x08\x0B\x0C\x0E-!#-\[\]-\x7F]|\\\[\x00-\xFF]))*")' .
- '(?>\.(?>[!#-\'*+\/-9=?^-~-]+|"(?>(?>[\x01-\x08\x0B\x0C\x0E-!#-\[\]-\x7F]|\\\[\x00-\xFF]))*"))*' .
- '@(?>(?![a-z0-9-]{64,})(?>[a-z0-9](?>[a-z0-9-]*[a-z0-9])?)(?>\.(?![a-z0-9-]{64,})' .
- '(?>[a-z0-9](?>[a-z0-9-]*[a-z0-9])?)){0,126}|\[(?:(?>IPv6:(?>(?>[a-f0-9]{1,4})(?>:' .
- '[a-f0-9]{1,4}){7}|(?!(?:.*[a-f0-9][:\]]){8,})(?>[a-f0-9]{1,4}(?>:[a-f0-9]{1,4}){0,6})?' .
- '::(?>[a-f0-9]{1,4}(?>:[a-f0-9]{1,4}){0,6})?))|(?>(?>IPv6:(?>[a-f0-9]{1,4}(?>:' .
- '[a-f0-9]{1,4}){5}:|(?!(?:.*[a-f0-9]:){6,})(?>[a-f0-9]{1,4}(?>:[a-f0-9]{1,4}){0,4})?' .
- '::(?>(?:[a-f0-9]{1,4}(?>:[a-f0-9]{1,4}){0,4}):)?))?(?>25[0-5]|2[0-4][0-9]|1[0-9]{2}' .
- '|[1-9]?[0-9])(?>\.(?>25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])){3}))\])$/isD',
- $address
- );
- case 'html5':
- /**
- * This is the pattern used in the HTML5 spec for validation of 'email' type form input elements.
- * @link http://www.whatwg.org/specs/web-apps/current-work/#e-mail-state-(type=email)
- */
- return (boolean)preg_match(
- '/^[a-zA-Z0-9.!#$%&\'*+\/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}' .
- '[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$/sD',
- $address
- );
- case 'noregex':
- //No PCRE! Do something _very_ approximate!
- //Check the address is 3 chars or longer and contains an @ that's not the first or last char
- return (strlen($address) >= 3
- and strpos($address, '@') >= 1
- and strpos($address, '@') != strlen($address) - 1);
- case 'php':
- default:
- return (boolean)filter_var($address, FILTER_VALIDATE_EMAIL);
- }
- }
-
- /**
- * Tells whether IDNs (Internationalized Domain Names) are supported or not. This requires the
- * "intl" and "mbstring" PHP extensions.
- * @return bool "true" if required functions for IDN support are present
- */
- public function idnSupported()
- {
- // @TODO: Write our own "idn_to_ascii" function for PHP <= 5.2.
- return function_exists('idn_to_ascii') and function_exists('mb_convert_encoding');
- }
-
- /**
- * Converts IDN in given email address to its ASCII form, also known as punycode, if possible.
- * Important: Address must be passed in same encoding as currently set in PHPMailer::$CharSet.
- * This function silently returns unmodified address if:
- * - No conversion is necessary (i.e. domain name is not an IDN, or is already in ASCII form)
- * - Conversion to punycode is impossible (e.g. required PHP functions are not available)
- * or fails for any reason (e.g. domain has characters not allowed in an IDN)
- * @see PHPMailer::$CharSet
- * @param string $address The email address to convert
- * @return string The encoded address in ASCII form
- */
- public function punyencodeAddress($address)
- {
- // Verify we have required functions, CharSet, and at-sign.
- if ($this->idnSupported() and
- !empty($this->CharSet) and
- ($pos = strrpos($address, '@')) !== false) {
- $domain = substr($address, ++$pos);
- // Verify CharSet string is a valid one, and domain properly encoded in this CharSet.
- if ($this->has8bitChars($domain) and @mb_check_encoding($domain, $this->CharSet)) {
- $domain = mb_convert_encoding($domain, 'UTF-8', $this->CharSet);
- if (($punycode = defined('INTL_IDNA_VARIANT_UTS46') ?
- idn_to_ascii($domain, 0, INTL_IDNA_VARIANT_UTS46) :
- idn_to_ascii($domain)) !== false) {
- return substr($address, 0, $pos) . $punycode;
- }
- }
- }
- return $address;
- }
-
- /**
- * Create a message and send it.
- * Uses the sending method specified by $Mailer.
- * @throws phpmailerException
- * @return boolean false on error - See the ErrorInfo property for details of the error.
- */
- public function send()
- {
- try {
- if (!$this->preSend()) {
- return false;
- }
- return $this->postSend();
- } catch (phpmailerException $exc) {
- $this->mailHeader = '';
- $this->setError($exc->getMessage());
- if ($this->exceptions) {
- throw $exc;
- }
- return false;
- }
- }
-
- /**
- * Prepare a message for sending.
- * @throws phpmailerException
- * @return boolean
- */
- public function preSend()
- {
- try {
- $this->error_count = 0; // Reset errors
- $this->mailHeader = '';
-
- // Dequeue recipient and Reply-To addresses with IDN
- foreach (array_merge($this->RecipientsQueue, $this->ReplyToQueue) as $params) {
- $params[1] = $this->punyencodeAddress($params[1]);
- call_user_func_array(array($this, 'addAnAddress'), $params);
- }
- if ((count($this->to) + count($this->cc) + count($this->bcc)) < 1) {
- throw new phpmailerException($this->lang('provide_address'), self::STOP_CRITICAL);
- }
-
- // Validate From, Sender, and ConfirmReadingTo addresses
- foreach (array('From', 'Sender', 'ConfirmReadingTo') as $address_kind) {
- $this->$address_kind = trim($this->$address_kind);
- if (empty($this->$address_kind)) {
- continue;
- }
- $this->$address_kind = $this->punyencodeAddress($this->$address_kind);
- if (!$this->validateAddress($this->$address_kind)) {
- $error_message = $this->lang('invalid_address') . ' (punyEncode) ' . $this->$address_kind;
- $this->setError($error_message);
- $this->edebug($error_message);
- if ($this->exceptions) {
- throw new phpmailerException($error_message);
- }
- return false;
- }
- }
-
- // Set whether the message is multipart/alternative
- if ($this->alternativeExists()) {
- $this->ContentType = 'multipart/alternative';
- }
-
- $this->setMessageType();
- // Refuse to send an empty message unless we are specifically allowing it
- if (!$this->AllowEmpty and empty($this->Body)) {
- throw new phpmailerException($this->lang('empty_message'), self::STOP_CRITICAL);
- }
-
- // Create body before headers in case body makes changes to headers (e.g. altering transfer encoding)
- $this->MIMEHeader = '';
- $this->MIMEBody = $this->createBody();
- // createBody may have added some headers, so retain them
- $tempheaders = $this->MIMEHeader;
- $this->MIMEHeader = $this->createHeader();
- $this->MIMEHeader .= $tempheaders;
-
- // To capture the complete message when using mail(), create
- // an extra header list which createHeader() doesn't fold in
- if ($this->Mailer == 'mail') {
- if (count($this->to) > 0) {
- $this->mailHeader .= $this->addrAppend('To', $this->to);
- } else {
- $this->mailHeader .= $this->headerLine('To', 'undisclosed-recipients:;');
- }
- $this->mailHeader .= $this->headerLine(
- 'Subject',
- $this->encodeHeader($this->secureHeader(trim($this->Subject)))
- );
- }
-
- // Sign with DKIM if enabled
- if (!empty($this->DKIM_domain)
- && !empty($this->DKIM_private)
- && !empty($this->DKIM_selector)
- && file_exists($this->DKIM_private)) {
- $header_dkim = $this->DKIM_Add(
- $this->MIMEHeader . $this->mailHeader,
- $this->encodeHeader($this->secureHeader($this->Subject)),
- $this->MIMEBody
- );
- $this->MIMEHeader = rtrim($this->MIMEHeader, "\r\n ") . self::CRLF .
- str_replace("\r\n", "\n", $header_dkim) . self::CRLF;
- }
- return true;
- } catch (phpmailerException $exc) {
- $this->setError($exc->getMessage());
- if ($this->exceptions) {
- throw $exc;
- }
- return false;
- }
- }
-
- /**
- * Actually send a message.
- * Send the email via the selected mechanism
- * @throws phpmailerException
- * @return boolean
- */
- public function postSend()
- {
- try {
- // Choose the mailer and send through it
- switch ($this->Mailer) {
- case 'sendmail':
- case 'qmail':
- return $this->sendmailSend($this->MIMEHeader, $this->MIMEBody);
- case 'smtp':
- return $this->smtpSend($this->MIMEHeader, $this->MIMEBody);
- case 'mail':
- return $this->mailSend($this->MIMEHeader, $this->MIMEBody);
- default:
- $sendMethod = $this->Mailer.'Send';
- if (method_exists($this, $sendMethod)) {
- return $this->$sendMethod($this->MIMEHeader, $this->MIMEBody);
- }
-
- return $this->mailSend($this->MIMEHeader, $this->MIMEBody);
- }
- } catch (phpmailerException $exc) {
- $this->setError($exc->getMessage());
- $this->edebug($exc->getMessage());
- if ($this->exceptions) {
- throw $exc;
- }
- }
- return false;
- }
-
- /**
- * Send mail using the $Sendmail program.
- * @param string $header The message headers
- * @param string $body The message body
- * @see PHPMailer::$Sendmail
- * @throws phpmailerException
- * @access protected
- * @return boolean
- */
- protected function sendmailSend($header, $body)
- {
- if ($this->Sender != '') {
- if ($this->Mailer == 'qmail') {
- $sendmail = sprintf('%s -f%s', escapeshellcmd($this->Sendmail), escapeshellarg($this->Sender));
- } else {
- $sendmail = sprintf('%s -oi -f%s -t', escapeshellcmd($this->Sendmail), escapeshellarg($this->Sender));
- }
- } else {
- if ($this->Mailer == 'qmail') {
- $sendmail = sprintf('%s', escapeshellcmd($this->Sendmail));
- } else {
- $sendmail = sprintf('%s -oi -t', escapeshellcmd($this->Sendmail));
- }
- }
- if ($this->SingleTo) {
- foreach ($this->SingleToArray as $toAddr) {
- if (!@$mail = popen($sendmail, 'w')) {
- throw new phpmailerException($this->lang('execute') . $this->Sendmail, self::STOP_CRITICAL);
- }
- fputs($mail, 'To: ' . $toAddr . "\n");
- fputs($mail, $header);
- fputs($mail, $body);
- $result = pclose($mail);
- $this->doCallback(
- ($result == 0),
- array($toAddr),
- $this->cc,
- $this->bcc,
- $this->Subject,
- $body,
- $this->From
- );
- if ($result != 0) {
- throw new phpmailerException($this->lang('execute') . $this->Sendmail, self::STOP_CRITICAL);
- }
- }
- } else {
- if (!@$mail = popen($sendmail, 'w')) {
- throw new phpmailerException($this->lang('execute') . $this->Sendmail, self::STOP_CRITICAL);
- }
- fputs($mail, $header);
- fputs($mail, $body);
- $result = pclose($mail);
- $this->doCallback(
- ($result == 0),
- $this->to,
- $this->cc,
- $this->bcc,
- $this->Subject,
- $body,
- $this->From
- );
- if ($result != 0) {
- throw new phpmailerException($this->lang('execute') . $this->Sendmail, self::STOP_CRITICAL);
- }
- }
- return true;
- }
-
- /**
- * Send mail using the PHP mail() function.
- * @param string $header The message headers
- * @param string $body The message body
- * @link http://www.php.net/manual/en/book.mail.php
- * @throws phpmailerException
- * @access protected
- * @return boolean
- */
- protected function mailSend($header, $body)
- {
- $toArr = array();
- foreach ($this->to as $toaddr) {
- $toArr[] = $this->addrFormat($toaddr);
- }
- $to = implode(', ', $toArr);
-
- if (empty($this->Sender)) {
- $params = ' ';
- } else {
- $params = sprintf('-f%s', $this->Sender);
- }
- if ($this->Sender != '' and !ini_get('safe_mode')) {
- $old_from = ini_get('sendmail_from');
- ini_set('sendmail_from', $this->Sender);
- }
- $result = false;
- if ($this->SingleTo && count($toArr) > 1) {
- foreach ($toArr as $toAddr) {
- $result = $this->mailPassthru($toAddr, $this->Subject, $body, $header, $params);
- $this->doCallback($result, array($toAddr), $this->cc, $this->bcc, $this->Subject, $body, $this->From);
- }
- } else {
- $result = $this->mailPassthru($to, $this->Subject, $body, $header, $params);
- $this->doCallback($result, $this->to, $this->cc, $this->bcc, $this->Subject, $body, $this->From);
- }
- if (isset($old_from)) {
- ini_set('sendmail_from', $old_from);
- }
- if (!$result) {
- throw new phpmailerException($this->lang('instantiate'), self::STOP_CRITICAL);
- }
- return true;
- }
-
- /**
- * Get an instance to use for SMTP operations.
- * Override this function to load your own SMTP implementation
- * @return SMTP
- */
- public function getSMTPInstance()
- {
- if (!is_object($this->smtp)) {
- $this->smtp = new SMTP;
- }
- return $this->smtp;
- }
-
- /**
- * Send mail via SMTP.
- * Returns false if there is a bad MAIL FROM, RCPT, or DATA input.
- * Uses the PHPMailerSMTP class by default.
- * @see PHPMailer::getSMTPInstance() to use a different class.
- * @param string $header The message headers
- * @param string $body The message body
- * @throws phpmailerException
- * @uses SMTP
- * @access protected
- * @return boolean
- */
- protected function smtpSend($header, $body)
- {
- $bad_rcpt = array();
- if (!$this->smtpConnect($this->SMTPOptions)) {
- throw new phpmailerException($this->lang('smtp_connect_failed'), self::STOP_CRITICAL);
- }
- if ('' == $this->Sender) {
- $smtp_from = $this->From;
- } else {
- $smtp_from = $this->Sender;
- }
- if (!$this->smtp->mail($smtp_from)) {
- $this->setError($this->lang('from_failed') . $smtp_from . ' : ' . implode(',', $this->smtp->getError()));
- throw new phpmailerException($this->ErrorInfo, self::STOP_CRITICAL);
- }
-
- // Attempt to send to all recipients
- foreach (array($this->to, $this->cc, $this->bcc) as $togroup) {
- foreach ($togroup as $to) {
- if (!$this->smtp->recipient($to[0])) {
- $error = $this->smtp->getError();
- $bad_rcpt[] = array('to' => $to[0], 'error' => $error['detail']);
- $isSent = false;
- } else {
- $isSent = true;
- }
- $this->doCallback($isSent, array($to[0]), array(), array(), $this->Subject, $body, $this->From);
- }
- }
-
- // Only send the DATA command if we have viable recipients
- if ((count($this->all_recipients) > count($bad_rcpt)) and !$this->smtp->data($header . $body)) {
- throw new phpmailerException($this->lang('data_not_accepted'), self::STOP_CRITICAL);
- }
- if ($this->SMTPKeepAlive) {
- $this->smtp->reset();
- } else {
- $this->smtp->quit();
- $this->smtp->close();
- }
- //Create error message for any bad addresses
- if (count($bad_rcpt) > 0) {
- $errstr = '';
- foreach ($bad_rcpt as $bad) {
- $errstr .= $bad['to'] . ': ' . $bad['error'];
- }
- throw new phpmailerException(
- $this->lang('recipients_failed') . $errstr,
- self::STOP_CONTINUE
- );
- }
- return true;
- }
-
- /**
- * Initiate a connection to an SMTP server.
- * Returns false if the operation failed.
- * @param array $options An array of options compatible with stream_context_create()
- * @uses SMTP
- * @access public
- * @throws phpmailerException
- * @return boolean
- */
- public function smtpConnect($options = array())
- {
- if (is_null($this->smtp)) {
- $this->smtp = $this->getSMTPInstance();
- }
-
- // Already connected?
- if ($this->smtp->connected()) {
- return true;
- }
-
- $this->smtp->setTimeout($this->Timeout);
- $this->smtp->setDebugLevel($this->SMTPDebug);
- $this->smtp->setDebugOutput($this->Debugoutput);
- $this->smtp->setVerp($this->do_verp);
- $hosts = explode(';', $this->Host);
- $lastexception = null;
-
- foreach ($hosts as $hostentry) {
- $hostinfo = array();
- if (!preg_match('/^((ssl|tls):\/\/)*([a-zA-Z0-9\.-]*):?([0-9]*)$/', trim($hostentry), $hostinfo)) {
- // Not a valid host entry
- continue;
- }
- // $hostinfo[2]: optional ssl or tls prefix
- // $hostinfo[3]: the hostname
- // $hostinfo[4]: optional port number
- // The host string prefix can temporarily override the current setting for SMTPSecure
- // If it's not specified, the default value is used
- $prefix = '';
- $secure = $this->SMTPSecure;
- $tls = ($this->SMTPSecure == 'tls');
- if ('ssl' == $hostinfo[2] or ('' == $hostinfo[2] and 'ssl' == $this->SMTPSecure)) {
- $prefix = 'ssl://';
- $tls = false; // Can't have SSL and TLS at the same time
- $secure = 'ssl';
- } elseif ($hostinfo[2] == 'tls') {
- $tls = true;
- // tls doesn't use a prefix
- $secure = 'tls';
- }
- //Do we need the OpenSSL extension?
- $sslext = defined('OPENSSL_ALGO_SHA1');
- if ('tls' === $secure or 'ssl' === $secure) {
- //Check for an OpenSSL constant rather than using extension_loaded, which is sometimes disabled
- if (!$sslext) {
- throw new phpmailerException($this->lang('extension_missing').'openssl', self::STOP_CRITICAL);
- }
- }
- $host = $hostinfo[3];
- $port = $this->Port;
- $tport = (integer)$hostinfo[4];
- if ($tport > 0 and $tport < 65536) {
- $port = $tport;
- }
- if ($this->smtp->connect($prefix . $host, $port, $this->Timeout, $options)) {
- try {
- if ($this->Helo) {
- $hello = $this->Helo;
- } else {
- $hello = $this->serverHostname();
- }
- $this->smtp->hello($hello);
- //Automatically enable TLS encryption if:
- // * it's not disabled
- // * we have openssl extension
- // * we are not already using SSL
- // * the server offers STARTTLS
- if ($this->SMTPAutoTLS and $sslext and $secure != 'ssl' and $this->smtp->getServerExt('STARTTLS')) {
- $tls = true;
- }
- if ($tls) {
- if (!$this->smtp->startTLS()) {
- throw new phpmailerException($this->lang('connect_host'));
- }
- // We must resend HELO after tls negotiation
- $this->smtp->hello($hello);
- }
- if ($this->SMTPAuth) {
- if (!$this->smtp->authenticate(
- $this->Username,
- $this->Password,
- $this->AuthType,
- $this->Realm,
- $this->Workstation
- )
- ) {
- throw new phpmailerException($this->lang('authenticate'));
- }
- }
- return true;
- } catch (phpmailerException $exc) {
- $lastexception = $exc;
- $this->edebug($exc->getMessage());
- // We must have connected, but then failed TLS or Auth, so close connection nicely
- $this->smtp->quit();
- }
- }
- }
- // If we get here, all connection attempts have failed, so close connection hard
- $this->smtp->close();
- // As we've caught all exceptions, just report whatever the last one was
- if ($this->exceptions and !is_null($lastexception)) {
- throw $lastexception;
- }
- return false;
- }
-
- /**
- * Close the active SMTP session if one exists.
- * @return void
- */
- public function smtpClose()
- {
- if ($this->smtp !== null) {
- if ($this->smtp->connected()) {
- $this->smtp->quit();
- $this->smtp->close();
- }
- }
- }
-
- /**
- * Set the language for error messages.
- * Returns false if it cannot load the language file.
- * The default language is English.
- * @param string $langcode ISO 639-1 2-character language code (e.g. French is "fr")
- * @param string $lang_path Path to the language file directory, with trailing separator (slash)
- * @return boolean
- * @access public
- */
- public function setLanguage($langcode = 'en', $lang_path = '')
- {
- // Define full set of translatable strings in English
- $PHPMAILER_LANG = array(
- 'authenticate' => 'SMTP Error: Could not authenticate.',
- 'connect_host' => 'SMTP Error: Could not connect to SMTP host.',
- 'data_not_accepted' => 'SMTP Error: data not accepted.',
- 'empty_message' => 'Message body empty',
- 'encoding' => 'Unknown encoding: ',
- 'execute' => 'Could not execute: ',
- 'file_access' => 'Could not access file: ',
- 'file_open' => 'File Error: Could not open file: ',
- 'from_failed' => 'The following From address failed: ',
- 'instantiate' => 'Could not instantiate mail function.',
- 'invalid_address' => 'Invalid address: ',
- 'mailer_not_supported' => ' mailer is not supported.',
- 'provide_address' => 'You must provide at least one recipient email address.',
- 'recipients_failed' => 'SMTP Error: The following recipients failed: ',
- 'signing' => 'Signing Error: ',
- 'smtp_connect_failed' => 'SMTP connect() failed.',
- 'smtp_error' => 'SMTP server error: ',
- 'variable_set' => 'Cannot set or reset variable: ',
- 'extension_missing' => 'Extension missing: '
- );
- if (empty($lang_path)) {
- // Calculate an absolute path so it can work if CWD is not here
- $lang_path = dirname(__FILE__). DIRECTORY_SEPARATOR . 'language'. DIRECTORY_SEPARATOR;
- }
- $foundlang = true;
- $lang_file = $lang_path . 'phpmailer.lang-' . $langcode . '.php';
- // There is no English translation file
- if ($langcode != 'en') {
- // Make sure language file path is readable
- if (!is_readable($lang_file)) {
- $foundlang = false;
- } else {
- // Overwrite language-specific strings.
- // This way we'll never have missing translation keys.
- $foundlang = include $lang_file;
- }
- }
- $this->language = $PHPMAILER_LANG;
- return (boolean)$foundlang; // Returns false if language not found
- }
-
- /**
- * Get the array of strings for the current language.
- * @return array
- */
- public function getTranslations()
- {
- return $this->language;
- }
-
- /**
- * Create recipient headers.
- * @access public
- * @param string $type
- * @param array $addr An array of recipient,
- * where each recipient is a 2-element indexed array with element 0 containing an address
- * and element 1 containing a name, like:
- * array(array('joe@example.com', 'Joe User'), array('zoe@example.com', 'Zoe User'))
- * @return string
- */
- public function addrAppend($type, $addr)
- {
- $addresses = array();
- foreach ($addr as $address) {
- $addresses[] = $this->addrFormat($address);
- }
- return $type . ': ' . implode(', ', $addresses) . $this->LE;
- }
-
- /**
- * Format an address for use in a message header.
- * @access public
- * @param array $addr A 2-element indexed array, element 0 containing an address, element 1 containing a name
- * like array('joe@example.com', 'Joe User')
- * @return string
- */
- public function addrFormat($addr)
- {
- if (empty($addr[1])) { // No name provided
- return $this->secureHeader($addr[0]);
- } else {
- return $this->encodeHeader($this->secureHeader($addr[1]), 'phrase') . ' <' . $this->secureHeader(
- $addr[0]
- ) . '>';
- }
- }
-
- /**
- * Word-wrap message.
- * For use with mailers that do not automatically perform wrapping
- * and for quoted-printable encoded messages.
- * Original written by philippe.
- * @param string $message The message to wrap
- * @param integer $length The line length to wrap to
- * @param boolean $qp_mode Whether to run in Quoted-Printable mode
- * @access public
- * @return string
- */
- public function wrapText($message, $length, $qp_mode = false)
- {
- if ($qp_mode) {
- $soft_break = sprintf(' =%s', $this->LE);
- } else {
- $soft_break = $this->LE;
- }
- // If utf-8 encoding is used, we will need to make sure we don't
- // split multibyte characters when we wrap
- $is_utf8 = (strtolower($this->CharSet) == 'utf-8');
- $lelen = strlen($this->LE);
- $crlflen = strlen(self::CRLF);
-
- $message = $this->fixEOL($message);
- //Remove a trailing line break
- if (substr($message, -$lelen) == $this->LE) {
- $message = substr($message, 0, -$lelen);
- }
-
- //Split message into lines
- $lines = explode($this->LE, $message);
- //Message will be rebuilt in here
- $message = '';
- foreach ($lines as $line) {
- $words = explode(' ', $line);
- $buf = '';
- $firstword = true;
- foreach ($words as $word) {
- if ($qp_mode and (strlen($word) > $length)) {
- $space_left = $length - strlen($buf) - $crlflen;
- if (!$firstword) {
- if ($space_left > 20) {
- $len = $space_left;
- if ($is_utf8) {
- $len = $this->utf8CharBoundary($word, $len);
- } elseif (substr($word, $len - 1, 1) == '=') {
- $len--;
- } elseif (substr($word, $len - 2, 1) == '=') {
- $len -= 2;
- }
- $part = substr($word, 0, $len);
- $word = substr($word, $len);
- $buf .= ' ' . $part;
- $message .= $buf . sprintf('=%s', self::CRLF);
- } else {
- $message .= $buf . $soft_break;
- }
- $buf = '';
- }
- while (strlen($word) > 0) {
- if ($length <= 0) {
- break;
- }
- $len = $length;
- if ($is_utf8) {
- $len = $this->utf8CharBoundary($word, $len);
- } elseif (substr($word, $len - 1, 1) == '=') {
- $len--;
- } elseif (substr($word, $len - 2, 1) == '=') {
- $len -= 2;
- }
- $part = substr($word, 0, $len);
- $word = substr($word, $len);
-
- if (strlen($word) > 0) {
- $message .= $part . sprintf('=%s', self::CRLF);
- } else {
- $buf = $part;
- }
- }
- } else {
- $buf_o = $buf;
- if (!$firstword) {
- $buf .= ' ';
- }
- $buf .= $word;
-
- if (strlen($buf) > $length and $buf_o != '') {
- $message .= $buf_o . $soft_break;
- $buf = $word;
- }
- }
- $firstword = false;
- }
- $message .= $buf . self::CRLF;
- }
-
- return $message;
- }
-
- /**
- * Find the last character boundary prior to $maxLength in a utf-8
- * quoted-printable encoded string.
- * Original written by Colin Brown.
- * @access public
- * @param string $encodedText utf-8 QP text
- * @param integer $maxLength Find the last character boundary prior to this length
- * @return integer
- */
- public function utf8CharBoundary($encodedText, $maxLength)
- {
- $foundSplitPos = false;
- $lookBack = 3;
- while (!$foundSplitPos) {
- $lastChunk = substr($encodedText, $maxLength - $lookBack, $lookBack);
- $encodedCharPos = strpos($lastChunk, '=');
- if (false !== $encodedCharPos) {
- // Found start of encoded character byte within $lookBack block.
- // Check the encoded byte value (the 2 chars after the '=')
- $hex = substr($encodedText, $maxLength - $lookBack + $encodedCharPos + 1, 2);
- $dec = hexdec($hex);
- if ($dec < 128) {
- // Single byte character.
- // If the encoded char was found at pos 0, it will fit
- // otherwise reduce maxLength to start of the encoded char
- if ($encodedCharPos > 0) {
- $maxLength = $maxLength - ($lookBack - $encodedCharPos);
- }
- $foundSplitPos = true;
- } elseif ($dec >= 192) {
- // First byte of a multi byte character
- // Reduce maxLength to split at start of character
- $maxLength = $maxLength - ($lookBack - $encodedCharPos);
- $foundSplitPos = true;
- } elseif ($dec < 192) {
- // Middle byte of a multi byte character, look further back
- $lookBack += 3;
- }
- } else {
- // No encoded character found
- $foundSplitPos = true;
- }
- }
- return $maxLength;
- }
-
- /**
- * Apply word wrapping to the message body.
- * Wraps the message body to the number of chars set in the WordWrap property.
- * You should only do this to plain-text bodies as wrapping HTML tags may break them.
- * This is called automatically by createBody(), so you don't need to call it yourself.
- * @access public
- * @return void
- */
- public function setWordWrap()
- {
- if ($this->WordWrap < 1) {
- return;
- }
-
- switch ($this->message_type) {
- case 'alt':
- case 'alt_inline':
- case 'alt_attach':
- case 'alt_inline_attach':
- $this->AltBody = $this->wrapText($this->AltBody, $this->WordWrap);
- break;
- default:
- $this->Body = $this->wrapText($this->Body, $this->WordWrap);
- break;
- }
- }
-
- /**
- * Assemble message headers.
- * @access public
- * @return string The assembled headers
- */
- public function createHeader()
- {
- $result = '';
-
- if ($this->MessageDate == '') {
- $this->MessageDate = self::rfcDate();
- }
- $result .= $this->headerLine('Date', $this->MessageDate);
-
- // To be created automatically by mail()
- if ($this->SingleTo) {
- if ($this->Mailer != 'mail') {
- foreach ($this->to as $toaddr) {
- $this->SingleToArray[] = $this->addrFormat($toaddr);
- }
- }
- } else {
- if (count($this->to) > 0) {
- if ($this->Mailer != 'mail') {
- $result .= $this->addrAppend('To', $this->to);
- }
- } elseif (count($this->cc) == 0) {
- $result .= $this->headerLine('To', 'undisclosed-recipients:;');
- }
- }
-
- $result .= $this->addrAppend('From', array(array(trim($this->From), $this->FromName)));
-
- // sendmail and mail() extract Cc from the header before sending
- if (count($this->cc) > 0) {
- $result .= $this->addrAppend('Cc', $this->cc);
- }
-
- // sendmail and mail() extract Bcc from the header before sending
- if ((
- $this->Mailer == 'sendmail' or $this->Mailer == 'qmail' or $this->Mailer == 'mail'
- )
- and count($this->bcc) > 0
- ) {
- $result .= $this->addrAppend('Bcc', $this->bcc);
- }
-
- if (count($this->ReplyTo) > 0) {
- $result .= $this->addrAppend('Reply-To', $this->ReplyTo);
- }
-
- // mail() sets the subject itself
- if ($this->Mailer != 'mail') {
- $result .= $this->headerLine('Subject', $this->encodeHeader($this->secureHeader($this->Subject)));
- }
-
- if ('' != $this->MessageID and preg_match('/^<.*@.*>$/', $this->MessageID)) {
- $this->lastMessageID = $this->MessageID;
- } else {
- $this->lastMessageID = sprintf('<%s@%s>', $this->uniqueid, $this->serverHostname());
- }
- $result .= $this->headerLine('Message-ID', $this->lastMessageID);
- if (!is_null($this->Priority)) {
- $result .= $this->headerLine('X-Priority', $this->Priority);
- }
- if ($this->XMailer == '') {
- $result .= $this->headerLine(
- 'X-Mailer',
- 'PHPMailer ' . $this->Version . ' (https://github.com/PHPMailer/PHPMailer)'
- );
- } else {
- $myXmailer = trim($this->XMailer);
- if ($myXmailer) {
- $result .= $this->headerLine('X-Mailer', $myXmailer);
- }
- }
-
- if ($this->ConfirmReadingTo != '') {
- $result .= $this->headerLine('Disposition-Notification-To', '<' . $this->ConfirmReadingTo . '>');
- }
-
- // Add custom headers
- foreach ($this->CustomHeader as $header) {
- $result .= $this->headerLine(
- trim($header[0]),
- $this->encodeHeader(trim($header[1]))
- );
- }
- if (!$this->sign_key_file) {
- $result .= $this->headerLine('MIME-Version', '1.0');
- $result .= $this->getMailMIME();
- }
-
- return $result;
- }
-
- /**
- * Get the message MIME type headers.
- * @access public
- * @return string
- */
- public function getMailMIME()
- {
- $result = '';
- $ismultipart = true;
- switch ($this->message_type) {
- case 'inline':
- $result .= $this->headerLine('Content-Type', 'multipart/related;');
- $result .= $this->textLine("\tboundary=\"" . $this->boundary[1] . '"');
- break;
- case 'attach':
- case 'inline_attach':
- case 'alt_attach':
- case 'alt_inline_attach':
- $result .= $this->headerLine('Content-Type', 'multipart/mixed;');
- $result .= $this->textLine("\tboundary=\"" . $this->boundary[1] . '"');
- break;
- case 'alt':
- case 'alt_inline':
- $result .= $this->headerLine('Content-Type', 'multipart/alternative;');
- $result .= $this->textLine("\tboundary=\"" . $this->boundary[1] . '"');
- break;
- default:
- // Catches case 'plain': and case '':
- $result .= $this->textLine('Content-Type: ' . $this->ContentType . '; charset=' . $this->CharSet);
- $ismultipart = false;
- break;
- }
- // RFC1341 part 5 says 7bit is assumed if not specified
- if ($this->Encoding != '7bit') {
- // RFC 2045 section 6.4 says multipart MIME parts may only use 7bit, 8bit or binary CTE
- if ($ismultipart) {
- if ($this->Encoding == '8bit') {
- $result .= $this->headerLine('Content-Transfer-Encoding', '8bit');
- }
- // The only remaining alternatives are quoted-printable and base64, which are both 7bit compatible
- } else {
- $result .= $this->headerLine('Content-Transfer-Encoding', $this->Encoding);
- }
- }
-
- if ($this->Mailer != 'mail') {
- $result .= $this->LE;
- }
-
- return $result;
- }
-
- /**
- * Returns the whole MIME message.
- * Includes complete headers and body.
- * Only valid post preSend().
- * @see PHPMailer::preSend()
- * @access public
- * @return string
- */
- public function getSentMIMEMessage()
- {
- return rtrim($this->MIMEHeader . $this->mailHeader, "\n\r") . self::CRLF . self::CRLF . $this->MIMEBody;
- }
-
- /**
- * Assemble the message body.
- * Returns an empty string on failure.
- * @access public
- * @throws phpmailerException
- * @return string The assembled message body
- */
- public function createBody()
- {
- $body = '';
- //Create unique IDs and preset boundaries
- $this->uniqueid = md5(uniqid(time()));
- $this->boundary[1] = 'b1_' . $this->uniqueid;
- $this->boundary[2] = 'b2_' . $this->uniqueid;
- $this->boundary[3] = 'b3_' . $this->uniqueid;
-
- if ($this->sign_key_file) {
- $body .= $this->getMailMIME() . $this->LE;
- }
-
- $this->setWordWrap();
-
- $bodyEncoding = $this->Encoding;
- $bodyCharSet = $this->CharSet;
- //Can we do a 7-bit downgrade?
- if ($bodyEncoding == '8bit' and !$this->has8bitChars($this->Body)) {
- $bodyEncoding = '7bit';
- $bodyCharSet = 'us-ascii';
- }
- //If lines are too long, and we're not already using an encoding that will shorten them,
- //change to quoted-printable transfer encoding
- if ('base64' != $this->Encoding and self::hasLineLongerThanMax($this->Body)) {
- $this->Encoding = 'quoted-printable';
- $bodyEncoding = 'quoted-printable';
- }
-
- $altBodyEncoding = $this->Encoding;
- $altBodyCharSet = $this->CharSet;
- //Can we do a 7-bit downgrade?
- if ($altBodyEncoding == '8bit' and !$this->has8bitChars($this->AltBody)) {
- $altBodyEncoding = '7bit';
- $altBodyCharSet = 'us-ascii';
- }
- //If lines are too long, and we're not already using an encoding that will shorten them,
- //change to quoted-printable transfer encoding
- if ('base64' != $altBodyEncoding and self::hasLineLongerThanMax($this->AltBody)) {
- $altBodyEncoding = 'quoted-printable';
- }
- //Use this as a preamble in all multipart message types
- $mimepre = "This is a multi-part message in MIME format." . $this->LE . $this->LE;
- switch ($this->message_type) {
- case 'inline':
- $body .= $mimepre;
- $body .= $this->getBoundary($this->boundary[1], $bodyCharSet, '', $bodyEncoding);
- $body .= $this->encodeString($this->Body, $bodyEncoding);
- $body .= $this->LE . $this->LE;
- $body .= $this->attachAll('inline', $this->boundary[1]);
- break;
- case 'attach':
- $body .= $mimepre;
- $body .= $this->getBoundary($this->boundary[1], $bodyCharSet, '', $bodyEncoding);
- $body .= $this->encodeString($this->Body, $bodyEncoding);
- $body .= $this->LE . $this->LE;
- $body .= $this->attachAll('attachment', $this->boundary[1]);
- break;
- case 'inline_attach':
- $body .= $mimepre;
- $body .= $this->textLine('--' . $this->boundary[1]);
- $body .= $this->headerLine('Content-Type', 'multipart/related;');
- $body .= $this->textLine("\tboundary=\"" . $this->boundary[2] . '"');
- $body .= $this->LE;
- $body .= $this->getBoundary($this->boundary[2], $bodyCharSet, '', $bodyEncoding);
- $body .= $this->encodeString($this->Body, $bodyEncoding);
- $body .= $this->LE . $this->LE;
- $body .= $this->attachAll('inline', $this->boundary[2]);
- $body .= $this->LE;
- $body .= $this->attachAll('attachment', $this->boundary[1]);
- break;
- case 'alt':
- $body .= $mimepre;
- $body .= $this->getBoundary($this->boundary[1], $altBodyCharSet, 'text/plain', $altBodyEncoding);
- $body .= $this->encodeString($this->AltBody, $altBodyEncoding);
- $body .= $this->LE . $this->LE;
- $body .= $this->getBoundary($this->boundary[1], $bodyCharSet, 'text/html', $bodyEncoding);
- $body .= $this->encodeString($this->Body, $bodyEncoding);
- $body .= $this->LE . $this->LE;
- if (!empty($this->Ical)) {
- $body .= $this->getBoundary($this->boundary[1], '', 'text/calendar; method=REQUEST', '');
- $body .= $this->encodeString($this->Ical, $this->Encoding);
- $body .= $this->LE . $this->LE;
- }
- $body .= $this->endBoundary($this->boundary[1]);
- break;
- case 'alt_inline':
- $body .= $mimepre;
- $body .= $this->getBoundary($this->boundary[1], $altBodyCharSet, 'text/plain', $altBodyEncoding);
- $body .= $this->encodeString($this->AltBody, $altBodyEncoding);
- $body .= $this->LE . $this->LE;
- $body .= $this->textLine('--' . $this->boundary[1]);
- $body .= $this->headerLine('Content-Type', 'multipart/related;');
- $body .= $this->textLine("\tboundary=\"" . $this->boundary[2] . '"');
- $body .= $this->LE;
- $body .= $this->getBoundary($this->boundary[2], $bodyCharSet, 'text/html', $bodyEncoding);
- $body .= $this->encodeString($this->Body, $bodyEncoding);
- $body .= $this->LE . $this->LE;
- $body .= $this->attachAll('inline', $this->boundary[2]);
- $body .= $this->LE;
- $body .= $this->endBoundary($this->boundary[1]);
- break;
- case 'alt_attach':
- $body .= $mimepre;
- $body .= $this->textLine('--' . $this->boundary[1]);
- $body .= $this->headerLine('Content-Type', 'multipart/alternative;');
- $body .= $this->textLine("\tboundary=\"" . $this->boundary[2] . '"');
- $body .= $this->LE;
- $body .= $this->getBoundary($this->boundary[2], $altBodyCharSet, 'text/plain', $altBodyEncoding);
- $body .= $this->encodeString($this->AltBody, $altBodyEncoding);
- $body .= $this->LE . $this->LE;
- $body .= $this->getBoundary($this->boundary[2], $bodyCharSet, 'text/html', $bodyEncoding);
- $body .= $this->encodeString($this->Body, $bodyEncoding);
- $body .= $this->LE . $this->LE;
- $body .= $this->endBoundary($this->boundary[2]);
- $body .= $this->LE;
- $body .= $this->attachAll('attachment', $this->boundary[1]);
- break;
- case 'alt_inline_attach':
- $body .= $mimepre;
- $body .= $this->textLine('--' . $this->boundary[1]);
- $body .= $this->headerLine('Content-Type', 'multipart/alternative;');
- $body .= $this->textLine("\tboundary=\"" . $this->boundary[2] . '"');
- $body .= $this->LE;
- $body .= $this->getBoundary($this->boundary[2], $altBodyCharSet, 'text/plain', $altBodyEncoding);
- $body .= $this->encodeString($this->AltBody, $altBodyEncoding);
- $body .= $this->LE . $this->LE;
- $body .= $this->textLine('--' . $this->boundary[2]);
- $body .= $this->headerLine('Content-Type', 'multipart/related;');
- $body .= $this->textLine("\tboundary=\"" . $this->boundary[3] . '"');
- $body .= $this->LE;
- $body .= $this->getBoundary($this->boundary[3], $bodyCharSet, 'text/html', $bodyEncoding);
- $body .= $this->encodeString($this->Body, $bodyEncoding);
- $body .= $this->LE . $this->LE;
- $body .= $this->attachAll('inline', $this->boundary[3]);
- $body .= $this->LE;
- $body .= $this->endBoundary($this->boundary[2]);
- $body .= $this->LE;
- $body .= $this->attachAll('attachment', $this->boundary[1]);
- break;
- default:
- // catch case 'plain' and case ''
- $body .= $this->encodeString($this->Body, $bodyEncoding);
- break;
- }
-
- if ($this->isError()) {
- $body = '';
- } elseif ($this->sign_key_file) {
- try {
- if (!defined('PKCS7_TEXT')) {
- throw new phpmailerException($this->lang('extension_missing') . 'openssl');
- }
- // @TODO would be nice to use php://temp streams here, but need to wrap for PHP < 5.1
- $file = tempnam(sys_get_temp_dir(), 'mail');
- if (false === file_put_contents($file, $body)) {
- throw new phpmailerException($this->lang('signing') . ' Could not write temp file');
- }
- $signed = tempnam(sys_get_temp_dir(), 'signed');
- //Workaround for PHP bug https://bugs.php.net/bug.php?id=69197
- if (empty($this->sign_extracerts_file)) {
- $sign = @openssl_pkcs7_sign(
- $file,
- $signed,
- 'file://' . realpath($this->sign_cert_file),
- array('file://' . realpath($this->sign_key_file), $this->sign_key_pass),
- null
- );
- } else {
- $sign = @openssl_pkcs7_sign(
- $file,
- $signed,
- 'file://' . realpath($this->sign_cert_file),
- array('file://' . realpath($this->sign_key_file), $this->sign_key_pass),
- null,
- PKCS7_DETACHED,
- $this->sign_extracerts_file
- );
- }
- if ($sign) {
- @unlink($file);
- $body = file_get_contents($signed);
- @unlink($signed);
- //The message returned by openssl contains both headers and body, so need to split them up
- $parts = explode("\n\n", $body, 2);
- $this->MIMEHeader .= $parts[0] . $this->LE . $this->LE;
- $body = $parts[1];
- } else {
- @unlink($file);
- @unlink($signed);
- throw new phpmailerException($this->lang('signing') . openssl_error_string());
- }
- } catch (phpmailerException $exc) {
- $body = '';
- if ($this->exceptions) {
- throw $exc;
- }
- }
- }
- return $body;
- }
-
- /**
- * Return the start of a message boundary.
- * @access protected
- * @param string $boundary
- * @param string $charSet
- * @param string $contentType
- * @param string $encoding
- * @return string
- */
- protected function getBoundary($boundary, $charSet, $contentType, $encoding)
- {
- $result = '';
- if ($charSet == '') {
- $charSet = $this->CharSet;
- }
- if ($contentType == '') {
- $contentType = $this->ContentType;
- }
- if ($encoding == '') {
- $encoding = $this->Encoding;
- }
- $result .= $this->textLine('--' . $boundary);
- $result .= sprintf('Content-Type: %s; charset=%s', $contentType, $charSet);
- $result .= $this->LE;
- // RFC1341 part 5 says 7bit is assumed if not specified
- if ($encoding != '7bit') {
- $result .= $this->headerLine('Content-Transfer-Encoding', $encoding);
- }
- $result .= $this->LE;
-
- return $result;
- }
-
- /**
- * Return the end of a message boundary.
- * @access protected
- * @param string $boundary
- * @return string
- */
- protected function endBoundary($boundary)
- {
- return $this->LE . '--' . $boundary . '--' . $this->LE;
- }
-
- /**
- * Set the message type.
- * PHPMailer only supports some preset message types,
- * not arbitrary MIME structures.
- * @access protected
- * @return void
- */
- protected function setMessageType()
- {
- $type = array();
- if ($this->alternativeExists()) {
- $type[] = 'alt';
- }
- if ($this->inlineImageExists()) {
- $type[] = 'inline';
- }
- if ($this->attachmentExists()) {
- $type[] = 'attach';
- }
- $this->message_type = implode('_', $type);
- if ($this->message_type == '') {
- $this->message_type = 'plain';
- }
- }
-
- /**
- * Format a header line.
- * @access public
- * @param string $name
- * @param string $value
- * @return string
- */
- public function headerLine($name, $value)
- {
- return $name . ': ' . $value . $this->LE;
- }
-
- /**
- * Return a formatted mail line.
- * @access public
- * @param string $value
- * @return string
- */
- public function textLine($value)
- {
- return $value . $this->LE;
- }
-
- /**
- * Add an attachment from a path on the filesystem.
- * Returns false if the file could not be found or read.
- * @param string $path Path to the attachment.
- * @param string $name Overrides the attachment name.
- * @param string $encoding File encoding (see $Encoding).
- * @param string $type File extension (MIME) type.
- * @param string $disposition Disposition to use
- * @throws phpmailerException
- * @return boolean
- */
- public function addAttachment($path, $name = '', $encoding = 'base64', $type = '', $disposition = 'attachment')
- {
- try {
- if (!@is_file($path)) {
- throw new phpmailerException($this->lang('file_access') . $path, self::STOP_CONTINUE);
- }
-
- // If a MIME type is not specified, try to work it out from the file name
- if ($type == '') {
- $type = self::filenameToType($path);
- }
-
- $filename = basename($path);
- if ($name == '') {
- $name = $filename;
- }
-
- $this->attachment[] = array(
- 0 => $path,
- 1 => $filename,
- 2 => $name,
- 3 => $encoding,
- 4 => $type,
- 5 => false, // isStringAttachment
- 6 => $disposition,
- 7 => 0
- );
-
- } catch (phpmailerException $exc) {
- $this->setError($exc->getMessage());
- $this->edebug($exc->getMessage());
- if ($this->exceptions) {
- throw $exc;
- }
- return false;
- }
- return true;
- }
-
- /**
- * Return the array of attachments.
- * @return array
- */
- public function getAttachments()
- {
- return $this->attachment;
- }
-
- /**
- * Attach all file, string, and binary attachments to the message.
- * Returns an empty string on failure.
- * @access protected
- * @param string $disposition_type
- * @param string $boundary
- * @return string
- */
- protected function attachAll($disposition_type, $boundary)
- {
- // Return text of body
- $mime = array();
- $cidUniq = array();
- $incl = array();
-
- // Add all attachments
- foreach ($this->attachment as $attachment) {
- // Check if it is a valid disposition_filter
- if ($attachment[6] == $disposition_type) {
- // Check for string attachment
- $string = '';
- $path = '';
- $bString = $attachment[5];
- if ($bString) {
- $string = $attachment[0];
- } else {
- $path = $attachment[0];
- }
-
- $inclhash = md5(serialize($attachment));
- if (in_array($inclhash, $incl)) {
- continue;
- }
- $incl[] = $inclhash;
- $name = $attachment[2];
- $encoding = $attachment[3];
- $type = $attachment[4];
- $disposition = $attachment[6];
- $cid = $attachment[7];
- if ($disposition == 'inline' && array_key_exists($cid, $cidUniq)) {
- continue;
- }
- $cidUniq[$cid] = true;
-
- $mime[] = sprintf('--%s%s', $boundary, $this->LE);
- //Only include a filename property if we have one
- if (!empty($name)) {
- $mime[] = sprintf(
- 'Content-Type: %s; name="%s"%s',
- $type,
- $this->encodeHeader($this->secureHeader($name)),
- $this->LE
- );
- } else {
- $mime[] = sprintf(
- 'Content-Type: %s%s',
- $type,
- $this->LE
- );
- }
- // RFC1341 part 5 says 7bit is assumed if not specified
- if ($encoding != '7bit') {
- $mime[] = sprintf('Content-Transfer-Encoding: %s%s', $encoding, $this->LE);
- }
-
- if ($disposition == 'inline') {
- $mime[] = sprintf('Content-ID: <%s>%s', $cid, $this->LE);
- }
-
- // If a filename contains any of these chars, it should be quoted,
- // but not otherwise: RFC2183 & RFC2045 5.1
- // Fixes a warning in IETF's msglint MIME checker
- // Allow for bypassing the Content-Disposition header totally
- if (!(empty($disposition))) {
- $encoded_name = $this->encodeHeader($this->secureHeader($name));
- if (preg_match('/[ \(\)<>@,;:\\"\/\[\]\?=]/', $encoded_name)) {
- $mime[] = sprintf(
- 'Content-Disposition: %s; filename="%s"%s',
- $disposition,
- $encoded_name,
- $this->LE . $this->LE
- );
- } else {
- if (!empty($encoded_name)) {
- $mime[] = sprintf(
- 'Content-Disposition: %s; filename=%s%s',
- $disposition,
- $encoded_name,
- $this->LE . $this->LE
- );
- } else {
- $mime[] = sprintf(
- 'Content-Disposition: %s%s',
- $disposition,
- $this->LE . $this->LE
- );
- }
- }
- } else {
- $mime[] = $this->LE;
- }
-
- // Encode as string attachment
- if ($bString) {
- $mime[] = $this->encodeString($string, $encoding);
- if ($this->isError()) {
- return '';
- }
- $mime[] = $this->LE . $this->LE;
- } else {
- $mime[] = $this->encodeFile($path, $encoding);
- if ($this->isError()) {
- return '';
- }
- $mime[] = $this->LE . $this->LE;
- }
- }
- }
-
- $mime[] = sprintf('--%s--%s', $boundary, $this->LE);
-
- return implode('', $mime);
- }
-
- /**
- * Encode a file attachment in requested format.
- * Returns an empty string on failure.
- * @param string $path The full path to the file
- * @param string $encoding The encoding to use; one of 'base64', '7bit', '8bit', 'binary', 'quoted-printable'
- * @throws phpmailerException
- * @access protected
- * @return string
- */
- protected function encodeFile($path, $encoding = 'base64')
- {
- try {
- if (!is_readable($path)) {
- throw new phpmailerException($this->lang('file_open') . $path, self::STOP_CONTINUE);
- }
- $magic_quotes = get_magic_quotes_runtime();
- if ($magic_quotes) {
- if (version_compare(PHP_VERSION, '5.3.0', '<')) {
- set_magic_quotes_runtime(false);
- } else {
- //Doesn't exist in PHP 5.4, but we don't need to check because
- //get_magic_quotes_runtime always returns false in 5.4+
- //so it will never get here
- ini_set('magic_quotes_runtime', false);
- }
- }
- $file_buffer = file_get_contents($path);
- $file_buffer = $this->encodeString($file_buffer, $encoding);
- if ($magic_quotes) {
- if (version_compare(PHP_VERSION, '5.3.0', '<')) {
- set_magic_quotes_runtime($magic_quotes);
- } else {
- ini_set('magic_quotes_runtime', $magic_quotes);
- }
- }
- return $file_buffer;
- } catch (Exception $exc) {
- $this->setError($exc->getMessage());
- return '';
- }
- }
-
- /**
- * Encode a string in requested format.
- * Returns an empty string on failure.
- * @param string $str The text to encode
- * @param string $encoding The encoding to use; one of 'base64', '7bit', '8bit', 'binary', 'quoted-printable'
- * @access public
- * @return string
- */
- public function encodeString($str, $encoding = 'base64')
- {
- $encoded = '';
- switch (strtolower($encoding)) {
- case 'base64':
- $encoded = chunk_split(base64_encode($str), 76, $this->LE);
- break;
- case '7bit':
- case '8bit':
- $encoded = $this->fixEOL($str);
- // Make sure it ends with a line break
- if (substr($encoded, -(strlen($this->LE))) != $this->LE) {
- $encoded .= $this->LE;
- }
- break;
- case 'binary':
- $encoded = $str;
- break;
- case 'quoted-printable':
- $encoded = $this->encodeQP($str);
- break;
- default:
- $this->setError($this->lang('encoding') . $encoding);
- break;
- }
- return $encoded;
- }
-
- /**
- * Encode a header string optimally.
- * Picks shortest of Q, B, quoted-printable or none.
- * @access public
- * @param string $str
- * @param string $position
- * @return string
- */
- public function encodeHeader($str, $position = 'text')
- {
- $matchcount = 0;
- switch (strtolower($position)) {
- case 'phrase':
- if (!preg_match('/[\200-\377]/', $str)) {
- // Can't use addslashes as we don't know the value of magic_quotes_sybase
- $encoded = addcslashes($str, "\0..\37\177\\\"");
- if (($str == $encoded) && !preg_match('/[^A-Za-z0-9!#$%&\'*+\/=?^_`{|}~ -]/', $str)) {
- return ($encoded);
- } else {
- return ("\"$encoded\"");
- }
- }
- $matchcount = preg_match_all('/[^\040\041\043-\133\135-\176]/', $str, $matches);
- break;
- /** @noinspection PhpMissingBreakStatementInspection */
- case 'comment':
- $matchcount = preg_match_all('/[()"]/', $str, $matches);
- // Intentional fall-through
- case 'text':
- default:
- $matchcount += preg_match_all('/[\000-\010\013\014\016-\037\177-\377]/', $str, $matches);
- break;
- }
-
- //There are no chars that need encoding
- if ($matchcount == 0) {
- return ($str);
- }
-
- $maxlen = 75 - 7 - strlen($this->CharSet);
- // Try to select the encoding which should produce the shortest output
- if ($matchcount > strlen($str) / 3) {
- // More than a third of the content will need encoding, so B encoding will be most efficient
- $encoding = 'B';
- if (function_exists('mb_strlen') && $this->hasMultiBytes($str)) {
- // Use a custom function which correctly encodes and wraps long
- // multibyte strings without breaking lines within a character
- $encoded = $this->base64EncodeWrapMB($str, "\n");
- } else {
- $encoded = base64_encode($str);
- $maxlen -= $maxlen % 4;
- $encoded = trim(chunk_split($encoded, $maxlen, "\n"));
- }
- } else {
- $encoding = 'Q';
- $encoded = $this->encodeQ($str, $position);
- $encoded = $this->wrapText($encoded, $maxlen, true);
- $encoded = str_replace('=' . self::CRLF, "\n", trim($encoded));
- }
-
- $encoded = preg_replace('/^(.*)$/m', ' =?' . $this->CharSet . "?$encoding?\\1?=", $encoded);
- $encoded = trim(str_replace("\n", $this->LE, $encoded));
-
- return $encoded;
- }
-
- /**
- * Check if a string contains multi-byte characters.
- * @access public
- * @param string $str multi-byte text to wrap encode
- * @return boolean
- */
- public function hasMultiBytes($str)
- {
- if (function_exists('mb_strlen')) {
- return (strlen($str) > mb_strlen($str, $this->CharSet));
- } else { // Assume no multibytes (we can't handle without mbstring functions anyway)
- return false;
- }
- }
-
- /**
- * Does a string contain any 8-bit chars (in any charset)?
- * @param string $text
- * @return boolean
- */
- public function has8bitChars($text)
- {
- return (boolean)preg_match('/[\x80-\xFF]/', $text);
- }
-
- /**
- * Encode and wrap long multibyte strings for mail headers
- * without breaking lines within a character.
- * Adapted from a function by paravoid
- * @link http://www.php.net/manual/en/function.mb-encode-mimeheader.php#60283
- * @access public
- * @param string $str multi-byte text to wrap encode
- * @param string $linebreak string to use as linefeed/end-of-line
- * @return string
- */
- public function base64EncodeWrapMB($str, $linebreak = null)
- {
- $start = '=?' . $this->CharSet . '?B?';
- $end = '?=';
- $encoded = '';
- if ($linebreak === null) {
- $linebreak = $this->LE;
- }
-
- $mb_length = mb_strlen($str, $this->CharSet);
- // Each line must have length <= 75, including $start and $end
- $length = 75 - strlen($start) - strlen($end);
- // Average multi-byte ratio
- $ratio = $mb_length / strlen($str);
- // Base64 has a 4:3 ratio
- $avgLength = floor($length * $ratio * .75);
-
- for ($i = 0; $i < $mb_length; $i += $offset) {
- $lookBack = 0;
- do {
- $offset = $avgLength - $lookBack;
- $chunk = mb_substr($str, $i, $offset, $this->CharSet);
- $chunk = base64_encode($chunk);
- $lookBack++;
- } while (strlen($chunk) > $length);
- $encoded .= $chunk . $linebreak;
- }
-
- // Chomp the last linefeed
- $encoded = substr($encoded, 0, -strlen($linebreak));
- return $encoded;
- }
-
- /**
- * Encode a string in quoted-printable format.
- * According to RFC2045 section 6.7.
- * @access public
- * @param string $string The text to encode
- * @param integer $line_max Number of chars allowed on a line before wrapping
- * @return string
- * @link http://www.php.net/manual/en/function.quoted-printable-decode.php#89417 Adapted from this comment
- */
- public function encodeQP($string, $line_max = 76)
- {
- // Use native function if it's available (>= PHP5.3)
- if (function_exists('quoted_printable_encode')) {
- return quoted_printable_encode($string);
- }
- // Fall back to a pure PHP implementation
- $string = str_replace(
- array('%20', '%0D%0A.', '%0D%0A', '%'),
- array(' ', "\r\n=2E", "\r\n", '='),
- rawurlencode($string)
- );
- return preg_replace('/[^\r\n]{' . ($line_max - 3) . '}[^=\r\n]{2}/', "$0=\r\n", $string);
- }
-
- /**
- * Backward compatibility wrapper for an old QP encoding function that was removed.
- * @see PHPMailer::encodeQP()
- * @access public
- * @param string $string
- * @param integer $line_max
- * @param boolean $space_conv
- * @return string
- * @deprecated Use encodeQP instead.
- */
- public function encodeQPphp(
- $string,
- $line_max = 76,
- /** @noinspection PhpUnusedParameterInspection */ $space_conv = false
- ) {
- return $this->encodeQP($string, $line_max);
- }
-
- /**
- * Encode a string using Q encoding.
- * @link http://tools.ietf.org/html/rfc2047
- * @param string $str the text to encode
- * @param string $position Where the text is going to be used, see the RFC for what that means
- * @access public
- * @return string
- */
- public function encodeQ($str, $position = 'text')
- {
- // There should not be any EOL in the string
- $pattern = '';
- $encoded = str_replace(array("\r", "\n"), '', $str);
- switch (strtolower($position)) {
- case 'phrase':
- // RFC 2047 section 5.3
- $pattern = '^A-Za-z0-9!*+\/ -';
- break;
- /** @noinspection PhpMissingBreakStatementInspection */
- case 'comment':
- // RFC 2047 section 5.2
- $pattern = '\(\)"';
- // intentional fall-through
- // for this reason we build the $pattern without including delimiters and []
- case 'text':
- default:
- // RFC 2047 section 5.1
- // Replace every high ascii, control, =, ? and _ characters
- $pattern = '\000-\011\013\014\016-\037\075\077\137\177-\377' . $pattern;
- break;
- }
- $matches = array();
- if (preg_match_all("/[{$pattern}]/", $encoded, $matches)) {
- // If the string contains an '=', make sure it's the first thing we replace
- // so as to avoid double-encoding
- $eqkey = array_search('=', $matches[0]);
- if (false !== $eqkey) {
- unset($matches[0][$eqkey]);
- array_unshift($matches[0], '=');
- }
- foreach (array_unique($matches[0]) as $char) {
- $encoded = str_replace($char, '=' . sprintf('%02X', ord($char)), $encoded);
- }
- }
- // Replace every spaces to _ (more readable than =20)
- return str_replace(' ', '_', $encoded);
- }
-
- /**
- * Add a string or binary attachment (non-filesystem).
- * This method can be used to attach ascii or binary data,
- * such as a BLOB record from a database.
- * @param string $string String attachment data.
- * @param string $filename Name of the attachment.
- * @param string $encoding File encoding (see $Encoding).
- * @param string $type File extension (MIME) type.
- * @param string $disposition Disposition to use
- * @return void
- */
- public function addStringAttachment(
- $string,
- $filename,
- $encoding = 'base64',
- $type = '',
- $disposition = 'attachment'
- ) {
- // If a MIME type is not specified, try to work it out from the file name
- if ($type == '') {
- $type = self::filenameToType($filename);
- }
- // Append to $attachment array
- $this->attachment[] = array(
- 0 => $string,
- 1 => $filename,
- 2 => basename($filename),
- 3 => $encoding,
- 4 => $type,
- 5 => true, // isStringAttachment
- 6 => $disposition,
- 7 => 0
- );
- }
-
- /**
- * Add an embedded (inline) attachment from a file.
- * This can include images, sounds, and just about any other document type.
- * These differ from 'regular' attachments in that they are intended to be
- * displayed inline with the message, not just attached for download.
- * This is used in HTML messages that embed the images
- * the HTML refers to using the $cid value.
- * @param string $path Path to the attachment.
- * @param string $cid Content ID of the attachment; Use this to reference
- * the content when using an embedded image in HTML.
- * @param string $name Overrides the attachment name.
- * @param string $encoding File encoding (see $Encoding).
- * @param string $type File MIME type.
- * @param string $disposition Disposition to use
- * @return boolean True on successfully adding an attachment
- */
- public function addEmbeddedImage($path, $cid, $name = '', $encoding = 'base64', $type = '', $disposition = 'inline')
- {
- if (!@is_file($path)) {
- $this->setError($this->lang('file_access') . $path);
- return false;
- }
-
- // If a MIME type is not specified, try to work it out from the file name
- if ($type == '') {
- $type = self::filenameToType($path);
- }
-
- $filename = basename($path);
- if ($name == '') {
- $name = $filename;
- }
-
- // Append to $attachment array
- $this->attachment[] = array(
- 0 => $path,
- 1 => $filename,
- 2 => $name,
- 3 => $encoding,
- 4 => $type,
- 5 => false, // isStringAttachment
- 6 => $disposition,
- 7 => $cid
- );
- return true;
- }
-
- /**
- * Add an embedded stringified attachment.
- * This can include images, sounds, and just about any other document type.
- * Be sure to set the $type to an image type for images:
- * JPEG images use 'image/jpeg', GIF uses 'image/gif', PNG uses 'image/png'.
- * @param string $string The attachment binary data.
- * @param string $cid Content ID of the attachment; Use this to reference
- * the content when using an embedded image in HTML.
- * @param string $name
- * @param string $encoding File encoding (see $Encoding).
- * @param string $type MIME type.
- * @param string $disposition Disposition to use
- * @return boolean True on successfully adding an attachment
- */
- public function addStringEmbeddedImage(
- $string,
- $cid,
- $name = '',
- $encoding = 'base64',
- $type = '',
- $disposition = 'inline'
- ) {
- // If a MIME type is not specified, try to work it out from the name
- if ($type == '' and !empty($name)) {
- $type = self::filenameToType($name);
- }
-
- // Append to $attachment array
- $this->attachment[] = array(
- 0 => $string,
- 1 => $name,
- 2 => $name,
- 3 => $encoding,
- 4 => $type,
- 5 => true, // isStringAttachment
- 6 => $disposition,
- 7 => $cid
- );
- return true;
- }
-
- /**
- * Check if an inline attachment is present.
- * @access public
- * @return boolean
- */
- public function inlineImageExists()
- {
- foreach ($this->attachment as $attachment) {
- if ($attachment[6] == 'inline') {
- return true;
- }
- }
- return false;
- }
-
- /**
- * Check if an attachment (non-inline) is present.
- * @return boolean
- */
- public function attachmentExists()
- {
- foreach ($this->attachment as $attachment) {
- if ($attachment[6] == 'attachment') {
- return true;
- }
- }
- return false;
- }
-
- /**
- * Check if this message has an alternative body set.
- * @return boolean
- */
- public function alternativeExists()
- {
- return !empty($this->AltBody);
- }
-
- /**
- * Clear queued addresses of given kind.
- * @access protected
- * @param string $kind 'to', 'cc', or 'bcc'
- * @return void
- */
- public function clearQueuedAddresses($kind)
- {
- $RecipientsQueue = $this->RecipientsQueue;
- foreach ($RecipientsQueue as $address => $params) {
- if ($params[0] == $kind) {
- unset($this->RecipientsQueue[$address]);
- }
- }
- }
-
- /**
- * Clear all To recipients.
- * @return void
- */
- public function clearAddresses()
- {
- foreach ($this->to as $to) {
- unset($this->all_recipients[strtolower($to[0])]);
- }
- $this->to = array();
- $this->clearQueuedAddresses('to');
- }
-
- /**
- * Clear all CC recipients.
- * @return void
- */
- public function clearCCs()
- {
- foreach ($this->cc as $cc) {
- unset($this->all_recipients[strtolower($cc[0])]);
- }
- $this->cc = array();
- $this->clearQueuedAddresses('cc');
- }
-
- /**
- * Clear all BCC recipients.
- * @return void
- */
- public function clearBCCs()
- {
- foreach ($this->bcc as $bcc) {
- unset($this->all_recipients[strtolower($bcc[0])]);
- }
- $this->bcc = array();
- $this->clearQueuedAddresses('bcc');
- }
-
- /**
- * Clear all ReplyTo recipients.
- * @return void
- */
- public function clearReplyTos()
- {
- $this->ReplyTo = array();
- $this->ReplyToQueue = array();
- }
-
- /**
- * Clear all recipient types.
- * @return void
- */
- public function clearAllRecipients()
- {
- $this->to = array();
- $this->cc = array();
- $this->bcc = array();
- $this->all_recipients = array();
- $this->RecipientsQueue = array();
- }
-
- /**
- * Clear all filesystem, string, and binary attachments.
- * @return void
- */
- public function clearAttachments()
- {
- $this->attachment = array();
- }
-
- /**
- * Clear all custom headers.
- * @return void
- */
- public function clearCustomHeaders()
- {
- $this->CustomHeader = array();
- }
-
- /**
- * Add an error message to the error container.
- * @access protected
- * @param string $msg
- * @return void
- */
- protected function setError($msg)
- {
- $this->error_count++;
- if ($this->Mailer == 'smtp' and !is_null($this->smtp)) {
- $lasterror = $this->smtp->getError();
- if (!empty($lasterror['error'])) {
- $msg .= $this->lang('smtp_error') . $lasterror['error'];
- if (!empty($lasterror['detail'])) {
- $msg .= ' Detail: '. $lasterror['detail'];
- }
- if (!empty($lasterror['smtp_code'])) {
- $msg .= ' SMTP code: ' . $lasterror['smtp_code'];
- }
- if (!empty($lasterror['smtp_code_ex'])) {
- $msg .= ' Additional SMTP info: ' . $lasterror['smtp_code_ex'];
- }
- }
- }
- $this->ErrorInfo = $msg;
- }
-
- /**
- * Return an RFC 822 formatted date.
- * @access public
- * @return string
- * @static
- */
- public static function rfcDate()
- {
- // Set the time zone to whatever the default is to avoid 500 errors
- // Will default to UTC if it's not set properly in php.ini
- date_default_timezone_set(@date_default_timezone_get());
- return date('D, j M Y H:i:s O');
- }
-
- /**
- * Get the server hostname.
- * Returns 'localhost.localdomain' if unknown.
- * @access protected
- * @return string
- */
- protected function serverHostname()
- {
- $result = 'localhost.localdomain';
- if (!empty($this->Hostname)) {
- $result = $this->Hostname;
- } elseif (isset($_SERVER) and array_key_exists('SERVER_NAME', $_SERVER) and !empty($_SERVER['SERVER_NAME'])) {
- $result = $_SERVER['SERVER_NAME'];
- } elseif (function_exists('gethostname') && gethostname() !== false) {
- $result = gethostname();
- } elseif (php_uname('n') !== false) {
- $result = php_uname('n');
- }
- return $result;
- }
-
- /**
- * Get an error message in the current language.
- * @access protected
- * @param string $key
- * @return string
- */
- protected function lang($key)
- {
- if (count($this->language) < 1) {
- $this->setLanguage('en'); // set the default language
- }
-
- if (array_key_exists($key, $this->language)) {
- if ($key == 'smtp_connect_failed') {
- //Include a link to troubleshooting docs on SMTP connection failure
- //this is by far the biggest cause of support questions
- //but it's usually not PHPMailer's fault.
- return $this->language[$key] . ' https://github.com/PHPMailer/PHPMailer/wiki/Troubleshooting';
- }
- return $this->language[$key];
- } else {
- //Return the key as a fallback
- return $key;
- }
- }
-
- /**
- * Check if an error occurred.
- * @access public
- * @return boolean True if an error did occur.
- */
- public function isError()
- {
- return ($this->error_count > 0);
- }
-
- /**
- * Ensure consistent line endings in a string.
- * Changes every end of line from CRLF, CR or LF to $this->LE.
- * @access public
- * @param string $str String to fixEOL
- * @return string
- */
- public function fixEOL($str)
- {
- // Normalise to \n
- $nstr = str_replace(array("\r\n", "\r"), "\n", $str);
- // Now convert LE as needed
- if ($this->LE !== "\n") {
- $nstr = str_replace("\n", $this->LE, $nstr);
- }
- return $nstr;
- }
-
- /**
- * Add a custom header.
- * $name value can be overloaded to contain
- * both header name and value (name:value)
- * @access public
- * @param string $name Custom header name
- * @param string $value Header value
- * @return void
- */
- public function addCustomHeader($name, $value = null)
- {
- if ($value === null) {
- // Value passed in as name:value
- $this->CustomHeader[] = explode(':', $name, 2);
- } else {
- $this->CustomHeader[] = array($name, $value);
- }
- }
-
- /**
- * Returns all custom headers.
- * @return array
- */
- public function getCustomHeaders()
- {
- return $this->CustomHeader;
- }
-
- /**
- * Create a message from an HTML string.
- * Automatically makes modifications for inline images and backgrounds
- * and creates a plain-text version by converting the HTML.
- * Overwrites any existing values in $this->Body and $this->AltBody
- * @access public
- * @param string $message HTML message string
- * @param string $basedir baseline directory for path
- * @param boolean|callable $advanced Whether to use the internal HTML to text converter
- * or your own custom converter @see PHPMailer::html2text()
- * @return string $message
- */
- public function msgHTML($message, $basedir = '', $advanced = false)
- {
- preg_match_all('/(src|background)=["\'](.*)["\']/Ui', $message, $images);
- if (array_key_exists(2, $images)) {
- foreach ($images[2] as $imgindex => $url) {
- // Convert data URIs into embedded images
- if (preg_match('#^data:(image[^;,]*)(;base64)?,#', $url, $match)) {
- $data = substr($url, strpos($url, ','));
- if ($match[2]) {
- $data = base64_decode($data);
- } else {
- $data = rawurldecode($data);
- }
- $cid = md5($url) . '@phpmailer.0'; // RFC2392 S 2
- if ($this->addStringEmbeddedImage($data, $cid, 'embed' . $imgindex, 'base64', $match[1])) {
- $message = str_replace(
- $images[0][$imgindex],
- $images[1][$imgindex] . '="cid:' . $cid . '"',
- $message
- );
- }
- } elseif (substr($url, 0, 4) !== 'cid:' && !preg_match('#^[A-z]+://#', $url)) {
- // Do not change urls for absolute images (thanks to corvuscorax)
- // Do not change urls that are already inline images
- $filename = basename($url);
- $directory = dirname($url);
- if ($directory == '.') {
- $directory = '';
- }
- $cid = md5($url) . '@phpmailer.0'; // RFC2392 S 2
- if (strlen($basedir) > 1 && substr($basedir, -1) != '/') {
- $basedir .= '/';
- }
- if (strlen($directory) > 1 && substr($directory, -1) != '/') {
- $directory .= '/';
- }
- if ($this->addEmbeddedImage(
- $basedir . $directory . $filename,
- $cid,
- $filename,
- 'base64',
- self::_mime_types((string)self::mb_pathinfo($filename, PATHINFO_EXTENSION))
- )
- ) {
- $message = preg_replace(
- '/' . $images[1][$imgindex] . '=["\']' . preg_quote($url, '/') . '["\']/Ui',
- $images[1][$imgindex] . '="cid:' . $cid . '"',
- $message
- );
- }
- }
- }
- }
- $this->isHTML(true);
- // Convert all message body line breaks to CRLF, makes quoted-printable encoding work much better
- $this->Body = $this->normalizeBreaks($message);
- $this->AltBody = $this->normalizeBreaks($this->html2text($message, $advanced));
- if (!$this->alternativeExists()) {
- $this->AltBody = 'To view this email message, open it in a program that understands HTML!' .
- self::CRLF . self::CRLF;
- }
- return $this->Body;
- }
-
- /**
- * Convert an HTML string into plain text.
- * This is used by msgHTML().
- * Note - older versions of this function used a bundled advanced converter
- * which was been removed for license reasons in #232
- * Example usage:
- *
- * // Use default conversion
- * $plain = $mail->html2text($html);
- * // Use your own custom converter
- * $plain = $mail->html2text($html, function($html) {
- * $converter = new MyHtml2text($html);
- * return $converter->get_text();
- * });
- *
- * @param string $html The HTML text to convert
- * @param boolean|callable $advanced Any boolean value to use the internal converter,
- * or provide your own callable for custom conversion.
- * @return string
- */
- public function html2text($html, $advanced = false)
- {
- if (is_callable($advanced)) {
- return call_user_func($advanced, $html);
- }
- return html_entity_decode(
- trim(strip_tags(preg_replace('/<(head|title|style|script)[^>]*>.*?<\/\\1>/si', '', $html))),
- ENT_QUOTES,
- $this->CharSet
- );
- }
-
- /**
- * Get the MIME type for a file extension.
- * @param string $ext File extension
- * @access public
- * @return string MIME type of file.
- * @static
- */
- public static function _mime_types($ext = '')
- {
- $mimes = array(
- 'xl' => 'application/excel',
- 'js' => 'application/javascript',
- 'hqx' => 'application/mac-binhex40',
- 'cpt' => 'application/mac-compactpro',
- 'bin' => 'application/macbinary',
- 'doc' => 'application/msword',
- 'word' => 'application/msword',
- 'xlsx' => 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
- 'xltx' => 'application/vnd.openxmlformats-officedocument.spreadsheetml.template',
- 'potx' => 'application/vnd.openxmlformats-officedocument.presentationml.template',
- 'ppsx' => 'application/vnd.openxmlformats-officedocument.presentationml.slideshow',
- 'pptx' => 'application/vnd.openxmlformats-officedocument.presentationml.presentation',
- 'sldx' => 'application/vnd.openxmlformats-officedocument.presentationml.slide',
- 'docx' => 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
- 'dotx' => 'application/vnd.openxmlformats-officedocument.wordprocessingml.template',
- 'xlam' => 'application/vnd.ms-excel.addin.macroEnabled.12',
- 'xlsb' => 'application/vnd.ms-excel.sheet.binary.macroEnabled.12',
- 'class' => 'application/octet-stream',
- 'dll' => 'application/octet-stream',
- 'dms' => 'application/octet-stream',
- 'exe' => 'application/octet-stream',
- 'lha' => 'application/octet-stream',
- 'lzh' => 'application/octet-stream',
- 'psd' => 'application/octet-stream',
- 'sea' => 'application/octet-stream',
- 'so' => 'application/octet-stream',
- 'oda' => 'application/oda',
- 'pdf' => 'application/pdf',
- 'ai' => 'application/postscript',
- 'eps' => 'application/postscript',
- 'ps' => 'application/postscript',
- 'smi' => 'application/smil',
- 'smil' => 'application/smil',
- 'mif' => 'application/vnd.mif',
- 'xls' => 'application/vnd.ms-excel',
- 'ppt' => 'application/vnd.ms-powerpoint',
- 'wbxml' => 'application/vnd.wap.wbxml',
- 'wmlc' => 'application/vnd.wap.wmlc',
- 'dcr' => 'application/x-director',
- 'dir' => 'application/x-director',
- 'dxr' => 'application/x-director',
- 'dvi' => 'application/x-dvi',
- 'gtar' => 'application/x-gtar',
- 'php3' => 'application/x-httpd-php',
- 'php4' => 'application/x-httpd-php',
- 'php' => 'application/x-httpd-php',
- 'phtml' => 'application/x-httpd-php',
- 'phps' => 'application/x-httpd-php-source',
- 'swf' => 'application/x-shockwave-flash',
- 'sit' => 'application/x-stuffit',
- 'tar' => 'application/x-tar',
- 'tgz' => 'application/x-tar',
- 'xht' => 'application/xhtml+xml',
- 'xhtml' => 'application/xhtml+xml',
- 'zip' => 'application/zip',
- 'mid' => 'audio/midi',
- 'midi' => 'audio/midi',
- 'mp2' => 'audio/mpeg',
- 'mp3' => 'audio/mpeg',
- 'mpga' => 'audio/mpeg',
- 'aif' => 'audio/x-aiff',
- 'aifc' => 'audio/x-aiff',
- 'aiff' => 'audio/x-aiff',
- 'ram' => 'audio/x-pn-realaudio',
- 'rm' => 'audio/x-pn-realaudio',
- 'rpm' => 'audio/x-pn-realaudio-plugin',
- 'ra' => 'audio/x-realaudio',
- 'wav' => 'audio/x-wav',
- 'bmp' => 'image/bmp',
- 'gif' => 'image/gif',
- 'jpeg' => 'image/jpeg',
- 'jpe' => 'image/jpeg',
- 'jpg' => 'image/jpeg',
- 'png' => 'image/png',
- 'tiff' => 'image/tiff',
- 'tif' => 'image/tiff',
- 'eml' => 'message/rfc822',
- 'css' => 'text/css',
- 'html' => 'text/html',
- 'htm' => 'text/html',
- 'shtml' => 'text/html',
- 'log' => 'text/plain',
- 'text' => 'text/plain',
- 'txt' => 'text/plain',
- 'rtx' => 'text/richtext',
- 'rtf' => 'text/rtf',
- 'vcf' => 'text/vcard',
- 'vcard' => 'text/vcard',
- 'xml' => 'text/xml',
- 'xsl' => 'text/xml',
- 'mpeg' => 'video/mpeg',
- 'mpe' => 'video/mpeg',
- 'mpg' => 'video/mpeg',
- 'mov' => 'video/quicktime',
- 'qt' => 'video/quicktime',
- 'rv' => 'video/vnd.rn-realvideo',
- 'avi' => 'video/x-msvideo',
- 'movie' => 'video/x-sgi-movie'
- );
- if (array_key_exists(strtolower($ext), $mimes)) {
- return $mimes[strtolower($ext)];
- }
- return 'application/octet-stream';
- }
-
- /**
- * Map a file name to a MIME type.
- * Defaults to 'application/octet-stream', i.e.. arbitrary binary data.
- * @param string $filename A file name or full path, does not need to exist as a file
- * @return string
- * @static
- */
- public static function filenameToType($filename)
- {
- // In case the path is a URL, strip any query string before getting extension
- $qpos = strpos($filename, '?');
- if (false !== $qpos) {
- $filename = substr($filename, 0, $qpos);
- }
- $pathinfo = self::mb_pathinfo($filename);
- return self::_mime_types($pathinfo['extension']);
- }
-
- /**
- * Multi-byte-safe pathinfo replacement.
- * Drop-in replacement for pathinfo(), but multibyte-safe, cross-platform-safe, old-version-safe.
- * Works similarly to the one in PHP >= 5.2.0
- * @link http://www.php.net/manual/en/function.pathinfo.php#107461
- * @param string $path A filename or path, does not need to exist as a file
- * @param integer|string $options Either a PATHINFO_* constant,
- * or a string name to return only the specified piece, allows 'filename' to work on PHP < 5.2
- * @return string|array
- * @static
- */
- public static function mb_pathinfo($path, $options = null)
- {
- $ret = array('dirname' => '', 'basename' => '', 'extension' => '', 'filename' => '');
- $pathinfo = array();
- if (preg_match('%^(.*?)[\\\\/]*(([^/\\\\]*?)(\.([^\.\\\\/]+?)|))[\\\\/\.]*$%im', $path, $pathinfo)) {
- if (array_key_exists(1, $pathinfo)) {
- $ret['dirname'] = $pathinfo[1];
- }
- if (array_key_exists(2, $pathinfo)) {
- $ret['basename'] = $pathinfo[2];
- }
- if (array_key_exists(5, $pathinfo)) {
- $ret['extension'] = $pathinfo[5];
- }
- if (array_key_exists(3, $pathinfo)) {
- $ret['filename'] = $pathinfo[3];
- }
- }
- switch ($options) {
- case PATHINFO_DIRNAME:
- case 'dirname':
- return $ret['dirname'];
- case PATHINFO_BASENAME:
- case 'basename':
- return $ret['basename'];
- case PATHINFO_EXTENSION:
- case 'extension':
- return $ret['extension'];
- case PATHINFO_FILENAME:
- case 'filename':
- return $ret['filename'];
- default:
- return $ret;
- }
- }
-
- /**
- * Set or reset instance properties.
- * You should avoid this function - it's more verbose, less efficient, more error-prone and
- * harder to debug than setting properties directly.
- * Usage Example:
- * `$mail->set('SMTPSecure', 'tls');`
- * is the same as:
- * `$mail->SMTPSecure = 'tls';`
- * @access public
- * @param string $name The property name to set
- * @param mixed $value The value to set the property to
- * @return boolean
- * @TODO Should this not be using the __set() magic function?
- */
- public function set($name, $value = '')
- {
- if (property_exists($this, $name)) {
- $this->$name = $value;
- return true;
- } else {
- $this->setError($this->lang('variable_set') . $name);
- return false;
- }
- }
-
- /**
- * Strip newlines to prevent header injection.
- * @access public
- * @param string $str
- * @return string
- */
- public function secureHeader($str)
- {
- return trim(str_replace(array("\r", "\n"), '', $str));
- }
-
- /**
- * Normalize line breaks in a string.
- * Converts UNIX LF, Mac CR and Windows CRLF line breaks into a single line break format.
- * Defaults to CRLF (for message bodies) and preserves consecutive breaks.
- * @param string $text
- * @param string $breaktype What kind of line break to use, defaults to CRLF
- * @return string
- * @access public
- * @static
- */
- public static function normalizeBreaks($text, $breaktype = "\r\n")
- {
- return preg_replace('/(\r\n|\r|\n)/ms', $breaktype, $text);
- }
-
- /**
- * Set the public and private key files and password for S/MIME signing.
- * @access public
- * @param string $cert_filename
- * @param string $key_filename
- * @param string $key_pass Password for private key
- * @param string $extracerts_filename Optional path to chain certificate
- */
- public function sign($cert_filename, $key_filename, $key_pass, $extracerts_filename = '')
- {
- $this->sign_cert_file = $cert_filename;
- $this->sign_key_file = $key_filename;
- $this->sign_key_pass = $key_pass;
- $this->sign_extracerts_file = $extracerts_filename;
- }
-
- /**
- * Quoted-Printable-encode a DKIM header.
- * @access public
- * @param string $txt
- * @return string
- */
- public function DKIM_QP($txt)
- {
- $line = '';
- for ($i = 0; $i < strlen($txt); $i++) {
- $ord = ord($txt[$i]);
- if (((0x21 <= $ord) && ($ord <= 0x3A)) || $ord == 0x3C || ((0x3E <= $ord) && ($ord <= 0x7E))) {
- $line .= $txt[$i];
- } else {
- $line .= '=' . sprintf('%02X', $ord);
- }
- }
- return $line;
- }
-
- /**
- * Generate a DKIM signature.
- * @access public
- * @param string $signHeader
- * @throws phpmailerException
- * @return string
- */
- public function DKIM_Sign($signHeader)
- {
- if (!defined('PKCS7_TEXT')) {
- if ($this->exceptions) {
- throw new phpmailerException($this->lang('extension_missing') . 'openssl');
- }
- return '';
- }
- $privKeyStr = file_get_contents($this->DKIM_private);
- if ($this->DKIM_passphrase != '') {
- $privKey = openssl_pkey_get_private($privKeyStr, $this->DKIM_passphrase);
- } else {
- $privKey = $privKeyStr;
- }
- if (openssl_sign($signHeader, $signature, $privKey)) {
- return base64_encode($signature);
- }
- return '';
- }
-
- /**
- * Generate a DKIM canonicalization header.
- * @access public
- * @param string $signHeader Header
- * @return string
- */
- public function DKIM_HeaderC($signHeader)
- {
- $signHeader = preg_replace('/\r\n\s+/', ' ', $signHeader);
- $lines = explode("\r\n", $signHeader);
- foreach ($lines as $key => $line) {
- list($heading, $value) = explode(':', $line, 2);
- $heading = strtolower($heading);
- $value = preg_replace('/\s+/', ' ', $value); // Compress useless spaces
- $lines[$key] = $heading . ':' . trim($value); // Don't forget to remove WSP around the value
- }
- $signHeader = implode("\r\n", $lines);
- return $signHeader;
- }
-
- /**
- * Generate a DKIM canonicalization body.
- * @access public
- * @param string $body Message Body
- * @return string
- */
- public function DKIM_BodyC($body)
- {
- if ($body == '') {
- return "\r\n";
- }
- // stabilize line endings
- $body = str_replace("\r\n", "\n", $body);
- $body = str_replace("\n", "\r\n", $body);
- // END stabilize line endings
- while (substr($body, strlen($body) - 4, 4) == "\r\n\r\n") {
- $body = substr($body, 0, strlen($body) - 2);
- }
- return $body;
- }
-
- /**
- * Create the DKIM header and body in a new message header.
- * @access public
- * @param string $headers_line Header lines
- * @param string $subject Subject
- * @param string $body Body
- * @return string
- */
- public function DKIM_Add($headers_line, $subject, $body)
- {
- $DKIMsignatureType = 'rsa-sha1'; // Signature & hash algorithms
- $DKIMcanonicalization = 'relaxed/simple'; // Canonicalization of header/body
- $DKIMquery = 'dns/txt'; // Query method
- $DKIMtime = time(); // Signature Timestamp = seconds since 00:00:00 - Jan 1, 1970 (UTC time zone)
- $subject_header = "Subject: $subject";
- $headers = explode($this->LE, $headers_line);
- $from_header = '';
- $to_header = '';
- $current = '';
- foreach ($headers as $header) {
- if (strpos($header, 'From:') === 0) {
- $from_header = $header;
- $current = 'from_header';
- } elseif (strpos($header, 'To:') === 0) {
- $to_header = $header;
- $current = 'to_header';
- } else {
- if (!empty($$current) && strpos($header, ' =?') === 0) {
- $$current .= $header;
- } else {
- $current = '';
- }
- }
- }
- $from = str_replace('|', '=7C', $this->DKIM_QP($from_header));
- $to = str_replace('|', '=7C', $this->DKIM_QP($to_header));
- $subject = str_replace(
- '|',
- '=7C',
- $this->DKIM_QP($subject_header)
- ); // Copied header fields (dkim-quoted-printable)
- $body = $this->DKIM_BodyC($body);
- $DKIMlen = strlen($body); // Length of body
- $DKIMb64 = base64_encode(pack('H*', sha1($body))); // Base64 of packed binary SHA-1 hash of body
- if ('' == $this->DKIM_identity) {
- $ident = '';
- } else {
- $ident = ' i=' . $this->DKIM_identity . ';';
- }
- $dkimhdrs = 'DKIM-Signature: v=1; a=' .
- $DKIMsignatureType . '; q=' .
- $DKIMquery . '; l=' .
- $DKIMlen . '; s=' .
- $this->DKIM_selector .
- ";\r\n" .
- "\tt=" . $DKIMtime . '; c=' . $DKIMcanonicalization . ";\r\n" .
- "\th=From:To:Subject;\r\n" .
- "\td=" . $this->DKIM_domain . ';' . $ident . "\r\n" .
- "\tz=$from\r\n" .
- "\t|$to\r\n" .
- "\t|$subject;\r\n" .
- "\tbh=" . $DKIMb64 . ";\r\n" .
- "\tb=";
- $toSign = $this->DKIM_HeaderC(
- $from_header . "\r\n" .
- $to_header . "\r\n" .
- $subject_header . "\r\n" .
- $dkimhdrs
- );
- $signed = $this->DKIM_Sign($toSign);
- return $dkimhdrs . $signed . "\r\n";
- }
-
- /**
- * Detect if a string contains a line longer than the maximum line length allowed.
- * @param string $str
- * @return boolean
- * @static
- */
- public static function hasLineLongerThanMax($str)
- {
- //+2 to include CRLF line break for a 1000 total
- return (boolean)preg_match('/^(.{'.(self::MAX_LINE_LENGTH + 2).',})/m', $str);
- }
-
- /**
- * Allows for public read access to 'to' property.
- * @note: Before the send() call, queued addresses (i.e. with IDN) are not yet included.
- * @access public
- * @return array
- */
- public function getToAddresses()
- {
- return $this->to;
- }
-
- /**
- * Allows for public read access to 'cc' property.
- * @note: Before the send() call, queued addresses (i.e. with IDN) are not yet included.
- * @access public
- * @return array
- */
- public function getCcAddresses()
- {
- return $this->cc;
- }
-
- /**
- * Allows for public read access to 'bcc' property.
- * @note: Before the send() call, queued addresses (i.e. with IDN) are not yet included.
- * @access public
- * @return array
- */
- public function getBccAddresses()
- {
- return $this->bcc;
- }
-
- /**
- * Allows for public read access to 'ReplyTo' property.
- * @note: Before the send() call, queued addresses (i.e. with IDN) are not yet included.
- * @access public
- * @return array
- */
- public function getReplyToAddresses()
- {
- return $this->ReplyTo;
- }
-
- /**
- * Allows for public read access to 'all_recipients' property.
- * @note: Before the send() call, queued addresses (i.e. with IDN) are not yet included.
- * @access public
- * @return array
- */
- public function getAllRecipientAddresses()
- {
- return $this->all_recipients;
- }
-
- /**
- * Perform a callback.
- * @param boolean $isSent
- * @param array $to
- * @param array $cc
- * @param array $bcc
- * @param string $subject
- * @param string $body
- * @param string $from
- */
- protected function doCallback($isSent, $to, $cc, $bcc, $subject, $body, $from)
- {
- if (!empty($this->action_function) && is_callable($this->action_function)) {
- $params = array($isSent, $to, $cc, $bcc, $subject, $body, $from);
- call_user_func_array($this->action_function, $params);
- }
- }
-}
-
-/**
- * PHPMailer exception handler
- * @package PHPMailer
- */
-class phpmailerException extends Exception
-{
- /**
- * Prettify error message output
- * @return string
- */
- public function errorMessage()
- {
- $errorMsg = '' . $this->getMessage() . " \n";
- return $errorMsg;
- }
-}
+
+ * @author Jim Jagielski (jimjag)
+ * @author Andy Prevost (codeworxtech)
+ * @author Brent R. Matzelle (original founder)
+ * @copyright 2012 - 2014 Marcus Bointon
+ * @copyright 2010 - 2012 Jim Jagielski
+ * @copyright 2004 - 2009 Andy Prevost
+ * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
+ * @note This program is distributed in the hope that it will be useful - WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
+/**
+ * PHPMailer - PHP email creation and transport class.
+ * @package PHPMailer
+ * @author Marcus Bointon (Synchro/coolbru)
+ * @author Jim Jagielski (jimjag)
+ * @author Andy Prevost (codeworxtech)
+ * @author Brent R. Matzelle (original founder)
+ */
+class PHPMailer
+{
+ /**
+ * The PHPMailer Version number.
+ * @var string
+ */
+ public $Version = '5.2.14';
+
+ /**
+ * Email priority.
+ * Options: null (default), 1 = High, 3 = Normal, 5 = low.
+ * When null, the header is not set at all.
+ * @var integer
+ */
+ public $Priority = null;
+
+ /**
+ * The character set of the message.
+ * @var string
+ */
+ public $CharSet = 'iso-8859-1';
+
+ /**
+ * The MIME Content-type of the message.
+ * @var string
+ */
+ public $ContentType = 'text/plain';
+
+ /**
+ * The message encoding.
+ * Options: "8bit", "7bit", "binary", "base64", and "quoted-printable".
+ * @var string
+ */
+ public $Encoding = '8bit';
+
+ /**
+ * Holds the most recent mailer error message.
+ * @var string
+ */
+ public $ErrorInfo = '';
+
+ /**
+ * The From email address for the message.
+ * @var string
+ */
+ public $From = 'root@localhost';
+
+ /**
+ * The From name of the message.
+ * @var string
+ */
+ public $FromName = 'Root User';
+
+ /**
+ * The Sender email (Return-Path) of the message.
+ * If not empty, will be sent via -f to sendmail or as 'MAIL FROM' in smtp mode.
+ * @var string
+ */
+ public $Sender = '';
+
+ /**
+ * The Return-Path of the message.
+ * If empty, it will be set to either From or Sender.
+ * @var string
+ * @deprecated Email senders should never set a return-path header;
+ * it's the receiver's job (RFC5321 section 4.4), so this no longer does anything.
+ * @link https://tools.ietf.org/html/rfc5321#section-4.4 RFC5321 reference
+ */
+ public $ReturnPath = '';
+
+ /**
+ * The Subject of the message.
+ * @var string
+ */
+ public $Subject = '';
+
+ /**
+ * An HTML or plain text message body.
+ * If HTML then call isHTML(true).
+ * @var string
+ */
+ public $Body = '';
+
+ /**
+ * The plain-text message body.
+ * This body can be read by mail clients that do not have HTML email
+ * capability such as mutt & Eudora.
+ * Clients that can read HTML will view the normal Body.
+ * @var string
+ */
+ public $AltBody = '';
+
+ /**
+ * An iCal message part body.
+ * Only supported in simple alt or alt_inline message types
+ * To generate iCal events, use the bundled extras/EasyPeasyICS.php class or iCalcreator
+ * @link http://sprain.ch/blog/downloads/php-class-easypeasyics-create-ical-files-with-php/
+ * @link http://kigkonsult.se/iCalcreator/
+ * @var string
+ */
+ public $Ical = '';
+
+ /**
+ * The complete compiled MIME message body.
+ * @access protected
+ * @var string
+ */
+ protected $MIMEBody = '';
+
+ /**
+ * The complete compiled MIME message headers.
+ * @var string
+ * @access protected
+ */
+ protected $MIMEHeader = '';
+
+ /**
+ * Extra headers that createHeader() doesn't fold in.
+ * @var string
+ * @access protected
+ */
+ protected $mailHeader = '';
+
+ /**
+ * Word-wrap the message body to this number of chars.
+ * Set to 0 to not wrap. A useful value here is 78, for RFC2822 section 2.1.1 compliance.
+ * @var integer
+ */
+ public $WordWrap = 0;
+
+ /**
+ * Which method to use to send mail.
+ * Options: "mail", "sendmail", or "smtp".
+ * @var string
+ */
+ public $Mailer = 'mail';
+
+ /**
+ * The path to the sendmail program.
+ * @var string
+ */
+ public $Sendmail = '/usr/sbin/sendmail';
+
+ /**
+ * Whether mail() uses a fully sendmail-compatible MTA.
+ * One which supports sendmail's "-oi -f" options.
+ * @var boolean
+ */
+ public $UseSendmailOptions = true;
+
+ /**
+ * Path to PHPMailer plugins.
+ * Useful if the SMTP class is not in the PHP include path.
+ * @var string
+ * @deprecated Should not be needed now there is an autoloader.
+ */
+ public $PluginDir = '';
+
+ /**
+ * The email address that a reading confirmation should be sent to, also known as read receipt.
+ * @var string
+ */
+ public $ConfirmReadingTo = '';
+
+ /**
+ * The hostname to use in the Message-ID header and as default HELO string.
+ * If empty, PHPMailer attempts to find one with, in order,
+ * $_SERVER['SERVER_NAME'], gethostname(), php_uname('n'), or the value
+ * 'localhost.localdomain'.
+ * @var string
+ */
+ public $Hostname = '';
+
+ /**
+ * An ID to be used in the Message-ID header.
+ * If empty, a unique id will be generated.
+ * @var string
+ */
+ public $MessageID = '';
+
+ /**
+ * The message Date to be used in the Date header.
+ * If empty, the current date will be added.
+ * @var string
+ */
+ public $MessageDate = '';
+
+ /**
+ * SMTP hosts.
+ * Either a single hostname or multiple semicolon-delimited hostnames.
+ * You can also specify a different port
+ * for each host by using this format: [hostname:port]
+ * (e.g. "smtp1.example.com:25;smtp2.example.com").
+ * You can also specify encryption type, for example:
+ * (e.g. "tls://smtp1.example.com:587;ssl://smtp2.example.com:465").
+ * Hosts will be tried in order.
+ * @var string
+ */
+ public $Host = 'localhost';
+
+ /**
+ * The default SMTP server port.
+ * @var integer
+ * @TODO Why is this needed when the SMTP class takes care of it?
+ */
+ public $Port = 25;
+
+ /**
+ * The SMTP HELO of the message.
+ * Default is $Hostname. If $Hostname is empty, PHPMailer attempts to find
+ * one with the same method described above for $Hostname.
+ * @var string
+ * @see PHPMailer::$Hostname
+ */
+ public $Helo = '';
+
+ /**
+ * What kind of encryption to use on the SMTP connection.
+ * Options: '', 'ssl' or 'tls'
+ * @var string
+ */
+ public $SMTPSecure = '';
+
+ /**
+ * Whether to enable TLS encryption automatically if a server supports it,
+ * even if `SMTPSecure` is not set to 'tls'.
+ * Be aware that in PHP >= 5.6 this requires that the server's certificates are valid.
+ * @var boolean
+ */
+ public $SMTPAutoTLS = true;
+
+ /**
+ * Whether to use SMTP authentication.
+ * Uses the Username and Password properties.
+ * @var boolean
+ * @see PHPMailer::$Username
+ * @see PHPMailer::$Password
+ */
+ public $SMTPAuth = false;
+
+ /**
+ * Options array passed to stream_context_create when connecting via SMTP.
+ * @var array
+ */
+ public $SMTPOptions = array();
+
+ /**
+ * SMTP username.
+ * @var string
+ */
+ public $Username = '';
+
+ /**
+ * SMTP password.
+ * @var string
+ */
+ public $Password = '';
+
+ /**
+ * SMTP auth type.
+ * Options are LOGIN (default), PLAIN, NTLM, CRAM-MD5
+ * @var string
+ */
+ public $AuthType = '';
+
+ /**
+ * SMTP realm.
+ * Used for NTLM auth
+ * @var string
+ */
+ public $Realm = '';
+
+ /**
+ * SMTP workstation.
+ * Used for NTLM auth
+ * @var string
+ */
+ public $Workstation = '';
+
+ /**
+ * The SMTP server timeout in seconds.
+ * Default of 5 minutes (300sec) is from RFC2821 section 4.5.3.2
+ * @var integer
+ */
+ public $Timeout = 300;
+
+ /**
+ * SMTP class debug output mode.
+ * Debug output level.
+ * Options:
+ * * `0` No output
+ * * `1` Commands
+ * * `2` Data and commands
+ * * `3` As 2 plus connection status
+ * * `4` Low-level data output
+ * @var integer
+ * @see SMTP::$do_debug
+ */
+ public $SMTPDebug = 0;
+
+ /**
+ * How to handle debug output.
+ * Options:
+ * * `echo` Output plain-text as-is, appropriate for CLI
+ * * `html` Output escaped, line breaks converted to ` `, appropriate for browser output
+ * * `error_log` Output to error log as configured in php.ini
+ *
+ * Alternatively, you can provide a callable expecting two params: a message string and the debug level:
+ *
+ * $mail->Debugoutput = function($str, $level) {echo "debug level $level; message: $str";};
+ *
+ * @var string|callable
+ * @see SMTP::$Debugoutput
+ */
+ public $Debugoutput = 'echo';
+
+ /**
+ * Whether to keep SMTP connection open after each message.
+ * If this is set to true then to close the connection
+ * requires an explicit call to smtpClose().
+ * @var boolean
+ */
+ public $SMTPKeepAlive = false;
+
+ /**
+ * Whether to split multiple to addresses into multiple messages
+ * or send them all in one message.
+ * @var boolean
+ */
+ public $SingleTo = false;
+
+ /**
+ * Storage for addresses when SingleTo is enabled.
+ * @var array
+ * @TODO This should really not be public
+ */
+ public $SingleToArray = array();
+
+ /**
+ * Whether to generate VERP addresses on send.
+ * Only applicable when sending via SMTP.
+ * @link https://en.wikipedia.org/wiki/Variable_envelope_return_path
+ * @link http://www.postfix.org/VERP_README.html Postfix VERP info
+ * @var boolean
+ */
+ public $do_verp = false;
+
+ /**
+ * Whether to allow sending messages with an empty body.
+ * @var boolean
+ */
+ public $AllowEmpty = false;
+
+ /**
+ * The default line ending.
+ * @note The default remains "\n". We force CRLF where we know
+ * it must be used via self::CRLF.
+ * @var string
+ */
+ public $LE = "\n";
+
+ /**
+ * DKIM selector.
+ * @var string
+ */
+ public $DKIM_selector = '';
+
+ /**
+ * DKIM Identity.
+ * Usually the email address used as the source of the email
+ * @var string
+ */
+ public $DKIM_identity = '';
+
+ /**
+ * DKIM passphrase.
+ * Used if your key is encrypted.
+ * @var string
+ */
+ public $DKIM_passphrase = '';
+
+ /**
+ * DKIM signing domain name.
+ * @example 'example.com'
+ * @var string
+ */
+ public $DKIM_domain = '';
+
+ /**
+ * DKIM private key file path.
+ * @var string
+ */
+ public $DKIM_private = '';
+
+ /**
+ * Callback Action function name.
+ *
+ * The function that handles the result of the send email action.
+ * It is called out by send() for each email sent.
+ *
+ * Value can be any php callable: http://www.php.net/is_callable
+ *
+ * Parameters:
+ * boolean $result result of the send action
+ * string $to email address of the recipient
+ * string $cc cc email addresses
+ * string $bcc bcc email addresses
+ * string $subject the subject
+ * string $body the email body
+ * string $from email address of sender
+ * @var string
+ */
+ public $action_function = '';
+
+ /**
+ * What to put in the X-Mailer header.
+ * Options: An empty string for PHPMailer default, whitespace for none, or a string to use
+ * @var string
+ */
+ public $XMailer = '';
+
+ /**
+ * An instance of the SMTP sender class.
+ * @var SMTP
+ * @access protected
+ */
+ protected $smtp = null;
+
+ /**
+ * The array of 'to' names and addresses.
+ * @var array
+ * @access protected
+ */
+ protected $to = array();
+
+ /**
+ * The array of 'cc' names and addresses.
+ * @var array
+ * @access protected
+ */
+ protected $cc = array();
+
+ /**
+ * The array of 'bcc' names and addresses.
+ * @var array
+ * @access protected
+ */
+ protected $bcc = array();
+
+ /**
+ * The array of reply-to names and addresses.
+ * @var array
+ * @access protected
+ */
+ protected $ReplyTo = array();
+
+ /**
+ * An array of all kinds of addresses.
+ * Includes all of $to, $cc, $bcc
+ * @var array
+ * @access protected
+ * @see PHPMailer::$to @see PHPMailer::$cc @see PHPMailer::$bcc
+ */
+ protected $all_recipients = array();
+
+ /**
+ * An array of names and addresses queued for validation.
+ * In send(), valid and non duplicate entries are moved to $all_recipients
+ * and one of $to, $cc, or $bcc.
+ * This array is used only for addresses with IDN.
+ * @var array
+ * @access protected
+ * @see PHPMailer::$to @see PHPMailer::$cc @see PHPMailer::$bcc
+ * @see PHPMailer::$all_recipients
+ */
+ protected $RecipientsQueue = array();
+
+ /**
+ * An array of reply-to names and addresses queued for validation.
+ * In send(), valid and non duplicate entries are moved to $ReplyTo.
+ * This array is used only for addresses with IDN.
+ * @var array
+ * @access protected
+ * @see PHPMailer::$ReplyTo
+ */
+ protected $ReplyToQueue = array();
+
+ /**
+ * The array of attachments.
+ * @var array
+ * @access protected
+ */
+ protected $attachment = array();
+
+ /**
+ * The array of custom headers.
+ * @var array
+ * @access protected
+ */
+ protected $CustomHeader = array();
+
+ /**
+ * The most recent Message-ID (including angular brackets).
+ * @var string
+ * @access protected
+ */
+ protected $lastMessageID = '';
+
+ /**
+ * The message's MIME type.
+ * @var string
+ * @access protected
+ */
+ protected $message_type = '';
+
+ /**
+ * The array of MIME boundary strings.
+ * @var array
+ * @access protected
+ */
+ protected $boundary = array();
+
+ /**
+ * The array of available languages.
+ * @var array
+ * @access protected
+ */
+ protected $language = array();
+
+ /**
+ * The number of errors encountered.
+ * @var integer
+ * @access protected
+ */
+ protected $error_count = 0;
+
+ /**
+ * The S/MIME certificate file path.
+ * @var string
+ * @access protected
+ */
+ protected $sign_cert_file = '';
+
+ /**
+ * The S/MIME key file path.
+ * @var string
+ * @access protected
+ */
+ protected $sign_key_file = '';
+
+ /**
+ * The optional S/MIME extra certificates ("CA Chain") file path.
+ * @var string
+ * @access protected
+ */
+ protected $sign_extracerts_file = '';
+
+ /**
+ * The S/MIME password for the key.
+ * Used only if the key is encrypted.
+ * @var string
+ * @access protected
+ */
+ protected $sign_key_pass = '';
+
+ /**
+ * Whether to throw exceptions for errors.
+ * @var boolean
+ * @access protected
+ */
+ protected $exceptions = false;
+
+ /**
+ * Unique ID used for message ID and boundaries.
+ * @var string
+ * @access protected
+ */
+ protected $uniqueid = '';
+
+ /**
+ * Error severity: message only, continue processing.
+ */
+ const STOP_MESSAGE = 0;
+
+ /**
+ * Error severity: message, likely ok to continue processing.
+ */
+ const STOP_CONTINUE = 1;
+
+ /**
+ * Error severity: message, plus full stop, critical error reached.
+ */
+ const STOP_CRITICAL = 2;
+
+ /**
+ * SMTP RFC standard line ending.
+ */
+ const CRLF = "\r\n";
+
+ /**
+ * The maximum line length allowed by RFC 2822 section 2.1.1
+ * @var integer
+ */
+ const MAX_LINE_LENGTH = 998;
+
+ /**
+ * Constructor.
+ * @param boolean $exceptions Should we throw external exceptions?
+ */
+ public function __construct($exceptions = false)
+ {
+ $this->exceptions = (boolean)$exceptions;
+ }
+
+ /**
+ * Destructor.
+ */
+ public function __destruct()
+ {
+ //Close any open SMTP connection nicely
+ if ($this->Mailer == 'smtp') {
+ $this->smtpClose();
+ }
+ }
+
+ /**
+ * Call mail() in a safe_mode-aware fashion.
+ * Also, unless sendmail_path points to sendmail (or something that
+ * claims to be sendmail), don't pass params (not a perfect fix,
+ * but it will do)
+ * @param string $to To
+ * @param string $subject Subject
+ * @param string $body Message Body
+ * @param string $header Additional Header(s)
+ * @param string $params Params
+ * @access private
+ * @return boolean
+ */
+ private function mailPassthru($to, $subject, $body, $header, $params)
+ {
+ //Check overloading of mail function to avoid double-encoding
+ if (ini_get('mbstring.func_overload') & 1) {
+ $subject = $this->secureHeader($subject);
+ } else {
+ $subject = $this->encodeHeader($this->secureHeader($subject));
+ }
+ if (ini_get('safe_mode') || !($this->UseSendmailOptions)) {
+ $result = @mail($to, $subject, $body, $header);
+ } else {
+ $result = @mail($to, $subject, $body, $header, $params);
+ }
+ return $result;
+ }
+
+ /**
+ * Output debugging info via user-defined method.
+ * Only generates output if SMTP debug output is enabled (@see SMTP::$do_debug).
+ * @see PHPMailer::$Debugoutput
+ * @see PHPMailer::$SMTPDebug
+ * @param string $str
+ */
+ protected function edebug($str)
+ {
+ if ($this->SMTPDebug <= 0) {
+ return;
+ }
+ //Avoid clash with built-in function names
+ if (!in_array($this->Debugoutput, array('error_log', 'html', 'echo')) and is_callable($this->Debugoutput)) {
+ call_user_func($this->Debugoutput, $str, $this->SMTPDebug);
+ return;
+ }
+ switch ($this->Debugoutput) {
+ case 'error_log':
+ //Don't output, just log
+ error_log($str);
+ break;
+ case 'html':
+ //Cleans up output a bit for a better looking, HTML-safe output
+ echo htmlentities(
+ preg_replace('/[\r\n]+/', '', $str),
+ ENT_QUOTES,
+ 'UTF-8'
+ )
+ . " \n";
+ break;
+ case 'echo':
+ default:
+ //Normalize line breaks
+ $str = preg_replace('/(\r\n|\r|\n)/ms', "\n", $str);
+ echo gmdate('Y-m-d H:i:s') . "\t" . str_replace(
+ "\n",
+ "\n \t ",
+ trim($str)
+ ) . "\n";
+ }
+ }
+
+ /**
+ * Sets message type to HTML or plain.
+ * @param boolean $isHtml True for HTML mode.
+ * @return void
+ */
+ public function isHTML($isHtml = true)
+ {
+ if ($isHtml) {
+ $this->ContentType = 'text/html';
+ } else {
+ $this->ContentType = 'text/plain';
+ }
+ }
+
+ /**
+ * Send messages using SMTP.
+ * @return void
+ */
+ public function isSMTP()
+ {
+ $this->Mailer = 'smtp';
+ }
+
+ /**
+ * Send messages using PHP's mail() function.
+ * @return void
+ */
+ public function isMail()
+ {
+ $this->Mailer = 'mail';
+ }
+
+ /**
+ * Send messages using $Sendmail.
+ * @return void
+ */
+ public function isSendmail()
+ {
+ $ini_sendmail_path = ini_get('sendmail_path');
+
+ if (!stristr($ini_sendmail_path, 'sendmail')) {
+ $this->Sendmail = '/usr/sbin/sendmail';
+ } else {
+ $this->Sendmail = $ini_sendmail_path;
+ }
+ $this->Mailer = 'sendmail';
+ }
+
+ /**
+ * Send messages using qmail.
+ * @return void
+ */
+ public function isQmail()
+ {
+ $ini_sendmail_path = ini_get('sendmail_path');
+
+ if (!stristr($ini_sendmail_path, 'qmail')) {
+ $this->Sendmail = '/var/qmail/bin/qmail-inject';
+ } else {
+ $this->Sendmail = $ini_sendmail_path;
+ }
+ $this->Mailer = 'qmail';
+ }
+
+ /**
+ * Add a "To" address.
+ * @param string $address The email address to send to
+ * @param string $name
+ * @return boolean true on success, false if address already used or invalid in some way
+ */
+ public function addAddress($address, $name = '')
+ {
+ return $this->addOrEnqueueAnAddress('to', $address, $name);
+ }
+
+ /**
+ * Add a "CC" address.
+ * @note: This function works with the SMTP mailer on win32, not with the "mail" mailer.
+ * @param string $address The email address to send to
+ * @param string $name
+ * @return boolean true on success, false if address already used or invalid in some way
+ */
+ public function addCC($address, $name = '')
+ {
+ return $this->addOrEnqueueAnAddress('cc', $address, $name);
+ }
+
+ /**
+ * Add a "BCC" address.
+ * @note: This function works with the SMTP mailer on win32, not with the "mail" mailer.
+ * @param string $address The email address to send to
+ * @param string $name
+ * @return boolean true on success, false if address already used or invalid in some way
+ */
+ public function addBCC($address, $name = '')
+ {
+ return $this->addOrEnqueueAnAddress('bcc', $address, $name);
+ }
+
+ /**
+ * Add a "Reply-To" address.
+ * @param string $address The email address to reply to
+ * @param string $name
+ * @return boolean true on success, false if address already used or invalid in some way
+ */
+ public function addReplyTo($address, $name = '')
+ {
+ return $this->addOrEnqueueAnAddress('Reply-To', $address, $name);
+ }
+
+ /**
+ * Add an address to one of the recipient arrays or to the ReplyTo array. Because PHPMailer
+ * can't validate addresses with an IDN without knowing the PHPMailer::$CharSet (that can still
+ * be modified after calling this function), addition of such addresses is delayed until send().
+ * Addresses that have been added already return false, but do not throw exceptions.
+ * @param string $kind One of 'to', 'cc', 'bcc', or 'ReplyTo'
+ * @param string $address The email address to send, resp. to reply to
+ * @param string $name
+ * @throws phpmailerException
+ * @return boolean true on success, false if address already used or invalid in some way
+ * @access protected
+ */
+ protected function addOrEnqueueAnAddress($kind, $address, $name)
+ {
+ $address = trim($address);
+ $name = trim(preg_replace('/[\r\n]+/', '', $name)); //Strip breaks and trim
+ if (($pos = strrpos($address, '@')) === false) {
+ // At-sign is misssing.
+ $error_message = $this->lang('invalid_address') . " (addAnAddress $kind): $address";
+ $this->setError($error_message);
+ $this->edebug($error_message);
+ if ($this->exceptions) {
+ throw new phpmailerException($error_message);
+ }
+ return false;
+ }
+ $params = array($kind, $address, $name);
+ // Enqueue addresses with IDN until we know the PHPMailer::$CharSet.
+ if ($this->has8bitChars(substr($address, ++$pos)) and $this->idnSupported()) {
+ if ($kind != 'Reply-To') {
+ if (!array_key_exists($address, $this->RecipientsQueue)) {
+ $this->RecipientsQueue[$address] = $params;
+ return true;
+ }
+ } else {
+ if (!array_key_exists($address, $this->ReplyToQueue)) {
+ $this->ReplyToQueue[$address] = $params;
+ return true;
+ }
+ }
+ return false;
+ }
+ // Immediately add standard addresses without IDN.
+ return call_user_func_array(array($this, 'addAnAddress'), $params);
+ }
+
+ /**
+ * Add an address to one of the recipient arrays or to the ReplyTo array.
+ * Addresses that have been added already return false, but do not throw exceptions.
+ * @param string $kind One of 'to', 'cc', 'bcc', or 'ReplyTo'
+ * @param string $address The email address to send, resp. to reply to
+ * @param string $name
+ * @throws phpmailerException
+ * @return boolean true on success, false if address already used or invalid in some way
+ * @access protected
+ */
+ protected function addAnAddress($kind, $address, $name = '')
+ {
+ if (!in_array($kind, array('to', 'cc', 'bcc', 'Reply-To'))) {
+ $error_message = $this->lang('Invalid recipient kind: ') . $kind;
+ $this->setError($error_message);
+ $this->edebug($error_message);
+ if ($this->exceptions) {
+ throw new phpmailerException($error_message);
+ }
+ return false;
+ }
+ if (!$this->validateAddress($address)) {
+ $error_message = $this->lang('invalid_address') . " (addAnAddress $kind): $address";
+ $this->setError($error_message);
+ $this->edebug($error_message);
+ if ($this->exceptions) {
+ throw new phpmailerException($error_message);
+ }
+ return false;
+ }
+ if ($kind != 'Reply-To') {
+ if (!array_key_exists(strtolower($address), $this->all_recipients)) {
+ array_push($this->$kind, array($address, $name));
+ $this->all_recipients[strtolower($address)] = true;
+ return true;
+ }
+ } else {
+ if (!array_key_exists(strtolower($address), $this->ReplyTo)) {
+ $this->ReplyTo[strtolower($address)] = array($address, $name);
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Parse and validate a string containing one or more RFC822-style comma-separated email addresses
+ * of the form "display name " into an array of name/address pairs.
+ * Uses the imap_rfc822_parse_adrlist function if the IMAP extension is available.
+ * Note that quotes in the name part are removed.
+ * @param string $addrstr The address list string
+ * @param bool $useimap Whether to use the IMAP extension to parse the list
+ * @return array
+ * @link http://www.andrew.cmu.edu/user/agreen1/testing/mrbs/web/Mail/RFC822.php A more careful implementation
+ */
+ public function parseAddresses($addrstr, $useimap = true)
+ {
+ $addresses = array();
+ if ($useimap and function_exists('imap_rfc822_parse_adrlist')) {
+ //Use this built-in parser if it's available
+ $list = imap_rfc822_parse_adrlist($addrstr, '');
+ foreach ($list as $address) {
+ if ($address->host != '.SYNTAX-ERROR.') {
+ if ($this->validateAddress($address->mailbox . '@' . $address->host)) {
+ $addresses[] = array(
+ 'name' => (property_exists($address, 'personal') ? $address->personal : ''),
+ 'address' => $address->mailbox . '@' . $address->host
+ );
+ }
+ }
+ }
+ } else {
+ //Use this simpler parser
+ $list = explode(',', $addrstr);
+ foreach ($list as $address) {
+ $address = trim($address);
+ //Is there a separate name part?
+ if (strpos($address, '<') === false) {
+ //No separate name, just use the whole thing
+ if ($this->validateAddress($address)) {
+ $addresses[] = array(
+ 'name' => '',
+ 'address' => $address
+ );
+ }
+ } else {
+ list($name, $email) = explode('<', $address);
+ $email = trim(str_replace('>', '', $email));
+ if ($this->validateAddress($email)) {
+ $addresses[] = array(
+ 'name' => trim(str_replace(array('"', "'"), '', $name)),
+ 'address' => $email
+ );
+ }
+ }
+ }
+ }
+ return $addresses;
+ }
+
+ /**
+ * Set the From and FromName properties.
+ * @param string $address
+ * @param string $name
+ * @param boolean $auto Whether to also set the Sender address, defaults to true
+ * @throws phpmailerException
+ * @return boolean
+ */
+ public function setFrom($address, $name = '', $auto = true)
+ {
+ $address = trim($address);
+ $name = trim(preg_replace('/[\r\n]+/', '', $name)); //Strip breaks and trim
+ // Don't validate now addresses with IDN. Will be done in send().
+ if (($pos = strrpos($address, '@')) === false or
+ (!$this->has8bitChars(substr($address, ++$pos)) or !$this->idnSupported()) and
+ !$this->validateAddress($address)) {
+ $error_message = $this->lang('invalid_address') . " (setFrom) $address";
+ $this->setError($error_message);
+ $this->edebug($error_message);
+ if ($this->exceptions) {
+ throw new phpmailerException($error_message);
+ }
+ return false;
+ }
+ $this->From = $address;
+ $this->FromName = $name;
+ if ($auto) {
+ if (empty($this->Sender)) {
+ $this->Sender = $address;
+ }
+ }
+ return true;
+ }
+
+ /**
+ * Return the Message-ID header of the last email.
+ * Technically this is the value from the last time the headers were created,
+ * but it's also the message ID of the last sent message except in
+ * pathological cases.
+ * @return string
+ */
+ public function getLastMessageID()
+ {
+ return $this->lastMessageID;
+ }
+
+ /**
+ * Check that a string looks like an email address.
+ * @param string $address The email address to check
+ * @param string $patternselect A selector for the validation pattern to use :
+ * * `auto` Pick best pattern automatically;
+ * * `pcre8` Use the squiloople.com pattern, requires PCRE > 8.0, PHP >= 5.3.2, 5.2.14;
+ * * `pcre` Use old PCRE implementation;
+ * * `php` Use PHP built-in FILTER_VALIDATE_EMAIL;
+ * * `html5` Use the pattern given by the HTML5 spec for 'email' type form input elements.
+ * * `noregex` Don't use a regex: super fast, really dumb.
+ * @return boolean
+ * @static
+ * @access public
+ */
+ public static function validateAddress($address, $patternselect = 'auto')
+ {
+ //Reject line breaks in addresses; it's valid RFC5322, but not RFC5321
+ if (strpos($address, "\n") !== false or strpos($address, "\r") !== false) {
+ return false;
+ }
+ if (!$patternselect or $patternselect == 'auto') {
+ //Check this constant first so it works when extension_loaded() is disabled by safe mode
+ //Constant was added in PHP 5.2.4
+ if (defined('PCRE_VERSION')) {
+ //This pattern can get stuck in a recursive loop in PCRE <= 8.0.2
+ if (version_compare(PCRE_VERSION, '8.0.3') >= 0) {
+ $patternselect = 'pcre8';
+ } else {
+ $patternselect = 'pcre';
+ }
+ } elseif (function_exists('extension_loaded') and extension_loaded('pcre')) {
+ //Fall back to older PCRE
+ $patternselect = 'pcre';
+ } else {
+ //Filter_var appeared in PHP 5.2.0 and does not require the PCRE extension
+ if (version_compare(PHP_VERSION, '5.2.0') >= 0) {
+ $patternselect = 'php';
+ } else {
+ $patternselect = 'noregex';
+ }
+ }
+ }
+ switch ($patternselect) {
+ case 'pcre8':
+ /**
+ * Uses the same RFC5322 regex on which FILTER_VALIDATE_EMAIL is based, but allows dotless domains.
+ * @link http://squiloople.com/2009/12/20/email-address-validation/
+ * @copyright 2009-2010 Michael Rushton
+ * Feel free to use and redistribute this code. But please keep this copyright notice.
+ */
+ return (boolean)preg_match(
+ '/^(?!(?>(?1)"?(?>\\\[ -~]|[^"])"?(?1)){255,})(?!(?>(?1)"?(?>\\\[ -~]|[^"])"?(?1)){65,}@)' .
+ '((?>(?>(?>((?>(?>(?>\x0D\x0A)?[\t ])+|(?>[\t ]*\x0D\x0A)?[\t ]+)?)(\((?>(?2)' .
+ '(?>[\x01-\x08\x0B\x0C\x0E-\'*-\[\]-\x7F]|\\\[\x00-\x7F]|(?3)))*(?2)\)))+(?2))|(?2))?)' .
+ '([!#-\'*+\/-9=?^-~-]+|"(?>(?2)(?>[\x01-\x08\x0B\x0C\x0E-!#-\[\]-\x7F]|\\\[\x00-\x7F]))*' .
+ '(?2)")(?>(?1)\.(?1)(?4))*(?1)@(?!(?1)[a-z0-9-]{64,})(?1)(?>([a-z0-9](?>[a-z0-9-]*[a-z0-9])?)' .
+ '(?>(?1)\.(?!(?1)[a-z0-9-]{64,})(?1)(?5)){0,126}|\[(?:(?>IPv6:(?>([a-f0-9]{1,4})(?>:(?6)){7}' .
+ '|(?!(?:.*[a-f0-9][:\]]){8,})((?6)(?>:(?6)){0,6})?::(?7)?))|(?>(?>IPv6:(?>(?6)(?>:(?6)){5}:' .
+ '|(?!(?:.*[a-f0-9]:){6,})(?8)?::(?>((?6)(?>:(?6)){0,4}):)?))?(25[0-5]|2[0-4][0-9]|1[0-9]{2}' .
+ '|[1-9]?[0-9])(?>\.(?9)){3}))\])(?1)$/isD',
+ $address
+ );
+ case 'pcre':
+ //An older regex that doesn't need a recent PCRE
+ return (boolean)preg_match(
+ '/^(?!(?>"?(?>\\\[ -~]|[^"])"?){255,})(?!(?>"?(?>\\\[ -~]|[^"])"?){65,}@)(?>' .
+ '[!#-\'*+\/-9=?^-~-]+|"(?>(?>[\x01-\x08\x0B\x0C\x0E-!#-\[\]-\x7F]|\\\[\x00-\xFF]))*")' .
+ '(?>\.(?>[!#-\'*+\/-9=?^-~-]+|"(?>(?>[\x01-\x08\x0B\x0C\x0E-!#-\[\]-\x7F]|\\\[\x00-\xFF]))*"))*' .
+ '@(?>(?![a-z0-9-]{64,})(?>[a-z0-9](?>[a-z0-9-]*[a-z0-9])?)(?>\.(?![a-z0-9-]{64,})' .
+ '(?>[a-z0-9](?>[a-z0-9-]*[a-z0-9])?)){0,126}|\[(?:(?>IPv6:(?>(?>[a-f0-9]{1,4})(?>:' .
+ '[a-f0-9]{1,4}){7}|(?!(?:.*[a-f0-9][:\]]){8,})(?>[a-f0-9]{1,4}(?>:[a-f0-9]{1,4}){0,6})?' .
+ '::(?>[a-f0-9]{1,4}(?>:[a-f0-9]{1,4}){0,6})?))|(?>(?>IPv6:(?>[a-f0-9]{1,4}(?>:' .
+ '[a-f0-9]{1,4}){5}:|(?!(?:.*[a-f0-9]:){6,})(?>[a-f0-9]{1,4}(?>:[a-f0-9]{1,4}){0,4})?' .
+ '::(?>(?:[a-f0-9]{1,4}(?>:[a-f0-9]{1,4}){0,4}):)?))?(?>25[0-5]|2[0-4][0-9]|1[0-9]{2}' .
+ '|[1-9]?[0-9])(?>\.(?>25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])){3}))\])$/isD',
+ $address
+ );
+ case 'html5':
+ /**
+ * This is the pattern used in the HTML5 spec for validation of 'email' type form input elements.
+ * @link http://www.whatwg.org/specs/web-apps/current-work/#e-mail-state-(type=email)
+ */
+ return (boolean)preg_match(
+ '/^[a-zA-Z0-9.!#$%&\'*+\/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}' .
+ '[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$/sD',
+ $address
+ );
+ case 'noregex':
+ //No PCRE! Do something _very_ approximate!
+ //Check the address is 3 chars or longer and contains an @ that's not the first or last char
+ return (strlen($address) >= 3
+ and strpos($address, '@') >= 1
+ and strpos($address, '@') != strlen($address) - 1);
+ case 'php':
+ default:
+ return (boolean)filter_var($address, FILTER_VALIDATE_EMAIL);
+ }
+ }
+
+ /**
+ * Tells whether IDNs (Internationalized Domain Names) are supported or not. This requires the
+ * "intl" and "mbstring" PHP extensions.
+ * @return bool "true" if required functions for IDN support are present
+ */
+ public function idnSupported()
+ {
+ // @TODO: Write our own "idn_to_ascii" function for PHP <= 5.2.
+ return function_exists('idn_to_ascii') and function_exists('mb_convert_encoding');
+ }
+
+ /**
+ * Converts IDN in given email address to its ASCII form, also known as punycode, if possible.
+ * Important: Address must be passed in same encoding as currently set in PHPMailer::$CharSet.
+ * This function silently returns unmodified address if:
+ * - No conversion is necessary (i.e. domain name is not an IDN, or is already in ASCII form)
+ * - Conversion to punycode is impossible (e.g. required PHP functions are not available)
+ * or fails for any reason (e.g. domain has characters not allowed in an IDN)
+ * @see PHPMailer::$CharSet
+ * @param string $address The email address to convert
+ * @return string The encoded address in ASCII form
+ */
+ public function punyencodeAddress($address)
+ {
+ // Verify we have required functions, CharSet, and at-sign.
+ if ($this->idnSupported() and
+ !empty($this->CharSet) and
+ ($pos = strrpos($address, '@')) !== false) {
+ $domain = substr($address, ++$pos);
+ // Verify CharSet string is a valid one, and domain properly encoded in this CharSet.
+ if ($this->has8bitChars($domain) and @mb_check_encoding($domain, $this->CharSet)) {
+ $domain = mb_convert_encoding($domain, 'UTF-8', $this->CharSet);
+ if (($punycode = defined('INTL_IDNA_VARIANT_UTS46') ?
+ idn_to_ascii($domain, 0, INTL_IDNA_VARIANT_UTS46) :
+ idn_to_ascii($domain)) !== false) {
+ return substr($address, 0, $pos) . $punycode;
+ }
+ }
+ }
+ return $address;
+ }
+
+ /**
+ * Create a message and send it.
+ * Uses the sending method specified by $Mailer.
+ * @throws phpmailerException
+ * @return boolean false on error - See the ErrorInfo property for details of the error.
+ */
+ public function send()
+ {
+ try {
+ if (!$this->preSend()) {
+ return false;
+ }
+ return $this->postSend();
+ } catch (phpmailerException $exc) {
+ $this->mailHeader = '';
+ $this->setError($exc->getMessage());
+ if ($this->exceptions) {
+ throw $exc;
+ }
+ return false;
+ }
+ }
+
+ /**
+ * Prepare a message for sending.
+ * @throws phpmailerException
+ * @return boolean
+ */
+ public function preSend()
+ {
+ try {
+ $this->error_count = 0; // Reset errors
+ $this->mailHeader = '';
+
+ // Dequeue recipient and Reply-To addresses with IDN
+ foreach (array_merge($this->RecipientsQueue, $this->ReplyToQueue) as $params) {
+ $params[1] = $this->punyencodeAddress($params[1]);
+ call_user_func_array(array($this, 'addAnAddress'), $params);
+ }
+ if ((count($this->to) + count($this->cc) + count($this->bcc)) < 1) {
+ throw new phpmailerException($this->lang('provide_address'), self::STOP_CRITICAL);
+ }
+
+ // Validate From, Sender, and ConfirmReadingTo addresses
+ foreach (array('From', 'Sender', 'ConfirmReadingTo') as $address_kind) {
+ $this->$address_kind = trim($this->$address_kind);
+ if (empty($this->$address_kind)) {
+ continue;
+ }
+ $this->$address_kind = $this->punyencodeAddress($this->$address_kind);
+ if (!$this->validateAddress($this->$address_kind)) {
+ $error_message = $this->lang('invalid_address') . ' (punyEncode) ' . $this->$address_kind;
+ $this->setError($error_message);
+ $this->edebug($error_message);
+ if ($this->exceptions) {
+ throw new phpmailerException($error_message);
+ }
+ return false;
+ }
+ }
+
+ // Set whether the message is multipart/alternative
+ if ($this->alternativeExists()) {
+ $this->ContentType = 'multipart/alternative';
+ }
+
+ $this->setMessageType();
+ // Refuse to send an empty message unless we are specifically allowing it
+ if (!$this->AllowEmpty and empty($this->Body)) {
+ throw new phpmailerException($this->lang('empty_message'), self::STOP_CRITICAL);
+ }
+
+ // Create body before headers in case body makes changes to headers (e.g. altering transfer encoding)
+ $this->MIMEHeader = '';
+ $this->MIMEBody = $this->createBody();
+ // createBody may have added some headers, so retain them
+ $tempheaders = $this->MIMEHeader;
+ $this->MIMEHeader = $this->createHeader();
+ $this->MIMEHeader .= $tempheaders;
+
+ // To capture the complete message when using mail(), create
+ // an extra header list which createHeader() doesn't fold in
+ if ($this->Mailer == 'mail') {
+ if (count($this->to) > 0) {
+ $this->mailHeader .= $this->addrAppend('To', $this->to);
+ } else {
+ $this->mailHeader .= $this->headerLine('To', 'undisclosed-recipients:;');
+ }
+ $this->mailHeader .= $this->headerLine(
+ 'Subject',
+ $this->encodeHeader($this->secureHeader(trim($this->Subject)))
+ );
+ }
+
+ // Sign with DKIM if enabled
+ if (!empty($this->DKIM_domain)
+ && !empty($this->DKIM_private)
+ && !empty($this->DKIM_selector)
+ && file_exists($this->DKIM_private)) {
+ $header_dkim = $this->DKIM_Add(
+ $this->MIMEHeader . $this->mailHeader,
+ $this->encodeHeader($this->secureHeader($this->Subject)),
+ $this->MIMEBody
+ );
+ $this->MIMEHeader = rtrim($this->MIMEHeader, "\r\n ") . self::CRLF .
+ str_replace("\r\n", "\n", $header_dkim) . self::CRLF;
+ }
+ return true;
+ } catch (phpmailerException $exc) {
+ $this->setError($exc->getMessage());
+ if ($this->exceptions) {
+ throw $exc;
+ }
+ return false;
+ }
+ }
+
+ /**
+ * Actually send a message.
+ * Send the email via the selected mechanism
+ * @throws phpmailerException
+ * @return boolean
+ */
+ public function postSend()
+ {
+ try {
+ // Choose the mailer and send through it
+ switch ($this->Mailer) {
+ case 'sendmail':
+ case 'qmail':
+ return $this->sendmailSend($this->MIMEHeader, $this->MIMEBody);
+ case 'smtp':
+ return $this->smtpSend($this->MIMEHeader, $this->MIMEBody);
+ case 'mail':
+ return $this->mailSend($this->MIMEHeader, $this->MIMEBody);
+ default:
+ $sendMethod = $this->Mailer.'Send';
+ if (method_exists($this, $sendMethod)) {
+ return $this->$sendMethod($this->MIMEHeader, $this->MIMEBody);
+ }
+
+ return $this->mailSend($this->MIMEHeader, $this->MIMEBody);
+ }
+ } catch (phpmailerException $exc) {
+ $this->setError($exc->getMessage());
+ $this->edebug($exc->getMessage());
+ if ($this->exceptions) {
+ throw $exc;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Send mail using the $Sendmail program.
+ * @param string $header The message headers
+ * @param string $body The message body
+ * @see PHPMailer::$Sendmail
+ * @throws phpmailerException
+ * @access protected
+ * @return boolean
+ */
+ protected function sendmailSend($header, $body)
+ {
+ if ($this->Sender != '') {
+ if ($this->Mailer == 'qmail') {
+ $sendmail = sprintf('%s -f%s', escapeshellcmd($this->Sendmail), escapeshellarg($this->Sender));
+ } else {
+ $sendmail = sprintf('%s -oi -f%s -t', escapeshellcmd($this->Sendmail), escapeshellarg($this->Sender));
+ }
+ } else {
+ if ($this->Mailer == 'qmail') {
+ $sendmail = sprintf('%s', escapeshellcmd($this->Sendmail));
+ } else {
+ $sendmail = sprintf('%s -oi -t', escapeshellcmd($this->Sendmail));
+ }
+ }
+ if ($this->SingleTo) {
+ foreach ($this->SingleToArray as $toAddr) {
+ if (!@$mail = popen($sendmail, 'w')) {
+ throw new phpmailerException($this->lang('execute') . $this->Sendmail, self::STOP_CRITICAL);
+ }
+ fputs($mail, 'To: ' . $toAddr . "\n");
+ fputs($mail, $header);
+ fputs($mail, $body);
+ $result = pclose($mail);
+ $this->doCallback(
+ ($result == 0),
+ array($toAddr),
+ $this->cc,
+ $this->bcc,
+ $this->Subject,
+ $body,
+ $this->From
+ );
+ if ($result != 0) {
+ throw new phpmailerException($this->lang('execute') . $this->Sendmail, self::STOP_CRITICAL);
+ }
+ }
+ } else {
+ if (!@$mail = popen($sendmail, 'w')) {
+ throw new phpmailerException($this->lang('execute') . $this->Sendmail, self::STOP_CRITICAL);
+ }
+ fputs($mail, $header);
+ fputs($mail, $body);
+ $result = pclose($mail);
+ $this->doCallback(
+ ($result == 0),
+ $this->to,
+ $this->cc,
+ $this->bcc,
+ $this->Subject,
+ $body,
+ $this->From
+ );
+ if ($result != 0) {
+ throw new phpmailerException($this->lang('execute') . $this->Sendmail, self::STOP_CRITICAL);
+ }
+ }
+ return true;
+ }
+
+ /**
+ * Send mail using the PHP mail() function.
+ * @param string $header The message headers
+ * @param string $body The message body
+ * @link http://www.php.net/manual/en/book.mail.php
+ * @throws phpmailerException
+ * @access protected
+ * @return boolean
+ */
+ protected function mailSend($header, $body)
+ {
+ $toArr = array();
+ foreach ($this->to as $toaddr) {
+ $toArr[] = $this->addrFormat($toaddr);
+ }
+ $to = implode(', ', $toArr);
+
+ if (empty($this->Sender)) {
+ $params = ' ';
+ } else {
+ $params = sprintf('-f%s', $this->Sender);
+ }
+ if ($this->Sender != '' and !ini_get('safe_mode')) {
+ $old_from = ini_get('sendmail_from');
+ ini_set('sendmail_from', $this->Sender);
+ }
+ $result = false;
+ if ($this->SingleTo && count($toArr) > 1) {
+ foreach ($toArr as $toAddr) {
+ $result = $this->mailPassthru($toAddr, $this->Subject, $body, $header, $params);
+ $this->doCallback($result, array($toAddr), $this->cc, $this->bcc, $this->Subject, $body, $this->From);
+ }
+ } else {
+ $result = $this->mailPassthru($to, $this->Subject, $body, $header, $params);
+ $this->doCallback($result, $this->to, $this->cc, $this->bcc, $this->Subject, $body, $this->From);
+ }
+ if (isset($old_from)) {
+ ini_set('sendmail_from', $old_from);
+ }
+ if (!$result) {
+ throw new phpmailerException($this->lang('instantiate'), self::STOP_CRITICAL);
+ }
+ return true;
+ }
+
+ /**
+ * Get an instance to use for SMTP operations.
+ * Override this function to load your own SMTP implementation
+ * @return SMTP
+ */
+ public function getSMTPInstance()
+ {
+ if (!is_object($this->smtp)) {
+ $this->smtp = new SMTP;
+ }
+ return $this->smtp;
+ }
+
+ /**
+ * Send mail via SMTP.
+ * Returns false if there is a bad MAIL FROM, RCPT, or DATA input.
+ * Uses the PHPMailerSMTP class by default.
+ * @see PHPMailer::getSMTPInstance() to use a different class.
+ * @param string $header The message headers
+ * @param string $body The message body
+ * @throws phpmailerException
+ * @uses SMTP
+ * @access protected
+ * @return boolean
+ */
+ protected function smtpSend($header, $body)
+ {
+ $bad_rcpt = array();
+ if (!$this->smtpConnect($this->SMTPOptions)) {
+ throw new phpmailerException($this->lang('smtp_connect_failed'), self::STOP_CRITICAL);
+ }
+ if ('' == $this->Sender) {
+ $smtp_from = $this->From;
+ } else {
+ $smtp_from = $this->Sender;
+ }
+ if (!$this->smtp->mail($smtp_from)) {
+ $this->setError($this->lang('from_failed') . $smtp_from . ' : ' . implode(',', $this->smtp->getError()));
+ throw new phpmailerException($this->ErrorInfo, self::STOP_CRITICAL);
+ }
+
+ // Attempt to send to all recipients
+ foreach (array($this->to, $this->cc, $this->bcc) as $togroup) {
+ foreach ($togroup as $to) {
+ if (!$this->smtp->recipient($to[0])) {
+ $error = $this->smtp->getError();
+ $bad_rcpt[] = array('to' => $to[0], 'error' => $error['detail']);
+ $isSent = false;
+ } else {
+ $isSent = true;
+ }
+ $this->doCallback($isSent, array($to[0]), array(), array(), $this->Subject, $body, $this->From);
+ }
+ }
+
+ // Only send the DATA command if we have viable recipients
+ if ((count($this->all_recipients) > count($bad_rcpt)) and !$this->smtp->data($header . $body)) {
+ throw new phpmailerException($this->lang('data_not_accepted'), self::STOP_CRITICAL);
+ }
+ if ($this->SMTPKeepAlive) {
+ $this->smtp->reset();
+ } else {
+ $this->smtp->quit();
+ $this->smtp->close();
+ }
+ //Create error message for any bad addresses
+ if (count($bad_rcpt) > 0) {
+ $errstr = '';
+ foreach ($bad_rcpt as $bad) {
+ $errstr .= $bad['to'] . ': ' . $bad['error'];
+ }
+ throw new phpmailerException(
+ $this->lang('recipients_failed') . $errstr,
+ self::STOP_CONTINUE
+ );
+ }
+ return true;
+ }
+
+ /**
+ * Initiate a connection to an SMTP server.
+ * Returns false if the operation failed.
+ * @param array $options An array of options compatible with stream_context_create()
+ * @uses SMTP
+ * @access public
+ * @throws phpmailerException
+ * @return boolean
+ */
+ public function smtpConnect($options = array())
+ {
+ if (is_null($this->smtp)) {
+ $this->smtp = $this->getSMTPInstance();
+ }
+
+ // Already connected?
+ if ($this->smtp->connected()) {
+ return true;
+ }
+
+ $this->smtp->setTimeout($this->Timeout);
+ $this->smtp->setDebugLevel($this->SMTPDebug);
+ $this->smtp->setDebugOutput($this->Debugoutput);
+ $this->smtp->setVerp($this->do_verp);
+ $hosts = explode(';', $this->Host);
+ $lastexception = null;
+
+ foreach ($hosts as $hostentry) {
+ $hostinfo = array();
+ if (!preg_match('/^((ssl|tls):\/\/)*([a-zA-Z0-9\.-]*):?([0-9]*)$/', trim($hostentry), $hostinfo)) {
+ // Not a valid host entry
+ continue;
+ }
+ // $hostinfo[2]: optional ssl or tls prefix
+ // $hostinfo[3]: the hostname
+ // $hostinfo[4]: optional port number
+ // The host string prefix can temporarily override the current setting for SMTPSecure
+ // If it's not specified, the default value is used
+ $prefix = '';
+ $secure = $this->SMTPSecure;
+ $tls = ($this->SMTPSecure == 'tls');
+ if ('ssl' == $hostinfo[2] or ('' == $hostinfo[2] and 'ssl' == $this->SMTPSecure)) {
+ $prefix = 'ssl://';
+ $tls = false; // Can't have SSL and TLS at the same time
+ $secure = 'ssl';
+ } elseif ($hostinfo[2] == 'tls') {
+ $tls = true;
+ // tls doesn't use a prefix
+ $secure = 'tls';
+ }
+ //Do we need the OpenSSL extension?
+ $sslext = defined('OPENSSL_ALGO_SHA1');
+ if ('tls' === $secure or 'ssl' === $secure) {
+ //Check for an OpenSSL constant rather than using extension_loaded, which is sometimes disabled
+ if (!$sslext) {
+ throw new phpmailerException($this->lang('extension_missing').'openssl', self::STOP_CRITICAL);
+ }
+ }
+ $host = $hostinfo[3];
+ $port = $this->Port;
+ $tport = (integer)$hostinfo[4];
+ if ($tport > 0 and $tport < 65536) {
+ $port = $tport;
+ }
+ if ($this->smtp->connect($prefix . $host, $port, $this->Timeout, $options)) {
+ try {
+ if ($this->Helo) {
+ $hello = $this->Helo;
+ } else {
+ $hello = $this->serverHostname();
+ }
+ $this->smtp->hello($hello);
+ //Automatically enable TLS encryption if:
+ // * it's not disabled
+ // * we have openssl extension
+ // * we are not already using SSL
+ // * the server offers STARTTLS
+ if ($this->SMTPAutoTLS and $sslext and $secure != 'ssl' and $this->smtp->getServerExt('STARTTLS')) {
+ $tls = true;
+ }
+ if ($tls) {
+ if (!$this->smtp->startTLS()) {
+ throw new phpmailerException($this->lang('connect_host'));
+ }
+ // We must resend HELO after tls negotiation
+ $this->smtp->hello($hello);
+ }
+ if ($this->SMTPAuth) {
+ if (!$this->smtp->authenticate(
+ $this->Username,
+ $this->Password,
+ $this->AuthType,
+ $this->Realm,
+ $this->Workstation
+ )
+ ) {
+ throw new phpmailerException($this->lang('authenticate'));
+ }
+ }
+ return true;
+ } catch (phpmailerException $exc) {
+ $lastexception = $exc;
+ $this->edebug($exc->getMessage());
+ // We must have connected, but then failed TLS or Auth, so close connection nicely
+ $this->smtp->quit();
+ }
+ }
+ }
+ // If we get here, all connection attempts have failed, so close connection hard
+ $this->smtp->close();
+ // As we've caught all exceptions, just report whatever the last one was
+ if ($this->exceptions and !is_null($lastexception)) {
+ throw $lastexception;
+ }
+ return false;
+ }
+
+ /**
+ * Close the active SMTP session if one exists.
+ * @return void
+ */
+ public function smtpClose()
+ {
+ if ($this->smtp !== null) {
+ if ($this->smtp->connected()) {
+ $this->smtp->quit();
+ $this->smtp->close();
+ }
+ }
+ }
+
+ /**
+ * Set the language for error messages.
+ * Returns false if it cannot load the language file.
+ * The default language is English.
+ * @param string $langcode ISO 639-1 2-character language code (e.g. French is "fr")
+ * @param string $lang_path Path to the language file directory, with trailing separator (slash)
+ * @return boolean
+ * @access public
+ */
+ public function setLanguage($langcode = 'en', $lang_path = '')
+ {
+ // Define full set of translatable strings in English
+ $PHPMAILER_LANG = array(
+ 'authenticate' => 'SMTP Error: Could not authenticate.',
+ 'connect_host' => 'SMTP Error: Could not connect to SMTP host.',
+ 'data_not_accepted' => 'SMTP Error: data not accepted.',
+ 'empty_message' => 'Message body empty',
+ 'encoding' => 'Unknown encoding: ',
+ 'execute' => 'Could not execute: ',
+ 'file_access' => 'Could not access file: ',
+ 'file_open' => 'File Error: Could not open file: ',
+ 'from_failed' => 'The following From address failed: ',
+ 'instantiate' => 'Could not instantiate mail function.',
+ 'invalid_address' => 'Invalid address: ',
+ 'mailer_not_supported' => ' mailer is not supported.',
+ 'provide_address' => 'You must provide at least one recipient email address.',
+ 'recipients_failed' => 'SMTP Error: The following recipients failed: ',
+ 'signing' => 'Signing Error: ',
+ 'smtp_connect_failed' => 'SMTP connect() failed.',
+ 'smtp_error' => 'SMTP server error: ',
+ 'variable_set' => 'Cannot set or reset variable: ',
+ 'extension_missing' => 'Extension missing: '
+ );
+ if (empty($lang_path)) {
+ // Calculate an absolute path so it can work if CWD is not here
+ $lang_path = dirname(__FILE__). DIRECTORY_SEPARATOR . 'language'. DIRECTORY_SEPARATOR;
+ }
+ $foundlang = true;
+ $lang_file = $lang_path . 'phpmailer.lang-' . $langcode . '.php';
+ // There is no English translation file
+ if ($langcode != 'en') {
+ // Make sure language file path is readable
+ if (!is_readable($lang_file)) {
+ $foundlang = false;
+ } else {
+ // Overwrite language-specific strings.
+ // This way we'll never have missing translation keys.
+ $foundlang = include $lang_file;
+ }
+ }
+ $this->language = $PHPMAILER_LANG;
+ return (boolean)$foundlang; // Returns false if language not found
+ }
+
+ /**
+ * Get the array of strings for the current language.
+ * @return array
+ */
+ public function getTranslations()
+ {
+ return $this->language;
+ }
+
+ /**
+ * Create recipient headers.
+ * @access public
+ * @param string $type
+ * @param array $addr An array of recipient,
+ * where each recipient is a 2-element indexed array with element 0 containing an address
+ * and element 1 containing a name, like:
+ * array(array('joe@example.com', 'Joe User'), array('zoe@example.com', 'Zoe User'))
+ * @return string
+ */
+ public function addrAppend($type, $addr)
+ {
+ $addresses = array();
+ foreach ($addr as $address) {
+ $addresses[] = $this->addrFormat($address);
+ }
+ return $type . ': ' . implode(', ', $addresses) . $this->LE;
+ }
+
+ /**
+ * Format an address for use in a message header.
+ * @access public
+ * @param array $addr A 2-element indexed array, element 0 containing an address, element 1 containing a name
+ * like array('joe@example.com', 'Joe User')
+ * @return string
+ */
+ public function addrFormat($addr)
+ {
+ if (empty($addr[1])) { // No name provided
+ return $this->secureHeader($addr[0]);
+ } else {
+ return $this->encodeHeader($this->secureHeader($addr[1]), 'phrase') . ' <' . $this->secureHeader(
+ $addr[0]
+ ) . '>';
+ }
+ }
+
+ /**
+ * Word-wrap message.
+ * For use with mailers that do not automatically perform wrapping
+ * and for quoted-printable encoded messages.
+ * Original written by philippe.
+ * @param string $message The message to wrap
+ * @param integer $length The line length to wrap to
+ * @param boolean $qp_mode Whether to run in Quoted-Printable mode
+ * @access public
+ * @return string
+ */
+ public function wrapText($message, $length, $qp_mode = false)
+ {
+ if ($qp_mode) {
+ $soft_break = sprintf(' =%s', $this->LE);
+ } else {
+ $soft_break = $this->LE;
+ }
+ // If utf-8 encoding is used, we will need to make sure we don't
+ // split multibyte characters when we wrap
+ $is_utf8 = (strtolower($this->CharSet) == 'utf-8');
+ $lelen = strlen($this->LE);
+ $crlflen = strlen(self::CRLF);
+
+ $message = $this->fixEOL($message);
+ //Remove a trailing line break
+ if (substr($message, -$lelen) == $this->LE) {
+ $message = substr($message, 0, -$lelen);
+ }
+
+ //Split message into lines
+ $lines = explode($this->LE, $message);
+ //Message will be rebuilt in here
+ $message = '';
+ foreach ($lines as $line) {
+ $words = explode(' ', $line);
+ $buf = '';
+ $firstword = true;
+ foreach ($words as $word) {
+ if ($qp_mode and (strlen($word) > $length)) {
+ $space_left = $length - strlen($buf) - $crlflen;
+ if (!$firstword) {
+ if ($space_left > 20) {
+ $len = $space_left;
+ if ($is_utf8) {
+ $len = $this->utf8CharBoundary($word, $len);
+ } elseif (substr($word, $len - 1, 1) == '=') {
+ $len--;
+ } elseif (substr($word, $len - 2, 1) == '=') {
+ $len -= 2;
+ }
+ $part = substr($word, 0, $len);
+ $word = substr($word, $len);
+ $buf .= ' ' . $part;
+ $message .= $buf . sprintf('=%s', self::CRLF);
+ } else {
+ $message .= $buf . $soft_break;
+ }
+ $buf = '';
+ }
+ while (strlen($word) > 0) {
+ if ($length <= 0) {
+ break;
+ }
+ $len = $length;
+ if ($is_utf8) {
+ $len = $this->utf8CharBoundary($word, $len);
+ } elseif (substr($word, $len - 1, 1) == '=') {
+ $len--;
+ } elseif (substr($word, $len - 2, 1) == '=') {
+ $len -= 2;
+ }
+ $part = substr($word, 0, $len);
+ $word = substr($word, $len);
+
+ if (strlen($word) > 0) {
+ $message .= $part . sprintf('=%s', self::CRLF);
+ } else {
+ $buf = $part;
+ }
+ }
+ } else {
+ $buf_o = $buf;
+ if (!$firstword) {
+ $buf .= ' ';
+ }
+ $buf .= $word;
+
+ if (strlen($buf) > $length and $buf_o != '') {
+ $message .= $buf_o . $soft_break;
+ $buf = $word;
+ }
+ }
+ $firstword = false;
+ }
+ $message .= $buf . self::CRLF;
+ }
+
+ return $message;
+ }
+
+ /**
+ * Find the last character boundary prior to $maxLength in a utf-8
+ * quoted-printable encoded string.
+ * Original written by Colin Brown.
+ * @access public
+ * @param string $encodedText utf-8 QP text
+ * @param integer $maxLength Find the last character boundary prior to this length
+ * @return integer
+ */
+ public function utf8CharBoundary($encodedText, $maxLength)
+ {
+ $foundSplitPos = false;
+ $lookBack = 3;
+ while (!$foundSplitPos) {
+ $lastChunk = substr($encodedText, $maxLength - $lookBack, $lookBack);
+ $encodedCharPos = strpos($lastChunk, '=');
+ if (false !== $encodedCharPos) {
+ // Found start of encoded character byte within $lookBack block.
+ // Check the encoded byte value (the 2 chars after the '=')
+ $hex = substr($encodedText, $maxLength - $lookBack + $encodedCharPos + 1, 2);
+ $dec = hexdec($hex);
+ if ($dec < 128) {
+ // Single byte character.
+ // If the encoded char was found at pos 0, it will fit
+ // otherwise reduce maxLength to start of the encoded char
+ if ($encodedCharPos > 0) {
+ $maxLength = $maxLength - ($lookBack - $encodedCharPos);
+ }
+ $foundSplitPos = true;
+ } elseif ($dec >= 192) {
+ // First byte of a multi byte character
+ // Reduce maxLength to split at start of character
+ $maxLength = $maxLength - ($lookBack - $encodedCharPos);
+ $foundSplitPos = true;
+ } elseif ($dec < 192) {
+ // Middle byte of a multi byte character, look further back
+ $lookBack += 3;
+ }
+ } else {
+ // No encoded character found
+ $foundSplitPos = true;
+ }
+ }
+ return $maxLength;
+ }
+
+ /**
+ * Apply word wrapping to the message body.
+ * Wraps the message body to the number of chars set in the WordWrap property.
+ * You should only do this to plain-text bodies as wrapping HTML tags may break them.
+ * This is called automatically by createBody(), so you don't need to call it yourself.
+ * @access public
+ * @return void
+ */
+ public function setWordWrap()
+ {
+ if ($this->WordWrap < 1) {
+ return;
+ }
+
+ switch ($this->message_type) {
+ case 'alt':
+ case 'alt_inline':
+ case 'alt_attach':
+ case 'alt_inline_attach':
+ $this->AltBody = $this->wrapText($this->AltBody, $this->WordWrap);
+ break;
+ default:
+ $this->Body = $this->wrapText($this->Body, $this->WordWrap);
+ break;
+ }
+ }
+
+ /**
+ * Assemble message headers.
+ * @access public
+ * @return string The assembled headers
+ */
+ public function createHeader()
+ {
+ $result = '';
+
+ if ($this->MessageDate == '') {
+ $this->MessageDate = self::rfcDate();
+ }
+ $result .= $this->headerLine('Date', $this->MessageDate);
+
+ // To be created automatically by mail()
+ if ($this->SingleTo) {
+ if ($this->Mailer != 'mail') {
+ foreach ($this->to as $toaddr) {
+ $this->SingleToArray[] = $this->addrFormat($toaddr);
+ }
+ }
+ } else {
+ if (count($this->to) > 0) {
+ if ($this->Mailer != 'mail') {
+ $result .= $this->addrAppend('To', $this->to);
+ }
+ } elseif (count($this->cc) == 0) {
+ $result .= $this->headerLine('To', 'undisclosed-recipients:;');
+ }
+ }
+
+ $result .= $this->addrAppend('From', array(array(trim($this->From), $this->FromName)));
+
+ // sendmail and mail() extract Cc from the header before sending
+ if (count($this->cc) > 0) {
+ $result .= $this->addrAppend('Cc', $this->cc);
+ }
+
+ // sendmail and mail() extract Bcc from the header before sending
+ if ((
+ $this->Mailer == 'sendmail' or $this->Mailer == 'qmail' or $this->Mailer == 'mail'
+ )
+ and count($this->bcc) > 0
+ ) {
+ $result .= $this->addrAppend('Bcc', $this->bcc);
+ }
+
+ if (count($this->ReplyTo) > 0) {
+ $result .= $this->addrAppend('Reply-To', $this->ReplyTo);
+ }
+
+ // mail() sets the subject itself
+ if ($this->Mailer != 'mail') {
+ $result .= $this->headerLine('Subject', $this->encodeHeader($this->secureHeader($this->Subject)));
+ }
+
+ if ('' != $this->MessageID and preg_match('/^<.*@.*>$/', $this->MessageID)) {
+ $this->lastMessageID = $this->MessageID;
+ } else {
+ $this->lastMessageID = sprintf('<%s@%s>', $this->uniqueid, $this->serverHostname());
+ }
+ $result .= $this->headerLine('Message-ID', $this->lastMessageID);
+ if (!is_null($this->Priority)) {
+ $result .= $this->headerLine('X-Priority', $this->Priority);
+ }
+ if ($this->XMailer == '') {
+ $result .= $this->headerLine(
+ 'X-Mailer',
+ 'PHPMailer ' . $this->Version . ' (https://github.com/PHPMailer/PHPMailer)'
+ );
+ } else {
+ $myXmailer = trim($this->XMailer);
+ if ($myXmailer) {
+ $result .= $this->headerLine('X-Mailer', $myXmailer);
+ }
+ }
+
+ if ($this->ConfirmReadingTo != '') {
+ $result .= $this->headerLine('Disposition-Notification-To', '<' . $this->ConfirmReadingTo . '>');
+ }
+
+ // Add custom headers
+ foreach ($this->CustomHeader as $header) {
+ $result .= $this->headerLine(
+ trim($header[0]),
+ $this->encodeHeader(trim($header[1]))
+ );
+ }
+ if (!$this->sign_key_file) {
+ $result .= $this->headerLine('MIME-Version', '1.0');
+ $result .= $this->getMailMIME();
+ }
+
+ return $result;
+ }
+
+ /**
+ * Get the message MIME type headers.
+ * @access public
+ * @return string
+ */
+ public function getMailMIME()
+ {
+ $result = '';
+ $ismultipart = true;
+ switch ($this->message_type) {
+ case 'inline':
+ $result .= $this->headerLine('Content-Type', 'multipart/related;');
+ $result .= $this->textLine("\tboundary=\"" . $this->boundary[1] . '"');
+ break;
+ case 'attach':
+ case 'inline_attach':
+ case 'alt_attach':
+ case 'alt_inline_attach':
+ $result .= $this->headerLine('Content-Type', 'multipart/mixed;');
+ $result .= $this->textLine("\tboundary=\"" . $this->boundary[1] . '"');
+ break;
+ case 'alt':
+ case 'alt_inline':
+ $result .= $this->headerLine('Content-Type', 'multipart/alternative;');
+ $result .= $this->textLine("\tboundary=\"" . $this->boundary[1] . '"');
+ break;
+ default:
+ // Catches case 'plain': and case '':
+ $result .= $this->textLine('Content-Type: ' . $this->ContentType . '; charset=' . $this->CharSet);
+ $ismultipart = false;
+ break;
+ }
+ // RFC1341 part 5 says 7bit is assumed if not specified
+ if ($this->Encoding != '7bit') {
+ // RFC 2045 section 6.4 says multipart MIME parts may only use 7bit, 8bit or binary CTE
+ if ($ismultipart) {
+ if ($this->Encoding == '8bit') {
+ $result .= $this->headerLine('Content-Transfer-Encoding', '8bit');
+ }
+ // The only remaining alternatives are quoted-printable and base64, which are both 7bit compatible
+ } else {
+ $result .= $this->headerLine('Content-Transfer-Encoding', $this->Encoding);
+ }
+ }
+
+ if ($this->Mailer != 'mail') {
+ $result .= $this->LE;
+ }
+
+ return $result;
+ }
+
+ /**
+ * Returns the whole MIME message.
+ * Includes complete headers and body.
+ * Only valid post preSend().
+ * @see PHPMailer::preSend()
+ * @access public
+ * @return string
+ */
+ public function getSentMIMEMessage()
+ {
+ return rtrim($this->MIMEHeader . $this->mailHeader, "\n\r") . self::CRLF . self::CRLF . $this->MIMEBody;
+ }
+
+ /**
+ * Assemble the message body.
+ * Returns an empty string on failure.
+ * @access public
+ * @throws phpmailerException
+ * @return string The assembled message body
+ */
+ public function createBody()
+ {
+ $body = '';
+ //Create unique IDs and preset boundaries
+ $this->uniqueid = md5(uniqid(time()));
+ $this->boundary[1] = 'b1_' . $this->uniqueid;
+ $this->boundary[2] = 'b2_' . $this->uniqueid;
+ $this->boundary[3] = 'b3_' . $this->uniqueid;
+
+ if ($this->sign_key_file) {
+ $body .= $this->getMailMIME() . $this->LE;
+ }
+
+ $this->setWordWrap();
+
+ $bodyEncoding = $this->Encoding;
+ $bodyCharSet = $this->CharSet;
+ //Can we do a 7-bit downgrade?
+ if ($bodyEncoding == '8bit' and !$this->has8bitChars($this->Body)) {
+ $bodyEncoding = '7bit';
+ $bodyCharSet = 'us-ascii';
+ }
+ //If lines are too long, and we're not already using an encoding that will shorten them,
+ //change to quoted-printable transfer encoding
+ if ('base64' != $this->Encoding and self::hasLineLongerThanMax($this->Body)) {
+ $this->Encoding = 'quoted-printable';
+ $bodyEncoding = 'quoted-printable';
+ }
+
+ $altBodyEncoding = $this->Encoding;
+ $altBodyCharSet = $this->CharSet;
+ //Can we do a 7-bit downgrade?
+ if ($altBodyEncoding == '8bit' and !$this->has8bitChars($this->AltBody)) {
+ $altBodyEncoding = '7bit';
+ $altBodyCharSet = 'us-ascii';
+ }
+ //If lines are too long, and we're not already using an encoding that will shorten them,
+ //change to quoted-printable transfer encoding
+ if ('base64' != $altBodyEncoding and self::hasLineLongerThanMax($this->AltBody)) {
+ $altBodyEncoding = 'quoted-printable';
+ }
+ //Use this as a preamble in all multipart message types
+ $mimepre = "This is a multi-part message in MIME format." . $this->LE . $this->LE;
+ switch ($this->message_type) {
+ case 'inline':
+ $body .= $mimepre;
+ $body .= $this->getBoundary($this->boundary[1], $bodyCharSet, '', $bodyEncoding);
+ $body .= $this->encodeString($this->Body, $bodyEncoding);
+ $body .= $this->LE . $this->LE;
+ $body .= $this->attachAll('inline', $this->boundary[1]);
+ break;
+ case 'attach':
+ $body .= $mimepre;
+ $body .= $this->getBoundary($this->boundary[1], $bodyCharSet, '', $bodyEncoding);
+ $body .= $this->encodeString($this->Body, $bodyEncoding);
+ $body .= $this->LE . $this->LE;
+ $body .= $this->attachAll('attachment', $this->boundary[1]);
+ break;
+ case 'inline_attach':
+ $body .= $mimepre;
+ $body .= $this->textLine('--' . $this->boundary[1]);
+ $body .= $this->headerLine('Content-Type', 'multipart/related;');
+ $body .= $this->textLine("\tboundary=\"" . $this->boundary[2] . '"');
+ $body .= $this->LE;
+ $body .= $this->getBoundary($this->boundary[2], $bodyCharSet, '', $bodyEncoding);
+ $body .= $this->encodeString($this->Body, $bodyEncoding);
+ $body .= $this->LE . $this->LE;
+ $body .= $this->attachAll('inline', $this->boundary[2]);
+ $body .= $this->LE;
+ $body .= $this->attachAll('attachment', $this->boundary[1]);
+ break;
+ case 'alt':
+ $body .= $mimepre;
+ $body .= $this->getBoundary($this->boundary[1], $altBodyCharSet, 'text/plain', $altBodyEncoding);
+ $body .= $this->encodeString($this->AltBody, $altBodyEncoding);
+ $body .= $this->LE . $this->LE;
+ $body .= $this->getBoundary($this->boundary[1], $bodyCharSet, 'text/html', $bodyEncoding);
+ $body .= $this->encodeString($this->Body, $bodyEncoding);
+ $body .= $this->LE . $this->LE;
+ if (!empty($this->Ical)) {
+ $body .= $this->getBoundary($this->boundary[1], '', 'text/calendar; method=REQUEST', '');
+ $body .= $this->encodeString($this->Ical, $this->Encoding);
+ $body .= $this->LE . $this->LE;
+ }
+ $body .= $this->endBoundary($this->boundary[1]);
+ break;
+ case 'alt_inline':
+ $body .= $mimepre;
+ $body .= $this->getBoundary($this->boundary[1], $altBodyCharSet, 'text/plain', $altBodyEncoding);
+ $body .= $this->encodeString($this->AltBody, $altBodyEncoding);
+ $body .= $this->LE . $this->LE;
+ $body .= $this->textLine('--' . $this->boundary[1]);
+ $body .= $this->headerLine('Content-Type', 'multipart/related;');
+ $body .= $this->textLine("\tboundary=\"" . $this->boundary[2] . '"');
+ $body .= $this->LE;
+ $body .= $this->getBoundary($this->boundary[2], $bodyCharSet, 'text/html', $bodyEncoding);
+ $body .= $this->encodeString($this->Body, $bodyEncoding);
+ $body .= $this->LE . $this->LE;
+ $body .= $this->attachAll('inline', $this->boundary[2]);
+ $body .= $this->LE;
+ $body .= $this->endBoundary($this->boundary[1]);
+ break;
+ case 'alt_attach':
+ $body .= $mimepre;
+ $body .= $this->textLine('--' . $this->boundary[1]);
+ $body .= $this->headerLine('Content-Type', 'multipart/alternative;');
+ $body .= $this->textLine("\tboundary=\"" . $this->boundary[2] . '"');
+ $body .= $this->LE;
+ $body .= $this->getBoundary($this->boundary[2], $altBodyCharSet, 'text/plain', $altBodyEncoding);
+ $body .= $this->encodeString($this->AltBody, $altBodyEncoding);
+ $body .= $this->LE . $this->LE;
+ $body .= $this->getBoundary($this->boundary[2], $bodyCharSet, 'text/html', $bodyEncoding);
+ $body .= $this->encodeString($this->Body, $bodyEncoding);
+ $body .= $this->LE . $this->LE;
+ $body .= $this->endBoundary($this->boundary[2]);
+ $body .= $this->LE;
+ $body .= $this->attachAll('attachment', $this->boundary[1]);
+ break;
+ case 'alt_inline_attach':
+ $body .= $mimepre;
+ $body .= $this->textLine('--' . $this->boundary[1]);
+ $body .= $this->headerLine('Content-Type', 'multipart/alternative;');
+ $body .= $this->textLine("\tboundary=\"" . $this->boundary[2] . '"');
+ $body .= $this->LE;
+ $body .= $this->getBoundary($this->boundary[2], $altBodyCharSet, 'text/plain', $altBodyEncoding);
+ $body .= $this->encodeString($this->AltBody, $altBodyEncoding);
+ $body .= $this->LE . $this->LE;
+ $body .= $this->textLine('--' . $this->boundary[2]);
+ $body .= $this->headerLine('Content-Type', 'multipart/related;');
+ $body .= $this->textLine("\tboundary=\"" . $this->boundary[3] . '"');
+ $body .= $this->LE;
+ $body .= $this->getBoundary($this->boundary[3], $bodyCharSet, 'text/html', $bodyEncoding);
+ $body .= $this->encodeString($this->Body, $bodyEncoding);
+ $body .= $this->LE . $this->LE;
+ $body .= $this->attachAll('inline', $this->boundary[3]);
+ $body .= $this->LE;
+ $body .= $this->endBoundary($this->boundary[2]);
+ $body .= $this->LE;
+ $body .= $this->attachAll('attachment', $this->boundary[1]);
+ break;
+ default:
+ // catch case 'plain' and case ''
+ $body .= $this->encodeString($this->Body, $bodyEncoding);
+ break;
+ }
+
+ if ($this->isError()) {
+ $body = '';
+ } elseif ($this->sign_key_file) {
+ try {
+ if (!defined('PKCS7_TEXT')) {
+ throw new phpmailerException($this->lang('extension_missing') . 'openssl');
+ }
+ // @TODO would be nice to use php://temp streams here, but need to wrap for PHP < 5.1
+ $file = tempnam(sys_get_temp_dir(), 'mail');
+ if (false === file_put_contents($file, $body)) {
+ throw new phpmailerException($this->lang('signing') . ' Could not write temp file');
+ }
+ $signed = tempnam(sys_get_temp_dir(), 'signed');
+ //Workaround for PHP bug https://bugs.php.net/bug.php?id=69197
+ if (empty($this->sign_extracerts_file)) {
+ $sign = @openssl_pkcs7_sign(
+ $file,
+ $signed,
+ 'file://' . realpath($this->sign_cert_file),
+ array('file://' . realpath($this->sign_key_file), $this->sign_key_pass),
+ null
+ );
+ } else {
+ $sign = @openssl_pkcs7_sign(
+ $file,
+ $signed,
+ 'file://' . realpath($this->sign_cert_file),
+ array('file://' . realpath($this->sign_key_file), $this->sign_key_pass),
+ null,
+ PKCS7_DETACHED,
+ $this->sign_extracerts_file
+ );
+ }
+ if ($sign) {
+ @unlink($file);
+ $body = file_get_contents($signed);
+ @unlink($signed);
+ //The message returned by openssl contains both headers and body, so need to split them up
+ $parts = explode("\n\n", $body, 2);
+ $this->MIMEHeader .= $parts[0] . $this->LE . $this->LE;
+ $body = $parts[1];
+ } else {
+ @unlink($file);
+ @unlink($signed);
+ throw new phpmailerException($this->lang('signing') . openssl_error_string());
+ }
+ } catch (phpmailerException $exc) {
+ $body = '';
+ if ($this->exceptions) {
+ throw $exc;
+ }
+ }
+ }
+ return $body;
+ }
+
+ /**
+ * Return the start of a message boundary.
+ * @access protected
+ * @param string $boundary
+ * @param string $charSet
+ * @param string $contentType
+ * @param string $encoding
+ * @return string
+ */
+ protected function getBoundary($boundary, $charSet, $contentType, $encoding)
+ {
+ $result = '';
+ if ($charSet == '') {
+ $charSet = $this->CharSet;
+ }
+ if ($contentType == '') {
+ $contentType = $this->ContentType;
+ }
+ if ($encoding == '') {
+ $encoding = $this->Encoding;
+ }
+ $result .= $this->textLine('--' . $boundary);
+ $result .= sprintf('Content-Type: %s; charset=%s', $contentType, $charSet);
+ $result .= $this->LE;
+ // RFC1341 part 5 says 7bit is assumed if not specified
+ if ($encoding != '7bit') {
+ $result .= $this->headerLine('Content-Transfer-Encoding', $encoding);
+ }
+ $result .= $this->LE;
+
+ return $result;
+ }
+
+ /**
+ * Return the end of a message boundary.
+ * @access protected
+ * @param string $boundary
+ * @return string
+ */
+ protected function endBoundary($boundary)
+ {
+ return $this->LE . '--' . $boundary . '--' . $this->LE;
+ }
+
+ /**
+ * Set the message type.
+ * PHPMailer only supports some preset message types,
+ * not arbitrary MIME structures.
+ * @access protected
+ * @return void
+ */
+ protected function setMessageType()
+ {
+ $type = array();
+ if ($this->alternativeExists()) {
+ $type[] = 'alt';
+ }
+ if ($this->inlineImageExists()) {
+ $type[] = 'inline';
+ }
+ if ($this->attachmentExists()) {
+ $type[] = 'attach';
+ }
+ $this->message_type = implode('_', $type);
+ if ($this->message_type == '') {
+ $this->message_type = 'plain';
+ }
+ }
+
+ /**
+ * Format a header line.
+ * @access public
+ * @param string $name
+ * @param string $value
+ * @return string
+ */
+ public function headerLine($name, $value)
+ {
+ return $name . ': ' . $value . $this->LE;
+ }
+
+ /**
+ * Return a formatted mail line.
+ * @access public
+ * @param string $value
+ * @return string
+ */
+ public function textLine($value)
+ {
+ return $value . $this->LE;
+ }
+
+ /**
+ * Add an attachment from a path on the filesystem.
+ * Returns false if the file could not be found or read.
+ * @param string $path Path to the attachment.
+ * @param string $name Overrides the attachment name.
+ * @param string $encoding File encoding (see $Encoding).
+ * @param string $type File extension (MIME) type.
+ * @param string $disposition Disposition to use
+ * @throws phpmailerException
+ * @return boolean
+ */
+ public function addAttachment($path, $name = '', $encoding = 'base64', $type = '', $disposition = 'attachment')
+ {
+ try {
+ if (!@is_file($path)) {
+ throw new phpmailerException($this->lang('file_access') . $path, self::STOP_CONTINUE);
+ }
+
+ // If a MIME type is not specified, try to work it out from the file name
+ if ($type == '') {
+ $type = self::filenameToType($path);
+ }
+
+ $filename = basename($path);
+ if ($name == '') {
+ $name = $filename;
+ }
+
+ $this->attachment[] = array(
+ 0 => $path,
+ 1 => $filename,
+ 2 => $name,
+ 3 => $encoding,
+ 4 => $type,
+ 5 => false, // isStringAttachment
+ 6 => $disposition,
+ 7 => 0
+ );
+
+ } catch (phpmailerException $exc) {
+ $this->setError($exc->getMessage());
+ $this->edebug($exc->getMessage());
+ if ($this->exceptions) {
+ throw $exc;
+ }
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * Return the array of attachments.
+ * @return array
+ */
+ public function getAttachments()
+ {
+ return $this->attachment;
+ }
+
+ /**
+ * Attach all file, string, and binary attachments to the message.
+ * Returns an empty string on failure.
+ * @access protected
+ * @param string $disposition_type
+ * @param string $boundary
+ * @return string
+ */
+ protected function attachAll($disposition_type, $boundary)
+ {
+ // Return text of body
+ $mime = array();
+ $cidUniq = array();
+ $incl = array();
+
+ // Add all attachments
+ foreach ($this->attachment as $attachment) {
+ // Check if it is a valid disposition_filter
+ if ($attachment[6] == $disposition_type) {
+ // Check for string attachment
+ $string = '';
+ $path = '';
+ $bString = $attachment[5];
+ if ($bString) {
+ $string = $attachment[0];
+ } else {
+ $path = $attachment[0];
+ }
+
+ $inclhash = md5(serialize($attachment));
+ if (in_array($inclhash, $incl)) {
+ continue;
+ }
+ $incl[] = $inclhash;
+ $name = $attachment[2];
+ $encoding = $attachment[3];
+ $type = $attachment[4];
+ $disposition = $attachment[6];
+ $cid = $attachment[7];
+ if ($disposition == 'inline' && array_key_exists($cid, $cidUniq)) {
+ continue;
+ }
+ $cidUniq[$cid] = true;
+
+ $mime[] = sprintf('--%s%s', $boundary, $this->LE);
+ //Only include a filename property if we have one
+ if (!empty($name)) {
+ $mime[] = sprintf(
+ 'Content-Type: %s; name="%s"%s',
+ $type,
+ $this->encodeHeader($this->secureHeader($name)),
+ $this->LE
+ );
+ } else {
+ $mime[] = sprintf(
+ 'Content-Type: %s%s',
+ $type,
+ $this->LE
+ );
+ }
+ // RFC1341 part 5 says 7bit is assumed if not specified
+ if ($encoding != '7bit') {
+ $mime[] = sprintf('Content-Transfer-Encoding: %s%s', $encoding, $this->LE);
+ }
+
+ if ($disposition == 'inline') {
+ $mime[] = sprintf('Content-ID: <%s>%s', $cid, $this->LE);
+ }
+
+ // If a filename contains any of these chars, it should be quoted,
+ // but not otherwise: RFC2183 & RFC2045 5.1
+ // Fixes a warning in IETF's msglint MIME checker
+ // Allow for bypassing the Content-Disposition header totally
+ if (!(empty($disposition))) {
+ $encoded_name = $this->encodeHeader($this->secureHeader($name));
+ if (preg_match('/[ \(\)<>@,;:\\"\/\[\]\?=]/', $encoded_name)) {
+ $mime[] = sprintf(
+ 'Content-Disposition: %s; filename="%s"%s',
+ $disposition,
+ $encoded_name,
+ $this->LE . $this->LE
+ );
+ } else {
+ if (!empty($encoded_name)) {
+ $mime[] = sprintf(
+ 'Content-Disposition: %s; filename=%s%s',
+ $disposition,
+ $encoded_name,
+ $this->LE . $this->LE
+ );
+ } else {
+ $mime[] = sprintf(
+ 'Content-Disposition: %s%s',
+ $disposition,
+ $this->LE . $this->LE
+ );
+ }
+ }
+ } else {
+ $mime[] = $this->LE;
+ }
+
+ // Encode as string attachment
+ if ($bString) {
+ $mime[] = $this->encodeString($string, $encoding);
+ if ($this->isError()) {
+ return '';
+ }
+ $mime[] = $this->LE . $this->LE;
+ } else {
+ $mime[] = $this->encodeFile($path, $encoding);
+ if ($this->isError()) {
+ return '';
+ }
+ $mime[] = $this->LE . $this->LE;
+ }
+ }
+ }
+
+ $mime[] = sprintf('--%s--%s', $boundary, $this->LE);
+
+ return implode('', $mime);
+ }
+
+ /**
+ * Encode a file attachment in requested format.
+ * Returns an empty string on failure.
+ * @param string $path The full path to the file
+ * @param string $encoding The encoding to use; one of 'base64', '7bit', '8bit', 'binary', 'quoted-printable'
+ * @throws phpmailerException
+ * @access protected
+ * @return string
+ */
+ protected function encodeFile($path, $encoding = 'base64')
+ {
+ try {
+ if (!is_readable($path)) {
+ throw new phpmailerException($this->lang('file_open') . $path, self::STOP_CONTINUE);
+ }
+ $magic_quotes = get_magic_quotes_runtime();
+ if ($magic_quotes) {
+ if (version_compare(PHP_VERSION, '5.3.0', '<')) {
+ set_magic_quotes_runtime(false);
+ } else {
+ //Doesn't exist in PHP 5.4, but we don't need to check because
+ //get_magic_quotes_runtime always returns false in 5.4+
+ //so it will never get here
+ ini_set('magic_quotes_runtime', false);
+ }
+ }
+ $file_buffer = file_get_contents($path);
+ $file_buffer = $this->encodeString($file_buffer, $encoding);
+ if ($magic_quotes) {
+ if (version_compare(PHP_VERSION, '5.3.0', '<')) {
+ set_magic_quotes_runtime($magic_quotes);
+ } else {
+ ini_set('magic_quotes_runtime', $magic_quotes);
+ }
+ }
+ return $file_buffer;
+ } catch (Exception $exc) {
+ $this->setError($exc->getMessage());
+ return '';
+ }
+ }
+
+ /**
+ * Encode a string in requested format.
+ * Returns an empty string on failure.
+ * @param string $str The text to encode
+ * @param string $encoding The encoding to use; one of 'base64', '7bit', '8bit', 'binary', 'quoted-printable'
+ * @access public
+ * @return string
+ */
+ public function encodeString($str, $encoding = 'base64')
+ {
+ $encoded = '';
+ switch (strtolower($encoding)) {
+ case 'base64':
+ $encoded = chunk_split(base64_encode($str), 76, $this->LE);
+ break;
+ case '7bit':
+ case '8bit':
+ $encoded = $this->fixEOL($str);
+ // Make sure it ends with a line break
+ if (substr($encoded, -(strlen($this->LE))) != $this->LE) {
+ $encoded .= $this->LE;
+ }
+ break;
+ case 'binary':
+ $encoded = $str;
+ break;
+ case 'quoted-printable':
+ $encoded = $this->encodeQP($str);
+ break;
+ default:
+ $this->setError($this->lang('encoding') . $encoding);
+ break;
+ }
+ return $encoded;
+ }
+
+ /**
+ * Encode a header string optimally.
+ * Picks shortest of Q, B, quoted-printable or none.
+ * @access public
+ * @param string $str
+ * @param string $position
+ * @return string
+ */
+ public function encodeHeader($str, $position = 'text')
+ {
+ $matchcount = 0;
+ switch (strtolower($position)) {
+ case 'phrase':
+ if (!preg_match('/[\200-\377]/', $str)) {
+ // Can't use addslashes as we don't know the value of magic_quotes_sybase
+ $encoded = addcslashes($str, "\0..\37\177\\\"");
+ if (($str == $encoded) && !preg_match('/[^A-Za-z0-9!#$%&\'*+\/=?^_`{|}~ -]/', $str)) {
+ return ($encoded);
+ } else {
+ return ("\"$encoded\"");
+ }
+ }
+ $matchcount = preg_match_all('/[^\040\041\043-\133\135-\176]/', $str, $matches);
+ break;
+ /** @noinspection PhpMissingBreakStatementInspection */
+ case 'comment':
+ $matchcount = preg_match_all('/[()"]/', $str, $matches);
+ // Intentional fall-through
+ case 'text':
+ default:
+ $matchcount += preg_match_all('/[\000-\010\013\014\016-\037\177-\377]/', $str, $matches);
+ break;
+ }
+
+ //There are no chars that need encoding
+ if ($matchcount == 0) {
+ return ($str);
+ }
+
+ $maxlen = 75 - 7 - strlen($this->CharSet);
+ // Try to select the encoding which should produce the shortest output
+ if ($matchcount > strlen($str) / 3) {
+ // More than a third of the content will need encoding, so B encoding will be most efficient
+ $encoding = 'B';
+ if (function_exists('mb_strlen') && $this->hasMultiBytes($str)) {
+ // Use a custom function which correctly encodes and wraps long
+ // multibyte strings without breaking lines within a character
+ $encoded = $this->base64EncodeWrapMB($str, "\n");
+ } else {
+ $encoded = base64_encode($str);
+ $maxlen -= $maxlen % 4;
+ $encoded = trim(chunk_split($encoded, $maxlen, "\n"));
+ }
+ } else {
+ $encoding = 'Q';
+ $encoded = $this->encodeQ($str, $position);
+ $encoded = $this->wrapText($encoded, $maxlen, true);
+ $encoded = str_replace('=' . self::CRLF, "\n", trim($encoded));
+ }
+
+ $encoded = preg_replace('/^(.*)$/m', ' =?' . $this->CharSet . "?$encoding?\\1?=", $encoded);
+ $encoded = trim(str_replace("\n", $this->LE, $encoded));
+
+ return $encoded;
+ }
+
+ /**
+ * Check if a string contains multi-byte characters.
+ * @access public
+ * @param string $str multi-byte text to wrap encode
+ * @return boolean
+ */
+ public function hasMultiBytes($str)
+ {
+ if (function_exists('mb_strlen')) {
+ return (strlen($str) > mb_strlen($str, $this->CharSet));
+ } else { // Assume no multibytes (we can't handle without mbstring functions anyway)
+ return false;
+ }
+ }
+
+ /**
+ * Does a string contain any 8-bit chars (in any charset)?
+ * @param string $text
+ * @return boolean
+ */
+ public function has8bitChars($text)
+ {
+ return (boolean)preg_match('/[\x80-\xFF]/', $text);
+ }
+
+ /**
+ * Encode and wrap long multibyte strings for mail headers
+ * without breaking lines within a character.
+ * Adapted from a function by paravoid
+ * @link http://www.php.net/manual/en/function.mb-encode-mimeheader.php#60283
+ * @access public
+ * @param string $str multi-byte text to wrap encode
+ * @param string $linebreak string to use as linefeed/end-of-line
+ * @return string
+ */
+ public function base64EncodeWrapMB($str, $linebreak = null)
+ {
+ $start = '=?' . $this->CharSet . '?B?';
+ $end = '?=';
+ $encoded = '';
+ if ($linebreak === null) {
+ $linebreak = $this->LE;
+ }
+
+ $mb_length = mb_strlen($str, $this->CharSet);
+ // Each line must have length <= 75, including $start and $end
+ $length = 75 - strlen($start) - strlen($end);
+ // Average multi-byte ratio
+ $ratio = $mb_length / strlen($str);
+ // Base64 has a 4:3 ratio
+ $avgLength = floor($length * $ratio * .75);
+
+ for ($i = 0; $i < $mb_length; $i += $offset) {
+ $lookBack = 0;
+ do {
+ $offset = $avgLength - $lookBack;
+ $chunk = mb_substr($str, $i, $offset, $this->CharSet);
+ $chunk = base64_encode($chunk);
+ $lookBack++;
+ } while (strlen($chunk) > $length);
+ $encoded .= $chunk . $linebreak;
+ }
+
+ // Chomp the last linefeed
+ $encoded = substr($encoded, 0, -strlen($linebreak));
+ return $encoded;
+ }
+
+ /**
+ * Encode a string in quoted-printable format.
+ * According to RFC2045 section 6.7.
+ * @access public
+ * @param string $string The text to encode
+ * @param integer $line_max Number of chars allowed on a line before wrapping
+ * @return string
+ * @link http://www.php.net/manual/en/function.quoted-printable-decode.php#89417 Adapted from this comment
+ */
+ public function encodeQP($string, $line_max = 76)
+ {
+ // Use native function if it's available (>= PHP5.3)
+ if (function_exists('quoted_printable_encode')) {
+ return quoted_printable_encode($string);
+ }
+ // Fall back to a pure PHP implementation
+ $string = str_replace(
+ array('%20', '%0D%0A.', '%0D%0A', '%'),
+ array(' ', "\r\n=2E", "\r\n", '='),
+ rawurlencode($string)
+ );
+ return preg_replace('/[^\r\n]{' . ($line_max - 3) . '}[^=\r\n]{2}/', "$0=\r\n", $string);
+ }
+
+ /**
+ * Backward compatibility wrapper for an old QP encoding function that was removed.
+ * @see PHPMailer::encodeQP()
+ * @access public
+ * @param string $string
+ * @param integer $line_max
+ * @param boolean $space_conv
+ * @return string
+ * @deprecated Use encodeQP instead.
+ */
+ public function encodeQPphp(
+ $string,
+ $line_max = 76,
+ /** @noinspection PhpUnusedParameterInspection */ $space_conv = false
+ ) {
+ return $this->encodeQP($string, $line_max);
+ }
+
+ /**
+ * Encode a string using Q encoding.
+ * @link http://tools.ietf.org/html/rfc2047
+ * @param string $str the text to encode
+ * @param string $position Where the text is going to be used, see the RFC for what that means
+ * @access public
+ * @return string
+ */
+ public function encodeQ($str, $position = 'text')
+ {
+ // There should not be any EOL in the string
+ $pattern = '';
+ $encoded = str_replace(array("\r", "\n"), '', $str);
+ switch (strtolower($position)) {
+ case 'phrase':
+ // RFC 2047 section 5.3
+ $pattern = '^A-Za-z0-9!*+\/ -';
+ break;
+ /** @noinspection PhpMissingBreakStatementInspection */
+ case 'comment':
+ // RFC 2047 section 5.2
+ $pattern = '\(\)"';
+ // intentional fall-through
+ // for this reason we build the $pattern without including delimiters and []
+ case 'text':
+ default:
+ // RFC 2047 section 5.1
+ // Replace every high ascii, control, =, ? and _ characters
+ $pattern = '\000-\011\013\014\016-\037\075\077\137\177-\377' . $pattern;
+ break;
+ }
+ $matches = array();
+ if (preg_match_all("/[{$pattern}]/", $encoded, $matches)) {
+ // If the string contains an '=', make sure it's the first thing we replace
+ // so as to avoid double-encoding
+ $eqkey = array_search('=', $matches[0]);
+ if (false !== $eqkey) {
+ unset($matches[0][$eqkey]);
+ array_unshift($matches[0], '=');
+ }
+ foreach (array_unique($matches[0]) as $char) {
+ $encoded = str_replace($char, '=' . sprintf('%02X', ord($char)), $encoded);
+ }
+ }
+ // Replace every spaces to _ (more readable than =20)
+ return str_replace(' ', '_', $encoded);
+ }
+
+ /**
+ * Add a string or binary attachment (non-filesystem).
+ * This method can be used to attach ascii or binary data,
+ * such as a BLOB record from a database.
+ * @param string $string String attachment data.
+ * @param string $filename Name of the attachment.
+ * @param string $encoding File encoding (see $Encoding).
+ * @param string $type File extension (MIME) type.
+ * @param string $disposition Disposition to use
+ * @return void
+ */
+ public function addStringAttachment(
+ $string,
+ $filename,
+ $encoding = 'base64',
+ $type = '',
+ $disposition = 'attachment'
+ ) {
+ // If a MIME type is not specified, try to work it out from the file name
+ if ($type == '') {
+ $type = self::filenameToType($filename);
+ }
+ // Append to $attachment array
+ $this->attachment[] = array(
+ 0 => $string,
+ 1 => $filename,
+ 2 => basename($filename),
+ 3 => $encoding,
+ 4 => $type,
+ 5 => true, // isStringAttachment
+ 6 => $disposition,
+ 7 => 0
+ );
+ }
+
+ /**
+ * Add an embedded (inline) attachment from a file.
+ * This can include images, sounds, and just about any other document type.
+ * These differ from 'regular' attachments in that they are intended to be
+ * displayed inline with the message, not just attached for download.
+ * This is used in HTML messages that embed the images
+ * the HTML refers to using the $cid value.
+ * @param string $path Path to the attachment.
+ * @param string $cid Content ID of the attachment; Use this to reference
+ * the content when using an embedded image in HTML.
+ * @param string $name Overrides the attachment name.
+ * @param string $encoding File encoding (see $Encoding).
+ * @param string $type File MIME type.
+ * @param string $disposition Disposition to use
+ * @return boolean True on successfully adding an attachment
+ */
+ public function addEmbeddedImage($path, $cid, $name = '', $encoding = 'base64', $type = '', $disposition = 'inline')
+ {
+ if (!@is_file($path)) {
+ $this->setError($this->lang('file_access') . $path);
+ return false;
+ }
+
+ // If a MIME type is not specified, try to work it out from the file name
+ if ($type == '') {
+ $type = self::filenameToType($path);
+ }
+
+ $filename = basename($path);
+ if ($name == '') {
+ $name = $filename;
+ }
+
+ // Append to $attachment array
+ $this->attachment[] = array(
+ 0 => $path,
+ 1 => $filename,
+ 2 => $name,
+ 3 => $encoding,
+ 4 => $type,
+ 5 => false, // isStringAttachment
+ 6 => $disposition,
+ 7 => $cid
+ );
+ return true;
+ }
+
+ /**
+ * Add an embedded stringified attachment.
+ * This can include images, sounds, and just about any other document type.
+ * Be sure to set the $type to an image type for images:
+ * JPEG images use 'image/jpeg', GIF uses 'image/gif', PNG uses 'image/png'.
+ * @param string $string The attachment binary data.
+ * @param string $cid Content ID of the attachment; Use this to reference
+ * the content when using an embedded image in HTML.
+ * @param string $name
+ * @param string $encoding File encoding (see $Encoding).
+ * @param string $type MIME type.
+ * @param string $disposition Disposition to use
+ * @return boolean True on successfully adding an attachment
+ */
+ public function addStringEmbeddedImage(
+ $string,
+ $cid,
+ $name = '',
+ $encoding = 'base64',
+ $type = '',
+ $disposition = 'inline'
+ ) {
+ // If a MIME type is not specified, try to work it out from the name
+ if ($type == '' and !empty($name)) {
+ $type = self::filenameToType($name);
+ }
+
+ // Append to $attachment array
+ $this->attachment[] = array(
+ 0 => $string,
+ 1 => $name,
+ 2 => $name,
+ 3 => $encoding,
+ 4 => $type,
+ 5 => true, // isStringAttachment
+ 6 => $disposition,
+ 7 => $cid
+ );
+ return true;
+ }
+
+ /**
+ * Check if an inline attachment is present.
+ * @access public
+ * @return boolean
+ */
+ public function inlineImageExists()
+ {
+ foreach ($this->attachment as $attachment) {
+ if ($attachment[6] == 'inline') {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Check if an attachment (non-inline) is present.
+ * @return boolean
+ */
+ public function attachmentExists()
+ {
+ foreach ($this->attachment as $attachment) {
+ if ($attachment[6] == 'attachment') {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Check if this message has an alternative body set.
+ * @return boolean
+ */
+ public function alternativeExists()
+ {
+ return !empty($this->AltBody);
+ }
+
+ /**
+ * Clear queued addresses of given kind.
+ * @access protected
+ * @param string $kind 'to', 'cc', or 'bcc'
+ * @return void
+ */
+ public function clearQueuedAddresses($kind)
+ {
+ $RecipientsQueue = $this->RecipientsQueue;
+ foreach ($RecipientsQueue as $address => $params) {
+ if ($params[0] == $kind) {
+ unset($this->RecipientsQueue[$address]);
+ }
+ }
+ }
+
+ /**
+ * Clear all To recipients.
+ * @return void
+ */
+ public function clearAddresses()
+ {
+ foreach ($this->to as $to) {
+ unset($this->all_recipients[strtolower($to[0])]);
+ }
+ $this->to = array();
+ $this->clearQueuedAddresses('to');
+ }
+
+ /**
+ * Clear all CC recipients.
+ * @return void
+ */
+ public function clearCCs()
+ {
+ foreach ($this->cc as $cc) {
+ unset($this->all_recipients[strtolower($cc[0])]);
+ }
+ $this->cc = array();
+ $this->clearQueuedAddresses('cc');
+ }
+
+ /**
+ * Clear all BCC recipients.
+ * @return void
+ */
+ public function clearBCCs()
+ {
+ foreach ($this->bcc as $bcc) {
+ unset($this->all_recipients[strtolower($bcc[0])]);
+ }
+ $this->bcc = array();
+ $this->clearQueuedAddresses('bcc');
+ }
+
+ /**
+ * Clear all ReplyTo recipients.
+ * @return void
+ */
+ public function clearReplyTos()
+ {
+ $this->ReplyTo = array();
+ $this->ReplyToQueue = array();
+ }
+
+ /**
+ * Clear all recipient types.
+ * @return void
+ */
+ public function clearAllRecipients()
+ {
+ $this->to = array();
+ $this->cc = array();
+ $this->bcc = array();
+ $this->all_recipients = array();
+ $this->RecipientsQueue = array();
+ }
+
+ /**
+ * Clear all filesystem, string, and binary attachments.
+ * @return void
+ */
+ public function clearAttachments()
+ {
+ $this->attachment = array();
+ }
+
+ /**
+ * Clear all custom headers.
+ * @return void
+ */
+ public function clearCustomHeaders()
+ {
+ $this->CustomHeader = array();
+ }
+
+ /**
+ * Add an error message to the error container.
+ * @access protected
+ * @param string $msg
+ * @return void
+ */
+ protected function setError($msg)
+ {
+ $this->error_count++;
+ if ($this->Mailer == 'smtp' and !is_null($this->smtp)) {
+ $lasterror = $this->smtp->getError();
+ if (!empty($lasterror['error'])) {
+ $msg .= $this->lang('smtp_error') . $lasterror['error'];
+ if (!empty($lasterror['detail'])) {
+ $msg .= ' Detail: '. $lasterror['detail'];
+ }
+ if (!empty($lasterror['smtp_code'])) {
+ $msg .= ' SMTP code: ' . $lasterror['smtp_code'];
+ }
+ if (!empty($lasterror['smtp_code_ex'])) {
+ $msg .= ' Additional SMTP info: ' . $lasterror['smtp_code_ex'];
+ }
+ }
+ }
+ $this->ErrorInfo = $msg;
+ }
+
+ /**
+ * Return an RFC 822 formatted date.
+ * @access public
+ * @return string
+ * @static
+ */
+ public static function rfcDate()
+ {
+ // Set the time zone to whatever the default is to avoid 500 errors
+ // Will default to UTC if it's not set properly in php.ini
+ date_default_timezone_set(@date_default_timezone_get());
+ return date('D, j M Y H:i:s O');
+ }
+
+ /**
+ * Get the server hostname.
+ * Returns 'localhost.localdomain' if unknown.
+ * @access protected
+ * @return string
+ */
+ protected function serverHostname()
+ {
+ $result = 'localhost.localdomain';
+ if (!empty($this->Hostname)) {
+ $result = $this->Hostname;
+ } elseif (isset($_SERVER) and array_key_exists('SERVER_NAME', $_SERVER) and !empty($_SERVER['SERVER_NAME'])) {
+ $result = $_SERVER['SERVER_NAME'];
+ } elseif (function_exists('gethostname') && gethostname() !== false) {
+ $result = gethostname();
+ } elseif (php_uname('n') !== false) {
+ $result = php_uname('n');
+ }
+ return $result;
+ }
+
+ /**
+ * Get an error message in the current language.
+ * @access protected
+ * @param string $key
+ * @return string
+ */
+ protected function lang($key)
+ {
+ if (count($this->language) < 1) {
+ $this->setLanguage('en'); // set the default language
+ }
+
+ if (array_key_exists($key, $this->language)) {
+ if ($key == 'smtp_connect_failed') {
+ //Include a link to troubleshooting docs on SMTP connection failure
+ //this is by far the biggest cause of support questions
+ //but it's usually not PHPMailer's fault.
+ return $this->language[$key] . ' https://github.com/PHPMailer/PHPMailer/wiki/Troubleshooting';
+ }
+ return $this->language[$key];
+ } else {
+ //Return the key as a fallback
+ return $key;
+ }
+ }
+
+ /**
+ * Check if an error occurred.
+ * @access public
+ * @return boolean True if an error did occur.
+ */
+ public function isError()
+ {
+ return ($this->error_count > 0);
+ }
+
+ /**
+ * Ensure consistent line endings in a string.
+ * Changes every end of line from CRLF, CR or LF to $this->LE.
+ * @access public
+ * @param string $str String to fixEOL
+ * @return string
+ */
+ public function fixEOL($str)
+ {
+ // Normalise to \n
+ $nstr = str_replace(array("\r\n", "\r"), "\n", $str);
+ // Now convert LE as needed
+ if ($this->LE !== "\n") {
+ $nstr = str_replace("\n", $this->LE, $nstr);
+ }
+ return $nstr;
+ }
+
+ /**
+ * Add a custom header.
+ * $name value can be overloaded to contain
+ * both header name and value (name:value)
+ * @access public
+ * @param string $name Custom header name
+ * @param string $value Header value
+ * @return void
+ */
+ public function addCustomHeader($name, $value = null)
+ {
+ if ($value === null) {
+ // Value passed in as name:value
+ $this->CustomHeader[] = explode(':', $name, 2);
+ } else {
+ $this->CustomHeader[] = array($name, $value);
+ }
+ }
+
+ /**
+ * Returns all custom headers.
+ * @return array
+ */
+ public function getCustomHeaders()
+ {
+ return $this->CustomHeader;
+ }
+
+ /**
+ * Create a message from an HTML string.
+ * Automatically makes modifications for inline images and backgrounds
+ * and creates a plain-text version by converting the HTML.
+ * Overwrites any existing values in $this->Body and $this->AltBody
+ * @access public
+ * @param string $message HTML message string
+ * @param string $basedir baseline directory for path
+ * @param boolean|callable $advanced Whether to use the internal HTML to text converter
+ * or your own custom converter @see PHPMailer::html2text()
+ * @return string $message
+ */
+ public function msgHTML($message, $basedir = '', $advanced = false)
+ {
+ preg_match_all('/(src|background)=["\'](.*)["\']/Ui', $message, $images);
+ if (array_key_exists(2, $images)) {
+ foreach ($images[2] as $imgindex => $url) {
+ // Convert data URIs into embedded images
+ if (preg_match('#^data:(image[^;,]*)(;base64)?,#', $url, $match)) {
+ $data = substr($url, strpos($url, ','));
+ if ($match[2]) {
+ $data = base64_decode($data);
+ } else {
+ $data = rawurldecode($data);
+ }
+ $cid = md5($url) . '@phpmailer.0'; // RFC2392 S 2
+ if ($this->addStringEmbeddedImage($data, $cid, 'embed' . $imgindex, 'base64', $match[1])) {
+ $message = str_replace(
+ $images[0][$imgindex],
+ $images[1][$imgindex] . '="cid:' . $cid . '"',
+ $message
+ );
+ }
+ } elseif (substr($url, 0, 4) !== 'cid:' && !preg_match('#^[A-z]+://#', $url)) {
+ // Do not change urls for absolute images (thanks to corvuscorax)
+ // Do not change urls that are already inline images
+ $filename = basename($url);
+ $directory = dirname($url);
+ if ($directory == '.') {
+ $directory = '';
+ }
+ $cid = md5($url) . '@phpmailer.0'; // RFC2392 S 2
+ if (strlen($basedir) > 1 && substr($basedir, -1) != '/') {
+ $basedir .= '/';
+ }
+ if (strlen($directory) > 1 && substr($directory, -1) != '/') {
+ $directory .= '/';
+ }
+ if ($this->addEmbeddedImage(
+ $basedir . $directory . $filename,
+ $cid,
+ $filename,
+ 'base64',
+ self::_mime_types((string)self::mb_pathinfo($filename, PATHINFO_EXTENSION))
+ )
+ ) {
+ $message = preg_replace(
+ '/' . $images[1][$imgindex] . '=["\']' . preg_quote($url, '/') . '["\']/Ui',
+ $images[1][$imgindex] . '="cid:' . $cid . '"',
+ $message
+ );
+ }
+ }
+ }
+ }
+ $this->isHTML(true);
+ // Convert all message body line breaks to CRLF, makes quoted-printable encoding work much better
+ $this->Body = $this->normalizeBreaks($message);
+ $this->AltBody = $this->normalizeBreaks($this->html2text($message, $advanced));
+ if (!$this->alternativeExists()) {
+ $this->AltBody = 'To view this email message, open it in a program that understands HTML!' .
+ self::CRLF . self::CRLF;
+ }
+ return $this->Body;
+ }
+
+ /**
+ * Convert an HTML string into plain text.
+ * This is used by msgHTML().
+ * Note - older versions of this function used a bundled advanced converter
+ * which was been removed for license reasons in #232
+ * Example usage:
+ *
+ * // Use default conversion
+ * $plain = $mail->html2text($html);
+ * // Use your own custom converter
+ * $plain = $mail->html2text($html, function($html) {
+ * $converter = new MyHtml2text($html);
+ * return $converter->get_text();
+ * });
+ *
+ * @param string $html The HTML text to convert
+ * @param boolean|callable $advanced Any boolean value to use the internal converter,
+ * or provide your own callable for custom conversion.
+ * @return string
+ */
+ public function html2text($html, $advanced = false)
+ {
+ if (is_callable($advanced)) {
+ return call_user_func($advanced, $html);
+ }
+ return html_entity_decode(
+ trim(strip_tags(preg_replace('/<(head|title|style|script)[^>]*>.*?<\/\\1>/si', '', $html))),
+ ENT_QUOTES,
+ $this->CharSet
+ );
+ }
+
+ /**
+ * Get the MIME type for a file extension.
+ * @param string $ext File extension
+ * @access public
+ * @return string MIME type of file.
+ * @static
+ */
+ public static function _mime_types($ext = '')
+ {
+ $mimes = array(
+ 'xl' => 'application/excel',
+ 'js' => 'application/javascript',
+ 'hqx' => 'application/mac-binhex40',
+ 'cpt' => 'application/mac-compactpro',
+ 'bin' => 'application/macbinary',
+ 'doc' => 'application/msword',
+ 'word' => 'application/msword',
+ 'xlsx' => 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
+ 'xltx' => 'application/vnd.openxmlformats-officedocument.spreadsheetml.template',
+ 'potx' => 'application/vnd.openxmlformats-officedocument.presentationml.template',
+ 'ppsx' => 'application/vnd.openxmlformats-officedocument.presentationml.slideshow',
+ 'pptx' => 'application/vnd.openxmlformats-officedocument.presentationml.presentation',
+ 'sldx' => 'application/vnd.openxmlformats-officedocument.presentationml.slide',
+ 'docx' => 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
+ 'dotx' => 'application/vnd.openxmlformats-officedocument.wordprocessingml.template',
+ 'xlam' => 'application/vnd.ms-excel.addin.macroEnabled.12',
+ 'xlsb' => 'application/vnd.ms-excel.sheet.binary.macroEnabled.12',
+ 'class' => 'application/octet-stream',
+ 'dll' => 'application/octet-stream',
+ 'dms' => 'application/octet-stream',
+ 'exe' => 'application/octet-stream',
+ 'lha' => 'application/octet-stream',
+ 'lzh' => 'application/octet-stream',
+ 'psd' => 'application/octet-stream',
+ 'sea' => 'application/octet-stream',
+ 'so' => 'application/octet-stream',
+ 'oda' => 'application/oda',
+ 'pdf' => 'application/pdf',
+ 'ai' => 'application/postscript',
+ 'eps' => 'application/postscript',
+ 'ps' => 'application/postscript',
+ 'smi' => 'application/smil',
+ 'smil' => 'application/smil',
+ 'mif' => 'application/vnd.mif',
+ 'xls' => 'application/vnd.ms-excel',
+ 'ppt' => 'application/vnd.ms-powerpoint',
+ 'wbxml' => 'application/vnd.wap.wbxml',
+ 'wmlc' => 'application/vnd.wap.wmlc',
+ 'dcr' => 'application/x-director',
+ 'dir' => 'application/x-director',
+ 'dxr' => 'application/x-director',
+ 'dvi' => 'application/x-dvi',
+ 'gtar' => 'application/x-gtar',
+ 'php3' => 'application/x-httpd-php',
+ 'php4' => 'application/x-httpd-php',
+ 'php' => 'application/x-httpd-php',
+ 'phtml' => 'application/x-httpd-php',
+ 'phps' => 'application/x-httpd-php-source',
+ 'swf' => 'application/x-shockwave-flash',
+ 'sit' => 'application/x-stuffit',
+ 'tar' => 'application/x-tar',
+ 'tgz' => 'application/x-tar',
+ 'xht' => 'application/xhtml+xml',
+ 'xhtml' => 'application/xhtml+xml',
+ 'zip' => 'application/zip',
+ 'mid' => 'audio/midi',
+ 'midi' => 'audio/midi',
+ 'mp2' => 'audio/mpeg',
+ 'mp3' => 'audio/mpeg',
+ 'mpga' => 'audio/mpeg',
+ 'aif' => 'audio/x-aiff',
+ 'aifc' => 'audio/x-aiff',
+ 'aiff' => 'audio/x-aiff',
+ 'ram' => 'audio/x-pn-realaudio',
+ 'rm' => 'audio/x-pn-realaudio',
+ 'rpm' => 'audio/x-pn-realaudio-plugin',
+ 'ra' => 'audio/x-realaudio',
+ 'wav' => 'audio/x-wav',
+ 'bmp' => 'image/bmp',
+ 'gif' => 'image/gif',
+ 'jpeg' => 'image/jpeg',
+ 'jpe' => 'image/jpeg',
+ 'jpg' => 'image/jpeg',
+ 'png' => 'image/png',
+ 'tiff' => 'image/tiff',
+ 'tif' => 'image/tiff',
+ 'eml' => 'message/rfc822',
+ 'css' => 'text/css',
+ 'html' => 'text/html',
+ 'htm' => 'text/html',
+ 'shtml' => 'text/html',
+ 'log' => 'text/plain',
+ 'text' => 'text/plain',
+ 'txt' => 'text/plain',
+ 'rtx' => 'text/richtext',
+ 'rtf' => 'text/rtf',
+ 'vcf' => 'text/vcard',
+ 'vcard' => 'text/vcard',
+ 'xml' => 'text/xml',
+ 'xsl' => 'text/xml',
+ 'mpeg' => 'video/mpeg',
+ 'mpe' => 'video/mpeg',
+ 'mpg' => 'video/mpeg',
+ 'mov' => 'video/quicktime',
+ 'qt' => 'video/quicktime',
+ 'rv' => 'video/vnd.rn-realvideo',
+ 'avi' => 'video/x-msvideo',
+ 'movie' => 'video/x-sgi-movie'
+ );
+ if (array_key_exists(strtolower($ext), $mimes)) {
+ return $mimes[strtolower($ext)];
+ }
+ return 'application/octet-stream';
+ }
+
+ /**
+ * Map a file name to a MIME type.
+ * Defaults to 'application/octet-stream', i.e.. arbitrary binary data.
+ * @param string $filename A file name or full path, does not need to exist as a file
+ * @return string
+ * @static
+ */
+ public static function filenameToType($filename)
+ {
+ // In case the path is a URL, strip any query string before getting extension
+ $qpos = strpos($filename, '?');
+ if (false !== $qpos) {
+ $filename = substr($filename, 0, $qpos);
+ }
+ $pathinfo = self::mb_pathinfo($filename);
+ return self::_mime_types($pathinfo['extension']);
+ }
+
+ /**
+ * Multi-byte-safe pathinfo replacement.
+ * Drop-in replacement for pathinfo(), but multibyte-safe, cross-platform-safe, old-version-safe.
+ * Works similarly to the one in PHP >= 5.2.0
+ * @link http://www.php.net/manual/en/function.pathinfo.php#107461
+ * @param string $path A filename or path, does not need to exist as a file
+ * @param integer|string $options Either a PATHINFO_* constant,
+ * or a string name to return only the specified piece, allows 'filename' to work on PHP < 5.2
+ * @return string|array
+ * @static
+ */
+ public static function mb_pathinfo($path, $options = null)
+ {
+ $ret = array('dirname' => '', 'basename' => '', 'extension' => '', 'filename' => '');
+ $pathinfo = array();
+ if (preg_match('%^(.*?)[\\\\/]*(([^/\\\\]*?)(\.([^\.\\\\/]+?)|))[\\\\/\.]*$%im', $path, $pathinfo)) {
+ if (array_key_exists(1, $pathinfo)) {
+ $ret['dirname'] = $pathinfo[1];
+ }
+ if (array_key_exists(2, $pathinfo)) {
+ $ret['basename'] = $pathinfo[2];
+ }
+ if (array_key_exists(5, $pathinfo)) {
+ $ret['extension'] = $pathinfo[5];
+ }
+ if (array_key_exists(3, $pathinfo)) {
+ $ret['filename'] = $pathinfo[3];
+ }
+ }
+ switch ($options) {
+ case PATHINFO_DIRNAME:
+ case 'dirname':
+ return $ret['dirname'];
+ case PATHINFO_BASENAME:
+ case 'basename':
+ return $ret['basename'];
+ case PATHINFO_EXTENSION:
+ case 'extension':
+ return $ret['extension'];
+ case PATHINFO_FILENAME:
+ case 'filename':
+ return $ret['filename'];
+ default:
+ return $ret;
+ }
+ }
+
+ /**
+ * Set or reset instance properties.
+ * You should avoid this function - it's more verbose, less efficient, more error-prone and
+ * harder to debug than setting properties directly.
+ * Usage Example:
+ * `$mail->set('SMTPSecure', 'tls');`
+ * is the same as:
+ * `$mail->SMTPSecure = 'tls';`
+ * @access public
+ * @param string $name The property name to set
+ * @param mixed $value The value to set the property to
+ * @return boolean
+ * @TODO Should this not be using the __set() magic function?
+ */
+ public function set($name, $value = '')
+ {
+ if (property_exists($this, $name)) {
+ $this->$name = $value;
+ return true;
+ } else {
+ $this->setError($this->lang('variable_set') . $name);
+ return false;
+ }
+ }
+
+ /**
+ * Strip newlines to prevent header injection.
+ * @access public
+ * @param string $str
+ * @return string
+ */
+ public function secureHeader($str)
+ {
+ return trim(str_replace(array("\r", "\n"), '', $str));
+ }
+
+ /**
+ * Normalize line breaks in a string.
+ * Converts UNIX LF, Mac CR and Windows CRLF line breaks into a single line break format.
+ * Defaults to CRLF (for message bodies) and preserves consecutive breaks.
+ * @param string $text
+ * @param string $breaktype What kind of line break to use, defaults to CRLF
+ * @return string
+ * @access public
+ * @static
+ */
+ public static function normalizeBreaks($text, $breaktype = "\r\n")
+ {
+ return preg_replace('/(\r\n|\r|\n)/ms', $breaktype, $text);
+ }
+
+ /**
+ * Set the public and private key files and password for S/MIME signing.
+ * @access public
+ * @param string $cert_filename
+ * @param string $key_filename
+ * @param string $key_pass Password for private key
+ * @param string $extracerts_filename Optional path to chain certificate
+ */
+ public function sign($cert_filename, $key_filename, $key_pass, $extracerts_filename = '')
+ {
+ $this->sign_cert_file = $cert_filename;
+ $this->sign_key_file = $key_filename;
+ $this->sign_key_pass = $key_pass;
+ $this->sign_extracerts_file = $extracerts_filename;
+ }
+
+ /**
+ * Quoted-Printable-encode a DKIM header.
+ * @access public
+ * @param string $txt
+ * @return string
+ */
+ public function DKIM_QP($txt)
+ {
+ $line = '';
+ for ($i = 0; $i < strlen($txt); $i++) {
+ $ord = ord($txt[$i]);
+ if (((0x21 <= $ord) && ($ord <= 0x3A)) || $ord == 0x3C || ((0x3E <= $ord) && ($ord <= 0x7E))) {
+ $line .= $txt[$i];
+ } else {
+ $line .= '=' . sprintf('%02X', $ord);
+ }
+ }
+ return $line;
+ }
+
+ /**
+ * Generate a DKIM signature.
+ * @access public
+ * @param string $signHeader
+ * @throws phpmailerException
+ * @return string
+ */
+ public function DKIM_Sign($signHeader)
+ {
+ if (!defined('PKCS7_TEXT')) {
+ if ($this->exceptions) {
+ throw new phpmailerException($this->lang('extension_missing') . 'openssl');
+ }
+ return '';
+ }
+ $privKeyStr = file_get_contents($this->DKIM_private);
+ if ($this->DKIM_passphrase != '') {
+ $privKey = openssl_pkey_get_private($privKeyStr, $this->DKIM_passphrase);
+ } else {
+ $privKey = $privKeyStr;
+ }
+ if (openssl_sign($signHeader, $signature, $privKey)) {
+ return base64_encode($signature);
+ }
+ return '';
+ }
+
+ /**
+ * Generate a DKIM canonicalization header.
+ * @access public
+ * @param string $signHeader Header
+ * @return string
+ */
+ public function DKIM_HeaderC($signHeader)
+ {
+ $signHeader = preg_replace('/\r\n\s+/', ' ', $signHeader);
+ $lines = explode("\r\n", $signHeader);
+ foreach ($lines as $key => $line) {
+ list($heading, $value) = explode(':', $line, 2);
+ $heading = strtolower($heading);
+ $value = preg_replace('/\s+/', ' ', $value); // Compress useless spaces
+ $lines[$key] = $heading . ':' . trim($value); // Don't forget to remove WSP around the value
+ }
+ $signHeader = implode("\r\n", $lines);
+ return $signHeader;
+ }
+
+ /**
+ * Generate a DKIM canonicalization body.
+ * @access public
+ * @param string $body Message Body
+ * @return string
+ */
+ public function DKIM_BodyC($body)
+ {
+ if ($body == '') {
+ return "\r\n";
+ }
+ // stabilize line endings
+ $body = str_replace("\r\n", "\n", $body);
+ $body = str_replace("\n", "\r\n", $body);
+ // END stabilize line endings
+ while (substr($body, strlen($body) - 4, 4) == "\r\n\r\n") {
+ $body = substr($body, 0, strlen($body) - 2);
+ }
+ return $body;
+ }
+
+ /**
+ * Create the DKIM header and body in a new message header.
+ * @access public
+ * @param string $headers_line Header lines
+ * @param string $subject Subject
+ * @param string $body Body
+ * @return string
+ */
+ public function DKIM_Add($headers_line, $subject, $body)
+ {
+ $DKIMsignatureType = 'rsa-sha1'; // Signature & hash algorithms
+ $DKIMcanonicalization = 'relaxed/simple'; // Canonicalization of header/body
+ $DKIMquery = 'dns/txt'; // Query method
+ $DKIMtime = time(); // Signature Timestamp = seconds since 00:00:00 - Jan 1, 1970 (UTC time zone)
+ $subject_header = "Subject: $subject";
+ $headers = explode($this->LE, $headers_line);
+ $from_header = '';
+ $to_header = '';
+ $current = '';
+ foreach ($headers as $header) {
+ if (strpos($header, 'From:') === 0) {
+ $from_header = $header;
+ $current = 'from_header';
+ } elseif (strpos($header, 'To:') === 0) {
+ $to_header = $header;
+ $current = 'to_header';
+ } else {
+ if (!empty($$current) && strpos($header, ' =?') === 0) {
+ $$current .= $header;
+ } else {
+ $current = '';
+ }
+ }
+ }
+ $from = str_replace('|', '=7C', $this->DKIM_QP($from_header));
+ $to = str_replace('|', '=7C', $this->DKIM_QP($to_header));
+ $subject = str_replace(
+ '|',
+ '=7C',
+ $this->DKIM_QP($subject_header)
+ ); // Copied header fields (dkim-quoted-printable)
+ $body = $this->DKIM_BodyC($body);
+ $DKIMlen = strlen($body); // Length of body
+ $DKIMb64 = base64_encode(pack('H*', sha1($body))); // Base64 of packed binary SHA-1 hash of body
+ if ('' == $this->DKIM_identity) {
+ $ident = '';
+ } else {
+ $ident = ' i=' . $this->DKIM_identity . ';';
+ }
+ $dkimhdrs = 'DKIM-Signature: v=1; a=' .
+ $DKIMsignatureType . '; q=' .
+ $DKIMquery . '; l=' .
+ $DKIMlen . '; s=' .
+ $this->DKIM_selector .
+ ";\r\n" .
+ "\tt=" . $DKIMtime . '; c=' . $DKIMcanonicalization . ";\r\n" .
+ "\th=From:To:Subject;\r\n" .
+ "\td=" . $this->DKIM_domain . ';' . $ident . "\r\n" .
+ "\tz=$from\r\n" .
+ "\t|$to\r\n" .
+ "\t|$subject;\r\n" .
+ "\tbh=" . $DKIMb64 . ";\r\n" .
+ "\tb=";
+ $toSign = $this->DKIM_HeaderC(
+ $from_header . "\r\n" .
+ $to_header . "\r\n" .
+ $subject_header . "\r\n" .
+ $dkimhdrs
+ );
+ $signed = $this->DKIM_Sign($toSign);
+ return $dkimhdrs . $signed . "\r\n";
+ }
+
+ /**
+ * Detect if a string contains a line longer than the maximum line length allowed.
+ * @param string $str
+ * @return boolean
+ * @static
+ */
+ public static function hasLineLongerThanMax($str)
+ {
+ //+2 to include CRLF line break for a 1000 total
+ return (boolean)preg_match('/^(.{'.(self::MAX_LINE_LENGTH + 2).',})/m', $str);
+ }
+
+ /**
+ * Allows for public read access to 'to' property.
+ * @note: Before the send() call, queued addresses (i.e. with IDN) are not yet included.
+ * @access public
+ * @return array
+ */
+ public function getToAddresses()
+ {
+ return $this->to;
+ }
+
+ /**
+ * Allows for public read access to 'cc' property.
+ * @note: Before the send() call, queued addresses (i.e. with IDN) are not yet included.
+ * @access public
+ * @return array
+ */
+ public function getCcAddresses()
+ {
+ return $this->cc;
+ }
+
+ /**
+ * Allows for public read access to 'bcc' property.
+ * @note: Before the send() call, queued addresses (i.e. with IDN) are not yet included.
+ * @access public
+ * @return array
+ */
+ public function getBccAddresses()
+ {
+ return $this->bcc;
+ }
+
+ /**
+ * Allows for public read access to 'ReplyTo' property.
+ * @note: Before the send() call, queued addresses (i.e. with IDN) are not yet included.
+ * @access public
+ * @return array
+ */
+ public function getReplyToAddresses()
+ {
+ return $this->ReplyTo;
+ }
+
+ /**
+ * Allows for public read access to 'all_recipients' property.
+ * @note: Before the send() call, queued addresses (i.e. with IDN) are not yet included.
+ * @access public
+ * @return array
+ */
+ public function getAllRecipientAddresses()
+ {
+ return $this->all_recipients;
+ }
+
+ /**
+ * Perform a callback.
+ * @param boolean $isSent
+ * @param array $to
+ * @param array $cc
+ * @param array $bcc
+ * @param string $subject
+ * @param string $body
+ * @param string $from
+ */
+ protected function doCallback($isSent, $to, $cc, $bcc, $subject, $body, $from)
+ {
+ if (!empty($this->action_function) && is_callable($this->action_function)) {
+ $params = array($isSent, $to, $cc, $bcc, $subject, $body, $from);
+ call_user_func_array($this->action_function, $params);
+ }
+ }
+}
+
+/**
+ * PHPMailer exception handler
+ * @package PHPMailer
+ */
+class phpmailerException extends Exception
+{
+ /**
+ * Prettify error message output
+ * @return string
+ */
+ public function errorMessage()
+ {
+ $errorMsg = '' . $this->getMessage() . " \n";
+ return $errorMsg;
+ }
+}
diff --git a/include/phpmailer/class.smtp.php b/include/phpmailer/class.smtp.php
index 2e32e2f..9204734 100644
--- a/include/phpmailer/class.smtp.php
+++ b/include/phpmailer/class.smtp.php
@@ -1,1181 +1,1181 @@
-
- * @author Jim Jagielski (jimjag)
- * @author Andy Prevost (codeworxtech)
- * @author Brent R. Matzelle (original founder)
- * @copyright 2014 Marcus Bointon
- * @copyright 2010 - 2012 Jim Jagielski
- * @copyright 2004 - 2009 Andy Prevost
- * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
- * @note This program is distributed in the hope that it will be useful - WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE.
- */
-
-/**
- * PHPMailer RFC821 SMTP email transport class.
- * Implements RFC 821 SMTP commands and provides some utility methods for sending mail to an SMTP server.
- * @package PHPMailer
- * @author Chris Ryan
- * @author Marcus Bointon
- */
-class SMTP
-{
- /**
- * The PHPMailer SMTP version number.
- * @var string
- */
- const VERSION = '5.2.14';
-
- /**
- * SMTP line break constant.
- * @var string
- */
- const CRLF = "\r\n";
-
- /**
- * The SMTP port to use if one is not specified.
- * @var integer
- */
- const DEFAULT_SMTP_PORT = 25;
-
- /**
- * The maximum line length allowed by RFC 2822 section 2.1.1
- * @var integer
- */
- const MAX_LINE_LENGTH = 998;
-
- /**
- * Debug level for no output
- */
- const DEBUG_OFF = 0;
-
- /**
- * Debug level to show client -> server messages
- */
- const DEBUG_CLIENT = 1;
-
- /**
- * Debug level to show client -> server and server -> client messages
- */
- const DEBUG_SERVER = 2;
-
- /**
- * Debug level to show connection status, client -> server and server -> client messages
- */
- const DEBUG_CONNECTION = 3;
-
- /**
- * Debug level to show all messages
- */
- const DEBUG_LOWLEVEL = 4;
-
- /**
- * The PHPMailer SMTP Version number.
- * @var string
- * @deprecated Use the `VERSION` constant instead
- * @see SMTP::VERSION
- */
- public $Version = '5.2.14';
-
- /**
- * SMTP server port number.
- * @var integer
- * @deprecated This is only ever used as a default value, so use the `DEFAULT_SMTP_PORT` constant instead
- * @see SMTP::DEFAULT_SMTP_PORT
- */
- public $SMTP_PORT = 25;
-
- /**
- * SMTP reply line ending.
- * @var string
- * @deprecated Use the `CRLF` constant instead
- * @see SMTP::CRLF
- */
- public $CRLF = "\r\n";
-
- /**
- * Debug output level.
- * Options:
- * * self::DEBUG_OFF (`0`) No debug output, default
- * * self::DEBUG_CLIENT (`1`) Client commands
- * * self::DEBUG_SERVER (`2`) Client commands and server responses
- * * self::DEBUG_CONNECTION (`3`) As DEBUG_SERVER plus connection status
- * * self::DEBUG_LOWLEVEL (`4`) Low-level data output, all messages
- * @var integer
- */
- public $do_debug = self::DEBUG_OFF;
-
- /**
- * How to handle debug output.
- * Options:
- * * `echo` Output plain-text as-is, appropriate for CLI
- * * `html` Output escaped, line breaks converted to ` `, appropriate for browser output
- * * `error_log` Output to error log as configured in php.ini
- *
- * Alternatively, you can provide a callable expecting two params: a message string and the debug level:
- *
- * $smtp->Debugoutput = function($str, $level) {echo "debug level $level; message: $str";};
- *
- * @var string|callable
- */
- public $Debugoutput = 'echo';
-
- /**
- * Whether to use VERP.
- * @link http://en.wikipedia.org/wiki/Variable_envelope_return_path
- * @link http://www.postfix.org/VERP_README.html Info on VERP
- * @var boolean
- */
- public $do_verp = false;
-
- /**
- * The timeout value for connection, in seconds.
- * Default of 5 minutes (300sec) is from RFC2821 section 4.5.3.2
- * This needs to be quite high to function correctly with hosts using greetdelay as an anti-spam measure.
- * @link http://tools.ietf.org/html/rfc2821#section-4.5.3.2
- * @var integer
- */
- public $Timeout = 300;
-
- /**
- * How long to wait for commands to complete, in seconds.
- * Default of 5 minutes (300sec) is from RFC2821 section 4.5.3.2
- * @var integer
- */
- public $Timelimit = 300;
-
- /**
- * The socket for the server connection.
- * @var resource
- */
- protected $smtp_conn;
-
- /**
- * Error information, if any, for the last SMTP command.
- * @var array
- */
- protected $error = array(
- 'error' => '',
- 'detail' => '',
- 'smtp_code' => '',
- 'smtp_code_ex' => ''
- );
-
- /**
- * The reply the server sent to us for HELO.
- * If null, no HELO string has yet been received.
- * @var string|null
- */
- protected $helo_rply = null;
-
- /**
- * The set of SMTP extensions sent in reply to EHLO command.
- * Indexes of the array are extension names.
- * Value at index 'HELO' or 'EHLO' (according to command that was sent)
- * represents the server name. In case of HELO it is the only element of the array.
- * Other values can be boolean TRUE or an array containing extension options.
- * If null, no HELO/EHLO string has yet been received.
- * @var array|null
- */
- protected $server_caps = null;
-
- /**
- * The most recent reply received from the server.
- * @var string
- */
- protected $last_reply = '';
-
- /**
- * Output debugging info via a user-selected method.
- * @see SMTP::$Debugoutput
- * @see SMTP::$do_debug
- * @param string $str Debug string to output
- * @param integer $level The debug level of this message; see DEBUG_* constants
- * @return void
- */
- protected function edebug($str, $level = 0)
- {
- if ($level > $this->do_debug) {
- return;
- }
- //Avoid clash with built-in function names
- if (!in_array($this->Debugoutput, array('error_log', 'html', 'echo')) and is_callable($this->Debugoutput)) {
- call_user_func($this->Debugoutput, $str, $this->do_debug);
- return;
- }
- switch ($this->Debugoutput) {
- case 'error_log':
- //Don't output, just log
- error_log($str);
- break;
- case 'html':
- //Cleans up output a bit for a better looking, HTML-safe output
- echo htmlentities(
- preg_replace('/[\r\n]+/', '', $str),
- ENT_QUOTES,
- 'UTF-8'
- )
- . " \n";
- break;
- case 'echo':
- default:
- //Normalize line breaks
- $str = preg_replace('/(\r\n|\r|\n)/ms', "\n", $str);
- echo gmdate('Y-m-d H:i:s') . "\t" . str_replace(
- "\n",
- "\n \t ",
- trim($str)
- )."\n";
- }
- }
-
- /**
- * Connect to an SMTP server.
- * @param string $host SMTP server IP or host name
- * @param integer $port The port number to connect to
- * @param integer $timeout How long to wait for the connection to open
- * @param array $options An array of options for stream_context_create()
- * @access public
- * @return boolean
- */
- public function connect($host, $port = null, $timeout = 30, $options = array())
- {
- static $streamok;
- //This is enabled by default since 5.0.0 but some providers disable it
- //Check this once and cache the result
- if (is_null($streamok)) {
- $streamok = function_exists('stream_socket_client');
- }
- // Clear errors to avoid confusion
- $this->setError('');
- // Make sure we are __not__ connected
- if ($this->connected()) {
- // Already connected, generate error
- $this->setError('Already connected to a server');
- return false;
- }
- if (empty($port)) {
- $port = self::DEFAULT_SMTP_PORT;
- }
- // Connect to the SMTP server
- $this->edebug(
- "Connection: opening to $host:$port, timeout=$timeout, options=".var_export($options, true),
- self::DEBUG_CONNECTION
- );
- $errno = 0;
- $errstr = '';
- if ($streamok) {
- $socket_context = stream_context_create($options);
- //Suppress errors; connection failures are handled at a higher level
- $this->smtp_conn = @stream_socket_client(
- $host . ":" . $port,
- $errno,
- $errstr,
- $timeout,
- STREAM_CLIENT_CONNECT,
- $socket_context
- );
- } else {
- //Fall back to fsockopen which should work in more places, but is missing some features
- $this->edebug(
- "Connection: stream_socket_client not available, falling back to fsockopen",
- self::DEBUG_CONNECTION
- );
- $this->smtp_conn = fsockopen(
- $host,
- $port,
- $errno,
- $errstr,
- $timeout
- );
- }
- // Verify we connected properly
- if (!is_resource($this->smtp_conn)) {
- $this->setError(
- 'Failed to connect to server',
- $errno,
- $errstr
- );
- $this->edebug(
- 'SMTP ERROR: ' . $this->error['error']
- . ": $errstr ($errno)",
- self::DEBUG_CLIENT
- );
- return false;
- }
- $this->edebug('Connection: opened', self::DEBUG_CONNECTION);
- // SMTP server can take longer to respond, give longer timeout for first read
- // Windows does not have support for this timeout function
- if (substr(PHP_OS, 0, 3) != 'WIN') {
- $max = ini_get('max_execution_time');
- // Don't bother if unlimited
- if ($max != 0 && $timeout > $max) {
- @set_time_limit($timeout);
- }
- stream_set_timeout($this->smtp_conn, $timeout, 0);
- }
- // Get any announcement
- $announce = $this->get_lines();
- $this->edebug('SERVER -> CLIENT: ' . $announce, self::DEBUG_SERVER);
- return true;
- }
-
- /**
- * Initiate a TLS (encrypted) session.
- * @access public
- * @return boolean
- */
- public function startTLS()
- {
- if (!$this->sendCommand('STARTTLS', 'STARTTLS', 220)) {
- return false;
- }
- // Begin encrypted connection
- if (!stream_socket_enable_crypto(
- $this->smtp_conn,
- true,
- STREAM_CRYPTO_METHOD_TLS_CLIENT
- )) {
- return false;
- }
- return true;
- }
-
- /**
- * Perform SMTP authentication.
- * Must be run after hello().
- * @see hello()
- * @param string $username The user name
- * @param string $password The password
- * @param string $authtype The auth type (PLAIN, LOGIN, NTLM, CRAM-MD5, XOAUTH2)
- * @param string $realm The auth realm for NTLM
- * @param string $workstation The auth workstation for NTLM
- * @param null|OAuth $OAuth An optional OAuth instance (@see PHPMailerOAuth)
- * @return bool True if successfully authenticated.* @access public
- */
- public function authenticate(
- $username,
- $password,
- $authtype = null,
- $realm = '',
- $workstation = '',
- $OAuth = null
- ) {
- if (!$this->server_caps) {
- $this->setError('Authentication is not allowed before HELO/EHLO');
- return false;
- }
-
- if (array_key_exists('EHLO', $this->server_caps)) {
- // SMTP extensions are available. Let's try to find a proper authentication method
-
- if (!array_key_exists('AUTH', $this->server_caps)) {
- $this->setError('Authentication is not allowed at this stage');
- // 'at this stage' means that auth may be allowed after the stage changes
- // e.g. after STARTTLS
- return false;
- }
-
- self::edebug('Auth method requested: ' . ($authtype ? $authtype : 'UNKNOWN'), self::DEBUG_LOWLEVEL);
- self::edebug(
- 'Auth methods available on the server: ' . implode(',', $this->server_caps['AUTH']),
- self::DEBUG_LOWLEVEL
- );
-
- if (empty($authtype)) {
- foreach (array('LOGIN', 'CRAM-MD5', 'NTLM', 'PLAIN', 'XOAUTH2') as $method) {
- if (in_array($method, $this->server_caps['AUTH'])) {
- $authtype = $method;
- break;
- }
- }
- if (empty($authtype)) {
- $this->setError('No supported authentication methods found');
- return false;
- }
- self::edebug('Auth method selected: '.$authtype, self::DEBUG_LOWLEVEL);
- }
-
- if (!in_array($authtype, $this->server_caps['AUTH'])) {
- $this->setError("The requested authentication method \"$authtype\" is not supported by the server");
- return false;
- }
- } elseif (empty($authtype)) {
- $authtype = 'LOGIN';
- }
- switch ($authtype) {
- case 'PLAIN':
- // Start authentication
- if (!$this->sendCommand('AUTH', 'AUTH PLAIN', 334)) {
- return false;
- }
- // Send encoded username and password
- if (!$this->sendCommand(
- 'User & Password',
- base64_encode("\0" . $username . "\0" . $password),
- 235
- )
- ) {
- return false;
- }
- break;
- case 'LOGIN':
- // Start authentication
- if (!$this->sendCommand('AUTH', 'AUTH LOGIN', 334)) {
- return false;
- }
- if (!$this->sendCommand("Username", base64_encode($username), 334)) {
- return false;
- }
- if (!$this->sendCommand("Password", base64_encode($password), 235)) {
- return false;
- }
- break;
- case 'XOAUTH2':
- //If the OAuth Instance is not set. Can be a case when PHPMailer is used
- //instead of PHPMailerOAuth
- if (is_null($OAuth)) {
- return false;
- }
- $oauth = $OAuth->getOauth64();
-
- // Start authentication
- if (!$this->sendCommand('AUTH', 'AUTH XOAUTH2 ' . $oauth, 235)) {
- return false;
- }
- break;
- case 'NTLM':
- /*
- * ntlm_sasl_client.php
- * Bundled with Permission
- *
- * How to telnet in windows:
- * http://technet.microsoft.com/en-us/library/aa995718%28EXCHG.65%29.aspx
- * PROTOCOL Docs http://curl.haxx.se/rfc/ntlm.html#ntlmSmtpAuthentication
- */
- require_once 'extras/ntlm_sasl_client.php';
- $temp = new stdClass;
- $ntlm_client = new ntlm_sasl_client_class;
- //Check that functions are available
- if (!$ntlm_client->Initialize($temp)) {
- $this->setError($temp->error);
- $this->edebug(
- 'You need to enable some modules in your php.ini file: '
- . $this->error['error'],
- self::DEBUG_CLIENT
- );
- return false;
- }
- //msg1
- $msg1 = $ntlm_client->TypeMsg1($realm, $workstation); //msg1
-
- if (!$this->sendCommand(
- 'AUTH NTLM',
- 'AUTH NTLM ' . base64_encode($msg1),
- 334
- )
- ) {
- return false;
- }
- //Though 0 based, there is a white space after the 3 digit number
- //msg2
- $challenge = substr($this->last_reply, 3);
- $challenge = base64_decode($challenge);
- $ntlm_res = $ntlm_client->NTLMResponse(
- substr($challenge, 24, 8),
- $password
- );
- //msg3
- $msg3 = $ntlm_client->TypeMsg3(
- $ntlm_res,
- $username,
- $realm,
- $workstation
- );
- // send encoded username
- return $this->sendCommand('Username', base64_encode($msg3), 235);
- case 'CRAM-MD5':
- // Start authentication
- if (!$this->sendCommand('AUTH CRAM-MD5', 'AUTH CRAM-MD5', 334)) {
- return false;
- }
- // Get the challenge
- $challenge = base64_decode(substr($this->last_reply, 4));
-
- // Build the response
- $response = $username . ' ' . $this->hmac($challenge, $password);
-
- // send encoded credentials
- return $this->sendCommand('Username', base64_encode($response), 235);
- default:
- $this->setError("Authentication method \"$authtype\" is not supported");
- return false;
- }
- return true;
- }
-
- /**
- * Calculate an MD5 HMAC hash.
- * Works like hash_hmac('md5', $data, $key)
- * in case that function is not available
- * @param string $data The data to hash
- * @param string $key The key to hash with
- * @access protected
- * @return string
- */
- protected function hmac($data, $key)
- {
- if (function_exists('hash_hmac')) {
- return hash_hmac('md5', $data, $key);
- }
-
- // The following borrowed from
- // http://php.net/manual/en/function.mhash.php#27225
-
- // RFC 2104 HMAC implementation for php.
- // Creates an md5 HMAC.
- // Eliminates the need to install mhash to compute a HMAC
- // by Lance Rushing
-
- $bytelen = 64; // byte length for md5
- if (strlen($key) > $bytelen) {
- $key = pack('H*', md5($key));
- }
- $key = str_pad($key, $bytelen, chr(0x00));
- $ipad = str_pad('', $bytelen, chr(0x36));
- $opad = str_pad('', $bytelen, chr(0x5c));
- $k_ipad = $key ^ $ipad;
- $k_opad = $key ^ $opad;
-
- return md5($k_opad . pack('H*', md5($k_ipad . $data)));
- }
-
- /**
- * Check connection state.
- * @access public
- * @return boolean True if connected.
- */
- public function connected()
- {
- if (is_resource($this->smtp_conn)) {
- $sock_status = stream_get_meta_data($this->smtp_conn);
- if ($sock_status['eof']) {
- // The socket is valid but we are not connected
- $this->edebug(
- 'SMTP NOTICE: EOF caught while checking if connected',
- self::DEBUG_CLIENT
- );
- $this->close();
- return false;
- }
- return true; // everything looks good
- }
- return false;
- }
-
- /**
- * Close the socket and clean up the state of the class.
- * Don't use this function without first trying to use QUIT.
- * @see quit()
- * @access public
- * @return void
- */
- public function close()
- {
- $this->setError('');
- $this->server_caps = null;
- $this->helo_rply = null;
- if (is_resource($this->smtp_conn)) {
- // close the connection and cleanup
- fclose($this->smtp_conn);
- $this->smtp_conn = null; //Makes for cleaner serialization
- $this->edebug('Connection: closed', self::DEBUG_CONNECTION);
- }
- }
-
- /**
- * Send an SMTP DATA command.
- * Issues a data command and sends the msg_data to the server,
- * finializing the mail transaction. $msg_data is the message
- * that is to be send with the headers. Each header needs to be
- * on a single line followed by a with the message headers
- * and the message body being separated by and additional .
- * Implements rfc 821: DATA
- * @param string $msg_data Message data to send
- * @access public
- * @return boolean
- */
- public function data($msg_data)
- {
- //This will use the standard timelimit
- if (!$this->sendCommand('DATA', 'DATA', 354)) {
- return false;
- }
-
- /* The server is ready to accept data!
- * According to rfc821 we should not send more than 1000 characters on a single line (including the CRLF)
- * so we will break the data up into lines by \r and/or \n then if needed we will break each of those into
- * smaller lines to fit within the limit.
- * We will also look for lines that start with a '.' and prepend an additional '.'.
- * NOTE: this does not count towards line-length limit.
- */
-
- // Normalize line breaks before exploding
- $lines = explode("\n", str_replace(array("\r\n", "\r"), "\n", $msg_data));
-
- /* To distinguish between a complete RFC822 message and a plain message body, we check if the first field
- * of the first line (':' separated) does not contain a space then it _should_ be a header and we will
- * process all lines before a blank line as headers.
- */
-
- $field = substr($lines[0], 0, strpos($lines[0], ':'));
- $in_headers = false;
- if (!empty($field) && strpos($field, ' ') === false) {
- $in_headers = true;
- }
-
- foreach ($lines as $line) {
- $lines_out = array();
- if ($in_headers and $line == '') {
- $in_headers = false;
- }
- //Break this line up into several smaller lines if it's too long
- //Micro-optimisation: isset($str[$len]) is faster than (strlen($str) > $len),
- while (isset($line[self::MAX_LINE_LENGTH])) {
- //Working backwards, try to find a space within the last MAX_LINE_LENGTH chars of the line to break on
- //so as to avoid breaking in the middle of a word
- $pos = strrpos(substr($line, 0, self::MAX_LINE_LENGTH), ' ');
- //Deliberately matches both false and 0
- if (!$pos) {
- //No nice break found, add a hard break
- $pos = self::MAX_LINE_LENGTH - 1;
- $lines_out[] = substr($line, 0, $pos);
- $line = substr($line, $pos);
- } else {
- //Break at the found point
- $lines_out[] = substr($line, 0, $pos);
- //Move along by the amount we dealt with
- $line = substr($line, $pos + 1);
- }
- //If processing headers add a LWSP-char to the front of new line RFC822 section 3.1.1
- if ($in_headers) {
- $line = "\t" . $line;
- }
- }
- $lines_out[] = $line;
-
- //Send the lines to the server
- foreach ($lines_out as $line_out) {
- //RFC2821 section 4.5.2
- if (!empty($line_out) and $line_out[0] == '.') {
- $line_out = '.' . $line_out;
- }
- $this->client_send($line_out . self::CRLF);
- }
- }
-
- //Message data has been sent, complete the command
- //Increase timelimit for end of DATA command
- $savetimelimit = $this->Timelimit;
- $this->Timelimit = $this->Timelimit * 2;
- $result = $this->sendCommand('DATA END', '.', 250);
- //Restore timelimit
- $this->Timelimit = $savetimelimit;
- return $result;
- }
-
- /**
- * Send an SMTP HELO or EHLO command.
- * Used to identify the sending server to the receiving server.
- * This makes sure that client and server are in a known state.
- * Implements RFC 821: HELO
- * and RFC 2821 EHLO.
- * @param string $host The host name or IP to connect to
- * @access public
- * @return boolean
- */
- public function hello($host = '')
- {
- //Try extended hello first (RFC 2821)
- return (boolean)($this->sendHello('EHLO', $host) or $this->sendHello('HELO', $host));
- }
-
- /**
- * Send an SMTP HELO or EHLO command.
- * Low-level implementation used by hello()
- * @see hello()
- * @param string $hello The HELO string
- * @param string $host The hostname to say we are
- * @access protected
- * @return boolean
- */
- protected function sendHello($hello, $host)
- {
- $noerror = $this->sendCommand($hello, $hello . ' ' . $host, 250);
- $this->helo_rply = $this->last_reply;
- if ($noerror) {
- $this->parseHelloFields($hello);
- } else {
- $this->server_caps = null;
- }
- return $noerror;
- }
-
- /**
- * Parse a reply to HELO/EHLO command to discover server extensions.
- * In case of HELO, the only parameter that can be discovered is a server name.
- * @access protected
- * @param string $type - 'HELO' or 'EHLO'
- */
- protected function parseHelloFields($type)
- {
- $this->server_caps = array();
- $lines = explode("\n", $this->last_reply);
-
- foreach ($lines as $n => $s) {
- //First 4 chars contain response code followed by - or space
- $s = trim(substr($s, 4));
- if (empty($s)) {
- continue;
- }
- $fields = explode(' ', $s);
- if (!empty($fields)) {
- if (!$n) {
- $name = $type;
- $fields = $fields[0];
- } else {
- $name = array_shift($fields);
- switch ($name) {
- case 'SIZE':
- $fields = ($fields ? $fields[0] : 0);
- break;
- case 'AUTH':
- if (!is_array($fields)) {
- $fields = array();
- }
- break;
- default:
- $fields = true;
- }
- }
- $this->server_caps[$name] = $fields;
- }
- }
- }
-
- /**
- * Send an SMTP MAIL command.
- * Starts a mail transaction from the email address specified in
- * $from. Returns true if successful or false otherwise. If True
- * the mail transaction is started and then one or more recipient
- * commands may be called followed by a data command.
- * Implements rfc 821: MAIL FROM:
- * @param string $from Source address of this message
- * @access public
- * @return boolean
- */
- public function mail($from)
- {
- $useVerp = ($this->do_verp ? ' XVERP' : '');
- return $this->sendCommand(
- 'MAIL FROM',
- 'MAIL FROM:<' . $from . '>' . $useVerp,
- 250
- );
- }
-
- /**
- * Send an SMTP QUIT command.
- * Closes the socket if there is no error or the $close_on_error argument is true.
- * Implements from rfc 821: QUIT
- * @param boolean $close_on_error Should the connection close if an error occurs?
- * @access public
- * @return boolean
- */
- public function quit($close_on_error = true)
- {
- $noerror = $this->sendCommand('QUIT', 'QUIT', 221);
- $err = $this->error; //Save any error
- if ($noerror or $close_on_error) {
- $this->close();
- $this->error = $err; //Restore any error from the quit command
- }
- return $noerror;
- }
-
- /**
- * Send an SMTP RCPT command.
- * Sets the TO argument to $toaddr.
- * Returns true if the recipient was accepted false if it was rejected.
- * Implements from rfc 821: RCPT TO:
- * @param string $address The address the message is being sent to
- * @access public
- * @return boolean
- */
- public function recipient($address)
- {
- return $this->sendCommand(
- 'RCPT TO',
- 'RCPT TO:<' . $address . '>',
- array(250, 251)
- );
- }
-
- /**
- * Send an SMTP RSET command.
- * Abort any transaction that is currently in progress.
- * Implements rfc 821: RSET
- * @access public
- * @return boolean True on success.
- */
- public function reset()
- {
- return $this->sendCommand('RSET', 'RSET', 250);
- }
-
- /**
- * Send a command to an SMTP server and check its return code.
- * @param string $command The command name - not sent to the server
- * @param string $commandstring The actual command to send
- * @param integer|array $expect One or more expected integer success codes
- * @access protected
- * @return boolean True on success.
- */
- protected function sendCommand($command, $commandstring, $expect)
- {
- if (!$this->connected()) {
- $this->setError("Called $command without being connected");
- return false;
- }
- //Reject line breaks in all commands
- if (strpos($commandstring, "\n") !== false or strpos($commandstring, "\r") !== false) {
- $this->setError("Command '$command' contained line breaks");
- return false;
- }
- $this->client_send($commandstring . self::CRLF);
-
- $this->last_reply = $this->get_lines();
- // Fetch SMTP code and possible error code explanation
- $matches = array();
- if (preg_match("/^([0-9]{3})[ -](?:([0-9]\\.[0-9]\\.[0-9]) )?/", $this->last_reply, $matches)) {
- $code = $matches[1];
- $code_ex = (count($matches) > 2 ? $matches[2] : null);
- // Cut off error code from each response line
- $detail = preg_replace(
- "/{$code}[ -]".($code_ex ? str_replace('.', '\\.', $code_ex).' ' : '')."/m",
- '',
- $this->last_reply
- );
- } else {
- // Fall back to simple parsing if regex fails
- $code = substr($this->last_reply, 0, 3);
- $code_ex = null;
- $detail = substr($this->last_reply, 4);
- }
-
- $this->edebug('SERVER -> CLIENT: ' . $this->last_reply, self::DEBUG_SERVER);
-
- if (!in_array($code, (array)$expect)) {
- $this->setError(
- "$command command failed",
- $detail,
- $code,
- $code_ex
- );
- $this->edebug(
- 'SMTP ERROR: ' . $this->error['error'] . ': ' . $this->last_reply,
- self::DEBUG_CLIENT
- );
- return false;
- }
-
- $this->setError('');
- return true;
- }
-
- /**
- * Send an SMTP SAML command.
- * Starts a mail transaction from the email address specified in $from.
- * Returns true if successful or false otherwise. If True
- * the mail transaction is started and then one or more recipient
- * commands may be called followed by a data command. This command
- * will send the message to the users terminal if they are logged
- * in and send them an email.
- * Implements rfc 821: SAML FROM:
- * @param string $from The address the message is from
- * @access public
- * @return boolean
- */
- public function sendAndMail($from)
- {
- return $this->sendCommand('SAML', "SAML FROM:$from", 250);
- }
-
- /**
- * Send an SMTP VRFY command.
- * @param string $name The name to verify
- * @access public
- * @return boolean
- */
- public function verify($name)
- {
- return $this->sendCommand('VRFY', "VRFY $name", array(250, 251));
- }
-
- /**
- * Send an SMTP NOOP command.
- * Used to keep keep-alives alive, doesn't actually do anything
- * @access public
- * @return boolean
- */
- public function noop()
- {
- return $this->sendCommand('NOOP', 'NOOP', 250);
- }
-
- /**
- * Send an SMTP TURN command.
- * This is an optional command for SMTP that this class does not support.
- * This method is here to make the RFC821 Definition complete for this class
- * and _may_ be implemented in future
- * Implements from rfc 821: TURN
- * @access public
- * @return boolean
- */
- public function turn()
- {
- $this->setError('The SMTP TURN command is not implemented');
- $this->edebug('SMTP NOTICE: ' . $this->error['error'], self::DEBUG_CLIENT);
- return false;
- }
-
- /**
- * Send raw data to the server.
- * @param string $data The data to send
- * @access public
- * @return integer|boolean The number of bytes sent to the server or false on error
- */
- public function client_send($data)
- {
- $this->edebug("CLIENT -> SERVER: $data", self::DEBUG_CLIENT);
- return fwrite($this->smtp_conn, $data);
- }
-
- /**
- * Get the latest error.
- * @access public
- * @return array
- */
- public function getError()
- {
- return $this->error;
- }
-
- /**
- * Get SMTP extensions available on the server
- * @access public
- * @return array|null
- */
- public function getServerExtList()
- {
- return $this->server_caps;
- }
-
- /**
- * A multipurpose method
- * The method works in three ways, dependent on argument value and current state
- * 1. HELO/EHLO was not sent - returns null and set up $this->error
- * 2. HELO was sent
- * $name = 'HELO': returns server name
- * $name = 'EHLO': returns boolean false
- * $name = any string: returns null and set up $this->error
- * 3. EHLO was sent
- * $name = 'HELO'|'EHLO': returns server name
- * $name = any string: if extension $name exists, returns boolean True
- * or its options. Otherwise returns boolean False
- * In other words, one can use this method to detect 3 conditions:
- * - null returned: handshake was not or we don't know about ext (refer to $this->error)
- * - false returned: the requested feature exactly not exists
- * - positive value returned: the requested feature exists
- * @param string $name Name of SMTP extension or 'HELO'|'EHLO'
- * @return mixed
- */
- public function getServerExt($name)
- {
- if (!$this->server_caps) {
- $this->setError('No HELO/EHLO was sent');
- return null;
- }
-
- // the tight logic knot ;)
- if (!array_key_exists($name, $this->server_caps)) {
- if ($name == 'HELO') {
- return $this->server_caps['EHLO'];
- }
- if ($name == 'EHLO' || array_key_exists('EHLO', $this->server_caps)) {
- return false;
- }
- $this->setError('HELO handshake was used. Client knows nothing about server extensions');
- return null;
- }
-
- return $this->server_caps[$name];
- }
-
- /**
- * Get the last reply from the server.
- * @access public
- * @return string
- */
- public function getLastReply()
- {
- return $this->last_reply;
- }
-
- /**
- * Read the SMTP server's response.
- * Either before eof or socket timeout occurs on the operation.
- * With SMTP we can tell if we have more lines to read if the
- * 4th character is '-' symbol. If it is a space then we don't
- * need to read anything else.
- * @access protected
- * @return string
- */
- protected function get_lines()
- {
- // If the connection is bad, give up straight away
- if (!is_resource($this->smtp_conn)) {
- return '';
- }
- $data = '';
- $endtime = 0;
- stream_set_timeout($this->smtp_conn, $this->Timeout);
- if ($this->Timelimit > 0) {
- $endtime = time() + $this->Timelimit;
- }
- while (is_resource($this->smtp_conn) && !feof($this->smtp_conn)) {
- $str = @fgets($this->smtp_conn, 515);
- $this->edebug("SMTP -> get_lines(): \$data is \"$data\"", self::DEBUG_LOWLEVEL);
- $this->edebug("SMTP -> get_lines(): \$str is \"$str\"", self::DEBUG_LOWLEVEL);
- $data .= $str;
- // If 4th character is a space, we are done reading, break the loop, micro-optimisation over strlen
- if ((isset($str[3]) and $str[3] == ' ')) {
- break;
- }
- // Timed-out? Log and break
- $info = stream_get_meta_data($this->smtp_conn);
- if ($info['timed_out']) {
- $this->edebug(
- 'SMTP -> get_lines(): timed-out (' . $this->Timeout . ' sec)',
- self::DEBUG_LOWLEVEL
- );
- break;
- }
- // Now check if reads took too long
- if ($endtime and time() > $endtime) {
- $this->edebug(
- 'SMTP -> get_lines(): timelimit reached ('.
- $this->Timelimit . ' sec)',
- self::DEBUG_LOWLEVEL
- );
- break;
- }
- }
- return $data;
- }
-
- /**
- * Enable or disable VERP address generation.
- * @param boolean $enabled
- */
- public function setVerp($enabled = false)
- {
- $this->do_verp = $enabled;
- }
-
- /**
- * Get VERP address generation mode.
- * @return boolean
- */
- public function getVerp()
- {
- return $this->do_verp;
- }
-
- /**
- * Set error messages and codes.
- * @param string $message The error message
- * @param string $detail Further detail on the error
- * @param string $smtp_code An associated SMTP error code
- * @param string $smtp_code_ex Extended SMTP code
- */
- protected function setError($message, $detail = '', $smtp_code = '', $smtp_code_ex = '')
- {
- $this->error = array(
- 'error' => $message,
- 'detail' => $detail,
- 'smtp_code' => $smtp_code,
- 'smtp_code_ex' => $smtp_code_ex
- );
- }
-
- /**
- * Set debug output method.
- * @param string|callable $method The name of the mechanism to use for debugging output, or a callable to handle it.
- */
- public function setDebugOutput($method = 'echo')
- {
- $this->Debugoutput = $method;
- }
-
- /**
- * Get debug output method.
- * @return string
- */
- public function getDebugOutput()
- {
- return $this->Debugoutput;
- }
-
- /**
- * Set debug output level.
- * @param integer $level
- */
- public function setDebugLevel($level = 0)
- {
- $this->do_debug = $level;
- }
-
- /**
- * Get debug output level.
- * @return integer
- */
- public function getDebugLevel()
- {
- return $this->do_debug;
- }
-
- /**
- * Set SMTP timeout.
- * @param integer $timeout
- */
- public function setTimeout($timeout = 0)
- {
- $this->Timeout = $timeout;
- }
-
- /**
- * Get SMTP timeout.
- * @return integer
- */
- public function getTimeout()
- {
- return $this->Timeout;
- }
-}
+
+ * @author Jim Jagielski (jimjag)
+ * @author Andy Prevost (codeworxtech)
+ * @author Brent R. Matzelle (original founder)
+ * @copyright 2014 Marcus Bointon
+ * @copyright 2010 - 2012 Jim Jagielski
+ * @copyright 2004 - 2009 Andy Prevost
+ * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
+ * @note This program is distributed in the hope that it will be useful - WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
+/**
+ * PHPMailer RFC821 SMTP email transport class.
+ * Implements RFC 821 SMTP commands and provides some utility methods for sending mail to an SMTP server.
+ * @package PHPMailer
+ * @author Chris Ryan
+ * @author Marcus Bointon
+ */
+class SMTP
+{
+ /**
+ * The PHPMailer SMTP version number.
+ * @var string
+ */
+ const VERSION = '5.2.14';
+
+ /**
+ * SMTP line break constant.
+ * @var string
+ */
+ const CRLF = "\r\n";
+
+ /**
+ * The SMTP port to use if one is not specified.
+ * @var integer
+ */
+ const DEFAULT_SMTP_PORT = 25;
+
+ /**
+ * The maximum line length allowed by RFC 2822 section 2.1.1
+ * @var integer
+ */
+ const MAX_LINE_LENGTH = 998;
+
+ /**
+ * Debug level for no output
+ */
+ const DEBUG_OFF = 0;
+
+ /**
+ * Debug level to show client -> server messages
+ */
+ const DEBUG_CLIENT = 1;
+
+ /**
+ * Debug level to show client -> server and server -> client messages
+ */
+ const DEBUG_SERVER = 2;
+
+ /**
+ * Debug level to show connection status, client -> server and server -> client messages
+ */
+ const DEBUG_CONNECTION = 3;
+
+ /**
+ * Debug level to show all messages
+ */
+ const DEBUG_LOWLEVEL = 4;
+
+ /**
+ * The PHPMailer SMTP Version number.
+ * @var string
+ * @deprecated Use the `VERSION` constant instead
+ * @see SMTP::VERSION
+ */
+ public $Version = '5.2.14';
+
+ /**
+ * SMTP server port number.
+ * @var integer
+ * @deprecated This is only ever used as a default value, so use the `DEFAULT_SMTP_PORT` constant instead
+ * @see SMTP::DEFAULT_SMTP_PORT
+ */
+ public $SMTP_PORT = 25;
+
+ /**
+ * SMTP reply line ending.
+ * @var string
+ * @deprecated Use the `CRLF` constant instead
+ * @see SMTP::CRLF
+ */
+ public $CRLF = "\r\n";
+
+ /**
+ * Debug output level.
+ * Options:
+ * * self::DEBUG_OFF (`0`) No debug output, default
+ * * self::DEBUG_CLIENT (`1`) Client commands
+ * * self::DEBUG_SERVER (`2`) Client commands and server responses
+ * * self::DEBUG_CONNECTION (`3`) As DEBUG_SERVER plus connection status
+ * * self::DEBUG_LOWLEVEL (`4`) Low-level data output, all messages
+ * @var integer
+ */
+ public $do_debug = self::DEBUG_OFF;
+
+ /**
+ * How to handle debug output.
+ * Options:
+ * * `echo` Output plain-text as-is, appropriate for CLI
+ * * `html` Output escaped, line breaks converted to ` `, appropriate for browser output
+ * * `error_log` Output to error log as configured in php.ini
+ *
+ * Alternatively, you can provide a callable expecting two params: a message string and the debug level:
+ *
+ * $smtp->Debugoutput = function($str, $level) {echo "debug level $level; message: $str";};
+ *
+ * @var string|callable
+ */
+ public $Debugoutput = 'echo';
+
+ /**
+ * Whether to use VERP.
+ * @link http://en.wikipedia.org/wiki/Variable_envelope_return_path
+ * @link http://www.postfix.org/VERP_README.html Info on VERP
+ * @var boolean
+ */
+ public $do_verp = false;
+
+ /**
+ * The timeout value for connection, in seconds.
+ * Default of 5 minutes (300sec) is from RFC2821 section 4.5.3.2
+ * This needs to be quite high to function correctly with hosts using greetdelay as an anti-spam measure.
+ * @link http://tools.ietf.org/html/rfc2821#section-4.5.3.2
+ * @var integer
+ */
+ public $Timeout = 300;
+
+ /**
+ * How long to wait for commands to complete, in seconds.
+ * Default of 5 minutes (300sec) is from RFC2821 section 4.5.3.2
+ * @var integer
+ */
+ public $Timelimit = 300;
+
+ /**
+ * The socket for the server connection.
+ * @var resource
+ */
+ protected $smtp_conn;
+
+ /**
+ * Error information, if any, for the last SMTP command.
+ * @var array
+ */
+ protected $error = array(
+ 'error' => '',
+ 'detail' => '',
+ 'smtp_code' => '',
+ 'smtp_code_ex' => ''
+ );
+
+ /**
+ * The reply the server sent to us for HELO.
+ * If null, no HELO string has yet been received.
+ * @var string|null
+ */
+ protected $helo_rply = null;
+
+ /**
+ * The set of SMTP extensions sent in reply to EHLO command.
+ * Indexes of the array are extension names.
+ * Value at index 'HELO' or 'EHLO' (according to command that was sent)
+ * represents the server name. In case of HELO it is the only element of the array.
+ * Other values can be boolean TRUE or an array containing extension options.
+ * If null, no HELO/EHLO string has yet been received.
+ * @var array|null
+ */
+ protected $server_caps = null;
+
+ /**
+ * The most recent reply received from the server.
+ * @var string
+ */
+ protected $last_reply = '';
+
+ /**
+ * Output debugging info via a user-selected method.
+ * @see SMTP::$Debugoutput
+ * @see SMTP::$do_debug
+ * @param string $str Debug string to output
+ * @param integer $level The debug level of this message; see DEBUG_* constants
+ * @return void
+ */
+ protected function edebug($str, $level = 0)
+ {
+ if ($level > $this->do_debug) {
+ return;
+ }
+ //Avoid clash with built-in function names
+ if (!in_array($this->Debugoutput, array('error_log', 'html', 'echo')) and is_callable($this->Debugoutput)) {
+ call_user_func($this->Debugoutput, $str, $this->do_debug);
+ return;
+ }
+ switch ($this->Debugoutput) {
+ case 'error_log':
+ //Don't output, just log
+ error_log($str);
+ break;
+ case 'html':
+ //Cleans up output a bit for a better looking, HTML-safe output
+ echo htmlentities(
+ preg_replace('/[\r\n]+/', '', $str),
+ ENT_QUOTES,
+ 'UTF-8'
+ )
+ . " \n";
+ break;
+ case 'echo':
+ default:
+ //Normalize line breaks
+ $str = preg_replace('/(\r\n|\r|\n)/ms', "\n", $str);
+ echo gmdate('Y-m-d H:i:s') . "\t" . str_replace(
+ "\n",
+ "\n \t ",
+ trim($str)
+ )."\n";
+ }
+ }
+
+ /**
+ * Connect to an SMTP server.
+ * @param string $host SMTP server IP or host name
+ * @param integer $port The port number to connect to
+ * @param integer $timeout How long to wait for the connection to open
+ * @param array $options An array of options for stream_context_create()
+ * @access public
+ * @return boolean
+ */
+ public function connect($host, $port = null, $timeout = 30, $options = array())
+ {
+ static $streamok;
+ //This is enabled by default since 5.0.0 but some providers disable it
+ //Check this once and cache the result
+ if (is_null($streamok)) {
+ $streamok = function_exists('stream_socket_client');
+ }
+ // Clear errors to avoid confusion
+ $this->setError('');
+ // Make sure we are __not__ connected
+ if ($this->connected()) {
+ // Already connected, generate error
+ $this->setError('Already connected to a server');
+ return false;
+ }
+ if (empty($port)) {
+ $port = self::DEFAULT_SMTP_PORT;
+ }
+ // Connect to the SMTP server
+ $this->edebug(
+ "Connection: opening to $host:$port, timeout=$timeout, options=".var_export($options, true),
+ self::DEBUG_CONNECTION
+ );
+ $errno = 0;
+ $errstr = '';
+ if ($streamok) {
+ $socket_context = stream_context_create($options);
+ //Suppress errors; connection failures are handled at a higher level
+ $this->smtp_conn = @stream_socket_client(
+ $host . ":" . $port,
+ $errno,
+ $errstr,
+ $timeout,
+ STREAM_CLIENT_CONNECT,
+ $socket_context
+ );
+ } else {
+ //Fall back to fsockopen which should work in more places, but is missing some features
+ $this->edebug(
+ "Connection: stream_socket_client not available, falling back to fsockopen",
+ self::DEBUG_CONNECTION
+ );
+ $this->smtp_conn = fsockopen(
+ $host,
+ $port,
+ $errno,
+ $errstr,
+ $timeout
+ );
+ }
+ // Verify we connected properly
+ if (!is_resource($this->smtp_conn)) {
+ $this->setError(
+ 'Failed to connect to server',
+ $errno,
+ $errstr
+ );
+ $this->edebug(
+ 'SMTP ERROR: ' . $this->error['error']
+ . ": $errstr ($errno)",
+ self::DEBUG_CLIENT
+ );
+ return false;
+ }
+ $this->edebug('Connection: opened', self::DEBUG_CONNECTION);
+ // SMTP server can take longer to respond, give longer timeout for first read
+ // Windows does not have support for this timeout function
+ if (substr(PHP_OS, 0, 3) != 'WIN') {
+ $max = ini_get('max_execution_time');
+ // Don't bother if unlimited
+ if ($max != 0 && $timeout > $max) {
+ @set_time_limit($timeout);
+ }
+ stream_set_timeout($this->smtp_conn, $timeout, 0);
+ }
+ // Get any announcement
+ $announce = $this->get_lines();
+ $this->edebug('SERVER -> CLIENT: ' . $announce, self::DEBUG_SERVER);
+ return true;
+ }
+
+ /**
+ * Initiate a TLS (encrypted) session.
+ * @access public
+ * @return boolean
+ */
+ public function startTLS()
+ {
+ if (!$this->sendCommand('STARTTLS', 'STARTTLS', 220)) {
+ return false;
+ }
+ // Begin encrypted connection
+ if (!stream_socket_enable_crypto(
+ $this->smtp_conn,
+ true,
+ STREAM_CRYPTO_METHOD_TLS_CLIENT
+ )) {
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * Perform SMTP authentication.
+ * Must be run after hello().
+ * @see hello()
+ * @param string $username The user name
+ * @param string $password The password
+ * @param string $authtype The auth type (PLAIN, LOGIN, NTLM, CRAM-MD5, XOAUTH2)
+ * @param string $realm The auth realm for NTLM
+ * @param string $workstation The auth workstation for NTLM
+ * @param null|OAuth $OAuth An optional OAuth instance (@see PHPMailerOAuth)
+ * @return bool True if successfully authenticated.* @access public
+ */
+ public function authenticate(
+ $username,
+ $password,
+ $authtype = null,
+ $realm = '',
+ $workstation = '',
+ $OAuth = null
+ ) {
+ if (!$this->server_caps) {
+ $this->setError('Authentication is not allowed before HELO/EHLO');
+ return false;
+ }
+
+ if (array_key_exists('EHLO', $this->server_caps)) {
+ // SMTP extensions are available. Let's try to find a proper authentication method
+
+ if (!array_key_exists('AUTH', $this->server_caps)) {
+ $this->setError('Authentication is not allowed at this stage');
+ // 'at this stage' means that auth may be allowed after the stage changes
+ // e.g. after STARTTLS
+ return false;
+ }
+
+ self::edebug('Auth method requested: ' . ($authtype ? $authtype : 'UNKNOWN'), self::DEBUG_LOWLEVEL);
+ self::edebug(
+ 'Auth methods available on the server: ' . implode(',', $this->server_caps['AUTH']),
+ self::DEBUG_LOWLEVEL
+ );
+
+ if (empty($authtype)) {
+ foreach (array('LOGIN', 'CRAM-MD5', 'NTLM', 'PLAIN', 'XOAUTH2') as $method) {
+ if (in_array($method, $this->server_caps['AUTH'])) {
+ $authtype = $method;
+ break;
+ }
+ }
+ if (empty($authtype)) {
+ $this->setError('No supported authentication methods found');
+ return false;
+ }
+ self::edebug('Auth method selected: '.$authtype, self::DEBUG_LOWLEVEL);
+ }
+
+ if (!in_array($authtype, $this->server_caps['AUTH'])) {
+ $this->setError("The requested authentication method \"$authtype\" is not supported by the server");
+ return false;
+ }
+ } elseif (empty($authtype)) {
+ $authtype = 'LOGIN';
+ }
+ switch ($authtype) {
+ case 'PLAIN':
+ // Start authentication
+ if (!$this->sendCommand('AUTH', 'AUTH PLAIN', 334)) {
+ return false;
+ }
+ // Send encoded username and password
+ if (!$this->sendCommand(
+ 'User & Password',
+ base64_encode("\0" . $username . "\0" . $password),
+ 235
+ )
+ ) {
+ return false;
+ }
+ break;
+ case 'LOGIN':
+ // Start authentication
+ if (!$this->sendCommand('AUTH', 'AUTH LOGIN', 334)) {
+ return false;
+ }
+ if (!$this->sendCommand("Username", base64_encode($username), 334)) {
+ return false;
+ }
+ if (!$this->sendCommand("Password", base64_encode($password), 235)) {
+ return false;
+ }
+ break;
+ case 'XOAUTH2':
+ //If the OAuth Instance is not set. Can be a case when PHPMailer is used
+ //instead of PHPMailerOAuth
+ if (is_null($OAuth)) {
+ return false;
+ }
+ $oauth = $OAuth->getOauth64();
+
+ // Start authentication
+ if (!$this->sendCommand('AUTH', 'AUTH XOAUTH2 ' . $oauth, 235)) {
+ return false;
+ }
+ break;
+ case 'NTLM':
+ /*
+ * ntlm_sasl_client.php
+ * Bundled with Permission
+ *
+ * How to telnet in windows:
+ * http://technet.microsoft.com/en-us/library/aa995718%28EXCHG.65%29.aspx
+ * PROTOCOL Docs http://curl.haxx.se/rfc/ntlm.html#ntlmSmtpAuthentication
+ */
+ require_once 'extras/ntlm_sasl_client.php';
+ $temp = new stdClass;
+ $ntlm_client = new ntlm_sasl_client_class;
+ //Check that functions are available
+ if (!$ntlm_client->Initialize($temp)) {
+ $this->setError($temp->error);
+ $this->edebug(
+ 'You need to enable some modules in your php.ini file: '
+ . $this->error['error'],
+ self::DEBUG_CLIENT
+ );
+ return false;
+ }
+ //msg1
+ $msg1 = $ntlm_client->TypeMsg1($realm, $workstation); //msg1
+
+ if (!$this->sendCommand(
+ 'AUTH NTLM',
+ 'AUTH NTLM ' . base64_encode($msg1),
+ 334
+ )
+ ) {
+ return false;
+ }
+ //Though 0 based, there is a white space after the 3 digit number
+ //msg2
+ $challenge = substr($this->last_reply, 3);
+ $challenge = base64_decode($challenge);
+ $ntlm_res = $ntlm_client->NTLMResponse(
+ substr($challenge, 24, 8),
+ $password
+ );
+ //msg3
+ $msg3 = $ntlm_client->TypeMsg3(
+ $ntlm_res,
+ $username,
+ $realm,
+ $workstation
+ );
+ // send encoded username
+ return $this->sendCommand('Username', base64_encode($msg3), 235);
+ case 'CRAM-MD5':
+ // Start authentication
+ if (!$this->sendCommand('AUTH CRAM-MD5', 'AUTH CRAM-MD5', 334)) {
+ return false;
+ }
+ // Get the challenge
+ $challenge = base64_decode(substr($this->last_reply, 4));
+
+ // Build the response
+ $response = $username . ' ' . $this->hmac($challenge, $password);
+
+ // send encoded credentials
+ return $this->sendCommand('Username', base64_encode($response), 235);
+ default:
+ $this->setError("Authentication method \"$authtype\" is not supported");
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * Calculate an MD5 HMAC hash.
+ * Works like hash_hmac('md5', $data, $key)
+ * in case that function is not available
+ * @param string $data The data to hash
+ * @param string $key The key to hash with
+ * @access protected
+ * @return string
+ */
+ protected function hmac($data, $key)
+ {
+ if (function_exists('hash_hmac')) {
+ return hash_hmac('md5', $data, $key);
+ }
+
+ // The following borrowed from
+ // http://php.net/manual/en/function.mhash.php#27225
+
+ // RFC 2104 HMAC implementation for php.
+ // Creates an md5 HMAC.
+ // Eliminates the need to install mhash to compute a HMAC
+ // by Lance Rushing
+
+ $bytelen = 64; // byte length for md5
+ if (strlen($key) > $bytelen) {
+ $key = pack('H*', md5($key));
+ }
+ $key = str_pad($key, $bytelen, chr(0x00));
+ $ipad = str_pad('', $bytelen, chr(0x36));
+ $opad = str_pad('', $bytelen, chr(0x5c));
+ $k_ipad = $key ^ $ipad;
+ $k_opad = $key ^ $opad;
+
+ return md5($k_opad . pack('H*', md5($k_ipad . $data)));
+ }
+
+ /**
+ * Check connection state.
+ * @access public
+ * @return boolean True if connected.
+ */
+ public function connected()
+ {
+ if (is_resource($this->smtp_conn)) {
+ $sock_status = stream_get_meta_data($this->smtp_conn);
+ if ($sock_status['eof']) {
+ // The socket is valid but we are not connected
+ $this->edebug(
+ 'SMTP NOTICE: EOF caught while checking if connected',
+ self::DEBUG_CLIENT
+ );
+ $this->close();
+ return false;
+ }
+ return true; // everything looks good
+ }
+ return false;
+ }
+
+ /**
+ * Close the socket and clean up the state of the class.
+ * Don't use this function without first trying to use QUIT.
+ * @see quit()
+ * @access public
+ * @return void
+ */
+ public function close()
+ {
+ $this->setError('');
+ $this->server_caps = null;
+ $this->helo_rply = null;
+ if (is_resource($this->smtp_conn)) {
+ // close the connection and cleanup
+ fclose($this->smtp_conn);
+ $this->smtp_conn = null; //Makes for cleaner serialization
+ $this->edebug('Connection: closed', self::DEBUG_CONNECTION);
+ }
+ }
+
+ /**
+ * Send an SMTP DATA command.
+ * Issues a data command and sends the msg_data to the server,
+ * finializing the mail transaction. $msg_data is the message
+ * that is to be send with the headers. Each header needs to be
+ * on a single line followed by a with the message headers
+ * and the message body being separated by and additional .
+ * Implements rfc 821: DATA
+ * @param string $msg_data Message data to send
+ * @access public
+ * @return boolean
+ */
+ public function data($msg_data)
+ {
+ //This will use the standard timelimit
+ if (!$this->sendCommand('DATA', 'DATA', 354)) {
+ return false;
+ }
+
+ /* The server is ready to accept data!
+ * According to rfc821 we should not send more than 1000 characters on a single line (including the CRLF)
+ * so we will break the data up into lines by \r and/or \n then if needed we will break each of those into
+ * smaller lines to fit within the limit.
+ * We will also look for lines that start with a '.' and prepend an additional '.'.
+ * NOTE: this does not count towards line-length limit.
+ */
+
+ // Normalize line breaks before exploding
+ $lines = explode("\n", str_replace(array("\r\n", "\r"), "\n", $msg_data));
+
+ /* To distinguish between a complete RFC822 message and a plain message body, we check if the first field
+ * of the first line (':' separated) does not contain a space then it _should_ be a header and we will
+ * process all lines before a blank line as headers.
+ */
+
+ $field = substr($lines[0], 0, strpos($lines[0], ':'));
+ $in_headers = false;
+ if (!empty($field) && strpos($field, ' ') === false) {
+ $in_headers = true;
+ }
+
+ foreach ($lines as $line) {
+ $lines_out = array();
+ if ($in_headers and $line == '') {
+ $in_headers = false;
+ }
+ //Break this line up into several smaller lines if it's too long
+ //Micro-optimisation: isset($str[$len]) is faster than (strlen($str) > $len),
+ while (isset($line[self::MAX_LINE_LENGTH])) {
+ //Working backwards, try to find a space within the last MAX_LINE_LENGTH chars of the line to break on
+ //so as to avoid breaking in the middle of a word
+ $pos = strrpos(substr($line, 0, self::MAX_LINE_LENGTH), ' ');
+ //Deliberately matches both false and 0
+ if (!$pos) {
+ //No nice break found, add a hard break
+ $pos = self::MAX_LINE_LENGTH - 1;
+ $lines_out[] = substr($line, 0, $pos);
+ $line = substr($line, $pos);
+ } else {
+ //Break at the found point
+ $lines_out[] = substr($line, 0, $pos);
+ //Move along by the amount we dealt with
+ $line = substr($line, $pos + 1);
+ }
+ //If processing headers add a LWSP-char to the front of new line RFC822 section 3.1.1
+ if ($in_headers) {
+ $line = "\t" . $line;
+ }
+ }
+ $lines_out[] = $line;
+
+ //Send the lines to the server
+ foreach ($lines_out as $line_out) {
+ //RFC2821 section 4.5.2
+ if (!empty($line_out) and $line_out[0] == '.') {
+ $line_out = '.' . $line_out;
+ }
+ $this->client_send($line_out . self::CRLF);
+ }
+ }
+
+ //Message data has been sent, complete the command
+ //Increase timelimit for end of DATA command
+ $savetimelimit = $this->Timelimit;
+ $this->Timelimit = $this->Timelimit * 2;
+ $result = $this->sendCommand('DATA END', '.', 250);
+ //Restore timelimit
+ $this->Timelimit = $savetimelimit;
+ return $result;
+ }
+
+ /**
+ * Send an SMTP HELO or EHLO command.
+ * Used to identify the sending server to the receiving server.
+ * This makes sure that client and server are in a known state.
+ * Implements RFC 821: HELO
+ * and RFC 2821 EHLO.
+ * @param string $host The host name or IP to connect to
+ * @access public
+ * @return boolean
+ */
+ public function hello($host = '')
+ {
+ //Try extended hello first (RFC 2821)
+ return (boolean)($this->sendHello('EHLO', $host) or $this->sendHello('HELO', $host));
+ }
+
+ /**
+ * Send an SMTP HELO or EHLO command.
+ * Low-level implementation used by hello()
+ * @see hello()
+ * @param string $hello The HELO string
+ * @param string $host The hostname to say we are
+ * @access protected
+ * @return boolean
+ */
+ protected function sendHello($hello, $host)
+ {
+ $noerror = $this->sendCommand($hello, $hello . ' ' . $host, 250);
+ $this->helo_rply = $this->last_reply;
+ if ($noerror) {
+ $this->parseHelloFields($hello);
+ } else {
+ $this->server_caps = null;
+ }
+ return $noerror;
+ }
+
+ /**
+ * Parse a reply to HELO/EHLO command to discover server extensions.
+ * In case of HELO, the only parameter that can be discovered is a server name.
+ * @access protected
+ * @param string $type - 'HELO' or 'EHLO'
+ */
+ protected function parseHelloFields($type)
+ {
+ $this->server_caps = array();
+ $lines = explode("\n", $this->last_reply);
+
+ foreach ($lines as $n => $s) {
+ //First 4 chars contain response code followed by - or space
+ $s = trim(substr($s, 4));
+ if (empty($s)) {
+ continue;
+ }
+ $fields = explode(' ', $s);
+ if (!empty($fields)) {
+ if (!$n) {
+ $name = $type;
+ $fields = $fields[0];
+ } else {
+ $name = array_shift($fields);
+ switch ($name) {
+ case 'SIZE':
+ $fields = ($fields ? $fields[0] : 0);
+ break;
+ case 'AUTH':
+ if (!is_array($fields)) {
+ $fields = array();
+ }
+ break;
+ default:
+ $fields = true;
+ }
+ }
+ $this->server_caps[$name] = $fields;
+ }
+ }
+ }
+
+ /**
+ * Send an SMTP MAIL command.
+ * Starts a mail transaction from the email address specified in
+ * $from. Returns true if successful or false otherwise. If True
+ * the mail transaction is started and then one or more recipient
+ * commands may be called followed by a data command.
+ * Implements rfc 821: MAIL FROM:
+ * @param string $from Source address of this message
+ * @access public
+ * @return boolean
+ */
+ public function mail($from)
+ {
+ $useVerp = ($this->do_verp ? ' XVERP' : '');
+ return $this->sendCommand(
+ 'MAIL FROM',
+ 'MAIL FROM:<' . $from . '>' . $useVerp,
+ 250
+ );
+ }
+
+ /**
+ * Send an SMTP QUIT command.
+ * Closes the socket if there is no error or the $close_on_error argument is true.
+ * Implements from rfc 821: QUIT
+ * @param boolean $close_on_error Should the connection close if an error occurs?
+ * @access public
+ * @return boolean
+ */
+ public function quit($close_on_error = true)
+ {
+ $noerror = $this->sendCommand('QUIT', 'QUIT', 221);
+ $err = $this->error; //Save any error
+ if ($noerror or $close_on_error) {
+ $this->close();
+ $this->error = $err; //Restore any error from the quit command
+ }
+ return $noerror;
+ }
+
+ /**
+ * Send an SMTP RCPT command.
+ * Sets the TO argument to $toaddr.
+ * Returns true if the recipient was accepted false if it was rejected.
+ * Implements from rfc 821: RCPT TO:
+ * @param string $address The address the message is being sent to
+ * @access public
+ * @return boolean
+ */
+ public function recipient($address)
+ {
+ return $this->sendCommand(
+ 'RCPT TO',
+ 'RCPT TO:<' . $address . '>',
+ array(250, 251)
+ );
+ }
+
+ /**
+ * Send an SMTP RSET command.
+ * Abort any transaction that is currently in progress.
+ * Implements rfc 821: RSET
+ * @access public
+ * @return boolean True on success.
+ */
+ public function reset()
+ {
+ return $this->sendCommand('RSET', 'RSET', 250);
+ }
+
+ /**
+ * Send a command to an SMTP server and check its return code.
+ * @param string $command The command name - not sent to the server
+ * @param string $commandstring The actual command to send
+ * @param integer|array $expect One or more expected integer success codes
+ * @access protected
+ * @return boolean True on success.
+ */
+ protected function sendCommand($command, $commandstring, $expect)
+ {
+ if (!$this->connected()) {
+ $this->setError("Called $command without being connected");
+ return false;
+ }
+ //Reject line breaks in all commands
+ if (strpos($commandstring, "\n") !== false or strpos($commandstring, "\r") !== false) {
+ $this->setError("Command '$command' contained line breaks");
+ return false;
+ }
+ $this->client_send($commandstring . self::CRLF);
+
+ $this->last_reply = $this->get_lines();
+ // Fetch SMTP code and possible error code explanation
+ $matches = array();
+ if (preg_match("/^([0-9]{3})[ -](?:([0-9]\\.[0-9]\\.[0-9]) )?/", $this->last_reply, $matches)) {
+ $code = $matches[1];
+ $code_ex = (count($matches) > 2 ? $matches[2] : null);
+ // Cut off error code from each response line
+ $detail = preg_replace(
+ "/{$code}[ -]".($code_ex ? str_replace('.', '\\.', $code_ex).' ' : '')."/m",
+ '',
+ $this->last_reply
+ );
+ } else {
+ // Fall back to simple parsing if regex fails
+ $code = substr($this->last_reply, 0, 3);
+ $code_ex = null;
+ $detail = substr($this->last_reply, 4);
+ }
+
+ $this->edebug('SERVER -> CLIENT: ' . $this->last_reply, self::DEBUG_SERVER);
+
+ if (!in_array($code, (array)$expect)) {
+ $this->setError(
+ "$command command failed",
+ $detail,
+ $code,
+ $code_ex
+ );
+ $this->edebug(
+ 'SMTP ERROR: ' . $this->error['error'] . ': ' . $this->last_reply,
+ self::DEBUG_CLIENT
+ );
+ return false;
+ }
+
+ $this->setError('');
+ return true;
+ }
+
+ /**
+ * Send an SMTP SAML command.
+ * Starts a mail transaction from the email address specified in $from.
+ * Returns true if successful or false otherwise. If True
+ * the mail transaction is started and then one or more recipient
+ * commands may be called followed by a data command. This command
+ * will send the message to the users terminal if they are logged
+ * in and send them an email.
+ * Implements rfc 821: SAML FROM:
+ * @param string $from The address the message is from
+ * @access public
+ * @return boolean
+ */
+ public function sendAndMail($from)
+ {
+ return $this->sendCommand('SAML', "SAML FROM:$from", 250);
+ }
+
+ /**
+ * Send an SMTP VRFY command.
+ * @param string $name The name to verify
+ * @access public
+ * @return boolean
+ */
+ public function verify($name)
+ {
+ return $this->sendCommand('VRFY', "VRFY $name", array(250, 251));
+ }
+
+ /**
+ * Send an SMTP NOOP command.
+ * Used to keep keep-alives alive, doesn't actually do anything
+ * @access public
+ * @return boolean
+ */
+ public function noop()
+ {
+ return $this->sendCommand('NOOP', 'NOOP', 250);
+ }
+
+ /**
+ * Send an SMTP TURN command.
+ * This is an optional command for SMTP that this class does not support.
+ * This method is here to make the RFC821 Definition complete for this class
+ * and _may_ be implemented in future
+ * Implements from rfc 821: TURN
+ * @access public
+ * @return boolean
+ */
+ public function turn()
+ {
+ $this->setError('The SMTP TURN command is not implemented');
+ $this->edebug('SMTP NOTICE: ' . $this->error['error'], self::DEBUG_CLIENT);
+ return false;
+ }
+
+ /**
+ * Send raw data to the server.
+ * @param string $data The data to send
+ * @access public
+ * @return integer|boolean The number of bytes sent to the server or false on error
+ */
+ public function client_send($data)
+ {
+ $this->edebug("CLIENT -> SERVER: $data", self::DEBUG_CLIENT);
+ return fwrite($this->smtp_conn, $data);
+ }
+
+ /**
+ * Get the latest error.
+ * @access public
+ * @return array
+ */
+ public function getError()
+ {
+ return $this->error;
+ }
+
+ /**
+ * Get SMTP extensions available on the server
+ * @access public
+ * @return array|null
+ */
+ public function getServerExtList()
+ {
+ return $this->server_caps;
+ }
+
+ /**
+ * A multipurpose method
+ * The method works in three ways, dependent on argument value and current state
+ * 1. HELO/EHLO was not sent - returns null and set up $this->error
+ * 2. HELO was sent
+ * $name = 'HELO': returns server name
+ * $name = 'EHLO': returns boolean false
+ * $name = any string: returns null and set up $this->error
+ * 3. EHLO was sent
+ * $name = 'HELO'|'EHLO': returns server name
+ * $name = any string: if extension $name exists, returns boolean True
+ * or its options. Otherwise returns boolean False
+ * In other words, one can use this method to detect 3 conditions:
+ * - null returned: handshake was not or we don't know about ext (refer to $this->error)
+ * - false returned: the requested feature exactly not exists
+ * - positive value returned: the requested feature exists
+ * @param string $name Name of SMTP extension or 'HELO'|'EHLO'
+ * @return mixed
+ */
+ public function getServerExt($name)
+ {
+ if (!$this->server_caps) {
+ $this->setError('No HELO/EHLO was sent');
+ return null;
+ }
+
+ // the tight logic knot ;)
+ if (!array_key_exists($name, $this->server_caps)) {
+ if ($name == 'HELO') {
+ return $this->server_caps['EHLO'];
+ }
+ if ($name == 'EHLO' || array_key_exists('EHLO', $this->server_caps)) {
+ return false;
+ }
+ $this->setError('HELO handshake was used. Client knows nothing about server extensions');
+ return null;
+ }
+
+ return $this->server_caps[$name];
+ }
+
+ /**
+ * Get the last reply from the server.
+ * @access public
+ * @return string
+ */
+ public function getLastReply()
+ {
+ return $this->last_reply;
+ }
+
+ /**
+ * Read the SMTP server's response.
+ * Either before eof or socket timeout occurs on the operation.
+ * With SMTP we can tell if we have more lines to read if the
+ * 4th character is '-' symbol. If it is a space then we don't
+ * need to read anything else.
+ * @access protected
+ * @return string
+ */
+ protected function get_lines()
+ {
+ // If the connection is bad, give up straight away
+ if (!is_resource($this->smtp_conn)) {
+ return '';
+ }
+ $data = '';
+ $endtime = 0;
+ stream_set_timeout($this->smtp_conn, $this->Timeout);
+ if ($this->Timelimit > 0) {
+ $endtime = time() + $this->Timelimit;
+ }
+ while (is_resource($this->smtp_conn) && !feof($this->smtp_conn)) {
+ $str = @fgets($this->smtp_conn, 515);
+ $this->edebug("SMTP -> get_lines(): \$data is \"$data\"", self::DEBUG_LOWLEVEL);
+ $this->edebug("SMTP -> get_lines(): \$str is \"$str\"", self::DEBUG_LOWLEVEL);
+ $data .= $str;
+ // If 4th character is a space, we are done reading, break the loop, micro-optimisation over strlen
+ if ((isset($str[3]) and $str[3] == ' ')) {
+ break;
+ }
+ // Timed-out? Log and break
+ $info = stream_get_meta_data($this->smtp_conn);
+ if ($info['timed_out']) {
+ $this->edebug(
+ 'SMTP -> get_lines(): timed-out (' . $this->Timeout . ' sec)',
+ self::DEBUG_LOWLEVEL
+ );
+ break;
+ }
+ // Now check if reads took too long
+ if ($endtime and time() > $endtime) {
+ $this->edebug(
+ 'SMTP -> get_lines(): timelimit reached ('.
+ $this->Timelimit . ' sec)',
+ self::DEBUG_LOWLEVEL
+ );
+ break;
+ }
+ }
+ return $data;
+ }
+
+ /**
+ * Enable or disable VERP address generation.
+ * @param boolean $enabled
+ */
+ public function setVerp($enabled = false)
+ {
+ $this->do_verp = $enabled;
+ }
+
+ /**
+ * Get VERP address generation mode.
+ * @return boolean
+ */
+ public function getVerp()
+ {
+ return $this->do_verp;
+ }
+
+ /**
+ * Set error messages and codes.
+ * @param string $message The error message
+ * @param string $detail Further detail on the error
+ * @param string $smtp_code An associated SMTP error code
+ * @param string $smtp_code_ex Extended SMTP code
+ */
+ protected function setError($message, $detail = '', $smtp_code = '', $smtp_code_ex = '')
+ {
+ $this->error = array(
+ 'error' => $message,
+ 'detail' => $detail,
+ 'smtp_code' => $smtp_code,
+ 'smtp_code_ex' => $smtp_code_ex
+ );
+ }
+
+ /**
+ * Set debug output method.
+ * @param string|callable $method The name of the mechanism to use for debugging output, or a callable to handle it.
+ */
+ public function setDebugOutput($method = 'echo')
+ {
+ $this->Debugoutput = $method;
+ }
+
+ /**
+ * Get debug output method.
+ * @return string
+ */
+ public function getDebugOutput()
+ {
+ return $this->Debugoutput;
+ }
+
+ /**
+ * Set debug output level.
+ * @param integer $level
+ */
+ public function setDebugLevel($level = 0)
+ {
+ $this->do_debug = $level;
+ }
+
+ /**
+ * Get debug output level.
+ * @return integer
+ */
+ public function getDebugLevel()
+ {
+ return $this->do_debug;
+ }
+
+ /**
+ * Set SMTP timeout.
+ * @param integer $timeout
+ */
+ public function setTimeout($timeout = 0)
+ {
+ $this->Timeout = $timeout;
+ }
+
+ /**
+ * Get SMTP timeout.
+ * @return integer
+ */
+ public function getTimeout()
+ {
+ return $this->Timeout;
+ }
+}
diff --git a/include/util.php b/include/util.php
index 1aceb86..1e158f9 100644
--- a/include/util.php
+++ b/include/util.php
@@ -1,127 +1,127 @@
-"ene", "feb", "mar", "abr", "may", "jun", "jul", "ago", "sep", "oct", "nov", "dic");
- $fechaTxt = trim($fechaTxt);
- if(substr($fechaTxt,2,1) == "/" && substr($fechaTxt,5,1) == "/"){// dd/mm/aaaa
- $fechaArr = explode("/", $fechaTxt);
- return $meses[intval($fechaArr[1])].", ".$fechaArr[2];
- }
- if(substr($fechaTxt,4,1) == "-" && substr($fechaTxt,7,1) == "-"){// aaaa-mm-dd
- $fechaArr = explode("-", $fechaTxt);
- return $meses[intval($fechaArr[1])].", ".$fechaArr[0];
- }
- return "";
-}
-
-
-function fechaMes($fechaTxt){
- $fechaTxt = trim($fechaTxt);
- if(substr($fechaTxt,2,1) == "/" && substr($fechaTxt,5,1) == "/"){// dd/mm/aaaa
- $fechaArr = explode("/", $fechaTxt);
- return intval(mesNombre($fechaArr[1])." ".$fechaArr[2]);
- }
- if(substr($fechaTxt,4,1) == "-" && substr($fechaTxt,7,1) == "-"){// aaaa-mm-dd
- $fechaArr = explode("-", $fechaTxt);
- return intval(mesNombre($fechaArr[2])." ".$fechaArr[1]);
- }
- return "";
-}
-
-function mesNombre($num){
- $meses=array(1=>"enero", "febrero", "marzo", "abril", "mayo", "junio", "julio", "agosto", "septiembre", "octubre", "noviembre", "diciembre");
- return $meses[intval($num)];
-}
-
-function diaNombre($num){
- $dias=array("domingo", "lunes", "martes", "mi茅rcoles", "jueves", "viernes", "s谩bado");
- return $dias[intval($num)];
-}
-
-function horaMin($arr, $campo = "Horario_hora"){
- $min = "";
- foreach($arr as $horario){
- if($min == "" || date('H:i', strtotime($horario[$campo])) < date('H:i', strtotime($min))){
- $min = $horario[$campo];
- }
- }
- return date('H:i', strtotime($min));
-}
-
-function horaMax($arr, $campo = "Horario_hora_final"){
- $max = "";
- foreach($arr as $horario){
- if($max == "" || date('H:i', strtotime($horario[$campo])) > date('H:i', strtotime($max))){
- $max = $horario[$campo];
- }
- }
- return date('H:i', strtotime($max));
-}
-function duracionMinutos($fechahora_i, $fechahora_f){
- return round((strtotime($fechahora_f) - strtotime($fechahora_i)) / 60,2);
-}
-
-function validaPassword($pass){
- $expr = '/^\S*(?=\S{5,})(?=\S*[a-zA-Z])(?=\S*[\d])(?=\S*[\W])\S*$/';
- return preg_match($expr, $pass);
+"ene", "feb", "mar", "abr", "may", "jun", "jul", "ago", "sep", "oct", "nov", "dic");
+ $fechaTxt = trim($fechaTxt);
+ if(substr($fechaTxt,2,1) == "/" && substr($fechaTxt,5,1) == "/"){// dd/mm/aaaa
+ $fechaArr = explode("/", $fechaTxt);
+ return $meses[intval($fechaArr[1])].", ".$fechaArr[2];
+ }
+ if(substr($fechaTxt,4,1) == "-" && substr($fechaTxt,7,1) == "-"){// aaaa-mm-dd
+ $fechaArr = explode("-", $fechaTxt);
+ return $meses[intval($fechaArr[1])].", ".$fechaArr[0];
+ }
+ return "";
+}
+
+
+function fechaMes($fechaTxt){
+ $fechaTxt = trim($fechaTxt);
+ if(substr($fechaTxt,2,1) == "/" && substr($fechaTxt,5,1) == "/"){// dd/mm/aaaa
+ $fechaArr = explode("/", $fechaTxt);
+ return intval(mesNombre($fechaArr[1])." ".$fechaArr[2]);
+ }
+ if(substr($fechaTxt,4,1) == "-" && substr($fechaTxt,7,1) == "-"){// aaaa-mm-dd
+ $fechaArr = explode("-", $fechaTxt);
+ return intval(mesNombre($fechaArr[2])." ".$fechaArr[1]);
+ }
+ return "";
+}
+
+function mesNombre($num){
+ $meses=array(1=>"enero", "febrero", "marzo", "abril", "mayo", "junio", "julio", "agosto", "septiembre", "octubre", "noviembre", "diciembre");
+ return $meses[intval($num)];
+}
+
+function diaNombre($num){
+ $dias=array("domingo", "lunes", "martes", "mi茅rcoles", "jueves", "viernes", "s谩bado");
+ return $dias[intval($num)];
+}
+
+function horaMin($arr, $campo = "Horario_hora"){
+ $min = "";
+ foreach($arr as $horario){
+ if($min == "" || date('H:i', strtotime($horario[$campo])) < date('H:i', strtotime($min))){
+ $min = $horario[$campo];
+ }
+ }
+ return date('H:i', strtotime($min));
+}
+
+function horaMax($arr, $campo = "Horario_hora_final"){
+ $max = "";
+ foreach($arr as $horario){
+ if($max == "" || date('H:i', strtotime($horario[$campo])) > date('H:i', strtotime($max))){
+ $max = $horario[$campo];
+ }
+ }
+ return date('H:i', strtotime($max));
+}
+function duracionMinutos($fechahora_i, $fechahora_f){
+ return round((strtotime($fechahora_f) - strtotime($fechahora_i)) / 60,2);
+}
+
+function validaPassword($pass){
+ $expr = '/^\S*(?=\S{5,})(?=\S*[a-zA-Z])(?=\S*[\d])(?=\S*[\W])\S*$/';
+ return preg_match($expr, $pass);
}
\ No newline at end of file
diff --git a/js/auditor铆a.js b/js/auditor铆a.js
index d8a5a83..b8143ca 100644
--- a/js/auditor铆a.js
+++ b/js/auditor铆a.js
@@ -1,352 +1,352 @@
-import { createApp, reactive } from 'https://unpkg.com/petite-vue?module';
-$('div.modal#cargando').modal({
- backdrop: 'static',
- keyboard: false,
- show: false,
-});
-const store = reactive({
- loading: false,
- perido: null,
- current: {
- comentario: '',
- clase_vista: null,
- empty: '',
- page: 1,
- maxPages: 10,
- perPage: 10,
- modal_state: "Cargando datos...",
- justificada: null,
- fechas_clicked: false,
- observaciones: false,
- },
- facultades: {
- data: [],
- async fetch() {
- this.data = [];
- const res = await fetch('action/action_facultad.php');
- this.data = await res.json();
- },
- },
- filters: {
- facultad_id: null,
- fecha: null,
- fecha_inicio: null,
- fecha_fin: null,
- profesor: null,
- periodo_id: null,
- bloque_horario: null,
- estados: [],
- switchFecha: false,
- async switchFechas() {
- const periodo = await fetch('action/periodo_datos.php');
- const periodo_data = await periodo.json();
- if (!store.filters.switchFecha) {
- $('div.modal#cargando').modal('show');
- await store.registros.fetch();
- $('div.modal#cargando').modal('hide');
- }
- $(function () {
- store.filters.fecha_inicio = store.filters.fecha_fin = store.filters.fecha = null;
- $("#fecha, #fecha_inicio, #fecha_fin").datepicker({
- minDate: new Date(`${periodo_data.periodo_fecha_inicio}:00:00:00`),
- maxDate: new Date(`${periodo_data.fecha_final}:00:00:00`),
- dateFormat: "yy-mm-dd",
- showAnim: "slide",
- beforeShowDay: (date) => [(date.getDay() != 0), ""]
- });
- const fecha = $("#fecha"), inicio = $("#fecha_inicio"), fin = $("#fecha_fin");
- fecha.datepicker("setDate", new Date(`${periodo_data.fecha_final}:00:00:00`));
- inicio.on("change", function () {
- store.current.fechas_clicked = false;
- store.filters.fecha_inicio = inicio.val();
- fin.datepicker("option", "minDate", inicio.val());
- });
- fin.on("change", function () {
- store.current.fechas_clicked = false;
- store.filters.fecha_fin = fin.val();
- inicio.datepicker("option", "maxDate", fin.val());
- });
- fecha.on("change", async function () {
- store.filters.fecha = fecha.val();
- $('div.modal#cargando').modal('show');
- await store.registros.fetch(store.filters.fecha);
- $('div.modal#cargando').modal('hide');
- });
- });
- },
- async fetchByDate() {
- store.current.fechas_clicked = true;
- $('div.modal#cargando').modal('show');
- await store.registros.fetch(undefined, store.filters.fecha_inicio, store.filters.fecha_fin);
- store.current.page = 1;
- $('div.modal#cargando').modal('hide');
- }
- },
- estados: {
- data: [],
- async fetch() {
- this.data = [];
- const res = await fetch('action/action_estado_supervisor.php');
- this.data = await res.json();
- },
- getEstado(id) {
- return this.data.find((estado) => estado.estado_supervisor_id === id) ?? {
- estado_color: 'dark',
- estado_icon: 'ing-cancelar',
- nombre: 'Sin registro',
- estado_supervisor_id: -1,
- };
- },
- printEstados() {
- if (store.filters.estados.length > 0)
- document.querySelector('#estados').innerHTML = store.filters.estados.map((estado) => `
- ${store.estados.getEstado(estado).nombre}
- `).join('');
- else
- document.querySelector('#estados').innerHTML = `Todos los registros`;
- }
- },
- bloques_horario: {
- data: [],
- async fetch() {
- this.data = [];
- const res = await fetch('action/action_grupo_horario.php');
- this.data = await res.json();
- if (this.data.every((bloque) => !bloque.selected))
- this.data[0].selected = true;
- },
- },
- toggle(arr, element) {
- const newArray = arr.includes(element) ? arr.filter((item) => item !== element) : [...arr, element];
- // if all are selected, then unselect all
- if (newArray.length === (this.estados.data.length + 1)) {
- setTimeout(() => {
- document.querySelectorAll('#dlAsistencia>ul>li.selected').forEach(element => element.classList.remove('selected'));
- }, 100);
- return [];
- }
- return newArray;
- },
- async justificar() {
- if (!store.current.justificada)
- return;
- store.current.justificada.registro_justificada = true;
- let data;
- try {
- const res = await fetch('action/justificar.php', {
- method: 'POST',
- headers: {
- 'Content-Type': 'application/json'
- },
- body: JSON.stringify(store.current.justificada)
- });
- data = await res.json();
- }
- catch (error) {
- alert('Error al justificar');
- store.current.justificada = store.current.clone_justificada;
- }
- finally {
- delete store.current.clone_justificada;
- }
- store.current.justificada.justificador_nombre = data.justificador_nombre;
- store.current.justificada.justificador_clave = data.justificador_clave;
- store.current.justificada.justificador_facultad = data.justificador_facultad;
- store.current.justificada.justificador_rol = data.justificador_rol;
- store.current.justificada.registro_fecha_justificacion = data.registro_fecha_justificacion;
- },
- async justificarBloque(fecha, bloques, justificacion) {
- if (bloques.length === 0) {
- alert('No se ha seleccionado ning煤n bloque');
- return;
- }
- if (!justificacion) {
- alert('No se ha ingresado ninguna observaci贸n');
- return;
- }
- try {
- const res = await fetch('action/action_justificar.php', {
- method: 'PUT',
- headers: {
- 'Content-Type': 'application/json'
- },
- body: JSON.stringify({
- fecha,
- bloques,
- justificacion,
- })
- });
- const resData = await res.json();
- if (resData.status === 'success') {
- alert('Se ha justificado el bloque');
- store.current.modal_state = 'Cargando datos...';
- $('div.modal#cargando').modal('show');
- await store.registros.fetch();
- $('div.modal#cargando').modal('hide');
- }
- else {
- alert('No se ha podido justificar el bloque');
- }
- }
- catch (error) {
- alert('Error al justificar');
- }
- },
- registros: {
- data: [],
- async fetch(fecha, fecha_inicio, fecha_fin) {
- // if (!store.filters.facultad_id || !store.filters.periodo_id) return
- this.loading = true;
- this.data = [];
- const params = {};
- if (fecha)
- params['fecha'] = fecha;
- if (fecha_inicio)
- params['fecha_inicio'] = fecha_inicio;
- if (fecha_fin)
- params['fecha_fin'] = fecha_fin;
- params['periodo_id'] = store.filters.todos_los_periodos ? 0 : store.periodo.periodo_id;
- console.log(store.periodo);
- const paramsUrl = new URLSearchParams(params).toString();
- try {
- const res = await fetch(`action/action_auditoria.php?${paramsUrl}`, {
- method: 'GET',
- });
- this.data = await res.json();
- }
- catch (error) {
- alert('Error al cargar los datos');
- }
- this.loading = false;
- store.current.page = 1;
- },
- invertir() {
- this.data = this.data.reverse();
- },
- mostrarComentario(registro_id) {
- const registro = this.data.find((registro) => registro.registro_id === registro_id);
- store.current.comentario = registro.comentario;
- $('#ver-comentario').modal('show');
- },
- get relevant() {
- /*
- facultad_id: null,
- fecha: null,
- fecha_inicio: null,
- fecha_fin: null,
- profesor: null,
- asistencia: null,
- estado_id: null,
- if one of the filters is null, then it is not relevant
-
- */
- const filters = Object.keys(store.filters).filter((filtro) => store.filters[filtro] !== null || store.filters[filtro]?.length > 0);
- return this.data.filter((registro) => {
- return filters.every((filtro) => {
- switch (filtro) {
- case 'fecha':
- return registro.registro_fecha_ideal === store.filters[filtro];
- case 'fecha_inicio':
- return registro.registro_fecha_ideal >= store.filters[filtro];
- case 'fecha_fin':
- return registro.registro_fecha_ideal <= store.filters[filtro];
- case 'profesor':
- const textoFiltro = store.filters[filtro].toLowerCase();
- if (/^\([^)]+\)\s[\s\S]+$/.test(textoFiltro)) {
- const clave = registro.profesor_clave.toLowerCase();
- const filtroClave = textoFiltro.match(/\((.*?)\)/)?.[1];
- // console.log(clave, filtroClave);
- return clave.includes(filtroClave);
- }
- else {
- const nombre = registro.profesor_nombre.toLowerCase();
- return nombre.includes(textoFiltro);
- }
- case 'facultad_id':
- return registro.facultad_id === store.filters[filtro];
- case 'estados':
- if (store.filters[filtro].length === 0)
- return true;
- else if (store.filters[filtro].includes(-1) && registro.estado_supervisor_id === null)
- return true;
- return store.filters[filtro].includes(registro.estado_supervisor_id);
- case 'bloque_horario':
- const bloque = store.bloques_horario.data.find((bloque) => bloque.id === store.filters[filtro]);
- return registro.horario_hora < bloque.hora_fin && registro.horario_fin > bloque.hora_inicio;
- default: return true;
- }
- });
- });
- },
- async descargar() {
- store.current.modal_state = 'Generando reporte en Excel...';
- $('div.modal#cargando').modal('show');
- this.loading = true;
- if (this.relevant.length === 0)
- return;
- try {
- const res = await fetch('export/supervisor_excel.php', {
- method: 'POST',
- headers: {
- 'Content-Type': 'application/json'
- },
- body: JSON.stringify(this.relevant)
- });
- const blob = await res.blob();
- window.saveAs(blob, `auditoria_${new Date().toISOString().slice(0, 10)}.xlsx`);
- }
- catch (error) {
- if (error.response && error.response.status === 413) {
- alert('Your request is too large! Please reduce the data size and try again.');
- }
- else {
- alert('An error occurred: ' + error.message);
- }
- }
- finally {
- $('#cargando').modal('hide');
- this.loading = false;
- }
- },
- loading: false,
- get pages() {
- return Math.ceil(this.relevant.length / store.current.perPage);
- }
- },
-});
-createApp({
- store,
- messages: [],
- get clase_vista() {
- return store.current.clase_vista;
- },
- set_justificar(horario_id, profesor_id, registro_fecha_ideal) {
- store.current.justificada = store.registros.relevant.find((registro) => registro.horario_id === horario_id && registro.profesor_id === profesor_id && registro.registro_fecha_ideal === registro_fecha_ideal);
- store.current.clone_justificada = JSON.parse(JSON.stringify(store.current.justificada));
- store.current.observaciones = false;
- },
- cancelar_justificacion() {
- Object.assign(store.current.justificada, store.current.clone_justificada);
- delete store.current.clone_justificada;
- },
- profesores: [],
- async mounted() {
- $('div.modal#cargando').modal('show');
- try {
- store.periodo = await fetch('action/periodo_datos.php').then(res => res.json());
- // await store.registros.fetch()
- await store.facultades.fetch();
- await store.estados.fetch();
- await store.bloques_horario.fetch();
- await store.filters.switchFechas();
- this.profesores = await (await fetch('action/action_profesor.php')).json();
- this.messages.push({ title: 'Datos cargados', text: 'Los datos se han cargado correctamente', type: 'success', timestamp: new Date() });
- }
- catch (error) {
- this.messages.push({ title: 'Error al cargar datos', text: 'No se pudieron cargar los datos', type: 'danger', timestamp: new Date() });
- }
- finally {
- $('div.modal#cargando').modal('hide');
- }
- }
-}).mount('#app');
+import { createApp, reactive } from 'https://unpkg.com/petite-vue?module';
+$('div.modal#cargando').modal({
+ backdrop: 'static',
+ keyboard: false,
+ show: false,
+});
+const store = reactive({
+ loading: false,
+ perido: null,
+ current: {
+ comentario: '',
+ clase_vista: null,
+ empty: '',
+ page: 1,
+ maxPages: 10,
+ perPage: 10,
+ modal_state: "Cargando datos...",
+ justificada: null,
+ fechas_clicked: false,
+ observaciones: false,
+ },
+ facultades: {
+ data: [],
+ async fetch() {
+ this.data = [];
+ const res = await fetch('action/action_facultad.php');
+ this.data = await res.json();
+ },
+ },
+ filters: {
+ facultad_id: null,
+ fecha: null,
+ fecha_inicio: null,
+ fecha_fin: null,
+ profesor: null,
+ periodo_id: null,
+ bloque_horario: null,
+ estados: [],
+ switchFecha: false,
+ async switchFechas() {
+ const periodo = await fetch('action/periodo_datos.php');
+ const periodo_data = await periodo.json();
+ if (!store.filters.switchFecha) {
+ $('div.modal#cargando').modal('show');
+ await store.registros.fetch();
+ $('div.modal#cargando').modal('hide');
+ }
+ $(function () {
+ store.filters.fecha_inicio = store.filters.fecha_fin = store.filters.fecha = null;
+ $("#fecha, #fecha_inicio, #fecha_fin").datepicker({
+ minDate: new Date(`${periodo_data.periodo_fecha_inicio}:00:00:00`),
+ maxDate: new Date(`${periodo_data.fecha_final}:00:00:00`),
+ dateFormat: "yy-mm-dd",
+ showAnim: "slide",
+ beforeShowDay: (date) => [(date.getDay() != 0), ""]
+ });
+ const fecha = $("#fecha"), inicio = $("#fecha_inicio"), fin = $("#fecha_fin");
+ fecha.datepicker("setDate", new Date(`${periodo_data.fecha_final}:00:00:00`));
+ inicio.on("change", function () {
+ store.current.fechas_clicked = false;
+ store.filters.fecha_inicio = inicio.val();
+ fin.datepicker("option", "minDate", inicio.val());
+ });
+ fin.on("change", function () {
+ store.current.fechas_clicked = false;
+ store.filters.fecha_fin = fin.val();
+ inicio.datepicker("option", "maxDate", fin.val());
+ });
+ fecha.on("change", async function () {
+ store.filters.fecha = fecha.val();
+ $('div.modal#cargando').modal('show');
+ await store.registros.fetch(store.filters.fecha);
+ $('div.modal#cargando').modal('hide');
+ });
+ });
+ },
+ async fetchByDate() {
+ store.current.fechas_clicked = true;
+ $('div.modal#cargando').modal('show');
+ await store.registros.fetch(undefined, store.filters.fecha_inicio, store.filters.fecha_fin);
+ store.current.page = 1;
+ $('div.modal#cargando').modal('hide');
+ }
+ },
+ estados: {
+ data: [],
+ async fetch() {
+ this.data = [];
+ const res = await fetch('action/action_estado_supervisor.php');
+ this.data = await res.json();
+ },
+ getEstado(id) {
+ return this.data.find((estado) => estado.estado_supervisor_id === id) ?? {
+ estado_color: 'dark',
+ estado_icon: 'ing-cancelar',
+ nombre: 'Sin registro',
+ estado_supervisor_id: -1,
+ };
+ },
+ printEstados() {
+ if (store.filters.estados.length > 0)
+ document.querySelector('#estados').innerHTML = store.filters.estados.map((estado) => `
+ ${store.estados.getEstado(estado).nombre}
+ `).join('');
+ else
+ document.querySelector('#estados').innerHTML = `Todos los registros`;
+ }
+ },
+ bloques_horario: {
+ data: [],
+ async fetch() {
+ this.data = [];
+ const res = await fetch('action/action_grupo_horario.php');
+ this.data = await res.json();
+ if (this.data.every((bloque) => !bloque.selected))
+ this.data[0].selected = true;
+ },
+ },
+ toggle(arr, element) {
+ const newArray = arr.includes(element) ? arr.filter((item) => item !== element) : [...arr, element];
+ // if all are selected, then unselect all
+ if (newArray.length === (this.estados.data.length + 1)) {
+ setTimeout(() => {
+ document.querySelectorAll('#dlAsistencia>ul>li.selected').forEach(element => element.classList.remove('selected'));
+ }, 100);
+ return [];
+ }
+ return newArray;
+ },
+ async justificar() {
+ if (!store.current.justificada)
+ return;
+ store.current.justificada.registro_justificada = true;
+ let data;
+ try {
+ const res = await fetch('action/justificar.php', {
+ method: 'POST',
+ headers: {
+ 'Content-Type': 'application/json'
+ },
+ body: JSON.stringify(store.current.justificada)
+ });
+ data = await res.json();
+ }
+ catch (error) {
+ alert('Error al justificar');
+ store.current.justificada = store.current.clone_justificada;
+ }
+ finally {
+ delete store.current.clone_justificada;
+ }
+ store.current.justificada.justificador_nombre = data.justificador_nombre;
+ store.current.justificada.justificador_clave = data.justificador_clave;
+ store.current.justificada.justificador_facultad = data.justificador_facultad;
+ store.current.justificada.justificador_rol = data.justificador_rol;
+ store.current.justificada.registro_fecha_justificacion = data.registro_fecha_justificacion;
+ },
+ async justificarBloque(fecha, bloques, justificacion) {
+ if (bloques.length === 0) {
+ alert('No se ha seleccionado ning煤n bloque');
+ return;
+ }
+ if (!justificacion) {
+ alert('No se ha ingresado ninguna observaci贸n');
+ return;
+ }
+ try {
+ const res = await fetch('action/action_justificar.php', {
+ method: 'PUT',
+ headers: {
+ 'Content-Type': 'application/json'
+ },
+ body: JSON.stringify({
+ fecha,
+ bloques,
+ justificacion,
+ })
+ });
+ const resData = await res.json();
+ if (resData.status === 'success') {
+ alert('Se ha justificado el bloque');
+ store.current.modal_state = 'Cargando datos...';
+ $('div.modal#cargando').modal('show');
+ await store.registros.fetch();
+ $('div.modal#cargando').modal('hide');
+ }
+ else {
+ alert('No se ha podido justificar el bloque');
+ }
+ }
+ catch (error) {
+ alert('Error al justificar');
+ }
+ },
+ registros: {
+ data: [],
+ async fetch(fecha, fecha_inicio, fecha_fin) {
+ // if (!store.filters.facultad_id || !store.filters.periodo_id) return
+ this.loading = true;
+ this.data = [];
+ const params = {};
+ if (fecha)
+ params['fecha'] = fecha;
+ if (fecha_inicio)
+ params['fecha_inicio'] = fecha_inicio;
+ if (fecha_fin)
+ params['fecha_fin'] = fecha_fin;
+ params['periodo_id'] = store.filters.todos_los_periodos ? 0 : store.periodo.periodo_id;
+ console.log(store.periodo);
+ const paramsUrl = new URLSearchParams(params).toString();
+ try {
+ const res = await fetch(`action/action_auditoria.php?${paramsUrl}`, {
+ method: 'GET',
+ });
+ this.data = await res.json();
+ }
+ catch (error) {
+ alert('Error al cargar los datos');
+ }
+ this.loading = false;
+ store.current.page = 1;
+ },
+ invertir() {
+ this.data = this.data.reverse();
+ },
+ mostrarComentario(registro_id) {
+ const registro = this.data.find((registro) => registro.registro_id === registro_id);
+ store.current.comentario = registro.comentario;
+ $('#ver-comentario').modal('show');
+ },
+ get relevant() {
+ /*
+ facultad_id: null,
+ fecha: null,
+ fecha_inicio: null,
+ fecha_fin: null,
+ profesor: null,
+ asistencia: null,
+ estado_id: null,
+ if one of the filters is null, then it is not relevant
+
+ */
+ const filters = Object.keys(store.filters).filter((filtro) => store.filters[filtro] !== null || store.filters[filtro]?.length > 0);
+ return this.data.filter((registro) => {
+ return filters.every((filtro) => {
+ switch (filtro) {
+ case 'fecha':
+ return registro.registro_fecha_ideal === store.filters[filtro];
+ case 'fecha_inicio':
+ return registro.registro_fecha_ideal >= store.filters[filtro];
+ case 'fecha_fin':
+ return registro.registro_fecha_ideal <= store.filters[filtro];
+ case 'profesor':
+ const textoFiltro = store.filters[filtro].toLowerCase();
+ if (/^\([^)]+\)\s[\s\S]+$/.test(textoFiltro)) {
+ const clave = registro.profesor_clave.toLowerCase();
+ const filtroClave = textoFiltro.match(/\((.*?)\)/)?.[1];
+ // console.log(clave, filtroClave);
+ return clave.includes(filtroClave);
+ }
+ else {
+ const nombre = registro.profesor_nombre.toLowerCase();
+ return nombre.includes(textoFiltro);
+ }
+ case 'facultad_id':
+ return registro.facultad_id === store.filters[filtro];
+ case 'estados':
+ if (store.filters[filtro].length === 0)
+ return true;
+ else if (store.filters[filtro].includes(-1) && registro.estado_supervisor_id === null)
+ return true;
+ return store.filters[filtro].includes(registro.estado_supervisor_id);
+ case 'bloque_horario':
+ const bloque = store.bloques_horario.data.find((bloque) => bloque.id === store.filters[filtro]);
+ return registro.horario_hora < bloque.hora_fin && registro.horario_fin > bloque.hora_inicio;
+ default: return true;
+ }
+ });
+ });
+ },
+ async descargar() {
+ store.current.modal_state = 'Generando reporte en Excel...';
+ $('div.modal#cargando').modal('show');
+ this.loading = true;
+ if (this.relevant.length === 0)
+ return;
+ try {
+ const res = await fetch('export/supervisor_excel.php', {
+ method: 'POST',
+ headers: {
+ 'Content-Type': 'application/json'
+ },
+ body: JSON.stringify(this.relevant)
+ });
+ const blob = await res.blob();
+ window.saveAs(blob, `auditoria_${new Date().toISOString().slice(0, 10)}.xlsx`);
+ }
+ catch (error) {
+ if (error.response && error.response.status === 413) {
+ alert('Your request is too large! Please reduce the data size and try again.');
+ }
+ else {
+ alert('An error occurred: ' + error.message);
+ }
+ }
+ finally {
+ $('#cargando').modal('hide');
+ this.loading = false;
+ }
+ },
+ loading: false,
+ get pages() {
+ return Math.ceil(this.relevant.length / store.current.perPage);
+ }
+ },
+});
+createApp({
+ store,
+ messages: [],
+ get clase_vista() {
+ return store.current.clase_vista;
+ },
+ set_justificar(horario_id, profesor_id, registro_fecha_ideal) {
+ store.current.justificada = store.registros.relevant.find((registro) => registro.horario_id === horario_id && registro.profesor_id === profesor_id && registro.registro_fecha_ideal === registro_fecha_ideal);
+ store.current.clone_justificada = JSON.parse(JSON.stringify(store.current.justificada));
+ store.current.observaciones = false;
+ },
+ cancelar_justificacion() {
+ Object.assign(store.current.justificada, store.current.clone_justificada);
+ delete store.current.clone_justificada;
+ },
+ profesores: [],
+ async mounted() {
+ $('div.modal#cargando').modal('show');
+ try {
+ store.periodo = await fetch('action/periodo_datos.php').then(res => res.json());
+ // await store.registros.fetch()
+ await store.facultades.fetch();
+ await store.estados.fetch();
+ await store.bloques_horario.fetch();
+ await store.filters.switchFechas();
+ this.profesores = await (await fetch('action/action_profesor.php')).json();
+ this.messages.push({ title: 'Datos cargados', text: 'Los datos se han cargado correctamente', type: 'success', timestamp: new Date() });
+ }
+ catch (error) {
+ this.messages.push({ title: 'Error al cargar datos', text: 'No se pudieron cargar los datos', type: 'danger', timestamp: new Date() });
+ }
+ finally {
+ $('div.modal#cargando').modal('hide');
+ }
+ }
+}).mount('#app');
diff --git a/js/avisos.js b/js/avisos.js
index 4713b43..5d83c3c 100644
--- a/js/avisos.js
+++ b/js/avisos.js
@@ -1,148 +1,148 @@
-import { createApp, reactive } from 'https://unpkg.com/petite-vue?module';
-const new_aviso = reactive({
- titulo: '',
- descripcion: '',
- fechaInicio: '',
- fechaFin: '',
- profesores: [],
- carreras: [],
- reset() {
- this.titulo = '';
- this.descripcion = '';
- this.fechaInicio = '';
- this.fechaFin = '';
- this.profesores = [];
- this.carreras = [];
- },
- get isValid() {
- return this.titulo !== '' && this.descripcion !== '' && this.fechaInicio !== '' && this.fechaFin !== '' && (this.profesores.length > 0 || this.carreras.length > 0) && this.facultad_id !== null;
- },
-});
-// define datepicker method
-const app = createApp({
- new_aviso,
- profesores: [],
- carreras: [],
- avisos: [],
- profesor: null,
- formatProfesor(profesor) {
- return `(${profesor.profesor_clave}) ${profesor.profesor_nombre}`;
- },
- addProfesor() {
- const profesorObj = this.profesores.find((profesor) => this.profesor === this.formatProfesor(profesor));
- if (profesorObj) {
- this.new_aviso.profesores.push(profesorObj);
- this.profesor = null;
- }
- },
- aviso_shown: null,
- // int?
- aviso_suspendido: null,
- suspenderAviso() {
- if (this.aviso_suspendido) {
- const aviso = this.avisos.find((aviso) => aviso.aviso_id === this.aviso_suspendido);
- if (aviso) {
- this.deleteAviso(aviso);
- }
- }
- },
- get relevant_profesores() {
- // not in array new_aviso.profesores
- const relevant = this.profesores.filter((profesor) => !this.new_aviso.profesores.map((profesor) => profesor.profesor_id).includes(profesor.profesor_id));
- // console.log('profesores:', this.profesores.map((profesor: Profesor) => profesor.profesor_nombre), 'relevant:', relevant.map((profesor: Profesor) => profesor.profesor_nombre), 'new_aviso:', this.new_aviso.profesores.map((profesor: Profesor) => profesor.profesor_nombre))
- return relevant;
- },
- get relevant_carreras() {
- // not in array new_aviso.carreras
- return this.carreras.filter((carrera) => !this.new_aviso.carreras.includes(carrera));
- },
- createAviso() {
- const data = {
- aviso_titulo: this.new_aviso.titulo,
- aviso_texto: this.new_aviso.descripcion,
- aviso_fecha_inicial: this.new_aviso.fechaInicio,
- aviso_fecha_final: this.new_aviso.fechaFin,
- profesores: this.new_aviso.profesores.map((profesor) => profesor.profesor_id),
- carreras: this.new_aviso.carreras.map((carrera) => carrera.carrera_id),
- };
- fetch('/action/avisos.php', {
- method: 'POST',
- body: JSON.stringify(data)
- }).then(res => res.json()).then(res => {
- if (res.success) {
- // hydrate with carreras and profesores
- this.avisos.push({
- ...data,
- carreras: this.carreras.filter((carrera) => data.carreras.includes(carrera.carrera_id)),
- profesores: this.profesores.filter((profesor) => data.profesores.includes(profesor.profesor_id)),
- aviso_estado: true,
- aviso_id: res.aviso_id,
- });
- this.new_aviso.reset();
- }
- else {
- alert(res.error);
- console.log(res.errors);
- }
- });
- },
- deleteAviso(aviso) {
- fetch(`/action/avisos.php`, {
- method: 'DELETE',
- body: JSON.stringify({ aviso_id: aviso.aviso_id })
- }).then(res => res.json()).then(res => {
- if (res.success) {
- this.avisos = this.avisos.filter((aviso) => aviso.aviso_id !== this.aviso_suspendido);
- this.aviso_suspendido = null;
- }
- else {
- alert(res.error);
- console.log(res.errors);
- }
- });
- },
- updateAviso() {
- fetch(`/action/avisos.php`, {
- method: 'PUT',
- body: JSON.stringify({
- aviso_id: this.aviso_shown.aviso_id,
- aviso_fecha_final: this.aviso_shown.aviso_fecha_final,
- })
- }).then(res => res.json()).then(res => {
- if (res.success) {
- }
- else {
- alert(res.error);
- console.log(res.errors);
- }
- });
- },
- async initializeDatepickers($el) {
- const periodo = await fetch('action/periodo_datos.php');
- const periodo_data = await periodo.json();
- $('.date-picker').datepicker({
- dateFormat: 'yy-mm-dd',
- maxDate: periodo_data.periodo_fecha_fin,
- minDate: 0,
- });
- $($el).on('change', () => {
- this.aviso_shown.aviso_fecha_final = $($el).val();
- });
- },
- async mounted() {
- this.avisos = await fetch("/action/avisos.php").then(res => res.json());
- this.profesores = await fetch('/action/action_profesor.php').then(res => res.json());
- this.carreras = await fetch('/action/action_carreras.php').then(res => res.json());
- await this.initializeDatepickers();
- const fechaInicio = $('#fechaInicio.date-picker');
- const fechaFin = $('#fechaFin.date-picker');
- fechaInicio.on("change", function () {
- new_aviso.fechaInicio = fechaInicio.val();
- fechaFin.datepicker("option", "minDate", fechaInicio.val());
- });
- fechaFin.on("change", function () {
- new_aviso.fechaFin = fechaFin.val();
- fechaInicio.datepicker("option", "maxDate", fechaFin.val());
- });
- }
-}).mount('#app');
+import { createApp, reactive } from 'https://unpkg.com/petite-vue?module';
+const new_aviso = reactive({
+ titulo: '',
+ descripcion: '',
+ fechaInicio: '',
+ fechaFin: '',
+ profesores: [],
+ carreras: [],
+ reset() {
+ this.titulo = '';
+ this.descripcion = '';
+ this.fechaInicio = '';
+ this.fechaFin = '';
+ this.profesores = [];
+ this.carreras = [];
+ },
+ get isValid() {
+ return this.titulo !== '' && this.descripcion !== '' && this.fechaInicio !== '' && this.fechaFin !== '' && (this.profesores.length > 0 || this.carreras.length > 0) && this.facultad_id !== null;
+ },
+});
+// define datepicker method
+const app = createApp({
+ new_aviso,
+ profesores: [],
+ carreras: [],
+ avisos: [],
+ profesor: null,
+ formatProfesor(profesor) {
+ return `(${profesor.profesor_clave}) ${profesor.profesor_nombre}`;
+ },
+ addProfesor() {
+ const profesorObj = this.profesores.find((profesor) => this.profesor === this.formatProfesor(profesor));
+ if (profesorObj) {
+ this.new_aviso.profesores.push(profesorObj);
+ this.profesor = null;
+ }
+ },
+ aviso_shown: null,
+ // int?
+ aviso_suspendido: null,
+ suspenderAviso() {
+ if (this.aviso_suspendido) {
+ const aviso = this.avisos.find((aviso) => aviso.aviso_id === this.aviso_suspendido);
+ if (aviso) {
+ this.deleteAviso(aviso);
+ }
+ }
+ },
+ get relevant_profesores() {
+ // not in array new_aviso.profesores
+ const relevant = this.profesores.filter((profesor) => !this.new_aviso.profesores.map((profesor) => profesor.profesor_id).includes(profesor.profesor_id));
+ // console.log('profesores:', this.profesores.map((profesor: Profesor) => profesor.profesor_nombre), 'relevant:', relevant.map((profesor: Profesor) => profesor.profesor_nombre), 'new_aviso:', this.new_aviso.profesores.map((profesor: Profesor) => profesor.profesor_nombre))
+ return relevant;
+ },
+ get relevant_carreras() {
+ // not in array new_aviso.carreras
+ return this.carreras.filter((carrera) => !this.new_aviso.carreras.includes(carrera));
+ },
+ createAviso() {
+ const data = {
+ aviso_titulo: this.new_aviso.titulo,
+ aviso_texto: this.new_aviso.descripcion,
+ aviso_fecha_inicial: this.new_aviso.fechaInicio,
+ aviso_fecha_final: this.new_aviso.fechaFin,
+ profesores: this.new_aviso.profesores.map((profesor) => profesor.profesor_id),
+ carreras: this.new_aviso.carreras.map((carrera) => carrera.carrera_id),
+ };
+ fetch('/action/avisos.php', {
+ method: 'POST',
+ body: JSON.stringify(data)
+ }).then(res => res.json()).then(res => {
+ if (res.success) {
+ // hydrate with carreras and profesores
+ this.avisos.push({
+ ...data,
+ carreras: this.carreras.filter((carrera) => data.carreras.includes(carrera.carrera_id)),
+ profesores: this.profesores.filter((profesor) => data.profesores.includes(profesor.profesor_id)),
+ aviso_estado: true,
+ aviso_id: res.aviso_id,
+ });
+ this.new_aviso.reset();
+ }
+ else {
+ alert(res.error);
+ console.log(res.errors);
+ }
+ });
+ },
+ deleteAviso(aviso) {
+ fetch(`/action/avisos.php`, {
+ method: 'DELETE',
+ body: JSON.stringify({ aviso_id: aviso.aviso_id })
+ }).then(res => res.json()).then(res => {
+ if (res.success) {
+ this.avisos = this.avisos.filter((aviso) => aviso.aviso_id !== this.aviso_suspendido);
+ this.aviso_suspendido = null;
+ }
+ else {
+ alert(res.error);
+ console.log(res.errors);
+ }
+ });
+ },
+ updateAviso() {
+ fetch(`/action/avisos.php`, {
+ method: 'PUT',
+ body: JSON.stringify({
+ aviso_id: this.aviso_shown.aviso_id,
+ aviso_fecha_final: this.aviso_shown.aviso_fecha_final,
+ })
+ }).then(res => res.json()).then(res => {
+ if (res.success) {
+ }
+ else {
+ alert(res.error);
+ console.log(res.errors);
+ }
+ });
+ },
+ async initializeDatepickers($el) {
+ const periodo = await fetch('action/periodo_datos.php');
+ const periodo_data = await periodo.json();
+ $('.date-picker').datepicker({
+ dateFormat: 'yy-mm-dd',
+ maxDate: periodo_data.periodo_fecha_fin,
+ minDate: 0,
+ });
+ $($el).on('change', () => {
+ this.aviso_shown.aviso_fecha_final = $($el).val();
+ });
+ },
+ async mounted() {
+ this.avisos = await fetch("/action/avisos.php").then(res => res.json());
+ this.profesores = await fetch('/action/action_profesor.php').then(res => res.json());
+ this.carreras = await fetch('/action/action_carreras.php').then(res => res.json());
+ await this.initializeDatepickers();
+ const fechaInicio = $('#fechaInicio.date-picker');
+ const fechaFin = $('#fechaFin.date-picker');
+ fechaInicio.on("change", function () {
+ new_aviso.fechaInicio = fechaInicio.val();
+ fechaFin.datepicker("option", "minDate", fechaInicio.val());
+ });
+ fechaFin.on("change", function () {
+ new_aviso.fechaFin = fechaFin.val();
+ fechaInicio.datepicker("option", "maxDate", fechaFin.val());
+ });
+ }
+}).mount('#app');
diff --git a/js/bootstrap/bootstrap.bundle.min.js b/js/bootstrap/bootstrap.bundle.min.js
index 72a46cf..92f9c27 100644
--- a/js/bootstrap/bootstrap.bundle.min.js
+++ b/js/bootstrap/bootstrap.bundle.min.js
@@ -1,7 +1,7 @@
-/*!
- * Bootstrap v4.1.3 (https://getbootstrap.com/)
- * Copyright 2011-2018 The Bootstrap Authors (https://github.com/twbs/bootstrap/graphs/contributors)
- * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
- */
-!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?t(exports,require("jquery")):"function"==typeof define&&define.amd?define(["exports","jquery"],t):t(e.bootstrap={},e.jQuery)}(this,function(e,t){"use strict";function i(e,t){for(var n=0;nthis._items.length-1||e<0))if(this._isSliding)k(this._element).one(q.SLID,function(){return t.to(e)});else{if(n===e)return this.pause(),void this.cycle();var i=n=i.clientWidth&&n>=i.clientHeight}),u=0l[e]&&!i.escapeWithReference&&(n=Math.min(u[t],l[e]-("right"===e?u.width:u.height))),Ve({},t,n)}};return c.forEach(function(e){var t=-1!==["left","top"].indexOf(e)?"primary":"secondary";u=ze({},u,f[t](e))}),e.offsets.popper=u,e},priority:["left","right","top","bottom"],padding:5,boundariesElement:"scrollParent"},keepTogether:{order:400,enabled:!0,fn:function(e){var t=e.offsets,n=t.popper,i=t.reference,r=e.placement.split("-")[0],o=Math.floor,s=-1!==["top","bottom"].indexOf(r),a=s?"right":"bottom",l=s?"left":"top",c=s?"width":"height";return n[a]o(i[a])&&(e.offsets.popper[l]=o(i[a])),e}},arrow:{order:500,enabled:!0,fn:function(e,t){var n;if(!pt(e.instance.modifiers,"arrow","keepTogether"))return e;var i=t.element;if("string"==typeof i){if(!(i=e.instance.popper.querySelector(i)))return e}else if(!e.instance.popper.contains(i))return console.warn("WARNING: `arrow.element` must be child of its popper element!"),e;var r=e.placement.split("-")[0],o=e.offsets,s=o.popper,a=o.reference,l=-1!==["left","right"].indexOf(r),c=l?"height":"width",u=l?"Top":"Left",f=u.toLowerCase(),h=l?"left":"top",d=l?"bottom":"right",p=nt(i)[c];a[d]-ps[d]&&(e.offsets.popper[f]+=a[f]+p-s[d]),e.offsets.popper=Ge(e.offsets.popper);var m=a[f]+a[c]/2-p/2,g=Pe(e.instance.popper),_=parseFloat(g["margin"+u],10),v=parseFloat(g["border"+u+"Width"],10),y=m-e.offsets.popper[f]-_-v;return y=Math.max(Math.min(s[c]-p,y),0),e.arrowElement=i,e.offsets.arrow=(Ve(n={},f,Math.round(y)),Ve(n,h,""),n),e},element:"[x-arrow]"},flip:{order:600,enabled:!0,fn:function(p,m){if(at(p.instance.modifiers,"inner"))return p;if(p.flipped&&p.placement===p.originalPlacement)return p;var g=$e(p.instance.popper,p.instance.reference,m.padding,m.boundariesElement,p.positionFixed),_=p.placement.split("-")[0],v=it(_),y=p.placement.split("-")[1]||"",E=[];switch(m.behavior){case vt:E=[_,v];break;case yt:E=_t(_);break;case Et:E=_t(_,!0);break;default:E=m.behavior}return E.forEach(function(e,t){if(_!==e||E.length===t+1)return p;_=p.placement.split("-")[0],v=it(_);var n,i=p.offsets.popper,r=p.offsets.reference,o=Math.floor,s="left"===_&&o(i.right)>o(r.left)||"right"===_&&o(i.left)o(r.top)||"bottom"===_&&o(i.top)o(g.right),c=o(i.top)o(g.bottom),f="left"===_&&a||"right"===_&&l||"top"===_&&c||"bottom"===_&&u,h=-1!==["top","bottom"].indexOf(_),d=!!m.flipVariations&&(h&&"start"===y&&a||h&&"end"===y&&l||!h&&"start"===y&&c||!h&&"end"===y&&u);(s||f||d)&&(p.flipped=!0,(s||f)&&(_=E[t+1]),d&&(y="end"===(n=y)?"start":"start"===n?"end":n),p.placement=_+(y?"-"+y:""),p.offsets.popper=ze({},p.offsets.popper,rt(p.instance.popper,p.offsets.reference,p.placement)),p=st(p.instance.modifiers,p,"flip"))}),p},behavior:"flip",padding:5,boundariesElement:"viewport"},inner:{order:700,enabled:!1,fn:function(e){var t=e.placement,n=t.split("-")[0],i=e.offsets,r=i.popper,o=i.reference,s=-1!==["left","right"].indexOf(n),a=-1===["top","left"].indexOf(n);return r[s?"left":"top"]=o[n]-(a?r[s?"width":"height"]:0),e.placement=it(t),e.offsets.popper=Ge(r),e}},hide:{order:800,enabled:!0,fn:function(e){if(!pt(e.instance.modifiers,"hide","preventOverflow"))return e;var t=e.offsets.reference,n=ot(e.instance.modifiers,function(e){return"preventOverflow"===e.name}).boundaries;if(t.bottomn.right||t.top>n.bottom||t.rightdocument.documentElement.clientHeight;!this._isBodyOverflowing&&e&&(this._element.style.paddingLeft=this._scrollbarWidth+"px"),this._isBodyOverflowing&&!e&&(this._element.style.paddingRight=this._scrollbarWidth+"px")},e._resetAdjustments=function(){this._element.style.paddingLeft="",this._element.style.paddingRight=""},e._checkScrollbar=function(){var e=document.body.getBoundingClientRect();this._isBodyOverflowing=e.left+e.right
',trigger:"hover focus",title:"",delay:0,html:!(An={AUTO:"auto",TOP:"top",RIGHT:"right",BOTTOM:"bottom",LEFT:"left"}),selector:!(Dn={animation:"boolean",template:"string",title:"(string|element|function)",trigger:"string",delay:"(number|object)",html:"boolean",selector:"(string|boolean)",placement:"(string|function)",offset:"(number|string)",container:"(string|element|boolean)",fallbackPlacement:"(string|array)",boundary:"(string|element)"}),placement:"top",offset:0,container:!1,fallbackPlacement:"flip",boundary:"scrollParent"},Nn="out",kn={HIDE:"hide"+wn,HIDDEN:"hidden"+wn,SHOW:(On="show")+wn,SHOWN:"shown"+wn,INSERTED:"inserted"+wn,CLICK:"click"+wn,FOCUSIN:"focusin"+wn,FOCUSOUT:"focusout"+wn,MOUSEENTER:"mouseenter"+wn,MOUSELEAVE:"mouseleave"+wn},xn="fade",Pn="show",Ln=".tooltip-inner",jn=".arrow",Hn="hover",Mn="focus",Fn="click",Wn="manual",Rn=function(){function i(e,t){if("undefined"==typeof Ct)throw new TypeError("Bootstrap tooltips require Popper.js (https://popper.js.org)");this._isEnabled=!0,this._timeout=0,this._hoverState="",this._activeTrigger={},this._popper=null,this.element=e,this.config=this._getConfig(t),this.tip=null,this._setListeners()}var e=i.prototype;return e.enable=function(){this._isEnabled=!0},e.disable=function(){this._isEnabled=!1},e.toggleEnabled=function(){this._isEnabled=!this._isEnabled},e.toggle=function(e){if(this._isEnabled)if(e){var t=this.constructor.DATA_KEY,n=yn(e.currentTarget).data(t);n||(n=new this.constructor(e.currentTarget,this._getDelegateConfig()),yn(e.currentTarget).data(t,n)),n._activeTrigger.click=!n._activeTrigger.click,n._isWithActiveTrigger()?n._enter(null,n):n._leave(null,n)}else{if(yn(this.getTipElement()).hasClass(Pn))return void this._leave(null,this);this._enter(null,this)}},e.dispose=function(){clearTimeout(this._timeout),yn.removeData(this.element,this.constructor.DATA_KEY),yn(this.element).off(this.constructor.EVENT_KEY),yn(this.element).closest(".modal").off("hide.bs.modal"),this.tip&&yn(this.tip).remove(),this._isEnabled=null,this._timeout=null,this._hoverState=null,(this._activeTrigger=null)!==this._popper&&this._popper.destroy(),this._popper=null,this.element=null,this.config=null,this.tip=null},e.show=function(){var t=this;if("none"===yn(this.element).css("display"))throw new Error("Please use show on visible elements");var e=yn.Event(this.constructor.Event.SHOW);if(this.isWithContent()&&this._isEnabled){yn(this.element).trigger(e);var n=yn.contains(this.element.ownerDocument.documentElement,this.element);if(e.isDefaultPrevented()||!n)return;var i=this.getTipElement(),r=we.getUID(this.constructor.NAME);i.setAttribute("id",r),this.element.setAttribute("aria-describedby",r),this.setContent(),this.config.animation&&yn(i).addClass(xn);var o="function"==typeof this.config.placement?this.config.placement.call(this,i,this.element):this.config.placement,s=this._getAttachment(o);this.addAttachmentClass(s);var a=!1===this.config.container?document.body:yn(document).find(this.config.container);yn(i).data(this.constructor.DATA_KEY,this),yn.contains(this.element.ownerDocument.documentElement,this.tip)||yn(i).appendTo(a),yn(this.element).trigger(this.constructor.Event.INSERTED),this._popper=new Ct(this.element,i,{placement:s,modifiers:{offset:{offset:this.config.offset},flip:{behavior:this.config.fallbackPlacement},arrow:{element:jn},preventOverflow:{boundariesElement:this.config.boundary}},onCreate:function(e){e.originalPlacement!==e.placement&&t._handlePopperPlacementChange(e)},onUpdate:function(e){t._handlePopperPlacementChange(e)}}),yn(i).addClass(Pn),"ontouchstart"in document.documentElement&&yn(document.body).children().on("mouseover",null,yn.noop);var l=function(){t.config.animation&&t._fixTransition();var e=t._hoverState;t._hoverState=null,yn(t.element).trigger(t.constructor.Event.SHOWN),e===Nn&&t._leave(null,t)};if(yn(this.tip).hasClass(xn)){var c=we.getTransitionDurationFromElement(this.tip);yn(this.tip).one(we.TRANSITION_END,l).emulateTransitionEnd(c)}else l()}},e.hide=function(e){var t=this,n=this.getTipElement(),i=yn.Event(this.constructor.Event.HIDE),r=function(){t._hoverState!==On&&n.parentNode&&n.parentNode.removeChild(n),t._cleanTipClass(),t.element.removeAttribute("aria-describedby"),yn(t.element).trigger(t.constructor.Event.HIDDEN),null!==t._popper&&t._popper.destroy(),e&&e()};if(yn(this.element).trigger(i),!i.isDefaultPrevented()){if(yn(n).removeClass(Pn),"ontouchstart"in document.documentElement&&yn(document.body).children().off("mouseover",null,yn.noop),this._activeTrigger[Fn]=!1,this._activeTrigger[Mn]=!1,this._activeTrigger[Hn]=!1,yn(this.tip).hasClass(xn)){var o=we.getTransitionDurationFromElement(n);yn(n).one(we.TRANSITION_END,r).emulateTransitionEnd(o)}else r();this._hoverState=""}},e.update=function(){null!==this._popper&&this._popper.scheduleUpdate()},e.isWithContent=function(){return Boolean(this.getTitle())},e.addAttachmentClass=function(e){yn(this.getTipElement()).addClass(Tn+"-"+e)},e.getTipElement=function(){return this.tip=this.tip||yn(this.config.template)[0],this.tip},e.setContent=function(){var e=this.getTipElement();this.setElementContent(yn(e.querySelectorAll(Ln)),this.getTitle()),yn(e).removeClass(xn+" "+Pn)},e.setElementContent=function(e,t){var n=this.config.html;"object"==typeof t&&(t.nodeType||t.jquery)?n?yn(t).parent().is(e)||e.empty().append(t):e.text(yn(t).text()):e[n?"html":"text"](t)},e.getTitle=function(){var e=this.element.getAttribute("data-original-title");return e||(e="function"==typeof this.config.title?this.config.title.call(this.element):this.config.title),e},e._getAttachment=function(e){return An[e.toUpperCase()]},e._setListeners=function(){var i=this;this.config.trigger.split(" ").forEach(function(e){if("click"===e)yn(i.element).on(i.constructor.Event.CLICK,i.config.selector,function(e){return i.toggle(e)});else if(e!==Wn){var t=e===Hn?i.constructor.Event.MOUSEENTER:i.constructor.Event.FOCUSIN,n=e===Hn?i.constructor.Event.MOUSELEAVE:i.constructor.Event.FOCUSOUT;yn(i.element).on(t,i.config.selector,function(e){return i._enter(e)}).on(n,i.config.selector,function(e){return i._leave(e)})}yn(i.element).closest(".modal").on("hide.bs.modal",function(){return i.hide()})}),this.config.selector?this.config=l({},this.config,{trigger:"manual",selector:""}):this._fixTitle()},e._fixTitle=function(){var e=typeof this.element.getAttribute("data-original-title");(this.element.getAttribute("title")||"string"!==e)&&(this.element.setAttribute("data-original-title",this.element.getAttribute("title")||""),this.element.setAttribute("title",""))},e._enter=function(e,t){var n=this.constructor.DATA_KEY;(t=t||yn(e.currentTarget).data(n))||(t=new this.constructor(e.currentTarget,this._getDelegateConfig()),yn(e.currentTarget).data(n,t)),e&&(t._activeTrigger["focusin"===e.type?Mn:Hn]=!0),yn(t.getTipElement()).hasClass(Pn)||t._hoverState===On?t._hoverState=On:(clearTimeout(t._timeout),t._hoverState=On,t.config.delay&&t.config.delay.show?t._timeout=setTimeout(function(){t._hoverState===On&&t.show()},t.config.delay.show):t.show())},e._leave=function(e,t){var n=this.constructor.DATA_KEY;(t=t||yn(e.currentTarget).data(n))||(t=new this.constructor(e.currentTarget,this._getDelegateConfig()),yn(e.currentTarget).data(n,t)),e&&(t._activeTrigger["focusout"===e.type?Mn:Hn]=!1),t._isWithActiveTrigger()||(clearTimeout(t._timeout),t._hoverState=Nn,t.config.delay&&t.config.delay.hide?t._timeout=setTimeout(function(){t._hoverState===Nn&&t.hide()},t.config.delay.hide):t.hide())},e._isWithActiveTrigger=function(){for(var e in this._activeTrigger)if(this._activeTrigger[e])return!0;return!1},e._getConfig=function(e){return"number"==typeof(e=l({},this.constructor.Default,yn(this.element).data(),"object"==typeof e&&e?e:{})).delay&&(e.delay={show:e.delay,hide:e.delay}),"number"==typeof e.title&&(e.title=e.title.toString()),"number"==typeof e.content&&(e.content=e.content.toString()),we.typeCheckConfig(En,e,this.constructor.DefaultType),e},e._getDelegateConfig=function(){var e={};if(this.config)for(var t in this.config)this.constructor.Default[t]!==this.config[t]&&(e[t]=this.config[t]);return e},e._cleanTipClass=function(){var e=yn(this.getTipElement()),t=e.attr("class").match(Sn);null!==t&&t.length&&e.removeClass(t.join(""))},e._handlePopperPlacementChange=function(e){var t=e.instance;this.tip=t.popper,this._cleanTipClass(),this.addAttachmentClass(this._getAttachment(e.placement))},e._fixTransition=function(){var e=this.getTipElement(),t=this.config.animation;null===e.getAttribute("x-placement")&&(yn(e).removeClass(xn),this.config.animation=!1,this.hide(),this.show(),this.config.animation=t)},i._jQueryInterface=function(n){return this.each(function(){var e=yn(this).data(bn),t="object"==typeof n&&n;if((e||!/dispose|hide/.test(n))&&(e||(e=new i(this,t),yn(this).data(bn,e)),"string"==typeof n)){if("undefined"==typeof e[n])throw new TypeError('No method named "'+n+'"');e[n]()}})},s(i,null,[{key:"VERSION",get:function(){return"4.1.3"}},{key:"Default",get:function(){return In}},{key:"NAME",get:function(){return En}},{key:"DATA_KEY",get:function(){return bn}},{key:"Event",get:function(){return kn}},{key:"EVENT_KEY",get:function(){return wn}},{key:"DefaultType",get:function(){return Dn}}]),i}(),yn.fn[En]=Rn._jQueryInterface,yn.fn[En].Constructor=Rn,yn.fn[En].noConflict=function(){return yn.fn[En]=Cn,Rn._jQueryInterface},Rn),Qi=(Bn="popover",Kn="."+(qn="bs.popover"),Qn=(Un=t).fn[Bn],Yn="bs-popover",Vn=new RegExp("(^|\\s)"+Yn+"\\S+","g"),zn=l({},Ki.Default,{placement:"right",trigger:"click",content:"",template:''}),Gn=l({},Ki.DefaultType,{content:"(string|element|function)"}),Jn="fade",Xn=".popover-header",$n=".popover-body",ei={HIDE:"hide"+Kn,HIDDEN:"hidden"+Kn,SHOW:(Zn="show")+Kn,SHOWN:"shown"+Kn,INSERTED:"inserted"+Kn,CLICK:"click"+Kn,FOCUSIN:"focusin"+Kn,FOCUSOUT:"focusout"+Kn,MOUSEENTER:"mouseenter"+Kn,MOUSELEAVE:"mouseleave"+Kn},ti=function(e){var t,n;function i(){return e.apply(this,arguments)||this}n=e,(t=i).prototype=Object.create(n.prototype),(t.prototype.constructor=t).__proto__=n;var r=i.prototype;return r.isWithContent=function(){return this.getTitle()||this._getContent()},r.addAttachmentClass=function(e){Un(this.getTipElement()).addClass(Yn+"-"+e)},r.getTipElement=function(){return this.tip=this.tip||Un(this.config.template)[0],this.tip},r.setContent=function(){var e=Un(this.getTipElement());this.setElementContent(e.find(Xn),this.getTitle());var t=this._getContent();"function"==typeof t&&(t=t.call(this.element)),this.setElementContent(e.find($n),t),e.removeClass(Jn+" "+Zn)},r._getContent=function(){return this.element.getAttribute("data-content")||this.config.content},r._cleanTipClass=function(){var e=Un(this.getTipElement()),t=e.attr("class").match(Vn);null!==t&&0=this._offsets[r]&&("undefined"==typeof this._offsets[r+1]||e li > .active",Fi='[data-toggle="tab"], [data-toggle="pill"], [data-toggle="list"]',Wi=".dropdown-toggle",Ri="> .dropdown-menu .active",Ui=function(){function i(e){this._element=e}var e=i.prototype;return e.show=function(){var n=this;if(!(this._element.parentNode&&this._element.parentNode.nodeType===Node.ELEMENT_NODE&&Ti(this._element).hasClass(Ni)||Ti(this._element).hasClass(ki))){var e,i,t=Ti(this._element).closest(ji)[0],r=we.getSelectorFromElement(this._element);if(t){var o="UL"===t.nodeName?Mi:Hi;i=(i=Ti.makeArray(Ti(t).find(o)))[i.length-1]}var s=Ti.Event(Ii.HIDE,{relatedTarget:this._element}),a=Ti.Event(Ii.SHOW,{relatedTarget:i});if(i&&Ti(i).trigger(s),Ti(this._element).trigger(a),!a.isDefaultPrevented()&&!s.isDefaultPrevented()){r&&(e=document.querySelector(r)),this._activate(this._element,t);var l=function(){var e=Ti.Event(Ii.HIDDEN,{relatedTarget:n._element}),t=Ti.Event(Ii.SHOWN,{relatedTarget:i});Ti(i).trigger(e),Ti(n._element).trigger(t)};e?this._activate(e,e.parentNode,l):l()}}},e.dispose=function(){Ti.removeData(this._element,Si),this._element=null},e._activate=function(e,t,n){var i=this,r=("UL"===t.nodeName?Ti(t).find(Mi):Ti(t).children(Hi))[0],o=n&&r&&Ti(r).hasClass(xi),s=function(){return i._transitionComplete(e,r,n)};if(r&&o){var a=we.getTransitionDurationFromElement(r);Ti(r).one(we.TRANSITION_END,s).emulateTransitionEnd(a)}else s()},e._transitionComplete=function(e,t,n){if(t){Ti(t).removeClass(Pi+" "+Ni);var i=Ti(t.parentNode).find(Ri)[0];i&&Ti(i).removeClass(Ni),"tab"===t.getAttribute("role")&&t.setAttribute("aria-selected",!1)}if(Ti(e).addClass(Ni),"tab"===e.getAttribute("role")&&e.setAttribute("aria-selected",!0),we.reflow(e),Ti(e).addClass(Pi),e.parentNode&&Ti(e.parentNode).hasClass(Oi)){var r=Ti(e).closest(Li)[0];if(r){var o=[].slice.call(r.querySelectorAll(Wi));Ti(o).addClass(Ni)}e.setAttribute("aria-expanded",!0)}n&&n()},i._jQueryInterface=function(n){return this.each(function(){var e=Ti(this),t=e.data(Si);if(t||(t=new i(this),e.data(Si,t)),"string"==typeof n){if("undefined"==typeof t[n])throw new TypeError('No method named "'+n+'"');t[n]()}})},s(i,null,[{key:"VERSION",get:function(){return"4.1.3"}}]),i}(),Ti(document).on(Ii.CLICK_DATA_API,Fi,function(e){e.preventDefault(),Ui._jQueryInterface.call(Ti(this),"show")}),Ti.fn.tab=Ui._jQueryInterface,Ti.fn.tab.Constructor=Ui,Ti.fn.tab.noConflict=function(){return Ti.fn.tab=Ai,Ui._jQueryInterface},Ui);!function(e){if("undefined"==typeof e)throw new TypeError("Bootstrap's JavaScript requires jQuery. jQuery must be included before Bootstrap's JavaScript.");var t=e.fn.jquery.split(" ")[0].split(".");if(t[0]<2&&t[1]<9||1===t[0]&&9===t[1]&&t[2]<1||4<=t[0])throw new Error("Bootstrap's JavaScript requires at least jQuery v1.9.1 but less than v4.0.0")}(t),e.Util=we,e.Alert=Ce,e.Button=Te,e.Carousel=Se,e.Collapse=De,e.Dropdown=Bi,e.Modal=qi,e.Popover=Qi,e.Scrollspy=Yi,e.Tab=Vi,e.Tooltip=Ki,Object.defineProperty(e,"__esModule",{value:!0})});
+/*!
+ * Bootstrap v4.1.3 (https://getbootstrap.com/)
+ * Copyright 2011-2018 The Bootstrap Authors (https://github.com/twbs/bootstrap/graphs/contributors)
+ * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
+ */
+!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?t(exports,require("jquery")):"function"==typeof define&&define.amd?define(["exports","jquery"],t):t(e.bootstrap={},e.jQuery)}(this,function(e,t){"use strict";function i(e,t){for(var n=0;nthis._items.length-1||e<0))if(this._isSliding)k(this._element).one(q.SLID,function(){return t.to(e)});else{if(n===e)return this.pause(),void this.cycle();var i=n=i.clientWidth&&n>=i.clientHeight}),u=0l[e]&&!i.escapeWithReference&&(n=Math.min(u[t],l[e]-("right"===e?u.width:u.height))),Ve({},t,n)}};return c.forEach(function(e){var t=-1!==["left","top"].indexOf(e)?"primary":"secondary";u=ze({},u,f[t](e))}),e.offsets.popper=u,e},priority:["left","right","top","bottom"],padding:5,boundariesElement:"scrollParent"},keepTogether:{order:400,enabled:!0,fn:function(e){var t=e.offsets,n=t.popper,i=t.reference,r=e.placement.split("-")[0],o=Math.floor,s=-1!==["top","bottom"].indexOf(r),a=s?"right":"bottom",l=s?"left":"top",c=s?"width":"height";return n[a]o(i[a])&&(e.offsets.popper[l]=o(i[a])),e}},arrow:{order:500,enabled:!0,fn:function(e,t){var n;if(!pt(e.instance.modifiers,"arrow","keepTogether"))return e;var i=t.element;if("string"==typeof i){if(!(i=e.instance.popper.querySelector(i)))return e}else if(!e.instance.popper.contains(i))return console.warn("WARNING: `arrow.element` must be child of its popper element!"),e;var r=e.placement.split("-")[0],o=e.offsets,s=o.popper,a=o.reference,l=-1!==["left","right"].indexOf(r),c=l?"height":"width",u=l?"Top":"Left",f=u.toLowerCase(),h=l?"left":"top",d=l?"bottom":"right",p=nt(i)[c];a[d]-ps[d]&&(e.offsets.popper[f]+=a[f]+p-s[d]),e.offsets.popper=Ge(e.offsets.popper);var m=a[f]+a[c]/2-p/2,g=Pe(e.instance.popper),_=parseFloat(g["margin"+u],10),v=parseFloat(g["border"+u+"Width"],10),y=m-e.offsets.popper[f]-_-v;return y=Math.max(Math.min(s[c]-p,y),0),e.arrowElement=i,e.offsets.arrow=(Ve(n={},f,Math.round(y)),Ve(n,h,""),n),e},element:"[x-arrow]"},flip:{order:600,enabled:!0,fn:function(p,m){if(at(p.instance.modifiers,"inner"))return p;if(p.flipped&&p.placement===p.originalPlacement)return p;var g=$e(p.instance.popper,p.instance.reference,m.padding,m.boundariesElement,p.positionFixed),_=p.placement.split("-")[0],v=it(_),y=p.placement.split("-")[1]||"",E=[];switch(m.behavior){case vt:E=[_,v];break;case yt:E=_t(_);break;case Et:E=_t(_,!0);break;default:E=m.behavior}return E.forEach(function(e,t){if(_!==e||E.length===t+1)return p;_=p.placement.split("-")[0],v=it(_);var n,i=p.offsets.popper,r=p.offsets.reference,o=Math.floor,s="left"===_&&o(i.right)>o(r.left)||"right"===_&&o(i.left)o(r.top)||"bottom"===_&&o(i.top)o(g.right),c=o(i.top)o(g.bottom),f="left"===_&&a||"right"===_&&l||"top"===_&&c||"bottom"===_&&u,h=-1!==["top","bottom"].indexOf(_),d=!!m.flipVariations&&(h&&"start"===y&&a||h&&"end"===y&&l||!h&&"start"===y&&c||!h&&"end"===y&&u);(s||f||d)&&(p.flipped=!0,(s||f)&&(_=E[t+1]),d&&(y="end"===(n=y)?"start":"start"===n?"end":n),p.placement=_+(y?"-"+y:""),p.offsets.popper=ze({},p.offsets.popper,rt(p.instance.popper,p.offsets.reference,p.placement)),p=st(p.instance.modifiers,p,"flip"))}),p},behavior:"flip",padding:5,boundariesElement:"viewport"},inner:{order:700,enabled:!1,fn:function(e){var t=e.placement,n=t.split("-")[0],i=e.offsets,r=i.popper,o=i.reference,s=-1!==["left","right"].indexOf(n),a=-1===["top","left"].indexOf(n);return r[s?"left":"top"]=o[n]-(a?r[s?"width":"height"]:0),e.placement=it(t),e.offsets.popper=Ge(r),e}},hide:{order:800,enabled:!0,fn:function(e){if(!pt(e.instance.modifiers,"hide","preventOverflow"))return e;var t=e.offsets.reference,n=ot(e.instance.modifiers,function(e){return"preventOverflow"===e.name}).boundaries;if(t.bottomn.right||t.top>n.bottom||t.rightdocument.documentElement.clientHeight;!this._isBodyOverflowing&&e&&(this._element.style.paddingLeft=this._scrollbarWidth+"px"),this._isBodyOverflowing&&!e&&(this._element.style.paddingRight=this._scrollbarWidth+"px")},e._resetAdjustments=function(){this._element.style.paddingLeft="",this._element.style.paddingRight=""},e._checkScrollbar=function(){var e=document.body.getBoundingClientRect();this._isBodyOverflowing=e.left+e.right
',trigger:"hover focus",title:"",delay:0,html:!(An={AUTO:"auto",TOP:"top",RIGHT:"right",BOTTOM:"bottom",LEFT:"left"}),selector:!(Dn={animation:"boolean",template:"string",title:"(string|element|function)",trigger:"string",delay:"(number|object)",html:"boolean",selector:"(string|boolean)",placement:"(string|function)",offset:"(number|string)",container:"(string|element|boolean)",fallbackPlacement:"(string|array)",boundary:"(string|element)"}),placement:"top",offset:0,container:!1,fallbackPlacement:"flip",boundary:"scrollParent"},Nn="out",kn={HIDE:"hide"+wn,HIDDEN:"hidden"+wn,SHOW:(On="show")+wn,SHOWN:"shown"+wn,INSERTED:"inserted"+wn,CLICK:"click"+wn,FOCUSIN:"focusin"+wn,FOCUSOUT:"focusout"+wn,MOUSEENTER:"mouseenter"+wn,MOUSELEAVE:"mouseleave"+wn},xn="fade",Pn="show",Ln=".tooltip-inner",jn=".arrow",Hn="hover",Mn="focus",Fn="click",Wn="manual",Rn=function(){function i(e,t){if("undefined"==typeof Ct)throw new TypeError("Bootstrap tooltips require Popper.js (https://popper.js.org)");this._isEnabled=!0,this._timeout=0,this._hoverState="",this._activeTrigger={},this._popper=null,this.element=e,this.config=this._getConfig(t),this.tip=null,this._setListeners()}var e=i.prototype;return e.enable=function(){this._isEnabled=!0},e.disable=function(){this._isEnabled=!1},e.toggleEnabled=function(){this._isEnabled=!this._isEnabled},e.toggle=function(e){if(this._isEnabled)if(e){var t=this.constructor.DATA_KEY,n=yn(e.currentTarget).data(t);n||(n=new this.constructor(e.currentTarget,this._getDelegateConfig()),yn(e.currentTarget).data(t,n)),n._activeTrigger.click=!n._activeTrigger.click,n._isWithActiveTrigger()?n._enter(null,n):n._leave(null,n)}else{if(yn(this.getTipElement()).hasClass(Pn))return void this._leave(null,this);this._enter(null,this)}},e.dispose=function(){clearTimeout(this._timeout),yn.removeData(this.element,this.constructor.DATA_KEY),yn(this.element).off(this.constructor.EVENT_KEY),yn(this.element).closest(".modal").off("hide.bs.modal"),this.tip&&yn(this.tip).remove(),this._isEnabled=null,this._timeout=null,this._hoverState=null,(this._activeTrigger=null)!==this._popper&&this._popper.destroy(),this._popper=null,this.element=null,this.config=null,this.tip=null},e.show=function(){var t=this;if("none"===yn(this.element).css("display"))throw new Error("Please use show on visible elements");var e=yn.Event(this.constructor.Event.SHOW);if(this.isWithContent()&&this._isEnabled){yn(this.element).trigger(e);var n=yn.contains(this.element.ownerDocument.documentElement,this.element);if(e.isDefaultPrevented()||!n)return;var i=this.getTipElement(),r=we.getUID(this.constructor.NAME);i.setAttribute("id",r),this.element.setAttribute("aria-describedby",r),this.setContent(),this.config.animation&&yn(i).addClass(xn);var o="function"==typeof this.config.placement?this.config.placement.call(this,i,this.element):this.config.placement,s=this._getAttachment(o);this.addAttachmentClass(s);var a=!1===this.config.container?document.body:yn(document).find(this.config.container);yn(i).data(this.constructor.DATA_KEY,this),yn.contains(this.element.ownerDocument.documentElement,this.tip)||yn(i).appendTo(a),yn(this.element).trigger(this.constructor.Event.INSERTED),this._popper=new Ct(this.element,i,{placement:s,modifiers:{offset:{offset:this.config.offset},flip:{behavior:this.config.fallbackPlacement},arrow:{element:jn},preventOverflow:{boundariesElement:this.config.boundary}},onCreate:function(e){e.originalPlacement!==e.placement&&t._handlePopperPlacementChange(e)},onUpdate:function(e){t._handlePopperPlacementChange(e)}}),yn(i).addClass(Pn),"ontouchstart"in document.documentElement&&yn(document.body).children().on("mouseover",null,yn.noop);var l=function(){t.config.animation&&t._fixTransition();var e=t._hoverState;t._hoverState=null,yn(t.element).trigger(t.constructor.Event.SHOWN),e===Nn&&t._leave(null,t)};if(yn(this.tip).hasClass(xn)){var c=we.getTransitionDurationFromElement(this.tip);yn(this.tip).one(we.TRANSITION_END,l).emulateTransitionEnd(c)}else l()}},e.hide=function(e){var t=this,n=this.getTipElement(),i=yn.Event(this.constructor.Event.HIDE),r=function(){t._hoverState!==On&&n.parentNode&&n.parentNode.removeChild(n),t._cleanTipClass(),t.element.removeAttribute("aria-describedby"),yn(t.element).trigger(t.constructor.Event.HIDDEN),null!==t._popper&&t._popper.destroy(),e&&e()};if(yn(this.element).trigger(i),!i.isDefaultPrevented()){if(yn(n).removeClass(Pn),"ontouchstart"in document.documentElement&&yn(document.body).children().off("mouseover",null,yn.noop),this._activeTrigger[Fn]=!1,this._activeTrigger[Mn]=!1,this._activeTrigger[Hn]=!1,yn(this.tip).hasClass(xn)){var o=we.getTransitionDurationFromElement(n);yn(n).one(we.TRANSITION_END,r).emulateTransitionEnd(o)}else r();this._hoverState=""}},e.update=function(){null!==this._popper&&this._popper.scheduleUpdate()},e.isWithContent=function(){return Boolean(this.getTitle())},e.addAttachmentClass=function(e){yn(this.getTipElement()).addClass(Tn+"-"+e)},e.getTipElement=function(){return this.tip=this.tip||yn(this.config.template)[0],this.tip},e.setContent=function(){var e=this.getTipElement();this.setElementContent(yn(e.querySelectorAll(Ln)),this.getTitle()),yn(e).removeClass(xn+" "+Pn)},e.setElementContent=function(e,t){var n=this.config.html;"object"==typeof t&&(t.nodeType||t.jquery)?n?yn(t).parent().is(e)||e.empty().append(t):e.text(yn(t).text()):e[n?"html":"text"](t)},e.getTitle=function(){var e=this.element.getAttribute("data-original-title");return e||(e="function"==typeof this.config.title?this.config.title.call(this.element):this.config.title),e},e._getAttachment=function(e){return An[e.toUpperCase()]},e._setListeners=function(){var i=this;this.config.trigger.split(" ").forEach(function(e){if("click"===e)yn(i.element).on(i.constructor.Event.CLICK,i.config.selector,function(e){return i.toggle(e)});else if(e!==Wn){var t=e===Hn?i.constructor.Event.MOUSEENTER:i.constructor.Event.FOCUSIN,n=e===Hn?i.constructor.Event.MOUSELEAVE:i.constructor.Event.FOCUSOUT;yn(i.element).on(t,i.config.selector,function(e){return i._enter(e)}).on(n,i.config.selector,function(e){return i._leave(e)})}yn(i.element).closest(".modal").on("hide.bs.modal",function(){return i.hide()})}),this.config.selector?this.config=l({},this.config,{trigger:"manual",selector:""}):this._fixTitle()},e._fixTitle=function(){var e=typeof this.element.getAttribute("data-original-title");(this.element.getAttribute("title")||"string"!==e)&&(this.element.setAttribute("data-original-title",this.element.getAttribute("title")||""),this.element.setAttribute("title",""))},e._enter=function(e,t){var n=this.constructor.DATA_KEY;(t=t||yn(e.currentTarget).data(n))||(t=new this.constructor(e.currentTarget,this._getDelegateConfig()),yn(e.currentTarget).data(n,t)),e&&(t._activeTrigger["focusin"===e.type?Mn:Hn]=!0),yn(t.getTipElement()).hasClass(Pn)||t._hoverState===On?t._hoverState=On:(clearTimeout(t._timeout),t._hoverState=On,t.config.delay&&t.config.delay.show?t._timeout=setTimeout(function(){t._hoverState===On&&t.show()},t.config.delay.show):t.show())},e._leave=function(e,t){var n=this.constructor.DATA_KEY;(t=t||yn(e.currentTarget).data(n))||(t=new this.constructor(e.currentTarget,this._getDelegateConfig()),yn(e.currentTarget).data(n,t)),e&&(t._activeTrigger["focusout"===e.type?Mn:Hn]=!1),t._isWithActiveTrigger()||(clearTimeout(t._timeout),t._hoverState=Nn,t.config.delay&&t.config.delay.hide?t._timeout=setTimeout(function(){t._hoverState===Nn&&t.hide()},t.config.delay.hide):t.hide())},e._isWithActiveTrigger=function(){for(var e in this._activeTrigger)if(this._activeTrigger[e])return!0;return!1},e._getConfig=function(e){return"number"==typeof(e=l({},this.constructor.Default,yn(this.element).data(),"object"==typeof e&&e?e:{})).delay&&(e.delay={show:e.delay,hide:e.delay}),"number"==typeof e.title&&(e.title=e.title.toString()),"number"==typeof e.content&&(e.content=e.content.toString()),we.typeCheckConfig(En,e,this.constructor.DefaultType),e},e._getDelegateConfig=function(){var e={};if(this.config)for(var t in this.config)this.constructor.Default[t]!==this.config[t]&&(e[t]=this.config[t]);return e},e._cleanTipClass=function(){var e=yn(this.getTipElement()),t=e.attr("class").match(Sn);null!==t&&t.length&&e.removeClass(t.join(""))},e._handlePopperPlacementChange=function(e){var t=e.instance;this.tip=t.popper,this._cleanTipClass(),this.addAttachmentClass(this._getAttachment(e.placement))},e._fixTransition=function(){var e=this.getTipElement(),t=this.config.animation;null===e.getAttribute("x-placement")&&(yn(e).removeClass(xn),this.config.animation=!1,this.hide(),this.show(),this.config.animation=t)},i._jQueryInterface=function(n){return this.each(function(){var e=yn(this).data(bn),t="object"==typeof n&&n;if((e||!/dispose|hide/.test(n))&&(e||(e=new i(this,t),yn(this).data(bn,e)),"string"==typeof n)){if("undefined"==typeof e[n])throw new TypeError('No method named "'+n+'"');e[n]()}})},s(i,null,[{key:"VERSION",get:function(){return"4.1.3"}},{key:"Default",get:function(){return In}},{key:"NAME",get:function(){return En}},{key:"DATA_KEY",get:function(){return bn}},{key:"Event",get:function(){return kn}},{key:"EVENT_KEY",get:function(){return wn}},{key:"DefaultType",get:function(){return Dn}}]),i}(),yn.fn[En]=Rn._jQueryInterface,yn.fn[En].Constructor=Rn,yn.fn[En].noConflict=function(){return yn.fn[En]=Cn,Rn._jQueryInterface},Rn),Qi=(Bn="popover",Kn="."+(qn="bs.popover"),Qn=(Un=t).fn[Bn],Yn="bs-popover",Vn=new RegExp("(^|\\s)"+Yn+"\\S+","g"),zn=l({},Ki.Default,{placement:"right",trigger:"click",content:"",template:''}),Gn=l({},Ki.DefaultType,{content:"(string|element|function)"}),Jn="fade",Xn=".popover-header",$n=".popover-body",ei={HIDE:"hide"+Kn,HIDDEN:"hidden"+Kn,SHOW:(Zn="show")+Kn,SHOWN:"shown"+Kn,INSERTED:"inserted"+Kn,CLICK:"click"+Kn,FOCUSIN:"focusin"+Kn,FOCUSOUT:"focusout"+Kn,MOUSEENTER:"mouseenter"+Kn,MOUSELEAVE:"mouseleave"+Kn},ti=function(e){var t,n;function i(){return e.apply(this,arguments)||this}n=e,(t=i).prototype=Object.create(n.prototype),(t.prototype.constructor=t).__proto__=n;var r=i.prototype;return r.isWithContent=function(){return this.getTitle()||this._getContent()},r.addAttachmentClass=function(e){Un(this.getTipElement()).addClass(Yn+"-"+e)},r.getTipElement=function(){return this.tip=this.tip||Un(this.config.template)[0],this.tip},r.setContent=function(){var e=Un(this.getTipElement());this.setElementContent(e.find(Xn),this.getTitle());var t=this._getContent();"function"==typeof t&&(t=t.call(this.element)),this.setElementContent(e.find($n),t),e.removeClass(Jn+" "+Zn)},r._getContent=function(){return this.element.getAttribute("data-content")||this.config.content},r._cleanTipClass=function(){var e=Un(this.getTipElement()),t=e.attr("class").match(Vn);null!==t&&0=this._offsets[r]&&("undefined"==typeof this._offsets[r+1]||e li > .active",Fi='[data-toggle="tab"], [data-toggle="pill"], [data-toggle="list"]',Wi=".dropdown-toggle",Ri="> .dropdown-menu .active",Ui=function(){function i(e){this._element=e}var e=i.prototype;return e.show=function(){var n=this;if(!(this._element.parentNode&&this._element.parentNode.nodeType===Node.ELEMENT_NODE&&Ti(this._element).hasClass(Ni)||Ti(this._element).hasClass(ki))){var e,i,t=Ti(this._element).closest(ji)[0],r=we.getSelectorFromElement(this._element);if(t){var o="UL"===t.nodeName?Mi:Hi;i=(i=Ti.makeArray(Ti(t).find(o)))[i.length-1]}var s=Ti.Event(Ii.HIDE,{relatedTarget:this._element}),a=Ti.Event(Ii.SHOW,{relatedTarget:i});if(i&&Ti(i).trigger(s),Ti(this._element).trigger(a),!a.isDefaultPrevented()&&!s.isDefaultPrevented()){r&&(e=document.querySelector(r)),this._activate(this._element,t);var l=function(){var e=Ti.Event(Ii.HIDDEN,{relatedTarget:n._element}),t=Ti.Event(Ii.SHOWN,{relatedTarget:i});Ti(i).trigger(e),Ti(n._element).trigger(t)};e?this._activate(e,e.parentNode,l):l()}}},e.dispose=function(){Ti.removeData(this._element,Si),this._element=null},e._activate=function(e,t,n){var i=this,r=("UL"===t.nodeName?Ti(t).find(Mi):Ti(t).children(Hi))[0],o=n&&r&&Ti(r).hasClass(xi),s=function(){return i._transitionComplete(e,r,n)};if(r&&o){var a=we.getTransitionDurationFromElement(r);Ti(r).one(we.TRANSITION_END,s).emulateTransitionEnd(a)}else s()},e._transitionComplete=function(e,t,n){if(t){Ti(t).removeClass(Pi+" "+Ni);var i=Ti(t.parentNode).find(Ri)[0];i&&Ti(i).removeClass(Ni),"tab"===t.getAttribute("role")&&t.setAttribute("aria-selected",!1)}if(Ti(e).addClass(Ni),"tab"===e.getAttribute("role")&&e.setAttribute("aria-selected",!0),we.reflow(e),Ti(e).addClass(Pi),e.parentNode&&Ti(e.parentNode).hasClass(Oi)){var r=Ti(e).closest(Li)[0];if(r){var o=[].slice.call(r.querySelectorAll(Wi));Ti(o).addClass(Ni)}e.setAttribute("aria-expanded",!0)}n&&n()},i._jQueryInterface=function(n){return this.each(function(){var e=Ti(this),t=e.data(Si);if(t||(t=new i(this),e.data(Si,t)),"string"==typeof n){if("undefined"==typeof t[n])throw new TypeError('No method named "'+n+'"');t[n]()}})},s(i,null,[{key:"VERSION",get:function(){return"4.1.3"}}]),i}(),Ti(document).on(Ii.CLICK_DATA_API,Fi,function(e){e.preventDefault(),Ui._jQueryInterface.call(Ti(this),"show")}),Ti.fn.tab=Ui._jQueryInterface,Ti.fn.tab.Constructor=Ui,Ti.fn.tab.noConflict=function(){return Ti.fn.tab=Ai,Ui._jQueryInterface},Ui);!function(e){if("undefined"==typeof e)throw new TypeError("Bootstrap's JavaScript requires jQuery. jQuery must be included before Bootstrap's JavaScript.");var t=e.fn.jquery.split(" ")[0].split(".");if(t[0]<2&&t[1]<9||1===t[0]&&9===t[1]&&t[2]<1||4<=t[0])throw new Error("Bootstrap's JavaScript requires at least jQuery v1.9.1 but less than v4.0.0")}(t),e.Util=we,e.Alert=Ce,e.Button=Te,e.Carousel=Se,e.Collapse=De,e.Dropdown=Bi,e.Modal=qi,e.Popover=Qi,e.Scrollspy=Yi,e.Tab=Vi,e.Tooltip=Ki,Object.defineProperty(e,"__esModule",{value:!0})});
//# sourceMappingURL=bootstrap.bundle.min.js.map
\ No newline at end of file
diff --git a/js/bootstrap/bootstrap.min.js b/js/bootstrap/bootstrap.min.js
index 00c895f..3d46e4b 100644
--- a/js/bootstrap/bootstrap.min.js
+++ b/js/bootstrap/bootstrap.min.js
@@ -1,7 +1,7 @@
-/*!
- * Bootstrap v4.1.3 (https://getbootstrap.com/)
- * Copyright 2011-2018 The Bootstrap Authors (https://github.com/twbs/bootstrap/graphs/contributors)
- * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
- */
-!function(t,e){"object"==typeof exports&&"undefined"!=typeof module?e(exports,require("jquery"),require("popper.js")):"function"==typeof define&&define.amd?define(["exports","jquery","popper.js"],e):e(t.bootstrap={},t.jQuery,t.Popper)}(this,function(t,e,h){"use strict";function i(t,e){for(var n=0;nthis._items.length-1||t<0))if(this._isSliding)P(this._element).one(Q.SLID,function(){return e.to(t)});else{if(n===t)return this.pause(),void this.cycle();var i=ndocument.documentElement.clientHeight;!this._isBodyOverflowing&&t&&(this._element.style.paddingLeft=this._scrollbarWidth+"px"),this._isBodyOverflowing&&!t&&(this._element.style.paddingRight=this._scrollbarWidth+"px")},t._resetAdjustments=function(){this._element.style.paddingLeft="",this._element.style.paddingRight=""},t._checkScrollbar=function(){var t=document.body.getBoundingClientRect();this._isBodyOverflowing=t.left+t.right
',trigger:"hover focus",title:"",delay:0,html:!(Ie={AUTO:"auto",TOP:"top",RIGHT:"right",BOTTOM:"bottom",LEFT:"left"}),selector:!(Se={animation:"boolean",template:"string",title:"(string|element|function)",trigger:"string",delay:"(number|object)",html:"boolean",selector:"(string|boolean)",placement:"(string|function)",offset:"(number|string)",container:"(string|element|boolean)",fallbackPlacement:"(string|array)",boundary:"(string|element)"}),placement:"top",offset:0,container:!1,fallbackPlacement:"flip",boundary:"scrollParent"},we="out",Ne={HIDE:"hide"+Ee,HIDDEN:"hidden"+Ee,SHOW:(De="show")+Ee,SHOWN:"shown"+Ee,INSERTED:"inserted"+Ee,CLICK:"click"+Ee,FOCUSIN:"focusin"+Ee,FOCUSOUT:"focusout"+Ee,MOUSEENTER:"mouseenter"+Ee,MOUSELEAVE:"mouseleave"+Ee},Oe="fade",ke="show",Pe=".tooltip-inner",je=".arrow",He="hover",Le="focus",Re="click",xe="manual",We=function(){function i(t,e){if("undefined"==typeof h)throw new TypeError("Bootstrap tooltips require Popper.js (https://popper.js.org)");this._isEnabled=!0,this._timeout=0,this._hoverState="",this._activeTrigger={},this._popper=null,this.element=t,this.config=this._getConfig(e),this.tip=null,this._setListeners()}var t=i.prototype;return t.enable=function(){this._isEnabled=!0},t.disable=function(){this._isEnabled=!1},t.toggleEnabled=function(){this._isEnabled=!this._isEnabled},t.toggle=function(t){if(this._isEnabled)if(t){var e=this.constructor.DATA_KEY,n=pe(t.currentTarget).data(e);n||(n=new this.constructor(t.currentTarget,this._getDelegateConfig()),pe(t.currentTarget).data(e,n)),n._activeTrigger.click=!n._activeTrigger.click,n._isWithActiveTrigger()?n._enter(null,n):n._leave(null,n)}else{if(pe(this.getTipElement()).hasClass(ke))return void this._leave(null,this);this._enter(null,this)}},t.dispose=function(){clearTimeout(this._timeout),pe.removeData(this.element,this.constructor.DATA_KEY),pe(this.element).off(this.constructor.EVENT_KEY),pe(this.element).closest(".modal").off("hide.bs.modal"),this.tip&&pe(this.tip).remove(),this._isEnabled=null,this._timeout=null,this._hoverState=null,(this._activeTrigger=null)!==this._popper&&this._popper.destroy(),this._popper=null,this.element=null,this.config=null,this.tip=null},t.show=function(){var e=this;if("none"===pe(this.element).css("display"))throw new Error("Please use show on visible elements");var t=pe.Event(this.constructor.Event.SHOW);if(this.isWithContent()&&this._isEnabled){pe(this.element).trigger(t);var n=pe.contains(this.element.ownerDocument.documentElement,this.element);if(t.isDefaultPrevented()||!n)return;var i=this.getTipElement(),r=Fn.getUID(this.constructor.NAME);i.setAttribute("id",r),this.element.setAttribute("aria-describedby",r),this.setContent(),this.config.animation&&pe(i).addClass(Oe);var o="function"==typeof this.config.placement?this.config.placement.call(this,i,this.element):this.config.placement,s=this._getAttachment(o);this.addAttachmentClass(s);var a=!1===this.config.container?document.body:pe(document).find(this.config.container);pe(i).data(this.constructor.DATA_KEY,this),pe.contains(this.element.ownerDocument.documentElement,this.tip)||pe(i).appendTo(a),pe(this.element).trigger(this.constructor.Event.INSERTED),this._popper=new h(this.element,i,{placement:s,modifiers:{offset:{offset:this.config.offset},flip:{behavior:this.config.fallbackPlacement},arrow:{element:je},preventOverflow:{boundariesElement:this.config.boundary}},onCreate:function(t){t.originalPlacement!==t.placement&&e._handlePopperPlacementChange(t)},onUpdate:function(t){e._handlePopperPlacementChange(t)}}),pe(i).addClass(ke),"ontouchstart"in document.documentElement&&pe(document.body).children().on("mouseover",null,pe.noop);var l=function(){e.config.animation&&e._fixTransition();var t=e._hoverState;e._hoverState=null,pe(e.element).trigger(e.constructor.Event.SHOWN),t===we&&e._leave(null,e)};if(pe(this.tip).hasClass(Oe)){var c=Fn.getTransitionDurationFromElement(this.tip);pe(this.tip).one(Fn.TRANSITION_END,l).emulateTransitionEnd(c)}else l()}},t.hide=function(t){var e=this,n=this.getTipElement(),i=pe.Event(this.constructor.Event.HIDE),r=function(){e._hoverState!==De&&n.parentNode&&n.parentNode.removeChild(n),e._cleanTipClass(),e.element.removeAttribute("aria-describedby"),pe(e.element).trigger(e.constructor.Event.HIDDEN),null!==e._popper&&e._popper.destroy(),t&&t()};if(pe(this.element).trigger(i),!i.isDefaultPrevented()){if(pe(n).removeClass(ke),"ontouchstart"in document.documentElement&&pe(document.body).children().off("mouseover",null,pe.noop),this._activeTrigger[Re]=!1,this._activeTrigger[Le]=!1,this._activeTrigger[He]=!1,pe(this.tip).hasClass(Oe)){var o=Fn.getTransitionDurationFromElement(n);pe(n).one(Fn.TRANSITION_END,r).emulateTransitionEnd(o)}else r();this._hoverState=""}},t.update=function(){null!==this._popper&&this._popper.scheduleUpdate()},t.isWithContent=function(){return Boolean(this.getTitle())},t.addAttachmentClass=function(t){pe(this.getTipElement()).addClass(Te+"-"+t)},t.getTipElement=function(){return this.tip=this.tip||pe(this.config.template)[0],this.tip},t.setContent=function(){var t=this.getTipElement();this.setElementContent(pe(t.querySelectorAll(Pe)),this.getTitle()),pe(t).removeClass(Oe+" "+ke)},t.setElementContent=function(t,e){var n=this.config.html;"object"==typeof e&&(e.nodeType||e.jquery)?n?pe(e).parent().is(t)||t.empty().append(e):t.text(pe(e).text()):t[n?"html":"text"](e)},t.getTitle=function(){var t=this.element.getAttribute("data-original-title");return t||(t="function"==typeof this.config.title?this.config.title.call(this.element):this.config.title),t},t._getAttachment=function(t){return Ie[t.toUpperCase()]},t._setListeners=function(){var i=this;this.config.trigger.split(" ").forEach(function(t){if("click"===t)pe(i.element).on(i.constructor.Event.CLICK,i.config.selector,function(t){return i.toggle(t)});else if(t!==xe){var e=t===He?i.constructor.Event.MOUSEENTER:i.constructor.Event.FOCUSIN,n=t===He?i.constructor.Event.MOUSELEAVE:i.constructor.Event.FOCUSOUT;pe(i.element).on(e,i.config.selector,function(t){return i._enter(t)}).on(n,i.config.selector,function(t){return i._leave(t)})}pe(i.element).closest(".modal").on("hide.bs.modal",function(){return i.hide()})}),this.config.selector?this.config=l({},this.config,{trigger:"manual",selector:""}):this._fixTitle()},t._fixTitle=function(){var t=typeof this.element.getAttribute("data-original-title");(this.element.getAttribute("title")||"string"!==t)&&(this.element.setAttribute("data-original-title",this.element.getAttribute("title")||""),this.element.setAttribute("title",""))},t._enter=function(t,e){var n=this.constructor.DATA_KEY;(e=e||pe(t.currentTarget).data(n))||(e=new this.constructor(t.currentTarget,this._getDelegateConfig()),pe(t.currentTarget).data(n,e)),t&&(e._activeTrigger["focusin"===t.type?Le:He]=!0),pe(e.getTipElement()).hasClass(ke)||e._hoverState===De?e._hoverState=De:(clearTimeout(e._timeout),e._hoverState=De,e.config.delay&&e.config.delay.show?e._timeout=setTimeout(function(){e._hoverState===De&&e.show()},e.config.delay.show):e.show())},t._leave=function(t,e){var n=this.constructor.DATA_KEY;(e=e||pe(t.currentTarget).data(n))||(e=new this.constructor(t.currentTarget,this._getDelegateConfig()),pe(t.currentTarget).data(n,e)),t&&(e._activeTrigger["focusout"===t.type?Le:He]=!1),e._isWithActiveTrigger()||(clearTimeout(e._timeout),e._hoverState=we,e.config.delay&&e.config.delay.hide?e._timeout=setTimeout(function(){e._hoverState===we&&e.hide()},e.config.delay.hide):e.hide())},t._isWithActiveTrigger=function(){for(var t in this._activeTrigger)if(this._activeTrigger[t])return!0;return!1},t._getConfig=function(t){return"number"==typeof(t=l({},this.constructor.Default,pe(this.element).data(),"object"==typeof t&&t?t:{})).delay&&(t.delay={show:t.delay,hide:t.delay}),"number"==typeof t.title&&(t.title=t.title.toString()),"number"==typeof t.content&&(t.content=t.content.toString()),Fn.typeCheckConfig(ve,t,this.constructor.DefaultType),t},t._getDelegateConfig=function(){var t={};if(this.config)for(var e in this.config)this.constructor.Default[e]!==this.config[e]&&(t[e]=this.config[e]);return t},t._cleanTipClass=function(){var t=pe(this.getTipElement()),e=t.attr("class").match(be);null!==e&&e.length&&t.removeClass(e.join(""))},t._handlePopperPlacementChange=function(t){var e=t.instance;this.tip=e.popper,this._cleanTipClass(),this.addAttachmentClass(this._getAttachment(t.placement))},t._fixTransition=function(){var t=this.getTipElement(),e=this.config.animation;null===t.getAttribute("x-placement")&&(pe(t).removeClass(Oe),this.config.animation=!1,this.hide(),this.show(),this.config.animation=e)},i._jQueryInterface=function(n){return this.each(function(){var t=pe(this).data(ye),e="object"==typeof n&&n;if((t||!/dispose|hide/.test(n))&&(t||(t=new i(this,e),pe(this).data(ye,t)),"string"==typeof n)){if("undefined"==typeof t[n])throw new TypeError('No method named "'+n+'"');t[n]()}})},s(i,null,[{key:"VERSION",get:function(){return"4.1.3"}},{key:"Default",get:function(){return Ae}},{key:"NAME",get:function(){return ve}},{key:"DATA_KEY",get:function(){return ye}},{key:"Event",get:function(){return Ne}},{key:"EVENT_KEY",get:function(){return Ee}},{key:"DefaultType",get:function(){return Se}}]),i}(),pe.fn[ve]=We._jQueryInterface,pe.fn[ve].Constructor=We,pe.fn[ve].noConflict=function(){return pe.fn[ve]=Ce,We._jQueryInterface},We),Jn=(qe="popover",Ke="."+(Fe="bs.popover"),Me=(Ue=e).fn[qe],Qe="bs-popover",Be=new RegExp("(^|\\s)"+Qe+"\\S+","g"),Ve=l({},zn.Default,{placement:"right",trigger:"click",content:"",template:''}),Ye=l({},zn.DefaultType,{content:"(string|element|function)"}),ze="fade",Ze=".popover-header",Ge=".popover-body",$e={HIDE:"hide"+Ke,HIDDEN:"hidden"+Ke,SHOW:(Je="show")+Ke,SHOWN:"shown"+Ke,INSERTED:"inserted"+Ke,CLICK:"click"+Ke,FOCUSIN:"focusin"+Ke,FOCUSOUT:"focusout"+Ke,MOUSEENTER:"mouseenter"+Ke,MOUSELEAVE:"mouseleave"+Ke},Xe=function(t){var e,n;function i(){return t.apply(this,arguments)||this}n=t,(e=i).prototype=Object.create(n.prototype),(e.prototype.constructor=e).__proto__=n;var r=i.prototype;return r.isWithContent=function(){return this.getTitle()||this._getContent()},r.addAttachmentClass=function(t){Ue(this.getTipElement()).addClass(Qe+"-"+t)},r.getTipElement=function(){return this.tip=this.tip||Ue(this.config.template)[0],this.tip},r.setContent=function(){var t=Ue(this.getTipElement());this.setElementContent(t.find(Ze),this.getTitle());var e=this._getContent();"function"==typeof e&&(e=e.call(this.element)),this.setElementContent(t.find(Ge),e),t.removeClass(ze+" "+Je)},r._getContent=function(){return this.element.getAttribute("data-content")||this.config.content},r._cleanTipClass=function(){var t=Ue(this.getTipElement()),e=t.attr("class").match(Be);null!==e&&0=this._offsets[r]&&("undefined"==typeof this._offsets[r+1]||t li > .active",xn='[data-toggle="tab"], [data-toggle="pill"], [data-toggle="list"]',Wn=".dropdown-toggle",Un="> .dropdown-menu .active",qn=function(){function i(t){this._element=t}var t=i.prototype;return t.show=function(){var n=this;if(!(this._element.parentNode&&this._element.parentNode.nodeType===Node.ELEMENT_NODE&&bn(this._element).hasClass(Nn)||bn(this._element).hasClass(On))){var t,i,e=bn(this._element).closest(Hn)[0],r=Fn.getSelectorFromElement(this._element);if(e){var o="UL"===e.nodeName?Rn:Ln;i=(i=bn.makeArray(bn(e).find(o)))[i.length-1]}var s=bn.Event(Dn.HIDE,{relatedTarget:this._element}),a=bn.Event(Dn.SHOW,{relatedTarget:i});if(i&&bn(i).trigger(s),bn(this._element).trigger(a),!a.isDefaultPrevented()&&!s.isDefaultPrevented()){r&&(t=document.querySelector(r)),this._activate(this._element,e);var l=function(){var t=bn.Event(Dn.HIDDEN,{relatedTarget:n._element}),e=bn.Event(Dn.SHOWN,{relatedTarget:i});bn(i).trigger(t),bn(n._element).trigger(e)};t?this._activate(t,t.parentNode,l):l()}}},t.dispose=function(){bn.removeData(this._element,Sn),this._element=null},t._activate=function(t,e,n){var i=this,r=("UL"===e.nodeName?bn(e).find(Rn):bn(e).children(Ln))[0],o=n&&r&&bn(r).hasClass(kn),s=function(){return i._transitionComplete(t,r,n)};if(r&&o){var a=Fn.getTransitionDurationFromElement(r);bn(r).one(Fn.TRANSITION_END,s).emulateTransitionEnd(a)}else s()},t._transitionComplete=function(t,e,n){if(e){bn(e).removeClass(Pn+" "+Nn);var i=bn(e.parentNode).find(Un)[0];i&&bn(i).removeClass(Nn),"tab"===e.getAttribute("role")&&e.setAttribute("aria-selected",!1)}if(bn(t).addClass(Nn),"tab"===t.getAttribute("role")&&t.setAttribute("aria-selected",!0),Fn.reflow(t),bn(t).addClass(Pn),t.parentNode&&bn(t.parentNode).hasClass(wn)){var r=bn(t).closest(jn)[0];if(r){var o=[].slice.call(r.querySelectorAll(Wn));bn(o).addClass(Nn)}t.setAttribute("aria-expanded",!0)}n&&n()},i._jQueryInterface=function(n){return this.each(function(){var t=bn(this),e=t.data(Sn);if(e||(e=new i(this),t.data(Sn,e)),"string"==typeof n){if("undefined"==typeof e[n])throw new TypeError('No method named "'+n+'"');e[n]()}})},s(i,null,[{key:"VERSION",get:function(){return"4.1.3"}}]),i}(),bn(document).on(Dn.CLICK_DATA_API,xn,function(t){t.preventDefault(),qn._jQueryInterface.call(bn(this),"show")}),bn.fn.tab=qn._jQueryInterface,bn.fn.tab.Constructor=qn,bn.fn.tab.noConflict=function(){return bn.fn.tab=An,qn._jQueryInterface},qn);!function(t){if("undefined"==typeof t)throw new TypeError("Bootstrap's JavaScript requires jQuery. jQuery must be included before Bootstrap's JavaScript.");var e=t.fn.jquery.split(" ")[0].split(".");if(e[0]<2&&e[1]<9||1===e[0]&&9===e[1]&&e[2]<1||4<=e[0])throw new Error("Bootstrap's JavaScript requires at least jQuery v1.9.1 but less than v4.0.0")}(e),t.Util=Fn,t.Alert=Kn,t.Button=Mn,t.Carousel=Qn,t.Collapse=Bn,t.Dropdown=Vn,t.Modal=Yn,t.Popover=Jn,t.Scrollspy=Zn,t.Tab=Gn,t.Tooltip=zn,Object.defineProperty(t,"__esModule",{value:!0})});
+/*!
+ * Bootstrap v4.1.3 (https://getbootstrap.com/)
+ * Copyright 2011-2018 The Bootstrap Authors (https://github.com/twbs/bootstrap/graphs/contributors)
+ * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
+ */
+!function(t,e){"object"==typeof exports&&"undefined"!=typeof module?e(exports,require("jquery"),require("popper.js")):"function"==typeof define&&define.amd?define(["exports","jquery","popper.js"],e):e(t.bootstrap={},t.jQuery,t.Popper)}(this,function(t,e,h){"use strict";function i(t,e){for(var n=0;nthis._items.length-1||t<0))if(this._isSliding)P(this._element).one(Q.SLID,function(){return e.to(t)});else{if(n===t)return this.pause(),void this.cycle();var i=ndocument.documentElement.clientHeight;!this._isBodyOverflowing&&t&&(this._element.style.paddingLeft=this._scrollbarWidth+"px"),this._isBodyOverflowing&&!t&&(this._element.style.paddingRight=this._scrollbarWidth+"px")},t._resetAdjustments=function(){this._element.style.paddingLeft="",this._element.style.paddingRight=""},t._checkScrollbar=function(){var t=document.body.getBoundingClientRect();this._isBodyOverflowing=t.left+t.right
',trigger:"hover focus",title:"",delay:0,html:!(Ie={AUTO:"auto",TOP:"top",RIGHT:"right",BOTTOM:"bottom",LEFT:"left"}),selector:!(Se={animation:"boolean",template:"string",title:"(string|element|function)",trigger:"string",delay:"(number|object)",html:"boolean",selector:"(string|boolean)",placement:"(string|function)",offset:"(number|string)",container:"(string|element|boolean)",fallbackPlacement:"(string|array)",boundary:"(string|element)"}),placement:"top",offset:0,container:!1,fallbackPlacement:"flip",boundary:"scrollParent"},we="out",Ne={HIDE:"hide"+Ee,HIDDEN:"hidden"+Ee,SHOW:(De="show")+Ee,SHOWN:"shown"+Ee,INSERTED:"inserted"+Ee,CLICK:"click"+Ee,FOCUSIN:"focusin"+Ee,FOCUSOUT:"focusout"+Ee,MOUSEENTER:"mouseenter"+Ee,MOUSELEAVE:"mouseleave"+Ee},Oe="fade",ke="show",Pe=".tooltip-inner",je=".arrow",He="hover",Le="focus",Re="click",xe="manual",We=function(){function i(t,e){if("undefined"==typeof h)throw new TypeError("Bootstrap tooltips require Popper.js (https://popper.js.org)");this._isEnabled=!0,this._timeout=0,this._hoverState="",this._activeTrigger={},this._popper=null,this.element=t,this.config=this._getConfig(e),this.tip=null,this._setListeners()}var t=i.prototype;return t.enable=function(){this._isEnabled=!0},t.disable=function(){this._isEnabled=!1},t.toggleEnabled=function(){this._isEnabled=!this._isEnabled},t.toggle=function(t){if(this._isEnabled)if(t){var e=this.constructor.DATA_KEY,n=pe(t.currentTarget).data(e);n||(n=new this.constructor(t.currentTarget,this._getDelegateConfig()),pe(t.currentTarget).data(e,n)),n._activeTrigger.click=!n._activeTrigger.click,n._isWithActiveTrigger()?n._enter(null,n):n._leave(null,n)}else{if(pe(this.getTipElement()).hasClass(ke))return void this._leave(null,this);this._enter(null,this)}},t.dispose=function(){clearTimeout(this._timeout),pe.removeData(this.element,this.constructor.DATA_KEY),pe(this.element).off(this.constructor.EVENT_KEY),pe(this.element).closest(".modal").off("hide.bs.modal"),this.tip&&pe(this.tip).remove(),this._isEnabled=null,this._timeout=null,this._hoverState=null,(this._activeTrigger=null)!==this._popper&&this._popper.destroy(),this._popper=null,this.element=null,this.config=null,this.tip=null},t.show=function(){var e=this;if("none"===pe(this.element).css("display"))throw new Error("Please use show on visible elements");var t=pe.Event(this.constructor.Event.SHOW);if(this.isWithContent()&&this._isEnabled){pe(this.element).trigger(t);var n=pe.contains(this.element.ownerDocument.documentElement,this.element);if(t.isDefaultPrevented()||!n)return;var i=this.getTipElement(),r=Fn.getUID(this.constructor.NAME);i.setAttribute("id",r),this.element.setAttribute("aria-describedby",r),this.setContent(),this.config.animation&&pe(i).addClass(Oe);var o="function"==typeof this.config.placement?this.config.placement.call(this,i,this.element):this.config.placement,s=this._getAttachment(o);this.addAttachmentClass(s);var a=!1===this.config.container?document.body:pe(document).find(this.config.container);pe(i).data(this.constructor.DATA_KEY,this),pe.contains(this.element.ownerDocument.documentElement,this.tip)||pe(i).appendTo(a),pe(this.element).trigger(this.constructor.Event.INSERTED),this._popper=new h(this.element,i,{placement:s,modifiers:{offset:{offset:this.config.offset},flip:{behavior:this.config.fallbackPlacement},arrow:{element:je},preventOverflow:{boundariesElement:this.config.boundary}},onCreate:function(t){t.originalPlacement!==t.placement&&e._handlePopperPlacementChange(t)},onUpdate:function(t){e._handlePopperPlacementChange(t)}}),pe(i).addClass(ke),"ontouchstart"in document.documentElement&&pe(document.body).children().on("mouseover",null,pe.noop);var l=function(){e.config.animation&&e._fixTransition();var t=e._hoverState;e._hoverState=null,pe(e.element).trigger(e.constructor.Event.SHOWN),t===we&&e._leave(null,e)};if(pe(this.tip).hasClass(Oe)){var c=Fn.getTransitionDurationFromElement(this.tip);pe(this.tip).one(Fn.TRANSITION_END,l).emulateTransitionEnd(c)}else l()}},t.hide=function(t){var e=this,n=this.getTipElement(),i=pe.Event(this.constructor.Event.HIDE),r=function(){e._hoverState!==De&&n.parentNode&&n.parentNode.removeChild(n),e._cleanTipClass(),e.element.removeAttribute("aria-describedby"),pe(e.element).trigger(e.constructor.Event.HIDDEN),null!==e._popper&&e._popper.destroy(),t&&t()};if(pe(this.element).trigger(i),!i.isDefaultPrevented()){if(pe(n).removeClass(ke),"ontouchstart"in document.documentElement&&pe(document.body).children().off("mouseover",null,pe.noop),this._activeTrigger[Re]=!1,this._activeTrigger[Le]=!1,this._activeTrigger[He]=!1,pe(this.tip).hasClass(Oe)){var o=Fn.getTransitionDurationFromElement(n);pe(n).one(Fn.TRANSITION_END,r).emulateTransitionEnd(o)}else r();this._hoverState=""}},t.update=function(){null!==this._popper&&this._popper.scheduleUpdate()},t.isWithContent=function(){return Boolean(this.getTitle())},t.addAttachmentClass=function(t){pe(this.getTipElement()).addClass(Te+"-"+t)},t.getTipElement=function(){return this.tip=this.tip||pe(this.config.template)[0],this.tip},t.setContent=function(){var t=this.getTipElement();this.setElementContent(pe(t.querySelectorAll(Pe)),this.getTitle()),pe(t).removeClass(Oe+" "+ke)},t.setElementContent=function(t,e){var n=this.config.html;"object"==typeof e&&(e.nodeType||e.jquery)?n?pe(e).parent().is(t)||t.empty().append(e):t.text(pe(e).text()):t[n?"html":"text"](e)},t.getTitle=function(){var t=this.element.getAttribute("data-original-title");return t||(t="function"==typeof this.config.title?this.config.title.call(this.element):this.config.title),t},t._getAttachment=function(t){return Ie[t.toUpperCase()]},t._setListeners=function(){var i=this;this.config.trigger.split(" ").forEach(function(t){if("click"===t)pe(i.element).on(i.constructor.Event.CLICK,i.config.selector,function(t){return i.toggle(t)});else if(t!==xe){var e=t===He?i.constructor.Event.MOUSEENTER:i.constructor.Event.FOCUSIN,n=t===He?i.constructor.Event.MOUSELEAVE:i.constructor.Event.FOCUSOUT;pe(i.element).on(e,i.config.selector,function(t){return i._enter(t)}).on(n,i.config.selector,function(t){return i._leave(t)})}pe(i.element).closest(".modal").on("hide.bs.modal",function(){return i.hide()})}),this.config.selector?this.config=l({},this.config,{trigger:"manual",selector:""}):this._fixTitle()},t._fixTitle=function(){var t=typeof this.element.getAttribute("data-original-title");(this.element.getAttribute("title")||"string"!==t)&&(this.element.setAttribute("data-original-title",this.element.getAttribute("title")||""),this.element.setAttribute("title",""))},t._enter=function(t,e){var n=this.constructor.DATA_KEY;(e=e||pe(t.currentTarget).data(n))||(e=new this.constructor(t.currentTarget,this._getDelegateConfig()),pe(t.currentTarget).data(n,e)),t&&(e._activeTrigger["focusin"===t.type?Le:He]=!0),pe(e.getTipElement()).hasClass(ke)||e._hoverState===De?e._hoverState=De:(clearTimeout(e._timeout),e._hoverState=De,e.config.delay&&e.config.delay.show?e._timeout=setTimeout(function(){e._hoverState===De&&e.show()},e.config.delay.show):e.show())},t._leave=function(t,e){var n=this.constructor.DATA_KEY;(e=e||pe(t.currentTarget).data(n))||(e=new this.constructor(t.currentTarget,this._getDelegateConfig()),pe(t.currentTarget).data(n,e)),t&&(e._activeTrigger["focusout"===t.type?Le:He]=!1),e._isWithActiveTrigger()||(clearTimeout(e._timeout),e._hoverState=we,e.config.delay&&e.config.delay.hide?e._timeout=setTimeout(function(){e._hoverState===we&&e.hide()},e.config.delay.hide):e.hide())},t._isWithActiveTrigger=function(){for(var t in this._activeTrigger)if(this._activeTrigger[t])return!0;return!1},t._getConfig=function(t){return"number"==typeof(t=l({},this.constructor.Default,pe(this.element).data(),"object"==typeof t&&t?t:{})).delay&&(t.delay={show:t.delay,hide:t.delay}),"number"==typeof t.title&&(t.title=t.title.toString()),"number"==typeof t.content&&(t.content=t.content.toString()),Fn.typeCheckConfig(ve,t,this.constructor.DefaultType),t},t._getDelegateConfig=function(){var t={};if(this.config)for(var e in this.config)this.constructor.Default[e]!==this.config[e]&&(t[e]=this.config[e]);return t},t._cleanTipClass=function(){var t=pe(this.getTipElement()),e=t.attr("class").match(be);null!==e&&e.length&&t.removeClass(e.join(""))},t._handlePopperPlacementChange=function(t){var e=t.instance;this.tip=e.popper,this._cleanTipClass(),this.addAttachmentClass(this._getAttachment(t.placement))},t._fixTransition=function(){var t=this.getTipElement(),e=this.config.animation;null===t.getAttribute("x-placement")&&(pe(t).removeClass(Oe),this.config.animation=!1,this.hide(),this.show(),this.config.animation=e)},i._jQueryInterface=function(n){return this.each(function(){var t=pe(this).data(ye),e="object"==typeof n&&n;if((t||!/dispose|hide/.test(n))&&(t||(t=new i(this,e),pe(this).data(ye,t)),"string"==typeof n)){if("undefined"==typeof t[n])throw new TypeError('No method named "'+n+'"');t[n]()}})},s(i,null,[{key:"VERSION",get:function(){return"4.1.3"}},{key:"Default",get:function(){return Ae}},{key:"NAME",get:function(){return ve}},{key:"DATA_KEY",get:function(){return ye}},{key:"Event",get:function(){return Ne}},{key:"EVENT_KEY",get:function(){return Ee}},{key:"DefaultType",get:function(){return Se}}]),i}(),pe.fn[ve]=We._jQueryInterface,pe.fn[ve].Constructor=We,pe.fn[ve].noConflict=function(){return pe.fn[ve]=Ce,We._jQueryInterface},We),Jn=(qe="popover",Ke="."+(Fe="bs.popover"),Me=(Ue=e).fn[qe],Qe="bs-popover",Be=new RegExp("(^|\\s)"+Qe+"\\S+","g"),Ve=l({},zn.Default,{placement:"right",trigger:"click",content:"",template:''}),Ye=l({},zn.DefaultType,{content:"(string|element|function)"}),ze="fade",Ze=".popover-header",Ge=".popover-body",$e={HIDE:"hide"+Ke,HIDDEN:"hidden"+Ke,SHOW:(Je="show")+Ke,SHOWN:"shown"+Ke,INSERTED:"inserted"+Ke,CLICK:"click"+Ke,FOCUSIN:"focusin"+Ke,FOCUSOUT:"focusout"+Ke,MOUSEENTER:"mouseenter"+Ke,MOUSELEAVE:"mouseleave"+Ke},Xe=function(t){var e,n;function i(){return t.apply(this,arguments)||this}n=t,(e=i).prototype=Object.create(n.prototype),(e.prototype.constructor=e).__proto__=n;var r=i.prototype;return r.isWithContent=function(){return this.getTitle()||this._getContent()},r.addAttachmentClass=function(t){Ue(this.getTipElement()).addClass(Qe+"-"+t)},r.getTipElement=function(){return this.tip=this.tip||Ue(this.config.template)[0],this.tip},r.setContent=function(){var t=Ue(this.getTipElement());this.setElementContent(t.find(Ze),this.getTitle());var e=this._getContent();"function"==typeof e&&(e=e.call(this.element)),this.setElementContent(t.find(Ge),e),t.removeClass(ze+" "+Je)},r._getContent=function(){return this.element.getAttribute("data-content")||this.config.content},r._cleanTipClass=function(){var t=Ue(this.getTipElement()),e=t.attr("class").match(Be);null!==e&&0=this._offsets[r]&&("undefined"==typeof this._offsets[r+1]||t li > .active",xn='[data-toggle="tab"], [data-toggle="pill"], [data-toggle="list"]',Wn=".dropdown-toggle",Un="> .dropdown-menu .active",qn=function(){function i(t){this._element=t}var t=i.prototype;return t.show=function(){var n=this;if(!(this._element.parentNode&&this._element.parentNode.nodeType===Node.ELEMENT_NODE&&bn(this._element).hasClass(Nn)||bn(this._element).hasClass(On))){var t,i,e=bn(this._element).closest(Hn)[0],r=Fn.getSelectorFromElement(this._element);if(e){var o="UL"===e.nodeName?Rn:Ln;i=(i=bn.makeArray(bn(e).find(o)))[i.length-1]}var s=bn.Event(Dn.HIDE,{relatedTarget:this._element}),a=bn.Event(Dn.SHOW,{relatedTarget:i});if(i&&bn(i).trigger(s),bn(this._element).trigger(a),!a.isDefaultPrevented()&&!s.isDefaultPrevented()){r&&(t=document.querySelector(r)),this._activate(this._element,e);var l=function(){var t=bn.Event(Dn.HIDDEN,{relatedTarget:n._element}),e=bn.Event(Dn.SHOWN,{relatedTarget:i});bn(i).trigger(t),bn(n._element).trigger(e)};t?this._activate(t,t.parentNode,l):l()}}},t.dispose=function(){bn.removeData(this._element,Sn),this._element=null},t._activate=function(t,e,n){var i=this,r=("UL"===e.nodeName?bn(e).find(Rn):bn(e).children(Ln))[0],o=n&&r&&bn(r).hasClass(kn),s=function(){return i._transitionComplete(t,r,n)};if(r&&o){var a=Fn.getTransitionDurationFromElement(r);bn(r).one(Fn.TRANSITION_END,s).emulateTransitionEnd(a)}else s()},t._transitionComplete=function(t,e,n){if(e){bn(e).removeClass(Pn+" "+Nn);var i=bn(e.parentNode).find(Un)[0];i&&bn(i).removeClass(Nn),"tab"===e.getAttribute("role")&&e.setAttribute("aria-selected",!1)}if(bn(t).addClass(Nn),"tab"===t.getAttribute("role")&&t.setAttribute("aria-selected",!0),Fn.reflow(t),bn(t).addClass(Pn),t.parentNode&&bn(t.parentNode).hasClass(wn)){var r=bn(t).closest(jn)[0];if(r){var o=[].slice.call(r.querySelectorAll(Wn));bn(o).addClass(Nn)}t.setAttribute("aria-expanded",!0)}n&&n()},i._jQueryInterface=function(n){return this.each(function(){var t=bn(this),e=t.data(Sn);if(e||(e=new i(this),t.data(Sn,e)),"string"==typeof n){if("undefined"==typeof e[n])throw new TypeError('No method named "'+n+'"');e[n]()}})},s(i,null,[{key:"VERSION",get:function(){return"4.1.3"}}]),i}(),bn(document).on(Dn.CLICK_DATA_API,xn,function(t){t.preventDefault(),qn._jQueryInterface.call(bn(this),"show")}),bn.fn.tab=qn._jQueryInterface,bn.fn.tab.Constructor=qn,bn.fn.tab.noConflict=function(){return bn.fn.tab=An,qn._jQueryInterface},qn);!function(t){if("undefined"==typeof t)throw new TypeError("Bootstrap's JavaScript requires jQuery. jQuery must be included before Bootstrap's JavaScript.");var e=t.fn.jquery.split(" ")[0].split(".");if(e[0]<2&&e[1]<9||1===e[0]&&9===e[1]&&e[2]<1||4<=e[0])throw new Error("Bootstrap's JavaScript requires at least jQuery v1.9.1 but less than v4.0.0")}(e),t.Util=Fn,t.Alert=Kn,t.Button=Mn,t.Carousel=Qn,t.Collapse=Bn,t.Dropdown=Vn,t.Modal=Yn,t.Popover=Jn,t.Scrollspy=Zn,t.Tab=Gn,t.Tooltip=zn,Object.defineProperty(t,"__esModule",{value:!0})});
//# sourceMappingURL=bootstrap.min.js.map
\ No newline at end of file
diff --git a/js/bootstrap/popper.min.js b/js/bootstrap/popper.min.js
index 18ecbfa..fd7ae5b 100644
--- a/js/bootstrap/popper.min.js
+++ b/js/bootstrap/popper.min.js
@@ -1,5 +1,5 @@
-/*
- Copyright (C) Federico Zivolo 2018
- Distributed under the MIT License (license terms are at http://opensource.org/licenses/MIT).
- */(function(e,t){'object'==typeof exports&&'undefined'!=typeof module?module.exports=t():'function'==typeof define&&define.amd?define(t):e.Popper=t()})(this,function(){'use strict';function e(e){return e&&'[object Function]'==={}.toString.call(e)}function t(e,t){if(1!==e.nodeType)return[];var o=e.ownerDocument.defaultView,n=o.getComputedStyle(e,null);return t?n[t]:n}function o(e){return'HTML'===e.nodeName?e:e.parentNode||e.host}function n(e){if(!e)return document.body;switch(e.nodeName){case'HTML':case'BODY':return e.ownerDocument.body;case'#document':return e.body;}var i=t(e),r=i.overflow,p=i.overflowX,s=i.overflowY;return /(auto|scroll|overlay)/.test(r+s+p)?e:n(o(e))}function r(e){return 11===e?re:10===e?pe:re||pe}function p(e){if(!e)return document.documentElement;for(var o=r(10)?document.body:null,n=e.offsetParent||null;n===o&&e.nextElementSibling;)n=(e=e.nextElementSibling).offsetParent;var i=n&&n.nodeName;return i&&'BODY'!==i&&'HTML'!==i?-1!==['TH','TD','TABLE'].indexOf(n.nodeName)&&'static'===t(n,'position')?p(n):n:e?e.ownerDocument.documentElement:document.documentElement}function s(e){var t=e.nodeName;return'BODY'!==t&&('HTML'===t||p(e.firstElementChild)===e)}function d(e){return null===e.parentNode?e:d(e.parentNode)}function a(e,t){if(!e||!e.nodeType||!t||!t.nodeType)return document.documentElement;var o=e.compareDocumentPosition(t)&Node.DOCUMENT_POSITION_FOLLOWING,n=o?e:t,i=o?t:e,r=document.createRange();r.setStart(n,0),r.setEnd(i,0);var l=r.commonAncestorContainer;if(e!==l&&t!==l||n.contains(i))return s(l)?l:p(l);var f=d(e);return f.host?a(f.host,t):a(e,d(t).host)}function l(e){var t=1=o.clientWidth&&n>=o.clientHeight}),l=0a[e]&&!t.escapeWithReference&&(n=Q(f[o],a[e]-('right'===e?f.width:f.height))),ae({},o,n)}};return l.forEach(function(e){var t=-1===['left','top'].indexOf(e)?'secondary':'primary';f=le({},f,m[t](e))}),e.offsets.popper=f,e},priority:['left','right','top','bottom'],padding:5,boundariesElement:'scrollParent'},keepTogether:{order:400,enabled:!0,fn:function(e){var t=e.offsets,o=t.popper,n=t.reference,i=e.placement.split('-')[0],r=$,p=-1!==['top','bottom'].indexOf(i),s=p?'right':'bottom',d=p?'left':'top',a=p?'width':'height';return o[s]r(n[s])&&(e.offsets.popper[d]=r(n[s])),e}},arrow:{order:500,enabled:!0,fn:function(e,o){var n;if(!q(e.instance.modifiers,'arrow','keepTogether'))return e;var i=o.element;if('string'==typeof i){if(i=e.instance.popper.querySelector(i),!i)return e;}else if(!e.instance.popper.contains(i))return console.warn('WARNING: `arrow.element` must be child of its popper element!'),e;var r=e.placement.split('-')[0],p=e.offsets,s=p.popper,d=p.reference,a=-1!==['left','right'].indexOf(r),l=a?'height':'width',f=a?'Top':'Left',m=f.toLowerCase(),h=a?'left':'top',c=a?'bottom':'right',u=S(i)[l];d[c]-us[c]&&(e.offsets.popper[m]+=d[m]+u-s[c]),e.offsets.popper=g(e.offsets.popper);var b=d[m]+d[l]/2-u/2,y=t(e.instance.popper),w=parseFloat(y['margin'+f],10),E=parseFloat(y['border'+f+'Width'],10),v=b-e.offsets.popper[m]-w-E;return v=J(Q(s[l]-u,v),0),e.arrowElement=i,e.offsets.arrow=(n={},ae(n,m,Z(v)),ae(n,h,''),n),e},element:'[x-arrow]'},flip:{order:600,enabled:!0,fn:function(e,t){if(W(e.instance.modifiers,'inner'))return e;if(e.flipped&&e.placement===e.originalPlacement)return e;var o=v(e.instance.popper,e.instance.reference,t.padding,t.boundariesElement,e.positionFixed),n=e.placement.split('-')[0],i=T(n),r=e.placement.split('-')[1]||'',p=[];switch(t.behavior){case he.FLIP:p=[n,i];break;case he.CLOCKWISE:p=G(n);break;case he.COUNTERCLOCKWISE:p=G(n,!0);break;default:p=t.behavior;}return p.forEach(function(s,d){if(n!==s||p.length===d+1)return e;n=e.placement.split('-')[0],i=T(n);var a=e.offsets.popper,l=e.offsets.reference,f=$,m='left'===n&&f(a.right)>f(l.left)||'right'===n&&f(a.left)f(l.top)||'bottom'===n&&f(a.top)f(o.right),g=f(a.top)f(o.bottom),b='left'===n&&h||'right'===n&&c||'top'===n&&g||'bottom'===n&&u,y=-1!==['top','bottom'].indexOf(n),w=!!t.flipVariations&&(y&&'start'===r&&h||y&&'end'===r&&c||!y&&'start'===r&&g||!y&&'end'===r&&u);(m||b||w)&&(e.flipped=!0,(m||b)&&(n=p[d+1]),w&&(r=K(r)),e.placement=n+(r?'-'+r:''),e.offsets.popper=le({},e.offsets.popper,D(e.instance.popper,e.offsets.reference,e.placement)),e=P(e.instance.modifiers,e,'flip'))}),e},behavior:'flip',padding:5,boundariesElement:'viewport'},inner:{order:700,enabled:!1,fn:function(e){var t=e.placement,o=t.split('-')[0],n=e.offsets,i=n.popper,r=n.reference,p=-1!==['left','right'].indexOf(o),s=-1===['top','left'].indexOf(o);return i[p?'left':'top']=r[o]-(s?i[p?'width':'height']:0),e.placement=T(t),e.offsets.popper=g(i),e}},hide:{order:800,enabled:!0,fn:function(e){if(!q(e.instance.modifiers,'hide','preventOverflow'))return e;var t=e.offsets.reference,o=C(e.instance.modifiers,function(e){return'preventOverflow'===e.name}).boundaries;if(t.bottomo.right||t.top>o.bottom||t.right=o.clientWidth&&n>=o.clientHeight}),l=0a[e]&&!t.escapeWithReference&&(n=Q(f[o],a[e]-('right'===e?f.width:f.height))),ae({},o,n)}};return l.forEach(function(e){var t=-1===['left','top'].indexOf(e)?'secondary':'primary';f=le({},f,m[t](e))}),e.offsets.popper=f,e},priority:['left','right','top','bottom'],padding:5,boundariesElement:'scrollParent'},keepTogether:{order:400,enabled:!0,fn:function(e){var t=e.offsets,o=t.popper,n=t.reference,i=e.placement.split('-')[0],r=$,p=-1!==['top','bottom'].indexOf(i),s=p?'right':'bottom',d=p?'left':'top',a=p?'width':'height';return o[s]r(n[s])&&(e.offsets.popper[d]=r(n[s])),e}},arrow:{order:500,enabled:!0,fn:function(e,o){var n;if(!q(e.instance.modifiers,'arrow','keepTogether'))return e;var i=o.element;if('string'==typeof i){if(i=e.instance.popper.querySelector(i),!i)return e;}else if(!e.instance.popper.contains(i))return console.warn('WARNING: `arrow.element` must be child of its popper element!'),e;var r=e.placement.split('-')[0],p=e.offsets,s=p.popper,d=p.reference,a=-1!==['left','right'].indexOf(r),l=a?'height':'width',f=a?'Top':'Left',m=f.toLowerCase(),h=a?'left':'top',c=a?'bottom':'right',u=S(i)[l];d[c]-us[c]&&(e.offsets.popper[m]+=d[m]+u-s[c]),e.offsets.popper=g(e.offsets.popper);var b=d[m]+d[l]/2-u/2,y=t(e.instance.popper),w=parseFloat(y['margin'+f],10),E=parseFloat(y['border'+f+'Width'],10),v=b-e.offsets.popper[m]-w-E;return v=J(Q(s[l]-u,v),0),e.arrowElement=i,e.offsets.arrow=(n={},ae(n,m,Z(v)),ae(n,h,''),n),e},element:'[x-arrow]'},flip:{order:600,enabled:!0,fn:function(e,t){if(W(e.instance.modifiers,'inner'))return e;if(e.flipped&&e.placement===e.originalPlacement)return e;var o=v(e.instance.popper,e.instance.reference,t.padding,t.boundariesElement,e.positionFixed),n=e.placement.split('-')[0],i=T(n),r=e.placement.split('-')[1]||'',p=[];switch(t.behavior){case he.FLIP:p=[n,i];break;case he.CLOCKWISE:p=G(n);break;case he.COUNTERCLOCKWISE:p=G(n,!0);break;default:p=t.behavior;}return p.forEach(function(s,d){if(n!==s||p.length===d+1)return e;n=e.placement.split('-')[0],i=T(n);var a=e.offsets.popper,l=e.offsets.reference,f=$,m='left'===n&&f(a.right)>f(l.left)||'right'===n&&f(a.left)f(l.top)||'bottom'===n&&f(a.top)f(o.right),g=f(a.top)f(o.bottom),b='left'===n&&h||'right'===n&&c||'top'===n&&g||'bottom'===n&&u,y=-1!==['top','bottom'].indexOf(n),w=!!t.flipVariations&&(y&&'start'===r&&h||y&&'end'===r&&c||!y&&'start'===r&&g||!y&&'end'===r&&u);(m||b||w)&&(e.flipped=!0,(m||b)&&(n=p[d+1]),w&&(r=K(r)),e.placement=n+(r?'-'+r:''),e.offsets.popper=le({},e.offsets.popper,D(e.instance.popper,e.offsets.reference,e.placement)),e=P(e.instance.modifiers,e,'flip'))}),e},behavior:'flip',padding:5,boundariesElement:'viewport'},inner:{order:700,enabled:!1,fn:function(e){var t=e.placement,o=t.split('-')[0],n=e.offsets,i=n.popper,r=n.reference,p=-1!==['left','right'].indexOf(o),s=-1===['top','left'].indexOf(o);return i[p?'left':'top']=r[o]-(s?i[p?'width':'height']:0),e.placement=T(t),e.offsets.popper=g(i),e}},hide:{order:800,enabled:!0,fn:function(e){if(!q(e.instance.modifiers,'hide','preventOverflow'))return e;var t=e.offsets.reference,o=C(e.instance.modifiers,function(e){return'preventOverflow'===e.name}).boundaries;if(t.bottomo.right||t.top>o.bottom||t.right res.json())
- .then(res => {
- this.message.title = "Actualizaci贸n";
- this.message.text = res.error ?? res.success;
- this.message.type = res.error ? 'danger' : 'success';
- this.message.timestamp = new Date().toLocaleTimeString();
- });
- },
- async mounted() {
- this.carreras = await fetch('action/carrera.php').then(res => res.json());
- this.niveles = await fetch('action/nivel.php').then(res => res.json());
- // group by facultad_id
- const carreras = this.carreras.reduce((acc, cur) => {
- const { facultad_nombre } = cur;
- if (!acc[facultad_nombre]) {
- acc[facultad_nombre] = [];
- }
- acc[facultad_nombre].push(cur);
- return acc;
- }, {});
- this.carreras = Object.entries(carreras).map(([facultad_nombre, carreras]) => ({
- facultad_nombre: facultad_nombre,
- carreras
- }));
- }
-}).mount('#app');
+import { createApp } from 'https://unpkg.com/petite-vue?module';
+const app = createApp({
+ carreras: [],
+ niveles: [],
+ message: {},
+ async setNivel(carrera, nivel) {
+ if (carrera.nivel_id === nivel.nivel_id) {
+ return;
+ }
+ carrera.nivel_id = nivel.nivel_id;
+ carrera.nivel_nombre = nivel.nivel_nombre;
+ await fetch('action/carrera.php', {
+ method: 'PUT',
+ body: JSON.stringify({
+ carrera_id: carrera.carrera_id,
+ nivel_id: nivel.nivel_id
+ })
+ })
+ .then(res => res.json())
+ .then(res => {
+ this.message.title = "Actualizaci贸n";
+ this.message.text = res.error ?? res.success;
+ this.message.type = res.error ? 'danger' : 'success';
+ this.message.timestamp = new Date().toLocaleTimeString();
+ });
+ },
+ async mounted() {
+ this.carreras = await fetch('action/carrera.php').then(res => res.json());
+ this.niveles = await fetch('action/nivel.php').then(res => res.json());
+ // group by facultad_id
+ const carreras = this.carreras.reduce((acc, cur) => {
+ const { facultad_nombre } = cur;
+ if (!acc[facultad_nombre]) {
+ acc[facultad_nombre] = [];
+ }
+ acc[facultad_nombre].push(cur);
+ return acc;
+ }, {});
+ this.carreras = Object.entries(carreras).map(([facultad_nombre, carreras]) => ({
+ facultad_nombre: facultad_nombre,
+ carreras
+ }));
+ }
+}).mount('#app');
diff --git a/js/client.js b/js/client.js
index 236d201..9087072 100644
--- a/js/client.js
+++ b/js/client.js
@@ -1,120 +1,120 @@
-// @ts-ignore Import module
-import { createApp, reactive } from 'https://unpkg.com/petite-vue?module';
-const webServices = {
- getPeriodosV1: async () => {
- try {
- const response = await fetch('periodos.v1.php');
- return await response.json();
- }
- catch (error) {
- console.log(error);
- return [];
- }
- },
- getPeriodosV2: async () => {
- try {
- const response = await fetch('periodos.v2.php');
- return await response.json();
- }
- catch (error) {
- console.log(error);
- return [];
- }
- }
-};
-const store = reactive({
- periodosV1: [],
- periodosV2: [],
- errors: [],
- fechas(idPeriodo) {
- const periodo = this.periodosV2.find((periodo) => periodo.IdPeriodo === idPeriodo);
- return {
- inicio: periodo ? periodo.FechaInicio : '',
- fin: periodo ? periodo.FechaFin : ''
- };
- },
- periodov1(idPeriodo) {
- return this.periodosV1.find((periodo) => periodo.IdPeriodo === idPeriodo);
- },
- periodov2(idPeriodo) {
- return this.periodosV2.filter((periodo) => periodo.IdPeriodo === idPeriodo);
- },
- async addPeriodo(periodo) {
- try {
- const result = await fetch('backend/periodos.php', {
- method: 'POST',
- body: JSON.stringify({
- ...periodo,
- ...this.fechas(periodo.IdPeriodo)
- }),
- headers: {
- 'Content-Type': 'application/json'
- }
- }).then((response) => response.json());
- if (result.success) {
- this.periodosV1 = this.periodosV1.map((periodoV1) => {
- if (periodoV1.IdPeriodo === periodo.IdPeriodo) {
- periodoV1.in_db = true;
- }
- return periodoV1;
- });
- return result;
- }
- else {
- this.errors.push(result.message);
- }
- }
- catch (error) {
- this.errors.push(error);
- }
- },
- async addCarreras(idPeriodo) {
- try {
- const periodoV1 = this.periodov1(idPeriodo);
- const periodoV2 = this.periodov2(idPeriodo);
- const data = periodoV2.map(({ ClaveCarrera, NombreCarrera }) => ({
- ClaveCarrera: ClaveCarrera,
- NombreCarrera: NombreCarrera,
- IdNivel: periodoV1.IdNivel,
- }));
- const result = await fetch('backend/carreras.php', {
- method: 'POST',
- body: JSON.stringify(data),
- headers: {
- 'Content-Type': 'application/json'
- }
- }).then((response) => response.json());
- if (result.success) {
- await webServices.getPeriodosV1().then((periodosV1) => {
- this.periodosV1 = periodosV1;
- });
- await webServices.getPeriodosV2().then((periodosV2) => {
- this.periodosV2 = periodosV2;
- });
- }
- }
- catch (error) {
- this.errors.push(error);
- }
- }
-});
-createApp({
- store,
- info(IdPeriodo) {
- const periodo = store.periodosV2.find((periodo) => periodo.IdPeriodo === IdPeriodo &&
- periodo.FechaInicio != '' && periodo.FechaFin != '');
- return periodo;
- },
- complete(IdPeriodo) {
- const info = this.info(IdPeriodo);
- return info !== undefined;
- },
- mounted: async () => {
- await webServices.getPeriodosV1().then((periodosV1) => {
- store.periodosV1 = periodosV1;
- });
- await webServices.getPeriodosV2().then((periodosV2) => {
- store.periodosV2 = periodosV2;
- });
- }
-}).mount();
+// @ts-ignore Import module
+import { createApp, reactive } from 'https://unpkg.com/petite-vue?module';
+const webServices = {
+ getPeriodosV1: async () => {
+ try {
+ const response = await fetch('periodos.v1.php');
+ return await response.json();
+ }
+ catch (error) {
+ console.log(error);
+ return [];
+ }
+ },
+ getPeriodosV2: async () => {
+ try {
+ const response = await fetch('periodos.v2.php');
+ return await response.json();
+ }
+ catch (error) {
+ console.log(error);
+ return [];
+ }
+ }
+};
+const store = reactive({
+ periodosV1: [],
+ periodosV2: [],
+ errors: [],
+ fechas(idPeriodo) {
+ const periodo = this.periodosV2.find((periodo) => periodo.IdPeriodo === idPeriodo);
+ return {
+ inicio: periodo ? periodo.FechaInicio : '',
+ fin: periodo ? periodo.FechaFin : ''
+ };
+ },
+ periodov1(idPeriodo) {
+ return this.periodosV1.find((periodo) => periodo.IdPeriodo === idPeriodo);
+ },
+ periodov2(idPeriodo) {
+ return this.periodosV2.filter((periodo) => periodo.IdPeriodo === idPeriodo);
+ },
+ async addPeriodo(periodo) {
+ try {
+ const result = await fetch('backend/periodos.php', {
+ method: 'POST',
+ body: JSON.stringify({
+ ...periodo,
+ ...this.fechas(periodo.IdPeriodo)
+ }),
+ headers: {
+ 'Content-Type': 'application/json'
+ }
+ }).then((response) => response.json());
+ if (result.success) {
+ this.periodosV1 = this.periodosV1.map((periodoV1) => {
+ if (periodoV1.IdPeriodo === periodo.IdPeriodo) {
+ periodoV1.in_db = true;
+ }
+ return periodoV1;
+ });
+ return result;
+ }
+ else {
+ this.errors.push(result.message);
+ }
+ }
+ catch (error) {
+ this.errors.push(error);
+ }
+ },
+ async addCarreras(idPeriodo) {
+ try {
+ const periodoV1 = this.periodov1(idPeriodo);
+ const periodoV2 = this.periodov2(idPeriodo);
+ const data = periodoV2.map(({ ClaveCarrera, NombreCarrera }) => ({
+ ClaveCarrera: ClaveCarrera,
+ NombreCarrera: NombreCarrera,
+ IdNivel: periodoV1.IdNivel,
+ }));
+ const result = await fetch('backend/carreras.php', {
+ method: 'POST',
+ body: JSON.stringify(data),
+ headers: {
+ 'Content-Type': 'application/json'
+ }
+ }).then((response) => response.json());
+ if (result.success) {
+ await webServices.getPeriodosV1().then((periodosV1) => {
+ this.periodosV1 = periodosV1;
+ });
+ await webServices.getPeriodosV2().then((periodosV2) => {
+ this.periodosV2 = periodosV2;
+ });
+ }
+ }
+ catch (error) {
+ this.errors.push(error);
+ }
+ }
+});
+createApp({
+ store,
+ info(IdPeriodo) {
+ const periodo = store.periodosV2.find((periodo) => periodo.IdPeriodo === IdPeriodo &&
+ periodo.FechaInicio != '' && periodo.FechaFin != '');
+ return periodo;
+ },
+ complete(IdPeriodo) {
+ const info = this.info(IdPeriodo);
+ return info !== undefined;
+ },
+ mounted: async () => {
+ await webServices.getPeriodosV1().then((periodosV1) => {
+ store.periodosV1 = periodosV1;
+ });
+ await webServices.getPeriodosV2().then((periodosV2) => {
+ store.periodosV2 = periodosV2;
+ });
+ }
+}).mount();
diff --git a/js/clockpicker.js b/js/clockpicker.js
index e9ed3f9..58abf55 100644
--- a/js/clockpicker.js
+++ b/js/clockpicker.js
@@ -1,731 +1,731 @@
-/*!
- * ClockPicker v{package.version} (http://weareoutman.github.io/clockpicker/)
- * Copyright 2014 Wang Shenwei.
- * Licensed under MIT (https://github.com/weareoutman/clockpicker/blob/gh-pages/LICENSE)
- */
-
-;(function(){
- var $ = window.jQuery,
- $win = $(window),
- $doc = $(document),
- $body;
-
- // Can I use inline svg ?
- var svgNS = 'http://www.w3.org/2000/svg',
- svgSupported = 'SVGAngle' in window && (function(){
- var supported,
- el = document.createElement('div');
- el.innerHTML = ' ';
- supported = (el.firstChild && el.firstChild.namespaceURI) == svgNS;
- el.innerHTML = '';
- return supported;
- })();
-
- // Can I use transition ?
- var transitionSupported = (function(){
- var style = document.createElement('div').style;
- return 'transition' in style ||
- 'WebkitTransition' in style ||
- 'MozTransition' in style ||
- 'msTransition' in style ||
- 'OTransition' in style;
- })();
-
- // Listen touch events in touch screen device, instead of mouse events in desktop.
- var touchSupported = 'ontouchstart' in window,
- mousedownEvent = 'mousedown' + ( touchSupported ? ' touchstart' : ''),
- mousemoveEvent = 'mousemove.clockpicker' + ( touchSupported ? ' touchmove.clockpicker' : ''),
- mouseupEvent = 'mouseup.clockpicker' + ( touchSupported ? ' touchend.clockpicker' : '');
-
- // Vibrate the device if supported
- var vibrate = navigator.vibrate ? 'vibrate' : navigator.webkitVibrate ? 'webkitVibrate' : null;
-
- function createSvgElement(name) {
- return document.createElementNS(svgNS, name);
- }
-
- function leadingZero(num) {
- return (num < 10 ? '0' : '') + num;
- }
-
- // Get a unique id
- var idCounter = 0;
- function uniqueId(prefix) {
- var id = ++idCounter + '';
- return prefix ? prefix + id : id;
- }
-
- // Clock size
- var dialRadius = 100,
- outerRadius = 80,
- // innerRadius = 80 on 12 hour clock
- innerRadius = 54,
- tickRadius = 13,
- diameter = dialRadius * 2,
- duration = transitionSupported ? 350 : 1;
-
- // Popover template
- var tpl = [
- '',
- '
',
- '
',
- ' ',
- ' : ',
- ' ',
- ' ',
- '
',
- '
',
- '
'
- ].join('');
-
- // ClockPicker
- function ClockPicker(element, options) {
- var popover = $(tpl),
- plate = popover.find('.clockpicker-plate'),
- hoursView = popover.find('.clockpicker-hours'),
- minutesView = popover.find('.clockpicker-minutes'),
- amPmBlock = popover.find('.clockpicker-am-pm-block'),
- isInput = element.prop('tagName') === 'INPUT',
- input = isInput ? element : element.find('input'),
- addon = element.find('.input-group-addon'),
- self = this,
- timer;
-
- this.id = uniqueId('cp');
- this.element = element;
- this.options = options;
- this.isAppended = false;
- this.isShown = false;
- this.currentView = 'hours';
- this.isInput = isInput;
- this.input = input;
- this.addon = addon;
- this.popover = popover;
- this.plate = plate;
- this.hoursView = hoursView;
- this.minutesView = minutesView;
- this.amPmBlock = amPmBlock;
- this.spanHours = popover.find('.clockpicker-span-hours');
- this.spanMinutes = popover.find('.clockpicker-span-minutes');
- this.spanAmPm = popover.find('.clockpicker-span-am-pm');
- this.amOrPm = "PM";
-
- // Setup for for 12 hour clock if option is selected
- if (options.twelvehour) {
-
- var amPmButtonsTemplate = ['',
- '',
- 'AM ',
- '',
- 'PM ',
- '
'].join('');
-
- var amPmButtons = $(amPmButtonsTemplate);
- //amPmButtons.appendTo(plate);
-
- ////Not working b/c they are not shown when this runs
- //$('clockpicker-am-button')
- // .on("click", function() {
- // self.amOrPm = "AM";
- // $('.clockpicker-span-am-pm').empty().append('AM');
- // });
- //
- //$('clockpicker-pm-button')
- // .on("click", function() {
- // self.amOrPm = "PM";
- // $('.clockpicker-span-am-pm').empty().append('PM');
- // });
-
- $('' + "AM" + ' ')
- .on("click", function() {
- self.amOrPm = "AM";
- $('.clockpicker-span-am-pm').empty().append('AM');
- }).appendTo(this.amPmBlock);
-
-
- $('' + "PM" + ' ')
- .on("click", function() {
- self.amOrPm = 'PM';
- $('.clockpicker-span-am-pm').empty().append('PM');
- }).appendTo(this.amPmBlock);
-
- }
-
- if (! options.autoclose) {
- // If autoclose is not setted, append a button
- $('' + options.donetext + ' ')
- .click($.proxy(this.done, this))
- .appendTo(popover);
- }
-
- // Placement and arrow align - make sure they make sense.
- if ((options.placement === 'top' || options.placement === 'bottom') && (options.align === 'top' || options.align === 'bottom')) options.align = 'left';
- if ((options.placement === 'left' || options.placement === 'right') && (options.align === 'left' || options.align === 'right')) options.align = 'top';
-
- popover.addClass(options.placement);
- popover.addClass('clockpicker-align-' + options.align);
-
- this.spanHours.click($.proxy(this.toggleView, this, 'hours'));
- this.spanMinutes.click($.proxy(this.toggleView, this, 'minutes'));
-
- // Show or toggle
- input.on('focus.clockpicker click.clockpicker', $.proxy(this.show, this));
- addon.on('click.clockpicker', $.proxy(this.toggle, this));
-
- // Build ticks
- var tickTpl = $('
'),
- i, tick, radian, radius;
-
- // Hours view
- if (options.twelvehour) {
- for (i = 1; i < 13; i += 1) {
- tick = tickTpl.clone();
- radian = i / 6 * Math.PI;
- radius = outerRadius;
- //tick.css('font-size', '120%');
- tick.css({
- left: dialRadius + Math.sin(radian) * radius - tickRadius,
- top: dialRadius - Math.cos(radian) * radius - tickRadius
- });
- tick.html(i === 0 ? '00' : i);
- hoursView.append(tick);
- tick.on(mousedownEvent, mousedown);
- }
- } else {
- for (i = 0; i < 24; i += 1) {
- tick = tickTpl.clone();
- radian = i / 6 * Math.PI;
- var inner = i > 0 && i < 13;
- radius = inner ? innerRadius : outerRadius;
- tick.css({
- left: dialRadius + Math.sin(radian) * radius - tickRadius,
- top: dialRadius - Math.cos(radian) * radius - tickRadius
- });
- /*if (inner) {
- tick.css('font-size', '120%');
- }*/
- tick.html(i === 0 ? '00' : i);
- hoursView.append(tick);
- tick.on(mousedownEvent, mousedown);
- }
- }
-
- // Minutes view
- for (i = 0; i < 60; i += 5) {
- tick = tickTpl.clone();
- radian = i / 30 * Math.PI;
- tick.css({
- left: dialRadius + Math.sin(radian) * outerRadius - tickRadius,
- top: dialRadius - Math.cos(radian) * outerRadius - tickRadius
- });
- //tick.css('font-size', '120%');
- tick.html(leadingZero(i));
- minutesView.append(tick);
- tick.on(mousedownEvent, mousedown);
- }
-
- // Clicking on minutes view space
- plate.on(mousedownEvent, function(e){
- if ($(e.target).closest('.clockpicker-tick').length === 0) {
- mousedown(e, true);
- }
- });
-
- // Mousedown or touchstart
- function mousedown(e, space) {
- var offset = plate.offset(),
- isTouch = /^touch/.test(e.type),
- x0 = offset.left + dialRadius,
- y0 = offset.top + dialRadius,
- dx = (isTouch ? e.originalEvent.touches[0] : e).pageX - x0,
- dy = (isTouch ? e.originalEvent.touches[0] : e).pageY - y0,
- z = Math.sqrt(dx * dx + dy * dy),
- moved = false;
-
- // When clicking on minutes view space, check the mouse position
- if (space && (z < outerRadius - tickRadius || z > outerRadius + tickRadius)) {
- return;
- }
- e.preventDefault();
-
- // Set cursor style of body after 200ms
- var movingTimer = setTimeout(function(){
- $body.addClass('clockpicker-moving');
- }, 200);
-
- // Place the canvas to top
- if (svgSupported) {
- plate.append(self.canvas);
- }
-
- // Clock
- self.setHand(dx, dy, ! space, true);
-
- // Mousemove on document
- $doc.off(mousemoveEvent).on(mousemoveEvent, function(e){
- e.preventDefault();
- var isTouch = /^touch/.test(e.type),
- x = (isTouch ? e.originalEvent.touches[0] : e).pageX - x0,
- y = (isTouch ? e.originalEvent.touches[0] : e).pageY - y0;
- if (! moved && x === dx && y === dy) {
- // Clicking in chrome on windows will trigger a mousemove event
- return;
- }
- moved = true;
- self.setHand(x, y, false, true);
- });
-
- // Mouseup on document
- $doc.off(mouseupEvent).on(mouseupEvent, function(e){
- $doc.off(mouseupEvent);
- e.preventDefault();
- var isTouch = /^touch/.test(e.type),
- x = (isTouch ? e.originalEvent.changedTouches[0] : e).pageX - x0,
- y = (isTouch ? e.originalEvent.changedTouches[0] : e).pageY - y0;
- if ((space || moved) && x === dx && y === dy) {
- self.setHand(x, y);
- }
- if (self.currentView === 'hours') {
- self.toggleView('minutes', duration / 2);
- } else {
- if (options.autoclose) {
- self.minutesView.addClass('clockpicker-dial-out');
- setTimeout(function(){
- self.done();
- }, duration / 2);
- }
- }
- plate.prepend(canvas);
-
- // Reset cursor style of body
- clearTimeout(movingTimer);
- $body.removeClass('clockpicker-moving');
-
- // Unbind mousemove event
- $doc.off(mousemoveEvent);
- });
- }
-
- if (svgSupported) {
- // Draw clock hands and others
- var canvas = popover.find('.clockpicker-canvas'),
- svg = createSvgElement('svg');
- svg.setAttribute('class', 'clockpicker-svg');
- svg.setAttribute('width', diameter);
- svg.setAttribute('height', diameter);
- var g = createSvgElement('g');
- g.setAttribute('transform', 'translate(' + dialRadius + ',' + dialRadius + ')');
- var bearing = createSvgElement('circle');
- bearing.setAttribute('class', 'clockpicker-canvas-bearing');
- bearing.setAttribute('cx', 0);
- bearing.setAttribute('cy', 0);
- bearing.setAttribute('r', 2);
- var hand = createSvgElement('line');
- hand.setAttribute('x1', 0);
- hand.setAttribute('y1', 0);
- var bg = createSvgElement('circle');
- bg.setAttribute('class', 'clockpicker-canvas-bg');
- bg.setAttribute('r', tickRadius);
- var fg = createSvgElement('circle');
- fg.setAttribute('class', 'clockpicker-canvas-fg');
- fg.setAttribute('r', 3.5);
- g.appendChild(hand);
- g.appendChild(bg);
- g.appendChild(fg);
- g.appendChild(bearing);
- svg.appendChild(g);
- canvas.append(svg);
-
- this.hand = hand;
- this.bg = bg;
- this.fg = fg;
- this.bearing = bearing;
- this.g = g;
- this.canvas = canvas;
- }
-
- raiseCallback(this.options.init);
- }
-
- function raiseCallback(callbackFunction) {
- if (callbackFunction && typeof callbackFunction === "function") {
- callbackFunction();
- }
- }
-
- // Default options
- ClockPicker.DEFAULTS = {
- 'default': '', // default time, 'now' or '13:14' e.g.
- fromnow: 0, // set default time to * milliseconds from now (using with default = 'now')
- placement: 'bottom', // clock popover placement
- align: 'left', // popover arrow align
- donetext: 'Aceptar', // done button text
- autoclose: false, // auto close when minute is selected
- twelvehour: false, // change to 12 hour AM/PM clock from 24 hour
- vibrate: true // vibrate the device when dragging clock hand
- };
-
- // Show or hide popover
- ClockPicker.prototype.toggle = function(){
- this[this.isShown ? 'hide' : 'show']();
- };
-
- // Set popover position
- ClockPicker.prototype.locate = function(){
- var element = this.element,
- popover = this.popover,
- offset = element.offset(),
- width = element.outerWidth(),
- height = element.outerHeight(),
- placement = this.options.placement,
- align = this.options.align,
- styles = {},
- self = this;
-
- popover.show();
-
- // Place the popover
- switch (placement) {
- case 'bottom':
- styles.top = offset.top + height;
- break;
- case 'right':
- styles.left = offset.left + width;
- break;
- case 'top':
- styles.top = offset.top - popover.outerHeight();
- break;
- case 'left':
- styles.left = offset.left - popover.outerWidth();
- break;
- }
-
- // Align the popover arrow
- switch (align) {
- case 'left':
- styles.left = offset.left;
- break;
- case 'right':
- styles.left = offset.left + width - popover.outerWidth();
- break;
- case 'top':
- styles.top = offset.top;
- break;
- case 'bottom':
- styles.top = offset.top + height - popover.outerHeight();
- break;
- }
-
- popover.css(styles);
- };
-
- // Show popover
- ClockPicker.prototype.show = function(e){
- // Not show again
- if (this.isShown) {
- return;
- }
-
- raiseCallback(this.options.beforeShow);
-
- var self = this;
-
- // Initialize
- if (! this.isAppended) {
- // Append popover to body
- $body = $(document.body).append(this.popover);
-
- // Reset position when resize
- $win.on('resize.clockpicker' + this.id, function(){
- if (self.isShown) {
- self.locate();
- }
- });
-
- this.isAppended = true;
- }
-
- // Get the time
- var value = ((this.input.prop('value') || this.options['default'] || '') + '').split(':');
- if (value[0] === 'now') {
- var now = new Date(+ new Date() + this.options.fromnow);
- value = [
- now.getHours(),
- now.getMinutes()
- ];
- }
- this.hours = + value[0] || 0;
- this.minutes = + value[1] || 0;
- this.spanHours.html(leadingZero(this.hours));
- this.spanMinutes.html(leadingZero(this.minutes));
-
- // Toggle to hours view
- this.toggleView('hours');
-
- // Set position
- this.locate();
-
- this.isShown = true;
-
- // Hide when clicking or tabbing on any element except the clock, input and addon
- $doc.on('click.clockpicker.' + this.id + ' focusin.clockpicker.' + this.id, function(e){
- var target = $(e.target);
- if (target.closest(self.popover).length === 0 &&
- target.closest(self.addon).length === 0 &&
- target.closest(self.input).length === 0) {
- self.hide();
- }
- });
-
- // Hide when ESC is pressed
- $doc.on('keyup.clockpicker.' + this.id, function(e){
- if (e.keyCode === 27) {
- self.hide();
- }
- });
-
- raiseCallback(this.options.afterShow);
- };
-
- // Hide popover
- ClockPicker.prototype.hide = function(){
- raiseCallback(this.options.beforeHide);
-
- this.isShown = false;
-
- // Unbinding events on document
- $doc.off('click.clockpicker.' + this.id + ' focusin.clockpicker.' + this.id);
- $doc.off('keyup.clockpicker.' + this.id);
-
- this.popover.hide();
-
- raiseCallback(this.options.afterHide);
- };
-
- // Toggle to hours or minutes view
- ClockPicker.prototype.toggleView = function(view, delay){
- var raiseAfterHourSelect = false;
- if (view === 'minutes' && $(this.hoursView).css("visibility") === "visible") {
- raiseCallback(this.options.beforeHourSelect);
- raiseAfterHourSelect = true;
- }
- var isHours = view === 'hours',
- nextView = isHours ? this.hoursView : this.minutesView,
- hideView = isHours ? this.minutesView : this.hoursView;
-
- this.currentView = view;
-
- this.spanHours.toggleClass('text-azul-ing', isHours);
- this.spanMinutes.toggleClass('text-azul-ing', ! isHours);
-
- // Let's make transitions
- hideView.addClass('clockpicker-dial-out');
- nextView.css('visibility', 'visible').removeClass('clockpicker-dial-out');
-
- // Reset clock hand
- this.resetClock(delay);
-
- // After transitions ended
- clearTimeout(this.toggleViewTimer);
- this.toggleViewTimer = setTimeout(function(){
- hideView.css('visibility', 'hidden');
- }, duration);
-
- if (raiseAfterHourSelect) {
- raiseCallback(this.options.afterHourSelect);
- }
- };
-
- // Reset clock hand
- ClockPicker.prototype.resetClock = function(delay){
- var view = this.currentView,
- value = this[view],
- isHours = view === 'hours',
- unit = Math.PI / (isHours ? 6 : 30),
- radian = value * unit,
- radius = isHours && value > 0 && value < 13 ? innerRadius : outerRadius,
- x = Math.sin(radian) * radius,
- y = - Math.cos(radian) * radius,
- self = this;
- if (svgSupported && delay) {
- self.canvas.addClass('clockpicker-canvas-out');
- setTimeout(function(){
- self.canvas.removeClass('clockpicker-canvas-out');
- self.setHand(x, y);
- }, delay);
- } else {
- this.setHand(x, y);
- }
- };
-
- // Set clock hand to (x, y)
- ClockPicker.prototype.setHand = function(x, y, roundBy5, dragging){
- var radian = Math.atan2(x, - y),
- isHours = this.currentView === 'hours',
- unit = Math.PI / (isHours || roundBy5 ? 6 : 30),
- z = Math.sqrt(x * x + y * y),
- options = this.options,
- inner = isHours && z < (outerRadius + innerRadius) / 2,
- radius = inner ? innerRadius : outerRadius,
- value;
-
- if (options.twelvehour) {
- radius = outerRadius;
- }
-
- // Radian should in range [0, 2PI]
- if (radian < 0) {
- radian = Math.PI * 2 + radian;
- }
-
- // Get the round value
- value = Math.round(radian / unit);
-
- // Get the round radian
- radian = value * unit;
-
- // Correct the hours or minutes
- if (options.twelvehour) {
- if (isHours) {
- if (value === 0) {
- value = 12;
- }
- } else {
- if (roundBy5) {
- value *= 5;
- }
- if (value === 60) {
- value = 0;
- }
- }
- } else {
- if (isHours) {
- if (value === 12) {
- value = 0;
- }
- value = inner ? (value === 0 ? 12 : value) : value === 0 ? 0 : value + 12;
- } else {
- if (roundBy5) {
- value *= 5;
- }
- if (value === 60) {
- value = 0;
- }
- }
- }
-
- // Once hours or minutes changed, vibrate the device
- if (this[this.currentView] !== value) {
- if (vibrate && this.options.vibrate) {
- // Do not vibrate too frequently
- if (! this.vibrateTimer) {
- navigator[vibrate](10);
- this.vibrateTimer = setTimeout($.proxy(function(){
- this.vibrateTimer = null;
- }, this), 100);
- }
- }
- }
-
- this[this.currentView] = value;
- this[isHours ? 'spanHours' : 'spanMinutes'].html(leadingZero(value));
-
- // If svg is not supported, just add an active class to the tick
- //if (! svgSupported) {
- this[isHours ? 'hoursView' : 'minutesView'].find('.clockpicker-tick').each(function(){
- var tick = $(this);
- tick.toggleClass('active', value === + tick.html());
- });
- //return;
- //}
-
-
-
- // Place clock hand at the top when dragging
- if (dragging || (! isHours && value % 5)) {
- this.g.insertBefore(this.hand, this.bearing);
- this.g.insertBefore(this.bg, this.fg);
- this.bg.setAttribute('class', 'clockpicker-canvas-bg clockpicker-canvas-bg-trans');
- } else {
- // Or place it at the bottom
- this.g.insertBefore(this.hand, this.bg);
- this.g.insertBefore(this.fg, this.bg);
- this.bg.setAttribute('class', 'clockpicker-canvas-bg');
- }
-
- // Set clock hand and others' position
- var cx = Math.sin(radian) * radius,
- cy = - Math.cos(radian) * radius;
- this.hand.setAttribute('x2', cx);
- this.hand.setAttribute('y2', cy);
- this.bg.setAttribute('cx', cx);
- this.bg.setAttribute('cy', cy);
- this.fg.setAttribute('cx', cx);
- this.fg.setAttribute('cy', cy);
- };
-
- // Hours and minutes are selected
- ClockPicker.prototype.done = function() {
- raiseCallback(this.options.beforeDone);
- this.hide();
- var last = this.input.prop('value'),
- value = leadingZero(this.hours) + ':' + leadingZero(this.minutes);
- if (this.options.twelvehour) {
- value = value + this.amOrPm;
- }
-
- this.input.prop('value', value);
- if (value !== last) {
- this.input.triggerHandler('change');
- if (! this.isInput) {
- this.element.trigger('change');
- }
- }
-
- if (this.options.autoclose) {
- this.input.trigger('blur');
- }
-
- raiseCallback(this.options.afterDone);
- };
-
- // Remove clockpicker from input
- ClockPicker.prototype.remove = function() {
- this.element.removeData('clockpicker');
- this.input.off('focus.clockpicker click.clockpicker');
- this.addon.off('click.clockpicker');
- if (this.isShown) {
- this.hide();
- }
- if (this.isAppended) {
- $win.off('resize.clockpicker' + this.id);
- this.popover.remove();
- }
- };
-
- // Extends $.fn.clockpicker
- $.fn.clockpicker = function(option){
- var args = Array.prototype.slice.call(arguments, 1);
- return this.each(function(){
- var $this = $(this),
- data = $this.data('clockpicker');
- if (! data) {
- var options = $.extend({}, ClockPicker.DEFAULTS, $this.data(), typeof option == 'object' && option);
- $this.data('clockpicker', new ClockPicker($this, options));
- } else {
- // Manual operatsions. show, hide, remove, e.g.
- if (typeof data[option] === 'function') {
- data[option].apply(data, args);
- }
- }
- });
- };
-}());
+/*!
+ * ClockPicker v{package.version} (http://weareoutman.github.io/clockpicker/)
+ * Copyright 2014 Wang Shenwei.
+ * Licensed under MIT (https://github.com/weareoutman/clockpicker/blob/gh-pages/LICENSE)
+ */
+
+;(function(){
+ var $ = window.jQuery,
+ $win = $(window),
+ $doc = $(document),
+ $body;
+
+ // Can I use inline svg ?
+ var svgNS = 'http://www.w3.org/2000/svg',
+ svgSupported = 'SVGAngle' in window && (function(){
+ var supported,
+ el = document.createElement('div');
+ el.innerHTML = ' ';
+ supported = (el.firstChild && el.firstChild.namespaceURI) == svgNS;
+ el.innerHTML = '';
+ return supported;
+ })();
+
+ // Can I use transition ?
+ var transitionSupported = (function(){
+ var style = document.createElement('div').style;
+ return 'transition' in style ||
+ 'WebkitTransition' in style ||
+ 'MozTransition' in style ||
+ 'msTransition' in style ||
+ 'OTransition' in style;
+ })();
+
+ // Listen touch events in touch screen device, instead of mouse events in desktop.
+ var touchSupported = 'ontouchstart' in window,
+ mousedownEvent = 'mousedown' + ( touchSupported ? ' touchstart' : ''),
+ mousemoveEvent = 'mousemove.clockpicker' + ( touchSupported ? ' touchmove.clockpicker' : ''),
+ mouseupEvent = 'mouseup.clockpicker' + ( touchSupported ? ' touchend.clockpicker' : '');
+
+ // Vibrate the device if supported
+ var vibrate = navigator.vibrate ? 'vibrate' : navigator.webkitVibrate ? 'webkitVibrate' : null;
+
+ function createSvgElement(name) {
+ return document.createElementNS(svgNS, name);
+ }
+
+ function leadingZero(num) {
+ return (num < 10 ? '0' : '') + num;
+ }
+
+ // Get a unique id
+ var idCounter = 0;
+ function uniqueId(prefix) {
+ var id = ++idCounter + '';
+ return prefix ? prefix + id : id;
+ }
+
+ // Clock size
+ var dialRadius = 100,
+ outerRadius = 80,
+ // innerRadius = 80 on 12 hour clock
+ innerRadius = 54,
+ tickRadius = 13,
+ diameter = dialRadius * 2,
+ duration = transitionSupported ? 350 : 1;
+
+ // Popover template
+ var tpl = [
+ '',
+ '
',
+ '
',
+ ' ',
+ ' : ',
+ ' ',
+ ' ',
+ '
',
+ '
',
+ '
'
+ ].join('');
+
+ // ClockPicker
+ function ClockPicker(element, options) {
+ var popover = $(tpl),
+ plate = popover.find('.clockpicker-plate'),
+ hoursView = popover.find('.clockpicker-hours'),
+ minutesView = popover.find('.clockpicker-minutes'),
+ amPmBlock = popover.find('.clockpicker-am-pm-block'),
+ isInput = element.prop('tagName') === 'INPUT',
+ input = isInput ? element : element.find('input'),
+ addon = element.find('.input-group-addon'),
+ self = this,
+ timer;
+
+ this.id = uniqueId('cp');
+ this.element = element;
+ this.options = options;
+ this.isAppended = false;
+ this.isShown = false;
+ this.currentView = 'hours';
+ this.isInput = isInput;
+ this.input = input;
+ this.addon = addon;
+ this.popover = popover;
+ this.plate = plate;
+ this.hoursView = hoursView;
+ this.minutesView = minutesView;
+ this.amPmBlock = amPmBlock;
+ this.spanHours = popover.find('.clockpicker-span-hours');
+ this.spanMinutes = popover.find('.clockpicker-span-minutes');
+ this.spanAmPm = popover.find('.clockpicker-span-am-pm');
+ this.amOrPm = "PM";
+
+ // Setup for for 12 hour clock if option is selected
+ if (options.twelvehour) {
+
+ var amPmButtonsTemplate = ['',
+ '',
+ 'AM ',
+ '',
+ 'PM ',
+ '
'].join('');
+
+ var amPmButtons = $(amPmButtonsTemplate);
+ //amPmButtons.appendTo(plate);
+
+ ////Not working b/c they are not shown when this runs
+ //$('clockpicker-am-button')
+ // .on("click", function() {
+ // self.amOrPm = "AM";
+ // $('.clockpicker-span-am-pm').empty().append('AM');
+ // });
+ //
+ //$('clockpicker-pm-button')
+ // .on("click", function() {
+ // self.amOrPm = "PM";
+ // $('.clockpicker-span-am-pm').empty().append('PM');
+ // });
+
+ $('' + "AM" + ' ')
+ .on("click", function() {
+ self.amOrPm = "AM";
+ $('.clockpicker-span-am-pm').empty().append('AM');
+ }).appendTo(this.amPmBlock);
+
+
+ $('' + "PM" + ' ')
+ .on("click", function() {
+ self.amOrPm = 'PM';
+ $('.clockpicker-span-am-pm').empty().append('PM');
+ }).appendTo(this.amPmBlock);
+
+ }
+
+ if (! options.autoclose) {
+ // If autoclose is not setted, append a button
+ $('' + options.donetext + ' ')
+ .click($.proxy(this.done, this))
+ .appendTo(popover);
+ }
+
+ // Placement and arrow align - make sure they make sense.
+ if ((options.placement === 'top' || options.placement === 'bottom') && (options.align === 'top' || options.align === 'bottom')) options.align = 'left';
+ if ((options.placement === 'left' || options.placement === 'right') && (options.align === 'left' || options.align === 'right')) options.align = 'top';
+
+ popover.addClass(options.placement);
+ popover.addClass('clockpicker-align-' + options.align);
+
+ this.spanHours.click($.proxy(this.toggleView, this, 'hours'));
+ this.spanMinutes.click($.proxy(this.toggleView, this, 'minutes'));
+
+ // Show or toggle
+ input.on('focus.clockpicker click.clockpicker', $.proxy(this.show, this));
+ addon.on('click.clockpicker', $.proxy(this.toggle, this));
+
+ // Build ticks
+ var tickTpl = $('
'),
+ i, tick, radian, radius;
+
+ // Hours view
+ if (options.twelvehour) {
+ for (i = 1; i < 13; i += 1) {
+ tick = tickTpl.clone();
+ radian = i / 6 * Math.PI;
+ radius = outerRadius;
+ //tick.css('font-size', '120%');
+ tick.css({
+ left: dialRadius + Math.sin(radian) * radius - tickRadius,
+ top: dialRadius - Math.cos(radian) * radius - tickRadius
+ });
+ tick.html(i === 0 ? '00' : i);
+ hoursView.append(tick);
+ tick.on(mousedownEvent, mousedown);
+ }
+ } else {
+ for (i = 0; i < 24; i += 1) {
+ tick = tickTpl.clone();
+ radian = i / 6 * Math.PI;
+ var inner = i > 0 && i < 13;
+ radius = inner ? innerRadius : outerRadius;
+ tick.css({
+ left: dialRadius + Math.sin(radian) * radius - tickRadius,
+ top: dialRadius - Math.cos(radian) * radius - tickRadius
+ });
+ /*if (inner) {
+ tick.css('font-size', '120%');
+ }*/
+ tick.html(i === 0 ? '00' : i);
+ hoursView.append(tick);
+ tick.on(mousedownEvent, mousedown);
+ }
+ }
+
+ // Minutes view
+ for (i = 0; i < 60; i += 5) {
+ tick = tickTpl.clone();
+ radian = i / 30 * Math.PI;
+ tick.css({
+ left: dialRadius + Math.sin(radian) * outerRadius - tickRadius,
+ top: dialRadius - Math.cos(radian) * outerRadius - tickRadius
+ });
+ //tick.css('font-size', '120%');
+ tick.html(leadingZero(i));
+ minutesView.append(tick);
+ tick.on(mousedownEvent, mousedown);
+ }
+
+ // Clicking on minutes view space
+ plate.on(mousedownEvent, function(e){
+ if ($(e.target).closest('.clockpicker-tick').length === 0) {
+ mousedown(e, true);
+ }
+ });
+
+ // Mousedown or touchstart
+ function mousedown(e, space) {
+ var offset = plate.offset(),
+ isTouch = /^touch/.test(e.type),
+ x0 = offset.left + dialRadius,
+ y0 = offset.top + dialRadius,
+ dx = (isTouch ? e.originalEvent.touches[0] : e).pageX - x0,
+ dy = (isTouch ? e.originalEvent.touches[0] : e).pageY - y0,
+ z = Math.sqrt(dx * dx + dy * dy),
+ moved = false;
+
+ // When clicking on minutes view space, check the mouse position
+ if (space && (z < outerRadius - tickRadius || z > outerRadius + tickRadius)) {
+ return;
+ }
+ e.preventDefault();
+
+ // Set cursor style of body after 200ms
+ var movingTimer = setTimeout(function(){
+ $body.addClass('clockpicker-moving');
+ }, 200);
+
+ // Place the canvas to top
+ if (svgSupported) {
+ plate.append(self.canvas);
+ }
+
+ // Clock
+ self.setHand(dx, dy, ! space, true);
+
+ // Mousemove on document
+ $doc.off(mousemoveEvent).on(mousemoveEvent, function(e){
+ e.preventDefault();
+ var isTouch = /^touch/.test(e.type),
+ x = (isTouch ? e.originalEvent.touches[0] : e).pageX - x0,
+ y = (isTouch ? e.originalEvent.touches[0] : e).pageY - y0;
+ if (! moved && x === dx && y === dy) {
+ // Clicking in chrome on windows will trigger a mousemove event
+ return;
+ }
+ moved = true;
+ self.setHand(x, y, false, true);
+ });
+
+ // Mouseup on document
+ $doc.off(mouseupEvent).on(mouseupEvent, function(e){
+ $doc.off(mouseupEvent);
+ e.preventDefault();
+ var isTouch = /^touch/.test(e.type),
+ x = (isTouch ? e.originalEvent.changedTouches[0] : e).pageX - x0,
+ y = (isTouch ? e.originalEvent.changedTouches[0] : e).pageY - y0;
+ if ((space || moved) && x === dx && y === dy) {
+ self.setHand(x, y);
+ }
+ if (self.currentView === 'hours') {
+ self.toggleView('minutes', duration / 2);
+ } else {
+ if (options.autoclose) {
+ self.minutesView.addClass('clockpicker-dial-out');
+ setTimeout(function(){
+ self.done();
+ }, duration / 2);
+ }
+ }
+ plate.prepend(canvas);
+
+ // Reset cursor style of body
+ clearTimeout(movingTimer);
+ $body.removeClass('clockpicker-moving');
+
+ // Unbind mousemove event
+ $doc.off(mousemoveEvent);
+ });
+ }
+
+ if (svgSupported) {
+ // Draw clock hands and others
+ var canvas = popover.find('.clockpicker-canvas'),
+ svg = createSvgElement('svg');
+ svg.setAttribute('class', 'clockpicker-svg');
+ svg.setAttribute('width', diameter);
+ svg.setAttribute('height', diameter);
+ var g = createSvgElement('g');
+ g.setAttribute('transform', 'translate(' + dialRadius + ',' + dialRadius + ')');
+ var bearing = createSvgElement('circle');
+ bearing.setAttribute('class', 'clockpicker-canvas-bearing');
+ bearing.setAttribute('cx', 0);
+ bearing.setAttribute('cy', 0);
+ bearing.setAttribute('r', 2);
+ var hand = createSvgElement('line');
+ hand.setAttribute('x1', 0);
+ hand.setAttribute('y1', 0);
+ var bg = createSvgElement('circle');
+ bg.setAttribute('class', 'clockpicker-canvas-bg');
+ bg.setAttribute('r', tickRadius);
+ var fg = createSvgElement('circle');
+ fg.setAttribute('class', 'clockpicker-canvas-fg');
+ fg.setAttribute('r', 3.5);
+ g.appendChild(hand);
+ g.appendChild(bg);
+ g.appendChild(fg);
+ g.appendChild(bearing);
+ svg.appendChild(g);
+ canvas.append(svg);
+
+ this.hand = hand;
+ this.bg = bg;
+ this.fg = fg;
+ this.bearing = bearing;
+ this.g = g;
+ this.canvas = canvas;
+ }
+
+ raiseCallback(this.options.init);
+ }
+
+ function raiseCallback(callbackFunction) {
+ if (callbackFunction && typeof callbackFunction === "function") {
+ callbackFunction();
+ }
+ }
+
+ // Default options
+ ClockPicker.DEFAULTS = {
+ 'default': '', // default time, 'now' or '13:14' e.g.
+ fromnow: 0, // set default time to * milliseconds from now (using with default = 'now')
+ placement: 'bottom', // clock popover placement
+ align: 'left', // popover arrow align
+ donetext: 'Aceptar', // done button text
+ autoclose: false, // auto close when minute is selected
+ twelvehour: false, // change to 12 hour AM/PM clock from 24 hour
+ vibrate: true // vibrate the device when dragging clock hand
+ };
+
+ // Show or hide popover
+ ClockPicker.prototype.toggle = function(){
+ this[this.isShown ? 'hide' : 'show']();
+ };
+
+ // Set popover position
+ ClockPicker.prototype.locate = function(){
+ var element = this.element,
+ popover = this.popover,
+ offset = element.offset(),
+ width = element.outerWidth(),
+ height = element.outerHeight(),
+ placement = this.options.placement,
+ align = this.options.align,
+ styles = {},
+ self = this;
+
+ popover.show();
+
+ // Place the popover
+ switch (placement) {
+ case 'bottom':
+ styles.top = offset.top + height;
+ break;
+ case 'right':
+ styles.left = offset.left + width;
+ break;
+ case 'top':
+ styles.top = offset.top - popover.outerHeight();
+ break;
+ case 'left':
+ styles.left = offset.left - popover.outerWidth();
+ break;
+ }
+
+ // Align the popover arrow
+ switch (align) {
+ case 'left':
+ styles.left = offset.left;
+ break;
+ case 'right':
+ styles.left = offset.left + width - popover.outerWidth();
+ break;
+ case 'top':
+ styles.top = offset.top;
+ break;
+ case 'bottom':
+ styles.top = offset.top + height - popover.outerHeight();
+ break;
+ }
+
+ popover.css(styles);
+ };
+
+ // Show popover
+ ClockPicker.prototype.show = function(e){
+ // Not show again
+ if (this.isShown) {
+ return;
+ }
+
+ raiseCallback(this.options.beforeShow);
+
+ var self = this;
+
+ // Initialize
+ if (! this.isAppended) {
+ // Append popover to body
+ $body = $(document.body).append(this.popover);
+
+ // Reset position when resize
+ $win.on('resize.clockpicker' + this.id, function(){
+ if (self.isShown) {
+ self.locate();
+ }
+ });
+
+ this.isAppended = true;
+ }
+
+ // Get the time
+ var value = ((this.input.prop('value') || this.options['default'] || '') + '').split(':');
+ if (value[0] === 'now') {
+ var now = new Date(+ new Date() + this.options.fromnow);
+ value = [
+ now.getHours(),
+ now.getMinutes()
+ ];
+ }
+ this.hours = + value[0] || 0;
+ this.minutes = + value[1] || 0;
+ this.spanHours.html(leadingZero(this.hours));
+ this.spanMinutes.html(leadingZero(this.minutes));
+
+ // Toggle to hours view
+ this.toggleView('hours');
+
+ // Set position
+ this.locate();
+
+ this.isShown = true;
+
+ // Hide when clicking or tabbing on any element except the clock, input and addon
+ $doc.on('click.clockpicker.' + this.id + ' focusin.clockpicker.' + this.id, function(e){
+ var target = $(e.target);
+ if (target.closest(self.popover).length === 0 &&
+ target.closest(self.addon).length === 0 &&
+ target.closest(self.input).length === 0) {
+ self.hide();
+ }
+ });
+
+ // Hide when ESC is pressed
+ $doc.on('keyup.clockpicker.' + this.id, function(e){
+ if (e.keyCode === 27) {
+ self.hide();
+ }
+ });
+
+ raiseCallback(this.options.afterShow);
+ };
+
+ // Hide popover
+ ClockPicker.prototype.hide = function(){
+ raiseCallback(this.options.beforeHide);
+
+ this.isShown = false;
+
+ // Unbinding events on document
+ $doc.off('click.clockpicker.' + this.id + ' focusin.clockpicker.' + this.id);
+ $doc.off('keyup.clockpicker.' + this.id);
+
+ this.popover.hide();
+
+ raiseCallback(this.options.afterHide);
+ };
+
+ // Toggle to hours or minutes view
+ ClockPicker.prototype.toggleView = function(view, delay){
+ var raiseAfterHourSelect = false;
+ if (view === 'minutes' && $(this.hoursView).css("visibility") === "visible") {
+ raiseCallback(this.options.beforeHourSelect);
+ raiseAfterHourSelect = true;
+ }
+ var isHours = view === 'hours',
+ nextView = isHours ? this.hoursView : this.minutesView,
+ hideView = isHours ? this.minutesView : this.hoursView;
+
+ this.currentView = view;
+
+ this.spanHours.toggleClass('text-azul-ing', isHours);
+ this.spanMinutes.toggleClass('text-azul-ing', ! isHours);
+
+ // Let's make transitions
+ hideView.addClass('clockpicker-dial-out');
+ nextView.css('visibility', 'visible').removeClass('clockpicker-dial-out');
+
+ // Reset clock hand
+ this.resetClock(delay);
+
+ // After transitions ended
+ clearTimeout(this.toggleViewTimer);
+ this.toggleViewTimer = setTimeout(function(){
+ hideView.css('visibility', 'hidden');
+ }, duration);
+
+ if (raiseAfterHourSelect) {
+ raiseCallback(this.options.afterHourSelect);
+ }
+ };
+
+ // Reset clock hand
+ ClockPicker.prototype.resetClock = function(delay){
+ var view = this.currentView,
+ value = this[view],
+ isHours = view === 'hours',
+ unit = Math.PI / (isHours ? 6 : 30),
+ radian = value * unit,
+ radius = isHours && value > 0 && value < 13 ? innerRadius : outerRadius,
+ x = Math.sin(radian) * radius,
+ y = - Math.cos(radian) * radius,
+ self = this;
+ if (svgSupported && delay) {
+ self.canvas.addClass('clockpicker-canvas-out');
+ setTimeout(function(){
+ self.canvas.removeClass('clockpicker-canvas-out');
+ self.setHand(x, y);
+ }, delay);
+ } else {
+ this.setHand(x, y);
+ }
+ };
+
+ // Set clock hand to (x, y)
+ ClockPicker.prototype.setHand = function(x, y, roundBy5, dragging){
+ var radian = Math.atan2(x, - y),
+ isHours = this.currentView === 'hours',
+ unit = Math.PI / (isHours || roundBy5 ? 6 : 30),
+ z = Math.sqrt(x * x + y * y),
+ options = this.options,
+ inner = isHours && z < (outerRadius + innerRadius) / 2,
+ radius = inner ? innerRadius : outerRadius,
+ value;
+
+ if (options.twelvehour) {
+ radius = outerRadius;
+ }
+
+ // Radian should in range [0, 2PI]
+ if (radian < 0) {
+ radian = Math.PI * 2 + radian;
+ }
+
+ // Get the round value
+ value = Math.round(radian / unit);
+
+ // Get the round radian
+ radian = value * unit;
+
+ // Correct the hours or minutes
+ if (options.twelvehour) {
+ if (isHours) {
+ if (value === 0) {
+ value = 12;
+ }
+ } else {
+ if (roundBy5) {
+ value *= 5;
+ }
+ if (value === 60) {
+ value = 0;
+ }
+ }
+ } else {
+ if (isHours) {
+ if (value === 12) {
+ value = 0;
+ }
+ value = inner ? (value === 0 ? 12 : value) : value === 0 ? 0 : value + 12;
+ } else {
+ if (roundBy5) {
+ value *= 5;
+ }
+ if (value === 60) {
+ value = 0;
+ }
+ }
+ }
+
+ // Once hours or minutes changed, vibrate the device
+ if (this[this.currentView] !== value) {
+ if (vibrate && this.options.vibrate) {
+ // Do not vibrate too frequently
+ if (! this.vibrateTimer) {
+ navigator[vibrate](10);
+ this.vibrateTimer = setTimeout($.proxy(function(){
+ this.vibrateTimer = null;
+ }, this), 100);
+ }
+ }
+ }
+
+ this[this.currentView] = value;
+ this[isHours ? 'spanHours' : 'spanMinutes'].html(leadingZero(value));
+
+ // If svg is not supported, just add an active class to the tick
+ //if (! svgSupported) {
+ this[isHours ? 'hoursView' : 'minutesView'].find('.clockpicker-tick').each(function(){
+ var tick = $(this);
+ tick.toggleClass('active', value === + tick.html());
+ });
+ //return;
+ //}
+
+
+
+ // Place clock hand at the top when dragging
+ if (dragging || (! isHours && value % 5)) {
+ this.g.insertBefore(this.hand, this.bearing);
+ this.g.insertBefore(this.bg, this.fg);
+ this.bg.setAttribute('class', 'clockpicker-canvas-bg clockpicker-canvas-bg-trans');
+ } else {
+ // Or place it at the bottom
+ this.g.insertBefore(this.hand, this.bg);
+ this.g.insertBefore(this.fg, this.bg);
+ this.bg.setAttribute('class', 'clockpicker-canvas-bg');
+ }
+
+ // Set clock hand and others' position
+ var cx = Math.sin(radian) * radius,
+ cy = - Math.cos(radian) * radius;
+ this.hand.setAttribute('x2', cx);
+ this.hand.setAttribute('y2', cy);
+ this.bg.setAttribute('cx', cx);
+ this.bg.setAttribute('cy', cy);
+ this.fg.setAttribute('cx', cx);
+ this.fg.setAttribute('cy', cy);
+ };
+
+ // Hours and minutes are selected
+ ClockPicker.prototype.done = function() {
+ raiseCallback(this.options.beforeDone);
+ this.hide();
+ var last = this.input.prop('value'),
+ value = leadingZero(this.hours) + ':' + leadingZero(this.minutes);
+ if (this.options.twelvehour) {
+ value = value + this.amOrPm;
+ }
+
+ this.input.prop('value', value);
+ if (value !== last) {
+ this.input.triggerHandler('change');
+ if (! this.isInput) {
+ this.element.trigger('change');
+ }
+ }
+
+ if (this.options.autoclose) {
+ this.input.trigger('blur');
+ }
+
+ raiseCallback(this.options.afterDone);
+ };
+
+ // Remove clockpicker from input
+ ClockPicker.prototype.remove = function() {
+ this.element.removeData('clockpicker');
+ this.input.off('focus.clockpicker click.clockpicker');
+ this.addon.off('click.clockpicker');
+ if (this.isShown) {
+ this.hide();
+ }
+ if (this.isAppended) {
+ $win.off('resize.clockpicker' + this.id);
+ this.popover.remove();
+ }
+ };
+
+ // Extends $.fn.clockpicker
+ $.fn.clockpicker = function(option){
+ var args = Array.prototype.slice.call(arguments, 1);
+ return this.each(function(){
+ var $this = $(this),
+ data = $this.data('clockpicker');
+ if (! data) {
+ var options = $.extend({}, ClockPicker.DEFAULTS, $this.data(), typeof option == 'object' && option);
+ $this.data('clockpicker', new ClockPicker($this, options));
+ } else {
+ // Manual operatsions. show, hide, remove, e.g.
+ if (typeof data[option] === 'function') {
+ data[option].apply(data, args);
+ }
+ }
+ });
+ };
+}());
diff --git a/js/datalist.js b/js/datalist.js
index 158f50d..ba16cd0 100644
--- a/js/datalist.js
+++ b/js/datalist.js
@@ -1,106 +1,106 @@
-$(function () {
- const toggleIcon = (el, fromClass, toClass) => $(el).removeClass(fromClass).addClass(toClass);
-
- const ocultaTodos = () => {
- toggleIcon('.datalist .icono', 'ing-cancelar iconoAzul pointer', 'ing-buscar');
- $('.datalist ul').hide();
- };
-
- $(document)
- .on('click', '.datalist-input,.icono', function () {
- const parent = $(this).parent();
- $(".datalist ul:visible").not(parent.find('ul')).siblings('.datalist-input').trigger('click');
- if (parent.find('ul').is(':visible') || parent.hasClass("disabled")) return ocultaTodos();
-
- parent.find('ul, .datalist-input').show();
- toggleIcon(parent.find('.icono'), 'ing-buscar', 'ing-cancelar iconoAzul pointer');
- })
- .on('click', '.datalist-select > ul li:not(.not-selectable)', function () {
- const parent = $(this).closest('.datalist');
- parent.find('.datalist-input').text($(this).text().trim());
- parent.find("input[type=hidden]").val($(this).data('id'));
- $('.datalist li').removeClass("selected");
- $(this).addClass("selected");
- parent.removeClass("datalist-invalid");
- ocultaTodos();
- })
- .on('click', (e) => {
- if (!$(e.target).closest('.datalist').length) ocultaTodos();
- });
-
- $('.modal').on('hide.bs.modal', ocultaTodos);
-});
-const setDatalist = (selector, value = -1) => {
- const parent = $(selector).closest('.datalist');
- parent.find('ul li:not(.not-selectable)').each(function () {
- if ($(this).data("id") !== value) return;
- parent.find('.datalist-input').text($(this).text().trim());
- $(selector).val(value);
- $('.datalist li').removeClass("selected");
- $(this).addClass("selected");
- $(this).click();
- });
-}
-const makeRequiredDatalist = (selector, required = true) => $(selector).closest('.datalist').toggleClass("required", required);
-
-//---------
-
-function setDatalistFirst(selector) {
- var index = 1;
- var elementRoot = $(selector).parents('.datalist');
- var num = elementRoot.find('ul li:not(.not-selectable)').length;
- if (index <= num) {
- while (elementRoot.find('ul li:nth-child(' + index + ')').hasClass("not-selectable") && index <= num) {
- index++;
- }
- var element = elementRoot.find('ul li:nth-child(' + index + ')');
- elementRoot.find('.datalist-input').text(element.html().replace(/[\t\n]+/g, ' ').trim());
- $(selector).val(element.data("id"));
- elementRoot.find("li").removeClass("selected");
- element.addClass("selected");
- }
-}
-
-
-function disableDatalist(selector, disabled = true) {
- var elementRoot = $(selector).parents('.datalist');
- if (disabled) {
- elementRoot.addClass("disabled");
- elementRoot.find('.icono').removeClass('ing-cancelar iconoAzul pointer').addClass('ing-buscar');
- elementRoot.find('ul').hide();
- } else
- elementRoot.removeClass("disabled");
-}
-
-function invalidDatalist(selector, invalid = true) {
- var elementRoot = $(selector).parents('.datalist');
- if (invalid) {
- elementRoot.addClass("datalist-invalid");
- } else
- elementRoot.removeClass("datalist-invalid");
-}
-
-//驴Se usa?
-function buscaDatalist(selector, valor) {
- selector.find('ul li').each(function () {
- var elem = $(this);
- if ($(this).parent().is('li'))
- elem = $(this).parent();
- if (!$(this).html().toUpperCase().includes(valor.toUpperCase())) {
- $(elem).hide();
- selector.find('.datalist-input').val("");
- selector.find("input[type=hidden]").val("");
- } else
- $(elem).show();
- });
-}
-function getDatalistText(selector, valor) {
- var elementRoot = $(selector).parents('.datalist');
- var text = "";
- $.each(elementRoot.find('ul li:not(.not-selectable)'), function () {
- if ($(this).data("id") == valor) {
- text = $(this).html();
- }
- });
- return text;
-}
+$(function () {
+ const toggleIcon = (el, fromClass, toClass) => $(el).removeClass(fromClass).addClass(toClass);
+
+ const ocultaTodos = () => {
+ toggleIcon('.datalist .icono', 'ing-cancelar iconoAzul pointer', 'ing-buscar');
+ $('.datalist ul').hide();
+ };
+
+ $(document)
+ .on('click', '.datalist-input,.icono', function () {
+ const parent = $(this).parent();
+ $(".datalist ul:visible").not(parent.find('ul')).siblings('.datalist-input').trigger('click');
+ if (parent.find('ul').is(':visible') || parent.hasClass("disabled")) return ocultaTodos();
+
+ parent.find('ul, .datalist-input').show();
+ toggleIcon(parent.find('.icono'), 'ing-buscar', 'ing-cancelar iconoAzul pointer');
+ })
+ .on('click', '.datalist-select > ul li:not(.not-selectable)', function () {
+ const parent = $(this).closest('.datalist');
+ parent.find('.datalist-input').text($(this).text().trim());
+ parent.find("input[type=hidden]").val($(this).data('id'));
+ $('.datalist li').removeClass("selected");
+ $(this).addClass("selected");
+ parent.removeClass("datalist-invalid");
+ ocultaTodos();
+ })
+ .on('click', (e) => {
+ if (!$(e.target).closest('.datalist').length) ocultaTodos();
+ });
+
+ $('.modal').on('hide.bs.modal', ocultaTodos);
+});
+const setDatalist = (selector, value = -1) => {
+ const parent = $(selector).closest('.datalist');
+ parent.find('ul li:not(.not-selectable)').each(function () {
+ if ($(this).data("id") !== value) return;
+ parent.find('.datalist-input').text($(this).text().trim());
+ $(selector).val(value);
+ $('.datalist li').removeClass("selected");
+ $(this).addClass("selected");
+ $(this).click();
+ });
+}
+const makeRequiredDatalist = (selector, required = true) => $(selector).closest('.datalist').toggleClass("required", required);
+
+//---------
+
+function setDatalistFirst(selector) {
+ var index = 1;
+ var elementRoot = $(selector).parents('.datalist');
+ var num = elementRoot.find('ul li:not(.not-selectable)').length;
+ if (index <= num) {
+ while (elementRoot.find('ul li:nth-child(' + index + ')').hasClass("not-selectable") && index <= num) {
+ index++;
+ }
+ var element = elementRoot.find('ul li:nth-child(' + index + ')');
+ elementRoot.find('.datalist-input').text(element.html().replace(/[\t\n]+/g, ' ').trim());
+ $(selector).val(element.data("id"));
+ elementRoot.find("li").removeClass("selected");
+ element.addClass("selected");
+ }
+}
+
+
+function disableDatalist(selector, disabled = true) {
+ var elementRoot = $(selector).parents('.datalist');
+ if (disabled) {
+ elementRoot.addClass("disabled");
+ elementRoot.find('.icono').removeClass('ing-cancelar iconoAzul pointer').addClass('ing-buscar');
+ elementRoot.find('ul').hide();
+ } else
+ elementRoot.removeClass("disabled");
+}
+
+function invalidDatalist(selector, invalid = true) {
+ var elementRoot = $(selector).parents('.datalist');
+ if (invalid) {
+ elementRoot.addClass("datalist-invalid");
+ } else
+ elementRoot.removeClass("datalist-invalid");
+}
+
+//驴Se usa?
+function buscaDatalist(selector, valor) {
+ selector.find('ul li').each(function () {
+ var elem = $(this);
+ if ($(this).parent().is('li'))
+ elem = $(this).parent();
+ if (!$(this).html().toUpperCase().includes(valor.toUpperCase())) {
+ $(elem).hide();
+ selector.find('.datalist-input').val("");
+ selector.find("input[type=hidden]").val("");
+ } else
+ $(elem).show();
+ });
+}
+function getDatalistText(selector, valor) {
+ var elementRoot = $(selector).parents('.datalist');
+ var text = "";
+ $.each(elementRoot.find('ul li:not(.not-selectable)'), function () {
+ if ($(this).data("id") == valor) {
+ text = $(this).html();
+ }
+ });
+ return text;
+}
diff --git a/js/datepicker-es.js b/js/datepicker-es.js
index ea7116e..f72e195 100644
--- a/js/datepicker-es.js
+++ b/js/datepicker-es.js
@@ -1,37 +1,37 @@
-/* Inicializaci贸n en espa帽ol para la extensi贸n 'UI date picker' para jQuery. */
-/* Traducido por Vester (xvester@gmail.com). */
-( function( factory ) {
- if ( typeof define === "function" && define.amd ) {
-
- // AMD. Register as an anonymous module.
- define( [ "../widgets/datepicker" ], factory );
- } else {
-
- // Browser globals
- factory( jQuery.datepicker );
- }
-}( function( datepicker ) {
-
-datepicker.regional.es = {
- closeText: "Cerrar",
- prevText: "<Ant",
- nextText: "Sig>",
- currentText: "Hoy",
- monthNames: [ "enero","febrero","marzo","abril","mayo","junio",
- "julio","agosto","septiembre","octubre","noviembre","diciembre" ],
- monthNamesShort: [ "ene","feb","mar","abr","may","jun",
- "jul","ago","sep","oct","nov","dic" ],
- dayNames: [ "domingo","lunes","martes","mi茅rcoles","jueves","viernes","s谩bado" ],
- dayNamesShort: [ "dom","lun","mar","mi茅","jue","vie","s谩b" ],
- dayNamesMin: [ "D","L","M","X","J","V","S" ],
- weekHeader: "Sm",
- dateFormat: "dd/mm/yy",
- firstDay: 1,
- isRTL: false,
- showMonthAfterYear: false,
- yearSuffix: "" };
-datepicker.setDefaults( datepicker.regional.es );
-
-return datepicker.regional.es;
-
-} ) );
+/* Inicializaci贸n en espa帽ol para la extensi贸n 'UI date picker' para jQuery. */
+/* Traducido por Vester (xvester@gmail.com). */
+( function( factory ) {
+ if ( typeof define === "function" && define.amd ) {
+
+ // AMD. Register as an anonymous module.
+ define( [ "../widgets/datepicker" ], factory );
+ } else {
+
+ // Browser globals
+ factory( jQuery.datepicker );
+ }
+}( function( datepicker ) {
+
+datepicker.regional.es = {
+ closeText: "Cerrar",
+ prevText: "<Ant",
+ nextText: "Sig>",
+ currentText: "Hoy",
+ monthNames: [ "enero","febrero","marzo","abril","mayo","junio",
+ "julio","agosto","septiembre","octubre","noviembre","diciembre" ],
+ monthNamesShort: [ "ene","feb","mar","abr","may","jun",
+ "jul","ago","sep","oct","nov","dic" ],
+ dayNames: [ "domingo","lunes","martes","mi茅rcoles","jueves","viernes","s谩bado" ],
+ dayNamesShort: [ "dom","lun","mar","mi茅","jue","vie","s谩b" ],
+ dayNamesMin: [ "D","L","M","X","J","V","S" ],
+ weekHeader: "Sm",
+ dateFormat: "dd/mm/yy",
+ firstDay: 1,
+ isRTL: false,
+ showMonthAfterYear: false,
+ yearSuffix: "" };
+datepicker.setDefaults( datepicker.regional.es );
+
+return datepicker.regional.es;
+
+} ) );
diff --git a/js/faltas.js b/js/faltas.js
index b1ffe85..4aa3b58 100644
--- a/js/faltas.js
+++ b/js/faltas.js
@@ -1,84 +1,84 @@
-import { createApp, reactive } from 'https://unpkg.com/petite-vue?module';
-const filter = reactive({
- facultad: -1,
- profesor: '',
- porcentaje: 0,
- faltas: 0,
- tipoFaltas: true,
-});
-
-const app = createApp({
- filter,
- facultades: [],
- profesores: [],
- faltas: [],
- mensaje: {
- titulo: '',
- texto: '',
- },
- async refresh() {
- if (filter.facultad == -1 || (filter.porcentaje < 10 && filter.faltas < 1)) {
- console.log('Facultad: ', filter.facultad, 'Porcentaje: ', filter.porcentaje, 'Faltas: ', filter.faltas);
- return;
- }
- $('#cargando').modal('show');
- try {
-
- this.faltas = await fetch(`action/profesor_faltas.php?facultad=${this.filter.facultad}&${this.filter.tipoFaltas ? 'supervisor' : 'profesor'}&${this.filter.faltas > 0 ? 'faltas' : 'porcentaje'}=${this.filter.faltas > 0 ? this.filter.faltas : this.filter.porcentaje}`).then(res => res.json());
- if (this.faltas.error) {
- $('.modal#mensaje').modal('show');
- this.mensaje.titulo = 'Informaci贸n';
- this.mensaje.texto = this.faltas.error;
- }
- } catch (error) {
- $('.modal#mensaje').modal('show');
- this.mensaje.titulo = 'Error';
- this.mensaje.texto = 'No se pudo cargar los datos';
- }
- finally {
- $('#cargando').modal('hide');
- }
- },
-
- async toExcel() {
- if (filter.facultad == -1 || filter.porcentaje < 10) {
- return;
- }
- $('#cargando').modal('show');
- try {
- const response = await fetch(`export/faltas_excel.php`, {
- method: 'POST',
- body: JSON.stringify(this.faltas.map(falta => ({
- 'profesor_clave': falta.profesor.profesor_clave,
- 'profesor_correo': falta.profesor.profesor_correo,
- 'profesor_nombre': falta.profesor.profesor_nombre,
- 'faltas': falta.faltas,
- 'porcentaje': `${falta.porcentaje}%`,
- 'total': falta.total,
- }))),
- })
-
- const blob = await response.blob();
- window.saveAs(blob, `faltas_${this.facultades.find(facultad => facultad.facultad_id == filter.facultad).facultad_nombre}_${new Date().toISOString().slice(0, 10)}.xlsx`);
- } catch (error) {
- $('.modal#mensaje').modal('show');
- this.mensaje.titulo = 'Error';
- this.mensaje.texto = 'No se pudo cargar los datos';
- console.log('Error: ', error);
- }
- finally {
- $('#cargando').modal('hide');
- }
- },
- async mounted() {
- try {
- this.facultades = await fetch('action/action_facultad.php').then(res => res.json());
- this.profesores = await fetch('action/action_profesor.php').then(res => res.json());
- } catch (error) {
- $('.modal#mensaje').modal('show');
- this.mensaje.titulo = 'Error';
- this.mensaje.texto = 'No se pudo cargar los datos';
- console.log('Error: ', error);
- }
- }
-}).mount('#app');
+import { createApp, reactive } from 'https://unpkg.com/petite-vue?module';
+const filter = reactive({
+ facultad: -1,
+ profesor: '',
+ porcentaje: 0,
+ faltas: 0,
+ tipoFaltas: true,
+});
+
+const app = createApp({
+ filter,
+ facultades: [],
+ profesores: [],
+ faltas: [],
+ mensaje: {
+ titulo: '',
+ texto: '',
+ },
+ async refresh() {
+ if (filter.facultad == -1 || (filter.porcentaje < 10 && filter.faltas < 1)) {
+ console.log('Facultad: ', filter.facultad, 'Porcentaje: ', filter.porcentaje, 'Faltas: ', filter.faltas);
+ return;
+ }
+ $('#cargando').modal('show');
+ try {
+
+ this.faltas = await fetch(`action/profesor_faltas.php?facultad=${this.filter.facultad}&${this.filter.tipoFaltas ? 'supervisor' : 'profesor'}&${this.filter.faltas > 0 ? 'faltas' : 'porcentaje'}=${this.filter.faltas > 0 ? this.filter.faltas : this.filter.porcentaje}`).then(res => res.json());
+ if (this.faltas.error) {
+ $('.modal#mensaje').modal('show');
+ this.mensaje.titulo = 'Informaci贸n';
+ this.mensaje.texto = this.faltas.error;
+ }
+ } catch (error) {
+ $('.modal#mensaje').modal('show');
+ this.mensaje.titulo = 'Error';
+ this.mensaje.texto = 'No se pudo cargar los datos';
+ }
+ finally {
+ $('#cargando').modal('hide');
+ }
+ },
+
+ async toExcel() {
+ if (filter.facultad == -1 || filter.porcentaje < 10) {
+ return;
+ }
+ $('#cargando').modal('show');
+ try {
+ const response = await fetch(`export/faltas_excel.php`, {
+ method: 'POST',
+ body: JSON.stringify(this.faltas.map(falta => ({
+ 'profesor_clave': falta.profesor.profesor_clave,
+ 'profesor_correo': falta.profesor.profesor_correo,
+ 'profesor_nombre': falta.profesor.profesor_nombre,
+ 'faltas': falta.faltas,
+ 'porcentaje': `${falta.porcentaje}%`,
+ 'total': falta.total,
+ }))),
+ })
+
+ const blob = await response.blob();
+ window.saveAs(blob, `faltas_${this.facultades.find(facultad => facultad.facultad_id == filter.facultad).facultad_nombre}_${new Date().toISOString().slice(0, 10)}.xlsx`);
+ } catch (error) {
+ $('.modal#mensaje').modal('show');
+ this.mensaje.titulo = 'Error';
+ this.mensaje.texto = 'No se pudo cargar los datos';
+ console.log('Error: ', error);
+ }
+ finally {
+ $('#cargando').modal('hide');
+ }
+ },
+ async mounted() {
+ try {
+ this.facultades = await fetch('action/action_facultad.php').then(res => res.json());
+ this.profesores = await fetch('action/action_profesor.php').then(res => res.json());
+ } catch (error) {
+ $('.modal#mensaje').modal('show');
+ this.mensaje.titulo = 'Error';
+ this.mensaje.texto = 'No se pudo cargar los datos';
+ console.log('Error: ', error);
+ }
+ }
+}).mount('#app');
diff --git a/js/horario.js b/js/horario.js
index b84d5a3..cc04acc 100644
--- a/js/horario.js
+++ b/js/horario.js
@@ -1,106 +1,106 @@
-import { createApp, reactive } from 'https://unpkg.com/petite-vue?module';
-const profesores = reactive({
- data: [],
- search: null,
- fetch: async function () {
- const response = await fetch('action/action_profesor.php');
- this.data = await response.json();
- },
- get clave() {
- const match = this.search.match(/^\((.+)\)/);
- return match ? match[1] : '';
- },
- get current() {
- return this.data.find((profesor) => profesor.profesor_clave === profesores.clave);
- },
-});
-const facultades = reactive({
- data: [],
- fetch: async function () {
- const facultades = await fetch('action/action_facultad.php').then(response => response.json());
- const carreras = await fetch(`action/carrera.php`).then(response => response.json());
- this.data = await Promise.all(facultades.map(async (facultad) => ({
- ...facultad,
- carreras: await Promise.all(carreras.filter((carrera) => carrera.facultad_id === facultad.facultad_id).map(async (carrera) => {
- const grupos = await fetch(`action/action_grupo.php?carrera_id=${carrera.carrera_id}`).then(response => response.json());
- return {
- ...carrera,
- grupos,
- };
- })),
- })));
- this.data = this.data.filter((facultad) => facultad.carreras.length > 0);
- }
-});
-const horarios = reactive({
- data: [],
- fetch: async function (grupo = null, carrera_id = null) {
- if (grupo && carrera_id) {
- const response = await fetch(`action/action_horario.php?grupo=${grupo}&carrera_id=${carrera_id}`);
- this.data = await response.json();
- }
- else if (profesores.current) {
- const response = await fetch(`action/action_horario.php?profesor_id=${profesores.current.profesor_id}`);
- this.data = await response.json();
- }
- },
- get structure() {
- if (this.data.length === 0)
- return null;
- const structure = {
- s谩bado: this.data.some((horario) => horario.horario_dia === 6),
- hora_m铆nima: Math.min(...this.data.map((horario) => parseInt(horario.horario_hora.split(':')[0]))),
- hora_m谩xima: Math.max(...this.data.map((horario) => {
- const [hour, minute] = horario.horario_fin.split(':').map(Number);
- return hour + Math.ceil(minute / 60);
- })),
- horas_totales: 0
- };
- structure.horas_totales = structure.hora_m谩xima - structure.hora_m铆nima;
- return structure;
- },
- get blocks() {
- if (this.data.length === 0)
- return null;
- return [...Array(this.structure.horas_totales).keys()].flatMap(hora => {
- const baseHour = hora + this.structure.hora_m铆nima;
- return [0, 15, 30, 45].map(block => ({ hour: baseHour, block }));
- });
- },
- getHorarioData(hour, block, d铆a) {
- const foundHorario = this.data.find((horario) => parseInt(horario.horario_hora.split(':')[0]) === hour &&
- parseInt(horario.horario_hora.split(':')[1]) === block &&
- horario.horario_dia === d铆a);
- return foundHorario;
- },
- isOccupied(hora, bloque, day) {
- if (this.getHorarioData(hora, bloque, day)) {
- return false;
- }
- const currentTimeInMinutes = hora * 60 + bloque;
- for (const item of this.data) {
- if (item.horario_dia !== day) {
- continue; // Skip items that are not on the specified day
- }
- // Split the hour and minute from horario_hora
- const [startHour, startMinute] = item.horario_hora.split(":").map(Number);
- const startTimeInMinutes = startHour * 60 + startMinute;
- // Calculate end time using duracion
- const [durationHours, durationMinutes] = item.duracion.split(":").map(Number);
- const endTimeInMinutes = startTimeInMinutes + (durationHours * 60) + durationMinutes;
- if (currentTimeInMinutes >= startTimeInMinutes && currentTimeInMinutes < endTimeInMinutes) {
- return true; // The block is occupied
- }
- }
- return false; // The block is not occupied by any class
- }
-});
-const app = createApp({
- profesores,
- horarios,
- facultades,
- mounted: async function () {
- await profesores.fetch();
- await facultades.fetch();
- }
-}).mount('#app');
+import { createApp, reactive } from 'https://unpkg.com/petite-vue?module';
+const profesores = reactive({
+ data: [],
+ search: null,
+ fetch: async function () {
+ const response = await fetch('action/action_profesor.php');
+ this.data = await response.json();
+ },
+ get clave() {
+ const match = this.search.match(/^\((.+)\)/);
+ return match ? match[1] : '';
+ },
+ get current() {
+ return this.data.find((profesor) => profesor.profesor_clave === profesores.clave);
+ },
+});
+const facultades = reactive({
+ data: [],
+ fetch: async function () {
+ const facultades = await fetch('action/action_facultad.php').then(response => response.json());
+ const carreras = await fetch(`action/carrera.php`).then(response => response.json());
+ this.data = await Promise.all(facultades.map(async (facultad) => ({
+ ...facultad,
+ carreras: await Promise.all(carreras.filter((carrera) => carrera.facultad_id === facultad.facultad_id).map(async (carrera) => {
+ const grupos = await fetch(`action/action_grupo.php?carrera_id=${carrera.carrera_id}`).then(response => response.json());
+ return {
+ ...carrera,
+ grupos,
+ };
+ })),
+ })));
+ this.data = this.data.filter((facultad) => facultad.carreras.length > 0);
+ }
+});
+const horarios = reactive({
+ data: [],
+ fetch: async function (grupo = null, carrera_id = null) {
+ if (grupo && carrera_id) {
+ const response = await fetch(`action/action_horario.php?grupo=${grupo}&carrera_id=${carrera_id}`);
+ this.data = await response.json();
+ }
+ else if (profesores.current) {
+ const response = await fetch(`action/action_horario.php?profesor_id=${profesores.current.profesor_id}`);
+ this.data = await response.json();
+ }
+ },
+ get structure() {
+ if (this.data.length === 0)
+ return null;
+ const structure = {
+ s谩bado: this.data.some((horario) => horario.horario_dia === 6),
+ hora_m铆nima: Math.min(...this.data.map((horario) => parseInt(horario.horario_hora.split(':')[0]))),
+ hora_m谩xima: Math.max(...this.data.map((horario) => {
+ const [hour, minute] = horario.horario_fin.split(':').map(Number);
+ return hour + Math.ceil(minute / 60);
+ })),
+ horas_totales: 0
+ };
+ structure.horas_totales = structure.hora_m谩xima - structure.hora_m铆nima;
+ return structure;
+ },
+ get blocks() {
+ if (this.data.length === 0)
+ return null;
+ return [...Array(this.structure.horas_totales).keys()].flatMap(hora => {
+ const baseHour = hora + this.structure.hora_m铆nima;
+ return [0, 15, 30, 45].map(block => ({ hour: baseHour, block }));
+ });
+ },
+ getHorarioData(hour, block, d铆a) {
+ const foundHorario = this.data.find((horario) => parseInt(horario.horario_hora.split(':')[0]) === hour &&
+ parseInt(horario.horario_hora.split(':')[1]) === block &&
+ horario.horario_dia === d铆a);
+ return foundHorario;
+ },
+ isOccupied(hora, bloque, day) {
+ if (this.getHorarioData(hora, bloque, day)) {
+ return false;
+ }
+ const currentTimeInMinutes = hora * 60 + bloque;
+ for (const item of this.data) {
+ if (item.horario_dia !== day) {
+ continue; // Skip items that are not on the specified day
+ }
+ // Split the hour and minute from horario_hora
+ const [startHour, startMinute] = item.horario_hora.split(":").map(Number);
+ const startTimeInMinutes = startHour * 60 + startMinute;
+ // Calculate end time using duracion
+ const [durationHours, durationMinutes] = item.duracion.split(":").map(Number);
+ const endTimeInMinutes = startTimeInMinutes + (durationHours * 60) + durationMinutes;
+ if (currentTimeInMinutes >= startTimeInMinutes && currentTimeInMinutes < endTimeInMinutes) {
+ return true; // The block is occupied
+ }
+ }
+ return false; // The block is not occupied by any class
+ }
+});
+const app = createApp({
+ profesores,
+ horarios,
+ facultades,
+ mounted: async function () {
+ await profesores.fetch();
+ await facultades.fetch();
+ }
+}).mount('#app');
diff --git a/js/jquery-ui.js b/js/jquery-ui.js
index 25398a1..1396c9b 100644
--- a/js/jquery-ui.js
+++ b/js/jquery-ui.js
@@ -1,13 +1,13 @@
-/*! jQuery UI - v1.12.1 - 2016-09-14
-* http://jqueryui.com
-* Includes: widget.js, position.js, data.js, disable-selection.js, effect.js, effects/effect-blind.js, effects/effect-bounce.js, effects/effect-clip.js, effects/effect-drop.js, effects/effect-explode.js, effects/effect-fade.js, effects/effect-fold.js, effects/effect-highlight.js, effects/effect-puff.js, effects/effect-pulsate.js, effects/effect-scale.js, effects/effect-shake.js, effects/effect-size.js, effects/effect-slide.js, effects/effect-transfer.js, focusable.js, form-reset-mixin.js, jquery-1-7.js, keycode.js, labels.js, scroll-parent.js, tabbable.js, unique-id.js, widgets/accordion.js, widgets/autocomplete.js, widgets/button.js, widgets/checkboxradio.js, widgets/controlgroup.js, widgets/datepicker.js, widgets/dialog.js, widgets/draggable.js, widgets/droppable.js, widgets/menu.js, widgets/mouse.js, widgets/progressbar.js, widgets/resizable.js, widgets/selectable.js, widgets/selectmenu.js, widgets/slider.js, widgets/sortable.js, widgets/spinner.js, widgets/tabs.js, widgets/tooltip.js
-* Copyright jQuery Foundation and other contributors; Licensed MIT */
-
-(function(t){"function"==typeof define&&define.amd?define(["jquery"],t):t(jQuery)})(function(t){function e(t){for(var e=t.css("visibility");"inherit"===e;)t=t.parent(),e=t.css("visibility");return"hidden"!==e}function i(t){for(var e,i;t.length&&t[0]!==document;){if(e=t.css("position"),("absolute"===e||"relative"===e||"fixed"===e)&&(i=parseInt(t.css("zIndex"),10),!isNaN(i)&&0!==i))return i;t=t.parent()}return 0}function s(){this._curInst=null,this._keyEvent=!1,this._disabledInputs=[],this._datepickerShowing=!1,this._inDialog=!1,this._mainDivId="ui-datepicker-div",this._inlineClass="ui-datepicker-inline",this._appendClass="ui-datepicker-append",this._triggerClass="ui-datepicker-trigger",this._dialogClass="ui-datepicker-dialog",this._disableClass="ui-datepicker-disabled",this._unselectableClass="ui-datepicker-unselectable",this._currentClass="ui-datepicker-current-day",this._dayOverClass="ui-datepicker-days-cell-over",this.regional=[],this.regional[""]={closeText:"Done",prevText:"Prev",nextText:"Next",currentText:"Today",monthNames:["January","February","March","April","May","June","July","August","September","October","November","December"],monthNamesShort:["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"],dayNames:["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"],dayNamesShort:["Sun","Mon","Tue","Wed","Thu","Fri","Sat"],dayNamesMin:["Su","Mo","Tu","We","Th","Fr","Sa"],weekHeader:"Wk",dateFormat:"mm/dd/yy",firstDay:0,isRTL:!1,showMonthAfterYear:!1,yearSuffix:""},this._defaults={showOn:"focus",showAnim:"fadeIn",showOptions:{},defaultDate:null,appendText:"",buttonText:"...",buttonImage:"",buttonImageOnly:!1,hideIfNoPrevNext:!1,navigationAsDateFormat:!1,gotoCurrent:!1,changeMonth:!1,changeYear:!1,yearRange:"c-10:c+10",showOtherMonths:!1,selectOtherMonths:!1,showWeek:!1,calculateWeek:this.iso8601Week,shortYearCutoff:"+10",minDate:null,maxDate:null,duration:"fast",beforeShowDay:null,beforeShow:null,onSelect:null,onChangeMonthYear:null,onClose:null,numberOfMonths:1,showCurrentAtPos:0,stepMonths:1,stepBigMonths:12,altField:"",altFormat:"",constrainInput:!0,showButtonPanel:!1,autoSize:!1,disabled:!1},t.extend(this._defaults,this.regional[""]),this.regional.en=t.extend(!0,{},this.regional[""]),this.regional["en-US"]=t.extend(!0,{},this.regional.en),this.dpDiv=n(t("
"))}function n(e){var i="button, .ui-datepicker-prev, .ui-datepicker-next, .ui-datepicker-calendar td a";return e.on("mouseout",i,function(){t(this).removeClass("ui-state-hover"),-1!==this.className.indexOf("ui-datepicker-prev")&&t(this).removeClass("ui-datepicker-prev-hover"),-1!==this.className.indexOf("ui-datepicker-next")&&t(this).removeClass("ui-datepicker-next-hover")}).on("mouseover",i,o)}function o(){t.datepicker._isDisabledDatepicker(m.inline?m.dpDiv.parent()[0]:m.input[0])||(t(this).parents(".ui-datepicker-calendar").find("a").removeClass("ui-state-hover"),t(this).addClass("ui-state-hover"),-1!==this.className.indexOf("ui-datepicker-prev")&&t(this).addClass("ui-datepicker-prev-hover"),-1!==this.className.indexOf("ui-datepicker-next")&&t(this).addClass("ui-datepicker-next-hover"))}function a(e,i){t.extend(e,i);for(var s in i)null==i[s]&&(e[s]=i[s]);return e}function r(t){return function(){var e=this.element.val();t.apply(this,arguments),this._refresh(),e!==this.element.val()&&this._trigger("change")}}t.ui=t.ui||{},t.ui.version="1.12.1";var h=0,l=Array.prototype.slice;t.cleanData=function(e){return function(i){var s,n,o;for(o=0;null!=(n=i[o]);o++)try{s=t._data(n,"events"),s&&s.remove&&t(n).triggerHandler("remove")}catch(a){}e(i)}}(t.cleanData),t.widget=function(e,i,s){var n,o,a,r={},h=e.split(".")[0];e=e.split(".")[1];var l=h+"-"+e;return s||(s=i,i=t.Widget),t.isArray(s)&&(s=t.extend.apply(null,[{}].concat(s))),t.expr[":"][l.toLowerCase()]=function(e){return!!t.data(e,l)},t[h]=t[h]||{},n=t[h][e],o=t[h][e]=function(t,e){return this._createWidget?(arguments.length&&this._createWidget(t,e),void 0):new o(t,e)},t.extend(o,n,{version:s.version,_proto:t.extend({},s),_childConstructors:[]}),a=new i,a.options=t.widget.extend({},a.options),t.each(s,function(e,s){return t.isFunction(s)?(r[e]=function(){function t(){return i.prototype[e].apply(this,arguments)}function n(t){return i.prototype[e].apply(this,t)}return function(){var e,i=this._super,o=this._superApply;return this._super=t,this._superApply=n,e=s.apply(this,arguments),this._super=i,this._superApply=o,e}}(),void 0):(r[e]=s,void 0)}),o.prototype=t.widget.extend(a,{widgetEventPrefix:n?a.widgetEventPrefix||e:e},r,{constructor:o,namespace:h,widgetName:e,widgetFullName:l}),n?(t.each(n._childConstructors,function(e,i){var s=i.prototype;t.widget(s.namespace+"."+s.widgetName,o,i._proto)}),delete n._childConstructors):i._childConstructors.push(o),t.widget.bridge(e,o),o},t.widget.extend=function(e){for(var i,s,n=l.call(arguments,1),o=0,a=n.length;a>o;o++)for(i in n[o])s=n[o][i],n[o].hasOwnProperty(i)&&void 0!==s&&(e[i]=t.isPlainObject(s)?t.isPlainObject(e[i])?t.widget.extend({},e[i],s):t.widget.extend({},s):s);return e},t.widget.bridge=function(e,i){var s=i.prototype.widgetFullName||e;t.fn[e]=function(n){var o="string"==typeof n,a=l.call(arguments,1),r=this;return o?this.length||"instance"!==n?this.each(function(){var i,o=t.data(this,s);return"instance"===n?(r=o,!1):o?t.isFunction(o[n])&&"_"!==n.charAt(0)?(i=o[n].apply(o,a),i!==o&&void 0!==i?(r=i&&i.jquery?r.pushStack(i.get()):i,!1):void 0):t.error("no such method '"+n+"' for "+e+" widget instance"):t.error("cannot call methods on "+e+" prior to initialization; "+"attempted to call method '"+n+"'")}):r=void 0:(a.length&&(n=t.widget.extend.apply(null,[n].concat(a))),this.each(function(){var e=t.data(this,s);e?(e.option(n||{}),e._init&&e._init()):t.data(this,s,new i(n,this))})),r}},t.Widget=function(){},t.Widget._childConstructors=[],t.Widget.prototype={widgetName:"widget",widgetEventPrefix:"",defaultElement:"