dolibarr  13.0.2
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  *
10  * This program is free software; you can redistribute it and/or modify
11  * it under the terms of the GNU General Public License as published by
12  * the Free Software Foundation; either version 3 of the License, or
13  * (at your option) any later version.
14  *
15  * This program is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18  * GNU General Public License for more details.
19  *
20  * You should have received a copy of the GNU General Public License
21  * along with this program. If not, see <https://www.gnu.org/licenses/>.
22  * or see https://www.gnu.org/
23  */
24 
31 // Enable this line to trace path when function is called.
32 //print xdebug_print_function_stack('Functions2.lib was called');exit;
33 
40 function jsUnEscape($source)
41 {
42  $decodedStr = "";
43  $pos = 0;
44  $len = strlen($source);
45  while ($pos < $len) {
46  $charAt = substr($source, $pos, 1);
47  if ($charAt == '%') {
48  $pos++;
49  $charAt = substr($source, $pos, 1);
50  if ($charAt == 'u') {
51  // we got a unicode character
52  $pos++;
53  $unicodeHexVal = substr($source, $pos, 4);
54  $unicode = hexdec($unicodeHexVal);
55  $entity = "&#".$unicode.';';
56  $decodedStr .= utf8_encode($entity);
57  $pos += 4;
58  } else {
59  // we have an escaped ascii character
60  $hexVal = substr($source, $pos, 2);
61  $decodedStr .= chr(hexdec($hexVal));
62  $pos += 2;
63  }
64  } else {
65  $decodedStr .= $charAt;
66  $pos++;
67  }
68  }
69  return dol_html_entity_decode($decodedStr, ENT_COMPAT | ENT_HTML5);
70 }
71 
72 
80 function dolGetModulesDirs($subdir = '')
81 {
82  global $conf;
83 
84  $modulesdir = array();
85 
86  foreach ($conf->file->dol_document_root as $type => $dirroot)
87  {
88  // Default core/modules dir
89  if ($type === 'main') {
90  $modulesdir[$dirroot.'/core/modules'.$subdir.'/'] = $dirroot.'/core/modules'.$subdir.'/';
91  }
92 
93  // Scan dir from external modules
94  $handle = @opendir($dirroot);
95  if (is_resource($handle))
96  {
97  while (($file = readdir($handle)) !== false)
98  {
99  if (preg_match('/disabled/', $file)) continue; // We discard module if it contains disabled into name.
100 
101  if (is_dir($dirroot.'/'.$file) && substr($file, 0, 1) <> '.' && substr($file, 0, 3) <> 'CVS' && $file != 'includes')
102  {
103  if (is_dir($dirroot.'/'.$file.'/core/modules'.$subdir.'/'))
104  {
105  $modulesdir[$dirroot.'/'.$file.'/core/modules'.$subdir.'/'] = $dirroot.'/'.$file.'/core/modules'.$subdir.'/';
106  }
107  }
108  }
109  closedir($handle);
110  }
111  }
112  return $modulesdir;
113 }
114 
115 
122 function dol_getDefaultFormat(Translate $outputlangs = null)
123 {
124  global $langs;
125 
126  $selected = 'EUA4';
127  if (!$outputlangs) {
128  $outputlangs = $langs;
129  }
130 
131  if ($outputlangs->defaultlang == 'ca_CA') $selected = 'CAP4'; // Canada
132  if ($outputlangs->defaultlang == 'en_US') $selected = 'USLetter'; // US
133  return $selected;
134 }
135 
144 function dol_print_file($langs, $filename, $searchalt = 0)
145 {
146  global $conf;
147 
148  // Test if file is in lang directory
149  foreach ($langs->dir as $searchdir)
150  {
151  $formfile = ($searchdir."/langs/".$langs->defaultlang."/".$filename);
152  dol_syslog('functions2::dol_print_file search file '.$formfile, LOG_DEBUG);
153  if (is_readable($formfile))
154  {
155  $content = file_get_contents($formfile);
156  $isutf8 = utf8_check($content);
157  if (!$isutf8 && $conf->file->character_set_client == 'UTF-8') print utf8_encode($content);
158  elseif ($isutf8 && $conf->file->character_set_client == 'ISO-8859-1') print utf8_decode($content);
159  else print $content;
160  return true;
161  } else dol_syslog('functions2::dol_print_file not found', LOG_DEBUG);
162 
163  if ($searchalt) {
164  // Test si fichier dans repertoire de la langue alternative
165  if ($langs->defaultlang != "en_US") $formfilealt = $searchdir."/langs/en_US/".$filename;
166  else $formfilealt = $searchdir."/langs/fr_FR/".$filename;
167  dol_syslog('functions2::dol_print_file search alt file '.$formfilealt, LOG_DEBUG);
168  //print 'getcwd='.getcwd().' htmlfilealt='.$formfilealt.' X '.file_exists(getcwd().'/'.$formfilealt);
169  if (is_readable($formfilealt))
170  {
171  $content = file_get_contents($formfilealt);
172  $isutf8 = utf8_check($content);
173  if (!$isutf8 && $conf->file->character_set_client == 'UTF-8') print utf8_encode($content);
174  elseif ($isutf8 && $conf->file->character_set_client == 'ISO-8859-1') print utf8_decode($content);
175  else print $content;
176  return true;
177  } else dol_syslog('functions2::dol_print_file not found', LOG_DEBUG);
178  }
179  }
180 
181  return false;
182 }
183 
192 function dol_print_object_info($object, $usetable = 0)
193 {
194  global $langs, $db;
195 
196  // Load translation files required by the page
197  $langs->loadLangs(array('other', 'admin'));
198 
199  include_once DOL_DOCUMENT_ROOT.'/core/lib/date.lib.php';
200 
201  $deltadateforserver = getServerTimeZoneInt('now');
202  $deltadateforclient = ((int) $_SESSION['dol_tz'] + (int) $_SESSION['dol_dst']);
203  //$deltadateforcompany=((int) $_SESSION['dol_tz'] + (int) $_SESSION['dol_dst']);
204  $deltadateforuser = round($deltadateforclient - $deltadateforserver);
205  //print "x".$deltadateforserver." - ".$deltadateforclient." - ".$deltadateforuser;
206 
207  if ($usetable) print '<table class="border tableforfield centpercent">';
208 
209  // Import key
210  if (!empty($object->import_key))
211  {
212  if ($usetable) print '<tr><td class="titlefield">';
213  print $langs->trans("ImportedWithSet");
214  if ($usetable) print '</td><td>';
215  else print ': ';
216  print $object->import_key;
217  if ($usetable) print '</td></tr>';
218  else print '<br>';
219  }
220 
221  // User creation (old method using already loaded object and not id is kept for backward compatibility)
222  if (!empty($object->user_creation) || !empty($object->user_creation_id))
223  {
224  if ($usetable) print '<tr><td class="titlefield">';
225  print $langs->trans("CreatedBy");
226  if ($usetable) print '</td><td>';
227  else print ': ';
228  if (is_object($object->user_creation))
229  {
230  if ($object->user_creation->id) print $object->user_creation->getNomUrl(-1, '', 0, 0, 0);
231  else print $langs->trans("Unknown");
232  } else {
233  $userstatic = new User($db);
234  $userstatic->fetch($object->user_creation_id ? $object->user_creation_id : $object->user_creation);
235  if ($userstatic->id) print $userstatic->getNomUrl(-1, '', 0, 0, 0);
236  else print $langs->trans("Unknown");
237  }
238  if ($usetable) print '</td></tr>';
239  else print '<br>';
240  }
241 
242  // Date creation
243  if (!empty($object->date_creation))
244  {
245  if ($usetable) print '<tr><td class="titlefield">';
246  print $langs->trans("DateCreation");
247  if ($usetable) print '</td><td>';
248  else print ': ';
249  print dol_print_date($object->date_creation, 'dayhour', 'tzserver');
250  if ($deltadateforuser) print ' '.$langs->trans("CurrentHour").' &nbsp; / &nbsp; '.dol_print_date($object->date_creation, "dayhour", "tzuserrel").' &nbsp;'.$langs->trans("ClientHour");
251  if ($usetable) print '</td></tr>';
252  else print '<br>';
253  }
254 
255  // User change (old method using already loaded object and not id is kept for backward compatibility)
256  if (!empty($object->user_modification) || !empty($object->user_modification_id))
257  {
258  if ($usetable) print '<tr><td class="titlefield">';
259  print $langs->trans("ModifiedBy");
260  if ($usetable) print '</td><td>';
261  else print ': ';
262  if (is_object($object->user_modification))
263  {
264  if ($object->user_modification->id) print $object->user_modification->getNomUrl(-1, '', 0, 0, 0);
265  else print $langs->trans("Unknown");
266  } else {
267  $userstatic = new User($db);
268  $userstatic->fetch($object->user_modification_id ? $object->user_modification_id : $object->user_modification);
269  if ($userstatic->id) print $userstatic->getNomUrl(-1, '', 0, 0, 0);
270  else print $langs->trans("Unknown");
271  }
272  if ($usetable) print '</td></tr>';
273  else print '<br>';
274  }
275 
276  // Date change
277  if (!empty($object->date_modification))
278  {
279  if ($usetable) print '<tr><td class="titlefield">';
280  print $langs->trans("DateLastModification");
281  if ($usetable) print '</td><td>';
282  else print ': ';
283  print dol_print_date($object->date_modification, 'dayhour', 'tzserver');
284  if ($deltadateforuser) print ' '.$langs->trans("CurrentHour").' &nbsp; / &nbsp; '.dol_print_date($object->date_modification, "dayhour", "tzuserrel").' &nbsp;'.$langs->trans("ClientHour");
285  if ($usetable) print '</td></tr>';
286  else print '<br>';
287  }
288 
289  // User validation (old method using already loaded object and not id is kept for backward compatibility)
290  if (!empty($object->user_validation) || !empty($object->user_validation_id))
291  {
292  if ($usetable) print '<tr><td class="titlefield">';
293  print $langs->trans("ValidatedBy");
294  if ($usetable) print '</td><td>';
295  else print ': ';
296  if (is_object($object->user_validation))
297  {
298  if ($object->user_validation->id) print $object->user_validation->getNomUrl(-1, '', 0, 0, 0);
299  else print $langs->trans("Unknown");
300  } else {
301  $userstatic = new User($db);
302  $userstatic->fetch($object->user_validation_id ? $object->user_validation_id : $object->user_validation);
303  if ($userstatic->id) print $userstatic->getNomUrl(-1, '', 0, 0, 0);
304  else print $langs->trans("Unknown");
305  }
306  if ($usetable) print '</td></tr>';
307  else print '<br>';
308  }
309 
310  // Date validation
311  if (!empty($object->date_validation))
312  {
313  if ($usetable) print '<tr><td class="titlefield">';
314  print $langs->trans("DateValidation");
315  if ($usetable) print '</td><td>';
316  else print ': ';
317  print dol_print_date($object->date_validation, 'dayhour', 'tzserver');
318  if ($deltadateforuser) print ' '.$langs->trans("CurrentHour").' &nbsp; / &nbsp; '.dol_print_date($object->date_validation, "dayhour", 'tzuserrel').' &nbsp;'.$langs->trans("ClientHour");
319  if ($usetable) print '</td></tr>';
320  else print '<br>';
321  }
322 
323  // User approve (old method using already loaded object and not id is kept for backward compatibility)
324  if (!empty($object->user_approve) || !empty($object->user_approve_id))
325  {
326  if ($usetable) print '<tr><td class="titlefield">';
327  print $langs->trans("ApprovedBy");
328  if ($usetable) print '</td><td>';
329  else print ': ';
330  if (is_object($object->user_approve))
331  {
332  if ($object->user_approve->id) print $object->user_approve->getNomUrl(-1, '', 0, 0, 0);
333  else print $langs->trans("Unknown");
334  } else {
335  $userstatic = new User($db);
336  $userstatic->fetch($object->user_approve_id ? $object->user_approve_id : $object->user_approve);
337  if ($userstatic->id) print $userstatic->getNomUrl(-1, '', 0, 0, 0);
338  else print $langs->trans("Unknown");
339  }
340  if ($usetable) print '</td></tr>';
341  else print '<br>';
342  }
343 
344  // Date approve
345  if (!empty($object->date_approve))
346  {
347  if ($usetable) print '<tr><td class="titlefield">';
348  print $langs->trans("DateApprove");
349  if ($usetable) print '</td><td>';
350  else print ': ';
351  print dol_print_date($object->date_approve, 'dayhour', 'tzserver');
352  if ($deltadateforuser) print ' '.$langs->trans("CurrentHour").' &nbsp; / &nbsp; '.dol_print_date($object->date_approve, "dayhour", 'tzuserrel').' &nbsp;'.$langs->trans("ClientHour");
353  if ($usetable) print '</td></tr>';
354  else print '<br>';
355  }
356 
357  // User approve
358  if (!empty($object->user_approve_id2))
359  {
360  if ($usetable) print '<tr><td class="titlefield">';
361  print $langs->trans("ApprovedBy");
362  if ($usetable) print '</td><td>';
363  else print ': ';
364  $userstatic = new User($db);
365  $userstatic->fetch($object->user_approve_id2);
366  if ($userstatic->id) print $userstatic->getNomUrl(-1, '', 0, 0, 0);
367  else print $langs->trans("Unknown");
368  if ($usetable) print '</td></tr>';
369  else print '<br>';
370  }
371 
372  // Date approve
373  if (!empty($object->date_approve2))
374  {
375  if ($usetable) print '<tr><td class="titlefield">';
376  print $langs->trans("DateApprove2");
377  if ($usetable) print '</td><td>';
378  else print ': ';
379  print dol_print_date($object->date_approve2, 'dayhour', 'tzserver');
380  if ($deltadateforuser) print ' '.$langs->trans("CurrentHour").' &nbsp; / &nbsp; '.dol_print_date($object->date_approve2, "dayhour", 'tzuserrel').' &nbsp;'.$langs->trans("ClientHour");
381  if ($usetable) print '</td></tr>';
382  else print '<br>';
383  }
384 
385  // User close
386  if (!empty($object->user_cloture) || !empty($object->user_closing))
387  {
388  if (isset($object->user_cloture) && !empty($object->user_cloture)) $object->user_closing = $object->user_cloture;
389  if ($usetable) print '<tr><td class="titlefield">';
390  print $langs->trans("ClosedBy");
391  if ($usetable) print '</td><td>';
392  else print ': ';
393  if (is_object($object->user_closing))
394  {
395  if ($object->user_closing->id) print $object->user_closing->getNomUrl(-1, '', 0, 0, 0);
396  else print $langs->trans("Unknown");
397  } else {
398  $userstatic = new User($db);
399  $userstatic->fetch($object->user_closing);
400  if ($userstatic->id) print $userstatic->getNomUrl(-1, '', 0, 0, 0);
401  else print $langs->trans("Unknown");
402  }
403  if ($usetable) print '</td></tr>';
404  else print '<br>';
405  }
406 
407  // Date close
408  if (!empty($object->date_cloture) || !empty($object->date_closing))
409  {
410  if (isset($object->date_cloture) && !empty($object->date_cloture)) $object->date_closing = $object->date_cloture;
411  if ($usetable) print '<tr><td class="titlefield">';
412  print $langs->trans("DateClosing");
413  if ($usetable) print '</td><td>';
414  else print ': ';
415  print dol_print_date($object->date_closing, 'dayhour', 'tzserver');
416  if ($deltadateforuser) print ' '.$langs->trans("CurrentHour").' &nbsp; / &nbsp; '.dol_print_date($object->date_closing, "dayhour", 'tzuserrel').' &nbsp;'.$langs->trans("ClientHour");
417  if ($usetable) print '</td></tr>';
418  else print '<br>';
419  }
420 
421  // User conciliate
422  if (!empty($object->user_rappro))
423  {
424  if ($usetable) print '<tr><td class="titlefield">';
425  print $langs->trans("ConciliatedBy");
426  if ($usetable) print '</td><td>';
427  else print ': ';
428  if (is_object($object->user_rappro))
429  {
430  if ($object->user_rappro->id) print $object->user_rappro->getNomUrl(-1, '', 0, 0, 0);
431  else print $langs->trans("Unknown");
432  } else {
433  $userstatic = new User($db);
434  $userstatic->fetch($object->user_rappro);
435  if ($userstatic->id) print $userstatic->getNomUrl(1, '', 0, 0, 0);
436  else print $langs->trans("Unknown");
437  }
438  if ($usetable) print '</td></tr>';
439  else print '<br>';
440  }
441 
442  // Date conciliate
443  if (!empty($object->date_rappro))
444  {
445  if ($usetable) print '<tr><td class="titlefield">';
446  print $langs->trans("DateConciliating");
447  if ($usetable) print '</td><td>';
448  else print ': ';
449  print dol_print_date($object->date_rappro, 'dayhour', 'tzserver');
450  if ($deltadateforuser) print ' '.$langs->trans("CurrentHour").' &nbsp; / &nbsp; '.dol_print_date($object->date_rappro, "dayhour", 'tzuserrel').' &nbsp;'.$langs->trans("ClientHour");
451  if ($usetable) print '</td></tr>';
452  else print '<br>';
453  }
454 
455  // Date send
456  if (!empty($object->date_envoi))
457  {
458  if ($usetable) print '<tr><td class="titlefield">';
459  print $langs->trans("DateLastSend");
460  if ($usetable) print '</td><td>';
461  else print ': ';
462  print dol_print_date($object->date_envoi, 'dayhour', 'tzserver');
463  if ($deltadateforuser) print ' '.$langs->trans("CurrentHour").' &nbsp; / &nbsp; '.dol_print_date($object->date_envoi, "dayhour", 'tzuserrel').' &nbsp;'.$langs->trans("ClientHour");
464  if ($usetable) print '</td></tr>';
465  else print '<br>';
466  }
467 
468  if ($usetable) print '</table>';
469 }
470 
471 
480 function dolAddEmailTrackId($email, $trackingid)
481 {
482  $tmp = explode('@', $email);
483  return $tmp[0].'+'.$trackingid.'@'.(isset($tmp[1]) ? $tmp[1] : '');
484 }
485 
492 function isValidMailDomain($mail)
493 {
494  list($user, $domain) = explode("@", $mail, 2);
495  return ($domain ? isValidMXRecord($domain) : 0);
496 }
497 
511 function isValidUrl($url, $http = 0, $pass = 0, $port = 0, $path = 0, $query = 0, $anchor = 0)
512 {
513  $ValidUrl = 0;
514  $urlregex = '';
515 
516  // SCHEME
517  if ($http) $urlregex .= "^(http:\/\/|https:\/\/)";
518 
519  // USER AND PASS
520  if ($pass) $urlregex .= "([a-z0-9+!*(),;?&=\$_.-]+(\:[a-z0-9+!*(),;?&=\$_.-]+)?@)";
521 
522  // HOSTNAME OR IP
523  //$urlregex .= "[a-z0-9+\$_-]+(\.[a-z0-9+\$_-]+)*"; // x allowed (ex. http://localhost, http://routerlogin)
524  //$urlregex .= "[a-z0-9+\$_-]+(\.[a-z0-9+\$_-]+)+"; // x.x
525  $urlregex .= "([a-z0-9+\$_\\\:-])+(\.[a-z0-9+\$_-][a-z0-9+\$_-]+)*"; // x ou x.xx (2 x ou plus)
526  //use only one of the above
527 
528  // PORT
529  if ($port) $urlregex .= "(\:[0-9]{2,5})";
530  // PATH
531  if ($path) $urlregex .= "(\/([a-z0-9+\$_-]\.?)+)*\/";
532  // GET Query
533  if ($query) $urlregex .= "(\?[a-z+&\$_.-][a-z0-9;:@\/&%=+\$_.-]*)";
534  // ANCHOR
535  if ($anchor) $urlregex .= "(#[a-z_.-][a-z0-9+\$_.-]*)$";
536 
537  // check
538  if (preg_match('/'.$urlregex.'/i', $url))
539  {
540  $ValidUrl = 1;
541  }
542  //print $urlregex.' - '.$url.' - '.$ValidUrl;
543 
544  return $ValidUrl;
545 }
546 
553 function isValidVATID($company)
554 {
555  if ($company->isInEEC()) // Syntax check rules for EEC countries
556  {
557  /* Disabled because some companies can have an address in Irland and a vat number in France.
558  $vatprefix = $company->country_code;
559  if ($vatprefix == 'GR') $vatprefix = '(EL|GR)';
560  elseif ($vatprefix == 'MC') $vatprefix = 'FR'; // Monaco is using french VAT numbers
561  else $vatprefix = preg_quote($vatprefix, '/');*/
562  $vatprefix = '[a-zA-Z][a-zA-Z]';
563  if (!preg_match('/^'.$vatprefix.'[a-zA-Z0-9\-\.]{5,14}$/i', str_replace(' ', '', $company->tva_intra)))
564  {
565  return 0;
566  }
567  }
568 
569  return 1;
570 }
571 
579 function clean_url($url, $http = 1)
580 {
581  // Fixed by Matelli (see http://matelli.fr/showcases/patchs-dolibarr/fix-cleaning-url.html)
582  // To include the minus sign in a char class, we must not escape it but put it at the end of the class
583  // Also, there's no need of escape a dot sign in a class
584  $regs = array();
585  if (preg_match('/^(https?:[\\/]+)?([0-9A-Z.-]+\.[A-Z]{2,4})(:[0-9]+)?/i', $url, $regs))
586  {
587  $proto = $regs[1];
588  $domain = $regs[2];
589  $port = isset($regs[3]) ? $regs[3] : '';
590  //print $url." -> ".$proto." - ".$domain." - ".$port;
591  //$url = dol_string_nospecial(trim($url));
592  $url = trim($url);
593 
594  // Si http: defini on supprime le http (Si https on ne supprime pas)
595  $newproto = $proto;
596  if ($http == 0)
597  {
598  if (preg_match('/^http:[\\/]+/i', $url))
599  {
600  $url = preg_replace('/^http:[\\/]+/i', '', $url);
601  $newproto = '';
602  }
603  }
604 
605  // On passe le nom de domaine en minuscule
606  $CleanUrl = preg_replace('/^'.preg_quote($proto.$domain, '/').'/i', $newproto.strtolower($domain), $url);
607 
608  return $CleanUrl;
609  } else return $url;
610 }
611 
612 
613 
625 function dolObfuscateEmail($mail, $replace = "*", $nbreplace = 8, $nbdisplaymail = 4, $nbdisplaydomain = 3, $displaytld = true)
626 {
627  if (!isValidEmail($mail))return '';
628  $tab = explode('@', $mail);
629  $tab2 = explode('.', $tab[1]);
630  $string_replace = '';
631  $mail_name = $tab[0];
632  $mail_domaine = $tab2[0];
633  $mail_tld = '';
634 
635  $nbofelem = count($tab2);
636  for ($i = 1; $i < $nbofelem && $displaytld; $i++)
637  {
638  $mail_tld .= '.'.$tab2[$i];
639  }
640 
641  for ($i = 0; $i < $nbreplace; $i++) {
642  $string_replace .= $replace;
643  }
644 
645  if (strlen($mail_name) > $nbdisplaymail) {
646  $mail_name = substr($mail_name, 0, $nbdisplaymail);
647  }
648 
649  if (strlen($mail_domaine) > $nbdisplaydomain) {
650  $mail_domaine = substr($mail_domaine, strlen($mail_domaine) - $nbdisplaydomain);
651  }
652 
653  return $mail_name.$string_replace.$mail_domaine.$mail_tld;
654 }
655 
656 
666 function array2tr($data, $troptions = '', $tdoptions = '')
667 {
668  $text = '<tr '.$troptions.'>';
669  foreach ($data as $key => $item) {
670  $text .= '<td '.$tdoptions.'>'.$item.'</td>';
671  }
672  $text .= '</tr>';
673  return $text;
674 }
675 
686 function array2table($data, $tableMarkup = 1, $tableoptions = '', $troptions = '', $tdoptions = '')
687 {
688  $text = '';
689  if ($tableMarkup) $text = '<table '.$tableoptions.'>';
690  foreach ($data as $key => $item) {
691  if (is_array($item)) {
692  $text .= array2tr($item, $troptions, $tdoptions);
693  } else {
694  $text .= '<tr '.$troptions.'>';
695  $text .= '<td '.$tdoptions.'>'.$key.'</td>';
696  $text .= '<td '.$tdoptions.'>'.$item.'</td>';
697  $text .= '</tr>';
698  }
699  }
700  if ($tableMarkup) $text .= '</table>';
701  return $text;
702 }
703 
720 function get_next_value($db, $mask, $table, $field, $where = '', $objsoc = '', $date = '', $mode = 'next', $bentityon = true, $objuser = null, $forceentity = null)
721 {
722  global $conf, $user;
723 
724  if (!is_object($objsoc)) $valueforccc = $objsoc;
725  elseif ($table == "commande_fournisseur" || $table == "facture_fourn") $valueforccc = dol_string_unaccent($objsoc->code_fournisseur);
726  else $valueforccc = dol_string_unaccent($objsoc->code_client);
727 
728  $sharetable = $table;
729  if ($table == 'facture' || $table == 'invoice') $sharetable = 'invoicenumber'; // for getEntity function
730 
731  // Clean parameters
732  if ($date == '') $date = dol_now(); // We use local year and month of PHP server to search numbers
733  // but we should use local year and month of user
734 
735  // For debugging
736  //dol_syslog("mask=".$mask, LOG_DEBUG);
737  //include_once(DOL_DOCUMENT_ROOT.'/core/lib/date.lib.php');
738  //$mask='FA{yy}{mm}-{0000@99}';
739  //$date=dol_mktime(12, 0, 0, 1, 1, 1900);
740  //$date=dol_stringtotime('20130101');
741 
742  $hasglobalcounter = false;
743  $reg = array();
744  // Extract value for mask counter, mask raz and mask offset
745  if (preg_match('/\{(0+)([@\+][0-9\-\+\=]+)?([@\+][0-9\-\+\=]+)?\}/i', $mask, $reg))
746  {
747  $masktri = $reg[1].(!empty($reg[2]) ? $reg[2] : '').(!empty($reg[3]) ? $reg[3] : '');
748  $maskcounter = $reg[1];
749  $hasglobalcounter = true;
750  } else {
751  // setting some defaults so the rest of the code won't fail if there is a third party counter
752  $masktri = '00000';
753  $maskcounter = '00000';
754  }
755 
756  $maskraz = -1;
757  $maskoffset = 0;
758  $resetEveryMonth = false;
759  if (dol_strlen($maskcounter) < 3 && empty($conf->global->MAIN_COUNTER_WITH_LESS_3_DIGITS)) return 'ErrorCounterMustHaveMoreThan3Digits';
760 
761  // Extract value for third party mask counter
762  $regClientRef = array();
763  if (preg_match('/\{(c+)(0*)\}/i', $mask, $regClientRef))
764  {
765  $maskrefclient = $regClientRef[1].$regClientRef[2];
766  $maskrefclient_maskclientcode = $regClientRef[1];
767  $maskrefclient_maskcounter = $regClientRef[2];
768  $maskrefclient_maskoffset = 0; //default value of maskrefclient_counter offset
769  $maskrefclient_clientcode = substr($valueforccc, 0, dol_strlen($maskrefclient_maskclientcode)); //get n first characters of client code where n is length in mask
770  $maskrefclient_clientcode = str_pad($maskrefclient_clientcode, dol_strlen($maskrefclient_maskclientcode), "#", STR_PAD_RIGHT); //padding maskrefclient_clientcode for having exactly n characters in maskrefclient_clientcode
771  $maskrefclient_clientcode = dol_string_nospecial($maskrefclient_clientcode); //sanitize maskrefclient_clientcode for sql insert and sql select like
772  if (dol_strlen($maskrefclient_maskcounter) > 0 && dol_strlen($maskrefclient_maskcounter) < 3) return 'ErrorCounterMustHaveMoreThan3Digits';
773  } else $maskrefclient = '';
774 
775  // fail if there is neither a global nor a third party counter
776  if (!$hasglobalcounter && ($maskrefclient_maskcounter == ''))
777  {
778  return 'ErrorBadMask';
779  }
780 
781  // Extract value for third party type
782  $regType = array();
783  if (preg_match('/\{(t+)\}/i', $mask, $regType))
784  {
785  $masktype = $regType[1];
786  $masktype_value = substr(preg_replace('/^TE_/', '', $objsoc->typent_code), 0, dol_strlen($regType[1])); // get n first characters of thirdpaty typent_code (where n is length in mask)
787  $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
788  } else {
789  $masktype = '';
790  $masktype_value = '';
791  }
792 
793  // Extract value for user
794  $regType = array();
795  if (preg_match('/\{(u+)\}/i', $mask, $regType)) {
796  $lastname = 'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX';
797  if (is_object($objuser)) $lastname = $objuser->lastname;
798 
799  $maskuser = $regType[1];
800  $maskuser_value = substr($lastname, 0, dol_strlen($regType[1])); // get n first characters of user firstname (where n is length in mask)
801  $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
802  } else {
803  $maskuser = '';
804  $maskuser_value = '';
805  }
806 
807  // Personalized field {XXX-1} à {XXX-9}
808  $maskperso = array();
809  $maskpersonew = array();
810  $tmpmask = $mask;
811  $regKey = array();
812  while (preg_match('/\{([A-Z]+)\-([1-9])\}/', $tmpmask, $regKey))
813  {
814  $maskperso[$regKey[1]] = '{'.$regKey[1].'-'.$regKey[2].'}';
815  $maskpersonew[$regKey[1]] = str_pad('', $regKey[2], '_', STR_PAD_RIGHT);
816  $tmpmask = preg_replace('/\{'.$regKey[1].'\-'.$regKey[2].'\}/i', $maskpersonew[$regKey[1]], $tmpmask);
817  }
818 
819  if (strstr($mask, 'user_extra_'))
820  {
821  $start = "{user_extra_";
822  $end = "\}";
823  $extra = get_string_between($mask, "user_extra_", "}");
824  if (!empty($user->array_options['options_'.$extra])) {
825  $mask = preg_replace('#('.$start.')(.*?)('.$end.')#si', $user->array_options['options_'.$extra], $mask);
826  }
827  }
828  $maskwithonlyymcode = $mask;
829  $maskwithonlyymcode = preg_replace('/\{(0+)([@\+][0-9\-\+\=]+)?([@\+][0-9\-\+\=]+)?\}/i', $maskcounter, $maskwithonlyymcode);
830  $maskwithonlyymcode = preg_replace('/\{dd\}/i', 'dd', $maskwithonlyymcode);
831  $maskwithonlyymcode = preg_replace('/\{(c+)(0*)\}/i', $maskrefclient, $maskwithonlyymcode);
832  $maskwithonlyymcode = preg_replace('/\{(t+)\}/i', $masktype_value, $maskwithonlyymcode);
833  $maskwithonlyymcode = preg_replace('/\{(u+)\}/i', $maskuser_value, $maskwithonlyymcode);
834  foreach ($maskperso as $key => $val)
835  {
836  $maskwithonlyymcode = preg_replace('/'.preg_quote($val, '/').'/i', $maskpersonew[$key], $maskwithonlyymcode);
837  }
838  $maskwithnocode = $maskwithonlyymcode;
839  $maskwithnocode = preg_replace('/\{yyyy\}/i', 'yyyy', $maskwithnocode);
840  $maskwithnocode = preg_replace('/\{yy\}/i', 'yy', $maskwithnocode);
841  $maskwithnocode = preg_replace('/\{y\}/i', 'y', $maskwithnocode);
842  $maskwithnocode = preg_replace('/\{mm\}/i', 'mm', $maskwithnocode);
843  // Now maskwithnocode = 0000ddmmyyyyccc for example
844  // and maskcounter = 0000 for example
845  //print "maskwithonlyymcode=".$maskwithonlyymcode." maskwithnocode=".$maskwithnocode."\n<br>";
846  //var_dump($reg);
847 
848  // If an offset is asked
849  if (!empty($reg[2]) && preg_match('/^\+/', $reg[2])) $maskoffset = preg_replace('/^\+/', '', $reg[2]);
850  if (!empty($reg[3]) && preg_match('/^\+/', $reg[3])) $maskoffset = preg_replace('/^\+/', '', $reg[3]);
851 
852  // Define $sqlwhere
853  $sqlwhere = '';
854  $yearoffset = 0; // Use year of current $date by default
855  $yearoffsettype = false; // false: no reset, 0,-,=,+: reset at offset SOCIETE_FISCAL_MONTH_START, x=reset at offset x
856 
857  // If a restore to zero after a month is asked we check if there is already a value for this year.
858  if (!empty($reg[2]) && preg_match('/^@/', $reg[2])) $yearoffsettype = preg_replace('/^@/', '', $reg[2]);
859  if (!empty($reg[3]) && preg_match('/^@/', $reg[3])) $yearoffsettype = preg_replace('/^@/', '', $reg[3]);
860 
861  //print "yearoffset=".$yearoffset." yearoffsettype=".$yearoffsettype;
862  if (is_numeric($yearoffsettype) && $yearoffsettype >= 1)
863  $maskraz = $yearoffsettype; // For backward compatibility
864  elseif ($yearoffsettype === '0' || (!empty($yearoffsettype) && !is_numeric($yearoffsettype) && $conf->global->SOCIETE_FISCAL_MONTH_START > 1))
865  $maskraz = $conf->global->SOCIETE_FISCAL_MONTH_START;
866  //print "maskraz=".$maskraz; // -1=no reset
867 
868  if ($maskraz > 0) { // A reset is required
869  if ($maskraz == 99) {
870  $maskraz = date('m', $date);
871  $resetEveryMonth = true;
872  }
873  if ($maskraz > 12) return 'ErrorBadMaskBadRazMonth';
874 
875  // Define posy, posm and reg
876  if ($maskraz > 1) // if reset is not first month, we need month and year into mask
877  {
878  if (preg_match('/^(.*)\{(y+)\}\{(m+)\}/i', $maskwithonlyymcode, $reg)) { $posy = 2; $posm = 3; } elseif (preg_match('/^(.*)\{(m+)\}\{(y+)\}/i', $maskwithonlyymcode, $reg)) { $posy = 3; $posm = 2; } else return 'ErrorCantUseRazInStartedYearIfNoYearMonthInMask';
879 
880  if (dol_strlen($reg[$posy]) < 2) return 'ErrorCantUseRazWithYearOnOneDigit';
881  } else // if reset is for a specific month in year, we need year
882  {
883  if (preg_match('/^(.*)\{(m+)\}\{(y+)\}/i', $maskwithonlyymcode, $reg)) { $posy = 3; $posm = 2; } elseif (preg_match('/^(.*)\{(y+)\}\{(m+)\}/i', $maskwithonlyymcode, $reg)) { $posy = 2; $posm = 3; } elseif (preg_match('/^(.*)\{(y+)\}/i', $maskwithonlyymcode, $reg)) { $posy = 2; $posm = 0; } else return 'ErrorCantUseRazIfNoYearInMask';
884  }
885  // Define length
886  $yearlen = $posy ?dol_strlen($reg[$posy]) : 0;
887  $monthlen = $posm ?dol_strlen($reg[$posm]) : 0;
888  // Define pos
889  $yearpos = (dol_strlen($reg[1]) + 1);
890  $monthpos = ($yearpos + $yearlen);
891  if ($posy == 3 && $posm == 2) { // if month is before year
892  $monthpos = (dol_strlen($reg[1]) + 1);
893  $yearpos = ($monthpos + $monthlen);
894  }
895  //print "xxx ".$maskwithonlyymcode." maskraz=".$maskraz." posy=".$posy." yearlen=".$yearlen." yearpos=".$yearpos." posm=".$posm." monthlen=".$monthlen." monthpos=".$monthpos." yearoffsettype=".$yearoffsettype." resetEveryMonth=".$resetEveryMonth."\n";
896 
897  // Define $yearcomp and $monthcomp (that will be use in the select where to search max number)
898  $monthcomp = $maskraz;
899  $yearcomp = 0;
900 
901  if (!empty($yearoffsettype) && !is_numeric($yearoffsettype) && $yearoffsettype != '=') // $yearoffsettype is - or +
902  {
903  $currentyear = date("Y", $date);
904  $fiscaldate = dol_mktime('0', '0', '0', $maskraz, '1', $currentyear);
905  $newyeardate = dol_mktime('0', '0', '0', '1', '1', $currentyear);
906  $nextnewyeardate = dol_mktime('0', '0', '0', '1', '1', $currentyear + 1);
907  //echo 'currentyear='.$currentyear.' date='.dol_print_date($date, 'day').' fiscaldate='.dol_print_date($fiscaldate, 'day').'<br>';
908 
909  // If after or equal of current fiscal date
910  if ($date >= $fiscaldate)
911  {
912  // If before of next new year date
913  if ($date < $nextnewyeardate && $yearoffsettype == '+') $yearoffset = 1;
914  } // If after or equal of current new year date
915  elseif ($date >= $newyeardate && $yearoffsettype == '-') $yearoffset = -1;
916  } // For backward compatibility
917  elseif (date("m", $date) < $maskraz && empty($resetEveryMonth)) { $yearoffset = -1; } // If current month lower that month of return to zero, year is previous year
918 
919  if ($yearlen == 4) $yearcomp = sprintf("%04d", date("Y", $date) + $yearoffset);
920  elseif ($yearlen == 2) $yearcomp = sprintf("%02d", date("y", $date) + $yearoffset);
921  elseif ($yearlen == 1) $yearcomp = substr(date("y", $date), 2, 1) + $yearoffset;
922  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)
923  {
924  if ($yearlen == 4) $yearcomp1 = sprintf("%04d", date("Y", $date) + $yearoffset + 1);
925  elseif ($yearlen == 2) $yearcomp1 = sprintf("%02d", date("y", $date) + $yearoffset + 1);
926 
927  $sqlwhere .= "(";
928  $sqlwhere .= " (SUBSTRING(".$field.", ".$yearpos.", ".$yearlen.") = '".$db->escape($yearcomp)."'";
929  $sqlwhere .= " AND SUBSTRING(".$field.", ".$monthpos.", ".$monthlen.") >= '".str_pad($monthcomp, $monthlen, '0', STR_PAD_LEFT)."')";
930  $sqlwhere .= " OR";
931  $sqlwhere .= " (SUBSTRING(".$field.", ".$yearpos.", ".$yearlen.") = '".$db->escape($yearcomp1)."'";
932  $sqlwhere .= " AND SUBSTRING(".$field.", ".$monthpos.", ".$monthlen.") < '".str_pad($monthcomp, $monthlen, '0', STR_PAD_LEFT)."') ";
933  $sqlwhere .= ')';
934  } elseif ($resetEveryMonth) {
935  $sqlwhere .= "(SUBSTRING(".$field.", ".$yearpos.", ".$yearlen.") = '".$db->escape($yearcomp)."'";
936  $sqlwhere .= " AND SUBSTRING(".$field.", ".$monthpos.", ".$monthlen.") = '".str_pad($monthcomp, $monthlen, '0', STR_PAD_LEFT)."')";
937  } else { // reset is done on january
938  $sqlwhere .= '(SUBSTRING('.$field.', '.$yearpos.', '.$yearlen.") = '".$db->escape($yearcomp)."')";
939  }
940  }
941  //print "sqlwhere=".$sqlwhere." yearcomp=".$yearcomp."<br>\n"; // sqlwhere and yearcomp defined only if we ask a reset
942  //print "masktri=".$masktri." maskcounter=".$maskcounter." maskraz=".$maskraz." maskoffset=".$maskoffset."<br>\n";
943 
944  // Define $sqlstring
945  if (function_exists('mb_strrpos')) {
946  $posnumstart = mb_strrpos($maskwithnocode, $maskcounter, 0, 'UTF-8');
947  } else {
948  $posnumstart = strrpos($maskwithnocode, $maskcounter);
949  } // Pos of counter in final string (from 0 to ...)
950  if ($posnumstart < 0) return 'ErrorBadMaskFailedToLocatePosOfSequence';
951  $sqlstring = 'SUBSTRING('.$field.', '.($posnumstart + 1).', '.dol_strlen($maskcounter).')';
952 
953  // Define $maskLike
954  $maskLike = dol_string_nospecial($mask);
955  $maskLike = str_replace("%", "_", $maskLike);
956 
957  // Replace protected special codes with matching number of _ as wild card caracter
958  $maskLike = preg_replace('/\{yyyy\}/i', '____', $maskLike);
959  $maskLike = preg_replace('/\{yy\}/i', '__', $maskLike);
960  $maskLike = preg_replace('/\{y\}/i', '_', $maskLike);
961  $maskLike = preg_replace('/\{mm\}/i', '__', $maskLike);
962  $maskLike = preg_replace('/\{dd\}/i', '__', $maskLike);
963  $maskLike = str_replace(dol_string_nospecial('{'.$masktri.'}'), str_pad("", dol_strlen($maskcounter), "_"), $maskLike);
964  if ($maskrefclient) $maskLike = str_replace(dol_string_nospecial('{'.$maskrefclient.'}'), str_pad("", dol_strlen($maskrefclient), "_"), $maskLike);
965  if ($masktype) $maskLike = str_replace(dol_string_nospecial('{'.$masktype.'}'), $masktype_value, $maskLike);
966  if ($maskuser) $maskLike = str_replace(dol_string_nospecial('{'.$maskuser.'}'), $maskuser_value, $maskLike);
967  foreach ($maskperso as $key => $val)
968  {
969  $maskLike = str_replace(dol_string_nospecial($maskperso[$key]), $maskpersonew[$key], $maskLike);
970  }
971 
972  // Get counter in database
973  $counter = 0;
974  $sql = "SELECT MAX(".$sqlstring.") as val";
975  $sql .= " FROM ".MAIN_DB_PREFIX.$table;
976  $sql .= " WHERE ".$field." LIKE '".$db->escape($maskLike)."'";
977  $sql .= " AND ".$field." NOT LIKE '(PROV%)'";
978  if ($bentityon) // only if entity enable
979  $sql .= " AND entity IN (".getEntity($sharetable).")";
980  elseif (!empty($forceentity))
981  $sql .= " AND entity IN (".$forceentity.")";
982  if ($where) $sql .= $where;
983  if ($sqlwhere) $sql .= ' AND '.$sqlwhere;
984 
985  //print $sql.'<br>';
986  dol_syslog("functions2::get_next_value mode=".$mode."", LOG_DEBUG);
987  $resql = $db->query($sql);
988  if ($resql)
989  {
990  $obj = $db->fetch_object($resql);
991  $counter = $obj->val;
992  } else dol_print_error($db);
993 
994  // Check if we must force counter to maskoffset
995  if (empty($counter)) $counter = $maskoffset;
996  elseif (preg_match('/[^0-9]/i', $counter))
997  {
998  $counter = 0;
999  dol_syslog("Error, the last counter found is '".$counter."' so is not a numeric value. We will restart to 1.", LOG_ERR);
1000  } elseif ($counter < $maskoffset && empty($conf->global->MAIN_NUMBERING_OFFSET_ONLY_FOR_FIRST)) $counter = $maskoffset;
1001 
1002  if ($mode == 'last') // We found value for counter = last counter value. Now need to get corresponding ref of invoice.
1003  {
1004  $counterpadded = str_pad($counter, dol_strlen($maskcounter), "0", STR_PAD_LEFT);
1005 
1006  // Define $maskLike
1007  $maskLike = dol_string_nospecial($mask);
1008  $maskLike = str_replace("%", "_", $maskLike);
1009  // Replace protected special codes with matching number of _ as wild card caracter
1010  $maskLike = preg_replace('/\{yyyy\}/i', '____', $maskLike);
1011  $maskLike = preg_replace('/\{yy\}/i', '__', $maskLike);
1012  $maskLike = preg_replace('/\{y\}/i', '_', $maskLike);
1013  $maskLike = preg_replace('/\{mm\}/i', '__', $maskLike);
1014  $maskLike = preg_replace('/\{dd\}/i', '__', $maskLike);
1015  $maskLike = str_replace(dol_string_nospecial('{'.$masktri.'}'), $counterpadded, $maskLike);
1016  if ($maskrefclient) $maskLike = str_replace(dol_string_nospecial('{'.$maskrefclient.'}'), str_pad("", dol_strlen($maskrefclient), "_"), $maskLike);
1017  if ($masktype) $maskLike = str_replace(dol_string_nospecial('{'.$masktype.'}'), $masktype_value, $maskLike);
1018  if ($maskuser) $maskLike = str_replace(dol_string_nospecial('{'.$maskuser.'}'), $maskuser_value, $maskLike);
1019 
1020  $ref = '';
1021  $sql = "SELECT ".$field." as ref";
1022  $sql .= " FROM ".MAIN_DB_PREFIX.$table;
1023  $sql .= " WHERE ".$field." LIKE '".$db->escape($maskLike)."'";
1024  $sql .= " AND ".$field." NOT LIKE '%PROV%'";
1025  if ($bentityon) // only if entity enable
1026  $sql .= " AND entity IN (".getEntity($sharetable).")";
1027  elseif (!empty($forceentity))
1028  $sql .= " AND entity IN (".$forceentity.")";
1029  if ($where) $sql .= $where;
1030  if ($sqlwhere) $sql .= ' AND '.$sqlwhere;
1031 
1032  dol_syslog("functions2::get_next_value mode=".$mode."", LOG_DEBUG);
1033  $resql = $db->query($sql);
1034  if ($resql)
1035  {
1036  $obj = $db->fetch_object($resql);
1037  if ($obj) $ref = $obj->ref;
1038  } else dol_print_error($db);
1039 
1040  $numFinal = $ref;
1041  } elseif ($mode == 'next') {
1042  $counter++;
1043 
1044  // If value for $counter has a length higher than $maskcounter chars
1045  if ($counter >= pow(10, dol_strlen($maskcounter)))
1046  {
1047  $counter = 'ErrorMaxNumberReachForThisMask';
1048  }
1049 
1050  if (!empty($maskrefclient_maskcounter))
1051  {
1052  //print "maskrefclient_maskcounter=".$maskrefclient_maskcounter." maskwithnocode=".$maskwithnocode." maskrefclient=".$maskrefclient."\n<br>";
1053 
1054  // Define $sqlstring
1055  $maskrefclient_posnumstart = strpos($maskwithnocode, $maskrefclient_maskcounter, strpos($maskwithnocode, $maskrefclient)); // Pos of counter in final string (from 0 to ...)
1056  if ($maskrefclient_posnumstart <= 0) return 'ErrorBadMask';
1057  $maskrefclient_sqlstring = 'SUBSTRING('.$field.', '.($maskrefclient_posnumstart + 1).', '.dol_strlen($maskrefclient_maskcounter).')';
1058  //print "x".$sqlstring;
1059 
1060  // Define $maskrefclient_maskLike
1061  $maskrefclient_maskLike = dol_string_nospecial($mask);
1062  $maskrefclient_maskLike = str_replace("%", "_", $maskrefclient_maskLike);
1063  // Replace protected special codes with matching number of _ as wild card caracter
1064  $maskrefclient_maskLike = str_replace(dol_string_nospecial('{yyyy}'), '____', $maskrefclient_maskLike);
1065  $maskrefclient_maskLike = str_replace(dol_string_nospecial('{yy}'), '__', $maskrefclient_maskLike);
1066  $maskrefclient_maskLike = str_replace(dol_string_nospecial('{y}'), '_', $maskrefclient_maskLike);
1067  $maskrefclient_maskLike = str_replace(dol_string_nospecial('{mm}'), '__', $maskrefclient_maskLike);
1068  $maskrefclient_maskLike = str_replace(dol_string_nospecial('{dd}'), '__', $maskrefclient_maskLike);
1069  $maskrefclient_maskLike = str_replace(dol_string_nospecial('{'.$masktri.'}'), str_pad("", dol_strlen($maskcounter), "_"), $maskrefclient_maskLike);
1070  $maskrefclient_maskLike = str_replace(dol_string_nospecial('{'.$maskrefclient.'}'), $maskrefclient_clientcode.str_pad("", dol_strlen($maskrefclient_maskcounter), "_"), $maskrefclient_maskLike);
1071 
1072  // Get counter in database
1073  $maskrefclient_counter = 0;
1074  $maskrefclient_sql = "SELECT MAX(".$maskrefclient_sqlstring.") as val";
1075  $maskrefclient_sql .= " FROM ".MAIN_DB_PREFIX.$table;
1076  //$sql.= " WHERE ".$field." not like '(%'";
1077  $maskrefclient_sql .= " WHERE ".$field." LIKE '".$db->escape($maskrefclient_maskLike)."'";
1078  if ($bentityon) // only if entity enable
1079  $maskrefclient_sql .= " AND entity IN (".getEntity($sharetable).")";
1080  elseif (!empty($forceentity))
1081  $sql .= " AND entity IN (".$forceentity.")";
1082  if ($where) $maskrefclient_sql .= $where; //use the same optional where as general mask
1083  if ($sqlwhere) $maskrefclient_sql .= ' AND '.$sqlwhere; //use the same sqlwhere as general mask
1084  $maskrefclient_sql .= ' AND (SUBSTRING('.$field.', '.(strpos($maskwithnocode, $maskrefclient) + 1).', '.dol_strlen($maskrefclient_maskclientcode).")='".$db->escape($maskrefclient_clientcode)."')";
1085 
1086  dol_syslog("functions2::get_next_value maskrefclient", LOG_DEBUG);
1087  $maskrefclient_resql = $db->query($maskrefclient_sql);
1088  if ($maskrefclient_resql)
1089  {
1090  $maskrefclient_obj = $db->fetch_object($maskrefclient_resql);
1091  $maskrefclient_counter = $maskrefclient_obj->val;
1092  } else dol_print_error($db);
1093 
1094  if (empty($maskrefclient_counter) || preg_match('/[^0-9]/i', $maskrefclient_counter)) $maskrefclient_counter = $maskrefclient_maskoffset;
1095  $maskrefclient_counter++;
1096  }
1097 
1098  // Build numFinal
1099  $numFinal = $mask;
1100 
1101  // We replace special codes except refclient
1102  if (!empty($yearoffsettype) && !is_numeric($yearoffsettype) && $yearoffsettype != '=') // yearoffsettype is - or +, so we don't want current year
1103  {
1104  $numFinal = preg_replace('/\{yyyy\}/i', date("Y", $date) + $yearoffset, $numFinal);
1105  $numFinal = preg_replace('/\{yy\}/i', date("y", $date) + $yearoffset, $numFinal);
1106  $numFinal = preg_replace('/\{y\}/i', substr(date("y", $date), 1, 1) + $yearoffset, $numFinal);
1107  } else // we want yyyy to be current year
1108  {
1109  $numFinal = preg_replace('/\{yyyy\}/i', date("Y", $date), $numFinal);
1110  $numFinal = preg_replace('/\{yy\}/i', date("y", $date), $numFinal);
1111  $numFinal = preg_replace('/\{y\}/i', substr(date("y", $date), 1, 1), $numFinal);
1112  }
1113  $numFinal = preg_replace('/\{mm\}/i', date("m", $date), $numFinal);
1114  $numFinal = preg_replace('/\{dd\}/i', date("d", $date), $numFinal);
1115 
1116  // Now we replace the counter
1117  $maskbefore = '{'.$masktri.'}';
1118  $maskafter = str_pad($counter, dol_strlen($maskcounter), "0", STR_PAD_LEFT);
1119  //print 'x'.$maskbefore.'-'.$maskafter.'y';
1120  $numFinal = str_replace($maskbefore, $maskafter, $numFinal);
1121 
1122  // Now we replace the refclient
1123  if ($maskrefclient)
1124  {
1125  //print "maskrefclient=".$maskrefclient." maskwithonlyymcode=".$maskwithonlyymcode." maskwithnocode=".$maskwithnocode." maskrefclient_clientcode=".$maskrefclient_clientcode."\n<br>";exit;
1126  $maskrefclient_maskbefore = '{'.$maskrefclient.'}';
1127  $maskrefclient_maskafter = $maskrefclient_clientcode.str_pad($maskrefclient_counter, dol_strlen($maskrefclient_maskcounter), "0", STR_PAD_LEFT);
1128  $numFinal = str_replace($maskrefclient_maskbefore, $maskrefclient_maskafter, $numFinal);
1129  }
1130 
1131  // Now we replace the type
1132  if ($masktype)
1133  {
1134  $masktype_maskbefore = '{'.$masktype.'}';
1135  $masktype_maskafter = $masktype_value;
1136  $numFinal = str_replace($masktype_maskbefore, $masktype_maskafter, $numFinal);
1137  }
1138 
1139  // Now we replace the user
1140  if ($maskuser)
1141  {
1142  $maskuser_maskbefore = '{'.$maskuser.'}';
1143  $maskuser_maskafter = $maskuser_value;
1144  $numFinal = str_replace($maskuser_maskbefore, $maskuser_maskafter, $numFinal);
1145  }
1146  }
1147 
1148  dol_syslog("functions2::get_next_value return ".$numFinal, LOG_DEBUG);
1149  return $numFinal;
1150 }
1151 
1160 function get_string_between($string, $start, $end)
1161 {
1162  $string = " ".$string;
1163  $ini = strpos($string, $start);
1164  if ($ini == 0) return "";
1165  $ini += strlen($start);
1166  $len = strpos($string, $end, $ini) - $ini;
1167  return substr($string, $ini, $len);
1168 }
1169 
1177 function check_value($mask, $value)
1178 {
1179  $result = 0;
1180 
1181  $hasglobalcounter = false;
1182  // Extract value for mask counter, mask raz and mask offset
1183  $reg = array();
1184  if (preg_match('/\{(0+)([@\+][0-9]+)?([@\+][0-9]+)?\}/i', $mask, $reg)) {
1185  $masktri = $reg[1].(isset($reg[2]) ? $reg[2] : '').(isset($reg[3]) ? $reg[3] : '');
1186  $maskcounter = $reg[1];
1187  $hasglobalcounter = true;
1188  } else {
1189  // setting some defaults so the rest of the code won't fail if there is a third party counter
1190  $masktri = '00000';
1191  $maskcounter = '00000';
1192  }
1193  $maskraz = -1;
1194  $maskoffset = 0;
1195  if (dol_strlen($maskcounter) < 3) return 'ErrorCounterMustHaveMoreThan3Digits';
1196 
1197  // Extract value for third party mask counter
1198  $regClientRef = array();
1199  if (preg_match('/\{(c+)(0*)\}/i', $mask, $regClientRef))
1200  {
1201  $maskrefclient = $regClientRef[1].$regClientRef[2];
1202  $maskrefclient_maskclientcode = $regClientRef[1];
1203  $maskrefclient_maskcounter = $regClientRef[2];
1204  $maskrefclient_maskoffset = 0; //default value of maskrefclient_counter offset
1205  $maskrefclient_clientcode = substr('', 0, dol_strlen($maskrefclient_maskclientcode)); //get n first characters of client code to form maskrefclient_clientcode
1206  $maskrefclient_clientcode = str_pad($maskrefclient_clientcode, dol_strlen($maskrefclient_maskclientcode), "#", STR_PAD_RIGHT); //padding maskrefclient_clientcode for having exactly n characters in maskrefclient_clientcode
1207  $maskrefclient_clientcode = dol_string_nospecial($maskrefclient_clientcode); //sanitize maskrefclient_clientcode for sql insert and sql select like
1208  if (dol_strlen($maskrefclient_maskcounter) > 0 && dol_strlen($maskrefclient_maskcounter) < 3) return 'ErrorCounterMustHaveMoreThan3Digits';
1209  } else $maskrefclient = '';
1210 
1211  // fail if there is neither a global nor a third party counter
1212  if (!$hasglobalcounter && ($maskrefclient_maskcounter == ''))
1213  {
1214  return 'ErrorBadMask';
1215  }
1216 
1217  $maskwithonlyymcode = $mask;
1218  $maskwithonlyymcode = preg_replace('/\{(0+)([@\+][0-9]+)?([@\+][0-9]+)?\}/i', $maskcounter, $maskwithonlyymcode);
1219  $maskwithonlyymcode = preg_replace('/\{dd\}/i', 'dd', $maskwithonlyymcode);
1220  $maskwithonlyymcode = preg_replace('/\{(c+)(0*)\}/i', $maskrefclient, $maskwithonlyymcode);
1221  $maskwithnocode = $maskwithonlyymcode;
1222  $maskwithnocode = preg_replace('/\{yyyy\}/i', 'yyyy', $maskwithnocode);
1223  $maskwithnocode = preg_replace('/\{yy\}/i', 'yy', $maskwithnocode);
1224  $maskwithnocode = preg_replace('/\{y\}/i', 'y', $maskwithnocode);
1225  $maskwithnocode = preg_replace('/\{mm\}/i', 'mm', $maskwithnocode);
1226  // Now maskwithnocode = 0000ddmmyyyyccc for example
1227  // and maskcounter = 0000 for example
1228  //print "maskwithonlyymcode=".$maskwithonlyymcode." maskwithnocode=".$maskwithnocode."\n<br>";
1229 
1230  // If an offset is asked
1231  if (!empty($reg[2]) && preg_match('/^\+/', $reg[2])) $maskoffset = preg_replace('/^\+/', '', $reg[2]);
1232  if (!empty($reg[3]) && preg_match('/^\+/', $reg[3])) $maskoffset = preg_replace('/^\+/', '', $reg[3]);
1233 
1234  // Define $sqlwhere
1235 
1236  // If a restore to zero after a month is asked we check if there is already a value for this year.
1237  if (!empty($reg[2]) && preg_match('/^@/', $reg[2])) $maskraz = preg_replace('/^@/', '', $reg[2]);
1238  if (!empty($reg[3]) && preg_match('/^@/', $reg[3])) $maskraz = preg_replace('/^@/', '', $reg[3]);
1239  if ($maskraz >= 0)
1240  {
1241  if ($maskraz == 99) {
1242  $maskraz = date('m');
1243  $resetEveryMonth = true;
1244  }
1245  if ($maskraz > 12) return 'ErrorBadMaskBadRazMonth';
1246 
1247  // Define reg
1248  if ($maskraz > 1 && !preg_match('/^(.*)\{(y+)\}\{(m+)\}/i', $maskwithonlyymcode, $reg)) return 'ErrorCantUseRazInStartedYearIfNoYearMonthInMask';
1249  if ($maskraz <= 1 && !preg_match('/^(.*)\{(y+)\}/i', $maskwithonlyymcode, $reg)) return 'ErrorCantUseRazIfNoYearInMask';
1250  //print "x".$maskwithonlyymcode." ".$maskraz;
1251  }
1252  //print "masktri=".$masktri." maskcounter=".$maskcounter." maskwithonlyymcode=".$maskwithonlyymcode." maskwithnocode=".$maskwithnocode." maskraz=".$maskraz." maskoffset=".$maskoffset."<br>\n";
1253 
1254  if (function_exists('mb_strrpos')) {
1255  $posnumstart = mb_strrpos($maskwithnocode, $maskcounter, 0, 'UTF-8');
1256  }
1257  else {
1258  $posnumstart = strrpos($maskwithnocode, $maskcounter);
1259  } // Pos of counter in final string (from 0 to ...)
1260  if ($posnumstart < 0) return 'ErrorBadMaskFailedToLocatePosOfSequence';
1261 
1262  // Check we have a number in $value at position ($posnumstart+1).', '.dol_strlen($maskcounter)
1263  // TODO
1264 
1265  // Check length
1266  $len = dol_strlen($maskwithnocode);
1267  if (dol_strlen($value) != $len) $result = -1;
1268 
1269  dol_syslog("functions2::check_value result=".$result, LOG_DEBUG);
1270  return $result;
1271 }
1272 
1281 function binhex($bin, $pad = false, $upper = false)
1282 {
1283  $last = dol_strlen($bin) - 1;
1284  for ($i = 0; $i <= $last; $i++) { $x += $bin[$last - $i] * pow(2, $i); }
1285  $x = dechex($x);
1286  if ($pad) { while (dol_strlen($x) < intval(dol_strlen($bin)) / 4) { $x = "0$x"; } }
1287  if ($upper) { $x = strtoupper($x); }
1288  return $x;
1289 }
1290 
1297 function hexbin($hexa)
1298 {
1299  $bin = '';
1300  $strLength = dol_strlen($hexa);
1301  for ($i = 0; $i < $strLength; $i++)
1302  {
1303  $bin .= str_pad(decbin(hexdec($hexa[$i])), 4, '0', STR_PAD_LEFT);
1304  }
1305  return $bin;
1306 }
1307 
1314 function numero_semaine($time)
1315 {
1316  $stime = strftime('%Y-%m-%d', $time);
1317 
1318  if (preg_match('/^([0-9]+)\-([0-9]+)\-([0-9]+)\s?([0-9]+)?:?([0-9]+)?/i', $stime, $reg))
1319  {
1320  // Date est au format 'YYYY-MM-DD' ou 'YYYY-MM-DD HH:MM:SS'
1321  $annee = $reg[1];
1322  $mois = $reg[2];
1323  $jour = $reg[3];
1324  }
1325 
1326  /*
1327  * Norme ISO-8601:
1328  * - La semaine 1 de toute annee est celle qui contient le 4 janvier ou que la semaine 1 de toute annee est celle qui contient le 1er jeudi de janvier.
1329  * - La majorite des annees ont 52 semaines mais les annees qui commence un jeudi et les annees bissextiles commencant un mercredi en possede 53.
1330  * - Le 1er jour de la semaine est le Lundi
1331  */
1332 
1333  // Definition du Jeudi de la semaine
1334  if (date("w", mktime(12, 0, 0, $mois, $jour, $annee)) == 0) // Dimanche
1335  $jeudiSemaine = mktime(12, 0, 0, $mois, $jour, $annee) - 3 * 24 * 60 * 60;
1336  elseif (date("w", mktime(12, 0, 0, $mois, $jour, $annee)) < 4) // du Lundi au Mercredi
1337  $jeudiSemaine = mktime(12, 0, 0, $mois, $jour, $annee) + (4 - date("w", mktime(12, 0, 0, $mois, $jour, $annee))) * 24 * 60 * 60;
1338  elseif (date("w", mktime(12, 0, 0, $mois, $jour, $annee)) > 4) // du Vendredi au Samedi
1339  $jeudiSemaine = mktime(12, 0, 0, $mois, $jour, $annee) - (date("w", mktime(12, 0, 0, $mois, $jour, $annee)) - 4) * 24 * 60 * 60;
1340  else // Jeudi
1341  $jeudiSemaine = mktime(12, 0, 0, $mois, $jour, $annee);
1342 
1343  // Definition du premier Jeudi de l'annee
1344  if (date("w", mktime(12, 0, 0, 1, 1, date("Y", $jeudiSemaine))) == 0) // Dimanche
1345  {
1346  $premierJeudiAnnee = mktime(12, 0, 0, 1, 1, date("Y", $jeudiSemaine)) + 4 * 24 * 60 * 60;
1347  } elseif (date("w", mktime(12, 0, 0, 1, 1, date("Y", $jeudiSemaine))) < 4) // du Lundi au Mercredi
1348  {
1349  $premierJeudiAnnee = mktime(12, 0, 0, 1, 1, date("Y", $jeudiSemaine)) + (4 - date("w", mktime(12, 0, 0, 1, 1, date("Y", $jeudiSemaine)))) * 24 * 60 * 60;
1350  } elseif (date("w", mktime(12, 0, 0, 1, 1, date("Y", $jeudiSemaine))) > 4) // du Vendredi au Samedi
1351  {
1352  $premierJeudiAnnee = mktime(12, 0, 0, 1, 1, date("Y", $jeudiSemaine)) + (7 - (date("w", mktime(12, 0, 0, 1, 1, date("Y", $jeudiSemaine))) - 4)) * 24 * 60 * 60;
1353  } else // Jeudi
1354  {
1355  $premierJeudiAnnee = mktime(12, 0, 0, 1, 1, date("Y", $jeudiSemaine));
1356  }
1357 
1358  // Definition du numero de semaine: nb de jours entre "premier Jeudi de l'annee" et "Jeudi de la semaine";
1359  $numeroSemaine = (
1360  (
1361  date("z", mktime(12, 0, 0, date("m", $jeudiSemaine), date("d", $jeudiSemaine), date("Y", $jeudiSemaine)))
1362  -
1363  date("z", mktime(12, 0, 0, date("m", $premierJeudiAnnee), date("d", $premierJeudiAnnee), date("Y", $premierJeudiAnnee)))
1364  ) / 7
1365  ) + 1;
1366 
1367  // Cas particulier de la semaine 53
1368  if ($numeroSemaine == 53)
1369  {
1370  // Les annees qui commence un Jeudi et les annees bissextiles commencant un Mercredi en possede 53
1371  if (date("w", mktime(12, 0, 0, 1, 1, date("Y", $jeudiSemaine))) == 4 || (date("w", mktime(12, 0, 0, 1, 1, date("Y", $jeudiSemaine))) == 3 && date("z", mktime(12, 0, 0, 12, 31, date("Y", $jeudiSemaine))) == 365))
1372  {
1373  $numeroSemaine = 53;
1374  } else {
1375  $numeroSemaine = 1;
1376  }
1377  }
1378 
1379  //echo $jour."-".$mois."-".$annee." (".date("d-m-Y",$premierJeudiAnnee)." - ".date("d-m-Y",$jeudiSemaine).") -> ".$numeroSemaine."<BR>";
1380 
1381  return sprintf("%02d", $numeroSemaine);
1382 }
1383 
1392 function weight_convert($weight, &$from_unit, $to_unit)
1393 {
1394  /* Pour convertire 320 gr en Kg appeler
1395  * $f = -3
1396  * weigh_convert(320, $f, 0) retournera 0.32
1397  *
1398  */
1399  while ($from_unit <> $to_unit)
1400  {
1401  if ($from_unit > $to_unit)
1402  {
1403  $weight = $weight * 10;
1404  $from_unit = $from_unit - 1;
1405  $weight = weight_convert($weight, $from_unit, $to_unit);
1406  }
1407  if ($from_unit < $to_unit)
1408  {
1409  $weight = $weight / 10;
1410  $from_unit = $from_unit + 1;
1411  $weight = weight_convert($weight, $from_unit, $to_unit);
1412  }
1413  }
1414 
1415  return $weight;
1416 }
1417 
1429 function dol_set_user_param($db, $conf, &$user, $tab)
1430 {
1431  // Verification parametres
1432  if (count($tab) < 1) return -1;
1433 
1434  $db->begin();
1435 
1436  // We remove old parameters for all keys in $tab
1437  $sql = "DELETE FROM ".MAIN_DB_PREFIX."user_param";
1438  $sql .= " WHERE fk_user = ".$user->id;
1439  $sql .= " AND entity = ".$conf->entity;
1440  $sql .= " AND param in (";
1441  $i = 0;
1442  foreach ($tab as $key => $value)
1443  {
1444  if ($i > 0) $sql .= ',';
1445  $sql .= "'".$db->escape($key)."'";
1446  $i++;
1447  }
1448  $sql .= ")";
1449  dol_syslog("functions2.lib::dol_set_user_param", LOG_DEBUG);
1450 
1451  $resql = $db->query($sql);
1452  if (!$resql)
1453  {
1454  dol_print_error($db);
1455  $db->rollback();
1456  return -1;
1457  }
1458 
1459  foreach ($tab as $key => $value)
1460  {
1461  // Set new parameters
1462  if ($value)
1463  {
1464  $sql = "INSERT INTO ".MAIN_DB_PREFIX."user_param(fk_user,entity,param,value)";
1465  $sql .= " VALUES (".$user->id.",".$conf->entity.",";
1466  $sql .= " '".$db->escape($key)."','".$db->escape($value)."')";
1467 
1468  dol_syslog("functions2.lib::dol_set_user_param", LOG_DEBUG);
1469  $result = $db->query($sql);
1470  if (!$result)
1471  {
1472  dol_print_error($db);
1473  $db->rollback();
1474  return -1;
1475  }
1476  $user->conf->$key = $value;
1477  //print "key=".$key." user->conf->key=".$user->conf->$key;
1478  } else {
1479  unset($user->conf->$key);
1480  }
1481  }
1482 
1483  $db->commit();
1484  return 1;
1485 }
1486 
1494 function dol_print_reduction($reduction, $langs)
1495 {
1496  $string = '';
1497  if ($reduction == 100)
1498  {
1499  $string = $langs->transnoentities("Offered");
1500  } else {
1501  $string = vatrate($reduction, true);
1502  }
1503 
1504  return $string;
1505 }
1506 
1514 function version_os($option = '')
1515 {
1516  if ($option == 'smr') $osversion = php_uname('s').' '.php_uname('m').' '.php_uname('r');
1517  else $osversion = php_uname();
1518  return $osversion;
1519 }
1520 
1527 function version_php()
1528 {
1529  return phpversion();
1530 }
1531 
1539 {
1540  return DOL_VERSION;
1541 }
1542 
1549 {
1550  return $_SERVER["SERVER_SOFTWARE"];
1551 }
1552 
1561 function getListOfModels($db, $type, $maxfilenamelength = 0)
1562 {
1563  global $conf, $langs;
1564  $liste = array();
1565  $found = 0;
1566  $dirtoscan = '';
1567 
1568  $sql = "SELECT nom as id, nom as doc_template_name, libelle as label, description as description";
1569  $sql .= " FROM ".MAIN_DB_PREFIX."document_model";
1570  $sql .= " WHERE type = '".$db->escape($type)."'";
1571  $sql .= " AND entity IN (0,".$conf->entity.")";
1572  $sql .= " ORDER BY description DESC";
1573 
1574  dol_syslog('/core/lib/function2.lib.php::getListOfModels', LOG_DEBUG);
1575  $resql = $db->query($sql);
1576  if ($resql)
1577  {
1578  $num = $db->num_rows($resql);
1579  $i = 0;
1580  while ($i < $num)
1581  {
1582  $found = 1;
1583 
1584  $obj = $db->fetch_object($resql);
1585 
1586  // If this generation module needs to scan a directory, then description field is filled
1587  // with the constant that contains list of directories to scan (COMPANY_ADDON_PDF_ODT_PATH, ...).
1588  if (!empty($obj->description)) // A list of directories to scan is defined
1589  {
1590  include_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
1591 
1592  $const = $obj->description;
1593  //irtoscan.=($dirtoscan?',':'').preg_replace('/[\r\n]+/',',',trim($conf->global->$const));
1594  $dirtoscan = preg_replace('/[\r\n]+/', ',', trim($conf->global->$const));
1595 
1596  $listoffiles = array();
1597 
1598  // Now we add models found in directories scanned
1599  $listofdir = explode(',', $dirtoscan);
1600  foreach ($listofdir as $key=>$tmpdir)
1601  {
1602  $tmpdir = trim($tmpdir);
1603  $tmpdir = preg_replace('/DOL_DATA_ROOT/', DOL_DATA_ROOT, $tmpdir);
1604  if (!$tmpdir) { unset($listofdir[$key]); continue; }
1605  if (is_dir($tmpdir))
1606  {
1607  // all type of template is allowed
1608  $tmpfiles = dol_dir_list($tmpdir, 'files', 0, '', '', 'name', SORT_ASC, 0);
1609  if (count($tmpfiles)) $listoffiles = array_merge($listoffiles, $tmpfiles);
1610  }
1611  }
1612 
1613  if (count($listoffiles))
1614  {
1615  foreach ($listoffiles as $record)
1616  {
1617  $max = ($maxfilenamelength ? $maxfilenamelength : 28);
1618  $liste[$obj->id.':'.$record['fullname']] = dol_trunc($record['name'], $max, 'middle');
1619  }
1620  } else {
1621  $liste[0] = $obj->label.': '.$langs->trans("None");
1622  }
1623  } else {
1624  if ($type == 'member' && $obj->doc_template_name == 'standard') // Special case, if member template, we add variant per format
1625  {
1626  global $_Avery_Labels;
1627  include_once DOL_DOCUMENT_ROOT.'/core/lib/format_cards.lib.php';
1628  foreach ($_Avery_Labels as $key => $val)
1629  {
1630  $liste[$obj->id.':'.$key] = ($obj->label ? $obj->label : $obj->doc_template_name).' '.$val['name'];
1631  }
1632  } else {
1633  // Common usage
1634  $liste[$obj->id] = $obj->label ? $obj->label : $obj->doc_template_name;
1635  }
1636  }
1637  $i++;
1638  }
1639  } else {
1640  dol_print_error($db);
1641  return -1;
1642  }
1643 
1644  if ($found) return $liste;
1645  else return 0;
1646 }
1647 
1655 function is_ip($ip)
1656 {
1657  // First we test if it is a valid IPv4
1658  if (filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4)) {
1659  // Then we test if it is a private range
1660  if (!filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_NO_PRIV_RANGE)) return 2;
1661 
1662  // Then we test if it is a reserved range
1663  if (!filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_NO_RES_RANGE)) return 0;
1664 
1665  return 1;
1666  }
1667 
1668  return 0;
1669 }
1670 
1678 function dol_buildlogin($lastname, $firstname)
1679 {
1680  global $conf;
1681 
1682  //$conf->global->MAIN_BUILD_LOGIN_RULE = 'f.lastname';
1683  if (!empty($conf->global->MAIN_BUILD_LOGIN_RULE) && $conf->global->MAIN_BUILD_LOGIN_RULE == 'f.lastname') { // f.lastname
1684  $login = strtolower(dol_string_unaccent(dol_trunc($firstname, 1, 'right', 'UTF-8', 1)));
1685  $login .= ($login ? '.' : '');
1686  $login .= strtolower(dol_string_unaccent($lastname));
1687  $login = dol_string_nospecial($login, ''); // For special names
1688  } else { // firstname.lastname
1689  $login = strtolower(dol_string_unaccent($firstname));
1690  $login .= ($login ? '.' : '');
1691  $login .= strtolower(dol_string_unaccent($lastname));
1692  $login = dol_string_nospecial($login, ''); // For special names
1693  }
1694 
1695  // TODO Add a hook to allow external modules to suggest new rules
1696 
1697  return $login;
1698 }
1699 
1705 function getSoapParams()
1706 {
1707  global $conf;
1708 
1709  $params = array();
1710  $proxyuse = (empty($conf->global->MAIN_PROXY_USE) ?false:true);
1711  $proxyhost = (empty($conf->global->MAIN_PROXY_USE) ?false:$conf->global->MAIN_PROXY_HOST);
1712  $proxyport = (empty($conf->global->MAIN_PROXY_USE) ?false:$conf->global->MAIN_PROXY_PORT);
1713  $proxyuser = (empty($conf->global->MAIN_PROXY_USE) ?false:$conf->global->MAIN_PROXY_USER);
1714  $proxypass = (empty($conf->global->MAIN_PROXY_USE) ?false:$conf->global->MAIN_PROXY_PASS);
1715  $timeout = (empty($conf->global->MAIN_USE_CONNECT_TIMEOUT) ? 10 : $conf->global->MAIN_USE_CONNECT_TIMEOUT); // Connection timeout
1716  $response_timeout = (empty($conf->global->MAIN_USE_RESPONSE_TIMEOUT) ? 30 : $conf->global->MAIN_USE_RESPONSE_TIMEOUT); // Response timeout
1717  //print extension_loaded('soap');
1718  if ($proxyuse)
1719  {
1720  $params = array('connection_timeout'=>$timeout,
1721  'response_timeout'=>$response_timeout,
1722  'proxy_use' => 1,
1723  'proxy_host' => $proxyhost,
1724  'proxy_port' => $proxyport,
1725  'proxy_login' => $proxyuser,
1726  'proxy_password' => $proxypass,
1727  'trace' => 1
1728  );
1729  } else {
1730  $params = array('connection_timeout'=>$timeout,
1731  'response_timeout'=>$response_timeout,
1732  'proxy_use' => 0,
1733  'proxy_host' => false,
1734  'proxy_port' => false,
1735  'proxy_login' => false,
1736  'proxy_password' => false,
1737  'trace' => 1
1738  );
1739  }
1740  return $params;
1741 }
1742 
1743 
1753 function dolGetElementUrl($objectid, $objecttype, $withpicto = 0, $option = '')
1754 {
1755  global $db, $conf, $langs;
1756 
1757  $ret = '';
1758  $regs = array();
1759 
1760  // If we ask a resource form external module (instead of default path)
1761  if (preg_match('/^([^@]+)@([^@]+)$/i', $objecttype, $regs)) {
1762  $myobject = $regs[1];
1763  $module = $regs[2];
1764  }
1765  else {
1766  // Parse $objecttype (ex: project_task)
1767  $module = $myobject = $objecttype;
1768  if (preg_match('/^([^_]+)_([^_]+)/i', $objecttype, $regs))
1769  {
1770  $module = $regs[1];
1771  $myobject = $regs[2];
1772  }
1773  }
1774 
1775  // Generic case for $classpath
1776  $classpath = $module.'/class';
1777 
1778  // Special cases, to work with non standard path
1779  if ($objecttype == 'facture' || $objecttype == 'invoice') {
1780  $classpath = 'compta/facture/class';
1781  $module = 'facture';
1782  $myobject = 'facture';
1783  } elseif ($objecttype == 'commande' || $objecttype == 'order') {
1784  $classpath = 'commande/class';
1785  $module = 'commande';
1786  $myobject = 'commande';
1787  } elseif ($objecttype == 'propal') {
1788  $classpath = 'comm/propal/class';
1789  } elseif ($objecttype == 'supplier_proposal') {
1790  $classpath = 'supplier_proposal/class';
1791  } elseif ($objecttype == 'shipping') {
1792  $classpath = 'expedition/class';
1793  $myobject = 'expedition';
1794  $module = 'expedition_bon';
1795  } elseif ($objecttype == 'delivery') {
1796  $classpath = 'delivery/class';
1797  $myobject = 'delivery';
1798  $module = 'delivery_note';
1799  } elseif ($objecttype == 'contract') {
1800  $classpath = 'contrat/class';
1801  $module = 'contrat';
1802  $myobject = 'contrat';
1803  } elseif ($objecttype == 'member') {
1804  $classpath = 'adherents/class';
1805  $module = 'adherent';
1806  $myobject = 'adherent';
1807  } elseif ($objecttype == 'cabinetmed_cons') {
1808  $classpath = 'cabinetmed/class';
1809  $module = 'cabinetmed';
1810  $myobject = 'cabinetmedcons';
1811  } elseif ($objecttype == 'fichinter') {
1812  $classpath = 'fichinter/class';
1813  $module = 'ficheinter';
1814  $myobject = 'fichinter';
1815  } elseif ($objecttype == 'task') {
1816  $classpath = 'projet/class';
1817  $module = 'projet';
1818  $myobject = 'task';
1819  } elseif ($objecttype == 'stock') {
1820  $classpath = 'product/stock/class';
1821  $module = 'stock';
1822  $myobject = 'stock';
1823  } elseif ($objecttype == 'inventory') {
1824  $classpath = 'product/inventory/class';
1825  $module = 'stock';
1826  $myobject = 'inventory';
1827  } elseif ($objecttype == 'mo') {
1828  $classpath = 'mrp/class';
1829  $module = 'mrp';
1830  $myobject = 'mo';
1831  }
1832 
1833  // Generic case for $classfile and $classname
1834  $classfile = strtolower($myobject); $classname = ucfirst($myobject);
1835  //print "objecttype=".$objecttype." module=".$module." subelement=".$subelement." classfile=".$classfile." classname=".$classname." classpath=".$classpath;
1836 
1837  if ($objecttype == 'invoice_supplier') {
1838  $classfile = 'fournisseur.facture';
1839  $classname = 'FactureFournisseur';
1840  $classpath = 'fourn/class';
1841  $module = 'fournisseur';
1842  } elseif ($objecttype == 'order_supplier') {
1843  $classfile = 'fournisseur.commande';
1844  $classname = 'CommandeFournisseur';
1845  $classpath = 'fourn/class';
1846  $module = 'fournisseur';
1847  }
1848  elseif ($objecttype == 'supplier_proposal') {
1849  $classfile = 'supplier_proposal';
1850  $classname = 'SupplierProposal';
1851  $classpath = 'supplier_proposal/class';
1852  $module = 'supplier_proposal';
1853  }
1854  elseif ($objecttype == 'stock') {
1855  $classpath = 'product/stock/class';
1856  $classfile = 'entrepot';
1857  $classname = 'Entrepot';
1858  }
1859 
1860  if (!empty($conf->$module->enabled))
1861  {
1862  $res = dol_include_once('/'.$classpath.'/'.$classfile.'.class.php');
1863  if ($res)
1864  {
1865  if (class_exists($classname))
1866  {
1867  $object = new $classname($db);
1868  $res = $object->fetch($objectid);
1869  if ($res > 0) {
1870  $ret = $object->getNomUrl($withpicto, $option);
1871  } elseif ($res == 0) {
1872  $ret = $langs->trans('Deleted');
1873  }
1874  unset($object);
1875  } else dol_syslog("Class with classname ".$classname." is unknown even after the include", LOG_ERR);
1876  }
1877  }
1878  return $ret;
1879 }
1880 
1881 
1890 function cleanCorruptedTree($db, $tabletocleantree, $fieldfkparent)
1891 {
1892  $totalnb = 0;
1893  $listofid = array();
1894  $listofparentid = array();
1895 
1896  // Get list of all id in array listofid and all parents in array listofparentid
1897  $sql = 'SELECT rowid, '.$fieldfkparent.' as parent_id FROM '.MAIN_DB_PREFIX.$tabletocleantree;
1898  $resql = $db->query($sql);
1899  if ($resql)
1900  {
1901  $num = $db->num_rows($resql);
1902  $i = 0;
1903  while ($i < $num)
1904  {
1905  $obj = $db->fetch_object($resql);
1906  $listofid[] = $obj->rowid;
1907  if ($obj->parent_id > 0) $listofparentid[$obj->rowid] = $obj->parent_id;
1908  $i++;
1909  }
1910  } else {
1911  dol_print_error($db);
1912  }
1913 
1914  if (count($listofid))
1915  {
1916  print 'Code requested to clean tree (may be to solve data corruption), so we check/clean orphelins and loops.'."<br>\n";
1917 
1918  // Check loops on each other
1919  $sql = "UPDATE ".MAIN_DB_PREFIX.$tabletocleantree." SET ".$fieldfkparent." = 0 WHERE ".$fieldfkparent." = rowid"; // So we update only records linked to themself
1920  $resql = $db->query($sql);
1921  if ($resql)
1922  {
1923  $nb = $db->affected_rows($sql);
1924  if ($nb > 0)
1925  {
1926  print '<br>Some record that were parent of themself were cleaned.';
1927  }
1928 
1929  $totalnb += $nb;
1930  }
1931  //else dol_print_error($db);
1932 
1933  // Check other loops
1934  $listofidtoclean = array();
1935  foreach ($listofparentid as $id => $pid)
1936  {
1937  // Check depth
1938  //print 'Analyse record id='.$id.' with parent '.$pid.'<br>';
1939 
1940  $cursor = $id; $arrayidparsed = array(); // We start from child $id
1941  while ($cursor > 0)
1942  {
1943  $arrayidparsed[$cursor] = 1;
1944  if ($arrayidparsed[$listofparentid[$cursor]]) // We detect a loop. A record with a parent that was already into child
1945  {
1946  print 'Found a loop between id '.$id.' - '.$cursor.'<br>';
1947  unset($arrayidparsed);
1948  $listofidtoclean[$cursor] = $id;
1949  break;
1950  }
1951  $cursor = $listofparentid[$cursor];
1952  }
1953 
1954  if (count($listofidtoclean)) break;
1955  }
1956 
1957  $sql = "UPDATE ".MAIN_DB_PREFIX.$tabletocleantree;
1958  $sql .= " SET ".$fieldfkparent." = 0";
1959  $sql .= " WHERE rowid IN (".join(',', $listofidtoclean).")"; // So we update only records detected wrong
1960  $resql = $db->query($sql);
1961  if ($resql)
1962  {
1963  $nb = $db->affected_rows($sql);
1964  if ($nb > 0)
1965  {
1966  // Removed orphelins records
1967  print '<br>Some records were detected to have parent that is a child, we set them as root record for id: ';
1968  print join(',', $listofidtoclean);
1969  }
1970 
1971  $totalnb += $nb;
1972  }
1973  //else dol_print_error($db);
1974 
1975  // Check and clean orphelins
1976  $sql = "UPDATE ".MAIN_DB_PREFIX.$tabletocleantree;
1977  $sql .= " SET ".$fieldfkparent." = 0";
1978  $sql .= " WHERE ".$fieldfkparent." NOT IN (".join(',', $listofid).")"; // So we update only records linked to a non existing parent
1979  $resql = $db->query($sql);
1980  if ($resql)
1981  {
1982  $nb = $db->affected_rows($sql);
1983  if ($nb > 0)
1984  {
1985  // Removed orphelins records
1986  print '<br>Some orphelins were found and modified to be parent so records are visible again for id: ';
1987  print join(',', $listofid);
1988  }
1989 
1990  $totalnb += $nb;
1991  }
1992  //else dol_print_error($db);
1993 
1994  print '<br>We fixed '.$totalnb.' record(s). Some records may still be corrupted. New check may be required.';
1995  return $totalnb;
1996  }
1997 }
1998 
1999 
2009 function colorArrayToHex($arraycolor, $colorifnotfound = '888888')
2010 {
2011  if (!is_array($arraycolor)) return $colorifnotfound;
2012  if (empty($arraycolor)) return $colorifnotfound;
2013  return sprintf("%02s", dechex($arraycolor[0])).sprintf("%02s", dechex($arraycolor[1])).sprintf("%02s", dechex($arraycolor[2]));
2014 }
2015 
2026 function colorStringToArray($stringcolor, $colorifnotfound = array(88, 88, 88))
2027 {
2028  if (is_array($stringcolor)) return $stringcolor; // If already into correct output format, we return as is
2029  $reg = array();
2030  $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);
2031  if (!$tmp)
2032  {
2033  $tmp = explode(',', $stringcolor);
2034  if (count($tmp) < 3) return $colorifnotfound;
2035  return $tmp;
2036  }
2037  return array(hexdec($reg[1]), hexdec($reg[2]), hexdec($reg[3]));
2038 }
2039 
2045 function colorValidateHex($color, $allow_white = true)
2046 {
2047  if (!$allow_white && ($color === '#fff' || $color === '#ffffff')) return false;
2048 
2049  if (preg_match('/^#[a-f0-9]{6}$/i', $color)) //hex color is valid
2050  {
2051  return true;
2052  }
2053  return false;
2054 }
2055 
2065 function colorAgressiveness($hex, $ratio = -50, $brightness = 0)
2066 {
2067  if (empty($ratio)) $ratio = 0; // To avoid null
2068 
2069  // Steps should be between -255 and 255. Negative = darker, positive = lighter
2070  $ratio = max(-100, min(100, $ratio));
2071 
2072  // Normalize into a six character long hex string
2073  $hex = str_replace('#', '', $hex);
2074  if (strlen($hex) == 3) {
2075  $hex = str_repeat(substr($hex, 0, 1), 2).str_repeat(substr($hex, 1, 1), 2).str_repeat(substr($hex, 2, 1), 2);
2076  }
2077 
2078  // Split into three parts: R, G and B
2079  $color_parts = str_split($hex, 2);
2080  $return = '#';
2081 
2082  foreach ($color_parts as $color) {
2083  $color = hexdec($color); // Convert to decimal
2084  if ($ratio > 0) // We increase aggressivity
2085  {
2086  if ($color > 127) $color += ((255 - $color) * ($ratio / 100));
2087  if ($color < 128) $color -= ($color * ($ratio / 100));
2088  } else // We decrease agressiveness
2089  {
2090  if ($color > 128) $color -= (($color - 128) * (abs($ratio) / 100));
2091  if ($color < 127) $color += ((128 - $color) * (abs($ratio) / 100));
2092  }
2093  if ($brightness > 0)
2094  {
2095  $color = ($color * (100 + abs($brightness)) / 100);
2096  } else {
2097  $color = ($color * (100 - abs($brightness)) / 100);
2098  }
2099 
2100  $color = max(0, min(255, $color)); // Adjust color to stay into valid range
2101  $return .= str_pad(dechex($color), 2, '0', STR_PAD_LEFT); // Make two char hex code
2102  }
2103 
2104  //var_dump($hex.' '.$ratio.' -> '.$return);
2105  return $return;
2106 }
2107 
2114 function colorAdjustBrightness($hex, $steps)
2115 {
2116  // Steps should be between -255 and 255. Negative = darker, positive = lighter
2117  $steps = max(-255, min(255, $steps));
2118 
2119  // Normalize into a six character long hex string
2120  $hex = str_replace('#', '', $hex);
2121  if (strlen($hex) == 3) {
2122  $hex = str_repeat(substr($hex, 0, 1), 2).str_repeat(substr($hex, 1, 1), 2).str_repeat(substr($hex, 2, 1), 2);
2123  }
2124 
2125  // Split into three parts: R, G and B
2126  $color_parts = str_split($hex, 2);
2127  $return = '#';
2128 
2129  foreach ($color_parts as $color) {
2130  $color = hexdec($color); // Convert to decimal
2131  $color = max(0, min(255, $color + $steps)); // Adjust color
2132  $return .= str_pad(dechex($color), 2, '0', STR_PAD_LEFT); // Make two char hex code
2133  }
2134 
2135  return $return;
2136 }
2137 
2143 function colorDarker($hex, $percent)
2144 {
2145  $steps = intval(255 * $percent / 100) * -1;
2146  return colorAdjustBrightness($hex, $steps);
2147 }
2148 
2154 function colorLighten($hex, $percent)
2155 {
2156  $steps = intval(255 * $percent / 100);
2157  return colorAdjustBrightness($hex, $steps);
2158 }
2159 
2160 
2167 function colorHexToRgb($hex, $alpha = false, $returnArray = false)
2168 {
2169  $string = '';
2170  $hex = str_replace('#', '', $hex);
2171  $length = strlen($hex);
2172  $rgb = array();
2173  $rgb['r'] = hexdec($length == 6 ? substr($hex, 0, 2) : ($length == 3 ? str_repeat(substr($hex, 0, 1), 2) : 0));
2174  $rgb['g'] = hexdec($length == 6 ? substr($hex, 2, 2) : ($length == 3 ? str_repeat(substr($hex, 1, 1), 2) : 0));
2175  $rgb['b'] = hexdec($length == 6 ? substr($hex, 4, 2) : ($length == 3 ? str_repeat(substr($hex, 2, 1), 2) : 0));
2176  if ($alpha !== false) {
2177  $rgb['a'] = floatval($alpha);
2178  $string = 'rgba('.implode(',', $rgb).')';
2179  } else {
2180  $string = 'rgb('.implode(',', $rgb).')';
2181  }
2182 
2183  if ($returnArray) {
2184  return $rgb;
2185  } else {
2186  return $string;
2187  }
2188 }
2189 
2190 
2198 function cartesianArray(array $input)
2199 {
2200  // filter out empty values
2201  $input = array_filter($input);
2202 
2203  $result = array(array());
2204 
2205  foreach ($input as $key => $values) {
2206  $append = array();
2207 
2208  foreach ($result as $product) {
2209  foreach ($values as $item) {
2210  $product[$key] = $item;
2211  $append[] = $product;
2212  }
2213  }
2214 
2215  $result = $append;
2216  }
2217 
2218  return $result;
2219 }
2220 
2221 
2228 function getModuleDirForApiClass($moduleobject)
2229 {
2230  $moduledirforclass = $moduleobject;
2231  if ($moduledirforclass != 'api') $moduledirforclass = preg_replace('/api$/i', '', $moduledirforclass);
2232 
2233  if ($moduleobject == 'contracts') {
2234  $moduledirforclass = 'contrat';
2235  } elseif (in_array($moduleobject, array('admin', 'login', 'setup', 'access', 'status', 'tools', 'documents'))) {
2236  $moduledirforclass = 'api';
2237  } elseif ($moduleobject == 'contact' || $moduleobject == 'contacts' || $moduleobject == 'customer' || $moduleobject == 'thirdparty' || $moduleobject == 'thirdparties') {
2238  $moduledirforclass = 'societe';
2239  } elseif ($moduleobject == 'propale' || $moduleobject == 'proposals') {
2240  $moduledirforclass = 'comm/propal';
2241  } elseif ($moduleobject == 'agenda' || $moduleobject == 'agendaevents') {
2242  $moduledirforclass = 'comm/action';
2243  } elseif ($moduleobject == 'adherent' || $moduleobject == 'members' || $moduleobject == 'memberstypes' || $moduleobject == 'subscriptions') {
2244  $moduledirforclass = 'adherents';
2245  } elseif ($moduleobject == 'don' || $moduleobject == 'donations') {
2246  $moduledirforclass = 'don';
2247  } elseif ($moduleobject == 'banque' || $moduleobject == 'bankaccounts') {
2248  $moduledirforclass = 'compta/bank';
2249  } elseif ($moduleobject == 'category' || $moduleobject == 'categorie') {
2250  $moduledirforclass = 'categories';
2251  } elseif ($moduleobject == 'order' || $moduleobject == 'orders') {
2252  $moduledirforclass = 'commande';
2253  } elseif ($moduleobject == 'shipments') {
2254  $moduledirforclass = 'expedition';
2255  } elseif ($moduleobject == 'facture' || $moduleobject == 'invoice' || $moduleobject == 'invoices') {
2256  $moduledirforclass = 'compta/facture';
2257  } elseif ($moduleobject == 'project' || $moduleobject == 'projects' || $moduleobject == 'task' || $moduleobject == 'tasks') {
2258  $moduledirforclass = 'projet';
2259  } elseif ($moduleobject == 'stock' || $moduleobject == 'stockmovements' || $moduleobject == 'warehouses') {
2260  $moduledirforclass = 'product/stock';
2261  } elseif ($moduleobject == 'supplierproposals' || $moduleobject == 'supplierproposal' || $moduleobject == 'supplier_proposal') {
2262  $moduledirforclass = 'supplier_proposal';
2263  } elseif ($moduleobject == 'fournisseur' || $moduleobject == 'supplierinvoices' || $moduleobject == 'supplierorders') {
2264  $moduledirforclass = 'fourn';
2265  } elseif ($moduleobject == 'ficheinter' || $moduleobject == 'interventions') {
2266  $moduledirforclass = 'fichinter';
2267  } elseif ($moduleobject == 'mos') {
2268  $moduledirforclass = 'mrp';
2269  } elseif (in_array($moduleobject, array('products', 'expensereports', 'users', 'tickets', 'boms'))) {
2270  $moduledirforclass = preg_replace('/s$/', '', $moduleobject);
2271  }
2272 
2273  return $moduledirforclass;
2274 }
2275 
2283 function randomColorPart($min = 0, $max = 255)
2284 {
2285  return str_pad(dechex(mt_rand($min, $max)), 2, '0', STR_PAD_LEFT);
2286 }
2287 
2295 function randomColor($min = 0, $max = 255)
2296 {
2297  return randomColorPart($min, $max).randomColorPart($min, $max).randomColorPart($min, $max);
2298 }
2299 
2300 
2301 if (!function_exists('dolEscapeXML'))
2302 {
2309  function dolEscapeXML($string)
2310  {
2311  return strtr($string, array('\''=>'&apos;', '"'=>'&quot;', '&'=>'&amp;', '<'=>'&lt;', '>'=>'&gt;'));
2312  }
2313 }
2314 
2315 
2324 function autoOrManual($automaticmanual, $case = 1, $color = 0)
2325 {
2326  global $langs;
2327  $result = 'unknown'; $classname = '';
2328  if ($automaticmanual == 1 || strtolower($automaticmanual) == 'automatic' || strtolower($automaticmanual) == 'true') // A mettre avant test sur no a cause du == 0
2329  {
2330  $result = $langs->trans('automatic');
2331  if ($case == 1 || $case == 3) $result = $langs->trans("Automatic");
2332  if ($case == 2) $result = '<input type="checkbox" value="1" checked disabled>';
2333  if ($case == 3) $result = '<input type="checkbox" value="1" checked disabled> '.$result;
2334 
2335  $classname = 'ok';
2336  } elseif ($automaticmanual == 0 || strtolower($automaticmanual) == 'manual' || strtolower($automaticmanual) == 'false')
2337  {
2338  $result = $langs->trans("manual");
2339  if ($case == 1 || $case == 3) $result = $langs->trans("Manual");
2340  if ($case == 2) $result = '<input type="checkbox" value="0" disabled>';
2341  if ($case == 3) $result = '<input type="checkbox" value="0" disabled> '.$result;
2342 
2343  if ($color == 2) $classname = 'ok';
2344  else $classname = 'error';
2345  }
2346  if ($color) return '<font class="'.$classname.'">'.$result.'</font>';
2347  return $result;
2348 }
2349 
2350 
2358 {
2359  global $dolibarr_main_url_root;
2360  // Define $urlwithroot
2361  $urlwithouturlroot = preg_replace('/'.preg_quote(DOL_URL_ROOT, '/').'$/i', '', trim($dolibarr_main_url_root));
2362  $urlwithroot = $urlwithouturlroot.DOL_URL_ROOT; // This is to use external domain name found into config file
2363  //$urlwithroot=DOL_MAIN_URL_ROOT; // This is to use same domain name than current
2364  $notetoshow = preg_replace('/src="[a-zA-Z0-9_\/\-\.]*(viewimage\.php\?modulepart=medias[^"]*)"/', 'src="'.$urlwithroot.'/\1"', $notetoshow);
2365  return $notetoshow;
2366 }
2367 
2376 function price2fec($amount)
2377 {
2378  global $conf;
2379 
2380  // Clean parameters
2381  if (empty($amount)) $amount = 0; // To have a numeric value if amount not defined or = ''
2382  $amount = (is_numeric($amount) ? $amount : 0); // Check if amount is numeric, for example, an error occured when amount value = o (letter) instead 0 (number)
2383 
2384  // Output decimal number by default
2385  $nbdecimal = (empty($conf->global->ACCOUNTING_FEC_DECIMAL_LENGTH) ? 2 : $conf->global->ACCOUNTING_FEC_DECIMAL_LENGTH);
2386 
2387  // Output separators by default
2388  $dec = (empty($conf->global->ACCOUNTING_FEC_DECIMAL_SEPARATOR) ? ',' : $conf->global->ACCOUNTING_FEC_DECIMAL_SEPARATOR);
2389  $thousand = (empty($conf->global->ACCOUNTING_FEC_THOUSAND_SEPARATOR) ? '' : $conf->global->ACCOUNTING_FEC_THOUSAND_SEPARATOR);
2390 
2391  // Format number
2392  $output = number_format($amount, $nbdecimal, $dec, $thousand);
2393 
2394  return $output;
2395 }
2396 
2403 function phpSyntaxError($code)
2404 {
2405  if (!defined("CR")) define("CR", "\r");
2406  if (!defined("LF")) define("LF", "\n");
2407  if (!defined("CRLF")) define("CRLF", "\r\n");
2408 
2409  $braces = 0;
2410  $inString = 0;
2411  foreach (token_get_all('<?php '.$code) as $token) {
2412  if (is_array($token)) {
2413  switch ($token[0]) {
2414  case T_CURLY_OPEN:
2415  case T_DOLLAR_OPEN_CURLY_BRACES:
2416  case T_START_HEREDOC: ++$inString; break;
2417  case T_END_HEREDOC: --$inString; break;
2418  }
2419  } elseif ($inString & 1) {
2420  switch ($token) {
2421  case '`':
2422  case '\'':
2423  case '"': --$inString; break;
2424  }
2425  } else {
2426  switch ($token) {
2427  case '`':
2428  case '\'':
2429  case '"': ++$inString; break;
2430  case '{': ++$braces; break;
2431  case '}':
2432  if ($inString) {
2433  --$inString;
2434  } else {
2435  --$braces;
2436  if ($braces < 0) break 2;
2437  }
2438  break;
2439  }
2440  }
2441  }
2442  $inString = @ini_set('log_errors', false);
2443  $token = @ini_set('display_errors', true);
2444  ob_start();
2445  $code = substr($code, strlen('<?php '));
2446  $braces || $code = "if(0){{$code}\n}";
2447  if (eval($code) === false) {
2448  if ($braces) {
2449  $braces = PHP_INT_MAX;
2450  } else {
2451  false !== strpos($code, CR) && $code = strtr(str_replace(CRLF, LF, $code), CR, LF);
2452  $braces = substr_count($code, LF);
2453  }
2454  $code = ob_get_clean();
2455  $code = strip_tags($code);
2456  if (preg_match("'syntax error, (.+) in .+ on line (\d+)$'s", $code, $code)) {
2457  $code[2] = (int) $code[2];
2458  $code = $code[2] <= $braces
2459  ? array($code[1], $code[2])
2460  : array('unexpected $end'.substr($code[1], 14), $braces);
2461  } else $code = array('syntax error', 0);
2462  } else {
2463  ob_end_clean();
2464  $code = false;
2465  }
2466  @ini_set('display_errors', $token);
2467  @ini_set('log_errors', $inString);
2468  return $code;
2469 }
if(!function_exists('dol_getprefix')) dol_include_once($relpath, $classname= '')
Make an include_once using default root and alternate root if it fails.
dol_mktime($hour, $minute, $second, $month, $day, $year, $gm= 'auto', $check=1)
Return a timestamp date built from detailed informations (by default a local PHP server timestamp) Re...
weight_convert($weight, &$from_unit, $to_unit)
Convertit une masse d&#39;une unite vers une autre unite.
getModuleDirForApiClass($moduleobject)
Get name of directory where the api_...class.php file is stored.
cartesianArray(array $input)
Applies the Cartesian product algorithm to an array Source: http://stackoverflow.com/a/15973172.
dol_print_reduction($reduction, $langs)
Returns formated reduction.
version_webserver()
Return web server version.
dolGetModulesDirs($subdir= '')
Return list of modules directories.
dol_html_entity_decode($a, $b, $c= 'UTF-8', $keepsomeentities=0)
Replace html_entity_decode functions to manage errors.
dol_now($mode= 'auto')
Return date for now.
colorDarker($hex, $percent)
dol_print_object_info($object, $usetable=0)
Show informations on an object TODO Move this into html.formother.
Class to manage Dolibarr users.
Definition: user.class.php:44
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)
colorLighten($hex, $percent)
cleanCorruptedTree($db, $tabletocleantree, $fieldfkparent)
Clean corrupted tree (orphelins linked to a not existing parent), record linked to themself and child...
clean_url($url, $http=1)
Clean an url string.
version_os($option= '')
Return OS version.
price2fec($amount)
Function to format a value into a defined format for French administration (no thousand separator &amp; d...
colorAdjustBrightness($hex, $steps)
dol_buildlogin($lastname, $firstname)
Build a login from lastname, firstname.
check_value($mask, $value)
Check value.
colorStringToArray($stringcolor, $colorifnotfound=array(88, 88, 88))
Convert a string RGB value (&#39;FFFFFF&#39;, &#39;255,255,255&#39;) into an array RGB array(255,255,255).
isValidUrl($url, $http=0, $pass=0, $port=0, $path=0, $query=0, $anchor=0)
Url string validation &lt;http[s]&gt; :// [user[:pass]@] hostname [port] [/path] [?getquery] [anchor]...
dolAddEmailTrackId($email, $trackingid)
Return an email formatted to include a tracking id For example myemail@example.com becom myemail+trac...
colorHexToRgb($hex, $alpha=false, $returnArray=false)
hexbin($hexa)
Convert an hexadecimal string into a binary string.
dol_string_nospecial($str, $newstr= '_', $badcharstoreplace= '')
Clean a string from all punctuation characters to use it as a ref or login.
isValidMXRecord($domain)
Return if the domain name has a valid MX record.
getServerTimeZoneInt($refgmtdate= 'now')
Return server timezone int.
Definition: date.lib.php:83
phpSyntaxError($code)
Check the syntax of some PHP code.
dol_strlen($string, $stringencoding= 'UTF-8')
Make a strlen call.
dol_syslog($message, $level=LOG_INFO, $ident=0, $suffixinfilename= '', $restricttologhandler= '', $logcontext=null)
Write log message into outputs.
jsUnEscape($source)
Same function than javascript unescape() function but in PHP.
dol_getDefaultFormat(Translate $outputlangs=null)
Try to guess default paper format according to language into $langs.
Class to manage translations.
version_dolibarr()
Return Dolibarr version.
dol_print_file($langs, $filename, $searchalt=0)
Output content of a file $filename in version of current language (otherwise may use an alternate lan...
is_ip($ip)
This function evaluates a string that should be a valid IPv4 Note: For ip 169.254.0.0, it returns 0 with some PHP (5.6.24) and 2 with some minor patchs of PHP (5.6.25).
isValidVATID($company)
Check if VAT numero is valid (check done on syntax only, no database or remote access) ...
dol_dir_list($path, $types="all", $recursive=0, $filter="", $excludefilter=null, $sortcriteria="name", $sortorder=SORT_ASC, $mode=0, $nohook=0, $relativename="", $donotfollowsymlinks=0)
Scan a directory and return a list of files/directories.
Definition: files.lib.php:60
randomColorPart($min=0, $max=255)
Return 2 hexa code randomly.
colorValidateHex($color, $allow_white=true)
dol_string_unaccent($str)
Clean a string from all accent characters to be used as ref, login or by dol_sanitizeFileName.
utf8_check($str)
Check if a string is in UTF8.
convertBackOfficeMediasLinksToPublicLinks($notetoshow)
Convert links to local wrapper to medias files into a string into a public external URL readable on i...
vatrate($rate, $addpercent=false, $info_bits=0, $usestarfornpr=0)
Return a string with VAT rate label formated for view output Used into pdf and HTML pages...
dol_set_user_param($db, $conf, &$user, $tab)
Save personnal parameter.
numero_semaine($time)
Retourne le numero de la semaine par rapport a une date.
version_php()
Return PHP version.
print $_SERVER["PHP_SELF"]
Edit parameters.
isValidMailDomain($mail)
Return true if email has a domain name that can be resolved to MX type.
print
Draft customers invoices.
Definition: index.php:89
randomColor($min=0, $max=255)
Return hexadecimal color randomly.
dol_print_date($time, $format= '', $tzoutput= 'auto', $outputlangs= '', $encodetooutput=false)
Output date in a string format according to outputlangs (or langs if not defined).
if(!empty($conf->facture->enabled)&&$user->rights->facture->lire) if((!empty($conf->fournisseur->enabled)&&empty($conf->global->MAIN_USE_NEW_SUPPLIERMOD)||!empty($conf->supplier_invoice->enabled))&&$user->rights->fournisseur->facture->lire) if(!empty($conf->don->enabled)&&$user->rights->don->lire) if(!empty($conf->tax->enabled)&&$user->rights->tax->charges->lire) if(!empty($conf->facture->enabled)&&!empty($conf->commande->enabled)&&$user->rights->commande->lire &&empty($conf->global->WORKFLOW_DISABLE_CREATE_INVOICE_FROM_ORDER)) if(!empty($conf->facture->enabled)&&$user->rights->facture->lire) if((!empty($conf->fournisseur->enabled)&&empty($conf->global->MAIN_USE_NEW_SUPPLIERMOD)||!empty($conf->supplier_invoice->enabled))&&$user->rights->fournisseur->facture->lire) $resql
Social contributions to pay.
Definition: index.php:1232
dol_print_error($db= '', $error= '', $errors=null)
Displays error message system with all the information to facilitate the diagnosis and the escalation...
array2table($data, $tableMarkup=1, $tableoptions= '', $troptions= '', $tdoptions= '')
Return an html table from an array.
dol_trunc($string, $size=40, $trunc= 'right', $stringencoding= 'UTF-8', $nodot=0, $display=0)
Truncate a string to a particular length adding &#39;...&#39; if string larger than length.
array2tr($data, $troptions= '', $tdoptions= '')
Return lines of an html table from an array Used by array2table function only.
getListOfModels($db, $type, $maxfilenamelength=0)
Return list of activated modules usable for document generation.
get_string_between($string, $start, $end)
Get string between.
dolObfuscateEmail($mail, $replace="*", $nbreplace=8, $nbdisplaymail=4, $nbdisplaydomain=3, $displaytld=true)
Returns an email value with obfuscated parts.
binhex($bin, $pad=false, $upper=false)
Convert a binary data to string that represent hexadecimal value.
getSoapParams()
Return array to use for SoapClient constructor.
isValidEmail($address, $acceptsupervisorkey=0)
Return true if email syntax is ok.
colorAgressiveness($hex, $ratio=-50, $brightness=0)
Change color to make it less aggressive (ratio is negative) or more aggressive (ratio is positive) ...
if(!function_exists('dolEscapeXML')) autoOrManual($automaticmanual, $case=1, $color=0)
Return automatic or manual in current language.
dolGetElementUrl($objectid, $objecttype, $withpicto=0, $option= '')
Return link url to an object.
colorArrayToHex($arraycolor, $colorifnotfound= '888888')
Convert an array with RGB value into hex RGB value.