dolibarr  18.0.0-alpha
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-2022 Thibault Foucart <support@ptibogxiv.net>
18  * Copyright (C) 2020 Open-Dsi <support@open-dsi.fr>
19  * Copyright (C) 2021 Gauthier VERDOL <gauthier.verdol@atm-consulting.fr>
20  * Copyright (C) 2022 Anthony Berton <anthony.berton@bb2a.fr>
21  * Copyright (C) 2022 Ferran Marcet <fmarcet@2byte.es>
22  * Copyright (C) 2022 Charlene Benke <charlene@patas-monkey.com>
23  *
24  * This program is free software; you can redistribute it and/or modify
25  * it under the terms of the GNU General Public License as published by
26  * the Free Software Foundation; either version 3 of the License, or
27  * (at your option) any later version.
28  *
29  * This program is distributed in the hope that it will be useful,
30  * but WITHOUT ANY WARRANTY; without even the implied warranty of
31  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
32  * GNU General Public License for more details.
33  *
34  * You should have received a copy of the GNU General Public License
35  * along with this program. If not, see <https://www.gnu.org/licenses/>.
36  * or see https://www.gnu.org/
37  */
38 
45 include_once DOL_DOCUMENT_ROOT.'/core/lib/json.lib.php';
46 
47 
48 if (!function_exists('utf8_encode')) {
55  function utf8_encode($elements)
56  {
57  return mb_convert_encoding($elements, 'UTF-8', 'ISO-8859-1');
58  }
59 }
60 
61 if (!function_exists('utf8_decode')) {
68  function utf8_decode($elements)
69  {
70  return mb_convert_encoding($elements, 'ISO-8859-1', 'UTF-8');
71  }
72 }
73 
82 function getMultidirOutput($object, $module = '')
83 {
84  global $conf;
85  if (!is_object($object) && empty($module)) {
86  return null;
87  }
88  if (empty($module) && !empty($object->element)) {
89  $module = $object->element;
90  }
91  return $conf->$module->multidir_output[(!empty($object->entity) ? $object->entity : $conf->entity)];
92 }
93 
101 function getDolGlobalString($key, $default = '')
102 {
103  global $conf;
104  // return $conf->global->$key ?? $default;
105  return (string) (empty($conf->global->$key) ? $default : $conf->global->$key);
106 }
107 
115 function getDolGlobalInt($key, $default = 0)
116 {
117  global $conf;
118  // return $conf->global->$key ?? $default;
119  return (int) (empty($conf->global->$key) ? $default : $conf->global->$key);
120 }
121 
130 function getDolUserString($key, $default = '', $tmpuser = null)
131 {
132  if (empty($tmpuser)) {
133  global $user;
134  $tmpuser = $user;
135  }
136 
137  // return $conf->global->$key ?? $default;
138  return (string) (empty($tmpuser->conf->$key) ? $default : $tmpuser->conf->$key);
139 }
140 
149 function getDolUserInt($key, $default = 0, $tmpuser = null)
150 {
151  if (empty($tmpuser)) {
152  global $user;
153  $tmpuser = $user;
154  }
155 
156  // return $conf->global->$key ?? $default;
157  return (int) (empty($tmpuser->conf->$key) ? $default : $tmpuser->conf->$key);
158 }
159 
166 function isModEnabled($module)
167 {
168  global $conf;
169  return !empty($conf->$module->enabled);
170 }
171 
183 function getDoliDBInstance($type, $host, $user, $pass, $name, $port)
184 {
185  require_once DOL_DOCUMENT_ROOT."/core/db/".$type.'.class.php';
186 
187  $class = 'DoliDB'.ucfirst($type);
188  $dolidb = new $class($type, $host, $user, $pass, $name, $port);
189  return $dolidb;
190 }
191 
209 function getEntity($element, $shared = 1, $currentobject = null)
210 {
211  global $conf, $mc, $hookmanager, $object, $action, $db;
212 
213  if (!is_object($hookmanager)) {
214  include_once DOL_DOCUMENT_ROOT.'/core/class/hookmanager.class.php';
215  $hookmanager = new HookManager($db);
216  }
217 
218  // fix different element names (France to English)
219  switch ($element) {
220  case 'contrat':
221  $element = 'contract';
222  break; // "/contrat/class/contrat.class.php"
223  case 'order_supplier':
224  $element = 'supplier_order';
225  break; // "/fourn/class/fournisseur.commande.class.php"
226  case 'invoice_supplier':
227  $element = 'supplier_invoice';
228  break; // "/fourn/class/fournisseur.facture.class.php"
229  }
230 
231  if (is_object($mc)) {
232  $out = $mc->getEntity($element, $shared, $currentobject);
233  } else {
234  $out = '';
235  $addzero = array('user', 'usergroup', 'c_email_templates', 'email_template', 'default_values');
236  if (in_array($element, $addzero)) {
237  $out .= '0,';
238  }
239  $out .= ((int) $conf->entity);
240  }
241 
242  // Manipulate entities to query on the fly
243  $parameters = array(
244  'element' => $element,
245  'shared' => $shared,
246  'object' => $object,
247  'currentobject' => $currentobject,
248  'out' => $out
249  );
250  $reshook = $hookmanager->executeHooks('hookGetEntity', $parameters, $currentobject, $action); // Note that $action and $object may have been modified by some hooks
251 
252  if (is_numeric($reshook)) {
253  if ($reshook == 0 && !empty($hookmanager->resPrint)) {
254  $out .= ','.$hookmanager->resPrint; // add
255  } elseif ($reshook == 1) {
256  $out = $hookmanager->resPrint; // replace
257  }
258  }
259 
260  return $out;
261 }
262 
269 function setEntity($currentobject)
270 {
271  global $conf, $mc;
272 
273  if (is_object($mc) && method_exists($mc, 'setEntity')) {
274  return $mc->setEntity($currentobject);
275  } else {
276  return ((is_object($currentobject) && $currentobject->id > 0 && $currentobject->entity > 0) ? $currentobject->entity : $conf->entity);
277  }
278 }
279 
286 function isASecretKey($keyname)
287 {
288  return preg_match('/(_pass|password|_pw|_key|securekey|serverkey|secret\d?|p12key|exportkey|_PW_[a-z]+|token)$/i', $keyname);
289 }
290 
291 
298 function num2Alpha($n)
299 {
300  for ($r = ""; $n >= 0; $n = intval($n / 26) - 1)
301  $r = chr($n % 26 + 0x41) . $r;
302  return $r;
303 }
304 
305 
322 function getBrowserInfo($user_agent)
323 {
324  include_once DOL_DOCUMENT_ROOT.'/includes/mobiledetect/mobiledetectlib/Mobile_Detect.php';
325 
326  $name = 'unknown';
327  $version = '';
328  $os = 'unknown';
329  $phone = '';
330 
331  $user_agent = substr($user_agent, 0, 512); // Avoid to process too large user agent
332 
333  $detectmobile = new Mobile_Detect(null, $user_agent);
334  $tablet = $detectmobile->isTablet();
335 
336  if ($detectmobile->isMobile()) {
337  $phone = 'unknown';
338 
339  // If phone/smartphone, we set phone os name.
340  if ($detectmobile->is('AndroidOS')) {
341  $os = $phone = 'android';
342  } elseif ($detectmobile->is('BlackBerryOS')) {
343  $os = $phone = 'blackberry';
344  } elseif ($detectmobile->is('iOS')) {
345  $os = 'ios';
346  $phone = 'iphone';
347  } elseif ($detectmobile->is('PalmOS')) {
348  $os = $phone = 'palm';
349  } elseif ($detectmobile->is('SymbianOS')) {
350  $os = 'symbian';
351  } elseif ($detectmobile->is('webOS')) {
352  $os = 'webos';
353  } elseif ($detectmobile->is('MaemoOS')) {
354  $os = 'maemo';
355  } elseif ($detectmobile->is('WindowsMobileOS') || $detectmobile->is('WindowsPhoneOS')) {
356  $os = 'windows';
357  }
358  }
359 
360  // OS
361  if (preg_match('/linux/i', $user_agent)) {
362  $os = 'linux';
363  } elseif (preg_match('/macintosh/i', $user_agent)) {
364  $os = 'macintosh';
365  } elseif (preg_match('/windows/i', $user_agent)) {
366  $os = 'windows';
367  }
368 
369  // Name
370  $reg = array();
371  if (preg_match('/firefox(\/|\s)([\d\.]*)/i', $user_agent, $reg)) {
372  $name = 'firefox';
373  $version = empty($reg[2]) ? '' : $reg[2];
374  } elseif (preg_match('/edge(\/|\s)([\d\.]*)/i', $user_agent, $reg)) {
375  $name = 'edge';
376  $version = empty($reg[2]) ? '' : $reg[2];
377  } elseif (preg_match('/chrome(\/|\s)([\d\.]+)/i', $user_agent, $reg)) {
378  $name = 'chrome';
379  $version = empty($reg[2]) ? '' : $reg[2];
380  } elseif (preg_match('/chrome/i', $user_agent, $reg)) {
381  // we can have 'chrome (Mozilla...) chrome x.y' in one string
382  $name = 'chrome';
383  } elseif (preg_match('/iceweasel/i', $user_agent)) {
384  $name = 'iceweasel';
385  } elseif (preg_match('/epiphany/i', $user_agent)) {
386  $name = 'epiphany';
387  } elseif (preg_match('/safari(\/|\s)([\d\.]*)/i', $user_agent, $reg)) {
388  $name = 'safari';
389  $version = empty($reg[2]) ? '' : $reg[2];
390  } elseif (preg_match('/opera(\/|\s)([\d\.]*)/i', $user_agent, $reg)) {
391  // Safari is often present in string for mobile but its not.
392  $name = 'opera';
393  $version = empty($reg[2]) ? '' : $reg[2];
394  } elseif (preg_match('/(MSIE\s([0-9]+\.[0-9]))|.*(Trident\/[0-9]+.[0-9];.*rv:([0-9]+\.[0-9]+))/i', $user_agent, $reg)) {
395  $name = 'ie';
396  $version = end($reg);
397  } elseif (preg_match('/(Windows NT\s([0-9]+\.[0-9])).*(Trident\/[0-9]+.[0-9];.*rv:([0-9]+\.[0-9]+))/i', $user_agent, $reg)) {
398  // MS products at end
399  $name = 'ie';
400  $version = end($reg);
401  } elseif (preg_match('/l[iy]n(x|ks)(\(|\/|\s)*([\d\.]+)/i', $user_agent, $reg)) {
402  // MS products at end
403  $name = 'lynxlinks';
404  $version = empty($reg[3]) ? '' : $reg[3];
405  }
406 
407  if ($tablet) {
408  $layout = 'tablet';
409  } elseif ($phone) {
410  $layout = 'phone';
411  } else {
412  $layout = 'classic';
413  }
414 
415  return array(
416  'browsername' => $name,
417  'browserversion' => $version,
418  'browseros' => $os,
419  'layout' => $layout,
420  'phone' => $phone,
421  'tablet' => $tablet
422  );
423 }
424 
430 function dol_shutdown()
431 {
432  global $conf, $user, $langs, $db;
433  $disconnectdone = false;
434  $depth = 0;
435  if (is_object($db) && !empty($db->connected)) {
436  $depth = $db->transaction_opened;
437  $disconnectdone = $db->close();
438  }
439  dol_syslog("--- End access to ".$_SERVER["PHP_SELF"].(($disconnectdone && $depth) ? ' (Warn: db disconnection forced, transaction depth was '.$depth.')' : ''), (($disconnectdone && $depth) ? LOG_WARNING : LOG_INFO));
440 }
441 
451 function GETPOSTISSET($paramname)
452 {
453  $isset = false;
454 
455  $relativepathstring = $_SERVER["PHP_SELF"];
456  // Clean $relativepathstring
457  if (constant('DOL_URL_ROOT')) {
458  $relativepathstring = preg_replace('/^'.preg_quote(constant('DOL_URL_ROOT'), '/').'/', '', $relativepathstring);
459  }
460  $relativepathstring = preg_replace('/^\//', '', $relativepathstring);
461  $relativepathstring = preg_replace('/^custom\//', '', $relativepathstring);
462  //var_dump($relativepathstring);
463  //var_dump($user->default_values);
464 
465  // Code for search criteria persistence.
466  // Retrieve values if restore_lastsearch_values
467  if (!empty($_GET['restore_lastsearch_values'])) { // Use $_GET here and not GETPOST
468  if (!empty($_SESSION['lastsearch_values_'.$relativepathstring])) { // If there is saved values
469  $tmp = json_decode($_SESSION['lastsearch_values_'.$relativepathstring], true);
470  if (is_array($tmp)) {
471  foreach ($tmp as $key => $val) {
472  if ($key == $paramname) { // We are on the requested parameter
473  $isset = true;
474  break;
475  }
476  }
477  }
478  }
479  // If there is saved contextpage, limit, page or mode
480  if ($paramname == 'contextpage' && !empty($_SESSION['lastsearch_contextpage_'.$relativepathstring])) {
481  $isset = true;
482  } elseif ($paramname == 'limit' && !empty($_SESSION['lastsearch_limit_'.$relativepathstring])) {
483  $isset = true;
484  } elseif ($paramname == 'page' && !empty($_SESSION['lastsearch_page_'.$relativepathstring])) {
485  $isset = true;
486  } elseif ($paramname == 'mode' && !empty($_SESSION['lastsearch_mode_'.$relativepathstring])) {
487  $isset = true;
488  }
489  } else {
490  $isset = (isset($_POST[$paramname]) || isset($_GET[$paramname])); // We must keep $_POST and $_GET here
491  }
492 
493  return $isset;
494 }
495 
504 function GETPOSTISARRAY($paramname, $method = 0)
505 {
506  // for $method test need return the same $val as GETPOST
507  if (empty($method)) {
508  $val = isset($_GET[$paramname]) ? $_GET[$paramname] : (isset($_POST[$paramname]) ? $_POST[$paramname] : '');
509  } elseif ($method == 1) {
510  $val = isset($_GET[$paramname]) ? $_GET[$paramname] : '';
511  } elseif ($method == 2) {
512  $val = isset($_POST[$paramname]) ? $_POST[$paramname] : '';
513  } elseif ($method == 3) {
514  $val = isset($_POST[$paramname]) ? $_POST[$paramname] : (isset($_GET[$paramname]) ? $_GET[$paramname] : '');
515  } else {
516  $val = 'BadFirstParameterForGETPOST';
517  }
518 
519  return is_array($val);
520 }
521 
551 function GETPOST($paramname, $check = 'alphanohtml', $method = 0, $filter = null, $options = null, $noreplace = 0)
552 {
553  global $mysoc, $user, $conf;
554 
555  if (empty($paramname)) {
556  return 'BadFirstParameterForGETPOST';
557  }
558  if (empty($check)) {
559  dol_syslog("Deprecated use of GETPOST, called with 1st param = ".$paramname." and 2nd param is '', when calling page ".$_SERVER["PHP_SELF"], LOG_WARNING);
560  // Enable this line to know who call the GETPOST with '' $check parameter.
561  //var_dump(debug_backtrace()[0]);
562  }
563 
564  if (empty($method)) {
565  $out = isset($_GET[$paramname]) ? $_GET[$paramname] : (isset($_POST[$paramname]) ? $_POST[$paramname] : '');
566  } elseif ($method == 1) {
567  $out = isset($_GET[$paramname]) ? $_GET[$paramname] : '';
568  } elseif ($method == 2) {
569  $out = isset($_POST[$paramname]) ? $_POST[$paramname] : '';
570  } elseif ($method == 3) {
571  $out = isset($_POST[$paramname]) ? $_POST[$paramname] : (isset($_GET[$paramname]) ? $_GET[$paramname] : '');
572  } else {
573  return 'BadThirdParameterForGETPOST';
574  }
575 
576  if (empty($method) || $method == 3 || $method == 4) {
577  $relativepathstring = $_SERVER["PHP_SELF"];
578  // Clean $relativepathstring
579  if (constant('DOL_URL_ROOT')) {
580  $relativepathstring = preg_replace('/^'.preg_quote(constant('DOL_URL_ROOT'), '/').'/', '', $relativepathstring);
581  }
582  $relativepathstring = preg_replace('/^\//', '', $relativepathstring);
583  $relativepathstring = preg_replace('/^custom\//', '', $relativepathstring);
584  //var_dump($relativepathstring);
585  //var_dump($user->default_values);
586 
587  // Code for search criteria persistence.
588  // Retrieve values if restore_lastsearch_values
589  if (!empty($_GET['restore_lastsearch_values'])) { // Use $_GET here and not GETPOST
590  if (!empty($_SESSION['lastsearch_values_'.$relativepathstring])) { // If there is saved values
591  $tmp = json_decode($_SESSION['lastsearch_values_'.$relativepathstring], true);
592  if (is_array($tmp)) {
593  foreach ($tmp as $key => $val) {
594  if ($key == $paramname) { // We are on the requested parameter
595  $out = $val;
596  break;
597  }
598  }
599  }
600  }
601  // If there is saved contextpage, page or limit
602  if ($paramname == 'contextpage' && !empty($_SESSION['lastsearch_contextpage_'.$relativepathstring])) {
603  $out = $_SESSION['lastsearch_contextpage_'.$relativepathstring];
604  } elseif ($paramname == 'limit' && !empty($_SESSION['lastsearch_limit_'.$relativepathstring])) {
605  $out = $_SESSION['lastsearch_limit_'.$relativepathstring];
606  } elseif ($paramname == 'page' && !empty($_SESSION['lastsearch_page_'.$relativepathstring])) {
607  $out = $_SESSION['lastsearch_page_'.$relativepathstring];
608  } elseif ($paramname == 'mode' && !empty($_SESSION['lastsearch_mode_'.$relativepathstring])) {
609  $out = $_SESSION['lastsearch_mode_'.$relativepathstring];
610  }
611  } elseif (!isset($_GET['sortfield'])) {
612  // Else, retrieve default values if we are not doing a sort
613  // 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
614  if (!empty($_GET['action']) && $_GET['action'] == 'create' && !isset($_GET[$paramname]) && !isset($_POST[$paramname])) {
615  // Search default value from $object->field
616  global $object;
617  if (is_object($object) && isset($object->fields[$paramname]['default'])) {
618  $out = $object->fields[$paramname]['default'];
619  }
620  }
621  if (!empty($conf->global->MAIN_ENABLE_DEFAULT_VALUES)) {
622  if (!empty($_GET['action']) && (preg_match('/^create|^add_price|^make/', $_GET['action']) || preg_match('/^presend/', $_GET['action'])) && !isset($_GET[$paramname]) && !isset($_POST[$paramname])) {
623  // Now search in setup to overwrite default values
624  if (!empty($user->default_values)) { // $user->default_values defined from menu 'Setup - Default values'
625  if (isset($user->default_values[$relativepathstring]['createform'])) {
626  foreach ($user->default_values[$relativepathstring]['createform'] as $defkey => $defval) {
627  $qualified = 0;
628  if ($defkey != '_noquery_') {
629  $tmpqueryarraytohave = explode('&', $defkey);
630  $tmpqueryarraywehave = explode('&', dol_string_nohtmltag($_SERVER['QUERY_STRING']));
631  $foundintru = 0;
632  foreach ($tmpqueryarraytohave as $tmpquerytohave) {
633  if (!in_array($tmpquerytohave, $tmpqueryarraywehave)) {
634  $foundintru = 1;
635  }
636  }
637  if (!$foundintru) {
638  $qualified = 1;
639  }
640  //var_dump($defkey.'-'.$qualified);
641  } else {
642  $qualified = 1;
643  }
644 
645  if ($qualified) {
646  if (isset($user->default_values[$relativepathstring]['createform'][$defkey][$paramname])) {
647  $out = $user->default_values[$relativepathstring]['createform'][$defkey][$paramname];
648  break;
649  }
650  }
651  }
652  }
653  }
654  } elseif (!empty($paramname) && !isset($_GET[$paramname]) && !isset($_POST[$paramname])) {
655  // Management of default search_filters and sort order
656  if (!empty($user->default_values)) {
657  // $user->default_values defined from menu 'Setup - Default values'
658  //var_dump($user->default_values[$relativepathstring]);
659  if ($paramname == 'sortfield' || $paramname == 'sortorder') {
660  // Sorted on which fields ? ASC or DESC ?
661  if (isset($user->default_values[$relativepathstring]['sortorder'])) {
662  // Even if paramname is sortfield, data are stored into ['sortorder...']
663  foreach ($user->default_values[$relativepathstring]['sortorder'] as $defkey => $defval) {
664  $qualified = 0;
665  if ($defkey != '_noquery_') {
666  $tmpqueryarraytohave = explode('&', $defkey);
667  $tmpqueryarraywehave = explode('&', dol_string_nohtmltag($_SERVER['QUERY_STRING']));
668  $foundintru = 0;
669  foreach ($tmpqueryarraytohave as $tmpquerytohave) {
670  if (!in_array($tmpquerytohave, $tmpqueryarraywehave)) {
671  $foundintru = 1;
672  }
673  }
674  if (!$foundintru) {
675  $qualified = 1;
676  }
677  //var_dump($defkey.'-'.$qualified);
678  } else {
679  $qualified = 1;
680  }
681 
682  if ($qualified) {
683  $forbidden_chars_to_replace = array(" ", "'", "/", "\\", ":", "*", "?", "\"", "<", ">", "|", "[", "]", ";", "="); // we accept _, -, . and ,
684  foreach ($user->default_values[$relativepathstring]['sortorder'][$defkey] as $key => $val) {
685  if ($out) {
686  $out .= ', ';
687  }
688  if ($paramname == 'sortfield') {
689  $out .= dol_string_nospecial($key, '', $forbidden_chars_to_replace);
690  }
691  if ($paramname == 'sortorder') {
692  $out .= dol_string_nospecial($val, '', $forbidden_chars_to_replace);
693  }
694  }
695  //break; // No break for sortfield and sortorder so we can cumulate fields (is it realy usefull ?)
696  }
697  }
698  }
699  } elseif (isset($user->default_values[$relativepathstring]['filters'])) {
700  foreach ($user->default_values[$relativepathstring]['filters'] as $defkey => $defval) { // $defkey is a querystring like 'a=b&c=d', $defval is key of user
701  if (!empty($_GET['disabledefaultvalues'])) { // If set of default values has been disabled by a request parameter
702  continue;
703  }
704  $qualified = 0;
705  if ($defkey != '_noquery_') {
706  $tmpqueryarraytohave = explode('&', $defkey);
707  $tmpqueryarraywehave = explode('&', dol_string_nohtmltag($_SERVER['QUERY_STRING']));
708  $foundintru = 0;
709  foreach ($tmpqueryarraytohave as $tmpquerytohave) {
710  if (!in_array($tmpquerytohave, $tmpqueryarraywehave)) {
711  $foundintru = 1;
712  }
713  }
714  if (!$foundintru) {
715  $qualified = 1;
716  }
717  //var_dump($defkey.'-'.$qualified);
718  } else {
719  $qualified = 1;
720  }
721 
722  if ($qualified) {
723  // We must keep $_POST and $_GET here
724  if (isset($_POST['sall']) || isset($_POST['search_all']) || isset($_GET['sall']) || isset($_GET['search_all'])) {
725  // We made a search from quick search menu, do we still use default filter ?
726  if (empty($conf->global->MAIN_DISABLE_DEFAULT_FILTER_FOR_QUICK_SEARCH)) {
727  $forbidden_chars_to_replace = array(" ", "'", "/", "\\", ":", "*", "?", "\"", "<", ">", "|", "[", "]", ";", "="); // we accept _, -, . and ,
728  $out = dol_string_nospecial($user->default_values[$relativepathstring]['filters'][$defkey][$paramname], '', $forbidden_chars_to_replace);
729  }
730  } else {
731  $forbidden_chars_to_replace = array(" ", "'", "/", "\\", ":", "*", "?", "\"", "<", ">", "|", "[", "]", ";", "="); // we accept _, -, . and ,
732  $out = dol_string_nospecial($user->default_values[$relativepathstring]['filters'][$defkey][$paramname], '', $forbidden_chars_to_replace);
733  }
734  break;
735  }
736  }
737  }
738  }
739  }
740  }
741  }
742  }
743 
744  // Substitution variables for GETPOST (used to get final url with variable parameters or final default value with variable parameters)
745  // Example of variables: __DAY__, __MONTH__, __YEAR__, __MYCOMPANY_COUNTRY_ID__, __USER_ID__, ...
746  // 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.
747  if (!is_array($out) && empty($_POST[$paramname]) && empty($noreplace)) {
748  $reg = array();
749  $maxloop = 20;
750  $loopnb = 0; // Protection against infinite loop
751  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.
752  $loopnb++;
753  $newout = '';
754 
755  if ($reg[1] == 'DAY') {
756  $tmp = dol_getdate(dol_now(), true);
757  $newout = $tmp['mday'];
758  } elseif ($reg[1] == 'MONTH') {
759  $tmp = dol_getdate(dol_now(), true);
760  $newout = $tmp['mon'];
761  } elseif ($reg[1] == 'YEAR') {
762  $tmp = dol_getdate(dol_now(), true);
763  $newout = $tmp['year'];
764  } elseif ($reg[1] == 'PREVIOUS_DAY') {
765  $tmp = dol_getdate(dol_now(), true);
766  $tmp2 = dol_get_prev_day($tmp['mday'], $tmp['mon'], $tmp['year']);
767  $newout = $tmp2['day'];
768  } elseif ($reg[1] == 'PREVIOUS_MONTH') {
769  $tmp = dol_getdate(dol_now(), true);
770  $tmp2 = dol_get_prev_month($tmp['mon'], $tmp['year']);
771  $newout = $tmp2['month'];
772  } elseif ($reg[1] == 'PREVIOUS_YEAR') {
773  $tmp = dol_getdate(dol_now(), true);
774  $newout = ($tmp['year'] - 1);
775  } elseif ($reg[1] == 'NEXT_DAY') {
776  $tmp = dol_getdate(dol_now(), true);
777  $tmp2 = dol_get_next_day($tmp['mday'], $tmp['mon'], $tmp['year']);
778  $newout = $tmp2['day'];
779  } elseif ($reg[1] == 'NEXT_MONTH') {
780  $tmp = dol_getdate(dol_now(), true);
781  $tmp2 = dol_get_next_month($tmp['mon'], $tmp['year']);
782  $newout = $tmp2['month'];
783  } elseif ($reg[1] == 'NEXT_YEAR') {
784  $tmp = dol_getdate(dol_now(), true);
785  $newout = ($tmp['year'] + 1);
786  } elseif ($reg[1] == 'MYCOMPANY_COUNTRY_ID' || $reg[1] == 'MYCOUNTRY_ID' || $reg[1] == 'MYCOUNTRYID') {
787  $newout = $mysoc->country_id;
788  } elseif ($reg[1] == 'USER_ID' || $reg[1] == 'USERID') {
789  $newout = $user->id;
790  } elseif ($reg[1] == 'USER_SUPERVISOR_ID' || $reg[1] == 'SUPERVISOR_ID' || $reg[1] == 'SUPERVISORID') {
791  $newout = $user->fk_user;
792  } elseif ($reg[1] == 'ENTITY_ID' || $reg[1] == 'ENTITYID') {
793  $newout = $conf->entity;
794  } else {
795  $newout = ''; // Key not found, we replace with empty string
796  }
797  //var_dump('__'.$reg[1].'__ -> '.$newout);
798  $out = preg_replace('/__'.preg_quote($reg[1], '/').'__/', $newout, $out);
799  }
800  }
801 
802  // Check rule
803  if (preg_match('/^array/', $check)) { // If 'array' or 'array:restricthtml' or 'array:aZ09' or 'array:intcomma'
804  if (!is_array($out) || empty($out)) {
805  $out = array();
806  } else {
807  $tmparray = explode(':', $check);
808  if (!empty($tmparray[1])) {
809  $tmpcheck = $tmparray[1];
810  } else {
811  $tmpcheck = 'alphanohtml';
812  }
813  foreach ($out as $outkey => $outval) {
814  $out[$outkey] = sanitizeVal($outval, $tmpcheck, $filter, $options);
815  }
816  }
817  } else {
818  // If field name is 'search_xxx' then we force the add of space after each < and > (when following char is numeric) because it means
819  // 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
820  if (strpos($paramname, 'search_') === 0) {
821  $out = preg_replace('/([<>])([-+]?\d)/', '\1 \2', $out);
822  }
823 
824  $out = sanitizeVal($out, $check, $filter, $options);
825  }
826 
827  // Sanitizing for special parameters.
828  // Note: There is no reason to allow the backtopage, backtolist or backtourl parameter to contains an external URL. Only relative URLs are allowed.
829  if ($paramname == 'backtopage' || $paramname == 'backtolist' || $paramname == 'backtourl') {
830  $out = str_replace('\\', '/', $out); // Can be before the loop because only 1 char is replaced. No risk to get it after other replacements.
831  $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.
832  do {
833  $oldstringtoclean = $out;
834  $out = str_ireplace(array('javascript', 'vbscript', '&colon', '&#'), '', $out);
835  $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'
836  $out = preg_replace(array('/^[a-z]*\/\s*\/+/i'), '', $out); // We remove schema*// to remove external URL
837  } while ($oldstringtoclean != $out);
838  }
839 
840  // Code for search criteria persistence.
841  // Save data into session if key start with 'search_' or is 'smonth', 'syear', 'month', 'year'
842  if (empty($method) || $method == 3 || $method == 4) {
843  if (preg_match('/^search_/', $paramname) || in_array($paramname, array('sortorder', 'sortfield'))) {
844  //var_dump($paramname.' - '.$out.' '.$user->default_values[$relativepathstring]['filters'][$paramname]);
845 
846  // We save search key only if $out not empty that means:
847  // - posted value not empty, or
848  // - 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).
849 
850  if ($out != '' && isset($user)) {// $out = '0' or 'abc', it is a search criteria to keep
851  $user->lastsearch_values_tmp[$relativepathstring][$paramname] = $out;
852  }
853  }
854  }
855 
856  return $out;
857 }
858 
868 function GETPOSTINT($paramname, $method = 0)
869 {
870  return (int) GETPOST($paramname, 'int', $method, null, null, 0);
871 }
872 
873 
884 function checkVal($out = '', $check = 'alphanohtml', $filter = null, $options = null)
885 {
886  return sanitizeVal($out, $check, $filter, $options);
887 }
888 
898 function sanitizeVal($out = '', $check = 'alphanohtml', $filter = null, $options = null)
899 {
900  global $conf;
901 
902  // TODO : use class "Validate" to perform tests (and add missing tests) if needed for factorize
903  // Check is done after replacement
904  switch ($check) {
905  case 'none':
906  break;
907  case 'int': // Check param is a numeric value (integer but also float or hexadecimal)
908  if (!is_numeric($out)) {
909  $out = '';
910  }
911  break;
912  case 'intcomma':
913  if (preg_match('/[^0-9,-]+/i', $out)) {
914  $out = '';
915  }
916  break;
917  case 'san_alpha':
918  $out = filter_var($out, FILTER_SANITIZE_STRING);
919  break;
920  case 'email':
921  $out = filter_var($out, FILTER_SANITIZE_EMAIL);
922  break;
923  case 'aZ':
924  if (!is_array($out)) {
925  $out = trim($out);
926  if (preg_match('/[^a-z]+/i', $out)) {
927  $out = '';
928  }
929  }
930  break;
931  case 'aZ09':
932  if (!is_array($out)) {
933  $out = trim($out);
934  if (preg_match('/[^a-z0-9_\-\.]+/i', $out)) {
935  $out = '';
936  }
937  }
938  break;
939  case 'aZ09arobase': // great to sanitize objecttype parameter
940  if (!is_array($out)) {
941  $out = trim($out);
942  if (preg_match('/[^a-z0-9_\-\.@]+/i', $out)) {
943  $out = '';
944  }
945  }
946  break;
947  case 'aZ09comma': // great to sanitize sortfield or sortorder params that can be t.abc,t.def_gh
948  if (!is_array($out)) {
949  $out = trim($out);
950  if (preg_match('/[^a-z0-9_\-\.,]+/i', $out)) {
951  $out = '';
952  }
953  }
954  break;
955  case 'nohtml': // No html
956  $out = dol_string_nohtmltag($out, 0);
957  break;
958  case 'alpha': // No html and no ../ and "
959  case 'alphanohtml': // Recommended for most scalar parameters and search parameters
960  if (!is_array($out)) {
961  $out = trim($out);
962  do {
963  $oldstringtoclean = $out;
964  // Remove html tags
965  $out = dol_string_nohtmltag($out, 0);
966  // Remove also other dangerous string sequences
967  // '"' is dangerous because param in url can close the href= or src= and add javascript functions.
968  // '../' or '..\' is dangerous because it allows dir transversals
969  // Note &#38, '&#0000038', '&#x26'... is a simple char like '&' alone but there is no reason to accept such way to encode input data.
970  $out = str_ireplace(array('&#38', '&#0000038', '&#x26', '&quot', '&#34', '&#0000034', '&#x22', '"', '&#47', '&#0000047', '&#92', '&#0000092', '&#x2F', '../', '..\\'), '', $out);
971  } while ($oldstringtoclean != $out);
972  // keep lines feed
973  }
974  break;
975  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'
976  if (!is_array($out)) {
977  $out = trim($out);
978  do {
979  $oldstringtoclean = $out;
980  // Remove html tags
981  $out = dol_html_entity_decode($out, ENT_COMPAT | ENT_HTML5, 'UTF-8');
982  // '"' is dangerous because param in url can close the href= or src= and add javascript functions.
983  // '../' or '..\' is dangerous because it allows dir transversals
984  // Note &#38, '&#0000038', '&#x26'... is a simple char like '&' alone but there is no reason to accept such way to encode input data.
985  $out = str_ireplace(array('&#38', '&#0000038', '&#x26', '&quot', '&#34', '&#0000034', '&#x22', '"', '&#47', '&#0000047', '&#92', '&#0000092', '&#x2F', '../', '..\\'), '', $out);
986  } while ($oldstringtoclean != $out);
987  }
988  break;
989  case 'restricthtml': // Recommended for most html textarea
990  case 'restricthtmlnolink':
991  case 'restricthtmlallowunvalid':
992  $out = dol_htmlwithnojs($out, 1, $check);
993  break;
994  case 'custom':
995  if (!empty($out)) {
996  if (empty($filter)) {
997  return 'BadParameterForGETPOST - Param 3 of sanitizeVal()';
998  }
999  /*if (empty($options)) {
1000  return 'BadParameterForGETPOST - Param 4 of sanitizeVal()';
1001  }*/
1002  $out = filter_var($out, $filter, $options);
1003  }
1004  break;
1005  }
1006 
1007  return $out;
1008 }
1009 
1010 
1011 if (!function_exists('dol_getprefix')) {
1021  function dol_getprefix($mode = '')
1022  {
1023  // If prefix is for email (we need to have $conf already loaded for this case)
1024  if ($mode == 'email') {
1025  global $conf;
1026 
1027  if (!empty($conf->global->MAIL_PREFIX_FOR_EMAIL_ID)) { // If MAIL_PREFIX_FOR_EMAIL_ID is set
1028  if ($conf->global->MAIL_PREFIX_FOR_EMAIL_ID != 'SERVER_NAME') {
1029  return $conf->global->MAIL_PREFIX_FOR_EMAIL_ID;
1030  } elseif (isset($_SERVER["SERVER_NAME"])) { // If MAIL_PREFIX_FOR_EMAIL_ID is set to 'SERVER_NAME'
1031  return $_SERVER["SERVER_NAME"];
1032  }
1033  }
1034 
1035  // The recommended value if MAIL_PREFIX_FOR_EMAIL_ID is not defined (may be not defined for old versions)
1036  if (!empty($conf->file->instance_unique_id)) {
1037  return sha1('dolibarr'.$conf->file->instance_unique_id);
1038  }
1039 
1040  // For backward compatibility when instance_unique_id is not set
1041  return sha1(DOL_DOCUMENT_ROOT.DOL_URL_ROOT);
1042  }
1043 
1044  // If prefix is for session (no need to have $conf loaded)
1045  global $dolibarr_main_instance_unique_id, $dolibarr_main_cookie_cryptkey; // This is loaded by filefunc.inc.php
1046  $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
1047 
1048  // The recommended value (may be not defined for old versions)
1049  if (!empty($tmp_instance_unique_id)) {
1050  return sha1('dolibarr'.$tmp_instance_unique_id);
1051  }
1052 
1053  // For backward compatibility when instance_unique_id is not set
1054  if (isset($_SERVER["SERVER_NAME"]) && isset($_SERVER["DOCUMENT_ROOT"])) {
1055  return sha1($_SERVER["SERVER_NAME"].$_SERVER["DOCUMENT_ROOT"].DOL_DOCUMENT_ROOT.DOL_URL_ROOT);
1056  } else {
1057  return sha1(DOL_DOCUMENT_ROOT.DOL_URL_ROOT);
1058  }
1059  }
1060 }
1061 
1072 function dol_include_once($relpath, $classname = '')
1073 {
1074  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']
1075 
1076  $fullpath = dol_buildpath($relpath);
1077 
1078  if (!file_exists($fullpath)) {
1079  dol_syslog('functions::dol_include_once Tried to load unexisting file: '.$relpath, LOG_WARNING);
1080  return false;
1081  }
1082 
1083  if (!empty($classname) && !class_exists($classname)) {
1084  return include $fullpath;
1085  } else {
1086  return include_once $fullpath;
1087  }
1088 }
1089 
1090 
1101 function dol_buildpath($path, $type = 0, $returnemptyifnotfound = 0)
1102 {
1103  global $conf;
1104 
1105  $path = preg_replace('/^\//', '', $path);
1106 
1107  if (empty($type)) { // For a filesystem path
1108  $res = DOL_DOCUMENT_ROOT.'/'.$path; // Standard default path
1109  if (is_array($conf->file->dol_document_root)) {
1110  foreach ($conf->file->dol_document_root as $key => $dirroot) { // ex: array("main"=>"/home/main/htdocs", "alt0"=>"/home/dirmod/htdocs", ...)
1111  if ($key == 'main') {
1112  continue;
1113  }
1114  if (file_exists($dirroot.'/'.$path)) {
1115  $res = $dirroot.'/'.$path;
1116  return $res;
1117  }
1118  }
1119  }
1120  if ($returnemptyifnotfound) {
1121  // Not found into alternate dir
1122  if ($returnemptyifnotfound == 1 || !file_exists($res)) {
1123  return '';
1124  }
1125  }
1126  } else {
1127  // For an url path
1128  // We try to get local path of file on filesystem from url
1129  // Note that trying to know if a file on disk exist by forging path on disk from url
1130  // works only for some web server and some setup. This is bugged when
1131  // using proxy, rewriting, virtual path, etc...
1132  $res = '';
1133  if ($type == 1) {
1134  $res = DOL_URL_ROOT.'/'.$path; // Standard value
1135  }
1136  if ($type == 2) {
1137  $res = DOL_MAIN_URL_ROOT.'/'.$path; // Standard value
1138  }
1139  if ($type == 3) {
1140  $res = DOL_URL_ROOT.'/'.$path;
1141  }
1142 
1143  foreach ($conf->file->dol_document_root as $key => $dirroot) { // ex: array(["main"]=>"/home/main/htdocs", ["alt0"]=>"/home/dirmod/htdocs", ...)
1144  if ($key == 'main') {
1145  if ($type == 3) {
1146  global $dolibarr_main_url_root;
1147 
1148  // Define $urlwithroot
1149  $urlwithouturlroot = preg_replace('/'.preg_quote(DOL_URL_ROOT, '/').'$/i', '', trim($dolibarr_main_url_root));
1150  $urlwithroot = $urlwithouturlroot.DOL_URL_ROOT; // This is to use external domain name found into config file
1151  //$urlwithroot=DOL_MAIN_URL_ROOT; // This is to use same domain name than current
1152 
1153  $res = (preg_match('/^http/i', $conf->file->dol_url_root[$key]) ? '' : $urlwithroot).'/'.$path; // Test on start with http is for old conf syntax
1154  }
1155  continue;
1156  }
1157  $regs = array();
1158  preg_match('/^([^\?]+(\.css\.php|\.css|\.js\.php|\.js|\.png|\.jpg|\.php)?)/i', $path, $regs); // Take part before '?'
1159  if (!empty($regs[1])) {
1160  //print $key.'-'.$dirroot.'/'.$path.'-'.$conf->file->dol_url_root[$type].'<br>'."\n";
1161  if (file_exists($dirroot.'/'.$regs[1])) {
1162  if ($type == 1) {
1163  $res = (preg_match('/^http/i', $conf->file->dol_url_root[$key]) ? '' : DOL_URL_ROOT).$conf->file->dol_url_root[$key].'/'.$path;
1164  }
1165  if ($type == 2) {
1166  $res = (preg_match('/^http/i', $conf->file->dol_url_root[$key]) ? '' : DOL_MAIN_URL_ROOT).$conf->file->dol_url_root[$key].'/'.$path;
1167  }
1168  if ($type == 3) {
1169  global $dolibarr_main_url_root;
1170 
1171  // Define $urlwithroot
1172  $urlwithouturlroot = preg_replace('/'.preg_quote(DOL_URL_ROOT, '/').'$/i', '', trim($dolibarr_main_url_root));
1173  $urlwithroot = $urlwithouturlroot.DOL_URL_ROOT; // This is to use external domain name found into config file
1174  //$urlwithroot=DOL_MAIN_URL_ROOT; // This is to use same domain name than current
1175 
1176  $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
1177  }
1178  break;
1179  }
1180  }
1181  }
1182  }
1183 
1184  return $res;
1185 }
1186 
1198 function dol_clone($object, $native = 0)
1199 {
1200  if ($native == 0) {
1201  // deprecated method, use the method with native = 2 instead
1202  $tmpsavdb = null;
1203  if (isset($object->db) && isset($object->db->db) && is_object($object->db->db) && get_class($object->db->db) == 'PgSql\Connection') {
1204  $tmpsavdb = $object->db;
1205  unset($object->db); // Such property can not be serialized with pgsl (when object->db->db = 'PgSql\Connection')
1206  }
1207 
1208  $myclone = unserialize(serialize($object)); // serialize then unserialize is a hack to be sure to have a new object for all fields
1209 
1210  if (!empty($tmpsavdb)) {
1211  $object->db = $tmpsavdb;
1212  }
1213  } elseif ($native == 2) {
1214  // recommended method to have a full isolated cloned object
1215  $myclone = new stdClass();
1216  $tmparray = get_object_vars($object); // return only public properties
1217 
1218  if (is_array($tmparray)) {
1219  foreach ($tmparray as $propertykey => $propertyval) {
1220  if (is_scalar($propertyval) || is_array($propertyval)) {
1221  $myclone->$propertykey = $propertyval;
1222  }
1223  }
1224  }
1225  } else {
1226  $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)
1227  }
1228 
1229  return $myclone;
1230 }
1231 
1241 function dol_size($size, $type = '')
1242 {
1243  global $conf;
1244  if (empty($conf->dol_optimize_smallscreen)) {
1245  return $size;
1246  }
1247  if ($type == 'width' && $size > 250) {
1248  return 250;
1249  } else {
1250  return 10;
1251  }
1252 }
1253 
1254 
1266 function dol_sanitizeFileName($str, $newstr = '_', $unaccent = 1)
1267 {
1268  // List of special chars for filenames in windows are defined on page https://docs.microsoft.com/en-us/windows/win32/fileio/naming-a-file
1269  // Char '>' '<' '|' '$' and ';' are special chars for shells.
1270  // Char '/' and '\' are file delimiters.
1271  // 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
1272  $filesystem_forbidden_chars = array('<', '>', '/', '\\', '?', '*', '|', '"', ':', '°', '$', ';');
1273  $tmp = dol_string_nospecial($unaccent ? dol_string_unaccent($str) : $str, $newstr, $filesystem_forbidden_chars);
1274  $tmp = preg_replace('/\-\-+/', '_', $tmp);
1275  $tmp = preg_replace('/\s+\-([^\s])/', ' _$1', $tmp);
1276  $tmp = preg_replace('/\s+\-$/', '', $tmp);
1277  $tmp = str_replace('..', '', $tmp);
1278  return $tmp;
1279 }
1280 
1292 function dol_sanitizePathName($str, $newstr = '_', $unaccent = 1)
1293 {
1294  // List of special chars for filenames in windows are defined on page https://docs.microsoft.com/en-us/windows/win32/fileio/naming-a-file
1295  // Char '>' '<' '|' '$' and ';' are special chars for shells.
1296  // 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
1297  $filesystem_forbidden_chars = array('<', '>', '?', '*', '|', '"', '°', '$', ';');
1298  $tmp = dol_string_nospecial($unaccent ? dol_string_unaccent($str) : $str, $newstr, $filesystem_forbidden_chars);
1299  $tmp = preg_replace('/\-\-+/', '_', $tmp);
1300  $tmp = preg_replace('/\s+\-([^\s])/', ' _$1', $tmp);
1301  $tmp = preg_replace('/\s+\-$/', '', $tmp);
1302  $tmp = str_replace('..', '', $tmp);
1303  return $tmp;
1304 }
1305 
1313 function dol_sanitizeUrl($stringtoclean, $type = 1)
1314 {
1315  // 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)
1316  // We should use dol_string_nounprintableascii but function may not be yet loaded/available
1317  $stringtoclean = preg_replace('/[\x00-\x1F\x7F]/u', '', $stringtoclean); // /u operator makes UTF8 valid characters being ignored so are not included into the replace
1318  // We clean html comments because some hacks try to obfuscate evil strings by inserting HTML comments. Example: on<!-- -->error=alert(1)
1319  $stringtoclean = preg_replace('/<!--[^>]*-->/', '', $stringtoclean);
1320 
1321  $stringtoclean = str_replace('\\', '/', $stringtoclean);
1322  if ($type == 1) {
1323  // removing : should disable links to external url like http:aaa)
1324  // removing ';' should disable "named" html entities encode into an url (we should not have this into an url)
1325  $stringtoclean = str_replace(array(':', ';', '@'), '', $stringtoclean);
1326  }
1327 
1328  do {
1329  $oldstringtoclean = $stringtoclean;
1330  // removing '&colon' should disable links to external url like http:aaa)
1331  // removing '&#' should disable "numeric" html entities encode into an url (we should not have this into an url)
1332  $stringtoclean = str_ireplace(array('javascript', 'vbscript', '&colon', '&#'), '', $stringtoclean);
1333  } while ($oldstringtoclean != $stringtoclean);
1334 
1335  if ($type == 1) {
1336  // removing '//' should disable links to external url like //aaa or http//)
1337  $stringtoclean = preg_replace(array('/^[a-z]*\/\/+/i'), '', $stringtoclean);
1338  }
1339 
1340  return $stringtoclean;
1341 }
1342 
1349 function dol_sanitizeEmail($stringtoclean)
1350 {
1351  do {
1352  $oldstringtoclean = $stringtoclean;
1353  $stringtoclean = str_ireplace(array('"', ':', '[', ']',"\n", "\r", '\\', '\/'), '', $stringtoclean);
1354  } while ($oldstringtoclean != $stringtoclean);
1355 
1356  return $stringtoclean;
1357 }
1358 
1367 function dol_string_unaccent($str)
1368 {
1369  global $conf;
1370 
1371  if (is_null($str)) {
1372  return '';
1373  }
1374 
1375  if (utf8_check($str)) {
1376  if (extension_loaded('intl') && !empty($conf->global->MAIN_UNACCENT_USE_TRANSLITERATOR)) {
1377  $transliterator = \Transliterator::createFromRules(':: Any-Latin; :: Latin-ASCII; :: NFD; :: [:Nonspacing Mark:] Remove; :: NFC;', \Transliterator::FORWARD);
1378  return $transliterator->transliterate($str);
1379  }
1380  // See http://www.utf8-chartable.de/
1381  $string = rawurlencode($str);
1382  $replacements = array(
1383  '%C3%80' => 'A', '%C3%81' => 'A', '%C3%82' => 'A', '%C3%83' => 'A', '%C3%84' => 'A', '%C3%85' => 'A',
1384  '%C3%87' => 'C',
1385  '%C3%88' => 'E', '%C3%89' => 'E', '%C3%8A' => 'E', '%C3%8B' => 'E',
1386  '%C3%8C' => 'I', '%C3%8D' => 'I', '%C3%8E' => 'I', '%C3%8F' => 'I',
1387  '%C3%91' => 'N',
1388  '%C3%92' => 'O', '%C3%93' => 'O', '%C3%94' => 'O', '%C3%95' => 'O', '%C3%96' => 'O',
1389  '%C5%A0' => 'S',
1390  '%C3%99' => 'U', '%C3%9A' => 'U', '%C3%9B' => 'U', '%C3%9C' => 'U',
1391  '%C3%9D' => 'Y', '%C5%B8' => 'y',
1392  '%C3%A0' => 'a', '%C3%A1' => 'a', '%C3%A2' => 'a', '%C3%A3' => 'a', '%C3%A4' => 'a', '%C3%A5' => 'a',
1393  '%C3%A7' => 'c',
1394  '%C3%A8' => 'e', '%C3%A9' => 'e', '%C3%AA' => 'e', '%C3%AB' => 'e',
1395  '%C3%AC' => 'i', '%C3%AD' => 'i', '%C3%AE' => 'i', '%C3%AF' => 'i',
1396  '%C3%B1' => 'n',
1397  '%C3%B2' => 'o', '%C3%B3' => 'o', '%C3%B4' => 'o', '%C3%B5' => 'o', '%C3%B6' => 'o',
1398  '%C5%A1' => 's',
1399  '%C3%B9' => 'u', '%C3%BA' => 'u', '%C3%BB' => 'u', '%C3%BC' => 'u',
1400  '%C3%BD' => 'y', '%C3%BF' => 'y'
1401  );
1402  $string = strtr($string, $replacements);
1403  return rawurldecode($string);
1404  } else {
1405  // See http://www.ascii-code.com/
1406  $string = strtr(
1407  $str,
1408  "\xC0\xC1\xC2\xC3\xC4\xC5\xC7
1409  \xC8\xC9\xCA\xCB\xCC\xCD\xCE\xCF\xD0\xD1
1410  \xD2\xD3\xD4\xD5\xD8\xD9\xDA\xDB\xDD
1411  \xE0\xE1\xE2\xE3\xE4\xE5\xE7\xE8\xE9\xEA\xEB
1412  \xEC\xED\xEE\xEF\xF0\xF1\xF2\xF3\xF4\xF5\xF8
1413  \xF9\xFA\xFB\xFC\xFD\xFF",
1414  "AAAAAAC
1415  EEEEIIIIDN
1416  OOOOOUUUY
1417  aaaaaaceeee
1418  iiiidnooooo
1419  uuuuyy"
1420  );
1421  $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"));
1422  return $string;
1423  }
1424 }
1425 
1438 function dol_string_nospecial($str, $newstr = '_', $badcharstoreplace = '', $badcharstoremove = '')
1439 {
1440  $forbidden_chars_to_replace = array(" ", "'", "/", "\\", ":", "*", "?", "\"", "<", ">", "|", "[", "]", ",", ";", "=", '°', '$', ';'); // more complete than dol_sanitizeFileName
1441  $forbidden_chars_to_remove = array();
1442  //$forbidden_chars_to_remove=array("(",")");
1443 
1444  if (is_array($badcharstoreplace)) {
1445  $forbidden_chars_to_replace = $badcharstoreplace;
1446  }
1447  if (is_array($badcharstoremove)) {
1448  $forbidden_chars_to_remove = $badcharstoremove;
1449  }
1450 
1451  return str_replace($forbidden_chars_to_replace, $newstr, str_replace($forbidden_chars_to_remove, "", $str));
1452 }
1453 
1454 
1468 function dol_string_nounprintableascii($str, $removetabcrlf = 1)
1469 {
1470  if ($removetabcrlf) {
1471  return preg_replace('/[\x00-\x1F\x7F]/u', '', $str); // /u operator makes UTF8 valid characters being ignored so are not included into the replace
1472  } else {
1473  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
1474  }
1475 }
1476 
1485 function dol_escape_js($stringtoescape, $mode = 0, $noescapebackslashn = 0)
1486 {
1487  if (is_null($stringtoescape)) {
1488  return '';
1489  }
1490 
1491  // escape quotes and backslashes, newlines, etc.
1492  $substitjs = array("&#039;"=>"\\'", "\r"=>'\\r');
1493  //$substitjs['</']='<\/'; // We removed this. Should be useless.
1494  if (empty($noescapebackslashn)) {
1495  $substitjs["\n"] = '\\n';
1496  $substitjs['\\'] = '\\\\';
1497  }
1498  if (empty($mode)) {
1499  $substitjs["'"] = "\\'";
1500  $substitjs['"'] = "\\'";
1501  } elseif ($mode == 1) {
1502  $substitjs["'"] = "\\'";
1503  } elseif ($mode == 2) {
1504  $substitjs['"'] = '\\"';
1505  } elseif ($mode == 3) {
1506  $substitjs["'"] = "\\'";
1507  $substitjs['"'] = "\\\"";
1508  }
1509  return strtr($stringtoescape, $substitjs);
1510 }
1511 
1518 function dol_escape_json($stringtoescape)
1519 {
1520  return str_replace('"', '\"', $stringtoescape);
1521 }
1522 
1534 function dol_escape_htmltag($stringtoescape, $keepb = 0, $keepn = 0, $noescapetags = '', $escapeonlyhtmltags = 0)
1535 {
1536  if ($noescapetags == 'common') {
1537  $noescapetags = 'html,body,a,b,em,hr,i,u,ul,li,br,div,img,font,p,span,strong,table,tr,td,th,tbody';
1538  }
1539 
1540  // escape quotes and backslashes, newlines, etc.
1541  if ($escapeonlyhtmltags) {
1542  $tmp = htmlspecialchars_decode((string) $stringtoescape, ENT_COMPAT);
1543  } else {
1544  $tmp = html_entity_decode((string) $stringtoescape, ENT_COMPAT, 'UTF-8');
1545  }
1546  if (!$keepb) {
1547  $tmp = strtr($tmp, array("<b>"=>'', '</b>'=>''));
1548  }
1549  if (!$keepn) {
1550  $tmp = strtr($tmp, array("\r"=>'\\r', "\n"=>'\\n'));
1551  }
1552 
1553  if ($escapeonlyhtmltags) {
1554  return htmlspecialchars($tmp, ENT_COMPAT, 'UTF-8');
1555  } else {
1556  // Escape tags to keep
1557  // TODO Does not works yet when there is attributes into tag
1558  $tmparrayoftags = array();
1559  if ($noescapetags) {
1560  $tmparrayoftags = explode(',', $noescapetags);
1561  }
1562  if (count($tmparrayoftags)) {
1563  foreach ($tmparrayoftags as $tagtoreplace) {
1564  $tmp = str_ireplace('<'.$tagtoreplace.'>', '__BEGINTAGTOREPLACE'.$tagtoreplace.'__', $tmp);
1565  $tmp = str_ireplace('</'.$tagtoreplace.'>', '__ENDTAGTOREPLACE'.$tagtoreplace.'__', $tmp);
1566  $tmp = str_ireplace('<'.$tagtoreplace.' />', '__BEGINENDTAGTOREPLACE'.$tagtoreplace.'__', $tmp);
1567  }
1568  }
1569 
1570  $result = htmlentities($tmp, ENT_COMPAT, 'UTF-8');
1571 
1572  if (count($tmparrayoftags)) {
1573  foreach ($tmparrayoftags as $tagtoreplace) {
1574  $result = str_ireplace('__BEGINTAGTOREPLACE'.$tagtoreplace.'__', '<'.$tagtoreplace.'>', $result);
1575  $result = str_ireplace('__ENDTAGTOREPLACE'.$tagtoreplace.'__', '</'.$tagtoreplace.'>', $result);
1576  $result = str_ireplace('__BEGINENDTAGTOREPLACE'.$tagtoreplace.'__', '<'.$tagtoreplace.' />', $result);
1577  }
1578  }
1579 
1580  return $result;
1581  }
1582 }
1583 
1591 function dol_strtolower($string, $encoding = "UTF-8")
1592 {
1593  if (function_exists('mb_strtolower')) {
1594  return mb_strtolower($string, $encoding);
1595  } else {
1596  return strtolower($string);
1597  }
1598 }
1599 
1607 function dol_strtoupper($string, $encoding = "UTF-8")
1608 {
1609  if (function_exists('mb_strtoupper')) {
1610  return mb_strtoupper($string, $encoding);
1611  } else {
1612  return strtoupper($string);
1613  }
1614 }
1615 
1623 function dol_ucfirst($string, $encoding = "UTF-8")
1624 {
1625  if (function_exists('mb_substr')) {
1626  return mb_strtoupper(mb_substr($string, 0, 1, $encoding), $encoding).mb_substr($string, 1, null, $encoding);
1627  } else {
1628  return ucfirst($string);
1629  }
1630 }
1631 
1639 function dol_ucwords($string, $encoding = "UTF-8")
1640 {
1641  if (function_exists('mb_convert_case')) {
1642  return mb_convert_case($string, MB_CASE_TITLE, $encoding);
1643  } else {
1644  return ucwords($string);
1645  }
1646 }
1647 
1669 function dol_syslog($message, $level = LOG_INFO, $ident = 0, $suffixinfilename = '', $restricttologhandler = '', $logcontext = null)
1670 {
1671  global $conf, $user, $debugbar;
1672 
1673  // If syslog module enabled
1674  if (!isModEnabled('syslog')) {
1675  return;
1676  }
1677 
1678  // Check if we are into execution of code of a website
1679  if (defined('USEEXTERNALSERVER') && !defined('USEDOLIBARRSERVER') && !defined('USEDOLIBARREDITOR')) {
1680  global $website, $websitekey;
1681  if (is_object($website) && !empty($website->ref)) {
1682  $suffixinfilename .= '_website_'.$website->ref;
1683  } elseif (!empty($websitekey)) {
1684  $suffixinfilename .= '_website_'.$websitekey;
1685  }
1686  }
1687 
1688  // Check if we have a forced suffix
1689  if (defined('USESUFFIXINLOG')) {
1690  $suffixinfilename .= constant('USESUFFIXINLOG');
1691  }
1692 
1693  if ($ident < 0) {
1694  foreach ($conf->loghandlers as $loghandlerinstance) {
1695  $loghandlerinstance->setIdent($ident);
1696  }
1697  }
1698 
1699  if (!empty($message)) {
1700  // Test log level
1701  $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');
1702  if (!array_key_exists($level, $logLevels)) {
1703  throw new Exception('Incorrect log level');
1704  }
1705  if ($level > getDolGlobalInt('SYSLOG_LEVEL')) {
1706  return;
1707  }
1708 
1709  if (empty($conf->global->MAIN_SHOW_PASSWORD_INTO_LOG)) {
1710  $message = preg_replace('/password=\'[^\']*\'/', 'password=\'hidden\'', $message); // protection to avoid to have value of password in log
1711  }
1712 
1713  // If adding log inside HTML page is required
1714  if ((!empty($_REQUEST['logtohtml']) && !empty($conf->global->MAIN_ENABLE_LOG_TO_HTML))
1715  || (!empty($user->rights->debugbar->read) && is_object($debugbar))) {
1716  $conf->logbuffer[] = dol_print_date(time(), "%Y-%m-%d %H:%M:%S")." ".$logLevels[$level]." ".$message;
1717  }
1718 
1719  //TODO: Remove this. MAIN_ENABLE_LOG_INLINE_HTML should be deprecated and use a log handler dedicated to HTML output
1720  // If html log tag enabled and url parameter log defined, we show output log on HTML comments
1721  if (!empty($conf->global->MAIN_ENABLE_LOG_INLINE_HTML) && !empty($_GET["log"])) {
1722  print "\n\n<!-- Log start\n";
1723  print dol_escape_htmltag($message)."\n";
1724  print "Log end -->\n";
1725  }
1726 
1727  $data = array(
1728  'message' => $message,
1729  'script' => (isset($_SERVER['PHP_SELF']) ? basename($_SERVER['PHP_SELF'], '.php') : false),
1730  'level' => $level,
1731  'user' => ((is_object($user) && $user->id) ? $user->login : false),
1732  'ip' => false
1733  );
1734 
1735  $remoteip = getUserRemoteIP(); // Get ip when page run on a web server
1736  if (!empty($remoteip)) {
1737  $data['ip'] = $remoteip;
1738  // This is when server run behind a reverse proxy
1739  if (!empty($_SERVER['HTTP_X_FORWARDED_FOR']) && $_SERVER['HTTP_X_FORWARDED_FOR'] != $remoteip) {
1740  $data['ip'] = $_SERVER['HTTP_X_FORWARDED_FOR'].' -> '.$data['ip'];
1741  } elseif (!empty($_SERVER['HTTP_CLIENT_IP']) && $_SERVER['HTTP_CLIENT_IP'] != $remoteip) {
1742  $data['ip'] = $_SERVER['HTTP_CLIENT_IP'].' -> '.$data['ip'];
1743  }
1744  } elseif (!empty($_SERVER['SERVER_ADDR'])) {
1745  // This is when PHP session is ran inside a web server but not inside a client request (example: init code of apache)
1746  $data['ip'] = $_SERVER['SERVER_ADDR'];
1747  } elseif (!empty($_SERVER['COMPUTERNAME'])) {
1748  // 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).
1749  $data['ip'] = $_SERVER['COMPUTERNAME'].(empty($_SERVER['USERNAME']) ? '' : '@'.$_SERVER['USERNAME']);
1750  } elseif (!empty($_SERVER['LOGNAME'])) {
1751  // 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).
1752  $data['ip'] = '???@'.$_SERVER['LOGNAME'];
1753  }
1754 
1755  // Loop on each log handler and send output
1756  foreach ($conf->loghandlers as $loghandlerinstance) {
1757  if ($restricttologhandler && $loghandlerinstance->code != $restricttologhandler) {
1758  continue;
1759  }
1760  $loghandlerinstance->export($data, $suffixinfilename);
1761  }
1762  unset($data);
1763  }
1764 
1765  if ($ident > 0) {
1766  foreach ($conf->loghandlers as $loghandlerinstance) {
1767  $loghandlerinstance->setIdent($ident);
1768  }
1769  }
1770 }
1771 
1788 function dolButtonToOpenUrlInDialogPopup($name, $label, $buttonstring, $url, $disabled = '', $morecss = 'classlink button bordertransp', $jsonopen = '', $backtopagejsfields = '', $accesskey = '')
1789 {
1790  global $conf;
1791 
1792  if (strpos($url, '?') > 0) {
1793  $url .= '&dol_hide_topmenu=1&dol_hide_leftmenu=1&dol_openinpopup='.urlencode($name);
1794  } else {
1795  $url .= '?dol_hide_topmenu=1&dol_hide_leftmenu=1&dol_openinpopup='.urlencode($name);
1796  }
1797 
1798  $out = '';
1799 
1800  $backtopagejsfieldsid = ''; $backtopagejsfieldslabel = '';
1801  if ($backtopagejsfields) {
1802  $tmpbacktopagejsfields = explode(':', $backtopagejsfields);
1803  if (empty($tmpbacktopagejsfields[1])) { // If the part 'keyforpopupid:' is missing, we add $name for it.
1804  $backtopagejsfields = $name.":".$backtopagejsfields;
1805  $tmp2backtopagejsfields = explode(',', $tmpbacktopagejsfields[0]);
1806  } else {
1807  $tmp2backtopagejsfields = explode(',', $tmpbacktopagejsfields[1]);
1808  }
1809  $backtopagejsfieldsid = empty($tmp2backtopagejsfields[0]) ? '' : $tmp2backtopagejsfields[0];
1810  $backtopagejsfieldslabel = empty($tmp2backtopagejsfields[1]) ? '' : $tmp2backtopagejsfields[1];
1811  $url .= '&backtopagejsfields='.urlencode($backtopagejsfields);
1812  }
1813 
1814  //print '<input type="submit" class="button bordertransp"'.$disabled.' value="'.dol_escape_htmltag($langs->trans("MediaFiles")).'" name="file_manager">';
1815  $out .= '<!-- a link for button to open url into a dialog popup with backtopagejsfields = '.$backtopagejsfields.' -->';
1816  $out .= '<a '.($accesskey ? ' accesskey="'.$accesskey.'"' : '').' class="cursorpointer button_'.$name.($morecss ? ' '.$morecss : '').'"'.$disabled.' title="'.dol_escape_htmltag($label).'"';
1817  if (empty($conf->use_javascript_ajax)) {
1818  $out .= ' href="'.DOL_URL_ROOT.$url.'" target="_blank"';
1819  } elseif ($jsonopen) {
1820  $out .= ' href="#" onclick="javascript:'.$jsonopen.'"';
1821  } else {
1822  $out .= ' href="#"';
1823  }
1824  $out .= '>'.$buttonstring.'</a>';
1825 
1826  if (!empty($conf->use_javascript_ajax)) {
1827  // Add code to open url using the popup. Add also hidden field to retreive the returned variables
1828  $out .= '<!-- code to open popup and variables to retreive returned variables -->';
1829  $out .= '<div id="idfordialog'.$name.'" class="hidden">div for dialog</div>';
1830  $out .= '<div id="varforreturndialogid'.$name.'" class="hidden">div for returned id</div>';
1831  $out .= '<div id="varforreturndialoglabel'.$name.'" class="hidden">div for returned label</div>';
1832  $out .= '<!-- Add js code to open dialog popup on dialog -->';
1833  $out .= '<script nonce="'.getNonce().'" type="text/javascript">
1834  jQuery(document).ready(function () {
1835  jQuery(".button_'.$name.'").click(function () {
1836  console.log(\'Open popup with jQuery(...).dialog() on URL '.dol_escape_js(DOL_URL_ROOT.$url).'\');
1837  var $tmpdialog = $(\'#idfordialog'.$name.'\');
1838  $tmpdialog.html(\'<iframe class="iframedialog" id="iframedialog'.$name.'" style="border: 0px;" src="'.DOL_URL_ROOT.$url.'" width="100%" height="98%"></iframe>\');
1839  $tmpdialog.dialog({
1840  autoOpen: false,
1841  modal: true,
1842  height: (window.innerHeight - 150),
1843  width: \'80%\',
1844  title: \''.dol_escape_js($label).'\',
1845  open: function (event, ui) {
1846  console.log("open popup name='.$name.', backtopagejsfields='.$backtopagejsfields.'");
1847  },
1848  close: function (event, ui) {
1849  returnedid = jQuery("#varforreturndialogid'.$name.'").text();
1850  returnedlabel = jQuery("#varforreturndialoglabel'.$name.'").text();
1851  console.log("popup has been closed. returnedid (js var defined into parent page)="+returnedid+" returnedlabel="+returnedlabel);
1852  if (returnedid != "" && returnedid != "div for returned id") {
1853  jQuery("#'.(empty($backtopagejsfieldsid)?"none":$backtopagejsfieldsid).'").val(returnedid);
1854  }
1855  if (returnedlabel != "" && returnedlabel != "div for returned label") {
1856  jQuery("#'.(empty($backtopagejsfieldslabel)?"none":$backtopagejsfieldslabel).'").val(returnedlabel);
1857  }
1858  }
1859  });
1860 
1861  $tmpdialog.dialog(\'open\');
1862  });
1863  });
1864  </script>';
1865  }
1866  return $out;
1867 }
1868 
1885 function dol_fiche_head($links = array(), $active = '0', $title = '', $notab = 0, $picto = '', $pictoisfullpath = 0, $morehtmlright = '', $morecss = '', $limittoshow = 0, $moretabssuffix = '')
1886 {
1887  print dol_get_fiche_head($links, $active, $title, $notab, $picto, $pictoisfullpath, $morehtmlright, $morecss, $limittoshow, $moretabssuffix);
1888 }
1889 
1906 function dol_get_fiche_head($links = array(), $active = '', $title = '', $notab = 0, $picto = '', $pictoisfullpath = 0, $morehtmlright = '', $morecss = '', $limittoshow = 0, $moretabssuffix = '', $dragdropfile = 0)
1907 {
1908  global $conf, $langs, $hookmanager;
1909 
1910  // Show title
1911  $showtitle = 1;
1912  if (!empty($conf->dol_optimize_smallscreen)) {
1913  $showtitle = 0;
1914  }
1915 
1916  $out = "\n".'<!-- dol_fiche_head - dol_get_fiche_head -->';
1917 
1918  if ((!empty($title) && $showtitle) || $morehtmlright || !empty($links)) {
1919  $out .= '<div class="tabs'.($picto ? '' : ' nopaddingleft').'" data-role="controlgroup" data-type="horizontal">'."\n";
1920  }
1921 
1922  // Show right part
1923  if ($morehtmlright) {
1924  $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.
1925  }
1926 
1927  // Show title
1928  if (!empty($title) && $showtitle && empty($conf->global->MAIN_OPTIMIZEFORTEXTBROWSER)) {
1929  $limittitle = 30;
1930  $out .= '<a class="tabTitle">';
1931  if ($picto) {
1932  $noprefix = $pictoisfullpath;
1933  if (strpos($picto, 'fontawesome_') !== false) {
1934  $noprefix = 1;
1935  }
1936  $out .= img_picto($title, ($noprefix ? '' : 'object_').$picto, '', $pictoisfullpath, 0, 0, '', 'imgTabTitle').' ';
1937  }
1938  $out .= '<span class="tabTitleText">'.dol_escape_htmltag(dol_trunc($title, $limittitle)).'</span>';
1939  $out .= '</a>';
1940  }
1941 
1942  // Show tabs
1943 
1944  // Define max of key (max may be higher than sizeof because of hole due to module disabling some tabs).
1945  $maxkey = -1;
1946  if (is_array($links) && !empty($links)) {
1947  $keys = array_keys($links);
1948  if (count($keys)) {
1949  $maxkey = max($keys);
1950  }
1951  }
1952 
1953  // Show tabs
1954  // if =0 we don't use the feature
1955  if (empty($limittoshow)) {
1956  $limittoshow = (empty($conf->global->MAIN_MAXTABS_IN_CARD) ? 99 : $conf->global->MAIN_MAXTABS_IN_CARD);
1957  }
1958  if (!empty($conf->dol_optimize_smallscreen)) {
1959  $limittoshow = 2;
1960  }
1961 
1962  $displaytab = 0;
1963  $nbintab = 0;
1964  $popuptab = 0;
1965  $outmore = '';
1966  for ($i = 0; $i <= $maxkey; $i++) {
1967  if ((is_numeric($active) && $i == $active) || (!empty($links[$i][2]) && !is_numeric($active) && $active == $links[$i][2])) {
1968  // If active tab is already present
1969  if ($i >= $limittoshow) {
1970  $limittoshow--;
1971  }
1972  }
1973  }
1974 
1975  for ($i = 0; $i <= $maxkey; $i++) {
1976  if ((is_numeric($active) && $i == $active) || (!empty($links[$i][2]) && !is_numeric($active) && $active == $links[$i][2])) {
1977  $isactive = true;
1978  } else {
1979  $isactive = false;
1980  }
1981 
1982  if ($i < $limittoshow || $isactive) {
1983  // Output entry with a visible tab
1984  $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])).' -->';
1985 
1986  if (isset($links[$i][2]) && $links[$i][2] == 'image') {
1987  if (!empty($links[$i][0])) {
1988  $out .= '<a class="tabimage'.($morecss ? ' '.$morecss : '').'" href="'.$links[$i][0].'">'.$links[$i][1].'</a>'."\n";
1989  } else {
1990  $out .= '<span class="tabspan">'.$links[$i][1].'</span>'."\n";
1991  }
1992  } elseif (!empty($links[$i][1])) {
1993  //print "x $i $active ".$links[$i][2]." z";
1994  $out .= '<div class="tab tab'.($isactive?'active':'unactive').'" style="margin: 0 !important">';
1995  if (!empty($links[$i][0])) {
1996  $titletoshow = preg_replace('/<.*$/', '', $links[$i][1]);
1997  $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).'">';
1998  }
1999  $out .= $links[$i][1];
2000  if (!empty($links[$i][0])) {
2001  $out .= '</a>'."\n";
2002  }
2003  $out .= empty($links[$i][4]) ? '' : $links[$i][4];
2004  $out .= '</div>';
2005  }
2006 
2007  $out .= '</div>';
2008  } else {
2009  // Add entry into the combo popup with the other tabs
2010  if (!$popuptab) {
2011  $popuptab = 1;
2012  $outmore .= '<div class="popuptabset wordwrap">'; // The css used to hide/show popup
2013  }
2014  $outmore .= '<div class="popuptab wordwrap" style="display:inherit;">';
2015  if (isset($links[$i][2]) && $links[$i][2] == 'image') {
2016  if (!empty($links[$i][0])) {
2017  $outmore .= '<a class="tabimage'.($morecss ? ' '.$morecss : '').'" href="'.$links[$i][0].'">'.$links[$i][1].'</a>'."\n";
2018  } else {
2019  $outmore .= '<span class="tabspan">'.$links[$i][1].'</span>'."\n";
2020  }
2021  } elseif (!empty($links[$i][1])) {
2022  $outmore .= '<a'.(!empty($links[$i][2]) ? ' id="'.$links[$i][2].'"' : '').' class="wordwrap inline-block'.($morecss ? ' '.$morecss : '').'" href="'.$links[$i][0].'">';
2023  $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.
2024  $outmore .= '</a>'."\n";
2025  }
2026  $outmore .= '</div>';
2027 
2028  $nbintab++;
2029  }
2030  $displaytab = $i;
2031  }
2032  if ($popuptab) {
2033  $outmore .= '</div>';
2034  }
2035 
2036  if ($popuptab) { // If there is some tabs not shown
2037  $left = ($langs->trans("DIRECTION") == 'rtl' ? 'right' : 'left');
2038  $right = ($langs->trans("DIRECTION") == 'rtl' ? 'left' : 'right');
2039  $widthofpopup = 200;
2040 
2041  $tabsname = $moretabssuffix;
2042  if (empty($tabsname)) {
2043  $tabsname = str_replace("@", "", $picto);
2044  }
2045  $out .= '<div id="moretabs'.$tabsname.'" class="inline-block tabsElem valignmiddle">';
2046  $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".
2047  $out .= '<div id="moretabsList'.$tabsname.'" style="width: '.$widthofpopup.'px; position: absolute; '.$left.': -999em; text-align: '.$left.'; margin:0px; padding:2px; z-index:10;">';
2048  $out .= $outmore;
2049  $out .= '</div>';
2050  $out .= '<div></div>';
2051  $out .= "</div>\n";
2052 
2053  $out .= '<script nonce="'.getNonce().'">';
2054  $out .= "$('#moretabs".$tabsname."').mouseenter( function() {
2055  var x = this.offsetLeft, y = this.offsetTop;
2056  console.log('mouseenter ".$left." x='+x+' y='+y+' window.innerWidth='+window.innerWidth);
2057  if ((window.innerWidth - x) < ".($widthofpopup + 10).") {
2058  $('#moretabsList".$tabsname."').css('".$right."','8px');
2059  }
2060  $('#moretabsList".$tabsname."').css('".$left."','auto');
2061  });
2062  ";
2063  $out .= "$('#moretabs".$tabsname."').mouseleave( function() { console.log('mouseleave ".$left."'); $('#moretabsList".$tabsname."').css('".$left."','-999em');});";
2064  $out .= "</script>";
2065  }
2066 
2067  if ((!empty($title) && $showtitle) || $morehtmlright || !empty($links)) {
2068  $out .= "</div>\n";
2069  }
2070 
2071  if (!$notab || $notab == -1 || $notab == -2 || $notab == -3) {
2072  $out .= "\n".'<div id="dragDropAreaTabBar" class="tabBar'.($notab == -1 ? '' : ($notab == -2 ? ' tabBarNoTop' : (($notab == -3 ? ' noborderbottom' : '').' tabBarWithBottom'))).'">'."\n";
2073  }
2074  if (!empty($dragdropfile)) {
2075  $out .= dragAndDropFileUpload("dragDropAreaTabBar");
2076  }
2077  $parameters = array('tabname' => $active, 'out' => $out);
2078  $reshook = $hookmanager->executeHooks('printTabsHead', $parameters); // This hook usage is called just before output the head of tabs. Take also a look at "completeTabsHead"
2079  if ($reshook > 0) {
2080  $out = $hookmanager->resPrint;
2081  }
2082 
2083  return $out;
2084 }
2085 
2093 function dol_fiche_end($notab = 0)
2094 {
2095  print dol_get_fiche_end($notab);
2096 }
2097 
2104 function dol_get_fiche_end($notab = 0)
2105 {
2106  if (!$notab || $notab == -1) {
2107  return "\n</div>\n";
2108  } else {
2109  return '';
2110  }
2111 }
2112 
2132 function dol_banner_tab($object, $paramid, $morehtml = '', $shownav = 1, $fieldid = 'rowid', $fieldref = 'ref', $morehtmlref = '', $moreparam = '', $nodbprefix = 0, $morehtmlleft = '', $morehtmlstatus = '', $onlybanner = 0, $morehtmlright = '')
2133 {
2134  global $conf, $form, $user, $langs, $hookmanager, $action;
2135 
2136  $error = 0;
2137 
2138  $maxvisiblephotos = 1;
2139  $showimage = 1;
2140  $entity = (empty($object->entity) ? $conf->entity : $object->entity);
2141  $showbarcode = empty($conf->barcode->enabled) ? 0 : (empty($object->barcode) ? 0 : 1);
2142  if (!empty($conf->global->MAIN_USE_ADVANCED_PERMS) && empty($user->rights->barcode->lire_advance)) {
2143  $showbarcode = 0;
2144  }
2145  $modulepart = 'unknown';
2146 
2147  if ($object->element == 'societe' || $object->element == 'contact' || $object->element == 'product' || $object->element == 'ticket') {
2148  $modulepart = $object->element;
2149  } elseif ($object->element == 'member') {
2150  $modulepart = 'memberphoto';
2151  } elseif ($object->element == 'user') {
2152  $modulepart = 'userphoto';
2153  }
2154 
2155  if (class_exists("Imagick")) {
2156  if ($object->element == 'expensereport' || $object->element == 'propal' || $object->element == 'commande' || $object->element == 'facture' || $object->element == 'supplier_proposal') {
2157  $modulepart = $object->element;
2158  } elseif ($object->element == 'fichinter') {
2159  $modulepart = 'ficheinter';
2160  } elseif ($object->element == 'contrat') {
2161  $modulepart = 'contract';
2162  } elseif ($object->element == 'order_supplier') {
2163  $modulepart = 'supplier_order';
2164  } elseif ($object->element == 'invoice_supplier') {
2165  $modulepart = 'supplier_invoice';
2166  }
2167  }
2168 
2169  if ($object->element == 'product') {
2170  $width = 80;
2171  $cssclass = 'photowithmargin photoref';
2172  $showimage = $object->is_photo_available($conf->product->multidir_output[$entity]);
2173  $maxvisiblephotos = (isset($conf->global->PRODUCT_MAX_VISIBLE_PHOTO) ? $conf->global->PRODUCT_MAX_VISIBLE_PHOTO : 5);
2174  if ($conf->browser->layout == 'phone') {
2175  $maxvisiblephotos = 1;
2176  }
2177  if ($showimage) {
2178  $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>';
2179  } else {
2180  if (!empty($conf->global->PRODUCT_NODISPLAYIFNOPHOTO)) {
2181  $nophoto = '';
2182  $morehtmlleft .= '<div class="floatleft inline-block valignmiddle divphotoref"></div>';
2183  } else { // Show no photo link
2184  $nophoto = '/public/theme/common/nophoto.png';
2185  $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>';
2186  }
2187  }
2188  } elseif ($object->element == 'ticket') {
2189  $width = 80;
2190  $cssclass = 'photoref';
2191  $showimage = $object->is_photo_available($conf->ticket->multidir_output[$entity].'/'.$object->ref);
2192  $maxvisiblephotos = (isset($conf->global->TICKET_MAX_VISIBLE_PHOTO) ? $conf->global->TICKET_MAX_VISIBLE_PHOTO : 2);
2193  if ($conf->browser->layout == 'phone') {
2194  $maxvisiblephotos = 1;
2195  }
2196 
2197  if ($showimage) {
2198  $showphoto = $object->show_photos('ticket', $conf->ticket->multidir_output[$entity], 'small', $maxvisiblephotos, 0, 0, 0, $width, 0);
2199  if ($object->nbphoto > 0) {
2200  $morehtmlleft .= '<div class="floatleft inline-block valignmiddle divphotoref">'.$showphoto.'</div>';
2201  } else {
2202  $showimage = 0;
2203  }
2204  }
2205  if (!$showimage) {
2206  if (!empty($conf->global->TICKET_NODISPLAYIFNOPHOTO)) {
2207  $nophoto = '';
2208  $morehtmlleft .= '<div class="floatleft inline-block valignmiddle divphotoref"></div>';
2209  } else { // Show no photo link
2210  $nophoto = img_picto('No photo', 'object_ticket');
2211  $morehtmlleft .= '<!-- No photo to show -->';
2212  $morehtmlleft .= '<div class="floatleft inline-block valignmiddle divphotoref"><div class="photoref">';
2213  $morehtmlleft .= $nophoto;
2214  $morehtmlleft .= '</div></div>';
2215  }
2216  }
2217  } else {
2218  if ($showimage) {
2219  if ($modulepart != 'unknown') {
2220  $phototoshow = '';
2221  // Check if a preview file is available
2222  if (in_array($modulepart, array('propal', 'commande', 'facture', 'ficheinter', 'contract', 'supplier_order', 'supplier_proposal', 'supplier_invoice', 'expensereport')) && class_exists("Imagick")) {
2223  $objectref = dol_sanitizeFileName($object->ref);
2224  $dir_output = (empty($conf->$modulepart->multidir_output[$entity]) ? $conf->$modulepart->dir_output : $conf->$modulepart->multidir_output[$entity])."/";
2225  if (in_array($modulepart, array('invoice_supplier', 'supplier_invoice'))) {
2226  $subdir = get_exdir($object->id, 2, 0, 1, $object, $modulepart);
2227  $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
2228  } else {
2229  $subdir = get_exdir($object->id, 0, 0, 1, $object, $modulepart);
2230  }
2231  if (empty($subdir)) {
2232  $subdir = 'errorgettingsubdirofobject'; // Protection to avoid to return empty path
2233  }
2234 
2235  $filepath = $dir_output.$subdir."/";
2236 
2237  $filepdf = $filepath.$objectref.".pdf";
2238  $relativepath = $subdir.'/'.$objectref.'.pdf';
2239 
2240  // Define path to preview pdf file (preview precompiled "file.ext" are "file.ext_preview.png")
2241  $fileimage = $filepdf.'_preview.png';
2242  $relativepathimage = $relativepath.'_preview.png';
2243 
2244  $pdfexists = file_exists($filepdf);
2245 
2246  // If PDF file exists
2247  if ($pdfexists) {
2248  // Conversion du PDF en image png si fichier png non existant
2249  if (!file_exists($fileimage) || (filemtime($fileimage) < filemtime($filepdf))) {
2250  if (empty($conf->global->MAIN_DISABLE_PDF_THUMBS)) { // If you experience trouble with pdf thumb generation and imagick, you can disable here.
2251  include_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
2252  $ret = dol_convert_file($filepdf, 'png', $fileimage, '0'); // Convert first page of PDF into a file _preview.png
2253  if ($ret < 0) {
2254  $error++;
2255  }
2256  }
2257  }
2258  }
2259 
2260  if ($pdfexists && !$error) {
2261  $heightforphotref = 80;
2262  if (!empty($conf->dol_optimize_smallscreen)) {
2263  $heightforphotref = 60;
2264  }
2265  // If the preview file is found
2266  if (file_exists($fileimage)) {
2267  $phototoshow = '<div class="photoref">';
2268  $phototoshow .= '<img height="'.$heightforphotref.'" class="photo photowithborder" src="'.DOL_URL_ROOT.'/viewimage.php?modulepart=apercu'.$modulepart.'&amp;file='.urlencode($relativepathimage).'">';
2269  $phototoshow .= '</div>';
2270  }
2271  }
2272  } elseif (!$phototoshow) { // example if modulepart = 'societe' or 'photo'
2273  $phototoshow .= $form->showphoto($modulepart, $object, 0, 0, 0, 'photowithmargin photoref', 'small', 1, 0, $maxvisiblephotos);
2274  }
2275 
2276  if ($phototoshow) {
2277  $morehtmlleft .= '<div class="floatleft inline-block valignmiddle divphotoref">';
2278  $morehtmlleft .= $phototoshow;
2279  $morehtmlleft .= '</div>';
2280  }
2281  }
2282 
2283  if (empty($phototoshow)) { // Show No photo link (picto of object)
2284  if ($object->element == 'action') {
2285  $width = 80;
2286  $cssclass = 'photorefcenter';
2287  $nophoto = img_picto('No photo', 'title_agenda');
2288  } else {
2289  $width = 14;
2290  $cssclass = 'photorefcenter';
2291  $picto = $object->picto;
2292  $prefix = 'object_';
2293  if ($object->element == 'project' && !$object->public) {
2294  $picto = 'project'; // instead of projectpub
2295  }
2296  if (strpos($picto, 'fontawesome_') !== false) {
2297  $prefix = '';
2298  }
2299  $nophoto = img_picto('No photo', $prefix.$picto);
2300  }
2301  $morehtmlleft .= '<!-- No photo to show -->';
2302  $morehtmlleft .= '<div class="floatleft inline-block valignmiddle divphotoref"><div class="photoref">';
2303  $morehtmlleft .= $nophoto;
2304  $morehtmlleft .= '</div></div>';
2305  }
2306  }
2307  }
2308 
2309  // Show barcode
2310  if ($showbarcode) {
2311  $morehtmlleft .= '<div class="floatleft inline-block valignmiddle divphotoref">'.$form->showbarcode($object, 100, 'photoref valignmiddle').'</div>';
2312  }
2313 
2314  if ($object->element == 'societe') {
2315  if (!empty($conf->use_javascript_ajax) && $user->hasRight('societe', 'creer') && !empty($conf->global->MAIN_DIRECT_STATUS_UPDATE)) {
2316  $morehtmlstatus .= ajax_object_onoff($object, 'status', 'status', 'InActivity', 'ActivityCeased');
2317  } else {
2318  $morehtmlstatus .= $object->getLibStatut(6);
2319  }
2320  } elseif ($object->element == 'product') {
2321  //$morehtmlstatus.=$langs->trans("Status").' ('.$langs->trans("Sell").') ';
2322  if (!empty($conf->use_javascript_ajax) && $user->rights->produit->creer && !empty($conf->global->MAIN_DIRECT_STATUS_UPDATE)) {
2323  $morehtmlstatus .= ajax_object_onoff($object, 'status', 'tosell', 'ProductStatusOnSell', 'ProductStatusNotOnSell');
2324  } else {
2325  $morehtmlstatus .= '<span class="statusrefsell">'.$object->getLibStatut(6, 0).'</span>';
2326  }
2327  $morehtmlstatus .= ' &nbsp; ';
2328  //$morehtmlstatus.=$langs->trans("Status").' ('.$langs->trans("Buy").') ';
2329  if (!empty($conf->use_javascript_ajax) && $user->rights->produit->creer && !empty($conf->global->MAIN_DIRECT_STATUS_UPDATE)) {
2330  $morehtmlstatus .= ajax_object_onoff($object, 'status_buy', 'tobuy', 'ProductStatusOnBuy', 'ProductStatusNotOnBuy');
2331  } else {
2332  $morehtmlstatus .= '<span class="statusrefbuy">'.$object->getLibStatut(6, 1).'</span>';
2333  }
2334  } elseif (in_array($object->element, array('facture', 'invoice', 'invoice_supplier', 'chargesociales', 'loan', 'tva', 'salary'))) {
2335  $tmptxt = $object->getLibStatut(6, $object->totalpaid);
2336  if (empty($tmptxt) || $tmptxt == $object->getLibStatut(3)) {
2337  $tmptxt = $object->getLibStatut(5, $object->totalpaid);
2338  }
2339  $morehtmlstatus .= $tmptxt;
2340  } elseif ($object->element == 'contrat' || $object->element == 'contract') {
2341  if ($object->statut == 0) {
2342  $morehtmlstatus .= $object->getLibStatut(5);
2343  } else {
2344  $morehtmlstatus .= $object->getLibStatut(4);
2345  }
2346  } elseif ($object->element == 'facturerec') {
2347  if ($object->frequency == 0) {
2348  $morehtmlstatus .= $object->getLibStatut(2);
2349  } else {
2350  $morehtmlstatus .= $object->getLibStatut(5);
2351  }
2352  } elseif ($object->element == 'project_task') {
2353  $object->fk_statut = 1;
2354  if ($object->progress > 0) {
2355  $object->fk_statut = 2;
2356  }
2357  if ($object->progress >= 100) {
2358  $object->fk_statut = 3;
2359  }
2360  $tmptxt = $object->getLibStatut(5);
2361  $morehtmlstatus .= $tmptxt; // No status on task
2362  } elseif (method_exists($object, 'getLibStatut')) { // Generic case
2363  $tmptxt = $object->getLibStatut(6);
2364  if (empty($tmptxt) || $tmptxt == $object->getLibStatut(3)) {
2365  $tmptxt = $object->getLibStatut(5);
2366  }
2367  $morehtmlstatus .= $tmptxt;
2368  }
2369 
2370  // Add if object was dispatched "into accountancy"
2371  if (isModEnabled('accounting') && in_array($object->element, array('bank', 'paiementcharge', 'facture', 'invoice', 'invoice_supplier', 'expensereport', 'payment_various'))) {
2372  // Note: For 'chargesociales', 'salaries'... this is the payments that are dispatched (so element = 'bank')
2373  if (method_exists($object, 'getVentilExportCompta')) {
2374  $accounted = $object->getVentilExportCompta();
2375  $langs->load("accountancy");
2376  $morehtmlstatus .= '</div><div class="statusref statusrefbis"><span class="opacitymedium">'.($accounted > 0 ? $langs->trans("Accounted") : $langs->trans("NotYetAccounted")).'</span>';
2377  }
2378  }
2379 
2380  // Add alias for thirdparty
2381  if (!empty($object->name_alias)) {
2382  $morehtmlref .= '<div class="refidno opacitymedium">'.dol_escape_htmltag($object->name_alias).'</div>';
2383  }
2384 
2385  // Add label
2386  if (in_array($object->element, array('product', 'bank_account', 'project_task'))) {
2387  if (!empty($object->label)) {
2388  $morehtmlref .= '<div class="refidno opacitymedium">'.$object->label.'</div>';
2389  }
2390  }
2391 
2392  // Show address and email
2393  if (method_exists($object, 'getBannerAddress') && !in_array($object->element, array('product', 'bookmark', 'ecm_directories', 'ecm_files'))) {
2394  $moreaddress = $object->getBannerAddress('refaddress', $object);
2395  if ($moreaddress) {
2396  $morehtmlref .= '<div class="refidno refaddress">';
2397  $morehtmlref .= $moreaddress;
2398  $morehtmlref .= '</div>';
2399  }
2400  }
2401  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)) {
2402  $morehtmlref .= '<div style="clear: both;"></div>';
2403  $morehtmlref .= '<div class="refidno opacitymedium">';
2404  $morehtmlref .= $langs->trans("TechnicalID").': '.((int) $object->id);
2405  $morehtmlref .= '</div>';
2406  }
2407 
2408  $parameters=array('morehtmlref'=>$morehtmlref);
2409  $reshook = $hookmanager->executeHooks('formDolBanner', $parameters, $object, $action);
2410  if ($reshook < 0) {
2411  setEventMessages($hookmanager->error, $hookmanager->errors, 'errors');
2412  } elseif (empty($reshook)) {
2413  $morehtmlref .= $hookmanager->resPrint;
2414  } elseif ($reshook > 0) {
2415  $morehtmlref = $hookmanager->resPrint;
2416  }
2417 
2418 
2419  print '<div class="'.($onlybanner ? 'arearefnobottom ' : 'arearef ').'heightref valignmiddle centpercent">';
2420  print $form->showrefnav($object, $paramid, $morehtml, $shownav, $fieldid, $fieldref, $morehtmlref, $moreparam, $nodbprefix, $morehtmlleft, $morehtmlstatus, $morehtmlright);
2421  print '</div>';
2422  print '<div class="underrefbanner clearboth"></div>';
2423 }
2424 
2434 function fieldLabel($langkey, $fieldkey, $fieldrequired = 0)
2435 {
2436  global $langs;
2437  $ret = '';
2438  if ($fieldrequired) {
2439  $ret .= '<span class="fieldrequired">';
2440  }
2441  $ret .= '<label for="'.$fieldkey.'">';
2442  $ret .= $langs->trans($langkey);
2443  $ret .= '</label>';
2444  if ($fieldrequired) {
2445  $ret .= '</span>';
2446  }
2447  return $ret;
2448 }
2449 
2457 function dol_bc($var, $moreclass = '')
2458 {
2459  global $bc;
2460  $ret = ' '.$bc[$var];
2461  if ($moreclass) {
2462  $ret = preg_replace('/class=\"/', 'class="'.$moreclass.' ', $ret);
2463  }
2464  return $ret;
2465 }
2466 
2480 function dol_format_address($object, $withcountry = 0, $sep = "\n", $outputlangs = '', $mode = 0, $extralangcode = '')
2481 {
2482  global $conf, $langs, $hookmanager;
2483 
2484  $ret = '';
2485  $countriesusingstate = array('AU', 'CA', 'US', 'IN', 'GB', 'ES', 'UK', 'TR', 'CN'); // See also MAIN_FORCE_STATE_INTO_ADDRESS
2486 
2487  // See format of addresses on https://en.wikipedia.org/wiki/Address
2488  // Address
2489  if (empty($mode)) {
2490  $ret .= ($extralangcode ? $object->array_languages['address'][$extralangcode] : (empty($object->address) ? '' : $object->address));
2491  }
2492  // Zip/Town/State
2493  if (isset($object->country_code) && in_array($object->country_code, array('AU', 'CA', 'US', 'CN')) || !empty($conf->global->MAIN_FORCE_STATE_INTO_ADDRESS)) {
2494  // US: title firstname name \n address lines \n town, state, zip \n country
2495  $town = ($extralangcode ? $object->array_languages['town'][$extralangcode] : (empty($object->town) ? '' : $object->town));
2496  $ret .= (($ret && $town) ? $sep : '').$town;
2497 
2498  if (!empty($object->state)) {
2499  $ret .= ($ret ? ($town ? ", " : $sep) : '').$object->state;
2500  }
2501  if (!empty($object->zip)) {
2502  $ret .= ($ret ? (($town || $object->state) ? ", " : $sep) : '').$object->zip;
2503  }
2504  } elseif (isset($object->country_code) && in_array($object->country_code, array('GB', 'UK'))) {
2505  // UK: title firstname name \n address lines \n town state \n zip \n country
2506  $town = ($extralangcode ? $object->array_languages['town'][$extralangcode] : (empty($object->town) ? '' : $object->town));
2507  $ret .= ($ret ? $sep : '').$town;
2508  if (!empty($object->state)) {
2509  $ret .= ($ret ? ", " : '').$object->state;
2510  }
2511  if (!empty($object->zip)) {
2512  $ret .= ($ret ? $sep : '').$object->zip;
2513  }
2514  } elseif (isset($object->country_code) && in_array($object->country_code, array('ES', 'TR'))) {
2515  // ES: title firstname name \n address lines \n zip town \n state \n country
2516  $ret .= ($ret ? $sep : '').$object->zip;
2517  $town = ($extralangcode ? $object->array_languages['town'][$extralangcode] : (empty($object->town) ? '' : $object->town));
2518  $ret .= ($town ? (($object->zip ? ' ' : '').$town) : '');
2519  if (!empty($object->state)) {
2520  $ret .= "\n".$object->state;
2521  }
2522  } elseif (isset($object->country_code) && in_array($object->country_code, array('JP'))) {
2523  // JP: In romaji, title firstname name\n address lines \n [state,] town zip \n country
2524  // See https://www.sljfaq.org/afaq/addresses.html
2525  $town = ($extralangcode ? $object->array_languages['town'][$extralangcode] : (empty($object->town) ? '' : $object->town));
2526  $ret .= ($ret ? $sep : '').($object->state ? $object->state.', ' : '').$town.($object->zip ? ' ' : '').$object->zip;
2527  } elseif (isset($object->country_code) && in_array($object->country_code, array('IT'))) {
2528  // IT: title firstname name\n address lines \n zip town state_code \n country
2529  $ret .= ($ret ? $sep : '').$object->zip;
2530  $town = ($extralangcode ? $object->array_languages['town'][$extralangcode] : (empty($object->town) ? '' : $object->town));
2531  $ret .= ($town ? (($object->zip ? ' ' : '').$town) : '');
2532  $ret .= (empty($object->state_code) ? '' : (' '.$object->state_code));
2533  } else {
2534  // Other: title firstname name \n address lines \n zip town[, state] \n country
2535  $town = ($extralangcode ? $object->array_languages['town'][$extralangcode] : (empty($object->town) ? '' : $object->town));
2536  $ret .= !empty($object->zip) ? (($ret ? $sep : '').$object->zip) : '';
2537  $ret .= ($town ? (($object->zip ? ' ' : ($ret ? $sep : '')).$town) : '');
2538  if (!empty($object->state) && in_array($object->country_code, $countriesusingstate)) {
2539  $ret .= ($ret ? ", " : '').$object->state;
2540  }
2541  }
2542  if (!is_object($outputlangs)) {
2543  $outputlangs = $langs;
2544  }
2545  if ($withcountry) {
2546  $langs->load("dict");
2547  $ret .= (empty($object->country_code) ? '' : ($ret ? $sep : '').$outputlangs->convToOutputCharset($outputlangs->transnoentitiesnoconv("Country".$object->country_code)));
2548  }
2549  if ($hookmanager) {
2550  $parameters = array('withcountry' => $withcountry, 'sep' => $sep, 'outputlangs' => $outputlangs,'mode' => $mode, 'extralangcode' => $extralangcode);
2551  $reshook = $hookmanager->executeHooks('formatAddress', $parameters, $object);
2552  if ($reshook > 0) {
2553  $ret = '';
2554  }
2555  $ret .= $hookmanager->resPrint;
2556  }
2557 
2558  return $ret;
2559 }
2560 
2561 
2562 
2571 function dol_strftime($fmt, $ts = false, $is_gmt = false)
2572 {
2573  if ((abs($ts) <= 0x7FFFFFFF)) { // check if number in 32-bit signed range
2574  return ($is_gmt) ? @gmstrftime($fmt, $ts) : @strftime($fmt, $ts);
2575  } else {
2576  return 'Error date into a not supported range';
2577  }
2578 }
2579 
2601 function dol_print_date($time, $format = '', $tzoutput = 'auto', $outputlangs = '', $encodetooutput = false)
2602 {
2603  global $conf, $langs;
2604 
2605  // If date undefined or "", we return ""
2606  if (dol_strlen($time) == 0) {
2607  return ''; // $time=0 allowed (it means 01/01/1970 00:00:00)
2608  }
2609 
2610  if ($tzoutput === 'auto') {
2611  $tzoutput = (empty($conf) ? 'tzserver' : (isset($conf->tzuserinputkey) ? $conf->tzuserinputkey : 'tzserver'));
2612  }
2613 
2614  // Clean parameters
2615  $to_gmt = false;
2616  $offsettz = $offsetdst = 0;
2617  if ($tzoutput) {
2618  $to_gmt = true; // For backward compatibility
2619  if (is_string($tzoutput)) {
2620  if ($tzoutput == 'tzserver') {
2621  $to_gmt = false;
2622  $offsettzstring = @date_default_timezone_get(); // Example 'Europe/Berlin' or 'Indian/Reunion'
2623  $offsettz = 0; // Timezone offset with server timezone (because to_gmt is false), so 0
2624  $offsetdst = 0; // Dst offset with server timezone (because to_gmt is false), so 0
2625  } elseif ($tzoutput == 'tzuser' || $tzoutput == 'tzuserrel') {
2626  $to_gmt = true;
2627  $offsettzstring = (empty($_SESSION['dol_tz_string']) ? 'UTC' : $_SESSION['dol_tz_string']); // Example 'Europe/Berlin' or 'Indian/Reunion'
2628 
2629  if (class_exists('DateTimeZone')) {
2630  $user_date_tz = new DateTimeZone($offsettzstring);
2631  $user_dt = new DateTime();
2632  $user_dt->setTimezone($user_date_tz);
2633  $user_dt->setTimestamp($tzoutput == 'tzuser' ? dol_now() : (int) $time);
2634  $offsettz = $user_dt->getOffset(); // should include dst ?
2635  } else { // with old method (The 'tzuser' was processed like the 'tzuserrel')
2636  $offsettz = (empty($_SESSION['dol_tz']) ? 0 : $_SESSION['dol_tz']) * 60 * 60; // Will not be used anymore
2637  $offsetdst = (empty($_SESSION['dol_dst']) ? 0 : $_SESSION['dol_dst']) * 60 * 60; // Will not be used anymore
2638  }
2639  }
2640  }
2641  }
2642  if (!is_object($outputlangs)) {
2643  $outputlangs = $langs;
2644  }
2645  if (!$format) {
2646  $format = 'daytextshort';
2647  }
2648 
2649  // Do we have to reduce the length of date (year on 2 chars) to save space.
2650  // Note: dayinputnoreduce is same than day but no reduction of year length will be done
2651  $reduceformat = (!empty($conf->dol_optimize_smallscreen) && in_array($format, array('day', 'dayhour'))) ? 1 : 0; // Test on original $format param.
2652  $format = preg_replace('/inputnoreduce/', '', $format); // so format 'dayinputnoreduce' is processed like day
2653  $formatwithoutreduce = preg_replace('/reduceformat/', '', $format);
2654  if ($formatwithoutreduce != $format) {
2655  $format = $formatwithoutreduce;
2656  $reduceformat = 1;
2657  } // so format 'dayreduceformat' is processed like day
2658 
2659  // Change predefined format into computer format. If found translation in lang file we use it, otherwise we use default.
2660  // TODO Add format daysmallyear and dayhoursmallyear
2661  if ($format == 'day') {
2662  $format = ($outputlangs->trans("FormatDateShort") != "FormatDateShort" ? $outputlangs->trans("FormatDateShort") : $conf->format_date_short);
2663  } elseif ($format == 'hour') {
2664  $format = ($outputlangs->trans("FormatHourShort") != "FormatHourShort" ? $outputlangs->trans("FormatHourShort") : $conf->format_hour_short);
2665  } elseif ($format == 'hourduration') {
2666  $format = ($outputlangs->trans("FormatHourShortDuration") != "FormatHourShortDuration" ? $outputlangs->trans("FormatHourShortDuration") : $conf->format_hour_short_duration);
2667  } elseif ($format == 'daytext') {
2668  $format = ($outputlangs->trans("FormatDateText") != "FormatDateText" ? $outputlangs->trans("FormatDateText") : $conf->format_date_text);
2669  } elseif ($format == 'daytextshort') {
2670  $format = ($outputlangs->trans("FormatDateTextShort") != "FormatDateTextShort" ? $outputlangs->trans("FormatDateTextShort") : $conf->format_date_text_short);
2671  } elseif ($format == 'dayhour') {
2672  $format = ($outputlangs->trans("FormatDateHourShort") != "FormatDateHourShort" ? $outputlangs->trans("FormatDateHourShort") : $conf->format_date_hour_short);
2673  } elseif ($format == 'dayhoursec') {
2674  $format = ($outputlangs->trans("FormatDateHourSecShort") != "FormatDateHourSecShort" ? $outputlangs->trans("FormatDateHourSecShort") : $conf->format_date_hour_sec_short);
2675  } elseif ($format == 'dayhourtext') {
2676  $format = ($outputlangs->trans("FormatDateHourText") != "FormatDateHourText" ? $outputlangs->trans("FormatDateHourText") : $conf->format_date_hour_text);
2677  } elseif ($format == 'dayhourtextshort') {
2678  $format = ($outputlangs->trans("FormatDateHourTextShort") != "FormatDateHourTextShort" ? $outputlangs->trans("FormatDateHourTextShort") : $conf->format_date_hour_text_short);
2679  } elseif ($format == 'dayhourlog') {
2680  // Format not sensitive to language
2681  $format = '%Y%m%d%H%M%S';
2682  } elseif ($format == 'dayhourlogsmall') {
2683  // Format not sensitive to language
2684  $format = '%y%m%d%H%M';
2685  } elseif ($format == 'dayhourldap') {
2686  $format = '%Y%m%d%H%M%SZ';
2687  } elseif ($format == 'dayhourxcard') {
2688  $format = '%Y%m%dT%H%M%SZ';
2689  } elseif ($format == 'dayxcard') {
2690  $format = '%Y%m%d';
2691  } elseif ($format == 'dayrfc') {
2692  $format = '%Y-%m-%d'; // DATE_RFC3339
2693  } elseif ($format == 'dayhourrfc') {
2694  $format = '%Y-%m-%dT%H:%M:%SZ'; // DATETIME RFC3339
2695  } elseif ($format == 'standard') {
2696  $format = '%Y-%m-%d %H:%M:%S';
2697  }
2698 
2699  if ($reduceformat) {
2700  $format = str_replace('%Y', '%y', $format);
2701  $format = str_replace('yyyy', 'yy', $format);
2702  }
2703 
2704  // Clean format
2705  if (preg_match('/%b/i', $format)) { // There is some text to translate
2706  // We inhibate translation to text made by strftime functions. We will use trans instead later.
2707  $format = str_replace('%b', '__b__', $format);
2708  $format = str_replace('%B', '__B__', $format);
2709  }
2710  if (preg_match('/%a/i', $format)) { // There is some text to translate
2711  // We inhibate translation to text made by strftime functions. We will use trans instead later.
2712  $format = str_replace('%a', '__a__', $format);
2713  $format = str_replace('%A', '__A__', $format);
2714  }
2715 
2716  // Analyze date
2717  $reg = array();
2718  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
2719  dol_print_error('', "Functions.lib::dol_print_date function called with a bad value from page ".$_SERVER["PHP_SELF"]);
2720  return '';
2721  } 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
2722  // This part of code should not be used anymore.
2723  dol_syslog("Functions.lib::dol_print_date function called with a bad value from page ".$_SERVER["PHP_SELF"], LOG_WARNING);
2724  //if (function_exists('debug_print_backtrace')) debug_print_backtrace();
2725  // Date has format 'YYYY-MM-DD' or 'YYYY-MM-DD HH:MM:SS'
2726  $syear = (!empty($reg[1]) ? $reg[1] : '');
2727  $smonth = (!empty($reg[2]) ? $reg[2] : '');
2728  $sday = (!empty($reg[3]) ? $reg[3] : '');
2729  $shour = (!empty($reg[4]) ? $reg[4] : '');
2730  $smin = (!empty($reg[5]) ? $reg[5] : '');
2731  $ssec = (!empty($reg[6]) ? $reg[6] : '');
2732 
2733  $time = dol_mktime($shour, $smin, $ssec, $smonth, $sday, $syear, true);
2734 
2735  if ($to_gmt) {
2736  $tzo = new DateTimeZone('UTC'); // when to_gmt is true, base for offsettz and offsetdst (so timetouse) is UTC
2737  } else {
2738  $tzo = new DateTimeZone(date_default_timezone_get()); // when to_gmt is false, base for offsettz and offsetdst (so timetouse) is PHP server
2739  }
2740  $dtts = new DateTime();
2741  $dtts->setTimestamp($time);
2742  $dtts->setTimezone($tzo);
2743  $newformat = str_replace(
2744  array('%Y', '%y', '%m', '%d', '%H', '%I', '%M', '%S', '%p', 'T', 'Z', '__a__', '__A__', '__b__', '__B__'),
2745  array('Y', 'y', 'm', 'd', 'H', 'h', 'i', 's', 'A', '__£__', '__$__', '__{__', '__}__', '__[__', '__]__'),
2746  $format);
2747  $ret = $dtts->format($newformat);
2748  $ret = str_replace(
2749  array('__£__', '__$__', '__{__', '__}__', '__[__', '__]__'),
2750  array('T', 'Z', '__a__', '__A__', '__b__', '__B__'),
2751  $ret
2752  );
2753  } else {
2754  // Date is a timestamps
2755  if ($time < 100000000000) { // Protection against bad date values
2756  $timetouse = $time + $offsettz + $offsetdst; // TODO We could be able to disable use of offsettz and offsetdst to use only offsettzstring.
2757 
2758  if ($to_gmt) {
2759  $tzo = new DateTimeZone('UTC'); // when to_gmt is true, base for offsettz and offsetdst (so timetouse) is UTC
2760  } else {
2761  $tzo = new DateTimeZone(date_default_timezone_get()); // when to_gmt is false, base for offsettz and offsetdst (so timetouse) is PHP server
2762  }
2763  $dtts = new DateTime();
2764  $dtts->setTimestamp($timetouse);
2765  $dtts->setTimezone($tzo);
2766  $newformat = str_replace(
2767  array('%Y', '%y', '%m', '%d', '%H', '%I', '%M', '%S', '%p', '%w', 'T', 'Z', '__a__', '__A__', '__b__', '__B__'),
2768  array('Y', 'y', 'm', 'd', 'H', 'h', 'i', 's', 'A', 'w', '__£__', '__$__', '__{__', '__}__', '__[__', '__]__'),
2769  $format);
2770  $ret = $dtts->format($newformat);
2771  $ret = str_replace(
2772  array('__£__', '__$__', '__{__', '__}__', '__[__', '__]__'),
2773  array('T', 'Z', '__a__', '__A__', '__b__', '__B__'),
2774  $ret
2775  );
2776  //var_dump($ret);exit;
2777  } else {
2778  $ret = 'Bad value '.$time.' for date';
2779  }
2780  }
2781 
2782  if (preg_match('/__b__/i', $format)) {
2783  $timetouse = $time + $offsettz + $offsetdst; // TODO We could be able to disable use of offsettz and offsetdst to use only offsettzstring.
2784 
2785  if ($to_gmt) {
2786  $tzo = new DateTimeZone('UTC'); // when to_gmt is true, base for offsettz and offsetdst (so timetouse) is UTC
2787  } else {
2788  $tzo = new DateTimeZone(date_default_timezone_get()); // when to_gmt is false, base for offsettz and offsetdst (so timetouse) is PHP server
2789  }
2790  $dtts = new DateTime();
2791  $dtts->setTimestamp($timetouse);
2792  $dtts->setTimezone($tzo);
2793  $month = $dtts->format("m");
2794  $month = sprintf("%02d", $month); // $month may be return with format '06' on some installation and '6' on other, so we force it to '06'.
2795  if ($encodetooutput) {
2796  $monthtext = $outputlangs->transnoentities('Month'.$month);
2797  $monthtextshort = $outputlangs->transnoentities('MonthShort'.$month);
2798  } else {
2799  $monthtext = $outputlangs->transnoentitiesnoconv('Month'.$month);
2800  $monthtextshort = $outputlangs->transnoentitiesnoconv('MonthShort'.$month);
2801  }
2802  //print 'monthtext='.$monthtext.' monthtextshort='.$monthtextshort;
2803  $ret = str_replace('__b__', $monthtextshort, $ret);
2804  $ret = str_replace('__B__', $monthtext, $ret);
2805  //print 'x'.$outputlangs->charset_output.'-'.$ret.'x';
2806  //return $ret;
2807  }
2808  if (preg_match('/__a__/i', $format)) {
2809  //print "time=$time offsettz=$offsettz offsetdst=$offsetdst offsettzstring=$offsettzstring";
2810  $timetouse = $time + $offsettz + $offsetdst; // TODO Replace this with function Date PHP. We also should not use anymore offsettz and offsetdst but only offsettzstring.
2811 
2812  if ($to_gmt) {
2813  $tzo = new DateTimeZone('UTC');
2814  } else {
2815  $tzo = new DateTimeZone(date_default_timezone_get());
2816  }
2817  $dtts = new DateTime();
2818  $dtts->setTimestamp($timetouse);
2819  $dtts->setTimezone($tzo);
2820  $w = $dtts->format("w");
2821  $dayweek = $outputlangs->transnoentitiesnoconv('Day'.$w);
2822 
2823  $ret = str_replace('__A__', $dayweek, $ret);
2824  $ret = str_replace('__a__', dol_substr($dayweek, 0, 3), $ret);
2825  }
2826 
2827  return $ret;
2828 }
2829 
2830 
2851 function dol_getdate($timestamp, $fast = false, $forcetimezone = '')
2852 {
2853  $datetimeobj = new DateTime();
2854  $datetimeobj->setTimestamp($timestamp); // Use local PHP server timezone
2855  if ($forcetimezone) {
2856  $datetimeobj->setTimezone(new DateTimeZone($forcetimezone == 'gmt' ? 'UTC' : $forcetimezone)); // (add timezone relative to the date entered)
2857  }
2858  $arrayinfo = array(
2859  'year'=>((int) date_format($datetimeobj, 'Y')),
2860  'mon'=>((int) date_format($datetimeobj, 'm')),
2861  'mday'=>((int) date_format($datetimeobj, 'd')),
2862  'wday'=>((int) date_format($datetimeobj, 'w')),
2863  'yday'=>((int) date_format($datetimeobj, 'z')),
2864  'hours'=>((int) date_format($datetimeobj, 'H')),
2865  'minutes'=>((int) date_format($datetimeobj, 'i')),
2866  'seconds'=>((int) date_format($datetimeobj, 's')),
2867  '0'=>$timestamp
2868  );
2869 
2870  return $arrayinfo;
2871 }
2872 
2894 function dol_mktime($hour, $minute, $second, $month, $day, $year, $gm = 'auto', $check = 1)
2895 {
2896  global $conf;
2897  //print "- ".$hour.",".$minute.",".$second.",".$month.",".$day.",".$year.",".$_SERVER["WINDIR"]." -";
2898 
2899  if ($gm === 'auto') {
2900  $gm = (empty($conf) ? 'tzserver' : $conf->tzuserinputkey);
2901  }
2902  //print 'gm:'.$gm.' gm === auto:'.($gm === 'auto').'<br>';exit;
2903 
2904  // Clean parameters
2905  if ($hour == -1 || empty($hour)) {
2906  $hour = 0;
2907  }
2908  if ($minute == -1 || empty($minute)) {
2909  $minute = 0;
2910  }
2911  if ($second == -1 || empty($second)) {
2912  $second = 0;
2913  }
2914 
2915  // Check parameters
2916  if ($check) {
2917  if (!$month || !$day) {
2918  return '';
2919  }
2920  if ($day > 31) {
2921  return '';
2922  }
2923  if ($month > 12) {
2924  return '';
2925  }
2926  if ($hour < 0 || $hour > 24) {
2927  return '';
2928  }
2929  if ($minute < 0 || $minute > 60) {
2930  return '';
2931  }
2932  if ($second < 0 || $second > 60) {
2933  return '';
2934  }
2935  }
2936 
2937  if (empty($gm) || ($gm === 'server' || $gm === 'tzserver')) {
2938  $default_timezone = @date_default_timezone_get(); // Example 'Europe/Berlin'
2939  $localtz = new DateTimeZone($default_timezone);
2940  } elseif ($gm === 'user' || $gm === 'tzuser' || $gm === 'tzuserrel') {
2941  // We use dol_tz_string first because it is more reliable.
2942  $default_timezone = (empty($_SESSION["dol_tz_string"]) ? @date_default_timezone_get() : $_SESSION["dol_tz_string"]); // Example 'Europe/Berlin'
2943  try {
2944  $localtz = new DateTimeZone($default_timezone);
2945  } catch (Exception $e) {
2946  dol_syslog("Warning dol_tz_string contains an invalid value ".$_SESSION["dol_tz_string"], LOG_WARNING);
2947  $default_timezone = @date_default_timezone_get();
2948  }
2949  } elseif (strrpos($gm, "tz,") !== false) {
2950  $timezone = str_replace("tz,", "", $gm); // Example 'tz,Europe/Berlin'
2951  try {
2952  $localtz = new DateTimeZone($timezone);
2953  } catch (Exception $e) {
2954  dol_syslog("Warning passed timezone contains an invalid value ".$timezone, LOG_WARNING);
2955  }
2956  }
2957 
2958  if (empty($localtz)) {
2959  $localtz = new DateTimeZone('UTC');
2960  }
2961  //var_dump($localtz);
2962  //var_dump($year.'-'.$month.'-'.$day.'-'.$hour.'-'.$minute);
2963  $dt = new DateTime('now', $localtz);
2964  $dt->setDate((int) $year, (int) $month, (int) $day);
2965  $dt->setTime((int) $hour, (int) $minute, (int) $second);
2966  $date = $dt->getTimestamp(); // should include daylight saving time
2967  //var_dump($date);
2968  return $date;
2969 }
2970 
2971 
2982 function dol_now($mode = 'auto')
2983 {
2984  $ret = 0;
2985 
2986  if ($mode === 'auto') {
2987  $mode = 'gmt';
2988  }
2989 
2990  if ($mode == 'gmt') {
2991  $ret = time(); // Time for now at greenwich.
2992  } elseif ($mode == 'tzserver') { // Time for now with PHP server timezone added
2993  require_once DOL_DOCUMENT_ROOT.'/core/lib/date.lib.php';
2994  $tzsecond = getServerTimeZoneInt('now'); // Contains tz+dayling saving time
2995  $ret = (int) (dol_now('gmt') + ($tzsecond * 3600));
2996  //} elseif ($mode == 'tzref') {// Time for now with parent company timezone is added
2997  // require_once DOL_DOCUMENT_ROOT.'/core/lib/date.lib.php';
2998  // $tzsecond=getParentCompanyTimeZoneInt(); // Contains tz+dayling saving time
2999  // $ret=dol_now('gmt')+($tzsecond*3600);
3000  //}
3001  } elseif ($mode == 'tzuser' || $mode == 'tzuserrel') {
3002  // Time for now with user timezone added
3003  //print 'time: '.time();
3004  $offsettz = (empty($_SESSION['dol_tz']) ? 0 : $_SESSION['dol_tz']) * 60 * 60;
3005  $offsetdst = (empty($_SESSION['dol_dst']) ? 0 : $_SESSION['dol_dst']) * 60 * 60;
3006  $ret = (int) (dol_now('gmt') + ($offsettz + $offsetdst));
3007  }
3008 
3009  return $ret;
3010 }
3011 
3012 
3021 function dol_print_size($size, $shortvalue = 0, $shortunit = 0)
3022 {
3023  global $conf, $langs;
3024  $level = 1024;
3025 
3026  if (!empty($conf->dol_optimize_smallscreen)) {
3027  $shortunit = 1;
3028  }
3029 
3030  // Set value text
3031  if (empty($shortvalue) || $size < ($level * 10)) {
3032  $ret = $size;
3033  $textunitshort = $langs->trans("b");
3034  $textunitlong = $langs->trans("Bytes");
3035  } else {
3036  $ret = round($size / $level, 0);
3037  $textunitshort = $langs->trans("Kb");
3038  $textunitlong = $langs->trans("KiloBytes");
3039  }
3040  // Use long or short text unit
3041  if (empty($shortunit)) {
3042  $ret .= ' '.$textunitlong;
3043  } else {
3044  $ret .= ' '.$textunitshort;
3045  }
3046 
3047  return $ret;
3048 }
3049 
3060 function dol_print_url($url, $target = '_blank', $max = 32, $withpicto = 0, $morecss = 'float')
3061 {
3062  global $langs;
3063 
3064  if (empty($url)) {
3065  return '';
3066  }
3067 
3068  $link = '<a href="';
3069  if (!preg_match('/^http/i', $url)) {
3070  $link .= 'http://';
3071  }
3072  $link .= $url;
3073  $link .= '"';
3074  if ($target) {
3075  $link .= ' target="'.$target.'"';
3076  }
3077  $link .= '>';
3078  if (!preg_match('/^http/i', $url)) {
3079  $link .= 'http://';
3080  }
3081  $link .= dol_trunc($url, $max);
3082  $link .= '</a>';
3083 
3084  if ($morecss == 'float') {
3085  return '<div class="nospan'.($morecss ? ' '.$morecss : '').'" style="margin-right: 10px">'.($withpicto ?img_picto($langs->trans("Url"), 'globe').' ' : '').$link.'</div>';
3086  } else {
3087  return '<span class="nospan'.($morecss ? ' '.$morecss : '').'" style="margin-right: 10px">'.($withpicto ?img_picto($langs->trans("Url"), 'globe').' ' : '').$link.'</span>';
3088  }
3089 }
3090 
3103 function dol_print_email($email, $cid = 0, $socid = 0, $addlink = 0, $max = 64, $showinvalid = 1, $withpicto = 0)
3104 {
3105  global $conf, $user, $langs, $hookmanager;
3106 
3107  $newemail = dol_escape_htmltag($email);
3108 
3109  if (!empty($conf->global->MAIN_OPTIMIZEFORTEXTBROWSER) && $withpicto) {
3110  $withpicto = 0;
3111  }
3112 
3113  if (empty($email)) {
3114  return '&nbsp;';
3115  }
3116 
3117  if (!empty($addlink)) {
3118  $newemail = '<a style="text-overflow: ellipsis;" href="';
3119  if (!preg_match('/^mailto:/i', $email)) {
3120  $newemail .= 'mailto:';
3121  }
3122  $newemail .= $email;
3123  $newemail .= '">';
3124  $newemail .= dol_trunc($email, $max);
3125  $newemail .= '</a>';
3126  if ($showinvalid && !isValidEmail($email)) {
3127  $langs->load("errors");
3128  $newemail .= img_warning($langs->trans("ErrorBadEMail", $email));
3129  }
3130 
3131  if (($cid || $socid) && isModEnabled('agenda') && $user->hasRight("agenda", "myactions", "create")) {
3132  $type = 'AC_EMAIL';
3133  $link = '';
3134  if (!empty($conf->global->AGENDA_ADDACTIONFOREMAIL)) {
3135  $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>';
3136  }
3137  if ($link) {
3138  $newemail = '<div>'.$newemail.' '.$link.'</div>';
3139  }
3140  }
3141  } else {
3142  if ($showinvalid && !isValidEmail($email)) {
3143  $langs->load("errors");
3144  $newemail .= img_warning($langs->trans("ErrorBadEMail", $email));
3145  }
3146  }
3147 
3148  //$rep = '<div class="nospan" style="margin-right: 10px">';
3149  $rep = ($withpicto ? img_picto($langs->trans("EMail").' : '.$email, (is_numeric($withpicto) ? 'email' : $withpicto)).' ' : '').$newemail;
3150  //$rep .= '</div>';
3151  if ($hookmanager) {
3152  $parameters = array('cid' => $cid, 'socid' => $socid, 'addlink' => $addlink, 'picto' => $withpicto);
3153 
3154  $reshook = $hookmanager->executeHooks('printEmail', $parameters, $email);
3155  if ($reshook > 0) {
3156  $rep = '';
3157  }
3158  $rep .= $hookmanager->resPrint;
3159  }
3160 
3161  return $rep;
3162 }
3163 
3170 {
3171  global $conf, $db;
3172 
3173  $socialnetworks = array();
3174  // Enable caching of array
3175  require_once DOL_DOCUMENT_ROOT.'/core/lib/memory.lib.php';
3176  $cachekey = 'socialnetworks_' . $conf->entity;
3177  $dataretrieved = dol_getcache($cachekey);
3178  if (!is_null($dataretrieved)) {
3179  $socialnetworks = $dataretrieved;
3180  } else {
3181  $sql = "SELECT rowid, code, label, url, icon, active FROM ".MAIN_DB_PREFIX."c_socialnetworks";
3182  $sql .= " WHERE entity=".$conf->entity;
3183  $resql = $db->query($sql);
3184  if ($resql) {
3185  while ($obj = $db->fetch_object($resql)) {
3186  $socialnetworks[$obj->code] = array(
3187  'rowid' => $obj->rowid,
3188  'label' => $obj->label,
3189  'url' => $obj->url,
3190  'icon' => $obj->icon,
3191  'active' => $obj->active,
3192  );
3193  }
3194  }
3195  dol_setcache($cachekey, $socialnetworks); // If setting cache fails, this is not a problem, so we do not test result.
3196  }
3197 
3198  return $socialnetworks;
3199 }
3200 
3211 function dol_print_socialnetworks($value, $cid, $socid, $type, $dictsocialnetworks = array())
3212 {
3213  global $conf, $user, $langs;
3214 
3215  $htmllink = $value;
3216 
3217  if (empty($value)) {
3218  return '&nbsp;';
3219  }
3220 
3221  if (!empty($type)) {
3222  $htmllink = '<div class="divsocialnetwork inline-block valignmiddle">';
3223  // Use dictionary definition for picto $dictsocialnetworks[$type]['icon']
3224  $htmllink .= '<span class="fa pictofixedwidth '.($dictsocialnetworks[$type]['icon'] ? $dictsocialnetworks[$type]['icon'] : 'fa-link').'"></span>';
3225  if ($type == 'skype') {
3226  $htmllink .= dol_escape_htmltag($value);
3227  $htmllink .= '&nbsp; <a href="skype:';
3228  $htmllink .= dol_string_nospecial($value, '_', '', array('@'));
3229  $htmllink .= '?call" alt="'.$langs->trans("Call").'&nbsp;'.$value.'" title="'.dol_escape_htmltag($langs->trans("Call").' '.$value).'">';
3230  $htmllink .= '<img src="'.DOL_URL_ROOT.'/theme/common/skype_callbutton.png" border="0">';
3231  $htmllink .= '</a><a href="skype:';
3232  $htmllink .= dol_string_nospecial($value, '_', '', array('@'));
3233  $htmllink .= '?chat" alt="'.$langs->trans("Chat").'&nbsp;'.$value.'" title="'.dol_escape_htmltag($langs->trans("Chat").' '.$value).'">';
3234  $htmllink .= '<img class="paddingleft" src="'.DOL_URL_ROOT.'/theme/common/skype_chatbutton.png" border="0">';
3235  $htmllink .= '</a>';
3236  if (($cid || $socid) && isModEnabled('agenda') && $user->rights->agenda->myactions->create) {
3237  $addlink = 'AC_SKYPE';
3238  $link = '';
3239  if (!empty($conf->global->AGENDA_ADDACTIONFORSKYPE)) {
3240  $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>';
3241  }
3242  $htmllink .= ($link ? ' '.$link : '');
3243  }
3244  } else {
3245  if (!empty($dictsocialnetworks[$type]['url'])) {
3246  $tmpvirginurl = preg_replace('/\/?{socialid}/', '', $dictsocialnetworks[$type]['url']);
3247  if ($tmpvirginurl) {
3248  $value = preg_replace('/^www\.'.preg_quote($tmpvirginurl, '/').'\/?/', '', $value);
3249  $value = preg_replace('/^'.preg_quote($tmpvirginurl, '/').'\/?/', '', $value);
3250 
3251  $tmpvirginurl3 = preg_replace('/^https:\/\//i', 'https://www.', $tmpvirginurl);
3252  if ($tmpvirginurl3) {
3253  $value = preg_replace('/^www\.'.preg_quote($tmpvirginurl3, '/').'\/?/', '', $value);
3254  $value = preg_replace('/^'.preg_quote($tmpvirginurl3, '/').'\/?/', '', $value);
3255  }
3256 
3257  $tmpvirginurl2 = preg_replace('/^https?:\/\//i', '', $tmpvirginurl);
3258  if ($tmpvirginurl2) {
3259  $value = preg_replace('/^www\.'.preg_quote($tmpvirginurl2, '/').'\/?/', '', $value);
3260  $value = preg_replace('/^'.preg_quote($tmpvirginurl2, '/').'\/?/', '', $value);
3261  }
3262  }
3263  $link = str_replace('{socialid}', $value, $dictsocialnetworks[$type]['url']);
3264  if (preg_match('/^https?:\/\//i', $link)) {
3265  $htmllink .= '<a href="'.dol_sanitizeUrl($link, 0).'" target="_blank" rel="noopener noreferrer">'.dol_escape_htmltag($value).'</a>';
3266  } else {
3267  $htmllink .= '<a href="'.dol_sanitizeUrl($link, 1).'" target="_blank" rel="noopener noreferrer">'.dol_escape_htmltag($value).'</a>';
3268  }
3269  } else {
3270  $htmllink .= dol_escape_htmltag($value);
3271  }
3272  }
3273  $htmllink .= '</div>';
3274  } else {
3275  $langs->load("errors");
3276  $htmllink .= img_warning($langs->trans("ErrorBadSocialNetworkValue", $value));
3277  }
3278  return $htmllink;
3279 }
3280 
3291 function dol_print_profids($profID, $profIDtype, $countrycode = '', $addcpButton = 1, $separ = '&nbsp;')
3292 {
3293  global $mysoc;
3294 
3295  if (empty($profID) || empty($profIDtype)) {
3296  return '';
3297  }
3298  if (empty($countrycode)) $countrycode = $mysoc->country_code;
3299  $newProfID = $profID;
3300  $id = substr($profIDtype, -1);
3301  $ret = '';
3302  if (strtoupper($countrycode) == 'FR') {
3303  // France
3304  if ($id == 1 && dol_strlen($newProfID) == 9) $newProfID = substr($newProfID, 0, 3).$separ.substr($newProfID, 3, 3).$separ.substr($newProfID, 6, 3);
3305  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);
3306  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);
3307  }
3308  if (!empty($addcpButton)) $ret = showValueWithClipboardCPButton(dol_escape_htmltag($profID), ($addcpButton == 1 ? 1 : 0), $newProfID);
3309  else $ret = $newProfID;
3310  return $ret;
3311 }
3312 
3327 function dol_print_phone($phone, $countrycode = '', $cid = 0, $socid = 0, $addlink = '', $separ = "&nbsp;", $withpicto = '', $titlealt = '', $adddivfloat = 0)
3328 {
3329  global $conf, $user, $langs, $mysoc, $hookmanager;
3330 
3331  // Clean phone parameter
3332  $phone = is_null($phone) ? '' : preg_replace("/[\s.-]/", "", trim($phone));
3333  if (empty($phone)) {
3334  return '';
3335  }
3336  if (!empty($conf->global->MAIN_PHONE_SEPAR)) {
3337  $separ = $conf->global->MAIN_PHONE_SEPAR;
3338  }
3339  if (empty($countrycode) && is_object($mysoc)) {
3340  $countrycode = $mysoc->country_code;
3341  }
3342 
3343  // Short format for small screens
3344  if ($conf->dol_optimize_smallscreen) {
3345  $separ = '';
3346  }
3347 
3348  $newphone = $phone;
3349  if (strtoupper($countrycode) == "FR") {
3350  // France
3351  if (dol_strlen($phone) == 10) {
3352  $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);
3353  } elseif (dol_strlen($phone) == 7) {
3354  $newphone = substr($newphone, 0, 3).$separ.substr($newphone, 3, 2).$separ.substr($newphone, 5, 2);
3355  } elseif (dol_strlen($phone) == 9) {
3356  $newphone = substr($newphone, 0, 2).$separ.substr($newphone, 2, 3).$separ.substr($newphone, 5, 2).$separ.substr($newphone, 7, 2);
3357  } elseif (dol_strlen($phone) == 11) {
3358  $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);
3359  } elseif (dol_strlen($phone) == 12) {
3360  $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);
3361  } elseif (dol_strlen($phone) == 13) {
3362  $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);
3363  }
3364  } elseif (strtoupper($countrycode) == "CA") {
3365  if (dol_strlen($phone) == 10) {
3366  $newphone = ($separ != '' ? '(' : '').substr($newphone, 0, 3).($separ != '' ? ')' : '').$separ.substr($newphone, 3, 3).($separ != '' ? '-' : '').substr($newphone, 6, 4);
3367  }
3368  } elseif (strtoupper($countrycode) == "PT") {//Portugal
3369  if (dol_strlen($phone) == 13) {//ex: +351_ABC_DEF_GHI
3370  $newphone = substr($newphone, 0, 4).$separ.substr($newphone, 4, 3).$separ.substr($newphone, 7, 3).$separ.substr($newphone, 10, 3);
3371  }
3372  } elseif (strtoupper($countrycode) == "SR") {//Suriname
3373  if (dol_strlen($phone) == 10) {//ex: +597_ABC_DEF
3374  $newphone = substr($newphone, 0, 4).$separ.substr($newphone, 4, 3).$separ.substr($newphone, 7, 3);
3375  } elseif (dol_strlen($phone) == 11) {//ex: +597_ABC_DEFG
3376  $newphone = substr($newphone, 0, 4).$separ.substr($newphone, 4, 3).$separ.substr($newphone, 7, 4);
3377  }
3378  } elseif (strtoupper($countrycode) == "DE") {//Allemagne
3379  if (dol_strlen($phone) == 14) {//ex: +49_ABCD_EFGH_IJK
3380  $newphone = substr($newphone, 0, 3).$separ.substr($newphone, 3, 4).$separ.substr($newphone, 7, 4).$separ.substr($newphone, 11, 3);
3381  } elseif (dol_strlen($phone) == 13) {//ex: +49_ABC_DEFG_HIJ
3382  $newphone = substr($newphone, 0, 3).$separ.substr($newphone, 3, 3).$separ.substr($newphone, 6, 4).$separ.substr($newphone, 10, 3);
3383  }
3384  } elseif (strtoupper($countrycode) == "ES") {//Espagne
3385  if (dol_strlen($phone) == 12) {//ex: +34_ABC_DEF_GHI
3386  $newphone = substr($newphone, 0, 3).$separ.substr($newphone, 3, 3).$separ.substr($newphone, 6, 3).$separ.substr($newphone, 9, 3);
3387  }
3388  } elseif (strtoupper($countrycode) == "BF") {// Burkina Faso
3389  if (dol_strlen($phone) == 12) {//ex : +22 A BC_DE_FG_HI
3390  $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);
3391  }
3392  } elseif (strtoupper($countrycode) == "RO") {// Roumanie
3393  if (dol_strlen($phone) == 12) {//ex : +40 AB_CDE_FG_HI
3394  $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);
3395  }
3396  } elseif (strtoupper($countrycode) == "TR") {//Turquie
3397  if (dol_strlen($phone) == 13) {//ex : +90 ABC_DEF_GHIJ
3398  $newphone = substr($newphone, 0, 3).$separ.substr($newphone, 3, 3).$separ.substr($newphone, 6, 3).$separ.substr($newphone, 9, 4);
3399  }
3400  } elseif (strtoupper($countrycode) == "US") {//Etat-Unis
3401  if (dol_strlen($phone) == 12) {//ex: +1 ABC_DEF_GHIJ
3402  $newphone = substr($newphone, 0, 2).$separ.substr($newphone, 2, 3).$separ.substr($newphone, 5, 3).$separ.substr($newphone, 8, 4);
3403  }
3404  } elseif (strtoupper($countrycode) == "MX") {//Mexique
3405  if (dol_strlen($phone) == 12) {//ex: +52 ABCD_EFG_HI
3406  $newphone = substr($newphone, 0, 3).$separ.substr($newphone, 3, 4).$separ.substr($newphone, 7, 3).$separ.substr($newphone, 10, 2);
3407  } elseif (dol_strlen($phone) == 11) {//ex: +52 AB_CD_EF_GH
3408  $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);
3409  } elseif (dol_strlen($phone) == 13) {//ex: +52 ABC_DEF_GHIJ
3410  $newphone = substr($newphone, 0, 3).$separ.substr($newphone, 3, 3).$separ.substr($newphone, 6, 3).$separ.substr($newphone, 9, 4);
3411  }
3412  } elseif (strtoupper($countrycode) == "ML") {//Mali
3413  if (dol_strlen($phone) == 12) {//ex: +223 AB_CD_EF_GH
3414  $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);
3415  }
3416  } elseif (strtoupper($countrycode) == "TH") {//Thaïlande
3417  if (dol_strlen($phone) == 11) {//ex: +66_ABC_DE_FGH
3418  $newphone = substr($newphone, 0, 3).$separ.substr($newphone, 3, 3).$separ.substr($newphone, 6, 2).$separ.substr($newphone, 8, 3);
3419  } elseif (dol_strlen($phone) == 12) {//ex: +66_A_BCD_EF_GHI
3420  $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);
3421  }
3422  } elseif (strtoupper($countrycode) == "MU") {
3423  //Maurice
3424  if (dol_strlen($phone) == 11) {//ex: +230_ABC_DE_FG
3425  $newphone = substr($newphone, 0, 4).$separ.substr($newphone, 4, 3).$separ.substr($newphone, 7, 2).$separ.substr($newphone, 9, 2);
3426  } elseif (dol_strlen($phone) == 12) {//ex: +230_ABCD_EF_GH
3427  $newphone = substr($newphone, 0, 4).$separ.substr($newphone, 4, 4).$separ.substr($newphone, 8, 2).$separ.substr($newphone, 10, 2);
3428  }
3429  } elseif (strtoupper($countrycode) == "ZA") {//Afrique du sud
3430  if (dol_strlen($phone) == 12) {//ex: +27_AB_CDE_FG_HI
3431  $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);
3432  }
3433  } elseif (strtoupper($countrycode) == "SY") {//Syrie
3434  if (dol_strlen($phone) == 12) {//ex: +963_AB_CD_EF_GH
3435  $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);
3436  } elseif (dol_strlen($phone) == 13) {//ex: +963_AB_CD_EF_GHI
3437  $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);
3438  }
3439  } elseif (strtoupper($countrycode) == "AE") {//Emirats Arabes Unis
3440  if (dol_strlen($phone) == 12) {//ex: +971_ABC_DEF_GH
3441  $newphone = substr($newphone, 0, 4).$separ.substr($newphone, 4, 3).$separ.substr($newphone, 7, 3).$separ.substr($newphone, 10, 2);
3442  } elseif (dol_strlen($phone) == 13) {//ex: +971_ABC_DEF_GHI
3443  $newphone = substr($newphone, 0, 4).$separ.substr($newphone, 4, 3).$separ.substr($newphone, 7, 3).$separ.substr($newphone, 10, 3);
3444  } elseif (dol_strlen($phone) == 14) {//ex: +971_ABC_DEF_GHIK
3445  $newphone = substr($newphone, 0, 4).$separ.substr($newphone, 4, 3).$separ.substr($newphone, 7, 3).$separ.substr($newphone, 10, 4);
3446  }
3447  } elseif (strtoupper($countrycode) == "DZ") {//Algérie
3448  if (dol_strlen($phone) == 13) {//ex: +213_ABC_DEF_GHI
3449  $newphone = substr($newphone, 0, 4).$separ.substr($newphone, 4, 3).$separ.substr($newphone, 7, 3).$separ.substr($newphone, 10, 3);
3450  }
3451  } elseif (strtoupper($countrycode) == "BE") {//Belgique
3452  if (dol_strlen($phone) == 11) {//ex: +32_ABC_DE_FGH
3453  $newphone = substr($newphone, 0, 3).$separ.substr($newphone, 3, 3).$separ.substr($newphone, 6, 2).$separ.substr($newphone, 8, 3);
3454  } elseif (dol_strlen($phone) == 12) {//ex: +32_ABC_DEF_GHI
3455  $newphone = substr($newphone, 0, 3).$separ.substr($newphone, 3, 3).$separ.substr($newphone, 6, 3).$separ.substr($newphone, 9, 3);
3456  }
3457  } elseif (strtoupper($countrycode) == "PF") {//Polynésie française
3458  if (dol_strlen($phone) == 12) {//ex: +689_AB_CD_EF_GH
3459  $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);
3460  }
3461  } elseif (strtoupper($countrycode) == "CO") {//Colombie
3462  if (dol_strlen($phone) == 13) {//ex: +57_ABC_DEF_GH_IJ
3463  $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);
3464  }
3465  } elseif (strtoupper($countrycode) == "JO") {//Jordanie
3466  if (dol_strlen($phone) == 12) {//ex: +962_A_BCD_EF_GH
3467  $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);
3468  }
3469  } elseif (strtoupper($countrycode) == "JM") {//Jamaïque
3470  if (dol_strlen($newphone) == 12) {//ex: +1867_ABC_DEFG
3471  $newphone = substr($newphone, 0, 5).$separ.substr($newphone, 5, 3).$separ.substr($newphone, 8, 4);
3472  }
3473  } elseif (strtoupper($countrycode) == "MG") {//Madagascar
3474  if (dol_strlen($phone) == 13) {//ex: +261_AB_CD_EFG_HI
3475  $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);
3476  }
3477  } elseif (strtoupper($countrycode) == "GB") {//Royaume uni
3478  if (dol_strlen($phone) == 13) {//ex: +44_ABCD_EFG_HIJ
3479  $newphone = substr($newphone, 0, 3).$separ.substr($newphone, 3, 4).$separ.substr($newphone, 7, 3).$separ.substr($newphone, 10, 3);
3480  }
3481  } elseif (strtoupper($countrycode) == "CH") {//Suisse
3482  if (dol_strlen($phone) == 12) {//ex: +41_AB_CDE_FG_HI
3483  $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);
3484  } elseif (dol_strlen($phone) == 15) {// +41_AB_CDE_FGH_IJKL
3485  $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);
3486  }
3487  } elseif (strtoupper($countrycode) == "TN") {//Tunisie
3488  if (dol_strlen($phone) == 12) {//ex: +216_AB_CDE_FGH
3489  $newphone = substr($newphone, 0, 4).$separ.substr($newphone, 4, 2).$separ.substr($newphone, 6, 3).$separ.substr($newphone, 9, 3);
3490  }
3491  } elseif (strtoupper($countrycode) == "GF") {//Guyane francaise
3492  if (dol_strlen($phone) == 13) {//ex: +594_ABC_DE_FG_HI (ABC=594 de nouveau)
3493  $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);
3494  }
3495  } elseif (strtoupper($countrycode) == "GP") {//Guadeloupe
3496  if (dol_strlen($phone) == 13) {//ex: +590_ABC_DE_FG_HI (ABC=590 de nouveau)
3497  $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);
3498  }
3499  } elseif (strtoupper($countrycode) == "MQ") {//Martinique
3500  if (dol_strlen($phone) == 13) {//ex: +596_ABC_DE_FG_HI (ABC=596 de nouveau)
3501  $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);
3502  }
3503  } elseif (strtoupper($countrycode) == "IT") {//Italie
3504  if (dol_strlen($phone) == 12) {//ex: +39_ABC_DEF_GHI
3505  $newphone = substr($newphone, 0, 3).$separ.substr($newphone, 3, 3).$separ.substr($newphone, 6, 3).$separ.substr($newphone, 9, 3);
3506  } elseif (dol_strlen($phone) == 13) {//ex: +39_ABC_DEF_GH_IJ
3507  $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);
3508  }
3509  } elseif (strtoupper($countrycode) == "AU") {
3510  //Australie
3511  if (dol_strlen($phone) == 12) {
3512  //ex: +61_A_BCDE_FGHI
3513  $newphone = substr($newphone, 0, 3).$separ.substr($newphone, 3, 1).$separ.substr($newphone, 4, 4).$separ.substr($newphone, 8, 4);
3514  }
3515  } elseif (strtoupper($countrycode) == "LU") {
3516  // Luxembourg
3517  if (dol_strlen($phone) == 10) {// fixe 6 chiffres +352_AA_BB_CC
3518  $newphone = substr($newphone, 0, 4).$separ.substr($newphone, 4, 2).$separ.substr($newphone, 6, 2).$separ.substr($newphone, 8, 2);
3519  } elseif (dol_strlen($phone) == 11) {// fixe 7 chiffres +352_AA_BB_CC_D
3520  $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);
3521  } elseif (dol_strlen($phone) == 12) {// fixe 8 chiffres +352_AA_BB_CC_DD
3522  $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);
3523  } elseif (dol_strlen($phone) == 13) {// mobile +352_AAA_BB_CC_DD
3524  $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);
3525  }
3526  }
3527  if (!empty($addlink)) { // Link on phone number (+ link to add action if conf->global->AGENDA_ADDACTIONFORPHONE set)
3528  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
3529  $newphoneform = $newphone;
3530  $newphone = '<a href="tel:'.$phone.'"';
3531  $newphone .= '>'.$newphoneform.'</a>';
3532  } elseif (isModEnabled('clicktodial') && $addlink == 'AC_TEL') { // If click to dial, we use click to dial url
3533  if (empty($user->clicktodial_loaded)) {
3534  $user->fetch_clicktodial();
3535  }
3536 
3537  // Define urlmask
3538  $urlmask = 'ErrorClickToDialModuleNotConfigured';
3539  if (!empty($conf->global->CLICKTODIAL_URL)) {
3540  $urlmask = $conf->global->CLICKTODIAL_URL;
3541  }
3542  if (!empty($user->clicktodial_url)) {
3543  $urlmask = $user->clicktodial_url;
3544  }
3545 
3546  $clicktodial_poste = (!empty($user->clicktodial_poste) ?urlencode($user->clicktodial_poste) : '');
3547  $clicktodial_login = (!empty($user->clicktodial_login) ?urlencode($user->clicktodial_login) : '');
3548  $clicktodial_password = (!empty($user->clicktodial_password) ?urlencode($user->clicktodial_password) : '');
3549  // This line is for backward compatibility
3550  $url = sprintf($urlmask, urlencode($phone), $clicktodial_poste, $clicktodial_login, $clicktodial_password);
3551  // Thoose lines are for substitution
3552  $substitarray = array('__PHONEFROM__'=>$clicktodial_poste,
3553  '__PHONETO__'=>urlencode($phone),
3554  '__LOGIN__'=>$clicktodial_login,
3555  '__PASS__'=>$clicktodial_password);
3556  $url = make_substitutions($url, $substitarray);
3557  $newphonesav = $newphone;
3558  if (empty($conf->global->CLICKTODIAL_DO_NOT_USE_AJAX_CALL)) {
3559  // Default and recommended: New method using ajax without submiting a page making a javascript history.go(-1) back
3560  $newphone = '<a href="'.$url.'" class="cssforclicktodial"'; // Call of ajax is handled by the lib_foot.js.php on class 'cssforclicktodial'
3561  $newphone .= '>'.$newphonesav.'</a>';
3562  } else {
3563  // Old method
3564  $newphone = '<a href="'.$url.'"';
3565  if (!empty($conf->global->CLICKTODIAL_FORCENEWTARGET)) {
3566  $newphone .= ' target="_blank" rel="noopener noreferrer"';
3567  }
3568  $newphone .= '>'.$newphonesav.'</a>';
3569  }
3570  }
3571 
3572  //if (($cid || $socid) && !empty($conf->agenda->enabled) && $user->rights->agenda->myactions->create)
3573  if (isModEnabled('agenda') && $user->hasRight("agenda", "myactions", "create")) {
3574  $type = 'AC_TEL';
3575  $link = '';
3576  if ($addlink == 'AC_FAX') {
3577  $type = 'AC_FAX';
3578  }
3579  if (!empty($conf->global->AGENDA_ADDACTIONFORPHONE)) {
3580  $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>';
3581  }
3582  if ($link) {
3583  $newphone = '<div>'.$newphone.' '.$link.'</div>';
3584  }
3585  }
3586  }
3587 
3588  if (empty($titlealt)) {
3589  $titlealt = ($withpicto == 'fax' ? $langs->trans("Fax") : $langs->trans("Phone"));
3590  }
3591  $rep = '';
3592 
3593  if ($hookmanager) {
3594  $parameters = array('countrycode' => $countrycode, 'cid' => $cid, 'socid' => $socid, 'titlealt' => $titlealt, 'picto' => $withpicto);
3595  $reshook = $hookmanager->executeHooks('printPhone', $parameters, $phone);
3596  $rep .= $hookmanager->resPrint;
3597  }
3598  if (empty($reshook)) {
3599  $picto = '';
3600  if ($withpicto) {
3601  if ($withpicto == 'fax') {
3602  $picto = 'phoning_fax';
3603  } elseif ($withpicto == 'phone') {
3604  $picto = 'phoning';
3605  } elseif ($withpicto == 'mobile') {
3606  $picto = 'phoning_mobile';
3607  } else {
3608  $picto = '';
3609  }
3610  }
3611  if ($adddivfloat == 1) {
3612  $rep .= '<div class="nospan float" style="margin-right: 10px">';
3613  } elseif (empty($adddivfloat)) {
3614  $rep .= '<span style="margin-right: 10px;">';
3615  }
3616  $rep .= ($withpicto ?img_picto($titlealt, 'object_'.$picto.'.png').' ' : '').$newphone;
3617  if ($adddivfloat == 1) {
3618  $rep .= '</div>';
3619  } elseif (empty($adddivfloat)) {
3620  $rep .= '</span>';
3621  }
3622  }
3623 
3624  return $rep;
3625 }
3626 
3634 function dol_print_ip($ip, $mode = 0)
3635 {
3636  global $conf, $langs;
3637 
3638  $ret = '';
3639 
3640  if (empty($mode)) {
3641  $ret .= $ip;
3642  }
3643 
3644  if ($mode != 2) {
3645  $countrycode = dolGetCountryCodeFromIp($ip);
3646  if ($countrycode) { // If success, countrycode is us, fr, ...
3647  if (file_exists(DOL_DOCUMENT_ROOT.'/theme/common/flags/'.$countrycode.'.png')) {
3648  $ret .= ' '.img_picto($countrycode.' '.$langs->trans("AccordingToGeoIPDatabase"), DOL_URL_ROOT.'/theme/common/flags/'.$countrycode.'.png', '', 1);
3649  } else {
3650  $ret .= ' ('.$countrycode.')';
3651  }
3652  } else {
3653  // Nothing
3654  }
3655  }
3656 
3657  return $ret;
3658 }
3659 
3669 {
3670  if (empty($_SERVER['HTTP_X_FORWARDED_FOR']) || preg_match('/[^0-9\.\:,\[\]]/', $_SERVER['HTTP_X_FORWARDED_FOR'])) {
3671  if (empty($_SERVER['HTTP_CLIENT_IP']) || preg_match('/[^0-9\.\:,\[\]]/', $_SERVER['HTTP_CLIENT_IP'])) {
3672  if (empty($_SERVER["HTTP_CF_CONNECTING_IP"])) {
3673  $ip = (empty($_SERVER['REMOTE_ADDR']) ? '' : $_SERVER['REMOTE_ADDR']); // value may have been the IP of the proxy and not the client
3674  } else {
3675  $ip = $_SERVER["HTTP_CF_CONNECTING_IP"]; // value here may have been forged by client
3676  }
3677  } else {
3678  $ip = $_SERVER['HTTP_CLIENT_IP']; // value is clean here but may have been forged by proxy
3679  }
3680  } else {
3681  $ip = $_SERVER['HTTP_X_FORWARDED_FOR']; // value is clean here but may have been forged by proxy
3682  }
3683  return $ip;
3684 }
3685 
3694 function isHTTPS()
3695 {
3696  $isSecure = false;
3697  if (isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] == 'on') {
3698  $isSecure = true;
3699  } 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') {
3700  $isSecure = true;
3701  }
3702  return $isSecure;
3703 }
3704 
3712 {
3713  global $conf;
3714 
3715  $countrycode = '';
3716 
3717  if (!empty($conf->geoipmaxmind->enabled)) {
3718  $datafile = getDolGlobalString('GEOIPMAXMIND_COUNTRY_DATAFILE');
3719  //$ip='24.24.24.24';
3720  //$datafile='/usr/share/GeoIP/GeoIP.dat'; Note that this must be downloaded datafile (not same than datafile provided with ubuntu packages)
3721  include_once DOL_DOCUMENT_ROOT.'/core/class/dolgeoip.class.php';
3722  $geoip = new DolGeoIP('country', $datafile);
3723  //print 'ip='.$ip.' databaseType='.$geoip->gi->databaseType." GEOIP_CITY_EDITION_REV1=".GEOIP_CITY_EDITION_REV1."\n";
3724  $countrycode = $geoip->getCountryCodeFromIP($ip);
3725  }
3726 
3727  return $countrycode;
3728 }
3729 
3730 
3738 {
3739  global $conf, $langs, $user;
3740 
3741  //$ret=$user->xxx;
3742  $ret = '';
3743  if (!empty($conf->geoipmaxmind->enabled)) {
3744  $ip = getUserRemoteIP();
3745  $datafile = getDolGlobalString('GEOIPMAXMIND_COUNTRY_DATAFILE');
3746  //$ip='24.24.24.24';
3747  //$datafile='E:\Mes Sites\Web\Admin1\awstats\maxmind\GeoIP.dat';
3748  include_once DOL_DOCUMENT_ROOT.'/core/class/dolgeoip.class.php';
3749  $geoip = new DolGeoIP('country', $datafile);
3750  $countrycode = $geoip->getCountryCodeFromIP($ip);
3751  $ret = $countrycode;
3752  }
3753  return $ret;
3754 }
3755 
3768 function dol_print_address($address, $htmlid, $element, $id, $noprint = 0, $charfornl = '')
3769 {
3770  global $conf, $user, $langs, $hookmanager;
3771 
3772  $out = '';
3773 
3774  if ($address) {
3775  if ($hookmanager) {
3776  $parameters = array('element' => $element, 'id' => $id);
3777  $reshook = $hookmanager->executeHooks('printAddress', $parameters, $address);
3778  $out .= $hookmanager->resPrint;
3779  }
3780  if (empty($reshook)) {
3781  if (empty($charfornl)) {
3782  $out .= nl2br($address);
3783  } else {
3784  $out .= preg_replace('/[\r\n]+/', $charfornl, $address);
3785  }
3786 
3787  // TODO Remove this block, we can add this using the hook now
3788  $showgmap = $showomap = 0;
3789  if (($element == 'thirdparty' || $element == 'societe') && !empty($conf->google->enabled) && !empty($conf->global->GOOGLE_ENABLE_GMAPS)) {
3790  $showgmap = 1;
3791  }
3792  if ($element == 'contact' && !empty($conf->google->enabled) && !empty($conf->global->GOOGLE_ENABLE_GMAPS_CONTACTS)) {
3793  $showgmap = 1;
3794  }
3795  if ($element == 'member' && !empty($conf->google->enabled) && !empty($conf->global->GOOGLE_ENABLE_GMAPS_MEMBERS)) {
3796  $showgmap = 1;
3797  }
3798  if (($element == 'thirdparty' || $element == 'societe') && !empty($conf->openstreetmap->enabled) && !empty($conf->global->OPENSTREETMAP_ENABLE_MAPS)) {
3799  $showomap = 1;
3800  }
3801  if ($element == 'contact' && !empty($conf->openstreetmap->enabled) && !empty($conf->global->OPENSTREETMAP_ENABLE_MAPS_CONTACTS)) {
3802  $showomap = 1;
3803  }
3804  if ($element == 'member' && !empty($conf->openstreetmap->enabled) && !empty($conf->global->OPENSTREETMAP_ENABLE_MAPS_MEMBERS)) {
3805  $showomap = 1;
3806  }
3807  if ($showgmap) {
3808  $url = dol_buildpath('/google/gmaps.php?mode='.$element.'&id='.$id, 1);
3809  $out .= ' <a href="'.$url.'" target="_gmaps"><img id="'.$htmlid.'" class="valigntextbottom" src="'.DOL_URL_ROOT.'/theme/common/gmap.png"></a>';
3810  }
3811  if ($showomap) {
3812  $url = dol_buildpath('/openstreetmap/maps.php?mode='.$element.'&id='.$id, 1);
3813  $out .= ' <a href="'.$url.'" target="_gmaps"><img id="'.$htmlid.'_openstreetmap" class="valigntextbottom" src="'.DOL_URL_ROOT.'/theme/common/gmap.png"></a>';
3814  }
3815  }
3816  }
3817  if ($noprint) {
3818  return $out;
3819  } else {
3820  print $out;
3821  }
3822 }
3823 
3824 
3834 function isValidEmail($address, $acceptsupervisorkey = 0, $acceptuserkey = 0)
3835 {
3836  if ($acceptsupervisorkey && $address == '__SUPERVISOREMAIL__') {
3837  return true;
3838  }
3839  if ($acceptuserkey && $address == '__USER_EMAIL__') {
3840  return true;
3841  }
3842  if (filter_var($address, FILTER_VALIDATE_EMAIL)) {
3843  return true;
3844  }
3845 
3846  return false;
3847 }
3848 
3857 function isValidMXRecord($domain)
3858 {
3859  if (function_exists('idn_to_ascii') && function_exists('checkdnsrr')) {
3860  if (!checkdnsrr(idn_to_ascii($domain), 'MX')) {
3861  return 0;
3862  }
3863  if (function_exists('getmxrr')) {
3864  $mxhosts = array();
3865  $weight = array();
3866  getmxrr(idn_to_ascii($domain), $mxhosts, $weight);
3867  if (count($mxhosts) > 1) {
3868  return 1;
3869  }
3870  if (count($mxhosts) == 1 && !empty($mxhosts[0])) {
3871  return 1;
3872  }
3873 
3874  return 0;
3875  }
3876  }
3877 
3878  // function idn_to_ascii or checkdnsrr or getmxrr does not exists
3879  return -1;
3880 }
3881 
3889 function isValidPhone($phone)
3890 {
3891  return true;
3892 }
3893 
3894 
3904 function dolGetFirstLetters($s, $nbofchar = 1)
3905 {
3906  $ret = '';
3907  $tmparray = explode(' ', $s);
3908  foreach ($tmparray as $tmps) {
3909  $ret .= dol_substr($tmps, 0, $nbofchar);
3910  }
3911 
3912  return $ret;
3913 }
3914 
3915 
3923 function dol_strlen($string, $stringencoding = 'UTF-8')
3924 {
3925  if (is_null($string)) {
3926  return 0;
3927  }
3928 
3929  if (function_exists('mb_strlen')) {
3930  return mb_strlen($string, $stringencoding);
3931  } else {
3932  return strlen($string);
3933  }
3934 }
3935 
3946 function dol_substr($string, $start, $length = null, $stringencoding = '', $trunconbytes = 0)
3947 {
3948  global $langs;
3949 
3950  if (empty($stringencoding)) {
3951  $stringencoding = $langs->charset_output;
3952  }
3953 
3954  $ret = '';
3955  if (empty($trunconbytes)) {
3956  if (function_exists('mb_substr')) {
3957  $ret = mb_substr($string, $start, $length, $stringencoding);
3958  } else {
3959  $ret = substr($string, $start, $length);
3960  }
3961  } else {
3962  if (function_exists('mb_strcut')) {
3963  $ret = mb_strcut($string, $start, $length, $stringencoding);
3964  } else {
3965  $ret = substr($string, $start, $length);
3966  }
3967  }
3968  return $ret;
3969 }
3970 
3971 
3985 function dol_trunc($string, $size = 40, $trunc = 'right', $stringencoding = 'UTF-8', $nodot = 0, $display = 0)
3986 {
3987  global $conf;
3988 
3989  if (empty($size) || !empty($conf->global->MAIN_DISABLE_TRUNC)) {
3990  return $string;
3991  }
3992 
3993  if (empty($stringencoding)) {
3994  $stringencoding = 'UTF-8';
3995  }
3996  // reduce for small screen
3997  if ($conf->dol_optimize_smallscreen == 1 && $display == 1) {
3998  $size = round($size / 3);
3999  }
4000 
4001  // We go always here
4002  if ($trunc == 'right') {
4003  $newstring = dol_textishtml($string) ? dol_string_nohtmltag($string, 1) : $string;
4004  if (dol_strlen($newstring, $stringencoding) > ($size + ($nodot ? 0 : 1))) {
4005  // If nodot is 0 and size is 1 chars more, we don't trunc and don't add …
4006  return dol_substr($newstring, 0, $size, $stringencoding).($nodot ? '' : '…');
4007  } else {
4008  //return 'u'.$size.'-'.$newstring.'-'.dol_strlen($newstring,$stringencoding).'-'.$string;
4009  return $string;
4010  }
4011  } elseif ($trunc == 'middle') {
4012  $newstring = dol_textishtml($string) ? dol_string_nohtmltag($string, 1) : $string;
4013  if (dol_strlen($newstring, $stringencoding) > 2 && dol_strlen($newstring, $stringencoding) > ($size + 1)) {
4014  $size1 = round($size / 2);
4015  $size2 = round($size / 2);
4016  return dol_substr($newstring, 0, $size1, $stringencoding).'…'.dol_substr($newstring, dol_strlen($newstring, $stringencoding) - $size2, $size2, $stringencoding);
4017  } else {
4018  return $string;
4019  }
4020  } elseif ($trunc == 'left') {
4021  $newstring = dol_textishtml($string) ? dol_string_nohtmltag($string, 1) : $string;
4022  if (dol_strlen($newstring, $stringencoding) > ($size + ($nodot ? 0 : 1))) {
4023  // If nodot is 0 and size is 1 chars more, we don't trunc and don't add …
4024  return '…'.dol_substr($newstring, dol_strlen($newstring, $stringencoding) - $size, $size, $stringencoding);
4025  } else {
4026  return $string;
4027  }
4028  } elseif ($trunc == 'wrap') {
4029  $newstring = dol_textishtml($string) ? dol_string_nohtmltag($string, 1) : $string;
4030  if (dol_strlen($newstring, $stringencoding) > ($size + 1)) {
4031  return dol_substr($newstring, 0, $size, $stringencoding)."\n".dol_trunc(dol_substr($newstring, $size, dol_strlen($newstring, $stringencoding) - $size, $stringencoding), $size, $trunc);
4032  } else {
4033  return $string;
4034  }
4035  } else {
4036  return 'BadParam3CallingDolTrunc';
4037  }
4038 }
4039 
4061 function img_picto($titlealt, $picto, $moreatt = '', $pictoisfullpath = false, $srconly = 0, $notitle = 0, $alt = '', $morecss = '', $marginleftonlyshort = 2)
4062 {
4063  global $conf, $langs;
4064 
4065  // We forge fullpathpicto for image to $path/img/$picto. By default, we take DOL_URL_ROOT/theme/$conf->theme/img/$picto
4066  $url = DOL_URL_ROOT;
4067  $theme = isset($conf->theme) ? $conf->theme : null;
4068  $path = 'theme/'.$theme;
4069  // Define fullpathpicto to use into src
4070  if ($pictoisfullpath) {
4071  // Clean parameters
4072  if (!preg_match('/(\.png|\.gif|\.svg)$/i', $picto)) {
4073  $picto .= '.png';
4074  }
4075  $fullpathpicto = $picto;
4076  $reg = array();
4077  if (preg_match('/class="([^"]+)"/', $moreatt, $reg)) {
4078  $morecss .= ($morecss ? ' ' : '').$reg[1];
4079  $moreatt = str_replace('class="'.$reg[1].'"', '', $moreatt);
4080  }
4081  } else {
4082  $pictowithouttext = preg_replace('/(\.png|\.gif|\.svg)$/', '', $picto);
4083  $pictowithouttext = str_replace('object_', '', $pictowithouttext);
4084  $pictowithouttext = str_replace('_nocolor', '', $pictowithouttext);
4085 
4086  if (strpos($pictowithouttext, 'fontawesome_') !== false || preg_match('/^fa-/', $pictowithouttext)) {
4087  // This is a font awesome image 'fonwtawesome_xxx' or 'fa-xxx'
4088  $pictowithouttext = str_replace('fontawesome_', '', $pictowithouttext);
4089  $pictowithouttext = str_replace('fa-', '', $pictowithouttext);
4090 
4091  $pictowithouttextarray = explode('_', $pictowithouttext);
4092  $marginleftonlyshort = 0;
4093 
4094  if (!empty($pictowithouttextarray[1])) {
4095  // Syntax is 'fontawesome_fakey_faprefix_facolor_fasize' or 'fa-fakey_faprefix_facolor_fasize'
4096  $fakey = 'fa-'.$pictowithouttextarray[0];
4097  $fa = empty($pictowithouttextarray[1]) ? 'fa' : $pictowithouttextarray[1];
4098  $facolor = empty($pictowithouttextarray[2]) ? '' : $pictowithouttextarray[2];
4099  $fasize = empty($pictowithouttextarray[3]) ? '' : $pictowithouttextarray[3];
4100  } else {
4101  $fakey = 'fa-'.$pictowithouttext;
4102  $fa = 'fa';
4103  $facolor = '';
4104  $fasize = '';
4105  }
4106 
4107  // This snippet only needed since function img_edit accepts only one additional parameter: no separate one for css only.
4108  // class/style need to be extracted to avoid duplicate class/style validation errors when $moreatt is added to the end of the attributes.
4109  $morestyle = '';
4110  $reg = array();
4111  if (preg_match('/class="([^"]+)"/', $moreatt, $reg)) {
4112  $morecss .= ($morecss ? ' ' : '').$reg[1];
4113  $moreatt = str_replace('class="'.$reg[1].'"', '', $moreatt);
4114  }
4115  if (preg_match('/style="([^"]+)"/', $moreatt, $reg)) {
4116  $morestyle = $reg[1];
4117  $moreatt = str_replace('style="'.$reg[1].'"', '', $moreatt);
4118  }
4119  $moreatt = trim($moreatt);
4120 
4121  $enabledisablehtml = '<span class="'.$fa.' '.$fakey.($marginleftonlyshort ? ($marginleftonlyshort == 1 ? ' marginleftonlyshort' : ' marginleftonly') : '');
4122  $enabledisablehtml .= ($morecss ? ' '.$morecss : '').'" style="'.($fasize ? ('font-size: '.$fasize.';') : '').($facolor ? (' color: '.$facolor.';') : '').($morestyle ? ' '.$morestyle : '').'"'.(($notitle || empty($titlealt)) ? '' : ' title="'.dol_escape_htmltag($titlealt).'"').($moreatt ? ' '.$moreatt : '').'>';
4123  /*if (!empty($conf->global->MAIN_OPTIMIZEFORTEXTBROWSER)) {
4124  $enabledisablehtml .= $titlealt;
4125  }*/
4126  $enabledisablehtml .= '</span>';
4127 
4128  return $enabledisablehtml;
4129  }
4130 
4131  if (empty($srconly) && in_array($pictowithouttext, array(
4132  '1downarrow', '1uparrow', '1leftarrow', '1rightarrow', '1uparrow_selected', '1downarrow_selected', '1leftarrow_selected', '1rightarrow_selected',
4133  'accountancy', 'accounting_account', 'account', 'accountline', 'action', 'add', 'address', 'angle-double-down', 'angle-double-up', 'asset',
4134  'bank_account', 'barcode', 'bank', 'bell', 'bill', 'billa', 'billr', 'billd', 'birthday-cake', 'bookmark', 'bom', 'briefcase-medical', 'bug', 'building',
4135  'card', 'calendarlist', 'calendar', 'calendarmonth', 'calendarweek', 'calendarday', 'calendarperuser', 'calendarpertype',
4136  'cash-register', 'category', 'chart', 'check', 'clock', 'close_title', 'cog', 'collab', 'company', 'contact', 'country', 'contract', 'conversation', 'cron', 'cross', 'cubes',
4137  'currency', 'multicurrency',
4138  'delete', 'dolly', 'dollyrevert', 'donation', 'download', 'dynamicprice',
4139  'edit', 'ellipsis-h', 'email', 'entity', 'envelope', 'eraser', 'establishment', 'expensereport', 'external-link-alt', 'external-link-square-alt', 'eye',
4140  'filter', 'file-code', 'file-export', 'file-import', 'file-upload', 'autofill', 'folder', 'folder-open', 'folder-plus',
4141  'gears', 'generate', 'globe', 'globe-americas', 'graph', 'grip', 'grip_title', 'group',
4142  'hands-helping', 'help', 'holiday',
4143  'id-card', 'images', 'incoterm', 'info', 'intervention', 'inventory', 'intracommreport', 'jobprofile',
4144  'knowledgemanagement',
4145  'label', 'language', 'line', 'link', 'list', 'list-alt', 'listlight', 'loan', 'lock', 'lot', 'long-arrow-alt-right',
4146  'margin', 'map-marker-alt', 'member', 'meeting', 'money-bill-alt', 'movement', 'mrp', 'note', 'next',
4147  'off', 'on', 'order',
4148  'paiment', 'paragraph', 'play', 'pdf', 'phone', 'phoning', 'phoning_mobile', 'phoning_fax', 'playdisabled', 'previous', 'poll', 'pos', 'printer', 'product', 'propal', 'proposal', 'puce',
4149  'stock', 'resize', 'service', 'stats', 'trip',
4150  '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',
4151  'github', 'google', 'jabber', 'microsoft', 'skype', 'twitter', 'facebook', 'linkedin', 'instagram', 'snapchat', 'youtube', 'google-plus-g', 'whatsapp',
4152  'chevron-left', 'chevron-right', 'chevron-down', 'chevron-top', 'commercial', 'companies',
4153  'generic', 'home', 'hrm', 'members', 'products', 'invoicing',
4154  'partnership', 'payment', 'payment_vat', 'pencil-ruler', 'pictoconfirm', 'preview', 'project', 'projectpub', 'projecttask', 'question', 'refresh', 'region',
4155  'salary', 'shipment', 'state', 'supplier_invoice', 'supplier_invoicea', 'supplier_invoicer', 'supplier_invoiced',
4156  'technic', 'ticket',
4157  'error', 'warning',
4158  'recent', 'reception', 'recruitmentcandidature', 'recruitmentjobposition', 'replacement', 'resource', 'recurring','rss',
4159  'shapes', 'skill', 'square', 'stop-circle', 'supplier', 'supplier_proposal', 'supplier_order', 'supplier_invoice',
4160  'timespent', 'title_setup', 'title_accountancy', 'title_bank', 'title_hrm', 'title_agenda',
4161  'uncheck', 'url', 'user-cog', 'user-injured', 'user-md', 'vat', 'website', 'workstation', 'webhook', 'world', 'private',
4162  'conferenceorbooth', 'eventorganization',
4163  'stamp', 'signature'
4164  ))) {
4165  $fakey = $pictowithouttext;
4166  $facolor = '';
4167  $fasize = '';
4168  $fa = 'fas';
4169  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'))) {
4170  $fa = 'far';
4171  }
4172  if (in_array($pictowithouttext, array('black-tie', 'github', 'google', 'microsoft', 'skype', 'twitter', 'facebook', 'linkedin', 'instagram', 'snapchat', 'stripe', 'stripe-s', 'youtube', 'google-plus-g', 'whatsapp'))) {
4173  $fa = 'fab';
4174  }
4175 
4176  $arrayconvpictotofa = array(
4177  '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',
4178  'bank_account'=>'university',
4179  'bill'=>'file-invoice-dollar', 'billa'=>'file-excel', 'billr'=>'file-invoice-dollar', 'billd'=>'file-medical',
4180  'supplier_invoice'=>'file-invoice-dollar', 'supplier_invoicea'=>'file-excel', 'supplier_invoicer'=>'file-invoice-dollar', 'supplier_invoiced'=>'file-medical',
4181  'bom'=>'shapes',
4182  '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',
4183  'donation'=>'file-alt', 'dynamicprice'=>'hand-holding-usd',
4184  'setup'=>'cog', 'companies'=>'building', 'products'=>'cube', 'commercial'=>'suitcase', 'invoicing'=>'coins',
4185  'accounting'=>'search-dollar', 'category'=>'tag', 'dollyrevert'=>'dolly',
4186  'generate'=>'plus-square', 'hrm'=>'user-tie', 'incoterm'=>'truck-loading',
4187  'margin'=>'calculator', 'members'=>'user-friends', 'ticket'=>'ticket-alt', 'globe'=>'external-link-alt', 'lot'=>'barcode',
4188  'email'=>'at', 'establishment'=>'building', 'edit'=>'pencil-alt', 'entity'=>'globe',
4189  'graph'=>'chart-line', 'grip_title'=>'arrows-alt', 'grip'=>'arrows-alt', 'help'=>'question-circle',
4190  'generic'=>'file', 'holiday'=>'umbrella-beach',
4191  'info'=>'info-circle', 'inventory'=>'boxes', 'intracommreport'=>'globe-europe', 'jobprofile'=>'cogs',
4192  'knowledgemanagement'=>'ticket-alt', 'label'=>'layer-group', 'line'=>'bars', 'loan'=>'money-bill-alt',
4193  'member'=>'user-alt', 'meeting'=>'chalkboard-teacher', 'mrp'=>'cubes', 'next'=>'arrow-alt-circle-right',
4194  'trip'=>'wallet', 'expensereport'=>'wallet', 'group'=>'users', 'movement'=>'people-carry',
4195  'sign-out'=>'sign-out-alt',
4196  'switch_off'=>'toggle-off', 'switch_on'=>'toggle-on', 'switch_on_warning'=>'toggle-on', 'switch_on_red'=>'toggle-on', 'check'=>'check', 'bookmark'=>'star',
4197  'bank'=>'university', 'close_title'=>'times', 'delete'=>'trash', 'filter'=>'filter',
4198  'list-alt'=>'list-alt', 'calendarlist'=>'bars', 'calendar'=>'calendar-alt', 'calendarmonth'=>'calendar-alt', 'calendarweek'=>'calendar-week', 'calendarday'=>'calendar-day', 'calendarperuser'=>'table',
4199  'intervention'=>'ambulance', 'invoice'=>'file-invoice-dollar', 'currency'=>'dollar-sign', 'multicurrency'=>'dollar-sign', 'order'=>'file-invoice',
4200  'error'=>'exclamation-triangle', 'warning'=>'exclamation-triangle',
4201  'other'=>'square',
4202  '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',
4203  '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',
4204  'recent' => 'check-square', 'reception'=>'dolly', 'recruitmentjobposition'=>'id-card-alt', 'recruitmentcandidature'=>'id-badge',
4205  'resize'=>'crop', 'supplier_order'=>'dol-order_supplier', 'supplier_proposal'=>'file-signature',
4206  'refresh'=>'redo', 'region'=>'map-marked', 'replacement'=>'exchange-alt', 'resource'=>'laptop-house', 'recurring'=>'history',
4207  'service'=>'concierge-bell',
4208  'skill'=>'shapes', 'state'=>'map-marked-alt', 'security'=>'key', 'salary'=>'wallet', 'shipment'=>'dolly', 'stock'=>'box-open', 'stats' => 'chart-bar', 'split'=>'code-branch', 'stripe'=>'stripe-s',
4209  'supplier'=>'building', 'technic'=>'cogs',
4210  'timespent'=>'clock', 'title_setup'=>'tools', 'title_accountancy'=>'money-check-alt', 'title_bank'=>'university', 'title_hrm'=>'umbrella-beach',
4211  'title_agenda'=>'calendar-alt',
4212  'uncheck'=>'times', 'uparrow'=>'share', 'url'=>'external-link-alt', 'vat'=>'money-check-alt', 'vcard'=>'arrow-alt-circle-down',
4213  'jabber'=>'comment-o',
4214  'website'=>'globe-americas', 'workstation'=>'pallet', 'webhook'=>'bullseye', 'world'=>'globe', 'private'=>'user-lock',
4215  'conferenceorbooth'=>'chalkboard-teacher', 'eventorganization'=>'project-diagram'
4216  );
4217  if ($pictowithouttext == 'off') {
4218  $fakey = 'fa-square';
4219  $fasize = '1.3em';
4220  } elseif ($pictowithouttext == 'on') {
4221  $fakey = 'fa-check-square';
4222  $fasize = '1.3em';
4223  } elseif ($pictowithouttext == 'listlight') {
4224  $fakey = 'fa-download';
4225  $marginleftonlyshort = 1;
4226  } elseif ($pictowithouttext == 'printer') {
4227  $fakey = 'fa-print';
4228  $fasize = '1.2em';
4229  } elseif ($pictowithouttext == 'note') {
4230  $fakey = 'fa-sticky-note';
4231  $marginleftonlyshort = 1;
4232  } elseif (in_array($pictowithouttext, array('1uparrow', '1downarrow', '1leftarrow', '1rightarrow', '1uparrow_selected', '1downarrow_selected', '1leftarrow_selected', '1rightarrow_selected'))) {
4233  $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');
4234  $fakey = 'fa-'.$convertarray[$pictowithouttext];
4235  if (preg_match('/selected/', $pictowithouttext)) {
4236  $facolor = '#888';
4237  }
4238  $marginleftonlyshort = 1;
4239  } elseif (!empty($arrayconvpictotofa[$pictowithouttext])) {
4240  $fakey = 'fa-'.$arrayconvpictotofa[$pictowithouttext];
4241  } else {
4242  $fakey = 'fa-'.$pictowithouttext;
4243  }
4244 
4245  if (in_array($pictowithouttext, array('dollyrevert', 'member', 'members', 'contract', 'group', 'resource', 'shipment'))) {
4246  $morecss .= ' em092';
4247  }
4248  if (in_array($pictowithouttext, array('conferenceorbooth', 'collab', 'eventorganization', 'holiday', 'info', 'project', 'workstation'))) {
4249  $morecss .= ' em088';
4250  }
4251  if (in_array($pictowithouttext, array('asset', 'intervention', 'payment', 'loan', 'partnership', 'stock', 'technic'))) {
4252  $morecss .= ' em080';
4253  }
4254 
4255  // Define $marginleftonlyshort
4256  $arrayconvpictotomarginleftonly = array(
4257  'bank', 'check', 'delete', 'generic', 'grip', 'grip_title', 'jabber',
4258  'grip_title', 'grip', 'listlight', 'note', 'on', 'off', 'playdisabled', 'printer', 'resize', 'sign-out', 'stats', 'switch_on', 'switch_on_red', 'switch_off',
4259  'uparrow', '1uparrow', '1downarrow', '1leftarrow', '1rightarrow', '1uparrow_selected', '1downarrow_selected', '1leftarrow_selected', '1rightarrow_selected'
4260  );
4261  if (!isset($arrayconvpictotomarginleftonly[$pictowithouttext])) {
4262  $marginleftonlyshort = 0;
4263  }
4264 
4265  // Add CSS
4266  $arrayconvpictotomorcess = array(
4267  'action'=>'infobox-action', 'account'=>'infobox-bank_account', 'accounting_account'=>'infobox-bank_account', 'accountline'=>'infobox-bank_account', 'accountancy'=>'infobox-bank_account', 'asset'=>'infobox-bank_account',
4268  'bank_account'=>'infobox-bank_account',
4269  'bill'=>'infobox-commande', 'billa'=>'infobox-commande', 'billr'=>'infobox-commande', 'billd'=>'infobox-commande',
4270  'margin'=>'infobox-bank_account', 'conferenceorbooth'=>'infobox-project',
4271  'cash-register'=>'infobox-bank_account', 'contract'=>'infobox-contrat', 'check'=>'font-status4', 'collab'=>'infobox-action', 'conversation'=>'infobox-contrat',
4272  'donation'=>'infobox-commande', 'dolly'=>'infobox-commande', 'dollyrevert'=>'flip infobox-order_supplier',
4273  'ecm'=>'infobox-action', 'eventorganization'=>'infobox-project',
4274  'hrm'=>'infobox-adherent', 'group'=>'infobox-adherent', 'intervention'=>'infobox-contrat',
4275  'incoterm'=>'infobox-supplier_proposal',
4276  'currency'=>'infobox-bank_account', 'multicurrency'=>'infobox-bank_account',
4277  'members'=>'infobox-adherent', 'member'=>'infobox-adherent', 'money-bill-alt'=>'infobox-bank_account',
4278  'order'=>'infobox-commande',
4279  'user'=>'infobox-adherent', 'users'=>'infobox-adherent',
4280  'error'=>'pictoerror', 'warning'=>'pictowarning', 'switch_on'=>'font-status4', 'switch_on_warning'=>'font-status4 warning', 'switch_on_red'=>'font-status8',
4281  'holiday'=>'infobox-holiday', 'info'=>'opacityhigh', 'invoice'=>'infobox-commande',
4282  'knowledgemanagement'=>'infobox-contrat rotate90', 'loan'=>'infobox-bank_account',
4283  'payment'=>'infobox-bank_account', 'payment_vat'=>'infobox-bank_account', 'poll'=>'infobox-adherent', 'pos'=>'infobox-bank_account', 'project'=>'infobox-project', 'projecttask'=>'infobox-project',
4284  'propal'=>'infobox-propal', 'proposal'=>'infobox-propal','private'=>'infobox-project',
4285  'reception'=>'flip', 'recruitmentjobposition'=>'infobox-adherent', 'recruitmentcandidature'=>'infobox-adherent',
4286  'resource'=>'infobox-action',
4287  'salary'=>'infobox-bank_account', 'shipment'=>'infobox-commande', 'supplier_invoice'=>'infobox-order_supplier', 'supplier_invoicea'=>'infobox-order_supplier', 'supplier_invoiced'=>'infobox-order_supplier',
4288  'supplier'=>'infobox-order_supplier', 'supplier_order'=>'infobox-order_supplier', 'supplier_proposal'=>'infobox-supplier_proposal',
4289  'ticket'=>'infobox-contrat', 'title_accountancy'=>'infobox-bank_account', 'title_hrm'=>'infobox-holiday', 'expensereport'=>'infobox-expensereport', 'trip'=>'infobox-expensereport', 'title_agenda'=>'infobox-action',
4290  'vat'=>'infobox-bank_account',
4291  //'title_setup'=>'infobox-action', 'tools'=>'infobox-action',
4292  'list-alt'=>'imgforviewmode', 'calendar'=>'imgforviewmode', 'calendarweek'=>'imgforviewmode', 'calendarmonth'=>'imgforviewmode', 'calendarday'=>'imgforviewmode', 'calendarperuser'=>'imgforviewmode'
4293  );
4294  if (!empty($arrayconvpictotomorcess[$pictowithouttext]) && strpos($picto, '_nocolor') === false) {
4295  $morecss .= ($morecss ? ' ' : '').$arrayconvpictotomorcess[$pictowithouttext];
4296  }
4297 
4298  // Define $color
4299  $arrayconvpictotocolor = array(
4300  'address'=>'#6c6aa8', 'building'=>'#6c6aa8', 'bom'=>'#a69944',
4301  'cog'=>'#999', 'companies'=>'#6c6aa8', 'company'=>'#6c6aa8', 'contact'=>'#6c6aa8', 'cron'=>'#555',
4302  'dynamicprice'=>'#a69944',
4303  'edit'=>'#444', 'note'=>'#999', 'error'=>'', 'help'=>'#bbb', 'listlight'=>'#999', 'language'=>'#555',
4304  //'dolly'=>'#a69944', 'dollyrevert'=>'#a69944',
4305  'lock'=>'#ddd', 'lot'=>'#a69944',
4306  'map-marker-alt'=>'#aaa', 'mrp'=>'#a69944', 'product'=>'#a69944', 'service'=>'#a69944', 'inventory'=>'#a69944', 'stock'=>'#a69944', 'movement'=>'#a69944',
4307  'other'=>'#ddd', 'world'=>'#986c6a',
4308  'partnership'=>'#6c6aa8', 'playdisabled'=>'#ccc', 'printer'=>'#444', 'projectpub'=>'#986c6a', 'reception'=>'#a69944', 'resize'=>'#444', 'rss'=>'#cba',
4309  //'shipment'=>'#a69944',
4310  'security'=>'#999', 'square'=>'#888', 'stop-circle'=>'#888', 'stats'=>'#444', 'switch_off'=>'#999', 'technic'=>'#999', 'timespent'=>'#555',
4311  'uncheck'=>'#800', 'uparrow'=>'#555', 'user-cog'=>'#999', 'country'=>'#aaa', 'globe-americas'=>'#aaa', 'region'=>'#aaa', 'state'=>'#aaa',
4312  'website'=>'#304', 'workstation'=>'#a69944'
4313  );
4314  if (isset($arrayconvpictotocolor[$pictowithouttext]) && strpos($picto, '_nocolor') === false) {
4315  $facolor = $arrayconvpictotocolor[$pictowithouttext];
4316  }
4317 
4318  // This snippet only needed since function img_edit accepts only one additional parameter: no separate one for css only.
4319  // class/style need to be extracted to avoid duplicate class/style validation errors when $moreatt is added to the end of the attributes.
4320  $morestyle = '';
4321  $reg = array();
4322  if (preg_match('/class="([^"]+)"/', $moreatt, $reg)) {
4323  $morecss .= ($morecss ? ' ' : '').$reg[1];
4324  $moreatt = str_replace('class="'.$reg[1].'"', '', $moreatt);
4325  }
4326  if (preg_match('/style="([^"]+)"/', $moreatt, $reg)) {
4327  $morestyle = $reg[1];
4328  $moreatt = str_replace('style="'.$reg[1].'"', '', $moreatt);
4329  }
4330  $moreatt = trim($moreatt);
4331 
4332  $enabledisablehtml = '<span class="'.$fa.' '.$fakey.($marginleftonlyshort ? ($marginleftonlyshort == 1 ? ' marginleftonlyshort' : ' marginleftonly') : '');
4333  $enabledisablehtml .= ($morecss ? ' '.$morecss : '').'" style="'.($fasize ? ('font-size: '.$fasize.';') : '').($facolor ? (' color: '.$facolor.';') : '').($morestyle ? ' '.$morestyle : '').'"'.(($notitle || empty($titlealt)) ? '' : ' title="'.dol_escape_htmltag($titlealt).'"').($moreatt ? ' '.$moreatt : '').'>';
4334  /*if (!empty($conf->global->MAIN_OPTIMIZEFORTEXTBROWSER)) {
4335  $enabledisablehtml .= $titlealt;
4336  }*/
4337  $enabledisablehtml .= '</span>';
4338 
4339  return $enabledisablehtml;
4340  }
4341 
4342  if (!empty($conf->global->MAIN_OVERWRITE_THEME_PATH)) {
4343  $path = $conf->global->MAIN_OVERWRITE_THEME_PATH.'/theme/'.$theme; // If the theme does not have the same name as the module
4344  } elseif (!empty($conf->global->MAIN_OVERWRITE_THEME_RES)) {
4345  $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
4346  } elseif (!empty($conf->modules_parts['theme']) && array_key_exists($theme, $conf->modules_parts['theme'])) {
4347  $path = $theme.'/theme/'.$theme; // If the theme have the same name as the module
4348  }
4349 
4350  // If we ask an image into $url/$mymodule/img (instead of default path)
4351  $regs = array();
4352  if (preg_match('/^([^@]+)@([^@]+)$/i', $picto, $regs)) {
4353  $picto = $regs[1];
4354  $path = $regs[2]; // $path is $mymodule
4355  }
4356 
4357  // Clean parameters
4358  if (!preg_match('/(\.png|\.gif|\.svg)$/i', $picto)) {
4359  $picto .= '.png';
4360  }
4361  // If alt path are defined, define url where img file is, according to physical path
4362  // ex: array(["main"]=>"/home/maindir/htdocs", ["alt0"]=>"/home/moddir0/htdocs", ...)
4363  foreach ($conf->file->dol_document_root as $type => $dirroot) {
4364  if ($type == 'main') {
4365  continue;
4366  }
4367  // This need a lot of time, that's why enabling alternative dir like "custom" dir is not recommanded
4368  if (file_exists($dirroot.'/'.$path.'/img/'.$picto)) {
4369  $url = DOL_URL_ROOT.$conf->file->dol_url_root[$type];
4370  break;
4371  }
4372  }
4373 
4374  // $url is '' or '/custom', $path is current theme or
4375  $fullpathpicto = $url.'/'.$path.'/img/'.$picto;
4376  }
4377 
4378  if ($srconly) {
4379  return $fullpathpicto;
4380  }
4381  // tag title is used for tooltip on <a>, tag alt can be used with very simple text on image for blind people
4382  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
4383 }
4384 
4398 function img_object($titlealt, $picto, $moreatt = '', $pictoisfullpath = false, $srconly = 0, $notitle = 0)
4399 {
4400  if (strpos($picto, '^') === 0) {
4401  return img_picto($titlealt, str_replace('^', '', $picto), $moreatt, $pictoisfullpath, $srconly, $notitle);
4402  } else {
4403  return img_picto($titlealt, 'object_'.$picto, $moreatt, $pictoisfullpath, $srconly, $notitle);
4404  }
4405 }
4406 
4418 function img_weather($titlealt, $picto, $moreatt = '', $pictoisfullpath = 0, $morecss = '')
4419 {
4420  global $conf;
4421 
4422  if (is_numeric($picto)) {
4423  //$leveltopicto = array(0=>'weather-clear.png', 1=>'weather-few-clouds.png', 2=>'weather-clouds.png', 3=>'weather-many-clouds.png', 4=>'weather-storm.png');
4424  //$picto = $leveltopicto[$picto];
4425  return '<i class="fa fa-weather-level'.$picto.'"></i>';
4426  } elseif (!preg_match('/(\.png|\.gif)$/i', $picto)) {
4427  $picto .= '.png';
4428  }
4429 
4430  $path = DOL_URL_ROOT.'/theme/'.$conf->theme.'/img/weather/'.$picto;
4431 
4432  return img_picto($titlealt, $path, $moreatt, 1, 0, 0, '', $morecss);
4433 }
4434 
4446 function img_picto_common($titlealt, $picto, $moreatt = '', $pictoisfullpath = 0, $notitle = 0)
4447 {
4448  global $conf;
4449 
4450  if (!preg_match('/(\.png|\.gif)$/i', $picto)) {
4451  $picto .= '.png';
4452  }
4453 
4454  if ($pictoisfullpath) {
4455  $path = $picto;
4456  } else {
4457  $path = DOL_URL_ROOT.'/theme/common/'.$picto;
4458 
4459  if (!empty($conf->global->MAIN_MODULE_CAN_OVERWRITE_COMMONICONS)) {
4460  $themepath = DOL_DOCUMENT_ROOT.'/theme/'.$conf->theme.'/img/'.$picto;
4461 
4462  if (file_exists($themepath)) {
4463  $path = $themepath;
4464  }
4465  }
4466  }
4467 
4468  return img_picto($titlealt, $path, $moreatt, 1, 0, $notitle);
4469 }
4470 
4483 function img_action($titlealt, $numaction, $picto = '')
4484 {
4485  global $langs;
4486 
4487  if (empty($titlealt) || $titlealt == 'default') {
4488  if ($numaction == '-1' || $numaction == 'ST_NO') {
4489  $numaction = -1;
4490  $titlealt = $langs->transnoentitiesnoconv('ChangeDoNotContact');
4491  } elseif ($numaction == '0' || $numaction == 'ST_NEVER') {
4492  $numaction = 0;
4493  $titlealt = $langs->transnoentitiesnoconv('ChangeNeverContacted');
4494  } elseif ($numaction == '1' || $numaction == 'ST_TODO') {
4495  $numaction = 1;
4496  $titlealt = $langs->transnoentitiesnoconv('ChangeToContact');
4497  } elseif ($numaction == '2' || $numaction == 'ST_PEND') {
4498  $numaction = 2;
4499  $titlealt = $langs->transnoentitiesnoconv('ChangeContactInProcess');
4500  } elseif ($numaction == '3' || $numaction == 'ST_DONE') {
4501  $numaction = 3;
4502  $titlealt = $langs->transnoentitiesnoconv('ChangeContactDone');
4503  } else {
4504  $titlealt = $langs->transnoentitiesnoconv('ChangeStatus '.$numaction);
4505  $numaction = 0;
4506  }
4507  }
4508  if (!is_numeric($numaction)) {
4509  $numaction = 0;
4510  }
4511 
4512  return img_picto($titlealt, !empty($picto) ? $picto : 'stcomm'.$numaction.'.png');
4513 }
4514 
4522 function img_pdf($titlealt = 'default', $size = 3)
4523 {
4524  global $langs;
4525 
4526  if ($titlealt == 'default') {
4527  $titlealt = $langs->trans('Show');
4528  }
4529 
4530  return img_picto($titlealt, 'pdf'.$size.'.png');
4531 }
4532 
4540 function img_edit_add($titlealt = 'default', $other = '')
4541 {
4542  global $langs;
4543 
4544  if ($titlealt == 'default') {
4545  $titlealt = $langs->trans('Add');
4546  }
4547 
4548  return img_picto($titlealt, 'edit_add.png', $other);
4549 }
4557 function img_edit_remove($titlealt = 'default', $other = '')
4558 {
4559  global $langs;
4560 
4561  if ($titlealt == 'default') {
4562  $titlealt = $langs->trans('Remove');
4563  }
4564 
4565  return img_picto($titlealt, 'edit_remove.png', $other);
4566 }
4567 
4576 function img_edit($titlealt = 'default', $float = 0, $other = '')
4577 {
4578  global $langs;
4579 
4580  if ($titlealt == 'default') {
4581  $titlealt = $langs->trans('Modify');
4582  }
4583 
4584  return img_picto($titlealt, 'edit.png', ($float ? 'style="float: '.($langs->tab_translate["DIRECTION"] == 'rtl' ? 'left' : 'right').'"' : "").($other ? ' '.$other : ''));
4585 }
4586 
4595 function img_view($titlealt = 'default', $float = 0, $other = 'class="valignmiddle"')
4596 {
4597  global $langs;
4598 
4599  if ($titlealt == 'default') {
4600  $titlealt = $langs->trans('View');
4601  }
4602 
4603  $moreatt = ($float ? 'style="float: right" ' : '').$other;
4604 
4605  return img_picto($titlealt, 'eye', $moreatt);
4606 }
4607 
4616 function img_delete($titlealt = 'default', $other = 'class="pictodelete"', $morecss = '')
4617 {
4618  global $langs;
4619 
4620  if ($titlealt == 'default') {
4621  $titlealt = $langs->trans('Delete');
4622  }
4623 
4624  return img_picto($titlealt, 'delete.png', $other, false, 0, 0, '', $morecss);
4625 }
4626 
4634 function img_printer($titlealt = "default", $other = '')
4635 {
4636  global $langs;
4637  if ($titlealt == "default") {
4638  $titlealt = $langs->trans("Print");
4639  }
4640  return img_picto($titlealt, 'printer.png', $other);
4641 }
4642 
4650 function img_split($titlealt = 'default', $other = 'class="pictosplit"')
4651 {
4652  global $langs;
4653 
4654  if ($titlealt == 'default') {
4655  $titlealt = $langs->trans('Split');
4656  }
4657 
4658  return img_picto($titlealt, 'split.png', $other);
4659 }
4660 
4668 function img_help($usehelpcursor = 1, $usealttitle = 1)
4669 {
4670  global $langs;
4671 
4672  if ($usealttitle) {
4673  if (is_string($usealttitle)) {
4674  $usealttitle = dol_escape_htmltag($usealttitle);
4675  } else {
4676  $usealttitle = $langs->trans('Info');
4677  }
4678  }
4679 
4680  return img_picto($usealttitle, 'info.png', 'style="vertical-align: middle;'.($usehelpcursor == 1 ? ' cursor: help' : ($usehelpcursor == 2 ? ' cursor: pointer' : '')).'"');
4681 }
4682 
4689 function img_info($titlealt = 'default')
4690 {
4691  global $langs;
4692 
4693  if ($titlealt == 'default') {
4694  $titlealt = $langs->trans('Informations');
4695  }
4696 
4697  return img_picto($titlealt, 'info.png', 'style="vertical-align: middle;"');
4698 }
4699 
4708 function img_warning($titlealt = 'default', $moreatt = '', $morecss = 'pictowarning')
4709 {
4710  global $langs;
4711 
4712  if ($titlealt == 'default') {
4713  $titlealt = $langs->trans('Warning');
4714  }
4715 
4716  //return '<div class="imglatecoin">'.img_picto($titlealt, 'warning_white.png', 'class="pictowarning valignmiddle"'.($moreatt ? ($moreatt == '1' ? ' style="float: right"' : ' '.$moreatt): '')).'</div>';
4717  return img_picto($titlealt, 'warning.png', 'class="'.$morecss.'"'.($moreatt ? ($moreatt == '1' ? ' style="float: right"' : ' '.$moreatt) : ''));
4718 }
4719 
4726 function img_error($titlealt = 'default')
4727 {
4728  global $langs;
4729 
4730  if ($titlealt == 'default') {
4731  $titlealt = $langs->trans('Error');
4732  }
4733 
4734  return img_picto($titlealt, 'error.png');
4735 }
4736 
4744 function img_next($titlealt = 'default', $moreatt = '')
4745 {
4746  global $langs;
4747 
4748  if ($titlealt == 'default') {
4749  $titlealt = $langs->trans('Next');
4750  }
4751 
4752  //return img_picto($titlealt, 'next.png', $moreatt);
4753  return '<span class="fa fa-chevron-right paddingright paddingleft" title="'.dol_escape_htmltag($titlealt).'"></span>';
4754 }
4755 
4763 function img_previous($titlealt = 'default', $moreatt = '')
4764 {
4765  global $langs;
4766 
4767  if ($titlealt == 'default') {
4768  $titlealt = $langs->trans('Previous');
4769  }
4770 
4771  //return img_picto($titlealt, 'previous.png', $moreatt);
4772  return '<span class="fa fa-chevron-left paddingright paddingleft" title="'.dol_escape_htmltag($titlealt).'"></span>';
4773 }
4774 
4783 function img_down($titlealt = 'default', $selected = 0, $moreclass = '')
4784 {
4785  global $langs;
4786 
4787  if ($titlealt == 'default') {
4788  $titlealt = $langs->trans('Down');
4789  }
4790 
4791  return img_picto($titlealt, ($selected ? '1downarrow_selected.png' : '1downarrow.png'), 'class="imgdown'.($moreclass ? " ".$moreclass : "").'"');
4792 }
4793 
4802 function img_up($titlealt = 'default', $selected = 0, $moreclass = '')
4803 {
4804  global $langs;
4805 
4806  if ($titlealt == 'default') {
4807  $titlealt = $langs->trans('Up');
4808  }
4809 
4810  return img_picto($titlealt, ($selected ? '1uparrow_selected.png' : '1uparrow.png'), 'class="imgup'.($moreclass ? " ".$moreclass : "").'"');
4811 }
4812 
4821 function img_left($titlealt = 'default', $selected = 0, $moreatt = '')
4822 {
4823  global $langs;
4824 
4825  if ($titlealt == 'default') {
4826  $titlealt = $langs->trans('Left');
4827  }
4828 
4829  return img_picto($titlealt, ($selected ? '1leftarrow_selected.png' : '1leftarrow.png'), $moreatt);
4830 }
4831 
4840 function img_right($titlealt = 'default', $selected = 0, $moreatt = '')
4841 {
4842  global $langs;
4843 
4844  if ($titlealt == 'default') {
4845  $titlealt = $langs->trans('Right');
4846  }
4847 
4848  return img_picto($titlealt, ($selected ? '1rightarrow_selected.png' : '1rightarrow.png'), $moreatt);
4849 }
4850 
4858 function img_allow($allow, $titlealt = 'default')
4859 {
4860  global $langs;
4861 
4862  if ($titlealt == 'default') {
4863  $titlealt = $langs->trans('Active');
4864  }
4865 
4866  if ($allow == 1) {
4867  return img_picto($titlealt, 'tick.png');
4868  }
4869 
4870  return '-';
4871 }
4872 
4880 function img_credit_card($brand, $morecss = null)
4881 {
4882  if (is_null($morecss)) {
4883  $morecss = 'fa-2x';
4884  }
4885 
4886  if ($brand == 'visa' || $brand == 'Visa') {
4887  $brand = 'cc-visa';
4888  } elseif ($brand == 'mastercard' || $brand == 'MasterCard') {
4889  $brand = 'cc-mastercard';
4890  } elseif ($brand == 'amex' || $brand == 'American Express') {
4891  $brand = 'cc-amex';
4892  } elseif ($brand == 'discover' || $brand == 'Discover') {
4893  $brand = 'cc-discover';
4894  } elseif ($brand == 'jcb' || $brand == 'JCB') {
4895  $brand = 'cc-jcb';
4896  } elseif ($brand == 'diners' || $brand == 'Diners club') {
4897  $brand = 'cc-diners-club';
4898  } elseif (!in_array($brand, array('cc-visa', 'cc-mastercard', 'cc-amex', 'cc-discover', 'cc-jcb', 'cc-diners-club'))) {
4899  $brand = 'credit-card';
4900  }
4901 
4902  return '<span class="fa fa-'.$brand.' fa-fw'.($morecss ? ' '.$morecss : '').'"></span>';
4903 }
4904 
4913 function img_mime($file, $titlealt = '', $morecss = '')
4914 {
4915  require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
4916 
4917  $mimetype = dol_mimetype($file, '', 1);
4918  $mimeimg = dol_mimetype($file, '', 2);
4919  $mimefa = dol_mimetype($file, '', 4);
4920 
4921  if (empty($titlealt)) {
4922  $titlealt = 'Mime type: '.$mimetype;
4923  }
4924 
4925  //return img_picto_common($titlealt, 'mime/'.$mimeimg, 'class="'.$morecss.'"');
4926  return '<i class="fa fa-'.$mimefa.' paddingright'.($morecss ? ' '.$morecss : '').'"'.($titlealt ? ' title="'.$titlealt.'"' : '').'></i>';
4927 }
4928 
4929 
4937 function img_search($titlealt = 'default', $other = '')
4938 {
4939  global $conf, $langs;
4940 
4941  if ($titlealt == 'default') {
4942  $titlealt = $langs->trans('Search');
4943  }
4944 
4945  $img = img_picto($titlealt, 'search.png', $other, false, 1);
4946 
4947  $input = '<input type="image" class="liste_titre" name="button_search" src="'.$img.'" ';
4948  $input .= 'value="'.dol_escape_htmltag($titlealt).'" title="'.dol_escape_htmltag($titlealt).'" >';
4949 
4950  return $input;
4951 }
4952 
4960 function img_searchclear($titlealt = 'default', $other = '')
4961 {
4962  global $conf, $langs;
4963 
4964  if ($titlealt == 'default') {
4965  $titlealt = $langs->trans('Search');
4966  }
4967 
4968  $img = img_picto($titlealt, 'searchclear.png', $other, false, 1);
4969 
4970  $input = '<input type="image" class="liste_titre" name="button_removefilter" src="'.$img.'" ';
4971  $input .= 'value="'.dol_escape_htmltag($titlealt).'" title="'.dol_escape_htmltag($titlealt).'" >';
4972 
4973  return $input;
4974 }
4975 
4987 function info_admin($text, $infoonimgalt = 0, $nodiv = 0, $admin = '1', $morecss = 'hideonsmartphone', $textfordropdown = '')
4988 {
4989  global $conf, $langs;
4990 
4991  if ($infoonimgalt) {
4992  $result = img_picto($text, 'info', 'class="'.($morecss ? ' '.$morecss : '').'"');
4993  } else {
4994  if (empty($conf->use_javascript_ajax)) {
4995  $textfordropdown = '';
4996  }
4997 
4998  $class = (empty($admin) ? 'undefined' : ($admin == '1' ? 'info' : $admin));
4999  $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>');
5000 
5001  if ($textfordropdown) {
5002  $tmpresult = '<span class="'.$class.'text opacitymedium cursorpointer">'.$langs->trans($textfordropdown).' '.img_picto($langs->trans($textfordropdown), '1downarrow').'</span>';
5003  $tmpresult .= '<script nonce="'.getNonce().'" type="text/javascript">
5004  jQuery(document).ready(function() {
5005  jQuery(".'.$class.'text").click(function() {
5006  console.log("toggle text");
5007  jQuery(".'.$class.'").toggle();
5008  });
5009  });
5010  </script>';
5011 
5012  $result = $tmpresult.$result;
5013  }
5014  }
5015 
5016  return $result;
5017 }
5018 
5019 
5031 function dol_print_error($db = '', $error = '', $errors = null)
5032 {
5033  global $conf, $langs, $argv;
5034  global $dolibarr_main_prod;
5035 
5036  $out = '';
5037  $syslog = '';
5038 
5039  // If error occurs before the $lang object was loaded
5040  if (!$langs) {
5041  require_once DOL_DOCUMENT_ROOT.'/core/class/translate.class.php';
5042  $langs = new Translate('', $conf);
5043  $langs->load("main");
5044  }
5045 
5046  // Load translation files required by the error messages
5047  $langs->loadLangs(array('main', 'errors'));
5048 
5049  if ($_SERVER['DOCUMENT_ROOT']) { // Mode web
5050  $out .= $langs->trans("DolibarrHasDetectedError").".<br>\n";
5051  if (getDolGlobalInt('MAIN_FEATURES_LEVEL') > 0) {
5052  $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";
5053  }
5054  $out .= $langs->trans("InformationToHelpDiagnose").":<br>\n";
5055 
5056  $out .= "<b>".$langs->trans("Date").":</b> ".dol_print_date(time(), 'dayhourlog')."<br>\n";
5057  $out .= "<b>".$langs->trans("Dolibarr").":</b> ".DOL_VERSION." - https://www.dolibarr.org<br>\n";
5058  if (isset($conf->global->MAIN_FEATURES_LEVEL)) {
5059  $out .= "<b>".$langs->trans("LevelOfFeature").":</b> ".getDolGlobalInt('MAIN_FEATURES_LEVEL')."<br>\n";
5060  }
5061  if (function_exists("phpversion")) {
5062  $out .= "<b>".$langs->trans("PHP").":</b> ".phpversion()."<br>\n";
5063  }
5064  $out .= "<b>".$langs->trans("Server").":</b> ".(isset($_SERVER["SERVER_SOFTWARE"]) ? dol_htmlentities($_SERVER["SERVER_SOFTWARE"], ENT_COMPAT) : '')."<br>\n";
5065  if (function_exists("php_uname")) {
5066  $out .= "<b>".$langs->trans("OS").":</b> ".php_uname()."<br>\n";
5067  }
5068  $out .= "<b>".$langs->trans("UserAgent").":</b> ".(isset($_SERVER["HTTP_USER_AGENT"]) ? dol_htmlentities($_SERVER["HTTP_USER_AGENT"], ENT_COMPAT) : '')."<br>\n";
5069  $out .= "<br>\n";
5070  $out .= "<b>".$langs->trans("RequestedUrl").":</b> ".dol_htmlentities($_SERVER["REQUEST_URI"], ENT_COMPAT)."<br>\n";
5071  $out .= "<b>".$langs->trans("Referer").":</b> ".(isset($_SERVER["HTTP_REFERER"]) ? dol_htmlentities($_SERVER["HTTP_REFERER"], ENT_COMPAT) : '')."<br>\n";
5072  $out .= "<b>".$langs->trans("MenuManager").":</b> ".(isset($conf->standard_menu) ? dol_htmlentities($conf->standard_menu, ENT_COMPAT) : '')."<br>\n";
5073  $out .= "<br>\n";
5074  $syslog .= "url=".dol_escape_htmltag($_SERVER["REQUEST_URI"]);
5075  $syslog .= ", query_string=".dol_escape_htmltag($_SERVER["QUERY_STRING"]);
5076  } else { // Mode CLI
5077  $out .= '> '.$langs->transnoentities("ErrorInternalErrorDetected").":\n".$argv[0]."\n";
5078  $syslog .= "pid=".dol_getmypid();
5079  }
5080 
5081  if (!empty($conf->modules)) {
5082  $out .= "<b>".$langs->trans("Modules").":</b> ".join(', ', $conf->modules)."<br>\n";
5083  }
5084 
5085  if (is_object($db)) {
5086  if ($_SERVER['DOCUMENT_ROOT']) { // Mode web
5087  $out .= "<b>".$langs->trans("DatabaseTypeManager").":</b> ".$db->type."<br>\n";
5088  $lastqueryerror = $db->lastqueryerror();
5089  if (!utf8_check($lastqueryerror)) {
5090  $lastqueryerror = "SQL error string is not a valid UTF8 string. We can't show it.";
5091  }
5092  $out .= "<b>".$langs->trans("RequestLastAccessInError").":</b> ".($lastqueryerror ? dol_escape_htmltag($lastqueryerror) : $langs->trans("ErrorNoRequestInError"))."<br>\n";
5093  $out .= "<b>".$langs->trans("ReturnCodeLastAccessInError").":</b> ".($db->lasterrno() ? dol_escape_htmltag($db->lasterrno()) : $langs->trans("ErrorNoRequestInError"))."<br>\n";
5094  $out .= "<b>".$langs->trans("InformationLastAccessInError").":</b> ".($db->lasterror() ? dol_escape_htmltag($db->lasterror()) : $langs->trans("ErrorNoRequestInError"))."<br>\n";
5095  $out .= "<br>\n";
5096  } else { // Mode CLI
5097  // No dol_escape_htmltag for output, we are in CLI mode
5098  $out .= '> '.$langs->transnoentities("DatabaseTypeManager").":\n".$db->type."\n";
5099  $out .= '> '.$langs->transnoentities("RequestLastAccessInError").":\n".($db->lastqueryerror() ? $db->lastqueryerror() : $langs->transnoentities("ErrorNoRequestInError"))."\n";
5100  $out .= '> '.$langs->transnoentities("ReturnCodeLastAccessInError").":\n".($db->lasterrno() ? $db->lasterrno() : $langs->transnoentities("ErrorNoRequestInError"))."\n";
5101  $out .= '> '.$langs->transnoentities("InformationLastAccessInError").":\n".($db->lasterror() ? $db->lasterror() : $langs->transnoentities("ErrorNoRequestInError"))."\n";
5102  }
5103  $syslog .= ", sql=".$db->lastquery();
5104  $syslog .= ", db_error=".$db->lasterror();
5105  }
5106 
5107  if ($error || $errors) {
5108  $langs->load("errors");
5109 
5110  // Merge all into $errors array
5111  if (is_array($error) && is_array($errors)) {
5112  $errors = array_merge($error, $errors);
5113  } elseif (is_array($error)) {
5114  $errors = $error;
5115  } elseif (is_array($errors)) {
5116  $errors = array_merge(array($error), $errors);
5117  } else {
5118  $errors = array_merge(array($error), array($errors));
5119  }
5120 
5121  foreach ($errors as $msg) {
5122  if (empty($msg)) {
5123  continue;
5124  }
5125  if ($_SERVER['DOCUMENT_ROOT']) { // Mode web
5126  $out .= "<b>".$langs->trans("Message").":</b> ".dol_escape_htmltag($msg)."<br>\n";
5127  } else // Mode CLI
5128  {
5129  $out .= '> '.$langs->transnoentities("Message").":\n".$msg."\n";
5130  }
5131  $syslog .= ", msg=".$msg;
5132  }
5133  }
5134  if (empty($dolibarr_main_prod) && $_SERVER['DOCUMENT_ROOT'] && function_exists('xdebug_print_function_stack') && function_exists('xdebug_call_file')) {
5135  xdebug_print_function_stack();
5136  $out .= '<b>XDebug informations:</b>'."<br>\n";
5137  $out .= 'File: '.xdebug_call_file()."<br>\n";
5138  $out .= 'Line: '.xdebug_call_line()."<br>\n";
5139  $out .= 'Function: '.xdebug_call_function()."<br>\n";
5140  $out .= "<br>\n";
5141  }
5142 
5143  // Return a http header with error code if possible
5144  if (!headers_sent()) {
5145  if (function_exists('top_httphead')) { // In CLI context, the method does not exists
5146  top_httphead();
5147  }
5148  http_response_code(500);
5149  }
5150 
5151  if (empty($dolibarr_main_prod)) {
5152  print $out;
5153  } else {
5154  if (empty($langs->defaultlang)) {
5155  $langs->setDefaultLang();
5156  }
5157  $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.
5158  // This should not happen, except if there is a bug somewhere. Enabled and check log in such case.
5159  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";
5160  print $langs->trans("DolibarrHasDetectedError").'. ';
5161  print $langs->trans("YouCanSetOptionDolibarrMainProdToZero");
5162  define("MAIN_CORE_ERROR", 1);
5163  }
5164 
5165  dol_syslog("Error ".$syslog, LOG_ERR);
5166 }
5167 
5178 function dol_print_error_email($prefixcode, $errormessage = '', $errormessages = array(), $morecss = 'error', $email = '')
5179 {
5180  global $langs, $conf;
5181 
5182  if (empty($email)) {
5183  $email = $conf->global->MAIN_INFO_SOCIETE_MAIL;
5184  }
5185 
5186  $langs->load("errors");
5187  $now = dol_now();
5188 
5189  print '<br><div class="center login_main_message"><div class="'.$morecss.'">';
5190  print $langs->trans("ErrorContactEMail", $email, $prefixcode.dol_print_date($now, '%Y%m%d%H%M%S'));
5191  if ($errormessage) {
5192  print '<br><br>'.$errormessage;
5193  }
5194  if (is_array($errormessages) && count($errormessages)) {
5195  foreach ($errormessages as $mesgtoshow) {
5196  print '<br><br>'.$mesgtoshow;
5197  }
5198  }
5199  print '</div></div>';
5200 }
5201 
5218 function print_liste_field_titre($name, $file = "", $field = "", $begin = "", $moreparam = "", $moreattrib = "", $sortfield = "", $sortorder = "", $prefix = "", $tooltip = "", $forcenowrapcolumntitle = 0)
5219 {
5220  print getTitleFieldOfList($name, 0, $file, $field, $begin, $moreparam, $moreattrib, $sortfield, $sortorder, $prefix, 0, $tooltip, $forcenowrapcolumntitle);
5221 }
5222 
5241 function getTitleFieldOfList($name, $thead = 0, $file = "", $field = "", $begin = "", $moreparam = "", $moreattrib = "", $sortfield = "", $sortorder = "", $prefix = "", $disablesortlink = 0, $tooltip = '', $forcenowrapcolumntitle = 0)
5242 {
5243  global $conf, $langs, $form;
5244  //print "$name, $file, $field, $begin, $options, $moreattrib, $sortfield, $sortorder<br>\n";
5245 
5246  if ($moreattrib == 'class="right"') {
5247  $prefix .= 'right '; // For backward compatibility
5248  }
5249 
5250  $sortorder = strtoupper($sortorder);
5251  $out = '';
5252  $sortimg = '';
5253 
5254  $tag = 'th';
5255  if ($thead == 2) {
5256  $tag = 'div';
5257  }
5258 
5259  $tmpsortfield = explode(',', $sortfield);
5260  $sortfield1 = trim($tmpsortfield[0]); // If $sortfield is 'd.datep,d.id', it becomes 'd.datep'
5261  $tmpfield = explode(',', $field);
5262  $field1 = trim($tmpfield[0]); // If $field is 'd.datep,d.id', it becomes 'd.datep'
5263 
5264  if (empty($conf->global->MAIN_DISABLE_WRAPPING_ON_COLUMN_TITLE) && empty($forcenowrapcolumntitle)) {
5265  $prefix = 'wrapcolumntitle '.$prefix;
5266  }
5267 
5268  //var_dump('field='.$field.' field1='.$field1.' sortfield='.$sortfield.' sortfield1='.$sortfield1);
5269  // If field is used as sort criteria we use a specific css class liste_titre_sel
5270  // Example if (sortfield,field)=("nom","xxx.nom") or (sortfield,field)=("nom","nom")
5271  $liste_titre = 'liste_titre';
5272  if ($field1 && ($sortfield1 == $field1 || $sortfield1 == preg_replace("/^[^\.]+\./", "", $field1))) {
5273  $liste_titre = 'liste_titre_sel';
5274  }
5275 
5276  $tagstart = '<'.$tag.' class="'.$prefix.$liste_titre.'" '.$moreattrib;
5277  //$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)).'"' : '');
5278  $tagstart .= ($name && empty($conf->global->MAIN_DISABLE_WRAPPING_ON_COLUMN_TITLE) && empty($forcenowrapcolumntitle) && !dol_textishtml($name)) ? ' title="'.dol_escape_htmltag($langs->trans($name)).'"' : '';
5279  $tagstart .= '>';
5280 
5281  if (empty($thead) && $field && empty($disablesortlink)) { // If this is a sort field
5282  $options = preg_replace('/sortfield=([a-zA-Z0-9,\s\.]+)/i', '', (is_scalar($moreparam) ? $moreparam : ''));
5283  $options = preg_replace('/sortorder=([a-zA-Z0-9,\s\.]+)/i', '', $options);
5284  $options = preg_replace('/&+/i', '&', $options);
5285  if (!preg_match('/^&/', $options)) {
5286  $options = '&'.$options;
5287  }
5288 
5289  $sortordertouseinlink = '';
5290  if ($field1 != $sortfield1) { // We are on another field than current sorted field
5291  if (preg_match('/^DESC/i', $sortorder)) {
5292  $sortordertouseinlink .= str_repeat('desc,', count(explode(',', $field)));
5293  } else { // We reverse the var $sortordertouseinlink
5294  $sortordertouseinlink .= str_repeat('asc,', count(explode(',', $field)));
5295  }
5296  } else { // We are on field that is the first current sorting criteria
5297  if (preg_match('/^ASC/i', $sortorder)) { // We reverse the var $sortordertouseinlink
5298  $sortordertouseinlink .= str_repeat('desc,', count(explode(',', $field)));
5299  } else {
5300  $sortordertouseinlink .= str_repeat('asc,', count(explode(',', $field)));
5301  }
5302  }
5303  $sortordertouseinlink = preg_replace('/,$/', '', $sortordertouseinlink);
5304  $out .= '<a class="reposition" href="'.$file.'?sortfield='.$field.'&sortorder='.$sortordertouseinlink.'&begin='.$begin.$options.'"';
5305  //$out .= (empty($conf->global->MAIN_DISABLE_WRAPPING_ON_COLUMN_TITLE) ? ' title="'.dol_escape_htmltag($langs->trans($name)).'"' : '');
5306  $out .= '>';
5307  }
5308  if ($tooltip) {
5309  // You can also use 'TranslationString:keyfortooltiponclick' for a tooltip on click.
5310  if (preg_match('/:\w+$/', $tooltip)) {
5311  $tmptooltip = explode(':', $tooltip);
5312  } else {
5313  $tmptooltip = array($tooltip);
5314  }
5315  $out .= $form->textwithpicto($langs->trans($name), $langs->trans($tmptooltip[0]), 1, 'help', '', 0, 3, (empty($tmptooltip[1]) ? '' : 'extra_'.str_replace('.', '_', $field).'_'.$tmptooltip[1]));
5316  } else {
5317  $out .= $langs->trans($name);
5318  }
5319 
5320  if (empty($thead) && $field && empty($disablesortlink)) { // If this is a sort field
5321  $out .= '</a>';
5322  }
5323 
5324  if (empty($thead) && $field) { // If this is a sort field
5325  $options = preg_replace('/sortfield=([a-zA-Z0-9,\s\.]+)/i', '', (is_scalar($moreparam) ? $moreparam : ''));
5326  $options = preg_replace('/sortorder=([a-zA-Z0-9,\s\.]+)/i', '', $options);
5327  $options = preg_replace('/&+/i', '&', $options);
5328  if (!preg_match('/^&/', $options)) {
5329  $options = '&'.$options;
5330  }
5331 
5332  if (!$sortorder || $field1 != $sortfield1) {
5333  //$out.= '<a href="'.$file.'?sortfield='.$field.'&sortorder=asc&begin='.$begin.$options.'">'.img_down("A-Z",0).'</a>';
5334  //$out.= '<a href="'.$file.'?sortfield='.$field.'&sortorder=desc&begin='.$begin.$options.'">'.img_up("Z-A",0).'</a>';
5335  } else {
5336  if (preg_match('/^DESC/', $sortorder)) {
5337  //$out.= '<a href="'.$file.'?sortfield='.$field.'&sortorder=asc&begin='.$begin.$options.'">'.img_down("A-Z",0).'</a>';
5338  //$out.= '<a href="'.$file.'?sortfield='.$field.'&sortorder=desc&begin='.$begin.$options.'">'.img_up("Z-A",1).'</a>';
5339  $sortimg .= '<span class="nowrap">'.img_up("Z-A", 0, 'paddingright').'</span>';
5340  }
5341  if (preg_match('/^ASC/', $sortorder)) {
5342  //$out.= '<a href="'.$file.'?sortfield='.$field.'&sortorder=asc&begin='.$begin.$options.'">'.img_down("A-Z",1).'</a>';
5343  //$out.= '<a href="'.$file.'?sortfield='.$field.'&sortorder=desc&begin='.$begin.$options.'">'.img_up("Z-A",0).'</a>';
5344  $sortimg .= '<span class="nowrap">'.img_down("A-Z", 0, 'paddingright').'</span>';
5345  }
5346  }
5347  }
5348 
5349  $tagend = '</'.$tag.'>';
5350 
5351  $out = $tagstart.$sortimg.$out.$tagend;
5352 
5353  return $out;
5354 }
5355 
5364 function print_titre($title)
5365 {
5366  dol_syslog(__FUNCTION__." is deprecated", LOG_WARNING);
5367 
5368  print '<div class="titre">'.$title.'</div>';
5369 }
5370 
5382 function print_fiche_titre($title, $mesg = '', $picto = 'generic', $pictoisfullpath = 0, $id = '')
5383 {
5384  print load_fiche_titre($title, $mesg, $picto, $pictoisfullpath, $id);
5385 }
5386 
5400 function load_fiche_titre($titre, $morehtmlright = '', $picto = 'generic', $pictoisfullpath = 0, $id = '', $morecssontable = '', $morehtmlcenter = '')
5401 {
5402  global $conf;
5403 
5404  $return = '';
5405 
5406  if ($picto == 'setup') {
5407  $picto = 'generic';
5408  }
5409 
5410  $return .= "\n";
5411  $return .= '<table '.($id ? 'id="'.$id.'" ' : '').'class="centpercent notopnoleftnoright table-fiche-title'.($morecssontable ? ' '.$morecssontable : '').'">'; // maring bottom must be same than into print_barre_list
5412  $return .= '<tr class="titre">';
5413  if ($picto) {
5414  $return .= '<td class="nobordernopadding widthpictotitle valignmiddle col-picto">'.img_picto('', $picto, 'class="valignmiddle widthpictotitle pictotitle"', $pictoisfullpath).'</td>';
5415  }
5416  $return .= '<td class="nobordernopadding valignmiddle col-title">';
5417  $return .= '<div class="titre inline-block">'.$titre.'</div>';
5418  $return .= '</td>';
5419  if (dol_strlen($morehtmlcenter)) {
5420  $return .= '<td class="nobordernopadding center valignmiddle col-center">'.$morehtmlcenter.'</td>';
5421  }
5422  if (dol_strlen($morehtmlright)) {
5423  $return .= '<td class="nobordernopadding titre_right wordbreakimp right valignmiddle col-right">'.$morehtmlright.'</td>';
5424  }
5425  $return .= '</tr></table>'."\n";
5426 
5427  return $return;
5428 }
5429 
5453 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 = '')
5454 {
5455  global $conf, $langs;
5456 
5457  $savlimit = $limit;
5458  $savtotalnboflines = $totalnboflines;
5459  $totalnboflines = abs((int) $totalnboflines);
5460 
5461  if ($picto == 'setup') {
5462  $picto = 'title_setup.png';
5463  }
5464  if (($conf->browser->name == 'ie') && $picto == 'generic') {
5465  $picto = 'title.gif';
5466  }
5467  if ($limit < 0) {
5468  $limit = $conf->liste_limit;
5469  }
5470 
5471  if ($savlimit != 0 && (($num > $limit) || ($num == -1) || ($limit == 0))) {
5472  $nextpage = 1;
5473  } else {
5474  $nextpage = 0;
5475  }
5476  //print 'totalnboflines='.$totalnboflines.'-savlimit='.$savlimit.'-limit='.$limit.'-num='.$num.'-nextpage='.$nextpage.'-hideselectlimit='.$hideselectlimit.'-hidenavigation='.$hidenavigation;
5477 
5478  print "\n";
5479  print "<!-- Begin title -->\n";
5480  print '<table class="centpercent notopnoleftnoright table-fiche-title'.($morecss ? ' '.$morecss : '').'"><tr>'; // maring bottom must be same than into load_fiche_tire
5481 
5482  // Left
5483 
5484  if ($picto && $titre) {
5485  print '<td class="nobordernopadding widthpictotitle valignmiddle col-picto">'.img_picto('', $picto, 'class="valignmiddle pictotitle widthpictotitle"', $pictoisfullpath).'</td>';
5486  }
5487 
5488  print '<td class="nobordernopadding valignmiddle col-title">';
5489  print '<div class="titre inline-block">'.$titre;
5490  if (!empty($titre) && $savtotalnboflines >= 0 && (string) $savtotalnboflines != '') {
5491  print '<span class="opacitymedium colorblack paddingleft">('.$totalnboflines.')</span>';
5492  }
5493  print '</div></td>';
5494 
5495  // Center
5496  if ($morehtmlcenter && empty($conf->dol_optimize_smallscreen)) {
5497  print '<td class="nobordernopadding center valignmiddle col-center">'.$morehtmlcenter.'</td>';
5498  }
5499 
5500  // Right
5501  print '<td class="nobordernopadding valignmiddle right col-right">';
5502  print '<input type="hidden" name="pageplusoneold" value="'.((int) $page + 1).'">';
5503  if ($sortfield) {
5504  $options .= "&sortfield=".urlencode($sortfield);
5505  }
5506  if ($sortorder) {
5507  $options .= "&sortorder=".urlencode($sortorder);
5508  }
5509  // Show navigation bar
5510  $pagelist = '';
5511  if ($savlimit != 0 && ((int) $page > 0 || $num > $limit)) {
5512  if ($totalnboflines) { // If we know total nb of lines
5513  // Define nb of extra page links before and after selected page + ... + first or last
5514  $maxnbofpage = (empty($conf->dol_optimize_smallscreen) ? 4 : 0);
5515 
5516  if ($limit > 0) {
5517  $nbpages = ceil($totalnboflines / $limit);
5518  } else {
5519  $nbpages = 1;
5520  }
5521  $cpt = ($page - $maxnbofpage);
5522  if ($cpt < 0) {
5523  $cpt = 0;
5524  }
5525 
5526  if ($cpt >= 1) {
5527  if (empty($pagenavastextinput)) {
5528  $pagelist .= '<li class="pagination"><a href="'.$file.'?page=0'.$options.'">1</a></li>';
5529  if ($cpt > 2) {
5530  $pagelist .= '<li class="pagination"><span class="inactive">...</span></li>';
5531  } elseif ($cpt == 2) {
5532  $pagelist .= '<li class="pagination"><a href="'.$file.'?page=1'.$options.'">2</a></li>';
5533  }
5534  }
5535  }
5536 
5537  do {
5538  if ($pagenavastextinput) {
5539  if ($cpt == $page) {
5540  $pagelist .= '<li class="pagination"><input type="text" class="width25 center pageplusone" name="pageplusone" value="'.($page + 1).'"></li>';
5541  $pagelist .= '/';
5542  }
5543  } else {
5544  if ($cpt == $page) {
5545  $pagelist .= '<li class="pagination"><span class="active">'.($page + 1).'</span></li>';
5546  } else {
5547  $pagelist .= '<li class="pagination"><a href="'.$file.'?page='.$cpt.$options.'">'.($cpt + 1).'</a></li>';
5548  }
5549  }
5550  $cpt++;
5551  } while ($cpt < $nbpages && $cpt <= ($page + $maxnbofpage));
5552 
5553  if (empty($pagenavastextinput)) {
5554  if ($cpt < $nbpages) {
5555  if ($cpt < $nbpages - 2) {
5556  $pagelist .= '<li class="pagination"><span class="inactive">...</span></li>';
5557  } elseif ($cpt == $nbpages - 2) {
5558  $pagelist .= '<li class="pagination"><a href="'.$file.'?page='.($nbpages - 2).$options.'">'.($nbpages - 1).'</a></li>';
5559  }
5560  $pagelist .= '<li class="pagination"><a href="'.$file.'?page='.($nbpages - 1).$options.'">'.$nbpages.'</a></li>';
5561  }
5562  } else {
5563  //var_dump($page.' '.$cpt.' '.$nbpages);
5564  $pagelist .= '<li class="pagination paginationlastpage"><a href="'.$file.'?page='.($nbpages - 1).$options.'">'.$nbpages.'</a></li>';
5565  }
5566  } else {
5567  $pagelist .= '<li class="pagination"><span class="active">'.($page + 1)."</li>";
5568  }
5569  }
5570 
5571  if ($savlimit || $morehtmlright || $morehtmlrightbeforearrow) {
5572  print_fleche_navigation((int) $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
5573  }
5574 
5575  // js to autoselect page field on focus
5576  if ($pagenavastextinput) {
5577  print ajax_autoselect('.pageplusone');
5578  }
5579 
5580  print '</td>';
5581  print '</tr>';
5582 
5583  print '</table>'."\n";
5584 
5585  // Center
5586  if ($morehtmlcenter && !empty($conf->dol_optimize_smallscreen)) {
5587  print '<div class="nobordernopadding marginbottomonly center valignmiddle col-center centpercent">'.$morehtmlcenter.'</div>';
5588  }
5589 
5590  print "<!-- End title -->\n\n";
5591 }
5592 
5609 function print_fleche_navigation($page, $file, $options = '', $nextpage = 0, $betweenarrows = '', $afterarrows = '', $limit = -1, $totalnboflines = 0, $hideselectlimit = 0, $beforearrows = '', $hidenavigation = 0)
5610 {
5611  global $conf, $langs;
5612 
5613  print '<div class="pagination"><ul>';
5614  if ($beforearrows) {
5615  print '<li class="paginationbeforearrows">';
5616  print $beforearrows;
5617  print '</li>';
5618  }
5619 
5620  if (empty($hidenavigation)) {
5621  if ((int) $limit > 0 && empty($hideselectlimit)) {
5622  $pagesizechoices = '10:10,15:15,20:20,30:30,40:40,50:50,100:100,250:250,500:500,1000:1000';
5623  $pagesizechoices .= ',5000:5000,10000:10000,20000:20000';
5624  //$pagesizechoices.=',0:'.$langs->trans("All"); // Not yet supported
5625  //$pagesizechoices.=',2:2';
5626  if (!empty($conf->global->MAIN_PAGESIZE_CHOICES)) {
5627  $pagesizechoices = $conf->global->MAIN_PAGESIZE_CHOICES;
5628  }
5629 
5630  print '<li class="pagination">';
5631  print '<select class="flat selectlimit" name="limit" title="'.dol_escape_htmltag($langs->trans("MaxNbOfRecordPerPage")).'">';
5632  $tmpchoice = explode(',', $pagesizechoices);
5633  $tmpkey = $limit.':'.$limit;
5634  if (!in_array($tmpkey, $tmpchoice)) {
5635  $tmpchoice[] = $tmpkey;
5636  }
5637  $tmpkey = $conf->liste_limit.':'.$conf->liste_limit;
5638  if (!in_array($tmpkey, $tmpchoice)) {
5639  $tmpchoice[] = $tmpkey;
5640  }
5641  asort($tmpchoice, SORT_NUMERIC);
5642  foreach ($tmpchoice as $val) {
5643  $selected = '';
5644  $tmp = explode(':', $val);
5645  $key = $tmp[0];
5646  $val = $tmp[1];
5647  if ($key != '' && $val != '') {
5648  if ((int) $key == (int) $limit) {
5649  $selected = ' selected="selected"';
5650  }
5651  print '<option name="'.$key.'"'.$selected.'>'.dol_escape_htmltag($val).'</option>'."\n";
5652  }
5653  }
5654  print '</select>';
5655  if ($conf->use_javascript_ajax) {
5656  print '<!-- JS CODE TO ENABLE select limit to launch submit of page -->
5657  <script>
5658  jQuery(document).ready(function () {
5659  jQuery(".selectlimit").change(function() {
5660  console.log("Change limit. Send submit");
5661  $(this).parents(\'form:first\').submit();
5662  });
5663  });
5664  </script>
5665  ';
5666  }
5667  print '</li>';
5668  }
5669  if ($page > 0) {
5670  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>';
5671  }
5672  if ($betweenarrows) {
5673  print '<!--<div class="betweenarrows nowraponall inline-block">-->';
5674  print $betweenarrows;
5675  print '<!--</div>-->';
5676  }
5677  if ($nextpage > 0) {
5678  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>';
5679  }
5680  if ($afterarrows) {
5681  print '<li class="paginationafterarrows">';
5682  print $afterarrows;
5683  print '</li>';
5684  }
5685  }
5686  print '</ul></div>'."\n";
5687 }
5688 
5689 
5701 function vatrate($rate, $addpercent = false, $info_bits = 0, $usestarfornpr = 0, $html = 0)
5702 {
5703  $morelabel = '';
5704 
5705  if (preg_match('/%/', $rate)) {
5706  $rate = str_replace('%', '', $rate);
5707  $addpercent = true;
5708  }
5709  $reg = array();
5710  if (preg_match('/\((.*)\)/', $rate, $reg)) {
5711  $morelabel = ' ('.$reg[1].')';
5712  $rate = preg_replace('/\s*'.preg_quote($morelabel, '/').'/', '', $rate);
5713  $morelabel = ' '.($html ? '<span class="opacitymedium">' : '').'('.$reg[1].')'.($html ? '</span>' : '');
5714  }
5715  if (preg_match('/\*/', $rate)) {
5716  $rate = str_replace('*', '', $rate);
5717  $info_bits |= 1;
5718  }
5719 
5720  // If rate is '9/9/9' we don't change it. If rate is '9.000' we apply price()
5721  if (!preg_match('/\//', $rate)) {
5722  $ret = price($rate, 0, '', 0, 0).($addpercent ? '%' : '');
5723  } else {
5724  // TODO Split on / and output with a price2num to have clean numbers without ton of 000.
5725  $ret = $rate.($addpercent ? '%' : '');
5726  }
5727  if (($info_bits & 1) && $usestarfornpr >= 0) {
5728  $ret .= ' *';
5729  }
5730  $ret .= $morelabel;
5731  return $ret;
5732 }
5733 
5734 
5750 function price($amount, $form = 0, $outlangs = '', $trunc = 1, $rounding = -1, $forcerounding = -1, $currency_code = '')
5751 {
5752  global $langs, $conf;
5753 
5754  // Clean parameters
5755  if (empty($amount)) {
5756  $amount = 0; // To have a numeric value if amount not defined or = ''
5757  }
5758  $amount = (is_numeric($amount) ? $amount : 0); // Check if amount is numeric, for example, an error occured when amount value = o (letter) instead 0 (number)
5759  if ($rounding == -1) {
5760  $rounding = min($conf->global->MAIN_MAX_DECIMALS_UNIT, $conf->global->MAIN_MAX_DECIMALS_TOT);
5761  }
5762  $nbdecimal = $rounding;
5763 
5764  if ($outlangs === 'none') {
5765  // Use international separators
5766  $dec = '.';
5767  $thousand = '';
5768  } else {
5769  // Output separators by default (french)
5770  $dec = ',';
5771  $thousand = ' ';
5772 
5773  // If $outlangs not forced, we use use language
5774  if (!is_object($outlangs)) {
5775  $outlangs = $langs;
5776  }
5777 
5778  if ($outlangs->transnoentitiesnoconv("SeparatorDecimal") != "SeparatorDecimal") {
5779  $dec = $outlangs->transnoentitiesnoconv("SeparatorDecimal");
5780  }
5781  if ($outlangs->transnoentitiesnoconv("SeparatorThousand") != "SeparatorThousand") {
5782  $thousand = $outlangs->transnoentitiesnoconv("SeparatorThousand");
5783  }
5784  if ($thousand == 'None') {
5785  $thousand = '';
5786  } elseif ($thousand == 'Space') {
5787  $thousand = ' ';
5788  }
5789  }
5790  //print "outlangs=".$outlangs->defaultlang." amount=".$amount." html=".$form." trunc=".$trunc." nbdecimal=".$nbdecimal." dec='".$dec."' thousand='".$thousand."'<br>";
5791 
5792  //print "amount=".$amount."-";
5793  $amount = str_replace(',', '.', $amount); // should be useless
5794  //print $amount."-";
5795  $datas = explode('.', $amount);
5796  $decpart = isset($datas[1]) ? $datas[1] : '';
5797  $decpart = preg_replace('/0+$/i', '', $decpart); // Supprime les 0 de fin de partie decimale
5798  //print "decpart=".$decpart."<br>";
5799  $end = '';
5800 
5801  // We increase nbdecimal if there is more decimal than asked (to not loose information)
5802  if (dol_strlen($decpart) > $nbdecimal) {
5803  $nbdecimal = dol_strlen($decpart);
5804  }
5805  // Si on depasse max
5806  if ($trunc && $nbdecimal > $conf->global->MAIN_MAX_DECIMALS_SHOWN) {
5807  $nbdecimal = $conf->global->MAIN_MAX_DECIMALS_SHOWN;
5808  if (preg_match('/\.\.\./i', $conf->global->MAIN_MAX_DECIMALS_SHOWN)) {
5809  // Si un affichage est tronque, on montre des ...
5810  $end = '...';
5811  }
5812  }
5813 
5814  // If force rounding
5815  if ((string) $forcerounding != '-1') {
5816  if ($forcerounding === 'MU') {
5817  $nbdecimal = $conf->global->MAIN_MAX_DECIMALS_UNIT;
5818  } elseif ($forcerounding === 'MT') {
5819  $nbdecimal = $conf->global->MAIN_MAX_DECIMALS_TOT;
5820  } elseif ($forcerounding >= 0) {
5821  $nbdecimal = $forcerounding;
5822  }
5823  }
5824 
5825  // Format number
5826  $output = number_format($amount, $nbdecimal, $dec, $thousand);
5827  if ($form) {
5828  $output = preg_replace('/\s/', '&nbsp;', $output);
5829  $output = preg_replace('/\'/', '&#039;', $output);
5830  }
5831  // Add symbol of currency if requested
5832  $cursymbolbefore = $cursymbolafter = '';
5833  if ($currency_code && is_object($outlangs)) {
5834  if ($currency_code == 'auto') {
5835  $currency_code = $conf->currency;
5836  }
5837 
5838  $listofcurrenciesbefore = array('AUD', 'CAD', 'CNY', 'COP', 'CLP', 'GBP', 'HKD', 'MXN', 'PEN', 'USD', 'CRC');
5839  $listoflanguagesbefore = array('nl_NL');
5840  if (in_array($currency_code, $listofcurrenciesbefore) || in_array($outlangs->defaultlang, $listoflanguagesbefore)) {
5841  $cursymbolbefore .= $outlangs->getCurrencySymbol($currency_code);
5842  } else {
5843  $tmpcur = $outlangs->getCurrencySymbol($currency_code);
5844  $cursymbolafter .= ($tmpcur == $currency_code ? ' '.$tmpcur : $tmpcur);
5845  }
5846  }
5847  $output = $cursymbolbefore.$output.$end.($cursymbolafter ? ' ' : '').$cursymbolafter;
5848 
5849  return $output;
5850 }
5851 
5876 function price2num($amount, $rounding = '', $option = 0)
5877 {
5878  global $langs, $conf;
5879 
5880  // Clean parameters
5881  if (is_null($amount)) {
5882  $amount = '';
5883  }
5884 
5885  // Round PHP function does not allow number like '1,234.56' nor '1.234,56' nor '1 234,56'
5886  // Numbers must be '1234.56'
5887  // Decimal delimiter for PHP and database SQL requests must be '.'
5888  $dec = ',';
5889  $thousand = ' ';
5890  if (is_null($langs)) { // $langs is not defined, we use english values.
5891  $dec = '.';
5892  $thousand = ',';
5893  } else {
5894  if ($langs->transnoentitiesnoconv("SeparatorDecimal") != "SeparatorDecimal") {
5895  $dec = $langs->transnoentitiesnoconv("SeparatorDecimal");
5896  }
5897  if ($langs->transnoentitiesnoconv("SeparatorThousand") != "SeparatorThousand") {
5898  $thousand = $langs->transnoentitiesnoconv("SeparatorThousand");
5899  }
5900  }
5901  if ($thousand == 'None') {
5902  $thousand = '';
5903  } elseif ($thousand == 'Space') {
5904  $thousand = ' ';
5905  }
5906  //print "amount=".$amount." html=".$form." trunc=".$trunc." nbdecimal=".$nbdecimal." dec='".$dec."' thousand='".$thousand."'<br>";
5907 
5908  // Convert value to universal number format (no thousand separator, '.' as decimal separator)
5909  if ($option != 1) { // If not a PHP number or unknown, we change or clean format
5910  //print "\n".'PP'.$amount.' - '.$dec.' - '.$thousand.' - '.intval($amount).'<br>';
5911  if (!is_numeric($amount)) {
5912  $amount = preg_replace('/[a-zA-Z\/\\\*\(\)<>\_]/', '', $amount);
5913  }
5914 
5915  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
5916  $amount = str_replace($thousand, '', $amount);
5917  }
5918 
5919  // Convert amount to format with dolibarr dec and thousand (this is because PHP convert a number
5920  // to format defined by LC_NUMERIC after a calculation and we want source format to be like defined by Dolibarr setup.
5921  // So if number was already a good number, it is converted into local Dolibarr setup.
5922  if (is_numeric($amount)) {
5923  // We put in temps value of decimal ("0.00001"). Works with 0 and 2.0E-5 and 9999.10
5924  $temps = sprintf("%0.10F", $amount - intval($amount)); // temps=0.0000000000 or 0.0000200000 or 9999.1000000000
5925  $temps = preg_replace('/([\.1-9])0+$/', '\\1', $temps); // temps=0. or 0.00002 or 9999.1
5926  $nbofdec = max(0, dol_strlen($temps) - 2); // -2 to remove "0."
5927  $amount = number_format($amount, $nbofdec, $dec, $thousand);
5928  }
5929  //print "QQ".$amount."<br>\n";
5930 
5931  // Now make replace (the main goal of function)
5932  if ($thousand != ',' && $thousand != '.') {
5933  $amount = str_replace(',', '.', $amount); // To accept 2 notations for french users
5934  }
5935 
5936  $amount = str_replace(' ', '', $amount); // To avoid spaces
5937  $amount = str_replace($thousand, '', $amount); // Replace of thousand before replace of dec to avoid pb if thousand is .
5938  $amount = str_replace($dec, '.', $amount);
5939 
5940  $amount = preg_replace('/[^0-9\-\.]/', '', $amount); // Clean non numeric chars (so it clean some UTF8 spaces for example.
5941  }
5942  //print ' XX'.$amount.' '.$rounding;
5943 
5944  // Now, $amount is a real PHP float number. We make a rounding if required.
5945  if ($rounding) {
5946  $nbofdectoround = '';
5947  if ($rounding == 'MU') {
5948  $nbofdectoround = $conf->global->MAIN_MAX_DECIMALS_UNIT;
5949  } elseif ($rounding == 'MT') {
5950  $nbofdectoround = $conf->global->MAIN_MAX_DECIMALS_TOT;
5951  } elseif ($rounding == 'MS') {
5952  $nbofdectoround = isset($conf->global->MAIN_MAX_DECIMALS_STOCK) ? $conf->global->MAIN_MAX_DECIMALS_STOCK : 5;
5953  } elseif ($rounding == 'CU') {
5954  $nbofdectoround = max($conf->global->MAIN_MAX_DECIMALS_UNIT, 8); // TODO Use param of currency
5955  } elseif ($rounding == 'CT') {
5956  $nbofdectoround = max($conf->global->MAIN_MAX_DECIMALS_TOT, 8); // TODO Use param of currency
5957  } elseif (is_numeric($rounding)) {
5958  $nbofdectoround = (int) $rounding;
5959  }
5960 
5961  //print " RR".$amount.' - '.$nbofdectoround.'<br>';
5962  if (dol_strlen($nbofdectoround)) {
5963  $amount = round(is_string($amount) ? (float) $amount : $amount, $nbofdectoround); // $nbofdectoround can be 0.
5964  } else {
5965  return 'ErrorBadParameterProvidedToFunction';
5966  }
5967  //print ' SS'.$amount.' - '.$nbofdec.' - '.$dec.' - '.$thousand.' - '.$nbofdectoround.'<br>';
5968 
5969  // Convert amount to format with dolibarr dec and thousand (this is because PHP convert a number
5970  // to format defined by LC_NUMERIC after a calculation and we want source format to be defined by Dolibarr setup.
5971  if (is_numeric($amount)) {
5972  // We put in temps value of decimal ("0.00001"). Works with 0 and 2.0E-5 and 9999.10
5973  $temps = sprintf("%0.10F", $amount - intval($amount)); // temps=0.0000000000 or 0.0000200000 or 9999.1000000000
5974  $temps = preg_replace('/([\.1-9])0+$/', '\\1', $temps); // temps=0. or 0.00002 or 9999.1
5975  $nbofdec = max(0, dol_strlen($temps) - 2); // -2 to remove "0."
5976  $amount = number_format($amount, min($nbofdec, $nbofdectoround), $dec, $thousand); // Convert amount to format with dolibarr dec and thousand
5977  }
5978  //print "TT".$amount.'<br>';
5979 
5980  // Always make replace because each math function (like round) replace
5981  // with local values and we want a number that has a SQL string format x.y
5982  if ($thousand != ',' && $thousand != '.') {
5983  $amount = str_replace(',', '.', $amount); // To accept 2 notations for french users
5984  }
5985 
5986  $amount = str_replace(' ', '', $amount); // To avoid spaces
5987  $amount = str_replace($thousand, '', $amount); // Replace of thousand before replace of dec to avoid pb if thousand is .
5988  $amount = str_replace($dec, '.', $amount);
5989 
5990  $amount = preg_replace('/[^0-9\-\.]/', '', $amount); // Clean non numeric chars (so it clean some UTF8 spaces for example.
5991  }
5992 
5993  return $amount;
5994 }
5995 
6008 function showDimensionInBestUnit($dimension, $unit, $type, $outputlangs, $round = -1, $forceunitoutput = 'no', $use_short_label = 0)
6009 {
6010  require_once DOL_DOCUMENT_ROOT.'/core/lib/product.lib.php';
6011 
6012  if (($forceunitoutput == 'no' && $dimension < 1 / 10000 && $unit < 90) || (is_numeric($forceunitoutput) && $forceunitoutput == -6)) {
6013  $dimension = $dimension * 1000000;
6014  $unit = $unit - 6;
6015  } elseif (($forceunitoutput == 'no' && $dimension < 1 / 10 && $unit < 90) || (is_numeric($forceunitoutput) && $forceunitoutput == -3)) {
6016  $dimension = $dimension * 1000;
6017  $unit = $unit - 3;
6018  } elseif (($forceunitoutput == 'no' && $dimension > 100000000 && $unit < 90) || (is_numeric($forceunitoutput) && $forceunitoutput == 6)) {
6019  $dimension = $dimension / 1000000;
6020  $unit = $unit + 6;
6021  } elseif (($forceunitoutput == 'no' && $dimension > 100000 && $unit < 90) || (is_numeric($forceunitoutput) && $forceunitoutput == 3)) {
6022  $dimension = $dimension / 1000;
6023  $unit = $unit + 3;
6024  }
6025  // Special case when we want output unit into pound or ounce
6026  /* TODO
6027  if ($unit < 90 && $type == 'weight' && is_numeric($forceunitoutput) && (($forceunitoutput == 98) || ($forceunitoutput == 99))
6028  {
6029  $dimension = // convert dimension from standard unit into ounce or pound
6030  $unit = $forceunitoutput;
6031  }
6032  if ($unit > 90 && $type == 'weight' && is_numeric($forceunitoutput) && $forceunitoutput < 90)
6033  {
6034  $dimension = // convert dimension from standard unit into ounce or pound
6035  $unit = $forceunitoutput;
6036  }*/
6037 
6038  $ret = price($dimension, 0, $outputlangs, 0, 0, $round);
6039  $ret .= ' '.measuringUnitString(0, $type, $unit, $use_short_label, $outputlangs);
6040 
6041  return $ret;
6042 }
6043 
6044 
6057 function get_localtax($vatrate, $local, $thirdparty_buyer = "", $thirdparty_seller = "", $vatnpr = 0)
6058 {
6059  global $db, $conf, $mysoc;
6060 
6061  if (empty($thirdparty_seller) || !is_object($thirdparty_seller)) {
6062  $thirdparty_seller = $mysoc;
6063  }
6064 
6065  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);
6066 
6067  $vatratecleaned = $vatrate;
6068  $reg = array();
6069  if (preg_match('/^(.*)\s*\((.*)\)$/', $vatrate, $reg)) { // If vat is "xx (yy)"
6070  $vatratecleaned = trim($reg[1]);
6071  $vatratecode = $reg[2];
6072  }
6073 
6074  /*if ($thirdparty_buyer->country_code != $thirdparty_seller->country_code)
6075  {
6076  return 0;
6077  }*/
6078 
6079  // Some test to guess with no need to make database access
6080  if ($mysoc->country_code == 'ES') { // For spain localtaxes 1 and 2, tax is qualified if buyer use local tax
6081  if ($local == 1) {
6082  if (!$mysoc->localtax1_assuj || (string) $vatratecleaned == "0") {
6083  return 0;
6084  }
6085  if ($thirdparty_seller->id == $mysoc->id) {
6086  if (!$thirdparty_buyer->localtax1_assuj) {
6087  return 0;
6088  }
6089  } else {
6090  if (!$thirdparty_seller->localtax1_assuj) {
6091  return 0;
6092  }
6093  }
6094  }
6095 
6096  if ($local == 2) {
6097  //if (! $mysoc->localtax2_assuj || (string) $vatratecleaned == "0") return 0;
6098  if (!$mysoc->localtax2_assuj) {
6099  return 0; // If main vat is 0, IRPF may be different than 0.
6100  }
6101  if ($thirdparty_seller->id == $mysoc->id) {
6102  if (!$thirdparty_buyer->localtax2_assuj) {
6103  return 0;
6104  }
6105  } else {
6106  if (!$thirdparty_seller->localtax2_assuj) {
6107  return 0;
6108  }
6109  }
6110  }
6111  } else {
6112  if ($local == 1 && !$thirdparty_seller->localtax1_assuj) {
6113  return 0;
6114  }
6115  if ($local == 2 && !$thirdparty_seller->localtax2_assuj) {
6116  return 0;
6117  }
6118  }
6119 
6120  // For some country MAIN_GET_LOCALTAXES_VALUES_FROM_THIRDPARTY is forced to on.
6121  if (in_array($mysoc->country_code, array('ES'))) {
6122  $conf->global->MAIN_GET_LOCALTAXES_VALUES_FROM_THIRDPARTY = 1;
6123  }
6124 
6125  // Search local taxes
6126  if (!empty($conf->global->MAIN_GET_LOCALTAXES_VALUES_FROM_THIRDPARTY)) {
6127  if ($local == 1) {
6128  if ($thirdparty_seller != $mysoc) {
6129  if (!isOnlyOneLocalTax($local)) { // TODO We should provide $vatrate to search on correct line and not always on line with highest vat rate
6130  return $thirdparty_seller->localtax1_value;
6131  }
6132  } else { // i am the seller
6133  if (!isOnlyOneLocalTax($local)) { // TODO If seller is me, why not always returning this, even if there is only one locatax vat.
6134  return $conf->global->MAIN_INFO_VALUE_LOCALTAX1;
6135  }
6136  }
6137  }
6138  if ($local == 2) {
6139  if ($thirdparty_seller != $mysoc) {
6140  if (!isOnlyOneLocalTax($local)) { // TODO We should provide $vatrate to search on correct line and not always on line with highest vat rate
6141  // TODO We should also return value defined on thirdparty only if defined
6142  return $thirdparty_seller->localtax2_value;
6143  }
6144  } else { // i am the seller
6145  if (in_array($mysoc->country_code, array('ES'))) {
6146  return $thirdparty_buyer->localtax2_value;
6147  } else {
6148  return $conf->global->MAIN_INFO_VALUE_LOCALTAX2;
6149  }
6150  }
6151  }
6152  }
6153 
6154  // By default, search value of local tax on line of common tax
6155  $sql = "SELECT t.localtax1, t.localtax2, t.localtax1_type, t.localtax2_type";
6156  $sql .= " FROM ".MAIN_DB_PREFIX."c_tva as t, ".MAIN_DB_PREFIX."c_country as c";
6157  $sql .= " WHERE t.fk_pays = c.rowid AND c.code = '".$db->escape($thirdparty_seller->country_code)."'";
6158  $sql .= " AND t.taux = ".((float) $vatratecleaned)." AND t.active = 1";
6159  if (!empty($vatratecode)) {
6160  $sql .= " AND t.code ='".$db->escape($vatratecode)."'"; // If we have the code, we use it in priority
6161  } else {
6162  $sql .= " AND t.recuperableonly = '".$db->escape($vatnpr)."'";
6163  }
6164 
6165  $resql = $db->query($sql);
6166 
6167  if ($resql) {
6168  $obj = $db->fetch_object($resql);
6169  if ($obj) {
6170  if ($local == 1) {
6171  return $obj->localtax1;
6172  } elseif ($local == 2) {
6173  return $obj->localtax2;
6174  }
6175  }
6176  }
6177 
6178  return 0;
6179 }
6180 
6181 
6190 function isOnlyOneLocalTax($local)
6191 {
6192  $tax = get_localtax_by_third($local);
6193 
6194  $valors = explode(":", $tax);
6195 
6196  if (count($valors) > 1) {
6197  return false;
6198  } else {
6199  return true;
6200  }
6201 }
6202 
6209 function get_localtax_by_third($local)
6210 {
6211  global $db, $mysoc;
6212 
6213  $sql = " SELECT t.localtax".$local." as localtax";
6214  $sql .= " FROM ".MAIN_DB_PREFIX."c_tva as t INNER JOIN ".MAIN_DB_PREFIX."c_country as c ON c.rowid = t.fk_pays";
6215  $sql .= " WHERE c.code = '".$db->escape($mysoc->country_code)."' AND t.active = 1 AND t.taux = (";
6216  $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";
6217  $sql .= " WHERE c.code = '".$db->escape($mysoc->country_code)."' AND tt.active = 1)";
6218  $sql .= " AND t.localtax".$local."_type <> '0'";
6219  $sql .= " ORDER BY t.rowid DESC";
6220 
6221  $resql = $db->query($sql);
6222  if ($resql) {
6223  $obj = $db->fetch_object($resql);
6224  return $obj->localtax;
6225  } else {
6226  return 'Error';
6227  }
6228 
6229  return '0';
6230 }
6231 
6232 
6244 function getTaxesFromId($vatrate, $buyer = null, $seller = null, $firstparamisid = 1)
6245 {
6246  global $db, $mysoc;
6247 
6248  dol_syslog("getTaxesFromId vat id or rate = ".$vatrate);
6249 
6250  // Search local taxes
6251  $sql = "SELECT t.rowid, t.code, t.taux as rate, t.recuperableonly as npr, t.accountancy_code_sell, t.accountancy_code_buy,";
6252  $sql .= " t.localtax1, t.localtax1_type, t.localtax2, t.localtax2_type";
6253  $sql .= " FROM ".MAIN_DB_PREFIX."c_tva as t";
6254  if ($firstparamisid) {
6255  $sql .= " WHERE t.rowid = ".(int) $vatrate;
6256  } else {
6257  $vatratecleaned = $vatrate;
6258  $vatratecode = '';
6259  $reg = array();
6260  if (preg_match('/^(.*)\s*\((.*)\)$/', $vatrate, $reg)) { // If vat is "xx (yy)"
6261  $vatratecleaned = $reg[1];
6262  $vatratecode = $reg[2];
6263  }
6264 
6265  $sql .= ", ".MAIN_DB_PREFIX."c_country as c";
6266  /*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 ??
6267  else $sql.= " WHERE t.fk_pays = c.rowid AND c.code = '".$db->escape($seller->country_code)."'";*/
6268  $sql .= " WHERE t.fk_pays = c.rowid AND c.code = '".$db->escape($seller->country_code)."'";
6269  $sql .= " AND t.taux = ".((float) $vatratecleaned)." AND t.active = 1";
6270  if ($vatratecode) {
6271  $sql .= " AND t.code = '".$db->escape($vatratecode)."'";
6272  }
6273  }
6274 
6275  $resql = $db->query($sql);
6276  if ($resql) {
6277  $obj = $db->fetch_object($resql);
6278  if ($obj) {
6279  return array(
6280  'rowid'=>$obj->rowid,
6281  'code'=>$obj->code,
6282  'rate'=>$obj->rate,
6283  'localtax1'=>$obj->localtax1,
6284  'localtax1_type'=>$obj->localtax1_type,
6285  'localtax2'=>$obj->localtax2,
6286  'localtax2_type'=>$obj->localtax2_type,
6287  'npr'=>$obj->npr,
6288  'accountancy_code_sell'=>$obj->accountancy_code_sell,
6289  'accountancy_code_buy'=>$obj->accountancy_code_buy
6290  );
6291  } else {
6292  return array();
6293  }
6294  } else {
6295  dol_print_error($db);
6296  }
6297 
6298  return array();
6299 }
6300 
6317 function getLocalTaxesFromRate($vatrate, $local, $buyer, $seller, $firstparamisid = 0)
6318 {
6319  global $db, $mysoc;
6320 
6321  dol_syslog("getLocalTaxesFromRate vatrate=".$vatrate." local=".$local);
6322 
6323  // Search local taxes
6324  $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";
6325  $sql .= " FROM ".MAIN_DB_PREFIX."c_tva as t";
6326  if ($firstparamisid) {
6327  $sql .= " WHERE t.rowid = ".(int) $vatrate;
6328  } else {
6329  $vatratecleaned = $vatrate;
6330  $vatratecode = '';
6331  $reg = array();
6332  if (preg_match('/^(.*)\s*\((.*)\)$/', $vatrate, $reg)) { // If vat is "x.x (yy)"
6333  $vatratecleaned = $reg[1];
6334  $vatratecode = $reg[2];
6335  }
6336 
6337  $sql .= ", ".MAIN_DB_PREFIX."c_country as c";
6338  if (!empty($mysoc) && $mysoc->country_code == 'ES') {
6339  $countrycodetouse = ((empty($buyer) || empty($buyer->country_code)) ? $mysoc->country_code : $buyer->country_code);
6340  $sql .= " WHERE t.fk_pays = c.rowid AND c.code = '".$db->escape($countrycodetouse)."'"; // local tax in spain use the buyer country ??
6341  } else {
6342  $countrycodetouse = ((empty($seller) || empty($seller->country_code)) ? $mysoc->country_code : $seller->country_code);
6343  $sql .= " WHERE t.fk_pays = c.rowid AND c.code = '".$db->escape($countrycodetouse)."'";
6344  }
6345  $sql .= " AND t.taux = ".((float) $vatratecleaned)." AND t.active = 1";
6346  if ($vatratecode) {
6347  $sql .= " AND t.code = '".$db->escape($vatratecode)."'";
6348  }
6349  }
6350 
6351  $resql = $db->query($sql);
6352  if ($resql) {
6353  $obj = $db->fetch_object($resql);
6354 
6355  if ($obj) {
6356  $vateratestring = $obj->rate.($obj->code ? ' ('.$obj->code.')' : '');
6357 
6358  if ($local == 1) {
6359  return array($obj->localtax1_type, get_localtax($vateratestring, $local, $buyer, $seller), $obj->accountancy_code_sell, $obj->accountancy_code_buy);
6360  } elseif ($local == 2) {
6361  return array($obj->localtax2_type, get_localtax($vateratestring, $local, $buyer, $seller), $obj->accountancy_code_sell, $obj->accountancy_code_buy);
6362  } else {
6363  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);
6364  }
6365  }
6366  }
6367 
6368  return array();
6369 }
6370 
6381 function get_product_vat_for_country($idprod, $thirdpartytouse, $idprodfournprice = 0)
6382 {
6383  global $db, $conf, $mysoc;
6384 
6385  require_once DOL_DOCUMENT_ROOT.'/product/class/product.class.php';
6386 
6387  $ret = 0;
6388  $found = 0;
6389 
6390  if ($idprod > 0) {
6391  // Load product
6392  $product = new Product($db);
6393  $product->fetch($idprod);
6394 
6395  if ($mysoc->country_code == $thirdpartytouse->country_code) {
6396  // If country to consider is ours
6397  if ($idprodfournprice > 0) { // We want vat for product for a "supplier" object
6398  $result = $product->get_buyprice($idprodfournprice, 0, 0, 0);
6399  if ($result > 0) {
6400  $ret = $product->vatrate_supplier;
6401  if ($product->default_vat_code_supplier) {
6402  $ret .= ' ('.$product->default_vat_code_supplier.')';
6403  }
6404  $found = 1;
6405  }
6406  }
6407  if (!$found) {
6408  $ret = $product->tva_tx; // Default sales vat of product
6409  if ($product->default_vat_code) {
6410  $ret .= ' ('.$product->default_vat_code.')';
6411  }
6412  $found = 1;
6413  }
6414  } else {
6415  // TODO Read default product vat according to product and another countrycode.
6416  // Vat for couple anothercountrycode/product is data that is not managed and store yet, so we will fallback on next rule.
6417  }
6418  }
6419 
6420  if (!$found) {
6421  if (empty($conf->global->MAIN_VAT_DEFAULT_IF_AUTODETECT_FAILS)) {
6422  // 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).
6423  $sql = "SELECT t.taux as vat_rate, t.code as default_vat_code";
6424  $sql .= " FROM ".MAIN_DB_PREFIX."c_tva as t, ".MAIN_DB_PREFIX."c_country as c";
6425  $sql .= " WHERE t.active = 1 AND t.fk_pays = c.rowid AND c.code = '".$db->escape($thirdpartytouse->country_code)."'";
6426  $sql .= " ORDER BY t.use_default DESC, t.taux DESC, t.code ASC, t.recuperableonly ASC";
6427  $sql .= $db->plimit(1);
6428 
6429  $resql = $db->query($sql);
6430  if ($resql) {
6431  $obj = $db->fetch_object($resql);
6432  if ($obj) {
6433  $ret = $obj->vat_rate;
6434  if ($obj->default_vat_code) {
6435  $ret .= ' ('.$obj->default_vat_code.')';
6436  }
6437  }
6438  $db->free($resql);
6439  } else {
6440  dol_print_error($db);
6441  }
6442  } else {
6443  // Forced value if autodetect fails. MAIN_VAT_DEFAULT_IF_AUTODETECT_FAILS can be
6444  // '1.23'
6445  // or '1.23 (CODE)'
6446  $defaulttx = '';
6447  if ($conf->global->MAIN_VAT_DEFAULT_IF_AUTODETECT_FAILS != 'none') {
6448  $defaulttx = $conf->global->MAIN_VAT_DEFAULT_IF_AUTODETECT_FAILS;
6449  }
6450  /*if (preg_match('/\((.*)\)/', $defaulttx, $reg)) {
6451  $defaultcode = $reg[1];
6452  $defaulttx = preg_replace('/\s*\(.*\)/', '', $defaulttx);
6453  }*/
6454 
6455  $ret = $defaulttx;
6456  }
6457  }
6458 
6459  dol_syslog("get_product_vat_for_country: ret=".$ret);
6460  return $ret;
6461 }
6462 
6472 function get_product_localtax_for_country($idprod, $local, $thirdpartytouse)
6473 {
6474  global $db, $mysoc;
6475 
6476  if (!class_exists('Product')) {
6477  require_once DOL_DOCUMENT_ROOT.'/product/class/product.class.php';
6478  }
6479 
6480  $ret = 0;
6481  $found = 0;
6482 
6483  if ($idprod > 0) {
6484  // Load product
6485  $product = new Product($db);
6486  $result = $product->fetch($idprod);
6487 
6488  if ($mysoc->country_code == $thirdpartytouse->country_code) { // If selling country is ours
6489  /* Not defined yet, so we don't use this
6490  if ($local==1) $ret=$product->localtax1_tx;
6491  elseif ($local==2) $ret=$product->localtax2_tx;
6492  $found=1;
6493  */
6494  } else {
6495  // TODO Read default product vat according to product and another countrycode.
6496  // Vat for couple anothercountrycode/product is data that is not managed and store yet, so we will fallback on next rule.
6497  }
6498  }
6499 
6500  if (!$found) {
6501  // If vat of product for the country not found or not defined, we return higher vat of country.
6502  $sql = "SELECT taux as vat_rate, localtax1, localtax2";
6503  $sql .= " FROM ".MAIN_DB_PREFIX."c_tva as t, ".MAIN_DB_PREFIX."c_country as c";
6504  $sql .= " WHERE t.active=1 AND t.fk_pays = c.rowid AND c.code='".$db->escape($thirdpartytouse->country_code)."'";
6505  $sql .= " ORDER BY t.taux DESC, t.recuperableonly ASC";
6506  $sql .= $db->plimit(1);
6507 
6508  $resql = $db->query($sql);
6509  if ($resql) {
6510  $obj = $db->fetch_object($resql);
6511  if ($obj) {
6512  if ($local == 1) {
6513  $ret = $obj->localtax1;
6514  } elseif ($local == 2) {
6515  $ret = $obj->localtax2;
6516  }
6517  }
6518  } else {
6519  dol_print_error($db);
6520  }
6521  }
6522 
6523  dol_syslog("get_product_localtax_for_country: ret=".$ret);
6524  return $ret;
6525 }
6526 
6543 function get_default_tva(Societe $thirdparty_seller, Societe $thirdparty_buyer, $idprod = 0, $idprodfournprice = 0)
6544 {
6545  global $conf;
6546 
6547  require_once DOL_DOCUMENT_ROOT.'/core/lib/company.lib.php';
6548 
6549  // Note: possible values for tva_assuj are 0/1 or franchise/reel
6550  $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;
6551 
6552  $seller_country_code = $thirdparty_seller->country_code;
6553  $seller_in_cee = isInEEC($thirdparty_seller);
6554 
6555  $buyer_country_code = $thirdparty_buyer->country_code;
6556  $buyer_in_cee = isInEEC($thirdparty_buyer);
6557 
6558  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 : ''));
6559 
6560  // 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)
6561  // we use the buyer VAT.
6562  if (!empty($conf->global->SERVICE_ARE_ECOMMERCE_200238EC)) {
6563  if ($seller_in_cee && $buyer_in_cee) {
6564  $isacompany = $thirdparty_buyer->isACompany();
6565  if ($isacompany && getDolGlobalString('MAIN_USE_VAT_COMPANIES_IN_EEC_WITH_INVALID_VAT_ID_ARE_INDIVIDUAL')) {
6566  require_once DOL_DOCUMENT_ROOT.'/core/lib/functions2.lib.php';
6567  if (!isValidVATID($thirdparty_buyer)) {
6568  $isacompany = 0;
6569  }
6570  }
6571 
6572  if (!$isacompany) {
6573  //print 'VATRULE 0';
6574  return get_product_vat_for_country($idprod, $thirdparty_buyer, $idprodfournprice);
6575  }
6576  }
6577  }
6578 
6579  // If seller does not use VAT, default VAT is 0. End of rule.
6580  if (!$seller_use_vat) {
6581  //print 'VATRULE 1';
6582  return 0;
6583  }
6584 
6585  // If the (seller country = buyer country) then the default VAT = VAT of the product sold. End of rule.
6586  if (($seller_country_code == $buyer_country_code)
6587  || (in_array($seller_country_code, array('FR', 'MC')) && in_array($buyer_country_code, array('FR', 'MC')))) { // Warning ->country_code not always defined
6588  //print 'VATRULE 2';
6589  $tmpvat = get_product_vat_for_country($idprod, $thirdparty_seller, $idprodfournprice);
6590 
6591  if ($seller_country_code == 'IN' && getDolGlobalString('MAIN_SALETAX_AUTOSWITCH_I_CS_FOR_INDIA')) {
6592  // Special case for india.
6593  //print 'VATRULE 2b';
6594  $reg = array();
6595  if (preg_match('/C+S-(\d+)/', $tmpvat, $reg) && $thirdparty_seller->state_id != $thirdparty_buyer->state_id) {
6596  // we must revert the C+S into I
6597  $tmpvat = str_replace("C+S", "I", $tmpvat);
6598  } elseif (preg_match('/I-(\d+)/', $tmpvat, $reg) && $thirdparty_seller->state_id == $thirdparty_buyer->state_id) {
6599  // we must revert the I into C+S
6600  $tmpvat = str_replace("I", "C+S", $tmpvat);
6601  }
6602  }
6603 
6604  return $tmpvat;
6605  }
6606 
6607  // 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.
6608  // 'VATRULE 3' - Not supported
6609 
6610  // If (seller and buyer in the European Community) and (buyer = individual) then VAT by default = VAT of the product sold. End of rule
6611  // If (seller and buyer in European Community) and (buyer = company) then VAT by default=0. End of rule
6612  if (($seller_in_cee && $buyer_in_cee)) {
6613  $isacompany = $thirdparty_buyer->isACompany();
6614  if ($isacompany && getDolGlobalString('MAIN_USE_VAT_COMPANIES_IN_EEC_WITH_INVALID_VAT_ID_ARE_INDIVIDUAL')) {
6615  require_once DOL_DOCUMENT_ROOT.'/core/lib/functions2.lib.php';
6616  if (!isValidVATID($thirdparty_buyer)) {
6617  $isacompany = 0;
6618  }
6619  }
6620 
6621  if (!$isacompany) {
6622  //print 'VATRULE 4';
6623  return get_product_vat_for_country($idprod, $thirdparty_seller, $idprodfournprice);
6624  } else {
6625  //print 'VATRULE 5';
6626  return 0;
6627  }
6628  }
6629 
6630  // 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
6631  // I don't see any use case that need this rule.
6632  if (!empty($conf->global->MAIN_USE_VAT_OF_PRODUCT_FOR_INDIVIDUAL_CUSTOMER_OUT_OF_EEC) && empty($buyer_in_cee)) {
6633  $isacompany = $thirdparty_buyer->isACompany();
6634  if (!$isacompany) {
6635  return get_product_vat_for_country($idprod, $thirdparty_seller, $idprodfournprice);
6636  //print 'VATRULE extra';
6637  }
6638  }
6639 
6640  // Otherwise the VAT proposed by default=0. End of rule.
6641  // Rem: This means that at least one of the 2 is outside the European Community and the country differs
6642  //print 'VATRULE 6';
6643  return 0;
6644 }
6645 
6646 
6657 function get_default_npr(Societe $thirdparty_seller, Societe $thirdparty_buyer, $idprod = 0, $idprodfournprice = 0)
6658 {
6659  global $db;
6660 
6661  if ($idprodfournprice > 0) {
6662  if (!class_exists('ProductFournisseur')) {
6663  require_once DOL_DOCUMENT_ROOT.'/fourn/class/fournisseur.product.class.php';
6664  }
6665  $prodprice = new ProductFournisseur($db);
6666  $prodprice->fetch_product_fournisseur_price($idprodfournprice);
6667  return $prodprice->fourn_tva_npr;
6668  } elseif ($idprod > 0) {
6669  if (!class_exists('Product')) {
6670  require_once DOL_DOCUMENT_ROOT.'/product/class/product.class.php';
6671  }
6672  $prod = new Product($db);
6673  $prod->fetch($idprod);
6674  return $prod->tva_npr;
6675  }
6676 
6677  return 0;
6678 }
6679 
6693 function get_default_localtax($thirdparty_seller, $thirdparty_buyer, $local, $idprod = 0)
6694 {
6695  global $mysoc;
6696 
6697  if (!is_object($thirdparty_seller)) {
6698  return -1;
6699  }
6700  if (!is_object($thirdparty_buyer)) {
6701  return -1;
6702  }
6703 
6704  if ($local == 1) { // Localtax 1
6705  if ($mysoc->country_code == 'ES') {
6706  if (is_numeric($thirdparty_buyer->localtax1_assuj) && !$thirdparty_buyer->localtax1_assuj) {
6707  return 0;
6708  }
6709  } else {
6710  // Si vendeur non assujeti a Localtax1, localtax1 par default=0
6711  if (is_numeric($thirdparty_seller->localtax1_assuj) && !$thirdparty_seller->localtax1_assuj) {
6712  return 0;
6713  }
6714  if (!is_numeric($thirdparty_seller->localtax1_assuj) && $thirdparty_seller->localtax1_assuj == 'localtax1off') {
6715  return 0;
6716  }
6717  }
6718  } elseif ($local == 2) { //I Localtax 2
6719  // Si vendeur non assujeti a Localtax2, localtax2 par default=0
6720  if (is_numeric($thirdparty_seller->localtax2_assuj) && !$thirdparty_seller->localtax2_assuj) {
6721  return 0;
6722  }
6723  if (!is_numeric($thirdparty_seller->localtax2_assuj) && $thirdparty_seller->localtax2_assuj == 'localtax2off') {
6724  return 0;
6725  }
6726  }
6727 
6728  if ($thirdparty_seller->country_code == $thirdparty_buyer->country_code) {
6729  return get_product_localtax_for_country($idprod, $local, $thirdparty_seller);
6730  }
6731 
6732  return 0;
6733 }
6734 
6743 function yn($yesno, $case = 1, $color = 0)
6744 {
6745  global $langs;
6746 
6747  $result = 'unknown';
6748  $classname = '';
6749  if ($yesno == 1 || strtolower($yesno) == 'yes' || strtolower($yesno) == 'true') { // A mettre avant test sur no a cause du == 0
6750  $result = $langs->trans('yes');
6751  if ($case == 1 || $case == 3) {
6752  $result = $langs->trans("Yes");
6753  }
6754  if ($case == 2) {
6755  $result = '<input type="checkbox" value="1" checked disabled>';
6756  }
6757  if ($case == 3) {
6758  $result = '<input type="checkbox" value="1" checked disabled> '.$result;
6759  }
6760 
6761  $classname = 'ok';
6762  } elseif ($yesno == 0 || strtolower($yesno) == 'no' || strtolower($yesno) == 'false') {
6763  $result = $langs->trans("no");
6764  if ($case == 1 || $case == 3) {
6765  $result = $langs->trans("No");
6766  }
6767  if ($case == 2) {
6768  $result = '<input type="checkbox" value="0" disabled>';
6769  }
6770  if ($case == 3) {
6771  $result = '<input type="checkbox" value="0" disabled> '.$result;
6772  }
6773 
6774  if ($color == 2) {
6775  $classname = 'ok';
6776  } else {
6777  $classname = 'error';
6778  }
6779  }
6780  if ($color) {
6781  return '<span class="'.$classname.'">'.$result.'</span>';
6782  }
6783  return $result;
6784 }
6785 
6801 function get_exdir($num, $level, $alpha, $withoutslash, $object, $modulepart = '')
6802 {
6803  global $conf;
6804 
6805  if (empty($modulepart) && !empty($object->module)) {
6806  $modulepart = $object->module;
6807  }
6808 
6809  $path = '';
6810 
6811  $arrayforoldpath = array('cheque', 'category', 'holiday', 'supplier_invoice', 'invoice_supplier', 'mailing', 'supplier_payment');
6812  if (getDolGlobalInt('PRODUCT_USE_OLD_PATH_FOR_PHOTO')) {
6813  $arrayforoldpath[] = 'product';
6814  }
6815  if (!empty($level) && in_array($modulepart, $arrayforoldpath)) {
6816  // This part should be removed once all code is using "get_exdir" to forge path, with parameter $object and $modulepart provided.
6817  if (empty($alpha)) {
6818  $num = preg_replace('/([^0-9])/i', '', $num);
6819  } else {
6820  $num = preg_replace('/^.*\-/i', '', $num);
6821  }
6822  $num = substr("000".$num, -$level);
6823  if ($level == 1) {
6824  $path = substr($num, 0, 1);
6825  }
6826  if ($level == 2) {
6827  $path = substr($num, 1, 1).'/'.substr($num, 0, 1);
6828  }
6829  if ($level == 3) {
6830  $path = substr($num, 2, 1).'/'.substr($num, 1, 1).'/'.substr($num, 0, 1);
6831  }
6832  } else {
6833  // We will enhance here a common way of forging path for document storage.
6834  // In a future, we may distribute directories on several levels depending on setup and object.
6835  // Here, $object->id, $object->ref and $modulepart are required.
6836  //var_dump($modulepart);
6837  $path = dol_sanitizeFileName(empty($object->ref) ? (string) $object->id : $object->ref);
6838  }
6839 
6840  if (empty($withoutslash) && !empty($path)) {
6841  $path .= '/';
6842  }
6843 
6844  return $path;
6845 }
6846 
6855 function dol_mkdir($dir, $dataroot = '', $newmask = '')
6856 {
6857  global $conf;
6858 
6859  dol_syslog("functions.lib::dol_mkdir: dir=".$dir, LOG_INFO);
6860 
6861  $dir_osencoded = dol_osencode($dir);
6862  if (@is_dir($dir_osencoded)) {
6863  return 0;
6864  }
6865 
6866  $nberr = 0;
6867  $nbcreated = 0;
6868 
6869  $ccdir = '';
6870  if (!empty($dataroot)) {
6871  // Remove data root from loop
6872  $dir = str_replace($dataroot.'/', '', $dir);
6873  $ccdir = $dataroot.'/';
6874  }
6875 
6876  $cdir = explode("/", $dir);
6877  $num = count($cdir);
6878  for ($i = 0; $i < $num; $i++) {
6879  if ($i > 0) {
6880  $ccdir .= '/'.$cdir[$i];
6881  } else {
6882  $ccdir .= $cdir[$i];
6883  }
6884  if (preg_match("/^.:$/", $ccdir, $regs)) {
6885  continue; // Si chemin Windows incomplet, on poursuit par rep suivant
6886  }
6887 
6888  // Attention, le is_dir() peut echouer bien que le rep existe.
6889  // (ex selon config de open_basedir)
6890  if ($ccdir) {
6891  $ccdir_osencoded = dol_osencode($ccdir);
6892  if (!@is_dir($ccdir_osencoded)) {
6893  dol_syslog("functions.lib::dol_mkdir: Directory '".$ccdir."' does not exists or is outside open_basedir PHP setting.", LOG_DEBUG);
6894 
6895  umask(0);
6896  $dirmaskdec = octdec((string) $newmask);
6897  if (empty($newmask)) {
6898  $dirmaskdec = empty($conf->global->MAIN_UMASK) ? octdec('0755') : octdec($conf->global->MAIN_UMASK);
6899  }
6900  $dirmaskdec |= octdec('0111'); // Set x bit required for directories
6901  if (!@mkdir($ccdir_osencoded, $dirmaskdec)) {
6902  // Si le is_dir a renvoye une fausse info, alors on passe ici.
6903  dol_syslog("functions.lib::dol_mkdir: Fails to create directory '".$ccdir."' or directory already exists.", LOG_WARNING);
6904  $nberr++;
6905  } else {
6906  dol_syslog("functions.lib::dol_mkdir: Directory '".$ccdir."' created", LOG_DEBUG);
6907  $nberr = 0; // On remet a zero car si on arrive ici, cela veut dire que les echecs precedents peuvent etre ignore
6908  $nbcreated++;
6909  }
6910  } else {
6911  $nberr = 0; // On remet a zero car si on arrive ici, cela veut dire que les echecs precedents peuvent etre ignores
6912  }
6913  }
6914  }
6915  return ($nberr ? -$nberr : $nbcreated);
6916 }
6917 
6918 
6926 function dolChmod($filepath, $newmask = '')
6927 {
6928  global $conf;
6929 
6930  if (!empty($newmask)) {
6931  @chmod($filepath, octdec($newmask));
6932  } elseif (!empty($conf->global->MAIN_UMASK)) {
6933  @chmod($filepath, octdec($conf->global->MAIN_UMASK));
6934  }
6935 }
6936 
6937 
6943 function picto_required()
6944 {
6945  return '<span class="fieldrequired">*</span>';
6946 }
6947 
6948 
6965 function dol_string_nohtmltag($stringtoclean, $removelinefeed = 1, $pagecodeto = 'UTF-8', $strip_tags = 0, $removedoublespaces = 1)
6966 {
6967  if (is_null($stringtoclean)) {
6968  return '';
6969  }
6970 
6971  if ($removelinefeed == 2) {
6972  $stringtoclean = preg_replace('/<br[^>]*>(\n|\r)+/ims', '<br>', $stringtoclean);
6973  }
6974  $temp = preg_replace('/<br[^>]*>/i', "\n", $stringtoclean);
6975 
6976  // We remove entities BEFORE stripping (in case of an open separator char that is entity encoded and not the closing other, the strip will fails)
6977  $temp = dol_html_entity_decode($temp, ENT_COMPAT | ENT_HTML5, $pagecodeto);
6978 
6979  $temp = str_replace('< ', '__ltspace__', $temp);
6980 
6981  if ($strip_tags) {
6982  $temp = strip_tags($temp);
6983  } else {
6984  // Remove '<' into remainging, so remove non closing html tags like '<abc' or '<<abc'. Note: '<123abc' is not a html tag (can be kept), but '<abc123' is (must be removed).
6985  $pattern = "/<[^<>]+>/";
6986  // Example of $temp: <a href="/myurl" title="<u>A title</u>">0000-021</a>
6987  // pa