dolibarr  9.0.0
functions.lib.php
Go to the documentation of this file.
1 <?php
2 /* Copyright (C) 2000-2007 Rodolphe Quiedeville <rodolphe@quiedeville.org>
3  * Copyright (C) 2003 Jean-Louis Bergamo <jlb@j1b.org>
4  * Copyright (C) 2004-2018 Laurent Destailleur <eldy@users.sourceforge.net>
5  * Copyright (C) 2004 Sebastien Di Cintio <sdicintio@ressource-toi.org>
6  * Copyright (C) 2004 Benoit Mortier <benoit.mortier@opensides.be>
7  * Copyright (C) 2004 Christophe Combelles <ccomb@free.fr>
8  * Copyright (C) 2005-2017 Regis Houssin <regis.houssin@inodbox.com>
9  * Copyright (C) 2008 Raphael Bertrand (Resultic) <raphael.bertrand@resultic.fr>
10  * Copyright (C) 2010-2018 Juanjo Menent <jmenent@2byte.es>
11  * Copyright (C) 2013 Cédric Salvador <csalvador@gpcsolutions.fr>
12  * Copyright (C) 2013-2017 Alexandre Spangaro <aspangaro@zendsi.com>
13  * Copyright (C) 2014 Cédric GROSS <c.gross@kreiz-it.fr>
14  * Copyright (C) 2014-2015 Marcos García <marcosgdf@gmail.com>
15  * Copyright (C) 2015 Jean-François Ferry <jfefe@aternatik.fr>
16  * Copyright (C) 2018 Frédéric France <frederic.france@netlogic.fr>
17  *
18  * This program is free software; you can redistribute it and/or modify
19  * it under the terms of the GNU General Public License as published by
20  * the Free Software Foundation; either version 3 of the License, or
21  * (at your option) any later version.
22  *
23  * This program is distributed in the hope that it will be useful,
24  * but WITHOUT ANY WARRANTY; without even the implied warranty of
25  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
26  * GNU General Public License for more details.
27  *
28  * You should have received a copy of the GNU General Public License
29  * along with this program. If not, see <http://www.gnu.org/licenses/>.
30  * or see http://www.gnu.org/
31  */
32 
39 include_once DOL_DOCUMENT_ROOT .'/core/lib/json.lib.php';
40 
41 
53 function getStaticMember($class, $member)
54 {
55  dol_syslog(__FUNCTION__ . " is deprecated", LOG_WARNING);
56 
57  // This part is deprecated. Uncomment if for php 5.2.*, and comment next isset class::member
58  /*if (version_compare(phpversion(), '5.3.0', '<'))
59  {
60  if (is_object($class)) $class = get_class($class);
61  $classObj = new ReflectionClass($class);
62  $result = null;
63 
64  $found=0;
65  foreach($classObj->getStaticProperties() as $prop => $value)
66  {
67  if ($prop == $member)
68  {
69  $result = $value;
70  $found++;
71  break;
72  }
73  }
74 
75  if ($found) return $result;
76  }*/
77 
78  if (isset($class::$member)) return $class::$member;
79  dol_print_error('','Try to get a static member "'.$member.'" in class "'.$class.'" that does not exists or is not static.');
80  return null;
81 }
82 
83 
95 function getDoliDBInstance($type, $host, $user, $pass, $name, $port)
96 {
97  require_once DOL_DOCUMENT_ROOT ."/core/db/".$type.'.class.php';
98 
99  $class='DoliDB'.ucfirst($type);
100  $dolidb=new $class($type, $host, $user, $pass, $name, $port);
101  return $dolidb;
102 }
103 
121 function getEntity($element, $shared=1, $currentobject=null)
122 {
123  global $conf, $mc;
124 
125  if (is_object($mc))
126  {
127  return $mc->getEntity($element, $shared, $currentobject);
128  }
129  else
130  {
131  $out='';
132  $addzero = array('user', 'usergroup', 'c_email_templates', 'email_template', 'default_values');
133  if (in_array($element, $addzero)) $out.= '0,';
134  $out.= $conf->entity;
135  return $out;
136  }
137 }
138 
155 function getBrowserInfo($user_agent)
156 {
157  include_once DOL_DOCUMENT_ROOT.'/includes/mobiledetect/mobiledetectlib/Mobile_Detect.php';
158 
159  $name='unknown';
160  $version='';
161  $os='unknown';
162  $phone = '';
163 
164  $detectmobile = new Mobile_Detect(null, $user_agent);
165  $tablet = $detectmobile->isTablet();
166 
167  if ($detectmobile->isMobile()) {
168 
169  $phone = 'unknown';
170 
171  // If phone/smartphone, we set phone os name.
172  if ($detectmobile->is('AndroidOS')) {
173  $os = $phone = 'android';
174  } elseif ($detectmobile->is('BlackBerryOS')) {
175  $os = $phone = 'blackberry';
176  } elseif ($detectmobile->is('iOS')) {
177  $os = 'ios';
178  $phone = 'iphone';
179  } elseif ($detectmobile->is('PalmOS')) {
180  $os = $phone = 'palm';
181  } elseif ($detectmobile->is('SymbianOS')) {
182  $os = 'symbian';
183  } elseif ($detectmobile->is('webOS')) {
184  $os = 'webos';
185  } elseif ($detectmobile->is('MaemoOS')) {
186  $os = 'maemo';
187  } elseif ($detectmobile->is('WindowsMobileOS') || $detectmobile->is('WindowsPhoneOS')) {
188  $os = 'windows';
189  }
190  }
191 
192  // OS
193  if (preg_match('/linux/i', $user_agent)) { $os='linux'; }
194  elseif (preg_match('/macintosh/i', $user_agent)) { $os='macintosh'; }
195  elseif (preg_match('/windows/i', $user_agent)) { $os='windows'; }
196 
197  // Name
198  if (preg_match('/firefox(\/|\s)([\d\.]*)/i', $user_agent, $reg)) { $name='firefox'; $version=$reg[2]; }
199  elseif (preg_match('/edge(\/|\s)([\d\.]*)/i', $user_agent, $reg)) { $name='edge'; $version=$reg[2]; }
200  elseif (preg_match('/chrome(\/|\s)([\d\.]+)/i', $user_agent, $reg)) { $name='chrome'; $version=$reg[2]; } // we can have 'chrome (Mozilla...) chrome x.y' in one string
201  elseif (preg_match('/chrome/i', $user_agent, $reg)) { $name='chrome'; }
202  elseif (preg_match('/iceweasel/i', $user_agent)) { $name='iceweasel'; }
203  elseif (preg_match('/epiphany/i', $user_agent)) { $name='epiphany'; }
204  elseif (preg_match('/safari(\/|\s)([\d\.]*)/i', $user_agent, $reg)) { $name='safari'; $version=$reg[2]; } // Safari is often present in string for mobile but its not.
205  elseif (preg_match('/opera(\/|\s)([\d\.]*)/i', $user_agent, $reg)) { $name='opera'; $version=$reg[2]; }
206  elseif (preg_match('/(MSIE\s([0-9]+\.[0-9]))|.*(Trident\/[0-9]+.[0-9];.*rv:([0-9]+\.[0-9]+))/i', $user_agent, $reg)) { $name='ie'; $version=end($reg); } // MS products at end
207  elseif (preg_match('/(Windows NT\s([0-9]+\.[0-9])).*(Trident\/[0-9]+.[0-9];.*rv:([0-9]+\.[0-9]+))/i', $user_agent, $reg)) { $name='ie'; $version=end($reg); } // MS products at end
208  elseif (preg_match('/l(i|y)n(x|ks)(\(|\/|\s)*([\d\.]+)/i', $user_agent, $reg)) { $name='lynxlinks'; $version=$reg[4]; }
209 
210  if ($tablet) {
211  $layout = 'tablet';
212  } elseif ($phone) {
213  $layout = 'phone';
214  } else {
215  $layout = 'classic';
216  }
217 
218  return array(
219  'browsername' => $name,
220  'browserversion' => $version,
221  'browseros' => $os,
222  'layout' => $layout,
223  'phone' => $phone,
224  'tablet' => $tablet
225  );
226 }
227 
233 function dol_shutdown()
234 {
235  global $conf,$user,$langs,$db;
236  $disconnectdone=false; $depth=0;
237  if (is_object($db) && ! empty($db->connected)) { $depth=$db->transaction_opened; $disconnectdone=$db->close(); }
238  dol_syslog("--- End access to ".$_SERVER["PHP_SELF"].(($disconnectdone && $depth)?' (Warn: db disconnection forced, transaction depth was '.$depth.')':''), (($disconnectdone && $depth)?LOG_WARNING:LOG_INFO));
239 }
240 
247 function GETPOSTISSET($paramname)
248 {
249  return (isset($_POST[$paramname]) || isset($_GET[$paramname]));
250 }
251 
276 function GETPOST($paramname, $check='none', $method=0, $filter=null, $options=null, $noreplace=0)
277 {
278  global $mysoc,$user,$conf;
279 
280  if (empty($paramname)) return 'BadFirstParameterForGETPOST';
281  if (empty($check))
282  {
283  dol_syslog("Deprecated use of GETPOST, called with 1st param = ".$paramname." and 2nd param is '', when calling page ".$_SERVER["PHP_SELF"], LOG_WARNING);
284  // Enable this line to know who call the GETPOST with '' $check parameter.
285  //var_dump(debug_backtrace()[0]);
286  }
287 
288  if (empty($method)) $out = isset($_GET[$paramname])?$_GET[$paramname]:(isset($_POST[$paramname])?$_POST[$paramname]:'');
289  elseif ($method==1) $out = isset($_GET[$paramname])?$_GET[$paramname]:'';
290  elseif ($method==2) $out = isset($_POST[$paramname])?$_POST[$paramname]:'';
291  elseif ($method==3) $out = isset($_POST[$paramname])?$_POST[$paramname]:(isset($_GET[$paramname])?$_GET[$paramname]:'');
292  elseif ($method==4) $out = isset($_POST[$paramname])?$_POST[$paramname]:(isset($_GET[$paramname])?$_GET[$paramname]:(isset($_COOKIE[$paramname])?$_COOKIE[$paramname]:''));
293  else return 'BadThirdParameterForGETPOST';
294 
295  if (empty($method) || $method == 3 || $method == 4)
296  {
297  $relativepathstring = $_SERVER["PHP_SELF"];
298  // Clean $relativepathstring
299  if (constant('DOL_URL_ROOT')) $relativepathstring = preg_replace('/^'.preg_quote(constant('DOL_URL_ROOT'),'/').'/', '', $relativepathstring);
300  $relativepathstring = preg_replace('/^\//', '', $relativepathstring);
301  $relativepathstring = preg_replace('/^custom\//', '', $relativepathstring);
302  //var_dump($relativepathstring);
303  //var_dump($user->default_values);
304 
305  // Code for search criteria persistence.
306  // Retrieve values if restore_lastsearch_values
307  if (! empty($_GET['restore_lastsearch_values'])) // Use $_GET here and not GETPOST
308  {
309  if (! empty($_SESSION['lastsearch_values_'.$relativepathstring])) // If there is saved values
310  {
311  $tmp=json_decode($_SESSION['lastsearch_values_'.$relativepathstring], true);
312  if (is_array($tmp))
313  {
314  foreach($tmp as $key => $val)
315  {
316  if ($key == $paramname) // We are on the requested parameter
317  {
318  $out=$val;
319  break;
320  }
321  }
322  }
323  }
324  // If there is saved contextpage, page or limit
325  if ($paramname == 'contextpage' && ! empty($_SESSION['lastsearch_contextpage_'.$relativepathstring]))
326  {
327  $out = $_SESSION['lastsearch_contextpage_'.$relativepathstring];
328  }
329  elseif ($paramname == 'page' && ! empty($_SESSION['lastsearch_page_'.$relativepathstring]))
330  {
331  $out = $_SESSION['lastsearch_page_'.$relativepathstring];
332  }
333  elseif ($paramname == 'limit' && ! empty($_SESSION['lastsearch_limit_'.$relativepathstring]))
334  {
335  $out = $_SESSION['lastsearch_limit_'.$relativepathstring];
336  }
337  }
338  // Else, retreive default values if we are not doing a sort
339  elseif (! isset($_GET['sortfield'])) // If we did a click on a field to sort, we do no apply default values. Same if option MAIN_ENABLE_DEFAULT_VALUES is not set
340  {
341  if (! empty($_GET['action']) && $_GET['action'] == 'create' && ! isset($_GET[$paramname]) && ! isset($_POST[$paramname]))
342  {
343  // Search default value from $object->field
344  global $object;
345  if (is_object($object) && isset($object->fields[$paramname]['default']))
346  {
347  $out = $object->fields[$paramname]['default'];
348  }
349  }
350  if (! empty($conf->global->MAIN_ENABLE_DEFAULT_VALUES))
351  {
352  if (! empty($_GET['action']) && $_GET['action'] == 'create' && ! isset($_GET[$paramname]) && ! isset($_POST[$paramname]))
353  {
354  // Now search in setup to overwrite default values
355  if (! empty($user->default_values)) // $user->default_values defined from menu 'Setup - Default values'
356  {
357  if (isset($user->default_values[$relativepathstring]['createform']))
358  {
359  foreach($user->default_values[$relativepathstring]['createform'] as $defkey => $defval)
360  {
361  $qualified = 0;
362  if ($defkey != '_noquery_')
363  {
364  $tmpqueryarraytohave=explode('&', $defkey);
365  $tmpqueryarraywehave=explode('&', dol_string_nohtmltag($_SERVER['QUERY_STRING']));
366  $foundintru=0;
367  foreach($tmpqueryarraytohave as $tmpquerytohave)
368  {
369  if (! in_array($tmpquerytohave, $tmpqueryarraywehave)) $foundintru=1;
370  }
371  if (! $foundintru) $qualified=1;
372  //var_dump($defkey.'-'.$qualified);
373  }
374  else $qualified = 1;
375 
376  if ($qualified)
377  {
378  //var_dump($user->default_values[$relativepathstring][$defkey]['createform']);
379  if (isset($user->default_values[$relativepathstring]['createform'][$defkey][$paramname]))
380  {
381  $out = $user->default_values[$relativepathstring]['createform'][$defkey][$paramname];
382  break;
383  }
384  }
385  }
386  }
387  }
388  }
389  // Management of default search_filters and sort order
390  //elseif (preg_match('/list.php$/', $_SERVER["PHP_SELF"]) && ! empty($paramname) && ! isset($_GET[$paramname]) && ! isset($_POST[$paramname]))
391  elseif (! empty($paramname) && ! isset($_GET[$paramname]) && ! isset($_POST[$paramname]))
392  {
393  if (! empty($user->default_values)) // $user->default_values defined from menu 'Setup - Default values'
394  {
395  //var_dump($user->default_values[$relativepathstring]);
396  if ($paramname == 'sortfield' || $paramname == 'sortorder') // Sorted on which fields ? ASC or DESC ?
397  {
398  if (isset($user->default_values[$relativepathstring]['sortorder'])) // Even if paramname is sortfield, data are stored into ['sortorder...']
399  {
400  foreach($user->default_values[$relativepathstring]['sortorder'] as $defkey => $defval)
401  {
402  $qualified = 0;
403  if ($defkey != '_noquery_')
404  {
405  $tmpqueryarraytohave=explode('&', $defkey);
406  $tmpqueryarraywehave=explode('&', dol_string_nohtmltag($_SERVER['QUERY_STRING']));
407  $foundintru=0;
408  foreach($tmpqueryarraytohave as $tmpquerytohave)
409  {
410  if (! in_array($tmpquerytohave, $tmpqueryarraywehave)) $foundintru=1;
411  }
412  if (! $foundintru) $qualified=1;
413  //var_dump($defkey.'-'.$qualified);
414  }
415  else $qualified = 1;
416 
417  if ($qualified)
418  {
419  $forbidden_chars_to_replace=array(" ","'","/","\\",":","*","?","\"","<",">","|","[","]",";","="); // we accept _, -, . and ,
420  foreach($user->default_values[$relativepathstring]['sortorder'][$defkey] as $key => $val)
421  {
422  if ($out) $out.=', ';
423  if ($paramname == 'sortfield')
424  {
425  $out.=dol_string_nospecial($key, '', $forbidden_chars_to_replace);
426  }
427  if ($paramname == 'sortorder')
428  {
429  $out.=dol_string_nospecial($val, '', $forbidden_chars_to_replace);
430  }
431  }
432  //break; // No break for sortfield and sortorder so we can cumulate fields (is it realy usefull ?)
433  }
434  }
435  }
436  }
437  elseif (isset($user->default_values[$relativepathstring]['filters']))
438  {
439  foreach($user->default_values[$relativepathstring]['filters'] as $defkey => $defval) // $defkey is a querystring like 'a=b&c=d', $defval is key of user
440  {
441  $qualified = 0;
442  if ($defkey != '_noquery_')
443  {
444  $tmpqueryarraytohave=explode('&', $defkey);
445  $tmpqueryarraywehave=explode('&', dol_string_nohtmltag($_SERVER['QUERY_STRING']));
446  $foundintru=0;
447  foreach($tmpqueryarraytohave as $tmpquerytohave)
448  {
449  if (! in_array($tmpquerytohave, $tmpqueryarraywehave)) $foundintru=1;
450  }
451  if (! $foundintru) $qualified=1;
452  //var_dump($defkey.'-'.$qualified);
453  }
454  else $qualified = 1;
455 
456  if ($qualified)
457  {
458  if (isset($_POST['sall']) || isset($_POST['search_all']) || isset($_GET['sall']) || isset($_GET['search_all']))
459  {
460  // We made a search from quick search menu, do we still use default filter ?
461  if (empty($conf->global->MAIN_DISABLE_DEFAULT_FILTER_FOR_QUICK_SEARCH))
462  {
463  $forbidden_chars_to_replace=array(" ","'","/","\\",":","*","?","\"","<",">","|","[","]",";","="); // we accept _, -, . and ,
464  $out = dol_string_nospecial($user->default_values[$relativepathstring]['filters'][$defkey][$paramname], '', $forbidden_chars_to_replace);
465  }
466  }
467  else
468  {
469  $forbidden_chars_to_replace=array(" ","'","/","\\",":","*","?","\"","<",">","|","[","]",";","="); // we accept _, -, . and ,
470  $out = dol_string_nospecial($user->default_values[$relativepathstring]['filters'][$defkey][$paramname], '', $forbidden_chars_to_replace);
471  }
472  break;
473  }
474  }
475  }
476  }
477  }
478  }
479  }
480  }
481 
482  // Substitution variables for GETPOST (used to get final url with variable parameters or final default value with variable paramaters)
483  // Example of variables: __DAY__, __MONTH__, __YEAR__, __MYCOMPANY_COUNTRY_ID__, __USER_ID__, ...
484  // We do this only if var is a GET. If it is a POST, may be we want to post the text with vars as the setup text.
485  if (! is_array($out) && empty($_POST[$paramname]) && empty($noreplace))
486  {
487  $maxloop=20; $loopnb=0; // Protection against infinite loop
488  while (preg_match('/__([A-Z0-9]+_?[A-Z0-9]+)__/i', $out, $reg) && ($loopnb < $maxloop)) // Detect '__ABCDEF__' as key 'ABCDEF' and '__ABC_DEF__' as key 'ABC_DEF'. Detection is also correct when 2 vars are side by side.
489  {
490  $loopnb++; $newout = '';
491 
492  if ($reg[1] == 'DAY') { $tmp=dol_getdate(dol_now(), true); $newout = $tmp['mday']; }
493  elseif ($reg[1] == 'MONTH') { $tmp=dol_getdate(dol_now(), true); $newout = $tmp['mon']; }
494  elseif ($reg[1] == 'YEAR') { $tmp=dol_getdate(dol_now(), true); $newout = $tmp['year']; }
495  elseif ($reg[1] == 'PREVIOUS_DAY') { $tmp=dol_getdate(dol_now(), true); $tmp2=dol_get_prev_day($tmp['mday'], $tmp['mon'], $tmp['year']); $newout = $tmp2['day']; }
496  elseif ($reg[1] == 'PREVIOUS_MONTH') { $tmp=dol_getdate(dol_now(), true); $tmp2=dol_get_prev_month($tmp['mon'], $tmp['year']); $newout = $tmp2['month']; }
497  elseif ($reg[1] == 'PREVIOUS_YEAR') { $tmp=dol_getdate(dol_now(), true); $newout = ($tmp['year'] - 1); }
498  elseif ($reg[1] == 'NEXT_DAY') { $tmp=dol_getdate(dol_now(), true); $tmp2=dol_get_next_day($tmp['mday'], $tmp['mon'], $tmp['year']); $newout = $tmp2['day']; }
499  elseif ($reg[1] == 'NEXT_MONTH') { $tmp=dol_getdate(dol_now(), true); $tmp2=dol_get_next_month($tmp['mon'], $tmp['year']); $newout = $tmp2['month']; }
500  elseif ($reg[1] == 'NEXT_YEAR') { $tmp=dol_getdate(dol_now(), true); $newout = ($tmp['year'] + 1); }
501  elseif ($reg[1] == 'MYCOMPANY_COUNTRY_ID' || $reg[1] == 'MYCOUNTRY_ID' || $reg[1] == 'MYCOUNTRYID')
502  {
503  $newout = $mysoc->country_id;
504  }
505  elseif ($reg[1] == 'USER_ID' || $reg[1] == 'USERID')
506  {
507  $newout = $user->id;
508  }
509  elseif ($reg[1] == 'USER_SUPERVISOR_ID' || $reg[1] == 'SUPERVISOR_ID' || $reg[1] == 'SUPERVISORID')
510  {
511  $newout = $user->fk_user;
512  }
513  elseif ($reg[1] == 'ENTITY_ID' || $reg[1] == 'ENTITYID')
514  {
515  $newout = $conf->entity;
516  }
517  else $newout = ''; // Key not found, we replace with empty string
518  //var_dump('__'.$reg[1].'__ -> '.$newout);
519  $out = preg_replace('/__'.preg_quote($reg[1],'/').'__/', $newout, $out);
520  }
521  }
522 
523  // Check is done after replacement
524  switch ($check)
525  {
526  case 'none':
527  break;
528  case 'int': // Check param is a numeric value (integer but also float or hexadecimal)
529  if (! is_numeric($out)) { $out=''; }
530  break;
531  case 'intcomma':
532  if (preg_match('/[^0-9,-]+/i',$out)) $out='';
533  break;
534  case 'alpha':
535  if (! is_array($out))
536  {
537  $out=trim($out);
538  // '"' is dangerous because param in url can close the href= or src= and add javascript functions.
539  // '../' is dangerous because it allows dir transversals
540  if (preg_match('/"/',$out)) $out='';
541  else if (preg_match('/\.\.\//',$out)) $out='';
542  }
543  break;
544  case 'san_alpha':
545  $out=filter_var($out,FILTER_SANITIZE_STRING);
546  break;
547  case 'aZ':
548  if (! is_array($out))
549  {
550  $out=trim($out);
551  if (preg_match('/[^a-z]+/i',$out)) $out='';
552  }
553  break;
554  case 'aZ09':
555  if (! is_array($out))
556  {
557  $out=trim($out);
558  if (preg_match('/[^a-z0-9_\-\.]+/i',$out)) $out='';
559  }
560  break;
561  case 'aZ09comma': // great to sanitize sortfield or sortorder params that can be t.abc,t.def_gh
562  if (! is_array($out))
563  {
564  $out=trim($out);
565  if (preg_match('/[^a-z0-9_\-\.,]+/i',$out)) $out='';
566  }
567  break;
568  case 'array':
569  if (! is_array($out) || empty($out)) $out=array();
570  break;
571  case 'nohtml': // Recommended for most scalar parameters
572  $out=dol_string_nohtmltag($out, 0);
573  break;
574  case 'alphanohtml': // Recommended for search parameters
575  if (! is_array($out))
576  {
577  $out=trim($out);
578  // '"' is dangerous because param in url can close the href= or src= and add javascript functions.
579  // '../' is dangerous because it allows dir transversals
580  if (preg_match('/"/',$out)) $out='';
581  else if (preg_match('/\.\.\//',$out)) $out='';
582  $out=dol_string_nohtmltag($out);
583  }
584  break;
585  case 'custom':
586  if (empty($filter)) return 'BadFourthParameterForGETPOST';
587  $out=filter_var($out, $filter, $options);
588  break;
589  }
590 
591  // Code for search criteria persistence.
592  // Save data into session if key start with 'search_' or is 'smonth', 'syear', 'month', 'year'
593  if (empty($method) || $method == 3 || $method == 4)
594  {
595  if (preg_match('/^search_/', $paramname) || in_array($paramname, array('sortorder','sortfield')))
596  {
597  //var_dump($paramname.' - '.$out.' '.$user->default_values[$relativepathstring]['filters'][$paramname]);
598 
599  // We save search key only if $out not empty that means:
600  // - posted value not empty, or
601  // - if posted value is empty and a default value exists that is not empty (it means we did a filter to an empty value when default was not).
602 
603  if ($out != '') // $out = '0' or 'abc', it is a search criteria to keep
604  {
605  $user->lastsearch_values_tmp[$relativepathstring][$paramname]=$out;
606  }
607  }
608  }
609 
610  return $out;
611 }
612 
613 
614 if (! function_exists('dol_getprefix'))
615 {
625  function dol_getprefix($mode='')
626  {
627  global $conf;
628 
629  // If prefix is for email
630  if ($mode == 'email')
631  {
632  if (! empty($conf->global->MAIL_PREFIX_FOR_EMAIL_ID)) // If MAIL_PREFIX_FOR_EMAIL_ID is set (a value initialized with a random value is recommended)
633  {
634  if ($conf->global->MAIL_PREFIX_FOR_EMAIL_ID != 'SERVER_NAME') return $conf->global->MAIL_PREFIX_FOR_EMAIL_ID;
635  else if (isset($_SERVER["SERVER_NAME"])) return $_SERVER["SERVER_NAME"];
636  }
637  return dol_hash(DOL_DOCUMENT_ROOT.DOL_URL_ROOT, '3');
638  }
639 
640  if (isset($_SERVER["SERVER_NAME"]) && isset($_SERVER["DOCUMENT_ROOT"]))
641  {
642  return dol_hash($_SERVER["SERVER_NAME"].$_SERVER["DOCUMENT_ROOT"].DOL_DOCUMENT_ROOT.DOL_URL_ROOT, '3');
643  // Use this for a "readable" key
644  //return dol_sanitizeFileName($_SERVER["SERVER_NAME"].$_SERVER["DOCUMENT_ROOT"].DOL_DOCUMENT_ROOT.DOL_URL_ROOT);
645  }
646  return dol_hash(DOL_DOCUMENT_ROOT.DOL_URL_ROOT, '3');
647  }
648 }
649 
660 function dol_include_once($relpath, $classname='')
661 {
662  global $conf,$langs,$user,$mysoc; // Do not remove this. They must be defined for files we include. Other globals var must be retreived with $GLOBALS['var']
663 
664  $fullpath = dol_buildpath($relpath);
665 
666  if (!file_exists($fullpath)) {
667  dol_syslog('functions::dol_include_once Tried to load unexisting file: '.$relpath, LOG_ERR);
668  return false;
669  }
670 
671  if (! empty($classname) && ! class_exists($classname)) {
672  return include $fullpath;
673  } else {
674  return include_once $fullpath;
675  }
676 }
677 
678 
689 function dol_buildpath($path, $type=0, $returnemptyifnotfound=0)
690 {
691  global $conf;
692 
693  $path=preg_replace('/^\//','',$path);
694 
695  if (empty($type)) // For a filesystem path
696  {
697  $res = DOL_DOCUMENT_ROOT.'/'.$path; // Standard default path
698  foreach ($conf->file->dol_document_root as $key => $dirroot) // ex: array(["main"]=>"/home/main/htdocs", ["alt0"]=>"/home/dirmod/htdocs", ...)
699  {
700  if ($key == 'main')
701  {
702  continue;
703  }
704  if (file_exists($dirroot.'/'.$path))
705  {
706  $res=$dirroot.'/'.$path;
707  return $res;
708  }
709  }
710  if ($returnemptyifnotfound) // Not found into alternate dir
711  {
712  if ($returnemptyifnotfound == 1 || ! file_exists($res)) return '';
713  }
714  }
715  else // For an url path
716  {
717  // We try to get local path of file on filesystem from url
718  // Note that trying to know if a file on disk exist by forging path on disk from url
719  // works only for some web server and some setup. This is bugged when
720  // using proxy, rewriting, virtual path, etc...
721  $res='';
722  if ($type == 1) $res = DOL_URL_ROOT.'/'.$path; // Standard value
723  if ($type == 2) $res = DOL_MAIN_URL_ROOT.'/'.$path; // Standard value
724  if ($type == 3) $res = DOL_URL_ROOT.'/'.$path;
725 
726  foreach ($conf->file->dol_document_root as $key => $dirroot) // ex: array(["main"]=>"/home/main/htdocs", ["alt0"]=>"/home/dirmod/htdocs", ...)
727  {
728  if ($key == 'main')
729  {
730  if ($type == 3)
731  {
732  global $dolibarr_main_url_root;
733 
734  // Define $urlwithroot
735  $urlwithouturlroot=preg_replace('/'.preg_quote(DOL_URL_ROOT,'/').'$/i','',trim($dolibarr_main_url_root));
736  $urlwithroot=$urlwithouturlroot.DOL_URL_ROOT; // This is to use external domain name found into config file
737  //$urlwithroot=DOL_MAIN_URL_ROOT; // This is to use same domain name than current
738 
739  $res=(preg_match('/^http/i',$conf->file->dol_url_root[$key])?'':$urlwithroot).'/'.$path; // Test on start with http is for old conf syntax
740  }
741  continue;
742  }
743  preg_match('/^([^\?]+(\.css\.php|\.css|\.js\.php|\.js|\.png|\.jpg|\.php)?)/i',$path,$regs); // Take part before '?'
744  if (! empty($regs[1]))
745  {
746  //print $key.'-'.$dirroot.'/'.$path.'-'.$conf->file->dol_url_root[$type].'<br>'."\n";
747  if (file_exists($dirroot.'/'.$regs[1]))
748  {
749  if ($type == 1)
750  {
751  $res=(preg_match('/^http/i',$conf->file->dol_url_root[$key])?'':DOL_URL_ROOT).$conf->file->dol_url_root[$key].'/'.$path;
752  }
753  if ($type == 2)
754  {
755  $res=(preg_match('/^http/i',$conf->file->dol_url_root[$key])?'':DOL_MAIN_URL_ROOT).$conf->file->dol_url_root[$key].'/'.$path;
756  }
757  if ($type == 3)
758  {
759  global $dolibarr_main_url_root;
760 
761  // Define $urlwithroot
762  $urlwithouturlroot=preg_replace('/'.preg_quote(DOL_URL_ROOT,'/').'$/i','',trim($dolibarr_main_url_root));
763  $urlwithroot=$urlwithouturlroot.DOL_URL_ROOT; // This is to use external domain name found into config file
764  //$urlwithroot=DOL_MAIN_URL_ROOT; // This is to use same domain name than current
765 
766  $res=(preg_match('/^http/i',$conf->file->dol_url_root[$key])?'':$urlwithroot).$conf->file->dol_url_root[$key].'/'.$path; // Test on start with http is for old conf syntax
767  }
768  break;
769  }
770  }
771  }
772  }
773 
774  return $res;
775 }
776 
787 function dol_clone($object, $native=0)
788 {
789  //dol_syslog(__FUNCTION__ . " is deprecated", LOG_WARNING);
790 
791  if (empty($native))
792  {
793  $myclone=unserialize(serialize($object));
794  }
795  else
796  {
797  $myclone = clone $object; // PHP clone is a shallow copy only, not a real clone, so properties of references will keep references (refer to the same target/variable)
798  }
799 
800  return $myclone;
801 }
802 
812 function dol_size($size,$type='')
813 {
814  global $conf;
815  if (empty($conf->dol_optimize_smallscreen)) return $size;
816  if ($type == 'width' && $size > 250) return 250;
817  else return 10;
818 }
819 
820 
831 function dol_sanitizeFileName($str,$newstr='_',$unaccent=1)
832 {
833  $filesystem_forbidden_chars = array('<','>','/','\\','?','*','|','"','°');
834  return dol_string_nospecial($unaccent?dol_string_unaccent($str):$str, $newstr, $filesystem_forbidden_chars);
835 }
836 
847 function dol_sanitizePathName($str,$newstr='_',$unaccent=1)
848 {
849  $filesystem_forbidden_chars = array('<','>','?','*','|','"','°');
850  return dol_string_nospecial($unaccent?dol_string_unaccent($str):$str, $newstr, $filesystem_forbidden_chars);
851 }
852 
861 function dol_string_unaccent($str)
862 {
863  if (utf8_check($str))
864  {
865  // See http://www.utf8-chartable.de/
866  $string = rawurlencode($str);
867  $replacements = array(
868  '%C3%80' => 'A','%C3%81' => 'A','%C3%82' => 'A','%C3%83' => 'A','%C3%84' => 'A','%C3%85' => 'A',
869  '%C3%88' => 'E','%C3%89' => 'E','%C3%8A' => 'E','%C3%8B' => 'E',
870  '%C3%8C' => 'I','%C3%8D' => 'I','%C3%8E' => 'I','%C3%8F' => 'I',
871  '%C3%92' => 'O','%C3%93' => 'O','%C3%94' => 'O','%C3%95' => 'O','%C3%96' => 'O',
872  '%C3%99' => 'U','%C3%9A' => 'U','%C3%9B' => 'U','%C3%9C' => 'U',
873  '%C3%A0' => 'a','%C3%A1' => 'a','%C3%A2' => 'a','%C3%A3' => 'a','%C3%A4' => 'a','%C3%A5' => 'a',
874  '%C3%A7' => 'c',
875  '%C3%A8' => 'e','%C3%A9' => 'e','%C3%AA' => 'e','%C3%AB' => 'e',
876  '%C3%AC' => 'i','%C3%AD' => 'i','%C3%AE' => 'i','%C3%AF' => 'i',
877  '%C3%B1' => 'n',
878  '%C3%B2' => 'o','%C3%B3' => 'o','%C3%B4' => 'o','%C3%B5' => 'o','%C3%B6' => 'o',
879  '%C3%B9' => 'u','%C3%BA' => 'u','%C3%BB' => 'u','%C3%BC' => 'u',
880  '%C3%BF' => 'y'
881  );
882  $string=strtr($string, $replacements);
883  return rawurldecode($string);
884  }
885  else
886  {
887  // See http://www.ascii-code.com/
888  $string = strtr(
889  $str,
890  "\xC0\xC1\xC2\xC3\xC4\xC5\xC7
891  \xC8\xC9\xCA\xCB\xCC\xCD\xCE\xCF\xD0\xD1
892  \xD2\xD3\xD4\xD5\xD8\xD9\xDA\xDB\xDD
893  \xE0\xE1\xE2\xE3\xE4\xE5\xE7\xE8\xE9\xEA\xEB
894  \xEC\xED\xEE\xEF\xF0\xF1\xF2\xF3\xF4\xF5\xF8
895  \xF9\xFA\xFB\xFC\xFD\xFF",
896  "AAAAAAC
897  EEEEIIIIDN
898  OOOOOUUUY
899  aaaaaaceeee
900  iiiidnooooo
901  uuuuyy"
902  );
903  $string = strtr($string, array("\xC4"=>"Ae", "\xC6"=>"AE", "\xD6"=>"Oe", "\xDC"=>"Ue", "\xDE"=>"TH", "\xDF"=>"ss", "\xE4"=>"ae", "\xE6"=>"ae", "\xF6"=>"oe", "\xFC"=>"ue", "\xFE"=>"th"));
904  return $string;
905  }
906 }
907 
919 function dol_string_nospecial($str,$newstr='_',$badcharstoreplace='')
920 {
921  $forbidden_chars_to_replace=array(" ", "'", "/", "\\", ":", "*", "?", "\"", "<", ">", "|", "[", "]", ",", ";", "=", '°'); // more complete than dol_sanitizeFileName
922  $forbidden_chars_to_remove=array();
923  if (is_array($badcharstoreplace)) $forbidden_chars_to_replace=$badcharstoreplace;
924  //$forbidden_chars_to_remove=array("(",")");
925 
926  return str_replace($forbidden_chars_to_replace,$newstr,str_replace($forbidden_chars_to_remove,"",$str));
927 }
928 
929 
936 function dolEscapeXML($string)
937 {
938  return strtr($string, array('\''=>'&apos;','"'=>'&quot;','&'=>'&amp;','<'=>'&lt;','>'=>'&gt;'));
939 }
940 
949 function dol_escape_js($stringtoescape, $mode=0, $noescapebackslashn=0)
950 {
951  // escape quotes and backslashes, newlines, etc.
952  $substitjs=array("&#039;"=>"\\'","\r"=>'\\r');
953  //$substitjs['</']='<\/'; // We removed this. Should be useless.
954  if (empty($noescapebackslashn)) { $substitjs["\n"]='\\n'; $substitjs['\\']='\\\\'; }
955  if (empty($mode)) { $substitjs["'"]="\\'"; $substitjs['"']="\\'"; }
956  else if ($mode == 1) $substitjs["'"]="\\'";
957  else if ($mode == 2) { $substitjs['"']='\\"'; }
958  else if ($mode == 3) { $substitjs["'"]="\\'"; $substitjs['"']="\\\""; }
959  return strtr($stringtoescape, $substitjs);
960 }
961 
962 
972 function dol_escape_htmltag($stringtoescape, $keepb=0, $keepn=0)
973 {
974  // escape quotes and backslashes, newlines, etc.
975  $tmp=html_entity_decode($stringtoescape, ENT_COMPAT, 'UTF-8'); // TODO Use htmlspecialchars_decode instead, that make only required change for html tags
976  if (! $keepb) $tmp=strtr($tmp, array("<b>"=>'','</b>'=>''));
977  if (! $keepn) $tmp=strtr($tmp, array("\r"=>'\\r',"\n"=>'\\n'));
978  return htmlentities($tmp, ENT_COMPAT, 'UTF-8'); // TODO Use htmlspecialchars instead, that make only required change for html tags
979 }
980 
981 
988 function dol_strtolower($utf8_string)
989 {
990  return mb_strtolower($utf8_string, "UTF-8");
991 }
992 
999 function dol_strtoupper($utf8_string)
1000 {
1001  return mb_strtoupper($utf8_string, "UTF-8");
1002 }
1003 
1004 
1025 function dol_syslog($message, $level = LOG_INFO, $ident = 0, $suffixinfilename='', $restricttologhandler='')
1026 {
1027  global $conf, $user;
1028 
1029  // If syslog module enabled
1030  if (empty($conf->syslog->enabled)) return;
1031 
1032  if ($ident < 0)
1033  {
1034  foreach ($conf->loghandlers as $loghandlerinstance)
1035  {
1036  $loghandlerinstance->setIdent($ident);
1037  }
1038  }
1039 
1040  if (! empty($message))
1041  {
1042  // Test log level
1043  $logLevels = array(LOG_EMERG, LOG_ALERT, LOG_CRIT, LOG_ERR, LOG_WARNING, LOG_NOTICE, LOG_INFO, LOG_DEBUG);
1044  if (!in_array($level, $logLevels, true))
1045  {
1046  throw new Exception('Incorrect log level');
1047  }
1048  if ($level > $conf->global->SYSLOG_LEVEL) return;
1049 
1050  $message = preg_replace('/password=\'[^\']*\'/', 'password=\'hidden\'', $message); // protection to avoid to have value of password in log
1051 
1052  // If adding log inside HTML page is required
1053  if (! empty($_REQUEST['logtohtml']) && (! empty($conf->global->MAIN_ENABLE_LOG_TO_HTML) || ! empty($conf->global->MAIN_LOGTOHTML))) // MAIN_LOGTOHTML kept for backward compatibility
1054  {
1055  $conf->logbuffer[] = dol_print_date(time(),"%Y-%m-%d %H:%M:%S")." ".$message;
1056  }
1057 
1058  //TODO: Remove this. MAIN_ENABLE_LOG_INLINE_HTML should be deprecated and use a log handler dedicated to HTML output
1059  // If html log tag enabled and url parameter log defined, we show output log on HTML comments
1060  if (! empty($conf->global->MAIN_ENABLE_LOG_INLINE_HTML) && ! empty($_GET["log"]))
1061  {
1062  print "\n\n<!-- Log start\n";
1063  print $message."\n";
1064  print "Log end -->\n";
1065  }
1066 
1067  $data = array(
1068  'message' => $message,
1069  'script' => (isset($_SERVER['PHP_SELF'])? basename($_SERVER['PHP_SELF'],'.php') : false),
1070  'level' => $level,
1071  'user' => ((is_object($user) && $user->id) ? $user->login : false),
1072  'ip' => false
1073  );
1074 
1075  // This is when server run behind a reverse proxy
1076  if (!empty($_SERVER['HTTP_X_FORWARDED_FOR'])) $data['ip'] = $_SERVER['HTTP_X_FORWARDED_FOR'].(empty($_SERVER["REMOTE_ADDR"])?'':'->'.$_SERVER['REMOTE_ADDR']);
1077  // This is when server run normally on a server
1078  else if (! empty($_SERVER["REMOTE_ADDR"])) $data['ip'] = $_SERVER['REMOTE_ADDR'];
1079  // This is when PHP session is ran inside a web server but not inside a client request (example: init code of apache)
1080  else if (! empty($_SERVER['SERVER_ADDR'])) $data['ip'] = $_SERVER['SERVER_ADDR'];
1081  // This is when PHP session is ran outside a web server, like from Windows command line (Not always defined, but useful if OS defined it).
1082  else if (! empty($_SERVER['COMPUTERNAME'])) $data['ip'] = $_SERVER['COMPUTERNAME'].(empty($_SERVER['USERNAME'])?'':'@'.$_SERVER['USERNAME']);
1083  // This is when PHP session is ran outside a web server, like from Linux command line (Not always defined, but usefull if OS defined it).
1084  else if (! empty($_SERVER['LOGNAME'])) $data['ip'] = '???@'.$_SERVER['LOGNAME'];
1085  // Loop on each log handler and send output
1086  foreach ($conf->loghandlers as $loghandlerinstance)
1087  {
1088  if ($restricttologhandler && $loghandlerinstance->code != $restricttologhandler) continue;
1089  $loghandlerinstance->export($data,$suffixinfilename);
1090  }
1091  unset($data);
1092  }
1093 
1094  if ($ident > 0)
1095  {
1096  foreach ($conf->loghandlers as $loghandlerinstance)
1097  {
1098  $loghandlerinstance->setIdent($ident);
1099  }
1100  }
1101 }
1102 
1103 
1117 function dol_fiche_head($links=array(), $active='0', $title='', $notab=0, $picto='', $pictoisfullpath=0, $morehtmlright='', $morecss='')
1118 {
1119  print dol_get_fiche_head($links, $active, $title, $notab, $picto, $pictoisfullpath, $morehtmlright, $morecss);
1120 }
1121 
1135 function dol_get_fiche_head($links=array(), $active='', $title='', $notab=0, $picto='', $pictoisfullpath=0, $morehtmlright='', $morecss='')
1136 {
1137  global $conf, $langs, $hookmanager;
1138 
1139  $out="\n".'<div class="tabs" data-role="controlgroup" data-type="horizontal">'."\n";
1140 
1141  if ($morehtmlright) $out.='<div class="inline-block floatright tabsElem">'.$morehtmlright.'</div>'; // Output right area first so when space is missing, text is in front of tabs and not under.
1142 
1143  // Show title
1144  $showtitle=1;
1145  if (! empty($conf->dol_optimize_smallscreen)) $showtitle=0;
1146  if (! empty($title) && $showtitle)
1147  {
1148  $limittitle=30;
1149  $out.='<a class="tabTitle">';
1150  if ($picto) $out.=img_picto($title,($pictoisfullpath?'':'object_').$picto,'',$pictoisfullpath).' ';
1151  $out.='<span class="tabTitleText">'.dol_trunc($title,$limittitle).'</span>';
1152  $out.='</a>';
1153  }
1154 
1155  // Define max of key (max may be higher than sizeof because of hole due to module disabling some tabs).
1156  $maxkey=-1;
1157  if (is_array($links) && ! empty($links))
1158  {
1159  $keys=array_keys($links);
1160  if (count($keys)) $maxkey=max($keys);
1161  }
1162 
1163  if (! empty($conf->dol_optimize_smallscreen)) $conf->global->MAIN_MAXTABS_IN_CARD=2;
1164 
1165  // Show tabs
1166  $bactive=false;
1167  // if =0 we don't use the feature
1168  $limittoshow=(empty($conf->global->MAIN_MAXTABS_IN_CARD)?99:$conf->global->MAIN_MAXTABS_IN_CARD);
1169  $displaytab=0;
1170  $nbintab=0;
1171  $popuptab=0; $outmore='';
1172  for ($i = 0 ; $i <= $maxkey ; $i++)
1173  {
1174  if ((is_numeric($active) && $i == $active) || (! empty($links[$i][2]) && ! is_numeric($active) && $active == $links[$i][2]))
1175  {
1176  // If active tab is already present
1177  if ($i >= $limittoshow) $limittoshow--;
1178  }
1179  }
1180 
1181  for ($i = 0 ; $i <= $maxkey ; $i++)
1182  {
1183  if ((is_numeric($active) && $i == $active) || (! empty($links[$i][2]) && ! is_numeric($active) && $active == $links[$i][2]))
1184  {
1185  $isactive=true;
1186  $bactive=true;
1187  }
1188  else
1189  {
1190  $isactive=false;
1191  }
1192 
1193  if ($i < $limittoshow || $isactive)
1194  {
1195  $out.='<div class="inline-block tabsElem'.($isactive ? ' tabsElemActive' : '').((! $isactive && ! empty($conf->global->MAIN_HIDE_INACTIVETAB_ON_PRINT))?' hideonprint':'').'"><!-- id tab = '.(empty($links[$i][2])?'':$links[$i][2]).' -->';
1196  if (isset($links[$i][2]) && $links[$i][2] == 'image')
1197  {
1198  if (!empty($links[$i][0]))
1199  {
1200  $out.='<a class="tabimage'.($morecss?' '.$morecss:'').'" href="'.$links[$i][0].'">'.$links[$i][1].'</a>'."\n";
1201  }
1202  else
1203  {
1204  $out.='<span class="tabspan">'.$links[$i][1].'</span>'."\n";
1205  }
1206  }
1207  else if (! empty($links[$i][1]))
1208  {
1209  //print "x $i $active ".$links[$i][2]." z";
1210  if ($isactive)
1211  {
1212  $out.='<a'.(! empty($links[$i][2])?' id="'.$links[$i][2].'"':'').' class="tabactive tab inline-block'.($morecss?' '.$morecss:'').'" href="'.$links[$i][0].'">';
1213  $out.=$links[$i][1];
1214  $out.='</a>'."\n";
1215  }
1216  else
1217  {
1218  $out.='<a'.(! empty($links[$i][2])?' id="'.$links[$i][2].'"':'').' class="tabunactive tab inline-block'.($morecss?' '.$morecss:'').'" href="'.$links[$i][0].'">';
1219  $out.=$links[$i][1];
1220  $out.='</a>'."\n";
1221  }
1222  }
1223  $out.='</div>';
1224  }
1225  else
1226  {
1227  // The popup with the other tabs
1228  if (! $popuptab)
1229  {
1230  $popuptab=1;
1231  $outmore.='<div class="popuptabset wordwrap">'; // The css used to hide/show popup
1232  }
1233  $outmore.='<div class="popuptab wordwrap" style="display:inherit;">';
1234  if (isset($links[$i][2]) && $links[$i][2] == 'image')
1235  {
1236  if (!empty($links[$i][0]))
1237  $outmore.='<a class="tabimage'.($morecss?' '.$morecss:'').'" href="'.$links[$i][0].'">'.$links[$i][1].'</a>'."\n";
1238  else
1239  $outmore.='<span class="tabspan">'.$links[$i][1].'</span>'."\n";
1240  }
1241  else if (! empty($links[$i][1]))
1242  {
1243  $outmore.='<a'.(! empty($links[$i][2])?' id="'.$links[$i][2].'"':'').' class="wordwrap inline-block'.($morecss?' '.$morecss:'').'" href="'.$links[$i][0].'">';
1244  $outmore.=preg_replace('/([a-z])\/([a-z])/i', '\\1 / \\2', $links[$i][1]); // Replace x/y with x / y to allow wrap on long composed texts.
1245  $outmore.='</a>'."\n";
1246  }
1247  $outmore.='</div>';
1248 
1249  $nbintab++;
1250  }
1251  $displaytab=$i;
1252  }
1253  if ($popuptab) $outmore.='</div>';
1254 
1255  if ($displaytab > $limittoshow)
1256  {
1257  $left=($langs->trans("DIRECTION") == 'rtl'?'right':'left');
1258  $right=($langs->trans("DIRECTION") == 'rtl'?'left':'right');
1259 
1260  $tabsname=str_replace("@", "", $picto);
1261  $out.='<div id="moretabs'.$tabsname.'" class="inline-block tabsElem">';
1262  $out.='<a href="#" class="tab moretab inline-block tabunactive reposition">'.$langs->trans("More").'... ('.$nbintab.')</a>';
1263  $out.='<div id="moretabsList'.$tabsname.'" style="position: absolute; '.$left.': -999em; text-align: '.$left.'; margin:0px; padding:2px">';
1264  $out.=$outmore;
1265  $out.='</div>';
1266  $out.='<div></div>';
1267  $out.="</div>\n";
1268 
1269  $out.="<script>";
1270  $out.="$('#moretabs".$tabsname."').mouseenter( function() { console.log('mouseenter ".$left."'); $('#moretabsList".$tabsname."').css('".$left."','auto');});";
1271  $out.="$('#moretabs".$tabsname."').mouseleave( function() { console.log('mouseleave ".$left."'); $('#moretabsList".$tabsname."').css('".$left."','-999em');});";
1272  $out.="</script>";
1273  }
1274 
1275  $out.="</div>\n";
1276 
1277  if (! $notab || $notab == -1) $out.="\n".'<div class="tabBar'.($notab == -1 ? '' : ' tabBarWithBottom').'">'."\n";
1278 
1279  $parameters=array('tabname' => $active, 'out' => $out);
1280  $reshook=$hookmanager->executeHooks('printTabsHead',$parameters); // This hook usage is called just before output the head of tabs. Take also a look at "completeTabsHead"
1281  if ($reshook > 0)
1282  {
1283  $out = $hookmanager->resPrint;
1284  }
1285 
1286  return $out;
1287 }
1288 
1295 function dol_fiche_end($notab=0)
1296 {
1297  print dol_get_fiche_end($notab);
1298 }
1299 
1306 function dol_get_fiche_end($notab=0)
1307 {
1308  if (! $notab || $notab == -1) return "\n</div>\n";
1309  else return '';
1310 }
1311 
1331 function dol_banner_tab($object, $paramid, $morehtml='', $shownav=1, $fieldid='rowid', $fieldref='ref', $morehtmlref='', $moreparam='', $nodbprefix=0, $morehtmlleft='', $morehtmlstatus='', $onlybanner=0, $morehtmlright='')
1332 {
1333  global $conf, $form, $user, $langs;
1334 
1335  $error = 0;
1336 
1337  $maxvisiblephotos=1;
1338  $showimage=1;
1339  $entity=(empty($object->entity)?$conf->entity:$object->entity);
1340  $showbarcode=empty($conf->barcode->enabled)?0:($object->barcode?1:0);
1341  if (! empty($conf->global->MAIN_USE_ADVANCED_PERMS) && empty($user->rights->barcode->lire_advance)) $showbarcode=0;
1342  $modulepart='unknown';
1343 
1344  if ($object->element == 'societe') $modulepart='societe';
1345  if ($object->element == 'contact') $modulepart='contact';
1346  if ($object->element == 'member') $modulepart='memberphoto';
1347  if ($object->element == 'user') $modulepart='userphoto';
1348  if ($object->element == 'product') $modulepart='product';
1349 
1350  if (class_exists("Imagick"))
1351  {
1352  if ($object->element == 'propal') $modulepart='propal';
1353  if ($object->element == 'commande') $modulepart='commande';
1354  if ($object->element == 'facture') $modulepart='facture';
1355  if ($object->element == 'fichinter') $modulepart='ficheinter';
1356  if ($object->element == 'contrat') $modulepart='contract';
1357  if ($object->element == 'supplier_proposal') $modulepart='supplier_proposal';
1358  if ($object->element == 'order_supplier') $modulepart='supplier_order';
1359  if ($object->element == 'invoice_supplier') $modulepart='supplier_invoice';
1360  if ($object->element == 'expensereport') $modulepart='expensereport';
1361  }
1362 
1363  if ($object->element == 'product')
1364  {
1365  $width=80; $cssclass='photoref';
1366  $showimage=$object->is_photo_available($conf->product->multidir_output[$entity]);
1367  $maxvisiblephotos=(isset($conf->global->PRODUCT_MAX_VISIBLE_PHOTO)?$conf->global->PRODUCT_MAX_VISIBLE_PHOTO:5);
1368  if ($conf->browser->layout == 'phone') $maxvisiblephotos=1;
1369  if ($showimage) $morehtmlleft.='<div class="floatleft inline-block valignmiddle divphotoref">'.$object->show_photos('product', $conf->product->multidir_output[$entity],'small',$maxvisiblephotos,0,0,0,$width,0).'</div>';
1370  else
1371  {
1372  if (!empty($conf->global->PRODUCT_NODISPLAYIFNOPHOTO)) {
1373  $nophoto='';
1374  $morehtmlleft.='<div class="floatleft inline-block valignmiddle divphotoref"></div>';
1375  }
1376  //elseif ($conf->browser->layout != 'phone') { // Show no photo link
1377  $nophoto='/public/theme/common/nophoto.png';
1378  $morehtmlleft.='<div class="floatleft inline-block valignmiddle divphotoref"><img class="photo'.$modulepart.($cssclass?' '.$cssclass:'').'" alt="No photo" border="0"'.($width?' width="'.$width.'"':'').' src="'.DOL_URL_ROOT.$nophoto.'"></div>';
1379  //}
1380  }
1381  }
1382  elseif ($object->element == 'ticket')
1383  {
1384  $width=80; $cssclass='photoref';
1385  $showimage=$object->is_photo_available($conf->ticket->multidir_output[$entity].'/'.$object->track_id);
1386  $maxvisiblephotos=(isset($conf->global->TICKETSUP_MAX_VISIBLE_PHOTO)?$conf->global->TICKETSUP_MAX_VISIBLE_PHOTO:2);
1387  if ($conf->browser->layout == 'phone') $maxvisiblephotos=1;
1388  if ($showimage) $morehtmlleft.='<div class="floatleft inline-block valignmiddle divphotoref">'.$object->show_photos('ticket', $conf->ticket->multidir_output[$entity],'small',$maxvisiblephotos,0,0,0,$width,0).'</div>';
1389  else
1390  {
1391  if (!empty($conf->global->TICKETSUP_NODISPLAYIFNOPHOTO)) {
1392  $nophoto='';
1393  $morehtmlleft.='<div class="floatleft inline-block valignmiddle divphotoref"></div>';
1394  }
1395  //elseif ($conf->browser->layout != 'phone') { // Show no photo link
1396  $nophoto='/public/theme/common/nophoto.png';
1397  $morehtmlleft.='<div class="floatleft inline-block valignmiddle divphotoref"><img class="photo'.$modulepart.($cssclass?' '.$cssclass:'').'" alt="No photo" border="0"'.($width?' width="'.$width.'"':'').' src="'.DOL_URL_ROOT.$nophoto.'"></div>';
1398  //}
1399  }
1400  }
1401  else
1402  {
1403  if ($showimage)
1404  {
1405  if ($modulepart != 'unknown')
1406  {
1407  $phototoshow='';
1408  // Check if a preview file is available
1409  if (in_array($modulepart, array('propal', 'commande', 'facture', 'ficheinter', 'contract', 'supplier_order', 'supplier_proposal', 'supplier_invoice', 'expensereport')) && class_exists("Imagick"))
1410  {
1411  $objectref = dol_sanitizeFileName($object->ref);
1412  $dir_output = (empty($conf->$modulepart->multidir_output[$entity]) ? $conf->$modulepart->dir_output : $conf->$modulepart->multidir_output[$entity]) . "/";
1413  if (in_array($modulepart, array('invoice_supplier', 'supplier_invoice')))
1414  {
1415  $subdir = get_exdir($object->id, 2, 0, 1, $object, $modulepart);
1416  $subdir.= ((! empty($subdir) && ! preg_match('/\/$/',$subdir))?'/':'').$objectref; // the objectref dir is not included into get_exdir when used with level=2, so we add it at end
1417  }
1418  else
1419  {
1420  $subdir = get_exdir($object->id, 0, 0, 1, $object, $modulepart);
1421  }
1422  if (empty($subdir)) $subdir = 'errorgettingsubdirofobject'; // Protection to avoid to return empty path
1423 
1424  $filepath = $dir_output . $subdir . "/";
1425 
1426  $file = $filepath . $objectref . ".pdf";
1427  $relativepath = $subdir.'/'.$objectref.'.pdf';
1428 
1429  // Define path to preview pdf file (preview precompiled "file.ext" are "file.ext_preview.png")
1430  $fileimage = $file.'_preview.png'; // If PDF has 1 page
1431  $fileimagebis = $file.'_preview-0.png'; // If PDF has more than one page
1432  $relativepathimage = $relativepath.'_preview.png';
1433 
1434  // Si fichier PDF existe
1435  if (file_exists($file))
1436  {
1437  $encfile = urlencode($file);
1438  // Conversion du PDF en image png si fichier png non existant
1439  if ( (! file_exists($fileimage) || (filemtime($fileimage) < filemtime($file)))
1440  && (! file_exists($fileimagebis) || (filemtime($fileimagebis) < filemtime($file)))
1441  )
1442  {
1443  if (empty($conf->global->MAIN_DISABLE_PDF_THUMBS)) // If you experienc trouble with pdf thumb generation and imagick, you can disable here.
1444  {
1445  include_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
1446  $ret = dol_convert_file($file, 'png', $fileimage);
1447  if ($ret < 0) $error++;
1448  }
1449  }
1450 
1451  $heightforphotref=70;
1452  if (! empty($conf->dol_optimize_smallscreen)) $heightforphotref=60;
1453  // Si fichier png PDF d'1 page trouve
1454  if (file_exists($fileimage))
1455  {
1456  $phototoshow = '<div class="floatleft inline-block valignmiddle divphotoref"><div class="photoref">';
1457  $phototoshow.= '<img height="'.$heightforphotref.'" class="photo photowithmargin photowithborder" src="'.DOL_URL_ROOT . '/viewimage.php?modulepart=apercu'.$modulepart.'&amp;file='.urlencode($relativepathimage).'">';
1458  $phototoshow.= '</div></div>';
1459  }
1460  // Si fichier png PDF de plus d'1 page trouve
1461  elseif (file_exists($fileimagebis))
1462  {
1463  $preview = preg_replace('/\.png/','',$relativepathimage) . "-0.png";
1464  $phototoshow = '<div class="floatleft inline-block valignmiddle divphotoref"><div class="photoref">';
1465  $phototoshow.= '<img height="'.$heightforphotref.'" class="photo photowithmargin photowithborder" src="'.DOL_URL_ROOT . '/viewimage.php?modulepart=apercu'.$modulepart.'&amp;file='.urlencode($preview).'"><p>';
1466  $phototoshow.= '</div></div>';
1467  }
1468  }
1469  }
1470  else if (! $phototoshow)
1471  {
1472  $phototoshow = $form->showphoto($modulepart,$object,0,0,0,'photoref','small',1,0,$maxvisiblephotos);
1473  }
1474 
1475  if ($phototoshow)
1476  {
1477  $morehtmlleft.='<div class="floatleft inline-block valignmiddle divphotoref">';
1478  $morehtmlleft.=$phototoshow;
1479  $morehtmlleft.='</div>';
1480  }
1481  }
1482 
1483  if (! $phototoshow) // Show No photo link (picto of pbject)
1484  {
1485  $morehtmlleft.='<div class="floatleft inline-block valignmiddle divphotoref">';
1486  if ($object->element == 'action')
1487  {
1488  $width=80;
1489  $cssclass='photorefcenter';
1490  $nophoto=img_picto('', 'title_agenda', '', false, 1);
1491  }
1492  else
1493  {
1494  $width=14; $cssclass='photorefcenter';
1495  $picto = $object->picto;
1496  if ($object->element == 'project' && ! $object->public) $picto = 'project'; // instead of projectpub
1497  $nophoto=img_picto('', 'object_'.$picto, '', false, 1);
1498  }
1499  $morehtmlleft.='<!-- No photo to show -->';
1500  $morehtmlleft.='<div class="floatleft inline-block valignmiddle divphotoref"><div class="photoref"><img class="photo'.$modulepart.($cssclass?' '.$cssclass:'').'" alt="No photo" border="0"'.($width?' width="'.$width.'"':'').' src="'.$nophoto.'"></div></div>';
1501 
1502  $morehtmlleft.='</div>';
1503  }
1504  }
1505  }
1506 
1507  if ($showbarcode) $morehtmlleft.='<div class="floatleft inline-block valignmiddle divphotoref">'.$form->showbarcode($object).'</div>';
1508 
1509  if ($object->element == 'societe')
1510  {
1511  if (! empty($conf->use_javascript_ajax) && $user->rights->societe->creer && ! empty($conf->global->MAIN_DIRECT_STATUS_UPDATE))
1512  {
1513  $morehtmlstatus.=ajax_object_onoff($object, 'status', 'status', 'InActivity', 'ActivityCeased');
1514  }
1515  else {
1516  $morehtmlstatus.=$object->getLibStatut(6);
1517  }
1518  }
1519  elseif ($object->element == 'product')
1520  {
1521  //$morehtmlstatus.=$langs->trans("Status").' ('.$langs->trans("Sell").') ';
1522  if (! empty($conf->use_javascript_ajax) && $user->rights->produit->creer && ! empty($conf->global->MAIN_DIRECT_STATUS_UPDATE)) {
1523  $morehtmlstatus.=ajax_object_onoff($object, 'status', 'tosell', 'ProductStatusOnSell', 'ProductStatusNotOnSell');
1524  } else {
1525  $morehtmlstatus.='<span class="statusrefsell">'.$object->getLibStatut(5,0).'</span>';
1526  }
1527  $morehtmlstatus.=' &nbsp; ';
1528  //$morehtmlstatus.=$langs->trans("Status").' ('.$langs->trans("Buy").') ';
1529  if (! empty($conf->use_javascript_ajax) && $user->rights->produit->creer && ! empty($conf->global->MAIN_DIRECT_STATUS_UPDATE)) {
1530  $morehtmlstatus.=ajax_object_onoff($object, 'status_buy', 'tobuy', 'ProductStatusOnBuy', 'ProductStatusNotOnBuy');
1531  } else {
1532  $morehtmlstatus.='<span class="statusrefbuy">'.$object->getLibStatut(5,1).'</span>';
1533  }
1534  }
1535  elseif (in_array($object->element, array('facture', 'invoice', 'invoice_supplier', 'chargesociales', 'loan')))
1536  {
1537  $tmptxt=$object->getLibStatut(6, $object->totalpaye);
1538  if (empty($tmptxt) || $tmptxt == $object->getLibStatut(3) || $conf->browser->layout=='phone') $tmptxt=$object->getLibStatut(5, $object->totalpaye);
1539  $morehtmlstatus.=$tmptxt;
1540  }
1541  elseif ($object->element == 'contrat' || $object->element == 'contract')
1542  {
1543  if ($object->statut == 0) $morehtmlstatus.=$object->getLibStatut(5);
1544  else $morehtmlstatus.=$object->getLibStatut(4);
1545  }
1546  elseif ($object->element == 'facturerec')
1547  {
1548  if ($object->frequency == 0) $morehtmlstatus.=$object->getLibStatut(2);
1549  else $morehtmlstatus.=$object->getLibStatut(5);
1550  }
1551  elseif ($object->element == 'project_task')
1552  {
1553  $object->fk_statut = 1;
1554  if ($object->progress > 0) $object->fk_statut = 2;
1555  if ($object->progress >= 100) $object->fk_statut = 3;
1556  $tmptxt=$object->getLibStatut(5);
1557  $morehtmlstatus.=$tmptxt; // No status on task
1558  }
1559  else { // Generic case
1560  $tmptxt=$object->getLibStatut(6);
1561  if (empty($tmptxt) || $tmptxt == $object->getLibStatut(3) || $conf->browser->layout=='phone') $tmptxt=$object->getLibStatut(5);
1562  $morehtmlstatus.=$tmptxt;
1563  }
1564 
1565  // Add if object was dispatched "into accountancy"
1566  if (! empty($conf->accounting->enabled) && in_array($object->element, array('bank', 'facture', 'invoice', 'invoice_supplier', 'expensereport')))
1567  {
1568  if (method_exists($object, 'getVentilExportCompta'))
1569  {
1570  $accounted = $object->getVentilExportCompta();
1571  $langs->load("accountancy");
1572  $morehtmlstatus.='</div><div class="statusref statusrefbis">'.($accounted > 0 ? $langs->trans("Accounted") : $langs->trans("NotYetAccounted"));
1573  }
1574  }
1575 
1576  // Add alias for thirdparty
1577  if (! empty($object->name_alias)) $morehtmlref.='<div class="refidno">'.$object->name_alias.'</div>';
1578 
1579  // Add label
1580  if ($object->element == 'product' || $object->element == 'bank_account' || $object->element == 'project_task')
1581  {
1582  if (! empty($object->label)) $morehtmlref.='<div class="refidno">'.$object->label.'</div>';
1583  }
1584 
1585  if (method_exists($object, 'getBannerAddress') && $object->element != 'product' && $object->element != 'bookmark' && $object->element != 'ecm_directories' && $object->element != 'ecm_files')
1586  {
1587  $morehtmlref.='<div class="refidno">';
1588  $morehtmlref.=$object->getBannerAddress('refaddress',$object);
1589  $morehtmlref.='</div>';
1590  }
1591  if (! empty($conf->global->MAIN_SHOW_TECHNICAL_ID) && in_array($object->element, array('societe', 'contact', 'member', 'product')))
1592  {
1593  $morehtmlref.='<div style="clear: both;"></div><div class="refidno">';
1594  $morehtmlref.=$langs->trans("TechnicalID").': '.$object->id;
1595  $morehtmlref.='</div>';
1596  }
1597 
1598  print '<div class="'.($onlybanner?'arearefnobottom ':'arearef ').'heightref valignmiddle" width="100%">';
1599  print $form->showrefnav($object, $paramid, $morehtml, $shownav, $fieldid, $fieldref, $morehtmlref, $moreparam, $nodbprefix, $morehtmlleft, $morehtmlstatus, $morehtmlright);
1600  print '</div>';
1601  print '<div class="underrefbanner clearboth"></div>';
1602 }
1603 
1613 function fieldLabel($langkey, $fieldkey, $fieldrequired=0)
1614 {
1615  global $conf, $langs;
1616  $ret='';
1617  if ($fieldrequired) $ret.='<span class="fieldrequired">';
1618  if (($conf->dol_use_jmobile != 4)) $ret.='<label for="'.$fieldkey.'">';
1619  $ret.=$langs->trans($langkey);
1620  if (($conf->dol_use_jmobile != 4)) $ret.='</label>';
1621  if ($fieldrequired) $ret.='</span>';
1622  return $ret;
1623 }
1624 
1632 function dol_bc($var,$moreclass='')
1633 {
1634  global $bc;
1635  $ret=' '.$bc[$var];
1636  if ($moreclass) $ret=preg_replace('/class=\"/','class="'.$moreclass.' ',$ret);
1637  return $ret;
1638 }
1639 
1651 function dol_format_address($object, $withcountry=0, $sep="\n", $outputlangs='', $mode=0)
1652 {
1653  global $conf,$langs;
1654 
1655  $ret='';
1656  $countriesusingstate=array('AU','CA','US','IN','GB','ES','UK','TR'); // See also MAIN_FORCE_STATE_INTO_ADDRESS
1657 
1658  // Address
1659  if (empty($mode)) {
1660  $ret .= $object->address;
1661  }
1662  // Zip/Town/State
1663  if (in_array($object->country_code,array('AU', 'CA', 'US')) || ! empty($conf->global->MAIN_FORCE_STATE_INTO_ADDRESS)) // US: title firstname name \n address lines \n town, state, zip \n country
1664  {
1665  $ret .= ($ret ? $sep : '' ).$object->town;
1666  if ($object->state)
1667  {
1668  $ret.=($ret?", ":'').$object->state;
1669  }
1670  if ($object->zip) $ret .= ($ret?", ":'').$object->zip;
1671  }
1672  else if (in_array($object->country_code,array('GB','UK'))) // UK: title firstname name \n address lines \n town state \n zip \n country
1673  {
1674  $ret .= ($ret ? $sep : '' ).$object->town;
1675  if ($object->state)
1676  {
1677  $ret.=($ret?", ":'').$object->state;
1678  }
1679  if ($object->zip) $ret .= ($ret ? $sep : '' ).$object->zip;
1680  }
1681  else if (in_array($object->country_code,array('ES','TR'))) // ES: title firstname name \n address lines \n zip town \n state \n country
1682  {
1683  $ret .= ($ret ? $sep : '' ).$object->zip;
1684  $ret .= ($object->town?(($object->zip?' ':'').$object->town):'');
1685  if ($object->state)
1686  {
1687  $ret.="\n".$object->state;
1688  }
1689  }
1690  else if (in_array($object->country_code,array('IT'))) // IT: tile firstname name\n address lines \n zip (Code Departement) \n country
1691  {
1692  $ret .= ($ret ? $sep : '' ).$object->zip;
1693  $ret .= ($object->town?(($object->zip?' ':'').$object->town):'');
1694  $ret .= ($object->departement_id?(' ('.($object->departement_id).')'):'');
1695  }
1696  else // Other: title firstname name \n address lines \n zip town \n country
1697  {
1698  $ret .= $object->zip ? (($ret ? $sep : '' ).$object->zip) : '';
1699  $ret .= ($object->town?(($object->zip?' ':($ret ? $sep : '' )).$object->town):'');
1700  if ($object->state && in_array($object->country_code,$countriesusingstate))
1701  {
1702  $ret.=($ret?", ":'').$object->state;
1703  }
1704  }
1705  if (! is_object($outputlangs)) $outputlangs=$langs;
1706  if ($withcountry)
1707  {
1708  $langs->load("dict");
1709  $ret.=($object->country_code?($ret?$sep:'').$outputlangs->convToOutputCharset($outputlangs->transnoentitiesnoconv("Country".$object->country_code)):'');
1710  }
1711 
1712  return $ret;
1713 }
1714 
1715 
1716 
1725 function dol_strftime($fmt, $ts=false, $is_gmt=false)
1726 {
1727  if ((abs($ts) <= 0x7FFFFFFF)) { // check if number in 32-bit signed range
1728  return ($is_gmt)? @gmstrftime($fmt,$ts): @strftime($fmt,$ts);
1729  }
1730  else return 'Error date into a not supported range';
1731 }
1732 
1754 function dol_print_date($time,$format='',$tzoutput='tzserver',$outputlangs='',$encodetooutput=false)
1755 {
1756  global $conf,$langs;
1757 
1758  // Clean parameters
1759  $to_gmt=false;
1760  $offsettz=$offsetdst=0;
1761  if ($tzoutput)
1762  {
1763  $to_gmt=true; // For backward compatibility
1764  if (is_string($tzoutput))
1765  {
1766  if ($tzoutput == 'tzserver')
1767  {
1768  $to_gmt=false;
1769  $offsettzstring=@date_default_timezone_get(); // Example 'Europe/Berlin' or 'Indian/Reunion'
1770  $offsettz=0;
1771  $offsetdst=0;
1772  }
1773  elseif ($tzoutput == 'tzuser' || $tzoutput == 'tzuserrel')
1774  {
1775  $to_gmt=true;
1776  $offsettzstring=(empty($_SESSION['dol_tz_string'])?'UTC':$_SESSION['dol_tz_string']); // Example 'Europe/Berlin' or 'Indian/Reunion'
1777  $offsettz=(empty($_SESSION['dol_tz'])?0:$_SESSION['dol_tz'])*60*60; // Will not be used anymore
1778  $offsetdst=(empty($_SESSION['dol_dst'])?0:$_SESSION['dol_dst'])*60*60; // Will not be used anymore
1779  }
1780  }
1781  }
1782  if (! is_object($outputlangs)) $outputlangs=$langs;
1783  if (! $format) $format='daytextshort';
1784  $reduceformat=(! empty($conf->dol_optimize_smallscreen) && in_array($format,array('day','dayhour')))?1:0;
1785  $formatwithoutreduce = preg_replace('/reduceformat/','',$format);
1786  if ($formatwithoutreduce != $format) { $format = $formatwithoutreduce; $reduceformat=1; } // so format 'dayreduceformat' is processed like day
1787 
1788  // Change predefined format into computer format. If found translation in lang file we use it, otherwise we use default.
1789  // TODO Add format daysmallyear and dayhoursmallyear
1790  if ($format == 'day') $format=($outputlangs->trans("FormatDateShort")!="FormatDateShort"?$outputlangs->trans("FormatDateShort"):$conf->format_date_short);
1791  else if ($format == 'hour') $format=($outputlangs->trans("FormatHourShort")!="FormatHourShort"?$outputlangs->trans("FormatHourShort"):$conf->format_hour_short);
1792  else if ($format == 'hourduration') $format=($outputlangs->trans("FormatHourShortDuration")!="FormatHourShortDuration"?$outputlangs->trans("FormatHourShortDuration"):$conf->format_hour_short_duration);
1793  else if ($format == 'daytext') $format=($outputlangs->trans("FormatDateText")!="FormatDateText"?$outputlangs->trans("FormatDateText"):$conf->format_date_text);
1794  else if ($format == 'daytextshort') $format=($outputlangs->trans("FormatDateTextShort")!="FormatDateTextShort"?$outputlangs->trans("FormatDateTextShort"):$conf->format_date_text_short);
1795  else if ($format == 'dayhour') $format=($outputlangs->trans("FormatDateHourShort")!="FormatDateHourShort"?$outputlangs->trans("FormatDateHourShort"):$conf->format_date_hour_short);
1796  else if ($format == 'dayhoursec') $format=($outputlangs->trans("FormatDateHourSecShort")!="FormatDateHourSecShort"?$outputlangs->trans("FormatDateHourSecShort"):$conf->format_date_hour_sec_short);
1797  else if ($format == 'dayhourtext') $format=($outputlangs->trans("FormatDateHourText")!="FormatDateHourText"?$outputlangs->trans("FormatDateHourText"):$conf->format_date_hour_text);
1798  else if ($format == 'dayhourtextshort') $format=($outputlangs->trans("FormatDateHourTextShort")!="FormatDateHourTextShort"?$outputlangs->trans("FormatDateHourTextShort"):$conf->format_date_hour_text_short);
1799  // Format not sensitive to language
1800  else if ($format == 'dayhourlog') $format='%Y%m%d%H%M%S';
1801  else if ($format == 'dayhourldap') $format='%Y%m%d%H%M%SZ';
1802  else if ($format == 'dayhourxcard') $format='%Y%m%dT%H%M%SZ';
1803  else if ($format == 'dayxcard') $format='%Y%m%d';
1804  else if ($format == 'dayrfc') $format='%Y-%m-%d'; // DATE_RFC3339
1805  else if ($format == 'dayhourrfc') $format='%Y-%m-%dT%H:%M:%SZ'; // DATETIME RFC3339
1806  else if ($format == 'standard') $format='%Y-%m-%d %H:%M:%S';
1807 
1808  if ($reduceformat)
1809  {
1810  $format=str_replace('%Y','%y',$format);
1811  $format=str_replace('yyyy','yy',$format);
1812  }
1813 
1814  // If date undefined or "", we return ""
1815  if (dol_strlen($time) == 0) return ''; // $time=0 allowed (it means 01/01/1970 00:00:00)
1816 
1817  // Clean format
1818  if (preg_match('/%b/i',$format)) // There is some text to translate
1819  {
1820  // We inhibate translation to text made by strftime functions. We will use trans instead later.
1821  $format=str_replace('%b','__b__',$format);
1822  $format=str_replace('%B','__B__',$format);
1823  }
1824  if (preg_match('/%a/i',$format)) // There is some text to translate
1825  {
1826  // We inhibate translation to text made by strftime functions. We will use trans instead later.
1827  $format=str_replace('%a','__a__',$format);
1828  $format=str_replace('%A','__A__',$format);
1829  }
1830 
1831  // Analyze date
1832  if (preg_match('/^([0-9]+)\-([0-9]+)\-([0-9]+) ?([0-9]+)?:?([0-9]+)?:?([0-9]+)?/i',$time,$reg)
1833  || preg_match('/^([0-9][0-9][0-9][0-9])([0-9][0-9])([0-9][0-9])([0-9][0-9])([0-9][0-9])([0-9][0-9])$/i',$time,$reg)) // Deprecated. Ex: 1970-01-01, 1970-01-01 01:00:00, 19700101010000
1834  {
1835  // TODO Remove this.
1836  // This part of code should not be used.
1837  dol_syslog("Functions.lib::dol_print_date function call with deprecated value of time in page ".$_SERVER["PHP_SELF"], LOG_ERR);
1838  // Date has format 'YYYY-MM-DD' or 'YYYY-MM-DD HH:MM:SS' or 'YYYYMMDDHHMMSS'
1839  $syear = (! empty($reg[1]) ? $reg[1] : '');
1840  $smonth = (! empty($reg[2]) ? $reg[2] : '');
1841  $sday = (! empty($reg[3]) ? $reg[3] : '');
1842  $shour = (! empty($reg[4]) ? $reg[4] : '');
1843  $smin = (! empty($reg[5]) ? $reg[5] : '');
1844  $ssec = (! empty($reg[6]) ? $reg[6] : '');
1845 
1846  $time=dol_mktime($shour,$smin,$ssec,$smonth,$sday,$syear,true);
1847  $ret=adodb_strftime($format, $time+$offsettz+$offsetdst, $to_gmt);
1848  }
1849  else
1850  {
1851  // Date is a timestamps
1852  if ($time < 100000000000) // Protection against bad date values
1853  {
1854  $timetouse = $time+$offsettz+$offsetdst; // TODO Replace this with function Date PHP. We also should not use anymore offsettz and offsetdst but only offsettzstring.
1855 
1856  $ret=adodb_strftime($format, $timetouse, $to_gmt);
1857  }
1858  else $ret='Bad value '.$time.' for date';
1859  }
1860 
1861  if (preg_match('/__b__/i',$format))
1862  {
1863  $timetouse = $time+$offsettz+$offsetdst; // TODO Replace this with function Date PHP. We also should not use anymore offsettz and offsetdst but only offsettzstring.
1864 
1865  // Here ret is string in PHP setup language (strftime was used). Now we convert to $outputlangs.
1866  $month=adodb_strftime('%m', $timetouse);
1867  $month=sprintf("%02d", $month); // $month may be return with format '06' on some installation and '6' on other, so we force it to '06'.
1868  if ($encodetooutput)
1869  {
1870  $monthtext=$outputlangs->transnoentities('Month'.$month);
1871  $monthtextshort=$outputlangs->transnoentities('MonthShort'.$month);
1872  }
1873  else
1874  {
1875  $monthtext=$outputlangs->transnoentitiesnoconv('Month'.$month);
1876  $monthtextshort=$outputlangs->transnoentitiesnoconv('MonthShort'.$month);
1877  }
1878  //print 'monthtext='.$monthtext.' monthtextshort='.$monthtextshort;
1879  $ret=str_replace('__b__',$monthtextshort,$ret);
1880  $ret=str_replace('__B__',$monthtext,$ret);
1881  //print 'x'.$outputlangs->charset_output.'-'.$ret.'x';
1882  //return $ret;
1883  }
1884  if (preg_match('/__a__/i',$format))
1885  {
1886  $timetouse = $time+$offsettz+$offsetdst; // TODO Replace this with function Date PHP. We also should not use anymore offsettz and offsetdst but only offsettzstring.
1887 
1888  $w=adodb_strftime('%w', $timetouse); // TODO Replace this with function Date PHP. We also should not use anymore offsettz and offsetdst but only offsettzstring.
1889  $dayweek=$outputlangs->transnoentitiesnoconv('Day'.$w);
1890  $ret=str_replace('__A__',$dayweek,$ret);
1891  $ret=str_replace('__a__',dol_substr($dayweek,0,3),$ret);
1892  }
1893 
1894  return $ret;
1895 }
1896 
1897 
1931 function dol_getdate($timestamp,$fast=false)
1932 {
1933  global $conf;
1934 
1935  $usealternatemethod=false;
1936  if ($timestamp <= 0) $usealternatemethod=true; // <= 1970
1937  if ($timestamp >= 2145913200) $usealternatemethod=true; // >= 2038
1938 
1939  if ($usealternatemethod)
1940  {
1941  $arrayinfo=adodb_getdate($timestamp,$fast);
1942  }
1943  else
1944  {
1945  $arrayinfo=getdate($timestamp);
1946  }
1947 
1948  return $arrayinfo;
1949 }
1950 
1970 function dol_mktime($hour,$minute,$second,$month,$day,$year,$gm=false,$check=1)
1971 {
1972  global $conf;
1973  //print "- ".$hour.",".$minute.",".$second.",".$month.",".$day.",".$year.",".$_SERVER["WINDIR"]." -";
1974 
1975  // Clean parameters
1976  if ($hour == -1 || empty($hour)) $hour=0;
1977  if ($minute == -1 || empty($minute)) $minute=0;
1978  if ($second == -1 || empty($second)) $second=0;
1979 
1980  // Check parameters
1981  if ($check)
1982  {
1983  if (! $month || ! $day) return '';
1984  if ($day > 31) return '';
1985  if ($month > 12) return '';
1986  if ($hour < 0 || $hour > 24) return '';
1987  if ($minute< 0 || $minute > 60) return '';
1988  if ($second< 0 || $second > 60) return '';
1989  }
1990 
1991  if (method_exists('DateTime','getTimestamp'))
1992  {
1993  if (empty($gm) || $gm === 'server')
1994  {
1995  $default_timezone=@date_default_timezone_get(); // Example 'Europe/Berlin'
1996  $localtz = new DateTimeZone($default_timezone);
1997  }
1998  else if ($gm === 'user')
1999  {
2000  // We use dol_tz_string first because it is more reliable.
2001  $default_timezone=(empty($_SESSION["dol_tz_string"])?@date_default_timezone_get():$_SESSION["dol_tz_string"]); // Example 'Europe/Berlin'
2002  try {
2003  $localtz = new DateTimeZone($default_timezone);
2004  }
2005  catch(Exception $e)
2006  {
2007  dol_syslog("Warning dol_tz_string contains an invalid value ".$_SESSION["dol_tz_string"], LOG_WARNING);
2008  $default_timezone=@date_default_timezone_get();
2009  }
2010  }
2011  else if (strrpos($gm, "tz,") !== false)
2012  {
2013  $timezone=str_replace("tz,", "", $gm); // Example 'tz,Europe/Berlin'
2014  try
2015  {
2016  $localtz = new DateTimeZone($timezone);
2017  }
2018  catch(Exception $e)
2019  {
2020  dol_syslog("Warning passed timezone contains an invalid value ".$timezone, LOG_WARNING);
2021  }
2022  }
2023 
2024  if (empty($localtz)) {
2025  $localtz = new DateTimeZone('UTC');
2026  }
2027  //var_dump($localtz);
2028  //var_dump($year.'-'.$month.'-'.$day.'-'.$hour.'-'.$minute);
2029  $dt = new DateTime(null,$localtz);
2030  $dt->setDate($year,$month,$day);
2031  $dt->setTime((int) $hour, (int) $minute, (int) $second);
2032  $date=$dt->getTimestamp(); // should include daylight saving time
2033  //var_dump($date);
2034  return $date;
2035  }
2036  else
2037  {
2038  dol_print_error('','PHP version must be 5.4+');
2039  return '';
2040  }
2041 }
2042 
2043 
2053 function dol_now($mode='gmt')
2054 {
2055  $ret=0;
2056 
2057  // Note that gmmktime and mktime return same value (GMT) when used without parameters
2058  //if ($mode == 'gmt') $ret=gmmktime(); // Strict Standards: gmmktime(): You should be using the time() function instead
2059  if ($mode == 'gmt') $ret=time(); // Time for now at greenwich.
2060  else if ($mode == 'tzserver') // Time for now with PHP server timezone added
2061  {
2062  require_once DOL_DOCUMENT_ROOT.'/core/lib/date.lib.php';
2063  $tzsecond=getServerTimeZoneInt('now'); // Contains tz+dayling saving time
2064  $ret=(int) (dol_now('gmt')+($tzsecond*3600));
2065  }
2066  /*else if ($mode == 'tzref') // Time for now with parent company timezone is added
2067  {
2068  require_once DOL_DOCUMENT_ROOT.'/core/lib/date.lib.php';
2069  $tzsecond=getParentCompanyTimeZoneInt(); // Contains tz+dayling saving time
2070  $ret=dol_now('gmt')+($tzsecond*3600);
2071  }*/
2072  else if ($mode == 'tzuser') // Time for now with user timezone added
2073  {
2074  //print 'time: '.time().'-'.mktime().'-'.gmmktime();
2075  $offsettz=(empty($_SESSION['dol_tz'])?0:$_SESSION['dol_tz'])*60*60;
2076  $offsetdst=(empty($_SESSION['dol_dst'])?0:$_SESSION['dol_dst'])*60*60;
2077  $ret=(int) (dol_now('gmt')+($offsettz+$offsetdst));
2078  }
2079 
2080  return $ret;
2081 }
2082 
2083 
2092 function dol_print_size($size,$shortvalue=0,$shortunit=0)
2093 {
2094  global $conf,$langs;
2095  $level=1024;
2096 
2097  if (! empty($conf->dol_optimize_smallscreen)) $shortunit=1;
2098 
2099  // Set value text
2100  if (empty($shortvalue) || $size < ($level*10))
2101  {
2102  $ret=$size;
2103  $textunitshort=$langs->trans("b");
2104  $textunitlong=$langs->trans("Bytes");
2105  }
2106  else
2107  {
2108  $ret=round($size/$level,0);
2109  $textunitshort=$langs->trans("Kb");
2110  $textunitlong=$langs->trans("KiloBytes");
2111  }
2112  // Use long or short text unit
2113  if (empty($shortunit)) { $ret.=' '.$textunitlong; }
2114  else { $ret.=' '.$textunitshort; }
2115 
2116  return $ret;
2117 }
2118 
2128 function dol_print_url($url,$target='_blank',$max=32,$withpicto=0)
2129 {
2130  global $langs;
2131 
2132  if (empty($url)) return '';
2133 
2134  $link='<a href="';
2135  if (! preg_match('/^http/i',$url)) $link.='http://';
2136  $link.=$url;
2137  $link.='"';
2138  if ($target) $link.=' target="'.$target.'"';
2139  $link.='>';
2140  if (! preg_match('/^http/i',$url)) $link.='http://';
2141  $link.=dol_trunc($url,$max);
2142  $link.='</a>';
2143  return '<div class="nospan float" style="margin-right: 10px">'.($withpicto?img_picto($langs->trans("Url"), 'object_globe.png').' ':'').$link.'</div>';
2144 }
2145 
2158 function dol_print_email($email,$cid=0,$socid=0,$addlink=0,$max=64,$showinvalid=1,$withpicto=0)
2159 {
2160  global $conf,$user,$langs,$hookmanager;
2161 
2162  $newemail=$email;
2163 
2164  if (empty($email)) return '&nbsp;';
2165 
2166  if (! empty($addlink))
2167  {
2168  $newemail='<a style="text-overflow: ellipsis;" href="';
2169  if (! preg_match('/^mailto:/i',$email)) $newemail.='mailto:';
2170  $newemail.=$email;
2171  $newemail.='">';
2172  $newemail.=dol_trunc($email,$max);
2173  $newemail.='</a>';
2174  if ($showinvalid && ! isValidEmail($email))
2175  {
2176  $langs->load("errors");
2177  $newemail.=img_warning($langs->trans("ErrorBadEMail",$email));
2178  }
2179 
2180  if (($cid || $socid) && ! empty($conf->agenda->enabled) && $user->rights->agenda->myactions->create)
2181  {
2182  $type='AC_EMAIL'; $link='';
2183  if (! empty($conf->global->AGENDA_ADDACTIONFOREMAIL)) $link='<a href="'.DOL_URL_ROOT.'/comm/action/card.php?action=create&amp;backtopage=1&amp;actioncode='.$type.'&amp;contactid='.$cid.'&amp;socid='.$socid.'">'.img_object($langs->trans("AddAction"),"calendar").'</a>';
2184  if ($link) $newemail='<div>'.$newemail.' '.$link.'</div>';
2185  }
2186  }
2187  else
2188  {
2189  if ($showinvalid && ! isValidEmail($email))
2190  {
2191  $langs->load("errors");
2192  $newemail.=img_warning($langs->trans("ErrorBadEMail",$email));
2193  }
2194  }
2195 
2196  $rep = '<div class="nospan float" style="margin-right: 10px">'.($withpicto?img_picto($langs->trans("EMail"), 'object_email.png').' ':'').$newemail.'</div>';
2197  if ($hookmanager) {
2198  $parameters = array('cid' => $cid, 'socid' => $socid,'addlink' => $addlink, 'picto' => $withpicto);
2199  $reshook = $hookmanager->executeHooks('printEmail', $parameters, $email);
2200  $rep.=$hookmanager->resPrint;
2201  }
2202 
2203  return $rep;
2204 }
2205 
2215 function dol_print_socialnetworks($value,$cid,$socid,$type)
2216 {
2217  global $conf,$user,$langs;
2218 
2219  $newskype=$value;
2220 
2221  if (empty($value)) return '&nbsp;';
2222 
2223  if (! empty($type))
2224  {
2225  $newskype ='<div class="divsocialnetwork inline-block valignmiddle">';
2226  $newskype.=img_picto($langs->trans(strtoupper($type)), $type.'.png', '', false, 0, 0, '', 'paddingright');
2227  $newskype.=$value;
2228  if ($type == 'skype')
2229  {
2230  $newskype.= '&nbsp;';
2231  $newskype.='<a href="skype:';
2232  $newskype.=$value;
2233  $newskype.='?call" alt="'.$langs->trans("Call").'&nbsp;'.$value.'" title="'.$langs->trans("Call").'&nbsp;'.$value.'">';
2234  $newskype.='<img src="'.DOL_URL_ROOT.'/theme/common/skype_callbutton.png" border="0">';
2235  $newskype.='</a><a href="skype:';
2236  $newskype.=$value;
2237  $newskype.='?chat" alt="'.$langs->trans("Chat").'&nbsp;'.$value.'" title="'.$langs->trans("Chat").'&nbsp;'.$value.'">';
2238  $newskype.='<img class="paddingleft" src="'.DOL_URL_ROOT.'/theme/common/skype_chatbutton.png" border="0">';
2239  $newskype.='</a>';
2240  }
2241  if (($cid || $socid) && ! empty($conf->agenda->enabled) && $user->rights->agenda->myactions->create && $type=='skype')
2242  {
2243  $addlink='AC_SKYPE'; $link='';
2244  if (! empty($conf->global->AGENDA_ADDACTIONFORSKYPE)) $link='<a href="'.DOL_URL_ROOT.'/comm/action/card.php?action=create&amp;backtopage=1&amp;actioncode='.$addlink.'&amp;contactid='.$cid.'&amp;socid='.$socid.'">'.img_object($langs->trans("AddAction"),"calendar").'</a>';
2245  $newskype.=($link?' '.$link:'');
2246  }
2247  $newskype.='</div>';
2248  }
2249  else
2250  {
2251  $langs->load("errors");
2252  $newskype.=img_warning($langs->trans("ErrorBadSocialNetworkValue",$value));
2253  }
2254  return $newskype;
2255 }
2256 
2271 function dol_print_phone($phone,$countrycode='',$cid=0,$socid=0,$addlink='',$separ="&nbsp;",$withpicto='',$titlealt='',$adddivfloat=0)
2272 {
2273  global $conf, $user, $langs, $mysoc, $hookmanager;
2274 
2275  // Clean phone parameter
2276  $phone = preg_replace("/[\s.-]/","",trim($phone));
2277  if (empty($phone)) { return ''; }
2278  if (empty($countrycode)) $countrycode=$mysoc->country_code;
2279 
2280  // Short format for small screens
2281  if ($conf->dol_optimize_smallscreen) $separ='';
2282 
2283  $newphone=$phone;
2284  if (strtoupper($countrycode) == "FR")
2285  {
2286  // France
2287  if (dol_strlen($phone) == 10) {
2288  $newphone=substr($newphone,0,2).$separ.substr($newphone,2,2).$separ.substr($newphone,4,2).$separ.substr($newphone,6,2).$separ.substr($newphone,8,2);
2289  }
2290  elseif (dol_strlen($phone) == 7)
2291  {
2292  $newphone=substr($newphone,0,3).$separ.substr($newphone,3,2).$separ.substr($newphone,5,2);
2293  }
2294  elseif (dol_strlen($phone) == 9)
2295  {
2296  $newphone=substr($newphone,0,2).$separ.substr($newphone,2,3).$separ.substr($newphone,5,2).$separ.substr($newphone,7,2);
2297  }
2298  elseif (dol_strlen($phone) == 11)
2299  {
2300  $newphone=substr($newphone,0,3).$separ.substr($newphone,3,2).$separ.substr($newphone,5,2).$separ.substr($newphone,7,2).$separ.substr($newphone,9,2);
2301  }
2302  elseif (dol_strlen($phone) == 12)
2303  {
2304  $newphone=substr($newphone,0,4).$separ.substr($newphone,4,2).$separ.substr($newphone,6,2).$separ.substr($newphone,8,2).$separ.substr($newphone,10,2);
2305  }
2306  }
2307 
2308  elseif (strtoupper($countrycode) == "CA")
2309  {
2310  if (dol_strlen($phone) == 10) {
2311  $newphone=($separ!=''?'(':'').substr($newphone,0,3).($separ!=''?')':'').$separ.substr($newphone,3,3).($separ!=''?'-':'').substr($newphone,6,4);
2312  }
2313  }
2314  elseif (strtoupper($countrycode) == "PT" )
2315  {//Portugal
2316  if (dol_strlen($phone) == 13)
2317  {//ex: +351_ABC_DEF_GHI
2318  $newphone= substr($newphone,0,4).$separ.substr($newphone,4,3).$separ.substr($newphone,7,3).$separ.substr($newphone,10,3);
2319  }
2320  }
2321  elseif (strtoupper($countrycode) == "SR" )
2322  {//Suriname
2323  if (dol_strlen($phone) == 10)
2324  {//ex: +597_ABC_DEF
2325  $newphone= substr($newphone,0,4).$separ.substr($newphone,4,3).$separ.substr($newphone,7,3);
2326  }
2327  elseif (dol_strlen($phone) == 11)
2328  {//ex: +597_ABC_DEFG
2329  $newphone= substr($newphone,0,4).$separ.substr($newphone,4,3).$separ.substr($newphone,7,4);
2330  }
2331  }
2332  elseif (strtoupper($countrycode) == "DE" )
2333  {//Allemagne
2334  if (dol_strlen($phone) == 14)
2335  {//ex: +49_ABCD_EFGH_IJK
2336  $newphone= substr($newphone,0,3).$separ.substr($newphone,3,4).$separ.substr($newphone,7,4).$separ.substr($newphone,11,3);
2337  }
2338  elseif (dol_strlen($phone) == 13)
2339  {//ex: +49_ABC_DEFG_HIJ
2340  $newphone= substr($newphone,0,3).$separ.substr($newphone,3,3).$separ.substr($newphone,6,4).$separ.substr($newphone,10,3);
2341  }
2342  }
2343  elseif (strtoupper($countrycode) == "ES")
2344  {//Espagne
2345  if (dol_strlen($phone) == 12)
2346  {//ex: +34_ABC_DEF_GHI
2347  $newphone= substr($newphone,0,3).$separ.substr($newphone,3,3).$separ.substr($newphone,6,3).$separ.substr($newphone,9,3);
2348  }
2349  }
2350  elseif (strtoupper($countrycode) == "BF")
2351  {// Burkina Faso
2352  if (dol_strlen($phone) == 12)
2353  {//ex : +22 A BC_DE_FG_HI
2354  $newphone= substr($newphone,0,3).$separ.substr($newphone,3,1).$separ.substr($newphone,4,2).$separ.substr($newphone,6,2).$separ.substr($newphone,8,2).$separ.substr($newphone,10,2);
2355  }
2356  }
2357  elseif (strtoupper($countrycode) == "RO")
2358  {// Roumanie
2359  if (dol_strlen($phone) == 12)
2360  {//ex : +40 AB_CDE_FG_HI
2361  $newphone= substr($newphone,0,3).$separ.substr($newphone,3,2).$separ.substr($newphone,5,3).$separ.substr($newphone,8,2).$separ.substr($newphone,10,2);
2362  }
2363  }
2364  elseif (strtoupper($countrycode) == "TR")
2365  {//Turquie
2366  if (dol_strlen($phone) == 13)
2367  {//ex : +90 ABC_DEF_GHIJ
2368  $newphone= substr($newphone,0,3).$separ.substr($newphone,3,3).$separ.substr($newphone,6,3).$separ.substr($newphone,9,4);
2369  }
2370  }
2371  elseif (strtoupper($countrycode) == "US")
2372  {//Etat-Unis
2373  if (dol_strlen($phone) == 12)
2374  {//ex: +1 ABC_DEF_GHIJ
2375  $newphone= substr($newphone,0,2).$separ.substr($newphone,2,3).$separ.substr($newphone,5,3).$separ.substr($newphone,8,4);
2376  }
2377  }
2378  elseif (strtoupper($countrycode) == "MX")
2379  {//Mexique
2380  if (dol_strlen($phone) == 12)
2381  {//ex: +52 ABCD_EFG_HI
2382  $newphone = substr($newphone,0,3).$separ.substr($newphone,3,4).$separ.substr($newphone,7,3).$separ.substr($newphone,10,2);
2383  }
2384  elseif (dol_strlen($phone) == 11)
2385  {//ex: +52 AB_CD_EF_GH
2386  $newphone = substr($newphone,0,3).$separ.substr($newphone,3,2).$separ.substr($newphone,5,2).$separ.substr($newphone,7,2).$separ.substr($newphone,9,2);
2387  }
2388  elseif (dol_strlen($phone) == 13)
2389  {//ex: +52 ABC_DEF_GHIJ
2390  $newphone = substr($newphone,0,3).$separ.substr($newphone,3,3).$separ.substr($newphone,6,3).$separ.substr($newphone,9,4);
2391  }
2392  }
2393  elseif (strtoupper($countrycode) == "ML")
2394  {//Mali
2395  if(dol_strlen($phone) == 12)
2396  {//ex: +223 AB_CD_EF_GH
2397  $newphone = substr($newphone,0,4).$separ.substr($newphone,4,2).$separ.substr($newphone,6,2).$separ.substr($newphone,8,2).$separ.substr($newphone,10,2);
2398  }
2399  }
2400  elseif (strtoupper($countrycode) == "TH")
2401  {//Thaïlande
2402  if(dol_strlen($phone) == 11)
2403  {//ex: +66_ABC_DE_FGH
2404  $newphone = substr($newphone,0,3).$separ.substr($newphone,3,3).$separ.substr($newphone,6,2).$separ.substr($newphone,8,3);
2405  }
2406  elseif(dol_strlen($phone) == 12)
2407  {//ex: +66_A_BCD_EF_GHI
2408  $newphone = substr($newphone,0,3).$separ.substr($newphone,3,1).$separ.substr($newphone,4,3).$separ.substr($newphone,7,2).$separ.substr($newphone,9,3);
2409  }
2410  }
2411  elseif (strtoupper($countrycode) == "MU")
2412  {//Maurice
2413  if(dol_strlen($phone) == 11)
2414  {//ex: +230_ABC_DE_FG
2415  $newphone = substr($newphone,0,4).$separ.substr($newphone,4,3).$separ.substr($newphone,7,2).$separ.substr($newphone,9,2);
2416  }
2417  elseif(dol_strlen($phone) == 12)
2418  {//ex: +230_ABCD_EF_GH
2419  $newphone = substr($newphone,0,4).$separ.substr($newphone,4,4).$separ.substr($newphone,8,2).$separ.substr($newphone,10,2);
2420  }
2421  }
2422  elseif (strtoupper($countrycode) == "ZA")
2423  {//Afrique du sud
2424  if(dol_strlen($phone) == 12)
2425  {//ex: +27_AB_CDE_FG_HI
2426  $newphone = substr($newphone,0,3).$separ.substr($newphone,3,2).$separ.substr($newphone,5,3).$separ.substr($newphone,8,2).$separ.substr($newphone,10,2);
2427  }
2428  }
2429  elseif (strtoupper($countrycode) == "SY")
2430  {//Syrie
2431  if(dol_strlen($phone) == 12)
2432  {//ex: +963_AB_CD_EF_GH
2433  $newphone = substr($newphone,0,4).$separ.substr($newphone,4,2).$separ.substr($newphone,6,2).$separ.substr($newphone,8,2).$separ.substr($newphone,10,2);
2434  }
2435  elseif(dol_strlen($phone) == 13)
2436  {//ex: +963_AB_CD_EF_GHI
2437  $newphone = substr($newphone,0,4).$separ.substr($newphone,4,2).$separ.substr($newphone,6,2).$separ.substr($newphone,8,2).$separ.substr($newphone,10,3);
2438  }
2439  }
2440  elseif (strtoupper($countrycode) == "AE")
2441  {//Emirats Arabes Unis
2442  if(dol_strlen($phone) == 12)
2443  {//ex: +971_ABC_DEF_GH
2444  $newphone = substr($newphone,0,4).$separ.substr($newphone,4,3).$separ.substr($newphone,7,3).$separ.substr($newphone,10,2);
2445  }
2446  elseif(dol_strlen($phone) == 13)
2447  {//ex: +971_ABC_DEF_GHI
2448  $newphone = substr($newphone,0,4).$separ.substr($newphone,4,3).$separ.substr($newphone,7,3).$separ.substr($newphone,10,3);
2449  }
2450  elseif(dol_strlen($phone) == 14)
2451  {//ex: +971_ABC_DEF_GHIK
2452  $newphone = substr($newphone,0,4).$separ.substr($newphone,4,3).$separ.substr($newphone,7,3).$separ.substr($newphone,10,4);
2453  }
2454  }
2455  elseif (strtoupper($countrycode) == "DZ")
2456  {//Algérie
2457  if(dol_strlen($phone) == 13)
2458  {//ex: +213_ABC_DEF_GHI
2459  $newphone = substr($newphone,0,4).$separ.substr($newphone,4,3).$separ.substr($newphone,7,3).$separ.substr($newphone,10,3);
2460  }
2461  }
2462  elseif (strtoupper($countrycode) == "BE")
2463  {//Belgique
2464  if(dol_strlen($phone) == 11)
2465  {//ex: +32_ABC_DE_FGH
2466  $newphone = substr($newphone,0,3).$separ.substr($newphone,3,3).$separ.substr($newphone,6,2).$separ.substr($newphone,8,3);
2467  }
2468  elseif(dol_strlen($phone) == 12)
2469  {//ex: +32_ABC_DEF_GHI
2470  $newphone = substr($newphone,0,3).$separ.substr($newphone,3,3).$separ.substr($newphone,6,3).$separ.substr($newphone,9,3);
2471  }
2472  }
2473  elseif (strtoupper($countrycode) == "PF")
2474  {//Polynésie française
2475  if(dol_strlen($phone) == 12)
2476  {//ex: +689_AB_CD_EF_GH
2477  $newphone = substr($newphone,0,4).$separ.substr($newphone,4,2).$separ.substr($newphone,6,2).$separ.substr($newphone,8,2).$separ.substr($newphone,10,2);
2478  }
2479  }
2480  elseif (strtoupper($countrycode) == "CO")
2481  {//Colombie
2482  if(dol_strlen($phone) == 13)
2483  {//ex: +57_ABC_DEF_GH_IJ
2484  $newphone = substr($newphone,0,3).$separ.substr($newphone,3,3).$separ.substr($newphone,6,3).$separ.substr($newphone,9,2).$separ.substr($newphone,11,2);
2485  }
2486  }
2487  elseif (strtoupper($countrycode) == "JO")
2488  {//Jordanie
2489  if(dol_strlen($phone) == 12)
2490  {//ex: +962_A_BCD_EF_GH
2491  $newphone = substr($newphone,0,4).$separ.substr($newphone,4,1).$separ.substr($newphone,5,3).$separ.substr($newphone,7,2).$separ.substr($newphone,9,2);
2492  }
2493  }
2494  elseif (strtoupper($countrycode) == "MG")
2495  {//Madagascar
2496  if(dol_strlen($phone) == 13)
2497  {//ex: +261_AB_CD_EF_GHI
2498  $newphone = substr($newphone,0,4).$separ.substr($newphone,4,2).$separ.substr($newphone,6,2).$separ.substr($newphone,8,2).$separ.substr($newphone,10,3);
2499  }
2500  }
2501  elseif (strtoupper($countrycode) == "GB")
2502  {//Royaume uni
2503  if(dol_strlen($phone) == 13)
2504  {//ex: +44_ABCD_EFG_HIJ
2505  $newphone = substr($newphone,0,3).$separ.substr($newphone,3,4).$separ.substr($newphone,7,3).$separ.substr($newphone,10,3);
2506  }
2507  }
2508  elseif (strtoupper($countrycode) == "CH")
2509  {//Suisse
2510  if(dol_strlen($phone) == 12)
2511  {//ex: +41_AB_CDE_FG_HI
2512  $newphone = substr($newphone,0,3).$separ.substr($newphone,3,2).$separ.substr($newphone,5,3).$separ.substr($newphone,8,2).$separ.substr($newphone,10,2);
2513  }
2514  elseif(dol_strlen($phone) == 15)
2515  {// +41_AB_CDE_FGH_IJKL
2516  $newphone =$newphone = substr($newphone,0,3).$separ.substr($newphone,3,2).$separ.substr($newphone,5,3).$separ.substr($newphone,8,3).$separ.substr($newphone,11,4);
2517  }
2518  }
2519  elseif (strtoupper($countrycode) == "TN")
2520  {//Tunisie
2521  if(dol_strlen($phone) == 12)
2522  {//ex: +216_AB_CDE_FGH
2523  $newphone = substr($newphone,0,4).$separ.substr($newphone,4,2).$separ.substr($newphone,6,3).$separ.substr($newphone,9,3);
2524  }
2525  }
2526  elseif (strtoupper($countrycode) == "GF")
2527  {//Guyane francaise
2528  if(dol_strlen($phone) == 13)
2529  {//ex: +594_ABC_DE_FG_HI (ABC=594 de nouveau)
2530  $newphone = substr($newphone,0,4).$separ.substr($newphone,4,3).$separ.substr($newphone,7,2).$separ.substr($newphone,9,2).$separ.substr($newphone,11,2);
2531  }
2532  }
2533  elseif (strtoupper($countrycode) == "GP")
2534  {//Guadeloupe
2535  if(dol_strlen($phone) == 13)
2536  {//ex: +590_ABC_DE_FG_HI (ABC=590 de nouveau)
2537  $newphone = substr($newphone,0,4).$separ.substr($newphone,4,3).$separ.substr($newphone,7,2).$separ.substr($newphone,9,2).$separ.substr($newphone,11,2);
2538  }
2539  }
2540  elseif (strtoupper($countrycode) == "MQ")
2541  {//Martinique
2542  if(dol_strlen($phone) == 13)
2543  {//ex: +596_ABC_DE_FG_HI (ABC=596 de nouveau)
2544  $newphone = substr($newphone,0,4).$separ.substr($newphone,4,3).$separ.substr($newphone,7,2).$separ.substr($newphone,9,2).$separ.substr($newphone,11,2);
2545  }
2546  }
2547  elseif (strtoupper($countrycode) == "IT")
2548  {//Italie
2549  if(dol_strlen($phone) == 12)
2550  {//ex: +39_ABC_DEF_GHI
2551  $newphone = substr($newphone,0,3).$separ.substr($newphone,3,3).$separ.substr($newphone,6,3).$separ.substr($newphone,9,3);
2552  }
2553  elseif(dol_strlen($phone) == 13)
2554  {//ex: +39_ABC_DEF_GH_IJ
2555  $newphone = substr($newphone,0,3).$separ.substr($newphone,3,3).$separ.substr($newphone,6,3).$separ.substr($newphone,9,2).$separ.substr($newphone,11,2);
2556  }
2557  }
2558  elseif(strtoupper($countrycode) == "AU")
2559  {//Australie
2560  if(dol_strlen($phone) == 12)
2561  {//ex: +61_A_BCDE_FGHI
2562  $newphone = substr($newphone,0,3).$separ.substr($newphone,3,1).$separ.substr($newphone,4,4).$separ.substr($newphone,8,4);
2563  }
2564  }
2565  if (! empty($addlink)) // Link on phone number (+ link to add action if conf->global->AGENDA_ADDACTIONFORPHONE set)
2566  {
2567  if ($conf->browser->layout == 'phone' || (! empty($conf->clicktodial->enabled) && ! empty($conf->global->CLICKTODIAL_USE_TEL_LINK_ON_PHONE_NUMBERS))) // If phone or option for, we use link of phone
2568  {
2569  $newphone ='<a href="tel:'.$phone.'"';
2570  $newphone.='>'.$phone.'</a>';
2571  }
2572  else if (! empty($conf->clicktodial->enabled) && $addlink == 'AC_TEL') // If click to dial, we use click to dial url
2573  {
2574  if (empty($user->clicktodial_loaded)) $user->fetch_clicktodial();
2575 
2576  // Define urlmask
2577  $urlmask='ErrorClickToDialModuleNotConfigured';
2578  if (! empty($conf->global->CLICKTODIAL_URL)) $urlmask=$conf->global->CLICKTODIAL_URL;
2579  if (! empty($user->clicktodial_url)) $urlmask=$user->clicktodial_url;
2580 
2581  $clicktodial_poste=(! empty($user->clicktodial_poste)?urlencode($user->clicktodial_poste):'');
2582  $clicktodial_login=(! empty($user->clicktodial_login)?urlencode($user->clicktodial_login):'');
2583  $clicktodial_password=(! empty($user->clicktodial_password)?urlencode($user->clicktodial_password):'');
2584  // This line is for backward compatibility
2585  $url = sprintf($urlmask, urlencode($phone), $clicktodial_poste, $clicktodial_login, $clicktodial_password);
2586  // Thoose lines are for substitution
2587  $substitarray=array('__PHONEFROM__'=>$clicktodial_poste,
2588  '__PHONETO__'=>urlencode($phone),
2589  '__LOGIN__'=>$clicktodial_login,
2590  '__PASS__'=>$clicktodial_password);
2591  $url = make_substitutions($url, $substitarray);
2592  $newphonesav=$newphone;
2593  $newphone ='<a href="'.$url.'"';
2594  if (! empty($conf->global->CLICKTODIAL_FORCENEWTARGET)) $newphone.=' target="_blank"';
2595  $newphone.='>'.$newphonesav.'</a>';
2596  }
2597 
2598  //if (($cid || $socid) && ! empty($conf->agenda->enabled) && $user->rights->agenda->myactions->create)
2599  if (! empty($conf->agenda->enabled) && $user->rights->agenda->myactions->create)
2600  {
2601  $type='AC_TEL'; $link='';
2602  if ($addlink == 'AC_FAX') $type='AC_FAX';
2603  if (! empty($conf->global->AGENDA_ADDACTIONFORPHONE)) $link='<a href="'.DOL_URL_ROOT.'/comm/action/card.php?action=create&amp;backtopage=1&amp;actioncode='.$type.($cid?'&amp;contactid='.$cid:'').($socid?'&amp;socid='.$socid:'').'">'.img_object($langs->trans("AddAction"),"calendar").'</a>';
2604  if ($link) $newphone='<div>'.$newphone.' '.$link.'</div>';
2605  }
2606  }
2607 
2608  if (empty($titlealt))
2609  {
2610  $titlealt=($withpicto=='fax'?$langs->trans("Fax"):$langs->trans("Phone"));
2611  }
2612  $rep='';
2613 
2614  if ($hookmanager) {
2615  $parameters = array('countrycode' => $countrycode, 'cid' => $cid, 'socid' => $socid,'titlealt' => $titlealt, 'picto' => $withpicto);
2616  $reshook = $hookmanager->executeHooks('printPhone', $parameters, $phone);
2617  $rep.=$hookmanager->resPrint;
2618  }
2619  if (empty($reshook))
2620  {
2621  $picto = '';
2622  if($withpicto){
2623  if($withpicto=='fax'){
2624  $picto = 'phoning_fax';
2625  }elseif($withpicto=='phone'){
2626  $picto = 'phoning';
2627  }elseif($withpicto=='mobile'){
2628  $picto = 'phoning_mobile';
2629  }else{
2630  $picto = '';
2631  }
2632  }
2633  if ($adddivfloat) $rep.='<div class="nospan float" style="margin-right: 10px">';
2634  else $rep.='<span style="margin-right: 10px;">';
2635  $rep.=($withpicto?img_picto($titlealt, 'object_'.$picto.'.png').' ':'').$newphone;
2636  if ($adddivfloat) $rep.='</div>';
2637  else $rep.='</span>';
2638  }
2639 
2640  return $rep;
2641 }
2642 
2650 function dol_print_ip($ip,$mode=0)
2651 {
2652  global $conf,$langs;
2653 
2654  $ret='';
2655 
2656  if (empty($mode)) $ret.=$ip;
2657 
2658  if ($mode != 2)
2659  {
2660  $countrycode=dolGetCountryCodeFromIp($ip);
2661  if ($countrycode) // If success, countrycode is us, fr, ...
2662  {
2663  if (file_exists(DOL_DOCUMENT_ROOT.'/theme/common/flags/'.$countrycode.'.png'))
2664  {
2665  $ret.=' '.img_picto($countrycode.' '.$langs->trans("AccordingToGeoIPDatabase"),DOL_URL_ROOT.'/theme/common/flags/'.$countrycode.'.png','',1);
2666  }
2667  else $ret.=' ('.$countrycode.')';
2668  }
2669  }
2670 
2671  return $ret;
2672 }
2673 
2683 {
2684  $ip = empty($_SERVER['HTTP_X_FORWARDED_FOR'])? (empty($_SERVER['HTTP_CLIENT_IP'])?(empty($_SERVER['REMOTE_ADDR'])?'':$_SERVER['REMOTE_ADDR']):$_SERVER['HTTP_CLIENT_IP']) : $_SERVER['HTTP_X_FORWARDED_FOR'];
2685  return $ip;
2686 }
2687 
2695 {
2696  global $conf;
2697 
2698  $countrycode='';
2699 
2700  if (! empty($conf->geoipmaxmind->enabled))
2701  {
2702  $datafile=$conf->global->GEOIPMAXMIND_COUNTRY_DATAFILE;
2703  //$ip='24.24.24.24';
2704  //$datafile='E:\Mes Sites\Web\Admin1\awstats\maxmind\GeoIP.dat'; Note that this must be downloaded datafile (not same than datafile provided with ubuntu packages)
2705 
2706  include_once DOL_DOCUMENT_ROOT.'/core/class/dolgeoip.class.php';
2707  $geoip=new DolGeoIP('country',$datafile);
2708  //print 'ip='.$ip.' databaseType='.$geoip->gi->databaseType." GEOIP_CITY_EDITION_REV1=".GEOIP_CITY_EDITION_REV1."\n";
2709  //print "geoip_country_id_by_addr=".geoip_country_id_by_addr($geoip->gi,$ip)."\n";
2710  $countrycode=$geoip->getCountryCodeFromIP($ip);
2711  }
2712 
2713  return $countrycode;
2714 }
2715 
2716 
2724 {
2725  global $conf,$langs,$user;
2726 
2727  //$ret=$user->xxx;
2728  $ret='';
2729  if (! empty($conf->geoipmaxmind->enabled))
2730  {
2731  $ip=getUserRemoteIP();
2732  $datafile=$conf->global->GEOIPMAXMIND_COUNTRY_DATAFILE;
2733  //$ip='24.24.24.24';
2734  //$datafile='E:\Mes Sites\Web\Admin1\awstats\maxmind\GeoIP.dat';
2735  include_once DOL_DOCUMENT_ROOT.'/core/class/dolgeoip.class.php';
2736  $geoip=new DolGeoIP('country',$datafile);
2737  $countrycode=$geoip->getCountryCodeFromIP($ip);
2738  $ret=$countrycode;
2739  }
2740  return $ret;
2741 }
2742 
2755 function dol_print_address($address, $htmlid, $mode, $id, $noprint=0, $charfornl='')
2756 {
2757  global $conf, $user, $langs, $hookmanager;
2758 
2759  $out = '';
2760 
2761  if ($address)
2762  {
2763  if ($hookmanager) {
2764  $parameters = array('element' => $mode, 'id' => $id);
2765  $reshook = $hookmanager->executeHooks('printAddress', $parameters, $address);
2766  $out.=$hookmanager->resPrint;
2767  }
2768  if (empty($reshook))
2769  {
2770  if (empty($charfornl)) $out.=nl2br($address);
2771  else $out.=preg_replace('/[\r\n]+/', $charfornl, $address);
2772 
2773  $showgmap=$showomap=0;
2774 
2775  // TODO Add a hook here
2776  if (($mode=='thirdparty' || $mode =='societe') && ! empty($conf->google->enabled) && ! empty($conf->global->GOOGLE_ENABLE_GMAPS)) $showgmap=1;
2777  if ($mode=='contact' && ! empty($conf->google->enabled) && ! empty($conf->global->GOOGLE_ENABLE_GMAPS_CONTACTS)) $showgmap=1;
2778  if ($mode=='member' && ! empty($conf->google->enabled) && ! empty($conf->global->GOOGLE_ENABLE_GMAPS_MEMBERS)) $showgmap=1;
2779  if (($mode=='thirdparty' || $mode =='societe') && ! empty($conf->openstreetmap->enabled) && ! empty($conf->global->OPENSTREETMAP_ENABLE_MAPS)) $showomap=1;
2780  if ($mode=='contact' && ! empty($conf->openstreetmap->enabled) && ! empty($conf->global->OPENSTREETMAP_ENABLE_MAPS_CONTACTS)) $showomap=1;
2781  if ($mode=='member' && ! empty($conf->openstreetmap->enabled) && ! empty($conf->global->OPENSTREETMAP_ENABLE_MAPS_MEMBERS)) $showomap=1;
2782 
2783  if ($showgmap)
2784  {
2785  $url=dol_buildpath('/google/gmaps.php?mode='.$mode.'&id='.$id,1);
2786  $out.=' <a href="'.$url.'" target="_gmaps"><img id="'.$htmlid.'" class="valigntextbottom" src="'.DOL_URL_ROOT.'/theme/common/gmap.png"></a>';
2787  }
2788  if ($showomap)
2789  {
2790  $url=dol_buildpath('/openstreetmap/maps.php?mode='.$mode.'&id='.$id,1);
2791  $out.=' <a href="'.$url.'" target="_gmaps"><img id="'.$htmlid.'_openstreetmap" class="valigntextbottom" src="'.DOL_URL_ROOT.'/theme/common/gmap.png"></a>';
2792  }
2793  }
2794  }
2795  if ($noprint) return $out;
2796  else print $out;
2797 }
2798 
2799 
2807 function isValidEmail($address, $acceptsupervisorkey=0)
2808 {
2809  if ($acceptsupervisorkey && $address == '__SUPERVISOREMAIL__') return true;
2810  if (filter_var($address, FILTER_VALIDATE_EMAIL)) return true;
2811 
2812  return false;
2813 }
2814 
2822 function isValidMXRecord($domain)
2823 {
2824  if (function_exists('idn_to_ascii') && function_exists('checkdnsrr'))
2825  {
2826  if (! checkdnsrr(idn_to_ascii($domain), 'MX'))
2827  {
2828  return 0;
2829  }
2830  if (function_exists('getmxrr'))
2831  {
2832  $mxhosts=array();
2833  $weight=array();
2834  getmxrr(idn_to_ascii($domain), $mxhosts, $weight);
2835  if (count($mxhosts) > 1) return 1;
2836  if (count($mxhosts) == 1 && ! empty($mxhosts[0])) return 1;
2837 
2838  return 0;
2839  }
2840  }
2841  return -1;
2842 }
2843 
2851 function isValidPhone($phone)
2852 {
2853  return true;
2854 }
2855 
2856 
2864 function dol_strlen($string, $stringencoding='UTF-8')
2865 {
2866  if (function_exists('mb_strlen')) return mb_strlen($string,$stringencoding);
2867  else return strlen($string);
2868 }
2869 
2880 function dol_substr($string, $start, $length, $stringencoding='', $trunconbytes=0)
2881 {
2882  global $langs;
2883 
2884  if (empty($stringencoding)) $stringencoding=$langs->charset_output;
2885 
2886  $ret='';
2887  if (empty($trunconbytes))
2888  {
2889  if (function_exists('mb_substr'))
2890  {
2891  $ret=mb_substr($string, $start, $length, $stringencoding);
2892  }
2893  else
2894  {
2895  $ret=substr($string, $start, $length);
2896  }
2897  }
2898  else
2899  {
2900  if (function_exists('mb_strcut'))
2901  {
2902  $ret=mb_strcut($string, $start, $length, $stringencoding);
2903  }
2904  else
2905  {
2906  $ret=substr($string, $start, $length);
2907  }
2908  }
2909  return $ret;
2910 }
2911 
2912 
2926 function dol_trunc($string,$size=40,$trunc='right',$stringencoding='UTF-8',$nodot=0, $display=0)
2927 {
2928  global $conf;
2929 
2930  if ($size==0 || ! empty($conf->global->MAIN_DISABLE_TRUNC)) return $string;
2931 
2932  if (empty($stringencoding)) $stringencoding='UTF-8';
2933  // reduce for small screen
2934  if ($conf->dol_optimize_smallscreen==1 && $display==1) $size = round($size/3);
2935 
2936  // We go always here
2937  if ($trunc == 'right')
2938  {
2939  $newstring=dol_textishtml($string)?dol_string_nohtmltag($string,1):$string;
2940  if (dol_strlen($newstring,$stringencoding) > ($size+($nodot?0:3))) // If nodot is 0 and size is 1,2 or 3 chars more, we don't trunc and don't add ...
2941  return dol_substr($newstring,0,$size,$stringencoding).($nodot?'':'...');
2942  else
2943  //return 'u'.$size.'-'.$newstring.'-'.dol_strlen($newstring,$stringencoding).'-'.$string;
2944  return $string;
2945  }
2946  elseif ($trunc == 'middle')
2947  {
2948  $newstring=dol_textishtml($string)?dol_string_nohtmltag($string,1):$string;
2949  if (dol_strlen($newstring,$stringencoding) > 2 && dol_strlen($newstring,$stringencoding) > ($size+1))
2950  {
2951  $size1=round($size/2);
2952  $size2=round($size/2);
2953  return dol_substr($newstring,0,$size1,$stringencoding).'...'.dol_substr($newstring,dol_strlen($newstring,$stringencoding) - $size2,$size2,$stringencoding);
2954  }
2955  else
2956  return $string;
2957  }
2958  elseif ($trunc == 'left')
2959  {
2960  $newstring=dol_textishtml($string)?dol_string_nohtmltag($string,1):$string;
2961  if (dol_strlen($newstring,$stringencoding) > ($size+($nodot?0:3))) // If nodot is 0 and size is 1,2 or 3 chars more, we don't trunc and don't add ...
2962  return '...'.dol_substr($newstring,dol_strlen($newstring,$stringencoding) - $size,$size,$stringencoding);
2963  else
2964  return $string;
2965  }
2966  elseif ($trunc == 'wrap')
2967  {
2968  $newstring=dol_textishtml($string)?dol_string_nohtmltag($string,1):$string;
2969  if (dol_strlen($newstring,$stringencoding) > ($size+1))
2970  return dol_substr($newstring,0,$size,$stringencoding)."\n".dol_trunc(dol_substr($newstring,$size,dol_strlen($newstring,$stringencoding)-$size,$stringencoding),$size,$trunc);
2971  else
2972  return $string;
2973  }
2974  else return 'BadParam3CallingDolTrunc';
2975 }
2976 
2995 function img_picto($titlealt, $picto, $moreatt = '', $pictoisfullpath = false, $srconly=0, $notitle=0, $alt='', $morecss='')
2996 {
2997  global $conf, $langs;
2998 
2999  // We forge fullpathpicto for image to $path/img/$picto. By default, we take DOL_URL_ROOT/theme/$conf->theme/img/$picto
3000  $url = DOL_URL_ROOT;
3001  $theme = $conf->theme;
3002  $path = 'theme/'.$theme;
3003 
3004  // Define fullpathpicto to use into src
3005  if ($pictoisfullpath) {
3006  // Clean parameters
3007  if (! preg_match('/(\.png|\.gif|\.svg)$/i',$picto)) {
3008  $picto .= '.png';
3009  }
3010  $fullpathpicto = $picto;
3011  }
3012  else {
3013  $pictowithoutext = preg_replace('/(\.png|\.gif|\.svg)$/', '', $picto);
3014 
3015  //if (in_array($picto, array('switch_off', 'switch_on', 'off', 'on')))
3016  if (empty($srconly) && in_array($pictowithoutext, array(
3017  'bank', 'close_title', 'delete', 'edit', 'ellipsis-h', 'filter', 'grip', 'grip_title', 'list', 'listlight', 'off', 'on', 'play', 'playdisabled', 'printer', 'resize',
3018  'note', 'split', 'switch_off', 'switch_on', 'unlink', 'uparrow', '1downarrow', '1uparrow',
3019  'jabber','skype','twitter','facebook'
3020  )
3021  )) {
3022  $fakey = $pictowithoutext;
3023  $facolor = ''; $fasize = '';
3024  $marginleftonlyshort = 2;
3025  if ($pictowithoutext == 'switch_off') {
3026  $fakey = 'fa-toggle-off';
3027  $facolor = '#999';
3028  $fasize = '2em';
3029  }
3030  elseif ($pictowithoutext == 'switch_on') {
3031  $fakey = 'fa-toggle-on';
3032  $facolor = '#227722';
3033  $fasize = '2em';
3034  }
3035  elseif ($pictowithoutext == 'off') {
3036  $fakey = 'fa-square-o';
3037  $fasize = '1.3em';
3038  }
3039  elseif ($pictowithoutext == 'on') {
3040  $fakey = 'fa-check-square-o';
3041  $fasize = '1.3em';
3042  }
3043  elseif ($pictowithoutext == 'bank') {
3044  $fakey = 'fa-bank';
3045  $facolor = '#444';
3046  }
3047  elseif ($pictowithoutext == 'close_title') {
3048  $fakey = 'fa-window-close';
3049  }
3050  elseif ($pictowithoutext == 'delete') {
3051  $fakey = 'fa-trash';
3052  $facolor = '#444';
3053  }
3054  elseif ($pictowithoutext == 'edit') {
3055  $fakey = 'fa-pencil';
3056  $facolor = '#444';
3057  }
3058  elseif ($pictowithoutext == 'filter') {
3059  $fakey = 'fa-'.$pictowithoutext;
3060  }
3061  elseif ($pictowithoutext == 'grip_title' || $pictowithoutext == 'grip') {
3062  $fakey = 'fa-arrows';
3063  }
3064  elseif ($pictowithoutext == 'listlight') {
3065  $fakey = 'fa-download';
3066  $facolor = '#999';
3067  $marginleftonlyshort=1;
3068  }
3069  elseif ($pictowithoutext == 'printer') {
3070  $fakey = 'fa-print';
3071  $fasize = '1.2em';
3072  $facolor = '#444';
3073  }
3074  elseif ($pictowithoutext == 'resize') {
3075  $fakey = 'fa-crop';
3076  $facolor = '#444';
3077  }
3078  elseif ($pictowithoutext == 'note') {
3079  $fakey = 'fa-sticky-note-o';
3080  $facolor = '#999';
3081  $marginleftonlyshort=1;
3082  }
3083  elseif ($pictowithoutext == 'uparrow') {
3084  $fakey = 'fa-mail-forward';
3085  $facolor = '#555';
3086  }
3087  elseif ($pictowithoutext == '1uparrow') {
3088  $fakey = 'fa-caret-up';
3089  $marginleftonlyshort = 1;
3090  }
3091  elseif ($pictowithoutext == '1downarrow') {
3092  $fakey = 'fa-caret-down';
3093  $marginleftonlyshort = 1;
3094  }
3095  elseif ($pictowithoutext == 'unlink') {
3096  $fakey = 'fa-chain-broken';
3097  $facolor = '#555';
3098  }
3099  elseif ($pictowithoutext == 'playdisabled') {
3100  $fakey = 'fa-play';
3101  $facolor = '#ccc';
3102  } elseif ($pictowithoutext == 'play') {
3103  $fakey = 'fa-play';
3104  $facolor = '#444';
3105  }
3106  elseif ($pictowithoutext == 'jabber') {
3107  $fakey = 'fa-comment-o';
3108  }
3109  elseif ($pictowithoutext == 'split') {
3110  $fakey = 'fa-code-fork';
3111  }
3112  else {
3113  $fakey = 'fa-'.$pictowithoutext;
3114  $facolor = '#444';
3115  $marginleftonlyshort=0;
3116  }
3117 
3118  $reg=array();
3119  if (preg_match('/class="([^"]+)"/', $moreatt, $reg)) {
3120  $morecss.= ($morecss?' ':'').$reg[1];
3121  }
3122  $enabledisablehtml = '<span class="fa '.$fakey.' '.($marginleftonlyshort?($marginleftonlyshort==1?'marginleftonlyshort':'marginleftonly'):'').' valignmiddle'.($morecss?' '.$morecss:'').'" style="'.($fasize?('font-size: '.$fasize.';'):'').($facolor?(' color: '.$facolor.';'):'').'" alt="'.dol_escape_htmltag($titlealt).'"'.(($notitle || empty($titlealt))?'':' title="'.dol_escape_htmltag($titlealt).'"').($moreatt?' '.$moreatt:'').'>';
3123  if (! empty($conf->global->MAIN_OPTIMIZEFORTEXTBROWSER)) {
3124  $enabledisablehtml.= $titlealt;
3125  }
3126  $enabledisablehtml.= '</span>';
3127 
3128  return $enabledisablehtml;
3129  }
3130 
3131  if (! empty($conf->global->MAIN_OVERWRITE_THEME_PATH)) {
3132  $path = $conf->global->MAIN_OVERWRITE_THEME_PATH.'/theme/'.$theme; // If the theme does not have the same name as the module
3133  }
3134  else if (! empty($conf->global->MAIN_OVERWRITE_THEME_RES)) {
3135  $path = $conf->global->MAIN_OVERWRITE_THEME_RES.'/theme/'.$conf->global->MAIN_OVERWRITE_THEME_RES; // To allow an external module to overwrite image resources whatever is activated theme
3136  }
3137  else if (! empty($conf->modules_parts['theme']) && array_key_exists($theme, $conf->modules_parts['theme'])) {
3138  $path = $theme.'/theme/'.$theme; // If the theme have the same name as the module
3139  }
3140 
3141  // If we ask an image into $url/$mymodule/img (instead of default path)
3142  if (preg_match('/^([^@]+)@([^@]+)$/i', $picto, $regs)) {
3143  $picto = $regs[1];
3144  $path = $regs[2]; // $path is $mymodule
3145  }
3146 
3147  // Clean parameters
3148  if (! preg_match('/(\.png|\.gif|\.svg)$/i',$picto)) {
3149  $picto .= '.png';
3150  }
3151  // If alt path are defined, define url where img file is, according to physical path
3152  // ex: array(["main"]=>"/home/maindir/htdocs", ["alt0"]=>"/home/moddir0/htdocs", ...)
3153  foreach ($conf->file->dol_document_root as $type => $dirroot) {
3154  if ($type == 'main') {
3155  continue;
3156  }
3157  // This need a lot of time, that's why enabling alternative dir like "custom" dir is not recommanded
3158  if (file_exists($dirroot.'/'.$path.'/img/'.$picto)) {
3159  $url = DOL_URL_ROOT.$conf->file->dol_url_root[$type];
3160  break;
3161  }
3162  }
3163 
3164  // $url is '' or '/custom', $path is current theme or
3165  $fullpathpicto = $url.'/'.$path.'/img/'.$picto;
3166  }
3167 
3168  if ($srconly) {
3169  return $fullpathpicto;
3170  }
3171  // tag title is used for tooltip on <a>, tag alt can be used with very simple text on image for bind people
3172  return '<img src="'.$fullpathpicto.'" alt="'.dol_escape_htmltag($alt).'"'.(($notitle || empty($titlealt))?'':' title="'.dol_escape_htmltag($titlealt).'"').($moreatt?' '.$moreatt:' class="inline-block'.($morecss?' '.$morecss:'').'"').'>'; // Alt is used for accessibility, title for popup
3173 }
3174 
3188 function img_object($titlealt, $picto, $moreatt = '', $pictoisfullpath = false, $srconly=0, $notitle=0)
3189 {
3190  return img_picto($titlealt, 'object_'.$picto, $moreatt, $pictoisfullpath, $srconly, $notitle);
3191 }
3192 
3203 function img_weather($titlealt, $picto, $moreatt = '', $pictoisfullpath = 0)
3204 {
3205  global $conf;
3206 
3207  if (! preg_match('/(\.png|\.gif)$/i', $picto)) $picto .= '.png';
3208 
3209  $path = DOL_URL_ROOT.'/theme/'.$conf->theme.'/img/weather/'.$picto;
3210 
3211  return img_picto($titlealt, $path, $moreatt, 1);
3212 }
3213 
3224 function img_picto_common($titlealt, $picto, $moreatt = '', $pictoisfullpath = 0)
3225 {
3226  global $conf;
3227 
3228  if (! preg_match('/(\.png|\.gif)$/i', $picto)) $picto .= '.png';
3229 
3230  if ($pictoisfullpath) $path = $picto;
3231  else
3232  {
3233  $path = DOL_URL_ROOT.'/theme/common/'.$picto;
3234 
3235  if (! empty($conf->global->MAIN_MODULE_CAN_OVERWRITE_COMMONICONS))
3236  {
3237  $themepath = DOL_DOCUMENT_ROOT.'/theme/'.$conf->theme.'/img/'.$picto;
3238 
3239  if (file_exists($themepath)) $path = $themepath;
3240  }
3241  }
3242 
3243  return img_picto($titlealt, $path, $moreatt, 1);
3244 }
3245 
3253 function img_action($titlealt, $numaction)
3254 {
3255  global $conf, $langs;
3256 
3257  if (empty($titlealt) || $titlealt == 'default')
3258  {
3259  if ($numaction == '-1' || $numaction == 'ST_NO') { $numaction = -1; $titlealt = $langs->transnoentitiesnoconv('ChangeDoNotContact'); }
3260  elseif ($numaction == '0' || $numaction == 'ST_NEVER') { $numaction = 0; $titlealt = $langs->transnoentitiesnoconv('ChangeNeverContacted'); }
3261  elseif ($numaction == '1' || $numaction == 'ST_TODO') { $numaction = 1; $titlealt = $langs->transnoentitiesnoconv('ChangeToContact'); }
3262  elseif ($numaction == '2' || $numaction == 'ST_PEND') { $numaction = 2; $titlealt = $langs->transnoentitiesnoconv('ChangeContactInProcess'); }
3263  elseif ($numaction == '3' || $numaction == 'ST_DONE') { $numaction = 3; $titlealt = $langs->transnoentitiesnoconv('ChangeContactDone'); }
3264  else { $titlealt = $langs->transnoentitiesnoconv('ChangeStatus '.$numaction); $numaction = 0; }
3265  }
3266  if (! is_numeric($numaction)) $numaction=0;
3267 
3268  return img_picto($titlealt, 'stcomm'.$numaction.'.png');
3269 }
3270 
3278 function img_pdf($titlealt = 'default', $size = 3)
3279 {
3280  global $conf, $langs;
3281 
3282  if ($titlealt == 'default') $titlealt = $langs->trans('Show');
3283 
3284  return img_picto($titlealt, 'pdf'.$size.'.png');
3285 }
3286 
3294 function img_edit_add($titlealt = 'default', $other = '')
3295 {
3296  global $conf, $langs;
3297 
3298  if ($titlealt == 'default') $titlealt = $langs->trans('Add');
3299 
3300  return img_picto($titlealt, 'edit_add.png', $other);
3301 }
3309 function img_edit_remove($titlealt = 'default', $other='')
3310 {
3311  global $conf, $langs;
3312 
3313  if ($titlealt == 'default') $titlealt = $langs->trans('Remove');
3314 
3315  return img_picto($titlealt, 'edit_remove.png', $other);
3316 }
3317 
3326 function img_edit($titlealt = 'default', $float = 0, $other = 'class="pictoedit"')
3327 {
3328  global $conf, $langs;
3329 
3330  if ($titlealt == 'default') $titlealt = $langs->trans('Modify');
3331 
3332  return img_picto($titlealt, 'edit.png', ($float ? 'style="float: '.($langs->tab_translate["DIRECTION"] == 'rtl'?'left':'right').'"' : "") . ($other?' '.$other:''));
3333 }
3334 
3343 function img_view($titlealt = 'default', $float = 0, $other = '')
3344 {
3345  global $conf, $langs;
3346 
3347  if ($titlealt == 'default') $titlealt = $langs->trans('View');
3348 
3349  $moreatt = ($float ? 'style="float: right" ' : '').$other;
3350 
3351  return img_picto($titlealt, 'view.png', $moreatt);
3352 }
3353 
3361 function img_delete($titlealt = 'default', $other = 'class="pictodelete"')
3362 {
3363  global $conf, $langs;
3364 
3365  if ($titlealt == 'default') $titlealt = $langs->trans('Delete');
3366 
3367  return img_picto($titlealt, 'delete.png', $other);
3368  //return '<span class="fa fa-trash fa-2x fa-fw" style="font-size: 1.7em;" title="'.$titlealt.'"></span>';
3369 }
3370 
3378 function img_printer($titlealt = "default", $other='')
3379 {
3380  global $conf,$langs;
3381  if ($titlealt=="default") $titlealt=$langs->trans("Print");
3382  return img_picto($titlealt,'printer.png',$other);
3383 }
3384 
3392 function img_split($titlealt = 'default', $other = 'class="pictosplit"')
3393 {
3394  global $conf, $langs;
3395 
3396  if ($titlealt == 'default') $titlealt = $langs->trans('Split');
3397 
3398  return img_picto($titlealt, 'split.png', $other);
3399 }
3400 
3408 function img_help($usehelpcursor = 1, $usealttitle = 1)
3409 {
3410  global $conf, $langs;
3411 
3412  if ($usealttitle)
3413  {
3414  if (is_string($usealttitle)) $usealttitle = dol_escape_htmltag($usealttitle);
3415  else $usealttitle = $langs->trans('Info');
3416  }
3417 
3418  return img_picto($usealttitle, 'info.png', 'style="vertical-align: middle;'.($usehelpcursor == 1 ? ' cursor: help': ($usehelpcursor == 2 ? ' cursor: pointer':'')).'"');
3419 }
3420 
3427 function img_info($titlealt = 'default')
3428 {
3429  global $conf, $langs;
3430 
3431  if ($titlealt == 'default') $titlealt = $langs->trans('Informations');
3432 
3433  return img_picto($titlealt, 'info.png', 'style="vertical-align: middle;"');
3434 }
3435 
3443 function img_warning($titlealt = 'default', $moreatt = '')
3444 {
3445  global $conf, $langs;
3446 
3447  if ($titlealt == 'default') $titlealt = $langs->trans('Warning');
3448 
3449  //return '<div class="imglatecoin">'.img_picto($titlealt, 'warning_white.png', 'class="pictowarning valignmiddle"'.($moreatt ? ($moreatt == '1' ? ' style="float: right"' : ' '.$moreatt): '')).'</div>';
3450  return img_picto($titlealt, 'warning.png', 'class="pictowarning valignmiddle"'.($moreatt ? ($moreatt == '1' ? ' style="float: right"' : ' '.$moreatt): ''));
3451 }
3452 
3459 function img_error($titlealt = 'default')
3460 {
3461  global $conf, $langs;
3462 
3463  if ($titlealt == 'default') $titlealt = $langs->trans('Error');
3464 
3465  return img_picto($titlealt, 'error.png', 'class="valigntextbottom"');
3466 }
3467 
3475 function img_next($titlealt = 'default', $moreatt='')
3476 {
3477  global $conf, $langs;
3478 
3479  if ($titlealt == 'default') $titlealt = $langs->trans('Next');
3480 
3481  //return img_picto($titlealt, 'next.png', $moreatt);
3482  return '<span class="fa fa-chevron-right paddingright paddingleft" title="'.dol_escape_htmltag($titlealt).'"></span>';
3483 }
3484 
3492 function img_previous($titlealt = 'default', $moreatt='')
3493 {
3494  global $conf, $langs;
3495 
3496  if ($titlealt == 'default') $titlealt = $langs->trans('Previous');
3497 
3498  //return img_picto($titlealt, 'previous.png', $moreatt);
3499  return '<span class="fa fa-chevron-left paddingright paddingleft" title="'.dol_escape_htmltag($titlealt).'"></span>';
3500 }
3501 
3510 function img_down($titlealt = 'default', $selected = 0, $moreclass='')
3511 {
3512  global $conf, $langs;
3513 
3514  if ($titlealt == 'default') $titlealt = $langs->trans('Down');
3515 
3516  return img_picto($titlealt, ($selected ? '1downarrow_selected.png' : '1downarrow.png'), 'class="imgdown'.($moreclass?" ".$moreclass:"").'"');
3517 }
3518 
3527 function img_up($titlealt = 'default', $selected = 0, $moreclass='')
3528 {
3529  global $conf, $langs;
3530 
3531  if ($titlealt == 'default') $titlealt = $langs->trans('Up');
3532 
3533  return img_picto($titlealt, ($selected ? '1uparrow_selected.png' : '1uparrow.png'), 'class="imgup'.($moreclass?" ".$moreclass:"").'"');
3534 }
3535 
3544 function img_left($titlealt = 'default', $selected = 0, $moreatt='')
3545 {
3546  global $conf, $langs;
3547 
3548  if ($titlealt == 'default') $titlealt = $langs->trans('Left');
3549 
3550  return img_picto($titlealt, ($selected ? '1leftarrow_selected.png' : '1leftarrow.png'), $moreatt);
3551 }
3552 
3561 function img_right($titlealt = 'default', $selected = 0, $moreatt='')
3562 {
3563  global $conf, $langs;
3564 
3565  if ($titlealt == 'default') $titlealt = $langs->trans('Right');
3566 
3567  return img_picto($titlealt, ($selected ? '1rightarrow_selected.png' : '1rightarrow.png'), $moreatt);
3568 }
3569 
3577 function img_allow($allow, $titlealt = 'default')
3578 {
3579  global $conf, $langs;
3580 
3581  if ($titlealt == 'default') $titlealt = $langs->trans('Active');
3582 
3583  if ($allow == 1) return img_picto($titlealt, 'tick.png');
3584 
3585  return '-';
3586 }
3587 
3594 function img_credit_card($brand)
3595 {
3596  if ($brand == 'Visa') {$brand='cc-visa';}
3597  elseif ($brand == 'MasterCard') {$brand='cc-mastercard';}
3598  elseif ($brand == 'American Express') {$brand='cc-amex';}
3599  elseif ($brand == 'Discover') {$brand='cc-discover';}
3600  elseif ($brand == 'JCB') {$brand='cc-jcb';}
3601  elseif ($brand == 'Diners Club') {$brand='cc-diners-club';}
3602  elseif (! in_array($brand, array('cc-visa','cc-mastercard','cc-amex','cc-discover','cc-jcb','cc-diners-club'))) {$brand='credit-card';}
3603 
3604  return '<span class="fa fa-'.$brand.' fa-2x fa-fw"></span>';
3605 }
3606 
3615 function img_mime($file, $titlealt = '', $morecss='')
3616 {
3617  require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
3618 
3619  $mimetype = dol_mimetype($file, '', 1);
3620  $mimeimg = dol_mimetype($file, '', 2);
3621  $mimefa = dol_mimetype($file, '', 4);
3622 
3623  if (empty($titlealt)) $titlealt = 'Mime type: '.$mimetype;
3624 
3625  //return img_picto_common($titlealt, 'mime/'.$mimeimg, 'class="'.$morecss.'"');
3626  return '<i class="fa fa-'.$mimefa.' paddingright"></i>';
3627 }
3628 
3629 
3640 function img_phone($titlealt = 'default', $option = 0)
3641 {
3642  dol_syslog(__FUNCTION__ . " is deprecated", LOG_WARNING);
3643 
3644  global $conf,$langs;
3645 
3646  if ($titlealt == 'default') $titlealt = $langs->trans('Call');
3647 
3648  if ($option == 1) $img = 'call';
3649  else $img = 'call_out';
3650 
3651  return img_picto($titlealt, $img);
3652 }
3653 
3661 function img_search($titlealt = 'default', $other = '')
3662 {
3663  global $conf, $langs;
3664 
3665  if ($titlealt == 'default') $titlealt = $langs->trans('Search');
3666 
3667  $img = img_picto($titlealt, 'search.png', $other, false, 1);
3668 
3669  $input = '<input type="image" class="liste_titre" name="button_search" src="'.$img.'" ';
3670  $input.= 'value="'.dol_escape_htmltag($titlealt).'" title="'.dol_escape_htmltag($titlealt).'" >';
3671 
3672  return $input;
3673 }
3674 
3682 function img_searchclear($titlealt = 'default', $other = '')
3683 {
3684  global $conf, $langs;
3685 
3686  if ($titlealt == 'default') $titlealt = $langs->trans('Search');
3687 
3688  $img = img_picto($titlealt, 'searchclear.png', $other, false, 1);
3689 
3690  $input = '<input type="image" class="liste_titre" name="button_removefilter" src="'.$img.'" ';
3691  $input.= 'value="'.dol_escape_htmltag($titlealt).'" title="'.dol_escape_htmltag($titlealt).'" >';
3692 
3693  return $input;
3694 }
3695 
3706 function info_admin($text, $infoonimgalt = 0, $nodiv=0, $admin='1', $morecss='')
3707 {
3708  global $conf, $langs;
3709 
3710  if ($infoonimgalt)
3711  {
3712  return img_picto($text, 'info', 'class="hideonsmartphone'.($morecss?' '.$morecss:'').'"');
3713  }
3714 
3715  return ($nodiv?'':'<div class="'.(empty($admin)?'':($admin=='1'?'info':$admin)).' hideonsmartphone'.($morecss?' '.$morecss:'').'">').'<span class="fa fa-info-circle" title="'.dol_escape_htmltag($admin?$langs->trans('InfoAdmin'):$langs->trans('Note')).'"></span> '.$text.($nodiv?'':'</div>');
3716 }
3717 
3718 
3731 function dol_print_error($db='',$error='',$errors=null)
3732 {
3733  global $conf,$langs,$argv;
3734  global $dolibarr_main_prod;
3735 
3736  $out = '';
3737  $syslog = '';
3738 
3739  // Si erreur intervenue avant chargement langue
3740  if (! $langs)
3741  {
3742  require_once DOL_DOCUMENT_ROOT .'/core/class/translate.class.php';
3743  $langs = new Translate('', $conf);
3744  $langs->load("main");
3745  }
3746  // Load translation files required by the page
3747  $langs->loadLangs(array('main', 'errors'));
3748 
3749  if ($_SERVER['DOCUMENT_ROOT']) // Mode web
3750  {
3751  $out.=$langs->trans("DolibarrHasDetectedError").".<br>\n";
3752  if (! empty($conf->global->MAIN_FEATURES_LEVEL)) $out.="You use an experimental or develop level of features, so please do NOT report any bugs, except if problem is confirmed moving option MAIN_FEATURES_LEVEL back to 0.<br>\n";
3753  $out.=$langs->trans("InformationToHelpDiagnose").":<br>\n";
3754 
3755  $out.="<b>".$langs->trans("Date").":</b> ".dol_print_date(time(),'dayhourlog')."<br>\n";
3756  $out.="<b>".$langs->trans("Dolibarr").":</b> ".DOL_VERSION."<br>\n";
3757  if (isset($conf->global->MAIN_FEATURES_LEVEL)) $out.="<b>".$langs->trans("LevelOfFeature").":</b> ".$conf->global->MAIN_FEATURES_LEVEL."<br>\n";
3758  if (function_exists("phpversion"))
3759  {
3760  $out.="<b>".$langs->trans("PHP").":</b> ".phpversion()."<br>\n";
3761  }
3762  $out.="<b>".$langs->trans("Server").":</b> ".$_SERVER["SERVER_SOFTWARE"]."<br>\n";
3763  if (function_exists("php_uname"))
3764  {
3765  $out.="<b>".$langs->trans("OS").":</b> ".php_uname()."<br>\n";
3766  }
3767  $out.="<b>".$langs->trans("UserAgent").":</b> ".$_SERVER["HTTP_USER_AGENT"]."<br>\n";
3768  $out.="<br>\n";
3769  $out.="<b>".$langs->trans("RequestedUrl").":</b> ".dol_htmlentities($_SERVER["REQUEST_URI"],ENT_COMPAT,'UTF-8')."<br>\n";
3770  $out.="<b>".$langs->trans("Referer").":</b> ".(isset($_SERVER["HTTP_REFERER"])?dol_htmlentities($_SERVER["HTTP_REFERER"],ENT_COMPAT,'UTF-8'):'')."<br>\n";
3771  $out.="<b>".$langs->trans("MenuManager").":</b> ".(isset($conf->standard_menu)?$conf->standard_menu:'')."<br>\n";
3772  $out.="<br>\n";
3773  $syslog.="url=".dol_escape_htmltag($_SERVER["REQUEST_URI"]);
3774  $syslog.=", query_string=".dol_escape_htmltag($_SERVER["QUERY_STRING"]);
3775  }
3776  else // Mode CLI
3777  {
3778  $out.='> '.$langs->transnoentities("ErrorInternalErrorDetected").":\n".$argv[0]."\n";
3779  $syslog.="pid=".dol_getmypid();
3780  }
3781 
3782  if (is_object($db))
3783  {
3784  if ($_SERVER['DOCUMENT_ROOT']) // Mode web
3785  {
3786  $out.="<b>".$langs->trans("DatabaseTypeManager").":</b> ".$db->type."<br>\n";
3787  $out.="<b>".$langs->trans("RequestLastAccessInError").":</b> ".($db->lastqueryerror()?dol_escape_htmltag($db->lastqueryerror()):$langs->trans("ErrorNoRequestInError"))."<br>\n";
3788  $out.="<b>".$langs->trans("ReturnCodeLastAccessInError").":</b> ".($db->lasterrno()?dol_escape_htmltag($db->lasterrno()):$langs->trans("ErrorNoRequestInError"))."<br>\n";
3789  $out.="<b>".$langs->trans("InformationLastAccessInError").":</b> ".($db->lasterror()?dol_escape_htmltag($db->lasterror()):$langs->trans("ErrorNoRequestInError"))."<br>\n";
3790  $out.="<br>\n";
3791  }
3792  else // Mode CLI
3793  {
3794  // No dol_escape_htmltag for output, we are in CLI mode
3795  $out.='> '.$langs->transnoentities("DatabaseTypeManager").":\n".$db->type."\n";
3796  $out.='> '.$langs->transnoentities("RequestLastAccessInError").":\n".($db->lastqueryerror()?$db->lastqueryerror():$langs->transnoentities("ErrorNoRequestInError"))."\n";
3797  $out.='> '.$langs->transnoentities("ReturnCodeLastAccessInError").":\n".($db->lasterrno()?$db->lasterrno():$langs->transnoentities("ErrorNoRequestInError"))."\n";
3798  $out.='> '.$langs->transnoentities("InformationLastAccessInError").":\n".($db->lasterror()?$db->lasterror():$langs->transnoentities("ErrorNoRequestInError"))."\n";
3799  }
3800  $syslog.=", sql=".$db->lastquery();
3801  $syslog.=", db_error=".$db->lasterror();
3802  }
3803 
3804  if ($error || $errors)
3805  {
3806  $langs->load("errors");
3807 
3808  // Merge all into $errors array
3809  if (is_array($error) && is_array($errors)) $errors=array_merge($error,$errors);
3810  elseif (is_array($error)) $errors=$error;
3811  elseif (is_array($errors)) $errors=array_merge(array($error),$errors);
3812  else $errors=array_merge(array($error));
3813 
3814  foreach($errors as $msg)
3815  {
3816  if (empty($msg)) continue;
3817  if ($_SERVER['DOCUMENT_ROOT']) // Mode web
3818  {
3819  $out.="<b>".$langs->trans("Message").":</b> ".dol_escape_htmltag($msg)."<br>\n" ;
3820  }
3821  else // Mode CLI
3822  {
3823  $out.='> '.$langs->transnoentities("Message").":\n".$msg."\n" ;
3824  }
3825  $syslog.=", msg=".$msg;
3826  }
3827  }
3828  if (empty($dolibarr_main_prod) && $_SERVER['DOCUMENT_ROOT'] && function_exists('xdebug_print_function_stack') && function_exists('xdebug_call_file'))
3829  {
3830  xdebug_print_function_stack();
3831  $out.='<b>XDebug informations:</b>'."<br>\n";
3832  $out.='File: '.xdebug_call_file()."<br>\n";
3833  $out.='Line: '.xdebug_call_line()."<br>\n";
3834  $out.='Function: '.xdebug_call_function()."<br>\n";
3835  $out.="<br>\n";
3836  }
3837 
3838  if (empty($dolibarr_main_prod)) print $out;
3839  else
3840  {
3841  print $langs->trans("DolibarrHasDetectedError").'. ';
3842  print $langs->trans("YouCanSetOptionDolibarrMainProdToZero");
3843  define("MAIN_CORE_ERROR", 1);
3844  }
3845  //else print 'Sorry, an error occured but the parameter $dolibarr_main_prod is defined in conf file so no message is reported to your browser. Please read the log file for error message.';
3846  dol_syslog("Error ".$syslog, LOG_ERR);
3847 }
3848 
3859 function dol_print_error_email($prefixcode, $errormessage='', $errormessages=array(), $morecss='error', $email='')
3860 {
3861  global $langs,$conf;
3862 
3863  if (empty($email)) $email=$conf->global->MAIN_INFO_SOCIETE_MAIL;
3864 
3865  $langs->load("errors");
3866  $now=dol_now();
3867 
3868  print '<br><div class="center login_main_message"><div class="'.$morecss.'">';
3869  print $langs->trans("ErrorContactEMail", $email, $prefixcode.dol_print_date($now,'%Y%m%d'));
3870  if ($errormessage) print '<br><br>'.$errormessage;
3871  if (is_array($errormessages) && count($errormessages))
3872  {
3873  foreach($errormessages as $mesgtoshow)
3874  {
3875  print '<br><br>'.$mesgtoshow;
3876  }
3877  }
3878  print '</div></div>';
3879 }
3880 
3896 function print_liste_field_titre($name, $file="", $field="", $begin="", $moreparam="", $moreattrib="", $sortfield="", $sortorder="", $prefix="", $tooltip="")
3897 {
3898  print getTitleFieldOfList($name, 0, $file, $field, $begin, $moreparam, $moreattrib, $sortfield, $sortorder, $prefix, 0, $tooltip);
3899 }
3900 
3918 function getTitleFieldOfList($name, $thead=0, $file="", $field="", $begin="", $moreparam="", $moreattrib="", $sortfield="", $sortorder="", $prefix="", $disablesortlink=0, $tooltip='')
3919 {
3920  global $conf, $langs, $form;
3921  //print "$name, $file, $field, $begin, $options, $moreattrib, $sortfield, $sortorder<br>\n";
3922 
3923  $sortorder=strtoupper($sortorder);
3924  $out='';
3925  $sortimg='';
3926 
3927  $tag='th';
3928  if ($thead==2) $tag='div';
3929 
3930  $tmpsortfield=explode(',',$sortfield);
3931  $sortfield1=trim($tmpsortfield[0]); // If $sortfield is 'd.datep,d.id', it becomes 'd.datep'
3932  $tmpfield=explode(',',$field);
3933  $field1=trim($tmpfield[0]); // If $field is 'd.datep,d.id', it becomes 'd.datep'
3934 
3935  //var_dump('field='.$field.' field1='.$field1.' sortfield='.$sortfield.' sortfield1='.$sortfield1);
3936  // If field is used as sort criteria we use a specific css class liste_titre_sel
3937  // Example if (sortfield,field)=("nom","xxx.nom") or (sortfield,field)=("nom","nom")
3938  if ($field1 && ($sortfield1 == $field1 || $sortfield1 == preg_replace("/^[^\.]+\./","",$field1))) $out.= '<'.$tag.' class="'.$prefix.'liste_titre_sel" '. $moreattrib.'>';
3939  else $out.= '<'.$tag.' class="'.$prefix.'liste_titre" '. $moreattrib.'>';
3940 
3941  if (empty($thead) && $field && empty($disablesortlink)) // If this is a sort field
3942  {
3943  $options=preg_replace('/sortfield=([a-zA-Z0-9,\s\.]+)/i','',$moreparam);
3944  $options=preg_replace('/sortorder=([a-zA-Z0-9,\s\.]+)/i','',$options);
3945  $options=preg_replace('/&+/i','&',$options);
3946  if (! preg_match('/^&/',$options)) $options='&'.$options;
3947 
3948  $sortordertouseinlink='';
3949  if ($field1 != $sortfield1) // We are on another field than current sorted field
3950  {
3951  if (preg_match('/^DESC/i', $sortorder))
3952  {
3953  $sortordertouseinlink.=str_repeat('desc,', count(explode(',',$field)));
3954  }
3955  else // We reverse the var $sortordertouseinlink
3956  {
3957  $sortordertouseinlink.=str_repeat('asc,', count(explode(',',$field)));
3958  }
3959  }
3960  else // We are on field that is the first current sorting criteria
3961  {
3962  if (preg_match('/^ASC/i', $sortorder)) // We reverse the var $sortordertouseinlink
3963  {
3964  $sortordertouseinlink.=str_repeat('desc,', count(explode(',',$field)));
3965  }
3966  else
3967  {
3968  $sortordertouseinlink.=str_repeat('asc,', count(explode(',',$field)));
3969  }
3970  }
3971  $sortordertouseinlink=preg_replace('/,$/', '', $sortordertouseinlink);
3972  $out.= '<a class="reposition" href="'.$file.'?sortfield='.$field.'&sortorder='.$sortordertouseinlink.'&begin='.$begin.$options.'">';
3973  }
3974 
3975  if ($tooltip) $out.=$form->textwithpicto($langs->trans($name), $langs->trans($tooltip));
3976  else $out.=$langs->trans($name);
3977 
3978  if (empty($thead) && $field && empty($disablesortlink)) // If this is a sort field
3979  {
3980  $out.='</a>';
3981  }
3982 
3983  if (empty($thead) && $field) // If this is a sort field
3984  {
3985  $options=preg_replace('/sortfield=([a-zA-Z0-9,\s\.]+)/i','',$moreparam);
3986  $options=preg_replace('/sortorder=([a-zA-Z0-9,\s\.]+)/i','',$options);
3987  $options=preg_replace('/&+/i','&',$options);
3988  if (! preg_match('/^&/',$options)) $options='&'.$options;
3989 
3990  if (! $sortorder || $field1 != $sortfield1)
3991  {
3992  //$out.= '<a href="'.$file.'?sortfield='.$field.'&sortorder=asc&begin='.$begin.$options.'">'.img_down("A-Z",0).'</a>';
3993  //$out.= '<a href="'.$file.'?sortfield='.$field.'&sortorder=desc&begin='.$begin.$options.'">'.img_up("Z-A",0).'</a>';
3994  }
3995  else
3996  {
3997  if (preg_match('/^DESC/', $sortorder)) {
3998  //$out.= '<a href="'.$file.'?sortfield='.$field.'&sortorder=asc&begin='.$begin.$options.'">'.img_down("A-Z",0).'</a>';
3999  //$out.= '<a href="'.$file.'?sortfield='.$field.'&sortorder=desc&begin='.$begin.$options.'">'.img_up("Z-A",1).'</a>';
4000  $sortimg.= '<span class="nowrap">'.img_up("Z-A",0).'</span>';
4001  }
4002  if (preg_match('/^ASC/', $sortorder)) {
4003  //$out.= '<a href="'.$file.'?sortfield='.$field.'&sortorder=asc&begin='.$begin.$options.'">'.img_down("A-Z",1).'</a>';
4004  //$out.= '<a href="'.$file.'?sortfield='.$field.'&sortorder=desc&begin='.$begin.$options.'">'.img_up("Z-A",0).'</a>';
4005  $sortimg.= '<span class="nowrap">'.img_down("A-Z",0).'</span>';
4006  }
4007  }
4008  }
4009 
4010  $out.=$sortimg;
4011 
4012  $out.='</'.$tag.'>';
4013 
4014  return $out;
4015 }
4016 
4025 function print_titre($title)
4026 {
4027  dol_syslog(__FUNCTION__ . " is deprecated", LOG_WARNING);
4028 
4029  print '<div class="titre">'.$title.'</div>';
4030 }
4031 
4043 function print_fiche_titre($title, $mesg='', $picto='title_generic.png', $pictoisfullpath=0, $id='')
4044 {
4045  print load_fiche_titre($title, $mesg, $picto, $pictoisfullpath, $id);
4046 }
4047 
4061 function load_fiche_titre($titre, $morehtmlright='', $picto='title_generic.png', $pictoisfullpath=0, $id='', $morecssontable='', $morehtmlcenter='')
4062 {
4063  global $conf;
4064 
4065  $return='';
4066 
4067  if ($picto == 'setup') $picto='title_generic.png';
4068 
4069  $return.= "\n";
4070  $return.= '<table '.($id?'id="'.$id.'" ':'').'summary="" class="centpercent notopnoleftnoright'.($morecssontable?' '.$morecssontable:'').'" style="margin-bottom: 6px;"><tr>'; // maring bottom must be same than into print_barre_list
4071  if ($picto) $return.= '<td class="nobordernopadding widthpictotitle opacityhigh" valign="middle">'.img_picto('',$picto, 'class="valignmiddle widthpictotitle pictotitle"', $pictoisfullpath).'</td>';
4072  $return.= '<td class="nobordernopadding valignmiddle">';
4073  $return.= '<div class="titre inline-block">'.$titre.'</div>';
4074  $return.= '</td>';
4075  if (dol_strlen($morehtmlcenter))
4076  {
4077  $return.= '<td class="nobordernopadding" align="center" valign="middle">'.$morehtmlcenter.'</td>';
4078  }
4079  if (dol_strlen($morehtmlright))
4080  {
4081  $return.= '<td class="nobordernopadding titre_right wordbreak" align="right" valign="middle">'.$morehtmlright.'</td>';
4082  }
4083  $return.= '</tr></table>'."\n";
4084 
4085  return $return;
4086 }
4087 
4109 function print_barre_liste($titre, $page, $file, $options='', $sortfield='', $sortorder='', $morehtmlcenter='', $num=-1, $totalnboflines='', $picto='title_generic.png', $pictoisfullpath=0, $morehtmlright='', $morecss='', $limit=-1, $hideselectlimit=0, $hidenavigation=0)
4110 {
4111  global $conf,$langs;
4112 
4113  $savlimit = $limit;
4114  $savtotalnboflines = $totalnboflines;
4115  $totalnboflines=abs($totalnboflines);
4116 
4117  if ($picto == 'setup') $picto='title_setup.png';
4118  if (($conf->browser->name == 'ie') && $picto=='title_generic.png') $picto='title.gif';
4119  if ($limit < 0) $limit = $conf->liste_limit;
4120  if ($savlimit != 0 && (($num > $limit) || ($num == -1) || ($limit == 0)))
4121  {
4122  $nextpage = 1;
4123  }
4124  else
4125  {
4126  $nextpage = 0;
4127  }
4128  //print 'totalnboflines='.$totalnboflines.'-savlimit='.$savlimit.'-limit='.$limit.'-num='.$num.'-nextpage='.$nextpage;
4129 
4130  print "\n";
4131  print "<!-- Begin title '".$titre."' -->\n";
4132  print '<table border="0" class="centpercent notopnoleftnoright'.($morecss?' '.$morecss:'').'" style="margin-bottom: 6px;"><tr>'; // maring bottom must be same than into load_fiche_tire
4133 
4134  // Left
4135  //if ($picto && $titre) print '<td class="nobordernopadding hideonsmartphone" width="40" align="left" valign="middle">'.img_picto('', $picto, 'id="pictotitle"', $pictoisfullpath).'</td>';
4136  print '<td class="nobordernopadding valignmiddle">';
4137  if ($picto && $titre) print img_picto('', $picto, 'class="hideonsmartphone valignmiddle opacityhigh pictotitle widthpictotitle"', $pictoisfullpath);
4138  print '<div class="titre inline-block">'.$titre;
4139  if (!empty($titre) && $savtotalnboflines >= 0 && (string) $savtotalnboflines != '') print ' ('.$totalnboflines.')';
4140  print '</div></td>';
4141 
4142  // Center
4143  if ($morehtmlcenter)
4144  {
4145  print '<td class="nobordernopadding center valignmiddle">'.$morehtmlcenter.'</td>';
4146  }
4147 
4148  // Right
4149  print '<td class="nobordernopadding valignmiddle" align="right">';
4150  if ($sortfield) $options .= "&sortfield=".urlencode($sortfield);
4151  if ($sortorder) $options .= "&sortorder=".urlencode($sortorder);
4152  // Show navigation bar
4153  $pagelist = '';
4154  if ($savlimit != 0 && ($page > 0 || $num > $limit))
4155  {
4156  if ($totalnboflines) // If we know total nb of lines
4157  {
4158  // Define nb of extra page links before and after selected page + ... + first or last
4159  $maxnbofpage=(empty($conf->dol_optimize_smallscreen) ? 4 : 1);
4160 
4161  if ($limit > 0) $nbpages=ceil($totalnboflines/$limit);
4162  else $nbpages=1;
4163  $cpt=($page-$maxnbofpage);
4164  if ($cpt < 0) { $cpt=0; }
4165 
4166  if ($cpt>=1)
4167  {
4168  $pagelist.= '<li'.(($conf->dol_use_jmobile != 4)?' class="pagination"':'').'><a href="'.$file.'?page=0'.$options.'">1</a></li>';
4169  if ($cpt > 2) $pagelist.='<li'.(($conf->dol_use_jmobile != 4)?' class="pagination"':'').'><span '.(($conf->dol_use_jmobile != 4)?'class="inactive"':'').'>...</span></li>';
4170  else if ($cpt == 2) $pagelist.='<li'.(($conf->dol_use_jmobile != 4)?' class="pagination"':'').'><a href="'.$file.'?page=1'.$options.'">2</a></li>';
4171  }
4172 
4173  do
4174  {
4175  if ($cpt==$page)
4176  {
4177  $pagelist.= '<li'.(($conf->dol_use_jmobile != 4)?' class="pagination"':'').'><span '.(($conf->dol_use_jmobile != 4)?'class="active"':'').'>'.($page+1).'</span></li>';
4178  }
4179  else
4180  {
4181  $pagelist.= '<li'.(($conf->dol_use_jmobile != 4)?' class="pagination"':'').'><a href="'.$file.'?page='.$cpt.$options.'">'.($cpt+1).'</a></li>';
4182  }
4183  $cpt++;
4184  }
4185  while ($cpt < $nbpages && $cpt<=$page+$maxnbofpage);
4186 
4187  if ($cpt<$nbpages)
4188  {
4189  if ($cpt<$nbpages-2) $pagelist.= '<li'.(($conf->dol_use_jmobile != 4)?' class="pagination"':'').'><span '.(($conf->dol_use_jmobile != 4)?'class="inactive"':'').'>...</span></li>';
4190  else if ($cpt == $nbpages-2) $pagelist.= '<li'.(($conf->dol_use_jmobile != 4)?' class="pagination"':'').'><a href="'.$file.'?page='.($nbpages-2).$options.'">'.($nbpages - 1).'</a></li>';
4191  $pagelist.= '<li'.(($conf->dol_use_jmobile != 4)?' class="pagination"':'').'><a href="'.$file.'?page='.($nbpages-1).$options.'">'.$nbpages.'</a></li>';
4192  }
4193  }
4194  else
4195  {
4196  $pagelist.= '<li'.(($conf->dol_use_jmobile != 4)?' class="pagination"':'').'><span '.(($conf->dol_use_jmobile != 4)?'class="active"':'').'>'.($page+1)."</li>";
4197  }
4198  }
4199 
4200  print_fleche_navigation($page, $file, $options, $nextpage, $pagelist, $morehtmlright, $savlimit, $totalnboflines, $hideselectlimit); // output the div and ul for previous/last completed with page numbers into $pagelist
4201 
4202  print '</td>';
4203 
4204  print '</tr></table>'."\n";
4205  print "<!-- End title -->\n\n";
4206 }
4207 
4222 function print_fleche_navigation($page, $file, $options='', $nextpage=0, $betweenarrows='', $afterarrows='', $limit=-1, $totalnboflines=0, $hideselectlimit=0)
4223 {
4224  global $conf, $langs;
4225 
4226  print '<div class="pagination"><ul>';
4227  if ((int) $limit >= 0 && empty($hideselectlimit))
4228  {
4229  $pagesizechoices='10:10,15:15,20:20,30:30,40:40,50:50,100:100,250:250,500:500,1000:1000,5000:5000';
4230  //$pagesizechoices.=',0:'.$langs->trans("All"); // Not yet supported
4231  //$pagesizechoices.=',2:2';
4232  if (! empty($conf->global->MAIN_PAGESIZE_CHOICES)) $pagesizechoices=$conf->global->MAIN_PAGESIZE_CHOICES;
4233 
4234  print '<li class="pagination">';
4235  print '<select class="flat selectlimit" name="limit" title="'.dol_escape_htmltag($langs->trans("MaxNbOfRecordPerPage")).'">';
4236  $tmpchoice=explode(',',$pagesizechoices);
4237  $tmpkey=$limit.':'.$limit;
4238  if (! in_array($tmpkey, $tmpchoice)) $tmpchoice[]=$tmpkey;
4239  $tmpkey=$conf->liste_limit.':'.$conf->liste_limit;
4240  if (! in_array($tmpkey, $tmpchoice)) $tmpchoice[]=$tmpkey;
4241  asort($tmpchoice, SORT_NUMERIC);
4242  $found=false;
4243  foreach($tmpchoice as $val)
4244  {
4245  $selected='';
4246  $tmp=explode(':',$val);
4247  $key=$tmp[0];
4248  $val=$tmp[1];
4249  if ($key != '' && $val != '')
4250  {
4251  if ((int) $key == (int) $limit)
4252  {
4253  $selected = ' selected="selected"';
4254  $found = true;
4255  }
4256  print '<option name="'.$key.'"'.$selected.'>'.dol_escape_htmltag($val).'</option>'."\n";
4257  }
4258  }
4259  print '</select>';
4260  if ($conf->use_javascript_ajax)
4261  {
4262  print '<!-- JS CODE TO ENABLE select limit to launch submit of page -->
4263  <script type="text/javascript">
4264  jQuery(document).ready(function () {
4265  jQuery(".selectlimit").change(function() {
4266  console.log("Change limit. Send submit");
4267  $(this).parents(\'form:first\').submit();
4268  });
4269  });
4270  </script>
4271  ';
4272  }
4273  print '</li>';
4274  }
4275  if ($page > 0)
4276  {
4277  print '<li class="pagination"><a class="paginationprevious" href="'.$file.'?page='.($page-1).$options.'"><i class="fa fa-chevron-left" title="'.dol_escape_htmltag($langs->trans("Previous")).'"></i></a></li>';
4278  }
4279  if ($betweenarrows)
4280  {
4281  print $betweenarrows;
4282  }
4283  if ($nextpage > 0)
4284  {
4285  print '<li class="pagination"><a class="paginationnext" href="'.$file.'?page='.($page+1).$options.'"><i class="fa fa-chevron-right" title="'.dol_escape_htmltag($langs->trans("Next")).'"></i></a></li>';
4286  }
4287  if ($afterarrows)
4288  {
4289  print '<li class="paginationafterarrows">';
4290  print $afterarrows;
4291  print '</li>';
4292  }
4293  print '</ul></div>'."\n";
4294 }
4295 
4296 
4307 function vatrate($rate, $addpercent=false, $info_bits=0, $usestarfornpr=0)
4308 {
4309  $morelabel='';
4310 
4311  if (preg_match('/%/',$rate))
4312  {
4313  $rate=str_replace('%','',$rate);
4314  $addpercent=true;
4315  }
4316  if (preg_match('/\((.*)\)/',$rate,$reg))
4317  {
4318  $morelabel=' ('.$reg[1].')';
4319  $rate=preg_replace('/\s*'.preg_quote($morelabel,'/').'/','',$rate);
4320  }
4321  if (preg_match('/\*/',$rate))
4322  {
4323  $rate=str_replace('*','',$rate);
4324  $info_bits |= 1;
4325  }
4326 
4327  // If rate is '9/9/9' we don't change it. If rate is '9.000' we apply price()
4328  if (! preg_match('/\//', $rate)) $ret=price($rate,0,'',0,0).($addpercent?'%':'');
4329  else
4330  {
4331  // TODO Split on / and output with a price2num to have clean numbers without ton of 000.
4332  $ret=$rate.($addpercent?'%':'');
4333  }
4334  if (($info_bits & 1) && $usestarfornpr >= 0) $ret.=' *';
4335  $ret.=$morelabel;
4336  return $ret;
4337 }
4338 
4339 
4355 function price($amount, $form=0, $outlangs='', $trunc=1, $rounding=-1, $forcerounding=-1, $currency_code='')
4356 {
4357  global $langs,$conf;
4358 
4359  // Clean parameters
4360  if (empty($amount)) $amount=0; // To have a numeric value if amount not defined or = ''
4361  $amount = (is_numeric($amount)?$amount:0); // Check if amount is numeric, for example, an error occured when amount value = o (letter) instead 0 (number)
4362  if ($rounding < 0) $rounding=min($conf->global->MAIN_MAX_DECIMALS_UNIT,$conf->global->MAIN_MAX_DECIMALS_TOT);
4363  $nbdecimal=$rounding;
4364 
4365  // Output separators by default (french)
4366  $dec=','; $thousand=' ';
4367 
4368  // If $outlangs not forced, we use use language
4369  if (! is_object($outlangs)) $outlangs=$langs;
4370 
4371  if ($outlangs->transnoentitiesnoconv("SeparatorDecimal") != "SeparatorDecimal") $dec=$outlangs->transnoentitiesnoconv("SeparatorDecimal");
4372  if ($outlangs->transnoentitiesnoconv("SeparatorThousand")!= "SeparatorThousand") $thousand=$outlangs->transnoentitiesnoconv("SeparatorThousand");
4373  if ($thousand == 'None') $thousand='';
4374  else if ($thousand == 'Space') $thousand=' ';
4375  //print "outlangs=".$outlangs->defaultlang." amount=".$amount." html=".$form." trunc=".$trunc." nbdecimal=".$nbdecimal." dec='".$dec."' thousand='".$thousand."'<br>";
4376 
4377  //print "amount=".$amount."-";
4378  $amount = str_replace(',','.',$amount); // should be useless
4379  //print $amount."-";
4380  $datas = explode('.',$amount);
4381  $decpart = isset($datas[1])?$datas[1]:'';
4382  $decpart = preg_replace('/0+$/i','',$decpart); // Supprime les 0 de fin de partie decimale
4383  //print "decpart=".$decpart."<br>";
4384  $end='';
4385 
4386  // We increase nbdecimal if there is more decimal than asked (to not loose information)
4387  if (dol_strlen($decpart) > $nbdecimal) $nbdecimal=dol_strlen($decpart);
4388  // Si on depasse max
4389  if ($trunc && $nbdecimal > $conf->global->MAIN_MAX_DECIMALS_SHOWN)
4390  {
4391  $nbdecimal=$conf->global->MAIN_MAX_DECIMALS_SHOWN;
4392  if (preg_match('/\.\.\./i',$conf->global->MAIN_MAX_DECIMALS_SHOWN))
4393  {
4394  // Si un affichage est tronque, on montre des ...
4395  $end='...';
4396  }
4397  }
4398 
4399  // If force rounding
4400  if ($forcerounding >= 0) $nbdecimal = $forcerounding;
4401 
4402  // Format number
4403  $output=number_format($amount, $nbdecimal, $dec, $thousand);
4404  if ($form)
4405  {
4406  $output=preg_replace('/\s/','&nbsp;',$output);
4407  $output=preg_replace('/\'/','&#039;',$output);
4408  }
4409  // Add symbol of currency if requested
4410  $cursymbolbefore=$cursymbolafter='';
4411  if ($currency_code)
4412  {
4413  if ($currency_code == 'auto') $currency_code=$conf->currency;
4414 
4415  $listofcurrenciesbefore=array('USD','GBP','AUD','MXN','PEN','CNY');
4416  $listoflanguagesbefore=array('nl_NL');
4417  if (in_array($currency_code, $listofcurrenciesbefore) || in_array($outlangs->defaultlang, $listoflanguagesbefore))
4418  {
4419  $cursymbolbefore.=$outlangs->getCurrencySymbol($currency_code);
4420  }
4421  else
4422  {
4423  $tmpcur=$outlangs->getCurrencySymbol($currency_code);
4424  $cursymbolafter.=($tmpcur == $currency_code ? ' '.$tmpcur : $tmpcur);
4425  }
4426  }
4427  $output=$cursymbolbefore.$output.$end.($cursymbolafter?' ':'').$cursymbolafter;
4428 
4429  return $output;
4430 }
4431 
4447 function price2num($amount,$rounding='',$alreadysqlnb=0)
4448 {
4449  global $langs,$conf;
4450 
4451  // Round PHP function does not allow number like '1,234.56' nor '1.234,56' nor '1 234,56'
4452  // Numbers must be '1234.56'
4453  // Decimal delimiter for PHP and database SQL requests must be '.'
4454  $dec=','; $thousand=' ';
4455  if ($langs->transnoentitiesnoconv("SeparatorDecimal") != "SeparatorDecimal") $dec=$langs->transnoentitiesnoconv("SeparatorDecimal");
4456  if ($langs->transnoentitiesnoconv("SeparatorThousand")!= "SeparatorThousand") $thousand=$langs->transnoentitiesnoconv("SeparatorThousand");
4457  if ($thousand == 'None') $thousand='';
4458  elseif ($thousand == 'Space') $thousand=' ';
4459  //print "amount=".$amount." html=".$form." trunc=".$trunc." nbdecimal=".$nbdecimal." dec='".$dec."' thousand='".$thousand."'<br>";
4460 
4461  // Convert value to universal number format (no thousand separator, '.' as decimal separator)
4462  if ($alreadysqlnb != 1) // If not a PHP number or unknown, we change format
4463  {
4464  //print 'PP'.$amount.' - '.$dec.' - '.$thousand.' - '.intval($amount).'<br>';
4465 
4466  // Convert amount to format with dolibarr dec and thousand (this is because PHP convert a number
4467  // to format defined by LC_NUMERIC after a calculation and we want source format to be like defined by Dolibarr setup.
4468  if (is_numeric($amount))
4469  {
4470  // We put in temps value of decimal ("0.00001"). Works with 0 and 2.0E-5 and 9999.10
4471  $temps=sprintf("%0.10F",$amount-intval($amount)); // temps=0.0000000000 or 0.0000200000 or 9999.1000000000
4472  $temps=preg_replace('/([\.1-9])0+$/','\\1',$temps); // temps=0. or 0.00002 or 9999.1
4473  $nbofdec=max(0,dol_strlen($temps)-2); // -2 to remove "0."
4474  $amount=number_format($amount,$nbofdec,$dec,$thousand);
4475  }
4476  //print "QQ".$amount.'<br>';
4477 
4478  // Now make replace (the main goal of function)
4479  if ($thousand != ',' && $thousand != '.') $amount=str_replace(',','.',$amount); // To accept 2 notations for french users
4480  $amount=str_replace(' ','',$amount); // To avoid spaces
4481  $amount=str_replace($thousand,'',$amount); // Replace of thousand before replace of dec to avoid pb if thousand is .
4482  $amount=str_replace($dec,'.',$amount);
4483  }
4484 
4485  // Now, make a rounding if required
4486  if ($rounding)
4487  {
4488  $nbofdectoround='';
4489  if ($rounding == 'MU') $nbofdectoround=$conf->global->MAIN_MAX_DECIMALS_UNIT;
4490  elseif ($rounding == 'MT') $nbofdectoround=$conf->global->MAIN_MAX_DECIMALS_TOT;
4491  elseif ($rounding == 'MS') $nbofdectoround=empty($conf->global->MAIN_MAX_DECIMALS_STOCK)?5:$conf->global->MAIN_MAX_DECIMALS_STOCK;
4492  elseif (is_numeric($rounding)) $nbofdectoround=$rounding;
4493  //print "RR".$amount.' - '.$nbofdectoround.'<br>';
4494  if (dol_strlen($nbofdectoround)) $amount = round($amount,$nbofdectoround); // $nbofdectoround can be 0.
4495  else return 'ErrorBadParameterProvidedToFunction';
4496  //print 'SS'.$amount.' - '.$nbofdec.' - '.$dec.' - '.$thousand.' - '.$nbofdectoround.'<br>';
4497 
4498  // Convert amount to format with dolibarr dec and thousand (this is because PHP convert a number
4499  // to format defined by LC_NUMERIC after a calculation and we want source format to be defined by Dolibarr setup.
4500  if (is_numeric($amount))
4501  {
4502  // We put in temps value of decimal ("0.00001"). Works with 0 and 2.0E-5 and 9999.10
4503  $temps=sprintf("%0.10F",$amount-intval($amount)); // temps=0.0000000000 or 0.0000200000 or 9999.1000000000
4504  $temps=preg_replace('/([\.1-9])0+$/','\\1',$temps); // temps=0. or 0.00002 or 9999.1
4505  $nbofdec=max(0,dol_strlen($temps)-2); // -2 to remove "0."
4506  $amount=number_format($amount,min($nbofdec,$nbofdectoround),$dec,$thousand); // Convert amount to format with dolibarr dec and thousand
4507  }
4508  //print "TT".$amount.'<br>';
4509 
4510  // Always make replace because each math function (like round) replace
4511  // with local values and we want a number that has a SQL string format x.y
4512  if ($thousand != ',' && $thousand != '.') $amount=str_replace(',','.',$amount); // To accept 2 notations for french users
4513  $amount=str_replace(' ','',$amount); // To avoid spaces
4514  $amount=str_replace($thousand,'',$amount); // Replace of thousand before replace of dec to avoid pb if thousand is .
4515  $amount=str_replace($dec,'.',$amount);
4516  }
4517 
4518  return $amount;
4519 }
4520 
4521 
4533 function showDimensionInBestUnit($dimension, $unit, $type, $outputlangs, $round=-1, $forceunitoutput='no')
4534 {
4535  require_once DOL_DOCUMENT_ROOT.'/core/lib/product.lib.php';
4536 
4537  if (($forceunitoutput == 'no' && $dimension < 1/10000 && $unit < 90) || (is_numeric($forceunitoutput) && $forceunitoutput == -6))
4538  {
4539  $dimension = $dimension * 1000000;
4540  $unit = $unit - 6;
4541  }
4542  elseif (($forceunitoutput == 'no' && $dimension < 1/10 && $unit < 90) || (is_numeric($forceunitoutput) && $forceunitoutput == -3))
4543  {
4544  $dimension = $dimension * 1000;
4545  $unit = $unit - 3;
4546  }
4547  elseif (($forceunitoutput == 'no' && $dimension > 100000000 && $unit < 90) || (is_numeric($forceunitoutput) && $forceunitoutput == 6))
4548  {
4549  $dimension = $dimension / 1000000;
4550  $unit = $unit + 6;
4551  }
4552  elseif (($forceunitoutput == 'no' && $dimension > 100000 && $unit < 90) || (is_numeric($forceunitoutput) && $forceunitoutput == 3))
4553  {
4554  $dimension = $dimension / 1000;
4555  $unit = $unit + 3;
4556  }
4557  // Special case when we want output unit into pound or ounce
4558  /* TODO
4559  if ($unit < 90 && $type == 'weight' && is_numeric($forceunitoutput) && (($forceunitoutput == 98) || ($forceunitoutput == 99))
4560  {
4561  $dimension = // convert dimension from standard unit into ounce or pound
4562  $unit = $forceunitoutput;
4563  }
4564  if ($unit > 90 && $type == 'weight' && is_numeric($forceunitoutput) && $forceunitoutput < 90)
4565  {
4566  $dimension = // convert dimension from standard unit into ounce or pound
4567  $unit = $forceunitoutput;
4568  }*/
4569 
4570  $ret=price($dimension, 0, $outputlangs, 0, 0, $round).' '.measuring_units_string($unit, $type);
4571 
4572  return $ret;
4573 }
4574 
4575 
4588 function get_localtax($vatrate, $local, $thirdparty_buyer="", $thirdparty_seller="", $vatnpr=0)
4589 {
4590  global $db, $conf, $mysoc;
4591 
4592  if (empty($thirdparty_seller) || ! is_object($thirdparty_seller)) $thirdparty_seller=$mysoc;
4593 
4594  dol_syslog("get_localtax tva=".$vatrate." local=".$local." thirdparty_buyer id=".(is_object($thirdparty_buyer)?$thirdparty_buyer->id:'')."/country_code=".(is_object($thirdparty_buyer)?$thirdparty_buyer->country_code:'')." thirdparty_seller id=".$thirdparty_seller->id."/country_code=".$thirdparty_seller->country_code." thirdparty_seller localtax1_assuj=".$thirdparty_seller->localtax1_assuj." thirdparty_seller localtax2_assuj=".$thirdparty_seller->localtax2_assuj);
4595 
4596  $vatratecleaned = $vatrate;
4597  if (preg_match('/^(.*)\s*\((.*)\)$/', $vatrate, $reg)) // If vat is "xx (yy)"
4598  {
4599  $vatratecleaned = trim($reg[1]);
4600  $vatratecode = $reg[2];
4601  }
4602 
4603  /*if ($thirdparty_buyer->country_code != $thirdparty_seller->country_code)
4604  {
4605  return 0;
4606  }*/
4607 
4608  // Some test to guess with no need to make database access
4609  if ($mysoc->country_code == 'ES') // For spain localtaxes 1 and 2, tax is qualified if buyer use local tax
4610  {
4611  if ($local == 1)
4612  {
4613  if (! $mysoc->localtax1_assuj || (string) $vatratecleaned == "0") return 0;
4614  if ($thirdparty_seller->id == $mysoc->id)
4615  {
4616  if (! $thirdparty_buyer->localtax1_assuj) return 0;
4617  }
4618  else
4619  {
4620  if (! $thirdparty_seller->localtax1_assuj) return 0;
4621  }
4622  }
4623 
4624  if ($local == 2)
4625  {
4626  //if (! $mysoc->localtax2_assuj || (string) $vatratecleaned == "0") return 0;
4627  if (! $mysoc->localtax2_assuj) return 0; // If main vat is 0, IRPF may be different than 0.
4628  if ($thirdparty_seller->id == $mysoc->id)
4629  {
4630  if (! $thirdparty_buyer->localtax2_assuj) return 0;
4631  }
4632  else
4633  {
4634  if (! $thirdparty_seller->localtax2_assuj) return 0;
4635  }
4636  }
4637  }
4638  else
4639  {
4640  if ($local == 1 && ! $thirdparty_seller->localtax1_assuj) return 0;
4641  if ($local == 2 && ! $thirdparty_seller->localtax2_assuj) return 0;
4642  }
4643 
4644  // For some country MAIN_GET_LOCALTAXES_VALUES_FROM_THIRDPARTY is forced to on.
4645  if (in_array($mysoc->country_code, array('ES')))
4646  {
4647  $conf->global->MAIN_GET_LOCALTAXES_VALUES_FROM_THIRDPARTY = 1;
4648  }
4649 
4650  // Search local taxes
4651  if (! empty($conf->global->MAIN_GET_LOCALTAXES_VALUES_FROM_THIRDPARTY))
4652  {
4653  if ($local==1)
4654  {
4655  if ($thirdparty_seller != $mysoc)
4656  {
4657  if (!isOnlyOneLocalTax($local)) // TODO We should provide $vatrate to search on correct line and not always on line with highest vat rate
4658  {
4659  return $thirdparty_seller->localtax1_value;
4660  }
4661  }
4662  else // i am the seller
4663  {
4664  if (!isOnlyOneLocalTax($local)) // TODO If seller is me, why not always returning this, even if there is only one locatax vat.
4665  {
4666  return $conf->global->MAIN_INFO_VALUE_LOCALTAX1;
4667  }
4668  }
4669  }
4670  if ($local==2)
4671  {
4672  if ($thirdparty_seller != $mysoc)
4673  {
4674  if (!isOnlyOneLocalTax($local)) // TODO We should provide $vatrate to search on correct line and not always on line with highest vat rate
4675  // TODO We should also return value defined on thirdparty only if defined
4676  {
4677  return $thirdparty_seller->localtax2_value;
4678  }
4679  }
4680  else // i am the seller
4681  {
4682  if (in_array($mysoc->country_code, array('ES')))
4683  {
4684  return $thirdparty_buyer->localtax2_value;
4685  }
4686  else
4687  {
4688  return $conf->global->MAIN_INFO_VALUE_LOCALTAX2;
4689  }
4690  }
4691  }
4692  }
4693 
4694  // By default, search value of local tax on line of common tax
4695  $sql = "SELECT t.localtax1, t.localtax2, t.localtax1_type, t.localtax2_type";
4696  $sql .= " FROM ".MAIN_DB_PREFIX."c_tva as t, ".MAIN_DB_PREFIX."c_country as c";
4697  $sql .= " WHERE t.fk_pays = c.rowid AND c.code = '".$thirdparty_seller->country_code."'";
4698  $sql .= " AND t.taux = ".((float) $vatratecleaned)." AND t.active = 1";
4699  if ($vatratecode) $sql.= " AND t.code ='".$vatratecode."'"; // If we have the code, we use it in priority
4700  else $sql.= " AND t.recuperableonly ='".$vatnpr."'";
4701  dol_syslog("get_localtax", LOG_DEBUG);
4702  $resql=$db->query($sql);
4703 
4704  if ($resql)
4705  {
4706  $obj = $db->fetch_object($resql);
4707  if ($local==1) return $obj->localtax1;
4708  elseif ($local==2) return $obj->localtax2;
4709  }
4710 
4711  return 0;
4712 }
4713 
4714 
4723 function isOnlyOneLocalTax($local)
4724 {
4725  $tax=get_localtax_by_third($local);
4726 
4727  $valors=explode(":", $tax);
4728 
4729  if (count($valors)>1)
4730  {
4731  return false;
4732  }
4733  else
4734  {
4735  return true;
4736  }
4737 }
4738 
4745 function get_localtax_by_third($local)
4746 {
4747  global $db, $mysoc;
4748  $sql ="SELECT t.localtax1, t.localtax2 ";
4749  $sql.=" FROM ".MAIN_DB_PREFIX."c_tva as t inner join ".MAIN_DB_PREFIX."c_country as c ON c.rowid=t.fk_pays";
4750  $sql.=" WHERE c.code = '".$mysoc->country_code."' AND t.active = 1 AND t.taux=(";
4751  $sql.=" SELECT max(tt.taux) FROM ".MAIN_DB_PREFIX."c_tva as tt inner join ".MAIN_DB_PREFIX."c_country as c ON c.rowid=tt.fk_pays";
4752  $sql.=" WHERE c.code = '".$mysoc->country_code."' AND tt.active = 1";
4753  $sql.=" )";
4754 
4755  $resql=$db->query($sql);
4756  if ($resql)
4757  {
4758  $obj = $db->fetch_object($resql);
4759  if ($local==1) return $obj->localtax1;
4760  elseif ($local==2) return $obj->localtax2;
4761  }
4762 
4763  return 0;
4764 }
4765 
4766 
4778 function getTaxesFromId($vatrate, $buyer=null, $seller=null, $firstparamisid=1)
4779 {
4780  global $db, $mysoc;
4781 
4782  dol_syslog("getTaxesFromId vat id or rate = ".$vatrate);
4783 
4784  // Search local taxes
4785  $sql = "SELECT t.rowid, t.code, t.taux as rate, t.recuperableonly as npr, t.accountancy_code_sell, t.accountancy_code_buy";
4786  $sql.= " FROM ".MAIN_DB_PREFIX."c_tva as t";
4787  if ($firstparamisid) $sql.= " WHERE t.rowid = ".(int) $vatrate;
4788  else
4789  {
4790  $vatratecleaned = $vatrate;
4791  $vatratecode = '';
4792  if (preg_match('/^(.*)\s*\((.*)\)$/', $vatrate, $reg)) // If vat is "xx (yy)"
4793  {
4794  $vatratecleaned = $reg[1];
4795  $vatratecode = $reg[2];
4796  }
4797 
4798  $sql.=", ".MAIN_DB_PREFIX."c_country as c";
4799  /*if ($mysoc->country_code == 'ES') $sql.= " WHERE t.fk_pays = c.rowid AND c.code = '".$buyer->country_code."'"; // vat in spain use the buyer country ??
4800  else $sql.= " WHERE t.fk_pays = c.rowid AND c.code = '".$seller->country_code."'";*/
4801  $sql.= " WHERE t.fk_pays = c.rowid AND c.code = '".$seller->country_code."'";
4802  $sql.= " AND t.taux = ".((float) $vatratecleaned)." AND t.active = 1";
4803  if ($vatratecode) $sql.= " AND t.code = '".$vatratecode."'";
4804  }
4805 
4806  $resql=$db->query($sql);
4807  if ($resql)
4808  {
4809  $obj = $db->fetch_object($resql);
4810  if ($obj) return array('rowid'=>$obj->rowid, 'code'=>$obj->code, 'rate'=>$obj->rate, 'npr'=>$obj->npr, 'accountancy_code_sell'=>$obj->accountancy_code_sell, 'accountancy_code_buy'=>$obj->accountancy_code_buy);
4811  else return array();
4812  }
4813  else dol_print_error($db);
4814 
4815  return array();
4816 }
4817 
4834 function getLocalTaxesFromRate($vatrate, $local, $buyer, $seller, $firstparamisid=0)
4835 {
4836  global $db, $mysoc;
4837 
4838  dol_syslog("getLocalTaxesFromRate vatrate=".$vatrate." local=".$local);
4839 
4840  // Search local taxes
4841  $sql = "SELECT t.localtax1, t.localtax1_type, t.localtax2, t.localtax2_type, t.accountancy_code_sell, t.accountancy_code_buy";
4842  $sql .= " FROM ".MAIN_DB_PREFIX."c_tva as t";
4843  if ($firstparamisid) $sql.= " WHERE t.rowid = ".(int) $vatrate;
4844  else
4845  {
4846  $vatratecleaned = $vatrate;
4847  $vatratecode = '';
4848  if (preg_match('/^(.*)\s*\((.*)\)$/', $vatrate, $reg)) // If vat is "x.x (yy)"
4849  {
4850  $vatratecleaned = $reg[1];
4851  $vatratecode = $reg[2];
4852  }
4853 
4854  $sql.=", ".MAIN_DB_PREFIX."c_country as c";
4855  if ($mysoc->country_code == 'ES') $sql .= " WHERE t.fk_pays = c.rowid AND c.code = '".$buyer->country_code."'"; // local tax in spain use the buyer country ??
4856  else $sql .= " WHERE t.fk_pays = c.rowid AND c.code = '".$seller->country_code."'";
4857  $sql.= " AND t.taux = ".((float) $vatratecleaned)." AND t.active = 1";
4858  if ($vatratecode) $sql.= " AND t.code = '".$vatratecode."'";
4859  }
4860 
4861  $resql=$db->query($sql);
4862  if ($resql)
4863  {
4864  $obj = $db->fetch_object($resql);
4865  if ($local == 1)
4866  {
4867  return array($obj->localtax1_type, get_localtax($vatrate, $local, $buyer, $seller), $obj->accountancy_code_sell, $obj->accountancy_code_buy);
4868  }
4869  elseif ($local == 2)
4870  {
4871  return array($obj->localtax2_type, get_localtax($vatrate, $local, $buyer, $seller),$obj->accountancy_code_sell, $obj->accountancy_code_buy);
4872  }
4873  else
4874  {
4875  return array($obj->localtax1_type, get_localtax($vatrate, 1, $buyer, $seller), $obj->localtax2_type, get_localtax($vatrate, 2, $buyer, $seller), $obj->accountancy_code_sell,$obj->accountancy_code_buy);
4876  }
4877  }
4878 
4879  return 0;
4880 }
4881 
4892 function get_product_vat_for_country($idprod, $thirdparty_seller, $idprodfournprice=0)
4893 {
4894  global $db,$conf,$mysoc;
4895 
4896  require_once DOL_DOCUMENT_ROOT . '/product/class/product.class.php';
4897 
4898  $ret=0;
4899  $found=0;
4900 
4901  if ($idprod > 0)
4902  {
4903  // Load product
4904  $product=new Product($db);
4905  $result=$product->fetch($idprod);
4906 
4907  if ($mysoc->country_code == $thirdparty_seller->country_code) // If selling country is ours
4908  {
4909  if ($idprodfournprice > 0) // We want vat for product for a "supplier" object
4910  {
4911  $product->get_buyprice($idprodfournprice,0,0,0);
4912  $ret=$product->vatrate_supplier;
4913  if ($product->default_vat_code) $ret.=' ('.$product->default_vat_code.')';
4914  }
4915  else
4916  {
4917  $ret=$product->tva_tx; // Default vat of product we defined
4918  if ($product->default_vat_code) $ret.=' ('.$product->default_vat_code.')';
4919  }
4920  $found=1;
4921  }
4922  else
4923  {
4924  // TODO Read default product vat according to countrycode and product. Vat for couple countrycode/product is a feature not implemeted yet.
4925  // May be usefull/required if hidden option SERVICE_ARE_ECOMMERCE_200238EC is on
4926  }
4927  }
4928 
4929  if (! $found)
4930  {
4931  if (empty($conf->global->MAIN_VAT_DEFAULT_IF_AUTODETECT_FAILS))
4932  {
4933  // If vat of product for the country not found or not defined, we return the first higher vat of country.
4934  $sql = "SELECT t.taux as vat_rate, t.code as default_vat_code";
4935  $sql.= " FROM ".MAIN_DB_PREFIX."c_tva as t, ".MAIN_DB_PREFIX."c_country as c";
4936  $sql.= " WHERE t.active=1 AND t.fk_pays = c.rowid AND c.code='".$thirdparty_seller->country_code."'";
4937  $sql.= " ORDER BY t.taux DESC, t.code ASC, t.recuperableonly ASC";
4938  $sql.= $db->plimit(1);
4939 
4940  $resql=$db->query($sql);
4941  if ($resql)
4942  {
4943  $obj=$db->fetch_object($resql);
4944  if ($obj)
4945  {
4946  $ret=$obj->vat_rate;
4947  if ($obj->default_vat_code) $ret.=' ('.$obj->default_vat_code.')';
4948  }
4949  $db->free($sql);
4950  }
4951  else dol_print_error($db);
4952  }
4953  else $ret=$conf->global->MAIN_VAT_DEFAULT_IF_AUTODETECT_FAILS; // Forced value if autodetect fails
4954  }
4955 
4956  dol_syslog("get_product_vat_for_country: ret=".$ret);
4957  return $ret;
4958 }
4959 
4969 function get_product_localtax_for_country($idprod, $local, $thirdparty_seller)
4970 {
4971  global $db,$mysoc;
4972 
4973  if (! class_exists('Product')) {
4974  require_once DOL_DOCUMENT_ROOT . '/product/class/product.class.php';
4975  }
4976 
4977  $ret=0;
4978  $found=0;
4979 
4980  if ($idprod > 0)
4981  {
4982  // Load product
4983  $product=new Product($db);
4984  $result=$product->fetch($idprod);
4985 
4986  if ($mysoc->country_code == $thirdparty_seller->country_code) // If selling country is ours
4987  {
4988  /* Not defined yet, so we don't use this
4989  if ($local==1) $ret=$product->localtax1_tx;
4990  elseif ($local==2) $ret=$product->localtax2_tx;
4991  $found=1;
4992  */
4993  }
4994  else
4995  {
4996  // TODO Read default product vat according to countrycode and product
4997  }
4998  }
4999 
5000  if (! $found)
5001  {
5002  // If vat of product for the country not found or not defined, we return higher vat of country.
5003  $sql = "SELECT taux as vat_rate, localtax1, localtax2";
5004  $sql.= " FROM ".MAIN_DB_PREFIX."c_tva as t, ".MAIN_DB_PREFIX."c_country as c";
5005  $sql.= " WHERE t.active=1 AND t.fk_pays = c.rowid AND c.code='".$thirdparty_seller->country_code."'";
5006  $sql.= " ORDER BY t.taux DESC, t.recuperableonly ASC";
5007  $sql.= $db->plimit(1);
5008 
5009  $resql=$db->query($sql);
5010  if ($resql)
5011  {
5012  $obj=$db->fetch_object($resql);
5013  if ($obj)
5014  {
5015  if ($local==1) $ret=$obj->localtax1;
5016  elseif ($local==2) $ret=$obj->localtax2;
5017  }
5018  }
5019  else dol_print_error($db);
5020  }
5021 
5022  dol_syslog("get_product_localtax_for_country: ret=".$ret);
5023  return $ret;
5024 }
5025 
5042 function get_default_tva(Societe $thirdparty_seller, Societe $thirdparty_buyer, $idprod=0, $idprodfournprice=0)
5043 {
5044  global $conf;
5045 
5046  require_once DOL_DOCUMENT_ROOT.'/core/lib/company.lib.php';
5047 
5048  // Note: possible values for tva_assuj are 0/1 or franchise/reel
5049  $seller_use_vat=((is_numeric($thirdparty_seller->tva_assuj) && ! $thirdparty_seller->tva_assuj) || (! is_numeric($thirdparty_seller->tva_assuj) && $thirdparty_seller->tva_assuj=='franchise'))?0:1;
5050 
5051  $seller_country_code = $thirdparty_seller->country_code;
5052  $seller_in_cee = isInEEC($thirdparty_seller);
5053 
5054  $buyer_country_code = $thirdparty_buyer->country_code;
5055  $buyer_in_cee = isInEEC($thirdparty_buyer);
5056 
5057  dol_syslog("get_default_tva: seller use vat=".$seller_use_vat.", seller country=".$seller_country_code.", seller in cee=".$seller_in_cee.", buyer vat number=".$thirdparty_buyer->tva_intra." buyer country=".$buyer_country_code.", buyer in cee=".$buyer_in_cee.", idprod=".$idprod.", idprodfournprice=".$idprodfournprice.", SERVICE_ARE_ECOMMERCE_200238EC=".(! empty($conf->global->SERVICES_ARE_ECOMMERCE_200238EC)?$conf->global->SERVICES_ARE_ECOMMERCE_200238EC:''));
5058 
5059  // If services are eServices according to EU Council Directive 2002/38/EC (http://ec.europa.eu/taxation_customs/taxation/vat/traders/e-commerce/article_1610_en.htm)
5060  // we use the buyer VAT.
5061  if (! empty($conf->global->SERVICE_ARE_ECOMMERCE_200238EC))
5062  {
5063  if ($seller_in_cee && $buyer_in_cee && ! $thirdparty_buyer->isACompany())
5064  {
5065  //print 'VATRULE 0';
5066  return get_product_vat_for_country($idprod,$thirdparty_buyer,$idprodfournprice);
5067  }
5068  }
5069 
5070  // If seller does not use VAT
5071  if (! $seller_use_vat)
5072  {
5073  //print 'VATRULE 1';
5074  return 0;
5075  }
5076 
5077  // Le test ci-dessus ne devrait pas etre necessaire. Me signaler l'exemple du cas juridique concerne si le test suivant n'est pas suffisant.
5078 
5079  // Si le (pays vendeur = pays acheteur) alors la TVA par defaut=TVA du produit vendu. Fin de regle.
5080  if (($seller_country_code == $buyer_country_code)
5081  || (in_array($seller_country_code,array('FR,MC')) && in_array($buyer_country_code,array('FR','MC')))) // Warning ->country_code not always defined
5082  {
5083  //print 'VATRULE 2';
5084  return get_product_vat_for_country($idprod,$thirdparty_seller,$idprodfournprice);
5085  }
5086 
5087  // Si (vendeur et acheteur dans Communaute europeenne) et (bien vendu = moyen de transports neuf comme auto, bateau, avion) alors TVA par defaut=0 (La TVA doit etre paye par l'acheteur au centre d'impots de son pays et non au vendeur). Fin de regle.
5088  // Not supported
5089 
5090  // Si (vendeur et acheteur dans Communaute europeenne) et (acheteur = entreprise) alors TVA par defaut=0. Fin de regle
5091  // Si (vendeur et acheteur dans Communaute europeenne) et (acheteur = particulier) alors TVA par defaut=TVA du produit vendu. Fin de regle
5092  if (($seller_in_cee && $buyer_in_cee))
5093  {
5094  $isacompany=$thirdparty_buyer->isACompany();
5095  if ($isacompany)
5096  {
5097  //print 'VATRULE 3';
5098  return 0;
5099  }
5100  else
5101  {
5102  //print 'VATRULE 4';
5103  return get_product_vat_for_country($idprod,$thirdparty_seller,$idprodfournprice);
5104  }
5105  }
5106 
5107  // Si (vendeur en France et acheteur hors Communaute europeenne et acheteur particulier) alors TVA par defaut=TVA du produit vendu. Fin de regle
5108  if (! empty($conf->global->MAIN_USE_VAT_OF_PRODUCT_FOR_INDIVIDUAL_CUSTOMER_OUT_OF_EEC) && empty($buyer_in_cee) && !$thirdparty_buyer->isACompany()) {
5109  return get_product_vat_for_country($idprod,$thirdparty_seller,$idprodfournprice);
5110  }
5111 
5112  // Sinon la TVA proposee par defaut=0. Fin de regle.
5113  // Rem: Cela signifie qu'au moins un des 2 est hors Communaute europeenne et que le pays differe
5114  //print 'VATRULE 5';
5115  return 0;
5116 }
5117 
5118 
5129 function get_default_npr(Societe $thirdparty_seller, Societe $thirdparty_buyer, $idprod=0, $idprodfournprice=0)
5130 {
5131  global $db;
5132 
5133  if ($idprodfournprice > 0)
5134  {
5135  if (! class_exists('ProductFournisseur'))
5136  require_once DOL_DOCUMENT_ROOT . '/fourn/class/fournisseur.product.class.php';
5137  $prodprice = new ProductFournisseur($db);
5138  $prodprice->fetch_product_fournisseur_price($idprodfournprice);
5139  return $prodprice->fourn_tva_npr;
5140  }
5141  elseif ($idprod > 0)
5142  {
5143  if (! class_exists('Product'))
5144  require_once DOL_DOCUMENT_ROOT . '/product/class/product.class.php';
5145  $prod = new Product($db);
5146  $prod->fetch($idprod);
5147  return $prod->tva_npr;
5148  }
5149 
5150  return 0;
5151 }
5152 
5166 function get_default_localtax($thirdparty_seller, $thirdparty_buyer, $local, $idprod=0)
5167 {
5168  global $mysoc;
5169 
5170  if (!is_object($thirdparty_seller)) return -1;
5171  if (!is_object($thirdparty_buyer)) return -1;
5172 
5173  if ($local==1) // Localtax 1
5174  {
5175  if ($mysoc->country_code == 'ES')
5176  {
5177  if (is_numeric($thirdparty_buyer->localtax1_assuj) && ! $thirdparty_buyer->localtax1_assuj) return 0;
5178  }
5179  else
5180  {
5181  // Si vendeur non assujeti a Localtax1, localtax1 par default=0
5182  if (is_numeric($thirdparty_seller->localtax1_assuj) && ! $thirdparty_seller->localtax1_assuj) return 0;
5183  if (! is_numeric($thirdparty_seller->localtax1_assuj) && $thirdparty_seller->localtax1_assuj=='localtax1off') return 0;
5184  }
5185  }
5186  elseif ($local==2) //I Localtax 2
5187  {
5188  // Si vendeur non assujeti a Localtax2, localtax2 par default=0
5189  if (is_numeric($thirdparty_seller->localtax2_assuj) && ! $thirdparty_seller->localtax2_assuj) return 0;
5190  if (! is_numeric($thirdparty_seller->localtax2_assuj) && $thirdparty_seller->localtax2_assuj=='localtax2off') return 0;
5191  }
5192 
5193  if ($thirdparty_seller->country_code == $thirdparty_buyer->country_code)
5194  {
5195  return get_product_localtax_for_country($idprod, $local, $thirdparty_seller);
5196  }
5197 
5198  return 0;
5199 }
5200 
5209 function yn($yesno, $case=1, $color=0)
5210 {
5211  global $langs;
5212  $result='unknown'; $classname='';
5213  if ($yesno == 1 || strtolower($yesno) == 'yes' || strtolower($yesno) == 'true') // A mettre avant test sur no a cause du == 0
5214  {
5215  $result=$langs->trans('yes');
5216  if ($case == 1 || $case == 3) $result=$langs->trans("Yes");
5217  if ($case == 2) $result='<input type="checkbox" value="1" checked disabled>';
5218  if ($case == 3) $result='<input type="checkbox" value="1" checked disabled> '.$result;
5219 
5220  $classname='ok';
5221  }
5222  elseif ($yesno == 0 || strtolower($yesno) == 'no' || strtolower($yesno) == 'false')
5223  {
5224  $result=$langs->trans("no");
5225  if ($case == 1 || $case == 3) $result=$langs->trans("No");
5226  if ($case == 2) $result='<input type="checkbox" value="0" disabled>';
5227  if ($case == 3) $result='<input type="checkbox" value="0" disabled> '.$result;
5228 
5229  if ($color == 2) $classname='ok';
5230  else $classname='error';
5231  }
5232  if ($color) return '<font class="'.$classname.'">'.$result.'</font>';
5233  return $result;
5234 }
5235 
5236 
5252 function get_exdir($num, $level, $alpha, $withoutslash, $object, $modulepart)
5253 {
5254  global $conf;
5255 
5256  $path = '';
5257 
5258  $arrayforoldpath=array('cheque','user','category','holiday','supplier_invoice','invoice_supplier','mailing','supplier_payment');
5259  if (! empty($conf->global->PRODUCT_USE_OLD_PATH_FOR_PHOTO)) $arrayforoldpath[]='product';
5260  if (! empty($level) && in_array($modulepart, $arrayforoldpath))
5261  {
5262  // This part should be removed once all code is using "get_exdir" to forge path, with all parameters provided.
5263  if (empty($alpha)) $num = preg_replace('/([^0-9])/i','',$num);
5264  else $num = preg_replace('/^.*\-/i','',$num);
5265  $num = substr("000".$num, -$level);
5266  if ($level == 1) $path = substr($num,0,1);
5267  if ($level == 2) $path = substr($num,1,1).'/'.substr($num,0,1);
5268  if ($level == 3) $path = substr($num,2,1).'/'.substr($num,1,1).'/'.substr($num,0,1);
5269  }
5270  else
5271  {
5272  // TODO
5273  // We will enhance here a common way of forging path for document storage
5274  // Here, object->id, object->ref and modulepart are required.
5275  //var_dump($modulepart);
5276  if (in_array($modulepart, array('thirdparty','contact','member','propal','proposal','commande','order','facture','invoice',
5277  'supplier_order','supplier_proposal','shipment','contract','expensereport')))
5278  {
5279  $path=($object->ref?$object->ref:$object->id);
5280  }
5281  }
5282 
5283  if (empty($withoutslash) && ! empty($path)) $path.='/';
5284 
5285  return $path;
5286 }
5287 
5296 function dol_mkdir($dir, $dataroot='', $newmask=null)
5297 {
5298  global $conf;
5299 
5300  dol_syslog("functions.lib::dol_mkdir: dir=".$dir,LOG_INFO);
5301 
5302  $dir_osencoded=dol_osencode($dir);
5303  if (@is_dir($dir_osencoded)) return 0;
5304 
5305  $nberr=0;
5306  $nbcreated=0;
5307 
5308  $ccdir='';
5309  if (! empty($dataroot)) {
5310  // Remove data root from loop
5311  $dir = str_replace($dataroot.'/', '', $dir);
5312  $ccdir = $dataroot.'/';
5313  }
5314 
5315  $cdir = explode("/", $dir);
5316  $num=count($cdir);
5317  for ($i = 0; $i < $num; $i++)
5318  {
5319  if ($i > 0) $ccdir .= '/'.$cdir[$i];
5320  else $ccdir .= $cdir[$i];
5321  if (preg_match("/^.:$/",$ccdir,$regs)) continue; // Si chemin Windows incomplet, on poursuit par rep suivant
5322 
5323  // Attention, le is_dir() peut echouer bien que le rep existe.
5324  // (ex selon config de open_basedir)
5325  if ($ccdir)
5326  {
5327  $ccdir_osencoded=dol_osencode($ccdir);
5328  if (! @is_dir($ccdir_osencoded))
5329  {
5330  dol_syslog("functions.lib::dol_mkdir: Directory '".$ccdir."' does not exists or is outside open_basedir PHP setting.",LOG_DEBUG);
5331 
5332  umask(0);
5333  $dirmaskdec=octdec($newmask);
5334  if (empty($newmask)) {
5335  $dirmaskdec = empty( $conf->global->MAIN_UMASK ) ? octdec( '0755' ) : octdec( $conf->global->MAIN_UMASK );
5336  }
5337  $dirmaskdec |= octdec('0111'); // Set x bit required for directories
5338  if (! @mkdir($ccdir_osencoded, $dirmaskdec))
5339  {
5340  // Si le is_dir a renvoye une fausse info, alors on passe ici.
5341  dol_syslog("functions.lib::dol_mkdir: Fails to create directory '".$ccdir."' or directory already exists.",LOG_WARNING);
5342  $nberr++;
5343  }
5344  else
5345  {
5346  dol_syslog("functions.lib::dol_mkdir: Directory '".$ccdir."' created",LOG_DEBUG);
5347  $nberr=0; // On remet a zero car si on arrive ici, cela veut dire que les echecs precedents peuvent etre ignore
5348  $nbcreated++;
5349  }
5350  }
5351  else
5352  {
5353  $nberr=0; // On remet a zero car si on arrive ici, cela veut dire que les echecs precedents peuvent etre ignores
5354  }
5355  }
5356  }
5357  return ($nberr ? -$nberr : $nbcreated);
5358 }
5359 
5360 
5366 function picto_required()
5367 {
5368  return '<span class="fieldrequired">*</span>';
5369 }
5370 
5371 
5387 function dol_string_nohtmltag($stringtoclean, $removelinefeed=1, $pagecodeto='UTF-8', $strip_tags=0)
5388 {
5389  if ($removelinefeed == 2) $stringtoclean = preg_replace('/<br[^>]*>\n+/ims', '<br>', $stringtoclean);
5390  $temp = preg_replace('/<br[^>]*>/i', "\n", $stringtoclean);
5391 
5392  if ($strip_tags) {
5393  $temp = strip_tags($temp);
5394  } else {
5395  $pattern = "/<[^<>]+>/";
5396  // Exemple of $temp: <a href="/myurl" title="<u>A title</u>">0000-021</a>
5397  $temp = preg_replace($pattern,"",$temp); // pass 1
5398  // $temp after pass 1: <a href="/myurl" title="A title">0000-021
5399  $temp = preg_replace($pattern,"",$temp); // pass 2
5400  // $temp after pass 2: 0000-021
5401  }
5402 
5403  $temp = dol_html_entity_decode($temp,ENT_COMPAT,$pagecodeto);
5404 
5405  // Supprime aussi les retours
5406  if ($removelinefeed == 1) $temp=str_replace(array("\r\n","\r","\n")," ",$temp);
5407 
5408  // et les espaces doubles
5409  while (strpos($temp," "))
5410  {
5411  $temp = str_replace(" "," ",$temp);
5412  }
5413 
5414  return trim($temp);
5415 }
5416 
5425 function dol_string_onlythesehtmltags($stringtoclean)
5426 {
5427  $allowed_tags = array(
5428  "html", "head", "meta", "body", "article", "a", "b", "br", "div", "em", "font", "img", "ins", "hr", "i", "li", "link",
5429  "ol", "p", "s", "section", "span", "strong", "title",
5430  "table", "tr", "th", "td", "u", "ul"
5431  );
5432 
5433  $allowed_tags_string = join("><", $allowed_tags);
5434  $allowed_tags_string = preg_replace('/^>/','',$allowed_tags_string);
5435  $allowed_tags_string = preg_replace('/<$/','',$allowed_tags_string);
5436 
5437  $temp = strip_tags($stringtoclean, $allowed_tags_string);
5438 
5439  return $temp;
5440 }
5441 
5451 function dol_string_neverthesehtmltags($stringtoclean, $disallowed_tags=array('textarea'))
5452 {
5453  $temp = $stringtoclean;
5454  foreach($disallowed_tags as $tagtoremove)
5455  {
5456  $temp = preg_replace('/<\/?'.$tagtoremove.'>/', '', $temp);
5457  $temp = preg_replace('/<\/?'.$tagtoremove.'\s+[^>]*>/', '', $temp);
5458  }
5459  return $temp;
5460 }
5461 
5462 
5471 function dolGetFirstLineOfText($text, $nboflines=1)
5472 {
5473  if ($nboflines == 1)
5474  {
5475  if (dol_textishtml($text))
5476  {
5477  $firstline=preg_replace('/<br[^>]*>.*$/s','',$text); // The s pattern modifier means the . can match newline characters
5478  $firstline=preg_replace('/<div[^>]*>.*$/s','',$firstline); // The s pattern modifier means the . can match newline characters
5479  }
5480  else
5481  {
5482  $firstline=preg_replace('/[\n\r].*/','',$text);
5483  }
5484  return $firstline.((strlen($firstline) != strlen($text))?'...':'');
5485  }
5486  else
5487  {
5488  $ishtml=0;
5489  if (dol_textishtml($text))
5490  {
5491  $text=preg_replace('/\n/','',$text);
5492  $ishtml=1;
5493  $repTable = array("\t" => " ", "\n" => " ", "\r" => " ", "\0" => " ", "\x0B" => " ");
5494  }
5495  else
5496  {
5497  $repTable = array("\t" => " ", "\n" => "<br>", "\r" => " ", "\0" => " ", "\x0B" => " ");
5498  }
5499 
5500  $text = strtr($text, $repTable);
5501  if ($charset == 'UTF-8') { $pattern = '/(<br[^>]*>)/Uu'; } // /U is to have UNGREEDY regex to limit to one html tag. /u is for UTF8 support
5502  else $pattern = '/(<br[^>]*>)/U'; // /U is to have UNGREEDY regex to limit to one html tag.
5503  $a = preg_split($pattern, $text, -1, PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY);
5504 
5505  $firstline='';
5506  $i=0;
5507  $nba = count($a); // 2x nb of lines in $a because $a contains also a line for each new line separator
5508  while (($i < $nba) && ($i < ($nboflines * 2)))
5509  {
5510  if ($i % 2 == 0) $firstline .= $a[$i];
5511  elseif (($i < (($nboflines * 2) - 1)) && ($i < ($nba - 1))) $firstline .= ($ishtml?"<br>\n":"\n");
5512  $i++;
5513  }
5514  unset($a);
5515  return $firstline.(($i < $nba)?'...':'');
5516  }
5517 }
5518 
5519 
5529 function dol_nl2br($stringtoencode,$nl2brmode=0,$forxml=false)
5530 {
5531  if (!$nl2brmode) {
5532  return nl2br($stringtoencode, $forxml);
5533  } else {
5534  $ret=preg_replace('/(\r\n|\r|\n)/i', ($forxml?'<br />':'<br>'), $stringtoencode);
5535  return $ret;
5536  }
5537 }
5538 
5539 
5557 function dol_htmlentitiesbr($stringtoencode, $nl2brmode=0, $pagecodefrom='UTF-8', $removelasteolbr=1)
5558 {
5559  $newstring=$stringtoencode;
5560  if (dol_textishtml($stringtoencode)) // Check if text is already HTML or not
5561  {
5562  $newstring=preg_replace('/<br(\s[\sa-zA-Z_="]*)?\/?>/i','<br>',$newstring); // Replace "<br type="_moz" />" by "<br>". It's same and avoid pb with FPDF.
5563  if ($removelasteolbr) $newstring=preg_replace('/<br>$/i','',$newstring); // Remove last <br> (remove only last one)
5564  $newstring=strtr($newstring,array('&'=>'__and__','<'=>'__lt__','>'=>'__gt__','"'=>'__dquot__'));
5565  $newstring=dol_htmlentities($newstring,ENT_COMPAT,$pagecodefrom); // Make entity encoding
5566  $newstring=strtr($newstring,array('__and__'=>'&','__lt__'=>'<','__gt__'=>'>','__dquot__'=>'"'));
5567  }
5568  else
5569  {
5570  if ($removelasteolbr) $newstring=preg_replace('/(\r\n|\r|\n)$/i','',$newstring); // Remove last \n (may remove several)
5571  $newstring=dol_nl2br(dol_htmlentities($newstring,ENT_COMPAT,$pagecodefrom),$nl2brmode);
5572  }
5573  // Other substitutions that htmlentities does not do
5574  //$newstring=str_replace(chr(128),'&euro;',$newstring); // 128 = 0x80. Not in html entity table. // Seems useles with TCPDF. Make bug with UTF8 languages
5575  return $newstring;
5576 }
5577 
5585 function dol_htmlentitiesbr_decode($stringtodecode,$pagecodeto='UTF-8')
5586 {
5587  $ret=dol_html_entity_decode($stringtodecode,ENT_COMPAT,$pagecodeto);
5588  $ret=preg_replace('/'."\r\n".'<br(\s[\sa-zA-Z_="]*)?\/?>/i',"<br>",$ret);
5589  $ret=preg_replace('/<br(\s[\sa-zA-Z_="]*)?\/?>'."\r\n".'/i',"\r\n",$ret);
5590  $ret=preg_replace('/<br(\s[\sa-zA-Z_="]*)?\/?>'."\n".'/i',"\n",$ret);
5591  $ret=preg_replace('/<br(\s[\sa-zA-Z_="]*)?\/?>/i',"\n",$ret);
5592  return $ret;
5593 }
5594 
5601 function dol_htmlcleanlastbr($stringtodecode)
5602 {
5603  $ret=preg_replace('/(<br>|<br(\s[\sa-zA-Z_="]*)?\/?>|'."\n".'|'."\r".')+$/i',"",$stringtodecode);
5604  return $ret;
5605 }
5606 
5615 function dol_html_entity_decode($a,$b,$c='UTF-8')
5616 {
5617  return html_entity_decode($a,$b,$c);
5618 }
5619 
5630 function dol_htmlentities($string, $flags=null, $encoding='UTF-8', $double_encode=false)
5631 {
5632  return htmlentities($string, $flags, $encoding, $double_encode);
5633 }
5634 
5644 {
5645  $len=dol_strlen($s);
5646  $ok=1;
5647  for($scursor=0;$scursor<$len;$scursor++)
5648  {
5649  $ordchar=ord($s{$scursor});
5650  //print $scursor.'-'.$ordchar.'<br>';
5651  if ($ordchar < 32 && $ordchar != 13 && $ordchar != 10) { $ok=0; break; }
5652  if ($ordchar > 126 && $ordchar < 160) { $ok=0; break; }
5653  }
5654  return $ok;
5655 }
5656 
5657 
5666 function dol_nboflines($s,$maxchar=0)
5667 {
5668  if ($s == '') return 0;
5669  $arraystring=explode("\n",$s);
5670  $nb=count($arraystring);
5671 
5672  return $nb;
5673 }
5674 
5675 
5685 function dol_nboflines_bis($text,$maxlinesize=0,$charset='UTF-8')
5686 {
5687  $repTable = array("\t" => " ", "\n" => "<br>", "\r" => " ", "\0" => " ", "\x0B" => " ");
5688  if (dol_textishtml($text)) $repTable = array("\t" => " ", "\n" => " ", "\r" => " ", "\0" => " ", "\x0B" => " ");
5689 
5690  $text = strtr($text, $repTable);
5691  if ($charset == 'UTF-8') { $pattern = '/(<br[^>]*>)/Uu'; } // /U is to have UNGREEDY regex to limit to one html tag. /u is for UTF8 support
5692  else $pattern = '/(<br[^>]*>)/U'; // /U is to have UNGREEDY regex to limit to one html tag.
5693  $a = preg_split($pattern, $text, -1, PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY);
5694 
5695  $nblines = (int) floor((count($a)+1)/2);
5696  // count possible auto line breaks
5697  if($maxlinesize)
5698  {
5699  foreach ($a as $line)
5700  {
5701  if (dol_strlen($line)>$maxlinesize)
5702  {
5703  //$line_dec = html_entity_decode(strip_tags($line));
5704  $line_dec = html_entity_decode($line);
5705  if(dol_strlen($line_dec)>$maxlinesize)
5706  {
5707  $line_dec=wordwrap($line_dec,$maxlinesize,'\n',true);
5708  $nblines+=substr_count($line_dec,'\n');
5709  }
5710  }
5711  }
5712  }
5713 
5714  unset($a);
5715  return $nblines;
5716 }
5717 
5726 {
5727  dol_syslog(__FUNCTION__ . " is deprecated", LOG_WARNING);
5728 
5729  return microtime(true);
5730 }
5731 
5740 function dol_textishtml($msg,$option=0)
5741 {
5742  if ($option == 1)
5743  {
5744  if (preg_match('/<html/i',$msg)) return true;
5745  elseif (preg_match('/<body/i',$msg)) return true;
5746  elseif (preg_match('/<br/i',$msg)) return true;
5747  return false;
5748  }
5749  else
5750  {
5751  if (preg_match('/<html/i',$msg)) return true;
5752  elseif (preg_match('/<body/i',$msg)) return true;
5753  elseif (preg_match('/<(b|em|i|u)>/i',$msg)) return true;
5754  elseif (preg_match('/<br\/>/i',$msg)) return true;
5755  elseif (preg_match('/<(br|div|font|li|p|span|strong|table)>/i',$msg)) return true;
5756  elseif (preg_match('/<(br|div|font|li|p|span|strong|table)\s+[^<>\/]*>/i',$msg)) return true;
5757  elseif (preg_match('/<(br|div|font|li|p|span|strong|table)\s+[^<>\/]*\/>/i',$msg)) return true;
5758  elseif (preg_match('/<img\s+[^<>]*src[^<>]*>/i',$msg)) return true; // must accept <img src="http://example.com/aaa.png" />
5759  elseif (preg_match('/<a\s+[^<>]*href[^<>]*>/i',$msg)) return true; // must accept <a href="http://example.com/aaa.png" />
5760  elseif (preg_match('/<h[0-9]>/i',$msg)) return true;
5761  elseif (preg_match('/&[A-Z0-9]{1,6};/i',$msg)) return true; // Html entities names (http://www.w3schools.com/tags/ref_entities.asp)
5762  elseif (preg_match('/&#[0-9]{2,3};/i',$msg)) return true; // Html entities numbers (http://www.w3schools.com/tags/ref_entities.asp)
5763 
5764  return false;
5765  }
5766 }
5767 
5781 function dol_concatdesc($text1,$text2,$forxml=false)
5782 {
5783  $ret='';
5784  $ret.= (! dol_textishtml($text1) && dol_textishtml($text2))?dol_nl2br($text1, 0, $forxml):$text1;
5785  $ret.= (! empty($text1) && ! empty($text2)) ? ((dol_textishtml($text1) || dol_textishtml($text2))?($forxml?"<br >\n":"<br>\n") : "\n") : "";
5786  $ret.= (dol_textishtml($text1) && ! dol_textishtml($text2))?dol_nl2br($text2, 0, $forxml):$text2;
5787  return $ret;
5788 }
5789 
5790 
5801 function getCommonSubstitutionArray($outputlangs, $onlykey=0, $exclude=null, $object=null)
5802 {
5803  global $db, $conf, $mysoc, $user, $extrafields;
5804 
5805  $substitutionarray=array();
5806 
5807  if (empty($exclude) || ! in_array('user', $exclude))
5808  {
5809  // Add SIGNATURE into substitutionarray first, so, when we will make the substitution,
5810  // this will include signature content first and then replace var found into content of signature
5811  $signature = $user->signature;
5812  $substitutionarray=array_merge($substitutionarray, array(
5813  '__USER_SIGNATURE__' => (string) (($signature && empty($conf->global->MAIN_MAIL_DO_NOT_USE_SIGN)) ? ($onlykey == 2 ? dol_trunc(dol_string_nohtmltag($signature), 30) : $signature) : '')
5814  )
5815  );
5816  // For backward compatibility
5817  if ($onlykey != 2)
5818  {
5819  $substitutionarray['__SIGNATURE__'] = (string) (($signature && empty($conf->global->MAIN_MAIL_DO_NOT_USE_SIGN)) ? ($onlykey == 2 ? dol_trunc(dol_string_nohtmltag($signature), 30) : $signature) : '');
5820  }
5821 
5822  $substitutionarray=array_merge($substitutionarray, array(
5823  '__USER_ID__' => (string) $user->id,
5824  '__USER_LOGIN__' => (string) $user->login,
5825  '__USER_LASTNAME__' => (string) $user->lastname,
5826  '__USER_FIRSTNAME__' => (string) $user->firstname,
5827  '__USER_FULLNAME__' => (string) $user->getFullName($outputlangs),
5828  '__USER_SUPERVISOR_ID__' => (string) ($user->fk_user ? $user->fk_user : '0'),
5829  '__USER_REMOTE_IP__' => (string) getUserRemoteIP()
5830  )
5831  );
5832  }
5833  if ((empty($exclude) || ! in_array('mycompany', $exclude)) && is_object($mysoc))
5834  {
5835  $substitutionarray=array_merge($substitutionarray, array(
5836  '__MYCOMPANY_NAME__' => $mysoc->name,
5837  '__MYCOMPANY_EMAIL__' => $mysoc->email,
5838  '__MYCOMPANY_PROFID1__' => $mysoc->idprof1,
5839  '__MYCOMPANY_PROFID2__' => $mysoc->idprof2,
5840  '__MYCOMPANY_PROFID3__' => $mysoc->idprof3,
5841  '__MYCOMPANY_PROFID4__' => $mysoc->idprof4,
5842  '__MYCOMPANY_PROFID5__' => $mysoc->idprof5,
5843  '__MYCOMPANY_PROFID6__' => $mysoc->idprof6,
5844  '__MYCOMPANY_CAPITAL__' => $mysoc->capital,
5845  '__MYCOMPANY_FULLADDRESS__' => $mysoc->getFullAddress(1, ', '),
5846  '__MYCOMPANY_ADDRESS__' => $mysoc->address,
5847  '__MYCOMPANY_ZIP__' => $mysoc->zip,
5848  '__MYCOMPANY_TOWN__' => $mysoc->town,
5849  '__MYCOMPANY_COUNTRY__' => $mysoc->country,
5850  '__MYCOMPANY_COUNTRY_ID__' => $mysoc->country_id,
5851  '__MYCOMPANY_CURRENCY_CODE__' => $conf->currency
5852  ));
5853  }
5854 
5855  if (($onlykey || is_object($object)) && (empty($exclude) || ! in_array('object', $exclude)))
5856  {
5857  if ($onlykey)
5858  {
5859  $substitutionarray['__ID__'] = '__ID__';
5860  $substitutionarray['__REF__'] = '__REF__';
5861  $substitutionarray['__REF_CLIENT__'] = '__REF_CLIENT__';
5862  $substitutionarray['__REF_SUPPLIER__'] = '__REF_SUPPLIER__';
5863  $substitutionarray['__EXTRAFIELD_XXX__'] = '__EXTRAFIELD_XXX__';
5864 
5865  $substitutionarray['__THIRDPARTY_ID__'] = '__THIRDPARTY_ID__';
5866  $substitutionarray['__THIRDPARTY_NAME__'] = '__THIRDPARTY_NAME__';
5867  $substitutionarray['__THIRDPARTY_NAME_ALIAS__'] = '__THIRDPARTY_NAME_ALIAS__';
5868  $substitutionarray['__THIRDPARTY_EMAIL__'] = '__THIRDPARTY_EMAIL__';
5869 
5870  if (! empty($conf->adherent->enabled))
5871  {
5872  $substitutionarray['__MEMBER_ID__'] = '__MEMBER_ID__';
5873  $substitutionarray['__MEMBER_CIVILITY__'] = '__MEMBER_CIVILITY__';
5874  $substitutionarray['__MEMBER_FIRSTNAME__'] = '__MEMBER_FIRSTNAME__';
5875  $substitutionarray['__MEMBER_LASTNAME__'] = '__MEMBER_LASTNAME__';
5876  }
5877  $substitutionarray['__PROJECT_ID__'] = '__PROJECT_ID__';
5878  $substitutionarray['__PROJECT_REF__'] = '__PROJECT_REF__';
5879  $substitutionarray['__PROJECT_NAME__'] = '__PROJECT_NAME__';
5880 
5881  $substitutionarray['__CONTRACT_HIGHEST_PLANNED_START_DATE__'] = 'Highest date planned for a service start';
5882  $substitutionarray['__CONTRACT_HIGHEST_PLANNED_START_DATETIME__'] = 'Highest date and hour planned for service start';
5883  $substitutionarray['__CONTRACT_LOWEST_EXPIRATION_DATE__'] = 'Lowest data for planned expiration of service';
5884  $substitutionarray['__CONTRACT_LOWEST_EXPIRATION_DATETIME__'] = 'Lowest date and hour for planned expiration of service';
5885 
5886  $substitutionarray['__ONLINE_PAYMENT_URL__'] = 'UrlToPayOnlineIfApplicable';
5887  $substitutionarray['__ONLINE_PAYMENT_TEXT_AND_URL__'] = 'TextAndUrlToPayOnlineIfApplicable';
5888  $substitutionarray['__SECUREKEYPAYMENT__'] = 'Security key (if key is not unique per record)';
5889  $substitutionarray['__SECUREKEYPAYMENT_MEMBER__'] = 'Security key for payment on a member subscription (one key per member)';
5890  $substitutionarray['__SECUREKEYPAYMENT_ORDER__'] = 'Security key for payment on an order';
5891  $substitutionarray['__SECUREKEYPAYMENT_INVOICE__'] = 'Security key for payment on an invoice';
5892  $substitutionarray['__SECUREKEYPAYMENT_CONTRACTLINE__'] = 'Security key for payment on a a service';
5893 
5894  $substitutionarray['__DIRECTDOWNLOAD_URL_PROPOSAL__'] = 'Direct download url of a proposal';
5895  $substitutionarray['__DIRECTDOWNLOAD_URL_ORDER__'] = 'Direct download url of an order';
5896  $substitutionarray['__DIRECTDOWNLOAD_URL_INVOICE__'] = 'Direct download url of an invoice';
5897 
5898  if (! empty($conf->expedition->enabled))
5899  {
5900  $substitutionarray['__SHIPPINGTRACKNUM__']='Shipping tacking number';
5901  $substitutionarray['__SHIPPINGTRACKNUMURL__']='Shipping tracking url';
5902  }
5903  }
5904  else
5905  {
5906  $substitutionarray['__ID__'] = $object->id;
5907  $substitutionarray['__REF__'] = $object->ref;
5908  $substitutionarray['__REF_CLIENT__'] = (isset($object->ref_client) ? $object->ref_client : (isset($object->ref_customer) ? $object->ref_customer : null));
5909  $substitutionarray['__REF_SUPPLIER__'] = (isset($object->ref_supplier) ? $object->ref_supplier : null);
5910  // For backward compatibility
5911  $substitutionarray['__REFCLIENT__'] = (isset($object->ref_client) ? $object->ref_client : (isset($object->ref_customer) ? $object->ref_customer : null));
5912  $substitutionarray['__REFSUPPLIER__'] = (isset($object->ref_supplier) ? $object->ref_supplier : null);
5913  $substitutionarray['__SUPPLIER_ORDER_DATE_DELIVERY__'] = (isset($object->date_livraison) ? dol_print_date($object->date_livraison, 'day', 0, $outputlangs): '');
5914 
5915  // TODO Remove this
5916  $msgishtml = 0;
5917 
5918  $birthday = dol_print_date($object->birth,'day');
5919 
5920  if ($object->id > 0)
5921  {
5922  $substitutionarray['__MEMBER_ID__']=$object->id;
5923  if (method_exists($object, 'getCivilityLabel')) $substitutionarray['__MEMBER_CIVILITY__'] = $object->getCivilityLabel();
5924  $substitutionarray['__MEMBER_FIRSTNAME__']=$msgishtml?dol_htmlentitiesbr($object->firstname):$object->firstname;
5925  $substitutionarray['__MEMBER_LASTNAME__']=$msgishtml?dol_htmlentitiesbr($object->lastname):$object->lastname;
5926  if (method_exists($object, 'getFullName')) $substitutionarray['__MEMBER_FULLNAME__']=$msgishtml?dol_htmlentitiesbr($object->getFullName($outputlangs)):$object->getFullName($outputlangs);
5927  $substitutionarray['__MEMBER_COMPANY__']=$msgishtml?dol_htmlentitiesbr($object->societe):$object->societe;
5928  $substitutionarray['__MEMBER_ADDRESS__']=$msgishtml?dol_htmlentitiesbr($object->address):$object->address;
5929  $substitutionarray['__MEMBER_ZIP__']=$msgishtml?dol_htmlentitiesbr($object->zip):$object->zip;
5930  $substitutionarray['__MEMBER_TOWN__']=$msgishtml?dol_htmlentitiesbr($object->town):$object->town;
5931  $substitutionarray['__MEMBER_COUNTRY__']=$msgishtml?dol_htmlentitiesbr($object->country):$object->country;
5932  $substitutionarray['__MEMBER_EMAIL__']=$msgishtml?dol_htmlentitiesbr($object->email):$object->email;
5933  $substitutionarray['__MEMBER_BIRTH__']=$msgishtml?dol_htmlentitiesbr($birthday):$birthday;
5934  $substitutionarray['__MEMBER_PHOTO__']=$msgishtml?dol_htmlentitiesbr($object->photo):$object->photo;
5935  $substitutionarray['__MEMBER_LOGIN__']=$msgishtml?dol_htmlentitiesbr($object->login):$object->login;
5936  $substitutionarray['__MEMBER_PASSWORD__']=$msgishtml?dol_htmlentitiesbr($object->pass):$object->pass;
5937  $substitutionarray['__MEMBER_PHONE__']=$msgishtml?dol_htmlentitiesbr($object->phone):$object->phone;
5938  $substitutionarray['__MEMBER_PHONEPRO__']=$msgishtml?dol_htmlentitiesbr($object->phone_perso):$object->phone_perso;
5939  $substitutionarray['__MEMBER_PHONEMOBILE__']=$msgishtml?dol_htmlentitiesbr($object->phone_mobile):$object->phone_mobile;
5940  $substitutionarray['__MEMBER_FIRST_SUBSCRIPTION_DATE__'] = dol_print_date($object->first_subscription_date, 'dayrfc');
5941  $substitutionarray['__MEMBER_FIRST_SUBSCRIPTION_DATE_START__'] = dol_print_date($object->first_subscription_date_start, 'dayrfc');
5942  $substitutionarray['__MEMBER_FIRST_SUBSCRIPTION_DATE_END__'] = dol_print_date($object->first_subscription_date_end, 'dayrfc');
5943  $substitutionarray['__MEMBER_LAST_SUBSCRIPTION_DATE__'] = dol_print_date($object->last_subscription_date, 'dayrfc');
5944  $substitutionarray['__MEMBER_LAST_SUBSCRIPTION_DATE_START__'] = dol_print_date($object->last_subscription_date_start, 'dayrfc');
5945  $substitutionarray['__MEMBER_LAST_SUBSCRIPTION_DATE_END__'] = dol_print_date($object->last_subscription_date_end, 'dayrfc');
5946  }
5947 
5948  if (is_object($object) && $object->element == 'societe')
5949  {
5950  $substitutionarray['__THIRDPARTY_ID__'] = (is_object($object)?$object->id:'');
5951  $substitutionarray['__THIRDPARTY_NAME__'] = (is_object($object)?$object->name:'');
5952  $substitutionarray['__THIRDPARTY_NAME_ALIAS__'] = (is_object($object)?$object->name_alias:'');
5953  $substitutionarray['__THIRDPARTY_EMAIL__'] = (is_object($object)?$object->email:'');
5954  }
5955  elseif (is_object($object->thirdparty) && $object->thirdparty->id > 0)
5956  {
5957  $substitutionarray['__THIRDPARTY_ID__'] = (is_object($object->thirdparty)?$object->thirdparty->id:'');
5958  $substitutionarray['__THIRDPARTY_NAME__'] = (is_object($object->thirdparty)?$object->thirdparty->name:'');
5959  $substitutionarray['__THIRDPARTY_NAME_ALIAS__'] = (is_object($object->thirdparty)?$object->thirdparty->name_alias:'');
5960  $substitutionarray['__THIRDPARTY_EMAIL__'] = (is_object($object->thirdparty)?$object->thirdparty->email:'');
5961  }
5962 
5963  if (is_object($object->projet) && $object->projet->id > 0)
5964  {
5965  $substitutionarray['__PROJECT_ID__'] = (is_object($object->projet)?$object->projet->id:'');
5966  $substitutionarray['__PROJECT_REF__'] = (is_object($object->projet)?$object->projet->ref:'');
5967  $substitutionarray['__PROJECT_NAME__'] = (is_object($object->projet)?$object->projet->title:'');
5968  }
5969 
5970  if (is_object($object) && $object->element == 'shipping')
5971  {
5972  $substitutionarray['__SHIPPINGTRACKNUM__']=$object->tracking_number;
5973  $substitutionarray['__SHIPPINGTRACKNUMURL__']=$object->tracking_url;
5974  }
5975 
5976  if (is_object($object) && $object->element == 'contrat' && is_array($object->lines))
5977  {
5978  if ($object->id > 0)
5979  {
5980  $dateplannedstart='';
5981  $datenextexpiration='';
5982  foreach($object->lines as $line)
5983  {
5984  if ($line->date_ouverture_prevue > $dateplannedstart) $dateplannedstart = $line->date_ouverture_prevue;
5985  if ($line->statut == 4 && $line->date_fin_prevue && (! $datenextexpiration || $line->date_fin_prevue < $datenextexpiration)) $datenextexpiration = $line->date_fin_prevue;
5986  }
5987  $substitutionarray['__CONTRACT_HIGHEST_PLANNED_START_DATE__'] = dol_print_date($dateplannedstart, 'dayrfc');
5988  $substitutionarray['__CONTRACT_HIGHEST_PLANNED_START_DATETIME__'] = dol_print_date($dateplannedstart, 'standard');
5989  $substitutionarray['__CONTRACT_LOWEST_EXPIRATION_DATE__'] = dol_print_date($datenextexpiration, 'dayrfc');
5990  $substitutionarray['__CONTRACT_LOWEST_EXPIRATION_DATETIME__'] = dol_print_date($datenextexpiration, 'standard');
5991  }
5992  }
5993 
5994  // Create dynamic tags for __EXTRAFIELD_FIELD__
5995  if ($object->table_element && $object->id > 0)
5996  {
5997  if (! is_object($extrafields)) $extrafields = new ExtraFields($db);
5998  $extrafields->fetch_name_optionals_label($object->table_element, true);
5999 
6000  if ($object->fetch_optionals() > 0)
6001  {
6002  if (is_array($extrafields->attributes[$object->table_element]['label']) && count($extrafields->attributes[$object->table_element]['label']) > 0)
6003  {
6004  foreach ($extrafields->attributes[$object->table_element]['label'] as $key => $label) {
6005  $substitutionarray['__EXTRAFIELD_' . strtoupper($key) . '__'] = $object->array_options['options_' . $key];
6006  }
6007  }
6008  }
6009  }
6010 
6011  // Complete substitution array with the url to make online payment
6012  $paymenturl='';
6013  if (empty($substitutionarray['__REF__']))
6014  {
6015  $paymenturl='';
6016  }
6017  else
6018  {
6019  // Set the online payment url link into __ONLINE_PAYMENT_URL__ key
6020  require_once DOL_DOCUMENT_ROOT.'/core/lib/payments.lib.php';
6021  $outputlangs->loadLangs(array('paypal','other'));
6022  $typeforonlinepayment='free';
6023  if (is_object($object) && $object->element == 'commande') $typeforonlinepayment='order';
6024  if (is_object($object) && $object->element == 'facture') $typeforonlinepayment='invoice';
6025  if (is_object($object) && $object->element == 'member') $typeforonlinepayment='member';
6026  $url=getOnlinePaymentUrl(0, $typeforonlinepayment, $substitutionarray['__REF__']);
6027  $paymenturl=$url;
6028  }
6029 
6030  if ($object->id > 0)
6031  {
6032  $substitutionarray['__ONLINE_PAYMENT_TEXT_AND_URL__']=($paymenturl?str_replace('\n', "\n", $outputlangs->trans("PredefinedMailContentLink", $paymenturl)):'');
6033  $substitutionarray['__ONLINE_PAYMENT_URL__']=$paymenturl;
6034 
6035  if (! empty($conf->global->PROPOSAL_ALLOW_EXTERNAL_DOWNLOAD) && is_object($object) && $object->element == 'propal')
6036  {
6037  $substitutionarray['__DIRECTDOWNLOAD_URL_PROPOSAL__'] = $object->getLastMainDocLink($object->element);
6038  }
6039  else $substitutionarray['__DIRECTDOWNLOAD_URL_PROPOSAL__'] = '';
6040  if (! empty($conf->global->ORDER_ALLOW_EXTERNAL_DOWNLOAD) && is_object($object) && $object->element == 'commande')
6041  {
6042  $substitutionarray['__DIRECTDOWNLOAD_URL_ORDER__'] = $object->getLastMainDocLink($object->element);
6043  }
6044  else $substitutionarray['__DIRECTDOWNLOAD_URL_ORDER__'] = '';
6045  if (! empty($conf->global->INVOICE_ALLOW_EXTERNAL_DOWNLOAD) && is_object($object) && $object->element == 'facture')
6046  {
6047  $substitutionarray['__DIRECTDOWNLOAD_URL_INVOICE__'] = $object->getLastMainDocLink($object->element);
6048  }
6049  else $substitutionarray['__DIRECTDOWNLOAD_URL_INVOICE__'] = '';
6050  }
6051  }
6052  }
6053  if (empty($exclude) || ! in_array('objectamount', $exclude))
6054  {
6055  $substitutionarray['__DATE_YMD__'] = is_object($object)?(isset($object->date) ? dol_print_date($object->date, 'day', 0, $outputlangs) : null) : '';
6056  $substitutionarray['__DATE_DUE_YMD__'] = is_object($object)?(isset($object->date_lim_reglement) ? dol_print_date($object->date_lim_reglement, 'day', 0, $outputlangs) : null) : '';
6057 
6058  $substitutionarray['__AMOUNT__'] = is_object($object)?$object->total_ttc:'';
6059  $substitutionarray['__AMOUNT_EXCL_TAX__'] = is_object($object)?$object->total_ht:'';
6060  $substitutionarray['__AMOUNT_VAT__'] = is_object($object)?($object->total_vat?$object->total_vat:$object->total_tva):'';
6061  if ($onlykey != 2 || $mysoc->useLocalTax(1)) $substitutionarray['__AMOUNT_TAX2__'] = is_object($object)?$object->total_localtax1:'';
6062  if ($onlykey != 2 || $mysoc->useLocalTax(2)) $substitutionarray['__AMOUNT_TAX3__'] = is_object($object)?$object->total_localtax2:'';
6063 
6064  $substitutionarray['__AMOUNT_FORMATED__'] = is_object($object)?($object->total_ttc ? price($object->total_ttc, 0, $outputlangs, 0, 0, -1, $conf->currency) : null):'';
6065  $substitutionarray['__AMOUNT_EXCL_TAX_FORMATED__'] = is_object($object)?($object->total_ht ? price($object->total_ht, 0, $outputlangs, 0, 0, -1, $conf->currency) : null):'';
6066  $substitutionarray['__AMOUNT_VAT_FORMATED__'] = is_object($object)?($object->total_vat ? price($object->total_vat, 0, $outputlangs, 0, 0, -1, $conf->currency): ($object->total_tva ? price($object->total_tva, 0, $outputlangs, 0, 0, -1, $conf->currency) : null)):'';
6067  if ($onlykey != 2 || $mysoc->useLocalTax(1)) $substitutionarray['__AMOUNT_TAX2_FORMATED__'] = is_object($object)? ($object->total_localtax1 ? price($object->total_localtax1, 0, $outputlangs, 0, 0, -1, $conf->currency) : null):'';
6068  if ($onlykey != 2 || $mysoc->useLocalTax(2)) $substitutionarray['__AMOUNT_TAX3_FORMATED__'] = is_object($object)? ($object->total_localtax2 ? price($object->total_localtax2, 0, $outputlangs, 0, 0, -1, $conf->currency) : null):'';
6069 
6070  // TODO Add keys for foreign multicurrency
6071 
6072  // For backward compatibility
6073  if ($onlykey != 2)
6074  {
6075  $substitutionarray['__TOTAL_TTC__'] = is_object($object)?$object->total_ttc:'';
6076  $substitutionarray['__TOTAL_HT__'] = is_object($object)?$object->total_ht:'';
6077  $substitutionarray['__TOTAL_VAT__'] = is_object($object)?($object->total_vat?$object->total_vat:$object->total_tva):'';
6078  }
6079  }
6080 
6081  //var_dump($substitutionarray['__AMOUNT_FORMATED__']);
6082  if (empty($exclude) || ! in_array('date', $exclude))
6083  {
6084  include_once DOL_DOCUMENT_ROOT.'/core/lib/date.lib.php';
6085 
6086  $tmp=dol_getdate(dol_now(), true);
6087  $tmp2=dol_get_prev_day($tmp['mday'], $tmp['mon'], $tmp['year']);
6088  $tmp3=dol_get_prev_month($tmp['mon'], $tmp['year']);
6089  $tmp4=dol_get_next_day($tmp['mday'], $tmp['mon'], $tmp['year']);
6090  $tmp5=dol_get_next_month($tmp['mon'], $tmp['year']);
6091 
6092  $substitutionarray=array_merge($substitutionarray, array(
6093  '__DAY__' => (string) $tmp['mday'],
6094  '__DAY_TEXT__' => $outputlangs->trans('Day'.$tmp['wday']), // Monday
6095  '__DAY_TEXT_SHORT__' => $outputlangs->trans($tmp['weekday'].'Min'), // Mon
6096  '__DAY_TEXT_MIN__' => $outputlangs->trans('Short'.$tmp['weekday']), // M
6097  '__MONTH__' => (string) $tmp['mon'],
6098  '__MONTH_TEXT__' => $outputlangs->trans('Month'.sprintf("%02d", $tmp['mon'])),
6099  '__MONTH_TEXT_SHORT__' => $outputlangs->trans('MonthShort'.sprintf("%02d", $tmp['mon'])),
6100  '__MONTH_TEXT_MIN__' => $outputlangs->trans('MonthVeryShort'.sprintf("%02d", $tmp['mon'])),
6101  '__YEAR__' => (string) $tmp['year'],
6102  '__PREVIOUS_DAY__' => (string) $tmp2['day'],
6103  '__PREVIOUS_MONTH__' => (string) $tmp3['month'],
6104  '__PREVIOUS_YEAR__' => (string) ($tmp['year'] - 1),
6105  '__NEXT_DAY__' => (string) $tmp4['day'],
6106  '__NEXT_MONTH__' => (string) $tmp5['month'],
6107  '__NEXT_YEAR__' => (string) ($tmp['year'] + 1),
6108  ));
6109  }
6110 
6111  if (! empty($conf->multicompany->enabled))
6112  {
6113  $substitutionarray=array_merge($substitutionarray, array('__ENTITY_ID__' => $conf->entity));
6114  }
6115  if (empty($exclude) || ! in_array('system', $exclude))
6116  {
6117  $substitutionarray['__DOL_MAIN_URL_ROOT__']=DOL_MAIN_URL_ROOT;
6118  $substitutionarray['__(AnyTranslationKey)__']=$outputlangs->trans('TranslationOfKey');
6119  $substitutionarray['__[AnyConstantKey]__']=$outputlangs->trans('ValueOfConstantKey');
6120  }
6121 
6122  return $substitutionarray;
6123 }
6124 
6139 function make_substitutions($text, $substitutionarray, $outputlangs=null)
6140 {
6141  global $conf, $langs;
6142 
6143  if (! is_array($substitutionarray)) return 'ErrorBadParameterSubstitutionArrayWhenCalling_make_substitutions';
6144 
6145  if (empty($outputlangs)) $outputlangs=$langs;
6146 
6147  // Make substitution for language keys
6148  if (is_object($outputlangs))
6149  {
6150  while (preg_match('/__\(([^\)]+)\)__/', $text, $reg))
6151  {
6152  $msgishtml = 0;
6153  if (dol_textishtml($text,1)) $msgishtml = 1;
6154 
6155  // If key is __(TranslationKey|langfile)__, then force load of langfile.lang
6156  $tmp=explode('|',$reg[1]);
6157  if (! empty($tmp[1])) $outputlangs->load($tmp[1]);
6158 
6159  $text = preg_replace('/__\('.preg_quote($reg[1], '/').'\)__/', $msgishtml?dol_htmlentitiesbr($outputlangs->transnoentitiesnoconv($reg[1])):$outputlangs->transnoentitiesnoconv($reg[1]), $text);
6160  }
6161  }
6162 
6163  // Make substitution for constant keys. Must be after the substitution of translation, so if text of translation contains a constant,
6164  // it is also converted.
6165  while (preg_match('/__\[([^\]]+)\]__/', $text, $reg))
6166  {
6167  $msgishtml = 0;
6168  if (dol_textishtml($text,1)) $msgishtml = 1;
6169 
6170  $keyfound = $reg[1];
6171  if (preg_match('/(_pass|password|secret|_key|key$)/i', $keyfound)) $newval = '*****forbidden*****';
6172  else $newval=empty($conf->global->$keyfound)?'':$conf->global->$keyfound;
6173  $text = preg_replace('/__\['.preg_quote($keyfound, '/').'\]__/', $msgishtml?dol_htmlentitiesbr($newval):$newval, $text);
6174  }
6175 
6176  // Make substitition for array $substitutionarray
6177  foreach ($substitutionarray as $key => $value)
6178  {
6179  if (! isset($value)) continue; // If value is null, it same than not having substitution key at all into array, we do not replace.
6180 
6181  if ($key == '__SIGNATURE__' && (! empty($conf->global->MAIN_MAIL_DO_NOT_USE_SIGN))) $value=''; // Protection
6182  if ($key == '__USER_SIGNATURE__' && (! empty($conf->global->MAIN_MAIL_DO_NOT_USE_SIGN))) $value=''; // Protection
6183 
6184  $text=str_replace("$key", "$value", $text); // We must keep the " to work when value is 123.5 for example
6185  }
6186 
6187  return $text;
6188 }
6189 
6202 function complete_substitutions_array(&$substitutionarray, $outputlangs, $object=null, $parameters=null, $callfunc="completesubstitutionarray")
6203 {
6204  global $conf,$user;
6205 
6206  require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
6207 
6208  // Add a substitution key for each extrafields, using key __EXTRA_XXX__
6209  // TODO Remove this. Already available into the getCommonSubstitutionArray used to build the substitution array.
6210  /*if (is_object($object) && is_array($object->array_options))
6211  {
6212  foreach($object->array_options as $key => $val)
6213  {
6214  $keyshort=preg_replace('/^(options|extra)_/','',$key);
6215  $substitutionarray['__EXTRAFIELD_'.$keyshort.'__']=$val;
6216  // For backward compatibiliy
6217  $substitutionarray['%EXTRA_'.$keyshort.'%']=$val;
6218  }
6219  }*/
6220 
6221  // Check if there is external substitution to do, requested by plugins
6222  $dirsubstitutions=array_merge(array(),(array) $conf->modules_parts['substitutions']);
6223 
6224  foreach($dirsubstitutions as $reldir)
6225  {
6226  $dir=dol_buildpath($reldir,0);
6227 
6228  // Check if directory exists
6229  if (! dol_is_dir($dir)) continue;
6230 
6231  $substitfiles=dol_dir_list($dir,'files',0,'functions_');
6232  foreach($substitfiles as $substitfile)
6233  {
6234  if (preg_match('/functions_(.*)\.lib\.php/i',$substitfile['name'],$reg))
6235  {
6236  $module=$reg[1];
6237 
6238  dol_syslog("Library ".$substitfile['name']." found into ".$dir);
6239  // Include the user's functions file
6240  require_once $dir.$substitfile['name'];
6241  // Call the user's function, and only if it is defined
6242  $function_name=$module."_".$callfunc;
6243  if (function_exists($function_name)) $function_name($substitutionarray,$outputlangs,$object,$parameters);
6244  }
6245  }
6246  }
6247 }
6248 
6258 function print_date_range($date_start,$date_end,$format = '',$outputlangs='')
6259 {
6260  print get_date_range($date_start,$date_end,$format,$outputlangs);
6261 }
6262 
6273 function get_date_range($date_start,$date_end,$format = '',$outputlangs='', $withparenthesis=1)
6274 {
6275  global $langs;
6276 
6277  $out='';
6278 
6279  if (! is_object($outputlangs)) $outputlangs=$langs;
6280 
6281  if ($date_start && $date_end)
6282  {
6283  $out.= ($withparenthesis?' (':'').$outputlangs->transnoentitiesnoconv('DateFromTo',dol_print_date($date_start, $format, false, $outputlangs),dol_print_date($date_end, $format, false, $outputlangs)).($withparenthesis?')':'');
6284  }
6285  if ($date_start && ! $date_end)
6286  {
6287  $out.= ($withparenthesis?' (':'').$outputlangs->transnoentitiesnoconv('DateFrom',dol_print_date($date_start, $format, false, $outputlangs)).($withparenthesis?')':'');
6288  }
6289  if (! $date_start && $date_end)
6290  {
6291  $out.= ($withparenthesis?' (':'').$outputlangs->transnoentitiesnoconv('DateUntil',dol_print_date($date_end, $format, false, $outputlangs)).($withparenthesis?')':'');
6292  }
6293 
6294  return $out;
6295 }
6296 
6305 function dolGetFirstLastname($firstname,$lastname,$nameorder=-1)
6306 {
6307  global $conf;
6308 
6309  $ret='';
6310  // If order not defined, we use the setup
6311  if ($nameorder < 0) $nameorder=(empty($conf->global->MAIN_FIRSTNAME_NAME_POSITION)?1:0);
6312  if ($nameorder && ((string) $nameorder != '2'))
6313  {
6314  $ret.=$firstname;
6315  if ($firstname && $lastname) $ret.=' ';
6316  $ret.=$lastname;
6317  }
6318  else if ($nameorder == 2)
6319  {
6320  $ret.=$firstname;
6321  }
6322  else
6323  {
6324  $ret.=$lastname;
6325  if ($firstname && $lastname) $ret.=' ';
6326  $ret.=$firstname;
6327  }
6328  return $ret;
6329 }
6330 
6331 
6342 function setEventMessage($mesgs, $style='mesgs')
6343 {
6344  //dol_syslog(__FUNCTION__ . " is deprecated", LOG_WARNING); This is not deprecated, it is used by setEventMessages function
6345  if (! is_array($mesgs)) // If mesgs is a string
6346  {
6347  if ($mesgs) $_SESSION['dol_events'][$style][] = $mesgs;
6348  }
6349  else // If mesgs is an array
6350  {
6351  foreach($mesgs as $mesg)
6352  {
6353  if ($mesg) $_SESSION['dol_events'][$style][] = $mesg;
6354  }
6355  }
6356 }
6357 
6368 function setEventMessages($mesg, $mesgs, $style='mesgs')
6369 {
6370  if (empty($mesg) && empty($mesgs))
6371  {
6372  dol_syslog("Try to add a message in stack with empty message", LOG_WARNING);
6373  }
6374  else
6375  {
6376  if (! in_array((string) $style, array('mesgs','warnings','errors'))) dol_print_error('','Bad parameter style='.$style.' for setEventMessages');
6377  if (empty($mesgs)) setEventMessage($mesg, $style);
6378  else
6379  {
6380  if (! empty($mesg) && ! in_array($mesg, $mesgs)) setEventMessage($mesg, $style); // Add message string if not already into array
6381  setEventMessage($mesgs, $style);
6382  }
6383  }
6384 }
6385 
6395 function dol_htmloutput_events($disabledoutputofmessages=0)
6396 {
6397  // Show mesgs
6398  if (isset($_SESSION['dol_events']['mesgs'])) {
6399  if (empty($disabledoutputofmessages)) dol_htmloutput_mesg('', $_SESSION['dol_events']['mesgs']);
6400  unset($_SESSION['dol_events']['mesgs']);
6401  }
6402 
6403  // Show errors
6404  if (isset($_SESSION['dol_events']['errors'])) {
6405  if (empty($disabledoutputofmessages)) dol_htmloutput_mesg('', $_SESSION['dol_events']['errors'], 'error');
6406  unset($_SESSION['dol_events']['errors']);
6407  }
6408 
6409  // Show warnings
6410  if (isset($_SESSION['dol_events']['warnings'])) {
6411  if (empty($disabledoutputofmessages)) dol_htmloutput_mesg('', $_SESSION['dol_events']['warnings'], 'warning');
6412  unset($_SESSION['dol_events']['warnings']);
6413  }
6414 }
6415 
6430 function get_htmloutput_mesg($mesgstring='',$mesgarray='', $style='ok', $keepembedded=0)
6431 {
6432  global $conf, $langs;
6433 
6434  $ret=0; $return='';
6435  $out='';
6436  $divstart=$divend='';
6437 
6438  // If inline message with no format, we add it.
6439  if ((empty($conf->use_javascript_ajax) || ! empty($conf->global->MAIN_DISABLE_JQUERY_JNOTIFY) || $keepembedded) && ! preg_match('/<div class=".*">/i',$out))
6440  {
6441  $divstart='<div class="'.$style.' clearboth">';
6442  $divend='</div>';
6443  }
6444 
6445  if ((is_array($mesgarray) && count($mesgarray)) || $mesgstring)
6446  {
6447  $langs->load("errors");
6448  $out.=$divstart;
6449  if (is_array($mesgarray) && count($mesgarray))
6450  {
6451  foreach($mesgarray as $message)
6452  {
6453  $ret++;
6454  $out.= $langs->trans($message);
6455  if ($ret < count($mesgarray)) $out.= "<br>\n";
6456  }
6457  }
6458  if ($mesgstring)
6459  {
6460  $langs->load("errors");
6461  $ret++;
6462  $out.= $langs->trans($mesgstring);
6463  }
6464  $out.=$divend;
6465  }
6466 
6467  if ($out)
6468  {
6469  if (! empty($conf->use_javascript_ajax) && empty($conf->global->MAIN_DISABLE_JQUERY_JNOTIFY) && empty($keepembedded))
6470  {
6471  $return = '<script type="text/javascript">
6472  $(document).ready(function() {
6473  var block = '.(! empty($conf->global->MAIN_USE_JQUERY_BLOCKUI)?"true":"false").'
6474  if (block) {
6475  $.dolEventValid("","'.dol_escape_js($out).'");
6476  } else {
6477  /* jnotify(message, preset of message type, keepmessage) */
6478  $.jnotify("'.dol_escape_js($out).'",
6479  "'.($style=="ok" ? 3000 : $style).'",
6480  '.($style=="ok" ? "false" : "true").',
6481  { remove: function (){} } );
6482  }
6483  });
6484  </script>';
6485  }
6486  else
6487  {
6488  $return = $out;
6489  }
6490  }
6491 
6492  return $return;
6493 }
6494 
6506 function get_htmloutput_errors($mesgstring='', $mesgarray=array(), $keepembedded=0)
6507 {
6508  return get_htmloutput_mesg($mesgstring, $mesgarray,'error',$keepembedded);
6509 }
6510 
6524 function dol_htmloutput_mesg($mesgstring = '',$mesgarray = array(), $style = 'ok', $keepembedded=0)
6525 {
6526  if (empty($mesgstring) && (! is_array($mesgarray) || count($mesgarray) == 0)) return;
6527 
6528  $iserror=0;
6529  $iswarning=0;
6530  if (is_array($mesgarray))
6531  {
6532  foreach($mesgarray as $val)
6533  {
6534  if ($val && preg_match('/class="error"/i',$val)) { $iserror++; break; }
6535  if ($val && preg_match('/class="warning"/i',$val)) { $iswarning++; break; }
6536  }
6537  }
6538  else if ($mesgstring && preg_match('/class="error"/i',$mesgstring)) $iserror++;
6539  else if ($mesgstring && preg_match('/class="warning"/i',$mesgstring)) $iswarning++;
6540  if ($style=='error') $iserror++;
6541  if ($style=='warning') $iswarning++;
6542 
6543  if ($iserror || $iswarning)
6544  {
6545  // Remove div from texts
6546  $mesgstring=preg_replace('/<\/div><div class="(error|warning)">/','<br>',$mesgstring);
6547  $mesgstring=preg_replace('/<div class="(error|warning)">/','',$mesgstring);
6548  $mesgstring=preg_replace('/<\/div>/','',$mesgstring);
6549  // Remove div from texts array
6550  if (is_array($mesgarray))
6551  {
6552  $newmesgarray=array();
6553  foreach($mesgarray as $val)
6554  {
6555  $tmpmesgstring=preg_replace('/<\/div><div class="(error|warning)">/','<br>',$val);
6556  $tmpmesgstring=preg_replace('/<div class="(error|warning)">/','',$tmpmesgstring);
6557  $tmpmesgstring=preg_replace('/<\/div>/','',$tmpmesgstring);
6558  $newmesgarray[]=$tmpmesgstring;
6559  }
6560  $mesgarray=$newmesgarray;
6561  }
6562  print get_htmloutput_mesg($mesgstring,$mesgarray,($iserror?'error':'warning'),$keepembedded);
6563  }
6564  else print get_htmloutput_mesg($mesgstring,$mesgarray,'ok',$keepembedded);
6565 }
6566 
6578 function dol_htmloutput_errors($mesgstring='', $mesgarray=array(), $keepembedded=0)
6579 {
6580  dol_htmloutput_mesg($mesgstring, $mesgarray, 'error', $keepembedded);
6581 }
6582 
6596 function dol_sort_array(&$array, $index, $order='asc', $natsort=0, $case_sensitive=0, $keepindex=0)
6597 {
6598  // Clean parameters
6599  $order=strtolower($order);
6600 
6601  if (is_array($array))
6602  {
6603  $sizearray=count($array);
6604  if ($sizearray>0)
6605  {
6606  $temp = array();
6607  foreach(array_keys($array) as $key) $temp[$key]=$array[$key][$index];
6608 
6609  if (!$natsort) ($order=='asc') ? asort($temp) : arsort($temp);
6610  else
6611  {
6612  ($case_sensitive) ? natsort($temp) : natcasesort($temp);
6613  if($order!='asc') $temp=array_reverse($temp, true);
6614  }
6615 
6616  $sorted = array();
6617 
6618  foreach(array_keys($temp) as $key)
6619  {
6620  (is_numeric($key) && empty($keepindex)) ? $sorted[]=$array[$key] : $sorted[$key]=$array[$key];
6621  }
6622 
6623  return $sorted;
6624  }
6625  }
6626  return $array;
6627 }
6628 
6629 
6636 function utf8_check($str)
6637 {
6638  // We must use here a binary strlen function (so not dol_strlen)
6639  $strLength = dol_strlen($str);
6640  for ($i=0; $i<$strLength; $i++)
6641  {
6642  if (ord($str[$i]) < 0x80) continue; // 0bbbbbbb
6643  elseif ((ord($str[$i]) & 0xE0) == 0xC0) $n=1; // 110bbbbb
6644  elseif ((ord($str[$i]) & 0xF0) == 0xE0) $n=2; // 1110bbbb
6645  elseif ((ord($str[$i]) & 0xF8) == 0xF0) $n=3; // 11110bbb
6646  elseif ((ord($str[$i]) & 0xFC) == 0xF8) $n=4; // 111110bb
6647  elseif ((ord($str[$i]) & 0xFE) == 0xFC) $n=5; // 1111110b
6648  else return false; // Does not match any model
6649  for ($j=0; $j<$n; $j++) { // n bytes matching 10bbbbbb follow ?
6650  if ((++$i == strlen($str)) || ((ord($str[$i]) & 0xC0) != 0x80))
6651  return false;
6652  }
6653  }
6654  return true;
6655 }
6656 
6657 
6665 function dol_osencode($str)
6666 {
6667  global $conf;
6668 
6669  $tmp=ini_get("unicode.filesystem_encoding"); // Disponible avec PHP 6.0
6670  if (empty($tmp) && ! empty($_SERVER["WINDIR"])) $tmp='iso-8859-1'; // By default for windows
6671  if (empty($tmp)) $tmp='utf-8'; // By default for other
6672  if (! empty($conf->global->MAIN_FILESYSTEM_ENCODING)) $tmp=$conf->global->MAIN_FILESYSTEM_ENCODING;
6673 
6674  if ($tmp == 'iso-8859-1') return utf8_decode($str);
6675  return $str;
6676 }
6677 
6678 
6692 function dol_getIdFromCode($db, $key, $tablename, $fieldkey='code', $fieldid='id', $entityfilter=0)
6693 {
6694  global $cache_codes;
6695 
6696  // If key empty
6697  if ($key == '') return '';
6698 
6699  // Check in cache
6700  if (isset($cache_codes[$tablename][$key][$fieldid])) // Can be defined to 0 or ''
6701  {
6702  return $cache_codes[$tablename][$key][$fieldid]; // Found in cache
6703  }
6704 
6705  dol_syslog('dol_getIdFromCode (value not found into cache)', LOG_DEBUG);
6706 
6707  $sql = "SELECT ".$fieldid." as valuetoget";
6708  $sql.= " FROM ".MAIN_DB_PREFIX.$tablename;
6709  $sql.= " WHERE ".$fieldkey." = '".$db->escape($key)."'";
6710  if (! empty($entityfilter))
6711  $sql.= " AND entity IN (" . getEntity($tablename) . ")";
6712 
6713  $resql = $db->query($sql);
6714  if ($resql)
6715  {
6716  $obj = $db->fetch_object($resql);
6717  if ($obj) $cache_codes[$tablename][$key][$fieldid]=$obj->valuetoget;
6718  else $cache_codes[$tablename][$key][$fieldid]='';
6719  $db->free($resql);
6720  return $cache_codes[$tablename][$key][$fieldid];
6721  }
6722  else
6723  {
6724  return -1;
6725  }
6726 }
6727 
6734 function verifCond($strRights)
6735 {
6736  global $user,$conf,$langs;
6737  global $leftmenu;
6738  global $rights; // To export to dol_eval function
6739 
6740  //print $strRights."<br>\n";
6741  $rights = true;
6742  if ($strRights != '')
6743  {
6744  $str = 'if(!(' . $strRights . ')) { $rights = false; }';
6745  dol_eval($str); // The dol_eval must contains all the global $xxx used into a condition
6746  }
6747  return $rights;
6748 }
6749 
6759 function dol_eval($s, $returnvalue=0, $hideerrors=1)
6760 {
6761  // Only global variables can be changed by eval function and returned to caller
6762  global $db, $langs, $user, $conf, $website, $websitepage;
6763  global $action, $mainmenu, $leftmenu;
6764  global $rights;
6765  global $object;
6766  global $mysoc;
6767 
6768  global $obj; // To get $obj used into list when dol_eval is used for computed fields and $obj is not yet $object
6769  global $soc; // For backward compatibility
6770 
6771  //print $s."<br>\n";
6772  if ($returnvalue)
6773  {
6774  if ($hideerrors) return @eval('return '.$s.';');
6775  else return eval('return '.$s.';');
6776  }
6777  else
6778  {
6779  if ($hideerrors) @eval($s);
6780  else eval($s);
6781  }
6782 }
6783 
6790 function dol_validElement($element)
6791 {
6792  return (trim($element) != '');
6793 }
6794 
6802 function picto_from_langcode($codelang, $moreatt = '')
6803 {
6804  global $langs;
6805 
6806  if (empty($codelang)) return '';
6807 
6808  if ($codelang == 'auto')
6809  {
6810  return '<span class="fa fa-globe"></span>';
6811  }
6812 
6813  $langtocountryflag = array(
6814  'ar_AR' => '',
6815  'ca_ES' => 'catalonia',
6816  'da_DA' => 'dk',
6817  'fr_CA' => 'mq',
6818  'sv_SV' => 'se'
6819  );
6820 
6821  if (isset($langtocountryflag[$codelang])) $flagImage = $langtocountryflag[$codelang];
6822  else
6823  {
6824  $tmparray = explode('_', $codelang);
6825  $flagImage = empty($tmparray[1]) ? $tmparray[0] : $tmparray[1];
6826  }
6827 
6828  return img_picto_common($codelang, 'flags/'.strtolower($flagImage).'.png', $moreatt);
6829 }
6830 
6837 function getLanguageCodeFromCountryCode($countrycode)
6838 {
6839  global $mysoc;
6840 
6841  if (strtoupper($countrycode) == 'MQ') return 'fr_CA';
6842  if (strtoupper($countrycode) == 'SE') return 'sv_SE'; // se_SE is Sami/Sweden, and we want in priority sv_SE for SE country
6843  if (strtoupper($countrycode) == 'CH')
6844  {
6845  if ($mysoc->country_code == 'FR') return 'fr_CH';
6846  if ($mysoc->country_code == 'DE') return 'de_CH';
6847  }
6848 
6849  // Locale list taken from:
6850  // http://stackoverflow.com/questions/3191664/
6851  // list-of-all-locales-and-their-short-codes
6852  $locales = array(
6853  'af-ZA',
6854  'am-ET',
6855  'ar-AE',
6856  'ar-BH',
6857  'ar-DZ',
6858  'ar-EG',
6859  'ar-IQ',
6860  'ar-JO',
6861  'ar-KW',
6862  'ar-LB',
6863  'ar-LY',
6864  'ar-MA',
6865  'ar-OM',
6866  'ar-QA',
6867  'ar-SA',
6868  'ar-SY',
6869  'ar-TN',
6870  'ar-YE',
6871  'as-IN',
6872  'ba-RU',
6873  'be-BY',
6874  'bg-BG',
6875  'bn-BD',
6876  'bn-IN',
6877  'bo-CN',
6878  'br-FR',
6879  'ca-ES',
6880  'co-FR',
6881  'cs-CZ',
6882  'cy-GB',
6883  'da-DK',
6884  'de-AT',
6885  'de-CH',
6886  'de-DE',
6887  'de-LI',
6888  'de-LU',
6889  'dv-MV',
6890  'el-GR',
6891  'en-AU',
6892  'en-BZ',
6893  'en-CA',
6894  'en-GB',
6895  'en-IE',
6896  'en-IN',
6897  'en-JM',
6898  'en-MY',
6899  'en-NZ',
6900  'en-PH',
6901  'en-SG',
6902  'en-TT',
6903  'en-US',
6904  'en-ZA',
6905  'en-ZW',
6906  'es-AR',
6907  'es-BO',
6908  'es-CL',
6909  'es-CO',
6910  'es-CR',
6911  'es-DO',
6912  'es-EC',
6913  'es-ES',
6914  'es-GT',
6915  'es-HN',
6916  'es-MX',
6917  'es-NI',
6918  'es-PA',
6919  'es-PE',
6920  'es-PR',
6921  'es-PY',
6922  'es-SV',
6923  'es-US',
6924  'es-UY',
6925  'es-VE',
6926  'et-EE',
6927  'eu-ES',
6928  'fa-IR',
6929  'fi-FI',
6930  'fo-FO',
6931  'fr-BE',
6932  'fr-CA',
6933  'fr-CH',
6934  'fr-FR',
6935  'fr-LU',
6936  'fr-MC',
6937  'fy-NL',
6938  'ga-IE',
6939  'gd-GB',
6940  'gl-ES',
6941  'gu-IN',
6942  'he-IL',
6943  'hi-IN',
6944  'hr-BA',
6945  'hr-HR',
6946  'hu-HU',
6947  'hy-AM',
6948  'id-ID',
6949  'ig-NG',
6950  'ii-CN',
6951  'is-IS',
6952  'it-CH',
6953  'it-IT',
6954  'ja-JP',
6955  'ka-GE',
6956  'kk-KZ',
6957  'kl-GL',
6958  'km-KH',
6959  'kn-IN',
6960  'ko-KR',
6961  'ky-KG',
6962  'lb-LU',
6963  'lo-LA',
6964  'lt-LT',
6965  'lv-LV',
6966  'mi-NZ',
6967  'mk-MK',
6968  'ml-IN',
6969  'mn-MN',
6970  'mr-IN',
6971  'ms-BN',
6972  'ms-MY',
6973  'mt-MT',
6974  'nb-NO',
6975  'ne-NP',
6976  'nl-BE',
6977  'nl-NL',
6978  'nn-NO',
6979  'oc-FR',
6980  'or-IN',
6981  'pa-IN',
6982  'pl-PL',
6983  'ps-AF',
6984  'pt-BR',
6985  'pt-PT',
6986  'rm-CH',
6987  'ro-RO',
6988  'ru-RU',
6989  'rw-RW',
6990  'sa-IN',
6991  'se-FI',
6992  'se-NO',
6993  'se-SE',
6994  'si-LK',
6995  'sk-SK',
6996  'sl-SI',
6997  'sq-AL',
6998  'sv-FI',
6999  'sv-SE',
7000  'sw-KE',
7001  'ta-IN',
7002  'te-IN',
7003  'th-TH',
7004  'tk-TM',
7005  'tn-ZA',
7006  'tr-TR',
7007  'tt-RU',
7008  'ug-CN',
7009  'uk-UA',
7010  'ur-PK',
7011  'vi-VN',
7012  'wo-SN',
7013  'xh-ZA',
7014  'yo-NG',
7015  'zh-CN',
7016  'zh-HK',
7017  'zh-MO',
7018  'zh-SG',
7019  'zh-TW',
7020  'zu-ZA',
7021  );
7022 
7023  $buildprimarykeytotest = strtolower($countrycode).'-'.strtoupper($countrycode);
7024  if (in_array($buildprimarykeytotest, $locales)) return strtolower($countrycode).'_'.strtoupper($countrycode);
7025 
7026  foreach ($locales as $locale)
7027  {
7028  $locale_language = locale_get_primary_language($locale);
7029  $locale_region = locale_get_region($locale);
7030  if (strtoupper($countrycode) == $locale_region)
7031  {
7032  //var_dump($locale.'-'.$locale_language.'-'.$locale_region);
7033  return strtolower($locale_language).'_'.strtoupper($locale_region);
7034  }
7035  }
7036 
7037  return null;
7038 }
7039 
7069 function complete_head_from_modules($conf,$langs,$object,&$head,&$h,$type,$mode='add')
7070 {
7071  global $hookmanager;
7072 
7073  if (isset($conf->modules_parts['tabs'][$type]) && is_array($conf->modules_parts['tabs'][$type]))
7074  {
7075  foreach ($conf->modules_parts['tabs'][$type] as $value)
7076  {
7077  $values=explode(':',$value);
7078 
7079  if ($mode == 'add' && ! preg_match('/^\-/',$values[1]))
7080  {
7081  if (count($values) == 6) // new declaration with permissions: $value='objecttype:+tabname1:Title1:langfile@mymodule:$user->rights->mymodule->read:/mymodule/mynewtab1.php?id=__ID__'
7082  {
7083  if ($values[0] != $type) continue;
7084 
7085  if (verifCond($values[4]))
7086  {
7087  if ($values[3]) $langs->load($values[3]);
7088  if (preg_match('/SUBSTITUTION_([^_]+)/i',$values[2],$reg))
7089  {
7090  $substitutionarray=array();
7091  complete_substitutions_array($substitutionarray,$langs,$object,array('needforkey'=>$values[2]));
7092  $label=make_substitutions($reg[1], $substitutionarray);
7093  }
7094  else $label=$langs->trans($values[2]);
7095 
7096  $head[$h][0] = dol_buildpath(preg_replace('/__ID__/i', ((is_object($object) && ! empty($object->id))?$object->id:''), $values[5]), 1);
7097  $head[$h][1] = $label;
7098  $head[$h][2] = str_replace('+','',$values[1]);
7099  $h++;
7100  }
7101  }
7102  else if (count($values) == 5) // deprecated
7103  {
7104  dol_syslog('Passing 5 values in tabs module_parts is deprecated. Please update to 6 with permissions.', LOG_WARNING);
7105 
7106  if ($values[0] != $type) continue;
7107  if ($values[3]) $langs->load($values[3]);
7108  if (preg_match('/SUBSTITUTION_([^_]+)/i',$values[2],$reg))
7109  {
7110  $substitutionarray=array();
7111  complete_substitutions_array($substitutionarray,$langs,$object,array('needforkey'=>$values[2]));
7112  $label=make_substitutions($reg[1], $substitutionarray);
7113  }
7114  else $label=$langs->trans($values[2]);
7115 
7116  $head[$h][0] = dol_buildpath(preg_replace('/__ID__/i', ((is_object($object) && ! empty($object->id))?$object->id:''), $values[4]), 1);
7117  $head[$h][1] = $label;
7118  $head[$h][2] = str_replace('+','',$values[1]);
7119  $h++;
7120  }
7121  }
7122  else if ($mode == 'remove' && preg_match('/^\-/',$values[1]))
7123  {
7124  if ($values[0] != $type) continue;
7125  $tabname=str_replace('-','',$values[1]);
7126  foreach($head as $key => $val)
7127  {
7128  $condition = (! empty($values[3]) ? verifCond($values[3]) : 1);
7129  //var_dump($key.' - '.$tabname.' - '.$head[$key][2].' - '.$values[3].' - '.$condition);
7130  if ($head[$key][2]==$tabname && $condition)
7131  {
7132  unset($head[$key]);
7133  break;
7134  }
7135  }
7136  }
7137  }
7138  }
7139 
7140  // No need to make a return $head. Var is modified as a reference
7141  if (! empty($hookmanager))
7142  {
7143  $parameters=array('object' => $object, 'mode' => $mode, 'head' => $head);
7144  $reshook=$hookmanager->executeHooks('completeTabsHead', $parameters);
7145  if ($reshook > 0)
7146  {
7147  $head = $hookmanager->resArray;
7148  $h = count($head);
7149  }
7150  }
7151 }
7152 
7164 function printCommonFooter($zone='private')
7165 {
7166  global $conf, $hookmanager, $user;
7167  global $action;
7168  global $micro_start_time;
7169 
7170  if ($zone == 'private') print "\n".'<!-- Common footer for private page -->'."\n";
7171  else print "\n".'<!-- Common footer for public page -->'."\n";
7172 
7173  // A div to store page_y POST parameter so we can read it using javascript
7174  print "\n<!-- A div to store page_y POST paramater -->\n";
7175  print '<div id="page_y" style="display: none;">'.$_POST['page_y'].'</div>'."\n";
7176 
7177  $parameters=array();
7178  $reshook=$hookmanager->executeHooks('printCommonFooter',$parameters); // Note that $action and $object may have been modified by some hooks
7179  if (empty($reshook))
7180  {
7181  if (! empty($conf->global->MAIN_HTML_FOOTER)) print $conf->global->MAIN_HTML_FOOTER."\n";
7182 
7183  print "\n";
7184  if (! empty($conf->use_javascript_ajax))
7185  {
7186  print '<script type="text/javascript" language="javascript">'."\n";
7187  print 'jQuery(document).ready(function() {'."\n";
7188 
7189  if ($zone == 'private' && empty($conf->dol_use_jmobile))
7190  {
7191  print "\n";
7192  print '/* JS CODE TO ENABLE to manage handler to switch left menu page (menuhider) */'."\n";
7193  print 'jQuery(".menuhider").click(function() {';
7194  print ' console.log("We click on .menuhider");'."\n";
7195  //print " $('.side-nav').animate({width:'toggle'},200);\n"; // OK with eldy theme but not with md
7196  print " $('.side-nav').toggle()\n";
7197  print " $('.login_block').toggle()\n";
7198  print '});'."\n";
7199  }
7200 
7201  // Management of focus and mandatory for fields
7202  if ($action == 'create' || $action == 'edit' || (empty($action) && (preg_match('/new\.php/', $_SERVER["PHP_SELF"]))))
7203  {
7204  print '/* JS CODE TO ENABLE to manage focus and mandatory form fields */'."\n";
7205  $relativepathstring = $_SERVER["PHP_SELF"];
7206  // Clean $relativepathstring
7207  if (constant('DOL_URL_ROOT')) $relativepathstring = preg_replace('/^'.preg_quote(constant('DOL_URL_ROOT'),'/').'/', '', $relativepathstring);
7208  $relativepathstring = preg_replace('/^\//', '', $relativepathstring);
7209  $relativepathstring = preg_replace('/^custom\//', '', $relativepathstring);
7210  $tmpqueryarraywehave=explode('&', dol_string_nohtmltag($_SERVER['QUERY_STRING']));
7211  if (!empty($user->default_values[$relativepathstring]['focus']))
7212  {
7213  foreach($user->default_values[$relativepathstring]['focus'] as $defkey => $defval)
7214  {
7215  $qualified = 0;
7216  if ($defkey != '_noquery_')
7217  {
7218  $tmpqueryarraytohave=explode('&', $defkey);
7219  $foundintru=0;
7220  foreach($tmpqueryarraytohave as $tmpquerytohave)
7221  {
7222  if (! in_array($tmpquerytohave, $tmpqueryarraywehave)) $foundintru=1;
7223  }
7224  if (! $foundintru) $qualified=1;
7225  //var_dump($defkey.'-'.$qualified);
7226  }
7227  else $qualified = 1;
7228 
7229  if ($qualified)
7230  {
7231  foreach($defval as $paramkey => $paramval)
7232  {
7233  // Set focus on field
7234  print 'jQuery("input[name=\''.$paramkey.'\']").focus();'."\n";
7235  print 'jQuery("textarea[name=\''.$paramkey.'\']").focus();'."\n";
7236  print 'jQuery("select[name=\''.$paramkey.'\']").focus();'."\n"; // Not really usefull, but we keep it in case of.
7237  }
7238  }
7239  }
7240  }
7241  if (!empty($user->default_values[$relativepathstring]['mandatory']))
7242  {
7243  foreach($user->default_values[$relativepathstring]['mandatory'] as $defkey => $defval)
7244  {
7245  $qualified = 0;
7246  if ($defkey != '_noquery_')
7247  {
7248  $tmpqueryarraytohave=explode('&', $defkey);
7249  $foundintru=0;
7250  foreach($tmpqueryarraytohave as $tmpquerytohave)
7251  {
7252  if (! in_array($tmpquerytohave, $tmpqueryarraywehave)) $foundintru=1;
7253  }
7254  if (! $foundintru) $qualified=1;
7255  //var_dump($defkey.'-'.$qualified);
7256  }
7257  else $qualified = 1;
7258 
7259  if ($qualified)
7260  {
7261  foreach($defval as $paramkey => $paramval)
7262  {
7263  // Add property 'required' on input
7264  print 'jQuery("input[name=\''.$paramkey.'\']").prop(\'required\',true);'."\n";
7265  print 'jQuery("textarea[name=\''.$paramkey.'\']").prop(\'required\',true);'."\n";
7266  print 'jQuery("select[name=\''.$paramkey.'\']").prop(\'required\',true);'."\n"; // required on a select works only if key is "", this does not happen in Dolibarr
7267  }
7268  }
7269  }
7270  }
7271  }
7272 
7273  print '});'."\n";
7274 
7275  // Google Analytics
7276  // TODO Add a hook here
7277  if (! empty($conf->google->enabled) && ! empty($conf->global->MAIN_GOOGLE_AN_ID))
7278  {
7279  if (($conf->dol_use_jmobile != 4))
7280  {
7281  print "\n";
7282  print "/* JS CODE TO ENABLE for google analtics tag */\n";
7283  print ' var _gaq = _gaq || [];'."\n";
7284  print ' _gaq.push([\'_setAccount\', \''.$conf->global->MAIN_GOOGLE_AN_ID.'\']);'."\n";
7285  print ' _gaq.push([\'_trackPageview\']);'."\n";
7286  print ''."\n";
7287  print ' (function() {'."\n";
7288  print ' var ga = document.createElement(\'script\'); ga.type = \'text/javascript\'; ga.async = true;'."\n";
7289  print ' ga.src = (\'https:\' == document.location.protocol ? \'https://ssl\' : \'http://www\') + \'.google-analytics.com/ga.js\';'."\n";
7290  print ' var s = document.getElementsByTagName(\'script\')[0]; s.parentNode.insertBefore(ga, s);'."\n";
7291  print ' })();'."\n";
7292  }
7293  }
7294 
7295  // End of tuning
7296  if (! empty($_SERVER['MAIN_SHOW_TUNING_INFO']) || ! empty($conf->global->MAIN_SHOW_TUNING_INFO))
7297  {
7298  print "\n";
7299  print "/* JS CODE TO ENABLE to add memory info */\n";
7300  print 'window.console && console.log("';
7301  if (! empty($conf->global->MEMCACHED_SERVER)) print 'MEMCACHED_SERVER='.$conf->global->MEMCACHED_SERVER.' - ';
7302  print 'MAIN_OPTIMIZE_SPEED='.(isset($conf->global->MAIN_OPTIMIZE_SPEED)?$conf->global->MAIN_OPTIMIZE_SPEED:'off');
7303  if (! empty($micro_start_time)) // Works only if MAIN_SHOW_TUNING_INFO is defined at $_SERVER level. Not in global variable.
7304  {
7305  $micro_end_time = microtime(true);
7306  print ' - Build time: '.ceil(1000*($micro_end_time-$micro_start_time)).' ms';
7307  }
7308  if (function_exists("memory_get_usage"))
7309  {
7310  print ' - Mem: '.memory_get_usage();
7311  }
7312  if (function_exists("xdebug_memory_usage"))
7313  {
7314  print ' - XDebug time: '.ceil(1000*xdebug_time_index()).' ms';
7315  print ' - XDebug mem: '.xdebug_memory_usage();
7316  print ' - XDebug mem peak: '.xdebug_peak_memory_usage();
7317  }
7318  if (function_exists("zend_loader_file_encoded"))
7319  {
7320  print ' - Zend encoded file: '.(zend_loader_file_encoded()?'yes':'no');
7321  }
7322  print '");'."\n";
7323  }
7324 
7325  print "\n".'</script>'."\n";
7326  }
7327 
7328  // Add Xdebug coverage of code
7329  if (defined('XDEBUGCOVERAGE'))
7330  {
7331  print_r(xdebug_get_code_coverage());
7332  }
7333 
7334  // If there is some logs in buffer to show
7335  if (count($conf->logbuffer))
7336  {
7337  print "\n";
7338  print "<!-- Start of log output\n";
7339  //print '<div class="hidden">'."\n";
7340  foreach($conf->logbuffer as $logline)
7341  {
7342  print $logline."<br>\n";
7343  }
7344  //print '</div>'."\n";
7345  print "End of log output -->\n";
7346  }
7347  }
7348 }
7349 
7359 function dolExplodeIntoArray($string, $delimiter = ';', $kv = '=')
7360 {
7361  if ($a = explode($delimiter, $string))
7362  {
7363  $ka = array();
7364  foreach ($a as $s) { // each part
7365  if ($s) {
7366  if ($pos = strpos($s, $kv)) { // key/value delimiter
7367  $ka[trim(substr($s, 0, $pos))] = trim(substr($s, $pos + strlen($kv)));
7368  } else { // key delimiter not found
7369  $ka[] = trim($s);
7370  }
7371  }
7372  }
7373  return $ka;
7374  }
7375  return array();
7376 }
7377 
7378 
7385 function dol_set_focus($selector)
7386 {
7387  print "\n".'<!-- Set focus onto a specific field -->'."\n";
7388  print '<script type="text/javascript" language="javascript">jQuery(document).ready(function() { jQuery("'.dol_escape_js($selector).'").focus(); });</script>'."\n";
7389 }
7390 
7391 
7399 function dol_getmypid()
7400 {
7401  if (! function_exists('getmypid')) {
7402  return mt_rand(1,32768);
7403  } else {
7404  return getmypid();
7405  }
7406 }
7407 
7408 
7425 function natural_search($fields, $value, $mode=0, $nofirstand=0)
7426 {
7427  global $db,$langs;
7428 
7429  $value=trim($value);
7430 
7431  if ($mode == 0)
7432  {
7433  $value=preg_replace('/\*/','%',$value); // Replace * with %
7434  }
7435  if ($mode == 1)
7436  {
7437  $value=preg_replace('/([<>=]+)\s+([0-9'.preg_quote($langs->trans("DecimalSeparator"),'/').'\-])/','\1\2',$value); // Clean string '< 10' into '<10' so we can the explode on space to get all tests to do
7438  }
7439 
7440  $value = preg_replace('/\s*\|\s*/','|', $value);
7441 
7442  $crits = explode(' ', $value);
7443  $res = '';
7444  if (! is_array($fields)) $fields = array($fields);
7445 
7446  $nboffields = count($fields);
7447  $end2 = count($crits);
7448  $j = 0;
7449  foreach ($crits as $crit)
7450  {
7451  $i = 0; $i2 = 0;
7452  $newres = '';
7453  foreach ($fields as $field)
7454  {
7455  if ($mode == 1)
7456  {
7457  $operator='=';
7458  $newcrit = preg_replace('/([<>=]+)/','',trim($crit));
7459 
7460  preg_match('/([<>=]+)/',trim($crit), $reg);
7461  if ($reg[1])
7462  {
7463  $operator = $reg[1];
7464  }
7465  if ($newcrit != '')
7466  {
7467  $numnewcrit = price2num($newcrit);
7468  if (is_numeric($numnewcrit))
7469  {