blob: b558ed9baf741ceb63ddae8f5e13ecd82b6be7f0 [file] [log] [blame]
Copybara botbe50d492023-11-30 00:16:42 +01001<?php
Adrià Vilanova Martínez5af86512023-12-02 20:44:16 +01002/*
3 * hores
4 * Copyright (c) 2023 Adrià Vilanova Martínez
5 *
6 * This program is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU Affero General Public License as
8 * published by the Free Software Foundation, either version 3 of the
9 * License, or (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU Affero General Public License for more details.
15 *
16 * You should have received a copy of the GNU Affero General Public
17 * License along with this program.
18 * If not, see http://www.gnu.org/licenses/.
19 */
20
Copybara botbe50d492023-11-30 00:16:42 +010021class incidents {
22 const STARTOFDAY = 0;
23 const ENDOFDAY = 60*60*24;
24
25 const PAGINATION_LIMIT = 20;
26
27 const UPDATE_STATE_NOT_UPDATED = 0;
28 const UPDATE_STATE_UPDATED = 1;
29
30 const STATE_UNVERIFIED = 0;
31 const STATE_REJECTED = 1;
32 const STATE_SCHEDULED = 2;
33 const STATE_REGISTERED = 3;
34 const STATE_MANUALLY_INVALIDATED = 4;
35 const STATE_VALIDATED_BY_WORKER = 5;
36
37 public static $stateIcons = array(
38 0 => "new_releases",
39 1 => "block",
40 2 => "schedule",
41 3 => "check",
42 4 => "delete_forever",
43 5 => "verified_user"
44 );
45
46 public static $stateIconColors = array(
47 0 => "mdl-color-text--orange",
48 1 => "mdl-color-text--red",
49 2 => "mdl-color-text--orange",
50 3 => "mdl-color-text--green",
51 4 => "mdl-color-text--red",
52 5 => "mdl-color-text--green"
53 );
54
55 public static $stateTooltips = array(
56 0 => "Pendiente de revisión",
57 1 => "Rechazada al revisar",
58 2 => "Programada",
59 3 => "Registrada",
60 4 => "Invalidada manualmente",
61 5 => "Validada"
62 );
63
64 public static $statesOrderForFilters = [0, 1, 4, 2, 3, 5];
65
66 public static $invalidStates = [incidents::STATE_REJECTED, incidents::STATE_MANUALLY_INVALIDATED];
67
68 public static $cannotEditCommentsStates = [];
69 public static $cannotEditStates = [self::STATE_VALIDATED_BY_WORKER, self::STATE_REGISTERED, self::STATE_MANUALLY_INVALIDATED, self::STATE_REJECTED];
70 public static $canRemoveStates = [self::STATE_SCHEDULED];
71 public static $canInvalidateStates = [self::STATE_VALIDATED_BY_WORKER, self::STATE_REGISTERED];
72 public static $workerCanEditStates = [self::STATE_UNVERIFIED];
73 public static $workerCanRemoveStates = [self::STATE_UNVERIFIED];
74
75 public static $adminPendingWhere = "i.verified = 0 AND i.confirmedby = -1";
76 public static $workerPendingWhere = "i.workervalidated = 0";
77 public static $activeWhere = "i.verified = 1 AND i.invalidated = 0";
78 public static $notInvalidatedOrRejectedWhere = "i.invalidated = 0 AND (i.verified = 1 OR i.confirmedby = -1)";
79
80 const FILTER_TYPE_ARRAY = 0;
81 const FILTER_TYPE_INT = 1;
82 const FILTER_TYPE_STRING = 2;
83
84 public static $filters = ["begins", "ends", "types", "states", "attachments", "details", "workerdetails"];
85 public static $filtersType = [
86 "begins" => self::FILTER_TYPE_STRING,
87 "ends" => self::FILTER_TYPE_STRING,
88 "types" => self::FILTER_TYPE_ARRAY,
89 "states" => self::FILTER_TYPE_ARRAY,
90 "attachments" => self::FILTER_TYPE_ARRAY,
91 "details" => self::FILTER_TYPE_ARRAY,
92 "workerdetails" => self::FILTER_TYPE_ARRAY
93 ];
94
95 public static $filtersSwitch = ["attachments", "details", "workerdetails"];
96 public static $filtersSwitchOptions = [
97 0 => "Sin",
98 1 => "Con"
99 ];
100 public static $filtersSwitchHelper = [
101 "attachments" => "archivo adjunto",
102 "details" => "observaciones de un administrador",
103 "workerdetails" => "observaciones del trabajador"
104 ];
105 public static $filtersSwitchMysqlField = [
106 "attachments" => "attachments",
107 "details" => "details",
108 "workerdetails" => "workerdetails"
109 ];
110
111 public static function getTypes($showHidden = true, $isForWorker = false) {
112 global $con, $conf;
113
114 $whereConditions = [];
115 if (!$showHidden) $whereConditions[] = "hidden = 0";
116 if ($isForWorker) $whereConditions[] = "workerfill = 1";
117
118 $where = (count($whereConditions) ? " WHERE ".implode(" AND ",$whereConditions) : "");
119
120 $query = mysqli_query($con, "SELECT * FROM typesincidents".$where." ORDER BY ".($conf["debug"] ? "id ASC" : "hidden ASC, name ASC"));
121
122 $incidents = [];
123
124 while ($row = mysqli_fetch_assoc($query)) {
125 $incidents[] = $row;
126 }
127
128 return $incidents;
129 }
130
131 public static function getTypesForm($isForWorker = false) {
132 return self::getTypes(false, $isForWorker);
133 }
134
135 public static function getType($id) {
136 global $con;
137
138 $sid = (int)$id;
139
140 $query = mysqli_query($con, "SELECT * FROM typesincidents WHERE id = $sid");
141
142 if (!mysqli_num_rows($query)) return false;
143
144 return mysqli_fetch_assoc($query);
145 }
146
147 public static function addType($name, $present, $paid, $workerfill, $notifies, $autovalidates, $hidden) {
148 global $con;
149
150 $sname = db::sanitize($name);
151 $spresent = (int)$present;
152 $spaid = (int)$paid;
153 $sworkerfill = (int)$workerfill;
154 $snotifies = (int)$notifies;
155 $sautovalidates = (int)$autovalidates;
156 $shidden = (int)$hidden;
157
158 return mysqli_query($con, "INSERT INTO typesincidents (name, present, paid, workerfill, notifies, autovalidates, hidden) VALUES ('$name', $spresent, $spaid, $sworkerfill, $snotifies, $sautovalidates, $shidden)");
159 }
160
161 public static function editType($id, $name, $present, $paid, $workerfill, $notifies, $autovalidates, $hidden) {
162 global $con;
163
164 $sid = (int)$id;
165 $sname = db::sanitize($name);
166 $spresent = (int)$present;
167 $spaid = (int)$paid;
168 $sworkerfill = (int)$workerfill;
169 $snotifies = (int)$notifies;
170 $sautovalidates = (int)$autovalidates;
171 $shidden = (int)$hidden;
172
173 return mysqli_query($con, "UPDATE typesincidents SET name = '$name', present = $spresent, paid = $spaid, workerfill = $sworkerfill, notifies = $snotifies, autovalidates = $sautovalidates, hidden = $shidden WHERE id = $sid LIMIT 1");
174 }
175
176 public static function addWhereConditionsHandledBySelect($select, &$whereConditions, $alreadyFilteredDates = false) {
177 if (!$alreadyFilteredDates) {
178 if ($select["enabled"]["begins"]) $whereConditions[] = "i.day >= ".(int)common::getTimestampFromRFC3339($select["selected"]["begins"]);
179 if ($select["enabled"]["ends"]) $whereConditions[] = "i.day <= ".(int)common::getTimestampFromRFC3339($select["selected"]["ends"]);
180 }
181
182 if ($select["enabled"]["types"]) {
183 $insideconditions = [];
184 foreach ($select["selected"]["types"] as $type) {
185 $insideconditions[] = "i.type = ".(int)$type;
186 }
187 $whereConditions[] = "(".implode(" OR ", $insideconditions).")";
188 }
189
190 foreach (self::$filtersSwitch as $f) {
191 if ($select["enabled"][$f]) {
192 foreach ($select["selected"][$f] as $onoff) {
193 $mysqlField = self::$filtersSwitchMysqlField[$f];
194
195 if ($onoff == "0") $insideconditions[] = "($mysqlField IS NULL OR $mysqlField = ''".($f == "attachments" ? " OR $mysqlField = '[]'" : "").")";
196 else if ($onoff == "1") $insideconditions[] = "($mysqlField IS NOT NULL AND $mysqlField <> ''".($f == "attachments" ? " AND $mysqlField <> '[]'" : "").")";
197 }
198 if (count($insideconditions) == 1) $whereConditions[] = $insideconditions[0];
199 }
200 }
201 }
202
203 public static function incidentIsWantedAccordingToSelect($select, $incident) {
204 if ($select["enabled"]["states"]) {
205 return in_array($incident["state"], $select["selected"]["states"]);
206 }
207
208 return true;
209 }
210
211 public static function todayPage($limit = self::PAGINATION_LIMIT) {
212 global $con;
213
214 $today = (int)common::getDayTimestamp(time());
215
216 $query = mysqli_query($con, "SELECT COUNT(*) count FROM incidents i WHERE i.day > $today");
217 if ($query === false) return 0;
218
219 $row = mysqli_fetch_assoc($query);
220 $first = $row["count"];
221
222 return floor($first/(int)$limit) + 1;
223 }
224
225 public static function numRows($select = null, $onlyAdminPending = false) {
226 global $con;
227
228 $whereConditions = [];
229
230 if ($select !== null) {
231 if (!$select["showResultsPaginated"]) return 0;
232
233 self::addWhereConditionsHandledBySelect($select, $whereConditions);
234 }
235
236 if ($onlyAdminPending) $whereConditions[] = self::$adminPendingWhere;
237
238 $where = (count($whereConditions) ? " WHERE ".implode(" AND ", $whereConditions) : "");
239 $query = mysqli_query($con, "SELECT COUNT(*) count FROM incidents i".$where);
240
241 if (!mysqli_num_rows($query)) return false;
242
243 $row = mysqli_fetch_assoc($query);
244
245 return (isset($row["count"]) ? (int)$row["count"] : false);
246 }
247
248 public static function numPending() {
249 return self::numRows(null, true);
250 }
251
252 public static function getStatus(&$incident, $beginsdatetime = -1) {
253 if ($beginsdatetime == -1) {
254 $date = new DateTime();
255 $date->setTimestamp($incident["day"]);
256 $dateFormat = $date->format("Y-m-d");
257
258 $begins = new DateTime($dateFormat."T".schedules::sec2time((int)$incident["begins"], false).":00");
259 $beginsdatetime = $begins->getTimestamp();
260 }
261
262 $time = time();
263
264 if ($incident["verified"] == 0 && $incident["confirmedby"] == -1) return self::STATE_UNVERIFIED;
265 elseif ($incident["verified"] == 0) return self::STATE_REJECTED;
266 elseif ($incident["invalidated"] == 1) return self::STATE_MANUALLY_INVALIDATED;
267 elseif ($beginsdatetime >= $time) return self::STATE_SCHEDULED;
268 elseif ($incident["workervalidated"] == 1) return self::STATE_VALIDATED_BY_WORKER;
269 else return self::STATE_REGISTERED;
270 }
271
272 public static function getAttachmentsFromIncident(&$incident) {
273 if (empty($incident["attachments"]) || $incident["attachments"] === null) return [];
274
275 $json = json_decode($incident["attachments"], true);
276 if (json_last_error() !== JSON_ERROR_NONE) return false;
277
278 return $json;
279 }
280
281 public static function getAttachments($id) {
282 global $con;
283
284 $sid = (int)$id;
285
286 $query = mysqli_query($con, "SELECT attachments FROM incidents WHERE id = ".$sid);
287 if (!mysqli_num_rows($query)) return false;
288
289 $incident = mysqli_fetch_assoc($query);
290
291 return self::getAttachmentsFromIncident($incident);
292 }
293
294 public static function addAttachment($id, &$file) {
295 global $con;
296
297 $name = "";
298 $status = files::uploadFile($file, $name);
299 if ($status !== 0) return $status;
300
301 $attachments = self::getAttachments($id);
302 $attachments[] = $name;
303
304 $sid = (int)$id;
305 $srawAttachments = db::sanitize(json_encode($attachments));
306
307 return (mysqli_query($con, "UPDATE incidents SET attachments = '".$srawAttachments."' WHERE id = $sid LIMIT 1") ? 0 : 1);
308 }
309
310 public static function deleteAttachment($id, $name = "ALL") {
311 global $con;
312
313 $incident = incidents::get($id, true);
314 if ($incident === false) return false;
315
316 $attachments = incidents::getAttachmentsFromIncident($incident);
317
318 if ($attachments === false) return false;
319 if (!count($attachments)) return ($name == "ALL");
320
321 $flag = false;
322
323 foreach ($attachments as $i => $attachment) {
324 if ($attachment == $name || $name === "ALL") {
325 $flag = true;
326
327 if (!files::removeFile($attachment)) return false;
328
329 unset($attachments[$i]);
330 $attachments = array_values($attachments);
331
332 $sid = (int)$id;
333 $srawAttachments = db::sanitize(json_encode($attachments));
334 }
335 }
336
337 return ($flag ? mysqli_query($con, "UPDATE incidents SET attachments = '".$srawAttachments."' WHERE id = $sid LIMIT 1") : false);
338 }
339
340 private static function magicIncident(&$row) {
341 $row["allday"] = ($row["begins"] == self::STARTOFDAY && $row["ends"] == self::ENDOFDAY);
342 $row["updatestate"] = ($row["updatedby"] == -1 ? self::UPDATE_STATE_NOT_UPDATED : self::UPDATE_STATE_UPDATED);
343
344 $date = new DateTime();
345 $date->setTimestamp($row["day"]);
346 $dateFormat = $date->format("Y-m-d");
347
348 $begins = new DateTime($dateFormat."T".schedules::sec2time((int)$row["begins"], false).":00");
349 $row["beginsdatetime"] = $begins->getTimestamp();
350 $ends = new DateTime($dateFormat."T".schedules::sec2time((int)$row["ends"], false).":00");
351 $row["endsdatetime"] = $ends->getTimestamp();
352
353 $row["state"] = self::getStatus($row, $row["beginsdatetime"]);
354 }
355
356 public static function get($id, $magic = false) {
357 global $con, $conf;
358
359 $sid = $id;
360
361 $query = mysqli_query($con, "SELECT * FROM incidents WHERE id = $sid");
362
363 if (!mysqli_num_rows($query)) return false;
364
365 $row = mysqli_fetch_assoc($query);
366 if ($magic) self::magicIncident($row);
367
368 return $row;
369 }
370
371 public static function getAll($onlyAdminPending = false, $start = 0, $limit = self::PAGINATION_LIMIT, $worker = "ALL", $begins = null, $ends = null, $onlyWorkerPending = false, $select = false) {
372 global $con, $conf;
373
374 $whereConditions = [];
375 if ($onlyAdminPending) $whereConditions[] = self::$adminPendingWhere;
376 if ($onlyWorkerPending) {
377 $whereConditions[] = self::$workerPendingWhere;
378 $whereConditions[] = self::$activeWhere;
379 }
380 if ($worker !== "ALL") $whereConditions[] = "i.worker = ".(int)$worker;
381 if ($begins !== null && $ends !== null) {
382 $whereConditions[] = "i.day <= ".(int)$ends." AND i.day >= ".(int)$begins;
383 $filteredDates = true;
384 } else $filteredDates = false;
385
386 if ($select !== false) self::addWhereConditionsHandledBySelect($select, $whereConditions, $filteredDates);
387
388 $where = (count($whereConditions) ? " WHERE ".implode(" AND ", $whereConditions) : "");
389
390 $query = mysqli_query($con, "SELECT i.*, t.id typeid, t.name typename, t.present typepresent, t.paid typepaid, t.workerfill typeworkerfill, t.notifies typenotifies, t.hidden typehidden, p.id personid, p.name workername, w.company companyid FROM incidents i LEFT JOIN typesincidents t ON i.type = t.id LEFT JOIN workers w ON i.worker = w.id LEFT JOIN people p ON w.person = p.id".$where." ORDER BY i.day DESC".db::limitPagination($start, $limit));
391
392 $return = [];
393 while ($row = mysqli_fetch_assoc($query)) {
394 self::magicIncident($row);
395 if ($onlyWorkerPending && $row["state"] !== self::STATE_REGISTERED) continue;
396 if ($select !== false && !self::incidentIsWantedAccordingToSelect($select, $row)) continue;
397 $return[] = $row;
398 }
399
400 return $return;
401 }
402
403 public static function checkOverlap($worker, $day, $begins, $ends, $sans = 0) {
404 global $con;
405
406 $sworker = (int)$worker;
407 $sday = (int)$day;
408 $sbegins = (int)$begins;
409 $sends = (int)$ends;
410 $ssans = (int)$sans;
411
412 $query = mysqli_query($con, "SELECT * FROM incidents i WHERE begins <= $sends AND ends >= $sbegins AND day = $sday AND worker = $sworker".($sans == 0 ? "" : " AND id <> $ssans")." AND ".self::$notInvalidatedOrRejectedWhere." LIMIT 1");
413
414 return (mysqli_num_rows($query) > 0);
415 }
416
417 public static function add($worker, $type, $details, $iday, $begins, $ends, $creator = "ME", $verified = 1, $alreadyTimestamp = false, $isWorkerView = false, $sendEmail = true, $forceAutoValidate = false) {
418 global $con, $conf;
419
420 // Gets information about the worker
421 $sworker = (int)$worker;
422 $workerInfo = workers::get($sworker);
423 if ($workerInfo === false) return 1;
424
425 // Sets who is the incident creator
426 if ($creator === "ME") $creator = people::userData("id");
427 $screator = (int)$creator;
428
429 // If the user is not an admin and the person to who we are going to add the incident is not the creator of the incident, do not continue
430 if (!security::isAllowed(security::ADMIN) && $workerInfo["person"] != $creator) return 5;
431
432 // Sanitizes other incident fields
433 $stype = (int)$type;
434 $sverified = (int)$verified;
435 $sdetails = db::sanitize($details);
436
437 // Gets information about the incident type
438 $incidenttype = self::getType($stype);
439 if ($incidenttype === false) return -1;
440
441 // Gets the timestamp of the incident day
442 if ($alreadyTimestamp) {
443 $sday = (int)$iday;
444 } else {
445 $day = new DateTime($iday);
446 $sday = (int)$day->getTimestamp();
447 }
448
449 // Gets the start and end times, and checks whether they are well-formed
450 $sbegins = (int)$begins;
451 $sends = (int)$ends;
452 if ($sbegins >= $sends) return 3;
453
454 // Checks whether the incident overlaps another incident
455 if (self::checkOverlap($worker, $sday, $begins, $ends)) return 2;
456
457 // Adds the incident
458 if (!mysqli_query($con, "INSERT INTO incidents (worker, creator, type, day, begins, ends, ".($isWorkerView ? "workerdetails" : "details").", verified) VALUES ($sworker, $screator, $stype, $sday, $sbegins, $sends, '$sdetails', $sverified)")) return -1;
459
460 // If the incident type is set to autovalidate or we pass the parameter to force the autovalidation, autovalidate it
461 if (($incidenttype["autovalidates"] == 1 || $forceAutoValidate) && !validations::validateIncident(mysqli_insert_id($con), validations::METHOD_AUTOVALIDATION, "ME", false, false)) {
462 return 5;
463 }
464
465 // Bonus: check whether we should send email notifications, and if applicable send them
466 if ($sendEmail && $conf["mail"]["enabled"] && ($conf["mail"]["capabilities"]["notifyOnWorkerIncidentCreation"] || $conf["mail"]["capabilities"]["notifyOnAdminIncidentCreation"] || $conf["mail"]["capabilities"]["notifyCategoryResponsiblesOnIncidentCreation"])) {
467 $workerName = people::workerData("name", $sworker);
468
469 $to = [];
470 if (($conf["mail"]["capabilities"]["notifyOnWorkerIncidentCreation"] && $isWorkerView) || ($conf["mail"]["capabilities"]["notifyOnAdminIncidentCreation"] && !$isWorkerView)) {
471 $to[] = array("email" => $conf["mail"]["adminEmail"]);
472 }
473
474 if ($conf["mail"]["capabilities"]["notifyCategoryResponsiblesOnIncidentCreation"] && $incidenttype["notifies"] == 1) {
475 $categoryid = people::workerData("category", $sworker);
476 $category = categories::get($categoryid);
477 if ($category === false) return 0;
478
479 $emails = json_decode($category["emails"], true);
480 if (json_last_error() === JSON_ERROR_NONE) {
481 foreach ($emails as $email) {
482 $to[] = array("email" => $email);
483 }
484 }
485 }
486
487 if (!count($to)) return 0;
488
489 $subject = "Incidencia del tipo \"".security::htmlsafe($incidenttype["name"])."\" creada para ".security::htmlsafe($workerName)." el ".strftime("%d %b %Y", $sday);
490 $body = mail::bodyTemplate("<p>Hola,</p>
491 <p>Este es un mensaje automático para avisarte de que ".security::htmlsafe(people::userData("name"))." ha introducido la siguiente incidencia en el sistema de registro horario:</p>
492 <ul>
493 <li><b>Trabajador:</b> ".security::htmlsafe($workerName)."</li>
494 <li><b>Motivo:</b> ".security::htmlsafe($incidenttype["name"])."</li>
495 <li><b>Fecha:</b> ".strftime("%d %b %Y", $sday)." ".(($sbegins == 0 && $sends == self::ENDOFDAY) ? "(todo el día)": schedules::sec2time($sbegins)."-".schedules::sec2time($sends))."</li>".
496 (!empty($details) ? "<li><b>Observaciones:</b> <span style='white-space: pre-wrap;'>".security::htmlsafe($details)."</span></li>" : "").
497 "</ul>
498 <p style='font-size: 11px;'>Has recibido este mensaje porque estás configurado como persona responsable de la categoría a la que pertenece este trabajador o eres el administrador del sistema.</p>");
499
500 return (mail::send($to, [], $subject, $body) ? 0 : 4);
501 }
502
503 return 0;
504 }
505
506 public static function edit($id, $type, $iday, $begins, $ends, $updatedby = "ME") {
507 global $con;
508
509 $sid = (int)$id;
510
511 $incident = incidents::get($id);
512 if ($incident === false) return 1;
513
514 $stype = (int)$type;
515 if ($updatedby === "ME") $updatedby = people::userData("id");
516 $supdatedby = (int)$updatedby;
517
518 $day = new DateTime($iday);
519 $sday = (int)$day->getTimestamp();
520
521 $sbegins = (int)$begins;
522 $sends = (int)$ends;
523 if ($sbegins >= $sends) return 3;
524 if (self::checkOverlap($incident["worker"], $sday, $begins, $ends, $id)) return 2;
525
526 return (mysqli_query($con, "UPDATE incidents SET type = $stype, day = $sday, begins = $sbegins, ends = $sends, updatedby = $supdatedby, workervalidated = 0, workervalidation = '' WHERE id = $sid LIMIT 1") ? 0 : -1);
527 }
528
529 public static function editDetails($id, $details, $updatedby = "ME") {
530 global $con;
531
532 $sid = (int)$id;
533 $sdetails = db::sanitize($details);
534 if ($updatedby === "ME") $updatedby = people::userData("id");
535 $supdatedby = (int)$updatedby;
536
537 $incident = self::get($sid);
538 if ($incident === false) return -1;
539
540 $status = self::getStatus($incident);
541 if (in_array($status, self::$cannotEditCommentsStates)) return 1;
542
543 return (mysqli_query($con, "UPDATE incidents SET details = '$sdetails', updatedby = '$supdatedby' WHERE id = $sid LIMIT 1") ? 0 : -1);
544 }
545
546 public static function verify($id, $value, $confirmedby = "ME") {
547 global $con, $conf;
548
549 $sid = (int)$id;
550 $svalue = ($value == 1 ? 1 : 0);
551 if ($confirmedby === "ME") $confirmedby = people::userData("id");
552 $sconfirmedby = (int)$confirmedby;
553
554 $incident = incidents::get($id);
555 if ($incident === false) return false;
556
557 $state = incidents::getStatus($incident);
558 if ($state != incidents::STATE_UNVERIFIED) return false;
559
560 if (!mysqli_query($con, "UPDATE incidents SET verified = $svalue, confirmedby = $sconfirmedby WHERE id = $sid LIMIT 1")) return false;
561
562 if ($conf["mail"]["enabled"] && $conf["mail"]["capabilities"]["notifyWorkerOnIncidentDecision"]) {
563 $workerEmail = people::workerData("email", $incident["worker"]);
564 if ($workerEmail !== false && !empty($workerEmail)) {
565 $workerName = people::workerData("name", $incident["worker"]);
566
567 $to = [array(
568 "email" => $workerEmail,
569 "name" => $workerName
570 )];
571 $subject = "Incidencia del ".strftime("%d %b %Y", $incident["day"])." ".($value == 1 ? "verificada" : "rechazada");
572 $body = mail::bodyTemplate("<p>Bienvenido ".security::htmlsafe($workerName).",</p>
573 <p>Este es un mensaje automático para avisarte de que la incidencia que introduciste para el día ".strftime("%d de %B de %Y", $incident["day"])." ha sido ".($value == 1 ? "aceptada" : "rechazada").".</p><p>Puedes ver el estado de todas tus incidencias en el <a href='".security::htmlsafe($conf["fullPath"])."'>aplicativo web</a>.</p>");
574
575 mail::send($to, [], $subject, $body);
576 }
577 }
578
579 return true;
580 }
581
582 public static function remove($id) {
583 global $con;
584
585 $sid = (int)$id;
586
587 $incident = incidents::get($id);
588 if ($incident === false) return false;
589
590 if (!self::deleteAttachment($id, "ALL")) return false;
591
592 return mysqli_query($con, "DELETE FROM incidents WHERE id = $sid LIMIT 1");
593 }
594
595 public static function invalidate($id, $updatedby = "ME") {
596 global $con;
597
598 $sid = (int)$id;
599 if ($updatedby === "ME") $updatedby = people::userData("id");
600 $supdatedby = (int)$updatedby;
601
602 $incident = incidents::get($id);
603 if ($incident === false) return false;
604
605 $state = incidents::getStatus($incident);
606 if (!in_array($state, self::$canInvalidateStates)) return false;
607
608 return mysqli_query($con, "UPDATE incidents SET invalidated = 1, updatedby = $supdatedby WHERE id = $sid LIMIT 1");
609 }
610
611 public static function checkIncidentIsFromPerson($incident, $person = "ME", $boolean = false) {
612 global $con;
613
614 if ($person == "ME") $person = people::userData("id");
615
616 $sincident = (int)$incident;
617 $sperson = (int)$person;
618
619 $query = mysqli_query($con, "SELECT i.id FROM incidents i INNER JOIN workers w ON i.worker = w.id INNER JOIN people p ON w.person = p.id WHERE p.id = $sperson AND i.id = $sincident LIMIT 1");
620
621 if (!mysqli_num_rows($query)) {
622 if ($boolean) return false;
623 security::denyUseMethod(security::METHOD_NOTFOUND);
624 }
625
626 return true;
627 }
628}