dolibarr  17.0.4
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-2022 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-2019 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-2021 Alexandre Spangaro <aspangaro@open-dsi.fr>
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-2022 Frédéric France <frederic.france@netlogic.fr>
17  * Copyright (C) 2019-2022 Thibault Foucart <support@ptibogxiv.net>
18  * Copyright (C) 2020 Open-Dsi <support@open-dsi.fr>
19  * Copyright (C) 2021 Gauthier VERDOL <gauthier.verdol@atm-consulting.fr>
20  * Copyright (C) 2022 Anthony Berton <anthony.berton@bb2a.fr>
21  * Copyright (C) 2022 Ferran Marcet <fmarcet@2byte.es>
22  * Copyright (C) 2022 Charlene Benke <charlene@patas-monkey.com>
23  *
24  * This program is free software; you can redistribute it and/or modify
25  * it under the terms of the GNU General Public License as published by
26  * the Free Software Foundation; either version 3 of the License, or
27  * (at your option) any later version.
28  *
29  * This program is distributed in the hope that it will be useful,
30  * but WITHOUT ANY WARRANTY; without even the implied warranty of
31  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
32  * GNU General Public License for more details.
33  *
34  * You should have received a copy of the GNU General Public License
35  * along with this program. If not, see <https://www.gnu.org/licenses/>.
36  * or see https://www.gnu.org/
37  */
38 
45 include_once DOL_DOCUMENT_ROOT.'/core/lib/json.lib.php';
46 
47 
48 if (!function_exists('utf8_encode')) {
55  function utf8_encode($elements)
56  {
57  return mb_convert_encoding($elements, 'UTF-8', 'ISO-8859-1');
58  }
59 }
60 
61 if (!function_exists('utf8_decode')) {
68  function utf8_decode($elements)
69  {
70  return mb_convert_encoding($elements, 'ISO-8859-1', 'UTF-8');
71  }
72 }
73 
74 
82 function getDolGlobalString($key, $default = '')
83 {
84  global $conf;
85  // return $conf->global->$key ?? $default;
86  return (string) (isset($conf->global->$key) ? $conf->global->$key : $default);
87 }
88 
96 function getDolGlobalInt($key, $default = 0)
97 {
98  global $conf;
99  // return $conf->global->$key ?? $default;
100  return (int) (isset($conf->global->$key) ? $conf->global->$key : $default);
101 }
102 
110 function getDolUserString($key, $default = '')
111 {
112  global $user;
113  // return $conf->global->$key ?? $default;
114  return (string) (empty($user->conf->$key) ? $default : $user->conf->$key);
115 }
116 
124 function getDolUserInt($key, $default = 0)
125 {
126  global $user;
127  // return $conf->global->$key ?? $default;
128  return (int) (empty($user->conf->$key) ? $default : $user->conf->$key);
129 }
130 
137 function isModEnabled($module)
138 {
139  global $conf;
140  return !empty($conf->$module->enabled);
141 }
142 
154 function getDoliDBInstance($type, $host, $user, $pass, $name, $port)
155 {
156  require_once DOL_DOCUMENT_ROOT."/core/db/".$type.'.class.php';
157 
158  $class = 'DoliDB'.ucfirst($type);
159  $dolidb = new $class($type, $host, $user, $pass, $name, $port);
160  return $dolidb;
161 }
162 
180 function getEntity($element, $shared = 1, $currentobject = null)
181 {
182  global $conf, $mc, $hookmanager, $object, $action, $db;
183 
184  if (! is_object($hookmanager)) {
185  $hookmanager = new HookManager($db);
186  }
187 
188  // fix different element names (France to English)
189  switch ($element) {
190  case 'contrat':
191  $element = 'contract';
192  break; // "/contrat/class/contrat.class.php"
193  case 'order_supplier':
194  $element = 'supplier_order';
195  break; // "/fourn/class/fournisseur.commande.class.php"
196  case 'invoice_supplier':
197  $element = 'supplier_invoice';
198  break; // "/fourn/class/fournisseur.facture.class.php"
199  }
200 
201  if (is_object($mc)) {
202  $out = $mc->getEntity($element, $shared, $currentobject);
203  } else {
204  $out = '';
205  $addzero = array('user', 'usergroup', 'c_email_templates', 'email_template', 'default_values');
206  if (in_array($element, $addzero)) {
207  $out .= '0,';
208  }
209  $out .= ((int) $conf->entity);
210  }
211 
212  // Manipulate entities to query on the fly
213  $parameters = array(
214  'element' => $element,
215  'shared' => $shared,
216  'object' => $object,
217  'currentobject' => $currentobject,
218  'out' => $out
219  );
220  $reshook = $hookmanager->executeHooks('hookGetEntity', $parameters, $currentobject, $action); // Note that $action and $object may have been modified by some hooks
221 
222  if (is_numeric($reshook)) {
223  if ($reshook == 0 && !empty($hookmanager->resPrint)) {
224  $out .= ','.$hookmanager->resPrint; // add
225  } elseif ($reshook == 1) {
226  $out = $hookmanager->resPrint; // replace
227  }
228  }
229 
230  return $out;
231 }
232 
239 function setEntity($currentobject)
240 {
241  global $conf, $mc;
242 
243  if (is_object($mc) && method_exists($mc, 'setEntity')) {
244  return $mc->setEntity($currentobject);
245  } else {
246  return ((is_object($currentobject) && $currentobject->id > 0 && $currentobject->entity > 0) ? $currentobject->entity : $conf->entity);
247  }
248 }
249 
256 function isASecretKey($keyname)
257 {
258  return preg_match('/(_pass|password|_pw|_key|securekey|serverkey|secret\d?|p12key|exportkey|_PW_[a-z]+|token)$/i', $keyname);
259 }
260 
261 
268 function num2Alpha($n)
269 {
270  for ($r = ""; $n >= 0; $n = intval($n / 26) - 1)
271  $r = chr($n % 26 + 0x41) . $r;
272  return $r;
273 }
274 
275 
292 function getBrowserInfo($user_agent)
293 {
294  include_once DOL_DOCUMENT_ROOT.'/includes/mobiledetect/mobiledetectlib/Mobile_Detect.php';
295 
296  $name = 'unknown';
297  $version = '';
298  $os = 'unknown';
299  $phone = '';
300 
301  $user_agent = substr($user_agent, 0, 512); // Avoid to process too large user agent
302 
303  $detectmobile = new Mobile_Detect(null, $user_agent);
304  $tablet = $detectmobile->isTablet();
305 
306  if ($detectmobile->isMobile()) {
307  $phone = 'unknown';
308 
309  // If phone/smartphone, we set phone os name.
310  if ($detectmobile->is('AndroidOS')) {
311  $os = $phone = 'android';
312  } elseif ($detectmobile->is('BlackBerryOS')) {
313  $os = $phone = 'blackberry';
314  } elseif ($detectmobile->is('iOS')) {
315  $os = 'ios';
316  $phone = 'iphone';
317  } elseif ($detectmobile->is('PalmOS')) {
318  $os = $phone = 'palm';
319  } elseif ($detectmobile->is('SymbianOS')) {
320  $os = 'symbian';
321  } elseif ($detectmobile->is('webOS')) {
322  $os = 'webos';
323  } elseif ($detectmobile->is('MaemoOS')) {
324  $os = 'maemo';
325  } elseif ($detectmobile->is('WindowsMobileOS') || $detectmobile->is('WindowsPhoneOS')) {
326  $os = 'windows';
327  }
328  }
329 
330  // OS
331  if (preg_match('/linux/i', $user_agent)) {
332  $os = 'linux';
333  } elseif (preg_match('/macintosh/i', $user_agent)) {
334  $os = 'macintosh';
335  } elseif (preg_match('/windows/i', $user_agent)) {
336  $os = 'windows';
337  }
338 
339  // Name
340  $reg = array();
341  if (preg_match('/firefox(\/|\s)([\d\.]*)/i', $user_agent, $reg)) {
342  $name = 'firefox';
343  $version = empty($reg[2]) ? '' : $reg[2];
344  } elseif (preg_match('/edge(\/|\s)([\d\.]*)/i', $user_agent, $reg)) {
345  $name = 'edge';
346  $version = empty($reg[2]) ? '' : $reg[2];
347  } elseif (preg_match('/chrome(\/|\s)([\d\.]+)/i', $user_agent, $reg)) {
348  $name = 'chrome';
349  $version = empty($reg[2]) ? '' : $reg[2];
350  } elseif (preg_match('/chrome/i', $user_agent, $reg)) {
351  // we can have 'chrome (Mozilla...) chrome x.y' in one string
352  $name = 'chrome';
353  } elseif (preg_match('/iceweasel/i', $user_agent)) {
354  $name = 'iceweasel';
355  } elseif (preg_match('/epiphany/i', $user_agent)) {
356  $name = 'epiphany';
357  } elseif (preg_match('/safari(\/|\s)([\d\.]*)/i', $user_agent, $reg)) {
358  $name = 'safari';
359  $version = empty($reg[2]) ? '' : $reg[2];
360  } elseif (preg_match('/opera(\/|\s)([\d\.]*)/i', $user_agent, $reg)) {
361  // Safari is often present in string for mobile but its not.
362  $name = 'opera';
363  $version = empty($reg[2]) ? '' : $reg[2];
364  } elseif (preg_match('/(MSIE\s([0-9]+\.[0-9]))|.*(Trident\/[0-9]+.[0-9];.*rv:([0-9]+\.[0-9]+))/i', $user_agent, $reg)) {
365  $name = 'ie';
366  $version = end($reg);
367  } elseif (preg_match('/(Windows NT\s([0-9]+\.[0-9])).*(Trident\/[0-9]+.[0-9];.*rv:([0-9]+\.[0-9]+))/i', $user_agent, $reg)) {
368  // MS products at end
369  $name = 'ie';
370  $version = end($reg);
371  } elseif (preg_match('/l[iy]n(x|ks)(\‍(|\/|\s)*([\d\.]+)/i', $user_agent, $reg)) {
372  // MS products at end
373  $name = 'lynxlinks';
374  $version = empty($reg[3]) ? '' : $reg[3];
375  }
376 
377  if ($tablet) {
378  $layout = 'tablet';
379  } elseif ($phone) {
380  $layout = 'phone';
381  } else {
382  $layout = 'classic';
383  }
384 
385  return array(
386  'browsername' => $name,
387  'browserversion' => $version,
388  'browseros' => $os,
389  'layout' => $layout,
390  'phone' => $phone,
391  'tablet' => $tablet
392  );
393 }
394 
400 function dol_shutdown()
401 {
402  global $conf, $user, $langs, $db;
403  $disconnectdone = false;
404  $depth = 0;
405  if (is_object($db) && !empty($db->connected)) {
406  $depth = $db->transaction_opened;
407  $disconnectdone = $db->close();
408  }
409  dol_syslog("--- End access to ".$_SERVER["PHP_SELF"].(($disconnectdone && $depth) ? ' (Warn: db disconnection forced, transaction depth was '.$depth.')' : ''), (($disconnectdone && $depth) ? LOG_WARNING : LOG_INFO));
410 }
411 
421 function GETPOSTISSET($paramname)
422 {
423  $isset = false;
424 
425  $relativepathstring = $_SERVER["PHP_SELF"];
426  // Clean $relativepathstring
427  if (constant('DOL_URL_ROOT')) {
428  $relativepathstring = preg_replace('/^'.preg_quote(constant('DOL_URL_ROOT'), '/').'/', '', $relativepathstring);
429  }
430  $relativepathstring = preg_replace('/^\//', '', $relativepathstring);
431  $relativepathstring = preg_replace('/^custom\//', '', $relativepathstring);
432  //var_dump($relativepathstring);
433  //var_dump($user->default_values);
434 
435  // Code for search criteria persistence.
436  // Retrieve values if restore_lastsearch_values
437  if (!empty($_GET['restore_lastsearch_values'])) { // Use $_GET here and not GETPOST
438  if (!empty($_SESSION['lastsearch_values_'.$relativepathstring])) { // If there is saved values
439  $tmp = json_decode($_SESSION['lastsearch_values_'.$relativepathstring], true);
440  if (is_array($tmp)) {
441  foreach ($tmp as $key => $val) {
442  if ($key == $paramname) { // We are on the requested parameter
443  $isset = true;
444  break;
445  }
446  }
447  }
448  }
449  // If there is saved contextpage, limit, page or mode
450  if ($paramname == 'contextpage' && !empty($_SESSION['lastsearch_contextpage_'.$relativepathstring])) {
451  $isset = true;
452  } elseif ($paramname == 'limit' && !empty($_SESSION['lastsearch_limit_'.$relativepathstring])) {
453  $isset = true;
454  } elseif ($paramname == 'page' && !empty($_SESSION['lastsearch_page_'.$relativepathstring])) {
455  $isset = true;
456  } elseif ($paramname == 'mode' && !empty($_SESSION['lastsearch_mode_'.$relativepathstring])) {
457  $isset = true;
458  }
459  } else {
460  $isset = (isset($_POST[$paramname]) || isset($_GET[$paramname])); // We must keep $_POST and $_GET here
461  }
462 
463  return $isset;
464 }
465 
474 function GETPOSTISARRAY($paramname, $method = 0)
475 {
476  // for $method test need return the same $val as GETPOST
477  if (empty($method)) {
478  $val = isset($_GET[$paramname]) ? $_GET[$paramname] : (isset($_POST[$paramname]) ? $_POST[$paramname] : '');
479  } elseif ($method == 1) {
480  $val = isset($_GET[$paramname]) ? $_GET[$paramname] : '';
481  } elseif ($method == 2) {
482  $val = isset($_POST[$paramname]) ? $_POST[$paramname] : '';
483  } elseif ($method == 3) {
484  $val = isset($_POST[$paramname]) ? $_POST[$paramname] : (isset($_GET[$paramname]) ? $_GET[$paramname] : '');
485  } else {
486  $val = 'BadFirstParameterForGETPOST';
487  }
488 
489  return is_array($val);
490 }
491 
520 function GETPOST($paramname, $check = 'alphanohtml', $method = 0, $filter = null, $options = null, $noreplace = 0)
521 {
522  global $mysoc, $user, $conf;
523 
524  if (empty($paramname)) {
525  return 'BadFirstParameterForGETPOST';
526  }
527  if (empty($check)) {
528  dol_syslog("Deprecated use of GETPOST, called with 1st param = ".$paramname." and 2nd param is '', when calling page ".$_SERVER["PHP_SELF"], LOG_WARNING);
529  // Enable this line to know who call the GETPOST with '' $check parameter.
530  //var_dump(debug_backtrace()[0]);
531  }
532 
533  if (empty($method)) {
534  $out = isset($_GET[$paramname]) ? $_GET[$paramname] : (isset($_POST[$paramname]) ? $_POST[$paramname] : '');
535  } elseif ($method == 1) {
536  $out = isset($_GET[$paramname]) ? $_GET[$paramname] : '';
537  } elseif ($method == 2) {
538  $out = isset($_POST[$paramname]) ? $_POST[$paramname] : '';
539  } elseif ($method == 3) {
540  $out = isset($_POST[$paramname]) ? $_POST[$paramname] : (isset($_GET[$paramname]) ? $_GET[$paramname] : '');
541  } else {
542  return 'BadThirdParameterForGETPOST';
543  }
544 
545  if (empty($method) || $method == 3 || $method == 4) {
546  $relativepathstring = $_SERVER["PHP_SELF"];
547  // Clean $relativepathstring
548  if (constant('DOL_URL_ROOT')) {
549  $relativepathstring = preg_replace('/^'.preg_quote(constant('DOL_URL_ROOT'), '/').'/', '', $relativepathstring);
550  }
551  $relativepathstring = preg_replace('/^\//', '', $relativepathstring);
552  $relativepathstring = preg_replace('/^custom\//', '', $relativepathstring);
553  //var_dump($relativepathstring);
554  //var_dump($user->default_values);
555 
556  // Code for search criteria persistence.
557  // Retrieve values if restore_lastsearch_values
558  if (!empty($_GET['restore_lastsearch_values'])) { // Use $_GET here and not GETPOST
559  if (!empty($_SESSION['lastsearch_values_'.$relativepathstring])) { // If there is saved values
560  $tmp = json_decode($_SESSION['lastsearch_values_'.$relativepathstring], true);
561  if (is_array($tmp)) {
562  foreach ($tmp as $key => $val) {
563  if ($key == $paramname) { // We are on the requested parameter
564  $out = $val;
565  break;
566  }
567  }
568  }
569  }
570  // If there is saved contextpage, page or limit
571  if ($paramname == 'contextpage' && !empty($_SESSION['lastsearch_contextpage_'.$relativepathstring])) {
572  $out = $_SESSION['lastsearch_contextpage_'.$relativepathstring];
573  } elseif ($paramname == 'limit' && !empty($_SESSION['lastsearch_limit_'.$relativepathstring])) {
574  $out = $_SESSION['lastsearch_limit_'.$relativepathstring];
575  } elseif ($paramname == 'page' && !empty($_SESSION['lastsearch_page_'.$relativepathstring])) {
576  $out = $_SESSION['lastsearch_page_'.$relativepathstring];
577  } elseif ($paramname == 'mode' && !empty($_SESSION['lastsearch_mode_'.$relativepathstring])) {
578  $out = $_SESSION['lastsearch_mode_'.$relativepathstring];
579  }
580  } elseif (!isset($_GET['sortfield'])) {
581  // Else, retrieve default values if we are not doing a sort
582  // 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
583  if (!empty($_GET['action']) && $_GET['action'] == 'create' && !isset($_GET[$paramname]) && !isset($_POST[$paramname])) {
584  // Search default value from $object->field
585  global $object;
586  if (is_object($object) && isset($object->fields[$paramname]['default'])) {
587  $out = $object->fields[$paramname]['default'];
588  }
589  }
590  if (!empty($conf->global->MAIN_ENABLE_DEFAULT_VALUES)) {
591  if (!empty($_GET['action']) && (preg_match('/^create|^add_price|^make/', $_GET['action']) || preg_match('/^presend/', $_GET['action'])) && !isset($_GET[$paramname]) && !isset($_POST[$paramname])) {
592  // Now search in setup to overwrite default values
593  if (!empty($user->default_values)) { // $user->default_values defined from menu 'Setup - Default values'
594  if (isset($user->default_values[$relativepathstring]['createform'])) {
595  foreach ($user->default_values[$relativepathstring]['createform'] as $defkey => $defval) {
596  $qualified = 0;
597  if ($defkey != '_noquery_') {
598  $tmpqueryarraytohave = explode('&', $defkey);
599  $tmpqueryarraywehave = explode('&', dol_string_nohtmltag($_SERVER['QUERY_STRING']));
600  $foundintru = 0;
601  foreach ($tmpqueryarraytohave as $tmpquerytohave) {
602  if (!in_array($tmpquerytohave, $tmpqueryarraywehave)) {
603  $foundintru = 1;
604  }
605  }
606  if (!$foundintru) {
607  $qualified = 1;
608  }
609  //var_dump($defkey.'-'.$qualified);
610  } else {
611  $qualified = 1;
612  }
613 
614  if ($qualified) {
615  if (isset($user->default_values[$relativepathstring]['createform'][$defkey][$paramname])) {
616  $out = $user->default_values[$relativepathstring]['createform'][$defkey][$paramname];
617  break;
618  }
619  }
620  }
621  }
622  }
623  } elseif (!empty($paramname) && !isset($_GET[$paramname]) && !isset($_POST[$paramname])) {
624  // Management of default search_filters and sort order
625  if (!empty($user->default_values)) {
626  // $user->default_values defined from menu 'Setup - Default values'
627  //var_dump($user->default_values[$relativepathstring]);
628  if ($paramname == 'sortfield' || $paramname == 'sortorder') {
629  // Sorted on which fields ? ASC or DESC ?
630  if (isset($user->default_values[$relativepathstring]['sortorder'])) {
631  // Even if paramname is sortfield, data are stored into ['sortorder...']
632  foreach ($user->default_values[$relativepathstring]['sortorder'] as $defkey => $defval) {
633  $qualified = 0;
634  if ($defkey != '_noquery_') {
635  $tmpqueryarraytohave = explode('&', $defkey);
636  $tmpqueryarraywehave = explode('&', dol_string_nohtmltag($_SERVER['QUERY_STRING']));
637  $foundintru = 0;
638  foreach ($tmpqueryarraytohave as $tmpquerytohave) {
639  if (!in_array($tmpquerytohave, $tmpqueryarraywehave)) {
640  $foundintru = 1;
641  }
642  }
643  if (!$foundintru) {
644  $qualified = 1;
645  }
646  //var_dump($defkey.'-'.$qualified);
647  } else {
648  $qualified = 1;
649  }
650 
651  if ($qualified) {
652  $forbidden_chars_to_replace = array(" ", "'", "/", "\\", ":", "*", "?", "\"", "<", ">", "|", "[", "]", ";", "="); // we accept _, -, . and ,
653  foreach ($user->default_values[$relativepathstring]['sortorder'][$defkey] as $key => $val) {
654  if ($out) {
655  $out .= ', ';
656  }
657  if ($paramname == 'sortfield') {
658  $out .= dol_string_nospecial($key, '', $forbidden_chars_to_replace);
659  }
660  if ($paramname == 'sortorder') {
661  $out .= dol_string_nospecial($val, '', $forbidden_chars_to_replace);
662  }
663  }
664  //break; // No break for sortfield and sortorder so we can cumulate fields (is it realy usefull ?)
665  }
666  }
667  }
668  } elseif (isset($user->default_values[$relativepathstring]['filters'])) {
669  foreach ($user->default_values[$relativepathstring]['filters'] as $defkey => $defval) { // $defkey is a querystring like 'a=b&c=d', $defval is key of user
670  if (!empty($_GET['disabledefaultvalues'])) { // If set of default values has been disabled by a request parameter
671  continue;
672  }
673  $qualified = 0;
674  if ($defkey != '_noquery_') {
675  $tmpqueryarraytohave = explode('&', $defkey);
676  $tmpqueryarraywehave = explode('&', dol_string_nohtmltag($_SERVER['QUERY_STRING']));
677  $foundintru = 0;
678  foreach ($tmpqueryarraytohave as $tmpquerytohave) {
679  if (!in_array($tmpquerytohave, $tmpqueryarraywehave)) {
680  $foundintru = 1;
681  }
682  }
683  if (!$foundintru) {
684  $qualified = 1;
685  }
686  //var_dump($defkey.'-'.$qualified);
687  } else {
688  $qualified = 1;
689  }
690 
691  if ($qualified) {
692  // We must keep $_POST and $_GET here
693  if (isset($_POST['sall']) || isset($_POST['search_all']) || isset($_GET['sall']) || isset($_GET['search_all'])) {
694  // We made a search from quick search menu, do we still use default filter ?
695  if (empty($conf->global->MAIN_DISABLE_DEFAULT_FILTER_FOR_QUICK_SEARCH)) {
696  $forbidden_chars_to_replace = array(" ", "'", "/", "\\", ":", "*", "?", "\"", "<", ">", "|", "[", "]", ";", "="); // we accept _, -, . and ,
697  $out = dol_string_nospecial($user->default_values[$relativepathstring]['filters'][$defkey][$paramname], '', $forbidden_chars_to_replace);
698  }
699  } else {
700  $forbidden_chars_to_replace = array(" ", "'", "/", "\\", ":", "*", "?", "\"", "<", ">", "|", "[", "]", ";", "="); // we accept _, -, . and ,
701  $out = dol_string_nospecial($user->default_values[$relativepathstring]['filters'][$defkey][$paramname], '', $forbidden_chars_to_replace);
702  }
703  break;
704  }
705  }
706  }
707  }
708  }
709  }
710  }
711  }
712 
713  // Substitution variables for GETPOST (used to get final url with variable parameters or final default value with variable parameters)
714  // Example of variables: __DAY__, __MONTH__, __YEAR__, __MYCOMPANY_COUNTRY_ID__, __USER_ID__, ...
715  // 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.
716  if (!is_array($out) && empty($_POST[$paramname]) && empty($noreplace)) {
717  $reg = array();
718  $maxloop = 20;
719  $loopnb = 0; // Protection against infinite loop
720  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.
721  $loopnb++;
722  $newout = '';
723 
724  if ($reg[1] == 'DAY') {
725  $tmp = dol_getdate(dol_now(), true);
726  $newout = $tmp['mday'];
727  } elseif ($reg[1] == 'MONTH') {
728  $tmp = dol_getdate(dol_now(), true);
729  $newout = $tmp['mon'];
730  } elseif ($reg[1] == 'YEAR') {
731  $tmp = dol_getdate(dol_now(), true);
732  $newout = $tmp['year'];
733  } elseif ($reg[1] == 'PREVIOUS_DAY') {
734  $tmp = dol_getdate(dol_now(), true);
735  $tmp2 = dol_get_prev_day($tmp['mday'], $tmp['mon'], $tmp['year']);
736  $newout = $tmp2['day'];
737  } elseif ($reg[1] == 'PREVIOUS_MONTH') {
738  $tmp = dol_getdate(dol_now(), true);
739  $tmp2 = dol_get_prev_month($tmp['mon'], $tmp['year']);
740  $newout = $tmp2['month'];
741  } elseif ($reg[1] == 'PREVIOUS_YEAR') {
742  $tmp = dol_getdate(dol_now(), true);
743  $newout = ($tmp['year'] - 1);
744  } elseif ($reg[1] == 'NEXT_DAY') {
745  $tmp = dol_getdate(dol_now(), true);
746  $tmp2 = dol_get_next_day($tmp['mday'], $tmp['mon'], $tmp['year']);
747  $newout = $tmp2['day'];
748  } elseif ($reg[1] == 'NEXT_MONTH') {
749  $tmp = dol_getdate(dol_now(), true);
750  $tmp2 = dol_get_next_month($tmp['mon'], $tmp['year']);
751  $newout = $tmp2['month'];
752  } elseif ($reg[1] == 'NEXT_YEAR') {
753  $tmp = dol_getdate(dol_now(), true);
754  $newout = ($tmp['year'] + 1);
755  } elseif ($reg[1] == 'MYCOMPANY_COUNTRY_ID' || $reg[1] == 'MYCOUNTRY_ID' || $reg[1] == 'MYCOUNTRYID') {
756  $newout = $mysoc->country_id;
757  } elseif ($reg[1] == 'USER_ID' || $reg[1] == 'USERID') {
758  $newout = $user->id;
759  } elseif ($reg[1] == 'USER_SUPERVISOR_ID' || $reg[1] == 'SUPERVISOR_ID' || $reg[1] == 'SUPERVISORID') {
760  $newout = $user->fk_user;
761  } elseif ($reg[1] == 'ENTITY_ID' || $reg[1] == 'ENTITYID') {
762  $newout = $conf->entity;
763  } else {
764  $newout = ''; // Key not found, we replace with empty string
765  }
766  //var_dump('__'.$reg[1].'__ -> '.$newout);
767  $out = preg_replace('/__'.preg_quote($reg[1], '/').'__/', $newout, $out);
768  }
769  }
770 
771  // Check rule
772  if (preg_match('/^array/', $check)) { // If 'array' or 'array:restricthtml' or 'array:aZ09' or 'array:intcomma'
773  if (!is_array($out) || empty($out)) {
774  $out = array();
775  } else {
776  $tmparray = explode(':', $check);
777  if (!empty($tmparray[1])) {
778  $tmpcheck = $tmparray[1];
779  } else {
780  $tmpcheck = 'alphanohtml';
781  }
782  foreach ($out as $outkey => $outval) {
783  $out[$outkey] = sanitizeVal($outval, $tmpcheck, $filter, $options);
784  }
785  }
786  } else {
787  // If field name is 'search_xxx' then we force the add of space after each < and > (when following char is numeric) because it means
788  // we use the < or > to make a search on a numeric value to do higher or lower so we can add a space to break html tags
789  if (strpos($paramname, 'search_') === 0) {
790  $out = preg_replace('/([<>])([-+]?\d)/', '\1 \2', $out);
791  }
792 
793  $out = sanitizeVal($out, $check, $filter, $options);
794  }
795 
796  // Sanitizing for special parameters.
797  // Note: There is no reason to allow the backtopage, backtolist or backtourl parameter to contains an external URL. Only relative URLs are allowed.
798  if ($paramname == 'backtopage' || $paramname == 'backtolist' || $paramname == 'backtourl') {
799  $out = str_replace('\\', '/', $out); // Can be before the loop because only 1 char is replaced. No risk to get it after other replacements.
800  $out = str_replace(array(':', ';', '@', "\t", ' '), '', $out); // Can be before the loop because only 1 char is replaced. No risk to retreive it after other replacements.
801  do {
802  $oldstringtoclean = $out;
803  $out = str_ireplace(array('javascript', 'vbscript', '&colon', '&#'), '', $out);
804  $out = preg_replace(array('/^[^\?]*%/'), '', $out); // We remove any % chars before the ?. Example in url: '/product/stock/card.php?action=create&backtopage=%2Fdolibarr_dev%2Fhtdocs%2Fpro%25duct%2Fcard.php%3Fid%3Dabc'
805  $out = preg_replace(array('/^[a-z]*\/\s*\/+/i'), '', $out); // We remove schema*// to remove external URL
806  } while ($oldstringtoclean != $out);
807  }
808 
809  // Code for search criteria persistence.
810  // Save data into session if key start with 'search_' or is 'smonth', 'syear', 'month', 'year'
811  if (empty($method) || $method == 3 || $method == 4) {
812  if (preg_match('/^search_/', $paramname) || in_array($paramname, array('sortorder', 'sortfield'))) {
813  //var_dump($paramname.' - '.$out.' '.$user->default_values[$relativepathstring]['filters'][$paramname]);
814 
815  // We save search key only if $out not empty that means:
816  // - posted value not empty, or
817  // - 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).
818 
819  if ($out != '' && isset($user)) {// $out = '0' or 'abc', it is a search criteria to keep
820  $user->lastsearch_values_tmp[$relativepathstring][$paramname] = $out;
821  }
822  }
823  }
824 
825  return $out;
826 }
827 
837 function GETPOSTINT($paramname, $method = 0)
838 {
839  return (int) GETPOST($paramname, 'int', $method, null, null, 0);
840 }
841 
842 
853 function checkVal($out = '', $check = 'alphanohtml', $filter = null, $options = null)
854 {
855  return sanitizeVal($out, $check, $filter, $options);
856 }
857 
867 function sanitizeVal($out = '', $check = 'alphanohtml', $filter = null, $options = null)
868 {
869  global $conf;
870 
871  // TODO : use class "Validate" to perform tests (and add missing tests) if needed for factorize
872  // Check is done after replacement
873  switch ($check) {
874  case 'none':
875  break;
876  case 'int': // Check param is a numeric value (integer but also float or hexadecimal)
877  if (!is_numeric($out)) {
878  $out = '';
879  }
880  break;
881  case 'intcomma':
882  if (preg_match('/[^0-9,-]+/i', $out)) {
883  $out = '';
884  }
885  break;
886  case 'san_alpha':
887  $out = filter_var($out, FILTER_SANITIZE_STRING);
888  break;
889  case 'email':
890  $out = filter_var($out, FILTER_SANITIZE_EMAIL);
891  break;
892  case 'aZ':
893  if (!is_array($out)) {
894  $out = trim($out);
895  if (preg_match('/[^a-z]+/i', $out)) {
896  $out = '';
897  }
898  }
899  break;
900  case 'aZ09':
901  if (!is_array($out)) {
902  $out = trim($out);
903  if (preg_match('/[^a-z0-9_\-\.]+/i', $out)) {
904  $out = '';
905  }
906  }
907  break;
908  case 'aZ09comma': // great to sanitize sortfield or sortorder params that can be t.abc,t.def_gh
909  if (!is_array($out)) {
910  $out = trim($out);
911  if (preg_match('/[^a-z0-9_\-\.,]+/i', $out)) {
912  $out = '';
913  }
914  }
915  break;
916  case 'nohtml': // No html
917  $out = dol_string_nohtmltag($out, 0);
918  break;
919  case 'alpha': // No html and no ../ and "
920  case 'alphanohtml': // Recommended for most scalar parameters and search parameters
921  if (!is_array($out)) {
922  $out = trim($out);
923  do {
924  $oldstringtoclean = $out;
925  // Remove html tags
926  $out = dol_string_nohtmltag($out, 0);
927  // Remove also other dangerous string sequences
928  // '"' is dangerous because param in url can close the href= or src= and add javascript functions.
929  // '../' or '..\' is dangerous because it allows dir transversals
930  // Note &#38, '&#0000038', '&#x26'... is a simple char like '&' alone but there is no reason to accept such way to encode input data.
931  $out = str_ireplace(array('&#38', '&#0000038', '&#x26', '&quot', '&#34', '&#0000034', '&#x22', '"', '&#47', '&#0000047', '&#92', '&#0000092', '&#x2F', '../', '..\\'), '', $out);
932  } while ($oldstringtoclean != $out);
933  // keep lines feed
934  }
935  break;
936  case 'alphawithlgt': // No " and no ../ but we keep balanced < > tags with no special chars inside. Can be used for email string like "Name <email>". Less secured than 'alphanohtml'
937  if (!is_array($out)) {
938  $out = trim($out);
939  do {
940  $oldstringtoclean = $out;
941  // Remove html tags
942  $out = dol_html_entity_decode($out, ENT_COMPAT | ENT_HTML5, 'UTF-8');
943  // '"' is dangerous because param in url can close the href= or src= and add javascript functions.
944  // '../' or '..\' is dangerous because it allows dir transversals
945  // Note &#38, '&#0000038', '&#x26'... is a simple char like '&' alone but there is no reason to accept such way to encode input data.
946  $out = str_ireplace(array('&#38', '&#0000038', '&#x26', '&quot', '&#34', '&#0000034', '&#x22', '"', '&#47', '&#0000047', '&#92', '&#0000092', '&#x2F', '../', '..\\'), '', $out);
947  } while ($oldstringtoclean != $out);
948  }
949  break;
950  case 'restricthtml': // Recommended for most html textarea
951  case 'restricthtmlallowunvalid':
952  $out = dol_htmlwithnojs($out, 1, $check);
953  break;
954  case 'custom':
955  if (!empty($out)) {
956  if (empty($filter)) {
957  return 'BadParameterForGETPOST - Param 3 of sanitizeVal()';
958  }
959  /*if (empty($options)) {
960  return 'BadParameterForGETPOST - Param 4 of sanitizeVal()';
961  }*/
962  $out = filter_var($out, $filter, $options);
963  }
964  break;
965  }
966 
967  return $out;
968 }
969 
970 
971 if (!function_exists('dol_getprefix')) {
981  function dol_getprefix($mode = '')
982  {
983  // If prefix is for email (we need to have $conf already loaded for this case)
984  if ($mode == 'email') {
985  global $conf;
986 
987  if (!empty($conf->global->MAIL_PREFIX_FOR_EMAIL_ID)) { // If MAIL_PREFIX_FOR_EMAIL_ID is set
988  if ($conf->global->MAIL_PREFIX_FOR_EMAIL_ID != 'SERVER_NAME') {
989  return $conf->global->MAIL_PREFIX_FOR_EMAIL_ID;
990  } elseif (isset($_SERVER["SERVER_NAME"])) { // If MAIL_PREFIX_FOR_EMAIL_ID is set to 'SERVER_NAME'
991  return $_SERVER["SERVER_NAME"];
992  }
993  }
994 
995  // The recommended value if MAIL_PREFIX_FOR_EMAIL_ID is not defined (may be not defined for old versions)
996  if (!empty($conf->file->instance_unique_id)) {
997  return sha1('dolibarr'.$conf->file->instance_unique_id);
998  }
999 
1000  // For backward compatibility when instance_unique_id is not set
1001  return sha1(DOL_DOCUMENT_ROOT.DOL_URL_ROOT);
1002  }
1003 
1004  // If prefix is for session (no need to have $conf loaded)
1005  global $dolibarr_main_instance_unique_id, $dolibarr_main_cookie_cryptkey; // This is loaded by filefunc.inc.php
1006  $tmp_instance_unique_id = empty($dolibarr_main_instance_unique_id) ? (empty($dolibarr_main_cookie_cryptkey) ? '' : $dolibarr_main_cookie_cryptkey) : $dolibarr_main_instance_unique_id; // Unique id of instance
1007 
1008  // The recommended value (may be not defined for old versions)
1009  if (!empty($tmp_instance_unique_id)) {
1010  return sha1('dolibarr'.$tmp_instance_unique_id);
1011  }
1012 
1013  // For backward compatibility when instance_unique_id is not set
1014  if (isset($_SERVER["SERVER_NAME"]) && isset($_SERVER["DOCUMENT_ROOT"])) {
1015  return sha1($_SERVER["SERVER_NAME"].$_SERVER["DOCUMENT_ROOT"].DOL_DOCUMENT_ROOT.DOL_URL_ROOT);
1016  } else {
1017  return sha1(DOL_DOCUMENT_ROOT.DOL_URL_ROOT);
1018  }
1019  }
1020 }
1021 
1032 function dol_include_once($relpath, $classname = '')
1033 {
1034  global $conf, $langs, $user, $mysoc; // Do not remove this. They must be defined for files we include. Other globals var must be retrieved with $GLOBALS['var']
1035 
1036  $fullpath = dol_buildpath($relpath);
1037 
1038  if (!file_exists($fullpath)) {
1039  dol_syslog('functions::dol_include_once Tried to load unexisting file: '.$relpath, LOG_WARNING);
1040  return false;
1041  }
1042 
1043  if (!empty($classname) && !class_exists($classname)) {
1044  return include $fullpath;
1045  } else {
1046  return include_once $fullpath;
1047  }
1048 }
1049 
1050 
1061 function dol_buildpath($path, $type = 0, $returnemptyifnotfound = 0)
1062 {
1063  global $conf;
1064 
1065  $path = preg_replace('/^\//', '', $path);
1066 
1067  if (empty($type)) { // For a filesystem path
1068  $res = DOL_DOCUMENT_ROOT.'/'.$path; // Standard default path
1069  if (is_array($conf->file->dol_document_root)) {
1070  foreach ($conf->file->dol_document_root as $key => $dirroot) { // ex: array("main"=>"/home/main/htdocs", "alt0"=>"/home/dirmod/htdocs", ...)
1071  if ($key == 'main') {
1072  continue;
1073  }
1074  if (file_exists($dirroot.'/'.$path)) {
1075  $res = $dirroot.'/'.$path;
1076  return $res;
1077  }
1078  }
1079  }
1080  if ($returnemptyifnotfound) {
1081  // Not found into alternate dir
1082  if ($returnemptyifnotfound == 1 || !file_exists($res)) {
1083  return '';
1084  }
1085  }
1086  } else {
1087  // For an url path
1088  // We try to get local path of file on filesystem from url
1089  // Note that trying to know if a file on disk exist by forging path on disk from url
1090  // works only for some web server and some setup. This is bugged when
1091  // using proxy, rewriting, virtual path, etc...
1092  $res = '';
1093  if ($type == 1) {
1094  $res = DOL_URL_ROOT.'/'.$path; // Standard value
1095  }
1096  if ($type == 2) {
1097  $res = DOL_MAIN_URL_ROOT.'/'.$path; // Standard value
1098  }
1099  if ($type == 3) {
1100  $res = DOL_URL_ROOT.'/'.$path;
1101  }
1102 
1103  foreach ($conf->file->dol_document_root as $key => $dirroot) { // ex: array(["main"]=>"/home/main/htdocs", ["alt0"]=>"/home/dirmod/htdocs", ...)
1104  if ($key == 'main') {
1105  if ($type == 3) {
1106  global $dolibarr_main_url_root;
1107 
1108  // Define $urlwithroot
1109  $urlwithouturlroot = preg_replace('/'.preg_quote(DOL_URL_ROOT, '/').'$/i', '', trim($dolibarr_main_url_root));
1110  $urlwithroot = $urlwithouturlroot.DOL_URL_ROOT; // This is to use external domain name found into config file
1111  //$urlwithroot=DOL_MAIN_URL_ROOT; // This is to use same domain name than current
1112 
1113  $res = (preg_match('/^http/i', $conf->file->dol_url_root[$key]) ? '' : $urlwithroot).'/'.$path; // Test on start with http is for old conf syntax
1114  }
1115  continue;
1116  }
1117  preg_match('/^([^\?]+(\.css\.php|\.css|\.js\.php|\.js|\.png|\.jpg|\.php)?)/i', $path, $regs); // Take part before '?'
1118  if (!empty($regs[1])) {
1119  //print $key.'-'.$dirroot.'/'.$path.'-'.$conf->file->dol_url_root[$type].'<br>'."\n";
1120  if (file_exists($dirroot.'/'.$regs[1])) {
1121  if ($type == 1) {
1122  $res = (preg_match('/^http/i', $conf->file->dol_url_root[$key]) ? '' : DOL_URL_ROOT).$conf->file->dol_url_root[$key].'/'.$path;
1123  }
1124  if ($type == 2) {
1125  $res = (preg_match('/^http/i', $conf->file->dol_url_root[$key]) ? '' : DOL_MAIN_URL_ROOT).$conf->file->dol_url_root[$key].'/'.$path;
1126  }
1127  if ($type == 3) {
1128  global $dolibarr_main_url_root;
1129 
1130  // Define $urlwithroot
1131  $urlwithouturlroot = preg_replace('/'.preg_quote(DOL_URL_ROOT, '/').'$/i', '', trim($dolibarr_main_url_root));
1132  $urlwithroot = $urlwithouturlroot.DOL_URL_ROOT; // This is to use external domain name found into config file
1133  //$urlwithroot=DOL_MAIN_URL_ROOT; // This is to use same domain name than current
1134 
1135  $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
1136  }
1137  break;
1138  }
1139  }
1140  }
1141  }
1142 
1143  return $res;
1144 }
1145 
1157 function dol_clone($object, $native = 0)
1158 {
1159  if ($native == 0) {
1160  // deprecated method, use the method with native = 2 instead
1161  $tmpsavdb = null;
1162  if (isset($object->db) && isset($object->db->db) && is_object($object->db->db) && get_class($object->db->db) == 'PgSql\Connection') {
1163  $tmpsavdb = $object->db;
1164  unset($object->db); // Such property can not be serialized with pgsl (when object->db->db = 'PgSql\Connection')
1165  }
1166 
1167  $myclone = unserialize(serialize($object)); // serialize then unserialize is a hack to be sure to have a new object for all fields
1168 
1169  if (!empty($tmpsavdb)) {
1170  $object->db = $tmpsavdb;
1171  }
1172  } elseif ($native == 2) {
1173  // recommended method to have a full isolated cloned object
1174  $myclone = new stdClass();
1175  $tmparray = get_object_vars($object); // return only public properties
1176 
1177  if (is_array($tmparray)) {
1178  foreach ($tmparray as $propertykey => $propertyval) {
1179  if (is_scalar($propertyval) || is_array($propertyval)) {
1180  $myclone->$propertykey = $propertyval;
1181  }
1182  }
1183  }
1184  } else {
1185  $myclone = clone $object; // PHP clone is a shallow copy only, not a real clone, so properties of references will keep the reference (refering to the same target/variable)
1186  }
1187 
1188  return $myclone;
1189 }
1190 
1200 function dol_size($size, $type = '')
1201 {
1202  global $conf;
1203  if (empty($conf->dol_optimize_smallscreen)) {
1204  return $size;
1205  }
1206  if ($type == 'width' && $size > 250) {
1207  return 250;
1208  } else {
1209  return 10;
1210  }
1211 }
1212 
1213 
1225 function dol_sanitizeFileName($str, $newstr = '_', $unaccent = 1)
1226 {
1227  // List of special chars for filenames in windows are defined on page https://docs.microsoft.com/en-us/windows/win32/fileio/naming-a-file
1228  // Char '>' '<' '|' '$' and ';' are special chars for shells.
1229  // Char '/' and '\' are file delimiters.
1230  // Chars '--' can be used into filename to inject special paramaters like --use-compress-program to make command with file as parameter making remote execution of command
1231  $filesystem_forbidden_chars = array('<', '>', '/', '\\', '?', '*', '|', '"', ':', '°', '$', ';', '`');
1232  $tmp = dol_string_nospecial($unaccent ? dol_string_unaccent($str) : $str, $newstr, $filesystem_forbidden_chars);
1233  $tmp = preg_replace('/\-\-+/', '_', $tmp);
1234  $tmp = preg_replace('/\s+\-([^\s])/', ' _$1', $tmp);
1235  $tmp = preg_replace('/\s+\-$/', '', $tmp);
1236  $tmp = str_replace('..', '', $tmp);
1237  return $tmp;
1238 }
1239 
1251 function dol_sanitizePathName($str, $newstr = '_', $unaccent = 1)
1252 {
1253  // List of special chars for filenames in windows are defined on page https://docs.microsoft.com/en-us/windows/win32/fileio/naming-a-file
1254  // Char '>' '<' '|' '$' and ';' are special chars for shells.
1255  // Chars '--' can be used into filename to inject special paramaters like --use-compress-program to make command with file as parameter making remote execution of command
1256  $filesystem_forbidden_chars = array('<', '>', '?', '*', '|', '"', '°', '$', ';', '`');
1257  $tmp = dol_string_nospecial($unaccent ? dol_string_unaccent($str) : $str, $newstr, $filesystem_forbidden_chars);
1258  $tmp = preg_replace('/\-\-+/', '_', $tmp);
1259  $tmp = preg_replace('/\s+\-([^\s])/', ' _$1', $tmp);
1260  $tmp = preg_replace('/\s+\-$/', '', $tmp);
1261  $tmp = str_replace('..', '', $tmp);
1262  return $tmp;
1263 }
1264 
1272 function dol_sanitizeUrl($stringtoclean, $type = 1)
1273 {
1274  // We clean string because some hacks try to obfuscate evil strings by inserting non printable chars. Example: 'java(ascci09)scr(ascii00)ipt' is processed like 'javascript' (whatever is place of evil ascii char)
1275  // We should use dol_string_nounprintableascii but function may not be yet loaded/available
1276  $stringtoclean = preg_replace('/[\x00-\x1F\x7F]/u', '', $stringtoclean); // /u operator makes UTF8 valid characters being ignored so are not included into the replace
1277  // We clean html comments because some hacks try to obfuscate evil strings by inserting HTML comments. Example: on<!-- -->error=alert(1)
1278  $stringtoclean = preg_replace('/<!--[^>]*-->/', '', $stringtoclean);
1279 
1280  $stringtoclean = str_replace('\\', '/', $stringtoclean);
1281  if ($type == 1) {
1282  // removing : should disable links to external url like http:aaa)
1283  // removing ';' should disable "named" html entities encode into an url (we should not have this into an url)
1284  $stringtoclean = str_replace(array(':', ';', '@'), '', $stringtoclean);
1285  }
1286 
1287  do {
1288  $oldstringtoclean = $stringtoclean;
1289  // removing '&colon' should disable links to external url like http:aaa)
1290  // removing '&#' should disable "numeric" html entities encode into an url (we should not have this into an url)
1291  $stringtoclean = str_ireplace(array('javascript', 'vbscript', '&colon', '&#'), '', $stringtoclean);
1292  } while ($oldstringtoclean != $stringtoclean);
1293 
1294  if ($type == 1) {
1295  // removing '//' should disable links to external url like //aaa or http//)
1296  $stringtoclean = preg_replace(array('/^[a-z]*\/\/+/i'), '', $stringtoclean);
1297  }
1298 
1299  return $stringtoclean;
1300 }
1301 
1308 function dol_sanitizeEmail($stringtoclean)
1309 {
1310  do {
1311  $oldstringtoclean = $stringtoclean;
1312  $stringtoclean = str_ireplace(array('"', ':', '[', ']',"\n", "\r", '\\', '\/'), '', $stringtoclean);
1313  } while ($oldstringtoclean != $stringtoclean);
1314 
1315  return $stringtoclean;
1316 }
1317 
1326 function dol_string_unaccent($str)
1327 {
1328  global $conf;
1329 
1330  if (is_null($str)) {
1331  return '';
1332  }
1333 
1334  if (utf8_check($str)) {
1335  if (extension_loaded('intl') && !empty($conf->global->MAIN_UNACCENT_USE_TRANSLITERATOR)) {
1336  $transliterator = \Transliterator::createFromRules(':: Any-Latin; :: Latin-ASCII; :: NFD; :: [:Nonspacing Mark:] Remove; :: NFC;', \Transliterator::FORWARD);
1337  return $transliterator->transliterate($str);
1338  }
1339  // See http://www.utf8-chartable.de/
1340  $string = rawurlencode($str);
1341  $replacements = array(
1342  '%C3%80' => 'A', '%C3%81' => 'A', '%C3%82' => 'A', '%C3%83' => 'A', '%C3%84' => 'A', '%C3%85' => 'A',
1343  '%C3%87' => 'C',
1344  '%C3%88' => 'E', '%C3%89' => 'E', '%C3%8A' => 'E', '%C3%8B' => 'E',
1345  '%C3%8C' => 'I', '%C3%8D' => 'I', '%C3%8E' => 'I', '%C3%8F' => 'I',
1346  '%C3%91' => 'N',
1347  '%C3%92' => 'O', '%C3%93' => 'O', '%C3%94' => 'O', '%C3%95' => 'O', '%C3%96' => 'O',
1348  '%C5%A0' => 'S',
1349  '%C3%99' => 'U', '%C3%9A' => 'U', '%C3%9B' => 'U', '%C3%9C' => 'U',
1350  '%C3%9D' => 'Y', '%C5%B8' => 'y',
1351  '%C3%A0' => 'a', '%C3%A1' => 'a', '%C3%A2' => 'a', '%C3%A3' => 'a', '%C3%A4' => 'a', '%C3%A5' => 'a',
1352  '%C3%A7' => 'c',
1353  '%C3%A8' => 'e', '%C3%A9' => 'e', '%C3%AA' => 'e', '%C3%AB' => 'e',
1354  '%C3%AC' => 'i', '%C3%AD' => 'i', '%C3%AE' => 'i', '%C3%AF' => 'i',
1355  '%C3%B1' => 'n',
1356  '%C3%B2' => 'o', '%C3%B3' => 'o', '%C3%B4' => 'o', '%C3%B5' => 'o', '%C3%B6' => 'o',
1357  '%C5%A1' => 's',
1358  '%C3%B9' => 'u', '%C3%BA' => 'u', '%C3%BB' => 'u', '%C3%BC' => 'u',
1359  '%C3%BD' => 'y', '%C3%BF' => 'y'
1360  );
1361  $string = strtr($string, $replacements);
1362  return rawurldecode($string);
1363  } else {
1364  // See http://www.ascii-code.com/
1365  $string = strtr(
1366  $str,
1367  "\xC0\xC1\xC2\xC3\xC4\xC5\xC7
1368  \xC8\xC9\xCA\xCB\xCC\xCD\xCE\xCF\xD0\xD1
1369  \xD2\xD3\xD4\xD5\xD8\xD9\xDA\xDB\xDD
1370  \xE0\xE1\xE2\xE3\xE4\xE5\xE7\xE8\xE9\xEA\xEB
1371  \xEC\xED\xEE\xEF\xF0\xF1\xF2\xF3\xF4\xF5\xF8
1372  \xF9\xFA\xFB\xFC\xFD\xFF",
1373  "AAAAAAC
1374  EEEEIIIIDN
1375  OOOOOUUUY
1376  aaaaaaceeee
1377  iiiidnooooo
1378  uuuuyy"
1379  );
1380  $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"));
1381  return $string;
1382  }
1383 }
1384 
1397 function dol_string_nospecial($str, $newstr = '_', $badcharstoreplace = '', $badcharstoremove = '')
1398 {
1399  $forbidden_chars_to_replace = array(" ", "'", "/", "\\", ":", "*", "?", "\"", "<", ">", "|", "[", "]", ",", ";", "=", '°'); // more complete than dol_sanitizeFileName
1400  $forbidden_chars_to_remove = array();
1401  //$forbidden_chars_to_remove=array("(",")");
1402 
1403  if (is_array($badcharstoreplace)) {
1404  $forbidden_chars_to_replace = $badcharstoreplace;
1405  }
1406  if (is_array($badcharstoremove)) {
1407  $forbidden_chars_to_remove = $badcharstoremove;
1408  }
1409 
1410  return str_replace($forbidden_chars_to_replace, $newstr, str_replace($forbidden_chars_to_remove, "", $str));
1411 }
1412 
1413 
1427 function dol_string_nounprintableascii($str, $removetabcrlf = 1)
1428 {
1429  if ($removetabcrlf) {
1430  return preg_replace('/[\x00-\x1F\x7F]/u', '', $str); // /u operator makes UTF8 valid characters being ignored so are not included into the replace
1431  } else {
1432  return preg_replace('/[\x00-\x08\x11-\x12\x14-\x1F\x7F]/u', '', $str); // /u operator should make UTF8 valid characters being ignored so are not included into the replace
1433  }
1434 }
1435 
1444 function dol_escape_js($stringtoescape, $mode = 0, $noescapebackslashn = 0)
1445 {
1446  if (is_null($stringtoescape)) {
1447  return '';
1448  }
1449 
1450  // escape quotes and backslashes, newlines, etc.
1451  $substitjs = array("&#039;"=>"\\'", "\r"=>'\\r');
1452  //$substitjs['</']='<\/'; // We removed this. Should be useless.
1453  if (empty($noescapebackslashn)) {
1454  $substitjs["\n"] = '\\n';
1455  $substitjs['\\'] = '\\\\';
1456  }
1457  if (empty($mode)) {
1458  $substitjs["'"] = "\\'";
1459  $substitjs['"'] = "\\'";
1460  } elseif ($mode == 1) {
1461  $substitjs["'"] = "\\'";
1462  } elseif ($mode == 2) {
1463  $substitjs['"'] = '\\"';
1464  } elseif ($mode == 3) {
1465  $substitjs["'"] = "\\'";
1466  $substitjs['"'] = "\\\"";
1467  }
1468  return strtr($stringtoescape, $substitjs);
1469 }
1470 
1477 function dol_escape_json($stringtoescape)
1478 {
1479  return str_replace('"', '\"', $stringtoescape);
1480 }
1481 
1493 function dol_escape_htmltag($stringtoescape, $keepb = 0, $keepn = 0, $noescapetags = '', $escapeonlyhtmltags = 0)
1494 {
1495  if ($noescapetags == 'common') {
1496  $noescapetags = 'html,body,a,b,em,hr,i,u,ul,li,br,div,img,font,p,span,strong,table,tr,td,th,tbody';
1497  }
1498 
1499  // escape quotes and backslashes, newlines, etc.
1500  if ($escapeonlyhtmltags) {
1501  $tmp = htmlspecialchars_decode((string) $stringtoescape, ENT_COMPAT);
1502  } else {
1503  $tmp = html_entity_decode((string) $stringtoescape, ENT_COMPAT, 'UTF-8');
1504  }
1505  if (!$keepb) {
1506  $tmp = strtr($tmp, array("<b>"=>'', '</b>'=>''));
1507  }
1508  if (!$keepn) {
1509  $tmp = strtr($tmp, array("\r"=>'\\r', "\n"=>'\\n'));
1510  }
1511 
1512  if ($escapeonlyhtmltags) {
1513  return htmlspecialchars($tmp, ENT_COMPAT, 'UTF-8');
1514  } else {
1515  // Escape tags to keep
1516  // TODO Does not works yet when there is attributes into tag
1517  $tmparrayoftags = array();
1518  if ($noescapetags) {
1519  $tmparrayoftags = explode(',', $noescapetags);
1520  }
1521  if (count($tmparrayoftags)) {
1522  foreach ($tmparrayoftags as $tagtoreplace) {
1523  $tmp = str_ireplace('<'.$tagtoreplace.'>', '__BEGINTAGTOREPLACE'.$tagtoreplace.'__', $tmp);
1524  $tmp = str_ireplace('</'.$tagtoreplace.'>', '__ENDTAGTOREPLACE'.$tagtoreplace.'__', $tmp);
1525  $tmp = str_ireplace('<'.$tagtoreplace.' />', '__BEGINENDTAGTOREPLACE'.$tagtoreplace.'__', $tmp);
1526  }
1527  }
1528 
1529  $result = htmlentities($tmp, ENT_COMPAT, 'UTF-8');
1530 
1531  if (count($tmparrayoftags)) {
1532  foreach ($tmparrayoftags as $tagtoreplace) {
1533  $result = str_ireplace('__BEGINTAGTOREPLACE'.$tagtoreplace.'__', '<'.$tagtoreplace.'>', $result);
1534  $result = str_ireplace('__ENDTAGTOREPLACE'.$tagtoreplace.'__', '</'.$tagtoreplace.'>', $result);
1535  $result = str_ireplace('__BEGINENDTAGTOREPLACE'.$tagtoreplace.'__', '<'.$tagtoreplace.' />', $result);
1536  }
1537  }
1538 
1539  return $result;
1540  }
1541 }
1542 
1550 function dol_strtolower($string, $encoding = "UTF-8")
1551 {
1552  if (function_exists('mb_strtolower')) {
1553  return mb_strtolower($string, $encoding);
1554  } else {
1555  return strtolower($string);
1556  }
1557 }
1558 
1566 function dol_strtoupper($string, $encoding = "UTF-8")
1567 {
1568  if (function_exists('mb_strtoupper')) {
1569  return mb_strtoupper($string, $encoding);
1570  } else {
1571  return strtoupper($string);
1572  }
1573 }
1574 
1582 function dol_ucfirst($string, $encoding = "UTF-8")
1583 {
1584  if (function_exists('mb_substr')) {
1585  return mb_strtoupper(mb_substr($string, 0, 1, $encoding), $encoding).mb_substr($string, 1, null, $encoding);
1586  } else {
1587  return ucfirst($string);
1588  }
1589 }
1590 
1598 function dol_ucwords($string, $encoding = "UTF-8")
1599 {
1600  if (function_exists('mb_convert_case')) {
1601  return mb_convert_case($string, MB_CASE_TITLE, $encoding);
1602  } else {
1603  return ucwords($string);
1604  }
1605 }
1606 
1628 function dol_syslog($message, $level = LOG_INFO, $ident = 0, $suffixinfilename = '', $restricttologhandler = '', $logcontext = null)
1629 {
1630  global $conf, $user, $debugbar;
1631 
1632  // If syslog module enabled
1633  if (empty($conf->syslog->enabled)) {
1634  return;
1635  }
1636 
1637  // Check if we are into execution of code of a website
1638  if (defined('USEEXTERNALSERVER') && !defined('USEDOLIBARRSERVER') && !defined('USEDOLIBARREDITOR')) {
1639  global $website, $websitekey;
1640  if (is_object($website) && !empty($website->ref)) {
1641  $suffixinfilename .= '_website_'.$website->ref;
1642  } elseif (!empty($websitekey)) {
1643  $suffixinfilename .= '_website_'.$websitekey;
1644  }
1645  }
1646 
1647  // Check if we have a forced suffix
1648  if (defined('USESUFFIXINLOG')) {
1649  $suffixinfilename .= constant('USESUFFIXINLOG');
1650  }
1651 
1652  if ($ident < 0) {
1653  foreach ($conf->loghandlers as $loghandlerinstance) {
1654  $loghandlerinstance->setIdent($ident);
1655  }
1656  }
1657 
1658  if (!empty($message)) {
1659  // Test log level
1660  $logLevels = array(LOG_EMERG=>'EMERG', LOG_ALERT=>'ALERT', LOG_CRIT=>'CRITICAL', LOG_ERR=>'ERR', LOG_WARNING=>'WARN', LOG_NOTICE=>'NOTICE', LOG_INFO=>'INFO', LOG_DEBUG=>'DEBUG');
1661  if (!array_key_exists($level, $logLevels)) {
1662  throw new Exception('Incorrect log level');
1663  }
1664  if ($level > $conf->global->SYSLOG_LEVEL) {
1665  return;
1666  }
1667 
1668  if (empty($conf->global->MAIN_SHOW_PASSWORD_INTO_LOG)) {
1669  $message = preg_replace('/password=\'[^\']*\'/', 'password=\'hidden\'', $message); // protection to avoid to have value of password in log
1670  }
1671 
1672  // If adding log inside HTML page is required
1673  if ((!empty($_REQUEST['logtohtml']) && !empty($conf->global->MAIN_ENABLE_LOG_TO_HTML))
1674  || (!empty($user->rights->debugbar->read) && is_object($debugbar))) {
1675  $conf->logbuffer[] = dol_print_date(time(), "%Y-%m-%d %H:%M:%S")." ".$logLevels[$level]." ".$message;
1676  }
1677 
1678  //TODO: Remove this. MAIN_ENABLE_LOG_INLINE_HTML should be deprecated and use a log handler dedicated to HTML output
1679  // If html log tag enabled and url parameter log defined, we show output log on HTML comments
1680  if (!empty($conf->global->MAIN_ENABLE_LOG_INLINE_HTML) && !empty($_GET["log"])) {
1681  print "\n\n<!-- Log start\n";
1682  print dol_escape_htmltag($message)."\n";
1683  print "Log end -->\n";
1684  }
1685 
1686  $data = array(
1687  'message' => $message,
1688  'script' => (isset($_SERVER['PHP_SELF']) ? basename($_SERVER['PHP_SELF'], '.php') : false),
1689  'level' => $level,
1690  'user' => ((is_object($user) && $user->id) ? $user->login : false),
1691  'ip' => false
1692  );
1693 
1694  $remoteip = getUserRemoteIP(); // Get ip when page run on a web server
1695  if (!empty($remoteip)) {
1696  $data['ip'] = $remoteip;
1697  // This is when server run behind a reverse proxy
1698  if (!empty($_SERVER['HTTP_X_FORWARDED_FOR']) && $_SERVER['HTTP_X_FORWARDED_FOR'] != $remoteip) {
1699  $data['ip'] = $_SERVER['HTTP_X_FORWARDED_FOR'].' -> '.$data['ip'];
1700  } elseif (!empty($_SERVER['HTTP_CLIENT_IP']) && $_SERVER['HTTP_CLIENT_IP'] != $remoteip) {
1701  $data['ip'] = $_SERVER['HTTP_CLIENT_IP'].' -> '.$data['ip'];
1702  }
1703  } elseif (!empty($_SERVER['SERVER_ADDR'])) {
1704  // This is when PHP session is ran inside a web server but not inside a client request (example: init code of apache)
1705  $data['ip'] = $_SERVER['SERVER_ADDR'];
1706  } elseif (!empty($_SERVER['COMPUTERNAME'])) {
1707  // 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).
1708  $data['ip'] = $_SERVER['COMPUTERNAME'].(empty($_SERVER['USERNAME']) ? '' : '@'.$_SERVER['USERNAME']);
1709  } elseif (!empty($_SERVER['LOGNAME'])) {
1710  // 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).
1711  $data['ip'] = '???@'.$_SERVER['LOGNAME'];
1712  }
1713 
1714  // Loop on each log handler and send output
1715  foreach ($conf->loghandlers as $loghandlerinstance) {
1716  if ($restricttologhandler && $loghandlerinstance->code != $restricttologhandler) {
1717  continue;
1718  }
1719  $loghandlerinstance->export($data, $suffixinfilename);
1720  }
1721  unset($data);
1722  }
1723 
1724  if ($ident > 0) {
1725  foreach ($conf->loghandlers as $loghandlerinstance) {
1726  $loghandlerinstance->setIdent($ident);
1727  }
1728  }
1729 }
1730 
1745 function dolButtonToOpenUrlInDialogPopup($name, $label, $buttonstring, $url, $disabled = '', $morecss = 'button bordertransp', $backtopagejsfields = '')
1746 {
1747  global $conf;
1748 
1749  if (strpos($url, '?') > 0) {
1750  $url .= '&dol_hide_topmenu=1&dol_hide_leftmenu=1&dol_openinpopup='.urlencode($name);
1751  } else {
1752  $url .= '?dol_hide_topmenu=1&dol_hide_leftmenu=1&dol_openinpopup='.urlencode($name);
1753  }
1754 
1755  $out = '';
1756 
1757  $backtopagejsfieldsid = ''; $backtopagejsfieldslabel = '';
1758  if ($backtopagejsfields) {
1759  $tmpbacktopagejsfields = explode(':', $backtopagejsfields);
1760  if (empty($tmpbacktopagejsfields[1])) { // If the part 'keyforpopupid:' is missing, we add $name for it.
1761  $backtopagejsfields = $name.":".$backtopagejsfields;
1762  $tmp2backtopagejsfields = explode(',', $tmpbacktopagejsfields[0]);
1763  } else {
1764  $tmp2backtopagejsfields = explode(',', $tmpbacktopagejsfields[1]);
1765  }
1766  $backtopagejsfieldsid = empty($tmp2backtopagejsfields[0]) ? '' : $tmp2backtopagejsfields[0];
1767  $backtopagejsfieldslabel = empty($tmp2backtopagejsfields[1]) ? '' : $tmp2backtopagejsfields[1];
1768  $url .= '&backtopagejsfields='.urlencode($backtopagejsfields);
1769  }
1770 
1771  //print '<input type="submit" class="button bordertransp"'.$disabled.' value="'.dol_escape_htmltag($langs->trans("MediaFiles")).'" name="file_manager">';
1772  $out .= '<!-- a link for button to open url into a dialog popup with backtopagejsfields = '.$backtopagejsfields.' -->';
1773  $out .= '<a class="cursorpointer classlink button_'.$name.($morecss ? ' '.$morecss : '').'"'.$disabled.' title="'.dol_escape_htmltag($label).'"';
1774  if (empty($conf->use_javascript_ajax)) {
1775  $out .= ' href="'.DOL_URL_ROOT.$url.'" target="_blank"';
1776  }
1777  $out .= '>'.$buttonstring.'</a>';
1778 
1779  if (!empty($conf->use_javascript_ajax)) {
1780  // Add code to open url using the popup. Add also hidden field to retreive the returned variables
1781  $out .= '<!-- code to open popup and variables to retreive returned variables -->';
1782  $out .= '<div id="idfordialog'.$name.'" class="hidden">div for dialog</div>';
1783  $out .= '<div id="varforreturndialogid'.$name.'" class="hidden">div for returned id</div>';
1784  $out .= '<div id="varforreturndialoglabel'.$name.'" class="hidden">div for returned label</div>';
1785  $out .= '<!-- Add js code to open dialog popup on dialog -->';
1786  $out .= '<script type="text/javascript">
1787  jQuery(document).ready(function () {
1788  jQuery(".button_'.$name.'").click(function () {
1789  console.log(\'Open popup with jQuery(...).dialog() on URL '.dol_escape_js(DOL_URL_ROOT.$url).'\');
1790  var $tmpdialog = $(\'#idfordialog'.$name.'\');
1791  $tmpdialog.html(\'<iframe class="iframedialog" id="iframedialog'.$name.'" style="border: 0px;" src="'.DOL_URL_ROOT.$url.'" width="100%" height="98%"></iframe>\');
1792  $tmpdialog.dialog({
1793  autoOpen: false,
1794  modal: true,
1795  height: (window.innerHeight - 150),
1796  width: \'80%\',
1797  title: \''.dol_escape_js($label).'\',
1798  open: function (event, ui) {
1799  console.log("open popup name='.$name.', backtopagejsfields='.$backtopagejsfields.'");
1800  },
1801  close: function (event, ui) {
1802  returnedid = jQuery("#varforreturndialogid'.$name.'").text();
1803  returnedlabel = jQuery("#varforreturndialoglabel'.$name.'").text();
1804  console.log("popup has been closed. returnedid (js var defined into parent page)="+returnedid+" returnedlabel="+returnedlabel);
1805  if (returnedid != "" && returnedid != "div for returned id") {
1806  jQuery("#'.(empty($backtopagejsfieldsid)?"none":$backtopagejsfieldsid).'").val(returnedid);
1807  }
1808  if (returnedlabel != "" && returnedlabel != "div for returned label") {
1809  jQuery("#'.(empty($backtopagejsfieldslabel)?"none":$backtopagejsfieldslabel).'").val(returnedlabel);
1810  }
1811  }
1812  });
1813 
1814  $tmpdialog.dialog(\'open\');
1815  });
1816  });
1817  </script>';
1818  }
1819  return $out;
1820 }
1821 
1838 function dol_fiche_head($links = array(), $active = '0', $title = '', $notab = 0, $picto = '', $pictoisfullpath = 0, $morehtmlright = '', $morecss = '', $limittoshow = 0, $moretabssuffix = '')
1839 {
1840  print dol_get_fiche_head($links, $active, $title, $notab, $picto, $pictoisfullpath, $morehtmlright, $morecss, $limittoshow, $moretabssuffix);
1841 }
1842 
1858 function dol_get_fiche_head($links = array(), $active = '', $title = '', $notab = 0, $picto = '', $pictoisfullpath = 0, $morehtmlright = '', $morecss = '', $limittoshow = 0, $moretabssuffix = '')
1859 {
1860  global $conf, $langs, $hookmanager;
1861 
1862  // Show title
1863  $showtitle = 1;
1864  if (!empty($conf->dol_optimize_smallscreen)) {
1865  $showtitle = 0;
1866  }
1867 
1868  $out = "\n".'<!-- dol_fiche_head - dol_get_fiche_head -->';
1869 
1870  if ((!empty($title) && $showtitle) || $morehtmlright || !empty($links)) {
1871  $out .= '<div class="tabs'.($picto ? '' : ' nopaddingleft').'" data-role="controlgroup" data-type="horizontal">'."\n";
1872  }
1873 
1874  // Show right part
1875  if ($morehtmlright) {
1876  $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.
1877  }
1878 
1879  // Show title
1880  if (!empty($title) && $showtitle && empty($conf->global->MAIN_OPTIMIZEFORTEXTBROWSER)) {
1881  $limittitle = 30;
1882  $out .= '<a class="tabTitle">';
1883  if ($picto) {
1884  $noprefix = $pictoisfullpath;
1885  if (strpos($picto, 'fontawesome_') !== false) {
1886  $noprefix = 1;
1887  }
1888  $out .= img_picto($title, ($noprefix ? '' : 'object_').$picto, '', $pictoisfullpath, 0, 0, '', 'imgTabTitle').' ';
1889  }
1890  $out .= '<span class="tabTitleText">'.dol_escape_htmltag(dol_trunc($title, $limittitle)).'</span>';
1891  $out .= '</a>';
1892  }
1893 
1894  // Show tabs
1895 
1896  // Define max of key (max may be higher than sizeof because of hole due to module disabling some tabs).
1897  $maxkey = -1;
1898  if (is_array($links) && !empty($links)) {
1899  $keys = array_keys($links);
1900  if (count($keys)) {
1901  $maxkey = max($keys);
1902  }
1903  }
1904 
1905  // Show tabs
1906  // if =0 we don't use the feature
1907  if (empty($limittoshow)) {
1908  $limittoshow = (empty($conf->global->MAIN_MAXTABS_IN_CARD) ? 99 : $conf->global->MAIN_MAXTABS_IN_CARD);
1909  }
1910  if (!empty($conf->dol_optimize_smallscreen)) {
1911  $limittoshow = 2;
1912  }
1913 
1914  $displaytab = 0;
1915  $nbintab = 0;
1916  $popuptab = 0;
1917  $outmore = '';
1918  for ($i = 0; $i <= $maxkey; $i++) {
1919  if ((is_numeric($active) && $i == $active) || (!empty($links[$i][2]) && !is_numeric($active) && $active == $links[$i][2])) {
1920  // If active tab is already present
1921  if ($i >= $limittoshow) {
1922  $limittoshow--;
1923  }
1924  }
1925  }
1926 
1927  for ($i = 0; $i <= $maxkey; $i++) {
1928  if ((is_numeric($active) && $i == $active) || (!empty($links[$i][2]) && !is_numeric($active) && $active == $links[$i][2])) {
1929  $isactive = true;
1930  } else {
1931  $isactive = false;
1932  }
1933 
1934  if ($i < $limittoshow || $isactive) {
1935  // Output entry with a visible tab
1936  $out .= '<div class="inline-block tabsElem'.($isactive ? ' tabsElemActive' : '').((!$isactive && !empty($conf->global->MAIN_HIDE_INACTIVETAB_ON_PRINT)) ? ' hideonprint' : '').'"><!-- id tab = '.(empty($links[$i][2]) ? '' : dol_escape_htmltag($links[$i][2])).' -->';
1937 
1938  if (isset($links[$i][2]) && $links[$i][2] == 'image') {
1939  if (!empty($links[$i][0])) {
1940  $out .= '<a class="tabimage'.($morecss ? ' '.$morecss : '').'" href="'.$links[$i][0].'">'.$links[$i][1].'</a>'."\n";
1941  } else {
1942  $out .= '<span class="tabspan">'.$links[$i][1].'</span>'."\n";
1943  }
1944  } elseif (!empty($links[$i][1])) {
1945  //print "x $i $active ".$links[$i][2]." z";
1946  $out .= '<div class="tab tab'.($isactive?'active':'unactive').'" style="margin: 0 !important">';
1947  if (!empty($links[$i][0])) {
1948  $titletoshow = preg_replace('/<.*$/', '', $links[$i][1]);
1949  $out .= '<a'.(!empty($links[$i][2]) ? ' id="'.$links[$i][2].'"' : '').' class="tab inline-block valignmiddle'.($morecss ? ' '.$morecss : '').(!empty($links[$i][5]) ? ' '.$links[$i][5] : '').'" href="'.$links[$i][0].'" title="'.dol_escape_htmltag($titletoshow).'">';
1950  }
1951  $out .= $links[$i][1];
1952  if (!empty($links[$i][0])) {
1953  $out .= '</a>'."\n";
1954  }
1955  $out .= empty($links[$i][4]) ? '' : $links[$i][4];
1956  $out .= '</div>';
1957  }
1958 
1959  $out .= '</div>';
1960  } else {
1961  // Add entry into the combo popup with the other tabs
1962  if (!$popuptab) {
1963  $popuptab = 1;
1964  $outmore .= '<div class="popuptabset wordwrap">'; // The css used to hide/show popup
1965  }
1966  $outmore .= '<div class="popuptab wordwrap" style="display:inherit;">';
1967  if (isset($links[$i][2]) && $links[$i][2] == 'image') {
1968  if (!empty($links[$i][0])) {
1969  $outmore .= '<a class="tabimage'.($morecss ? ' '.$morecss : '').'" href="'.$links[$i][0].'">'.$links[$i][1].'</a>'."\n";
1970  } else {
1971  $outmore .= '<span class="tabspan">'.$links[$i][1].'</span>'."\n";
1972  }
1973  } elseif (!empty($links[$i][1])) {
1974  $outmore .= '<a'.(!empty($links[$i][2]) ? ' id="'.$links[$i][2].'"' : '').' class="wordwrap inline-block'.($morecss ? ' '.$morecss : '').'" href="'.$links[$i][0].'">';
1975  $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.
1976  $outmore .= '</a>'."\n";
1977  }
1978  $outmore .= '</div>';
1979 
1980  $nbintab++;
1981  }
1982  $displaytab = $i;
1983  }
1984  if ($popuptab) {
1985  $outmore .= '</div>';
1986  }
1987 
1988  if ($popuptab) { // If there is some tabs not shown
1989  $left = ($langs->trans("DIRECTION") == 'rtl' ? 'right' : 'left');
1990  $right = ($langs->trans("DIRECTION") == 'rtl' ? 'left' : 'right');
1991  $widthofpopup = 200;
1992 
1993  $tabsname = $moretabssuffix;
1994  if (empty($tabsname)) {
1995  $tabsname = str_replace("@", "", $picto);
1996  }
1997  $out .= '<div id="moretabs'.$tabsname.'" class="inline-block tabsElem valignmiddle">';
1998  $out .= '<div class="tab"><a href="#" class="tab moretab inline-block tabunactive"><span class="hideonsmartphone">'.$langs->trans("More").'</span>... ('.$nbintab.')</a></div>'; // Do not use "reposition" class in the "More".
1999  $out .= '<div id="moretabsList'.$tabsname.'" style="width: '.$widthofpopup.'px; position: absolute; '.$left.': -999em; text-align: '.$left.'; margin:0px; padding:2px; z-index:10;">';
2000  $out .= $outmore;
2001  $out .= '</div>';
2002  $out .= '<div></div>';
2003  $out .= "</div>\n";
2004 
2005  $out .= "<script>";
2006  $out .= "$('#moretabs".$tabsname."').mouseenter( function() {
2007  var x = this.offsetLeft, y = this.offsetTop;
2008  console.log('mouseenter ".$left." x='+x+' y='+y+' window.innerWidth='+window.innerWidth);
2009  if ((window.innerWidth - x) < ".($widthofpopup + 10).") {
2010  $('#moretabsList".$tabsname."').css('".$right."','8px');
2011  }
2012  $('#moretabsList".$tabsname."').css('".$left."','auto');
2013  });
2014  ";
2015  $out .= "$('#moretabs".$tabsname."').mouseleave( function() { console.log('mouseleave ".$left."'); $('#moretabsList".$tabsname."').css('".$left."','-999em');});";
2016  $out .= "</script>";
2017  }
2018 
2019  if ((!empty($title) && $showtitle) || $morehtmlright || !empty($links)) {
2020  $out .= "</div>\n";
2021  }
2022 
2023  if (!$notab || $notab == -1 || $notab == -2 || $notab == -3) {
2024  $out .= "\n".'<div class="tabBar'.($notab == -1 ? '' : ($notab == -2 ? ' tabBarNoTop' : (($notab == -3 ? ' noborderbottom' : '').' tabBarWithBottom'))).'">'."\n";
2025  }
2026 
2027  $parameters = array('tabname' => $active, 'out' => $out);
2028  $reshook = $hookmanager->executeHooks('printTabsHead', $parameters); // This hook usage is called just before output the head of tabs. Take also a look at "completeTabsHead"
2029  if ($reshook > 0) {
2030  $out = $hookmanager->resPrint;
2031  }
2032 
2033  return $out;
2034 }
2035 
2043 function dol_fiche_end($notab = 0)
2044 {
2045  print dol_get_fiche_end($notab);
2046 }
2047 
2054 function dol_get_fiche_end($notab = 0)
2055 {
2056  if (!$notab || $notab == -1) {
2057  return "\n</div>\n";
2058  } else {
2059  return '';
2060  }
2061 }
2062 
2082 function dol_banner_tab($object, $paramid, $morehtml = '', $shownav = 1, $fieldid = 'rowid', $fieldref = 'ref', $morehtmlref = '', $moreparam = '', $nodbprefix = 0, $morehtmlleft = '', $morehtmlstatus = '', $onlybanner = 0, $morehtmlright = '')
2083 {
2084  global $conf, $form, $user, $langs, $hookmanager, $action;
2085 
2086  $error = 0;
2087 
2088  $maxvisiblephotos = 1;
2089  $showimage = 1;
2090  $entity = (empty($object->entity) ? $conf->entity : $object->entity);
2091  $showbarcode = empty($conf->barcode->enabled) ? 0 : (empty($object->barcode) ? 0 : 1);
2092  if (!empty($conf->global->MAIN_USE_ADVANCED_PERMS) && empty($user->rights->barcode->lire_advance)) {
2093  $showbarcode = 0;
2094  }
2095  $modulepart = 'unknown';
2096 
2097  if ($object->element == 'societe' || $object->element == 'contact' || $object->element == 'product' || $object->element == 'ticket') {
2098  $modulepart = $object->element;
2099  } elseif ($object->element == 'member') {
2100  $modulepart = 'memberphoto';
2101  } elseif ($object->element == 'user') {
2102  $modulepart = 'userphoto';
2103  }
2104 
2105  if (class_exists("Imagick")) {
2106  if ($object->element == 'expensereport' || $object->element == 'propal' || $object->element == 'commande' || $object->element == 'facture' || $object->element == 'supplier_proposal') {
2107  $modulepart = $object->element;
2108  } elseif ($object->element == 'fichinter') {
2109  $modulepart = 'ficheinter';
2110  } elseif ($object->element == 'contrat') {
2111  $modulepart = 'contract';
2112  } elseif ($object->element == 'order_supplier') {
2113  $modulepart = 'supplier_order';
2114  } elseif ($object->element == 'invoice_supplier') {
2115  $modulepart = 'supplier_invoice';
2116  }
2117  }
2118 
2119  if ($object->element == 'product') {
2120  $width = 80;
2121  $cssclass = 'photowithmargin photoref';
2122  $showimage = $object->is_photo_available($conf->product->multidir_output[$entity]);
2123  $maxvisiblephotos = (isset($conf->global->PRODUCT_MAX_VISIBLE_PHOTO) ? $conf->global->PRODUCT_MAX_VISIBLE_PHOTO : 5);
2124  if ($conf->browser->layout == 'phone') {
2125  $maxvisiblephotos = 1;
2126  }
2127  if ($showimage) {
2128  $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>';
2129  } else {
2130  if (!empty($conf->global->PRODUCT_NODISPLAYIFNOPHOTO)) {
2131  $nophoto = '';
2132  $morehtmlleft .= '<div class="floatleft inline-block valignmiddle divphotoref"></div>';
2133  } else { // Show no photo link
2134  $nophoto = '/public/theme/common/nophoto.png';
2135  $morehtmlleft .= '<div class="floatleft inline-block valignmiddle divphotoref"><img class="photo'.$modulepart.($cssclass ? ' '.$cssclass : '').'" alt="No photo"'.($width ? ' style="width: '.$width.'px"' : '').' src="'.DOL_URL_ROOT.$nophoto.'"></div>';
2136  }
2137  }
2138  } elseif ($object->element == 'ticket') {
2139  $width = 80;
2140  $cssclass = 'photoref';
2141  $showimage = $object->is_photo_available($conf->ticket->multidir_output[$entity].'/'.$object->ref);
2142  $maxvisiblephotos = (isset($conf->global->TICKET_MAX_VISIBLE_PHOTO) ? $conf->global->TICKET_MAX_VISIBLE_PHOTO : 2);
2143  if ($conf->browser->layout == 'phone') {
2144  $maxvisiblephotos = 1;
2145  }
2146 
2147  if ($showimage) {
2148  $showphoto = $object->show_photos('ticket', $conf->ticket->multidir_output[$entity], 'small', $maxvisiblephotos, 0, 0, 0, $width, 0);
2149  if ($object->nbphoto > 0) {
2150  $morehtmlleft .= '<div class="floatleft inline-block valignmiddle divphotoref">'.$showphoto.'</div>';
2151  } else {
2152  $showimage = 0;
2153  }
2154  }
2155  if (!$showimage) {
2156  if (!empty($conf->global->TICKET_NODISPLAYIFNOPHOTO)) {
2157  $nophoto = '';
2158  $morehtmlleft .= '<div class="floatleft inline-block valignmiddle divphotoref"></div>';
2159  } else { // Show no photo link
2160  $nophoto = img_picto('No photo', 'object_ticket');
2161  $morehtmlleft .= '<!-- No photo to show -->';
2162  $morehtmlleft .= '<div class="floatleft inline-block valignmiddle divphotoref"><div class="photoref">';
2163  $morehtmlleft .= $nophoto;
2164  $morehtmlleft .= '</div></div>';
2165  }
2166  }
2167  } else {
2168  if ($showimage) {
2169  if ($modulepart != 'unknown') {
2170  $phototoshow = '';
2171  // Check if a preview file is available
2172  if (in_array($modulepart, array('propal', 'commande', 'facture', 'ficheinter', 'contract', 'supplier_order', 'supplier_proposal', 'supplier_invoice', 'expensereport')) && class_exists("Imagick")) {
2173  $objectref = dol_sanitizeFileName($object->ref);
2174  $dir_output = (empty($conf->$modulepart->multidir_output[$entity]) ? $conf->$modulepart->dir_output : $conf->$modulepart->multidir_output[$entity])."/";
2175  if (in_array($modulepart, array('invoice_supplier', 'supplier_invoice'))) {
2176  $subdir = get_exdir($object->id, 2, 0, 1, $object, $modulepart);
2177  $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
2178  } else {
2179  $subdir = get_exdir($object->id, 0, 0, 1, $object, $modulepart);
2180  }
2181  if (empty($subdir)) {
2182  $subdir = 'errorgettingsubdirofobject'; // Protection to avoid to return empty path
2183  }
2184 
2185  $filepath = $dir_output.$subdir."/";
2186 
2187  $filepdf = $filepath.$objectref.".pdf";
2188  $relativepath = $subdir.'/'.$objectref.'.pdf';
2189 
2190  // Define path to preview pdf file (preview precompiled "file.ext" are "file.ext_preview.png")
2191  $fileimage = $filepdf.'_preview.png';
2192  $relativepathimage = $relativepath.'_preview.png';
2193 
2194  $pdfexists = file_exists($filepdf);
2195 
2196  // If PDF file exists
2197  if ($pdfexists) {
2198  // Conversion du PDF en image png si fichier png non existant
2199  if (!file_exists($fileimage) || (filemtime($fileimage) < filemtime($filepdf))) {
2200  if (empty($conf->global->MAIN_DISABLE_PDF_THUMBS)) { // If you experience trouble with pdf thumb generation and imagick, you can disable here.
2201  include_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
2202  $ret = dol_convert_file($filepdf, 'png', $fileimage, '0'); // Convert first page of PDF into a file _preview.png
2203  if ($ret < 0) {
2204  $error++;
2205  }
2206  }
2207  }
2208  }
2209 
2210  if ($pdfexists && !$error) {
2211  $heightforphotref = 80;
2212  if (!empty($conf->dol_optimize_smallscreen)) {
2213  $heightforphotref = 60;
2214  }
2215  // If the preview file is found
2216  if (file_exists($fileimage)) {
2217  $phototoshow = '<div class="photoref">';
2218  $phototoshow .= '<img height="'.$heightforphotref.'" class="photo photowithborder" src="'.DOL_URL_ROOT.'/viewimage.php?modulepart=apercu'.$modulepart.'&amp;file='.urlencode($relativepathimage).'">';
2219  $phototoshow .= '</div>';
2220  }
2221  }
2222  } elseif (!$phototoshow) { // example if modulepart = 'societe' or 'photo'
2223  $phototoshow .= $form->showphoto($modulepart, $object, 0, 0, 0, 'photowithmargin photoref', 'small', 1, 0, $maxvisiblephotos);
2224  }
2225 
2226  if ($phototoshow) {
2227  $morehtmlleft .= '<div class="floatleft inline-block valignmiddle divphotoref">';
2228  $morehtmlleft .= $phototoshow;
2229  $morehtmlleft .= '</div>';
2230  }
2231  }
2232 
2233  if (empty($phototoshow)) { // Show No photo link (picto of object)
2234  if ($object->element == 'action') {
2235  $width = 80;
2236  $cssclass = 'photorefcenter';
2237  $nophoto = img_picto('No photo', 'title_agenda');
2238  } else {
2239  $width = 14;
2240  $cssclass = 'photorefcenter';
2241  $picto = $object->picto;
2242  $prefix = 'object_';
2243  if ($object->element == 'project' && !$object->public) {
2244  $picto = 'project'; // instead of projectpub
2245  }
2246  if (strpos($picto, 'fontawesome_') !== false) {
2247  $prefix = '';
2248  }
2249  $nophoto = img_picto('No photo', $prefix.$picto);
2250  }
2251  $morehtmlleft .= '<!-- No photo to show -->';
2252  $morehtmlleft .= '<div class="floatleft inline-block valignmiddle divphotoref"><div class="photoref">';
2253  $morehtmlleft .= $nophoto;
2254  $morehtmlleft .= '</div></div>';
2255  }
2256  }
2257  }
2258 
2259  if ($showbarcode) {
2260  $morehtmlleft .= '<div class="floatleft inline-block valignmiddle divphotoref">'.$form->showbarcode($object, 100, 'photoref valignmiddle').'</div>';
2261  }
2262 
2263  if ($object->element == 'societe') {
2264  if (!empty($conf->use_javascript_ajax) && $user->hasRight('societe', 'creer') && !empty($conf->global->MAIN_DIRECT_STATUS_UPDATE)) {
2265  $morehtmlstatus .= ajax_object_onoff($object, 'status', 'status', 'InActivity', 'ActivityCeased');
2266  } else {
2267  $morehtmlstatus .= $object->getLibStatut(6);
2268  }
2269  } elseif ($object->element == 'product') {
2270  //$morehtmlstatus.=$langs->trans("Status").' ('.$langs->trans("Sell").') ';
2271  if (!empty($conf->use_javascript_ajax) && $user->rights->produit->creer && !empty($conf->global->MAIN_DIRECT_STATUS_UPDATE)) {
2272  $morehtmlstatus .= ajax_object_onoff($object, 'status', 'tosell', 'ProductStatusOnSell', 'ProductStatusNotOnSell');
2273  } else {
2274  $morehtmlstatus .= '<span class="statusrefsell">'.$object->getLibStatut(6, 0).'</span>';
2275  }
2276  $morehtmlstatus .= ' &nbsp; ';
2277  //$morehtmlstatus.=$langs->trans("Status").' ('.$langs->trans("Buy").') ';
2278  if (!empty($conf->use_javascript_ajax) && $user->rights->produit->creer && !empty($conf->global->MAIN_DIRECT_STATUS_UPDATE)) {
2279  $morehtmlstatus .= ajax_object_onoff($object, 'status_buy', 'tobuy', 'ProductStatusOnBuy', 'ProductStatusNotOnBuy');
2280  } else {
2281  $morehtmlstatus .= '<span class="statusrefbuy">'.$object->getLibStatut(6, 1).'</span>';
2282  }
2283  } elseif (in_array($object->element, array('facture', 'invoice', 'invoice_supplier', 'chargesociales', 'loan', 'tva', 'salary'))) {
2284  $tmptxt = $object->getLibStatut(6, $object->totalpaid);
2285  if (empty($tmptxt) || $tmptxt == $object->getLibStatut(3)) {
2286  $tmptxt = $object->getLibStatut(5, $object->totalpaid);
2287  }
2288  $morehtmlstatus .= $tmptxt;
2289  } elseif ($object->element == 'contrat' || $object->element == 'contract') {
2290  if ($object->statut == 0) {
2291  $morehtmlstatus .= $object->getLibStatut(5);
2292  } else {
2293  $morehtmlstatus .= $object->getLibStatut(4);
2294  }
2295  } elseif ($object->element == 'facturerec') {
2296  if ($object->frequency == 0) {
2297  $morehtmlstatus .= $object->getLibStatut(2);
2298  } else {
2299  $morehtmlstatus .= $object->getLibStatut(5);
2300  }
2301  } elseif ($object->element == 'project_task') {
2302  $object->fk_statut = 1;
2303  if ($object->progress > 0) {
2304  $object->fk_statut = 2;
2305  }
2306  if ($object->progress >= 100) {
2307  $object->fk_statut = 3;
2308  }
2309  $tmptxt = $object->getLibStatut(5);
2310  $morehtmlstatus .= $tmptxt; // No status on task
2311  } elseif (method_exists($object, 'getLibStatut')) { // Generic case
2312  $tmptxt = $object->getLibStatut(6);
2313  if (empty($tmptxt) || $tmptxt == $object->getLibStatut(3)) {
2314  $tmptxt = $object->getLibStatut(5);
2315  }
2316  $morehtmlstatus .= $tmptxt;
2317  }
2318 
2319  // Add if object was dispatched "into accountancy"
2320  if (isModEnabled('accounting') && in_array($object->element, array('bank', 'paiementcharge', 'facture', 'invoice', 'invoice_supplier', 'expensereport', 'payment_various'))) {
2321  // Note: For 'chargesociales', 'salaries'... this is the payments that are dispatched (so element = 'bank')
2322  if (method_exists($object, 'getVentilExportCompta')) {
2323  $accounted = $object->getVentilExportCompta();
2324  $langs->load("accountancy");
2325  $morehtmlstatus .= '</div><div class="statusref statusrefbis"><span class="opacitymedium">'.($accounted > 0 ? $langs->trans("Accounted") : $langs->trans("NotYetAccounted")).'</span>';
2326  }
2327  }
2328 
2329  // Add alias for thirdparty
2330  if (!empty($object->name_alias)) {
2331  $morehtmlref .= '<div class="refidno opacitymedium">'.$object->name_alias.'</div>';
2332  }
2333 
2334  // Add label
2335  if (in_array($object->element, array('product', 'bank_account', 'project_task'))) {
2336  if (!empty($object->label)) {
2337  $morehtmlref .= '<div class="refidno opacitymedium">'.$object->label.'</div>';
2338  }
2339  }
2340 
2341  // Show address and email
2342  if (method_exists($object, 'getBannerAddress') && !in_array($object->element, array('product', 'bookmark', 'ecm_directories', 'ecm_files'))) {
2343  $moreaddress = $object->getBannerAddress('refaddress', $object);
2344  if ($moreaddress) {
2345  $morehtmlref .= '<div class="refidno refaddress">';
2346  $morehtmlref .= $moreaddress;
2347  $morehtmlref .= '</div>';
2348  }
2349  }
2350  if (!empty($conf->global->MAIN_SHOW_TECHNICAL_ID) && ($conf->global->MAIN_SHOW_TECHNICAL_ID == '1' || preg_match('/'.preg_quote($object->element, '/').'/i', $conf->global->MAIN_SHOW_TECHNICAL_ID)) && !empty($object->id)) {
2351  $morehtmlref .= '<div style="clear: both;"></div>';
2352  $morehtmlref .= '<div class="refidno opacitymedium">';
2353  $morehtmlref .= $langs->trans("TechnicalID").': '.$object->id;
2354  $morehtmlref .= '</div>';
2355  }
2356 
2357  $parameters=array('morehtmlref'=>$morehtmlref);
2358  $reshook = $hookmanager->executeHooks('formDolBanner', $parameters, $object, $action);
2359  if ($reshook < 0) {
2360  setEventMessages($hookmanager->error, $hookmanager->errors, 'errors');
2361  } elseif (empty($reshook)) {
2362  $morehtmlref .= $hookmanager->resPrint;
2363  } elseif ($reshook > 0) {
2364  $morehtmlref = $hookmanager->resPrint;
2365  }
2366 
2367 
2368  print '<div class="'.($onlybanner ? 'arearefnobottom ' : 'arearef ').'heightref valignmiddle centpercent">';
2369  print $form->showrefnav($object, $paramid, $morehtml, $shownav, $fieldid, $fieldref, $morehtmlref, $moreparam, $nodbprefix, $morehtmlleft, $morehtmlstatus, $morehtmlright);
2370  print '</div>';
2371  print '<div class="underrefbanner clearboth"></div>';
2372 }
2373 
2383 function fieldLabel($langkey, $fieldkey, $fieldrequired = 0)
2384 {
2385  global $langs;
2386  $ret = '';
2387  if ($fieldrequired) {
2388  $ret .= '<span class="fieldrequired">';
2389  }
2390  $ret .= '<label for="'.$fieldkey.'">';
2391  $ret .= $langs->trans($langkey);
2392  $ret .= '</label>';
2393  if ($fieldrequired) {
2394  $ret .= '</span>';
2395  }
2396  return $ret;
2397 }
2398 
2406 function dol_bc($var, $moreclass = '')
2407 {
2408  global $bc;
2409  $ret = ' '.$bc[$var];
2410  if ($moreclass) {
2411  $ret = preg_replace('/class=\"/', 'class="'.$moreclass.' ', $ret);
2412  }
2413  return $ret;
2414 }
2415 
2429 function dol_format_address($object, $withcountry = 0, $sep = "\n", $outputlangs = '', $mode = 0, $extralangcode = '')
2430 {
2431  global $conf, $langs, $hookmanager;
2432 
2433  $ret = '';
2434  $countriesusingstate = array('AU', 'CA', 'US', 'IN', 'GB', 'ES', 'UK', 'TR', 'CN'); // See also MAIN_FORCE_STATE_INTO_ADDRESS
2435 
2436  // See format of addresses on https://en.wikipedia.org/wiki/Address
2437  // Address
2438  if (empty($mode)) {
2439  $ret .= ($extralangcode ? $object->array_languages['address'][$extralangcode] : (empty($object->address) ? '' : $object->address));
2440  }
2441  // Zip/Town/State
2442  if (isset($object->country_code) && in_array($object->country_code, array('AU', 'CA', 'US', 'CN')) || !empty($conf->global->MAIN_FORCE_STATE_INTO_ADDRESS)) {
2443  // US: title firstname name \n address lines \n town, state, zip \n country
2444  $town = ($extralangcode ? $object->array_languages['town'][$extralangcode] : (empty($object->town) ? '' : $object->town));
2445  $ret .= (($ret && $town) ? $sep : '').$town;
2446 
2447  if (!empty($object->state)) {
2448  $ret .= ($ret ? ($town ? ", " : $sep) : '').$object->state;
2449  }
2450  if (!empty($object->zip)) {
2451  $ret .= ($ret ? (($town || $object->state) ? ", " : $sep) : '').$object->zip;
2452  }
2453  } elseif (isset($object->country_code) && in_array($object->country_code, array('GB', 'UK'))) {
2454  // UK: title firstname name \n address lines \n town state \n zip \n country
2455  $town = ($extralangcode ? $object->array_languages['town'][$extralangcode] : (empty($object->town) ? '' : $object->town));
2456  $ret .= ($ret ? $sep : '').$town;
2457  if (!empty($object->state)) {
2458  $ret .= ($ret ? ", " : '').$object->state;
2459  }
2460  if (!empty($object->zip)) {
2461  $ret .= ($ret ? $sep : '').$object->zip;
2462  }
2463  } elseif (isset($object->country_code) && in_array($object->country_code, array('ES', 'TR'))) {
2464  // ES: title firstname name \n address lines \n zip town \n state \n country
2465  $ret .= ($ret ? $sep : '').$object->zip;
2466  $town = ($extralangcode ? $object->array_languages['town'][$extralangcode] : (empty($object->town) ? '' : $object->town));
2467  $ret .= ($town ? (($object->zip ? ' ' : '').$town) : '');
2468  if (!empty($object->state)) {
2469  $ret .= "\n".$object->state;
2470  }
2471  } elseif (isset($object->country_code) && in_array($object->country_code, array('JP'))) {
2472  // JP: In romaji, title firstname name\n address lines \n [state,] town zip \n country
2473  // See https://www.sljfaq.org/afaq/addresses.html
2474  $town = ($extralangcode ? $object->array_languages['town'][$extralangcode] : (empty($object->town) ? '' : $object->town));
2475  $ret .= ($ret ? $sep : '').($object->state ? $object->state.', ' : '').$town.($object->zip ? ' ' : '').$object->zip;
2476  } elseif (isset($object->country_code) && in_array($object->country_code, array('IT'))) {
2477  // IT: title firstname name\n address lines \n zip town state_code \n country
2478  $ret .= ($ret ? $sep : '').$object->zip;
2479  $town = ($extralangcode ? $object->array_languages['town'][$extralangcode] : (empty($object->town) ? '' : $object->town));
2480  $ret .= ($town ? (($object->zip ? ' ' : '').$town) : '');
2481  $ret .= (empty($object->state_code) ? '' : (' '.$object->state_code));
2482  } else {
2483  // Other: title firstname name \n address lines \n zip town[, state] \n country
2484  $town = ($extralangcode ? $object->array_languages['town'][$extralangcode] : (empty($object->town) ? '' : $object->town));
2485  $ret .= !empty($object->zip) ? (($ret ? $sep : '').$object->zip) : '';
2486  $ret .= ($town ? (($object->zip ? ' ' : ($ret ? $sep : '')).$town) : '');
2487  if (!empty($object->state) && in_array($object->country_code, $countriesusingstate)) {
2488  $ret .= ($ret ? ", " : '').$object->state;
2489  }
2490  }
2491  if (!is_object($outputlangs)) {
2492  $outputlangs = $langs;
2493  }
2494  if ($withcountry) {
2495  $langs->load("dict");
2496  $ret .= (empty($object->country_code) ? '' : ($ret ? $sep : '').$outputlangs->convToOutputCharset($outputlangs->transnoentitiesnoconv("Country".$object->country_code)));
2497  }
2498  if ($hookmanager) {
2499  $parameters = array('withcountry' => $withcountry, 'sep' => $sep, 'outputlangs' => $outputlangs,'mode' => $mode, 'extralangcode' => $extralangcode);
2500  $reshook = $hookmanager->executeHooks('formatAddress', $parameters, $object);
2501  if ($reshook > 0) {
2502  $ret = '';
2503  }
2504  $ret .= $hookmanager->resPrint;
2505  }
2506 
2507  return $ret;
2508 }
2509 
2510 
2511 
2520 function dol_strftime($fmt, $ts = false, $is_gmt = false)
2521 {
2522  if ((abs($ts) <= 0x7FFFFFFF)) { // check if number in 32-bit signed range
2523  return ($is_gmt) ? @gmstrftime($fmt, $ts) : @strftime($fmt, $ts);
2524  } else {
2525  return 'Error date into a not supported range';
2526  }
2527 }
2528 
2550 function dol_print_date($time, $format = '', $tzoutput = 'auto', $outputlangs = '', $encodetooutput = false)
2551 {
2552  global $conf, $langs;
2553 
2554  // If date undefined or "", we return ""
2555  if (dol_strlen($time) == 0) {
2556  return ''; // $time=0 allowed (it means 01/01/1970 00:00:00)
2557  }
2558 
2559  if ($tzoutput === 'auto') {
2560  $tzoutput = (empty($conf) ? 'tzserver' : (isset($conf->tzuserinputkey) ? $conf->tzuserinputkey : 'tzserver'));
2561  }
2562 
2563  // Clean parameters
2564  $to_gmt = false;
2565  $offsettz = $offsetdst = 0;
2566  if ($tzoutput) {
2567  $to_gmt = true; // For backward compatibility
2568  if (is_string($tzoutput)) {
2569  if ($tzoutput == 'tzserver') {
2570  $to_gmt = false;
2571  $offsettzstring = @date_default_timezone_get(); // Example 'Europe/Berlin' or 'Indian/Reunion'
2572  $offsettz = 0; // Timezone offset with server timezone (because to_gmt is false), so 0
2573  $offsetdst = 0; // Dst offset with server timezone (because to_gmt is false), so 0
2574  } elseif ($tzoutput == 'tzuser' || $tzoutput == 'tzuserrel') {
2575  $to_gmt = true;
2576  $offsettzstring = (empty($_SESSION['dol_tz_string']) ? 'UTC' : $_SESSION['dol_tz_string']); // Example 'Europe/Berlin' or 'Indian/Reunion'
2577 
2578  if (class_exists('DateTimeZone')) {
2579  $user_date_tz = new DateTimeZone($offsettzstring);
2580  $user_dt = new DateTime();
2581  $user_dt->setTimezone($user_date_tz);
2582  $user_dt->setTimestamp($tzoutput == 'tzuser' ? dol_now() : (int) $time);
2583  $offsettz = $user_dt->getOffset(); // should include dst ?
2584  } else { // with old method (The 'tzuser' was processed like the 'tzuserrel')
2585  $offsettz = (empty($_SESSION['dol_tz']) ? 0 : $_SESSION['dol_tz']) * 60 * 60; // Will not be used anymore
2586  $offsetdst = (empty($_SESSION['dol_dst']) ? 0 : $_SESSION['dol_dst']) * 60 * 60; // Will not be used anymore
2587  }
2588  }
2589  }
2590  }
2591  if (!is_object($outputlangs)) {
2592  $outputlangs = $langs;
2593  }
2594  if (!$format) {
2595  $format = 'daytextshort';
2596  }
2597 
2598  // Do we have to reduce the length of date (year on 2 chars) to save space.
2599  // Note: dayinputnoreduce is same than day but no reduction of year length will be done
2600  $reduceformat = (!empty($conf->dol_optimize_smallscreen) && in_array($format, array('day', 'dayhour'))) ? 1 : 0; // Test on original $format param.
2601  $format = preg_replace('/inputnoreduce/', '', $format); // so format 'dayinputnoreduce' is processed like day
2602  $formatwithoutreduce = preg_replace('/reduceformat/', '', $format);
2603  if ($formatwithoutreduce != $format) {
2604  $format = $formatwithoutreduce;
2605  $reduceformat = 1;
2606  } // so format 'dayreduceformat' is processed like day
2607 
2608  // Change predefined format into computer format. If found translation in lang file we use it, otherwise we use default.
2609  // TODO Add format daysmallyear and dayhoursmallyear
2610  if ($format == 'day') {
2611  $format = ($outputlangs->trans("FormatDateShort") != "FormatDateShort" ? $outputlangs->trans("FormatDateShort") : $conf->format_date_short);
2612  } elseif ($format == 'hour') {
2613  $format = ($outputlangs->trans("FormatHourShort") != "FormatHourShort" ? $outputlangs->trans("FormatHourShort") : $conf->format_hour_short);
2614  } elseif ($format == 'hourduration') {
2615  $format = ($outputlangs->trans("FormatHourShortDuration") != "FormatHourShortDuration" ? $outputlangs->trans("FormatHourShortDuration") : $conf->format_hour_short_duration);
2616  } elseif ($format == 'daytext') {
2617  $format = ($outputlangs->trans("FormatDateText") != "FormatDateText" ? $outputlangs->trans("FormatDateText") : $conf->format_date_text);
2618  } elseif ($format == 'daytextshort') {
2619  $format = ($outputlangs->trans("FormatDateTextShort") != "FormatDateTextShort" ? $outputlangs->trans("FormatDateTextShort") : $conf->format_date_text_short);
2620  } elseif ($format == 'dayhour') {
2621  $format = ($outputlangs->trans("FormatDateHourShort") != "FormatDateHourShort" ? $outputlangs->trans("FormatDateHourShort") : $conf->format_date_hour_short);
2622  } elseif ($format == 'dayhoursec') {
2623  $format = ($outputlangs->trans("FormatDateHourSecShort") != "FormatDateHourSecShort" ? $outputlangs->trans("FormatDateHourSecShort") : $conf->format_date_hour_sec_short);
2624  } elseif ($format == 'dayhourtext') {
2625  $format = ($outputlangs->trans("FormatDateHourText") != "FormatDateHourText" ? $outputlangs->trans("FormatDateHourText") : $conf->format_date_hour_text);
2626  } elseif ($format == 'dayhourtextshort') {
2627  $format = ($outputlangs->trans("FormatDateHourTextShort") != "FormatDateHourTextShort" ? $outputlangs->trans("FormatDateHourTextShort") : $conf->format_date_hour_text_short);
2628  } elseif ($format == 'dayhourlog') {
2629  // Format not sensitive to language
2630  $format = '%Y%m%d%H%M%S';
2631  } elseif ($format == 'dayhourlogsmall') {
2632  // Format not sensitive to language
2633  $format = '%y%m%d%H%M';
2634  } elseif ($format == 'dayhourldap') {
2635  $format = '%Y%m%d%H%M%SZ';
2636  } elseif ($format == 'dayhourxcard') {
2637  $format = '%Y%m%dT%H%M%SZ';
2638  } elseif ($format == 'dayxcard') {
2639  $format = '%Y%m%d';
2640  } elseif ($format == 'dayrfc') {
2641  $format = '%Y-%m-%d'; // DATE_RFC3339
2642  } elseif ($format == 'dayhourrfc') {
2643  $format = '%Y-%m-%dT%H:%M:%SZ'; // DATETIME RFC3339
2644  } elseif ($format == 'standard') {
2645  $format = '%Y-%m-%d %H:%M:%S';
2646  }
2647 
2648  if ($reduceformat) {
2649  $format = str_replace('%Y', '%y', $format);
2650  $format = str_replace('yyyy', 'yy', $format);
2651  }
2652 
2653  // Clean format
2654  if (preg_match('/%b/i', $format)) { // There is some text to translate
2655  // We inhibate translation to text made by strftime functions. We will use trans instead later.
2656  $format = str_replace('%b', '__b__', $format);
2657  $format = str_replace('%B', '__B__', $format);
2658  }
2659  if (preg_match('/%a/i', $format)) { // There is some text to translate
2660  // We inhibate translation to text made by strftime functions. We will use trans instead later.
2661  $format = str_replace('%a', '__a__', $format);
2662  $format = str_replace('%A', '__A__', $format);
2663  }
2664 
2665  $useadodb = getDolGlobalInt('MAIN_USE_LEGACY_ADODB_FOR_DATE', 0);
2666  //$useadodb = 1; // To switch to adodb
2667  if (!empty($useadodb)) {
2668  include_once DOL_DOCUMENT_ROOT.'/includes/adodbtime/adodb-time.inc.php';
2669  }
2670 
2671  // Analyze date
2672  $reg = array();
2673  if (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
2674  dol_print_error('', "Functions.lib::dol_print_date function called with a bad value from page ".$_SERVER["PHP_SELF"]);
2675  return '';
2676  } elseif (preg_match('/^([0-9]+)\-([0-9]+)\-([0-9]+) ?([0-9]+)?:?([0-9]+)?:?([0-9]+)?/i', $time, $reg)) { // Still available to solve problems in extrafields of type date
2677  // This part of code should not be used anymore.
2678  dol_syslog("Functions.lib::dol_print_date function called with a bad value from page ".$_SERVER["PHP_SELF"], LOG_WARNING);
2679  //if (function_exists('debug_print_backtrace')) debug_print_backtrace();
2680  // Date has format 'YYYY-MM-DD' or 'YYYY-MM-DD HH:MM:SS'
2681  $syear = (!empty($reg[1]) ? $reg[1] : '');
2682  $smonth = (!empty($reg[2]) ? $reg[2] : '');
2683  $sday = (!empty($reg[3]) ? $reg[3] : '');
2684  $shour = (!empty($reg[4]) ? $reg[4] : '');
2685  $smin = (!empty($reg[5]) ? $reg[5] : '');
2686  $ssec = (!empty($reg[6]) ? $reg[6] : '');
2687 
2688  $time = dol_mktime($shour, $smin, $ssec, $smonth, $sday, $syear, true);
2689  if (empty($useadodb)) {
2690  if ($to_gmt) {
2691  $tzo = new DateTimeZone('UTC'); // when to_gmt is true, base for offsettz and offsetdst (so timetouse) is UTC
2692  } else {
2693  $tzo = new DateTimeZone(date_default_timezone_get()); // when to_gmt is false, base for offsettz and offsetdst (so timetouse) is PHP server
2694  }
2695  $dtts = new DateTime();
2696  $dtts->setTimestamp($time);
2697  $dtts->setTimezone($tzo);
2698  $newformat = str_replace(
2699  array('%Y', '%y', '%m', '%d', '%H', '%I', '%M', '%S', '%p', '%w', 'T', 'Z', '__a__', '__A__', '__b__', '__B__'),
2700  array('Y', 'y', 'm', 'd', 'H', 'h', 'i', 's', 'A', 'w', '__£__', '__$__', '__{__', '__}__', '__[__', '__]__'),
2701  $format);
2702  $ret = $dtts->format($newformat);
2703  $ret = str_replace(
2704  array('__£__', '__$__', '__{__', '__}__', '__[__', '__]__'),
2705  array('T', 'Z', '__a__', '__A__', '__b__', '__B__'),
2706  $ret);
2707  } else {
2708  $ret = adodb_strftime($format, $time + $offsettz + $offsetdst, $to_gmt);
2709  }
2710  } else {
2711  // Date is a timestamps
2712  if ($time < 100000000000) { // Protection against bad date values
2713  $timetouse = $time + $offsettz + $offsetdst; // TODO We could be able to disable use of offsettz and offsetdst to use only offsettzstring.
2714 
2715  if (empty($useadodb)) {
2716  if ($to_gmt) {
2717  $tzo = new DateTimeZone('UTC'); // when to_gmt is true, base for offsettz and offsetdst (so timetouse) is UTC
2718  } else {
2719  $tzo = new DateTimeZone(date_default_timezone_get()); // when to_gmt is false, base for offsettz and offsetdst (so timetouse) is PHP server
2720  }
2721  $dtts = new DateTime();
2722  $dtts->setTimestamp($timetouse);
2723  $dtts->setTimezone($tzo);
2724  $newformat = str_replace(
2725  array('%Y', '%y', '%m', '%d', '%H', '%I', '%M', '%S', '%p', '%w', 'T', 'Z', '__a__', '__A__', '__b__', '__B__'),
2726  array('Y', 'y', 'm', 'd', 'H', 'h', 'i', 's', 'A', 'w', '__£__', '__$__', '__{__', '__}__', '__[__', '__]__'),
2727  $format);
2728  $ret = $dtts->format($newformat);
2729  $ret = str_replace(
2730  array('__£__', '__$__', '__{__', '__}__', '__[__', '__]__'),
2731  array('T', 'Z', '__a__', '__A__', '__b__', '__B__'),
2732  $ret);
2733  } else {
2734  $ret = adodb_strftime($format, $timetouse, $to_gmt); // If to_gmt = false then adodb_strftime use TZ of server
2735  }
2736  //var_dump($ret);exit;
2737  } else {
2738  $ret = 'Bad value '.$time.' for date';
2739  }
2740  }
2741 
2742  if (preg_match('/__b__/i', $format)) {
2743  $timetouse = $time + $offsettz + $offsetdst; // TODO We could be able to disable use of offsettz and offsetdst to use only offsettzstring.
2744 
2745  if (empty($useadodb)) {
2746  if ($to_gmt) {
2747  $tzo = new DateTimeZone('UTC'); // when to_gmt is true, base for offsettz and offsetdst (so timetouse) is UTC
2748  } else {
2749  $tzo = new DateTimeZone(date_default_timezone_get()); // when to_gmt is false, base for offsettz and offsetdst (so timetouse) is PHP server
2750  }
2751  $dtts = new DateTime();
2752  $dtts->setTimestamp($timetouse);
2753  $dtts->setTimezone($tzo);
2754  $month = $dtts->format("m");
2755  } else {
2756  // After this ret is string in PHP setup language (strftime was used). Now we convert to $outputlangs.
2757  $month = adodb_strftime('%m', $timetouse, $to_gmt); // If to_gmt = false then adodb_strftime use TZ of server
2758  }
2759  $month = sprintf("%02d", $month); // $month may be return with format '06' on some installation and '6' on other, so we force it to '06'.
2760  if ($encodetooutput) {
2761  $monthtext = $outputlangs->transnoentities('Month'.$month);
2762  $monthtextshort = $outputlangs->transnoentities('MonthShort'.$month);
2763  } else {
2764  $monthtext = $outputlangs->transnoentitiesnoconv('Month'.$month);
2765  $monthtextshort = $outputlangs->transnoentitiesnoconv('MonthShort'.$month);
2766  }
2767  //print 'monthtext='.$monthtext.' monthtextshort='.$monthtextshort;
2768  $ret = str_replace('__b__', $monthtextshort, $ret);
2769  $ret = str_replace('__B__', $monthtext, $ret);
2770  //print 'x'.$outputlangs->charset_output.'-'.$ret.'x';
2771  //return $ret;
2772  }
2773  if (preg_match('/__a__/i', $format)) {
2774  //print "time=$time offsettz=$offsettz offsetdst=$offsetdst offsettzstring=$offsettzstring";
2775  $timetouse = $time + $offsettz + $offsetdst; // TODO Replace this with function Date PHP. We also should not use anymore offsettz and offsetdst but only offsettzstring.
2776 
2777  if (empty($useadodb)) {
2778  if ($to_gmt) {
2779  $tzo = new DateTimeZone('UTC');
2780  } else {
2781  $tzo = new DateTimeZone(date_default_timezone_get());
2782  }
2783  $dtts = new DateTime();
2784  $dtts->setTimestamp($timetouse);
2785  $dtts->setTimezone($tzo);
2786  $w = $dtts->format("w");
2787  } else {
2788  $w = adodb_strftime('%w', $timetouse, $to_gmt); // If to_gmt = false then adodb_strftime use TZ of server
2789  }
2790  $dayweek = $outputlangs->transnoentitiesnoconv('Day'.$w);
2791 
2792  $ret = str_replace('__A__', $dayweek, $ret);
2793  $ret = str_replace('__a__', dol_substr($dayweek, 0, 3), $ret);
2794  }
2795 
2796  return $ret;
2797 }
2798 
2799 
2820 function dol_getdate($timestamp, $fast = false, $forcetimezone = '')
2821 {
2822  $datetimeobj = new DateTime();
2823  $datetimeobj->setTimestamp($timestamp); // Use local PHP server timezone
2824  if ($forcetimezone) {
2825  $datetimeobj->setTimezone(new DateTimeZone($forcetimezone == 'gmt' ? 'UTC' : $forcetimezone)); // (add timezone relative to the date entered)
2826  }
2827  $arrayinfo = array(
2828  'year'=>((int) date_format($datetimeobj, 'Y')),
2829  'mon'=>((int) date_format($datetimeobj, 'm')),
2830  'mday'=>((int) date_format($datetimeobj, 'd')),
2831  'wday'=>((int) date_format($datetimeobj, 'w')),
2832  'yday'=>((int) date_format($datetimeobj, 'z')),
2833  'hours'=>((int) date_format($datetimeobj, 'H')),
2834  'minutes'=>((int) date_format($datetimeobj, 'i')),
2835  'seconds'=>((int) date_format($datetimeobj, 's')),
2836  '0'=>$timestamp
2837  );
2838 
2839  return $arrayinfo;
2840 }
2841 
2863 function dol_mktime($hour, $minute, $second, $month, $day, $year, $gm = 'auto', $check = 1)
2864 {
2865  global $conf;
2866  //print "- ".$hour.",".$minute.",".$second.",".$month.",".$day.",".$year.",".$_SERVER["WINDIR"]." -";
2867 
2868  if ($gm === 'auto') {
2869  $gm = (empty($conf) ? 'tzserver' : $conf->tzuserinputkey);
2870  }
2871  //print 'gm:'.$gm.' gm === auto:'.($gm === 'auto').'<br>';exit;
2872 
2873  // Clean parameters
2874  if ($hour == -1 || empty($hour)) {
2875  $hour = 0;
2876  }
2877  if ($minute == -1 || empty($minute)) {
2878  $minute = 0;
2879  }
2880  if ($second == -1 || empty($second)) {
2881  $second = 0;
2882  }
2883 
2884  // Check parameters
2885  if ($check) {
2886  if (!$month || !$day) {
2887  return '';
2888  }
2889  if ($day > 31) {
2890  return '';
2891  }
2892  if ($month > 12) {
2893  return '';
2894  }
2895  if ($hour < 0 || $hour > 24) {
2896  return '';
2897  }
2898  if ($minute < 0 || $minute > 60) {
2899  return '';
2900  }
2901  if ($second < 0 || $second > 60) {
2902  return '';
2903  }
2904  }
2905 
2906  if (empty($gm) || ($gm === 'server' || $gm === 'tzserver')) {
2907  $default_timezone = @date_default_timezone_get(); // Example 'Europe/Berlin'
2908  $localtz = new DateTimeZone($default_timezone);
2909  } elseif ($gm === 'user' || $gm === 'tzuser' || $gm === 'tzuserrel') {
2910  // We use dol_tz_string first because it is more reliable.
2911  $default_timezone = (empty($_SESSION["dol_tz_string"]) ? @date_default_timezone_get() : $_SESSION["dol_tz_string"]); // Example 'Europe/Berlin'
2912  try {
2913  $localtz = new DateTimeZone($default_timezone);
2914  } catch (Exception $e) {
2915  dol_syslog("Warning dol_tz_string contains an invalid value ".$_SESSION["dol_tz_string"], LOG_WARNING);
2916  $default_timezone = @date_default_timezone_get();
2917  }
2918  } elseif (strrpos($gm, "tz,") !== false) {
2919  $timezone = str_replace("tz,", "", $gm); // Example 'tz,Europe/Berlin'
2920  try {
2921  $localtz = new DateTimeZone($timezone);
2922  } catch (Exception $e) {
2923  dol_syslog("Warning passed timezone contains an invalid value ".$timezone, LOG_WARNING);
2924  }
2925  }
2926 
2927  if (empty($localtz)) {
2928  $localtz = new DateTimeZone('UTC');
2929  }
2930  //var_dump($localtz);
2931  //var_dump($year.'-'.$month.'-'.$day.'-'.$hour.'-'.$minute);
2932  $dt = new DateTime('now', $localtz);
2933  $dt->setDate((int) $year, (int) $month, (int) $day);
2934  $dt->setTime((int) $hour, (int) $minute, (int) $second);
2935  $date = $dt->getTimestamp(); // should include daylight saving time
2936  //var_dump($date);
2937  return $date;
2938 }
2939 
2940 
2951 function dol_now($mode = 'auto')
2952 {
2953  $ret = 0;
2954 
2955  if ($mode === 'auto') {
2956  $mode = 'gmt';
2957  }
2958 
2959  if ($mode == 'gmt') {
2960  $ret = time(); // Time for now at greenwich.
2961  } elseif ($mode == 'tzserver') { // Time for now with PHP server timezone added
2962  require_once DOL_DOCUMENT_ROOT.'/core/lib/date.lib.php';
2963  $tzsecond = getServerTimeZoneInt('now'); // Contains tz+dayling saving time
2964  $ret = (int) (dol_now('gmt') + ($tzsecond * 3600));
2965  //} elseif ($mode == 'tzref') {// Time for now with parent company timezone is added
2966  // require_once DOL_DOCUMENT_ROOT.'/core/lib/date.lib.php';
2967  // $tzsecond=getParentCompanyTimeZoneInt(); // Contains tz+dayling saving time
2968  // $ret=dol_now('gmt')+($tzsecond*3600);
2969  //}
2970  } elseif ($mode == 'tzuser' || $mode == 'tzuserrel') {
2971  // Time for now with user timezone added
2972  //print 'time: '.time();
2973  $offsettz = (empty($_SESSION['dol_tz']) ? 0 : $_SESSION['dol_tz']) * 60 * 60;
2974  $offsetdst = (empty($_SESSION['dol_dst']) ? 0 : $_SESSION['dol_dst']) * 60 * 60;
2975  $ret = (int) (dol_now('gmt') + ($offsettz + $offsetdst));
2976  }
2977 
2978  return $ret;
2979 }
2980 
2981 
2990 function dol_print_size($size, $shortvalue = 0, $shortunit = 0)
2991 {
2992  global $conf, $langs;
2993  $level = 1024;
2994 
2995  if (!empty($conf->dol_optimize_smallscreen)) {
2996  $shortunit = 1;
2997  }
2998 
2999  // Set value text
3000  if (empty($shortvalue) || $size < ($level * 10)) {
3001  $ret = $size;
3002  $textunitshort = $langs->trans("b");
3003  $textunitlong = $langs->trans("Bytes");
3004  } else {
3005  $ret = round($size / $level, 0);
3006  $textunitshort = $langs->trans("Kb");
3007  $textunitlong = $langs->trans("KiloBytes");
3008  }
3009  // Use long or short text unit
3010  if (empty($shortunit)) {
3011  $ret .= ' '.$textunitlong;
3012  } else {
3013  $ret .= ' '.$textunitshort;
3014  }
3015 
3016  return $ret;
3017 }
3018 
3028 function dol_print_url($url, $target = '_blank', $max = 32, $withpicto = 0)
3029 {
3030  global $langs;
3031 
3032  if (empty($url)) {
3033  return '';
3034  }
3035 
3036  $link = '<a href="';
3037  if (!preg_match('/^http/i', $url)) {
3038  $link .= 'http://';
3039  }
3040  $link .= $url;
3041  $link .= '"';
3042  if ($target) {
3043  $link .= ' target="'.$target.'"';
3044  }
3045  $link .= '>';
3046  if (!preg_match('/^http/i', $url)) {
3047  $link .= 'http://';
3048  }
3049  $link .= dol_trunc($url, $max);
3050  $link .= '</a>';
3051  return '<div class="nospan float" style="margin-right: 10px">'.($withpicto ?img_picto($langs->trans("Url"), 'globe').' ' : '').$link.'</div>';
3052 }
3053 
3066 function dol_print_email($email, $cid = 0, $socid = 0, $addlink = 0, $max = 64, $showinvalid = 1, $withpicto = 0)
3067 {
3068  global $conf, $user, $langs, $hookmanager;
3069 
3070  $newemail = dol_escape_htmltag($email);
3071 
3072  if (!empty($conf->global->MAIN_OPTIMIZEFORTEXTBROWSER) && $withpicto) {
3073  $withpicto = 0;
3074  }
3075 
3076  if (empty($email)) {
3077  return '&nbsp;';
3078  }
3079 
3080  if (!empty($addlink)) {
3081  $newemail = '<a style="text-overflow: ellipsis;" href="';
3082  if (!preg_match('/^mailto:/i', $email)) {
3083  $newemail .= 'mailto:';
3084  }
3085  $newemail .= $email;
3086  $newemail .= '">';
3087  $newemail .= dol_trunc($email, $max);
3088  $newemail .= '</a>';
3089  if ($showinvalid && !isValidEmail($email)) {
3090  $langs->load("errors");
3091  $newemail .= img_warning($langs->trans("ErrorBadEMail", $email));
3092  }
3093 
3094  if (($cid || $socid) && isModEnabled('agenda') && $user->hasRight("agenda", "myactions", "create")) {
3095  $type = 'AC_EMAIL';
3096  $link = '';
3097  if (!empty($conf->global->AGENDA_ADDACTIONFOREMAIL)) {
3098  $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>';
3099  }
3100  if ($link) {
3101  $newemail = '<div>'.$newemail.' '.$link.'</div>';
3102  }
3103  }
3104  } else {
3105  if ($showinvalid && !isValidEmail($email)) {
3106  $langs->load("errors");
3107  $newemail .= img_warning($langs->trans("ErrorBadEMail", $email));
3108  }
3109  }
3110 
3111  //$rep = '<div class="nospan" style="margin-right: 10px">';
3112  $rep = ($withpicto ? img_picto($langs->trans("EMail").' : '.$email, (is_numeric($withpicto) ? 'email' : $withpicto)).' ' : '').$newemail;
3113  //$rep .= '</div>';
3114  if ($hookmanager) {
3115  $parameters = array('cid' => $cid, 'socid' => $socid, 'addlink' => $addlink, 'picto' => $withpicto);
3116 
3117  $reshook = $hookmanager->executeHooks('printEmail', $parameters, $email);
3118  if ($reshook > 0) {
3119  $rep = '';
3120  }
3121  $rep .= $hookmanager->resPrint;
3122  }
3123 
3124  return $rep;
3125 }
3126 
3133 {
3134  global $conf, $db;
3135 
3136  $socialnetworks = array();
3137  // Enable caching of array
3138  require_once DOL_DOCUMENT_ROOT.'/core/lib/memory.lib.php';
3139  $cachekey = 'socialnetworks_' . $conf->entity;
3140  $dataretrieved = dol_getcache($cachekey);
3141  if (!is_null($dataretrieved)) {
3142  $socialnetworks = $dataretrieved;
3143  } else {
3144  $sql = "SELECT rowid, code, label, url, icon, active FROM ".MAIN_DB_PREFIX."c_socialnetworks";
3145  $sql .= " WHERE entity=".$conf->entity;
3146  $resql = $db->query($sql);
3147  if ($resql) {
3148  while ($obj = $db->fetch_object($resql)) {
3149  $socialnetworks[$obj->code] = array(
3150  'rowid' => $obj->rowid,
3151  'label' => $obj->label,
3152  'url' => $obj->url,
3153  'icon' => $obj->icon,
3154  'active' => $obj->active,
3155  );
3156  }
3157  }
3158  dol_setcache($cachekey, $socialnetworks); // If setting cache fails, this is not a problem, so we do not test result.
3159  }
3160 
3161  return $socialnetworks;
3162 }
3163 
3174 function dol_print_socialnetworks($value, $cid, $socid, $type, $dictsocialnetworks = array())
3175 {
3176  global $conf, $user, $langs;
3177 
3178  $htmllink = $value;
3179 
3180  if (empty($value)) {
3181  return '&nbsp;';
3182  }
3183 
3184  if (!empty($type)) {
3185  $htmllink = '<div class="divsocialnetwork inline-block valignmiddle">';
3186  // Use dictionary definition for picto $dictsocialnetworks[$type]['icon']
3187  $htmllink .= '<span class="fa paddingright '.($dictsocialnetworks[$type]['icon'] ? $dictsocialnetworks[$type]['icon'] : 'fa-link').'"></span>';
3188  if ($type == 'skype') {
3189  $htmllink .= dol_escape_htmltag($value);
3190  $htmllink .= '&nbsp;';
3191  $htmllink .= '<a href="skype:';
3192  $htmllink .= dol_string_nospecial($value, '_', '', array('@'));
3193  $htmllink .= '?call" alt="'.$langs->trans("Call").'&nbsp;'.$value.'" title="'.dol_escape_htmltag($langs->trans("Call").' '.$value).'">';
3194  $htmllink .= '<img src="'.DOL_URL_ROOT.'/theme/common/skype_callbutton.png" border="0">';
3195  $htmllink .= '</a><a href="skype:';
3196  $htmllink .= dol_string_nospecial($value, '_', '', array('@'));
3197  $htmllink .= '?chat" alt="'.$langs->trans("Chat").'&nbsp;'.$value.'" title="'.dol_escape_htmltag($langs->trans("Chat").' '.$value).'">';
3198  $htmllink .= '<img class="paddingleft" src="'.DOL_URL_ROOT.'/theme/common/skype_chatbutton.png" border="0">';
3199  $htmllink .= '</a>';
3200  if (($cid || $socid) && isModEnabled('agenda') && $user->rights->agenda->myactions->create) {
3201  $addlink = 'AC_SKYPE';
3202  $link = '';
3203  if (!empty($conf->global->AGENDA_ADDACTIONFORSKYPE)) {
3204  $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>';
3205  }
3206  $htmllink .= ($link ? ' '.$link : '');
3207  }
3208  } else {
3209  if (!empty($dictsocialnetworks[$type]['url'])) {
3210  $tmpvirginurl = preg_replace('/\/?{socialid}/', '', $dictsocialnetworks[$type]['url']);
3211  if ($tmpvirginurl) {
3212  $value = preg_replace('/^www\.'.preg_quote($tmpvirginurl, '/').'\/?/', '', $value);
3213  $value = preg_replace('/^'.preg_quote($tmpvirginurl, '/').'\/?/', '', $value);
3214 
3215  $tmpvirginurl3 = preg_replace('/^https:\/\//i', 'https://www.', $tmpvirginurl);
3216  if ($tmpvirginurl3) {
3217  $value = preg_replace('/^www\.'.preg_quote($tmpvirginurl3, '/').'\/?/', '', $value);
3218  $value = preg_replace('/^'.preg_quote($tmpvirginurl3, '/').'\/?/', '', $value);
3219  }
3220 
3221  $tmpvirginurl2 = preg_replace('/^https?:\/\//i', '', $tmpvirginurl);
3222  if ($tmpvirginurl2) {
3223  $value = preg_replace('/^www\.'.preg_quote($tmpvirginurl2, '/').'\/?/', '', $value);
3224  $value = preg_replace('/^'.preg_quote($tmpvirginurl2, '/').'\/?/', '', $value);
3225  }
3226  }
3227  $link = str_replace('{socialid}', $value, $dictsocialnetworks[$type]['url']);
3228  if (preg_match('/^https?:\/\//i', $link)) {
3229  $htmllink .= '&nbsp;<a href="'.dol_sanitizeUrl($link, 0).'" target="_blank" rel="noopener noreferrer">'.dol_escape_htmltag($value).'</a>';
3230  } else {
3231  $htmllink .= '&nbsp;<a href="'.dol_sanitizeUrl($link, 1).'" target="_blank" rel="noopener noreferrer">'.dol_escape_htmltag($value).'</a>';
3232  }
3233  } else {
3234  $htmllink .= dol_escape_htmltag($value);
3235  }
3236  }
3237  $htmllink .= '</div>';
3238  } else {
3239  $langs->load("errors");
3240  $htmllink .= img_warning($langs->trans("ErrorBadSocialNetworkValue", $value));
3241  }
3242  return $htmllink;
3243 }
3244 
3255 function dol_print_profids($profID, $profIDtype, $countrycode = '', $addcpButton = 1, $separ = '&nbsp;')
3256 {
3257  global $mysoc;
3258 
3259  if (empty($profID) || empty($profIDtype)) {
3260  return '';
3261  }
3262  if (empty($countrycode)) $countrycode = $mysoc->country_code;
3263  $newProfID = $profID;
3264  $id = substr($profIDtype, -1);
3265  $ret = '';
3266  if (strtoupper($countrycode) == 'FR') {
3267  // France
3268  if ($id == 1 && dol_strlen($newProfID) == 9) $newProfID = substr($newProfID, 0, 3).$separ.substr($newProfID, 3, 3).$separ.substr($newProfID, 6, 3);
3269  if ($id == 2 && dol_strlen($newProfID) == 14) $newProfID = substr($newProfID, 0, 3).$separ.substr($newProfID, 3, 3).$separ.substr($newProfID, 6, 3).$separ.substr($newProfID, 9, 5);
3270  if ($profIDtype === 'VAT' && dol_strlen($newProfID) == 13) $newProfID = substr($newProfID, 0, 4).$separ.substr($newProfID, 4, 3).$separ.substr($newProfID, 7, 3).$separ.substr($newProfID, 10, 3);
3271  }
3272  if (!empty($addcpButton)) $ret = showValueWithClipboardCPButton(dol_escape_htmltag($profID), ($addcpButton == 1 ? 1 : 0), $newProfID);
3273  else $ret = $newProfID;
3274  return $ret;
3275 }
3276 
3291 function dol_print_phone($phone, $countrycode = '', $cid = 0, $socid = 0, $addlink = '', $separ = "&nbsp;", $withpicto = '', $titlealt = '', $adddivfloat = 0)
3292 {
3293  global $conf, $user, $langs, $mysoc, $hookmanager;
3294 
3295  // Clean phone parameter
3296  $phone = is_null($phone) ? '' : preg_replace("/[\s.-]/", "", trim($phone));
3297  if (empty($phone)) {
3298  return '';
3299  }
3300  if (!empty($conf->global->MAIN_PHONE_SEPAR)) {
3301  $separ = $conf->global->MAIN_PHONE_SEPAR;
3302  }
3303  if (empty($countrycode) && is_object($mysoc)) {
3304  $countrycode = $mysoc->country_code;
3305  }
3306 
3307  // Short format for small screens
3308  if ($conf->dol_optimize_smallscreen) {
3309  $separ = '';
3310  }
3311 
3312  $newphone = $phone;
3313  if (strtoupper($countrycode) == "FR") {
3314  // France
3315  if (dol_strlen($phone) == 10) {
3316  $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);
3317  } elseif (dol_strlen($phone) == 7) {
3318  $newphone = substr($newphone, 0, 3).$separ.substr($newphone, 3, 2).$separ.substr($newphone, 5, 2);
3319  } elseif (dol_strlen($phone) == 9) {
3320  $newphone = substr($newphone, 0, 2).$separ.substr($newphone, 2, 3).$separ.substr($newphone, 5, 2).$separ.substr($newphone, 7, 2);
3321  } elseif (dol_strlen($phone) == 11) {
3322  $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);
3323  } elseif (dol_strlen($phone) == 12) {
3324  $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);
3325  } elseif (dol_strlen($phone) == 13) {
3326  $newphone = substr($newphone, 0, 4).$separ.substr($newphone, 4, 2).$separ.substr($newphone, 6, 2).$separ.substr($newphone, 8, 3).$separ.substr($newphone, 11, 2);
3327  }
3328  } elseif (strtoupper($countrycode) == "CA") {
3329  if (dol_strlen($phone) == 10) {
3330  $newphone = ($separ != '' ? '(' : '').substr($newphone, 0, 3).($separ != '' ? ')' : '').$separ.substr($newphone, 3, 3).($separ != '' ? '-' : '').substr($newphone, 6, 4);
3331  }
3332  } elseif (strtoupper($countrycode) == "PT") {//Portugal
3333  if (dol_strlen($phone) == 13) {//ex: +351_ABC_DEF_GHI
3334  $newphone = substr($newphone, 0, 4).$separ.substr($newphone, 4, 3).$separ.substr($newphone, 7, 3).$separ.substr($newphone, 10, 3);
3335  }
3336  } elseif (strtoupper($countrycode) == "SR") {//Suriname
3337  if (dol_strlen($phone) == 10) {//ex: +597_ABC_DEF
3338  $newphone = substr($newphone, 0, 4).$separ.substr($newphone, 4, 3).$separ.substr($newphone, 7, 3);
3339  } elseif (dol_strlen($phone) == 11) {//ex: +597_ABC_DEFG
3340  $newphone = substr($newphone, 0, 4).$separ.substr($newphone, 4, 3).$separ.substr($newphone, 7, 4);
3341  }
3342  } elseif (strtoupper($countrycode) == "DE") {//Allemagne
3343  if (dol_strlen($phone) == 14) {//ex: +49_ABCD_EFGH_IJK
3344  $newphone = substr($newphone, 0, 3).$separ.substr($newphone, 3, 4).$separ.substr($newphone, 7, 4).$separ.substr($newphone, 11, 3);
3345  } elseif (dol_strlen($phone) == 13) {//ex: +49_ABC_DEFG_HIJ
3346  $newphone = substr($newphone, 0, 3).$separ.substr($newphone, 3, 3).$separ.substr($newphone, 6, 4).$separ.substr($newphone, 10, 3);
3347  }
3348  } elseif (strtoupper($countrycode) == "ES") {//Espagne
3349  if (dol_strlen($phone) == 12) {//ex: +34_ABC_DEF_GHI
3350  $newphone = substr($newphone, 0, 3).$separ.substr($newphone, 3, 3).$separ.substr($newphone, 6, 3).$separ.substr($newphone, 9, 3);
3351  }
3352  } elseif (strtoupper($countrycode) == "BF") {// Burkina Faso
3353  if (dol_strlen($phone) == 12) {//ex : +22 A BC_DE_FG_HI
3354  $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);
3355  }
3356  } elseif (strtoupper($countrycode) == "RO") {// Roumanie
3357  if (dol_strlen($phone) == 12) {//ex : +40 AB_CDE_FG_HI
3358  $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);
3359  }
3360  } elseif (strtoupper($countrycode) == "TR") {//Turquie
3361  if (dol_strlen($phone) == 13) {//ex : +90 ABC_DEF_GHIJ
3362  $newphone = substr($newphone, 0, 3).$separ.substr($newphone, 3, 3).$separ.substr($newphone, 6, 3).$separ.substr($newphone, 9, 4);
3363  }
3364  } elseif (strtoupper($countrycode) == "US") {//Etat-Unis
3365  if (dol_strlen($phone) == 12) {//ex: +1 ABC_DEF_GHIJ
3366  $newphone = substr($newphone, 0, 2).$separ.substr($newphone, 2, 3).$separ.substr($newphone, 5, 3).$separ.substr($newphone, 8, 4);
3367  }
3368  } elseif (strtoupper($countrycode) == "MX") {//Mexique
3369  if (dol_strlen($phone) == 12) {//ex: +52 ABCD_EFG_HI
3370  $newphone = substr($newphone, 0, 3).$separ.substr($newphone, 3, 4).$separ.substr($newphone, 7, 3).$separ.substr($newphone, 10, 2);
3371  } elseif (dol_strlen($phone) == 11) {//ex: +52 AB_CD_EF_GH
3372  $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);
3373  } elseif (dol_strlen($phone) == 13) {//ex: +52 ABC_DEF_GHIJ
3374  $newphone = substr($newphone, 0, 3).$separ.substr($newphone, 3, 3).$separ.substr($newphone, 6, 3).$separ.substr($newphone, 9, 4);
3375  }
3376  } elseif (strtoupper($countrycode) == "ML") {//Mali
3377  if (dol_strlen($phone) == 12) {//ex: +223 AB_CD_EF_GH
3378  $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);
3379  }
3380  } elseif (strtoupper($countrycode) == "TH") {//Thaïlande
3381  if (dol_strlen($phone) == 11) {//ex: +66_ABC_DE_FGH
3382  $newphone = substr($newphone, 0, 3).$separ.substr($newphone, 3, 3).$separ.substr($newphone, 6, 2).$separ.substr($newphone, 8, 3);
3383  } elseif (dol_strlen($phone) == 12) {//ex: +66_A_BCD_EF_GHI
3384  $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);
3385  }
3386  } elseif (strtoupper($countrycode) == "MU") {
3387  //Maurice
3388  if (dol_strlen($phone) == 11) {//ex: +230_ABC_DE_FG
3389  $newphone = substr($newphone, 0, 4).$separ.substr($newphone, 4, 3).$separ.substr($newphone, 7, 2).$separ.substr($newphone, 9, 2);
3390  } elseif (dol_strlen($phone) == 12) {//ex: +230_ABCD_EF_GH
3391  $newphone = substr($newphone, 0, 4).$separ.substr($newphone, 4, 4).$separ.substr($newphone, 8, 2).$separ.substr($newphone, 10, 2);
3392  }
3393  } elseif (strtoupper($countrycode) == "ZA") {//Afrique du sud
3394  if (dol_strlen($phone) == 12) {//ex: +27_AB_CDE_FG_HI
3395  $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);
3396  }
3397  } elseif (strtoupper($countrycode) == "SY") {//Syrie
3398  if (dol_strlen($phone) == 12) {//ex: +963_AB_CD_EF_GH
3399  $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);
3400  } elseif (dol_strlen($phone) == 13) {//ex: +963_AB_CD_EF_GHI
3401  $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);
3402  }
3403  } elseif (strtoupper($countrycode) == "AE") {//Emirats Arabes Unis
3404  if (dol_strlen($phone) == 12) {//ex: +971_ABC_DEF_GH
3405  $newphone = substr($newphone, 0, 4).$separ.substr($newphone, 4, 3).$separ.substr($newphone, 7, 3).$separ.substr($newphone, 10, 2);
3406  } elseif (dol_strlen($phone) == 13) {//ex: +971_ABC_DEF_GHI
3407  $newphone = substr($newphone, 0, 4).$separ.substr($newphone, 4, 3).$separ.substr($newphone, 7, 3).$separ.substr($newphone, 10, 3);
3408  } elseif (dol_strlen($phone) == 14) {//ex: +971_ABC_DEF_GHIK
3409  $newphone = substr($newphone, 0, 4).$separ.substr($newphone, 4, 3).$separ.substr($newphone, 7, 3).$separ.substr($newphone, 10, 4);
3410  }
3411  } elseif (strtoupper($countrycode) == "DZ") {//Algérie
3412  if (dol_strlen($phone) == 13) {//ex: +213_ABC_DEF_GHI
3413  $newphone = substr($newphone, 0, 4).$separ.substr($newphone, 4, 3).$separ.substr($newphone, 7, 3).$separ.substr($newphone, 10, 3);
3414  }
3415  } elseif (strtoupper($countrycode) == "BE") {//Belgique
3416  if (dol_strlen($phone) == 11) {//ex: +32_ABC_DE_FGH
3417  $newphone = substr($newphone, 0, 3).$separ.substr($newphone, 3, 3).$separ.substr($newphone, 6, 2).$separ.substr($newphone, 8, 3);
3418  } elseif (dol_strlen($phone) == 12) {//ex: +32_ABC_DEF_GHI
3419  $newphone = substr($newphone, 0, 3).$separ.substr($newphone, 3, 3).$separ.substr($newphone, 6, 3).$separ.substr($newphone, 9, 3);
3420  }
3421  } elseif (strtoupper($countrycode) == "PF") {//Polynésie française
3422  if (dol_strlen($phone) == 12) {//ex: +689_AB_CD_EF_GH
3423  $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);
3424  }
3425  } elseif (strtoupper($countrycode) == "CO") {//Colombie
3426  if (dol_strlen($phone) == 13) {//ex: +57_ABC_DEF_GH_IJ
3427  $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);
3428  }
3429  } elseif (strtoupper($countrycode) == "JO") {//Jordanie
3430  if (dol_strlen($phone) == 12) {//ex: +962_A_BCD_EF_GH
3431  $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);
3432  }
3433  } elseif (strtoupper($countrycode) == "JM") {//Jamaïque
3434  if (dol_strlen($newphone) == 12) {//ex: +1867_ABC_DEFG
3435  $newphone = substr($newphone, 0, 5).$separ.substr($newphone, 5, 3).$separ.substr($newphone, 8, 4);
3436  }
3437  } elseif (strtoupper($countrycode) == "MG") {//Madagascar
3438  if (dol_strlen($phone) == 13) {//ex: +261_AB_CD_EFG_HI
3439  $newphone = substr($newphone, 0, 4).$separ.substr($newphone, 4, 2).$separ.substr($newphone, 6, 2).$separ.substr($newphone, 8, 3).$separ.substr($newphone, 11, 2);
3440  }
3441  } elseif (strtoupper($countrycode) == "GB") {//Royaume uni
3442  if (dol_strlen($phone) == 13) {//ex: +44_ABCD_EFG_HIJ
3443  $newphone = substr($newphone, 0, 3).$separ.substr($newphone, 3, 4).$separ.substr($newphone, 7, 3).$separ.substr($newphone, 10, 3);
3444  }
3445  } elseif (strtoupper($countrycode) == "CH") {//Suisse
3446  if (dol_strlen($phone) == 12) {//ex: +41_AB_CDE_FG_HI
3447  $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);
3448  } elseif (dol_strlen($phone) == 15) {// +41_AB_CDE_FGH_IJKL
3449  $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);
3450  }
3451  } elseif (strtoupper($countrycode) == "TN") {//Tunisie
3452  if (dol_strlen($phone) == 12) {//ex: +216_AB_CDE_FGH
3453  $newphone = substr($newphone, 0, 4).$separ.substr($newphone, 4, 2).$separ.substr($newphone, 6, 3).$separ.substr($newphone, 9, 3);
3454  }
3455  } elseif (strtoupper($countrycode) == "GF") {//Guyane francaise
3456  if (dol_strlen($phone) == 13) {//ex: +594_ABC_DE_FG_HI (ABC=594 de nouveau)
3457  $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);
3458  }
3459  } elseif (strtoupper($countrycode) == "GP") {//Guadeloupe
3460  if (dol_strlen($phone) == 13) {//ex: +590_ABC_DE_FG_HI (ABC=590 de nouveau)
3461  $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);
3462  }
3463  } elseif (strtoupper($countrycode) == "MQ") {//Martinique
3464  if (dol_strlen($phone) == 13) {//ex: +596_ABC_DE_FG_HI (ABC=596 de nouveau)
3465  $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);
3466  }
3467  } elseif (strtoupper($countrycode) == "IT") {//Italie
3468  if (dol_strlen($phone) == 12) {//ex: +39_ABC_DEF_GHI
3469  $newphone = substr($newphone, 0, 3).$separ.substr($newphone, 3, 3).$separ.substr($newphone, 6, 3).$separ.substr($newphone, 9, 3);
3470  } elseif (dol_strlen($phone) == 13) {//ex: +39_ABC_DEF_GH_IJ
3471  $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);
3472  }
3473  } elseif (strtoupper($countrycode) == "AU") {
3474  //Australie
3475  if (dol_strlen($phone) == 12) {
3476  //ex: +61_A_BCDE_FGHI
3477  $newphone = substr($newphone, 0, 3).$separ.substr($newphone, 3, 1).$separ.substr($newphone, 4, 4).$separ.substr($newphone, 8, 4);
3478  }
3479  } elseif (strtoupper($countrycode) == "LU") {
3480  // Luxembourg
3481  if (dol_strlen($phone) == 10) {// fixe 6 chiffres +352_AA_BB_CC
3482  $newphone = substr($newphone, 0, 4).$separ.substr($newphone, 4, 2).$separ.substr($newphone, 6, 2).$separ.substr($newphone, 8, 2);
3483  } elseif (dol_strlen($phone) == 11) {// fixe 7 chiffres +352_AA_BB_CC_D
3484  $newphone = substr($newphone, 0, 4).$separ.substr($newphone, 4, 2).$separ.substr($newphone, 6, 2).$separ.substr($newphone, 8, 2).$separ.substr($newphone, 10, 1);
3485  } elseif (dol_strlen($phone) == 12) {// fixe 8 chiffres +352_AA_BB_CC_DD
3486  $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);
3487  } elseif (dol_strlen($phone) == 13) {// mobile +352_AAA_BB_CC_DD
3488  $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);
3489  }
3490  }
3491  if (!empty($addlink)) { // Link on phone number (+ link to add action if conf->global->AGENDA_ADDACTIONFORPHONE set)
3492  if ($conf->browser->layout == 'phone' || (isModEnabled('clicktodial') && !empty($conf->global->CLICKTODIAL_USE_TEL_LINK_ON_PHONE_NUMBERS))) { // If phone or option for, we use link of phone
3493  $newphoneform = $newphone;
3494  $newphone = '<a href="tel:'.$phone.'"';
3495  $newphone .= '>'.$newphoneform.'</a>';
3496  } elseif (isModEnabled('clicktodial') && $addlink == 'AC_TEL') { // If click to dial, we use click to dial url
3497  if (empty($user->clicktodial_loaded)) {
3498  $user->fetch_clicktodial();
3499  }
3500 
3501  // Define urlmask
3502  $urlmask = 'ErrorClickToDialModuleNotConfigured';
3503  if (!empty($conf->global->CLICKTODIAL_URL)) {
3504  $urlmask = $conf->global->CLICKTODIAL_URL;
3505  }
3506  if (!empty($user->clicktodial_url)) {
3507  $urlmask = $user->clicktodial_url;
3508  }
3509 
3510  $clicktodial_poste = (!empty($user->clicktodial_poste) ?urlencode($user->clicktodial_poste) : '');
3511  $clicktodial_login = (!empty($user->clicktodial_login) ?urlencode($user->clicktodial_login) : '');
3512  $clicktodial_password = (!empty($user->clicktodial_password) ?urlencode($user->clicktodial_password) : '');
3513  // This line is for backward compatibility
3514  $url = sprintf($urlmask, urlencode($phone), $clicktodial_poste, $clicktodial_login, $clicktodial_password);
3515  // Thoose lines are for substitution
3516  $substitarray = array('__PHONEFROM__'=>$clicktodial_poste,
3517  '__PHONETO__'=>urlencode($phone),
3518  '__LOGIN__'=>$clicktodial_login,
3519  '__PASS__'=>$clicktodial_password);
3520  $url = make_substitutions($url, $substitarray);
3521  $newphonesav = $newphone;
3522  if (empty($conf->global->CLICKTODIAL_DO_NOT_USE_AJAX_CALL)) {
3523  // Default and recommended: New method using ajax without submiting a page making a javascript history.go(-1) back
3524  $newphone = '<a href="'.$url.'" class="cssforclicktodial"'; // Call of ajax is handled by the lib_foot.js.php on class 'cssforclicktodial'
3525  $newphone .= '>'.$newphonesav.'</a>';
3526  } else {
3527  // Old method
3528  $newphone = '<a href="'.$url.'"';
3529  if (!empty($conf->global->CLICKTODIAL_FORCENEWTARGET)) {
3530  $newphone .= ' target="_blank" rel="noopener noreferrer"';
3531  }
3532  $newphone .= '>'.$newphonesav.'</a>';
3533  }
3534  }
3535 
3536  //if (($cid || $socid) && !empty($conf->agenda->enabled) && $user->rights->agenda->myactions->create)
3537  if (isModEnabled('agenda') && $user->hasRight("agenda", "myactions", "create")) {
3538  $type = 'AC_TEL';
3539  $link = '';
3540  if ($addlink == 'AC_FAX') {
3541  $type = 'AC_FAX';
3542  }
3543  if (!empty($conf->global->AGENDA_ADDACTIONFORPHONE)) {
3544  $link = '<a href="'.DOL_URL_ROOT.'/comm/action/card.php?action=create&amp;backtopage='. urlencode($_SERVER['REQUEST_URI']) .'&amp;actioncode='.$type.($cid ? '&amp;contactid='.$cid : '').($socid ? '&amp;socid='.$socid : '').'">'.img_object($langs->trans("AddAction"), "calendar").'</a>';
3545  }
3546  if ($link) {
3547  $newphone = '<div>'.$newphone.' '.$link.'</div>';
3548  }
3549  }
3550  }
3551 
3552  if (empty($titlealt)) {
3553  $titlealt = ($withpicto == 'fax' ? $langs->trans("Fax") : $langs->trans("Phone"));
3554  }
3555  $rep = '';
3556 
3557  if ($hookmanager) {
3558  $parameters = array('countrycode' => $countrycode, 'cid' => $cid, 'socid' => $socid, 'titlealt' => $titlealt, 'picto' => $withpicto);
3559  $reshook = $hookmanager->executeHooks('printPhone', $parameters, $phone);
3560  $rep .= $hookmanager->resPrint;
3561  }
3562  if (empty($reshook)) {
3563  $picto = '';
3564  if ($withpicto) {
3565  if ($withpicto == 'fax') {
3566  $picto = 'phoning_fax';
3567  } elseif ($withpicto == 'phone') {
3568  $picto = 'phoning';
3569  } elseif ($withpicto == 'mobile') {
3570  $picto = 'phoning_mobile';
3571  } else {
3572  $picto = '';
3573  }
3574  }
3575  if ($adddivfloat == 1) {
3576  $rep .= '<div class="nospan float" style="margin-right: 10px">';
3577  } elseif (empty($adddivfloat)) {
3578  $rep .= '<span style="margin-right: 10px;">';
3579  }
3580  $rep .= ($withpicto ?img_picto($titlealt, 'object_'.$picto.'.png').' ' : '').$newphone;
3581  if ($adddivfloat == 1) {
3582  $rep .= '</div>';
3583  } elseif (empty($adddivfloat)) {
3584  $rep .= '</span>';
3585  }
3586  }
3587 
3588  return $rep;
3589 }
3590 
3598 function dol_print_ip($ip, $mode = 0)
3599 {
3600  global $conf, $langs;
3601 
3602  $ret = '';
3603 
3604  if (empty($mode)) {
3605  $ret .= $ip;
3606  }
3607 
3608  if ($mode != 2) {
3609  $countrycode = dolGetCountryCodeFromIp($ip);
3610  if ($countrycode) { // If success, countrycode is us, fr, ...
3611  if (file_exists(DOL_DOCUMENT_ROOT.'/theme/common/flags/'.$countrycode.'.png')) {
3612  $ret .= ' '.img_picto($countrycode.' '.$langs->trans("AccordingToGeoIPDatabase"), DOL_URL_ROOT.'/theme/common/flags/'.$countrycode.'.png', '', 1);
3613  } else {
3614  $ret .= ' ('.$countrycode.')';
3615  }
3616  } else {
3617  // Nothing
3618  }
3619  }
3620 
3621  return $ret;
3622 }
3623 
3633 {
3634  if (empty($_SERVER['HTTP_X_FORWARDED_FOR']) || preg_match('/[^0-9\.\:,\[\]]/', $_SERVER['HTTP_X_FORWARDED_FOR'])) {
3635  if (empty($_SERVER['HTTP_CLIENT_IP']) || preg_match('/[^0-9\.\:,\[\]]/', $_SERVER['HTTP_CLIENT_IP'])) {
3636  if (empty($_SERVER["HTTP_CF_CONNECTING_IP"])) {
3637  $ip = (empty($_SERVER['REMOTE_ADDR']) ? '' : $_SERVER['REMOTE_ADDR']); // value may have been the IP of the proxy and not the client
3638  } else {
3639  $ip = $_SERVER["HTTP_CF_CONNECTING_IP"]; // value here may have been forged by client
3640  }
3641  } else {
3642  $ip = $_SERVER['HTTP_CLIENT_IP']; // value is clean here but may have been forged by proxy
3643  }
3644  } else {
3645  $ip = $_SERVER['HTTP_X_FORWARDED_FOR']; // value is clean here but may have been forged by proxy
3646  }
3647  return $ip;
3648 }
3649 
3658 function isHTTPS()
3659 {
3660  $isSecure = false;
3661  if (isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] == 'on') {
3662  $isSecure = true;
3663  } elseif (!empty($_SERVER['HTTP_X_FORWARDED_PROTO']) && $_SERVER['HTTP_X_FORWARDED_PROTO'] == 'https' || !empty($_SERVER['HTTP_X_FORWARDED_SSL']) && $_SERVER['HTTP_X_FORWARDED_SSL'] == 'on') {
3664  $isSecure = true;
3665  }
3666  return $isSecure;
3667 }
3668 
3676 {
3677  global $conf;
3678 
3679  $countrycode = '';
3680 
3681  if (!empty($conf->geoipmaxmind->enabled)) {
3682  $datafile = getDolGlobalString('GEOIPMAXMIND_COUNTRY_DATAFILE');
3683  //$ip='24.24.24.24';
3684  //$datafile='/usr/share/GeoIP/GeoIP.dat'; Note that this must be downloaded datafile (not same than datafile provided with ubuntu packages)
3685  include_once DOL_DOCUMENT_ROOT.'/core/class/dolgeoip.class.php';
3686  $geoip = new DolGeoIP('country', $datafile);
3687  //print 'ip='.$ip.' databaseType='.$geoip->gi->databaseType." GEOIP_CITY_EDITION_REV1=".GEOIP_CITY_EDITION_REV1."\n";
3688  $countrycode = $geoip->getCountryCodeFromIP($ip);
3689  }
3690 
3691  return $countrycode;
3692 }
3693 
3694 
3702 {
3703  global $conf, $langs, $user;
3704 
3705  //$ret=$user->xxx;
3706  $ret = '';
3707  if (!empty($conf->geoipmaxmind->enabled)) {
3708  $ip = getUserRemoteIP();
3709  $datafile = getDolGlobalString('GEOIPMAXMIND_COUNTRY_DATAFILE');
3710  //$ip='24.24.24.24';
3711  //$datafile='E:\Mes Sites\Web\Admin1\awstats\maxmind\GeoIP.dat';
3712  include_once DOL_DOCUMENT_ROOT.'/core/class/dolgeoip.class.php';
3713  $geoip = new DolGeoIP('country', $datafile);
3714  $countrycode = $geoip->getCountryCodeFromIP($ip);
3715  $ret = $countrycode;
3716  }
3717  return $ret;
3718 }
3719 
3732 function dol_print_address($address, $htmlid, $element, $id, $noprint = 0, $charfornl = '')
3733 {
3734  global $conf, $user, $langs, $hookmanager;
3735 
3736  $out = '';
3737 
3738  if ($address) {
3739  if ($hookmanager) {
3740  $parameters = array('element' => $element, 'id' => $id);
3741  $reshook = $hookmanager->executeHooks('printAddress', $parameters, $address);
3742  $out .= $hookmanager->resPrint;
3743  }
3744  if (empty($reshook)) {
3745  if (empty($charfornl)) {
3746  $out .= nl2br($address);
3747  } else {
3748  $out .= preg_replace('/[\r\n]+/', $charfornl, $address);
3749  }
3750 
3751  // TODO Remove this block, we can add this using the hook now
3752  $showgmap = $showomap = 0;
3753  if (($element == 'thirdparty' || $element == 'societe') && !empty($conf->google->enabled) && !empty($conf->global->GOOGLE_ENABLE_GMAPS)) {
3754  $showgmap = 1;
3755  }
3756  if ($element == 'contact' && !empty($conf->google->enabled) && !empty($conf->global->GOOGLE_ENABLE_GMAPS_CONTACTS)) {
3757  $showgmap = 1;
3758  }
3759  if ($element == 'member' && !empty($conf->google->enabled) && !empty($conf->global->GOOGLE_ENABLE_GMAPS_MEMBERS)) {
3760  $showgmap = 1;
3761  }
3762  if (($element == 'thirdparty' || $element == 'societe') && !empty($conf->openstreetmap->enabled) && !empty($conf->global->OPENSTREETMAP_ENABLE_MAPS)) {
3763  $showomap = 1;
3764  }
3765  if ($element == 'contact' && !empty($conf->openstreetmap->enabled) && !empty($conf->global->OPENSTREETMAP_ENABLE_MAPS_CONTACTS)) {
3766  $showomap = 1;
3767  }
3768  if ($element == 'member' && !empty($conf->openstreetmap->enabled) && !empty($conf->global->OPENSTREETMAP_ENABLE_MAPS_MEMBERS)) {
3769  $showomap = 1;
3770  }
3771  if ($showgmap) {
3772  $url = dol_buildpath('/google/gmaps.php?mode='.$element.'&id='.$id, 1);
3773  $out .= ' <a href="'.$url.'" target="_gmaps"><img id="'.$htmlid.'" class="valigntextbottom" src="'.DOL_URL_ROOT.'/theme/common/gmap.png"></a>';
3774  }
3775  if ($showomap) {
3776  $url = dol_buildpath('/openstreetmap/maps.php?mode='.$element.'&id='.$id, 1);
3777  $out .= ' <a href="'.$url.'" target="_gmaps"><img id="'.$htmlid.'_openstreetmap" class="valigntextbottom" src="'.DOL_URL_ROOT.'/theme/common/gmap.png"></a>';
3778  }
3779  }
3780  }
3781  if ($noprint) {
3782  return $out;
3783  } else {
3784  print $out;
3785  }
3786 }
3787 
3788 
3798 function isValidEmail($address, $acceptsupervisorkey = 0, $acceptuserkey = 0)
3799 {
3800  if ($acceptsupervisorkey && $address == '__SUPERVISOREMAIL__') {
3801  return true;
3802  }
3803  if ($acceptuserkey && $address == '__USER_EMAIL__') {
3804  return true;
3805  }
3806  if (filter_var($address, FILTER_VALIDATE_EMAIL)) {
3807  return true;
3808  }
3809 
3810  return false;
3811 }
3812 
3821 function isValidMXRecord($domain)
3822 {
3823  if (function_exists('idn_to_ascii') && function_exists('checkdnsrr')) {
3824  if (!checkdnsrr(idn_to_ascii($domain), 'MX')) {
3825  return 0;
3826  }
3827  if (function_exists('getmxrr')) {
3828  $mxhosts = array();
3829  $weight = array();
3830  getmxrr(idn_to_ascii($domain), $mxhosts, $weight);
3831  if (count($mxhosts) > 1) {
3832  return 1;
3833  }
3834  if (count($mxhosts) == 1 && !empty($mxhosts[0])) {
3835  return 1;
3836  }
3837 
3838  return 0;
3839  }
3840  }
3841 
3842  // function idn_to_ascii or checkdnsrr does not exists
3843  return -1;
3844 }
3845 
3853 function isValidPhone($phone)
3854 {
3855  return true;
3856 }
3857 
3858 
3868 function dolGetFirstLetters($s, $nbofchar = 1)
3869 {
3870  $ret = '';
3871  $tmparray = explode(' ', $s);
3872  foreach ($tmparray as $tmps) {
3873  $ret .= dol_substr($tmps, 0, $nbofchar);
3874  }
3875 
3876  return $ret;
3877 }
3878 
3879 
3887 function dol_strlen($string, $stringencoding = 'UTF-8')
3888 {
3889  if (is_null($string)) {
3890  return 0;
3891  }
3892 
3893  if (function_exists('mb_strlen')) {
3894  return mb_strlen($string, $stringencoding);
3895  } else {
3896  return strlen($string);
3897  }
3898 }
3899 
3910 function dol_substr($string, $start, $length, $stringencoding = '', $trunconbytes = 0)
3911 {
3912  global $langs;
3913 
3914  if (empty($stringencoding)) {
3915  $stringencoding = $langs->charset_output;
3916  }
3917 
3918  $ret = '';
3919  if (empty($trunconbytes)) {
3920  if (function_exists('mb_substr')) {
3921  $ret = mb_substr($string, $start, $length, $stringencoding);
3922  } else {
3923  $ret = substr($string, $start, $length);
3924  }
3925  } else {
3926  if (function_exists('mb_strcut')) {
3927  $ret = mb_strcut($string, $start, $length, $stringencoding);
3928  } else {
3929  $ret = substr($string, $start, $length);
3930  }
3931  }
3932  return $ret;
3933 }
3934 
3935 
3949 function dol_trunc($string, $size = 40, $trunc = 'right', $stringencoding = 'UTF-8', $nodot = 0, $display = 0)
3950 {
3951  global $conf;
3952 
3953  if (empty($size) || !empty($conf->global->MAIN_DISABLE_TRUNC)) {
3954  return $string;
3955  }
3956 
3957  if (empty($stringencoding)) {
3958  $stringencoding = 'UTF-8';
3959  }
3960  // reduce for small screen
3961  if ($conf->dol_optimize_smallscreen == 1 && $display == 1) {
3962  $size = round($size / 3);
3963  }
3964 
3965  // We go always here
3966  if ($trunc == 'right') {
3967  $newstring = dol_textishtml($string) ? dol_string_nohtmltag($string, 1) : $string;
3968  if (dol_strlen($newstring, $stringencoding) > ($size + ($nodot ? 0 : 1))) {
3969  // If nodot is 0 and size is 1 chars more, we don't trunc and don't add …
3970  return dol_substr($newstring, 0, $size, $stringencoding).($nodot ? '' : '…');
3971  } else {
3972  //return 'u'.$size.'-'.$newstring.'-'.dol_strlen($newstring,$stringencoding).'-'.$string;
3973  return $string;
3974  }
3975  } elseif ($trunc == 'middle') {
3976  $newstring = dol_textishtml($string) ? dol_string_nohtmltag($string, 1) : $string;
3977  if (dol_strlen($newstring, $stringencoding) > 2 && dol_strlen($newstring, $stringencoding) > ($size + 1)) {
3978  $size1 = round($size / 2);
3979  $size2 = round($size / 2);
3980  return dol_substr($newstring, 0, $size1, $stringencoding).'…'.dol_substr($newstring, dol_strlen($newstring, $stringencoding) - $size2, $size2, $stringencoding);
3981  } else {
3982  return $string;
3983  }
3984  } elseif ($trunc == 'left') {
3985  $newstring = dol_textishtml($string) ? dol_string_nohtmltag($string, 1) : $string;
3986  if (dol_strlen($newstring, $stringencoding) > ($size + ($nodot ? 0 : 1))) {
3987  // If nodot is 0 and size is 1 chars more, we don't trunc and don't add …
3988  return '…'.dol_substr($newstring, dol_strlen($newstring, $stringencoding) - $size, $size, $stringencoding);
3989  } else {
3990  return $string;
3991  }
3992  } elseif ($trunc == 'wrap') {
3993  $newstring = dol_textishtml($string) ? dol_string_nohtmltag($string, 1) : $string;
3994  if (dol_strlen($newstring, $stringencoding) > ($size + 1)) {
3995  return dol_substr($newstring, 0, $size, $stringencoding)."\n".dol_trunc(dol_substr($newstring, $size, dol_strlen($newstring, $stringencoding) - $size, $stringencoding), $size, $trunc);
3996  } else {
3997  return $string;
3998  }
3999  } else {
4000  return 'BadParam3CallingDolTrunc';
4001  }
4002 }
4003 
4024 function img_picto($titlealt, $picto, $moreatt = '', $pictoisfullpath = false, $srconly = 0, $notitle = 0, $alt = '', $morecss = '', $marginleftonlyshort = 2)
4025 {
4026  global $conf, $langs;
4027 
4028  // We forge fullpathpicto for image to $path/img/$picto. By default, we take DOL_URL_ROOT/theme/$conf->theme/img/$picto
4029  $url = DOL_URL_ROOT;
4030  $theme = isset($conf->theme) ? $conf->theme : null;
4031  $path = 'theme/'.$theme;
4032  // Define fullpathpicto to use into src
4033  if ($pictoisfullpath) {
4034  // Clean parameters
4035  if (!preg_match('/(\.png|\.gif|\.svg)$/i', $picto)) {
4036  $picto .= '.png';
4037  }
4038  $fullpathpicto = $picto;
4039  $reg = array();
4040  if (preg_match('/class="([^"]+)"/', $moreatt, $reg)) {
4041  $morecss .= ($morecss ? ' ' : '').$reg[1];
4042  $moreatt = str_replace('class="'.$reg[1].'"', '', $moreatt);
4043  }
4044  } else {
4045  $pictowithouttext = preg_replace('/(\.png|\.gif|\.svg)$/', '', $picto);
4046  $pictowithouttext = str_replace('object_', '', $pictowithouttext);
4047 
4048  if (strpos($pictowithouttext, 'fontawesome_') !== false || preg_match('/^fa-/', $pictowithouttext)) {
4049  // This is a font awesome image 'fonwtawesome_xxx' or 'fa-xxx'
4050  $pictowithouttext = str_replace('fontawesome_', '', $pictowithouttext);
4051  $pictowithouttext = str_replace('fa-', '', $pictowithouttext);
4052 
4053  $pictowithouttextarray = explode('_', $pictowithouttext);
4054  $marginleftonlyshort = 0;
4055 
4056  if (!empty($pictowithouttextarray[1])) {
4057  // Syntax is 'fontawesome_fakey_faprefix_facolor_fasize' or 'fa-fakey_faprefix_facolor_fasize'
4058  $fakey = 'fa-'.$pictowithouttextarray[0];
4059  $fa = empty($pictowithouttextarray[1]) ? 'fa' : $pictowithouttextarray[1];
4060  $facolor = empty($pictowithouttextarray[2]) ? '' : $pictowithouttextarray[2];
4061  $fasize = empty($pictowithouttextarray[3]) ? '' : $pictowithouttextarray[3];
4062  } else {
4063  $fakey = 'fa-'.$pictowithouttext;
4064  $fa = 'fa';
4065  $facolor = '';
4066  $fasize = '';
4067  }
4068 
4069  // This snippet only needed since function img_edit accepts only one additional parameter: no separate one for css only.
4070  // class/style need to be extracted to avoid duplicate class/style validation errors when $moreatt is added to the end of the attributes.
4071  $morestyle = '';
4072  $reg = array();
4073  if (preg_match('/class="([^"]+)"/', $moreatt, $reg)) {
4074  $morecss .= ($morecss ? ' ' : '').$reg[1];
4075  $moreatt = str_replace('class="'.$reg[1].'"', '', $moreatt);
4076  }
4077  if (preg_match('/style="([^"]+)"/', $moreatt, $reg)) {
4078  $morestyle = $reg[1];
4079  $moreatt = str_replace('style="'.$reg[1].'"', '', $moreatt);
4080  }
4081  $moreatt = trim($moreatt);
4082 
4083  $enabledisablehtml = '<span class="'.$fa.' '.$fakey.($marginleftonlyshort ? ($marginleftonlyshort == 1 ? ' marginleftonlyshort' : ' marginleftonly') : '');
4084  $enabledisablehtml .= ($morecss ? ' '.$morecss : '').'" style="'.($fasize ? ('font-size: '.$fasize.';') : '').($facolor ? (' color: '.$facolor.';') : '').($morestyle ? ' '.$morestyle : '').'"'.(($notitle || empty($titlealt)) ? '' : ' title="'.dol_escape_htmltag($titlealt).'"').($moreatt ? ' '.$moreatt : '').'>';
4085  /*if (!empty($conf->global->MAIN_OPTIMIZEFORTEXTBROWSER)) {
4086  $enabledisablehtml .= $titlealt;
4087  }*/
4088  $enabledisablehtml .= '</span>';
4089 
4090  return $enabledisablehtml;
4091  }
4092 
4093  if (empty($srconly) && in_array($pictowithouttext, array(
4094  '1downarrow', '1uparrow', '1leftarrow', '1rightarrow', '1uparrow_selected', '1downarrow_selected', '1leftarrow_selected', '1rightarrow_selected',
4095  'accountancy', 'accounting_account', 'account', 'accountline', 'action', 'add', 'address', 'angle-double-down', 'angle-double-up', 'asset',
4096  'bank_account', 'barcode', 'bank', 'bell', 'bill', 'billa', 'billr', 'billd', 'bookmark', 'bom', 'briefcase-medical', 'bug', 'building',
4097  'card', 'calendarlist', 'calendar', 'calendarmonth', 'calendarweek', 'calendarday', 'calendarperuser', 'calendarpertype',
4098  'cash-register', 'category', 'chart', 'check', 'clock', 'close_title', 'cog', 'collab', 'company', 'contact', 'country', 'contract', 'conversation', 'cron', 'cubes',
4099  'currency', 'multicurrency',
4100  'delete', 'dolly', 'dollyrevert', 'donation', 'download', 'dynamicprice',
4101  'edit', 'ellipsis-h', 'email', 'entity', 'envelope', 'eraser', 'establishment', 'expensereport', 'external-link-alt', 'external-link-square-alt', 'eye',
4102  'filter', 'file-code', 'file-export', 'file-import', 'file-upload', 'autofill', 'folder', 'folder-open', 'folder-plus',
4103  'gears', 'generate', 'globe', 'globe-americas', 'graph', 'grip', 'grip_title', 'group',
4104  'help', 'holiday',
4105  'images', 'incoterm', 'info', 'intervention', 'inventory', 'intracommreport', 'jobprofile',
4106  'knowledgemanagement',
4107  'label', 'language', 'line', 'link', 'list', 'list-alt', 'listlight', 'loan', 'lock', 'lot', 'long-arrow-alt-right',
4108  'margin', 'map-marker-alt', 'member', 'meeting', 'money-bill-alt', 'movement', 'mrp', 'note', 'next',
4109  'off', 'on', 'order',
4110  'paiment', 'paragraph', 'play', 'pdf', 'phone', 'phoning', 'phoning_mobile', 'phoning_fax', 'playdisabled', 'previous', 'poll', 'pos', 'printer', 'product', 'propal', 'proposal', 'puce',
4111  'stock', 'resize', 'service', 'stats', 'trip',
4112  'security', 'setup', 'share-alt', 'sign-out', 'split', 'stripe', 'stripe-s', 'switch_off', 'switch_on', 'switch_on_red', 'tools', 'unlink', 'uparrow', 'user', 'user-tie', 'vcard', 'wrench',
4113  'github', 'google', 'jabber', 'microsoft', 'skype', 'twitter', 'facebook', 'linkedin', 'instagram', 'snapchat', 'youtube', 'google-plus-g', 'whatsapp',
4114  'chevron-left', 'chevron-right', 'chevron-down', 'chevron-top', 'commercial', 'companies',
4115  'generic', 'home', 'hrm', 'members', 'products', 'invoicing',
4116  'partnership', 'payment', 'payment_vat', 'pencil-ruler', 'pictoconfirm', 'preview', 'project', 'projectpub', 'projecttask', 'question', 'refresh', 'region',
4117  'salary', 'shipment', 'state', 'supplier_invoice', 'supplier_invoicea', 'supplier_invoicer', 'supplier_invoiced',
4118  'technic', 'ticket',
4119  'error', 'warning',
4120  'recent', 'reception', 'recruitmentcandidature', 'recruitmentjobposition', 'replacement', 'resource', 'recurring','rss',
4121  'shapes', 'skill', 'square', 'stop-circle', 'supplier', 'supplier_proposal', 'supplier_order', 'supplier_invoice',
4122  'timespent', 'title_setup', 'title_accountancy', 'title_bank', 'title_hrm', 'title_agenda',
4123  'uncheck', 'url', 'user-cog', 'user-injured', 'user-md', 'vat', 'website', 'workstation', 'webhook', 'world', 'private',
4124  'conferenceorbooth', 'eventorganization',
4125  'stamp', 'signature'
4126  ))) {
4127  $fakey = $pictowithouttext;
4128  $facolor = '';
4129  $fasize = '';
4130  $fa = 'fas';
4131  if (in_array($pictowithouttext, array('card', 'bell', 'clock', 'establishment', 'generic', 'minus-square', 'object_generic', 'pdf', 'plus-square', 'timespent', 'note', 'off', 'on', 'object_bookmark', 'bookmark', 'vcard'))) {
4132  $fa = 'far';
4133  }
4134  if (in_array($pictowithouttext, array('black-tie', 'github', 'google', 'microsoft', 'skype', 'twitter', 'facebook', 'linkedin', 'instagram', 'snapchat', 'stripe', 'stripe-s', 'youtube', 'google-plus-g', 'whatsapp'))) {
4135  $fa = 'fab';
4136  }
4137 
4138  $arrayconvpictotofa = array(
4139  'account'=>'university', 'accounting_account'=>'clipboard-list', 'accountline'=>'receipt', 'accountancy'=>'search-dollar', 'action'=>'calendar-alt', 'add'=>'plus-circle', 'address'=> 'address-book', 'asset'=>'money-check-alt', 'autofill'=>'fill',
4140  'bank_account'=>'university',
4141  'bill'=>'file-invoice-dollar', 'billa'=>'file-excel', 'billr'=>'file-invoice-dollar', 'billd'=>'file-medical',
4142  'supplier_invoice'=>'file-invoice-dollar', 'supplier_invoicea'=>'file-excel', 'supplier_invoicer'=>'file-invoice-dollar', 'supplier_invoiced'=>'file-medical',
4143  'bom'=>'shapes',
4144  'card'=>'address-card', 'chart'=>'chart-line', 'company'=>'building', 'contact'=>'address-book', 'contract'=>'suitcase', 'collab'=>'people-arrows', 'conversation'=>'comments', 'country'=>'globe-americas', 'cron'=>'business-time',
4145  'donation'=>'file-alt', 'dynamicprice'=>'hand-holding-usd',
4146  'setup'=>'cog', 'companies'=>'building', 'products'=>'cube', 'commercial'=>'suitcase', 'invoicing'=>'coins',
4147  'accounting'=>'search-dollar', 'category'=>'tag', 'dollyrevert'=>'dolly',
4148  'generate'=>'plus-square', 'hrm'=>'user-tie', 'incoterm'=>'truck-loading',
4149  'margin'=>'calculator', 'members'=>'user-friends', 'ticket'=>'ticket-alt', 'globe'=>'external-link-alt', 'lot'=>'barcode',
4150  'email'=>'at', 'establishment'=>'building', 'edit'=>'pencil-alt', 'entity'=>'globe',
4151  'graph'=>'chart-line', 'grip_title'=>'arrows-alt', 'grip'=>'arrows-alt', 'help'=>'question-circle',
4152  'generic'=>'file', 'holiday'=>'umbrella-beach',
4153  'info'=>'info-circle', 'inventory'=>'boxes', 'intracommreport'=>'globe-europe', 'jobprofile'=>'cogs',
4154  'knowledgemanagement'=>'ticket-alt', 'label'=>'layer-group', 'line'=>'bars', 'loan'=>'money-bill-alt',
4155  'member'=>'user-alt', 'meeting'=>'chalkboard-teacher', 'mrp'=>'cubes', 'next'=>'arrow-alt-circle-right',
4156  'trip'=>'wallet', 'expensereport'=>'wallet', 'group'=>'users', 'movement'=>'people-carry',
4157  'sign-out'=>'sign-out-alt',
4158  'switch_off'=>'toggle-off', 'switch_on'=>'toggle-on', 'switch_on_red'=>'toggle-on', 'check'=>'check', 'bookmark'=>'star',
4159  'bank'=>'university', 'close_title'=>'times', 'delete'=>'trash', 'filter'=>'filter',
4160  'list-alt'=>'list-alt', 'calendarlist'=>'bars', 'calendar'=>'calendar-alt', 'calendarmonth'=>'calendar-alt', 'calendarweek'=>'calendar-week', 'calendarday'=>'calendar-day', 'calendarperuser'=>'table',
4161  'intervention'=>'ambulance', 'invoice'=>'file-invoice-dollar', 'currency'=>'dollar-sign', 'multicurrency'=>'dollar-sign', 'order'=>'file-invoice',
4162  'error'=>'exclamation-triangle', 'warning'=>'exclamation-triangle',
4163  'other'=>'square',
4164  'playdisabled'=>'play', 'pdf'=>'file-pdf', 'poll'=>'check-double', 'pos'=>'cash-register', 'preview'=>'binoculars', 'project'=>'project-diagram', 'projectpub'=>'project-diagram', 'projecttask'=>'tasks', 'propal'=>'file-signature', 'proposal'=>'file-signature',
4165  'partnership'=>'handshake', 'payment'=>'money-check-alt', 'payment_vat'=>'money-check-alt', 'pictoconfirm'=>'check-square', 'phoning'=>'phone', 'phoning_mobile'=>'mobile-alt', 'phoning_fax'=>'fax', 'previous'=>'arrow-alt-circle-left', 'printer'=>'print', 'product'=>'cube', 'puce'=>'angle-right',
4166  'recent' => 'check-square', 'reception'=>'dolly', 'recruitmentjobposition'=>'id-card-alt', 'recruitmentcandidature'=>'id-badge',
4167  'resize'=>'crop', 'supplier_order'=>'dol-order_supplier', 'supplier_proposal'=>'file-signature',
4168  'refresh'=>'redo', 'region'=>'map-marked', 'replacement'=>'exchange-alt', 'resource'=>'laptop-house', 'recurring'=>'history',
4169  'service'=>'concierge-bell',
4170  'skill'=>'shapes', 'state'=>'map-marked-alt', 'security'=>'key', 'salary'=>'wallet', 'shipment'=>'dolly', 'stock'=>'box-open', 'stats' => 'chart-bar', 'split'=>'code-branch', 'stripe'=>'stripe-s',
4171  'supplier'=>'building', 'technic'=>'cogs',
4172  'timespent'=>'clock', 'title_setup'=>'tools', 'title_accountancy'=>'money-check-alt', 'title_bank'=>'university', 'title_hrm'=>'umbrella-beach',
4173  'title_agenda'=>'calendar-alt',
4174  'uncheck'=>'times', 'uparrow'=>'share', 'url'=>'external-link-alt', 'vat'=>'money-check-alt', 'vcard'=>'address-card',
4175  'jabber'=>'comment-o',
4176  'website'=>'globe-americas', 'workstation'=>'pallet', 'webhook'=>'bullseye', 'world'=>'globe', 'private'=>'user-lock',
4177  'conferenceorbooth'=>'chalkboard-teacher', 'eventorganization'=>'project-diagram'
4178  );
4179  if ($pictowithouttext == 'off') {
4180  $fakey = 'fa-square';
4181  $fasize = '1.3em';
4182  } elseif ($pictowithouttext == 'on') {
4183  $fakey = 'fa-check-square';
4184  $fasize = '1.3em';
4185  } elseif ($pictowithouttext == 'listlight') {
4186  $fakey = 'fa-download';
4187  $marginleftonlyshort = 1;
4188  } elseif ($pictowithouttext == 'printer') {
4189  $fakey = 'fa-print';
4190  $fasize = '1.2em';
4191  } elseif ($pictowithouttext == 'note') {
4192  $fakey = 'fa-sticky-note';
4193  $marginleftonlyshort = 1;
4194  } elseif (in_array($pictowithouttext, array('1uparrow', '1downarrow', '1leftarrow', '1rightarrow', '1uparrow_selected', '1downarrow_selected', '1leftarrow_selected', '1rightarrow_selected'))) {
4195  $convertarray = array('1uparrow'=>'caret-up', '1downarrow'=>'caret-down', '1leftarrow'=>'caret-left', '1rightarrow'=>'caret-right', '1uparrow_selected'=>'caret-up', '1downarrow_selected'=>'caret-down', '1leftarrow_selected'=>'caret-left', '1rightarrow_selected'=>'caret-right');
4196  $fakey = 'fa-'.$convertarray[$pictowithouttext];
4197  if (preg_match('/selected/', $pictowithouttext)) {
4198  $facolor = '#888';
4199  }
4200  $marginleftonlyshort = 1;
4201  } elseif (!empty($arrayconvpictotofa[$pictowithouttext])) {
4202  $fakey = 'fa-'.$arrayconvpictotofa[$pictowithouttext];
4203  } else {
4204  $fakey = 'fa-'.$pictowithouttext;
4205  }
4206 
4207  if (in_array($pictowithouttext, array('dollyrevert', 'member', 'members', 'contract', 'group', 'resource', 'shipment'))) {
4208  $morecss .= ' em092';
4209  }
4210  if (in_array($pictowithouttext, array('conferenceorbooth', 'collab', 'eventorganization', 'holiday', 'info', 'project', 'workstation'))) {
4211  $morecss .= ' em088';
4212  }
4213  if (in_array($pictowithouttext, array('asset', 'intervention', 'payment', 'loan', 'partnership', 'stock', 'technic'))) {
4214  $morecss .= ' em080';
4215  }
4216 
4217  // Define $marginleftonlyshort
4218  $arrayconvpictotomarginleftonly = array(
4219  'bank', 'check', 'delete', 'generic', 'grip', 'grip_title', 'jabber',
4220  'grip_title', 'grip', 'listlight', 'note', 'on', 'off', 'playdisabled', 'printer', 'resize', 'sign-out', 'stats', 'switch_on', 'switch_on_red', 'switch_off',
4221  'uparrow', '1uparrow', '1downarrow', '1leftarrow', '1rightarrow', '1uparrow_selected', '1downarrow_selected', '1leftarrow_selected', '1rightarrow_selected'
4222  );
4223  if (!isset($arrayconvpictotomarginleftonly[$pictowithouttext])) {
4224  $marginleftonlyshort = 0;
4225  }
4226 
4227  // Add CSS
4228  $arrayconvpictotomorcess = array(
4229  'action'=>'infobox-action', 'account'=>'infobox-bank_account', 'accounting_account'=>'infobox-bank_account', 'accountline'=>'infobox-bank_account', 'accountancy'=>'infobox-bank_account', 'asset'=>'infobox-bank_account',
4230  'bank_account'=>'infobox-bank_account',
4231  'bill'=>'infobox-commande', 'billa'=>'infobox-commande', 'billr'=>'infobox-commande', 'billd'=>'infobox-commande',
4232  'margin'=>'infobox-bank_account', 'conferenceorbooth'=>'infobox-project',
4233  'cash-register'=>'infobox-bank_account', 'contract'=>'infobox-contrat', 'check'=>'font-status4', 'collab'=>'infobox-action', 'conversation'=>'infobox-contrat',
4234  'donation'=>'infobox-commande', 'dolly'=>'infobox-commande', 'dollyrevert'=>'flip infobox-order_supplier',
4235  'ecm'=>'infobox-action', 'eventorganization'=>'infobox-project',
4236  'hrm'=>'infobox-adherent', 'group'=>'infobox-adherent', 'intervention'=>'infobox-contrat',
4237  'incoterm'=>'infobox-supplier_proposal',
4238  'currency'=>'infobox-bank_account', 'multicurrency'=>'infobox-bank_account',
4239  'members'=>'infobox-adherent', 'member'=>'infobox-adherent', 'money-bill-alt'=>'infobox-bank_account',
4240  'order'=>'infobox-commande',
4241  'user'=>'infobox-adherent', 'users'=>'infobox-adherent',
4242  'error'=>'pictoerror', 'warning'=>'pictowarning', 'switch_on'=>'font-status4', 'switch_on_red'=>'font-status8',
4243  'holiday'=>'infobox-holiday', 'info'=>'opacityhigh', 'invoice'=>'infobox-commande',
4244  'knowledgemanagement'=>'infobox-contrat rotate90', 'loan'=>'infobox-bank_account',
4245  'payment'=>'infobox-bank_account', 'payment_vat'=>'infobox-bank_account', 'poll'=>'infobox-adherent', 'pos'=>'infobox-bank_account', 'project'=>'infobox-project', 'projecttask'=>'infobox-project',
4246  'propal'=>'infobox-propal', 'proposal'=>'infobox-propal','private'=>'infobox-project',
4247  'reception'=>'flip', 'recruitmentjobposition'=>'infobox-adherent', 'recruitmentcandidature'=>'infobox-adherent',
4248  'resource'=>'infobox-action',
4249  'salary'=>'infobox-bank_account', 'shipment'=>'infobox-commande', 'supplier_invoice'=>'infobox-order_supplier', 'supplier_invoicea'=>'infobox-order_supplier', 'supplier_invoiced'=>'infobox-order_supplier',
4250  'supplier'=>'infobox-order_supplier', 'supplier_order'=>'infobox-order_supplier', 'supplier_proposal'=>'infobox-supplier_proposal',
4251  'ticket'=>'infobox-contrat', 'title_accountancy'=>'infobox-bank_account', 'title_hrm'=>'infobox-holiday', 'expensereport'=>'infobox-expensereport', 'trip'=>'infobox-expensereport', 'title_agenda'=>'infobox-action',
4252  'vat'=>'infobox-bank_account',
4253  //'title_setup'=>'infobox-action', 'tools'=>'infobox-action',
4254  'list-alt'=>'imgforviewmode', 'calendar'=>'imgforviewmode', 'calendarweek'=>'imgforviewmode', 'calendarmonth'=>'imgforviewmode', 'calendarday'=>'imgforviewmode', 'calendarperuser'=>'imgforviewmode'
4255  );
4256  if (!empty($arrayconvpictotomorcess[$pictowithouttext])) {
4257  $morecss .= ($morecss ? ' ' : '').$arrayconvpictotomorcess[$pictowithouttext];
4258  }
4259 
4260  // Define $color
4261  $arrayconvpictotocolor = array(
4262  'address'=>'#6c6aa8', 'building'=>'#6c6aa8', 'bom'=>'#a69944',
4263  'cog'=>'#999', 'companies'=>'#6c6aa8', 'company'=>'#6c6aa8', 'contact'=>'#6c6aa8', 'cron'=>'#555',
4264  'dynamicprice'=>'#a69944',
4265  'edit'=>'#444', 'note'=>'#999', 'error'=>'', 'help'=>'#bbb', 'listlight'=>'#999', 'language'=>'#555',
4266  //'dolly'=>'#a69944', 'dollyrevert'=>'#a69944',
4267  'lock'=>'#ddd', 'lot'=>'#a69944',
4268  'map-marker-alt'=>'#aaa', 'mrp'=>'#a69944', 'product'=>'#a69944', 'service'=>'#a69944', 'inventory'=>'#a69944', 'stock'=>'#a69944', 'movement'=>'#a69944',
4269  'other'=>'#ddd', 'world'=>'#986c6a',
4270  'partnership'=>'#6c6aa8', 'playdisabled'=>'#ccc', 'printer'=>'#444', 'projectpub'=>'#986c6a', 'reception'=>'#a69944', 'resize'=>'#444', 'rss'=>'#cba',
4271  //'shipment'=>'#a69944',
4272  'security'=>'#999', 'square'=>'#888', 'stop-circle'=>'#888', 'stats'=>'#444', 'switch_off'=>'#999', 'technic'=>'#999', 'timespent'=>'#555',
4273  'uncheck'=>'#800', 'uparrow'=>'#555', 'user-cog'=>'#999', 'country'=>'#aaa', 'globe-americas'=>'#aaa', 'region'=>'#aaa', 'state'=>'#aaa',
4274  'website'=>'#304', 'workstation'=>'#a69944'
4275  );
4276  if (isset($arrayconvpictotocolor[$pictowithouttext])) {
4277  $facolor = $arrayconvpictotocolor[$pictowithouttext];
4278  }
4279 
4280  // This snippet only needed since function img_edit accepts only one additional parameter: no separate one for css only.
4281  // class/style need to be extracted to avoid duplicate class/style validation errors when $moreatt is added to the end of the attributes.
4282  $morestyle = '';
4283  $reg = array();
4284  if (preg_match('/class="([^"]+)"/', $moreatt, $reg)) {
4285  $morecss .= ($morecss ? ' ' : '').$reg[1];
4286  $moreatt = str_replace('class="'.$reg[1].'"', '', $moreatt);
4287  }
4288  if (preg_match('/style="([^"]+)"/', $moreatt, $reg)) {
4289  $morestyle = $reg[1];
4290  $moreatt = str_replace('style="'.$reg[1].'"', '', $moreatt);
4291  }
4292  $moreatt = trim($moreatt);
4293 
4294  $enabledisablehtml = '<span class="'.$fa.' '.$fakey.($marginleftonlyshort ? ($marginleftonlyshort == 1 ? ' marginleftonlyshort' : ' marginleftonly') : '');
4295  $enabledisablehtml .= ($morecss ? ' '.$morecss : '').'" style="'.($fasize ? ('font-size: '.$fasize.';') : '').($facolor ? (' color: '.$facolor.';') : '').($morestyle ? ' '.$morestyle : '').'"'.(($notitle || empty($titlealt)) ? '' : ' title="'.dol_escape_htmltag($titlealt).'"').($moreatt ? ' '.$moreatt : '').'>';
4296  /*if (!empty($conf->global->MAIN_OPTIMIZEFORTEXTBROWSER)) {
4297  $enabledisablehtml .= $titlealt;
4298  }*/
4299  $enabledisablehtml .= '</span>';
4300 
4301  return $enabledisablehtml;
4302  }
4303 
4304  if (!empty($conf->global->MAIN_OVERWRITE_THEME_PATH)) {
4305  $path = $conf->global->MAIN_OVERWRITE_THEME_PATH.'/theme/'.$theme; // If the theme does not have the same name as the module
4306  } elseif (!empty($conf->global->MAIN_OVERWRITE_THEME_RES)) {
4307  $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
4308  } elseif (!empty($conf->modules_parts['theme']) && array_key_exists($theme, $conf->modules_parts['theme'])) {
4309  $path = $theme.'/theme/'.$theme; // If the theme have the same name as the module
4310  }
4311 
4312  // If we ask an image into $url/$mymodule/img (instead of default path)
4313  $regs = array();
4314  if (preg_match('/^([^@]+)@([^@]+)$/i', $picto, $regs)) {
4315  $picto = $regs[1];
4316  $path = $regs[2]; // $path is $mymodule
4317  }
4318 
4319  // Clean parameters
4320  if (!preg_match('/(\.png|\.gif|\.svg)$/i', $picto)) {
4321  $picto .= '.png';
4322  }
4323  // If alt path are defined, define url where img file is, according to physical path
4324  // ex: array(["main"]=>"/home/maindir/htdocs", ["alt0"]=>"/home/moddir0/htdocs", ...)
4325  foreach ($conf->file->dol_document_root as $type => $dirroot) {
4326  if ($type == 'main') {
4327  continue;
4328  }
4329  // This need a lot of time, that's why enabling alternative dir like "custom" dir is not recommanded
4330  if (file_exists($dirroot.'/'.$path.'/img/'.$picto)) {
4331  $url = DOL_URL_ROOT.$conf->file->dol_url_root[$type];
4332  break;
4333  }
4334  }
4335 
4336  // $url is '' or '/custom', $path is current theme or
4337  $fullpathpicto = $url.'/'.$path.'/img/'.$picto;
4338  }
4339 
4340  if ($srconly) {
4341  return $fullpathpicto;
4342  }
4343  // tag title is used for tooltip on <a>, tag alt can be used with very simple text on image for blind people
4344  return '<img src="'.$fullpathpicto.'"'.($notitle ? '' : ' alt="'.dol_escape_htmltag($alt).'"').(($notitle || empty($titlealt)) ? '' : ' title="'.dol_escape_htmltag($titlealt).'"').($moreatt ? ' '.$moreatt.($morecss ? ' class="'.$morecss.'"' : '') : ' class="inline-block'.($morecss ? ' '.$morecss : '').'"').'>'; // Alt is used for accessibility, title for popup
4345 }
4346 
4360 function img_object($titlealt, $picto, $moreatt = '', $pictoisfullpath = false, $srconly = 0, $notitle = 0)
4361 {
4362  if (strpos($picto, '^') === 0) {
4363  return img_picto($titlealt, str_replace('^', '', $picto), $moreatt, $pictoisfullpath, $srconly, $notitle);
4364  } else {
4365  return img_picto($titlealt, 'object_'.$picto, $moreatt, $pictoisfullpath, $srconly, $notitle);
4366  }
4367 }
4368 
4380 function img_weather($titlealt, $picto, $moreatt = '', $pictoisfullpath = 0, $morecss = '')
4381 {
4382  global $conf;
4383 
4384  if (is_numeric($picto)) {
4385  //$leveltopicto = array(0=>'weather-clear.png', 1=>'weather-few-clouds.png', 2=>'weather-clouds.png', 3=>'weather-many-clouds.png', 4=>'weather-storm.png');
4386  //$picto = $leveltopicto[$picto];
4387  return '<i class="fa fa-weather-level'.$picto.'"></i>';
4388  } elseif (!preg_match('/(\.png|\.gif)$/i', $picto)) {
4389  $picto .= '.png';
4390  }
4391 
4392  $path = DOL_URL_ROOT.'/theme/'.$conf->theme.'/img/weather/'.$picto;
4393 
4394  return img_picto($titlealt, $path, $moreatt, 1, 0, 0, '', $morecss);
4395 }
4396 
4408 function img_picto_common($titlealt, $picto, $moreatt = '', $pictoisfullpath = 0, $notitle = 0)
4409 {
4410  global $conf;
4411 
4412  if (!preg_match('/(\.png|\.gif)$/i', $picto)) {
4413  $picto .= '.png';
4414  }
4415 
4416  if ($pictoisfullpath) {
4417  $path = $picto;
4418  } else {
4419  $path = DOL_URL_ROOT.'/theme/common/'.$picto;
4420 
4421  if (!empty($conf->global->MAIN_MODULE_CAN_OVERWRITE_COMMONICONS)) {
4422  $themepath = DOL_DOCUMENT_ROOT.'/theme/'.$conf->theme.'/img/'.$picto;
4423 
4424  if (file_exists($themepath)) {
4425  $path = $themepath;
4426  }
4427  }
4428  }
4429 
4430  return img_picto($titlealt, $path, $moreatt, 1, 0, $notitle);
4431 }
4432 
4445 function img_action($titlealt, $numaction, $picto = '')
4446 {
4447  global $langs;
4448 
4449  if (empty($titlealt) || $titlealt == 'default') {
4450  if ($numaction == '-1' || $numaction == 'ST_NO') {
4451  $numaction = -1;
4452  $titlealt = $langs->transnoentitiesnoconv('ChangeDoNotContact');
4453  } elseif ($numaction == '0' || $numaction == 'ST_NEVER') {
4454  $numaction = 0;
4455  $titlealt = $langs->transnoentitiesnoconv('ChangeNeverContacted');
4456  } elseif ($numaction == '1' || $numaction == 'ST_TODO') {
4457  $numaction = 1;
4458  $titlealt = $langs->transnoentitiesnoconv('ChangeToContact');
4459  } elseif ($numaction == '2' || $numaction == 'ST_PEND') {
4460  $numaction = 2;
4461  $titlealt = $langs->transnoentitiesnoconv('ChangeContactInProcess');
4462  } elseif ($numaction == '3' || $numaction == 'ST_DONE') {
4463  $numaction = 3;
4464  $titlealt = $langs->transnoentitiesnoconv('ChangeContactDone');
4465  } else {
4466  $titlealt = $langs->transnoentitiesnoconv('ChangeStatus '.$numaction);
4467  $numaction = 0;
4468  }
4469  }
4470  if (!is_numeric($numaction)) {
4471  $numaction = 0;
4472  }
4473 
4474  return img_picto($titlealt, !empty($picto) ? $picto : 'stcomm'.$numaction.'.png');
4475 }
4476 
4484 function img_pdf($titlealt = 'default', $size = 3)
4485 {
4486  global $langs;
4487 
4488  if ($titlealt == 'default') {
4489  $titlealt = $langs->trans('Show');
4490  }
4491 
4492  return img_picto($titlealt, 'pdf'.$size.'.png');
4493 }
4494 
4502 function img_edit_add($titlealt = 'default', $other = '')
4503 {
4504  global $langs;
4505 
4506  if ($titlealt == 'default') {
4507  $titlealt = $langs->trans('Add');
4508  }
4509 
4510  return img_picto($titlealt, 'edit_add.png', $other);
4511 }
4519 function img_edit_remove($titlealt = 'default', $other = '')
4520 {
4521  global $langs;
4522 
4523  if ($titlealt == 'default') {
4524  $titlealt = $langs->trans('Remove');
4525  }
4526 
4527  return img_picto($titlealt, 'edit_remove.png', $other);
4528 }
4529 
4538 function img_edit($titlealt = 'default', $float = 0, $other = '')
4539 {
4540  global $langs;
4541 
4542  if ($titlealt == 'default') {
4543  $titlealt = $langs->trans('Modify');
4544  }
4545 
4546  return img_picto($titlealt, 'edit.png', ($float ? 'style="float: '.($langs->tab_translate["DIRECTION"] == 'rtl' ? 'left' : 'right').'"' : "").($other ? ' '.$other : ''));
4547 }
4548 
4557 function img_view($titlealt = 'default', $float = 0, $other = 'class="valignmiddle"')
4558 {
4559  global $langs;
4560 
4561  if ($titlealt == 'default') {
4562  $titlealt = $langs->trans('View');
4563  }
4564 
4565  $moreatt = ($float ? 'style="float: right" ' : '').$other;
4566 
4567  return img_picto($titlealt, 'eye', $moreatt);
4568 }
4569 
4578 function img_delete($titlealt = 'default', $other = 'class="pictodelete"', $morecss = '')
4579 {
4580  global $langs;
4581 
4582  if ($titlealt == 'default') {
4583  $titlealt = $langs->trans('Delete');
4584  }
4585 
4586  return img_picto($titlealt, 'delete.png', $other, false, 0, 0, '', $morecss);
4587 }
4588 
4596 function img_printer($titlealt = "default", $other = '')
4597 {
4598  global $langs;
4599  if ($titlealt == "default") {
4600  $titlealt = $langs->trans("Print");
4601  }
4602  return img_picto($titlealt, 'printer.png', $other);
4603 }
4604 
4612 function img_split($titlealt = 'default', $other = 'class="pictosplit"')
4613 {
4614  global $langs;
4615 
4616  if ($titlealt == 'default') {
4617  $titlealt = $langs->trans('Split');
4618  }
4619 
4620  return img_picto($titlealt, 'split.png', $other);
4621 }
4622 
4630 function img_help($usehelpcursor = 1, $usealttitle = 1)
4631 {
4632  global $langs;
4633 
4634  if ($usealttitle) {
4635  if (is_string($usealttitle)) {
4636  $usealttitle = dol_escape_htmltag($usealttitle);
4637  } else {
4638  $usealttitle = $langs->trans('Info');
4639  }
4640  }
4641 
4642  return img_picto($usealttitle, 'info.png', 'style="vertical-align: middle;'.($usehelpcursor == 1 ? ' cursor: help' : ($usehelpcursor == 2 ? ' cursor: pointer' : '')).'"');
4643 }
4644 
4651 function img_info($titlealt = 'default')
4652 {
4653  global $langs;
4654 
4655  if ($titlealt == 'default') {
4656  $titlealt = $langs->trans('Informations');
4657  }
4658 
4659  return img_picto($titlealt, 'info.png', 'style="vertical-align: middle;"');
4660 }
4661 
4670 function img_warning($titlealt = 'default', $moreatt = '', $morecss = 'pictowarning')
4671 {
4672  global $langs;
4673 
4674  if ($titlealt == 'default') {
4675  $titlealt = $langs->trans('Warning');
4676  }
4677 
4678  //return '<div class="imglatecoin">'.img_picto($titlealt, 'warning_white.png', 'class="pictowarning valignmiddle"'.($moreatt ? ($moreatt == '1' ? ' style="float: right"' : ' '.$moreatt): '')).'</div>';
4679  return img_picto($titlealt, 'warning.png', 'class="'.$morecss.'"'.($moreatt ? ($moreatt == '1' ? ' style="float: right"' : ' '.$moreatt) : ''));
4680 }
4681 
4688 function img_error($titlealt = 'default')
4689 {
4690  global $langs;
4691 
4692  if ($titlealt == 'default') {
4693  $titlealt = $langs->trans('Error');
4694  }
4695 
4696  return img_picto($titlealt, 'error.png');
4697 }
4698 
4706 function img_next($titlealt = 'default', $moreatt = '')
4707 {
4708  global $langs;
4709 
4710  if ($titlealt == 'default') {
4711  $titlealt = $langs->trans('Next');
4712  }
4713 
4714  //return img_picto($titlealt, 'next.png', $moreatt);
4715  return '<span class="fa fa-chevron-right paddingright paddingleft" title="'.dol_escape_htmltag($titlealt).'"></span>';
4716 }
4717 
4725 function img_previous($titlealt = 'default', $moreatt = '')
4726 {
4727  global $langs;
4728 
4729  if ($titlealt == 'default') {
4730  $titlealt = $langs->trans('Previous');
4731  }
4732 
4733  //return img_picto($titlealt, 'previous.png', $moreatt);
4734  return '<span class="fa fa-chevron-left paddingright paddingleft" title="'.dol_escape_htmltag($titlealt).'"></span>';
4735 }
4736 
4745 function img_down($titlealt = 'default', $selected = 0, $moreclass = '')
4746 {
4747  global $langs;
4748 
4749  if ($titlealt == 'default') {
4750  $titlealt = $langs->trans('Down');
4751  }
4752 
4753  return img_picto($titlealt, ($selected ? '1downarrow_selected.png' : '1downarrow.png'), 'class="imgdown'.($moreclass ? " ".$moreclass : "").'"');
4754 }
4755 
4764 function img_up($titlealt = 'default', $selected = 0, $moreclass = '')
4765 {
4766  global $langs;
4767 
4768  if ($titlealt == 'default') {
4769  $titlealt = $langs->trans('Up');
4770  }
4771 
4772  return img_picto($titlealt, ($selected ? '1uparrow_selected.png' : '1uparrow.png'), 'class="imgup'.($moreclass ? " ".$moreclass : "").'"');
4773 }
4774 
4783 function img_left($titlealt = 'default', $selected = 0, $moreatt = '')
4784 {
4785  global $langs;
4786 
4787  if ($titlealt == 'default') {
4788  $titlealt = $langs->trans('Left');
4789  }
4790 
4791  return img_picto($titlealt, ($selected ? '1leftarrow_selected.png' : '1leftarrow.png'), $moreatt);
4792 }
4793 
4802 function img_right($titlealt = 'default', $selected = 0, $moreatt = '')
4803 {
4804  global $langs;
4805 
4806  if ($titlealt == 'default') {
4807  $titlealt = $langs->trans('Right');
4808  }
4809 
4810  return img_picto($titlealt, ($selected ? '1rightarrow_selected.png' : '1rightarrow.png'), $moreatt);
4811 }
4812 
4820 function img_allow($allow, $titlealt = 'default')
4821 {
4822  global $langs;
4823 
4824  if ($titlealt == 'default') {
4825  $titlealt = $langs->trans('Active');
4826  }
4827 
4828  if ($allow == 1) {
4829  return img_picto($titlealt, 'tick.png');
4830  }
4831 
4832  return '-';
4833 }
4834 
4842 function img_credit_card($brand, $morecss = null)
4843 {
4844  if (is_null($morecss)) {
4845  $morecss = 'fa-2x';
4846  }
4847 
4848  if ($brand == 'visa' || $brand == 'Visa') {
4849  $brand = 'cc-visa';
4850  } elseif ($brand == 'mastercard' || $brand == 'MasterCard') {
4851  $brand = 'cc-mastercard';
4852  } elseif ($brand == 'amex' || $brand == 'American Express') {
4853  $brand = 'cc-amex';
4854  } elseif ($brand == 'discover' || $brand == 'Discover') {
4855  $brand = 'cc-discover';
4856  } elseif ($brand == 'jcb' || $brand == 'JCB') {
4857  $brand = 'cc-jcb';
4858  } elseif ($brand == 'diners' || $brand == 'Diners club') {
4859  $brand = 'cc-diners-club';
4860  } elseif (!in_array($brand, array('cc-visa', 'cc-mastercard', 'cc-amex', 'cc-discover', 'cc-jcb', 'cc-diners-club'))) {
4861  $brand = 'credit-card';
4862  }
4863 
4864  return '<span class="fa fa-'.$brand.' fa-fw'.($morecss ? ' '.$morecss : '').'"></span>';
4865 }
4866 
4875 function img_mime($file, $titlealt = '', $morecss = '')
4876 {
4877  require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
4878 
4879  $mimetype = dol_mimetype($file, '', 1);
4880  $mimeimg = dol_mimetype($file, '', 2);
4881  $mimefa = dol_mimetype($file, '', 4);
4882 
4883  if (empty($titlealt)) {
4884  $titlealt = 'Mime type: '.$mimetype;
4885  }
4886 
4887  //return img_picto_common($titlealt, 'mime/'.$mimeimg, 'class="'.$morecss.'"');
4888  return '<i class="fa fa-'.$mimefa.' paddingright'.($morecss ? ' '.$morecss : '').'"'.($titlealt ? ' title="'.$titlealt.'"' : '').'></i>';
4889 }
4890 
4891 
4899 function img_search($titlealt = 'default', $other = '')
4900 {
4901  global $conf, $langs;
4902 
4903  if ($titlealt == 'default') {
4904  $titlealt = $langs->trans('Search');
4905  }
4906 
4907  $img = img_picto($titlealt, 'search.png', $other, false, 1);
4908 
4909  $input = '<input type="image" class="liste_titre" name="button_search" src="'.$img.'" ';
4910  $input .= 'value="'.dol_escape_htmltag($titlealt).'" title="'.dol_escape_htmltag($titlealt).'" >';
4911 
4912  return $input;
4913 }
4914 
4922 function img_searchclear($titlealt = 'default', $other = '')
4923 {
4924  global $conf, $langs;
4925 
4926  if ($titlealt == 'default') {
4927  $titlealt = $langs->trans('Search');
4928  }
4929 
4930  $img = img_picto($titlealt, 'searchclear.png', $other, false, 1);
4931 
4932  $input = '<input type="image" class="liste_titre" name="button_removefilter" src="'.$img.'" ';
4933  $input .= 'value="'.dol_escape_htmltag($titlealt).'" title="'.dol_escape_htmltag($titlealt).'" >';
4934 
4935  return $input;
4936 }
4937 
4949 function info_admin($text, $infoonimgalt = 0, $nodiv = 0, $admin = '1', $morecss = 'hideonsmartphone', $textfordropdown = '')
4950 {
4951  global $conf, $langs;
4952 
4953  if ($infoonimgalt) {
4954  $result = img_picto($text, 'info', 'class="'.($morecss ? ' '.$morecss : '').'"');
4955  } else {
4956  if (empty($conf->use_javascript_ajax)) {
4957  $textfordropdown = '';
4958  }
4959 
4960  $class = (empty($admin) ? 'undefined' : ($admin == '1' ? 'info' : $admin));
4961  $result = ($nodiv ? '' : '<div class="'.$class.($morecss ? ' '.$morecss : '').($textfordropdown ? ' hidden' : '').'">').'<span class="fa fa-info-circle" title="'.dol_escape_htmltag($admin ? $langs->trans('InfoAdmin') : $langs->trans('Note')).'"></span> '.$text.($nodiv ? '' : '</div>');
4962 
4963  if ($textfordropdown) {
4964  $tmpresult = '<span class="'.$class.'text opacitymedium cursorpointer">'.$langs->trans($textfordropdown).' '.img_picto($langs->trans($textfordropdown), '1downarrow').'</span>';
4965  $tmpresult .= '<script type="text/javascript">
4966  jQuery(document).ready(function() {
4967  jQuery(".'.$class.'text").click(function() {
4968  console.log("toggle text");
4969  jQuery(".'.$class.'").toggle();
4970  });
4971  });
4972  </script>';
4973 
4974  $result = $tmpresult.$result;
4975  }
4976  }
4977 
4978  return $result;
4979 }
4980 
4981 
4993 function dol_print_error($db = '', $error = '', $errors = null)
4994 {
4995  global $conf, $langs, $argv;
4996  global $dolibarr_main_prod;
4997 
4998  $out = '';
4999  $syslog = '';
5000 
5001  // If error occurs before the $lang object was loaded
5002  if (!$langs) {
5003  require_once DOL_DOCUMENT_ROOT.'/core/class/translate.class.php';
5004  $langs = new Translate('', $conf);
5005  $langs->load("main");
5006  }
5007 
5008  // Load translation files required by the error messages
5009  $langs->loadLangs(array('main', 'errors'));
5010 
5011  if ($_SERVER['DOCUMENT_ROOT']) { // Mode web
5012  $out .= $langs->trans("DolibarrHasDetectedError").".<br>\n";
5013  if (getDolGlobalInt('MAIN_FEATURES_LEVEL') > 0) {
5014  $out .= "You use an experimental or develop level of features, so please do NOT report any bugs or vulnerability, except if problem is confirmed after moving option MAIN_FEATURES_LEVEL back to 0.<br>\n";
5015  }
5016  $out .= $langs->trans("InformationToHelpDiagnose").":<br>\n";
5017 
5018  $out .= "<b>".$langs->trans("Date").":</b> ".dol_print_date(time(), 'dayhourlog')."<br>\n";
5019  $out .= "<b>".$langs->trans("Dolibarr").":</b> ".DOL_VERSION." - https://www.dolibarr.org<br>\n";
5020  if (isset($conf->global->MAIN_FEATURES_LEVEL)) {
5021  $out .= "<b>".$langs->trans("LevelOfFeature").":</b> ".getDolGlobalInt('MAIN_FEATURES_LEVEL')."<br>\n";
5022  }
5023  if (function_exists("phpversion")) {
5024  $out .= "<b>".$langs->trans("PHP").":</b> ".phpversion()."<br>\n";
5025  }
5026  $out .= "<b>".$langs->trans("Server").":</b> ".(isset($_SERVER["SERVER_SOFTWARE"]) ? dol_htmlentities($_SERVER["SERVER_SOFTWARE"], ENT_COMPAT) : '')."<br>\n";
5027  if (function_exists("php_uname")) {
5028  $out .= "<b>".$langs->trans("OS").":</b> ".php_uname()."<br>\n";
5029  }
5030  $out .= "<b>".$langs->trans("UserAgent").":</b> ".(isset($_SERVER["HTTP_USER_AGENT"]) ? dol_htmlentities($_SERVER["HTTP_USER_AGENT"], ENT_COMPAT) : '')."<br>\n";
5031  $out .= "<br>\n";
5032  $out .= "<b>".$langs->trans("RequestedUrl").":</b> ".dol_htmlentities($_SERVER["REQUEST_URI"], ENT_COMPAT)."<br>\n";
5033  $out .= "<b>".$langs->trans("Referer").":</b> ".(isset($_SERVER["HTTP_REFERER"]) ? dol_htmlentities($_SERVER["HTTP_REFERER"], ENT_COMPAT) : '')."<br>\n";
5034  $out .= "<b>".$langs->trans("MenuManager").":</b> ".(isset($conf->standard_menu) ? dol_htmlentities($conf->standard_menu, ENT_COMPAT) : '')."<br>\n";
5035  $out .= "<br>\n";
5036  $syslog .= "url=".dol_escape_htmltag($_SERVER["REQUEST_URI"]);
5037  $syslog .= ", query_string=".dol_escape_htmltag($_SERVER["QUERY_STRING"]);
5038  } else // Mode CLI
5039  {
5040  $out .= '> '.$langs->transnoentities("ErrorInternalErrorDetected").":\n".$argv[0]."\n";
5041  $syslog .= "pid=".dol_getmypid();
5042  }
5043 
5044  if (!empty($conf->modules)) {
5045  $out .= "<b>".$langs->trans("Modules").":</b> ".join(', ', $conf->modules)."<br>\n";
5046  }
5047 
5048  if (is_object($db)) {
5049  if ($_SERVER['DOCUMENT_ROOT']) { // Mode web
5050  $out .= "<b>".$langs->trans("DatabaseTypeManager").":</b> ".$db->type."<br>\n";
5051  $out .= "<b>".$langs->trans("RequestLastAccessInError").":</b> ".($db->lastqueryerror() ? dol_escape_htmltag($db->lastqueryerror()) : $langs->trans("ErrorNoRequestInError"))."<br>\n";
5052  $out .= "<b>".$langs->trans("ReturnCodeLastAccessInError").":</b> ".($db->lasterrno() ? dol_escape_htmltag($db->lasterrno()) : $langs->trans("ErrorNoRequestInError"))."<br>\n";
5053  $out .= "<b>".$langs->trans("InformationLastAccessInError").":</b> ".($db->lasterror() ? dol_escape_htmltag($db->lasterror()) : $langs->trans("ErrorNoRequestInError"))."<br>\n";
5054  $out .= "<br>\n";
5055  } else // Mode CLI
5056  {
5057  // No dol_escape_htmltag for output, we are in CLI mode
5058  $out .= '> '.$langs->transnoentities("DatabaseTypeManager").":\n".$db->type."\n";
5059  $out .= '> '.$langs->transnoentities("RequestLastAccessInError").":\n".($db->lastqueryerror() ? $db->lastqueryerror() : $langs->transnoentities("ErrorNoRequestInError"))."\n";
5060  $out .= '> '.$langs->transnoentities("ReturnCodeLastAccessInError").":\n".($db->lasterrno() ? $db->lasterrno() : $langs->transnoentities("ErrorNoRequestInError"))."\n";
5061  $out .= '> '.$langs->transnoentities("InformationLastAccessInError").":\n".($db->lasterror() ? $db->lasterror() : $langs->transnoentities("ErrorNoRequestInError"))."\n";
5062  }
5063  $syslog .= ", sql=".$db->lastquery();
5064  $syslog .= ", db_error=".$db->lasterror();
5065  }
5066 
5067  if ($error || $errors) {
5068  $langs->load("errors");
5069 
5070  // Merge all into $errors array
5071  if (is_array($error) && is_array($errors)) {
5072  $errors = array_merge($error, $errors);
5073  } elseif (is_array($error)) {
5074  $errors = $error;
5075  } elseif (is_array($errors)) {
5076  $errors = array_merge(array($error), $errors);
5077  } else {
5078  $errors = array_merge(array($error), array($errors));
5079  }
5080 
5081  foreach ($errors as $msg) {
5082  if (empty($msg)) {
5083  continue;
5084  }
5085  if ($_SERVER['DOCUMENT_ROOT']) { // Mode web
5086  $out .= "<b>".$langs->trans("Message").":</b> ".dol_escape_htmltag($msg)."<br>\n";
5087  } else // Mode CLI
5088  {
5089  $out .= '> '.$langs->transnoentities("Message").":\n".$msg."\n";
5090  }
5091  $syslog .= ", msg=".$msg;
5092  }
5093  }
5094  if (empty($dolibarr_main_prod) && $_SERVER['DOCUMENT_ROOT'] && function_exists('xdebug_print_function_stack') && function_exists('xdebug_call_file')) {
5095  xdebug_print_function_stack();
5096  $out .= '<b>XDebug informations:</b>'."<br>\n";
5097  $out .= 'File: '.xdebug_call_file()."<br>\n";
5098  $out .= 'Line: '.xdebug_call_line()."<br>\n";
5099  $out .= 'Function: '.xdebug_call_function()."<br>\n";
5100  $out .= "<br>\n";
5101  }
5102 
5103  // Return a http header with error code if possible
5104  if (!headers_sent()) {
5105  if (function_exists('top_httphead')) { // In CLI context, the method does not exists
5106  top_httphead();
5107  }
5108  http_response_code(500);
5109  }
5110 
5111  if (empty($dolibarr_main_prod)) {
5112  print $out;
5113  } else {
5114  if (empty($langs->defaultlang)) {
5115  $langs->setDefaultLang();
5116  }
5117  $langs->loadLangs(array("main", "errors")); // Reload main because language may have been set only on previous line so we have to reload files we need.
5118  // This should not happen, except if there is a bug somewhere. Enabled and check log in such case.
5119  print 'This website or feature is currently temporarly not available or failed after a technical error.<br><br>This may be due to a maintenance operation. Current status of operation ('.dol_print_date(dol_now(), 'dayhourrfc').') are on next line...<br><br>'."\n";
5120  print $langs->trans("DolibarrHasDetectedError").'. ';
5121  print $langs->trans("YouCanSetOptionDolibarrMainProdToZero");
5122  define("MAIN_CORE_ERROR", 1);
5123  }
5124 
5125  dol_syslog("Error ".$syslog, LOG_ERR);
5126 }
5127 
5138 function dol_print_error_email($prefixcode, $errormessage = '', $errormessages = array(), $morecss = 'error', $email = '')
5139 {
5140  global $langs, $conf;
5141 
5142  if (empty($email)) {
5143  $email = $conf->global->MAIN_INFO_SOCIETE_MAIL;
5144  }
5145 
5146  $langs->load("errors");
5147  $now = dol_now();
5148 
5149  print '<br><div class="center login_main_message"><div class="'.$morecss.'">';
5150  print $langs->trans("ErrorContactEMail", $email, $prefixcode.dol_print_date($now, '%Y%m%d%H%M%S'));
5151  if ($errormessage) {
5152  print '<br><br>'.$errormessage;
5153  }
5154  if (is_array($errormessages) && count($errormessages)) {
5155  foreach ($errormessages as $mesgtoshow) {
5156  print '<br><br>'.$mesgtoshow;
5157  }
5158  }
5159  print '</div></div>';
5160 }
5161 
5178 function print_liste_field_titre($name, $file = "", $field = "", $begin = "", $moreparam = "", $moreattrib = "", $sortfield = "", $sortorder = "", $prefix = "", $tooltip = "", $forcenowrapcolumntitle = 0)
5179 {
5180  print getTitleFieldOfList($name, 0, $file, $field, $begin, $moreparam, $moreattrib, $sortfield, $sortorder, $prefix, 0, $tooltip, $forcenowrapcolumntitle);
5181 }
5182 
5201 function getTitleFieldOfList($name, $thead = 0, $file = "", $field = "", $begin = "", $moreparam = "", $moreattrib = "", $sortfield = "", $sortorder = "", $prefix = "", $disablesortlink = 0, $tooltip = '', $forcenowrapcolumntitle = 0)
5202 {
5203  global $conf, $langs, $form;
5204  //print "$name, $file, $field, $begin, $options, $moreattrib, $sortfield, $sortorder<br>\n";
5205 
5206  if ($moreattrib == 'class="right"') {
5207  $prefix .= 'right '; // For backward compatibility
5208  }
5209 
5210  $sortorder = strtoupper($sortorder);
5211  $out = '';
5212  $sortimg = '';
5213 
5214  $tag = 'th';
5215  if ($thead == 2) {
5216  $tag = 'div';
5217  }
5218 
5219  $tmpsortfield = explode(',', $sortfield);
5220  $sortfield1 = trim($tmpsortfield[0]); // If $sortfield is 'd.datep,d.id', it becomes 'd.datep'
5221  $tmpfield = explode(',', $field);
5222  $field1 = trim($tmpfield[0]); // If $field is 'd.datep,d.id', it becomes 'd.datep'
5223 
5224  if (empty($conf->global->MAIN_DISABLE_WRAPPING_ON_COLUMN_TITLE) && empty($forcenowrapcolumntitle)) {
5225  $prefix = 'wrapcolumntitle '.$prefix;
5226  }
5227 
5228  //var_dump('field='.$field.' field1='.$field1.' sortfield='.$sortfield.' sortfield1='.$sortfield1);
5229  // If field is used as sort criteria we use a specific css class liste_titre_sel
5230  // Example if (sortfield,field)=("nom","xxx.nom") or (sortfield,field)=("nom","nom")
5231  $liste_titre = 'liste_titre';
5232  if ($field1 && ($sortfield1 == $field1 || $sortfield1 == preg_replace("/^[^\.]+\./", "", $field1))) {
5233  $liste_titre = 'liste_titre_sel';
5234  }
5235 
5236  $tagstart = '<'.$tag.' class="'.$prefix.$liste_titre.'" '.$moreattrib;
5237  //$out .= (($field && empty($conf->global->MAIN_DISABLE_WRAPPING_ON_COLUMN_TITLE) && preg_match('/^[a-zA-Z_0-9\s\.\-:&;]*$/', $name)) ? ' title="'.dol_escape_htmltag($langs->trans($name)).'"' : '');
5238  $tagstart .= ($name && empty($conf->global->MAIN_DISABLE_WRAPPING_ON_COLUMN_TITLE) && empty($forcenowrapcolumntitle) && !dol_textishtml($name)) ? ' title="'.dol_escape_htmltag($langs->trans($name)).'"' : '';
5239  $tagstart .= '>';
5240 
5241  if (empty($thead) && $field && empty($disablesortlink)) { // If this is a sort field
5242  $options = preg_replace('/sortfield=([a-zA-Z0-9,\s\.]+)/i', '', (is_scalar($moreparam) ? $moreparam : ''));
5243  $options = preg_replace('/sortorder=([a-zA-Z0-9,\s\.]+)/i', '', $options);
5244  $options = preg_replace('/&+/i', '&', $options);
5245  if (!preg_match('/^&/', $options)) {
5246  $options = '&'.$options;
5247  }
5248 
5249  $sortordertouseinlink = '';
5250  if ($field1 != $sortfield1) { // We are on another field than current sorted field
5251  if (preg_match('/^DESC/i', $sortorder)) {
5252  $sortordertouseinlink .= str_repeat('desc,', count(explode(',', $field)));
5253  } else { // We reverse the var $sortordertouseinlink
5254  $sortordertouseinlink .= str_repeat('asc,', count(explode(',', $field)));
5255  }
5256  } else { // We are on field that is the first current sorting criteria
5257  if (preg_match('/^ASC/i', $sortorder)) { // We reverse the var $sortordertouseinlink
5258  $sortordertouseinlink .= str_repeat('desc,', count(explode(',', $field)));
5259  } else {
5260  $sortordertouseinlink .= str_repeat('asc,', count(explode(',', $field)));
5261  }
5262  }
5263  $sortordertouseinlink = preg_replace('/,$/', '', $sortordertouseinlink);
5264  $out .= '<a class="reposition" href="'.$file.'?sortfield='.$field.'&sortorder='.$sortordertouseinlink.'&begin='.$begin.$options.'"';
5265  //$out .= (empty($conf->global->MAIN_DISABLE_WRAPPING_ON_COLUMN_TITLE) ? ' title="'.dol_escape_htmltag($langs->trans($name)).'"' : '');
5266  $out .= '>';
5267  }
5268  if ($tooltip) {
5269  // You can also use 'TranslationString:keyfortooltiponclick' for a tooltip on click.
5270  if (preg_match('/:\w+$/', $tooltip)) {
5271  $tmptooltip = explode(':', $tooltip);
5272  } else {
5273  $tmptooltip = array($tooltip);
5274  }
5275  $out .= $form->textwithpicto($langs->trans($name), $langs->trans($tmptooltip[0]), 1, 'help', '', 0, 3, (empty($tmptooltip[1]) ? '' : 'extra_'.str_replace('.', '_', $field).'_'.$tmptooltip[1]));
5276  } else {
5277  $out .= $langs->trans($name);
5278  }
5279 
5280  if (empty($thead) && $field && empty($disablesortlink)) { // If this is a sort field
5281  $out .= '</a>';
5282  }
5283 
5284  if (empty($thead) && $field) { // If this is a sort field
5285  $options = preg_replace('/sortfield=([a-zA-Z0-9,\s\.]+)/i', '', (is_scalar($moreparam) ? $moreparam : ''));
5286  $options = preg_replace('/sortorder=([a-zA-Z0-9,\s\.]+)/i', '', $options);
5287  $options = preg_replace('/&+/i', '&', $options);
5288  if (!preg_match('/^&/', $options)) {
5289  $options = '&'.$options;
5290  }
5291 
5292  if (!$sortorder || $field1 != $sortfield1) {
5293  //$out.= '<a href="'.$file.'?sortfield='.$field.'&sortorder=asc&begin='.$begin.$options.'">'.img_down("A-Z",0).'</a>';
5294  //$out.= '<a href="'.$file.'?sortfield='.$field.'&sortorder=desc&begin='.$begin.$options.'">'.img_up("Z-A",0).'</a>';
5295  } else {
5296  if (preg_match('/^DESC/', $sortorder)) {
5297  //$out.= '<a href="'.$file.'?sortfield='.$field.'&sortorder=asc&begin='.$begin.$options.'">'.img_down("A-Z",0).'</a>';
5298  //$out.= '<a href="'.$file.'?sortfield='.$field.'&sortorder=desc&begin='.$begin.$options.'">'.img_up("Z-A",1).'</a>';
5299  $sortimg .= '<span class="nowrap">'.img_up("Z-A", 0, 'paddingright').'</span>';
5300  }
5301  if (preg_match('/^ASC/', $sortorder)) {
5302  //$out.= '<a href="'.$file.'?sortfield='.$field.'&sortorder=asc&begin='.$begin.$options.'">'.img_down("A-Z",1).'</a>';
5303  //$out.= '<a href="'.$file.'?sortfield='.$field.'&sortorder=desc&begin='.$begin.$options.'">'.img_up("Z-A",0).'</a>';
5304  $sortimg .= '<span class="nowrap">'.img_down("A-Z", 0, 'paddingright').'</span>';
5305  }
5306  }
5307  }
5308 
5309  $tagend = '</'.$tag.'>';
5310 
5311  $out = $tagstart.$sortimg.$out.$tagend;
5312 
5313  return $out;
5314 }
5315 
5324 function print_titre($title)
5325 {
5326  dol_syslog(__FUNCTION__." is deprecated", LOG_WARNING);
5327 
5328  print '<div class="titre">'.$title.'</div>';
5329 }
5330 
5342 function print_fiche_titre($title, $mesg = '', $picto = 'generic', $pictoisfullpath = 0, $id = '')
5343 {
5344  print load_fiche_titre($title, $mesg, $picto, $pictoisfullpath, $id);
5345 }
5346 
5360 function load_fiche_titre($titre, $morehtmlright = '', $picto = 'generic', $pictoisfullpath = 0, $id = '', $morecssontable = '', $morehtmlcenter = '')
5361 {
5362  global $conf;
5363 
5364  $return = '';
5365 
5366  if ($picto == 'setup') {
5367  $picto = 'generic';
5368  }
5369 
5370  $return .= "\n";
5371  $return .= '<table '.($id ? 'id="'.$id.'" ' : '').'class="centpercent notopnoleftnoright table-fiche-title'.($morecssontable ? ' '.$morecssontable : '').'">'; // maring bottom must be same than into print_barre_list
5372  $return .= '<tr class="titre">';
5373  if ($picto) {
5374  $return .= '<td class="nobordernopadding widthpictotitle valignmiddle col-picto">'.img_picto('', $picto, 'class="valignmiddle widthpictotitle pictotitle"', $pictoisfullpath).'</td>';
5375  }
5376  $return .= '<td class="nobordernopadding valignmiddle col-title">';
5377  $return .= '<div class="titre inline-block">'.$titre.'</div>';
5378  $return .= '</td>';
5379  if (dol_strlen($morehtmlcenter)) {
5380  $return .= '<td class="nobordernopadding center valignmiddle col-center">'.$morehtmlcenter.'</td>';
5381  }
5382  if (dol_strlen($morehtmlright)) {
5383  $return .= '<td class="nobordernopadding titre_right wordbreakimp right valignmiddle col-right">'.$morehtmlright.'</td>';
5384  }
5385  $return .= '</tr></table>'."\n";
5386 
5387  return $return;
5388 }
5389 
5413 function print_barre_liste($titre, $page, $file, $options = '', $sortfield = '', $sortorder = '', $morehtmlcenter = '', $num = -1, $totalnboflines = '', $picto = 'generic', $pictoisfullpath = 0, $morehtmlright = '', $morecss = '', $limit = -1, $hideselectlimit = 0, $hidenavigation = 0, $pagenavastextinput = 0, $morehtmlrightbeforearrow = '')
5414 {
5415  global $conf, $langs;
5416 
5417  $savlimit = $limit;
5418  $savtotalnboflines = $totalnboflines;
5419  $totalnboflines = abs((int) $totalnboflines);
5420 
5421  if ($picto == 'setup') {
5422  $picto = 'title_setup.png';
5423  }
5424  if (($conf->browser->name == 'ie') && $picto == 'generic') {
5425  $picto = 'title.gif';
5426  }
5427  if ($limit < 0) {
5428  $limit = $conf->liste_limit;
5429  }
5430  if ($savlimit != 0 && (($num > $limit) || ($num == -1) || ($limit == 0))) {
5431  $nextpage = 1;
5432  } else {
5433  $nextpage = 0;
5434  }
5435  //print 'totalnboflines='.$totalnboflines.'-savlimit='.$savlimit.'-limit='.$limit.'-num='.$num.'-nextpage='.$nextpage;
5436 
5437  print "\n";
5438  print "<!-- Begin title -->\n";
5439  print '<table class="centpercent notopnoleftnoright table-fiche-title'.($morecss ? ' '.$morecss : '').'"><tr>'; // maring bottom must be same than into load_fiche_tire
5440 
5441  // Left
5442 
5443  if ($picto && $titre) {
5444  print '<td class="nobordernopadding widthpictotitle valignmiddle col-picto">'.img_picto('', $picto, 'class="valignmiddle pictotitle widthpictotitle"', $pictoisfullpath).'</td>';
5445  }
5446  print '<td class="nobordernopadding valignmiddle col-title">';
5447  print '<div class="titre inline-block">'.$titre;
5448  if (!empty($titre) && $savtotalnboflines >= 0 && (string) $savtotalnboflines != '') {
5449  print '<span class="opacitymedium colorblack paddingleft">('.$totalnboflines.')</span>';
5450  }
5451  print '</div></td>';
5452 
5453  // Center
5454  if ($morehtmlcenter) {
5455  print '<td class="nobordernopadding center valignmiddle col-center">'.$morehtmlcenter.'</td>';
5456  }
5457 
5458  // Right
5459  print '<td class="nobordernopadding valignmiddle right col-right">';
5460  print '<input type="hidden" name="pageplusoneold" value="'.((int) $page + 1).'">';
5461  if ($sortfield) {
5462  $options .= "&sortfield=".urlencode($sortfield);
5463  }
5464  if ($sortorder) {
5465  $options .= "&sortorder=".urlencode($sortorder);
5466  }
5467  // Show navigation bar
5468  $pagelist = '';
5469  if ($savlimit != 0 && ($page > 0 || $num > $limit)) {
5470  if ($totalnboflines) { // If we know total nb of lines
5471  // Define nb of extra page links before and after selected page + ... + first or last
5472  $maxnbofpage = (empty($conf->dol_optimize_smallscreen) ? 4 : 0);
5473 
5474  if ($limit > 0) {
5475  $nbpages = ceil($totalnboflines / $limit);
5476  } else {
5477  $nbpages = 1;
5478  }
5479  $cpt = ($page - $maxnbofpage);
5480  if ($cpt < 0) {
5481  $cpt = 0;
5482  }
5483 
5484  if ($cpt >= 1) {
5485  if (empty($pagenavastextinput)) {
5486  $pagelist .= '<li class="pagination"><a href="'.$file.'?page=0'.$options.'">1</a></li>';
5487  if ($cpt > 2) {
5488  $pagelist .= '<li class="pagination"><span class="inactive">...</span></li>';
5489  } elseif ($cpt == 2) {
5490  $pagelist .= '<li class="pagination"><a href="'.$file.'?page=1'.$options.'">2</a></li>';
5491  }
5492  }
5493  }
5494 
5495  do {
5496  if ($pagenavastextinput) {
5497  if ($cpt == $page) {
5498  $pagelist .= '<li class="pagination"><input type="text" class="width25 center pageplusone" name="pageplusone" value="'.($page + 1).'"></li>';
5499  $pagelist .= '/';
5500  }
5501  } else {
5502  if ($cpt == $page) {
5503  $pagelist .= '<li class="pagination"><span class="active">'.($page + 1).'</span></li>';
5504  } else {
5505  $pagelist .= '<li class="pagination"><a href="'.$file.'?page='.$cpt.$options.'">'.($cpt + 1).'</a></li>';
5506  }
5507  }
5508  $cpt++;
5509  } while ($cpt < $nbpages && $cpt <= ($page + $maxnbofpage));
5510 
5511  if (empty($pagenavastextinput)) {
5512  if ($cpt < $nbpages) {
5513  if ($cpt < $nbpages - 2) {
5514  $pagelist .= '<li class="pagination"><span class="inactive">...</span></li>';
5515  } elseif ($cpt == $nbpages - 2) {
5516  $pagelist .= '<li class="pagination"><a href="'.$file.'?page='.($nbpages - 2).$options.'">'.($nbpages - 1).'</a></li>';
5517  }
5518  $pagelist .= '<li class="pagination"><a href="'.$file.'?page='.($nbpages - 1).$options.'">'.$nbpages.'</a></li>';
5519  }
5520  } else {
5521  //var_dump($page.' '.$cpt.' '.$nbpages);
5522  $pagelist .= '<li class="pagination paginationlastpage"><a href="'.$file.'?page='.($nbpages - 1).$options.'">'.$nbpages.'</a></li>';
5523  }
5524  } else {
5525  $pagelist .= '<li class="pagination"><span class="active">'.($page + 1)."</li>";
5526  }
5527  }
5528 
5529  if ($savlimit || $morehtmlright || $morehtmlrightbeforearrow) {
5530  print_fleche_navigation($page, $file, $options, $nextpage, $pagelist, $morehtmlright, $savlimit, $totalnboflines, $hideselectlimit, $morehtmlrightbeforearrow); // output the div and ul for previous/last completed with page numbers into $pagelist
5531  }
5532 
5533  // js to autoselect page field on focus
5534  if ($pagenavastextinput) {
5535  print ajax_autoselect('.pageplusone');
5536  }
5537 
5538  print '</td>';
5539 
5540  print '</tr></table>'."\n";
5541  print "<!-- End title -->\n\n";
5542 }
5543 
5559 function print_fleche_navigation($page, $file, $options = '', $nextpage = 0, $betweenarrows = '', $afterarrows = '', $limit = -1, $totalnboflines = 0, $hideselectlimit = 0, $beforearrows = '')
5560 {
5561  global $conf, $langs;
5562 
5563  print '<div class="pagination"><ul>';
5564  if ($beforearrows) {
5565  print '<li class="paginationbeforearrows">';
5566  print $beforearrows;
5567  print '</li>';
5568  }
5569  if ((int) $limit > 0 && empty($hideselectlimit)) {
5570  $pagesizechoices = '10:10,15:15,20:20,30:30,40:40,50:50,100:100,250:250,500:500,1000:1000';
5571  $pagesizechoices .= ',5000:5000,10000:10000,20000:20000';
5572  //$pagesizechoices.=',0:'.$langs->trans("All"); // Not yet supported
5573  //$pagesizechoices.=',2:2';
5574  if (!empty($conf->global->MAIN_PAGESIZE_CHOICES)) {
5575  $pagesizechoices = $conf->global->MAIN_PAGESIZE_CHOICES;
5576  }
5577 
5578  print '<li class="pagination">';
5579  print '<select class="flat selectlimit" name="limit" title="'.dol_escape_htmltag($langs->trans("MaxNbOfRecordPerPage")).'">';
5580  $tmpchoice = explode(',', $pagesizechoices);
5581  $tmpkey = $limit.':'.$limit;
5582  if (!in_array($tmpkey, $tmpchoice)) {
5583  $tmpchoice[] = $tmpkey;
5584  }
5585  $tmpkey = $conf->liste_limit.':'.$conf->liste_limit;
5586  if (!in_array($tmpkey, $tmpchoice)) {
5587  $tmpchoice[] = $tmpkey;
5588  }
5589  asort($tmpchoice, SORT_NUMERIC);
5590  foreach ($tmpchoice as $val) {
5591  $selected = '';
5592  $tmp = explode(':', $val);
5593  $key = $tmp[0];
5594  $val = $tmp[1];
5595  if ($key != '' && $val != '') {
5596  if ((int) $key == (int) $limit) {
5597  $selected = ' selected="selected"';
5598  }
5599  print '<option name="'.$key.'"'.$selected.'>'.dol_escape_htmltag($val).'</option>'."\n";
5600  }
5601  }
5602  print '</select>';
5603  if ($conf->use_javascript_ajax) {
5604  print '<!-- JS CODE TO ENABLE select limit to launch submit of page -->
5605  <script>
5606  jQuery(document).ready(function () {
5607  jQuery(".selectlimit").change(function() {
5608  console.log("Change limit. Send submit");
5609  $(this).parents(\'form:first\').submit();
5610  });
5611  });
5612  </script>
5613  ';
5614  }
5615  print '</li>';
5616  }
5617  if ($page > 0) {
5618  print '<li class="pagination paginationpage paginationpageleft"><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>';
5619  }
5620  if ($betweenarrows) {
5621  print '<!--<div class="betweenarrows nowraponall inline-block">-->';
5622  print $betweenarrows;
5623  print '<!--</div>-->';
5624  }
5625  if ($nextpage > 0) {
5626  print '<li class="pagination paginationpage paginationpageright"><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>';
5627  }
5628  if ($afterarrows) {
5629  print '<li class="paginationafterarrows">';
5630  print $afterarrows;
5631  print '</li>';
5632  }
5633  print '</ul></div>'."\n";
5634 }
5635 
5636 
5648 function vatrate($rate, $addpercent = false, $info_bits = 0, $usestarfornpr = 0, $html = 0)
5649 {
5650  $morelabel = '';
5651 
5652  if (preg_match('/%/', $rate)) {
5653  $rate = str_replace('%', '', $rate);
5654  $addpercent = true;
5655  }
5656  $reg = array();
5657  if (preg_match('/\‍((.*)\‍)/', $rate, $reg)) {
5658  $morelabel = ' ('.$reg[1].')';
5659  $rate = preg_replace('/\s*'.preg_quote($morelabel, '/').'/', '', $rate);
5660  $morelabel = ' '.($html ? '<span class="opacitymedium">' : '').'('.$reg[1].')'.($html ? '</span>' : '');
5661  }
5662  if (preg_match('/\*/', $rate)) {
5663  $rate = str_replace('*', '', $rate);
5664  $info_bits |= 1;
5665  }
5666 
5667  // If rate is '9/9/9' we don't change it. If rate is '9.000' we apply price()
5668  if (!preg_match('/\//', $rate)) {
5669  $ret = price($rate, 0, '', 0, 0).($addpercent ? '%' : '');
5670  } else {
5671  // TODO Split on / and output with a price2num to have clean numbers without ton of 000.
5672  $ret = $rate.($addpercent ? '%' : '');
5673  }
5674  if (($info_bits & 1) && $usestarfornpr >= 0) {
5675  $ret .= ' *';
5676  }
5677  $ret .= $morelabel;
5678  return $ret;
5679 }
5680 
5681 
5697 function price($amount, $form = 0, $outlangs = '', $trunc = 1, $rounding = -1, $forcerounding = -1, $currency_code = '')
5698 {
5699  global $langs, $conf;
5700 
5701  // Clean parameters
5702  if (empty($amount)) {
5703  $amount = 0; // To have a numeric value if amount not defined or = ''
5704  }
5705  $amount = (is_numeric($amount) ? $amount : 0); // Check if amount is numeric, for example, an error occured when amount value = o (letter) instead 0 (number)
5706  if ($rounding < 0) {
5707  $rounding = min($conf->global->MAIN_MAX_DECIMALS_UNIT, $conf->global->MAIN_MAX_DECIMALS_TOT);
5708  }
5709  $nbdecimal = $rounding;
5710 
5711  if ($outlangs === 'none') {
5712  // Use international separators
5713  $dec = '.';
5714  $thousand = '';
5715  } else {
5716  // Output separators by default (french)
5717  $dec = ',';
5718  $thousand = ' ';
5719 
5720  // If $outlangs not forced, we use use language
5721  if (!is_object($outlangs)) {
5722  $outlangs = $langs;
5723  }
5724 
5725  if ($outlangs->transnoentitiesnoconv("SeparatorDecimal") != "SeparatorDecimal") {
5726  $dec = $outlangs->transnoentitiesnoconv("SeparatorDecimal");
5727  }
5728  if ($outlangs->transnoentitiesnoconv("SeparatorThousand") != "SeparatorThousand") {
5729  $thousand = $outlangs->transnoentitiesnoconv("SeparatorThousand");
5730  }
5731  if ($thousand == 'None') {
5732  $thousand = '';
5733  } elseif ($thousand == 'Space') {
5734  $thousand = ' ';
5735  }
5736  }
5737  //print "outlangs=".$outlangs->defaultlang." amount=".$amount." html=".$form." trunc=".$trunc." nbdecimal=".$nbdecimal." dec='".$dec."' thousand='".$thousand."'<br>";
5738 
5739  //print "amount=".$amount."-";
5740  $amount = str_replace(',', '.', $amount); // should be useless
5741  //print $amount."-";
5742  $datas = explode('.', $amount);
5743  $decpart = isset($datas[1]) ? $datas[1] : '';
5744  $decpart = preg_replace('/0+$/i', '', $decpart); // Supprime les 0 de fin de partie decimale
5745  //print "decpart=".$decpart."<br>";
5746  $end = '';
5747 
5748  // We increase nbdecimal if there is more decimal than asked (to not loose information)
5749  if (dol_strlen($decpart) > $nbdecimal) {
5750  $nbdecimal = dol_strlen($decpart);
5751  }
5752  // Si on depasse max
5753  if ($trunc && $nbdecimal > $conf->global->MAIN_MAX_DECIMALS_SHOWN) {
5754  $nbdecimal = $conf->global->MAIN_MAX_DECIMALS_SHOWN;
5755  if (preg_match('/\.\.\./i', $conf->global->MAIN_MAX_DECIMALS_SHOWN)) {
5756  // Si un affichage est tronque, on montre des ...
5757  $end = '...';
5758  }
5759  }
5760 
5761  // If force rounding
5762  if ((string) $forcerounding != '-1') {
5763  if ($forcerounding === 'MU') {
5764  $nbdecimal = $conf->global->MAIN_MAX_DECIMALS_UNIT;
5765  } elseif ($forcerounding === 'MT') {
5766  $nbdecimal = $conf->global->MAIN_MAX_DECIMALS_TOT;
5767  } elseif ($forcerounding >= 0) {
5768  $nbdecimal = $forcerounding;
5769  }
5770  }
5771 
5772  // Format number
5773  $output = number_format($amount, $nbdecimal, $dec, $thousand);
5774  if ($form) {
5775  $output = preg_replace('/\s/', '&nbsp;', $output);
5776  $output = preg_replace('/\'/', '&#039;', $output);
5777  }
5778  // Add symbol of currency if requested
5779  $cursymbolbefore = $cursymbolafter = '';
5780  if ($currency_code && is_object($outlangs)) {
5781  if ($currency_code == 'auto') {
5782  $currency_code = $conf->currency;
5783  }
5784 
5785  $listofcurrenciesbefore = array('AUD', 'CAD', 'CNY', 'COP', 'CLP', 'GBP', 'HKD', 'MXN', 'PEN', 'USD', 'CRC');
5786  $listoflanguagesbefore = array('nl_NL');
5787  if (in_array($currency_code, $listofcurrenciesbefore) || in_array($outlangs->defaultlang, $listoflanguagesbefore)) {
5788  $cursymbolbefore .= $outlangs->getCurrencySymbol($currency_code);
5789  } else {
5790  $tmpcur = $outlangs->getCurrencySymbol($currency_code);
5791  $cursymbolafter .= ($tmpcur == $currency_code ? ' '.$tmpcur : $tmpcur);
5792  }
5793  }
5794  $output = $cursymbolbefore.$output.$end.($cursymbolafter ? ' ' : '').$cursymbolafter;
5795 
5796  return $output;
5797 }
5798 
5823 function price2num($amount, $rounding = '', $option = 0)
5824 {
5825  global $langs, $conf;
5826 
5827  // Clean parameters
5828  if (is_null($amount)) {
5829  $amount = '';
5830  }
5831 
5832  // Round PHP function does not allow number like '1,234.56' nor '1.234,56' nor '1 234,56'
5833  // Numbers must be '1234.56'
5834  // Decimal delimiter for PHP and database SQL requests must be '.'
5835  $dec = ',';
5836  $thousand = ' ';
5837  if (is_null($langs)) { // $langs is not defined, we use english values.
5838  $dec = '.';
5839  $thousand = ',';
5840  } else {
5841  if ($langs->transnoentitiesnoconv("SeparatorDecimal") != "SeparatorDecimal") {
5842  $dec = $langs->transnoentitiesnoconv("SeparatorDecimal");
5843  }
5844  if ($langs->transnoentitiesnoconv("SeparatorThousand") != "SeparatorThousand") {
5845  $thousand = $langs->transnoentitiesnoconv("SeparatorThousand");
5846  }
5847  }
5848  if ($thousand == 'None') {
5849  $thousand = '';
5850  } elseif ($thousand == 'Space') {
5851  $thousand = ' ';
5852  }
5853  //print "amount=".$amount." html=".$form." trunc=".$trunc." nbdecimal=".$nbdecimal." dec='".$dec."' thousand='".$thousand."'<br>";
5854 
5855  // Convert value to universal number format (no thousand separator, '.' as decimal separator)
5856  if ($option != 1) { // If not a PHP number or unknown, we change or clean format
5857  //print "\n".'PP'.$amount.' - '.$dec.' - '.$thousand.' - '.intval($amount).'<br>';
5858  if (!is_numeric($amount)) {
5859  $amount = preg_replace('/[a-zA-Z\/\\\*\‍(\‍)<>\_]/', '', $amount);
5860  }
5861 
5862  if ($option == 2 && $thousand == '.' && preg_match('/\.(\d\d\d)$/', (string) $amount)) { // It means the . is used as a thousand separator and string come from input data, so 1.123 is 1123
5863  $amount = str_replace($thousand, '', $amount);
5864  }
5865 
5866  // Convert amount to format with dolibarr dec and thousand (this is because PHP convert a number
5867  // to format defined by LC_NUMERIC after a calculation and we want source format to be like defined by Dolibarr setup.
5868  // So if number was already a good number, it is converted into local Dolibarr setup.
5869  if (is_numeric($amount)) {
5870  // We put in temps value of decimal ("0.00001"). Works with 0 and 2.0E-5 and 9999.10
5871  $temps = sprintf("%0.10F", $amount - intval($amount)); // temps=0.0000000000 or 0.0000200000 or 9999.1000000000
5872  $temps = preg_replace('/([\.1-9])0+$/', '\\1', $temps); // temps=0. or 0.00002 or 9999.1
5873  $nbofdec = max(0, dol_strlen($temps) - 2); // -2 to remove "0."
5874  $amount = number_format($amount, $nbofdec, $dec, $thousand);
5875  }
5876  //print "QQ".$amount."<br>\n";
5877 
5878  // Now make replace (the main goal of function)
5879  if ($thousand != ',' && $thousand != '.') {
5880  $amount = str_replace(',', '.', $amount); // To accept 2 notations for french users
5881  }
5882 
5883  $amount = str_replace(' ', '', $amount); // To avoid spaces
5884  $amount = str_replace($thousand, '', $amount); // Replace of thousand before replace of dec to avoid pb if thousand is .
5885  $amount = str_replace($dec, '.', $amount);
5886 
5887  $amount = preg_replace('/[^0-9\-\.]/', '', $amount); // Clean non numeric chars (so it clean some UTF8 spaces for example.
5888  }
5889  //print ' XX'.$amount.' '.$rounding;
5890 
5891  // Now, $amount is a real PHP float number. We make a rounding if required.
5892  if ($rounding) {
5893  $nbofdectoround = '';
5894  if ($rounding == 'MU') {
5895  $nbofdectoround = $conf->global->MAIN_MAX_DECIMALS_UNIT;
5896  } elseif ($rounding == 'MT') {
5897  $nbofdectoround = $conf->global->MAIN_MAX_DECIMALS_TOT;
5898  } elseif ($rounding == 'MS') {
5899  $nbofdectoround = isset($conf->global->MAIN_MAX_DECIMALS_STOCK) ? $conf->global->MAIN_MAX_DECIMALS_STOCK : 5;
5900  } elseif ($rounding == 'CU') {
5901  $nbofdectoround = max($conf->global->MAIN_MAX_DECIMALS_UNIT, 8); // TODO Use param of currency
5902  } elseif ($rounding == 'CT') {
5903  $nbofdectoround = max($conf->global->MAIN_MAX_DECIMALS_TOT, 8); // TODO Use param of currency
5904  } elseif (is_numeric($rounding)) {
5905  $nbofdectoround = (int) $rounding;
5906  }
5907 
5908  //print " RR".$amount.' - '.$nbofdectoround.'<br>';
5909  if (dol_strlen($nbofdectoround)) {
5910  $amount = round(is_string($amount) ? (float) $amount : $amount, $nbofdectoround); // $nbofdectoround can be 0.
5911  } else {
5912  return 'ErrorBadParameterProvidedToFunction';
5913  }
5914  //print ' SS'.$amount.' - '.$nbofdec.' - '.$dec.' - '.$thousand.' - '.$nbofdectoround.'<br>';
5915 
5916  // Convert amount to format with dolibarr dec and thousand (this is because PHP convert a number
5917  // to format defined by LC_NUMERIC after a calculation and we want source format to be defined by Dolibarr setup.
5918  if (is_numeric($amount)) {
5919  // We put in temps value of decimal ("0.00001"). Works with 0 and 2.0E-5 and 9999.10
5920  $temps = sprintf("%0.10F", $amount - intval($amount)); // temps=0.0000000000 or 0.0000200000 or 9999.1000000000
5921  $temps = preg_replace('/([\.1-9])0+$/', '\\1', $temps); // temps=0. or 0.00002 or 9999.1
5922  $nbofdec = max(0, dol_strlen($temps) - 2); // -2 to remove "0."
5923  $amount = number_format($amount, min($nbofdec, $nbofdectoround), $dec, $thousand); // Convert amount to format with dolibarr dec and thousand
5924  }
5925  //print "TT".$amount.'<br>';
5926 
5927  // Always make replace because each math function (like round) replace
5928  // with local values and we want a number that has a SQL string format x.y
5929  if ($thousand != ',' && $thousand != '.') {
5930  $amount = str_replace(',', '.', $amount); // To accept 2 notations for french users
5931  }
5932 
5933  $amount = str_replace(' ', '', $amount); // To avoid spaces
5934  $amount = str_replace($thousand, '', $amount); // Replace of thousand before replace of dec to avoid pb if thousand is .
5935  $amount = str_replace($dec, '.', $amount);
5936 
5937  $amount = preg_replace('/[^0-9\-\.]/', '', $amount); // Clean non numeric chars (so it clean some UTF8 spaces for example.
5938  }
5939 
5940  return $amount;
5941 }
5942 
5955 function showDimensionInBestUnit($dimension, $unit, $type, $outputlangs, $round = -1, $forceunitoutput = 'no', $use_short_label = 0)
5956 {
5957  require_once DOL_DOCUMENT_ROOT.'/core/lib/product.lib.php';
5958 
5959  if (($forceunitoutput == 'no' && $dimension < 1 / 10000 && $unit < 90) || (is_numeric($forceunitoutput) && $forceunitoutput == -6)) {
5960  $dimension = $dimension * 1000000;
5961  $unit = $unit - 6;
5962  } elseif (($forceunitoutput == 'no' && $dimension < 1 / 10 && $unit < 90) || (is_numeric($forceunitoutput) && $forceunitoutput == -3)) {
5963  $dimension = $dimension * 1000;
5964  $unit = $unit - 3;
5965  } elseif (($forceunitoutput == 'no' && $dimension > 100000000 && $unit < 90) || (is_numeric($forceunitoutput) && $forceunitoutput == 6)) {
5966  $dimension = $dimension / 1000000;
5967  $unit = $unit + 6;
5968  } elseif (($forceunitoutput == 'no' && $dimension > 100000 && $unit < 90) || (is_numeric($forceunitoutput) && $forceunitoutput == 3)) {
5969  $dimension = $dimension / 1000;
5970  $unit = $unit + 3;
5971  }
5972  // Special case when we want output unit into pound or ounce
5973  /* TODO
5974  if ($unit < 90 && $type == 'weight' && is_numeric($forceunitoutput) && (($forceunitoutput == 98) || ($forceunitoutput == 99))
5975  {
5976  $dimension = // convert dimension from standard unit into ounce or pound
5977  $unit = $forceunitoutput;
5978  }
5979  if ($unit > 90 && $type == 'weight' && is_numeric($forceunitoutput) && $forceunitoutput < 90)
5980  {
5981  $dimension = // convert dimension from standard unit into ounce or pound
5982  $unit = $forceunitoutput;
5983  }*/
5984 
5985  $ret = price($dimension, 0, $outputlangs, 0, 0, $round);
5986  $ret .= ' '.measuringUnitString(0, $type, $unit, $use_short_label, $outputlangs);
5987 
5988  return $ret;
5989 }
5990 
5991 
6004 function get_localtax($vatrate, $local, $thirdparty_buyer = "", $thirdparty_seller = "", $vatnpr = 0)
6005 {
6006  global $db, $conf, $mysoc;
6007 
6008  if (empty($thirdparty_seller) || !is_object($thirdparty_seller)) {
6009  $thirdparty_seller = $mysoc;
6010  }
6011 
6012  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);
6013 
6014  $vatratecleaned = $vatrate;
6015  $reg = array();
6016  if (preg_match('/^(.*)\s*\‍((.*)\‍)$/', $vatrate, $reg)) { // If vat is "xx (yy)"
6017  $vatratecleaned = trim($reg[1]);
6018  $vatratecode = $reg[2];
6019  }
6020 
6021  /*if ($thirdparty_buyer->country_code != $thirdparty_seller->country_code)
6022  {
6023  return 0;
6024  }*/
6025 
6026  // Some test to guess with no need to make database access
6027  if ($mysoc->country_code == 'ES') { // For spain localtaxes 1 and 2, tax is qualified if buyer use local tax
6028  if ($local == 1) {
6029  if (!$mysoc->localtax1_assuj || (string) $vatratecleaned == "0") {
6030  return 0;
6031  }
6032  if ($thirdparty_seller->id == $mysoc->id) {
6033  if (!$thirdparty_buyer->localtax1_assuj) {
6034  return 0;
6035  }
6036  } else {
6037  if (!$thirdparty_seller->localtax1_assuj) {
6038  return 0;
6039  }
6040  }
6041  }
6042 
6043  if ($local == 2) {
6044  //if (! $mysoc->localtax2_assuj || (string) $vatratecleaned == "0") return 0;
6045  if (!$mysoc->localtax2_assuj) {
6046  return 0; // If main vat is 0, IRPF may be different than 0.
6047  }
6048  if ($thirdparty_seller->id == $mysoc->id) {
6049  if (!$thirdparty_buyer->localtax2_assuj) {
6050  return 0;
6051  }
6052  } else {
6053  if (!$thirdparty_seller->localtax2_assuj) {
6054  return 0;
6055  }
6056  }
6057  }
6058  } else {
6059  if ($local == 1 && !$thirdparty_seller->localtax1_assuj) {
6060  return 0;
6061  }
6062  if ($local == 2 && !$thirdparty_seller->localtax2_assuj) {
6063  return 0;
6064  }
6065  }
6066 
6067  // For some country MAIN_GET_LOCALTAXES_VALUES_FROM_THIRDPARTY is forced to on.
6068  if (in_array($mysoc->country_code, array('ES'))) {
6069  $conf->global->MAIN_GET_LOCALTAXES_VALUES_FROM_THIRDPARTY = 1;
6070  }
6071 
6072  // Search local taxes
6073  if (!empty($conf->global->MAIN_GET_LOCALTAXES_VALUES_FROM_THIRDPARTY)) {
6074  if ($local == 1) {
6075  if ($thirdparty_seller != $mysoc) {
6076  if (!isOnlyOneLocalTax($local)) { // TODO We should provide $vatrate to search on correct line and not always on line with highest vat rate
6077  return $thirdparty_seller->localtax1_value;
6078  }
6079  } else { // i am the seller
6080  if (!isOnlyOneLocalTax($local)) { // TODO If seller is me, why not always returning this, even if there is only one locatax vat.
6081  return $conf->global->MAIN_INFO_VALUE_LOCALTAX1;
6082  }
6083  }
6084  }
6085  if ($local == 2) {
6086  if ($thirdparty_seller != $mysoc) {
6087  if (!isOnlyOneLocalTax($local)) { // TODO We should provide $vatrate to search on correct line and not always on line with highest vat rate
6088  // TODO We should also return value defined on thirdparty only if defined
6089  return $thirdparty_seller->localtax2_value;
6090  }
6091  } else { // i am the seller
6092  if (in_array($mysoc->country_code, array('ES'))) {
6093  return $thirdparty_buyer->localtax2_value;
6094  } else {
6095  return $conf->global->MAIN_INFO_VALUE_LOCALTAX2;
6096  }
6097  }
6098  }
6099  }
6100 
6101  // By default, search value of local tax on line of common tax
6102  $sql = "SELECT t.localtax1, t.localtax2, t.localtax1_type, t.localtax2_type";
6103  $sql .= " FROM ".MAIN_DB_PREFIX."c_tva as t, ".MAIN_DB_PREFIX."c_country as c";
6104  $sql .= " WHERE t.fk_pays = c.rowid AND c.code = '".$db->escape($thirdparty_seller->country_code)."'";
6105  $sql .= " AND t.taux = ".((float) $vatratecleaned)." AND t.active = 1";
6106  if (!empty($vatratecode)) {
6107  $sql .= " AND t.code ='".$db->escape($vatratecode)."'"; // If we have the code, we use it in priority
6108  } else {
6109  $sql .= " AND t.recuperableonly = '".$db->escape($vatnpr)."'";
6110  }
6111 
6112  $resql = $db->query($sql);
6113 
6114  if ($resql) {
6115  $obj = $db->fetch_object($resql);
6116  if ($obj) {
6117  if ($local == 1) {
6118  return $obj->localtax1;
6119  } elseif ($local == 2) {
6120  return $obj->localtax2;
6121  }
6122  }
6123  }
6124 
6125  return 0;
6126 }
6127 
6128 
6137 function isOnlyOneLocalTax($local)
6138 {
6139  $tax = get_localtax_by_third($local);
6140 
6141  $valors = explode(":", $tax);
6142 
6143  if (count($valors) > 1) {
6144  return false;
6145  } else {
6146  return true;
6147  }
6148 }
6149 
6156 function get_localtax_by_third($local)
6157 {
6158  global $db, $mysoc;
6159 
6160  $sql = " SELECT t.localtax".$local." as localtax";
6161  $sql .= " FROM ".MAIN_DB_PREFIX."c_tva as t INNER JOIN ".MAIN_DB_PREFIX."c_country as c ON c.rowid = t.fk_pays";
6162  $sql .= " WHERE c.code = '".$db->escape($mysoc->country_code)."' AND t.active = 1 AND t.taux = (";
6163  $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";
6164  $sql .= " WHERE c.code = '".$db->escape($mysoc->country_code)."' AND tt.active = 1)";
6165  $sql .= " AND t.localtax".$local."_type <> '0'";
6166  $sql .= " ORDER BY t.rowid DESC";
6167 
6168  $resql = $db->query($sql);
6169  if ($resql) {
6170  $obj = $db->fetch_object($resql);
6171  return $obj->localtax;
6172  } else {
6173  return 'Error';
6174  }
6175 
6176  return '0';
6177 }
6178 
6179 
6191 function getTaxesFromId($vatrate, $buyer = null, $seller = null, $firstparamisid = 1)
6192 {
6193  global $db, $mysoc;
6194 
6195  dol_syslog("getTaxesFromId vat id or rate = ".$vatrate);
6196 
6197  // Search local taxes
6198  $sql = "SELECT t.rowid, t.code, t.taux as rate, t.recuperableonly as npr, t.accountancy_code_sell, t.accountancy_code_buy,";
6199  $sql .= " t.localtax1, t.localtax1_type, t.localtax2, t.localtax2_type";
6200  $sql .= " FROM ".MAIN_DB_PREFIX."c_tva as t";
6201  if ($firstparamisid) {
6202  $sql .= " WHERE t.rowid = ".(int) $vatrate;
6203  } else {
6204  $vatratecleaned = $vatrate;
6205  $vatratecode = '';
6206  $reg = array();
6207  if (preg_match('/^(.*)\s*\‍((.*)\‍)$/', $vatrate, $reg)) { // If vat is "xx (yy)"
6208  $vatratecleaned = $reg[1];
6209  $vatratecode = $reg[2];
6210  }
6211 
6212  $sql .= ", ".MAIN_DB_PREFIX."c_country as c";
6213  /*if ($mysoc->country_code == 'ES') $sql.= " WHERE t.fk_pays = c.rowid AND c.code = '".$db->escape($buyer->country_code)."'"; // vat in spain use the buyer country ??
6214  else $sql.= " WHERE t.fk_pays = c.rowid AND c.code = '".$db->escape($seller->country_code)."'";*/
6215  $sql .= " WHERE t.fk_pays = c.rowid AND c.code = '".$db->escape($seller->country_code)."'";
6216  $sql .= " AND t.taux = ".((float) $vatratecleaned)." AND t.active = 1";
6217  if ($vatratecode) {
6218  $sql .= " AND t.code = '".$db->escape($vatratecode)."'";
6219  }
6220  }
6221 
6222  $resql = $db->query($sql);
6223  if ($resql) {
6224  $obj = $db->fetch_object($resql);
6225  if ($obj) {
6226  return array(
6227  'rowid'=>$obj->rowid,
6228  'code'=>$obj->code,
6229  'rate'=>$obj->rate,
6230  'localtax1'=>$obj->localtax1,
6231  'localtax1_type'=>$obj->localtax1_type,
6232  'localtax2'=>$obj->localtax2,
6233  'localtax2_type'=>$obj->localtax2_type,
6234  'npr'=>$obj->npr,
6235  'accountancy_code_sell'=>$obj->accountancy_code_sell,
6236  'accountancy_code_buy'=>$obj->accountancy_code_buy
6237  );
6238  } else {
6239  return array();
6240  }
6241  } else {
6242  dol_print_error($db);
6243  }
6244 
6245  return array();
6246 }
6247 
6264 function getLocalTaxesFromRate($vatrate, $local, $buyer, $seller, $firstparamisid = 0)
6265 {
6266  global $db, $mysoc;
6267 
6268  dol_syslog("getLocalTaxesFromRate vatrate=".$vatrate." local=".$local);
6269 
6270  // Search local taxes
6271  $sql = "SELECT t.taux as rate, t.code, t.localtax1, t.localtax1_type, t.localtax2, t.localtax2_type, t.accountancy_code_sell, t.accountancy_code_buy";
6272  $sql .= " FROM ".MAIN_DB_PREFIX."c_tva as t";
6273  if ($firstparamisid) {
6274  $sql .= " WHERE t.rowid = ".(int) $vatrate;
6275  } else {
6276  $vatratecleaned = $vatrate;
6277  $vatratecode = '';
6278  $reg = array();
6279  if (preg_match('/^(.*)\s*\‍((.*)\‍)$/', $vatrate, $reg)) { // If vat is "x.x (yy)"
6280  $vatratecleaned = $reg[1];
6281  $vatratecode = $reg[2];
6282  }
6283 
6284  $sql .= ", ".MAIN_DB_PREFIX."c_country as c";
6285  if (!empty($mysoc) && $mysoc->country_code == 'ES') {
6286  $countrycodetouse = ((empty($buyer) || empty($buyer->country_code)) ? $mysoc->country_code : $buyer->country_code);
6287  $sql .= " WHERE t.fk_pays = c.rowid AND c.code = '".$db->escape($countrycodetouse)."'"; // local tax in spain use the buyer country ??
6288  } else {
6289  $countrycodetouse = ((empty($seller) || empty($seller->country_code)) ? $mysoc->country_code : $seller->country_code);
6290  $sql .= " WHERE t.fk_pays = c.rowid AND c.code = '".$db->escape($countrycodetouse)."'";
6291  }
6292  $sql .= " AND t.taux = ".((float) $vatratecleaned)." AND t.active = 1";
6293  if ($vatratecode) {
6294  $sql .= " AND t.code = '".$db->escape($vatratecode)."'";
6295  }
6296  }
6297 
6298  $resql = $db->query($sql);
6299  if ($resql) {
6300  $obj = $db->fetch_object($resql);
6301 
6302  if ($obj) {
6303  $vateratestring = $obj->rate.($obj->code ? ' ('.$obj->code.')' : '');
6304 
6305  if ($local == 1) {
6306  return array($obj->localtax1_type, get_localtax($vateratestring, $local, $buyer, $seller), $obj->accountancy_code_sell, $obj->accountancy_code_buy);
6307  } elseif ($local == 2) {
6308  return array($obj->localtax2_type, get_localtax($vateratestring, $local, $buyer, $seller), $obj->accountancy_code_sell, $obj->accountancy_code_buy);
6309  } else {
6310  return array($obj->localtax1_type, get_localtax($vateratestring, 1, $buyer, $seller), $obj->localtax2_type, get_localtax($vateratestring, 2, $buyer, $seller), $obj->accountancy_code_sell, $obj->accountancy_code_buy);
6311  }
6312  }
6313  }
6314 
6315  return array();
6316 }
6317 
6328 function get_product_vat_for_country($idprod, $thirdpartytouse, $idprodfournprice = 0)
6329 {
6330  global $db, $conf, $mysoc;
6331 
6332  require_once DOL_DOCUMENT_ROOT.'/product/class/product.class.php';
6333 
6334  $ret = 0;
6335  $found = 0;
6336 
6337  if ($idprod > 0) {
6338  // Load product
6339  $product = new Product($db);
6340  $product->fetch($idprod);
6341 
6342  if ($mysoc->country_code == $thirdpartytouse->country_code) {
6343  // If country to consider is ours
6344  if ($idprodfournprice > 0) { // We want vat for product for a "supplier" object
6345  $result = $product->get_buyprice($idprodfournprice, 0, 0, 0);
6346  if ($result > 0) {
6347  $ret = $product->vatrate_supplier;
6348  if ($product->default_vat_code_supplier) {
6349  $ret .= ' ('.$product->default_vat_code_supplier.')';
6350  }
6351  $found = 1;
6352  }
6353  }
6354  if (!$found) {
6355  $ret = $product->tva_tx; // Default sales vat of product
6356  if ($product->default_vat_code) {
6357  $ret .= ' ('.$product->default_vat_code.')';
6358  }
6359  $found = 1;
6360  }
6361  } else {
6362  // TODO Read default product vat according to product and another countrycode.
6363  // Vat for couple anothercountrycode/product is data that is not managed and store yet, so we will fallback on next rule.
6364  }
6365  }
6366 
6367  if (!$found) {
6368  if (empty($conf->global->MAIN_VAT_DEFAULT_IF_AUTODETECT_FAILS)) {
6369  // If vat of product for the country not found or not defined, we return the first rate found (sorting on use_default, then on higher vat of country).
6370  $sql = "SELECT t.taux as vat_rate, t.code as default_vat_code";
6371  $sql .= " FROM ".MAIN_DB_PREFIX."c_tva as t, ".MAIN_DB_PREFIX."c_country as c";
6372  $sql .= " WHERE t.active = 1 AND t.fk_pays = c.rowid AND c.code = '".$db->escape($thirdpartytouse->country_code)."'";
6373  $sql .= " ORDER BY t.use_default DESC, t.taux DESC, t.code ASC, t.recuperableonly ASC";
6374  $sql .= $db->plimit(1);
6375 
6376  $resql = $db->query($sql);
6377  if ($resql) {
6378  $obj = $db->fetch_object($resql);
6379  if ($obj) {
6380  $ret = $obj->vat_rate;
6381  if ($obj->default_vat_code) {
6382  $ret .= ' ('.$obj->default_vat_code.')';
6383  }
6384  }
6385  $db->free($resql);
6386  } else {
6387  dol_print_error($db);
6388  }
6389  } else {
6390  // Forced value if autodetect fails. MAIN_VAT_DEFAULT_IF_AUTODETECT_FAILS can be
6391  // '1.23'
6392  // or '1.23 (CODE)'
6393  $defaulttx = '';
6394  if ($conf->global->MAIN_VAT_DEFAULT_IF_AUTODETECT_FAILS != 'none') {
6395  $defaulttx = $conf->global->MAIN_VAT_DEFAULT_IF_AUTODETECT_FAILS;
6396  }
6397  /*if (preg_match('/\‍((.*)\‍)/', $defaulttx, $reg)) {
6398  $defaultcode = $reg[1];
6399  $defaulttx = preg_replace('/\s*\‍(.*\‍)/', '', $defaulttx);
6400  }*/
6401 
6402  $ret = $defaulttx;
6403  }
6404  }
6405 
6406  dol_syslog("get_product_vat_for_country: ret=".$ret);
6407  return $ret;
6408 }
6409 
6419 function get_product_localtax_for_country($idprod, $local, $thirdpartytouse)
6420 {
6421  global $db, $mysoc;
6422 
6423  if (!class_exists('Product')) {
6424  require_once DOL_DOCUMENT_ROOT.'/product/class/product.class.php';
6425  }
6426 
6427  $ret = 0;
6428  $found = 0;
6429 
6430  if ($idprod > 0) {
6431  // Load product
6432  $product = new Product($db);
6433  $result = $product->fetch($idprod);
6434 
6435  if ($mysoc->country_code == $thirdpartytouse->country_code) { // If selling country is ours
6436  /* Not defined yet, so we don't use this
6437  if ($local==1) $ret=$product->localtax1_tx;
6438  elseif ($local==2) $ret=$product->localtax2_tx;
6439  $found=1;
6440  */
6441  } else {
6442  // TODO Read default product vat according to product and another countrycode.
6443  // Vat for couple anothercountrycode/product is data that is not managed and store yet, so we will fallback on next rule.
6444  }
6445  }
6446 
6447  if (!$found) {
6448  // If vat of product for the country not found or not defined, we return higher vat of country.
6449  $sql = "SELECT taux as vat_rate, localtax1, localtax2";
6450  $sql .= " FROM ".MAIN_DB_PREFIX."c_tva as t, ".MAIN_DB_PREFIX."c_country as c";
6451  $sql .= " WHERE t.active=1 AND t.fk_pays = c.rowid AND c.code='".$db->escape($thirdpartytouse->country_code)."'";
6452  $sql .= " ORDER BY t.taux DESC, t.recuperableonly ASC";
6453  $sql .= $db->plimit(1);
6454 
6455  $resql = $db->query($sql);
6456  if ($resql) {
6457  $obj = $db->fetch_object($resql);
6458  if ($obj) {
6459  if ($local == 1) {
6460  $ret = $obj->localtax1;
6461  } elseif ($local == 2) {
6462  $ret = $obj->localtax2;
6463  }
6464  }
6465  } else {
6466  dol_print_error($db);
6467  }
6468  }
6469 
6470  dol_syslog("get_product_localtax_for_country: ret=".$ret);
6471  return $ret;
6472 }
6473 
6490 function get_default_tva(Societe $thirdparty_seller, Societe $thirdparty_buyer, $idprod = 0, $idprodfournprice = 0)
6491 {
6492  global $conf;
6493 
6494  require_once DOL_DOCUMENT_ROOT.'/core/lib/company.lib.php';
6495 
6496  // Note: possible values for tva_assuj are 0/1 or franchise/reel
6497  $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;
6498 
6499  $seller_country_code = $thirdparty_seller->country_code;
6500  $seller_in_cee = isInEEC($thirdparty_seller);
6501 
6502  $buyer_country_code = $thirdparty_buyer->country_code;
6503  $buyer_in_cee = isInEEC($thirdparty_buyer);
6504 
6505  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 : ''));
6506 
6507  // 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)
6508  // we use the buyer VAT.
6509  if (!empty($conf->global->SERVICE_ARE_ECOMMERCE_200238EC)) {
6510  if ($seller_in_cee && $buyer_in_cee) {
6511  $isacompany = $thirdparty_buyer->isACompany();
6512  if ($isacompany && getDolGlobalString('MAIN_USE_VAT_COMPANIES_IN_EEC_WITH_INVALID_VAT_ID_ARE_INDIVIDUAL')) {
6513  require_once DOL_DOCUMENT_ROOT.'/core/lib/functions2.lib.php';
6514  if (!isValidVATID($thirdparty_buyer)) {
6515  $isacompany = 0;
6516  }
6517  }
6518 
6519  if (!$isacompany) {
6520  //print 'VATRULE 0';
6521  return get_product_vat_for_country($idprod, $thirdparty_buyer, $idprodfournprice);
6522  }
6523  }
6524  }
6525 
6526  // If seller does not use VAT
6527  if (!$seller_use_vat) {
6528  //print 'VATRULE 1';
6529  return 0;
6530  }
6531 
6532  // 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.
6533 
6534  // Si le (pays vendeur = pays acheteur) alors la TVA par defaut=TVA du produit vendu. Fin de regle.
6535  if (($seller_country_code == $buyer_country_code)
6536  || (in_array($seller_country_code, array('FR', 'MC')) && in_array($buyer_country_code, array('FR', 'MC')))) { // Warning ->country_code not always defined
6537  //print 'VATRULE 2';
6538  return get_product_vat_for_country($idprod, $thirdparty_seller, $idprodfournprice);
6539  }
6540 
6541  // 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.
6542  // 'VATRULE 3' - Not supported
6543 
6544  // Si (vendeur et acheteur dans Communaute europeenne) et (acheteur = entreprise) alors TVA par defaut=0. Fin de regle
6545  // Si (vendeur et acheteur dans Communaute europeenne) et (acheteur = particulier) alors TVA par defaut=TVA du produit vendu. Fin de regle
6546  if (($seller_in_cee && $buyer_in_cee)) {
6547  $isacompany = $thirdparty_buyer->isACompany();
6548  if ($isacompany && getDolGlobalString('MAIN_USE_VAT_COMPANIES_IN_EEC_WITH_INVALID_VAT_ID_ARE_INDIVIDUAL')) {
6549  require_once DOL_DOCUMENT_ROOT.'/core/lib/functions2.lib.php';
6550  if (!isValidVATID($thirdparty_buyer)) {
6551  $isacompany = 0;
6552  }
6553  }
6554 
6555  if (!$isacompany) {
6556  //print 'VATRULE 4';
6557  return get_product_vat_for_country($idprod, $thirdparty_seller, $idprodfournprice);
6558  } else {
6559  //print 'VATRULE 5';
6560  return 0;
6561  }
6562  }
6563 
6564  // Si (vendeur dans Communaute europeene et acheteur hors Communaute europeenne et acheteur particulier) alors TVA par defaut=TVA du produit vendu. Fin de regle
6565  // I don't see any use case that need this rule.
6566  if (!empty($conf->global->MAIN_USE_VAT_OF_PRODUCT_FOR_INDIVIDUAL_CUSTOMER_OUT_OF_EEC) && empty($buyer_in_cee)) {
6567  $isacompany = $thirdparty_buyer->isACompany();
6568  if (!$isacompany) {
6569  return get_product_vat_for_country($idprod, $thirdparty_seller, $idprodfournprice);
6570  //print 'VATRULE extra';
6571  }
6572  }
6573 
6574  // Sinon la TVA proposee par defaut=0. Fin de regle.
6575  // Rem: Cela signifie qu'au moins un des 2 est hors Communaute europeenne et que le pays differe
6576  //print 'VATRULE 6';
6577  return 0;
6578 }
6579 
6580 
6591 function get_default_npr(Societe $thirdparty_seller, Societe $thirdparty_buyer, $idprod = 0, $idprodfournprice = 0)
6592 {
6593  global $db;
6594 
6595  if ($idprodfournprice > 0) {
6596  if (!class_exists('ProductFournisseur')) {
6597  require_once DOL_DOCUMENT_ROOT.'/fourn/class/fournisseur.product.class.php';
6598  }
6599  $prodprice = new ProductFournisseur($db);
6600  $prodprice->fetch_product_fournisseur_price($idprodfournprice);
6601  return $prodprice->fourn_tva_npr;
6602  } elseif ($idprod > 0) {
6603  if (!class_exists('Product')) {
6604  require_once DOL_DOCUMENT_ROOT.'/product/class/product.class.php';
6605  }
6606  $prod = new Product($db);
6607  $prod->fetch($idprod);
6608  return $prod->tva_npr;
6609  }
6610 
6611  return 0;
6612 }
6613 
6627 function get_default_localtax($thirdparty_seller, $thirdparty_buyer, $local, $idprod = 0)
6628 {
6629  global $mysoc;
6630 
6631  if (!is_object($thirdparty_seller)) {
6632  return -1;
6633  }
6634  if (!is_object($thirdparty_buyer)) {
6635  return -1;
6636  }
6637 
6638  if ($local == 1) { // Localtax 1
6639  if ($mysoc->country_code == 'ES') {
6640  if (is_numeric($thirdparty_buyer->localtax1_assuj) && !$thirdparty_buyer->localtax1_assuj) {
6641  return 0;
6642  }
6643  } else {
6644  // Si vendeur non assujeti a Localtax1, localtax1 par default=0
6645  if (is_numeric($thirdparty_seller->localtax1_assuj) && !$thirdparty_seller->localtax1_assuj) {
6646  return 0;
6647  }
6648  if (!is_numeric($thirdparty_seller->localtax1_assuj) && $thirdparty_seller->localtax1_assuj == 'localtax1off') {
6649  return 0;
6650  }
6651  }
6652  } elseif ($local == 2) { //I Localtax 2
6653  // Si vendeur non assujeti a Localtax2, localtax2 par default=0
6654  if (is_numeric($thirdparty_seller->localtax2_assuj) && !$thirdparty_seller->localtax2_assuj) {
6655  return 0;
6656  }
6657  if (!is_numeric($thirdparty_seller->localtax2_assuj) && $thirdparty_seller->localtax2_assuj == 'localtax2off') {
6658  return 0;
6659  }
6660  }
6661 
6662  if ($thirdparty_seller->country_code == $thirdparty_buyer->country_code) {
6663  return get_product_localtax_for_country($idprod, $local, $thirdparty_seller);
6664  }
6665 
6666  return 0;
6667 }
6668 
6677 function yn($yesno, $case = 1, $color = 0)
6678 {
6679  global $langs;
6680 
6681  $result = 'unknown';
6682  $classname = '';
6683  if ($yesno == 1 || strtolower($yesno) == 'yes' || strtolower($yesno) == 'true') { // A mettre avant test sur no a cause du == 0
6684  $result = $langs->trans('yes');
6685  if ($case == 1 || $case == 3) {
6686  $result = $langs->trans("Yes");
6687  }
6688  if ($case == 2) {
6689  $result = '<input type="checkbox" value="1" checked disabled>';
6690  }
6691  if ($case == 3) {
6692  $result = '<input type="checkbox" value="1" checked disabled> '.$result;
6693  }
6694 
6695  $classname = 'ok';
6696  } elseif ($yesno == 0 || strtolower($yesno) == 'no' || strtolower($yesno) == 'false') {
6697  $result = $langs->trans("no");
6698  if ($case == 1 || $case == 3) {
6699  $result = $langs->trans("No");
6700  }
6701  if ($case == 2) {
6702  $result = '<input type="checkbox" value="0" disabled>';
6703  }
6704  if ($case == 3) {
6705  $result = '<input type="checkbox" value="0" disabled> '.$result;
6706  }
6707 
6708  if ($color == 2) {
6709  $classname = 'ok';
6710  } else {
6711  $classname = 'error';
6712  }
6713  }
6714  if ($color) {
6715  return '<span class="'.$classname.'">'.$result.'</span>';
6716  }
6717  return $result;
6718 }
6719 
6735 function get_exdir($num, $level, $alpha, $withoutslash, $object, $modulepart = '')
6736 {
6737  global $conf;
6738 
6739  if (empty($modulepart) && !empty($object->module)) {
6740  $modulepart = $object->module;
6741  }
6742 
6743  $path = '';
6744 
6745  $arrayforoldpath = array('cheque', 'category', 'holiday', 'supplier_invoice', 'invoice_supplier', 'mailing', 'supplier_payment');
6746  if (getDolGlobalInt('PRODUCT_USE_OLD_PATH_FOR_PHOTO')) {
6747  $arrayforoldpath[] = 'product';
6748  }
6749  if (!empty($level) && in_array($modulepart, $arrayforoldpath)) {
6750  // This part should be removed once all code is using "get_exdir" to forge path, with parameter $object and $modulepart provided.
6751  if (empty($alpha)) {
6752  $num = preg_replace('/([^0-9])/i', '', $num);
6753  } else {
6754  $num = preg_replace('/^.*\-/i', '', $num);
6755  }
6756  $num = substr("000".$num, -$level);
6757  if ($level == 1) {
6758  $path = substr($num, 0, 1);
6759  }
6760  if ($level == 2) {
6761  $path = substr($num, 1, 1).'/'.substr($num, 0, 1);
6762  }
6763  if ($level == 3) {
6764  $path = substr($num, 2, 1).'/'.substr($num, 1, 1).'/'.substr($num, 0, 1);
6765  }
6766  } else {
6767  // We will enhance here a common way of forging path for document storage.
6768  // In a future, we may distribute directories on several levels depending on setup and object.
6769  // Here, $object->id, $object->ref and $modulepart are required.
6770  //var_dump($modulepart);
6771  $path = dol_sanitizeFileName(empty($object->ref) ? (string) $object->id : $object->ref);
6772  }
6773 
6774  if (empty($withoutslash) && !empty($path)) {
6775  $path .= '/';
6776  }
6777 
6778  return $path;
6779 }
6780 
6789 function dol_mkdir($dir, $dataroot = '', $newmask = '')
6790 {
6791  global $conf;
6792 
6793  dol_syslog("functions.lib::dol_mkdir: dir=".$dir, LOG_INFO);
6794 
6795  $dir_osencoded = dol_osencode($dir);
6796  if (@is_dir($dir_osencoded)) {
6797  return 0;
6798  }
6799 
6800  $nberr = 0;
6801  $nbcreated = 0;
6802 
6803  $ccdir = '';
6804  if (!empty($dataroot)) {
6805  // Remove data root from loop
6806  $dir = str_replace($dataroot.'/', '', $dir);
6807  $ccdir = $dataroot.'/';
6808  }
6809 
6810  $cdir = explode("/", $dir);
6811  $num = count($cdir);
6812  for ($i = 0; $i < $num; $i++) {
6813  if ($i > 0) {
6814  $ccdir .= '/'.$cdir[$i];
6815  } else {
6816  $ccdir .= $cdir[$i];
6817  }
6818  if (preg_match("/^.:$/", $ccdir, $regs)) {
6819  continue; // Si chemin Windows incomplet, on poursuit par rep suivant
6820  }
6821 
6822  // Attention, le is_dir() peut echouer bien que le rep existe.
6823  // (ex selon config de open_basedir)
6824  if ($ccdir) {
6825  $ccdir_osencoded = dol_osencode($ccdir);
6826  if (!@is_dir($ccdir_osencoded)) {
6827  dol_syslog("functions.lib::dol_mkdir: Directory '".$ccdir."' does not exists or is outside open_basedir PHP setting.", LOG_DEBUG);
6828 
6829  umask(0);
6830  $dirmaskdec = octdec((string) $newmask);
6831  if (empty($newmask)) {
6832  $dirmaskdec = empty($conf->global->MAIN_UMASK) ? octdec('0755') : octdec($conf->global->MAIN_UMASK);
6833  }
6834  $dirmaskdec |= octdec('0111'); // Set x bit required for directories
6835  if (!@mkdir($ccdir_osencoded, $dirmaskdec)) {
6836  // Si le is_dir a renvoye une fausse info, alors on passe ici.
6837  dol_syslog("functions.lib::dol_mkdir: Fails to create directory '".$ccdir."' or directory already exists.", LOG_WARNING);
6838  $nberr++;
6839  } else {
6840  dol_syslog("functions.lib::dol_mkdir: Directory '".$ccdir."' created", LOG_DEBUG);
6841  $nberr = 0; // On remet a zero car si on arrive ici, cela veut dire que les echecs precedents peuvent etre ignore
6842  $nbcreated++;
6843  }
6844  } else {
6845  $nberr = 0; // On remet a zero car si on arrive ici, cela veut dire que les echecs precedents peuvent etre ignores
6846  }
6847  }
6848  }
6849  return ($nberr ? -$nberr : $nbcreated);
6850 }
6851 
6852 
6858 function picto_required()
6859 {
6860  return '<span class="fieldrequired">*</span>';
6861 }
6862 
6863 
6880 function dol_string_nohtmltag($stringtoclean, $removelinefeed = 1, $pagecodeto = 'UTF-8', $strip_tags = 0, $removedoublespaces = 1)
6881 {
6882  if (is_null($stringtoclean)) {
6883  return '';
6884  }
6885 
6886  if ($removelinefeed == 2) {
6887  $stringtoclean = preg_replace('/<br[^>]*>(\n|\r)+/ims', '<br>', $stringtoclean);
6888  }
6889  $temp = preg_replace('/<br[^>]*>/i', "\n", $stringtoclean);
6890 
6891  // We remove entities BEFORE stripping (in case of an open separator char that is entity encoded and not the closing other, the strip will fails)
6892  $temp = dol_html_entity_decode($temp, ENT_COMPAT | ENT_HTML5, $pagecodeto);
6893 
6894  $temp = str_replace('< ', '__ltspace__', $temp);
6895 
6896  if ($strip_tags) {
6897  $temp = strip_tags($temp);
6898  } else {
6899  // Remove '<' into remainging, so remove non closing html tags like '<abc' or '<<abc'. Note: '<123abc' is not a html tag (can be kept), but '<abc123' is (must be removed).
6900  $pattern = "/<[^<>]+>/";
6901  // Example of $temp: <a href="/myurl" title="<u>A title</u>">0000-021</a>
6902  // pass 1 - $temp after pass 1: <a href="/myurl" title="A title">0000-021
6903  // pass 2 - $temp after pass 2: 0000-021
6904  $tempbis = $temp;
6905  do {
6906  $temp = $tempbis;
6907  $tempbis = str_replace('<>', '', $temp); // No reason to have this into a text, except if value is to try bypass the next html cleaning
6908  $tempbis = preg_replace($pattern, '', $tempbis);
6909  //$idowhile++; print $temp.'-'.$tempbis."\n"; if ($idowhile > 100) break;
6910  } while ($tempbis != $temp);
6911 
6912  $temp = $tempbis;
6913 
6914  // Remove '<' into remaining, so remove non closing html tags like '<abc' or '<<abc'. Note: '<123abc' is not a html tag (can be kept), but '<abc123' is (must be removed).
6915  $temp = preg_replace('/<+([a-z]+)/i', '\1', $temp);
6916  }
6917 
6918  $temp = dol_html_entity_decode($temp, ENT_COMPAT, $pagecodeto);
6919 
6920  // Remove also carriage returns
6921  if ($removelinefeed == 1) {
6922  $temp = str_replace(array("\r\n", "\r", "\n"), " ", $temp);
6923  }
6924 
6925  // And double quotes
6926  if ($removedoublespaces) {
6927  while (strpos($temp, " ")) {
6928  $temp = str_replace(" ", " ", $temp);
6929  }
6930  }
6931 
6932  $temp = str_replace('__ltspace__', '< ', $temp);
6933 
6934  return trim($temp);
6935 }
6936 
6951 function dol_string_onlythesehtmltags($stringtoclean, $cleanalsosomestyles = 1, $removeclassattribute = 1, $cleanalsojavascript = 0, $allowiframe = 0, $allowed_tags = array())
6952 {
6953  if (empty($allowed_tags)) {
6954  $allowed_tags = array(
6955  "html", "head", "meta", "body", "article", "a", "abbr", "b", "blockquote", "br", "cite", "div", "dl", "dd", "dt", "em", "font", "img", "ins", "hr", "i", "li", "link",
6956  "ol", "p", "q", "s", "section", "span", "strike", "strong", "title", "table", "tr", "th", "td", "u", "ul", "sup", "sub", "blockquote", "pre", "h1", "h2", "h3", "h4", "h5", "h6"
6957  );
6958  }
6959  $allowed_tags[] = "comment"; // this tags is added to manage comment <!--...--> that are replaced into <comment>...</comment>
6960  if ($allowiframe) {
6961  $allowed_tags[] = "iframe";
6962  }
6963 
6964  $allowed_tags_string = join("><", $allowed_tags);
6965  $allowed_tags_string = '<'.$allowed_tags_string.'>';
6966 
6967  $stringtoclean = str_replace('<!DOCTYPE html>', '__!DOCTYPE_HTML__', $stringtoclean); // Replace DOCTYPE to avoid to have it removed by the strip_tags
6968 
6969  $stringtoclean = dol_string_nounprintableascii($stringtoclean, 0);
6970 
6971  //$stringtoclean = preg_replace('/<!--[^>]*-->/', '', $stringtoclean);
6972  $stringtoclean = preg_replace('/<!--([^>]*)-->/', '<comment>\1</comment>', $stringtoclean);
6973 
6974  $stringtoclean = preg_replace('/&colon;/i', ':', $stringtoclean);
6975  $stringtoclean = preg_replace('/&#58;|&#0+58|&#x3A/i', '', $stringtoclean); // refused string ':' encoded (no reason to have a : encoded like this) to disable 'javascript:...'
6976 
6977  $temp = strip_tags($stringtoclean, $allowed_tags_string); // Warning: This remove also undesired </>, so may changes string obfuscated with </> that pass the injection detection into a harmfull string
6978 
6979  if ($cleanalsosomestyles) { // Clean for remaining html tags
6980  $temp = preg_replace('/position\s*:\s*(absolute|fixed)\s*!\s*important/i', '', $temp); // Note: If hacker try to introduce css comment into string to bypass this regex, the string must also be encoded by the dol_htmlentitiesbr during output so it become harmless
6981  }
6982  if ($removeclassattribute) { // Clean for remaining html tags
6983  $temp = preg_replace('/(<[^>]+)\s+class=((["\']).*?\\3|\\w*)/i', '\\1', $temp);
6984  }
6985 
6986  // Remove 'javascript:' that we should not find into a text with
6987  // Warning: This is not reliable to fight against obfuscated javascript, there is a lot of other solution to include js into a common html tag (only filtered by a GETPOST(.., powerfullfilter)).