init
[garradin.git] / include / lib.utils.php
1 <?php
2
3 namespace Garradin;
4
5 class utils
6 {
7 static protected $country_list = null;
8
9 static protected $g2x = null;
10
11 static private $french_date_names = [
12 'January'=>'Janvier', 'February'=>'Février', 'March'=>'Mars', 'April'=>'Avril', 'May'=>'Mai',
13 'June'=>'Juin', 'July'=>'Juillet', 'August'=>'Août', 'September'=>'Septembre', 'October'=>'Octobre',
14 'November'=>'Novembre', 'December'=>'Décembre', 'Monday'=>'Lundi', 'Tuesday'=>'Mardi', 'Wednesday'=>'Mercredi',
15 'Thursday'=>'Jeudi','Friday'=>'Vendredi','Saturday'=>'Samedi','Sunday'=>'Dimanche',
16 'Feb'=>'Fév','Apr'=>'Avr','May'=>'Mai','Jun'=>'Juin', 'Jul'=>'Juil','Aug'=>'Aout','Dec'=>'Déc',
17 'Mon'=>'Lun','Tue'=>'Mar','Wed'=>'Mer','Thu'=>'Jeu','Fri'=>'Ven','Sat'=>'Sam','Sun'=>'Dim'];
18
19 static public function strftime_fr($format=null, $ts=null)
20 {
21 if (is_null($format))
22 {
23 $format = '%d/%m/%Y à %H:%M';
24 }
25
26 $date = strftime($format, $ts);
27 $date = strtr($date, self::$french_date_names);
28 $date = strtolower($date);
29 return $date;
30 }
31
32 static public function date_fr($format=null, $ts=null)
33 {
34 if (is_null($format))
35 {
36 $format = 'd/m/Y à H:i';
37 }
38
39 $date = date($format, $ts);
40 $date = strtr($date, self::$french_date_names);
41 $date = strtolower($date);
42 return $date;
43 }
44
45 static public function sqliteDateToFrench($d, $short = false)
46 {
47 if (strlen($d) == 10 || $short)
48 {
49 $d = substr($d, 0, 10);
50 $f = 'Y-m-d';
51 $f2 = 'd/m/Y';
52 }
53 elseif (strlen($d) == 16)
54 {
55 $f = 'Y-m-d H:i';
56 $f2 = 'd/m/Y H:i';
57 }
58 else
59 {
60 $f = 'Y-m-d H:i:s';
61 $f2 = 'd/m/Y H:i';
62 }
63
64 if ($dt = \DateTime::createFromFormat($f, $d))
65 return $dt->format($f2);
66 else
67 return $d;
68 }
69
70 static public function makeTimestampFromForm($d)
71 {
72 return mktime($d['h'], $d['min'], 0, $d['m'], $d['d'], $d['y']);
73 }
74
75 static public function modifyDate($str, $change)
76 {
77 $date = \DateTime::createFromFormat('Y-m-d', $str);
78 $date->modify($change);
79 return $date->format('Y-m-d');
80 }
81
82 static public function checkDate($str)
83 {
84 if (!preg_match('!^(\d{4})-(\d{2})-(\d{2})$!', $str, $match))
85 return false;
86
87 if (!checkdate($match[2], $match[3], $match[1]))
88 return false;
89
90 return true;
91 }
92
93 static public function checkDateTime($str)
94 {
95 if (!preg_match('!^(\d{4}-\d{2}-\d{2})[T ](\d{2}):(\d{2})!', $str, $match))
96 return false;
97
98 if (!self::checkDate($match[1]))
99 return false;
100
101 if ((int) $match[2] < 0 || (int) $match[2] > 23)
102 return false;
103
104 if ((int) $match[3] < 0 || (int) $match[3] > 59)
105 return false;
106
107 if (isset($match[4]) && ((int) $match[4] < 0 || (int) $match[4] > 59))
108 return false;
109
110 return true;
111 }
112
113 static public function getRequestURI()
114 {
115 if (!empty($_SERVER['REQUEST_URI']))
116 return $_SERVER['REQUEST_URI'];
117 else
118 return false;
119 }
120
121 static public function getSelfURL($no_qs = false)
122 {
123 $uri = self::getRequestUri();
124
125 if (strpos($uri, WWW_URI) === 0)
126 {
127 $uri = substr($uri, strlen(WWW_URI));
128 }
129
130 if ($no_qs && (strpos($uri, '?') !== false))
131 {
132 $uri = substr($uri, 0, strpos($uri, '?'));
133 }
134
135 return WWW_URL . $uri;
136 }
137
138 static public function disableHttpCaching()
139 {
140 header("Cache-Control: no-cache, must-revalidate");
141 header("Expires: Mon, 26 Jul 1997 05:00:00 GMT");
142 header('Pragma: no-cache');
143 }
144
145
146 public static function redirect($destination=false, $exit=true)
147 {
148 if (empty($destination) || !preg_match('/^https?:\/\//', $destination))
149 {
150 if (empty($destination))
151 $destination = WWW_URL;
152 else
153 $destination = WWW_URL . preg_replace('/^\//', '', $destination);
154 }
155
156 if (headers_sent())
157 {
158 echo
159 '<html>'.
160 ' <head>' .
161 ' <script type="text/javascript">' .
162 ' document.location = "' . htmlspecialchars($destination, ENT_QUOTES, 'UTF-8', false) . '";' .
163 ' </script>' .
164 ' </head>'.
165 ' <body>'.
166 ' <div>'.
167 ' <a href="' . htmlspecialchars($destination, ENT_QUOTES, 'UTF-8', false) . '">Cliquez ici pour continuer...</a>'.
168 ' </div>'.
169 ' </body>'.
170 '</html>';
171
172 if ($exit)
173 exit();
174
175 return true;
176 }
177
178 header("Location: " . $destination);
179
180 if ($exit)
181 exit();
182 }
183
184
185 static protected function _sessionStart($force = false)
186 {
187 if (!isset($_SESSION) && ($force || isset($_COOKIE[session_name()])))
188 {
189 session_start();
190 }
191 return true;
192 }
193
194 static public function CSRF_create($key)
195 {
196 self::_sessionStart(true);
197
198 if (!isset($_SESSION['csrf']))
199 {
200 $_SESSION['csrf'] = [];
201 }
202
203 $_SESSION['csrf'][$key] = sha1($key . uniqid($key, true) . time());
204 return $_SESSION['csrf'][$key];
205 }
206
207 static public function CSRF_check($key, $hash=null)
208 {
209 self::_sessionStart();
210
211 if (is_null($hash))
212 {
213 $name = self::CSRF_field_name($key);
214
215 if (!isset($_POST[$name]))
216 return false;
217
218 $hash = $_POST[$name];
219 }
220
221 if (empty($_SESSION['csrf'][$key]))
222 return false;
223
224 if ($_SESSION['csrf'][$key] != $hash)
225 return false;
226
227 unset($_SESSION['csrf'][$key]);
228
229 return true;
230 }
231
232 static public function CSRF_field_name($key)
233 {
234 return 'gecko/'.base64_encode(sha1($key, true));
235 }
236
237 static public function generatePassword($length, $chars='abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890')
238 {
239 $string = '';
240 for ($i = 0; $i < $length; $i++)
241 {
242 $pos = rand(0, strlen($chars)-1);
243 $string .= $chars[$pos];
244 }
245 return $string;
246 }
247
248 static public function post($key)
249 {
250 return isset($_POST[$key]) ? $_POST[$key] : '';
251 }
252
253 static public function get($key)
254 {
255 return isset($_GET[$key]) ? $_GET[$key] : '';
256 }
257
258 static public function getIP()
259 {
260 if (!empty($_SERVER['REMOTE_ADDR']))
261 return $_SERVER['REMOTE_ADDR'];
262 return '';
263 }
264
265 static public function &getCountryList()
266 {
267 if (is_null(self::$country_list))
268 {
269 require_once ROOT . '/include/libs/countries/countries_fr.php';
270 self::$country_list = $countries;
271 }
272
273 return self::$country_list;
274 }
275
276 static public function getCountryName($code)
277 {
278 $list = self::getCountryList();
279
280 if (!isset($list[$code]))
281 return false;
282
283 return $list[$code];
284 }
285
286 /**
287 * Génération pagination à partir de la page courante ($current),
288 * du nombre d'items total ($total), et du nombre d'items par page ($bypage).
289 * $listLength représente la longueur d'items de la pagination à génerer
290 *
291 * @param int $current
292 * @param int $total
293 * @param int $bypage
294 * @param int $listLength
295 * @param bool $showLast Toggle l'affichage du dernier élément de la pagination
296 * @return array
297 */
298 public static function getGenericPagination($current, $total, $bypage, $listLength=11, $showLast = true)
299 {
300 if ($total <= $bypage)
301 return false;
302
303 $total = ceil($total / $bypage);
304
305 if ($total < $current)
306 return false;
307
308 $length = ($listLength / 2);
309
310 $begin = $current - ceil($length);
311 if ($begin < 1)
312 {
313 $begin = 1;
314 }
315
316 $end = $begin + $listLength;
317 if($end > $total)
318 {
319 $begin -= ($end - $total);
320 $end = $total;
321 }
322 if ($begin < 1)
323 {
324 $begin = 1;
325 }
326 if($end==($total-1)) {
327 $end = $total;
328 }
329 if($begin == 2) {
330 $begin = 1;
331 }
332 $out = [];
333
334 if ($current > 1) {
335 $out[] = ['id' => $current - 1, 'label' => '« ' . 'Page précédente', 'class' => 'prev', 'accesskey' => 'a'];
336 }
337
338 if ($begin > 1) {
339 $out[] = ['id' => 1, 'label' => '1 ...', 'class' => 'first'];
340 }
341
342 for ($i = $begin; $i <= $end; $i++)
343 {
344 $out[] = ['id' => $i, 'label' => $i, 'class' => ($i == $current) ? 'current' : ''];
345 }
346
347 if ($showLast && $end < $total) {
348 $out[] = ['id' => $total, 'label' => '... ' . $total, 'class' => 'last'];
349 }
350
351 if ($current < $total) {
352 $out[] = ['id' => $current + 1, 'label' => 'Page suivante' . ' »', 'class' => 'next', 'accesskey' => 'z'];
353 }
354
355 return $out;
356 }
357
358 static public function transliterateToAscii($str, $charset='UTF-8')
359 {
360 // Don't process empty strings
361 if (!trim($str))
362 return $str;
363
364 // We only process non-ascii strings
365 if (preg_match('!^[[:ascii:]]+$!', $str))
366 return $str;
367
368 $str = htmlentities($str, ENT_NOQUOTES, $charset);
369
370 $str = preg_replace('#&([A-za-z])(?:acute|cedil|circ|grave|orn|ring|slash|th|tilde|uml);#', '\1', $str);
371 $str = preg_replace('#&([A-za-z]{2})(?:lig);#', '\1', $str); // pour les ligatures e.g. '&oelig;'
372
373 $str = preg_replace('#&[^;]+;#', '', $str); // supprime les autres caractères
374 $str = preg_replace('![^[:ascii:]]+!', '', $str);
375
376 return $str;
377 }
378
379 static public function htmlLinksOnUrls($str)
380 {
381 return preg_replace_callback('!(?<=\s|^)((?:(ftp|https?|file|ed2k|ircs?)://|(magnet|mailto|data|tel|fax|geo|sips?|xmpp):)([^\s<]+))!',
382 function ($match) {
383 $proto = $match[2] ?: $match[3];
384 $text = ($proto == 'http' || $proto == 'mailto') ? $match[4] : $match[1];
385 return '<a class="'.$proto.'" href="'.htmlspecialchars($match[1], ENT_QUOTES, 'UTF-8').'">'.htmlspecialchars($text, ENT_QUOTES, 'UTF-8').'</a>';
386 }, $str);
387 }
388
389 static public function htmlGarbage2xhtml($str)
390 {
391 if (!self::$g2x)
392 {
393 require_once ROOT . '/include/libs/garbage2xhtml/lib.garbage2xhtml.php';
394 self::$g2x = new \garbage2xhtml;
395 self::$g2x->core_attributes = ['class', 'id', 'title'];
396 }
397
398 return self::$g2x->process($str);
399 }
400
401 static public function htmlSpip($str, $prefix = '')
402 {
403 // Intertitres
404 $str = preg_replace('/(?<!\\\\)\{{3}(\V*)\}{3}/', '<h3>$1</h3>', $str);
405
406 // Gras
407 $str = preg_replace('/(?<!\\\\)\{{2}(\V*)\}{2}/', '<strong>$1</strong>', $str);
408
409 // Italique
410 $str = preg_replace('/(?<!\\\\)\{(\V*)\}/', '<em>$1</em>', $str);
411
412 // Espaces typograhiques
413 $str = preg_replace('/\h*([?!;:»])(\s+|$)/u', '&nbsp;$1$2', $str);
414 $str = preg_replace('/(^|\s+)([«])\h*/u', '$1$2&nbsp;', $str);
415
416 // Liens
417 $str = preg_replace('/(?<!\\\\)\[(.+?)->(.+?)\]/', '<a href="$2">$1</a>', $str);
418 $str = preg_replace('/(?<!\\\\)\[(.+?)\]/', '<a href="$1">$1</a>', $str);
419
420 // Adresses email
421 $str = preg_replace('/<a href="((?!http).*@.*)">/iU', '<a href="mailto:$1">', $str);
422
423 return $str;
424 }
425
426 static public function mail($to, $subject, $content, $additional_headers = [])
427 {
428 // Création du contenu du message
429 $content = wordwrap($content);
430 $content = trim($content);
431
432 $content = preg_replace("#(?<!\r)\n#si", "\r\n", $content);
433
434 // Construction des entêtes
435 $headers = '';
436
437 $config = Config::getInstance();
438
439 if (empty($additional_headers['From']))
440 {
441 $additional_headers['From'] = '"NE PAS REPONDRE" <'.$config->get('email_envoi_automatique').'>';
442 }
443
444 $additional_headers['MIME-Version'] = '1.0';
445 $additional_headers['Content-type'] = 'text/plain; charset=UTF-8';
446 $additional_headers['Return-Path'] = $config->get('email_envoi_automatique');
447
448 foreach ($additional_headers as $name=>$value)
449 {
450 $headers .= $name . ': '.$value."\r\n";
451 }
452
453 $headers = preg_replace("#(?<!\r)\n#si", "\r\n", $headers);
454
455 $subject = '=?UTF-8?B?'.base64_encode($subject).'?=';
456
457 if (is_array($to))
458 {
459 foreach ($to as $t)
460 {
461 return mail($t, $suject, $content, $headers);
462 }
463 }
464 else
465 {
466 return mail($to, $subject, $content, $headers);
467 }
468 }
469
470 static public function clearCaches()
471 {
472 $path = DATA_ROOT . '/cache/compiled';
473 $dir = dir($path);
474
475 while ($file = $dir->read())
476 {
477 if ($file[0] != '.')
478 {
479 unlink($path . '/' . $file);
480 }
481 }
482
483 $dir->close();
484 return true;
485 }
486
487 static public function suggestPassword()
488 {
489 require_once ROOT . '/include/libs/passphrase/lib.passphrase.french.php';
490 return \Passphrase::generate();
491 }
492
493 static public function checkIBAN($iban)
494 {
495 $iban = substr($iban, 4) . substr($iban, 0, 4);
496 $iban = str_replace(range('A', 'Z'), range(10, 35), $iban);
497 return (bcmod($iban, 97) == 1);
498 }
499
500 static public function IBAN_RIB($iban)
501 {
502 if (substr($iban, 0, 2) != 'FR')
503 {
504 return '';
505 }
506
507 return substr($iban, 4, 5) // Code banque
508 . ' ' . substr($iban, 4+5, 5) // Code guichet
509 . ' ' . substr($iban, 4+5+5, -2) // Numéro de compte
510 . ' ' . substr($iban, -2); // Clé RIB
511 }
512
513 static public function checkBIC($bic)
514 {
515 return preg_match('!^[A-Z]{4}[A-Z]{2}[1-9A-Z]{2}(?:[A-Z\d]{3})?$!', $bic);
516 }
517
518 static public function normalizePhoneNumber($n)
519 {
520 $n = preg_replace('!(\+\d+)\(0\)!', '\\1', $n);
521 $n = preg_replace('![^\d\+]!', '', $n);
522 return $n;
523 }
524
525 static public function write_ini_string($in)
526 {
527 $out = '';
528 $get_ini_line = function ($key, $value) use (&$get_ini_line)
529 {
530 if (is_bool($value))
531 {
532 return $key . ' = ' . ($value ? 'true' : 'false');
533 }
534 elseif (is_numeric($value))
535 {
536 return $key . ' = ' . $value;
537 }
538 elseif (is_array($value))
539 {
540 $out = '';
541 foreach ($value as $row)
542 {
543 $out .= $get_ini_line($key . '[]', $row) . "\n";
544 }
545
546 return substr($out, 0, -1);
547 }
548 else
549 {
550 return $key . ' = "' . str_replace('"', '\\"', $value) . '"';
551 }
552 };
553
554 foreach ($in as $key=>$value)
555 {
556 if (is_array($value) && is_string($key))
557 {
558 $out .= '[' . $key . "]\n";
559
560 foreach ($value as $row_key=>$row_value)
561 {
562 $out .= $get_ini_line($row_key, $row_value) . "\n";
563 }
564
565 $out .= "\n";
566 }
567 else
568 {
569 $out .= $get_ini_line($key, $value) . "\n";
570 }
571 }
572
573 return $out;
574 }
575
576 static public function getMaxUploadSize()
577 {
578 return min([
579 self::return_bytes(ini_get('upload_max_filesize')),
580 self::return_bytes(ini_get('post_max_size')),
581 self::return_bytes(ini_get('memory_limit'))
582 ]);
583 }
584
585
586 static public function return_bytes ($size_str)
587 {
588 switch (substr($size_str, -1))
589 {
590 case 'G': case 'g': return (int)$size_str * pow(1024, 3);
591 case 'M': case 'm': return (int)$size_str * pow(1024, 2);
592 case 'K': case 'k': return (int)$size_str * 1024;
593 default: return $size_str;
594 }
595 }
596
597 static public function deleteRecursive($path, $delete_target = false)
598 {
599 if (!file_exists($path))
600 return false;
601
602 $dir = dir($path);
603 if (!$dir) return false;
604
605 while ($file = $dir->read())
606 {
607 if ($file == '.' || $file == '..')
608 continue;
609
610 if (is_dir($path . '/' . $file))
611 {
612 if (!self::deleteRecursive($path . '/' . $file, true))
613 return false;
614 }
615 else
616 {
617 unlink($path . '/' . $file);
618 }
619 }
620
621 $dir->close();
622 rmdir($path);
623
624 return true;
625 }
626
627 static public function plugin_url($params = [])
628 {
629 if (isset($params['id']))
630 {
631 $url = WWW_URL . 'admin/plugin/' . $params['id'] . '/';
632 }
633 else
634 {
635 $url = PLUGIN_URL;
636 }
637
638 if (!empty($params['file']))
639 $url .= $params['file'];
640
641 if (!empty($params['query']))
642 {
643 $url .= '?';
644
645 if (!(is_numeric($params['query']) && (int)$params['query'] === 1) && $params['query'] !== true)
646 $url .= $params['query'];
647 }
648
649 return $url;
650 }
651
652 static public function find_csv_delim($fp)
653 {
654 $line = '';
655
656 while ($line === '' && !feof($fp))
657 {
658 $line = trim(fgets($fp, 4096));
659 }
660
661 // Delete the columns content
662 $line = preg_replace('/".*?"/', '', $line);
663
664 $delims = [
665 ';' => substr_count($line, ';'),
666 ',' => substr_count($line, ','),
667 "\t"=> substr_count($line, "\t")
668 ];
669
670 arsort($delims);
671 reset($delims);
672
673 rewind($fp);
674 return key($delims);
675 }
676
677 }