dolibarr  18.0.0
functions.lib.php
Go to the documentation of this file.
1 <?php
2 /* Copyright (C) 2000-2007 Rodolphe Quiedeville <rodolphe@quiedeville.org>
3  * Copyright (C) 2003 Jean-Louis Bergamo <jlb@j1b.org>
4  * Copyright (C) 2004-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-2023 Frédéric France <frederic.france@netlogic.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  *
25  * This program is free software; you can redistribute it and/or modify
26  * it under the terms of the GNU General Public License as published by
27  * the Free Software Foundation; either version 3 of the License, or
28  * (at your option) any later version.
29  *
30  * This program is distributed in the hope that it will be useful,
31  * but WITHOUT ANY WARRANTY; without even the implied warranty of
32  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
33  * GNU General Public License for more details.
34  *
35  * You should have received a copy of the GNU General Public License
36  * along with this program. If not, see <https://www.gnu.org/licenses/>.
37  * or see https://www.gnu.org/
38  */
39 
46 include_once DOL_DOCUMENT_ROOT.'/core/lib/json.lib.php';
47 
48 // Function for better PHP x compatibility
49 if (!function_exists('utf8_encode')) {
56  function utf8_encode($elements)
57  {
58  return mb_convert_encoding($elements, 'UTF-8', 'ISO-8859-1');
59  }
60 }
61 
62 if (!function_exists('utf8_decode')) {
69  function utf8_decode($elements)
70  {
71  return mb_convert_encoding($elements, 'ISO-8859-1', 'UTF-8');
72  }
73 }
74 if (!function_exists('str_starts_with')) {
82  function str_starts_with($haystack, $needle)
83  {
84  return (string) $needle !== '' && strncmp($haystack, $needle, strlen($needle)) === 0;
85  }
86 }
87 if (!function_exists('str_ends_with')) {
95  function str_ends_with($haystack, $needle)
96  {
97  return $needle !== '' && substr($haystack, -strlen($needle)) === (string) $needle;
98  }
99 }
100 if (!function_exists('str_contains')) {
108  function str_contains($haystack, $needle)
109  {
110  return $needle !== '' && mb_strpos($haystack, $needle) !== false;
111  }
112 }
113 
114 
123 function getMultidirOutput($object, $module = '')
124 {
125  global $conf;
126  if (!is_object($object) && empty($module)) {
127  return null;
128  }
129  if (empty($module) && !empty($object->element)) {
130  $module = $object->element;
131  }
132  return $conf->$module->multidir_output[(!empty($object->entity) ? $object->entity : $conf->entity)];
133 }
134 
142 function getDolGlobalString($key, $default = '')
143 {
144  global $conf;
145  // return $conf->global->$key ?? $default;
146  return (string) (isset($conf->global->$key) ? $conf->global->$key : $default);
147 }
148 
156 function getDolGlobalInt($key, $default = 0)
157 {
158  global $conf;
159  // return $conf->global->$key ?? $default;
160  return (int) (isset($conf->global->$key) ? $conf->global->$key : $default);
161 }
162 
171 function getDolUserString($key, $default = '', $tmpuser = null)
172 {
173  if (empty($tmpuser)) {
174  global $user;
175  $tmpuser = $user;
176  }
177 
178  // return $conf->global->$key ?? $default;
179  return (string) (empty($tmpuser->conf->$key) ? $default : $tmpuser->conf->$key);
180 }
181 
190 function getDolUserInt($key, $default = 0, $tmpuser = null)
191 {
192  if (empty($tmpuser)) {
193  global $user;
194  $tmpuser = $user;
195  }
196 
197  // return $conf->global->$key ?? $default;
198  return (int) (empty($tmpuser->conf->$key) ? $default : $tmpuser->conf->$key);
199 }
200 
207 function isModEnabled($module)
208 {
209  global $conf;
210 
211  // Fix special cases
212  $arrayconv = array(
213  'project' => 'projet',
214  'contract' => 'contrat',
215  'bank' => 'banque'
216  );
217  if (empty($conf->global->MAIN_USE_NEW_SUPPLIERMOD)) {
218  $arrayconv['supplier_order'] = 'fournisseur';
219  $arrayconv['supplier_invoice'] = 'fournisseur';
220  }
221  if (!empty($arrayconv[$module])) {
222  $module = $arrayconv[$module];
223  }
224 
225  return !empty($conf->modules[$module]);
226  //return !empty($conf->$module->enabled);
227 }
228 
240 function getDoliDBInstance($type, $host, $user, $pass, $name, $port)
241 {
242  require_once DOL_DOCUMENT_ROOT."/core/db/".$type.'.class.php';
243 
244  $class = 'DoliDB'.ucfirst($type);
245  $dolidb = new $class($type, $host, $user, $pass, $name, $port);
246  return $dolidb;
247 }
248 
266 function getEntity($element, $shared = 1, $currentobject = null)
267 {
268  global $conf, $mc, $hookmanager, $object, $action, $db;
269 
270  if (!is_object($hookmanager)) {
271  include_once DOL_DOCUMENT_ROOT.'/core/class/hookmanager.class.php';
272  $hookmanager = new HookManager($db);
273  }
274 
275  // fix different element names (France to English)
276  switch ($element) {
277  case 'contrat':
278  $element = 'contract';
279  break; // "/contrat/class/contrat.class.php"
280  case 'order_supplier':
281  $element = 'supplier_order';
282  break; // "/fourn/class/fournisseur.commande.class.php"
283  case 'invoice_supplier':
284  $element = 'supplier_invoice';
285  break; // "/fourn/class/fournisseur.facture.class.php"
286  }
287 
288  if (is_object($mc)) {
289  $out = $mc->getEntity($element, $shared, $currentobject);
290  } else {
291  $out = '';
292  $addzero = array('user', 'usergroup', 'cronjob', 'c_email_templates', 'email_template', 'default_values');
293  if (in_array($element, $addzero)) {
294  $out .= '0,';
295  }
296  $out .= ((int) $conf->entity);
297  }
298 
299  // Manipulate entities to query on the fly
300  $parameters = array(
301  'element' => $element,
302  'shared' => $shared,
303  'object' => $object,
304  'currentobject' => $currentobject,
305  'out' => $out
306  );
307  $reshook = $hookmanager->executeHooks('hookGetEntity', $parameters, $currentobject, $action); // Note that $action and $object may have been modified by some hooks
308 
309  if (is_numeric($reshook)) {
310  if ($reshook == 0 && !empty($hookmanager->resPrint)) {
311  $out .= ','.$hookmanager->resPrint; // add
312  } elseif ($reshook == 1) {
313  $out = $hookmanager->resPrint; // replace
314  }
315  }
316 
317  return $out;
318 }
319 
326 function setEntity($currentobject)
327 {
328  global $conf, $mc;
329 
330  if (is_object($mc) && method_exists($mc, 'setEntity')) {
331  return $mc->setEntity($currentobject);
332  } else {
333  return ((is_object($currentobject) && $currentobject->id > 0 && $currentobject->entity > 0) ? $currentobject->entity : $conf->entity);
334  }
335 }
336 
343 function isASecretKey($keyname)
344 {
345  return preg_match('/(_pass|password|_pw|_key|securekey|serverkey|secret\d?|p12key|exportkey|_PW_[a-z]+|token)$/i', $keyname);
346 }
347 
348 
355 function num2Alpha($n)
356 {
357  for ($r = ""; $n >= 0; $n = intval($n / 26) - 1)
358  $r = chr($n % 26 + 0x41) . $r;
359  return $r;
360 }
361 
362 
379 function getBrowserInfo($user_agent)
380 {
381  include_once DOL_DOCUMENT_ROOT.'/includes/mobiledetect/mobiledetectlib/Mobile_Detect.php';
382 
383  $name = 'unknown';
384  $version = '';
385  $os = 'unknown';
386  $phone = '';
387 
388  $user_agent = substr($user_agent, 0, 512); // Avoid to process too large user agent
389 
390  $detectmobile = new Mobile_Detect(null, $user_agent);
391  $tablet = $detectmobile->isTablet();
392 
393  if ($detectmobile->isMobile()) {
394  $phone = 'unknown';
395 
396  // If phone/smartphone, we set phone os name.
397  if ($detectmobile->is('AndroidOS')) {
398  $os = $phone = 'android';
399  } elseif ($detectmobile->is('BlackBerryOS')) {
400  $os = $phone = 'blackberry';
401  } elseif ($detectmobile->is('iOS')) {
402  $os = 'ios';
403  $phone = 'iphone';
404  } elseif ($detectmobile->is('PalmOS')) {
405  $os = $phone = 'palm';
406  } elseif ($detectmobile->is('SymbianOS')) {
407  $os = 'symbian';
408  } elseif ($detectmobile->is('webOS')) {
409  $os = 'webos';
410  } elseif ($detectmobile->is('MaemoOS')) {
411  $os = 'maemo';
412  } elseif ($detectmobile->is('WindowsMobileOS') || $detectmobile->is('WindowsPhoneOS')) {
413  $os = 'windows';
414  }
415  }
416 
417  // OS
418  if (preg_match('/linux/i', $user_agent)) {
419  $os = 'linux';
420  } elseif (preg_match('/macintosh/i', $user_agent)) {
421  $os = 'macintosh';
422  } elseif (preg_match('/windows/i', $user_agent)) {
423  $os = 'windows';
424  }
425 
426  // Name
427  $reg = array();
428  if (preg_match('/firefox(\/|\s)([\d\.]*)/i', $user_agent, $reg)) {
429  $name = 'firefox';
430  $version = empty($reg[2]) ? '' : $reg[2];
431  } elseif (preg_match('/edge(\/|\s)([\d\.]*)/i', $user_agent, $reg)) {
432  $name = 'edge';
433  $version = empty($reg[2]) ? '' : $reg[2];
434  } elseif (preg_match('/chrome(\/|\s)([\d\.]+)/i', $user_agent, $reg)) {
435  $name = 'chrome';
436  $version = empty($reg[2]) ? '' : $reg[2];
437  } elseif (preg_match('/chrome/i', $user_agent, $reg)) {
438  // we can have 'chrome (Mozilla...) chrome x.y' in one string
439  $name = 'chrome';
440  } elseif (preg_match('/iceweasel/i', $user_agent)) {
441  $name = 'iceweasel';
442  } elseif (preg_match('/epiphany/i', $user_agent)) {
443  $name = 'epiphany';
444  } elseif (preg_match('/safari(\/|\s)([\d\.]*)/i', $user_agent, $reg)) {
445  $name = 'safari';
446  $version = empty($reg[2]) ? '' : $reg[2];
447  } elseif (preg_match('/opera(\/|\s)([\d\.]*)/i', $user_agent, $reg)) {
448  // Safari is often present in string for mobile but its not.
449  $name = 'opera';
450  $version = empty($reg[2]) ? '' : $reg[2];
451  } elseif (preg_match('/(MSIE\s([0-9]+\.[0-9]))|.*(Trident\/[0-9]+.[0-9];.*rv:([0-9]+\.[0-9]+))/i', $user_agent, $reg)) {
452  $name = 'ie';
453  $version = end($reg);
454  } elseif (preg_match('/(Windows NT\s([0-9]+\.[0-9])).*(Trident\/[0-9]+.[0-9];.*rv:([0-9]+\.[0-9]+))/i', $user_agent, $reg)) {
455  // MS products at end
456  $name = 'ie';
457  $version = end($reg);
458  } elseif (preg_match('/l[iy]n(x|ks)(\(|\/|\s)*([\d\.]+)/i', $user_agent, $reg)) {
459  // MS products at end
460  $name = 'lynxlinks';
461  $version = empty($reg[3]) ? '' : $reg[3];
462  }
463 
464  if ($tablet) {
465  $layout = 'tablet';
466  } elseif ($phone) {
467  $layout = 'phone';
468  } else {
469  $layout = 'classic';
470  }
471 
472  return array(
473  'browsername' => $name,
474  'browserversion' => $version,
475  'browseros' => $os,
476  'browserua' => $user_agent,
477  'layout' => $layout, // tablet, phone, classic
478  'phone' => $phone, // deprecated
479  'tablet' => $tablet // deprecated
480  );
481 }
482 
488 function dol_shutdown()
489 {
490  global $user, $langs, $db;
491  $disconnectdone = false;
492  $depth = 0;
493  if (is_object($db) && !empty($db->connected)) {
494  $depth = $db->transaction_opened;
495  $disconnectdone = $db->close();
496  }
497  dol_syslog("--- End access to ".$_SERVER["PHP_SELF"].(($disconnectdone && $depth) ? ' (Warn: db disconnection forced, transaction depth was '.$depth.')' : ''), (($disconnectdone && $depth) ? LOG_WARNING : LOG_INFO));
498 }
499 
509 function GETPOSTISSET($paramname)
510 {
511  $isset = false;
512 
513  $relativepathstring = $_SERVER["PHP_SELF"];
514  // Clean $relativepathstring
515  if (constant('DOL_URL_ROOT')) {
516  $relativepathstring = preg_replace('/^'.preg_quote(constant('DOL_URL_ROOT'), '/').'/', '', $relativepathstring);
517  }
518  $relativepathstring = preg_replace('/^\//', '', $relativepathstring);
519  $relativepathstring = preg_replace('/^custom\//', '', $relativepathstring);
520  //var_dump($relativepathstring);
521  //var_dump($user->default_values);
522 
523  // Code for search criteria persistence.
524  // Retrieve values if restore_lastsearch_values
525  if (!empty($_GET['restore_lastsearch_values'])) { // Use $_GET here and not GETPOST
526  if (!empty($_SESSION['lastsearch_values_'.$relativepathstring])) { // If there is saved values
527  $tmp = json_decode($_SESSION['lastsearch_values_'.$relativepathstring], true);
528  if (is_array($tmp)) {
529  foreach ($tmp as $key => $val) {
530  if ($key == $paramname) { // We are on the requested parameter
531  $isset = true;
532  break;
533  }
534  }
535  }
536  }
537  // If there is saved contextpage, limit, page or mode
538  if ($paramname == 'contextpage' && !empty($_SESSION['lastsearch_contextpage_'.$relativepathstring])) {
539  $isset = true;
540  } elseif ($paramname == 'limit' && !empty($_SESSION['lastsearch_limit_'.$relativepathstring])) {
541  $isset = true;
542  } elseif ($paramname == 'page' && !empty($_SESSION['lastsearch_page_'.$relativepathstring])) {
543  $isset = true;
544  } elseif ($paramname == 'mode' && !empty($_SESSION['lastsearch_mode_'.$relativepathstring])) {
545  $isset = true;
546  }
547  } else {
548  $isset = (isset($_POST[$paramname]) || isset($_GET[$paramname])); // We must keep $_POST and $_GET here
549  }
550 
551  return $isset;
552 }
553 
562 function GETPOSTISARRAY($paramname, $method = 0)
563 {
564  // for $method test need return the same $val as GETPOST
565  if (empty($method)) {
566  $val = isset($_GET[$paramname]) ? $_GET[$paramname] : (isset($_POST[$paramname]) ? $_POST[$paramname] : '');
567  } elseif ($method == 1) {
568  $val = isset($_GET[$paramname]) ? $_GET[$paramname] : '';
569  } elseif ($method == 2) {
570  $val = isset($_POST[$paramname]) ? $_POST[$paramname] : '';
571  } elseif ($method == 3) {
572  $val = isset($_POST[$paramname]) ? $_POST[$paramname] : (isset($_GET[$paramname]) ? $_GET[$paramname] : '');
573  } else {
574  $val = 'BadFirstParameterForGETPOST';
575  }
576 
577  return is_array($val);
578 }
579 
609 function GETPOST($paramname, $check = 'alphanohtml', $method = 0, $filter = null, $options = null, $noreplace = 0)
610 {
611  global $mysoc, $user, $conf;
612 
613  if (empty($paramname)) {
614  return 'BadFirstParameterForGETPOST';
615  }
616  if (empty($check)) {
617  dol_syslog("Deprecated use of GETPOST, called with 1st param = ".$paramname." and 2nd param is '', when calling page ".$_SERVER["PHP_SELF"], LOG_WARNING);
618  // Enable this line to know who call the GETPOST with '' $check parameter.
619  //var_dump(debug_backtrace()[0]);
620  }
621 
622  if (empty($method)) {
623  $out = isset($_GET[$paramname]) ? $_GET[$paramname] : (isset($_POST[$paramname]) ? $_POST[$paramname] : '');
624  } elseif ($method == 1) {
625  $out = isset($_GET[$paramname]) ? $_GET[$paramname] : '';
626  } elseif ($method == 2) {
627  $out = isset($_POST[$paramname]) ? $_POST[$paramname] : '';
628  } elseif ($method == 3) {
629  $out = isset($_POST[$paramname]) ? $_POST[$paramname] : (isset($_GET[$paramname]) ? $_GET[$paramname] : '');
630  } else {
631  return 'BadThirdParameterForGETPOST';
632  }
633 
634  if (empty($method) || $method == 3 || $method == 4) {
635  $relativepathstring = $_SERVER["PHP_SELF"];
636  // Clean $relativepathstring
637  if (constant('DOL_URL_ROOT')) {
638  $relativepathstring = preg_replace('/^'.preg_quote(constant('DOL_URL_ROOT'), '/').'/', '', $relativepathstring);
639  }
640  $relativepathstring = preg_replace('/^\//', '', $relativepathstring);
641  $relativepathstring = preg_replace('/^custom\//', '', $relativepathstring);
642  //var_dump($relativepathstring);
643  //var_dump($user->default_values);
644 
645  // Code for search criteria persistence.
646  // Retrieve values if restore_lastsearch_values
647  if (!empty($_GET['restore_lastsearch_values'])) { // Use $_GET here and not GETPOST
648  if (!empty($_SESSION['lastsearch_values_'.$relativepathstring])) { // If there is saved values
649  $tmp = json_decode($_SESSION['lastsearch_values_'.$relativepathstring], true);
650  if (is_array($tmp)) {
651  foreach ($tmp as $key => $val) {
652  if ($key == $paramname) { // We are on the requested parameter
653  $out = $val;
654  break;
655  }
656  }
657  }
658  }
659  // If there is saved contextpage, page or limit
660  if ($paramname == 'contextpage' && !empty($_SESSION['lastsearch_contextpage_'.$relativepathstring])) {
661  $out = $_SESSION['lastsearch_contextpage_'.$relativepathstring];
662  } elseif ($paramname == 'limit' && !empty($_SESSION['lastsearch_limit_'.$relativepathstring])) {
663  $out = $_SESSION['lastsearch_limit_'.$relativepathstring];
664  } elseif ($paramname == 'page' && !empty($_SESSION['lastsearch_page_'.$relativepathstring])) {
665  $out = $_SESSION['lastsearch_page_'.$relativepathstring];
666  } elseif ($paramname == 'mode' && !empty($_SESSION['lastsearch_mode_'.$relativepathstring])) {
667  $out = $_SESSION['lastsearch_mode_'.$relativepathstring];
668  }
669  } elseif (!isset($_GET['sortfield'])) {
670  // Else, retrieve default values if we are not doing a sort
671  // 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
672  if (!empty($_GET['action']) && $_GET['action'] == 'create' && !isset($_GET[$paramname]) && !isset($_POST[$paramname])) {
673  // Search default value from $object->field
674  global $object;
675  if (is_object($object) && isset($object->fields[$paramname]['default'])) {
676  $out = $object->fields[$paramname]['default'];
677  }
678  }
679  if (!empty($conf->global->MAIN_ENABLE_DEFAULT_VALUES)) {
680  if (!empty($_GET['action']) && (preg_match('/^create/', $_GET['action']) || preg_match('/^presend/', $_GET['action'])) && !isset($_GET[$paramname]) && !isset($_POST[$paramname])) {
681  // Now search in setup to overwrite default values
682  if (!empty($user->default_values)) { // $user->default_values defined from menu 'Setup - Default values'
683  if (isset($user->default_values[$relativepathstring]['createform'])) {
684  foreach ($user->default_values[$relativepathstring]['createform'] as $defkey => $defval) {
685  $qualified = 0;
686  if ($defkey != '_noquery_') {
687  $tmpqueryarraytohave = explode('&', $defkey);
688  $tmpqueryarraywehave = explode('&', dol_string_nohtmltag($_SERVER['QUERY_STRING']));
689  $foundintru = 0;
690  foreach ($tmpqueryarraytohave as $tmpquerytohave) {
691  if (!in_array($tmpquerytohave, $tmpqueryarraywehave)) {
692  $foundintru = 1;
693  }
694  }
695  if (!$foundintru) {
696  $qualified = 1;
697  }
698  //var_dump($defkey.'-'.$qualified);
699  } else {
700  $qualified = 1;
701  }
702 
703  if ($qualified) {
704  if (isset($user->default_values[$relativepathstring]['createform'][$defkey][$paramname])) {
705  $out = $user->default_values[$relativepathstring]['createform'][$defkey][$paramname];
706  break;
707  }
708  }
709  }
710  }
711  }
712  } elseif (!empty($paramname) && !isset($_GET[$paramname]) && !isset($_POST[$paramname])) {
713  // Management of default search_filters and sort order
714  if (!empty($user->default_values)) {
715  // $user->default_values defined from menu 'Setup - Default values'
716  //var_dump($user->default_values[$relativepathstring]);
717  if ($paramname == 'sortfield' || $paramname == 'sortorder') {
718  // Sorted on which fields ? ASC or DESC ?
719  if (isset($user->default_values[$relativepathstring]['sortorder'])) {
720  // Even if paramname is sortfield, data are stored into ['sortorder...']
721  foreach ($user->default_values[$relativepathstring]['sortorder'] as $defkey => $defval) {
722  $qualified = 0;
723  if ($defkey != '_noquery_') {
724  $tmpqueryarraytohave = explode('&', $defkey);
725  $tmpqueryarraywehave = explode('&', dol_string_nohtmltag($_SERVER['QUERY_STRING']));
726  $foundintru = 0;
727  foreach ($tmpqueryarraytohave as $tmpquerytohave) {
728  if (!in_array($tmpquerytohave, $tmpqueryarraywehave)) {
729  $foundintru = 1;
730  }
731  }
732  if (!$foundintru) {
733  $qualified = 1;
734  }
735  //var_dump($defkey.'-'.$qualified);
736  } else {
737  $qualified = 1;
738  }
739 
740  if ($qualified) {
741  $forbidden_chars_to_replace = array(" ", "'", "/", "\\", ":", "*", "?", "\"", "<", ">", "|", "[", "]", ";", "="); // we accept _, -, . and ,
742  foreach ($user->default_values[$relativepathstring]['sortorder'][$defkey] as $key => $val) {
743  if ($out) {
744  $out .= ', ';
745  }
746  if ($paramname == 'sortfield') {
747  $out .= dol_string_nospecial($key, '', $forbidden_chars_to_replace);
748  }
749  if ($paramname == 'sortorder') {
750  $out .= dol_string_nospecial($val, '', $forbidden_chars_to_replace);
751  }
752  }
753  //break; // No break for sortfield and sortorder so we can cumulate fields (is it realy usefull ?)
754  }
755  }
756  }
757  } elseif (isset($user->default_values[$relativepathstring]['filters'])) {
758  foreach ($user->default_values[$relativepathstring]['filters'] as $defkey => $defval) { // $defkey is a querystring like 'a=b&c=d', $defval is key of user
759  if (!empty($_GET['disabledefaultvalues'])) { // If set of default values has been disabled by a request parameter
760  continue;
761  }
762  $qualified = 0;
763  if ($defkey != '_noquery_') {
764  $tmpqueryarraytohave = explode('&', $defkey);
765  $tmpqueryarraywehave = explode('&', dol_string_nohtmltag($_SERVER['QUERY_STRING']));
766  $foundintru = 0;
767  foreach ($tmpqueryarraytohave as $tmpquerytohave) {
768  if (!in_array($tmpquerytohave, $tmpqueryarraywehave)) {
769  $foundintru = 1;
770  }
771  }
772  if (!$foundintru) {
773  $qualified = 1;
774  }
775  //var_dump($defkey.'-'.$qualified);
776  } else {
777  $qualified = 1;
778  }
779 
780  if ($qualified && isset($user->default_values[$relativepathstring]['filters'][$defkey][$paramname])) {
781  // We must keep $_POST and $_GET here
782  if (isset($_POST['sall']) || isset($_POST['search_all']) || isset($_GET['sall']) || isset($_GET['search_all'])) {
783  // We made a search from quick search menu, do we still use default filter ?
784  if (empty($conf->global->MAIN_DISABLE_DEFAULT_FILTER_FOR_QUICK_SEARCH)) {
785  $forbidden_chars_to_replace = array(" ", "'", "/", "\\", ":", "*", "?", "\"", "<", ">", "|", "[", "]", ";", "="); // we accept _, -, . and ,
786  $out = dol_string_nospecial($user->default_values[$relativepathstring]['filters'][$defkey][$paramname], '', $forbidden_chars_to_replace);
787  }
788  } else {
789  $forbidden_chars_to_replace = array(" ", "'", "/", "\\", ":", "*", "?", "\"", "<", ">", "|", "[", "]", ";", "="); // we accept _, -, . and ,
790  $out = dol_string_nospecial($user->default_values[$relativepathstring]['filters'][$defkey][$paramname], '', $forbidden_chars_to_replace);
791  }
792  break;
793  }
794  }
795  }
796  }
797  }
798  }
799  }
800  }
801 
802  // Substitution variables for GETPOST (used to get final url with variable parameters or final default value with variable parameters)
803  // Example of variables: __DAY__, __MONTH__, __YEAR__, __MYCOMPANY_COUNTRY_ID__, __USER_ID__, ...
804  // 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.
805  if (!is_array($out) && empty($_POST[$paramname]) && empty($noreplace)) {
806  $reg = array();
807  $maxloop = 20;
808  $loopnb = 0; // Protection against infinite loop
809  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.
810  $loopnb++;
811  $newout = '';
812 
813  if ($reg[1] == 'DAY') {
814  $tmp = dol_getdate(dol_now(), true);
815  $newout = $tmp['mday'];
816  } elseif ($reg[1] == 'MONTH') {
817  $tmp = dol_getdate(dol_now(), true);
818  $newout = $tmp['mon'];
819  } elseif ($reg[1] == 'YEAR') {
820  $tmp = dol_getdate(dol_now(), true);
821  $newout = $tmp['year'];
822  } elseif ($reg[1] == 'PREVIOUS_DAY') {
823  $tmp = dol_getdate(dol_now(), true);
824  $tmp2 = dol_get_prev_day($tmp['mday'], $tmp['mon'], $tmp['year']);
825  $newout = $tmp2['day'];
826  } elseif ($reg[1] == 'PREVIOUS_MONTH') {
827  $tmp = dol_getdate(dol_now(), true);
828  $tmp2 = dol_get_prev_month($tmp['mon'], $tmp['year']);
829  $newout = $tmp2['month'];
830  } elseif ($reg[1] == 'PREVIOUS_YEAR') {
831  $tmp = dol_getdate(dol_now(), true);
832  $newout = ($tmp['year'] - 1);
833  } elseif ($reg[1] == 'NEXT_DAY') {
834  $tmp = dol_getdate(dol_now(), true);
835  $tmp2 = dol_get_next_day($tmp['mday'], $tmp['mon'], $tmp['year']);
836  $newout = $tmp2['day'];
837  } elseif ($reg[1] == 'NEXT_MONTH') {
838  $tmp = dol_getdate(dol_now(), true);
839  $tmp2 = dol_get_next_month($tmp['mon'], $tmp['year']);
840  $newout = $tmp2['month'];
841  } elseif ($reg[1] == 'NEXT_YEAR') {
842  $tmp = dol_getdate(dol_now(), true);
843  $newout = ($tmp['year'] + 1);
844  } elseif ($reg[1] == 'MYCOMPANY_COUNTRY_ID' || $reg[1] == 'MYCOUNTRY_ID' || $reg[1] == 'MYCOUNTRYID') {
845  $newout = $mysoc->country_id;
846  } elseif ($reg[1] == 'USER_ID' || $reg[1] == 'USERID') {
847  $newout = $user->id;
848  } elseif ($reg[1] == 'USER_SUPERVISOR_ID' || $reg[1] == 'SUPERVISOR_ID' || $reg[1] == 'SUPERVISORID') {
849  $newout = $user->fk_user;
850  } elseif ($reg[1] == 'ENTITY_ID' || $reg[1] == 'ENTITYID') {
851  $newout = $conf->entity;
852  } else {
853  $newout = ''; // Key not found, we replace with empty string
854  }
855  //var_dump('__'.$reg[1].'__ -> '.$newout);
856  $out = preg_replace('/__'.preg_quote($reg[1], '/').'__/', $newout, $out);
857  }
858  }
859 
860  // Check rule
861  if (preg_match('/^array/', $check)) { // If 'array' or 'array:restricthtml' or 'array:aZ09' or 'array:intcomma'
862  if (!is_array($out) || empty($out)) {
863  $out = array();
864  } else {
865  $tmparray = explode(':', $check);
866  if (!empty($tmparray[1])) {
867  $tmpcheck = $tmparray[1];
868  } else {
869  $tmpcheck = 'alphanohtml';
870  }
871  foreach ($out as $outkey => $outval) {
872  $out[$outkey] = sanitizeVal($outval, $tmpcheck, $filter, $options);
873  }
874  }
875  } else {
876  // If field name is 'search_xxx' then we force the add of space after each < and > (when following char is numeric) because it means
877  // 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
878  if (strpos($paramname, 'search_') === 0) {
879  $out = preg_replace('/([<>])([-+]?\d)/', '\1 \2', $out);
880  }
881 
882  $out = sanitizeVal($out, $check, $filter, $options);
883  }
884 
885  // Sanitizing for special parameters.
886  // Note: There is no reason to allow the backtopage, backtolist or backtourl parameter to contains an external URL. Only relative URLs are allowed.
887  if ($paramname == 'backtopage' || $paramname == 'backtolist' || $paramname == 'backtourl') {
888  $out = str_replace('\\', '/', $out); // Can be before the loop because only 1 char is replaced. No risk to get it after other replacements.
889  $out = str_replace(array(':', ';', '@', "\t", ' '), '', $out); // Can be before the loop because only 1 char is replaced. No risk to retreive it after other replacements.
890  do {
891  $oldstringtoclean = $out;
892  $out = str_ireplace(array('javascript', 'vbscript', '&colon', '&#'), '', $out);
893  $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'
894  $out = preg_replace(array('/^[a-z]*\/\s*\/+/i'), '', $out); // We remove schema*// to remove external URL
895  } while ($oldstringtoclean != $out);
896  }
897 
898  // Code for search criteria persistence.
899  // Save data into session if key start with 'search_' or is 'smonth', 'syear', 'month', 'year'
900  if (empty($method) || $method == 3 || $method == 4) {
901  if (preg_match('/^search_/', $paramname) || in_array($paramname, array('sortorder', 'sortfield'))) {
902  //var_dump($paramname.' - '.$out.' '.$user->default_values[$relativepathstring]['filters'][$paramname]);
903 
904  // We save search key only if $out not empty that means:
905  // - posted value not empty, or
906  // - 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).
907 
908  if ($out != '' && isset($user)) {// $out = '0' or 'abc', it is a search criteria to keep
909  $user->lastsearch_values_tmp[$relativepathstring][$paramname] = $out;
910  }
911  }
912  }
913 
914  return $out;
915 }
916 
926 function GETPOSTINT($paramname, $method = 0)
927 {
928  return (int) GETPOST($paramname, 'int', $method, null, null, 0);
929 }
930 
931 
942 function checkVal($out = '', $check = 'alphanohtml', $filter = null, $options = null)
943 {
944  return sanitizeVal($out, $check, $filter, $options);
945 }
946 
956 function sanitizeVal($out = '', $check = 'alphanohtml', $filter = null, $options = null)
957 {
958  // TODO : use class "Validate" to perform tests (and add missing tests) if needed for factorize
959  // Check is done after replacement
960  switch ($check) {
961  case 'none':
962  break;
963  case 'int': // Check param is a numeric value (integer but also float or hexadecimal)
964  if (!is_numeric($out)) {
965  $out = '';
966  }
967  break;
968  case 'intcomma':
969  if (preg_match('/[^0-9,-]+/i', $out)) {
970  $out = '';
971  }
972  break;
973  case 'san_alpha':
974  $out = filter_var($out, FILTER_SANITIZE_STRING);
975  break;
976  case 'email':
977  $out = filter_var($out, FILTER_SANITIZE_EMAIL);
978  break;
979  case 'aZ':
980  if (!is_array($out)) {
981  $out = trim($out);
982  if (preg_match('/[^a-z]+/i', $out)) {
983  $out = '';
984  }
985  }
986  break;
987  case 'aZ09':
988  if (!is_array($out)) {
989  $out = trim($out);
990  if (preg_match('/[^a-z0-9_\-\.]+/i', $out)) {
991  $out = '';
992  }
993  }
994  break;
995  case 'aZ09arobase': // great to sanitize $objecttype parameter
996  if (!is_array($out)) {
997  $out = trim($out);
998  if (preg_match('/[^a-z0-9_\-\.@]+/i', $out)) {
999  $out = '';
1000  }
1001  }
1002  break;
1003  case 'aZ09comma': // great to sanitize $sortfield or $sortorder params that can be 't.abc,t.def_gh'
1004  if (!is_array($out)) {
1005  $out = trim($out);
1006  if (preg_match('/[^a-z0-9_\-\.,]+/i', $out)) {
1007  $out = '';
1008  }
1009  }
1010  break;
1011  case 'alpha': // No html and no ../ and "
1012  case 'alphanohtml': // Recommended for most scalar parameters and search parameters
1013  if (!is_array($out)) {
1014  $out = trim($out);
1015  do {
1016  $oldstringtoclean = $out;
1017  // Remove html tags
1018  $out = dol_string_nohtmltag($out, 0);
1019  // Remove also other dangerous string sequences
1020  // '"' is dangerous because param in url can close the href= or src= and add javascript functions.
1021  // '../' or '..\' is dangerous because it allows dir transversals
1022  // Note &#38, '&#0000038', '&#x26'... is a simple char like '&' alone but there is no reason to accept such way to encode input data.
1023  $out = str_ireplace(array('&#38', '&#0000038', '&#x26', '&quot', '&#34', '&#0000034', '&#x22', '"', '&#47', '&#0000047', '&#92', '&#0000092', '&#x2F', '../', '..\\'), '', $out);
1024  } while ($oldstringtoclean != $out);
1025  // keep lines feed
1026  }
1027  break;
1028  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'
1029  if (!is_array($out)) {
1030  $out = trim($out);
1031  do {
1032  $oldstringtoclean = $out;
1033  // Remove html tags
1034  $out = dol_html_entity_decode($out, ENT_COMPAT | ENT_HTML5, 'UTF-8');
1035  // '"' is dangerous because param in url can close the href= or src= and add javascript functions.
1036  // '../' or '..\' is dangerous because it allows dir transversals
1037  // Note &#38, '&#0000038', '&#x26'... is a simple char like '&' alone but there is no reason to accept such way to encode input data.
1038  $out = str_ireplace(array('&#38', '&#0000038', '&#x26', '&quot', '&#34', '&#0000034', '&#x22', '"', '&#47', '&#0000047', '&#92', '&#0000092', '&#x2F', '../', '..\\'), '', $out);
1039  } while ($oldstringtoclean != $out);
1040  }
1041  break;
1042  case 'nohtml': // No html
1043  $out = dol_string_nohtmltag($out, 0);
1044  break;
1045  case 'restricthtmlnolink':
1046  case 'restricthtml': // Recommended for most html textarea
1047  case 'restricthtmlallowclass':
1048  case 'restricthtmlallowunvalid':
1049  $out = dol_htmlwithnojs($out, 1, $check);
1050  break;
1051  case 'custom':
1052  if (!empty($out)) {
1053  if (empty($filter)) {
1054  return 'BadParameterForGETPOST - Param 3 of sanitizeVal()';
1055  }
1056  /*if (empty($options)) {
1057  return 'BadParameterForGETPOST - Param 4 of sanitizeVal()';
1058  }*/
1059  $out = filter_var($out, $filter, $options);
1060  }
1061  break;
1062  }
1063 
1064  return $out;
1065 }
1066 
1067 
1068 if (!function_exists('dol_getprefix')) {
1078  function dol_getprefix($mode = '')
1079  {
1080  // If prefix is for email (we need to have $conf already loaded for this case)
1081  if ($mode == 'email') {
1082  global $conf;
1083 
1084  if (!empty($conf->global->MAIL_PREFIX_FOR_EMAIL_ID)) { // If MAIL_PREFIX_FOR_EMAIL_ID is set
1085  if ($conf->global->MAIL_PREFIX_FOR_EMAIL_ID != 'SERVER_NAME') {
1086  return $conf->global->MAIL_PREFIX_FOR_EMAIL_ID;
1087  } elseif (isset($_SERVER["SERVER_NAME"])) { // If MAIL_PREFIX_FOR_EMAIL_ID is set to 'SERVER_NAME'
1088  return $_SERVER["SERVER_NAME"];
1089  }
1090  }
1091 
1092  // The recommended value if MAIL_PREFIX_FOR_EMAIL_ID is not defined (may be not defined for old versions)
1093  if (!empty($conf->file->instance_unique_id)) {
1094  return sha1('dolibarr'.$conf->file->instance_unique_id);
1095  }
1096 
1097  // For backward compatibility when instance_unique_id is not set
1098  return sha1(DOL_DOCUMENT_ROOT.DOL_URL_ROOT);
1099  }
1100 
1101  // If prefix is for session (no need to have $conf loaded)
1102  global $dolibarr_main_instance_unique_id, $dolibarr_main_cookie_cryptkey; // This is loaded by filefunc.inc.php
1103  $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
1104 
1105  // The recommended value (may be not defined for old versions)
1106  if (!empty($tmp_instance_unique_id)) {
1107  return sha1('dolibarr'.$tmp_instance_unique_id);
1108  }
1109 
1110  // For backward compatibility when instance_unique_id is not set
1111  if (isset($_SERVER["SERVER_NAME"]) && isset($_SERVER["DOCUMENT_ROOT"])) {
1112  return sha1($_SERVER["SERVER_NAME"].$_SERVER["DOCUMENT_ROOT"].DOL_DOCUMENT_ROOT.DOL_URL_ROOT);
1113  } else {
1114  return sha1(DOL_DOCUMENT_ROOT.DOL_URL_ROOT);
1115  }
1116  }
1117 }
1118 
1129 function dol_include_once($relpath, $classname = '')
1130 {
1131  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']
1132 
1133  $fullpath = dol_buildpath($relpath);
1134 
1135  if (!file_exists($fullpath)) {
1136  dol_syslog('functions::dol_include_once Tried to load unexisting file: '.$relpath, LOG_WARNING);
1137  return false;
1138  }
1139 
1140  if (!empty($classname) && !class_exists($classname)) {
1141  return include $fullpath;
1142  } else {
1143  return include_once $fullpath;
1144  }
1145 }
1146 
1147 
1158 function dol_buildpath($path, $type = 0, $returnemptyifnotfound = 0)
1159 {
1160  global $conf;
1161 
1162  $path = preg_replace('/^\//', '', $path);
1163 
1164  if (empty($type)) { // For a filesystem path
1165  $res = DOL_DOCUMENT_ROOT.'/'.$path; // Standard default path
1166  if (is_array($conf->file->dol_document_root)) {
1167  foreach ($conf->file->dol_document_root as $key => $dirroot) { // ex: array("main"=>"/home/main/htdocs", "alt0"=>"/home/dirmod/htdocs", ...)
1168  if ($key == 'main') {
1169  continue;
1170  }
1171  if (file_exists($dirroot.'/'.$path)) {
1172  $res = $dirroot.'/'.$path;
1173  return $res;
1174  }
1175  }
1176  }
1177  if ($returnemptyifnotfound) {
1178  // Not found into alternate dir
1179  if ($returnemptyifnotfound == 1 || !file_exists($res)) {
1180  return '';
1181  }
1182  }
1183  } else {
1184  // For an url path
1185  // We try to get local path of file on filesystem from url
1186  // Note that trying to know if a file on disk exist by forging path on disk from url
1187  // works only for some web server and some setup. This is bugged when
1188  // using proxy, rewriting, virtual path, etc...
1189  $res = '';
1190  if ($type == 1) {
1191  $res = DOL_URL_ROOT.'/'.$path; // Standard value
1192  }
1193  if ($type == 2) {
1194  $res = DOL_MAIN_URL_ROOT.'/'.$path; // Standard value
1195  }
1196  if ($type == 3) {
1197  $res = DOL_URL_ROOT.'/'.$path;
1198  }
1199 
1200  foreach ($conf->file->dol_document_root as $key => $dirroot) { // ex: array(["main"]=>"/home/main/htdocs", ["alt0"]=>"/home/dirmod/htdocs", ...)
1201  if ($key == 'main') {
1202  if ($type == 3) {
1203  global $dolibarr_main_url_root;
1204 
1205  // Define $urlwithroot
1206  $urlwithouturlroot = preg_replace('/'.preg_quote(DOL_URL_ROOT, '/').'$/i', '', trim($dolibarr_main_url_root));
1207  $urlwithroot = $urlwithouturlroot.DOL_URL_ROOT; // This is to use external domain name found into config file
1208  //$urlwithroot=DOL_MAIN_URL_ROOT; // This is to use same domain name than current
1209 
1210  $res = (preg_match('/^http/i', $conf->file->dol_url_root[$key]) ? '' : $urlwithroot).'/'.$path; // Test on start with http is for old conf syntax
1211  }
1212  continue;
1213  }
1214  $regs = array();
1215  preg_match('/^([^\?]+(\.css\.php|\.css|\.js\.php|\.js|\.png|\.jpg|\.php)?)/i', $path, $regs); // Take part before '?'
1216  if (!empty($regs[1])) {
1217  //print $key.'-'.$dirroot.'/'.$path.'-'.$conf->file->dol_url_root[$type].'<br>'."\n";
1218  if (file_exists($dirroot.'/'.$regs[1])) {
1219  if ($type == 1) {
1220  $res = (preg_match('/^http/i', $conf->file->dol_url_root[$key]) ? '' : DOL_URL_ROOT).$conf->file->dol_url_root[$key].'/'.$path;
1221  }
1222  if ($type == 2) {
1223  $res = (preg_match('/^http/i', $conf->file->dol_url_root[$key]) ? '' : DOL_MAIN_URL_ROOT).$conf->file->dol_url_root[$key].'/'.$path;
1224  }
1225  if ($type == 3) {
1226  global $dolibarr_main_url_root;
1227 
1228  // Define $urlwithroot
1229  $urlwithouturlroot = preg_replace('/'.preg_quote(DOL_URL_ROOT, '/').'$/i', '', trim($dolibarr_main_url_root));
1230  $urlwithroot = $urlwithouturlroot.DOL_URL_ROOT; // This is to use external domain name found into config file
1231  //$urlwithroot=DOL_MAIN_URL_ROOT; // This is to use same domain name than current
1232 
1233  $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
1234  }
1235  break;
1236  }
1237  }
1238  }
1239  }
1240 
1241  return $res;
1242 }
1243 
1255 function dol_clone($object, $native = 0)
1256 {
1257  if ($native == 0) {
1258  // deprecated method, use the method with native = 2 instead
1259  $tmpsavdb = null;
1260  if (isset($object->db) && isset($object->db->db) && is_object($object->db->db) && get_class($object->db->db) == 'PgSql\Connection') {
1261  $tmpsavdb = $object->db;
1262  unset($object->db); // Such property can not be serialized with pgsl (when object->db->db = 'PgSql\Connection')
1263  }
1264 
1265  $myclone = unserialize(serialize($object)); // serialize then unserialize is a hack to be sure to have a new object for all fields
1266 
1267  if (!empty($tmpsavdb)) {
1268  $object->db = $tmpsavdb;
1269  }
1270  } elseif ($native == 2) {
1271  // recommended method to have a full isolated cloned object
1272  $myclone = new stdClass();
1273  $tmparray = get_object_vars($object); // return only public properties
1274 
1275  if (is_array($tmparray)) {
1276  foreach ($tmparray as $propertykey => $propertyval) {
1277  if (is_scalar($propertyval) || is_array($propertyval)) {
1278  $myclone->$propertykey = $propertyval;
1279  }
1280  }
1281  }
1282  } else {
1283  $myclone = clone $object; // PHP clone is a shallow copy only, not a real clone, so properties of references will keep the reference (refering to the same target/variable)
1284  }
1285 
1286  return $myclone;
1287 }
1288 
1298 function dol_size($size, $type = '')
1299 {
1300  global $conf;
1301  if (empty($conf->dol_optimize_smallscreen)) {
1302  return $size;
1303  }
1304  if ($type == 'width' && $size > 250) {
1305  return 250;
1306  } else {
1307  return 10;
1308  }
1309 }
1310 
1311 
1323 function dol_sanitizeFileName($str, $newstr = '_', $unaccent = 1)
1324 {
1325  // List of special chars for filenames in windows are defined on page https://docs.microsoft.com/en-us/windows/win32/fileio/naming-a-file
1326  // Char '>' '<' '|' '$' and ';' are special chars for shells.
1327  // Char '/' and '\' are file delimiters.
1328  // Chars '--' can be used into filename to inject special paramaters like --use-compress-program to make command with file as parameter making remote execution of command
1329  $filesystem_forbidden_chars = array('<', '>', '/', '\\', '?', '*', '|', '"', ':', '°', '$', ';', '`');
1330  $tmp = dol_string_nospecial($unaccent ? dol_string_unaccent($str) : $str, $newstr, $filesystem_forbidden_chars);
1331  $tmp = preg_replace('/\-\-+/', '_', $tmp);
1332  $tmp = preg_replace('/\s+\-([^\s])/', ' _$1', $tmp);
1333  $tmp = preg_replace('/\s+\-$/', '', $tmp);
1334  $tmp = str_replace('..', '', $tmp);
1335  return $tmp;
1336 }
1337 
1349 function dol_sanitizePathName($str, $newstr = '_', $unaccent = 1)
1350 {
1351  // List of special chars for filenames in windows are defined on page https://docs.microsoft.com/en-us/windows/win32/fileio/naming-a-file
1352  // Char '>' '<' '|' '$' and ';' are special chars for shells.
1353  // Chars '--' can be used into filename to inject special paramaters like --use-compress-program to make command with file as parameter making remote execution of command
1354  $filesystem_forbidden_chars = array('<', '>', '?', '*', '|', '"', '°', '$', ';', '`');
1355  $tmp = dol_string_nospecial($unaccent ? dol_string_unaccent($str) : $str, $newstr, $filesystem_forbidden_chars);
1356  $tmp = preg_replace('/\-\-+/', '_', $tmp);
1357  $tmp = preg_replace('/\s+\-([^\s])/', ' _$1', $tmp);
1358  $tmp = preg_replace('/\s+\-$/', '', $tmp);
1359  $tmp = str_replace('..', '', $tmp);
1360  return $tmp;
1361 }
1362 
1370 function dol_sanitizeUrl($stringtoclean, $type = 1)
1371 {
1372  // 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)
1373  // We should use dol_string_nounprintableascii but function may not be yet loaded/available
1374  $stringtoclean = preg_replace('/[\x00-\x1F\x7F]/u', '', $stringtoclean); // /u operator makes UTF8 valid characters being ignored so are not included into the replace
1375  // We clean html comments because some hacks try to obfuscate evil strings by inserting HTML comments. Example: on<!-- -->error=alert(1)
1376  $stringtoclean = preg_replace('/<!--[^>]*-->/', '', $stringtoclean);
1377 
1378  $stringtoclean = str_replace('\\', '/', $stringtoclean);
1379  if ($type == 1) {
1380  // removing : should disable links to external url like http:aaa)
1381  // removing ';' should disable "named" html entities encode into an url (we should not have this into an url)
1382  $stringtoclean = str_replace(array(':', ';', '@'), '', $stringtoclean);
1383  }
1384 
1385  do {
1386  $oldstringtoclean = $stringtoclean;
1387  // removing '&colon' should disable links to external url like http:aaa)
1388  // removing '&#' should disable "numeric" html entities encode into an url (we should not have this into an url)
1389  $stringtoclean = str_ireplace(array('javascript', 'vbscript', '&colon', '&#'), '', $stringtoclean);
1390  } while ($oldstringtoclean != $stringtoclean);
1391 
1392  if ($type == 1) {
1393  // removing '//' should disable links to external url like //aaa or http//)
1394  $stringtoclean = preg_replace(array('/^[a-z]*\/\/+/i'), '', $stringtoclean);
1395  }
1396 
1397  return $stringtoclean;
1398 }
1399 
1406 function dol_sanitizeEmail($stringtoclean)
1407 {
1408  do {
1409  $oldstringtoclean = $stringtoclean;
1410  $stringtoclean = str_ireplace(array('"', ':', '[', ']',"\n", "\r", '\\', '\/'), '', $stringtoclean);
1411  } while ($oldstringtoclean != $stringtoclean);
1412 
1413  return $stringtoclean;
1414 }
1415 
1424 function dol_string_unaccent($str)
1425 {
1426  global $conf;
1427 
1428  if (is_null($str)) {
1429  return '';
1430  }
1431 
1432  if (utf8_check($str)) {
1433  if (extension_loaded('intl') && !empty($conf->global->MAIN_UNACCENT_USE_TRANSLITERATOR)) {
1434  $transliterator = Transliterator::createFromRules(':: Any-Latin; :: Latin-ASCII; :: NFD; :: [:Nonspacing Mark:] Remove; :: NFC;', Transliterator::FORWARD);
1435  return $transliterator->transliterate($str);
1436  }
1437  // See http://www.utf8-chartable.de/
1438  $string = rawurlencode($str);
1439  $replacements = array(
1440  '%C3%80' => 'A', '%C3%81' => 'A', '%C3%82' => 'A', '%C3%83' => 'A', '%C3%84' => 'A', '%C3%85' => 'A',
1441  '%C3%87' => 'C',
1442  '%C3%88' => 'E', '%C3%89' => 'E', '%C3%8A' => 'E', '%C3%8B' => 'E',
1443  '%C3%8C' => 'I', '%C3%8D' => 'I', '%C3%8E' => 'I', '%C3%8F' => 'I',
1444  '%C3%91' => 'N',
1445  '%C3%92' => 'O', '%C3%93' => 'O', '%C3%94' => 'O', '%C3%95' => 'O', '%C3%96' => 'O',
1446  '%C5%A0' => 'S',
1447  '%C3%99' => 'U', '%C3%9A' => 'U', '%C3%9B' => 'U', '%C3%9C' => 'U',
1448  '%C3%9D' => 'Y', '%C5%B8' => 'y',
1449  '%C3%A0' => 'a', '%C3%A1' => 'a', '%C3%A2' => 'a', '%C3%A3' => 'a', '%C3%A4' => 'a', '%C3%A5' => 'a',
1450  '%C3%A7' => 'c',
1451  '%C3%A8' => 'e', '%C3%A9' => 'e', '%C3%AA' => 'e', '%C3%AB' => 'e',
1452  '%C3%AC' => 'i', '%C3%AD' => 'i', '%C3%AE' => 'i', '%C3%AF' => 'i',
1453  '%C3%B1' => 'n',
1454  '%C3%B2' => 'o', '%C3%B3' => 'o', '%C3%B4' => 'o', '%C3%B5' => 'o', '%C3%B6' => 'o',
1455  '%C5%A1' => 's',
1456  '%C3%B9' => 'u', '%C3%BA' => 'u', '%C3%BB' => 'u', '%C3%BC' => 'u',
1457  '%C3%BD' => 'y', '%C3%BF' => 'y'
1458  );
1459  $string = strtr($string, $replacements);
1460  return rawurldecode($string);
1461  } else {
1462  // See http://www.ascii-code.com/
1463  $string = strtr(
1464  $str,
1465  "\xC0\xC1\xC2\xC3\xC4\xC5\xC7
1466  \xC8\xC9\xCA\xCB\xCC\xCD\xCE\xCF\xD0\xD1
1467  \xD2\xD3\xD4\xD5\xD8\xD9\xDA\xDB\xDD
1468  \xE0\xE1\xE2\xE3\xE4\xE5\xE7\xE8\xE9\xEA\xEB
1469  \xEC\xED\xEE\xEF\xF0\xF1\xF2\xF3\xF4\xF5\xF8
1470  \xF9\xFA\xFB\xFC\xFD\xFF",
1471  "AAAAAAC
1472  EEEEIIIIDN
1473  OOOOOUUUY
1474  aaaaaaceeee
1475  iiiidnooooo
1476  uuuuyy"
1477  );
1478  $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"));
1479  return $string;
1480  }
1481 }
1482 
1496 function dol_string_nospecial($str, $newstr = '_', $badcharstoreplace = '', $badcharstoremove = '', $keepspaces = 0)
1497 {
1498  $forbidden_chars_to_replace = array("'", "/", "\\", ":", "*", "?", "\"", "<", ">", "|", "[", "]", ",", ";", "=", '°', '$', ';'); // more complete than dol_sanitizeFileName
1499  if (empty($keepspaces)) {
1500  $forbidden_chars_to_replace[] = " ";
1501  }
1502  $forbidden_chars_to_remove = array();
1503  //$forbidden_chars_to_remove=array("(",")");
1504 
1505  if (is_array($badcharstoreplace)) {
1506  $forbidden_chars_to_replace = $badcharstoreplace;
1507  }
1508  if (is_array($badcharstoremove)) {
1509  $forbidden_chars_to_remove = $badcharstoremove;
1510  }
1511 
1512  return str_replace($forbidden_chars_to_replace, $newstr, str_replace($forbidden_chars_to_remove, "", $str));
1513 }
1514 
1515 
1529 function dol_string_nounprintableascii($str, $removetabcrlf = 1)
1530 {
1531  if ($removetabcrlf) {
1532  return preg_replace('/[\x00-\x1F\x7F]/u', '', $str); // /u operator makes UTF8 valid characters being ignored so are not included into the replace
1533  } else {
1534  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
1535  }
1536 }
1537 
1546 function dol_escape_js($stringtoescape, $mode = 0, $noescapebackslashn = 0)
1547 {
1548  if (is_null($stringtoescape)) {
1549  return '';
1550  }
1551 
1552  // escape quotes and backslashes, newlines, etc.
1553  $substitjs = array("&#039;"=>"\\'", "\r"=>'\\r');
1554  //$substitjs['</']='<\/'; // We removed this. Should be useless.
1555  if (empty($noescapebackslashn)) {
1556  $substitjs["\n"] = '\\n';
1557  $substitjs['\\'] = '\\\\';
1558  }
1559  if (empty($mode)) {
1560  $substitjs["'"] = "\\'";
1561  $substitjs['"'] = "\\'";
1562  } elseif ($mode == 1) {
1563  $substitjs["'"] = "\\'";
1564  } elseif ($mode == 2) {
1565  $substitjs['"'] = '\\"';
1566  } elseif ($mode == 3) {
1567  $substitjs["'"] = "\\'";
1568  $substitjs['"'] = "\\\"";
1569  }
1570  return strtr($stringtoescape, $substitjs);
1571 }
1572 
1579 function dol_escape_json($stringtoescape)
1580 {
1581  return str_replace('"', '\"', $stringtoescape);
1582 }
1583 
1600 function dol_escape_htmltag($stringtoescape, $keepb = 0, $keepn = 0, $noescapetags = '', $escapeonlyhtmltags = 0, $cleanalsojavascript = 0)
1601 {
1602  if ($noescapetags == 'common') {
1603  $noescapetags = 'html,body,a,b,em,hr,i,u,ul,li,br,div,img,font,p,span,strong,table,tr,td,th,tbody';
1604  }
1605  if ($cleanalsojavascript) {
1606  $stringtoescape = dol_string_onlythesehtmltags($stringtoescape, 0, 0, $cleanalsojavascript, 0, array(), 0);
1607  }
1608 
1609  // escape quotes and backslashes, newlines, etc.
1610  if ($escapeonlyhtmltags) {
1611  $tmp = htmlspecialchars_decode((string) $stringtoescape, ENT_COMPAT);
1612  } else {
1613  $tmp = html_entity_decode((string) $stringtoescape, ENT_COMPAT, 'UTF-8');
1614  }
1615  if (!$keepb) {
1616  $tmp = strtr($tmp, array("<b>"=>'', '</b>'=>''));
1617  }
1618  if (!$keepn) {
1619  $tmp = strtr($tmp, array("\r"=>'\\r', "\n"=>'\\n'));
1620  }
1621 
1622  if ($escapeonlyhtmltags) {
1623  return htmlspecialchars($tmp, ENT_COMPAT, 'UTF-8');
1624  } else {
1625  // Escape tags to keep
1626  // TODO Does not works yet when there is attributes into tag
1627  $tmparrayoftags = array();
1628  if ($noescapetags) {
1629  $tmparrayoftags = explode(',', $noescapetags);
1630  }
1631  if (count($tmparrayoftags)) {
1632  foreach ($tmparrayoftags as $tagtoreplace) {
1633  $tmp = str_ireplace('<'.$tagtoreplace.'>', '__BEGINTAGTOREPLACE'.$tagtoreplace.'__', $tmp);
1634  $tmp = str_ireplace('</'.$tagtoreplace.'>', '__ENDTAGTOREPLACE'.$tagtoreplace.'__', $tmp);
1635  $tmp = str_ireplace('<'.$tagtoreplace.' />', '__BEGINENDTAGTOREPLACE'.$tagtoreplace.'__', $tmp);
1636  }
1637  }
1638 
1639  $result = htmlentities($tmp, ENT_COMPAT, 'UTF-8');
1640 
1641  if (count($tmparrayoftags)) {
1642  foreach ($tmparrayoftags as $tagtoreplace) {
1643  $result = str_ireplace('__BEGINTAGTOREPLACE'.$tagtoreplace.'__', '<'.$tagtoreplace.'>', $result);
1644  $result = str_ireplace('__ENDTAGTOREPLACE'.$tagtoreplace.'__', '</'.$tagtoreplace.'>', $result);
1645  $result = str_ireplace('__BEGINENDTAGTOREPLACE'.$tagtoreplace.'__', '<'.$tagtoreplace.' />', $result);
1646  }
1647  }
1648 
1649  return $result;
1650  }
1651 }
1652 
1660 function dol_strtolower($string, $encoding = "UTF-8")
1661 {
1662  if (function_exists('mb_strtolower')) {
1663  return mb_strtolower($string, $encoding);
1664  } else {
1665  return strtolower($string);
1666  }
1667 }
1668 
1677 function dol_strtoupper($string, $encoding = "UTF-8")
1678 {
1679  if (function_exists('mb_strtoupper')) {
1680  return mb_strtoupper($string, $encoding);
1681  } else {
1682  return strtoupper($string);
1683  }
1684 }
1685 
1694 function dol_ucfirst($string, $encoding = "UTF-8")
1695 {
1696  if (function_exists('mb_substr')) {
1697  return mb_strtoupper(mb_substr($string, 0, 1, $encoding), $encoding).mb_substr($string, 1, null, $encoding);
1698  } else {
1699  return ucfirst($string);
1700  }
1701 }
1702 
1711 function dol_ucwords($string, $encoding = "UTF-8")
1712 {
1713  if (function_exists('mb_convert_case')) {
1714  return mb_convert_case($string, MB_CASE_TITLE, $encoding);
1715  } else {
1716  return ucwords($string);
1717  }
1718 }
1719 
1741 function dol_syslog($message, $level = LOG_INFO, $ident = 0, $suffixinfilename = '', $restricttologhandler = '', $logcontext = null)
1742 {
1743  global $conf, $user, $debugbar;
1744 
1745  // If syslog module enabled
1746  if (!isModEnabled('syslog')) {
1747  return;
1748  }
1749 
1750  // Check if we are into execution of code of a website
1751  if (defined('USEEXTERNALSERVER') && !defined('USEDOLIBARRSERVER') && !defined('USEDOLIBARREDITOR')) {
1752  global $website, $websitekey;
1753  if (is_object($website) && !empty($website->ref)) {
1754  $suffixinfilename .= '_website_'.$website->ref;
1755  } elseif (!empty($websitekey)) {
1756  $suffixinfilename .= '_website_'.$websitekey;
1757  }
1758  }
1759 
1760  // Check if we have a forced suffix
1761  if (defined('USESUFFIXINLOG')) {
1762  $suffixinfilename .= constant('USESUFFIXINLOG');
1763  }
1764 
1765  if ($ident < 0) {
1766  foreach ($conf->loghandlers as $loghandlerinstance) {
1767  $loghandlerinstance->setIdent($ident);
1768  }
1769  }
1770 
1771  if (!empty($message)) {
1772  // Test log level
1773  $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');
1774  if (!array_key_exists($level, $logLevels)) {
1775  throw new Exception('Incorrect log level');
1776  }
1777  if ($level > getDolGlobalInt('SYSLOG_LEVEL')) {
1778  return;
1779  }
1780 
1781  if (empty($conf->global->MAIN_SHOW_PASSWORD_INTO_LOG)) {
1782  $message = preg_replace('/password=\'[^\']*\'/', 'password=\'hidden\'', $message); // protection to avoid to have value of password in log
1783  }
1784 
1785  // If adding log inside HTML page is required
1786  if ((!empty($_REQUEST['logtohtml']) && !empty($conf->global->MAIN_ENABLE_LOG_TO_HTML))
1787  || (!empty($user->rights->debugbar->read) && is_object($debugbar))) {
1788  $conf->logbuffer[] = dol_print_date(time(), "%Y-%m-%d %H:%M:%S")." ".$logLevels[$level]." ".$message;
1789  }
1790 
1791  //TODO: Remove this. MAIN_ENABLE_LOG_INLINE_HTML should be deprecated and use a log handler dedicated to HTML output
1792  // If html log tag enabled and url parameter log defined, we show output log on HTML comments
1793  if (!empty($conf->global->MAIN_ENABLE_LOG_INLINE_HTML) && !empty($_GET["log"])) {
1794  print "\n\n<!-- Log start\n";
1795  print dol_escape_htmltag($message)."\n";
1796  print "Log end -->\n";
1797  }
1798 
1799  $data = array(
1800  'message' => $message,
1801  'script' => (isset($_SERVER['PHP_SELF']) ? basename($_SERVER['PHP_SELF'], '.php') : false),
1802  'level' => $level,
1803  'user' => ((is_object($user) && $user->id) ? $user->login : false),
1804  'ip' => false
1805  );
1806 
1807  $remoteip = getUserRemoteIP(); // Get ip when page run on a web server
1808  if (!empty($remoteip)) {
1809  $data['ip'] = $remoteip;
1810  // This is when server run behind a reverse proxy
1811  if (!empty($_SERVER['HTTP_X_FORWARDED_FOR']) && $_SERVER['HTTP_X_FORWARDED_FOR'] != $remoteip) {
1812  $data['ip'] = $_SERVER['HTTP_X_FORWARDED_FOR'].' -> '.$data['ip'];
1813  } elseif (!empty($_SERVER['HTTP_CLIENT_IP']) && $_SERVER['HTTP_CLIENT_IP'] != $remoteip) {
1814  $data['ip'] = $_SERVER['HTTP_CLIENT_IP'].' -> '.$data['ip'];
1815  }
1816  } elseif (!empty($_SERVER['SERVER_ADDR'])) {
1817  // This is when PHP session is ran inside a web server but not inside a client request (example: init code of apache)
1818  $data['ip'] = $_SERVER['SERVER_ADDR'];
1819  } elseif (!empty($_SERVER['COMPUTERNAME'])) {
1820  // This is when PHP session is ran outside a web server, like from Windows command line (Not always defined, but useful if OS defined it).
1821  $data['ip'] = $_SERVER['COMPUTERNAME'].(empty($_SERVER['USERNAME']) ? '' : '@'.$_SERVER['USERNAME']);
1822  } elseif (!empty($_SERVER['LOGNAME'])) {
1823  // This is when PHP session is ran outside a web server, like from Linux command line (Not always defined, but usefull if OS defined it).
1824  $data['ip'] = '???@'.$_SERVER['LOGNAME'];
1825  }
1826 
1827  // Loop on each log handler and send output
1828  foreach ($conf->loghandlers as $loghandlerinstance) {
1829  if ($restricttologhandler && $loghandlerinstance->code != $restricttologhandler) {
1830  continue;
1831  }
1832  $loghandlerinstance->export($data, $suffixinfilename);
1833  }
1834  unset($data);
1835  }
1836 
1837  if ($ident > 0) {
1838  foreach ($conf->loghandlers as $loghandlerinstance) {
1839  $loghandlerinstance->setIdent($ident);
1840  }
1841  }
1842 }
1843 
1860 function dolButtonToOpenUrlInDialogPopup($name, $label, $buttonstring, $url, $disabled = '', $morecss = 'classlink button bordertransp', $jsonopen = '', $backtopagejsfields = '', $accesskey = '')
1861 {
1862  global $conf;
1863 
1864  if (strpos($url, '?') > 0) {
1865  $url .= '&dol_hide_topmenu=1&dol_hide_leftmenu=1&dol_openinpopup='.urlencode($name);
1866  } else {
1867  $url .= '?dol_hide_topmenu=1&dol_hide_leftmenu=1&dol_openinpopup='.urlencode($name);
1868  }
1869 
1870  $out = '';
1871 
1872  $backtopagejsfieldsid = ''; $backtopagejsfieldslabel = '';
1873  if ($backtopagejsfields) {
1874  $tmpbacktopagejsfields = explode(':', $backtopagejsfields);
1875  if (empty($tmpbacktopagejsfields[1])) { // If the part 'keyforpopupid:' is missing, we add $name for it.
1876  $backtopagejsfields = $name.":".$backtopagejsfields;
1877  $tmp2backtopagejsfields = explode(',', $tmpbacktopagejsfields[0]);
1878  } else {
1879  $tmp2backtopagejsfields = explode(',', $tmpbacktopagejsfields[1]);
1880  }
1881  $backtopagejsfieldsid = empty($tmp2backtopagejsfields[0]) ? '' : $tmp2backtopagejsfields[0];
1882  $backtopagejsfieldslabel = empty($tmp2backtopagejsfields[1]) ? '' : $tmp2backtopagejsfields[1];
1883  $url .= '&backtopagejsfields='.urlencode($backtopagejsfields);
1884  }
1885 
1886  //print '<input type="submit" class="button bordertransp"'.$disabled.' value="'.dol_escape_htmltag($langs->trans("MediaFiles")).'" name="file_manager">';
1887  $out .= '<!-- a link for button to open url into a dialog popup with backtopagejsfields = '.$backtopagejsfields.' -->';
1888  $out .= '<a '.($accesskey ? ' accesskey="'.$accesskey.'"' : '').' class="cursorpointer reposition button_'.$name.($morecss ? ' '.$morecss : '').'"'.$disabled.' title="'.dol_escape_htmltag($label).'"';
1889  if (empty($conf->use_javascript_ajax)) {
1890  $out .= ' href="'.DOL_URL_ROOT.$url.'" target="_blank"';
1891  } elseif ($jsonopen) {
1892  $out .= ' href="#" onclick="'.$jsonopen.'"';
1893  } else {
1894  $out .= ' href="#"';
1895  }
1896  $out .= '>'.$buttonstring.'</a>';
1897 
1898  if (!empty($conf->use_javascript_ajax)) {
1899  // Add code to open url using the popup. Add also hidden field to retreive the returned variables
1900  $out .= '<!-- code to open popup and variables to retreive returned variables -->';
1901  $out .= '<div id="idfordialog'.$name.'" class="hidden">div for dialog</div>';
1902  $out .= '<div id="varforreturndialogid'.$name.'" class="hidden">div for returned id</div>';
1903  $out .= '<div id="varforreturndialoglabel'.$name.'" class="hidden">div for returned label</div>';
1904  $out .= '<!-- Add js code to open dialog popup on dialog -->';
1905  $out .= '<script nonce="'.getNonce().'" type="text/javascript">
1906  jQuery(document).ready(function () {
1907  jQuery(".button_'.$name.'").click(function () {
1908  console.log(\'Open popup with jQuery(...).dialog() on URL '.dol_escape_js(DOL_URL_ROOT.$url).'\');
1909  var $tmpdialog = $(\'#idfordialog'.$name.'\');
1910  $tmpdialog.html(\'<iframe class="iframedialog" id="iframedialog'.$name.'" style="border: 0px;" src="'.DOL_URL_ROOT.$url.'" width="100%" height="98%"></iframe>\');
1911  $tmpdialog.dialog({
1912  autoOpen: false,
1913  modal: true,
1914  height: (window.innerHeight - 150),
1915  width: \'80%\',
1916  title: \''.dol_escape_js($label).'\',
1917  open: function (event, ui) {
1918  console.log("open popup name='.$name.', backtopagejsfields='.$backtopagejsfields.'");
1919  },
1920  close: function (event, ui) {
1921  var returnedid = jQuery("#varforreturndialogid'.$name.'").text();
1922  var returnedlabel = jQuery("#varforreturndialoglabel'.$name.'").text();
1923  console.log("popup has been closed. returnedid (js var defined into parent page)="+returnedid+" returnedlabel="+returnedlabel);
1924  if (returnedid != "" && returnedid != "div for returned id") {
1925  jQuery("#'.(empty($backtopagejsfieldsid)?"none":$backtopagejsfieldsid).'").val(returnedid);
1926  }
1927  if (returnedlabel != "" && returnedlabel != "div for returned label") {
1928  jQuery("#'.(empty($backtopagejsfieldslabel)?"none":$backtopagejsfieldslabel).'").val(returnedlabel);
1929  }
1930  }
1931  });
1932 
1933  $tmpdialog.dialog(\'open\');
1934  return false;
1935  });
1936  });
1937  </script>';
1938  }
1939  return $out;
1940 }
1941 
1958 function dol_fiche_head($links = array(), $active = '0', $title = '', $notab = 0, $picto = '', $pictoisfullpath = 0, $morehtmlright = '', $morecss = '', $limittoshow = 0, $moretabssuffix = '')
1959 {
1960  print dol_get_fiche_head($links, $active, $title, $notab, $picto, $pictoisfullpath, $morehtmlright, $morecss, $limittoshow, $moretabssuffix);
1961 }
1962 
1979 function dol_get_fiche_head($links = array(), $active = '', $title = '', $notab = 0, $picto = '', $pictoisfullpath = 0, $morehtmlright = '', $morecss = '', $limittoshow = 0, $moretabssuffix = '', $dragdropfile = 0)
1980 {
1981  global $conf, $langs, $hookmanager;
1982 
1983  // Show title
1984  $showtitle = 1;
1985  if (!empty($conf->dol_optimize_smallscreen)) {
1986  $showtitle = 0;
1987  }
1988 
1989  $out = "\n".'<!-- dol_fiche_head - dol_get_fiche_head -->';
1990 
1991  if ((!empty($title) && $showtitle) || $morehtmlright || !empty($links)) {
1992  $out .= '<div class="tabs'.($picto ? '' : ' nopaddingleft').'" data-role="controlgroup" data-type="horizontal">'."\n";
1993  }
1994 
1995  // Show right part
1996  if ($morehtmlright) {
1997  $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.
1998  }
1999 
2000  // Show title
2001  if (!empty($title) && $showtitle && empty($conf->global->MAIN_OPTIMIZEFORTEXTBROWSER)) {
2002  $limittitle = 30;
2003  $out .= '<a class="tabTitle">';
2004  if ($picto) {
2005  $noprefix = $pictoisfullpath;
2006  if (strpos($picto, 'fontawesome_') !== false) {
2007  $noprefix = 1;
2008  }
2009  $out .= img_picto($title, ($noprefix ? '' : 'object_').$picto, '', $pictoisfullpath, 0, 0, '', 'imgTabTitle').' ';
2010  }
2011  $out .= '<span class="tabTitleText">'.dol_escape_htmltag(dol_trunc($title, $limittitle)).'</span>';
2012  $out .= '</a>';
2013  }
2014 
2015  // Show tabs
2016 
2017  // Define max of key (max may be higher than sizeof because of hole due to module disabling some tabs).
2018  $maxkey = -1;
2019  if (is_array($links) && !empty($links)) {
2020  $keys = array_keys($links);
2021  if (count($keys)) {
2022  $maxkey = max($keys);
2023  }
2024  }
2025 
2026  // Show tabs
2027  // if =0 we don't use the feature
2028  if (empty($limittoshow)) {
2029  $limittoshow = (empty($conf->global->MAIN_MAXTABS_IN_CARD) ? 99 : $conf->global->MAIN_MAXTABS_IN_CARD);
2030  }
2031  if (!empty($conf->dol_optimize_smallscreen)) {
2032  $limittoshow = 2;
2033  }
2034 
2035  $displaytab = 0;
2036  $nbintab = 0;
2037  $popuptab = 0;
2038  $outmore = '';
2039  for ($i = 0; $i <= $maxkey; $i++) {
2040  if ((is_numeric($active) && $i == $active) || (!empty($links[$i][2]) && !is_numeric($active) && $active == $links[$i][2])) {
2041  // If active tab is already present
2042  if ($i >= $limittoshow) {
2043  $limittoshow--;
2044  }
2045  }
2046  }
2047 
2048  for ($i = 0; $i <= $maxkey; $i++) {
2049  if ((is_numeric($active) && $i == $active) || (!empty($links[$i][2]) && !is_numeric($active) && $active == $links[$i][2])) {
2050  $isactive = true;
2051  } else {
2052  $isactive = false;
2053  }
2054 
2055  if ($i < $limittoshow || $isactive) {
2056  // Output entry with a visible tab
2057  $out .= '<div class="inline-block tabsElem'.($isactive ? ' tabsElemActive' : '').((!$isactive && !empty($conf->global->MAIN_HIDE_INACTIVETAB_ON_PRINT)) ? ' hideonprint' : '').'"><!-- id tab = '.(empty($links[$i][2]) ? '' : dol_escape_htmltag($links[$i][2])).' -->';
2058 
2059  if (isset($links[$i][2]) && $links[$i][2] == 'image') {
2060  if (!empty($links[$i][0])) {
2061  $out .= '<a class="tabimage'.($morecss ? ' '.$morecss : '').'" href="'.$links[$i][0].'">'.$links[$i][1].'</a>'."\n";
2062  } else {
2063  $out .= '<span class="tabspan">'.$links[$i][1].'</span>'."\n";
2064  }
2065  } elseif (!empty($links[$i][1])) {
2066  //print "x $i $active ".$links[$i][2]." z";
2067  $out .= '<div class="tab tab'.($isactive?'active':'unactive').'" style="margin: 0 !important">';
2068  if (!empty($links[$i][0])) {
2069  $titletoshow = preg_replace('/<.*$/', '', $links[$i][1]);
2070  $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).'">';
2071  }
2072  $out .= $links[$i][1];
2073  if (!empty($links[$i][0])) {
2074  $out .= '</a>'."\n";
2075  }
2076  $out .= empty($links[$i][4]) ? '' : $links[$i][4];
2077  $out .= '</div>';
2078  }
2079 
2080  $out .= '</div>';
2081  } else {
2082  // Add entry into the combo popup with the other tabs
2083  if (!$popuptab) {
2084  $popuptab = 1;
2085  $outmore .= '<div class="popuptabset wordwrap">'; // The css used to hide/show popup
2086  }
2087  $outmore .= '<div class="popuptab wordwrap" style="display:inherit;">';
2088  if (isset($links[$i][2]) && $links[$i][2] == 'image') {
2089  if (!empty($links[$i][0])) {
2090  $outmore .= '<a class="tabimage'.($morecss ? ' '.$morecss : '').'" href="'.$links[$i][0].'">'.$links[$i][1].'</a>'."\n";
2091  } else {
2092  $outmore .= '<span class="tabspan">'.$links[$i][1].'</span>'."\n";
2093  }
2094  } elseif (!empty($links[$i][1])) {
2095  $outmore .= '<a'.(!empty($links[$i][2]) ? ' id="'.$links[$i][2].'"' : '').' class="wordwrap inline-block'.($morecss ? ' '.$morecss : '').'" href="'.$links[$i][0].'">';
2096  $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.
2097  $outmore .= '</a>'."\n";
2098  }
2099  $outmore .= '</div>';
2100 
2101  $nbintab++;
2102  }
2103  $displaytab = $i;
2104  }
2105  if ($popuptab) {
2106  $outmore .= '</div>';
2107  }
2108 
2109  if ($popuptab) { // If there is some tabs not shown
2110  $left = ($langs->trans("DIRECTION") == 'rtl' ? 'right' : 'left');
2111  $right = ($langs->trans("DIRECTION") == 'rtl' ? 'left' : 'right');
2112  $widthofpopup = 200;
2113 
2114  $tabsname = $moretabssuffix;
2115  if (empty($tabsname)) {
2116  $tabsname = str_replace("@", "", $picto);
2117  }
2118  $out .= '<div id="moretabs'.$tabsname.'" class="inline-block tabsElem valignmiddle">';
2119  $out .= '<div class="tab"><a href="#" class="tab moretab inline-block tabunactive"><span class="hideonsmartphone">'.$langs->trans("More").'</span>... ('.$nbintab.')</a></div>'; // Do not use "reposition" class in the "More".
2120  $out .= '<div id="moretabsList'.$tabsname.'" style="width: '.$widthofpopup.'px; position: absolute; '.$left.': -999em; text-align: '.$left.'; margin:0px; padding:2px; z-index:10;">';
2121  $out .= $outmore;
2122  $out .= '</div>';
2123  $out .= '<div></div>';
2124  $out .= "</div>\n";
2125 
2126  $out .= '<script nonce="'.getNonce().'">';
2127  $out .= "$('#moretabs".$tabsname."').mouseenter( function() {
2128  var x = this.offsetLeft, y = this.offsetTop;
2129  console.log('mouseenter ".$left." x='+x+' y='+y+' window.innerWidth='+window.innerWidth);
2130  if ((window.innerWidth - x) < ".($widthofpopup + 10).") {
2131  $('#moretabsList".$tabsname."').css('".$right."','8px');
2132  }
2133  $('#moretabsList".$tabsname."').css('".$left."','auto');
2134  });
2135  ";
2136  $out .= "$('#moretabs".$tabsname."').mouseleave( function() { console.log('mouseleave ".$left."'); $('#moretabsList".$tabsname."').css('".$left."','-999em');});";
2137  $out .= "</script>";
2138  }
2139 
2140  if ((!empty($title) && $showtitle) || $morehtmlright || !empty($links)) {
2141  $out .= "</div>\n";
2142  }
2143 
2144  if (!$notab || $notab == -1 || $notab == -2 || $notab == -3) {
2145  $out .= "\n".'<div id="dragDropAreaTabBar" class="tabBar'.($notab == -1 ? '' : ($notab == -2 ? ' tabBarNoTop' : (($notab == -3 ? ' noborderbottom' : '').' tabBarWithBottom'))).'">'."\n";
2146  }
2147  if (!empty($dragdropfile)) {
2148  $out .= dragAndDropFileUpload("dragDropAreaTabBar");
2149  }
2150  $parameters = array('tabname' => $active, 'out' => $out);
2151  $reshook = $hookmanager->executeHooks('printTabsHead', $parameters); // This hook usage is called just before output the head of tabs. Take also a look at "completeTabsHead"
2152  if ($reshook > 0) {
2153  $out = $hookmanager->resPrint;
2154  }
2155 
2156  return $out;
2157 }
2158 
2166 function dol_fiche_end($notab = 0)
2167 {
2168  print dol_get_fiche_end($notab);
2169 }
2170 
2177 function dol_get_fiche_end($notab = 0)
2178 {
2179  if (!$notab || $notab == -1) {
2180  return "\n</div>\n";
2181  } else {
2182  return '';
2183  }
2184 }
2185 
2205 function dol_banner_tab($object, $paramid, $morehtml = '', $shownav = 1, $fieldid = 'rowid', $fieldref = 'ref', $morehtmlref = '', $moreparam = '', $nodbprefix = 0, $morehtmlleft = '', $morehtmlstatus = '', $onlybanner = 0, $morehtmlright = '')
2206 {
2207  global $conf, $form, $user, $langs, $hookmanager, $action;
2208 
2209  $error = 0;
2210 
2211  $maxvisiblephotos = 1;
2212  $showimage = 1;
2213  $entity = (empty($object->entity) ? $conf->entity : $object->entity);
2214  $showbarcode = empty($conf->barcode->enabled) ? 0 : (empty($object->barcode) ? 0 : 1);
2215  if (!empty($conf->global->MAIN_USE_ADVANCED_PERMS) && empty($user->rights->barcode->lire_advance)) {
2216  $showbarcode = 0;
2217  }
2218  $modulepart = 'unknown';
2219 
2220  if ($object->element == 'societe' || $object->element == 'contact' || $object->element == 'product' || $object->element == 'ticket') {
2221  $modulepart = $object->element;
2222  } elseif ($object->element == 'member') {
2223  $modulepart = 'memberphoto';
2224  } elseif ($object->element == 'user') {
2225  $modulepart = 'userphoto';
2226  }
2227 
2228  if (class_exists("Imagick")) {
2229  if ($object->element == 'expensereport' || $object->element == 'propal' || $object->element == 'commande' || $object->element == 'facture' || $object->element == 'supplier_proposal') {
2230  $modulepart = $object->element;
2231  } elseif ($object->element == 'fichinter') {
2232  $modulepart = 'ficheinter';
2233  } elseif ($object->element == 'contrat') {
2234  $modulepart = 'contract';
2235  } elseif ($object->element == 'order_supplier') {
2236  $modulepart = 'supplier_order';
2237  } elseif ($object->element == 'invoice_supplier') {
2238  $modulepart = 'supplier_invoice';
2239  }
2240  }
2241 
2242  if ($object->element == 'product') {
2243  $width = 80;
2244  $cssclass = 'photowithmargin photoref';
2245  $showimage = $object->is_photo_available($conf->product->multidir_output[$entity]);
2246  $maxvisiblephotos = (isset($conf->global->PRODUCT_MAX_VISIBLE_PHOTO) ? $conf->global->PRODUCT_MAX_VISIBLE_PHOTO : 5);
2247  if ($conf->browser->layout == 'phone') {
2248  $maxvisiblephotos = 1;
2249  }
2250  if ($showimage) {
2251  $morehtmlleft .= '<div class="floatleft inline-block valignmiddle divphotoref">'.$object->show_photos('product', $conf->product->multidir_output[$entity], 'small', $maxvisiblephotos, 0, 0, 0, 0, $width, 0, '').'</div>';
2252  } else {
2253  if (!empty($conf->global->PRODUCT_NODISPLAYIFNOPHOTO)) {
2254  $nophoto = '';
2255  $morehtmlleft .= '<div class="floatleft inline-block valignmiddle divphotoref"></div>';
2256  } else { // Show no photo link
2257  $nophoto = '/public/theme/common/nophoto.png';
2258  $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>';
2259  }
2260  }
2261  } elseif ($object->element == 'ticket') {
2262  $width = 80;
2263  $cssclass = 'photoref';
2264  $showimage = $object->is_photo_available($conf->ticket->multidir_output[$entity].'/'.$object->ref);
2265  $maxvisiblephotos = (isset($conf->global->TICKET_MAX_VISIBLE_PHOTO) ? $conf->global->TICKET_MAX_VISIBLE_PHOTO : 2);
2266  if ($conf->browser->layout == 'phone') {
2267  $maxvisiblephotos = 1;
2268  }
2269 
2270  if ($showimage) {
2271  $showphoto = $object->show_photos('ticket', $conf->ticket->multidir_output[$entity], 'small', $maxvisiblephotos, 0, 0, 0, $width, 0);
2272  if ($object->nbphoto > 0) {
2273  $morehtmlleft .= '<div class="floatleft inline-block valignmiddle divphotoref">'.$showphoto.'</div>';
2274  } else {
2275  $showimage = 0;
2276  }
2277  }
2278  if (!$showimage) {
2279  if (!empty($conf->global->TICKET_NODISPLAYIFNOPHOTO)) {
2280  $nophoto = '';
2281  $morehtmlleft .= '<div class="floatleft inline-block valignmiddle divphotoref"></div>';
2282  } else { // Show no photo link
2283  $nophoto = img_picto('No photo', 'object_ticket');
2284  $morehtmlleft .= '<!-- No photo to show -->';
2285  $morehtmlleft .= '<div class="floatleft inline-block valignmiddle divphotoref"><div class="photoref">';
2286  $morehtmlleft .= $nophoto;
2287  $morehtmlleft .= '</div></div>';
2288  }
2289  }
2290  } else {
2291  if ($showimage) {
2292  if ($modulepart != 'unknown') {
2293  $phototoshow = '';
2294  // Check if a preview file is available
2295  if (in_array($modulepart, array('propal', 'commande', 'facture', 'ficheinter', 'contract', 'supplier_order', 'supplier_proposal', 'supplier_invoice', 'expensereport')) && class_exists("Imagick")) {
2296  $objectref = dol_sanitizeFileName($object->ref);
2297  $dir_output = (empty($conf->$modulepart->multidir_output[$entity]) ? $conf->$modulepart->dir_output : $conf->$modulepart->multidir_output[$entity])."/";
2298  if (in_array($modulepart, array('invoice_supplier', 'supplier_invoice'))) {
2299  $subdir = get_exdir($object->id, 2, 0, 1, $object, $modulepart);
2300  $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
2301  } else {
2302  $subdir = get_exdir($object->id, 0, 0, 1, $object, $modulepart);
2303  }
2304  if (empty($subdir)) {
2305  $subdir = 'errorgettingsubdirofobject'; // Protection to avoid to return empty path
2306  }
2307 
2308  $filepath = $dir_output.$subdir."/";
2309 
2310  $filepdf = $filepath.$objectref.".pdf";
2311  $relativepath = $subdir.'/'.$objectref.'.pdf';
2312 
2313  // Define path to preview pdf file (preview precompiled "file.ext" are "file.ext_preview.png")
2314  $fileimage = $filepdf.'_preview.png';
2315  $relativepathimage = $relativepath.'_preview.png';
2316 
2317  $pdfexists = file_exists($filepdf);
2318 
2319  // If PDF file exists
2320  if ($pdfexists) {
2321  // Conversion du PDF en image png si fichier png non existant
2322  if (!file_exists($fileimage) || (filemtime($fileimage) < filemtime($filepdf))) {
2323  if (empty($conf->global->MAIN_DISABLE_PDF_THUMBS)) { // If you experience trouble with pdf thumb generation and imagick, you can disable here.
2324  include_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
2325  $ret = dol_convert_file($filepdf, 'png', $fileimage, '0'); // Convert first page of PDF into a file _preview.png
2326  if ($ret < 0) {
2327  $error++;
2328  }
2329  }
2330  }
2331  }
2332 
2333  if ($pdfexists && !$error) {
2334  $heightforphotref = 80;
2335  if (!empty($conf->dol_optimize_smallscreen)) {
2336  $heightforphotref = 60;
2337  }
2338  // If the preview file is found
2339  if (file_exists($fileimage)) {
2340  $phototoshow = '<div class="photoref">';
2341  $phototoshow .= '<img height="'.$heightforphotref.'" class="photo photowithborder" src="'.DOL_URL_ROOT.'/viewimage.php?modulepart=apercu'.$modulepart.'&amp;file='.urlencode($relativepathimage).'">';
2342  $phototoshow .= '</div>';
2343  }
2344  }
2345  } elseif (!$phototoshow) { // example if modulepart = 'societe' or 'photo'
2346  $phototoshow .= $form->showphoto($modulepart, $object, 0, 0, 0, 'photowithmargin photoref', 'small', 1, 0, $maxvisiblephotos);
2347  }
2348 
2349  if ($phototoshow) {
2350  $morehtmlleft .= '<div class="floatleft inline-block valignmiddle divphotoref">';
2351  $morehtmlleft .= $phototoshow;
2352  $morehtmlleft .= '</div>';
2353  }
2354  }
2355 
2356  if (empty($phototoshow)) { // Show No photo link (picto of object)
2357  if ($object->element == 'action') {
2358  $width = 80;
2359  $cssclass = 'photorefcenter';
2360  $nophoto = img_picto('No photo', 'title_agenda');
2361  } else {
2362  $width = 14;
2363  $cssclass = 'photorefcenter';
2364  $picto = $object->picto;
2365  $prefix = 'object_';
2366  if ($object->element == 'project' && !$object->public) {
2367  $picto = 'project'; // instead of projectpub
2368  }
2369  if (strpos($picto, 'fontawesome_') !== false) {
2370  $prefix = '';
2371  }
2372  $nophoto = img_picto('No photo', $prefix.$picto);
2373  }
2374  $morehtmlleft .= '<!-- No photo to show -->';
2375  $morehtmlleft .= '<div class="floatleft inline-block valignmiddle divphotoref"><div class="photoref">';
2376  $morehtmlleft .= $nophoto;
2377  $morehtmlleft .= '</div></div>';
2378  }
2379  }
2380  }
2381 
2382  // Show barcode
2383  if ($showbarcode) {
2384  $morehtmlleft .= '<div class="floatleft inline-block valignmiddle divphotoref">'.$form->showbarcode($object, 100, 'photoref valignmiddle').'</div>';
2385  }
2386 
2387  if ($object->element == 'societe') {
2388  if (!empty($conf->use_javascript_ajax) && $user->hasRight('societe', 'creer') && !empty($conf->global->MAIN_DIRECT_STATUS_UPDATE)) {
2389  $morehtmlstatus .= ajax_object_onoff($object, 'status', 'status', 'InActivity', 'ActivityCeased');
2390  } else {
2391  $morehtmlstatus .= $object->getLibStatut(6);
2392  }
2393  } elseif ($object->element == 'product') {
2394  //$morehtmlstatus.=$langs->trans("Status").' ('.$langs->trans("Sell").') ';
2395  if (!empty($conf->use_javascript_ajax) && $user->hasRight('produit', 'creer') && !empty($conf->global->MAIN_DIRECT_STATUS_UPDATE)) {
2396  $morehtmlstatus .= ajax_object_onoff($object, 'status', 'tosell', 'ProductStatusOnSell', 'ProductStatusNotOnSell');
2397  } else {
2398  $morehtmlstatus .= '<span class="statusrefsell">'.$object->getLibStatut(6, 0).'</span>';
2399  }
2400  $morehtmlstatus .= ' &nbsp; ';
2401  //$morehtmlstatus.=$langs->trans("Status").' ('.$langs->trans("Buy").') ';
2402  if (!empty($conf->use_javascript_ajax) && $user->hasRight('produit', 'creer') && !empty($conf->global->MAIN_DIRECT_STATUS_UPDATE)) {
2403  $morehtmlstatus .= ajax_object_onoff($object, 'status_buy', 'tobuy', 'ProductStatusOnBuy', 'ProductStatusNotOnBuy');
2404  } else {
2405  $morehtmlstatus .= '<span class="statusrefbuy">'.$object->getLibStatut(6, 1).'</span>';
2406  }
2407  } elseif (in_array($object->element, array('facture', 'invoice', 'invoice_supplier', 'chargesociales', 'loan', 'tva', 'salary'))) {
2408  $tmptxt = $object->getLibStatut(6, $object->totalpaid);
2409  if (empty($tmptxt) || $tmptxt == $object->getLibStatut(3)) {
2410  $tmptxt = $object->getLibStatut(5, $object->totalpaid);
2411  }
2412  $morehtmlstatus .= $tmptxt;
2413  } elseif ($object->element == 'contrat' || $object->element == 'contract') {
2414  if ($object->statut == 0) {
2415  $morehtmlstatus .= $object->getLibStatut(5);
2416  } else {
2417  $morehtmlstatus .= $object->getLibStatut(4);
2418  }
2419  } elseif ($object->element == 'facturerec') {
2420  if ($object->frequency == 0) {
2421  $morehtmlstatus .= $object->getLibStatut(2);
2422  } else {
2423  $morehtmlstatus .= $object->getLibStatut(5);
2424  }
2425  } elseif ($object->element == 'project_task') {
2426  $object->fk_statut = 1;
2427  if ($object->progress > 0) {
2428  $object->fk_statut = 2;
2429  }
2430  if ($object->progress >= 100) {
2431  $object->fk_statut = 3;
2432  }
2433  $tmptxt = $object->getLibStatut(5);
2434  $morehtmlstatus .= $tmptxt; // No status on task
2435  } elseif (method_exists($object, 'getLibStatut')) { // Generic case
2436  $tmptxt = $object->getLibStatut(6);
2437  if (empty($tmptxt) || $tmptxt == $object->getLibStatut(3)) {
2438  $tmptxt = $object->getLibStatut(5);
2439  }
2440  $morehtmlstatus .= $tmptxt;
2441  }
2442 
2443  // Add if object was dispatched "into accountancy"
2444  if (isModEnabled('accounting') && in_array($object->element, array('bank', 'paiementcharge', 'facture', 'invoice', 'invoice_supplier', 'expensereport', 'payment_various'))) {
2445  // Note: For 'chargesociales', 'salaries'... this is the payments that are dispatched (so element = 'bank')
2446  if (method_exists($object, 'getVentilExportCompta')) {
2447  $accounted = $object->getVentilExportCompta();
2448  $langs->load("accountancy");
2449  $morehtmlstatus .= '</div><div class="statusref statusrefbis"><span class="opacitymedium">'.($accounted > 0 ? $langs->trans("Accounted") : $langs->trans("NotYetAccounted")).'</span>';
2450  }
2451  }
2452 
2453  // Add alias for thirdparty
2454  if (!empty($object->name_alias)) {
2455  $morehtmlref .= '<div class="refidno opacitymedium">'.dol_escape_htmltag($object->name_alias).'</div>';
2456  }
2457 
2458  // Add label
2459  if (in_array($object->element, array('product', 'bank_account', 'project_task'))) {
2460  if (!empty($object->label)) {
2461  $morehtmlref .= '<div class="refidno opacitymedium">'.$object->label.'</div>';
2462  }
2463  }
2464 
2465  // Show address and email
2466  if (method_exists($object, 'getBannerAddress') && !in_array($object->element, array('product', 'bookmark', 'ecm_directories', 'ecm_files'))) {
2467  $moreaddress = $object->getBannerAddress('refaddress', $object);
2468  if ($moreaddress) {
2469  $morehtmlref .= '<div class="refidno refaddress">';
2470  $morehtmlref .= $moreaddress;
2471  $morehtmlref .= '</div>';
2472  }
2473  }
2474  if (!empty($conf->global->MAIN_SHOW_TECHNICAL_ID) && ($conf->global->MAIN_SHOW_TECHNICAL_ID == '1' || preg_match('/'.preg_quote($object->element, '/').'/i', $conf->global->MAIN_SHOW_TECHNICAL_ID)) && !empty($object->id)) {
2475  $morehtmlref .= '<div style="clear: both;"></div>';
2476  $morehtmlref .= '<div class="refidno opacitymedium">';
2477  $morehtmlref .= $langs->trans("TechnicalID").': '.((int) $object->id);
2478  $morehtmlref .= '</div>';
2479  }
2480 
2481  $parameters=array('morehtmlref'=>$morehtmlref);
2482  $reshook = $hookmanager->executeHooks('formDolBanner', $parameters, $object, $action);
2483  if ($reshook < 0) {
2484  setEventMessages($hookmanager->error, $hookmanager->errors, 'errors');
2485  } elseif (empty($reshook)) {
2486  $morehtmlref .= $hookmanager->resPrint;
2487  } elseif ($reshook > 0) {
2488  $morehtmlref = $hookmanager->resPrint;
2489  }
2490 
2491 
2492  print '<div class="'.($onlybanner ? 'arearefnobottom ' : 'arearef ').'heightref valignmiddle centpercent">';
2493  print $form->showrefnav($object, $paramid, $morehtml, $shownav, $fieldid, $fieldref, $morehtmlref, $moreparam, $nodbprefix, $morehtmlleft, $morehtmlstatus, $morehtmlright);
2494  print '</div>';
2495  print '<div class="underrefbanner clearboth"></div>';
2496 }
2497 
2507 function fieldLabel($langkey, $fieldkey, $fieldrequired = 0)
2508 {
2509  global $langs;
2510  $ret = '';
2511  if ($fieldrequired) {
2512  $ret .= '<span class="fieldrequired">';
2513  }
2514  $ret .= '<label for="'.$fieldkey.'">';
2515  $ret .= $langs->trans($langkey);
2516  $ret .= '</label>';
2517  if ($fieldrequired) {
2518  $ret .= '</span>';
2519  }
2520  return $ret;
2521 }
2522 
2530 function dol_bc($var, $moreclass = '')
2531 {
2532  global $bc;
2533  $ret = ' '.$bc[$var];
2534  if ($moreclass) {
2535  $ret = preg_replace('/class=\"/', 'class="'.$moreclass.' ', $ret);
2536  }
2537  return $ret;
2538 }
2539 
2553 function dol_format_address($object, $withcountry = 0, $sep = "\n", $outputlangs = '', $mode = 0, $extralangcode = '')
2554 {
2555  global $conf, $langs, $hookmanager;
2556 
2557  $ret = '';
2558  $countriesusingstate = array('AU', 'CA', 'US', 'IN', 'GB', 'ES', 'UK', 'TR', 'CN'); // See also MAIN_FORCE_STATE_INTO_ADDRESS
2559 
2560  // See format of addresses on https://en.wikipedia.org/wiki/Address
2561  // Address
2562  if (empty($mode)) {
2563  $ret .= ($extralangcode ? $object->array_languages['address'][$extralangcode] : (empty($object->address) ? '' : preg_replace('/[\n\r]/', $sep, $object->address)));
2564  }
2565  // Zip/Town/State
2566  if (isset($object->country_code) && in_array($object->country_code, array('AU', 'CA', 'US', 'CN')) || !empty($conf->global->MAIN_FORCE_STATE_INTO_ADDRESS)) {
2567  // US: title firstname name \n address lines \n town, state, zip \n country
2568  $town = ($extralangcode ? $object->array_languages['town'][$extralangcode] : (empty($object->town) ? '' : $object->town));
2569  $ret .= (($ret && $town) ? $sep : '').$town;
2570 
2571  if (!empty($object->state)) {
2572  $ret .= ($ret ? ($town ? ", " : $sep) : '').$object->state;
2573  }
2574  if (!empty($object->zip)) {
2575  $ret .= ($ret ? (($town || $object->state) ? ", " : $sep) : '').$object->zip;
2576  }
2577  } elseif (isset($object->country_code) && in_array($object->country_code, array('GB', 'UK'))) {
2578  // UK: title firstname name \n address lines \n town state \n zip \n country
2579  $town = ($extralangcode ? $object->array_languages['town'][$extralangcode] : (empty($object->town) ? '' : $object->town));
2580  $ret .= ($ret ? $sep : '').$town;
2581  if (!empty($object->state)) {
2582  $ret .= ($ret ? ", " : '').$object->state;
2583  }
2584  if (!empty($object->zip)) {
2585  $ret .= ($ret ? $sep : '').$object->zip;
2586  }
2587  } elseif (isset($object->country_code) && in_array($object->country_code, array('ES', 'TR'))) {
2588  // ES: title firstname name \n address lines \n zip town \n state \n country
2589  $ret .= ($ret ? $sep : '').$object->zip;
2590  $town = ($extralangcode ? $object->array_languages['town'][$extralangcode] : (empty($object->town) ? '' : $object->town));
2591  $ret .= ($town ? (($object->zip ? ' ' : '').$town) : '');
2592  if (!empty($object->state)) {
2593  $ret .= $sep.$object->state;
2594  }
2595  } elseif (isset($object->country_code) && in_array($object->country_code, array('JP'))) {
2596  // JP: In romaji, title firstname name\n address lines \n [state,] town zip \n country
2597  // See https://www.sljfaq.org/afaq/addresses.html
2598  $town = ($extralangcode ? $object->array_languages['town'][$extralangcode] : (empty($object->town) ? '' : $object->town));
2599  $ret .= ($ret ? $sep : '').($object->state ? $object->state.', ' : '').$town.($object->zip ? ' ' : '').$object->zip;
2600  } elseif (isset($object->country_code) && in_array($object->country_code, array('IT'))) {
2601  // IT: title firstname name\n address lines \n zip town state_code \n country
2602  $ret .= ($ret ? $sep : '').$object->zip;
2603  $town = ($extralangcode ? $object->array_languages['town'][$extralangcode] : (empty($object->town) ? '' : $object->town));
2604  $ret .= ($town ? (($object->zip ? ' ' : '').$town) : '');
2605  $ret .= (empty($object->state_code) ? '' : (' '.$object->state_code));
2606  } else {
2607  // Other: title firstname name \n address lines \n zip town[, state] \n country
2608  $town = ($extralangcode ? $object->array_languages['town'][$extralangcode] : (empty($object->town) ? '' : $object->town));
2609  $ret .= !empty($object->zip) ? (($ret ? $sep : '').$object->zip) : '';
2610  $ret .= ($town ? (($object->zip ? ' ' : ($ret ? $sep : '')).$town) : '');
2611  if (!empty($object->state) && in_array($object->country_code, $countriesusingstate)) {
2612  $ret .= ($ret ? ", " : '').$object->state;
2613  }
2614  }
2615 
2616  if (!is_object($outputlangs)) {
2617  $outputlangs = $langs;
2618  }
2619  if ($withcountry) {
2620  $langs->load("dict");
2621  $ret .= (empty($object->country_code) ? '' : ($ret ? $sep : '').$outputlangs->convToOutputCharset($outputlangs->transnoentitiesnoconv("Country".$object->country_code)));
2622  }
2623  if ($hookmanager) {
2624  $parameters = array('withcountry' => $withcountry, 'sep' => $sep, 'outputlangs' => $outputlangs,'mode' => $mode, 'extralangcode' => $extralangcode);
2625  $reshook = $hookmanager->executeHooks('formatAddress', $parameters, $object);
2626  if ($reshook > 0) {
2627  $ret = '';
2628  }
2629  $ret .= $hookmanager->resPrint;
2630  }
2631 
2632  return $ret;
2633 }
2634 
2635 
2636 
2645 function dol_strftime($fmt, $ts = false, $is_gmt = false)
2646 {
2647  if ((abs($ts) <= 0x7FFFFFFF)) { // check if number in 32-bit signed range
2648  return ($is_gmt) ? @gmstrftime($fmt, $ts) : @strftime($fmt, $ts);
2649  } else {
2650  return 'Error date into a not supported range';
2651  }
2652 }
2653 
2675 function dol_print_date($time, $format = '', $tzoutput = 'auto', $outputlangs = '', $encodetooutput = false)
2676 {
2677  global $conf, $langs;
2678 
2679  // If date undefined or "", we return ""
2680  if (dol_strlen($time) == 0) {
2681  return ''; // $time=0 allowed (it means 01/01/1970 00:00:00)
2682  }
2683 
2684  if ($tzoutput === 'auto') {
2685  $tzoutput = (empty($conf) ? 'tzserver' : (isset($conf->tzuserinputkey) ? $conf->tzuserinputkey : 'tzserver'));
2686  }
2687 
2688  // Clean parameters
2689  $to_gmt = false;
2690  $offsettz = $offsetdst = 0;
2691  if ($tzoutput) {
2692  $to_gmt = true; // For backward compatibility
2693  if (is_string($tzoutput)) {
2694  if ($tzoutput == 'tzserver') {
2695  $to_gmt = false;
2696  $offsettzstring = @date_default_timezone_get(); // Example 'Europe/Berlin' or 'Indian/Reunion'
2697  $offsettz = 0; // Timezone offset with server timezone (because to_gmt is false), so 0
2698  $offsetdst = 0; // Dst offset with server timezone (because to_gmt is false), so 0
2699  } elseif ($tzoutput == 'tzuser' || $tzoutput == 'tzuserrel') {
2700  $to_gmt = true;
2701  $offsettzstring = (empty($_SESSION['dol_tz_string']) ? 'UTC' : $_SESSION['dol_tz_string']); // Example 'Europe/Berlin' or 'Indian/Reunion'
2702 
2703  if (class_exists('DateTimeZone')) {
2704  $user_date_tz = new DateTimeZone($offsettzstring);
2705  $user_dt = new DateTime();
2706  $user_dt->setTimezone($user_date_tz);
2707  $user_dt->setTimestamp($tzoutput == 'tzuser' ? dol_now() : (int) $time);
2708  $offsettz = $user_dt->getOffset(); // should include dst ?
2709  } else { // with old method (The 'tzuser' was processed like the 'tzuserrel')
2710  $offsettz = (empty($_SESSION['dol_tz']) ? 0 : $_SESSION['dol_tz']) * 60 * 60; // Will not be used anymore
2711  $offsetdst = (empty($_SESSION['dol_dst']) ? 0 : $_SESSION['dol_dst']) * 60 * 60; // Will not be used anymore
2712  }
2713  }
2714  }
2715  }
2716  if (!is_object($outputlangs)) {
2717  $outputlangs = $langs;
2718  }
2719  if (!$format) {
2720  $format = 'daytextshort';
2721  }
2722 
2723  // Do we have to reduce the length of date (year on 2 chars) to save space.
2724  // Note: dayinputnoreduce is same than day but no reduction of year length will be done
2725  $reduceformat = (!empty($conf->dol_optimize_smallscreen) && in_array($format, array('day', 'dayhour'))) ? 1 : 0; // Test on original $format param.
2726  $format = preg_replace('/inputnoreduce/', '', $format); // so format 'dayinputnoreduce' is processed like day
2727  $formatwithoutreduce = preg_replace('/reduceformat/', '', $format);
2728  if ($formatwithoutreduce != $format) {
2729  $format = $formatwithoutreduce;
2730  $reduceformat = 1;
2731  } // so format 'dayreduceformat' is processed like day
2732 
2733  // Change predefined format into computer format. If found translation in lang file we use it, otherwise we use default.
2734  // TODO Add format daysmallyear and dayhoursmallyear
2735  if ($format == 'day') {
2736  $format = ($outputlangs->trans("FormatDateShort") != "FormatDateShort" ? $outputlangs->trans("FormatDateShort") : $conf->format_date_short);
2737  } elseif ($format == 'hour') {
2738  $format = ($outputlangs->trans("FormatHourShort") != "FormatHourShort" ? $outputlangs->trans("FormatHourShort") : $conf->format_hour_short);
2739  } elseif ($format == 'hourduration') {
2740  $format = ($outputlangs->trans("FormatHourShortDuration") != "FormatHourShortDuration" ? $outputlangs->trans("FormatHourShortDuration") : $conf->format_hour_short_duration);
2741  } elseif ($format == 'daytext') {
2742  $format = ($outputlangs->trans("FormatDateText") != "FormatDateText" ? $outputlangs->trans("FormatDateText") : $conf->format_date_text);
2743  } elseif ($format == 'daytextshort') {
2744  $format = ($outputlangs->trans("FormatDateTextShort") != "FormatDateTextShort" ? $outputlangs->trans("FormatDateTextShort") : $conf->format_date_text_short);
2745  } elseif ($format == 'dayhour') {
2746  $format = ($outputlangs->trans("FormatDateHourShort") != "FormatDateHourShort" ? $outputlangs->trans("FormatDateHourShort") : $conf->format_date_hour_short);
2747  } elseif ($format == 'dayhoursec') {
2748  $format = ($outputlangs->trans("FormatDateHourSecShort") != "FormatDateHourSecShort" ? $outputlangs->trans("FormatDateHourSecShort") : $conf->format_date_hour_sec_short);
2749  } elseif ($format == 'dayhourtext') {
2750  $format = ($outputlangs->trans("FormatDateHourText") != "FormatDateHourText" ? $outputlangs->trans("FormatDateHourText") : $conf->format_date_hour_text);
2751  } elseif ($format == 'dayhourtextshort') {
2752  $format = ($outputlangs->trans("FormatDateHourTextShort") != "FormatDateHourTextShort" ? $outputlangs->trans("FormatDateHourTextShort") : $conf->format_date_hour_text_short);
2753  } elseif ($format == 'dayhourlog') {
2754  // Format not sensitive to language
2755  $format = '%Y%m%d%H%M%S';
2756  } elseif ($format == 'dayhourlogsmall') {
2757  // Format not sensitive to language
2758  $format = '%y%m%d%H%M';
2759  } elseif ($format == 'dayhourldap') {
2760  $format = '%Y%m%d%H%M%SZ';
2761  } elseif ($format == 'dayhourxcard') {
2762  $format = '%Y%m%dT%H%M%SZ';
2763  } elseif ($format == 'dayxcard') {
2764  $format = '%Y%m%d';
2765  } elseif ($format == 'dayrfc') {
2766  $format = '%Y-%m-%d'; // DATE_RFC3339
2767  } elseif ($format == 'dayhourrfc') {
2768  $format = '%Y-%m-%dT%H:%M:%SZ'; // DATETIME RFC3339
2769  } elseif ($format == 'standard') {
2770  $format = '%Y-%m-%d %H:%M:%S';
2771  }
2772 
2773  if ($reduceformat) {
2774  $format = str_replace('%Y', '%y', $format);
2775  $format = str_replace('yyyy', 'yy', $format);
2776  }
2777 
2778  // Clean format
2779  if (preg_match('/%b/i', $format)) { // There is some text to translate
2780  // We inhibate translation to text made by strftime functions. We will use trans instead later.
2781  $format = str_replace('%b', '__b__', $format);
2782  $format = str_replace('%B', '__B__', $format);
2783  }
2784  if (preg_match('/%a/i', $format)) { // There is some text to translate
2785  // We inhibate translation to text made by strftime functions. We will use trans instead later.
2786  $format = str_replace('%a', '__a__', $format);
2787  $format = str_replace('%A', '__A__', $format);
2788  }
2789 
2790  // Analyze date
2791  $reg = array();
2792  if (preg_match('/^([0-9][0-9][0-9][0-9])([0-9][0-9])([0-9][0-9])([0-9][0-9])([0-9][0-9])([0-9][0-9])$/i', $time, $reg)) { // Deprecated. Ex: 1970-01-01, 1970-01-01 01:00:00, 19700101010000
2793  dol_print_error('', "Functions.lib::dol_print_date function called with a bad value from page ".$_SERVER["PHP_SELF"]);
2794  return '';
2795  } elseif (preg_match('/^([0-9]+)\-([0-9]+)\-([0-9]+) ?([0-9]+)?:?([0-9]+)?:?([0-9]+)?/i', $time, $reg)) { // Still available to solve problems in extrafields of type date
2796  // This part of code should not be used anymore.
2797  dol_syslog("Functions.lib::dol_print_date function called with a bad value from page ".$_SERVER["PHP_SELF"], LOG_WARNING);
2798  //if (function_exists('debug_print_backtrace')) debug_print_backtrace();
2799  // Date has format 'YYYY-MM-DD' or 'YYYY-MM-DD HH:MM:SS'
2800  $syear = (!empty($reg[1]) ? $reg[1] : '');
2801  $smonth = (!empty($reg[2]) ? $reg[2] : '');
2802  $sday = (!empty($reg[3]) ? $reg[3] : '');
2803  $shour = (!empty($reg[4]) ? $reg[4] : '');
2804  $smin = (!empty($reg[5]) ? $reg[5] : '');
2805  $ssec = (!empty($reg[6]) ? $reg[6] : '');
2806 
2807  $time = dol_mktime($shour, $smin, $ssec, $smonth, $sday, $syear, true);
2808 
2809  if ($to_gmt) {
2810  $tzo = new DateTimeZone('UTC'); // when to_gmt is true, base for offsettz and offsetdst (so timetouse) is UTC
2811  } else {
2812  $tzo = new DateTimeZone(date_default_timezone_get()); // when to_gmt is false, base for offsettz and offsetdst (so timetouse) is PHP server
2813  }
2814  $dtts = new DateTime();
2815  $dtts->setTimestamp($time);
2816  $dtts->setTimezone($tzo);
2817  $newformat = str_replace(
2818  array('%Y', '%y', '%m', '%d', '%H', '%I', '%M', '%S', '%p', 'T', 'Z', '__a__', '__A__', '__b__', '__B__'),
2819  array('Y', 'y', 'm', 'd', 'H', 'h', 'i', 's', 'A', '__£__', '__$__', '__{__', '__}__', '__[__', '__]__'),
2820  $format);
2821  $ret = $dtts->format($newformat);
2822  $ret = str_replace(
2823  array('__£__', '__$__', '__{__', '__}__', '__[__', '__]__'),
2824  array('T', 'Z', '__a__', '__A__', '__b__', '__B__'),
2825  $ret
2826  );
2827  } else {
2828  // Date is a timestamps
2829  if ($time < 100000000000) { // Protection against bad date values
2830  $timetouse = $time + $offsettz + $offsetdst; // TODO We could be able to disable use of offsettz and offsetdst to use only offsettzstring.
2831 
2832  if ($to_gmt) {
2833  $tzo = new DateTimeZone('UTC'); // when to_gmt is true, base for offsettz and offsetdst (so timetouse) is UTC
2834  } else {
2835  $tzo = new DateTimeZone(date_default_timezone_get()); // when to_gmt is false, base for offsettz and offsetdst (so timetouse) is PHP server
2836  }
2837  $dtts = new DateTime();
2838  $dtts->setTimestamp($timetouse);
2839  $dtts->setTimezone($tzo);
2840  $newformat = str_replace(
2841  array('%Y', '%y', '%m', '%d', '%H', '%I', '%M', '%S', '%p', '%w', 'T', 'Z', '__a__', '__A__', '__b__', '__B__'),
2842  array('Y', 'y', 'm', 'd', 'H', 'h', 'i', 's', 'A', 'w', '__£__', '__$__', '__{__', '__}__', '__[__', '__]__'),
2843  $format);
2844  $ret = $dtts->format($newformat);
2845  $ret = str_replace(
2846  array('__£__', '__$__', '__{__', '__}__', '__[__', '__]__'),
2847  array('T', 'Z', '__a__', '__A__', '__b__', '__B__'),
2848  $ret
2849  );
2850  //var_dump($ret);exit;
2851  } else {
2852  $ret = 'Bad value '.$time.' for date';
2853  }
2854  }
2855 
2856  if (preg_match('/__b__/i', $format)) {
2857  $timetouse = $time + $offsettz + $offsetdst; // TODO We could be able to disable use of offsettz and offsetdst to use only offsettzstring.
2858 
2859  if ($to_gmt) {
2860  $tzo = new DateTimeZone('UTC'); // when to_gmt is true, base for offsettz and offsetdst (so timetouse) is UTC
2861  } else {
2862  $tzo = new DateTimeZone(date_default_timezone_get()); // when to_gmt is false, base for offsettz and offsetdst (so timetouse) is PHP server
2863  }
2864  $dtts = new DateTime();
2865  $dtts->setTimestamp($timetouse);
2866  $dtts->setTimezone($tzo);
2867  $month = $dtts->format("m");
2868  $month = sprintf("%02d", $month); // $month may be return with format '06' on some installation and '6' on other, so we force it to '06'.
2869  if ($encodetooutput) {
2870  $monthtext = $outputlangs->transnoentities('Month'.$month);
2871  $monthtextshort = $outputlangs->transnoentities('MonthShort'.$month);
2872  } else {
2873  $monthtext = $outputlangs->transnoentitiesnoconv('Month'.$month);
2874  $monthtextshort = $outputlangs->transnoentitiesnoconv('MonthShort'.$month);
2875  }
2876  //print 'monthtext='.$monthtext.' monthtextshort='.$monthtextshort;
2877  $ret = str_replace('__b__', $monthtextshort, $ret);
2878  $ret = str_replace('__B__', $monthtext, $ret);
2879  //print 'x'.$outputlangs->charset_output.'-'.$ret.'x';
2880  //return $ret;
2881  }
2882  if (preg_match('/__a__/i', $format)) {
2883  //print "time=$time offsettz=$offsettz offsetdst=$offsetdst offsettzstring=$offsettzstring";
2884  $timetouse = $time + $offsettz + $offsetdst; // TODO Replace this with function Date PHP. We also should not use anymore offsettz and offsetdst but only offsettzstring.
2885 
2886  if ($to_gmt) {
2887  $tzo = new DateTimeZone('UTC');
2888  } else {
2889  $tzo = new DateTimeZone(date_default_timezone_get());
2890  }
2891  $dtts = new DateTime();
2892  $dtts->setTimestamp($timetouse);
2893  $dtts->setTimezone($tzo);
2894  $w = $dtts->format("w");
2895  $dayweek = $outputlangs->transnoentitiesnoconv('Day'.$w);
2896 
2897  $ret = str_replace('__A__', $dayweek, $ret);
2898  $ret = str_replace('__a__', dol_substr($dayweek, 0, 3), $ret);
2899  }
2900 
2901  return $ret;
2902 }
2903 
2904 
2925 function dol_getdate($timestamp, $fast = false, $forcetimezone = '')
2926 {
2927  $datetimeobj = new DateTime();
2928  $datetimeobj->setTimestamp($timestamp); // Use local PHP server timezone
2929  if ($forcetimezone) {
2930  $datetimeobj->setTimezone(new DateTimeZone($forcetimezone == 'gmt' ? 'UTC' : $forcetimezone)); // (add timezone relative to the date entered)
2931  }
2932  $arrayinfo = array(
2933  'year'=>((int) date_format($datetimeobj, 'Y')),
2934  'mon'=>((int) date_format($datetimeobj, 'm')),
2935  'mday'=>((int) date_format($datetimeobj, 'd')),
2936  'wday'=>((int) date_format($datetimeobj, 'w')),
2937  'yday'=>((int) date_format($datetimeobj, 'z')),
2938  'hours'=>((int) date_format($datetimeobj, 'H')),
2939  'minutes'=>((int) date_format($datetimeobj, 'i')),
2940  'seconds'=>((int) date_format($datetimeobj, 's')),
2941  '0'=>$timestamp
2942  );
2943 
2944  return $arrayinfo;
2945 }
2946 
2968 function dol_mktime($hour, $minute, $second, $month, $day, $year, $gm = 'auto', $check = 1)
2969 {
2970  global $conf;
2971  //print "- ".$hour.",".$minute.",".$second.",".$month.",".$day.",".$year.",".$_SERVER["WINDIR"]." -";
2972 
2973  if ($gm === 'auto') {
2974  $gm = (empty($conf) ? 'tzserver' : $conf->tzuserinputkey);
2975  }
2976  //print 'gm:'.$gm.' gm === auto:'.($gm === 'auto').'<br>';exit;
2977 
2978  // Clean parameters
2979  if ($hour == -1 || empty($hour)) {
2980  $hour = 0;
2981  }
2982  if ($minute == -1 || empty($minute)) {
2983  $minute = 0;
2984  }
2985  if ($second == -1 || empty($second)) {
2986  $second = 0;
2987  }
2988 
2989  // Check parameters
2990  if ($check) {
2991  if (!$month || !$day) {
2992  return '';
2993  }
2994  if ($day > 31) {
2995  return '';
2996  }
2997  if ($month > 12) {
2998  return '';
2999  }
3000  if ($hour < 0 || $hour > 24) {
3001  return '';
3002  }
3003  if ($minute < 0 || $minute > 60) {
3004  return '';
3005  }
3006  if ($second < 0 || $second > 60) {
3007  return '';
3008  }
3009  }
3010 
3011  if (empty($gm) || ($gm === 'server' || $gm === 'tzserver')) {
3012  $default_timezone = @date_default_timezone_get(); // Example 'Europe/Berlin'
3013  $localtz = new DateTimeZone($default_timezone);
3014  } elseif ($gm === 'user' || $gm === 'tzuser' || $gm === 'tzuserrel') {
3015  // We use dol_tz_string first because it is more reliable.
3016  $default_timezone = (empty($_SESSION["dol_tz_string"]) ? @date_default_timezone_get() : $_SESSION["dol_tz_string"]); // Example 'Europe/Berlin'
3017  try {
3018  $localtz = new DateTimeZone($default_timezone);
3019  } catch (Exception $e) {
3020  dol_syslog("Warning dol_tz_string contains an invalid value ".$_SESSION["dol_tz_string"], LOG_WARNING);
3021  $default_timezone = @date_default_timezone_get();
3022  }
3023  } elseif (strrpos($gm, "tz,") !== false) {
3024  $timezone = str_replace("tz,", "", $gm); // Example 'tz,Europe/Berlin'
3025  try {
3026  $localtz = new DateTimeZone($timezone);
3027  } catch (Exception $e) {
3028  dol_syslog("Warning passed timezone contains an invalid value ".$timezone, LOG_WARNING);
3029  }
3030  }
3031 
3032  if (empty($localtz)) {
3033  $localtz = new DateTimeZone('UTC');
3034  }
3035  //var_dump($localtz);
3036  //var_dump($year.'-'.$month.'-'.$day.'-'.$hour.'-'.$minute);
3037  $dt = new DateTime('now', $localtz);
3038  $dt->setDate((int) $year, (int) $month, (int) $day);
3039  $dt->setTime((int) $hour, (int) $minute, (int) $second);
3040  $date = $dt->getTimestamp(); // should include daylight saving time
3041  //var_dump($date);
3042  return $date;
3043 }
3044 
3045 
3056 function dol_now($mode = 'auto')
3057 {
3058  $ret = 0;
3059 
3060  if ($mode === 'auto') {
3061  $mode = 'gmt';
3062  }
3063 
3064  if ($mode == 'gmt') {
3065  $ret = time(); // Time for now at greenwich.
3066  } elseif ($mode == 'tzserver') { // Time for now with PHP server timezone added
3067  require_once DOL_DOCUMENT_ROOT.'/core/lib/date.lib.php';
3068  $tzsecond = getServerTimeZoneInt('now'); // Contains tz+dayling saving time
3069  $ret = (int) (dol_now('gmt') + ($tzsecond * 3600));
3070  //} elseif ($mode == 'tzref') {// Time for now with parent company timezone is added
3071  // require_once DOL_DOCUMENT_ROOT.'/core/lib/date.lib.php';
3072  // $tzsecond=getParentCompanyTimeZoneInt(); // Contains tz+dayling saving time
3073  // $ret=dol_now('gmt')+($tzsecond*3600);
3074  //}
3075  } elseif ($mode == 'tzuser' || $mode == 'tzuserrel') {
3076  // Time for now with user timezone added
3077  //print 'time: '.time();
3078  $offsettz = (empty($_SESSION['dol_tz']) ? 0 : $_SESSION['dol_tz']) * 60 * 60;
3079  $offsetdst = (empty($_SESSION['dol_dst']) ? 0 : $_SESSION['dol_dst']) * 60 * 60;
3080  $ret = (int) (dol_now('gmt') + ($offsettz + $offsetdst));
3081  }
3082 
3083  return $ret;
3084 }
3085 
3086 
3095 function dol_print_size($size, $shortvalue = 0, $shortunit = 0)
3096 {
3097  global $conf, $langs;
3098  $level = 1024;
3099 
3100  if (!empty($conf->dol_optimize_smallscreen)) {
3101  $shortunit = 1;
3102  }
3103 
3104  // Set value text
3105  if (empty($shortvalue) || $size < ($level * 10)) {
3106  $ret = $size;
3107  $textunitshort = $langs->trans("b");
3108  $textunitlong = $langs->trans("Bytes");
3109  } else {
3110  $ret = round($size / $level, 0);
3111  $textunitshort = $langs->trans("Kb");
3112  $textunitlong = $langs->trans("KiloBytes");
3113  }
3114  // Use long or short text unit
3115  if (empty($shortunit)) {
3116  $ret .= ' '.$textunitlong;
3117  } else {
3118  $ret .= ' '.$textunitshort;
3119  }
3120 
3121  return $ret;
3122 }
3123 
3134 function dol_print_url($url, $target = '_blank', $max = 32, $withpicto = 0, $morecss = 'float')
3135 {
3136  global $langs;
3137 
3138  if (empty($url)) {
3139  return '';
3140  }
3141 
3142  $link = '<a href="';
3143  if (!preg_match('/^http/i', $url)) {
3144  $link .= 'http://';
3145  }
3146  $link .= $url;
3147  $link .= '"';
3148  if ($target) {
3149  $link .= ' target="'.$target.'"';
3150  }
3151  $link .= '>';
3152  if (!preg_match('/^http/i', $url)) {
3153  $link .= 'http://';
3154  }
3155  $link .= dol_trunc($url, $max);
3156  $link .= '</a>';
3157 
3158  if ($morecss == 'float') {
3159  return '<div class="nospan'.($morecss ? ' '.$morecss : '').'" style="margin-right: 10px">'.($withpicto ?img_picto($langs->trans("Url"), 'globe').' ' : '').$link.'</div>';
3160  } else {
3161  return '<span class="nospan'.($morecss ? ' '.$morecss : '').'" style="margin-right: 10px">'.($withpicto ?img_picto($langs->trans("Url"), 'globe').' ' : '').$link.'</span>';
3162  }
3163 }
3164 
3177 function dol_print_email($email, $cid = 0, $socid = 0, $addlink = 0, $max = 64, $showinvalid = 1, $withpicto = 0)
3178 {
3179  global $conf, $user, $langs, $hookmanager;
3180 
3181  $newemail = dol_escape_htmltag($email);
3182 
3183  if (!empty($conf->global->MAIN_OPTIMIZEFORTEXTBROWSER) && $withpicto) {
3184  $withpicto = 0;
3185  }
3186 
3187  if (empty($email)) {
3188  return '&nbsp;';
3189  }
3190 
3191  if (!empty($addlink)) {
3192  $newemail = '<a style="text-overflow: ellipsis;" href="';
3193  if (!preg_match('/^mailto:/i', $email)) {
3194  $newemail .= 'mailto:';
3195  }
3196  $newemail .= $email;
3197  $newemail .= '">';
3198  $newemail .= dol_trunc($email, $max);
3199  $newemail .= '</a>';
3200  if ($showinvalid && !isValidEmail($email)) {
3201  $langs->load("errors");
3202  $newemail .= img_warning($langs->trans("ErrorBadEMail", $email));
3203  }
3204 
3205  if (($cid || $socid) && isModEnabled('agenda') && $user->hasRight("agenda", "myactions", "create")) {
3206  $type = 'AC_EMAIL';
3207  $link = '';
3208  if (!empty($conf->global->AGENDA_ADDACTIONFOREMAIL)) {
3209  $link = '<a href="'.DOL_URL_ROOT.'/comm/action/card.php?action=create&amp;backtopage=1&amp;actioncode='.$type.'&amp;contactid='.$cid.'&amp;socid='.$socid.'">'.img_object($langs->trans("AddAction"), "calendar").'</a>';
3210  }
3211  if ($link) {
3212  $newemail = '<div>'.$newemail.' '.$link.'</div>';
3213  }
3214  }
3215  } else {
3216  if ($showinvalid && !isValidEmail($email)) {
3217  $langs->load("errors");
3218  $newemail .= img_warning($langs->trans("ErrorBadEMail", $email));
3219  }
3220  }
3221 
3222  //$rep = '<div class="nospan" style="margin-right: 10px">';
3223  $rep = ($withpicto ? img_picto($langs->trans("EMail").' : '.$email, (is_numeric($withpicto) ? 'email' : $withpicto)).' ' : '').$newemail;
3224  //$rep .= '</div>';
3225  if ($hookmanager) {
3226  $parameters = array('cid' => $cid, 'socid' => $socid, 'addlink' => $addlink, 'picto' => $withpicto);
3227 
3228  $reshook = $hookmanager->executeHooks('printEmail', $parameters, $email);
3229  if ($reshook > 0) {
3230  $rep = '';
3231  }
3232  $rep .= $hookmanager->resPrint;
3233  }
3234 
3235  return $rep;
3236 }
3237 
3244 {
3245  global $conf, $db;
3246 
3247  $socialnetworks = array();
3248  // Enable caching of array
3249  require_once DOL_DOCUMENT_ROOT.'/core/lib/memory.lib.php';
3250  $cachekey = 'socialnetworks_' . $conf->entity;
3251  $dataretrieved = dol_getcache($cachekey);
3252  if (!is_null($dataretrieved)) {
3253  $socialnetworks = $dataretrieved;
3254  } else {
3255  $sql = "SELECT rowid, code, label, url, icon, active FROM ".MAIN_DB_PREFIX."c_socialnetworks";
3256  $sql .= " WHERE entity=".$conf->entity;
3257  $resql = $db->query($sql);
3258  if ($resql) {
3259  while ($obj = $db->fetch_object($resql)) {
3260  $socialnetworks[$obj->code] = array(
3261  'rowid' => $obj->rowid,
3262  'label' => $obj->label,
3263  'url' => $obj->url,
3264  'icon' => $obj->icon,
3265  'active' => $obj->active,
3266  );
3267  }
3268  }
3269  dol_setcache($cachekey, $socialnetworks); // If setting cache fails, this is not a problem, so we do not test result.
3270  }
3271 
3272  return $socialnetworks;
3273 }
3274 
3285 function dol_print_socialnetworks($value, $cid, $socid, $type, $dictsocialnetworks = array())
3286 {
3287  global $conf, $user, $langs;
3288 
3289  $htmllink = $value;
3290 
3291  if (empty($value)) {
3292  return '&nbsp;';
3293  }
3294 
3295  if (!empty($type)) {
3296  $htmllink = '<div class="divsocialnetwork inline-block valignmiddle">';
3297  // Use dictionary definition for picto $dictsocialnetworks[$type]['icon']
3298  $htmllink .= '<span class="fa pictofixedwidth '.($dictsocialnetworks[$type]['icon'] ? $dictsocialnetworks[$type]['icon'] : 'fa-link').'"></span>';
3299  if ($type == 'skype') {
3300  $htmllink .= dol_escape_htmltag($value);
3301  $htmllink .= '&nbsp; <a href="skype:';
3302  $htmllink .= dol_string_nospecial($value, '_', '', array('@'));
3303  $htmllink .= '?call" alt="'.$langs->trans("Call").'&nbsp;'.$value.'" title="'.dol_escape_htmltag($langs->trans("Call").' '.$value).'">';
3304  $htmllink .= '<img src="'.DOL_URL_ROOT.'/theme/common/skype_callbutton.png" border="0">';
3305  $htmllink .= '</a><a href="skype:';
3306  $htmllink .= dol_string_nospecial($value, '_', '', array('@'));
3307  $htmllink .= '?chat" alt="'.$langs->trans("Chat").'&nbsp;'.$value.'" title="'.dol_escape_htmltag($langs->trans("Chat").' '.$value).'">';
3308  $htmllink .= '<img class="paddingleft" src="'.DOL_URL_ROOT.'/theme/common/skype_chatbutton.png" border="0">';
3309  $htmllink .= '</a>';
3310  if (($cid || $socid) && isModEnabled('agenda') && $user->hasRight('agenda', 'myactions', 'create')) {
3311  $addlink = 'AC_SKYPE';
3312  $link = '';
3313  if (!empty($conf->global->AGENDA_ADDACTIONFORSKYPE)) {
3314  $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>';
3315  }
3316  $htmllink .= ($link ? ' '.$link : '');
3317  }
3318  } else {
3319  if (!empty($dictsocialnetworks[$type]['url'])) {
3320  $tmpvirginurl = preg_replace('/\/?{socialid}/', '', $dictsocialnetworks[$type]['url']);
3321  if ($tmpvirginurl) {
3322  $value = preg_replace('/^www\.'.preg_quote($tmpvirginurl, '/').'\/?/', '', $value);
3323  $value = preg_replace('/^'.preg_quote($tmpvirginurl, '/').'\/?/', '', $value);
3324 
3325  $tmpvirginurl3 = preg_replace('/^https:\/\//i', 'https://www.', $tmpvirginurl);
3326  if ($tmpvirginurl3) {
3327  $value = preg_replace('/^www\.'.preg_quote($tmpvirginurl3, '/').'\/?/', '', $value);
3328  $value = preg_replace('/^'.preg_quote($tmpvirginurl3, '/').'\/?/', '', $value);
3329  }
3330 
3331  $tmpvirginurl2 = preg_replace('/^https?:\/\//i', '', $tmpvirginurl);
3332  if ($tmpvirginurl2) {
3333  $value = preg_replace('/^www\.'.preg_quote($tmpvirginurl2, '/').'\/?/', '', $value);
3334  $value = preg_replace('/^'.preg_quote($tmpvirginurl2, '/').'\/?/', '', $value);
3335  }
3336  }
3337  $link = str_replace('{socialid}', $value, $dictsocialnetworks[$type]['url']);
3338  if (preg_match('/^https?:\/\//i', $link)) {
3339  $htmllink .= '<a href="'.dol_sanitizeUrl($link, 0).'" target="_blank" rel="noopener noreferrer">'.dol_escape_htmltag($value).'</a>';
3340  } else {
3341  $htmllink .= '<a href="'.dol_sanitizeUrl($link, 1).'" target="_blank" rel="noopener noreferrer">'.dol_escape_htmltag($value).'</a>';
3342  }
3343  } else {
3344  $htmllink .= dol_escape_htmltag($value);
3345  }
3346  }
3347  $htmllink .= '</div>';
3348  } else {
3349  $langs->load("errors");
3350  $htmllink .= img_warning($langs->trans("ErrorBadSocialNetworkValue", $value));
3351  }
3352  return $htmllink;
3353 }
3354 
3365 function dol_print_profids($profID, $profIDtype, $countrycode = '', $addcpButton = 1, $separ = '&nbsp;')
3366 {
3367  global $mysoc;
3368 
3369  if (empty($profID) || empty($profIDtype)) {
3370  return '';
3371  }
3372  if (empty($countrycode)) $countrycode = $mysoc->country_code;
3373  $newProfID = $profID;
3374  $id = substr($profIDtype, -1);
3375  $ret = '';
3376  if (strtoupper($countrycode) == 'FR') {
3377  // France
3378  if ($id == 1 && dol_strlen($newProfID) == 9) $newProfID = substr($newProfID, 0, 3).$separ.substr($newProfID, 3, 3).$separ.substr($newProfID, 6, 3);
3379  if ($id == 2 && dol_strlen($newProfID) == 14) $newProfID = substr($newProfID, 0, 3).$separ.substr($newProfID, 3, 3).$separ.substr($newProfID, 6, 3).$separ.substr($newProfID, 9, 5);
3380  if ($profIDtype === 'VAT' && dol_strlen($newProfID) == 13) $newProfID = substr($newProfID, 0, 4).$separ.substr($newProfID, 4, 3).$separ.substr($newProfID, 7, 3).$separ.substr($newProfID, 10, 3);
3381  }
3382  if (!empty($addcpButton)) $ret = showValueWithClipboardCPButton(dol_escape_htmltag($profID), ($addcpButton == 1 ? 1 : 0), $newProfID);
3383  else $ret = $newProfID;
3384  return $ret;
3385 }
3386 
3401 function dol_print_phone($phone, $countrycode = '', $cid = 0, $socid = 0, $addlink = '', $separ = "&nbsp;", $withpicto = '', $titlealt = '', $adddivfloat = 0)
3402 {
3403  global $conf, $user, $langs, $mysoc, $hookmanager;
3404 
3405  // Clean phone parameter
3406  $phone = is_null($phone) ? '' : preg_replace("/[\s.-]/", "", trim($phone));
3407  if (empty($phone)) {
3408  return '';
3409  }
3410  if (!empty($conf->global->MAIN_PHONE_SEPAR)) {
3411  $separ = $conf->global->MAIN_PHONE_SEPAR;
3412  }
3413  if (empty($countrycode) && is_object($mysoc)) {
3414  $countrycode = $mysoc->country_code;
3415  }
3416 
3417  // Short format for small screens
3418  if ($conf->dol_optimize_smallscreen) {
3419  $separ = '';
3420  }
3421 
3422  $newphone = $phone;
3423  if (strtoupper($countrycode) == "FR") {
3424  // France
3425  if (dol_strlen($phone) == 10) {
3426  $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);
3427  } elseif (dol_strlen($phone) == 7) {
3428  $newphone = substr($newphone, 0, 3).$separ.substr($newphone, 3, 2).$separ.substr($newphone, 5, 2);
3429  } elseif (dol_strlen($phone) == 9) {
3430  $newphone = substr($newphone, 0, 2).$separ.substr($newphone, 2, 3).$separ.substr($newphone, 5, 2).$separ.substr($newphone, 7, 2);
3431  } elseif (dol_strlen($phone) == 11) {
3432  $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);
3433  } elseif (dol_strlen($phone) == 12) {
3434  $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);
3435  } elseif (dol_strlen($phone) == 13) {
3436  $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);
3437  }
3438  } elseif (strtoupper($countrycode) == "CA") {
3439  if (dol_strlen($phone) == 10) {
3440  $newphone = ($separ != '' ? '(' : '').substr($newphone, 0, 3).($separ != '' ? ')' : '').$separ.substr($newphone, 3, 3).($separ != '' ? '-' : '').substr($newphone, 6, 4);
3441  }
3442  } elseif (strtoupper($countrycode) == "PT") {//Portugal
3443  if (dol_strlen($phone) == 13) {//ex: +351_ABC_DEF_GHI
3444  $newphone = substr($newphone, 0, 4).$separ.substr($newphone, 4, 3).$separ.substr($newphone, 7, 3).$separ.substr($newphone, 10, 3);
3445  }
3446  } elseif (strtoupper($countrycode) == "SR") {//Suriname
3447  if (dol_strlen($phone) == 10) {//ex: +597_ABC_DEF
3448  $newphone = substr($newphone, 0, 4).$separ.substr($newphone, 4, 3).$separ.substr($newphone, 7, 3);
3449  } elseif (dol_strlen($phone) == 11) {//ex: +597_ABC_DEFG
3450  $newphone = substr($newphone, 0, 4).$separ.substr($newphone, 4, 3).$separ.substr($newphone, 7, 4);
3451  }
3452  } elseif (strtoupper($countrycode) == "DE") {//Allemagne
3453  if (dol_strlen($phone) == 14) {//ex: +49_ABCD_EFGH_IJK
3454  $newphone = substr($newphone, 0, 3).$separ.substr($newphone, 3, 4).$separ.substr($newphone, 7, 4).$separ.substr($newphone, 11, 3);
3455  } elseif (dol_strlen($phone) == 13) {//ex: +49_ABC_DEFG_HIJ
3456  $newphone = substr($newphone, 0, 3).$separ.substr($newphone, 3, 3).$separ.substr($newphone, 6, 4).$separ.substr($newphone, 10, 3);
3457  }
3458  } elseif (strtoupper($countrycode) == "ES") {//Espagne
3459  if (dol_strlen($phone) == 12) {//ex: +34_ABC_DEF_GHI
3460  $newphone = substr($newphone, 0, 3).$separ.substr($newphone, 3, 3).$separ.substr($newphone, 6, 3).$separ.substr($newphone, 9, 3);
3461  }
3462  } elseif (strtoupper($countrycode) == "BF") {// Burkina Faso
3463  if (dol_strlen($phone) == 12) {//ex : +22 A BC_DE_FG_HI
3464  $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);
3465  }
3466  } elseif (strtoupper($countrycode) == "RO") {// Roumanie
3467  if (dol_strlen($phone) == 12) {//ex : +40 AB_CDE_FG_HI
3468  $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);
3469  }
3470  } elseif (strtoupper($countrycode) == "TR") {//Turquie
3471  if (dol_strlen($phone) == 13) {//ex : +90 ABC_DEF_GHIJ
3472  $newphone = substr($newphone, 0, 3).$separ.substr($newphone, 3, 3).$separ.substr($newphone, 6, 3).$separ.substr($newphone, 9, 4);
3473  }
3474  } elseif (strtoupper($countrycode) == "US") {//Etat-Unis
3475  if (dol_strlen($phone) == 12) {//ex: +1 ABC_DEF_GHIJ
3476  $newphone = substr($newphone, 0, 2).$separ.substr($newphone, 2, 3).$separ.substr($newphone, 5, 3).$separ.substr($newphone, 8, 4);
3477  }
3478  } elseif (strtoupper($countrycode) == "MX") {//Mexique
3479  if (dol_strlen($phone) == 12) {//ex: +52 ABCD_EFG_HI
3480  $newphone = substr($newphone, 0, 3).$separ.substr($newphone, 3, 4).$separ.substr($newphone, 7, 3).$separ.substr($newphone, 10, 2);
3481  } elseif (dol_strlen($phone) == 11) {//ex: +52 AB_CD_EF_GH
3482  $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);
3483  } elseif (dol_strlen($phone) == 13) {//ex: +52 ABC_DEF_GHIJ
3484  $newphone = substr($newphone, 0, 3).$separ.substr($newphone, 3, 3).$separ.substr($newphone, 6, 3).$separ.substr($newphone, 9, 4);
3485  }
3486  } elseif (strtoupper($countrycode) == "ML") {//Mali
3487  if (dol_strlen($phone) == 12) {//ex: +223 AB_CD_EF_GH
3488  $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);
3489  }
3490  } elseif (strtoupper($countrycode) == "TH") {//Thaïlande
3491  if (dol_strlen($phone) == 11) {//ex: +66_ABC_DE_FGH
3492  $newphone = substr($newphone, 0, 3).$separ.substr($newphone, 3, 3).$separ.substr($newphone, 6, 2).$separ.substr($newphone, 8, 3);
3493  } elseif (dol_strlen($phone) == 12) {//ex: +66_A_BCD_EF_GHI
3494  $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);
3495  }
3496  } elseif (strtoupper($countrycode) == "MU") {
3497  //Maurice
3498  if (dol_strlen($phone) == 11) {//ex: +230_ABC_DE_FG
3499  $newphone = substr($newphone, 0, 4).$separ.substr($newphone, 4, 3).$separ.substr($newphone, 7, 2).$separ.substr($newphone, 9, 2);
3500  } elseif (dol_strlen($phone) == 12) {//ex: +230_ABCD_EF_GH
3501  $newphone = substr($newphone, 0, 4).$separ.substr($newphone, 4, 4).$separ.substr($newphone, 8, 2).$separ.substr($newphone, 10, 2);
3502  }
3503  } elseif (strtoupper($countrycode) == "ZA") {//Afrique du sud
3504  if (dol_strlen($phone) == 12) {//ex: +27_AB_CDE_FG_HI
3505  $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);
3506  }
3507  } elseif (strtoupper($countrycode) == "SY") {//Syrie
3508  if (dol_strlen($phone) == 12) {//ex: +963_AB_CD_EF_GH
3509  $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);
3510  } elseif (dol_strlen($phone) == 13) {//ex: +963_AB_CD_EF_GHI
3511  $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);
3512  }
3513  } elseif (strtoupper($countrycode) == "AE") {//Emirats Arabes Unis
3514  if (dol_strlen($phone) == 12) {//ex: +971_ABC_DEF_GH
3515  $newphone = substr($newphone, 0, 4).$separ.substr($newphone, 4, 3).$separ.substr($newphone, 7, 3).$separ.substr($newphone, 10, 2);
3516  } elseif (dol_strlen($phone) == 13) {//ex: +971_ABC_DEF_GHI
3517  $newphone = substr($newphone, 0, 4).$separ.substr($newphone, 4, 3).$separ.substr($newphone, 7, 3).$separ.substr($newphone, 10, 3);
3518  } elseif (dol_strlen($phone) == 14) {//ex: +971_ABC_DEF_GHIK
3519  $newphone = substr($newphone, 0, 4).$separ.substr($newphone, 4, 3).$separ.substr($newphone, 7, 3).$separ.substr($newphone, 10, 4);
3520  }
3521  } elseif (strtoupper($countrycode) == "DZ") {//Algérie
3522  if (dol_strlen($phone) == 13) {//ex: +213_ABC_DEF_GHI
3523  $newphone = substr($newphone, 0, 4).$separ.substr($newphone, 4, 3).$separ.substr($newphone, 7, 3).$separ.substr($newphone, 10, 3);
3524  }
3525  } elseif (strtoupper($countrycode) == "BE") {//Belgique
3526  if (dol_strlen($phone) == 11) {//ex: +32_ABC_DE_FGH
3527  $newphone = substr($newphone, 0, 3).$separ.substr($newphone, 3, 3).$separ.substr($newphone, 6, 2).$separ.substr($newphone, 8, 3);
3528  } elseif (dol_strlen($phone) == 12) {//ex: +32_ABC_DEF_GHI
3529  $newphone = substr($newphone, 0, 3).$separ.substr($newphone, 3, 3).$separ.substr($newphone, 6, 3).$separ.substr($newphone, 9, 3);
3530  }
3531  } elseif (strtoupper($countrycode) == "PF") {//Polynésie française
3532  if (dol_strlen($phone) == 12) {//ex: +689_AB_CD_EF_GH
3533  $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);
3534  }
3535  } elseif (strtoupper($countrycode) == "CO") {//Colombie
3536  if (dol_strlen($phone) == 13) {//ex: +57_ABC_DEF_GH_IJ
3537  $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);
3538  }
3539  } elseif (strtoupper($countrycode) == "JO") {//Jordanie
3540  if (dol_strlen($phone) == 12) {//ex: +962_A_BCD_EF_GH
3541  $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);
3542  }
3543  } elseif (strtoupper($countrycode) == "JM") {//Jamaïque
3544  if (dol_strlen($newphone) == 12) {//ex: +1867_ABC_DEFG
3545  $newphone = substr($newphone, 0, 5).$separ.substr($newphone, 5, 3).$separ.substr($newphone, 8, 4);
3546  }
3547  } elseif (strtoupper($countrycode) == "MG") {//Madagascar
3548  if (dol_strlen($phone) == 13) {//ex: +261_AB_CD_EFG_HI
3549  $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);
3550  }
3551  } elseif (strtoupper($countrycode) == "GB") {//Royaume uni
3552  if (dol_strlen($phone) == 13) {//ex: +44_ABCD_EFG_HIJ
3553  $newphone = substr($newphone, 0, 3).$separ.substr($newphone, 3, 4).$separ.substr($newphone, 7, 3).$separ.substr($newphone, 10, 3);
3554  }
3555  } elseif (strtoupper($countrycode) == "CH") {//Suisse
3556  if (dol_strlen($phone) == 12) {//ex: +41_AB_CDE_FG_HI
3557  $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);
3558  } elseif (dol_strlen($phone) == 15) {// +41_AB_CDE_FGH_IJKL
3559  $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);
3560  }
3561  } elseif (strtoupper($countrycode) == "TN") {//Tunisie
3562  if (dol_strlen($phone) == 12) {//ex: +216_AB_CDE_FGH
3563  $newphone = substr($newphone, 0, 4).$separ.substr($newphone, 4, 2).$separ.substr($newphone, 6, 3).$separ.substr($newphone, 9, 3);
3564  }
3565  } elseif (strtoupper($countrycode) == "GF") {//Guyane francaise
3566  if (dol_strlen($phone) == 13) {//ex: +594_ABC_DE_FG_HI (ABC=594 de nouveau)
3567  $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);
3568  }
3569  } elseif (strtoupper($countrycode) == "GP") {//Guadeloupe
3570  if (dol_strlen($phone) == 13) {//ex: +590_ABC_DE_FG_HI (ABC=590 de nouveau)
3571  $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);
3572  }
3573  } elseif (strtoupper($countrycode) == "MQ") {//Martinique
3574  if (dol_strlen($phone) == 13) {//ex: +596_ABC_DE_FG_HI (ABC=596 de nouveau)
3575  $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);
3576  }
3577  } elseif (strtoupper($countrycode) == "IT") {//Italie
3578  if (dol_strlen($phone) == 12) {//ex: +39_ABC_DEF_GHI
3579  $newphone = substr($newphone, 0, 3).$separ.substr($newphone, 3, 3).$separ.substr($newphone, 6, 3).$separ.substr($newphone, 9, 3);
3580  } elseif (dol_strlen($phone) == 13) {//ex: +39_ABC_DEF_GH_IJ
3581  $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);
3582  }
3583  } elseif (strtoupper($countrycode) == "AU") {
3584  //Australie
3585  if (dol_strlen($phone) == 12) {
3586  //ex: +61_A_BCDE_FGHI
3587  $newphone = substr($newphone, 0, 3).$separ.substr($newphone, 3, 1).$separ.substr($newphone, 4, 4).$separ.substr($newphone, 8, 4);
3588  }
3589  } elseif (strtoupper($countrycode) == "LU") {
3590  // Luxembourg
3591  if (dol_strlen($phone) == 10) {// fixe 6 chiffres +352_AA_BB_CC
3592  $newphone = substr($newphone, 0, 4).$separ.substr($newphone, 4, 2).$separ.substr($newphone, 6, 2).$separ.substr($newphone, 8, 2);
3593  } elseif (dol_strlen($phone) == 11) {// fixe 7 chiffres +352_AA_BB_CC_D
3594  $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);
3595  } elseif (dol_strlen($phone) == 12) {// fixe 8 chiffres +352_AA_BB_CC_DD
3596  $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);
3597  } elseif (dol_strlen($phone) == 13) {// mobile +352_AAA_BB_CC_DD
3598  $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);
3599  }
3600  }
3601  if (!empty($addlink)) { // Link on phone number (+ link to add action if conf->global->AGENDA_ADDACTIONFORPHONE set)
3602  if ($addlink == 'tel' || $conf->browser->layout == 'phone' || (isModEnabled('clicktodial') && !empty($conf->global->CLICKTODIAL_USE_TEL_LINK_ON_PHONE_NUMBERS))) { // If phone or option for, we use link of phone
3603  $newphoneform = $newphone;
3604  $newphone = '<a href="tel:'.$phone.'"';
3605  $newphone .= '>'.$newphoneform.'</a>';
3606  } elseif (isModEnabled('clicktodial') && $addlink == 'AC_TEL') { // If click to dial, we use click to dial url
3607  if (empty($user->clicktodial_loaded)) {
3608  $user->fetch_clicktodial();
3609  }
3610 
3611  // Define urlmask
3612  $urlmask = 'ErrorClickToDialModuleNotConfigured';
3613  if (!empty($conf->global->CLICKTODIAL_URL)) {
3614  $urlmask = $conf->global->CLICKTODIAL_URL;
3615  }
3616  if (!empty($user->clicktodial_url)) {
3617  $urlmask = $user->clicktodial_url;
3618  }
3619 
3620  $clicktodial_poste = (!empty($user->clicktodial_poste) ?urlencode($user->clicktodial_poste) : '');
3621  $clicktodial_login = (!empty($user->clicktodial_login) ?urlencode($user->clicktodial_login) : '');
3622  $clicktodial_password = (!empty($user->clicktodial_password) ?urlencode($user->clicktodial_password) : '');
3623  // This line is for backward compatibility
3624  $url = sprintf($urlmask, urlencode($phone), $clicktodial_poste, $clicktodial_login, $clicktodial_password);
3625  // Thoose lines are for substitution
3626  $substitarray = array('__PHONEFROM__'=>$clicktodial_poste,
3627  '__PHONETO__'=>urlencode($phone),
3628  '__LOGIN__'=>$clicktodial_login,
3629  '__PASS__'=>$clicktodial_password);
3630  $url = make_substitutions($url, $substitarray);
3631  $newphonesav = $newphone;
3632  if (empty($conf->global->CLICKTODIAL_DO_NOT_USE_AJAX_CALL)) {
3633  // Default and recommended: New method using ajax without submiting a page making a javascript history.go(-1) back
3634  $newphone = '<a href="'.$url.'" class="cssforclicktodial"'; // Call of ajax is handled by the lib_foot.js.php on class 'cssforclicktodial'
3635  $newphone .= '>'.$newphonesav.'</a>';
3636  } else {
3637  // Old method
3638  $newphone = '<a href="'.$url.'"';
3639  if (!empty($conf->global->CLICKTODIAL_FORCENEWTARGET)) {
3640  $newphone .= ' target="_blank" rel="noopener noreferrer"';
3641  }
3642  $newphone .= '>'.$newphonesav.'</a>';
3643  }
3644  }
3645 
3646  //if (($cid || $socid) && isModEnabled('agenda') && $user->hasRight('agenda', 'myactions', 'create'))
3647  if (isModEnabled('agenda') && $user->hasRight("agenda", "myactions", "create")) {
3648  $type = 'AC_TEL';
3649  $link = '';
3650  if ($addlink == 'AC_FAX') {
3651  $type = 'AC_FAX';
3652  }
3653  if (!empty($conf->global->AGENDA_ADDACTIONFORPHONE)) {
3654  $link = '<a href="'.DOL_URL_ROOT.'/comm/action/card.php?action=create&amp;backtopage='. urlencode($_SERVER['REQUEST_URI']) .'&amp;actioncode='.$type.($cid ? '&amp;contactid='.$cid : '').($socid ? '&amp;socid='.$socid : '').'">'.img_object($langs->trans("AddAction"), "calendar").'</a>';
3655  }
3656  if ($link) {
3657  $newphone = '<div>'.$newphone.' '.$link.'</div>';
3658  }
3659  }
3660  }
3661 
3662  if (empty($titlealt)) {
3663  $titlealt = ($withpicto == 'fax' ? $langs->trans("Fax") : $langs->trans("Phone"));
3664  }
3665  $rep = '';
3666 
3667  if ($hookmanager) {
3668  $parameters = array('countrycode' => $countrycode, 'cid' => $cid, 'socid' => $socid, 'titlealt' => $titlealt, 'picto' => $withpicto);
3669  $reshook = $hookmanager->executeHooks('printPhone', $parameters, $phone);
3670  $rep .= $hookmanager->resPrint;
3671  }
3672  if (empty($reshook)) {
3673  $picto = '';
3674  if ($withpicto) {
3675  if ($withpicto == 'fax') {
3676  $picto = 'phoning_fax';
3677  } elseif ($withpicto == 'phone') {
3678  $picto = 'phoning';
3679  } elseif ($withpicto == 'mobile') {
3680  $picto = 'phoning_mobile';
3681  } else {
3682  $picto = '';
3683  }
3684  }
3685  if ($adddivfloat == 1) {
3686  $rep .= '<div class="nospan float" style="margin-right: 10px">';
3687  } elseif (empty($adddivfloat)) {
3688  $rep .= '<span style="margin-right: 10px;">';
3689  }
3690  $rep .= ($withpicto ?img_picto($titlealt, 'object_'.$picto.'.png').' ' : '').$newphone;
3691  if ($adddivfloat == 1) {
3692  $rep .= '</div>';
3693  } elseif (empty($adddivfloat)) {
3694  $rep .= '</span>';
3695  }
3696  }
3697 
3698  return $rep;
3699 }
3700 
3708 function dol_print_ip($ip, $mode = 0)
3709 {
3710  global $conf, $langs;
3711 
3712  $ret = '';
3713 
3714  if (empty($mode)) {
3715  $ret .= $ip;
3716  }
3717 
3718  if ($mode != 2) {
3719  $countrycode = dolGetCountryCodeFromIp($ip);
3720  if ($countrycode) { // If success, countrycode is us, fr, ...
3721  if (file_exists(DOL_DOCUMENT_ROOT.'/theme/common/flags/'.$countrycode.'.png')) {
3722  $ret .= ' '.img_picto($countrycode.' '.$langs->trans("AccordingToGeoIPDatabase"), DOL_URL_ROOT.'/theme/common/flags/'.$countrycode.'.png', '', 1);
3723  } else {
3724  $ret .= ' ('.$countrycode.')';
3725  }
3726  } else {
3727  // Nothing
3728  }
3729  }
3730 
3731  return $ret;
3732 }
3733 
3743 {
3744  if (empty($_SERVER['HTTP_X_FORWARDED_FOR']) || preg_match('/[^0-9\.\:,\[\]]/', $_SERVER['HTTP_X_FORWARDED_FOR'])) {
3745  if (empty($_SERVER['HTTP_CLIENT_IP']) || preg_match('/[^0-9\.\:,\[\]]/', $_SERVER['HTTP_CLIENT_IP'])) {
3746  if (empty($_SERVER["HTTP_CF_CONNECTING_IP"])) {
3747  $ip = (empty($_SERVER['REMOTE_ADDR']) ? '' : $_SERVER['REMOTE_ADDR']); // value may have been the IP of the proxy and not the client
3748  } else {
3749  $ip = $_SERVER["HTTP_CF_CONNECTING_IP"]; // value here may have been forged by client
3750  }
3751  } else {
3752  $ip = $_SERVER['HTTP_CLIENT_IP']; // value is clean here but may have been forged by proxy
3753  }
3754  } else {
3755  $ip = $_SERVER['HTTP_X_FORWARDED_FOR']; // value is clean here but may have been forged by proxy
3756  }
3757  return $ip;
3758 }
3759 
3768 function isHTTPS()
3769 {
3770  $isSecure = false;
3771  if (isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] == 'on') {
3772  $isSecure = true;
3773  } 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') {
3774  $isSecure = true;
3775  }
3776  return $isSecure;
3777 }
3778 
3786 {
3787  global $conf;
3788 
3789  $countrycode = '';
3790 
3791  if (!empty($conf->geoipmaxmind->enabled)) {
3792  $datafile = getDolGlobalString('GEOIPMAXMIND_COUNTRY_DATAFILE');
3793  //$ip='24.24.24.24';
3794  //$datafile='/usr/share/GeoIP/GeoIP.dat'; Note that this must be downloaded datafile (not same than datafile provided with ubuntu packages)
3795  include_once DOL_DOCUMENT_ROOT.'/core/class/dolgeoip.class.php';
3796  $geoip = new DolGeoIP('country', $datafile);
3797  //print 'ip='.$ip.' databaseType='.$geoip->gi->databaseType." GEOIP_CITY_EDITION_REV1=".GEOIP_CITY_EDITION_REV1."\n";
3798  $countrycode = $geoip->getCountryCodeFromIP($ip);
3799  }
3800 
3801  return $countrycode;
3802 }
3803 
3804 
3812 {
3813  global $conf, $langs, $user;
3814 
3815  //$ret=$user->xxx;
3816  $ret = '';
3817  if (!empty($conf->geoipmaxmind->enabled)) {
3818  $ip = getUserRemoteIP();
3819  $datafile = getDolGlobalString('GEOIPMAXMIND_COUNTRY_DATAFILE');
3820  //$ip='24.24.24.24';
3821  //$datafile='E:\Mes Sites\Web\Admin1\awstats\maxmind\GeoIP.dat';
3822  include_once DOL_DOCUMENT_ROOT.'/core/class/dolgeoip.class.php';
3823  $geoip = new DolGeoIP('country', $datafile);
3824  $countrycode = $geoip->getCountryCodeFromIP($ip);
3825  $ret = $countrycode;
3826  }
3827  return $ret;
3828 }
3829 
3842 function dol_print_address($address, $htmlid, $element, $id, $noprint = 0, $charfornl = '')
3843 {
3844  global $conf, $user, $langs, $hookmanager;
3845 
3846  $out = '';
3847 
3848  if ($address) {
3849  if ($hookmanager) {
3850  $parameters = array('element' => $element, 'id' => $id);
3851  $reshook = $hookmanager->executeHooks('printAddress', $parameters, $address);
3852  $out .= $hookmanager->resPrint;
3853  }
3854  if (empty($reshook)) {
3855  if (empty($charfornl)) {
3856  $out .= nl2br($address);
3857  } else {
3858  $out .= preg_replace('/[\r\n]+/', $charfornl, $address);
3859  }
3860 
3861  // TODO Remove this block, we can add this using the hook now
3862  $showgmap = $showomap = 0;
3863  if (($element == 'thirdparty' || $element == 'societe') && isModEnabled('google') && !empty($conf->global->GOOGLE_ENABLE_GMAPS)) {
3864  $showgmap = 1;
3865  }
3866  if ($element == 'contact' && isModEnabled('google') && !empty($conf->global->GOOGLE_ENABLE_GMAPS_CONTACTS)) {
3867  $showgmap = 1;
3868  }
3869  if ($element == 'member' && isModEnabled('google') && !empty($conf->global->GOOGLE_ENABLE_GMAPS_MEMBERS)) {
3870  $showgmap = 1;
3871  }
3872  if (($element == 'thirdparty' || $element == 'societe') && isModEnabled('openstreetmap') && !empty($conf->global->OPENSTREETMAP_ENABLE_MAPS)) {
3873  $showomap = 1;
3874  }
3875  if ($element == 'contact' && isModEnabled('openstreetmap') && !empty($conf->global->OPENSTREETMAP_ENABLE_MAPS_CONTACTS)) {
3876  $showomap = 1;
3877  }
3878  if ($element == 'member' && isModEnabled('openstreetmap') && !empty($conf->global->OPENSTREETMAP_ENABLE_MAPS_MEMBERS)) {
3879  $showomap = 1;
3880  }
3881  if ($showgmap) {
3882  $url = dol_buildpath('/google/gmaps.php?mode='.$element.'&id='.$id, 1);
3883  $out .= ' <a href="'.$url.'" target="_gmaps"><img id="'.$htmlid.'" class="valigntextbottom" src="'.DOL_URL_ROOT.'/theme/common/gmap.png"></a>';
3884  }
3885  if ($showomap) {
3886  $url = dol_buildpath('/openstreetmap/maps.php?mode='.$element.'&id='.$id, 1);
3887  $out .= ' <a href="'.$url.'" target="_gmaps"><img id="'.$htmlid.'_openstreetmap" class="valigntextbottom" src="'.DOL_URL_ROOT.'/theme/common/gmap.png"></a>';
3888  }
3889  }
3890  }
3891  if ($noprint) {
3892  return $out;
3893  } else {
3894  print $out;
3895  }
3896 }
3897 
3898 
3908 function isValidEmail($address, $acceptsupervisorkey = 0, $acceptuserkey = 0)
3909 {
3910  if ($acceptsupervisorkey && $address == '__SUPERVISOREMAIL__') {
3911  return true;
3912  }
3913  if ($acceptuserkey && $address == '__USER_EMAIL__') {
3914  return true;
3915  }
3916  if (filter_var($address, FILTER_VALIDATE_EMAIL)) {
3917  return true;
3918  }
3919 
3920  return false;
3921 }
3922 
3931 function isValidMXRecord($domain)
3932 {
3933  if (function_exists('idn_to_ascii') && function_exists('checkdnsrr')) {
3934  if (!checkdnsrr(idn_to_ascii($domain), 'MX')) {
3935  return 0;
3936  }
3937  if (function_exists('getmxrr')) {
3938  $mxhosts = array();
3939  $weight = array();
3940  getmxrr(idn_to_ascii($domain), $mxhosts, $weight);
3941  if (count($mxhosts) > 1) {
3942  return 1;
3943  }
3944  if (count($mxhosts) == 1 && !empty($mxhosts[0])) {
3945  return 1;
3946  }
3947 
3948  return 0;
3949  }
3950  }
3951 
3952  // function idn_to_ascii or checkdnsrr or getmxrr does not exists
3953  return -1;
3954 }
3955 
3963 function isValidPhone($phone)
3964 {
3965  return true;
3966 }
3967 
3968 
3978 function dolGetFirstLetters($s, $nbofchar = 1)
3979 {
3980  $ret = '';
3981  $tmparray = explode(' ', $s);
3982  foreach ($tmparray as $tmps) {
3983  $ret .= dol_substr($tmps, 0, $nbofchar);
3984  }
3985 
3986  return $ret;
3987 }
3988 
3989 
3997 function dol_strlen($string, $stringencoding = 'UTF-8')
3998 {
3999  if (is_null($string)) {
4000  return 0;
4001  }
4002 
4003  if (function_exists('mb_strlen')) {
4004  return mb_strlen($string, $stringencoding);
4005  } else {
4006  return strlen($string);
4007  }
4008 }
4009 
4020 function dol_substr($string, $start, $length = null, $stringencoding = '', $trunconbytes = 0)
4021 {
4022  global $langs;
4023 
4024  if (empty($stringencoding)) {
4025  $stringencoding = $langs->charset_output;
4026  }
4027 
4028  $ret = '';
4029  if (empty($trunconbytes)) {
4030  if (function_exists('mb_substr')) {
4031  $ret = mb_substr($string, $start, $length, $stringencoding);
4032  } else {
4033  $ret = substr($string, $start, $length);
4034  }
4035  } else {
4036  if (function_exists('mb_strcut')) {
4037  $ret = mb_strcut($string, $start, $length, $stringencoding);
4038  } else {
4039  $ret = substr($string, $start, $length);
4040  }
4041  }
4042  return $ret;
4043 }
4044 
4045 
4059 function dol_trunc($string, $size = 40, $trunc = 'right', $stringencoding = 'UTF-8', $nodot = 0, $display = 0)
4060 {
4061  global $conf;
4062 
4063  if (empty($size) || !empty($conf->global->MAIN_DISABLE_TRUNC)) {
4064  return $string;
4065  }
4066 
4067  if (empty($stringencoding)) {
4068  $stringencoding = 'UTF-8';
4069  }
4070  // reduce for small screen
4071  if ($conf->dol_optimize_smallscreen == 1 && $display == 1) {
4072  $size = round($size / 3);
4073  }
4074 
4075  // We go always here
4076  if ($trunc == 'right') {
4077  $newstring = dol_textishtml($string) ? dol_string_nohtmltag($string, 1) : $string;
4078  if (dol_strlen($newstring, $stringencoding) > ($size + ($nodot ? 0 : 1))) {
4079  // If nodot is 0 and size is 1 chars more, we don't trunc and don't add …
4080  return dol_substr($newstring, 0, $size, $stringencoding).($nodot ? '' : '…');
4081  } else {
4082  //return 'u'.$size.'-'.$newstring.'-'.dol_strlen($newstring,$stringencoding).'-'.$string;
4083  return $string;
4084  }
4085  } elseif ($trunc == 'middle') {
4086  $newstring = dol_textishtml($string) ? dol_string_nohtmltag($string, 1) : $string;
4087  if (dol_strlen($newstring, $stringencoding) > 2 && dol_strlen($newstring, $stringencoding) > ($size + 1)) {
4088  $size1 = round($size / 2);
4089  $size2 = round($size / 2);
4090  return dol_substr($newstring, 0, $size1, $stringencoding).'…'.dol_substr($newstring, dol_strlen($newstring, $stringencoding) - $size2, $size2, $stringencoding);
4091  } else {
4092  return $string;
4093  }
4094  } elseif ($trunc == 'left') {
4095  $newstring = dol_textishtml($string) ? dol_string_nohtmltag($string, 1) : $string;
4096  if (dol_strlen($newstring, $stringencoding) > ($size + ($nodot ? 0 : 1))) {
4097  // If nodot is 0 and size is 1 chars more, we don't trunc and don't add …
4098  return '…'.dol_substr($newstring, dol_strlen($newstring, $stringencoding) - $size, $size, $stringencoding);
4099  } else {
4100  return $string;
4101  }
4102  } elseif ($trunc == 'wrap') {
4103  $newstring = dol_textishtml($string) ? dol_string_nohtmltag($string, 1) : $string;
4104  if (dol_strlen($newstring, $stringencoding) > ($size + 1)) {
4105  return dol_substr($newstring, 0, $size, $stringencoding)."\n".dol_trunc(dol_substr($newstring, $size, dol_strlen($newstring, $stringencoding) - $size, $stringencoding), $size, $trunc);
4106  } else {
4107  return $string;
4108  }
4109  } else {
4110  return 'BadParam3CallingDolTrunc';
4111  }
4112 }
4113 
4135 function img_picto($titlealt, $picto, $moreatt = '', $pictoisfullpath = false, $srconly = 0, $notitle = 0, $alt = '', $morecss = '', $marginleftonlyshort = 2)
4136 {
4137  global $conf, $langs;
4138 
4139  // We forge fullpathpicto for image to $path/img/$picto. By default, we take DOL_URL_ROOT/theme/$conf->theme/img/$picto
4140  $url = DOL_URL_ROOT;
4141  $theme = isset($conf->theme) ? $conf->theme : null;
4142  $path = 'theme/'.$theme;
4143  // Define fullpathpicto to use into src
4144  if ($pictoisfullpath) {
4145  // Clean parameters
4146  if (!preg_match('/(\.png|\.gif|\.svg)$/i', $picto)) {
4147  $picto .= '.png';
4148  }
4149  $fullpathpicto = $picto;
4150  $reg = array();
4151  if (preg_match('/class="([^"]+)"/', $moreatt, $reg)) {
4152  $morecss .= ($morecss ? ' ' : '').$reg[1];
4153  $moreatt = str_replace('class="'.$reg[1].'"', '', $moreatt);
4154  }
4155  } else {
4156  $pictowithouttext = preg_replace('/(\.png|\.gif|\.svg)$/', '', $picto);
4157  $pictowithouttext = str_replace('object_', '', $pictowithouttext);
4158  $pictowithouttext = str_replace('_nocolor', '', $pictowithouttext);
4159 
4160  if (strpos($pictowithouttext, 'fontawesome_') !== false || preg_match('/^fa-/', $pictowithouttext)) {
4161  // This is a font awesome image 'fonwtawesome_xxx' or 'fa-xxx'
4162  $pictowithouttext = str_replace('fontawesome_', '', $pictowithouttext);
4163  $pictowithouttext = str_replace('fa-', '', $pictowithouttext);
4164 
4165  $pictowithouttextarray = explode('_', $pictowithouttext);
4166  $marginleftonlyshort = 0;
4167 
4168  if (!empty($pictowithouttextarray[1])) {
4169  // Syntax is 'fontawesome_fakey_faprefix_facolor_fasize' or 'fa-fakey_faprefix_facolor_fasize'
4170  $fakey = 'fa-'.$pictowithouttextarray[0];
4171  $fa = empty($pictowithouttextarray[1]) ? 'fa' : $pictowithouttextarray[1];
4172  $facolor = empty($pictowithouttextarray[2]) ? '' : $pictowithouttextarray[2];
4173  $fasize = empty($pictowithouttextarray[3]) ? '' : $pictowithouttextarray[3];
4174  } else {
4175  $fakey = 'fa-'.$pictowithouttext;
4176  $fa = 'fa';
4177  $facolor = '';
4178  $fasize = '';
4179  }
4180 
4181  // This snippet only needed since function img_edit accepts only one additional parameter: no separate one for css only.
4182  // class/style need to be extracted to avoid duplicate class/style validation errors when $moreatt is added to the end of the attributes.
4183  $morestyle = '';
4184  $reg = array();
4185  if (preg_match('/class="([^"]+)"/', $moreatt, $reg)) {
4186  $morecss .= ($morecss ? ' ' : '').$reg[1];
4187  $moreatt = str_replace('class="'.$reg[1].'"', '', $moreatt);
4188  }
4189  if (preg_match('/style="([^"]+)"/', $moreatt, $reg)) {
4190  $morestyle = $reg[1];
4191  $moreatt = str_replace('style="'.$reg[1].'"', '', $moreatt);
4192  }
4193  $moreatt = trim($moreatt);
4194 
4195  $enabledisablehtml = '<span class="'.$fa.' '.$fakey.($marginleftonlyshort ? ($marginleftonlyshort == 1 ? ' marginleftonlyshort' : ' marginleftonly') : '');
4196  $enabledisablehtml .= ($morecss ? ' '.$morecss : '').'" style="'.($fasize ? ('font-size: '.$fasize.';') : '').($facolor ? (' color: '.$facolor.';') : '').($morestyle ? ' '.$morestyle : '').'"'.(($notitle || empty($titlealt)) ? '' : ' title="'.dol_escape_htmltag($titlealt).'"').($moreatt ? ' '.$moreatt : '').'>';
4197  /*if (!empty($conf->global->MAIN_OPTIMIZEFORTEXTBROWSER)) {
4198  $enabledisablehtml .= $titlealt;
4199  }*/
4200  $enabledisablehtml .= '</span>';
4201 
4202  return $enabledisablehtml;
4203  }
4204 
4205  if (empty($srconly) && in_array($pictowithouttext, array(
4206  '1downarrow', '1uparrow', '1leftarrow', '1rightarrow', '1uparrow_selected', '1downarrow_selected', '1leftarrow_selected', '1rightarrow_selected',
4207  'accountancy', 'accounting_account', 'account', 'accountline', 'action', 'add', 'address', 'angle-double-down', 'angle-double-up', 'asset',
4208  'bank_account', 'barcode', 'bank', 'bell', 'bill', 'billa', 'billr', 'billd', 'birthday-cake', 'bookmark', 'bom', 'briefcase-medical', 'bug', 'building',
4209  'card', 'calendarlist', 'calendar', 'calendarmonth', 'calendarweek', 'calendarday', 'calendarperuser', 'calendarpertype',
4210  'cash-register', 'category', 'chart', 'check', 'clock', 'clone', 'close_title', 'cog', 'collab', 'company', 'contact', 'country', 'contract', 'conversation', 'cron', 'cross', 'cubes',
4211  'currency', 'multicurrency',
4212  'delete', 'dolly', 'dollyrevert', 'donation', 'download', 'dynamicprice',
4213  'edit', 'ellipsis-h', 'email', 'entity', 'envelope', 'eraser', 'establishment', 'expensereport', 'external-link-alt', 'external-link-square-alt', 'eye',
4214  'filter', 'file-code', 'file-export', 'file-import', 'file-upload', 'autofill', 'folder', 'folder-open', 'folder-plus',
4215  'gears', 'generate', 'generic', 'globe', 'globe-americas', 'graph', 'grip', 'grip_title', 'group',
4216  'hands-helping', 'help', 'holiday',
4217  'id-card', 'images', 'incoterm', 'info', 'intervention', 'inventory', 'intracommreport', 'jobprofile',
4218  'knowledgemanagement',
4219  'label', 'language', 'line', 'link', 'list', 'list-alt', 'listlight', 'loan', 'lock', 'lot', 'long-arrow-alt-right',
4220  'margin', 'map-marker-alt', 'member', 'meeting', 'money-bill-alt', 'movement', 'mrp', 'note', 'next',
4221  'off', 'on', 'order',
4222  'paiment', 'paragraph', 'play', 'pdf', 'phone', 'phoning', 'phoning_mobile', 'phoning_fax', 'playdisabled', 'previous', 'poll', 'pos', 'printer', 'product', 'propal', 'proposal', 'puce',
4223  'stock', 'resize', 'service', 'stats', 'trip',
4224  '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',
4225  'github', 'google', 'jabber', 'microsoft', 'skype', 'twitter', 'facebook', 'linkedin', 'instagram', 'snapchat', 'youtube', 'google-plus-g', 'whatsapp',
4226  'chevron-left', 'chevron-right', 'chevron-down', 'chevron-top', 'commercial', 'companies',
4227  'generic', 'home', 'hrm', 'members', 'products', 'invoicing',
4228  'partnership', 'payment', 'payment_vat', 'pencil-ruler', 'pictoconfirm', 'preview', 'project', 'projectpub', 'projecttask', 'question', 'refresh', 'region',
4229  'salary', 'shipment', 'state', 'supplier_invoice', 'supplier_invoicea', 'supplier_invoicer', 'supplier_invoiced',
4230  'technic', 'ticket',
4231  'error', 'warning',
4232  'recent', 'reception', 'recruitmentcandidature', 'recruitmentjobposition', 'replacement', 'resource', 'recurring','rss',
4233  'shapes', 'skill', 'square', 'stop-circle', 'supplier', 'supplier_proposal', 'supplier_order', 'supplier_invoice',
4234  'timespent', 'title_setup', 'title_accountancy', 'title_bank', 'title_hrm', 'title_agenda',
4235  'uncheck', 'url', 'user-cog', 'user-injured', 'user-md', 'vat', 'website', 'workstation', 'webhook', 'world', 'private',
4236  'conferenceorbooth', 'eventorganization',
4237  'stamp', 'signature'
4238  ))) {
4239  $fakey = $pictowithouttext;
4240  $facolor = '';
4241  $fasize = '';
4242  $fa = 'fas';
4243  if (in_array($pictowithouttext, array('card', 'bell', 'clock', 'establishment', 'generic', 'minus-square', 'object_generic', 'pdf', 'plus-square', 'timespent', 'note', 'off', 'on', 'object_bookmark', 'bookmark', 'vcard'))) {
4244  $fa = 'far';
4245  }
4246  if (in_array($pictowithouttext, array('black-tie', 'github', 'google', 'microsoft', 'skype', 'twitter', 'facebook', 'linkedin', 'instagram', 'snapchat', 'stripe', 'stripe-s', 'youtube', 'google-plus-g', 'whatsapp'))) {
4247  $fa = 'fab';
4248  }
4249 
4250  $arrayconvpictotofa = array(
4251  'account'=>'university', 'accounting_account'=>'clipboard-list', 'accountline'=>'receipt', 'accountancy'=>'search-dollar', 'action'=>'calendar-alt', 'add'=>'plus-circle', 'address'=> 'address-book', 'asset'=>'money-check-alt', 'autofill'=>'fill',
4252  'bank_account'=>'university',
4253  'bill'=>'file-invoice-dollar', 'billa'=>'file-excel', 'billr'=>'file-invoice-dollar', 'billd'=>'file-medical',
4254  'supplier_invoice'=>'file-invoice-dollar', 'supplier_invoicea'=>'file-excel', 'supplier_invoicer'=>'file-invoice-dollar', 'supplier_invoiced'=>'file-medical',
4255  'bom'=>'shapes',
4256  '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',
4257  'donation'=>'file-alt', 'dynamicprice'=>'hand-holding-usd',
4258  'setup'=>'cog', 'companies'=>'building', 'products'=>'cube', 'commercial'=>'suitcase', 'invoicing'=>'coins',
4259  'accounting'=>'search-dollar', 'category'=>'tag', 'dollyrevert'=>'dolly',
4260  'generate'=>'plus-square', 'hrm'=>'user-tie', 'incoterm'=>'truck-loading',
4261  'margin'=>'calculator', 'members'=>'user-friends', 'ticket'=>'ticket-alt', 'globe'=>'external-link-alt', 'lot'=>'barcode',
4262  'email'=>'at', 'establishment'=>'building', 'edit'=>'pencil-alt', 'entity'=>'globe',
4263  'graph'=>'chart-line', 'grip_title'=>'arrows-alt', 'grip'=>'arrows-alt', 'help'=>'question-circle',
4264  'generic'=>'file', 'holiday'=>'umbrella-beach',
4265  'info'=>'info-circle', 'inventory'=>'boxes', 'intracommreport'=>'globe-europe', 'jobprofile'=>'cogs',
4266  'knowledgemanagement'=>'ticket-alt', 'label'=>'layer-group', 'line'=>'bars', 'loan'=>'money-bill-alt',
4267  'member'=>'user-alt', 'meeting'=>'chalkboard-teacher', 'mrp'=>'cubes', 'next'=>'arrow-alt-circle-right',
4268  'trip'=>'wallet', 'expensereport'=>'wallet', 'group'=>'users', 'movement'=>'people-carry',
4269  'sign-out'=>'sign-out-alt',
4270  'switch_off'=>'toggle-off', 'switch_on'=>'toggle-on', 'switch_on_warning'=>'toggle-on', 'switch_on_red'=>'toggle-on', 'check'=>'check', 'bookmark'=>'star',
4271  'bank'=>'university', 'close_title'=>'times', 'delete'=>'trash', 'filter'=>'filter',
4272  'list-alt'=>'list-alt', 'calendarlist'=>'bars', 'calendar'=>'calendar-alt', 'calendarmonth'=>'calendar-alt', 'calendarweek'=>'calendar-week', 'calendarday'=>'calendar-day', 'calendarperuser'=>'table',
4273  'intervention'=>'ambulance', 'invoice'=>'file-invoice-dollar', 'currency'=>'dollar-sign', 'multicurrency'=>'dollar-sign', 'order'=>'file-invoice',
4274  'error'=>'exclamation-triangle', 'warning'=>'exclamation-triangle',
4275  'other'=>'square',
4276  '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',
4277  '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',
4278  'recent' => 'check-square', 'reception'=>'dolly', 'recruitmentjobposition'=>'id-card-alt', 'recruitmentcandidature'=>'id-badge',
4279  'resize'=>'crop', 'supplier_order'=>'dol-order_supplier', 'supplier_proposal'=>'file-signature',
4280  'refresh'=>'redo', 'region'=>'map-marked', 'replacement'=>'exchange-alt', 'resource'=>'laptop-house', 'recurring'=>'history',
4281  'service'=>'concierge-bell',
4282  'skill'=>'shapes', 'state'=>'map-marked-alt', 'security'=>'key', 'salary'=>'wallet', 'shipment'=>'dolly', 'stock'=>'box-open', 'stats' => 'chart-bar', 'split'=>'code-branch', 'stripe'=>'stripe-s',
4283  'supplier'=>'building', 'technic'=>'cogs',
4284  'timespent'=>'clock', 'title_setup'=>'tools', 'title_accountancy'=>'money-check-alt', 'title_bank'=>'university', 'title_hrm'=>'umbrella-beach',
4285  'title_agenda'=>'calendar-alt',
4286  'uncheck'=>'times', 'uparrow'=>'share', 'url'=>'external-link-alt', 'vat'=>'money-check-alt', 'vcard'=>'arrow-alt-circle-down',
4287  'jabber'=>'comment-o',
4288  'website'=>'globe-americas', 'workstation'=>'pallet', 'webhook'=>'bullseye', 'world'=>'globe', 'private'=>'user-lock',
4289  'conferenceorbooth'=>'chalkboard-teacher', 'eventorganization'=>'project-diagram'
4290  );
4291  if ($pictowithouttext == 'off') {
4292  $fakey = 'fa-square';
4293  $fasize = '1.3em';
4294  } elseif ($pictowithouttext == 'on') {
4295  $fakey = 'fa-check-square';
4296  $fasize = '1.3em';
4297  } elseif ($pictowithouttext == 'listlight') {
4298  $fakey = 'fa-download';
4299  $marginleftonlyshort = 1;
4300  } elseif ($pictowithouttext == 'printer') {
4301  $fakey = 'fa-print';
4302  $fasize = '1.2em';
4303  } elseif ($pictowithouttext == 'note') {
4304  $fakey = 'fa-sticky-note';
4305  $marginleftonlyshort = 1;
4306  } elseif (in_array($pictowithouttext, array('1uparrow', '1downarrow', '1leftarrow', '1rightarrow', '1uparrow_selected', '1downarrow_selected', '1leftarrow_selected', '1rightarrow_selected'))) {
4307  $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');
4308  $fakey = 'fa-'.$convertarray[$pictowithouttext];
4309  if (preg_match('/selected/', $pictowithouttext)) {
4310  $facolor = '#888';
4311  }
4312  $marginleftonlyshort = 1;
4313  } elseif (!empty($arrayconvpictotofa[$pictowithouttext])) {
4314  $fakey = 'fa-'.$arrayconvpictotofa[$pictowithouttext];
4315  } else {
4316  $fakey = 'fa-'.$pictowithouttext;
4317  }
4318 
4319  if (in_array($pictowithouttext, array('dollyrevert', 'member', 'members', 'contract', 'group', 'resource', 'shipment'))) {
4320  $morecss .= ' em092';
4321  }
4322  if (in_array($pictowithouttext, array('conferenceorbooth', 'collab', 'eventorganization', 'holiday', 'info', 'project', 'workstation'))) {
4323  $morecss .= ' em088';
4324  }
4325  if (in_array($pictowithouttext, array('asset', 'intervention', 'payment', 'loan', 'partnership', 'stock', 'technic'))) {
4326  $morecss .= ' em080';
4327  }
4328 
4329  // Define $marginleftonlyshort
4330  $arrayconvpictotomarginleftonly = array(
4331  'bank', 'check', 'delete', 'generic', 'grip', 'grip_title', 'jabber',
4332  'grip_title', 'grip', 'listlight', 'note', 'on', 'off', 'playdisabled', 'printer', 'resize', 'sign-out', 'stats', 'switch_on', 'switch_on_red', 'switch_off',
4333  'uparrow', '1uparrow', '1downarrow', '1leftarrow', '1rightarrow', '1uparrow_selected', '1downarrow_selected', '1leftarrow_selected', '1rightarrow_selected'
4334  );
4335  if (!isset($arrayconvpictotomarginleftonly[$pictowithouttext])) {
4336  $marginleftonlyshort = 0;
4337  }
4338 
4339  // Add CSS
4340  $arrayconvpictotomorcess = array(
4341  'action'=>'infobox-action', 'account'=>'infobox-bank_account', 'accounting_account'=>'infobox-bank_account', 'accountline'=>'infobox-bank_account', 'accountancy'=>'infobox-bank_account', 'asset'=>'infobox-bank_account',
4342  'bank_account'=>'infobox-bank_account',
4343  'bill'=>'infobox-commande', 'billa'=>'infobox-commande', 'billr'=>'infobox-commande', 'billd'=>'infobox-commande',
4344  'margin'=>'infobox-bank_account', 'conferenceorbooth'=>'infobox-project',
4345  'cash-register'=>'infobox-bank_account', 'contract'=>'infobox-contrat', 'check'=>'font-status4', 'collab'=>'infobox-action', 'conversation'=>'infobox-contrat',
4346  'donation'=>'infobox-commande', 'dolly'=>'infobox-commande', 'dollyrevert'=>'flip infobox-order_supplier',
4347  'ecm'=>'infobox-action', 'eventorganization'=>'infobox-project',
4348  'hrm'=>'infobox-adherent', 'group'=>'infobox-adherent', 'intervention'=>'infobox-contrat',
4349  'incoterm'=>'infobox-supplier_proposal',
4350  'currency'=>'infobox-bank_account', 'multicurrency'=>'infobox-bank_account',
4351  'members'=>'infobox-adherent', 'member'=>'infobox-adherent', 'money-bill-alt'=>'infobox-bank_account',
4352  'order'=>'infobox-commande',
4353  'user'=>'infobox-adherent', 'users'=>'infobox-adherent',
4354  'error'=>'pictoerror', 'warning'=>'pictowarning', 'switch_on'=>'font-status4', 'switch_on_warning'=>'font-status4 warning', 'switch_on_red'=>'font-status8',
4355  'holiday'=>'infobox-holiday', 'info'=>'opacityhigh', 'invoice'=>'infobox-commande',
4356  'knowledgemanagement'=>'infobox-contrat rotate90', 'loan'=>'infobox-bank_account',
4357  'payment'=>'infobox-bank_account', 'payment_vat'=>'infobox-bank_account', 'poll'=>'infobox-adherent', 'pos'=>'infobox-bank_account', 'project'=>'infobox-project', 'projecttask'=>'infobox-project',
4358  'propal'=>'infobox-propal', 'proposal'=>'infobox-propal','private'=>'infobox-project',
4359  'reception'=>'flip', 'recruitmentjobposition'=>'infobox-adherent', 'recruitmentcandidature'=>'infobox-adherent',
4360  'resource'=>'infobox-action',
4361  'salary'=>'infobox-bank_account', 'shipment'=>'infobox-commande', 'supplier_invoice'=>'infobox-order_supplier', 'supplier_invoicea'=>'infobox-order_supplier', 'supplier_invoiced'=>'infobox-order_supplier',
4362  'supplier'=>'infobox-order_supplier', 'supplier_order'=>'infobox-order_supplier', 'supplier_proposal'=>'infobox-supplier_proposal',
4363  'ticket'=>'infobox-contrat', 'title_accountancy'=>'infobox-bank_account', 'title_hrm'=>'infobox-holiday', 'expensereport'=>'infobox-expensereport', 'trip'=>'infobox-expensereport', 'title_agenda'=>'infobox-action',
4364  'vat'=>'infobox-bank_account',
4365  //'title_setup'=>'infobox-action', 'tools'=>'infobox-action',
4366  'list-alt'=>'imgforviewmode', 'calendar'=>'imgforviewmode', 'calendarweek'=>'imgforviewmode', 'calendarmonth'=>'imgforviewmode', 'calendarday'=>'imgforviewmode', 'calendarperuser'=>'imgforviewmode'
4367  );
4368  if (!empty($arrayconvpictotomorcess[$pictowithouttext]) && strpos($picto, '_nocolor') === false) {
4369  $morecss .= ($morecss ? ' ' : '').$arrayconvpictotomorcess[$pictowithouttext];
4370  }
4371 
4372  // Define $color
4373  $arrayconvpictotocolor = array(
4374  'address'=>'#6c6aa8', 'building'=>'#6c6aa8', 'bom'=>'#a69944',
4375  'clone'=>'#999', 'cog'=>'#999', 'companies'=>'#6c6aa8', 'company'=>'#6c6aa8', 'contact'=>'#6c6aa8', 'cron'=>'#555',
4376  'dynamicprice'=>'#a69944',
4377  'edit'=>'#444', 'note'=>'#999', 'error'=>'', 'help'=>'#bbb', 'listlight'=>'#999', 'language'=>'#555',
4378  //'dolly'=>'#a69944', 'dollyrevert'=>'#a69944',
4379  'lock'=>'#ddd', 'lot'=>'#a69944',
4380  'map-marker-alt'=>'#aaa', 'mrp'=>'#a69944', 'product'=>'#a69944', 'service'=>'#a69944', 'inventory'=>'#a69944', 'stock'=>'#a69944', 'movement'=>'#a69944',
4381  'other'=>'#ddd', 'world'=>'#986c6a',
4382  'partnership'=>'#6c6aa8', 'playdisabled'=>'#ccc', 'printer'=>'#444', 'projectpub'=>'#986c6a', 'reception'=>'#a69944', 'resize'=>'#444', 'rss'=>'#cba',
4383  //'shipment'=>'#a69944',
4384  'security'=>'#999', 'square'=>'#888', 'stop-circle'=>'#888', 'stats'=>'#444', 'switch_off'=>'#999', 'technic'=>'#999', 'timespent'=>'#555',
4385  'uncheck'=>'#800', 'uparrow'=>'#555', 'user-cog'=>'#999', 'country'=>'#aaa', 'globe-americas'=>'#aaa', 'region'=>'#aaa', 'state'=>'#aaa',
4386  'website'=>'#304', 'workstation'=>'#a69944'
4387  );
4388  if (isset($arrayconvpictotocolor[$pictowithouttext]) && strpos($picto, '_nocolor') === false) {
4389  $facolor = $arrayconvpictotocolor[$pictowithouttext];
4390  }
4391 
4392  // This snippet only needed since function img_edit accepts only one additional parameter: no separate one for css only.
4393  // class/style need to be extracted to avoid duplicate class/style validation errors when $moreatt is added to the end of the attributes.
4394  $morestyle = '';
4395  $reg = array();
4396  if (preg_match('/class="([^"]+)"/', $moreatt, $reg)) {
4397  $morecss .= ($morecss ? ' ' : '').$reg[1];
4398  $moreatt = str_replace('class="'.$reg[1].'"', '', $moreatt);
4399  }
4400  if (preg_match('/style="([^"]+)"/', $moreatt, $reg)) {
4401  $morestyle = $reg[1];
4402  $moreatt = str_replace('style="'.$reg[1].'"', '', $moreatt);
4403  }
4404  $moreatt = trim($moreatt);
4405 
4406  $enabledisablehtml = '<span class="'.$fa.' '.$fakey.($marginleftonlyshort ? ($marginleftonlyshort == 1 ? ' marginleftonlyshort' : ' marginleftonly') : '');
4407  $enabledisablehtml .= ($morecss ? ' '.$morecss : '').'" style="'.($fasize ? ('font-size: '.$fasize.';') : '').($facolor ? (' color: '.$facolor.';') : '').($morestyle ? ' '.$morestyle : '').'"'.(($notitle || empty($titlealt)) ? '' : ' title="'.dol_escape_htmltag($titlealt).'"').($moreatt ? ' '.$moreatt : '').'>';
4408  /*if (!empty($conf->global->MAIN_OPTIMIZEFORTEXTBROWSER)) {
4409  $enabledisablehtml .= $titlealt;
4410  }*/
4411  $enabledisablehtml .= '</span>';
4412 
4413  return $enabledisablehtml;
4414  }
4415 
4416  if (!empty($conf->global->MAIN_OVERWRITE_THEME_PATH)) {
4417  $path = $conf->global->MAIN_OVERWRITE_THEME_PATH.'/theme/'.$theme; // If the theme does not have the same name as the module
4418  } elseif (!empty($conf->global->MAIN_OVERWRITE_THEME_RES)) {
4419  $path = $conf->global->MAIN_OVERWRITE_THEME_RES.'/theme/'.$conf->global->MAIN_OVERWRITE_THEME_RES; // To allow an external module to overwrite image resources whatever is activated theme
4420  } elseif (!empty($conf->modules_parts['theme']) && array_key_exists($theme, $conf->modules_parts['theme'])) {
4421  $path = $theme.'/theme/'.$theme; // If the theme have the same name as the module
4422  }
4423 
4424  // If we ask an image into $url/$mymodule/img (instead of default path)
4425  $regs = array();
4426  if (preg_match('/^([^@]+)@([^@]+)$/i', $picto, $regs)) {
4427  $picto = $regs[1];
4428  $path = $regs[2]; // $path is $mymodule
4429  }
4430 
4431  // Clean parameters
4432  if (!preg_match('/(\.png|\.gif|\.svg)$/i', $picto)) {
4433  $picto .= '.png';
4434  }
4435  // If alt path are defined, define url where img file is, according to physical path
4436  // ex: array(["main"]=>"/home/maindir/htdocs", ["alt0"]=>"/home/moddir0/htdocs", ...)
4437  foreach ($conf->file->dol_document_root as $type => $dirroot) {
4438  if ($type == 'main') {
4439  continue;
4440  }
4441  // This need a lot of time, that's why enabling alternative dir like "custom" dir is not recommanded
4442  if (file_exists($dirroot.'/'.$path.'/img/'.$picto)) {
4443  $url = DOL_URL_ROOT.$conf->file->dol_url_root[$type];
4444  break;
4445  }
4446  }
4447 
4448  // $url is '' or '/custom', $path is current theme or
4449  $fullpathpicto = $url.'/'.$path.'/img/'.$picto;
4450  }
4451 
4452  if ($srconly) {
4453  return $fullpathpicto;
4454  }
4455 
4456  // tag title is used for tooltip on <a>, tag alt can be used with very simple text on image for blind people
4457  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
4458 }
4459 
4473 function img_object($titlealt, $picto, $moreatt = '', $pictoisfullpath = false, $srconly = 0, $notitle = 0)
4474 {
4475  if (strpos($picto, '^') === 0) {
4476  return img_picto($titlealt, str_replace('^', '', $picto), $moreatt, $pictoisfullpath, $srconly, $notitle);
4477  } else {
4478  return img_picto($titlealt, 'object_'.$picto, $moreatt, $pictoisfullpath, $srconly, $notitle);
4479  }
4480 }
4481 
4493 function img_weather($titlealt, $picto, $moreatt = '', $pictoisfullpath = 0, $morecss = '')
4494 {
4495  global $conf;
4496 
4497  if (is_numeric($picto)) {
4498  //$leveltopicto = array(0=>'weather-clear.png', 1=>'weather-few-clouds.png', 2=>'weather-clouds.png', 3=>'weather-many-clouds.png', 4=>'weather-storm.png');
4499  //$picto = $leveltopicto[$picto];
4500  return '<i class="fa fa-weather-level'.$picto.'"></i>';
4501  } elseif (!preg_match('/(\.png|\.gif)$/i', $picto)) {
4502  $picto .= '.png';
4503  }
4504 
4505  $path = DOL_URL_ROOT.'/theme/'.$conf->theme.'/img/weather/'.$picto;
4506 
4507  return img_picto($titlealt, $path, $moreatt, 1, 0, 0, '', $morecss);
4508 }
4509 
4521 function img_picto_common($titlealt, $picto, $moreatt = '', $pictoisfullpath = 0, $notitle = 0)
4522 {
4523  global $conf;
4524 
4525  if (!preg_match('/(\.png|\.gif)$/i', $picto)) {
4526  $picto .= '.png';
4527  }
4528 
4529  if ($pictoisfullpath) {
4530  $path = $picto;
4531  } else {
4532  $path = DOL_URL_ROOT.'/theme/common/'.$picto;
4533 
4534  if (!empty($conf->global->MAIN_MODULE_CAN_OVERWRITE_COMMONICONS)) {
4535  $themepath = DOL_DOCUMENT_ROOT.'/theme/'.$conf->theme.'/img/'.$picto;
4536 
4537  if (file_exists($themepath)) {
4538  $path = $themepath;
4539  }
4540  }
4541  }
4542 
4543  return img_picto($titlealt, $path, $moreatt, 1, 0, $notitle);
4544 }
4545 
4559 function img_action($titlealt, $numaction, $picto = '', $moreatt = '')
4560 {
4561  global $langs;
4562 
4563  if (empty($titlealt) || $titlealt == 'default') {
4564  if ($numaction == '-1' || $numaction == 'ST_NO') {
4565  $numaction = -1;
4566  $titlealt = $langs->transnoentitiesnoconv('ChangeDoNotContact');
4567  } elseif ($numaction == '0' || $numaction == 'ST_NEVER') {
4568  $numaction = 0;
4569  $titlealt = $langs->transnoentitiesnoconv('ChangeNeverContacted');
4570  } elseif ($numaction == '1' || $numaction == 'ST_TODO') {
4571  $numaction = 1;
4572  $titlealt = $langs->transnoentitiesnoconv('ChangeToContact');
4573  } elseif ($numaction == '2' || $numaction == 'ST_PEND') {
4574  $numaction = 2;
4575  $titlealt = $langs->transnoentitiesnoconv('ChangeContactInProcess');
4576  } elseif ($numaction == '3' || $numaction == 'ST_DONE') {
4577  $numaction = 3;
4578  $titlealt = $langs->transnoentitiesnoconv('ChangeContactDone');
4579  } else {
4580  $titlealt = $langs->transnoentitiesnoconv('ChangeStatus '.$numaction);
4581  $numaction = 0;
4582  }
4583  }
4584  if (!is_numeric($numaction)) {
4585  $numaction = 0;
4586  }
4587 
4588  return img_picto($titlealt, (empty($picto) ? 'stcomm'.$numaction.'.png' : $picto), $moreatt);
4589 }
4590 
4598 function img_pdf($titlealt = 'default', $size = 3)
4599 {
4600  global $langs;
4601 
4602  if ($titlealt == 'default') {
4603  $titlealt = $langs->trans('Show');
4604  }
4605 
4606  return img_picto($titlealt, 'pdf'.$size.'.png');
4607 }
4608 
4616 function img_edit_add($titlealt = 'default', $other = '')
4617 {
4618  global $langs;
4619 
4620  if ($titlealt == 'default') {
4621  $titlealt = $langs->trans('Add');
4622  }
4623 
4624  return img_picto($titlealt, 'edit_add.png', $other);
4625 }
4633 function img_edit_remove($titlealt = 'default', $other = '')
4634 {
4635  global $langs;
4636 
4637  if ($titlealt == 'default') {
4638  $titlealt = $langs->trans('Remove');
4639  }
4640 
4641  return img_picto($titlealt, 'edit_remove.png', $other);
4642 }
4643 
4652 function img_edit($titlealt = 'default', $float = 0, $other = '')
4653 {
4654  global $langs;
4655 
4656  if ($titlealt == 'default') {
4657  $titlealt = $langs->trans('Modify');
4658  }
4659 
4660  return img_picto($titlealt, 'edit.png', ($float ? 'style="float: '.($langs->tab_translate["DIRECTION"] == 'rtl' ? 'left' : 'right').'"' : "").($other ? ' '.$other : ''));
4661 }
4662 
4671 function img_view($titlealt = 'default', $float = 0, $other = 'class="valignmiddle"')
4672 {
4673  global $langs;
4674 
4675  if ($titlealt == 'default') {
4676  $titlealt = $langs->trans('View');
4677  }
4678 
4679  $moreatt = ($float ? 'style="float: right" ' : '').$other;
4680 
4681  return img_picto($titlealt, 'eye', $moreatt);
4682 }
4683 
4692 function img_delete($titlealt = 'default', $other = 'class="pictodelete"', $morecss = '')
4693 {
4694  global $langs;
4695 
4696  if ($titlealt == 'default') {
4697  $titlealt = $langs->trans('Delete');
4698  }
4699 
4700  return img_picto($titlealt, 'delete.png', $other, false, 0, 0, '', $morecss);
4701 }
4702 
4710 function img_printer($titlealt = "default", $other = '')
4711 {
4712  global $langs;
4713  if ($titlealt == "default") {
4714  $titlealt = $langs->trans("Print");
4715  }
4716  return img_picto($titlealt, 'printer.png', $other);
4717 }
4718 
4726 function img_split($titlealt = 'default', $other = 'class="pictosplit"')
4727 {
4728  global $langs;
4729 
4730  if ($titlealt == 'default') {
4731  $titlealt = $langs->trans('Split');
4732  }
4733 
4734  return img_picto($titlealt, 'split.png', $other);
4735 }
4736 
4744 function img_help($usehelpcursor = 1, $usealttitle = 1)
4745 {
4746  global $langs;
4747 
4748  if ($usealttitle) {
4749  if (is_string($usealttitle)) {
4750  $usealttitle = dol_escape_htmltag($usealttitle);
4751  } else {
4752  $usealttitle = $langs->trans('Info');
4753  }
4754  }
4755 
4756  return img_picto($usealttitle, 'info.png', 'style="vertical-align: middle;'.($usehelpcursor == 1 ? ' cursor: help' : ($usehelpcursor == 2 ? ' cursor: pointer' : '')).'"');
4757 }
4758 
4765 function img_info($titlealt = 'default')
4766 {
4767  global $langs;
4768 
4769  if ($titlealt == 'default') {
4770  $titlealt = $langs->trans('Informations');
4771  }
4772 
4773  return img_picto($titlealt, 'info.png', 'style="vertical-align: middle;"');
4774 }
4775 
4784 function img_warning($titlealt = 'default', $moreatt = '', $morecss = 'pictowarning')
4785 {
4786  global $langs;
4787 
4788  if ($titlealt == 'default') {
4789  $titlealt = $langs->trans('Warning');
4790  }
4791 
4792  //return '<div class="imglatecoin">'.img_picto($titlealt, 'warning_white.png', 'class="pictowarning valignmiddle"'.($moreatt ? ($moreatt == '1' ? ' style="float: right"' : ' '.$moreatt): '')).'</div>';
4793  return img_picto($titlealt, 'warning.png', 'class="'.$morecss.'"'.($moreatt ? ($moreatt == '1' ? ' style="float: right"' : ' '.$moreatt) : ''));
4794 }
4795 
4802 function img_error($titlealt = 'default')
4803 {
4804  global $langs;
4805 
4806  if ($titlealt == 'default') {
4807  $titlealt = $langs->trans('Error');
4808  }
4809 
4810  return img_picto($titlealt, 'error.png');
4811 }
4812 
4820 function img_next($titlealt = 'default', $moreatt = '')
4821 {
4822  global $langs;
4823 
4824  if ($titlealt == 'default') {
4825  $titlealt = $langs->trans('Next');
4826  }
4827 
4828  //return img_picto($titlealt, 'next.png', $moreatt);
4829  return '<span class="fa fa-chevron-right paddingright paddingleft" title="'.dol_escape_htmltag($titlealt).'"></span>';
4830 }
4831 
4839 function img_previous($titlealt = 'default', $moreatt = '')
4840 {
4841  global $langs;
4842 
4843  if ($titlealt == 'default') {
4844  $titlealt = $langs->trans('Previous');
4845  }
4846 
4847  //return img_picto($titlealt, 'previous.png', $moreatt);
4848  return '<span class="fa fa-chevron-left paddingright paddingleft" title="'.dol_escape_htmltag($titlealt).'"></span>';
4849 }
4850 
4859 function img_down($titlealt = 'default', $selected = 0, $moreclass = '')
4860 {
4861  global $langs;
4862 
4863  if ($titlealt == 'default') {
4864  $titlealt = $langs->trans('Down');
4865  }
4866 
4867  return img_picto($titlealt, ($selected ? '1downarrow_selected.png' : '1downarrow.png'), 'class="imgdown'.($moreclass ? " ".$moreclass : "").'"');
4868 }
4869 
4878 function img_up($titlealt = 'default', $selected = 0, $moreclass = '')
4879 {
4880  global $langs;
4881 
4882  if ($titlealt == 'default') {
4883  $titlealt = $langs->trans('Up');
4884  }
4885 
4886  return img_picto($titlealt, ($selected ? '1uparrow_selected.png' : '1uparrow.png'), 'class="imgup'.($moreclass ? " ".$moreclass : "").'"');
4887 }
4888 
4897 function img_left($titlealt = 'default', $selected = 0, $moreatt = '')
4898 {
4899  global $langs;
4900 
4901  if ($titlealt == 'default') {
4902  $titlealt = $langs->trans('Left');
4903  }
4904 
4905  return img_picto($titlealt, ($selected ? '1leftarrow_selected.png' : '1leftarrow.png'), $moreatt);
4906 }
4907 
4916 function img_right($titlealt = 'default', $selected = 0, $moreatt = '')
4917 {
4918  global $langs;
4919 
4920  if ($titlealt == 'default') {
4921  $titlealt = $langs->trans('Right');
4922  }
4923 
4924  return img_picto($titlealt, ($selected ? '1rightarrow_selected.png' : '1rightarrow.png'), $moreatt);
4925 }
4926 
4934 function img_allow($allow, $titlealt = 'default')
4935 {
4936  global $langs;
4937 
4938  if ($titlealt == 'default') {
4939  $titlealt = $langs->trans('Active');
4940  }
4941 
4942  if ($allow == 1) {
4943  return img_picto($titlealt, 'tick.png');
4944  }
4945 
4946  return '-';
4947 }
4948 
4956 function img_credit_card($brand, $morecss = null)
4957 {
4958  if (is_null($morecss)) {
4959  $morecss = 'fa-2x';
4960  }
4961 
4962  if ($brand == 'visa' || $brand == 'Visa') {
4963  $brand = 'cc-visa';
4964  } elseif ($brand == 'mastercard' || $brand == 'MasterCard') {
4965  $brand = 'cc-mastercard';
4966  } elseif ($brand == 'amex' || $brand == 'American Express') {
4967  $brand = 'cc-amex';
4968  } elseif ($brand == 'discover' || $brand == 'Discover') {
4969  $brand = 'cc-discover';
4970  } elseif ($brand == 'jcb' || $brand == 'JCB') {
4971  $brand = 'cc-jcb';
4972  } elseif ($brand == 'diners' || $brand == 'Diners club') {
4973  $brand = 'cc-diners-club';
4974  } elseif (!in_array($brand, array('cc-visa', 'cc-mastercard', 'cc-amex', 'cc-discover', 'cc-jcb', 'cc-diners-club'))) {
4975  $brand = 'credit-card';
4976  }
4977 
4978  return '<span class="fa fa-'.$brand.' fa-fw'.($morecss ? ' '.$morecss : '').'"></span>';
4979 }
4980 
4989 function img_mime($file, $titlealt = '', $morecss = '')
4990 {
4991  require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
4992 
4993  $mimetype = dol_mimetype($file, '', 1);
4994  $mimeimg = dol_mimetype($file, '', 2);
4995  $mimefa = dol_mimetype($file, '', 4);
4996 
4997  if (empty($titlealt)) {
4998  $titlealt = 'Mime type: '.$mimetype;
4999  }
5000 
5001  //return img_picto_common($titlealt, 'mime/'.$mimeimg, 'class="'.$morecss.'"');
5002  return '<i class="fa fa-'.$mimefa.' paddingright'.($morecss ? ' '.$morecss : '').'"'.($titlealt ? ' title="'.$titlealt.'"' : '').'></i>';
5003 }
5004 
5005 
5013 function img_search($titlealt = 'default', $other = '')
5014 {
5015  global $conf, $langs;
5016 
5017  if ($titlealt == 'default') {
5018  $titlealt = $langs->trans('Search');
5019  }
5020 
5021  $img = img_picto($titlealt, 'search.png', $other, false, 1);
5022 
5023  $input = '<input type="image" class="liste_titre" name="button_search" src="'.$img.'" ';
5024  $input .= 'value="'.dol_escape_htmltag($titlealt).'" title="'.dol_escape_htmltag($titlealt).'" >';
5025 
5026  return $input;
5027 }
5028 
5036 function img_searchclear($titlealt = 'default', $other = '')
5037 {
5038  global $conf, $langs;
5039 
5040  if ($titlealt == 'default') {
5041  $titlealt = $langs->trans('Search');
5042  }
5043 
5044  $img = img_picto($titlealt, 'searchclear.png', $other, false, 1);
5045 
5046  $input = '<input type="image" class="liste_titre" name="button_removefilter" src="'.$img.'" ';
5047  $input .= 'value="'.dol_escape_htmltag($titlealt).'" title="'.dol_escape_htmltag($titlealt).'" >';
5048 
5049  return $input;
5050 }
5051 
5063 function info_admin($text, $infoonimgalt = 0, $nodiv = 0, $admin = '1', $morecss = 'hideonsmartphone', $textfordropdown = '')
5064 {
5065  global $conf, $langs;
5066 
5067  if ($infoonimgalt) {
5068  $result = img_picto($text, 'info', 'class="'.($morecss ? ' '.$morecss : '').'"');
5069  } else {
5070  if (empty($conf->use_javascript_ajax)) {
5071  $textfordropdown = '';
5072  }
5073 
5074  $class = (empty($admin) ? 'undefined' : ($admin == '1' ? 'info' : $admin));
5075  $result = ($nodiv ? '' : '<div class="'.$class.($morecss ? ' '.$morecss : '').($textfordropdown ? ' hidden' : '').'">').'<span class="fa fa-info-circle" title="'.dol_escape_htmltag($admin ? $langs->trans('InfoAdmin') : $langs->trans('Note')).'"></span> '.$text.($nodiv ? '' : '</div>');
5076 
5077  if ($textfordropdown) {
5078  $tmpresult = '<span class="'.$class.'text opacitymedium cursorpointer">'.$langs->trans($textfordropdown).' '.img_picto($langs->trans($textfordropdown), '1downarrow').'</span>';
5079  $tmpresult .= '<script nonce="'.getNonce().'" type="text/javascript">
5080  jQuery(document).ready(function() {
5081  jQuery(".'.$class.'text").click(function() {
5082  console.log("toggle text");
5083  jQuery(".'.$class.'").toggle();
5084  });
5085  });
5086  </script>';
5087 
5088  $result = $tmpresult.$result;
5089  }
5090  }
5091 
5092  return $result;
5093 }
5094 
5095 
5107 function dol_print_error($db = '', $error = '', $errors = null)
5108 {
5109  global $conf, $langs, $argv;
5110  global $dolibarr_main_prod;
5111 
5112  $out = '';
5113  $syslog = '';
5114 
5115  // If error occurs before the $lang object was loaded
5116  if (!$langs) {
5117  require_once DOL_DOCUMENT_ROOT.'/core/class/translate.class.php';
5118  $langs = new Translate('', $conf);
5119  $langs->load("main");
5120  }
5121 
5122  // Load translation files required by the error messages
5123  $langs->loadLangs(array('main', 'errors'));
5124 
5125  if ($_SERVER['DOCUMENT_ROOT']) { // Mode web
5126  $out .= $langs->trans("DolibarrHasDetectedError").".<br>\n";
5127  if (getDolGlobalInt('MAIN_FEATURES_LEVEL') > 0) {
5128  $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";
5129  }
5130  $out .= $langs->trans("InformationToHelpDiagnose").":<br>\n";
5131 
5132  $out .= "<b>".$langs->trans("Date").":</b> ".dol_print_date(time(), 'dayhourlog')."<br>\n";
5133  $out .= "<b>".$langs->trans("Dolibarr").":</b> ".DOL_VERSION." - https://www.dolibarr.org<br>\n";
5134  if (isset($conf->global->MAIN_FEATURES_LEVEL)) {
5135  $out .= "<b>".$langs->trans("LevelOfFeature").":</b> ".getDolGlobalInt('MAIN_FEATURES_LEVEL')."<br>\n";
5136  }
5137  if (function_exists("phpversion")) {
5138  $out .= "<b>".$langs->trans("PHP").":</b> ".phpversion()."<br>\n";
5139  }
5140  $out .= "<b>".$langs->trans("Server").":</b> ".(isset($_SERVER["SERVER_SOFTWARE"]) ? dol_htmlentities($_SERVER["SERVER_SOFTWARE"], ENT_COMPAT) : '')."<br>\n";
5141  if (function_exists("php_uname")) {
5142  $out .= "<b>".$langs->trans("OS").":</b> ".php_uname()."<br>\n";
5143  }
5144  $out .= "<b>".$langs->trans("UserAgent").":</b> ".(isset($_SERVER["HTTP_USER_AGENT"]) ? dol_htmlentities($_SERVER["HTTP_USER_AGENT"], ENT_COMPAT) : '')."<br>\n";
5145  $out .= "<br>\n";
5146  $out .= "<b>".$langs->trans("RequestedUrl").":</b> ".dol_htmlentities($_SERVER["REQUEST_URI"], ENT_COMPAT)."<br>\n";
5147  $out .= "<b>".$langs->trans("Referer").":</b> ".(isset($_SERVER["HTTP_REFERER"]) ? dol_htmlentities($_SERVER["HTTP_REFERER"], ENT_COMPAT) : '')."<br>\n";
5148  $out .= "<b>".$langs->trans("MenuManager").":</b> ".(isset($conf->standard_menu) ? dol_htmlentities($conf->standard_menu, ENT_COMPAT) : '')."<br>\n";
5149  $out .= "<br>\n";
5150  $syslog .= "url=".dol_escape_htmltag($_SERVER["REQUEST_URI"]);
5151  $syslog .= ", query_string=".dol_escape_htmltag($_SERVER["QUERY_STRING"]);
5152  } else { // Mode CLI
5153  $out .= '> '.$langs->transnoentities("ErrorInternalErrorDetected").":\n".$argv[0]."\n";
5154  $syslog .= "pid=".dol_getmypid();
5155  }
5156 
5157  if (!empty($conf->modules)) {
5158  $out .= "<b>".$langs->trans("Modules").":</b> ".join(', ', $conf->modules)."<br>\n";
5159  }
5160 
5161  if (is_object($db)) {
5162  if ($_SERVER['DOCUMENT_ROOT']) { // Mode web
5163  $out .= "<b>".$langs->trans("DatabaseTypeManager").":</b> ".$db->type."<br>\n";
5164  $lastqueryerror = $db->lastqueryerror();
5165  if (!utf8_check($lastqueryerror)) {
5166  $lastqueryerror = "SQL error string is not a valid UTF8 string. We can't show it.";
5167  }
5168  $out .= "<b>".$langs->trans("RequestLastAccessInError").":</b> ".($lastqueryerror ? dol_escape_htmltag($lastqueryerror) : $langs->trans("ErrorNoRequestInError"))."<br>\n";
5169  $out .= "<b>".$langs->trans("ReturnCodeLastAccessInError").":</b> ".($db->lasterrno() ? dol_escape_htmltag($db->lasterrno()) : $langs->trans("ErrorNoRequestInError"))."<br>\n";
5170  $out .= "<b>".$langs->trans("InformationLastAccessInError").":</b> ".($db->lasterror() ? dol_escape_htmltag($db->lasterror()) : $langs->trans("ErrorNoRequestInError"))."<br>\n";
5171  $out .= "<br>\n";
5172  } else { // Mode CLI
5173  // No dol_escape_htmltag for output, we are in CLI mode
5174  $out .= '> '.$langs->transnoentities("DatabaseTypeManager").":\n".$db->type."\n";
5175  $out .= '> '.$langs->transnoentities("RequestLastAccessInError").":\n".($db->lastqueryerror() ? $db->lastqueryerror() : $langs->transnoentities("ErrorNoRequestInError"))."\n";
5176  $out .= '> '.$langs->transnoentities("ReturnCodeLastAccessInError").":\n".($db->lasterrno() ? $db->lasterrno() : $langs->transnoentities("ErrorNoRequestInError"))."\n";
5177  $out .= '> '.$langs->transnoentities("InformationLastAccessInError").":\n".($db->lasterror() ? $db->lasterror() : $langs->transnoentities("ErrorNoRequestInError"))."\n";
5178  }
5179  $syslog .= ", sql=".$db->lastquery();
5180  $syslog .= ", db_error=".$db->lasterror();
5181  }
5182 
5183  if ($error || $errors) {
5184  $langs->load("errors");
5185 
5186  // Merge all into $errors array
5187  if (is_array($error) && is_array($errors)) {
5188  $errors = array_merge($error, $errors);
5189  } elseif (is_array($error)) {
5190  $errors = $error;
5191  } elseif (is_array($errors)) {
5192  $errors = array_merge(array($error), $errors);
5193  } else {
5194  $errors = array_merge(array($error), array($errors));
5195  }
5196 
5197  foreach ($errors as $msg) {
5198  if (empty($msg)) {
5199  continue;
5200  }
5201  if ($_SERVER['DOCUMENT_ROOT']) { // Mode web
5202  $out .= "<b>".$langs->trans("Message").":</b> ".dol_escape_htmltag($msg)."<br>\n";
5203  } else // Mode CLI
5204  {
5205  $out .= '> '.$langs->transnoentities("Message").":\n".$msg."\n";
5206  }
5207  $syslog .= ", msg=".$msg;
5208  }
5209  }
5210  if (empty($dolibarr_main_prod) && $_SERVER['DOCUMENT_ROOT'] && function_exists('xdebug_print_function_stack') && function_exists('xdebug_call_file')) {
5211  xdebug_print_function_stack();
5212  $out .= '<b>XDebug informations:</b>'."<br>\n";
5213  $out .= 'File: '.xdebug_call_file()."<br>\n";
5214  $out .= 'Line: '.xdebug_call_line()."<br>\n";
5215  $out .= 'Function: '.xdebug_call_function()."<br>\n";
5216  $out .= "<br>\n";
5217  }
5218 
5219  // Return a http header with error code if possible
5220  if (!headers_sent()) {
5221  if (function_exists('top_httphead')) { // In CLI context, the method does not exists
5222  top_httphead();
5223  }
5224  //http_response_code(500); // If we use 500, message is not ouput with some command line tools
5225  http_response_code(202); // If we use 202, this is not really an error message, but this allow to ouput message on command line tools
5226  }
5227 
5228  if (empty($dolibarr_main_prod)) {
5229  print $out;
5230  } else {
5231  if (empty($langs->defaultlang)) {
5232  $langs->setDefaultLang();
5233  }
5234  $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.
5235  // This should not happen, except if there is a bug somewhere. Enabled and check log in such case.
5236  print 'This website or feature is currently temporarly not available or failed after a technical error.<br><br>This may be due to a maintenance operation. Current status of operation ('.dol_print_date(dol_now(), 'dayhourrfc').') are on next line...<br><br>'."\n";
5237  print $langs->trans("DolibarrHasDetectedError").'. ';
5238  print $langs->trans("YouCanSetOptionDolibarrMainProdToZero");
5239  define("MAIN_CORE_ERROR", 1);
5240  }
5241 
5242  dol_syslog("Error ".$syslog, LOG_ERR);
5243 }
5244 
5255 function dol_print_error_email($prefixcode, $errormessage = '', $errormessages = array(), $morecss = 'error', $email = '')
5256 {
5257  global $langs, $conf;
5258 
5259  if (empty($email)) {
5260  $email = $conf->global->MAIN_INFO_SOCIETE_MAIL;
5261  }
5262 
5263  $langs->load("errors");
5264  $now = dol_now();
5265 
5266  print '<br><div class="center login_main_message"><div class="'.$morecss.'">';
5267  print $langs->trans("ErrorContactEMail", $email, $prefixcode.'-'.dol_print_date($now, '%Y%m%d%H%M%S'));
5268  if ($errormessage) {
5269  print '<br><br>'.$errormessage;
5270  }
5271  if (is_array($errormessages) && count($errormessages)) {
5272  foreach ($errormessages as $mesgtoshow) {
5273  print '<br><br>'.$mesgtoshow;
5274  }
5275  }
5276  print '</div></div>';
5277 }
5278 
5295 function print_liste_field_titre($name, $file = "", $field = "", $begin = "", $moreparam = "", $moreattrib = "", $sortfield = "", $sortorder = "", $prefix = "", $tooltip = "", $forcenowrapcolumntitle = 0)
5296 {
5297  print getTitleFieldOfList($name, 0, $file, $field, $begin, $moreparam, $moreattrib, $sortfield, $sortorder, $prefix, 0, $tooltip, $forcenowrapcolumntitle);
5298 }
5299 
5318 function getTitleFieldOfList($name, $thead = 0, $file = "", $field = "", $begin = "", $moreparam = "", $moreattrib = "", $sortfield = "", $sortorder = "", $prefix = "", $disablesortlink = 0, $tooltip = '', $forcenowrapcolumntitle = 0)
5319 {
5320  global $conf, $langs, $form;
5321  //print "$name, $file, $field, $begin, $options, $moreattrib, $sortfield, $sortorder<br>\n";
5322 
5323  if ($moreattrib == 'class="right"') {
5324  $prefix .= 'right '; // For backward compatibility
5325  }
5326 
5327  $sortorder = strtoupper($sortorder);
5328  $out = '';
5329  $sortimg = '';
5330 
5331  $tag = 'th';
5332  if ($thead == 2) {
5333  $tag = 'div';
5334  }
5335 
5336  $tmpsortfield = explode(',', $sortfield);
5337  $sortfield1 = trim($tmpsortfield[0]); // If $sortfield is 'd.datep,d.id', it becomes 'd.datep'
5338  $tmpfield = explode(',', $field);
5339  $field1 = trim($tmpfield[0]); // If $field is 'd.datep,d.id', it becomes 'd.datep'
5340 
5341  if (empty($conf->global->MAIN_DISABLE_WRAPPING_ON_COLUMN_TITLE) && empty($forcenowrapcolumntitle)) {
5342  $prefix = 'wrapcolumntitle '.$prefix;
5343  }
5344 
5345  //var_dump('field='.$field.' field1='.$field1.' sortfield='.$sortfield.' sortfield1='.$sortfield1);
5346  // If field is used as sort criteria we use a specific css class liste_titre_sel
5347  // Example if (sortfield,field)=("nom","xxx.nom") or (sortfield,field)=("nom","nom")
5348  $liste_titre = 'liste_titre';
5349  if ($field1 && ($sortfield1 == $field1 || $sortfield1 == preg_replace("/^[^\.]+\./", "", $field1))) {
5350  $liste_titre = 'liste_titre_sel';
5351  }
5352 
5353  $tagstart = '<'.$tag.' class="'.$prefix.$liste_titre.'" '.$moreattrib;
5354  //$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)).'"' : '');
5355  $tagstart .= ($name && empty($conf->global->MAIN_DISABLE_WRAPPING_ON_COLUMN_TITLE) && empty($forcenowrapcolumntitle) && !dol_textishtml($name)) ? ' title="'.dol_escape_htmltag($langs->trans($name)).'"' : '';
5356  $tagstart .= '>';
5357 
5358  if (empty($thead) && $field && empty($disablesortlink)) { // If this is a sort field
5359  $options = preg_replace('/sortfield=([a-zA-Z0-9,\s\.]+)/i', '', (is_scalar($moreparam) ? $moreparam : ''));
5360  $options = preg_replace('/sortorder=([a-zA-Z0-9,\s\.]+)/i', '', $options);
5361  $options = preg_replace('/&+/i', '&', $options);
5362  if (!preg_match('/^&/', $options)) {
5363  $options = '&'.$options;
5364  }
5365 
5366  $sortordertouseinlink = '';
5367  if ($field1 != $sortfield1) { // We are on another field than current sorted field
5368  if (preg_match('/^DESC/i', $sortorder)) {
5369  $sortordertouseinlink .= str_repeat('desc,', count(explode(',', $field)));
5370  } else { // We reverse the var $sortordertouseinlink
5371  $sortordertouseinlink .= str_repeat('asc,', count(explode(',', $field)));
5372  }
5373  } else { // We are on field that is the first current sorting criteria
5374  if (preg_match('/^ASC/i', $sortorder)) { // We reverse the var $sortordertouseinlink
5375  $sortordertouseinlink .= str_repeat('desc,', count(explode(',', $field)));
5376  } else {
5377  $sortordertouseinlink .= str_repeat('asc,', count(explode(',', $field)));
5378  }
5379  }
5380  $sortordertouseinlink = preg_replace('/,$/', '', $sortordertouseinlink);
5381  $out .= '<a class="reposition" href="'.$file.'?sortfield='.$field.'&sortorder='.$sortordertouseinlink.'&begin='.$begin.$options.'"';
5382  //$out .= (empty($conf->global->MAIN_DISABLE_WRAPPING_ON_COLUMN_TITLE) ? ' title="'.dol_escape_htmltag($langs->trans($name)).'"' : '');
5383  $out .= '>';
5384  }
5385  if ($tooltip) {
5386  // You can also use 'TranslationString:keyfortooltiponclick' for a tooltip on click.
5387  if (preg_match('/:\w+$/', $tooltip)) {
5388  $tmptooltip = explode(':', $tooltip);
5389  } else {
5390  $tmptooltip = array($tooltip);
5391  }
5392  $out .= $form->textwithpicto($langs->trans($name), $langs->trans($tmptooltip[0]), 1, 'help', '', 0, 3, (empty($tmptooltip[1]) ? '' : 'extra_'.str_replace('.', '_', $field).'_'.$tmptooltip[1]));
5393  } else {
5394  $out .= $langs->trans($name);
5395  }
5396 
5397  if (empty($thead) && $field && empty($disablesortlink)) { // If this is a sort field
5398  $out .= '</a>';
5399  }
5400 
5401  if (empty($thead) && $field) { // If this is a sort field
5402  $options = preg_replace('/sortfield=([a-zA-Z0-9,\s\.]+)/i', '', (is_scalar($moreparam) ? $moreparam : ''));
5403  $options = preg_replace('/sortorder=([a-zA-Z0-9,\s\.]+)/i', '', $options);
5404  $options = preg_replace('/&+/i', '&', $options);
5405  if (!preg_match('/^&/', $options)) {
5406  $options = '&'.$options;
5407  }
5408 
5409  if (!$sortorder || ($field1 != $sortfield1)) {
5410  //$out.= '<a href="'.$file.'?sortfield='.$field.'&sortorder=asc&begin='.$begin.$options.'">'.img_down("A-Z",0).'</a>';
5411  //$out.= '<a href="'.$file.'?sortfield='.$field.'&sortorder=desc&begin='.$begin.$options.'">'.img_up("Z-A",0).'</a>';
5412  } else {
5413  if (preg_match('/^DESC/', $sortorder)) {
5414  //$out.= '<a href="'.$file.'?sortfield='.$field.'&sortorder=asc&begin='.$begin.$options.'">'.img_down("A-Z",0).'</a>';
5415  //$out.= '<a href="'.$file.'?sortfield='.$field.'&sortorder=desc&begin='.$begin.$options.'">'.img_up("Z-A",1).'</a>';
5416  $sortimg .= '<span class="nowrap">'.img_up("Z-A", 0, 'paddingright').'</span>';
5417  }
5418  if (preg_match('/^ASC/', $sortorder)) {
5419  //$out.= '<a href="'.$file.'?sortfield='.$field.'&sortorder=asc&begin='.$begin.$options.'">'.img_down("A-Z",1).'</a>';
5420  //$out.= '<a href="'.$file.'?sortfield='.$field.'&sortorder=desc&begin='.$begin.$options.'">'.img_up("Z-A",0).'</a>';
5421  $sortimg .= '<span class="nowrap">'.img_down("A-Z", 0, 'paddingright').'</span>';
5422  }
5423  }
5424  }
5425 
5426  $tagend = '</'.$tag.'>';
5427 
5428  $out = $tagstart.$sortimg.$out.$tagend;
5429 
5430  return $out;
5431 }
5432 
5441 function print_titre($title)
5442 {
5443  dol_syslog(__FUNCTION__." is deprecated", LOG_WARNING);
5444 
5445  print '<div class="titre">'.$title.'</div>';
5446 }
5447 
5459 function print_fiche_titre($title, $mesg = '', $picto = 'generic', $pictoisfullpath = 0, $id = '')
5460 {
5461  print load_fiche_titre($title, $mesg, $picto, $pictoisfullpath, $id);
5462 }
5463 
5477 function load_fiche_titre($titre, $morehtmlright = '', $picto = 'generic', $pictoisfullpath = 0, $id = '', $morecssontable = '', $morehtmlcenter = '')
5478 {
5479  global $conf;
5480 
5481  $return = '';
5482 
5483  if ($picto == 'setup') {
5484  $picto = 'generic';
5485  }
5486 
5487  $return .= "\n";
5488  $return .= '<table '.($id ? 'id="'.$id.'" ' : '').'class="centpercent notopnoleftnoright table-fiche-title'.($morecssontable ? ' '.$morecssontable : '').'">'; // maring bottom must be same than into print_barre_list
5489  $return .= '<tr class="titre">';
5490  if ($picto) {
5491  $return .= '<td class="nobordernopadding widthpictotitle valignmiddle col-picto">'.img_picto('', $picto, 'class="valignmiddle widthpictotitle pictotitle"', $pictoisfullpath).'</td>';
5492  }
5493  $return .= '<td class="nobordernopadding valignmiddle col-title">';
5494  $return .= '<div class="titre inline-block">'.$titre.'</div>';
5495  $return .= '</td>';
5496  if (dol_strlen($morehtmlcenter)) {
5497  $return .= '<td class="nobordernopadding center valignmiddle col-center">'.$morehtmlcenter.'</td>';
5498  }
5499  if (dol_strlen($morehtmlright)) {
5500  $return .= '<td class="nobordernopadding titre_right wordbreakimp right valignmiddle col-right">'.$morehtmlright.'</td>';
5501  }
5502  $return .= '</tr></table>'."\n";
5503 
5504  return $return;
5505 }
5506 
5530 function print_barre_liste($titre, $page, $file, $options = '', $sortfield = '', $sortorder = '', $morehtmlcenter = '', $num = -1, $totalnboflines = '', $picto = 'generic', $pictoisfullpath = 0, $morehtmlright = '', $morecss = '', $limit = -1, $hideselectlimit = 0, $hidenavigation = 0, $pagenavastextinput = 0, $morehtmlrightbeforearrow = '')
5531 {
5532  global $conf, $langs;
5533 
5534  $savlimit = $limit;
5535  $savtotalnboflines = $totalnboflines;
5536  $totalnboflines = abs((int) $totalnboflines);
5537 
5538  $page = (int) $page;
5539 
5540  if ($picto == 'setup') {
5541  $picto = 'title_setup.png';
5542  }
5543  if (($conf->browser->name == 'ie') && $picto == 'generic') {
5544  $picto = 'title.gif';
5545  }
5546  if ($limit < 0) {
5547  $limit = $conf->liste_limit;
5548  }
5549 
5550  if ($savlimit != 0 && (($num > $limit) || ($num == -1) || ($limit == 0))) {
5551  $nextpage = 1;
5552  } else {
5553  $nextpage = 0;
5554  }
5555  //print 'totalnboflines='.$totalnboflines.'-savlimit='.$savlimit.'-limit='.$limit.'-num='.$num.'-nextpage='.$nextpage.'-hideselectlimit='.$hideselectlimit.'-hidenavigation='.$hidenavigation;
5556 
5557  print "\n";
5558  print "<!-- Begin title -->\n";
5559  print '<table class="centpercent notopnoleftnoright table-fiche-title'.($morecss ? ' '.$morecss : '').'"><tr>'; // maring bottom must be same than into load_fiche_tire
5560 
5561  // Left
5562 
5563  if ($picto && $titre) {
5564  print '<td class="nobordernopadding widthpictotitle valignmiddle col-picto">'.img_picto('', $picto, 'class="valignmiddle pictotitle widthpictotitle"', $pictoisfullpath).'</td>';
5565  }
5566 
5567  print '<td class="nobordernopadding valignmiddle col-title">';
5568  print '<div class="titre inline-block">'.$titre;
5569  if (!empty($titre) && $savtotalnboflines >= 0 && (string) $savtotalnboflines != '') {
5570  print '<span class="opacitymedium colorblack paddingleft">('.$totalnboflines.')</span>';
5571  }
5572  print '</div></td>';
5573 
5574  // Center
5575  if ($morehtmlcenter && empty($conf->dol_optimize_smallscreen)) {
5576  print '<td class="nobordernopadding center valignmiddle col-center">'.$morehtmlcenter.'</td>';
5577  }
5578 
5579  // Right
5580  print '<td class="nobordernopadding valignmiddle right col-right">';
5581  print '<input type="hidden" name="pageplusoneold" value="'.((int) $page + 1).'">';
5582  if ($sortfield) {
5583  $options .= "&sortfield=".urlencode($sortfield);
5584  }
5585  if ($sortorder) {
5586  $options .= "&sortorder=".urlencode($sortorder);
5587  }
5588  // Show navigation bar
5589  $pagelist = '';
5590  if ($savlimit != 0 && ($page > 0 || $num > $limit)) {
5591  if ($totalnboflines) { // If we know total nb of lines
5592  // Define nb of extra page links before and after selected page + ... + first or last
5593  $maxnbofpage = (empty($conf->dol_optimize_smallscreen) ? 4 : 0);
5594 
5595  if ($limit > 0) {
5596  $nbpages = ceil($totalnboflines / $limit);
5597  } else {
5598  $nbpages = 1;
5599  }
5600  $cpt = ($page - $maxnbofpage);
5601  if ($cpt < 0) {
5602  $cpt = 0;
5603  }
5604 
5605  if ($cpt >= 1) {
5606  if (empty($pagenavastextinput)) {
5607  $pagelist .= '<li class="pagination"><a href="'.$file.'?page=0'.$options.'">1</a></li>';
5608  if ($cpt > 2) {
5609  $pagelist .= '<li class="pagination"><span class="inactive">...</span></li>';
5610  } elseif ($cpt == 2) {
5611  $pagelist .= '<li class="pagination"><a href="'.$file.'?page=1'.$options.'">2</a></li>';
5612  }
5613  }
5614  }
5615 
5616  do {
5617  if ($pagenavastextinput) {
5618  if ($cpt == $page) {
5619  $pagelist .= '<li class="pagination"><input type="text" class="'.($totalnboflines > 100 ? 'width40' : 'width25').' center pageplusone" name="pageplusone" value="'.($page + 1).'"></li>';
5620  $pagelist .= '/';
5621  }
5622  } else {
5623  if ($cpt == $page) {
5624  $pagelist .= '<li class="pagination"><span class="active">'.($page + 1).'</span></li>';
5625  } else {
5626  $pagelist .= '<li class="pagination"><a href="'.$file.'?page='.$cpt.$options.'">'.($cpt + 1).'</a></li>';
5627  }
5628  }
5629  $cpt++;
5630  } while ($cpt < $nbpages && $cpt <= ($page + $maxnbofpage));
5631 
5632  if (empty($pagenavastextinput)) {
5633  if ($cpt < $nbpages) {
5634  if ($cpt < $nbpages - 2) {
5635  $pagelist .= '<li class="pagination"><span class="inactive">...</span></li>';
5636  } elseif ($cpt == $nbpages - 2) {
5637  $pagelist .= '<li class="pagination"><a href="'.$file.'?page='.($nbpages - 2).$options.'">'.($nbpages - 1).'</a></li>';
5638  }
5639  $pagelist .= '<li class="pagination"><a href="'.$file.'?page='.($nbpages - 1).$options.'">'.$nbpages.'</a></li>';
5640  }
5641  } else {
5642  //var_dump($page.' '.$cpt.' '.$nbpages);
5643  $pagelist .= '<li class="pagination paginationlastpage"><a href="'.$file.'?page='.($nbpages - 1).$options.'">'.$nbpages.'</a></li>';
5644  }
5645  } else {
5646  $pagelist .= '<li class="pagination"><span class="active">'.($page + 1)."</li>";
5647  }
5648  }
5649 
5650  if ($savlimit || $morehtmlright || $morehtmlrightbeforearrow) {
5651  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
5652  }
5653 
5654  // js to autoselect page field on focus
5655  if ($pagenavastextinput) {
5656  print ajax_autoselect('.pageplusone');
5657  }
5658 
5659  print '</td>';
5660  print '</tr>';
5661 
5662  print '</table>'."\n";
5663 
5664  // Center
5665  if ($morehtmlcenter && !empty($conf->dol_optimize_smallscreen)) {
5666  print '<div class="nobordernopadding marginbottomonly center valignmiddle col-center centpercent">'.$morehtmlcenter.'</div>';
5667  }
5668 
5669  print "<!-- End title -->\n\n";
5670 }
5671 
5688 function print_fleche_navigation($page, $file, $options = '', $nextpage = 0, $betweenarrows = '', $afterarrows = '', $limit = -1, $totalnboflines = 0, $hideselectlimit = 0, $beforearrows = '', $hidenavigation = 0)
5689 {
5690  global $conf, $langs;
5691 
5692  print '<div class="pagination"><ul>';
5693  if ($beforearrows) {
5694  print '<li class="paginationbeforearrows">';
5695  print $beforearrows;
5696  print '</li>';
5697  }
5698 
5699  if (empty($hidenavigation)) {
5700  if ((int) $limit > 0 && empty($hideselectlimit)) {
5701  $pagesizechoices = '10:10,15:15,20:20,30:30,40:40,50:50,100:100,250:250,500:500,1000:1000';
5702  $pagesizechoices .= ',5000:5000,10000:10000,20000:20000';
5703  //$pagesizechoices.=',0:'.$langs->trans("All"); // Not yet supported
5704  //$pagesizechoices.=',2:2';
5705  if (!empty($conf->global->MAIN_PAGESIZE_CHOICES)) {
5706  $pagesizechoices = $conf->global->MAIN_PAGESIZE_CHOICES;
5707  }
5708 
5709  print '<li class="pagination">';
5710  print '<select class="flat selectlimit" name="limit" title="'.dol_escape_htmltag($langs->trans("MaxNbOfRecordPerPage")).'">';
5711  $tmpchoice = explode(',', $pagesizechoices);
5712  $tmpkey = $limit.':'.$limit;
5713  if (!in_array($tmpkey, $tmpchoice)) {
5714  $tmpchoice[] = $tmpkey;
5715  }
5716  $tmpkey = $conf->liste_limit.':'.$conf->liste_limit;
5717  if (!in_array($tmpkey, $tmpchoice)) {
5718  $tmpchoice[] = $tmpkey;
5719  }
5720  asort($tmpchoice, SORT_NUMERIC);
5721  foreach ($tmpchoice as $val) {
5722  $selected = '';
5723  $tmp = explode(':', $val);
5724  $key = $tmp[0];
5725  $val = $tmp[1];
5726  if ($key != '' && $val != '') {
5727  if ((int) $key == (int) $limit) {
5728  $selected = ' selected="selected"';
5729  }
5730  print '<option name="'.$key.'"'.$selected.'>'.dol_escape_htmltag($val).'</option>'."\n";
5731  }
5732  }
5733  print '</select>';
5734  if ($conf->use_javascript_ajax) {
5735  print '<!-- JS CODE TO ENABLE select limit to launch submit of page -->
5736  <script>
5737  jQuery(document).ready(function () {
5738  jQuery(".selectlimit").change(function() {
5739  console.log("Change limit. Send submit");
5740  $(this).parents(\'form:first\').submit();
5741  });
5742  });
5743  </script>
5744  ';
5745  }
5746  print '</li>';
5747  }
5748  if ($page > 0) {
5749  print '<li class="pagination paginationpage paginationpageleft"><a class="paginationprevious" href="'.$file.'?page='.($page - 1).$options.'"><i class="fa fa-chevron-left" title="'.dol_escape_htmltag($langs->trans("Previous")).'"></i></a></li>';
5750  }
5751  if ($betweenarrows) {
5752  print '<!--<div class="betweenarrows nowraponall inline-block">-->';
5753  print $betweenarrows;
5754  print '<!--</div>-->';
5755  }
5756  if ($nextpage > 0) {
5757  print '<li class="pagination paginationpage paginationpageright"><a class="paginationnext" href="'.$file.'?page='.($page + 1).$options.'"><i class="fa fa-chevron-right" title="'.dol_escape_htmltag($langs->trans("Next")).'"></i></a></li>';
5758  }
5759  if ($afterarrows) {
5760  print '<li class="paginationafterarrows">';
5761  print $afterarrows;
5762  print '</li>';
5763  }
5764  }
5765  print '</ul></div>'."\n";
5766 }
5767 
5768 
5780 function vatrate($rate, $addpercent = false, $info_bits = 0, $usestarfornpr = 0, $html = 0)
5781 {
5782  $morelabel = '';
5783 
5784  if (preg_match('/%/', $rate)) {
5785  $rate = str_replace('%', '', $rate);
5786  $addpercent = true;
5787  }
5788  $reg = array();
5789  if (preg_match('/\((.*)\)/', $rate, $reg)) {
5790  $morelabel = ' ('.$reg[1].')';
5791  $rate = preg_replace('/\s*'.preg_quote($morelabel, '/').'/', '', $rate);
5792  $morelabel = ' '.($html ? '<span class="opacitymedium">' : '').'('.$reg[1].')'.($html ? '</span>' : '');
5793  }
5794  if (preg_match('/\*/', $rate)) {
5795  $rate = str_replace('*', '', $rate);
5796  $info_bits |= 1;
5797  }
5798 
5799  // If rate is '9/9/9' we don't change it. If rate is '9.000' we apply price()
5800  if (!preg_match('/\//', $rate)) {
5801  $ret = price($rate, 0, '', 0, 0).($addpercent ? '%' : '');
5802  } else {
5803  // TODO Split on / and output with a price2num to have clean numbers without ton of 000.
5804  $ret = $rate.($addpercent ? '%' : '');
5805  }
5806  if (($info_bits & 1) && $usestarfornpr >= 0) {
5807  $ret .= ' *';
5808  }
5809  $ret .= $morelabel;
5810  return $ret;
5811 }
5812 
5813 
5829 function price($amount, $form = 0, $outlangs = '', $trunc = 1, $rounding = -1, $forcerounding = -1, $currency_code = '')
5830 {
5831  global $langs, $conf;
5832 
5833  // Clean parameters
5834  if (empty($amount)) {
5835  $amount = 0; // To have a numeric value if amount not defined or = ''
5836  }
5837  $amount = (is_numeric($amount) ? $amount : 0); // Check if amount is numeric, for example, an error occured when amount value = o (letter) instead 0 (number)
5838  if ($rounding == -1) {
5839  $rounding = min($conf->global->MAIN_MAX_DECIMALS_UNIT, $conf->global->MAIN_MAX_DECIMALS_TOT);
5840  }
5841  $nbdecimal = $rounding;
5842 
5843  if ($outlangs === 'none') {
5844  // Use international separators
5845  $dec = '.';
5846  $thousand = '';
5847  } else {
5848  // Output separators by default (french)
5849  $dec = ',';
5850  $thousand = ' ';
5851 
5852  // If $outlangs not forced, we use use language
5853  if (!is_object($outlangs)) {
5854  $outlangs = $langs;
5855  }
5856 
5857  if ($outlangs->transnoentitiesnoconv("SeparatorDecimal") != "SeparatorDecimal") {
5858  $dec = $outlangs->transnoentitiesnoconv("SeparatorDecimal");
5859  }
5860  if ($outlangs->transnoentitiesnoconv("SeparatorThousand") != "SeparatorThousand") {
5861  $thousand = $outlangs->transnoentitiesnoconv("SeparatorThousand");
5862  }
5863  if ($thousand == 'None') {
5864  $thousand = '';
5865  } elseif ($thousand == 'Space') {
5866  $thousand = ' ';
5867  }
5868  }
5869  //print "outlangs=".$outlangs->defaultlang." amount=".$amount." html=".$form." trunc=".$trunc." nbdecimal=".$nbdecimal." dec='".$dec."' thousand='".$thousand."'<br>";
5870 
5871  //print "amount=".$amount."-";
5872  $amount = str_replace(',', '.', $amount); // should be useless
5873  //print $amount."-";
5874  $datas = explode('.', $amount);
5875  $decpart = isset($datas[1]) ? $datas[1] : '';
5876  $decpart = preg_replace('/0+$/i', '', $decpart); // Supprime les 0 de fin de partie decimale
5877  //print "decpart=".$decpart."<br>";
5878  $end = '';
5879 
5880  // We increase nbdecimal if there is more decimal than asked (to not loose information)
5881  if (dol_strlen($decpart) > $nbdecimal) {
5882  $nbdecimal = dol_strlen($decpart);
5883  }
5884  // Si on depasse max
5885  if ($trunc && $nbdecimal > $conf->global->MAIN_MAX_DECIMALS_SHOWN) {
5886  $nbdecimal = $conf->global->MAIN_MAX_DECIMALS_SHOWN;
5887  if (preg_match('/\.\.\./i', $conf->global->MAIN_MAX_DECIMALS_SHOWN)) {
5888  // Si un affichage est tronque, on montre des ...
5889  $end = '...';
5890  }
5891  }
5892 
5893  // If force rounding
5894  if ((string) $forcerounding != '-1') {
5895  if ($forcerounding === 'MU') {
5896  $nbdecimal = $conf->global->MAIN_MAX_DECIMALS_UNIT;
5897  } elseif ($forcerounding === 'MT') {
5898  $nbdecimal = $conf->global->MAIN_MAX_DECIMALS_TOT;
5899  } elseif ($forcerounding >= 0) {
5900  $nbdecimal = $forcerounding;
5901  }
5902  }
5903 
5904  // Format number
5905  $output = number_format($amount, $nbdecimal, $dec, $thousand);
5906  if ($form) {
5907  $output = preg_replace('/\s/', '&nbsp;', $output);
5908  $output = preg_replace('/\'/', '&#039;', $output);
5909  }
5910  // Add symbol of currency if requested
5911  $cursymbolbefore = $cursymbolafter = '';
5912  if ($currency_code && is_object($outlangs)) {
5913  if ($currency_code == 'auto') {
5914  $currency_code = $conf->currency;
5915  }
5916 
5917  $listofcurrenciesbefore = array('AUD', 'CAD', 'CNY', 'COP', 'CLP', 'GBP', 'HKD', 'MXN', 'PEN', 'USD', 'CRC');
5918  $listoflanguagesbefore = array('nl_NL');
5919  if (in_array($currency_code, $listofcurrenciesbefore) || in_array($outlangs->defaultlang, $listoflanguagesbefore)) {
5920  $cursymbolbefore .= $outlangs->getCurrencySymbol($currency_code);
5921  } else {
5922  $tmpcur = $outlangs->getCurrencySymbol($currency_code);
5923  $cursymbolafter .= ($tmpcur == $currency_code ? ' '.$tmpcur : $tmpcur);
5924  }
5925  }
5926  $output = $cursymbolbefore.$output.$end.($cursymbolafter ? ' ' : '').$cursymbolafter;
5927 
5928  return $output;
5929 }
5930 
5955 function price2num($amount, $rounding = '', $option = 0)
5956 {
5957  global $langs, $conf;
5958 
5959  // Clean parameters
5960  if (is_null($amount)) {
5961  $amount = '';
5962  }
5963 
5964  // Round PHP function does not allow number like '1,234.56' nor '1.234,56' nor '1 234,56'
5965  // Numbers must be '1234.56'
5966  // Decimal delimiter for PHP and database SQL requests must be '.'
5967  $dec = ',';
5968  $thousand = ' ';
5969  if (is_null($langs)) { // $langs is not defined, we use english values.
5970  $dec = '.';
5971  $thousand = ',';
5972  } else {
5973  if ($langs->transnoentitiesnoconv("SeparatorDecimal") != "SeparatorDecimal") {
5974  $dec = $langs->transnoentitiesnoconv("SeparatorDecimal");
5975  }
5976  if ($langs->transnoentitiesnoconv("SeparatorThousand") != "SeparatorThousand") {
5977  $thousand = $langs->transnoentitiesnoconv("SeparatorThousand");
5978  }
5979  }
5980  if ($thousand == 'None') {
5981  $thousand = '';
5982  } elseif ($thousand == 'Space') {
5983  $thousand = ' ';
5984  }
5985  //print "amount=".$amount." html=".$form." trunc=".$trunc." nbdecimal=".$nbdecimal." dec='".$dec."' thousand='".$thousand."'<br>";
5986 
5987  // Convert value to universal number format (no thousand separator, '.' as decimal separator)
5988  if ($option != 1) { // If not a PHP number or unknown, we change or clean format
5989  //print "\n".'PP'.$amount.' - '.$dec.' - '.$thousand.' - '.intval($amount).'<br>';
5990  if (!is_numeric($amount)) {
5991  $amount = preg_replace('/[a-zA-Z\/\\\*\(\)<>\_]/', '', $amount);
5992  }
5993 
5994  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
5995  $amount = str_replace($thousand, '', $amount);
5996  }
5997 
5998  // Convert amount to format with dolibarr dec and thousand (this is because PHP convert a number
5999  // to format defined by LC_NUMERIC after a calculation and we want source format to be like defined by Dolibarr setup.
6000  // So if number was already a good number, it is converted into local Dolibarr setup.
6001  if (is_numeric($amount)) {
6002  // We put in temps value of decimal ("0.00001"). Works with 0 and 2.0E-5 and 9999.10
6003  $temps = sprintf("%0.10F", $amount - intval($amount)); // temps=0.0000000000 or 0.0000200000 or 9999.1000000000
6004  $temps = preg_replace('/([\.1-9])0+$/', '\\1', $temps); // temps=0. or 0.00002 or 9999.1
6005  $nbofdec = max(0, dol_strlen($temps) - 2); // -2 to remove "0."
6006  $amount = number_format($amount, $nbofdec, $dec, $thousand);
6007  }
6008  //print "QQ".$amount."<br>\n";
6009 
6010  // Now make replace (the main goal of function)
6011  if ($thousand != ',' && $thousand != '.') {
6012  $amount = str_replace(',', '.', $amount); // To accept 2 notations for french users
6013  }
6014 
6015  $amount = str_replace(' ', '', $amount); // To avoid spaces
6016  $amount = str_replace($thousand, '', $amount); // Replace of thousand before replace of dec to avoid pb if thousand is .
6017  $amount = str_replace($dec, '.', $amount);
6018 
6019  $amount = preg_replace('/[^0-9\-\.]/', '', $amount); // Clean non numeric chars (so it clean some UTF8 spaces for example.
6020  }
6021  //print ' XX'.$amount.' '.$rounding;
6022 
6023  // Now, $amount is a real PHP float number. We make a rounding if required.
6024  if ($rounding) {
6025  $nbofdectoround = '';
6026  if ($rounding == 'MU') {
6027  $nbofdectoround = $conf->global->MAIN_MAX_DECIMALS_UNIT;
6028  } elseif ($rounding == 'MT') {
6029  $nbofdectoround = $conf->global->MAIN_MAX_DECIMALS_TOT;
6030  } elseif ($rounding == 'MS') {
6031  $nbofdectoround = isset($conf->global->MAIN_MAX_DECIMALS_STOCK) ? $conf->global->MAIN_MAX_DECIMALS_STOCK : 5;
6032  } elseif ($rounding == 'CU') {
6033  $nbofdectoround = max($conf->global->MAIN_MAX_DECIMALS_UNIT, 8); // TODO Use param of currency
6034  } elseif ($rounding == 'CT') {
6035  $nbofdectoround = max($conf->global->MAIN_MAX_DECIMALS_TOT, 8); // TODO Use param of currency
6036  } elseif (is_numeric($rounding)) {
6037  $nbofdectoround = (int) $rounding;
6038  }
6039 
6040  //print " RR".$amount.' - '.$nbofdectoround.'<br>';
6041  if (dol_strlen($nbofdectoround)) {
6042  $amount = round(is_string($amount) ? (float) $amount : $amount, $nbofdectoround); // $nbofdectoround can be 0.
6043  } else {
6044  return 'ErrorBadParameterProvidedToFunction';
6045  }
6046  //print ' SS'.$amount.' - '.$nbofdec.' - '.$dec.' - '.$thousand.' - '.$nbofdectoround.'<br>';
6047 
6048  // Convert amount to format with dolibarr dec and thousand (this is because PHP convert a number
6049  // to format defined by LC_NUMERIC after a calculation and we want source format to be defined by Dolibarr setup.
6050  if (is_numeric($amount)) {
6051  // We put in temps value of decimal ("0.00001"). Works with 0 and 2.0E-5 and 9999.10
6052  $temps = sprintf("%0.10F", $amount - intval($amount)); // temps=0.0000000000 or 0.0000200000 or 9999.1000000000
6053  $temps = preg_replace('/([\.1-9])0+$/', '\\1', $temps); // temps=0. or 0.00002 or 9999.1
6054  $nbofdec = max(0, dol_strlen($temps) - 2); // -2 to remove "0."
6055  $amount = number_format($amount, min($nbofdec, $nbofdectoround), $dec, $thousand); // Convert amount to format with dolibarr dec and thousand
6056  }
6057  //print "TT".$amount.'<br>';
6058 
6059  // Always make replace because each math function (like round) replace
6060  // with local values and we want a number that has a SQL string format x.y
6061  if ($thousand != ',' && $thousand != '.') {
6062  $amount = str_replace(',', '.', $amount); // To accept 2 notations for french users
6063  }
6064 
6065  $amount = str_replace(' ', '', $amount); // To avoid spaces
6066  $amount = str_replace($thousand, '', $amount); // Replace of thousand before replace of dec to avoid pb if thousand is .
6067  $amount = str_replace($dec, '.', $amount);
6068 
6069  $amount = preg_replace('/[^0-9\-\.]/', '', $amount); // Clean non numeric chars (so it clean some UTF8 spaces for example.
6070  }
6071 
6072  return $amount;
6073 }
6074 
6087 function showDimensionInBestUnit($dimension, $unit, $type, $outputlangs, $round = -1, $forceunitoutput = 'no', $use_short_label = 0)
6088 {
6089  require_once DOL_DOCUMENT_ROOT.'/core/lib/product.lib.php';
6090 
6091  if (($forceunitoutput == 'no' && $dimension < 1 / 10000 && $unit < 90) || (is_numeric($forceunitoutput) && $forceunitoutput == -6)) {
6092  $dimension = $dimension * 1000000;
6093  $unit = $unit - 6;
6094  } elseif (($forceunitoutput == 'no' && $dimension < 1 / 10 && $unit < 90) || (is_numeric($forceunitoutput) && $forceunitoutput == -3)) {
6095  $dimension = $dimension * 1000;
6096  $unit = $unit - 3;
6097  } elseif (($forceunitoutput == 'no' && $dimension > 100000000 && $unit < 90) || (is_numeric($forceunitoutput) && $forceunitoutput == 6)) {
6098  $dimension = $dimension / 1000000;
6099  $unit = $unit + 6;
6100  } elseif (($forceunitoutput == 'no' && $dimension > 100000 && $unit < 90) || (is_numeric($forceunitoutput) && $forceunitoutput == 3)) {
6101  $dimension = $dimension / 1000;
6102  $unit = $unit + 3;
6103  }
6104  // Special case when we want output unit into pound or ounce
6105  /* TODO
6106  if ($unit < 90 && $type == 'weight' && is_numeric($forceunitoutput) && (($forceunitoutput == 98) || ($forceunitoutput == 99))
6107  {
6108  $dimension = // convert dimension from standard unit into ounce or pound
6109  $unit = $forceunitoutput;
6110  }
6111  if ($unit > 90 && $type == 'weight' && is_numeric($forceunitoutput) && $forceunitoutput < 90)
6112  {
6113  $dimension = // convert dimension from standard unit into ounce or pound
6114  $unit = $forceunitoutput;
6115  }*/
6116 
6117  $ret = price($dimension, 0, $outputlangs, 0, 0, $round);
6118  $ret .= ' '.measuringUnitString(0, $type, $unit, $use_short_label, $outputlangs);
6119 
6120  return $ret;
6121 }
6122 
6123 
6136 function get_localtax($vatrate, $local, $thirdparty_buyer = "", $thirdparty_seller = "", $vatnpr = 0)
6137 {
6138  global $db, $conf, $mysoc;
6139 
6140  if (empty($thirdparty_seller) || !is_object($thirdparty_seller)) {
6141  $thirdparty_seller = $mysoc;
6142  }
6143 
6144  dol_syslog("get_localtax tva=".$vatrate." local=".$local." thirdparty_buyer id=".(is_object($thirdparty_buyer) ? $thirdparty_buyer->id : '')."/country_code=".(is_object($thirdparty_buyer) ? $thirdparty_buyer->country_code : '')." thirdparty_seller id=".$thirdparty_seller->id."/country_code=".$thirdparty_seller->country_code." thirdparty_seller localtax1_assuj=".$thirdparty_seller->localtax1_assuj." thirdparty_seller localtax2_assuj=".$thirdparty_seller->localtax2_assuj);
6145 
6146  $vatratecleaned = $vatrate;
6147  $reg = array();
6148  if (preg_match('/^(.*)\s*\((.*)\)$/', $vatrate, $reg)) { // If vat is "xx (yy)"
6149  $vatratecleaned = trim($reg[1]);
6150  $vatratecode = $reg[2];
6151  }
6152 
6153  /*if ($thirdparty_buyer->country_code != $thirdparty_seller->country_code)
6154  {
6155  return 0;
6156  }*/
6157 
6158  // Some test to guess with no need to make database access
6159  if ($mysoc->country_code == 'ES') { // For spain localtaxes 1 and 2, tax is qualified if buyer use local tax
6160  if ($local == 1) {
6161  if (!$mysoc->localtax1_assuj || (string) $vatratecleaned == "0") {
6162  return 0;
6163  }
6164  if ($thirdparty_seller->id == $mysoc->id) {
6165  if (!$thirdparty_buyer->localtax1_assuj) {
6166  return 0;
6167  }
6168  } else {
6169  if (!$thirdparty_seller->localtax1_assuj) {
6170  return 0;
6171  }
6172  }
6173  }
6174 
6175  if ($local == 2) {
6176  //if (! $mysoc->localtax2_assuj || (string) $vatratecleaned == "0") return 0;
6177  if (!$mysoc->localtax2_assuj) {
6178  return 0; // If main vat is 0, IRPF may be different than 0.
6179  }
6180  if ($thirdparty_seller->id == $mysoc->id) {
6181  if (!$thirdparty_buyer->localtax2_assuj) {
6182  return 0;
6183  }
6184  } else {
6185  if (!$thirdparty_seller->localtax2_assuj) {
6186  return 0;
6187  }
6188  }
6189  }
6190  } else {
6191  if ($local == 1 && !$thirdparty_seller->localtax1_assuj) {
6192  return 0;
6193  }
6194  if ($local == 2 && !$thirdparty_seller->localtax2_assuj) {
6195  return 0;
6196  }
6197  }
6198 
6199  // For some country MAIN_GET_LOCALTAXES_VALUES_FROM_THIRDPARTY is forced to on.
6200  if (in_array($mysoc->country_code, array('ES'))) {
6201  $conf->global->MAIN_GET_LOCALTAXES_VALUES_FROM_THIRDPARTY = 1;
6202  }
6203 
6204  // Search local taxes
6205  if (!empty($conf->global->MAIN_GET_LOCALTAXES_VALUES_FROM_THIRDPARTY)) {
6206  if ($local == 1) {
6207  if ($thirdparty_seller != $mysoc) {
6208  if (!isOnlyOneLocalTax($local)) { // TODO We should provide $vatrate to search on correct line and not always on line with highest vat rate
6209  return $thirdparty_seller->localtax1_value;
6210  }
6211  } else { // i am the seller
6212  if (!isOnlyOneLocalTax($local)) { // TODO If seller is me, why not always returning this, even if there is only one locatax vat.
6213  return $conf->global->MAIN_INFO_VALUE_LOCALTAX1;
6214  }
6215  }
6216  }
6217  if ($local == 2) {
6218  if ($thirdparty_seller != $mysoc) {
6219  if (!isOnlyOneLocalTax($local)) { // TODO We should provide $vatrate to search on correct line and not always on line with highest vat rate
6220  // TODO We should also return value defined on thirdparty only if defined
6221  return $thirdparty_seller->localtax2_value;
6222  }
6223  } else { // i am the seller
6224  if (in_array($mysoc->country_code, array('ES'))) {
6225  return $thirdparty_buyer->localtax2_value;
6226  } else {
6227  return $conf->global->MAIN_INFO_VALUE_LOCALTAX2;
6228  }
6229  }
6230  }
6231  }
6232 
6233  // By default, search value of local tax on line of common tax
6234  $sql = "SELECT t.localtax1, t.localtax2, t.localtax1_type, t.localtax2_type";
6235  $sql .= " FROM ".MAIN_DB_PREFIX."c_tva as t, ".MAIN_DB_PREFIX."c_country as c";
6236  $sql .= " WHERE t.fk_pays = c.rowid AND c.code = '".$db->escape($thirdparty_seller->country_code)."'";
6237  $sql .= " AND t.taux = ".((float) $vatratecleaned)." AND t.active = 1";
6238  if (!empty($vatratecode)) {
6239  $sql .= " AND t.code ='".$db->escape($vatratecode)."'"; // If we have the code, we use it in priority
6240  } else {
6241  $sql .= " AND t.recuperableonly = '".$db->escape($vatnpr)."'";
6242  }
6243 
6244  $resql = $db->query($sql);
6245 
6246  if ($resql) {
6247  $obj = $db->fetch_object($resql);
6248  if ($obj) {
6249  if ($local == 1) {
6250  return $obj->localtax1;
6251  } elseif ($local == 2) {
6252  return $obj->localtax2;
6253  }
6254  }
6255  }
6256 
6257  return 0;
6258 }
6259 
6260 
6269 function isOnlyOneLocalTax($local)
6270 {
6271  $tax = get_localtax_by_third($local);
6272 
6273  $valors = explode(":", $tax);
6274 
6275  if (count($valors) > 1) {
6276  return false;
6277  } else {
6278  return true;
6279  }
6280 }
6281 
6288 function get_localtax_by_third($local)
6289 {
6290  global $db, $mysoc;
6291 
6292  $sql = " SELECT t.localtax".$local." as localtax";
6293  $sql .= " FROM ".MAIN_DB_PREFIX."c_tva as t INNER JOIN ".MAIN_DB_PREFIX."c_country as c ON c.rowid = t.fk_pays";
6294  $sql .= " WHERE c.code = '".$db->escape($mysoc->country_code)."' AND t.active = 1 AND t.taux = (";
6295  $sql .= "SELECT MAX(tt.taux) FROM ".MAIN_DB_PREFIX."c_tva as tt INNER JOIN ".MAIN_DB_PREFIX."c_country as c ON c.rowid = tt.fk_pays";
6296  $sql .= " WHERE c.code = '".$db->escape($mysoc->country_code)."' AND tt.active = 1)";
6297  $sql .= " AND t.localtax".$local."_type <> '0'";
6298  $sql .= " ORDER BY t.rowid DESC";
6299 
6300  $resql = $db->query($sql);
6301  if ($resql) {
6302  $obj = $db->fetch_object($resql);
6303  if ($obj) {
6304  return $obj->localtax;
6305  } else {
6306  return '0';
6307  }
6308  }
6309 
6310  return 'Error';
6311 }
6312 
6313 
6325 function getTaxesFromId($vatrate, $buyer = null, $seller = null, $firstparamisid = 1)
6326 {
6327  global $db, $mysoc;
6328 
6329  dol_syslog("getTaxesFromId vat id or rate = ".$vatrate);
6330 
6331  // Search local taxes
6332  $sql = "SELECT t.rowid, t.code, t.taux as rate, t.recuperableonly as npr, t.accountancy_code_sell, t.accountancy_code_buy,";
6333  $sql .= " t.localtax1, t.localtax1_type, t.localtax2, t.localtax2_type";
6334  $sql .= " FROM ".MAIN_DB_PREFIX."c_tva as t";
6335  if ($firstparamisid) {
6336  $sql .= " WHERE t.rowid = ".(int) $vatrate;
6337  } else {
6338  $vatratecleaned = $vatrate;
6339  $vatratecode = '';
6340  $reg = array();
6341  if (preg_match('/^(.*)\s*\((.*)\)$/', $vatrate, $reg)) { // If vat is "xx (yy)"
6342  $vatratecleaned = $reg[1];
6343  $vatratecode = $reg[2];
6344  }
6345 
6346  $sql .= ", ".MAIN_DB_PREFIX."c_country as c";
6347  /*if ($mysoc->country_code == 'ES') $sql.= " WHERE t.fk_pays = c.rowid AND c.code = '".$db->escape($buyer->country_code)."'"; // vat in spain use the buyer country ??
6348  else $sql.= " WHERE t.fk_pays = c.rowid AND c.code = '".$db->escape($seller->country_code)."'";*/
6349  $sql .= " WHERE t.fk_pays = c.rowid AND c.code = '".$db->escape($seller->country_code)."'";
6350  $sql .= " AND t.taux = ".((float) $vatratecleaned)." AND t.active = 1";
6351  if ($vatratecode) {
6352  $sql .= " AND t.code = '".$db->escape($vatratecode)."'";
6353  }
6354  }
6355 
6356  $resql = $db->query($sql);
6357  if ($resql) {
6358  $obj = $db->fetch_object($resql);
6359  if ($obj) {
6360  return array(
6361  'rowid'=>$obj->rowid,
6362  'code'=>$obj->code,
6363  'rate'=>$obj->rate,
6364  'localtax1'=>$obj->localtax1,
6365  'localtax1_type'=>$obj->localtax1_type,
6366  'localtax2'=>$obj->localtax2,
6367  'localtax2_type'=>$obj->localtax2_type,
6368  'npr'=>$obj->npr,
6369  'accountancy_code_sell'=>$obj->accountancy_code_sell,
6370  'accountancy_code_buy'=>$obj->accountancy_code_buy
6371  );
6372  } else {
6373  return array();
6374  }
6375  } else {
6376  dol_print_error($db);
6377  }
6378 
6379  return array();
6380 }
6381 
6398 function getLocalTaxesFromRate($vatrate, $local, $buyer, $seller, $firstparamisid = 0)
6399 {
6400  global $db, $mysoc;
6401 
6402  dol_syslog("getLocalTaxesFromRate vatrate=".$vatrate." local=".$local);
6403 
6404  // Search local taxes
6405  $sql = "SELECT t.taux as rate, t.code, t.localtax1, t.localtax1_type, t.localtax2, t.localtax2_type, t.accountancy_code_sell, t.accountancy_code_buy";
6406  $sql .= " FROM ".MAIN_DB_PREFIX."c_tva as t";
6407  if ($firstparamisid) {
6408  $sql .= " WHERE t.rowid = ".(int) $vatrate;
6409  } else {
6410  $vatratecleaned = $vatrate;
6411  $vatratecode = '';
6412  $reg = array();
6413  if (preg_match('/^(.*)\s*\((.*)\)$/', $vatrate, $reg)) { // If vat is "x.x (yy)"
6414  $vatratecleaned = $reg[1];
6415  $vatratecode = $reg[2];
6416  }
6417 
6418  $sql .= ", ".MAIN_DB_PREFIX."c_country as c";
6419  if (!empty($mysoc) && $mysoc->country_code == 'ES') {
6420  $countrycodetouse = ((empty($buyer) || empty($buyer->country_code)) ? $mysoc->country_code : $buyer->country_code);
6421  $sql .= " WHERE t.fk_pays = c.rowid AND c.code = '".$db->escape($countrycodetouse)."'"; // local tax in spain use the buyer country ??
6422  } else {
6423  $countrycodetouse = ((empty($seller) || empty($seller->country_code)) ? $mysoc->country_code : $seller->country_code);
6424  $sql .= " WHERE t.fk_pays = c.rowid AND c.code = '".$db->escape($countrycodetouse)."'";
6425  }
6426  $sql .= " AND t.taux = ".((float) $vatratecleaned)." AND t.active = 1";
6427  if ($vatratecode) {
6428  $sql .= " AND t.code = '".$db->escape($vatratecode)."'";
6429  }
6430  }
6431 
6432  $resql = $db->query($sql);
6433  if ($resql) {
6434  $obj = $db->fetch_object($resql);
6435 
6436  if ($obj) {
6437  $vateratestring = $obj->rate.($obj->code ? ' ('.$obj->code.')' : '');
6438 
6439  if ($local == 1) {
6440  return array($obj->localtax1_type, get_localtax($vateratestring, $local, $buyer, $seller), $obj->accountancy_code_sell, $obj->accountancy_code_buy);
6441  } elseif ($local == 2) {
6442  return array($obj->localtax2_type, get_localtax($vateratestring, $local, $buyer, $seller), $obj->accountancy_code_sell, $obj->accountancy_code_buy);
6443  } else {
6444  return array($obj->localtax1_type, get_localtax($vateratestring, 1, $buyer, $seller), $obj->localtax2_type, get_localtax($vateratestring, 2, $buyer, $seller), $obj->accountancy_code_sell, $obj->accountancy_code_buy);
6445  }
6446  }
6447  }
6448 
6449  return array();
6450 }
6451 
6462 function get_product_vat_for_country($idprod, $thirdpartytouse, $idprodfournprice = 0)
6463 {
6464  global $db, $conf, $mysoc;
6465 
6466  require_once DOL_DOCUMENT_ROOT.'/product/class/product.class.php';
6467 
6468  $ret = 0;
6469  $found = 0;
6470 
6471  if ($idprod > 0) {
6472  // Load product
6473  $product = new Product($db);
6474  $product->fetch($idprod);
6475 
6476  if ($mysoc->country_code == $thirdpartytouse->country_code) {
6477  // If country to consider is ours
6478  if ($idprodfournprice > 0) { // We want vat for product for a "supplier" object
6479  $result = $product->get_buyprice($idprodfournprice, 0, 0, 0);
6480  if ($result > 0) {
6481  $ret = $product->vatrate_supplier;
6482  if ($product->default_vat_code_supplier) {
6483  $ret .= ' ('.$product->default_vat_code_supplier.')';
6484  }
6485  $found = 1;
6486  }
6487  }
6488  if (!$found) {
6489  $ret = $product->tva_tx; // Default sales vat of product
6490  if ($product->default_vat_code) {
6491  $ret .= ' ('.$product->default_vat_code.')';
6492  }
6493  $found = 1;
6494  }
6495  } else {
6496  // TODO Read default product vat according to product and another countrycode.
6497  // Vat for couple anothercountrycode/product is data that is not managed and store yet, so we will fallback on next rule.
6498  }
6499  }
6500 
6501  if (!$found) {
6502  if (empty($conf->global->MAIN_VAT_DEFAULT_IF_AUTODETECT_FAILS)) {
6503  // If vat of product for the country not found or not defined, we return the first rate found (sorting on use_default, then on higher vat of country).
6504  $sql = "SELECT t.taux as vat_rate, t.code as default_vat_code";
6505  $sql .= " FROM ".MAIN_DB_PREFIX."c_tva as t, ".MAIN_DB_PREFIX."c_country as c";
6506  $sql .= " WHERE t.active = 1 AND t.fk_pays = c.rowid AND c.code = '".$db->escape($thirdpartytouse->country_code)."'";
6507  $sql .= " ORDER BY t.use_default DESC, t.taux DESC, t.code ASC, t.recuperableonly ASC";
6508  $sql .= $db->plimit(1);
6509 
6510  $resql = $db->query($sql);
6511  if ($resql) {
6512  $obj = $db->fetch_object($resql);
6513  if ($obj) {
6514  $ret = $obj->vat_rate;
6515  if ($obj->default_vat_code) {
6516  $ret .= ' ('.$obj->default_vat_code.')';
6517  }
6518  }
6519  $db->free($resql);
6520  } else {
6521  dol_print_error($db);
6522  }
6523  } else {
6524  // Forced value if autodetect fails. MAIN_VAT_DEFAULT_IF_AUTODETECT_FAILS can be
6525  // '1.23'
6526  // or '1.23 (CODE)'
6527  $defaulttx = '';
6528  if ($conf->global->MAIN_VAT_DEFAULT_IF_AUTODETECT_FAILS != 'none') {
6529  $defaulttx = $conf->global->MAIN_VAT_DEFAULT_IF_AUTODETECT_FAILS;
6530  }
6531  /*if (preg_match('/\((.*)\)/', $defaulttx, $reg)) {
6532  $defaultcode = $reg[1];
6533  $defaulttx = preg_replace('/\s*\(.*\)/', '', $defaulttx);
6534  }*/
6535 
6536  $ret = $defaulttx;
6537  }
6538  }
6539 
6540  dol_syslog("get_product_vat_for_country: ret=".$ret);
6541  return $ret;
6542 }
6543 
6553 function get_product_localtax_for_country($idprod, $local, $thirdpartytouse)
6554 {
6555  global $db, $mysoc;
6556 
6557  if (!class_exists('Product')) {
6558  require_once DOL_DOCUMENT_ROOT.'/product/class/product.class.php';
6559  }
6560 
6561  $ret = 0;
6562  $found = 0;
6563 
6564  if ($idprod > 0) {
6565  // Load product
6566  $product = new Product($db);
6567  $result = $product->fetch($idprod);
6568 
6569  if ($mysoc->country_code == $thirdpartytouse->country_code) { // If selling country is ours
6570  /* Not defined yet, so we don't use this
6571  if ($local==1) $ret=$product->localtax1_tx;
6572  elseif ($local==2) $ret=$product->localtax2_tx;
6573  $found=1;
6574  */
6575  } else {
6576  // TODO Read default product vat according to product and another countrycode.
6577  // Vat for couple anothercountrycode/product is data that is not managed and store yet, so we will fallback on next rule.
6578  }
6579  }
6580 
6581  if (!$found) {
6582  // If vat of product for the country not found or not defined, we return higher vat of country.
6583  $sql = "SELECT taux as vat_rate, localtax1, localtax2";
6584  $sql .= " FROM ".MAIN_DB_PREFIX."c_tva as t, ".MAIN_DB_PREFIX."c_country as c";
6585  $sql .= " WHERE t.active=1 AND t.fk_pays = c.rowid AND c.code='".$db->escape($thirdpartytouse->country_code)."'";
6586  $sql .= " ORDER BY t.taux DESC, t.recuperableonly ASC";
6587  $sql .= $db->plimit(1);
6588 
6589  $resql = $db->query($sql);
6590  if ($resql) {
6591  $obj = $db->fetch_object($resql);
6592  if ($obj) {
6593  if ($local == 1) {
6594  $ret = $obj->localtax1;
6595  } elseif ($local == 2) {
6596  $ret = $obj->localtax2;
6597  }
6598  }
6599  } else {
6600  dol_print_error($db);
6601  }
6602  }
6603 
6604  dol_syslog("get_product_localtax_for_country: ret=".$ret);
6605  return $ret;
6606 }
6607 
6624 function get_default_tva(Societe $thirdparty_seller, Societe $thirdparty_buyer, $idprod = 0, $idprodfournprice = 0)
6625 {
6626  global $conf;
6627 
6628  require_once DOL_DOCUMENT_ROOT.'/core/lib/company.lib.php';
6629 
6630  // Note: possible values for tva_assuj are 0/1 or franchise/reel
6631  $seller_use_vat = ((is_numeric($thirdparty_seller->tva_assuj) && !$thirdparty_seller->tva_assuj) || (!is_numeric($thirdparty_seller->tva_assuj) && $thirdparty_seller->tva_assuj == 'franchise')) ? 0 : 1;
6632 
6633  $seller_country_code = $thirdparty_seller->country_code;
6634  $seller_in_cee = isInEEC($thirdparty_seller);
6635 
6636  $buyer_country_code = $thirdparty_buyer->country_code;
6637  $buyer_in_cee = isInEEC($thirdparty_buyer);
6638 
6639  dol_syslog("get_default_tva: seller use vat=".$seller_use_vat.", seller country=".$seller_country_code.", seller in cee=".$seller_in_cee.", buyer vat number=".$thirdparty_buyer->tva_intra." buyer country=".$buyer_country_code.", buyer in cee=".$buyer_in_cee.", idprod=".$idprod.", idprodfournprice=".$idprodfournprice.", SERVICE_ARE_ECOMMERCE_200238EC=".(!empty($conf->global->SERVICES_ARE_ECOMMERCE_200238EC) ? $conf->global->SERVICES_ARE_ECOMMERCE_200238EC : ''));
6640 
6641  // If services are eServices according to EU Council Directive 2002/38/EC (http://ec.europa.eu/taxation_customs/taxation/vat/traders/e-commerce/article_1610_en.htm)
6642  // we use the buyer VAT.
6643  if (!empty($conf->global->SERVICE_ARE_ECOMMERCE_200238EC)) {
6644  if ($seller_in_cee && $buyer_in_cee) {
6645  $isacompany = $thirdparty_buyer->isACompany();
6646  if ($isacompany && getDolGlobalString('MAIN_USE_VAT_COMPANIES_IN_EEC_WITH_INVALID_VAT_ID_ARE_INDIVIDUAL')) {
6647  require_once DOL_DOCUMENT_ROOT.'/core/lib/functions2.lib.php';
6648  if (!isValidVATID($thirdparty_buyer)) {
6649  $isacompany = 0;
6650  }
6651  }
6652 
6653  if (!$isacompany) {
6654  //print 'VATRULE 0';
6655  return get_product_vat_for_country($idprod, $thirdparty_buyer, $idprodfournprice);
6656  }
6657  }
6658  }
6659 
6660  // If seller does not use VAT, default VAT is 0. End of rule.
6661  if (!$seller_use_vat) {
6662  //print 'VATRULE 1';
6663  return 0;
6664  }
6665 
6666  // If the (seller country = buyer country) then the default VAT = VAT of the product sold. End of rule.
6667  if (($seller_country_code == $buyer_country_code)
6668  || (in_array($seller_country_code, array('FR', 'MC')) && in_array($buyer_country_code, array('FR', 'MC')))) { // Warning ->country_code not always defined
6669  //print 'VATRULE 2';
6670  $tmpvat = get_product_vat_for_country($idprod, $thirdparty_seller, $idprodfournprice);
6671 
6672  if ($seller_country_code == 'IN' && getDolGlobalString('MAIN_SALETAX_AUTOSWITCH_I_CS_FOR_INDIA')) {
6673  // Special case for india.
6674  //print 'VATRULE 2b';
6675  $reg = array();
6676  if (preg_match('/C+S-(\d+)/', $tmpvat, $reg) && $thirdparty_seller->state_id != $thirdparty_buyer->state_id) {
6677  // we must revert the C+S into I
6678  $tmpvat = str_replace("C+S", "I", $tmpvat);
6679  } elseif (preg_match('/I-(\d+)/', $tmpvat, $reg) && $thirdparty_seller->state_id == $thirdparty_buyer->state_id) {
6680  // we must revert the I into C+S
6681  $tmpvat = str_replace("I", "C+S", $tmpvat);
6682  }
6683  }
6684 
6685  return $tmpvat;
6686  }
6687 
6688  // If (seller and buyer in the European Community) and (property sold = new means of transport such as car, boat, plane) then VAT by default = 0 (VAT must be paid by the buyer to the tax center of his country and not to the seller). End of rule.
6689  // 'VATRULE 3' - Not supported
6690 
6691  // If (seller and buyer in the European Community) and (buyer = individual) then VAT by default = VAT of the product sold. End of rule
6692  // If (seller and buyer in European Community) and (buyer = company) then VAT by default=0. End of rule
6693  if (($seller_in_cee && $buyer_in_cee)) {
6694  $isacompany = $thirdparty_buyer->isACompany();
6695  if ($isacompany && getDolGlobalString('MAIN_USE_VAT_COMPANIES_IN_EEC_WITH_INVALID_VAT_ID_ARE_INDIVIDUAL')) {
6696  require_once DOL_DOCUMENT_ROOT.'/core/lib/functions2.lib.php';
6697  if (!isValidVATID($thirdparty_buyer)) {
6698  $isacompany = 0;
6699  }
6700  }
6701 
6702  if (!$isacompany) {
6703  //print 'VATRULE 4';
6704  return get_product_vat_for_country($idprod, $thirdparty_seller, $idprodfournprice);
6705  } else {
6706  //print 'VATRULE 5';
6707  return 0;
6708  }
6709  }
6710 
6711  // If (seller in the European Community and buyer outside the European Community and private buyer) then VAT by default = VAT of the product sold. End of rule
6712  // I don't see any use case that need this rule.
6713  if (!empty($conf->global->MAIN_USE_VAT_OF_PRODUCT_FOR_INDIVIDUAL_CUSTOMER_OUT_OF_EEC) && empty($buyer_in_cee)) {
6714  $isacompany = $thirdparty_buyer->isACompany();
6715  if (!$isacompany) {
6716  return get_product_vat_for_country($idprod, $thirdparty_seller, $idprodfournprice);
6717  //print 'VATRULE extra';
6718  }
6719  }
6720 
6721  // Otherwise the VAT proposed by default=0. End of rule.
6722  // Rem: This means that at least one of the 2 is outside the European Community and the country differs
6723  //print 'VATRULE 6';
6724  return 0;
6725 }
6726 
6727 
6738 function get_default_npr(Societe $thirdparty_seller, Societe $thirdparty_buyer, $idprod = 0, $idprodfournprice = 0)
6739 {
6740  global $db;
6741 
6742  if ($idprodfournprice > 0) {
6743  if (!class_exists('ProductFournisseur')) {
6744  require_once DOL_DOCUMENT_ROOT.'/fourn/class/fournisseur.product.class.php';
6745  }
6746  $prodprice = new ProductFournisseur($db);
6747  $prodprice->fetch_product_fournisseur_price($idprodfournprice);
6748  return $prodprice->fourn_tva_npr;
6749  } elseif ($idprod > 0) {
6750  if (!class_exists('Product')) {
6751  require_once DOL_DOCUMENT_ROOT.'/product/class/product.class.php';
6752  }
6753  $prod = new Product($db);
6754  $prod->fetch($idprod);
6755  return $prod->tva_npr;
6756  }
6757 
6758  return 0;
6759 }
6760 
6774 function get_default_localtax($thirdparty_seller, $thirdparty_buyer, $local, $idprod = 0)
6775 {
6776  global $mysoc;
6777 
6778  if (!is_object($thirdparty_seller)) {
6779  return -1;
6780  }
6781  if (!is_object($thirdparty_buyer)) {
6782  return -1;
6783  }
6784 
6785  if ($local == 1) { // Localtax 1
6786  if ($mysoc->country_code == 'ES') {
6787  if (is_numeric($thirdparty_buyer->localtax1_assuj) && !$thirdparty_buyer->localtax1_assuj) {
6788  return 0;
6789  }
6790  } else {
6791  // Si vendeur non assujeti a Localtax1, localtax1 par default=0
6792  if (is_numeric($thirdparty_seller->localtax1_assuj) && !$thirdparty_seller->localtax1_assuj) {
6793  return 0;
6794  }
6795  if (!is_numeric($thirdparty_seller->localtax1_assuj) && $thirdparty_seller->localtax1_assuj == 'localtax1off') {
6796  return 0;
6797  }
6798  }
6799  } elseif ($local == 2) { //I Localtax 2
6800  // Si vendeur non assujeti a Localtax2, localtax2 par default=0
6801  if (is_numeric($thirdparty_seller->localtax2_assuj) && !$thirdparty_seller->localtax2_assuj) {
6802  return 0;
6803  }
6804  if (!is_numeric($thirdparty_seller->localtax2_assuj) && $thirdparty_seller->localtax2_assuj == 'localtax2off') {
6805  return 0;
6806  }
6807  }
6808 
6809  if ($thirdparty_seller->country_code == $thirdparty_buyer->country_code) {
6810  return get_product_localtax_for_country($idprod, $local, $thirdparty_seller);
6811  }
6812 
6813  return 0;
6814 }
6815 
6824 function yn($yesno, $case = 1, $color = 0)
6825 {
6826  global $langs;
6827 
6828  $result = 'unknown';
6829  $classname = '';
6830  if ($yesno == 1 || (isset($yesno) && (strtolower($yesno) == 'yes' || strtolower($yesno) == 'true'))) { // A mettre avant test sur no a cause du == 0
6831  $result = $langs->trans('yes');
6832  if ($case == 1 || $case == 3) {
6833  $result = $langs->trans("Yes");
6834  }
6835  if ($case == 2) {
6836  $result = '<input type="checkbox" value="1" checked disabled>';
6837  }
6838  if ($case == 3) {
6839  $result = '<input type="checkbox" value="1" checked disabled> '.$result;
6840  }
6841 
6842  $classname = 'ok';
6843  } elseif ($yesno == 0 || strtolower($yesno) == 'no' || strtolower($yesno) == 'false') {
6844  $result = $langs->trans("no");
6845  if ($case == 1 || $case == 3) {
6846  $result = $langs->trans("No");
6847  }
6848  if ($case == 2) {
6849  $result = '<input type="checkbox" value="0" disabled>';
6850  }
6851  if ($case == 3) {
6852  $result = '<input type="checkbox" value="0" disabled> '.$result;
6853  }
6854 
6855  if ($color == 2) {
6856  $classname = 'ok';
6857  } else {
6858  $classname = 'error';
6859  }
6860  }
6861  if ($color) {
6862  return '<span class="'.$classname.'">'.$result.'</span>';
6863  }
6864  return $result;
6865 }
6866 
6882 function get_exdir($num, $level, $alpha, $withoutslash, $object, $modulepart = '')
6883 {
6884  global $conf;
6885 
6886  if (empty($modulepart) && !empty($object->module)) {
6887  $modulepart = $object->module;
6888  }
6889 
6890  $path = '';
6891 
6892  $arrayforoldpath = array('cheque', 'category', 'holiday', 'supplier_invoice', 'invoice_supplier', 'mailing', 'supplier_payment');
6893  if (getDolGlobalInt('PRODUCT_USE_OLD_PATH_FOR_PHOTO')) {
6894  $arrayforoldpath[] = 'product';
6895  }
6896  if (!empty($level) && in_array($modulepart, $arrayforoldpath)) {
6897  // This part should be removed once all code is using "get_exdir" to forge path, with parameter $object and $modulepart provided.
6898  if (empty($alpha)) {
6899  $num = preg_replace('/([^0-9])/i', '', $num);
6900  } else {
6901  $num = preg_replace('/^.*\-/i', '', $num);
6902  }
6903  $num = substr("000".$num, -$level);
6904  if ($level == 1) {
6905  $path = substr($num, 0, 1);
6906  }
6907  if ($level == 2) {
6908  $path = substr($num, 1, 1).'/'.substr($num, 0, 1);
6909  }
6910  if ($level == 3) {
6911  $path = substr($num, 2, 1).'/'.substr($num, 1, 1).'/'.substr($num, 0, 1);
6912  }
6913  } else {
6914  // We will enhance here a common way of forging path for document storage.
6915  // In a future, we may distribute directories on several levels depending on setup and object.
6916  // Here, $object->id, $object->ref and $modulepart are required.
6917  //var_dump($modulepart);
6918  $path = dol_sanitizeFileName(empty($object->ref) ? (string) $object->id : $object->ref);
6919  }
6920 
6921  if (empty($withoutslash) && !empty($path)) {
6922  $path .= '/';
6923  }
6924 
6925  return $path;
6926 }
6927 
6936 function dol_mkdir($dir, $dataroot = '', $newmask = '')
6937 {
6938  global $conf;
6939 
6940  dol_syslog("functions.lib::dol_mkdir: dir=".$dir, LOG_INFO);
6941 
6942  $dir_osencoded = dol_osencode($dir);
6943  if (@is_dir($dir_osencoded)) {
6944  return 0;
6945  }
6946 
6947  $nberr = 0;
6948  $nbcreated = 0;
6949 
6950  $ccdir = '';
6951  if (!empty($dataroot)) {
6952  // Remove data root from loop
6953  $dir = str_replace($dataroot.'/', '', $dir);
6954  $ccdir = $dataroot.'/';
6955  }
6956 
6957  $cdir = explode("/", $dir);
6958  $num = count($cdir);
6959  for ($i = 0; $i < $num; $i++) {
6960  if ($i > 0) {
6961  $ccdir .= '/'.$cdir[$i];
6962  } else {
6963  $ccdir .= $cdir[$i];
6964  }
6965  if (preg_match("/^.:$/", $ccdir, $regs)) {
6966  continue; // Si chemin Windows incomplet, on poursuit par rep suivant
6967  }
6968 
6969  // Attention, le is_dir() peut echouer bien que le rep existe.
6970  // (ex selon config de open_basedir)
6971  if ($ccdir) {
6972  $ccdir_osencoded = dol_osencode($ccdir);
6973  if (!@is_dir($ccdir_osencoded)) {
6974  dol_syslog("functions.lib::dol_mkdir: Directory '".$ccdir."' does not exists or is outside open_basedir PHP setting.", LOG_DEBUG);
6975 
6976  umask(0);
6977  $dirmaskdec = octdec((string) $newmask);
6978  if (empty($newmask)) {
6979  $dirmaskdec = empty($conf->global->MAIN_UMASK) ? octdec('0755') : octdec($conf->global->MAIN_UMASK);
6980  }