Project import generated by Copybara.
GitOrigin-RevId: 63746295f1a5ab5a619056791995793d65529e62
diff --git a/src/dynamic/addincidentbulk.php b/src/dynamic/addincidentbulk.php
new file mode 100644
index 0000000..afb04ba
--- /dev/null
+++ b/src/dynamic/addincidentbulk.php
@@ -0,0 +1,86 @@
+<?php
+require_once(__DIR__."/../core.php");
+security::checkType(security::ADMIN, security::METHOD_NOTFOUND);
+
+if (!security::checkParams("GET", [
+ ["workers", security::PARAM_ISARRAY]
+])) {
+ security::notFound();
+}
+?>
+
+<style>
+.notvisible {
+ display: none;
+}
+</style>
+
+<dynscript>
+document.getElementById("allday").addEventListener("change", e => {
+ var partialtime = document.getElementById("partialtime");
+ if (e.target.checked) {
+ partialtime.classList.add("notvisible");
+ } else {
+ partialtime.classList.remove("notvisible");
+ }
+});
+</dynscript>
+
+<form action="doaddincidentbulk.php" method="POST" autocomplete="off">
+ <h4 class="mdl-dialog__title">Añade una incidencia</h4>
+ <div class="mdl-dialog__content">
+ <div class="mdlext-selectfield mdlext-js-selectfield mdlext-selectfield--floating-label">
+ <select name="type" id="type" class="mdlext-selectfield__select" data-required>
+ <option></option>
+ <?php
+ foreach (incidents::getTypesForm() as $i) {
+ echo '<option value="'.(int)$i["id"].'">'.security::htmlsafe($i["name"]).'</option>';
+ }
+ ?>
+ </select>
+ <label for="type" class="mdlext-selectfield__label">Tipo</label>
+ </div>
+
+ <h5>Afectación</h5>
+ <div class="mdl-textfield mdl-js-textfield mdl-textfield--floating-label">
+ <input class="mdl-textfield__input" type="date" name="day" id="day" autocomplete="off" data-required>
+ <label class="mdl-textfield__label always-focused" for="day">Día</label>
+ </div>
+ <br>
+ <p>
+ <label class="mdl-switch mdl-js-switch mdl-js-ripple-effect" for="allday">
+ <input type="checkbox" id="allday" name="allday" value="1" class="mdl-switch__input">
+ <span class="mdl-switch__label">Día entero</span>
+ </label>
+ </p>
+ <div id="partialtime">De <input type="time" name="begins"> a <input type="time" name="ends"></div>
+
+ <h5>Detalles adicionales</h5>
+ <div class="mdl-textfield mdl-js-textfield mdl-textfield--floating-label">
+ <textarea class="mdl-textfield__input" name="details" id="details"></textarea>
+ <label class="mdl-textfield__label" for="details">Observaciones (opcional)</label>
+ </div>
+ <p>Las observaciones aparecerán en los PDFs que se exporten.</p>
+ <p>Después de crear la incidencia podrás añadir archivos adjuntos haciendo clic en el botón <i class="material-icons" style="vertical-align: middle;">attach_file</i>.</p>
+
+ <b>Trabajadores:</b>
+ <div class="copyto">
+ <ul>
+ <?php
+ foreach ($_GET["workers"] as $workerid) {
+ $worker = workers::get($workerid);
+ if ($worker === false) {
+ die("Error: Uno de los trabajadores seleccionados ya no existe");
+ }
+
+ echo "<li><input type='hidden' name='workers[]' value='".(int)$worker["id"]."'> ".security::htmlsafe($worker["name"])." (".security::htmlsafe($worker["companyname"]).")</li>";
+ }
+ ?>
+ </ul>
+ </div>
+ </div>
+ <div class="mdl-dialog__actions">
+ <button type="submit" class="mdl-button mdl-js-button mdl-js-ripple-effect mdl-button--accent">Añadir</button>
+ <button data-dyndialog-close class="mdl-button mdl-js-button mdl-js-ripple-effect cancel">Cancelar</button>
+ </div>
+</form>
diff --git a/src/dynamic/addworkhistoryitem.php b/src/dynamic/addworkhistoryitem.php
new file mode 100644
index 0000000..712c3f6
--- /dev/null
+++ b/src/dynamic/addworkhistoryitem.php
@@ -0,0 +1,50 @@
+<?php
+require_once(__DIR__."/../core.php");
+security::checkType(security::ADMIN, security::METHOD_NOTFOUND);
+
+if (!isset($_GET["id"])) {
+ security::notFound();
+}
+
+$id = (int)$_GET["id"];
+
+$worker = workers::get($id);
+if ($worker === false) security::notFound();
+?>
+
+<dynscript>
+document.getElementById("cancel").addEventListener("click", e => {
+ e.preventDefault();
+ dynDialog.load("dynamic/workhistory.php?id="+parseInt(document.getElementById("cancel").getAttribute("data-worker-id")));
+});
+</dynscript>
+
+<form action="doaddworkhistoryitem.php" method="POST" autocomplete="off">
+ <input type="hidden" name="id" value="<?=(int)$worker["id"]?>">
+ <h4 class="mdl-dialog__title">Añadir alta/baja</h4>
+ <div class="mdl-dialog__content">
+ <p><b>Persona:</b> <?=security::htmlsafe($worker["name"])?><br>
+ <b>Empresa:</b> <?=security::htmlsafe($worker["companyname"])?></p>
+
+ <div class="mdl-textfield mdl-js-textfield mdl-textfield--floating-label">
+ <input class="mdl-textfield__input" type="date" name="day" id="day" autocomplete="off" data-required>
+ <label class="mdl-textfield__label" for="day">Fecha</label>
+ </div>
+ <br>
+ <div class="mdlext-selectfield mdlext-js-selectfield mdlext-selectfield--floating-label">
+ <select name="status" id="status" class="mdlext-selectfield__select" data-required>
+ <option></option>
+ <?php
+ foreach (workers::$affiliationStatusesManual as $status) {
+ echo '<option value="'.(int)$status.'">'.security::htmlsafe(workers::affiliationStatusHelper($status)).'</option>';
+ }
+ ?>
+ </select>
+ <label for="status" class="mdlext-selectfield__label">Tipo</label>
+ </div>
+ </div>
+ <div class="mdl-dialog__actions">
+ <button type="submit" class="mdl-button mdl-js-button mdl-js-ripple-effect mdl-button--accent">Añadir</button>
+ <button id="cancel" class="mdl-button mdl-js-button mdl-js-ripple-effect" data-worker-id="<?=(int)$worker["id"]?>">Cancelar</button>
+ </div>
+</form>
diff --git a/src/dynamic/authorsincident.php b/src/dynamic/authorsincident.php
new file mode 100644
index 0000000..25194f3
--- /dev/null
+++ b/src/dynamic/authorsincident.php
@@ -0,0 +1,43 @@
+<?php
+require_once(__DIR__."/../core.php");
+security::checkType(security::WORKER, security::METHOD_NOTFOUND);
+security::checkWorkerUIEnabled();
+
+$isAdmin = security::isAllowed(security::ADMIN);
+
+if (!isset($_GET["id"])) {
+ security::notFound();
+}
+
+$id = (int)$_GET["id"];
+
+$incident = incidents::get($id, true);
+if ($incident === false) security::notFound();
+
+if (!$isAdmin) incidents::checkIncidentIsFromPerson($incident["id"]);
+?>
+
+<style>
+#dynDialog {
+ max-width: 380px;
+ width: auto;
+}
+</style>
+
+<h4 class="mdl-dialog__title">Autoría de la incidencia</h4>
+<div class="mdl-dialog__content">
+ <ul>
+ <?php if ($incident["creator"] != -1) { ?><li><b>Creador:</b> <?=security::htmlsafe(people::userData("name", $incident["creator"]))?></li><?php } ?>
+ <?php if ($incident["updatedby"] != -1) { ?><li><b>Última modificación por:</b> <?=security::htmlsafe(people::userData("name", $incident["updatedby"]))?></li><?php } ?>
+ <?php if ($incident["confirmedby"] != -1) { ?><li><b>Revisor:</b> <?=security::htmlsafe(people::userData("name", $incident["confirmedby"]))?></li><?php } ?>
+ <?php
+ if ($incident["state"] == incidents::STATE_VALIDATED_BY_WORKER) {
+ $validation = json_decode($incident["workervalidation"], true);
+ validationsView::renderValidationInfo($validation);
+ }
+ ?>
+ </ul>
+</div>
+<div class="mdl-dialog__actions">
+ <button data-dyndialog-close class="mdl-button mdl-js-button mdl-js-ripple-effect mdl-button--accent">Cerrar</button>
+</div>
diff --git a/src/dynamic/authorsrecord.php b/src/dynamic/authorsrecord.php
new file mode 100644
index 0000000..8888af3
--- /dev/null
+++ b/src/dynamic/authorsrecord.php
@@ -0,0 +1,43 @@
+<?php
+require_once(__DIR__."/../core.php");
+security::checkType(security::WORKER, security::METHOD_NOTFOUND);
+security::checkWorkerUIEnabled();
+
+$isAdmin = security::isAllowed(security::ADMIN);
+
+if (!isset($_GET["id"])) {
+ security::notFound();
+}
+
+$id = (int)$_GET["id"];
+
+$record = registry::get($id, true);
+if ($record === false) security::notFound();
+
+if (!$isAdmin) registry::checkRecordIsFromPerson($record["id"]);
+?>
+
+<style>
+#dynDialog {
+ max-width: 380px;
+ width: auto;
+}
+</style>
+
+<h4 class="mdl-dialog__title">Autoría del elemento del registro</h4>
+<div class="mdl-dialog__content">
+ <ul>
+ <li><b>Creador:</b> <?=($record["creator"] == -1 ? "<span style='font-family: monospace;'>cron</span>" : security::htmlsafe(people::userData("name", $record["creator"])))?></li>
+ <li><b>Fecha de creación:</b> <?=date("d/m/Y H:i", $record["created"])?></li>
+ <?php if ($record["invalidatedby"] != -1) { ?><li><b>Invalidado por:</b> <?=security::htmlsafe(people::userData("name", $record["invalidatedby"]))?></li><?php } ?>
+ <?php
+ if ($record["state"] == registry::STATE_VALIDATED_BY_WORKER) {
+ $validation = json_decode($record["workervalidation"], true);
+ validationsView::renderValidationInfo($validation);
+ }
+ ?>
+ </ul>
+</div>
+<div class="mdl-dialog__actions">
+ <button data-dyndialog-close class="mdl-button mdl-js-button mdl-js-ripple-effect mdl-button--accent">Cerrar</button>
+</div>
diff --git a/src/dynamic/companyuser.php b/src/dynamic/companyuser.php
new file mode 100644
index 0000000..0acfac3
--- /dev/null
+++ b/src/dynamic/companyuser.php
@@ -0,0 +1,102 @@
+<?php
+require_once(__DIR__."/../core.php");
+security::checkType(security::ADMIN, security::METHOD_NOTFOUND);
+
+if (!isset($_GET["id"])) {
+ security::notFound();
+}
+
+$p = people::get($_GET["id"]);
+
+if ($p === false) {
+ security::notFound();
+}
+
+$companies = companies::getAll();
+?>
+
+<style>
+.mdl-dialog__content {
+ color: rgba(0,0,0,.87)!important;
+}
+
+#dynDialog {
+ max-width: 300px;
+ width: auto;
+}
+</style>
+
+<dynscript>
+var person = <?=(int)$p["id"]?>;
+
+document.querySelectorAll("button[data-company-id]").forEach(btn => {
+ btn.addEventListener("click", e => {
+ var id = e.currentTarget.getAttribute("data-company-id");
+ fetch("ajax/addpersontocompany.php", {
+ method: "post",
+ body: "person="+parseInt(person)+"&company="+parseInt(id),
+ headers: {
+ "Content-Type": "application/x-www-form-urlencoded"
+ }
+ }).then(response => {
+ response.text().then(text => console.log);
+ dynDialog.reload();
+ }).catch(error => {
+ alert("Ha habido un error dando de alta a este trabajador de esta empresa: "+error);
+ });
+ });
+});
+
+document.querySelectorAll("button[data-worker-id]").forEach(btn => {
+ btn.addEventListener("click", e => {
+ var id = e.currentTarget.getAttribute("data-worker-id");
+ dynDialog.load("dynamic/workhistory.php?id="+parseInt(id));
+ });
+});
+</dynscript>
+
+<h4 class="mdl-dialog__title"><?=security::htmlsafe($p["name"])?></h4>
+<div class="mdl-dialog__content">
+<?php
+$list = [];
+$list["visible"] = "";
+$list["hidden"] = "";
+
+$workers = workers::getPersonWorkers($p["id"]);
+foreach ($workers as $w) {
+ $list[($w["hidden"] ? "hidden" : "visible")] .= '<li>'.
+ security::htmlsafe($companies[$w["company"]]).'
+ <button class="mdl-button mdl-js-button mdl-button--icon" title="Acceder al historial de altas y bajas" data-worker-id="'.(int)$w["id"].'">
+ <i class="material-icons">history</i>
+ </button>
+ <br>
+ <span class="mdl-color-text--grey-600">'.($w["hidden"] ? "Dada de baja" : "Dada de alta").' el '.date("d/m/Y", $w["lastupdated"]).'</span></li>';
+}
+?>
+ <p><b>Dada de alta en:</b></p>
+ <?php
+ if (!empty($list["visible"])) {
+ echo "<ul>".$list["visible"]."</ul>";
+ }
+ ?>
+ <p><b>Dada de baja en:</b></p>
+ <?php
+ if (!empty($list["hidden"])) {
+ echo "<ul>".$list["hidden"]."</ul>";
+ }
+ ?>
+ <p><b>No dada de alta en:</b></p>
+ <ul>
+ <?php
+ foreach ($companies as $id => $name) {
+ if (in_array($id, $p["companies"])) continue;
+ ?>
+ <li><?=security::htmlsafe($name)?> <button class="mdl-button mdl-js-button mdl-button--icon mdl-color-text--green" title="Dar de alta en esta empresa" data-company-id="<?=(int)$id?>"><i class="material-icons">add</i></button></li>
+ <?php
+ }
+ ?>
+ </ul>
+</div>
+<div class="mdl-dialog__actions">
+ <button data-dyndialog-close class="mdl-button mdl-js-button mdl-js-ripple-effect cancel">Cerrar</button>
+</div>
diff --git a/src/dynamic/copytemplate.php b/src/dynamic/copytemplate.php
new file mode 100644
index 0000000..0f127ba
--- /dev/null
+++ b/src/dynamic/copytemplate.php
@@ -0,0 +1,52 @@
+<?php
+require_once(__DIR__."/../core.php");
+security::checkType(security::ADMIN, security::METHOD_NOTFOUND);
+
+if (!security::checkParams("GET", [
+ ["workers", security::PARAM_ISARRAY]
+])) {
+ security::notFound();
+}
+?>
+
+<form action="docopytemplate.php" method="POST" autocomplete="off">
+ <h4 class="mdl-dialog__title">Copiar plantilla a trabajadores</h4>
+ <div class="mdl-dialog__content">
+ <div class="mdlext-selectfield mdlext-js-selectfield mdlext-selectfield--floating-label">
+ <select name="template" id="template" class="mdlext-selectfield__select" data-required>
+ <?php
+ $templates = schedules::getTemplates();
+ foreach ($templates as $t) {
+ echo '<option value="'.(int)$t["id"].'">'.security::htmlsafe($t["name"]).'</option>';
+ }
+ ?>
+ </select>
+ <label for="template" class="mdlext-selectfield__label">Plantilla</label>
+ </div>
+ <br>
+ <label class="mdl-switch mdl-js-switch mdl-js-ripple-effect" for="active">
+ <input type="checkbox" id="active" name="active" value="1" class="mdl-switch__input">
+ <span class="mdl-switch__label">Activar horario</span>
+ </label>
+ <br><br>
+ <b>Copiar a:</b>
+ <div class="copyto">
+ <ul>
+ <?php
+ foreach ($_GET["workers"] as $workerid) {
+ $worker = workers::get($workerid);
+ if ($worker === false) {
+ die("Error: Uno de los trabajadores seleccionados ya no existe");
+ }
+
+ echo "<li><input type='hidden' name='workers[]' value='".(int)$worker["id"]."'> ".security::htmlsafe($worker["name"])." (".security::htmlsafe($worker["companyname"]).")</li>";
+ }
+ ?>
+ </ul>
+ </div>
+ </div>
+ <div class="mdl-dialog__actions">
+ <button type="submit" class="mdl-button mdl-js-button mdl-js-ripple-effect mdl-button--accent">Copiar</button>
+ <button data-dyndialog-close class="mdl-button mdl-js-button mdl-js-ripple-effect cancel">Cancelar</button>
+ </div>
+</form>
diff --git a/src/dynamic/deleteattachment.php b/src/dynamic/deleteattachment.php
new file mode 100644
index 0000000..66b6b1f
--- /dev/null
+++ b/src/dynamic/deleteattachment.php
@@ -0,0 +1,49 @@
+<?php
+require_once(__DIR__."/../core.php");
+security::checkType(security::WORKER, security::METHOD_NOTFOUND);
+security::checkWorkerUIEnabled();
+
+if (!security::checkParams("GET", [
+ ["id", security::PARAM_ISINT],
+ ["name", security::PARAM_NEMPTY]
+])) {
+ security::notFound();
+}
+
+$id = (int)$_GET["id"];
+$name = $_GET["name"];
+
+$incident = incidents::get($id, true);
+if ($incident === false) security::notFound();
+
+if (!security::isAllowed(security::ADMIN)) incidents::checkIncidentIsFromPerson($incident["id"]);
+
+$attachments = incidents::getAttachmentsFromIncident($incident);
+
+if ($attachments === false || !count($attachments)) security::notFound();
+
+$flag = false;
+
+foreach ($attachments as $attachment) {
+ if ($attachment == $name) {
+ $flag = true;
+ ?>
+ <form action="dodeleteattachment.php" method="POST" autocomplete="off">
+ <input type="hidden" name="id" value="<?=(int)$id?>">
+ <?php visual::addContinueInput(); ?>
+ <input type="hidden" name="name" value="<?=security::htmlsafe($name)?>">
+ <h4 class="mdl-dialog__title">Eliminar archivo adjunto</h4>
+ <div class="mdl-dialog__content">
+ <p>¿Estás seguro que quieres eliminar el archivo adjunto <code><?=security::htmlsafe($name)?></code>? <span style="color:#EF5350;font-weight:bold;">Esta acción es irreversible</span></p>
+ </div>
+ <div class="mdl-dialog__actions">
+ <button type="submit" class="mdl-button mdl-js-button mdl-js-ripple-effect mdl-button--accent">Eliminar</button>
+ <button data-dyndialog-close class="mdl-button mdl-js-button mdl-js-ripple-effect cancel">Cancelar</button>
+ </div>
+ </form>
+ <?php
+ break;
+ }
+}
+
+if ($flag === false) security::notFound();
diff --git a/src/dynamic/deletecalendar.php b/src/dynamic/deletecalendar.php
new file mode 100644
index 0000000..83f19cc
--- /dev/null
+++ b/src/dynamic/deletecalendar.php
@@ -0,0 +1,26 @@
+<?php
+require_once(__DIR__."/../core.php");
+security::checkType(security::ADMIN, security::METHOD_NOTFOUND);
+
+if (!isset($_GET["id"])) {
+ security::notFound();
+}
+
+$id = (int)$_GET["id"];
+
+if (!calendars::exists($id)) {
+ security::notFound();
+}
+?>
+
+<form action="dodeletecalendar.php" method="POST" autocomplete="off">
+ <input type="hidden" name="id" value="<?=(int)$id?>">
+ <h4 class="mdl-dialog__title">Eliminar calendario</h4>
+ <div class="mdl-dialog__content">
+ <p>¿Estás seguro que quieres eliminar el calendario? <span style="color:#EF5350;font-weight:bold;">Esta acción es irreversible</span></p>
+ </div>
+ <div class="mdl-dialog__actions">
+ <button type="submit" class="mdl-button mdl-js-button mdl-js-ripple-effect mdl-button--accent">Eliminar</button>
+ <button data-dyndialog-close class="mdl-button mdl-js-button mdl-js-ripple-effect cancel">Cancelar</button>
+ </div>
+</form>
diff --git a/src/dynamic/deleteday.php b/src/dynamic/deleteday.php
new file mode 100644
index 0000000..d22206a
--- /dev/null
+++ b/src/dynamic/deleteday.php
@@ -0,0 +1,26 @@
+<?php
+require_once(__DIR__."/../core.php");
+security::checkType(security::ADMIN, security::METHOD_NOTFOUND);
+
+if (!isset($_GET["id"])) {
+ security::notFound();
+}
+
+$id = (int)$_GET["id"];
+
+if (!schedules::dayExists($id)) {
+ security::notFound();
+}
+?>
+
+<form action="dodeleteday.php" method="POST" autocomplete="off">
+ <input type="hidden" name="id" value="<?=(int)$id?>">
+ <h4 class="mdl-dialog__title">Eliminar horario</h4>
+ <div class="mdl-dialog__content">
+ <p>¿Estás seguro que quieres eliminar este horario diario? <span style="color:#EF5350;font-weight:bold;">Esta acción es irreversible</span></p>
+ </div>
+ <div class="mdl-dialog__actions">
+ <button type="submit" class="mdl-button mdl-js-button mdl-js-ripple-effect mdl-button--accent">Eliminar</button>
+ <button data-dyndialog-close class="mdl-button mdl-js-button mdl-js-ripple-effect cancel">Cancelar</button>
+ </div>
+</form>
diff --git a/src/dynamic/deleteincident.php b/src/dynamic/deleteincident.php
new file mode 100644
index 0000000..8ba455c
--- /dev/null
+++ b/src/dynamic/deleteincident.php
@@ -0,0 +1,31 @@
+<?php
+require_once(__DIR__."/../core.php");
+security::checkType(security::WORKER, security::METHOD_NOTFOUND);
+security::checkWorkerUIEnabled();
+
+if (!isset($_GET["id"])) {
+ security::notFound();
+}
+
+$id = (int)$_GET["id"];
+
+$incident = incidents::get($id);
+if ($incident === false) security::notFound();
+
+$isAdmin = security::isAdminView();
+$status = incidents::getStatus($incident);
+if (($isAdmin && !in_array($status, incidents::$canRemoveStates)) || (!$isAdmin && !in_array($status, incidents::$workerCanRemoveStates))) security::notFound();
+?>
+
+<form action="dodeleteincident.php" method="POST" autocomplete="off">
+ <input type="hidden" name="id" value="<?=(int)$id?>">
+ <?php visual::addContinueInput(); ?>
+ <h4 class="mdl-dialog__title">Eliminar incidencia</h4>
+ <div class="mdl-dialog__content">
+ <p>¿Estás seguro que quieres eliminar esta incidencia? <span style="color:#EF5350;font-weight:bold;">Esta acción es irreversible</span></p>
+ </div>
+ <div class="mdl-dialog__actions">
+ <button type="submit" class="mdl-button mdl-js-button mdl-js-ripple-effect mdl-button--accent">Eliminar</button>
+ <button data-dyndialog-close class="mdl-button mdl-js-button mdl-js-ripple-effect cancel">Cancelar</button>
+ </div>
+</form>
diff --git a/src/dynamic/deleteincidentsbulk.php b/src/dynamic/deleteincidentsbulk.php
new file mode 100644
index 0000000..b3a397c
--- /dev/null
+++ b/src/dynamic/deleteincidentsbulk.php
@@ -0,0 +1,33 @@
+<?php
+require_once(__DIR__."/../core.php");
+security::checkType(security::ADMIN, security::METHOD_NOTFOUND);
+
+if (!security::checkParams("GET", [
+ ["incidents", security::PARAM_ISARRAY]
+])) {
+ security::notFound();
+}
+?>
+
+<style>
+#dynDialog, #dynDialog .mdl-dialog__content {
+ background: #FFCDD2;
+}
+</style>
+
+<form action="dodeleteincidentsbulk.php" method="POST" autocomplete="off">
+ <?php
+ foreach ($_GET["incidents"] as $incident) {
+ echo "<input type='hidden' name='incidents[]' value='".(int)$incident."'></li>";
+ }
+ ?>
+ <h4 class="mdl-dialog__title">Eliminar/invalidar incidencias</h4>
+ <div class="mdl-dialog__content">
+ <p>¿Estás seguro que quieres eliminar/invalidar estas incidencias? <span style="color:#EF5350;font-weight:bold;">Esta acción es irreversible</span></p>
+ <p>Dependiendo del estado de cada incidencia, esta se eliminará o se invalidará.</p>
+ </div>
+ <div class="mdl-dialog__actions">
+ <button type="submit" class="mdl-button mdl-js-button mdl-js-ripple-effect mdl-button--accent">Eliminar</button>
+ <button data-dyndialog-close class="mdl-button mdl-js-button mdl-js-ripple-effect cancel">Cancelar</button>
+ </div>
+</form>
diff --git a/src/dynamic/deleteschedule.php b/src/dynamic/deleteschedule.php
new file mode 100644
index 0000000..52c14f9
--- /dev/null
+++ b/src/dynamic/deleteschedule.php
@@ -0,0 +1,26 @@
+<?php
+require_once(__DIR__."/../core.php");
+security::checkType(security::ADMIN, security::METHOD_NOTFOUND);
+
+if (!isset($_GET["id"])) {
+ security::notFound();
+}
+
+$id = (int)$_GET["id"];
+
+if (!schedules::exists($id)) {
+ security::notFound();
+}
+?>
+
+<form action="dodeleteschedule.php" method="POST" autocomplete="off">
+ <input type="hidden" name="id" value="<?=(int)$id?>">
+ <h4 class="mdl-dialog__title">Eliminar horario</h4>
+ <div class="mdl-dialog__content">
+ <p>¿Estás seguro que quieres eliminar este horario? <span style="color:#EF5350;font-weight:bold;">Esta acción es irreversible</span></p>
+ </div>
+ <div class="mdl-dialog__actions">
+ <button type="submit" class="mdl-button mdl-js-button mdl-js-ripple-effect mdl-button--accent">Eliminar</button>
+ <button data-dyndialog-close class="mdl-button mdl-js-button mdl-js-ripple-effect cancel">Cancelar</button>
+ </div>
+</form>
diff --git a/src/dynamic/deletescheduletemplate.php b/src/dynamic/deletescheduletemplate.php
new file mode 100644
index 0000000..ad3a1ae
--- /dev/null
+++ b/src/dynamic/deletescheduletemplate.php
@@ -0,0 +1,26 @@
+<?php
+require_once(__DIR__."/../core.php");
+security::checkType(security::ADMIN, security::METHOD_NOTFOUND);
+
+if (!isset($_GET["id"])) {
+ security::notFound();
+}
+
+$id = (int)$_GET["id"];
+
+if (!schedules::templateExists($id)) {
+ security::notFound();
+}
+?>
+
+<form action="dodeletescheduletemplate.php" method="POST" autocomplete="off">
+ <input type="hidden" name="id" value="<?=(int)$id?>">
+ <h4 class="mdl-dialog__title">Eliminar plantilla</h4>
+ <div class="mdl-dialog__content">
+ <p>¿Estás seguro que quieres eliminar esta plantilla? <span style="color:#EF5350;font-weight:bold;">Esta acción es irreversible</span></p>
+ </div>
+ <div class="mdl-dialog__actions">
+ <button type="submit" class="mdl-button mdl-js-button mdl-js-ripple-effect mdl-button--accent">Eliminar</button>
+ <button data-dyndialog-close class="mdl-button mdl-js-button mdl-js-ripple-effect cancel">Cancelar</button>
+ </div>
+</form>
diff --git a/src/dynamic/deletesecuritykey.php b/src/dynamic/deletesecuritykey.php
new file mode 100644
index 0000000..8b62609
--- /dev/null
+++ b/src/dynamic/deletesecuritykey.php
@@ -0,0 +1,24 @@
+<?php
+require_once(__DIR__."/../core.php");
+security::checkType(security::WORKER, security::METHOD_NOTFOUND);
+security::checkWorkerUIEnabled();
+secondFactor::checkAvailability();
+
+if (!isset($_GET["id"])) security::notFound();
+$id = (int)$_GET["id"];
+
+$s = secondFactor::getSecurityKeyById($id);
+if ($s === false || people::userData("id") != $s["person"]) security::notFound();
+?>
+
+<form action="dodeletesecuritykey.php" method="POST" autocomplete="off">
+ <input type="hidden" name="id" value="<?=(int)$s["id"]?>">
+ <h4 class="mdl-dialog__title">Eliminar llave de seguridad</h4>
+ <div class="mdl-dialog__content">
+ <p>¿Estás seguro que quieres eliminar la llave de seguridad <b><?=security::htmlsafe($s["name"])?></b>? <span style="color:#EF5350;font-weight:bold;">Una vez la elimines no tendrás la opción de escogerla como segundo factor.</span></p>
+ </div>
+ <div class="mdl-dialog__actions">
+ <button type="submit" class="mdl-button mdl-js-button mdl-js-ripple-effect mdl-button--accent">Eliminar</button>
+ <button data-dyndialog-close class="mdl-button mdl-js-button mdl-js-ripple-effect cancel">Cancelar</button>
+ </div>
+</form>
diff --git a/src/dynamic/deletetemplateday.php b/src/dynamic/deletetemplateday.php
new file mode 100644
index 0000000..63e0cb8
--- /dev/null
+++ b/src/dynamic/deletetemplateday.php
@@ -0,0 +1,26 @@
+<?php
+require_once(__DIR__."/../core.php");
+security::checkType(security::ADMIN, security::METHOD_NOTFOUND);
+
+if (!isset($_GET["id"])) {
+ security::notFound();
+}
+
+$id = (int)$_GET["id"];
+
+if (!schedules::templateDayExists($id)) {
+ security::notFound();
+}
+?>
+
+<form action="dodeletetemplateday.php" method="POST" autocomplete="off">
+ <input type="hidden" name="id" value="<?=(int)$id?>">
+ <h4 class="mdl-dialog__title">Eliminar horario</h4>
+ <div class="mdl-dialog__content">
+ <p>¿Estás seguro que quieres eliminar este horario de la plantilla? <span style="color:#EF5350;font-weight:bold;">Esta acción es irreversible</span></p>
+ </div>
+ <div class="mdl-dialog__actions">
+ <button type="submit" class="mdl-button mdl-js-button mdl-js-ripple-effect mdl-button--accent">Eliminar</button>
+ <button data-dyndialog-close class="mdl-button mdl-js-button mdl-js-ripple-effect cancel">Cancelar</button>
+ </div>
+</form>
diff --git a/src/dynamic/deleteworkhistoryitem.php b/src/dynamic/deleteworkhistoryitem.php
new file mode 100644
index 0000000..0dd5613
--- /dev/null
+++ b/src/dynamic/deleteworkhistoryitem.php
@@ -0,0 +1,44 @@
+<?php
+require_once(__DIR__."/../core.php");
+security::checkType(security::ADMIN, security::METHOD_NOTFOUND);
+
+if (!isset($_GET["id"])) {
+ security::notFound();
+}
+
+$id = (int)$_GET["id"];
+
+$item = workers::getWorkHistoryItem($id);
+if ($item === false) security::notFound();
+
+$isHidden = workers::isHidden($item["status"]);
+
+$worker = workers::get($item["worker"]);
+if ($worker === false) security::notFound();
+
+$helper = security::htmlsafe(strtolower(workers::affiliationStatusHelper($item["status"])));
+?>
+
+<dynscript>
+document.getElementById("cancel").addEventListener("click", e => {
+ e.preventDefault();
+ dynDialog.load("dynamic/workhistory.php?id="+parseInt(document.getElementById("cancel").getAttribute("data-worker-id")));
+});
+</dynscript>
+
+<form action="dodeleteworkhistoryitem.php" method="POST" autocomplete="off">
+ <input type="hidden" name="id" value="<?=(int)$id?>">
+ <h4 class="mdl-dialog__title">Eliminar registro de <?=security::htmlsafe($helper)?></h4>
+
+ <div class="mdl-dialog__content">
+ <p><b>Persona:</b> <?=security::htmlsafe($worker["name"])?><br>
+ <b>Empresa:</b> <?=security::htmlsafe($worker["companyname"])?><br>
+ <b>Día:</b> <?=security::htmlsafe(date("d/m/Y", $item["day"]))?></p>
+
+ <p>¿Estás seguro que quieres eliminar este registro de <?=security::htmlsafe($helper)?>? <span style="color:#EF5350;font-weight:bold;">Esta acción es irreversible</span></p>
+ </div>
+ <div class="mdl-dialog__actions">
+ <button type="submit" class="mdl-button mdl-js-button mdl-js-ripple-effect mdl-button--accent">Eliminar</button>
+ <button id="cancel" class="mdl-button mdl-js-button mdl-js-ripple-effect" data-worker-id="<?=(int)$worker["id"]?>">Cancelar</button>
+ </div>
+</form>
diff --git a/src/dynamic/disablesecondfactor.php b/src/dynamic/disablesecondfactor.php
new file mode 100644
index 0000000..ad9913e
--- /dev/null
+++ b/src/dynamic/disablesecondfactor.php
@@ -0,0 +1,55 @@
+<?php
+require_once(__DIR__."/../core.php");
+security::checkType(security::WORKER, security::METHOD_NOTFOUND);
+security::checkWorkerUIEnabled();
+secondFactor::checkAvailability();
+
+if (!isset($_GET["id"])) security::notFound();
+$id = (int)$_GET["id"];
+
+if (!secondFactor::isEnabled($id)) {
+ security::notFound();
+}
+
+if (!security::isAllowed(security::ADMIN) && $id != people::userData("id")) security::notFound();
+
+if ($id == people::userData("id")) {
+?>
+<style>
+#dynDialog {
+ max-width: 500px;
+ width: auto;
+}
+</style>
+<?php
+}
+?>
+
+<form action="dodisablesecondfactor.php" method="POST" autocomplete="off">
+ <input type="hidden" name="id" value="<?=(int)$id?>">
+ <h4 class="mdl-dialog__title">Desactivar la verificación en dos pasos</h4>
+ <div class="mdl-dialog__content">
+ <?php
+ if ($id != people::userData("id")) {
+ ?>
+ <p>¿Estás seguro que quieres desactivar la verificación en dos pasos para <b><?=security::htmlsafe(people::userData("name", $id))?></b>?</p>
+ <p>Esta acción solo debe tomarse cuando el trabajador no puede acceder a su cuenta, puesto que <span style="color:#EF5350;font-weight:bold;">esta acción solo la puede revertir el trabajador reactivando de nuevo la verificación en dos pasos</span></p>
+ <?php
+ } else {
+ ?>
+ <p>¿Estás seguro que quieres desactivar la verificación en dos pasos?</p>
+ <p>La verificación en 2 pasos ofrece seguridad extra a tu cuenta. Si inhabilitas la verificación en 2 pasos todas tus llaves de seguridad se desvincularán de esta cuenta y no te pediremos ningún código de verificación al iniciar sesión.</p>
+ <p>Si aun así sigues quiriendo desactivarla, introduce tu contraseña y haz clic en el botón Desactivar.</p>
+ <div class="mdl-textfield mdl-js-textfield mdl-textfield--floating-label">
+ <input class="mdl-textfield__input" type="password" name="password" id="password" data-required>
+ <label class="mdl-textfield__label" for="password">Contraseña actual</label>
+ </div>
+ <?php
+ }
+ ?>
+ </div>
+ <div class="mdl-dialog__actions">
+ <button type="submit" class="mdl-button mdl-js-button mdl-js-ripple-effect mdl-button--accent">Desactivar</button>
+ <button data-dyndialog-close class="mdl-button mdl-js-button mdl-js-ripple-effect cancel">Cancelar</button>
+ </div>
+</form>
diff --git a/src/dynamic/editcategory.php b/src/dynamic/editcategory.php
new file mode 100644
index 0000000..1057df1
--- /dev/null
+++ b/src/dynamic/editcategory.php
@@ -0,0 +1,53 @@
+<?php
+require_once(__DIR__."/../core.php");
+security::checkType(security::ADMIN, security::METHOD_NOTFOUND);
+
+if (!isset($_GET["id"])) {
+ security::notFound();
+}
+
+$c = categories::get($_GET["id"]);
+
+if ($c === false) {
+ security::notFound();
+}
+?>
+
+<form action="doeditcategory.php" method="POST" autocomplete="off">
+ <h4 class="mdl-dialog__title">Editar categoría</h4>
+ <div class="mdl-dialog__content">
+ <div class="mdl-textfield mdl-js-textfield mdl-textfield--floating-label">
+ <input class="mdl-textfield__input" type="text" name="id" id="edit_id" value="<?=security::htmlsafe($c['id'])?>" readonly="readonly" autocomplete="off">
+ <label class="mdl-textfield__label" for="edit_nombre">ID</label>
+ </div>
+ <br>
+ <div class="mdl-textfield mdl-js-textfield mdl-textfield--floating-label">
+ <input class="mdl-textfield__input" type="text" name="name" id="edit_name" value="<?=security::htmlsafe($c['name'])?>" autocomplete="off" data-required>
+ <label class="mdl-textfield__label" for="edit_name">Nombre de la categoría</label>
+ </div>
+ <br>
+ <div class="mdlext-selectfield mdlext-js-selectfield mdlext-selectfield--floating-label">
+ <select name="parent" id="parent" class="mdlext-selectfield__select">
+ <option value="0"></option>
+ <?php
+ foreach (categories::getAll(false) as $category) {
+ if ($category["parent"] == 0 && $category["id"] != $c["id"]) {
+ echo '<option value="'.$category["id"].'"'.($c["parent"] == $category["id"] ? "selected" : "").'>'.$category["name"].'</option>';
+ }
+ }
+ ?>
+ </select>
+ <label for="parent" class="mdlext-selectfield__label">Categoría padre</label>
+ </div>
+ <br>
+ <div class="mdl-textfield mdl-js-textfield mdl-textfield--floating-label">
+ <textarea class="mdl-textfield__input" name="emails" id="emails"><?=security::htmlsafe(categories::readableEmails($c["emails"]))?></textarea>
+ <label class="mdl-textfield__label" for="emails">Correos electrónicos de los responsables</label>
+ </div>
+ <span style="font-size: 12px;">Introduce los correos separados por comas.</span>
+ </div>
+ <div class="mdl-dialog__actions">
+ <button type="submit" class="mdl-button mdl-js-button mdl-js-ripple-effect mdl-button--accent">Confirmar</button>
+ <button data-dyndialog-close class="mdl-button mdl-js-button mdl-js-ripple-effect cancel">Cancelar</button>
+ </div>
+</form>
diff --git a/src/dynamic/editcompany.php b/src/dynamic/editcompany.php
new file mode 100644
index 0000000..1af315e
--- /dev/null
+++ b/src/dynamic/editcompany.php
@@ -0,0 +1,38 @@
+<?php
+require_once(__DIR__."/../core.php");
+security::checkType(security::ADMIN, security::METHOD_NOTFOUND);
+
+if (!isset($_GET["id"])) {
+ security::notFound();
+}
+
+$c = companies::get($_GET["id"]);
+
+if ($c === false) {
+ security::notFound();
+}
+?>
+
+<form action="doeditcompany.php" method="POST" autocomplete="off">
+ <h4 class="mdl-dialog__title">Editar empresa</h4>
+ <div class="mdl-dialog__content">
+ <div class="mdl-textfield mdl-js-textfield mdl-textfield--floating-label">
+ <input class="mdl-textfield__input" type="text" name="id" id="edit_id" value="<?=security::htmlsafe($c['id'])?>" readonly="readonly" autocomplete="off">
+ <label class="mdl-textfield__label" for="edit_nombre">ID</label>
+ </div>
+ <br>
+ <div class="mdl-textfield mdl-js-textfield mdl-textfield--floating-label">
+ <input class="mdl-textfield__input" type="text" name="name" id="edit_name" value="<?=security::htmlsafe($c['name'])?>" autocomplete="off" data-required>
+ <label class="mdl-textfield__label" for="edit_name">Nombre de la empresa</label>
+ </div>
+ <br>
+ <div class="mdl-textfield mdl-js-textfield mdl-textfield--floating-label">
+ <input class="mdl-textfield__input" type="text" name="cif" id="edit_cif" value="<?=security::htmlsafe($c['cif'])?>" autocomplete="off">
+ <label class="mdl-textfield__label" for="edit_cif">CIF (opcional)</label>
+ </div>
+ </div>
+ <div class="mdl-dialog__actions">
+ <button type="submit" class="mdl-button mdl-js-button mdl-js-ripple-effect mdl-button--accent">Confirmar</button>
+ <button data-dyndialog-close class="mdl-button mdl-js-button mdl-js-ripple-effect cancel">Cancelar</button>
+ </div>
+</form>
diff --git a/src/dynamic/editday.php b/src/dynamic/editday.php
new file mode 100644
index 0000000..3829b47
--- /dev/null
+++ b/src/dynamic/editday.php
@@ -0,0 +1,65 @@
+<?php
+require_once(__DIR__."/../core.php");
+security::checkType(security::ADMIN, security::METHOD_NOTFOUND);
+
+if (!isset($_GET["id"])) {
+ security::notFound();
+}
+
+$id = (int)$_GET["id"];
+
+$day = schedules::getDay($id);
+
+if ($day === false) {
+ security::notFound();
+}
+
+$empty = [];
+
+foreach (schedules::$allEvents as $date) {
+ $empty[$date] = (intervals::measure([$day["begins".$date], $day["ends".$date]]) == 0);
+}
+?>
+
+<form action="doeditdayschedule.php" method="POST" autocomplete="off">
+ <input type="hidden" name="id" value="<?=(int)$day["id"]?>">
+ <h4 class="mdl-dialog__title">Modificar horario</h4>
+ <div class="mdl-dialog__content">
+ <h5>Día</h5>
+ <div class="mdlext-selectfield mdlext-js-selectfield mdlext-selectfield--floating-label">
+ <select id="edit_day" class="mdlext-selectfield__select" disabled>
+ <?php
+ foreach (calendars::$days as $id => $tday) {
+ echo '<option value="'.(int)$id.'"'.($day["day"] == $id ? " selected" : "").'>'.security::htmlsafe($tday).'</option>';
+ }
+ ?>
+ </select>
+ <label for="edit_day" class="mdlext-selectfield__label">Día de la semana</label>
+ </div>
+ <br>
+ <div class="mdlext-selectfield mdlext-js-selectfield mdlext-selectfield--floating-label">
+ <select id="edit_type" class="mdlext-selectfield__select" disabled>
+ <?php
+ foreach (calendars::$types as $id => $type) {
+ if ($id == calendars::TYPE_FESTIU) continue;
+ echo '<option value="'.(int)$id.'"'.($day["typeday"] == $id ? " selected" : "").'>'.security::htmlsafe($type).'</option>';
+ }
+ ?>
+ </select>
+ <label for="edit_type" class="mdlext-selectfield__label">Tipo de día</label>
+ </div>
+
+ <h5>Jornada laboral</h5>
+ <p>De <input type="time" name="beginswork" <?=(!$empty["work"] ? " value='".schedules::sec2time($day["beginswork"])."'" : "")?> required> a <input type="time" name="endswork" <?=(!$empty["work"] ? " value='".schedules::sec2time($day["endswork"])."'" : "")?> required></p>
+
+ <h5>Desayuno</h5>
+ <p>De <input type="time" name="beginsbreakfast" <?=(!$empty["breakfast"] ? " value='".schedules::sec2time($day["beginsbreakfast"])."'" : "")?>> a <input type="time" name="endsbreakfast" <?=(!$empty["breakfast"] ? " value='".schedules::sec2time($day["endsbreakfast"])."'" : "")?>></p>
+
+ <h5>Comida</h5>
+ <p>De <input type="time" name="beginslunch" <?=(!$empty["lunch"] ? " value='".schedules::sec2time($day["beginslunch"])."'" : "")?>> a <input type="time" name="endslunch" <?=(!$empty["lunch"] ? " value='".schedules::sec2time($day["endslunch"])."'" : "")?>></p>
+ </div>
+ <div class="mdl-dialog__actions">
+ <button type="submit" class="mdl-button mdl-js-button mdl-js-ripple-effect mdl-button--primary">Modificar</button>
+ <button data-dyndialog-close class="mdl-button mdl-js-button mdl-js-ripple-effect cancel">Cancelar</button>
+ </div>
+</form>
diff --git a/src/dynamic/editincident.php b/src/dynamic/editincident.php
new file mode 100644
index 0000000..808070e
--- /dev/null
+++ b/src/dynamic/editincident.php
@@ -0,0 +1,68 @@
+<?php
+require_once(__DIR__."/../core.php");
+security::checkType(security::WORKER, security::METHOD_NOTFOUND);
+security::checkWorkerUIEnabled();
+
+if (!isset($_GET["id"])) {
+ security::notFound();
+}
+
+$id = (int)$_GET["id"];
+
+$incident = incidents::get($id, true);
+if ($incident === false) security::notFound();
+
+$isAdmin = security::isAdminView();
+$status = incidents::getStatus($incident);
+
+if (($isAdmin && in_array($status, incidents::$cannotEditStates)) || (!$isAdmin && !in_array($status, incidents::$workerCanEditStates))) security::notFound();
+if (!$isAdmin) incidents::checkIncidentIsFromPerson($incident["id"]);
+?>
+
+<dynscript>
+document.getElementById("edit_allday").addEventListener("change", e => {
+ var partialtime = document.getElementById("edit_partialtime");
+ if (e.target.checked) {
+ partialtime.classList.add("notvisible");
+ } else {
+ partialtime.classList.remove("notvisible");
+ }
+});
+</dynscript>
+
+<form action="doeditincident.php" method="POST" autocomplete="off">
+ <input type="hidden" name="id" value="<?=(int)$id?>">
+ <?php visual::addContinueInput(); ?>
+ <h4 class="mdl-dialog__title">Editar incidencia</h4>
+ <div class="mdl-dialog__content">
+ <div class="mdlext-selectfield mdlext-js-selectfield mdlext-selectfield--floating-label">
+ <select name="type" id="edit_type" class="mdlext-selectfield__select" data-required>
+ <option></option>
+ <?php
+ foreach (incidents::getTypesForm() as $i) {
+ echo '<option value="'.(int)$i["id"].'"'.($i["id"] == $incident["type"] ? " selected" : "").'>'.security::htmlsafe($i["name"]).'</option>';
+ }
+ ?>
+ </select>
+ <label for="edit_type" class="mdlext-selectfield__label">Tipo</label>
+ </div>
+
+ <h5>Afectación</h5>
+ <div class="mdl-textfield mdl-js-textfield mdl-textfield--floating-label">
+ <input class="mdl-textfield__input" type="date" name="day" id="edit_day" autocomplete="off" value="<?=security::htmlsafe(date("Y-m-d", $incident["day"]))?>" data-required>
+ <label class="mdl-textfield__label always-focused" for="edit_day">Día</label>
+ </div>
+ <br>
+ <p>
+ <label class="mdl-switch mdl-js-switch mdl-js-ripple-effect" for="edit_allday">
+ <input type="checkbox" id="edit_allday" name="allday" value="1" class="mdl-switch__input"<?=($incident["allday"] ? " checked" : "")?>>
+ <span class="mdl-switch__label">Día entero</span>
+ </label>
+ </p>
+ <div id="edit_partialtime"<?=($incident["allday"] ? ' class="notvisible"' : '')?>>De <input type="time" name="begins"<?=($incident["allday"] ? '' : ' value="'.schedules::sec2time($incident["begins"]).'"')?>> a <input type="time" name="ends"<?=($incident["allday"] ? '' : ' value="'.schedules::sec2time($incident["ends"]).'"')?>></div>
+ </div>
+ <div class="mdl-dialog__actions">
+ <button type="submit" class="mdl-button mdl-js-button mdl-js-ripple-effect mdl-button--accent">Editar</button>
+ <button data-dyndialog-close class="mdl-button mdl-js-button mdl-js-ripple-effect cancel">Cancelar</button>
+ </div>
+</form>
diff --git a/src/dynamic/editincidentcomment.php b/src/dynamic/editincidentcomment.php
new file mode 100644
index 0000000..8fd9dd6
--- /dev/null
+++ b/src/dynamic/editincidentcomment.php
@@ -0,0 +1,40 @@
+<?php
+require_once(__DIR__."/../core.php");
+security::checkType(security::WORKER, security::METHOD_NOTFOUND);
+security::checkWorkerUIEnabled();
+
+$isAdmin = security::isAllowed(security::ADMIN);
+
+if (!isset($_GET["id"])) {
+ security::notFound();
+}
+
+$id = (int)$_GET["id"];
+
+$incident = incidents::get($id);
+if ($incident === false) security::notFound();
+
+if (!$isAdmin) incidents::checkIncidentIsFromPerson($incident["id"]);
+
+$status = incidents::getStatus($incident);
+$cantedit = (in_array($status, incidents::$cannotEditCommentsStates) || !$isAdmin);
+?>
+
+<form action="<?=(!$isAdmin ? "doeditworkerincidentcomment.php" : "doeditincidentcomment.php")?>" method="POST" autocomplete="off">
+ <input type="hidden" name="id" value="<?=(int)$incident["id"]?>">
+ <h4 class="mdl-dialog__title">Observaciones incidencia</h4>
+ <div class="mdl-dialog__content">
+ <h5>Observaciones</h5>
+ <div class="mdl-textfield mdl-js-textfield mdl-textfield--floating-label">
+ <textarea class="mdl-textfield__input" name="details" id="details"<?=($cantedit ? " disabled" : "")?>><?=security::htmlsafe($incident["details"])?></textarea>
+ <label class="mdl-textfield__label" for="details">Observaciones (opcional)</label>
+ </div>
+
+ <h5>Observaciones del trabajador</h5>
+ <p><?=security::htmlsafe((!empty($incident["workerdetails"]) ? $incident["workerdetails"] : "-"))?></p>
+ </div>
+ <div class="mdl-dialog__actions">
+ <button type="submit" class="mdl-button mdl-js-button mdl-js-ripple-effect mdl-button--primary"<?=($cantedit ? " disabled" : "")?>>Modificar</button>
+ <button data-dyndialog-close class="mdl-button mdl-js-button mdl-js-ripple-effect cancel">Cancelar</button>
+ </div>
+</form>
diff --git a/src/dynamic/editincidenttype.php b/src/dynamic/editincidenttype.php
new file mode 100644
index 0000000..2901a4f
--- /dev/null
+++ b/src/dynamic/editincidenttype.php
@@ -0,0 +1,74 @@
+<?php
+require_once(__DIR__."/../core.php");
+security::checkType(security::ADMIN, security::METHOD_NOTFOUND);
+
+if (!isset($_GET["id"])) {
+ security::notFound();
+}
+
+$t = incidents::getType($_GET["id"]);
+
+if ($t === false) {
+ security::notFound();
+}
+?>
+
+<form action="doeditincidenttype.php" method="POST" autocomplete="off">
+ <h4 class="mdl-dialog__title">Edita tipo de incidencia</h4>
+ <div class="mdl-dialog__content">
+ <div class="mdl-textfield mdl-js-textfield mdl-textfield--floating-label">
+ <input class="mdl-textfield__input" type="text" name="id" id="edit_id" value="<?=security::htmlsafe($t['id'])?>" readonly="readonly" autocomplete="off">
+ <label class="mdl-textfield__label" for="edit_id">ID</label>
+ </div>
+ <br>
+ <div class="mdl-textfield mdl-js-textfield mdl-textfield--floating-label">
+ <input class="mdl-textfield__input" type="text" name="name" id="e_name" value="<?=security::htmlsafe($t['name'])?>" autocomplete="off" data-required>
+ <label class="mdl-textfield__label" for="e_name">Nombre del tipo de incidencia</label>
+ </div>
+ <p>
+ <label class="mdl-switch mdl-js-switch mdl-js-ripple-effect" for="e_present">
+ <input type="checkbox" id="e_present" name="present" value="1" class="mdl-switch__input" <?=($t["present"] ? " checked" : "")?>>
+ <span class="mdl-switch__label">Presente <i class="material-icons help" id="edit_present">help</i></span>
+ </label>
+ <div class="mdl-tooltip" for="edit_present">Márquese si el trabajador está físicamente presente en el espacio de trabajo durante la incidencia.</div>
+ </p>
+ <p>
+ <label class="mdl-switch mdl-js-switch mdl-js-ripple-effect" for="e_paid">
+ <input type="checkbox" id="e_paid" name="paid" value="1" class="mdl-switch__input" <?=($t["paid"] ? " checked" : "")?>>
+ <span class="mdl-switch__label">Remunerada <i class="material-icons help" id="edit_paid">help</i></span>
+ </label>
+ <div class="mdl-tooltip" for="edit_paid">Márquese si el trabajador es remunerado las horas que dura la incidencia.</div>
+ </p>
+ <p>
+ <label class="mdl-switch mdl-js-switch mdl-js-ripple-effect" for="e_workerfill">
+ <input type="checkbox" id="e_workerfill" name="workerfill" value="1" class="mdl-switch__input"<?=($t["workerfill"] ? " checked" : "")?>>
+ <span class="mdl-switch__label">Puede autorrellenarse <i class="material-icons help" id="edit_workerfill">help</i></span>
+ </label>
+ </p>
+ <div class="mdl-tooltip" for="edit_workerfill">Márquese si se permite que el trabajador pueda rellenar una incidencia de este tipo él mismo (con la posterior verificación por parte de un administrador).</div>
+ <p>
+ <label class="mdl-switch mdl-js-switch mdl-js-ripple-effect" for="e_notifies">
+ <input type="checkbox" id="e_notifies" name="notifies" value="1" class="mdl-switch__input"<?=($t["notifies"] ? " checked" : "")?>>
+ <span class="mdl-switch__label">Notifica <i class="material-icons help" id="edit_notifies">help</i></span>
+ </label>
+ <div class="mdl-tooltip" for="edit_notifies">Márquese si la introducción de una incidencia de este tipo notifica por correo electrónico a las personas especificadas en la categoría del trabajador.</div>
+ </p>
+ <p>
+ <label class="mdl-switch mdl-js-switch mdl-js-ripple-effect" for="e_autovalidates">
+ <input type="checkbox" id="e_autovalidates" name="autovalidates" value="1" class="mdl-switch__input"<?=($t["autovalidates"] ? " checked" : "")?>>
+ <span class="mdl-switch__label">Se autovalida <i class="material-icons help" id="edit_autovalidates">help</i></span>
+ </label>
+ <div class="mdl-tooltip" for="edit_autovalidates">Márquese si al introducir una incidencia de este tipo se quiere que se autovalide sin necesidad de ser validada posteriormente por el trabajador.</div>
+ </p>
+ <p>
+ <label class="mdl-switch mdl-js-switch mdl-js-ripple-effect" for="e_hidden">
+ <input type="checkbox" id="e_hidden" name="hidden" value="1" class="mdl-switch__input"<?=($t["hidden"] ? " checked" : "")?>>
+ <span class="mdl-switch__label">Oculto</span>
+ </label>
+ </p>
+ </div>
+ <div class="mdl-dialog__actions">
+ <button type="submit" class="mdl-button mdl-js-button mdl-js-ripple-effect mdl-button--accent">Confirmar</button>
+ <button data-dyndialog-close class="mdl-button mdl-js-button mdl-js-ripple-effect cancel">Cancelar</button>
+ </div>
+</form>
diff --git a/src/dynamic/editschedule.php b/src/dynamic/editschedule.php
new file mode 100644
index 0000000..c8d0b6a
--- /dev/null
+++ b/src/dynamic/editschedule.php
@@ -0,0 +1,34 @@
+<?php
+require_once(__DIR__."/../core.php");
+security::checkType(security::ADMIN, security::METHOD_NOTFOUND);
+
+if (!isset($_GET["id"])) {
+ security::notFound();
+}
+
+$s = schedules::get($_GET["id"]);
+
+if ($s === false) {
+ security::notFound();
+}
+?>
+
+<form action="doeditschedule.php" method="POST" autocomplete="off">
+ <input type="hidden" name="id" value="<?=(int)$s["id"]?>">
+ <h4 class="mdl-dialog__title">Editar horario</h4>
+ <div class="mdl-dialog__content">
+ <div class="mdl-textfield mdl-js-textfield mdl-textfield--floating-label">
+ <input class="mdl-textfield__input" type="date" name="begins" id="begins" autocomplete="off" value="<?=security::htmlsafe(date("Y-m-d", $s["begins"]))?>" data-required>
+ <label class="mdl-textfield__label always-focused" for="begins">Fecha inicio de validez del horario</label>
+ </div>
+ <br>
+ <div class="mdl-textfield mdl-js-textfield mdl-textfield--floating-label">
+ <input class="mdl-textfield__input" type="date" name="ends" id="ends" autocomplete="off" value="<?=security::htmlsafe(date("Y-m-d", $s["ends"]))?>" data-required>
+ <label class="mdl-textfield__label always-focused" for="ends">Fecha fin de validez del horario</label>
+ </div>
+ </div>
+ <div class="mdl-dialog__actions">
+ <button type="submit" class="mdl-button mdl-js-button mdl-js-ripple-effect mdl-button--accent">Confirmar</button>
+ <button data-dyndialog-close class="mdl-button mdl-js-button mdl-js-ripple-effect cancel">Cancelar</button>
+ </div>
+</form>
diff --git a/src/dynamic/editscheduletemplate.php b/src/dynamic/editscheduletemplate.php
new file mode 100644
index 0000000..3c67d74
--- /dev/null
+++ b/src/dynamic/editscheduletemplate.php
@@ -0,0 +1,39 @@
+<?php
+require_once(__DIR__."/../core.php");
+security::checkType(security::ADMIN, security::METHOD_NOTFOUND);
+
+if (!isset($_GET["id"])) {
+ security::notFound();
+}
+
+$t = schedules::getTemplate($_GET["id"]);
+
+if ($t === false) {
+ security::notFound();
+}
+?>
+
+<form action="doeditscheduletemplate.php" method="POST" autocomplete="off">
+ <input type="hidden" name="id" value="<?=(int)$t["id"]?>">
+ <h4 class="mdl-dialog__title">Editar plantilla</h4>
+ <div class="mdl-dialog__content">
+ <div class="mdl-textfield mdl-js-textfield mdl-textfield--floating-label">
+ <input class="mdl-textfield__input" type="text" name="name" id="name" autocomplete="off" value="<?=security::htmlsafe($t["name"])?>" data-required>
+ <label class="mdl-textfield__label" for="name">Nombre de la plantilla</label>
+ </div>
+ <br>
+ <div class="mdl-textfield mdl-js-textfield mdl-textfield--floating-label">
+ <input class="mdl-textfield__input" type="date" name="begins" id="begins" autocomplete="off" value="<?=security::htmlsafe(date("Y-m-d", $t["begins"]))?>" data-required>
+ <label class="mdl-textfield__label always-focused" for="begins">Fecha inicio de validez del horario</label>
+ </div>
+ <br>
+ <div class="mdl-textfield mdl-js-textfield mdl-textfield--floating-label">
+ <input class="mdl-textfield__input" type="date" name="ends" id="ends" autocomplete="off" value="<?=security::htmlsafe(date("Y-m-d", $t["ends"]))?>" data-required>
+ <label class="mdl-textfield__label always-focused" for="ends">Fecha fin de validez del horario</label>
+ </div>
+ </div>
+ <div class="mdl-dialog__actions">
+ <button type="submit" class="mdl-button mdl-js-button mdl-js-ripple-effect mdl-button--accent">Confirmar</button>
+ <button data-dyndialog-close class="mdl-button mdl-js-button mdl-js-ripple-effect cancel">Cancelar</button>
+ </div>
+</form>
diff --git a/src/dynamic/edittemplateday.php b/src/dynamic/edittemplateday.php
new file mode 100644
index 0000000..136ff1a
--- /dev/null
+++ b/src/dynamic/edittemplateday.php
@@ -0,0 +1,65 @@
+<?php
+require_once(__DIR__."/../core.php");
+security::checkType(security::ADMIN, security::METHOD_NOTFOUND);
+
+if (!isset($_GET["id"])) {
+ security::notFound();
+}
+
+$id = (int)$_GET["id"];
+
+$day = schedules::getTemplateDay($id);
+
+if ($day === false) {
+ security::notFound();
+}
+
+$empty = [];
+
+foreach (schedules::$allEvents as $date) {
+ $empty[$date] = (intervals::measure([$day["begins".$date], $day["ends".$date]]) == 0);
+}
+?>
+
+<form action="doeditdayscheduletemplate.php" method="POST" autocomplete="off">
+ <input type="hidden" name="id" value="<?=(int)$day["id"]?>">
+ <h4 class="mdl-dialog__title">Modificar horario</h4>
+ <div class="mdl-dialog__content">
+ <h5>Día</h5>
+ <div class="mdlext-selectfield mdlext-js-selectfield mdlext-selectfield--floating-label">
+ <select id="edit_day" class="mdlext-selectfield__select" disabled>
+ <?php
+ foreach (calendars::$days as $id => $tday) {
+ echo '<option value="'.(int)$id.'"'.($day["day"] == $id ? " selected" : "").'>'.security::htmlsafe($tday).'</option>';
+ }
+ ?>
+ </select>
+ <label for="edit_day" class="mdlext-selectfield__label">Día de la semana</label>
+ </div>
+ <br>
+ <div class="mdlext-selectfield mdlext-js-selectfield mdlext-selectfield--floating-label">
+ <select id="edit_type" class="mdlext-selectfield__select" disabled>
+ <?php
+ foreach (calendars::$types as $id => $type) {
+ if ($id == calendars::TYPE_FESTIU) continue;
+ echo '<option value="'.(int)$id.'"'.($day["typeday"] == $id ? " selected" : "").'>'.security::htmlsafe($type).'</option>';
+ }
+ ?>
+ </select>
+ <label for="edit_type" class="mdlext-selectfield__label">Tipo de día</label>
+ </div>
+
+ <h5>Jornada laboral</h5>
+ <p>De <input type="time" name="beginswork" <?=(!$empty["work"] ? " value='".schedules::sec2time($day["beginswork"])."'" : "")?> required> a <input type="time" name="endswork" <?=(!$empty["work"] ? " value='".schedules::sec2time($day["endswork"])."'" : "")?> required></p>
+
+ <h5>Desayuno</h5>
+ <p>De <input type="time" name="beginsbreakfast" <?=(!$empty["breakfast"] ? " value='".schedules::sec2time($day["beginsbreakfast"])."'" : "")?>> a <input type="time" name="endsbreakfast" <?=(!$empty["breakfast"] ? " value='".schedules::sec2time($day["endsbreakfast"])."'" : "")?>></p>
+
+ <h5>Comida</h5>
+ <p>De <input type="time" name="beginslunch" <?=(!$empty["lunch"] ? " value='".schedules::sec2time($day["beginslunch"])."'" : "")?>> a <input type="time" name="endslunch" <?=(!$empty["lunch"] ? " value='".schedules::sec2time($day["endslunch"])."'" : "")?>></p>
+ </div>
+ <div class="mdl-dialog__actions">
+ <button type="submit" class="mdl-button mdl-js-button mdl-js-ripple-effect mdl-button--primary">Modificar</button>
+ <button data-dyndialog-close class="mdl-button mdl-js-button mdl-js-ripple-effect cancel">Cancelar</button>
+ </div>
+</form>
diff --git a/src/dynamic/edituser.php b/src/dynamic/edituser.php
new file mode 100644
index 0000000..36fb51c
--- /dev/null
+++ b/src/dynamic/edituser.php
@@ -0,0 +1,79 @@
+<?php
+require_once(__DIR__."/../core.php");
+security::checkType(security::ADMIN, security::METHOD_NOTFOUND);
+
+if (!isset($_GET["id"])) {
+ security::notFound();
+}
+
+$p = people::get($_GET["id"]);
+
+if ($p === false) {
+ security::notFound();
+}
+?>
+
+<form action="doedituser.php" method="POST" autocomplete="off">
+ <h4 class="mdl-dialog__title">Edita persona</h4>
+ <div class="mdl-dialog__content">
+ <div class="mdl-textfield mdl-js-textfield mdl-textfield--floating-label">
+ <input class="mdl-textfield__input" type="text" name="id" id="edit_id" value="<?=security::htmlsafe($p['id'])?>" readonly="readonly" autocomplete="off">
+ <label class="mdl-textfield__label" for="edit_nombre">ID</label>
+ </div>
+ <br>
+ <div class="mdl-textfield mdl-js-textfield mdl-textfield--floating-label">
+ <input class="mdl-textfield__input" type="text" name="username" id="edit_username" value="<?=security::htmlsafe($p['username'])?>" autocomplete="off" data-required>
+ <label class="mdl-textfield__label" for="edit_username">Nombre de usuario</label>
+ </div>
+ <br>
+ <div class="mdl-textfield mdl-js-textfield mdl-textfield--floating-label">
+ <input class="mdl-textfield__input" type="text" name="name" id="edit_name" value="<?=security::htmlsafe($p['name'])?>" autocomplete="off" data-required>
+ <label class="mdl-textfield__label" for="edit_name">Nombre</label>
+ </div>
+ <br>
+ <div class="mdl-textfield mdl-js-textfield mdl-textfield--floating-label">
+ <input class="mdl-textfield__input" type="text" name="dni" id="edit_dni" value="<?=security::htmlsafe($p['dni'])?>" autocomplete="off">
+ <label class="mdl-textfield__label" for="edit_dni">DNI (opcional)</label>
+ </div>
+ <br>
+ <div class="mdl-textfield mdl-js-textfield mdl-textfield--floating-label">
+ <input class="mdl-textfield__input" type="email" name="email" id="edit_email" value="<?=security::htmlsafe($p['email'])?>" autocomplete="off">
+ <label class="mdl-textfield__label" for="edit_email">Correo electrónico (opcional)</label>
+ </div>
+ <br>
+ <div class="mdlext-selectfield mdlext-js-selectfield mdlext-selectfield--floating-label">
+ <select name="category" id="edit_category" class="mdlext-selectfield__select">
+ <option value="-1"></option>
+ <?php
+ $categories = categories::getAll();
+ foreach ($categories as $id => $category) {
+ $selected = ($id == $p["categoryid"] ? " selected" : "");
+ echo '<option value="'.(int)$id.'"'.$selected.'>'.security::htmlsafe($category).'</option>';
+ }
+ ?>
+ </select>
+ <label for="edit_category" class="mdlext-selectfield__label">Categoría (opcional)</label>
+ </div>
+ <br>
+ <div class="mdl-textfield mdl-js-textfield mdl-textfield--floating-label">
+ <input class="mdl-textfield__input" type="password" name="password" id="edit_password" autocomplete="off">
+ <label class="mdl-textfield__label" for="edit_password">Contraseña</label>
+ </div>
+ <p><?=security::htmlsafe(security::$passwordHelperText)?></p>
+ <div class="mdlext-selectfield mdlext-js-selectfield mdlext-selectfield--floating-label">
+ <select name="type" id="edit_type" class="mdlext-selectfield__select" data-required>
+ <?php
+ foreach (security::$types as $i => $type) {
+ $selected = ($i == $p["type"] ? " selected" : "");
+ echo '<option value="'.(int)$i.'"'.$selected.(security::isAllowed($i) ? "" : " disabled").'>'.security::htmlsafe($type).'</option>';
+ }
+ ?>
+ </select>
+ <label for="edit_type" class="mdlext-selectfield__label">Tipo</label>
+ </div>
+ </div>
+ <div class="mdl-dialog__actions">
+ <button type="submit" class="mdl-button mdl-js-button mdl-js-ripple-effect mdl-button--accent">Confirmar</button>
+ <button data-dyndialog-close class="mdl-button mdl-js-button mdl-js-ripple-effect cancel">Cancelar</button>
+ </div>
+</form>
diff --git a/src/dynamic/editworkhistoryitem.php b/src/dynamic/editworkhistoryitem.php
new file mode 100644
index 0000000..14fd04c
--- /dev/null
+++ b/src/dynamic/editworkhistoryitem.php
@@ -0,0 +1,56 @@
+<?php
+require_once(__DIR__."/../core.php");
+security::checkType(security::ADMIN, security::METHOD_NOTFOUND);
+
+if (!isset($_GET["id"])) {
+ security::notFound();
+}
+
+$id = (int)$_GET["id"];
+
+$item = workers::getWorkHistoryItem($id);
+if ($item === false) security::notFound();
+
+$isHidden = workers::isHidden($item["status"]);
+
+$worker = workers::get($item["worker"]);
+if ($worker === false) security::notFound();
+?>
+
+<dynscript>
+document.getElementById("cancel").addEventListener("click", e => {
+ e.preventDefault();
+ dynDialog.load("dynamic/workhistory.php?id="+parseInt(document.getElementById("cancel").getAttribute("data-worker-id")));
+});
+</dynscript>
+
+<form action="doeditworkhistoryitem.php" method="POST" autocomplete="off">
+ <input type="hidden" name="id" value="<?=(int)$id?>">
+ <h4 class="mdl-dialog__title">Editar <?=security::htmlsafe(strtolower(workers::affiliationStatusHelper($item["status"])))?></h4>
+ <div class="mdl-dialog__content">
+ <p><b>Persona:</b> <?=security::htmlsafe($worker["name"])?><br>
+ <b>Empresa:</b> <?=security::htmlsafe($worker["companyname"])?></p>
+
+ <div class="mdl-textfield mdl-js-textfield mdl-textfield--floating-label">
+ <input class="mdl-textfield__input" type="date" name="day" id="day" autocomplete="off" data-required value="<?=security::htmlsafe(date("Y-m-d", $item["day"]))?>">
+ <label class="mdl-textfield__label" for="day">Fecha</label>
+ </div>
+ <br>
+ <div class="mdlext-selectfield mdlext-js-selectfield mdlext-selectfield--floating-label">
+ <select name="status" id="status" class="mdlext-selectfield__select" data-required>
+ <option></option>
+ <?php
+ foreach (workers::$affiliationStatusesManual as $status) {
+ $currentIsHidden = workers::isHidden($status);
+ echo '<option value="'.(int)$status.'"'.((($isHidden && $currentIsHidden) || (!$isHidden && !$currentIsHidden)) ? ' selected' : '').'>'.security::htmlsafe(workers::affiliationStatusHelper($status)).'</option>';
+ }
+ ?>
+ </select>
+ <label for="status" class="mdlext-selectfield__label">Tipo</label>
+ </div>
+ </div>
+ <div class="mdl-dialog__actions">
+ <button type="submit" class="mdl-button mdl-js-button mdl-js-ripple-effect mdl-button--accent">Editar</button>
+ <button id="cancel" class="mdl-button mdl-js-button mdl-js-ripple-effect" data-worker-id="<?=(int)$worker["id"]?>">Cancelar</button>
+ </div>
+</form>
diff --git a/src/dynamic/enablesecondfactor.php b/src/dynamic/enablesecondfactor.php
new file mode 100644
index 0000000..633d36e
--- /dev/null
+++ b/src/dynamic/enablesecondfactor.php
@@ -0,0 +1,102 @@
+<?php
+require_once(__DIR__."/../core.php");
+security::checkType(security::WORKER, security::METHOD_NOTFOUND);
+security::checkWorkerUIEnabled();
+secondFactor::checkAvailability();
+
+if (secondFactor::isEnabled()) {
+ security::notFound();
+}
+
+$secret = secondFactor::generateSecret();
+$url = "otpauth://totp/".str_replace("+", "%20", urlencode($conf["appName"])).":".urlencode(people::userData('username'))."?secret=".urlencode($secret)."&issuer=".str_replace("+", "%20", urlencode($conf["appName"]));
+?>
+
+<style>
+#dynDialog {
+ max-width: 500px;
+ width: auto;
+}
+
+.step {
+ padding: 10px 0;
+ border-bottom: 1px solid #ebebeb;
+}
+
+.step .number {
+ display: inline-block;
+ vertical-align: middle;
+ font-family: "Arial", sans-serif;
+ font-size: 36px;
+ font-weight: bold;
+ color: green;
+ margin: 0;
+ margin-right: 15px;
+ padding: 0;
+ line-height: normal;
+}
+
+.step .text {
+ display: inline-block;
+ vertical-align: middle;
+ margin: 0;
+ padding: 0;
+ width: Calc(100% - 40px);
+}
+
+.step .icon_container {
+ float: right;
+ height: 24px;
+ padding-top: 9px;
+ padding-right: 9px;
+}
+
+#qrcode {
+ margin: 8px 0;
+}
+
+#qrcode img, #qrcode canvas {
+ margin: auto;
+}
+</style>
+
+<dynscript>
+new QRCode(document.getElementById("qrcode"), {
+ text: "<?=security::htmlsafe($url)?>",
+ width: 200,
+ height: 200
+});
+</dynscript>
+
+<form action="doenablesecondfactor.php" method="POST" autocomplete="off">
+ <input type="hidden" name="secret" value="<?=security::htmlsafe($secret)?>">
+ <h4 class="mdl-dialog__title">Activa la verificación en dos pasos</h4>
+ <div class="mdl-dialog__content">
+ <p>Para activar la verificación en dos pasos, sigue los siguientes pasos:</p>
+
+ <div class="step">
+ <div class="number">1</div>
+ <div class="text"><b>Instala la aplicación Google Authenticator en tu <a href="http://appstore.com/googleauthenticator" target="_blank" rel="noopener noreferrer">iPhone</a> o <a href="https://play.google.com/store/apps/details?id=com.google.android.apps.authenticator2" target="_blank" rel="noopener noreferrer">Android</a>.</b><br>También puedes usar otra aplicación si lo prefieres.</div>
+ </div>
+ <div class="step">
+ <div class="number">2</div><div class="text"><b>Configura tu cuenta en la app Google Authenticator escaneando el siguiente código QR:</b></div>
+ </div>
+
+ <div id="qrcode"></div>
+
+ <div class="step" style="border-top: 1px solid #ebebeb;">
+ <div class="number">3</div><div class="text"><b>¿No puedes escanear el código QR? Introduce manualmente la siguiente clave secreta:</b><br><?=security::htmlsafe(secondFactorView::renderSecret($secret))?></div>
+ </div>
+ <div class="step" style="margin-bottom: 5px;">
+ <div class="number">4</div><div class="text"><b>Introduce el código de verificación de 6 dígitos:</b></div>
+ </div>
+ <div class="mdl-textfield mdl-js-textfield mdl-textfield--floating-label">
+ <input class="mdl-textfield__input" type="text" name="code" id="code" autocomplete="off" pattern="[0-9]{6}" data-required>
+ <label class="mdl-textfield__label" for="code">Código de verificación</label>
+ </div>
+ </div>
+ <div class="mdl-dialog__actions">
+ <button type="submit" class="mdl-button mdl-js-button mdl-js-ripple-effect mdl-button--primary">Activar</button>
+ <button data-dyndialog-close class="mdl-button mdl-js-button mdl-js-ripple-effect cancel">Cancelar</button>
+ </div>
+</form>
diff --git a/src/dynamic/exportcalendar.php b/src/dynamic/exportcalendar.php
new file mode 100644
index 0000000..47b88fa
--- /dev/null
+++ b/src/dynamic/exportcalendar.php
@@ -0,0 +1,59 @@
+<?php
+require_once(__DIR__."/../core.php");
+security::checkType(security::ADMIN, security::METHOD_NOTFOUND);
+
+if (!isset($_GET["id"])) {
+ security::notFound();
+}
+
+$id = (int)$_GET["id"];
+
+$c = calendars::get($id);
+
+if ($c === false) {
+ security::notFound();
+}
+
+$details = json_decode($c["details"], true);
+$export = array(
+ "begins" => $c["begins"],
+ "ends" => $c["ends"],
+ "calendar" => $details
+);
+?>
+
+<style>
+textarea.code {
+ width: 100%;
+ height: 100px;
+}
+</style>
+
+<dynscript>
+document.querySelector("textarea.code").select();
+
+document.getElementById("copy").addEventListener("click", _ => {
+ navigator.clipboard.writeText(document.querySelector("textarea.code").value).then(_ => {
+ document.querySelector(".mdl-js-snackbar").MaterialSnackbar.showSnackbar({
+ message: "Se ha copiado el texto correctamente.",
+ timeout: 5000
+ });
+ }).catch(error => {
+ document.querySelector(".mdl-js-snackbar").MaterialSnackbar.showSnackbar({
+ message: "Ha ocurrido un error copiando el texto. Por favor, cópialo manualmente.",
+ timeout: 5000
+ });
+ console.error(error);
+ });
+});
+</dynscript>
+
+<h4 class="mdl-dialog__title">Exportar calendario</h4>
+<div class="mdl-dialog__content">
+ <p>Este es el código que contiene toda la información del calendario y que puedes usar de plantilla más tarde:</p>
+ <textarea class="code" readonly><?=security::htmlsafe(json_encode($export))?></textarea>
+</div>
+<div class="mdl-dialog__actions">
+ <button data-dyndialog-close class="mdl-button mdl-js-button mdl-js-ripple-effect mdl-button--accent cancel">Cerrar</button>
+ <button id="copy" class="mdl-button mdl-js-button mdl-js-ripple-effect">Copiar</button>
+</div>
diff --git a/src/dynamic/incidentattachments.php b/src/dynamic/incidentattachments.php
new file mode 100644
index 0000000..2402688
--- /dev/null
+++ b/src/dynamic/incidentattachments.php
@@ -0,0 +1,107 @@
+<?php
+require_once(__DIR__."/../core.php");
+security::checkType(security::WORKER, security::METHOD_NOTFOUND);
+security::checkWorkerUIEnabled();
+
+if (!isset($_GET["id"])) {
+ security::notFound();
+}
+
+$id = (int)$_GET["id"];
+
+$incident = incidents::get($id, true);
+if ($incident === false) security::notFound();
+
+$isAdmin = security::isAllowed(security::ADMIN);
+$status = incidents::getStatus($incident);
+
+$cantedit = in_array($status, incidents::$cannotEditCommentsStates);
+
+if (!$isAdmin) incidents::checkIncidentIsFromPerson($incident["id"]);
+?>
+
+<dynscript>
+document.querySelectorAll(".deleteattachment").forEach(el => {
+ el.addEventListener("click", e => {
+ dynDialog.load("dynamic/deleteattachment.php?id="+el.getAttribute("data-id")+"&name="+el.getAttribute("data-name")<?=(isset($_GET["continue"]) ? '+"&continue='.security::htmlsafe(urlencode($_GET["continue"])).'"' : '')?>);
+ });
+});
+</dynscript>
+
+<style>
+#dynDialog {
+ max-width: 380px;
+ width: auto;
+}
+
+.addAttachmentForm {
+ display: flex;
+ align-items: center;
+}
+
+.addAttachmentForm input[type="file"] {
+ width: 100%;
+ height: min-content;
+}
+
+.addAttachmentForm button {
+ min-width: min-content;
+}
+
+.attachmentDescription {
+ margin-top: 16px;
+}
+
+.attachmentDescription code {
+ font-size: 12px;
+}
+</style>
+
+<h4 class="mdl-dialog__title">Archivos adjuntos</h4>
+<div class="mdl-dialog__content">
+ <?php
+ $attachments = incidents::getAttachmentsFromIncident($incident);
+
+ if ($attachments === false) {
+ echo "<p>Ha ocurrido un problema cargando los archivos adjuntos.</p>";
+ } elseif (!count($attachments)) {
+ echo "<p>No hay ningún archivo adjunto</p>";
+ } else {
+ echo '<ul class="mdl-list">';
+ foreach ($attachments as $attachment) {
+ $extension = files::getFileExtension($attachment);
+ $icon = files::$mimeTypesIcons[$extension] ?? "broken_image";
+ $title = files::$readableMimeTypes[$extension] ?? "Documento desconocido";
+ echo '<li class="mdl-list__item">
+ <span class="mdl-list__item-primary-content">
+ <i class="material-icons mdl-list__item-icon">'.security::htmlsafe($icon).'</i>
+ '.security::htmlsafe($title).'
+ </span>
+ <a href="incidentattachment.php?id='.(int)$incident["id"].'&name='.security::htmlsafe($attachment).'" target="_blank" class="mdl-list__item-secondar-action mdl-button mdl-js-button mdl-button--icon mdl-js-ripple-effect">
+ <i class="material-icons">open_in_new</i>
+ </a>'.
+ ($cantedit ? '' : '<button class="mdl-list__item-secondar-action mdl-button mdl-js-button mdl-button--icon mdl-js-ripple-effect deleteattachment" data-id="'.(int)$id.'" data-name="'.security::htmlsafe($attachment).'">
+ <i class="material-icons">delete</i>
+ </button>').'
+ </li>';
+ }
+ echo "</ul>";
+ }
+
+ if (!$cantedit) {
+ ?>
+ <h5>Añade un archivo adjunto</h5>
+ <form action="doaddincidentattachment.php" method="POST" enctype="multipart/form-data" class="addAttachmentForm">
+ <input type="hidden" name="id" value="<?=(int)$incident["id"]?>">
+ <?php visual::addContinueInput(); ?>
+ <input type="file" name="file" accept="<?=security::htmlsafe(files::getAcceptAttribute())?>" required>
+ <button class="mdl-button mdl-js-button mdl-js-ripple-effect mdl-button--raised">Subir</button>
+ </form>
+ <div class="attachmentDescription">Se aceptan archivos de hasta <?=security::htmlsafe(files::READABLE_MAX_SIZE)?> con los siguientes formatos: <code><?=security::htmlsafe(files::getAcceptAttribute(true))?></code></div>
+ <?php
+ }
+ ?>
+</div>
+<div class="mdl-dialog__actions">
+ <button data-dyndialog-close class="mdl-button mdl-js-button mdl-js-ripple-effect mdl-button--accent">Cerrar</button>
+</div>
diff --git a/src/dynamic/invalidateincident.php b/src/dynamic/invalidateincident.php
new file mode 100644
index 0000000..ceeeac3
--- /dev/null
+++ b/src/dynamic/invalidateincident.php
@@ -0,0 +1,29 @@
+<?php
+require_once(__DIR__."/../core.php");
+security::checkType(security::ADMIN, security::METHOD_NOTFOUND);
+
+if (!isset($_GET["id"])) {
+ security::notFound();
+}
+
+$id = (int)$_GET["id"];
+
+$incident = incidents::get($id);
+if ($incident === false) security::notFound();
+
+$status = incidents::getStatus($incident);
+if (!in_array($status, incidents::$canInvalidateStates)) security::notFound();
+?>
+
+<form action="doinvalidateincident.php" method="POST" autocomplete="off">
+ <input type="hidden" name="id" value="<?=(int)$id?>">
+ <?php visual::addContinueInput(); ?>
+ <h4 class="mdl-dialog__title">Invalidar incidencia</h4>
+ <div class="mdl-dialog__content">
+ <p>¿Estás seguro que quieres invalidar esta incidencia? <span style="color:#EF5350;font-weight:bold;">Esta acción es irreversible</span></p>
+ </div>
+ <div class="mdl-dialog__actions">
+ <button type="submit" class="mdl-button mdl-js-button mdl-js-ripple-effect mdl-button--accent">Invalidar</button>
+ <button data-dyndialog-close class="mdl-button mdl-js-button mdl-js-ripple-effect cancel">Cancelar</button>
+ </div>
+</form>
diff --git a/src/dynamic/invalidaterecord.php b/src/dynamic/invalidaterecord.php
new file mode 100644
index 0000000..2dc0dfc
--- /dev/null
+++ b/src/dynamic/invalidaterecord.php
@@ -0,0 +1,29 @@
+<?php
+require_once(__DIR__."/../core.php");
+security::checkType(security::WORKER, security::METHOD_NOTFOUND);
+security::checkWorkerUIEnabled();
+
+if (!isset($_GET["id"])) {
+ security::notFound();
+}
+
+$id = (int)$_GET["id"];
+
+$record = registry::get($id);
+if ($record === false || $record["invalidated"] != 0) security::notFound();
+
+$isAdmin = security::isAllowed(security::ADMIN);
+if (!$isAdmin) registry::checkRecordIsFromPerson($record["id"]);
+?>
+
+<form action="doinvalidaterecord.php" method="POST" autocomplete="off">
+ <input type="hidden" name="id" value="<?=(int)$id?>">
+ <h4 class="mdl-dialog__title">Invalidar elemento del registro</h4>
+ <div class="mdl-dialog__content">
+ <p>¿Estás seguro que quieres eliminar este elemento del registro? <span style="color:#EF5350;font-weight:bold;">Esta acción es irreversible</span></p>
+ </div>
+ <div class="mdl-dialog__actions">
+ <button type="submit" class="mdl-button mdl-js-button mdl-js-ripple-effect mdl-button--accent">Invalidar</button>
+ <button data-dyndialog-close class="mdl-button mdl-js-button mdl-js-ripple-effect cancel">Cancelar</button>
+ </div>
+</form>
diff --git a/src/dynamic/log.php b/src/dynamic/log.php
new file mode 100644
index 0000000..1c10dc5
--- /dev/null
+++ b/src/dynamic/log.php
@@ -0,0 +1,59 @@
+<?php
+require_once(__DIR__."/../core.php");
+security::checkType(security::ADMIN, security::METHOD_NOTFOUND);
+
+if (!isset($_GET["id"])) {
+ security::notFound();
+}
+
+$id = (int)$_GET["id"];
+
+$log = registry::getLog($id);
+if ($log === false) security::notFound();
+?>
+
+<style>
+#dynDialog {
+ max-width: 500px;
+ width: auto;
+}
+
+.log {
+ white-space: pre-wrap;
+}
+</style>
+
+<h4 class="mdl-dialog__title">
+ Log
+ <?php
+ if ($log["warningpos"] > 0) {
+ visual::addTooltip("warning", "El log contiene mensajes de advertencia");
+ ?>
+ <i class="material-icons mdl-color-text--orange help" id="warning">warning</i>
+ <?php
+ }
+
+ if ($log["errorpos"] > 0) {
+ visual::addTooltip("error", "El log contiene mensajes de error");
+ ?>
+ <i class="material-icons mdl-color-text--red help" id="error">error</i>
+ <?php
+ }
+
+ if ($log["fatalerrorpos"] > 0) {
+ visual::addTooltip("fatalerror", "El log contiene errores fatales");
+ ?>
+ <i class="material-icons mdl-color-text--red help-900" id="fatalerror">error</i>
+ <?php
+ }
+ ?>
+</h4>
+<div class="mdl-dialog__content">
+ <pre class="log"><?=registry::beautifyLog(security::htmlsafe($log["logdetails"]))?></pre>
+</div>
+<div class="mdl-dialog__actions">
+ <button data-dyndialog-close class="mdl-button mdl-js-button mdl-js-ripple-effect mdl-button--primary">Cerrar</button>
+</div>
+<?php
+visual::renderTooltips();
+?>
diff --git a/src/dynamic/sethelpresource.php b/src/dynamic/sethelpresource.php
new file mode 100644
index 0000000..2171dde
--- /dev/null
+++ b/src/dynamic/sethelpresource.php
@@ -0,0 +1,31 @@
+<?php
+require_once(__DIR__."/../core.php");
+security::checkType(security::HYPERADMIN, security::METHOD_NOTFOUND);
+
+if (!security::checkParams("GET", [
+ ["place", security::PARAM_ISINT]
+])) {
+ security::notFound();
+}
+
+$place = $_GET["place"];
+if (!in_array($place, help::$places)) security::notFound();
+
+$url = help::get($place);
+?>
+
+<form action="dosethelpresource.php" method="POST" autocomplete="off">
+ <input type="hidden" name="place" value="<?=(int)$place?>">
+ <h4 class="mdl-dialog__title">Enlace de ayuda</h4>
+ <div class="mdl-dialog__content">
+ <p><b>Lugar:</b> <?=security::htmlsafe(help::$placesName[$place] ?? "undefined")?></b></p>
+ <div class="mdl-textfield mdl-js-textfield mdl-textfield--floating-label">
+ <input class="mdl-textfield__input" type="url" name="url" id="url" autocomplete="off"<?=($url !== false ? ' value="'.security::htmlsafe($url).'"' : '')?>>
+ <label class="mdl-textfield__label" for="url">URL</label>
+ </div>
+ </div>
+ <div class="mdl-dialog__actions">
+ <button type="submit" class="mdl-button mdl-js-button mdl-js-ripple-effect mdl-button--primary">Configurar</button>
+ <button data-dyndialog-close class="mdl-button mdl-js-button mdl-js-ripple-effect cancel">Cancelar</button>
+ </div>
+</form>
diff --git a/src/dynamic/user.php b/src/dynamic/user.php
new file mode 100644
index 0000000..d0aec1d
--- /dev/null
+++ b/src/dynamic/user.php
@@ -0,0 +1,59 @@
+<?php
+require_once(__DIR__."/../core.php");
+security::checkType(security::ADMIN, security::METHOD_NOTFOUND);
+
+if (!isset($_GET["id"])) {
+ security::notFound();
+}
+
+$p = people::get($_GET["id"], false);
+
+if ($p === false) {
+ security::notFound();
+}
+
+$companies = companies::getAll();
+$pcompanies = [];
+
+foreach($p["companies"] as $company) {
+ $pcompanies[] = $companies[$company];
+}
+
+$secondFactor = secondFactor::isEnabled($p["id"]);
+
+if ($secondFactor) {
+?>
+<dynscript>
+document.querySelector(".disable-second-factor").addEventListener("click", e => {
+ dynDialog.load("dynamic/disablesecondfactor.php?id=<?=(int)$p["id"]?>");
+});
+</dynscript>
+<?php
+}
+?>
+
+<style>
+#dynDialog {
+ max-width: 380px;
+ width: auto;
+}
+</style>
+
+<h4 class="mdl-dialog__title"><?=security::htmlsafe($p["name"])?></h4>
+<ul>
+ <li><b>Nombre de usuario:</b> <?=security::htmlsafe($p["username"])?></li>
+ <li><b>DNI:</b> <?=(!empty($p["dni"]) ? security::htmlsafe($p["dni"]) : "-")?></li>
+ <li><b>Correo electrónico:</b> <?=(!empty($p["email"]) ? "<a href=\"mailto:".security::htmlsafe(rawurlencode($p["email"]))."\" target=\"_blank\">".security::htmlsafe($p["email"])."</a>" : "-")?>
+ <li><b>Categoría:</b> <?=($p["categoryid"] == -1 ? "-" : security::htmlsafe($p["category"]))?></li>
+ <li><b>Dada de baja:</b> <?=($p["baixa"] == 1 ? visual::YES : "No")?></li>
+ <li><b>Empresas:</b> <?=security::htmlsafe((count($p["companies"]) ? implode(", ", $pcompanies) : "-"))?></li>
+ <li><b>Tipo de usuario:</b> <?=security::htmlsafe(security::$types[$p["type"]])?></li>
+ <?php if (secondFactor::isAvailable()) { ?><li><b>Verificación en dos pasos:</b> <?=($secondFactor ? 'activada <button class="mdl-button mdl-js-button mdl-button--raised mdl-js-ripple-effect mdl-button--accent disable-second-factor">Desactivar</button>' : 'desactivada')?></li><?php } ?>
+</ul>
+
+<div class="mdl-dialog__actions">
+ <button data-dyndialog-close class="mdl-button mdl-js-button mdl-js-ripple-effect cancel">Cerrar</button>
+ <a href="userregistry.php?id=<?=(int)$p["id"]?>" class="mdl-button mdl-js-button mdl-button--icon mdl-js-ripple-effect"><i class="material-icons">list</i><span class="mdl-ripple"></span></a>
+ <a href="userincidents.php?id=<?=(int)$p["id"]?>" class="mdl-button mdl-js-button mdl-button--icon mdl-js-ripple-effect"><i class="material-icons">assignment_late</i><span class="mdl-ripple"></span></a>
+ <a href="workerschedule.php?id=<?=(int)$p["id"]?>" class="mdl-button mdl-js-button mdl-button--icon mdl-js-ripple-effect"><i class="material-icons">timelapse</i><span class="mdl-ripple"></span></a>
+</div>
diff --git a/src/dynamic/workhistory.php b/src/dynamic/workhistory.php
new file mode 100644
index 0000000..4c74000
--- /dev/null
+++ b/src/dynamic/workhistory.php
@@ -0,0 +1,91 @@
+<?php
+require_once(__DIR__."/../core.php");
+security::checkType(security::ADMIN, security::METHOD_NOTFOUND);
+
+if (!isset($_GET["id"])) {
+ security::notFound();
+}
+
+$id = (int)$_GET["id"];
+
+$worker = workers::get($id);
+if ($worker === false) security::notFound();
+?>
+
+<dynscript>
+document.getElementById("additem").addEventListener("click", e => {
+ dynDialog.load("dynamic/addworkhistoryitem.php?id="+parseInt(document.getElementById("additem").getAttribute("worker-id")));
+});
+
+document.querySelectorAll(".edititem").forEach(el => {
+ el.addEventListener("click", e => {
+ dynDialog.load("dynamic/editworkhistoryitem.php?id="+parseInt(el.getAttribute("data-id")));
+ });
+});
+
+document.querySelectorAll(".deleteitem").forEach(el => {
+ el.addEventListener("click", e => {
+ dynDialog.load("dynamic/deleteworkhistoryitem.php?id="+parseInt(el.getAttribute("data-id")));
+ });
+});
+</dynscript>
+
+<style>
+#dynDialog {
+ max-width: 380px;
+ width: auto;
+}
+
+#dynDialog .mdl-list {
+ margin-top: 0;
+ padding-top: 0;
+}
+
+.float-right {
+ float: right;
+}
+</style>
+
+<h4 class="mdl-dialog__title">Historial de altas y bajas</h4>
+<div class="mdl-dialog__content">
+ <p><b>Persona:</b> <?=security::htmlsafe($worker["name"])?><br>
+ <b>Empresa:</b> <?=security::htmlsafe($worker["companyname"])?></p>
+
+ <div class="float-right"><button id="additem" class="mdl-button mdl-js-button mdl-js-ripple-effect" worker-id="<?=(int)$worker["id"]?>"><i class="material-icons">add</i> Añadir alta/baja</button></div>
+ <div style="clear: both;"></div>
+
+ <?php
+ $items = workers::getWorkHistory($id);
+
+ if ($items === false) {
+ echo "<p>Ha ocurrido un problema cargando los elementos del historial.</p>";
+ } elseif (!count($items)) {
+ echo "<p>No hay ningún elmento en el historial, así que el aplicativo está considerando que el trabajador está de baja.</p>";
+ } else {
+ echo '<ul class="mdl-list">';
+ foreach ($items as $item) {
+ $icon = security::htmlsafe(workers::affiliationStatusIcon($item["status"]) ?? "indeterminate_check_box");
+ $helper = workers::affiliationStatusHelper($item["status"]);
+ $day = date("d/m/Y", $item["day"]);
+ $isAutomatic = workers::isAutomaticAffiliation($item["status"]);
+ echo '<li class="mdl-list__item '.($isAutomatic ? 'mdl-list__item--two-line' : '').'">
+ <span class="mdl-list__item-primary-content">
+ <i class="material-icons mdl-list__item-icon">'.security::htmlsafe($icon).'</i>
+ <span>'.security::htmlsafe($helper).' ('.security::htmlsafe($day).')</span>
+ '.($isAutomatic ? '<span class="mdl-list__item-sub-title">Registro automático</span>' : '').'
+ </span>
+ <button class="mdl-list__item-secondar-action mdl-button mdl-js-button mdl-button--icon mdl-js-ripple-effect edititem" data-id="'.(int)$item["id"].'">
+ <i class="material-icons">edit</i>
+ </button>
+ <button class="mdl-list__item-secondar-action mdl-button mdl-js-button mdl-button--icon mdl-js-ripple-effect deleteitem" data-id="'.(int)$item["id"].'">
+ <i class="material-icons">delete</i>
+ </button>
+ </li>';
+ }
+ echo "</ul>";
+ }
+ ?>
+</div>
+<div class="mdl-dialog__actions">
+ <button data-dyndialog-close class="mdl-button mdl-js-button mdl-js-ripple-effect mdl-button--accent">Cerrar</button>
+</div>