Copybara bot | be50d49 | 2023-11-30 00:16:42 +0100 | [diff] [blame] | 1 | <?php |
| 2 | class 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 | } |