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($stringtodecode, $pagecodeto = 'UTF-8')
7021 {
7022  $ret = dol_html_entity_decode($stringtodecode, ENT_COMPAT | ENT_HTML5, $pagecodeto);
7023  $ret = preg_replace('/'."\r\n".'<br(\s[\sa-zA-Z_="]*)?\/?>/i', "<br>", $ret);
7024  $ret = preg_replace('/<br(\s[\sa-zA-Z_="]*)?\/?>'."\r\n".'/i', "\r\n", $ret);
7025  $ret = preg_replace('/<br(\s[\sa-zA-Z_="]*)?\/?>'."\n".'/i', "\n", $ret);
7026  $ret = preg_replace('/<br(\s[\sa-zA-Z_="]*)?\/?>/i', "\n", $ret);
7027  return $ret;
7028 }
7029 
7036 function dol_htmlcleanlastbr($stringtodecode)
7037 {
7038  $ret = preg_replace('/&nbsp;$/i', "", $stringtodecode); // Because wysiwyg editor may add a &nbsp; at end of last line
7039  $ret = preg_replace('/(<br>|<br(\s[\sa-zA-Z_="]*)?\/?>|'."\n".'|'."\r".')+$/i', "", $ret);
7040  return $ret;
7041 }
7042 
7052 function dol_html_entity_decode($a, $b, $c = 'UTF-8', $keepsomeentities = 0)
7053 {
7054  $newstring = $a;
7055  if ($keepsomeentities) {
7056  $newstring = strtr($newstring, array('&amp;'=>'__andamp__', '&lt;'=>'__andlt__', '&gt;'=>'__andgt__', '"'=>'__dquot__'));
7057  }
7058  $newstring = html_entity_decode((string) $newstring, (int) $b, (string) $c);
7059  if ($keepsomeentities) {
7060  $newstring = strtr($newstring, array('__andamp__'=>'&amp;', '__andlt__'=>'&lt;', '__andgt__'=>'&gt;', '__dquot__'=>'"'));
7061  }
7062  return $newstring;
7063 }
7064 
7075 function dol_htmlentities($string, $flags = ENT_QUOTES|ENT_SUBSTITUTE, $encoding = 'UTF-8', $double_encode = false)
7076 {
7077  return htmlentities($string, $flags, $encoding, $double_encode);
7078 }
7079 
7089 function dol_string_is_good_iso($s, $clean = 0)
7090 {
7091  $len = dol_strlen($s);
7092  $out = '';
7093  $ok = 1;
7094  for ($scursor = 0; $scursor < $len; $scursor++) {
7095  $ordchar = ord($s[$scursor]);
7096  //print $scursor.'-'.$ordchar.'<br>';
7097  if ($ordchar < 32 && $ordchar != 13 && $ordchar != 10) {
7098  $ok = 0;
7099  break;
7100  } elseif ($ordchar > 126 && $ordchar < 160) {
7101  $ok = 0;
7102  break;
7103  } elseif ($clean) {
7104  $out .= $s[$scursor];
7105  }
7106  }
7107  if ($clean) {
7108  return $out;
7109  }
7110  return $ok;
7111 }
7112 
7121 function dol_nboflines($s, $maxchar = 0)
7122 {
7123  if ($s == '') {
7124  return 0;
7125  }
7126  $arraystring = explode("\n", $s);
7127  $nb = count($arraystring);
7128 
7129  return $nb;
7130 }
7131 
7132 
7142 function dol_nboflines_bis($text, $maxlinesize = 0, $charset = 'UTF-8')
7143 {
7144  $repTable = array("\t" => " ", "\n" => "<br>", "\r" => " ", "\0" => " ", "\x0B" => " ");
7145  if (dol_textishtml($text)) {
7146  $repTable = array("\t" => " ", "\n" => " ", "\r" => " ", "\0" => " ", "\x0B" => " ");
7147  }
7148 
7149  $text = strtr($text, $repTable);
7150  if ($charset == 'UTF-8') {
7151  $pattern = '/(<br[^>]*>)/Uu';
7152  } else {
7153  // /U is to have UNGREEDY regex to limit to one html tag. /u is for UTF8 support
7154  $pattern = '/(<br[^>]*>)/U'; // /U is to have UNGREEDY regex to limit to one html tag.
7155  }
7156  $a = preg_split($pattern, $text, -1, PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY);
7157 
7158  $nblines = (int) floor((count($a) + 1) / 2);
7159  // count possible auto line breaks
7160  if ($maxlinesize) {
7161  foreach ($a as $line) {
7162  if (dol_strlen($line) > $maxlinesize) {
7163  //$line_dec = html_entity_decode(strip_tags($line));
7164  $line_dec = html_entity_decode($line);
7165  if (dol_strlen($line_dec) > $maxlinesize) {
7166  $line_dec = wordwrap($line_dec, $maxlinesize, '\n', true);
7167  $nblines += substr_count($line_dec, '\n');
7168  }
7169  }
7170  }
7171  }
7172 
7173  unset($a);
7174  return $nblines;
7175 }
7176 
7185 function dol_textishtml($msg, $option = 0)
7186 {
7187  if ($option == 1) {
7188  if (preg_match('/<html/i', $msg)) {
7189  return true;
7190  } elseif (preg_match('/<body/i', $msg)) {
7191  return true;
7192  } elseif (preg_match('/<\/textarea/i', $msg)) {
7193  return true;
7194  } elseif (preg_match('/<(b|em|i|u)>/i', $msg)) {
7195  return true;
7196  } elseif (preg_match('/<br/i', $msg)) {
7197  return true;
7198  }
7199  return false;
7200  } else {
7201  // Remove all urls because 'http://aa?param1=abc&amp;param2=def' must not be used inside detection
7202  $msg = preg_replace('/https?:\/\/[^"\'\s]+/i', '', $msg);
7203  if (preg_match('/<html/i', $msg)) {
7204  return true;
7205  } elseif (preg_match('/<body/i', $msg)) {
7206  return true;
7207  } elseif (preg_match('/<\/textarea/i', $msg)) {
7208  return true;
7209  } elseif (preg_match('/<(b|em|i|u)>/i', $msg)) {
7210  return true;
7211  } elseif (preg_match('/<br\/>/i', $msg)) {
7212  return true;
7213  } elseif (preg_match('/<(br|div|font|li|p|span|strong|table)>/i', $msg)) {
7214  return true;
7215  } elseif (preg_match('/<(br|div|font|li|p|span|strong|table)\s+[^<>\/]*\/?>/i', $msg)) {
7216  return true;
7217  } elseif (preg_match('/<img\s+[^<>]*src[^<>]*>/i', $msg)) {
7218  return true; // must accept <img src="http://example.com/aaa.png" />
7219  } elseif (preg_match('/<a\s+[^<>]*href[^<>]*>/i', $msg)) {
7220  return true; // must accept <a href="http://example.com/aaa.png" />
7221  } elseif (preg_match('/<h[0-9]>/i', $msg)) {
7222  return true;
7223  } elseif (preg_match('/&[A-Z0-9]{1,6};/i', $msg)) {
7224  // TODO If content is 'A link https://aaa?param=abc&amp;param2=def', it return true but must be false
7225  return true; // Html entities names (http://www.w3schools.com/tags/ref_entities.asp)
7226  } elseif (preg_match('/&#[0-9]{2,3};/i', $msg)) {
7227  return true; // Html entities numbers (http://www.w3schools.com/tags/ref_entities.asp)
7228  }
7229 
7230  return false;
7231  }
7232 }
7233 
7248 function dol_concatdesc($text1, $text2, $forxml = false, $invert = false)
7249 {
7250  if (!empty($invert)) {
7251  $tmp = $text1;
7252  $text1 = $text2;
7253  $text2 = $tmp;
7254  }
7255 
7256  $ret = '';
7257  $ret .= (!dol_textishtml($text1) && dol_textishtml($text2)) ? dol_nl2br(dol_escape_htmltag($text1, 0, 1, '', 1), 0, $forxml) : $text1;
7258  $ret .= (!empty($text1) && !empty($text2)) ? ((dol_textishtml($text1) || dol_textishtml($text2)) ? ($forxml ? "<br >\n" : "<br>\n") : "\n") : "";
7259  $ret .= (dol_textishtml($text1) && !dol_textishtml($text2)) ? dol_nl2br(dol_escape_htmltag($text2, 0, 1, '', 1), 0, $forxml) : $text2;
7260  return $ret;
7261 }
7262 
7263 
7264 
7275 function getCommonSubstitutionArray($outputlangs, $onlykey = 0, $exclude = null, $object = null)
7276 {
7277  global $db, $conf, $mysoc, $user, $extrafields;
7278 
7279  $substitutionarray = array();
7280 
7281  if (empty($exclude) || !in_array('user', $exclude)) {
7282  // Add SIGNATURE into substitutionarray first, so, when we will make the substitution,
7283  // this will include signature content first and then replace var found into content of signature
7284  $signature = $user->signature;
7285  $substitutionarray = array_merge($substitutionarray, array(
7286  '__USER_SIGNATURE__' => (string) (($signature && empty($conf->global->MAIN_MAIL_DO_NOT_USE_SIGN)) ? ($onlykey == 2 ? dol_trunc(dol_string_nohtmltag($signature), 30) : $signature) : '')
7287  ));
7288 
7289  if (is_object($user)) {
7290  $substitutionarray = array_merge($substitutionarray, array(
7291  '__USER_ID__' => (string) $user->id,
7292  '__USER_LOGIN__' => (string) $user->login,
7293  '__USER_EMAIL__' => (string) $user->email,
7294  '__USER_PHONE__' => (string) dol_print_phone($user->office_phone),
7295  '__USER_PHONEPRO__' => (string) dol_print_phone($user->user_mobile),
7296  '__USER_PHONEMOBILE__' => (string) dol_print_phone($user->personal_mobile),
7297  '__USER_FAX__' => (string) $user->office_fax,
7298  '__USER_LASTNAME__' => (string) $user->lastname,
7299  '__USER_FIRSTNAME__' => (string) $user->firstname,
7300  '__USER_FULLNAME__' => (string) $user->getFullName($outputlangs),
7301  '__USER_SUPERVISOR_ID__' => (string) ($user->fk_user ? $user->fk_user : '0'),
7302  '__USER_JOB__' => (string) $user->job,
7303  '__USER_REMOTE_IP__' => (string) getUserRemoteIP()
7304  ));
7305  }
7306  }
7307  if ((empty($exclude) || !in_array('mycompany', $exclude)) && is_object($mysoc)) {
7308  $substitutionarray = array_merge($substitutionarray, array(
7309  '__MYCOMPANY_NAME__' => $mysoc->name,
7310  '__MYCOMPANY_EMAIL__' => $mysoc->email,
7311  '__MYCOMPANY_PHONE__' => dol_print_phone($mysoc->phone),
7312  '__MYCOMPANY_FAX__' => dol_print_phone($mysoc->fax),
7313  '__MYCOMPANY_PROFID1__' => $mysoc->idprof1,
7314  '__MYCOMPANY_PROFID2__' => $mysoc->idprof2,
7315  '__MYCOMPANY_PROFID3__' => $mysoc->idprof3,
7316  '__MYCOMPANY_PROFID4__' => $mysoc->idprof4,
7317  '__MYCOMPANY_PROFID5__' => $mysoc->idprof5,
7318  '__MYCOMPANY_PROFID6__' => $mysoc->idprof6,
7319  '__MYCOMPANY_CAPITAL__' => $mysoc->capital,
7320  '__MYCOMPANY_FULLADDRESS__' => (method_exists($mysoc, 'getFullAddress') ? $mysoc->getFullAddress(1, ', ') : ''), // $mysoc may be stdClass
7321  '__MYCOMPANY_ADDRESS__' => $mysoc->address,
7322  '__MYCOMPANY_ZIP__' => $mysoc->zip,
7323  '__MYCOMPANY_TOWN__' => $mysoc->town,
7324  '__MYCOMPANY_COUNTRY__' => $mysoc->country,
7325  '__MYCOMPANY_COUNTRY_ID__' => $mysoc->country_id,
7326  '__MYCOMPANY_COUNTRY_CODE__' => $mysoc->country_code,
7327  '__MYCOMPANY_CURRENCY_CODE__' => $conf->currency
7328  ));
7329  }
7330 
7331  if (($onlykey || is_object($object)) && (empty($exclude) || !in_array('object', $exclude))) {
7332  if ($onlykey) {
7333  $substitutionarray['__ID__'] = '__ID__';
7334  $substitutionarray['__REF__'] = '__REF__';
7335  $substitutionarray['__NEWREF__'] = '__NEWREF__';
7336  $substitutionarray['__REF_CLIENT__'] = '__REF_CLIENT__';
7337  $substitutionarray['__REF_SUPPLIER__'] = '__REF_SUPPLIER__';
7338  $substitutionarray['__NOTE_PUBLIC__'] = '__NOTE_PUBLIC__';
7339  $substitutionarray['__NOTE_PRIVATE__'] = '__NOTE_PRIVATE__';
7340  $substitutionarray['__EXTRAFIELD_XXX__'] = '__EXTRAFIELD_XXX__';
7341 
7342  if (!empty($conf->societe->enabled)) { // Most objects are concerned
7343  $substitutionarray['__THIRDPARTY_ID__'] = '__THIRDPARTY_ID__';
7344  $substitutionarray['__THIRDPARTY_NAME__'] = '__THIRDPARTY_NAME__';
7345  $substitutionarray['__THIRDPARTY_NAME_ALIAS__'] = '__THIRDPARTY_NAME_ALIAS__';
7346  $substitutionarray['__THIRDPARTY_CODE_CLIENT__'] = '__THIRDPARTY_CODE_CLIENT__';
7347  $substitutionarray['__THIRDPARTY_CODE_FOURNISSEUR__'] = '__THIRDPARTY_CODE_FOURNISSEUR__';
7348  $substitutionarray['__THIRDPARTY_EMAIL__'] = '__THIRDPARTY_EMAIL__';
7349  $substitutionarray['__THIRDPARTY_PHONE__'] = '__THIRDPARTY_PHONE__';
7350  $substitutionarray['__THIRDPARTY_FAX__'] = '__THIRDPARTY_FAX__';
7351  $substitutionarray['__THIRDPARTY_ADDRESS__'] = '__THIRDPARTY_ADDRESS__';
7352  $substitutionarray['__THIRDPARTY_ZIP__'] = '__THIRDPARTY_ZIP__';
7353  $substitutionarray['__THIRDPARTY_TOWN__'] = '__THIRDPARTY_TOWN__';
7354  $substitutionarray['__THIRDPARTY_IDPROF1__'] = '__THIRDPARTY_IDPROF1__';
7355  $substitutionarray['__THIRDPARTY_IDPROF2__'] = '__THIRDPARTY_IDPROF2__';
7356  $substitutionarray['__THIRDPARTY_IDPROF3__'] = '__THIRDPARTY_IDPROF3__';
7357  $substitutionarray['__THIRDPARTY_IDPROF4__'] = '__THIRDPARTY_IDPROF4__';
7358  $substitutionarray['__THIRDPARTY_IDPROF5__'] = '__THIRDPARTY_IDPROF5__';
7359  $substitutionarray['__THIRDPARTY_IDPROF6__'] = '__THIRDPARTY_IDPROF6__';
7360  $substitutionarray['__THIRDPARTY_TVAINTRA__'] = '__THIRDPARTY_TVAINTRA__';
7361  $substitutionarray['__THIRDPARTY_NOTE_PUBLIC__'] = '__THIRDPARTY_NOTE_PUBLIC__';
7362  $substitutionarray['__THIRDPARTY_NOTE_PRIVATE__'] = '__THIRDPARTY_NOTE_PRIVATE__';
7363  }
7364  if (!empty($conf->adherent->enabled) && (!is_object($object) || $object->element == 'adherent')) {
7365  $substitutionarray['__MEMBER_ID__'] = '__MEMBER_ID__';
7366  $substitutionarray['__MEMBER_CIVILITY__'] = '__MEMBER_CIVILITY__';
7367  $substitutionarray['__MEMBER_FIRSTNAME__'] = '__MEMBER_FIRSTNAME__';
7368  $substitutionarray['__MEMBER_LASTNAME__'] = '__MEMBER_LASTNAME__';
7369  $substitutionarray['__MEMBER_USER_LOGIN_INFORMATION__'] = 'Login and pass of the external user account';
7370  /*$substitutionarray['__MEMBER_NOTE_PUBLIC__'] = '__MEMBER_NOTE_PUBLIC__';
7371  $substitutionarray['__MEMBER_NOTE_PRIVATE__'] = '__MEMBER_NOTE_PRIVATE__';*/
7372  }
7373  // add variables subtitutions ticket
7374  if (!empty($conf->ticket->enabled) && (!is_object($object) || $object->element == 'ticket')) {
7375  $substitutionarray['__TICKET_TRACKID__'] = '__TICKET_TRACKID__';
7376  $substitutionarray['__TICKET_SUBJECT__'] = '__TICKET_SUBJECT__';
7377  $substitutionarray['__TICKET_TYPE__'] = '__TICKET_TYPE__';
7378  $substitutionarray['__TICKET_SEVERITY__'] = '__TICKET_SEVERITY__';
7379  $substitutionarray['__TICKET_CATEGORY__'] = '__TICKET_CATEGORY__';
7380  $substitutionarray['__TICKET_ANALYTIC_CODE__'] = '__TICKET_ANALYTIC_CODE__';
7381  $substitutionarray['__TICKET_MESSAGE__'] = '__TICKET_MESSAGE__';
7382  $substitutionarray['__TICKET_PROGRESSION__'] = '__TICKET_PROGRESSION__';
7383  $substitutionarray['__TICKET_USER_ASSIGN__'] = '__TICKET_USER_ASSIGN__';
7384  }
7385 
7386  if (!empty($conf->recruitment->enabled) && (!is_object($object) || $object->element == 'recruitmentcandidature')) {
7387  $substitutionarray['__CANDIDATE_FULLNAME__'] = '__CANDIDATE_FULLNAME__';
7388  $substitutionarray['__CANDIDATE_FIRSTNAME__'] = '__CANDIDATE_FIRSTNAME__';
7389  $substitutionarray['__CANDIDATE_LASTNAME__'] = '__CANDIDATE_LASTNAME__';
7390  }
7391  if (!empty($conf->project->enabled)) { // Most objects
7392  $substitutionarray['__PROJECT_ID__'] = '__PROJECT_ID__';
7393  $substitutionarray['__PROJECT_REF__'] = '__PROJECT_REF__';
7394  $substitutionarray['__PROJECT_NAME__'] = '__PROJECT_NAME__';
7395  /*$substitutionarray['__PROJECT_NOTE_PUBLIC__'] = '__PROJECT_NOTE_PUBLIC__';
7396  $substitutionarray['__PROJECT_NOTE_PRIVATE__'] = '__PROJECT_NOTE_PRIVATE__';*/
7397  }
7398  if (!empty($conf->contrat->enabled) && (!is_object($object) || $object->element == 'contract')) {
7399  $substitutionarray['__CONTRACT_HIGHEST_PLANNED_START_DATE__'] = 'Highest date planned for a service start';
7400  $substitutionarray['__CONTRACT_HIGHEST_PLANNED_START_DATETIME__'] = 'Highest date and hour planned for service start';
7401  $substitutionarray['__CONTRACT_LOWEST_EXPIRATION_DATE__'] = 'Lowest data for planned expiration of service';
7402  $substitutionarray['__CONTRACT_LOWEST_EXPIRATION_DATETIME__'] = 'Lowest date and hour for planned expiration of service';
7403  }
7404  $substitutionarray['__ONLINE_PAYMENT_URL__'] = 'UrlToPayOnlineIfApplicable';
7405  $substitutionarray['__ONLINE_PAYMENT_TEXT_AND_URL__'] = 'TextAndUrlToPayOnlineIfApplicable';
7406  $substitutionarray['__SECUREKEYPAYMENT__'] = 'Security key (if key is not unique per record)';
7407  $substitutionarray['__SECUREKEYPAYMENT_MEMBER__'] = 'Security key for payment on a member subscription (one key per member)';
7408  $substitutionarray['__SECUREKEYPAYMENT_ORDER__'] = 'Security key for payment on an order';
7409  $substitutionarray['__SECUREKEYPAYMENT_INVOICE__'] = 'Security key for payment on an invoice';
7410  $substitutionarray['__SECUREKEYPAYMENT_CONTRACTLINE__'] = 'Security key for payment on a service of a contract';
7411 
7412  $substitutionarray['__DIRECTDOWNLOAD_URL_PROPOSAL__'] = 'Direct download url of a proposal';
7413  $substitutionarray['__DIRECTDOWNLOAD_URL_ORDER__'] = 'Direct download url of an order';
7414  $substitutionarray['__DIRECTDOWNLOAD_URL_INVOICE__'] = 'Direct download url of an invoice';
7415  $substitutionarray['__DIRECTDOWNLOAD_URL_CONTRACT__'] = 'Direct download url of a contract';
7416  $substitutionarray['__DIRECTDOWNLOAD_URL_SUPPLIER_PROPOSAL__'] = 'Direct download url of a supplier proposal';
7417 
7418  if (!empty($conf->expedition->enabled) && (!is_object($object) || $object->element == 'shipping')) {
7419  $substitutionarray['__SHIPPINGTRACKNUM__'] = 'Shipping tracking number';
7420  $substitutionarray['__SHIPPINGTRACKNUMURL__'] = 'Shipping tracking url';
7421  }
7422  if (!empty($conf->reception->enabled) && (!is_object($object) || $object->element == 'reception')) {
7423  $substitutionarray['__RECEPTIONTRACKNUM__'] = 'Shippin tracking number of shipment';
7424  $substitutionarray['__RECEPTIONTRACKNUMURL__'] = 'Shipping tracking url';
7425  }
7426  } else {
7427  $substitutionarray['__ID__'] = $object->id;
7428  $substitutionarray['__REF__'] = $object->ref;
7429  $substitutionarray['__NEWREF__'] = $object->newref;
7430  $substitutionarray['__REF_CLIENT__'] = (isset($object->ref_client) ? $object->ref_client : (isset($object->ref_customer) ? $object->ref_customer : null));
7431  $substitutionarray['__REF_SUPPLIER__'] = (isset($object->ref_supplier) ? $object->ref_supplier : null);
7432  $substitutionarray['__NOTE_PUBLIC__'] = (isset($object->note_public) ? $object->note_public : null);
7433  $substitutionarray['__NOTE_PRIVATE__'] = (isset($object->note_private) ? $object->note_private : null);
7434  if ($object->element == "shipping") {
7435  $substitutionarray['__DATE_DELIVERY__'] = (isset($object->date_delivery) ? dol_print_date($object->date_delivery, 'day', 0, $outputlangs) : '');
7436  } else {
7437  $substitutionarray['__DATE_DELIVERY__'] = (isset($object->date_livraison) ? dol_print_date($object->date_livraison, 'day', 0, $outputlangs) : '');
7438  }
7439  $substitutionarray['__DATE_DELIVERY_DAY__'] = (isset($object->date_livraison) ? dol_print_date($object->date_livraison, "%d") : '');
7440  $substitutionarray['__DATE_DELIVERY_DAY_TEXT__'] = (isset($object->date_livraison) ? dol_print_date($object->date_livraison, "%A") : '');
7441  $substitutionarray['__DATE_DELIVERY_MON__'] = (isset($object->date_livraison) ? dol_print_date($object->date_livraison, "%m") : '');
7442  $substitutionarray['__DATE_DELIVERY_MON_TEXT__'] = (isset($object->date_livraison) ? dol_print_date($object->date_livraison, "%b") : '');
7443  $substitutionarray['__DATE_DELIVERY_YEAR__'] = (isset($object->date_livraison) ? dol_print_date($object->date_livraison, "%Y") : '');
7444  $substitutionarray['__DATE_DELIVERY_HH__'] = (isset($object->date_livraison) ? dol_print_date($object->date_livraison, "%H") : '');
7445  $substitutionarray['__DATE_DELIVERY_MM__'] = (isset($object->date_livraison) ? dol_print_date($object->date_livraison, "%M") : '');
7446  $substitutionarray['__DATE_DELIVERY_SS__'] = (isset($object->date_livraison) ? dol_print_date($object->date_livraison, "%S") : '');
7447 
7448  // For backward compatibility
7449  $substitutionarray['__REFCLIENT__'] = (isset($object->ref_client) ? $object->ref_client : (isset($object->ref_customer) ? $object->ref_customer : null));
7450  $substitutionarray['__REFSUPPLIER__'] = (isset($object->ref_supplier) ? $object->ref_supplier : null);
7451  $substitutionarray['__SUPPLIER_ORDER_DATE_DELIVERY__'] = (isset($object->date_livraison) ? dol_print_date($object->date_livraison, 'day', 0, $outputlangs) : '');
7452  $substitutionarray['__SUPPLIER_ORDER_DELAY_DELIVERY__'] = (isset($object->availability_code) ? ($outputlangs->transnoentities("AvailabilityType".$object->availability_code) != ('AvailabilityType'.$object->availability_code) ? $outputlangs->transnoentities("AvailabilityType".$object->availability_code) : $outputlangs->convToOutputCharset(isset($object->availability) ? $object->availability : '')) : '');
7453 
7454  if (is_object($object) && ($object->element == 'adherent' || $object->element == 'member') && $object->id > 0) {
7455  $birthday = (empty($object->birth) ? '' : dol_print_date($object->birth, 'day'));
7456 
7457  $substitutionarray['__MEMBER_ID__'] = (isset($object->id) ? $object->id : '');
7458  if (method_exists($object, 'getCivilityLabel')) {
7459  $substitutionarray['__MEMBER_CIVILITY__'] = $object->getCivilityLabel();
7460  }
7461  $substitutionarray['__MEMBER_FIRSTNAME__'] = (isset($object->firstname) ? $object->firstname : '');
7462  $substitutionarray['__MEMBER_LASTNAME__'] = (isset($object->lastname) ? $object->lastname : '');
7463  $substitutionarray['__MEMBER_USER_LOGIN_INFORMATION__'] = '';
7464  if (method_exists($object, 'getFullName')) {
7465  $substitutionarray['__MEMBER_FULLNAME__'] = $object->getFullName($outputlangs);
7466  }
7467  $substitutionarray['__MEMBER_COMPANY__'] = (isset($object->societe) ? $object->societe : '');
7468  $substitutionarray['__MEMBER_ADDRESS__'] = (isset($object->address) ? $object->address : '');
7469  $substitutionarray['__MEMBER_ZIP__'] = (isset($object->zip) ? $object->zip : '');
7470  $substitutionarray['__MEMBER_TOWN__'] = (isset($object->town) ? $object->town : '');
7471  $substitutionarray['__MEMBER_COUNTRY__'] = (isset($object->country) ? $object->country : '');
7472  $substitutionarray['__MEMBER_EMAIL__'] = (isset($object->email) ? $object->email : '');
7473  $substitutionarray['__MEMBER_BIRTH__'] = (isset($birthday) ? $birthday : '');
7474  $substitutionarray['__MEMBER_PHOTO__'] = (isset($object->photo) ? $object->photo : '');
7475  $substitutionarray['__MEMBER_LOGIN__'] = (isset($object->login) ? $object->login : '');
7476  $substitutionarray['__MEMBER_PASSWORD__'] = (isset($object->pass) ? $object->pass : '');
7477  $substitutionarray['__MEMBER_PHONE__'] = (isset($object->phone) ? dol_print_phone($object->phone) : '');
7478  $substitutionarray['__MEMBER_PHONEPRO__'] = (isset($object->phone_perso) ? dol_print_phone($object->phone_perso) : '');
7479  $substitutionarray['__MEMBER_PHONEMOBILE__'] = (isset($object->phone_mobile) ? dol_print_phone($object->phone_mobile) : '');
7480  $substitutionarray['__MEMBER_TYPE__'] = (isset($object->type) ? $object->type : '');
7481  $substitutionarray['__MEMBER_FIRST_SUBSCRIPTION_DATE__'] = dol_print_date($object->first_subscription_date, 'dayrfc');
7482  $substitutionarray['__MEMBER_FIRST_SUBSCRIPTION_DATE_START__'] = (isset($object->first_subscription_date_start) ? dol_print_date($object->first_subscription_date_start, 'dayrfc') : '');
7483  $substitutionarray['__MEMBER_FIRST_SUBSCRIPTION_DATE_END__'] = (isset($object->first_subscription_date_end) ? dol_print_date($object->first_subscription_date_end, 'dayrfc') : '');
7484  $substitutionarray['__MEMBER_LAST_SUBSCRIPTION_DATE__'] = dol_print_date($object->last_subscription_date, 'dayrfc');
7485  $substitutionarray['__MEMBER_LAST_SUBSCRIPTION_DATE_START__'] = dol_print_date($object->last_subscription_date_start, 'dayrfc');
7486  $substitutionarray['__MEMBER_LAST_SUBSCRIPTION_DATE_END__'] = dol_print_date($object->last_subscription_date_end, 'dayrfc');
7487  }
7488 
7489  if (is_object($object) && $object->element == 'societe') {
7490  $substitutionarray['__THIRDPARTY_ID__'] = (is_object($object) ? $object->id : '');
7491  $substitutionarray['__THIRDPARTY_NAME__'] = (is_object($object) ? $object->name : '');
7492  $substitutionarray['__THIRDPARTY_NAME_ALIAS__'] = (is_object($object) ? $object->name_alias : '');
7493  $substitutionarray['__THIRDPARTY_CODE_CLIENT__'] = (is_object($object) ? $object->code_client : '');
7494  $substitutionarray['__THIRDPARTY_CODE_FOURNISSEUR__'] = (is_object($object) ? $object->code_fournisseur : '');
7495  $substitutionarray['__THIRDPARTY_EMAIL__'] = (is_object($object) ? $object->email : '');
7496  $substitutionarray['__THIRDPARTY_PHONE__'] = (is_object($object) ? dol_print_phone($object->phone) : '');
7497  $substitutionarray['__THIRDPARTY_FAX__'] = (is_object($object) ? dol_print_phone($object->fax) : '');
7498  $substitutionarray['__THIRDPARTY_ADDRESS__'] = (is_object($object) ? $object->address : '');
7499  $substitutionarray['__THIRDPARTY_ZIP__'] = (is_object($object) ? $object->zip : '');
7500  $substitutionarray['__THIRDPARTY_TOWN__'] = (is_object($object) ? $object->town : '');
7501  $substitutionarray['__THIRDPARTY_COUNTRY_ID__'] = (is_object($object) ? $object->country_id : '');
7502  $substitutionarray['__THIRDPARTY_COUNTRY_CODE__'] = (is_object($object) ? $object->country_code : '');
7503  $substitutionarray['__THIRDPARTY_IDPROF1__'] = (is_object($object) ? $object->idprof1 : '');
7504  $substitutionarray['__THIRDPARTY_IDPROF2__'] = (is_object($object) ? $object->idprof2 : '');
7505  $substitutionarray['__THIRDPARTY_IDPROF3__'] = (is_object($object) ? $object->idprof3 : '');
7506  $substitutionarray['__THIRDPARTY_IDPROF4__'] = (is_object($object) ? $object->idprof4 : '');
7507  $substitutionarray['__THIRDPARTY_IDPROF5__'] = (is_object($object) ? $object->idprof5 : '');
7508  $substitutionarray['__THIRDPARTY_IDPROF6__'] = (is_object($object) ? $object->idprof6 : '');
7509  $substitutionarray['__THIRDPARTY_TVAINTRA__'] = (is_object($object) ? $object->tva_intra : '');
7510  $substitutionarray['__THIRDPARTY_NOTE_PUBLIC__'] = (is_object($object) ? dol_htmlentitiesbr($object->note_public) : '');
7511  $substitutionarray['__THIRDPARTY_NOTE_PRIVATE__'] = (is_object($object) ? dol_htmlentitiesbr($object->note_private) : '');
7512  } elseif (is_object($object->thirdparty)) {
7513  $substitutionarray['__THIRDPARTY_ID__'] = (is_object($object->thirdparty) ? $object->thirdparty->id : '');
7514  $substitutionarray['__THIRDPARTY_NAME__'] = (is_object($object->thirdparty) ? $object->thirdparty->name : '');
7515  $substitutionarray['__THIRDPARTY_NAME_ALIAS__'] = (is_object($object->thirdparty) ? $object->thirdparty->name_alias : '');
7516  $substitutionarray['__THIRDPARTY_CODE_CLIENT__'] = (is_object($object->thirdparty) ? $object->thirdparty->code_client : '');
7517  $substitutionarray['__THIRDPARTY_CODE_FOURNISSEUR__'] = (is_object($object->thirdparty) ? $object->thirdparty->code_fournisseur : '');
7518  $substitutionarray['__THIRDPARTY_EMAIL__'] = (is_object($object->thirdparty) ? $object->thirdparty->email : '');
7519  $substitutionarray['__THIRDPARTY_PHONE__'] = (is_object($object->thirdparty) ? dol_print_phone($object->thirdparty->phone) : '');
7520  $substitutionarray['__THIRDPARTY_FAX__'] = (is_object($object->thirdparty) ? dol_print_phone($object->thirdparty->fax) : '');
7521  $substitutionarray['__THIRDPARTY_ADDRESS__'] = (is_object($object->thirdparty) ? $object->thirdparty->address : '');
7522  $substitutionarray['__THIRDPARTY_ZIP__'] = (is_object($object->thirdparty) ? $object->thirdparty->zip : '');
7523  $substitutionarray['__THIRDPARTY_TOWN__'] = (is_object($object->thirdparty) ? $object->thirdparty->town : '');
7524  $substitutionarray['__THIRDPARTY_COUNTRY_ID__'] = (is_object($object->thirdparty) ? $object->thirdparty->country_id : '');
7525  $substitutionarray['__THIRDPARTY_COUNTRY_CODE__'] = (is_object($object->thirdparty) ? $object->thirdparty->country_code : '');
7526  $substitutionarray['__THIRDPARTY_IDPROF1__'] = (is_object($object->thirdparty) ? $object->thirdparty->idprof1 : '');
7527  $substitutionarray['__THIRDPARTY_IDPROF2__'] = (is_object($object->thirdparty) ? $object->thirdparty->idprof2 : '');
7528  $substitutionarray['__THIRDPARTY_IDPROF3__'] = (is_object($object->thirdparty) ? $object->thirdparty->idprof3 : '');
7529  $substitutionarray['__THIRDPARTY_IDPROF4__'] = (is_object($object->thirdparty) ? $object->thirdparty->idprof4 : '');
7530  $substitutionarray['__THIRDPARTY_IDPROF5__'] = (is_object($object->thirdparty) ? $object->thirdparty->idprof5 : '');
7531  $substitutionarray['__THIRDPARTY_IDPROF6__'] = (is_object($object->thirdparty) ? $object->thirdparty->idprof6 : '');
7532  $substitutionarray['__THIRDPARTY_TVAINTRA__'] = (is_object($object->thirdparty) ? $object->thirdparty->tva_intra : '');
7533  $substitutionarray['__THIRDPARTY_NOTE_PUBLIC__'] = (is_object($object->thirdparty) ? dol_htmlentitiesbr($object->thirdparty->note_public) : '');
7534  $substitutionarray['__THIRDPARTY_NOTE_PRIVATE__'] = (is_object($object->thirdparty) ? dol_htmlentitiesbr($object->thirdparty->note_private) : '');
7535  }
7536 
7537  if (is_object($object) && $object->element == 'recruitmentcandidature') {
7538  $substitutionarray['__CANDIDATE_FULLNAME__'] = $object->getFullName($outputlangs);
7539  $substitutionarray['__CANDIDATE_FIRSTNAME__'] = isset($object->firstname) ? $object->firstname : '';
7540  $substitutionarray['__CANDIDATE_LASTNAME__'] = isset($object->lastname) ? $object->lastname : '';
7541  }
7542 
7543  $project = null;
7544  if (is_object($object->project)) {
7545  $project = $object->project;
7546  } elseif (is_object($object->projet)) { // Deprecated, for backward compatibility
7547  $project = $object->projet;
7548  }
7549  if ($project) {
7550  $substitutionarray['__PROJECT_ID__'] = $project->id;
7551  $substitutionarray['__PROJECT_REF__'] = $project->ref;
7552  $substitutionarray['__PROJECT_NAME__'] = $project->title;
7553  }
7554  if (is_object($object) && $object->element == 'project') {
7555  $substitutionarray['__PROJECT_NAME__'] = $object->title;
7556  }
7557 
7558  if (is_object($object) && $object->element == 'shipping') {
7559  $substitutionarray['__SHIPPINGTRACKNUM__'] = $object->tracking_number;
7560  $substitutionarray['__SHIPPINGTRACKNUMURL__'] = $object->tracking_url;
7561  }
7562  if (is_object($object) && $object->element == 'reception') {
7563  $substitutionarray['__RECEPTIONTRACKNUM__'] = $object->tracking_number;
7564  $substitutionarray['__RECEPTIONTRACKNUMURL__'] = $object->tracking_url;
7565  }
7566 
7567  if (is_object($object) && $object->element == 'contrat' && $object->id > 0 && is_array($object->lines)) {
7568  $dateplannedstart = '';
7569  $datenextexpiration = '';
7570  foreach ($object->lines as $line) {
7571  if ($line->date_ouverture_prevue > $dateplannedstart) {
7572  $dateplannedstart = $line->date_ouverture_prevue;
7573  }
7574  if ($line->statut == 4 && $line->date_fin_prevue && (!$datenextexpiration || $line->date_fin_prevue < $datenextexpiration)) {
7575  $datenextexpiration = $line->date_fin_prevue;
7576  }
7577  }
7578  $substitutionarray['__CONTRACT_HIGHEST_PLANNED_START_DATE__'] = dol_print_date($dateplannedstart, 'dayrfc');
7579  $substitutionarray['__CONTRACT_HIGHEST_PLANNED_START_DATETIME__'] = dol_print_date($dateplannedstart, 'standard');
7580  $substitutionarray['__CONTRACT_LOWEST_EXPIRATION_DATE__'] = dol_print_date($datenextexpiration, 'dayrfc');
7581  $substitutionarray['__CONTRACT_LOWEST_EXPIRATION_DATETIME__'] = dol_print_date($datenextexpiration, 'standard');
7582  }
7583  // add substition variable for ticket
7584  if (is_object($object) && $object->element == 'ticket') {
7585  $substitutionarray['__TICKET_TRACKID__'] = $object->track_id;
7586  $substitutionarray['__REF__'] = $object->ref;
7587  $substitutionarray['__TICKET_SUBJECT__'] = $object->subject;
7588  $substitutionarray['__TICKET_TYPE__'] = $object->type_code;
7589  $substitutionarray['__TICKET_SEVERITY__'] = $object->severity_code;
7590  $substitutionarray['__TICKET_CATEGORY__'] = $object->category_code; // For backward compatibility
7591  $substitutionarray['__TICKET_ANALYTIC_CODE__'] = $object->category_code;
7592  $substitutionarray['__TICKET_MESSAGE__'] = $object->message;
7593  $substitutionarray['__TICKET_PROGRESSION__'] = $object->progress;
7594  $userstat = new User($db);
7595  if ($object->fk_user_assign > 0) {
7596  $userstat->fetch($object->fk_user_assign);
7597  $substitutionarray['__TICKET_USER_ASSIGN__'] = dolGetFirstLastname($userstat->firstname, $userstat->lastname);
7598  }
7599 
7600  if ($object->fk_user_create > 0) {
7601  $userstat->fetch($object->fk_user_create);
7602  $substitutionarray['__USER_CREATE__'] = dolGetFirstLastname($userstat->firstname, $userstat->lastname);
7603  }
7604  }
7605 
7606  // Create dynamic tags for __EXTRAFIELD_FIELD__
7607  if ($object->table_element && $object->id > 0) {
7608  if (!is_object($extrafields)) {
7609  $extrafields = new ExtraFields($db);
7610  }
7611  $extrafields->fetch_name_optionals_label($object->table_element, true);
7612 
7613  if ($object->fetch_optionals() > 0) {
7614  if (is_array($extrafields->attributes[$object->table_element]['label']) && count($extrafields->attributes[$object->table_element]['label']) > 0) {
7615  foreach ($extrafields->attributes[$object->table_element]['label'] as $key => $label) {
7616  $substitutionarray['__EXTRAFIELD_'.strtoupper($key).'__'] = $object->array_options['options_'.$key];
7617  if ($extrafields->attributes[$object->table_element]['type'][$key] == 'date') {
7618  $substitutionarray['__EXTRAFIELD_'.strtoupper($key).'__'] = dol_print_date($object->array_options['options_'.$key], 'day');
7619  $substitutionarray['__EXTRAFIELD_'.strtoupper($key).'_LOCALE__'] = dol_print_date($object->array_options['options_'.$key], 'day', 'tzserver', $outputlangs);
7620  $substitutionarray['__EXTRAFIELD_'.strtoupper($key).'_RFC__'] = dol_print_date($object->array_options['options_'.$key], 'dayrfc');
7621  } elseif ($extrafields->attributes[$object->table_element]['type'][$key] == 'datetime') {
7622  $datetime = $object->array_options['options_'.$key];
7623  $substitutionarray['__EXTRAFIELD_'.strtoupper($key).'__'] = ($datetime != "0000-00-00 00:00:00" ? dol_print_date($datetime, 'dayhour') : '');
7624  $substitutionarray['__EXTRAFIELD_'.strtoupper($key).'_LOCALE__'] = ($datetime != "0000-00-00 00:00:00" ? dol_print_date($datetime, 'dayhour', 'tzserver', $outputlangs) : '');
7625  $substitutionarray['__EXTRAFIELD_'.strtoupper($key).'_DAY_LOCALE__'] = ($datetime != "0000-00-00 00:00:00" ? dol_print_date($datetime, 'day', 'tzserver', $outputlangs) : '');
7626  $substitutionarray['__EXTRAFIELD_'.strtoupper($key).'_RFC__'] = ($datetime != "0000-00-00 00:00:00" ? dol_print_date($datetime, 'dayhourrfc') : '');
7627  } elseif ($extrafields->attributes[$object->table_element]['type'][$key] == 'phone') {
7628  $substitutionarray['__EXTRAFIELD_'.strtoupper($key).'__'] = dol_print_phone($object->array_options['options_'.$key]);
7629  } elseif ($extrafields->attributes[$object->table_element]['type'][$key] == 'price') {
7630  $substitutionarray['__EXTRAFIELD_'.strtoupper($key).'__'] = $object->array_options['options_'.$key];
7631  $substitutionarray['__EXTRAFIELD_'.strtoupper($key).'_FORMATED__'] = price($object->array_options['options_'.$key]);
7632  }
7633  }
7634  }
7635  }
7636  }
7637 
7638  // Complete substitution array with the url to make online payment
7639  $paymenturl = '';
7640  if (empty($substitutionarray['__REF__'])) {
7641  $paymenturl = '';
7642  } else {
7643  // Set the online payment url link into __ONLINE_PAYMENT_URL__ key
7644  require_once DOL_DOCUMENT_ROOT.'/core/lib/payments.lib.php';
7645  $outputlangs->loadLangs(array('paypal', 'other'));
7646  $typeforonlinepayment = 'free';
7647  if (is_object($object) && $object->element == 'commande') {
7648  $typeforonlinepayment = 'order';
7649  }
7650  if (is_object($object) && $object->element == 'facture') {
7651  $typeforonlinepayment = 'invoice';
7652  }
7653  if (is_object($object) && $object->element == 'member') {
7654  $typeforonlinepayment = 'member';
7655  }
7656  if (is_object($object) && $object->element == 'contrat') {
7657  $typeforonlinepayment = 'contract';
7658  }
7659  $url = getOnlinePaymentUrl(0, $typeforonlinepayment, $substitutionarray['__REF__']);
7660  $paymenturl = $url;
7661  }
7662 
7663  if ($object->id > 0) {
7664  $substitutionarray['__ONLINE_PAYMENT_TEXT_AND_URL__'] = ($paymenturl ?str_replace('\n', "\n", $outputlangs->trans("PredefinedMailContentLink", $paymenturl)) : '');
7665  $substitutionarray['__ONLINE_PAYMENT_URL__'] = $paymenturl;
7666 
7667  if (is_object($object) && $object->element == 'propal') {
7668  require_once DOL_DOCUMENT_ROOT.'/core/lib/signature.lib.php';
7669  $substitutionarray['__ONLINE_SIGN_URL__'] = getOnlineSignatureUrl(0, 'proposal', $object->ref);
7670  }
7671  if (!empty($conf->global->PROPOSAL_ALLOW_EXTERNAL_DOWNLOAD) && is_object($object) && $object->element == 'propal') {
7672  $substitutionarray['__DIRECTDOWNLOAD_URL_PROPOSAL__'] = $object->getLastMainDocLink($object->element);
7673  } else {
7674  $substitutionarray['__DIRECTDOWNLOAD_URL_PROPOSAL__'] = '';
7675  }
7676  if (!empty($conf->global->ORDER_ALLOW_EXTERNAL_DOWNLOAD) && is_object($object) && $object->element == 'commande') {
7677  $substitutionarray['__DIRECTDOWNLOAD_URL_ORDER__'] = $object->getLastMainDocLink($object->element);
7678  } else {
7679  $substitutionarray['__DIRECTDOWNLOAD_URL_ORDER__'] = '';
7680  }
7681  if (!empty($conf->global->INVOICE_ALLOW_EXTERNAL_DOWNLOAD) && is_object($object) && $object->element == 'facture') {
7682  $substitutionarray['__DIRECTDOWNLOAD_URL_INVOICE__'] = $object->getLastMainDocLink($object->element);
7683  } else {
7684  $substitutionarray['__DIRECTDOWNLOAD_URL_INVOICE__'] = '';
7685  }
7686  if (!empty($conf->global->CONTRACT_ALLOW_EXTERNAL_DOWNLOAD) && is_object($object) && $object->element == 'contrat') {
7687  $substitutionarray['__DIRECTDOWNLOAD_URL_CONTRACT__'] = $object->getLastMainDocLink($object->element);
7688  } else {
7689  $substitutionarray['__DIRECTDOWNLOAD_URL_CONTRACT__'] = '';
7690  }
7691  if (!empty($conf->global->SUPPLIER_PROPOSAL_ALLOW_EXTERNAL_DOWNLOAD) && is_object($object) && $object->element == 'supplier_proposal') {
7692  $substitutionarray['__DIRECTDOWNLOAD_URL_SUPPLIER_PROPOSAL__'] = $object->getLastMainDocLink($object->element);
7693  } else {
7694  $substitutionarray['__DIRECTDOWNLOAD_URL_SUPPLIER_PROPOSAL__'] = '';
7695  }
7696 
7697  if (is_object($object) && $object->element == 'propal') {
7698  $substitutionarray['__URL_PROPOSAL__'] = DOL_MAIN_URL_ROOT."/comm/propal/card.php?id=".$object->id;
7699  }
7700  if (is_object($object) && $object->element == 'commande') {
7701  $substitutionarray['__URL_ORDER__'] = DOL_MAIN_URL_ROOT."/commande/card.php?id=".$object->id;
7702  }
7703  if (is_object($object) && $object->element == 'facture') {
7704  $substitutionarray['__URL_INVOICE__'] = DOL_MAIN_URL_ROOT."/compta/facture/card.php?id=".$object->id;
7705  }
7706  if (is_object($object) && $object->element == 'contrat') {
7707  $substitutionarray['__URL_CONTRACT__'] = DOL_MAIN_URL_ROOT."/contrat/card.php?id=".$object->id;
7708  }
7709  if (is_object($object) && $object->element == 'supplier_proposal') {
7710  $substitutionarray['__URL_SUPPLIER_PROPOSAL__'] = DOL_MAIN_URL_ROOT."/supplier_proposal/card.php?id=".$object->id;
7711  }
7712  if (is_object($object) && $object->element == 'shipping') {
7713  $substitutionarray['__URL_SHIPMENT__'] = DOL_MAIN_URL_ROOT."/expedition/card.php?id=".$object->id;
7714  }
7715  }
7716 
7717  if (is_object($object) && $object->element == 'action') {
7718  $substitutionarray['__EVENT_LABEL__'] = $object->label;
7719  $substitutionarray['__EVENT_DATE__'] = dol_print_date($object->datep, '%A %d %b %Y');
7720  $substitutionarray['__EVENT_TIME__'] = dol_print_date($object->datep, '%H:%M:%S');
7721  }
7722  }
7723  }
7724  if (empty($exclude) || !in_array('objectamount', $exclude)) {
7725  include_once DOL_DOCUMENT_ROOT.'/core/lib/functionsnumtoword.lib.php';
7726 
7727  $substitutionarray['__DATE_YMD__'] = is_object($object) ? (isset($object->date) ? dol_print_date($object->date, 'day', 0, $outputlangs) : null) : '';
7728  $substitutionarray['__DATE_DUE_YMD__'] = is_object($object) ? (isset($object->date_lim_reglement) ? dol_print_date($object->date_lim_reglement, 'day', 0, $outputlangs) : null) : '';
7729 
7730  $already_payed_all = 0;
7731  if (is_object($object) && ($object instanceof Facture)) {
7732  $already_payed_all = $object->sumpayed + $object->sumdeposit + $object->sumcreditnote;
7733  }
7734 
7735  $substitutionarray['__AMOUNT_EXCL_TAX__'] = is_object($object) ? $object->total_ht : '';
7736 
7737  $substitutionarray['__AMOUNT__'] = is_object($object) ? $object->total_ttc : '';
7738  $substitutionarray['__AMOUNT_TEXT__'] = is_object($object) ? dol_convertToWord($object->total_ttc, $outputlangs, '', true) : '';
7739  $substitutionarray['__AMOUNT_TEXTCURRENCY__'] = is_object($object) ? dol_convertToWord($object->total_ttc, $outputlangs, $conf->currency, true) : '';
7740 
7741  $substitutionarray['__AMOUNT_REMAIN__'] = is_object($object) ? price2num($object->total_ttc - $already_payed_all, 'MT') : '';
7742 
7743  $substitutionarray['__AMOUNT_VAT__'] = is_object($object) ? (isset($object->total_vat) ? $object->total_vat : $object->total_tva) : '';
7744  $substitutionarray['__AMOUNT_VAT_TEXT__'] = is_object($object) ? (isset($object->total_vat) ? dol_convertToWord($object->total_vat, $outputlangs, '', true) : dol_convertToWord($object->total_tva, $outputlangs, '', true)) : '';
7745  $substitutionarray['__AMOUNT_VAT_TEXTCURRENCY__'] = is_object($object) ? (isset($object->total_vat) ? dol_convertToWord($object->total_vat, $outputlangs, $conf->currency, true) : dol_convertToWord($object->total_tva, $outputlangs, $conf->currency, true)) : '';
7746 
7747  if ($onlykey != 2 || $mysoc->useLocalTax(1)) {
7748  $substitutionarray['__AMOUNT_TAX2__'] = is_object($object) ? $object->total_localtax1 : '';
7749  }
7750  if ($onlykey != 2 || $mysoc->useLocalTax(2)) {
7751  $substitutionarray['__AMOUNT_TAX3__'] = is_object($object) ? $object->total_localtax2 : '';
7752  }
7753 
7754  // Amount keys formated in a currency
7755  $substitutionarray['__AMOUNT_EXCL_TAX_FORMATED__'] = is_object($object) ? ($object->total_ht ? price($object->total_ht, 0, $outputlangs, 0, -1, -1, $conf->currency) : null) : '';
7756  $substitutionarray['__AMOUNT_FORMATED__'] = is_object($object) ? ($object->total_ttc ? price($object->total_ttc, 0, $outputlangs, 0, -1, -1, $conf->currency) : null) : '';
7757  $substitutionarray['__AMOUNT_REMAIN_FORMATED__'] = is_object($object) ? ($object->total_ttc ? price($object->total_ttc - $already_payed_all, 0, $outputlangs, 0, -1, -1, $conf->currency) : null) : '';
7758  $substitutionarray['__AMOUNT_VAT_FORMATED__'] = is_object($object) ? (isset($object->total_vat) ? price($object->total_vat, 0, $outputlangs, 0, -1, -1, $conf->currency) : ($object->total_tva ? price($object->total_tva, 0, $outputlangs, 0, -1, -1, $conf->currency) : null)) : '';
7759  if ($onlykey != 2 || $mysoc->useLocalTax(1)) {
7760  $substitutionarray['__AMOUNT_TAX2_FORMATED__'] = is_object($object) ? ($object->total_localtax1 ? price($object->total_localtax1, 0, $outputlangs, 0, -1, -1, $conf->currency) : null) : '';
7761  }
7762  if ($onlykey != 2 || $mysoc->useLocalTax(2)) {
7763  $substitutionarray['__AMOUNT_TAX3_FORMATED__'] = is_object($object) ? ($object->total_localtax2 ? price($object->total_localtax2, 0, $outputlangs, 0, -1, -1, $conf->currency) : null) : '';
7764  }
7765 
7766  $substitutionarray['__AMOUNT_MULTICURRENCY__'] = (is_object($object) && isset($object->multicurrency_total_ttc)) ? $object->multicurrency_total_ttc : '';
7767  $substitutionarray['__AMOUNT_MULTICURRENCY_TEXT__'] = (is_object($object) && isset($object->multicurrency_total_ttc)) ? dol_convertToWord($object->multicurrency_total_ttc, $outputlangs, '', true) : '';
7768  $substitutionarray['__AMOUNT_MULTICURRENCY_TEXTCURRENCY__'] = (is_object($object) && isset($object->multicurrency_total_ttc)) ? dol_convertToWord($object->multicurrency_total_ttc, $outputlangs, $object->multicurrency_code, true) : '';
7769  // TODO Add other keys for foreign multicurrency
7770 
7771  // For backward compatibility
7772  if ($onlykey != 2) {
7773  $substitutionarray['__TOTAL_TTC__'] = is_object($object) ? $object->total_ttc : '';
7774  $substitutionarray['__TOTAL_HT__'] = is_object($object) ? $object->total_ht : '';
7775  $substitutionarray['__TOTAL_VAT__'] = is_object($object) ? (isset($object->total_vat) ? $object->total_vat : $object->total_tva) : '';
7776  }
7777  }
7778 
7779  //var_dump($substitutionarray['__AMOUNT_FORMATED__']);
7780  if (empty($exclude) || !in_array('date', $exclude)) {
7781  include_once DOL_DOCUMENT_ROOT.'/core/lib/date.lib.php';
7782 
7783  $tmp = dol_getdate(dol_now(), true);
7784  $tmp2 = dol_get_prev_day($tmp['mday'], $tmp['mon'], $tmp['year']);
7785  $tmp3 = dol_get_prev_month($tmp['mon'], $tmp['year']);
7786  $tmp4 = dol_get_next_day($tmp['mday'], $tmp['mon'], $tmp['year']);
7787  $tmp5 = dol_get_next_month($tmp['mon'], $tmp['year']);
7788 
7789  $daytext = $outputlangs->trans('Day'.$tmp['wday']);
7790 
7791  $substitutionarray = array_merge($substitutionarray, array(
7792  '__DAY__' => (string) $tmp['mday'],
7793  '__DAY_TEXT__' => $daytext, // Monday
7794  '__DAY_TEXT_SHORT__' => dol_trunc($daytext, 3, 'right', 'UTF-8', 1), // Mon
7795  '__DAY_TEXT_MIN__' => dol_trunc($daytext, 1, 'right', 'UTF-8', 1), // M
7796  '__MONTH__' => (string) $tmp['mon'],
7797  '__MONTH_TEXT__' => $outputlangs->trans('Month'.sprintf("%02d", $tmp['mon'])),
7798  '__MONTH_TEXT_SHORT__' => $outputlangs->trans('MonthShort'.sprintf("%02d", $tmp['mon'])),
7799  '__MONTH_TEXT_MIN__' => $outputlangs->trans('MonthVeryShort'.sprintf("%02d", $tmp['mon'])),
7800  '__YEAR__' => (string) $tmp['year'],
7801  '__PREVIOUS_DAY__' => (string) $tmp2['day'],
7802  '__PREVIOUS_MONTH__' => (string) $tmp3['month'],
7803  '__PREVIOUS_YEAR__' => (string) ($tmp['year'] - 1),
7804  '__NEXT_DAY__' => (string) $tmp4['day'],
7805  '__NEXT_MONTH__' => (string) $tmp5['month'],
7806  '__NEXT_YEAR__' => (string) ($tmp['year'] + 1),
7807  ));
7808  }
7809 
7810  if (!empty($conf->multicompany->enabled)) {
7811  $substitutionarray = array_merge($substitutionarray, array('__ENTITY_ID__' => $conf->entity));
7812  }
7813  if (empty($exclude) || !in_array('system', $exclude)) {
7814  $substitutionarray['__DOL_MAIN_URL_ROOT__'] = DOL_MAIN_URL_ROOT;
7815  $substitutionarray['__(AnyTranslationKey)__'] = $outputlangs->trans('TranslationOfKey');
7816  $substitutionarray['__(AnyTranslationKey|langfile)__'] = $outputlangs->trans('TranslationOfKey').' (load also language file before)';
7817  $substitutionarray['__[AnyConstantKey]__'] = $outputlangs->trans('ValueOfConstantKey');
7818  }
7819 
7820  return $substitutionarray;
7821 }
7822 
7839 function make_substitutions($text, $substitutionarray, $outputlangs = null, $converttextinhtmlifnecessary = 0)
7840 {
7841  global $conf, $langs;
7842 
7843  if (!is_array($substitutionarray)) {
7844  return 'ErrorBadParameterSubstitutionArrayWhenCalling_make_substitutions';
7845  }
7846 
7847  if (empty($outputlangs)) {
7848  $outputlangs = $langs;
7849  }
7850 
7851  // Is initial text HTML or simple text ?
7852  $msgishtml = 0;
7853  if (dol_textishtml($text, 1)) {
7854  $msgishtml = 1;
7855  }
7856 
7857  // Make substitution for language keys: __(AnyTranslationKey)__ or __(AnyTranslationKey|langfile)__
7858  if (is_object($outputlangs)) {
7859  $reg = array();
7860  while (preg_match('/__\(([^\)]+)\)__/', $text, $reg)) {
7861  // If key is __(TranslationKey|langfile)__, then force load of langfile.lang
7862  $tmp = explode('|', $reg[1]);
7863  if (!empty($tmp[1])) {
7864  $outputlangs->load($tmp[1]);
7865  }
7866 
7867  $value = $outputlangs->transnoentitiesnoconv($reg[1]);
7868 
7869  if (empty($converttextinhtmlifnecessary)) {
7870  // convert $newval into HTML is necessary
7871  $text = preg_replace('/__\('.preg_quote($reg[1], '/').'\)__/', $msgishtml ? dol_htmlentitiesbr($value) : $value, $text);
7872  } else {
7873  if (! $msgishtml) {
7874  $valueishtml = dol_textishtml($value, 1);
7875 
7876  if ($valueishtml) {
7877  $text = dol_htmlentitiesbr($text);
7878  $msgishtml = 1;
7879  }
7880  } else {
7881  $value = dol_nl2br("$value");
7882  }
7883 
7884  $text = preg_replace('/__\('.preg_quote($reg[1], '/').'\)__/', $value, $text);
7885  }
7886  }
7887  }
7888 
7889  // Make substitution for constant keys.
7890  // Must be after the substitution of translation, so if the text of translation contains a string __[xxx]__, it is also converted.
7891  $reg = array();
7892  while (preg_match('/__\[([^\]]+)\]__/', $text, $reg)) {
7893  $keyfound = $reg[1];
7894  if (isASecretKey($keyfound)) {
7895  $value = '*****forbidden*****';
7896  } else {
7897  $value = empty($conf->global->$keyfound) ? '' : $conf->global->$keyfound;
7898  }
7899 
7900  if (empty($converttextinhtmlifnecessary)) {
7901  // convert $newval into HTML is necessary
7902  $text = preg_replace('/__\['.preg_quote($keyfound, '/').'\]__/', $msgishtml ? dol_htmlentitiesbr($value) : $value, $text);
7903  } else {
7904  if (! $msgishtml) {
7905  $valueishtml = dol_textishtml($value, 1);
7906 
7907  if ($valueishtml) {
7908  $text = dol_htmlentitiesbr($text);
7909  $msgishtml = 1;
7910  }
7911  } else {
7912  $value = dol_nl2br("$value");
7913  }
7914 
7915  $text = preg_replace('/__\['.preg_quote($keyfound, '/').'\]__/', $value, $text);
7916  }
7917  }
7918 
7919  // Make substitition for array $substitutionarray
7920  foreach ($substitutionarray as $key => $value) {
7921  if (!isset($value)) {
7922  continue; // If value is null, it same than not having substitution key at all into array, we do not replace.
7923  }
7924 
7925  if ($key == '__USER_SIGNATURE__' && (!empty($conf->global->MAIN_MAIL_DO_NOT_USE_SIGN))) {
7926  $value = ''; // Protection
7927  }
7928 
7929  if (empty($converttextinhtmlifnecessary)) {
7930  $text = str_replace("$key", "$value", $text); // We must keep the " to work when value is 123.5 for example
7931  } else {
7932  if (! $msgishtml) {
7933  $valueishtml = dol_textishtml($value, 1);
7934 
7935  if ($valueishtml) {
7936  $text = dol_htmlentitiesbr($text);
7937  $msgishtml = 1;
7938  }
7939  } else {
7940  $value = dol_nl2br("$value");
7941  }
7942  $text = str_replace("$key", "$value", $text); // We must keep the " to work when value is 123.5 for example
7943  }
7944  }
7945 
7946  return $text;
7947 }
7948 
7961 function complete_substitutions_array(&$substitutionarray, $outputlangs, $object = null, $parameters = null, $callfunc = "completesubstitutionarray")
7962 {
7963  global $conf, $user;
7964 
7965  require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
7966 
7967  // Note: substitution key for each extrafields, using key __EXTRA_XXX__ is already available into the getCommonSubstitutionArray used to build the substitution array.
7968 
7969  // Check if there is external substitution to do, requested by plugins
7970  $dirsubstitutions = array_merge(array(), (array) $conf->modules_parts['substitutions']);
7971 
7972  foreach ($dirsubstitutions as $reldir) {
7973  $dir = dol_buildpath($reldir, 0);
7974 
7975  // Check if directory exists
7976  if (!dol_is_dir($dir)) {
7977  continue;
7978  }
7979 
7980  $substitfiles = dol_dir_list($dir, 'files', 0, 'functions_');
7981  foreach ($substitfiles as $substitfile) {
7982  $reg = array();
7983  if (preg_match('/functions_(.*)\.lib\.php/i', $substitfile['name'], $reg)) {
7984  $module = $reg[1];
7985 
7986  dol_syslog("Library ".$substitfile['name']." found into ".$dir);
7987  // Include the user's functions file
7988  require_once $dir.$substitfile['name'];
7989  // Call the user's function, and only if it is defined
7990  $function_name = $module."_".$callfunc;
7991  if (function_exists($function_name)) {
7992  $function_name($substitutionarray, $outputlangs, $object, $parameters);
7993  }
7994  }
7995  }
7996  }
7997  if (!empty($conf->global->ODT_ENABLE_ALL_TAGS_IN_SUBSTITUTIONS)) {
7998  // to list all tags in odt template
7999  $tags = '';
8000  foreach ($substitutionarray as $key => $value) {
8001  $tags .= '{'.$key.'} => '.$value."\n";
8002  }
8003  $substitutionarray = array_merge($substitutionarray, array('__ALL_TAGS__' => $tags));
8004  }
8005 }
8006 
8016 function print_date_range($date_start, $date_end, $format = '', $outputlangs = '')
8017 {
8018  print get_date_range($date_start, $date_end, $format, $outputlangs);
8019 }
8020 
8031 function get_date_range($date_start, $date_end, $format = '', $outputlangs = '', $withparenthesis = 1)
8032 {
8033  global $langs;
8034 
8035  $out = '';
8036 
8037  if (!is_object($outputlangs)) {
8038  $outputlangs = $langs;
8039  }
8040 
8041  if ($date_start && $date_end) {
8042  $out .= ($withparenthesis ? ' (' : '').$outputlangs->transnoentitiesnoconv('DateFromTo', dol_print_date($date_start, $format, false, $outputlangs), dol_print_date($date_end, $format, false, $outputlangs)).($withparenthesis ? ')' : '');
8043  }
8044  if ($date_start && !$date_end) {
8045  $out .= ($withparenthesis ? ' (' : '').$outputlangs->transnoentitiesnoconv('DateFrom', dol_print_date($date_start, $format, false, $outputlangs)).($withparenthesis ? ')' : '');
8046  }
8047  if (!$date_start && $date_end) {
8048  $out .= ($withparenthesis ? ' (' : '').$outputlangs->transnoentitiesnoconv('DateUntil', dol_print_date($date_end, $format, false, $outputlangs)).($withparenthesis ? ')' : '');
8049  }
8050 
8051  return $out;
8052 }
8053 
8062 function dolGetFirstLastname($firstname, $lastname, $nameorder = -1)
8063 {
8064  global $conf;
8065 
8066  $ret = '';
8067  // If order not defined, we use the setup
8068  if ($nameorder < 0) {
8069  $nameorder = (empty($conf->global->MAIN_FIRSTNAME_NAME_POSITION) ? 1 : 0);
8070  }
8071  if ($nameorder == 1) {
8072  $ret .= $firstname;
8073  if ($firstname && $lastname) {
8074  $ret .= ' ';
8075  }
8076  $ret .= $lastname;
8077  } elseif ($nameorder == 2 || $nameorder == 3) {
8078  $ret .= $firstname;
8079  if (empty($ret) && $nameorder == 3) {
8080  $ret .= $lastname;
8081  }
8082  } else { // 0, 4 or 5
8083  $ret .= $lastname;
8084  if (empty($ret) && $nameorder == 5) {
8085  $ret .= $firstname;
8086  }
8087  if ($nameorder == 0) {
8088  if ($firstname && $lastname) {
8089  $ret .= ' ';
8090  }
8091  $ret .= $firstname;
8092  }
8093  }
8094  return $ret;
8095 }
8096 
8097 
8108 function setEventMessage($mesgs, $style = 'mesgs')
8109 {
8110  //dol_syslog(__FUNCTION__ . " is deprecated", LOG_WARNING); This is not deprecated, it is used by setEventMessages function
8111  if (!is_array($mesgs)) {
8112  // If mesgs is a string
8113  if ($mesgs) {
8114  $_SESSION['dol_events'][$style][] = $mesgs;
8115  }
8116  } else {
8117  // If mesgs is an array
8118  foreach ($mesgs as $mesg) {
8119  if ($mesg) {
8120  $_SESSION['dol_events'][$style][] = $mesg;
8121  }
8122  }
8123  }
8124 }
8125 
8137 function setEventMessages($mesg, $mesgs, $style = 'mesgs', $messagekey = '')
8138 {
8139  if (empty($mesg) && empty($mesgs)) {
8140  dol_syslog("Try to add a message in stack with empty message", LOG_WARNING);
8141  } else {
8142  if ($messagekey) {
8143  // Complete message with a js link to set a cookie "DOLHIDEMESSAGE".$messagekey;
8144  // TODO
8145  $mesg .= '';
8146  }
8147  if (empty($messagekey) || empty($_COOKIE["DOLHIDEMESSAGE".$messagekey])) {
8148  if (!in_array((string) $style, array('mesgs', 'warnings', 'errors'))) {
8149  dol_print_error('', 'Bad parameter style='.$style.' for setEventMessages');
8150  }
8151  if (empty($mesgs)) {
8152  setEventMessage($mesg, $style);
8153  } else {
8154  if (!empty($mesg) && !in_array($mesg, $mesgs)) {
8155  setEventMessage($mesg, $style); // Add message string if not already into array
8156  }
8157  setEventMessage($mesgs, $style);
8158  }
8159  }
8160  }
8161 }
8162 
8172 function dol_htmloutput_events($disabledoutputofmessages = 0)
8173 {
8174  // Show mesgs
8175  if (isset($_SESSION['dol_events']['mesgs'])) {
8176  if (empty($disabledoutputofmessages)) {
8177  dol_htmloutput_mesg('', $_SESSION['dol_events']['mesgs']);
8178  }
8179  unset($_SESSION['dol_events']['mesgs']);
8180  }
8181  // Show errors
8182  if (isset($_SESSION['dol_events']['errors'])) {
8183  if (empty($disabledoutputofmessages)) {
8184  dol_htmloutput_mesg('', $_SESSION['dol_events']['errors'], 'error');
8185  }
8186  unset($_SESSION['dol_events']['errors']);
8187  }
8188 
8189  // Show warnings
8190  if (isset($_SESSION['dol_events']['warnings'])) {
8191  if (empty($disabledoutputofmessages)) {
8192  dol_htmloutput_mesg('', $_SESSION['dol_events']['warnings'], 'warning');
8193  }
8194  unset($_SESSION['dol_events']['warnings']);
8195  }
8196 }
8197 
8212 function get_htmloutput_mesg($mesgstring = '', $mesgarray = '', $style = 'ok', $keepembedded = 0)
8213 {
8214  global $conf, $langs;
8215 
8216  $ret = 0;
8217  $return = '';
8218  $out = '';
8219  $divstart = $divend = '';
8220 
8221  // If inline message with no format, we add it.
8222  if ((empty($conf->use_javascript_ajax) || !empty($conf->global->MAIN_DISABLE_JQUERY_JNOTIFY) || $keepembedded) && !preg_match('/<div class=".*">/i', $out)) {
8223  $divstart = '<div class="'.$style.' clearboth">';
8224  $divend = '</div>';
8225  }
8226 
8227  if ((is_array($mesgarray) && count($mesgarray)) || $mesgstring) {
8228  $langs->load("errors");
8229  $out .= $divstart;
8230  if (is_array($mesgarray) && count($mesgarray)) {
8231  foreach ($mesgarray as $message) {
8232  $ret++;
8233  $out .= $langs->trans($message);
8234  if ($ret < count($mesgarray)) {
8235  $out .= "<br>\n";
8236  }
8237  }
8238  }
8239  if ($mesgstring) {
8240  $ret++;
8241  $out .= $langs->trans($mesgstring);
8242  }
8243  $out .= $divend;
8244  }
8245 
8246  if ($out) {
8247  if (!empty($conf->use_javascript_ajax) && empty($conf->global->MAIN_DISABLE_JQUERY_JNOTIFY) && empty($keepembedded)) {
8248  $return = '<script>
8249  $(document).ready(function() {
8250  var block = '.(!empty($conf->global->MAIN_USE_JQUERY_BLOCKUI) ? "true" : "false").'
8251  if (block) {
8252  $.dolEventValid("","'.dol_escape_js($out).'");
8253  } else {
8254  /* jnotify(message, preset of message type, keepmessage) */
8255  $.jnotify("'.dol_escape_js($out).'",
8256  "'.($style == "ok" ? 3000 : $style).'",
8257  '.($style == "ok" ? "false" : "true").',
8258  { remove: function (){} } );
8259  }
8260  });
8261  </script>';
8262  } else {
8263  $return = $out;
8264  }
8265  }
8266 
8267  return $return;
8268 }
8269 
8281 function get_htmloutput_errors($mesgstring = '', $mesgarray = array(), $keepembedded = 0)
8282 {
8283  return get_htmloutput_mesg($mesgstring, $mesgarray, 'error', $keepembedded);
8284 }
8285 
8299 function dol_htmloutput_mesg($mesgstring = '', $mesgarray = array(), $style = 'ok', $keepembedded = 0)
8300 {
8301  if (empty($mesgstring) && (!is_array($mesgarray) || count($mesgarray) == 0)) {
8302  return;
8303  }
8304 
8305  $iserror = 0;
8306  $iswarning = 0;
8307  if (is_array($mesgarray)) {
8308  foreach ($mesgarray as $val) {
8309  if ($val && preg_match('/class="error"/i', $val)) {
8310  $iserror++;
8311  break;
8312  }
8313  if ($val && preg_match('/class="warning"/i', $val)) {
8314  $iswarning++;
8315  break;
8316  }
8317  }
8318  } elseif ($mesgstring && preg_match('/class="error"/i', $mesgstring)) {
8319  $iserror++;
8320  } elseif ($mesgstring && preg_match('/class="warning"/i', $mesgstring)) {
8321  $iswarning++;
8322  }
8323  if ($style == 'error') {
8324  $iserror++;
8325  }
8326  if ($style == 'warning') {
8327  $iswarning++;
8328  }
8329 
8330  if ($iserror || $iswarning) {
8331  // Remove div from texts
8332  $mesgstring = preg_replace('/<\/div><div class="(error|warning)">/', '<br>', $mesgstring);
8333  $mesgstring = preg_replace('/<div class="(error|warning)">/', '', $mesgstring);
8334  $mesgstring = preg_replace('/<\/div>/', '', $mesgstring);
8335  // Remove div from texts array
8336  if (is_array($mesgarray)) {
8337  $newmesgarray = array();
8338  foreach ($mesgarray as $val) {
8339  if (is_string($val)) {
8340  $tmpmesgstring = preg_replace('/<\/div><div class="(error|warning)">/', '<br>', $val);
8341  $tmpmesgstring = preg_replace('/<div class="(error|warning)">/', '', $tmpmesgstring);
8342  $tmpmesgstring = preg_replace('/<\/div>/', '', $tmpmesgstring);
8343  $newmesgarray[] = $tmpmesgstring;
8344  } else {
8345  dol_syslog("Error call of dol_htmloutput_mesg with an array with a value that is not a string", LOG_WARNING);
8346  }
8347  }
8348  $mesgarray = $newmesgarray;
8349  }
8350  print get_htmloutput_mesg($mesgstring, $mesgarray, ($iserror ? 'error' : 'warning'), $keepembedded);
8351  } else {
8352  print get_htmloutput_mesg($mesgstring, $mesgarray, 'ok', $keepembedded);
8353  }
8354 }
8355 
8367 function dol_htmloutput_errors($mesgstring = '', $mesgarray = array(), $keepembedded = 0)
8368 {
8369  dol_htmloutput_mesg($mesgstring, $mesgarray, 'error', $keepembedded);
8370 }
8371 
8385 function dol_sort_array(&$array, $index, $order = 'asc', $natsort = 0, $case_sensitive = 0, $keepindex = 0)
8386 {
8387  // Clean parameters
8388  $order = strtolower($order);
8389 
8390  if (is_array($array)) {
8391  $sizearray = count($array);
8392  if ($sizearray > 0) {
8393  $temp = array();
8394  foreach (array_keys($array) as $key) {
8395  if (is_object($array[$key])) {
8396  $temp[$key] = empty($array[$key]->$index) ? 0 : $array[$key]->$index;
8397  } else {
8398  $temp[$key] = empty($array[$key][$index]) ? 0 : $array[$key][$index];
8399  }
8400  }
8401 
8402  if (!$natsort) {
8403  if ($order == 'asc') {
8404  asort($temp);
8405  } else {
8406  arsort($temp);
8407  }
8408  } else {
8409  if ($case_sensitive) {
8410  natsort($temp);
8411  } else {
8412  natcasesort($temp); // natecasesort is not sensible to case
8413  }
8414  if ($order != 'asc') {
8415  $temp = array_reverse($temp, true);
8416  }
8417  }
8418 
8419  $sorted = array();
8420 
8421  foreach (array_keys($temp) as $key) {
8422  (is_numeric($key) && empty($keepindex)) ? $sorted[] = $array[$key] : $sorted[$key] = $array[$key];
8423  }
8424 
8425  return $sorted;
8426  }
8427  }
8428  return $array;
8429 }
8430 
8431 
8438 function utf8_check($str)
8439 {
8440  $str = (string) $str; // Sometimes string is an int.
8441 
8442  // We must use here a binary strlen function (so not dol_strlen)
8443  $strLength = dol_strlen($str);
8444  for ($i = 0; $i < $strLength; $i++) {
8445  if (ord($str[$i]) < 0x80) {
8446  continue; // 0bbbbbbb
8447  } elseif ((ord($str[$i]) & 0xE0) == 0xC0) {
8448  $n = 1; // 110bbbbb
8449  } elseif ((ord($str[$i]) & 0xF0) == 0xE0) {
8450  $n = 2; // 1110bbbb
8451  } elseif ((ord($str[$i]) & 0xF8) == 0xF0) {
8452  $n = 3; // 11110bbb
8453  } elseif ((ord($str[$i]) & 0xFC) == 0xF8) {
8454  $n = 4; // 111110bb
8455  } elseif ((ord($str[$i]) & 0xFE) == 0xFC) {
8456  $n = 5; // 1111110b
8457  } else {
8458  return false; // Does not match any model
8459  }
8460  for ($j = 0; $j < $n; $j++) { // n bytes matching 10bbbbbb follow ?
8461  if ((++$i == strlen($str)) || ((ord($str[$i]) & 0xC0) != 0x80)) {
8462  return false;
8463  }
8464  }
8465  }
8466  return true;
8467 }
8468 
8475 function ascii_check($str)
8476 {
8477  if (function_exists('mb_check_encoding')) {
8478  //if (mb_detect_encoding($str, 'ASCII', true) return false;
8479  if (!mb_check_encoding($str, 'ASCII')) {
8480  return false;
8481  }
8482  } else {
8483  if (preg_match('/[^\x00-\x7f]/', $str)) {
8484  return false; // Contains a byte > 7f
8485  }
8486  }
8487 
8488  return true;
8489 }
8490 
8491 
8499 function dol_osencode($str)
8500 {
8501  global $conf;
8502 
8503  $tmp = ini_get("unicode.filesystem_encoding"); // Disponible avec PHP 6.0
8504  if (empty($tmp) && !empty($_SERVER["WINDIR"])) {
8505  $tmp = 'iso-8859-1'; // By default for windows
8506  }
8507  if (empty($tmp)) {
8508  $tmp = 'utf-8'; // By default for other
8509  }
8510  if (!empty($conf->global->MAIN_FILESYSTEM_ENCODING)) {
8511  $tmp = $conf->global->MAIN_FILESYSTEM_ENCODING;
8512  }
8513 
8514  if ($tmp == 'iso-8859-1') {
8515  return utf8_decode($str);
8516  }
8517  return $str;
8518 }
8519 
8520 
8535 function dol_getIdFromCode($db, $key, $tablename, $fieldkey = 'code', $fieldid = 'id', $entityfilter = 0, $filters = '')
8536 {
8537  global $cache_codes;
8538 
8539  // If key empty
8540  if ($key == '') {
8541  return '';
8542  }
8543 
8544  // Check in cache
8545  if (isset($cache_codes[$tablename][$key][$fieldid])) { // Can be defined to 0 or ''
8546  return $cache_codes[$tablename][$key][$fieldid]; // Found in cache
8547  }
8548 
8549  dol_syslog('dol_getIdFromCode (value for field '.$fieldid.' from key '.$key.' not found into cache)', LOG_DEBUG);
8550 
8551  $sql = "SELECT ".$fieldid." as valuetoget";
8552  $sql .= " FROM ".MAIN_DB_PREFIX.$tablename;
8553  $sql .= " WHERE ".$fieldkey." = '".$db->escape($key)."'";
8554  if (!empty($entityfilter)) {
8555  $sql .= " AND entity IN (".getEntity($tablename).")";
8556  }
8557  if ($filters) {
8558  $sql .= $filters;
8559  }
8560 
8561  $resql = $db->query($sql);
8562  if ($resql) {
8563  $obj = $db->fetch_object($resql);
8564  if ($obj) {
8565  $cache_codes[$tablename][$key][$fieldid] = $obj->valuetoget;
8566  } else {
8567  $cache_codes[$tablename][$key][$fieldid] = '';
8568  }
8569  $db->free($resql);
8570  return $cache_codes[$tablename][$key][$fieldid];
8571  } else {
8572  return -1;
8573  }
8574 }
8575 
8582 function verifCond($strToEvaluate)
8583 {
8584  global $user, $conf, $langs;
8585  global $leftmenu;
8586  global $rights; // To export to dol_eval function
8587 
8588  //print $strToEvaluate."<br>\n";
8589  $rights = true;
8590  if (isset($strToEvaluate) && $strToEvaluate !== '') {
8591  //$str = 'if(!('.$strToEvaluate.')) $rights = false;';
8592  //dol_eval($str, 0, 1, '2'); // The dol_eval must contains all the global $xxx used into a condition
8593  //var_dump($strToEvaluate);
8594  $rep = dol_eval($strToEvaluate, 1, 1, '1'); // The dol_eval must contains all the global $xxx for all variables $xxx found into the string condition
8595  $rights = $rep && (!is_string($rep) || strpos($rep, 'Bad string syntax to evaluate') === false);
8596  //var_dump($rights);
8597  }
8598  return $rights;
8599 }
8600 
8611 function dol_eval($s, $returnvalue = 0, $hideerrors = 1, $onlysimplestring = '1')
8612 {
8613  // Only global variables can be changed by eval function and returned to caller
8614  global $db, $langs, $user, $conf, $website, $websitepage;
8615  global $action, $mainmenu, $leftmenu;
8616  global $mysoc;
8617  global $objectoffield;
8618 
8619  // Old variables used
8620  global $rights;
8621  global $object;
8622  global $obj; // To get $obj used into list when dol_eval is used for computed fields and $obj is not yet $object
8623  global $soc; // For backward compatibility
8624 
8625  // Test on dangerous char (used for RCE), we allow only characters to make PHP variable testing
8626  if ($onlysimplestring == '1') {
8627  // We must accept: '1 && getDolGlobalInt("doesnotexist1") && $conf->global->MAIN_FEATURES_LEVEL'
8628  // We must accept: '$conf->barcode->enabled && preg_match(\'/^(AAA|BBB)/\',$leftmenu)'
8629  // We must accept: '$user->rights->cabinetmed->read && $object->canvas=="patient@cabinetmed"'
8630  if (preg_match('/[^a-z0-9\s'.preg_quote('^$_+-.*>&|=!?():"\',/@', '/').']/i', $s)) {
8631  if ($returnvalue) {
8632  return 'Bad string syntax to evaluate (found chars that are not chars for simplestring): '.$s;
8633  } else {
8634  dol_syslog('Bad string syntax to evaluate (found chars that are not chars for simplestring): '.$s);
8635  return '';
8636  }
8637  // TODO We can exclude all () that is not '($db)' and 'getDolGlobalInt(' and 'getDolGlobalString(' and 'preg_match('
8638  // ...
8639  }
8640  } elseif ($onlysimplestring == '2') {
8641  // We must accept: (($reloadedobj = new Task($db)) && ($reloadedobj->fetchNoCompute($object->id) > 0) && ($secondloadedobj = new Project($db)) && ($secondloadedobj->fetchNoCompute($reloadedobj->fk_project) > 0)) ? $secondloadedobj->ref : "Parent project not found"
8642  if (preg_match('/[^a-z0-9\s'.preg_quote('^$_+-.*>&|=!?():"\',/@;[]', '/').']/i', $s)) {
8643  if ($returnvalue) {
8644  return 'Bad string syntax to evaluate (found chars that are not chars for simplestring): '.$s;
8645  } else {
8646  dol_syslog('Bad string syntax to evaluate (found chars that are not chars for simplestring): '.$s);
8647  return '';
8648  }
8649  }
8650  }
8651 
8652  if (is_array($s) || $s === 'Array') {
8653  return 'Bad string syntax to evaluate (value is Array) '.var_export($s, true);
8654  }
8655  if (strpos($s, '::') !== false) {
8656  if ($returnvalue) {
8657  return 'Bad string syntax to evaluate (double : char is forbidden): '.$s;
8658  } else {
8659  dol_syslog('Bad string syntax to evaluate (double : char is forbidden): '.$s);
8660  return '';
8661  }
8662  }
8663  if (strpos($s, '`') !== false) {
8664  if ($returnvalue) {
8665  return 'Bad string syntax to evaluate (backtick char is forbidden): '.$s;
8666  } else {
8667  dol_syslog('Bad string syntax to evaluate (backtick char is forbidden): '.$s);
8668  return '';
8669  }
8670  }
8671  if (preg_match('/[^0-9]+\.[^0-9]+/', $s)) { // We refuse . if not between 2 numbers
8672  if ($returnvalue) {
8673  return 'Bad string syntax to evaluate (dot char is forbidden): '.$s;
8674  } else {
8675  dol_syslog('Bad string syntax to evaluate (dot char is forbidden): '.$s);
8676  return '';
8677  }
8678  }
8679 
8680  // We block use of php exec or php file functions
8681  $forbiddenphpstrings = array('$$');
8682  $forbiddenphpstrings = array_merge($forbiddenphpstrings, array('_ENV', '_SESSION', '_COOKIE', '_GET', '_POST', '_REQUEST'));
8683 
8684  $forbiddenphpfunctions = array("exec", "passthru", "shell_exec", "system", "proc_open", "popen", "eval", "dol_eval", "executeCLI", "verifCond", "base64_decode");
8685  $forbiddenphpfunctions = array_merge($forbiddenphpfunctions, array("fopen", "file_put_contents", "fputs", "fputscsv", "fwrite", "fpassthru", "require", "include", "mkdir", "rmdir", "symlink", "touch", "unlink", "umask"));
8686  $forbiddenphpfunctions = array_merge($forbiddenphpfunctions, array("function", "call_user_func"));
8687 
8688  $forbiddenphpregex = 'global\s+\$|\b('.implode('|', $forbiddenphpfunctions).')\b';
8689 
8690  do {
8691  $oldstringtoclean = $s;
8692  $s = str_ireplace($forbiddenphpstrings, '__forbiddenstring__', $s);
8693  $s = preg_replace('/'.$forbiddenphpregex.'/i', '__forbiddenstring__', $s);
8694  //$s = preg_replace('/\$[a-zA-Z0-9_\->\$]+\(/i', '', $s); // Remove $function( call and $mycall->mymethod(
8695  } while ($oldstringtoclean != $s);
8696 
8697  if (strpos($s, '__forbiddenstring__') !== false) {
8698  dol_syslog('Bad string syntax to evaluate: '.$s, LOG_WARNING);
8699  if ($returnvalue) {
8700  return 'Bad string syntax to evaluate: '.$s;
8701  } else {
8702  dol_syslog('Bad string syntax to evaluate: '.$s);
8703  return '';
8704  }
8705  }
8706 
8707  //print $s."<br>\n";
8708  if ($returnvalue) {
8709  if ($hideerrors) {
8710  return @eval('return '.$s.';');
8711  } else {
8712  return eval('return '.$s.';');
8713  }
8714  } else {
8715  if ($hideerrors) {
8716  @eval($s);
8717  } else {
8718  eval($s);
8719  }
8720  }
8721 }
8722 
8729 function dol_validElement($element)
8730 {
8731  return (trim($element) != '');
8732 }
8733 
8742 function picto_from_langcode($codelang, $moreatt = '', $notitlealt = 0)
8743 {
8744  if (empty($codelang)) {
8745  return '';
8746  }
8747 
8748  if ($codelang == 'auto') {
8749  return '<span class="fa fa-language"></span>';
8750  }
8751 
8752  $langtocountryflag = array(
8753  'ar_AR' => '',
8754  'ca_ES' => 'catalonia',
8755  'da_DA' => 'dk',
8756  'fr_CA' => 'mq',
8757  'sv_SV' => 'se',
8758  'sw_SW' => 'unknown',
8759  'AQ' => 'unknown',
8760  'CW' => 'unknown',
8761  'IM' => 'unknown',
8762  'JE' => 'unknown',
8763  'MF' => 'unknown',
8764  'BL' => 'unknown',
8765  'SX' => 'unknown'
8766  );
8767 
8768  if (isset($langtocountryflag[$codelang])) {
8769  $flagImage = $langtocountryflag[$codelang];
8770  } else {
8771  $tmparray = explode('_', $codelang);
8772  $flagImage = empty($tmparray[1]) ? $tmparray[0] : $tmparray[1];
8773  }
8774 
8775  return img_picto_common($codelang, 'flags/'.strtolower($flagImage).'.png', $moreatt, 0, $notitlealt);
8776 }
8777 
8785 function getLanguageCodeFromCountryCode($countrycode)
8786 {
8787  global $mysoc;
8788 
8789  if (empty($countrycode)) {
8790  return null;
8791  }
8792 
8793  if (strtoupper($countrycode) == 'MQ') {
8794  return 'fr_CA';
8795  }
8796  if (strtoupper($countrycode) == 'SE') {
8797  return 'sv_SE'; // se_SE is Sami/Sweden, and we want in priority sv_SE for SE country
8798  }
8799  if (strtoupper($countrycode) == 'CH') {
8800  if ($mysoc->country_code == 'FR') {
8801  return 'fr_CH';
8802  }
8803  if ($mysoc->country_code == 'DE') {
8804  return 'de_CH';
8805  }
8806  if ($mysoc->country_code == 'IT') {
8807  return 'it_CH';
8808  }
8809  }
8810 
8811  // Locale list taken from:
8812  // http://stackoverflow.com/questions/3191664/
8813  // list-of-all-locales-and-their-short-codes
8814  $locales = array(
8815  'af-ZA',
8816  'am-ET',
8817  'ar-AE',
8818  'ar-BH',
8819  'ar-DZ',
8820  'ar-EG',
8821  'ar-IQ',
8822  'ar-JO',
8823  'ar-KW',
8824  'ar-LB',
8825  'ar-LY',
8826  'ar-MA',
8827  'ar-OM',
8828  'ar-QA',
8829  'ar-SA',
8830  'ar-SY',
8831  'ar-TN',
8832  'ar-YE',
8833  //'as-IN', // Moved after en-IN
8834  'ba-RU',
8835  'be-BY',
8836  'bg-BG',
8837  'bn-BD',
8838  //'bn-IN', // Moved after en-IN
8839  'bo-CN',
8840  'br-FR',
8841  'ca-ES',
8842  'co-FR',
8843  'cs-CZ',
8844  'cy-GB',
8845  'da-DK',
8846  'de-AT',
8847  'de-CH',
8848  'de-DE',
8849  'de-LI',
8850  'de-LU',
8851  'dv-MV',
8852  'el-GR',
8853  'en-AU',
8854  'en-BZ',
8855  'en-CA',
8856  'en-GB',
8857  'en-IE',
8858  'en-IN',
8859  'as-IN', // as-IN must be after en-IN (en in priority if country is IN)
8860  'bn-IN', // bn-IN must be after en-IN (en in priority if country is IN)
8861  'en-JM',
8862  'en-MY',
8863  'en-NZ',
8864  'en-PH',
8865  'en-SG',
8866  'en-TT',
8867  'en-US',
8868  'en-ZA',
8869  'en-ZW',
8870  'es-AR',
8871  'es-BO',
8872  'es-CL',
8873  'es-CO',
8874  'es-CR',
8875  'es-DO',
8876  'es-EC',
8877  'es-ES',
8878  'es-GT',
8879  'es-HN',
8880  'es-MX',
8881  'es-NI',
8882  'es-PA',
8883  'es-PE',
8884  'es-PR',
8885  'es-PY',
8886  'es-SV',
8887  'es-US',
8888  'es-UY',
8889  'es-VE',
8890  'et-EE',
8891  'eu-ES',
8892  'fa-IR',
8893  'fi-FI',
8894  'fo-FO',
8895  'fr-BE',
8896  'fr-CA',
8897  'fr-CH',
8898  'fr-FR',
8899  'fr-LU',
8900  'fr-MC',
8901  'fy-NL',
8902  'ga-IE',
8903  'gd-GB',
8904  'gl-ES',
8905  'gu-IN',
8906  'he-IL',
8907  'hi-IN',
8908  'hr-BA',
8909  'hr-HR',
8910  'hu-HU',
8911  'hy-AM',
8912  'id-ID',
8913  'ig-NG',
8914  'ii-CN',
8915  'is-IS',
8916  'it-CH',
8917  'it-IT',
8918  'ja-JP',
8919  'ka-GE',
8920  'kk-KZ',
8921  'kl-GL',
8922  'km-KH',
8923  'kn-IN',
8924  'ko-KR',
8925  'ky-KG',
8926  'lb-LU',
8927  'lo-LA',
8928  'lt-LT',
8929  'lv-LV',
8930  'mi-NZ',
8931  'mk-MK',
8932  'ml-IN',
8933  'mn-MN',
8934  'mr-IN',
8935  'ms-BN',
8936  'ms-MY',
8937  'mt-MT',
8938  'nb-NO',
8939  'ne-NP',
8940  'nl-BE',
8941  'nl-NL',
8942  'nn-NO',
8943  'oc-FR',
8944  'or-IN',
8945  'pa-IN',
8946  'pl-PL',
8947  'ps-AF',
8948  'pt-BR',
8949  'pt-PT',
8950  'rm-CH',
8951  'ro-MD',
8952  'ro-RO',
8953  'ru-RU',
8954  'rw-RW',
8955  'sa-IN',
8956  'se-FI',
8957  'se-NO',
8958  'se-SE',
8959  'si-LK',
8960  'sk-SK',
8961  'sl-SI',
8962  'sq-AL',
8963  'sv-FI',
8964  'sv-SE',
8965  'sw-KE',
8966  'ta-IN',
8967  'te-IN',
8968  'th-TH',
8969  'tk-TM',
8970  'tn-ZA',
8971  'tr-TR',
8972  'tt-RU',
8973  'ug-CN',
8974  'uk-UA',
8975  'ur-PK',
8976  'vi-VN',
8977  'wo-SN',
8978  'xh-ZA',
8979  'yo-NG',
8980  'zh-CN',
8981  'zh-HK',
8982  'zh-MO',
8983  'zh-SG',
8984  'zh-TW',
8985  'zu-ZA',
8986  );
8987 
8988  $buildprimarykeytotest = strtolower($countrycode).'-'.strtoupper($countrycode);
8989  if (in_array($buildprimarykeytotest, $locales)) {
8990  return strtolower($countrycode).'_'.strtoupper($countrycode);
8991  }
8992 
8993  if (function_exists('locale_get_primary_language') && function_exists('locale_get_region')) { // Need extension php-intl
8994  foreach ($locales as $locale) {
8995  $locale_language = locale_get_primary_language($locale);
8996  $locale_region = locale_get_region($locale);
8997  if (strtoupper($countrycode) == $locale_region) {
8998  //var_dump($locale.' - '.$locale_language.' - '.$locale_region);
8999  return strtolower($locale_language).'_'.strtoupper($locale_region);
9000  }
9001  }
9002  } else {
9003  dol_syslog("Warning Exention php-intl is not available", LOG_WARNING);
9004  }
9005 
9006  return null;
9007 }
9008 
9038 function complete_head_from_modules($conf, $langs, $object, &$head, &$h, $type, $mode = 'add')
9039 {
9040  global $hookmanager, $db;
9041 
9042  if (isset($conf->modules_parts['tabs'][$type]) && is_array($conf->modules_parts['tabs'][$type])) {
9043  foreach ($conf->modules_parts['tabs'][$type] as $value) {
9044  $values = explode(':', $value);
9045 
9046  $reg = array();
9047  if ($mode == 'add' && !preg_match('/^\-/', $values[1])) {
9048  if (count($values) == 6) {
9049  // new declaration with permissions:
9050  // $value='objecttype:+tabname1:Title1:langfile@mymodule:$user->rights->mymodule->read:/mymodule/mynewtab1.php?id=__ID__'
9051  // $value='objecttype:+tabname1:Title1,class,pathfile,method:langfile@mymodule:$user->rights->mymodule->read:/mymodule/mynewtab1.php?id=__ID__'
9052  if ($values[0] != $type) {
9053  continue;
9054  }
9055  //var_dump(verifCond($values[4]));
9056 
9057  if (verifCond($values[4])) {
9058  if ($values[3]) {
9059  $langs->load($values[3]);
9060  }
9061  if (preg_match('/SUBSTITUTION_([^_]+)/i', $values[2], $reg)) {
9062  $substitutionarray = array();
9063  complete_substitutions_array($substitutionarray, $langs, $object, array('needforkey'=>$values[2]));
9064  $label = make_substitutions($reg[1], $substitutionarray);
9065  } else {
9066  $labeltemp = explode(',', $values[2]);
9067  $label = $langs->trans($labeltemp[0]);
9068  if (!empty($labeltemp[1]) && is_object($object) && !empty($object->id)) {
9069  dol_include_once($labeltemp[2]);
9070  $classtoload = $labeltemp[1];
9071  if (class_exists($classtoload)) {
9072  $obj = new $classtoload($db);
9073  $function = $labeltemp[3];
9074  if ($obj && $function && method_exists($obj, $function)) {
9075  $nbrec = $obj->$function($object->id, $obj);
9076  $label .= '<span class="badge marginleftonlyshort">'.$nbrec.'</span>';
9077  }
9078  }
9079  }
9080  }
9081 
9082  $head[$h][0] = dol_buildpath(preg_replace('/__ID__/i', ((is_object($object) && !empty($object->id)) ? $object->id : ''), $values[5]), 1);
9083  $head[$h][1] = $label;
9084  $head[$h][2] = str_replace('+', '', $values[1]);
9085  $h++;
9086  }
9087  } elseif (count($values) == 5) { // deprecated
9088  dol_syslog('Passing 5 values in tabs module_parts is deprecated. Please update to 6 with permissions.', LOG_WARNING);
9089 
9090  if ($values[0] != $type) {
9091  continue;
9092  }
9093  if ($values[3]) {
9094  $langs->load($values[3]);
9095  }
9096  if (preg_match('/SUBSTITUTION_([^_]+)/i', $values[2], $reg)) {
9097  $substitutionarray = array();
9098  complete_substitutions_array($substitutionarray, $langs, $object, array('needforkey'=>$values[2]));
9099  $label = make_substitutions($reg[1], $substitutionarray);
9100  } else {
9101  $label = $langs->trans($values[2]);
9102  }
9103 
9104  $head[$h][0] = dol_buildpath(preg_replace('/__ID__/i', ((is_object($object) && !empty($object->id)) ? $object->id : ''), $values[4]), 1);
9105  $head[$h][1] = $label;
9106  $head[$h][2] = str_replace('+', '', $values[1]);
9107  $h++;
9108  }
9109  } elseif ($mode == 'remove' && preg_match('/^\-/', $values[1])) {
9110  if ($values[0] != $type) {
9111  continue;
9112  }
9113  $tabname = str_replace('-', '', $values[1]);
9114  foreach ($head as $key => $val) {
9115  $condition = (!empty($values[3]) ? verifCond($values[3]) : 1);
9116  //var_dump($key.' - '.$tabname.' - '.$head[$key][2].' - '.$values[3].' - '.$condition);
9117  if ($head[$key][2] == $tabname && $condition) {
9118  unset($head[$key]);
9119  break;
9120  }
9121  }
9122  }
9123  }
9124  }
9125 
9126  // No need to make a return $head. Var is modified as a reference
9127  if (!empty($hookmanager)) {
9128  $parameters = array('object' => $object, 'mode' => $mode, 'head' => &$head);
9129  $reshook = $hookmanager->executeHooks('completeTabsHead', $parameters);
9130  if ($reshook > 0) { // Hook ask to replace completely the array
9131  $head = $hookmanager->resArray;
9132  } else { // Hook
9133  $head = array_merge($head, $hookmanager->resArray);
9134  }
9135  $h = count($head);
9136  }
9137 }
9138 
9150 function printCommonFooter($zone = 'private')
9151 {
9152  global $conf, $hookmanager, $user, $debugbar;
9153  global $action;
9154  global $micro_start_time;
9155 
9156  if ($zone == 'private') {
9157  print "\n".'<!-- Common footer for private page -->'."\n";
9158  } else {
9159  print "\n".'<!-- Common footer for public page -->'."\n";
9160  }
9161 
9162  // A div to store page_y POST parameter so we can read it using javascript
9163  print "\n<!-- A div to store page_y POST parameter -->\n";
9164  print '<div id="page_y" style="display: none;">'.(GETPOST('page_y') ? GETPOST('page_y') : '').'</div>'."\n";
9165 
9166  $parameters = array();
9167  $reshook = $hookmanager->executeHooks('printCommonFooter', $parameters); // Note that $action and $object may have been modified by some hooks
9168  if (empty($reshook)) {
9169  if (!empty($conf->global->MAIN_HTML_FOOTER)) {
9170  print $conf->global->MAIN_HTML_FOOTER."\n";
9171  }
9172 
9173  print "\n";
9174  if (!empty($conf->use_javascript_ajax)) {
9175  print '<script>'."\n";
9176  print 'jQuery(document).ready(function() {'."\n";
9177 
9178  if ($zone == 'private' && empty($conf->dol_use_jmobile)) {
9179  print "\n";
9180  print '/* JS CODE TO ENABLE to manage handler to switch left menu page (menuhider) */'."\n";
9181  print 'jQuery("li.menuhider").click(function(event) {';
9182  print ' if (!$( "body" ).hasClass( "sidebar-collapse" )){ event.preventDefault(); }'."\n";
9183  print ' console.log("We click on .menuhider");'."\n";
9184  print ' $("body").toggleClass("sidebar-collapse")'."\n";
9185  print '});'."\n";
9186  }
9187 
9188  // Management of focus and mandatory for fields
9189  if ($action == 'create' || $action == 'edit' || (empty($action) && (preg_match('/new\.php/', $_SERVER["PHP_SELF"])))) {
9190  print '/* JS CODE TO ENABLE to manage focus and mandatory form fields */'."\n";
9191  $relativepathstring = $_SERVER["PHP_SELF"];
9192  // Clean $relativepathstring
9193  if (constant('DOL_URL_ROOT')) {
9194  $relativepathstring = preg_replace('/^'.preg_quote(constant('DOL_URL_ROOT'), '/').'/', '', $relativepathstring);
9195  }
9196  $relativepathstring = preg_replace('/^\//', '', $relativepathstring);
9197  $relativepathstring = preg_replace('/^custom\//', '', $relativepathstring);
9198  //$tmpqueryarraywehave = explode('&', dol_string_nohtmltag($_SERVER['QUERY_STRING']));
9199  if (!empty($user->default_values[$relativepathstring]['focus'])) {
9200  foreach ($user->default_values[$relativepathstring]['focus'] as $defkey => $defval) {
9201  $qualified = 0;
9202  if ($defkey != '_noquery_') {
9203  $tmpqueryarraytohave = explode('&', $defkey);
9204  $foundintru = 0;
9205  foreach ($tmpqueryarraytohave as $tmpquerytohave) {
9206  $tmpquerytohaveparam = explode('=', $tmpquerytohave);
9207  //print "console.log('".$tmpquerytohaveparam[0]." ".$tmpquerytohaveparam[1]." ".GETPOST($tmpquerytohaveparam[0])."');";
9208  if (!GETPOSTISSET($tmpquerytohaveparam[0]) || ($tmpquerytohaveparam[1] != GETPOST($tmpquerytohaveparam[0]))) {
9209  $foundintru = 1;
9210  }
9211  }
9212  if (!$foundintru) {
9213  $qualified = 1;
9214  }
9215  //var_dump($defkey.'-'.$qualified);
9216  } else {
9217  $qualified = 1;
9218  }
9219 
9220  if ($qualified) {
9221  foreach ($defval as $paramkey => $paramval) {
9222  // Set focus on field
9223  print 'jQuery("input[name=\''.$paramkey.'\']").focus();'."\n";
9224  print 'jQuery("textarea[name=\''.$paramkey.'\']").focus();'."\n";
9225  print 'jQuery("select[name=\''.$paramkey.'\']").focus();'."\n"; // Not really usefull, but we keep it in case of.
9226  }
9227  }
9228  }
9229  }
9230  if (!empty($user->default_values[$relativepathstring]['mandatory'])) {
9231  foreach ($user->default_values[$relativepathstring]['mandatory'] as $defkey => $defval) {
9232  $qualified = 0;
9233  if ($defkey != '_noquery_') {
9234  $tmpqueryarraytohave = explode('&', $defkey);
9235  $foundintru = 0;
9236  foreach ($tmpqueryarraytohave as $tmpquerytohave) {
9237  $tmpquerytohaveparam = explode('=', $tmpquerytohave);
9238  //print "console.log('".$tmpquerytohaveparam[0]." ".$tmpquerytohaveparam[1]." ".GETPOST($tmpquerytohaveparam[0])."');";
9239  if (!GETPOSTISSET($tmpquerytohaveparam[0]) || ($tmpquerytohaveparam[1] != GETPOST($tmpquerytohaveparam[0]))) {
9240  $foundintru = 1;
9241  }
9242  }
9243  if (!$foundintru) {
9244  $qualified = 1;
9245  }
9246  //var_dump($defkey.'-'.$qualified);
9247  } else {
9248  $qualified = 1;
9249  }
9250 
9251  if ($qualified) {
9252  foreach ($defval as $paramkey => $paramval) {
9253  // Add property 'required' on input
9254  print 'jQuery("input[name=\''.$paramkey.'\']").prop(\'required\',true);'."\n";
9255  print 'jQuery("textarea[name=\''.$paramkey.'\']").prop(\'required\',true);'."\n";
9256  print '// required on a select works only if key is "", so we add the required attributes but also we reset the key -1 or 0 to an empty string'."\n";
9257  print 'jQuery("select[name=\''.$paramkey.'\']").prop(\'required\',true);'."\n";
9258  print 'jQuery("select[name=\''.$paramkey.'\'] option[value=\'-1\']").prop(\'value\', \'\');'."\n";
9259  print 'jQuery("select[name=\''.$paramkey.'\'] option[value=\'0\']").prop(\'value\', \'\');'."\n";
9260 
9261  // Add 'field required' class on closest td for all input elements : input, textarea and select
9262  print 'jQuery(":input[name=\'' . $paramkey . '\']").closest("tr").find("td:first").addClass("fieldrequired");' . "\n";
9263  }
9264  }
9265  }
9266  }
9267  }
9268 
9269  print '});'."\n";
9270 
9271  // End of tuning
9272  if (!empty($_SERVER['MAIN_SHOW_TUNING_INFO']) || !empty($conf->global->MAIN_SHOW_TUNING_INFO)) {
9273  print "\n";
9274  print "/* JS CODE TO ENABLE to add memory info */\n";
9275  print 'window.console && console.log("';
9276  if (!empty($conf->global->MEMCACHED_SERVER)) {
9277  print 'MEMCACHED_SERVER='.$conf->global->MEMCACHED_SERVER.' - ';
9278  }
9279  print 'MAIN_OPTIMIZE_SPEED='.(isset($conf->global->MAIN_OPTIMIZE_SPEED) ? $conf->global->MAIN_OPTIMIZE_SPEED : 'off');
9280  if (!empty($micro_start_time)) { // Works only if MAIN_SHOW_TUNING_INFO is defined at $_SERVER level. Not in global variable.
9281  $micro_end_time = microtime(true);
9282  print ' - Build time: '.ceil(1000 * ($micro_end_time - $micro_start_time)).' ms';
9283  }
9284 
9285  if (function_exists("memory_get_usage")) {
9286  print ' - Mem: '.memory_get_usage(); // Do not use true here, it seems it takes the peak amount
9287  }
9288  if (function_exists("memory_get_peak_usage")) {
9289  print ' - Real mem peak: '.memory_get_peak_usage(true);
9290  }
9291  if (function_exists("zend_loader_file_encoded")) {
9292  print ' - Zend encoded file: '.(zend_loader_file_encoded() ? 'yes' : 'no');
9293  }
9294  print '");'."\n";
9295  }
9296 
9297  print "\n".'</script>'."\n";
9298 
9299  // Google Analytics
9300  // TODO Add a hook here
9301  if (!empty($conf->google->enabled) && !empty($conf->global->MAIN_GOOGLE_AN_ID)) {
9302  $tmptagarray = explode(',', $conf->global->MAIN_GOOGLE_AN_ID);
9303  foreach ($tmptagarray as $tmptag) {
9304  print "\n";
9305  print "<!-- JS CODE TO ENABLE for google analtics tag -->\n";
9306  print "
9307  <!-- Global site tag (gtag.js) - Google Analytics -->
9308  <script async src=\"https://www.googletagmanager.com/gtag/js?id=".trim($tmptag)."\"></script>
9309  <script>
9310  window.dataLayer = window.dataLayer || [];
9311  function gtag(){dataLayer.push(arguments);}
9312  gtag('js', new Date());
9313 
9314  gtag('config', '".trim($tmptag)."');
9315  </script>";
9316  print "\n";
9317  }
9318  }
9319  }
9320 
9321  // Add Xdebug coverage of code
9322  if (defined('XDEBUGCOVERAGE')) {
9323  print_r(xdebug_get_code_coverage());
9324  }
9325 
9326  // Add DebugBar data
9327  if (!empty($user->rights->debugbar->read) && is_object($debugbar)) {
9328  $debugbar['time']->stopMeasure('pageaftermaster');
9329  print '<!-- Output debugbar data -->'."\n";
9330  $renderer = $debugbar->getRenderer();
9331  print $debugbar->getRenderer()->render();
9332  } elseif (count($conf->logbuffer)) { // If there is some logs in buffer to show
9333  print "\n";
9334  print "<!-- Start of log output\n";
9335  //print '<div class="hidden">'."\n";
9336  foreach ($conf->logbuffer as $logline) {
9337  print $logline."<br>\n";
9338  }
9339  //print '</div>'."\n";
9340  print "End of log output -->\n";
9341  }
9342  }
9343 }
9344 
9354 function dolExplodeIntoArray($string, $delimiter = ';', $kv = '=')
9355 {
9356  if ($a = explode($delimiter, $string)) {
9357  $ka = array();
9358  foreach ($a as $s) { // each part
9359  if ($s) {
9360  if ($pos = strpos($s, $kv)) { // key/value delimiter
9361  $ka[trim(substr($s, 0, $pos))] = trim(substr($s, $pos + strlen($kv)));
9362  } else { // key delimiter not found
9363  $ka[] = trim($s);
9364  }
9365  }
9366  }
9367  return $ka;
9368  }
9369  return array();
9370 }
9371 
9372 
9379 function dol_set_focus($selector)
9380 {
9381  print "\n".'<!-- Set focus onto a specific field -->'."\n";
9382  print '<script>jQuery(document).ready(function() { jQuery("'.dol_escape_js($selector).'").focus(); });</script>'."\n";
9383 }
9384 
9385 
9393 function dol_getmypid()
9394 {
9395  if (!function_exists('getmypid')) {
9396  return mt_rand(1, 32768);
9397  } else {
9398  return getmypid();
9399  }
9400 }
9401 
9402 
9420 function natural_search($fields, $value, $mode = 0, $nofirstand = 0)
9421 {
9422  global $db, $langs;
9423 
9424  $value = trim($value);
9425 
9426  if ($mode == 0) {
9427  $value = preg_replace('/\*/', '%', $value); // Replace * with %
9428  }
9429  if ($mode == 1) {
9430  $value = preg_replace('/([<>=]+)\s+([0-9'.preg_quote($langs->trans("DecimalSeparator"), '/').'\-])/', '\1\2', $value); // Clean string '< 10' into '<10' so we can the explode on space to get all tests to do
9431  }
9432 
9433  $value = preg_replace('/\s*\|\s*/', '|', $value);
9434 
9435  $crits = explode(' ', $value);
9436  $res = '';
9437  if (!is_array($fields)) {
9438  $fields = array($fields);
9439  }
9440 
9441  $j = 0;
9442  foreach ($crits as $crit) {
9443  $crit = trim($crit);
9444  $i = 0;
9445  $i2 = 0;
9446  $newres = '';
9447  foreach ($fields as $field) {
9448  if ($mode == 1) {
9449  $operator = '=';
9450  $newcrit = preg_replace('/([<>=]+)/', '', $crit);
9451 
9452  $reg = array();
9453  preg_match('/([<>=]+)/', $crit, $reg);
9454  if (!empty($reg[1])) {
9455  $operator = $reg[1];
9456  }
9457  if ($newcrit != '') {
9458  $numnewcrit = price2num($newcrit);
9459  if (is_numeric($numnewcrit)) {
9460  $newres .= ($i2 > 0 ? ' OR ' : '').$field.' '.$operator.' '.((float) $numnewcrit); // should be a numeric
9461  } else {
9462  $newres .= ($i2 > 0 ? ' OR ' : '').'1 = 2'; // force false
9463  }
9464  $i2++; // a criteria was added to string
9465  }
9466  } elseif ($mode == 2 || $mode == -2) {
9467  $crit = preg_replace('/[^0-9,]/', '', $crit); // ID are always integer
9468  $newres .= ($i2 > 0 ? ' OR ' : '').$field." ".($mode == -2 ? 'NOT ' : '');
9469  $newres .= $crit ? "IN (".$db->sanitize($db->escape($crit)).")" : "IN (0)";
9470  if ($mode == -2) {
9471  $newres .= ' OR '.$field.' IS NULL';
9472  }
9473  $i2++; // a criteria was added to string
9474  } elseif ($mode == 3 || $mode == -3) {
9475  $tmparray = explode(',', $crit);
9476  if (count($tmparray)) {
9477  $listofcodes = '';
9478  foreach ($tmparray as $val) {
9479  $val = trim($val);
9480  if ($val) {
9481  $listofcodes .= ($listofcodes ? ',' : '');
9482  $listofcodes .= "'".$db->escape($val)."'";
9483  }
9484  }
9485  $newres .= ($i2 > 0 ? ' OR ' : '').$field." ".($mode == -3 ? 'NOT ' : '')."IN (".$db->sanitize($listofcodes, 1).")";
9486  $i2++; // a criteria was added to string
9487  }
9488  if ($mode == -3) {
9489  $newres .= ' OR '.$field.' IS NULL';
9490  }
9491  } elseif ($mode == 4) {
9492  $tmparray = explode(',', $crit);
9493  if (count($tmparray)) {
9494  $listofcodes = '';
9495  foreach ($tmparray as $val) {
9496  $val = trim($val);
9497  if ($val) {
9498  $newres .= ($i2 > 0 ? " OR (" : "(").$field." LIKE '".$db->escape($val).",%'";
9499  $newres .= ' OR '.$field." = '".$db->escape($val)."'";
9500  $newres .= ' OR '.$field." LIKE '%,".$db->escape($val)."'";
9501  $newres .= ' OR '.$field." LIKE '%,".$db->escape($val).",%'";
9502  $newres .= ')';
9503  $i2++;
9504  }
9505  }
9506  }
9507  } else // $mode=0
9508  {
9509  $tmpcrits = explode('|', $crit);
9510  $i3 = 0;
9511  foreach ($tmpcrits as $tmpcrit) {
9512  if ($tmpcrit !== '0' && empty($tmpcrit)) {
9513  continue;
9514  }
9515 
9516  $newres .= (($i2 > 0 || $i3 > 0) ? ' OR ' : '');
9517 
9518  if (preg_match('/\.(id|rowid)$/', $field)) { // Special case for rowid that is sometimes a ref so used as a search field
9519  $newres .= $field." = ".(is_numeric(trim($tmpcrit)) ? ((float) trim($tmpcrit)) : '0');
9520  } else {
9521  $tmpcrit = trim($tmpcrit);
9522  $tmpcrit2 = $tmpcrit;
9523  $tmpbefore = '%';
9524  $tmpafter = '%';
9525  if (preg_match('/^!/', $tmpcrit)) {
9526  $newres .= $field." NOT LIKE '"; // ! as exclude character
9527  $tmpcrit2 = preg_replace('/^!/', '', $tmpcrit2);
9528  } else $newres .= $field." LIKE '";
9529 
9530  if (preg_match('/^[\^\$]/', $tmpcrit)) {
9531  $tmpbefore = '';
9532  $tmpcrit2 = preg_replace('/^[\^\$]/', '', $tmpcrit2);
9533  }
9534  if (preg_match('/[\^\$]$/', $tmpcrit)) {
9535  $tmpafter = '';
9536  $tmpcrit2 = preg_replace('/[\^\$]$/', '', $tmpcrit2);
9537  }
9538  $newres .= $tmpbefore;
9539  $newres .= $db->escape($tmpcrit2);
9540  $newres .= $tmpafter;
9541  $newres .= "'";
9542  if ($tmpcrit2 == '') {
9543  $newres .= " OR ".$field." IS NULL";
9544  }
9545  }
9546 
9547  $i3++;
9548  }
9549  $i2++; // a criteria was added to string
9550  }
9551  $i++;
9552  }
9553  if ($newres) {
9554  $res = $res.($res ? ' AND ' : '').($i2 > 1 ? '(' : '').$newres.($i2 > 1 ? ')' : '');
9555  }
9556  $j++;
9557  }
9558  $res = ($nofirstand ? "" : " AND ")."(".$res.")";
9559  //print 'xx'.$res.'yy';
9560  return $res;
9561 }
9562 
9569 function showDirectDownloadLink($object)
9570 {
9571  global $conf, $langs;
9572 
9573  $out = '';
9574  $url = $object->getLastMainDocLink($object->element);
9575 
9576  $out .= img_picto($langs->trans("PublicDownloadLinkDesc"), 'globe').' <span class="opacitymedium">'.$langs->trans("DirectDownloadLink").'</span><br>';
9577  if ($url) {
9578  $out .= '<div class="urllink"><input type="text" id="directdownloadlink" class="quatrevingtpercent" value="'.$url.'"></div>';
9579  $out .= ajax_autoselect("directdownloadlink", 0);
9580  } else {
9581  $out .= '<div class="urllink">'.$langs->trans("FileNotShared").'</div>';
9582  }
9583 
9584  return $out;
9585 }
9586 
9595 function getImageFileNameForSize($file, $extName, $extImgTarget = '')
9596 {
9597  $dirName = dirname($file);
9598  if ($dirName == '.') {
9599  $dirName = '';
9600  }
9601 
9602  $fileName = preg_replace('/(\.gif|\.jpeg|\.jpg|\.png|\.bmp|\.webp)$/i', '', $file); // We remove extension, whatever is its case
9603  $fileName = basename($fileName);
9604 
9605  if (empty($extImgTarget)) {
9606  $extImgTarget = (preg_match('/\.jpg$/i', $file) ? '.jpg' : '');
9607  }
9608  if (empty($extImgTarget)) {
9609  $extImgTarget = (preg_match('/\.jpeg$/i', $file) ? '.jpeg' : '');
9610  }
9611  if (empty($extImgTarget)) {
9612  $extImgTarget = (preg_match('/\.gif$/i', $file) ? '.gif' : '');
9613  }
9614  if (empty($extImgTarget)) {
9615  $extImgTarget = (preg_match('/\.png$/i', $file) ? '.png' : '');
9616  }
9617  if (empty($extImgTarget)) {
9618  $extImgTarget = (preg_match('/\.bmp$/i', $file) ? '.bmp' : '');
9619  }
9620  if (empty($extImgTarget)) {
9621  $extImgTarget = (preg_match('/\.webp$/i', $file) ? '.webp' : '');
9622  }
9623 
9624  if (!$extImgTarget) {
9625  return $file;
9626  }
9627 
9628  $subdir = '';
9629  if ($extName) {
9630  $subdir = 'thumbs/';
9631  }
9632 
9633  return ($dirName ? $dirName.'/' : '').$subdir.$fileName.$extName.$extImgTarget; // New filename for thumb
9634 }
9635 
9636 
9646 function getAdvancedPreviewUrl($modulepart, $relativepath, $alldata = 0, $param = '')
9647 {
9648  global $conf, $langs;
9649 
9650  if (empty($conf->use_javascript_ajax)) {
9651  return '';
9652  }
9653 
9654  $isAllowedForPreview = dolIsAllowedForPreview($relativepath);
9655 
9656  if ($alldata == 1) {
9657  if ($isAllowedForPreview) {
9658  return array('target'=>'_blank', 'css'=>'documentpreview', 'url'=>DOL_URL_ROOT.'/document.php?modulepart='.$modulepart.'&attachment=0&file='.urlencode($relativepath).($param ? '&'.$param : ''), 'mime'=>dol_mimetype($relativepath));
9659  } else {
9660  return array();
9661  }
9662  }
9663 
9664  // old behavior, return a string
9665  if ($isAllowedForPreview) {
9666  return 'javascript:document_preview(\''.dol_escape_js(DOL_URL_ROOT.'/document.php?modulepart='.$modulepart.'&attachment=0&file='.urlencode($relativepath).($param ? '&'.$param : '')).'\', \''.dol_mimetype($relativepath).'\', \''.dol_escape_js($langs->trans('Preview')).'\')';
9667  } else {
9668  return '';
9669  }
9670 }
9671 
9672 
9681 function ajax_autoselect($htmlname, $addlink = '', $textonlink = 'Link')
9682 {
9683  global $langs;
9684  $out = '<script>
9685  jQuery(document).ready(function () {
9686  jQuery("'.((strpos($htmlname, '.') === 0 ? '' : '#').$htmlname).'").click(function() { jQuery(this).select(); } );
9687  });
9688  </script>';
9689  if ($addlink) {
9690  if ($textonlink === 'image') {
9691  $out .= ' <a href="'.$addlink.'" target="_blank" rel="noopener noreferrer">'.img_picto('', 'globe').'</a>';
9692  } else {
9693  $out .= ' <a href="'.$addlink.'" target="_blank" rel="noopener noreferrer">'.$langs->trans("Link").'</a>';
9694  }
9695  }
9696  return $out;
9697 }
9698 
9706 function dolIsAllowedForPreview($file)
9707 {
9708  global $conf;
9709 
9710  // Check .noexe extension in filename
9711  if (preg_match('/\.noexe$/i', $file)) {
9712  return 0;
9713  }
9714 
9715  // Check mime types
9716  $mime_preview = array('bmp', 'jpeg', 'png', 'gif', 'tiff', 'pdf', 'plain', 'css', 'webp');
9717  if (!empty($conf->global->MAIN_ALLOW_SVG_FILES_AS_IMAGES)) {
9718  $mime_preview[] = 'svg+xml';
9719  }
9720  //$mime_preview[]='vnd.oasis.opendocument.presentation';
9721  //$mime_preview[]='archive';
9722  $num_mime = array_search(dol_mimetype($file, '', 1), $mime_preview);
9723  if ($num_mime !== false) {
9724  return 1;
9725  }
9726 
9727  // By default, not allowed for preview
9728  return 0;
9729 }
9730 
9731 
9741 function dol_mimetype($file, $default = 'application/octet-stream', $mode = 0)
9742 {
9743  $mime = $default;
9744  $imgmime = 'other.png';
9745  $famime = 'file-o';
9746  $srclang = '';
9747 
9748  $tmpfile = preg_replace('/\.noexe$/', '', $file);
9749 
9750  // Plain text files
9751  if (preg_match('/\.txt$/i', $tmpfile)) {
9752  $mime = 'text/plain';
9753  $imgmime = 'text.png';
9754  $famime = 'file-text-o';
9755  }
9756  if (preg_match('/\.rtx$/i', $tmpfile)) {
9757  $mime = 'text/richtext';
9758  $imgmime = 'text.png';
9759  $famime = 'file-text-o';
9760  }
9761  if (preg_match('/\.csv$/i', $tmpfile)) {
9762  $mime = 'text/csv';
9763  $imgmime = 'text.png';
9764  $famime = 'file-text-o';
9765  }
9766  if (preg_match('/\.tsv$/i', $tmpfile)) {
9767  $mime = 'text/tab-separated-values';
9768  $imgmime = 'text.png';
9769  $famime = 'file-text-o';
9770  }
9771  if (preg_match('/\.(cf|conf|log)$/i', $tmpfile)) {
9772  $mime = 'text/plain';
9773  $imgmime = 'text.png';
9774  $famime = 'file-text-o';
9775  }
9776  if (preg_match('/\.ini$/i', $tmpfile)) {
9777  $mime = 'text/plain';
9778  $imgmime = 'text.png';
9779  $srclang = 'ini';
9780  $famime = 'file-text-o';
9781  }
9782  if (preg_match('/\.md$/i', $tmpfile)) {
9783  $mime = 'text/plain';
9784  $imgmime = 'text.png';
9785  $srclang = 'md';
9786  $famime = 'file-text-o';
9787  }
9788  if (preg_match('/\.css$/i', $tmpfile)) {
9789  $mime = 'text/css';
9790  $imgmime = 'css.png';
9791  $srclang = 'css';
9792  $famime = 'file-text-o';
9793  }
9794  if (preg_match('/\.lang$/i', $tmpfile)) {
9795  $mime = 'text/plain';
9796  $imgmime = 'text.png';
9797  $srclang = 'lang';
9798  $famime = 'file-text-o';
9799  }
9800  // Certificate files
9801  if (preg_match('/\.(crt|cer|key|pub)$/i', $tmpfile)) {
9802  $mime = 'text/plain';
9803  $imgmime = 'text.png';
9804  $famime = 'file-text-o';
9805  }
9806  // XML based (HTML/XML/XAML)
9807  if (preg_match('/\.(html|htm|shtml)$/i', $tmpfile)) {
9808  $mime = 'text/html';
9809  $imgmime = 'html.png';
9810  $srclang = 'html';
9811  $famime = 'file-text-o';
9812  }
9813  if (preg_match('/\.(xml|xhtml)$/i', $tmpfile)) {
9814  $mime = 'text/xml';
9815  $imgmime = 'other.png';
9816  $srclang = 'xml';
9817  $famime = 'file-text-o';
9818  }
9819  if (preg_match('/\.xaml$/i', $tmpfile)) {
9820  $mime = 'text/xml';
9821  $imgmime = 'other.png';
9822  $srclang = 'xaml';
9823  $famime = 'file-text-o';
9824  }
9825  // Languages
9826  if (preg_match('/\.bas$/i', $tmpfile)) {
9827  $mime = 'text/plain';
9828  $imgmime = 'text.png';
9829  $srclang = 'bas';
9830  $famime = 'file-code-o';
9831  }
9832  if (preg_match('/\.(c)$/i', $tmpfile)) {
9833  $mime = 'text/plain';
9834  $imgmime = 'text.png';
9835  $srclang = 'c';
9836  $famime = 'file-code-o';
9837  }
9838  if (preg_match('/\.(cpp)$/i', $tmpfile)) {
9839  $mime = 'text/plain';
9840  $imgmime = 'text.png';
9841  $srclang = 'cpp';
9842  $famime = 'file-code-o';
9843  }
9844  if (preg_match('/\.cs$/i', $tmpfile)) {
9845  $mime = 'text/plain';
9846  $imgmime = 'text.png';
9847  $srclang = 'cs';
9848  $famime = 'file-code-o';
9849  }
9850  if (preg_match('/\.(h)$/i', $tmpfile)) {
9851  $mime = 'text/plain';
9852  $imgmime = 'text.png';
9853  $srclang = 'h';
9854  $famime = 'file-code-o';
9855  }
9856  if (preg_match('/\.(java|jsp)$/i', $tmpfile)) {
9857  $mime = 'text/plain';
9858  $imgmime = 'text.png';
9859  $srclang = 'java';
9860  $famime = 'file-code-o';
9861  }
9862  if (preg_match('/\.php([0-9]{1})?$/i', $tmpfile)) {
9863  $mime = 'text/plain';
9864  $imgmime = 'php.png';
9865  $srclang = 'php';
9866  $famime = 'file-code-o';
9867  }
9868  if (preg_match('/\.phtml$/i', $tmpfile)) {
9869  $mime = 'text/plain';
9870  $imgmime = 'php.png';
9871  $srclang = 'php';
9872  $famime = 'file-code-o';
9873  }
9874  if (preg_match('/\.(pl|pm)$/i', $tmpfile)) {
9875  $mime = 'text/plain';
9876  $imgmime = 'pl.png';
9877  $srclang = 'perl';
9878  $famime = 'file-code-o';
9879  }
9880  if (preg_match('/\.sql$/i', $tmpfile)) {
9881  $mime = 'text/plain';
9882  $imgmime = 'text.png';
9883  $srclang = 'sql';
9884  $famime = 'file-code-o';
9885  }
9886  if (preg_match('/\.js$/i', $tmpfile)) {
9887  $mime = 'text/x-javascript';
9888  $imgmime = 'jscript.png';
9889  $srclang = 'js';
9890  $famime = 'file-code-o';
9891  }
9892  // Open office
9893  if (preg_match('/\.odp$/i', $tmpfile)) {
9894  $mime = 'application/vnd.oasis.opendocument.presentation';
9895  $imgmime = 'ooffice.png';
9896  $famime = 'file-powerpoint-o';
9897  }
9898  if (preg_match('/\.ods$/i', $tmpfile)) {
9899  $mime = 'application/vnd.oasis.opendocument.spreadsheet';
9900  $imgmime = 'ooffice.png';
9901  $famime = 'file-excel-o';
9902  }
9903  if (preg_match('/\.odt$/i', $tmpfile)) {
9904  $mime = 'application/vnd.oasis.opendocument.text';
9905  $imgmime = 'ooffice.png';
9906  $famime = 'file-word-o';
9907  }
9908  // MS Office
9909  if (preg_match('/\.mdb$/i', $tmpfile)) {
9910  $mime = 'application/msaccess';
9911  $imgmime = 'mdb.png';
9912  $famime = 'file-o';
9913  }
9914  if (preg_match('/\.doc(x|m)?$/i', $tmpfile)) {
9915  $mime = 'application/msword';
9916  $imgmime = 'doc.png';
9917  $famime = 'file-word-o';
9918  }
9919  if (preg_match('/\.dot(x|m)?$/i', $tmpfile)) {
9920  $mime = 'application/msword';
9921  $imgmime = 'doc.png';
9922  $famime = 'file-word-o';
9923  }
9924  if (preg_match('/\.xlt(x)?$/i', $tmpfile)) {
9925  $mime = 'application/vnd.ms-excel';
9926  $imgmime = 'xls.png';
9927  $famime = 'file-excel-o';
9928  }
9929  if (preg_match('/\.xla(m)?$/i', $tmpfile)) {
9930  $mime = 'application/vnd.ms-excel';
9931  $imgmime = 'xls.png';
9932  $famime = 'file-excel-o';
9933  }
9934  if (preg_match('/\.xls$/i', $tmpfile)) {
9935  $mime = 'application/vnd.ms-excel';
9936  $imgmime = 'xls.png';
9937  $famime = 'file-excel-o';
9938  }
9939  if (preg_match('/\.xls(b|m|x)$/i', $tmpfile)) {
9940  $mime = 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet';
9941  $imgmime = 'xls.png';
9942  $famime = 'file-excel-o';
9943  }
9944  if (preg_match('/\.pps(m|x)?$/i', $tmpfile)) {
9945  $mime = 'application/vnd.ms-powerpoint';
9946  $imgmime = 'ppt.png';
9947  $famime = 'file-powerpoint-o';
9948  }
9949  if (preg_match('/\.ppt(m|x)?$/i', $tmpfile)) {
9950  $mime = 'application/x-mspowerpoint';
9951  $imgmime = 'ppt.png';
9952  $famime = 'file-powerpoint-o';
9953  }
9954  // Other
9955  if (preg_match('/\.pdf$/i', $tmpfile)) {
9956  $mime = 'application/pdf';
9957  $imgmime = 'pdf.png';
9958  $famime = 'file-pdf-o';
9959  }
9960  // Scripts
9961  if (preg_match('/\.bat$/i', $tmpfile)) {
9962  $mime = 'text/x-bat';
9963  $imgmime = 'script.png';
9964  $srclang = 'dos';
9965  $famime = 'file-code-o';
9966  }
9967  if (preg_match('/\.sh$/i', $tmpfile)) {
9968  $mime = 'text/x-sh';
9969  $imgmime = 'script.png';
9970  $srclang = 'bash';
9971  $famime = 'file-code-o';
9972  }
9973  if (preg_match('/\.ksh$/i', $tmpfile)) {
9974  $mime = 'text/x-ksh';
9975  $imgmime = 'script.png';
9976  $srclang = 'bash';
9977  $famime = 'file-code-o';
9978  }
9979  if (preg_match('/\.bash$/i', $tmpfile)) {
9980  $mime = 'text/x-bash';
9981  $imgmime = 'script.png';
9982  $srclang = 'bash';
9983  $famime = 'file-code-o';
9984  }
9985  // Images
9986  if (preg_match('/\.ico$/i', $tmpfile)) {
9987  $mime = 'image/x-icon';
9988  $imgmime = 'image.png';
9989  $famime = 'file-image-o';
9990  }
9991  if (preg_match('/\.(jpg|jpeg)$/i', $tmpfile)) {
9992  $mime = 'image/jpeg';
9993  $imgmime = 'image.png';
9994  $famime = 'file-image-o';
9995  }
9996  if (preg_match('/\.png$/i', $tmpfile)) {
9997  $mime = 'image/png';
9998  $imgmime = 'image.png';
9999  $famime = 'file-image-o';
10000  }
10001  if (preg_match('/\.gif$/i', $tmpfile)) {
10002  $mime = 'image/gif';
10003  $imgmime = 'image.png';
10004  $famime = 'file-image-o';
10005  }
10006  if (preg_match('/\.bmp$/i', $tmpfile)) {
10007  $mime = 'image/bmp';
10008  $imgmime = 'image.png';
10009  $famime = 'file-image-o';
10010  }
10011  if (preg_match('/\.(tif|tiff)$/i', $tmpfile)) {
10012  $mime = 'image/tiff';
10013  $imgmime = 'image.png';
10014  $famime = 'file-image-o';
10015  }
10016  if (preg_match('/\.svg$/i', $tmpfile)) {
10017  $mime = 'image/svg+xml';
10018  $imgmime = 'image.png';
10019  $famime = 'file-image-o';
10020  }
10021  if (preg_match('/\.webp$/i', $tmpfile)) {
10022  $mime = 'image/webp';
10023  $imgmime = 'image.png';
10024  $famime = 'file-image-o';
10025  }
10026  // Calendar
10027  if (preg_match('/\.vcs$/i', $tmpfile)) {
10028  $mime = 'text/calendar';
10029  $imgmime = 'other.png';
10030  $famime = 'file-text-o';
10031  }
10032  if (preg_match('/\.ics$/i', $tmpfile)) {
10033  $mime = 'text/calendar';
10034  $imgmime = 'other.png';
10035  $famime = 'file-text-o';
10036  }
10037  // Other
10038  if (preg_match('/\.torrent$/i', $tmpfile)) {
10039  $mime = 'application/x-bittorrent';
10040  $imgmime = 'other.png';
10041  $famime = 'file-o';
10042  }
10043  // Audio
10044  if (preg_match('/\.(mp3|ogg|au|wav|wma|mid)$/i', $tmpfile)) {
10045  $mime = 'audio';
10046  $imgmime = 'audio.png';
10047  $famime = 'file-audio-o';
10048  }
10049  // Video
10050  if (preg_match('/\.mp4$/i', $tmpfile)) {
10051  $mime = 'video/mp4';
10052  $imgmime = 'video.png';
10053  $famime = 'file-video-o';
10054  }
10055  if (preg_match('/\.ogv$/i', $tmpfile)) {
10056  $mime = 'video/ogg';
10057  $imgmime = 'video.png';
10058  $famime = 'file-video-o';
10059  }
10060  if (preg_match('/\.webm$/i', $tmpfile)) {
10061  $mime = 'video/webm';
10062  $imgmime = 'video.png';
10063  $famime = 'file-video-o';
10064  }
10065  if (preg_match('/\.avi$/i', $tmpfile)) {
10066  $mime = 'video/x-msvideo';
10067  $imgmime = 'video.png';
10068  $famime = 'file-video-o';
10069  }
10070  if (preg_match('/\.divx$/i', $tmpfile)) {
10071  $mime = 'video/divx';
10072  $imgmime = 'video.png';
10073  $famime = 'file-video-o';
10074  }
10075  if (preg_match('/\.xvid$/i', $tmpfile)) {
10076  $mime = 'video/xvid';
10077  $imgmime = 'video.png';
10078  $famime = 'file-video-o';
10079  }
10080  if (preg_match('/\.(wmv|mpg|mpeg)$/i', $tmpfile)) {
10081  $mime = 'video';
10082  $imgmime = 'video.png';
10083  $famime = 'file-video-o';
10084  }
10085  // Archive
10086  if (preg_match('/\.(zip|rar|gz|tgz|z|cab|bz2|7z|tar|lzh|zst)$/i', $tmpfile)) {
10087  $mime = 'archive';
10088  $imgmime = 'archive.png';
10089  $famime = 'file-archive-o';
10090  } // application/xxx where zzz is zip, ...
10091  // Exe
10092  if (preg_match('/\.(exe|com)$/i', $tmpfile)) {
10093  $mime = 'application/octet-stream';
10094  $imgmime = 'other.png';
10095  $famime = 'file-o';
10096  }
10097  // Lib
10098  if (preg_match('/\.(dll|lib|o|so|a)$/i', $tmpfile)) {
10099  $mime = 'library';
10100  $imgmime = 'library.png';
10101  $famime = 'file-o';
10102  }
10103  // Err
10104  if (preg_match('/\.err$/i', $tmpfile)) {
10105  $mime = 'error';
10106  $imgmime = 'error.png';
10107  $famime = 'file-text-o';
10108  }
10109 
10110  // Return string
10111  if ($mode == 1) {
10112  $tmp = explode('/', $mime);
10113  return (!empty($tmp[1]) ? $tmp[1] : $tmp[0]);
10114  }
10115  if ($mode == 2) {
10116  return $imgmime;
10117  }
10118  if ($mode == 3) {
10119  return $srclang;
10120  }
10121  if ($mode == 4) {
10122  return $famime;
10123  }
10124  return $mime;
10125 }
10126 
10138 function getDictionaryValue($tablename, $field, $id, $checkentity = false, $rowidfield = 'rowid')
10139 {
10140  global $conf, $db;
10141 
10142  $tablename = preg_replace('/^'.preg_quote(MAIN_DB_PREFIX, '/').'/', '', $tablename); // Clean name of table for backward compatibility.
10143 
10144  $dictvalues = (isset($conf->cache['dictvalues_'.$tablename]) ? $conf->cache['dictvalues_'.$tablename] : null);
10145 
10146  if (is_null($dictvalues)) {
10147  $dictvalues = array();
10148 
10149  $sql = "SELECT * FROM ".MAIN_DB_PREFIX.$tablename." WHERE 1 = 1"; // Here select * is allowed as it is generic code and we don't have list of fields
10150  if ($checkentity) {
10151  $sql .= ' AND entity IN (0,'.getEntity($tablename).')';
10152  }
10153 
10154  $resql = $db->query($sql);
10155  if ($resql) {
10156  while ($obj = $db->fetch_object($resql)) {
10157  $dictvalues[$obj->{$rowidfield}] = $obj; // $obj is stdClass
10158  }
10159  } else {
10160  dol_print_error($db);
10161  }
10162 
10163  $conf->cache['dictvalues_'.$tablename] = $dictvalues;
10164  }
10165 
10166  if (!empty($dictvalues[$id])) {
10167  // Found
10168  $tmp = $dictvalues[$id];
10169  return (property_exists($tmp, $field) ? $tmp->$field : '');
10170  } else {
10171  // Not found
10172  return '';
10173  }
10174 }
10175 
10182 function colorIsLight($stringcolor)
10183 {
10184  $stringcolor = str_replace('#', '', $stringcolor);
10185  $res = -1;
10186  if (!empty($stringcolor)) {
10187  $res = 0;
10188  $tmp = explode(',', $stringcolor);
10189  if (count($tmp) > 1) { // This is a comma RGB ('255','255','255')
10190  $r = $tmp[0];
10191  $g = $tmp[1];
10192  $b = $tmp[2];
10193  } else {
10194  $hexr = $stringcolor[0].$stringcolor[1];
10195  $hexg = $stringcolor[2].$stringcolor[3];
10196  $hexb = $stringcolor[4].$stringcolor[5];
10197  $r = hexdec($hexr);
10198  $g = hexdec($hexg);
10199  $b = hexdec($hexb);
10200  }
10201  $bright = (max($r, $g, $b) + min($r, $g, $b)) / 510.0; // HSL algorithm
10202  if ($bright > 0.6) {
10203  $res = 1;
10204  }
10205  }
10206  return $res;
10207 }
10208 
10217 function isVisibleToUserType($type_user, &$menuentry, &$listofmodulesforexternal)
10218 {
10219  global $conf;
10220 
10221  //print 'type_user='.$type_user.' module='.$menuentry['module'].' enabled='.$menuentry['enabled'].' perms='.$menuentry['perms'];
10222  //print 'ok='.in_array($menuentry['module'], $listofmodulesforexternal);
10223  if (empty($menuentry['enabled'])) {
10224  return 0; // Entry disabled by condition
10225  }
10226  if ($type_user && $menuentry['module']) {
10227  $tmploops = explode('|', $menuentry['module']);
10228  $found = 0;
10229  foreach ($tmploops as $tmploop) {
10230  if (in_array($tmploop, $listofmodulesforexternal)) {
10231  $found++;
10232  break;
10233  }
10234  }
10235  if (!$found) {
10236  return 0; // Entry is for menus all excluded to external users
10237  }
10238  }
10239  if (!$menuentry['perms'] && $type_user) {
10240  return 0; // No permissions and user is external
10241  }
10242  if (!$menuentry['perms'] && !empty($conf->global->MAIN_MENU_HIDE_UNAUTHORIZED)) {
10243  return 0; // No permissions and option to hide when not allowed, even for internal user, is on
10244  }
10245  if (!$menuentry['perms']) {
10246  return 2; // No permissions and user is external
10247  }
10248  return 1;
10249 }
10250 
10258 function roundUpToNextMultiple($n, $x = 5)
10259 {
10260  return (ceil($n) % $x === 0) ? ceil($n) : round(($n + $x / 2) / $x) * $x;
10261 }
10262 
10274 function dolGetBadge($label, $html = '', $type = 'primary', $mode = '', $url = '', $params = array())
10275 {
10276  $attr = array(
10277  'class'=>'badge '.(!empty($mode) ? ' badge-'.$mode : '').(!empty($type) ? ' badge-'.$type : '').(empty($params['css']) ? '' : ' '.$params['css'])
10278  );
10279 
10280  if (empty($html)) {
10281  $html = $label;
10282  }
10283 
10284  if (!empty($url)) {
10285  $attr['href'] = $url;
10286  }
10287 
10288  if ($mode === 'dot') {
10289  $attr['class'] .= ' classfortooltip';
10290  $attr['title'] = $html;
10291  $attr['aria-label'] = $label;
10292  $html = '';
10293  }
10294 
10295  // Override attr
10296  if (!empty($params['attr']) && is_array($params['attr'])) {
10297  foreach ($params['attr'] as $key => $value) {
10298  if ($key == 'class') {
10299  $attr['class'] .= ' '.$value;
10300  } elseif ($key == 'classOverride') {
10301  $attr['class'] = $value;
10302  } else {
10303  $attr[$key] = $value;
10304  }
10305  }
10306  }
10307 
10308  // TODO: add hook
10309 
10310  // escape all attribute
10311  $attr = array_map('dol_escape_htmltag', $attr);
10312 
10313  $TCompiledAttr = array();
10314  foreach ($attr as $key => $value) {
10315  $TCompiledAttr[] = $key.'="'.$value.'"';
10316  }
10317 
10318  $compiledAttributes = !empty($TCompiledAttr) ?implode(' ', $TCompiledAttr) : '';
10319 
10320  $tag = !empty($url) ? 'a' : 'span';
10321 
10322  return '<'.$tag.' '.$compiledAttributes.'>'.$html.'</'.$tag.'>';
10323 }
10324 
10325 
10338 function dolGetStatus($statusLabel = '', $statusLabelShort = '', $html = '', $statusType = 'status0', $displayMode = 0, $url = '', $params = array())
10339 {
10340  global $conf;
10341 
10342  $return = '';
10343  $dolGetBadgeParams = array();
10344 
10345  if (!empty($params['badgeParams'])) {
10346  $dolGetBadgeParams = $params['badgeParams'];
10347  }
10348 
10349  // TODO : add a hook
10350  if ($displayMode == 0) {
10351  $return = !empty($html) ? $html : (empty($conf->dol_optimize_smallscreen) ? $statusLabel : (empty($statusLabelShort) ? $statusLabel : $statusLabelShort));
10352  } elseif ($displayMode == 1) {
10353  $return = !empty($html) ? $html : (empty($statusLabelShort) ? $statusLabel : $statusLabelShort);
10354  } elseif (!empty($conf->global->MAIN_STATUS_USES_IMAGES)) {
10355  // Use status with images (for backward compatibility)
10356  $return = '';
10357  $htmlLabel = (in_array($displayMode, array(1, 2, 5)) ? '<span class="hideonsmartphone">' : '').(!empty($html) ? $html : $statusLabel).(in_array($displayMode, array(1, 2, 5)) ? '</span>' : '');
10358  $htmlLabelShort = (in_array($displayMode, array(1, 2, 5)) ? '<span class="hideonsmartphone">' : '').(!empty($html) ? $html : (!empty($statusLabelShort) ? $statusLabelShort : $statusLabel)).(in_array($displayMode, array(1, 2, 5)) ? '</span>' : '');
10359 
10360  // For small screen, we always use the short label instead of long label.
10361  if (!empty($conf->dol_optimize_smallscreen)) {
10362  if ($displayMode == 0) {
10363  $displayMode = 1;
10364  } elseif ($displayMode == 4) {
10365  $displayMode = 2;
10366  } elseif ($displayMode == 6) {
10367  $displayMode = 5;
10368  }
10369  }
10370 
10371  // For backward compatibility. Image's filename are still in French, so we use this array to convert
10372  $statusImg = array(
10373  'status0' => 'statut0',
10374  'status1' => 'statut1',
10375  'status2' => 'statut2',
10376  'status3' => 'statut3',
10377  'status4' => 'statut4',
10378  'status5' => 'statut5',
10379  'status6' => 'statut6',
10380  'status7' => 'statut7',
10381  'status8' => 'statut8',
10382  'status9' => 'statut9'
10383  );
10384 
10385  if (!empty($statusImg[$statusType])) {
10386  $htmlImg = img_picto($statusLabel, $statusImg[$statusType]);
10387  } else {
10388  $htmlImg = img_picto($statusLabel, $statusType);
10389  }
10390 
10391  if ($displayMode === 2) {
10392  $return = $htmlImg.' '.$htmlLabelShort;
10393  } elseif ($displayMode === 3) {
10394  $return = $htmlImg;
10395  } elseif ($displayMode === 4) {
10396  $return = $htmlImg.' '.$htmlLabel;
10397  } elseif ($displayMode === 5) {
10398  $return = $htmlLabelShort.' '.$htmlImg;
10399  } else { // $displayMode >= 6
10400  $return = $htmlLabel.' '.$htmlImg;
10401  }
10402  } elseif (empty($conf->global->MAIN_STATUS_USES_IMAGES) && !empty($displayMode)) {
10403  // Use new badge
10404  $statusLabelShort = (empty($statusLabelShort) ? $statusLabel : $statusLabelShort);
10405 
10406  $dolGetBadgeParams['attr']['class'] = 'badge-status';
10407  $dolGetBadgeParams['attr']['title'] = empty($params['tooltip']) ? $statusLabel : ($params['tooltip'] != 'no' ? $params['tooltip'] : '');
10408 
10409  if ($displayMode == 3) {
10410  $return = dolGetBadge((empty($conf->dol_optimize_smallscreen) ? $statusLabel : (empty($statusLabelShort) ? $statusLabel : $statusLabelShort)), '', $statusType, 'dot', $url, $dolGetBadgeParams);
10411  } elseif ($displayMode === 5) {
10412  $return = dolGetBadge($statusLabelShort, $html, $statusType, '', $url, $dolGetBadgeParams);
10413  } else {
10414  $return = dolGetBadge((empty($conf->dol_optimize_smallscreen) ? $statusLabel : (empty($statusLabelShort) ? $statusLabel : $statusLabelShort)), $html, $statusType, '', $url, $dolGetBadgeParams);
10415  }
10416  }
10417 
10418  return $return;
10419 }
10420 
10421 
10450 function dolGetButtonAction($label, $html = '', $actionType = 'default', $url = '', $id = '', $userRight = 1, $params = array())
10451 {
10452  global $hookmanager, $action, $object, $langs;
10453 
10454  $class = 'butAction';
10455  if ($actionType == 'danger' || $actionType == 'delete') {
10456  $class = 'butActionDelete';
10457  if (!empty($url) && strpos($url, 'token=') === false) $url .= '&token='.newToken();
10458  }
10459 
10460  $attr = array(
10461  'class' => $class,
10462  'href' => empty($url) ? '' : $url,
10463  'title' => $label
10464  );
10465 
10466  if (empty($html)) {
10467  $html = $label;
10468  $attr['title'] = ''; // if html not set, leave label on title is redundant
10469  } else {
10470  $attr['aria-label'] = $label;
10471  }
10472 
10473  if (empty($userRight)) {
10474  $attr['class'] = 'butActionRefused';
10475  $attr['href'] = '';
10476  }
10477 
10478  if (!empty($id)) {
10479  $attr['id'] = $id;
10480  }
10481 
10482 
10483  // Override attr
10484  if (!empty($params['attr']) && is_array($params['attr'])) {
10485  foreach ($params['attr'] as $key => $value) {
10486  if ($key == 'class') {
10487  $attr['class'] .= ' '.$value;
10488  } elseif ($key == 'classOverride') {
10489  $attr['class'] = $value;
10490  } else {
10491  $attr[$key] = $value;
10492  }
10493  }
10494  }
10495 
10496  // automatic add tooltip when title is detected
10497  if (!empty($attr['title']) && !empty($attr['class']) && strpos($attr['class'], 'classfortooltip') === false) {
10498  $attr['class'].= ' classfortooltip';
10499  }
10500 
10501  // Js Confirm button
10502  if ($userRight && !empty($params['confirm'])) {
10503  if (!is_array($params['confirm'])) {
10504  $params['confirm'] = array();
10505  }
10506 
10507  if (empty($params['confirm']['url'])) {
10508  $params['confirm']['url'] = $url . (strpos($url, '?') > 0 ? '&' : '?') . 'confirm=yes';
10509  }
10510 
10511  // for js desabled compatibility set $url as call to confirm action and $params['confirm']['url'] to confirmed action
10512  $attr['data-confirm-url'] = $params['confirm']['url'];
10513  $attr['data-confirm-title'] = !empty($params['confirm']['title']) ? $params['confirm']['title'] : $langs->trans('ConfirmBtnCommonTitle', $label);
10514  $attr['data-confirm-content'] = !empty($params['confirm']['content']) ? $params['confirm']['content'] : $langs->trans('ConfirmBtnCommonContent', $label);
10515  $attr['data-confirm-content'] = preg_replace("/\r|\n/", "", $attr['data-confirm-content']);
10516  $attr['data-confirm-action-btn-label'] = !empty($params['confirm']['action-btn-label']) ? $params['confirm']['action-btn-label'] : $langs->trans('Confirm');
10517  $attr['data-confirm-cancel-btn-label'] = !empty($params['confirm']['cancel-btn-label']) ? $params['confirm']['cancel-btn-label'] : $langs->trans('CloseDialog');
10518  $attr['data-confirm-modal'] = !empty($params['confirm']['modal']) ? $params['confirm']['modal'] : true;
10519 
10520  $attr['class'].= ' butActionConfirm';
10521  }
10522 
10523  if (isset($attr['href']) && empty($attr['href'])) {
10524  unset($attr['href']);
10525  }
10526 
10527  // escape all attribute
10528  $attr = array_map('dol_escape_htmltag', $attr);
10529 
10530  $TCompiledAttr = array();
10531  foreach ($attr as $key => $value) {
10532  $TCompiledAttr[] = $key.'="'.$value.'"';
10533  }
10534 
10535  $compiledAttributes = empty($TCompiledAttr) ? '' : implode(' ', $TCompiledAttr);
10536 
10537  $tag = !empty($attr['href']) ? 'a' : 'span';
10538 
10539 
10540  $parameters = array(
10541  'TCompiledAttr' => $TCompiledAttr, // array
10542  'compiledAttributes' => $compiledAttributes, // string
10543  'attr' => $attr,
10544  'tag' => $tag,
10545  'label' => $label,
10546  'html' => $html,
10547  'actionType' => $actionType,
10548  'url' => $url,
10549  'id' => $id,
10550  'userRight' => $userRight,
10551  'params' => $params
10552  );
10553 
10554  $reshook = $hookmanager->executeHooks('dolGetButtonAction', $parameters, $object, $action); // Note that $action and $object may have been modified by some hooks
10555  if ($reshook < 0) setEventMessages($hookmanager->error, $hookmanager->errors, 'errors');
10556 
10557  if (empty($reshook)) {
10558  return '<' . $tag . ' ' . $compiledAttributes . '>' . $html . '</' . $tag . '>';
10559  } else {
10560  return $hookmanager->resPrint;
10561  }
10562 }
10563 
10570 function dolGetButtonTitleSeparator($moreClass = "")
10571 {
10572  return '<span class="button-title-separator '.$moreClass.'" ></span>';
10573 }
10574 
10581 function getFieldErrorIcon($fieldValidationErrorMsg)
10582 {
10583  $out = '';
10584  if (!empty($fieldValidationErrorMsg)) {
10585  $out.= '<span class="field-error-icon classfortooltip" title="'.dol_escape_htmltag($fieldValidationErrorMsg, 1).'" role="alert" >'; // role alert is used for accessibility
10586  $out.= '<span class="fa fa-exclamation-circle" aria-hidden="true" ></span>'; // For accessibility icon is separated and aria-hidden
10587  $out.= '</span>';
10588  }
10589 
10590  return $out;
10591 }
10592 
10605 function dolGetButtonTitle($label, $helpText = '', $iconClass = 'fa fa-file', $url = '', $id = '', $status = 1, $params = array())
10606 {
10607  global $langs, $conf, $user;
10608 
10609  // Actually this conf is used in css too for external module compatibility and smooth transition to this function
10610  if (!empty($conf->global->MAIN_BUTTON_HIDE_UNAUTHORIZED) && (!$user->admin) && $status <= 0) {
10611  return '';
10612  }
10613 
10614  $class = 'btnTitle';
10615  if (in_array($iconClass, array('fa fa-plus-circle', 'fa fa-plus-circle size15x', 'fa fa-comment-dots'))) {
10616  $class .= ' btnTitlePlus';
10617  }
10618  $useclassfortooltip = 1;
10619 
10620  if (!empty($params['morecss'])) {
10621  $class .= ' '.$params['morecss'];
10622  }
10623 
10624  $attr = array(
10625  'class' => $class,
10626  'href' => empty($url) ? '' : $url
10627  );
10628 
10629  if (!empty($helpText)) {
10630  $attr['title'] = dol_escape_htmltag($helpText);
10631  } elseif (empty($attr['title']) && $label) {
10632  $attr['title'] = $label;
10633  $useclassfortooltip = 0;
10634  }
10635 
10636  if ($status == 2) {
10637  $attr['class'] .= ' btnTitleSelected';
10638  } elseif ($status <= 0) {
10639  $attr['class'] .= ' refused';
10640 
10641  $attr['href'] = '';
10642 
10643  if ($status == -1) { // disable
10644  $attr['title'] = dol_escape_htmltag($langs->transnoentitiesnoconv("FeatureDisabled"));
10645  } elseif ($status == 0) { // Not enough permissions
10646  $attr['title'] = dol_escape_htmltag($langs->transnoentitiesnoconv("NotEnoughPermissions"));
10647  }
10648  }
10649 
10650  if (!empty($attr['title']) && $useclassfortooltip) {
10651  $attr['class'] .= ' classfortooltip';
10652  }
10653 
10654  if (!empty($id)) {
10655  $attr['id'] = $id;
10656  }
10657 
10658  // Override attr
10659  if (!empty($params['attr']) && is_array($params['attr'])) {
10660  foreach ($params['attr'] as $key => $value) {
10661  if ($key == 'class') {
10662  $attr['class'] .= ' '.$value;
10663  } elseif ($key == 'classOverride') {
10664  $attr['class'] = $value;
10665  } else {
10666  $attr[$key] = $value;
10667  }
10668  }
10669  }
10670 
10671  if (isset($attr['href']) && empty($attr['href'])) {
10672  unset($attr['href']);
10673  }
10674 
10675  // TODO : add a hook
10676 
10677  // escape all attribute
10678  $attr = array_map('dol_escape_htmltag', $attr);
10679 
10680  $TCompiledAttr = array();
10681  foreach ($attr as $key => $value) {
10682  $TCompiledAttr[] = $key.'="'.$value.'"';
10683  }
10684 
10685  $compiledAttributes = (empty($TCompiledAttr) ? '' : implode(' ', $TCompiledAttr));
10686 
10687  $tag = (empty($attr['href']) ? 'span' : 'a');
10688 
10689  $button = '<'.$tag.' '.$compiledAttributes.'>';
10690  $button .= '<span class="'.$iconClass.' valignmiddle btnTitle-icon"></span>';
10691  if (!empty($params['forcenohideoftext'])) {
10692  $button .= '<span class="valignmiddle text-plus-circle btnTitle-label'.(empty($params['forcenohideoftext']) ? ' hideonsmartphone' : '').'">'.$label.'</span>';
10693  }
10694  $button .= '</'.$tag.'>';
10695 
10696  return $button;
10697 }
10698 
10706 function getElementProperties($element_type)
10707 {
10708  $regs = array();
10709 
10710  $classfile = $classname = $classpath = '';
10711 
10712  // Parse element/subelement (ex: project_task)
10713  $module = $element_type;
10714  $element = $element_type;
10715  $subelement = $element_type;
10716 
10717  // If we ask an resource form external module (instead of default path)
10718  if (preg_match('/^([^@]+)@([^@]+)$/i', $element_type, $regs)) {
10719  $element = $subelement = $regs[1];
10720  $module = $regs[2];
10721  }
10722 
10723  //print '<br>1. element : '.$element.' - module : '.$module .'<br>';
10724  if (preg_match('/^([^_]+)_([^_]+)/i', $element, $regs)) {
10725  $module = $element = $regs[1];
10726  $subelement = $regs[2];
10727  }
10728 
10729  // For compat
10730  if ($element_type == "action") {
10731  $classpath = 'comm/action/class';
10732  $subelement = 'Actioncomm';
10733  $module = 'agenda';
10734  }
10735 
10736  // To work with non standard path
10737  if ($element_type == 'facture' || $element_type == 'invoice') {
10738  $classpath = 'compta/facture/class';
10739  $module = 'facture';
10740  $subelement = 'facture';
10741  }
10742  if ($element_type == 'commande' || $element_type == 'order') {
10743  $classpath = 'commande/class';
10744  $module = 'commande';
10745  $subelement = 'commande';
10746  }
10747  if ($element_type == 'propal') {
10748  $classpath = 'comm/propal/class';
10749  }
10750  if ($element_type == 'supplier_proposal') {
10751  $classpath = 'supplier_proposal/class';
10752  }
10753  if ($element_type == 'shipping') {
10754  $classpath = 'expedition/class';
10755  $subelement = 'expedition';
10756  $module = 'expedition_bon';
10757  }
10758  if ($element_type == 'delivery') {
10759  $classpath = 'delivery/class';
10760  $subelement = 'delivery';
10761  $module = 'delivery_note';
10762  }
10763  if ($element_type == 'contract') {
10764  $classpath = 'contrat/class';
10765  $module = 'contrat';
10766  $subelement = 'contrat';
10767  }
10768  if ($element_type == 'member') {
10769  $classpath = 'adherents/class';
10770  $module = 'adherent';
10771  $subelement = 'adherent';
10772  }
10773  if ($element_type == 'cabinetmed_cons') {
10774  $classpath = 'cabinetmed/class';
10775  $module = 'cabinetmed';
10776  $subelement = 'cabinetmedcons';
10777  }
10778  if ($element_type == 'fichinter') {
10779  $classpath = 'fichinter/class';
10780  $module = 'ficheinter';
10781  $subelement = 'fichinter';
10782  }
10783  if ($element_type == 'dolresource' || $element_type == 'resource') {
10784  $classpath = 'resource/class';
10785  $module = 'resource';
10786  $subelement = 'dolresource';
10787  }
10788  if ($element_type == 'propaldet') {
10789  $classpath = 'comm/propal/class';
10790  $module = 'propal';
10791  $subelement = 'propaleligne';
10792  }
10793  if ($element_type == 'order_supplier') {
10794  $classpath = 'fourn/class';
10795  $module = 'fournisseur';
10796  $subelement = 'commandefournisseur';
10797  $classfile = 'fournisseur.commande';
10798  }
10799  if ($element_type == 'invoice_supplier') {
10800  $classpath = 'fourn/class';
10801  $module = 'fournisseur';
10802  $subelement = 'facturefournisseur';
10803  $classfile = 'fournisseur.facture';
10804  }
10805  if ($element_type == "service") {
10806  $classpath = 'product/class';
10807  $subelement = 'product';
10808  }
10809 
10810  if (empty($classfile)) {
10811  $classfile = strtolower($subelement);
10812  }
10813  if (empty($classname)) {
10814  $classname = ucfirst($subelement);
10815  }
10816  if (empty($classpath)) {
10817  $classpath = $module.'/class';
10818  }
10819 
10820  $element_properties = array(
10821  'module' => $module,
10822  'classpath' => $classpath,
10823  'element' => $element,
10824  'subelement' => $subelement,
10825  'classfile' => $classfile,
10826  'classname' => $classname
10827  );
10828  return $element_properties;
10829 }
10830 
10840 function fetchObjectByElement($element_id, $element_type, $element_ref = '')
10841 {
10842  global $conf, $db;
10843 
10844  $element_prop = getElementProperties($element_type);
10845  if (is_array($element_prop) && $conf->{$element_prop['module']}->enabled) {
10846  dol_include_once('/'.$element_prop['classpath'].'/'.$element_prop['classfile'].'.class.php');
10847 
10848  $objecttmp = new $element_prop['classname']($db);
10849  $ret = $objecttmp->fetch($element_id, $element_ref);
10850  if ($ret >= 0) {
10851  return $objecttmp;
10852  }
10853  }
10854  return 0;
10855 }
10856 
10863 function isAFileWithExecutableContent($filename)
10864 {
10865  if (preg_match('/\.(htm|html|js|phar|php|php\d+|phtml|pht|pl|py|cgi|ksh|sh|shtml|bash|bat|cmd|wpk|exe|dmg)$/i', $filename)) {
10866  return true;
10867  }
10868 
10869  return false;
10870 }
10871 
10878 function newToken()
10879 {
10880  return empty($_SESSION['newtoken']) ? '' : $_SESSION['newtoken'];
10881 }
10882 
10889 function currentToken()
10890 {
10891  return isset($_SESSION['token']) ? $_SESSION['token'] : '';
10892 }
10893 
10906 function startSimpleTable($header, $link = "", $arguments = "", $emptyRows = 0, $number = -1)
10907 {
10908  global $langs;
10909 
10910  print '<div class="div-table-responsive-no-min">';
10911  print '<table class="noborder centpercent">';
10912  print '<tr class="liste_titre">';
10913 
10914  print $emptyRows < 1 ? '<th>' : '<th colspan="'.($emptyRows + 1).'">';
10915 
10916  print $langs->trans($header);
10917 
10918  // extra space between the first header and the number
10919  if ($number > -1) {
10920  print ' ';
10921  }
10922 
10923  if (!empty($link)) {
10924  if (!empty($arguments)) {
10925  print '<a href="'.DOL_URL_ROOT.'/'.$link.'?'.$arguments.'">';
10926  } else {
10927  print '<a href="'.DOL_URL_ROOT.'/'.$link.'">';
10928  }
10929  }
10930 
10931  if ($number > -1) {
10932  print '<span class="badge">'.$number.'</span>';
10933  }
10934 
10935  if (!empty($link)) {
10936  print '</a>';
10937  }
10938 
10939  print '</th>';
10940 
10941  if ($number < 0 && !empty($link)) {
10942  print '<th class="right">';
10943 
10944  if (!empty($arguments)) {
10945  print '<a class="commonlink" href="'.DOL_URL_ROOT.'/'.$link.'?'.$arguments.'">';
10946  } else {
10947  print '<a class="commonlink" href="'.DOL_URL_ROOT.'/'.$link.'">';
10948  }
10949 
10950  print $langs->trans("FullList");
10951  print '</a>';
10952  print '</th>';
10953  }
10954 
10955  print '</tr>';
10956 }
10957 
10966 function finishSimpleTable($addLineBreak = false)
10967 {
10968  print '</table>';
10969  print '</div>';
10970 
10971  if ($addLineBreak) {
10972  print '<br>';
10973  }
10974 }
10975 
10987 function addSummaryTableLine($tableColumnCount, $num, $nbofloop = 0, $total = 0, $noneWord = "None", $extraRightColumn = false)
10988 {
10989  global $langs;
10990 
10991  if ($num === 0) {
10992  print '<tr class="oddeven">';
10993  print '<td colspan="'.$tableColumnCount.'" class="opacitymedium">'.$langs->trans($noneWord).'</td>';
10994  print '</tr>';
10995  return;
10996  }
10997 
10998  if ($nbofloop === 0) {
10999  // don't show a summary line
11000  return;
11001  }
11002 
11003  if ($num === 0) {
11004  $colspan = $tableColumnCount;
11005  } elseif ($num > $nbofloop) {
11006  $colspan = $tableColumnCount;
11007  } else {
11008  $colspan = $tableColumnCount - 1;
11009  }
11010 
11011  if ($extraRightColumn) {
11012  $colspan--;
11013  }
11014 
11015  print '<tr class="liste_total">';
11016 
11017  if ($nbofloop > 0 && $num > $nbofloop) {
11018  print '<td colspan="'.$colspan.'" class="right">'.$langs->trans("XMoreLines", ($num - $nbofloop)).'</td>';
11019  } else {
11020  print '<td colspan="'.$colspan.'" class="right"> '.$langs->trans("Total").'</td>';
11021  print '<td class="right" width="100">'.price($total).'</td>';
11022  }
11023 
11024  if ($extraRightColumn) {
11025  print '<td></td>';
11026  }
11027 
11028  print '</tr>';
11029 }
11030 
11039 function readfileLowMemory($fullpath_original_file_osencoded, $method = -1)
11040 {
11041  global $conf;
11042 
11043  if ($method == -1) {
11044  $method = 0;
11045  if (!empty($conf->global->MAIN_FORCE_READFILE_WITH_FREAD)) {
11046  $method = 1;
11047  }
11048  if (!empty($conf->global->MAIN_FORCE_READFILE_WITH_STREAM_COPY)) {
11049  $method = 2;
11050  }
11051  }
11052 
11053  // Be sure we don't have output buffering enabled to have readfile working correctly
11054  while (ob_get_level()) {
11055  ob_end_flush();
11056  }
11057 
11058  // Solution 0
11059  if ($method == 0) {
11060  readfile($fullpath_original_file_osencoded);
11061  } elseif ($method == 1) {
11062  // Solution 1
11063  $handle = fopen($fullpath_original_file_osencoded, "rb");
11064  while (!feof($handle)) {
11065  print fread($handle, 8192);
11066  }
11067  fclose($handle);
11068  } elseif ($method == 2) {
11069  // Solution 2
11070  $handle1 = fopen($fullpath_original_file_osencoded, "rb");
11071  $handle2 = fopen("php://output", "wb");
11072  stream_copy_to_stream($handle1, $handle2);
11073  fclose($handle1);
11074  fclose($handle2);
11075  }
11076 }
11077 
11087 function showValueWithClipboardCPButton($valuetocopy, $showonlyonhover = 1, $texttoshow = '')
11088 {
11089  /*
11090  global $conf;
11091 
11092  if (!empty($conf->dol_no_mouse_hover)) {
11093  $showonlyonhover = 0;
11094  }*/
11095 
11096  $tag = 'span'; // Using div (like any style of type 'block') does not work when using the js copy code.
11097  if ($texttoshow === 'none') {
11098  $result = '<span class="clipboardCP'.($showonlyonhover ? ' clipboardCPShowOnHover' : '').'"><'.$tag.' class="clipboardCPValue hidewithsize">'.dol_escape_htmltag($valuetocopy, 1, 1).'</'.$tag.'><span class="clipboardCPValueToPrint"></span><span class="clipboardCPButton far fa-clipboard opacitymedium paddingleft paddingright"></span><span class="clipboardCPText"></span></span>';
11099  } elseif ($texttoshow) {
11100  $result = '<span class="clipboardCP'.($showonlyonhover ? ' clipboardCPShowOnHover' : '').'"><'.$tag.' class="clipboardCPValue hidewithsize">'.dol_escape_htmltag($valuetocopy, 1, 1).'</'.$tag.'><span class="clipboardCPValueToPrint">'.dol_escape_htmltag($texttoshow, 1, 1).'</span><span class="clipboardCPButton far fa-clipboard opacitymedium paddingleft paddingright"></span><span class="clipboardCPText"></span></span>';
11101  } else {
11102  $result = '<span class="clipboardCP'.($showonlyonhover ? ' clipboardCPShowOnHover' : '').'"><'.$tag.' class="clipboardCPValue">'.dol_escape_htmltag($valuetocopy, 1, 1).'</'.$tag.'><span class="clipboardCPButton far fa-clipboard opacitymedium paddingleft paddingright"></span><span class="clipboardCPText"></span></span>';
11103  }
11104 
11105  return $result;
11106 }
11107 
11108 
11115 function jsonOrUnserialize($stringtodecode)
11116 {
11117  $result = json_decode($stringtodecode);
11118  if ($result === null) {
11119  $result = unserialize($stringtodecode);
11120  }
11121 
11122  return $result;
11123 }
11124 
11125 
11126 
11142 function dolCheckFilters($sqlfilters, &$error = '')
11143 {
11144  //$regexstring='\(([^:\'\(\)]+:[^:\'\(\)]+:[^:\(\)]+)\)';
11145  //$tmp=preg_replace_all('/'.$regexstring.'/', '', $sqlfilters);
11146  $tmp = $sqlfilters;
11147  $i = 0; $nb = strlen($tmp);
11148  $counter = 0;
11149  while ($i < $nb) {
11150  if ($tmp[$i] == '(') {
11151  $counter++;
11152  }
11153  if ($tmp[$i] == ')') {
11154  $counter--;
11155  }
11156  if ($counter < 0) {
11157  $error = "Bad sqlfilters=".$sqlfilters;
11158  dol_syslog($error, LOG_WARNING);
11159  return false;
11160  }
11161  $i++;
11162  }
11163  return true;
11164 }
11165 
11181 function dolForgeCriteriaCallback($matches)
11182 {
11183  global $db;
11184 
11185  dol_syslog("Convert matches ".$matches[1]);
11186  if (empty($matches[1])) {
11187  return '';
11188  }
11189  $tmp = explode(':', $matches[1], 3);
11190 
11191  if (count($tmp) < 3) {
11192  return '';
11193  }
11194 
11195  $operand = preg_replace('/[^a-z0-9\._]/i', '', trim($tmp[0]));
11196 
11197  $operator = strtoupper(preg_replace('/[^a-z<>!=]/i', '', trim($tmp[1])));
11198  if ($operator == 'NOTLIKE') {
11199  $operator = 'NOT LIKE';
11200  }
11201 
11202  $tmpescaped = trim($tmp[2]);
11203  $regbis = array();
11204  if ($operator == 'IN') {
11205  $tmpescaped = "(".$db->sanitize($tmpescaped, 1).")";
11206  } elseif (preg_match('/^\'(.*)\'$/', $tmpescaped, $regbis)) {
11207  $tmpescaped = "'".$db->escape($regbis[1])."'";
11208  } else {
11209  $tmpescaped = $db->sanitize($db->escape($tmpescaped));
11210  }
11211 
11212  return $db->escape($operand).' '.$db->escape($operator)." ".$tmpescaped;
11213 }
dol_nboflines_bis
dol_nboflines_bis($text, $maxlinesize=0, $charset='UTF-8')
Return nb of lines of a formated text with and (WARNING: string must not have mixed and br sepa...
Definition: functions.lib.php:7142
getTaxesFromId
getTaxesFromId($vatrate, $buyer=null, $seller=null, $firstparamisid=1)
Get tax (VAT) main information from Id.
Definition: functions.lib.php:6024
make_substitutions
make_substitutions($text, $substitutionarray, $outputlangs=null, $converttextinhtmlifnecessary=0)
Make substitution into a text string, replacing keys with vals from $substitutionarray (oldval=>newva...
Definition: functions.lib.php:7839
img_allow
img_allow($allow, $titlealt='default')
Show tick logo if allowed.
Definition: functions.lib.php:4671
dol_getdate
dol_getdate($timestamp, $fast=false, $forcetimezone='')
Return an array with locale date info.
Definition: functions.lib.php:2713
img_picto_common
img_picto_common($titlealt, $picto, $moreatt='', $pictoisfullpath=0, $notitle=0)
Show picto (generic function)
Definition: functions.lib.php:4259
dol_setcache
dol_setcache($memoryid, $data, $expire=0)
Save data into a memory area shared by all users, all sessions on server.
Definition: memory.lib.php:68
Societe
Class to manage third parties objects (customers, suppliers, prospects...)
Definition: societe.class.php:48
getLocalTaxesFromRate
getLocalTaxesFromRate($vatrate, $local, $buyer, $seller, $firstparamisid=0)
Get type and rate of localtaxes for a particular vat rate/country of a thirdparty.
Definition: functions.lib.php:6097
get_htmloutput_errors
get_htmloutput_errors($mesgstring='', $mesgarray=array(), $keepembedded=0)
Get formated error messages to output (Used to show messages on html output).
Definition: functions.lib.php:8281
yn
yn($yesno, $case=1, $color=0)
Return yes or no in current language.
Definition: functions.lib.php:6491
dol_sanitizePathName
dol_sanitizePathName($str, $newstr='_', $unaccent=1)
Clean a string to use it as a path name.
Definition: functions.lib.php:1251
dol_escape_htmltag
dol_escape_htmltag($stringtoescape, $keepb=0, $keepn=0, $noescapetags='', $escapeonlyhtmltags=0)
Returns text escaped for inclusion in HTML alt or title tags, or into values of HTML input fields.
Definition: functions.lib.php:1468
dol_trunc
dol_trunc($string, $size=40, $trunc='right', $stringencoding='UTF-8', $nodot=0, $display=0)
Truncate a string to a particular length adding '…' if string larger than length.
Definition: functions.lib.php:3805
realCharForNumericEntities
if(!empty($_SERVER['MAIN_SHOW_TUNING_INFO'])) realCharForNumericEntities($matches)
Return the real char for a numeric entities.
Definition: main.inc.php:61
dol_sanitizeFileName
dol_sanitizeFileName($str, $newstr='_', $unaccent=1)
Clean a string to use it as a file name.
Definition: functions.lib.php:1226
dol_htmloutput_events
dol_htmloutput_events($disabledoutputofmessages=0)
Print formated messages to output (Used to show messages on html output).
Definition: functions.lib.php:8172
isValidPhone
isValidPhone($phone)
Return true if phone number syntax is ok TODO Decide what to do with this.
Definition: functions.lib.php:3734
img_error
img_error($titlealt='default')
Show error logo.
Definition: functions.lib.php:4539
dolButtonToOpenUrlInDialogPopup
dolButtonToOpenUrlInDialogPopup($name, $label, $buttonstring, $url, $disabled='', $morecss='button bordertransp', $backtopagejsfields='')
Return HTML code to output a button to open a dialog popup box.
Definition: functions.lib.php:1720
img_action
img_action($titlealt, $numaction, $picto='')
Show logo action.
Definition: functions.lib.php:4296
picto_required
picto_required()
Return picto saying a field is required.
Definition: functions.lib.php:6672
dol_htmloutput_errors
dol_htmloutput_errors($mesgstring='', $mesgarray=array(), $keepembedded=0)
Print formated error messages to output (Used to show messages on html output).
Definition: functions.lib.php:8367
ascii_check
ascii_check($str)
Check if a string is in ASCII.
Definition: functions.lib.php:8475
dol_getcache
dol_getcache($memoryid)
Read a memory area shared by all users, all sessions on server.
Definition: memory.lib.php:135
dol_htmlentitiesbr_decode
dol_htmlentitiesbr_decode($stringtodecode, $pagecodeto='UTF-8')
This function is called to decode a HTML string (it decodes entities and br tags)
Definition: functions.lib.php:7020
dol_osencode
dol_osencode($str)
Return a string encoded into OS filesystem encoding.
Definition: functions.lib.php:8499
ProductFournisseur
Class to manage predefined suppliers products.
Definition: fournisseur.product.class.php:41
img_searchclear
img_searchclear($titlealt='default', $other='')
Show search logo.
Definition: functions.lib.php:4773
dol_substr
dol_substr($string, $start, $length, $stringencoding='', $trunconbytes=0)
Make a substring.
Definition: functions.lib.php:3766
getTitleFieldOfList
getTitleFieldOfList($name, $thead=0, $file="", $field="", $begin="", $moreparam="", $moreattrib="", $sortfield="", $sortorder="", $prefix="", $disablesortlink=0, $tooltip='', $forcenowrapcolumntitle=0)
Get title line of an array.
Definition: functions.lib.php:5049
load_fiche_titre
load_fiche_titre($titre, $morehtmlright='', $picto='generic', $pictoisfullpath=0, $id='', $morecssontable='', $morehtmlcenter='')
Load a title with picto.
Definition: functions.lib.php:5204
GETPOST
GETPOST($paramname, $check='alphanohtml', $method=0, $filter=null, $options=null, $noreplace=0)
Return value of a param into GET or POST supervariable.
Definition: functions.lib.php:484
dol_nl2br
dol_nl2br($stringtoencode, $nl2brmode=0, $forxml=false)
Replace CRLF in string with a HTML BR tag.
Definition: functions.lib.php:6963
img_info
img_info($titlealt='default')
Show info logo.
Definition: functions.lib.php:4502
dol_print_error
dol_print_error($db='', $error='', $errors=null)
Displays error message system with all the information to facilitate the diagnosis and the escalation...
Definition: functions.lib.php:4844
dol_string_nounprintableascii
dol_string_nounprintableascii($str, $removetabcrlf=1)
Clean a string from all non printable ASCII chars (0x00-0x1F and 0x7F).
Definition: functions.lib.php:1406
dol_getmypid
dol_getmypid()
Return getmypid() or random PID when function is disabled Some web hosts disable this php function fo...
Definition: functions.lib.php:9393
dol_sort_array
dol_sort_array(&$array, $index, $order='asc', $natsort=0, $case_sensitive=0, $keepindex=0)
Advanced sort array by second index function, which produces ascending (default) or descending output...
Definition: functions.lib.php:8385
img_help
img_help($usehelpcursor=1, $usealttitle=1)
Show help logo with cursor "?".
Definition: functions.lib.php:4481
dolCheckFilters
dolCheckFilters($sqlfilters, &$error='')
Return if a $sqlfilters parameter is valid and will pass the preg_replace_callback() to replace Gener...
Definition: functions.lib.php:11142
dol_include_once
if(!function_exists('dol_getprefix')) dol_include_once($relpath, $classname='')
Make an include_once using default root and alternate root if it fails.
Definition: functions.lib.php:1033
verifCond
verifCond($strToEvaluate)
Verify if condition in string is ok or not.
Definition: functions.lib.php:8582
Translate
Class to manage translations.
Definition: translate.class.php:30
dol_buildpath
dol_buildpath($path, $type=0, $returnemptyifnotfound=0)
Return path of url or filesystem.
Definition: functions.lib.php:1062
dol_validElement
dol_validElement($element)
Return if var element is ok.
Definition: functions.lib.php:8729
dol_size
dol_size($size, $type='')
Optimize a size for some browsers (phone, smarphone, ...)
Definition: functions.lib.php:1201
showDirectDownloadLink
showDirectDownloadLink($object)
Return string with full Url.
Definition: functions.lib.php:9569
img_warning
img_warning($titlealt='default', $moreatt='', $morecss='pictowarning')
Show warning logo.
Definition: functions.lib.php:4521
dol_shutdown
dol_shutdown()
Function called at end of web php process.
Definition: functions.lib.php:368
dolGetButtonTitleSeparator
dolGetButtonTitleSeparator($moreClass="")
Add space between dolGetButtonTitle.
Definition: functions.lib.php:10570
picto_from_langcode
picto_from_langcode($codelang, $moreatt='', $notitlealt=0)
Return img flag of country for a language code or country code.
Definition: functions.lib.php:8742
dol_mimetype
dol_mimetype($file, $default='application/octet-stream', $mode=0)
Return MIME type of a file from its name with extension.
Definition: functions.lib.php:9741
dolGetCountryCodeFromIp
dolGetCountryCodeFromIp($ip)
Return a country code from IP.
Definition: functions.lib.php:3558
img_split
img_split($titlealt='default', $other='class="pictosplit"')
Show split logo.
Definition: functions.lib.php:4463
$form
if($cancel &&! $id) if($action=='add' &&! $cancel) if($action=='delete') if($id) $form
Actions.
Definition: card.php:142
rowid
print *****$script_file(".$version.") pid c cd cd cd description as p label as s rowid
Definition: email_expire_services_to_representatives.php:79
sanitizeVal
sanitizeVal($out='', $check='alphanohtml', $filter=null, $options=null)
Return a sanitized or empty value after checking value against a rule.
Definition: functions.lib.php:825
ajax_object_onoff
ajax_object_onoff($object, $code, $field, $text_on, $text_off, $input=array(), $morecss='')
On/off button to change status of an object This is called when MAIN_DIRECT_STATUS_UPDATE is set and ...
Definition: ajax.lib.php:652
dol_dir_list
dol_dir_list($path, $types="all", $recursive=0, $filter="", $excludefilter=null, $sortcriteria="name", $sortorder=SORT_ASC, $mode=0, $nohook=0, $relativename="", $donotfollowsymlinks=0)
Scan a directory and return a list of files/directories.
Definition: files.lib.php:60
dol_print_url
dol_print_url($url, $target='_blank', $max=32, $withpicto=0)
Show Url link.
Definition: functions.lib.php:2922
getLanguageCodeFromCountryCode
getLanguageCodeFromCountryCode($countrycode)
Return default language from country code.
Definition: functions.lib.php:8785
name
$conf db name
Definition: repair.php:122
isVisibleToUserType
isVisibleToUserType($type_user, &$menuentry, &$listofmodulesforexternal)
Function to test if an entry is enabled or not.
Definition: functions.lib.php:10217
complete_head_from_modules
complete_head_from_modules($conf, $langs, $object, &$head, &$h, $type, $mode='add')
Complete or removed entries into a head array (used to build tabs).
Definition: functions.lib.php:9038
dolGetButtonAction
dolGetButtonAction($label, $html='', $actionType='default', $url='', $id='', $userRight=1, $params=array())
Function dolGetButtonAction.
Definition: functions.lib.php:10450
Facture
Class to manage invoices.
Definition: facture.class.php:60
dol_clone
dol_clone($object, $native=0)
Create a clone of instance of object (new instance with same value for each properties) With native =...
Definition: functions.lib.php:1158
printCommonFooter
printCommonFooter($zone='private')
Print common footer : conf->global->MAIN_HTML_FOOTER js for switch of menu hider js for conf->global-...
Definition: functions.lib.php:9150
img_edit
img_edit($titlealt='default', $float=0, $other='')
Show logo editer/modifier fiche.
Definition: functions.lib.php:4389
getDoliDBInstance
getDoliDBInstance($type, $host, $user, $pass, $name, $port)
Return a DoliDB instance (database handler).
Definition: functions.lib.php:122
dol_ucfirst
dol_ucfirst($string, $encoding="UTF-8")
Convert first character of the first word of a string to upper.
Definition: functions.lib.php:1557
dol_nboflines
dol_nboflines($s, $maxchar=0)
Return nb of lines of a clear text.
Definition: functions.lib.php:7121
getArrayOfSocialNetworks
getArrayOfSocialNetworks()
Get array of social network dictionary.
Definition: functions.lib.php:3026
DolGeoIP
Classe to manage GeoIP Usage: $geoip=new GeoIP('country',$datfile); $geoip->getCountryCodeFromIP($ip)...
Definition: dolgeoip.class.php:34
dol_banner_tab
dol_banner_tab($object, $paramid, $morehtml='', $shownav=1, $fieldid='rowid', $fieldref='ref', $morehtmlref='', $moreparam='', $nodbprefix=0, $morehtmlleft='', $morehtmlstatus='', $onlybanner=0, $morehtmlright='')
Show tab footer of a card.
Definition: functions.lib.php:2046
get_product_localtax_for_country
get_product_localtax_for_country($idprod, $local, $thirdpartytouse)
Return localtax vat rate of a product in a particular country or default country vat if product is un...
Definition: functions.lib.php:6233
price2num
price2num($amount, $rounding='', $option=0)
Function that return a number with universal decimal format (decimal separator is '.
Definition: functions.lib.php:5661
img_credit_card
img_credit_card($brand, $morecss=null)
Return image of a credit card according to its brand name.
Definition: functions.lib.php:4693
dol_print_error_email
dol_print_error_email($prefixcode, $errormessage='', $errormessages=array(), $morecss='error', $email='')
Show a public email and error code to contact if technical error.
Definition: functions.lib.php:4986
isValidMXRecord
isValidMXRecord($domain)
Return if the domain name has a valid MX record.
Definition: functions.lib.php:3704
dol_print_date
dol_print_date($time, $format='', $tzoutput='auto', $outputlangs='', $encodetooutput=false)
Output date in a string format according to outputlangs (or langs if not defined).
Definition: functions.lib.php:2514
img_left
img_left($titlealt='default', $selected=0, $moreatt='')
Show left arrow logo.
Definition: functions.lib.php:4634
dol_print_profids
dol_print_profids($profID, $profIDtype, $countrycode='', $addcpButton=1, $separ='&nbsp;')
Format profIDs according to country.
Definition: functions.lib.php:3149
isASecretKey
isASecretKey($keyname)
Return if string has a name dedicated to store a secret.
Definition: functions.lib.php:224
dolExplodeIntoArray
dolExplodeIntoArray($string, $delimiter=';', $kv='=')
Split a string with 2 keys into key array.
Definition: functions.lib.php:9354
get_date_range
get_date_range($date_start, $date_end, $format='', $outputlangs='', $withparenthesis=1)
Format output for start and end date.
Definition: functions.lib.php:8031
dol_concatdesc
dol_concatdesc($text1, $text2, $forxml=false, $invert=false)
Concat 2 descriptions with a new line between them (second operand after first one with appropriate n...
Definition: functions.lib.php:7248
img_picto
img_picto($titlealt, $picto, $moreatt='', $pictoisfullpath=false, $srconly=0, $notitle=0, $alt='', $morecss='', $marginleftonlyshort=2)
Show picto whatever it's its name (generic function)
Definition: functions.lib.php:3880
jsonOrUnserialize
jsonOrUnserialize($stringtodecode)
Decode an encode string.
Definition: functions.lib.php:11115
dolGetBadge
dolGetBadge($label, $html='', $type='primary', $mode='', $url='', $params=array())
Function dolGetBadge.
Definition: functions.lib.php:10274
dol_user_country
dol_user_country()
Return country code for current user.
Definition: functions.lib.php:3584
utf8_check
utf8_check($str)
Check if a string is in UTF8.
Definition: functions.lib.php:8438
img_up
img_up($titlealt='default', $selected=0, $moreclass='')
Show top arrow logo.
Definition: functions.lib.php:4615
dol_strtoupper
dol_strtoupper($string, $encoding="UTF-8")
Convert a string to upper.
Definition: functions.lib.php:1541
Exception
get_exdir
get_exdir($num, $level, $alpha, $withoutslash, $object, $modulepart='')
Return a path to have a the directory according to object where files are stored.
Definition: functions.lib.php:6549
img_delete
img_delete($titlealt='default', $other='class="pictodelete"', $morecss='')
Show delete logo.
Definition: functions.lib.php:4429
img_next
img_next($titlealt='default', $moreatt='')
Show next logo.
Definition: functions.lib.php:4557
isValidVATID
isValidVATID($company)
Check if VAT numero is valid (check done on syntax only, no database or remote access)
Definition: functions2.lib.php:801
dol_escape_js
dol_escape_js($stringtoescape, $mode=0, $noescapebackslashn=0)
Returns text escaped for inclusion into javascript code.
Definition: functions.lib.php:1423
dol_getIdFromCode
dol_getIdFromCode($db, $key, $tablename, $fieldkey='code', $fieldid='id', $entityfilter=0, $filters='')
Return an id or code from a code or id.
Definition: functions.lib.php:8535
dol_print_socialnetworks
dol_print_socialnetworks($value, $cid, $socid, $type, $dictsocialnetworks=array())
Show social network link.
Definition: functions.lib.php:3068
get_default_npr
get_default_npr(Societe $thirdparty_seller, Societe $thirdparty_buyer, $idprod=0, $idprodfournprice=0)
Fonction qui renvoie si tva doit etre tva percue recuperable.
Definition: functions.lib.php:6405
img_search
img_search($titlealt='default', $other='')
Show search logo.
Definition: functions.lib.php:4750
getBrowserInfo
getBrowserInfo($user_agent)
Return information about user browser.
Definition: functions.lib.php:260
num2Alpha
num2Alpha($n)
Return a numeric value into an Excel like column number.
Definition: functions.lib.php:236
dol_sanitizeUrl
dol_sanitizeUrl($stringtoclean, $type=1)
Clean a string to use it as an URL (into a href or src attribute)
Definition: functions.lib.php:1271
get_default_localtax
get_default_localtax($thirdparty_seller, $thirdparty_buyer, $local, $idprod=0)
Function that return localtax of a product line (according to seller, buyer and product vat rate) Si ...
Definition: functions.lib.php:6441
getEntity
getEntity($element, $shared=1, $currentobject=null)
Get list of entity id to use.
Definition: functions.lib.php:148
GETPOSTISARRAY
GETPOSTISARRAY($paramname, $method=0)
Return true if the parameter $paramname is submit from a POST OR GET as an array.
Definition: functions.lib.php:439
showValueWithClipboardCPButton
showValueWithClipboardCPButton($valuetocopy, $showonlyonhover=1, $texttoshow='')
Create a button to copy $valuetocopy in the clipboard (for copy and paste feature).
Definition: functions.lib.php:11087
code
print *****$script_file(".$version.") pid code
! Closing after partial payment: discount_vat, badcustomer or badsupplier, bankcharge,...
Definition: sync_members_ldap2dolibarr.php:60
get_localtax
get_localtax($vatrate, $local, $thirdparty_buyer="", $thirdparty_seller="", $vatnpr=0)
Return localtax rate for a particular vat, when selling a product with vat $vatrate,...
Definition: functions.lib.php:5837
dol_convert_file
dol_convert_file($fileinput, $ext='png', $fileoutput='', $page='')
Convert an image file or a PDF into another image format.
Definition: files.lib.php:1970
dol_string_nospecial
dol_string_nospecial($str, $newstr='_', $badcharstoreplace='', $badcharstoremove='')
Clean a string from all punctuation characters to use it as a ref or login.
Definition: functions.lib.php:1376
dol_string_unaccent
dol_string_unaccent($str)
Clean a string from all accent characters to be used as ref, login or by dol_sanitizeFileName.
Definition: functions.lib.php:1309
print_fiche_titre
print_fiche_titre($title, $mesg='', $picto='generic', $pictoisfullpath=0, $id='')
Show a title with picto.
Definition: functions.lib.php:5186
Societe\isACompany
isACompany()
Return if third party is a company (Business) or an end user (Consumer)
Definition: societe.class.php:3916
getAdvancedPreviewUrl
getAdvancedPreviewUrl($modulepart, $relativepath, $alldata=0, $param='')
Return URL we can use for advanced preview links.
Definition: functions.lib.php:9646
isValidEmail
isValidEmail($address, $acceptsupervisorkey=0, $acceptuserkey=0)
Return true if email syntax is ok.
Definition: functions.lib.php:3681
dolGetButtonTitle
dolGetButtonTitle($label, $helpText='', $iconClass='fa fa-file', $url='', $id='', $status=1, $params=array())
Function dolGetButtonTitle : this kind of buttons are used in title in list.
Definition: functions.lib.php:10605
get_product_vat_for_country
get_product_vat_for_country($idprod, $thirdpartytouse, $idprodfournprice=0)
Return vat rate of a product in a particular country, or default country vat if product is unknown.
Definition: functions.lib.php:6159
img_edit_add
img_edit_add($titlealt='default', $other='')
Show logo +.
Definition: functions.lib.php:4353
getCommonSubstitutionArray
getCommonSubstitutionArray($outputlangs, $onlykey=0, $exclude=null, $object=null)
Return array of possible common substitutions.
Definition: functions.lib.php:7275
getServerTimeZoneInt
getServerTimeZoneInt($refgmtdate='now')
Return server timezone int.
Definition: date.lib.php:83
dol_string_nohtmltag
dol_string_nohtmltag($stringtoclean, $removelinefeed=1, $pagecodeto='UTF-8', $strip_tags=0, $removedoublespaces=1)
Clean a string from all HTML tags and entities.
Definition: functions.lib.php:6694
GETPOSTINT
GETPOSTINT($paramname, $method=0)
Return value of a param into GET or POST supervariable.
Definition: functions.lib.php:795
dol_print_address
dol_print_address($address, $htmlid, $element, $id, $noprint=0, $charfornl='')
Format address string.
Definition: functions.lib.php:3615
dol_syslog
dol_syslog($message, $level=LOG_INFO, $ident=0, $suffixinfilename='', $restricttologhandler='', $logcontext=null)
Write log message into outputs.
Definition: functions.lib.php:1603
conf
conf($dolibarr_main_document_root)
Load conf file (file must exists)
Definition: inc.php:300
dol_bc
dol_bc($var, $moreclass='')
Return string to add class property on html element with pair/impair.
Definition: functions.lib.php:2370
setEventMessage
setEventMessage($mesgs, $style='mesgs')
Set event message in dol_events session object.
Definition: functions.lib.php:8108
isOnlyOneLocalTax
isOnlyOneLocalTax($local)
Return true if LocalTax (1 or 2) is unique.
Definition: functions.lib.php:5970
dol_string_neverthesehtmltags
dol_string_neverthesehtmltags($stringtoclean, $disallowed_tags=array('textarea'), $cleanalsosomestyles=0)
Clean a string from some undesirable HTML tags.
Definition: functions.lib.php:6882
print_barre_liste
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='')
Print a title with navigation controls for pagination.
Definition: functions.lib.php:5257
checkVal
checkVal($out='', $check='alphanohtml', $filter=null, $options=null)
Return a sanitized or empty value after checking value against a rule.
Definition: functions.lib.php:811
getFieldErrorIcon
getFieldErrorIcon($fieldValidationErrorMsg)
get field error icon
Definition: functions.lib.php:10581
dol_print_size
dol_print_size($size, $shortvalue=0, $shortunit=0)
Return string with formated size.
Definition: functions.lib.php:2884
dol_format_address
dol_format_address($object, $withcountry=0, $sep="\n", $outputlangs='', $mode=0, $extralangcode='')
Return a formated address (part address/zip/town/state) according to country rules.
Definition: functions.lib.php:2393
img_down
img_down($titlealt='default', $selected=0, $moreclass='')
Show down arrow logo.
Definition: functions.lib.php:4596
dol_get_fiche_head
dol_get_fiche_head($links=array(), $active='', $title='', $notab=0, $picto='', $pictoisfullpath=0, $morehtmlright='', $morecss='', $limittoshow=0, $moretabssuffix='')
Show tabs of a record.
Definition: functions.lib.php:1822
getDolGlobalString
if(!function_exists('utf8_encode')) if(!function_exists('utf8_decode')) getDolGlobalString($key, $default='')
Return dolibarr global constant string value.
Definition: functions.lib.php:80
isAFileWithExecutableContent
isAFileWithExecutableContent($filename)
Return if a file can contains executable content.
Definition: functions.lib.php:10863
dol_htmlcleanlastbr
dol_htmlcleanlastbr($stringtodecode)
This function remove all ending and br at end.
Definition: functions.lib.php:7036
print_date_range
print_date_range($date_start, $date_end, $format='', $outputlangs='')
Format output for start and end date.
Definition: functions.lib.php:8016
get_default_tva
get_default_tva(Societe $thirdparty_seller, Societe $thirdparty_buyer, $idprod=0, $idprodfournprice=0)
Function that return vat rate of a product line (according to seller, buyer and product vat rate) VAT...
Definition: functions.lib.php:6304
dol_strlen
dol_strlen($string, $stringencoding='UTF-8')
Make a strlen call.
Definition: functions.lib.php:3747
dol_get_prev_month
dol_get_prev_month($month, $year)
Return previous month.
Definition: date.lib.php:470
dol_fiche_end
dol_fiche_end($notab=0)
Show tab footer of a card.
Definition: functions.lib.php:2007
info_admin
info_admin($text, $infoonimgalt=0, $nodiv=0, $admin='1', $morecss='hideonsmartphone', $textfordropdown='')
Show information for admin users or standard users.
Definition: functions.lib.php:4800
get_localtax_by_third
get_localtax_by_third($local)
Get values of localtaxes (1 or 2) for company country for the common vat with the highest value.
Definition: functions.lib.php:5989
dol_print_ip
dol_print_ip($ip, $mode=0)
Return an IP formated to be shown on screen.
Definition: functions.lib.php:3481
newToken
newToken()
Return the value of token currently saved into session with name 'newtoken'.
Definition: functions.lib.php:10878
dol_get_fiche_end
dol_get_fiche_end($notab=0)
Return tab footer of a card.
Definition: functions.lib.php:2018
dol_convertToWord
dol_convertToWord($num, $langs, $currency='', $centimes=false)
Function to return a number into a text.
Definition: functionsnumtoword.lib.php:36
isModEnabled
isModEnabled($module)
Is Dolibarr module enabled.
Definition: functions.lib.php:105
readfileLowMemory
readfileLowMemory($fullpath_original_file_osencoded, $method=-1)
Return a file on output using a low memory.
Definition: functions.lib.php:11039
dol_htmloutput_mesg
dol_htmloutput_mesg($mesgstring='', $mesgarray=array(), $style='ok', $keepembedded=0)
Print formated messages to output (Used to show messages on html output).
Definition: functions.lib.php:8299
dolGetFirstLastname
dolGetFirstLastname($firstname, $lastname, $nameorder=-1)
Return firstname and lastname in correct order.
Definition: functions.lib.php:8062
getElementProperties
getElementProperties($element_type)
Get an array with properties of an element.
Definition: functions.lib.php:10706
User
Class to manage Dolibarr users.
Definition: user.class.php:44
img_edit_remove
img_edit_remove($titlealt='default', $other='')
Show logo -.
Definition: functions.lib.php:4370
GETPOSTISSET
GETPOSTISSET($paramname)
Return true if we are in a context of submitting the parameter $paramname from a POST of a form.
Definition: functions.lib.php:386
img_pdf
img_pdf($titlealt='default', $size=3)
Show pdf logo.
Definition: functions.lib.php:4335
dol_html_entity_decode
dol_html_entity_decode($a, $b, $c='UTF-8', $keepsomeentities=0)
Replace html_entity_decode functions to manage errors.
Definition: functions.lib.php:7052
img_view
img_view($titlealt='default', $float=0, $other='class="valignmiddle"')
Show logo view card.
Definition: functions.lib.php:4408
print_liste_field_titre
print_liste_field_titre($name, $file="", $field="", $begin="", $moreparam="", $moreattrib="", $sortfield="", $sortorder="", $prefix="", $tooltip="", $forcenowrapcolumntitle=0)
Show title line of an array.
Definition: functions.lib.php:5026
natural_search
natural_search($fields, $value, $mode=0, $nofirstand=0)
Generate natural SQL search string for a criteria (this criteria can be tested on one or several fiel...
Definition: functions.lib.php:9420
dol_eval
dol_eval($s, $returnvalue=0, $hideerrors=1, $onlysimplestring='1')
Replace eval function to add more security.
Definition: functions.lib.php:8611
ExtraFields
Class to manage standard extra fields.
Definition: extrafields.class.php:39
img_mime
img_mime($file, $titlealt='', $morecss='')
Show MIME img of a file.
Definition: functions.lib.php:4726
Product
Class to manage products or services.
Definition: product.class.php:46
dol_string_is_good_iso
dol_string_is_good_iso($s, $clean=0)
Check if a string is a correct iso string If not, it will we considered not HTML encoded even if it i...
Definition: functions.lib.php:7089
dolGetStatus
dolGetStatus($statusLabel='', $statusLabelShort='', $html='', $statusType='status0', $displayMode=0, $url='', $params=array())
Output the badge of a status.
Definition: functions.lib.php:10338
img_weather
img_weather($titlealt, $picto, $moreatt='', $pictoisfullpath=0, $morecss='')
Show weather picto.
Definition: functions.lib.php:4231
print_fleche_navigation
print_fleche_navigation($page, $file, $options='', $nextpage=0, $betweenarrows='', $afterarrows='', $limit=-1, $totalnboflines=0, $hideselectlimit=0, $beforearrows='')
Function to show navigation arrows into lists.
Definition: functions.lib.php:5403
dol_get_next_month
dol_get_next_month($month, $year)
Return next month.
Definition: date.lib.php:489
dol_set_focus
dol_set_focus($selector)
Set focus onto field with selector (similar behaviour of 'autofocus' HTML5 tag)
Definition: functions.lib.php:9379
img_object
img_object($titlealt, $picto, $moreatt='', $pictoisfullpath=false, $srconly=0, $notitle=0)
Show a picto called object_picto (generic function)
Definition: functions.lib.php:4211
isHTTPS
isHTTPS()
Return if we are using a HTTPS connexion Check HTTPS (no way to be modified by user but may be empty ...
Definition: functions.lib.php:3541
startSimpleTable
startSimpleTable($header, $link="", $arguments="", $emptyRows=0, $number=-1)
Start a table with headers and a optinal clickable number (don't forget to use "finishSimpleTable()" ...
Definition: functions.lib.php:10906
dol_print_email
dol_print_email($email, $cid=0, $socid=0, $addlink=0, $max=64, $showinvalid=1, $withpicto=0)
Show EMail link formatted for HTML output.
Definition: functions.lib.php:2960
dol_strftime
dol_strftime($fmt, $ts=false, $is_gmt=false)
Format a string.
Definition: functions.lib.php:2484
dol_htmlentitiesbr
dol_htmlentitiesbr($stringtoencode, $nl2brmode=0, $pagecodefrom='UTF-8', $removelasteolbr=1)
This function is called to encode a string into a HTML string but differs from htmlentities because a...
Definition: functions.lib.php:6991
dol_now
dol_now($mode='auto')
Return date for now.
Definition: functions.lib.php:2845
$resql
if(isModEnabled('facture') &&!empty($user->rights->facture->lire)) if((isModEnabled('fournisseur') &&empty($conf->global->MAIN_USE_NEW_SUPPLIERMOD) && $user->rights->fournisseur->facture->lire)||(isModEnabled('supplier_invoice') && $user->rights->supplier_invoice->lire)) if(isModEnabled('don') &&!empty($user->rights->don->lire)) if(isModEnabled('tax') &&!empty($user->rights->tax->charges->lire)) if(isModEnabled('facture') &&isModEnabled('commande') && $user->rights->commande->lire &&empty($conf->global->WORKFLOW_DISABLE_CREATE_INVOICE_FROM_ORDER)) $resql
Social contributions to pay.
Definition: index.php:742
dolForgeCriteriaCallback
dolForgeCriteriaCallback($matches)
Function to forge a SQL criteria from a Generic filter string.
Definition: functions.lib.php:11181
getImageFileNameForSize
getImageFileNameForSize($file, $extName, $extImgTarget='')
Return the filename of file to get the thumbs.
Definition: functions.lib.php:9595
roundUpToNextMultiple
roundUpToNextMultiple($n, $x=5)
Round to next multiple.
Definition: functions.lib.php:10258
price
price($amount, $form=0, $outlangs='', $trunc=1, $rounding=-1, $forcerounding=-1, $currency_code='')
Function to format a value into an amount for visual output Function used into PDF and HTML pages.
Definition: functions.lib.php:5541
dolIsAllowedForPreview
dolIsAllowedForPreview($file)
Return if a file is qualified for preview.
Definition: functions.lib.php:9706
dol_print_phone
dol_print_phone($phone, $countrycode='', $cid=0, $socid=0, $addlink='', $separ="&nbsp;", $withpicto='', $titlealt='', $adddivfloat=0)
Format phone numbers according to country.
Definition: functions.lib.php:3185
setEventMessages
setEventMessages($mesg, $mesgs, $style='mesgs', $messagekey='')
Set event messages in dol_events session object.
Definition: functions.lib.php:8137
dol_textishtml
dol_textishtml($msg, $option=0)
Return if a text is a html content.
Definition: functions.lib.php:7185
dol_is_dir
dol_is_dir($folder)
Test if filename is a directory.
Definition: files.lib.php:447
dolGetFirstLineOfText
dolGetFirstLineOfText($text, $nboflines=1, $charset='UTF-8')
Return first line of text.
Definition: functions.lib.php:6907
ajax_autoselect
ajax_autoselect($htmlname, $addlink='', $textonlink='Link')
Make content of an input box selected when we click into input field.
Definition: functions.lib.php:9681
dol_mkdir
dol_mkdir($dir, $dataroot='', $newmask='')
Creation of a directory (this can create recursive subdir)
Definition: functions.lib.php:6603
dol_mktime
dol_mktime($hour, $minute, $second, $month, $day, $year, $gm='auto', $check=1)
Return a timestamp date built from detailed informations (by default a local PHP server timestamp) Re...
Definition: functions.lib.php:2757
img_previous
img_previous($titlealt='default', $moreatt='')
Show previous logo.
Definition: functions.lib.php:4576
fetchObjectByElement
fetchObjectByElement($element_id, $element_type, $element_ref='')
Fetch an object from its id and element_type Inclusion of classes is automatic.
Definition: functions.lib.php:10840
currentToken
currentToken()
Return the value of token currently saved into session with name 'token'.
Definition: functions.lib.php:10889
getDolGlobalInt
getDolGlobalInt($key, $default=0)
Return dolibarr global constant int value.
Definition: functions.lib.php:93
showDimensionInBestUnit
showDimensionInBestUnit($dimension, $unit, $type, $outputlangs, $round=-1, $forceunitoutput='no', $use_short_label=0)
Output a dimension with best unit.
Definition: functions.lib.php:5788
colorIsLight
colorIsLight($stringcolor)
Return true if the color is light.
Definition: functions.lib.php:10182
setEntity
setEntity($currentobject)
Set entity id to use when to create an object.
Definition: functions.lib.php:207
dol_string_onlythesehtmltags
dol_string_onlythesehtmltags($stringtoclean, $cleanalsosomestyles=1, $removeclassattribute=1, $cleanalsojavascript=0, $allowiframe=0)
Clean a string to keep only desirable HTML tags.
Definition: functions.lib.php:6760
print_titre
print_titre($title)
Show a title.
Definition: functions.lib.php:5168
dol_strtolower
dol_strtolower($string, $encoding="UTF-8")
Convert a string to lower.
Definition: functions.lib.php:1525
dol_string_onlythesehtmlattributes
dol_string_onlythesehtmlattributes($stringtoclean, $allowed_attributes=array("allow", "allowfullscreen", "alt", "class", "contenteditable", "data-html", "frameborder", "height", "href", "id", "name", "src", "style", "target", "title", "width"))
Clean a string from some undesirable HTML tags.
Definition: functions.lib.php:6820
img_right
img_right($titlealt='default', $selected=0, $moreatt='')
Show right arrow logo.
Definition: functions.lib.php:4653
dol_escape_json
dol_escape_json($stringtoescape)
Returns text escaped for inclusion into javascript code.
Definition: functions.lib.php:1452
HookManager
Class to manage hooks.
Definition: hookmanager.class.php:30
get_htmloutput_mesg
get_htmloutput_mesg($mesgstring='', $mesgarray='', $style='ok', $keepembedded=0)
Get formated messages to output (Used to show messages on html output).
Definition: functions.lib.php:8212
dol_htmlentities
dol_htmlentities($string, $flags=ENT_QUOTES|ENT_SUBSTITUTE, $encoding='UTF-8', $double_encode=false)
Replace htmlentities functions.
Definition: functions.lib.php:7075
dol_fiche_head
dol_fiche_head($links=array(), $active='0', $title='', $notab=0, $picto='', $pictoisfullpath=0, $morehtmlright='', $morecss='', $limittoshow=0, $moretabssuffix='')
Show tab header of a card.
Definition: functions.lib.php:1802
complete_substitutions_array
complete_substitutions_array(&$substitutionarray, $outputlangs, $object=null, $parameters=null, $callfunc="completesubstitutionarray")
Complete the $substitutionarray with more entries coming from external module that had set the "subst...
Definition: functions.lib.php:7961
vatrate
vatrate($rate, $addpercent=false, $info_bits=0, $usestarfornpr=0, $html=0)
Return a string with VAT rate label formated for view output Used into pdf and HTML pages.
Definition: functions.lib.php:5492
getUserRemoteIP
getUserRemoteIP()
Return the IP of remote user.
Definition: functions.lib.php:3515
isInEEC
isInEEC($object)
Return if a country of an object is inside the EEC (European Economic Community)
Definition: company.lib.php:753
dol_get_prev_day
dol_get_prev_day($day, $month, $year)
Return previous day.
Definition: date.lib.php:439
fieldLabel
fieldLabel($langkey, $fieldkey, $fieldrequired=0)
Show a string with the label tag dedicated to the HTML edit field.
Definition: functions.lib.php:2347
dol_ucwords
dol_ucwords($string, $encoding="UTF-8")
Convert first character of all the words of a string to upper.
Definition: functions.lib.php:1573
img_printer
img_printer($titlealt="default", $other='')
Show printer logo.
Definition: functions.lib.php:4447
float
div float
Buy price without taxes.
Definition: style.css.php:809
dol_get_next_day
dol_get_next_day($day, $month, $year)
Return next day.
Definition: date.lib.php:455
if
if(!defined( 'CSRFCHECK_WITH_TOKEN'))
Definition: journals_list.php:25