dolibarr  7.0.0-beta
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@capnetworks.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 <http://www.gnu.org/licenses/>.
22  * or see http://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  }
59  else {
60  // we have an escaped ascii character
61  $hexVal = substr($source, $pos, 2);
62  $decodedStr .= chr(hexdec($hexVal));
63  $pos += 2;
64  }
65  } else {
66  $decodedStr .= $charAt;
67  $pos++;
68  }
69  }
70  return dol_html_entity_decode($decodedStr, ENT_COMPAT);
71 }
72 
73 
81 function dolGetModulesDirs($subdir='')
82 {
83  global $conf;
84 
85  $modulesdir=array();
86 
87  foreach ($conf->file->dol_document_root as $type => $dirroot)
88  {
89  // Default core/modules dir
90  if ($type === 'main') {
91  $modulesdir[$dirroot . '/core/modules' . $subdir . '/'] = $dirroot . '/core/modules' . $subdir . '/';
92  }
93 
94  // Scan dir from external modules
95  $handle=@opendir($dirroot);
96  if (is_resource($handle))
97  {
98  while (($file = readdir($handle))!==false)
99  {
100  if (preg_match('/disabled/',$file)) continue; // We discard module if it contains disabled into name.
101 
102  if (is_dir($dirroot.'/'.$file) && substr($file, 0, 1) <> '.' && substr($file, 0, 3) <> 'CVS' && $file != 'includes')
103  {
104  if (is_dir($dirroot . '/' . $file . '/core/modules'.$subdir.'/'))
105  {
106  $modulesdir[$dirroot . '/' . $file . '/core/modules'.$subdir.'/'] = $dirroot . '/' . $file . '/core/modules'.$subdir.'/';
107  }
108  }
109  }
110  closedir($handle);
111  }
112  }
113  return $modulesdir;
114 }
115 
116 
123 function dol_getDefaultFormat(Translate $outputlangs = null)
124 {
125  global $langs;
126 
127  $selected='EUA4';
128  if (!$outputlangs) {
129  $outputlangs=$langs;
130  }
131 
132  if ($outputlangs->defaultlang == 'ca_CA') $selected='CAP4'; // Canada
133  if ($outputlangs->defaultlang == 'en_US') $selected='USLetter'; // US
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  {
152  $formfile=($searchdir."/langs/".$langs->defaultlang."/".$filename);
153  dol_syslog('functions2::dol_print_file search file '.$formfile, LOG_DEBUG);
154  if (is_readable($formfile))
155  {
156  $content=file_get_contents($formfile);
157  $isutf8=utf8_check($content);
158  if (! $isutf8 && $conf->file->character_set_client == 'UTF-8') print utf8_encode($content);
159  elseif ($isutf8 && $conf->file->character_set_client == 'ISO-8859-1') print utf8_decode($content);
160  else print $content;
161  return true;
162  }
163  else dol_syslog('functions2::dol_print_file not found', LOG_DEBUG);
164 
165  if ($searchalt) {
166  // Test si fichier dans repertoire de la langue alternative
167  if ($langs->defaultlang != "en_US") $formfilealt = $searchdir."/langs/en_US/".$filename;
168  else $formfilealt = $searchdir."/langs/fr_FR/".$filename;
169  dol_syslog('functions2::dol_print_file search alt file '.$formfilealt, LOG_DEBUG);
170  //print 'getcwd='.getcwd().' htmlfilealt='.$formfilealt.' X '.file_exists(getcwd().'/'.$formfilealt);
171  if (is_readable($formfilealt))
172  {
173  $content=file_get_contents($formfilealt);
174  $isutf8=utf8_check($content);
175  if (! $isutf8 && $conf->file->character_set_client == 'UTF-8') print utf8_encode($content);
176  elseif ($isutf8 && $conf->file->character_set_client == 'ISO-8859-1') print utf8_decode($content);
177  else print $content;
178  return true;
179  }
180  else dol_syslog('functions2::dol_print_file not found', LOG_DEBUG);
181  }
182  }
183 
184  return false;
185 }
186 
195 function dol_print_object_info($object, $usetable=0)
196 {
197  global $langs,$db;
198  $langs->load("other");
199  $langs->load("admin");
200 
201  include_once DOL_DOCUMENT_ROOT.'/core/lib/date.lib.php';
202 
203  $deltadateforserver=getServerTimeZoneInt('now');
204  $deltadateforclient=((int) $_SESSION['dol_tz'] + (int) $_SESSION['dol_dst']);
205  //$deltadateforcompany=((int) $_SESSION['dol_tz'] + (int) $_SESSION['dol_dst']);
206  $deltadateforuser=round($deltadateforclient-$deltadateforserver);
207  //print "x".$deltadateforserver." - ".$deltadateforclient." - ".$deltadateforuser;
208 
209  if ($usetable) print '<table class="border centpercent">';
210 
211  // Import key
212  if (! empty($object->import_key))
213  {
214  if ($usetable) print '<tr><td class="titlefield">';
215  print $langs->trans("ImportedWithSet");
216  if ($usetable) print '</td><td>';
217  else print ': ';
218  print $object->import_key;
219  if ($usetable) print '</td></tr>';
220  else print '<br>';
221  }
222 
223  // User creation (old method using already loaded object and not id is kept for backward compatibility)
224  if (! empty($object->user_creation) || ! empty($object->user_creation_id))
225  {
226  if ($usetable) print '<tr><td class="titlefield">';
227  print $langs->trans("CreatedBy");
228  if ($usetable) print '</td><td>';
229  else print ': ';
230  if (is_object($object->user_creation))
231  {
232  if ($object->user_creation->id) print $object->user_creation->getNomUrl(1, '', 0, 0, 0);
233  else print $langs->trans("Unknown");
234  }
235  else
236  {
237  $userstatic=new User($db);
238  $userstatic->fetch($object->user_creation_id ? $object->user_creation_id : $object->user_creation);
239  if ($userstatic->id) print $userstatic->getNomUrl(1, '', 0, 0, 0);
240  else print $langs->trans("Unknown");
241  }
242  if ($usetable) print '</td></tr>';
243  else print '<br>';
244  }
245 
246  // Date creation
247  if (! empty($object->date_creation))
248  {
249  if ($usetable) print '<tr><td class="titlefield">';
250  print $langs->trans("DateCreation");
251  if ($usetable) print '</td><td>';
252  else print ': ';
253  print dol_print_date($object->date_creation, 'dayhour');
254  if ($deltadateforuser) print ' '.$langs->trans("CurrentHour").' &nbsp; / &nbsp; '.dol_print_date($object->date_creation+($deltadateforuser*3600),"dayhour").' &nbsp;'.$langs->trans("ClientHour");
255  if ($usetable) print '</td></tr>';
256  else print '<br>';
257  }
258 
259  // User change (old method using already loaded object and not id is kept for backward compatibility)
260  if (! empty($object->user_modification) || ! empty($object->user_modification_id))
261  {
262  if ($usetable) print '<tr><td class="titlefield">';
263  print $langs->trans("ModifiedBy");
264  if ($usetable) print '</td><td>';
265  else print ': ';
266  if (is_object($object->user_modification))
267  {
268  if ($object->user_modification->id) print $object->user_modification->getNomUrl(1, '', 0, 0, 0);
269  else print $langs->trans("Unknown");
270  }
271  else
272  {
273  $userstatic=new User($db);
274  $userstatic->fetch($object->user_modification_id ? $object->user_modification_id : $object->user_modification);
275  if ($userstatic->id) print $userstatic->getNomUrl(1, '', 0, 0, 0);
276  else print $langs->trans("Unknown");
277  }
278  if ($usetable) print '</td></tr>';
279  else print '<br>';
280  }
281 
282  // Date change
283  if (! empty($object->date_modification))
284  {
285  if ($usetable) print '<tr><td class="titlefield">';
286  print $langs->trans("DateLastModification");
287  if ($usetable) print '</td><td>';
288  else print ': ';
289  print dol_print_date($object->date_modification, 'dayhour');
290  if ($deltadateforuser) print ' '.$langs->trans("CurrentHour").' &nbsp; / &nbsp; '.dol_print_date($object->date_modification+($deltadateforuser*3600),"dayhour").' &nbsp;'.$langs->trans("ClientHour");
291  if ($usetable) print '</td></tr>';
292  else print '<br>';
293  }
294 
295  // User validation (old method using already loaded object and not id is kept for backward compatibility)
296  if (! empty($object->user_validation) || ! empty($object->user_validation_id))
297  {
298  if ($usetable) print '<tr><td class="titlefield">';
299  print $langs->trans("ValidatedBy");
300  if ($usetable) print '</td><td>';
301  else print ': ';
302  if (is_object($object->user_validation))
303  {
304  if ($object->user_validation->id) print $object->user_validation->getNomUrl(1, '', 0, 0, 0);
305  else print $langs->trans("Unknown");
306  }
307  else
308  {
309  $userstatic=new User($db);
310  $userstatic->fetch($object->user_validation_id ? $object->user_validation_id : $object->user_validation);
311  if ($userstatic->id) print $userstatic->getNomUrl(1, '', 0, 0, 0);
312  else print $langs->trans("Unknown");
313  }
314  if ($usetable) print '</td></tr>';
315  else print '<br>';
316  }
317 
318  // Date validation
319  if (! empty($object->date_validation))
320  {
321  if ($usetable) print '<tr><td class="titlefield">';
322  print $langs->trans("DateValidation");
323  if ($usetable) print '</td><td>';
324  else print ': ';
325  print dol_print_date($object->date_validation, 'dayhour');
326  if ($deltadateforuser) print ' '.$langs->trans("CurrentHour").' &nbsp; / &nbsp; '.dol_print_date($object->date_validation+($deltadateforuser*3600),"dayhour").' &nbsp;'.$langs->trans("ClientHour");
327  if ($usetable) print '</td></tr>';
328  else print '<br>';
329  }
330 
331  // User approve (old method using already loaded object and not id is kept for backward compatibility)
332  if (! empty($object->user_approve) || ! empty($object->user_approve_id))
333  {
334  if ($usetable) print '<tr><td class="titlefield">';
335  print $langs->trans("ApprovedBy");
336  if ($usetable) print '</td><td>';
337  else print ': ';
338  if (is_object($object->user_approve))
339  {
340  if ($object->user_approve->id) print $object->user_approve->getNomUrl(1, '', 0, 0, 0);
341  else print $langs->trans("Unknown");
342  }
343  else
344  {
345  $userstatic=new User($db);
346  $userstatic->fetch($object->user_approve_id ? $object->user_approve_id : $object->user_approve);
347  if ($userstatic->id) print $userstatic->getNomUrl(1, '', 0, 0, 0);
348  else print $langs->trans("Unknown");
349  }
350  if ($usetable) print '</td></tr>';
351  else print '<br>';
352  }
353 
354  // Date approve
355  if (! empty($object->date_approve))
356  {
357  if ($usetable) print '<tr><td class="titlefield">';
358  print $langs->trans("DateApprove");
359  if ($usetable) print '</td><td>';
360  else print ': ';
361  print dol_print_date($object->date_approve, 'dayhour');
362  if ($deltadateforuser) print ' '.$langs->trans("CurrentHour").' &nbsp; / &nbsp; '.dol_print_date($object->date_approve+($deltadateforuser*3600),"dayhour").' &nbsp;'.$langs->trans("ClientHour");
363  if ($usetable) print '</td></tr>';
364  else print '<br>';
365  }
366 
367  // User approve
368  if (! empty($object->user_approve_id2))
369  {
370  if ($usetable) print '<tr><td class="titlefield">';
371  print $langs->trans("ApprovedBy");
372  if ($usetable) print '</td><td>';
373  else print ': ';
374  $userstatic=new User($db);
375  $userstatic->fetch($object->user_approve_id2);
376  if ($userstatic->id) print $userstatic->getNomUrl(1, '', 0, 0, 0);
377  else print $langs->trans("Unknown");
378  if ($usetable) print '</td></tr>';
379  else print '<br>';
380  }
381 
382  // Date approve
383  if (! empty($object->date_approve2))
384  {
385  if ($usetable) print '<tr><td class="titlefield">';
386  print $langs->trans("DateApprove2");
387  if ($usetable) print '</td><td>';
388  else print ': ';
389  print dol_print_date($object->date_approve2, 'dayhour');
390  if ($deltadateforuser) print ' '.$langs->trans("CurrentHour").' &nbsp; / &nbsp; '.dol_print_date($object->date_approve2+($deltadateforuser*3600),"dayhour").' &nbsp;'.$langs->trans("ClientHour");
391  if ($usetable) print '</td></tr>';
392  else print '<br>';
393  }
394 
395  // User close
396  if (! empty($object->user_cloture))
397  {
398  if ($usetable) print '<tr><td class="titlefield">';
399  print $langs->trans("ClosedBy");
400  if ($usetable) print '</td><td>';
401  else print ': ';
402  if (is_object($object->user_cloture))
403  {
404  if ($object->user_cloture->id) print $object->user_cloture->getNomUrl(1, '', 0, 0, 0);
405  else print $langs->trans("Unknown");
406  }
407  else
408  {
409  $userstatic=new User($db);
410  $userstatic->fetch($object->user_cloture);
411  if ($userstatic->id) print $userstatic->getNomUrl(1, '', 0, 0, 0);
412  else print $langs->trans("Unknown");
413  }
414  if ($usetable) print '</td></tr>';
415  else print '<br>';
416  }
417 
418  // Date close
419  if (! empty($object->date_cloture))
420  {
421  if ($usetable) print '<tr><td class="titlefield">';
422  print $langs->trans("DateClosing");
423  if ($usetable) print '</td><td>';
424  else print ': ';
425  print dol_print_date($object->date_cloture, 'dayhour');
426  if ($deltadateforuser) print ' '.$langs->trans("CurrentHour").' &nbsp; / &nbsp; '.dol_print_date($object->date_cloture+($deltadateforuser*3600),"dayhour").' &nbsp;'.$langs->trans("ClientHour");
427  if ($usetable) print '</td></tr>';
428  else print '<br>';
429  }
430 
431  // User conciliate
432  if (! empty($object->user_rappro))
433  {
434  if ($usetable) print '<tr><td class="titlefield">';
435  print $langs->trans("ConciliatedBy");
436  if ($usetable) print '</td><td>';
437  else print ': ';
438  if (is_object($object->user_rappro))
439  {
440  if ($object->user_rappro->id) print $object->user_rappro->getNomUrl(1, '', 0, 0, 0);
441  else print $langs->trans("Unknown");
442  }
443  else
444  {
445  $userstatic=new User($db);
446  $userstatic->fetch($object->user_rappro);
447  if ($userstatic->id) print $userstatic->getNomUrl(1, '', 0, 0, 0);
448  else print $langs->trans("Unknown");
449  }
450  if ($usetable) print '</td></tr>';
451  else print '<br>';
452  }
453 
454  // Date conciliate
455  if (! empty($object->date_rappro))
456  {
457  if ($usetable) print '<tr><td class="titlefield">';
458  print $langs->trans("DateConciliating");
459  if ($usetable) print '</td><td>';
460  else print ': ';
461  print dol_print_date($object->date_rappro, 'dayhour');
462  if ($deltadateforuser) print ' '.$langs->trans("CurrentHour").' &nbsp; / &nbsp; '.dol_print_date($object->date_rappro+($deltadateforuser*3600),"dayhour").' &nbsp;'.$langs->trans("ClientHour");
463  if ($usetable) print '</td></tr>';
464  else print '<br>';
465  }
466 
467  // Date send
468  if (! empty($object->date_envoi))
469  {
470  if ($usetable) print '<tr><td class="titlefield">';
471  print $langs->trans("DateLastSend");
472  if ($usetable) print '</td><td>';
473  else print ': ';
474  print dol_print_date($object->date_envoi, 'dayhour');
475  if ($deltadateforuser) print ' '.$langs->trans("CurrentHour").' &nbsp; / &nbsp; '.dol_print_date($object->date_envoi+($deltadateforuser*3600),"dayhour").' &nbsp;'.$langs->trans("ClientHour");
476  if ($usetable) print '</td></tr>';
477  else print '<br>';
478  }
479 
480  if ($usetable) print '</table>';
481 }
482 
483 
492 function dolAddEmailTrackId($email, $trackingid)
493 {
494  $tmp=explode('@',$email);
495  return $tmp[0].'+'.$trackingid.'@'.(isset($tmp[1])?$tmp[1]:'');
496 }
497 
504 function isValidMailDomain($mail)
505 {
506  list($user, $domain) = explode("@", $mail, 2);
507  if (checkdnsrr($domain, "MX"))
508  {
509  return true;
510  }
511  else
512  {
513  return false;
514  }
515 }
516 
530 function isValidUrl($url,$http=0,$pass=0,$port=0,$path=0,$query=0,$anchor=0)
531 {
532  $ValidUrl = 0;
533  $urlregex = '';
534 
535  // SCHEME
536  if ($http) $urlregex .= "^(http:\/\/|https:\/\/)";
537 
538  // USER AND PASS
539  if ($pass) $urlregex .= "([a-z0-9+!*(),;?&=\$_.-]+(\:[a-z0-9+!*(),;?&=\$_.-]+)?@)";
540 
541  // HOSTNAME OR IP
542  //$urlregex .= "[a-z0-9+\$_-]+(\.[a-z0-9+\$_-]+)*"; // x allowed (ex. http://localhost, http://routerlogin)
543  //$urlregex .= "[a-z0-9+\$_-]+(\.[a-z0-9+\$_-]+)+"; // x.x
544  $urlregex .= "([a-z0-9+\$_\\\:-])+(\.[a-z0-9+\$_-][a-z0-9+\$_-]+)*"; // x ou x.xx (2 x ou plus)
545  //use only one of the above
546 
547  // PORT
548  if ($port) $urlregex .= "(\:[0-9]{2,5})";
549  // PATH
550  if ($path) $urlregex .= "(\/([a-z0-9+\$_-]\.?)+)*\/";
551  // GET Query
552  if ($query) $urlregex .= "(\?[a-z+&\$_.-][a-z0-9;:@\/&%=+\$_.-]*)";
553  // ANCHOR
554  if ($anchor) $urlregex .= "(#[a-z_.-][a-z0-9+\$_.-]*)$";
555 
556  // check
557  if (preg_match('/'.$urlregex.'/i', $url))
558  {
559  $ValidUrl = 1;
560  }
561  //print $urlregex.' - '.$url.' - '.$ValidUrl;
562 
563  return $ValidUrl;
564 }
565 
573 function clean_url($url,$http=1)
574 {
575  // Fixed by Matelli (see http://matelli.fr/showcases/patchs-dolibarr/fix-cleaning-url.html)
576  // To include the minus sign in a char class, we must not escape it but put it at the end of the class
577  // Also, there's no need of escape a dot sign in a class
578  if (preg_match('/^(https?:[\\/]+)?([0-9A-Z.-]+\.[A-Z]{2,4})(:[0-9]+)?/i',$url,$regs))
579  {
580  $proto=$regs[1];
581  $domain=$regs[2];
582  $port=isset($regs[3])?$regs[3]:'';
583  //print $url." -> ".$proto." - ".$domain." - ".$port;
584  //$url = dol_string_nospecial(trim($url));
585  $url = trim($url);
586 
587  // Si http: defini on supprime le http (Si https on ne supprime pas)
588  $newproto=$proto;
589  if ($http==0)
590  {
591  if (preg_match('/^http:[\\/]+/i',$url))
592  {
593  $url = preg_replace('/^http:[\\/]+/i','',$url);
594  $newproto = '';
595  }
596  }
597 
598  // On passe le nom de domaine en minuscule
599  $CleanUrl = preg_replace('/^'.preg_quote($proto.$domain,'/').'/i', $newproto.strtolower($domain), $url);
600 
601  return $CleanUrl;
602  }
603  else return $url;
604 }
605 
606 
607 
619 function dolObfuscateEmail($mail, $replace="*", $nbreplace=8, $nbdisplaymail=4, $nbdisplaydomain=3, $displaytld=true)
620 {
621  if(!isValidEmail($mail))return '';
622  $tab = explode('@', $mail);
623  $tab2 = explode('.',$tab[1]);
624  $string_replace = '';
625  $mail_name = $tab[0];
626  $mail_domaine = $tab2[0];
627  $mail_tld = '';
628 
629  $nbofelem = count($tab2);
630  for($i=1; $i < $nbofelem && $displaytld; $i++)
631  {
632  $mail_tld .= '.'.$tab2[$i];
633  }
634 
635  for($i=0; $i < $nbreplace; $i++){
636  $string_replace .= $replace;
637  }
638 
639  if(strlen($mail_name) > $nbdisplaymail){
640  $mail_name = substr($mail_name, 0, $nbdisplaymail);
641  }
642 
643  if(strlen($mail_domaine) > $nbdisplaydomain){
644  $mail_domaine = substr($mail_domaine, strlen($mail_domaine)-$nbdisplaydomain);
645  }
646 
647  return $mail_name . $string_replace . $mail_domaine . $mail_tld;
648 }
649 
650 
660 function array2tr($data,$troptions='',$tdoptions='')
661 {
662  $text = '<tr '.$troptions.'>' ;
663  foreach($data as $key => $item){
664  $text.= '<td '.$tdoptions.'>'.$item.'</td>' ;
665  }
666  $text.= '</tr>' ;
667  return $text ;
668 }
669 
680 function array2table($data,$tableMarkup=1,$tableoptions='',$troptions='',$tdoptions='')
681 {
682  $text='' ;
683  if($tableMarkup) $text = '<table '.$tableoptions.'>' ;
684  foreach($data as $key => $item){
685  if(is_array($item)){
686  $text.=array2tr($item,$troptions,$tdoptions);
687  } else {
688  $text.= '<tr '.$troptions.'>' ;
689  $text.= '<td '.$tdoptions.'>'.$key.'</td>' ;
690  $text.= '<td '.$tdoptions.'>'.$item.'</td>' ;
691  $text.= '</tr>' ;
692  }
693  }
694  if($tableMarkup) $text.= '</table>' ;
695  return $text ;
696 }
697 
713 function get_next_value($db,$mask,$table,$field,$where='',$objsoc='',$date='',$mode='next', $bentityon=true, $objuser=null)
714 {
715  global $conf,$user;
716 
717  if (! is_object($objsoc)) $valueforccc=$objsoc;
718  else if($table == "commande_fournisseur" || $table == "facture_fourn" ) $valueforccc=$objsoc->code_fournisseur;
719  else $valueforccc=$objsoc->code_client;
720 
721  // Clean parameters
722  if ($date == '') $date=dol_now(); // We use local year and month of PHP server to search numbers
723  // but we should use local year and month of user
724 
725  // For debugging
726  //dol_syslog("mask=".$mask, LOG_DEBUG);
727  //include_once(DOL_DOCUMENT_ROOT.'/core/lib/date.lib.php');
728  //$mask='FA{yy}{mm}-{0000@99}';
729  //$date=dol_mktime(12, 0, 0, 1, 1, 1900);
730  //$date=dol_stringtotime('20130101');
731 
732  $hasglobalcounter=false;
733  // Extract value for mask counter, mask raz and mask offset
734  if (preg_match('/\{(0+)([@\+][0-9\-\+\=]+)?([@\+][0-9\-\+\=]+)?\}/i',$mask,$reg))
735  {
736  $masktri=$reg[1].(! empty($reg[2])?$reg[2]:'').(! empty($reg[3])?$reg[3]:'');
737  $maskcounter=$reg[1];
738  $hasglobalcounter=true;
739  }
740  else
741  {
742  // setting some defaults so the rest of the code won't fail if there is a third party counter
743  $masktri='00000';
744  $maskcounter='00000';
745  }
746 
747  $maskraz=-1;
748  $maskoffset=0;
749  $resetEveryMonth=false;
750  if (dol_strlen($maskcounter) < 3 && empty($conf->global->MAIN_COUNTER_WITH_LESS_3_DIGITS)) return 'ErrorCounterMustHaveMoreThan3Digits';
751 
752  // Extract value for third party mask counter
753  if (preg_match('/\{(c+)(0*)\}/i',$mask,$regClientRef))
754  {
755  $maskrefclient=$regClientRef[1].$regClientRef[2];
756  $maskrefclient_maskclientcode=$regClientRef[1];
757  $maskrefclient_maskcounter=$regClientRef[2];
758  $maskrefclient_maskoffset=0; //default value of maskrefclient_counter offset
759  $maskrefclient_clientcode=substr($valueforccc,0,dol_strlen($maskrefclient_maskclientcode));//get n first characters of client code where n is length in mask
760  $maskrefclient_clientcode=str_pad($maskrefclient_clientcode,dol_strlen($maskrefclient_maskclientcode),"#",STR_PAD_RIGHT);//padding maskrefclient_clientcode for having exactly n characters in maskrefclient_clientcode
761  $maskrefclient_clientcode=dol_string_nospecial($maskrefclient_clientcode);//sanitize maskrefclient_clientcode for sql insert and sql select like
762  if (dol_strlen($maskrefclient_maskcounter) > 0 && dol_strlen($maskrefclient_maskcounter) < 3) return 'ErrorCounterMustHaveMoreThan3Digits';
763  }
764  else $maskrefclient='';
765 
766  // fail if there is neither a global nor a third party counter
767  if (! $hasglobalcounter && ($maskrefclient_maskcounter == ''))
768  {
769  return 'ErrorBadMask';
770  }
771 
772  // Extract value for third party type
773  if (preg_match('/\{(t+)\}/i',$mask,$regType))
774  {
775  $masktype=$regType[1];
776  $masktype_value=substr(preg_replace('/^TE_/','',$objsoc->typent_code),0,dol_strlen($regType[1]));// get n first characters of thirdpaty typent_code (where n is length in mask)
777  $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
778  }
779  else
780  {
781  $masktype='';
782  $masktype_value='';
783  }
784 
785  // Extract value for user
786  if (preg_match('/\{(u+)\}/i',$mask,$regType))
787  {
788  $lastname = 'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX';
789  if (is_object($objuser)) $lastname = $objuser->lastname;
790 
791  $maskuser=$regType[1];
792  $maskuser_value=substr($lastname,0,dol_strlen($regType[1]));// get n first characters of user firstname (where n is length in mask)
793  $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
794  }
795  else
796  {
797  $maskuser='';
798  $maskuser_value='';
799  }
800 
801  // Personalized field {XXX-1} à {XXX-9}
802  /*$maskperso=array();
803  $maskpersonew=array();
804  $tmpmask=$mask;
805  while (preg_match('/\{([A-Z]+)\-([1-9])\}/i',$tmpmask,$regKey))
806  {
807  $maskperso[$regKey[1]]='\{'.$regKey[1]+'\-'.$regKey[2].'\}';
808  $maskpersonew[$regKey[1]]=str_pad('', '_', $regKey[2], STR_PAD_RIGHT);
809  $tmpmask=preg_replace('/\{'.$regKey[1].'\-'.$regKey[2].'\}/i', $maskpersonew, $tmpmask);
810  }*/
811 
812  if (strstr($mask,'user_extra_'))
813  {
814  $start = "{user_extra_";
815  $end = "\}";
816  $extra= get_string_between($mask, "user_extra_", "}");
817  if(!empty($user->array_options['options_'.$extra])){
818  $mask = preg_replace('#('.$start.')(.*?)('.$end.')#si', $user->array_options['options_'.$extra], $mask);
819  }
820  }
821  $maskwithonlyymcode=$mask;
822  $maskwithonlyymcode=preg_replace('/\{(0+)([@\+][0-9\-\+\=]+)?([@\+][0-9\-\+\=]+)?\}/i',$maskcounter,$maskwithonlyymcode);
823  $maskwithonlyymcode=preg_replace('/\{dd\}/i','dd',$maskwithonlyymcode);
824  $maskwithonlyymcode=preg_replace('/\{(c+)(0*)\}/i',$maskrefclient,$maskwithonlyymcode);
825  $maskwithonlyymcode=preg_replace('/\{(t+)\}/i',$masktype_value,$maskwithonlyymcode);
826  $maskwithonlyymcode=preg_replace('/\{(u+)\}/i',$maskuser_value,$maskwithonlyymcode);
827  /*foreach($maskperso as $key => $val)
828  {
829  $maskwithonlyymcode=preg_replace('/'.$val.'/i', $maskpersonew[$key], $maskwithonlyymcode);
830  }*/
831  $maskwithnocode=$maskwithonlyymcode;
832  $maskwithnocode=preg_replace('/\{yyyy\}/i','yyyy',$maskwithnocode);
833  $maskwithnocode=preg_replace('/\{yy\}/i','yy',$maskwithnocode);
834  $maskwithnocode=preg_replace('/\{y\}/i','y',$maskwithnocode);
835  $maskwithnocode=preg_replace('/\{mm\}/i','mm',$maskwithnocode);
836  // Now maskwithnocode = 0000ddmmyyyyccc for example
837  // and maskcounter = 0000 for example
838  //print "maskwithonlyymcode=".$maskwithonlyymcode." maskwithnocode=".$maskwithnocode."\n<br>";
839  //var_dump($reg);
840 
841  // If an offset is asked
842  if (! empty($reg[2]) && preg_match('/^\+/',$reg[2])) $maskoffset=preg_replace('/^\+/','',$reg[2]);
843  if (! empty($reg[3]) && preg_match('/^\+/',$reg[3])) $maskoffset=preg_replace('/^\+/','',$reg[3]);
844 
845  // Define $sqlwhere
846  $sqlwhere='';
847  $yearoffset=0; // Use year of current $date by default
848  $yearoffsettype=false; // false: no reset, 0,-,=,+: reset at offset SOCIETE_FISCAL_MONTH_START, x=reset at offset x
849 
850  // If a restore to zero after a month is asked we check if there is already a value for this year.
851  if (! empty($reg[2]) && preg_match('/^@/',$reg[2])) $yearoffsettype = preg_replace('/^@/','',$reg[2]);
852  if (! empty($reg[3]) && preg_match('/^@/',$reg[3])) $yearoffsettype = preg_replace('/^@/','',$reg[3]);
853 
854  //print "yearoffset=".$yearoffset." yearoffsettype=".$yearoffsettype;
855  if (is_numeric($yearoffsettype) && $yearoffsettype >= 1)
856  $maskraz=$yearoffsettype; // For backward compatibility
857  else if ($yearoffsettype === '0' || (! empty($yearoffsettype) && ! is_numeric($yearoffsettype) && $conf->global->SOCIETE_FISCAL_MONTH_START > 1))
858  $maskraz = $conf->global->SOCIETE_FISCAL_MONTH_START;
859  //print "maskraz=".$maskraz; // -1=no reset
860 
861  if ($maskraz > 0) { // A reset is required
862  if ($maskraz == 99) {
863  $maskraz = date('m', $date);
864  $resetEveryMonth = true;
865  }
866  if ($maskraz > 12) return 'ErrorBadMaskBadRazMonth';
867 
868  // Define posy, posm and reg
869  if ($maskraz > 1) // if reset is not first month, we need month and year into mask
870  {
871  if (preg_match('/^(.*)\{(y+)\}\{(m+)\}/i',$maskwithonlyymcode,$reg)) { $posy=2; $posm=3; }
872  elseif (preg_match('/^(.*)\{(m+)\}\{(y+)\}/i',$maskwithonlyymcode,$reg)) { $posy=3; $posm=2; }
873  else return 'ErrorCantUseRazInStartedYearIfNoYearMonthInMask';
874 
875  if (dol_strlen($reg[$posy]) < 2) return 'ErrorCantUseRazWithYearOnOneDigit';
876  }
877  else // if reset is for a specific month in year, we need year
878  {
879  if (preg_match('/^(.*)\{(m+)\}\{(y+)\}/i',$maskwithonlyymcode,$reg)) { $posy=3; $posm=2; }
880  else if (preg_match('/^(.*)\{(y+)\}\{(m+)\}/i',$maskwithonlyymcode,$reg)) { $posy=2; $posm=3; }
881  else if (preg_match('/^(.*)\{(y+)\}/i',$maskwithonlyymcode,$reg)) { $posy=2; $posm=0; }
882  else return 'ErrorCantUseRazIfNoYearInMask';
883  }
884  // Define length
885  $yearlen = $posy?dol_strlen($reg[$posy]):0;
886  $monthlen = $posm?dol_strlen($reg[$posm]):0;
887  // Define pos
888  $yearpos = (dol_strlen($reg[1])+1);
889  $monthpos = ($yearpos+$yearlen);
890  if ($posy == 3 && $posm == 2) { // if month is before year
891  $monthpos = (dol_strlen($reg[1])+1);
892  $yearpos = ($monthpos+$monthlen);
893  }
894  //print "xxx ".$maskwithonlyymcode." maskraz=".$maskraz." posy=".$posy." yearlen=".$yearlen." yearpos=".$yearpos." posm=".$posm." monthlen=".$monthlen." monthpos=".$monthpos." yearoffsettype=".$yearoffsettype." resetEveryMonth=".$resetEveryMonth."\n";
895 
896  // Define $yearcomp and $monthcomp (that will be use in the select where to search max number)
897  $monthcomp=$maskraz;
898  $yearcomp=0;
899 
900  if (! empty($yearoffsettype) && ! is_numeric($yearoffsettype) && $yearoffsettype != '=') // $yearoffsettype is - or +
901  {
902  $currentyear=date("Y", $date);
903  $fiscaldate=dol_mktime('0','0','0',$maskraz,'1',$currentyear);
904  $newyeardate=dol_mktime('0','0','0','1','1',$currentyear);
905  $nextnewyeardate=dol_mktime('0','0','0','1','1',$currentyear+1);
906  //echo 'currentyear='.$currentyear.' date='.dol_print_date($date, 'day').' fiscaldate='.dol_print_date($fiscaldate, 'day').'<br>';
907 
908  // If after or equal of current fiscal date
909  if ($date >= $fiscaldate)
910  {
911  // If before of next new year date
912  if ($date < $nextnewyeardate && $yearoffsettype == '+') $yearoffset=1;
913  }
914  // If after or equal of current new year date
915  else if ($date >= $newyeardate && $yearoffsettype == '-') $yearoffset=-1;
916  }
917  // For backward compatibility
918  else if (date("m",$date) < $maskraz && empty($resetEveryMonth)) { $yearoffset=-1; } // If current month lower that month of return to zero, year is previous year
919 
920  if ($yearlen == 4) $yearcomp=sprintf("%04d",date("Y",$date)+$yearoffset);
921  elseif ($yearlen == 2) $yearcomp=sprintf("%02d",date("y",$date)+$yearoffset);
922  elseif ($yearlen == 1) $yearcomp=substr(date("y",$date),2,1)+$yearoffset;
923  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)
924  {
925  if ($yearlen == 4) $yearcomp1=sprintf("%04d",date("Y",$date)+$yearoffset+1);
926  elseif ($yearlen == 2) $yearcomp1=sprintf("%02d",date("y",$date)+$yearoffset+1);
927 
928  $sqlwhere.="(";
929  $sqlwhere.=" (SUBSTRING(".$field.", ".$yearpos.", ".$yearlen.") = '".$yearcomp."'";
930  $sqlwhere.=" AND SUBSTRING(".$field.", ".$monthpos.", ".$monthlen.") >= '".str_pad($monthcomp, $monthlen, '0', STR_PAD_LEFT)."')";
931  $sqlwhere.=" OR";
932  $sqlwhere.=" (SUBSTRING(".$field.", ".$yearpos.", ".$yearlen.") = '".$yearcomp1."'";
933  $sqlwhere.=" AND SUBSTRING(".$field.", ".$monthpos.", ".$monthlen.") < '".str_pad($monthcomp, $monthlen, '0', STR_PAD_LEFT)."') ";
934  $sqlwhere.=')';
935  }
936  else if ($resetEveryMonth)
937  {
938  $sqlwhere.="(SUBSTRING(".$field.", ".$yearpos.", ".$yearlen.") = '".$yearcomp."'";
939  $sqlwhere.=" AND SUBSTRING(".$field.", ".$monthpos.", ".$monthlen.") = '".str_pad($monthcomp, $monthlen, '0', STR_PAD_LEFT)."')";
940  }
941  else // reset is done on january
942  {
943  $sqlwhere.='(SUBSTRING('.$field.', '.$yearpos.', '.$yearlen.") = '".$yearcomp."')";
944  }
945  }
946  //print "sqlwhere=".$sqlwhere." yearcomp=".$yearcomp."<br>\n"; // sqlwhere and yearcomp defined only if we ask a reset
947  //print "masktri=".$masktri." maskcounter=".$maskcounter." maskraz=".$maskraz." maskoffset=".$maskoffset."<br>\n";
948 
949  // Define $sqlstring
950  if (function_exists('mb_strrpos'))
951  {
952  $posnumstart=mb_strrpos($maskwithnocode,$maskcounter, 'UTF-8');
953  }
954  else
955  {
956  $posnumstart=strrpos($maskwithnocode,$maskcounter);
957  } // Pos of counter in final string (from 0 to ...)
958  if ($posnumstart < 0) return 'ErrorBadMaskFailedToLocatePosOfSequence';
959  $sqlstring='SUBSTRING('.$field.', '.($posnumstart+1).', '.dol_strlen($maskcounter).')';
960 
961  // Define $maskLike
962  $maskLike = dol_string_nospecial($mask);
963  $maskLike = str_replace("%","_",$maskLike);
964  // Replace protected special codes with matching number of _ as wild card caracter
965  $maskLike = preg_replace('/\{yyyy\}/i','____',$maskLike);
966  $maskLike = preg_replace('/\{yy\}/i','__',$maskLike);
967  $maskLike = preg_replace('/\{y\}/i','_',$maskLike);
968  $maskLike = preg_replace('/\{mm\}/i','__',$maskLike);
969  $maskLike = preg_replace('/\{dd\}/i','__',$maskLike);
970  $maskLike = str_replace(dol_string_nospecial('{'.$masktri.'}'),str_pad("",dol_strlen($maskcounter),"_"),$maskLike);
971  if ($maskrefclient) $maskLike = str_replace(dol_string_nospecial('{'.$maskrefclient.'}'),str_pad("",dol_strlen($maskrefclient),"_"),$maskLike);
972  if ($masktype) $maskLike = str_replace(dol_string_nospecial('{'.$masktype.'}'),$masktype_value,$maskLike);
973  if ($maskuser) $maskLike = str_replace(dol_string_nospecial('{'.$maskuser.'}'),$maskuser_value,$maskLike);
974 
975  // Get counter in database
976  $counter=0;
977  $sql = "SELECT MAX(".$sqlstring.") as val";
978  $sql.= " FROM ".MAIN_DB_PREFIX.$table;
979  $sql.= " WHERE ".$field." LIKE '".$maskLike."'";
980  $sql.= " AND ".$field." NOT LIKE '(PROV%)'";
981  if ($bentityon) // only if entity enable
982  $sql.= " AND entity IN (".getEntity($table, 1).")";
983 
984  if ($where) $sql.=$where;
985  if ($sqlwhere) $sql.=' AND '.$sqlwhere;
986 
987  //print $sql.'<br>';
988  dol_syslog("functions2::get_next_value mode=".$mode."", LOG_DEBUG);
989  $resql=$db->query($sql);
990  if ($resql)
991  {
992  $obj = $db->fetch_object($resql);
993  $counter = $obj->val;
994  }
995  else dol_print_error($db);
996 
997  // Check if we must force counter to maskoffset
998  if (empty($counter) || preg_match('/[^0-9]/i',$counter)) $counter=$maskoffset;
999  else if ($counter < $maskoffset && empty($conf->global->MAIN_NUMBERING_OFFSET_ONLY_FOR_FIRST)) $counter=$maskoffset;
1000 
1001  if ($mode == 'last') // We found value for counter = last counter value. Now need to get corresponding ref of invoice.
1002  {
1003  $counterpadded=str_pad($counter,dol_strlen($maskcounter),"0",STR_PAD_LEFT);
1004 
1005  // Define $maskLike
1006  $maskLike = dol_string_nospecial($mask);
1007  $maskLike = str_replace("%","_",$maskLike);
1008  // Replace protected special codes with matching number of _ as wild card caracter
1009  $maskLike = preg_replace('/\{yyyy\}/i','____',$maskLike);
1010  $maskLike = preg_replace('/\{yy\}/i','__',$maskLike);
1011  $maskLike = preg_replace('/\{y\}/i','_',$maskLike);
1012  $maskLike = preg_replace('/\{mm\}/i','__',$maskLike);
1013  $maskLike = preg_replace('/\{dd\}/i','__',$maskLike);
1014  $maskLike = str_replace(dol_string_nospecial('{'.$masktri.'}'),$counterpadded,$maskLike);
1015  if ($maskrefclient) $maskLike = str_replace(dol_string_nospecial('{'.$maskrefclient.'}'),str_pad("",dol_strlen($maskrefclient),"_"),$maskLike);
1016  if ($masktype) $maskLike = str_replace(dol_string_nospecial('{'.$masktype.'}'),$masktype_value,$maskLike);
1017  if ($maskuser) $maskLike = str_replace(dol_string_nospecial('{'.$maskuser.'}'),$maskuser_value,$maskLike);
1018 
1019  $ref='';
1020  $sql = "SELECT ".$field." as ref";
1021  $sql.= " FROM ".MAIN_DB_PREFIX.$table;
1022  $sql.= " WHERE ".$field." LIKE '".$maskLike."'";
1023  $sql.= " AND ".$field." NOT LIKE '%PROV%'";
1024  if ($bentityon) // only if entity enable
1025  $sql.= " AND entity IN (".getEntity($table, 1).")";
1026  if ($where) $sql.=$where;
1027  if ($sqlwhere) $sql.=' AND '.$sqlwhere;
1028 
1029  dol_syslog("functions2::get_next_value mode=".$mode."", LOG_DEBUG);
1030  $resql=$db->query($sql);
1031  if ($resql)
1032  {
1033  $obj = $db->fetch_object($resql);
1034  if ($obj) $ref = $obj->ref;
1035  }
1036  else dol_print_error($db);
1037 
1038  $numFinal=$ref;
1039  }
1040  else if ($mode == 'next')
1041  {
1042  $counter++;
1043 
1044  // If value for $counter has a length higher than $maskcounter chars
1045  if ($counter >= pow(10, dol_strlen($maskcounter)))
1046  {
1047  $counter='ErrorMaxNumberReachForThisMask';
1048  }
1049 
1050  if (! empty($maskrefclient_maskcounter))
1051  {
1052  //print "maskrefclient_maskcounter=".$maskrefclient_maskcounter." maskwithnocode=".$maskwithnocode." maskrefclient=".$maskrefclient."\n<br>";
1053 
1054  // Define $sqlstring
1055  $maskrefclient_posnumstart=strpos($maskwithnocode,$maskrefclient_maskcounter,strpos($maskwithnocode,$maskrefclient)); // Pos of counter in final string (from 0 to ...)
1056  if ($maskrefclient_posnumstart <= 0) return 'ErrorBadMask';
1057  $maskrefclient_sqlstring='SUBSTRING('.$field.', '.($maskrefclient_posnumstart+1).', '.dol_strlen($maskrefclient_maskcounter).')';
1058  //print "x".$sqlstring;
1059 
1060  // Define $maskrefclient_maskLike
1061  $maskrefclient_maskLike = dol_string_nospecial($mask);
1062  $maskrefclient_maskLike = str_replace("%","_",$maskrefclient_maskLike);
1063  // Replace protected special codes with matching number of _ as wild card caracter
1064  $maskrefclient_maskLike = str_replace(dol_string_nospecial('{yyyy}'),'____',$maskrefclient_maskLike);
1065  $maskrefclient_maskLike = str_replace(dol_string_nospecial('{yy}'),'__',$maskrefclient_maskLike);
1066  $maskrefclient_maskLike = str_replace(dol_string_nospecial('{y}'),'_',$maskrefclient_maskLike);
1067  $maskrefclient_maskLike = str_replace(dol_string_nospecial('{mm}'),'__',$maskrefclient_maskLike);
1068  $maskrefclient_maskLike = str_replace(dol_string_nospecial('{dd}'),'__',$maskrefclient_maskLike);
1069  $maskrefclient_maskLike = str_replace(dol_string_nospecial('{'.$masktri.'}'),str_pad("",dol_strlen($maskcounter),"_"),$maskrefclient_maskLike);
1070  $maskrefclient_maskLike = str_replace(dol_string_nospecial('{'.$maskrefclient.'}'),$maskrefclient_clientcode.str_pad("",dol_strlen($maskrefclient_maskcounter),"_"),$maskrefclient_maskLike);
1071 
1072  // Get counter in database
1073  $maskrefclient_counter=0;
1074  $maskrefclient_sql = "SELECT MAX(".$maskrefclient_sqlstring.") as val";
1075  $maskrefclient_sql.= " FROM ".MAIN_DB_PREFIX.$table;
1076  //$sql.= " WHERE ".$field." not like '(%'";
1077  $maskrefclient_sql.= " WHERE ".$field." LIKE '".$maskrefclient_maskLike."'";
1078  if ($bentityon) // only if entity enable
1079  $maskrefclient_sql.= " AND entity IN (".getEntity($table, 1).")";
1080  if ($where) $maskrefclient_sql.=$where; //use the same optional where as general mask
1081  if ($sqlwhere) $maskrefclient_sql.=' AND '.$sqlwhere; //use the same sqlwhere as general mask
1082  $maskrefclient_sql.=' AND (SUBSTRING('.$field.', '.(strpos($maskwithnocode,$maskrefclient)+1).', '.dol_strlen($maskrefclient_maskclientcode).")='".$maskrefclient_clientcode."')";
1083 
1084  dol_syslog("functions2::get_next_value maskrefclient", LOG_DEBUG);
1085  $maskrefclient_resql=$db->query($maskrefclient_sql);
1086  if ($maskrefclient_resql)
1087  {
1088  $maskrefclient_obj = $db->fetch_object($maskrefclient_resql);
1089  $maskrefclient_counter = $maskrefclient_obj->val;
1090  }
1091  else dol_print_error($db);
1092 
1093  if (empty($maskrefclient_counter) || preg_match('/[^0-9]/i',$maskrefclient_counter)) $maskrefclient_counter=$maskrefclient_maskoffset;
1094  $maskrefclient_counter++;
1095  }
1096 
1097  // Build numFinal
1098  $numFinal = $mask;
1099 
1100  // We replace special codes except refclient
1101  if (! empty($yearoffsettype) && ! is_numeric($yearoffsettype) && $yearoffsettype != '=') // yearoffsettype is - or +, so we don't want current year
1102  {
1103  $numFinal = preg_replace('/\{yyyy\}/i',date("Y",$date)+$yearoffset, $numFinal);
1104  $numFinal = preg_replace('/\{yy\}/i', date("y",$date)+$yearoffset, $numFinal);
1105  $numFinal = preg_replace('/\{y\}/i', substr(date("y",$date),1,1)+$yearoffset, $numFinal);
1106  }
1107  else // we want yyyy to be current year
1108  {
1109  $numFinal = preg_replace('/\{yyyy\}/i',date("Y",$date), $numFinal);
1110  $numFinal = preg_replace('/\{yy\}/i', date("y",$date), $numFinal);
1111  $numFinal = preg_replace('/\{y\}/i', substr(date("y",$date),1,1), $numFinal);
1112  }
1113  $numFinal = preg_replace('/\{mm\}/i', date("m",$date), $numFinal);
1114  $numFinal = preg_replace('/\{dd\}/i', date("d",$date), $numFinal);
1115 
1116  // Now we replace the counter
1117  $maskbefore='{'.$masktri.'}';
1118  $maskafter=str_pad($counter,dol_strlen($maskcounter),"0",STR_PAD_LEFT);
1119  //print 'x'.$maskbefore.'-'.$maskafter.'y';
1120  $numFinal = str_replace($maskbefore,$maskafter,$numFinal);
1121 
1122  // Now we replace the refclient
1123  if ($maskrefclient)
1124  {
1125  //print "maskrefclient=".$maskrefclient." maskwithonlyymcode=".$maskwithonlyymcode." maskwithnocode=".$maskwithnocode."\n<br>";
1126  $maskrefclient_maskbefore='{'.$maskrefclient.'}';
1127  $maskrefclient_maskafter=$maskrefclient_clientcode.str_pad($maskrefclient_counter,dol_strlen($maskrefclient_maskcounter),"0",STR_PAD_LEFT);
1128  $numFinal = str_replace($maskrefclient_maskbefore,$maskrefclient_maskafter,$numFinal);
1129  }
1130 
1131  // Now we replace the type
1132  if ($masktype)
1133  {
1134  $masktype_maskbefore='{'.$masktype.'}';
1135  $masktype_maskafter=$masktype_value;
1136  $numFinal = str_replace($masktype_maskbefore,$masktype_maskafter,$numFinal);
1137  }
1138 
1139  // Now we replace the user
1140  if ($maskuser)
1141  {
1142  $maskuser_maskbefore='{'.$maskuser.'}';
1143  $maskuser_maskafter=$maskuser_value;
1144  $numFinal = str_replace($maskuser_maskbefore,$maskuser_maskafter,$numFinal);
1145  }
1146  }
1147 
1148  dol_syslog("functions2::get_next_value return ".$numFinal,LOG_DEBUG);
1149  return $numFinal;
1150 }
1151 
1152 function get_string_between($string, $start, $end){
1153  $string = " ".$string;
1154  $ini = strpos($string,$start);
1155  if ($ini == 0) return "";
1156  $ini += strlen($start);
1157  $len = strpos($string,$end,$ini) - $ini;
1158  return substr($string,$ini,$len);
1159 }
1160 
1168 function check_value($mask,$value)
1169 {
1170  $result=0;
1171 
1172  $hasglobalcounter=false;
1173  // Extract value for mask counter, mask raz and mask offset
1174  if (preg_match('/\{(0+)([@\+][0-9]+)?([@\+][0-9]+)?\}/i',$mask,$reg))
1175  {
1176  $masktri=$reg[1].(isset($reg[2])?$reg[2]:'').(isset($reg[3])?$reg[3]:'');
1177  $maskcounter=$reg[1];
1178  $hasglobalcounter=true;
1179  }
1180  else
1181  {
1182  // setting some defaults so the rest of the code won't fail if there is a third party counter
1183  $masktri='00000';
1184  $maskcounter='00000';
1185  }
1186 
1187  $maskraz=-1;
1188  $maskoffset=0;
1189  if (dol_strlen($maskcounter) < 3) return 'ErrorCounterMustHaveMoreThan3Digits';
1190 
1191  // Extract value for third party mask counter
1192  if (preg_match('/\{(c+)(0*)\}/i',$mask,$regClientRef))
1193  {
1194  $maskrefclient=$regClientRef[1].$regClientRef[2];
1195  $maskrefclient_maskclientcode=$regClientRef[1];
1196  $maskrefclient_maskcounter=$regClientRef[2];
1197  $maskrefclient_maskoffset=0; //default value of maskrefclient_counter offset
1198  $maskrefclient_clientcode=substr('',0,dol_strlen($maskrefclient_maskclientcode));//get n first characters of client code to form maskrefclient_clientcode
1199  $maskrefclient_clientcode=str_pad($maskrefclient_clientcode,dol_strlen($maskrefclient_maskclientcode),"#",STR_PAD_RIGHT);//padding maskrefclient_clientcode for having exactly n characters in maskrefclient_clientcode
1200  $maskrefclient_clientcode=dol_string_nospecial($maskrefclient_clientcode);//sanitize maskrefclient_clientcode for sql insert and sql select like
1201  if (dol_strlen($maskrefclient_maskcounter) > 0 && dol_strlen($maskrefclient_maskcounter) < 3) return 'ErrorCounterMustHaveMoreThan3Digits';
1202  }
1203  else $maskrefclient='';
1204 
1205  // fail if there is neither a global nor a third party counter
1206  if (! $hasglobalcounter && ($maskrefclient_maskcounter == ''))
1207  {
1208  return 'ErrorBadMask';
1209  }
1210 
1211  $maskwithonlyymcode=$mask;
1212  $maskwithonlyymcode=preg_replace('/\{(0+)([@\+][0-9]+)?([@\+][0-9]+)?\}/i',$maskcounter,$maskwithonlyymcode);
1213  $maskwithonlyymcode=preg_replace('/\{dd\}/i','dd',$maskwithonlyymcode);
1214  $maskwithonlyymcode=preg_replace('/\{(c+)(0*)\}/i',$maskrefclient,$maskwithonlyymcode);
1215  $maskwithnocode=$maskwithonlyymcode;
1216  $maskwithnocode=preg_replace('/\{yyyy\}/i','yyyy',$maskwithnocode);
1217  $maskwithnocode=preg_replace('/\{yy\}/i','yy',$maskwithnocode);
1218  $maskwithnocode=preg_replace('/\{y\}/i','y',$maskwithnocode);
1219  $maskwithnocode=preg_replace('/\{mm\}/i','mm',$maskwithnocode);
1220  // Now maskwithnocode = 0000ddmmyyyyccc for example
1221  // and maskcounter = 0000 for example
1222  //print "maskwithonlyymcode=".$maskwithonlyymcode." maskwithnocode=".$maskwithnocode."\n<br>";
1223 
1224  // If an offset is asked
1225  if (! empty($reg[2]) && preg_match('/^\+/',$reg[2])) $maskoffset=preg_replace('/^\+/','',$reg[2]);
1226  if (! empty($reg[3]) && preg_match('/^\+/',$reg[3])) $maskoffset=preg_replace('/^\+/','',$reg[3]);
1227 
1228  // Define $sqlwhere
1229 
1230  // If a restore to zero after a month is asked we check if there is already a value for this year.
1231  if (! empty($reg[2]) && preg_match('/^@/',$reg[2])) $maskraz=preg_replace('/^@/','',$reg[2]);
1232  if (! empty($reg[3]) && preg_match('/^@/',$reg[3])) $maskraz=preg_replace('/^@/','',$reg[3]);
1233  if ($maskraz >= 0)
1234  {
1235  if ($maskraz > 12) return 'ErrorBadMaskBadRazMonth';
1236 
1237  // Define reg
1238  if ($maskraz > 1 && ! preg_match('/^(.*)\{(y+)\}\{(m+)\}/i',$maskwithonlyymcode,$reg)) return 'ErrorCantUseRazInStartedYearIfNoYearMonthInMask';
1239  if ($maskraz <= 1 && ! preg_match('/^(.*)\{(y+)\}/i',$maskwithonlyymcode,$reg)) return 'ErrorCantUseRazIfNoYearInMask';
1240  //print "x".$maskwithonlyymcode." ".$maskraz;
1241  }
1242  //print "masktri=".$masktri." maskcounter=".$maskcounter." maskraz=".$maskraz." maskoffset=".$maskoffset."<br>\n";
1243 
1244  // Check we have a number in ($posnumstart+1).', '.dol_strlen($maskcounter)
1245  //
1246 
1247  // Check length
1248  $len=dol_strlen($maskwithnocode);
1249  if (dol_strlen($value) != $len) $result=-1;
1250 
1251  // Define $maskLike
1252  /* seems not used
1253  $maskLike = dol_string_nospecial($mask);
1254  $maskLike = str_replace("%","_",$maskLike);
1255  // Replace protected special codes with matching number of _ as wild card caracter
1256  $maskLike = str_replace(dol_string_nospecial('{yyyy}'),'____',$maskLike);
1257  $maskLike = str_replace(dol_string_nospecial('{yy}'),'__',$maskLike);
1258  $maskLike = str_replace(dol_string_nospecial('{y}'),'_',$maskLike);
1259  $maskLike = str_replace(dol_string_nospecial('{mm}'),'__',$maskLike);
1260  $maskLike = str_replace(dol_string_nospecial('{dd}'),'__',$maskLike);
1261  $maskLike = str_replace(dol_string_nospecial('{'.$masktri.'}'),str_pad("",dol_strlen($maskcounter),"_"),$maskLike);
1262  if ($maskrefclient) $maskLike = str_replace(dol_string_nospecial('{'.$maskrefclient.'}'),str_pad("",strlen($maskrefclient),"_"),$maskLike);
1263  */
1264 
1265  dol_syslog("functions2::check_value result=".$result,LOG_DEBUG);
1266  return $result;
1267 }
1268 
1277 function binhex($bin, $pad=false, $upper=false)
1278 {
1279  $last = dol_strlen($bin)-1;
1280  for($i=0; $i<=$last; $i++){ $x += $bin[$last-$i] * pow(2,$i); }
1281  $x = dechex($x);
1282  if($pad){ while(dol_strlen($x) < intval(dol_strlen($bin))/4){ $x = "0$x"; } }
1283  if($upper){ $x = strtoupper($x); }
1284  return $x;
1285 }
1286 
1293 function hexbin($hexa)
1294 {
1295  $bin='';
1296  $strLength = dol_strlen($hexa);
1297  for($i=0;$i<$strLength;$i++)
1298  {
1299  $bin.=str_pad(decbin(hexdec($hexa{$i})),4,'0',STR_PAD_LEFT);
1300  }
1301  return $bin;
1302 }
1303 
1310 function numero_semaine($time)
1311 {
1312  $stime = strftime('%Y-%m-%d',$time);
1313 
1314  if (preg_match('/^([0-9]+)\-([0-9]+)\-([0-9]+)\s?([0-9]+)?:?([0-9]+)?/i',$stime,$reg))
1315  {
1316  // Date est au format 'YYYY-MM-DD' ou 'YYYY-MM-DD HH:MM:SS'
1317  $annee = $reg[1];
1318  $mois = $reg[2];
1319  $jour = $reg[3];
1320  }
1321 
1322  /*
1323  * Norme ISO-8601:
1324  * - 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.
1325  * - La majorite des annees ont 52 semaines mais les annees qui commence un jeudi et les annees bissextiles commencant un mercredi en possede 53.
1326  * - Le 1er jour de la semaine est le Lundi
1327  */
1328 
1329  // Definition du Jeudi de la semaine
1330  if (date("w",mktime(12,0,0,$mois,$jour,$annee))==0) // Dimanche
1331  $jeudiSemaine = mktime(12,0,0,$mois,$jour,$annee)-3*24*60*60;
1332  else if (date("w",mktime(12,0,0,$mois,$jour,$annee))<4) // du Lundi au Mercredi
1333  $jeudiSemaine = mktime(12,0,0,$mois,$jour,$annee)+(4-date("w",mktime(12,0,0,$mois,$jour,$annee)))*24*60*60;
1334  else if (date("w",mktime(12,0,0,$mois,$jour,$annee))>4) // du Vendredi au Samedi
1335  $jeudiSemaine = mktime(12,0,0,$mois,$jour,$annee)-(date("w",mktime(12,0,0,$mois,$jour,$annee))-4)*24*60*60;
1336  else // Jeudi
1337  $jeudiSemaine = mktime(12,0,0,$mois,$jour,$annee);
1338 
1339  // Definition du premier Jeudi de l'annee
1340  if (date("w",mktime(12,0,0,1,1,date("Y",$jeudiSemaine)))==0) // Dimanche
1341  {
1342  $premierJeudiAnnee = mktime(12,0,0,1,1,date("Y",$jeudiSemaine))+4*24*60*60;
1343  }
1344  else if (date("w",mktime(12,0,0,1,1,date("Y",$jeudiSemaine)))<4) // du Lundi au Mercredi
1345  {
1346  $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;
1347  }
1348  else if (date("w",mktime(12,0,0,1,1,date("Y",$jeudiSemaine)))>4) // du Vendredi au Samedi
1349  {
1350  $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;
1351  }
1352  else // Jeudi
1353  {
1354  $premierJeudiAnnee = mktime(12,0,0,1,1,date("Y",$jeudiSemaine));
1355  }
1356 
1357  // Definition du numero de semaine: nb de jours entre "premier Jeudi de l'annee" et "Jeudi de la semaine";
1358  $numeroSemaine = (
1359  (
1360  date("z",mktime(12,0,0,date("m",$jeudiSemaine),date("d",$jeudiSemaine),date("Y",$jeudiSemaine)))
1361  -
1362  date("z",mktime(12,0,0,date("m",$premierJeudiAnnee),date("d",$premierJeudiAnnee),date("Y",$premierJeudiAnnee)))
1363  ) / 7
1364  ) + 1;
1365 
1366  // Cas particulier de la semaine 53
1367  if ($numeroSemaine==53)
1368  {
1369  // Les annees qui commence un Jeudi et les annees bissextiles commencant un Mercredi en possede 53
1370  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))
1371  {
1372  $numeroSemaine = 53;
1373  }
1374  else
1375  {
1376  $numeroSemaine = 1;
1377  }
1378  }
1379 
1380  //echo $jour."-".$mois."-".$annee." (".date("d-m-Y",$premierJeudiAnnee)." - ".date("d-m-Y",$jeudiSemaine).") -> ".$numeroSemaine."<BR>";
1381 
1382  return sprintf("%02d",$numeroSemaine);
1383 }
1384 
1393 function weight_convert($weight,&$from_unit,$to_unit)
1394 {
1395  /* Pour convertire 320 gr en Kg appeler
1396  * $f = -3
1397  * weigh_convert(320, $f, 0) retournera 0.32
1398  *
1399  */
1400  while ($from_unit <> $to_unit)
1401  {
1402  if ($from_unit > $to_unit)
1403  {
1404  $weight = $weight * 10;
1405  $from_unit = $from_unit - 1;
1406  $weight = weight_convert($weight,$from_unit, $to_unit);
1407  }
1408  if ($from_unit < $to_unit)
1409  {
1410  $weight = $weight / 10;
1411  $from_unit = $from_unit + 1;
1412  $weight = weight_convert($weight,$from_unit, $to_unit);
1413  }
1414  }
1415 
1416  return $weight;
1417 }
1418 
1430 function dol_set_user_param($db, $conf, &$user, $tab)
1431 {
1432  // Verification parametres
1433  if (count($tab) < 1) return -1;
1434 
1435  $db->begin();
1436 
1437  // We remove old parameters for all keys in $tab
1438  $sql = "DELETE FROM ".MAIN_DB_PREFIX."user_param";
1439  $sql.= " WHERE fk_user = ".$user->id;
1440  $sql.= " AND entity = ".$conf->entity;
1441  $sql.= " AND param in (";
1442  $i=0;
1443  foreach ($tab as $key => $value)
1444  {
1445  if ($i > 0) $sql.=',';
1446  $sql.="'".$key."'";
1447  $i++;
1448  }
1449  $sql.= ")";
1450  dol_syslog("functions2.lib::dol_set_user_param", LOG_DEBUG);
1451 
1452  $resql=$db->query($sql);
1453  if (! $resql)
1454  {
1455  dol_print_error($db);
1456  $db->rollback();
1457  return -1;
1458  }
1459 
1460  foreach ($tab as $key => $value)
1461  {
1462  // Set new parameters
1463  if ($value)
1464  {
1465  $sql = "INSERT INTO ".MAIN_DB_PREFIX."user_param(fk_user,entity,param,value)";
1466  $sql.= " VALUES (".$user->id.",".$conf->entity.",";
1467  $sql.= " '".$key."','".$db->escape($value)."')";
1468 
1469  dol_syslog("functions2.lib::dol_set_user_param", LOG_DEBUG);
1470  $result=$db->query($sql);
1471  if (! $result)
1472  {
1473  dol_print_error($db);
1474  $db->rollback();
1475  return -1;
1476  }
1477  $user->conf->$key = $value;
1478  //print "key=".$key." user->conf->key=".$user->conf->$key;
1479  }
1480  else
1481  {
1482  unset($user->conf->$key);
1483  }
1484  }
1485 
1486  $db->commit();
1487  return 1;
1488 }
1489 
1497 function dol_print_reduction($reduction,$langs)
1498 {
1499  $string = '';
1500  if ($reduction == 100)
1501  {
1502  $string = $langs->transnoentities("Offered");
1503  }
1504  else
1505  {
1506  $string = price($reduction).'%';
1507  }
1508 
1509  return $string;
1510 }
1511 
1518 function version_os()
1519 {
1520  $osversion=php_uname();
1521  return $osversion;
1522 }
1523 
1530 function version_php()
1531 {
1532  return phpversion();
1533 }
1534 
1542 {
1543  return DOL_VERSION;
1544 }
1545 
1552 {
1553  return $_SERVER["SERVER_SOFTWARE"];
1554 }
1555 
1564 function getListOfModels($db,$type,$maxfilenamelength=0)
1565 {
1566  global $conf,$langs;
1567  $liste=array();
1568  $found=0;
1569  $dirtoscan='';
1570 
1571  $sql = "SELECT nom as id, nom as lib, libelle as label, description as description";
1572  $sql.= " FROM ".MAIN_DB_PREFIX."document_model";
1573  $sql.= " WHERE type = '".$type."'";
1574  $sql.= " AND entity IN (0,".$conf->entity.")";
1575  $sql.= " ORDER BY description DESC";
1576 
1577  dol_syslog('/core/lib/function2.lib.php::getListOfModels', LOG_DEBUG);
1578  $resql = $db->query($sql);
1579  if ($resql)
1580  {
1581  $num = $db->num_rows($resql);
1582  $i = 0;
1583  while ($i < $num)
1584  {
1585  $found=1;
1586 
1587  $obj = $db->fetch_object($resql);
1588 
1589  // If this generation module needs to scan a directory, then description field is filled
1590  // with the constant that contains list of directories to scan (COMPANY_ADDON_PDF_ODT_PATH, ...).
1591  if (! empty($obj->description)) // A list of directories to scan is defined
1592  {
1593  include_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
1594 
1595  $const=$obj->description;
1596  //irtoscan.=($dirtoscan?',':'').preg_replace('/[\r\n]+/',',',trim($conf->global->$const));
1597  $dirtoscan= preg_replace('/[\r\n]+/',',',trim($conf->global->$const));
1598 
1599  $listoffiles=array();
1600 
1601  // Now we add models found in directories scanned
1602  $listofdir=explode(',',$dirtoscan);
1603  foreach($listofdir as $key=>$tmpdir)
1604  {
1605  $tmpdir=trim($tmpdir);
1606  $tmpdir=preg_replace('/DOL_DATA_ROOT/',DOL_DATA_ROOT,$tmpdir);
1607  if (! $tmpdir) { unset($listofdir[$key]); continue; }
1608  if (is_dir($tmpdir))
1609  {
1610  // all type of template is allowed
1611  $tmpfiles=dol_dir_list($tmpdir, 'files', 0, '', '', 'name', SORT_ASC, 0);
1612  if (count($tmpfiles)) $listoffiles=array_merge($listoffiles,$tmpfiles);
1613  }
1614  }
1615 
1616  if (count($listoffiles))
1617  {
1618  foreach($listoffiles as $record)
1619  {
1620  $max=($maxfilenamelength?$maxfilenamelength:28);
1621  $liste[$obj->id.':'.$record['fullname']]=dol_trunc($record['name'],$max,'middle');
1622  }
1623  }
1624  else
1625  {
1626  $liste[0]=$obj->label.': '.$langs->trans("None");
1627  }
1628  }
1629  else
1630  {
1631  if ($type == 'member' && $obj->lib == 'standard') // Special case, if member template, we add variant per format
1632  {
1633  global $_Avery_Labels;
1634  include_once DOL_DOCUMENT_ROOT.'/core/lib/format_cards.lib.php';
1635  foreach($_Avery_Labels as $key => $val)
1636  {
1637  $liste[$obj->id.':'.$key]=($obj->label?$obj->label:$obj->lib).' '.$val['name'];
1638  }
1639  }
1640  else // Common usage
1641  {
1642  $liste[$obj->id]=$obj->label?$obj->label:$obj->lib;
1643  }
1644  }
1645  $i++;
1646  }
1647  }
1648  else
1649  {
1650  dol_print_error($db);
1651  return -1;
1652  }
1653 
1654  if ($found) return $liste;
1655  else return 0;
1656 }
1657 
1665 function is_ip($ip)
1666 {
1667  // First we test if it is a valid IPv4
1668  if (filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4)) {
1669 
1670  // Then we test if it is a private range
1671  if (! filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_NO_PRIV_RANGE)) return 2;
1672 
1673  // Then we test if it is a reserved range
1674  if (! filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_NO_RES_RANGE)) return 0;
1675 
1676  return 1;
1677  }
1678 
1679  return 0;
1680 }
1681 
1689 function dol_buildlogin($lastname,$firstname)
1690 {
1691  $login=strtolower(dol_string_unaccent($firstname));
1692  $login.=($login?'.':'');
1693  $login.=strtolower(dol_string_unaccent($lastname));
1694  $login=dol_string_nospecial($login,''); // For special names
1695  return $login;
1696 }
1697 
1703 function getSoapParams()
1704 {
1705  global $conf;
1706 
1707  $params=array();
1708  $proxyuse =(empty($conf->global->MAIN_PROXY_USE)?false:true);
1709  $proxyhost=(empty($conf->global->MAIN_PROXY_USE)?false:$conf->global->MAIN_PROXY_HOST);
1710  $proxyport=(empty($conf->global->MAIN_PROXY_USE)?false:$conf->global->MAIN_PROXY_PORT);
1711  $proxyuser=(empty($conf->global->MAIN_PROXY_USE)?false:$conf->global->MAIN_PROXY_USER);
1712  $proxypass=(empty($conf->global->MAIN_PROXY_USE)?false:$conf->global->MAIN_PROXY_PASS);
1713  $timeout =(empty($conf->global->MAIN_USE_CONNECT_TIMEOUT)?10:$conf->global->MAIN_USE_CONNECT_TIMEOUT); // Connection timeout
1714  $response_timeout=(empty($conf->global->MAIN_USE_RESPONSE_TIMEOUT)?30:$conf->global->MAIN_USE_RESPONSE_TIMEOUT); // Response timeout
1715  //print extension_loaded('soap');
1716  if ($proxyuse)
1717  {
1718  $params=array('connection_timeout'=>$timeout,
1719  'response_timeout'=>$response_timeout,
1720  'proxy_use' => 1,
1721  'proxy_host' => $proxyhost,
1722  'proxy_port' => $proxyport,
1723  'proxy_login' => $proxyuser,
1724  'proxy_password' => $proxypass,
1725  'trace' => 1
1726  );
1727  }
1728  else
1729  {
1730  $params=array('connection_timeout'=>$timeout,
1731  'response_timeout'=>$response_timeout,
1732  'proxy_use' => 0,
1733  'proxy_host' => false,
1734  'proxy_port' => false,
1735  'proxy_login' => false,
1736  'proxy_password' => false,
1737  'trace' => 1
1738  );
1739  }
1740  return $params;
1741 }
1742 
1743 
1753 function dolGetElementUrl($objectid,$objecttype,$withpicto=0,$option='')
1754 {
1755  global $db,$conf;
1756 
1757  $ret='';
1758 
1759  // Parse element/subelement (ex: project_task)
1760  $module = $element = $subelement = $objecttype;
1761  if (preg_match('/^([^_]+)_([^_]+)/i',$objecttype,$regs))
1762  {
1763  $module = $element = $regs[1];
1764  $subelement = $regs[2];
1765  }
1766 
1767  $classpath = $element.'/class';
1768 
1769  // To work with non standard path
1770  if ($objecttype == 'facture' || $objecttype == 'invoice') {
1771  $classpath = 'compta/facture/class';
1772  $module='facture';
1773  $subelement='facture';
1774  }
1775  if ($objecttype == 'commande' || $objecttype == 'order') {
1776  $classpath = 'commande/class';
1777  $module='commande';
1778  $subelement='commande';
1779  }
1780  if ($objecttype == 'propal') {
1781  $classpath = 'comm/propal/class';
1782  }
1783  if ($objecttype == 'supplier_proposal') {
1784  $classpath = 'supplier_proposal/class';
1785  }
1786  if ($objecttype == 'shipping') {
1787  $classpath = 'expedition/class';
1788  $subelement = 'expedition';
1789  $module = 'expedition_bon';
1790  }
1791  if ($objecttype == 'delivery') {
1792  $classpath = 'livraison/class';
1793  $subelement = 'livraison';
1794  $module = 'livraison_bon';
1795  }
1796  if ($objecttype == 'contract') {
1797  $classpath = 'contrat/class';
1798  $module='contrat';
1799  $subelement='contrat';
1800  }
1801  if ($objecttype == 'member') {
1802  $classpath = 'adherents/class';
1803  $module='adherent';
1804  $subelement='adherent';
1805  }
1806  if ($objecttype == 'cabinetmed_cons') {
1807  $classpath = 'cabinetmed/class';
1808  $module='cabinetmed';
1809  $subelement='cabinetmedcons';
1810  }
1811  if ($objecttype == 'fichinter') {
1812  $classpath = 'fichinter/class';
1813  $module='ficheinter';
1814  $subelement='fichinter';
1815  }
1816  if ($objecttype == 'task') {
1817  $classpath = 'projet/class';
1818  $module='projet';
1819  $subelement='task';
1820  }
1821 
1822  //print "objecttype=".$objecttype." module=".$module." subelement=".$subelement;
1823 
1824  $classfile = strtolower($subelement); $classname = ucfirst($subelement);
1825  if ($objecttype == 'invoice_supplier') {
1826  $classfile = 'fournisseur.facture';
1827  $classname='FactureFournisseur';
1828  $classpath = 'fourn/class';
1829  $module='fournisseur';
1830  }
1831  if ($objecttype == 'order_supplier') {
1832  $classfile = 'fournisseur.commande';
1833  $classname='CommandeFournisseur';
1834  $classpath = 'fourn/class';
1835  $module='fournisseur';
1836  }
1837 
1838  if (! empty($conf->$module->enabled))
1839  {
1840  $res=dol_include_once('/'.$classpath.'/'.$classfile.'.class.php');
1841  if ($res)
1842  {
1843  $object = new $classname($db);
1844  $res=$object->fetch($objectid);
1845  if ($res > 0) $ret=$object->getNomUrl($withpicto,$option);
1846  unset($object);
1847  }
1848  }
1849  return $ret;
1850 }
1851 
1852 
1861 function cleanCorruptedTree($db, $tabletocleantree, $fieldfkparent)
1862 {
1863  $totalnb=0;
1864  $listofid=array();
1865  $listofparentid=array();
1866 
1867  // Get list of all id in array listofid and all parents in array listofparentid
1868  $sql='SELECT rowid, '.$fieldfkparent.' as parent_id FROM '.MAIN_DB_PREFIX.$tabletocleantree;
1869  $resql = $db->query($sql);
1870  if ($resql)
1871  {
1872  $num = $db->num_rows($resql);
1873  $i = 0;
1874  while ($i < $num)
1875  {
1876  $obj = $db->fetch_object($resql);
1877  $listofid[]=$obj->rowid;
1878  if ($obj->parent_id > 0) $listofparentid[$obj->rowid]=$obj->parent_id;
1879  $i++;
1880  }
1881  }
1882  else
1883  {
1884  dol_print_error($db);
1885  }
1886 
1887  if (count($listofid))
1888  {
1889  print 'Code requested to clean tree (may be to solve data corruption), so we check/clean orphelins and loops.'."<br>\n";
1890 
1891  // Check loops on each other
1892  $sql = "UPDATE ".MAIN_DB_PREFIX.$tabletocleantree." SET ".$fieldfkparent." = 0 WHERE ".$fieldfkparent." = rowid"; // So we update only records linked to themself
1893  $resql = $db->query($sql);
1894  if ($resql)
1895  {
1896  $nb=$db->affected_rows($sql);
1897  if ($nb > 0)
1898  {
1899  print '<br>Some record that were parent of themself were cleaned.';
1900  }
1901 
1902  $totalnb+=$nb;
1903  }
1904  //else dol_print_error($db);
1905 
1906  // Check other loops
1907  $listofidtoclean=array();
1908  foreach($listofparentid as $id => $pid)
1909  {
1910  // Check depth
1911  //print 'Analyse record id='.$id.' with parent '.$pid.'<br>';
1912 
1913  $cursor=$id; $arrayidparsed=array(); // We start from child $id
1914  while ($cursor > 0)
1915  {
1916  $arrayidparsed[$cursor]=1;
1917  if ($arrayidparsed[$listofparentid[$cursor]]) // We detect a loop. A record with a parent that was already into child
1918  {
1919  print 'Found a loop between id '.$id.' - '.$cursor.'<br>';
1920  unset($arrayidparsed);
1921  $listofidtoclean[$cursor]=$id;
1922  break;
1923  }
1924  $cursor=$listofparentid[$cursor];
1925  }
1926 
1927  if (count($listofidtoclean)) break;
1928  }
1929 
1930  $sql = "UPDATE ".MAIN_DB_PREFIX.$tabletocleantree;
1931  $sql.= " SET ".$fieldfkparent." = 0";
1932  $sql.= " WHERE rowid IN (".join(',',$listofidtoclean).")"; // So we update only records detected wrong
1933  $resql = $db->query($sql);
1934  if ($resql)
1935  {
1936  $nb=$db->affected_rows($sql);
1937  if ($nb > 0)
1938  {
1939  // Removed orphelins records
1940  print '<br>Some records were detected to have parent that is a child, we set them as root record for id: ';
1941  print join(',',$listofidtoclean);
1942  }
1943 
1944  $totalnb+=$nb;
1945  }
1946  //else dol_print_error($db);
1947 
1948  // Check and clean orphelins
1949  $sql = "UPDATE ".MAIN_DB_PREFIX.$tabletocleantree;
1950  $sql.= " SET ".$fieldfkparent." = 0";
1951  $sql.= " WHERE ".$fieldfkparent." NOT IN (".join(',',$listofid).")"; // So we update only records linked to a non existing parent
1952  $resql = $db->query($sql);
1953  if ($resql)
1954  {
1955  $nb=$db->affected_rows($sql);
1956  if ($nb > 0)
1957  {
1958  // Removed orphelins records
1959  print '<br>Some orphelins were found and modified to be parent so records are visible again for id: ';
1960  print join(',',$listofid);
1961  }
1962 
1963  $totalnb+=$nb;
1964  }
1965  //else dol_print_error($db);
1966 
1967  print '<br>We fixed '.$totalnb.' record(s). Some records may still be corrupted. New check may be required.';
1968  return $totalnb;
1969  }
1970 }
1971 
1978 function getElementProperties($element_type)
1979 {
1980  // Parse element/subelement (ex: project_task)
1981  $module = $element = $subelement = $element_type;
1982 
1983  // If we ask an resource form external module (instead of default path)
1984  if (preg_match('/^([^@]+)@([^@]+)$/i',$element_type,$regs))
1985  {
1986  $element = $subelement = $regs[1];
1987  $module = $regs[2];
1988  }
1989 
1990  //print '<br />1. element : '.$element.' - module : '.$module .'<br />';
1991  if ( preg_match('/^([^_]+)_([^_]+)/i',$element,$regs))
1992  {
1993  $module = $element = $regs[1];
1994  $subelement = $regs[2];
1995  }
1996 
1997  // For compat
1998  if($element_type == "action") {
1999  $classpath = 'comm/action/class';
2000  $subelement = 'Actioncomm';
2001  $module = 'agenda';
2002  }
2003 
2004  // To work with non standard path
2005  if ($element_type == 'facture' || $element_type == 'invoice') {
2006  $classpath = 'compta/facture/class';
2007  $module='facture';
2008  $subelement='facture';
2009  }
2010  if ($element_type == 'commande' || $element_type == 'order') {
2011  $classpath = 'commande/class';
2012  $module='commande';
2013  $subelement='commande';
2014  }
2015  if ($element_type == 'propal') {
2016  $classpath = 'comm/propal/class';
2017  }
2018  if ($element_type == 'supplier_proposal') {
2019  $classpath = 'supplier_proposal/class';
2020  }
2021  if ($element_type == 'shipping') {
2022  $classpath = 'expedition/class';
2023  $subelement = 'expedition';
2024  $module = 'expedition_bon';
2025  }
2026  if ($element_type == 'delivery') {
2027  $classpath = 'livraison/class';
2028  $subelement = 'livraison';
2029  $module = 'livraison_bon';
2030  }
2031  if ($element_type == 'contract') {
2032  $classpath = 'contrat/class';
2033  $module='contrat';
2034  $subelement='contrat';
2035  }
2036  if ($element_type == 'member') {
2037  $classpath = 'adherents/class';
2038  $module='adherent';
2039  $subelement='adherent';
2040  }
2041  if ($element_type == 'cabinetmed_cons') {
2042  $classpath = 'cabinetmed/class';
2043  $module='cabinetmed';
2044  $subelement='cabinetmedcons';
2045  }
2046  if ($element_type == 'fichinter') {
2047  $classpath = 'fichinter/class';
2048  $module='ficheinter';
2049  $subelement='fichinter';
2050  }
2051  if ($element_type == 'dolresource' || $element_type == 'resource') {
2052  $classpath = 'resource/class';
2053  $module='resource';
2054  $subelement='dolresource';
2055  }
2056  if ($element_type == 'propaldet') {
2057  $classpath = 'comm/propal/class';
2058  $module='propal';
2059  $subelement='propaleligne';
2060  }
2061  if ($element_type == 'order_supplier') {
2062  $classpath = 'fourn/class';
2063  $module='fournisseur';
2064  $subelement='commandefournisseur';
2065  $classfile='fournisseur.commande';
2066  }
2067  if ($element_type == 'invoice_supplier') {
2068  $classpath = 'fourn/class';
2069  $module='fournisseur';
2070  $subelement='facturefournisseur';
2071  $classfile='fournisseur.facture';
2072  }
2073 
2074  if (!isset($classfile)) $classfile = strtolower($subelement);
2075  if (!isset($classname)) $classname = ucfirst($subelement);
2076  if (!isset($classpath)) $classpath = $module.'/class';
2077 
2078  $element_properties = array(
2079  'module' => $module,
2080  'classpath' => $classpath,
2081  'element' => $element,
2082  'subelement' => $subelement,
2083  'classfile' => $classfile,
2084  'classname' => $classname
2085  );
2086  return $element_properties;
2087 }
2088 
2098 function fetchObjectByElement($element_id, $element_type, $element_ref='')
2099 {
2100  global $conf;
2101  global $db,$conf;
2102 
2103  $element_prop = getElementProperties($element_type);
2104  if (is_array($element_prop) && $conf->{$element_prop['module']}->enabled)
2105  {
2106  dol_include_once('/'.$element_prop['classpath'].'/'.$element_prop['classfile'].'.class.php');
2107 
2108  $objecttmp = new $element_prop['classname']($db);
2109  $ret = $objecttmp->fetch($element_id, $element_ref);
2110  if ($ret >= 0)
2111  {
2112  return $objecttmp;
2113  }
2114  }
2115  return 0;
2116 }
2117 
2118 
2128 function colorArrayToHex($arraycolor,$colorifnotfound='888888')
2129 {
2130  if (! is_array($arraycolor)) return $colorifnotfound;
2131  if (empty($arraycolor)) return $colorifnotfound;
2132  return sprintf("%02s",dechex($arraycolor[0])).sprintf("%02s",dechex($arraycolor[1])).sprintf("%02s",dechex($arraycolor[2]));
2133 }
2134 
2145 function colorStringToArray($stringcolor,$colorifnotfound=array(88,88,88))
2146 {
2147  if (is_array($stringcolor)) return $stringcolor; // If already into correct output format, we return as is
2148  $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);
2149  if (! $tmp)
2150  {
2151  $tmp=explode(',',$stringcolor);
2152  if (count($tmp) < 3) return $colorifnotfound;
2153  return $tmp;
2154  }
2155  return array(hexdec($reg[1]),hexdec($reg[2]),hexdec($reg[3]));
2156 }
2157 
2165 function cartesianArray(array $input) {
2166  // filter out empty values
2167  $input = array_filter($input);
2168 
2169  $result = array(array());
2170 
2171  foreach ($input as $key => $values) {
2172  $append = array();
2173 
2174  foreach($result as $product) {
2175  foreach($values as $item) {
2176  $product[$key] = $item;
2177  $append[] = $product;
2178  }
2179  }
2180 
2181  $result = $append;
2182  }
2183 
2184  return $result;
2185 }
2186 
2187 
2194 function getModuleDirForApiClass($module)
2195 {
2196  $moduledirforclass=$module;
2197  if ($moduledirforclass != 'api') $moduledirforclass = preg_replace('/api$/i','',$moduledirforclass);
2198 
2199  if ($module == 'contracts') {
2200  $moduledirforclass = 'contrat';
2201  }
2202  elseif (in_array($module, array('admin', 'login', 'setup', 'access', 'status', 'tools', 'documents'))) {
2203  $moduledirforclass = 'api';
2204  }
2205  elseif ($module == 'contact' || $module == 'contacts' || $module == 'customer' || $module == 'thirdparty' || $module == 'thirdparties') {
2206  $moduledirforclass = 'societe';
2207  }
2208  elseif ($module == 'propale' || $module == 'proposals') {
2209  $moduledirforclass = 'comm/propal';
2210  }
2211  elseif ($module == 'agenda' || $module == 'agendaevents') {
2212  $moduledirforclass = 'comm/action';
2213  }
2214  elseif ($module == 'adherent' || $module == 'members' || $module == 'memberstypes' || $module == 'subscriptions') {
2215  $moduledirforclass = 'adherents';
2216  }
2217  elseif ($module == 'banque' || $module == 'bankaccounts') {
2218  $moduledirforclass = 'compta/bank';
2219  }
2220  elseif ($module == 'category' || $module == 'categorie') {
2221  $moduledirforclass = 'categories';
2222  }
2223  elseif ($module == 'order' || $module == 'orders') {
2224  $moduledirforclass = 'commande';
2225  }
2226  elseif ($module == 'shipments') {
2227  $moduledirforclass = 'expedition';
2228  }
2229  elseif ($module == 'facture' || $module == 'invoice' || $module == 'invoices') {
2230  $moduledirforclass = 'compta/facture';
2231  }
2232  elseif ($module == 'products') {
2233  $moduledirforclass = 'product';
2234  }
2235  elseif ($module == 'project' || $module == 'projects' || $module == 'tasks') {
2236  $moduledirforclass = 'projet';
2237  }
2238  elseif ($module == 'task') {
2239  $moduledirforclass = 'projet';
2240  }
2241  elseif ($module == 'stock' || $module == 'stockmovements' || $module == 'warehouses') {
2242  $moduledirforclass = 'product/stock';
2243  }
2244  elseif ($module == 'supplierproposals' || $module == 'supplierproposal' || $module == 'supplier_proposal') {
2245  $moduledirforclass = 'supplier_proposal';
2246  }
2247  elseif ($module == 'fournisseur' || $module == 'supplierinvoices' || $module == 'supplierorders') {
2248  $moduledirforclass = 'fourn';
2249  }
2250  elseif ($module == 'expensereports') {
2251  $moduledirforclass = 'expensereport';
2252  }
2253  elseif ($module == 'users') {
2254  $moduledirforclass = 'user';
2255  }
2256 
2257  return $moduledirforclass;
2258 }
2259 
2260 /*
2261  * Return 2 hexa code randomly
2262  *
2263  * @param $min int Between 0 and 255
2264  * @param $max int Between 0 and 255
2265  * @return String
2266  */
2267 function random_color_part($min=0,$max=255) {
2268  return str_pad( dechex( mt_rand( $min, $max) ), 2, '0', STR_PAD_LEFT);
2269 }
2270 
2271 /*
2272  * Return hexadecimal color randomly
2273  *
2274  * @param $min int Between 0 and 255
2275  * @param $max int Between 0 and 255
2276  * @return String
2277  */
2278 function random_color($min=0, $max=255) {
2279  return random_color_part($min, $max) . random_color_part($min, $max) . random_color_part($min, $max);
2280 }
array2table($data, $tableMarkup=1, $tableoptions='', $troptions='', $tdoptions='')
Return an html table from an array.
dol_trunc($string, $size=40, $trunc='right', $stringencoding='UTF-8', $nodot=0, $display=0)
Truncate a string to a particular length adding '...' if string larger than length.
getModuleDirForApiClass($module)
Get name of directory where the api_...class.php file is stored.
weight_convert($weight, &$from_unit, $to_unit)
Convertit une masse d'une unite vers une autre unite.
cartesianArray(array $input)
Applies the Cartesian product algorithm to an array Source: http://stackoverflow.com/a/15973172.
dol_mktime($hour, $minute, $second, $month, $day, $year, $gm=false, $check=1)
Return a timestamp date built from detailed informations (by default a local PHP server timestamp) Re...
dol_print_reduction($reduction, $langs)
Returns formated reduction.
version_webserver()
Return web server version.
version_os()
Return OS version.
dol_print_object_info($object, $usetable=0)
Show informations on an object TODO Move this into html.formother.
Class to manage Dolibarr users.
Definition: user.class.php:39
dolGetModulesDirs($subdir='')
Return list of modules directories.
cleanCorruptedTree($db, $tabletocleantree, $fieldfkparent)
Clean corrupted tree (orphelins linked to a not existing parent), record linked to themself and child...
clean_url($url, $http=1)
Clean an url string.
dol_print_error($db='', $error='', $errors=null)
Affiche message erreur system avec toutes les informations pour faciliter le diagnostic et la remonte...
dol_buildlogin($lastname, $firstname)
Build a login from lastname, firstname.
check_value($mask, $value)
Check value.
colorStringToArray($stringcolor, $colorifnotfound=array(88, 88, 88))
Convert a string RGB value ('FFFFFF', '255,255,255') into an array RGB array(255,255,255).
dol_dir_list($path, $types="all", $recursive=0, $filter="", $excludefilter=null, $sortcriteria="name", $sortorder=SORT_ASC, $mode=0, $nohook=0, $relativename="")
Scan a directory and return a list of files/directories.
Definition: files.lib.php:58
isValidUrl($url, $http=0, $pass=0, $port=0, $path=0, $query=0, $anchor=0)
Url string validation :// [user[:pass]@] hostname [port] [/path] [?getquery] [anchor]...
dolAddEmailTrackId($email, $trackingid)
Return an email formatted to include a tracking id For example myemail@example.com becom myemail+trac...
hexbin($hexa)
Convert an hexadecimal string into a binary string.
fetchObjectByElement($element_id, $element_type, $element_ref='')
Fetch an object from its id and element_type Inclusion of classes is automatic.
dol_syslog($message, $level=LOG_INFO, $ident=0, $suffixinfilename='', $restricttologhandler='')
Write log message into outputs.
jsUnEscape($source)
Same function than javascript unescape() function but in PHP.
dol_getDefaultFormat(Translate $outputlangs=null)
Try to guess default paper format according to language into $langs.
Class to manage translations.
version_dolibarr()
Return Dolibarr version.
dol_print_file($langs, $filename, $searchalt=0)
Output content of a file $filename in version of current language (otherwise may use an alternate lan...
is_ip($ip)
This function evaluates a string that should be a valid IPv4 Note: For ip 169.254.0.0, it returns 0 with some PHP (5.6.24) and 2 with some minor patchs of PHP (5.6.25).
getServerTimeZoneInt($refgmtdate='now')
Return server timezone int.
Definition: date.lib.php:82
dol_string_unaccent($str)
Clean a string from all accent characters to be used as ref, login or by dol_sanitizeFileName.
dol_now($mode='gmt')
Return date for now.
utf8_check($str)
Check if a string is in UTF8.
dol_set_user_param($db, $conf, &$user, $tab)
Save personnal parameter.
numero_semaine($time)
Retourne le numero de la semaine par rapport a une date.
version_php()
Return PHP version.
get_next_value($db, $mask, $table, $field, $where='', $objsoc='', $date='', $mode='next', $bentityon=true, $objuser=null)
Return last or next value for a mask (according to area we should not reset)
price($amount, $form=0, $outlangs='', $trunc=1, $rounding=-1, $forcerounding=-1, $currency_code='')
Function to format a value into an amount for visual output Function used into PDF and HTML pages...
dol_print_date($time, $format='', $tzoutput='tzserver', $outputlangs='', $encodetooutput=false)
Output date in a string format according to outputlangs (or langs if not defined).
isValidMailDomain($mail)
Return true if email has a domain name that can't be resolved.
dolGetElementUrl($objectid, $objecttype, $withpicto=0, $option='')
List urls of element.
print
Draft customers invoices.
Definition: index.php:91
array2tr($data, $troptions='', $tdoptions='')
Return lines of an html table from an array Used by array2table function only.
if(!empty($conf->facture->enabled)&&$user->rights->facture->lire) if(!empty($conf->fournisseur->enabled)&&$user->rights->fournisseur->facture->lire) if(!empty($conf->don->enabled)&&$user->rights->societe->lire) if(!empty($conf->tax->enabled)&&$user->rights->tax->charges->lire) if(!empty($conf->facture->enabled)&&!empty($conf->commande->enabled)&&$user->rights->commande->lire &&empty($conf->global->WORKFLOW_DISABLE_CREATE_INVOICE_FROM_ORDER)) if(!empty($conf->facture->enabled)&&$user->rights->facture->lire) if(!empty($conf->fournisseur->enabled)&&$user->rights->fournisseur->facture->lire) $resql
Social contributions to pay.
Definition: index.php:1013
if(!function_exists('dol_getprefix')) dol_include_once($relpath, $classname='')
Return a prefix to use for this Dolibarr instance, for session/cookie names or email id...
colorArrayToHex($arraycolor, $colorifnotfound='888888')
Convert an array with RGB value into hex RGB value.
dol_html_entity_decode($a, $b, $c='UTF-8')
Replace html_entity_decode functions to manage errors.
getListOfModels($db, $type, $maxfilenamelength=0)
Return list of activated modules usable for document generation.
dolObfuscateEmail($mail, $replace="*", $nbreplace=8, $nbdisplaymail=4, $nbdisplaydomain=3, $displaytld=true)
Returns an email value with obfuscated parts.
binhex($bin, $pad=false, $upper=false)
Convert a binary data to string that represent hexadecimal value.
dol_string_nospecial($str, $newstr='_', $badcharstoreplace='')
Clean a string from all punctuation characters to use it as a ref or login.
getSoapParams()
Return array to use for SoapClient constructor.
isValidEmail($address, $acceptsupervisorkey=0)
Return true if email syntax is ok.
getElementProperties($element_type)
Get an array with properties of an element.
dol_strlen($string, $stringencoding='UTF-8')
Make a strlen call.