dolibarr  20.0.0-beta
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-2024 Frédéric France <frederic.france@free.fr>
17  * Copyright (C) 2019-2023 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  * Copyright (C) 2023 Joachim Kueter <git-jk@bloxera.com>
24  * Copyright (C) 2024 MDW <mdeweerd@users.noreply.github.com>
25  * Copyright (C) 2024 Lenin Rivas <lenin.rivas777@gmail.com>
26  *
27  * This program is free software; you can redistribute it and/or modify
28  * it under the terms of the GNU General Public License as published by
29  * the Free Software Foundation; either version 3 of the License, or
30  * (at your option) any later version.
31  *
32  * This program is distributed in the hope that it will be useful,
33  * but WITHOUT ANY WARRANTY; without even the implied warranty of
34  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
35  * GNU General Public License for more details.
36  *
37  * You should have received a copy of the GNU General Public License
38  * along with this program. If not, see <https://www.gnu.org/licenses/>.
39  * or see https://www.gnu.org/
40  */
41 
48 include_once DOL_DOCUMENT_ROOT.'/core/lib/json.lib.php';
49 
50 // Function for better PHP x compatibility
51 if (!function_exists('utf8_encode')) {
59  function utf8_encode($elements)
60  {
61  return mb_convert_encoding($elements, 'UTF-8', 'ISO-8859-1');
62  }
63 }
64 
65 if (!function_exists('utf8_decode')) {
73  function utf8_decode($elements)
74  {
75  return mb_convert_encoding($elements, 'ISO-8859-1', 'UTF-8');
76  }
77 }
78 if (!function_exists('str_starts_with')) {
87  function str_starts_with($haystack, $needle)
88  {
89  return (string) $needle !== '' && strncmp($haystack, $needle, strlen($needle)) === 0;
90  }
91 }
92 if (!function_exists('str_ends_with')) {
101  function str_ends_with($haystack, $needle)
102  {
103  return $needle !== '' && substr($haystack, -strlen($needle)) === (string) $needle;
104  }
105 }
106 if (!function_exists('str_contains')) {
115  function str_contains($haystack, $needle)
116  {
117  return $needle !== '' && mb_strpos($haystack, $needle) !== false;
118  }
119 }
120 
121 
133 function getMultidirOutput($object, $module = '', $forobject = 0, $mode = 'output')
134 {
135  global $conf;
136 
137  if (!is_object($object) && empty($module)) {
138  return null;
139  }
140  if (empty($module) && !empty($object->element)) {
141  $module = $object->element;
142  }
143 
144  // Special case for backward compatibility
145  if ($module == 'fichinter') {
146  $module = 'ficheinter';
147  }
148 
149  // Get the relative path of directory
150  if ($mode == 'output' || $mode == 'version') {
151  if (isset($conf->$module) && property_exists($conf->$module, 'multidir_output')) {
152  $s = $conf->$module->multidir_output[(empty($object->entity) ? $conf->entity : $object->entity)];
153  if ($forobject && $object->id > 0) {
154  $s .= '/'.get_exdir(0, 0, 0, 0, $object);
155  }
156  return $s;
157  } else {
158  return 'error-diroutput-not-defined-for-this-object='.$module;
159  }
160  } elseif ($mode == 'temp') {
161  if (isset($conf->$module) && property_exists($conf->$module, 'multidir_temp')) {
162  return $conf->$module->multidir_temp[(empty($object->entity) ? $conf->entity : $object->entity)];
163  } else {
164  return 'error-dirtemp-not-defined-for-this-object='.$module;
165  }
166  } else {
167  return 'error-bad-value-for-mode';
168  }
169 }
170 
180 function getMultidirTemp($object, $module = '', $forobject = 0)
181 {
182  return getMultidirOutput($object, $module, $forobject, 'temp');
183 }
184 
194 function getMultidirVersion($object, $module = '', $forobject = 0)
195 {
196  return getMultidirOutput($object, $module, $forobject, 'version');
197 }
198 
199 
208 function getDolGlobalString($key, $default = '')
209 {
210  global $conf;
211  return (string) (isset($conf->global->$key) ? $conf->global->$key : $default);
212 }
213 
223 function getDolGlobalInt($key, $default = 0)
224 {
225  global $conf;
226  return (int) (isset($conf->global->$key) ? $conf->global->$key : $default);
227 }
228 
237 function getDolUserString($key, $default = '', $tmpuser = null)
238 {
239  if (empty($tmpuser)) {
240  global $user;
241  $tmpuser = $user;
242  }
243 
244  return (string) (empty($tmpuser->conf->$key) ? $default : $tmpuser->conf->$key);
245 }
246 
255 function getDolUserInt($key, $default = 0, $tmpuser = null)
256 {
257  if (empty($tmpuser)) {
258  global $user;
259  $tmpuser = $user;
260  }
261 
262  return (int) (empty($tmpuser->conf->$key) ? $default : $tmpuser->conf->$key);
263 }
264 
265 
275 define(
276  'MODULE_MAPPING',
277  array(
278  // Map deprecated names to new names
279  'adherent' => 'member', // Has new directory
280  'member_type' => 'adherent_type', // No directory, but file called adherent_type
281  'banque' => 'bank', // Has new directory
282  'contrat' => 'contract', // Has new directory
283  'entrepot' => 'stock', // Has new directory
284  'projet' => 'project', // Has new directory
285  'categorie' => 'category', // Has old directory
286  'commande' => 'order', // Has old directory
287  'expedition' => 'shipping', // Has old directory
288  'facture' => 'invoice', // Has old directory
289  'fichinter' => 'intervention', // Has old directory
290  'ficheinter' => 'intervention', // Backup for 'fichinter'
291  'propale' => 'propal', // Has old directory
292  'socpeople' => 'contact', // Has old directory
293  'fournisseur' => 'supplier', // Has old directory
294 
295  'actioncomm' => 'agenda', // NO module directory (public dir agenda)
296  'product_price' => 'productprice', // NO directory
297  'product_fournisseur_price' => 'productsupplierprice', // NO directory
298  )
299 );
300 
307 function isModEnabled($module)
308 {
309  global $conf;
310 
311  // Fix old names (map to new names)
312  $arrayconv = MODULE_MAPPING;
313  $arrayconvbis = array_flip(MODULE_MAPPING);
314 
315  if (!getDolGlobalString('MAIN_USE_NEW_SUPPLIERMOD')) {
316  // Special cases: both use the same module.
317  $arrayconv['supplier_order'] = 'fournisseur';
318  $arrayconv['supplier_invoice'] = 'fournisseur';
319  }
320  // Special case.
321  // @TODO Replace isModEnabled('delivery_note') with
322  // isModEnabled('shipping') && getDolGlobalString('MAIN_SUBMODULE_EXPEDITION')
323  if ($module == 'delivery_note') {
324  if (!getDolGlobalString('MAIN_SUBMODULE_EXPEDITION')) {
325  return false;
326  } else {
327  $module = 'shipping';
328  }
329  }
330 
331  $module_alt = $module;
332  if (!empty($arrayconv[$module])) {
333  $module_alt = $arrayconv[$module];
334  }
335  $module_bis = $module;
336  if (!empty($arrayconvbis[$module])) {
337  $module_bis = $arrayconvbis[$module];
338  }
339 
340  return !empty($conf->modules[$module]) || !empty($conf->modules[$module_alt]) || !empty($conf->modules[$module_bis]);
341  //return !empty($conf->$module->enabled);
342 }
343 
350 function isDolTms($timestamp)
351 {
352  if ($timestamp === '') {
353  dol_syslog('Using empty string for a timestamp is deprecated, prefer use of null when calling page '.$_SERVER["PHP_SELF"], LOG_NOTICE);
354  return false;
355  }
356  if (is_null($timestamp) || !is_numeric($timestamp)) {
357  return false;
358  }
359 
360  return true;
361 }
362 
374 function getDoliDBInstance($type, $host, $user, $pass, $name, $port)
375 {
376  require_once DOL_DOCUMENT_ROOT."/core/db/".$type.'.class.php';
377 
378  $class = 'DoliDB'.ucfirst($type);
379  $db = new $class($type, $host, $user, $pass, $name, $port);
380  return $db;
381 }
382 
400 function getEntity($element, $shared = 1, $currentobject = null)
401 {
402  global $conf, $mc, $hookmanager, $object, $action, $db;
403 
404  if (!is_object($hookmanager)) {
405  include_once DOL_DOCUMENT_ROOT.'/core/class/hookmanager.class.php';
406  $hookmanager = new HookManager($db);
407  }
408 
409  // fix different element names (France to English)
410  switch ($element) {
411  case 'projet':
412  $element = 'project';
413  break;
414  case 'contrat':
415  $element = 'contract';
416  break; // "/contrat/class/contrat.class.php"
417  case 'order_supplier':
418  $element = 'supplier_order';
419  break; // "/fourn/class/fournisseur.commande.class.php"
420  case 'invoice_supplier':
421  $element = 'supplier_invoice';
422  break; // "/fourn/class/fournisseur.facture.class.php"
423  }
424 
425  if (is_object($mc)) {
426  $out = $mc->getEntity($element, $shared, $currentobject);
427  } else {
428  $out = '';
429  $addzero = array('user', 'usergroup', 'cronjob', 'c_email_templates', 'email_template', 'default_values', 'overwrite_trans');
430  if (in_array($element, $addzero)) {
431  $out .= '0,';
432  }
433  $out .= ((int) $conf->entity);
434  }
435 
436  // Manipulate entities to query on the fly
437  $parameters = array(
438  'element' => $element,
439  'shared' => $shared,
440  'object' => $object,
441  'currentobject' => $currentobject,
442  'out' => $out
443  );
444  $reshook = $hookmanager->executeHooks('hookGetEntity', $parameters, $currentobject, $action); // Note that $action and $object may have been modified by some hooks
445 
446  if (is_numeric($reshook)) {
447  if ($reshook == 0 && !empty($hookmanager->resPrint)) {
448  $out .= ','.$hookmanager->resPrint; // add
449  } elseif ($reshook == 1) {
450  $out = $hookmanager->resPrint; // replace
451  }
452  }
453 
454  return $out;
455 }
456 
463 function setEntity($currentobject)
464 {
465  global $conf, $mc;
466 
467  if (is_object($mc) && method_exists($mc, 'setEntity')) {
468  return $mc->setEntity($currentobject);
469  } else {
470  return ((is_object($currentobject) && $currentobject->id > 0 && $currentobject->entity > 0) ? $currentobject->entity : $conf->entity);
471  }
472 }
473 
480 function isASecretKey($keyname)
481 {
482  return preg_match('/(_pass|password|_pw|_key|securekey|serverkey|secret\d?|p12key|exportkey|_PW_[a-z]+|token)$/i', $keyname);
483 }
484 
485 
492 function num2Alpha($n)
493 {
494  for ($r = ""; $n >= 0; $n = intval($n / 26) - 1) {
495  $r = chr($n % 26 + 0x41) . $r;
496  }
497  return $r;
498 }
499 
500 
517 function getBrowserInfo($user_agent)
518 {
519  include_once DOL_DOCUMENT_ROOT.'/includes/mobiledetect/mobiledetectlib/Mobile_Detect.php';
520 
521  $name = 'unknown';
522  $version = '';
523  $os = 'unknown';
524  $phone = '';
525 
526  $user_agent = substr($user_agent, 0, 512); // Avoid to process too large user agent
527 
528  $detectmobile = new Mobile_Detect(null, $user_agent);
529  $tablet = $detectmobile->isTablet();
530 
531  if ($detectmobile->isMobile()) {
532  $phone = 'unknown';
533 
534  // If phone/smartphone, we set phone os name.
535  if ($detectmobile->is('AndroidOS')) {
536  $os = $phone = 'android';
537  } elseif ($detectmobile->is('BlackBerryOS')) {
538  $os = $phone = 'blackberry';
539  } elseif ($detectmobile->is('iOS')) {
540  $os = 'ios';
541  $phone = 'iphone';
542  } elseif ($detectmobile->is('PalmOS')) {
543  $os = $phone = 'palm';
544  } elseif ($detectmobile->is('SymbianOS')) {
545  $os = 'symbian';
546  } elseif ($detectmobile->is('webOS')) {
547  $os = 'webos';
548  } elseif ($detectmobile->is('MaemoOS')) {
549  $os = 'maemo';
550  } elseif ($detectmobile->is('WindowsMobileOS') || $detectmobile->is('WindowsPhoneOS')) {
551  $os = 'windows';
552  }
553  }
554 
555  // OS
556  if (preg_match('/linux/i', $user_agent)) {
557  $os = 'linux';
558  } elseif (preg_match('/macintosh/i', $user_agent)) {
559  $os = 'macintosh';
560  } elseif (preg_match('/windows/i', $user_agent)) {
561  $os = 'windows';
562  }
563 
564  // Name
565  $reg = array();
566  if (preg_match('/firefox(\/|\s)([\d\.]*)/i', $user_agent, $reg)) {
567  $name = 'firefox';
568  $version = empty($reg[2]) ? '' : $reg[2];
569  } elseif (preg_match('/edge(\/|\s)([\d\.]*)/i', $user_agent, $reg)) {
570  $name = 'edge';
571  $version = empty($reg[2]) ? '' : $reg[2];
572  } elseif (preg_match('/chrome(\/|\s)([\d\.]+)/i', $user_agent, $reg)) {
573  $name = 'chrome';
574  $version = empty($reg[2]) ? '' : $reg[2];
575  } elseif (preg_match('/chrome/i', $user_agent, $reg)) {
576  // we can have 'chrome (Mozilla...) chrome x.y' in one string
577  $name = 'chrome';
578  } elseif (preg_match('/iceweasel/i', $user_agent)) {
579  $name = 'iceweasel';
580  } elseif (preg_match('/epiphany/i', $user_agent)) {
581  $name = 'epiphany';
582  } elseif (preg_match('/safari(\/|\s)([\d\.]*)/i', $user_agent, $reg)) {
583  $name = 'safari';
584  $version = empty($reg[2]) ? '' : $reg[2];
585  } elseif (preg_match('/opera(\/|\s)([\d\.]*)/i', $user_agent, $reg)) {
586  // Safari is often present in string for mobile but its not.
587  $name = 'opera';
588  $version = empty($reg[2]) ? '' : $reg[2];
589  } elseif (preg_match('/(MSIE\s([0-9]+\.[0-9]))|.*(Trident\/[0-9]+.[0-9];.*rv:([0-9]+\.[0-9]+))/i', $user_agent, $reg)) {
590  $name = 'ie';
591  $version = end($reg);
592  } elseif (preg_match('/(Windows NT\s([0-9]+\.[0-9])).*(Trident\/[0-9]+.[0-9];.*rv:([0-9]+\.[0-9]+))/i', $user_agent, $reg)) {
593  // MS products at end
594  $name = 'ie';
595  $version = end($reg);
596  } elseif (preg_match('/l[iy]n(x|ks)(\‍(|\/|\s)*([\d\.]+)/i', $user_agent, $reg)) {
597  // MS products at end
598  $name = 'lynxlinks';
599  $version = empty($reg[3]) ? '' : $reg[3];
600  }
601 
602  if ($tablet) {
603  $layout = 'tablet';
604  } elseif ($phone) {
605  $layout = 'phone';
606  } else {
607  $layout = 'classic';
608  }
609 
610  return array(
611  'browsername' => $name,
612  'browserversion' => $version,
613  'browseros' => $os,
614  'browserua' => $user_agent,
615  'layout' => $layout, // tablet, phone, classic
616  'phone' => $phone, // deprecated
617  'tablet' => $tablet // deprecated
618  );
619 }
620 
626 function dol_shutdown()
627 {
628  global $db;
629  $disconnectdone = false;
630  $depth = 0;
631  if (is_object($db) && !empty($db->connected)) {
632  $depth = $db->transaction_opened;
633  $disconnectdone = $db->close();
634  }
635  dol_syslog("--- End access to ".(empty($_SERVER["PHP_SELF"]) ? 'unknown' : $_SERVER["PHP_SELF"]).(($disconnectdone && $depth) ? ' (Warn: db disconnection forced, transaction depth was '.$depth.')' : ''), (($disconnectdone && $depth) ? LOG_WARNING : LOG_INFO));
636 }
637 
647 function GETPOSTISSET($paramname)
648 {
649  $isset = false;
650 
651  $relativepathstring = $_SERVER["PHP_SELF"];
652  // Clean $relativepathstring
653  if (constant('DOL_URL_ROOT')) {
654  $relativepathstring = preg_replace('/^'.preg_quote(constant('DOL_URL_ROOT'), '/').'/', '', $relativepathstring);
655  }
656  $relativepathstring = preg_replace('/^\//', '', $relativepathstring);
657  $relativepathstring = preg_replace('/^custom\//', '', $relativepathstring);
658  //var_dump($relativepathstring);
659  //var_dump($user->default_values);
660 
661  // Code for search criteria persistence.
662  // Retrieve values if restore_lastsearch_values
663  if (!empty($_GET['restore_lastsearch_values'])) { // Use $_GET here and not GETPOST
664  if (!empty($_SESSION['lastsearch_values_'.$relativepathstring])) { // If there is saved values
665  $tmp = json_decode($_SESSION['lastsearch_values_'.$relativepathstring], true);
666  if (is_array($tmp)) {
667  foreach ($tmp as $key => $val) {
668  if ($key == $paramname) { // We are on the requested parameter
669  $isset = true;
670  break;
671  }
672  }
673  }
674  }
675  // If there is saved contextpage, limit, page or mode
676  if ($paramname == 'contextpage' && !empty($_SESSION['lastsearch_contextpage_'.$relativepathstring])) {
677  $isset = true;
678  } elseif ($paramname == 'limit' && !empty($_SESSION['lastsearch_limit_'.$relativepathstring])) {
679  $isset = true;
680  } elseif ($paramname == 'page' && !empty($_SESSION['lastsearch_page_'.$relativepathstring])) {
681  $isset = true;
682  } elseif ($paramname == 'mode' && !empty($_SESSION['lastsearch_mode_'.$relativepathstring])) {
683  $isset = true;
684  }
685  } else {
686  $isset = (isset($_POST[$paramname]) || isset($_GET[$paramname])); // We must keep $_POST and $_GET here
687  }
688 
689  return $isset;
690 }
691 
700 function GETPOSTISARRAY($paramname, $method = 0)
701 {
702  // for $method test need return the same $val as GETPOST
703  if (empty($method)) {
704  $val = isset($_GET[$paramname]) ? $_GET[$paramname] : (isset($_POST[$paramname]) ? $_POST[$paramname] : '');
705  } elseif ($method == 1) {
706  $val = isset($_GET[$paramname]) ? $_GET[$paramname] : '';
707  } elseif ($method == 2) {
708  $val = isset($_POST[$paramname]) ? $_POST[$paramname] : '';
709  } elseif ($method == 3) {
710  $val = isset($_POST[$paramname]) ? $_POST[$paramname] : (isset($_GET[$paramname]) ? $_GET[$paramname] : '');
711  } else {
712  $val = 'BadFirstParameterForGETPOST';
713  }
714 
715  return is_array($val);
716 }
717 
747 function GETPOST($paramname, $check = 'alphanohtml', $method = 0, $filter = null, $options = null, $noreplace = 0)
748 {
749  global $mysoc, $user, $conf;
750 
751  if (empty($paramname)) { // Explicit test for null for phan.
752  return 'BadFirstParameterForGETPOST';
753  }
754  if (empty($check)) {
755  dol_syslog("Deprecated use of GETPOST, called with 1st param = ".$paramname." and a 2nd param that is '', when calling page ".$_SERVER["PHP_SELF"], LOG_WARNING);
756  // Enable this line to know who call the GETPOST with '' $check parameter.
757  //var_dump(debug_backtrace()[0]);
758  }
759 
760  if (empty($method)) {
761  $out = isset($_GET[$paramname]) ? $_GET[$paramname] : (isset($_POST[$paramname]) ? $_POST[$paramname] : '');
762  } elseif ($method == 1) {
763  $out = isset($_GET[$paramname]) ? $_GET[$paramname] : '';
764  } elseif ($method == 2) {
765  $out = isset($_POST[$paramname]) ? $_POST[$paramname] : '';
766  } elseif ($method == 3) {
767  $out = isset($_POST[$paramname]) ? $_POST[$paramname] : (isset($_GET[$paramname]) ? $_GET[$paramname] : '');
768  } else {
769  return 'BadThirdParameterForGETPOST';
770  }
771 
772  $relativepathstring = ''; // For static analysis - looks possibly undefined if not set.
773 
774  if (empty($method) || $method == 3 || $method == 4) {
775  $relativepathstring = (empty($_SERVER["PHP_SELF"]) ? '' : $_SERVER["PHP_SELF"]);
776  // Clean $relativepathstring
777  if (constant('DOL_URL_ROOT')) {
778  $relativepathstring = preg_replace('/^'.preg_quote(constant('DOL_URL_ROOT'), '/').'/', '', $relativepathstring);
779  }
780  $relativepathstring = preg_replace('/^\//', '', $relativepathstring);
781  $relativepathstring = preg_replace('/^custom\//', '', $relativepathstring);
782  //var_dump($relativepathstring);
783  //var_dump($user->default_values);
784 
785  // Code for search criteria persistence.
786  // Retrieve saved values if restore_lastsearch_values is set
787  if (!empty($_GET['restore_lastsearch_values'])) { // Use $_GET here and not GETPOST
788  if (!empty($_SESSION['lastsearch_values_'.$relativepathstring])) { // If there is saved values
789  $tmp = json_decode($_SESSION['lastsearch_values_'.$relativepathstring], true);
790  if (is_array($tmp)) {
791  foreach ($tmp as $key => $val) {
792  if ($key == $paramname) { // We are on the requested parameter
793  $out = $val;
794  break;
795  }
796  }
797  }
798  }
799  // If there is saved contextpage, page or limit
800  if ($paramname == 'contextpage' && !empty($_SESSION['lastsearch_contextpage_'.$relativepathstring])) {
801  $out = $_SESSION['lastsearch_contextpage_'.$relativepathstring];
802  } elseif ($paramname == 'limit' && !empty($_SESSION['lastsearch_limit_'.$relativepathstring])) {
803  $out = $_SESSION['lastsearch_limit_'.$relativepathstring];
804  } elseif ($paramname == 'page' && !empty($_SESSION['lastsearch_page_'.$relativepathstring])) {
805  $out = $_SESSION['lastsearch_page_'.$relativepathstring];
806  } elseif ($paramname == 'mode' && !empty($_SESSION['lastsearch_mode_'.$relativepathstring])) {
807  $out = $_SESSION['lastsearch_mode_'.$relativepathstring];
808  }
809  } elseif (!isset($_GET['sortfield'])) {
810  // Else, retrieve default values if we are not doing a sort
811  // 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
812  if (!empty($_GET['action']) && $_GET['action'] == 'create' && !isset($_GET[$paramname]) && !isset($_POST[$paramname])) {
813  // Search default value from $object->field
814  global $object;
815  '@phan-var-force CommonObject $object'; // Suppose it's a CommonObject for analysis, but other objects have the $fields field as well
816  if (is_object($object) && isset($object->fields[$paramname]['default'])) {
817  // @phan-suppress-next-line PhanTypePossiblyInvalidDimOffset
818  $out = $object->fields[$paramname]['default'];
819  }
820  }
821  if (getDolGlobalString('MAIN_ENABLE_DEFAULT_VALUES')) {
822  if (!empty($_GET['action']) && (preg_match('/^create/', $_GET['action']) || preg_match('/^presend/', $_GET['action'])) && !isset($_GET[$paramname]) && !isset($_POST[$paramname])) {
823  // Now search in setup to overwrite default values
824  if (!empty($user->default_values)) { // $user->default_values defined from menu 'Setup - Default values'
825  if (isset($user->default_values[$relativepathstring]['createform'])) {
826  foreach ($user->default_values[$relativepathstring]['createform'] as $defkey => $defval) {
827  $qualified = 0;
828  if ($defkey != '_noquery_') {
829  $tmpqueryarraytohave = explode('&', $defkey);
830  $tmpqueryarraywehave = explode('&', dol_string_nohtmltag($_SERVER['QUERY_STRING']));
831  $foundintru = 0;
832  foreach ($tmpqueryarraytohave as $tmpquerytohave) {
833  if (!in_array($tmpquerytohave, $tmpqueryarraywehave)) {
834  $foundintru = 1;
835  }
836  }
837  if (!$foundintru) {
838  $qualified = 1;
839  }
840  //var_dump($defkey.'-'.$qualified);
841  } else {
842  $qualified = 1;
843  }
844 
845  if ($qualified) {
846  if (isset($user->default_values[$relativepathstring]['createform'][$defkey][$paramname])) {
847  $out = $user->default_values[$relativepathstring]['createform'][$defkey][$paramname];
848  break;
849  }
850  }
851  }
852  }
853  }
854  } elseif (!empty($paramname) && !isset($_GET[$paramname]) && !isset($_POST[$paramname])) {
855  // Management of default search_filters and sort order
856  if (!empty($user->default_values)) {
857  // $user->default_values defined from menu 'Setup - Default values'
858  //var_dump($user->default_values[$relativepathstring]);
859  if ($paramname == 'sortfield' || $paramname == 'sortorder') {
860  // Sorted on which fields ? ASC or DESC ?
861  if (isset($user->default_values[$relativepathstring]['sortorder'])) {
862  // Even if paramname is sortfield, data are stored into ['sortorder...']
863  foreach ($user->default_values[$relativepathstring]['sortorder'] as $defkey => $defval) {
864  $qualified = 0;
865  if ($defkey != '_noquery_') {
866  $tmpqueryarraytohave = explode('&', $defkey);
867  $tmpqueryarraywehave = explode('&', dol_string_nohtmltag($_SERVER['QUERY_STRING']));
868  $foundintru = 0;
869  foreach ($tmpqueryarraytohave as $tmpquerytohave) {
870  if (!in_array($tmpquerytohave, $tmpqueryarraywehave)) {
871  $foundintru = 1;
872  }
873  }
874  if (!$foundintru) {
875  $qualified = 1;
876  }
877  //var_dump($defkey.'-'.$qualified);
878  } else {
879  $qualified = 1;
880  }
881 
882  if ($qualified) {
883  $forbidden_chars_to_replace = array(" ", "'", "/", "\\", ":", "*", "?", "\"", "<", ">", "|", "[", "]", ";", "="); // we accept _, -, . and ,
884  foreach ($user->default_values[$relativepathstring]['sortorder'][$defkey] as $key => $val) {
885  if ($out) {
886  $out .= ', ';
887  }
888  if ($paramname == 'sortfield') {
889  $out .= dol_string_nospecial($key, '', $forbidden_chars_to_replace);
890  }
891  if ($paramname == 'sortorder') {
892  $out .= dol_string_nospecial($val, '', $forbidden_chars_to_replace);
893  }
894  }
895  //break; // No break for sortfield and sortorder so we can cumulate fields (is it really useful ?)
896  }
897  }
898  }
899  } elseif (isset($user->default_values[$relativepathstring]['filters'])) {
900  foreach ($user->default_values[$relativepathstring]['filters'] as $defkey => $defval) { // $defkey is a querystring like 'a=b&c=d', $defval is key of user
901  if (!empty($_GET['disabledefaultvalues'])) { // If set of default values has been disabled by a request parameter
902  continue;
903  }
904  $qualified = 0;
905  if ($defkey != '_noquery_') {
906  $tmpqueryarraytohave = explode('&', $defkey);
907  $tmpqueryarraywehave = explode('&', dol_string_nohtmltag($_SERVER['QUERY_STRING']));
908  $foundintru = 0;
909  foreach ($tmpqueryarraytohave as $tmpquerytohave) {
910  if (!in_array($tmpquerytohave, $tmpqueryarraywehave)) {
911  $foundintru = 1;
912  }
913  }
914  if (!$foundintru) {
915  $qualified = 1;
916  }
917  //var_dump($defkey.'-'.$qualified);
918  } else {
919  $qualified = 1;
920  }
921 
922  if ($qualified && isset($user->default_values[$relativepathstring]['filters'][$defkey][$paramname])) {
923  // We must keep $_POST and $_GET here
924  if (isset($_POST['sall']) || isset($_POST['search_all']) || isset($_GET['sall']) || isset($_GET['search_all'])) {
925  // We made a search from quick search menu, do we still use default filter ?
926  if (!getDolGlobalString('MAIN_DISABLE_DEFAULT_FILTER_FOR_QUICK_SEARCH')) {
927  $forbidden_chars_to_replace = array(" ", "'", "/", "\\", ":", "*", "?", "\"", "<", ">", "|", "[", "]", ";", "="); // we accept _, -, . and ,
928  $out = dol_string_nospecial($user->default_values[$relativepathstring]['filters'][$defkey][$paramname], '', $forbidden_chars_to_replace);
929  }
930  } else {
931  $forbidden_chars_to_replace = array(" ", "'", "/", "\\", ":", "*", "?", "\"", "<", ">", "|", "[", "]", ";", "="); // we accept _, -, . and ,
932  $out = dol_string_nospecial($user->default_values[$relativepathstring]['filters'][$defkey][$paramname], '', $forbidden_chars_to_replace);
933  }
934  break;
935  }
936  }
937  }
938  }
939  }
940  }
941  }
942  }
943 
944  // Substitution variables for GETPOST (used to get final url with variable parameters or final default value, when using variable parameters __XXX__ in the GET URL)
945  // Example of variables: __DAY__, __MONTH__, __YEAR__, __MYCOMPANY_COUNTRY_ID__, __USER_ID__, ...
946  // 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.
947  '@phan-var-force string $paramname';
948  if (!is_array($out) && empty($_POST[$paramname]) && empty($noreplace)) {
949  $reg = array();
950  $maxloop = 20;
951  $loopnb = 0; // Protection against infinite loop
952  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.
953  $loopnb++;
954  $newout = '';
955 
956  if ($reg[1] == 'DAY') {
957  $tmp = dol_getdate(dol_now(), true);
958  $newout = $tmp['mday'];
959  } elseif ($reg[1] == 'MONTH') {
960  $tmp = dol_getdate(dol_now(), true);
961  $newout = $tmp['mon'];
962  } elseif ($reg[1] == 'YEAR') {
963  $tmp = dol_getdate(dol_now(), true);
964  $newout = $tmp['year'];
965  } elseif ($reg[1] == 'PREVIOUS_DAY') {
966  $tmp = dol_getdate(dol_now(), true);
967  $tmp2 = dol_get_prev_day($tmp['mday'], $tmp['mon'], $tmp['year']);
968  $newout = $tmp2['day'];
969  } elseif ($reg[1] == 'PREVIOUS_MONTH') {
970  $tmp = dol_getdate(dol_now(), true);
971  $tmp2 = dol_get_prev_month($tmp['mon'], $tmp['year']);
972  $newout = $tmp2['month'];
973  } elseif ($reg[1] == 'PREVIOUS_YEAR') {
974  $tmp = dol_getdate(dol_now(), true);
975  $newout = ($tmp['year'] - 1);
976  } elseif ($reg[1] == 'NEXT_DAY') {
977  $tmp = dol_getdate(dol_now(), true);
978  $tmp2 = dol_get_next_day($tmp['mday'], $tmp['mon'], $tmp['year']);
979  $newout = $tmp2['day'];
980  } elseif ($reg[1] == 'NEXT_MONTH') {
981  $tmp = dol_getdate(dol_now(), true);
982  $tmp2 = dol_get_next_month($tmp['mon'], $tmp['year']);
983  $newout = $tmp2['month'];
984  } elseif ($reg[1] == 'NEXT_YEAR') {
985  $tmp = dol_getdate(dol_now(), true);
986  $newout = ($tmp['year'] + 1);
987  } elseif ($reg[1] == 'MYCOMPANY_COUNTRY_ID' || $reg[1] == 'MYCOUNTRY_ID' || $reg[1] == 'MYCOUNTRYID') {
988  $newout = $mysoc->country_id;
989  } elseif ($reg[1] == 'USER_ID' || $reg[1] == 'USERID') {
990  $newout = $user->id;
991  } elseif ($reg[1] == 'USER_SUPERVISOR_ID' || $reg[1] == 'SUPERVISOR_ID' || $reg[1] == 'SUPERVISORID') {
992  $newout = $user->fk_user;
993  } elseif ($reg[1] == 'ENTITY_ID' || $reg[1] == 'ENTITYID') {
994  $newout = $conf->entity;
995  } else {
996  $newout = ''; // Key not found, we replace with empty string
997  }
998  //var_dump('__'.$reg[1].'__ -> '.$newout);
999  $out = preg_replace('/__'.preg_quote($reg[1], '/').'__/', $newout, $out);
1000  }
1001  }
1002 
1003  // Check type of variable and make sanitization according to this
1004  if (preg_match('/^array/', $check)) { // If 'array' or 'array:restricthtml' or 'array:aZ09' or 'array:intcomma'
1005  if (!is_array($out) || empty($out)) {
1006  $out = array();
1007  } else {
1008  $tmparray = explode(':', $check);
1009  if (!empty($tmparray[1])) {
1010  $tmpcheck = $tmparray[1];
1011  } else {
1012  $tmpcheck = 'alphanohtml';
1013  }
1014  foreach ($out as $outkey => $outval) {
1015  $out[$outkey] = sanitizeVal($outval, $tmpcheck, $filter, $options);
1016  }
1017  }
1018  } else {
1019  // If field name is 'search_xxx' then we force the add of space after each < and > (when following char is numeric) because it means
1020  // 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
1021  if (strpos($paramname, 'search_') === 0) {
1022  $out = preg_replace('/([<>])([-+]?\d)/', '\1 \2', $out);
1023  }
1024 
1025  // @phan-suppress-next-line UnknownSanitizeType
1026  $out = sanitizeVal($out, $check, $filter, $options);
1027  }
1028 
1029  // Sanitizing for special parameters.
1030  // Note: There is no reason to allow the backtopage, backtolist or backtourl parameter to contains an external URL. Only relative URLs are allowed.
1031  if ($paramname == 'backtopage' || $paramname == 'backtolist' || $paramname == 'backtourl') {
1032  $out = str_replace('\\', '/', $out); // Can be before the loop because only 1 char is replaced. No risk to get it after other replacements.
1033  $out = str_replace(array(':', ';', '@', "\t", ' '), '', $out); // Can be before the loop because only 1 char is replaced. No risk to retrieve it after other replacements.
1034  do {
1035  $oldstringtoclean = $out;
1036  $out = str_ireplace(array('javascript', 'vbscript', '&colon', '&#'), '', $out);
1037  $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'
1038  $out = preg_replace(array('/^[a-z]*\/\s*\/+/i'), '', $out); // We remove schema*// to remove external URL
1039  } while ($oldstringtoclean != $out);
1040  }
1041 
1042  // Code for search criteria persistence.
1043  // Save data into session if key start with 'search_'
1044  if (empty($method) || $method == 3 || $method == 4) {
1045  if (preg_match('/^search_/', $paramname) || in_array($paramname, array('sortorder', 'sortfield'))) {
1046  //var_dump($paramname.' - '.$out.' '.$user->default_values[$relativepathstring]['filters'][$paramname]);
1047 
1048  // We save search key only if $out not empty that means:
1049  // - posted value not empty, or
1050  // - 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).
1051 
1052  if ($out != '' && isset($user)) {// $out = '0' or 'abc', it is a search criteria to keep
1053  $user->lastsearch_values_tmp[$relativepathstring][$paramname] = $out;
1054  }
1055  }
1056  }
1057 
1058  return $out;
1059 }
1060 
1070 function GETPOSTINT($paramname, $method = 0)
1071 {
1072  return (int) GETPOST($paramname, 'int', $method, null, null, 0);
1073 }
1074 
1075 
1084 function GETPOSTFLOAT($paramname, $rounding = '')
1085 {
1086  // price2num() is used to sanitize any valid user input (such as "1 234.5", "1 234,5", "1'234,5", "1·234,5", "1,234.5", etc.)
1087  return (float) price2num(GETPOST($paramname), $rounding, 2);
1088 }
1089 
1090 
1101 function checkVal($out = '', $check = 'alphanohtml', $filter = null, $options = null)
1102 {
1103  return sanitizeVal($out, $check, $filter, $options);
1104 }
1105 
1115 function sanitizeVal($out = '', $check = 'alphanohtml', $filter = null, $options = null)
1116 {
1117  // TODO : use class "Validate" to perform tests (and add missing tests) if needed for factorize
1118  // Check is done after replacement
1119  switch ($check) {
1120  case 'none':
1121  break;
1122  case 'int': // Check param is a numeric value (integer but also float or hexadecimal)
1123  if (!is_numeric($out)) {
1124  $out = '';
1125  }
1126  break;
1127  case 'intcomma':
1128  if (is_array($out)) {
1129  $out = implode(',', $out);
1130  }
1131  if (preg_match('/[^0-9,-]+/i', $out)) {
1132  $out = '';
1133  }
1134  break;
1135  case 'san_alpha':
1136  $out = filter_var($out, FILTER_SANITIZE_STRING);
1137  break;
1138  case 'email':
1139  $out = filter_var($out, FILTER_SANITIZE_EMAIL);
1140  break;
1141  case 'aZ':
1142  if (!is_array($out)) {
1143  $out = trim($out);
1144  if (preg_match('/[^a-z]+/i', $out)) {
1145  $out = '';
1146  }
1147  }
1148  break;
1149  case 'aZ09':
1150  if (!is_array($out)) {
1151  $out = trim($out);
1152  if (preg_match('/[^a-z0-9_\-\.]+/i', $out)) {
1153  $out = '';
1154  }
1155  }
1156  break;
1157  case 'aZ09arobase': // great to sanitize $objecttype parameter
1158  if (!is_array($out)) {
1159  $out = trim($out);
1160  if (preg_match('/[^a-z0-9_\-\.@]+/i', $out)) {
1161  $out = '';
1162  }
1163  }
1164  break;
1165  case 'aZ09comma': // great to sanitize $sortfield or $sortorder params that can be 't.abc,t.def_gh'
1166  if (!is_array($out)) {
1167  $out = trim($out);
1168  if (preg_match('/[^a-z0-9_\-\.,]+/i', $out)) {
1169  $out = '';
1170  }
1171  }
1172  break;
1173  case 'alpha': // No html and no ../ and "
1174  case 'alphanohtml': // Recommended for most scalar parameters and search parameters
1175  if (!is_array($out)) {
1176  $out = trim($out);
1177  do {
1178  $oldstringtoclean = $out;
1179  // Remove html tags
1180  $out = dol_string_nohtmltag($out, 0);
1181  // Convert '\' used for windows path into '/' so we can use for path but not for octal syntax \999, hexa syntax \x999 and unicode syntax \u{999}
1182  $out = str_ireplace('\\', '/', $out);
1183  // Remove also other dangerous string sequences
1184  // '../' or '..\' is dangerous because it allows dir transversals
1185  // '&#38', '&#0000038', '&#x26'... is a the char '&' alone but there is no reason to accept such way to encode input char
1186  // '"' = '&#34' = '&#0000034' = '&#x22' is dangerous because param in url can close the href= or src= and add javascript functions.
1187  // '&#47', '&#0000047', '&#x2F' is the char '/' but there is no reason to accept such way to encode this input char
1188  // '&#92' = '&#0000092' = '&#x5C' is the char '\' but there is no reason to accept such way to encode this input char
1189  $out = str_ireplace(array('../', '..\\', '&#38', '&#0000038', '&#x26', '&quot', '"', '&#34', '&#0000034', '&#x22', '&#47', '&#0000047', '&#x2F', '&#92', '&#0000092', '&#x5C'), '', $out);
1190  } while ($oldstringtoclean != $out);
1191  // keep lines feed
1192  }
1193  break;
1194  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'
1195  if (!is_array($out)) {
1196  $out = trim($out);
1197  do {
1198  $oldstringtoclean = $out;
1199  // Decode html entities
1200  $out = dol_html_entity_decode($out, ENT_COMPAT | ENT_HTML5, 'UTF-8');
1201  // Convert '\' used for windows path into '/' so we can use for path but not for octal syntax \999, hexa syntax \x999 and unicode syntax \u{999}
1202  $out = str_ireplace('\\', '/', $out);
1203  // Remove also other dangerous string sequences
1204  // '../' or '..\' is dangerous because it allows dir transversals
1205  // '&#38', '&#0000038', '&#x26'... is a the char '&' alone but there is no reason to accept such way to encode input char
1206  // '"' = '&#34' = '&#0000034' = '&#x22' is dangerous because param in url can close the href= or src= and add javascript functions.
1207  // '&#47', '&#0000047', '&#x2F' is the char '/' but there is no reason to accept such way to encode this input char
1208  // '&#92' = '&#0000092' = '&#x5C' is the char '\' but there is no reason to accept such way to encode this input char
1209  $out = str_ireplace(array('../', '..\\', '&#38', '&#0000038', '&#x26', '&quot', '"', '&#34', '&#0000034', '&#x22', '&#47', '&#0000047', '&#x2F', '&#92', '&#0000092', '&#x5C'), '', $out);
1210  } while ($oldstringtoclean != $out);
1211  }
1212  break;
1213  case 'nohtml': // No html
1214  $out = dol_string_nohtmltag($out, 0);
1215  break;
1216  case 'restricthtmlnolink':
1217  case 'restricthtml': // Recommended for most html textarea
1218  case 'restricthtmlallowclass':
1219  case 'restricthtmlallowunvalid':
1220  $out = dol_htmlwithnojs($out, 1, $check);
1221  break;
1222  case 'custom':
1223  if (!empty($out)) {
1224  if (empty($filter)) {
1225  return 'BadParameterForGETPOST - Param 3 of sanitizeVal()';
1226  }
1227  if (is_null($options)) {
1228  $options = 0;
1229  }
1230  $out = filter_var($out, $filter, $options);
1231  }
1232  break;
1233  default:
1234  dol_syslog("Error, you call sanitizeVal() with a bad value for the check type. Data will be sanitized with alphanohtml.", LOG_ERR);
1235  $out = GETPOST($out, 'alphanohtml');
1236  break;
1237  }
1238 
1239  return $out;
1240 }
1241 
1242 
1243 if (!function_exists('dol_getprefix')) {
1254  function dol_getprefix($mode = '')
1255  {
1256  // If prefix is for email (we need to have $conf already loaded for this case)
1257  if ($mode == 'email') {
1258  global $conf;
1259 
1260  if (getDolGlobalString('MAIL_PREFIX_FOR_EMAIL_ID')) { // If MAIL_PREFIX_FOR_EMAIL_ID is set
1261  if (getDolGlobalString('MAIL_PREFIX_FOR_EMAIL_ID') != 'SERVER_NAME') {
1262  return $conf->global->MAIL_PREFIX_FOR_EMAIL_ID;
1263  } elseif (isset($_SERVER["SERVER_NAME"])) { // If MAIL_PREFIX_FOR_EMAIL_ID is set to 'SERVER_NAME'
1264  return $_SERVER["SERVER_NAME"];
1265  }
1266  }
1267 
1268  // The recommended value if MAIL_PREFIX_FOR_EMAIL_ID is not defined (may be not defined for old versions)
1269  if (!empty($conf->file->instance_unique_id)) {
1270  return sha1('dolibarr'.$conf->file->instance_unique_id);
1271  }
1272 
1273  // For backward compatibility when instance_unique_id is not set
1274  return sha1(DOL_DOCUMENT_ROOT.DOL_URL_ROOT);
1275  }
1276 
1277  // If prefix is for session (no need to have $conf loaded)
1278  global $dolibarr_main_instance_unique_id, $dolibarr_main_cookie_cryptkey; // This is loaded by filefunc.inc.php
1279  $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
1280 
1281  // The recommended value (may be not defined for old versions)
1282  if (!empty($tmp_instance_unique_id)) {
1283  return sha1('dolibarr'.$tmp_instance_unique_id);
1284  }
1285 
1286  // For backward compatibility when instance_unique_id is not set
1287  if (isset($_SERVER["SERVER_NAME"]) && isset($_SERVER["DOCUMENT_ROOT"])) {
1288  return sha1($_SERVER["SERVER_NAME"].$_SERVER["DOCUMENT_ROOT"].DOL_DOCUMENT_ROOT.DOL_URL_ROOT);
1289  } else {
1290  return sha1(DOL_DOCUMENT_ROOT.DOL_URL_ROOT);
1291  }
1292  }
1293 }
1294 
1305 function dol_include_once($relpath, $classname = '')
1306 {
1307  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']
1308 
1309  $fullpath = dol_buildpath($relpath);
1310 
1311  if (!file_exists($fullpath)) {
1312  dol_syslog('functions::dol_include_once Tried to load unexisting file: '.$relpath, LOG_WARNING);
1313  return false;
1314  }
1315 
1316  if (!empty($classname) && !class_exists($classname)) {
1317  return include $fullpath;
1318  } else {
1319  return include_once $fullpath;
1320  }
1321 }
1322 
1323 
1334 function dol_buildpath($path, $type = 0, $returnemptyifnotfound = 0)
1335 {
1336  global $conf;
1337 
1338  $path = preg_replace('/^\//', '', $path);
1339 
1340  if (empty($type)) { // For a filesystem path
1341  $res = DOL_DOCUMENT_ROOT.'/'.$path; // Standard default path
1342  if (is_array($conf->file->dol_document_root)) {
1343  foreach ($conf->file->dol_document_root as $key => $dirroot) { // ex: array("main"=>"/home/main/htdocs", "alt0"=>"/home/dirmod/htdocs", ...)
1344  if ($key == 'main') {
1345  continue;
1346  }
1347  // if (@file_exists($dirroot.'/'.$path)) {
1348  if (@file_exists($dirroot.'/'.$path)) { // avoid [php:warn]
1349  $res = $dirroot.'/'.$path;
1350  return $res;
1351  }
1352  }
1353  }
1354  if ($returnemptyifnotfound) {
1355  // Not found into alternate dir
1356  if ($returnemptyifnotfound == 1 || !file_exists($res)) {
1357  return '';
1358  }
1359  }
1360  } else {
1361  // For an url path
1362  // We try to get local path of file on filesystem from url
1363  // Note that trying to know if a file on disk exist by forging path on disk from url
1364  // works only for some web server and some setup. This is bugged when
1365  // using proxy, rewriting, virtual path, etc...
1366  $res = '';
1367  if ($type == 1) {
1368  $res = DOL_URL_ROOT.'/'.$path; // Standard value
1369  }
1370  if ($type == 2) {
1371  $res = DOL_MAIN_URL_ROOT.'/'.$path; // Standard value
1372  }
1373  if ($type == 3) {
1374  $res = DOL_URL_ROOT.'/'.$path;
1375  }
1376 
1377  foreach ($conf->file->dol_document_root as $key => $dirroot) { // ex: array(["main"]=>"/home/main/htdocs", ["alt0"]=>"/home/dirmod/htdocs", ...)
1378  if ($key == 'main') {
1379  if ($type == 3) {
1380  /*global $dolibarr_main_url_root;*/
1381 
1382  // Define $urlwithroot
1383  $urlwithouturlroot = preg_replace('/'.preg_quote(DOL_URL_ROOT, '/').'$/i', '', trim($conf->file->dol_main_url_root));
1384  $urlwithroot = $urlwithouturlroot.DOL_URL_ROOT; // This is to use external domain name found into config file
1385  //$urlwithroot=DOL_MAIN_URL_ROOT; // This is to use same domain name than current
1386 
1387  $res = (preg_match('/^http/i', $conf->file->dol_url_root[$key]) ? '' : $urlwithroot).'/'.$path; // Test on start with http is for old conf syntax
1388  }
1389  continue;
1390  }
1391  $regs = array();
1392  preg_match('/^([^\?]+(\.css\.php|\.css|\.js\.php|\.js|\.png|\.jpg|\.php)?)/i', $path, $regs); // Take part before '?'
1393  if (!empty($regs[1])) {
1394  //print $key.'-'.$dirroot.'/'.$path.'-'.$conf->file->dol_url_root[$type].'<br>'."\n";
1395  //if (file_exists($dirroot.'/'.$regs[1])) {
1396  if (@file_exists($dirroot.'/'.$regs[1])) { // avoid [php:warn]
1397  if ($type == 1) {
1398  $res = (preg_match('/^http/i', $conf->file->dol_url_root[$key]) ? '' : DOL_URL_ROOT).$conf->file->dol_url_root[$key].'/'.$path;
1399  }
1400  if ($type == 2) {
1401  $res = (preg_match('/^http/i', $conf->file->dol_url_root[$key]) ? '' : DOL_MAIN_URL_ROOT).$conf->file->dol_url_root[$key].'/'.$path;
1402  }
1403  if ($type == 3) {
1404  /*global $dolibarr_main_url_root;*/
1405 
1406  // Define $urlwithroot
1407  $urlwithouturlroot = preg_replace('/'.preg_quote(DOL_URL_ROOT, '/').'$/i', '', trim($conf->file->dol_main_url_root));
1408  $urlwithroot = $urlwithouturlroot.DOL_URL_ROOT; // This is to use external domain name found into config file
1409  //$urlwithroot=DOL_MAIN_URL_ROOT; // This is to use same domain name than current
1410 
1411  $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
1412  }
1413  break;
1414  }
1415  }
1416  }
1417  }
1418 
1419  return $res;
1420 }
1421 
1432 function dol_get_object_properties($obj, $properties = [])
1433 {
1434  // Get real properties using get_object_vars() if $properties is empty
1435  if (empty($properties)) {
1436  return get_object_vars($obj);
1437  }
1438 
1439  $existingProperties = [];
1440  $realProperties = get_object_vars($obj);
1441 
1442  // Get the real or magic property values
1443  foreach ($properties as $property) {
1444  if (array_key_exists($property, $realProperties)) {
1445  // Real property, add the value
1446  $existingProperties[$property] = $obj->{$property};
1447  } elseif (property_exists($obj, $property)) {
1448  // Magic property
1449  $existingProperties[$property] = $obj->{$property};
1450  }
1451  }
1452 
1453  return $existingProperties;
1454 }
1455 
1456 
1471 function dol_clone($object, $native = 0)
1472 {
1473  if ($native == 0) {
1474  // deprecated method, use the method with native = 2 instead
1475  $tmpsavdb = null;
1476  if (isset($object->db) && isset($object->db->db) && is_object($object->db->db) && get_class($object->db->db) == 'PgSql\Connection') {
1477  $tmpsavdb = $object->db;
1478  unset($object->db); // Such property can not be serialized with pgsl (when object->db->db = 'PgSql\Connection')
1479  }
1480 
1481  $myclone = unserialize(serialize($object)); // serialize then unserialize is a hack to be sure to have a new object for all fields
1482 
1483  if (!empty($tmpsavdb)) {
1484  $object->db = $tmpsavdb;
1485  }
1486  } elseif ($native == 2) {
1487  // recommended method to have a full isolated cloned object
1488  $myclone = new stdClass();
1489  $tmparray = get_object_vars($object); // return only public properties
1490 
1491  if (is_array($tmparray)) {
1492  foreach ($tmparray as $propertykey => $propertyval) {
1493  if (is_scalar($propertyval) || is_array($propertyval)) {
1494  $myclone->$propertykey = $propertyval;
1495  }
1496  }
1497  }
1498  } else {
1499  $myclone = clone $object; // PHP clone is a shallow copy only, not a real clone, so properties of references will keep the reference (referring to the same target/variable)
1500  }
1501 
1502  return $myclone;
1503 }
1504 
1514 function dol_size($size, $type = '')
1515 {
1516  global $conf;
1517  if (empty($conf->dol_optimize_smallscreen)) {
1518  return $size;
1519  }
1520  if ($type == 'width' && $size > 250) {
1521  return 250;
1522  } else {
1523  return 10;
1524  }
1525 }
1526 
1527 
1539 function dol_sanitizeFileName($str, $newstr = '_', $unaccent = 1)
1540 {
1541  // List of special chars for filenames in windows are defined on page https://docs.microsoft.com/en-us/windows/win32/fileio/naming-a-file
1542  // Char '>' '<' '|' '$' and ';' are special chars for shells.
1543  // Char '/' and '\' are file delimiters.
1544  // Chars '--' can be used into filename to inject special parameters like --use-compress-program to make command with file as parameter making remote execution of command
1545  $filesystem_forbidden_chars = array('<', '>', '/', '\\', '?', '*', '|', '"', ':', '°', '$', ';', '`');
1546  $tmp = dol_string_nospecial($unaccent ? dol_string_unaccent($str) : $str, $newstr, $filesystem_forbidden_chars);
1547  $tmp = preg_replace('/\-\-+/', '_', $tmp);
1548  $tmp = preg_replace('/\s+\-([^\s])/', ' _$1', $tmp);
1549  $tmp = preg_replace('/\s+\-$/', '', $tmp);
1550  $tmp = str_replace('..', '', $tmp);
1551  return $tmp;
1552 }
1553 
1554 
1566 function dol_sanitizePathName($str, $newstr = '_', $unaccent = 1)
1567 {
1568  // List of special chars for filenames in windows are defined on page https://docs.microsoft.com/en-us/windows/win32/fileio/naming-a-file
1569  // Char '>' '<' '|' '$' and ';' are special chars for shells.
1570  // Chars '--' can be used into filename to inject special parameters like --use-compress-program to make command with file as parameter making remote execution of command
1571  $filesystem_forbidden_chars = array('<', '>', '?', '*', '|', '"', '°', '$', ';', '`');
1572  $tmp = dol_string_nospecial($unaccent ? dol_string_unaccent($str) : $str, $newstr, $filesystem_forbidden_chars);
1573  $tmp = preg_replace('/\-\-+/', '_', $tmp);
1574  $tmp = preg_replace('/\s+\-([^\s])/', ' _$1', $tmp);
1575  $tmp = preg_replace('/\s+\-$/', '', $tmp);
1576  $tmp = str_replace('..', '', $tmp);
1577  return $tmp;
1578 }
1579 
1587 function dol_sanitizeUrl($stringtoclean, $type = 1)
1588 {
1589  // 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)
1590  // We should use dol_string_nounprintableascii but function may not be yet loaded/available
1591  $stringtoclean = preg_replace('/[\x00-\x1F\x7F]/u', '', $stringtoclean); // /u operator makes UTF8 valid characters being ignored so are not included into the replace
1592  // We clean html comments because some hacks try to obfuscate evil strings by inserting HTML comments. Example: on<!-- -->error=alert(1)
1593  $stringtoclean = preg_replace('/<!--[^>]*-->/', '', $stringtoclean);
1594 
1595  $stringtoclean = str_replace('\\', '/', $stringtoclean);
1596  if ($type == 1) {
1597  // removing : should disable links to external url like http:aaa)
1598  // removing ';' should disable "named" html entities encode into an url (we should not have this into an url)
1599  $stringtoclean = str_replace(array(':', ';', '@'), '', $stringtoclean);
1600  }
1601 
1602  do {
1603  $oldstringtoclean = $stringtoclean;
1604  // removing '&colon' should disable links to external url like http:aaa)
1605  // removing '&#' should disable "numeric" html entities encode into an url (we should not have this into an url)
1606  $stringtoclean = str_ireplace(array('javascript', 'vbscript', '&colon', '&#'), '', $stringtoclean);
1607  } while ($oldstringtoclean != $stringtoclean);
1608 
1609  if ($type == 1) {
1610  // removing '//' should disable links to external url like //aaa or http//)
1611  $stringtoclean = preg_replace(array('/^[a-z]*\/\/+/i'), '', $stringtoclean);
1612  }
1613 
1614  return $stringtoclean;
1615 }
1616 
1623 function dol_sanitizeEmail($stringtoclean)
1624 {
1625  do {
1626  $oldstringtoclean = $stringtoclean;
1627  $stringtoclean = str_ireplace(array('"', ':', '[', ']',"\n", "\r", '\\', '\/'), '', $stringtoclean);
1628  } while ($oldstringtoclean != $stringtoclean);
1629 
1630  return $stringtoclean;
1631 }
1632 
1641 function dol_string_unaccent($str)
1642 {
1643  global $conf;
1644 
1645  if (is_null($str)) {
1646  return '';
1647  }
1648 
1649  if (utf8_check($str)) {
1650  if (extension_loaded('intl') && getDolGlobalString('MAIN_UNACCENT_USE_TRANSLITERATOR')) {
1651  $transliterator = Transliterator::createFromRules(':: Any-Latin; :: Latin-ASCII; :: NFD; :: [:Nonspacing Mark:] Remove; :: NFC;', Transliterator::FORWARD);
1652  return $transliterator->transliterate($str);
1653  }
1654  // See http://www.utf8-chartable.de/
1655  $string = rawurlencode($str);
1656  $replacements = array(
1657  '%C3%80' => 'A', '%C3%81' => 'A', '%C3%82' => 'A', '%C3%83' => 'A', '%C3%84' => 'A', '%C3%85' => 'A',
1658  '%C3%87' => 'C',
1659  '%C3%88' => 'E', '%C3%89' => 'E', '%C3%8A' => 'E', '%C3%8B' => 'E',
1660  '%C3%8C' => 'I', '%C3%8D' => 'I', '%C3%8E' => 'I', '%C3%8F' => 'I',
1661  '%C3%91' => 'N',
1662  '%C3%92' => 'O', '%C3%93' => 'O', '%C3%94' => 'O', '%C3%95' => 'O', '%C3%96' => 'O',
1663  '%C5%A0' => 'S',
1664  '%C3%99' => 'U', '%C3%9A' => 'U', '%C3%9B' => 'U', '%C3%9C' => 'U',
1665  '%C3%9D' => 'Y', '%C5%B8' => 'y',
1666  '%C3%A0' => 'a', '%C3%A1' => 'a', '%C3%A2' => 'a', '%C3%A3' => 'a', '%C3%A4' => 'a', '%C3%A5' => 'a',
1667  '%C3%A7' => 'c',
1668  '%C3%A8' => 'e', '%C3%A9' => 'e', '%C3%AA' => 'e', '%C3%AB' => 'e',
1669  '%C3%AC' => 'i', '%C3%AD' => 'i', '%C3%AE' => 'i', '%C3%AF' => 'i',
1670  '%C3%B1' => 'n',
1671  '%C3%B2' => 'o', '%C3%B3' => 'o', '%C3%B4' => 'o', '%C3%B5' => 'o', '%C3%B6' => 'o',
1672  '%C5%A1' => 's',
1673  '%C3%B9' => 'u', '%C3%BA' => 'u', '%C3%BB' => 'u', '%C3%BC' => 'u',
1674  '%C3%BD' => 'y', '%C3%BF' => 'y'
1675  );
1676  $string = strtr($string, $replacements);
1677  return rawurldecode($string);
1678  } else {
1679  // See http://www.ascii-code.com/
1680  $string = strtr(
1681  $str,
1682  "\xC0\xC1\xC2\xC3\xC4\xC5\xC7
1683  \xC8\xC9\xCA\xCB\xCC\xCD\xCE\xCF\xD0\xD1
1684  \xD2\xD3\xD4\xD5\xD8\xD9\xDA\xDB\xDD
1685  \xE0\xE1\xE2\xE3\xE4\xE5\xE7\xE8\xE9\xEA\xEB
1686  \xEC\xED\xEE\xEF\xF0\xF1\xF2\xF3\xF4\xF5\xF8
1687  \xF9\xFA\xFB\xFC\xFD\xFF",
1688  "AAAAAAC
1689  EEEEIIIIDN
1690  OOOOOUUUY
1691  aaaaaaceeee
1692  iiiidnooooo
1693  uuuuyy"
1694  );
1695  $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"));
1696  return $string;
1697  }
1698 }
1699 
1713 function dol_string_nospecial($str, $newstr = '_', $badcharstoreplace = '', $badcharstoremove = '', $keepspaces = 0)
1714 {
1715  $forbidden_chars_to_replace = array("'", "/", "\\", ":", "*", "?", "\"", "<", ">", "|", "[", "]", ",", ";", "=", '°', '$', ';'); // more complete than dol_sanitizeFileName
1716  if (empty($keepspaces)) {
1717  $forbidden_chars_to_replace[] = " ";
1718  }
1719  $forbidden_chars_to_remove = array();
1720  //$forbidden_chars_to_remove=array("(",")");
1721 
1722  if (is_array($badcharstoreplace)) {
1723  $forbidden_chars_to_replace = $badcharstoreplace;
1724  }
1725  if (is_array($badcharstoremove)) {
1726  $forbidden_chars_to_remove = $badcharstoremove;
1727  }
1728 
1729  // @phan-suppress-next-line PhanPluginSuspiciousParamOrderInternal
1730  return str_replace($forbidden_chars_to_replace, $newstr, str_replace($forbidden_chars_to_remove, "", $str));
1731 }
1732 
1733 
1747 function dol_string_nounprintableascii($str, $removetabcrlf = 1)
1748 {
1749  if ($removetabcrlf) {
1750  return preg_replace('/[\x00-\x1F\x7F]/u', '', $str); // /u operator makes UTF8 valid characters being ignored so are not included into the replace
1751  } else {
1752  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
1753  }
1754 }
1755 
1764 function dol_escape_js($stringtoescape, $mode = 0, $noescapebackslashn = 0)
1765 {
1766  if (is_null($stringtoescape)) {
1767  return '';
1768  }
1769 
1770  // escape quotes and backslashes, newlines, etc.
1771  $substitjs = array("&#039;" => "\\'", "\r" => '\\r');
1772  //$substitjs['</']='<\/'; // We removed this. Should be useless.
1773  if (empty($noescapebackslashn)) {
1774  $substitjs["\n"] = '\\n';
1775  $substitjs['\\'] = '\\\\';
1776  }
1777  if (empty($mode)) {
1778  $substitjs["'"] = "\\'";
1779  $substitjs['"'] = "\\'";
1780  } elseif ($mode == 1) {
1781  $substitjs["'"] = "\\'";
1782  } elseif ($mode == 2) {
1783  $substitjs['"'] = '\\"';
1784  } elseif ($mode == 3) {
1785  $substitjs["'"] = "\\'";
1786  $substitjs['"'] = "\\\"";
1787  }
1788  return strtr($stringtoescape, $substitjs);
1789 }
1790 
1797 function dol_escape_json($stringtoescape)
1798 {
1799  return str_replace('"', '\"', $stringtoescape);
1800 }
1801 
1809 function dol_escape_php($stringtoescape, $stringforquotes = 2)
1810 {
1811  if (is_null($stringtoescape)) {
1812  return '';
1813  }
1814 
1815  if ($stringforquotes == 2) {
1816  return str_replace('"', "'", $stringtoescape);
1817  } elseif ($stringforquotes == 1) {
1818  // We remove the \ char.
1819  // If we allow the \ char, we can have $stringtoescape =
1820  // abc\';phpcodedanger; so the escapement will become
1821  // abc\\';phpcodedanger; and injecting this into
1822  // $a='...' will give $ac='abc\\';phpcodedanger;
1823  $stringtoescape = str_replace('\\', '', $stringtoescape);
1824  return str_replace("'", "\'", str_replace('"', "'", $stringtoescape));
1825  }
1826 
1827  return 'Bad parameter for stringforquotes in dol_escape_php';
1828 }
1829 
1836 function dol_escape_xml($stringtoescape)
1837 {
1838  return $stringtoescape;
1839 }
1840 
1848 function dolPrintLabel($s)
1849 {
1850  return dol_escape_htmltag(dol_string_nohtmltag($s, 1, 'UTF-8', 0, 0), 0, 0, '', 0, 1);
1851 }
1852 
1862 function dolPrintHTML($s, $allowiframe = 0)
1863 {
1864  return dol_escape_htmltag(dol_htmlwithnojs(dol_string_onlythesehtmltags(dol_htmlentitiesbr($s), 1, 1, 1, $allowiframe)), 1, 1, 'common', 0, 1);
1865 }
1866 
1874 function dolPrintHTMLForAttribute($s)
1875 {
1876  // The dol_htmlentitiesbr will convert simple text into html
1877  // The dol_escape_htmltag will escape html chars.
1878  return dol_escape_htmltag(dol_string_onlythesehtmltags(dol_htmlentitiesbr($s), 1, 0, 0, 0, array('br', 'b', 'font', 'span')), 1, -1, '', 0, 1);
1879 }
1880 
1889 function dolPrintHTMLForTextArea($s, $allowiframe = 0)
1890 {
1891  return dol_escape_htmltag(dol_htmlwithnojs(dol_string_onlythesehtmltags(dol_htmlentitiesbr($s), 1, 1, 1, $allowiframe)), 1, 1, '', 0, 1);
1892 }
1893 
1900 function dolPrintPassword($s)
1901 {
1902  return htmlspecialchars($s, ENT_COMPAT, 'UTF-8');
1903 }
1904 
1905 
1921 function dol_escape_htmltag($stringtoescape, $keepb = 0, $keepn = 0, $noescapetags = '', $escapeonlyhtmltags = 0, $cleanalsojavascript = 0)
1922 {
1923  if ($noescapetags == 'common') {
1924  $noescapetags = 'html,body,a,b,em,hr,i,u,ul,li,br,div,img,font,p,span,strong,table,tr,td,th,tbody,h1,h2,h3,h4,h5,h6,h7,h8,h9';
1925  // Add also html5 tags
1926  $noescapetags .= ',header,footer,nav,section,menu,menuitem';
1927  }
1928  if ($cleanalsojavascript) {
1929  $stringtoescape = dol_string_onlythesehtmltags($stringtoescape, 0, 0, $cleanalsojavascript, 0, array(), 0);
1930  }
1931 
1932  // escape quotes and backslashes, newlines, etc.
1933  if ($escapeonlyhtmltags) {
1934  $tmp = htmlspecialchars_decode((string) $stringtoescape, ENT_COMPAT);
1935  } else {
1936  $tmp = html_entity_decode((string) $stringtoescape, ENT_COMPAT, 'UTF-8');
1937  }
1938  if (!$keepb) {
1939  $tmp = strtr($tmp, array("<b>" => '', '</b>' => '', '<strong>' => '', '</strong>' => ''));
1940  }
1941  if (!$keepn) {
1942  $tmp = strtr($tmp, array("\r" => '\\r', "\n" => '\\n'));
1943  } elseif ($keepn == -1) {
1944  $tmp = strtr($tmp, array("\r" => '', "\n" => ''));
1945  }
1946 
1947  if ($escapeonlyhtmltags) {
1948  return htmlspecialchars($tmp, ENT_COMPAT, 'UTF-8');
1949  } else {
1950  // Escape tags to keep
1951  $tmparrayoftags = array();
1952  if ($noescapetags) {
1953  $tmparrayoftags = explode(',', $noescapetags);
1954  }
1955  if (count($tmparrayoftags)) {
1956  $tmp = str_ireplace('DOUBLEQUOTE', '', $tmp); // The keyword DOUBLEQUOTE is forbidden. Reserved, so we removed it if we find it.
1957 
1958  foreach ($tmparrayoftags as $tagtoreplace) {
1959  $tmp = preg_replace('/<'.preg_quote($tagtoreplace, '/').'>/', '__BEGINTAGTOREPLACE'.$tagtoreplace.'__', $tmp);
1960  $tmp = str_ireplace('</'.$tagtoreplace.'>', '__ENDTAGTOREPLACE'.$tagtoreplace.'__', $tmp);
1961  $tmp = preg_replace('/<'.preg_quote($tagtoreplace, '/').' \/>/', '__BEGINENDTAGTOREPLACE'.$tagtoreplace.'__', $tmp);
1962 
1963  // For case of tag with attribute
1964  $reg = array();
1965  if (preg_match('/<'.preg_quote($tagtoreplace, '/').'\s+([^>]+)>/', $tmp, $reg)) {
1966  $tmpattributes = str_ireplace(array('[', ']'), '_', $reg[1]); // We must not have [ ] inside the attribute string
1967  $tmpattributes = str_ireplace('"', 'DOUBLEQUOTE', $tmpattributes);
1968  $tmpattributes = preg_replace('/[^a-z0-9_\/\?\;\s=&\.]/i', '', $tmpattributes);
1969  $tmp = preg_replace('/<'.preg_quote($tagtoreplace, '/').'\s+([^>]+)>/', '__BEGINTAGTOREPLACE'.$tagtoreplace.'['.$tmpattributes.']__', $tmp);
1970  }
1971  if (preg_match('/<'.preg_quote($tagtoreplace, '/').'\s+([^>]+)> \/>/', $tmp, $reg)) {
1972  $tmpattributes = str_ireplace(array('[', ']'), '_', $reg[1]); // We must not have [ ] inside the attribute string
1973  $tmpattributes = str_ireplace('"', 'DOUBLEQUOTE', $tmpattributes);
1974  $tmpattributes = preg_replace('/[^a-z0-9_\/\?\;\s=&]/i', '', $tmpattributes);
1975  $tmp = preg_replace('/<'.preg_quote($tagtoreplace, '/').'\s+([^>]+) \/>/', '__BEGINENDTAGTOREPLACE'.$tagtoreplace.'['.$tmpattributes.']__', $tmp);
1976  }
1977  }
1978  }
1979 
1980  $result = htmlentities($tmp, ENT_COMPAT, 'UTF-8');
1981 
1982  if (count($tmparrayoftags)) {
1983  foreach ($tmparrayoftags as $tagtoreplace) {
1984  $result = str_ireplace('__BEGINTAGTOREPLACE'.$tagtoreplace.'__', '<'.$tagtoreplace.'>', $result);
1985  $result = preg_replace('/__BEGINTAGTOREPLACE'.$tagtoreplace.'\[(.*)\]__/', '<'.$tagtoreplace.' \1>', $result);
1986  $result = str_ireplace('__ENDTAGTOREPLACE'.$tagtoreplace.'__', '</'.$tagtoreplace.'>', $result);
1987  $result = str_ireplace('__BEGINENDTAGTOREPLACE'.$tagtoreplace.'__', '<'.$tagtoreplace.' />', $result);
1988  $result = preg_replace('/__BEGINENDTAGTOREPLACE'.$tagtoreplace.'\[(.*)\]__/', '<'.$tagtoreplace.' \1 />', $result);
1989  }
1990 
1991  $result = str_ireplace('DOUBLEQUOTE', '"', $result);
1992  }
1993 
1994  return $result;
1995  }
1996 }
1997 
2005 function dol_strtolower($string, $encoding = "UTF-8")
2006 {
2007  if (function_exists('mb_strtolower')) {
2008  return mb_strtolower($string, $encoding);
2009  } else {
2010  return strtolower($string);
2011  }
2012 }
2013 
2022 function dol_strtoupper($string, $encoding = "UTF-8")
2023 {
2024  if (function_exists('mb_strtoupper')) {
2025  return mb_strtoupper($string, $encoding);
2026  } else {
2027  return strtoupper($string);
2028  }
2029 }
2030 
2039 function dol_ucfirst($string, $encoding = "UTF-8")
2040 {
2041  if (function_exists('mb_substr')) {
2042  return mb_strtoupper(mb_substr($string, 0, 1, $encoding), $encoding).mb_substr($string, 1, null, $encoding);
2043  } else {
2044  return ucfirst($string);
2045  }
2046 }
2047 
2056 function dol_ucwords($string, $encoding = "UTF-8")
2057 {
2058  if (function_exists('mb_convert_case')) {
2059  return mb_convert_case($string, MB_CASE_TITLE, $encoding);
2060  } else {
2061  return ucwords($string);
2062  }
2063 }
2064 
2087 function dol_syslog($message, $level = LOG_INFO, $ident = 0, $suffixinfilename = '', $restricttologhandler = '', $logcontext = null)
2088 {
2089  global $conf, $user, $debugbar;
2090 
2091  // If syslog module enabled
2092  if (!isModEnabled('syslog')) {
2093  return;
2094  }
2095 
2096  // Check if we are into execution of code of a website
2097  if (defined('USEEXTERNALSERVER') && !defined('USEDOLIBARRSERVER') && !defined('USEDOLIBARREDITOR')) {
2098  global $website, $websitekey;
2099  if (is_object($website) && !empty($website->ref)) {
2100  $suffixinfilename .= '_website_'.$website->ref;
2101  } elseif (!empty($websitekey)) {
2102  $suffixinfilename .= '_website_'.$websitekey;
2103  }
2104  }
2105 
2106  // Check if we have a forced suffix
2107  if (defined('USESUFFIXINLOG')) {
2108  $suffixinfilename .= constant('USESUFFIXINLOG');
2109  }
2110 
2111  if ($ident < 0) {
2112  foreach ($conf->loghandlers as $loghandlerinstance) {
2113  $loghandlerinstance->setIdent($ident);
2114  }
2115  }
2116 
2117  if (!empty($message)) {
2118  // Test log level
2119  // @phan-suppress-next-line PhanPluginDuplicateArrayKey
2120  $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');
2121  if (!array_key_exists($level, $logLevels)) {
2122  throw new Exception('Incorrect log level');
2123  }
2124  if ($level > getDolGlobalInt('SYSLOG_LEVEL')) {
2125  return;
2126  }
2127 
2128  if (!getDolGlobalString('MAIN_SHOW_PASSWORD_INTO_LOG')) {
2129  $message = preg_replace('/password=\'[^\']*\'/', 'password=\'hidden\'', $message); // protection to avoid to have value of password in log
2130  }
2131 
2132  // If adding log inside HTML page is required
2133  if ((!empty($_REQUEST['logtohtml']) && getDolGlobalString('MAIN_ENABLE_LOG_TO_HTML'))
2134  || (is_object($user) && $user->hasRight('debugbar', 'read') && is_object($debugbar))) {
2135  $ospid = sprintf("%7s", dol_trunc(getmypid(), 7, 'right', 'UTF-8', 1));
2136  $osuser = " ".sprintf("%6s", dol_trunc(function_exists('posix_getuid') ? posix_getuid() : '', 6, 'right', 'UTF-8', 1));
2137 
2138  $conf->logbuffer[] = dol_print_date(time(), "%Y-%m-%d %H:%M:%S")." ".sprintf("%-7s", $logLevels[$level])." ".$ospid." ".$osuser." ".$message;
2139  }
2140 
2141  //TODO: Remove this. MAIN_ENABLE_LOG_INLINE_HTML should be deprecated and use a log handler dedicated to HTML output
2142  // If html log tag enabled and url parameter log defined, we show output log on HTML comments
2143  if (getDolGlobalString('MAIN_ENABLE_LOG_INLINE_HTML') && GETPOSTINT("log")) {
2144  print "\n\n<!-- Log start\n";
2145  print dol_escape_htmltag($message)."\n";
2146  print "Log end -->\n";
2147  }
2148 
2149  $data = array(
2150  'message' => $message,
2151  'script' => (isset($_SERVER['PHP_SELF']) ? basename($_SERVER['PHP_SELF'], '.php') : false),
2152  'level' => $level,
2153  'user' => ((is_object($user) && $user->id) ? $user->login : false),
2154  'ip' => false,
2155  'osuser' => function_exists('posix_getuid') ? posix_getuid() : false,
2156  'ospid' => getmypid() // on linux, max value is defined into cat /proc/sys/kernel/pid_max
2157  );
2158 
2159  $remoteip = getUserRemoteIP(); // Get ip when page run on a web server
2160  if (!empty($remoteip)) {
2161  $data['ip'] = $remoteip;
2162  // This is when server run behind a reverse proxy
2163  if (!empty($_SERVER['HTTP_X_FORWARDED_FOR']) && $_SERVER['HTTP_X_FORWARDED_FOR'] != $remoteip) {
2164  $data['ip'] = $_SERVER['HTTP_X_FORWARDED_FOR'].' -> '.$data['ip'];
2165  } elseif (!empty($_SERVER['HTTP_CLIENT_IP']) && $_SERVER['HTTP_CLIENT_IP'] != $remoteip) {
2166  $data['ip'] = $_SERVER['HTTP_CLIENT_IP'].' -> '.$data['ip'];
2167  }
2168  } elseif (!empty($_SERVER['SERVER_ADDR'])) {
2169  // This is when PHP session is ran inside a web server but not inside a client request (example: init code of apache)
2170  $data['ip'] = $_SERVER['SERVER_ADDR'];
2171  } elseif (!empty($_SERVER['COMPUTERNAME'])) {
2172  // This is when PHP session is ran outside a web server, like from Windows command line (Not always defined, but useful if OS defines it).
2173  $data['ip'] = $_SERVER['COMPUTERNAME'];
2174  } else {
2175  $data['ip'] = '???';
2176  }
2177 
2178  if (!empty($_SERVER['USERNAME'])) {
2179  // This is when PHP session is ran outside a web server, like from Linux command line (Not always defined, but useful if OS defines it).
2180  $data['osuser'] = $_SERVER['USERNAME'];
2181  } elseif (!empty($_SERVER['LOGNAME'])) {
2182  // This is when PHP session is ran outside a web server, like from Linux command line (Not always defined, but useful if OS defines it).
2183  $data['osuser'] = $_SERVER['LOGNAME'];
2184  }
2185 
2186  // Loop on each log handler and send output
2187  foreach ($conf->loghandlers as $loghandlerinstance) {
2188  if ($restricttologhandler && $loghandlerinstance->code != $restricttologhandler) {
2189  continue;
2190  }
2191  $loghandlerinstance->export($data, $suffixinfilename);
2192  }
2193  unset($data);
2194  }
2195 
2196  if ($ident > 0) {
2197  foreach ($conf->loghandlers as $loghandlerinstance) {
2198  $loghandlerinstance->setIdent($ident);
2199  }
2200  }
2201 }
2202 
2214 function dolButtonToOpenExportDialog($name, $label, $buttonstring, $exportSiteName, $overwriteGitUrl, $website)
2215 {
2216  global $langs, $db;
2217 
2218  $form = new Form($db);
2219 
2220  $templatenameforexport = $website->name_template; // Example 'website_template-corporate'
2221  if (empty($templatenameforexport)) {
2222  $templatenameforexport = 'website_'.$website->ref;
2223  }
2224 
2225  $out = '';
2226  $out .= '<input type="button" class="cursorpointer button bordertransp" id="open-dialog-' . $name . '" value="'.dol_escape_htmltag($buttonstring).'"/>';
2227 
2228  // for generate popup
2229  $out .= '<script nonce="' . getNonce() . '" type="text/javascript">';
2230  $out .= 'jQuery(document).ready(function () {';
2231  $out .= ' jQuery("#open-dialog-' . $name . '").click(function () {';
2232  $out .= ' var dialogHtml = \'';
2233 
2234  $dialogcontent = ' <div id="custom-dialog-' . $name . '">';
2235  $dialogcontent .= ' <div style="margin-top: 20px;">';
2236  $dialogcontent .= ' <label for="export-site-' . $name . '"><strong>'.$langs->trans("ExportSiteLabel").'...</label><br>';
2237  $dialogcontent .= ' <button class="button smallpaddingimp" id="export-site-' . $name . '">' . dol_escape_htmltag($langs->trans("DownloadZip")) . '</button>';
2238  $dialogcontent .= ' </div>';
2239  $dialogcontent .= ' <br>';
2240  $dialogcontent .= ' <div style="margin-top: 20px;">';
2241  $dialogcontent .= ' <strong>'.$langs->trans("ExportSiteGitLabel").' '.$form->textwithpicto('', $langs->trans("SourceFiles"), 1, 'help', '', 0, 3, '').'</strong><br>';
2242  $dialogcontent .= ' <form action="'.dol_escape_htmltag($overwriteGitUrl).'" method="POST">';
2243  $dialogcontent .= ' <input type="hidden" name="action" value="overwritesite">';
2244  $dialogcontent .= ' <input type="hidden" name="token" value="'.newToken().'">';
2245  $dialogcontent .= ' <input type="text" autofocus name="export_path" id="export-path-'.$name.'" placeholder="'.$langs->trans('ExportPath').'" style="width:400px " value="'.dol_escape_htmltag($templatenameforexport).'"/><br>';
2246  $dialogcontent .= ' <button type="submit" class="button smallpaddingimp" id="overwrite-git-' . $name . '">' . dol_escape_htmltag($langs->trans("ExportIntoGIT")) . '</button>';
2247  $dialogcontent .= ' </form>';
2248  $dialogcontent .= ' </div>';
2249  $dialogcontent .= ' </div>';
2250 
2251  $out .= dol_escape_js($dialogcontent);
2252 
2253  $out .= '\';';
2254 
2255 
2256  // Add the content of the dialog to the body of the page
2257  $out .= ' var $dialog = jQuery("#custom-dialog-' . $name . '");';
2258  $out .= ' if ($dialog.length > 0) {
2259  $dialog.remove();
2260  }
2261  jQuery("body").append(dialogHtml);';
2262 
2263  // Configuration of popup
2264  $out .= ' jQuery("#custom-dialog-' . $name . '").dialog({';
2265  $out .= ' autoOpen: false,';
2266  $out .= ' modal: true,';
2267  $out .= ' height: 290,';
2268  $out .= ' width: "40%",';
2269  $out .= ' title: "' . dol_escape_js($label) . '",';
2270  $out .= ' });';
2271 
2272  // Simulate a click on the original "submit" input to export the site.
2273  $out .= ' jQuery("#export-site-' . $name . '").click(function () {';
2274  $out .= ' console.log("Clic on exportsite.");';
2275  $out .= ' var target = jQuery("input[name=\'' . dol_escape_js($exportSiteName) . '\']");';
2276  $out .= ' console.log("element founded:", target.length > 0);';
2277  $out .= ' if (target.length > 0) { target.click(); }';
2278  $out .= ' jQuery("#custom-dialog-' . $name . '").dialog("close");';
2279  $out .= ' });';
2280 
2281  // open popup
2282  $out .= ' jQuery("#custom-dialog-' . $name . '").dialog("open");';
2283  $out .= ' return false;';
2284  $out .= ' });';
2285  $out .= '});';
2286  $out .= '</script>';
2287 
2288  return $out;
2289 }
2290 
2291 
2308 function dolButtonToOpenUrlInDialogPopup($name, $label, $buttonstring, $url, $disabled = '', $morecss = 'classlink button bordertransp', $jsonopen = '', $backtopagejsfields = '', $accesskey = '')
2309 {
2310  global $conf;
2311 
2312  if (strpos($url, '?') > 0) {
2313  $url .= '&dol_hide_topmenu=1&dol_hide_leftmenu=1&dol_openinpopup='.urlencode($name);
2314  } else {
2315  $url .= '?dol_hide_topmenu=1&dol_hide_leftmenu=1&dol_openinpopup='.urlencode($name);
2316  }
2317 
2318  $out = '';
2319 
2320  $backtopagejsfieldsid = '';
2321  $backtopagejsfieldslabel = '';
2322  if ($backtopagejsfields) {
2323  $tmpbacktopagejsfields = explode(':', $backtopagejsfields);
2324  if (empty($tmpbacktopagejsfields[1])) { // If the part 'keyforpopupid:' is missing, we add $name for it.
2325  $backtopagejsfields = $name.":".$backtopagejsfields;
2326  $tmp2backtopagejsfields = explode(',', $tmpbacktopagejsfields[0]);
2327  } else {
2328  $tmp2backtopagejsfields = explode(',', $tmpbacktopagejsfields[1]);
2329  }
2330  $backtopagejsfieldsid = empty($tmp2backtopagejsfields[0]) ? '' : $tmp2backtopagejsfields[0];
2331  $backtopagejsfieldslabel = empty($tmp2backtopagejsfields[1]) ? '' : $tmp2backtopagejsfields[1];
2332  $url .= '&backtopagejsfields='.urlencode($backtopagejsfields);
2333  }
2334 
2335  //print '<input type="submit" class="button bordertransp"'.$disabled.' value="'.dol_escape_htmltag($langs->trans("MediaFiles")).'" name="file_manager">';
2336  $out .= '<!-- a link for button to open url into a dialog popup with backtopagejsfields = '.$backtopagejsfields.' -->';
2337  $out .= '<a '.($accesskey ? ' accesskey="'.$accesskey.'"' : '').' class="cursorpointer reposition button_'.$name.($morecss ? ' '.$morecss : '').'"'.$disabled.' title="'.dol_escape_htmltag($label).'"';
2338  if (empty($conf->use_javascript_ajax)) {
2339  $out .= ' href="'.DOL_URL_ROOT.$url.'" target="_blank"';
2340  } elseif ($jsonopen) {
2341  $out .= ' href="#" onclick="'.$jsonopen.'"';
2342  } else {
2343  $out .= ' href="#"';
2344  }
2345  $out .= '>'.$buttonstring.'</a>';
2346 
2347  if (!empty($conf->use_javascript_ajax)) {
2348  // Add code to open url using the popup. Add also hidden field to retrieve the returned variables
2349  $out .= '<!-- code to open popup and variables to retrieve returned variables -->';
2350  $out .= '<div id="idfordialog'.$name.'" class="hidden">'.(getDolGlobalInt('MAIN_OPTIMIZEFORTEXTBROWSER') < 2 ? 'div for dialog' : '').'</div>';
2351  $out .= '<div id="varforreturndialogid'.$name.'" class="hidden">'.(getDolGlobalInt('MAIN_OPTIMIZEFORTEXTBROWSER') < 2 ? 'div for returned id' : '').'</div>';
2352  $out .= '<div id="varforreturndialoglabel'.$name.'" class="hidden">'.(getDolGlobalInt('MAIN_OPTIMIZEFORTEXTBROWSER') < 2 ? 'div for returned label' : '').'</div>';
2353 
2354  $out .= '<!-- Add js code to open dialog popup on dialog -->';
2355  $out .= '<script nonce="'.getNonce().'" type="text/javascript">
2356  jQuery(document).ready(function () {
2357  jQuery(".button_'.$name.'").click(function () {
2358  console.log(\'Open popup with jQuery(...).dialog() on URL '.dol_escape_js(DOL_URL_ROOT.$url).'\');
2359  var $tmpdialog = $(\'#idfordialog'.$name.'\');
2360  $tmpdialog.html(\'<iframe class="iframedialog" id="iframedialog'.$name.'" style="border: 0px;" src="'.DOL_URL_ROOT.$url.'" width="100%" height="98%"></iframe>\');
2361  $tmpdialog.dialog({
2362  autoOpen: false,
2363  modal: true,
2364  height: (window.innerHeight - 150),
2365  width: \'80%\',
2366  title: \''.dol_escape_js($label).'\',
2367  open: function (event, ui) {
2368  console.log("open popup name='.$name.', backtopagejsfields='.$backtopagejsfields.'");
2369  },
2370  close: function (event, ui) {
2371  var returnedid = jQuery("#varforreturndialogid'.$name.'").text();
2372  var returnedlabel = jQuery("#varforreturndialoglabel'.$name.'").text();
2373  console.log("popup has been closed. returnedid (js var defined into parent page)="+returnedid+" returnedlabel="+returnedlabel);
2374  if (returnedid != "" && returnedid != "div for returned id") {
2375  jQuery("#'.(empty($backtopagejsfieldsid) ? "none" : $backtopagejsfieldsid).'").val(returnedid);
2376  }
2377  if (returnedlabel != "" && returnedlabel != "div for returned label") {
2378  jQuery("#'.(empty($backtopagejsfieldslabel) ? "none" : $backtopagejsfieldslabel).'").val(returnedlabel);
2379  }
2380  }
2381  });
2382 
2383  $tmpdialog.dialog(\'open\');
2384  return false;
2385  });
2386  });
2387  </script>';
2388  }
2389  return $out;
2390 }
2391 
2408 function dol_fiche_head($links = array(), $active = '0', $title = '', $notab = 0, $picto = '', $pictoisfullpath = 0, $morehtmlright = '', $morecss = '', $limittoshow = 0, $moretabssuffix = '')
2409 {
2410  print dol_get_fiche_head($links, $active, $title, $notab, $picto, $pictoisfullpath, $morehtmlright, $morecss, $limittoshow, $moretabssuffix);
2411 }
2412 
2429 function dol_get_fiche_head($links = array(), $active = '', $title = '', $notab = 0, $picto = '', $pictoisfullpath = 0, $morehtmlright = '', $morecss = '', $limittoshow = 0, $moretabssuffix = '', $dragdropfile = 0)
2430 {
2431  global $conf, $langs, $hookmanager;
2432 
2433  // Show title
2434  $showtitle = 1;
2435  if (!empty($conf->dol_optimize_smallscreen)) {
2436  $showtitle = 0;
2437  }
2438 
2439  $out = "\n".'<!-- dol_fiche_head - dol_get_fiche_head -->';
2440 
2441  if ((!empty($title) && $showtitle) || $morehtmlright || !empty($links)) {
2442  $out .= '<div class="tabs'.($picto ? '' : ' nopaddingleft').'" data-role="controlgroup" data-type="horizontal">'."\n";
2443  }
2444 
2445  // Show right part
2446  if ($morehtmlright) {
2447  $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.
2448  }
2449 
2450  // Show title
2451  if (!empty($title) && $showtitle && !getDolGlobalString('MAIN_OPTIMIZEFORTEXTBROWSER')) {
2452  $limittitle = 30;
2453  $out .= '<a class="tabTitle">';
2454  if ($picto) {
2455  $noprefix = $pictoisfullpath;
2456  if (strpos($picto, 'fontawesome_') !== false) {
2457  $noprefix = 1;
2458  }
2459  $out .= img_picto($title, ($noprefix ? '' : 'object_').$picto, '', $pictoisfullpath, 0, 0, '', 'imgTabTitle').' ';
2460  }
2461  $out .= '<span class="tabTitleText">'.dol_escape_htmltag(dol_trunc($title, $limittitle)).'</span>';
2462  $out .= '</a>';
2463  }
2464 
2465  // Show tabs
2466 
2467  // Define max of key (max may be higher than sizeof because of hole due to module disabling some tabs).
2468  $maxkey = -1;
2469  if (is_array($links) && !empty($links)) {
2470  $keys = array_keys($links);
2471  if (count($keys)) {
2472  $maxkey = max($keys);
2473  }
2474  }
2475 
2476  // Show tabs
2477  // if =0 we don't use the feature
2478  if (empty($limittoshow)) {
2479  $limittoshow = (!getDolGlobalString('MAIN_MAXTABS_IN_CARD') ? 99 : $conf->global->MAIN_MAXTABS_IN_CARD);
2480  }
2481  if (!empty($conf->dol_optimize_smallscreen)) {
2482  $limittoshow = 2;
2483  }
2484 
2485  $displaytab = 0;
2486  $nbintab = 0;
2487  $popuptab = 0;
2488  $outmore = '';
2489  for ($i = 0; $i <= $maxkey; $i++) {
2490  if ((is_numeric($active) && $i == $active) || (!empty($links[$i][2]) && !is_numeric($active) && $active == $links[$i][2])) {
2491  // If active tab is already present
2492  if ($i >= $limittoshow) {
2493  $limittoshow--;
2494  }
2495  }
2496  }
2497 
2498  for ($i = 0; $i <= $maxkey; $i++) {
2499  if ((is_numeric($active) && $i == $active) || (!empty($links[$i][2]) && !is_numeric($active) && $active == $links[$i][2])) {
2500  $isactive = true;
2501  } else {
2502  $isactive = false;
2503  }
2504 
2505  if ($i < $limittoshow || $isactive) {
2506  // Output entry with a visible tab
2507  $out .= '<div class="inline-block tabsElem'.($isactive ? ' tabsElemActive' : '').((!$isactive && getDolGlobalString('MAIN_HIDE_INACTIVETAB_ON_PRINT')) ? ' hideonprint' : '').'"><!-- id tab = '.(empty($links[$i][2]) ? '' : dol_escape_htmltag($links[$i][2])).' -->';
2508 
2509  if (isset($links[$i][2]) && $links[$i][2] == 'image') {
2510  if (!empty($links[$i][0])) {
2511  $out .= '<a class="tabimage'.($morecss ? ' '.$morecss : '').'" href="'.$links[$i][0].'">'.$links[$i][1].'</a>'."\n";
2512  } else {
2513  $out .= '<span class="tabspan">'.$links[$i][1].'</span>'."\n";
2514  }
2515  } elseif (!empty($links[$i][1])) {
2516  //print "x $i $active ".$links[$i][2]." z";
2517  $out .= '<div class="tab tab'.($isactive ? 'active' : 'unactive').'" style="margin: 0 !important">';
2518  if (!empty($links[$i][0])) {
2519  $titletoshow = preg_replace('/<.*$/', '', $links[$i][1]);
2520  $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).'">';
2521  }
2522  $out .= $links[$i][1];
2523  if (!empty($links[$i][0])) {
2524  $out .= '</a>'."\n";
2525  }
2526  $out .= empty($links[$i][4]) ? '' : $links[$i][4];
2527  $out .= '</div>';
2528  }
2529 
2530  $out .= '</div>';
2531  } else {
2532  // Add entry into the combo popup with the other tabs
2533  if (!$popuptab) {
2534  $popuptab = 1;
2535  $outmore .= '<div class="popuptabset wordwrap">'; // The css used to hide/show popup
2536  }
2537  $outmore .= '<div class="popuptab wordwrap" style="display:inherit;">';
2538  if (isset($links[$i][2]) && $links[$i][2] == 'image') {
2539  if (!empty($links[$i][0])) {
2540  $outmore .= '<a class="tabimage'.($morecss ? ' '.$morecss : '').'" href="'.$links[$i][0].'">'.$links[$i][1].'</a>'."\n";
2541  } else {
2542  $outmore .= '<span class="tabspan">'.$links[$i][1].'</span>'."\n";
2543  }
2544  } elseif (!empty($links[$i][1])) {
2545  $outmore .= '<a'.(!empty($links[$i][2]) ? ' id="'.$links[$i][2].'"' : '').' class="wordwrap inline-block'.($morecss ? ' '.$morecss : '').'" href="'.$links[$i][0].'">';
2546  $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.
2547  $outmore .= '</a>'."\n";
2548  }
2549  $outmore .= '</div>';
2550 
2551  $nbintab++;
2552  }
2553  $displaytab = $i;
2554  }
2555  if ($popuptab) {
2556  $outmore .= '</div>';
2557  }
2558 
2559  if ($popuptab) { // If there is some tabs not shown
2560  $left = ($langs->trans("DIRECTION") == 'rtl' ? 'right' : 'left');
2561  $right = ($langs->trans("DIRECTION") == 'rtl' ? 'left' : 'right');
2562  $widthofpopup = 200;
2563 
2564  $tabsname = $moretabssuffix;
2565  if (empty($tabsname)) {
2566  $tabsname = str_replace("@", "", $picto);
2567  }
2568  $out .= '<div id="moretabs'.$tabsname.'" class="inline-block tabsElem valignmiddle">';
2569  if (getDolGlobalInt('MAIN_OPTIMIZEFORTEXTBROWSER') < 2) {
2570  $out .= '<div class="tab valignmiddle"><a href="#" class="tab moretab inline-block tabunactive valignmiddle"><span class="hideonsmartphone">'.$langs->trans("More").'</span>... ('.$nbintab.')</a></div>'; // Do not use "reposition" class in the "More".
2571  }
2572  $out .= '<div id="moretabsList'.$tabsname.'" style="width: '.$widthofpopup.'px; position: absolute; '.$left.': -999em; text-align: '.$left.'; margin:0px; padding:2px; z-index:10;">';
2573  $out .= $outmore;
2574  $out .= '</div>';
2575  $out .= '<div></div>';
2576  $out .= "</div>\n";
2577 
2578  $out .= '<script nonce="'.getNonce().'">';
2579  $out .= "$('#moretabs".$tabsname."').mouseenter( function() {
2580  var x = this.offsetLeft, y = this.offsetTop;
2581  console.log('mouseenter ".$left." x='+x+' y='+y+' window.innerWidth='+window.innerWidth);
2582  if ((window.innerWidth - x) < ".($widthofpopup + 10).") {
2583  $('#moretabsList".$tabsname."').css('".$right."','8px');
2584  }
2585  $('#moretabsList".$tabsname."').css('".$left."','auto');
2586  });
2587  ";
2588  $out .= "$('#moretabs".$tabsname."').mouseleave( function() { console.log('mouseleave ".$left."'); $('#moretabsList".$tabsname."').css('".$left."','-999em');});";
2589  $out .= "</script>";
2590  }
2591 
2592  if ((!empty($title) && $showtitle) || $morehtmlright || !empty($links)) {
2593  $out .= "</div>\n";
2594  }
2595 
2596  if (!$notab || $notab == -1 || $notab == -2 || $notab == -3) {
2597  $out .= "\n".'<div id="dragDropAreaTabBar" class="tabBar'.($notab == -1 ? '' : ($notab == -2 ? ' tabBarNoTop' : (($notab == -3 ? ' noborderbottom' : '').' tabBarWithBottom')));
2598  $out .= '">'."\n";
2599  }
2600  if (!empty($dragdropfile)) {
2601  include_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
2602  $out .= dragAndDropFileUpload("dragDropAreaTabBar");
2603  }
2604  $parameters = array('tabname' => $active, 'out' => $out);
2605  $reshook = $hookmanager->executeHooks('printTabsHead', $parameters); // This hook usage is called just before output the head of tabs. Take also a look at "completeTabsHead"
2606  if ($reshook > 0) {
2607  $out = $hookmanager->resPrint;
2608  }
2609 
2610  return $out;
2611 }
2612 
2620 function dol_fiche_end($notab = 0)
2621 {
2622  print dol_get_fiche_end($notab);
2623 }
2624 
2631 function dol_get_fiche_end($notab = 0)
2632 {
2633  if (!$notab || $notab == -1) {
2634  return "\n</div>\n";
2635  } else {
2636  return '';
2637  }
2638 }
2639 
2659 function dol_banner_tab($object, $paramid, $morehtml = '', $shownav = 1, $fieldid = 'rowid', $fieldref = 'ref', $morehtmlref = '', $moreparam = '', $nodbprefix = 0, $morehtmlleft = '', $morehtmlstatus = '', $onlybanner = 0, $morehtmlright = '')
2660 {
2661  global $conf, $form, $user, $langs, $hookmanager, $action;
2662 
2663  $error = 0;
2664 
2665  $maxvisiblephotos = 1;
2666  $showimage = 1;
2667  $entity = (empty($object->entity) ? $conf->entity : $object->entity);
2668  // @phan-suppress-next-line PhanUndeclaredMethod
2669  $showbarcode = !isModEnabled('barcode') ? 0 : (empty($object->barcode) ? 0 : 1);
2670  if (getDolGlobalString('MAIN_USE_ADVANCED_PERMS') && !$user->hasRight('barcode', 'lire_advance')) {
2671  $showbarcode = 0;
2672  }
2673  $modulepart = 'unknown';
2674 
2675  if (in_array($object->element, ['societe', 'contact', 'product', 'ticket', 'bom'])) {
2676  $modulepart = $object->element;
2677  } elseif ($object->element == 'member') {
2678  $modulepart = 'memberphoto';
2679  } elseif ($object->element == 'user') {
2680  $modulepart = 'userphoto';
2681  }
2682 
2683  if (class_exists("Imagick")) {
2684  if ($object->element == 'expensereport' || $object->element == 'propal' || $object->element == 'commande' || $object->element == 'facture' || $object->element == 'supplier_proposal') {
2685  $modulepart = $object->element;
2686  } elseif ($object->element == 'fichinter' || $object->element == 'intervention') {
2687  $modulepart = 'ficheinter';
2688  } elseif ($object->element == 'contrat' || $object->element == 'contract') {
2689  $modulepart = 'contract';
2690  } elseif ($object->element == 'order_supplier') {
2691  $modulepart = 'supplier_order';
2692  } elseif ($object->element == 'invoice_supplier') {
2693  $modulepart = 'supplier_invoice';
2694  }
2695  }
2696 
2697  if ($object->element == 'product') {
2699  '@phan-var-force Product $object';
2700  $width = 80;
2701  $cssclass = 'photowithmargin photoref';
2702  $showimage = $object->is_photo_available($conf->product->multidir_output[$entity]);
2703  $maxvisiblephotos = getDolGlobalInt('PRODUCT_MAX_VISIBLE_PHOTO', 5);
2704  if ($conf->browser->layout == 'phone') {
2705  $maxvisiblephotos = 1;
2706  }
2707  if ($showimage) {
2708  $morehtmlleft .= '<div class="floatleft inline-block valignmiddle divphotoref">'.$object->show_photos('product', $conf->product->multidir_output[$entity], 1, $maxvisiblephotos, 0, 0, 0, 0, $width, 0, '').'</div>';
2709  } else {
2710  if (getDolGlobalString('PRODUCT_NODISPLAYIFNOPHOTO')) {
2711  $nophoto = '';
2712  $morehtmlleft .= '<div class="floatleft inline-block valignmiddle divphotoref"></div>';
2713  } else { // Show no photo link
2714  $nophoto = '/public/theme/common/nophoto.png';
2715  $morehtmlleft .= '<div class="floatleft inline-block valignmiddle divphotoref"><img class="photo'.$modulepart.($cssclass ? ' '.$cssclass : '').'" title="'.dol_escape_htmltag($langs->trans("UploadAnImageToSeeAPhotoHere", $langs->transnoentitiesnoconv("Documents"))).'" alt="No photo"'.($width ? ' style="width: '.$width.'px"' : '').' src="'.DOL_URL_ROOT.$nophoto.'"></div>';
2716  }
2717  }
2718  } elseif ($object->element == 'category') {
2720  '@phan-var-force Categorie $object';
2721  $width = 80;
2722  $cssclass = 'photowithmargin photoref';
2723  $showimage = $object->isAnyPhotoAvailable($conf->categorie->multidir_output[$entity]);
2724  $maxvisiblephotos = getDolGlobalInt('CATEGORY_MAX_VISIBLE_PHOTO', 5);
2725  if ($conf->browser->layout == 'phone') {
2726  $maxvisiblephotos = 1;
2727  }
2728  if ($showimage) {
2729  $morehtmlleft .= '<div class="floatleft inline-block valignmiddle divphotoref">'.$object->show_photos('category', $conf->categorie->multidir_output[$entity], 'small', $maxvisiblephotos, 0, 0, 0, 0, $width, 0, '').'</div>';
2730  } else {
2731  if (getDolGlobalString('CATEGORY_NODISPLAYIFNOPHOTO')) {
2732  $nophoto = '';
2733  $morehtmlleft .= '<div class="floatleft inline-block valignmiddle divphotoref"></div>';
2734  } else { // Show no photo link
2735  $nophoto = '/public/theme/common/nophoto.png';
2736  $morehtmlleft .= '<div class="floatleft inline-block valignmiddle divphotoref"><img class="photo'.$modulepart.($cssclass ? ' '.$cssclass : '').'" title="'.dol_escape_htmltag($langs->trans("UploadAnImageToSeeAPhotoHere", $langs->transnoentitiesnoconv("Documents"))).'" alt="No photo"'.($width ? ' style="width: '.$width.'px"' : '').' src="'.DOL_URL_ROOT.$nophoto.'"></div>';
2737  }
2738  }
2739  } elseif ($object->element == 'bom') {
2741  '@phan-var-force Bom $object';
2742  $width = 80;
2743  $cssclass = 'photowithmargin photoref';
2744  $showimage = $object->is_photo_available($conf->bom->multidir_output[$entity]);
2745  $maxvisiblephotos = getDolGlobalInt('BOM_MAX_VISIBLE_PHOTO', 5);
2746  if ($conf->browser->layout == 'phone') {
2747  $maxvisiblephotos = 1;
2748  }
2749  if ($showimage) {
2750  $morehtmlleft .= '<div class="floatleft inline-block valignmiddle divphotoref">'.$object->show_photos('bom', $conf->bom->multidir_output[$entity], 'small', $maxvisiblephotos, 0, 0, 0, 0, $width, 0, '').'</div>';
2751  } else {
2752  if (getDolGlobalString('BOM_NODISPLAYIFNOPHOTO')) {
2753  $nophoto = '';
2754  $morehtmlleft .= '<div class="floatleft inline-block valignmiddle divphotoref"></div>';
2755  } else { // Show no photo link
2756  $nophoto = '/public/theme/common/nophoto.png';
2757  $morehtmlleft .= '<div class="floatleft inline-block valignmiddle divphotoref"><img class="photo'.$modulepart.($cssclass ? ' '.$cssclass : '').'" title="'.dol_escape_htmltag($langs->trans("UploadAnImageToSeeAPhotoHere", $langs->transnoentitiesnoconv("Documents"))).'" alt="No photo"'.($width ? ' style="width: '.$width.'px"' : '').' src="'.DOL_URL_ROOT.$nophoto.'"></div>';
2758  }
2759  }
2760  } elseif ($object->element == 'ticket') {
2761  $width = 80;
2762  $cssclass = 'photoref';
2764  '@phan-var-force Ticket $object';
2765  $showimage = $object->is_photo_available($conf->ticket->multidir_output[$entity].'/'.$object->ref);
2766  $maxvisiblephotos = getDolGlobalInt('TICKET_MAX_VISIBLE_PHOTO', 2);
2767  if ($conf->browser->layout == 'phone') {
2768  $maxvisiblephotos = 1;
2769  }
2770 
2771  if ($showimage) {
2772  $showphoto = $object->show_photos('ticket', $conf->ticket->multidir_output[$entity], 'small', $maxvisiblephotos, 0, 0, 0, $width, 0);
2773  if ($object->nbphoto > 0) {
2774  $morehtmlleft .= '<div class="floatleft inline-block valignmiddle divphotoref">'.$showphoto.'</div>';
2775  } else {
2776  $showimage = 0;
2777  }
2778  }
2779  if (!$showimage) {
2780  if (getDolGlobalString('TICKET_NODISPLAYIFNOPHOTO')) {
2781  $nophoto = '';
2782  $morehtmlleft .= '<div class="floatleft inline-block valignmiddle divphotoref"></div>';
2783  } else { // Show no photo link
2784  $nophoto = img_picto('No photo', 'object_ticket');
2785  $morehtmlleft .= '<!-- No photo to show -->';
2786  $morehtmlleft .= '<div class="floatleft inline-block valignmiddle divphotoref"><div class="photoref">';
2787  $morehtmlleft .= $nophoto;
2788  $morehtmlleft .= '</div></div>';
2789  }
2790  }
2791  } else {
2792  if ($showimage) {
2793  if ($modulepart != 'unknown' || method_exists($object, 'getDataToShowPhoto')) {
2794  $phototoshow = '';
2795  // Check if a preview file is available
2796  if (in_array($modulepart, array('propal', 'commande', 'facture', 'ficheinter', 'contract', 'supplier_order', 'supplier_proposal', 'supplier_invoice', 'expensereport')) && class_exists("Imagick")) {
2797  $objectref = dol_sanitizeFileName($object->ref);
2798  $dir_output = (empty($conf->$modulepart->multidir_output[$entity]) ? $conf->$modulepart->dir_output : $conf->$modulepart->multidir_output[$entity])."/";
2799  if (in_array($modulepart, array('invoice_supplier', 'supplier_invoice'))) {
2800  $subdir = get_exdir($object->id, 2, 0, 1, $object, $modulepart);
2801  $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
2802  } else {
2803  $subdir = get_exdir($object->id, 0, 0, 1, $object, $modulepart);
2804  }
2805  if (empty($subdir)) {
2806  $subdir = 'errorgettingsubdirofobject'; // Protection to avoid to return empty path
2807  }
2808 
2809  $filepath = $dir_output.$subdir."/";
2810 
2811  $filepdf = $filepath.$objectref.".pdf";
2812  $relativepath = $subdir.'/'.$objectref.'.pdf';
2813 
2814  // Define path to preview pdf file (preview precompiled "file.ext" are "file.ext_preview.png")
2815  $fileimage = $filepdf.'_preview.png';
2816  $relativepathimage = $relativepath.'_preview.png';
2817 
2818  $pdfexists = file_exists($filepdf);
2819 
2820  // If PDF file exists
2821  if ($pdfexists) {
2822  // Conversion du PDF en image png si fichier png non existent
2823  if (!file_exists($fileimage) || (filemtime($fileimage) < filemtime($filepdf))) {
2824  if (!getDolGlobalString('MAIN_DISABLE_PDF_THUMBS')) { // If you experience trouble with pdf thumb generation and imagick, you can disable here.
2825  include_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
2826  $ret = dol_convert_file($filepdf, 'png', $fileimage, '0'); // Convert first page of PDF into a file _preview.png
2827  if ($ret < 0) {
2828  $error++;
2829  }
2830  }
2831  }
2832  }
2833 
2834  if ($pdfexists && !$error) {
2835  $heightforphotref = 80;
2836  if (!empty($conf->dol_optimize_smallscreen)) {
2837  $heightforphotref = 60;
2838  }
2839  // If the preview file is found
2840  if (file_exists($fileimage)) {
2841  $phototoshow = '<div class="photoref">';
2842  $phototoshow .= '<img height="'.$heightforphotref.'" class="photo photowithborder" src="'.DOL_URL_ROOT.'/viewimage.php?modulepart=apercu'.$modulepart.'&amp;file='.urlencode($relativepathimage).'">';
2843  $phototoshow .= '</div>';
2844  }
2845  }
2846  } elseif (!$phototoshow) { // example if modulepart = 'societe' or 'photo' or 'memberphoto'
2847  $phototoshow .= $form->showphoto($modulepart, $object, 0, 0, 0, 'photowithmargin photoref', 'small', 1, 0, $maxvisiblephotos);
2848  }
2849 
2850  if ($phototoshow) {
2851  $morehtmlleft .= '<div class="floatleft inline-block valignmiddle divphotoref">';
2852  $morehtmlleft .= $phototoshow;
2853  $morehtmlleft .= '</div>';
2854  }
2855  }
2856 
2857  if (empty($phototoshow)) { // Show No photo link (picto of object)
2858  if ($object->element == 'action') {
2859  $width = 80;
2860  $cssclass = 'photorefcenter';
2861  $nophoto = img_picto('No photo', 'title_agenda');
2862  } else {
2863  $width = 14;
2864  $cssclass = 'photorefcenter';
2865  $picto = $object->picto; // @phan-suppress-current-line PhanUndeclaredProperty
2866  $prefix = 'object_';
2867  if ($object->element == 'project' && !$object->public) { // @phan-suppress-current-line PhanUndeclaredProperty
2868  $picto = 'project'; // instead of projectpub
2869  }
2870  if (strpos($picto, 'fontawesome_') !== false) {
2871  $prefix = '';
2872  }
2873  $nophoto = img_picto('No photo', $prefix.$picto);
2874  }
2875  $morehtmlleft .= '<!-- No photo to show -->';
2876  $morehtmlleft .= '<div class="floatleft inline-block valignmiddle divphotoref"><div class="photoref">';
2877  $morehtmlleft .= $nophoto;
2878  $morehtmlleft .= '</div></div>';
2879  }
2880  }
2881  }
2882 
2883  // Show barcode
2884  if ($showbarcode) {
2885  $morehtmlleft .= '<div class="floatleft inline-block valignmiddle divphotoref">'.$form->showbarcode($object, 100, 'photoref valignmiddle').'</div>';
2886  }
2887 
2888  if ($object->element == 'societe') {
2889  if (!empty($conf->use_javascript_ajax) && $user->hasRight('societe', 'creer') && getDolGlobalString('MAIN_DIRECT_STATUS_UPDATE')) {
2890  $morehtmlstatus .= ajax_object_onoff($object, 'status', 'status', 'InActivity', 'ActivityCeased');
2891  } else {
2892  $morehtmlstatus .= $object->getLibStatut(6);
2893  }
2894  } elseif ($object->element == 'product') {
2895  //$morehtmlstatus.=$langs->trans("Status").' ('.$langs->trans("Sell").') ';
2896  if (!empty($conf->use_javascript_ajax) && $user->hasRight('produit', 'creer') && getDolGlobalString('MAIN_DIRECT_STATUS_UPDATE')) {
2897  $morehtmlstatus .= ajax_object_onoff($object, 'status', 'tosell', 'ProductStatusOnSell', 'ProductStatusNotOnSell');
2898  } else {
2899  $morehtmlstatus .= '<span class="statusrefsell">'.$object->getLibStatut(6, 0).'</span>';
2900  }
2901  $morehtmlstatus .= ' &nbsp; ';
2902  //$morehtmlstatus.=$langs->trans("Status").' ('.$langs->trans("Buy").') ';
2903  if (!empty($conf->use_javascript_ajax) && $user->hasRight('produit', 'creer') && getDolGlobalString('MAIN_DIRECT_STATUS_UPDATE')) {
2904  $morehtmlstatus .= ajax_object_onoff($object, 'status_buy', 'tobuy', 'ProductStatusOnBuy', 'ProductStatusNotOnBuy');
2905  } else {
2906  $morehtmlstatus .= '<span class="statusrefbuy">'.$object->getLibStatut(6, 1).'</span>';
2907  }
2908  } elseif (in_array($object->element, array('salary'))) {
2909  $tmptxt = $object->getLibStatut(6, $object->alreadypaid);
2910  if (empty($tmptxt) || $tmptxt == $object->getLibStatut(3)) {
2911  $tmptxt = $object->getLibStatut(5, $object->alreadypaid);
2912  }
2913  $morehtmlstatus .= $tmptxt;
2914  } elseif (in_array($object->element, array('facture', 'invoice', 'invoice_supplier', 'chargesociales', 'loan', 'tva'))) { // TODO Move this to use ->alreadypaid
2915  $tmptxt = $object->getLibStatut(6, $object->totalpaid);
2916  if (empty($tmptxt) || $tmptxt == $object->getLibStatut(3)) {
2917  $tmptxt = $object->getLibStatut(5, $object->totalpaid);
2918  }
2919  $morehtmlstatus .= $tmptxt;
2920  } elseif ($object->element == 'contrat' || $object->element == 'contract') {
2921  if ($object->statut == 0) {
2922  $morehtmlstatus .= $object->getLibStatut(5);
2923  } else {
2924  $morehtmlstatus .= $object->getLibStatut(4);
2925  }
2926  } elseif ($object->element == 'facturerec') {
2927  '@phan-var-force FactureRec $object';
2928  if ($object->frequency == 0) {
2929  $morehtmlstatus .= $object->getLibStatut(2);
2930  } else {
2931  $morehtmlstatus .= $object->getLibStatut(5);
2932  }
2933  } elseif ($object->element == 'project_task') {
2934  $object->fk_statut = 1;
2935  if ($object->progress > 0) {
2936  $object->fk_statut = 2;
2937  }
2938  if ($object->progress >= 100) {
2939  $object->fk_statut = 3;
2940  }
2941  $tmptxt = $object->getLibStatut(5);
2942  $morehtmlstatus .= $tmptxt; // No status on task
2943  } elseif (method_exists($object, 'getLibStatut')) { // Generic case for status
2944  $tmptxt = $object->getLibStatut(6);
2945  if (empty($tmptxt) || $tmptxt == $object->getLibStatut(3)) {
2946  $tmptxt = $object->getLibStatut(5);
2947  }
2948  $morehtmlstatus .= $tmptxt;
2949  }
2950 
2951  // Add if object was dispatched "into accountancy"
2952  if (isModEnabled('accounting') && in_array($object->element, array('bank', 'paiementcharge', 'facture', 'invoice', 'invoice_supplier', 'expensereport', 'payment_various'))) {
2953  // Note: For 'chargesociales', 'salaries'... this is the payments that are dispatched (so element = 'bank')
2954  if (method_exists($object, 'getVentilExportCompta')) {
2955  $accounted = $object->getVentilExportCompta();
2956  $langs->load("accountancy");
2957  $morehtmlstatus .= '</div><div class="statusref statusrefbis"><span class="opacitymedium">'.($accounted > 0 ? $langs->trans("Accounted") : $langs->trans("NotYetAccounted")).'</span>';
2958  }
2959  }
2960 
2961  // Add alias for thirdparty
2962  if (!empty($object->name_alias)) {
2963  '@phan-var-force Societe $object';
2964  $morehtmlref .= '<div class="refidno opacitymedium">'.dol_escape_htmltag($object->name_alias).'</div>';
2965  }
2966 
2967  // Add label
2968  if (in_array($object->element, array('product', 'bank_account', 'project_task'))) {
2969  if (!empty($object->label)) {
2970  $morehtmlref .= '<div class="refidno opacitymedium">'.$object->label.'</div>';
2971  }
2972  }
2973  // Show address and email
2974  if (method_exists($object, 'getBannerAddress') && !in_array($object->element, array('product', 'bookmark', 'ecm_directories', 'ecm_files'))) {
2975  $moreaddress = $object->getBannerAddress('refaddress', $object); // address, email, url, social networks
2976  if ($moreaddress) {
2977  $morehtmlref .= '<div class="refidno refaddress">';
2978  $morehtmlref .= $moreaddress;
2979  $morehtmlref .= '</div>';
2980  }
2981  }
2982  if (getDolGlobalString('MAIN_SHOW_TECHNICAL_ID') && (getDolGlobalString('MAIN_SHOW_TECHNICAL_ID') == '1' || preg_match('/'.preg_quote($object->element, '/').'/i', $conf->global->MAIN_SHOW_TECHNICAL_ID)) && !empty($object->id)) {
2983  $morehtmlref .= '<div style="clear: both;"></div>';
2984  $morehtmlref .= '<div class="refidno opacitymedium">';
2985  $morehtmlref .= $langs->trans("TechnicalID").': '.((int) $object->id);
2986  $morehtmlref .= '</div>';
2987  }
2988 
2989  $parameters = array('morehtmlref' => &$morehtmlref, 'moreparam' => &$moreparam, 'morehtmlleft' => &$morehtmlleft, 'morehtmlstatus' => &$morehtmlstatus, 'morehtmlright' => &$morehtmlright);
2990  $reshook = $hookmanager->executeHooks('formDolBanner', $parameters, $object, $action);
2991  if ($reshook < 0) {
2992  setEventMessages($hookmanager->error, $hookmanager->errors, 'errors');
2993  } elseif (empty($reshook)) {
2994  $morehtmlref .= $hookmanager->resPrint;
2995  } elseif ($reshook > 0) {
2996  $morehtmlref = $hookmanager->resPrint;
2997  }
2998 
2999 
3000  print '<div class="'.($onlybanner ? 'arearefnobottom ' : 'arearef ').'heightref valignmiddle centpercent">';
3001  print $form->showrefnav($object, $paramid, $morehtml, $shownav, $fieldid, $fieldref, $morehtmlref, $moreparam, $nodbprefix, $morehtmlleft, $morehtmlstatus, $morehtmlright);
3002  print '</div>';
3003  print '<div class="underrefbanner clearboth"></div>';
3004 }
3005 
3015 function fieldLabel($langkey, $fieldkey, $fieldrequired = 0)
3016 {
3017  global $langs;
3018  $ret = '';
3019  if ($fieldrequired) {
3020  $ret .= '<span class="fieldrequired">';
3021  }
3022  $ret .= '<label for="'.$fieldkey.'">';
3023  $ret .= $langs->trans($langkey);
3024  $ret .= '</label>';
3025  if ($fieldrequired) {
3026  $ret .= '</span>';
3027  }
3028  return $ret;
3029 }
3030 
3038 function dol_bc($var, $moreclass = '')
3039 {
3040  global $bc;
3041  $ret = ' '.$bc[$var];
3042  if ($moreclass) {
3043  $ret = preg_replace('/class=\"/', 'class="'.$moreclass.' ', $ret);
3044  }
3045  return $ret;
3046 }
3047 
3061 function dol_format_address($object, $withcountry = 0, $sep = "\n", $outputlangs = null, $mode = 0, $extralangcode = '')
3062 {
3063  global $langs, $hookmanager;
3064 
3065  $ret = '';
3066  $countriesusingstate = array('AU', 'CA', 'US', 'IN', 'GB', 'ES', 'UK', 'TR', 'CN'); // See also MAIN_FORCE_STATE_INTO_ADDRESS
3067 
3068  // See format of addresses on https://en.wikipedia.org/wiki/Address
3069  // Address
3070  if (empty($mode)) {
3071  $ret .= ($extralangcode ? $object->array_languages['address'][$extralangcode] : (empty($object->address) ? '' : preg_replace('/(\r\n|\r|\n)+/', $sep, $object->address)));
3072  }
3073  // Zip/Town/State
3074  if (isset($object->country_code) && in_array($object->country_code, array('AU', 'CA', 'US', 'CN')) || getDolGlobalString('MAIN_FORCE_STATE_INTO_ADDRESS')) {
3075  // US: title firstname name \n address lines \n town, state, zip \n country
3076  $town = ($extralangcode ? $object->array_languages['town'][$extralangcode] : (empty($object->town) ? '' : $object->town));
3077  $ret .= (($ret && $town) ? $sep : '').$town;
3078 
3079  if (!empty($object->state)) {
3080  $ret .= ($ret ? ($town ? ", " : $sep) : '').$object->state;
3081  }
3082  if (!empty($object->zip)) {
3083  $ret .= ($ret ? (($town || $object->state) ? ", " : $sep) : '').$object->zip;
3084  }
3085  } elseif (isset($object->country_code) && in_array($object->country_code, array('GB', 'UK'))) {
3086  // UK: title firstname name \n address lines \n town state \n zip \n country
3087  $town = ($extralangcode ? $object->array_languages['town'][$extralangcode] : (empty($object->town) ? '' : $object->town));
3088  $ret .= ($ret ? $sep : '').$town;
3089  if (!empty($object->state)) {
3090  $ret .= ($ret ? ", " : '').$object->state;
3091  }
3092  if (!empty($object->zip)) {
3093  $ret .= ($ret ? $sep : '').$object->zip;
3094  }
3095  } elseif (isset($object->country_code) && in_array($object->country_code, array('ES', 'TR'))) {
3096  // ES: title firstname name \n address lines \n zip town \n state \n country
3097  $ret .= ($ret ? $sep : '').$object->zip;
3098  $town = ($extralangcode ? $object->array_languages['town'][$extralangcode] : (empty($object->town) ? '' : $object->town));
3099  $ret .= ($town ? (($object->zip ? ' ' : '').$town) : '');
3100  if (!empty($object->state)) {
3101  $ret .= $sep.$object->state;
3102  }
3103  } elseif (isset($object->country_code) && in_array($object->country_code, array('JP'))) {
3104  // JP: In romaji, title firstname name\n address lines \n [state,] town zip \n country
3105  // See https://www.sljfaq.org/afaq/addresses.html
3106  $town = ($extralangcode ? $object->array_languages['town'][$extralangcode] : (empty($object->town) ? '' : $object->town));
3107  $ret .= ($ret ? $sep : '').($object->state ? $object->state.', ' : '').$town.($object->zip ? ' ' : '').$object->zip;
3108  } elseif (isset($object->country_code) && in_array($object->country_code, array('IT'))) {
3109  // IT: title firstname name\n address lines \n zip town state_code \n country
3110  $ret .= ($ret ? $sep : '').$object->zip;
3111  $town = ($extralangcode ? $object->array_languages['town'][$extralangcode] : (empty($object->town) ? '' : $object->town));
3112  $ret .= ($town ? (($object->zip ? ' ' : '').$town) : '');
3113  $ret .= (empty($object->state_code) ? '' : (' '.$object->state_code));
3114  } else {
3115  // Other: title firstname name \n address lines \n zip town[, state] \n country
3116  $town = ($extralangcode ? $object->array_languages['town'][$extralangcode] : (empty($object->town) ? '' : $object->town));
3117  $ret .= !empty($object->zip) ? (($ret ? $sep : '').$object->zip) : '';
3118  $ret .= ($town ? (($object->zip ? ' ' : ($ret ? $sep : '')).$town) : '');
3119  if (!empty($object->state) && in_array($object->country_code, $countriesusingstate)) {
3120  $ret .= ($ret ? ", " : '').$object->state;
3121  }
3122  }
3123 
3124  if (!is_object($outputlangs)) {
3125  $outputlangs = $langs;
3126  }
3127  if ($withcountry) {
3128  $langs->load("dict");
3129  $ret .= (empty($object->country_code) ? '' : ($ret ? $sep : '').$outputlangs->convToOutputCharset($outputlangs->transnoentitiesnoconv("Country".$object->country_code)));
3130  }
3131  if ($hookmanager) {
3132  $parameters = array('withcountry' => $withcountry, 'sep' => $sep, 'outputlangs' => $outputlangs,'mode' => $mode, 'extralangcode' => $extralangcode);
3133  $reshook = $hookmanager->executeHooks('formatAddress', $parameters, $object);
3134  if ($reshook > 0) {
3135  $ret = '';
3136  }
3137  $ret .= $hookmanager->resPrint;
3138  }
3139 
3140  return $ret;
3141 }
3142 
3143 
3144 
3154 function dol_strftime($fmt, $ts = false, $is_gmt = false)
3155 {
3156  if ((abs($ts) <= 0x7FFFFFFF)) { // check if number in 32-bit signed range
3157  return dol_print_date($ts, $fmt, $is_gmt);
3158  } else {
3159  return 'Error date outside supported range';
3160  }
3161 }
3162 
3184 function dol_print_date($time, $format = '', $tzoutput = 'auto', $outputlangs = null, $encodetooutput = false)
3185 {
3186  global $conf, $langs;
3187 
3188  // If date undefined or "", we return ""
3189  if (dol_strlen($time) == 0) {
3190  return ''; // $time=0 allowed (it means 01/01/1970 00:00:00)
3191  }
3192 
3193  if ($tzoutput === 'auto') {
3194  $tzoutput = (empty($conf) ? 'tzserver' : (isset($conf->tzuserinputkey) ? $conf->tzuserinputkey : 'tzserver'));
3195  }
3196 
3197  // Clean parameters
3198  $to_gmt = false;
3199  $offsettz = $offsetdst = 0;
3200  if ($tzoutput) {
3201  $to_gmt = true; // For backward compatibility
3202  if (is_string($tzoutput)) {
3203  if ($tzoutput == 'tzserver') {
3204  $to_gmt = false;
3205  $offsettzstring = @date_default_timezone_get(); // Example 'Europe/Berlin' or 'Indian/Reunion'
3206  // @phan-suppress-next-line PhanPluginRedundantAssignment
3207  $offsettz = 0; // Timezone offset with server timezone (because to_gmt is false), so 0
3208  // @phan-suppress-next-line PhanPluginRedundantAssignment
3209  $offsetdst = 0; // Dst offset with server timezone (because to_gmt is false), so 0
3210  } elseif ($tzoutput == 'tzuser' || $tzoutput == 'tzuserrel') {
3211  $to_gmt = true;
3212  $offsettzstring = (empty($_SESSION['dol_tz_string']) ? 'UTC' : $_SESSION['dol_tz_string']); // Example 'Europe/Berlin' or 'Indian/Reunion'
3213 
3214  if (class_exists('DateTimeZone')) {
3215  $user_date_tz = new DateTimeZone($offsettzstring);
3216  $user_dt = new DateTime();
3217  $user_dt->setTimezone($user_date_tz);
3218  $user_dt->setTimestamp($tzoutput == 'tzuser' ? dol_now() : (int) $time);
3219  $offsettz = $user_dt->getOffset(); // should include dst ?
3220  } else { // with old method (The 'tzuser' was processed like the 'tzuserrel')
3221  $offsettz = (empty($_SESSION['dol_tz']) ? 0 : $_SESSION['dol_tz']) * 60 * 60; // Will not be used anymore
3222  $offsetdst = (empty($_SESSION['dol_dst']) ? 0 : $_SESSION['dol_dst']) * 60 * 60; // Will not be used anymore
3223  }
3224  }
3225  }
3226  }
3227  if (!is_object($outputlangs)) {
3228  $outputlangs = $langs;
3229  }
3230  if (!$format) {
3231  $format = 'daytextshort';
3232  }
3233 
3234  // Do we have to reduce the length of date (year on 2 chars) to save space.
3235  // Note: dayinputnoreduce is same than day but no reduction of year length will be done
3236  $reduceformat = (!empty($conf->dol_optimize_smallscreen) && in_array($format, array('day', 'dayhour', 'dayhoursec'))) ? 1 : 0; // Test on original $format param.
3237  $format = preg_replace('/inputnoreduce/', '', $format); // so format 'dayinputnoreduce' is processed like day
3238  $formatwithoutreduce = preg_replace('/reduceformat/', '', $format);
3239  if ($formatwithoutreduce != $format) {
3240  $format = $formatwithoutreduce;
3241  $reduceformat = 1;
3242  } // so format 'dayreduceformat' is processed like day
3243 
3244  // Change predefined format into computer format. If found translation in lang file we use it, otherwise we use default.
3245  // TODO Add format daysmallyear and dayhoursmallyear
3246  if ($format == 'day') {
3247  $format = ($outputlangs->trans("FormatDateShort") != "FormatDateShort" ? $outputlangs->trans("FormatDateShort") : $conf->format_date_short);
3248  } elseif ($format == 'hour') {
3249  $format = ($outputlangs->trans("FormatHourShort") != "FormatHourShort" ? $outputlangs->trans("FormatHourShort") : $conf->format_hour_short);
3250  } elseif ($format == 'hourduration') {
3251  $format = ($outputlangs->trans("FormatHourShortDuration") != "FormatHourShortDuration" ? $outputlangs->trans("FormatHourShortDuration") : $conf->format_hour_short_duration);
3252  } elseif ($format == 'daytext') {
3253  $format = ($outputlangs->trans("FormatDateText") != "FormatDateText" ? $outputlangs->trans("FormatDateText") : $conf->format_date_text);
3254  } elseif ($format == 'daytextshort') {
3255  $format = ($outputlangs->trans("FormatDateTextShort") != "FormatDateTextShort" ? $outputlangs->trans("FormatDateTextShort") : $conf->format_date_text_short);
3256  } elseif ($format == 'dayhour') {
3257  $format = ($outputlangs->trans("FormatDateHourShort") != "FormatDateHourShort" ? $outputlangs->trans("FormatDateHourShort") : $conf->format_date_hour_short);
3258  } elseif ($format == 'dayhoursec') {
3259  $format = ($outputlangs->trans("FormatDateHourSecShort") != "FormatDateHourSecShort" ? $outputlangs->trans("FormatDateHourSecShort") : $conf->format_date_hour_sec_short);
3260  } elseif ($format == 'dayhourtext') {
3261  $format = ($outputlangs->trans("FormatDateHourText") != "FormatDateHourText" ? $outputlangs->trans("FormatDateHourText") : $conf->format_date_hour_text);
3262  } elseif ($format == 'dayhourtextshort') {
3263  $format = ($outputlangs->trans("FormatDateHourTextShort") != "FormatDateHourTextShort" ? $outputlangs->trans("FormatDateHourTextShort") : $conf->format_date_hour_text_short);
3264  } elseif ($format == 'dayhourlog') {
3265  // Format not sensitive to language
3266  $format = '%Y%m%d%H%M%S';
3267  } elseif ($format == 'dayhourlogsmall') {
3268  // Format not sensitive to language
3269  $format = '%y%m%d%H%M';
3270  } elseif ($format == 'dayhourldap') {
3271  $format = '%Y%m%d%H%M%SZ';
3272  } elseif ($format == 'dayhourxcard') {
3273  $format = '%Y%m%dT%H%M%SZ';
3274  } elseif ($format == 'dayxcard') {
3275  $format = '%Y%m%d';
3276  } elseif ($format == 'dayrfc') {
3277  $format = '%Y-%m-%d'; // DATE_RFC3339
3278  } elseif ($format == 'dayhourrfc') {
3279  $format = '%Y-%m-%dT%H:%M:%SZ'; // DATETIME RFC3339
3280  } elseif ($format == 'standard') {
3281  $format = '%Y-%m-%d %H:%M:%S';
3282  }
3283 
3284  if ($reduceformat) {
3285  $format = str_replace('%Y', '%y', $format);
3286  $format = str_replace('yyyy', 'yy', $format);
3287  }
3288 
3289  // Clean format
3290  if (preg_match('/%b/i', $format)) { // There is some text to translate
3291  // We inhibit translation to text made by strftime functions. We will use trans instead later.
3292  $format = str_replace('%b', '__b__', $format);
3293  $format = str_replace('%B', '__B__', $format);
3294  }
3295  if (preg_match('/%a/i', $format)) { // There is some text to translate
3296  // We inhibit translation to text made by strftime functions. We will use trans instead later.
3297  $format = str_replace('%a', '__a__', $format);
3298  $format = str_replace('%A', '__A__', $format);
3299  }
3300 
3301  // Analyze date
3302  $reg = array();
3303  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', (string) $time, $reg)) { // Deprecated. Ex: 1970-01-01, 1970-01-01 01:00:00, 19700101010000
3304  dol_print_error(null, "Functions.lib::dol_print_date function called with a bad value from page ".(empty($_SERVER["PHP_SELF"]) ? 'unknown' : $_SERVER["PHP_SELF"]));
3305  return '';
3306  } elseif (preg_match('/^([0-9]+)\-([0-9]+)\-([0-9]+) ?([0-9]+)?:?([0-9]+)?:?([0-9]+)?/i', (string) $time, $reg)) { // Still available to solve problems in extrafields of type date
3307  // This part of code should not be used anymore.
3308  dol_syslog("Functions.lib::dol_print_date function called with a bad value from page ".(empty($_SERVER["PHP_SELF"]) ? 'unknown' : $_SERVER["PHP_SELF"]), LOG_WARNING);
3309  //if (function_exists('debug_print_backtrace')) debug_print_backtrace();
3310  // Date has format 'YYYY-MM-DD' or 'YYYY-MM-DD HH:MM:SS'
3311  $syear = (!empty($reg[1]) ? $reg[1] : '');
3312  $smonth = (!empty($reg[2]) ? $reg[2] : '');
3313  $sday = (!empty($reg[3]) ? $reg[3] : '');
3314  $shour = (!empty($reg[4]) ? $reg[4] : '');
3315  $smin = (!empty($reg[5]) ? $reg[5] : '');
3316  $ssec = (!empty($reg[6]) ? $reg[6] : '');
3317 
3318  $time = dol_mktime($shour, $smin, $ssec, $smonth, $sday, $syear, true);
3319 
3320  if ($to_gmt) {
3321  $tzo = new DateTimeZone('UTC'); // when to_gmt is true, base for offsettz and offsetdst (so timetouse) is UTC
3322  } else {
3323  $tzo = new DateTimeZone(date_default_timezone_get()); // when to_gmt is false, base for offsettz and offsetdst (so timetouse) is PHP server
3324  }
3325  $dtts = new DateTime();
3326  $dtts->setTimestamp($time);
3327  $dtts->setTimezone($tzo);
3328  $newformat = str_replace(
3329  array('%Y', '%y', '%m', '%d', '%H', '%I', '%M', '%S', '%p', 'T', 'Z', '__a__', '__A__', '__b__', '__B__'),
3330  array('Y', 'y', 'm', 'd', 'H', 'h', 'i', 's', 'A', '__£__', '__$__', '__{__', '__}__', '__[__', '__]__'),
3331  $format
3332  );
3333  $ret = $dtts->format($newformat);
3334  $ret = str_replace(
3335  array('__£__', '__$__', '__{__', '__}__', '__[__', '__]__'),
3336  array('T', 'Z', '__a__', '__A__', '__b__', '__B__'),
3337  $ret
3338  );
3339  } else {
3340  // Date is a timestamps
3341  if ($time < 100000000000) { // Protection against bad date values
3342  $timetouse = $time + $offsettz + $offsetdst; // TODO We could be able to disable use of offsettz and offsetdst to use only offsettzstring.
3343 
3344  if ($to_gmt) {
3345  $tzo = new DateTimeZone('UTC'); // when to_gmt is true, base for offsettz and offsetdst (so timetouse) is UTC
3346  } else {
3347  $tzo = new DateTimeZone(date_default_timezone_get()); // when to_gmt is false, base for offsettz and offsetdst (so timetouse) is PHP server
3348  }
3349  $dtts = new DateTime();
3350  $dtts->setTimestamp($timetouse);
3351  $dtts->setTimezone($tzo);
3352  $newformat = str_replace(
3353  array('%Y', '%y', '%m', '%d', '%H', '%I', '%M', '%S', '%p', '%w', 'T', 'Z', '__a__', '__A__', '__b__', '__B__'),
3354  array('Y', 'y', 'm', 'd', 'H', 'h', 'i', 's', 'A', 'w', '__£__', '__$__', '__{__', '__}__', '__[__', '__]__'),
3355  $format
3356  );
3357  $ret = $dtts->format($newformat);
3358  $ret = str_replace(
3359  array('__£__', '__$__', '__{__', '__}__', '__[__', '__]__'),
3360  array('T', 'Z', '__a__', '__A__', '__b__', '__B__'),
3361  $ret
3362  );
3363  //var_dump($ret);exit;
3364  } else {
3365  $ret = 'Bad value '.$time.' for date';
3366  }
3367  }
3368 
3369  if (preg_match('/__b__/i', $format)) {
3370  $timetouse = $time + $offsettz + $offsetdst; // TODO We could be able to disable use of offsettz and offsetdst to use only offsettzstring.
3371 
3372  if ($to_gmt) {
3373  $tzo = new DateTimeZone('UTC'); // when to_gmt is true, base for offsettz and offsetdst (so timetouse) is UTC
3374  } else {
3375  $tzo = new DateTimeZone(date_default_timezone_get()); // when to_gmt is false, base for offsettz and offsetdst (so timetouse) is PHP server
3376  }
3377  $dtts = new DateTime();
3378  $dtts->setTimestamp($timetouse);
3379  $dtts->setTimezone($tzo);
3380  $month = (int) $dtts->format("m");
3381  $month = sprintf("%02d", $month); // $month may be return with format '06' on some installation and '6' on other, so we force it to '06'.
3382  if ($encodetooutput) {
3383  $monthtext = $outputlangs->transnoentities('Month'.$month);
3384  $monthtextshort = $outputlangs->transnoentities('MonthShort'.$month);
3385  } else {
3386  $monthtext = $outputlangs->transnoentitiesnoconv('Month'.$month);
3387  $monthtextshort = $outputlangs->transnoentitiesnoconv('MonthShort'.$month);
3388  }
3389  //print 'monthtext='.$monthtext.' monthtextshort='.$monthtextshort;
3390  $ret = str_replace('__b__', $monthtextshort, $ret);
3391  $ret = str_replace('__B__', $monthtext, $ret);
3392  //print 'x'.$outputlangs->charset_output.'-'.$ret.'x';
3393  //return $ret;
3394  }
3395  if (preg_match('/__a__/i', $format)) {
3396  //print "time=$time offsettz=$offsettz offsetdst=$offsetdst offsettzstring=$offsettzstring";
3397  $timetouse = $time + $offsettz + $offsetdst; // TODO Replace this with function Date PHP. We also should not use anymore offsettz and offsetdst but only offsettzstring.
3398 
3399  if ($to_gmt) {
3400  $tzo = new DateTimeZone('UTC');
3401  } else {
3402  $tzo = new DateTimeZone(date_default_timezone_get());
3403  }
3404  $dtts = new DateTime();
3405  $dtts->setTimestamp($timetouse);
3406  $dtts->setTimezone($tzo);
3407  $w = $dtts->format("w");
3408  $dayweek = $outputlangs->transnoentitiesnoconv('Day'.$w);
3409 
3410  $ret = str_replace('__A__', $dayweek, $ret);
3411  $ret = str_replace('__a__', dol_substr($dayweek, 0, 3), $ret);
3412  }
3413 
3414  return $ret;
3415 }
3416 
3417 
3438 function dol_getdate($timestamp, $fast = false, $forcetimezone = '')
3439 {
3440  if ($timestamp === '') {
3441  return array();
3442  }
3443 
3444  $datetimeobj = new DateTime();
3445  $datetimeobj->setTimestamp($timestamp); // Use local PHP server timezone
3446  if ($forcetimezone) {
3447  $datetimeobj->setTimezone(new DateTimeZone($forcetimezone == 'gmt' ? 'UTC' : $forcetimezone)); // (add timezone relative to the date entered)
3448  }
3449  $arrayinfo = array(
3450  'year' => ((int) date_format($datetimeobj, 'Y')),
3451  'mon' => ((int) date_format($datetimeobj, 'm')),
3452  'mday' => ((int) date_format($datetimeobj, 'd')),
3453  'wday' => ((int) date_format($datetimeobj, 'w')),
3454  'yday' => ((int) date_format($datetimeobj, 'z')),
3455  'hours' => ((int) date_format($datetimeobj, 'H')),
3456  'minutes' => ((int) date_format($datetimeobj, 'i')),
3457  'seconds' => ((int) date_format($datetimeobj, 's')),
3458  '0' => $timestamp
3459  );
3460 
3461  return $arrayinfo;
3462 }
3463 
3485 function dol_mktime($hour, $minute, $second, $month, $day, $year, $gm = 'auto', $check = 1)
3486 {
3487  global $conf;
3488  //print "- ".$hour.",".$minute.",".$second.",".$month.",".$day.",".$year.",".$_SERVER["WINDIR"]." -";
3489 
3490  if ($gm === 'auto') {
3491  $gm = (empty($conf) ? 'tzserver' : $conf->tzuserinputkey);
3492  }
3493  //print 'gm:'.$gm.' gm === auto:'.($gm === 'auto').'<br>';exit;
3494 
3495  // Clean parameters
3496  if ($hour == -1 || empty($hour)) {
3497  $hour = 0;
3498  }
3499  if ($minute == -1 || empty($minute)) {
3500  $minute = 0;
3501  }
3502  if ($second == -1 || empty($second)) {
3503  $second = 0;
3504  }
3505 
3506  // Check parameters
3507  if ($check) {
3508  if (!$month || !$day) {
3509  return '';
3510  }
3511  if ($day > 31) {
3512  return '';
3513  }
3514  if ($month > 12) {
3515  return '';
3516  }
3517  if ($hour < 0 || $hour > 24) {
3518  return '';
3519  }
3520  if ($minute < 0 || $minute > 60) {
3521  return '';
3522  }
3523  if ($second < 0 || $second > 60) {
3524  return '';
3525  }
3526  }
3527 
3528  if (empty($gm) || ($gm === 'server' || $gm === 'tzserver')) {
3529  $default_timezone = @date_default_timezone_get(); // Example 'Europe/Berlin'
3530  $localtz = new DateTimeZone($default_timezone);
3531  } elseif ($gm === 'user' || $gm === 'tzuser' || $gm === 'tzuserrel') {
3532  // We use dol_tz_string first because it is more reliable.
3533  $default_timezone = (empty($_SESSION["dol_tz_string"]) ? @date_default_timezone_get() : $_SESSION["dol_tz_string"]); // Example 'Europe/Berlin'
3534  try {
3535  $localtz = new DateTimeZone($default_timezone);
3536  } catch (Exception $e) {
3537  dol_syslog("Warning dol_tz_string contains an invalid value ".json_encode($_SESSION["dol_tz_string"] ?? null), LOG_WARNING);
3538  $default_timezone = @date_default_timezone_get();
3539  }
3540  } elseif (strrpos($gm, "tz,") !== false) {
3541  $timezone = str_replace("tz,", "", $gm); // Example 'tz,Europe/Berlin'
3542  try {
3543  $localtz = new DateTimeZone($timezone);
3544  } catch (Exception $e) {
3545  dol_syslog("Warning passed timezone contains an invalid value ".$timezone, LOG_WARNING);
3546  }
3547  }
3548 
3549  if (empty($localtz)) {
3550  $localtz = new DateTimeZone('UTC');
3551  }
3552  //var_dump($localtz);
3553  //var_dump($year.'-'.$month.'-'.$day.'-'.$hour.'-'.$minute);
3554  $dt = new DateTime('now', $localtz);
3555  $dt->setDate((int) $year, (int) $month, (int) $day);
3556  $dt->setTime((int) $hour, (int) $minute, (int) $second);
3557  $date = $dt->getTimestamp(); // should include daylight saving time
3558  //var_dump($date);
3559  return $date;
3560 }
3561 
3562 
3573 function dol_now($mode = 'auto')
3574 {
3575  $ret = 0;
3576 
3577  if ($mode === 'auto') {
3578  $mode = 'gmt';
3579  }
3580 
3581  if ($mode == 'gmt') {
3582  $ret = time(); // Time for now at greenwich.
3583  } elseif ($mode == 'tzserver') { // Time for now with PHP server timezone added
3584  require_once DOL_DOCUMENT_ROOT.'/core/lib/date.lib.php';
3585  $tzsecond = getServerTimeZoneInt('now'); // Contains tz+dayling saving time
3586  $ret = (int) (dol_now('gmt') + ($tzsecond * 3600));
3587  //} elseif ($mode == 'tzref') {// Time for now with parent company timezone is added
3588  // require_once DOL_DOCUMENT_ROOT.'/core/lib/date.lib.php';
3589  // $tzsecond=getParentCompanyTimeZoneInt(); // Contains tz+dayling saving time
3590  // $ret=dol_now('gmt')+($tzsecond*3600);
3591  //}
3592  } elseif ($mode == 'tzuser' || $mode == 'tzuserrel') {
3593  // Time for now with user timezone added
3594  //print 'time: '.time();
3595  $offsettz = (empty($_SESSION['dol_tz']) ? 0 : $_SESSION['dol_tz']) * 60 * 60;
3596  $offsetdst = (empty($_SESSION['dol_dst']) ? 0 : $_SESSION['dol_dst']) * 60 * 60;
3597  $ret = (int) (dol_now('gmt') + ($offsettz + $offsetdst));
3598  }
3599 
3600  return $ret;
3601 }
3602 
3603 
3612 function dol_print_size($size, $shortvalue = 0, $shortunit = 0)
3613 {
3614  global $conf, $langs;
3615  $level = 1024;
3616 
3617  if (!empty($conf->dol_optimize_smallscreen)) {
3618  $shortunit = 1;
3619  }
3620 
3621  // Set value text
3622  if (empty($shortvalue) || $size < ($level * 10)) {
3623  $ret = $size;
3624  $textunitshort = $langs->trans("b");
3625  $textunitlong = $langs->trans("Bytes");
3626  } else {
3627  $ret = round($size / $level, 0);
3628  $textunitshort = $langs->trans("Kb");
3629  $textunitlong = $langs->trans("KiloBytes");
3630  }
3631  // Use long or short text unit
3632  if (empty($shortunit)) {
3633  $ret .= ' '.$textunitlong;
3634  } else {
3635  $ret .= ' '.$textunitshort;
3636  }
3637 
3638  return $ret;
3639 }
3640 
3651 function dol_print_url($url, $target = '_blank', $max = 32, $withpicto = 0, $morecss = '')
3652 {
3653  global $langs;
3654 
3655  if (empty($url)) {
3656  return '';
3657  }
3658 
3659  $linkstart = '<a href="';
3660  if (!preg_match('/^http/i', $url)) {
3661  $linkstart .= 'http://';
3662  }
3663  $linkstart .= $url;
3664  $linkstart .= '"';
3665  if ($target) {
3666  $linkstart .= ' target="'.$target.'"';
3667  }
3668  $linkstart .= ' title="'.$langs->trans("URL").': '.$url.'"';
3669  $linkstart .= '>';
3670 
3671  $link = '';
3672  if (!preg_match('/^http/i', $url)) {
3673  $link .= 'http://';
3674  }
3675  $link .= dol_trunc($url, $max);
3676 
3677  $linkend = '</a>';
3678 
3679  if ($morecss == 'float') { // deprecated
3680  return '<div class="nospan'.($morecss ? ' '.$morecss : '').'" style="margin-right: 10px">'.($withpicto ? img_picto($langs->trans("Url"), 'globe', 'class="paddingrightonly"') : '').$link.'</div>';
3681  } else {
3682  return $linkstart.'<span class="nospan'.($morecss ? ' '.$morecss : '').'" style="margin-right: 10px">'.($withpicto ? img_picto('', 'globe', 'class="paddingrightonly"') : '').$link.'</span>'.$linkend;
3683  }
3684 }
3685 
3698 function dol_print_email($email, $cid = 0, $socid = 0, $addlink = 0, $max = 64, $showinvalid = 1, $withpicto = 0)
3699 {
3700  global $user, $langs, $hookmanager;
3701 
3702  //global $conf; $conf->global->AGENDA_ADDACTIONFOREMAIL = 1;
3703  //$showinvalid = 1; $email = 'rrrrr';
3704 
3705  $newemail = dol_escape_htmltag($email);
3706 
3707  if (getDolGlobalString('MAIN_OPTIMIZEFORTEXTBROWSER') && $withpicto) {
3708  $withpicto = 0;
3709  }
3710 
3711  if (empty($email)) {
3712  return '&nbsp;';
3713  }
3714 
3715  if (!empty($addlink)) {
3716  $newemail = '<a class="paddingrightonly" style="text-overflow: ellipsis;" href="';
3717  if (!preg_match('/^mailto:/i', $email)) {
3718  $newemail .= 'mailto:';
3719  }
3720  $newemail .= $email;
3721  $newemail .= '">';
3722 
3723  $newemail .= ($withpicto ? img_picto($langs->trans("EMail").' : '.$email, (is_numeric($withpicto) ? 'email' : $withpicto), 'class="paddingrightonly"') : '');
3724 
3725  $newemail .= dol_trunc($email, $max);
3726  $newemail .= '</a>';
3727  if ($showinvalid && !isValidEmail($email)) {
3728  $langs->load("errors");
3729  $newemail .= img_warning($langs->trans("ErrorBadEMail", $email), '', 'paddingrightonly');
3730  }
3731 
3732  if (($cid || $socid) && isModEnabled('agenda') && $user->hasRight("agenda", "myactions", "create")) {
3733  $type = 'AC_EMAIL';
3734  $linktoaddaction = '';
3735  if (getDolGlobalString('AGENDA_ADDACTIONFOREMAIL')) {
3736  $linktoaddaction = '<a href="'.DOL_URL_ROOT.'/comm/action/card.php?action=create&amp;backtopage=1&amp;actioncode='.urlencode($type).'&amp;contactid='.((int) $cid).'&amp;socid='.((int) $socid).'">'.img_object($langs->trans("AddAction"), "calendar").'</a>';
3737  }
3738  if ($linktoaddaction) {
3739  $newemail = '<div>'.$newemail.' '.$linktoaddaction.'</div>';
3740  }
3741  }
3742  } else {
3743  $newemail = ($withpicto ? img_picto($langs->trans("EMail").' : '.$email, (is_numeric($withpicto) ? 'email' : $withpicto), 'class="paddingrightonly"') : '').$newemail;
3744 
3745  if ($showinvalid && !isValidEmail($email)) {
3746  $langs->load("errors");
3747  $newemail .= img_warning($langs->trans("ErrorBadEMail", $email));
3748  }
3749  }
3750 
3751  //$rep = '<div class="nospan" style="margin-right: 10px">';
3752  //$rep = ($withpicto ? img_picto($langs->trans("EMail").' : '.$email, (is_numeric($withpicto) ? 'email' : $withpicto), 'class="paddingrightonly"') : '').$newemail;
3753  //$rep .= '</div>';
3754  $rep = $newemail;
3755 
3756  if ($hookmanager) {
3757  $parameters = array('cid' => $cid, 'socid' => $socid, 'addlink' => $addlink, 'picto' => $withpicto);
3758 
3759  $reshook = $hookmanager->executeHooks('printEmail', $parameters, $email);
3760  if ($reshook > 0) {
3761  $rep = '';
3762  }
3763  $rep .= $hookmanager->resPrint;
3764  }
3765 
3766  return $rep;
3767 }
3768 
3774 function getArrayOfSocialNetworks()
3775 {
3776  global $conf, $db;
3777 
3778  $socialnetworks = array();
3779  // Enable caching of array
3780  require_once DOL_DOCUMENT_ROOT.'/core/lib/memory.lib.php';
3781  $cachekey = 'socialnetworks_' . $conf->entity;
3782  $dataretrieved = dol_getcache($cachekey);
3783  if (!is_null($dataretrieved)) {
3784  $socialnetworks = $dataretrieved;
3785  } else {
3786  $sql = "SELECT rowid, code, label, url, icon, active FROM ".MAIN_DB_PREFIX."c_socialnetworks";
3787  $sql .= " WHERE entity=".$conf->entity;
3788  $resql = $db->query($sql);
3789  if ($resql) {
3790  while ($obj = $db->fetch_object($resql)) {
3791  $socialnetworks[$obj->code] = array(
3792  'rowid' => $obj->rowid,
3793  'label' => $obj->label,
3794  'url' => $obj->url,
3795  'icon' => $obj->icon,
3796  'active' => $obj->active,
3797  );
3798  }
3799  }
3800  dol_setcache($cachekey, $socialnetworks); // If setting cache fails, this is not a problem, so we do not test result.
3801  }
3802  return $socialnetworks;
3803 }
3804 
3815 function dol_print_socialnetworks($value, $cid, $socid, $type, $dictsocialnetworks = array())
3816 {
3817  global $user, $langs;
3818 
3819  $htmllink = $value;
3820 
3821  if (empty($value)) {
3822  return '&nbsp;';
3823  }
3824 
3825  if (!empty($type)) {
3826  $htmllink = '<div class="divsocialnetwork inline-block valignmiddle">';
3827  // Use dictionary definition for picto $dictsocialnetworks[$type]['icon']
3828  $htmllink .= '<span class="fab pictofixedwidth '.($dictsocialnetworks[$type]['icon'] ? $dictsocialnetworks[$type]['icon'] : 'fa-link').'"></span>';
3829  if ($type == 'skype') {
3830  $htmllink .= dol_escape_htmltag($value);
3831  $htmllink .= '&nbsp; <a href="skype:';
3832  $htmllink .= dol_string_nospecial($value, '_', '', array('@'));
3833  $htmllink .= '?call" alt="'.$langs->trans("Call").'&nbsp;'.$value.'" title="'.dol_escape_htmltag($langs->trans("Call").' '.$value).'">';
3834  $htmllink .= '<img src="'.DOL_URL_ROOT.'/theme/common/skype_callbutton.png" border="0">';
3835  $htmllink .= '</a><a href="skype:';
3836  $htmllink .= dol_string_nospecial($value, '_', '', array('@'));
3837  $htmllink .= '?chat" alt="'.$langs->trans("Chat").'&nbsp;'.$value.'" title="'.dol_escape_htmltag($langs->trans("Chat").' '.$value).'">';
3838  $htmllink .= '<img class="paddingleft" src="'.DOL_URL_ROOT.'/theme/common/skype_chatbutton.png" border="0">';
3839  $htmllink .= '</a>';
3840  if (($cid || $socid) && isModEnabled('agenda') && $user->hasRight('agenda', 'myactions', 'create')) {
3841  $addlink = 'AC_SKYPE';
3842  $link = '';
3843  if (getDolGlobalString('AGENDA_ADDACTIONFORSKYPE')) {
3844  $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>';
3845  }
3846  $htmllink .= ($link ? ' '.$link : '');
3847  }
3848  } else {
3849  $networkconstname = 'MAIN_INFO_SOCIETE_'.strtoupper($type).'_URL';
3850  if (getDolGlobalString($networkconstname)) {
3851  $link = str_replace('{socialid}', $value, getDolGlobalString($networkconstname));
3852  if (preg_match('/^https?:\/\//i', $link)) {
3853  $htmllink .= '<a href="'.dol_sanitizeUrl($link, 0).'" target="_blank" rel="noopener noreferrer">'.dol_escape_htmltag($value).'</a>';
3854  } else {
3855  $htmllink .= '<a href="'.dol_sanitizeUrl($link, 1).'" target="_blank" rel="noopener noreferrer">'.dol_escape_htmltag($value).'</a>';
3856  }
3857  } elseif (!empty($dictsocialnetworks[$type]['url'])) {
3858  $tmpvirginurl = preg_replace('/\/?{socialid}/', '', $dictsocialnetworks[$type]['url']);
3859  if ($tmpvirginurl) {
3860  $value = preg_replace('/^www\.'.preg_quote($tmpvirginurl, '/').'\/?/', '', $value);
3861  $value = preg_replace('/^'.preg_quote($tmpvirginurl, '/').'\/?/', '', $value);
3862 
3863  $tmpvirginurl3 = preg_replace('/^https:\/\//i', 'https://www.', $tmpvirginurl);
3864  if ($tmpvirginurl3) {
3865  $value = preg_replace('/^www\.'.preg_quote($tmpvirginurl3, '/').'\/?/', '', $value);
3866  $value = preg_replace('/^'.preg_quote($tmpvirginurl3, '/').'\/?/', '', $value);
3867  }
3868 
3869  $tmpvirginurl2 = preg_replace('/^https?:\/\//i', '', $tmpvirginurl);
3870  if ($tmpvirginurl2) {
3871  $value = preg_replace('/^www\.'.preg_quote($tmpvirginurl2, '/').'\/?/', '', $value);
3872  $value = preg_replace('/^'.preg_quote($tmpvirginurl2, '/').'\/?/', '', $value);
3873  }
3874  }
3875  $link = str_replace('{socialid}', $value, $dictsocialnetworks[$type]['url']);
3876  if (preg_match('/^https?:\/\//i', $link)) {
3877  $htmllink .= '<a href="'.dol_sanitizeUrl($link, 0).'" target="_blank" rel="noopener noreferrer">'.dol_escape_htmltag($value).'</a>';
3878  } else {
3879  $htmllink .= '<a href="'.dol_sanitizeUrl($link, 1).'" target="_blank" rel="noopener noreferrer">'.dol_escape_htmltag($value).'</a>';
3880  }
3881  } else {
3882  $htmllink .= dol_escape_htmltag($value);
3883  }
3884  }
3885  $htmllink .= '</div>';
3886  } else {
3887  $langs->load("errors");
3888  $htmllink .= img_warning($langs->trans("ErrorBadSocialNetworkValue", $value));
3889  }
3890  return $htmllink;
3891 }
3892 
3902 function dol_print_profids($profID, $profIDtype, $countrycode = '', $addcpButton = 1)
3903 {
3904  global $mysoc;
3905 
3906  if (empty($profID) || empty($profIDtype)) {
3907  return '';
3908  }
3909  if (empty($countrycode)) {
3910  $countrycode = $mysoc->country_code;
3911  }
3912  $newProfID = $profID;
3913  $id = substr($profIDtype, -1);
3914  $ret = '';
3915  if (strtoupper($countrycode) == 'FR') {
3916  // France
3917  // (see https://www.economie.gouv.fr/entreprises/numeros-identification-entreprise)
3918 
3919  if ($id == 1 && dol_strlen($newProfID) == 9) {
3920  // SIREN (ex: 123 123 123)
3921  $newProfID = substr($newProfID, 0, 3).' '.substr($newProfID, 3, 3).' '.substr($newProfID, 6, 3);
3922  }
3923  if ($id == 2 && dol_strlen($newProfID) == 14) {
3924  // SIRET (ex: 123 123 123 12345)
3925  $newProfID = substr($newProfID, 0, 3).' '.substr($newProfID, 3, 3).' '.substr($newProfID, 6, 3).' '.substr($newProfID, 9, 5);
3926  }
3927  if ($id == 3 && dol_strlen($newProfID) == 5) {
3928  // NAF/APE (ex: 69.20Z)
3929  $newProfID = substr($newProfID, 0, 2).'.'.substr($newProfID, 2, 3);
3930  }
3931  if ($profIDtype === 'VAT' && dol_strlen($newProfID) == 13) {
3932  // TVA intracommunautaire (ex: FR12 123 123 123)
3933  $newProfID = substr($newProfID, 0, 4).' '.substr($newProfID, 4, 3).' '.substr($newProfID, 7, 3).' '.substr($newProfID, 10, 3);
3934  }
3935  }
3936  if (!empty($addcpButton)) {
3937  $ret = showValueWithClipboardCPButton(dol_escape_htmltag($profID), ($addcpButton == 1 ? 1 : 0), $newProfID);
3938  } else {
3939  $ret = $newProfID;
3940  }
3941  return $ret;
3942 }
3943 
3958 function dol_print_phone($phone, $countrycode = '', $cid = 0, $socid = 0, $addlink = '', $separ = "&nbsp;", $withpicto = '', $titlealt = '', $adddivfloat = 0)
3959 {
3960  global $conf, $user, $langs, $mysoc, $hookmanager;
3961 
3962  // Clean phone parameter
3963  $phone = is_null($phone) ? '' : preg_replace("/[\s.-]/", "", trim($phone));
3964  if (empty($phone)) {
3965  return '';
3966  }
3967  if (getDolGlobalString('MAIN_PHONE_SEPAR')) {
3968  $separ = getDolGlobalString('MAIN_PHONE_SEPAR');
3969  }
3970  if (empty($countrycode) && is_object($mysoc)) {
3971  $countrycode = $mysoc->country_code;
3972  }
3973 
3974  // Short format for small screens
3975  if ($conf->dol_optimize_smallscreen) {
3976  $separ = '';
3977  }
3978 
3979  $newphone = $phone;
3980  $newphonewa = $phone;
3981  if (strtoupper($countrycode) == "FR") {
3982  // France
3983  if (dol_strlen($phone) == 10) {
3984  $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);
3985  } elseif (dol_strlen($phone) == 7) {
3986  $newphone = substr($newphone, 0, 3).$separ.substr($newphone, 3, 2).$separ.substr($newphone, 5, 2);
3987  } elseif (dol_strlen($phone) == 9) {
3988  $newphone = substr($newphone, 0, 2).$separ.substr($newphone, 2, 3).$separ.substr($newphone, 5, 2).$separ.substr($newphone, 7, 2);
3989  } elseif (dol_strlen($phone) == 11) {
3990  $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);
3991  } elseif (dol_strlen($phone) == 12) {
3992  $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);
3993  } elseif (dol_strlen($phone) == 13) {
3994  $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);
3995  }
3996  } elseif (strtoupper($countrycode) == "CA") {
3997  if (dol_strlen($phone) == 10) {
3998  $newphone = ($separ != '' ? '(' : '').substr($newphone, 0, 3).($separ != '' ? ')' : '').$separ.substr($newphone, 3, 3).($separ != '' ? '-' : '').substr($newphone, 6, 4);
3999  }
4000  } elseif (strtoupper($countrycode) == "PT") {//Portugal
4001  if (dol_strlen($phone) == 13) {//ex: +351_ABC_DEF_GHI
4002  $newphone = substr($newphone, 0, 4).$separ.substr($newphone, 4, 3).$separ.substr($newphone, 7, 3).$separ.substr($newphone, 10, 3);
4003  }
4004  } elseif (strtoupper($countrycode) == "SR") {//Suriname
4005  if (dol_strlen($phone) == 10) {//ex: +597_ABC_DEF
4006  $newphone = substr($newphone, 0, 4).$separ.substr($newphone, 4, 3).$separ.substr($newphone, 7, 3);
4007  } elseif (dol_strlen($phone) == 11) {//ex: +597_ABC_DEFG
4008  $newphone = substr($newphone, 0, 4).$separ.substr($newphone, 4, 3).$separ.substr($newphone, 7, 4);
4009  }
4010  } elseif (strtoupper($countrycode) == "DE") {//Allemagne
4011  if (dol_strlen($phone) == 14) {//ex: +49_ABCD_EFGH_IJK
4012  $newphone = substr($newphone, 0, 3).$separ.substr($newphone, 3, 4).$separ.substr($newphone, 7, 4).$separ.substr($newphone, 11, 3);
4013  } elseif (dol_strlen($phone) == 13) {//ex: +49_ABC_DEFG_HIJ
4014  $newphone = substr($newphone, 0, 3).$separ.substr($newphone, 3, 3).$separ.substr($newphone, 6, 4).$separ.substr($newphone, 10, 3);
4015  }
4016  } elseif (strtoupper($countrycode) == "ES") {//Espagne
4017  if (dol_strlen($phone) == 12) {//ex: +34_ABC_DEF_GHI
4018  $newphone = substr($newphone, 0, 3).$separ.substr($newphone, 3, 3).$separ.substr($newphone, 6, 3).$separ.substr($newphone, 9, 3);
4019  }
4020  } elseif (strtoupper($countrycode) == "BF") {// Burkina Faso
4021  if (dol_strlen($phone) == 12) {//ex : +22 A BC_DE_FG_HI
4022  $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);
4023  }
4024  } elseif (strtoupper($countrycode) == "RO") {// Roumanie
4025  if (dol_strlen($phone) == 12) {//ex : +40 AB_CDE_FG_HI
4026  $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);
4027  }
4028  } elseif (strtoupper($countrycode) == "TR") {//Turquie
4029  if (dol_strlen($phone) == 13) {//ex : +90 ABC_DEF_GHIJ
4030  $newphone = substr($newphone, 0, 3).$separ.substr($newphone, 3, 3).$separ.substr($newphone, 6, 3).$separ.substr($newphone, 9, 4);
4031  }
4032  } elseif (strtoupper($countrycode) == "US") {//Etat-Unis
4033  if (dol_strlen($phone) == 12) {//ex: +1 ABC_DEF_GHIJ
4034  $newphone = substr($newphone, 0, 2).$separ.substr($newphone, 2, 3).$separ.substr($newphone, 5, 3).$separ.substr($newphone, 8, 4);
4035  }
4036  } elseif (strtoupper($countrycode) == "MX") {//Mexique
4037  if (dol_strlen($phone) == 12) {//ex: +52 ABCD_EFG_HI
4038  $newphone = substr($newphone, 0, 3).$separ.substr($newphone, 3, 4).$separ.substr($newphone, 7, 3).$separ.substr($newphone, 10, 2);
4039  } elseif (dol_strlen($phone) == 11) {//ex: +52 AB_CD_EF_GH
4040  $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);
4041  } elseif (dol_strlen($phone) == 13) {//ex: +52 ABC_DEF_GHIJ
4042  $newphone = substr($newphone, 0, 3).$separ.substr($newphone, 3, 3).$separ.substr($newphone, 6, 3).$separ.substr($newphone, 9, 4);
4043  }
4044  } elseif (strtoupper($countrycode) == "ML") {//Mali
4045  if (dol_strlen($phone) == 12) {//ex: +223 AB_CD_EF_GH
4046  $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);
4047  }
4048  } elseif (strtoupper($countrycode) == "TH") {//Thaïlande
4049  if (dol_strlen($phone) == 11) {//ex: +66_ABC_DE_FGH
4050  $newphone = substr($newphone, 0, 3).$separ.substr($newphone, 3, 3).$separ.substr($newphone, 6, 2).$separ.substr($newphone, 8, 3);
4051  } elseif (dol_strlen($phone) == 12) {//ex: +66_A_BCD_EF_GHI
4052  $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);
4053  }
4054  } elseif (strtoupper($countrycode) == "MU") {
4055  //Maurice
4056  if (dol_strlen($phone) == 11) {//ex: +230_ABC_DE_FG
4057  $newphone = substr($newphone, 0, 4).$separ.substr($newphone, 4, 3).$separ.substr($newphone, 7, 2).$separ.substr($newphone, 9, 2);
4058  } elseif (dol_strlen($phone) == 12) {//ex: +230_ABCD_EF_GH
4059  $newphone = substr($newphone, 0, 4).$separ.substr($newphone, 4, 4).$separ.substr($newphone, 8, 2).$separ.substr($newphone, 10, 2);
4060  }
4061  } elseif (strtoupper($countrycode) == "ZA") {//Afrique du sud
4062  if (dol_strlen($phone) == 12) {//ex: +27_AB_CDE_FG_HI
4063  $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);
4064  }
4065  } elseif (strtoupper($countrycode) == "SY") {//Syrie
4066  if (dol_strlen($phone) == 12) {//ex: +963_AB_CD_EF_GH
4067  $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);
4068  } elseif (dol_strlen($phone) == 13) {//ex: +963_AB_CD_EF_GHI
4069  $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);
4070  }
4071  } elseif (strtoupper($countrycode) == "AE") {//Emirats Arabes Unis
4072  if (dol_strlen($phone) == 12) {//ex: +971_ABC_DEF_GH
4073  $newphone = substr($newphone, 0, 4).$separ.substr($newphone, 4, 3).$separ.substr($newphone, 7, 3).$separ.substr($newphone, 10, 2);
4074  } elseif (dol_strlen($phone) == 13) {//ex: +971_ABC_DEF_GHI
4075  $newphone = substr($newphone, 0, 4).$separ.substr($newphone, 4, 3).$separ.substr($newphone, 7, 3).$separ.substr($newphone, 10, 3);
4076  } elseif (dol_strlen($phone) == 14) {//ex: +971_ABC_DEF_GHIK
4077  $newphone = substr($newphone, 0, 4).$separ.substr($newphone, 4, 3).$separ.substr($newphone, 7, 3).$separ.substr($newphone, 10, 4);
4078  }
4079  } elseif (strtoupper($countrycode) == "DZ") {//Algérie
4080  if (dol_strlen($phone) == 13) {//ex: +213_ABC_DEF_GHI
4081  $newphone = substr($newphone, 0, 4).$separ.substr($newphone, 4, 3).$separ.substr($newphone, 7, 3).$separ.substr($newphone, 10, 3);
4082  }
4083  } elseif (strtoupper($countrycode) == "BE") {//Belgique
4084  if (dol_strlen($phone) == 11) {//ex: +32_ABC_DE_FGH
4085  $newphone = substr($newphone, 0, 3).$separ.substr($newphone, 3, 3).$separ.substr($newphone, 6, 2).$separ.substr($newphone, 8, 3);
4086  } elseif (dol_strlen($phone) == 12) {//ex: +32_ABC_DEF_GHI
4087  $newphone = substr($newphone, 0, 3).$separ.substr($newphone, 3, 3).$separ.substr($newphone, 6, 3).$separ.substr($newphone, 9, 3);
4088  }
4089  } elseif (strtoupper($countrycode) == "PF") {//Polynésie française
4090  if (dol_strlen($phone) == 12) {//ex: +689_AB_CD_EF_GH
4091  $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);
4092  }
4093  } elseif (strtoupper($countrycode) == "CO") {//Colombie
4094  if (dol_strlen($phone) == 13) {//ex: +57_ABC_DEF_GH_IJ
4095  $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);
4096  }
4097  } elseif (strtoupper($countrycode) == "JO") {//Jordanie
4098  if (dol_strlen($phone) == 12) {//ex: +962_A_BCD_EF_GH
4099  $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);
4100  }
4101  } elseif (strtoupper($countrycode) == "JM") {//Jamaïque
4102  if (dol_strlen($newphone) == 12) {//ex: +1867_ABC_DEFG
4103  $newphone = substr($newphone, 0, 5).$separ.substr($newphone, 5, 3).$separ.substr($newphone, 8, 4);
4104  }
4105  } elseif (strtoupper($countrycode) == "MG") {//Madagascar
4106  if (dol_strlen($phone) == 13) {//ex: +261_AB_CD_EFG_HI
4107  $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);
4108  }
4109  } elseif (strtoupper($countrycode) == "GB") {//Royaume uni
4110  if (dol_strlen($phone) == 13) {//ex: +44_ABCD_EFG_HIJ
4111  $newphone = substr($newphone, 0, 3).$separ.substr($newphone, 3, 4).$separ.substr($newphone, 7, 3).$separ.substr($newphone, 10, 3);
4112  }
4113  } elseif (strtoupper($countrycode) == "CH") {//Suisse
4114  if (dol_strlen($phone) == 12) {//ex: +41_AB_CDE_FG_HI
4115  $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);
4116  } elseif (dol_strlen($phone) == 15) {// +41_AB_CDE_FGH_IJKL
4117  $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);
4118  }
4119  } elseif (strtoupper($countrycode) == "TN") {//Tunisie
4120  if (dol_strlen($phone) == 12) {//ex: +216_AB_CDE_FGH
4121  $newphone = substr($newphone, 0, 4).$separ.substr($newphone, 4, 2).$separ.substr($newphone, 6, 3).$separ.substr($newphone, 9, 3);
4122  }
4123  } elseif (strtoupper($countrycode) == "GF") {//Guyane francaise
4124  if (dol_strlen($phone) == 13) {//ex: +594_ABC_DE_FG_HI (ABC=594 de nouveau)
4125  $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);
4126  }
4127  } elseif (strtoupper($countrycode) == "GP") {//Guadeloupe
4128  if (dol_strlen($phone) == 13) {//ex: +590_ABC_DE_FG_HI (ABC=590 de nouveau)
4129  $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);
4130  }
4131  } elseif (strtoupper($countrycode) == "MQ") {//Martinique
4132  if (dol_strlen($phone) == 13) {//ex: +596_ABC_DE_FG_HI (ABC=596 de nouveau)
4133  $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);
4134  }
4135  } elseif (strtoupper($countrycode) == "IT") {//Italie
4136  if (dol_strlen($phone) == 12) {//ex: +39_ABC_DEF_GHI
4137  $newphone = substr($newphone, 0, 3).$separ.substr($newphone, 3, 3).$separ.substr($newphone, 6, 3).$separ.substr($newphone, 9, 3);
4138  } elseif (dol_strlen($phone) == 13) {//ex: +39_ABC_DEF_GH_IJ
4139  $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);
4140  }
4141  } elseif (strtoupper($countrycode) == "AU") {
4142  //Australie
4143  if (dol_strlen($phone) == 12) {
4144  //ex: +61_A_BCDE_FGHI
4145  $newphone = substr($newphone, 0, 3).$separ.substr($newphone, 3, 1).$separ.substr($newphone, 4, 4).$separ.substr($newphone, 8, 4);
4146  }
4147  } elseif (strtoupper($countrycode) == "LU") {
4148  // Luxembourg
4149  if (dol_strlen($phone) == 10) {// fix 6 digits +352_AA_BB_CC
4150  $newphone = substr($newphone, 0, 4).$separ.substr($newphone, 4, 2).$separ.substr($newphone, 6, 2).$separ.substr($newphone, 8, 2);
4151  } elseif (dol_strlen($phone) == 11) {// fix 7 digits +352_AA_BB_CC_D
4152  $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);
4153  } elseif (dol_strlen($phone) == 12) {// fix 8 digits +352_AA_BB_CC_DD
4154  $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);
4155  } elseif (dol_strlen($phone) == 13) {// mobile +352_AAA_BB_CC_DD
4156  $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);
4157  }
4158  } elseif (strtoupper($countrycode) == "PE") {
4159  // Peru
4160  if (dol_strlen($phone) == 7) {// fix 7 chiffres without code AAA_BBBB
4161  $newphone = substr($newphone, 0, 3).$separ.substr($newphone, 3, 4);
4162  } elseif (dol_strlen($phone) == 9) {// mobile add code and fix 9 chiffres +51_AAA_BBB_CCC
4163  $newphonewa = '+51'.$newphone;
4164  $newphone = substr($newphone, 0, 3).$separ.substr($newphone, 3, 3).$separ.substr($newphone, 6, 3).$separ.substr($newphone, 10, 3);
4165  } elseif (dol_strlen($phone) == 11) {// fix 11 chiffres +511_AAA_BBBB
4166  $newphone = substr($newphone, 0, 4).$separ.substr($newphone, 4, 3).$separ.substr($newphone, 8, 4);
4167  } elseif (dol_strlen($phone) == 12) {// mobile +51_AAA_BBB_CCC
4168  $newphonewa = $newphone;
4169  $newphone = substr($newphone, 0, 3).$separ.substr($newphone, 3, 3).$separ.substr($newphone, 6, 3).$separ.substr($newphone, 10, 3).$separ.substr($newphone, 14, 3);
4170  }
4171  }
4172 
4173  $newphoneastart = $newphoneaend = '';
4174  if (!empty($addlink)) { // Link on phone number (+ link to add action if conf->global->AGENDA_ADDACTIONFORPHONE set)
4175  if ($addlink == 'tel' || $conf->browser->layout == 'phone' || (isModEnabled('clicktodial') && getDolGlobalString('CLICKTODIAL_USE_TEL_LINK_ON_PHONE_NUMBERS'))) { // If phone or option for, we use link of phone
4176  $newphoneastart = '<a href="tel:'.urlencode($phone).'">';
4177  $newphoneaend .= '</a>';
4178  } elseif (isModEnabled('clicktodial') && $addlink == 'AC_TEL') { // If click to dial, we use click to dial url
4179  if (empty($user->clicktodial_loaded)) {
4180  $user->fetch_clicktodial();
4181  }
4182 
4183  // Define urlmask
4184  $urlmask = getDolGlobalString('CLICKTODIAL_URL', 'ErrorClickToDialModuleNotConfigured');
4185  if (!empty($user->clicktodial_url)) {
4186  $urlmask = $user->clicktodial_url;
4187  }
4188 
4189  $clicktodial_poste = (!empty($user->clicktodial_poste) ? urlencode($user->clicktodial_poste) : '');
4190  $clicktodial_login = (!empty($user->clicktodial_login) ? urlencode($user->clicktodial_login) : '');
4191  $clicktodial_password = (!empty($user->clicktodial_password) ? urlencode($user->clicktodial_password) : '');
4192  // This line is for backward compatibility @phan-suppress-next-line PhanPluginPrintfVariableFormatString
4193  $url = sprintf($urlmask, urlencode($phone), $clicktodial_poste, $clicktodial_login, $clicktodial_password);
4194  // Those lines are for substitution
4195  $substitarray = array('__PHONEFROM__' => $clicktodial_poste,
4196  '__PHONETO__' => urlencode($phone),
4197  '__LOGIN__' => $clicktodial_login,
4198  '__PASS__' => $clicktodial_password);
4199  $url = make_substitutions($url, $substitarray);
4200  if (!getDolGlobalString('CLICKTODIAL_DO_NOT_USE_AJAX_CALL')) {
4201  // Default and recommended: New method using ajax without submitting a page making a javascript history.go(-1) back
4202  $newphoneastart = '<a href="'.$url.'" class="cssforclicktodial">'; // Call of ajax is handled by the lib_foot.js.php on class 'cssforclicktodial'
4203  $newphoneaend = '</a>';
4204  } else {
4205  // Old method
4206  $newphoneastart = '<a href="'.$url.'"';
4207  if (getDolGlobalString('CLICKTODIAL_FORCENEWTARGET')) {
4208  $newphoneastart .= ' target="_blank" rel="noopener noreferrer"';
4209  }
4210  $newphoneastart .= '>';
4211  $newphoneaend .= '</a>';
4212  }
4213  }
4214 
4215  //if (($cid || $socid) && isModEnabled('agenda') && $user->hasRight('agenda', 'myactions', 'create'))
4216  if (isModEnabled('agenda') && $user->hasRight("agenda", "myactions", "create")) {
4217  $type = 'AC_TEL';
4218  $addlinktoagenda = '';
4219  if ($addlink == 'AC_FAX') {
4220  $type = 'AC_FAX';
4221  }
4222  if (getDolGlobalString('AGENDA_ADDACTIONFORPHONE')) {
4223  $addlinktoagenda = '<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>';
4224  }
4225  if ($addlinktoagenda) {
4226  $newphone = '<span>'.$newphone.' '.$addlinktoagenda.'</span>';
4227  }
4228  }
4229  }
4230 
4231  if (getDolGlobalString('CONTACT_PHONEMOBILE_SHOW_LINK_TO_WHATSAPP') && $withpicto == 'mobile') {
4232  // Link to Whatsapp
4233  $newphone .= ' <a href="https://wa.me/'.$newphonewa.'" target="_blank"';// Use api to whatasapp contacts
4234  $newphone .= '><span class="paddingright fab fa-whatsapp" style="color:#25D366;" title="WhatsApp"></span></a>';
4235  }
4236 
4237  if (empty($titlealt)) {
4238  $titlealt = ($withpicto == 'fax' ? $langs->trans("Fax") : $langs->trans("Phone"));
4239  }
4240  $rep = '';
4241 
4242  if ($hookmanager) {
4243  $parameters = array('countrycode' => $countrycode, 'cid' => $cid, 'socid' => $socid, 'titlealt' => $titlealt, 'picto' => $withpicto);
4244  $reshook = $hookmanager->executeHooks('printPhone', $parameters, $phone);
4245  $rep .= $hookmanager->resPrint;
4246  }
4247  if (empty($reshook)) {
4248  $picto = '';
4249  if ($withpicto) {
4250  if ($withpicto == 'fax') {
4251  $picto = 'phoning_fax';
4252  } elseif ($withpicto == 'phone') {
4253  $picto = 'phoning';
4254  } elseif ($withpicto == 'mobile') {
4255  $picto = 'phoning_mobile';
4256  } else {
4257  $picto = '';
4258  }
4259  }
4260  if ($adddivfloat == 1) {
4261  $rep .= '<div class="nospan float" style="margin-right: 10px">';
4262  } elseif (empty($adddivfloat)) {
4263  $rep .= '<span style="margin-right: 10px;">';
4264  }
4265 
4266  $rep .= $newphoneastart;
4267  $rep .= ($withpicto ? img_picto($titlealt, 'object_'.$picto.'.png') : '');
4268  if ($separ != 'hidenum') {
4269  $rep .= ($withpicto ? ' ' : '').$newphone;
4270  }
4271  $rep .= $newphoneaend;
4272 
4273  if ($adddivfloat == 1) {
4274  $rep .= '</div>';
4275  } elseif (empty($adddivfloat)) {
4276  $rep .= '</span>';
4277  }
4278  }
4279 
4280  return $rep;
4281 }
4282 
4290 function dol_print_ip($ip, $mode = 0)
4291 {
4292  global $langs;
4293 
4294  $ret = '';
4295 
4296  if (empty($mode)) {
4297  $ret .= $ip;
4298  }
4299 
4300  if ($mode != 2) {
4301  $countrycode = dolGetCountryCodeFromIp($ip);
4302  if ($countrycode) { // If success, countrycode is us, fr, ...
4303  if (file_exists(DOL_DOCUMENT_ROOT.'/theme/common/flags/'.$countrycode.'.png')) {
4304  $ret .= ' '.img_picto($countrycode.' '.$langs->trans("AccordingToGeoIPDatabase"), DOL_URL_ROOT.'/theme/common/flags/'.$countrycode.'.png', '', 1);
4305  } else {
4306  $ret .= ' ('.$countrycode.')';
4307  }
4308  } else {
4309  // Nothing
4310  }
4311  }
4312 
4313  return $ret;
4314 }
4315 
4324 function getUserRemoteIP()
4325 {
4326  if (empty($_SERVER['HTTP_X_FORWARDED_FOR']) || preg_match('/[^0-9\.\:,\[\]]/', $_SERVER['HTTP_X_FORWARDED_FOR'])) {
4327  if (empty($_SERVER['HTTP_CLIENT_IP']) || preg_match('/[^0-9\.\:,\[\]]/', $_SERVER['HTTP_CLIENT_IP'])) {
4328  if (empty($_SERVER["HTTP_CF_CONNECTING_IP"])) {
4329  $ip = (empty($_SERVER['REMOTE_ADDR']) ? '' : $_SERVER['REMOTE_ADDR']); // value may have been the IP of the proxy and not the client
4330  } else {
4331  $ip = $_SERVER["HTTP_CF_CONNECTING_IP"]; // value here may have been forged by client
4332  }
4333  } else {
4334  $ip = $_SERVER['HTTP_CLIENT_IP']; // value is clean here but may have been forged by proxy
4335  }
4336  } else {
4337  $ip = $_SERVER['HTTP_X_FORWARDED_FOR']; // value is clean here but may have been forged by proxy
4338  }
4339  return $ip;
4340 }
4341 
4350 function isHTTPS()
4351 {
4352  $isSecure = false;
4353  if (isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] == 'on') {
4354  $isSecure = true;
4355  } 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') {
4356  $isSecure = true;
4357  }
4358  return $isSecure;
4359 }
4360 
4367 function dolGetCountryCodeFromIp($ip)
4368 {
4369  global $conf;
4370 
4371  $countrycode = '';
4372 
4373  if (!empty($conf->geoipmaxmind->enabled)) {
4374  $datafile = getDolGlobalString('GEOIPMAXMIND_COUNTRY_DATAFILE');
4375  //$ip='24.24.24.24';
4376  //$datafile='/usr/share/GeoIP/GeoIP.dat'; Note that this must be downloaded datafile (not same than datafile provided with ubuntu packages)
4377  include_once DOL_DOCUMENT_ROOT.'/core/class/dolgeoip.class.php';
4378  $geoip = new DolGeoIP('country', $datafile);
4379  //print 'ip='.$ip.' databaseType='.$geoip->gi->databaseType." GEOIP_CITY_EDITION_REV1=".GEOIP_CITY_EDITION_REV1."\n";
4380  $countrycode = $geoip->getCountryCodeFromIP($ip);
4381  }
4382 
4383  return $countrycode;
4384 }
4385 
4386 
4393 function dol_user_country()
4394 {
4395  global $conf, $langs, $user;
4396 
4397  //$ret=$user->xxx;
4398  $ret = '';
4399  if (!empty($conf->geoipmaxmind->enabled)) {
4400  $ip = getUserRemoteIP();
4401  $datafile = getDolGlobalString('GEOIPMAXMIND_COUNTRY_DATAFILE');
4402  //$ip='24.24.24.24';
4403  //$datafile='E:\Mes Sites\Web\Admin1\awstats\maxmind\GeoIP.dat';
4404  include_once DOL_DOCUMENT_ROOT.'/core/class/dolgeoip.class.php';
4405  $geoip = new DolGeoIP('country', $datafile);
4406  $countrycode = $geoip->getCountryCodeFromIP($ip);
4407  $ret = $countrycode;
4408  }
4409  return $ret;
4410 }
4411 
4424 function dol_print_address($address, $htmlid, $element, $id, $noprint = 0, $charfornl = '')
4425 {
4426  global $conf, $user, $langs, $hookmanager;
4427 
4428  $out = '';
4429 
4430  if ($address) {
4431  if ($hookmanager) {
4432  $parameters = array('element' => $element, 'id' => $id);
4433  $reshook = $hookmanager->executeHooks('printAddress', $parameters, $address);
4434  $out .= $hookmanager->resPrint;
4435  }
4436  if (empty($reshook)) {
4437  if (empty($charfornl)) {
4438  $out .= nl2br($address);
4439  } else {
4440  $out .= preg_replace('/[\r\n]+/', $charfornl, $address);
4441  }
4442 
4443  // TODO Remove this block, we can add this using the hook now
4444  $showgmap = $showomap = 0;
4445  if (($element == 'thirdparty' || $element == 'societe') && isModEnabled('google') && getDolGlobalString('GOOGLE_ENABLE_GMAPS')) {
4446  $showgmap = 1;
4447  }
4448  if ($element == 'contact' && isModEnabled('google') && getDolGlobalString('GOOGLE_ENABLE_GMAPS_CONTACTS')) {
4449  $showgmap = 1;
4450  }
4451  if ($element == 'member' && isModEnabled('google') && getDolGlobalString('GOOGLE_ENABLE_GMAPS_MEMBERS')) {
4452  $showgmap = 1;
4453  }
4454  if ($element == 'user' && isModEnabled('google') && getDolGlobalString('GOOGLE_ENABLE_GMAPS_USERS')) {
4455  $showgmap = 1;
4456  }
4457  if (($element == 'thirdparty' || $element == 'societe') && isModEnabled('openstreetmap') && getDolGlobalString('OPENSTREETMAP_ENABLE_MAPS')) {
4458  $showomap = 1;
4459  }
4460  if ($element == 'contact' && isModEnabled('openstreetmap') && getDolGlobalString('OPENSTREETMAP_ENABLE_MAPS_CONTACTS')) {
4461  $showomap = 1;
4462  }
4463  if ($element == 'member' && isModEnabled('openstreetmap') && getDolGlobalString('OPENSTREETMAP_ENABLE_MAPS_MEMBERS')) {
4464  $showomap = 1;
4465  }
4466  if ($element == 'user' && isModEnabled('openstreetmap') && getDolGlobalString('OPENSTREETMAP_ENABLE_MAPS_USERS')) {
4467  $showomap = 1;
4468  }
4469  if ($showgmap) {
4470  $url = dol_buildpath('/google/gmaps.php?mode='.$element.'&id='.$id, 1);
4471  $out .= ' <a href="'.$url.'" target="_gmaps"><img id="'.$htmlid.'" class="valigntextbottom" src="'.DOL_URL_ROOT.'/theme/common/gmap.png"></a>';
4472  }
4473  if ($showomap) {
4474  $url = dol_buildpath('/openstreetmap/maps.php?mode='.$element.'&id='.$id, 1);
4475  $out .= ' <a href="'.$url.'" target="_gmaps"><img id="'.$htmlid.'_openstreetmap" class="valigntextbottom" src="'.DOL_URL_ROOT.'/theme/common/gmap.png"></a>';
4476  }
4477  }
4478  }
4479  if ($noprint) {
4480  return $out;
4481  } else {
4482  print $out;
4483  }
4484 }
4485 
4486 
4496 function isValidEmail($address, $acceptsupervisorkey = 0, $acceptuserkey = 0)
4497 {
4498  if ($acceptsupervisorkey && $address == '__SUPERVISOREMAIL__') {
4499  return true;
4500  }
4501  if ($acceptuserkey && $address == '__USER_EMAIL__') {
4502  return true;
4503  }
4504  if (filter_var($address, FILTER_VALIDATE_EMAIL)) {
4505  return true;
4506  }
4507 
4508  return false;
4509 }
4510 
4520 function isValidMXRecord($domain)
4521 {
4522  if (function_exists('idn_to_ascii') && function_exists('checkdnsrr')) {
4523  if (!checkdnsrr(idn_to_ascii($domain), 'MX')) {
4524  return 0;
4525  }
4526  if (function_exists('getmxrr')) {
4527  $mxhosts = array();
4528  $weight = array();
4529  getmxrr(idn_to_ascii($domain), $mxhosts, $weight);
4530  if (count($mxhosts) > 1) {
4531  return 1;
4532  }
4533  if (count($mxhosts) == 1 && !in_array((string) $mxhosts[0], array('', '.'))) {
4534  return 1;
4535  }
4536 
4537  return 0;
4538  }
4539  }
4540 
4541  // function idn_to_ascii or checkdnsrr or getmxrr does not exists
4542  return -1;
4543 }
4544 
4552 function isValidPhone($phone)
4553 {
4554  return true;
4555 }
4556 
4557 
4567 function dolGetFirstLetters($s, $nbofchar = 1)
4568 {
4569  $ret = '';
4570  $tmparray = explode(' ', $s);
4571  foreach ($tmparray as $tmps) {
4572  $ret .= dol_substr($tmps, 0, $nbofchar);
4573  }
4574 
4575  return $ret;
4576 }
4577 
4578 
4586 function dol_strlen($string, $stringencoding = 'UTF-8')
4587 {
4588  if (is_null($string)) {
4589  return 0;
4590  }
4591 
4592  if (function_exists('mb_strlen')) {
4593  return mb_strlen($string, $stringencoding);
4594  } else {
4595  return strlen($string);
4596  }
4597 }
4598 
4609 function dol_substr($string, $start, $length = null, $stringencoding = '', $trunconbytes = 0)
4610 {
4611  global $langs;
4612 
4613  if (empty($stringencoding)) {
4614  $stringencoding = (empty($langs) ? 'UTF-8' : $langs->charset_output);
4615  }
4616 
4617  $ret = '';
4618  if (empty($trunconbytes)) {
4619  if (function_exists('mb_substr')) {
4620  $ret = mb_substr($string, $start, $length, $stringencoding);
4621  } else {
4622  $ret = substr($string, $start, $length);
4623  }
4624  } else {
4625  if (function_exists('mb_strcut')) {
4626  $ret = mb_strcut($string, $start, $length, $stringencoding);
4627  } else {
4628  $ret = substr($string, $start, $length);
4629  }
4630  }
4631  return $ret;
4632 }
4633 
4634 
4648 function dol_trunc($string, $size = 40, $trunc = 'right', $stringencoding = 'UTF-8', $nodot = 0, $display = 0)
4649 {
4650  global $conf;
4651 
4652  if (empty($size) || getDolGlobalString('MAIN_DISABLE_TRUNC')) {
4653  return $string;
4654  }
4655 
4656  if (empty($stringencoding)) {
4657  $stringencoding = 'UTF-8';
4658  }
4659  // reduce for small screen
4660  if (!empty($conf->dol_optimize_smallscreen) && $conf->dol_optimize_smallscreen == 1 && $display == 1) {
4661  $size = round($size / 3);
4662  }
4663 
4664  // We go always here
4665  if ($trunc == 'right') {
4666  $newstring = dol_textishtml($string) ? dol_string_nohtmltag($string, 1) : $string;
4667  if (dol_strlen($newstring, $stringencoding) > ($size + ($nodot ? 0 : 1))) {
4668  // If nodot is 0 and size is 1 chars more, we don't trunc and don't add …
4669  return dol_substr($newstring, 0, $size, $stringencoding).($nodot ? '' : '…');
4670  } else {
4671  //return 'u'.$size.'-'.$newstring.'-'.dol_strlen($newstring,$stringencoding).'-'.$string;
4672  return $string;
4673  }
4674  } elseif ($trunc == 'middle') {
4675  $newstring = dol_textishtml($string) ? dol_string_nohtmltag($string, 1) : $string;
4676  if (dol_strlen($newstring, $stringencoding) > 2 && dol_strlen($newstring, $stringencoding) > ($size + 1)) {
4677  $size1 = round($size / 2);
4678  $size2 = round($size / 2);
4679  return dol_substr($newstring, 0, $size1, $stringencoding).'…'.dol_substr($newstring, dol_strlen($newstring, $stringencoding) - $size2, $size2, $stringencoding);
4680  } else {
4681  return $string;
4682  }
4683  } elseif ($trunc == 'left') {
4684  $newstring = dol_textishtml($string) ? dol_string_nohtmltag($string, 1) : $string;
4685  if (dol_strlen($newstring, $stringencoding) > ($size + ($nodot ? 0 : 1))) {
4686  // If nodot is 0 and size is 1 chars more, we don't trunc and don't add …
4687  return '…'.dol_substr($newstring, dol_strlen($newstring, $stringencoding) - $size, $size, $stringencoding);
4688  } else {
4689  return $string;
4690  }
4691  } elseif ($trunc == 'wrap') {
4692  $newstring = dol_textishtml($string) ? dol_string_nohtmltag($string, 1) : $string;
4693  if (dol_strlen($newstring, $stringencoding) > ($size + 1)) {
4694  return dol_substr($newstring, 0, $size, $stringencoding)."\n".dol_trunc(dol_substr($newstring, $size, dol_strlen($newstring, $stringencoding) - $size, $stringencoding), $size, $trunc);
4695  } else {
4696  return $string;
4697  }
4698  } else {
4699  return 'BadParam3CallingDolTrunc';
4700  }
4701 }
4702 
4710 function getPictoForType($key, $morecss = '')
4711 {
4712  // Set array with type -> picto
4713  $type2picto = array(
4714  'varchar' => 'font',
4715  'text' => 'font',
4716  'html' => 'code',
4717  'int' => 'sort-numeric-down',
4718  'double' => 'sort-numeric-down',
4719  'price' => 'currency',
4720  'pricecy' => 'multicurrency',
4721  'password' => 'key',
4722  'boolean' => 'check-square',
4723  'date' => 'calendar',
4724  'datetime' => 'calendar',
4725  'phone' => 'phone',
4726  'mail' => 'email',
4727  'url' => 'url',
4728  'ip' => 'country',
4729  'select' => 'list',
4730  'sellist' => 'list',
4731  'radio' => 'check-circle',
4732  'checkbox' => 'check-square',
4733  'chkbxlst' => 'check-square',
4734  'link' => 'link',
4735  'icon' => "question",
4736  'point' => "country",
4737  'multipts' => 'country',
4738  'linestrg' => "country",
4739  'polygon' => "country",
4740  'separate' => 'minus'
4741  );
4742 
4743  if (!empty($type2picto[$key])) {
4744  return img_picto('', $type2picto[$key], 'class="pictofixedwidth'.($morecss ? ' '.$morecss : '').'"');
4745  }
4746 
4747  return img_picto('', 'generic', 'class="pictofixedwidth'.($morecss ? ' '.$morecss : '').'"');
4748 }
4749 
4750 
4772 function img_picto($titlealt, $picto, $moreatt = '', $pictoisfullpath = 0, $srconly = 0, $notitle = 0, $alt = '', $morecss = '', $marginleftonlyshort = 2)
4773 {
4774  global $conf;
4775 
4776  // We forge fullpathpicto for image to $path/img/$picto. By default, we take DOL_URL_ROOT/theme/$conf->theme/img/$picto
4777  $url = DOL_URL_ROOT;
4778  $theme = isset($conf->theme) ? $conf->theme : null;
4779  $path = 'theme/'.$theme;
4780  if (empty($picto)) {
4781  $picto = 'generic';
4782  }
4783 
4784  // Define fullpathpicto to use into src
4785  if ($pictoisfullpath) {
4786  // Clean parameters
4787  if (!preg_match('/(\.png|\.gif|\.svg)$/i', $picto)) {
4788  $picto .= '.png';
4789  }
4790  $fullpathpicto = $picto;
4791  $reg = array();
4792  if (preg_match('/class="([^"]+)"/', $moreatt, $reg)) {
4793  $morecss .= ($morecss ? ' ' : '').$reg[1];
4794  $moreatt = str_replace('class="'.$reg[1].'"', '', $moreatt);
4795  }
4796  } else {
4797  $pictowithouttext = preg_replace('/(\.png|\.gif|\.svg)$/', '', (is_null($picto) ? '' : $picto));
4798  $pictowithouttext = str_replace('object_', '', $pictowithouttext);
4799  $pictowithouttext = str_replace('_nocolor', '', $pictowithouttext);
4800 
4801  if (strpos($pictowithouttext, 'fontawesome_') === 0 || strpos($pictowithouttext, 'fa-') === 0) {
4802  // This is a font awesome image 'fontawesome_xxx' or 'fa-xxx'
4803  $pictowithouttext = str_replace('fontawesome_', '', $pictowithouttext);
4804  $pictowithouttext = str_replace('fa-', '', $pictowithouttext);
4805 
4806  // Compatibility with old fontawesome versions
4807  if ($pictowithouttext == 'file-o') {
4808  $pictowithouttext = 'file';
4809  }
4810 
4811  $pictowithouttextarray = explode('_', $pictowithouttext);
4812  $marginleftonlyshort = 0;
4813 
4814  if (!empty($pictowithouttextarray[1])) {
4815  // Syntax is 'fontawesome_fakey_faprefix_facolor_fasize' or 'fa-fakey_faprefix_facolor_fasize'
4816  $fakey = 'fa-'.$pictowithouttextarray[0];
4817  $faprefix = empty($pictowithouttextarray[1]) ? 'fas' : $pictowithouttextarray[1];
4818  $facolor = empty($pictowithouttextarray[2]) ? '' : $pictowithouttextarray[2];
4819  $fasize = empty($pictowithouttextarray[3]) ? '' : $pictowithouttextarray[3];
4820  } else {
4821  $fakey = 'fa-'.$pictowithouttext;
4822  $faprefix = 'fas';
4823  $facolor = '';
4824  $fasize = '';
4825  }
4826 
4827  // This snippet only needed since function img_edit accepts only one additional parameter: no separate one for css only.
4828  // class/style need to be extracted to avoid duplicate class/style validation errors when $moreatt is added to the end of the attributes.
4829  $morestyle = '';
4830  $reg = array();
4831  if (preg_match('/class="([^"]+)"/', $moreatt, $reg)) {
4832  $morecss .= ($morecss ? ' ' : '').$reg[1];
4833  $moreatt = str_replace('class="'.$reg[1].'"', '', $moreatt);
4834  }
4835  if (preg_match('/style="([^"]+)"/', $moreatt, $reg)) {
4836  $morestyle = $reg[1];
4837  $moreatt = str_replace('style="'.$reg[1].'"', '', $moreatt);
4838  }
4839  $moreatt = trim($moreatt);
4840 
4841  $enabledisablehtml = '<span class="'.$faprefix.' '.$fakey.($marginleftonlyshort ? ($marginleftonlyshort == 1 ? ' marginleftonlyshort' : ' marginleftonly') : '');
4842  $enabledisablehtml .= ($morecss ? ' '.$morecss : '').'" style="'.($fasize ? ('font-size: '.$fasize.';') : '').($facolor ? (' color: '.$facolor.';') : '').($morestyle ? ' '.$morestyle : '').'"'.(($notitle || empty($titlealt)) ? '' : ' title="'.dol_escape_htmltag($titlealt).'"').($moreatt ? ' '.$moreatt : '').'>';
4843  /*if (!empty($conf->global->MAIN_OPTIMIZEFORTEXTBROWSER)) {
4844  $enabledisablehtml .= $titlealt;
4845  }*/
4846  $enabledisablehtml .= '</span>';
4847 
4848  return $enabledisablehtml;
4849  }
4850 
4851  if (empty($srconly) && in_array($pictowithouttext, array(
4852  '1downarrow', '1uparrow', '1leftarrow', '1rightarrow', '1uparrow_selected', '1downarrow_selected', '1leftarrow_selected', '1rightarrow_selected',
4853  'accountancy', 'accounting_account', 'account', 'accountline', 'action', 'add', 'address', 'ai', 'angle-double-down', 'angle-double-up', 'asset',
4854  'bank_account', 'barcode', 'bank', 'bell', 'bill', 'billa', 'billr', 'billd', 'birthday-cake', 'bom', 'bookcal', 'bookmark', 'briefcase-medical', 'bug', 'building',
4855  'card', 'calendarlist', 'calendar', 'calendarmonth', 'calendarweek', 'calendarday', 'calendarperuser', 'calendarpertype',
4856  'cash-register', 'category', 'chart', 'check', 'clock', 'clone', 'close_title', 'code', 'cog', 'collab', 'company', 'contact', 'country', 'contract', 'conversation', 'cron', 'cross', 'cubes',
4857  'check-circle', 'check-square', 'currency', 'multicurrency',
4858  'chevron-left', 'chevron-right', 'chevron-down', 'chevron-top', 'commercial', 'companies',
4859  'delete', 'dolly', 'dollyrevert', 'donation', 'download', 'dynamicprice',
4860  'edit', 'ellipsis-h', 'email', 'entity', 'envelope', 'eraser', 'establishment', 'expensereport', 'external-link-alt', 'external-link-square-alt', 'eye',
4861  'filter', 'file', 'file-o', 'file-code', 'file-export', 'file-import', 'file-upload', 'autofill', 'folder', 'folder-open', 'folder-plus', 'font',
4862  'gears', 'generate', 'generic', 'globe', 'globe-americas', 'graph', 'grip', 'grip_title', 'group',
4863  'hands-helping', 'help', 'holiday',
4864  'id-card', 'images', 'incoterm', 'info', 'intervention', 'inventory', 'intracommreport', 'jobprofile',
4865  'key', 'knowledgemanagement',
4866  'label', 'language', 'layout', 'line', 'link', 'list', 'list-alt', 'listlight', 'loan', 'lock', 'lot', 'long-arrow-alt-right',
4867  'margin', 'map-marker-alt', 'member', 'meeting', 'minus', 'money-bill-alt', 'movement', 'mrp', 'note', 'next',
4868  'off', 'on', 'order',
4869  'paiment', 'paragraph', 'play', 'pdf', 'phone', 'phoning', 'phoning_mobile', 'phoning_fax', 'playdisabled', 'previous', 'poll', 'pos', 'printer', 'product', 'propal', 'proposal', 'puce',
4870  'stock', 'resize', 'service', 'stats',
4871  'security', 'setup', 'share-alt', 'sign-out', 'split', 'stripe', 'stripe-s', 'switch_off', 'switch_on', 'switch_on_warning', 'switch_on_red', 'tools', 'unlink', 'uparrow', 'user', 'user-tie', 'vcard', 'wrench',
4872  'github', 'google', 'jabber', 'microsoft', 'skype', 'twitter', 'facebook', 'linkedin', 'instagram', 'snapchat', 'youtube', 'google-plus-g', 'whatsapp',
4873  'generic', 'home', 'hrm', 'members', 'products', 'invoicing',
4874  'partnership', 'payment', 'payment_vat', 'pencil-ruler', 'pictoconfirm', 'preview', 'project', 'projectpub', 'projecttask', 'question', 'refresh', 'region',
4875  'salary', 'shipment', 'state', 'supplier_invoice', 'supplier_invoicea', 'supplier_invoicer', 'supplier_invoiced',
4876  'technic', 'ticket',
4877  'error', 'warning',
4878  'recent', 'reception', 'recruitmentcandidature', 'recruitmentjobposition', 'replacement', 'resource', 'recurring','rss',
4879  'shapes', 'skill', 'square', 'sort-numeric-down', 'stop-circle', 'supplier', 'supplier_proposal', 'supplier_order', 'supplier_invoice',
4880  'terminal', 'tick', 'timespent', 'title_setup', 'title_accountancy', 'title_bank', 'title_hrm', 'title_agenda', 'trip',
4881  'uncheck', 'url', 'user-cog', 'user-injured', 'user-md', 'vat', 'website', 'workstation', 'webhook', 'world', 'private',
4882  'conferenceorbooth', 'eventorganization',
4883  'stamp', 'signature',
4884  'webportal'
4885  ))) {
4886  $fakey = $pictowithouttext;
4887  $facolor = '';
4888  $fasize = '';
4889  $fa = getDolGlobalString('MAIN_FONTAWESOME_ICON_STYLE', 'fas');
4890  if (in_array($pictowithouttext, array('card', 'bell', 'clock', 'establishment', 'file', 'file-o', 'generic', 'minus-square', 'object_generic', 'pdf', 'plus-square', 'timespent', 'note', 'off', 'on', 'object_bookmark', 'bookmark', 'vcard'))) {
4891  $fa = 'far';
4892  }
4893  if (in_array($pictowithouttext, array('black-tie', 'github', 'google', 'microsoft', 'skype', 'twitter', 'facebook', 'linkedin', 'instagram', 'snapchat', 'stripe', 'stripe-s', 'youtube', 'google-plus-g', 'whatsapp'))) {
4894  $fa = 'fab';
4895  }
4896 
4897  $arrayconvpictotofa = array(
4898  'account' => 'university', 'accounting_account' => 'clipboard-list', 'accountline' => 'receipt', 'accountancy' => 'search-dollar', 'action' => 'calendar-alt', 'add' => 'plus-circle', 'address' => 'address-book', 'ai' => 'magic',
4899  'asset' => 'money-check-alt', 'autofill' => 'fill',
4900  'bank_account' => 'university',
4901  'bill' => 'file-invoice-dollar', 'billa' => 'file-excel', 'billr' => 'file-invoice-dollar', 'billd' => 'file-medical',
4902  'bookcal' => 'calendar-check',
4903  'supplier_invoice' => 'file-invoice-dollar', 'supplier_invoicea' => 'file-excel', 'supplier_invoicer' => 'file-invoice-dollar', 'supplier_invoiced' => 'file-medical',
4904  'bom' => 'shapes',
4905  'card' => 'address-card', 'chart' => 'chart-line', 'company' => 'building', 'contact' => 'address-book', 'contract' => 'suitcase', 'collab' => 'people-arrows', 'conversation' => 'comments', 'country' => 'globe-americas', 'cron' => 'business-time', 'cross' => 'times',
4906  'donation' => 'file-alt', 'dynamicprice' => 'hand-holding-usd',
4907  'setup' => 'cog', 'companies' => 'building', 'products' => 'cube', 'commercial' => 'suitcase', 'invoicing' => 'coins',
4908  'accounting' => 'search-dollar', 'category' => 'tag', 'dollyrevert' => 'dolly',
4909  'file-o' => 'file', 'generate' => 'plus-square', 'hrm' => 'user-tie', 'incoterm' => 'truck-loading',
4910  'margin' => 'calculator', 'members' => 'user-friends', 'ticket' => 'ticket-alt', 'globe' => 'external-link-alt', 'lot' => 'barcode',
4911  'email' => 'at', 'establishment' => 'building', 'edit' => 'pencil-alt', 'entity' => 'globe',
4912  'graph' => 'chart-line', 'grip_title' => 'arrows-alt', 'grip' => 'arrows-alt', 'help' => 'question-circle',
4913  'generic' => 'file', 'holiday' => 'umbrella-beach',
4914  'info' => 'info-circle', 'inventory' => 'boxes', 'intracommreport' => 'globe-europe', 'jobprofile' => 'cogs',
4915  'knowledgemanagement' => 'ticket-alt', 'label' => 'layer-group', 'layout' => 'columns', 'line' => 'bars', 'loan' => 'money-bill-alt',
4916  'member' => 'user-alt', 'meeting' => 'chalkboard-teacher', 'mrp' => 'cubes', 'next' => 'arrow-alt-circle-right',
4917  'trip' => 'wallet', 'expensereport' => 'wallet', 'group' => 'users', 'movement' => 'people-carry',
4918  'sign-out' => 'sign-out-alt',
4919  'switch_off' => 'toggle-off', 'switch_on' => 'toggle-on', 'switch_on_warning' => 'toggle-on', 'switch_on_red' => 'toggle-on', 'check' => 'check', 'bookmark' => 'star',
4920  'bank' => 'university', 'close_title' => 'times', 'delete' => 'trash', 'filter' => 'filter',
4921  'list-alt' => 'list-alt', 'calendarlist' => 'bars', 'calendar' => 'calendar-alt', 'calendarmonth' => 'calendar-alt', 'calendarweek' => 'calendar-week', 'calendarday' => 'calendar-day', 'calendarperuser' => 'table',
4922  'intervention' => 'ambulance', 'invoice' => 'file-invoice-dollar', 'order' => 'file-invoice',
4923  'error' => 'exclamation-triangle', 'warning' => 'exclamation-triangle',
4924  'other' => 'square',
4925  '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',
4926  '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',
4927  'recent' => 'check-square', 'reception' => 'dolly', 'recruitmentjobposition' => 'id-card-alt', 'recruitmentcandidature' => 'id-badge',
4928  'resize' => 'crop', 'supplier_order' => 'dol-order_supplier', 'supplier_proposal' => 'file-signature',
4929  'refresh' => 'redo', 'region' => 'map-marked', 'replacement' => 'exchange-alt', 'resource' => 'laptop-house', 'recurring' => 'history',
4930  'service' => 'concierge-bell',
4931  'skill' => 'shapes', 'state' => 'map-marked-alt', 'security' => 'key', 'salary' => 'wallet', 'shipment' => 'dolly', 'stock' => 'box-open', 'stats' => 'chart-bar', 'split' => 'code-branch', 'stripe' => 'stripe-s',
4932  'supplier' => 'building',
4933  'technic' => 'cogs', 'tick' => 'check', 'timespent' => 'clock', 'title_setup' => 'tools', 'title_accountancy' => 'money-check-alt', 'title_bank' => 'university', 'title_hrm' => 'umbrella-beach',
4934  'title_agenda' => 'calendar-alt',
4935  'uncheck' => 'times', 'uparrow' => 'share', 'url' => 'external-link-alt', 'vat' => 'money-check-alt', 'vcard' => 'arrow-alt-circle-down',
4936  'jabber' => 'comment-o',
4937  'website' => 'globe-americas', 'workstation' => 'pallet', 'webhook' => 'bullseye', 'world' => 'globe', 'private' => 'user-lock',
4938  'conferenceorbooth' => 'chalkboard-teacher', 'eventorganization' => 'project-diagram',
4939  'webportal' => 'door-open'
4940  );
4941  if ($conf->currency == 'EUR') {
4942  $arrayconvpictotofa['currency'] = 'euro-sign';
4943  $arrayconvpictotofa['multicurrency'] = 'dollar-sign';
4944  } else {
4945  $arrayconvpictotofa['currency'] = 'dollar-sign';
4946  $arrayconvpictotofa['multicurrency'] = 'euro-sign';
4947  }
4948  if ($pictowithouttext == 'off') {
4949  $fakey = 'fa-square';
4950  $fasize = '1.3em';
4951  } elseif ($pictowithouttext == 'on') {
4952  $fakey = 'fa-check-square';
4953  $fasize = '1.3em';
4954  } elseif ($pictowithouttext == 'listlight') {
4955  $fakey = 'fa-download';
4956  $marginleftonlyshort = 1;
4957  } elseif ($pictowithouttext == 'printer') {
4958  $fakey = 'fa-print';
4959  $fasize = '1.2em';
4960  } elseif ($pictowithouttext == 'note') {
4961  $fakey = 'fa-sticky-note';
4962  $marginleftonlyshort = 1;
4963  } elseif (in_array($pictowithouttext, array('1uparrow', '1downarrow', '1leftarrow', '1rightarrow', '1uparrow_selected', '1downarrow_selected', '1leftarrow_selected', '1rightarrow_selected'))) {
4964  $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');
4965  $fakey = 'fa-'.$convertarray[$pictowithouttext];
4966  if (preg_match('/selected/', $pictowithouttext)) {
4967  $facolor = '#888';
4968  }
4969  $marginleftonlyshort = 1;
4970  } elseif (!empty($arrayconvpictotofa[$pictowithouttext])) {
4971  $fakey = 'fa-'.$arrayconvpictotofa[$pictowithouttext];
4972  } else {
4973  $fakey = 'fa-'.$pictowithouttext;
4974  }
4975 
4976  if (in_array($pictowithouttext, array('dollyrevert', 'member', 'members', 'contract', 'group', 'resource', 'shipment', 'reception'))) {
4977  $morecss .= ' em092';
4978  }
4979  if (in_array($pictowithouttext, array('conferenceorbooth', 'collab', 'eventorganization', 'holiday', 'info', 'project', 'workstation'))) {
4980  $morecss .= ' em088';
4981  }
4982  if (in_array($pictowithouttext, array('asset', 'intervention', 'payment', 'loan', 'partnership', 'stock', 'technic'))) {
4983  $morecss .= ' em080';
4984  }
4985 
4986  // Define $marginleftonlyshort
4987  $arrayconvpictotomarginleftonly = array(
4988  'bank', 'check', 'delete', 'generic', 'grip', 'grip_title', 'jabber',
4989  'grip_title', 'grip', 'listlight', 'note', 'on', 'off', 'playdisabled', 'printer', 'resize', 'sign-out', 'stats', 'switch_on', 'switch_on_red', 'switch_off',
4990  'uparrow', '1uparrow', '1downarrow', '1leftarrow', '1rightarrow', '1uparrow_selected', '1downarrow_selected', '1leftarrow_selected', '1rightarrow_selected'
4991  );
4992  if (!isset($arrayconvpictotomarginleftonly[$pictowithouttext])) {
4993  $marginleftonlyshort = 0;
4994  }
4995 
4996  // Add CSS
4997  $arrayconvpictotomorcess = array(
4998  'action' => 'infobox-action', 'account' => 'infobox-bank_account', 'accounting_account' => 'infobox-bank_account', 'accountline' => 'infobox-bank_account', 'accountancy' => 'infobox-bank_account', 'asset' => 'infobox-bank_account',
4999  'bank_account' => 'infobox-bank_account',
5000  'bill' => 'infobox-commande', 'billa' => 'infobox-commande', 'billr' => 'infobox-commande', 'billd' => 'infobox-commande',
5001  'bookcal' => 'infobox-action',
5002  'margin' => 'infobox-bank_account', 'conferenceorbooth' => 'infobox-project',
5003  'cash-register' => 'infobox-bank_account', 'contract' => 'infobox-contrat', 'check' => 'font-status4', 'collab' => 'infobox-action', 'conversation' => 'infobox-contrat',
5004  'donation' => 'infobox-commande', 'dolly' => 'infobox-commande', 'dollyrevert' => 'flip infobox-order_supplier',
5005  'ecm' => 'infobox-action', 'eventorganization' => 'infobox-project',
5006  'hrm' => 'infobox-adherent', 'group' => 'infobox-adherent', 'intervention' => 'infobox-contrat',
5007  'incoterm' => 'infobox-supplier_proposal',
5008  'currency' => 'infobox-bank_account', 'multicurrency' => 'infobox-bank_account',
5009  'members' => 'infobox-adherent', 'member' => 'infobox-adherent', 'money-bill-alt' => 'infobox-bank_account',
5010  'order' => 'infobox-commande',
5011  'user' => 'infobox-adherent', 'users' => 'infobox-adherent',
5012  'error' => 'pictoerror', 'warning' => 'pictowarning', 'switch_on' => 'font-status4', 'switch_on_warning' => 'font-status4 warning', 'switch_on_red' => 'font-status8',
5013  'holiday' => 'infobox-holiday', 'info' => 'opacityhigh', 'invoice' => 'infobox-commande',
5014  'knowledgemanagement' => 'infobox-contrat rotate90', 'loan' => 'infobox-bank_account',
5015  'payment' => 'infobox-bank_account', 'payment_vat' => 'infobox-bank_account', 'poll' => 'infobox-adherent', 'pos' => 'infobox-bank_account', 'project' => 'infobox-project', 'projecttask' => 'infobox-project',
5016  'propal' => 'infobox-propal', 'proposal' => 'infobox-propal','private' => 'infobox-project',
5017  'reception' => 'flip infobox-order_supplier', 'recruitmentjobposition' => 'infobox-adherent', 'recruitmentcandidature' => 'infobox-adherent',
5018  'resource' => 'infobox-action',
5019  'salary' => 'infobox-bank_account', 'shapes' => 'infobox-adherent', 'shipment' => 'infobox-commande', 'supplier_invoice' => 'infobox-order_supplier', 'supplier_invoicea' => 'infobox-order_supplier', 'supplier_invoiced' => 'infobox-order_supplier',
5020  'supplier' => 'infobox-order_supplier', 'supplier_order' => 'infobox-order_supplier', 'supplier_proposal' => 'infobox-supplier_proposal',
5021  'ticket' => 'infobox-contrat', 'title_accountancy' => 'infobox-bank_account', 'title_hrm' => 'infobox-holiday', 'expensereport' => 'infobox-expensereport', 'trip' => 'infobox-expensereport', 'title_agenda' => 'infobox-action',
5022  'vat' => 'infobox-bank_account',
5023  //'title_setup'=>'infobox-action', 'tools'=>'infobox-action',
5024  'list-alt' => 'imgforviewmode', 'calendar' => 'imgforviewmode', 'calendarweek' => 'imgforviewmode', 'calendarmonth' => 'imgforviewmode', 'calendarday' => 'imgforviewmode', 'calendarperuser' => 'imgforviewmode'
5025  );
5026  if (!empty($arrayconvpictotomorcess[$pictowithouttext]) && strpos($picto, '_nocolor') === false) {
5027  $morecss .= ($morecss ? ' ' : '').$arrayconvpictotomorcess[$pictowithouttext];
5028  }
5029 
5030  // Define $color
5031  $arrayconvpictotocolor = array(
5032  'address' => '#6c6aa8', 'building' => '#6c6aa8', 'bom' => '#a69944',
5033  'clone' => '#999', 'cog' => '#999', 'companies' => '#6c6aa8', 'company' => '#6c6aa8', 'contact' => '#6c6aa8', 'cron' => '#555',
5034  'dynamicprice' => '#a69944',
5035  'edit' => '#444', 'note' => '#999', 'error' => '', 'help' => '#bbb', 'listlight' => '#999', 'language' => '#555',
5036  //'dolly'=>'#a69944', 'dollyrevert'=>'#a69944',
5037  'lock' => '#ddd', 'lot' => '#a69944',
5038  'map-marker-alt' => '#aaa', 'mrp' => '#a69944', 'product' => '#a69944', 'service' => '#a69944', 'inventory' => '#a69944', 'stock' => '#a69944', 'movement' => '#a69944',
5039  'other' => '#ddd', 'world' => '#986c6a',
5040  'partnership' => '#6c6aa8', 'playdisabled' => '#ccc', 'printer' => '#444', 'projectpub' => '#986c6a', 'resize' => '#444', 'rss' => '#cba',
5041  //'shipment'=>'#a69944',
5042  'security' => '#999', 'square' => '#888', 'stop-circle' => '#888', 'stats' => '#444', 'switch_off' => '#999',
5043  'technic' => '#999', 'tick' => '#282', 'timespent' => '#555',
5044  'uncheck' => '#800', 'uparrow' => '#555', 'user-cog' => '#999', 'country' => '#aaa', 'globe-americas' => '#aaa', 'region' => '#aaa', 'state' => '#aaa',
5045  'website' => '#304', 'workstation' => '#a69944'
5046  );
5047  if (isset($arrayconvpictotocolor[$pictowithouttext]) && strpos($picto, '_nocolor') === false) {
5048  $facolor = $arrayconvpictotocolor[$pictowithouttext];
5049  }
5050 
5051  // This snippet only needed since function img_edit accepts only one additional parameter: no separate one for css only.
5052  // class/style need to be extracted to avoid duplicate class/style validation errors when $moreatt is added to the end of the attributes.
5053  $morestyle = '';
5054  $reg = array();
5055  if (preg_match('/class="([^"]+)"/', $moreatt, $reg)) {
5056  $morecss .= ($morecss ? ' ' : '').$reg[1];
5057  $moreatt = str_replace('class="'.$reg[1].'"', '', $moreatt);
5058  }
5059  if (preg_match('/style="([^"]+)"/', $moreatt, $reg)) {
5060  $morestyle = $reg[1];
5061  $moreatt = str_replace('style="'.$reg[1].'"', '', $moreatt);
5062  }
5063  $moreatt = trim($moreatt);
5064 
5065  $enabledisablehtml = '<span class="'.$fa.' '.$fakey.($marginleftonlyshort ? ($marginleftonlyshort == 1 ? ' marginleftonlyshort' : ' marginleftonly') : '');
5066  $enabledisablehtml .= ($morecss ? ' '.$morecss : '').'" style="'.($fasize ? ('font-size: '.$fasize.';') : '').($facolor ? (' color: '.$facolor.';') : '').($morestyle ? ' '.$morestyle : '').'"'.(($notitle || empty($titlealt)) ? '' : ' title="'.dol_escape_htmltag($titlealt).'"').($moreatt ? ' '.$moreatt : '').'>';
5067  /*if (!empty($conf->global->MAIN_OPTIMIZEFORTEXTBROWSER)) {
5068  $enabledisablehtml .= $titlealt;
5069  }*/
5070  $enabledisablehtml .= '</span>';
5071 
5072  return $enabledisablehtml;
5073  }
5074 
5075  if (getDolGlobalString('MAIN_OVERWRITE_THEME_PATH')) {
5076  $path = getDolGlobalString('MAIN_OVERWRITE_THEME_PATH') . '/theme/'.$theme; // If the theme does not have the same name as the module
5077  } elseif (getDolGlobalString('MAIN_OVERWRITE_THEME_RES')) {
5078  $path = getDolGlobalString('MAIN_OVERWRITE_THEME_RES') . '/theme/' . getDolGlobalString('MAIN_OVERWRITE_THEME_RES'); // To allow an external module to overwrite image resources whatever is activated theme
5079  } elseif (!empty($conf->modules_parts['theme']) && array_key_exists($theme, $conf->modules_parts['theme'])) {
5080  $path = $theme.'/theme/'.$theme; // If the theme have the same name as the module
5081  }
5082 
5083  // If we ask an image into $url/$mymodule/img (instead of default path)
5084  $regs = array();
5085  if (preg_match('/^([^@]+)@([^@]+)$/i', $picto, $regs)) {
5086  $picto = $regs[1];
5087  $path = $regs[2]; // $path is $mymodule
5088  }
5089 
5090  // Clean parameters
5091  if (!preg_match('/(\.png|\.gif|\.svg)$/i', $picto)) {
5092  $picto .= '.png';
5093  }
5094  // If alt path are defined, define url where img file is, according to physical path
5095  // ex: array(["main"]=>"/home/maindir/htdocs", ["alt0"]=>"/home/moddir0/htdocs", ...)
5096  foreach ($conf->file->dol_document_root as $type => $dirroot) {
5097  if ($type == 'main') {
5098  continue;
5099  }
5100  // This need a lot of time, that's why enabling alternative dir like "custom" dir is not recommended
5101  if (file_exists($dirroot.'/'.$path.'/img/'.$picto)) {
5102  $url = DOL_URL_ROOT.$conf->file->dol_url_root[$type];
5103  break;
5104  }
5105  }
5106 
5107  // $url is '' or '/custom', $path is current theme or
5108  $fullpathpicto = $url.'/'.$path.'/img/'.$picto;
5109  }
5110 
5111  if ($srconly) {
5112  return $fullpathpicto;
5113  }
5114 
5115  // tag title is used for tooltip on <a>, tag alt can be used with very simple text on image for blind people
5116  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
5117 }
5118 
5132 function img_object($titlealt, $picto, $moreatt = '', $pictoisfullpath = 0, $srconly = 0, $notitle = 0)
5133 {
5134  if (strpos($picto, '^') === 0) {
5135  return img_picto($titlealt, str_replace('^', '', $picto), $moreatt, $pictoisfullpath, $srconly, $notitle);
5136  } else {
5137  return img_picto($titlealt, 'object_'.$picto, $moreatt, $pictoisfullpath, $srconly, $notitle);
5138  }
5139 }
5140 
5152 function img_weather($titlealt, $picto, $moreatt = '', $pictoisfullpath = 0, $morecss = '')
5153 {
5154  global $conf;
5155 
5156  if (is_numeric($picto)) {
5157  //$leveltopicto = array(0=>'weather-clear.png', 1=>'weather-few-clouds.png', 2=>'weather-clouds.png', 3=>'weather-many-clouds.png', 4=>'weather-storm.png');
5158  //$picto = $leveltopicto[$picto];
5159  return '<i class="fa fa-weather-level'.$picto.'"></i>';
5160  } elseif (!preg_match('/(\.png|\.gif)$/i', $picto)) {
5161  $picto .= '.png';
5162  }
5163 
5164  $path = DOL_URL_ROOT.'/theme/'.$conf->theme.'/img/weather/'.$picto;
5165 
5166  return img_picto($titlealt, $path, $moreatt, 1, 0, 0, '', $morecss);
5167 }
5168 
5180 function img_picto_common($titlealt, $picto, $moreatt = '', $pictoisfullpath = 0, $notitle = 0)
5181 {
5182  global $conf;
5183 
5184  if (!preg_match('/(\.png|\.gif)$/i', $picto)) {
5185  $picto .= '.png';
5186  }
5187 
5188  if ($pictoisfullpath) {
5189  $path = $picto;
5190  } else {
5191  $path = DOL_URL_ROOT.'/theme/common/'.$picto;
5192 
5193  if (getDolGlobalInt('MAIN_MODULE_CAN_OVERWRITE_COMMONICONS')) {
5194  $themepath = DOL_DOCUMENT_ROOT.'/theme/'.$conf->theme.'/img/'.$picto;
5195 
5196  if (file_exists($themepath)) {
5197  $path = $themepath;
5198  }
5199  }
5200  }
5201 
5202  return img_picto($titlealt, $path, $moreatt, 1, 0, $notitle);
5203 }
5204 
5218 function img_action($titlealt, $numaction, $picto = '', $moreatt = '')
5219 {
5220  global $langs;
5221 
5222  if (empty($titlealt) || $titlealt == 'default') {
5223  if ($numaction == '-1' || $numaction == 'ST_NO') {
5224  $numaction = -1;
5225  $titlealt = $langs->transnoentitiesnoconv('ChangeDoNotContact');
5226  } elseif ($numaction == '0' || $numaction == 'ST_NEVER') {
5227  $numaction = 0;
5228  $titlealt = $langs->transnoentitiesnoconv('ChangeNeverContacted');
5229  } elseif ($numaction == '1' || $numaction == 'ST_TODO') {
5230  $numaction = 1;
5231  $titlealt = $langs->transnoentitiesnoconv('ChangeToContact');
5232  } elseif ($numaction == '2' || $numaction == 'ST_PEND') {
5233  $numaction = 2;
5234  $titlealt = $langs->transnoentitiesnoconv('ChangeContactInProcess');
5235  } elseif ($numaction == '3' || $numaction == 'ST_DONE') {
5236  $numaction = 3;
5237  $titlealt = $langs->transnoentitiesnoconv('ChangeContactDone');
5238  } else {
5239  $titlealt = $langs->transnoentitiesnoconv('ChangeStatus '.$numaction);
5240  $numaction = 0;
5241  }
5242  }
5243  if (!is_numeric($numaction)) {
5244  $numaction = 0;
5245  }
5246 
5247  return img_picto($titlealt, (empty($picto) ? 'stcomm'.$numaction.'.png' : $picto), $moreatt);
5248 }
5249 
5257 function img_pdf($titlealt = 'default', $size = 3)
5258 {
5259  global $langs;
5260 
5261  if ($titlealt == 'default') {
5262  $titlealt = $langs->trans('Show');
5263  }
5264 
5265  return img_picto($titlealt, 'pdf'.$size.'.png');
5266 }
5267 
5275 function img_edit_add($titlealt = 'default', $other = '')
5276 {
5277  global $langs;
5278 
5279  if ($titlealt == 'default') {
5280  $titlealt = $langs->trans('Add');
5281  }
5282 
5283  return img_picto($titlealt, 'edit_add.png', $other);
5284 }
5292 function img_edit_remove($titlealt = 'default', $other = '')
5293 {
5294  global $langs;
5295 
5296  if ($titlealt == 'default') {
5297  $titlealt = $langs->trans('Remove');
5298  }
5299 
5300  return img_picto($titlealt, 'edit_remove.png', $other);
5301 }
5302 
5311 function img_edit($titlealt = 'default', $float = 0, $other = '')
5312 {
5313  global $langs;
5314 
5315  if ($titlealt == 'default') {
5316  $titlealt = $langs->trans('Modify');
5317  }
5318 
5319  return img_picto($titlealt, 'edit.png', ($float ? 'style="float: '.($langs->tab_translate["DIRECTION"] == 'rtl' ? 'left' : 'right').'"' : "").($other ? ' '.$other : ''));
5320 }
5321 
5330 function img_view($titlealt = 'default', $float = 0, $other = 'class="valignmiddle"')
5331 {
5332  global $langs;
5333 
5334  if ($titlealt == 'default') {
5335  $titlealt = $langs->trans('View');
5336  }
5337 
5338  $moreatt = ($float ? 'style="float: right" ' : '').$other;
5339 
5340  return img_picto($titlealt, 'eye', $moreatt);
5341 }
5342 
5351 function img_delete($titlealt = 'default', $other = 'class="pictodelete"', $morecss = '')
5352 {
5353  global $langs;
5354 
5355  if ($titlealt == 'default') {
5356  $titlealt = $langs->trans('Delete');
5357  }
5358 
5359  return img_picto($titlealt, 'delete.png', $other, 0, 0, 0, '', $morecss);
5360 }
5361 
5369 function img_printer($titlealt = "default", $other = '')
5370 {
5371  global $langs;
5372  if ($titlealt == "default") {
5373  $titlealt = $langs->trans("Print");
5374  }
5375  return img_picto($titlealt, 'printer.png', $other);
5376 }
5377 
5385 function img_split($titlealt = 'default', $other = 'class="pictosplit"')
5386 {
5387  global $langs;
5388 
5389  if ($titlealt == 'default') {
5390  $titlealt = $langs->trans('Split');
5391  }
5392 
5393  return img_picto($titlealt, 'split.png', $other);
5394 }
5395 
5403 function img_help($usehelpcursor = 1, $usealttitle = 1)
5404 {
5405  global $langs;
5406 
5407  if ($usealttitle) {
5408  if (is_string($usealttitle)) {
5409  $usealttitle = dol_escape_htmltag($usealttitle);
5410  } else {
5411  $usealttitle = $langs->trans('Info');
5412  }
5413  }
5414 
5415  return img_picto($usealttitle, 'info.png', 'style="vertical-align: middle;'.($usehelpcursor == 1 ? ' cursor: help' : ($usehelpcursor == 2 ? ' cursor: pointer' : '')).'"');
5416 }
5417 
5424 function img_info($titlealt = 'default')
5425 {
5426  global $langs;
5427 
5428  if ($titlealt == 'default') {
5429  $titlealt = $langs->trans('Informations');
5430  }
5431 
5432  return img_picto($titlealt, 'info.png', 'style="vertical-align: middle;"');
5433 }
5434 
5443 function img_warning($titlealt = 'default', $moreatt = '', $morecss = 'pictowarning')
5444 {
5445  global $langs;
5446 
5447  if ($titlealt == 'default') {
5448  $titlealt = $langs->trans('Warning');
5449  }
5450 
5451  //return '<div class="imglatecoin">'.img_picto($titlealt, 'warning_white.png', 'class="pictowarning valignmiddle"'.($moreatt ? ($moreatt == '1' ? ' style="float: right"' : ' '.$moreatt): '')).'</div>';
5452  return img_picto($titlealt, 'warning.png', 'class="'.$morecss.'"'.($moreatt ? ($moreatt == '1' ? ' style="float: right"' : ' '.$moreatt) : ''));
5453 }
5454 
5461 function img_error($titlealt = 'default')
5462 {
5463  global $langs;
5464 
5465  if ($titlealt == 'default') {
5466  $titlealt = $langs->trans('Error');
5467  }
5468 
5469  return img_picto($titlealt, 'error.png');
5470 }
5471 
5479 function img_next($titlealt = 'default', $moreatt = '')
5480 {
5481  global $langs;
5482 
5483  if ($titlealt == 'default') {
5484  $titlealt = $langs->trans('Next');
5485  }
5486 
5487  //return img_picto($titlealt, 'next.png', $moreatt);
5488  return '<span class="fa fa-chevron-right paddingright paddingleft" title="'.dol_escape_htmltag($titlealt).'"></span>';
5489 }
5490 
5498 function img_previous($titlealt = 'default', $moreatt = '')
5499 {
5500  global $langs;
5501 
5502  if ($titlealt == 'default') {
5503  $titlealt = $langs->trans('Previous');
5504  }
5505 
5506  //return img_picto($titlealt, 'previous.png', $moreatt);
5507  return '<span class="fa fa-chevron-left paddingright paddingleft" title="'.dol_escape_htmltag($titlealt).'"></span>';
5508 }
5509 
5518 function img_down($titlealt = 'default', $selected = 0, $moreclass = '')
5519 {
5520  global $langs;
5521 
5522  if ($titlealt == 'default') {
5523  $titlealt = $langs->trans('Down');
5524  }
5525 
5526  return img_picto($titlealt, ($selected ? '1downarrow_selected.png' : '1downarrow.png'), 'class="imgdown'.($moreclass ? " ".$moreclass : "").'"');
5527 }
5528 
5537 function img_up($titlealt = 'default', $selected = 0, $moreclass = '')
5538 {
5539  global $langs;
5540 
5541  if ($titlealt == 'default') {
5542  $titlealt = $langs->trans('Up');
5543  }
5544 
5545  return img_picto($titlealt, ($selected ? '1uparrow_selected.png' : '1uparrow.png'), 'class="imgup'.($moreclass ? " ".$moreclass : "").'"');
5546 }
5547 
5556 function img_left($titlealt = 'default', $selected = 0, $moreatt = '')
5557 {
5558  global $langs;
5559 
5560  if ($titlealt == 'default') {
5561  $titlealt = $langs->trans('Left');
5562  }
5563 
5564  return img_picto($titlealt, ($selected ? '1leftarrow_selected.png' : '1leftarrow.png'), $moreatt);
5565 }
5566 
5575 function img_right($titlealt = 'default', $selected = 0, $moreatt = '')
5576 {
5577  global $langs;
5578 
5579  if ($titlealt == 'default') {
5580  $titlealt = $langs->trans('Right');
5581  }
5582 
5583  return img_picto($titlealt, ($selected ? '1rightarrow_selected.png' : '1rightarrow.png'), $moreatt);
5584 }
5585 
5593 function img_allow($allow, $titlealt = 'default')
5594 {
5595  global $langs;
5596 
5597  if ($titlealt == 'default') {
5598  $titlealt = $langs->trans('Active');
5599  }
5600 
5601  if ($allow == 1) {
5602  return img_picto($titlealt, 'tick.png');
5603  }
5604 
5605  return '-';
5606 }
5607 
5615 function img_credit_card($brand, $morecss = null)
5616 {
5617  if (is_null($morecss)) {
5618  $morecss = 'fa-2x';
5619  }
5620 
5621  if ($brand == 'visa' || $brand == 'Visa') {
5622  $brand = 'cc-visa';
5623  } elseif ($brand == 'mastercard' || $brand == 'MasterCard') {
5624  $brand = 'cc-mastercard';
5625  } elseif ($brand == 'amex' || $brand == 'American Express') {
5626  $brand = 'cc-amex';
5627  } elseif ($brand == 'discover' || $brand == 'Discover') {
5628  $brand = 'cc-discover';
5629  } elseif ($brand == 'jcb' || $brand == 'JCB') {
5630  $brand = 'cc-jcb';
5631  } elseif ($brand == 'diners' || $brand == 'Diners club') {
5632  $brand = 'cc-diners-club';
5633  } elseif (!in_array($brand, array('cc-visa', 'cc-mastercard', 'cc-amex', 'cc-discover', 'cc-jcb', 'cc-diners-club'))) {
5634  $brand = 'credit-card';
5635  }
5636 
5637  return '<span class="fa fa-'.$brand.' fa-fw'.($morecss ? ' '.$morecss : '').'"></span>';
5638 }
5639 
5648 function img_mime($file, $titlealt = '', $morecss = '')
5649 {
5650  require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
5651 
5652  $mimetype = dol_mimetype($file, '', 1);
5653  $mimeimg = dol_mimetype($file, '', 2);
5654  $mimefa = dol_mimetype($file, '', 4);
5655 
5656  if (empty($titlealt)) {
5657  $titlealt = 'Mime type: '.$mimetype;
5658  }
5659 
5660  //return img_picto_common($titlealt, 'mime/'.$mimeimg, 'class="'.$morecss.'"');
5661  return '<i class="fa fa-'.$mimefa.' paddingright'.($morecss ? ' '.$morecss : '').'"'.($titlealt ? ' title="'.$titlealt.'"' : '').'></i>';
5662 }
5663 
5664 
5672 function img_search($titlealt = 'default', $other = '')
5673 {
5674  global $langs;
5675 
5676  if ($titlealt == 'default') {
5677  $titlealt = $langs->trans('Search');
5678  }
5679 
5680  $img = img_picto($titlealt, 'search.png', $other, 0, 1);
5681 
5682  $input = '<input type="image" class="liste_titre" name="button_search" src="'.$img.'" ';
5683  $input .= 'value="'.dol_escape_htmltag($titlealt).'" title="'.dol_escape_htmltag($titlealt).'" >';
5684 
5685  return $input;
5686 }
5687 
5695 function img_searchclear($titlealt = 'default', $other = '')
5696 {
5697  global $langs;
5698 
5699  if ($titlealt == 'default') {
5700  $titlealt = $langs->trans('Search');
5701  }
5702 
5703  $img = img_picto($titlealt, 'searchclear.png', $other, 0, 1);
5704 
5705  $input = '<input type="image" class="liste_titre" name="button_removefilter" src="'.$img.'" ';
5706  $input .= 'value="'.dol_escape_htmltag($titlealt).'" title="'.dol_escape_htmltag($titlealt).'" >';
5707 
5708  return $input;
5709 }
5710 
5722 function info_admin($text, $infoonimgalt = 0, $nodiv = 0, $admin = '1', $morecss = 'hideonsmartphone', $textfordropdown = '')
5723 {
5724  global $conf, $langs;
5725 
5726  if ($infoonimgalt) {
5727  $result = img_picto($text, 'info', 'class="'.($morecss ? ' '.$morecss : '').'"');
5728  } else {
5729  if (empty($conf->use_javascript_ajax)) {
5730  $textfordropdown = '';
5731  }
5732 
5733  $class = (empty($admin) ? 'undefined' : ($admin == '1' ? 'info' : $admin));
5734  $result = ($nodiv ? '' : '<div class="wordbreak '.$class.($morecss ? ' '.$morecss : '').($textfordropdown ? ' hidden' : '').'">').'<span class="fa fa-info-circle" title="'.dol_escape_htmltag($admin ? $langs->trans('InfoAdmin') : $langs->trans('Note')).'"></span> ';
5735  $result .= dol_escape_htmltag($text, 1, 0, 'div,span,b,br,a');
5736  $result .= ($nodiv ? '' : '</div>');
5737 
5738  if ($textfordropdown) {
5739  $tmpresult = '<span class="'.$class.'text opacitymedium cursorpointer">'.$langs->trans($textfordropdown).' '.img_picto($langs->trans($textfordropdown), '1downarrow').'</span>';
5740  $tmpresult .= '<script nonce="'.getNonce().'" type="text/javascript">
5741  jQuery(document).ready(function() {
5742  jQuery(".'.$class.'text").click(function() {
5743  console.log("toggle text");
5744  jQuery(".'.$class.'").toggle();
5745  });
5746  });
5747  </script>';
5748 
5749  $result = $tmpresult.$result;
5750  }
5751  }
5752 
5753  return $result;
5754 }
5755 
5756 
5768 function dol_print_error($db = null, $error = '', $errors = null)
5769 {
5770  global $conf, $langs, $argv;
5771  global $dolibarr_main_prod;
5772 
5773  $out = '';
5774  $syslog = '';
5775 
5776  // If error occurs before the $lang object was loaded
5777  if (!$langs) {
5778  require_once DOL_DOCUMENT_ROOT.'/core/class/translate.class.php';
5779  $langs = new Translate('', $conf);
5780  $langs->load("main");
5781  }
5782 
5783  // Load translation files required by the error messages
5784  $langs->loadLangs(array('main', 'errors'));
5785 
5786  if ($_SERVER['DOCUMENT_ROOT']) { // Mode web
5787  $out .= $langs->trans("DolibarrHasDetectedError").".<br>\n";
5788  if (getDolGlobalInt('MAIN_FEATURES_LEVEL') > 0) {
5789  $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";
5790  }
5791  $out .= $langs->trans("InformationToHelpDiagnose").":<br>\n";
5792 
5793  $out .= "<b>".$langs->trans("Date").":</b> ".dol_print_date(time(), 'dayhourlog')."<br>\n";
5794  $out .= "<b>".$langs->trans("Dolibarr").":</b> ".DOL_VERSION." - https://www.dolibarr.org<br>\n";
5795  if (isset($conf->global->MAIN_FEATURES_LEVEL)) {
5796  $out .= "<b>".$langs->trans("LevelOfFeature").":</b> ".getDolGlobalInt('MAIN_FEATURES_LEVEL')."<br>\n";
5797  }
5798  if (function_exists("phpversion")) {
5799  $out .= "<b>".$langs->trans("PHP").":</b> ".phpversion()."<br>\n";
5800  }
5801  $out .= "<b>".$langs->trans("Server").":</b> ".(isset($_SERVER["SERVER_SOFTWARE"]) ? dol_htmlentities($_SERVER["SERVER_SOFTWARE"], ENT_COMPAT) : '')."<br>\n";
5802  if (function_exists("php_uname")) {
5803  $out .= "<b>".$langs->trans("OS").":</b> ".php_uname()."<br>\n";
5804  }
5805  $out .= "<b>".$langs->trans("UserAgent").":</b> ".(isset($_SERVER["HTTP_USER_AGENT"]) ? dol_htmlentities($_SERVER["HTTP_USER_AGENT"], ENT_COMPAT) : '')."<br>\n";
5806  $out .= "<br>\n";
5807  $out .= "<b>".$langs->trans("RequestedUrl").":</b> ".dol_htmlentities($_SERVER["REQUEST_URI"], ENT_COMPAT)."<br>\n";
5808  $out .= "<b>".$langs->trans("Referer").":</b> ".(isset($_SERVER["HTTP_REFERER"]) ? dol_htmlentities($_SERVER["HTTP_REFERER"], ENT_COMPAT) : '')."<br>\n";
5809  $out .= "<b>".$langs->trans("MenuManager").":</b> ".(isset($conf->standard_menu) ? dol_htmlentities($conf->standard_menu, ENT_COMPAT) : '')."<br>\n";
5810  $out .= "<br>\n";
5811  $syslog .= "url=".dol_escape_htmltag($_SERVER["REQUEST_URI"]);
5812  $syslog .= ", query_string=".dol_escape_htmltag($_SERVER["QUERY_STRING"]);
5813  } else { // Mode CLI
5814  $out .= '> '.$langs->transnoentities("ErrorInternalErrorDetected").":\n".$argv[0]."\n";
5815  $syslog .= "pid=".dol_getmypid();
5816  }
5817 
5818  if (!empty($conf->modules)) {
5819  $out .= "<b>".$langs->trans("Modules").":</b> ".implode(', ', $conf->modules)."<br>\n";
5820  }
5821 
5822  if (is_object($db)) {
5823  if ($_SERVER['DOCUMENT_ROOT']) { // Mode web
5824  $out .= "<b>".$langs->trans("DatabaseTypeManager").":</b> ".$db->type."<br>\n";
5825  $lastqueryerror = $db->lastqueryerror();
5826  if (!utf8_check($lastqueryerror)) {
5827  $lastqueryerror = "SQL error string is not a valid UTF8 string. We can't show it.";
5828  }
5829  $out .= "<b>".$langs->trans("RequestLastAccessInError").":</b> ".($lastqueryerror ? dol_escape_htmltag($lastqueryerror) : $langs->trans("ErrorNoRequestInError"))."<br>\n";
5830  $out .= "<b>".$langs->trans("ReturnCodeLastAccessInError").":</b> ".($db->lasterrno() ? dol_escape_htmltag($db->lasterrno()) : $langs->trans("ErrorNoRequestInError"))."<br>\n";
5831  $out .= "<b>".$langs->trans("InformationLastAccessInError").":</b> ".($db->lasterror() ? dol_escape_htmltag($db->lasterror()) : $langs->trans("ErrorNoRequestInError"))."<br>\n";
5832  $out .= "<br>\n";
5833  } else { // Mode CLI
5834  // No dol_escape_htmltag for output, we are in CLI mode
5835  $out .= '> '.$langs->transnoentities("DatabaseTypeManager").":\n".$db->type."\n";
5836  $out .= '> '.$langs->transnoentities("RequestLastAccessInError").":\n".($db->lastqueryerror() ? $db->lastqueryerror() : $langs->transnoentities("ErrorNoRequestInError"))."\n";
5837  $out .= '> '.$langs->transnoentities("ReturnCodeLastAccessInError").":\n".($db->lasterrno() ? $db->lasterrno() : $langs->transnoentities("ErrorNoRequestInError"))."\n";
5838  $out .= '> '.$langs->transnoentities("InformationLastAccessInError").":\n".($db->lasterror() ? $db->lasterror() : $langs->transnoentities("ErrorNoRequestInError"))."\n";
5839  }
5840  $syslog .= ", sql=".$db->lastquery();
5841  $syslog .= ", db_error=".$db->lasterror();
5842  }
5843 
5844  if ($error || $errors) {
5845  // Merge all into $errors array
5846  if (is_array($error) && is_array($errors)) {
5847  $errors = array_merge($error, $errors);
5848  } elseif (is_array($error)) { // deprecated, use second parameters
5849  $errors = $error;
5850  } elseif (is_array($errors) && !empty($error)) {
5851  $errors = array_merge(array($error), $errors);
5852  } elseif (!empty($error)) {
5853  $errors = array_merge(array($error), array($errors));
5854  }
5855 
5856  $langs->load("errors");
5857 
5858  foreach ($errors as $msg) {
5859  if (empty($msg)) {
5860  continue;
5861  }
5862  if ($_SERVER['DOCUMENT_ROOT']) { // Mode web
5863  $out .= "<b>".$langs->trans("Message").":</b> ".dol_escape_htmltag($msg)."<br>\n";
5864  } else { // Mode CLI
5865  $out .= '> '.$langs->transnoentities("Message").":\n".$msg."\n";
5866  }
5867  $syslog .= ", msg=".$msg;
5868  }
5869  }
5870  if (empty($dolibarr_main_prod) && $_SERVER['DOCUMENT_ROOT'] && function_exists('xdebug_print_function_stack') && function_exists('xdebug_call_file')) {
5871  xdebug_print_function_stack();
5872  $out .= '<b>XDebug information:</b>'."<br>\n";
5873  $out .= 'File: '.xdebug_call_file()."<br>\n";
5874  $out .= 'Line: '.xdebug_call_line()."<br>\n";
5875  $out .= 'Function: '.xdebug_call_function()."<br>\n";
5876  $out .= "<br>\n";
5877  }
5878 
5879  // Return a http header with error code if possible
5880  if (!headers_sent()) {
5881  if (function_exists('top_httphead')) { // In CLI context, the method does not exists
5882  top_httphead();
5883  }
5884  //http_response_code(500); // If we use 500, message is not output with some command line tools
5885  http_response_code(202); // If we use 202, this is not really an error message, but this allow to output message on command line tools
5886  }
5887 
5888  if (empty($dolibarr_main_prod)) {
5889  print $out;
5890  } else {
5891  if (empty($langs->defaultlang)) {
5892  $langs->setDefaultLang();
5893  }
5894  $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.
5895  // This should not happen, except if there is a bug somewhere. Enabled and check log in such case.
5896  print 'This website or feature is currently temporarily 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";
5897  print $langs->trans("DolibarrHasDetectedError").'. ';
5898  print $langs->trans("YouCanSetOptionDolibarrMainProdToZero");
5899  if (!defined("MAIN_CORE_ERROR")) {
5900  define("MAIN_CORE_ERROR", 1);
5901  }
5902  }
5903 
5904  dol_syslog("Error ".$syslog, LOG_ERR);
5905 }
5906 
5917 function dol_print_error_email($prefixcode, $errormessage = '', $errormessages = array(), $morecss = 'error', $email = '')
5918 {
5919  global $langs;
5920 
5921  if (empty($email)) {
5922  $email = getDolGlobalString('MAIN_INFO_SOCIETE_MAIL');
5923  }
5924 
5925  $langs->load("errors");
5926  $now = dol_now();
5927 
5928  print '<br><div class="center login_main_message"><div class="'.$morecss.'">';
5929  print $langs->trans("ErrorContactEMail", $email, $prefixcode.'-'.dol_print_date($now, '%Y%m%d%H%M%S'));
5930  if ($errormessage) {
5931  print '<br><br>'.$errormessage;
5932  }
5933  if (is_array($errormessages) && count($errormessages)) {
5934  foreach ($errormessages as $mesgtoshow) {
5935  print '<br><br>'.$mesgtoshow;
5936  }
5937  }
5938  print '</div></div>';
5939 }
5940 
5957 function print_liste_field_titre($name, $file = "", $field = "", $begin = "", $moreparam = "", $moreattrib = "", $sortfield = "", $sortorder = "", $prefix = "", $tooltip = "", $forcenowrapcolumntitle = 0)
5958 {
5959  print getTitleFieldOfList($name, 0, $file, $field, $begin, $moreparam, $moreattrib, $sortfield, $sortorder, $prefix, 0, $tooltip, $forcenowrapcolumntitle);
5960 }
5961 
5980 function getTitleFieldOfList($name, $thead = 0, $file = "", $field = "", $begin = "", $moreparam = "", $moreattrib = "", $sortfield = "", $sortorder = "", $prefix = "", $disablesortlink = 0, $tooltip = '', $forcenowrapcolumntitle = 0)
5981 {
5982  global $langs, $form;
5983  //print "$name, $file, $field, $begin, $options, $moreattrib, $sortfield, $sortorder<br>\n";
5984 
5985  if ($moreattrib == 'class="right"') {
5986  $prefix .= 'right '; // For backward compatibility
5987  }
5988 
5989  $sortorder = strtoupper($sortorder);
5990  $out = '';
5991  $sortimg = '';
5992 
5993  $tag = 'th';
5994  if ($thead == 2) {
5995  $tag = 'div';
5996  }
5997 
5998  $tmpsortfield = explode(',', $sortfield);
5999  $sortfield1 = trim($tmpsortfield[0]); // If $sortfield is 'd.datep,d.id', it becomes 'd.datep'
6000  $tmpfield = explode(',', $field);
6001  $field1 = trim($tmpfield[0]); // If $field is 'd.datep,d.id', it becomes 'd.datep'
6002 
6003  if (!getDolGlobalString('MAIN_DISABLE_WRAPPING_ON_COLUMN_TITLE') && empty($forcenowrapcolumntitle)) {
6004  $prefix = 'wrapcolumntitle '.$prefix;
6005  }
6006 
6007  //var_dump('field='.$field.' field1='.$field1.' sortfield='.$sortfield.' sortfield1='.$sortfield1);
6008  // If field is used as sort criteria we use a specific css class liste_titre_sel
6009  // Example if (sortfield,field)=("nom","xxx.nom") or (sortfield,field)=("nom","nom")
6010  $liste_titre = 'liste_titre';
6011  if ($field1 && ($sortfield1 == $field1 || $sortfield1 == preg_replace("/^[^\.]+\./", "", $field1))) {
6012  $liste_titre = 'liste_titre_sel';
6013  }
6014 
6015  $tagstart = '<'.$tag.' class="'.$prefix.$liste_titre.'" '.$moreattrib;
6016  //$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)).'"' : '');
6017  $tagstart .= ($name && !getDolGlobalString('MAIN_DISABLE_WRAPPING_ON_COLUMN_TITLE') && empty($forcenowrapcolumntitle) && !dol_textishtml($name)) ? ' title="'.dol_escape_htmltag($langs->trans($name)).'"' : '';
6018  $tagstart .= '>';
6019 
6020  if (empty($thead) && $field && empty($disablesortlink)) { // If this is a sort field
6021  $options = preg_replace('/sortfield=([a-zA-Z0-9,\s\.]+)/i', '', (is_scalar($moreparam) ? $moreparam : ''));
6022  $options = preg_replace('/sortorder=([a-zA-Z0-9,\s\.]+)/i', '', $options);
6023  $options = preg_replace('/&+/i', '&', $options);
6024  if (!preg_match('/^&/', $options)) {
6025  $options = '&'.$options;
6026  }
6027 
6028  $sortordertouseinlink = '';
6029  if ($field1 != $sortfield1) { // We are on another field than current sorted field
6030  if (preg_match('/^DESC/i', $sortorder)) {
6031  $sortordertouseinlink .= str_repeat('desc,', count(explode(',', $field)));
6032  } else { // We reverse the var $sortordertouseinlink
6033  $sortordertouseinlink .= str_repeat('asc,', count(explode(',', $field)));
6034  }
6035  } else { // We are on field that is the first current sorting criteria
6036  if (preg_match('/^ASC/i', $sortorder)) { // We reverse the var $sortordertouseinlink
6037  $sortordertouseinlink .= str_repeat('desc,', count(explode(',', $field)));
6038  } else {
6039  $sortordertouseinlink .= str_repeat('asc,', count(explode(',', $field)));
6040  }
6041  }
6042  $sortordertouseinlink = preg_replace('/,$/', '', $sortordertouseinlink);
6043  $out .= '<a class="reposition" href="'.$file.'?sortfield='.$field.'&sortorder='.$sortordertouseinlink.'&begin='.$begin.$options.'"';
6044  //$out .= (empty($conf->global->MAIN_DISABLE_WRAPPING_ON_COLUMN_TITLE) ? ' title="'.dol_escape_htmltag($langs->trans($name)).'"' : '');
6045  $out .= '>';
6046  }
6047  if ($tooltip) {
6048  // You can also use 'TranslationString:keyfortooltiponclick' for a tooltip on click.
6049  if (preg_match('/:\w+$/', $tooltip)) {
6050  $tmptooltip = explode(':', $tooltip);
6051  } else {
6052  $tmptooltip = array($tooltip);
6053  }
6054  $out .= $form->textwithpicto($langs->trans($name), $langs->trans($tmptooltip[0]), 1, 'help', '', 0, 3, (empty($tmptooltip[1]) ? '' : 'extra_'.str_replace('.', '_', $field).'_'.$tmptooltip[1]));
6055  } else {
6056  $out .= $langs->trans($name);
6057  }
6058 
6059  if (empty($thead) && $field && empty($disablesortlink)) { // If this is a sort field
6060  $out .= '</a>';
6061  }
6062 
6063  if (empty($thead) && $field) { // If this is a sort field
6064  $options = preg_replace('/sortfield=([a-zA-Z0-9,\s\.]+)/i', '', (is_scalar($moreparam) ? $moreparam : ''));
6065  $options = preg_replace('/sortorder=([a-zA-Z0-9,\s\.]+)/i', '', $options);
6066  $options = preg_replace('/&+/i', '&', $options);
6067  if (!preg_match('/^&/', $options)) {
6068  $options = '&'.$options;
6069  }
6070 
6071  if (!$sortorder || ($field1 != $sortfield1)) {
6072  //$out.= '<a href="'.$file.'?sortfield='.$field.'&sortorder=asc&begin='.$begin.$options.'">'.img_down("A-Z",0).'</a>';
6073  //$out.= '<a href="'.$file.'?sortfield='.$field.'&sortorder=desc&begin='.$begin.$options.'">'.img_up("Z-A",0).'</a>';
6074  } else {
6075  if (preg_match('/^DESC/', $sortorder)) {
6076  //$out.= '<a href="'.$file.'?sortfield='.$field.'&sortorder=asc&begin='.$begin.$options.'">'.img_down("A-Z",0).'</a>';
6077  //$out.= '<a href="'.$file.'?sortfield='.$field.'&sortorder=desc&begin='.$begin.$options.'">'.img_up("Z-A",1).'</a>';
6078  $sortimg .= '<span class="nowrap">'.img_up("Z-A", 0, 'paddingright').'</span>';
6079  }
6080  if (preg_match('/^ASC/', $sortorder)) {
6081  //$out.= '<a href="'.$file.'?sortfield='.$field.'&sortorder=asc&begin='.$begin.$options.'">'.img_down("A-Z",1).'</a>';
6082  //$out.= '<a href="'.$file.'?sortfield='.$field.'&sortorder=desc&begin='.$begin.$options.'">'.img_up("Z-A",0).'</a>';
6083  $sortimg .= '<span class="nowrap">'.img_down("A-Z", 0, 'paddingright').'</span>';
6084  }
6085  }
6086  }
6087 
6088  $tagend = '</'.$tag.'>';
6089 
6090  $out = $tagstart.$sortimg.$out.$tagend;
6091 
6092  return $out;
6093 }
6094 
6103 function print_titre($title)
6104 {
6105  dol_syslog(__FUNCTION__." is deprecated", LOG_WARNING);
6106 
6107  print '<div class="titre">'.$title.'</div>';
6108 }
6109 
6121 function print_fiche_titre($title, $mesg = '', $picto = 'generic', $pictoisfullpath = 0, $id = '')
6122 {
6123  print load_fiche_titre($title, $mesg, $picto, $pictoisfullpath, $id);
6124 }
6125 
6139 function load_fiche_titre($title, $morehtmlright = '', $picto = 'generic', $pictoisfullpath = 0, $id = '', $morecssontable = '', $morehtmlcenter = '')
6140 {
6141  $return = '';
6142 
6143  if ($picto == 'setup') {
6144  $picto = 'generic';
6145  }
6146 
6147  $return .= "\n";
6148  $return .= '<table '.($id ? 'id="'.$id.'" ' : '').'class="centpercent notopnoleftnoright table-fiche-title'.($morecssontable ? ' '.$morecssontable : '').'">'; // maring bottom must be same than into print_barre_list
6149  $return .= '<tr class="titre">';
6150  if ($picto) {
6151  $return .= '<td class="nobordernopadding widthpictotitle valignmiddle col-picto">'.img_picto('', $picto, 'class="valignmiddle widthpictotitle pictotitle"', $pictoisfullpath).'</td>';
6152  }
6153  $return .= '<td class="nobordernopadding valignmiddle col-title">';
6154  $return .= '<div class="titre inline-block">';
6155  $return .= $title; // $title is already HTML sanitized content
6156  $return .= '</div>';
6157  $return .= '</td>';
6158  if (dol_strlen($morehtmlcenter)) {
6159  $return .= '<td class="nobordernopadding center valignmiddle col-center">'.$morehtmlcenter.'</td>';
6160  }
6161  if (dol_strlen($morehtmlright)) {
6162  $return .= '<td class="nobordernopadding titre_right wordbreakimp right valignmiddle col-right">'.$morehtmlright.'</td>';
6163  }
6164  $return .= '</tr></table>'."\n";
6165 
6166  return $return;
6167 }
6168 
6192 function print_barre_liste($title, $page, $file, $options = '', $sortfield = '', $sortorder = '', $morehtmlcenter = '', $num = -1, $totalnboflines = '', $picto = 'generic', $pictoisfullpath = 0, $morehtmlright = '', $morecss = '', $limit = -1, $hideselectlimit = 0, $hidenavigation = 0, $pagenavastextinput = 0, $morehtmlrightbeforearrow = '')
6193 {
6194  global $conf;
6195 
6196  $savlimit = $limit;
6197  $savtotalnboflines = $totalnboflines;
6198  if (is_numeric($totalnboflines)) {
6199  $totalnboflines = abs($totalnboflines);
6200  }
6201 
6202  $page = (int) $page;
6203 
6204  if ($picto == 'setup') {
6205  $picto = 'title_setup.png';
6206  }
6207  if (($conf->browser->name == 'ie') && $picto == 'generic') {
6208  $picto = 'title.gif';
6209  }
6210  if ($limit < 0) {
6211  $limit = $conf->liste_limit;
6212  }
6213 
6214  if ($savlimit != 0 && (($num > $limit) || ($num == -1) || ($limit == 0))) {
6215  $nextpage = 1;
6216  } else {
6217  $nextpage = 0;
6218  }
6219  //print 'totalnboflines='.$totalnboflines.'-savlimit='.$savlimit.'-limit='.$limit.'-num='.$num.'-nextpage='.$nextpage.'-hideselectlimit='.$hideselectlimit.'-hidenavigation='.$hidenavigation;
6220 
6221  print "\n";
6222  print "<!-- Begin title -->\n";
6223  print '<table class="centpercent notopnoleftnoright table-fiche-title'.($morecss ? ' '.$morecss : '').'"><tr>'; // maring bottom must be same than into load_fiche_tire
6224 
6225  // Left
6226 
6227  if ($picto && $title) {
6228  print '<td class="nobordernopadding widthpictotitle valignmiddle col-picto">'.img_picto('', $picto, 'class="valignmiddle pictotitle widthpictotitle"', $pictoisfullpath).'</td>';
6229  }
6230 
6231  print '<td class="nobordernopadding valignmiddle col-title">';
6232  print '<div class="titre inline-block">';
6233  print $title; // $title may contains HTML
6234  if (!empty($title) && $savtotalnboflines >= 0 && (string) $savtotalnboflines != '') {
6235  print '<span class="opacitymedium colorblack paddingleft">('.$totalnboflines.')</span>';
6236  }
6237  print '</div></td>';
6238 
6239  // Center
6240  if ($morehtmlcenter && empty($conf->dol_optimize_smallscreen)) {
6241  print '<td class="nobordernopadding center valignmiddle col-center">'.$morehtmlcenter.'</td>';
6242  }
6243 
6244  // Right
6245  print '<td class="nobordernopadding valignmiddle right col-right">';
6246  print '<input type="hidden" name="pageplusoneold" value="'.((int) $page + 1).'">';
6247  if ($sortfield) {
6248  $options .= "&sortfield=".urlencode($sortfield);
6249  }
6250  if ($sortorder) {
6251  $options .= "&sortorder=".urlencode($sortorder);
6252  }
6253  // Show navigation bar
6254  $pagelist = '';
6255  if ($savlimit != 0 && ($page > 0 || $num > $limit)) {
6256  if ($totalnboflines) { // If we know total nb of lines
6257  // Define nb of extra page links before and after selected page + ... + first or last
6258  $maxnbofpage = (empty($conf->dol_optimize_smallscreen) ? 4 : 0);
6259 
6260  if ($limit > 0) {
6261  $nbpages = ceil($totalnboflines / $limit);
6262  } else {
6263  $nbpages = 1;
6264  }
6265  $cpt = ($page - $maxnbofpage);
6266  if ($cpt < 0) {
6267  $cpt = 0;
6268  }
6269 
6270  if ($cpt >= 1) {
6271  if (empty($pagenavastextinput)) {
6272  $pagelist .= '<li class="pagination"><a class="reposition" href="'.$file.'?page=0'.$options.'">1</a></li>';
6273  if ($cpt > 2) {
6274  $pagelist .= '<li class="pagination"><span class="inactive">...</span></li>';
6275  } elseif ($cpt == 2) {
6276  $pagelist .= '<li class="pagination"><a class="reposition" href="'.$file.'?page=1'.$options.'">2</a></li>';
6277  }
6278  }
6279  }
6280 
6281  do {
6282  if ($pagenavastextinput) {
6283  if ($cpt == $page) {
6284  $pagelist .= '<li class="pagination"><input type="text" class="'.($totalnboflines > 100 ? 'width40' : 'width25').' center pageplusone" name="pageplusone" value="'.($page + 1).'"></li>';
6285  $pagelist .= '/';
6286  }
6287  } else {
6288  if ($cpt == $page) {
6289  $pagelist .= '<li class="pagination"><span class="active">'.($page + 1).'</span></li>';
6290  } else {
6291  $pagelist .= '<li class="pagination"><a class="reposition" href="'.$file.'?page='.$cpt.$options.'">'.($cpt + 1).'</a></li>';
6292  }
6293  }
6294  $cpt++;
6295  } while ($cpt < $nbpages && $cpt <= ($page + $maxnbofpage));
6296 
6297  if (empty($pagenavastextinput)) {
6298  if ($cpt < $nbpages) {
6299  if ($cpt < $nbpages - 2) {
6300  $pagelist .= '<li class="pagination"><span class="inactive">...</span></li>';
6301  } elseif ($cpt == $nbpages - 2) {
6302  $pagelist .= '<li class="pagination"><a class="reposition" href="'.$file.'?page='.($nbpages - 2).$options.'">'.($nbpages - 1).'</a></li>';
6303  }
6304  $pagelist .= '<li class="pagination"><a class="reposition" href="'.$file.'?page='.($nbpages - 1).$options.'">'.$nbpages.'</a></li>';
6305  }
6306  } else {
6307  //var_dump($page.' '.$cpt.' '.$nbpages);
6308  $pagelist .= '<li class="pagination paginationlastpage"><a class="reposition" href="'.$file.'?page='.($nbpages - 1).$options.'">'.$nbpages.'</a></li>';
6309  }
6310  } else {
6311  $pagelist .= '<li class="pagination"><span class="active">'.($page + 1)."</li>";
6312  }
6313  }
6314 
6315  if ($savlimit || $morehtmlright || $morehtmlrightbeforearrow) {
6316  print_fleche_navigation($page, $file, $options, $nextpage, $pagelist, $morehtmlright, $savlimit, $totalnboflines, $hideselectlimit, $morehtmlrightbeforearrow, $hidenavigation); // output the div and ul for previous/last completed with page numbers into $pagelist
6317  }
6318 
6319  // js to autoselect page field on focus
6320  if ($pagenavastextinput) {
6321  print ajax_autoselect('.pageplusone');
6322  }
6323 
6324  print '</td>';
6325  print '</tr>';
6326 
6327  print '</table>'."\n";
6328 
6329  // Center
6330  if ($morehtmlcenter && !empty($conf->dol_optimize_smallscreen)) {
6331  print '<div class="nobordernopadding marginbottomonly center valignmiddle col-center centpercent">'.$morehtmlcenter.'</div>';
6332  }
6333 
6334  print "<!-- End title -->\n\n";
6335 }
6336 
6353 function print_fleche_navigation($page, $file, $options = '', $nextpage = 0, $betweenarrows = '', $afterarrows = '', $limit = -1, $totalnboflines = 0, $hideselectlimit = 0, $beforearrows = '', $hidenavigation = 0)
6354 {
6355  global $conf, $langs;
6356 
6357  print '<div class="pagination"><ul>';
6358  if ($beforearrows) {
6359  print '<li class="paginationbeforearrows">';
6360  print $beforearrows;
6361  print '</li>';
6362  }
6363 
6364  if (empty($hidenavigation)) {
6365  if ((int) $limit > 0 && empty($hideselectlimit)) {
6366  $pagesizechoices = '10:10,15:15,20:20,30:30,40:40,50:50,100:100,250:250,500:500,1000:1000';
6367  $pagesizechoices .= ',5000:5000,10000:10000,20000:20000';
6368  //$pagesizechoices.=',0:'.$langs->trans("All"); // Not yet supported
6369  //$pagesizechoices.=',2:2';
6370  if (getDolGlobalString('MAIN_PAGESIZE_CHOICES')) {
6371  $pagesizechoices = getDolGlobalString('MAIN_PAGESIZE_CHOICES');
6372  }
6373 
6374  if (getDolGlobalString('MAIN_USE_HTML5_LIMIT_SELECTOR')) {
6375  print '<li class="pagination">';
6376  print '<input onfocus="this.value=null;" onchange="this.blur();" class="flat selectlimit nopadding maxwidth75 right pageplusone" id="limit" name="limit" list="limitlist" title="'.dol_escape_htmltag($langs->trans("MaxNbOfRecordPerPage")).'" value="'.$limit.'">';
6377  print '<datalist id="limitlist">';
6378  } else {
6379  print '<li class="paginationxxx">';
6380  print '<select id="limit" class="flat selectlimit nopadding maxwidth75 center" name="limit" title="'.dol_escape_htmltag($langs->trans("MaxNbOfRecordPerPage")).'">';
6381  }
6382  $tmpchoice = explode(',', $pagesizechoices);
6383  $tmpkey = $limit.':'.$limit;
6384  if (!in_array($tmpkey, $tmpchoice)) {
6385  $tmpchoice[] = $tmpkey;
6386  }
6387  $tmpkey = $conf->liste_limit.':'.$conf->liste_limit;
6388  if (!in_array($tmpkey, $tmpchoice)) {
6389  $tmpchoice[] = $tmpkey;
6390  }
6391  asort($tmpchoice, SORT_NUMERIC);
6392  foreach ($tmpchoice as $val) {
6393  $selected = '';
6394  $tmp = explode(':', $val);
6395  $key = $tmp[0];
6396  $val = $tmp[1];
6397  if ($key != '' && $val != '') {
6398  if ((int) $key == (int) $limit) {
6399  $selected = ' selected="selected"';
6400  }
6401  print '<option name="'.$key.'"'.$selected.'>'.dol_escape_htmltag($val).'</option>'."\n";
6402  }
6403  }
6404  if (getDolGlobalString('MAIN_USE_HTML5_LIMIT_SELECTOR')) {
6405  print '</datalist>';
6406  } else {
6407  print '</select>';
6408  print ajax_combobox("limit", array(), 0, 0, 'resolve', -1, 'limit');
6409  //print ajax_combobox("limit");
6410  }
6411 
6412  if ($conf->use_javascript_ajax) {
6413  print '<!-- JS CODE TO ENABLE select limit to launch submit of page -->
6414  <script>
6415  jQuery(document).ready(function () {
6416  jQuery(".selectlimit").change(function() {
6417  console.log("Change limit. Send submit");
6418  $(this).parents(\'form:first\').submit();
6419  });
6420  });
6421  </script>
6422  ';
6423  }
6424  print '</li>';
6425  }
6426  if ($page > 0) {
6427  print '<li class="pagination paginationpage paginationpageleft"><a class="paginationprevious reposition" href="'.$file.'?page='.($page - 1).$options.'"><i class="fa fa-chevron-left" title="'.dol_escape_htmltag($langs->trans("Previous")).'"></i></a></li>';
6428  }
6429  if ($betweenarrows) {
6430  print '<!--<div class="betweenarrows nowraponall inline-block">-->';
6431  print $betweenarrows;
6432  print '<!--</div>-->';
6433  }
6434  if ($nextpage > 0) {
6435  print '<li class="pagination paginationpage paginationpageright"><a class="paginationnext reposition" href="'.$file.'?page='.($page + 1).$options.'"><i class="fa fa-chevron-right" title="'.dol_escape_htmltag($langs->trans("Next")).'"></i></a></li>';
6436  }
6437  if ($afterarrows) {
6438  print '<li class="paginationafterarrows">';
6439  print $afterarrows;
6440  print '</li>';
6441  }
6442  }
6443  print '</ul></div>'."\n";
6444 }
6445 
6446 
6458 function vatrate($rate, $addpercent = false, $info_bits = 0, $usestarfornpr = 0, $html = 0)
6459 {
6460  $morelabel = '';
6461 
6462  if (preg_match('/%/', $rate)) {
6463  $rate = str_replace('%', '', $rate);
6464  $addpercent = true;
6465  }
6466  $reg = array();
6467  if (preg_match('/\‍((.*)\‍)/', $rate, $reg)) {
6468  $morelabel = ' ('.$reg[1].')';
6469  $rate = preg_replace('/\s*'.preg_quote($morelabel, '/').'/', '', $rate);
6470  $morelabel = ' '.($html ? '<span class="opacitymedium">' : '').'('.$reg[1].')'.($html ? '</span>' : '');
6471  }
6472  if (preg_match('/\*/', $rate)) {
6473  $rate = str_replace('*', '', $rate);
6474  $info_bits |= 1;
6475  }
6476 
6477  // If rate is '9/9/9' we don't change it. If rate is '9.000' we apply price()
6478  if (!preg_match('/\//', $rate)) {
6479  $ret = price($rate, 0, '', 0, 0).($addpercent ? '%' : '');
6480  } else {
6481  // TODO Split on / and output with a price2num to have clean numbers without ton of 000.
6482  $ret = $rate.($addpercent ? '%' : '');
6483  }
6484  if (($info_bits & 1) && $usestarfornpr >= 0) {
6485  $ret .= ' *';
6486  }
6487  $ret .= $morelabel;
6488  return $ret;
6489 }
6490 
6491 
6507 function price($amount, $form = 0, $outlangs = '', $trunc = 1, $rounding = -1, $forcerounding = -1, $currency_code = '')
6508 {
6509  global $langs, $conf;
6510 
6511  // Clean parameters
6512  if (empty($amount)) {
6513  $amount = 0; // To have a numeric value if amount not defined or = ''
6514  }
6515  $amount = (is_numeric($amount) ? $amount : 0); // Check if amount is numeric, for example, an error occurred when amount value = o (letter) instead 0 (number)
6516  if ($rounding == -1) {
6517  $rounding = min(getDolGlobalString('MAIN_MAX_DECIMALS_UNIT'), getDolGlobalString('MAIN_MAX_DECIMALS_TOT'));
6518  }
6519  $nbdecimal = $rounding;
6520 
6521  if ($outlangs === 'none') {
6522  // Use international separators
6523  $dec = '.';
6524  $thousand = '';
6525  } else {
6526  // Output separators by default (french)
6527  $dec = ',';
6528  $thousand = ' ';
6529 
6530  // If $outlangs not forced, we use use language
6531  if (!($outlangs instanceof Translate)) {
6532  $outlangs = $langs;
6533  }
6534 
6535  if ($outlangs->transnoentitiesnoconv("SeparatorDecimal") != "SeparatorDecimal") {
6536  $dec = $outlangs->transnoentitiesnoconv("SeparatorDecimal");
6537  }
6538  if ($outlangs->transnoentitiesnoconv("SeparatorThousand") != "SeparatorThousand") {
6539  $thousand = $outlangs->transnoentitiesnoconv("SeparatorThousand");
6540  }
6541  if ($thousand == 'None') {
6542  $thousand = '';
6543  } elseif ($thousand == 'Space') {
6544  $thousand = ' ';
6545  }
6546  }
6547  //print "outlangs=".$outlangs->defaultlang." amount=".$amount." html=".$form." trunc=".$trunc." nbdecimal=".$nbdecimal." dec='".$dec."' thousand='".$thousand."'<br>";
6548 
6549  //print "amount=".$amount."-";
6550  $amount = str_replace(',', '.', $amount); // should be useless
6551  //print $amount."-";
6552  $data = explode('.', $amount);
6553  $decpart = isset($data[1]) ? $data[1] : '';
6554  $decpart = preg_replace('/0+$/i', '', $decpart); // Supprime les 0 de fin de partie decimale
6555  //print "decpart=".$decpart."<br>";
6556  $end = '';
6557 
6558  // We increase nbdecimal if there is more decimal than asked (to not loose information)
6559  if (dol_strlen($decpart) > $nbdecimal) {
6560  $nbdecimal = dol_strlen($decpart);
6561  }
6562  // Si on depasse max
6563  $max_nbdecimal = getDolGlobalString('MAIN_MAX_DECIMALS_SHOWN');
6564  if ($trunc && $nbdecimal > (int) $max_nbdecimal) {
6565  $nbdecimal = $max_nbdecimal;
6566  if (preg_match('/\.\.\./i', $nbdecimal)) {
6567  // Si un affichage est tronque, on montre des ...
6568  $end = '...';
6569  }
6570  }
6571 
6572  // If force rounding
6573  if ((string) $forcerounding != '-1') {
6574  if ($forcerounding === 'MU') {
6575  $nbdecimal = getDolGlobalString('MAIN_MAX_DECIMALS_UNIT');
6576  } elseif ($forcerounding === 'MT') {
6577  $nbdecimal = getDolGlobalString('MAIN_MAX_DECIMALS_TOT');
6578  } elseif ($forcerounding >= 0) {
6579  $nbdecimal = $forcerounding;
6580  }
6581  }
6582 
6583  // Format number
6584  $output = number_format((float) $amount, $nbdecimal, $dec, $thousand);
6585  if ($form) {
6586  $output = preg_replace('/\s/', '&nbsp;', $output);
6587  $output = preg_replace('/\'/', '&#039;', $output);
6588  }
6589  // Add symbol of currency if requested
6590  $cursymbolbefore = $cursymbolafter = '';
6591  if ($currency_code && is_object($outlangs)) {
6592  if ($currency_code == 'auto') {
6593  $currency_code = $conf->currency;
6594  }
6595 
6596  $listofcurrenciesbefore = array('AUD', 'CAD', 'CNY', 'COP', 'CLP', 'GBP', 'HKD', 'MXN', 'PEN', 'USD', 'CRC', 'ZAR');
6597  $listoflanguagesbefore = array('nl_NL');
6598  if (in_array($currency_code, $listofcurrenciesbefore) || in_array($outlangs->defaultlang, $listoflanguagesbefore)) {
6599  $cursymbolbefore .= $outlangs->getCurrencySymbol($currency_code);
6600  } else {
6601  $tmpcur = $outlangs->getCurrencySymbol($currency_code);
6602  $cursymbolafter .= ($tmpcur == $currency_code ? ' '.$tmpcur : $tmpcur);
6603  }
6604  }
6605  $output = $cursymbolbefore.$output.$end.($cursymbolafter ? ' ' : '').$cursymbolafter;
6606 
6607  return $output;
6608 }
6609 
6634 function price2num($amount, $rounding = '', $option = 0)
6635 {
6636  global $langs, $conf;
6637 
6638  // Clean parameters
6639  if (is_null($amount)) {
6640  $amount = '';
6641  }
6642 
6643  // Round PHP function does not allow number like '1,234.56' nor '1.234,56' nor '1 234,56'
6644  // Numbers must be '1234.56'
6645  // Decimal delimiter for PHP and database SQL requests must be '.'
6646  $dec = ',';
6647  $thousand = ' ';
6648  if (is_null($langs)) { // $langs is not defined, we use english values.
6649  $dec = '.';
6650  $thousand = ',';
6651  } else {
6652  if ($langs->transnoentitiesnoconv("SeparatorDecimal") != "SeparatorDecimal") {
6653  $dec = $langs->transnoentitiesnoconv("SeparatorDecimal");
6654  }
6655  if ($langs->transnoentitiesnoconv("SeparatorThousand") != "SeparatorThousand") {
6656  $thousand = $langs->transnoentitiesnoconv("SeparatorThousand");
6657  }
6658  }
6659  if ($thousand == 'None') {
6660  $thousand = '';
6661  } elseif ($thousand == 'Space') {
6662  $thousand = ' ';
6663  }
6664  //print "amount=".$amount." html=".$form." trunc=".$trunc." nbdecimal=".$nbdecimal." dec='".$dec."' thousand='".$thousand."'<br>";
6665 
6666  // Convert value to universal number format (no thousand separator, '.' as decimal separator)
6667  if ($option != 1) { // If not a PHP number or unknown, we change or clean format
6668  //print "\n".'PP'.$amount.' - '.$dec.' - '.$thousand.' - '.intval($amount).'<br>';
6669  if (!is_numeric($amount)) {
6670  $amount = preg_replace('/[a-zA-Z\/\\\*\‍(\‍)<>\_]/', '', $amount);
6671  }
6672 
6673  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
6674  $amount = str_replace($thousand, '', $amount);
6675  }
6676 
6677  // Convert amount to format with dolibarr dec and thousand (this is because PHP convert a number
6678  // to format defined by LC_NUMERIC after a calculation and we want source format to be like defined by Dolibarr setup.
6679  // So if number was already a good number, it is converted into local Dolibarr setup.
6680  if (is_numeric($amount)) {
6681  // We put in temps value of decimal ("0.00001"). Works with 0 and 2.0E-5 and 9999.10
6682  $temps = sprintf("%10.10F", $amount - intval($amount)); // temps=0.0000000000 or 0.0000200000 or 9999.1000000000
6683  $temps = preg_replace('/([\.1-9])0+$/', '\\1', $temps); // temps=0. or 0.00002 or 9999.1
6684  $nbofdec = max(0, dol_strlen($temps) - 2); // -2 to remove "0."
6685  $amount = number_format($amount, $nbofdec, $dec, $thousand);
6686  }
6687  //print "QQ".$amount."<br>\n";
6688 
6689  // Now make replace (the main goal of function)
6690  if ($thousand != ',' && $thousand != '.') {
6691  $amount = str_replace(',', '.', $amount); // To accept 2 notations for french users
6692  }
6693 
6694  $amount = str_replace(' ', '', $amount); // To avoid spaces
6695  $amount = str_replace($thousand, '', $amount); // Replace of thousand before replace of dec to avoid pb if thousand is .
6696  $amount = str_replace($dec, '.', $amount);
6697 
6698  $amount = preg_replace('/[^0-9\-\.]/', '', $amount); // Clean non numeric chars (so it clean some UTF8 spaces for example.
6699  }
6700  //print ' XX'.$amount.' '.$rounding;
6701 
6702  // Now, $amount is a real PHP float number. We make a rounding if required.
6703  if ($rounding) {
6704  $nbofdectoround = '';
6705  if ($rounding == 'MU') {
6706  $nbofdectoround = getDolGlobalString('MAIN_MAX_DECIMALS_UNIT');
6707  } elseif ($rounding == 'MT') {
6708  $nbofdectoround = getDolGlobalString('MAIN_MAX_DECIMALS_TOT');
6709  } elseif ($rounding == 'MS') {
6710  $nbofdectoround = isset($conf->global->MAIN_MAX_DECIMALS_STOCK) ? $conf->global->MAIN_MAX_DECIMALS_STOCK : 5;
6711  } elseif ($rounding == 'CU') {
6712  $nbofdectoround = max(getDolGlobalString('MAIN_MAX_DECIMALS_UNIT'), 8); // TODO Use param of currency
6713  } elseif ($rounding == 'CT') {
6714  $nbofdectoround = max(