dolibarr  16.0.5
functions.lib.php
Go to the documentation of this file.
1 <?php
2 /* Copyright (C) 2000-2007 Rodolphe Quiedeville <rodolphe@quiedeville.org>
3  * Copyright (C) 2003 Jean-Louis Bergamo <jlb@j1b.org>
4  * Copyright (C) 2004-2018 Laurent Destailleur <eldy@users.sourceforge.net>
5  * Copyright (C) 2004 Sebastien Di Cintio <sdicintio@ressource-toi.org>
6  * Copyright (C) 2004 Benoit Mortier <benoit.mortier@opensides.be>
7  * Copyright (C) 2004 Christophe Combelles <ccomb@free.fr>
8  * Copyright (C) 2005-2019 Regis Houssin <regis.houssin@inodbox.com>
9  * Copyright (C) 2008 Raphael Bertrand (Resultic) <raphael.bertrand@resultic.fr>
10  * Copyright (C) 2010-2018 Juanjo Menent <jmenent@2byte.es>
11  * Copyright (C) 2013 Cédric Salvador <csalvador@gpcsolutions.fr>
12  * Copyright (C) 2013-2021 Alexandre Spangaro <aspangaro@open-dsi.fr>
13  * Copyright (C) 2014 Cédric GROSS <c.gross@kreiz-it.fr>
14  * Copyright (C) 2014-2015 Marcos García <marcosgdf@gmail.com>
15  * Copyright (C) 2015 Jean-François Ferry <jfefe@aternatik.fr>
16  * Copyright (C) 2018-2022 Frédéric France <frederic.france@netlogic.fr>
17  * Copyright (C) 2019 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  *
23  * This program is free software; you can redistribute it and/or modify
24  * it under the terms of the GNU General Public License as published by
25  * the Free Software Foundation; either version 3 of the License, or
26  * (at your option) any later version.
27  *
28  * This program is distributed in the hope that it will be useful,
29  * but WITHOUT ANY WARRANTY; without even the implied warranty of
30  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
31  * GNU General Public License for more details.
32  *
33  * You should have received a copy of the GNU General Public License
34  * along with this program. If not, see <https://www.gnu.org/licenses/>.
35  * or see https://www.gnu.org/
36  */
37 
44 include_once DOL_DOCUMENT_ROOT.'/core/lib/json.lib.php';
45 
46 
47 if (!function_exists('utf8_encode')) {
54  function utf8_encode($elements)
55  {
56  return mb_convert_encoding($elements, 'UTF-8', 'ISO-8859-1');
57  }
58 }
59 
60 if (!function_exists('utf8_decode')) {
67  function utf8_decode($elements)
68  {
69  return mb_convert_encoding($elements, 'ISO-8859-1', 'UTF-8');
70  }
71 }
72 
73 
80 function getDolGlobalString($key, $default = '')
81 {
82  global $conf;
83  // return $conf->global->$key ?? $default;
84  return (string) (isset($conf->global->$key) ? $conf->global->$key : $default);
85 }
86 
93 function getDolGlobalInt($key, $default = 0)
94 {
95  global $conf;
96  // return $conf->global->$key ?? $default;
97  return (int) (isset($conf->global->$key) ? $conf->global->$key : $default);
98 }
99 
105 function isModEnabled($module)
106 {
107  global $conf;
108  return !empty($conf->$module->enabled);
109 }
110 
122 function getDoliDBInstance($type, $host, $user, $pass, $name, $port)
123 {
124  require_once DOL_DOCUMENT_ROOT."/core/db/".$type.'.class.php';
125 
126  $class = 'DoliDB'.ucfirst($type);
127  $dolidb = new $class($type, $host, $user, $pass, $name, $port);
128  return $dolidb;
129 }
130 
148 function getEntity($element, $shared = 1, $currentobject = null)
149 {
150  global $conf, $mc, $hookmanager, $object, $action, $db;
151 
152  if (! is_object($hookmanager)) {
153  $hookmanager = new HookManager($db);
154  }
155 
156  // fix different element names (France to English)
157  switch ($element) {
158  case 'contrat':
159  $element = 'contract';
160  break; // "/contrat/class/contrat.class.php"
161  case 'order_supplier':
162  $element = 'supplier_order';
163  break; // "/fourn/class/fournisseur.commande.class.php"
164  case 'invoice_supplier':
165  $element = 'supplier_invoice';
166  break; // "/fourn/class/fournisseur.facture.class.php"
167  }
168 
169  if (is_object($mc)) {
170  $out = $mc->getEntity($element, $shared, $currentobject);
171  } else {
172  $out = '';
173  $addzero = array('user', 'usergroup', 'c_email_templates', 'email_template', 'default_values');
174  if (in_array($element, $addzero)) {
175  $out .= '0,';
176  }
177  $out .= ((int) $conf->entity);
178  }
179 
180  // Manipulate entities to query on the fly
181  $parameters = array(
182  'element' => $element,
183  'shared' => $shared,
184  'object' => $object,
185  'currentobject' => $currentobject,
186  'out' => $out
187  );
188  $reshook = $hookmanager->executeHooks('hookGetEntity', $parameters, $currentobject, $action); // Note that $action and $object may have been modified by some hooks
189 
190  if (is_numeric($reshook)) {
191  if ($reshook == 0 && !empty($hookmanager->resPrint)) {
192  $out .= ','.$hookmanager->resPrint; // add
193  } elseif ($reshook == 1) {
194  $out = $hookmanager->resPrint; // replace
195  }
196  }
197 
198  return $out;
199 }
200 
207 function setEntity($currentobject)
208 {
209  global $conf, $mc;
210 
211  if (is_object($mc) && method_exists($mc, 'setEntity')) {
212  return $mc->setEntity($currentobject);
213  } else {
214  return ((is_object($currentobject) && $currentobject->id > 0 && $currentobject->entity > 0) ? $currentobject->entity : $conf->entity);
215  }
216 }
217 
224 function isASecretKey($keyname)
225 {
226  return preg_match('/(_pass|password|_pw|_key|securekey|serverkey|secret\d?|p12key|exportkey|_PW_[a-z]+|token)$/i', $keyname);
227 }
228 
229 
236 function num2Alpha($n)
237 {
238  for ($r = ""; $n >= 0; $n = intval($n / 26) - 1)
239  $r = chr($n % 26 + 0x41) . $r;
240  return $r;
241 }
242 
243 
260 function getBrowserInfo($user_agent)
261 {
262  include_once DOL_DOCUMENT_ROOT.'/includes/mobiledetect/mobiledetectlib/Mobile_Detect.php';
263 
264  $name = 'unknown';
265  $version = '';
266  $os = 'unknown';
267  $phone = '';
268 
269  $user_agent = substr($user_agent, 0, 512); // Avoid to process too large user agent
270 
271  $detectmobile = new Mobile_Detect(null, $user_agent);
272  $tablet = $detectmobile->isTablet();
273 
274  if ($detectmobile->isMobile()) {
275  $phone = 'unknown';
276 
277  // If phone/smartphone, we set phone os name.
278  if ($detectmobile->is('AndroidOS')) {
279  $os = $phone = 'android';
280  } elseif ($detectmobile->is('BlackBerryOS')) {
281  $os = $phone = 'blackberry';
282  } elseif ($detectmobile->is('iOS')) {
283  $os = 'ios';
284  $phone = 'iphone';
285  } elseif ($detectmobile->is('PalmOS')) {
286  $os = $phone = 'palm';
287  } elseif ($detectmobile->is('SymbianOS')) {
288  $os = 'symbian';
289  } elseif ($detectmobile->is('webOS')) {
290  $os = 'webos';
291  } elseif ($detectmobile->is('MaemoOS')) {
292  $os = 'maemo';
293  } elseif ($detectmobile->is('WindowsMobileOS') || $detectmobile->is('WindowsPhoneOS')) {
294  $os = 'windows';
295  }
296  }
297 
298  // OS
299  if (preg_match('/linux/i', $user_agent)) {
300  $os = 'linux';
301  } elseif (preg_match('/macintosh/i', $user_agent)) {
302  $os = 'macintosh';
303  } elseif (preg_match('/windows/i', $user_agent)) {
304  $os = 'windows';
305  }
306 
307  // Name
308  $reg = array();
309  if (preg_match('/firefox(\/|\s)([\d\.]*)/i', $user_agent, $reg)) {
310  $name = 'firefox';
311  $version = $reg[2];
312  } elseif (preg_match('/edge(\/|\s)([\d\.]*)/i', $user_agent, $reg)) {
313  $name = 'edge';
314  $version = $reg[2];
315  } elseif (preg_match('/chrome(\/|\s)([\d\.]+)/i', $user_agent, $reg)) {
316  $name = 'chrome';
317  $version = $reg[2];
318  } elseif (preg_match('/chrome/i', $user_agent, $reg)) {
319  // we can have 'chrome (Mozilla...) chrome x.y' in one string
320  $name = 'chrome';
321  } elseif (preg_match('/iceweasel/i', $user_agent)) {
322  $name = 'iceweasel';
323  } elseif (preg_match('/epiphany/i', $user_agent)) {
324  $name = 'epiphany';
325  } elseif (preg_match('/safari(\/|\s)([\d\.]*)/i', $user_agent, $reg)) {
326  $name = 'safari';
327  $version = $reg[2];
328  } elseif (preg_match('/opera(\/|\s)([\d\.]*)/i', $user_agent, $reg)) {
329  // Safari is often present in string for mobile but its not.
330  $name = 'opera';
331  $version = $reg[2];
332  } elseif (preg_match('/(MSIE\s([0-9]+\.[0-9]))|.*(Trident\/[0-9]+.[0-9];.*rv:([0-9]+\.[0-9]+))/i', $user_agent, $reg)) {
333  $name = 'ie';
334  $version = end($reg);
335  } elseif (preg_match('/(Windows NT\s([0-9]+\.[0-9])).*(Trident\/[0-9]+.[0-9];.*rv:([0-9]+\.[0-9]+))/i', $user_agent, $reg)) {
336  // MS products at end
337  $name = 'ie';
338  $version = end($reg);
339  } elseif (preg_match('/l(i|y)n(x|ks)(\(|\/|\s)*([\d\.]+)/i', $user_agent, $reg)) {
340  // MS products at end
341  $name = 'lynxlinks';
342  $version = $reg[4];
343  }
344 
345  if ($tablet) {
346  $layout = 'tablet';
347  } elseif ($phone) {
348  $layout = 'phone';
349  } else {
350  $layout = 'classic';
351  }
352 
353  return array(
354  'browsername' => $name,
355  'browserversion' => $version,
356  'browseros' => $os,
357  'layout' => $layout,
358  'phone' => $phone,
359  'tablet' => $tablet
360  );
361 }
362 
368 function dol_shutdown()
369 {
370  global $conf, $user, $langs, $db;
371  $disconnectdone = false;
372  $depth = 0;
373  if (is_object($db) && !empty($db->connected)) {
374  $depth = $db->transaction_opened;
375  $disconnectdone = $db->close();
376  }
377  dol_syslog("--- End access to ".$_SERVER["PHP_SELF"].(($disconnectdone && $depth) ? ' (Warn: db disconnection forced, transaction depth was '.$depth.')' : ''), (($disconnectdone && $depth) ? LOG_WARNING : LOG_INFO));
378 }
379 
386 function GETPOSTISSET($paramname)
387 {
388  $isset = false;
389 
390  $relativepathstring = $_SERVER["PHP_SELF"];
391  // Clean $relativepathstring
392  if (constant('DOL_URL_ROOT')) {
393  $relativepathstring = preg_replace('/^'.preg_quote(constant('DOL_URL_ROOT'), '/').'/', '', $relativepathstring);
394  }
395  $relativepathstring = preg_replace('/^\//', '', $relativepathstring);
396  $relativepathstring = preg_replace('/^custom\//', '', $relativepathstring);
397  //var_dump($relativepathstring);
398  //var_dump($user->default_values);
399 
400  // Code for search criteria persistence.
401  // Retrieve values if restore_lastsearch_values
402  if (!empty($_GET['restore_lastsearch_values'])) { // Use $_GET here and not GETPOST
403  if (!empty($_SESSION['lastsearch_values_'.$relativepathstring])) { // If there is saved values
404  $tmp = json_decode($_SESSION['lastsearch_values_'.$relativepathstring], true);
405  if (is_array($tmp)) {
406  foreach ($tmp as $key => $val) {
407  if ($key == $paramname) { // We are on the requested parameter
408  $isset = true;
409  break;
410  }
411  }
412  }
413  }
414  // If there is saved contextpage, limit, page or mode
415  if ($paramname == 'contextpage' && !empty($_SESSION['lastsearch_contextpage_'.$relativepathstring])) {
416  $isset = true;
417  } elseif ($paramname == 'limit' && !empty($_SESSION['lastsearch_limit_'.$relativepathstring])) {
418  $isset = true;
419  } elseif ($paramname == 'page' && !empty($_SESSION['lastsearch_page_'.$relativepathstring])) {
420  $isset = true;
421  } elseif ($paramname == 'mode' && !empty($_SESSION['lastsearch_mode_'.$relativepathstring])) {
422  $isset = true;
423  }
424  } else {
425  $isset = (isset($_POST[$paramname]) || isset($_GET[$paramname])); // We must keep $_POST and $_GET here
426  }
427 
428  return $isset;
429 }
430 
439 function GETPOSTISARRAY($paramname, $method = 0)
440 {
441  // for $method test need return the same $val as GETPOST
442  if (empty($method)) {
443  $val = isset($_GET[$paramname]) ? $_GET[$paramname] : (isset($_POST[$paramname]) ? $_POST[$paramname] : '');
444  } elseif ($method == 1) {
445  $val = isset($_GET[$paramname]) ? $_GET[$paramname] : '';
446  } elseif ($method == 2) {
447  $val = isset($_POST[$paramname]) ? $_POST[$paramname] : '';
448  } elseif ($method == 3) {
449  $val = isset($_POST[$paramname]) ? $_POST[$paramname] : (isset($_GET[$paramname]) ? $_GET[$paramname] : '');
450  } else {
451  $val = 'BadFirstParameterForGETPOST';
452  }
453 
454  return is_array($val);
455 }
456 
484 function GETPOST($paramname, $check = 'alphanohtml', $method = 0, $filter = null, $options = null, $noreplace = 0)
485 {
486  global $mysoc, $user, $conf;
487 
488  if (empty($paramname)) {
489  return 'BadFirstParameterForGETPOST';
490  }
491  if (empty($check)) {
492  dol_syslog("Deprecated use of GETPOST, called with 1st param = ".$paramname." and 2nd param is '', when calling page ".$_SERVER["PHP_SELF"], LOG_WARNING);
493  // Enable this line to know who call the GETPOST with '' $check parameter.
494  //var_dump(debug_backtrace()[0]);
495  }
496 
497  if (empty($method)) {
498  $out = isset($_GET[$paramname]) ? $_GET[$paramname] : (isset($_POST[$paramname]) ? $_POST[$paramname] : '');
499  } elseif ($method == 1) {
500  $out = isset($_GET[$paramname]) ? $_GET[$paramname] : '';
501  } elseif ($method == 2) {
502  $out = isset($_POST[$paramname]) ? $_POST[$paramname] : '';
503  } elseif ($method == 3) {
504  $out = isset($_POST[$paramname]) ? $_POST[$paramname] : (isset($_GET[$paramname]) ? $_GET[$paramname] : '');
505  } else {
506  return 'BadThirdParameterForGETPOST';
507  }
508 
509  if (empty($method) || $method == 3 || $method == 4) {
510  $relativepathstring = $_SERVER["PHP_SELF"];
511  // Clean $relativepathstring
512  if (constant('DOL_URL_ROOT')) {
513  $relativepathstring = preg_replace('/^'.preg_quote(constant('DOL_URL_ROOT'), '/').'/', '', $relativepathstring);
514  }
515  $relativepathstring = preg_replace('/^\//', '', $relativepathstring);
516  $relativepathstring = preg_replace('/^custom\//', '', $relativepathstring);
517  //var_dump($relativepathstring);
518  //var_dump($user->default_values);
519 
520  // Code for search criteria persistence.
521  // Retrieve values if restore_lastsearch_values
522  if (!empty($_GET['restore_lastsearch_values'])) { // Use $_GET here and not GETPOST
523  if (!empty($_SESSION['lastsearch_values_'.$relativepathstring])) { // If there is saved values
524  $tmp = json_decode($_SESSION['lastsearch_values_'.$relativepathstring], true);
525  if (is_array($tmp)) {
526  foreach ($tmp as $key => $val) {
527  if ($key == $paramname) { // We are on the requested parameter
528  $out = $val;
529  break;
530  }
531  }
532  }
533  }
534  // If there is saved contextpage, page or limit
535  if ($paramname == 'contextpage' && !empty($_SESSION['lastsearch_contextpage_'.$relativepathstring])) {
536  $out = $_SESSION['lastsearch_contextpage_'.$relativepathstring];
537  } elseif ($paramname == 'limit' && !empty($_SESSION['lastsearch_limit_'.$relativepathstring])) {
538  $out = $_SESSION['lastsearch_limit_'.$relativepathstring];
539  } elseif ($paramname == 'page' && !empty($_SESSION['lastsearch_page_'.$relativepathstring])) {
540  $out = $_SESSION['lastsearch_page_'.$relativepathstring];
541  } elseif ($paramname == 'mode' && !empty($_SESSION['lastsearch_mode_'.$relativepathstring])) {
542  $out = $_SESSION['lastsearch_mode_'.$relativepathstring];
543  }
544  } elseif (!isset($_GET['sortfield'])) {
545  // Else, retrieve default values if we are not doing a sort
546  // 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
547  if (!empty($_GET['action']) && $_GET['action'] == 'create' && !isset($_GET[$paramname]) && !isset($_POST[$paramname])) {
548  // Search default value from $object->field
549  global $object;
550  if (is_object($object) && isset($object->fields[$paramname]['default'])) {
551  $out = $object->fields[$paramname]['default'];
552  }
553  }
554  if (!empty($conf->global->MAIN_ENABLE_DEFAULT_VALUES)) {
555  if (!empty($_GET['action']) && (preg_match('/^create/', $_GET['action']) || preg_match('/^presend/', $_GET['action'])) && !isset($_GET[$paramname]) && !isset($_POST[$paramname])) {
556  // Now search in setup to overwrite default values
557  if (!empty($user->default_values)) { // $user->default_values defined from menu 'Setup - Default values'
558  if (isset($user->default_values[$relativepathstring]['createform'])) {
559  foreach ($user->default_values[$relativepathstring]['createform'] as $defkey => $defval) {
560  $qualified = 0;
561  if ($defkey != '_noquery_') {
562  $tmpqueryarraytohave = explode('&', $defkey);
563  $tmpqueryarraywehave = explode('&', dol_string_nohtmltag($_SERVER['QUERY_STRING']));
564  $foundintru = 0;
565  foreach ($tmpqueryarraytohave as $tmpquerytohave) {
566  if (!in_array($tmpquerytohave, $tmpqueryarraywehave)) {
567  $foundintru = 1;
568  }
569  }
570  if (!$foundintru) {
571  $qualified = 1;
572  }
573  //var_dump($defkey.'-'.$qualified);
574  } else {
575  $qualified = 1;
576  }
577 
578  if ($qualified) {
579  if (isset($user->default_values[$relativepathstring]['createform'][$defkey][$paramname])) {
580  $out = $user->default_values[$relativepathstring]['createform'][$defkey][$paramname];
581  break;
582  }
583  }
584  }
585  }
586  }
587  } elseif (!empty($paramname) && !isset($_GET[$paramname]) && !isset($_POST[$paramname])) {
588  // Management of default search_filters and sort order
589  if (!empty($user->default_values)) {
590  // $user->default_values defined from menu 'Setup - Default values'
591  //var_dump($user->default_values[$relativepathstring]);
592  if ($paramname == 'sortfield' || $paramname == 'sortorder') {
593  // Sorted on which fields ? ASC or DESC ?
594  if (isset($user->default_values[$relativepathstring]['sortorder'])) {
595  // Even if paramname is sortfield, data are stored into ['sortorder...']
596  foreach ($user->default_values[$relativepathstring]['sortorder'] as $defkey => $defval) {
597  $qualified = 0;
598  if ($defkey != '_noquery_') {
599  $tmpqueryarraytohave = explode('&', $defkey);
600  $tmpqueryarraywehave = explode('&', dol_string_nohtmltag($_SERVER['QUERY_STRING']));
601  $foundintru = 0;
602  foreach ($tmpqueryarraytohave as $tmpquerytohave) {
603  if (!in_array($tmpquerytohave, $tmpqueryarraywehave)) {
604  $foundintru = 1;
605  }
606  }
607  if (!$foundintru) {
608  $qualified = 1;
609  }
610  //var_dump($defkey.'-'.$qualified);
611  } else {
612  $qualified = 1;
613  }
614 
615  if ($qualified) {
616  $forbidden_chars_to_replace = array(" ", "'", "/", "\\", ":", "*", "?", "\"", "<", ">", "|", "[", "]", ";", "="); // we accept _, -, . and ,
617  foreach ($user->default_values[$relativepathstring]['sortorder'][$defkey] as $key => $val) {
618  if ($out) {
619  $out .= ', ';
620  }
621  if ($paramname == 'sortfield') {
622  $out .= dol_string_nospecial($key, '', $forbidden_chars_to_replace);
623  }
624  if ($paramname == 'sortorder') {
625  $out .= dol_string_nospecial($val, '', $forbidden_chars_to_replace);
626  }
627  }
628  //break; // No break for sortfield and sortorder so we can cumulate fields (is it realy usefull ?)
629  }
630  }
631  }
632  } elseif (isset($user->default_values[$relativepathstring]['filters'])) {
633  foreach ($user->default_values[$relativepathstring]['filters'] as $defkey => $defval) { // $defkey is a querystring like 'a=b&c=d', $defval is key of user
634  if (!empty($_GET['disabledefaultvalues'])) { // If set of default values has been disabled by a request parameter
635  continue;
636  }
637  $qualified = 0;
638  if ($defkey != '_noquery_') {
639  $tmpqueryarraytohave = explode('&', $defkey);
640  $tmpqueryarraywehave = explode('&', dol_string_nohtmltag($_SERVER['QUERY_STRING']));
641  $foundintru = 0;
642  foreach ($tmpqueryarraytohave as $tmpquerytohave) {
643  if (!in_array($tmpquerytohave, $tmpqueryarraywehave)) {
644  $foundintru = 1;
645  }
646  }
647  if (!$foundintru) {
648  $qualified = 1;
649  }
650  //var_dump($defkey.'-'.$qualified);
651  } else {
652  $qualified = 1;
653  }
654 
655  if ($qualified) {
656  // We must keep $_POST and $_GET here
657  if (isset($_POST['sall']) || isset($_POST['search_all']) || isset($_GET['sall']) || isset($_GET['search_all'])) {
658  // We made a search from quick search menu, do we still use default filter ?
659  if (empty($conf->global->MAIN_DISABLE_DEFAULT_FILTER_FOR_QUICK_SEARCH)) {
660  $forbidden_chars_to_replace = array(" ", "'", "/", "\\", ":", "*", "?", "\"", "<", ">", "|", "[", "]", ";", "="); // we accept _, -, . and ,
661  $out = dol_string_nospecial($user->default_values[$relativepathstring]['filters'][$defkey][$paramname], '', $forbidden_chars_to_replace);
662  }
663  } else {
664  $forbidden_chars_to_replace = array(" ", "'", "/", "\\", ":", "*", "?", "\"", "<", ">", "|", "[", "]", ";", "="); // we accept _, -, . and ,
665  $out = dol_string_nospecial($user->default_values[$relativepathstring]['filters'][$defkey][$paramname], '', $forbidden_chars_to_replace);
666  }
667  break;
668  }
669  }
670  }
671  }
672  }
673  }
674  }
675  }
676 
677  // Substitution variables for GETPOST (used to get final url with variable parameters or final default value with variable parameters)
678  // Example of variables: __DAY__, __MONTH__, __YEAR__, __MYCOMPANY_COUNTRY_ID__, __USER_ID__, ...
679  // 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.
680  if (!is_array($out) && empty($_POST[$paramname]) && empty($noreplace)) {
681  $reg = array();
682  $maxloop = 20;
683  $loopnb = 0; // Protection against infinite loop
684  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.
685  $loopnb++;
686  $newout = '';
687 
688  if ($reg[1] == 'DAY') {
689  $tmp = dol_getdate(dol_now(), true);
690  $newout = $tmp['mday'];
691  } elseif ($reg[1] == 'MONTH') {
692  $tmp = dol_getdate(dol_now(), true);
693  $newout = $tmp['mon'];
694  } elseif ($reg[1] == 'YEAR') {
695  $tmp = dol_getdate(dol_now(), true);
696  $newout = $tmp['year'];
697  } elseif ($reg[1] == 'PREVIOUS_DAY') {
698  $tmp = dol_getdate(dol_now(), true);
699  $tmp2 = dol_get_prev_day($tmp['mday'], $tmp['mon'], $tmp['year']);
700  $newout = $tmp2['day'];
701  } elseif ($reg[1] == 'PREVIOUS_MONTH') {
702  $tmp = dol_getdate(dol_now(), true);
703  $tmp2 = dol_get_prev_month($tmp['mon'], $tmp['year']);
704  $newout = $tmp2['month'];
705  } elseif ($reg[1] == 'PREVIOUS_YEAR') {
706  $tmp = dol_getdate(dol_now(), true);
707  $newout = ($tmp['year'] - 1);
708  } elseif ($reg[1] == 'NEXT_DAY') {
709  $tmp = dol_getdate(dol_now(), true);
710  $tmp2 = dol_get_next_day($tmp['mday'], $tmp['mon'], $tmp['year']);
711  $newout = $tmp2['day'];
712  } elseif ($reg[1] == 'NEXT_MONTH') {
713  $tmp = dol_getdate(dol_now(), true);
714  $tmp2 = dol_get_next_month($tmp['mon'], $tmp['year']);
715  $newout = $tmp2['month'];
716  } elseif ($reg[1] == 'NEXT_YEAR') {
717  $tmp = dol_getdate(dol_now(), true);
718  $newout = ($tmp['year'] + 1);
719  } elseif ($reg[1] == 'MYCOMPANY_COUNTRY_ID' || $reg[1] == 'MYCOUNTRY_ID' || $reg[1] == 'MYCOUNTRYID') {
720  $newout = $mysoc->country_id;
721  } elseif ($reg[1] == 'USER_ID' || $reg[1] == 'USERID') {
722  $newout = $user->id;
723  } elseif ($reg[1] == 'USER_SUPERVISOR_ID' || $reg[1] == 'SUPERVISOR_ID' || $reg[1] == 'SUPERVISORID') {
724  $newout = $user->fk_user;
725  } elseif ($reg[1] == 'ENTITY_ID' || $reg[1] == 'ENTITYID') {
726  $newout = $conf->entity;
727  } else {
728  $newout = ''; // Key not found, we replace with empty string
729  }
730  //var_dump('__'.$reg[1].'__ -> '.$newout);
731  $out = preg_replace('/__'.preg_quote($reg[1], '/').'__/', $newout, $out);
732  }
733  }
734 
735  // Check rule
736  if (preg_match('/^array/', $check)) { // If 'array' or 'array:restricthtml' or 'array:aZ09' or 'array:intcomma'
737  if (!is_array($out) || empty($out)) {
738  $out = array();
739  } else {
740  $tmparray = explode(':', $check);
741  if (!empty($tmparray[1])) {
742  $tmpcheck = $tmparray[1];
743  } else {
744  $tmpcheck = 'alphanohtml';
745  }
746  foreach ($out as $outkey => $outval) {
747  $out[$outkey] = sanitizeVal($outval, $tmpcheck, $filter, $options);
748  }
749  }
750  } else {
751  $out = sanitizeVal($out, $check, $filter, $options);
752  }
753 
754  // Sanitizing for special parameters.
755  // Note: There is no reason to allow the backtopage, backtolist or backtourl parameter to contains an external URL. Only relative URLs are allowed.
756  if ($paramname == 'backtopage' || $paramname == 'backtolist' || $paramname == 'backtourl') {
757  $out = str_replace('\\', '/', $out); // Can be before the loop because only 1 char is replaced. No risk to get it after other replacements.
758  $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.
759  do {
760  $oldstringtoclean = $out;
761  $out = str_ireplace(array('javascript', 'vbscript', '&colon', '&#'), '', $out);
762  $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'
763  $out = preg_replace(array('/^[a-z]*\/\s*\/+/i'), '', $out); // We remove schema*// to remove external URL
764  } while ($oldstringtoclean != $out);
765  }
766 
767  // Code for search criteria persistence.
768  // Save data into session if key start with 'search_' or is 'smonth', 'syear', 'month', 'year'
769  if (empty($method) || $method == 3 || $method == 4) {
770  if (preg_match('/^search_/', $paramname) || in_array($paramname, array('sortorder', 'sortfield'))) {
771  //var_dump($paramname.' - '.$out.' '.$user->default_values[$relativepathstring]['filters'][$paramname]);
772 
773  // We save search key only if $out not empty that means:
774  // - posted value not empty, or
775  // - 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).
776 
777  if ($out != '' && isset($user)) {// $out = '0' or 'abc', it is a search criteria to keep
778  $user->lastsearch_values_tmp[$relativepathstring][$paramname] = $out;
779  }
780  }
781  }
782 
783  return $out;
784 }
785 
795 function GETPOSTINT($paramname, $method = 0)
796 {
797  return (int) GETPOST($paramname, 'int', $method, null, null, 0);
798 }
799 
800 
811 function checkVal($out = '', $check = 'alphanohtml', $filter = null, $options = null)
812 {
813  return sanitizeVal($out, $check, $filter, $options);
814 }
815 
825 function sanitizeVal($out = '', $check = 'alphanohtml', $filter = null, $options = null)
826 {
827  global $conf;
828 
829  // TODO : use class "Validate" to perform tests (and add missing tests) if needed for factorize
830  // Check is done after replacement
831  switch ($check) {
832  case 'none':
833  break;
834  case 'int': // Check param is a numeric value (integer but also float or hexadecimal)
835  if (!is_numeric($out)) {
836  $out = '';
837  }
838  break;
839  case 'intcomma':
840  if (preg_match('/[^0-9,-]+/i', $out)) {
841  $out = '';
842  }
843  break;
844  case 'san_alpha':
845  $out = filter_var($out, FILTER_SANITIZE_STRING);
846  break;
847  case 'email':
848  $out = filter_var($out, FILTER_SANITIZE_EMAIL);
849  break;
850  case 'aZ':
851  if (!is_array($out)) {
852  $out = trim($out);
853  if (preg_match('/[^a-z]+/i', $out)) {
854  $out = '';
855  }
856  }
857  break;
858  case 'aZ09':
859  if (!is_array($out)) {
860  $out = trim($out);
861  if (preg_match('/[^a-z0-9_\-\.]+/i', $out)) {
862  $out = '';
863  }
864  }
865  break;
866  case 'aZ09comma': // great to sanitize sortfield or sortorder params that can be t.abc,t.def_gh
867  if (!is_array($out)) {
868  $out = trim($out);
869  if (preg_match('/[^a-z0-9_\-\.,]+/i', $out)) {
870  $out = '';
871  }
872  }
873  break;
874  case 'nohtml': // No html
875  $out = dol_string_nohtmltag($out, 0);
876  break;
877  case 'alpha': // No html and no ../ and "
878  case 'alphanohtml': // Recommended for most scalar parameters and search parameters
879  if (!is_array($out)) {
880  $out = trim($out);
881  do {
882  $oldstringtoclean = $out;
883  // Remove html tags
884  $out = dol_string_nohtmltag($out, 0);
885  // Remove also other dangerous string sequences
886  // '"' is dangerous because param in url can close the href= or src= and add javascript functions.
887  // '../' or '..\' is dangerous because it allows dir transversals
888  // Note &#38, '&#0000038', '&#x26'... is a simple char like '&' alone but there is no reason to accept such way to encode input data.
889  $out = str_ireplace(array('&#38', '&#0000038', '&#x26', '&quot', '&#34', '&#0000034', '&#x22', '"', '&#47', '&#0000047', '&#92', '&#0000092', '&#x2F', '../', '..\\'), '', $out);
890  } while ($oldstringtoclean != $out);
891  // keep lines feed
892  }
893  break;
894  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'
895  if (!is_array($out)) {
896  $out = trim($out);
897  do {
898  $oldstringtoclean = $out;
899  // Remove html tags
900  $out = dol_html_entity_decode($out, ENT_COMPAT | ENT_HTML5, 'UTF-8');
901  // '"' is dangerous because param in url can close the href= or src= and add javascript functions.
902  // '../' or '..\' is dangerous because it allows dir transversals
903  // Note &#38, '&#0000038', '&#x26'... is a simple char like '&' alone but there is no reason to accept such way to encode input data.
904  $out = str_ireplace(array('&#38', '&#0000038', '&#x26', '&quot', '&#34', '&#0000034', '&#x22', '"', '&#47', '&#0000047', '&#92', '&#0000092', '&#x2F', '../', '..\\'), '', $out);
905  } while ($oldstringtoclean != $out);
906  }
907  break;
908  case 'restricthtml': // Recommended for most html textarea
909  case 'restricthtmlallowunvalid':
910  do {
911  $oldstringtoclean = $out;
912 
913  if (!empty($out) && !empty($conf->global->MAIN_RESTRICTHTML_ONLY_VALID_HTML) && $check != 'restricthtmlallowunvalid') {
914  try {
915  $dom = new DOMDocument;
916  // Add a trick to solve pb with text without parent tag
917  // like '<h1>Foo</h1><p>bar</p>' that ends up with '<h1>Foo<p>bar</p></h1>'
918  // like 'abc' that ends up with '<p>abc</p>'
919  $out = '<div class="tricktoremove">'.$out.'</div>';
920 
921  $dom->loadHTML($out, LIBXML_ERR_NONE|LIBXML_HTML_NOIMPLIED|LIBXML_HTML_NODEFDTD|LIBXML_NONET|LIBXML_NOWARNING|LIBXML_NOXMLDECL);
922  $out = trim($dom->saveHTML());
923 
924  // Remove the trick added to solve pb with text without parent tag
925  $out = preg_replace('/^<div class="tricktoremove">/', '', $out);
926  $out = preg_replace('/<\/div>$/', '', $out);
927  } catch (Exception $e) {
928  //print $e->getMessage();
929  return 'InvalidHTMLString';
930  }
931  }
932 
933  // Ckeditor use the numeric entitic for apostrophe so we force it to text entity (all other special chars are
934  // encoded using text entities) so we can then exclude all numeric entities.
935  $out = preg_replace('/&#39;/i', '&apos;', $out);
936 
937  // We replace chars from a/A to z/Z encoded with numeric HTML entities with the real char so we won't loose the chars at the next step (preg_replace).
938  // No need to use a loop here, this step is not to sanitize (this is done at next step, this is to try to save chars, even if they are
939  // using a non coventionnel way to be encoded, to not have them sanitized just after)
940  //$out = preg_replace_callback('/&#(x?[0-9][0-9a-f]+;?)/i', 'realCharForNumericEntities', $out);
941  $out = preg_replace_callback('/&#(x?[0-9][0-9a-f]+;?)/i', function ($m) {
942  return realCharForNumericEntities($m); }, $out);
943 
944 
945  // Now we remove all remaining HTML entities starting with a number. We don't want such entities.
946  $out = preg_replace('/&#x?[0-9]+/i', '', $out); // For example if we have j&#x61vascript with an entities without the ; to hide the 'a' of 'javascript'.
947 
948  $out = dol_string_onlythesehtmltags($out, 0, 1, 1);
949 
950  // We should also exclude non expected HTML attributes and clean content of some attributes.
951  if (!empty($conf->global->MAIN_RESTRICTHTML_REMOVE_ALSO_BAD_ATTRIBUTES)) {
952  // Warning, the function may add a LF so we are forced to trim to compare with old $out without having always a difference and an infinit loop.
954  }
955 
956  // Restore entity &apos; into &#39; (restricthtml is for html content so we can use html entity)
957  $out = preg_replace('/&apos;/i', "&#39;", $out);
958  } while ($oldstringtoclean != $out);
959  break;
960  case 'custom':
961  if (empty($filter)) {
962  return 'BadFourthParameterForGETPOST';
963  }
964  $out = filter_var($out, $filter, $options);
965  break;
966  }
967 
968  return $out;
969 }
970 
971 
972 if (!function_exists('dol_getprefix')) {
982  function dol_getprefix($mode = '')
983  {
984  // If prefix is for email (we need to have $conf already loaded for this case)
985  if ($mode == 'email') {
986  global $conf;
987 
988  if (!empty($conf->global->MAIL_PREFIX_FOR_EMAIL_ID)) { // If MAIL_PREFIX_FOR_EMAIL_ID is set
989  if ($conf->global->MAIL_PREFIX_FOR_EMAIL_ID != 'SERVER_NAME') {
990  return $conf->global->MAIL_PREFIX_FOR_EMAIL_ID;
991  } elseif (isset($_SERVER["SERVER_NAME"])) { // If MAIL_PREFIX_FOR_EMAIL_ID is set to 'SERVER_NAME'
992  return $_SERVER["SERVER_NAME"];
993  }
994  }
995 
996  // The recommended value if MAIL_PREFIX_FOR_EMAIL_ID is not defined (may be not defined for old versions)
997  if (!empty($conf->file->instance_unique_id)) {
998  return sha1('dolibarr'.$conf->file->instance_unique_id);
999  }
1000 
1001  // For backward compatibility when instance_unique_id is not set
1002  return sha1(DOL_DOCUMENT_ROOT.DOL_URL_ROOT);
1003  }
1004 
1005  // If prefix is for session (no need to have $conf loaded)
1006  global $dolibarr_main_instance_unique_id, $dolibarr_main_cookie_cryptkey; // This is loaded by filefunc.inc.php
1007  $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
1008 
1009  // The recommended value (may be not defined for old versions)
1010  if (!empty($tmp_instance_unique_id)) {
1011  return sha1('dolibarr'.$tmp_instance_unique_id);
1012  }
1013 
1014  // For backward compatibility when instance_unique_id is not set
1015  if (isset($_SERVER["SERVER_NAME"]) && isset($_SERVER["DOCUMENT_ROOT"])) {
1016  return sha1($_SERVER["SERVER_NAME"].$_SERVER["DOCUMENT_ROOT"].DOL_DOCUMENT_ROOT.DOL_URL_ROOT);
1017  } else {
1018  return sha1(DOL_DOCUMENT_ROOT.DOL_URL_ROOT);
1019  }
1020  }
1021 }
1022 
1033 function dol_include_once($relpath, $classname = '')
1034 {
1035  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']
1036 
1037  $fullpath = dol_buildpath($relpath);
1038 
1039  if (!file_exists($fullpath)) {
1040  dol_syslog('functions::dol_include_once Tried to load unexisting file: '.$relpath, LOG_WARNING);
1041  return false;
1042  }
1043 
1044  if (!empty($classname) && !class_exists($classname)) {
1045  return include $fullpath;
1046  } else {
1047  return include_once $fullpath;
1048  }
1049 }
1050 
1051 
1062 function dol_buildpath($path, $type = 0, $returnemptyifnotfound = 0)
1063 {
1064  global $conf;
1065 
1066  $path = preg_replace('/^\//', '', $path);
1067 
1068  if (empty($type)) { // For a filesystem path
1069  $res = DOL_DOCUMENT_ROOT.'/'.$path; // Standard default path
1070  if (is_array($conf->file->dol_document_root)) {
1071  foreach ($conf->file->dol_document_root as $key => $dirroot) { // ex: array("main"=>"/home/main/htdocs", "alt0"=>"/home/dirmod/htdocs", ...)
1072  if ($key == 'main') {
1073  continue;
1074  }
1075  if (file_exists($dirroot.'/'.$path)) {
1076  $res = $dirroot.'/'.$path;
1077  return $res;
1078  }
1079  }
1080  }
1081  if ($returnemptyifnotfound) {
1082  // Not found into alternate dir
1083  if ($returnemptyifnotfound == 1 || !file_exists($res)) {
1084  return '';
1085  }
1086  }
1087  } else {
1088  // For an url path
1089  // We try to get local path of file on filesystem from url
1090  // Note that trying to know if a file on disk exist by forging path on disk from url
1091  // works only for some web server and some setup. This is bugged when
1092  // using proxy, rewriting, virtual path, etc...
1093  $res = '';
1094  if ($type == 1) {
1095  $res = DOL_URL_ROOT.'/'.$path; // Standard value
1096  }
1097  if ($type == 2) {
1098  $res = DOL_MAIN_URL_ROOT.'/'.$path; // Standard value
1099  }
1100  if ($type == 3) {
1101  $res = DOL_URL_ROOT.'/'.$path;
1102  }
1103 
1104  foreach ($conf->file->dol_document_root as $key => $dirroot) { // ex: array(["main"]=>"/home/main/htdocs", ["alt0"]=>"/home/dirmod/htdocs", ...)
1105  if ($key == 'main') {
1106  if ($type == 3) {
1107  global $dolibarr_main_url_root;
1108 
1109  // Define $urlwithroot
1110  $urlwithouturlroot = preg_replace('/'.preg_quote(DOL_URL_ROOT, '/').'$/i', '', trim($dolibarr_main_url_root));
1111  $urlwithroot = $urlwithouturlroot.DOL_URL_ROOT; // This is to use external domain name found into config file
1112  //$urlwithroot=DOL_MAIN_URL_ROOT; // This is to use same domain name than current
1113 
1114  $res = (preg_match('/^http/i', $conf->file->dol_url_root[$key]) ? '' : $urlwithroot).'/'.$path; // Test on start with http is for old conf syntax
1115  }
1116  continue;
1117  }
1118  preg_match('/^([^\?]+(\.css\.php|\.css|\.js\.php|\.js|\.png|\.jpg|\.php)?)/i', $path, $regs); // Take part before '?'
1119  if (!empty($regs[1])) {
1120  //print $key.'-'.$dirroot.'/'.$path.'-'.$conf->file->dol_url_root[$type].'<br>'."\n";
1121  if (file_exists($dirroot.'/'.$regs[1])) {
1122  if ($type == 1) {
1123  $res = (preg_match('/^http/i', $conf->file->dol_url_root[$key]) ? '' : DOL_URL_ROOT).$conf->file->dol_url_root[$key].'/'.$path;
1124  }
1125  if ($type == 2) {
1126  $res = (preg_match('/^http/i', $conf->file->dol_url_root[$key]) ? '' : DOL_MAIN_URL_ROOT).$conf->file->dol_url_root[$key].'/'.$path;
1127  }
1128  if ($type == 3) {
1129  global $dolibarr_main_url_root;
1130 
1131  // Define $urlwithroot
1132  $urlwithouturlroot = preg_replace('/'.preg_quote(DOL_URL_ROOT, '/').'$/i', '', trim($dolibarr_main_url_root));
1133  $urlwithroot = $urlwithouturlroot.DOL_URL_ROOT; // This is to use external domain name found into config file
1134  //$urlwithroot=DOL_MAIN_URL_ROOT; // This is to use same domain name than current
1135 
1136  $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
1137  }
1138  break;
1139  }
1140  }
1141  }
1142  }
1143 
1144  return $res;
1145 }
1146 
1158 function dol_clone($object, $native = 0)
1159 {
1160  if ($native == 0) {
1161  // deprecated method, use the method with native = 2 instead
1162  $tmpsavdb = null;
1163  if (isset($object->db) && isset($object->db->db) && is_object($object->db->db) && get_class($object->db->db) == 'PgSql\Connection') {
1164  $tmpsavdb = $object->db;
1165  unset($object->db); // Such property can not be serialized with pgsl (when object->db->db = 'PgSql\Connection')
1166  }
1167 
1168  $myclone = unserialize(serialize($object)); // serialize then unserialize is a hack to be sure to have a new object for all fields
1169 
1170  if (!empty($tmpsavdb)) {
1171  $object->db = $tmpsavdb;
1172  }
1173  } elseif ($native == 2) {
1174  // recommended method to have a full isolated cloned object
1175  $myclone = new stdClass();
1176  $tmparray = get_object_vars($object); // return only public properties
1177 
1178  if (is_array($tmparray)) {
1179  foreach ($tmparray as $propertykey => $propertyval) {
1180  if (is_scalar($propertyval) || is_array($propertyval)) {
1181  $myclone->$propertykey = $propertyval;
1182  }
1183  }
1184  }
1185  } else {
1186  $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)
1187  }
1188 
1189  return $myclone;
1190 }
1191 
1201 function dol_size($size, $type = '')
1202 {
1203  global $conf;
1204  if (empty($conf->dol_optimize_smallscreen)) {
1205  return $size;
1206  }
1207  if ($type == 'width' && $size > 250) {
1208  return 250;
1209  } else {
1210  return 10;
1211  }
1212 }
1213 
1214 
1226 function dol_sanitizeFileName($str, $newstr = '_', $unaccent = 1)
1227 {
1228  // List of special chars for filenames in windows are defined on page https://docs.microsoft.com/en-us/windows/win32/fileio/naming-a-file
1229  // Char '>' '<' '|' '$' and ';' are special chars for shells.
1230  // Char '/' and '\' are file delimiters.
1231  // 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
1232  $filesystem_forbidden_chars = array('<', '>', '/', '\\', '?', '*', '|', '"', ':', '°', '$', ';');
1233  $tmp = dol_string_nospecial($unaccent ? dol_string_unaccent($str) : $str, $newstr, $filesystem_forbidden_chars);
1234  $tmp = preg_replace('/\-\-+/', '_', $tmp);
1235  $tmp = preg_replace('/\s+\-([^\s])/', ' _$1', $tmp);
1236  $tmp = str_replace('..', '', $tmp);
1237  return $tmp;
1238 }
1239 
1251 function dol_sanitizePathName($str, $newstr = '_', $unaccent = 1)
1252 {
1253  // List of special chars for filenames in windows are defined on page https://docs.microsoft.com/en-us/windows/win32/fileio/naming-a-file
1254  // Char '>' '<' '|' '$' and ';' are special chars for shells.
1255  // Chars '--' can be used into filename to inject special paramaters like --use-compress-program to make command with file as parameter making remote execution of command
1256  $filesystem_forbidden_chars = array('<', '>', '?', '*', '|', '"', '°', '$', ';');
1257  $tmp = dol_string_nospecial($unaccent ? dol_string_unaccent($str) : $str, $newstr, $filesystem_forbidden_chars);
1258  $tmp = preg_replace('/\-\-+/', '_', $tmp);
1259  $tmp = preg_replace('/\s+\-([^\s])/', ' _$1', $tmp);
1260  $tmp = str_replace('..', '', $tmp);
1261  return $tmp;
1262 }
1263 
1271 function dol_sanitizeUrl($stringtoclean, $type = 1)
1272 {
1273  // 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)
1274  // We should use dol_string_nounprintableascii but function may not be yet loaded/available
1275  $stringtoclean = preg_replace('/[\x00-\x1F\x7F]/u', '', $stringtoclean); // /u operator makes UTF8 valid characters being ignored so are not included into the replace
1276  // We clean html comments because some hacks try to obfuscate evil strings by inserting HTML comments. Example: on<!-- -->error=alert(1)
1277  $stringtoclean = preg_replace('/<!--[^>]*-->/', '', $stringtoclean);
1278 
1279  $stringtoclean = str_replace('\\', '/', $stringtoclean);
1280  if ($type == 1) {
1281  // removing : should disable links to external url like http:aaa)
1282  // removing ';' should disable "named" html entities encode into an url (we should not have this into an url)
1283  $stringtoclean = str_replace(array(':', ';', '@'), '', $stringtoclean);
1284  }
1285 
1286  do {
1287  $oldstringtoclean = $stringtoclean;
1288  // removing '&colon' should disable links to external url like http:aaa)
1289  // removing '&#' should disable "numeric" html entities encode into an url (we should not have this into an url)
1290  $stringtoclean = str_ireplace(array('javascript', 'vbscript', '&colon', '&#'), '', $stringtoclean);
1291  } while ($oldstringtoclean != $stringtoclean);
1292 
1293  if ($type == 1) {
1294  // removing '//' should disable links to external url like //aaa or http//)
1295  $stringtoclean = preg_replace(array('/^[a-z]*\/\/+/i'), '', $stringtoclean);
1296  }
1297 
1298  return $stringtoclean;
1299 }
1300 
1309 function dol_string_unaccent($str)
1310 {
1311  global $conf;
1312 
1313  if (utf8_check($str)) {
1314  if (extension_loaded('intl') && !empty($conf->global->MAIN_UNACCENT_USE_TRANSLITERATOR)) {
1315  $transliterator = \Transliterator::createFromRules(':: Any-Latin; :: Latin-ASCII; :: NFD; :: [:Nonspacing Mark:] Remove; :: NFC;', \Transliterator::FORWARD);
1316  return $transliterator->transliterate($str);
1317  }
1318  // See http://www.utf8-chartable.de/
1319  $string = rawurlencode($str);
1320  $replacements = array(
1321  '%C3%80' => 'A', '%C3%81' => 'A', '%C3%82' => 'A', '%C3%83' => 'A', '%C3%84' => 'A', '%C3%85' => 'A',
1322  '%C3%87' => 'C',
1323  '%C3%88' => 'E', '%C3%89' => 'E', '%C3%8A' => 'E', '%C3%8B' => 'E',
1324  '%C3%8C' => 'I', '%C3%8D' => 'I', '%C3%8E' => 'I', '%C3%8F' => 'I',
1325  '%C3%91' => 'N',
1326  '%C3%92' => 'O', '%C3%93' => 'O', '%C3%94' => 'O', '%C3%95' => 'O', '%C3%96' => 'O',
1327  '%C5%A0' => 'S',
1328  '%C3%99' => 'U', '%C3%9A' => 'U', '%C3%9B' => 'U', '%C3%9C' => 'U',
1329  '%C3%9D' => 'Y', '%C5%B8' => 'y',
1330  '%C3%A0' => 'a', '%C3%A1' => 'a', '%C3%A2' => 'a', '%C3%A3' => 'a', '%C3%A4' => 'a', '%C3%A5' => 'a',
1331  '%C3%A7' => 'c',
1332  '%C3%A8' => 'e', '%C3%A9' => 'e', '%C3%AA' => 'e', '%C3%AB' => 'e',
1333  '%C3%AC' => 'i', '%C3%AD' => 'i', '%C3%AE' => 'i', '%C3%AF' => 'i',
1334  '%C3%B1' => 'n',
1335  '%C3%B2' => 'o', '%C3%B3' => 'o', '%C3%B4' => 'o', '%C3%B5' => 'o', '%C3%B6' => 'o',
1336  '%C5%A1' => 's',
1337  '%C3%B9' => 'u', '%C3%BA' => 'u', '%C3%BB' => 'u', '%C3%BC' => 'u',
1338  '%C3%BD' => 'y', '%C3%BF' => 'y'
1339  );
1340  $string = strtr($string, $replacements);
1341  return rawurldecode($string);
1342  } else {
1343  // See http://www.ascii-code.com/
1344  $string = strtr(
1345  $str,
1346  "\xC0\xC1\xC2\xC3\xC4\xC5\xC7
1347  \xC8\xC9\xCA\xCB\xCC\xCD\xCE\xCF\xD0\xD1
1348  \xD2\xD3\xD4\xD5\xD8\xD9\xDA\xDB\xDD
1349  \xE0\xE1\xE2\xE3\xE4\xE5\xE7\xE8\xE9\xEA\xEB
1350  \xEC\xED\xEE\xEF\xF0\xF1\xF2\xF3\xF4\xF5\xF8
1351  \xF9\xFA\xFB\xFC\xFD\xFF",
1352  "AAAAAAC
1353  EEEEIIIIDN
1354  OOOOOUUUY
1355  aaaaaaceeee
1356  iiiidnooooo
1357  uuuuyy"
1358  );
1359  $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"));
1360  return $string;
1361  }
1362 }
1363 
1376 function dol_string_nospecial($str, $newstr = '_', $badcharstoreplace = '', $badcharstoremove = '')
1377 {
1378  $forbidden_chars_to_replace = array(" ", "'", "/", "\\", ":", "*", "?", "\"", "<", ">", "|", "[", "]", ",", ";", "=", '°'); // more complete than dol_sanitizeFileName
1379  $forbidden_chars_to_remove = array();
1380  //$forbidden_chars_to_remove=array("(",")");
1381 
1382  if (is_array($badcharstoreplace)) {
1383  $forbidden_chars_to_replace = $badcharstoreplace;
1384  }
1385  if (is_array($badcharstoremove)) {
1386  $forbidden_chars_to_remove = $badcharstoremove;
1387  }
1388 
1389  return str_replace($forbidden_chars_to_replace, $newstr, str_replace($forbidden_chars_to_remove, "", $str));
1390 }
1391 
1392 
1406 function dol_string_nounprintableascii($str, $removetabcrlf = 1)
1407 {
1408  if ($removetabcrlf) {
1409  return preg_replace('/[\x00-\x1F\x7F]/u', '', $str); // /u operator makes UTF8 valid characters being ignored so are not included into the replace
1410  } else {
1411  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
1412  }
1413 }
1414 
1423 function dol_escape_js($stringtoescape, $mode = 0, $noescapebackslashn = 0)
1424 {
1425  // escape quotes and backslashes, newlines, etc.
1426  $substitjs = array("&#039;"=>"\\'", "\r"=>'\\r');
1427  //$substitjs['</']='<\/'; // We removed this. Should be useless.
1428  if (empty($noescapebackslashn)) {
1429  $substitjs["\n"] = '\\n';
1430  $substitjs['\\'] = '\\\\';
1431  }
1432  if (empty($mode)) {
1433  $substitjs["'"] = "\\'";
1434  $substitjs['"'] = "\\'";
1435  } elseif ($mode == 1) {
1436  $substitjs["'"] = "\\'";
1437  } elseif ($mode == 2) {
1438  $substitjs['"'] = '\\"';
1439  } elseif ($mode == 3) {
1440  $substitjs["'"] = "\\'";
1441  $substitjs['"'] = "\\\"";
1442  }
1443  return strtr($stringtoescape, $substitjs);
1444 }
1445 
1452 function dol_escape_json($stringtoescape)
1453 {
1454  return str_replace('"', '\"', $stringtoescape);
1455 }
1456 
1468 function dol_escape_htmltag($stringtoescape, $keepb = 0, $keepn = 0, $noescapetags = '', $escapeonlyhtmltags = 0)
1469 {
1470  if ($noescapetags == 'common') {
1471  $noescapetags = 'html,body,a,b,em,hr,i,u,ul,li,br,div,img,font,p,span,strong,table,tr,td,th,tbody';
1472  }
1473 
1474  // escape quotes and backslashes, newlines, etc.
1475  if ($escapeonlyhtmltags) {
1476  $tmp = htmlspecialchars_decode((string) $stringtoescape, ENT_COMPAT);
1477  } else {
1478  $tmp = html_entity_decode((string) $stringtoescape, ENT_COMPAT, 'UTF-8');
1479  }
1480  if (!$keepb) {
1481  $tmp = strtr($tmp, array("<b>"=>'', '</b>'=>''));
1482  }
1483  if (!$keepn) {
1484  $tmp = strtr($tmp, array("\r"=>'\\r', "\n"=>'\\n'));
1485  }
1486 
1487  if ($escapeonlyhtmltags) {
1488  return htmlspecialchars($tmp, ENT_COMPAT, 'UTF-8');
1489  } else {
1490  // Escape tags to keep
1491  // TODO Does not works yet when there is attributes to tag
1492  $tmparrayoftags = array();
1493  if ($noescapetags) {
1494  $tmparrayoftags = explode(',', $noescapetags);
1495  }
1496  if (count($tmparrayoftags)) {
1497  foreach ($tmparrayoftags as $tagtoreplace) {
1498  $tmp = str_ireplace('<'.$tagtoreplace.'>', '__BEGINTAGTOREPLACE'.$tagtoreplace.'__', $tmp);
1499  $tmp = str_ireplace('</'.$tagtoreplace.'>', '__ENDTAGTOREPLACE'.$tagtoreplace.'__', $tmp);
1500  $tmp = str_ireplace('<'.$tagtoreplace.' />', '__BEGINENDTAGTOREPLACE'.$tagtoreplace.'__', $tmp);
1501  }
1502  }
1503 
1504  $result = htmlentities($tmp, ENT_COMPAT, 'UTF-8');
1505 
1506  if (count($tmparrayoftags)) {
1507  foreach ($tmparrayoftags as $tagtoreplace) {
1508  $result = str_ireplace('__BEGINTAGTOREPLACE'.$tagtoreplace.'__', '<'.$tagtoreplace.'>', $result);
1509  $result = str_ireplace('__ENDTAGTOREPLACE'.$tagtoreplace.'__', '</'.$tagtoreplace.'>', $result);
1510  $result = str_ireplace('__BEGINENDTAGTOREPLACE'.$tagtoreplace.'__', '<'.$tagtoreplace.' />', $result);
1511  }
1512  }
1513 
1514  return $result;
1515  }
1516 }
1517 
1525 function dol_strtolower($string, $encoding = "UTF-8")
1526 {
1527  if (function_exists('mb_strtolower')) {
1528  return mb_strtolower($string, $encoding);
1529  } else {
1530  return strtolower($string);
1531  }
1532 }
1533 
1541 function dol_strtoupper($string, $encoding = "UTF-8")
1542 {
1543  if (function_exists('mb_strtoupper')) {
1544  return mb_strtoupper($string, $encoding);
1545  } else {
1546  return strtoupper($string);
1547  }
1548 }
1549 
1557 function dol_ucfirst($string, $encoding = "UTF-8")
1558 {
1559  if (function_exists('mb_substr')) {
1560  return mb_strtoupper(mb_substr($string, 0, 1, $encoding), $encoding).mb_substr($string, 1, null, $encoding);
1561  } else {
1562  return ucfirst($string);
1563  }
1564 }
1565 
1573 function dol_ucwords($string, $encoding = "UTF-8")
1574 {
1575  if (function_exists('mb_convert_case')) {
1576  return mb_convert_case($string, MB_CASE_TITLE, $encoding);
1577  } else {
1578  return ucwords($string);
1579  }
1580 }
1581 
1603 function dol_syslog($message, $level = LOG_INFO, $ident = 0, $suffixinfilename = '', $restricttologhandler = '', $logcontext = null)
1604 {
1605  global $conf, $user, $debugbar;
1606 
1607  // If syslog module enabled
1608  if (empty($conf->syslog->enabled)) {
1609  return;
1610  }
1611 
1612  // Check if we are into execution of code of a website
1613  if (defined('USEEXTERNALSERVER') && !defined('USEDOLIBARRSERVER') && !defined('USEDOLIBARREDITOR')) {
1614  global $website, $websitekey;
1615  if (is_object($website) && !empty($website->ref)) {
1616  $suffixinfilename .= '_website_'.$website->ref;
1617  } elseif (!empty($websitekey)) {
1618  $suffixinfilename .= '_website_'.$websitekey;
1619  }
1620  }
1621 
1622  // Check if we have a forced suffix
1623  if (defined('USESUFFIXINLOG')) {
1624  $suffixinfilename .= constant('USESUFFIXINLOG');
1625  }
1626 
1627  if ($ident < 0) {
1628  foreach ($conf->loghandlers as $loghandlerinstance) {
1629  $loghandlerinstance->setIdent($ident);
1630  }
1631  }
1632 
1633  if (!empty($message)) {
1634  // Test log level
1635  $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');
1636  if (!array_key_exists($level, $logLevels)) {
1637  throw new Exception('Incorrect log level');
1638  }
1639  if ($level > $conf->global->SYSLOG_LEVEL) {
1640  return;
1641  }
1642 
1643  if (empty($conf->global->MAIN_SHOW_PASSWORD_INTO_LOG)) {
1644  $message = preg_replace('/password=\'[^\']*\'/', 'password=\'hidden\'', $message); // protection to avoid to have value of password in log
1645  }
1646 
1647  // If adding log inside HTML page is required
1648  if ((!empty($_REQUEST['logtohtml']) && !empty($conf->global->MAIN_ENABLE_LOG_TO_HTML))
1649  || (!empty($user->rights->debugbar->read) && is_object($debugbar))) {
1650  $conf->logbuffer[] = dol_print_date(time(), "%Y-%m-%d %H:%M:%S")." ".$logLevels[$level]." ".$message;
1651  }
1652 
1653  //TODO: Remove this. MAIN_ENABLE_LOG_INLINE_HTML should be deprecated and use a log handler dedicated to HTML output
1654  // If html log tag enabled and url parameter log defined, we show output log on HTML comments
1655  if (!empty($conf->global->MAIN_ENABLE_LOG_INLINE_HTML) && !empty($_GET["log"])) {
1656  print "\n\n<!-- Log start\n";
1657  print dol_escape_htmltag($message)."\n";
1658  print "Log end -->\n";
1659  }
1660 
1661  $data = array(
1662  'message' => $message,
1663  'script' => (isset($_SERVER['PHP_SELF']) ? basename($_SERVER['PHP_SELF'], '.php') : false),
1664  'level' => $level,
1665  'user' => ((is_object($user) && $user->id) ? $user->login : false),
1666  'ip' => false
1667  );
1668 
1669  $remoteip = getUserRemoteIP(); // Get ip when page run on a web server
1670  if (!empty($remoteip)) {
1671  $data['ip'] = $remoteip;
1672  // This is when server run behind a reverse proxy
1673  if (!empty($_SERVER['HTTP_X_FORWARDED_FOR']) && $_SERVER['HTTP_X_FORWARDED_FOR'] != $remoteip) {
1674  $data['ip'] = $_SERVER['HTTP_X_FORWARDED_FOR'].' -> '.$data['ip'];
1675  } elseif (!empty($_SERVER['HTTP_CLIENT_IP']) && $_SERVER['HTTP_CLIENT_IP'] != $remoteip) {
1676  $data['ip'] = $_SERVER['HTTP_CLIENT_IP'].' -> '.$data['ip'];
1677  }
1678  } elseif (!empty($_SERVER['SERVER_ADDR'])) {
1679  // This is when PHP session is ran inside a web server but not inside a client request (example: init code of apache)
1680  $data['ip'] = $_SERVER['SERVER_ADDR'];
1681  } elseif (!empty($_SERVER['COMPUTERNAME'])) {
1682  // 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).
1683  $data['ip'] = $_SERVER['COMPUTERNAME'].(empty($_SERVER['USERNAME']) ? '' : '@'.$_SERVER['USERNAME']);
1684  } elseif (!empty($_SERVER['LOGNAME'])) {
1685  // 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).
1686  $data['ip'] = '???@'.$_SERVER['LOGNAME'];
1687  }
1688 
1689  // Loop on each log handler and send output
1690  foreach ($conf->loghandlers as $loghandlerinstance) {
1691  if ($restricttologhandler && $loghandlerinstance->code != $restricttologhandler) {
1692  continue;
1693  }
1694  $loghandlerinstance->export($data, $suffixinfilename);
1695  }
1696  unset($data);
1697  }
1698 
1699  if ($ident > 0) {
1700  foreach ($conf->loghandlers as $loghandlerinstance) {
1701  $loghandlerinstance->setIdent($ident);
1702  }
1703  }
1704 }
1705 
1720 function dolButtonToOpenUrlInDialogPopup($name, $label, $buttonstring, $url, $disabled = '', $morecss = 'button bordertransp', $backtopagejsfields = '')
1721 {
1722  if (strpos($url, '?') > 0) {
1723  $url .= '&dol_hide_topmenu=1&dol_hide_leftmenu=1&dol_openinpopup='.urlencode($name);
1724  } else {
1725  $url .= '?dol_hide_topmenu=1&dol_hide_leftmenu=1&dol_openinpopup='.urlencode($name);
1726  }
1727 
1728  $out = '';
1729 
1730  $backtopagejsfieldsid = ''; $backtopagejsfieldslabel = '';
1731  if ($backtopagejsfields) {
1732  $tmpbacktopagejsfields = explode(':', $backtopagejsfields);
1733  if (empty($tmpbacktopagejsfields[1])) { // If the part 'keyforpopupid:' is missing, we add $name for it.
1734  $backtopagejsfields = $name.":".$backtopagejsfields;
1735  $tmp2backtopagejsfields = explode(',', $tmpbacktopagejsfields[0]);
1736  } else {
1737  $tmp2backtopagejsfields = explode(',', $tmpbacktopagejsfields[1]);
1738  }
1739  $backtopagejsfieldsid = empty($tmp2backtopagejsfields[0]) ? '' : $tmp2backtopagejsfields[0];
1740  $backtopagejsfieldslabel = empty($tmp2backtopagejsfields[1]) ? '' : $tmp2backtopagejsfields[1];
1741  $url .= '&backtopagejsfields='.urlencode($backtopagejsfields);
1742  }
1743 
1744  //print '<input type="submit" class="button bordertransp"'.$disabled.' value="'.dol_escape_htmltag($langs->trans("MediaFiles")).'" name="file_manager">';
1745  $out .= '<!-- a link for button to open url into a dialog popup backtopagejsfields = '.$backtopagejsfields.' -->'."\n";
1746  $out .= '<a class="cursorpointer button_'.$name.($morecss ? ' '.$morecss : '').'"'.$disabled.' title="'.dol_escape_htmltag($label).'">'.$buttonstring.'</a>';
1747  $out .= '<div id="idfordialog'.$name.'" class="hidden">div for dialog</div>';
1748  $out .= '<div id="varforreturndialogid'.$name.'" class="hidden">div for returned id</div>';
1749  $out .= '<div id="varforreturndialoglabel'.$name.'" class="hidden">div for returned label</div>';
1750  $out .= '<!-- Add js code to open dialog popup on dialog -->';
1751  $out .= '<script type="text/javascript">
1752  jQuery(document).ready(function () {
1753  jQuery(".button_'.$name.'").click(function () {
1754  console.log(\'Open popup with jQuery(...).dialog() on URL '.dol_escape_js(DOL_URL_ROOT.$url).'\');
1755  var $tmpdialog = $(\'#idfordialog'.$name.'\');
1756  $tmpdialog.html(\'<iframe class="iframedialog" id="iframedialog'.$name.'" style="border: 0px;" src="'.DOL_URL_ROOT.$url.'" width="100%" height="98%"></iframe>\');
1757  $tmpdialog.dialog({
1758  autoOpen: false,
1759  modal: true,
1760  height: (window.innerHeight - 150),
1761  width: \'80%\',
1762  title: \''.dol_escape_js($label).'\',
1763  open: function (event, ui) {
1764  console.log("open popup name='.$name.', backtopagejsfields='.$backtopagejsfields.'");
1765  },
1766  close: function (event, ui) {
1767  returnedid = jQuery("#varforreturndialogid'.$name.'").text();
1768  returnedlabel = jQuery("#varforreturndialoglabel'.$name.'").text();
1769  console.log("popup has been closed. returnedid (js var defined into parent page)="+returnedid+" returnedlabel="+returnedlabel);
1770  if (returnedid != "" && returnedid != "div for returned id") {
1771  jQuery("#'.(empty($backtopagejsfieldsid)?"none":$backtopagejsfieldsid).'").val(returnedid);
1772  }
1773  if (returnedlabel != "" && returnedlabel != "div for returned label") {
1774  jQuery("#'.(empty($backtopagejsfieldslabel)?"none":$backtopagejsfieldslabel).'").val(returnedlabel);
1775  }
1776  }
1777  });
1778 
1779  $tmpdialog.dialog(\'open\');
1780  });
1781  });
1782  </script>';
1783  return $out;
1784 }
1785 
1802 function dol_fiche_head($links = array(), $active = '0', $title = '', $notab = 0, $picto = '', $pictoisfullpath = 0, $morehtmlright = '', $morecss = '', $limittoshow = 0, $moretabssuffix = '')
1803 {
1804  print dol_get_fiche_head($links, $active, $title, $notab, $picto, $pictoisfullpath, $morehtmlright, $morecss, $limittoshow, $moretabssuffix);
1805 }
1806 
1822 function dol_get_fiche_head($links = array(), $active = '', $title = '', $notab = 0, $picto = '', $pictoisfullpath = 0, $morehtmlright = '', $morecss = '', $limittoshow = 0, $moretabssuffix = '')
1823 {
1824  global $conf, $langs, $hookmanager;
1825 
1826  // Show title
1827  $showtitle = 1;
1828  if (!empty($conf->dol_optimize_smallscreen)) {
1829  $showtitle = 0;
1830  }
1831 
1832  $out = "\n".'<!-- dol_fiche_head - dol_get_fiche_head -->';
1833 
1834  if ((!empty($title) && $showtitle) || $morehtmlright || !empty($links)) {
1835  $out .= '<div class="tabs'.($picto ? '' : ' nopaddingleft').'" data-role="controlgroup" data-type="horizontal">'."\n";
1836  }
1837 
1838  // Show right part
1839  if ($morehtmlright) {
1840  $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.
1841  }
1842 
1843  // Show title
1844  if (!empty($title) && $showtitle && empty($conf->global->MAIN_OPTIMIZEFORTEXTBROWSER)) {
1845  $limittitle = 30;
1846  $out .= '<a class="tabTitle">';
1847  if ($picto) {
1848  $noprefix = $pictoisfullpath;
1849  if (strpos($picto, 'fontawesome_') !== false) {
1850  $noprefix = 1;
1851  }
1852  $out .= img_picto($title, ($noprefix ? '' : 'object_').$picto, '', $pictoisfullpath, 0, 0, '', 'imgTabTitle').' ';
1853  }
1854  $out .= '<span class="tabTitleText">'.dol_escape_htmltag(dol_trunc($title, $limittitle)).'</span>';
1855  $out .= '</a>';
1856  }
1857 
1858  // Show tabs
1859 
1860  // Define max of key (max may be higher than sizeof because of hole due to module disabling some tabs).
1861  $maxkey = -1;
1862  if (is_array($links) && !empty($links)) {
1863  $keys = array_keys($links);
1864  if (count($keys)) {
1865  $maxkey = max($keys);
1866  }
1867  }
1868 
1869  // Show tabs
1870  // if =0 we don't use the feature
1871  if (empty($limittoshow)) {
1872  $limittoshow = (empty($conf->global->MAIN_MAXTABS_IN_CARD) ? 99 : $conf->global->MAIN_MAXTABS_IN_CARD);
1873  }
1874  if (!empty($conf->dol_optimize_smallscreen)) {
1875  $limittoshow = 2;
1876  }
1877 
1878  $displaytab = 0;
1879  $nbintab = 0;
1880  $popuptab = 0;
1881  $outmore = '';
1882  for ($i = 0; $i <= $maxkey; $i++) {
1883  if ((is_numeric($active) && $i == $active) || (!empty($links[$i][2]) && !is_numeric($active) && $active == $links[$i][2])) {
1884  // If active tab is already present
1885  if ($i >= $limittoshow) {
1886  $limittoshow--;
1887  }
1888  }
1889  }
1890 
1891  for ($i = 0; $i <= $maxkey; $i++) {
1892  if ((is_numeric($active) && $i == $active) || (!empty($links[$i][2]) && !is_numeric($active) && $active == $links[$i][2])) {
1893  $isactive = true;
1894  } else {
1895  $isactive = false;
1896  }
1897 
1898  if ($i < $limittoshow || $isactive) {
1899  // Output entry with a visible tab
1900  $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])).' -->';
1901 
1902  if (isset($links[$i][2]) && $links[$i][2] == 'image') {
1903  if (!empty($links[$i][0])) {
1904  $out .= '<a class="tabimage'.($morecss ? ' '.$morecss : '').'" href="'.$links[$i][0].'">'.$links[$i][1].'</a>'."\n";
1905  } else {
1906  $out .= '<span class="tabspan">'.$links[$i][1].'</span>'."\n";
1907  }
1908  } elseif (!empty($links[$i][1])) {
1909  //print "x $i $active ".$links[$i][2]." z";
1910  $out .= '<div class="tab tab'.($isactive?'active':'unactive').'" style="margin: 0 !important">';
1911  if (!empty($links[$i][0])) {
1912  $titletoshow = preg_replace('/<.*$/', '', $links[$i][1]);
1913  $out .= '<a'.(!empty($links[$i][2]) ? ' id="'.$links[$i][2].'"' : '').' class="tab inline-block valignmiddle'.($morecss ? ' '.$morecss : '').'" href="'.$links[$i][0].'" title="'.dol_escape_htmltag($titletoshow).'">';
1914  }
1915  $out .= $links[$i][1];
1916  if (!empty($links[$i][0])) {
1917  $out .= '</a>'."\n";
1918  }
1919  $out .= empty($links[$i][4]) ? '' : $links[$i][4];
1920  $out .= '</div>';
1921  }
1922 
1923  $out .= '</div>';
1924  } else {
1925  // Add entry into the combo popup with the other tabs
1926  if (!$popuptab) {
1927  $popuptab = 1;
1928  $outmore .= '<div class="popuptabset wordwrap">'; // The css used to hide/show popup
1929  }
1930  $outmore .= '<div class="popuptab wordwrap" style="display:inherit;">';
1931  if (isset($links[$i][2]) && $links[$i][2] == 'image') {
1932  if (!empty($links[$i][0])) {
1933  $outmore .= '<a class="tabimage'.($morecss ? ' '.$morecss : '').'" href="'.$links[$i][0].'">'.$links[$i][1].'</a>'."\n";
1934  } else {
1935  $outmore .= '<span class="tabspan">'.$links[$i][1].'</span>'."\n";
1936  }
1937  } elseif (!empty($links[$i][1])) {
1938  $outmore .= '<a'.(!empty($links[$i][2]) ? ' id="'.$links[$i][2].'"' : '').' class="wordwrap inline-block'.($morecss ? ' '.$morecss : '').'" href="'.$links[$i][0].'">';
1939  $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.
1940  $outmore .= '</a>'."\n";
1941  }
1942  $outmore .= '</div>';
1943 
1944  $nbintab++;
1945  }
1946  $displaytab = $i;
1947  }
1948  if ($popuptab) {
1949  $outmore .= '</div>';
1950  }
1951 
1952  if ($popuptab) { // If there is some tabs not shown
1953  $left = ($langs->trans("DIRECTION") == 'rtl' ? 'right' : 'left');
1954  $right = ($langs->trans("DIRECTION") == 'rtl' ? 'left' : 'right');
1955  $widthofpopup = 200;
1956 
1957  $tabsname = $moretabssuffix;
1958  if (empty($tabsname)) {
1959  $tabsname = str_replace("@", "", $picto);
1960  }
1961  $out .= '<div id="moretabs'.$tabsname.'" class="inline-block tabsElem valignmiddle">';
1962  $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".
1963  $out .= '<div id="moretabsList'.$tabsname.'" style="width: '.$widthofpopup.'px; position: absolute; '.$left.': -999em; text-align: '.$left.'; margin:0px; padding:2px; z-index:10;">';
1964  $out .= $outmore;
1965  $out .= '</div>';
1966  $out .= '<div></div>';
1967  $out .= "</div>\n";
1968 
1969  $out .= "<script>";
1970  $out .= "$('#moretabs".$tabsname."').mouseenter( function() {
1971  var x = this.offsetLeft, y = this.offsetTop;
1972  console.log('mouseenter ".$left." x='+x+' y='+y+' window.innerWidth='+window.innerWidth);
1973  if ((window.innerWidth - x) < ".($widthofpopup + 10).") {
1974  $('#moretabsList".$tabsname."').css('".$right."','8px');
1975  }
1976  $('#moretabsList".$tabsname."').css('".$left."','auto');
1977  });
1978  ";
1979  $out .= "$('#moretabs".$tabsname."').mouseleave( function() { console.log('mouseleave ".$left."'); $('#moretabsList".$tabsname."').css('".$left."','-999em');});";
1980  $out .= "</script>";
1981  }
1982 
1983  if ((!empty($title) && $showtitle) || $morehtmlright || !empty($links)) {
1984  $out .= "</div>\n";
1985  }
1986 
1987  if (!$notab || $notab == -1 || $notab == -2) {
1988  $out .= "\n".'<div class="tabBar'.($notab == -1 ? '' : ($notab == -2 ? ' tabBarNoTop' : ' tabBarWithBottom')).'">'."\n";
1989  }
1990 
1991  $parameters = array('tabname' => $active, 'out' => $out);
1992  $reshook = $hookmanager->executeHooks('printTabsHead', $parameters); // This hook usage is called just before output the head of tabs. Take also a look at "completeTabsHead"
1993  if ($reshook > 0) {
1994  $out = $hookmanager->resPrint;
1995  }
1996 
1997  return $out;
1998 }
1999 
2007 function dol_fiche_end($notab = 0)
2008 {
2009  print dol_get_fiche_end($notab);
2010 }
2011 
2018 function dol_get_fiche_end($notab = 0)
2019 {
2020  if (!$notab || $notab == -1) {
2021  return "\n</div>\n";
2022  } else {
2023  return '';
2024  }
2025 }
2026 
2046 function dol_banner_tab($object, $paramid, $morehtml = '', $shownav = 1, $fieldid = 'rowid', $fieldref = 'ref', $morehtmlref = '', $moreparam = '', $nodbprefix = 0, $morehtmlleft = '', $morehtmlstatus = '', $onlybanner = 0, $morehtmlright = '')
2047 {
2048  global $conf, $form, $user, $langs, $hookmanager, $action;
2049 
2050  $error = 0;
2051 
2052  $maxvisiblephotos = 1;
2053  $showimage = 1;
2054  $entity = (empty($object->entity) ? $conf->entity : $object->entity);
2055  $showbarcode = empty($conf->barcode->enabled) ? 0 : (empty($object->barcode) ? 0 : 1);
2056  if (!empty($conf->global->MAIN_USE_ADVANCED_PERMS) && empty($user->rights->barcode->lire_advance)) {
2057  $showbarcode = 0;
2058  }
2059  $modulepart = 'unknown';
2060 
2061  if ($object->element == 'societe' || $object->element == 'contact' || $object->element == 'product' || $object->element == 'ticket') {
2062  $modulepart = $object->element;
2063  } elseif ($object->element == 'member') {
2064  $modulepart = 'memberphoto';
2065  } elseif ($object->element == 'user') {
2066  $modulepart = 'userphoto';
2067  }
2068 
2069  if (class_exists("Imagick")) {
2070  if ($object->element == 'expensereport' || $object->element == 'propal' || $object->element == 'commande' || $object->element == 'facture' || $object->element == 'supplier_proposal') {
2071  $modulepart = $object->element;
2072  } elseif ($object->element == 'fichinter') {
2073  $modulepart = 'ficheinter';
2074  } elseif ($object->element == 'contrat') {
2075  $modulepart = 'contract';
2076  } elseif ($object->element == 'order_supplier') {
2077  $modulepart = 'supplier_order';
2078  } elseif ($object->element == 'invoice_supplier') {
2079  $modulepart = 'supplier_invoice';
2080  }
2081  }
2082 
2083  if ($object->element == 'product') {
2084  $width = 80;
2085  $cssclass = 'photowithmargin photoref';
2086  $showimage = $object->is_photo_available($conf->product->multidir_output[$entity]);
2087  $maxvisiblephotos = (isset($conf->global->PRODUCT_MAX_VISIBLE_PHOTO) ? $conf->global->PRODUCT_MAX_VISIBLE_PHOTO : 5);
2088  if ($conf->browser->layout == 'phone') {
2089  $maxvisiblephotos = 1;
2090  }
2091  if ($showimage) {
2092  $morehtmlleft .= '<div class="floatleft inline-block valignmiddle divphotoref">'.$object->show_photos('product', $conf->product->multidir_output[$entity], 'small', $maxvisiblephotos, 0, 0, 0, $width, 0).'</div>';
2093  } else {
2094  if (!empty($conf->global->PRODUCT_NODISPLAYIFNOPHOTO)) {
2095  $nophoto = '';
2096  $morehtmlleft .= '<div class="floatleft inline-block valignmiddle divphotoref"></div>';
2097  } else { // Show no photo link
2098  $nophoto = '/public/theme/common/nophoto.png';
2099  $morehtmlleft .= '<div class="floatleft inline-block valignmiddle divphotoref"><img class="photo'.$modulepart.($cssclass ? ' '.$cssclass : '').'" alt="No photo"'.($width ? ' style="width: '.$width.'px"' : '').' src="'.DOL_URL_ROOT.$nophoto.'"></div>';
2100  }
2101  }
2102  } elseif ($object->element == 'ticket') {
2103  $width = 80;
2104  $cssclass = 'photoref';
2105  $showimage = $object->is_photo_available($conf->ticket->multidir_output[$entity].'/'.$object->ref);
2106  $maxvisiblephotos = (isset($conf->global->TICKET_MAX_VISIBLE_PHOTO) ? $conf->global->TICKET_MAX_VISIBLE_PHOTO : 2);
2107  if ($conf->browser->layout == 'phone') {
2108  $maxvisiblephotos = 1;
2109  }
2110 
2111  if ($showimage) {
2112  $showphoto = $object->show_photos('ticket', $conf->ticket->multidir_output[$entity], 'small', $maxvisiblephotos, 0, 0, 0, $width, 0);
2113  if ($object->nbphoto > 0) {
2114  $morehtmlleft .= '<div class="floatleft inline-block valignmiddle divphotoref">'.$showphoto.'</div>';
2115  } else {
2116  $showimage = 0;
2117  }
2118  }
2119  if (!$showimage) {
2120  if (!empty($conf->global->TICKET_NODISPLAYIFNOPHOTO)) {
2121  $nophoto = '';
2122  $morehtmlleft .= '<div class="floatleft inline-block valignmiddle divphotoref"></div>';
2123  } else { // Show no photo link
2124  $nophoto = img_picto('No photo', 'object_ticket');
2125  $morehtmlleft .= '<!-- No photo to show -->';
2126  $morehtmlleft .= '<div class="floatleft inline-block valignmiddle divphotoref"><div class="photoref">';
2127  $morehtmlleft .= $nophoto;
2128  $morehtmlleft .= '</div></div>';
2129  }
2130  }
2131  } else {
2132  if ($showimage) {
2133  if ($modulepart != 'unknown') {
2134  $phototoshow = '';
2135  // Check if a preview file is available
2136  if (in_array($modulepart, array('propal', 'commande', 'facture', 'ficheinter', 'contract', 'supplier_order', 'supplier_proposal', 'supplier_invoice', 'expensereport')) && class_exists("Imagick")) {
2137  $objectref = dol_sanitizeFileName($object->ref);
2138  $dir_output = (empty($conf->$modulepart->multidir_output[$entity]) ? $conf->$modulepart->dir_output : $conf->$modulepart->multidir_output[$entity])."/";
2139  if (in_array($modulepart, array('invoice_supplier', 'supplier_invoice'))) {
2140  $subdir = get_exdir($object->id, 2, 0, 1, $object, $modulepart);
2141  $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
2142  } else {
2143  $subdir = get_exdir($object->id, 0, 0, 1, $object, $modulepart);
2144  }
2145  if (empty($subdir)) {
2146  $subdir = 'errorgettingsubdirofobject'; // Protection to avoid to return empty path
2147  }
2148 
2149  $filepath = $dir_output.$subdir."/";
2150 
2151  $filepdf = $filepath.$objectref.".pdf";
2152  $relativepath = $subdir.'/'.$objectref.'.pdf';
2153 
2154  // Define path to preview pdf file (preview precompiled "file.ext" are "file.ext_preview.png")
2155  $fileimage = $filepdf.'_preview.png';
2156  $relativepathimage = $relativepath.'_preview.png';
2157 
2158  $pdfexists = file_exists($filepdf);
2159 
2160  // If PDF file exists
2161  if ($pdfexists) {
2162  // Conversion du PDF en image png si fichier png non existant
2163  if (!file_exists($fileimage) || (filemtime($fileimage) < filemtime($filepdf))) {
2164  if (empty($conf->global->MAIN_DISABLE_PDF_THUMBS)) { // If you experience trouble with pdf thumb generation and imagick, you can disable here.
2165  include_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
2166  $ret = dol_convert_file($filepdf, 'png', $fileimage, '0'); // Convert first page of PDF into a file _preview.png
2167  if ($ret < 0) {
2168  $error++;
2169  }
2170  }
2171  }
2172  }
2173 
2174  if ($pdfexists && !$error) {
2175  $heightforphotref = 80;
2176  if (!empty($conf->dol_optimize_smallscreen)) {
2177  $heightforphotref = 60;
2178  }
2179  // If the preview file is found
2180  if (file_exists($fileimage)) {
2181  $phototoshow = '<div class="photoref">';
2182  $phototoshow .= '<img height="'.$heightforphotref.'" class="photo photowithmargin photowithborder" src="'.DOL_URL_ROOT.'/viewimage.php?modulepart=apercu'.$modulepart.'&amp;file='.urlencode($relativepathimage).'">';
2183  $phototoshow .= '</div>';
2184  }
2185  }
2186  } elseif (!$phototoshow) { // example if modulepart = 'societe' or 'photo'
2187  $phototoshow .= $form->showphoto($modulepart, $object, 0, 0, 0, 'photowithmargin photoref', 'small', 1, 0, $maxvisiblephotos);
2188  }
2189 
2190  if ($phototoshow) {
2191  $morehtmlleft .= '<div class="floatleft inline-block valignmiddle divphotoref">';
2192  $morehtmlleft .= $phototoshow;
2193  $morehtmlleft .= '</div>';
2194  }
2195  }
2196 
2197  if (empty($phototoshow)) { // Show No photo link (picto of object)
2198  if ($object->element == 'action') {
2199  $width = 80;
2200  $cssclass = 'photorefcenter';
2201  $nophoto = img_picto('No photo', 'title_agenda');
2202  } else {
2203  $width = 14;
2204  $cssclass = 'photorefcenter';
2205  $picto = $object->picto;
2206  $prefix = 'object_';
2207  if ($object->element == 'project' && !$object->public) {
2208  $picto = 'project'; // instead of projectpub
2209  }
2210  if (strpos($picto, 'fontawesome_') !== false) {
2211  $prefix = '';
2212  }
2213  $nophoto = img_picto('No photo', $prefix.$picto);
2214  }
2215  $morehtmlleft .= '<!-- No photo to show -->';
2216  $morehtmlleft .= '<div class="floatleft inline-block valignmiddle divphotoref"><div class="photoref">';
2217  $morehtmlleft .= $nophoto;
2218  $morehtmlleft .= '</div></div>';
2219  }
2220  }
2221  }
2222 
2223  if ($showbarcode) {
2224  $morehtmlleft .= '<div class="floatleft inline-block valignmiddle divphotoref">'.$form->showbarcode($object, 100, 'photoref valignmiddle').'</div>';
2225  }
2226 
2227  if ($object->element == 'societe') {
2228  if (!empty($conf->use_javascript_ajax) && $user->rights->societe->creer && !empty($conf->global->MAIN_DIRECT_STATUS_UPDATE)) {
2229  $morehtmlstatus .= ajax_object_onoff($object, 'status', 'status', 'InActivity', 'ActivityCeased');
2230  } else {
2231  $morehtmlstatus .= $object->getLibStatut(6);
2232  }
2233  } elseif ($object->element == 'product') {
2234  //$morehtmlstatus.=$langs->trans("Status").' ('.$langs->trans("Sell").') ';
2235  if (!empty($conf->use_javascript_ajax) && $user->rights->produit->creer && !empty($conf->global->MAIN_DIRECT_STATUS_UPDATE)) {
2236  $morehtmlstatus .= ajax_object_onoff($object, 'status', 'tosell', 'ProductStatusOnSell', 'ProductStatusNotOnSell');
2237  } else {
2238  $morehtmlstatus .= '<span class="statusrefsell">'.$object->getLibStatut(6, 0).'</span>';
2239  }
2240  $morehtmlstatus .= ' &nbsp; ';
2241  //$morehtmlstatus.=$langs->trans("Status").' ('.$langs->trans("Buy").') ';
2242  if (!empty($conf->use_javascript_ajax) && $user->rights->produit->creer && !empty($conf->global->MAIN_DIRECT_STATUS_UPDATE)) {
2243  $morehtmlstatus .= ajax_object_onoff($object, 'status_buy', 'tobuy', 'ProductStatusOnBuy', 'ProductStatusNotOnBuy');
2244  } else {
2245  $morehtmlstatus .= '<span class="statusrefbuy">'.$object->getLibStatut(6, 1).'</span>';
2246  }
2247  } elseif (in_array($object->element, array('facture', 'invoice', 'invoice_supplier', 'chargesociales', 'loan', 'tva', 'salary'))) {
2248  $tmptxt = $object->getLibStatut(6, $object->totalpaid);
2249  if (empty($tmptxt) || $tmptxt == $object->getLibStatut(3)) {
2250  $tmptxt = $object->getLibStatut(5, $object->totalpaid);
2251  }
2252  $morehtmlstatus .= $tmptxt;
2253  } elseif ($object->element == 'contrat' || $object->element == 'contract') {
2254  if ($object->statut == 0) {
2255  $morehtmlstatus .= $object->getLibStatut(5);
2256  } else {
2257  $morehtmlstatus .= $object->getLibStatut(4);
2258  }
2259  } elseif ($object->element == 'facturerec') {
2260  if ($object->frequency == 0) {
2261  $morehtmlstatus .= $object->getLibStatut(2);
2262  } else {
2263  $morehtmlstatus .= $object->getLibStatut(5);
2264  }
2265  } elseif ($object->element == 'project_task') {
2266  $object->fk_statut = 1;
2267  if ($object->progress > 0) {
2268  $object->fk_statut = 2;
2269  }
2270  if ($object->progress >= 100) {
2271  $object->fk_statut = 3;
2272  }
2273  $tmptxt = $object->getLibStatut(5);
2274  $morehtmlstatus .= $tmptxt; // No status on task
2275  } else { // Generic case
2276  $tmptxt = $object->getLibStatut(6);
2277  if (empty($tmptxt) || $tmptxt == $object->getLibStatut(3)) {
2278  $tmptxt = $object->getLibStatut(5);
2279  }
2280  $morehtmlstatus .= $tmptxt;
2281  }
2282 
2283  // Add if object was dispatched "into accountancy"
2284  if (!empty($conf->accounting->enabled) && in_array($object->element, array('bank', 'paiementcharge', 'facture', 'invoice', 'invoice_supplier', 'expensereport', 'payment_various'))) {
2285  // Note: For 'chargesociales', 'salaries'... this is the payments that are dispatched (so element = 'bank')
2286  if (method_exists($object, 'getVentilExportCompta')) {
2287  $accounted = $object->getVentilExportCompta();
2288  $langs->load("accountancy");
2289  $morehtmlstatus .= '</div><div class="statusref statusrefbis"><span class="opacitymedium">'.($accounted > 0 ? $langs->trans("Accounted") : $langs->trans("NotYetAccounted")).'</span>';
2290  }
2291  }
2292 
2293  // Add alias for thirdparty
2294  if (!empty($object->name_alias)) {
2295  $morehtmlref .= '<div class="refidno">'.$object->name_alias.'</div>';
2296  }
2297 
2298  // Add label
2299  if (in_array($object->element, array('product', 'bank_account', 'project_task'))) {
2300  if (!empty($object->label)) {
2301  $morehtmlref .= '<div class="refidno">'.$object->label.'</div>';
2302  }
2303  }
2304 
2305  // Show address and email
2306  if (method_exists($object, 'getBannerAddress') && !in_array($object->element, array('product', 'bookmark', 'ecm_directories', 'ecm_files'))) {
2307  $moreaddress = $object->getBannerAddress('refaddress', $object);
2308  if ($moreaddress) {
2309  $morehtmlref .= '<div class="refidno">';
2310  $morehtmlref .= $moreaddress;
2311  $morehtmlref .= '</div>';
2312  }
2313  }
2314  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)) {
2315  $morehtmlref .= '<div style="clear: both;"></div>';
2316  $morehtmlref .= '<div class="refidno">';
2317  $morehtmlref .= $langs->trans("TechnicalID").': '.$object->id;
2318  $morehtmlref .= '</div>';
2319  }
2320 
2321  $parameters=array('morehtmlref'=>$morehtmlref);
2322  $reshook = $hookmanager->executeHooks('formDolBanner', $parameters, $object, $action);
2323  if ($reshook < 0) {
2324  setEventMessages($hookmanager->error, $hookmanager->errors, 'errors');
2325  } elseif (empty($reshook)) {
2326  $morehtmlref .= $hookmanager->resPrint;
2327  } elseif ($reshook > 0) {
2328  $morehtmlref = $hookmanager->resPrint;
2329  }
2330 
2331 
2332  print '<div class="'.($onlybanner ? 'arearefnobottom ' : 'arearef ').'heightref valignmiddle centpercent">';
2333  print $form->showrefnav($object, $paramid, $morehtml, $shownav, $fieldid, $fieldref, $morehtmlref, $moreparam, $nodbprefix, $morehtmlleft, $morehtmlstatus, $morehtmlright);
2334  print '</div>';
2335  print '<div class="underrefbanner clearboth"></div>';
2336 }
2337 
2347 function fieldLabel($langkey, $fieldkey, $fieldrequired = 0)
2348 {
2349  global $langs;
2350  $ret = '';
2351  if ($fieldrequired) {
2352  $ret .= '<span class="fieldrequired">';
2353  }
2354  $ret .= '<label for="'.$fieldkey.'">';
2355  $ret .= $langs->trans($langkey);
2356  $ret .= '</label>';
2357  if ($fieldrequired) {
2358  $ret .= '</span>';
2359  }
2360  return $ret;
2361 }
2362 
2370 function dol_bc($var, $moreclass = '')
2371 {
2372  global $bc;
2373  $ret = ' '.$bc[$var];
2374  if ($moreclass) {
2375  $ret = preg_replace('/class=\"/', 'class="'.$moreclass.' ', $ret);
2376  }
2377  return $ret;
2378 }
2379 
2393 function dol_format_address($object, $withcountry = 0, $sep = "\n", $outputlangs = '', $mode = 0, $extralangcode = '')
2394 {
2395  global $conf, $langs, $hookmanager;
2396 
2397  $ret = '';
2398  $countriesusingstate = array('AU', 'CA', 'US', 'IN', 'GB', 'ES', 'UK', 'TR', 'CN'); // See also MAIN_FORCE_STATE_INTO_ADDRESS
2399 
2400  // See format of addresses on https://en.wikipedia.org/wiki/Address
2401  // Address
2402  if (empty($mode)) {
2403  $ret .= ($extralangcode ? $object->array_languages['address'][$extralangcode] : (empty($object->address) ? '' : $object->address));
2404  }
2405  // Zip/Town/State
2406  if (isset($object->country_code) && in_array($object->country_code, array('AU', 'CA', 'US', 'CN')) || !empty($conf->global->MAIN_FORCE_STATE_INTO_ADDRESS)) {
2407  // US: title firstname name \n address lines \n town, state, zip \n country
2408  $town = ($extralangcode ? $object->array_languages['town'][$extralangcode] : (empty($object->town) ? '' : $object->town));
2409  $ret .= (($ret && $town) ? $sep : '').$town;
2410 
2411  if (!empty($object->state)) {
2412  $ret .= ($ret ? ($town ? ", " : $sep) : '').$object->state;
2413  }
2414  if (!empty($object->zip)) {
2415  $ret .= ($ret ? (($town || $object->state) ? ", " : $sep) : '').$object->zip;
2416  }
2417  } elseif (isset($object->country_code) && in_array($object->country_code, array('GB', 'UK'))) {
2418  // UK: title firstname name \n address lines \n town state \n zip \n country
2419  $town = ($extralangcode ? $object->array_languages['town'][$extralangcode] : (empty($object->town) ? '' : $object->town));
2420  $ret .= ($ret ? $sep : '').$town;
2421  if (!empty($object->state)) {
2422  $ret .= ($ret ? ", " : '').$object->state;
2423  }
2424  if (!empty($object->zip)) {
2425  $ret .= ($ret ? $sep : '').$object->zip;
2426  }
2427  } elseif (isset($object->country_code) && in_array($object->country_code, array('ES', 'TR'))) {
2428  // ES: title firstname name \n address lines \n zip town \n state \n country
2429  $ret .= ($ret ? $sep : '').$object->zip;
2430  $town = ($extralangcode ? $object->array_languages['town'][$extralangcode] : (empty($object->town) ? '' : $object->town));
2431  $ret .= ($town ? (($object->zip ? ' ' : '').$town) : '');
2432  if (!empty($object->state)) {
2433  $ret .= "\n".$object->state;
2434  }
2435  } elseif (isset($object->country_code) && in_array($object->country_code, array('JP'))) {
2436  // JP: In romaji, title firstname name\n address lines \n [state,] town zip \n country
2437  // See https://www.sljfaq.org/afaq/addresses.html
2438  $town = ($extralangcode ? $object->array_languages['town'][$extralangcode] : (empty($object->town) ? '' : $object->town));
2439  $ret .= ($ret ? $sep : '').($object->state ? $object->state.', ' : '').$town.($object->zip ? ' ' : '').$object->zip;
2440  } elseif (isset($object->country_code) && in_array($object->country_code, array('IT'))) {
2441  // IT: title firstname name\n address lines \n zip town state_code \n country
2442  $ret .= ($ret ? $sep : '').$object->zip;
2443  $town = ($extralangcode ? $object->array_languages['town'][$extralangcode] : (empty($object->town) ? '' : $object->town));
2444  $ret .= ($town ? (($object->zip ? ' ' : '').$town) : '');
2445  $ret .= (empty($object->state_code) ? '' : (' '.$object->state_code));
2446  } else {
2447  // Other: title firstname name \n address lines \n zip town[, state] \n country
2448  $town = ($extralangcode ? $object->array_languages['town'][$extralangcode] : (empty($object->town) ? '' : $object->town));
2449  $ret .= !empty($object->zip) ? (($ret ? $sep : '').$object->zip) : '';
2450  $ret .= ($town ? (($object->zip ? ' ' : ($ret ? $sep : '')).$town) : '');
2451  if (!empty($object->state) && in_array($object->country_code, $countriesusingstate)) {
2452  $ret .= ($ret ? ", " : '').$object->state;
2453  }
2454  }
2455  if (!is_object($outputlangs)) {
2456  $outputlangs = $langs;
2457  }
2458  if ($withcountry) {
2459  $langs->load("dict");
2460  $ret .= (empty($object->country_code) ? '' : ($ret ? $sep : '').$outputlangs->convToOutputCharset($outputlangs->transnoentitiesnoconv("Country".$object->country_code)));
2461  }
2462  if ($hookmanager) {
2463  $parameters = array('withcountry' => $withcountry, 'sep' => $sep, 'outputlangs' => $outputlangs,'mode' => $mode, 'extralangcode' => $extralangcode);
2464  $reshook = $hookmanager->executeHooks('formatAddress', $parameters, $object);
2465  if ($reshook > 0) {
2466  $ret = '';
2467  }
2468  $ret .= $hookmanager->resPrint;
2469  }
2470 
2471  return $ret;
2472 }
2473 
2474 
2475 
2484 function dol_strftime($fmt, $ts = false, $is_gmt = false)
2485 {
2486  if ((abs($ts) <= 0x7FFFFFFF)) { // check if number in 32-bit signed range
2487  return ($is_gmt) ? @gmstrftime($fmt, $ts) : @strftime($fmt, $ts);
2488  } else {
2489  return 'Error date into a not supported range';
2490  }
2491 }
2492 
2514 function dol_print_date($time, $format = '', $tzoutput = 'auto', $outputlangs = '', $encodetooutput = false)
2515 {
2516  global $conf, $langs;
2517 
2518  // If date undefined or "", we return ""
2519  if (dol_strlen($time) == 0) {
2520  return ''; // $time=0 allowed (it means 01/01/1970 00:00:00)
2521  }
2522 
2523  if ($tzoutput === 'auto') {
2524  $tzoutput = (empty($conf) ? 'tzserver' : (isset($conf->tzuserinputkey) ? $conf->tzuserinputkey : 'tzserver'));
2525  }
2526 
2527  // Clean parameters
2528  $to_gmt = false;
2529  $offsettz = $offsetdst = 0;
2530  if ($tzoutput) {
2531  $to_gmt = true; // For backward compatibility
2532  if (is_string($tzoutput)) {
2533  if ($tzoutput == 'tzserver') {
2534  $to_gmt = false;
2535  $offsettzstring = @date_default_timezone_get(); // Example 'Europe/Berlin' or 'Indian/Reunion'
2536  $offsettz = 0; // Timezone offset with server timezone, so 0
2537  $offsetdst = 0; // Dst offset with server timezone, so 0
2538  } elseif ($tzoutput == 'tzuser' || $tzoutput == 'tzuserrel') {
2539  $to_gmt = true;
2540  $offsettzstring = (empty($_SESSION['dol_tz_string']) ? 'UTC' : $_SESSION['dol_tz_string']); // Example 'Europe/Berlin' or 'Indian/Reunion'
2541 
2542  if (class_exists('DateTimeZone')) {
2543  $user_date_tz = new DateTimeZone($offsettzstring);
2544  $user_dt = new DateTime();
2545  $user_dt->setTimezone($user_date_tz);
2546  $user_dt->setTimestamp($tzoutput == 'tzuser' ? dol_now() : (int) $time);
2547  $offsettz = $user_dt->getOffset();
2548  } else { // old method (The 'tzuser' was processed like the 'tzuserrel')
2549  $offsettz = (empty($_SESSION['dol_tz']) ? 0 : $_SESSION['dol_tz']) * 60 * 60; // Will not be used anymore
2550  $offsetdst = (empty($_SESSION['dol_dst']) ? 0 : $_SESSION['dol_dst']) * 60 * 60; // Will not be used anymore
2551  }
2552  }
2553  }
2554  }
2555  if (!is_object($outputlangs)) {
2556  $outputlangs = $langs;
2557  }
2558  if (!$format) {
2559  $format = 'daytextshort';
2560  }
2561 
2562  // Do we have to reduce the length of date (year on 2 chars) to save space.
2563  // Note: dayinputnoreduce is same than day but no reduction of year length will be done
2564  $reduceformat = (!empty($conf->dol_optimize_smallscreen) && in_array($format, array('day', 'dayhour'))) ? 1 : 0; // Test on original $format param.
2565  $format = preg_replace('/inputnoreduce/', '', $format); // so format 'dayinputnoreduce' is processed like day
2566  $formatwithoutreduce = preg_replace('/reduceformat/', '', $format);
2567  if ($formatwithoutreduce != $format) {
2568  $format = $formatwithoutreduce;
2569  $reduceformat = 1;
2570  } // so format 'dayreduceformat' is processed like day
2571 
2572  // Change predefined format into computer format. If found translation in lang file we use it, otherwise we use default.
2573  // TODO Add format daysmallyear and dayhoursmallyear
2574  if ($format == 'day') {
2575  $format = ($outputlangs->trans("FormatDateShort") != "FormatDateShort" ? $outputlangs->trans("FormatDateShort") : $conf->format_date_short);
2576  } elseif ($format == 'hour') {
2577  $format = ($outputlangs->trans("FormatHourShort") != "FormatHourShort" ? $outputlangs->trans("FormatHourShort") : $conf->format_hour_short);
2578  } elseif ($format == 'hourduration') {
2579  $format = ($outputlangs->trans("FormatHourShortDuration") != "FormatHourShortDuration" ? $outputlangs->trans("FormatHourShortDuration") : $conf->format_hour_short_duration);
2580  } elseif ($format == 'daytext') {
2581  $format = ($outputlangs->trans("FormatDateText") != "FormatDateText" ? $outputlangs->trans("FormatDateText") : $conf->format_date_text);
2582  } elseif ($format == 'daytextshort') {
2583  $format = ($outputlangs->trans("FormatDateTextShort") != "FormatDateTextShort" ? $outputlangs->trans("FormatDateTextShort") : $conf->format_date_text_short);
2584  } elseif ($format == 'dayhour') {
2585  $format = ($outputlangs->trans("FormatDateHourShort") != "FormatDateHourShort" ? $outputlangs->trans("FormatDateHourShort") : $conf->format_date_hour_short);
2586  } elseif ($format == 'dayhoursec') {
2587  $format = ($outputlangs->trans("FormatDateHourSecShort") != "FormatDateHourSecShort" ? $outputlangs->trans("FormatDateHourSecShort") : $conf->format_date_hour_sec_short);
2588  } elseif ($format == 'dayhourtext') {
2589  $format = ($outputlangs->trans("FormatDateHourText") != "FormatDateHourText" ? $outputlangs->trans("FormatDateHourText") : $conf->format_date_hour_text);
2590  } elseif ($format == 'dayhourtextshort') {
2591  $format = ($outputlangs->trans("FormatDateHourTextShort") != "FormatDateHourTextShort" ? $outputlangs->trans("FormatDateHourTextShort") : $conf->format_date_hour_text_short);
2592  } elseif ($format == 'dayhourlog') {
2593  // Format not sensitive to language
2594  $format = '%Y%m%d%H%M%S';
2595  } elseif ($format == 'dayhourlogsmall') {
2596  // Format not sensitive to language
2597  $format = '%Y%m%d%H%M';
2598  } elseif ($format == 'dayhourldap') {
2599  $format = '%Y%m%d%H%M%SZ';
2600  } elseif ($format == 'dayhourxcard') {
2601  $format = '%Y%m%dT%H%M%SZ';
2602  } elseif ($format == 'dayxcard') {
2603  $format = '%Y%m%d';
2604  } elseif ($format == 'dayrfc') {
2605  $format = '%Y-%m-%d'; // DATE_RFC3339
2606  } elseif ($format == 'dayhourrfc') {
2607  $format = '%Y-%m-%dT%H:%M:%SZ'; // DATETIME RFC3339
2608  } elseif ($format == 'standard') {
2609  $format = '%Y-%m-%d %H:%M:%S';
2610  }
2611 
2612  if ($reduceformat) {
2613  $format = str_replace('%Y', '%y', $format);
2614  $format = str_replace('yyyy', 'yy', $format);
2615  }
2616 
2617  // Clean format
2618  if (preg_match('/%b/i', $format)) { // There is some text to translate
2619  // We inhibate translation to text made by strftime functions. We will use trans instead later.
2620  $format = str_replace('%b', '__b__', $format);
2621  $format = str_replace('%B', '__B__', $format);
2622  }
2623  if (preg_match('/%a/i', $format)) { // There is some text to translate
2624  // We inhibate translation to text made by strftime functions. We will use trans instead later.
2625  $format = str_replace('%a', '__a__', $format);
2626  $format = str_replace('%A', '__A__', $format);
2627  }
2628 
2629 
2630  // Analyze date
2631  $reg = array();
2632  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
2633  dol_print_error('', "Functions.lib::dol_print_date function called with a bad value from page ".$_SERVER["PHP_SELF"]);
2634  return '';
2635  } 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
2636  // This part of code should not be used anymore.
2637  dol_syslog("Functions.lib::dol_print_date function called with a bad value from page ".$_SERVER["PHP_SELF"], LOG_WARNING);
2638  //if (function_exists('debug_print_backtrace')) debug_print_backtrace();
2639  // Date has format 'YYYY-MM-DD' or 'YYYY-MM-DD HH:MM:SS'
2640  $syear = (!empty($reg[1]) ? $reg[1] : '');
2641  $smonth = (!empty($reg[2]) ? $reg[2] : '');
2642  $sday = (!empty($reg[3]) ? $reg[3] : '');
2643  $shour = (!empty($reg[4]) ? $reg[4] : '');
2644  $smin = (!empty($reg[5]) ? $reg[5] : '');
2645  $ssec = (!empty($reg[6]) ? $reg[6] : '');
2646 
2647  $time = dol_mktime($shour, $smin, $ssec, $smonth, $sday, $syear, true);
2648  $ret = adodb_strftime($format, $time + $offsettz + $offsetdst, $to_gmt);
2649  } else {
2650  // Date is a timestamps
2651  if ($time < 100000000000) { // Protection against bad date values
2652  $timetouse = $time + $offsettz + $offsetdst; // TODO Replace this with function Date PHP. We also should not use anymore offsettz and offsetdst but only offsettzstring.
2653 
2654  $ret = adodb_strftime($format, $timetouse, $to_gmt); // If to_gmt = false then adodb_strftime use TZ of server
2655  } else {
2656  $ret = 'Bad value '.$time.' for date';
2657  }
2658  }
2659 
2660  if (preg_match('/__b__/i', $format)) {
2661  $timetouse = $time + $offsettz + $offsetdst; // TODO Replace this with function Date PHP. We also should not use anymore offsettz and offsetdst but only offsettzstring.
2662 
2663  // Here ret is string in PHP setup language (strftime was used). Now we convert to $outputlangs.
2664  $month = adodb_strftime('%m', $timetouse, $to_gmt); // If to_gmt = false then adodb_strftime use TZ of server
2665  $month = sprintf("%02d", $month); // $month may be return with format '06' on some installation and '6' on other, so we force it to '06'.
2666  if ($encodetooutput) {
2667  $monthtext = $outputlangs->transnoentities('Month'.$month);
2668  $monthtextshort = $outputlangs->transnoentities('MonthShort'.$month);
2669  } else {
2670  $monthtext = $outputlangs->transnoentitiesnoconv('Month'.$month);
2671  $monthtextshort = $outputlangs->transnoentitiesnoconv('MonthShort'.$month);
2672  }
2673  //print 'monthtext='.$monthtext.' monthtextshort='.$monthtextshort;
2674  $ret = str_replace('__b__', $monthtextshort, $ret);
2675  $ret = str_replace('__B__', $monthtext, $ret);
2676  //print 'x'.$outputlangs->charset_output.'-'.$ret.'x';
2677  //return $ret;
2678  }
2679  if (preg_match('/__a__/i', $format)) {
2680  //print "time=$time offsettz=$offsettz offsetdst=$offsetdst offsettzstring=$offsettzstring";
2681  $timetouse = $time + $offsettz + $offsetdst; // TODO Replace this with function Date PHP. We also should not use anymore offsettz and offsetdst but only offsettzstring.
2682 
2683  $w = adodb_strftime('%w', $timetouse, $to_gmt); // If to_gmt = false then adodb_strftime use TZ of server
2684  $dayweek = $outputlangs->transnoentitiesnoconv('Day'.$w);
2685  $ret = str_replace('__A__', $dayweek, $ret);
2686  $ret = str_replace('__a__', dol_substr($dayweek, 0, 3), $ret);
2687  }
2688 
2689  return $ret;
2690 }
2691 
2692 
2713 function dol_getdate($timestamp, $fast = false, $forcetimezone = '')
2714 {
2715  //$datetimeobj = new DateTime('@'.$timestamp);
2716  $datetimeobj = new DateTime();
2717  $datetimeobj->setTimestamp($timestamp); // Use local PHP server timezone
2718  if ($forcetimezone) {
2719  $datetimeobj->setTimezone(new DateTimeZone($forcetimezone == 'gmt' ? 'UTC' : $forcetimezone)); // (add timezone relative to the date entered)
2720  }
2721  $arrayinfo = array(
2722  'year'=>((int) date_format($datetimeobj, 'Y')),
2723  'mon'=>((int) date_format($datetimeobj, 'm')),
2724  'mday'=>((int) date_format($datetimeobj, 'd')),
2725  'wday'=>((int) date_format($datetimeobj, 'w')),
2726  'yday'=>((int) date_format($datetimeobj, 'z')),
2727  'hours'=>((int) date_format($datetimeobj, 'H')),
2728  'minutes'=>((int) date_format($datetimeobj, 'i')),
2729  'seconds'=>((int) date_format($datetimeobj, 's')),
2730  '0'=>$timestamp
2731  );
2732 
2733  return $arrayinfo;
2734 }
2735 
2757 function dol_mktime($hour, $minute, $second, $month, $day, $year, $gm = 'auto', $check = 1)
2758 {
2759  global $conf;
2760  //print "- ".$hour.",".$minute.",".$second.",".$month.",".$day.",".$year.",".$_SERVER["WINDIR"]." -";
2761 
2762  if ($gm === 'auto') {
2763  $gm = (empty($conf) ? 'tzserver' : $conf->tzuserinputkey);
2764  }
2765  //print 'gm:'.$gm.' gm === auto:'.($gm === 'auto').'<br>';exit;
2766 
2767  // Clean parameters
2768  if ($hour == -1 || empty($hour)) {
2769  $hour = 0;
2770  }
2771  if ($minute == -1 || empty($minute)) {
2772  $minute = 0;
2773  }
2774  if ($second == -1 || empty($second)) {
2775  $second = 0;
2776  }
2777 
2778  // Check parameters
2779  if ($check) {
2780  if (!$month || !$day) {
2781  return '';
2782  }
2783  if ($day > 31) {
2784  return '';
2785  }
2786  if ($month > 12) {
2787  return '';
2788  }
2789  if ($hour < 0 || $hour > 24) {
2790  return '';
2791  }
2792  if ($minute < 0 || $minute > 60) {
2793  return '';
2794  }
2795  if ($second < 0 || $second > 60) {
2796  return '';
2797  }
2798  }
2799 
2800  if (empty($gm) || ($gm === 'server' || $gm === 'tzserver')) {
2801  $default_timezone = @date_default_timezone_get(); // Example 'Europe/Berlin'
2802  $localtz = new DateTimeZone($default_timezone);
2803  } elseif ($gm === 'user' || $gm === 'tzuser' || $gm === 'tzuserrel') {
2804  // We use dol_tz_string first because it is more reliable.
2805  $default_timezone = (empty($_SESSION["dol_tz_string"]) ? @date_default_timezone_get() : $_SESSION["dol_tz_string"]); // Example 'Europe/Berlin'
2806  try {
2807  $localtz = new DateTimeZone($default_timezone);
2808  } catch (Exception $e) {
2809  dol_syslog("Warning dol_tz_string contains an invalid value ".$_SESSION["dol_tz_string"], LOG_WARNING);
2810  $default_timezone = @date_default_timezone_get();
2811  }
2812  } elseif (strrpos($gm, "tz,") !== false) {
2813  $timezone = str_replace("tz,", "", $gm); // Example 'tz,Europe/Berlin'
2814  try {
2815  $localtz = new DateTimeZone($timezone);
2816  } catch (Exception $e) {
2817  dol_syslog("Warning passed timezone contains an invalid value ".$timezone, LOG_WARNING);
2818  }
2819  }
2820 
2821  if (empty($localtz)) {
2822  $localtz = new DateTimeZone('UTC');
2823  }
2824  //var_dump($localtz);
2825  //var_dump($year.'-'.$month.'-'.$day.'-'.$hour.'-'.$minute);
2826  $dt = new DateTime('now', $localtz);
2827  $dt->setDate((int) $year, (int) $month, (int) $day);
2828  $dt->setTime((int) $hour, (int) $minute, (int) $second);
2829  $date = $dt->getTimestamp(); // should include daylight saving time
2830  //var_dump($date);
2831  return $date;
2832 }
2833 
2834 
2845 function dol_now($mode = 'auto')
2846 {
2847  $ret = 0;
2848 
2849  if ($mode === 'auto') {
2850  $mode = 'gmt';
2851  }
2852 
2853  if ($mode == 'gmt') {
2854  $ret = time(); // Time for now at greenwich.
2855  } elseif ($mode == 'tzserver') { // Time for now with PHP server timezone added
2856  require_once DOL_DOCUMENT_ROOT.'/core/lib/date.lib.php';
2857  $tzsecond = getServerTimeZoneInt('now'); // Contains tz+dayling saving time
2858  $ret = (int) (dol_now('gmt') + ($tzsecond * 3600));
2859  //} elseif ($mode == 'tzref') {// Time for now with parent company timezone is added
2860  // require_once DOL_DOCUMENT_ROOT.'/core/lib/date.lib.php';
2861  // $tzsecond=getParentCompanyTimeZoneInt(); // Contains tz+dayling saving time
2862  // $ret=dol_now('gmt')+($tzsecond*3600);
2863  //}
2864  } elseif ($mode == 'tzuser' || $mode == 'tzuserrel') {
2865  // Time for now with user timezone added
2866  //print 'time: '.time();
2867  $offsettz = (empty($_SESSION['dol_tz']) ? 0 : $_SESSION['dol_tz']) * 60 * 60;
2868  $offsetdst = (empty($_SESSION['dol_dst']) ? 0 : $_SESSION['dol_dst']) * 60 * 60;
2869  $ret = (int) (dol_now('gmt') + ($offsettz + $offsetdst));
2870  }
2871 
2872  return $ret;
2873 }
2874 
2875 
2884 function dol_print_size($size, $shortvalue = 0, $shortunit = 0)
2885 {
2886  global $conf, $langs;
2887  $level = 1024;
2888 
2889  if (!empty($conf->dol_optimize_smallscreen)) {
2890  $shortunit = 1;
2891  }
2892 
2893  // Set value text
2894  if (empty($shortvalue) || $size < ($level * 10)) {
2895  $ret = $size;
2896  $textunitshort = $langs->trans("b");
2897  $textunitlong = $langs->trans("Bytes");
2898  } else {
2899  $ret = round($size / $level, 0);
2900  $textunitshort = $langs->trans("Kb");
2901  $textunitlong = $langs->trans("KiloBytes");
2902  }
2903  // Use long or short text unit
2904  if (empty($shortunit)) {
2905  $ret .= ' '.$textunitlong;
2906  } else {
2907  $ret .= ' '.$textunitshort;
2908  }
2909 
2910  return $ret;
2911 }
2912 
2922 function dol_print_url($url, $target = '_blank', $max = 32, $withpicto = 0)
2923 {
2924  global $langs;
2925 
2926  if (empty($url)) {
2927  return '';
2928  }
2929 
2930  $link = '<a href="';
2931  if (!preg_match('/^http/i', $url)) {
2932  $link .= 'http://';
2933  }
2934  $link .= $url;
2935  $link .= '"';
2936  if ($target) {
2937  $link .= ' target="'.$target.'"';
2938  }
2939  $link .= '>';
2940  if (!preg_match('/^http/i', $url)) {
2941  $link .= 'http://';
2942  }
2943  $link .= dol_trunc($url, $max);
2944  $link .= '</a>';
2945  return '<div class="nospan float" style="margin-right: 10px">'.($withpicto ?img_picto($langs->trans("Url"), 'globe').' ' : '').$link.'</div>';
2946 }
2947 
2960 function dol_print_email($email, $cid = 0, $socid = 0, $addlink = 0, $max = 64, $showinvalid = 1, $withpicto = 0)
2961 {
2962  global $conf, $user, $langs, $hookmanager;
2963 
2964  $newemail = dol_escape_htmltag($email);
2965 
2966  if (!empty($conf->global->MAIN_OPTIMIZEFORTEXTBROWSER) && $withpicto) {
2967  $withpicto = 0;
2968  }
2969 
2970  if (empty($email)) {
2971  return '&nbsp;';
2972  }
2973 
2974  if (!empty($addlink)) {
2975  $newemail = '<a style="text-overflow: ellipsis;" href="';
2976  if (!preg_match('/^mailto:/i', $email)) {
2977  $newemail .= 'mailto:';
2978  }
2979  $newemail .= $email;
2980  $newemail .= '">';
2981  $newemail .= dol_trunc($email, $max);
2982  $newemail .= '</a>';
2983  if ($showinvalid && !isValidEmail($email)) {
2984  $langs->load("errors");
2985  $newemail .= img_warning($langs->trans("ErrorBadEMail", $email));
2986  }
2987 
2988  if (($cid || $socid) && !empty($conf->agenda->enabled) && $user->rights->agenda->myactions->create) {
2989  $type = 'AC_EMAIL';
2990  $link = '';
2991  if (!empty($conf->global->AGENDA_ADDACTIONFOREMAIL)) {
2992  $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>';
2993  }
2994  if ($link) {
2995  $newemail = '<div>'.$newemail.' '.$link.'</div>';
2996  }
2997  }
2998  } else {
2999  if ($showinvalid && !isValidEmail($email)) {
3000  $langs->load("errors");
3001  $newemail .= img_warning($langs->trans("ErrorBadEMail", $email));
3002  }
3003  }
3004 
3005  //$rep = '<div class="nospan" style="margin-right: 10px">';
3006  $rep = ($withpicto ? img_picto($langs->trans("EMail").' : '.$email, (is_numeric($withpicto) ? 'email' : $withpicto)).' ' : '').$newemail;
3007  //$rep .= '</div>';
3008  if ($hookmanager) {
3009  $parameters = array('cid' => $cid, 'socid' => $socid, 'addlink' => $addlink, 'picto' => $withpicto);
3010 
3011  $reshook = $hookmanager->executeHooks('printEmail', $parameters, $email);
3012  if ($reshook > 0) {
3013  $rep = '';
3014  }
3015  $rep .= $hookmanager->resPrint;
3016  }
3017 
3018  return $rep;
3019 }
3020 
3027 {
3028  global $conf, $db;
3029 
3030  $socialnetworks = array();
3031  // Enable caching of array
3032  require_once DOL_DOCUMENT_ROOT.'/core/lib/memory.lib.php';
3033  $cachekey = 'socialnetworks_' . $conf->entity;
3034  $dataretrieved = dol_getcache($cachekey);
3035  if (!is_null($dataretrieved)) {
3036  $socialnetworks = $dataretrieved;
3037  } else {
3038  $sql = "SELECT rowid, code, label, url, icon, active FROM ".MAIN_DB_PREFIX."c_socialnetworks";
3039  $sql .= " WHERE entity=".$conf->entity;
3040  $resql = $db->query($sql);
3041  if ($resql) {
3042  while ($obj = $db->fetch_object($resql)) {
3043  $socialnetworks[$obj->code] = array(
3044  'rowid' => $obj->rowid,
3045  'label' => $obj->label,
3046  'url' => $obj->url,
3047  'icon' => $obj->icon,
3048  'active' => $obj->active,
3049  );
3050  }
3051  }
3052  dol_setcache($cachekey, $socialnetworks); // If setting cache fails, this is not a problem, so we do not test result.
3053  }
3054 
3055  return $socialnetworks;
3056 }
3057 
3068 function dol_print_socialnetworks($value, $cid, $socid, $type, $dictsocialnetworks = array())
3069 {
3070  global $conf, $user, $langs;
3071 
3072  $htmllink = $value;
3073 
3074  if (empty($value)) {
3075  return '&nbsp;';
3076  }
3077 
3078  if (!empty($type)) {
3079  $htmllink = '<div class="divsocialnetwork inline-block valignmiddle">';
3080  // Use dictionary definition for picto $dictsocialnetworks[$type]['icon']
3081  $htmllink .= '<span class="fa paddingright '.($dictsocialnetworks[$type]['icon'] ? $dictsocialnetworks[$type]['icon'] : 'fa-link').'"></span>';
3082  if ($type == 'skype') {
3083  $htmllink .= dol_escape_htmltag($value);
3084  $htmllink .= '&nbsp;';
3085  $htmllink .= '<a href="skype:';
3086  $htmllink .= dol_string_nospecial($value, '_', '', array('@'));
3087  $htmllink .= '?call" alt="'.$langs->trans("Call").'&nbsp;'.$value.'" title="'.dol_escape_htmltag($langs->trans("Call").' '.$value).'">';
3088  $htmllink .= '<img src="'.DOL_URL_ROOT.'/theme/common/skype_callbutton.png" border="0">';
3089  $htmllink .= '</a><a href="skype:';
3090  $htmllink .= dol_string_nospecial($value, '_', '', array('@'));
3091  $htmllink .= '?chat" alt="'.$langs->trans("Chat").'&nbsp;'.$value.'" title="'.dol_escape_htmltag($langs->trans("Chat").' '.$value).'">';
3092  $htmllink .= '<img class="paddingleft" src="'.DOL_URL_ROOT.'/theme/common/skype_chatbutton.png" border="0">';
3093  $htmllink .= '</a>';
3094  if (($cid || $socid) && !empty($conf->agenda->enabled) && $user->rights->agenda->myactions->create) {
3095  $addlink = 'AC_SKYPE';
3096  $link = '';
3097  if (!empty($conf->global->AGENDA_ADDACTIONFORSKYPE)) {
3098  $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>';
3099  }
3100  $htmllink .= ($link ? ' '.$link : '');
3101  }
3102  } else {
3103  if (!empty($dictsocialnetworks[$type]['url'])) {
3104  $tmpvirginurl = preg_replace('/\/?{socialid}/', '', $dictsocialnetworks[$type]['url']);
3105  if ($tmpvirginurl) {
3106  $value = preg_replace('/^www\.'.preg_quote($tmpvirginurl, '/').'\/?/', '', $value);
3107  $value = preg_replace('/^'.preg_quote($tmpvirginurl, '/').'\/?/', '', $value);
3108 
3109  $tmpvirginurl3 = preg_replace('/^https:\/\//i', 'https://www.', $tmpvirginurl);
3110  if ($tmpvirginurl3) {
3111  $value = preg_replace('/^www\.'.preg_quote($tmpvirginurl3, '/').'\/?/', '', $value);
3112  $value = preg_replace('/^'.preg_quote($tmpvirginurl3, '/').'\/?/', '', $value);
3113  }
3114 
3115  $tmpvirginurl2 = preg_replace('/^https?:\/\//i', '', $tmpvirginurl);
3116  if ($tmpvirginurl2) {
3117  $value = preg_replace('/^www\.'.preg_quote($tmpvirginurl2, '/').'\/?/', '', $value);
3118  $value = preg_replace('/^'.preg_quote($tmpvirginurl2, '/').'\/?/', '', $value);
3119  }
3120  }
3121  $link = str_replace('{socialid}', $value, $dictsocialnetworks[$type]['url']);
3122  if (preg_match('/^https?:\/\//i', $link)) {
3123  $htmllink .= '&nbsp;<a href="'.dol_sanitizeUrl($link, 0).'" target="_blank" rel="noopener noreferrer">'.dol_escape_htmltag($value).'</a>';
3124  } else {
3125  $htmllink .= '&nbsp;<a href="'.dol_sanitizeUrl($link, 1).'" target="_blank" rel="noopener noreferrer">'.dol_escape_htmltag($value).'</a>';
3126  }
3127  } else {
3128  $htmllink .= dol_escape_htmltag($value);
3129  }
3130  }
3131  $htmllink .= '</div>';
3132  } else {
3133  $langs->load("errors");
3134  $htmllink .= img_warning($langs->trans("ErrorBadSocialNetworkValue", $value));
3135  }
3136  return $htmllink;
3137 }
3138 
3149 function dol_print_profids($profID, $profIDtype, $countrycode = '', $addcpButton = 1, $separ = '&nbsp;')
3150 {
3151  global $mysoc;
3152 
3153  if (empty($profID) || empty($profIDtype)) {
3154  return '';
3155  }
3156  if (empty($countrycode)) $countrycode = $mysoc->country_code;
3157  $newProfID = $profID;
3158  $id = substr($profIDtype, -1);
3159  $ret = '';
3160  if (strtoupper($countrycode) == 'FR') {
3161  // France
3162  if ($id == 1 && dol_strlen($newProfID) == 9) $newProfID = substr($newProfID, 0, 3).$separ.substr($newProfID, 3, 3).$separ.substr($newProfID, 6, 3);
3163  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);
3164  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);
3165  }
3166  if (!empty($addcpButton)) $ret = showValueWithClipboardCPButton(dol_escape_htmltag($profID), ($addcpButton == 1 ? 1 : 0), $newProfID);
3167  else $ret = $newProfID;
3168  return $ret;
3169 }
3170 
3185 function dol_print_phone($phone, $countrycode = '', $cid = 0, $socid = 0, $addlink = '', $separ = "&nbsp;", $withpicto = '', $titlealt = '', $adddivfloat = 0)
3186 {
3187  global $conf, $user, $langs, $mysoc, $hookmanager;
3188 
3189  // Clean phone parameter
3190  $phone = preg_replace("/[\s.-]/", "", trim($phone));
3191  if (empty($phone)) {
3192  return '';
3193  }
3194  if (!empty($conf->global->MAIN_PHONE_SEPAR)) {
3195  $separ = $conf->global->MAIN_PHONE_SEPAR;
3196  }
3197  if (empty($countrycode) && is_object($mysoc)) {
3198  $countrycode = $mysoc->country_code;
3199  }
3200 
3201  // Short format for small screens
3202  if ($conf->dol_optimize_smallscreen) {
3203  $separ = '';
3204  }
3205 
3206  $newphone = $phone;
3207  if (strtoupper($countrycode) == "FR") {
3208  // France
3209  if (dol_strlen($phone) == 10) {
3210  $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);
3211  } elseif (dol_strlen($phone) == 7) {
3212  $newphone = substr($newphone, 0, 3).$separ.substr($newphone, 3, 2).$separ.substr($newphone, 5, 2);
3213  } elseif (dol_strlen($phone) == 9) {
3214  $newphone = substr($newphone, 0, 2).$separ.substr($newphone, 2, 3).$separ.substr($newphone, 5, 2).$separ.substr($newphone, 7, 2);
3215  } elseif (dol_strlen($phone) == 11) {
3216  $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);
3217  } elseif (dol_strlen($phone) == 12) {
3218  $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);
3219  } elseif (dol_strlen($phone) == 13) {
3220  $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);
3221  }
3222  } elseif (strtoupper($countrycode) == "CA") {
3223  if (dol_strlen($phone) == 10) {
3224  $newphone = ($separ != '' ? '(' : '').substr($newphone, 0, 3).($separ != '' ? ')' : '').$separ.substr($newphone, 3, 3).($separ != '' ? '-' : '').substr($newphone, 6, 4);
3225  }
3226  } elseif (strtoupper($countrycode) == "PT") {//Portugal
3227  if (dol_strlen($phone) == 13) {//ex: +351_ABC_DEF_GHI
3228  $newphone = substr($newphone, 0, 4).$separ.substr($newphone, 4, 3).$separ.substr($newphone, 7, 3).$separ.substr($newphone, 10, 3);
3229  }
3230  } elseif (strtoupper($countrycode) == "SR") {//Suriname
3231  if (dol_strlen($phone) == 10) {//ex: +597_ABC_DEF
3232  $newphone = substr($newphone, 0, 4).$separ.substr($newphone, 4, 3).$separ.substr($newphone, 7, 3);
3233  } elseif (dol_strlen($phone) == 11) {//ex: +597_ABC_DEFG
3234  $newphone = substr($newphone, 0, 4).$separ.substr($newphone, 4, 3).$separ.substr($newphone, 7, 4);
3235  }
3236  } elseif (strtoupper($countrycode) == "DE") {//Allemagne
3237  if (dol_strlen($phone) == 14) {//ex: +49_ABCD_EFGH_IJK
3238  $newphone = substr($newphone, 0, 3).$separ.substr($newphone, 3, 4).$separ.substr($newphone, 7, 4).$separ.substr($newphone, 11, 3);
3239  } elseif (dol_strlen($phone) == 13) {//ex: +49_ABC_DEFG_HIJ
3240  $newphone = substr($newphone, 0, 3).$separ.substr($newphone, 3, 3).$separ.substr($newphone, 6, 4).$separ.substr($newphone, 10, 3);
3241  }
3242  } elseif (strtoupper($countrycode) == "ES") {//Espagne
3243  if (dol_strlen($phone) == 12) {//ex: +34_ABC_DEF_GHI
3244  $newphone = substr($newphone, 0, 3).$separ.substr($newphone, 3, 3).$separ.substr($newphone, 6, 3).$separ.substr($newphone, 9, 3);
3245  }
3246  } elseif (strtoupper($countrycode) == "BF") {// Burkina Faso
3247  if (dol_strlen($phone) == 12) {//ex : +22 A BC_DE_FG_HI
3248  $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);
3249  }
3250  } elseif (strtoupper($countrycode) == "RO") {// Roumanie
3251  if (dol_strlen($phone) == 12) {//ex : +40 AB_CDE_FG_HI
3252  $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);
3253  }
3254  } elseif (strtoupper($countrycode) == "TR") {//Turquie
3255  if (dol_strlen($phone) == 13) {//ex : +90 ABC_DEF_GHIJ
3256  $newphone = substr($newphone, 0, 3).$separ.substr($newphone, 3, 3).$separ.substr($newphone, 6, 3).$separ.substr($newphone, 9, 4);
3257  }
3258  } elseif (strtoupper($countrycode) == "US") {//Etat-Unis
3259  if (dol_strlen($phone) == 12) {//ex: +1 ABC_DEF_GHIJ
3260  $newphone = substr($newphone, 0, 2).$separ.substr($newphone, 2, 3).$separ.substr($newphone, 5, 3).$separ.substr($newphone, 8, 4);
3261  }
3262  } elseif (strtoupper($countrycode) == "MX") {//Mexique
3263  if (dol_strlen($phone) == 12) {//ex: +52 ABCD_EFG_HI
3264  $newphone = substr($newphone, 0, 3).$separ.substr($newphone, 3, 4).$separ.substr($newphone, 7, 3).$separ.substr($newphone, 10, 2);
3265  } elseif (dol_strlen($phone) == 11) {//ex: +52 AB_CD_EF_GH
3266  $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);
3267  } elseif (dol_strlen($phone) == 13) {//ex: +52 ABC_DEF_GHIJ
3268  $newphone = substr($newphone, 0, 3).$separ.substr($newphone, 3, 3).$separ.substr($newphone, 6, 3).$separ.substr($newphone, 9, 4);
3269  }
3270  } elseif (strtoupper($countrycode) == "ML") {//Mali
3271  if (dol_strlen($phone) == 12) {//ex: +223 AB_CD_EF_GH
3272  $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);
3273  }
3274  } elseif (strtoupper($countrycode) == "TH") {//Thaïlande
3275  if (dol_strlen($phone) == 11) {//ex: +66_ABC_DE_FGH
3276  $newphone = substr($newphone, 0, 3).$separ.substr($newphone, 3, 3).$separ.substr($newphone, 6, 2).$separ.substr($newphone, 8, 3);
3277  } elseif (dol_strlen($phone) == 12) {//ex: +66_A_BCD_EF_GHI
3278  $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);
3279  }
3280  } elseif (strtoupper($countrycode) == "MU") {
3281  //Maurice
3282  if (dol_strlen($phone) == 11) {//ex: +230_ABC_DE_FG
3283  $newphone = substr($newphone, 0, 4).$separ.substr($newphone, 4, 3).$separ.substr($newphone, 7, 2).$separ.substr($newphone, 9, 2);
3284  } elseif (dol_strlen($phone) == 12) {//ex: +230_ABCD_EF_GH
3285  $newphone = substr($newphone, 0, 4).$separ.substr($newphone, 4, 4).$separ.substr($newphone, 8, 2).$separ.substr($newphone, 10, 2);
3286  }
3287  } elseif (strtoupper($countrycode) == "ZA") {//Afrique du sud
3288  if (dol_strlen($phone) == 12) {//ex: +27_AB_CDE_FG_HI
3289  $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);
3290  }
3291  } elseif (strtoupper($countrycode) == "SY") {//Syrie
3292  if (dol_strlen($phone) == 12) {//ex: +963_AB_CD_EF_GH
3293  $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);
3294  } elseif (dol_strlen($phone) == 13) {//ex: +963_AB_CD_EF_GHI
3295  $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);
3296  }
3297  } elseif (strtoupper($countrycode) == "AE") {//Emirats Arabes Unis
3298  if (dol_strlen($phone) == 12) {//ex: +971_ABC_DEF_GH
3299  $newphone = substr($newphone, 0, 4).$separ.substr($newphone, 4, 3).$separ.substr($newphone, 7, 3).$separ.substr($newphone, 10, 2);
3300  } elseif (dol_strlen($phone) == 13) {//ex: +971_ABC_DEF_GHI
3301  $newphone = substr($newphone, 0, 4).$separ.substr($newphone, 4, 3).$separ.substr($newphone, 7, 3).$separ.substr($newphone, 10, 3);
3302  } elseif (dol_strlen($phone) == 14) {//ex: +971_ABC_DEF_GHIK
3303  $newphone = substr($newphone, 0, 4).$separ.substr($newphone, 4, 3).$separ.substr($newphone, 7, 3).$separ.substr($newphone, 10, 4);
3304  }
3305  } elseif (strtoupper($countrycode) == "DZ") {//Algérie
3306  if (dol_strlen($phone) == 13) {//ex: +213_ABC_DEF_GHI
3307  $newphone = substr($newphone, 0, 4).$separ.substr($newphone, 4, 3).$separ.substr($newphone, 7, 3).$separ.substr($newphone, 10, 3);
3308  }
3309  } elseif (strtoupper($countrycode) == "BE") {//Belgique
3310  if (dol_strlen($phone) == 11) {//ex: +32_ABC_DE_FGH
3311  $newphone = substr($newphone, 0, 3).$separ.substr($newphone, 3, 3).$separ.substr($newphone, 6, 2).$separ.substr($newphone, 8, 3);
3312  } elseif (dol_strlen($phone) == 12) {//ex: +32_ABC_DEF_GHI
3313  $newphone = substr($newphone, 0, 3).$separ.substr($newphone, 3, 3).$separ.substr($newphone, 6, 3).$separ.substr($newphone, 9, 3);
3314  }
3315  } elseif (strtoupper($countrycode) == "PF") {//Polynésie française
3316  if (dol_strlen($phone) == 12) {//ex: +689_AB_CD_EF_GH
3317  $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);
3318  }
3319  } elseif (strtoupper($countrycode) == "CO") {//Colombie
3320  if (dol_strlen($phone) == 13) {//ex: +57_ABC_DEF_GH_IJ
3321  $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);
3322  }
3323  } elseif (strtoupper($countrycode) == "JO") {//Jordanie
3324  if (dol_strlen($phone) == 12) {//ex: +962_A_BCD_EF_GH
3325  $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);
3326  }
3327  } elseif (strtoupper($countrycode) == "JM") {//Jamaïque
3328  if (dol_strlen($newphone) == 12) {//ex: +1867_ABC_DEFG
3329  $newphone = substr($newphone, 0, 5).$separ.substr($newphone, 5, 3).$separ.substr($newphone, 8, 4);
3330  }
3331  } elseif (strtoupper($countrycode) == "MG") {//Madagascar
3332  if (dol_strlen($phone) == 13) {//ex: +261_AB_CD_EFG_HI
3333  $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);
3334  }
3335  } elseif (strtoupper($countrycode) == "GB") {//Royaume uni
3336  if (dol_strlen($phone) == 13) {//ex: +44_ABCD_EFG_HIJ
3337  $newphone = substr($newphone, 0, 3).$separ.substr($newphone, 3, 4).$separ.substr($newphone, 7, 3).$separ.substr($newphone, 10, 3);
3338  }
3339  } elseif (strtoupper($countrycode) == "CH") {//Suisse
3340  if (dol_strlen($phone) == 12) {//ex: +41_AB_CDE_FG_HI
3341  $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);
3342  } elseif (dol_strlen($phone) == 15) {// +41_AB_CDE_FGH_IJKL
3343  $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);
3344  }
3345  } elseif (strtoupper($countrycode) == "TN") {//Tunisie
3346  if (dol_strlen($phone) == 12) {//ex: +216_AB_CDE_FGH
3347  $newphone = substr($newphone, 0, 4).$separ.substr($newphone, 4, 2).$separ.substr($newphone, 6, 3).$separ.substr($newphone, 9, 3);
3348  }
3349  } elseif (strtoupper($countrycode) == "GF") {//Guyane francaise
3350  if (dol_strlen($phone) == 13) {//ex: +594_ABC_DE_FG_HI (ABC=594 de nouveau)
3351  $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);
3352  }
3353  } elseif (strtoupper($countrycode) == "GP") {//Guadeloupe
3354  if (dol_strlen($phone) == 13) {//ex: +590_ABC_DE_FG_HI (ABC=590 de nouveau)
3355  $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);
3356  }
3357  } elseif (strtoupper($countrycode) == "MQ") {//Martinique
3358  if (dol_strlen($phone) == 13) {//ex: +596_ABC_DE_FG_HI (ABC=596 de nouveau)
3359  $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);
3360  }
3361  } elseif (strtoupper($countrycode) == "IT") {//Italie
3362  if (dol_strlen($phone) == 12) {//ex: +39_ABC_DEF_GHI
3363  $newphone = substr($newphone, 0, 3).$separ.substr($newphone, 3, 3).$separ.substr($newphone, 6, 3).$separ.substr($newphone, 9, 3);
3364  } elseif (dol_strlen($phone) == 13) {//ex: +39_ABC_DEF_GH_IJ
3365  $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);
3366  }
3367  } elseif (strtoupper($countrycode) == "AU") {
3368  //Australie
3369  if (dol_strlen($phone) == 12) {
3370  //ex: +61_A_BCDE_FGHI
3371  $newphone = substr($newphone, 0, 3).$separ.substr($newphone, 3, 1).$separ.substr($newphone, 4, 4).$separ.substr($newphone, 8, 4);
3372  }
3373  }
3374  if (!empty($addlink)) { // Link on phone number (+ link to add action if conf->global->AGENDA_ADDACTIONFORPHONE set)
3375  if ($conf->browser->layout == 'phone' || (!empty($conf->clicktodial->enabled) && !empty($conf->global->CLICKTODIAL_USE_TEL_LINK_ON_PHONE_NUMBERS))) { // If phone or option for, we use link of phone
3376  $newphoneform = $newphone;
3377  $newphone = '<a href="tel:'.$phone.'"';
3378  $newphone .= '>'.$newphoneform.'</a>';
3379  } elseif (!empty($conf->clicktodial->enabled) && $addlink == 'AC_TEL') { // If click to dial, we use click to dial url
3380  if (empty($user->clicktodial_loaded)) {
3381  $user->fetch_clicktodial();
3382  }
3383 
3384  // Define urlmask
3385  $urlmask = 'ErrorClickToDialModuleNotConfigured';
3386  if (!empty($conf->global->CLICKTODIAL_URL)) {
3387  $urlmask = $conf->global->CLICKTODIAL_URL;
3388  }
3389  if (!empty($user->clicktodial_url)) {
3390  $urlmask = $user->clicktodial_url;
3391  }
3392 
3393  $clicktodial_poste = (!empty($user->clicktodial_poste) ?urlencode($user->clicktodial_poste) : '');
3394  $clicktodial_login = (!empty($user->clicktodial_login) ?urlencode($user->clicktodial_login) : '');
3395  $clicktodial_password = (!empty($user->clicktodial_password) ?urlencode($user->clicktodial_password) : '');
3396  // This line is for backward compatibility
3397  $url = sprintf($urlmask, urlencode($phone), $clicktodial_poste, $clicktodial_login, $clicktodial_password);
3398  // Thoose lines are for substitution
3399  $substitarray = array('__PHONEFROM__'=>$clicktodial_poste,
3400  '__PHONETO__'=>urlencode($phone),
3401  '__LOGIN__'=>$clicktodial_login,
3402  '__PASS__'=>$clicktodial_password);
3403  $url = make_substitutions($url, $substitarray);
3404  $newphonesav = $newphone;
3405  if (empty($conf->global->CLICKTODIAL_DO_NOT_USE_AJAX_CALL)) {
3406  // Default and recommended: New method using ajax without submiting a page making a javascript history.go(-1) back
3407  $newphone = '<a href="'.$url.'" class="cssforclicktodial"'; // Call of ajax is handled by the lib_foot.js.php on class 'cssforclicktodial'
3408  $newphone .= '>'.$newphonesav.'</a>';
3409  } else {
3410  // Old method
3411  $newphone = '<a href="'.$url.'"';
3412  if (!empty($conf->global->CLICKTODIAL_FORCENEWTARGET)) {
3413  $newphone .= ' target="_blank" rel="noopener noreferrer"';
3414  }
3415  $newphone .= '>'.$newphonesav.'</a>';
3416  }
3417  }
3418 
3419  //if (($cid || $socid) && ! empty($conf->agenda->enabled) && $user->rights->agenda->myactions->create)
3420  if (isModEnabled('agenda') && $user->rights->agenda->myactions->create) {
3421  $type = 'AC_TEL';
3422  $link = '';
3423  if ($addlink == 'AC_FAX') {
3424  $type = 'AC_FAX';
3425  }
3426  if (!empty($conf->global->AGENDA_ADDACTIONFORPHONE)) {
3427  $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>';
3428  }
3429  if ($link) {
3430  $newphone = '<div>'.$newphone.' '.$link.'</div>';
3431  }
3432  }
3433  }
3434 
3435  if (empty($titlealt)) {
3436  $titlealt = ($withpicto == 'fax' ? $langs->trans("Fax") : $langs->trans("Phone"));
3437  }
3438  $rep = '';
3439 
3440  if ($hookmanager) {
3441  $parameters = array('countrycode' => $countrycode, 'cid' => $cid, 'socid' => $socid, 'titlealt' => $titlealt, 'picto' => $withpicto);
3442  $reshook = $hookmanager->executeHooks('printPhone', $parameters, $phone);
3443  $rep .= $hookmanager->resPrint;
3444  }
3445  if (empty($reshook)) {
3446  $picto = '';
3447  if ($withpicto) {
3448  if ($withpicto == 'fax') {
3449  $picto = 'phoning_fax';
3450  } elseif ($withpicto == 'phone') {
3451  $picto = 'phoning';
3452  } elseif ($withpicto == 'mobile') {
3453  $picto = 'phoning_mobile';
3454  } else {
3455  $picto = '';
3456  }
3457  }
3458  if ($adddivfloat) {
3459  $rep .= '<div class="nospan float" style="margin-right: 10px">';
3460  } else {
3461  $rep .= '<span style="margin-right: 10px;">';
3462  }
3463  $rep .= ($withpicto ?img_picto($titlealt, 'object_'.$picto.'.png').' ' : '').$newphone;
3464  if ($adddivfloat) {
3465  $rep .= '</div>';
3466  } else {
3467  $rep .= '</span>';
3468  }
3469  }
3470 
3471  return $rep;
3472 }
3473 
3481 function dol_print_ip($ip, $mode = 0)
3482 {
3483  global $conf, $langs;
3484 
3485  $ret = '';
3486 
3487  if (empty($mode)) {
3488  $ret .= $ip;
3489  }
3490 
3491  if ($mode != 2) {
3492  $countrycode = dolGetCountryCodeFromIp($ip);
3493  if ($countrycode) { // If success, countrycode is us, fr, ...
3494  if (file_exists(DOL_DOCUMENT_ROOT.'/theme/common/flags/'.$countrycode.'.png')) {
3495  $ret .= ' '.img_picto($countrycode.' '.$langs->trans("AccordingToGeoIPDatabase"), DOL_URL_ROOT.'/theme/common/flags/'.$countrycode.'.png', '', 1);
3496  } else {
3497  $ret .= ' ('.$countrycode.')';
3498  }
3499  } else {
3500  // Nothing
3501  }
3502  }
3503 
3504  return $ret;
3505 }
3506 
3516 {
3517  if (empty($_SERVER['HTTP_X_FORWARDED_FOR']) || preg_match('/[^0-9\.\:,\[\]]/', $_SERVER['HTTP_X_FORWARDED_FOR'])) {
3518  if (empty($_SERVER['HTTP_CLIENT_IP']) || preg_match('/[^0-9\.\:,\[\]]/', $_SERVER['HTTP_CLIENT_IP'])) {
3519  if (empty($_SERVER["HTTP_CF_CONNECTING_IP"])) {
3520  $ip = (empty($_SERVER['REMOTE_ADDR']) ? '' : $_SERVER['REMOTE_ADDR']); // value may have been the IP of the proxy and not the client
3521  } else {
3522  $ip = $_SERVER["HTTP_CF_CONNECTING_IP"]; // value here may have been forged by client
3523  }
3524  } else {
3525  $ip = $_SERVER['HTTP_CLIENT_IP']; // value is clean here but may have been forged by proxy
3526  }
3527  } else {
3528  $ip = $_SERVER['HTTP_X_FORWARDED_FOR']; // value is clean here but may have been forged by proxy
3529  }
3530  return $ip;
3531 }
3532 
3541 function isHTTPS()
3542 {
3543  $isSecure = false;
3544  if (isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] == 'on') {
3545  $isSecure = true;
3546  } 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') {
3547  $isSecure = true;
3548  }
3549  return $isSecure;
3550 }
3551 
3559 {
3560  global $conf;
3561 
3562  $countrycode = '';
3563 
3564  if (!empty($conf->geoipmaxmind->enabled)) {
3565  $datafile = getDolGlobalString('GEOIPMAXMIND_COUNTRY_DATAFILE');
3566  //$ip='24.24.24.24';
3567  //$datafile='/usr/share/GeoIP/GeoIP.dat'; Note that this must be downloaded datafile (not same than datafile provided with ubuntu packages)
3568  include_once DOL_DOCUMENT_ROOT.'/core/class/dolgeoip.class.php';
3569  $geoip = new DolGeoIP('country', $datafile);
3570  //print 'ip='.$ip.' databaseType='.$geoip->gi->databaseType." GEOIP_CITY_EDITION_REV1=".GEOIP_CITY_EDITION_REV1."\n";
3571  $countrycode = $geoip->getCountryCodeFromIP($ip);
3572  }
3573 
3574  return $countrycode;
3575 }
3576 
3577 
3585 {
3586  global $conf, $langs, $user;
3587 
3588  //$ret=$user->xxx;
3589  $ret = '';
3590  if (!empty($conf->geoipmaxmind->enabled)) {
3591  $ip = getUserRemoteIP();
3592  $datafile = getDolGlobalString('GEOIPMAXMIND_COUNTRY_DATAFILE');
3593  //$ip='24.24.24.24';
3594  //$datafile='E:\Mes Sites\Web\Admin1\awstats\maxmind\GeoIP.dat';
3595  include_once DOL_DOCUMENT_ROOT.'/core/class/dolgeoip.class.php';
3596  $geoip = new DolGeoIP('country', $datafile);
3597  $countrycode = $geoip->getCountryCodeFromIP($ip);
3598  $ret = $countrycode;
3599  }
3600  return $ret;
3601 }
3602 
3615 function dol_print_address($address, $htmlid, $element, $id, $noprint = 0, $charfornl = '')
3616 {
3617  global $conf, $user, $langs, $hookmanager;
3618 
3619  $out = '';
3620 
3621  if ($address) {
3622  if ($hookmanager) {
3623  $parameters = array('element' => $element, 'id' => $id);
3624  $reshook = $hookmanager->executeHooks('printAddress', $parameters, $address);
3625  $out .= $hookmanager->resPrint;
3626  }
3627  if (empty($reshook)) {
3628  if (empty($charfornl)) {
3629  $out .= nl2br($address);
3630  } else {
3631  $out .= preg_replace('/[\r\n]+/', $charfornl, $address);
3632  }
3633 
3634  // TODO Remove this block, we can add this using the hook now
3635  $showgmap = $showomap = 0;
3636  if (($element == 'thirdparty' || $element == 'societe') && !empty($conf->google->enabled) && !empty($conf->global->GOOGLE_ENABLE_GMAPS)) {
3637  $showgmap = 1;
3638  }
3639  if ($element == 'contact' && !empty($conf->google->enabled) && !empty($conf->global->GOOGLE_ENABLE_GMAPS_CONTACTS)) {
3640  $showgmap = 1;
3641  }
3642  if ($element == 'member' && !empty($conf->google->enabled) && !empty($conf->global->GOOGLE_ENABLE_GMAPS_MEMBERS)) {
3643  $showgmap = 1;
3644  }
3645  if (($element == 'thirdparty' || $element == 'societe') && !empty($conf->openstreetmap->enabled) && !empty($conf->global->OPENSTREETMAP_ENABLE_MAPS)) {
3646  $showomap = 1;
3647  }
3648  if ($element == 'contact' && !empty($conf->openstreetmap->enabled) && !empty($conf->global->OPENSTREETMAP_ENABLE_MAPS_CONTACTS)) {
3649  $showomap = 1;
3650  }
3651  if ($element == 'member' && !empty($conf->openstreetmap->enabled) && !empty($conf->global->OPENSTREETMAP_ENABLE_MAPS_MEMBERS)) {
3652  $showomap = 1;
3653  }
3654  if ($showgmap) {
3655  $url = dol_buildpath('/google/gmaps.php?mode='.$element.'&id='.$id, 1);
3656  $out .= ' <a href="'.$url.'" target="_gmaps"><img id="'.$htmlid.'" class="valigntextbottom" src="'.DOL_URL_ROOT.'/theme/common/gmap.png"></a>';
3657  }
3658  if ($showomap) {
3659  $url = dol_buildpath('/openstreetmap/maps.php?mode='.$element.'&id='.$id, 1);
3660  $out .= ' <a href="'.$url.'" target="_gmaps"><img id="'.$htmlid.'_openstreetmap" class="valigntextbottom" src="'.DOL_URL_ROOT.'/theme/common/gmap.png"></a>';
3661  }
3662  }
3663  }
3664  if ($noprint) {
3665  return $out;
3666  } else {
3667  print $out;
3668  }
3669 }
3670 
3671 
3681 function isValidEmail($address, $acceptsupervisorkey = 0, $acceptuserkey = 0)
3682 {
3683  if ($acceptsupervisorkey && $address == '__SUPERVISOREMAIL__') {
3684  return true;
3685  }
3686  if ($acceptuserkey && $address == '__USER_EMAIL__') {
3687  return true;
3688  }
3689  if (filter_var($address, FILTER_VALIDATE_EMAIL)) {
3690  return true;
3691  }
3692 
3693  return false;
3694 }
3695 
3704 function isValidMXRecord($domain)
3705 {
3706  if (function_exists('idn_to_ascii') && function_exists('checkdnsrr')) {
3707  if (!checkdnsrr(idn_to_ascii($domain), 'MX')) {
3708  return 0;
3709  }
3710  if (function_exists('getmxrr')) {
3711  $mxhosts = array();
3712  $weight = array();
3713  getmxrr(idn_to_ascii($domain), $mxhosts, $weight);
3714  if (count($mxhosts) > 1) {
3715  return 1;
3716  }
3717  if (count($mxhosts) == 1 && !empty($mxhosts[0])) {
3718  return 1;
3719  }
3720 
3721  return 0;
3722  }
3723  }
3724  return -1;
3725 }
3726 
3734 function isValidPhone($phone)
3735 {
3736  return true;
3737 }
3738 
3739 
3747 function dol_strlen($string, $stringencoding = 'UTF-8')
3748 {
3749  if (function_exists('mb_strlen')) {
3750  return mb_strlen($string, $stringencoding);
3751  } else {
3752  return strlen($string);
3753  }
3754 }
3755 
3766 function dol_substr($string, $start, $length, $stringencoding = '', $trunconbytes = 0)
3767 {
3768  global $langs;
3769 
3770  if (empty($stringencoding)) {
3771  $stringencoding = $langs->charset_output;
3772  }
3773 
3774  $ret = '';
3775  if (empty($trunconbytes)) {
3776  if (function_exists('mb_substr')) {
3777  $ret = mb_substr($string, $start, $length, $stringencoding);
3778  } else {
3779  $ret = substr($string, $start, $length);
3780  }
3781  } else {
3782  if (function_exists('mb_strcut')) {
3783  $ret = mb_strcut($string, $start, $length, $stringencoding);
3784  } else {
3785  $ret = substr($string, $start, $length);
3786  }
3787  }
3788  return $ret;
3789 }
3790 
3791 
3805 function dol_trunc($string, $size = 40, $trunc = 'right', $stringencoding = 'UTF-8', $nodot = 0, $display = 0)
3806 {
3807  global $conf;
3808 
3809  if (empty($size) || !empty($conf->global->MAIN_DISABLE_TRUNC)) {
3810  return $string;
3811  }
3812 
3813  if (empty($stringencoding)) {
3814  $stringencoding = 'UTF-8';
3815  }
3816  // reduce for small screen
3817  if ($conf->dol_optimize_smallscreen == 1 && $display == 1) {
3818  $size = round($size / 3);
3819  }
3820 
3821  // We go always here
3822  if ($trunc == 'right') {
3823  $newstring = dol_textishtml($string) ? dol_string_nohtmltag($string, 1) : $string;
3824  if (dol_strlen($newstring, $stringencoding) > ($size + ($nodot ? 0 : 1))) {
3825  // If nodot is 0 and size is 1 chars more, we don't trunc and don't add …
3826  return dol_substr($newstring, 0, $size, $stringencoding).($nodot ? '' : '…');
3827  } else {
3828  //return 'u'.$size.'-'.$newstring.'-'.dol_strlen($newstring,$stringencoding).'-'.$string;
3829  return $string;
3830  }
3831  } elseif ($trunc == 'middle') {
3832  $newstring = dol_textishtml($string) ? dol_string_nohtmltag($string, 1) : $string;
3833  if (dol_strlen($newstring, $stringencoding) > 2 && dol_strlen($newstring, $stringencoding) > ($size + 1)) {
3834  $size1 = round($size / 2);
3835  $size2 = round($size / 2);
3836  return dol_substr($newstring, 0, $size1, $stringencoding).'…'.dol_substr($newstring, dol_strlen($newstring, $stringencoding) - $size2, $size2, $stringencoding);
3837  } else {
3838  return $string;
3839  }
3840  } elseif ($trunc == 'left') {
3841  $newstring = dol_textishtml($string) ? dol_string_nohtmltag($string, 1) : $string;
3842  if (dol_strlen($newstring, $stringencoding) > ($size + ($nodot ? 0 : 1))) {
3843  // If nodot is 0 and size is 1 chars more, we don't trunc and don't add …
3844  return '…'.dol_substr($newstring, dol_strlen($newstring, $stringencoding) - $size, $size, $stringencoding);
3845  } else {
3846  return $string;
3847  }
3848  } elseif ($trunc == 'wrap') {
3849  $newstring = dol_textishtml($string) ? dol_string_nohtmltag($string, 1) : $string;
3850  if (dol_strlen($newstring, $stringencoding) > ($size + 1)) {
3851  return dol_substr($newstring, 0, $size, $stringencoding)."\n".dol_trunc(dol_substr($newstring, $size, dol_strlen($newstring, $stringencoding) - $size, $stringencoding), $size, $trunc);
3852  } else {
3853  return $string;
3854  }
3855  } else {
3856  return 'BadParam3CallingDolTrunc';
3857  }
3858 }
3859 
3880 function img_picto($titlealt, $picto, $moreatt = '', $pictoisfullpath = false, $srconly = 0, $notitle = 0, $alt = '', $morecss = '', $marginleftonlyshort = 2)
3881 {
3882  global $conf, $langs;
3883 
3884  // We forge fullpathpicto for image to $path/img/$picto. By default, we take DOL_URL_ROOT/theme/$conf->theme/img/$picto
3885  $url = DOL_URL_ROOT;
3886  $theme = isset($conf->theme) ? $conf->theme : null;
3887  $path = 'theme/'.$theme;
3888  // Define fullpathpicto to use into src
3889  if ($pictoisfullpath) {
3890  // Clean parameters
3891  if (!preg_match('/(\.png|\.gif|\.svg)$/i', $picto)) {
3892  $picto .= '.png';
3893  }
3894  $fullpathpicto = $picto;
3895  $reg = array();
3896  if (preg_match('/class="([^"]+)"/', $moreatt, $reg)) {
3897  $morecss .= ($morecss ? ' ' : '').$reg[1];
3898  $moreatt = str_replace('class="'.$reg[1].'"', '', $moreatt);
3899  }
3900  } else {
3901  $pictowithouttext = preg_replace('/(\.png|\.gif|\.svg)$/', '', $picto);
3902  $pictowithouttext = str_replace('object_', '', $pictowithouttext);
3903 
3904  if (strpos($pictowithouttext, 'fontawesome_') !== false || preg_match('/^fa-/', $pictowithouttext)) {
3905  // This is a font awesome image 'fonwtawesome_xxx' or 'fa-xxx'
3906  $pictowithouttext = str_replace('fa-', '', $pictowithouttext);
3907  $pictowithouttextarray = explode('_', $pictowithouttext);
3908  $marginleftonlyshort = 0;
3909 
3910  if (!empty($pictowithouttextarray[1])) {
3911  $fakey = 'fa-'.$pictowithouttextarray[1];
3912  $fa = empty($pictowithouttextarray[2]) ? 'fa' : $pictowithouttextarray[2];
3913  $facolor = empty($pictowithouttextarray[3]) ? '' : $pictowithouttextarray[3];
3914  $fasize = empty($pictowithouttextarray[4]) ? '' : $pictowithouttextarray[4];
3915  } else {
3916  $fakey = 'fa-'.$pictowithouttext;
3917  $fa = 'fa';
3918  $facolor = '';
3919  $fasize = '';
3920  }
3921 
3922  // This snippet only needed since function img_edit accepts only one additional parameter: no separate one for css only.
3923  // class/style need to be extracted to avoid duplicate class/style validation errors when $moreatt is added to the end of the attributes.
3924  $morestyle = '';
3925  $reg = array();
3926  if (preg_match('/class="([^"]+)"/', $moreatt, $reg)) {
3927  $morecss .= ($morecss ? ' ' : '').$reg[1];
3928  $moreatt = str_replace('class="'.$reg[1].'"', '', $moreatt);
3929  }
3930  if (preg_match('/style="([^"]+)"/', $moreatt, $reg)) {
3931  $morestyle = $reg[1];
3932  $moreatt = str_replace('style="'.$reg[1].'"', '', $moreatt);
3933  }
3934  $moreatt = trim($moreatt);
3935 
3936  $enabledisablehtml = '<span class="'.$fa.' '.$fakey.($marginleftonlyshort ? ($marginleftonlyshort == 1 ? ' marginleftonlyshort' : ' marginleftonly') : '');
3937  $enabledisablehtml .= ($morecss ? ' '.$morecss : '').'" style="'.($fasize ? ('font-size: '.$fasize.';') : '').($facolor ? (' color: '.$facolor.';') : '').($morestyle ? ' '.$morestyle : '').'"'.(($notitle || empty($titlealt)) ? '' : ' title="'.dol_escape_htmltag($titlealt).'"').($moreatt ? ' '.$moreatt : '').'>';
3938  /*if (!empty($conf->global->MAIN_OPTIMIZEFORTEXTBROWSER)) {
3939  $enabledisablehtml .= $titlealt;
3940  }*/
3941  $enabledisablehtml .= '</span>';
3942 
3943  return $enabledisablehtml;
3944  }
3945 
3946  if (empty($srconly) && in_array($pictowithouttext, array(
3947  '1downarrow', '1uparrow', '1leftarrow', '1rightarrow', '1uparrow_selected', '1downarrow_selected', '1leftarrow_selected', '1rightarrow_selected',
3948  'accountancy', 'accounting_account', 'account', 'accountline', 'action', 'add', 'address', 'angle-double-down', 'angle-double-up', 'asset',
3949  'bank_account', 'barcode', 'bank', 'bell', 'bill', 'billa', 'billr', 'billd', 'bookmark', 'bom', 'briefcase-medical', 'bug', 'building',
3950  'card', 'calendarlist', 'calendar', 'calendarmonth', 'calendarweek', 'calendarday', 'calendarperuser', 'calendarpertype',
3951  'cash-register', 'category', 'chart', 'check', 'clock', 'close_title', 'cog', 'collab', 'company', 'contact', 'country', 'contract', 'conversation', 'cron', 'cubes',
3952  'currency', 'multicurrency',
3953  'delete', 'dolly', 'dollyrevert', 'donation', 'download', 'dynamicprice',
3954  'edit', 'ellipsis-h', 'email', 'entity', 'envelope', 'eraser', 'establishment', 'expensereport', 'external-link-alt', 'external-link-square-alt',
3955  'filter', 'file-code', 'file-export', 'file-import', 'file-upload', 'autofill', 'folder', 'folder-open', 'folder-plus',
3956  'generate', 'globe', 'globe-americas', 'graph', 'grip', 'grip_title', 'group',
3957  'help', 'holiday',
3958  'images', 'incoterm', 'info', 'intervention', 'inventory', 'intracommreport', 'knowledgemanagement',
3959  'label', 'language', 'line', 'link', 'list', 'list-alt', 'listlight', 'loan', 'lot', 'long-arrow-alt-right',
3960  'margin', 'map-marker-alt', 'member', 'meeting', 'money-bill-alt', 'movement', 'mrp', 'note', 'next',
3961  'off', 'on', 'order',
3962  'paiment', 'paragraph', 'play', 'pdf', 'phone', 'phoning', 'phoning_mobile', 'phoning_fax', 'playdisabled', 'previous', 'poll', 'pos', 'printer', 'product', 'propal', 'puce',
3963  'stock', 'resize', 'service', 'stats', 'trip',
3964  'security', 'setup', 'share-alt', 'sign-out', 'split', 'stripe', 'stripe-s', 'switch_off', 'switch_on', 'switch_on_red', 'tools', 'unlink', 'uparrow', 'user', 'user-tie', 'vcard', 'wrench',
3965  'github', 'google', 'jabber', 'skype', 'twitter', 'facebook', 'linkedin', 'instagram', 'snapchat', 'youtube', 'google-plus-g', 'whatsapp',
3966  'chevron-left', 'chevron-right', 'chevron-down', 'chevron-top', 'commercial', 'companies',
3967  'generic', 'home', 'hrm', 'members', 'products', 'invoicing',
3968  'partnership', 'payment', 'payment_vat', 'pencil-ruler', 'preview', 'project', 'projectpub', 'projecttask', 'question', 'refresh', 'region',
3969  'salary', 'shipment', 'state', 'supplier_invoice', 'supplier_invoicea', 'supplier_invoicer', 'supplier_invoiced',
3970  'technic', 'ticket',
3971  'error', 'warning',
3972  'recent', 'reception', 'recruitmentcandidature', 'recruitmentjobposition', 'resource', 'recurring',
3973  'shapes', 'square', 'stop-circle', 'supplier', 'supplier_proposal', 'supplier_order', 'supplier_invoice',
3974  'timespent', 'title_setup', 'title_accountancy', 'title_bank', 'title_hrm', 'title_agenda',
3975  'uncheck', 'user-cog', 'user-injured', 'user-md', 'vat', 'website', 'workstation', 'webhook', 'world', 'private',
3976  'conferenceorbooth', 'eventorganization',
3977  'stamp', 'signature'
3978  ))) {
3979  $fakey = $pictowithouttext;
3980  $facolor = '';
3981  $fasize = '';
3982  $fa = 'fas';
3983  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'))) {
3984  $fa = 'far';
3985  }
3986  if (in_array($pictowithouttext, array('black-tie', 'github', 'google', 'skype', 'twitter', 'facebook', 'linkedin', 'instagram', 'snapchat', 'stripe', 'stripe-s', 'youtube', 'google-plus-g', 'whatsapp'))) {
3987  $fa = 'fab';
3988  }
3989 
3990  $arrayconvpictotofa = array(
3991  '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',
3992  'bank_account'=>'university',
3993  'bill'=>'file-invoice-dollar', 'billa'=>'file-excel', 'billr'=>'file-invoice-dollar', 'billd'=>'file-medical',
3994  'supplier_invoice'=>'file-invoice-dollar', 'supplier_invoicea'=>'file-excel', 'supplier_invoicer'=>'file-invoice-dollar', 'supplier_invoiced'=>'file-medical',
3995  'bom'=>'shapes',
3996  'card'=>'address-card', 'chart'=>'chart-line', 'company'=>'building', 'contact'=>'address-book', 'contract'=>'suitcase', 'collab'=>'people-arrows', 'conversation'=>'comments', 'country'=>'globe-americas', 'cron'=>'business-time',
3997  'donation'=>'file-alt', 'dynamicprice'=>'hand-holding-usd',
3998  'setup'=>'cog', 'companies'=>'building', 'products'=>'cube', 'commercial'=>'suitcase', 'invoicing'=>'coins',
3999  'accounting'=>'search-dollar', 'category'=>'tag', 'dollyrevert'=>'dolly',
4000  'generate'=>'plus-square', 'hrm'=>'user-tie', 'incoterm'=>'truck-loading',
4001  'margin'=>'calculator', 'members'=>'user-friends', 'ticket'=>'ticket-alt', 'globe'=>'external-link-alt', 'lot'=>'barcode',
4002  'email'=>'at', 'establishment'=>'building', 'edit'=>'pencil-alt', 'entity'=>'globe',
4003  'graph'=>'chart-line', 'grip_title'=>'arrows-alt', 'grip'=>'arrows-alt', 'help'=>'question-circle',
4004  'generic'=>'file', 'holiday'=>'umbrella-beach',
4005  'info'=>'info-circle', 'inventory'=>'boxes', 'intracommreport'=>'globe-europe', 'knowledgemanagement'=>'ticket-alt', 'label'=>'layer-group', 'line'=>'bars', 'loan'=>'money-bill-alt',
4006  'member'=>'user-alt', 'meeting'=>'chalkboard-teacher', 'mrp'=>'cubes', 'next'=>'arrow-alt-circle-right',
4007  'trip'=>'wallet', 'expensereport'=>'wallet', 'group'=>'users', 'movement'=>'people-carry',
4008  'sign-out'=>'sign-out-alt',
4009  'switch_off'=>'toggle-off', 'switch_on'=>'toggle-on', 'switch_on_red'=>'toggle-on', 'check'=>'check', 'bookmark'=>'star',
4010  'bank'=>'university', 'close_title'=>'times', 'delete'=>'trash', 'filter'=>'filter',
4011  'list-alt'=>'list-alt', 'calendarlist'=>'bars', 'calendar'=>'calendar-alt', 'calendarmonth'=>'calendar-alt', 'calendarweek'=>'calendar-week', 'calendarday'=>'calendar-day', 'calendarperuser'=>'table',
4012  'intervention'=>'ambulance', 'invoice'=>'file-invoice-dollar', 'currency'=>'dollar-sign', 'multicurrency'=>'dollar-sign', 'order'=>'file-invoice',
4013  'error'=>'exclamation-triangle', 'warning'=>'exclamation-triangle',
4014  'other'=>'square',
4015  'playdisabled'=>'play', 'pdf'=>'file-pdf', 'poll'=>'check-double', 'pos'=>'cash-register', 'preview'=>'binoculars', 'project'=>'project-diagram', 'projectpub'=>'project-diagram', 'projecttask'=>'tasks', 'propal'=>'file-signature',
4016  'partnership'=>'handshake', 'payment'=>'money-check-alt', 'payment_vat'=>'money-check-alt', 'phoning'=>'phone', 'phoning_mobile'=>'mobile-alt', 'phoning_fax'=>'fax', 'previous'=>'arrow-alt-circle-left', 'printer'=>'print', 'product'=>'cube', 'puce'=>'angle-right',
4017  'recent' => 'question', 'reception'=>'dolly', 'recruitmentjobposition'=>'id-card-alt', 'recruitmentcandidature'=>'id-badge',
4018  'resize'=>'crop', 'supplier_order'=>'dol-order_supplier', 'supplier_proposal'=>'file-signature',
4019  'refresh'=>'redo', 'region'=>'map-marked', 'resource'=>'laptop-house', 'recurring'=>'history',
4020  'service'=>'concierge-bell',
4021  'state'=>'map-marked-alt', 'security'=>'key', 'salary'=>'wallet', 'shipment'=>'dolly', 'stock'=>'box-open', 'stats' => 'chart-bar', 'split'=>'code-branch', 'stripe'=>'stripe-s',
4022  'supplier'=>'building', 'technic'=>'cogs',
4023  'timespent'=>'clock', 'title_setup'=>'tools', 'title_accountancy'=>'money-check-alt', 'title_bank'=>'university', 'title_hrm'=>'umbrella-beach',
4024  'title_agenda'=>'calendar-alt',
4025  'uncheck'=>'times', 'uparrow'=>'share', 'vat'=>'money-check-alt', 'vcard'=>'address-card',
4026  'jabber'=>'comment-o',
4027  'website'=>'globe-americas', 'workstation'=>'pallet', 'webhook'=>'bullseye', 'world'=>'globe', 'private'=>'user-lock',
4028  'conferenceorbooth'=>'chalkboard-teacher', 'eventorganization'=>'project-diagram'
4029  );
4030  if ($pictowithouttext == 'off') {
4031  $fakey = 'fa-square';
4032  $fasize = '1.3em';
4033  } elseif ($pictowithouttext == 'on') {
4034  $fakey = 'fa-check-square';
4035  $fasize = '1.3em';
4036  } elseif ($pictowithouttext == 'listlight') {
4037  $fakey = 'fa-download';
4038  $marginleftonlyshort = 1;
4039  } elseif ($pictowithouttext == 'printer') {
4040  $fakey = 'fa-print';
4041  $fasize = '1.2em';
4042  } elseif ($pictowithouttext == 'note') {
4043  $fakey = 'fa-sticky-note';
4044  $marginleftonlyshort = 1;
4045  } elseif (in_array($pictowithouttext, array('1uparrow', '1downarrow', '1leftarrow', '1rightarrow', '1uparrow_selected', '1downarrow_selected', '1leftarrow_selected', '1rightarrow_selected'))) {
4046  $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');
4047  $fakey = 'fa-'.$convertarray[$pictowithouttext];
4048  if (preg_match('/selected/', $pictowithouttext)) {
4049  $facolor = '#888';
4050  }
4051  $marginleftonlyshort = 1;
4052  } elseif (!empty($arrayconvpictotofa[$pictowithouttext])) {
4053  $fakey = 'fa-'.$arrayconvpictotofa[$pictowithouttext];
4054  } else {
4055  $fakey = 'fa-'.$pictowithouttext;
4056  }
4057 
4058  if (in_array($pictowithouttext, array('dollyrevert', 'member', 'members', 'contract', 'group', 'resource', 'shipment'))) {
4059  $morecss .= ' em092';
4060  }
4061  if (in_array($pictowithouttext, array('conferenceorbooth', 'collab', 'eventorganization', 'holiday', 'info', 'project', 'workstation'))) {
4062  $morecss .= ' em088';
4063  }
4064  if (in_array($pictowithouttext, array('asset', 'intervention', 'payment', 'loan', 'partnership', 'stock', 'technic'))) {
4065  $morecss .= ' em080';
4066  }
4067 
4068  // Define $marginleftonlyshort
4069  $arrayconvpictotomarginleftonly = array(
4070  'bank', 'check', 'delete', 'generic', 'grip', 'grip_title', 'jabber',
4071  'grip_title', 'grip', 'listlight', 'note', 'on', 'off', 'playdisabled', 'printer', 'resize', 'sign-out', 'stats', 'switch_on', 'switch_on_red', 'switch_off',
4072  'uparrow', '1uparrow', '1downarrow', '1leftarrow', '1rightarrow', '1uparrow_selected', '1downarrow_selected', '1leftarrow_selected', '1rightarrow_selected'
4073  );
4074  if (!isset($arrayconvpictotomarginleftonly[$pictowithouttext])) {
4075  $marginleftonlyshort = 0;
4076  }
4077 
4078  // Add CSS
4079  $arrayconvpictotomorcess = array(
4080  'action'=>'infobox-action', 'account'=>'infobox-bank_account', 'accounting_account'=>'infobox-bank_account', 'accountline'=>'infobox-bank_account', 'accountancy'=>'infobox-bank_account', 'asset'=>'infobox-bank_account',
4081  'bank_account'=>'infobox-bank_account',
4082  'bill'=>'infobox-commande', 'billa'=>'infobox-commande', 'billr'=>'infobox-commande', 'billd'=>'infobox-commande',
4083  'margin'=>'infobox-bank_account', 'conferenceorbooth'=>'infobox-project',
4084  'cash-register'=>'infobox-bank_account', 'contract'=>'infobox-contrat', 'check'=>'font-status4', 'collab'=>'infobox-action', 'conversation'=>'infobox-contrat',
4085  'donation'=>'infobox-commande', 'dolly'=>'infobox-commande', 'dollyrevert'=>'flip infobox-order_supplier',
4086  'ecm'=>'infobox-action', 'eventorganization'=>'infobox-project',
4087  'hrm'=>'infobox-adherent', 'group'=>'infobox-adherent', 'intervention'=>'infobox-contrat',
4088  'incoterm'=>'infobox-supplier_proposal',
4089  'currency'=>'infobox-bank_account', 'multicurrency'=>'infobox-bank_account',
4090  'members'=>'infobox-adherent', 'member'=>'infobox-adherent', 'money-bill-alt'=>'infobox-bank_account',
4091  'order'=>'infobox-commande',
4092  'user'=>'infobox-adherent', 'users'=>'infobox-adherent',
4093  'error'=>'pictoerror', 'warning'=>'pictowarning', 'switch_on'=>'font-status4', 'switch_on_red'=>'font-status8',
4094  'holiday'=>'infobox-holiday', 'info'=>'opacityhigh', 'invoice'=>'infobox-commande',
4095  'knowledgemanagement'=>'infobox-contrat rotate90', 'loan'=>'infobox-bank_account',
4096  'payment'=>'infobox-bank_account', 'payment_vat'=>'infobox-bank_account', 'poll'=>'infobox-adherent', 'pos'=>'infobox-bank_account', 'project'=>'infobox-project', 'projecttask'=>'infobox-project',
4097  'propal'=>'infobox-propal', 'private'=>'infobox-project',
4098  'reception'=>'flip', 'recruitmentjobposition'=>'infobox-adherent', 'recruitmentcandidature'=>'infobox-adherent',
4099  'resource'=>'infobox-action',
4100  'salary'=>'infobox-bank_account', 'shipment'=>'infobox-commande', 'supplier_invoice'=>'infobox-order_supplier', 'supplier_invoicea'=>'infobox-order_supplier', 'supplier_invoiced'=>'infobox-order_supplier',
4101  'supplier'=>'infobox-order_supplier', 'supplier_order'=>'infobox-order_supplier', 'supplier_proposal'=>'infobox-supplier_proposal',
4102  'ticket'=>'infobox-contrat', 'title_accountancy'=>'infobox-bank_account', 'title_hrm'=>'infobox-holiday', 'expensereport'=>'infobox-expensereport', 'trip'=>'infobox-expensereport', 'title_agenda'=>'infobox-action',
4103  'vat'=>'infobox-bank_account',
4104  //'title_setup'=>'infobox-action', 'tools'=>'infobox-action',
4105  'list-alt'=>'imgforviewmode', 'calendar'=>'imgforviewmode', 'calendarweek'=>'imgforviewmode', 'calendarmonth'=>'imgforviewmode', 'calendarday'=>'imgforviewmode', 'calendarperuser'=>'imgforviewmode'
4106  );
4107  if (!empty($arrayconvpictotomorcess[$pictowithouttext])) {
4108  $morecss .= ($morecss ? ' ' : '').$arrayconvpictotomorcess[$pictowithouttext];
4109  }
4110 
4111  // Define $color
4112  $arrayconvpictotocolor = array(
4113  'address'=>'#6c6aa8', 'building'=>'#6c6aa8', 'bom'=>'#a69944',
4114  'cog'=>'#999', 'companies'=>'#6c6aa8', 'company'=>'#6c6aa8', 'contact'=>'#6c6aa8', 'cron'=>'#555',
4115  'dynamicprice'=>'#a69944',
4116  'edit'=>'#444', 'note'=>'#999', 'error'=>'', 'help'=>'#bbb', 'listlight'=>'#999', 'language'=>'#555',
4117  //'dolly'=>'#a69944', 'dollyrevert'=>'#a69944',
4118  'lot'=>'#a69944',
4119  'map-marker-alt'=>'#aaa', 'mrp'=>'#a69944', 'product'=>'#a69944', 'service'=>'#a69944', 'inventory'=>'#a69944', 'stock'=>'#a69944', 'movement'=>'#a69944',
4120  'other'=>'#ddd', 'world'=>'#986c6a',
4121  'partnership'=>'#6c6aa8', 'playdisabled'=>'#ccc', 'printer'=>'#444', 'projectpub'=>'#986c6a', 'reception'=>'#a69944', 'resize'=>'#444', 'rss'=>'#cba',
4122  //'shipment'=>'#a69944',
4123  'security'=>'#999', 'square'=>'#888', 'stop-circle'=>'#888', 'stats'=>'#444', 'switch_off'=>'#999', 'technic'=>'#999', 'timespent'=>'#555',
4124  'uncheck'=>'#800', 'uparrow'=>'#555', 'user-cog'=>'#999', 'country'=>'#aaa', 'globe-americas'=>'#aaa', 'region'=>'#aaa', 'state'=>'#aaa',
4125  'website'=>'#304', 'workstation'=>'#a69944'
4126  );
4127  if (isset($arrayconvpictotocolor[$pictowithouttext])) {
4128  $facolor = $arrayconvpictotocolor[$pictowithouttext];
4129  }
4130 
4131  // This snippet only needed since function img_edit accepts only one additional parameter: no separate one for css only.
4132  // class/style need to be extracted to avoid duplicate class/style validation errors when $moreatt is added to the end of the attributes.
4133  $morestyle = '';
4134  $reg = array();
4135  if (preg_match('/class="([^"]+)"/', $moreatt, $reg)) {
4136  $morecss .= ($morecss ? ' ' : '').$reg[1];
4137  $moreatt = str_replace('class="'.$reg[1].'"', '', $moreatt);
4138  }
4139  if (preg_match('/style="([^"]+)"/', $moreatt, $reg)) {
4140  $morestyle = $reg[1];
4141  $moreatt = str_replace('style="'.$reg[1].'"', '', $moreatt);
4142  }
4143  $moreatt = trim($moreatt);
4144 
4145  $enabledisablehtml = '<span class="'.$fa.' '.$fakey.($marginleftonlyshort ? ($marginleftonlyshort == 1 ? ' marginleftonlyshort' : ' marginleftonly') : '');
4146  $enabledisablehtml .= ($morecss ? ' '.$morecss : '').'" style="'.($fasize ? ('font-size: '.$fasize.';') : '').($facolor ? (' color: '.$facolor.';') : '').($morestyle ? ' '.$morestyle : '').'"'.(($notitle || empty($titlealt)) ? '' : ' title="'.dol_escape_htmltag($titlealt).'"').($moreatt ? ' '.$moreatt : '').'>';
4147  /*if (!empty($conf->global->MAIN_OPTIMIZEFORTEXTBROWSER)) {
4148  $enabledisablehtml .= $titlealt;
4149  }*/
4150  $enabledisablehtml .= '</span>';
4151 
4152  return $enabledisablehtml;
4153  }
4154 
4155  if (!empty($conf->global->MAIN_OVERWRITE_THEME_PATH)) {
4156  $path = $conf->global->MAIN_OVERWRITE_THEME_PATH.'/theme/'.$theme; // If the theme does not have the same name as the module
4157  } elseif (!empty($conf->global->MAIN_OVERWRITE_THEME_RES)) {
4158  $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
4159  } elseif (!empty($conf->modules_parts['theme']) && array_key_exists($theme, $conf->modules_parts['theme'])) {
4160  $path = $theme.'/theme/'.$theme; // If the theme have the same name as the module
4161  }
4162 
4163  // If we ask an image into $url/$mymodule/img (instead of default path)
4164  $regs = array();
4165  if (preg_match('/^([^@]+)@([^@]+)$/i', $picto, $regs)) {
4166  $picto = $regs[1];
4167  $path = $regs[2]; // $path is $mymodule
4168  }
4169 
4170  // Clean parameters
4171  if (!preg_match('/(\.png|\.gif|\.svg)$/i', $picto)) {
4172  $picto .= '.png';
4173  }
4174  // If alt path are defined, define url where img file is, according to physical path
4175  // ex: array(["main"]=>"/home/maindir/htdocs", ["alt0"]=>"/home/moddir0/htdocs", ...)
4176  foreach ($conf->file->dol_document_root as $type => $dirroot) {
4177  if ($type == 'main') {
4178  continue;
4179  }
4180  // This need a lot of time, that's why enabling alternative dir like "custom" dir is not recommanded
4181  if (file_exists($dirroot.'/'.$path.'/img/'.$picto)) {
4182  $url = DOL_URL_ROOT.$conf->file->dol_url_root[$type];
4183  break;
4184  }
4185  }
4186 
4187  // $url is '' or '/custom', $path is current theme or
4188  $fullpathpicto = $url.'/'.$path.'/img/'.$picto;
4189  }
4190 
4191  if ($srconly) {
4192  return $fullpathpicto;
4193  }
4194  // tag title is used for tooltip on <a>, tag alt can be used with very simple text on image for blind people
4195  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
4196 }
4197 
4211 function img_object($titlealt, $picto, $moreatt = '', $pictoisfullpath = false, $srconly = 0, $notitle = 0)
4212 {
4213  if (strpos($picto, '^') === 0) {
4214  return img_picto($titlealt, str_replace('^', '', $picto), $moreatt, $pictoisfullpath, $srconly, $notitle);
4215  } else {
4216  return img_picto($titlealt, 'object_'.$picto, $moreatt, $pictoisfullpath, $srconly, $notitle);
4217  }
4218 }
4219 
4231 function img_weather($titlealt, $picto, $moreatt = '', $pictoisfullpath = 0, $morecss = '')
4232 {
4233  global $conf;
4234 
4235  if (is_numeric($picto)) {
4236  //$leveltopicto = array(0=>'weather-clear.png', 1=>'weather-few-clouds.png', 2=>'weather-clouds.png', 3=>'weather-many-clouds.png', 4=>'weather-storm.png');
4237  //$picto = $leveltopicto[$picto];
4238  return '<i class="fa fa-weather-level'.$picto.'"></i>';
4239  } elseif (!preg_match('/(\.png|\.gif)$/i', $picto)) {
4240  $picto .= '.png';
4241  }
4242 
4243  $path = DOL_URL_ROOT.'/theme/'.$conf->theme.'/img/weather/'.$picto;
4244 
4245  return img_picto($titlealt, $path, $moreatt, 1, 0, 0, '', $morecss);
4246 }
4247 
4259 function img_picto_common($titlealt, $picto, $moreatt = '', $pictoisfullpath = 0, $notitle = 0)
4260 {
4261  global $conf;
4262 
4263  if (!preg_match('/(\.png|\.gif)$/i', $picto)) {
4264  $picto .= '.png';
4265  }
4266 
4267  if ($pictoisfullpath) {
4268  $path = $picto;
4269  } else {
4270  $path = DOL_URL_ROOT.'/theme/common/'.$picto;
4271 
4272  if (!empty($conf->global->MAIN_MODULE_CAN_OVERWRITE_COMMONICONS)) {
4273  $themepath = DOL_DOCUMENT_ROOT.'/theme/'.$conf->theme.'/img/'.$picto;
4274 
4275  if (file_exists($themepath)) {
4276  $path = $themepath;
4277  }
4278  }
4279  }
4280 
4281  return img_picto($titlealt, $path, $moreatt, 1, 0, $notitle);
4282 }
4283 
4296 function img_action($titlealt, $numaction, $picto = '')
4297 {
4298  global $langs;
4299 
4300  if (empty($titlealt) || $titlealt == 'default') {
4301  if ($numaction == '-1' || $numaction == 'ST_NO') {
4302  $numaction = -1;
4303  $titlealt = $langs->transnoentitiesnoconv('ChangeDoNotContact');
4304  } elseif ($numaction == '0' || $numaction == 'ST_NEVER') {
4305  $numaction = 0;
4306  $titlealt = $langs->transnoentitiesnoconv('ChangeNeverContacted');
4307  } elseif ($numaction == '1' || $numaction == 'ST_TODO') {
4308  $numaction = 1;
4309  $titlealt = $langs->transnoentitiesnoconv('ChangeToContact');
4310  } elseif ($numaction == '2' || $numaction == 'ST_PEND') {
4311  $numaction = 2;
4312  $titlealt = $langs->transnoentitiesnoconv('ChangeContactInProcess');
4313  } elseif ($numaction == '3' || $numaction == 'ST_DONE') {
4314  $numaction = 3;
4315  $titlealt = $langs->transnoentitiesnoconv('ChangeContactDone');
4316  } else {
4317  $titlealt = $langs->transnoentitiesnoconv('ChangeStatus '.$numaction);
4318  $numaction = 0;
4319  }
4320  }
4321  if (!is_numeric($numaction)) {
4322  $numaction = 0;
4323  }
4324 
4325  return img_picto($titlealt, !empty($picto) ? $picto : 'stcomm'.$numaction.'.png');
4326 }
4327 
4335 function img_pdf($titlealt = 'default', $size = 3)
4336 {
4337  global $langs;
4338 
4339  if ($titlealt == 'default') {
4340  $titlealt = $langs->trans('Show');
4341  }
4342 
4343  return img_picto($titlealt, 'pdf'.$size.'.png');
4344 }
4345 
4353 function img_edit_add($titlealt = 'default', $other = '')
4354 {
4355  global $langs;
4356 
4357  if ($titlealt == 'default') {
4358  $titlealt = $langs->trans('Add');
4359  }
4360 
4361  return img_picto($titlealt, 'edit_add.png', $other);
4362 }
4370 function img_edit_remove($titlealt = 'default', $other = '')
4371 {
4372  global $langs;
4373 
4374  if ($titlealt == 'default') {
4375  $titlealt = $langs->trans('Remove');
4376  }
4377 
4378  return img_picto($titlealt, 'edit_remove.png', $other);
4379 }
4380 
4389 function img_edit($titlealt = 'default', $float = 0, $other = '')
4390 {
4391  global $langs;
4392 
4393  if ($titlealt == 'default') {
4394  $titlealt = $langs->trans('Modify');
4395  }
4396 
4397  return img_picto($titlealt, 'edit.png', ($float ? 'style="float: '.($langs->tab_translate["DIRECTION"] == 'rtl' ? 'left' : 'right').'"' : "").($other ? ' '.$other : ''));
4398 }
4399 
4408 function img_view($titlealt = 'default', $float = 0, $other = 'class="valignmiddle"')
4409 {
4410  global $langs;
4411 
4412  if ($titlealt == 'default') {
4413  $titlealt = $langs->trans('View');
4414  }
4415 
4416  $moreatt = ($float ? 'style="float: right" ' : '').$other;
4417 
4418  return img_picto($titlealt, 'view.png', $moreatt);
4419 }
4420 
4429 function img_delete($titlealt = 'default', $other = 'class="pictodelete"', $morecss = '')
4430 {
4431  global $langs;
4432 
4433  if ($titlealt == 'default') {
4434  $titlealt = $langs->trans('Delete');
4435  }
4436 
4437  return img_picto($titlealt, 'delete.png', $other, false, 0, 0, '', $morecss);
4438 }
4439 
4447 function img_printer($titlealt = "default", $other = '')
4448 {
4449  global $langs;
4450  if ($titlealt == "default") {
4451  $titlealt = $langs->trans("Print");
4452  }
4453  return img_picto($titlealt, 'printer.png', $other);
4454 }
4455 
4463 function img_split($titlealt = 'default', $other = 'class="pictosplit"')
4464 {
4465  global $langs;
4466 
4467  if ($titlealt == 'default') {
4468  $titlealt = $langs->trans('Split');
4469  }
4470 
4471  return img_picto($titlealt, 'split.png', $other);
4472 }
4473 
4481 function img_help($usehelpcursor = 1, $usealttitle = 1)
4482 {
4483  global $langs;
4484 
4485  if ($usealttitle) {
4486  if (is_string($usealttitle)) {
4487  $usealttitle = dol_escape_htmltag($usealttitle);
4488  } else {
4489  $usealttitle = $langs->trans('Info');
4490  }
4491  }
4492 
4493  return img_picto($usealttitle, 'info.png', 'style="vertical-align: middle;'.($usehelpcursor == 1 ? ' cursor: help' : ($usehelpcursor == 2 ? ' cursor: pointer' : '')).'"');
4494 }
4495 
4502 function img_info($titlealt = 'default')
4503 {
4504  global $langs;
4505 
4506  if ($titlealt == 'default') {
4507  $titlealt = $langs->trans('Informations');
4508  }
4509 
4510  return img_picto($titlealt, 'info.png', 'style="vertical-align: middle;"');
4511 }
4512 
4521 function img_warning($titlealt = 'default', $moreatt = '', $morecss = 'pictowarning')
4522 {
4523  global $langs;
4524 
4525  if ($titlealt == 'default') {
4526  $titlealt = $langs->trans('Warning');
4527  }
4528 
4529  //return '<div class="imglatecoin">'.img_picto($titlealt, 'warning_white.png', 'class="pictowarning valignmiddle"'.($moreatt ? ($moreatt == '1' ? ' style="float: right"' : ' '.$moreatt): '')).'</div>';
4530  return img_picto($titlealt, 'warning.png', 'class="'.$morecss.'"'.($moreatt ? ($moreatt == '1' ? ' style="float: right"' : ' '.$moreatt) : ''));
4531 }
4532 
4539 function img_error($titlealt = 'default')
4540 {
4541  global $langs;
4542 
4543  if ($titlealt == 'default') {
4544  $titlealt = $langs->trans('Error');
4545  }
4546 
4547  return img_picto($titlealt, 'error.png');
4548 }
4549 
4557 function img_next($titlealt = 'default', $moreatt = '')
4558 {
4559  global $langs;
4560 
4561  if ($titlealt == 'default') {
4562  $titlealt = $langs->trans('Next');
4563  }
4564 
4565  //return img_picto($titlealt, 'next.png', $moreatt);
4566  return '<span class="fa fa-chevron-right paddingright paddingleft" title="'.dol_escape_htmltag($titlealt).'"></span>';
4567 }
4568 
4576 function img_previous($titlealt = 'default', $moreatt = '')
4577 {
4578  global $langs;
4579 
4580  if ($titlealt == 'default') {
4581  $titlealt = $langs->trans('Previous');
4582  }
4583 
4584  //return img_picto($titlealt, 'previous.png', $moreatt);
4585  return '<span class="fa fa-chevron-left paddingright paddingleft" title="'.dol_escape_htmltag($titlealt).'"></span>';
4586 }
4587 
4596 function img_down($titlealt = 'default', $selected = 0, $moreclass = '')
4597 {
4598  global $langs;
4599 
4600  if ($titlealt == 'default') {
4601  $titlealt = $langs->trans('Down');
4602  }
4603 
4604  return img_picto($titlealt, ($selected ? '1downarrow_selected.png' : '1downarrow.png'), 'class="imgdown'.($moreclass ? " ".$moreclass : "").'"');
4605 }
4606 
4615 function img_up($titlealt = 'default', $selected = 0, $moreclass = '')
4616 {
4617  global $langs;
4618 
4619  if ($titlealt == 'default') {
4620  $titlealt = $langs->trans('Up');
4621  }
4622 
4623  return img_picto($titlealt, ($selected ? '1uparrow_selected.png' : '1uparrow.png'), 'class="imgup'.($moreclass ? " ".$moreclass : "").'"');
4624 }
4625 
4634 function img_left($titlealt = 'default', $selected = 0, $moreatt = '')
4635 {
4636  global $langs;
4637 
4638  if ($titlealt == 'default') {
4639  $titlealt = $langs->trans('Left');
4640  }
4641 
4642  return img_picto($titlealt, ($selected ? '1leftarrow_selected.png' : '1leftarrow.png'), $moreatt);
4643 }
4644 
4653 function img_right($titlealt = 'default', $selected = 0, $moreatt = '')
4654 {
4655  global $langs;
4656 
4657  if ($titlealt == 'default') {
4658  $titlealt = $langs->trans('Right');
4659  }
4660 
4661  return img_picto($titlealt, ($selected ? '1rightarrow_selected.png' : '1rightarrow.png'), $moreatt);
4662 }
4663 
4671 function img_allow($allow, $titlealt = 'default')
4672 {
4673  global $langs;
4674 
4675  if ($titlealt == 'default') {
4676  $titlealt = $langs->trans('Active');
4677  }
4678 
4679  if ($allow == 1) {
4680  return img_picto($titlealt, 'tick.png');
4681  }
4682 
4683  return '-';
4684 }
4685 
4693 function img_credit_card($brand, $morecss = null)
4694 {
4695  if (is_null($morecss)) {
4696  $morecss = 'fa-2x';
4697  }
4698 
4699  if ($brand == 'visa' || $brand == 'Visa') {
4700  $brand = 'cc-visa';
4701  } elseif ($brand == 'mastercard' || $brand == 'MasterCard') {
4702  $brand = 'cc-mastercard';
4703  } elseif ($brand == 'amex' || $brand == 'American Express') {
4704  $brand = 'cc-amex';
4705  } elseif ($brand == 'discover' || $brand == 'Discover') {
4706  $brand = 'cc-discover';
4707  } elseif ($brand == 'jcb' || $brand == 'JCB') {
4708  $brand = 'cc-jcb';
4709  } elseif ($brand == 'diners' || $brand == 'Diners club') {
4710  $brand = 'cc-diners-club';
4711  } elseif (!in_array($brand, array('cc-visa', 'cc-mastercard', 'cc-amex', 'cc-discover', 'cc-jcb', 'cc-diners-club'))) {
4712  $brand = 'credit-card';
4713  }
4714 
4715  return '<span class="fa fa-'.$brand.' fa-fw'.($morecss ? ' '.$morecss : '').'"></span>';
4716 }
4717 
4726 function img_mime($file, $titlealt = '', $morecss = '')
4727 {
4728  require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
4729 
4730  $mimetype = dol_mimetype($file, '', 1);
4731  $mimeimg = dol_mimetype($file, '', 2);
4732  $mimefa = dol_mimetype($file, '', 4);
4733 
4734  if (empty($titlealt)) {
4735  $titlealt = 'Mime type: '.$mimetype;
4736  }
4737 
4738  //return img_picto_common($titlealt, 'mime/'.$mimeimg, 'class="'.$morecss.'"');
4739  return '<i class="fa fa-'.$mimefa.' paddingright'.($morecss ? ' '.$morecss : '').'"'.($titlealt ? ' title="'.$titlealt.'"' : '').'></i>';
4740 }
4741 
4742 
4750 function img_search($titlealt = 'default', $other = '')
4751 {
4752  global $conf, $langs;
4753 
4754  if ($titlealt == 'default') {
4755  $titlealt = $langs->trans('Search');
4756  }
4757 
4758  $img = img_picto($titlealt, 'search.png', $other, false, 1);
4759 
4760  $input = '<input type="image" class="liste_titre" name="button_search" src="'.$img.'" ';
4761  $input .= 'value="'.dol_escape_htmltag($titlealt).'" title="'.dol_escape_htmltag($titlealt).'" >';
4762 
4763  return $input;
4764 }
4765 
4773 function img_searchclear($titlealt = 'default', $other = '')
4774 {
4775  global $conf, $langs;
4776 
4777  if ($titlealt == 'default') {
4778  $titlealt = $langs->trans('Search');
4779  }
4780 
4781  $img = img_picto($titlealt, 'searchclear.png', $other, false, 1);
4782 
4783  $input = '<input type="image" class="liste_titre" name="button_removefilter" src="'.$img.'" ';
4784  $input .= 'value="'.dol_escape_htmltag($titlealt).'" title="'.dol_escape_htmltag($titlealt).'" >';
4785 
4786  return $input;
4787 }
4788 
4800 function info_admin($text, $infoonimgalt = 0, $nodiv = 0, $admin = '1', $morecss = 'hideonsmartphone', $textfordropdown = '')
4801 {
4802  global $conf, $langs;
4803 
4804  if ($infoonimgalt) {
4805  $result = img_picto($text, 'info', 'class="'.($morecss ? ' '.$morecss : '').'"');
4806  } else {
4807  if (empty($conf->use_javascript_ajax)) {
4808  $textfordropdown = '';
4809  }
4810 
4811  $class = (empty($admin) ? 'undefined' : ($admin == '1' ? 'info' : $admin));
4812  $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>');
4813 
4814  if ($textfordropdown) {
4815  $tmpresult = '<span class="'.$class.'text opacitymedium cursorpointer">'.$langs->trans($textfordropdown).' '.img_picto($langs->trans($textfordropdown), '1downarrow').'</span>';
4816  $tmpresult .= '<script type="text/javascript">
4817  jQuery(document).ready(function() {
4818  jQuery(".'.$class.'text").click(function() {
4819  console.log("toggle text");
4820  jQuery(".'.$class.'").toggle();
4821  });
4822  });
4823  </script>';
4824 
4825  $result = $tmpresult.$result;
4826  }
4827  }
4828 
4829  return $result;
4830 }
4831 
4832 
4844 function dol_print_error($db = '', $error = '', $errors = null)
4845 {
4846  global $conf, $langs, $argv;
4847  global $dolibarr_main_prod;
4848 
4849  $out = '';
4850  $syslog = '';
4851 
4852  // If error occurs before the $lang object was loaded
4853  if (!$langs) {
4854  require_once DOL_DOCUMENT_ROOT.'/core/class/translate.class.php';
4855  $langs = new Translate('', $conf);
4856  $langs->load("main");
4857  }
4858 
4859  // Load translation files required by the error messages
4860  $langs->loadLangs(array('main', 'errors'));
4861 
4862  if ($_SERVER['DOCUMENT_ROOT']) { // Mode web
4863  $out .= $langs->trans("DolibarrHasDetectedError").".<br>\n";
4864  if (getDolGlobalInt('MAIN_FEATURES_LEVEL') > 0) {
4865  $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";
4866  }
4867  $out .= $langs->trans("InformationToHelpDiagnose").":<br>\n";
4868 
4869  $out .= "<b>".$langs->trans("Date").":</b> ".dol_print_date(time(), 'dayhourlog')."<br>\n";
4870  $out .= "<b>".$langs->trans("Dolibarr").":</b> ".DOL_VERSION." - https://www.dolibarr.org<br>\n";
4871  if (isset($conf->global->MAIN_FEATURES_LEVEL)) {
4872  $out .= "<b>".$langs->trans("LevelOfFeature").":</b> ".getDolGlobalInt('MAIN_FEATURES_LEVEL')."<br>\n";
4873  }
4874  if (function_exists("phpversion")) {
4875  $out .= "<b>".$langs->trans("PHP").":</b> ".phpversion()."<br>\n";
4876  }
4877  $out .= "<b>".$langs->trans("Server").":</b> ".(isset($_SERVER["SERVER_SOFTWARE"]) ? dol_htmlentities($_SERVER["SERVER_SOFTWARE"], ENT_COMPAT) : '')."<br>\n";
4878  if (function_exists("php_uname")) {
4879  $out .= "<b>".$langs->trans("OS").":</b> ".php_uname()."<br>\n";
4880  }
4881  $out .= "<b>".$langs->trans("UserAgent").":</b> ".(isset($_SERVER["HTTP_USER_AGENT"]) ? dol_htmlentities($_SERVER["HTTP_USER_AGENT"], ENT_COMPAT) : '')."<br>\n";
4882  $out .= "<br>\n";
4883  $out .= "<b>".$langs->trans("RequestedUrl").":</b> ".dol_htmlentities($_SERVER["REQUEST_URI"], ENT_COMPAT)."<br>\n";
4884  $out .= "<b>".$langs->trans("Referer").":</b> ".(isset($_SERVER["HTTP_REFERER"]) ? dol_htmlentities($_SERVER["HTTP_REFERER"], ENT_COMPAT) : '')."<br>\n";
4885  $out .= "<b>".$langs->trans("MenuManager").":</b> ".(isset($conf->standard_menu) ? dol_htmlentities($conf->standard_menu, ENT_COMPAT) : '')."<br>\n";
4886  $out .= "<br>\n";
4887  $syslog .= "url=".dol_escape_htmltag($_SERVER["REQUEST_URI"]);
4888  $syslog .= ", query_string=".dol_escape_htmltag($_SERVER["QUERY_STRING"]);
4889  } else // Mode CLI
4890  {
4891  $out .= '> '.$langs->transnoentities("ErrorInternalErrorDetected").":\n".$argv[0]."\n";
4892  $syslog .= "pid=".dol_getmypid();
4893  }
4894 
4895  if (!empty($conf->modules)) {
4896  $out .= "<b>".$langs->trans("Modules").":</b> ".join(', ', $conf->modules)."<br>\n";
4897  }
4898 
4899  if (is_object($db)) {
4900  if ($_SERVER['DOCUMENT_ROOT']) { // Mode web
4901  $out .= "<b>".$langs->trans("DatabaseTypeManager").":</b> ".$db->type."<br>\n";
4902  $out .= "<b>".$langs->trans("RequestLastAccessInError").":</b> ".($db->lastqueryerror() ? dol_escape_htmltag($db->lastqueryerror()) : $langs->trans("ErrorNoRequestInError"))."<br>\n";
4903  $out .= "<b>".$langs->trans("ReturnCodeLastAccessInError").":</b> ".($db->lasterrno() ? dol_escape_htmltag($db->lasterrno()) : $langs->trans("ErrorNoRequestInError"))."<br>\n";
4904  $out .= "<b>".$langs->trans("InformationLastAccessInError").":</b> ".($db->lasterror() ? dol_escape_htmltag($db->lasterror()) : $langs->trans("ErrorNoRequestInError"))."<br>\n";
4905  $out .= "<br>\n";
4906  } else // Mode CLI
4907  {
4908  // No dol_escape_htmltag for output, we are in CLI mode
4909  $out .= '> '.$langs->transnoentities("DatabaseTypeManager").":\n".$db->type."\n";
4910  $out .= '> '.$langs->transnoentities("RequestLastAccessInError").":\n".($db->lastqueryerror() ? $db->lastqueryerror() : $langs->transnoentities("ErrorNoRequestInError"))."\n";
4911  $out .= '> '.$langs->transnoentities("ReturnCodeLastAccessInError").":\n".($db->lasterrno() ? $db->lasterrno() : $langs->transnoentities("ErrorNoRequestInError"))."\n";
4912  $out .= '> '.$langs->transnoentities("InformationLastAccessInError").":\n".($db->lasterror() ? $db->lasterror() : $langs->transnoentities("ErrorNoRequestInError"))."\n";
4913  }
4914  $syslog .= ", sql=".$db->lastquery();
4915  $syslog .= ", db_error=".$db->lasterror();
4916  }
4917 
4918  if ($error || $errors) {
4919  $langs->load("errors");
4920 
4921  // Merge all into $errors array
4922  if (is_array($error) && is_array($errors)) {
4923  $errors = array_merge($error, $errors);
4924  } elseif (is_array($error)) {
4925  $errors = $error;
4926  } elseif (is_array($errors)) {
4927  $errors = array_merge(array($error), $errors);
4928  } else {
4929  $errors = array_merge(array($error));
4930  }
4931 
4932  foreach ($errors as $msg) {
4933  if (empty($msg)) {
4934  continue;
4935  }
4936  if ($_SERVER['DOCUMENT_ROOT']) { // Mode web
4937  $out .= "<b>".$langs->trans("Message").":</b> ".dol_escape_htmltag($msg)."<br>\n";
4938  } else // Mode CLI
4939  {
4940  $out .= '> '.$langs->transnoentities("Message").":\n".$msg."\n";
4941  }
4942  $syslog .= ", msg=".$msg;
4943  }
4944  }
4945  if (empty($dolibarr_main_prod) && $_SERVER['DOCUMENT_ROOT'] && function_exists('xdebug_print_function_stack') && function_exists('xdebug_call_file')) {
4946  xdebug_print_function_stack();
4947  $out .= '<b>XDebug informations:</b>'."<br>\n";
4948  $out .= 'File: '.xdebug_call_file()."<br>\n";
4949  $out .= 'Line: '.xdebug_call_line()."<br>\n";
4950  $out .= 'Function: '.xdebug_call_function()."<br>\n";
4951  $out .= "<br>\n";
4952  }
4953 
4954  // Return a http error code if possible
4955  if (!headers_sent()) {
4956  http_response_code(500);
4957  }
4958 
4959  if (empty($dolibarr_main_prod)) {
4960  print $out;
4961  } else {
4962  if (empty($langs->defaultlang)) {
4963  $langs->setDefaultLang();
4964  }
4965  $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.
4966  // This should not happen, except if there is a bug somewhere. Enabled and check log in such case.
4967  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";
4968  print $langs->trans("DolibarrHasDetectedError").'. ';
4969  print $langs->trans("YouCanSetOptionDolibarrMainProdToZero");
4970  define("MAIN_CORE_ERROR", 1);
4971  }
4972 
4973  dol_syslog("Error ".$syslog, LOG_ERR);
4974 }
4975 
4986 function dol_print_error_email($prefixcode, $errormessage = '', $errormessages = array(), $morecss = 'error', $email = '')
4987 {
4988  global $langs, $conf;
4989 
4990  if (empty($email)) {
4991  $email = $conf->global->MAIN_INFO_SOCIETE_MAIL;
4992  }
4993 
4994  $langs->load("errors");
4995  $now = dol_now();
4996 
4997  print '<br><div class="center login_main_message"><div class="'.$morecss.'">';
4998  print $langs->trans("ErrorContactEMail", $email, $prefixcode.dol_print_date($now, '%Y%m%d%H%M%S'));
4999  if ($errormessage) {
5000  print '<br><br>'.$errormessage;
5001  }
5002  if (is_array($errormessages) && count($errormessages)) {
5003  foreach ($errormessages as $mesgtoshow) {
5004  print '<br><br>'.$mesgtoshow;
5005  }
5006  }
5007  print '</div></div>';
5008 }
5009 
5026 function print_liste_field_titre($name, $file = "", $field = "", $begin = "", $moreparam = "", $moreattrib = "", $sortfield = "", $sortorder = "", $prefix = "", $tooltip = "", $forcenowrapcolumntitle = 0)
5027 {
5028  print getTitleFieldOfList($name, 0, $file, $field, $begin, $moreparam, $moreattrib, $sortfield, $sortorder, $prefix, 0, $tooltip, $forcenowrapcolumntitle);
5029 }
5030 
5049 function getTitleFieldOfList($name, $thead = 0, $file = "", $field = "", $begin = "", $moreparam = "", $moreattrib = "", $sortfield = "", $sortorder = "", $prefix = "", $disablesortlink = 0, $tooltip = '', $forcenowrapcolumntitle = 0)
5050 {
5051  global $conf, $langs, $form;
5052  //print "$name, $file, $field, $begin, $options, $moreattrib, $sortfield, $sortorder<br>\n";
5053 
5054  if ($moreattrib == 'class="right"') {
5055  $prefix .= 'right '; // For backward compatibility
5056  }
5057 
5058  $sortorder = strtoupper($sortorder);
5059  $out = '';
5060  $sortimg = '';
5061 
5062  $tag = 'th';
5063  if ($thead == 2) {
5064  $tag = 'div';
5065  }
5066 
5067  $tmpsortfield = explode(',', $sortfield);
5068  $sortfield1 = trim($tmpsortfield[0]); // If $sortfield is 'd.datep,d.id', it becomes 'd.datep'
5069  $tmpfield = explode(',', $field);
5070  $field1 = trim($tmpfield[0]); // If $field is 'd.datep,d.id', it becomes 'd.datep'
5071 
5072  if (empty($conf->global->MAIN_DISABLE_WRAPPING_ON_COLUMN_TITLE) && empty($forcenowrapcolumntitle)) {
5073  $prefix = 'wrapcolumntitle '.$prefix;
5074  }
5075 
5076  //var_dump('field='.$field.' field1='.$field1.' sortfield='.$sortfield.' sortfield1='.$sortfield1);
5077  // If field is used as sort criteria we use a specific css class liste_titre_sel
5078  // Example if (sortfield,field)=("nom","xxx.nom") or (sortfield,field)=("nom","nom")
5079  $liste_titre = 'liste_titre';
5080  if ($field1 && ($sortfield1 == $field1 || $sortfield1 == preg_replace("/^[^\.]+\./", "", $field1))) {
5081  $liste_titre = 'liste_titre_sel';
5082  }
5083 
5084  $tagstart = '<'.$tag.' class="'.$prefix.$liste_titre.'" '.$moreattrib;
5085  //$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)).'"' : '');
5086  $tagstart .= ($name && empty($conf->global->MAIN_DISABLE_WRAPPING_ON_COLUMN_TITLE) && empty($forcenowrapcolumntitle) && !dol_textishtml($name)) ? ' title="'.dol_escape_htmltag($langs->trans($name)).'"' : '';
5087  $tagstart .= '>';
5088 
5089  if (empty($thead) && $field && empty($disablesortlink)) { // If this is a sort field
5090  $options = preg_replace('/sortfield=([a-zA-Z0-9,\s\.]+)/i', '', (is_scalar($moreparam) ? $moreparam : ''));
5091  $options = preg_replace('/sortorder=([a-zA-Z0-9,\s\.]+)/i', '', $options);
5092  $options = preg_replace('/&+/i', '&', $options);
5093  if (!preg_match('/^&/', $options)) {
5094  $options = '&'.$options;
5095  }
5096 
5097  $sortordertouseinlink = '';
5098  if ($field1 != $sortfield1) { // We are on another field than current sorted field
5099  if (preg_match('/^DESC/i', $sortorder)) {
5100  $sortordertouseinlink .= str_repeat('desc,', count(explode(',', $field)));
5101  } else { // We reverse the var $sortordertouseinlink
5102  $sortordertouseinlink .= str_repeat('asc,', count(explode(',', $field)));
5103  }
5104  } else { // We are on field that is the first current sorting criteria
5105  if (preg_match('/^ASC/i', $sortorder)) { // We reverse the var $sortordertouseinlink
5106  $sortordertouseinlink .= str_repeat('desc,', count(explode(',', $field)));
5107  } else {
5108  $sortordertouseinlink .= str_repeat('asc,', count(explode(',', $field)));
5109  }
5110  }
5111  $sortordertouseinlink = preg_replace('/,$/', '', $sortordertouseinlink);
5112  $out .= '<a class="reposition" href="'.$file.'?sortfield='.$field.'&sortorder='.$sortordertouseinlink.'&begin='.$begin.$options.'"';
5113  //$out .= (empty($conf->global->MAIN_DISABLE_WRAPPING_ON_COLUMN_TITLE) ? ' title="'.dol_escape_htmltag($langs->trans($name)).'"' : '');
5114  $out .= '>';
5115  }
5116  if ($tooltip) {
5117  // You can also use 'TranslationString:keyfortooltiponlick' for a tooltip on click.
5118  $tmptooltip = explode(':', $tooltip);
5119  $out .= $form->textwithpicto($langs->trans($name), $langs->trans($tmptooltip[0]), 1, 'help', '', 0, 3, (empty($tmptooltip[1]) ? '' : 'extra_'.str_replace('.', '_', $field).'_'.$tmptooltip[1]));
5120  } else {
5121  $out .= $langs->trans($name);
5122  }
5123 
5124  if (empty($thead) && $field && empty($disablesortlink)) { // If this is a sort field
5125  $out .= '</a>';
5126  }
5127 
5128  if (empty($thead) && $field) { // If this is a sort field
5129  $options = preg_replace('/sortfield=([a-zA-Z0-9,\s\.]+)/i', '', (is_scalar($moreparam) ? $moreparam : ''));
5130  $options = preg_replace('/sortorder=([a-zA-Z0-9,\s\.]+)/i', '', $options);
5131  $options = preg_replace('/&+/i', '&', $options);
5132  if (!preg_match('/^&/', $options)) {
5133  $options = '&'.$options;
5134  }
5135 
5136  if (!$sortorder || $field1 != $sortfield1) {
5137  //$out.= '<a href="'.$file.'?sortfield='.$field.'&sortorder=asc&begin='.$begin.$options.'">'.img_down("A-Z",0).'</a>';
5138  //$out.= '<a href="'.$file.'?sortfield='.$field.'&sortorder=desc&begin='.$begin.$options.'">'.img_up("Z-A",0).'</a>';
5139  } else {
5140  if (preg_match('/^DESC/', $sortorder)) {
5141  //$out.= '<a href="'.$file.'?sortfield='.$field.'&sortorder=asc&begin='.$begin.$options.'">'.img_down("A-Z",0).'</a>';
5142  //$out.= '<a href="'.$file.'?sortfield='.$field.'&sortorder=desc&begin='.$begin.$options.'">'.img_up("Z-A",1).'</a>';
5143  $sortimg .= '<span class="nowrap">'.img_up("Z-A", 0, 'paddingright').'</span>';
5144  }
5145  if (preg_match('/^ASC/', $sortorder)) {
5146  //$out.= '<a href="'.$file.'?sortfield='.$field.'&sortorder=asc&begin='.$begin.$options.'">'.img_down("A-Z",1).'</a>';
5147  //$out.= '<a href="'.$file.'?sortfield='.$field.'&sortorder=desc&begin='.$begin.$options.'">'.img_up("Z-A",0).'</a>';
5148  $sortimg .= '<span class="nowrap">'.img_down("A-Z", 0, 'paddingright').'</span>';
5149  }
5150  }
5151  }
5152 
5153  $tagend = '</'.$tag.'>';
5154 
5155  $out = $tagstart.$sortimg.$out.$tagend;
5156 
5157  return $out;
5158 }
5159 
5168 function print_titre($title)
5169 {
5170  dol_syslog(__FUNCTION__." is deprecated", LOG_WARNING);
5171 
5172  print '<div class="titre">'.$title.'</div>';
5173 }
5174 
5186 function print_fiche_titre($title, $mesg = '', $picto = 'generic', $pictoisfullpath = 0, $id = '')
5187 {
5188  print load_fiche_titre($title, $mesg, $picto, $pictoisfullpath, $id);
5189 }
5190 
5204 function load_fiche_titre($titre, $morehtmlright = '', $picto = 'generic', $pictoisfullpath = 0, $id = '', $morecssontable = '', $morehtmlcenter = '')
5205 {
5206  global $conf;
5207 
5208  $return = '';
5209 
5210  if ($picto == 'setup') {
5211  $picto = 'generic';
5212  }
5213 
5214  $return .= "\n";
5215  $return .= '<table '.($id ? 'id="'.$id.'" ' : '').'class="centpercent notopnoleftnoright table-fiche-title'.($morecssontable ? ' '.$morecssontable : '').'">'; // maring bottom must be same than into print_barre_list
5216  $return .= '<tr class="titre">';
5217  if ($picto) {
5218  $return .= '<td class="nobordernopadding widthpictotitle valignmiddle col-picto">'.img_picto('', $picto, 'class="valignmiddle widthpictotitle pictotitle"', $pictoisfullpath).'</td>';
5219  }
5220  $return .= '<td class="nobordernopadding valignmiddle col-title">';
5221  $return .= '<div class="titre inline-block">'.$titre.'</div>';
5222  $return .= '</td>';
5223  if (dol_strlen($morehtmlcenter)) {
5224  $return .= '<td class="nobordernopadding center valignmiddle">'.$morehtmlcenter.'</td>';
5225  }
5226  if (dol_strlen($morehtmlright)) {
5227  $return .= '<td class="nobordernopadding titre_right wordbreakimp right valignmiddle">'.$morehtmlright.'</td>';
5228  }
5229  $return .= '</tr></table>'."\n";
5230 
5231  return $return;
5232 }
5233 
5257 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 = '')
5258 {
5259  global $conf, $langs;
5260 
5261  $savlimit = $limit;
5262  $savtotalnboflines = $totalnboflines;
5263  $totalnboflines = abs((int) $totalnboflines);
5264 
5265  if ($picto == 'setup') {
5266  $picto = 'title_setup.png';
5267  }
5268  if (($conf->browser->name == 'ie') && $picto == 'generic') {
5269  $picto = 'title.gif';
5270  }
5271  if ($limit < 0) {
5272  $limit = $conf->liste_limit;
5273  }
5274  if ($savlimit != 0 && (($num > $limit) || ($num == -1) || ($limit == 0))) {
5275  $nextpage = 1;
5276  } else {
5277  $nextpage = 0;
5278  }
5279  //print 'totalnboflines='.$totalnboflines.'-savlimit='.$savlimit.'-limit='.$limit.'-num='.$num.'-nextpage='.$nextpage;
5280 
5281  print "\n";
5282  print "<!-- Begin title -->\n";
5283  print '<table class="centpercent notopnoleftnoright table-fiche-title'.($morecss ? ' '.$morecss : '').'"><tr>'; // maring bottom must be same than into load_fiche_tire
5284 
5285  // Left
5286 
5287  if ($picto && $titre) {
5288  print '<td class="nobordernopadding widthpictotitle valignmiddle col-picto">'.img_picto('', $picto, 'class="valignmiddle pictotitle widthpictotitle"', $pictoisfullpath).'</td>';
5289  }
5290  print '<td class="nobordernopadding valignmiddle col-title">';
5291  print '<div class="titre inline-block">'.$titre;
5292  if (!empty($titre) && $savtotalnboflines >= 0 && (string) $savtotalnboflines != '') {
5293  print '<span class="opacitymedium colorblack paddingleft">('.$totalnboflines.')</span>';
5294  }
5295  print '</div></td>';
5296 
5297  // Center
5298  if ($morehtmlcenter) {
5299  print '<td class="nobordernopadding center valignmiddle">'.$morehtmlcenter.'</td>';
5300  }
5301 
5302  // Right
5303  print '<td class="nobordernopadding valignmiddle right">';
5304  print '<input type="hidden" name="pageplusoneold" value="'.((int) $page + 1).'">';
5305  if ($sortfield) {
5306  $options .= "&sortfield=".urlencode($sortfield);
5307  }
5308  if ($sortorder) {
5309  $options .= "&sortorder=".urlencode($sortorder);
5310  }
5311  // Show navigation bar
5312  $pagelist = '';
5313  if ($savlimit != 0 && ($page > 0 || $num > $limit)) {
5314  if ($totalnboflines) { // If we know total nb of lines
5315  // Define nb of extra page links before and after selected page + ... + first or last
5316  $maxnbofpage = (empty($conf->dol_optimize_smallscreen) ? 4 : 0);
5317 
5318  if ($limit > 0) {
5319  $nbpages = ceil($totalnboflines / $limit);
5320  } else {
5321  $nbpages = 1;
5322  }
5323  $cpt = ($page - $maxnbofpage);
5324  if ($cpt < 0) {
5325  $cpt = 0;
5326  }
5327 
5328  if ($cpt >= 1) {
5329  if (empty($pagenavastextinput)) {
5330  $pagelist .= '<li class="pagination"><a href="'.$file.'?page=0'.$options.'">1</a></li>';
5331  if ($cpt > 2) {
5332  $pagelist .= '<li class="pagination"><span class="inactive">...</span></li>';
5333  } elseif ($cpt == 2) {
5334  $pagelist .= '<li class="pagination"><a href="'.$file.'?page=1'.$options.'">2</a></li>';
5335  }
5336  }
5337  }
5338 
5339  do {
5340  if ($pagenavastextinput) {
5341  if ($cpt == $page) {
5342  $pagelist .= '<li class="pagination"><input type="text" class="width25 center pageplusone" name="pageplusone" value="'.($page + 1).'"></li>';
5343  $pagelist .= '/';
5344  }
5345  } else {
5346  if ($cpt == $page) {
5347  $pagelist .= '<li class="pagination"><span class="active">'.($page + 1).'</span></li>';
5348  } else {
5349  $pagelist .= '<li class="pagination"><a href="'.$file.'?page='.$cpt.$options.'">'.($cpt + 1).'</a></li>';
5350  }
5351  }
5352  $cpt++;
5353  } while ($cpt < $nbpages && $cpt <= ($page + $maxnbofpage));
5354 
5355  if (empty($pagenavastextinput)) {
5356  if ($cpt < $nbpages) {
5357  if ($cpt < $nbpages - 2) {
5358  $pagelist .= '<li class="pagination"><span class="inactive">...</span></li>';
5359  } elseif ($cpt == $nbpages - 2) {
5360  $pagelist .= '<li class="pagination"><a href="'.$file.'?page='.($nbpages - 2).$options.'">'.($nbpages - 1).'</a></li>';
5361  }
5362  $pagelist .= '<li class="pagination"><a href="'.$file.'?page='.($nbpages - 1).$options.'">'.$nbpages.'</a></li>';
5363  }
5364  } else {
5365  //var_dump($page.' '.$cpt.' '.$nbpages);
5366  $pagelist .= '<li class="pagination paginationlastpage"><a href="'.$file.'?page='.($nbpages - 1).$options.'">'.$nbpages.'</a></li>';
5367  }
5368  } else {
5369  $pagelist .= '<li class="pagination"><span class="active">'.($page + 1)."</li>";
5370  }
5371  }
5372 
5373  if ($savlimit || $morehtmlright || $morehtmlrightbeforearrow) {
5374  print_fleche_navigation($page, $file, $options, $nextpage, $pagelist, $morehtmlright, $savlimit, $totalnboflines, $hideselectlimit, $morehtmlrightbeforearrow); // output the div and ul for previous/last completed with page numbers into $pagelist
5375  }
5376 
5377  // js to autoselect page field on focus
5378  if ($pagenavastextinput) {
5379  print ajax_autoselect('.pageplusone');
5380  }
5381 
5382  print '</td>';
5383 
5384  print '</tr></table>'."\n";
5385  print "<!-- End title -->\n\n";
5386 }
5387 
5403 function print_fleche_navigation($page, $file, $options = '', $nextpage = 0, $betweenarrows = '', $afterarrows = '', $limit = -1, $totalnboflines = 0, $hideselectlimit = 0, $beforearrows = '')
5404 {
5405  global $conf, $langs;
5406 
5407  print '<div class="pagination"><ul>';
5408  if ($beforearrows) {
5409  print '<li class="paginationbeforearrows">';
5410  print $beforearrows;
5411  print '</li>';
5412  }
5413  if ((int) $limit > 0 && empty($hideselectlimit)) {
5414  $pagesizechoices = '10:10,15:15,20:20,30:30,40:40,50:50,100:100,250:250,500:500,1000:1000';
5415  $pagesizechoices .= ',5000:5000,10000:10000,20000:20000';
5416  //$pagesizechoices.=',0:'.$langs->trans("All"); // Not yet supported
5417  //$pagesizechoices.=',2:2';
5418  if (!empty($conf->global->MAIN_PAGESIZE_CHOICES)) {
5419  $pagesizechoices = $conf->global->MAIN_PAGESIZE_CHOICES;
5420  }
5421 
5422  print '<li class="pagination">';
5423  print '<select class="flat selectlimit" name="limit" title="'.dol_escape_htmltag($langs->trans("MaxNbOfRecordPerPage")).'">';
5424  $tmpchoice = explode(',', $pagesizechoices);
5425  $tmpkey = $limit.':'.$limit;
5426  if (!in_array($tmpkey, $tmpchoice)) {
5427  $tmpchoice[] = $tmpkey;
5428  }
5429  $tmpkey = $conf->liste_limit.':'.$conf->liste_limit;
5430  if (!in_array($tmpkey, $tmpchoice)) {
5431  $tmpchoice[] = $tmpkey;
5432  }
5433  asort($tmpchoice, SORT_NUMERIC);
5434  foreach ($tmpchoice as $val) {
5435  $selected = '';
5436  $tmp = explode(':', $val);
5437  $key = $tmp[0];
5438  $val = $tmp[1];
5439  if ($key != '' && $val != '') {
5440  if ((int) $key == (int) $limit) {
5441  $selected = ' selected="selected"';
5442  }
5443  print '<option name="'.$key.'"'.$selected.'>'.dol_escape_htmltag($val).'</option>'."\n";
5444  }
5445  }
5446  print '</select>';
5447  if ($conf->use_javascript_ajax) {
5448  print '<!-- JS CODE TO ENABLE select limit to launch submit of page -->
5449  <script>
5450  jQuery(document).ready(function () {
5451  jQuery(".selectlimit").change(function() {
5452  console.log("Change limit. Send submit");
5453  $(this).parents(\'form:first\').submit();
5454  });
5455  });
5456  </script>
5457  ';
5458  }
5459  print '</li>';
5460  }
5461  if ($page > 0) {
5462  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>';
5463  }
5464  if ($betweenarrows) {
5465  print '<!--<div class="betweenarrows nowraponall inline-block">-->';
5466  print $betweenarrows;
5467  print '<!--</div>-->';
5468  }
5469  if ($nextpage > 0) {
5470  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>';
5471  }
5472  if ($afterarrows) {
5473  print '<li class="paginationafterarrows">';
5474  print $afterarrows;
5475  print '</li>';
5476  }
5477  print '</ul></div>'."\n";
5478 }
5479 
5480 
5492 function vatrate($rate, $addpercent = false, $info_bits = 0, $usestarfornpr = 0, $html = 0)
5493 {
5494  $morelabel = '';
5495 
5496  if (preg_match('/%/', $rate)) {
5497  $rate = str_replace('%', '', $rate);
5498  $addpercent = true;
5499  }
5500  $reg = array();
5501  if (preg_match('/\((.*)\)/', $rate, $reg)) {
5502  $morelabel = ' ('.$reg[1].')';
5503  $rate = preg_replace('/\s*'.preg_quote($morelabel, '/').'/', '', $rate);
5504  $morelabel = ' '.($html ? '<span class="opacitymedium">' : '').'('.$reg[1].')'.($html ? '</span>' : '');
5505  }
5506  if (preg_match('/\*/', $rate)) {
5507  $rate = str_replace('*', '', $rate);
5508  $info_bits |= 1;
5509  }
5510 
5511  // If rate is '9/9/9' we don't change it. If rate is '9.000' we apply price()
5512  if (!preg_match('/\//', $rate)) {
5513  $ret = price($rate, 0, '', 0, 0).($addpercent ? '%' : '');
5514  } else {
5515  // TODO Split on / and output with a price2num to have clean numbers without ton of 000.
5516  $ret = $rate.($addpercent ? '%' : '');
5517  }
5518  if (($info_bits & 1) && $usestarfornpr >= 0) {
5519  $ret .= ' *';
5520  }
5521  $ret .= $morelabel;
5522  return $ret;
5523 }
5524 
5525 
5541 function price($amount, $form = 0, $outlangs = '', $trunc = 1, $rounding = -1, $forcerounding = -1, $currency_code = '')
5542 {
5543  global $langs, $conf;
5544 
5545  // Clean parameters
5546  if (empty($amount)) {
5547  $amount = 0; // To have a numeric value if amount not defined or = ''
5548  }
5549  $amount = (is_numeric($amount) ? $amount : 0); // Check if amount is numeric, for example, an error occured when amount value = o (letter) instead 0 (number)
5550  if ($rounding < 0) {
5551  $rounding = min($conf->global->MAIN_MAX_DECIMALS_UNIT, $conf->global->MAIN_MAX_DECIMALS_TOT);
5552  }
5553  $nbdecimal = $rounding;
5554 
5555  if ($outlangs === 'none') {
5556  // Use international separators
5557  $dec = '.';
5558  $thousand = '';
5559  } else {
5560  // Output separators by default (french)
5561  $dec = ',';
5562  $thousand = ' ';
5563 
5564  // If $outlangs not forced, we use use language
5565  if (!is_object($outlangs)) {
5566  $outlangs = $langs;
5567  }
5568 
5569  if ($outlangs->transnoentitiesnoconv("SeparatorDecimal") != "SeparatorDecimal") {
5570  $dec = $outlangs->transnoentitiesnoconv("SeparatorDecimal");
5571  }
5572  if ($outlangs->transnoentitiesnoconv("SeparatorThousand") != "SeparatorThousand") {
5573  $thousand = $outlangs->transnoentitiesnoconv("SeparatorThousand");
5574  }
5575  if ($thousand == 'None') {
5576  $thousand = '';
5577  } elseif ($thousand == 'Space') {
5578  $thousand = ' ';
5579  }
5580  }
5581  //print "outlangs=".$outlangs->defaultlang." amount=".$amount." html=".$form." trunc=".$trunc." nbdecimal=".$nbdecimal." dec='".$dec."' thousand='".$thousand."'<br>";
5582 
5583  //print "amount=".$amount."-";
5584  $amount = str_replace(',', '.', $amount); // should be useless
5585  //print $amount."-";
5586  $datas = explode('.', $amount);
5587  $decpart = isset($datas[1]) ? $datas[1] : '';
5588  $decpart = preg_replace('/0+$/i', '', $decpart); // Supprime les 0 de fin de partie decimale
5589  //print "decpart=".$decpart."<br>";
5590  $end = '';
5591 
5592  // We increase nbdecimal if there is more decimal than asked (to not loose information)
5593  if (dol_strlen($decpart) > $nbdecimal) {
5594  $nbdecimal = dol_strlen($decpart);
5595  }
5596  // Si on depasse max
5597  if ($trunc && $nbdecimal > $conf->global->MAIN_MAX_DECIMALS_SHOWN) {
5598  $nbdecimal = $conf->global->MAIN_MAX_DECIMALS_SHOWN;
5599  if (preg_match('/\.\.\./i', $conf->global->MAIN_MAX_DECIMALS_SHOWN)) {
5600  // Si un affichage est tronque, on montre des ...
5601  $end = '...';
5602  }
5603  }
5604 
5605  // If force rounding
5606  if ($forcerounding >= 0) {
5607  $nbdecimal = $forcerounding;
5608  }
5609 
5610  // Format number
5611  $output = number_format($amount, $nbdecimal, $dec, $thousand);
5612  if ($form) {
5613  $output = preg_replace('/\s/', '&nbsp;', $output);
5614  $output = preg_replace('/\'/', '&#039;', $output);
5615  }
5616  // Add symbol of currency if requested
5617  $cursymbolbefore = $cursymbolafter = '';
5618  if ($currency_code && is_object($outlangs)) {
5619  if ($currency_code == 'auto') {
5620  $currency_code = $conf->currency;
5621  }
5622 
5623  $listofcurrenciesbefore = array('AUD', 'CAD', 'CNY', 'COP', 'CLP', 'GBP', 'HKD', 'MXN', 'PEN', 'USD');
5624  $listoflanguagesbefore = array('nl_NL');
5625  if (in_array($currency_code, $listofcurrenciesbefore) || in_array($outlangs->defaultlang, $listoflanguagesbefore)) {
5626  $cursymbolbefore .= $outlangs->getCurrencySymbol($currency_code);
5627  } else {
5628  $tmpcur = $outlangs->getCurrencySymbol($currency_code);
5629  $cursymbolafter .= ($tmpcur == $currency_code ? ' '.$tmpcur : $tmpcur);
5630  }
5631  }
5632  $output = $cursymbolbefore.$output.$end.($cursymbolafter ? ' ' : '').$cursymbolafter;
5633 
5634  return $output;
5635 }
5636 
5661 function price2num($amount, $rounding = '', $option = 0)
5662 {
5663  global $langs, $conf;
5664 
5665  // Round PHP function does not allow number like '1,234.56' nor '1.234,56' nor '1 234,56'
5666  // Numbers must be '1234.56'
5667  // Decimal delimiter for PHP and database SQL requests must be '.'
5668  $dec = ',';
5669  $thousand = ' ';
5670  if (is_null($langs)) { // $langs is not defined, we use english values.
5671  $dec = '.';
5672  $thousand = ',';
5673  } else {
5674  if ($langs->transnoentitiesnoconv("SeparatorDecimal") != "SeparatorDecimal") {
5675  $dec = $langs->transnoentitiesnoconv("SeparatorDecimal");
5676  }
5677  if ($langs->transnoentitiesnoconv("SeparatorThousand") != "SeparatorThousand") {
5678  $thousand = $langs->transnoentitiesnoconv("SeparatorThousand");
5679  }
5680  }
5681  if ($thousand == 'None') {
5682  $thousand = '';
5683  } elseif ($thousand == 'Space') {
5684  $thousand = ' ';
5685  }
5686  //print "amount=".$amount." html=".$form." trunc=".$trunc." nbdecimal=".$nbdecimal." dec='".$dec."' thousand='".$thousand."'<br>";
5687 
5688  // Convert value to universal number format (no thousand separator, '.' as decimal separator)
5689  if ($option != 1) { // If not a PHP number or unknown, we change or clean format
5690  //print "\n".'PP'.$amount.' - '.$dec.' - '.$thousand.' - '.intval($amount).'<br>';
5691  if (!is_numeric($amount)) {
5692  $amount = preg_replace('/[a-zA-Z\/\\\*\(\)<>\_]/', '', $amount);
5693  }
5694 
5695  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
5696  $amount = str_replace($thousand, '', $amount);
5697  }
5698 
5699  // Convert amount to format with dolibarr dec and thousand (this is because PHP convert a number
5700  // to format defined by LC_NUMERIC after a calculation and we want source format to be like defined by Dolibarr setup.
5701  // So if number was already a good number, it is converted into local Dolibarr setup.
5702  if (is_numeric($amount)) {
5703  // We put in temps value of decimal ("0.00001"). Works with 0 and 2.0E-5 and 9999.10
5704  $temps = sprintf("%0.10F", $amount - intval($amount)); // temps=0.0000000000 or 0.0000200000 or 9999.1000000000
5705  $temps = preg_replace('/([\.1-9])0+$/', '\\1', $temps); // temps=0. or 0.00002 or 9999.1
5706  $nbofdec = max(0, dol_strlen($temps) - 2); // -2 to remove "0."
5707  $amount = number_format($amount, $nbofdec, $dec, $thousand);
5708  }
5709  //print "QQ".$amount."<br>\n";
5710 
5711  // Now make replace (the main goal of function)
5712  if ($thousand != ',' && $thousand != '.') {
5713  $amount = str_replace(',', '.', $amount); // To accept 2 notations for french users
5714  }
5715 
5716  $amount = str_replace(' ', '', $amount); // To avoid spaces
5717  $amount = str_replace($thousand, '', $amount); // Replace of thousand before replace of dec to avoid pb if thousand is .
5718  $amount = str_replace($dec, '.', $amount);
5719 
5720  $amount = preg_replace('/[^0-9\-\.]/', '', $amount); // Clean non numeric chars (so it clean some UTF8 spaces for example.
5721  }
5722  //print ' XX'.$amount.' '.$rounding;
5723 
5724  // Now, $amount is a real PHP float number. We make a rounding if required.
5725  if ($rounding) {
5726  $nbofdectoround = '';
5727  if ($rounding == 'MU') {
5728  $nbofdectoround = $conf->global->MAIN_MAX_DECIMALS_UNIT;
5729  } elseif ($rounding == 'MT') {
5730  $nbofdectoround = $conf->global->MAIN_MAX_DECIMALS_TOT;
5731  } elseif ($rounding == 'MS') {
5732  $nbofdectoround = isset($conf->global->MAIN_MAX_DECIMALS_STOCK) ? $conf->global->MAIN_MAX_DECIMALS_STOCK : 5;
5733  } elseif ($rounding == 'CU') {
5734  $nbofdectoround = max($conf->global->MAIN_MAX_DECIMALS_UNIT, 8); // TODO Use param of currency
5735  } elseif ($rounding == 'CT') {
5736  $nbofdectoround = max($conf->global->MAIN_MAX_DECIMALS_TOT, 8); // TODO Use param of currency
5737  } elseif (is_numeric($rounding)) {
5738  $nbofdectoround = (int) $rounding;
5739  }
5740 
5741  //print " RR".$amount.' - '.$nbofdectoround.'<br>';
5742  if (dol_strlen($nbofdectoround)) {
5743  $amount = round(is_string($amount) ? (float) $amount : $amount, $nbofdectoround); // $nbofdectoround can be 0.
5744  } else {
5745  return 'ErrorBadParameterProvidedToFunction';
5746  }
5747  //print ' SS'.$amount.' - '.$nbofdec.' - '.$dec.' - '.$thousand.' - '.$nbofdectoround.'<br>';
5748 
5749  // Convert amount to format with dolibarr dec and thousand (this is because PHP convert a number
5750  // to format defined by LC_NUMERIC after a calculation and we want source format to be defined by Dolibarr setup.
5751  if (is_numeric($amount)) {
5752  // We put in temps value of decimal ("0.00001"). Works with 0 and 2.0E-5 and 9999.10
5753  $temps = sprintf("%0.10F", $amount - intval($amount)); // temps=0.0000000000 or 0.0000200000 or 9999.1000000000
5754  $temps = preg_replace('/([\.1-9])0+$/', '\\1', $temps); // temps=0. or 0.00002 or 9999.1
5755  $nbofdec = max(0, dol_strlen($temps) - 2); // -2 to remove "0."
5756  $amount = number_format($amount, min($nbofdec, $nbofdectoround), $dec, $thousand); // Convert amount to format with dolibarr dec and thousand
5757  }
5758  //print "TT".$amount.'<br>';
5759 
5760  // Always make replace because each math function (like round) replace
5761  // with local values and we want a number that has a SQL string format x.y
5762  if ($thousand != ',' && $thousand != '.') {
5763  $amount = str_replace(',', '.', $amount); // To accept 2 notations for french users
5764  }
5765 
5766  $amount = str_replace(' ', '', $amount); // To avoid spaces
5767  $amount = str_replace($thousand, '', $amount); // Replace of thousand before replace of dec to avoid pb if thousand is .
5768  $amount = str_replace($dec, '.', $amount);
5769 
5770  $amount = preg_replace('/[^0-9\-\.]/', '', $amount); // Clean non numeric chars (so it clean some UTF8 spaces for example.
5771  }
5772 
5773  return $amount;
5774 }
5775 
5788 function showDimensionInBestUnit($dimension, $unit, $type, $outputlangs, $round = -1, $forceunitoutput = 'no', $use_short_label = 0)
5789 {
5790  require_once DOL_DOCUMENT_ROOT.'/core/lib/product.lib.php';
5791 
5792  if (($forceunitoutput == 'no' && $dimension < 1 / 10000 && $unit < 90) || (is_numeric($forceunitoutput) && $forceunitoutput == -6)) {
5793  $dimension = $dimension * 1000000;
5794  $unit = $unit - 6;
5795  } elseif (($forceunitoutput == 'no' && $dimension < 1 / 10 && $unit < 90) || (is_numeric($forceunitoutput) && $forceunitoutput == -3)) {
5796  $dimension = $dimension * 1000;
5797  $unit = $unit - 3;
5798  } elseif (($forceunitoutput == 'no' && $dimension > 100000000 && $unit < 90) || (is_numeric($forceunitoutput) && $forceunitoutput == 6)) {
5799  $dimension = $dimension / 1000000;
5800  $unit = $unit + 6;
5801  } elseif (($forceunitoutput == 'no' && $dimension > 100000 && $unit < 90) || (is_numeric($forceunitoutput) && $forceunitoutput == 3)) {
5802  $dimension = $dimension / 1000;
5803  $unit = $unit + 3;
5804  }
5805  // Special case when we want output unit into pound or ounce
5806  /* TODO
5807  if ($unit < 90 && $type == 'weight' && is_numeric($forceunitoutput) && (($forceunitoutput == 98) || ($forceunitoutput == 99))
5808  {
5809  $dimension = // convert dimension from standard unit into ounce or pound
5810  $unit = $forceunitoutput;
5811  }
5812  if ($unit > 90 && $type == 'weight' && is_numeric($forceunitoutput) && $forceunitoutput < 90)
5813  {
5814  $dimension = // convert dimension from standard unit into ounce or pound
5815  $unit = $forceunitoutput;
5816  }*/
5817 
5818  $ret = price($dimension, 0, $outputlangs, 0, 0, $round);
5819  $ret .= ' '.measuringUnitString(0, $type, $unit, $use_short_label, $outputlangs);
5820 
5821  return $ret;
5822 }
5823 
5824 
5837 function get_localtax($vatrate, $local, $thirdparty_buyer = "", $thirdparty_seller = "", $vatnpr = 0)
5838 {
5839  global $db, $conf, $mysoc;
5840 
5841  if (empty($thirdparty_seller) || !is_object($thirdparty_seller)) {
5842  $thirdparty_seller = $mysoc;
5843  }
5844 
5845  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);
5846 
5847  $vatratecleaned = $vatrate;
5848  $reg = array();
5849  if (preg_match('/^(.*)\s*\((.*)\)$/', $vatrate, $reg)) { // If vat is "xx (yy)"
5850  $vatratecleaned = trim($reg[1]);
5851  $vatratecode = $reg[2];
5852  }
5853 
5854  /*if ($thirdparty_buyer->country_code != $thirdparty_seller->country_code)
5855  {
5856  return 0;
5857  }*/
5858 
5859  // Some test to guess with no need to make database access
5860  if ($mysoc->country_code == 'ES') { // For spain localtaxes 1 and 2, tax is qualified if buyer use local tax
5861  if ($local == 1) {
5862  if (!$mysoc->localtax1_assuj || (string) $vatratecleaned == "0") {
5863  return 0;
5864  }
5865  if ($thirdparty_seller->id == $mysoc->id) {
5866  if (!$thirdparty_buyer->localtax1_assuj) {
5867  return 0;
5868  }
5869  } else {
5870  if (!$thirdparty_seller->localtax1_assuj) {
5871  return 0;
5872  }
5873  }
5874  }
5875 
5876  if ($local == 2) {
5877  //if (! $mysoc->localtax2_assuj || (string) $vatratecleaned == "0") return 0;
5878  if (!$mysoc->localtax2_assuj) {
5879  return 0; // If main vat is 0, IRPF may be different than 0.
5880  }
5881  if ($thirdparty_seller->id == $mysoc->id) {
5882  if (!$thirdparty_buyer->localtax2_assuj) {
5883  return 0;
5884  }
5885  } else {
5886  if (!$thirdparty_seller->localtax2_assuj) {
5887  return 0;
5888  }
5889  }
5890  }
5891  } else {
5892  if ($local == 1 && !$thirdparty_seller->localtax1_assuj) {
5893  return 0;
5894  }
5895  if ($local == 2 && !$thirdparty_seller->localtax2_assuj) {
5896  return 0;
5897  }
5898  }
5899 
5900  // For some country MAIN_GET_LOCALTAXES_VALUES_FROM_THIRDPARTY is forced to on.
5901  if (in_array($mysoc->country_code, array('ES'))) {
5902  $conf->global->MAIN_GET_LOCALTAXES_VALUES_FROM_THIRDPARTY = 1;
5903  }
5904 
5905  // Search local taxes
5906  if (!empty($conf->global->MAIN_GET_LOCALTAXES_VALUES_FROM_THIRDPARTY)) {
5907  if ($local == 1) {
5908  if ($thirdparty_seller != $mysoc) {
5909  if (!isOnlyOneLocalTax($local)) { // TODO We should provide $vatrate to search on correct line and not always on line with highest vat rate
5910  return $thirdparty_seller->localtax1_value;
5911  }
5912  } else { // i am the seller
5913  if (!isOnlyOneLocalTax($local)) { // TODO If seller is me, why not always returning this, even if there is only one locatax vat.
5914  return $conf->global->MAIN_INFO_VALUE_LOCALTAX1;
5915  }
5916  }
5917  }
5918  if ($local == 2) {
5919  if ($thirdparty_seller != $mysoc) {
5920  if (!isOnlyOneLocalTax($local)) { // TODO We should provide $vatrate to search on correct line and not always on line with highest vat rate
5921  // TODO We should also return value defined on thirdparty only if defined
5922  return $thirdparty_seller->localtax2_value;
5923  }
5924  } else { // i am the seller
5925  if (in_array($mysoc->country_code, array('ES'))) {
5926  return $thirdparty_buyer->localtax2_value;
5927  } else {
5928  return $conf->global->MAIN_INFO_VALUE_LOCALTAX2;
5929  }
5930  }
5931  }
5932  }
5933 
5934  // By default, search value of local tax on line of common tax
5935  $sql = "SELECT t.localtax1, t.localtax2, t.localtax1_type, t.localtax2_type";
5936  $sql .= " FROM ".MAIN_DB_PREFIX."c_tva as t, ".MAIN_DB_PREFIX."c_country as c";
5937  $sql .= " WHERE t.fk_pays = c.rowid AND c.code = '".$db->escape($thirdparty_seller->country_code)."'";
5938  $sql .= " AND t.taux = ".((float) $vatratecleaned)." AND t.active = 1";
5939  if (!empty($vatratecode)) {
5940  $sql .= " AND t.code ='".$db->escape($vatratecode)."'"; // If we have the code, we use it in priority
5941  } else {
5942  $sql .= " AND t.recuperableonly = '".$db->escape($vatnpr)."'";
5943  }
5944 
5945  $resql = $db->query($sql);
5946 
5947  if ($resql) {
5948  $obj = $db->fetch_object($resql);
5949  if ($obj) {
5950  if ($local == 1) {
5951  return $obj->localtax1;
5952  } elseif ($local == 2) {
5953  return $obj->localtax2;
5954  }
5955  }
5956  }
5957 
5958  return 0;
5959 }
5960 
5961 
5970 function isOnlyOneLocalTax($local)
5971 {
5972  $tax = get_localtax_by_third($local);
5973 
5974  $valors = explode(":", $tax);
5975 
5976  if (count($valors) > 1) {
5977  return false;
5978  } else {
5979  return true;
5980  }
5981 }
5982 
5989 function get_localtax_by_third($local)
5990 {
5991  global $db, $mysoc;
5992 
5993  $sql = " SELECT t.localtax".$local." as localtax";
5994  $sql .= " FROM ".MAIN_DB_PREFIX."c_tva as t INNER JOIN ".MAIN_DB_PREFIX."c_country as c ON c.rowid = t.fk_pays";
5995  $sql .= " WHERE c.code = '".$db->escape($mysoc->country_code)."' AND t.active = 1 AND t.taux = (";
5996  $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";
5997  $sql .= " WHERE c.code = '".$db->escape($mysoc->country_code)."' AND tt.active = 1)";
5998  $sql .= " AND t.localtax".$local."_type <> '0'";
5999  $sql .= " ORDER BY t.rowid DESC";
6000 
6001  $resql = $db->query($sql);
6002  if ($resql) {
6003  $obj = $db->fetch_object($resql);
6004  return $obj->localtax;
6005  } else {
6006  return 'Error';
6007  }
6008 
6009  return '0';
6010 }
6011 
6012 
6024 function getTaxesFromId($vatrate, $buyer = null, $seller = null, $firstparamisid = 1)
6025 {
6026  global $db, $mysoc;
6027 
6028  dol_syslog("getTaxesFromId vat id or rate = ".$vatrate);
6029 
6030  // Search local taxes
6031  $sql = "SELECT t.rowid, t.code, t.taux as rate, t.recuperableonly as npr, t.accountancy_code_sell, t.accountancy_code_buy,";
6032  $sql .= " t.localtax1, t.localtax1_type, t.localtax2, t.localtax2_type";
6033  $sql .= " FROM ".MAIN_DB_PREFIX."c_tva as t";
6034  if ($firstparamisid) {
6035  $sql .= " WHERE t.rowid = ".(int) $vatrate;
6036  } else {
6037  $vatratecleaned = $vatrate;
6038  $vatratecode = '';
6039  $reg = array();
6040  if (preg_match('/^(.*)\s*\((.*)\)$/', $vatrate, $reg)) { // If vat is "xx (yy)"
6041  $vatratecleaned = $reg[1];
6042  $vatratecode = $reg[2];
6043  }
6044 
6045  $sql .= ", ".MAIN_DB_PREFIX."c_country as c";
6046  /*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 ??
6047  else $sql.= " WHERE t.fk_pays = c.rowid AND c.code = '".$db->escape($seller->country_code)."'";*/
6048  $sql .= " WHERE t.fk_pays = c.rowid AND c.code = '".$db->escape($seller->country_code)."'";
6049  $sql .= " AND t.taux = ".((float) $vatratecleaned)." AND t.active = 1";
6050  if ($vatratecode) {
6051  $sql .= " AND t.code = '".$db->escape($vatratecode)."'";
6052  }
6053  }
6054 
6055  $resql = $db->query($sql);
6056  if ($resql) {
6057  $obj = $db->fetch_object($resql);
6058  if ($obj) {
6059  return array(
6060  'rowid'=>$obj->rowid,
6061  'code'=>$obj->code,
6062  'rate'=>$obj->rate,
6063  'localtax1'=>$obj->localtax1,
6064  'localtax1_type'=>$obj->localtax1_type,
6065  'localtax2'=>$obj->localtax2,
6066  'localtax2_type'=>$obj->localtax2_type,
6067  'npr'=>$obj->npr,
6068  'accountancy_code_sell'=>$obj->accountancy_code_sell,
6069  'accountancy_code_buy'=>$obj->accountancy_code_buy
6070  );
6071  } else {
6072  return array();
6073  }
6074  } else {
6075  dol_print_error($db);
6076  }
6077 
6078  return array();
6079 }
6080 
6097 function getLocalTaxesFromRate($vatrate, $local, $buyer, $seller, $firstparamisid = 0)
6098 {
6099  global $db, $mysoc;
6100 
6101  dol_syslog("getLocalTaxesFromRate vatrate=".$vatrate." local=".$local);
6102 
6103  // Search local taxes
6104  $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";
6105  $sql .= " FROM ".MAIN_DB_PREFIX."c_tva as t";
6106  if ($firstparamisid) {
6107  $sql .= " WHERE t.rowid = ".(int) $vatrate;
6108  } else {
6109  $vatratecleaned = $vatrate;
6110  $vatratecode = '';
6111  $reg = array();
6112  if (preg_match('/^(.*)\s*\((.*)\)$/', $vatrate, $reg)) { // If vat is "x.x (yy)"
6113  $vatratecleaned = $reg[1];
6114  $vatratecode = $reg[2];
6115  }
6116 
6117  $sql .= ", ".MAIN_DB_PREFIX."c_country as c";
6118  if (!empty($mysoc) && $mysoc->country_code == 'ES') {
6119  $sql .= " WHERE t.fk_pays = c.rowid AND c.code = '".$db->escape($buyer->country_code)."'"; // local tax in spain use the buyer country ??
6120  } else {
6121  $sql .= " WHERE t.fk_pays = c.rowid AND c.code = '".$db->escape(empty($seller->country_code) ? $mysoc->country_code : $seller->country_code)."'";
6122  }
6123  $sql .= " AND t.taux = ".((float) $vatratecleaned)." AND t.active = 1";
6124  if ($vatratecode) {
6125  $sql .= " AND t.code = '".$db->escape($vatratecode)."'";
6126  }
6127  }
6128 
6129  $resql = $db->query($sql);
6130  if ($resql) {
6131  $obj = $db->fetch_object($resql);
6132 
6133  if ($obj) {
6134  $vateratestring = $obj->rate.($obj->code ? ' ('.$obj->code.')' : '');
6135 
6136  if ($local == 1) {
6137  return array($obj->localtax1_type, get_localtax($vateratestring, $local, $buyer, $seller), $obj->accountancy_code_sell, $obj->accountancy_code_buy);
6138  } elseif ($local == 2) {
6139  return array($obj->localtax2_type, get_localtax($vateratestring, $local, $buyer, $seller), $obj->accountancy_code_sell, $obj->accountancy_code_buy);
6140  } else {
6141  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);
6142  }
6143  }
6144  }
6145 
6146  return array();
6147 }
6148 
6159 function get_product_vat_for_country($idprod, $thirdpartytouse, $idprodfournprice = 0)
6160 {
6161  global $db, $conf, $mysoc;
6162 
6163  require_once DOL_DOCUMENT_ROOT.'/product/class/product.class.php';
6164 
6165  $ret = 0;
6166  $found = 0;
6167 
6168  if ($idprod > 0) {
6169  // Load product
6170  $product = new Product($db);
6171  $result = $product->fetch($idprod);
6172 
6173  if ($mysoc->country_code == $thirdpartytouse->country_code) { // If country to consider is ours
6174  if ($idprodfournprice > 0) { // We want vat for product for a "supplier" object
6175  $product->get_buyprice($idprodfournprice, 0, 0, 0);
6176  $ret = $product->vatrate_supplier;
6177  if ($product->default_vat_code) {
6178  $ret .= ' ('.$product->default_vat_code.')';
6179  }
6180  } else {
6181  $ret = $product->tva_tx; // Default vat of product we defined
6182  if ($product->default_vat_code) {
6183  $ret .= ' ('.$product->default_vat_code.')';
6184  }
6185  }
6186  $found = 1;
6187  } else {
6188  // TODO Read default product vat according to product and another countrycode.
6189  // Vat for couple anothercountrycode/product is data that is not managed and store yet, so we will fallback on next rule.
6190  }
6191  }
6192 
6193  if (!$found) {
6194  if (empty($conf->global->MAIN_VAT_DEFAULT_IF_AUTODETECT_FAILS)) {
6195  // If vat of product for the country not found or not defined, we return the first higher vat of country.
6196  $sql = "SELECT t.taux as vat_rate, t.code as default_vat_code";
6197  $sql .= " FROM ".MAIN_DB_PREFIX."c_tva as t, ".MAIN_DB_PREFIX."c_country as c";
6198  $sql .= " WHERE t.active=1 AND t.fk_pays = c.rowid AND c.code='".$db->escape($thirdpartytouse->country_code)."'";
6199  $sql .= " ORDER BY t.taux DESC, t.code ASC, t.recuperableonly ASC";
6200  $sql .= $db->plimit(1);
6201 
6202  $resql = $db->query($sql);
6203  if ($resql) {
6204  $obj = $db->fetch_object($resql);
6205  if ($obj) {
6206  $ret = $obj->vat_rate;
6207  if ($obj->default_vat_code) {
6208  $ret .= ' ('.$obj->default_vat_code.')';
6209  }
6210  }
6211  $db->free($resql);
6212  } else {
6213  dol_print_error($db);
6214  }
6215  } else {
6216  $ret = $conf->global->MAIN_VAT_DEFAULT_IF_AUTODETECT_FAILS; // Forced value if autodetect fails
6217  }
6218  }
6219 
6220  dol_syslog("get_product_vat_for_country: ret=".$ret);
6221  return $ret;
6222 }
6223 
6233 function get_product_localtax_for_country($idprod, $local, $thirdpartytouse)
6234 {
6235  global $db, $mysoc;
6236 
6237  if (!class_exists('Product')) {
6238  require_once DOL_DOCUMENT_ROOT.'/product/class/product.class.php';
6239  }
6240 
6241  $ret = 0;
6242  $found = 0;
6243 
6244  if ($idprod > 0) {
6245  // Load product
6246  $product = new Product($db);
6247  $result = $product->fetch($idprod);
6248 
6249  if ($mysoc->country_code == $thirdpartytouse->country_code) { // If selling country is ours
6250  /* Not defined yet, so we don't use this
6251  if ($local==1) $ret=$product->localtax1_tx;
6252  elseif ($local==2) $ret=$product->localtax2_tx;
6253  $found=1;
6254  */
6255  } else {
6256  // TODO Read default product vat according to product and another countrycode.
6257  // Vat for couple anothercountrycode/product is data that is not managed and store yet, so we will fallback on next rule.
6258  }
6259  }
6260 
6261  if (!$found) {
6262  // If vat of product for the country not found or not defined, we return higher vat of country.
6263  $sql = "SELECT taux as vat_rate, localtax1, localtax2";
6264  $sql .= " FROM ".MAIN_DB_PREFIX."c_tva as t, ".MAIN_DB_PREFIX."c_country as c";
6265  $sql .= " WHERE t.active=1 AND t.fk_pays = c.rowid AND c.code='".$db->escape($thirdpartytouse->country_code)."'";
6266  $sql .= " ORDER BY t.taux DESC, t.recuperableonly ASC";
6267  $sql .= $db->plimit(1);
6268 
6269  $resql = $db->query($sql);
6270  if ($resql) {
6271  $obj = $db->fetch_object($resql);
6272  if ($obj) {
6273  if ($local == 1) {
6274  $ret = $obj->localtax1;
6275  } elseif ($local == 2) {
6276  $ret = $obj->localtax2;
6277  }
6278  }
6279  } else {
6280  dol_print_error($db);
6281  }
6282  }
6283 
6284  dol_syslog("get_product_localtax_for_country: ret=".$ret);
6285  return $ret;
6286 }
6287 
6304 function get_default_tva(Societe $thirdparty_seller, Societe $thirdparty_buyer, $idprod = 0, $idprodfournprice = 0)
6305 {
6306  global $conf;
6307 
6308  require_once DOL_DOCUMENT_ROOT.'/core/lib/company.lib.php';
6309 
6310  // Note: possible values for tva_assuj are 0/1 or franchise/reel
6311  $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;
6312 
6313  $seller_country_code = $thirdparty_seller->country_code;
6314  $seller_in_cee = isInEEC($thirdparty_seller);
6315 
6316  $buyer_country_code = $thirdparty_buyer->country_code;
6317  $buyer_in_cee = isInEEC($thirdparty_buyer);
6318 
6319  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 : ''));
6320 
6321  // 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)
6322  // we use the buyer VAT.
6323  if (!empty($conf->global->SERVICE_ARE_ECOMMERCE_200238EC)) {
6324  if ($seller_in_cee && $buyer_in_cee) {
6325  $isacompany = $thirdparty_buyer->isACompany();
6326  if ($isacompany && !empty($conf->global->MAIN_USE_VAT_COMPANIES_IN_EEC_WITH_INVALID_VAT_ID_ARE_INDIVIDUAL)) {
6327  require_once DOL_DOCUMENT_ROOT.'/core/lib/functions2.lib.php';
6328  if (!isValidVATID($thirdparty_buyer)) {
6329  $isacompany = 0;
6330  }
6331  }
6332 
6333  if (!$isacompany) {
6334  //print 'VATRULE 0';
6335  return get_product_vat_for_country($idprod, $thirdparty_buyer, $idprodfournprice);
6336  }
6337  }
6338  }
6339 
6340  // If seller does not use VAT
6341  if (!$seller_use_vat) {
6342  //print 'VATRULE 1';
6343  return 0;
6344  }
6345 
6346  // Le test ci-dessus ne devrait pas etre necessaire. Me signaler l'exemple du cas juridique concerne si le test suivant n'est pas suffisant.
6347 
6348  // Si le (pays vendeur = pays acheteur) alors la TVA par defaut=TVA du produit vendu. Fin de regle.
6349  if (($seller_country_code == $buyer_country_code)
6350  || (in_array($seller_country_code, array('FR', 'MC')) && in_array($buyer_country_code, array('FR', 'MC')))) { // Warning ->country_code not always defined
6351  //print 'VATRULE 2';
6352  return get_product_vat_for_country($idprod, $thirdparty_seller, $idprodfournprice);
6353  }
6354 
6355  // Si (vendeur et acheteur dans Communaute europeenne) et (bien vendu = moyen de transports neuf comme auto, bateau, avion) alors TVA par defaut=0 (La TVA doit etre paye par l'acheteur au centre d'impots de son pays et non au vendeur). Fin de regle.
6356  // 'VATRULE 3' - Not supported
6357 
6358  // Si (vendeur et acheteur dans Communaute europeenne) et (acheteur = entreprise) alors TVA par defaut=0. Fin de regle
6359  // Si (vendeur et acheteur dans Communaute europeenne) et (acheteur = particulier) alors TVA par defaut=TVA du produit vendu. Fin de regle
6360  if (($seller_in_cee && $buyer_in_cee)) {
6361  $isacompany = $thirdparty_buyer->isACompany();
6362  if ($isacompany && !empty($conf->global->MAIN_USE_VAT_COMPANIES_IN_EEC_WITH_INVALID_VAT_ID_ARE_INDIVIDUAL)) {
6363  require_once DOL_DOCUMENT_ROOT.'/core/lib/functions2.lib.php';
6364  if (!isValidVATID($thirdparty_buyer)) {
6365  $isacompany = 0;
6366  }
6367  }
6368 
6369  if (!$isacompany) {
6370  //print 'VATRULE 4';
6371  return get_product_vat_for_country($idprod, $thirdparty_seller, $idprodfournprice);
6372  } else {
6373  //print 'VATRULE 5';
6374  return 0;
6375  }
6376  }
6377 
6378  // Si (vendeur dans Communaute europeene et acheteur hors Communaute europeenne et acheteur particulier) alors TVA par defaut=TVA du produit vendu. Fin de regle
6379  // I don't see any use case that need this rule.
6380  if (!empty($conf->global->MAIN_USE_VAT_OF_PRODUCT_FOR_INDIVIDUAL_CUSTOMER_OUT_OF_EEC) && empty($buyer_in_cee)) {
6381  $isacompany = $thirdparty_buyer->isACompany();
6382  if (!$isacompany) {
6383  return get_product_vat_for_country($idprod, $thirdparty_seller, $idprodfournprice);
6384  //print 'VATRULE extra';
6385  }
6386  }
6387 
6388  // Sinon la TVA proposee par defaut=0. Fin de regle.
6389  // Rem: Cela signifie qu'au moins un des 2 est hors Communaute europeenne et que le pays differe
6390  //print 'VATRULE 6';
6391  return 0;
6392 }
6393 
6394 
6405 function get_default_npr(Societe $thirdparty_seller, Societe $thirdparty_buyer, $idprod = 0, $idprodfournprice = 0)
6406 {
6407  global $db;
6408 
6409  if ($idprodfournprice > 0) {
6410  if (!class_exists('ProductFournisseur')) {
6411  require_once DOL_DOCUMENT_ROOT.'/fourn/class/fournisseur.product.class.php';
6412  }
6413  $prodprice = new ProductFournisseur($db);
6414  $prodprice->fetch_product_fournisseur_price($idprodfournprice);
6415  return $prodprice->fourn_tva_npr;
6416  } elseif ($idprod > 0) {
6417  if (!class_exists('Product')) {
6418  require_once DOL_DOCUMENT_ROOT.'/product/class/product.class.php';
6419  }
6420  $prod = new Product($db);
6421  $prod->fetch($idprod);
6422  return $prod->tva_npr;
6423  }
6424 
6425  return 0;
6426 }
6427 
6441 function get_default_localtax($thirdparty_seller, $thirdparty_buyer, $local, $idprod = 0)
6442 {
6443  global $mysoc;
6444 
6445  if (!is_object($thirdparty_seller)) {
6446  return -1;
6447  }
6448  if (!is_object($thirdparty_buyer)) {
6449  return -1;
6450  }
6451 
6452  if ($local == 1) { // Localtax 1
6453  if ($mysoc->country_code == 'ES') {
6454  if (is_numeric($thirdparty_buyer->localtax1_assuj) && !$thirdparty_buyer->localtax1_assuj) {
6455  return 0;
6456  }
6457  } else {
6458  // Si vendeur non assujeti a Localtax1, localtax1 par default=0
6459  if (is_numeric($thirdparty_seller->localtax1_assuj) && !$thirdparty_seller->localtax1_assuj) {
6460  return 0;
6461  }
6462  if (!is_numeric($thirdparty_seller->localtax1_assuj) && $thirdparty_seller->localtax1_assuj == 'localtax1off') {
6463  return 0;
6464  }
6465  }
6466  } elseif ($local == 2) { //I Localtax 2
6467  // Si vendeur non assujeti a Localtax2, localtax2 par default=0
6468  if (is_numeric($thirdparty_seller->localtax2_assuj) && !$thirdparty_seller->localtax2_assuj) {
6469  return 0;
6470  }
6471  if (!is_numeric($thirdparty_seller->localtax2_assuj) && $thirdparty_seller->localtax2_assuj == 'localtax2off') {
6472  return 0;
6473  }
6474  }
6475 
6476  if ($thirdparty_seller->country_code == $thirdparty_buyer->country_code) {
6477  return get_product_localtax_for_country($idprod, $local, $thirdparty_seller);
6478  }
6479 
6480  return 0;
6481 }
6482 
6491 function yn($yesno, $case = 1, $color = 0)
6492 {
6493  global $langs;
6494 
6495  $result = 'unknown';
6496  $classname = '';
6497  if ($yesno == 1 || strtolower($yesno) == 'yes' || strtolower($yesno) == 'true') { // A mettre avant test sur no a cause du == 0
6498  $result = $langs->trans('yes');
6499  if ($case == 1 || $case == 3) {
6500  $result = $langs->trans("Yes");
6501  }
6502  if ($case == 2) {
6503  $result = '<input type="checkbox" value="1" checked disabled>';
6504  }
6505  if ($case == 3) {
6506  $result = '<input type="checkbox" value="1" checked disabled> '.$result;
6507  }
6508 
6509  $classname = 'ok';
6510  } elseif ($yesno == 0 || strtolower($yesno) == 'no' || strtolower($yesno) == 'false') {
6511  $result = $langs->trans("no");
6512  if ($case == 1 || $case == 3) {
6513  $result = $langs->trans("No");
6514  }
6515  if ($case == 2) {
6516  $result = '<input type="checkbox" value="0" disabled>';
6517  }
6518  if ($case == 3) {
6519  $result = '<input type="checkbox" value="0" disabled> '.$result;
6520  }
6521 
6522  if ($color == 2) {
6523  $classname = 'ok';
6524  } else {
6525  $classname = 'error';
6526  }
6527  }
6528  if ($color) {
6529  return '<span class="'.$classname.'">'.$result.'</span>';
6530  }
6531  return $result;
6532 }
6533 
6549 function get_exdir($num, $level, $alpha, $withoutslash, $object, $modulepart = '')
6550 {
6551  global $conf;
6552 
6553  if (empty($modulepart) && !empty($object->module)) {
6554  $modulepart = $object->module;
6555  }
6556 
6557  $path = '';
6558 
6559  $arrayforoldpath = array('cheque', 'category', 'holiday', 'supplier_invoice', 'invoice_supplier', 'mailing', 'supplier_payment');
6560  if (!empty($conf->global->PRODUCT_USE_OLD_PATH_FOR_PHOTO)) {
6561  $arrayforoldpath[] = 'product';
6562  }
6563  if (!empty($level) && in_array($modulepart, $arrayforoldpath)) {
6564  // This part should be removed once all code is using "get_exdir" to forge path, with parameter $object and $modulepart provided.
6565  if (empty($alpha)) {
6566  $num = preg_replace('/([^0-9])/i', '', $num);
6567  } else {
6568  $num = preg_replace('/^.*\-/i', '', $num);
6569  }
6570  $num = substr("000".$num, -$level);
6571  if ($level == 1) {
6572  $path = substr($num, 0, 1);
6573  }
6574  if ($level == 2) {
6575  $path = substr($num, 1, 1).'/'.substr($num, 0, 1);
6576  }
6577  if ($level == 3) {
6578  $path = substr($num, 2, 1).'/'.substr($num, 1, 1).'/'.substr($num, 0, 1);
6579  }
6580  } else {
6581  // We will enhance here a common way of forging path for document storage.
6582  // In a future, we may distribute directories on several levels depending on setup and object.
6583  // Here, $object->id, $object->ref and $modulepart are required.
6584  //var_dump($modulepart);
6585  $path = dol_sanitizeFileName(empty($object->ref) ? (string) $object->id : $object->ref);
6586  }
6587 
6588  if (empty($withoutslash) && !empty($path)) {
6589  $path .= '/';
6590  }
6591 
6592  return $path;
6593 }
6594 
6603 function dol_mkdir($dir, $dataroot = '', $newmask = '')
6604 {
6605  global $conf;
6606 
6607  dol_syslog("functions.lib::dol_mkdir: dir=".$dir, LOG_INFO);
6608 
6609  $dir_osencoded = dol_osencode($dir);
6610  if (@is_dir($dir_osencoded)) {
6611  return 0;
6612  }
6613 
6614  $nberr = 0;
6615  $nbcreated = 0;
6616 
6617  $ccdir = '';
6618  if (!empty($dataroot)) {
6619  // Remove data root from loop
6620  $dir = str_replace($dataroot.'/', '', $dir);
6621  $ccdir = $dataroot.'/';
6622  }
6623 
6624  $cdir = explode("/", $dir);
6625  $num = count($cdir);
6626  for ($i = 0; $i < $num; $i++) {
6627  if ($i > 0) {
6628  $ccdir .= '/'.$cdir[$i];
6629  } else {
6630  $ccdir .= $cdir[$i];
6631  }
6632  if (preg_match("/^.:$/", $ccdir, $regs)) {
6633  continue; // Si chemin Windows incomplet, on poursuit par rep suivant
6634  }
6635 
6636  // Attention, le is_dir() peut echouer bien que le rep existe.
6637  // (ex selon config de open_basedir)
6638  if ($ccdir) {
6639  $ccdir_osencoded = dol_osencode($ccdir);
6640  if (!@is_dir($ccdir_osencoded)) {
6641  dol_syslog("functions.lib::dol_mkdir: Directory '".$ccdir."' does not exists or is outside open_basedir PHP setting.", LOG_DEBUG);
6642 
6643  umask(0);
6644  $dirmaskdec = octdec((string) $newmask);
6645  if (empty($newmask)) {
6646  $dirmaskdec = empty($conf->global->MAIN_UMASK) ? octdec('0755') : octdec($conf->global->MAIN_UMASK);
6647  }
6648  $dirmaskdec |= octdec('0111'); // Set x bit required for directories
6649  if (!@mkdir($ccdir_osencoded, $dirmaskdec)) {
6650  // Si le is_dir a renvoye une fausse info, alors on passe ici.
6651  dol_syslog("functions.lib::dol_mkdir: Fails to create directory '".$ccdir."' or directory already exists.", LOG_WARNING);
6652  $nberr++;
6653  } else {
6654  dol_syslog("functions.lib::dol_mkdir: Directory '".$ccdir."' created", LOG_DEBUG);
6655  $nberr = 0; // On remet a zero car si on arrive ici, cela veut dire que les echecs precedents peuvent etre ignore
6656  $nbcreated++;
6657  }
6658  } else {
6659  $nberr = 0; // On remet a zero car si on arrive ici, cela veut dire que les echecs precedents peuvent etre ignores
6660  }
6661  }
6662  }
6663  return ($nberr ? -$nberr : $nbcreated);
6664 }
6665 
6666 
6672 function picto_required()
6673 {
6674  return '<span class="fieldrequired">*</span>';
6675 }
6676 
6677 
6694 function dol_string_nohtmltag($stringtoclean, $removelinefeed = 1, $pagecodeto = 'UTF-8', $strip_tags = 0, $removedoublespaces = 1)
6695 {
6696  if ($removelinefeed == 2) {
6697  $stringtoclean = preg_replace('/<br[^>]*>(\n|\r)+/ims', '<br>', $stringtoclean);
6698  }
6699  $temp = preg_replace('/<br[^>]*>/i', "\n", $stringtoclean);
6700 
6701  // 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)
6702  $temp = dol_html_entity_decode($temp, ENT_COMPAT | ENT_HTML5, $pagecodeto);
6703 
6704  $temp = str_replace('< ', '__ltspace__', $temp);
6705 
6706  if ($strip_tags) {
6707  $temp = strip_tags($temp);
6708  } else {
6709  // 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).
6710  $pattern = "/<[^<>]+>/";
6711  // Example of $temp: <a href="/myurl" title="<u>A title</u>">0000-021</a>
6712  // pass 1 - $temp after pass 1: <a href="/myurl" title="A title">0000-021
6713  // pass 2 - $temp after pass 2: 0000-021
6714  $tempbis = $temp;
6715  do {
6716  $temp = $tempbis;
6717  $tempbis = str_replace('<>', '', $temp); // No reason to have this into a text, except if value is to try bypass the next html cleaning
6718  $tempbis = preg_replace($pattern, '', $tempbis);
6719  //$idowhile++; print $temp.'-'.$tempbis."\n"; if ($idowhile > 100) break;
6720  } while ($tempbis != $temp);
6721 
6722  $temp = $tempbis;
6723 
6724  // Remove '<' into remaining, so remove non closing html tags like '<abc' or '<<abc'. Note: '<123abc' is not a html tag (can be kept), but '<abc123' is (must be removed).
6725  $temp = preg_replace('/<+([a-z]+)/i', '\1', $temp);
6726  }
6727 
6728  $temp = dol_html_entity_decode($temp, ENT_COMPAT, $pagecodeto);
6729 
6730  // Remove also carriage returns
6731  if ($removelinefeed == 1) {
6732  $temp = str_replace(array("\r\n", "\r", "\n"), " ", $temp);
6733  }
6734 
6735  // And double quotes
6736  if ($removedoublespaces) {
6737  while (strpos($temp, " ")) {
6738  $temp = str_replace(" ", " ", $temp);
6739  }
6740  }
6741 
6742  $temp = str_replace('__ltspace__', '< ', $temp);
6743 
6744  return trim($temp);
6745 }
6746 
6760 function dol_string_onlythesehtmltags($stringtoclean, $cleanalsosomestyles = 1, $removeclassattribute = 1, $cleanalsojavascript = 0, $allowiframe = 0)
6761 {
6762  $allowed_tags = array(
6763  "html", "head", "meta", "body", "article", "a", "abbr", "b", "blockquote", "br", "cite", "div", "dl", "dd", "dt", "em", "font", "img", "ins", "hr", "i", "li", "link",
6764  "ol", "p", "q", "s", "section", "span", "strike", "strong", "title", "table", "tr", "th", "td", "u", "ul", "sup", "sub", "blockquote", "pre", "h1", "h2", "h3", "h4", "h5", "h6",
6765  "comment" // this tags is added to manage comment <!--...--> that are replaced into <comment>...</comment>
6766  );
6767  if ($allowiframe) {
6768  $allowed_tags[] = "iframe";
6769  }
6770 
6771  $allowed_tags_string = join("><", $allowed_tags);
6772  $allowed_tags_string = '<'.$allowed_tags_string.'>';
6773 
6774  $stringtoclean = str_replace('<!DOCTYPE html>', '__!DOCTYPE_HTML__', $stringtoclean); // Replace DOCTYPE to avoid to have it removed by the strip_tags
6775 
6776  $stringtoclean = dol_string_nounprintableascii($stringtoclean, 0);
6777 
6778  //$stringtoclean = preg_replace('/<!--[^>]*-->/', '', $stringtoclean);
6779  $stringtoclean = preg_replace('/<!--([^>]*)-->/', '<comment>\1</comment>', $stringtoclean);
6780 
6781  $stringtoclean = preg_replace('/&colon;/i', ':', $stringtoclean);
6782  $stringtoclean = preg_replace('/&#58;|&#0+58|&#x3A/i', '', $stringtoclean); // refused string ':' encoded (no reason to have a : encoded like this) to disable 'javascript:...'
6783  $stringtoclean = preg_replace('/javascript\s*:/i', '', $stringtoclean);
6784 
6785  $temp = strip_tags($stringtoclean, $allowed_tags_string); // Warning: This remove also undesired </> changing string obfuscated with </> that pass injection detection into harmfull string
6786 
6787  if ($cleanalsosomestyles) { // Clean for remaining html tags
6788  $temp = preg_replace('/position\s*:\s*(absolute|fixed)\s*!\s*important/i', '', $temp); // Note: If hacker try to introduce css comment into string to bypass this regex, the string must also be encoded by the dol_htmlentitiesbr during output so it become harmless
6789  }
6790  if ($removeclassattribute) { // Clean for remaining html tags
6791  $temp = preg_replace('/(<[^>]+)\s+class=((["\']).*?\\3|\\w*)/i', '\\1', $temp);
6792  }
6793 
6794  // Remove 'javascript:' that we should not find into a text with
6795  // Warning: This is not reliable to fight against obfuscated javascript, there is a lot of other solution to include js into a common html tag (only filtered by a GETPOST(.., powerfullfilter)).
6796  if ($cleanalsojavascript) {
6797  $temp = preg_replace('/javascript\s*:/i', '', $temp);
6798  }
6799 
6800  $temp = str_replace('__!DOCTYPE_HTML__', '<!DOCTYPE html>', $temp); // Restore the DOCTYPE
6801 
6802  $temp = preg_replace('/<comment>([^>]*)<\/comment>/', '<!--\1-->', $temp); // Restore html comments
6803 
6804 
6805  return $temp;
6806 }
6807 
6808 
6820 function dol_string_onlythesehtmlattributes($stringtoclean, $allowed_attributes = array("allow", "allowfullscreen", "alt", "class", "contenteditable", "data-html", "frameborder", "height", "href", "id", "name", "src", "style", "target", "title", "width"))
6821 {
6822  if (class_exists('DOMDocument') && !empty($stringtoclean)) {
6823  $stringtoclean = '<?xml encoding="UTF-8"><html><body>'.$stringtoclean.'</body></html>';
6824 
6825  $dom = new DOMDocument(null, 'UTF-8');
6826  $dom->loadHTML($stringtoclean, LIBXML_ERR_NONE|LIBXML_HTML_NOIMPLIED|LIBXML_HTML_NODEFDTD|LIBXML_NONET|LIBXML_NOWARNING|LIBXML_NOXMLDECL);
6827 
6828  if (is_object($dom)) {
6829  for ($els = $dom->getElementsByTagname('*'), $i = $els->length - 1; $i >= 0; $i--) {
6830  for ($attrs = $els->item($i)->attributes, $ii = $attrs->length - 1; $ii >= 0; $ii--) {
6831  //var_dump($attrs->item($ii));
6832  if (! empty($attrs->item($ii)->name)) {
6833  // Delete attribute if not into allowed_attributes
6834  if (! in_array($attrs->item($ii)->name, $allowed_attributes)) {
6835  $els->item($i)->removeAttribute($attrs->item($ii)->name);
6836  } elseif (in_array($attrs->item($ii)->name, array('style'))) {
6837  $valuetoclean = $attrs->item($ii)->value;
6838 
6839  if (isset($valuetoclean)) {
6840  do {
6841  $oldvaluetoclean = $valuetoclean;
6842  $valuetoclean = preg_replace('/\/\*.*\*\//m', '', $valuetoclean); // clean css comments
6843  $valuetoclean = preg_replace('/position\s*:\s*[a-z]+/mi', '', $valuetoclean);
6844  if ($els->item($i)->tagName == 'a') { // more paranoiac cleaning for clickable tags.
6845  $valuetoclean = preg_replace('/display\s*://m', '', $valuetoclean);
6846  $valuetoclean = preg_replace('/z-index\s*://m', '', $valuetoclean);
6847  $valuetoclean = preg_replace('/\s+(top|left|right|bottom)\s*://m', '', $valuetoclean);
6848  }
6849  } while ($oldvaluetoclean != $valuetoclean);
6850  }
6851 
6852  $attrs->item($ii)->value = $valuetoclean;
6853  }
6854  }
6855  }
6856  }
6857  }
6858 
6859  $return = $dom->saveHTML();
6860  //$return = '<html><body>aaaa</p>bb<p>ssdd</p>'."\n<p>aaa</p>aa<p>bb</p>";
6861 
6862  $return = preg_replace('/^'.preg_quote('<?xml encoding="UTF-8">', '/').'/', '', $return);
6863  $return = preg_replace('/^'.preg_quote('<html><body>', '/').'/', '', $return);
6864  $return = preg_replace('/'.preg_quote('</body></html>', '/').'$/', '', $return);
6865  return trim($return);
6866  } else {
6867  return $stringtoclean;
6868  }
6869 }
6870 
6882 function dol_string_neverthesehtmltags($stringtoclean, $disallowed_tags = array('textarea'), $cleanalsosomestyles = 0)
6883 {
6884  $temp = $stringtoclean;
6885  foreach ($disallowed_tags as $tagtoremove) {
6886  $temp = preg_replace('/<\/?'.$tagtoremove.'>/', '', $temp);
6887  $temp = preg_replace('/<\/?'.$tagtoremove.'\s+[^>]*>/', '', $temp);
6888  }
6889 
6890  if ($cleanalsosomestyles) {
6891  $temp = preg_replace('/position\s*:\s*(absolute|fixed)\s*!\s*important/', '', $temp); // Note: If hacker try to introduce css comment into string to avoid this, string should be encoded by the dol_htmlentitiesbr so be harmless
6892  }
6893 
6894  return $temp;
6895 }
6896 
6897 
6907 function dolGetFirstLineOfText($text, $nboflines = 1, $charset = 'UTF-8')
6908 {
6909  if ($nboflines == 1) {
6910  if (dol_textishtml($text)) {
6911  $firstline = preg_replace('/<br[^>]*>.*$/s', '', $text); // The s pattern modifier means the . can match newline characters
6912  $firstline = preg_replace('/<div[^>]*>.*$/s', '', $firstline); // The s pattern modifier means the . can match newline characters
6913  } else {
6914  $firstline = preg_replace('/[\n\r].*/', '', $text);
6915  }
6916  return $firstline.((strlen($firstline) != strlen($text)) ? '...' : '');
6917  } else {
6918  $ishtml = 0;
6919  if (dol_textishtml($text)) {
6920  $text = preg_replace('/\n/', '', $text);
6921  $ishtml = 1;
6922  $repTable = array("\t" => " ", "\n" => " ", "\r" => " ", "\0" => " ", "\x0B" => " ");
6923  } else {
6924  $repTable = array("\t" => " ", "\n" => "<br>", "\r" => " ", "\0" => " ", "\x0B" => " ");
6925  }
6926 
6927  $text = strtr($text, $repTable);
6928  if ($charset == 'UTF-8') {
6929  $pattern = '/(<br[^>]*>)/Uu';
6930  } else {
6931  // /U is to have UNGREEDY regex to limit to one html tag. /u is for UTF8 support
6932  $pattern = '/(<br[^>]*>)/U'; // /U is to have UNGREEDY regex to limit to one html tag.
6933  }
6934  $a = preg_split($pattern, $text, -1, PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY);
6935 
6936  $firstline = '';
6937  $i = 0;
6938  $nba = count($a); // 2x nb of lines in $a because $a contains also a line for each new line separator
6939  while (($i < $nba) && ($i < ($nboflines * 2))) {
6940  if ($i % 2 == 0) {
6941  $firstline .= $a[$i];
6942  } elseif (($i < (($nboflines * 2) - 1)) && ($i < ($nba - 1))) {
6943  $firstline .= ($ishtml ? "<br>\n" : "\n");
6944  }
6945  $i++;
6946  }
6947  unset($a);
6948  return $firstline.(($i < $nba) ? '...' : '');
6949  }
6950 }
6951 
6952 
6963 function dol_nl2br($stringtoencode, $nl2brmode = 0, $forxml = false)
6964 {
6965  if (!$nl2brmode) {
6966  return nl2br($stringtoencode, $forxml);
6967  } else {
6968  $ret = preg_replace('/(\r\n|\r|\n)/i', ($forxml ? '<br />' : '<br>'), $stringtoencode);
6969  return $ret;
6970  }
6971 }
6972 
6973 
6991 function dol_htmlentitiesbr($stringtoencode, $nl2brmode = 0, $pagecodefrom = 'UTF-8', $removelasteolbr = 1)
6992 {
6993  $newstring = $stringtoencode;
6994  if (dol_textishtml($stringtoencode)) { // Check if text is already HTML or not
6995  $newstring = preg_replace('/<br(\s[\sa-zA-Z_="]*)?\/?>/i', '<br>', $newstring); // Replace "<br type="_moz" />" by "<br>". It's same and avoid pb with FPDF.
6996  if ($removelasteolbr) {
6997  $newstring = preg_replace('/<br>$/i', '', $newstring); // Remove last <br> (remove only last one)
6998  }
6999  $newstring = strtr($newstring, array('&'=>'__and__', '<'=>'__lt__', '>'=>'__gt__', '"'=>'__dquot__'));
7000  $newstring = dol_htmlentities($newstring, ENT_COMPAT, $pagecodefrom); // Make entity encoding
7001  $newstring = strtr($newstring, array('__and__'=>'&', '__lt__'=>'<', '__gt__'=>'>', '__dquot__'=>'"'));
7002  } else {
7003  if ($removelasteolbr) {
7004  $newstring = preg_replace('/(\r\n|\r|\n)$/i', '', $newstring); // Remove last \n (may remove several)
7005  }
7006  $newstring = dol_nl2br(dol_htmlentities($newstring, ENT_COMPAT, $pagecodefrom), $nl2brmode);
7007  }
7008  // Other substitutions that htmlentities does not do
7009  //$newstring=str_replace(chr(128),'&euro;',$newstring); // 128 = 0x80. Not in html entity table. // Seems useles with TCPDF. Make bug with UTF8 languages
7010  return $newstring;
7011 }
7012 
7020 function dol_htmlentitiesbr_decode($stringtodecod