dolibarr 22.0.5
functions2.lib.php
Go to the documentation of this file.
1<?php
2/* Copyright (C) 2008-2011 Laurent Destailleur <eldy@users.sourceforge.net>
3 * Copyright (C) 2008-2012 Regis Houssin <regis.houssin@inodbox.com>
4 * Copyright (C) 2008 Raphael Bertrand (Resultic) <raphael.bertrand@resultic.fr>
5 * Copyright (C) 2014-2016 Marcos García <marcosgdf@gmail.com>
6 * Copyright (C) 2015 Ferran Marcet <fmarcet@2byte.es>
7 * Copyright (C) 2015-2016 Raphaël Doursenaud <rdoursenaud@gpcsolutions.fr>
8 * Copyright (C) 2017 Juanjo Menent <jmenent@2byte.es>
9 * Copyright (C) 2024 MDW <mdeweerd@users.noreply.github.com>
10 * Copyright (C) 2024 Frédéric France <frederic.france@free.fr>
11 *
12 * This program is free software; you can redistribute it and/or modify
13 * it under the terms of the GNU General Public License as published by
14 * the Free Software Foundation; either version 3 of the License, or
15 * (at your option) any later version.
16 *
17 * This program is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 * GNU General Public License for more details.
21 *
22 * You should have received a copy of the GNU General Public License
23 * along with this program. If not, see <https://www.gnu.org/licenses/>.
24 * or see https://www.gnu.org/
25 */
26
33// Enable this line to trace path when function is called.
34//print xdebug_print_function_stack('Functions2.lib was called');exit;
35
42function jsUnEscape($source)
43{
44 $decodedStr = "";
45 $pos = 0;
46 $len = strlen($source);
47 while ($pos < $len) {
48 $charAt = substr($source, $pos, 1);
49 if ($charAt == '%') {
50 $pos++;
51 $charAt = substr($source, $pos, 1);
52 if ($charAt == 'u') {
53 // we got a unicode character
54 $pos++;
55 $unicodeHexVal = substr($source, $pos, 4);
56 $unicode = hexdec($unicodeHexVal);
57 $entity = "&#".$unicode.';';
58 $decodedStr .= mb_convert_encoding($entity, 'UTF-8', 'ISO-8859-1');
59 $pos += 4;
60 } else {
61 // we have an escaped ascii character
62 $hexVal = substr($source, $pos, 2);
63 $decodedStr .= chr(hexdec($hexVal));
64 $pos += 2;
65 }
66 } else {
67 $decodedStr .= $charAt;
68 $pos++;
69 }
70 }
71 return dol_html_entity_decode($decodedStr, ENT_COMPAT | ENT_HTML5);
72}
73
74
84function dolGetModulesDirs($subdir = '')
85{
86 global $conf;
87
88 $modulesdir = array();
89
90 foreach ($conf->file->dol_document_root as $type => $dirroot) {
91 // Default core/modules dir
92 if ($type === 'main') {
93 $modulesdir[$dirroot.'/core/modules'.$subdir.'/'] = $dirroot.'/core/modules'.$subdir.'/';
94 }
95
96 // Scan dir from external modules
97 $handle = @opendir($dirroot);
98 if (is_resource($handle)) {
99 while (($file = readdir($handle)) !== false) {
100 if (preg_match('/disabled/', $file)) {
101 continue; // We discard module if it contains disabled into name.
102 }
103
104 if (substr($file, 0, 1) != '.' && is_dir($dirroot.'/'.$file) && strtoupper(substr($file, 0, 3)) != 'CVS' && $file != 'includes') {
105 if (is_dir($dirroot.'/'.$file.'/core/modules'.$subdir.'/')) {
106 $modulesdir[$dirroot.'/'.$file.'/core/modules'.$subdir.'/'] = $dirroot.'/'.$file.'/core/modules'.$subdir.'/';
107 }
108 }
109 }
110 closedir($handle);
111 }
112 }
113 return $modulesdir;
114}
115
116
123function dol_getDefaultFormat($outputlangs = null)
124{
125 global $langs;
126
127 $selected = 'EUA4';
128 if (!$outputlangs) {
129 $outputlangs = $langs;
130 }
131
132 if ($outputlangs->defaultlang == 'ca_CA') {
133 $selected = 'CAP4'; // Canada
134 }
135 if ($outputlangs->defaultlang == 'en_US') {
136 $selected = 'USLetter'; // US
137 }
138 return $selected;
139}
140
141
150function dol_print_object_info($object, $usetable = 0)
151{
152 global $langs, $db;
153
154 // Load translation files required by the page
155 $langs->loadLangs(array('other', 'admin'));
156
157 include_once DOL_DOCUMENT_ROOT.'/core/lib/date.lib.php';
158
159 $deltadateforserver = getServerTimeZoneInt('now');
160 $deltadateforclient = ((int) $_SESSION['dol_tz'] + (int) $_SESSION['dol_dst']);
161 //$deltadateforcompany=((int) $_SESSION['dol_tz'] + (int) $_SESSION['dol_dst']);
162 $deltadateforuser = round($deltadateforclient - $deltadateforserver);
163 //print "x".$deltadateforserver." - ".$deltadateforclient." - ".$deltadateforuser;
164
165 if ($usetable) {
166 print '<table class="border tableforfield centpercent">';
167 }
168
169 // Import key
170 if (!empty($object->import_key)) {
171 if ($usetable) {
172 print '<tr><td class="titlefield">';
173 }
174 print $langs->trans("ImportedWithSet");
175 if ($usetable) {
176 print '</td><td>';
177 } else {
178 print ': ';
179 }
180 print $object->import_key;
181 if ($usetable) {
182 print '</td></tr>';
183 } else {
184 print '<br>';
185 }
186 }
187
188 // User creation (old method using already loaded object and not id is kept for backward compatibility)
189 if (!empty($object->user_creation) || !empty($object->user_creation_id)) {
190 if ($usetable) {
191 print '<tr><td class="titlefield">';
192 }
193 print $langs->trans("CreatedBy");
194 if ($usetable) {
195 print '</td><td>';
196 } else {
197 print ': ';
198 }
199 if (! empty($object->user_creation) && is_object($object->user_creation)) { // deprecated mode
200 if ($object->user_creation->id) {
201 print $object->user_creation->getNomUrl(-1, '', 0, 0, 0);
202 } else {
203 print $langs->trans("Unknown");
204 }
205 } else {
206 $userstatic = new User($db);
207 $userstatic->fetch($object->user_creation_id);
208 if ($userstatic->id) {
209 print $userstatic->getNomUrl(-1, '', 0, 0, 0);
210 } else {
211 print $langs->trans("Unknown");
212 }
213 }
214 if ($usetable) {
215 print '</td></tr>';
216 } else {
217 print '<br>';
218 }
219 }
220
221 // Date creation
222 if (!empty($object->date_creation)) {
223 if ($usetable) {
224 print '<tr><td class="titlefield">';
225 }
226 print $langs->trans("DateCreation");
227 if ($usetable) {
228 print '</td><td>';
229 } else {
230 print ': ';
231 }
232 print dol_print_date($object->date_creation, 'dayhour', 'tzserver');
233 if ($deltadateforuser) {
234 print ' <span class="opacitymedium">'.$langs->trans("CurrentHour").'</span> &nbsp; / &nbsp; '.dol_print_date($object->date_creation, "dayhour", "tzuserrel").' &nbsp;<span class="opacitymedium">'.$langs->trans("ClientHour").'</span>';
235 }
236 if ($usetable) {
237 print '</td></tr>';
238 } else {
239 print '<br>';
240 }
241 }
242
243 // User change (old method using already loaded object and not id is kept for backward compatibility)
244 if (!empty($object->user_modification) || !empty($object->user_modification_id)) {
245 if ($usetable) {
246 print '<tr><td class="titlefield">';
247 }
248 print $langs->trans("ModifiedBy");
249 if ($usetable) {
250 print '</td><td>';
251 } else {
252 print ': ';
253 }
254 if (is_object($object->user_modification)) {
255 if ($object->user_modification->id) {
256 print $object->user_modification->getNomUrl(-1, '', 0, 0, 0);
257 } else {
258 print $langs->trans("Unknown");
259 }
260 } else {
261 $userstatic = new User($db);
262 $userstatic->fetch($object->user_modification_id);
263 if ($userstatic->id) {
264 print $userstatic->getNomUrl(-1, '', 0, 0, 0);
265 } else {
266 print $langs->trans("Unknown");
267 }
268 }
269 if ($usetable) {
270 print '</td></tr>';
271 } else {
272 print '<br>';
273 }
274 }
275
276 // Date change
277 if (!empty($object->date_modification)) {
278 if ($usetable) {
279 print '<tr><td class="titlefield">';
280 }
281 print $langs->trans("DateLastModification");
282 if ($usetable) {
283 print '</td><td>';
284 } else {
285 print ': ';
286 }
287 print dol_print_date($object->date_modification, 'dayhour', 'tzserver');
288 if ($deltadateforuser) {
289 print ' <span class="opacitymedium">'.$langs->trans("CurrentHour").'</span> &nbsp; / &nbsp; '.dol_print_date($object->date_modification, "dayhour", "tzuserrel").' &nbsp;<span class="opacitymedium">'.$langs->trans("ClientHour").'</span>';
290 }
291 if ($usetable) {
292 print '</td></tr>';
293 } else {
294 print '<br>';
295 }
296 }
297
298 // User validation (old method using already loaded object and not id is kept for backward compatibility)
299 if (!empty($object->user_validation) || !empty($object->user_validation_id)) {
300 if ($usetable) {
301 print '<tr><td class="titlefield">';
302 }
303 print $langs->trans("ValidatedBy");
304 if ($usetable) {
305 print '</td><td>';
306 } else {
307 print ': ';
308 }
309 if (is_object($object->user_validation)) {
310 if ($object->user_validation->id) {
311 print $object->user_validation->getNomUrl(-1, '', 0, 0, 0);
312 } else {
313 print $langs->trans("Unknown");
314 }
315 } else {
316 $userstatic = new User($db);
317 $userstatic->fetch($object->user_validation_id ? $object->user_validation_id : $object->user_validation);
318 if ($userstatic->id) {
319 print $userstatic->getNomUrl(-1, '', 0, 0, 0);
320 } else {
321 print $langs->trans("Unknown");
322 }
323 }
324 if ($usetable) {
325 print '</td></tr>';
326 } else {
327 print '<br>';
328 }
329 }
330
331 // Date validation
332 if (!empty($object->date_validation)) {
333 if ($usetable) {
334 print '<tr><td class="titlefield">';
335 }
336 print $langs->trans("DateValidation");
337 if ($usetable) {
338 print '</td><td>';
339 } else {
340 print ': ';
341 }
342 print dol_print_date($object->date_validation, 'dayhour', 'tzserver');
343 if ($deltadateforuser) {
344 print ' <span class="opacitymedium">'.$langs->trans("CurrentHour").'</span> &nbsp; / &nbsp; '.dol_print_date($object->date_validation, "dayhour", 'tzuserrel').' &nbsp;<span class="opacitymedium">'.$langs->trans("ClientHour").'</span>';
345 }
346 if ($usetable) {
347 print '</td></tr>';
348 } else {
349 print '<br>';
350 }
351 }
352
353 // User approve (old method using already loaded object and not id is kept for backward compatibility)
354 if (!empty($object->user_approve) || !empty($object->user_approve_id)) {
355 if ($usetable) {
356 print '<tr><td class="titlefield">';
357 }
358 print $langs->trans("ApprovedBy");
359 if ($usetable) {
360 print '</td><td>';
361 } else {
362 print ': ';
363 }
364 // user_approve is not defined in Dolibarr code @phan-suppress-next-line PhanUndeclaredProperty
365 if (!empty($object->user_approve) && is_object($object->user_approve)) {
366 if ($object->user_approve->id) { // @phan-suppress-current-line PhanUndeclaredProperty
367 // @phan-suppress-next-line PhanUndeclaredProperty,PhanPluginUnknownObjectMethodCall
368 print $object->user_approve->getNomUrl(-1, '', 0, 0, 0);
369 } else {
370 print $langs->trans("Unknown");
371 }
372 } else {
373 $userstatic = new User($db);
374 $userstatic->fetch($object->user_approve_id ? $object->user_approve_id : $object->user_approve);
375 if ($userstatic->id) {
376 print $userstatic->getNomUrl(-1, '', 0, 0, 0);
377 } else {
378 print $langs->trans("Unknown");
379 }
380 }
381 if ($usetable) {
382 print '</td></tr>';
383 } else {
384 print '<br>';
385 }
386 }
387
388 // Date approve
389 if (!empty($object->date_approve) || !empty($object->date_approval)) {
390 '@phan-var-force ExpenseReport|CommandeFournisseur $object';
391 if ($usetable) {
392 print '<tr><td class="titlefield">';
393 }
394 print $langs->trans("DateApprove");
395 if ($usetable) {
396 print '</td><td>';
397 } else {
398 print ': ';
399 }
400 print dol_print_date($object->date_approve ? $object->date_approve : $object->date_approval, 'dayhour', 'tzserver');
401 if ($deltadateforuser) {
402 print ' <span class="opacitymedium">'.$langs->trans("CurrentHour").'</span> &nbsp; / &nbsp; '.dol_print_date($object->date_approve, "dayhour", 'tzuserrel').' &nbsp;<span class="opacitymedium">'.$langs->trans("ClientHour").'</span>';
403 }
404 if ($usetable) {
405 print '</td></tr>';
406 } else {
407 print '<br>';
408 }
409 }
410
411 // User approve
412 if (!empty($object->user_approve_id2)) {
413 '@phan-var-force CommandeFournisseur $object';
414 if ($usetable) {
415 print '<tr><td class="titlefield">';
416 }
417 print $langs->trans("ApprovedBy");
418 if ($usetable) {
419 print '</td><td>';
420 } else {
421 print ': ';
422 }
423 $userstatic = new User($db);
424 $userstatic->fetch($object->user_approve_id2);
425 if ($userstatic->id) {
426 print $userstatic->getNomUrl(-1, '', 0, 0, 0);
427 } else {
428 print $langs->trans("Unknown");
429 }
430 if ($usetable) {
431 print '</td></tr>';
432 } else {
433 print '<br>';
434 }
435 }
436
437 // Date approve
438 if (!empty($object->date_approve2)) {
439 if ($usetable) {
440 print '<tr><td class="titlefield">';
441 }
442 print $langs->trans("DateApprove2");
443 if ($usetable) {
444 print '</td><td>';
445 } else {
446 print ': ';
447 }
448 print dol_print_date($object->date_approve2, 'dayhour', 'tzserver');
449 if ($deltadateforuser) {
450 print ' <span class="opacitymedium">'.$langs->trans("CurrentHour").'</span> &nbsp; / &nbsp; '.dol_print_date($object->date_approve2, "dayhour", 'tzuserrel').' &nbsp;<span class="opacitymedium">'.$langs->trans("ClientHour").'</span>';
451 }
452 if ($usetable) {
453 print '</td></tr>';
454 } else {
455 print '<br>';
456 }
457 }
458
459 // User signature
460 if (!empty($object->user_signature) || !empty($object->user_signature_id)) {
461 '@phan-var-force Propal $object';
462 if ($usetable) {
463 print '<tr><td class="titlefield">';
464 }
465 print $langs->trans('SignedBy');
466 if ($usetable) {
467 print '</td><td>';
468 } else {
469 print ': ';
470 }
471 if (is_object($object->user_signature)) {
472 if ($object->user_signature->id) {
473 print $object->user_signature->getNomUrl(-1, '', 0, 0, 0);
474 } else {
475 print $langs->trans('Unknown');
476 }
477 } else {
478 $userstatic = new User($db);
479 $userstatic->fetch($object->user_signature_id ? $object->user_signature_id : $object->user_signature);
480 if ($userstatic->id) {
481 print $userstatic->getNomUrl(-1, '', 0, 0, 0);
482 } else {
483 print $langs->trans('Unknown');
484 }
485 }
486 if ($usetable) {
487 print '</td></tr>';
488 } else {
489 print '<br>';
490 }
491 }
492
493 // Date signature
494 if (!empty($object->date_signature)) {
495 if ($usetable) {
496 print '<tr><td class="titlefield">';
497 }
498 print $langs->trans('DateSigning');
499 if ($usetable) {
500 print '</td><td>';
501 } else {
502 print ': ';
503 }
504 print dol_print_date($object->date_signature, 'dayhour');
505 if ($deltadateforuser) {
506 print ' <span class="opacitymedium">'.$langs->trans('CurrentHour').'</span> &nbsp; / &nbsp; '.dol_print_date($object->date_signature, 'dayhour', 'tzuserrel').' &nbsp;<span class="opacitymedium">'.$langs->trans('ClientHour').'</span>';
507 }
508 if ($usetable) {
509 print '</td></tr>';
510 } else {
511 print '<br>';
512 }
513 }
514
515 // User close
516 if (!empty($object->user_closing_id)) {
517 if ($usetable) {
518 print '<tr><td class="titlefield">';
519 }
520 print $langs->trans("ClosedBy");
521 if ($usetable) {
522 print '</td><td>';
523 } else {
524 print ': ';
525 }
526 $userstatic = new User($db);
527 $userstatic->fetch($object->user_closing_id);
528 if ($userstatic->id) {
529 print $userstatic->getNomUrl(-1, '', 0, 0, 0);
530 } else {
531 print $langs->trans("Unknown");
532 }
533 if ($usetable) {
534 print '</td></tr>';
535 } else {
536 print '<br>';
537 }
538 }
539
540 // Date close
541 if (!empty($object->date_cloture) || !empty($object->date_closing)) {
542 if (isset($object->date_cloture) && !empty($object->date_cloture)) {
543 $object->date_closing = $object->date_cloture;
544 }
545 if ($usetable) {
546 print '<tr><td class="titlefield">';
547 }
548 print $langs->trans("DateClosing");
549 if ($usetable) {
550 print '</td><td>';
551 } else {
552 print ': ';
553 }
554 print dol_print_date($object->date_closing, 'dayhour', 'tzserver');
555 if ($deltadateforuser) {
556 print ' <span class="opacitymedium">'.$langs->trans("CurrentHour").'</span> &nbsp; / &nbsp; '.dol_print_date($object->date_closing, "dayhour", 'tzuserrel').' &nbsp;<span class="opacitymedium">'.$langs->trans("ClientHour").'</span>';
557 }
558 if ($usetable) {
559 print '</td></tr>';
560 } else {
561 print '<br>';
562 }
563 }
564
565 // User conciliate
566 if (!empty($object->user_rappro) || !empty($object->user_rappro_id)) {
567 '@phan-var-force Account $object';
568 if ($usetable) {
569 print '<tr><td class="titlefield">';
570 }
571 print $langs->trans("ReconciledBy");
572 if ($usetable) {
573 print '</td><td>';
574 } else {
575 print ': ';
576 }
577 if (is_object($object->user_rappro)) {
578 $user_rappro = $object->user_rappro;
579 '@phan-var-force User $user_rappro';
580 if ($user_rappro->id) {
581 print $user_rappro->getNomUrl(-1, '', 0, 0, 0);
582 } else {
583 print $langs->trans("Unknown");
584 }
585 } else {
586 $userstatic = new User($db);
587 $userstatic->fetch($object->user_rappro_id ? $object->user_rappro_id : $object->user_rappro);
588 if ($userstatic->id) {
589 print $userstatic->getNomUrl(1, '', 0, 0, 0);
590 } else {
591 print $langs->trans("Unknown");
592 }
593 }
594 if ($usetable) {
595 print '</td></tr>';
596 } else {
597 print '<br>';
598 }
599 }
600
601 // Date conciliate Note: date_rappro is not found on Dolibarr classes
602 if (!empty($object->date_rappro)) {
603 // Datte
604 if ($usetable) {
605 print '<tr><td class="titlefield">';
606 }
607 print $langs->trans("DateConciliating");
608 if ($usetable) {
609 print '</td><td>';
610 } else {
611 print ': ';
612 }
613 print dol_print_date($object->date_rappro, 'dayhour', 'tzserver'); // @phan-suppress-current-line PhanUndeclaredProperty
614 if ($deltadateforuser) {
615 print ' <span class="opacitymedium">'.$langs->trans("CurrentHour").'</span> &nbsp; / &nbsp; '.dol_print_date($object->date_rappro, "dayhour", 'tzuserrel').' &nbsp;<span class="opacitymedium">'.$langs->trans("ClientHour").'</span>'; // @phan-suppress-current-line PhanUndeclaredProperty
616 }
617 if ($usetable) {
618 print '</td></tr>';
619 } else {
620 print '<br>';
621 }
622 }
623
624 // Date send
625 if (!empty($object->date_envoi)) {
626 '@phan-var-force Mailing $object';
627 if ($usetable) {
628 print '<tr><td class="titlefield">';
629 }
630 print $langs->trans("DateLastSend");
631 if ($usetable) {
632 print '</td><td>';
633 } else {
634 print ': ';
635 }
636 print dol_print_date($object->date_envoi, 'dayhour', 'tzserver');
637 if ($deltadateforuser) {
638 print ' <span class="opacitymedium">'.$langs->trans("CurrentHour").'</span> &nbsp; / &nbsp; '.dol_print_date($object->date_envoi, "dayhour", 'tzuserrel').' &nbsp;<span class="opacitymedium">'.$langs->trans("ClientHour").'</span>';
639 }
640 if ($usetable) {
641 print '</td></tr>';
642 } else {
643 print '<br>';
644 }
645 }
646
647 if ($usetable) {
648 print '</table>';
649 }
650}
651
652
661function dolAddEmailTrackId($email, $trackingid)
662{
663 $tmp = explode('@', $email);
664 return $tmp[0].'+'.$trackingid.'@'.(isset($tmp[1]) ? $tmp[1] : '');
665}
666
673function isValidMailDomain($mail)
674{
675 list($user, $domain) = explode("@", $mail, 2);
676 return ($domain ? isValidMXRecord($domain) : 0);
677}
678
692function isValidUrl($url, $http = 0, $pass = 0, $port = 0, $path = 0, $query = 0, $anchor = 0)
693{
694 $ValidUrl = 0;
695 $urlregex = '';
696
697 // SCHEME
698 if ($http) {
699 $urlregex .= "^(http:\/\/|https:\/\/)";
700 }
701
702 // USER AND PASS
703 if ($pass) {
704 $urlregex .= "([a-z0-9+!*(),;?&=\$_.-]+(\:[a-z0-9+!*(),;?&=\$_.-]+)?@)";
705 }
706
707 // HOSTNAME OR IP
708 //$urlregex .= "[a-z0-9+\$_-]+(\.[a-z0-9+\$_-]+)*"; // x allowed (ex. http://localhost, http://routerlogin)
709 //$urlregex .= "[a-z0-9+\$_-]+(\.[a-z0-9+\$_-]+)+"; // x.x
710 $urlregex .= "([a-z0-9+\$_\\\:-])+(\.[a-z0-9+\$_-][a-z0-9+\$_-]+)*"; // x ou x.xx (2 x ou plus)
711 //use only one of the above
712
713 // PORT
714 if ($port) {
715 $urlregex .= "(\:[0-9]{2,5})";
716 }
717 // PATH
718 if ($path) {
719 $urlregex .= "(\/([a-z0-9+\$_-]\.?)+)*\/";
720 }
721 // GET Query
722 if ($query) {
723 $urlregex .= "(\?[a-z+&\$_.-][a-z0-9;:@\/&%=+\$_.-]*)";
724 }
725 // ANCHOR
726 if ($anchor) {
727 $urlregex .= "(#[a-z_.-][a-z0-9+\$_.-]*)$";
728 }
729
730 // check
731 if (preg_match('/'.$urlregex.'/i', $url)) {
732 $ValidUrl = 1;
733 }
734 //print $urlregex.' - '.$url.' - '.$ValidUrl;
735
736 return $ValidUrl;
737}
738
745function isValidVATID($company)
746{
747 if ($company->isInEEC()) { // Syntax check rules for EEC countries
748 /* Disabled because some companies can have an address in Irland and a vat number in France.
749 $vatprefix = $company->country_code;
750 if ($vatprefix == 'GR') $vatprefix = '(EL|GR)';
751 elseif ($vatprefix == 'MC') $vatprefix = 'FR'; // Monaco is using french VAT numbers
752 else $vatprefix = preg_quote($vatprefix, '/');*/
753 $vatprefix = '[a-zA-Z][a-zA-Z]';
754 if (!preg_match('/^'.$vatprefix.'[a-zA-Z0-9\-\.]{5,14}$/i', str_replace(' ', '', $company->tva_intra))) {
755 return 0;
756 }
757 }
758
759 return 1;
760}
761
769function clean_url($url, $http = 1)
770{
771 // Fixed by Matelli (see http://matelli.fr/showcases/patch%73-dolibarr/fix-cleaning-url.html)
772 // To include the minus sign in a char class, we must not escape it but put it at the end of the class
773 // Also, there's no need of escape a dot sign in a class
774 $regs = array();
775 if (preg_match('/^(https?:[\\/]+)?([0-9A-Z.-]+\.[A-Z]{2,4})(:[0-9]+)?/i', $url, $regs)) {
776 $proto = $regs[1];
777 $domain = $regs[2];
778 $port = isset($regs[3]) ? $regs[3] : '';
779 //print $url." -> ".$proto." - ".$domain." - ".$port;
780 //$url = dol_string_nospecial(trim($url));
781 $url = trim($url);
782
783 // Si http: defini on supprime le http (Si https on ne supprime pas)
784 $newproto = $proto;
785 if ($http == 0) {
786 if (preg_match('/^http:[\\/]+/i', $url)) {
787 $url = preg_replace('/^http:[\\/]+/i', '', $url);
788 $newproto = '';
789 }
790 }
791
792 // On passe le nom de domaine en minuscule
793 $CleanUrl = preg_replace('/^'.preg_quote($proto.$domain, '/').'/i', $newproto.strtolower($domain), $url);
794
795 return $CleanUrl;
796 } else {
797 return $url;
798 }
799}
800
801
802
814function dolObfuscateEmail($mail, $replace = "*", $nbreplace = 8, $nbdisplaymail = 4, $nbdisplaydomain = 3, $displaytld = true)
815{
816 if (!isValidEmail($mail)) {
817 return '';
818 }
819 $tab = explode('@', $mail);
820 $tab2 = explode('.', $tab[1]);
821 $string_replace = '';
822 $mail_name = $tab[0];
823 $mail_domaine = $tab2[0];
824 $mail_tld = '';
825
826 $nbofelem = count($tab2);
827 for ($i = 1; $i < $nbofelem && $displaytld; $i++) {
828 $mail_tld .= '.'.$tab2[$i];
829 }
830
831 for ($i = 0; $i < $nbreplace; $i++) {
832 $string_replace .= $replace;
833 }
834
835 if (strlen($mail_name) > $nbdisplaymail) {
836 $mail_name = substr($mail_name, 0, $nbdisplaymail);
837 }
838
839 if (strlen($mail_domaine) > $nbdisplaydomain) {
840 $mail_domaine = substr($mail_domaine, strlen($mail_domaine) - $nbdisplaydomain);
841 }
842
843 return $mail_name.$string_replace.$mail_domaine.$mail_tld;
844}
845
846
856function array2tr($data, $troptions = '', $tdoptions = '')
857{
858 $text = '<tr '.$troptions.'>';
859 foreach ($data as $key => $item) {
860 $text .= '<td '.$tdoptions.'>'.((string) $item).'</td>';
861 }
862 $text .= '</tr>';
863 return $text;
864}
865
876function array2table($data, $tableMarkup = 1, $tableoptions = '', $troptions = '', $tdoptions = '')
877{
878 $text = '';
879 if ($tableMarkup) {
880 $text = '<table '.$tableoptions.'>';
881 }
882 foreach ($data as $key => $item) {
883 if (is_array($item)) {
884 $text .= array2tr($item, $troptions, $tdoptions);
885 } else {
886 $text .= '<tr '.$troptions.'>';
887 $text .= '<td '.$tdoptions.'>'.((string) $key).'</td>';
888 $text .= '<td '.$tdoptions.'>'.((string) $item).'</td>';
889 $text .= '</tr>';
890 }
891 }
892 if ($tableMarkup) {
893 $text .= '</table>';
894 }
895 return $text;
896}
897
915function get_next_value($db, $mask, $table, $field, $where = '', $objsoc = '', $date = '', $mode = 'next', $bentityon = true, $objuser = null, $forceentity = null, $objbookkeeping = null)
916{
917 global $user;
918
919 if (!is_object($objsoc)) {
920 $valueforccc = (string) $objsoc;
921 } elseif ($table == "commande_fournisseur" || $table == "facture_fourn" || $table == "paiementfourn") {
922 $valueforccc = dol_string_unaccent($objsoc->code_fournisseur);
923 } else {
924 $valueforccc = dol_string_unaccent($objsoc->code_client);
925 }
926
927 $sharetable = $table;
928 if ($table == 'facture' || $table == 'invoice') {
929 $sharetable = 'invoicenumber'; // for getEntity function
930 }
931
932 // Clean parameters
933 if ($date == '') {
934 $date = dol_now(); // We use local year and month of PHP server to search numbers
935 }
936 // but we should use local year and month of user
937
938 // For debugging
939 //dol_syslog("mask=".$mask, LOG_DEBUG);
940 //include_once(DOL_DOCUMENT_ROOT.'/core/lib/date.lib.php');
941 //$mask='FA{yy}{mm}-{0000@99}';
942 //$date=dol_mktime(12, 0, 0, 1, 1, 1900);
943 //$date=dol_stringtotime('20130101');
944 $hasglobalcounter = false;
945 $maskrefclient_maskcounter = '';
946 $maskrefclient_clientcode = '';
947 $maskrefclient_maskclientcode = '';
948 $maskrefclient_maskoffset = '';
949
950 $reg = array();
951 // Extract value for mask counter, mask raz and mask offset
952 if (preg_match('/\{(0+)([@\+][0-9\-\+\=]+)?([@\+][0-9\-\+\=]+)?\}/i', $mask, $reg)) {
953 $masktri = $reg[1].(!empty($reg[2]) ? $reg[2] : '').(!empty($reg[3]) ? $reg[3] : '');
954 $maskcounter = $reg[1];
955 $hasglobalcounter = true;
956 } else {
957 // setting some defaults so the rest of the code won't fail if there is a third party counter
958 $masktri = '00000';
959 $maskcounter = '00000';
960 }
961
962 $maskraz = -1;
963 $maskoffset = 0;
964 $resetEveryMonth = false;
965 if (dol_strlen($maskcounter) < 3 && !getDolGlobalString('MAIN_COUNTER_WITH_LESS_3_DIGITS')) {
966 return 'ErrorCounterMustHaveMoreThan3Digits';
967 }
968
969 // Extract value for third party mask counter
970 $regClientRef = array();
971 if (preg_match('/\{(c+)(0*)\}/i', $mask, $regClientRef)) {
972 $maskrefclient = $regClientRef[1].$regClientRef[2];
973 $maskrefclient_maskclientcode = $regClientRef[1];
974 $maskrefclient_maskcounter = $regClientRef[2];
975 $maskrefclient_maskoffset = 0; //default value of maskrefclient_counter offset
976 $maskrefclient_clientcode = substr($valueforccc, 0, dol_strlen($maskrefclient_maskclientcode)); //get n first characters of client code where n is length in mask
977 $maskrefclient_clientcode = str_pad($maskrefclient_clientcode, dol_strlen($maskrefclient_maskclientcode), "#", STR_PAD_RIGHT); //padding maskrefclient_clientcode for having exactly n characters in maskrefclient_clientcode
978 $maskrefclient_clientcode = dol_string_nospecial($maskrefclient_clientcode); //sanitize maskrefclient_clientcode for sql insert and sql select like
979 if (dol_strlen($maskrefclient_maskcounter) > 0 && dol_strlen($maskrefclient_maskcounter) < 3) {
980 return 'ErrorCounterMustHaveMoreThan3Digits';
981 }
982 } else {
983 $maskrefclient = '';
984 }
985
986 // fail if there is neither a global nor a third party counter
987 if (!$hasglobalcounter && ($maskrefclient_maskcounter == '')) {
988 return 'ErrorBadMask';
989 }
990
991 // Extract value for third party type
992 $regType = array();
993 if (preg_match('/\{(t+)\}/i', $mask, $regType)) {
994 $masktype = $regType[1];
995 $masktype_value = dol_substr(preg_replace('/^TE_/', '', $objsoc->typent_code), 0, dol_strlen($regType[1])); // get n first characters of thirdparty typent_code (where n is length in mask)
996 $masktype_value = str_pad($masktype_value, dol_strlen($regType[1]), "#", STR_PAD_RIGHT); // we fill on right with # to have same number of char than into mask
997 } else {
998 $masktype = '';
999 $masktype_value = '';
1000 }
1001
1002 // Extract value for user
1003 $regUser = array();
1004 if (preg_match('/\{(u+)\}/i', $mask, $regUser)) {
1005 $lastname = 'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX';
1006 if (is_object($objuser)) {
1007 $lastname = $objuser->lastname;
1008 }
1009
1010 $maskuser = $regUser[1];
1011 $maskuser_value = substr($lastname, 0, dol_strlen($regUser[1])); // get n first characters of user firstname (where n is length in mask)
1012 $maskuser_value = str_pad($maskuser_value, dol_strlen($regUser[1]), "#", STR_PAD_RIGHT); // we fill on right with # to have same number of char than into mask
1013 } else {
1014 $maskuser = '';
1015 $maskuser_value = '';
1016 }
1017
1018 // Extract value for journal code
1019 $regJournal = array();
1020 if (preg_match('/\{(jj+)\}/i', $mask, $regJournal)) {
1021 $journalcode = 'JJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJ';
1022 if (is_object($objbookkeeping)) {
1023 $journalcode = $objbookkeeping->code_journal;
1024 }
1025
1026 $maskjournal = $regJournal[1];
1027 $maskjournal_value = substr($journalcode, 0, dol_strlen($regJournal[1])); // get n first characters of journal code (where n is length in mask)
1028 $maskjournal_value = str_pad($maskjournal_value, dol_strlen($regJournal[1]), "#", STR_PAD_RIGHT); // we fill on right with # to have same number of char than into mask
1029 } else {
1030 $maskjournal = '';
1031 $maskjournal_value = '';
1032 }
1033
1034 // Personalized field {XXX-1} à {XXX-99}
1035 $maskperso = array();
1036 $maskpersonew = array();
1037 $tmpmask = $mask;
1038 $regKey = array();
1039 while (preg_match('/\{([A-Z]+)\-([0-9]+)\}/', $tmpmask, $regKey)) {
1040 $maskperso[$regKey[1]] = '{'.$regKey[1].'-'.$regKey[2].'}';
1041 // @phan-suppress-next-line PhanParamSuspiciousOrder
1042 $maskpersonew[$regKey[1]] = str_pad('', (int) $regKey[2], '_', STR_PAD_RIGHT);
1043 $tmpmask = preg_replace('/\{'.$regKey[1].'\-'.$regKey[2].'\}/i', $maskpersonew[$regKey[1]], $tmpmask);
1044 }
1045
1046 if (strstr($mask, 'user_extra_')) {
1047 $start = "{user_extra_";
1048 $end = "\}";
1049 $extra = get_string_between($mask, "user_extra_", "}");
1050 if (!empty($user->array_options['options_'.$extra])) {
1051 $mask = preg_replace('#('.$start.')(.*?)('.$end.')#si', $user->array_options['options_'.$extra], $mask);
1052 }
1053 }
1054
1055 // Now define value for all alternative $mask variable we will need to work
1056 $maskwithonlyymcode = $mask;
1057 $maskwithonlyymcode = preg_replace('/\{(0+)([@\+][0-9\-\+\=]+)?([@\+][0-9\-\+\=]+)?\}/i', $maskcounter, $maskwithonlyymcode);
1058 $maskwithonlyymcode = preg_replace('/\{dd\}/i', 'dd', $maskwithonlyymcode);
1059 $maskwithonlyymcode = preg_replace('/\{(c+)(0*)\}/i', $maskrefclient, $maskwithonlyymcode);
1060 $maskwithonlyymcode = preg_replace('/\{(t+)\}/i', $masktype_value, $maskwithonlyymcode);
1061 $maskwithonlyymcode = preg_replace('/\{(u+)\}/i', $maskuser_value, $maskwithonlyymcode);
1062 $maskwithonlyymcode = preg_replace('/\{(j+)\}/i', $maskjournal_value, $maskwithonlyymcode);
1063 foreach ($maskperso as $key => $val) {
1064 $maskwithonlyymcode = preg_replace('/'.preg_quote($val, '/').'/i', $maskpersonew[$key], $maskwithonlyymcode);
1065 }
1066 $maskwithnocode = $maskwithonlyymcode;
1067 $maskwithnocode = preg_replace('/\{yyyy\}/i', 'yyyy', $maskwithnocode);
1068 $maskwithnocode = preg_replace('/\{yy\}/i', 'yy', $maskwithnocode);
1069 $maskwithnocode = preg_replace('/\{y\}/i', 'y', $maskwithnocode);
1070 $maskwithnocode = preg_replace('/\{mm\}/i', 'mm', $maskwithnocode);
1071 // Now maskwithnocode = 0000ddmmyyyyccc for example
1072 // and maskcounter = 0000 for example
1073 //print "maskwithonlyymcode=".$maskwithonlyymcode." maskwithnocode=".$maskwithnocode."\n<br>";
1074 //var_dump($reg);
1075
1076 // If an offset is asked ($reg is parsed from the counter mask)
1077 if (!empty($reg[2]) && preg_match('/^\+/', $reg[2])) {
1078 $maskoffset = preg_replace('/^\+/', '', $reg[2]);
1079 }
1080 if (!empty($reg[3]) && preg_match('/^\+/', $reg[3])) {
1081 $maskoffset = preg_replace('/^\+/', '', $reg[3]);
1082 }
1083
1084 // Define $sqlwhere
1085 $sqlwhere = '';
1086 $yearoffset = 0; // Use year of current $date by default
1087 $yearoffsettype = false; // false: no reset, 0,-,=,+: reset at offset month SOCIETE_FISCAL_MONTH_START, x=reset at offset month x
1088
1089 // If a restore to zero after a month is asked we check if there is already a value for this year ($reg is parsed from the counter mask)
1090 if (!empty($reg[2]) && preg_match('/^@/', $reg[2])) {
1091 $yearoffsettype = preg_replace('/^@/', '', $reg[2]);
1092 }
1093 if (!empty($reg[3]) && preg_match('/^@/', $reg[3])) {
1094 $yearoffsettype = preg_replace('/^@/', '', $reg[3]);
1095 }
1096
1097 //print "yearoffset=".$yearoffset." yearoffsettype=".$yearoffsettype;
1098 if (is_numeric($yearoffsettype) && $yearoffsettype >= 1) {
1099 $maskraz = $yearoffsettype; // For backward compatibility
1100 } elseif ($yearoffsettype === '0' || (!empty($yearoffsettype) && !is_numeric($yearoffsettype) && getDolGlobalInt('SOCIETE_FISCAL_MONTH_START') > 1)) {
1101 $maskraz = getDolGlobalString('SOCIETE_FISCAL_MONTH_START');
1102 }
1103 //print "maskraz=".$maskraz; // -1=no reset
1104
1105 $monthcomp = 0;
1106 $yearcomp = '';
1107
1108 if ($maskraz > 0) { // A reset is required
1109 if ($maskraz == 99) {
1110 $maskraz = (int) date('m', $date);
1111 $resetEveryMonth = true;
1112 }
1113 if ($maskraz > 12) {
1114 return 'ErrorBadMaskBadRazMonth';
1115 }
1116
1117 // Define posy, posm and reg
1118 if ($maskraz > 1) { // if reset is not first month, we need month and year into mask
1119 if (preg_match('/^(.*)\{(y+)\}\{(m+)\}/i', $maskwithonlyymcode, $reg)) {
1120 $posy = 2;
1121 $posm = 3;
1122 } elseif (preg_match('/^(.*)\{(m+)\}\{(y+)\}/i', $maskwithonlyymcode, $reg)) {
1123 $posy = 3;
1124 $posm = 2;
1125 } else {
1126 return 'ErrorCantUseRazInStartedYearIfNoYearMonthInMask';
1127 }
1128
1129 if (dol_strlen($reg[$posy]) < 2) {
1130 return 'ErrorCantUseRazWithYearOnOneDigit';
1131 }
1132 } else { // if reset is for a specific month in year, we need year
1133 if (preg_match('/^(.*)\{(m+)\}\{(y+)\}/i', $maskwithonlyymcode, $reg)) {
1134 $posy = 3; //index in regex
1135 $posm = 2;
1136 } elseif (preg_match('/^(.*)\{(y+)\}\{(m+)\}/i', $maskwithonlyymcode, $reg)) {
1137 $posy = 2; //index in regex
1138 $posm = 3;
1139 } elseif (preg_match('/^(.*)\{(y+)\}/i', $maskwithonlyymcode, $reg)) {
1140 $posy = 2; //index in regex
1141 $posm = 0;
1142 } else {
1143 return 'ErrorCantUseRazIfNoYearInMask';
1144 }
1145 }
1146 // Define length
1147 $yearlen = $posy ? dol_strlen($reg[$posy]) : 0;
1148 $monthlen = $posm ? dol_strlen($reg[$posm]) : 0;
1149 // Define pos
1150 $yearpos = (dol_strlen($reg[1]) + 1);
1151 $monthpos = ($yearpos + $yearlen);
1152 if ($posy == 3 && $posm == 2) { // if month is before year
1153 $monthpos = (dol_strlen($reg[1]) + 1);
1154 $yearpos = ($monthpos + $monthlen);
1155 }
1156 //print "xxx ".$maskwithonlyymcode." maskraz=".$maskraz." posy=".$posy." yearlen=".$yearlen." yearpos=".$yearpos." posm=".$posm." monthlen=".$monthlen." monthpos=".$monthpos." yearoffsettype=".$yearoffsettype." resetEveryMonth=".$resetEveryMonth."\n";
1157
1158 // Define $yearcomp and $monthcomp (that will be use in the select where to search max number)
1159 $monthcomp = $maskraz;
1160 $yearcomp = '';
1161
1162 if (!empty($yearoffsettype) && !is_numeric($yearoffsettype) && $yearoffsettype != '=') { // $yearoffsettype is - or +
1163 $currentyear = (int) date("Y", $date);
1164 $fiscaldate = dol_mktime(0, 0, 0, $maskraz, 1, $currentyear);
1165 $newyeardate = dol_mktime(0, 0, 0, 1, 1, $currentyear);
1166 $nextnewyeardate = dol_mktime(0, 0, 0, 1, 1, $currentyear + 1);
1167 //echo 'currentyear='.$currentyear.' date='.dol_print_date($date, 'day').' fiscaldate='.dol_print_date($fiscaldate, 'day').'<br>';
1168
1169 // If after or equal of current fiscal date
1170 if ($date >= $fiscaldate) {
1171 // If before of next new year date
1172 if ($date < $nextnewyeardate && $yearoffsettype == '+') {
1173 $yearoffset = 1;
1174 }
1175 } elseif ($date >= $newyeardate && $yearoffsettype == '-') {
1176 // If after or equal of current new year date
1177 $yearoffset = -1;
1178 }
1179 } elseif ((int) date("m", $date) < $maskraz && empty($resetEveryMonth)) {
1180 // For backward compatibility
1181 $yearoffset = -1;
1182 } // If current month lower that month of return to zero, year is previous year
1183
1184 if ($yearlen == 4) {
1185 $yearcomp = sprintf("%04d", idate("Y", $date) + $yearoffset);
1186 } elseif ($yearlen == 2) {
1187 $yearcomp = sprintf("%02d", idate("y", $date) + $yearoffset);
1188 } elseif ($yearlen == 1) {
1189 $yearcomp = (string) ((int) substr(date('y', $date), 1, 1) + $yearoffset);
1190 }
1191 if ($monthcomp > 1 && empty($resetEveryMonth)) { // Test with month is useless if monthcomp = 1 (0 is same as 1)
1192 if ($yearlen == 4) {
1193 $yearcomp1 = sprintf("%04d", idate("Y", $date) + $yearoffset + 1);
1194 } elseif ($yearlen == 2) {
1195 $yearcomp1 = sprintf("%02d", idate("y", $date) + $yearoffset + 1);
1196 } else {
1197 $yearcomp1 = '';
1198 }
1199
1200 $sqlwhere .= "(";
1201 $sqlwhere .= " (SUBSTRING(".$field.", ".$yearpos.", ".$yearlen.") = '".$db->escape($yearcomp)."'";
1202 $sqlwhere .= " AND SUBSTRING(".$field.", ".$monthpos.", ".$monthlen.") >= '".str_pad($monthcomp, $monthlen, '0', STR_PAD_LEFT)."')";
1203 $sqlwhere .= " OR";
1204 $sqlwhere .= " (SUBSTRING(".$field.", ".$yearpos.", ".$yearlen.") = '".$db->escape($yearcomp1)."'";
1205 $sqlwhere .= " AND SUBSTRING(".$field.", ".$monthpos.", ".$monthlen.") < '".str_pad($monthcomp, $monthlen, '0', STR_PAD_LEFT)."') ";
1206 $sqlwhere .= ')';
1207 } elseif ($resetEveryMonth) {
1208 $sqlwhere .= "(SUBSTRING(".$field.", ".$yearpos.", ".$yearlen.") = '".$db->escape($yearcomp)."'";
1209 $sqlwhere .= " AND SUBSTRING(".$field.", ".$monthpos.", ".$monthlen.") = '".str_pad($monthcomp, $monthlen, '0', STR_PAD_LEFT)."')";
1210 } else { // reset is done on january
1211 $sqlwhere .= "(SUBSTRING(".$field.", ".$yearpos.", ".$yearlen.") = '".$db->escape($yearcomp)."')";
1212 }
1213 }
1214 //print "sqlwhere=".$sqlwhere." yearcomp=".$yearcomp."<br>\n"; // sqlwhere and yearcomp defined only if we ask a reset
1215 //print "masktri=".$masktri." maskcounter=".$maskcounter." maskraz=".$maskraz." maskoffset=".$maskoffset."<br>\n";
1216
1217 // Define $sqlstring
1218 if (function_exists('mb_strrpos')) {
1219 $posnumstart = mb_strrpos($maskwithnocode, $maskcounter, 0, 'UTF-8');
1220 } else {
1221 $posnumstart = strrpos($maskwithnocode, $maskcounter);
1222 } // Pos of counter in final string (from 0 to ...)
1223 if ($posnumstart < 0) {
1224 return 'ErrorBadMaskFailedToLocatePosOfSequence';
1225 }
1226 $sqlstring = "SUBSTRING(".$field.", ".($posnumstart + 1).", ".dol_strlen($maskcounter).")";
1227
1228 // Define $maskLike
1229 $maskLike = dol_string_nospecial($mask);
1230 $maskLike = str_replace("%", "_", $maskLike);
1231
1232 // Replace protected special codes with matching number of _ as wild card character
1233 if ($resetEveryMonth && $yearcomp && $monthcomp) { // Perf optimization, when a reset is requested at each month, we can include the year and month inside the filter
1234 $maskLike = preg_replace('/\{yyyy\}/i', $yearcomp, $maskLike);
1235 $maskLike = preg_replace('/\{yy\}/i', $yearcomp, $maskLike);
1236 $maskLike = preg_replace('/\{y\}/i', $yearcomp, $maskLike);
1237 $maskLike = preg_replace('/\{mm\}/i', sprintf("%02d", $monthcomp), $maskLike);
1238 } elseif ($maskraz == 1 && $yearcomp) { // Perf optimization, when a reset is requested at first month, we can include the year inside the filter, but not the month
1239 $maskLike = preg_replace('/\{yyyy\}/i', $yearcomp, $maskLike);
1240 $maskLike = preg_replace('/\{yy\}/i', $yearcomp, $maskLike);
1241 $maskLike = preg_replace('/\{y\}/i', $yearcomp, $maskLike);
1242 $maskLike = preg_replace('/\{mm\}/i', '__', $maskLike); // we can't include the month in the filter
1243 } else {
1244 $maskLike = preg_replace('/\{yyyy\}/i', '____', $maskLike);
1245 $maskLike = preg_replace('/\{yy\}/i', '__', $maskLike);
1246 $maskLike = preg_replace('/\{y\}/i', '_', $maskLike);
1247 $maskLike = preg_replace('/\{mm\}/i', '__', $maskLike);
1248 }
1249 $maskLike = preg_replace('/\{dd\}/i', '__', $maskLike);
1250 // @phan-suppress-next-line PhanParamSuspiciousOrder
1251 $maskLike = str_replace(dol_string_nospecial('{'.$masktri.'}'), str_pad("", dol_strlen($maskcounter), "_"), $maskLike);
1252 if ($maskrefclient) {
1253 // @phan-suppress-next-line PhanParamSuspiciousOrder
1254 $maskLike = str_replace(dol_string_nospecial('{'.$maskrefclient.'}'), str_pad("", dol_strlen($maskrefclient), "_"), $maskLike);
1255 }
1256 if ($masktype) {
1257 $maskLike = str_replace(dol_string_nospecial('{'.$masktype.'}'), $masktype_value, $maskLike);
1258 }
1259 if ($maskuser) {
1260 $maskLike = str_replace(dol_string_nospecial('{'.$maskuser.'}'), $maskuser_value, $maskLike);
1261 }
1262 if ($maskjournal) {
1263 $maskLike = str_replace(dol_string_nospecial('{'.$maskjournal.'}'), $maskjournal_value, $maskLike);
1264 }
1265 foreach ($maskperso as $key => $val) {
1266 $maskLike = str_replace(dol_string_nospecial($maskperso[$key]), $maskpersonew[$key], $maskLike);
1267 }
1268
1269 // Get counter in database
1270 $counter = 0;
1271 $sql = "SELECT MAX(".$sqlstring.") as val";
1272 $sql .= " FROM ".MAIN_DB_PREFIX.$db->sanitize($table);
1273 $sql .= " WHERE ".$db->sanitize($field)." LIKE '".$db->escape($maskLike) . (getDolGlobalString('SEARCH_FOR_NEXT_VAL_ON_START_ONLY') ? "%" : "") . "'";
1274 $sql .= " AND ".$db->sanitize($field)." NOT LIKE '(PROV%)'";
1275
1276 // To ensure that all variables within the MAX() brackets are integers
1277 // This avoid bad detection of max when data are noised with non numeric values at the position of the numero
1278 if (getDolGlobalInt('MAIN_NUMBERING_FILTER_ON_INT_ONLY')) {
1279 // @phan-suppress-next-line PhanPluginSuspiciousParamPosition
1280 $sql .= " AND ". $db->regexpsql($sqlstring, '^[0-9]+$', 1);
1281 }
1282
1283 if ($bentityon) { // only if entity enable
1284 $sql .= " AND entity IN (".getEntity($sharetable).")";
1285 } elseif (!empty($forceentity)) {
1286 $sql .= " AND entity IN (".$db->sanitize($forceentity).")";
1287 }
1288 if ($where) {
1289 $sql .= $where;
1290 }
1291 if ($sqlwhere) {
1292 $sql .= " AND ".$sqlwhere;
1293 }
1294
1295 //print $sql.'<br>';
1296 dol_syslog("functions2::get_next_value mode=".$mode, LOG_DEBUG);
1297 $resql = $db->query($sql);
1298 if ($resql) {
1299 $obj = $db->fetch_object($resql);
1300 $counter = $obj->val;
1301 } else {
1302 dol_print_error($db);
1303 }
1304
1305 // Check if we must force counter to maskoffset
1306 if (empty($counter)) {
1307 $counter = $maskoffset;
1308 } elseif (preg_match('/[^0-9]/i', $counter)) {
1309 dol_syslog("Error, the last counter found is '".$counter."' so is not a numeric value. We will restart to 1.", LOG_ERR);
1310 $counter = 0;
1311 } elseif ($counter < $maskoffset && !getDolGlobalString('MAIN_NUMBERING_OFFSET_ONLY_FOR_FIRST')) {
1312 $counter = $maskoffset;
1313 }
1314
1315 if ($mode == 'last') { // We found value for counter = last counter value. Now need to get corresponding ref of invoice.
1316 $counterpadded = str_pad($counter, dol_strlen($maskcounter), "0", STR_PAD_LEFT);
1317
1318 // Define $maskLike
1319 $maskLike = dol_string_nospecial($mask);
1320 $maskLike = str_replace("%", "_", $maskLike);
1321 // Replace protected special codes with matching number of _ as wild card character
1322 $maskLike = preg_replace('/\{yyyy\}/i', '____', $maskLike);
1323 $maskLike = preg_replace('/\{yy\}/i', '__', $maskLike);
1324 $maskLike = preg_replace('/\{y\}/i', '_', $maskLike);
1325 $maskLike = preg_replace('/\{mm\}/i', '__', $maskLike);
1326 $maskLike = preg_replace('/\{dd\}/i', '__', $maskLike);
1327 $maskLike = str_replace(dol_string_nospecial('{'.$masktri.'}'), $counterpadded, $maskLike);
1328 if ($maskrefclient) {
1329 // @phan-suppress-next-line PhanParamSuspiciousOrder
1330 $maskLike = str_replace(dol_string_nospecial('{'.$maskrefclient.'}'), str_pad("", dol_strlen($maskrefclient), "_"), $maskLike);
1331 }
1332 if ($masktype) {
1333 $maskLike = str_replace(dol_string_nospecial('{'.$masktype.'}'), $masktype_value, $maskLike);
1334 }
1335 if ($maskuser) {
1336 $maskLike = str_replace(dol_string_nospecial('{'.$maskuser.'}'), $maskuser_value, $maskLike);
1337 }
1338
1339 $ref = '';
1340 $sql = "SELECT ".$db->sanitize($field)." as ref";
1341 $sql .= " FROM ".MAIN_DB_PREFIX.$db->sanitize($table);
1342 $sql .= " WHERE ".$db->sanitize($field)." LIKE '".$db->escape($maskLike) . (getDolGlobalString('SEARCH_FOR_NEXT_VAL_ON_START_ONLY') ? "%" : "") . "'";
1343 $sql .= " AND ".$db->sanitize($field)." NOT LIKE '%PROV%'";
1344 if ($bentityon) { // only if entity enable
1345 $sql .= " AND entity IN (".getEntity($sharetable).")";
1346 } elseif (!empty($forceentity)) {
1347 $sql .= " AND entity IN (".$db->sanitize($forceentity).")";
1348 }
1349 if ($where) {
1350 $sql .= $where;
1351 }
1352 if ($sqlwhere) {
1353 $sql .= " AND ".$sqlwhere;
1354 }
1355
1356 dol_syslog("functions2::get_next_value mode=".$mode, LOG_DEBUG);
1357 $resql = $db->query($sql);
1358 if ($resql) {
1359 $obj = $db->fetch_object($resql);
1360 if ($obj) {
1361 $ref = $obj->ref;
1362 }
1363 } else {
1364 dol_print_error($db);
1365 }
1366
1367 $numFinal = $ref;
1368 } elseif ($mode == 'next') {
1369 $counter++;
1370 $maskrefclient_counter = 0;
1371
1372 // If value for $counter has a length higher than $maskcounter chars
1373 if ($counter >= pow(10, dol_strlen($maskcounter))) {
1374 $counter = 'ErrorMaxNumberReachForThisMask';
1375 }
1376
1377 if (!empty($maskrefclient_maskcounter)) {
1378 //print "maskrefclient_maskcounter=".$maskrefclient_maskcounter." maskwithnocode=".$maskwithnocode." maskrefclient=".$maskrefclient."\n<br>";
1379
1380 // Define $sqlstring
1381 $maskrefclient_posnumstart = strpos($maskwithnocode, $maskrefclient_maskcounter, strpos($maskwithnocode, $maskrefclient)); // Pos of counter in final string (from 0 to ...)
1382 if ($maskrefclient_posnumstart <= 0) {
1383 return 'ErrorBadMask';
1384 }
1385 $maskrefclient_sqlstring = 'SUBSTRING('.$field.', '.($maskrefclient_posnumstart + 1).', '.dol_strlen($maskrefclient_maskcounter).')';
1386 //print "x".$sqlstring;
1387
1388 // Define $maskrefclient_maskLike
1389 $maskrefclient_maskLike = dol_string_nospecial($mask);
1390 $maskrefclient_maskLike = str_replace("%", "_", $maskrefclient_maskLike);
1391 // Replace protected special codes with matching number of _ as wild card character
1392 $maskrefclient_maskLike = str_replace(dol_string_nospecial('{yyyy}'), '____', $maskrefclient_maskLike);
1393 $maskrefclient_maskLike = str_replace(dol_string_nospecial('{yy}'), '__', $maskrefclient_maskLike);
1394 $maskrefclient_maskLike = str_replace(dol_string_nospecial('{y}'), '_', $maskrefclient_maskLike);
1395 $maskrefclient_maskLike = str_replace(dol_string_nospecial('{mm}'), '__', $maskrefclient_maskLike);
1396 $maskrefclient_maskLike = str_replace(dol_string_nospecial('{dd}'), '__', $maskrefclient_maskLike);
1397 // @phan-suppress-next-line PhanParamSuspiciousOrder
1398 $maskrefclient_maskLike = str_replace(dol_string_nospecial('{'.$masktri.'}'), str_pad("", dol_strlen($maskcounter), "_"), $maskrefclient_maskLike);
1399 // @phan-suppress-next-line PhanParamSuspiciousOrder
1400 $maskrefclient_maskLike = str_replace(dol_string_nospecial('{'.$maskrefclient.'}'), $maskrefclient_clientcode.str_pad("", dol_strlen($maskrefclient_maskcounter), "_"), $maskrefclient_maskLike);
1401
1402 // Get counter in database
1403 $maskrefclient_sql = "SELECT MAX(".$maskrefclient_sqlstring.") as val";
1404 $maskrefclient_sql .= " FROM ".MAIN_DB_PREFIX.$table;
1405 $maskrefclient_sql .= " WHERE ".$db->sanitize($field)." LIKE '".$db->escape($maskrefclient_maskLike) . (getDolGlobalString('SEARCH_FOR_NEXT_VAL_ON_START_ONLY') ? "%" : "") . "'";
1406 if ($bentityon) { // only if entity enable
1407 $maskrefclient_sql .= " AND entity IN (".getEntity($sharetable).")";
1408 } elseif (!empty($forceentity)) {
1409 $maskrefclient_sql .= " AND entity IN (".$db->sanitize($forceentity).")";
1410 }
1411 if ($where) {
1412 $maskrefclient_sql .= $where; //use the same optional where as general mask
1413 }
1414 if ($sqlwhere) {
1415 $maskrefclient_sql .= ' AND '.$sqlwhere; //use the same sqlwhere as general mask
1416 }
1417 $maskrefclient_sql .= " AND (SUBSTRING(".$field.", ".(strpos($maskwithnocode, $maskrefclient) + 1).", ".dol_strlen($maskrefclient_maskclientcode).") = '".$db->escape($maskrefclient_clientcode)."')";
1418
1419 dol_syslog("functions2::get_next_value maskrefclient", LOG_DEBUG);
1420 $maskrefclient_resql = $db->query($maskrefclient_sql);
1421 if ($maskrefclient_resql) {
1422 $maskrefclient_obj = $db->fetch_object($maskrefclient_resql);
1423 $maskrefclient_counter = $maskrefclient_obj->val;
1424 } else {
1425 dol_print_error($db);
1426 }
1427
1428 if (empty($maskrefclient_counter) || preg_match('/[^0-9]/i', $maskrefclient_counter)) {
1429 $maskrefclient_counter = $maskrefclient_maskoffset;
1430 }
1431 $maskrefclient_counter++;
1432 }
1433
1434 // Build numFinal
1435 $numFinal = $mask;
1436
1437 // We replace special codes except refclient
1438 if (!empty($yearoffsettype) && !is_numeric($yearoffsettype) && $yearoffsettype != '=') { // yearoffsettype is - or +, so we don't want current year
1439 $numFinal = preg_replace('/\{yyyy\}/i', (string) ((int) date("Y", $date) + $yearoffset), $numFinal);
1440 $numFinal = preg_replace('/\{yy\}/i', (string) ((int) date("y", $date) + $yearoffset), $numFinal);
1441 $numFinal = preg_replace('/\{y\}/i', (string) ((int) substr((string) date("y", $date), 1, 1) + $yearoffset), $numFinal);
1442 } else { // we want yyyy to be current year
1443 $numFinal = preg_replace('/\{yyyy\}/i', date("Y", $date), $numFinal);
1444 $numFinal = preg_replace('/\{yy\}/i', date("y", $date), $numFinal);
1445 $numFinal = preg_replace('/\{y\}/i', substr(date("y", $date), 1, 1), $numFinal);
1446 }
1447 $numFinal = preg_replace('/\{mm\}/i', date("m", $date), $numFinal);
1448 $numFinal = preg_replace('/\{dd\}/i', date("d", $date), $numFinal);
1449
1450 // Now we replace the counter
1451 $maskbefore = '{'.$masktri.'}';
1452 $maskafter = str_pad($counter, dol_strlen($maskcounter), "0", STR_PAD_LEFT);
1453 //print 'x'.$numFinal.' - '.$maskbefore.' - '.$maskafter.'y';exit;
1454 $numFinal = str_replace($maskbefore, $maskafter, $numFinal);
1455
1456 // Now we replace the refclient
1457 if ($maskrefclient) {
1458 //print "maskrefclient=".$maskrefclient." maskrefclient_counter=".$maskrefclient_counter." maskwithonlyymcode=".$maskwithonlyymcode." maskwithnocode=".$maskwithnocode." maskrefclient_clientcode=".$maskrefclient_clientcode." maskrefclient_maskcounter=".$maskrefclient_maskcounter."\n<br>";exit;
1459 $maskrefclient_maskbefore = '{'.$maskrefclient.'}';
1460 $maskrefclient_maskafter = $maskrefclient_clientcode;
1461 if (dol_strlen($maskrefclient_maskcounter) > 0) {
1462 $maskrefclient_maskafter .= str_pad((string) $maskrefclient_counter, dol_strlen($maskrefclient_maskcounter), "0", STR_PAD_LEFT);
1463 }
1464 $numFinal = str_replace($maskrefclient_maskbefore, (string) $maskrefclient_maskafter, $numFinal);
1465 }
1466
1467 // Now we replace the type
1468 if ($masktype) {
1469 $masktype_maskbefore = '{'.$masktype.'}';
1470 $masktype_maskafter = $masktype_value;
1471 $numFinal = str_replace($masktype_maskbefore, $masktype_maskafter, $numFinal);
1472 }
1473
1474 // Now we replace the user
1475 if ($maskuser) {
1476 $maskuser_maskbefore = '{'.$maskuser.'}';
1477 $maskuser_maskafter = $maskuser_value;
1478 $numFinal = str_replace($maskuser_maskbefore, $maskuser_maskafter, $numFinal);
1479 }
1480
1481 // Now we replace the journal code
1482 if ($maskjournal) {
1483 $maskjournal_maskbefore = '{'.$maskjournal.'}';
1484 $maskjournal_maskafter = $maskjournal_value;
1485 $numFinal = str_replace($maskjournal_maskbefore, $maskjournal_maskafter, $numFinal);
1486 }
1487 } else {
1488 $numFinal = "ErrorBadMode";
1489 dol_syslog("functions2::get_next_value ErrorBadMode '$mode'", LOG_ERR);
1490 }
1491
1492 dol_syslog("functions2::get_next_value return ".$numFinal, LOG_DEBUG);
1493 return $numFinal;
1494}
1495
1507function get_string_between($string, $start, $end)
1508{
1509 $ini = strpos($string, $start);
1510 if ($ini === false) {
1511 return '';
1512 }
1513 $ini += strlen($start);
1514 $endpos = strpos($string, $end, $ini);
1515 if ($endpos === false) {
1516 return '';
1517 }
1518 return substr($string, $ini, $endpos - $ini);
1519}
1520
1528function check_value($mask, $value)
1529{
1530 $result = 0;
1531
1532 $hasglobalcounter = false;
1533 $maskrefclient_maskcounter = '';
1534
1535 // Extract value for mask counter, mask raz and mask offset
1536 $reg = array();
1537 if (preg_match('/\{(0+)([@\+][0-9]+)?([@\+][0-9]+)?\}/i', $mask, $reg)) {
1538 $masktri = $reg[1].(isset($reg[2]) ? $reg[2] : '').(isset($reg[3]) ? $reg[3] : '');
1539 $maskcounter = $reg[1];
1540 $hasglobalcounter = true;
1541 } else {
1542 // setting some defaults so the rest of the code won't fail if there is a third party counter
1543 $masktri = '00000';
1544 $maskcounter = '00000';
1545 }
1546 $maskraz = -1;
1547 $maskoffset = 0;
1548 if (dol_strlen($maskcounter) < 3) {
1549 return 'ErrorCounterMustHaveMoreThan3Digits';
1550 }
1551
1552 // Extract value for third party mask counter
1553 $regClientRef = array();
1554 if (preg_match('/\{(c+)(0*)\}/i', $mask, $regClientRef)) {
1555 $maskrefclient = $regClientRef[1].$regClientRef[2];
1556 $maskrefclient_maskclientcode = $regClientRef[1];
1557 $maskrefclient_maskcounter = $regClientRef[2];
1558 $maskrefclient_maskoffset = 0; //default value of maskrefclient_counter offset
1559 $maskrefclient_clientcode = substr('', 0, dol_strlen($maskrefclient_maskclientcode)); //get n first characters of client code to form maskrefclient_clientcode
1560 $maskrefclient_clientcode = str_pad($maskrefclient_clientcode, dol_strlen($maskrefclient_maskclientcode), "#", STR_PAD_RIGHT); //padding maskrefclient_clientcode for having exactly n characters in maskrefclient_clientcode
1561 $maskrefclient_clientcode = dol_string_nospecial($maskrefclient_clientcode); //sanitize maskrefclient_clientcode for sql insert and sql select like
1562 if (dol_strlen($maskrefclient_maskcounter) > 0 && dol_strlen($maskrefclient_maskcounter) < 3) {
1563 return 'ErrorCounterMustHaveMoreThan3Digits';
1564 }
1565 } else {
1566 $maskrefclient = '';
1567 }
1568
1569 // fail if there is neither a global nor a third party counter
1570 if (!$hasglobalcounter && ($maskrefclient_maskcounter == '')) {
1571 return 'ErrorBadMask';
1572 }
1573
1574 $maskwithonlyymcode = $mask;
1575 $maskwithonlyymcode = preg_replace('/\{(0+)([@\+][0-9]+)?([@\+][0-9]+)?\}/i', $maskcounter, $maskwithonlyymcode);
1576 $maskwithonlyymcode = preg_replace('/\{dd\}/i', 'dd', $maskwithonlyymcode);
1577 $maskwithonlyymcode = preg_replace('/\{(c+)(0*)\}/i', $maskrefclient, $maskwithonlyymcode);
1578 $maskwithnocode = $maskwithonlyymcode;
1579 $maskwithnocode = preg_replace('/\{yyyy\}/i', 'yyyy', $maskwithnocode);
1580 $maskwithnocode = preg_replace('/\{yy\}/i', 'yy', $maskwithnocode);
1581 $maskwithnocode = preg_replace('/\{y\}/i', 'y', $maskwithnocode);
1582 $maskwithnocode = preg_replace('/\{mm\}/i', 'mm', $maskwithnocode);
1583 // Now maskwithnocode = 0000ddmmyyyyccc for example
1584 // and maskcounter = 0000 for example
1585 //print "maskwithonlyymcode=".$maskwithonlyymcode." maskwithnocode=".$maskwithnocode."\n<br>";
1586
1587 // If an offset is asked
1588 if (!empty($reg[2]) && preg_match('/^\+/', $reg[2])) {
1589 $maskoffset = preg_replace('/^\+/', '', $reg[2]);
1590 }
1591 if (!empty($reg[3]) && preg_match('/^\+/', $reg[3])) {
1592 $maskoffset = preg_replace('/^\+/', '', $reg[3]);
1593 }
1594
1595 // Define $sqlwhere
1596
1597 // If a restore to zero after a month is asked we check if there is already a value for this year.
1598 if (!empty($reg[2]) && preg_match('/^@/', $reg[2])) {
1599 $maskraz = preg_replace('/^@/', '', $reg[2]);
1600 }
1601 if (!empty($reg[3]) && preg_match('/^@/', $reg[3])) {
1602 $maskraz = preg_replace('/^@/', '', $reg[3]);
1603 }
1604 if ($maskraz >= 0) {
1605 if ($maskraz == 99) {
1606 $maskraz = (int) date('m');
1607 $resetEveryMonth = true;
1608 }
1609 if ($maskraz > 12) {
1610 return 'ErrorBadMaskBadRazMonth';
1611 }
1612
1613 // Define reg
1614 if ($maskraz > 1 && !preg_match('/^(.*)\{(y+)\}\{(m+)\}/i', $maskwithonlyymcode, $reg)) {
1615 return 'ErrorCantUseRazInStartedYearIfNoYearMonthInMask';
1616 }
1617 if ($maskraz <= 1 && !preg_match('/^(.*)\{(y+)\}/i', $maskwithonlyymcode, $reg)) {
1618 return 'ErrorCantUseRazIfNoYearInMask';
1619 }
1620 //print "x".$maskwithonlyymcode." ".$maskraz;
1621 }
1622 //print "masktri=".$masktri." maskcounter=".$maskcounter." maskwithonlyymcode=".$maskwithonlyymcode." maskwithnocode=".$maskwithnocode." maskraz=".$maskraz." maskoffset=".$maskoffset."<br>\n";
1623
1624 if (function_exists('mb_strrpos')) {
1625 $posnumstart = mb_strrpos($maskwithnocode, $maskcounter, 0, 'UTF-8');
1626 } else {
1627 $posnumstart = strrpos($maskwithnocode, $maskcounter);
1628 } // Pos of counter in final string (from 0 to ...)
1629 if ($posnumstart < 0) {
1630 return 'ErrorBadMaskFailedToLocatePosOfSequence';
1631 }
1632
1633 // Check we have a number in $value at position ($posnumstart+1).', '.dol_strlen($maskcounter)
1634 // TODO
1635
1636 // Check length
1637 $len = dol_strlen($maskwithnocode);
1638 if (dol_strlen($value) != $len) {
1639 $result = -1;
1640 }
1641
1642 dol_syslog("functions2::check_value result=".$result, LOG_DEBUG);
1643 return $result;
1644}
1645
1654function binhex($bin, $pad = false, $upper = false)
1655{
1656 $last = dol_strlen($bin) - 1;
1657 $x = 0;
1658 for ($i = 0; $i <= $last; $i++) {
1659 $x += ($bin[$last - $i] ? 1 : 0) << $i;
1660 }
1661 $x = dechex($x);
1662 if ($pad) {
1663 while (dol_strlen($x) < intval(dol_strlen($bin)) / 4) {
1664 $x = "0$x";
1665 }
1666 }
1667 if ($upper) {
1668 $x = strtoupper($x);
1669 }
1670 return $x;
1671}
1672
1679function hexbin($hexa)
1680{
1681 $bin = '';
1682 $strLength = dol_strlen($hexa);
1683 for ($i = 0; $i < $strLength; $i++) {
1684 $bin .= str_pad(decbin(hexdec($hexa[$i])), 4, '0', STR_PAD_LEFT);
1685 }
1686 return $bin;
1687}
1688
1695function numero_semaine($time)
1696{
1697 $stime = dol_print_date($time, '%Y-%m-%d');
1698
1699 if (preg_match('/^([0-9]+)\-([0-9]+)\-([0-9]+)\s?([0-9]+)?:?([0-9]+)?/i', $stime, $reg)) {
1700 // Date est au format 'YYYY-MM-DD' ou 'YYYY-MM-DD HH:MM:SS'
1701 $annee = (int) $reg[1];
1702 $mois = (int) $reg[2];
1703 $jour = (int) $reg[3];
1704 } else {
1705 $annee = 0;
1706 $mois = 0;
1707 $jour = 0;
1708 }
1709
1710 /*
1711 * Norme ISO-8601:
1712 * - Week 1 of the year contains Jan 4th, or contains the first Thursday of January.
1713 * - Most years have 52 weeks, but 53 weeks for years starting on a Thursday and bisectile years that start on a Wednesday.
1714 * - The first day of a week is Monday
1715 */
1716
1717 // Definition du Jeudi de la semaine
1718 if ((int) date("w", mktime(12, 0, 0, $mois, $jour, $annee)) == 0) { // Dimanche
1719 $jeudiSemaine = mktime(12, 0, 0, $mois, $jour, $annee) - 3 * 24 * 60 * 60;
1720 } elseif (date("w", mktime(12, 0, 0, $mois, $jour, $annee)) < 4) { // du Lundi au Mercredi
1721 $jeudiSemaine = mktime(12, 0, 0, $mois, $jour, $annee) + (4 - (int) date("w", mktime(12, 0, 0, $mois, $jour, $annee))) * 24 * 60 * 60;
1722 } elseif ((int) date("w", mktime(12, 0, 0, $mois, $jour, $annee)) > 4) { // du Vendredi au Samedi
1723 $jeudiSemaine = mktime(12, 0, 0, $mois, $jour, $annee) - ((int) date("w", mktime(12, 0, 0, $mois, $jour, $annee)) - 4) * 24 * 60 * 60;
1724 } else { // Jeudi
1725 $jeudiSemaine = mktime(12, 0, 0, $mois, $jour, $annee);
1726 }
1727
1728 // Definition du premier Jeudi de l'annee
1729 if ((int) date("w", mktime(12, 0, 0, 1, 1, (int) date("Y", $jeudiSemaine))) == 0) { // Dimanche
1730 $premierJeudiAnnee = mktime(12, 0, 0, 1, 1, (int) date("Y", $jeudiSemaine)) + 4 * 24 * 60 * 60;
1731 } elseif ((int) date("w", mktime(12, 0, 0, 1, 1, (int) date("Y", $jeudiSemaine))) < 4) { // du Lundi au Mercredi
1732 $premierJeudiAnnee = mktime(12, 0, 0, 1, 1, (int) date("Y", $jeudiSemaine)) + (4 - (int) date("w", mktime(12, 0, 0, 1, 1, (int) date("Y", $jeudiSemaine)))) * 24 * 60 * 60;
1733 } elseif ((int) date("w", mktime(12, 0, 0, 1, 1, (int) date("Y", $jeudiSemaine))) > 4) { // du Vendredi au Samedi
1734 $premierJeudiAnnee = mktime(12, 0, 0, 1, 1, (int) date("Y", $jeudiSemaine)) + (7 - ((int) date("w", mktime(12, 0, 0, 1, 1, (int) date("Y", $jeudiSemaine))) - 4)) * 24 * 60 * 60;
1735 } else { // Jeudi
1736 $premierJeudiAnnee = mktime(12, 0, 0, 1, 1, (int) date("Y", $jeudiSemaine));
1737 }
1738
1739 // Definition du numero de semaine: nb de jours entre "premier Jeudi de l'annee" et "Jeudi de la semaine";
1740 $numeroSemaine = (
1741 (
1742 (int) date("z", mktime(12, 0, 0, (int) date("m", $jeudiSemaine), (int) date("d", $jeudiSemaine), (int) date("Y", $jeudiSemaine)))
1743 -
1744 (int) date("z", mktime(12, 0, 0, (int) date("m", $premierJeudiAnnee), (int) date("d", $premierJeudiAnnee), (int) date("Y", $premierJeudiAnnee)))
1745 ) / 7
1746 ) + 1;
1747
1748 // Cas particulier de la semaine 53
1749 if ($numeroSemaine == 53) {
1750 // Les annees qui commencent un Jeudi et les annees bissextiles commencant un Mercredi en possedent 53
1751 if (
1752 ((int) date("w", mktime(12, 0, 0, 1, 1, (int) date("Y", $jeudiSemaine))) == 4)
1753 || (
1754 ((int) date("w", mktime(12, 0, 0, 1, 1, (int) date("Y", $jeudiSemaine))) == 3)
1755 && ((int) date("z", mktime(12, 0, 0, 12, 31, (int) date("Y", $jeudiSemaine))) == 365)
1756 )
1757 ) {
1758 $numeroSemaine = 53;
1759 } else {
1760 $numeroSemaine = 1;
1761 }
1762 }
1763
1764 //echo $jour."-".$mois."-".$annee." (".date("d-m-Y",$premierJeudiAnnee)." - ".date("d-m-Y",$jeudiSemaine).") -> ".$numeroSemaine."<BR>";
1765
1766 return sprintf("%02d", $numeroSemaine);
1767}
1768
1777function weight_convert($weight, &$from_unit, $to_unit)
1778{
1779 /* Pour convertire 320 gr en Kg appeler
1780 * $f = -3
1781 * weigh_convert(320, $f, 0) retournera 0.32
1782 *
1783 */
1784 $weight = is_numeric($weight) ? $weight : 0;
1785 while ($from_unit != $to_unit) {
1786 if ($from_unit > $to_unit) {
1787 $weight *= 10;
1788 $from_unit -= 1;
1789 $weight = weight_convert($weight, $from_unit, $to_unit);
1790 }
1791 if ($from_unit < $to_unit) {
1792 $weight /= 10;
1793 $from_unit += 1;
1794 $weight = weight_convert($weight, $from_unit, $to_unit);
1795 }
1796 }
1797
1798 return $weight;
1799}
1800
1812function dol_set_user_param($db, $conf, &$user, $tab)
1813{
1814 // Verification parameters
1815 if (count($tab) < 1) {
1816 return -1;
1817 }
1818
1819 $db->begin();
1820
1821 // We remove old parameters for all keys in $tab
1822 $sql = "DELETE FROM ".MAIN_DB_PREFIX."user_param";
1823 $sql .= " WHERE fk_user = ".((int) $user->id);
1824 $sql .= " AND entity = ".((int) $conf->entity);
1825 $sql .= " AND param in (";
1826 $i = 0;
1827 foreach ($tab as $key => $value) {
1828 if ($i > 0) {
1829 $sql .= ',';
1830 }
1831 $sql .= "'".$db->escape($key)."'";
1832 $i++;
1833 }
1834 $sql .= ")";
1835 dol_syslog("functions2.lib::dol_set_user_param", LOG_DEBUG);
1836
1837 $resql = $db->query($sql);
1838 if (!$resql) {
1839 dol_print_error($db);
1840 $db->rollback();
1841 return -1;
1842 }
1843
1844 foreach ($tab as $key => $value) {
1845 // Set new parameters
1846 $forcevalue = 0;
1847 if (is_array($value)) {
1848 if ($value["forcevalue"] == 1) {
1849 $forcevalue = 1;
1850 }
1851 $value = $value["value"];
1852 }
1853 if ($forcevalue == 1 || $value) {
1854 $sql = "INSERT INTO ".MAIN_DB_PREFIX."user_param(fk_user,entity,param,value)";
1855 $sql .= " VALUES (".((int) $user->id).",".((int) $conf->entity).",";
1856 $sql .= " '".$db->escape($key)."','".$db->escape($value)."')";
1857
1858 dol_syslog("functions2.lib::dol_set_user_param", LOG_DEBUG);
1859 $result = $db->query($sql);
1860 if (!$result) {
1861 dol_print_error($db);
1862 $db->rollback();
1863 return -1;
1864 }
1865 $user->conf->$key = $value;
1866 //print "key=".$key." user->conf->key=".$user->conf->$key;
1867 } else {
1868 unset($user->conf->$key);
1869 }
1870 }
1871
1872 $db->commit();
1873 return 1;
1874}
1875
1883function dol_print_reduction($reduction, $langs)
1884{
1885 $string = '';
1886 if ($reduction == 100) {
1887 $string = $langs->transnoentities("Offered");
1888 } else {
1889 $string = vatrate((string) $reduction, true);
1890 }
1891
1892 return $string;
1893}
1894
1902function version_os($option = '')
1903{
1904 if ($option == 'smr') {
1905 $osversion = php_uname('s').' '.php_uname('m').' '.php_uname('r');
1906 } else {
1907 $osversion = php_uname();
1908 }
1909 return $osversion;
1910}
1911
1918function version_php()
1919{
1920 return phpversion();
1921}
1922
1928function version_db()
1929{
1930 global $db;
1931 if (is_object($db) && method_exists($db, 'getVersion')) {
1932 return $db->getVersion();
1933 }
1934 return '';
1935}
1936
1944{
1945 return DOL_VERSION;
1946}
1947
1954{
1955 return $_SERVER["SERVER_SOFTWARE"];
1956}
1957
1967function getListOfModels($db, $type, $maxfilenamelength = 0, $showempty = 0)
1968{
1969 global $conf, $langs;
1970
1971 $liste = array();
1972 $found = 0;
1973 $dirtoscan = '';
1974
1975 $sql = "SELECT nom as id, nom as doc_template_name, libelle as label, description as description";
1976 $sql .= " FROM ".MAIN_DB_PREFIX."document_model";
1977 $sql .= " WHERE type = '".$db->escape($type)."'";
1978 $sql .= " AND entity IN (0,".((int) $conf->entity).")";
1979 $sql .= " ORDER BY description DESC";
1980
1981 dol_syslog('/core/lib/function2.lib.php::getListOfModels', LOG_DEBUG);
1982
1983 $resql_models = $db->query($sql);
1984 if ($resql_models) {
1985 $num = $db->num_rows($resql_models);
1986
1987 if ($showempty) {
1988 $liste[0] = '&nbsp;';
1989 }
1990
1991 $i = 0;
1992 while ($i < $num) {
1993 $found = 1;
1994
1995 $obj = $db->fetch_object($resql_models);
1996
1997 if ($obj->id == '0') { // We discard bad record (should not happen)
1998 $i++;
1999 continue;
2000 }
2001
2002 // If this generation module needs to scan a directory, then description field is filled
2003 // with the constant that contains list of directories to scan (COMPANY_ADDON_PDF_ODT_PATH, ...).
2004 if (!empty($obj->description)) { // A list of directories to scan is defined
2005 include_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
2006
2007 $const = $obj->description;
2008 $dirtoscan = preg_replace('/[\r\n]+/', ',', trim(getDolGlobalString($const)));
2009
2010 $listoffiles = array();
2011
2012 // Now we add models found in directories scanned
2013 $listofdir = explode(',', $dirtoscan);
2014 foreach ($listofdir as $key => $tmpdir) {
2015 $tmpdir = trim($tmpdir);
2016 $tmpdir = preg_replace('/DOL_DATA_ROOT/', DOL_DATA_ROOT, $tmpdir);
2017 if (!$tmpdir) {
2018 unset($listofdir[$key]);
2019 continue;
2020 }
2021 if (is_dir($tmpdir)) {
2022 // all type of template is allowed
2023 $tmpfiles = dol_dir_list($tmpdir, 'files', 0, '', array(), 'name', SORT_ASC, 0);
2024 if (count($tmpfiles)) {
2025 $listoffiles = array_merge($listoffiles, $tmpfiles);
2026 }
2027 }
2028 }
2029
2030 if (count($listoffiles)) {
2031 foreach ($listoffiles as $record) {
2032 $max = ($maxfilenamelength ? $maxfilenamelength : 28);
2033 $liste[$obj->id.':'.$record['fullname']] = dol_trunc($record['name'], $max, 'middle');
2034 }
2035 } else {
2036 $liste[0] = $obj->label.': '.$langs->trans("None");
2037 }
2038 } else {
2039 if ($type == 'member' && $obj->doc_template_name == 'standard_member') { // Special case, if member template, we add variant per format
2040 global $_Avery_Labels;
2041 include_once DOL_DOCUMENT_ROOT.'/core/lib/format_cards.lib.php';
2042 foreach ($_Avery_Labels as $key => $val) {
2043 $liste[$obj->id.':'.$key] = ($obj->label ? $obj->label : $obj->doc_template_name).' '.$val['name'];
2044 }
2045 } else {
2046 // Common usage
2047 $liste[$obj->id] = $obj->label ? $obj->label : $obj->doc_template_name;
2048 }
2049 }
2050
2051 $i++;
2052 }
2053 } else {
2054 dol_print_error($db);
2055 return -1;
2056 }
2057
2058 if ($found) {
2059 return $liste;
2060 } else {
2061 return 0;
2062 }
2063}
2064
2072function is_ip($ip)
2073{
2074 // First we test if it is a valid IPv4
2075 if (filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4)) {
2076 // Then we test if it is a private range
2077 if (!filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_NO_PRIV_RANGE)) {
2078 return 2;
2079 }
2080
2081 // Then we test if it is a reserved range
2082 if (!filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_NO_RES_RANGE)) {
2083 return 0;
2084 }
2085
2086 return 1;
2087 }
2088
2089 return 0;
2090}
2091
2099function dol_buildlogin($lastname, $firstname)
2100{
2101 //$conf->global->MAIN_BUILD_LOGIN_RULE = 'f.lastname';
2102 $charforseparator = getDolGlobalString("MAIN_USER_SEPARATOR_CHAR_FOR_GENERATED_LOGIN", '.');
2103 if ($charforseparator == 'none') {
2104 $charforseparator = '';
2105 }
2106
2107 if (getDolGlobalString('MAIN_BUILD_LOGIN_RULE') == 'f.lastname') { // f.lastname
2108 $login = strtolower(dol_string_unaccent(dol_trunc($firstname, 1, 'right', 'UTF-8', 1)));
2109 $login .= ($login ? $charforseparator : '');
2110 $login .= strtolower(dol_string_unaccent($lastname));
2111 $login = dol_string_nospecial($login, ''); // For special names
2112 } else { // firstname.lastname
2113 $login = strtolower(dol_string_unaccent($firstname));
2114 $login .= ($login ? $charforseparator : '');
2115 $login .= strtolower(dol_string_unaccent($lastname));
2116 $login = dol_string_nospecial($login, ''); // For special names
2117 }
2118
2119 // TODO Add a hook to allow external modules to suggest new rules
2120
2121 return $login;
2122}
2123
2130{
2131 global $conf;
2132
2133 $params = array();
2134 $proxyuse = getDolGlobalString('MAIN_PROXY_USE');
2135 $proxyhost = (!$proxyuse ? false : $conf->global->MAIN_PROXY_HOST);
2136 $proxyport = (!$proxyuse ? false : $conf->global->MAIN_PROXY_PORT);
2137 $proxyuser = (!$proxyuse ? false : $conf->global->MAIN_PROXY_USER);
2138 $proxypass = (!$proxyuse ? false : $conf->global->MAIN_PROXY_PASS);
2139 $timeout = getDolGlobalInt('MAIN_USE_CONNECT_TIMEOUT', 10); // Connection timeout
2140 $response_timeout = getDolGlobalInt('MAIN_USE_RESPONSE_TIMEOUT', 30); // Response timeout
2141 //print extension_loaded('soap');
2142 if ($proxyuse) {
2143 $params = array('connection_timeout' => $timeout,
2144 'response_timeout' => $response_timeout,
2145 'proxy_use' => 1,
2146 'proxy_host' => $proxyhost,
2147 'proxy_port' => $proxyport,
2148 'proxy_login' => $proxyuser,
2149 'proxy_password' => $proxypass,
2150 'trace' => 1
2151 );
2152 } else {
2153 $params = array('connection_timeout' => $timeout,
2154 'response_timeout' => $response_timeout,
2155 'proxy_use' => 0,
2156 'proxy_host' => false,
2157 'proxy_port' => false,
2158 'proxy_login' => false,
2159 'proxy_password' => false,
2160 'trace' => 1
2161 );
2162 }
2163 return $params;
2164}
2165
2166
2176function dolGetElementUrl($objectid, $objecttype, $withpicto = 0, $option = '')
2177{
2178 global $db, $langs;
2179
2180 $ret = '';
2181 $regs = array();
2182
2183 // If we ask a resource form external module (instead of default path)
2184 if (preg_match('/^([^@]+)@([^@]+)$/i', $objecttype, $regs)) {
2185 $myobject = $regs[1];
2186 $module = $regs[2];
2187 } else {
2188 // Parse $objecttype (ex: project_task)
2189 $module = $myobject = $objecttype;
2190 if (preg_match('/^([^_]+)_([^_]+)/i', $objecttype, $regs)) {
2191 $module = $regs[1];
2192 $myobject = $regs[2];
2193 }
2194 }
2195
2196 // Generic case for $classpath
2197 $classpath = $module.'/class';
2198
2199 // Special cases, to work with non standard path
2200 if ($objecttype == 'facture' || $objecttype == 'invoice') {
2201 $langs->load('bills');
2202 $classpath = 'compta/facture/class';
2203 $module = 'facture';
2204 $myobject = 'facture';
2205 } elseif ($objecttype == 'commande' || $objecttype == 'order') {
2206 $langs->load('orders');
2207 $classpath = 'commande/class';
2208 $module = 'commande';
2209 $myobject = 'commande';
2210 } elseif ($objecttype == 'propal') {
2211 $langs->load('propal');
2212 $classpath = 'comm/propal/class';
2213 } elseif ($objecttype == 'supplier_proposal') {
2214 $langs->load('supplier_proposal');
2215 $classpath = 'supplier_proposal/class';
2216 } elseif ($objecttype == 'shipping') {
2217 $langs->load('sendings');
2218 $classpath = 'expedition/class';
2219 $myobject = 'expedition';
2220 $module = 'expedition_bon';
2221 } elseif ($objecttype == 'delivery') {
2222 $langs->load('deliveries');
2223 $classpath = 'delivery/class';
2224 $myobject = 'delivery';
2225 $module = 'delivery_note';
2226 } elseif ($objecttype == 'contract') {
2227 $langs->load('contracts');
2228 $classpath = 'contrat/class';
2229 $module = 'contrat';
2230 $myobject = 'contrat';
2231 } elseif ($objecttype == 'member') {
2232 $langs->load('members');
2233 $classpath = 'adherents/class';
2234 $module = 'adherent';
2235 $myobject = 'adherent';
2236 } elseif ($objecttype == 'cabinetmed_cons') {
2237 $classpath = 'cabinetmed/class';
2238 $module = 'cabinetmed';
2239 $myobject = 'cabinetmedcons';
2240 } elseif ($objecttype == 'fichinter') {
2241 $langs->load('interventions');
2242 $classpath = 'fichinter/class';
2243 $module = 'ficheinter';
2244 $myobject = 'fichinter';
2245 } elseif ($objecttype == 'project') {
2246 $langs->load('projects');
2247 $classpath = 'projet/class';
2248 $module = 'projet';
2249 } elseif ($objecttype == 'task') {
2250 $langs->load('projects');
2251 $classpath = 'projet/class';
2252 $module = 'projet';
2253 $myobject = 'task';
2254 } elseif ($objecttype == 'stock') {
2255 $classpath = 'product/stock/class';
2256 $module = 'stock';
2257 $myobject = 'stock';
2258 } elseif ($objecttype == 'inventory') {
2259 $classpath = 'product/inventory/class';
2260 $module = 'stock';
2261 $myobject = 'inventory';
2262 } elseif ($objecttype == 'mo') {
2263 $classpath = 'mrp/class';
2264 $module = 'mrp';
2265 $myobject = 'mo';
2266 } elseif ($objecttype == 'productlot') {
2267 $classpath = 'product/stock/class';
2268 $module = 'stock';
2269 $myobject = 'productlot';
2270 }
2271
2272 // Generic case for $classfile and $classname
2273 $classfile = strtolower($myobject);
2274 $classname = ucfirst($myobject);
2275 //print "objecttype=".$objecttype." module=".$module." subelement=".$subelement." classfile=".$classfile." classname=".$classname." classpath=".$classpath;
2276
2277 if ($objecttype == 'invoice_supplier') {
2278 $classfile = 'fournisseur.facture';
2279 $classname = 'FactureFournisseur';
2280 $classpath = 'fourn/class';
2281 $module = 'fournisseur';
2282 } elseif ($objecttype == 'order_supplier') {
2283 $classfile = 'fournisseur.commande';
2284 $classname = 'CommandeFournisseur';
2285 $classpath = 'fourn/class';
2286 $module = 'fournisseur';
2287 } elseif ($objecttype == 'supplier_proposal') {
2288 $classfile = 'supplier_proposal';
2289 $classname = 'SupplierProposal';
2290 $classpath = 'supplier_proposal/class';
2291 $module = 'supplier_proposal';
2292 } elseif ($objecttype == 'stock') {
2293 $classpath = 'product/stock/class';
2294 $classfile = 'entrepot';
2295 $classname = 'Entrepot';
2296 } elseif ($objecttype == 'facturerec') {
2297 $classpath = 'compta/facture/class';
2298 $classfile = 'facture-rec';
2299 $classname = 'FactureRec';
2300 $module = 'facture';
2301 } elseif ($objecttype == 'mailing') {
2302 $classpath = 'comm/mailing/class';
2303 $classfile = 'mailing';
2304 $classname = 'Mailing';
2305 }
2306
2307 if (isModEnabled($module)) {
2308 $res = dol_include_once('/'.$classpath.'/'.$classfile.'.class.php');
2309 if ($res) {
2310 if (class_exists($classname)) {
2311 $object = new $classname($db);
2312 $res = $object->fetch($objectid);
2313 if ($res > 0) {
2314 $ret = $object->getNomUrl($withpicto, $option);
2315 } elseif ($res == 0) {
2316 $ret = $langs->trans('Deleted');
2317 }
2318 unset($object);
2319 } else {
2320 dol_syslog("Class with classname ".$classname." is unknown even after the include", LOG_ERR);
2321 }
2322 }
2323 }
2324 return $ret;
2325}
2326
2327
2336function cleanCorruptedTree($db, $tabletocleantree, $fieldfkparent)
2337{
2338 $totalnb = 0;
2339 $listofid = array();
2340 $listofparentid = array();
2341
2342 // Get list of all id in array listofid and all parents in array listofparentid
2343 $sql = "SELECT rowid, ".$fieldfkparent." as parent_id FROM ".MAIN_DB_PREFIX.$tabletocleantree;
2344 $resql = $db->query($sql);
2345 if ($resql) {
2346 $num = $db->num_rows($resql);
2347 $i = 0;
2348 while ($i < $num) {
2349 $obj = $db->fetch_object($resql);
2350 $listofid[] = $obj->rowid;
2351 if ($obj->parent_id > 0) {
2352 $listofparentid[$obj->rowid] = $obj->parent_id;
2353 }
2354 $i++;
2355 }
2356 } else {
2357 dol_print_error($db);
2358 }
2359
2360 if (count($listofid)) {
2361 print 'Code requested to clean tree (may be to solve data corruption), so we check/clean orphelins and loops.'."<br>\n";
2362
2363 // Check loops on each other
2364 $sql = "UPDATE ".MAIN_DB_PREFIX.$tabletocleantree." SET ".$fieldfkparent." = 0 WHERE ".$fieldfkparent." = rowid"; // So we update only records linked to themself
2365 $resql = $db->query($sql);
2366 if ($resql) {
2367 $nb = $db->affected_rows($resql);
2368 if ($nb > 0) {
2369 print '<br>Some record that were parent of themself were cleaned.';
2370 }
2371
2372 $totalnb += $nb;
2373 }
2374 //else dol_print_error($db);
2375
2376 // Check other loops
2377 $listofidtoclean = array();
2378 foreach ($listofparentid as $id => $pid) {
2379 // Check depth
2380 //print 'Analyse record id='.$id.' with parent '.$pid.'<br>';
2381
2382 $cursor = $id;
2383 $arrayidparsed = array(); // We start from child $id
2384 while ($cursor > 0) {
2385 $arrayidparsed[$cursor] = 1;
2386 if ($arrayidparsed[$listofparentid[$cursor]]) { // We detect a loop. A record with a parent that was already into child
2387 print 'Found a loop between id '.$id.' - '.$cursor.'<br>';
2388 unset($arrayidparsed);
2389 $listofidtoclean[$cursor] = $id;
2390 break;
2391 }
2392 // @phpstan-ignore-next-line PHPStan thinks this line is never reached
2393 $cursor = $listofparentid[$cursor];
2394 }
2395
2396 if (count($listofidtoclean)) {
2397 break;
2398 }
2399 }
2400
2401 $sql = "UPDATE ".MAIN_DB_PREFIX.$db->sanitize($tabletocleantree);
2402 $sql .= " SET ".$db->sanitize($fieldfkparent)." = 0";
2403 $sql .= " WHERE rowid IN (".$db->sanitize(implode(',', $listofidtoclean)).")"; // So we update only records detected wrong
2404 $resql = $db->query($sql);
2405 if ($resql) {
2406 $nb = $db->affected_rows($resql);
2407 if ($nb > 0) {
2408 // Removed orphelins records
2409 print '<br>Some records were detected to have parent that is a child, we set them as root record for id: ';
2410 print implode(',', $listofidtoclean);
2411 }
2412
2413 $totalnb += $nb;
2414 }
2415 //else dol_print_error($db);
2416
2417 // Check and clean orphelins
2418 $sql = "UPDATE ".MAIN_DB_PREFIX.$db->sanitize($tabletocleantree);
2419 $sql .= " SET ".$db->sanitize($fieldfkparent)." = 0";
2420 $sql .= " WHERE ".$db->sanitize($fieldfkparent)." NOT IN (".$db->sanitize(implode(',', $listofid), 1).")"; // So we update only records linked to a non existing parent
2421 $resql = $db->query($sql);
2422 if ($resql) {
2423 $nb = $db->affected_rows($resql);
2424 if ($nb > 0) {
2425 // Removed orphelins records
2426 print '<br>Some orphelins were found and modified to be parent so records are visible again for id: ';
2427 print implode(',', $listofid);
2428 }
2429
2430 $totalnb += $nb;
2431 }
2432 //else dol_print_error($db);
2433
2434 print '<br>We fixed '.$totalnb.' record(s). Some records may still be corrupted. New check may be required.';
2435 return $totalnb;
2436 }
2437 return -1;
2438}
2439
2440
2450function colorArrayToHex($arraycolor, $colorifnotfound = '888888')
2451{
2452 if (!is_array($arraycolor)) {
2453 return $colorifnotfound;
2454 }
2455 if (empty($arraycolor)) {
2456 return $colorifnotfound;
2457 }
2458 return sprintf("%02s", dechex($arraycolor[0])).sprintf("%02s", dechex($arraycolor[1])).sprintf("%02s", dechex($arraycolor[2]));
2459}
2460
2471function colorStringToArray($stringcolor, $colorifnotfound = array(88, 88, 88))
2472{
2473 if (is_array($stringcolor)) {
2474 return $stringcolor; // If already in the correct output format, we return as is
2475 }
2476 $reg = array();
2477 $tmp = preg_match('/^#?([0-9a-fA-F][0-9a-fA-F])([0-9a-fA-F][0-9a-fA-F])([0-9a-fA-F][0-9a-fA-F])$/', $stringcolor, $reg);
2478 if (!$tmp) {
2479 $tmp = array_map('intval', explode(',', $stringcolor));
2480 '@phan-var-force int[] $tmp';
2481 if (count($tmp) < 3) {
2482 return $colorifnotfound;
2483 }
2484 return $tmp;
2485 }
2486 return array(hexdec($reg[1]), hexdec($reg[2]), hexdec($reg[3]));
2487}
2488
2494function colorValidateHex($color, $allow_white = true)
2495{
2496 if (!$allow_white && ($color === '#fff' || $color === '#ffffff')) {
2497 return false;
2498 }
2499
2500 if (preg_match('/^#[a-f0-9]{6}$/i', $color)) { //hex color is valid
2501 return true;
2502 }
2503 return false;
2504}
2505
2515function colorAgressiveness($hex, $ratio = -50, $brightness = 0)
2516{
2517 if (empty($ratio)) {
2518 $ratio = 0; // To avoid null
2519 }
2520
2521 // Steps should be between -255 and 255. Negative = darker, positive = lighter
2522 $ratio = max(-100, min(100, $ratio));
2523
2524 // Normalize into a six character long hex string
2525 $hex = str_replace('#', '', $hex);
2526 if (strlen($hex) == 3) {
2527 $hex = str_repeat(substr($hex, 0, 1), 2).str_repeat(substr($hex, 1, 1), 2).str_repeat(substr($hex, 2, 1), 2);
2528 }
2529
2530 // Split into three parts: R, G and B
2531 $color_parts = str_split($hex, 2);
2532 $return = '#';
2533
2534 foreach ($color_parts as $color) {
2535 $color = hexdec($color); // Convert to decimal
2536 if ($ratio > 0) { // We increase aggressivity
2537 if ($color > 127) {
2538 $color += ((255 - $color) * ($ratio / 100));
2539 }
2540 if ($color < 128) {
2541 $color -= ($color * ($ratio / 100));
2542 }
2543 } else { // We decrease aggressiveness
2544 if ($color > 128) {
2545 $color -= (($color - 128) * (abs($ratio) / 100));
2546 }
2547 if ($color < 127) {
2548 $color += ((128 - $color) * (abs($ratio) / 100));
2549 }
2550 }
2551 if ($brightness > 0) {
2552 $color = (int) ($color * (100 + abs($brightness)) / 100);
2553 } else {
2554 $color = (int) ($color * (100 - abs($brightness)) / 100);
2555 }
2556
2557 $color = max(0, min(255, (int) $color)); // Adjust color to stay into valid range
2558 $return .= str_pad(dechex($color), 2, '0', STR_PAD_LEFT); // Make two char hex code
2559 }
2560
2561 //var_dump($hex.' '.$ratio.' -> '.$return);
2562 return $return;
2563}
2564
2571function colorAdjustBrightness($hex, $steps)
2572{
2573 // Steps should be between -255 and 255. Negative = darker, positive = lighter
2574 $steps = max(-255, min(255, $steps));
2575
2576 // Normalize into a six character long hex string
2577 $hex = str_replace('#', '', $hex);
2578 if (strlen($hex) == 3) {
2579 $hex = str_repeat(substr($hex, 0, 1), 2).str_repeat(substr($hex, 1, 1), 2).str_repeat(substr($hex, 2, 1), 2);
2580 }
2581
2582 // Split into three parts: R, G and B
2583 $color_parts = str_split($hex, 2);
2584 $return = '#';
2585
2586 foreach ($color_parts as $color) {
2587 $color = hexdec($color); // Convert to decimal
2588 $color = max(0, min(255, $color + $steps)); // Adjust color
2589 $return .= str_pad(dechex($color), 2, '0', STR_PAD_LEFT); // Make two char hex code
2590 }
2591
2592 return $return;
2593}
2594
2600function colorDarker($hex, $percent)
2601{
2602 $steps = intval(255 * $percent / 100) * -1;
2603 return colorAdjustBrightness($hex, $steps);
2604}
2605
2611function colorLighten($hex, $percent)
2612{
2613 $steps = intval(255 * $percent / 100);
2614 return colorAdjustBrightness($hex, $steps);
2615}
2616
2617
2624function colorHexToRgb($hex, $alpha = false, $returnArray = false)
2625{
2626 $string = '';
2627 $hex = str_replace('#', '', $hex);
2628 $length = strlen($hex);
2629 $rgb = array();
2630 $rgb['r'] = hexdec($length == 6 ? substr($hex, 0, 2) : ($length == 3 ? str_repeat(substr($hex, 0, 1), 2) : 0));
2631 $rgb['g'] = hexdec($length == 6 ? substr($hex, 2, 2) : ($length == 3 ? str_repeat(substr($hex, 1, 1), 2) : 0));
2632 $rgb['b'] = hexdec($length == 6 ? substr($hex, 4, 2) : ($length == 3 ? str_repeat(substr($hex, 2, 1), 2) : 0));
2633 if ($alpha !== false) {
2634 $rgb['a'] = (float) $alpha;
2635 $string = 'rgba('.implode(',', array_map('strval', $rgb)).')';
2636 } else {
2637 $string = 'rgb('.implode(',', array_map('strval', $rgb)).')';
2638 }
2639
2640 if ($returnArray) {
2641 return $rgb;
2642 } else {
2643 return $string;
2644 }
2645}
2646
2655function colorHexToHsl($hex, $alpha = false, $returnArray = false)
2656{
2657 $hex = colorArrayToHex(colorStringToArray($hex));
2658 $hex = str_replace('#', '', $hex);
2659 $red = hexdec(substr($hex, 0, 2)) / 255;
2660 $green = hexdec(substr($hex, 2, 2)) / 255;
2661 $blue = hexdec(substr($hex, 4, 2)) / 255;
2662
2663 $cmin = min($red, $green, $blue);
2664 $cmax = max($red, $green, $blue);
2665 $delta = $cmax - $cmin;
2666
2667 if ($delta == 0) {
2668 $hue = 0;
2669 } elseif ($cmax === $red) {
2670 $hue = (($green - $blue) / $delta);
2671 } elseif ($cmax === $green) {
2672 $hue = ($blue - $red) / $delta + 2;
2673 } else {
2674 $hue = ($red - $green) / $delta + 4;
2675 }
2676
2677 $hue = round($hue * 60);
2678 if ($hue < 0) {
2679 $hue += 360;
2680 }
2681
2682 $lightness = (($cmax + $cmin) / 2);
2683 $saturation = $delta === 0 ? 0 : ($delta / (1 - abs(2 * $lightness - 1)));
2684 if ($saturation < 0) {
2685 $saturation += 1;
2686 }
2687
2688 $lightness = round($lightness * 100);
2689 $saturation = round($saturation * 100);
2690
2691 if ($returnArray) {
2692 return array(
2693 'h' => $hue,
2694 'l' => $lightness,
2695 's' => $saturation,
2696 'a' => $alpha === false ? 1 : $alpha
2697 );
2698 } elseif ($alpha) {
2699 return 'hsla('.$hue.', '.$saturation.', '.$lightness.' / '.$alpha.')';
2700 } else {
2701 return 'hsl('.$hue.', '.$saturation.', '.$lightness.')';
2702 }
2703}
2704
2712function cartesianArray(array $input)
2713{
2714 // filter out empty values
2715 $input = array_filter($input);
2716
2717 $result = array(array());
2718
2719 foreach ($input as $key => $values) {
2720 $append = array();
2721
2722 foreach ($result as $product) {
2723 foreach ($values as $item) {
2724 $product[$key] = $item;
2725 $append[] = $product;
2726 }
2727 }
2728
2729 $result = $append;
2730 }
2731
2732 return $result;
2733}
2734
2735
2742function getModuleDirForApiClass($moduleobject)
2743{
2744 $moduledirforclass = $moduleobject;
2745 if ($moduledirforclass != 'api') {
2746 $moduledirforclass = preg_replace('/api$/i', '', $moduledirforclass);
2747 }
2748
2749 if ($moduleobject == 'contracts') {
2750 $moduledirforclass = 'contrat';
2751 } elseif (in_array($moduleobject, array('admin', 'login', 'setup', 'access', 'status', 'tools', 'documents', 'objectlinks'))) {
2752 $moduledirforclass = 'api';
2753 } elseif ($moduleobject == 'contact' || $moduleobject == 'contacts' || $moduleobject == 'customer' || $moduleobject == 'thirdparty' || $moduleobject == 'thirdparties') {
2754 $moduledirforclass = 'societe';
2755 } elseif ($moduleobject == 'propal' || $moduleobject == 'propale' || $moduleobject == 'proposals') {
2756 $moduledirforclass = 'comm/propal';
2757 } elseif ($moduleobject == 'agenda' || $moduleobject == 'agendaevents') {
2758 $moduledirforclass = 'comm/action';
2759 } elseif ($moduleobject == 'adherent' || $moduleobject == 'members' || $moduleobject == 'memberstypes' || $moduleobject == 'subscriptions') {
2760 $moduledirforclass = 'adherents';
2761 } elseif ($moduleobject == 'don' || $moduleobject == 'donations') {
2762 $moduledirforclass = 'don';
2763 } elseif ($moduleobject == 'banque' || $moduleobject == 'bankaccounts') {
2764 $moduledirforclass = 'compta/bank';
2765 } elseif ($moduleobject == 'category' || $moduleobject == 'categorie') {
2766 $moduledirforclass = 'categories';
2767 } elseif ($moduleobject == 'order' || $moduleobject == 'orders') {
2768 $moduledirforclass = 'commande';
2769 } elseif ($moduleobject == 'shipments') {
2770 $moduledirforclass = 'expedition';
2771 } elseif ($moduleobject == 'multicurrencies') {
2772 $moduledirforclass = 'multicurrency';
2773 } elseif ($moduleobject == 'facture' || $moduleobject == 'invoice' || $moduleobject == 'invoices') {
2774 $moduledirforclass = 'compta/facture';
2775 } elseif ($moduleobject == 'project' || $moduleobject == 'projects' || $moduleobject == 'task' || $moduleobject == 'tasks') {
2776 $moduledirforclass = 'projet';
2777 } elseif ($moduleobject == 'stock' || $moduleobject == 'stockmovements' || $moduleobject == 'warehouses') {
2778 $moduledirforclass = 'product/stock';
2779 } elseif ($moduleobject == 'supplierproposals' || $moduleobject == 'supplierproposal' || $moduleobject == 'supplier_proposal') {
2780 $moduledirforclass = 'supplier_proposal';
2781 } elseif ($moduleobject == 'fournisseur' || $moduleobject == 'supplierinvoices' || $moduleobject == 'supplierorders') {
2782 $moduledirforclass = 'fourn';
2783 } elseif ($moduleobject == 'ficheinter' || $moduleobject == 'interventions') {
2784 $moduledirforclass = 'fichinter';
2785 } elseif ($moduleobject == 'mos') {
2786 $moduledirforclass = 'mrp';
2787 } elseif ($moduleobject == 'workstations') {
2788 $moduledirforclass = 'workstation';
2789 } elseif ($moduleobject == 'accounting') {
2790 $moduledirforclass = 'accountancy';
2791 } elseif (in_array($moduleobject, array('products', 'expensereports', 'users', 'tickets', 'boms', 'receptions', 'partnerships', 'recruitments'))) {
2792 $moduledirforclass = preg_replace('/s$/', '', $moduleobject);
2793 } elseif ($moduleobject == 'paymentsalaries') {
2794 $moduledirforclass = 'salaries';
2795 } elseif ($moduleobject == 'paymentexpensereports') {
2796 $moduledirforclass = 'expensereport';
2797 } elseif ($moduleobject == 'eventattendees') {
2798 $moduledirforclass = 'eventorganization';
2799 } elseif ($moduleobject == 'holidays') {
2800 $moduledirforclass = 'holiday';
2801 }
2802
2803 return $moduledirforclass;
2804}
2805
2813function randomColorPart($min = 0, $max = 255)
2814{
2815 return str_pad(dechex(mt_rand($min, $max)), 2, '0', STR_PAD_LEFT);
2816}
2817
2825function randomColor($min = 0, $max = 255)
2826{
2827 return randomColorPart($min, $max).randomColorPart($min, $max).randomColorPart($min, $max);
2828}
2829
2830
2831if (!function_exists('dolEscapeXML')) {
2838 function dolEscapeXML($string)
2839 {
2840 return strtr($string, array('\'' => '&apos;', '"' => '&quot;', '&' => '&amp;', '<' => '&lt;', '>' => '&gt;'));
2841 }
2842}
2843
2844
2852{
2854 // Define $urlwithroot
2855 $urlwithouturlroot = preg_replace('/'.preg_quote(DOL_URL_ROOT, '/').'$/i', '', trim($dolibarr_main_url_root));
2856 $urlwithroot = $urlwithouturlroot.DOL_URL_ROOT; // This is to use external domain name found into config file
2857 //$urlwithroot=DOL_MAIN_URL_ROOT; // This is to use same domain name than current
2858 $notetoshow = preg_replace('/src="[a-zA-Z0-9_\/\-\.]*(viewimage\.php\?modulepart=medias[^"]*)"/', 'src="'.$urlwithroot.'/\1"', $notetoshow);
2859 return $notetoshow;
2860}
2861
2870function price2fec($amount)
2871{
2872 global $conf;
2873
2874 // Clean parameters
2875 if (empty($amount)) {
2876 $amount = 0; // To have a numeric value if amount not defined or = ''
2877 }
2878 $amount = (is_numeric($amount) ? $amount : 0); // Check if amount is numeric, for example, an error occurred when amount value = o (letter) instead 0 (number)
2879
2880 // Output decimal number by default
2881 $nbdecimal = (!getDolGlobalString('ACCOUNTING_FEC_DECIMAL_LENGTH') ? 2 : $conf->global->ACCOUNTING_FEC_DECIMAL_LENGTH);
2882
2883 // Output separators by default
2884 $dec = (!getDolGlobalString('ACCOUNTING_FEC_DECIMAL_SEPARATOR') ? ',' : $conf->global->ACCOUNTING_FEC_DECIMAL_SEPARATOR);
2885 $thousand = (!getDolGlobalString('ACCOUNTING_FEC_THOUSAND_SEPARATOR') ? '' : $conf->global->ACCOUNTING_FEC_THOUSAND_SEPARATOR);
2886
2887 // Format number
2888 $output = number_format($amount, $nbdecimal, $dec, $thousand);
2889
2890 return $output;
2891}
2892
2899function phpSyntaxError($code)
2900{
2901 if (!defined("CR")) {
2902 define("CR", "\r");
2903 }
2904 if (!defined("LF")) {
2905 define("LF", "\n");
2906 }
2907 if (!defined("CRLF")) {
2908 define("CRLF", "\r\n");
2909 }
2910
2911 $braces = 0;
2912 $inString = 0;
2913 foreach (token_get_all('<?php '.$code) as $token) {
2914 if (is_array($token)) {
2915 switch ($token[0]) {
2916 case T_CURLY_OPEN:
2917 case T_DOLLAR_OPEN_CURLY_BRACES:
2918 case T_START_HEREDOC:
2919 ++$inString;
2920 break;
2921 case T_END_HEREDOC:
2922 --$inString;
2923 break;
2924 }
2925 } elseif ($inString & 1) {
2926 switch ($token) {
2927 case '`':
2928 case '\'':
2929 case '"':
2930 --$inString;
2931 break;
2932 }
2933 } else {
2934 switch ($token) {
2935 case '`':
2936 case '\'':
2937 case '"':
2938 ++$inString;
2939 break;
2940 case '{':
2941 ++$braces;
2942 break;
2943 case '}':
2944 if ($inString) {
2945 --$inString;
2946 } else {
2947 --$braces;
2948 if ($braces < 0) {
2949 break 2;
2950 }
2951 }
2952 break;
2953 }
2954 }
2955 }
2956 $inString = @ini_set('log_errors', false);
2957 $token = @ini_set('display_errors', true);
2958 ob_start();
2959 $code = substr($code, strlen('<?php '));
2960 $braces || $code = "if(0){{$code}\n}";
2961 // @phan-suppress-next-line PhanPluginUnsafeEval
2962 if (eval($code) === false) {
2963 if ($braces) {
2964 $braces = PHP_INT_MAX;
2965 } else {
2966 false !== strpos($code, CR) && $code = strtr(str_replace(CRLF, LF, $code), CR, LF);
2967 $braces = substr_count($code, LF);
2968 }
2969 $code = ob_get_clean();
2970 $code = strip_tags($code);
2971 if (preg_match("'syntax error, (.+) in .+ on line (\d+)$'s", $code, $code)) {
2972 $code[2] = (int) $code[2];
2973 $code = $code[2] <= $braces
2974 ? array($code[1], $code[2])
2975 : array('unexpected $end'.substr($code[1], 14), $braces);
2976 } else {
2977 $code = array('syntax error', 0);
2978 }
2979 } else {
2980 ob_end_clean();
2981 $code = false;
2982 }
2983 @ini_set('display_errors', $token);
2984 @ini_set('log_errors', $inString);
2985 return $code;
2986}
2987
2988
2995{
2996 global $user;
2997
2998 // If $acceptlocallinktomedia is true, we can add link media files int email templates (we already can do this into HTML editor of an email).
2999 // Note that local link to a file into medias are replaced with a real link by email in CMailFile.class.php with value $urlwithroot defined like this:
3000 // $urlwithouturlroot = preg_replace('/'.preg_quote(DOL_URL_ROOT, '/').'$/i', '', trim($dolibarr_main_url_root));
3001 // $urlwithroot = $urlwithouturlroot.DOL_URL_ROOT; // This is to use external domain name found into config file
3002
3003 $acceptlocallinktomedia = getDolGlobalInt('MAIN_DISALLOW_MEDIAS_IN_EMAIL_TEMPLATES') ? 0 : 1;
3004
3005 // By default we acceptto add medias from emails templates but this may be refused if later
3006 // we detect we are not on a public url that we can access remotely (if we are on a private network, such files can't be reached),
3007 // except if MAIN_ALLOW_WYSIWYG_LOCAL_MEDIAS_ON_PRIVATE_NETWORK is net, in which case we accept also if instance has a local or private network URL.
3008
3009 if ($acceptlocallinktomedia) {
3011 $urlwithouturlroot = preg_replace('/'.preg_quote(DOL_URL_ROOT, '/').'$/i', '', trim($dolibarr_main_url_root));
3012
3013 // Parse $newUrl to get the IP of the server
3014 $newUrlArray = parse_url($urlwithouturlroot);
3015 $hosttocheck = $newUrlArray['host'];
3016 $hosttocheck = str_replace(array('[', ']'), '', $hosttocheck); // Remove brackets of IPv6
3017
3018 if (function_exists('gethostbyname')) {
3019 $iptocheck = gethostbyname($hosttocheck);
3020 } else {
3021 $iptocheck = $hosttocheck;
3022 }
3023
3024 //var_dump($iptocheck.' '.$acceptlocallinktomedia);
3025 $allowPrivateNetworkIP = getDolGlobalInt('MAIN_ALLOW_WYSIWYG_LOCAL_MEDIAS_ON_PRIVATE_NETWORK');
3026 if (!$allowPrivateNetworkIP && !filter_var($iptocheck, FILTER_VALIDATE_IP, FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE)) {
3027 // If ip of public url ($iptocheck) is a private network IP (192.168.0.1; 127.0.0.1...), we do not allow to upload files on media (they are unreachable publicly).
3028 $acceptlocallinktomedia = 0;
3029 //dol_syslog("WYSIWYG Editor : local media not allowed (checked IP: {$iptocheck}). Use MAIN_ALLOW_WYSIWYG_LOCAL_MEDIAS_ON_PRIVATE_NETWORK = 1 to allow local URL into WYSIWYG html content");
3030 }
3031
3032 $allowUrlInHTTP = getDolGlobalInt('MAIN_ALLOW_WYSIWYG_EVEN_ON_UNSECURED_EXTERNAL_HTTP_URL');
3033 if (!$allowUrlInHTTP && preg_match('/http:/i', $urlwithouturlroot)) {
3034 // If public url is not a https, we do not allow to add medias link. It will generate security alerts when email will be sent.
3035 $acceptlocallinktomedia = 0;
3036 // TODO Show a warning
3037 }
3038
3039 if (!empty($user->socid)) {
3040 $acceptlocallinktomedia = 0;
3041 }
3042 }
3043
3044 //return 1;
3045 return $acceptlocallinktomedia;
3046}
3047
3048
3056{
3057 $string = trim($string);
3058
3059 // If string does not start and end with parenthesis, we return $string as is.
3060 if (! preg_match('/^\‍(.*\‍)$/', $string)) {
3061 return $string;
3062 }
3063
3064 $nbofchars = dol_strlen($string);
3065 $i = 0;
3066 $g = 0;
3067 $countparenthesis = 0;
3068 while ($i < $nbofchars) {
3069 $char = dol_substr($string, $i, 1);
3070 if ($char == '(') {
3071 $countparenthesis++;
3072 } elseif ($char == ')') {
3073 $countparenthesis--;
3074 if ($countparenthesis <= 0) { // We reach the end of an independent group of parenthesis
3075 $g++;
3076 }
3077 }
3078 $i++;
3079 }
3080
3081 if ($g <= 1) {
3082 return preg_replace('/^\‍(/', '', preg_replace('/\‍)$/', '', $string));
3083 }
3084
3085 return $string;
3086}
3087
3088
3096{
3097 $arrayofcommonemoji = array(
3098 'misc' => array('2600', '26FF'), // Miscellaneous Symbols
3099 'ding' => array('2700', '27BF'), // Dingbats
3100 '????' => array('9989', '9989'), // Variation Selectors
3101 'vars' => array('FE00', 'FE0F'), // Variation Selectors
3102 'pict' => array('1F300', '1F5FF'), // Miscellaneous Symbols and Pictographs
3103 'emot' => array('1F600', '1F64F'), // Emoticons
3104 'tran' => array('1F680', '1F6FF'), // Transport and Map Symbols
3105 'flag' => array('1F1E0', '1F1FF'), // Flags (note: may be 1F1E6 instead of 1F1E0)
3106 'supp' => array('1F900', '1F9FF'), // Supplemental Symbols and Pictographs
3107 );
3108
3109 return $arrayofcommonemoji;
3110}
3111
3119function removeEmoji($text, $allowedemoji = 1)
3120{
3121 // $allowedemoji can be
3122 // 0=no emoji, 1=exclude the main known emojis (default), 2=keep only the main known (not implemented), 3=accept all
3123 // Note that to accept emoji in database, you must use utf8mb4, utf8mb3 is not enough.
3124
3125 if ($allowedemoji == 0) {
3126 // For a large removal:
3127 $text = preg_replace('/[\x{2600}-\x{FFFF}]/u', '', $text);
3128 $text = preg_replace('/[\x{10000}-\x{10FFFF}]/u', '', $text);
3129 }
3130
3131 // Delete emoji chars with a regex
3132 // See https://www.unicode.org/emoji/charts/full-emoji-list.html
3133 if ($allowedemoji == 1) {
3134 $arrayofcommonemoji = getArrayOfEmojiBis();
3135
3136 foreach ($arrayofcommonemoji as $key => $valarray) {
3137 $text = preg_replace('/[\x{'.$valarray[0].'}-\x{'.$valarray[1].'}]/u', '', $text);
3138 }
3139 }
3140
3141 if ($allowedemoji == 2) {
3142 // TODO Not yet implemented
3143 }
3144
3145 return $text;
3146}
3147
3148
3157function csvClean($newvalue, $charset = '', $separator = '')
3158{
3159 global $langs;
3160
3161 $addquote = 0;
3162
3163 if (empty($charset)) {
3164 $charset = getDolGlobalString('EXPORT_CSV_FORCE_CHARSET');
3165 }
3166 if (empty($separator)) {
3167 $separator = getDolGlobalString('EXPORT_CSV_SEPARATOR_TO_USE');
3168 }
3169
3170
3171 $newvalue = $langs->convToOutputCharset($newvalue, 'UTF-8', $charset); // newvalue is now encoded into $charset
3172
3173
3174 // Rule Dolibarr: No HTML
3175 //print $charset.' '.$newvalue."\n";
3176 //$newvalue=dol_string_nohtmltag($newvalue,0,$charset);
3177 $newvalue = dol_htmlcleanlastbr($newvalue);
3178 //print $charset.' '.$newvalue."\n";
3179
3180 // Rule 1 CSV: No CR, LF in cells (except if USE_STRICT_CSV_RULES is 1, we can keep record as it is but we must add quotes)
3181 $oldvalue = $newvalue;
3182 $newvalue = str_replace("\r", '', $newvalue);
3183 $newvalue = str_replace("\n", '\n', $newvalue);
3184 if (getDolGlobalString('USE_STRICT_CSV_RULES') && $oldvalue != $newvalue) {
3185 // If we must use enclusure on text with CR/LF)
3186 if (getDolGlobalInt('USE_STRICT_CSV_RULES') == 1) {
3187 // If we use strict CSV rules (original value must remain but we add quote)
3188 $newvalue = $oldvalue;
3189 }
3190 $addquote = 1;
3191 }
3192
3193 // Rule 2 CSV: If value contains ", we must escape with ", and add "
3194 if (preg_match('/"/', $newvalue)) {
3195 $addquote = 1;
3196 $newvalue = str_replace('"', '""', $newvalue);
3197 }
3198
3199 // Rule 3 CSV: If value contains separator, we must add "
3200 if (preg_match('/'.$separator.'/', $newvalue)) {
3201 $addquote = 1;
3202 }
3203
3204 return ($addquote ? '"' : '').$newvalue.($addquote ? '"' : '');
3205}
$id
Support class for third parties, contacts, members, users or resources.
Definition account.php:48
if( $user->socid > 0) if(! $user->hasRight('accounting', 'chartofaccount')) $object
Definition card.php:67
global $dolibarr_main_url_root
Class to manage Dolibarr users.
getServerTimeZoneInt($refgmtdate='now')
Return server timezone int.
Definition date.lib.php:86
dol_dir_list($utf8_path, $types="all", $recursive=0, $filter="", $excludefilter=null, $sortcriteria="name", $sortorder=SORT_ASC, $mode=0, $nohook=0, $relativename="", $donotfollowsymlinks=0, $nbsecondsold=0)
Scan a directory and return a list of files/directories.
Definition files.lib.php:63
array2table($data, $tableMarkup=1, $tableoptions='', $troptions='', $tdoptions='')
Return an html table from an array.
dol_buildlogin($lastname, $firstname)
Build a login from lastname, firstname.
if(!function_exists( 'dolEscapeXML')) convertBackOfficeMediasLinksToPublicLinks($notetoshow)
Convert links to local wrapper to medias files into a string into a public external URL readable on i...
getArrayOfEmojiBis()
Return array of Emojis for miscellaneous use.
colorHexToRgb($hex, $alpha=false, $returnArray=false)
get_string_between($string, $start, $end)
Get string from "$start" up to "$end".
dolObfuscateEmail($mail, $replace="*", $nbreplace=8, $nbdisplaymail=4, $nbdisplaydomain=3, $displaytld=true)
Returns an email value with obfuscated parts.
version_webserver()
Return web server version.
getModuleDirForApiClass($moduleobject)
Get name of directory where the api_...class.php file is stored.
phpSyntaxError($code)
Check the syntax of some PHP code.
dolGetModulesDirs($subdir='')
Return list of directories that contain modules.
array2tr($data, $troptions='', $tdoptions='')
Return lines of an html table from an array Used by array2table function only.
version_dolibarr()
Return Dolibarr version.
binhex($bin, $pad=false, $upper=false)
Convert a binary data to string that represent hexadecimal value.
colorAgressiveness($hex, $ratio=-50, $brightness=0)
Change color to make it less aggressive (ratio is negative) or more aggressive (ratio is positive)
removeGlobalParenthesis($string)
Remove first and last parenthesis but only if first is the opening and last the closing of the same g...
cartesianArray(array $input)
Applies the Cartesian product algorithm to an array Source: http://stackoverflow.com/a/15973172.
getSoapParams()
Return array to use for SoapClient constructor.
colorAdjustBrightness($hex, $steps)
cleanCorruptedTree($db, $tabletocleantree, $fieldfkparent)
Clean corrupted database tree (orphelins linked to a not existing parent), record linked to themself,...
colorArrayToHex($arraycolor, $colorifnotfound='888888')
Convert an array with RGB value into hex RGB value.
dol_print_reduction($reduction, $langs)
Returns formatted reduction.
colorStringToArray($stringcolor, $colorifnotfound=array(88, 88, 88))
Convert a string RGB value ('FFFFFF', '255,255,255') into an array RGB array(255,255,...
acceptLocalLinktoMedia()
Check the syntax of some PHP code.
randomColor($min=0, $max=255)
Return hexadecimal color randomly.
is_ip($ip)
This function evaluates a string that should be a valid IPv4 Note: For ip 169.254....
check_value($mask, $value)
Check value.
weight_convert($weight, &$from_unit, $to_unit)
Convertit une masse d'une unite vers une autre unite.
getListOfModels($db, $type, $maxfilenamelength=0, $showempty=0)
Return list of activated modules usable for document generation.
clean_url($url, $http=1)
Clean an url string.
version_php()
Return PHP version.
colorHexToHsl($hex, $alpha=false, $returnArray=false)
Color Hex to Hsl (used for style)
dol_getDefaultFormat($outputlangs=null)
Try to guess default paper format according to language into $langs.
hexbin($hexa)
Convert an hexadecimal string into a binary string.
dol_set_user_param($db, $conf, &$user, $tab)
Save personal parameter.
numero_semaine($time)
Retourne le numero de la semaine par rapport a une date.
randomColorPart($min=0, $max=255)
Return 2 hexa code randomly.
colorDarker($hex, $percent)
dolGetElementUrl($objectid, $objecttype, $withpicto=0, $option='')
Return link url to an object.
dol_print_object_info($object, $usetable=0)
Show information on an object TODO Move this into html.formother.
isValidUrl($url, $http=0, $pass=0, $port=0, $path=0, $query=0, $anchor=0)
Url string validation <http[s]> :// [user[:pass]@] hostname [port] [/path] [?getquery] [anchor].
version_db()
Return DB version.
isValidMailDomain($mail)
Return true if email has a domain name that can be resolved to MX type.
dolAddEmailTrackId($email, $trackingid)
Return an email formatted to include a tracking id For example myemail@example.com becom myemail+trac...
colorLighten($hex, $percent)
isValidVATID($company)
Check if VAT numero is valid (check done on syntax only, no database or remote access)
version_os($option='')
Return OS version.
price2fec($amount)
Function to format a value into a defined format for French administration (no thousand separator & d...
get_next_value($db, $mask, $table, $field, $where='', $objsoc='', $date='', $mode='next', $bentityon=true, $objuser=null, $forceentity=null, $objbookkeeping=null)
Return last or next value for a mask (according to area we should not reset)
csvClean($newvalue, $charset='', $separator='')
Clean a cell to respect rules of CSV file cells.
removeEmoji($text, $allowedemoji=1)
Remove EMoji from email content.
jsUnEscape($source)
Same function than javascript unescape() function but in PHP.
colorValidateHex($color, $allow_white=true)
dol_html_entity_decode($a, $b, $c='UTF-8', $keepsomeentities=0)
Replace html_entity_decode functions to manage errors.
dol_mktime($hour, $minute, $second, $month, $day, $year, $gm='auto', $check=1)
Return a timestamp date built from detailed information (by default a local PHP server timestamp) Rep...
vatrate($rate, $addpercent=false, $info_bits=0, $usestarfornpr=0, $html=0)
Return a string with VAT rate label formatted for view output Used into pdf and HTML pages.
dol_string_nospecial($str, $newstr='_', $badcharstoreplace='', $badcharstoremove='', $keepspaces=0)
Clean a string from all punctuation characters to use it as a ref or login.
dol_strlen($string, $stringencoding='UTF-8')
Make a strlen call.
isValidMXRecord($domain)
Return if the domain name has a valid MX record.
dol_now($mode='auto')
Return date for now.
getDolGlobalInt($key, $default=0)
Return a Dolibarr global constant int value.
dol_print_date($time, $format='', $tzoutput='auto', $outputlangs=null, $encodetooutput=false)
Output date in a string format according to outputlangs (or langs if not defined).
if(!function_exists( 'dol_getprefix')) dol_include_once($relpath, $classname='')
Make an include_once using default root and alternate root if it fails.
dol_string_unaccent($str)
Clean a string from all accent characters to be used as ref, login or by dol_sanitizeFileName.
dol_htmlcleanlastbr($stringtodecode)
This function remove all ending and br at end.
dol_substr($string, $start, $length=null, $stringencoding='', $trunconbytes=0)
Make a substring.
dol_print_error($db=null, $error='', $errors=null)
Displays error message system with all the information to facilitate the diagnosis and the escalation...
dol_trunc($string, $size=40, $trunc='right', $stringencoding='UTF-8', $nodot=0, $display=0)
Truncate a string to a particular length adding '…' if string larger than length.
isValidEmail($address, $acceptsupervisorkey=0, $acceptuserkey=0)
Return true if email syntax is ok.
getDolGlobalString($key, $default='')
Return a Dolibarr global constant string value.
dol_syslog($message, $level=LOG_INFO, $ident=0, $suffixinfilename='', $restricttologhandler='', $logcontext=null)
Write log message into outputs.
global $conf
The following vars must be defined: $type2label $form $conf, $lang, The following vars may also be de...
Definition member.php:79