blob: 2b063f9ad7ffe18fa31a69ba74d29b2f51870392 [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 validations {
22 const DEFAULT_REMINDER_GRACE_PERIOD = 3; // When sending email notifications about pending elements to verify,
23 // only elements effective until the current day minus the grace period
24 // will be checked for validation. (value is given in days)
25
26 const METHOD_SIMPLE = 0;
27 const METHOD_AUTOVALIDATION = 1;
28
29 public static $methodCodename = [
30 0 => "simple",
31 1 => "autovalidation"
32 ];
33
34 public static $methodName = [
35 0 => "Validación por dirección IP",
36 1 => "Validación automática"
37 ];
38
39 public static $methods = [self::METHOD_SIMPLE, self::METHOD_AUTOVALIDATION];
40 public static $manualMethods = [self::METHOD_SIMPLE];
41
42 public static function reminderGracePeriod() {
43 return $conf["validation"]["gracePeriod"] ?? self::DEFAULT_REMINDER_GRACE_PERIOD;
44 }
45
46 public static function numPending($userId = "ME", $gracePeriod = 0) {
47 global $con;
48
49 if ($userId === "ME") $userId = people::userData("id");
50 $suser = (int)$userId;
51
52 $query = mysqli_query($con, "SELECT COUNT(*) count FROM incidents i INNER JOIN workers w ON i.worker = w.id WHERE w.person = ".$suser." AND ".incidents::$workerPendingWhere." AND ".incidents::$activeWhere.($gracePeriod === false ? "" : " AND (i.day + i.begins) < ".(int)(time() - (int)$gracePeriod*(24*60*60))));
53
54 if (!mysqli_num_rows($query)) return "?";
55
56 $row = mysqli_fetch_assoc($query);
57
58 $count = (int)(isset($row["count"]) ? $row["count"] : 0);
59
60 $query2 = mysqli_query($con, "SELECT COUNT(*) count FROM records r INNER JOIN workers w ON r.worker = w.id WHERE w.person = ".$suser." AND ".registry::$notInvalidatedWhere." AND ".registry::$workerPendingWhere.($gracePeriod === false ? "" : " AND r.day < ".(int)(time() - (int)$gracePeriod*(24*60*60))));
61
62 if (!mysqli_num_rows($query2)) return "?";
63
64 $row2 = mysqli_fetch_assoc($query2);
65
66 return $count + (int)(isset($row2["count"]) ? $row2["count"] : 0);
67 }
68
69 public static function getAllowedMethods() {
70 global $conf;
71
72 $allowedMethods = [];
73 foreach (self::$manualMethods as $method) {
74 if (in_array($method, $conf["validation"]["allowedMethods"])) $allowedMethods[] = $method;
75 }
76
77 return $allowedMethods;
78 }
79
80 private static function string2array($string) {
81 if (!is_string($string) || empty($string)) return [];
82 $explode = explode(",", $string);
83
84 $array = [];
85 foreach ($explode as $el) {
86 $array[] = (int)$el;
87 }
88
89 return $array;
90 }
91
92 private static function createSimpleAttestation($method, $user) {
93 $attestation = [];
94 if (!isset($_SERVER["REMOTE_ADDR"])) return false;
95 $attestation["ipAddress"] = (string)$_SERVER["REMOTE_ADDR"];
96 if ($method === self::METHOD_AUTOVALIDATION) $attestation["user"] = (int)$user;
97 return $attestation;
98 }
99
100 private static function createIncidentAttestation(&$incident, $method, $user) {
101 switch ($method) {
102 case self::METHOD_SIMPLE:
103 case self::METHOD_AUTOVALIDATION:
104 return self::createSimpleAttestation($method, $user);
105 break;
106
107 default:
108 return false;
109 }
110 }
111
112 private static function createRecordAttestation(&$record, $method, $user) {
113 switch ($method) {
114 case self::METHOD_SIMPLE:
115 case self::METHOD_AUTOVALIDATION:
116 return self::createSimpleAttestation($method, $user);
117 break;
118
119 default:
120 return false;
121 }
122 }
123
124 private static function createValidation($method, $attestation) {
125 $validation = [];
126 $validation["method"] = (int)$method;
127 $validation["timestamp"] = time();
128 $validation["attestation"] = $attestation;
129
130 return $validation;
131 }
132
133 public static function validateIncident($id, $method, $user, $userCheck = true, $stateCheck = true) {
134 global $con;
135
136 if ($user == "ME") $user = people::userData("id");
137 if ($userCheck && !incidents::checkIncidentIsFromPerson($id, "ME", true)) return false;
138
139 $incident = incidents::get($id, true);
140 if ($incident === false || ($stateCheck && $incident["state"] !== incidents::STATE_REGISTERED)) return false;
141
142 $attestation = self::createIncidentAttestation($incident, $method, $user);
143 if ($attestation === false) return false;
144
145 $validation = self::createValidation($method, $attestation);
146
147 $svalidation = db::sanitize(json_encode($validation));
148 $sid = (int)$incident["id"];
149
150 return mysqli_query($con, "UPDATE incidents SET workervalidated = 1, workervalidation = '$svalidation' WHERE id = $sid LIMIT 1");
151 }
152
153 public static function validateRecord($id, $method, $user, $userCheck = true) {
154 global $con;
155
156 if ($user == "ME") $user = people::userData("id");
157 if ($userCheck && !registry::checkRecordIsFromPerson($id, "ME", true)) return false;
158
159 $record = registry::get($id, true);
160 if ($record === false || $record["state"] !== registry::STATE_REGISTERED) return false;
161
162 $attestation = self::createRecordAttestation($record, $method, $user);
163 if ($attestation === false) return false;
164
165 $validation = self::createValidation($method, $attestation);
166
167 $svalidation = db::sanitize(json_encode($validation));
168 $sid = (int)$record["id"];
169
170 return mysqli_query($con, "UPDATE records SET workervalidated = 1, workervalidation = '$svalidation' WHERE id = $sid LIMIT 1");
171 }
172
173 public static function validate($method, $incidents, $records, $user = "ME") {
174 if (!in_array($method, self::getAllowedMethods())) return -1;
175
176 $incidents = self::string2array($incidents);
177 $records = self::string2array($records);
178
179 if ($user == "ME") $user = people::userData("id");
180
181 $flag = false;
182 foreach ($incidents as $incident) {
183 if (!self::validateIncident($incident, $method, $user)) $flag = true;
184 }
185 foreach ($records as $record) {
186 if (!self::validateRecord($record, $method, $user)) $flag = true;
187 }
188
189 return ($flag ? 1 : 0);
190 }
191
192 public static function getPeopleWithPendingValidations($gracePeriod = "DEFAULT") {
193 if ($gracePeriod === "DEFAULT") $gracePeriod = self::reminderGracePeriod();
194
195 $pendingPeople = [];
196
197 $people = people::getAll();
198 foreach ($people as $p) {
199 $numPending = self::numPending((int)$p["id"], $gracePeriod);
200
201 if ($numPending > 0) {
202 $pending = [];
203 $pending["person"] = $p;
204 $pending["numPending"] = $numPending;
205
206 $pendingPeople[] = $pending;
207 }
208 }
209
210 return $pendingPeople;
211 }
212
213 public static function sendPendingValidationsReminder() {
214 global $conf;
215
216 if (!$conf["mail"]["enabled"]) {
217 echo "[error] The mail functionality is not enabled in config.php.\n";
218 return false;
219 }
220
221 if (!$conf["mail"]["capabilities"]["sendPendingValidationsReminder"]) {
222 echo "[error] The pending validation reminders functionality is not inabled in config.php.\n";
223 return false;
224 }
225
226 $pendingPeople = self::getPeopleWithPendingValidations();
227
228 foreach ($pendingPeople as $p) {
229 if (!isset($p["person"]["email"]) || empty($p["person"]["email"])) {
230 echo "[info] ".$p["person"]["id"]." doesn't have an email address defined.\n";
231 continue;
232 }
233
234 $to = [["email" => $p["person"]["email"]]];
235
236 $subject = "[Recordatorio] Tienes validaciones pendientes en el aplicativo de control horario";
237 $body = mail::bodyTemplate("<p>Hola ".security::htmlsafe(($p["person"]["name"]) ?? "").",</p>
238 <p>Este es un mensaje automático para avisarte de que tienes ".(int)$p["numPending"]." incidencias y/o registros pendientes de validar en el aplicativo de control horario.</p>
239 <p>Para validarlos, puedes acceder al aplicativo desde la siguiente dirección:</p>
240 <ul>
241 <li><a href='".security::htmlsafe($conf["fullPath"])."'>".security::htmlsafe($conf["fullPath"])."</a></li>
242 </ul>");
243
244 if (mail::send($to, [], $subject, $body)) {
245 echo "[info] Mail sent to ".$p["person"]["id"]." successfuly.\n";
246 } else {
247 echo "[error] The email couldn't be sent to ".$p["person"]["id"]."\n";
248 }
249 }
250
251 return true;
252 }
253}