dolibarr  19.0.0-dev
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  // Default core/modules dir
88  if ($type === 'main') {
89  $modulesdir[$dirroot.'/core/modules'.$subdir.'/'] = $dirroot.'/core/modules'.$subdir.'/';
90  }
91 
92  // Scan dir from external modules
93  $handle = @opendir($dirroot);
94  if (is_resource($handle)) {
95  while (($file = readdir($handle)) !== false) {
96  if (preg_match('/disabled/', $file)) {
97  continue; // We discard module if it contains disabled into name.
98  }
99 
100  if (is_dir($dirroot.'/'.$file) && substr($file, 0, 1) <> '.' && substr($file, 0, 3) <> 'CVS' && $file != 'includes') {
101  if (is_dir($dirroot.'/'.$file.'/core/modules'.$subdir.'/')) {
102  $modulesdir[$dirroot.'/'.$file.'/core/modules'.$subdir.'/'] = $dirroot.'/'.$file.'/core/modules'.$subdir.'/';
103  }
104  }
105  }
106  closedir($handle);
107  }
108  }
109  return $modulesdir;
110 }
111 
112 
119 function dol_getDefaultFormat(Translate $outputlangs = null)
120 {
121  global $langs;
122 
123  $selected = 'EUA4';
124  if (!$outputlangs) {
125  $outputlangs = $langs;
126  }
127 
128  if ($outputlangs->defaultlang == 'ca_CA') {
129  $selected = 'CAP4'; // Canada
130  }
131  if ($outputlangs->defaultlang == 'en_US') {
132  $selected = 'USLetter'; // US
133  }
134  return $selected;
135 }
136 
145 function dol_print_file($langs, $filename, $searchalt = 0)
146 {
147  global $conf;
148 
149  // Test if file is in lang directory
150  foreach ($langs->dir as $searchdir) {
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  $content = file_get_contents($formfile);
155  $isutf8 = utf8_check($content);
156  if (!$isutf8 && $conf->file->character_set_client == 'UTF-8') {
157  print utf8_encode($content);
158  } elseif ($isutf8 && $conf->file->character_set_client == 'ISO-8859-1') {
159  print utf8_decode($content);
160  } else {
161  print $content;
162  }
163  return true;
164  } else {
165  dol_syslog('functions2::dol_print_file not found', LOG_DEBUG);
166  }
167 
168  if ($searchalt) {
169  // Test si fichier dans repertoire de la langue alternative
170  if ($langs->defaultlang != "en_US") {
171  $formfilealt = $searchdir."/langs/en_US/".$filename;
172  } else {
173  $formfilealt = $searchdir."/langs/fr_FR/".$filename;
174  }
175  dol_syslog('functions2::dol_print_file search alt file '.$formfilealt, LOG_DEBUG);
176  //print 'getcwd='.getcwd().' htmlfilealt='.$formfilealt.' X '.file_exists(getcwd().'/'.$formfilealt);
177  if (is_readable($formfilealt)) {
178  $content = file_get_contents($formfilealt);
179  $isutf8 = utf8_check($content);
180  if (!$isutf8 && $conf->file->character_set_client == 'UTF-8') {
181  print mb_convert_encoding($content, 'UTF-8', 'ISO-8859-1');
182  } elseif ($isutf8 && $conf->file->character_set_client == 'ISO-8859-1') {
183  print mb_convert_encoding($content, 'ISO-8859-1', 'UTF-8');
184  } else {
185  print $content;
186  }
187  return true;
188  } else {
189  dol_syslog('functions2::dol_print_file not found', LOG_DEBUG);
190  }
191  }
192  }
193 
194  return false;
195 }
196 
205 function dol_print_object_info($object, $usetable = 0)
206 {
207  global $langs, $db;
208 
209  // Load translation files required by the page
210  $langs->loadLangs(array('other', 'admin'));
211 
212  include_once DOL_DOCUMENT_ROOT.'/core/lib/date.lib.php';
213 
214  $deltadateforserver = getServerTimeZoneInt('now');
215  $deltadateforclient = ((int) $_SESSION['dol_tz'] + (int) $_SESSION['dol_dst']);
216  //$deltadateforcompany=((int) $_SESSION['dol_tz'] + (int) $_SESSION['dol_dst']);
217  $deltadateforuser = round($deltadateforclient - $deltadateforserver);
218  //print "x".$deltadateforserver." - ".$deltadateforclient." - ".$deltadateforuser;
219 
220  if ($usetable) {
221  print '<table class="border tableforfield centpercent">';
222  }
223 
224  // Import key
225  if (!empty($object->import_key)) {
226  if ($usetable) {
227  print '<tr><td class="titlefield">';
228  }
229  print $langs->trans("ImportedWithSet");
230  if ($usetable) {
231  print '</td><td>';
232  } else {
233  print ': ';
234  }
235  print $object->import_key;
236  if ($usetable) {
237  print '</td></tr>';
238  } else {
239  print '<br>';
240  }
241  }
242 
243  // User creation (old method using already loaded object and not id is kept for backward compatibility)
244  if (!empty($object->user_creation) || !empty($object->user_creation_id)) {
245  if ($usetable) {
246  print '<tr><td class="titlefield">';
247  }
248  print $langs->trans("CreatedBy");
249  if ($usetable) {
250  print '</td><td>';
251  } else {
252  print ': ';
253  }
254  if (is_object($object->user_creation)) {
255  if ($object->user_creation->id) {
256  print $object->user_creation->getNomUrl(-1, '', 0, 0, 0);
257  } else {
258  print $langs->trans("Unknown");
259  }
260  } else {
261  $userstatic = new User($db);
262  $userstatic->fetch($object->user_creation_id ? $object->user_creation_id : $object->user_creation);
263  if ($userstatic->id) {
264  print $userstatic->getNomUrl(-1, '', 0, 0, 0);
265  } else {
266  print $langs->trans("Unknown");
267  }
268  }
269  if ($usetable) {
270  print '</td></tr>';
271  } else {
272  print '<br>';
273  }
274  }
275 
276  // Date creation
277  if (!empty($object->date_creation)) {
278  if ($usetable) {
279  print '<tr><td class="titlefield">';
280  }
281  print $langs->trans("DateCreation");
282  if ($usetable) {
283  print '</td><td>';
284  } else {
285  print ': ';
286  }
287  print dol_print_date($object->date_creation, 'dayhour', 'tzserver');
288  if ($deltadateforuser) {
289  print ' <span class="opacitymedium">'.$langs->trans("CurrentHour").'</span> &nbsp; / &nbsp; '.dol_print_date($object->date_creation, "dayhour", "tzuserrel").' &nbsp;<span class="opacitymedium">'.$langs->trans("ClientHour").'</span>';
290  }
291  if ($usetable) {
292  print '</td></tr>';
293  } else {
294  print '<br>';
295  }
296  }
297 
298  // User change (old method using already loaded object and not id is kept for backward compatibility)
299  if (!empty($object->user_modification) || !empty($object->user_modification_id)) {
300  if ($usetable) {
301  print '<tr><td class="titlefield">';
302  }
303  print $langs->trans("ModifiedBy");
304  if ($usetable) {
305  print '</td><td>';
306  } else {
307  print ': ';
308  }
309  if (is_object($object->user_modification)) {
310  if ($object->user_modification->id) {
311  print $object->user_modification->getNomUrl(-1, '', 0, 0, 0);
312  } else {
313  print $langs->trans("Unknown");
314  }
315  } else {
316  $userstatic = new User($db);
317  $userstatic->fetch($object->user_modification_id ? $object->user_modification_id : $object->user_modification);
318  if ($userstatic->id) {
319  print $userstatic->getNomUrl(-1, '', 0, 0, 0);
320  } else {
321  print $langs->trans("Unknown");
322  }
323  }
324  if ($usetable) {
325  print '</td></tr>';
326  } else {
327  print '<br>';
328  }
329  }
330 
331  // Date change
332  if (!empty($object->date_modification)) {
333  if ($usetable) {
334  print '<tr><td class="titlefield">';
335  }
336  print $langs->trans("DateLastModification");
337  if ($usetable) {
338  print '</td><td>';
339  } else {
340  print ': ';
341  }
342  print dol_print_date($object->date_modification, 'dayhour', 'tzserver');
343  if ($deltadateforuser) {
344  print ' <span class="opacitymedium">'.$langs->trans("CurrentHour").'</span> &nbsp; / &nbsp; '.dol_print_date($object->date_modification, "dayhour", "tzuserrel").' &nbsp;<span class="opacitymedium">'.$langs->trans("ClientHour").'</span>';
345  }
346  if ($usetable) {
347  print '</td></tr>';
348  } else {
349  print '<br>';
350  }
351  }
352 
353  // User validation (old method using already loaded object and not id is kept for backward compatibility)
354  if (!empty($object->user_validation) || !empty($object->user_validation_id)) {
355  if ($usetable) {
356  print '<tr><td class="titlefield">';
357  }
358  print $langs->trans("ValidatedBy");
359  if ($usetable) {
360  print '</td><td>';
361  } else {
362  print ': ';
363  }
364  if (is_object($object->user_validation)) {
365  if ($object->user_validation->id) {
366  print $object->user_validation->getNomUrl(-1, '', 0, 0, 0);
367  } else {
368  print $langs->trans("Unknown");
369  }
370  } else {
371  $userstatic = new User($db);
372  $userstatic->fetch($object->user_validation_id ? $object->user_validation_id : $object->user_validation);
373  if ($userstatic->id) {
374  print $userstatic->getNomUrl(-1, '', 0, 0, 0);
375  } else {
376  print $langs->trans("Unknown");
377  }
378  }
379  if ($usetable) {
380  print '</td></tr>';
381  } else {
382  print '<br>';
383  }
384  }
385 
386  // Date validation
387  if (!empty($object->date_validation)) {
388  if ($usetable) {
389  print '<tr><td class="titlefield">';
390  }
391  print $langs->trans("DateValidation");
392  if ($usetable) {
393  print '</td><td>';
394  } else {
395  print ': ';
396  }
397  print dol_print_date($object->date_validation, 'dayhour', 'tzserver');
398  if ($deltadateforuser) {
399  print ' <span class="opacitymedium">'.$langs->trans("CurrentHour").'</span> &nbsp; / &nbsp; '.dol_print_date($object->date_validation, "dayhour", 'tzuserrel').' &nbsp;<span class="opacitymedium">'.$langs->trans("ClientHour").'</span>';
400  }
401  if ($usetable) {
402  print '</td></tr>';
403  } else {
404  print '<br>';
405  }
406  }
407 
408  // User approve (old method using already loaded object and not id is kept for backward compatibility)
409  if (!empty($object->user_approve) || !empty($object->user_approve_id)) {
410  if ($usetable) {
411  print '<tr><td class="titlefield">';
412  }
413  print $langs->trans("ApprovedBy");
414  if ($usetable) {
415  print '</td><td>';
416  } else {
417  print ': ';
418  }
419  if (!empty($object->user_approve) && is_object($object->user_approve)) {
420  if ($object->user_approve->id) {
421  print $object->user_approve->getNomUrl(-1, '', 0, 0, 0);
422  } else {
423  print $langs->trans("Unknown");
424  }
425  } else {
426  $userstatic = new User($db);
427  $userstatic->fetch($object->user_approve_id ? $object->user_approve_id : $object->user_approve);
428  if ($userstatic->id) {
429  print $userstatic->getNomUrl(-1, '', 0, 0, 0);
430  } else {
431  print $langs->trans("Unknown");
432  }
433  }
434  if ($usetable) {
435  print '</td></tr>';
436  } else {
437  print '<br>';
438  }
439  }
440 
441  // Date approve
442  if (!empty($object->date_approve) || !empty($object->date_approval)) {
443  if ($usetable) {
444  print '<tr><td class="titlefield">';
445  }
446  print $langs->trans("DateApprove");
447  if ($usetable) {
448  print '</td><td>';
449  } else {
450  print ': ';
451  }
452  print dol_print_date($object->date_approve ? $object->date_approve : $object->date_approval, 'dayhour', 'tzserver');
453  if ($deltadateforuser) {
454  print ' <span class="opacitymedium">'.$langs->trans("CurrentHour").'</span> &nbsp; / &nbsp; '.dol_print_date($object->date_approve, "dayhour", 'tzuserrel').' &nbsp;<span class="opacitymedium">'.$langs->trans("ClientHour").'</span>';
455  }
456  if ($usetable) {
457  print '</td></tr>';
458  } else {
459  print '<br>';
460  }
461  }
462 
463  // User approve
464  if (!empty($object->user_approve_id2)) {
465  if ($usetable) {
466  print '<tr><td class="titlefield">';
467  }
468  print $langs->trans("ApprovedBy");
469  if ($usetable) {
470  print '</td><td>';
471  } else {
472  print ': ';
473  }
474  $userstatic = new User($db);
475  $userstatic->fetch($object->user_approve_id2);
476  if ($userstatic->id) {
477  print $userstatic->getNomUrl(-1, '', 0, 0, 0);
478  } else {
479  print $langs->trans("Unknown");
480  }
481  if ($usetable) {
482  print '</td></tr>';
483  } else {
484  print '<br>';
485  }
486  }
487 
488  // Date approve
489  if (!empty($object->date_approve2)) {
490  if ($usetable) {
491  print '<tr><td class="titlefield">';
492  }
493  print $langs->trans("DateApprove2");
494  if ($usetable) {
495  print '</td><td>';
496  } else {
497  print ': ';
498  }
499  print dol_print_date($object->date_approve2, 'dayhour', 'tzserver');
500  if ($deltadateforuser) {
501  print ' <span class="opacitymedium">'.$langs->trans("CurrentHour").'</span> &nbsp; / &nbsp; '.dol_print_date($object->date_approve2, "dayhour", 'tzuserrel').' &nbsp;<span class="opacitymedium">'.$langs->trans("ClientHour").'</span>';
502  }
503  if ($usetable) {
504  print '</td></tr>';
505  } else {
506  print '<br>';
507  }
508  }
509 
510  // User signature
511  if (!empty($object->user_signature) || !empty($object->user_signature_id)) {
512  if ($usetable) {
513  print '<tr><td class="titlefield">';
514  }
515  print $langs->trans('SignedBy');
516  if ($usetable) {
517  print '</td><td>';
518  } else {
519  print ': ';
520  }
521  if (is_object($object->user_signature)) {
522  if ($object->user_signature->id) {
523  print $object->user_signature->getNomUrl(-1, '', 0, 0, 0);
524  } else {
525  print $langs->trans('Unknown');
526  }
527  } else {
528  $userstatic = new User($db);
529  $userstatic->fetch($object->user_signature_id ? $object->user_signature_id : $object->user_signature);
530  if ($userstatic->id) {
531  print $userstatic->getNomUrl(-1, '', 0, 0, 0);
532  } else {
533  print $langs->trans('Unknown');
534  }
535  }
536  if ($usetable) {
537  print '</td></tr>';
538  } else {
539  print '<br>';
540  }
541  }
542 
543  // Date signature
544  if (!empty($object->date_signature)) {
545  if ($usetable) {
546  print '<tr><td class="titlefield">';
547  }
548  print $langs->trans('DateSigning');
549  if ($usetable) {
550  print '</td><td>';
551  } else {
552  print ': ';
553  }
554  print dol_print_date($object->date_signature, 'dayhour');
555  if ($deltadateforuser) {
556  print ' <span class="opacitymedium">'.$langs->trans('CurrentHour').'</span> &nbsp; / &nbsp; '.dol_print_date($object->date_signature, 'dayhour', 'tzuserrel').' &nbsp;<span class="opacitymedium">'.$langs->trans('ClientHour').'</span>';
557  }
558  if ($usetable) {
559  print '</td></tr>';
560  } else {
561  print '<br>';
562  }
563  }
564 
565  // User close
566  if (!empty($object->user_cloture) || !empty($object->user_closing) || !empty($object->user_closing_id)) {
567  if (isset($object->user_cloture) && !empty($object->user_cloture)) {
568  $object->user_closing = $object->user_cloture;
569  }
570  if ($usetable) {
571  print '<tr><td class="titlefield">';
572  }
573  print $langs->trans("ClosedBy");
574  if ($usetable) {
575  print '</td><td>';
576  } else {
577  print ': ';
578  }
579  if (is_object($object->user_closing)) {
580  if ($object->user_closing->id) {
581  print $object->user_closing->getNomUrl(-1, '', 0, 0, 0);
582  } else {
583  print $langs->trans("Unknown");
584  }
585  } else {
586  $userstatic = new User($db);
587  $userstatic->fetch($object->user_closing_id ? $object->user_closing_id : $object->user_closing);
588  if ($userstatic->id) {
589  print $userstatic->getNomUrl(-1, '', 0, 0, 0);
590  } else {
591  print $langs->trans("Unknown");
592  }
593  }
594  if ($usetable) {
595  print '</td></tr>';
596  } else {
597  print '<br>';
598  }
599  }
600 
601  // Date close
602  if (!empty($object->date_cloture) || !empty($object->date_closing)) {
603  if (isset($object->date_cloture) && !empty($object->date_cloture)) {
604  $object->date_closing = $object->date_cloture;
605  }
606  if ($usetable) {
607  print '<tr><td class="titlefield">';
608  }
609  print $langs->trans("DateClosing");
610  if ($usetable) {
611  print '</td><td>';
612  } else {
613  print ': ';
614  }
615  print dol_print_date($object->date_closing, 'dayhour', 'tzserver');
616  if ($deltadateforuser) {
617  print ' <span class="opacitymedium">'.$langs->trans("CurrentHour").'</span> &nbsp; / &nbsp; '.dol_print_date($object->date_closing, "dayhour", 'tzuserrel').' &nbsp;<span class="opacitymedium">'.$langs->trans("ClientHour").'</span>';
618  }
619  if ($usetable) {
620  print '</td></tr>';
621  } else {
622  print '<br>';
623  }
624  }
625 
626  // User conciliate
627  if (!empty($object->user_rappro) || !empty($object->user_rappro_id)) {
628  if ($usetable) {
629  print '<tr><td class="titlefield">';
630  }
631  print $langs->trans("ReconciledBy");
632  if ($usetable) {
633  print '</td><td>';
634  } else {
635  print ': ';
636  }
637  if (is_object($object->user_rappro)) {
638  if ($object->user_rappro->id) {
639  print $object->user_rappro->getNomUrl(-1, '', 0, 0, 0);
640  } else {
641  print $langs->trans("Unknown");
642  }
643  } else {
644  $userstatic = new User($db);
645  $userstatic->fetch($object->user_rappro_id ? $object->user_rappro_id : $object->user_rappro);
646  if ($userstatic->id) {
647  print $userstatic->getNomUrl(1, '', 0, 0, 0);
648  } else {
649  print $langs->trans("Unknown");
650  }
651  }
652  if ($usetable) {
653  print '</td></tr>';
654  } else {
655  print '<br>';
656  }
657  }
658 
659  // Date conciliate
660  if (!empty($object->date_rappro)) {
661  if ($usetable) {
662  print '<tr><td class="titlefield">';
663  }
664  print $langs->trans("DateConciliating");
665  if ($usetable) {
666  print '</td><td>';
667  } else {
668  print ': ';
669  }
670  print dol_print_date($object->date_rappro, 'dayhour', 'tzserver');
671  if ($deltadateforuser) {
672  print ' <span class="opacitymedium">'.$langs->trans("CurrentHour").'</span> &nbsp; / &nbsp; '.dol_print_date($object->date_rappro, "dayhour", 'tzuserrel').' &nbsp;<span class="opacitymedium">'.$langs->trans("ClientHour").'</span>';
673  }
674  if ($usetable) {
675  print '</td></tr>';
676  } else {
677  print '<br>';
678  }
679  }
680 
681  // Date send
682  if (!empty($object->date_envoi)) {
683  if ($usetable) {
684  print '<tr><td class="titlefield">';
685  }
686  print $langs->trans("DateLastSend");
687  if ($usetable) {
688  print '</td><td>';
689  } else {
690  print ': ';
691  }
692  print dol_print_date($object->date_envoi, 'dayhour', 'tzserver');
693  if ($deltadateforuser) {
694  print ' <span class="opacitymedium">'.$langs->trans("CurrentHour").'</span> &nbsp; / &nbsp; '.dol_print_date($object->date_envoi, "dayhour", 'tzuserrel').' &nbsp;<span class="opacitymedium">'.$langs->trans("ClientHour").'</span>';
695  }
696  if ($usetable) {
697  print '</td></tr>';
698  } else {
699  print '<br>';
700  }
701  }
702 
703  if ($usetable) {
704  print '</table>';
705  }
706 }
707 
708 
717 function dolAddEmailTrackId($email, $trackingid)
718 {
719  $tmp = explode('@', $email);
720  return $tmp[0].'+'.$trackingid.'@'.(isset($tmp[1]) ? $tmp[1] : '');
721 }
722 
729 function isValidMailDomain($mail)
730 {
731  list($user, $domain) = explode("@", $mail, 2);
732  return ($domain ? isValidMXRecord($domain) : 0);
733 }
734 
748 function isValidUrl($url, $http = 0, $pass = 0, $port = 0, $path = 0, $query = 0, $anchor = 0)
749 {
750  $ValidUrl = 0;
751  $urlregex = '';
752 
753  // SCHEME
754  if ($http) {
755  $urlregex .= "^(http:\/\/|https:\/\/)";
756  }
757 
758  // USER AND PASS
759  if ($pass) {
760  $urlregex .= "([a-z0-9+!*(),;?&=\$_.-]+(\:[a-z0-9+!*(),;?&=\$_.-]+)?@)";
761  }
762 
763  // HOSTNAME OR IP
764  //$urlregex .= "[a-z0-9+\$_-]+(\.[a-z0-9+\$_-]+)*"; // x allowed (ex. http://localhost, http://routerlogin)
765  //$urlregex .= "[a-z0-9+\$_-]+(\.[a-z0-9+\$_-]+)+"; // x.x
766  $urlregex .= "([a-z0-9+\$_\\\:-])+(\.[a-z0-9+\$_-][a-z0-9+\$_-]+)*"; // x ou x.xx (2 x ou plus)
767  //use only one of the above
768 
769  // PORT
770  if ($port) {
771  $urlregex .= "(\:[0-9]{2,5})";
772  }
773  // PATH
774  if ($path) {
775  $urlregex .= "(\/([a-z0-9+\$_-]\.?)+)*\/";
776  }
777  // GET Query
778  if ($query) {
779  $urlregex .= "(\?[a-z+&\$_.-][a-z0-9;:@\/&%=+\$_.-]*)";
780  }
781  // ANCHOR
782  if ($anchor) {
783  $urlregex .= "(#[a-z_.-][a-z0-9+\$_.-]*)$";
784  }
785 
786  // check
787  if (preg_match('/'.$urlregex.'/i', $url)) {
788  $ValidUrl = 1;
789  }
790  //print $urlregex.' - '.$url.' - '.$ValidUrl;
791 
792  return $ValidUrl;
793 }
794 
801 function isValidVATID($company)
802 {
803  if ($company->isInEEC()) { // Syntax check rules for EEC countries
804  /* Disabled because some companies can have an address in Irland and a vat number in France.
805  $vatprefix = $company->country_code;
806  if ($vatprefix == 'GR') $vatprefix = '(EL|GR)';
807  elseif ($vatprefix == 'MC') $vatprefix = 'FR'; // Monaco is using french VAT numbers
808  else $vatprefix = preg_quote($vatprefix, '/');*/
809  $vatprefix = '[a-zA-Z][a-zA-Z]';
810  if (!preg_match('/^'.$vatprefix.'[a-zA-Z0-9\-\.]{5,14}$/i', str_replace(' ', '', $company->tva_intra))) {
811  return 0;
812  }
813  }
814 
815  return 1;
816 }
817 
825 function clean_url($url, $http = 1)
826 {
827  // Fixed by Matelli (see http://matelli.fr/showcases/patchs-dolibarr/fix-cleaning-url.html)
828  // To include the minus sign in a char class, we must not escape it but put it at the end of the class
829  // Also, there's no need of escape a dot sign in a class
830  $regs = array();
831  if (preg_match('/^(https?:[\\/]+)?([0-9A-Z.-]+\.[A-Z]{2,4})(:[0-9]+)?/i', $url, $regs)) {
832  $proto = $regs[1];
833  $domain = $regs[2];
834  $port = isset($regs[3]) ? $regs[3] : '';
835  //print $url." -> ".$proto." - ".$domain." - ".$port;
836  //$url = dol_string_nospecial(trim($url));
837  $url = trim($url);
838 
839  // Si http: defini on supprime le http (Si https on ne supprime pas)
840  $newproto = $proto;
841  if ($http == 0) {
842  if (preg_match('/^http:[\\/]+/i', $url)) {
843  $url = preg_replace('/^http:[\\/]+/i', '', $url);
844  $newproto = '';
845  }
846  }
847 
848  // On passe le nom de domaine en minuscule
849  $CleanUrl = preg_replace('/^'.preg_quote($proto.$domain, '/').'/i', $newproto.strtolower($domain), $url);
850 
851  return $CleanUrl;
852  } else {
853  return $url;
854  }
855 }
856 
857 
858 
870 function dolObfuscateEmail($mail, $replace = "*", $nbreplace = 8, $nbdisplaymail = 4, $nbdisplaydomain = 3, $displaytld = true)
871 {
872  if (!isValidEmail($mail)) {
873  return '';
874  }
875  $tab = explode('@', $mail);
876  $tab2 = explode('.', $tab[1]);
877  $string_replace = '';
878  $mail_name = $tab[0];
879  $mail_domaine = $tab2[0];
880  $mail_tld = '';
881 
882  $nbofelem = count($tab2);
883  for ($i = 1; $i < $nbofelem && $displaytld; $i++) {
884  $mail_tld .= '.'.$tab2[$i];
885  }
886 
887  for ($i = 0; $i < $nbreplace; $i++) {
888  $string_replace .= $replace;
889  }
890 
891  if (strlen($mail_name) > $nbdisplaymail) {
892  $mail_name = substr($mail_name, 0, $nbdisplaymail);
893  }
894 
895  if (strlen($mail_domaine) > $nbdisplaydomain) {
896  $mail_domaine = substr($mail_domaine, strlen($mail_domaine) - $nbdisplaydomain);
897  }
898 
899  return $mail_name.$string_replace.$mail_domaine.$mail_tld;
900 }
901 
902 
912 function array2tr($data, $troptions = '', $tdoptions = '')
913 {
914  $text = '<tr '.$troptions.'>';
915  foreach ($data as $key => $item) {
916  $text .= '<td '.$tdoptions.'>'.$item.'</td>';
917  }
918  $text .= '</tr>';
919  return $text;
920 }
921 
932 function array2table($data, $tableMarkup = 1, $tableoptions = '', $troptions = '', $tdoptions = '')
933 {
934  $text = '';
935  if ($tableMarkup) {
936  $text = '<table '.$tableoptions.'>';
937  }
938  foreach ($data as $key => $item) {
939  if (is_array($item)) {
940  $text .= array2tr($item, $troptions, $tdoptions);
941  } else {
942  $text .= '<tr '.$troptions.'>';
943  $text .= '<td '.$tdoptions.'>'.$key.'</td>';
944  $text .= '<td '.$tdoptions.'>'.$item.'</td>';
945  $text .= '</tr>';
946  }
947  }
948  if ($tableMarkup) {
949  $text .= '</table>';
950  }
951  return $text;
952 }
953 
970 function get_next_value($db, $mask, $table, $field, $where = '', $objsoc = '', $date = '', $mode = 'next', $bentityon = true, $objuser = null, $forceentity = null)
971 {
972  global $conf, $user;
973 
974  if (!is_object($objsoc)) {
975  $valueforccc = $objsoc;
976  } elseif ($table == "commande_fournisseur" || $table == "facture_fourn") {
977  $valueforccc = dol_string_unaccent($objsoc->code_fournisseur);
978  } else {
979  $valueforccc = dol_string_unaccent($objsoc->code_client);
980  }
981 
982  $sharetable = $table;
983  if ($table == 'facture' || $table == 'invoice') {
984  $sharetable = 'invoicenumber'; // for getEntity function
985  }
986 
987  // Clean parameters
988  if ($date == '') {
989  $date = dol_now(); // We use local year and month of PHP server to search numbers
990  }
991  // but we should use local year and month of user
992 
993  // For debugging
994  //dol_syslog("mask=".$mask, LOG_DEBUG);
995  //include_once(DOL_DOCUMENT_ROOT.'/core/lib/date.lib.php');
996  //$mask='FA{yy}{mm}-{0000@99}';
997  //$date=dol_mktime(12, 0, 0, 1, 1, 1900);
998  //$date=dol_stringtotime('20130101');
999 
1000  $hasglobalcounter = false;
1001  $reg = array();
1002  // Extract value for mask counter, mask raz and mask offset
1003  if (preg_match('/\{(0+)([@\+][0-9\-\+\=]+)?([@\+][0-9\-\+\=]+)?\}/i', $mask, $reg)) {
1004  $masktri = $reg[1].(!empty($reg[2]) ? $reg[2] : '').(!empty($reg[3]) ? $reg[3] : '');
1005  $maskcounter = $reg[1];
1006  $hasglobalcounter = true;
1007  } else {
1008  // setting some defaults so the rest of the code won't fail if there is a third party counter
1009  $masktri = '00000';
1010  $maskcounter = '00000';
1011  }
1012 
1013  $maskraz = -1;
1014  $maskoffset = 0;
1015  $resetEveryMonth = false;
1016  if (dol_strlen($maskcounter) < 3 && empty($conf->global->MAIN_COUNTER_WITH_LESS_3_DIGITS)) {
1017  return 'ErrorCounterMustHaveMoreThan3Digits';
1018  }
1019 
1020  // Extract value for third party mask counter
1021  $regClientRef = array();
1022  if (preg_match('/\{(c+)(0*)\}/i', $mask, $regClientRef)) {
1023  $maskrefclient = $regClientRef[1].$regClientRef[2];
1024  $maskrefclient_maskclientcode = $regClientRef[1];
1025  $maskrefclient_maskcounter = $regClientRef[2];
1026  $maskrefclient_maskoffset = 0; //default value of maskrefclient_counter offset
1027  $maskrefclient_clientcode = substr($valueforccc, 0, dol_strlen($maskrefclient_maskclientcode)); //get n first characters of client code where n is length in mask
1028  $maskrefclient_clientcode = str_pad($maskrefclient_clientcode, dol_strlen($maskrefclient_maskclientcode), "#", STR_PAD_RIGHT); //padding maskrefclient_clientcode for having exactly n characters in maskrefclient_clientcode
1029  $maskrefclient_clientcode = dol_string_nospecial($maskrefclient_clientcode); //sanitize maskrefclient_clientcode for sql insert and sql select like
1030  if (dol_strlen($maskrefclient_maskcounter) > 0 && dol_strlen($maskrefclient_maskcounter) < 3) {
1031  return 'ErrorCounterMustHaveMoreThan3Digits';
1032  }
1033  } else {
1034  $maskrefclient = '';
1035  }
1036 
1037  // fail if there is neither a global nor a third party counter
1038  if (!$hasglobalcounter && ($maskrefclient_maskcounter == '')) {
1039  return 'ErrorBadMask';
1040  }
1041 
1042  // Extract value for third party type
1043  $regType = array();
1044  if (preg_match('/\{(t+)\}/i', $mask, $regType)) {
1045  $masktype = $regType[1];
1046  $masktype_value = dol_substr(preg_replace('/^TE_/', '', $objsoc->typent_code), 0, dol_strlen($regType[1])); // get n first characters of thirdparty typent_code (where n is length in mask)
1047  $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
1048  } else {
1049  $masktype = '';
1050  $masktype_value = '';
1051  }
1052 
1053  // Extract value for user
1054  $regType = array();
1055  if (preg_match('/\{(u+)\}/i', $mask, $regType)) {
1056  $lastname = 'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX';
1057  if (is_object($objuser)) {
1058  $lastname = $objuser->lastname;
1059  }
1060 
1061  $maskuser = $regType[1];
1062  $maskuser_value = substr($lastname, 0, dol_strlen($regType[1])); // get n first characters of user firstname (where n is length in mask)
1063  $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
1064  } else {
1065  $maskuser = '';
1066  $maskuser_value = '';
1067  }
1068 
1069  // Personalized field {XXX-1} à {XXX-9}
1070  $maskperso = array();
1071  $maskpersonew = array();
1072  $tmpmask = $mask;
1073  $regKey = array();
1074  while (preg_match('/\{([A-Z]+)\-([1-9])\}/', $tmpmask, $regKey)) {
1075  $maskperso[$regKey[1]] = '{'.$regKey[1].'-'.$regKey[2].'}';
1076  $maskpersonew[$regKey[1]] = str_pad('', $regKey[2], '_', STR_PAD_RIGHT);
1077  $tmpmask = preg_replace('/\{'.$regKey[1].'\-'.$regKey[2].'\}/i', $maskpersonew[$regKey[1]], $tmpmask);
1078  }
1079 
1080  if (strstr($mask, 'user_extra_')) {
1081  $start = "{user_extra_";
1082  $end = "\}";
1083  $extra = get_string_between($mask, "user_extra_", "}");
1084  if (!empty($user->array_options['options_'.$extra])) {
1085  $mask = preg_replace('#('.$start.')(.*?)('.$end.')#si', $user->array_options['options_'.$extra], $mask);
1086  }
1087  }
1088  $maskwithonlyymcode = $mask;
1089  $maskwithonlyymcode = preg_replace('/\{(0+)([@\+][0-9\-\+\=]+)?([@\+][0-9\-\+\=]+)?\}/i', $maskcounter, $maskwithonlyymcode);
1090  $maskwithonlyymcode = preg_replace('/\{dd\}/i', 'dd', $maskwithonlyymcode);
1091  $maskwithonlyymcode = preg_replace('/\{(c+)(0*)\}/i', $maskrefclient, $maskwithonlyymcode);
1092  $maskwithonlyymcode = preg_replace('/\{(t+)\}/i', $masktype_value, $maskwithonlyymcode);
1093  $maskwithonlyymcode = preg_replace('/\{(u+)\}/i', $maskuser_value, $maskwithonlyymcode);
1094  foreach ($maskperso as $key => $val) {
1095  $maskwithonlyymcode = preg_replace('/'.preg_quote($val, '/').'/i', $maskpersonew[$key], $maskwithonlyymcode);
1096  }
1097  $maskwithnocode = $maskwithonlyymcode;
1098  $maskwithnocode = preg_replace('/\{yyyy\}/i', 'yyyy', $maskwithnocode);
1099  $maskwithnocode = preg_replace('/\{yy\}/i', 'yy', $maskwithnocode);
1100  $maskwithnocode = preg_replace('/\{y\}/i', 'y', $maskwithnocode);
1101  $maskwithnocode = preg_replace('/\{mm\}/i', 'mm', $maskwithnocode);
1102  // Now maskwithnocode = 0000ddmmyyyyccc for example
1103  // and maskcounter = 0000 for example
1104  //print "maskwithonlyymcode=".$maskwithonlyymcode." maskwithnocode=".$maskwithnocode."\n<br>";
1105  //var_dump($reg);
1106 
1107  // If an offset is asked
1108  if (!empty($reg[2]) && preg_match('/^\+/', $reg[2])) {
1109  $maskoffset = preg_replace('/^\+/', '', $reg[2]);
1110  }
1111  if (!empty($reg[3]) && preg_match('/^\+/', $reg[3])) {
1112  $maskoffset = preg_replace('/^\+/', '', $reg[3]);
1113  }
1114 
1115  // Define $sqlwhere
1116  $sqlwhere = '';
1117  $yearoffset = 0; // Use year of current $date by default
1118  $yearoffsettype = false; // false: no reset, 0,-,=,+: reset at offset SOCIETE_FISCAL_MONTH_START, x=reset at offset x
1119 
1120  // If a restore to zero after a month is asked we check if there is already a value for this year.
1121  if (!empty($reg[2]) && preg_match('/^@/', $reg[2])) {
1122  $yearoffsettype = preg_replace('/^@/', '', $reg[2]);
1123  }
1124  if (!empty($reg[3]) && preg_match('/^@/', $reg[3])) {
1125  $yearoffsettype = preg_replace('/^@/', '', $reg[3]);
1126  }
1127 
1128  //print "yearoffset=".$yearoffset." yearoffsettype=".$yearoffsettype;
1129  if (is_numeric($yearoffsettype) && $yearoffsettype >= 1) {
1130  $maskraz = $yearoffsettype; // For backward compatibility
1131  } elseif ($yearoffsettype === '0' || (!empty($yearoffsettype) && !is_numeric($yearoffsettype) && $conf->global->SOCIETE_FISCAL_MONTH_START > 1)) {
1132  $maskraz = $conf->global->SOCIETE_FISCAL_MONTH_START;
1133  }
1134  //print "maskraz=".$maskraz; // -1=no reset
1135 
1136  if ($maskraz > 0) { // A reset is required
1137  if ($maskraz == 99) {
1138  $maskraz = date('m', $date);
1139  $resetEveryMonth = true;
1140  }
1141  if ($maskraz > 12) {
1142  return 'ErrorBadMaskBadRazMonth';
1143  }
1144 
1145  // Define posy, posm and reg
1146  if ($maskraz > 1) { // if reset is not first month, we need month and year into mask
1147  if (preg_match('/^(.*)\{(y+)\}\{(m+)\}/i', $maskwithonlyymcode, $reg)) {
1148  $posy = 2;
1149  $posm = 3;
1150  } elseif (preg_match('/^(.*)\{(m+)\}\{(y+)\}/i', $maskwithonlyymcode, $reg)) {
1151  $posy = 3;
1152  $posm = 2;
1153  } else {
1154  return 'ErrorCantUseRazInStartedYearIfNoYearMonthInMask';
1155  }
1156 
1157  if (dol_strlen($reg[$posy]) < 2) {
1158  return 'ErrorCantUseRazWithYearOnOneDigit';
1159  }
1160  } else // if reset is for a specific month in year, we need year
1161  {
1162  if (preg_match('/^(.*)\{(m+)\}\{(y+)\}/i', $maskwithonlyymcode, $reg)) {
1163  $posy = 3;
1164  $posm = 2;
1165  } elseif (preg_match('/^(.*)\{(y+)\}\{(m+)\}/i', $maskwithonlyymcode, $reg)) {
1166  $posy = 2;
1167  $posm = 3;
1168  } elseif (preg_match('/^(.*)\{(y+)\}/i', $maskwithonlyymcode, $reg)) {
1169  $posy = 2;
1170  $posm = 0;
1171  } else {
1172  return 'ErrorCantUseRazIfNoYearInMask';
1173  }
1174  }
1175  // Define length
1176  $yearlen = $posy ?dol_strlen($reg[$posy]) : 0;
1177  $monthlen = $posm ?dol_strlen($reg[$posm]) : 0;
1178  // Define pos
1179  $yearpos = (dol_strlen($reg[1]) + 1);
1180  $monthpos = ($yearpos + $yearlen);
1181  if ($posy == 3 && $posm == 2) { // if month is before year
1182  $monthpos = (dol_strlen($reg[1]) + 1);
1183  $yearpos = ($monthpos + $monthlen);
1184  }
1185  //print "xxx ".$maskwithonlyymcode." maskraz=".$maskraz." posy=".$posy." yearlen=".$yearlen." yearpos=".$yearpos." posm=".$posm." monthlen=".$monthlen." monthpos=".$monthpos." yearoffsettype=".$yearoffsettype." resetEveryMonth=".$resetEveryMonth."\n";
1186 
1187  // Define $yearcomp and $monthcomp (that will be use in the select where to search max number)
1188  $monthcomp = $maskraz;
1189  $yearcomp = 0;
1190 
1191  if (!empty($yearoffsettype) && !is_numeric($yearoffsettype) && $yearoffsettype != '=') { // $yearoffsettype is - or +
1192  $currentyear = date("Y", $date);
1193  $fiscaldate = dol_mktime('0', '0', '0', $maskraz, '1', $currentyear);
1194  $newyeardate = dol_mktime('0', '0', '0', '1', '1', $currentyear);
1195  $nextnewyeardate = dol_mktime('0', '0', '0', '1', '1', $currentyear + 1);
1196  //echo 'currentyear='.$currentyear.' date='.dol_print_date($date, 'day').' fiscaldate='.dol_print_date($fiscaldate, 'day').'<br>';
1197 
1198  // If after or equal of current fiscal date
1199  if ($date >= $fiscaldate) {
1200  // If before of next new year date
1201  if ($date < $nextnewyeardate && $yearoffsettype == '+') {
1202  $yearoffset = 1;
1203  }
1204  } elseif ($date >= $newyeardate && $yearoffsettype == '-') {
1205  // If after or equal of current new year date
1206  $yearoffset = -1;
1207  }
1208  } elseif (date("m", $date) < $maskraz && empty($resetEveryMonth)) {
1209  // For backward compatibility
1210  $yearoffset = -1;
1211  } // If current month lower that month of return to zero, year is previous year
1212 
1213  if ($yearlen == 4) {
1214  $yearcomp = sprintf("%04d", date("Y", $date) + $yearoffset);
1215  } elseif ($yearlen == 2) {
1216  $yearcomp = sprintf("%02d", date("y", $date) + $yearoffset);
1217  } elseif ($yearlen == 1) {
1218  $yearcomp = substr(date('y', $date), 1, 1) + $yearoffset;
1219  }
1220  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)
1221  if ($yearlen == 4) {
1222  $yearcomp1 = sprintf("%04d", date("Y", $date) + $yearoffset + 1);
1223  } elseif ($yearlen == 2) {
1224  $yearcomp1 = sprintf("%02d", date("y", $date) + $yearoffset + 1);
1225  }
1226 
1227  $sqlwhere .= "(";
1228  $sqlwhere .= " (SUBSTRING(".$field.", ".$yearpos.", ".$yearlen.") = '".$db->escape($yearcomp)."'";
1229  $sqlwhere .= " AND SUBSTRING(".$field.", ".$monthpos.", ".$monthlen.") >= '".str_pad($monthcomp, $monthlen, '0', STR_PAD_LEFT)."')";
1230  $sqlwhere .= " OR";
1231  $sqlwhere .= " (SUBSTRING(".$field.", ".$yearpos.", ".$yearlen.") = '".$db->escape($yearcomp1)."'";
1232  $sqlwhere .= " AND SUBSTRING(".$field.", ".$monthpos.", ".$monthlen.") < '".str_pad($monthcomp, $monthlen, '0', STR_PAD_LEFT)."') ";
1233  $sqlwhere .= ')';
1234  } elseif ($resetEveryMonth) {
1235  $sqlwhere .= "(SUBSTRING(".$field.", ".$yearpos.", ".$yearlen.") = '".$db->escape($yearcomp)."'";
1236  $sqlwhere .= " AND SUBSTRING(".$field.", ".$monthpos.", ".$monthlen.") = '".str_pad($monthcomp, $monthlen, '0', STR_PAD_LEFT)."')";
1237  } else { // reset is done on january
1238  $sqlwhere .= "(SUBSTRING(".$field.", ".$yearpos.", ".$yearlen.") = '".$db->escape($yearcomp)."')";
1239  }
1240  }
1241  //print "sqlwhere=".$sqlwhere." yearcomp=".$yearcomp."<br>\n"; // sqlwhere and yearcomp defined only if we ask a reset
1242  //print "masktri=".$masktri." maskcounter=".$maskcounter." maskraz=".$maskraz." maskoffset=".$maskoffset."<br>\n";
1243 
1244  // Define $sqlstring
1245  if (function_exists('mb_strrpos')) {
1246  $posnumstart = mb_strrpos($maskwithnocode, $maskcounter, 0, 'UTF-8');
1247  } else {
1248  $posnumstart = strrpos($maskwithnocode, $maskcounter);
1249  } // Pos of counter in final string (from 0 to ...)
1250  if ($posnumstart < 0) {
1251  return 'ErrorBadMaskFailedToLocatePosOfSequence';
1252  }
1253  $sqlstring = "SUBSTRING(".$field.", ".($posnumstart + 1).", ".dol_strlen($maskcounter).")";
1254 
1255  // Define $maskLike
1256  $maskLike = dol_string_nospecial($mask);
1257  $maskLike = str_replace("%", "_", $maskLike);
1258 
1259  // Replace protected special codes with matching number of _ as wild card caracter
1260  $maskLike = preg_replace('/\{yyyy\}/i', '____', $maskLike);
1261  $maskLike = preg_replace('/\{yy\}/i', '__', $maskLike);
1262  $maskLike = preg_replace('/\{y\}/i', '_', $maskLike);
1263  $maskLike = preg_replace('/\{mm\}/i', '__', $maskLike);
1264  $maskLike = preg_replace('/\{dd\}/i', '__', $maskLike);
1265  $maskLike = str_replace(dol_string_nospecial('{'.$masktri.'}'), str_pad("", dol_strlen($maskcounter), "_"), $maskLike);
1266  if ($maskrefclient) {
1267  $maskLike = str_replace(dol_string_nospecial('{'.$maskrefclient.'}'), str_pad("", dol_strlen($maskrefclient), "_"), $maskLike);
1268  }
1269  if ($masktype) {
1270  $maskLike = str_replace(dol_string_nospecial('{'.$masktype.'}'), $masktype_value, $maskLike);
1271  }
1272  if ($maskuser) {
1273  $maskLike = str_replace(dol_string_nospecial('{'.$maskuser.'}'), $maskuser_value, $maskLike);
1274  }
1275  foreach ($maskperso as $key => $val) {
1276  $maskLike = str_replace(dol_string_nospecial($maskperso[$key]), $maskpersonew[$key], $maskLike);
1277  }
1278 
1279  // Get counter in database
1280  $counter = 0;
1281  $sql = "SELECT MAX(".$sqlstring.") as val";
1282  $sql .= " FROM ".MAIN_DB_PREFIX.$table;
1283  $sql .= " WHERE ".$field." LIKE '".$db->escape($maskLike) . (!empty($conf->global->SEARCH_FOR_NEXT_VAL_ON_START_ONLY) ? "%" : "") . "'";
1284  $sql .= " AND ".$field." NOT LIKE '(PROV%)'";
1285 
1286  // To ensure that all variables within the MAX() brackets are integers
1287  if (getDolGlobalInt('MAIN_NUMBERING_FILTER_ON_INT_ONLY')) {
1288  $sql .= " AND ". $db->regexpsql($sqlstring, '^[0-9]+$', true);
1289  }
1290 
1291  if ($bentityon) { // only if entity enable
1292  $sql .= " AND entity IN (".getEntity($sharetable).")";
1293  } elseif (!empty($forceentity)) {
1294  $sql .= " AND entity IN (".$db->sanitize($forceentity).")";
1295  }
1296  if ($where) {
1297  $sql .= $where;
1298  }
1299  if ($sqlwhere) {
1300  $sql .= " AND ".$sqlwhere;
1301  }
1302 
1303  //print $sql.'<br>';
1304  dol_syslog("functions2::get_next_value mode=".$mode, LOG_DEBUG);
1305  $resql = $db->query($sql);
1306  if ($resql) {
1307  $obj = $db->fetch_object($resql);
1308  $counter = $obj->val;
1309  } else {
1310  dol_print_error($db);
1311  }
1312 
1313  // Check if we must force counter to maskoffset
1314  if (empty($counter)) {
1315  $counter = $maskoffset;
1316  } elseif (preg_match('/[^0-9]/i', $counter)) {
1317  dol_syslog("Error, the last counter found is '".$counter."' so is not a numeric value. We will restart to 1.", LOG_ERR);
1318  $counter = 0;
1319  } elseif ($counter < $maskoffset && empty($conf->global->MAIN_NUMBERING_OFFSET_ONLY_FOR_FIRST)) {
1320  $counter = $maskoffset;
1321  }
1322 
1323  if ($mode == 'last') { // We found value for counter = last counter value. Now need to get corresponding ref of invoice.
1324  $counterpadded = str_pad($counter, dol_strlen($maskcounter), "0", STR_PAD_LEFT);
1325 
1326  // Define $maskLike
1327  $maskLike = dol_string_nospecial($mask);
1328  $maskLike = str_replace("%", "_", $maskLike);
1329  // Replace protected special codes with matching number of _ as wild card caracter
1330  $maskLike = preg_replace('/\{yyyy\}/i', '____', $maskLike);
1331  $maskLike = preg_replace('/\{yy\}/i', '__', $maskLike);
1332  $maskLike = preg_replace('/\{y\}/i', '_', $maskLike);
1333  $maskLike = preg_replace('/\{mm\}/i', '__', $maskLike);
1334  $maskLike = preg_replace('/\{dd\}/i', '__', $maskLike);
1335  $maskLike = str_replace(dol_string_nospecial('{'.$masktri.'}'), $counterpadded, $maskLike);
1336  if ($maskrefclient) {
1337  $maskLike = str_replace(dol_string_nospecial('{'.$maskrefclient.'}'), str_pad("", dol_strlen($maskrefclient), "_"), $maskLike);
1338  }
1339  if ($masktype) {
1340  $maskLike = str_replace(dol_string_nospecial('{'.$masktype.'}'), $masktype_value, $maskLike);
1341  }
1342  if ($maskuser) {
1343  $maskLike = str_replace(dol_string_nospecial('{'.$maskuser.'}'), $maskuser_value, $maskLike);
1344  }
1345 
1346  $ref = '';
1347  $sql = "SELECT ".$field." as ref";
1348  $sql .= " FROM ".MAIN_DB_PREFIX.$table;
1349  $sql .= " WHERE ".$field." LIKE '".$db->escape($maskLike) . (!empty($conf->global->SEARCH_FOR_NEXT_VAL_ON_START_ONLY) ? "%" : "") . "'";
1350  $sql .= " AND ".$field." NOT LIKE '%PROV%'";
1351  if ($bentityon) { // only if entity enable
1352  $sql .= " AND entity IN (".getEntity($sharetable).")";
1353  } elseif (!empty($forceentity)) {
1354  $sql .= " AND entity IN (".$db->sanitize($forceentity).")";
1355  }
1356  if ($where) {
1357  $sql .= $where;
1358  }
1359  if ($sqlwhere) {
1360  $sql .= " AND ".$sqlwhere;
1361  }
1362 
1363  dol_syslog("functions2::get_next_value mode=".$mode, LOG_DEBUG);
1364  $resql = $db->query($sql);
1365  if ($resql) {
1366  $obj = $db->fetch_object($resql);
1367  if ($obj) {
1368  $ref = $obj->ref;
1369  }
1370  } else {
1371  dol_print_error($db);
1372  }
1373 
1374  $numFinal = $ref;
1375  } elseif ($mode == 'next') {
1376  $counter++;
1377  $maskrefclient_counter = 0;
1378 
1379  // If value for $counter has a length higher than $maskcounter chars
1380  if ($counter >= pow(10, dol_strlen($maskcounter))) {
1381  $counter = 'ErrorMaxNumberReachForThisMask';
1382  }
1383 
1384  if (!empty($maskrefclient_maskcounter)) {
1385  //print "maskrefclient_maskcounter=".$maskrefclient_maskcounter." maskwithnocode=".$maskwithnocode." maskrefclient=".$maskrefclient."\n<br>";
1386 
1387  // Define $sqlstring
1388  $maskrefclient_posnumstart = strpos($maskwithnocode, $maskrefclient_maskcounter, strpos($maskwithnocode, $maskrefclient)); // Pos of counter in final string (from 0 to ...)
1389  if ($maskrefclient_posnumstart <= 0) {
1390  return 'ErrorBadMask';
1391  }
1392  $maskrefclient_sqlstring = 'SUBSTRING('.$field.', '.($maskrefclient_posnumstart + 1).', '.dol_strlen($maskrefclient_maskcounter).')';
1393  //print "x".$sqlstring;
1394 
1395  // Define $maskrefclient_maskLike
1396  $maskrefclient_maskLike = dol_string_nospecial($mask);
1397  $maskrefclient_maskLike = str_replace("%", "_", $maskrefclient_maskLike);
1398  // Replace protected special codes with matching number of _ as wild card caracter
1399  $maskrefclient_maskLike = str_replace(dol_string_nospecial('{yyyy}'), '____', $maskrefclient_maskLike);
1400  $maskrefclient_maskLike = str_replace(dol_string_nospecial('{yy}'), '__', $maskrefclient_maskLike);
1401  $maskrefclient_maskLike = str_replace(dol_string_nospecial('{y}'), '_', $maskrefclient_maskLike);
1402  $maskrefclient_maskLike = str_replace(dol_string_nospecial('{mm}'), '__', $maskrefclient_maskLike);
1403  $maskrefclient_maskLike = str_replace(dol_string_nospecial('{dd}'), '__', $maskrefclient_maskLike);
1404  $maskrefclient_maskLike = str_replace(dol_string_nospecial('{'.$masktri.'}'), str_pad("", dol_strlen($maskcounter), "_"), $maskrefclient_maskLike);
1405  $maskrefclient_maskLike = str_replace(dol_string_nospecial('{'.$maskrefclient.'}'), $maskrefclient_clientcode.str_pad("", dol_strlen($maskrefclient_maskcounter), "_"), $maskrefclient_maskLike);
1406 
1407  // Get counter in database
1408  $maskrefclient_sql = "SELECT MAX(".$maskrefclient_sqlstring.") as val";
1409  $maskrefclient_sql .= " FROM ".MAIN_DB_PREFIX.$table;
1410  //$sql.= " WHERE ".$field." not like '(%'";
1411  $maskrefclient_sql .= " WHERE ".$field." LIKE '".$db->escape($maskrefclient_maskLike) . (!empty($conf->global->SEARCH_FOR_NEXT_VAL_ON_START_ONLY) ? "%" : "") . "'";
1412  if ($bentityon) { // only if entity enable
1413  $maskrefclient_sql .= " AND entity IN (".getEntity($sharetable).")";
1414  } elseif (!empty($forceentity)) {
1415  $sql .= " AND entity IN (".$db->sanitize($forceentity).")";
1416  }
1417  if ($where) {
1418  $maskrefclient_sql .= $where; //use the same optional where as general mask
1419  }
1420  if ($sqlwhere) {
1421  $maskrefclient_sql .= ' AND '.$sqlwhere; //use the same sqlwhere as general mask
1422  }
1423  $maskrefclient_sql .= " AND (SUBSTRING(".$field.", ".(strpos($maskwithnocode, $maskrefclient) + 1).", ".dol_strlen($maskrefclient_maskclientcode).") = '".$db->escape($maskrefclient_clientcode)."')";
1424 
1425  dol_syslog("functions2::get_next_value maskrefclient", LOG_DEBUG);
1426  $maskrefclient_resql = $db->query($maskrefclient_sql);
1427  if ($maskrefclient_resql) {
1428  $maskrefclient_obj = $db->fetch_object($maskrefclient_resql);
1429  $maskrefclient_counter = $maskrefclient_obj->val;
1430  } else {
1431  dol_print_error($db);
1432  }
1433 
1434  if (empty($maskrefclient_counter) || preg_match('/[^0-9]/i', $maskrefclient_counter)) {
1435  $maskrefclient_counter = $maskrefclient_maskoffset;
1436  }
1437  $maskrefclient_counter++;
1438  }
1439 
1440  // Build numFinal
1441  $numFinal = $mask;
1442 
1443  // We replace special codes except refclient
1444  if (!empty($yearoffsettype) && !is_numeric($yearoffsettype) && $yearoffsettype != '=') { // yearoffsettype is - or +, so we don't want current year
1445  $numFinal = preg_replace('/\{yyyy\}/i', date("Y", $date) + $yearoffset, $numFinal);
1446  $numFinal = preg_replace('/\{yy\}/i', date("y", $date) + $yearoffset, $numFinal);
1447  $numFinal = preg_replace('/\{y\}/i', substr(date("y", $date), 1, 1) + $yearoffset, $numFinal);
1448  } else // we want yyyy to be current year
1449  {
1450  $numFinal = preg_replace('/\{yyyy\}/i', date("Y", $date), $numFinal);
1451  $numFinal = preg_replace('/\{yy\}/i', date("y", $date), $numFinal);
1452  $numFinal = preg_replace('/\{y\}/i', substr(date("y", $date), 1, 1), $numFinal);
1453  }
1454  $numFinal = preg_replace('/\{mm\}/i', date("m", $date), $numFinal);
1455  $numFinal = preg_replace('/\{dd\}/i', date("d", $date), $numFinal);
1456 
1457  // Now we replace the counter
1458  $maskbefore = '{'.$masktri.'}';
1459  $maskafter = str_pad($counter, dol_strlen($maskcounter), "0", STR_PAD_LEFT);
1460  //print 'x'.$numFinal.' - '.$maskbefore.' - '.$maskafter.'y';exit;
1461  $numFinal = str_replace($maskbefore, $maskafter, $numFinal);
1462 
1463  // Now we replace the refclient
1464  if ($maskrefclient) {
1465  //print "maskrefclient=".$maskrefclient." maskrefclient_counter=".$maskrefclient_counter." maskwithonlyymcode=".$maskwithonlyymcode." maskwithnocode=".$maskwithnocode." maskrefclient_clientcode=".$maskrefclient_clientcode." maskrefclient_maskcounter=".$maskrefclient_maskcounter."\n<br>";exit;
1466  $maskrefclient_maskbefore = '{'.$maskrefclient.'}';
1467  $maskrefclient_maskafter = $maskrefclient_clientcode;
1468  if (dol_strlen($maskrefclient_maskcounter) > 0) {
1469  $maskrefclient_maskafter .= str_pad($maskrefclient_counter, dol_strlen($maskrefclient_maskcounter), "0", STR_PAD_LEFT);
1470  }
1471  $numFinal = str_replace($maskrefclient_maskbefore, $maskrefclient_maskafter, $numFinal);
1472  }
1473 
1474  // Now we replace the type
1475  if ($masktype) {
1476  $masktype_maskbefore = '{'.$masktype.'}';
1477  $masktype_maskafter = $masktype_value;
1478  $numFinal = str_replace($masktype_maskbefore, $masktype_maskafter, $numFinal);
1479  }
1480 
1481  // Now we replace the user
1482  if ($maskuser) {
1483  $maskuser_maskbefore = '{'.$maskuser.'}';
1484  $maskuser_maskafter = $maskuser_value;
1485  $numFinal = str_replace($maskuser_maskbefore, $maskuser_maskafter, $numFinal);
1486  }
1487  }
1488 
1489  dol_syslog("functions2::get_next_value return ".$numFinal, LOG_DEBUG);
1490  return $numFinal;
1491 }
1492 
1501 function get_string_between($string, $start, $end)
1502 {
1503  $string = " ".$string;
1504  $ini = strpos($string, $start);
1505  if ($ini == 0) {
1506  return "";
1507  }
1508  $ini += strlen($start);
1509  $len = strpos($string, $end, $ini) - $ini;
1510  return substr($string, $ini, $len);
1511 }
1512 
1520 function check_value($mask, $value)
1521 {
1522  $result = 0;
1523 
1524  $hasglobalcounter = false;
1525  // Extract value for mask counter, mask raz and mask offset
1526  $reg = array();
1527  if (preg_match('/\{(0+)([@\+][0-9]+)?([@\+][0-9]+)?\}/i', $mask, $reg)) {
1528  $masktri = $reg[1].(isset($reg[2]) ? $reg[2] : '').(isset($reg[3]) ? $reg[3] : '');
1529  $maskcounter = $reg[1];
1530  $hasglobalcounter = true;
1531  } else {
1532  // setting some defaults so the rest of the code won't fail if there is a third party counter
1533  $masktri = '00000';
1534  $maskcounter = '00000';
1535  }
1536  $maskraz = -1;
1537  $maskoffset = 0;
1538  if (dol_strlen($maskcounter) < 3) {
1539  return 'ErrorCounterMustHaveMoreThan3Digits';
1540  }
1541 
1542  // Extract value for third party mask counter
1543  $regClientRef = array();
1544  if (preg_match('/\{(c+)(0*)\}/i', $mask, $regClientRef)) {
1545  $maskrefclient = $regClientRef[1].$regClientRef[2];
1546  $maskrefclient_maskclientcode = $regClientRef[1];
1547  $maskrefclient_maskcounter = $regClientRef[2];
1548  $maskrefclient_maskoffset = 0; //default value of maskrefclient_counter offset
1549  $maskrefclient_clientcode = substr('', 0, dol_strlen($maskrefclient_maskclientcode)); //get n first characters of client code to form maskrefclient_clientcode
1550  $maskrefclient_clientcode = str_pad($maskrefclient_clientcode, dol_strlen($maskrefclient_maskclientcode), "#", STR_PAD_RIGHT); //padding maskrefclient_clientcode for having exactly n characters in maskrefclient_clientcode
1551  $maskrefclient_clientcode = dol_string_nospecial($maskrefclient_clientcode); //sanitize maskrefclient_clientcode for sql insert and sql select like
1552  if (dol_strlen($maskrefclient_maskcounter) > 0 && dol_strlen($maskrefclient_maskcounter) < 3) {
1553  return 'ErrorCounterMustHaveMoreThan3Digits';
1554  }
1555  } else {
1556  $maskrefclient = '';
1557  }
1558 
1559  // fail if there is neither a global nor a third party counter
1560  if (!$hasglobalcounter && ($maskrefclient_maskcounter == '')) {
1561  return 'ErrorBadMask';
1562  }
1563 
1564  $maskwithonlyymcode = $mask;
1565  $maskwithonlyymcode = preg_replace('/\{(0+)([@\+][0-9]+)?([@\+][0-9]+)?\}/i', $maskcounter, $maskwithonlyymcode);
1566  $maskwithonlyymcode = preg_replace('/\{dd\}/i', 'dd', $maskwithonlyymcode);
1567  $maskwithonlyymcode = preg_replace('/\{(c+)(0*)\}/i', $maskrefclient, $maskwithonlyymcode);
1568  $maskwithnocode = $maskwithonlyymcode;
1569  $maskwithnocode = preg_replace('/\{yyyy\}/i', 'yyyy', $maskwithnocode);
1570  $maskwithnocode = preg_replace('/\{yy\}/i', 'yy', $maskwithnocode);
1571  $maskwithnocode = preg_replace('/\{y\}/i', 'y', $maskwithnocode);
1572  $maskwithnocode = preg_replace('/\{mm\}/i', 'mm', $maskwithnocode);
1573  // Now maskwithnocode = 0000ddmmyyyyccc for example
1574  // and maskcounter = 0000 for example
1575  //print "maskwithonlyymcode=".$maskwithonlyymcode." maskwithnocode=".$maskwithnocode."\n<br>";
1576 
1577  // If an offset is asked
1578  if (!empty($reg[2]) && preg_match('/^\+/', $reg[2])) {
1579  $maskoffset = preg_replace('/^\+/', '', $reg[2]);
1580  }
1581  if (!empty($reg[3]) && preg_match('/^\+/', $reg[3])) {
1582  $maskoffset = preg_replace('/^\+/', '', $reg[3]);
1583  }
1584 
1585  // Define $sqlwhere
1586 
1587  // If a restore to zero after a month is asked we check if there is already a value for this year.
1588  if (!empty($reg[2]) && preg_match('/^@/', $reg[2])) {
1589  $maskraz = preg_replace('/^@/', '', $reg[2]);
1590  }
1591  if (!empty($reg[3]) && preg_match('/^@/', $reg[3])) {
1592  $maskraz = preg_replace('/^@/', '', $reg[3]);
1593  }
1594  if ($maskraz >= 0) {
1595  if ($maskraz == 99) {
1596  $maskraz = date('m');
1597  $resetEveryMonth = true;
1598  }
1599  if ($maskraz > 12) {
1600  return 'ErrorBadMaskBadRazMonth';
1601  }
1602 
1603  // Define reg
1604  if ($maskraz > 1 && !preg_match('/^(.*)\{(y+)\}\{(m+)\}/i', $maskwithonlyymcode, $reg)) {
1605  return 'ErrorCantUseRazInStartedYearIfNoYearMonthInMask';
1606  }
1607  if ($maskraz <= 1 && !preg_match('/^(.*)\{(y+)\}/i', $maskwithonlyymcode, $reg)) {
1608  return 'ErrorCantUseRazIfNoYearInMask';
1609  }
1610  //print "x".$maskwithonlyymcode." ".$maskraz;
1611  }
1612  //print "masktri=".$masktri." maskcounter=".$maskcounter." maskwithonlyymcode=".$maskwithonlyymcode." maskwithnocode=".$maskwithnocode." maskraz=".$maskraz." maskoffset=".$maskoffset."<br>\n";
1613 
1614  if (function_exists('mb_strrpos')) {
1615  $posnumstart = mb_strrpos($maskwithnocode, $maskcounter, 0, 'UTF-8');
1616  } else {
1617  $posnumstart = strrpos($maskwithnocode, $maskcounter);
1618  } // Pos of counter in final string (from 0 to ...)
1619  if ($posnumstart < 0) {
1620  return 'ErrorBadMaskFailedToLocatePosOfSequence';
1621  }
1622 
1623  // Check we have a number in $value at position ($posnumstart+1).', '.dol_strlen($maskcounter)
1624  // TODO
1625 
1626  // Check length
1627  $len = dol_strlen($maskwithnocode);
1628  if (dol_strlen($value) != $len) {
1629  $result = -1;
1630  }
1631 
1632  dol_syslog("functions2::check_value result=".$result, LOG_DEBUG);
1633  return $result;
1634 }
1635 
1644 function binhex($bin, $pad = false, $upper = false)
1645 {
1646  $last = dol_strlen($bin) - 1;
1647  for ($i = 0; $i <= $last; $i++) {
1648  $x += $bin[$last - $i] * pow(2, $i);
1649  }
1650  $x = dechex($x);
1651  if ($pad) {
1652  while (dol_strlen($x) < intval(dol_strlen($bin)) / 4) {
1653  $x = "0$x";
1654  }
1655  }
1656  if ($upper) {
1657  $x = strtoupper($x);
1658  }
1659  return $x;
1660 }
1661 
1668 function hexbin($hexa)
1669 {
1670  $bin = '';
1671  $strLength = dol_strlen($hexa);
1672  for ($i = 0; $i < $strLength; $i++) {
1673  $bin .= str_pad(decbin(hexdec($hexa[$i])), 4, '0', STR_PAD_LEFT);
1674  }
1675  return $bin;
1676 }
1677 
1684 function numero_semaine($time)
1685 {
1686  $stime = strftime('%Y-%m-%d', $time);
1687 
1688  if (preg_match('/^([0-9]+)\-([0-9]+)\-([0-9]+)\s?([0-9]+)?:?([0-9]+)?/i', $stime, $reg)) {
1689  // Date est au format 'YYYY-MM-DD' ou 'YYYY-MM-DD HH:MM:SS'
1690  $annee = $reg[1];
1691  $mois = $reg[2];
1692  $jour = $reg[3];
1693  }
1694 
1695  /*
1696  * Norme ISO-8601:
1697  * - 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.
1698  * - La majorite des annees ont 52 semaines mais les annees qui commence un jeudi et les annees bissextiles commencant un mercredi en possede 53.
1699  * - Le 1er jour de la semaine est le Lundi
1700  */
1701 
1702  // Definition du Jeudi de la semaine
1703  if (date("w", mktime(12, 0, 0, $mois, $jour, $annee)) == 0) { // Dimanche
1704  $jeudiSemaine = mktime(12, 0, 0, $mois, $jour, $annee) - 3 * 24 * 60 * 60;
1705  } elseif (date("w", mktime(12, 0, 0, $mois, $jour, $annee)) < 4) { // du Lundi au Mercredi
1706  $jeudiSemaine = mktime(12, 0, 0, $mois, $jour, $annee) + (4 - date("w", mktime(12, 0, 0, $mois, $jour, $annee))) * 24 * 60 * 60;
1707  } elseif (date("w", mktime(12, 0, 0, $mois, $jour, $annee)) > 4) { // du Vendredi au Samedi
1708  $jeudiSemaine = mktime(12, 0, 0, $mois, $jour, $annee) - (date("w", mktime(12, 0, 0, $mois, $jour, $annee)) - 4) * 24 * 60 * 60;
1709  } else { // Jeudi
1710  $jeudiSemaine = mktime(12, 0, 0, $mois, $jour, $annee);
1711  }
1712 
1713  // Definition du premier Jeudi de l'annee
1714  if (date("w", mktime(12, 0, 0, 1, 1, date("Y", $jeudiSemaine))) == 0) { // Dimanche
1715  $premierJeudiAnnee = mktime(12, 0, 0, 1, 1, date("Y", $jeudiSemaine)) + 4 * 24 * 60 * 60;
1716  } elseif (date("w", mktime(12, 0, 0, 1, 1, date("Y", $jeudiSemaine))) < 4) { // du Lundi au Mercredi
1717  $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;
1718  } elseif (date("w", mktime(12, 0, 0, 1, 1, date("Y", $jeudiSemaine))) > 4) { // du Vendredi au Samedi
1719  $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;
1720  } else // Jeudi
1721  {
1722  $premierJeudiAnnee = mktime(12, 0, 0, 1, 1, date("Y", $jeudiSemaine));
1723  }
1724 
1725  // Definition du numero de semaine: nb de jours entre "premier Jeudi de l'annee" et "Jeudi de la semaine";
1726  $numeroSemaine = (
1727  (
1728  date("z", mktime(12, 0, 0, date("m", $jeudiSemaine), date("d", $jeudiSemaine), date("Y", $jeudiSemaine)))
1729  -
1730  date("z", mktime(12, 0, 0, date("m", $premierJeudiAnnee), date("d", $premierJeudiAnnee), date("Y", $premierJeudiAnnee)))
1731  ) / 7
1732  ) + 1;
1733 
1734  // Cas particulier de la semaine 53
1735  if ($numeroSemaine == 53) {
1736  // Les annees qui commence un Jeudi et les annees bissextiles commencant un Mercredi en possede 53
1737  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)) {
1738  $numeroSemaine = 53;
1739  } else {
1740  $numeroSemaine = 1;
1741  }
1742  }
1743 
1744  //echo $jour."-".$mois."-".$annee." (".date("d-m-Y",$premierJeudiAnnee)." - ".date("d-m-Y",$jeudiSemaine).") -> ".$numeroSemaine."<BR>";
1745 
1746  return sprintf("%02d", $numeroSemaine);
1747 }
1748 
1757 function weight_convert($weight, &$from_unit, $to_unit)
1758 {
1759  /* Pour convertire 320 gr en Kg appeler
1760  * $f = -3
1761  * weigh_convert(320, $f, 0) retournera 0.32
1762  *
1763  */
1764  $weight = is_numeric($weight) ? $weight : 0;
1765  while ($from_unit <> $to_unit) {
1766  if ($from_unit > $to_unit) {
1767  $weight = $weight * 10;
1768  $from_unit = $from_unit - 1;
1769  $weight = weight_convert($weight, $from_unit, $to_unit);
1770  }
1771  if ($from_unit < $to_unit) {
1772  $weight = $weight / 10;
1773  $from_unit = $from_unit + 1;
1774  $weight = weight_convert($weight, $from_unit, $to_unit);
1775  }
1776  }
1777 
1778  return $weight;
1779 }
1780 
1792 function dol_set_user_param($db, $conf, &$user, $tab)
1793 {
1794  // Verification parametres
1795  if (count($tab) < 1) {
1796  return -1;
1797  }
1798 
1799  $db->begin();
1800 
1801  // We remove old parameters for all keys in $tab
1802  $sql = "DELETE FROM ".MAIN_DB_PREFIX."user_param";
1803  $sql .= " WHERE fk_user = ".((int) $user->id);
1804  $sql .= " AND entity = ".((int) $conf->entity);
1805  $sql .= " AND param in (";
1806  $i = 0;
1807  foreach ($tab as $key => $value) {
1808  if ($i > 0) {
1809  $sql .= ',';
1810  }
1811  $sql .= "'".$db->escape($key)."'";
1812  $i++;
1813  }
1814  $sql .= ")";
1815  dol_syslog("functions2.lib::dol_set_user_param", LOG_DEBUG);
1816 
1817  $resql = $db->query($sql);
1818  if (!$resql) {
1819  dol_print_error($db);
1820  $db->rollback();
1821  return -1;
1822  }
1823 
1824  foreach ($tab as $key => $value) {
1825  // Set new parameters
1826  if ($value) {
1827  $sql = "INSERT INTO ".MAIN_DB_PREFIX."user_param(fk_user,entity,param,value)";
1828  $sql .= " VALUES (".((int) $user->id).",".((int) $conf->entity).",";
1829  $sql .= " '".$db->escape($key)."','".$db->escape($value)."')";
1830 
1831  dol_syslog("functions2.lib::dol_set_user_param", LOG_DEBUG);
1832  $result = $db->query($sql);
1833  if (!$result) {
1834  dol_print_error($db);
1835  $db->rollback();
1836  return -1;
1837  }
1838  $user->conf->$key = $value;
1839  //print "key=".$key." user->conf->key=".$user->conf->$key;
1840  } else {
1841  unset($user->conf->$key);
1842  }
1843  }
1844 
1845  $db->commit();
1846  return 1;
1847 }
1848 
1856 function dol_print_reduction($reduction, $langs)
1857 {
1858  $string = '';
1859  if ($reduction == 100) {
1860  $string = $langs->transnoentities("Offered");
1861  } else {
1862  $string = vatrate($reduction, true);
1863  }
1864 
1865  return $string;
1866 }
1867 
1875 function version_os($option = '')
1876 {
1877  if ($option == 'smr') {
1878  $osversion = php_uname('s').' '.php_uname('m').' '.php_uname('r');
1879  } else {
1880  $osversion = php_uname();
1881  }
1882  return $osversion;
1883 }
1884 
1891 function version_php()
1892 {
1893  return phpversion();
1894 }
1895 
1903 {
1904  return DOL_VERSION;
1905 }
1906 
1913 {
1914  return $_SERVER["SERVER_SOFTWARE"];
1915 }
1916 
1925 function getListOfModels($db, $type, $maxfilenamelength = 0)
1926 {
1927  global $conf, $langs;
1928  $liste = array();
1929  $found = 0;
1930  $dirtoscan = '';
1931 
1932  $sql = "SELECT nom as id, nom as doc_template_name, libelle as label, description as description";
1933  $sql .= " FROM ".MAIN_DB_PREFIX."document_model";
1934  $sql .= " WHERE type = '".$db->escape($type)."'";
1935  $sql .= " AND entity IN (0,".$conf->entity.")";
1936  $sql .= " ORDER BY description DESC";
1937 
1938  dol_syslog('/core/lib/function2.lib.php::getListOfModels', LOG_DEBUG);
1939  $resql_models = $db->query($sql);
1940  if ($resql_models) {
1941  $num = $db->num_rows($resql_models);
1942  $i = 0;
1943  while ($i < $num) {
1944  $found = 1;
1945 
1946  $obj = $db->fetch_object($resql_models);
1947 
1948  // If this generation module needs to scan a directory, then description field is filled
1949  // with the constant that contains list of directories to scan (COMPANY_ADDON_PDF_ODT_PATH, ...).
1950  if (!empty($obj->description)) { // A list of directories to scan is defined
1951  include_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
1952 
1953  $const = $obj->description;
1954  //irtoscan.=($dirtoscan?',':'').preg_replace('/[\r\n]+/',',',trim($conf->global->$const));
1955  $dirtoscan = preg_replace('/[\r\n]+/', ',', trim($conf->global->$const));
1956 
1957  $listoffiles = array();
1958 
1959  // Now we add models found in directories scanned
1960  $listofdir = explode(',', $dirtoscan);
1961  foreach ($listofdir as $key => $tmpdir) {
1962  $tmpdir = trim($tmpdir);
1963  $tmpdir = preg_replace('/DOL_DATA_ROOT/', DOL_DATA_ROOT, $tmpdir);
1964  if (!$tmpdir) {
1965  unset($listofdir[$key]);
1966  continue;
1967  }
1968  if (is_dir($tmpdir)) {
1969  // all type of template is allowed
1970  $tmpfiles = dol_dir_list($tmpdir, 'files', 0, '', '', 'name', SORT_ASC, 0);
1971  if (count($tmpfiles)) {
1972  $listoffiles = array_merge($listoffiles, $tmpfiles);
1973  }
1974  }
1975  }
1976 
1977  if (count($listoffiles)) {
1978  foreach ($listoffiles as $record) {
1979  $max = ($maxfilenamelength ? $maxfilenamelength : 28);
1980  $liste[$obj->id.':'.$record['fullname']] = dol_trunc($record['name'], $max, 'middle');
1981  }
1982  } else {
1983  $liste[0] = $obj->label.': '.$langs->trans("None");
1984  }
1985  } else {
1986  if ($type == 'member' && $obj->doc_template_name == 'standard') { // Special case, if member template, we add variant per format
1987  global $_Avery_Labels;
1988  include_once DOL_DOCUMENT_ROOT.'/core/lib/format_cards.lib.php';
1989  foreach ($_Avery_Labels as $key => $val) {
1990  $liste[$obj->id.':'.$key] = ($obj->label ? $obj->label : $obj->doc_template_name).' '.$val['name'];
1991  }
1992  } else {
1993  // Common usage
1994  $liste[$obj->id] = $obj->label ? $obj->label : $obj->doc_template_name;
1995  }
1996  }
1997  $i++;
1998  }
1999  } else {
2000  dol_print_error($db);
2001  return -1;
2002  }
2003 
2004  if ($found) {
2005  return $liste;
2006  } else {
2007  return 0;
2008  }
2009 }
2010 
2018 function is_ip($ip)
2019 {
2020  // First we test if it is a valid IPv4
2021  if (filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4)) {
2022  // Then we test if it is a private range
2023  if (!filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_NO_PRIV_RANGE)) {
2024  return 2;
2025  }
2026 
2027  // Then we test if it is a reserved range
2028  if (!filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_NO_RES_RANGE)) {
2029  return 0;
2030  }
2031 
2032  return 1;
2033  }
2034 
2035  return 0;
2036 }
2037 
2045 function dol_buildlogin($lastname, $firstname)
2046 {
2047  global $conf;
2048 
2049  //$conf->global->MAIN_BUILD_LOGIN_RULE = 'f.lastname';
2050  if (!empty($conf->global->MAIN_BUILD_LOGIN_RULE) && $conf->global->MAIN_BUILD_LOGIN_RULE == 'f.lastname') { // f.lastname
2051  $login = strtolower(dol_string_unaccent(dol_trunc($firstname, 1, 'right', 'UTF-8', 1)));
2052  $login .= ($login ? '.' : '');
2053  $login .= strtolower(dol_string_unaccent($lastname));
2054  $login = dol_string_nospecial($login, ''); // For special names
2055  } else { // firstname.lastname
2056  $login = strtolower(dol_string_unaccent($firstname));
2057  $login .= ($login ? '.' : '');
2058  $login .= strtolower(dol_string_unaccent($lastname));
2059  $login = dol_string_nospecial($login, ''); // For special names
2060  }
2061 
2062  // TODO Add a hook to allow external modules to suggest new rules
2063 
2064  return $login;
2065 }
2066 
2072 function getSoapParams()
2073 {
2074  global $conf;
2075 
2076  $params = array();
2077  $proxyuse = (empty($conf->global->MAIN_PROXY_USE) ?false:true);
2078  $proxyhost = (empty($conf->global->MAIN_PROXY_USE) ?false:$conf->global->MAIN_PROXY_HOST);
2079  $proxyport = (empty($conf->global->MAIN_PROXY_USE) ?false:$conf->global->MAIN_PROXY_PORT);
2080  $proxyuser = (empty($conf->global->MAIN_PROXY_USE) ?false:$conf->global->MAIN_PROXY_USER);
2081  $proxypass = (empty($conf->global->MAIN_PROXY_USE) ?false:$conf->global->MAIN_PROXY_PASS);
2082  $timeout = (empty($conf->global->MAIN_USE_CONNECT_TIMEOUT) ? 10 : $conf->global->MAIN_USE_CONNECT_TIMEOUT); // Connection timeout
2083  $response_timeout = (empty($conf->global->MAIN_USE_RESPONSE_TIMEOUT) ? 30 : $conf->global->MAIN_USE_RESPONSE_TIMEOUT); // Response timeout
2084  //print extension_loaded('soap');
2085  if ($proxyuse) {
2086  $params = array('connection_timeout'=>$timeout,
2087  'response_timeout'=>$response_timeout,
2088  'proxy_use' => 1,
2089  'proxy_host' => $proxyhost,
2090  'proxy_port' => $proxyport,
2091  'proxy_login' => $proxyuser,
2092  'proxy_password' => $proxypass,
2093  'trace' => 1
2094  );
2095  } else {
2096  $params = array('connection_timeout'=>$timeout,
2097  'response_timeout'=>$response_timeout,
2098  'proxy_use' => 0,
2099  'proxy_host' => false,
2100  'proxy_port' => false,
2101  'proxy_login' => false,
2102  'proxy_password' => false,
2103  'trace' => 1
2104  );
2105  }
2106  return $params;
2107 }
2108 
2109 
2119 function dolGetElementUrl($objectid, $objecttype, $withpicto = 0, $option = '')
2120 {
2121  global $db, $conf, $langs;
2122 
2123  $ret = '';
2124  $regs = array();
2125 
2126  // If we ask a resource form external module (instead of default path)
2127  if (preg_match('/^([^@]+)@([^@]+)$/i', $objecttype, $regs)) {
2128  $myobject = $regs[1];
2129  $module = $regs[2];
2130  } else {
2131  // Parse $objecttype (ex: project_task)
2132  $module = $myobject = $objecttype;
2133  if (preg_match('/^([^_]+)_([^_]+)/i', $objecttype, $regs)) {
2134  $module = $regs[1];
2135  $myobject = $regs[2];
2136  }
2137  }
2138 
2139  // Generic case for $classpath
2140  $classpath = $module.'/class';
2141 
2142  // Special cases, to work with non standard path
2143  if ($objecttype == 'facture' || $objecttype == 'invoice') {
2144  $langs->load('bills');
2145  $classpath = 'compta/facture/class';
2146  $module = 'facture';
2147  $myobject = 'facture';
2148  } elseif ($objecttype == 'commande' || $objecttype == 'order') {
2149  $langs->load('orders');
2150  $classpath = 'commande/class';
2151  $module = 'commande';
2152  $myobject = 'commande';
2153  } elseif ($objecttype == 'propal') {
2154  $langs->load('propal');
2155  $classpath = 'comm/propal/class';
2156  } elseif ($objecttype == 'supplier_proposal') {
2157  $langs->load('supplier_proposal');
2158  $classpath = 'supplier_proposal/class';
2159  } elseif ($objecttype == 'shipping') {
2160  $langs->load('sendings');
2161  $classpath = 'expedition/class';
2162  $myobject = 'expedition';
2163  $module = 'expedition_bon';
2164  } elseif ($objecttype == 'delivery') {
2165  $langs->load('deliveries');
2166  $classpath = 'delivery/class';
2167  $myobject = 'delivery';
2168  $module = 'delivery_note';
2169  } elseif ($objecttype == 'contract') {
2170  $langs->load('contracts');
2171  $classpath = 'contrat/class';
2172  $module = 'contrat';
2173  $myobject = 'contrat';
2174  } elseif ($objecttype == 'member') {
2175  $langs->load('members');
2176  $classpath = 'adherents/class';
2177  $module = 'adherent';
2178  $myobject = 'adherent';
2179  } elseif ($objecttype == 'cabinetmed_cons') {
2180  $classpath = 'cabinetmed/class';
2181  $module = 'cabinetmed';
2182  $myobject = 'cabinetmedcons';
2183  } elseif ($objecttype == 'fichinter') {
2184  $langs->load('interventions');
2185  $classpath = 'fichinter/class';
2186  $module = 'ficheinter';
2187  $myobject = 'fichinter';
2188  } elseif ($objecttype == 'project') {
2189  $langs->load('projects');
2190  $classpath = 'projet/class';
2191  $module = 'projet';
2192  } elseif ($objecttype == 'task') {
2193  $langs->load('projects');
2194  $classpath = 'projet/class';
2195  $module = 'projet';
2196  $myobject = 'task';
2197  } elseif ($objecttype == 'stock') {
2198  $classpath = 'product/stock/class';
2199  $module = 'stock';
2200  $myobject = 'stock';
2201  } elseif ($objecttype == 'inventory') {
2202  $classpath = 'product/inventory/class';
2203  $module = 'stock';
2204  $myobject = 'inventory';
2205  } elseif ($objecttype == 'mo') {
2206  $classpath = 'mrp/class';
2207  $module = 'mrp';
2208  $myobject = 'mo';
2209  } elseif ($objecttype == 'productlot') {
2210  $classpath = 'product/stock/class';
2211  $module = 'stock';
2212  $myobject = 'productlot';
2213  }
2214 
2215  // Generic case for $classfile and $classname
2216  $classfile = strtolower($myobject);
2217  $classname = ucfirst($myobject);
2218  //print "objecttype=".$objecttype." module=".$module." subelement=".$subelement." classfile=".$classfile." classname=".$classname." classpath=".$classpath;
2219 
2220  if ($objecttype == 'invoice_supplier') {
2221  $classfile = 'fournisseur.facture';
2222  $classname = 'FactureFournisseur';
2223  $classpath = 'fourn/class';
2224  $module = 'fournisseur';
2225  } elseif ($objecttype == 'order_supplier') {
2226  $classfile = 'fournisseur.commande';
2227  $classname = 'CommandeFournisseur';
2228  $classpath = 'fourn/class';
2229  $module = 'fournisseur';
2230  } elseif ($objecttype == 'supplier_proposal') {
2231  $classfile = 'supplier_proposal';
2232  $classname = 'SupplierProposal';
2233  $classpath = 'supplier_proposal/class';
2234  $module = 'supplier_proposal';
2235  } elseif ($objecttype == 'stock') {
2236  $classpath = 'product/stock/class';
2237  $classfile = 'entrepot';
2238  $classname = 'Entrepot';
2239  } elseif ($objecttype == 'facturerec') {
2240  $classpath = 'compta/facture/class';
2241  $classfile = 'facture-rec';
2242  $classname = 'FactureRec';
2243  $module = 'facture';
2244  } elseif ($objecttype == 'mailing') {
2245  $classpath = 'comm/mailing/class';
2246  $classfile = 'mailing';
2247  $classname = 'Mailing';
2248  }
2249 
2250  if (isModEnabled($module)) {
2251  $res = dol_include_once('/'.$classpath.'/'.$classfile.'.class.php');
2252  if ($res) {
2253  if (class_exists($classname)) {
2254  $object = new $classname($db);
2255  $res = $object->fetch($objectid);
2256  if ($res > 0) {
2257  $ret = $object->getNomUrl($withpicto, $option);
2258  } elseif ($res == 0) {
2259  $ret = $langs->trans('Deleted');
2260  }
2261  unset($object);
2262  } else {
2263  dol_syslog("Class with classname ".$classname." is unknown even after the include", LOG_ERR);
2264  }
2265  }
2266  }
2267  return $ret;
2268 }
2269 
2270 
2279 function cleanCorruptedTree($db, $tabletocleantree, $fieldfkparent)
2280 {
2281  $totalnb = 0;
2282  $listofid = array();
2283  $listofparentid = array();
2284 
2285  // Get list of all id in array listofid and all parents in array listofparentid
2286  $sql = "SELECT rowid, ".$fieldfkparent." as parent_id FROM ".MAIN_DB_PREFIX.$tabletocleantree;
2287  $resql = $db->query($sql);
2288  if ($resql) {
2289  $num = $db->num_rows($resql);
2290  $i = 0;
2291  while ($i < $num) {
2292  $obj = $db->fetch_object($resql);
2293  $listofid[] = $obj->rowid;
2294  if ($obj->parent_id > 0) {
2295  $listofparentid[$obj->rowid] = $obj->parent_id;
2296  }
2297  $i++;
2298  }
2299  } else {
2300  dol_print_error($db);
2301  }
2302 
2303  if (count($listofid)) {
2304  print 'Code requested to clean tree (may be to solve data corruption), so we check/clean orphelins and loops.'."<br>\n";
2305 
2306  // Check loops on each other
2307  $sql = "UPDATE ".MAIN_DB_PREFIX.$tabletocleantree." SET ".$fieldfkparent." = 0 WHERE ".$fieldfkparent." = rowid"; // So we update only records linked to themself
2308  $resql = $db->query($sql);
2309  if ($resql) {
2310  $nb = $db->affected_rows($sql);
2311  if ($nb > 0) {
2312  print '<br>Some record that were parent of themself were cleaned.';
2313  }
2314 
2315  $totalnb += $nb;
2316  }
2317  //else dol_print_error($db);
2318 
2319  // Check other loops
2320  $listofidtoclean = array();
2321  foreach ($listofparentid as $id => $pid) {
2322  // Check depth
2323  //print 'Analyse record id='.$id.' with parent '.$pid.'<br>';
2324 
2325  $cursor = $id;
2326  $arrayidparsed = array(); // We start from child $id
2327  while ($cursor > 0) {
2328  $arrayidparsed[$cursor] = 1;
2329  if ($arrayidparsed[$listofparentid[$cursor]]) { // We detect a loop. A record with a parent that was already into child
2330  print 'Found a loop between id '.$id.' - '.$cursor.'<br>';
2331  unset($arrayidparsed);
2332  $listofidtoclean[$cursor] = $id;
2333  break;
2334  }
2335  $cursor = $listofparentid[$cursor];
2336  }
2337 
2338  if (count($listofidtoclean)) {
2339  break;
2340  }
2341  }
2342 
2343  $sql = "UPDATE ".MAIN_DB_PREFIX.$tabletocleantree;
2344  $sql .= " SET ".$fieldfkparent." = 0";
2345  $sql .= " WHERE rowid IN (".$db->sanitize(join(',', $listofidtoclean)).")"; // So we update only records detected wrong
2346  $resql = $db->query($sql);
2347  if ($resql) {
2348  $nb = $db->affected_rows($sql);
2349  if ($nb > 0) {
2350  // Removed orphelins records
2351  print '<br>Some records were detected to have parent that is a child, we set them as root record for id: ';
2352  print join(',', $listofidtoclean);
2353  }
2354 
2355  $totalnb += $nb;
2356  }
2357  //else dol_print_error($db);
2358 
2359  // Check and clean orphelins
2360  $sql = "UPDATE ".MAIN_DB_PREFIX.$tabletocleantree;
2361  $sql .= " SET ".$fieldfkparent." = 0";
2362  $sql .= " WHERE ".$fieldfkparent." NOT IN (".$db->sanitize(join(',', $listofid), 1).")"; // So we update only records linked to a non existing parent
2363  $resql = $db->query($sql);
2364  if ($resql) {
2365  $nb = $db->affected_rows($sql);
2366  if ($nb > 0) {
2367  // Removed orphelins records
2368  print '<br>Some orphelins were found and modified to be parent so records are visible again for id: ';
2369  print join(',', $listofid);
2370  }
2371 
2372  $totalnb += $nb;
2373  }
2374  //else dol_print_error($db);
2375 
2376  print '<br>We fixed '.$totalnb.' record(s). Some records may still be corrupted. New check may be required.';
2377  return $totalnb;
2378  }
2379  return -1;
2380 }
2381 
2382 
2392 function colorArrayToHex($arraycolor, $colorifnotfound = '888888')
2393 {
2394  if (!is_array($arraycolor)) {
2395  return $colorifnotfound;
2396  }
2397  if (empty($arraycolor)) {
2398  return $colorifnotfound;
2399  }
2400  return sprintf("%02s", dechex($arraycolor[0])).sprintf("%02s", dechex($arraycolor[1])).sprintf("%02s", dechex($arraycolor[2]));
2401 }
2402 
2413 function colorStringToArray($stringcolor, $colorifnotfound = array(88, 88, 88))
2414 {
2415  if (is_array($stringcolor)) {
2416  return $stringcolor; // If already into correct output format, we return as is
2417  }
2418  $reg = array();
2419  $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);
2420  if (!$tmp) {
2421  $tmp = explode(',', $stringcolor);
2422  if (count($tmp) < 3) {
2423  return $colorifnotfound;
2424  }
2425  return $tmp;
2426  }
2427  return array(hexdec($reg[1]), hexdec($reg[2]), hexdec($reg[3]));
2428 }
2429 
2435 function colorValidateHex($color, $allow_white = true)
2436 {
2437  if (!$allow_white && ($color === '#fff' || $color === '#ffffff')) {
2438  return false;
2439  }
2440 
2441  if (preg_match('/^#[a-f0-9]{6}$/i', $color)) { //hex color is valid
2442  return true;
2443  }
2444  return false;
2445 }
2446 
2456 function colorAgressiveness($hex, $ratio = -50, $brightness = 0)
2457 {
2458  if (empty($ratio)) {
2459  $ratio = 0; // To avoid null
2460  }
2461 
2462  // Steps should be between -255 and 255. Negative = darker, positive = lighter
2463  $ratio = max(-100, min(100, $ratio));
2464 
2465  // Normalize into a six character long hex string
2466  $hex = str_replace('#', '', $hex);
2467  if (strlen($hex) == 3) {
2468  $hex = str_repeat(substr($hex, 0, 1), 2).str_repeat(substr($hex, 1, 1), 2).str_repeat(substr($hex, 2, 1), 2);
2469  }
2470 
2471  // Split into three parts: R, G and B
2472  $color_parts = str_split($hex, 2);
2473  $return = '#';
2474 
2475  foreach ($color_parts as $color) {
2476  $color = hexdec($color); // Convert to decimal
2477  if ($ratio > 0) { // We increase aggressivity
2478  if ($color > 127) {
2479  $color += ((255 - $color) * ($ratio / 100));
2480  }
2481  if ($color < 128) {
2482  $color -= ($color * ($ratio / 100));
2483  }
2484  } else // We decrease agressiveness
2485  {
2486  if ($color > 128) {
2487  $color -= (($color - 128) * (abs($ratio) / 100));
2488  }
2489  if ($color < 127) {
2490  $color += ((128 - $color) * (abs($ratio) / 100));
2491  }
2492  }
2493  if ($brightness > 0) {
2494  $color = ($color * (100 + abs($brightness)) / 100);
2495  } else {
2496  $color = ($color * (100 - abs($brightness)) / 100);
2497  }
2498 
2499  $color = max(0, min(255, $color)); // Adjust color to stay into valid range
2500  $return .= str_pad(dechex($color), 2, '0', STR_PAD_LEFT); // Make two char hex code
2501  }
2502 
2503  //var_dump($hex.' '.$ratio.' -> '.$return);
2504  return $return;
2505 }
2506 
2513 function colorAdjustBrightness($hex, $steps)
2514 {
2515  // Steps should be between -255 and 255. Negative = darker, positive = lighter
2516  $steps = max(-255, min(255, $steps));
2517 
2518  // Normalize into a six character long hex string
2519  $hex = str_replace('#', '', $hex);
2520  if (strlen($hex) == 3) {
2521  $hex = str_repeat(substr($hex, 0, 1), 2).str_repeat(substr($hex, 1, 1), 2).str_repeat(substr($hex, 2, 1), 2);
2522  }
2523 
2524  // Split into three parts: R, G and B
2525  $color_parts = str_split($hex, 2);
2526  $return = '#';
2527 
2528  foreach ($color_parts as $color) {
2529  $color = hexdec($color); // Convert to decimal
2530  $color = max(0, min(255, $color + $steps)); // Adjust color
2531  $return .= str_pad(dechex($color), 2, '0', STR_PAD_LEFT); // Make two char hex code
2532  }
2533 
2534  return $return;
2535 }
2536 
2542 function colorDarker($hex, $percent)
2543 {
2544  $steps = intval(255 * $percent / 100) * -1;
2545  return colorAdjustBrightness($hex, $steps);
2546 }
2547 
2553 function colorLighten($hex, $percent)
2554 {
2555  $steps = intval(255 * $percent / 100);
2556  return colorAdjustBrightness($hex, $steps);
2557 }
2558 
2559 
2566 function colorHexToRgb($hex, $alpha = false, $returnArray = false)
2567 {
2568  $string = '';
2569  $hex = str_replace('#', '', $hex);
2570  $length = strlen($hex);
2571  $rgb = array();
2572  $rgb['r'] = hexdec($length == 6 ? substr($hex, 0, 2) : ($length == 3 ? str_repeat(substr($hex, 0, 1), 2) : 0));
2573  $rgb['g'] = hexdec($length == 6 ? substr($hex, 2, 2) : ($length == 3 ? str_repeat(substr($hex, 1, 1), 2) : 0));
2574  $rgb['b'] = hexdec($length == 6 ? substr($hex, 4, 2) : ($length == 3 ? str_repeat(substr($hex, 2, 1), 2) : 0));
2575  if ($alpha !== false) {
2576  $rgb['a'] = floatval($alpha);
2577  $string = 'rgba('.implode(',', $rgb).')';
2578  } else {
2579  $string = 'rgb('.implode(',', $rgb).')';
2580  }
2581 
2582  if ($returnArray) {
2583  return $rgb;
2584  } else {
2585  return $string;
2586  }
2587 }
2588 
2589 
2597 function cartesianArray(array $input)
2598 {
2599  // filter out empty values
2600  $input = array_filter($input);
2601 
2602  $result = array(array());
2603 
2604  foreach ($input as $key => $values) {
2605  $append = array();
2606 
2607  foreach ($result as $product) {
2608  foreach ($values as $item) {
2609  $product[$key] = $item;
2610  $append[] = $product;
2611  }
2612  }
2613 
2614  $result = $append;
2615  }
2616 
2617  return $result;
2618 }
2619 
2620 
2627 function getModuleDirForApiClass($moduleobject)
2628 {
2629  $moduledirforclass = $moduleobject;
2630  if ($moduledirforclass != 'api') {
2631  $moduledirforclass = preg_replace('/api$/i', '', $moduledirforclass);
2632  }
2633 
2634  if ($moduleobject == 'contracts') {
2635  $moduledirforclass = 'contrat';
2636  } elseif (in_array($moduleobject, array('admin', 'login', 'setup', 'access', 'status', 'tools', 'documents'))) {
2637  $moduledirforclass = 'api';
2638  } elseif ($moduleobject == 'contact' || $moduleobject == 'contacts' || $moduleobject == 'customer' || $moduleobject == 'thirdparty' || $moduleobject == 'thirdparties') {
2639  $moduledirforclass = 'societe';
2640  } elseif ($moduleobject == 'propale' || $moduleobject == 'proposals') {
2641  $moduledirforclass = 'comm/propal';
2642  } elseif ($moduleobject == 'agenda' || $moduleobject == 'agendaevents') {
2643  $moduledirforclass = 'comm/action';
2644  } elseif ($moduleobject == 'adherent' || $moduleobject == 'members' || $moduleobject == 'memberstypes' || $moduleobject == 'subscriptions') {
2645  $moduledirforclass = 'adherents';
2646  } elseif ($moduleobject == 'don' || $moduleobject == 'donations') {
2647  $moduledirforclass = 'don';
2648  } elseif ($moduleobject == 'banque' || $moduleobject == 'bankaccounts') {
2649  $moduledirforclass = 'compta/bank';
2650  } elseif ($moduleobject == 'category' || $moduleobject == 'categorie') {
2651  $moduledirforclass = 'categories';
2652  } elseif ($moduleobject == 'order' || $moduleobject == 'orders') {
2653  $moduledirforclass = 'commande';
2654  } elseif ($moduleobject == 'shipments') {
2655  $moduledirforclass = 'expedition';
2656  } elseif ($moduleobject == 'multicurrencies') {
2657  $moduledirforclass = 'multicurrency';
2658  } elseif ($moduleobject == 'facture' || $moduleobject == 'invoice' || $moduleobject == 'invoices') {
2659  $moduledirforclass = 'compta/facture';
2660  } elseif ($moduleobject == 'project' || $moduleobject == 'projects' || $moduleobject == 'task' || $moduleobject == 'tasks') {
2661  $moduledirforclass = 'projet';
2662  } elseif ($moduleobject == 'stock' || $moduleobject == 'stockmovements' || $moduleobject == 'warehouses') {
2663  $moduledirforclass = 'product/stock';
2664  } elseif ($moduleobject == 'supplierproposals' || $moduleobject == 'supplierproposal' || $moduleobject == 'supplier_proposal') {
2665  $moduledirforclass = 'supplier_proposal';
2666  } elseif ($moduleobject == 'fournisseur' || $moduleobject == 'supplierinvoices' || $moduleobject == 'supplierorders') {
2667  $moduledirforclass = 'fourn';
2668  } elseif ($moduleobject == 'ficheinter' || $moduleobject == 'interventions') {
2669  $moduledirforclass = 'fichinter';
2670  } elseif ($moduleobject == 'mos') {
2671  $moduledirforclass = 'mrp';
2672  } elseif ($moduleobject == 'accounting') {
2673  $moduledirforclass = 'accountancy';
2674  } elseif (in_array($moduleobject, array('products', 'expensereports', 'users', 'tickets', 'boms', 'receptions'))) {
2675  $moduledirforclass = preg_replace('/s$/', '', $moduleobject);
2676  }
2677 
2678  return $moduledirforclass;
2679 }
2680 
2688 function randomColorPart($min = 0, $max = 255)
2689 {
2690  return str_pad(dechex(mt_rand($min, $max)), 2, '0', STR_PAD_LEFT);
2691 }
2692 
2700 function randomColor($min = 0, $max = 255)
2701 {
2702  return randomColorPart($min, $max).randomColorPart($min, $max).randomColorPart($min, $max);
2703 }
2704 
2705 
2706 if (!function_exists('dolEscapeXML')) {
2713  function dolEscapeXML($string)
2714  {
2715  return strtr($string, array('\''=>'&apos;', '"'=>'&quot;', '&'=>'&amp;', '<'=>'&lt;', '>'=>'&gt;'));
2716  }
2717 }
2718 
2719 
2727 {
2728  global $dolibarr_main_url_root;
2729  // Define $urlwithroot
2730  $urlwithouturlroot = preg_replace('/'.preg_quote(DOL_URL_ROOT, '/').'$/i', '', trim($dolibarr_main_url_root));
2731  $urlwithroot = $urlwithouturlroot.DOL_URL_ROOT; // This is to use external domain name found into config file
2732  //$urlwithroot=DOL_MAIN_URL_ROOT; // This is to use same domain name than current
2733  $notetoshow = preg_replace('/src="[a-zA-Z0-9_\/\-\.]*(viewimage\.php\?modulepart=medias[^"]*)"/', 'src="'.$urlwithroot.'/\1"', $notetoshow);
2734  return $notetoshow;
2735 }
2736 
2745 function price2fec($amount)
2746 {
2747  global $conf;
2748 
2749  // Clean parameters
2750  if (empty($amount)) {
2751  $amount = 0; // To have a numeric value if amount not defined or = ''
2752  }
2753  $amount = (is_numeric($amount) ? $amount : 0); // Check if amount is numeric, for example, an error occured when amount value = o (letter) instead 0 (number)
2754 
2755  // Output decimal number by default
2756  $nbdecimal = (empty($conf->global->ACCOUNTING_FEC_DECIMAL_LENGTH) ? 2 : $conf->global->ACCOUNTING_FEC_DECIMAL_LENGTH);
2757 
2758  // Output separators by default
2759  $dec = (empty($conf->global->ACCOUNTING_FEC_DECIMAL_SEPARATOR) ? ',' : $conf->global->ACCOUNTING_FEC_DECIMAL_SEPARATOR);
2760  $thousand = (empty($conf->global->ACCOUNTING_FEC_THOUSAND_SEPARATOR) ? '' : $conf->global->ACCOUNTING_FEC_THOUSAND_SEPARATOR);
2761 
2762  // Format number
2763  $output = number_format($amount, $nbdecimal, $dec, $thousand);
2764 
2765  return $output;
2766 }
2767 
2774 function phpSyntaxError($code)
2775 {
2776  if (!defined("CR")) {
2777  define("CR", "\r");
2778  }
2779  if (!defined("LF")) {
2780  define("LF", "\n");
2781  }
2782  if (!defined("CRLF")) {
2783  define("CRLF", "\r\n");
2784  }
2785 
2786  $braces = 0;
2787  $inString = 0;
2788  foreach (token_get_all('<?php '.$code) as $token) {
2789  if (is_array($token)) {
2790  switch ($token[0]) {
2791  case T_CURLY_OPEN:
2792  case T_DOLLAR_OPEN_CURLY_BRACES:
2793  case T_START_HEREDOC:
2794  ++$inString;
2795  break;
2796  case T_END_HEREDOC:
2797  --$inString;
2798  break;
2799  }
2800  } elseif ($inString & 1) {
2801  switch ($token) {
2802  case '`':
2803  case '\'':
2804  case '"':
2805  --$inString;
2806  break;
2807  }
2808  } else {
2809  switch ($token) {
2810  case '`':
2811  case '\'':
2812  case '"':
2813  ++$inString;
2814  break;
2815  case '{':
2816  ++$braces;
2817  break;
2818  case '}':
2819  if ($inString) {
2820  --$inString;
2821  } else {
2822  --$braces;
2823  if ($braces < 0) {
2824  break 2;
2825  }
2826  }
2827  break;
2828  }
2829  }
2830  }
2831  $inString = @ini_set('log_errors', false);
2832  $token = @ini_set('display_errors', true);
2833  ob_start();
2834  $code = substr($code, strlen('<?php '));
2835  $braces || $code = "if(0){{$code}\n}";
2836  if (eval($code) === false) {
2837  if ($braces) {
2838  $braces = PHP_INT_MAX;
2839  } else {
2840  false !== strpos($code, CR) && $code = strtr(str_replace(CRLF, LF, $code), CR, LF);
2841  $braces = substr_count($code, LF);
2842  }
2843  $code = ob_get_clean();
2844  $code = strip_tags($code);
2845  if (preg_match("'syntax error, (.+) in .+ on line (\d+)$'s", $code, $code)) {
2846  $code[2] = (int) $code[2];
2847  $code = $code[2] <= $braces
2848  ? array($code[1], $code[2])
2849  : array('unexpected $end'.substr($code[1], 14), $braces);
2850  } else {
2851  $code = array('syntax error', 0);
2852  }
2853  } else {
2854  ob_end_clean();
2855  $code = false;
2856  }
2857  @ini_set('display_errors', $token);
2858  @ini_set('log_errors', $inString);
2859  return $code;
2860 }
2861 
2862 
2869 {
2870  global $user;
2871 
2872  // If $acceptlocallinktomedia is true, we can add link media files int email templates (we already can do this into HTML editor of an email).
2873  // Note that local link to a file into medias are replaced with a real link by email in CMailFile.class.php with value $urlwithroot defined like this:
2874  // $urlwithouturlroot = preg_replace('/'.preg_quote(DOL_URL_ROOT, '/').'$/i', '', trim($dolibarr_main_url_root));
2875  // $urlwithroot = $urlwithouturlroot.DOL_URL_ROOT; // This is to use external domain name found into config file
2876  $acceptlocallinktomedia = getDolGlobalInt('MAIN_DISALLOW_MEDIAS_IN_EMAIL_TEMPLATES') ? 0 : 1;
2877  if ($acceptlocallinktomedia) {
2878  global $dolibarr_main_url_root;
2879  $urlwithouturlroot = preg_replace('/'.preg_quote(DOL_URL_ROOT, '/').'$/i', '', trim($dolibarr_main_url_root));
2880 
2881  // Parse $newUrl
2882  $newUrlArray = parse_url($urlwithouturlroot);
2883  $hosttocheck = $newUrlArray['host'];
2884  $hosttocheck = str_replace(array('[', ']'), '', $hosttocheck); // Remove brackets of IPv6
2885 
2886  if (function_exists('gethostbyname')) {
2887  $iptocheck = gethostbyname($hosttocheck);
2888  } else {
2889  $iptocheck = $hosttocheck;
2890  }
2891 
2892  //var_dump($iptocheck.' '.$acceptlocallinktomedia);
2893  if (!filter_var($iptocheck, FILTER_VALIDATE_IP, FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE)) {
2894  // If ip of public url is a private network IP, we do not allow this.
2895  $acceptlocallinktomedia = 0;
2896  // TODO Show a warning
2897  }
2898 
2899  if (preg_match('/http:/i', $urlwithouturlroot)) {
2900  // If public url is not a https, we do not allow to add medias link. It will generate security alerts when email will be sent.
2901  $acceptlocallinktomedia = 0;
2902  // TODO Show a warning
2903  }
2904 
2905  if (!empty($user->socid)) {
2906  $acceptlocallinktomedia = 0;
2907  }
2908  }
2909 
2910  //return 1;
2911  return $acceptlocallinktomedia;
2912 }
2913 
2914 
2921 function removeGlobalParenthesis($string)
2922 {
2923  $string = trim($string);
2924 
2925  // If string does not start and end with parenthesis, we return $string as is.
2926  if (! preg_match('/^\‍(.*\‍)$/', $string)) {
2927  return $string;
2928  }
2929 
2930  $nbofchars = dol_strlen($string);
2931  $i = 0; $g = 0;
2932  $countparenthesis = 0;
2933  while ($i < $nbofchars) {
2934  $char = dol_substr($string, $i, 1);
2935  if ($char == '(') {
2936  $countparenthesis++;
2937  } elseif ($char == ')') {
2938  $countparenthesis--;
2939  if ($countparenthesis <= 0) { // We reach the end of an independent group of parenthesis
2940  $g++;
2941  }
2942  }
2943  $i++;
2944  }
2945 
2946  if ($g <= 1) {
2947  return preg_replace('/^\‍(/', '', preg_replace('/\‍)$/', '', $string));
2948  }
2949 
2950  return $string;
2951 }
Class to manage translations.
Class to manage Dolibarr users.
Definition: user.class.php:48
if(isModEnabled('facture') && $user->hasRight('facture', 'lire')) if((isModEnabled('fournisseur') &&empty($conf->global->MAIN_USE_NEW_SUPPLIERMOD) && $user->hasRight("fournisseur", "facture", "lire"))||(isModEnabled('supplier_invoice') && $user->hasRight("supplier_invoice", "lire"))) if(isModEnabled('don') && $user->hasRight('don', 'lire')) if(isModEnabled('tax') &&!empty($user->rights->tax->charges->lire)) if(isModEnabled('facture') &&isModEnabled('commande') && $user->hasRight("commande", "lire") &&empty($conf->global->WORKFLOW_DISABLE_CREATE_INVOICE_FROM_ORDER)) $sql
Social contributions to pay.
Definition: index.php:746
getServerTimeZoneInt($refgmtdate='now')
Return server timezone int.
Definition: date.lib.php:83
dol_dir_list($path, $types="all", $recursive=0, $filter="", $excludefilter=null, $sortcriteria="name", $sortorder=SORT_ASC, $mode=0, $nohook=0, $relativename="", $donotfollowsymlinks=0, $nbsecondsold=0)
Scan a directory and return a list of files/directories.
Definition: files.lib.php:62
array2table($data, $tableMarkup=1, $tableoptions='', $troptions='', $tdoptions='')
Return an html table from an array.
dol_buildlogin($lastname, $firstname)
Build a login from lastname, firstname.
colorHexToRgb($hex, $alpha=false, $returnArray=false)
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.
version_webserver()
Return web server version.
getModuleDirForApiClass($moduleobject)
Get name of directory where the api_...class.php file is stored.
phpSyntaxError($code)
Check the syntax of some PHP code.
dolGetModulesDirs($subdir='')
Return list of modules directories.
array2tr($data, $troptions='', $tdoptions='')
Return lines of an html table from an array Used by array2table function only.
version_dolibarr()
Return Dolibarr version.
binhex($bin, $pad=false, $upper=false)
Convert a binary data to string that represent hexadecimal value.
colorAgressiveness($hex, $ratio=-50, $brightness=0)
Change color to make it less aggressive (ratio is negative) or more aggressive (ratio is positive)
removeGlobalParenthesis($string)
Remove first and last parenthesis but only if first is the opening and last the closing of the same g...
cartesianArray(array $input)
Applies the Cartesian product algorithm to an array Source: http://stackoverflow.com/a/15973172.
getSoapParams()
Return array to use for SoapClient constructor.
colorAdjustBrightness($hex, $steps)
cleanCorruptedTree($db, $tabletocleantree, $fieldfkparent)
Clean corrupted tree (orphelins linked to a not existing parent), record linked to themself and child...
colorArrayToHex($arraycolor, $colorifnotfound='888888')
Convert an array with RGB value into hex RGB value.
dol_print_reduction($reduction, $langs)
Returns formated reduction.
dol_print_file($langs, $filename, $searchalt=0)
Output content of a file $filename in version of current language (otherwise may use an alternate lan...
colorStringToArray($stringcolor, $colorifnotfound=array(88, 88, 88))
Convert a string RGB value ('FFFFFF', '255,255,255') into an array RGB array(255,255,...
acceptLocalLinktoMedia()
Check the syntax of some PHP code.
randomColor($min=0, $max=255)
Return hexadecimal color randomly.
is_ip($ip)
This function evaluates a string that should be a valid IPv4 Note: For ip 169.254....
check_value($mask, $value)
Check value.
weight_convert($weight, &$from_unit, $to_unit)
Convertit une masse d'une unite vers une autre unite.
clean_url($url, $http=1)
Clean an url string.
version_php()
Return PHP version.
hexbin($hexa)
Convert an hexadecimal string into a binary string.
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.
randomColorPart($min=0, $max=255)
Return 2 hexa code randomly.
if(!function_exists('dolEscapeXML')) convertBackOfficeMediasLinksToPublicLinks($notetoshow)
Convert links to local wrapper to medias files into a string into a public external URL readable on i...
colorDarker($hex, $percent)
dolGetElementUrl($objectid, $objecttype, $withpicto=0, $option='')
Return link url to an object.
getListOfModels($db, $type, $maxfilenamelength=0)
Return list of activated modules usable for document generation.
dol_print_object_info($object, $usetable=0)
Show informations on an object TODO Move this into html.formother.
get_next_value($db, $mask, $table, $field, $where='', $objsoc='', $date='', $mode='next', $bentityon=true, $objuser=null, $forceentity=null)
Return last or next value for a mask (according to area we should not reset)
isValidUrl($url, $http=0, $pass=0, $port=0, $path=0, $query=0, $anchor=0)
Url string validation <http[s]> :// [user[:pass]@] hostname [port] [/path] [?getquery] [anchor].
isValidMailDomain($mail)
Return true if email has a domain name that can be resolved to MX type.
dolAddEmailTrackId($email, $trackingid)
Return an email formatted to include a tracking id For example myemail@example.com becom myemail+trac...
colorLighten($hex, $percent)
isValidVATID($company)
Check if VAT numero is valid (check done on syntax only, no database or remote access)
version_os($option='')
Return OS version.
dol_getDefaultFormat(Translate $outputlangs=null)
Try to guess default paper format according to language into $langs.
price2fec($amount)
Function to format a value into a defined format for French administration (no thousand separator & d...
jsUnEscape($source)
Same function than javascript unescape() function but in PHP.
colorValidateHex($color, $allow_white=true)
dol_html_entity_decode($a, $b, $c='UTF-8', $keepsomeentities=0)
Replace html_entity_decode functions to manage errors.
dol_mktime($hour, $minute, $second, $month, $day, $year, $gm='auto', $check=1)
Return a timestamp date built from detailed informations (by default a local PHP server timestamp) Re...
vatrate($rate, $addpercent=false, $info_bits=0, $usestarfornpr=0, $html=0)
Return a string with VAT rate label formated for view output Used into pdf and HTML pages.
dol_print_error($db='', $error='', $errors=null)
Displays error message system with all the information to facilitate the diagnosis and the escalation...
dol_string_nospecial($str, $newstr='_', $badcharstoreplace='', $badcharstoremove='', $keepspaces=0)
Clean a string from all punctuation characters to use it as a ref or login.
dol_strlen($string, $stringencoding='UTF-8')
Make a strlen call.
dol_print_date($time, $format='', $tzoutput='auto', $outputlangs='', $encodetooutput=false)
Output date in a string format according to outputlangs (or langs if not defined).
isValidMXRecord($domain)
Return if the domain name has a valid MX record.
if(!function_exists('dol_getprefix')) dol_include_once($relpath, $classname='')
Make an include_once using default root and alternate root if it fails.
dol_now($mode='auto')
Return date for now.
getDolGlobalInt($key, $default=0)
Return dolibarr global constant int value.
dol_string_unaccent($str)
Clean a string from all accent characters to be used as ref, login or by dol_sanitizeFileName.
dol_substr($string, $start, $length=null, $stringencoding='', $trunconbytes=0)
Make a substring.
dol_trunc($string, $size=40, $trunc='right', $stringencoding='UTF-8', $nodot=0, $display=0)
Truncate a string to a particular length adding '…' if string larger than length.
isValidEmail($address, $acceptsupervisorkey=0, $acceptuserkey=0)
Return true if email syntax is ok.
isModEnabled($module)
Is Dolibarr module enabled.
utf8_check($str)
Check if a string is in UTF8.
dol_syslog($message, $level=LOG_INFO, $ident=0, $suffixinfilename='', $restricttologhandler='', $logcontext=null)
Write log message into outputs.