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