dolibarr  21.0.0-alpha
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 
42 function 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 
84 function 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 
123 function dol_getDefaultFormat(Translate $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 
150 function 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  if (!empty($object->user_approve) && is_object($object->user_approve)) {
365  if ($object->user_approve->id) {
366  print $object->user_approve->getNomUrl(-1, '', 0, 0, 0);
367  } else {
368  print $langs->trans("Unknown");
369  }
370  } else {
371  $userstatic = new User($db);
372  $userstatic->fetch($object->user_approve_id ? $object->user_approve_id : $object->user_approve);
373  if ($userstatic->id) {
374  print $userstatic->getNomUrl(-1, '', 0, 0, 0);
375  } else {
376  print $langs->trans("Unknown");
377  }
378  }
379  if ($usetable) {
380  print '</td></tr>';
381  } else {
382  print '<br>';
383  }
384  }
385 
386  // Date approve
387  if (!empty($object->date_approve) || !empty($object->date_approval)) {
388  if ($usetable) {
389  print '<tr><td class="titlefield">';
390  }
391  print $langs->trans("DateApprove");
392  if ($usetable) {
393  print '</td><td>';
394  } else {
395  print ': ';
396  }
397  print dol_print_date($object->date_approve ? $object->date_approve : $object->date_approval, 'dayhour', 'tzserver');
398  if ($deltadateforuser) {
399  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>';
400  }
401  if ($usetable) {
402  print '</td></tr>';
403  } else {
404  print '<br>';
405  }
406  }
407 
408  // User approve
409  if (!empty($object->user_approve_id2)) {
410  if ($usetable) {
411  print '<tr><td class="titlefield">';
412  }
413  print $langs->trans("ApprovedBy");
414  if ($usetable) {
415  print '</td><td>';
416  } else {
417  print ': ';
418  }
419  $userstatic = new User($db);
420  $userstatic->fetch($object->user_approve_id2);
421  if ($userstatic->id) {
422  print $userstatic->getNomUrl(-1, '', 0, 0, 0);
423  } else {
424  print $langs->trans("Unknown");
425  }
426  if ($usetable) {
427  print '</td></tr>';
428  } else {
429  print '<br>';
430  }
431  }
432 
433  // Date approve
434  if (!empty($object->date_approve2)) {
435  if ($usetable) {
436  print '<tr><td class="titlefield">';
437  }
438  print $langs->trans("DateApprove2");
439  if ($usetable) {
440  print '</td><td>';
441  } else {
442  print ': ';
443  }
444  print dol_print_date($object->date_approve2, 'dayhour', 'tzserver');
445  if ($deltadateforuser) {
446  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>';
447  }
448  if ($usetable) {
449  print '</td></tr>';
450  } else {
451  print '<br>';
452  }
453  }
454 
455  // User signature
456  if (!empty($object->user_signature) || !empty($object->user_signature_id)) {
457  if ($usetable) {
458  print '<tr><td class="titlefield">';
459  }
460  print $langs->trans('SignedBy');
461  if ($usetable) {
462  print '</td><td>';
463  } else {
464  print ': ';
465  }
466  if (is_object($object->user_signature)) {
467  if ($object->user_signature->id) {
468  print $object->user_signature->getNomUrl(-1, '', 0, 0, 0);
469  } else {
470  print $langs->trans('Unknown');
471  }
472  } else {
473  $userstatic = new User($db);
474  $userstatic->fetch($object->user_signature_id ? $object->user_signature_id : $object->user_signature);
475  if ($userstatic->id) {
476  print $userstatic->getNomUrl(-1, '', 0, 0, 0);
477  } else {
478  print $langs->trans('Unknown');
479  }
480  }
481  if ($usetable) {
482  print '</td></tr>';
483  } else {
484  print '<br>';
485  }
486  }
487 
488  // Date signature
489  if (!empty($object->date_signature)) {
490  if ($usetable) {
491  print '<tr><td class="titlefield">';
492  }
493  print $langs->trans('DateSigning');
494  if ($usetable) {
495  print '</td><td>';
496  } else {
497  print ': ';
498  }
499  print dol_print_date($object->date_signature, 'dayhour');
500  if ($deltadateforuser) {
501  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>';
502  }
503  if ($usetable) {
504  print '</td></tr>';
505  } else {
506  print '<br>';
507  }
508  }
509 
510  // User close
511  if (!empty($object->user_closing_id)) {
512  if ($usetable) {
513  print '<tr><td class="titlefield">';
514  }
515  print $langs->trans("ClosedBy");
516  if ($usetable) {
517  print '</td><td>';
518  } else {
519  print ': ';
520  }
521  $userstatic = new User($db);
522  $userstatic->fetch($object->user_closing_id);
523  if ($userstatic->id) {
524  print $userstatic->getNomUrl(-1, '', 0, 0, 0);
525  } else {
526  print $langs->trans("Unknown");
527  }
528  if ($usetable) {
529  print '</td></tr>';
530  } else {
531  print '<br>';
532  }
533  }
534 
535  // Date close
536  if (!empty($object->date_cloture) || !empty($object->date_closing)) {
537  if (isset($object->date_cloture) && !empty($object->date_cloture)) {
538  $object->date_closing = $object->date_cloture;
539  }
540  if ($usetable) {
541  print '<tr><td class="titlefield">';
542  }
543  print $langs->trans("DateClosing");
544  if ($usetable) {
545  print '</td><td>';
546  } else {
547  print ': ';
548  }
549  print dol_print_date($object->date_closing, 'dayhour', 'tzserver');
550  if ($deltadateforuser) {
551  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>';
552  }
553  if ($usetable) {
554  print '</td></tr>';
555  } else {
556  print '<br>';
557  }
558  }
559 
560  // User conciliate
561  if (!empty($object->user_rappro) || !empty($object->user_rappro_id)) {
562  if ($usetable) {
563  print '<tr><td class="titlefield">';
564  }
565  print $langs->trans("ReconciledBy");
566  if ($usetable) {
567  print '</td><td>';
568  } else {
569  print ': ';
570  }
571  if (is_object($object->user_rappro)) {
572  if ($object->user_rappro->id) {
573  print $object->user_rappro->getNomUrl(-1, '', 0, 0, 0);
574  } else {
575  print $langs->trans("Unknown");
576  }
577  } else {
578  $userstatic = new User($db);
579  $userstatic->fetch($object->user_rappro_id ? $object->user_rappro_id : $object->user_rappro);
580  if ($userstatic->id) {
581  print $userstatic->getNomUrl(1, '', 0, 0, 0);
582  } else {
583  print $langs->trans("Unknown");
584  }
585  }
586  if ($usetable) {
587  print '</td></tr>';
588  } else {
589  print '<br>';
590  }
591  }
592 
593  // Date conciliate
594  if (!empty($object->date_rappro)) {
595  if ($usetable) {
596  print '<tr><td class="titlefield">';
597  }
598  print $langs->trans("DateConciliating");
599  if ($usetable) {
600  print '</td><td>';
601  } else {
602  print ': ';
603  }
604  print dol_print_date($object->date_rappro, 'dayhour', 'tzserver');
605  if ($deltadateforuser) {
606  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>';
607  }
608  if ($usetable) {
609  print '</td></tr>';
610  } else {
611  print '<br>';
612  }
613  }
614 
615  // Date send
616  if (!empty($object->date_envoi)) {
617  if ($usetable) {
618  print '<tr><td class="titlefield">';
619  }
620  print $langs->trans("DateLastSend");
621  if ($usetable) {
622  print '</td><td>';
623  } else {
624  print ': ';
625  }
626  print dol_print_date($object->date_envoi, 'dayhour', 'tzserver');
627  if ($deltadateforuser) {
628  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>';
629  }
630  if ($usetable) {
631  print '</td></tr>';
632  } else {
633  print '<br>';
634  }
635  }
636 
637  if ($usetable) {
638  print '</table>';
639  }
640 }
641 
642 
651 function dolAddEmailTrackId($email, $trackingid)
652 {
653  $tmp = explode('@', $email);
654  return $tmp[0].'+'.$trackingid.'@'.(isset($tmp[1]) ? $tmp[1] : '');
655 }
656 
663 function isValidMailDomain($mail)
664 {
665  list($user, $domain) = explode("@", $mail, 2);
666  return ($domain ? isValidMXRecord($domain) : 0);
667 }
668 
682 function isValidUrl($url, $http = 0, $pass = 0, $port = 0, $path = 0, $query = 0, $anchor = 0)
683 {
684  $ValidUrl = 0;
685  $urlregex = '';
686 
687  // SCHEME
688  if ($http) {
689  $urlregex .= "^(http:\/\/|https:\/\/)";
690  }
691 
692  // USER AND PASS
693  if ($pass) {
694  $urlregex .= "([a-z0-9+!*(),;?&=\$_.-]+(\:[a-z0-9+!*(),;?&=\$_.-]+)?@)";
695  }
696 
697  // HOSTNAME OR IP
698  //$urlregex .= "[a-z0-9+\$_-]+(\.[a-z0-9+\$_-]+)*"; // x allowed (ex. http://localhost, http://routerlogin)
699  //$urlregex .= "[a-z0-9+\$_-]+(\.[a-z0-9+\$_-]+)+"; // x.x
700  $urlregex .= "([a-z0-9+\$_\\\:-])+(\.[a-z0-9+\$_-][a-z0-9+\$_-]+)*"; // x ou x.xx (2 x ou plus)
701  //use only one of the above
702 
703  // PORT
704  if ($port) {
705  $urlregex .= "(\:[0-9]{2,5})";
706  }
707  // PATH
708  if ($path) {
709  $urlregex .= "(\/([a-z0-9+\$_-]\.?)+)*\/";
710  }
711  // GET Query
712  if ($query) {
713  $urlregex .= "(\?[a-z+&\$_.-][a-z0-9;:@\/&%=+\$_.-]*)";
714  }
715  // ANCHOR
716  if ($anchor) {
717  $urlregex .= "(#[a-z_.-][a-z0-9+\$_.-]*)$";
718  }
719 
720  // check
721  if (preg_match('/'.$urlregex.'/i', $url)) {
722  $ValidUrl = 1;
723  }
724  //print $urlregex.' - '.$url.' - '.$ValidUrl;
725 
726  return $ValidUrl;
727 }
728 
735 function isValidVATID($company)
736 {
737  if ($company->isInEEC()) { // Syntax check rules for EEC countries
738  /* Disabled because some companies can have an address in Irland and a vat number in France.
739  $vatprefix = $company->country_code;
740  if ($vatprefix == 'GR') $vatprefix = '(EL|GR)';
741  elseif ($vatprefix == 'MC') $vatprefix = 'FR'; // Monaco is using french VAT numbers
742  else $vatprefix = preg_quote($vatprefix, '/');*/
743  $vatprefix = '[a-zA-Z][a-zA-Z]';
744  if (!preg_match('/^'.$vatprefix.'[a-zA-Z0-9\-\.]{5,14}$/i', str_replace(' ', '', $company->tva_intra))) {
745  return 0;
746  }
747  }
748 
749  return 1;
750 }
751 
759 function clean_url($url, $http = 1)
760 {
761  // Fixed by Matelli (see http://matelli.fr/showcases/patch%73-dolibarr/fix-cleaning-url.html)
762  // To include the minus sign in a char class, we must not escape it but put it at the end of the class
763  // Also, there's no need of escape a dot sign in a class
764  $regs = array();
765  if (preg_match('/^(https?:[\\/]+)?([0-9A-Z.-]+\.[A-Z]{2,4})(:[0-9]+)?/i', $url, $regs)) {
766  $proto = $regs[1];
767  $domain = $regs[2];
768  $port = isset($regs[3]) ? $regs[3] : '';
769  //print $url." -> ".$proto." - ".$domain." - ".$port;
770  //$url = dol_string_nospecial(trim($url));
771  $url = trim($url);
772 
773  // Si http: defini on supprime le http (Si https on ne supprime pas)
774  $newproto = $proto;
775  if ($http == 0) {
776  if (preg_match('/^http:[\\/]+/i', $url)) {
777  $url = preg_replace('/^http:[\\/]+/i', '', $url);
778  $newproto = '';
779  }
780  }
781 
782  // On passe le nom de domaine en minuscule
783  $CleanUrl = preg_replace('/^'.preg_quote($proto.$domain, '/').'/i', $newproto.strtolower($domain), $url);
784 
785  return $CleanUrl;
786  } else {
787  return $url;
788  }
789 }
790 
791 
792 
804 function dolObfuscateEmail($mail, $replace = "*", $nbreplace = 8, $nbdisplaymail = 4, $nbdisplaydomain = 3, $displaytld = true)
805 {
806  if (!isValidEmail($mail)) {
807  return '';
808  }
809  $tab = explode('@', $mail);
810  $tab2 = explode('.', $tab[1]);
811  $string_replace = '';
812  $mail_name = $tab[0];
813  $mail_domaine = $tab2[0];
814  $mail_tld = '';
815 
816  $nbofelem = count($tab2);
817  for ($i = 1; $i < $nbofelem && $displaytld; $i++) {
818  $mail_tld .= '.'.$tab2[$i];
819  }
820 
821  for ($i = 0; $i < $nbreplace; $i++) {
822  $string_replace .= $replace;
823  }
824 
825  if (strlen($mail_name) > $nbdisplaymail) {
826  $mail_name = substr($mail_name, 0, $nbdisplaymail);
827  }
828 
829  if (strlen($mail_domaine) > $nbdisplaydomain) {
830  $mail_domaine = substr($mail_domaine, strlen($mail_domaine) - $nbdisplaydomain);
831  }
832 
833  return $mail_name.$string_replace.$mail_domaine.$mail_tld;
834 }
835 
836 
846 function array2tr($data, $troptions = '', $tdoptions = '')
847 {
848  $text = '<tr '.$troptions.'>';
849  foreach ($data as $key => $item) {
850  $text .= '<td '.$tdoptions.'>'.$item.'</td>';
851  }
852  $text .= '</tr>';
853  return $text;
854 }
855 
866 function array2table($data, $tableMarkup = 1, $tableoptions = '', $troptions = '', $tdoptions = '')
867 {
868  $text = '';
869  if ($tableMarkup) {
870  $text = '<table '.$tableoptions.'>';
871  }
872  foreach ($data as $key => $item) {
873  if (is_array($item)) {
874  $text .= array2tr($item, $troptions, $tdoptions);
875  } else {
876  $text .= '<tr '.$troptions.'>';
877  $text .= '<td '.$tdoptions.'>'.$key.'</td>';
878  $text .= '<td '.$tdoptions.'>'.$item.'</td>';
879  $text .= '</tr>';
880  }
881  }
882  if ($tableMarkup) {
883  $text .= '</table>';
884  }
885  return $text;
886 }
887 
904 function get_next_value($db, $mask, $table, $field, $where = '', $objsoc = '', $date = '', $mode = 'next', $bentityon = true, $objuser = null, $forceentity = null)
905 {
906  global $user;
907 
908  if (!is_object($objsoc)) {
909  $valueforccc = $objsoc;
910  } elseif ($table == "commande_fournisseur" || $table == "facture_fourn" || $table == "paiementfourn") {
911  $valueforccc = dol_string_unaccent($objsoc->code_fournisseur);
912  } else {
913  $valueforccc = dol_string_unaccent($objsoc->code_client);
914  }
915 
916  $sharetable = $table;
917  if ($table == 'facture' || $table == 'invoice') {
918  $sharetable = 'invoicenumber'; // for getEntity function
919  }
920 
921  // Clean parameters
922  if ($date == '') {
923  $date = dol_now(); // We use local year and month of PHP server to search numbers
924  }
925  // but we should use local year and month of user
926 
927  // For debugging
928  //dol_syslog("mask=".$mask, LOG_DEBUG);
929  //include_once(DOL_DOCUMENT_ROOT.'/core/lib/date.lib.php');
930  //$mask='FA{yy}{mm}-{0000@99}';
931  //$date=dol_mktime(12, 0, 0, 1, 1, 1900);
932  //$date=dol_stringtotime('20130101');
933 
934  $hasglobalcounter = false;
935  $reg = array();
936  // Extract value for mask counter, mask raz and mask offset
937  if (preg_match('/\{(0+)([@\+][0-9\-\+\=]+)?([@\+][0-9\-\+\=]+)?\}/i', $mask, $reg)) {
938  $masktri = $reg[1].(!empty($reg[2]) ? $reg[2] : '').(!empty($reg[3]) ? $reg[3] : '');
939  $maskcounter = $reg[1];
940  $hasglobalcounter = true;
941  } else {
942  // setting some defaults so the rest of the code won't fail if there is a third party counter
943  $masktri = '00000';
944  $maskcounter = '00000';
945  }
946 
947  $maskraz = -1;
948  $maskoffset = 0;
949  $resetEveryMonth = false;
950  if (dol_strlen($maskcounter) < 3 && !getDolGlobalString('MAIN_COUNTER_WITH_LESS_3_DIGITS')) {
951  return 'ErrorCounterMustHaveMoreThan3Digits';
952  }
953 
954  // Extract value for third party mask counter
955  $regClientRef = array();
956  if (preg_match('/\{(c+)(0*)\}/i', $mask, $regClientRef)) {
957  $maskrefclient = $regClientRef[1].$regClientRef[2];
958  $maskrefclient_maskclientcode = $regClientRef[1];
959  $maskrefclient_maskcounter = $regClientRef[2];
960  $maskrefclient_maskoffset = 0; //default value of maskrefclient_counter offset
961  $maskrefclient_clientcode = substr($valueforccc, 0, dol_strlen($maskrefclient_maskclientcode)); //get n first characters of client code where n is length in mask
962  $maskrefclient_clientcode = str_pad($maskrefclient_clientcode, dol_strlen($maskrefclient_maskclientcode), "#", STR_PAD_RIGHT); //padding maskrefclient_clientcode for having exactly n characters in maskrefclient_clientcode
963  $maskrefclient_clientcode = dol_string_nospecial($maskrefclient_clientcode); //sanitize maskrefclient_clientcode for sql insert and sql select like
964  if (dol_strlen($maskrefclient_maskcounter) > 0 && dol_strlen($maskrefclient_maskcounter) < 3) {
965  return 'ErrorCounterMustHaveMoreThan3Digits';
966  }
967  } else {
968  $maskrefclient = '';
969  }
970 
971  // fail if there is neither a global nor a third party counter
972  if (!$hasglobalcounter && ($maskrefclient_maskcounter == '')) {
973  return 'ErrorBadMask';
974  }
975 
976  // Extract value for third party type
977  $regType = array();
978  if (preg_match('/\{(t+)\}/i', $mask, $regType)) {
979  $masktype = $regType[1];
980  $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)
981  $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
982  } else {
983  $masktype = '';
984  $masktype_value = '';
985  }
986 
987  // Extract value for user
988  $regType = array();
989  if (preg_match('/\{(u+)\}/i', $mask, $regType)) {
990  $lastname = 'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX';
991  if (is_object($objuser)) {
992  $lastname = $objuser->lastname;
993  }
994 
995  $maskuser = $regType[1];
996  $maskuser_value = substr($lastname, 0, dol_strlen($regType[1])); // get n first characters of user firstname (where n is length in mask)
997  $maskuser_value = str_pad($maskuser_value, dol_strlen($regType[1]), "#", STR_PAD_RIGHT); // we fill on right with # to have same number of char than into mask
998  } else {
999  $maskuser = '';
1000  $maskuser_value = '';
1001  }
1002 
1003  // Personalized field {XXX-1} à {XXX-9}
1004  $maskperso = array();
1005  $maskpersonew = array();
1006  $tmpmask = $mask;
1007  $regKey = array();
1008  while (preg_match('/\{([A-Z]+)\-([1-9])\}/', $tmpmask, $regKey)) {
1009  $maskperso[$regKey[1]] = '{'.$regKey[1].'-'.$regKey[2].'}';
1010  // @phan-suppress-next-line PhanParamSuspiciousOrder
1011  $maskpersonew[$regKey[1]] = str_pad('', (int) $regKey[2], '_', STR_PAD_RIGHT);
1012  $tmpmask = preg_replace('/\{'.$regKey[1].'\-'.$regKey[2].'\}/i', $maskpersonew[$regKey[1]], $tmpmask);
1013  }
1014 
1015  if (strstr($mask, 'user_extra_')) {
1016  $start = "{user_extra_";
1017  $end = "\}";
1018  $extra = get_string_between($mask, "user_extra_", "}");
1019  if (!empty($user->array_options['options_'.$extra])) {
1020  $mask = preg_replace('#('.$start.')(.*?)('.$end.')#si', $user->array_options['options_'.$extra], $mask);
1021  }
1022  }
1023  $maskwithonlyymcode = $mask;
1024  $maskwithonlyymcode = preg_replace('/\{(0+)([@\+][0-9\-\+\=]+)?([@\+][0-9\-\+\=]+)?\}/i', $maskcounter, $maskwithonlyymcode);
1025  $maskwithonlyymcode = preg_replace('/\{dd\}/i', 'dd', $maskwithonlyymcode);
1026  $maskwithonlyymcode = preg_replace('/\{(c+)(0*)\}/i', $maskrefclient, $maskwithonlyymcode);
1027  $maskwithonlyymcode = preg_replace('/\{(t+)\}/i', $masktype_value, $maskwithonlyymcode);
1028  $maskwithonlyymcode = preg_replace('/\{(u+)\}/i', $maskuser_value, $maskwithonlyymcode);
1029  foreach ($maskperso as $key => $val) {
1030  $maskwithonlyymcode = preg_replace('/'.preg_quote($val, '/').'/i', $maskpersonew[$key], $maskwithonlyymcode);
1031  }
1032  $maskwithnocode = $maskwithonlyymcode;
1033  $maskwithnocode = preg_replace('/\{yyyy\}/i', 'yyyy', $maskwithnocode);
1034  $maskwithnocode = preg_replace('/\{yy\}/i', 'yy', $maskwithnocode);
1035  $maskwithnocode = preg_replace('/\{y\}/i', 'y', $maskwithnocode);
1036  $maskwithnocode = preg_replace('/\{mm\}/i', 'mm', $maskwithnocode);
1037  // Now maskwithnocode = 0000ddmmyyyyccc for example
1038  // and maskcounter = 0000 for example
1039  //print "maskwithonlyymcode=".$maskwithonlyymcode." maskwithnocode=".$maskwithnocode."\n<br>";
1040  //var_dump($reg);
1041 
1042  // If an offset is asked
1043  if (!empty($reg[2]) && preg_match('/^\+/', $reg[2])) {
1044  $maskoffset = preg_replace('/^\+/', '', $reg[2]);
1045  }
1046  if (!empty($reg[3]) && preg_match('/^\+/', $reg[3])) {
1047  $maskoffset = preg_replace('/^\+/', '', $reg[3]);
1048  }
1049 
1050  // Define $sqlwhere
1051  $sqlwhere = '';
1052  $yearoffset = 0; // Use year of current $date by default
1053  $yearoffsettype = false; // false: no reset, 0,-,=,+: reset at offset SOCIETE_FISCAL_MONTH_START, x=reset at offset x
1054 
1055  // If a restore to zero after a month is asked we check if there is already a value for this year.
1056  if (!empty($reg[2]) && preg_match('/^@/', $reg[2])) {
1057  $yearoffsettype = preg_replace('/^@/', '', $reg[2]);
1058  }
1059  if (!empty($reg[3]) && preg_match('/^@/', $reg[3])) {
1060  $yearoffsettype = preg_replace('/^@/', '', $reg[3]);
1061  }
1062 
1063  //print "yearoffset=".$yearoffset." yearoffsettype=".$yearoffsettype;
1064  if (is_numeric($yearoffsettype) && $yearoffsettype >= 1) {
1065  $maskraz = $yearoffsettype; // For backward compatibility
1066  } elseif ($yearoffsettype === '0' || (!empty($yearoffsettype) && !is_numeric($yearoffsettype) && getDolGlobalInt('SOCIETE_FISCAL_MONTH_START') > 1)) {
1067  $maskraz = getDolGlobalString('SOCIETE_FISCAL_MONTH_START');
1068  }
1069  //print "maskraz=".$maskraz; // -1=no reset
1070 
1071  if ($maskraz > 0) { // A reset is required
1072  if ($maskraz == 99) {
1073  $maskraz = (int) date('m', $date);
1074  $resetEveryMonth = true;
1075  }
1076  if ($maskraz > 12) {
1077  return 'ErrorBadMaskBadRazMonth';
1078  }
1079 
1080  // Define posy, posm and reg
1081  if ($maskraz > 1) { // if reset is not first month, we need month and year into mask
1082  if (preg_match('/^(.*)\{(y+)\}\{(m+)\}/i', $maskwithonlyymcode, $reg)) {
1083  $posy = 2;
1084  $posm = 3;
1085  } elseif (preg_match('/^(.*)\{(m+)\}\{(y+)\}/i', $maskwithonlyymcode, $reg)) {
1086  $posy = 3;
1087  $posm = 2;
1088  } else {
1089  return 'ErrorCantUseRazInStartedYearIfNoYearMonthInMask';
1090  }
1091 
1092  if (dol_strlen($reg[$posy]) < 2) {
1093  return 'ErrorCantUseRazWithYearOnOneDigit';
1094  }
1095  } else { // if reset is for a specific month in year, we need year
1096  if (preg_match('/^(.*)\{(m+)\}\{(y+)\}/i', $maskwithonlyymcode, $reg)) {
1097  $posy = 3;
1098  $posm = 2;
1099  } elseif (preg_match('/^(.*)\{(y+)\}\{(m+)\}/i', $maskwithonlyymcode, $reg)) {
1100  $posy = 2;
1101  $posm = 3;
1102  } elseif (preg_match('/^(.*)\{(y+)\}/i', $maskwithonlyymcode, $reg)) {
1103  $posy = 2;
1104  $posm = 0;
1105  } else {
1106  return 'ErrorCantUseRazIfNoYearInMask';
1107  }
1108  }
1109  // Define length
1110  $yearlen = $posy ? dol_strlen($reg[$posy]) : 0;
1111  $monthlen = $posm ? dol_strlen($reg[$posm]) : 0;
1112  // Define pos
1113  $yearpos = (dol_strlen($reg[1]) + 1);
1114  $monthpos = ($yearpos + $yearlen);
1115  if ($posy == 3 && $posm == 2) { // if month is before year
1116  $monthpos = (dol_strlen($reg[1]) + 1);
1117  $yearpos = ($monthpos + $monthlen);
1118  }
1119  //print "xxx ".$maskwithonlyymcode." maskraz=".$maskraz." posy=".$posy." yearlen=".$yearlen." yearpos=".$yearpos." posm=".$posm." monthlen=".$monthlen." monthpos=".$monthpos." yearoffsettype=".$yearoffsettype." resetEveryMonth=".$resetEveryMonth."\n";
1120 
1121  // Define $yearcomp and $monthcomp (that will be use in the select where to search max number)
1122  $monthcomp = $maskraz;
1123  $yearcomp = 0;
1124 
1125  if (!empty($yearoffsettype) && !is_numeric($yearoffsettype) && $yearoffsettype != '=') { // $yearoffsettype is - or +
1126  $currentyear = (int) date("Y", $date);
1127  $fiscaldate = dol_mktime('0', '0', '0', $maskraz, '1', $currentyear);
1128  $newyeardate = dol_mktime('0', '0', '0', '1', '1', $currentyear);
1129  $nextnewyeardate = dol_mktime('0', '0', '0', '1', '1', $currentyear + 1);
1130  //echo 'currentyear='.$currentyear.' date='.dol_print_date($date, 'day').' fiscaldate='.dol_print_date($fiscaldate, 'day').'<br>';
1131 
1132  // If after or equal of current fiscal date
1133  if ($date >= $fiscaldate) {
1134  // If before of next new year date
1135  if ($date < $nextnewyeardate && $yearoffsettype == '+') {
1136  $yearoffset = 1;
1137  }
1138  } elseif ($date >= $newyeardate && $yearoffsettype == '-') {
1139  // If after or equal of current new year date
1140  $yearoffset = -1;
1141  }
1142  } elseif ((int) date("m", $date) < $maskraz && empty($resetEveryMonth)) {
1143  // For backward compatibility
1144  $yearoffset = -1;
1145  } // If current month lower that month of return to zero, year is previous year
1146 
1147  if ($yearlen == 4) {
1148  $yearcomp = sprintf("%04d", idate("Y", $date) + $yearoffset);
1149  } elseif ($yearlen == 2) {
1150  $yearcomp = sprintf("%02d", idate("y", $date) + $yearoffset);
1151  } elseif ($yearlen == 1) {
1152  $yearcomp = (int) substr(date('y', $date), 1, 1) + $yearoffset;
1153  }
1154  if ($monthcomp > 1 && empty($resetEveryMonth)) { // Test with month is useless if monthcomp = 0 or 1 (0 is same as 1) (regis: $monthcomp can't equal 0)
1155  if ($yearlen == 4) {
1156  $yearcomp1 = sprintf("%04d", idate("Y", $date) + $yearoffset + 1);
1157  } elseif ($yearlen == 2) {
1158  $yearcomp1 = sprintf("%02d", idate("y", $date) + $yearoffset + 1);
1159  }
1160 
1161  $sqlwhere .= "(";
1162  $sqlwhere .= " (SUBSTRING(".$field.", ".$yearpos.", ".$yearlen.") = '".$db->escape($yearcomp)."'";
1163  $sqlwhere .= " AND SUBSTRING(".$field.", ".$monthpos.", ".$monthlen.") >= '".str_pad($monthcomp, $monthlen, '0', STR_PAD_LEFT)."')";
1164  $sqlwhere .= " OR";
1165  $sqlwhere .= " (SUBSTRING(".$field.", ".$yearpos.", ".$yearlen.") = '".$db->escape($yearcomp1)."'";
1166  $sqlwhere .= " AND SUBSTRING(".$field.", ".$monthpos.", ".$monthlen.") < '".str_pad($monthcomp, $monthlen, '0', STR_PAD_LEFT)."') ";
1167  $sqlwhere .= ')';
1168  } elseif ($resetEveryMonth) {
1169  $sqlwhere .= "(SUBSTRING(".$field.", ".$yearpos.", ".$yearlen.") = '".$db->escape($yearcomp)."'";
1170  $sqlwhere .= " AND SUBSTRING(".$field.", ".$monthpos.", ".$monthlen.") = '".str_pad($monthcomp, $monthlen, '0', STR_PAD_LEFT)."')";
1171  } else { // reset is done on january
1172  $sqlwhere .= "(SUBSTRING(".$field.", ".$yearpos.", ".$yearlen.") = '".$db->escape($yearcomp)."')";
1173  }
1174  }
1175  //print "sqlwhere=".$sqlwhere." yearcomp=".$yearcomp."<br>\n"; // sqlwhere and yearcomp defined only if we ask a reset
1176  //print "masktri=".$masktri." maskcounter=".$maskcounter." maskraz=".$maskraz." maskoffset=".$maskoffset."<br>\n";
1177 
1178  // Define $sqlstring
1179  if (function_exists('mb_strrpos')) {
1180  $posnumstart = mb_strrpos($maskwithnocode, $maskcounter, 0, 'UTF-8');
1181  } else {
1182  $posnumstart = strrpos($maskwithnocode, $maskcounter);
1183  } // Pos of counter in final string (from 0 to ...)
1184  if ($posnumstart < 0) {
1185  return 'ErrorBadMaskFailedToLocatePosOfSequence';
1186  }
1187  $sqlstring = "SUBSTRING(".$field.", ".($posnumstart + 1).", ".dol_strlen($maskcounter).")";
1188 
1189  // Define $maskLike
1190  $maskLike = dol_string_nospecial($mask);
1191  $maskLike = str_replace("%", "_", $maskLike);
1192 
1193  // Replace protected special codes with matching number of _ as wild card character
1194  $maskLike = preg_replace('/\{yyyy\}/i', '____', $maskLike);
1195  $maskLike = preg_replace('/\{yy\}/i', '__', $maskLike);
1196  $maskLike = preg_replace('/\{y\}/i', '_', $maskLike);
1197  $maskLike = preg_replace('/\{mm\}/i', '__', $maskLike);
1198  $maskLike = preg_replace('/\{dd\}/i', '__', $maskLike);
1199  // @phan-suppress-next-line PhanParamSuspiciousOrder
1200  $maskLike = str_replace(dol_string_nospecial('{'.$masktri.'}'), str_pad("", dol_strlen($maskcounter), "_"), $maskLike);
1201  if ($maskrefclient) {
1202  // @phan-suppress-next-line PhanParamSuspiciousOrder
1203  $maskLike = str_replace(dol_string_nospecial('{'.$maskrefclient.'}'), str_pad("", dol_strlen($maskrefclient), "_"), $maskLike);
1204  }
1205  if ($masktype) {
1206  $maskLike = str_replace(dol_string_nospecial('{'.$masktype.'}'), $masktype_value, $maskLike);
1207  }
1208  if ($maskuser) {
1209  $maskLike = str_replace(dol_string_nospecial('{'.$maskuser.'}'), $maskuser_value, $maskLike);
1210  }
1211  foreach ($maskperso as $key => $val) {
1212  $maskLike = str_replace(dol_string_nospecial($maskperso[$key]), $maskpersonew[$key], $maskLike);
1213  }
1214 
1215  // Get counter in database
1216  $counter = 0;
1217  $sql = "SELECT MAX(".$sqlstring.") as val";
1218  $sql .= " FROM ".MAIN_DB_PREFIX.$table;
1219  $sql .= " WHERE ".$field." LIKE '".$db->escape($maskLike) . (getDolGlobalString('SEARCH_FOR_NEXT_VAL_ON_START_ONLY') ? "%" : "") . "'";
1220  $sql .= " AND ".$field." NOT LIKE '(PROV%)'";
1221 
1222  // To ensure that all variables within the MAX() brackets are integers
1223  // This avoid bad detection of max when data are noised with non numeric values at the position of the numero
1224  if (getDolGlobalInt('MAIN_NUMBERING_FILTER_ON_INT_ONLY')) {
1225  // @phan-suppress-next-line PhanPluginSuspiciousParamPosition
1226  $sql .= " AND ". $db->regexpsql($sqlstring, '^[0-9]+$', 1);
1227  }
1228 
1229  if ($bentityon) { // only if entity enable
1230  $sql .= " AND entity IN (".getEntity($sharetable).")";
1231  } elseif (!empty($forceentity)) {
1232  $sql .= " AND entity IN (".$db->sanitize($forceentity).")";
1233  }
1234  if ($where) {
1235  $sql .= $where;
1236  }
1237  if ($sqlwhere) {
1238  $sql .= " AND ".$sqlwhere;
1239  }
1240 
1241  //print $sql.'<br>';
1242  dol_syslog("functions2::get_next_value mode=".$mode, LOG_DEBUG);
1243  $resql = $db->query($sql);
1244  if ($resql) {
1245  $obj = $db->fetch_object($resql);
1246  $counter = $obj->val;
1247  } else {
1248  dol_print_error($db);
1249  }
1250 
1251  // Check if we must force counter to maskoffset
1252  if (empty($counter)) {
1253  $counter = $maskoffset;
1254  } elseif (preg_match('/[^0-9]/i', $counter)) {
1255  dol_syslog("Error, the last counter found is '".$counter."' so is not a numeric value. We will restart to 1.", LOG_ERR);
1256  $counter = 0;
1257  } elseif ($counter < $maskoffset && !getDolGlobalString('MAIN_NUMBERING_OFFSET_ONLY_FOR_FIRST')) {
1258  $counter = $maskoffset;
1259  }
1260 
1261  if ($mode == 'last') { // We found value for counter = last counter value. Now need to get corresponding ref of invoice.
1262  $counterpadded = str_pad($counter, dol_strlen($maskcounter), "0", STR_PAD_LEFT);
1263 
1264  // Define $maskLike
1265  $maskLike = dol_string_nospecial($mask);
1266  $maskLike = str_replace("%", "_", $maskLike);
1267  // Replace protected special codes with matching number of _ as wild card character
1268  $maskLike = preg_replace('/\{yyyy\}/i', '____', $maskLike);
1269  $maskLike = preg_replace('/\{yy\}/i', '__', $maskLike);
1270  $maskLike = preg_replace('/\{y\}/i', '_', $maskLike);
1271  $maskLike = preg_replace('/\{mm\}/i', '__', $maskLike);
1272  $maskLike = preg_replace('/\{dd\}/i', '__', $maskLike);
1273  $maskLike = str_replace(dol_string_nospecial('{'.$masktri.'}'), $counterpadded, $maskLike);
1274  if ($maskrefclient) {
1275  // @phan-suppress-next-line PhanParamSuspiciousOrder
1276  $maskLike = str_replace(dol_string_nospecial('{'.$maskrefclient.'}'), str_pad("", dol_strlen($maskrefclient), "_"), $maskLike);
1277  }
1278  if ($masktype) {
1279  $maskLike = str_replace(dol_string_nospecial('{'.$masktype.'}'), $masktype_value, $maskLike);
1280  }
1281  if ($maskuser) {
1282  $maskLike = str_replace(dol_string_nospecial('{'.$maskuser.'}'), $maskuser_value, $maskLike);
1283  }
1284 
1285  $ref = '';
1286  $sql = "SELECT ".$field." as ref";
1287  $sql .= " FROM ".MAIN_DB_PREFIX.$table;
1288  $sql .= " WHERE ".$field." LIKE '".$db->escape($maskLike) . (getDolGlobalString('SEARCH_FOR_NEXT_VAL_ON_START_ONLY') ? "%" : "") . "'";
1289  $sql .= " AND ".$field." NOT LIKE '%PROV%'";
1290  if ($bentityon) { // only if entity enable
1291  $sql .= " AND entity IN (".getEntity($sharetable).")";
1292  } elseif (!empty($forceentity)) {
1293  $sql .= " AND entity IN (".$db->sanitize($forceentity).")";
1294  }
1295  if ($where) {
1296  $sql .= $where;
1297  }
1298  if ($sqlwhere) {
1299  $sql .= " AND ".$sqlwhere;
1300  }
1301 
1302  dol_syslog("functions2::get_next_value mode=".$mode, LOG_DEBUG);
1303  $resql = $db->query($sql);
1304  if ($resql) {
1305  $obj = $db->fetch_object($resql);
1306  if ($obj) {
1307  $ref = $obj->ref;
1308  }
1309  } else {
1310  dol_print_error($db);
1311  }
1312 
1313  $numFinal = $ref;
1314  } elseif ($mode == 'next') {
1315  $counter++;
1316  $maskrefclient_counter = 0;
1317 
1318  // If value for $counter has a length higher than $maskcounter chars
1319  if ($counter >= pow(10, dol_strlen($maskcounter))) {
1320  $counter = 'ErrorMaxNumberReachForThisMask';
1321  }
1322 
1323  if (!empty($maskrefclient_maskcounter)) {
1324  //print "maskrefclient_maskcounter=".$maskrefclient_maskcounter." maskwithnocode=".$maskwithnocode." maskrefclient=".$maskrefclient."\n<br>";
1325 
1326  // Define $sqlstring
1327  $maskrefclient_posnumstart = strpos($maskwithnocode, $maskrefclient_maskcounter, strpos($maskwithnocode, $maskrefclient)); // Pos of counter in final string (from 0 to ...)
1328  if ($maskrefclient_posnumstart <= 0) {
1329  return 'ErrorBadMask';
1330  }
1331  $maskrefclient_sqlstring = 'SUBSTRING('.$field.', '.($maskrefclient_posnumstart + 1).', '.dol_strlen($maskrefclient_maskcounter).')';
1332  //print "x".$sqlstring;
1333 
1334  // Define $maskrefclient_maskLike
1335  $maskrefclient_maskLike = dol_string_nospecial($mask);
1336  $maskrefclient_maskLike = str_replace("%", "_", $maskrefclient_maskLike);
1337  // Replace protected special codes with matching number of _ as wild card character
1338  $maskrefclient_maskLike = str_replace(dol_string_nospecial('{yyyy}'), '____', $maskrefclient_maskLike);
1339  $maskrefclient_maskLike = str_replace(dol_string_nospecial('{yy}'), '__', $maskrefclient_maskLike);
1340  $maskrefclient_maskLike = str_replace(dol_string_nospecial('{y}'), '_', $maskrefclient_maskLike);
1341  $maskrefclient_maskLike = str_replace(dol_string_nospecial('{mm}'), '__', $maskrefclient_maskLike);
1342  $maskrefclient_maskLike = str_replace(dol_string_nospecial('{dd}'), '__', $maskrefclient_maskLike);
1343  // @phan-suppress-next-line PhanParamSuspiciousOrder
1344  $maskrefclient_maskLike = str_replace(dol_string_nospecial('{'.$masktri.'}'), str_pad("", dol_strlen($maskcounter), "_"), $maskrefclient_maskLike);
1345  // @phan-suppress-next-line PhanParamSuspiciousOrder
1346  $maskrefclient_maskLike = str_replace(dol_string_nospecial('{'.$maskrefclient.'}'), $maskrefclient_clientcode.str_pad("", dol_strlen($maskrefclient_maskcounter), "_"), $maskrefclient_maskLike);
1347 
1348  // Get counter in database
1349  $maskrefclient_sql = "SELECT MAX(".$maskrefclient_sqlstring.") as val";
1350  $maskrefclient_sql .= " FROM ".MAIN_DB_PREFIX.$table;
1351  //$sql.= " WHERE ".$field." not like '(%'";
1352  $maskrefclient_sql .= " WHERE ".$field." LIKE '".$db->escape($maskrefclient_maskLike) . (getDolGlobalString('SEARCH_FOR_NEXT_VAL_ON_START_ONLY') ? "%" : "") . "'";
1353  if ($bentityon) { // only if entity enable
1354  $maskrefclient_sql .= " AND entity IN (".getEntity($sharetable).")";
1355  } elseif (!empty($forceentity)) {
1356  $sql .= " AND entity IN (".$db->sanitize($forceentity).")";
1357  }
1358  if ($where) {
1359  $maskrefclient_sql .= $where; //use the same optional where as general mask
1360  }
1361  if ($sqlwhere) {
1362  $maskrefclient_sql .= ' AND '.$sqlwhere; //use the same sqlwhere as general mask
1363  }
1364  $maskrefclient_sql .= " AND (SUBSTRING(".$field.", ".(strpos($maskwithnocode, $maskrefclient) + 1).", ".dol_strlen($maskrefclient_maskclientcode).") = '".$db->escape($maskrefclient_clientcode)."')";
1365 
1366  dol_syslog("functions2::get_next_value maskrefclient", LOG_DEBUG);
1367  $maskrefclient_resql = $db->query($maskrefclient_sql);
1368  if ($maskrefclient_resql) {
1369  $maskrefclient_obj = $db->fetch_object($maskrefclient_resql);
1370  $maskrefclient_counter = $maskrefclient_obj->val;
1371  } else {
1372  dol_print_error($db);
1373  }
1374 
1375  if (empty($maskrefclient_counter) || preg_match('/[^0-9]/i', $maskrefclient_counter)) {
1376  $maskrefclient_counter = $maskrefclient_maskoffset;
1377  }
1378  $maskrefclient_counter++;
1379  }
1380 
1381  // Build numFinal
1382  $numFinal = $mask;
1383 
1384  // We replace special codes except refclient
1385  if (!empty($yearoffsettype) && !is_numeric($yearoffsettype) && $yearoffsettype != '=') { // yearoffsettype is - or +, so we don't want current year
1386  $numFinal = preg_replace('/\{yyyy\}/i', (string) ((int) date("Y", $date) + $yearoffset), $numFinal);
1387  $numFinal = preg_replace('/\{yy\}/i', (string) ((int) date("y", $date) + $yearoffset), $numFinal);
1388  $numFinal = preg_replace('/\{y\}/i', (string) ((int) substr((string) date("y", $date), 1, 1) + $yearoffset), $numFinal);
1389  } else { // we want yyyy to be current year
1390  $numFinal = preg_replace('/\{yyyy\}/i', date("Y", $date), $numFinal);
1391  $numFinal = preg_replace('/\{yy\}/i', date("y", $date), $numFinal);
1392  $numFinal = preg_replace('/\{y\}/i', substr(date("y", $date), 1, 1), $numFinal);
1393  }
1394  $numFinal = preg_replace('/\{mm\}/i', date("m", $date), $numFinal);
1395  $numFinal = preg_replace('/\{dd\}/i', date("d", $date), $numFinal);
1396 
1397  // Now we replace the counter
1398  $maskbefore = '{'.$masktri.'}';
1399  $maskafter = str_pad($counter, dol_strlen($maskcounter), "0", STR_PAD_LEFT);
1400  //print 'x'.$numFinal.' - '.$maskbefore.' - '.$maskafter.'y';exit;
1401  $numFinal = str_replace($maskbefore, $maskafter, $numFinal);
1402 
1403  // Now we replace the refclient
1404  if ($maskrefclient) {
1405  //print "maskrefclient=".$maskrefclient." maskrefclient_counter=".$maskrefclient_counter." maskwithonlyymcode=".$maskwithonlyymcode." maskwithnocode=".$maskwithnocode." maskrefclient_clientcode=".$maskrefclient_clientcode." maskrefclient_maskcounter=".$maskrefclient_maskcounter."\n<br>";exit;
1406  $maskrefclient_maskbefore = '{'.$maskrefclient.'}';
1407  $maskrefclient_maskafter = $maskrefclient_clientcode;
1408  if (dol_strlen($maskrefclient_maskcounter) > 0) {
1409  $maskrefclient_maskafter .= str_pad($maskrefclient_counter, dol_strlen($maskrefclient_maskcounter), "0", STR_PAD_LEFT);
1410  }
1411  $numFinal = str_replace($maskrefclient_maskbefore, $maskrefclient_maskafter, $numFinal);
1412  }
1413 
1414  // Now we replace the type
1415  if ($masktype) {
1416  $masktype_maskbefore = '{'.$masktype.'}';
1417  $masktype_maskafter = $masktype_value;
1418  $numFinal = str_replace($masktype_maskbefore, $masktype_maskafter, $numFinal);
1419  }
1420 
1421  // Now we replace the user
1422  if ($maskuser) {
1423  $maskuser_maskbefore = '{'.$maskuser.'}';
1424  $maskuser_maskafter = $maskuser_value;
1425  $numFinal = str_replace($maskuser_maskbefore, $maskuser_maskafter, $numFinal);
1426  }
1427  } else {
1428  $numFinal = "ErrorBadMode";
1429  dol_syslog("functions2::get_next_value ErrorBadMode '$mode'", LOG_ERR);
1430  }
1431 
1432  dol_syslog("functions2::get_next_value return ".$numFinal, LOG_DEBUG);
1433  return $numFinal;
1434 }
1435 
1447 function get_string_between($string, $start, $end)
1448 {
1449  $ini = strpos($string, $start);
1450  if ($ini === false) {
1451  return '';
1452  }
1453  $ini += strlen($start);
1454  $endpos = strpos($string, $end, $ini);
1455  if ($endpos === false) {
1456  return '';
1457  }
1458  return substr($string, $ini, $endpos - $ini);
1459 }
1460 
1468 function check_value($mask, $value)
1469 {
1470  $result = 0;
1471 
1472  $hasglobalcounter = false;
1473  // Extract value for mask counter, mask raz and mask offset
1474  $reg = array();
1475  if (preg_match('/\{(0+)([@\+][0-9]+)?([@\+][0-9]+)?\}/i', $mask, $reg)) {
1476  $masktri = $reg[1].(isset($reg[2]) ? $reg[2] : '').(isset($reg[3]) ? $reg[3] : '');
1477  $maskcounter = $reg[1];
1478  $hasglobalcounter = true;
1479  } else {
1480  // setting some defaults so the rest of the code won't fail if there is a third party counter
1481  $masktri = '00000';
1482  $maskcounter = '00000';
1483  }
1484  $maskraz = -1;
1485  $maskoffset = 0;
1486  if (dol_strlen($maskcounter) < 3) {
1487  return 'ErrorCounterMustHaveMoreThan3Digits';
1488  }
1489 
1490  // Extract value for third party mask counter
1491  $regClientRef = array();
1492  if (preg_match('/\{(c+)(0*)\}/i', $mask, $regClientRef)) {
1493  $maskrefclient = $regClientRef[1].$regClientRef[2];
1494  $maskrefclient_maskclientcode = $regClientRef[1];
1495  $maskrefclient_maskcounter = $regClientRef[2];
1496  $maskrefclient_maskoffset = 0; //default value of maskrefclient_counter offset
1497  $maskrefclient_clientcode = substr('', 0, dol_strlen($maskrefclient_maskclientcode)); //get n first characters of client code to form maskrefclient_clientcode
1498  $maskrefclient_clientcode = str_pad($maskrefclient_clientcode, dol_strlen($maskrefclient_maskclientcode), "#", STR_PAD_RIGHT); //padding maskrefclient_clientcode for having exactly n characters in maskrefclient_clientcode
1499  $maskrefclient_clientcode = dol_string_nospecial($maskrefclient_clientcode); //sanitize maskrefclient_clientcode for sql insert and sql select like
1500  if (dol_strlen($maskrefclient_maskcounter) > 0 && dol_strlen($maskrefclient_maskcounter) < 3) {
1501  return 'ErrorCounterMustHaveMoreThan3Digits';
1502  }
1503  } else {
1504  $maskrefclient = '';
1505  }
1506 
1507  // fail if there is neither a global nor a third party counter
1508  if (!$hasglobalcounter && ($maskrefclient_maskcounter == '')) {
1509  return 'ErrorBadMask';
1510  }
1511 
1512  $maskwithonlyymcode = $mask;
1513  $maskwithonlyymcode = preg_replace('/\{(0+)([@\+][0-9]+)?([@\+][0-9]+)?\}/i', $maskcounter, $maskwithonlyymcode);
1514  $maskwithonlyymcode = preg_replace('/\{dd\}/i', 'dd', $maskwithonlyymcode);
1515  $maskwithonlyymcode = preg_replace('/\{(c+)(0*)\}/i', $maskrefclient, $maskwithonlyymcode);
1516  $maskwithnocode = $maskwithonlyymcode;
1517  $maskwithnocode = preg_replace('/\{yyyy\}/i', 'yyyy', $maskwithnocode);
1518  $maskwithnocode = preg_replace('/\{yy\}/i', 'yy', $maskwithnocode);
1519  $maskwithnocode = preg_replace('/\{y\}/i', 'y', $maskwithnocode);
1520  $maskwithnocode = preg_replace('/\{mm\}/i', 'mm', $maskwithnocode);
1521  // Now maskwithnocode = 0000ddmmyyyyccc for example
1522  // and maskcounter = 0000 for example
1523  //print "maskwithonlyymcode=".$maskwithonlyymcode." maskwithnocode=".$maskwithnocode."\n<br>";
1524 
1525  // If an offset is asked
1526  if (!empty($reg[2]) && preg_match('/^\+/', $reg[2])) {
1527  $maskoffset = preg_replace('/^\+/', '', $reg[2]);
1528  }
1529  if (!empty($reg[3]) && preg_match('/^\+/', $reg[3])) {
1530  $maskoffset = preg_replace('/^\+/', '', $reg[3]);
1531  }
1532 
1533  // Define $sqlwhere
1534 
1535  // If a restore to zero after a month is asked we check if there is already a value for this year.
1536  if (!empty($reg[2]) && preg_match('/^@/', $reg[2])) {
1537  $maskraz = preg_replace('/^@/', '', $reg[2]);
1538  }
1539  if (!empty($reg[3]) && preg_match('/^@/', $reg[3])) {
1540  $maskraz = preg_replace('/^@/', '', $reg[3]);
1541  }
1542  if ($maskraz >= 0) {
1543  if ($maskraz == 99) {
1544  $maskraz = (int) date('m');
1545  $resetEveryMonth = true;
1546  }
1547  if ($maskraz > 12) {
1548  return 'ErrorBadMaskBadRazMonth';
1549  }
1550 
1551  // Define reg
1552  if ($maskraz > 1 && !preg_match('/^(.*)\{(y+)\}\{(m+)\}/i', $maskwithonlyymcode, $reg)) {
1553  return 'ErrorCantUseRazInStartedYearIfNoYearMonthInMask';
1554  }
1555  if ($maskraz <= 1 && !preg_match('/^(.*)\{(y+)\}/i', $maskwithonlyymcode, $reg)) {
1556  return 'ErrorCantUseRazIfNoYearInMask';
1557  }
1558  //print "x".$maskwithonlyymcode." ".$maskraz;
1559  }
1560  //print "masktri=".$masktri." maskcounter=".$maskcounter." maskwithonlyymcode=".$maskwithonlyymcode." maskwithnocode=".$maskwithnocode." maskraz=".$maskraz." maskoffset=".$maskoffset."<br>\n";
1561 
1562  if (function_exists('mb_strrpos')) {
1563  $posnumstart = mb_strrpos($maskwithnocode, $maskcounter, 0, 'UTF-8');
1564  } else {
1565  $posnumstart = strrpos($maskwithnocode, $maskcounter);
1566  } // Pos of counter in final string (from 0 to ...)
1567  if ($posnumstart < 0) {
1568  return 'ErrorBadMaskFailedToLocatePosOfSequence';
1569  }
1570 
1571  // Check we have a number in $value at position ($posnumstart+1).', '.dol_strlen($maskcounter)
1572  // TODO
1573 
1574  // Check length
1575  $len = dol_strlen($maskwithnocode);
1576  if (dol_strlen($value) != $len) {
1577  $result = -1;
1578  }
1579 
1580  dol_syslog("functions2::check_value result=".$result, LOG_DEBUG);
1581  return $result;
1582 }
1583 
1592 function binhex($bin, $pad = false, $upper = false)
1593 {
1594  $last = dol_strlen($bin) - 1;
1595  $x = 0;
1596  for ($i = 0; $i <= $last; $i++) {
1597  $x += ($bin[$last - $i] ? 1 : 0) << $i;
1598  }
1599  $x = dechex($x);
1600  if ($pad) {
1601  while (dol_strlen($x) < intval(dol_strlen($bin)) / 4) {
1602  $x = "0$x";
1603  }
1604  }
1605  if ($upper) {
1606  $x = strtoupper($x);
1607  }
1608  return $x;
1609 }
1610 
1617 function hexbin($hexa)
1618 {
1619  $bin = '';
1620  $strLength = dol_strlen($hexa);
1621  for ($i = 0; $i < $strLength; $i++) {
1622  $bin .= str_pad(decbin(hexdec($hexa[$i])), 4, '0', STR_PAD_LEFT);
1623  }
1624  return $bin;
1625 }
1626 
1633 function numero_semaine($time)
1634 {
1635  $stime = dol_print_date($time, '%Y-%m-%d');
1636 
1637  if (preg_match('/^([0-9]+)\-([0-9]+)\-([0-9]+)\s?([0-9]+)?:?([0-9]+)?/i', $stime, $reg)) {
1638  // Date est au format 'YYYY-MM-DD' ou 'YYYY-MM-DD HH:MM:SS'
1639  $annee = (int) $reg[1];
1640  $mois = (int) $reg[2];
1641  $jour = (int) $reg[3];
1642  }
1643 
1644  /*
1645  * Norme ISO-8601:
1646  * - Week 1 of the year contains Jan 4th, or contains the first Thursday of January.
1647  * - Most years have 52 weeks, but 53 weeks for years starting on a Thursday and bisectile years that start on a Wednesday.
1648  * - The first day of a week is Monday
1649  */
1650 
1651  // Definition du Jeudi de la semaine
1652  if ((int) date("w", mktime(12, 0, 0, $mois, $jour, $annee)) == 0) { // Dimanche
1653  $jeudiSemaine = mktime(12, 0, 0, $mois, $jour, $annee) - 3 * 24 * 60 * 60;
1654  } elseif (date("w", mktime(12, 0, 0, $mois, $jour, $annee)) < 4) { // du Lundi au Mercredi
1655  $jeudiSemaine = mktime(12, 0, 0, $mois, $jour, $annee) + (4 - (int) date("w", mktime(12, 0, 0, $mois, $jour, $annee))) * 24 * 60 * 60;
1656  } elseif ((int) date("w", mktime(12, 0, 0, $mois, $jour, $annee)) > 4) { // du Vendredi au Samedi
1657  $jeudiSemaine = mktime(12, 0, 0, $mois, $jour, $annee) - ((int) date("w", mktime(12, 0, 0, $mois, $jour, $annee)) - 4) * 24 * 60 * 60;
1658  } else { // Jeudi
1659  $jeudiSemaine = mktime(12, 0, 0, $mois, $jour, $annee);
1660  }
1661 
1662  // Definition du premier Jeudi de l'annee
1663  if ((int) date("w", mktime(12, 0, 0, 1, 1, (int) date("Y", $jeudiSemaine))) == 0) { // Dimanche
1664  $premierJeudiAnnee = mktime(12, 0, 0, 1, 1, (int) date("Y", $jeudiSemaine)) + 4 * 24 * 60 * 60;
1665  } elseif ((int) date("w", mktime(12, 0, 0, 1, 1, (int) date("Y", $jeudiSemaine))) < 4) { // du Lundi au Mercredi
1666  $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;
1667  } elseif ((int) date("w", mktime(12, 0, 0, 1, 1, (int) date("Y", $jeudiSemaine))) > 4) { // du Vendredi au Samedi
1668  $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;
1669  } else { // Jeudi
1670  $premierJeudiAnnee = mktime(12, 0, 0, 1, 1, (int) date("Y", $jeudiSemaine));
1671  }
1672 
1673  // Definition du numero de semaine: nb de jours entre "premier Jeudi de l'annee" et "Jeudi de la semaine";
1674  $numeroSemaine = (
1675  (
1676  (int) date("z", mktime(12, 0, 0, (int) date("m", $jeudiSemaine), (int) date("d", $jeudiSemaine), (int) date("Y", $jeudiSemaine)))
1677  -
1678  (int) date("z", mktime(12, 0, 0, (int) date("m", $premierJeudiAnnee), (int) date("d", $premierJeudiAnnee), (int) date("Y", $premierJeudiAnnee)))
1679  ) / 7
1680  ) + 1;
1681 
1682  // Cas particulier de la semaine 53
1683  if ($numeroSemaine == 53) {
1684  // Les annees qui commencent un Jeudi et les annees bissextiles commencant un Mercredi en possedent 53
1685  if (
1686  ((int) date("w", mktime(12, 0, 0, 1, 1, (int) date("Y", $jeudiSemaine))) == 4)
1687  || (
1688  ((int) date("w", mktime(12, 0, 0, 1, 1, (int) date("Y", $jeudiSemaine))) == 3)
1689  && ((int) date("z", mktime(12, 0, 0, 12, 31, (int) date("Y", $jeudiSemaine))) == 365)
1690  )
1691  ) {
1692  $numeroSemaine = 53;
1693  } else {
1694  $numeroSemaine = 1;
1695  }
1696  }
1697 
1698  //echo $jour."-".$mois."-".$annee." (".date("d-m-Y",$premierJeudiAnnee)." - ".date("d-m-Y",$jeudiSemaine).") -> ".$numeroSemaine."<BR>";
1699 
1700  return sprintf("%02d", $numeroSemaine);
1701 }
1702 
1711 function weight_convert($weight, &$from_unit, $to_unit)
1712 {
1713  /* Pour convertire 320 gr en Kg appeler
1714  * $f = -3
1715  * weigh_convert(320, $f, 0) retournera 0.32
1716  *
1717  */
1718  $weight = is_numeric($weight) ? $weight : 0;
1719  while ($from_unit != $to_unit) {
1720  if ($from_unit > $to_unit) {
1721  $weight = $weight * 10;
1722  $from_unit = $from_unit - 1;
1723  $weight = weight_convert($weight, $from_unit, $to_unit);
1724  }
1725  if ($from_unit < $to_unit) {
1726  $weight = $weight / 10;
1727  $from_unit = $from_unit + 1;
1728  $weight = weight_convert($weight, $from_unit, $to_unit);
1729  }
1730  }
1731 
1732  return $weight;
1733 }
1734 
1746 function dol_set_user_param($db, $conf, &$user, $tab)
1747 {
1748  // Verification parameters
1749  if (count($tab) < 1) {
1750  return -1;
1751  }
1752 
1753  $db->begin();
1754 
1755  // We remove old parameters for all keys in $tab
1756  $sql = "DELETE FROM ".MAIN_DB_PREFIX."user_param";
1757  $sql .= " WHERE fk_user = ".((int) $user->id);
1758  $sql .= " AND entity = ".((int) $conf->entity);
1759  $sql .= " AND param in (";
1760  $i = 0;
1761  foreach ($tab as $key => $value) {
1762  if ($i > 0) {
1763  $sql .= ',';
1764  }
1765  $sql .= "'".$db->escape($key)."'";
1766  $i++;
1767  }
1768  $sql .= ")";
1769  dol_syslog("functions2.lib::dol_set_user_param", LOG_DEBUG);
1770 
1771  $resql = $db->query($sql);
1772  if (!$resql) {
1773  dol_print_error($db);
1774  $db->rollback();
1775  return -1;
1776  }
1777 
1778  foreach ($tab as $key => $value) {
1779  // Set new parameters
1780  if ($value) {
1781  $sql = "INSERT INTO ".MAIN_DB_PREFIX."user_param(fk_user,entity,param,value)";
1782  $sql .= " VALUES (".((int) $user->id).",".((int) $conf->entity).",";
1783  $sql .= " '".$db->escape($key)."','".$db->escape($value)."')";
1784 
1785  dol_syslog("functions2.lib::dol_set_user_param", LOG_DEBUG);
1786  $result = $db->query($sql);
1787  if (!$result) {
1788  dol_print_error($db);
1789  $db->rollback();
1790  return -1;
1791  }
1792  $user->conf->$key = $value;
1793  //print "key=".$key." user->conf->key=".$user->conf->$key;
1794  } else {
1795  unset($user->conf->$key);
1796  }
1797  }
1798 
1799  $db->commit();
1800  return 1;
1801 }
1802 
1810 function dol_print_reduction($reduction, $langs)
1811 {
1812  $string = '';
1813  if ($reduction == 100) {
1814  $string = $langs->transnoentities("Offered");
1815  } else {
1816  $string = vatrate($reduction, true);
1817  }
1818 
1819  return $string;
1820 }
1821 
1829 function version_os($option = '')
1830 {
1831  if ($option == 'smr') {
1832  $osversion = php_uname('s').' '.php_uname('m').' '.php_uname('r');
1833  } else {
1834  $osversion = php_uname();
1835  }
1836  return $osversion;
1837 }
1838 
1845 function version_php()
1846 {
1847  return phpversion();
1848 }
1849 
1855 function version_db()
1856 {
1857  global $db;
1858  if (is_object($db) && method_exists($db, 'getVersion')) {
1859  return $db->getVersion();
1860  }
1861  return '';
1862 }
1863 
1871 {
1872  return DOL_VERSION;
1873 }
1874 
1881 {
1882  return $_SERVER["SERVER_SOFTWARE"];
1883 }
1884 
1893 function getListOfModels($db, $type, $maxfilenamelength = 0)
1894 {
1895  global $conf, $langs;
1896  $liste = array();
1897  $found = 0;
1898  $dirtoscan = '';
1899 
1900  $sql = "SELECT nom as id, nom as doc_template_name, libelle as label, description as description";
1901  $sql .= " FROM ".MAIN_DB_PREFIX."document_model";
1902  $sql .= " WHERE type = '".$db->escape($type)."'";
1903  $sql .= " AND entity IN (0,".$conf->entity.")";
1904  $sql .= " ORDER BY description DESC";
1905 
1906  dol_syslog('/core/lib/function2.lib.php::getListOfModels', LOG_DEBUG);
1907  $resql_models = $db->query($sql);
1908  if ($resql_models) {
1909  $num = $db->num_rows($resql_models);
1910  $i = 0;
1911  while ($i < $num) {
1912  $found = 1;
1913 
1914  $obj = $db->fetch_object($resql_models);
1915 
1916  // If this generation module needs to scan a directory, then description field is filled
1917  // with the constant that contains list of directories to scan (COMPANY_ADDON_PDF_ODT_PATH, ...).
1918  if (!empty($obj->description)) { // A list of directories to scan is defined
1919  include_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
1920 
1921  $const = $obj->description;
1922  $dirtoscan = preg_replace('/[\r\n]+/', ',', trim(getDolGlobalString($const)));
1923 
1924  $listoffiles = array();
1925 
1926  // Now we add models found in directories scanned
1927  $listofdir = explode(',', $dirtoscan);
1928  foreach ($listofdir as $key => $tmpdir) {
1929  $tmpdir = trim($tmpdir);
1930  $tmpdir = preg_replace('/DOL_DATA_ROOT/', DOL_DATA_ROOT, $tmpdir);
1931  if (!$tmpdir) {
1932  unset($listofdir[$key]);
1933  continue;
1934  }
1935  if (is_dir($tmpdir)) {
1936  // all type of template is allowed
1937  $tmpfiles = dol_dir_list($tmpdir, 'files', 0, '', null, 'name', SORT_ASC, 0);
1938  if (count($tmpfiles)) {
1939  $listoffiles = array_merge($listoffiles, $tmpfiles);
1940  }
1941  }
1942  }
1943 
1944  if (count($listoffiles)) {
1945  foreach ($listoffiles as $record) {
1946  $max = ($maxfilenamelength ? $maxfilenamelength : 28);
1947  $liste[$obj->id.':'.$record['fullname']] = dol_trunc($record['name'], $max, 'middle');
1948  }
1949  } else {
1950  $liste[0] = $obj->label.': '.$langs->trans("None");
1951  }
1952  } else {
1953  if ($type == 'member' && $obj->doc_template_name == 'standard') { // Special case, if member template, we add variant per format
1954  global $_Avery_Labels;
1955  include_once DOL_DOCUMENT_ROOT.'/core/lib/format_cards.lib.php';
1956  foreach ($_Avery_Labels as $key => $val) {
1957  $liste[$obj->id.':'.$key] = ($obj->label ? $obj->label : $obj->doc_template_name).' '.$val['name'];
1958  }
1959  } else {
1960  // Common usage
1961  $liste[$obj->id] = $obj->label ? $obj->label : $obj->doc_template_name;
1962  }
1963  }
1964  $i++;
1965  }
1966  } else {
1967  dol_print_error($db);
1968  return -1;
1969  }
1970 
1971  if ($found) {
1972  return $liste;
1973  } else {
1974  return 0;
1975  }
1976 }
1977 
1985 function is_ip($ip)
1986 {
1987  // First we test if it is a valid IPv4
1988  if (filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4)) {
1989  // Then we test if it is a private range
1990  if (!filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_NO_PRIV_RANGE)) {
1991  return 2;
1992  }
1993 
1994  // Then we test if it is a reserved range
1995  if (!filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_NO_RES_RANGE)) {
1996  return 0;
1997  }
1998 
1999  return 1;
2000  }
2001 
2002  return 0;
2003 }
2004 
2012 function dol_buildlogin($lastname, $firstname)
2013 {
2014  //$conf->global->MAIN_BUILD_LOGIN_RULE = 'f.lastname';
2015  $charforseparator = getDolGlobalString("MAIN_USER_SEPARATOR_CHAR_FOR_GENERATED_LOGIN", '.');
2016  if ($charforseparator == 'none') {
2017  $charforseparator = '';
2018  }
2019 
2020  if (getDolGlobalString('MAIN_BUILD_LOGIN_RULE') == 'f.lastname') { // f.lastname
2021  $login = strtolower(dol_string_unaccent(dol_trunc($firstname, 1, 'right', 'UTF-8', 1)));
2022  $login .= ($login ? $charforseparator : '');
2023  $login .= strtolower(dol_string_unaccent($lastname));
2024  $login = dol_string_nospecial($login, ''); // For special names
2025  } else { // firstname.lastname
2026  $login = strtolower(dol_string_unaccent($firstname));
2027  $login .= ($login ? $charforseparator : '');
2028  $login .= strtolower(dol_string_unaccent($lastname));
2029  $login = dol_string_nospecial($login, ''); // For special names
2030  }
2031 
2032  // TODO Add a hook to allow external modules to suggest new rules
2033 
2034  return $login;
2035 }
2036 
2042 function getSoapParams()
2043 {
2044  global $conf;
2045 
2046  $params = array();
2047  $proxyuse = (!getDolGlobalString('MAIN_PROXY_USE') ? false : true);
2048  $proxyhost = (!getDolGlobalString('MAIN_PROXY_USE') ? false : $conf->global->MAIN_PROXY_HOST);
2049  $proxyport = (!getDolGlobalString('MAIN_PROXY_USE') ? false : $conf->global->MAIN_PROXY_PORT);
2050  $proxyuser = (!getDolGlobalString('MAIN_PROXY_USE') ? false : $conf->global->MAIN_PROXY_USER);
2051  $proxypass = (!getDolGlobalString('MAIN_PROXY_USE') ? false : $conf->global->MAIN_PROXY_PASS);
2052  $timeout = (!getDolGlobalString('MAIN_USE_CONNECT_TIMEOUT') ? 10 : $conf->global->MAIN_USE_CONNECT_TIMEOUT); // Connection timeout
2053  $response_timeout = (!getDolGlobalString('MAIN_USE_RESPONSE_TIMEOUT') ? 30 : $conf->global->MAIN_USE_RESPONSE_TIMEOUT); // Response timeout
2054  //print extension_loaded('soap');
2055  if ($proxyuse) {
2056  $params = array('connection_timeout' => $timeout,
2057  'response_timeout' => $response_timeout,
2058  'proxy_use' => 1,
2059  'proxy_host' => $proxyhost,
2060  'proxy_port' => $proxyport,
2061  'proxy_login' => $proxyuser,
2062  'proxy_password' => $proxypass,
2063  'trace' => 1
2064  );
2065  } else {
2066  $params = array('connection_timeout' => $timeout,
2067  'response_timeout' => $response_timeout,
2068  'proxy_use' => 0,
2069  'proxy_host' => false,
2070  'proxy_port' => false,
2071  'proxy_login' => false,
2072  'proxy_password' => false,
2073  'trace' => 1
2074  );
2075  }
2076  return $params;
2077 }
2078 
2079 
2089 function dolGetElementUrl($objectid, $objecttype, $withpicto = 0, $option = '')
2090 {
2091  global $db, $conf, $langs;
2092 
2093  $ret = '';
2094  $regs = array();
2095 
2096  // If we ask a resource form external module (instead of default path)
2097  if (preg_match('/^([^@]+)@([^@]+)$/i', $objecttype, $regs)) {
2098  $myobject = $regs[1];
2099  $module = $regs[2];
2100  } else {
2101  // Parse $objecttype (ex: project_task)
2102  $module = $myobject = $objecttype;
2103  if (preg_match('/^([^_]+)_([^_]+)/i', $objecttype, $regs)) {
2104  $module = $regs[1];
2105  $myobject = $regs[2];
2106  }
2107  }
2108 
2109  // Generic case for $classpath
2110  $classpath = $module.'/class';
2111 
2112  // Special cases, to work with non standard path
2113  if ($objecttype == 'facture' || $objecttype == 'invoice') {
2114  $langs->load('bills');
2115  $classpath = 'compta/facture/class';
2116  $module = 'facture';
2117  $myobject = 'facture';
2118  } elseif ($objecttype == 'commande' || $objecttype == 'order') {
2119  $langs->load('orders');
2120  $classpath = 'commande/class';
2121  $module = 'commande';
2122  $myobject = 'commande';
2123  } elseif ($objecttype == 'propal') {
2124  $langs->load('propal');
2125  $classpath = 'comm/propal/class';
2126  } elseif ($objecttype == 'supplier_proposal') {
2127  $langs->load('supplier_proposal');
2128  $classpath = 'supplier_proposal/class';
2129  } elseif ($objecttype == 'shipping') {
2130  $langs->load('sendings');
2131  $classpath = 'expedition/class';
2132  $myobject = 'expedition';
2133  $module = 'expedition_bon';
2134  } elseif ($objecttype == 'delivery') {
2135  $langs->load('deliveries');
2136  $classpath = 'delivery/class';
2137  $myobject = 'delivery';
2138  $module = 'delivery_note';
2139  } elseif ($objecttype == 'contract') {
2140  $langs->load('contracts');
2141  $classpath = 'contrat/class';
2142  $module = 'contrat';
2143  $myobject = 'contrat';
2144  } elseif ($objecttype == 'member') {
2145  $langs->load('members');
2146  $classpath = 'adherents/class';
2147  $module = 'adherent';
2148  $myobject = 'adherent';
2149  } elseif ($objecttype == 'cabinetmed_cons') {
2150  $classpath = 'cabinetmed/class';
2151  $module = 'cabinetmed';
2152  $myobject = 'cabinetmedcons';
2153  } elseif ($objecttype == 'fichinter') {
2154  $langs->load('interventions');
2155  $classpath = 'fichinter/class';
2156  $module = 'ficheinter';
2157  $myobject = 'fichinter';
2158  } elseif ($objecttype == 'project') {
2159  $langs->load('projects');
2160  $classpath = 'projet/class';
2161  $module = 'projet';
2162  } elseif ($objecttype == 'task') {
2163  $langs->load('projects');
2164  $classpath = 'projet/class';
2165  $module = 'projet';
2166  $myobject = 'task';
2167  } elseif ($objecttype == 'stock') {
2168  $classpath = 'product/stock/class';
2169  $module = 'stock';
2170  $myobject = 'stock';
2171  } elseif ($objecttype == 'inventory') {
2172  $classpath = 'product/inventory/class';
2173  $module = 'stock';
2174  $myobject = 'inventory';
2175  } elseif ($objecttype == 'mo') {
2176  $classpath = 'mrp/class';
2177  $module = 'mrp';
2178  $myobject = 'mo';
2179  } elseif ($objecttype == 'productlot') {
2180  $classpath = 'product/stock/class';
2181  $module = 'stock';
2182  $myobject = 'productlot';
2183  }
2184 
2185  // Generic case for $classfile and $classname
2186  $classfile = strtolower($myobject);
2187  $classname = ucfirst($myobject);
2188  //print "objecttype=".$objecttype." module=".$module." subelement=".$subelement." classfile=".$classfile." classname=".$classname." classpath=".$classpath;
2189 
2190  if ($objecttype == 'invoice_supplier') {
2191  $classfile = 'fournisseur.facture';
2192  $classname = 'FactureFournisseur';
2193  $classpath = 'fourn/class';
2194  $module = 'fournisseur';
2195  } elseif ($objecttype == 'order_supplier') {
2196  $classfile = 'fournisseur.commande';
2197  $classname = 'CommandeFournisseur';
2198  $classpath = 'fourn/class';
2199  $module = 'fournisseur';
2200  } elseif ($objecttype == 'supplier_proposal') {
2201  $classfile = 'supplier_proposal';
2202  $classname = 'SupplierProposal';
2203  $classpath = 'supplier_proposal/class';
2204  $module = 'supplier_proposal';
2205  } elseif ($objecttype == 'stock') {
2206  $classpath = 'product/stock/class';
2207  $classfile = 'entrepot';
2208  $classname = 'Entrepot';
2209  } elseif ($objecttype == 'facturerec') {
2210  $classpath = 'compta/facture/class';
2211  $classfile = 'facture-rec';
2212  $classname = 'FactureRec';
2213  $module = 'facture';
2214  } elseif ($objecttype == 'mailing') {
2215  $classpath = 'comm/mailing/class';
2216  $classfile = 'mailing';
2217  $classname = 'Mailing';
2218  }
2219 
2220  if (isModEnabled($module)) {
2221  $res = dol_include_once('/'.$classpath.'/'.$classfile.'.class.php');
2222  if ($res) {
2223  if (class_exists($classname)) {
2224  $object = new $classname($db);
2225  $res = $object->fetch($objectid);
2226  if ($res > 0) {
2227  $ret = $object->getNomUrl($withpicto, $option);
2228  } elseif ($res == 0) {
2229  $ret = $langs->trans('Deleted');
2230  }
2231  unset($object);
2232  } else {
2233  dol_syslog("Class with classname ".$classname." is unknown even after the include", LOG_ERR);
2234  }
2235  }
2236  }
2237  return $ret;
2238 }
2239 
2240 
2249 function cleanCorruptedTree($db, $tabletocleantree, $fieldfkparent)
2250 {
2251  $totalnb = 0;
2252  $listofid = array();
2253  $listofparentid = array();
2254 
2255  // Get list of all id in array listofid and all parents in array listofparentid
2256  $sql = "SELECT rowid, ".$fieldfkparent." as parent_id FROM ".MAIN_DB_PREFIX.$tabletocleantree;
2257  $resql = $db->query($sql);
2258  if ($resql) {
2259  $num = $db->num_rows($resql);
2260  $i = 0;
2261  while ($i < $num) {
2262  $obj = $db->fetch_object($resql);
2263  $listofid[] = $obj->rowid;
2264  if ($obj->parent_id > 0) {
2265  $listofparentid[$obj->rowid] = $obj->parent_id;
2266  }
2267  $i++;
2268  }
2269  } else {
2270  dol_print_error($db);
2271  }
2272 
2273  if (count($listofid)) {
2274  print 'Code requested to clean tree (may be to solve data corruption), so we check/clean orphelins and loops.'."<br>\n";
2275 
2276  // Check loops on each other
2277  $sql = "UPDATE ".MAIN_DB_PREFIX.$tabletocleantree." SET ".$fieldfkparent." = 0 WHERE ".$fieldfkparent." = rowid"; // So we update only records linked to themself
2278  $resql = $db->query($sql);
2279  if ($resql) {
2280  $nb = $db->affected_rows($sql);
2281  if ($nb > 0) {
2282  print '<br>Some record that were parent of themself were cleaned.';
2283  }
2284 
2285  $totalnb += $nb;
2286  }
2287  //else dol_print_error($db);
2288 
2289  // Check other loops
2290  $listofidtoclean = array();
2291  foreach ($listofparentid as $id => $pid) {
2292  // Check depth
2293  //print 'Analyse record id='.$id.' with parent '.$pid.'<br>';
2294 
2295  $cursor = $id;
2296  $arrayidparsed = array(); // We start from child $id
2297  while ($cursor > 0) {
2298  $arrayidparsed[$cursor] = 1;
2299  if ($arrayidparsed[$listofparentid[$cursor]]) { // We detect a loop. A record with a parent that was already into child
2300  print 'Found a loop between id '.$id.' - '.$cursor.'<br>';
2301  unset($arrayidparsed);
2302  $listofidtoclean[$cursor] = $id;
2303  break;
2304  }
2305  $cursor = $listofparentid[$cursor];
2306  }
2307 
2308  if (count($listofidtoclean)) {
2309  break;
2310  }
2311  }
2312 
2313  $sql = "UPDATE ".MAIN_DB_PREFIX.$tabletocleantree;
2314  $sql .= " SET ".$fieldfkparent." = 0";
2315  $sql .= " WHERE rowid IN (".$db->sanitize(implode(',', $listofidtoclean)).")"; // So we update only records detected wrong
2316  $resql = $db->query($sql);
2317  if ($resql) {
2318  $nb = $db->affected_rows($sql);
2319  if ($nb > 0) {
2320  // Removed orphelins records
2321  print '<br>Some records were detected to have parent that is a child, we set them as root record for id: ';
2322  print implode(',', $listofidtoclean);
2323  }
2324 
2325  $totalnb += $nb;
2326  }
2327  //else dol_print_error($db);
2328 
2329  // Check and clean orphelins
2330  $sql = "UPDATE ".MAIN_DB_PREFIX.$tabletocleantree;
2331  $sql .= " SET ".$fieldfkparent." = 0";
2332  $sql .= " WHERE ".$fieldfkparent." NOT IN (".$db->sanitize(implode(',', $listofid), 1).")"; // So we update only records linked to a non existing parent
2333  $resql = $db->query($sql);
2334  if ($resql) {
2335  $nb = $db->affected_rows($sql);
2336  if ($nb > 0) {
2337  // Removed orphelins records
2338  print '<br>Some orphelins were found and modified to be parent so records are visible again for id: ';
2339  print implode(',', $listofid);
2340  }
2341 
2342  $totalnb += $nb;
2343  }
2344  //else dol_print_error($db);
2345 
2346  print '<br>We fixed '.$totalnb.' record(s). Some records may still be corrupted. New check may be required.';
2347  return $totalnb;
2348  }
2349  return -1;
2350 }
2351 
2352 
2362 function colorArrayToHex($arraycolor, $colorifnotfound = '888888')
2363 {
2364  if (!is_array($arraycolor)) {
2365  return $colorifnotfound;
2366  }
2367  if (empty($arraycolor)) {
2368  return $colorifnotfound;
2369  }
2370  return sprintf("%02s", dechex($arraycolor[0])).sprintf("%02s", dechex($arraycolor[1])).sprintf("%02s", dechex($arraycolor[2]));
2371 }
2372 
2383 function colorStringToArray($stringcolor, $colorifnotfound = array(88, 88, 88))
2384 {
2385  if (is_array($stringcolor)) {
2386  return $stringcolor; // If already into correct output format, we return as is
2387  }
2388  $reg = array();
2389  $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);
2390  if (!$tmp) {
2391  $tmp = explode(',', $stringcolor);
2392  if (count($tmp) < 3) {
2393  return $colorifnotfound;
2394  }
2395  return $tmp;
2396  }
2397  return array(hexdec($reg[1]), hexdec($reg[2]), hexdec($reg[3]));
2398 }
2399 
2405 function colorValidateHex($color, $allow_white = true)
2406 {
2407  if (!$allow_white && ($color === '#fff' || $color === '#ffffff')) {
2408  return false;
2409  }
2410 
2411  if (preg_match('/^#[a-f0-9]{6}$/i', $color)) { //hex color is valid
2412  return true;
2413  }
2414  return false;
2415 }
2416 
2426 function colorAgressiveness($hex, $ratio = -50, $brightness = 0)
2427 {
2428  if (empty($ratio)) {
2429  $ratio = 0; // To avoid null
2430  }
2431 
2432  // Steps should be between -255 and 255. Negative = darker, positive = lighter
2433  $ratio = max(-100, min(100, $ratio));
2434 
2435  // Normalize into a six character long hex string
2436  $hex = str_replace('#', '', $hex);
2437  if (strlen($hex) == 3) {
2438  $hex = str_repeat(substr($hex, 0, 1), 2).str_repeat(substr($hex, 1, 1), 2).str_repeat(substr($hex, 2, 1), 2);
2439  }
2440 
2441  // Split into three parts: R, G and B
2442  $color_parts = str_split($hex, 2);
2443  $return = '#';
2444 
2445  foreach ($color_parts as $color) {
2446  $color = hexdec($color); // Convert to decimal
2447  if ($ratio > 0) { // We increase aggressivity
2448  if ($color > 127) {
2449  $color += ((255 - $color) * ($ratio / 100));
2450  }
2451  if ($color < 128) {
2452  $color -= ($color * ($ratio / 100));
2453  }
2454  } else { // We decrease aggressiveness
2455  if ($color > 128) {
2456  $color -= (($color - 128) * (abs($ratio) / 100));
2457  }
2458  if ($color < 127) {
2459  $color += ((128 - $color) * (abs($ratio) / 100));
2460  }
2461  }
2462  if ($brightness > 0) {
2463  $color = ($color * (100 + abs($brightness)) / 100);
2464  } else {
2465  $color = ($color * (100 - abs($brightness)) / 100);
2466  }
2467 
2468  $color = max(0, min(255, $color)); // Adjust color to stay into valid range
2469  $return .= str_pad(dechex($color), 2, '0', STR_PAD_LEFT); // Make two char hex code
2470  }
2471 
2472  //var_dump($hex.' '.$ratio.' -> '.$return);
2473  return $return;
2474 }
2475 
2482 function colorAdjustBrightness($hex, $steps)
2483 {
2484  // Steps should be between -255 and 255. Negative = darker, positive = lighter
2485  $steps = max(-255, min(255, $steps));
2486 
2487  // Normalize into a six character long hex string
2488  $hex = str_replace('#', '', $hex);
2489  if (strlen($hex) == 3) {
2490  $hex = str_repeat(substr($hex, 0, 1), 2).str_repeat(substr($hex, 1, 1), 2).str_repeat(substr($hex, 2, 1), 2);
2491  }
2492 
2493  // Split into three parts: R, G and B
2494  $color_parts = str_split($hex, 2);
2495  $return = '#';
2496 
2497  foreach ($color_parts as $color) {
2498  $color = hexdec($color); // Convert to decimal
2499  $color = max(0, min(255, $color + $steps)); // Adjust color
2500  $return .= str_pad(dechex($color), 2, '0', STR_PAD_LEFT); // Make two char hex code
2501  }
2502 
2503  return $return;
2504 }
2505 
2511 function colorDarker($hex, $percent)
2512 {
2513  $steps = intval(255 * $percent / 100) * -1;
2514  return colorAdjustBrightness($hex, $steps);
2515 }
2516 
2522 function colorLighten($hex, $percent)
2523 {
2524  $steps = intval(255 * $percent / 100);
2525  return colorAdjustBrightness($hex, $steps);
2526 }
2527 
2528 
2535 function colorHexToRgb($hex, $alpha = false, $returnArray = false)
2536 {
2537  $string = '';
2538  $hex = str_replace('#', '', $hex);
2539  $length = strlen($hex);
2540  $rgb = array();
2541  $rgb['r'] = hexdec($length == 6 ? substr($hex, 0, 2) : ($length == 3 ? str_repeat(substr($hex, 0, 1), 2) : 0));
2542  $rgb['g'] = hexdec($length == 6 ? substr($hex, 2, 2) : ($length == 3 ? str_repeat(substr($hex, 1, 1), 2) : 0));
2543  $rgb['b'] = hexdec($length == 6 ? substr($hex, 4, 2) : ($length == 3 ? str_repeat(substr($hex, 2, 1), 2) : 0));
2544  if ($alpha !== false) {
2545  $rgb['a'] = (float) $alpha;
2546  $string = 'rgba('.implode(',', array_map('strval', $rgb)).')';
2547  } else {
2548  $string = 'rgb('.implode(',', array_map('strval', $rgb)).')';
2549  }
2550 
2551  if ($returnArray) {
2552  return $rgb;
2553  } else {
2554  return $string;
2555  }
2556 }
2557 
2566 function colorHexToHsl($hex, $alpha = false, $returnArray = false)
2567 {
2568  $hex = str_replace('#', '', $hex);
2569  $red = hexdec(substr($hex, 0, 2)) / 255;
2570  $green = hexdec(substr($hex, 2, 2)) / 255;
2571  $blue = hexdec(substr($hex, 4, 2)) / 255;
2572 
2573  $cmin = min($red, $green, $blue);
2574  $cmax = max($red, $green, $blue);
2575  $delta = $cmax - $cmin;
2576 
2577  if ($delta == 0) {
2578  $hue = 0;
2579  } elseif ($cmax === $red) {
2580  $hue = (($green - $blue) / $delta);
2581  } elseif ($cmax === $green) {
2582  $hue = ($blue - $red) / $delta + 2;
2583  } else {
2584  $hue = ($red - $green) / $delta + 4;
2585  }
2586 
2587  $hue = round($hue * 60);
2588  if ($hue < 0) {
2589  $hue += 360;
2590  }
2591 
2592  $lightness = (($cmax + $cmin) / 2);
2593  $saturation = $delta === 0 ? 0 : ($delta / (1 - abs(2 * $lightness - 1)));
2594  if ($saturation < 0) {
2595  $saturation += 1;
2596  }
2597 
2598  $lightness = round($lightness * 100);
2599  $saturation = round($saturation * 100);
2600 
2601  if ($returnArray) {
2602  return array(
2603  'h' => $hue,
2604  'l' => $lightness,
2605  's' => $saturation,
2606  'a' => $alpha === false ? 1 : $alpha
2607  );
2608  } elseif ($alpha) {
2609  return 'hsla('.$hue.', '.$saturation.', '.$lightness.' / '.$alpha.')';
2610  } else {
2611  return 'hsl('.$hue.', '.$saturation.', '.$lightness.')';
2612  }
2613 }
2614 
2622 function cartesianArray(array $input)
2623 {
2624  // filter out empty values
2625  $input = array_filter($input);
2626 
2627  $result = array(array());
2628 
2629  foreach ($input as $key => $values) {
2630  $append = array();
2631 
2632  foreach ($result as $product) {
2633  foreach ($values as $item) {
2634  $product[$key] = $item;
2635  $append[] = $product;
2636  }
2637  }
2638 
2639  $result = $append;
2640  }
2641 
2642  return $result;
2643 }
2644 
2645 
2652 function getModuleDirForApiClass($moduleobject)
2653 {
2654  $moduledirforclass = $moduleobject;
2655  if ($moduledirforclass != 'api') {
2656  $moduledirforclass = preg_replace('/api$/i', '', $moduledirforclass);
2657  }
2658 
2659  if ($moduleobject == 'contracts') {
2660  $moduledirforclass = 'contrat';
2661  } elseif (in_array($moduleobject, array('admin', 'login', 'setup', 'access', 'status', 'tools', 'documents'))) {
2662  $moduledirforclass = 'api';
2663  } elseif ($moduleobject == 'contact' || $moduleobject == 'contacts' || $moduleobject == 'customer' || $moduleobject == 'thirdparty' || $moduleobject == 'thirdparties') {
2664  $moduledirforclass = 'societe';
2665  } elseif ($moduleobject == 'propale' || $moduleobject == 'proposals') {
2666  $moduledirforclass = 'comm/propal';
2667  } elseif ($moduleobject == 'agenda' || $moduleobject == 'agendaevents') {
2668  $moduledirforclass = 'comm/action';
2669  } elseif ($moduleobject == 'adherent' || $moduleobject == 'members' || $moduleobject == 'memberstypes' || $moduleobject == 'subscriptions') {
2670  $moduledirforclass = 'adherents';
2671  } elseif ($moduleobject == 'don' || $moduleobject == 'donations') {
2672  $moduledirforclass = 'don';
2673  } elseif ($moduleobject == 'banque' || $moduleobject == 'bankaccounts') {
2674  $moduledirforclass = 'compta/bank';
2675  } elseif ($moduleobject == 'category' || $moduleobject == 'categorie') {
2676  $moduledirforclass = 'categories';
2677  } elseif ($moduleobject == 'order' || $moduleobject == 'orders') {
2678  $moduledirforclass = 'commande';
2679  } elseif ($moduleobject == 'shipments') {
2680  $moduledirforclass = 'expedition';
2681  } elseif ($moduleobject == 'multicurrencies') {
2682  $moduledirforclass = 'multicurrency';
2683  } elseif ($moduleobject == 'facture' || $moduleobject == 'invoice' || $moduleobject == 'invoices') {
2684  $moduledirforclass = 'compta/facture';
2685  } elseif ($moduleobject == 'project' || $moduleobject == 'projects' || $moduleobject == 'task' || $moduleobject == 'tasks') {
2686  $moduledirforclass = 'projet';
2687  } elseif ($moduleobject == 'stock' || $moduleobject == 'stockmovements' || $moduleobject == 'warehouses') {
2688  $moduledirforclass = 'product/stock';
2689  } elseif ($moduleobject == 'supplierproposals' || $moduleobject == 'supplierproposal' || $moduleobject == 'supplier_proposal') {
2690  $moduledirforclass = 'supplier_proposal';
2691  } elseif ($moduleobject == 'fournisseur' || $moduleobject == 'supplierinvoices' || $moduleobject == 'supplierorders') {
2692  $moduledirforclass = 'fourn';
2693  } elseif ($moduleobject == 'ficheinter' || $moduleobject == 'interventions') {
2694  $moduledirforclass = 'fichinter';
2695  } elseif ($moduleobject == 'mos') {
2696  $moduledirforclass = 'mrp';
2697  } elseif ($moduleobject == 'workstations') {
2698  $moduledirforclass = 'workstation';
2699  } elseif ($moduleobject == 'accounting') {
2700  $moduledirforclass = 'accountancy';
2701  } elseif (in_array($moduleobject, array('products', 'expensereports', 'users', 'tickets', 'boms', 'receptions', 'partnerships', 'recruitments'))) {
2702  $moduledirforclass = preg_replace('/s$/', '', $moduleobject);
2703  } elseif ($moduleobject == 'paymentsalaries') {
2704  $moduledirforclass = 'salaries';
2705  } elseif ($moduleobject == 'paymentexpensereports') {
2706  $moduledirforclass = 'expensereport';
2707  }
2708 
2709  return $moduledirforclass;
2710 }
2711 
2719 function randomColorPart($min = 0, $max = 255)
2720 {
2721  return str_pad(dechex(mt_rand($min, $max)), 2, '0', STR_PAD_LEFT);
2722 }
2723 
2731 function randomColor($min = 0, $max = 255)
2732 {
2733  return randomColorPart($min, $max).randomColorPart($min, $max).randomColorPart($min, $max);
2734 }
2735 
2736 
2737 if (!function_exists('dolEscapeXML')) {
2744  function dolEscapeXML($string)
2745  {
2746  return strtr($string, array('\'' => '&apos;', '"' => '&quot;', '&' => '&amp;', '<' => '&lt;', '>' => '&gt;'));
2747  }
2748 }
2749 
2750 
2758 {
2759  global $dolibarr_main_url_root;
2760  // Define $urlwithroot
2761  $urlwithouturlroot = preg_replace('/'.preg_quote(DOL_URL_ROOT, '/').'$/i', '', trim($dolibarr_main_url_root));
2762  $urlwithroot = $urlwithouturlroot.DOL_URL_ROOT; // This is to use external domain name found into config file
2763  //$urlwithroot=DOL_MAIN_URL_ROOT; // This is to use same domain name than current
2764  $notetoshow = preg_replace('/src="[a-zA-Z0-9_\/\-\.]*(viewimage\.php\?modulepart=medias[^"]*)"/', 'src="'.$urlwithroot.'/\1"', $notetoshow);
2765  return $notetoshow;
2766 }
2767 
2776 function price2fec($amount)
2777 {
2778  global $conf;
2779 
2780  // Clean parameters
2781  if (empty($amount)) {
2782  $amount = 0; // To have a numeric value if amount not defined or = ''
2783  }
2784  $amount = (is_numeric($amount) ? $amount : 0); // Check if amount is numeric, for example, an error occurred when amount value = o (letter) instead 0 (number)
2785 
2786  // Output decimal number by default
2787  $nbdecimal = (!getDolGlobalString('ACCOUNTING_FEC_DECIMAL_LENGTH') ? 2 : $conf->global->ACCOUNTING_FEC_DECIMAL_LENGTH);
2788 
2789  // Output separators by default
2790  $dec = (!getDolGlobalString('ACCOUNTING_FEC_DECIMAL_SEPARATOR') ? ',' : $conf->global->ACCOUNTING_FEC_DECIMAL_SEPARATOR);
2791  $thousand = (!getDolGlobalString('ACCOUNTING_FEC_THOUSAND_SEPARATOR') ? '' : $conf->global->ACCOUNTING_FEC_THOUSAND_SEPARATOR);
2792 
2793  // Format number
2794  $output = number_format($amount, $nbdecimal, $dec, $thousand);
2795 
2796  return $output;
2797 }
2798 
2805 function phpSyntaxError($code)
2806 {
2807  if (!defined("CR")) {
2808  define("CR", "\r");
2809  }
2810  if (!defined("LF")) {
2811  define("LF", "\n");
2812  }
2813  if (!defined("CRLF")) {
2814  define("CRLF", "\r\n");
2815  }
2816 
2817  $braces = 0;
2818  $inString = 0;
2819  foreach (token_get_all('<?php '.$code) as $token) {
2820  if (is_array($token)) {
2821  switch ($token[0]) {
2822  case T_CURLY_OPEN:
2823  case T_DOLLAR_OPEN_CURLY_BRACES:
2824  case T_START_HEREDOC:
2825  ++$inString;
2826  break;
2827  case T_END_HEREDOC:
2828  --$inString;
2829  break;
2830  }
2831  } elseif ($inString & 1) {
2832  switch ($token) {
2833  case '`':
2834  case '\'':
2835  case '"':
2836  --$inString;
2837  break;
2838  }
2839  } else {
2840  switch ($token) {
2841  case '`':
2842  case '\'':
2843  case '"':
2844  ++$inString;
2845  break;
2846  case '{':
2847  ++$braces;
2848  break;
2849  case '}':
2850  if ($inString) {
2851  --$inString;
2852  } else {
2853  --$braces;
2854  if ($braces < 0) {
2855  break 2;
2856  }
2857  }
2858  break;
2859  }
2860  }
2861  }
2862  $inString = @ini_set('log_errors', false);
2863  $token = @ini_set('display_errors', true);
2864  ob_start();
2865  $code = substr($code, strlen('<?php '));
2866  $braces || $code = "if(0){{$code}\n}";
2867  // @phan-suppress-next-line PhanPluginUnsafeEval
2868  if (eval($code) === false) {
2869  if ($braces) {
2870  $braces = PHP_INT_MAX;
2871  } else {
2872  false !== strpos($code, CR) && $code = strtr(str_replace(CRLF, LF, $code), CR, LF);
2873  $braces = substr_count($code, LF);
2874  }
2875  $code = ob_get_clean();
2876  $code = strip_tags($code);
2877  if (preg_match("'syntax error, (.+) in .+ on line (\d+)$'s", $code, $code)) {
2878  $code[2] = (int) $code[2];
2879  $code = $code[2] <= $braces
2880  ? array($code[1], $code[2])
2881  : array('unexpected $end'.substr($code[1], 14), $braces);
2882  } else {
2883  $code = array('syntax error', 0);
2884  }
2885  } else {
2886  ob_end_clean();
2887  $code = false;
2888  }
2889  @ini_set('display_errors', $token);
2890  @ini_set('log_errors', $inString);
2891  return $code;
2892 }
2893 
2894 
2901 {
2902  global $user;
2903 
2904  // If $acceptlocallinktomedia is true, we can add link media files int email templates (we already can do this into HTML editor of an email).
2905  // 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:
2906  // $urlwithouturlroot = preg_replace('/'.preg_quote(DOL_URL_ROOT, '/').'$/i', '', trim($dolibarr_main_url_root));
2907  // $urlwithroot = $urlwithouturlroot.DOL_URL_ROOT; // This is to use external domain name found into config file
2908  $acceptlocallinktomedia = getDolGlobalInt('MAIN_DISALLOW_MEDIAS_IN_EMAIL_TEMPLATES') ? 0 : 1;
2909  if ($acceptlocallinktomedia) {
2910  global $dolibarr_main_url_root;
2911  $urlwithouturlroot = preg_replace('/'.preg_quote(DOL_URL_ROOT, '/').'$/i', '', trim($dolibarr_main_url_root));
2912 
2913  // Parse $newUrl
2914  $newUrlArray = parse_url($urlwithouturlroot);
2915  $hosttocheck = $newUrlArray['host'];
2916  $hosttocheck = str_replace(array('[', ']'), '', $hosttocheck); // Remove brackets of IPv6
2917 
2918  if (function_exists('gethostbyname')) {
2919  $iptocheck = gethostbyname($hosttocheck);
2920  } else {
2921  $iptocheck = $hosttocheck;
2922  }
2923 
2924  //var_dump($iptocheck.' '.$acceptlocallinktomedia);
2925  $allowParamName = 'MAIN_ALLOW_WYSIWYG_LOCAL_MEDIAS_ON_PRIVATE_NETWORK';
2926  $allowPrivateNetworkIP = getDolGlobalInt($allowParamName);
2927  if (!$allowPrivateNetworkIP && !filter_var($iptocheck, FILTER_VALIDATE_IP, FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE)) {
2928  // If ip of public url is a private network IP, we do not allow this.
2929  $acceptlocallinktomedia = 0;
2930  //dol_syslog("WYSIWYG Editor : local media not allowed (checked IP: {$iptocheck}). Use {$allowParamName} = 1 to allow local URL into WYSIWYG html content");
2931  }
2932 
2933  if (preg_match('/http:/i', $urlwithouturlroot)) {
2934  // 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.
2935  $acceptlocallinktomedia = 0;
2936  // TODO Show a warning
2937  }
2938 
2939  if (!empty($user->socid)) {
2940  $acceptlocallinktomedia = 0;
2941  }
2942  }
2943 
2944  //return 1;
2945  return $acceptlocallinktomedia;
2946 }
2947 
2948 
2955 function removeGlobalParenthesis($string)
2956 {
2957  $string = trim($string);
2958 
2959  // If string does not start and end with parenthesis, we return $string as is.
2960  if (! preg_match('/^\‍(.*\‍)$/', $string)) {
2961  return $string;
2962  }
2963 
2964  $nbofchars = dol_strlen($string);
2965  $i = 0;
2966  $g = 0;
2967  $countparenthesis = 0;
2968  while ($i < $nbofchars) {
2969  $char = dol_substr($string, $i, 1);
2970  if ($char == '(') {
2971  $countparenthesis++;
2972  } elseif ($char == ')') {
2973  $countparenthesis--;
2974  if ($countparenthesis <= 0) { // We reach the end of an independent group of parenthesis
2975  $g++;
2976  }
2977  }
2978  $i++;
2979  }
2980 
2981  if ($g <= 1) {
2982  return preg_replace('/^\‍(/', '', preg_replace('/\‍)$/', '', $string));
2983  }
2984 
2985  return $string;
2986 }
2987 
2988 
2996 {
2997  $arrayofcommonemoji = array(
2998  'misc' => array('2600', '26FF'), // Miscellaneous Symbols
2999  'ding' => array('2700', '27BF'), // Dingbats
3000  '????' => array('9989', '9989'), // Variation Selectors
3001  'vars' => array('FE00', 'FE0F'), // Variation Selectors
3002  'pict' => array('1F300', '1F5FF'), // Miscellaneous Symbols and Pictographs
3003  'emot' => array('1F600', '1F64F'), // Emoticons
3004  'tran' => array('1F680', '1F6FF'), // Transport and Map Symbols
3005  'flag' => array('1F1E0', '1F1FF'), // Flags (note: may be 1F1E6 instead of 1F1E0)
3006  'supp' => array('1F900', '1F9FF'), // Supplemental Symbols and Pictographs
3007  );
3008 
3009  return $arrayofcommonemoji;
3010 }
3011 
3019 function removeEmoji($text, $allowedemoji = 1)
3020 {
3021  // $allowedemoji can be
3022  // 0=no emoji, 1=exclude the main known emojis (default), 2=keep only the main known (not implemented), 3=accept all
3023  // Note that to accept emoji in database, you must use utf8mb4, utf8mb3 is not enough.
3024 
3025  if ($allowedemoji == 0) {
3026  // For a large removal:
3027  $text = preg_replace('/[\x{2600}-\x{FFFF}]/u', '', $text);
3028  $text = preg_replace('/[\x{10000}-\x{10FFFF}]/u', '', $text);
3029  }
3030 
3031  // Delete emoji chars with a regex
3032  // See https://www.unicode.org/emoji/charts/full-emoji-list.html
3033  if ($allowedemoji == 1) {
3034  $arrayofcommonemoji = getArrayOfEmojiBis();
3035 
3036  foreach ($arrayofcommonemoji as $key => $valarray) {
3037  $text = preg_replace('/[\x{'.$valarray[0].'}-\x{'.$valarray[1].'}]/u', '', $text);
3038  }
3039  }
3040 
3041  if ($allowedemoji == 2) {
3042  // TODO Not yet implemented
3043  }
3044 
3045  return $text;
3046 }
if($user->socid > 0) if(! $user->hasRight('accounting', 'chartofaccount')) $object
Definition: card.php:58
Class to manage translations.
Class to manage Dolibarr users.
Definition: user.class.php:50
if(isModEnabled('invoice') && $user->hasRight('facture', 'lire')) if((isModEnabled('fournisseur') &&!getDolGlobalString('MAIN_USE_NEW_SUPPLIERMOD') && $user->hasRight("fournisseur", "facture", "lire"))||(isModEnabled('supplier_invoice') && $user->hasRight("supplier_invoice", "lire"))) if(isModEnabled('don') && $user->hasRight('don', 'lire')) if(isModEnabled('tax') && $user->hasRight('tax', 'charges', 'lire')) if(isModEnabled('invoice') &&isModEnabled('order') && $user->hasRight("commande", "lire") &&!getDolGlobalString('WORKFLOW_DISABLE_CREATE_INVOICE_FROM_ORDER')) $sql
Social contributions to pay.
Definition: index.php:751
getServerTimeZoneInt($refgmtdate='now')
Return server timezone int.
Definition: date.lib.php:85
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.
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 tree (orphelins linked to a not existing parent), record linked to themself and child...
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.
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)
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.
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...
colorDarker($hex, $percent)
dolGetElementUrl($objectid, $objecttype, $withpicto=0, $option='')
Return link url to an object.
getListOfModels($db, $type, $maxfilenamelength=0)
Return list of activated modules usable for document generation.
dol_print_object_info($object, $usetable=0)
Show information on an object TODO Move this into html.formother.
get_next_value($db, $mask, $table, $field, $where='', $objsoc='', $date='', $mode='next', $bentityon=true, $objuser=null, $forceentity=null)
Return last or next value for a mask (according to area we should not reset)
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.
dol_getDefaultFormat(Translate $outputlangs=null)
Try to guess default paper format according to language into $langs.
price2fec($amount)
Function to format a value into a defined format for French administration (no thousand separator & d...
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.
if(!function_exists('dol_getprefix')) dol_include_once($relpath, $classname='')
Make an include_once using default root and alternate root if it fails.
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).
dol_string_unaccent($str)
Clean a string from all accent characters to be used as ref, login or by dol_sanitizeFileName.
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 dolibarr global constant string value.
isModEnabled($module)
Is Dolibarr module enabled.
dol_syslog($message, $level=LOG_INFO, $ident=0, $suffixinfilename='', $restricttologhandler='', $logcontext=null)
Write log message into outputs.
div float
Unit price before taxes.
Definition: style.css.php:963